crouter
SDK

Memory

Read and write memory documents with an explicit target for every request.

client.memory

Phase 3. client.memory reads and writes the memory documents that shape an agent run. Every call names its target; the daemon never uses its own working directory or environment to select memory.

const page = await client.memory.list({ node: nodeId, kind: 'knowledge', limit: 50 });
const document = await client.memory.retrieve('insights/capture', { node: nodeId, frontmatter: true });

Target

type MemoryScope = {
  node?: string;
  cwd?: string;
  profile?: string;
  store?: 'node' | 'project' | 'profile' | 'user' | 'builtin';
};

Pass node to use that node's working directory and profile. Do not combine it with cwd or profile. With no target, only user and builtin memory are searched. store restricts the request to one memory tier; store: 'node' requires node, and store: 'profile' requires a profile target. create defaults to the nearest project store when one is available, otherwise the user store. update, delete, and move resolve the existing document through normal scope precedence when store is omitted. builtin is read-only.

When a request supplies node, its node row must hold memory:read for list, retrieve, search, history, and resolve, or memory:write for create, update, delete, and move. Calls without node are external application calls and are not narrowed by a node scope list.

Methods

Every method accepts RequestOptions as its final optional argument (signal, timeout, maxRetries, and headers). A document name can contain /; the SDK encodes it as one path segment.

| Method | Parameters | Result | |---|---|---| | memory.list(query?, options?) | MemoryScope & { kind?, limit?, after? }; limit is 1–100 (default 100) | MemoryPage<MemoryDocSummaryDTO> | | memory.retrieve(name, query?, options?) | name; MemoryScope & { frontmatter? } | MemoryDocDTO | | memory.create(params, options?) | MemoryDocCreateRequest | MemoryDocDTO | | memory.update(name, params, options?) | name; MemoryDocUpdateRequest | MemoryDocDTO | | memory.delete(name, scope?, options?) | name; MemoryScope | MemoryDocDeletedDTO | | memory.move(name, params, options?) | name; MemoryDocMoveRequest | MemoryDocMovedDTO | | memory.search(params, options?) | MemorySearchRequest | MemoryPage<MemorySearchHitDTO> | | memory.history(name, query?, options?) | name; MemoryHistoryQuery | MemoryHistoryDTO | | memory.resolve(name, scope?, options?) | name; MemoryScope | MemoryDocRefDTO |

MemoryDocCreateRequest requires name, kind, when_and_why_to_read, and body; it also accepts frontmatter, extensions, and the scope fields. MemoryDocUpdateRequest requires rationale, which is stored in revision history rather than document text, and optionally accepts body, frontmatter, extensions, unset, and the scope fields. MemoryDocMoveRequest requires to and accepts the scope fields.

retrieve returns the body by default. With frontmatter: true, its body is the complete document source. MemoryDocDTO also contains frontmatter, representation ('leaf-file' or 'directory-index'), links, and any lint findings; document DTOs include their canonical name, kind, scope, extensions, path, store root, physical relative path, and any competing candidates. resolve returns the winning document's location and optional plugin name without reading its body.

await client.memory.create({
  name: 'billing/refund-policy',
  kind: 'knowledge',
  when_and_why_to_read: 'When handling a refund request, read this because the eligibility window is not in the order record.',
  body: 'Refunds are available for 60 days.',
  profile: 'my-app',
});

await client.memory.update('billing/refund-policy', {
  rationale: 'The eligibility window changed on 2026-01-04.',
  body: 'Refunds are available for 90 days.',
  profile: 'my-app',
});

The daemon returns document text without expanding shell blocks. Builtin and plugin documents are read-only. kind, when-and-why-to-read, origin, and last-updated cannot be set or removed directly. Each mutation writes a complete before/after history record; delete leaves a tombstone. retrieve and resolve return NotFoundError with status 404 and code memory_document_not_found for a deleted document or an unknown name. Concurrent writes are last-write-wins.

search uses ranked search by default. Set body: true to include document bodies in ranking, or grep: true for regular-expression line matches; grep and body cannot be combined, and min_score is only available in ranked search. Ranked hits contain kind, score, and short_form; grep hits contain line and text.

history returns newest-first revision summaries, optionally with diff, or the full before/after record for one revision. A delete leaves history available through its tombstone.

Pages

list and search return { object: 'list', data, has_more } as a MemoryPage. A page has hasNextPage(), getNextPage(), and is async iterable across every page. The list cursor is the final document's canonical name. Ranked search uses the final hit's name; grep uses an opaque cursor that includes the match location, which the SDK handles.

const page = await client.memory.search({ query: 'refund window', node: nodeId });
for (const hit of page.data) console.log(hit.name);

if (page.hasNextPage()) {
  const next = await page.getNextPage();
}

for await (const hit of page) console.log(hit.name);