The memory-store resource is declarative, while agents memory* commands manage
runtime content without editing agents.yaml.
YAML entries initialize new stores only by default. Applying changes to store
attributes or initialization entries does not overwrite content in an existing
store. Use entries_mode: sync explicitly when those paths should continue to
follow YAML; runtime-created paths remain preserved.
Save this as agents.yaml, with the two credential variables supplied by your
environment or .env:
version: "1"
providers:
bailian:
api_key: ${DASHSCOPE_API_KEY}
workspace_id: ${BAILIAN_WORKSPACE_ID}
defaults:
provider: bailian
environments:
dev:
config: { type: cloud }
memory_stores:
notes:
name: Project notes
description: Shared project decisions
metadata:
team: platform
settings: { reviewed: true }
entries_mode: initialize
entries:
- key: /notes/decision.md
content: Use a modular monolith.
agents:
assistant:
model: qwen-plus
instructions: Use project memory when answering.
environment: dev
resources:
- type: memory_store
memory_store: notes
access: read_write
instructions: Keep durable project decisions here.agents plan -f agents.yaml
agents apply -f agents.yamlnotes is the logical reference; Project notes is a non-unique display name.
Without a recorded remote ID, apply creates a new store. To reuse an existing
store, explicitly import its ID before apply; there is no adoption by name.
Imported and existing stores keep their contents in the default initialize
mode. Store-attribute updates read and merge metadata, changing only declared
keys and preserving keys added remotely.
For a new Bailian store, State records its ID and the pending initializer paths and SHA-256 values before any file is written. A failed apply can resume against that ID: only pending, absent paths are created, and existing files are preserved. Keep the original pending content until initialization completes; changed or removed pending sources are rejected. State contains hashes, not initializer bodies. Session mounting is blocked until this initialization completes.
Set entries_mode: sync only when YAML should keep managing declared paths.
Sync creates or updates those files without deleting other files, including
files whose declarations were removed. Removing the whole store declaration and
applying, or destroying the project, irreversibly archives the Bailian store.
Session resources resolve memory_store: notes through State. The shorthand
memory_stores: [notes] mounts it read-write; an explicit resource can override
that mount's access and instructions. A Session accepts at most eight distinct
stores. Do not specify a Memory mount path; the server returns it. Bailian
Deployments accept files only and reject explicit or inherited Memory mounts.
The SDK's runtime APIs also work with a provider-only context, without loading YAML or changing project State:
import {
createManagedApiContext,
getMemoryVersion,
listMemories,
listMemoryVersions,
updateMemory,
} from "@openagentpack/sdk";
const context = createManagedApiContext({
providers: {
bailian: {
api_key: process.env.DASHSCOPE_API_KEY,
workspace_id: process.env.BAILIAN_WORKSPACE_ID,
},
},
});
const storeId = "memstore_...";
const memoryId = "mem_...";
const firstPage = await listMemories(context.providers, "bailian", storeId, {
path_prefix: "/notes/",
depth: 1,
view: "full",
limit: 20,
});
if (firstPage.next_page) {
const nextPage = await listMemories(context.providers, "bailian", storeId, {
path_prefix: "/notes/",
depth: 1,
view: "full",
limit: 20,
page: firstPage.next_page,
});
console.log(nextPage.data);
}
await updateMemory(context.providers, "bailian", storeId, memoryId, {
content: "Keep modules independent.",
precondition: { type: "content_sha256", content_sha256: "<sha-from-getMemory>" },
});
const history = await listMemoryVersions(context.providers, "bailian", storeId, {
memory_id: memoryId,
operation: "modified",
view: "full",
});
if (history.data[0]) {
const version = await getMemoryVersion(context.providers, "bailian", storeId, history.data[0].id);
console.log(version);
}Bailian preserves its native response fields, including unknown service fields:
- Paths retain their leading
/. Relative create paths are normalized to an absolute path. Directory listings can return a separateprefixesarray. - Lists return
dataand opaquenext_page; pass that value aspagefor the next request. A null or missingnext_pageends pagination; do not assumehas_moreornext_cursoris present. - Memory updates accept an atomic path rename and an optional content SHA
precondition.
expected_content_sha256is also accepted and maps to the nativepreconditionobject. Conflict errors are passed through without retrying the write. Update responses may omitcontent; usegetMemoryto read the body. - Version lists support
memory_id,operation,created_at.gte,created_at.lte,view,limit, andpage. The native operationmodifiedis preserved.getMemoryVersionreads a snapshot andredactMemoryVersionredacts one; fields absent from a redacted response remain absent. Redaction is irreversible and does not provide a way to restore an archived store. deleteMemoryreturns the nativememory_deletedreceipt. Bailian does not support a delete SHA precondition, per-file metadata, or batch creation.archiveMemoryStorearchives the container irreversibly.
agents apply -f examples/qoder/with-memory/agents.yaml
agents memory-store list -f examples/qoder/with-memory/agents.yaml --provider qoder
agents memory create <store-id> notes/decision.md \
--content "Use a modular monolith" \
-f examples/qoder/with-memory/agents.yaml --provider qoder
agents memory list <store-id> --full \
-f examples/qoder/with-memory/agents.yaml --provider qoder
agents memory update <store-id> <memory-id> \
--content "Use services after the team reaches 30 engineers" \
--expected-sha256 <sha256-from-get> \
-f examples/qoder/with-memory/agents.yaml --provider qoderThe same commands work with --provider claude and --provider ark. For these
portable adapters, use relative paths; the Claude adapter converts them to
Claude's required leading-slash form. Bailian retains native absolute paths as
described above.
Qoder and Claude expose immutable version history:
agents memory version list <store-id> --memory-id <memory-id> --full -f agents.yaml --provider qoder
agents memory version redact <store-id> <version-id> -f agents.yaml --provider qoderArk exposes partial-success batch creation (items.json is an array of
{"path":"...","content":"..."} objects):
agents memory batch-create <store-id> items.json --on-conflict overwrite -f agents.yaml --provider arkArk currently uses last-write-wins and does not expose version retrieval or redaction. Calling an unsupported extension returns a provider capability error.
The live probe creates a temporary store, exercises CRUD (plus versions or batch
creation where supported), and deletes the store in finally:
QODER_PAT=... bun packages/sdk/tests/e2e/memory-live.ts qoder
ANTHROPIC_API_KEY=... bun packages/sdk/tests/e2e/memory-live.ts claude
ARK_API_KEY=... bun packages/sdk/tests/e2e/memory-live.ts ark