Agent Memory
SDKs

Node SDK

The agentmemory client for Node.js 18+ — install, configure, and remember, recall, report and forget with typed results.

agentmemory is a small ES-module client for the /v1 API. It uses the global fetch (Node 18+), has no dependencies and ships TypeScript declarations. Current version: 0.4.0.

Install

The package is not published to a public registry. Install it from the tarball your Agent Memory deployment serves, or from a checkout of the source repository.

Every Agent Memory instance serves its SDK packages under /sdk/:

npm install "$AGENT_MEMORY_BASE_URL/sdk/agentmemory-0.4.0.tgz"

Open $AGENT_MEMORY_BASE_URL/sdk/ in a browser to see the available versions.

0.4.0 is a breaking release

search() now returns the whole context (with receiptId) instead of a list, and delete() returns { status }. The 0.3.0 tarball stays available under /sdk/ for pinned installs.

Configure

import { AgentMemoryClient } from 'agentmemory'

const memory = new AgentMemoryClient({
  apiKey: process.env.AGENT_MEMORY_API_KEY,       // amk_…
  baseUrl: process.env.AGENT_MEMORY_BASE_URL,     // e.g. http://localhost:5199
  workspaceId: process.env.AGENT_MEMORY_WORKSPACE_ID,
})
OptionRequiredNotes
apiKeyyesA workspace API key. It proves who is calling and decides which agent acts.
baseUrlyesYour deployment's URL, without /v1. Trailing slashes are ignored.
workspaceIdyesThe workspace the key belongs to. Sent on every call.
principalnoDefaults { agentId?, userId? } applied to every call.

Leave agentId out. With no agent ID, the server acts as the agent the key is bound to; set one only when a key is bound to several agents. userId is the end user a memory is about, never the agent — naming one requires a key with act for users enabled. See Identity and visibility.

Remember — add()

const { id, merged, operationId } = await memory.add('Alex is vegetarian', {
  type: 'preference',
  userId: 'alex',
  runId: 'onboarding-42',
  visibility: 'user',
  sourceKind: 'user',
  tags: { facet: 'diet' },
  idempotencyKey: 'onboarding-42:diet',
})

Writes go to POST /v1/memory/append with layer: "Workspace", so the memory is durable and searchable at once.

OptionDefaultNotes
type'decision'decision, correction, violation, approved_pattern, artifact_ref, preference, raw.
runId'default'Use one stable runId per task.
userIdprincipalEnd user the memory is about.
agentIdprincipalOnly for multi-agent keys.
tags—String key/value pairs; facet is the conventional facet tag.
visibilityserver default workspaceprivate, agent, user, workspace, tenant.
sourceKindserver default agentagent, user, document or import.
sourceRef, observedAt, validFrom, validTo—Provenance and valid time (ISO 8601 strings).
idempotencyKey—Sent as Idempotency-Key; a retry replays the first answer.

Resolves to { id, merged, contentReplaced, operationId }. When the write was merged into a near-duplicate, id is the surviving memory.

const context = await memory.search('what can Alex eat?', {
  userId: 'alex',
  runId: 'onboarding-42',
  tokenBudget: 2000,
})

for (const entry of context.workspaceEntries) console.log(entry.content)
const receiptId = context.receiptId

Options: runId (default 'default'), userId, agentId, topN, mode ('assisted' | 'deterministic'), rerank, includeSuperseded, asOf, tokenBudget, explain.

Resolves to the full context: receiptId, workspaceEntries, tenantEntries, blocks, procedures, relevantEpisodes, sessionEntries, compilation and the other sections.

Report — outcome() and feedback()

Pass the recall's receiptId and the same runId:

await memory.outcome({
  runId: 'onboarding-42',
  receiptId,
  buildSuccess: true,
  testPassRate: 1,
  notes: 'user accepted the menu',
})

await memory.feedback({
  runId: 'onboarding-42',
  receiptId,
  usedEntryIds: [context.workspaceEntries[0].id],
})

outcome() accepts runId, receiptId, buildSuccess, testPassRate, deploySuccess, errorCount, warningCount, userRating and notes. feedback() accepts runId, receiptId, agentId, usedEntryIds, ignoredEntryIds, appliedProcedureIds and skippedProcedureIds. Both resolve to undefined. See Learning loop.

Read, explain and track

const entry = await memory.get(id)                 // MemoryEntry | null
const how = await memory.explain(id)               // explanation object | null
const receipt = await memory.operation(operationId) // { status: 'pending' | 'indexed' | 'failed', effects, ... } | null

get(), explain() and erasure() take optional { agentId, userId } and resolve to null when the memory does not exist or is not visible.

Update — update()

const result = await memory.update(id, 'Alex is vegan', { ifMatch: entry.version })
// { status: 'updated', version } | { status: 'notFound' } | { status: 'forbidden' }
// | { status: 'versionMismatch', currentVersion }

Share — share()

const outcome = await memory.share(id, 'workspace')  // 'shared' | 'notFound' | 'forbidden' | 'invalid'

Needs a key with the share capability, and ownership of the memory.

Forget — delete() and erasure()

const result = await memory.delete(id)
switch (result.status) {
  case 'deleted':   break
  case 'pending':   console.log(await memory.erasure(id)); break   // { status: 'pending' | 'complete' | ... }
  case 'blocked':   await memory.delete(id, { cascade: true }); break  // result.dependents lists them
  case 'not_found': break
  case 'forbidden': console.log(result.reason); break              // 'policy' | 'tenant_scope'
}

Deleting needs a key with the delete capability. delete() never cascades unless you pass { cascade: true }. See Updating and forgetting.

Errors

Any unexpected HTTP status throws AgentMemoryError:

import { AgentMemoryError } from 'agentmemory'

try {
  await memory.add('…')
} catch (err) {
  if (err instanceof AgentMemoryError) {
    console.error(err.status, err.body)   // body is the raw problem+json text
  }
}

Statuses that are part of a method's result (404 on get, the delete outcomes, 412 on update, …) are returned, not thrown.

Not in the client

The Node client covers the memory lifecycle. For consolidation, blocks, user profiles, episodes, procedures, files and categories, call the REST API directly with the same key — or rely on the background worker to consolidate idle runs.

Method reference

MethodEndpoint
add(content, options?)POST /v1/memory/append
search(query, options?)POST /v1/memory/retrieve
get(memoryId, options?)GET /v1/memory/{id}
update(memoryId, content, options?)PUT /v1/memory/{id}
delete(memoryId, options?)DELETE /v1/memory/{id}
erasure(memoryId, options?)GET /v1/memory/{id}/erasure
operation(operationId)GET /v1/memory/operations/{id}
explain(memoryId, options?)GET /v1/memory/{id}/explain
share(memoryId, visibility, options?)POST /v1/memory/{id}/share
outcome(report)POST /v1/outcomes
feedback(report)POST /v1/retrieval-feedback

On this page