# TypeScript SDK

Use workflow helpers and capture policy from the TypeScript SDK without duplicating memory semantics in the UI.

- Canonical: https://docs.xmemo.dev/docs/sdk/typescript
- Locale: en-US
- Content-Locale: en-US
- Canonical-Content-Digest: 7268bbb3420d5f8d3b0c4942cb40bf08babdb44443f6a19101b07ba496e397f8
- Edition-Digest: ad77f263c9e5ca34af5d70ce44ed295f39a2fafe403225e9c8a5216c2faf4b67
- Source-Revision: sha256:d99320a7be368c26380f417c750eeb2ac4d4f054544f37014c134924ad65bad8

## Install

Install the TypeScript package and use the setup command for the hosted service. The package wraps the XMemo REST surface and the Agent Integration Kit event capture path.

```bash
npm install -g @xmemo/client
npx @xmemo/client setup --url https://xmemo.dev
```

## Construct a client

MemoryOSClient requires a baseUrl and apiKey. Keep the API key in the environment; never inline it in source or public configuration. Optional agentId and agentInstanceId values are attribution fields, not credentials.

```ts
import { MemoryOSClient } from '@xmemo/client';

const client = new MemoryOSClient({
  baseUrl: 'https://xmemo.dev',
  apiKey: process.env.XMEMO_KEY!,
});
```

## Client option types

These option types and fields are declared in packages/memory-os-js/src/index.ts. Workflow methods accept AgentWorkflowOptions, while recallContext accepts RecallContextOptions.

```ts
interface MemoryOSClientOptions {
  baseUrl: string;
  apiKey: string;
  fetchImpl?: typeof fetch;
  agentId?: string;
  agentInstanceId?: string;
}

interface AgentWorkflowOptions extends CaptureOptions {
  agentId?: string;
  agentInstanceId?: string;
  sessionId?: string;
  taskId?: string;
  status?: string;
  metadata?: Record<string, unknown>;
  memoryType?: MemoryType;
}

interface RecallContextOptions extends RecallPlanOptions {
  status?: string;
  threshold?: number;
  maxItems?: number;
  maxTokens?: number;
  limit?: number;
}
```

## taskStart / taskEnd

Both methods take content: string and an optional AgentWorkflowOptions object, and both return Promise<CaptureResult>. taskStart supplies default status started and memoryType working; taskEnd supplies completed and working.

```ts
async taskStart(content: string, options: AgentWorkflowOptions = {}): Promise<CaptureResult>
async taskEnd(content: string, options: AgentWorkflowOptions = {}): Promise<CaptureResult>

await client.taskStart('Implement MCP token repair', { source: 'codex', sessionId: 's1', taskId: 'p0' });
await client.taskEnd('MCP token repair completed', { source: 'codex', sessionId: 's1', taskId: 'p0' });
```

## recordDecision / recordBugFix

These workflow helpers use the same exact signature and return type. recordDecision and recordBugFix default the captured memory type to semantic; the event type identifies which lifecycle fact was recorded.

```ts
async recordDecision(content: string, options: AgentWorkflowOptions = {}): Promise<CaptureResult>
async recordBugFix(content: string, options: AgentWorkflowOptions = {}): Promise<CaptureResult>

await client.recordDecision('Remote agents should use network MCP, not SSH stdio', { source: 'codex', scope: 'memory-os' });
await client.recordBugFix('Fixed streamable-http token identity fallback', { source: 'codex', taskId: 'p0' });
```

## CaptureResult return shape

taskStart, taskEnd, recordDecision, recordBugFix, recordCorrection, captureEvent, and recordEvent all resolve to CaptureResult. The policy can return a decision without a memory_id when it skips a low-signal event; do not assume every call writes.

```ts
interface CaptureResult extends CaptureDecision {
  event_id: string;
  memory_id?: string;
}

interface CaptureDecision {
  shouldCapture: boolean;
  reason: string;
  action: CaptureAction;
  qualityScore: number;
  event: AgentEvent;
  candidate?: MemoryCandidate;
}
```

## recallContext signature and request

recallContext sends a POST request to /v1/recall/context and returns the server JSON object. The SDK maps camelCase options such as maxItems and maxTokens to the REST fields max_items and max_tokens; the defaults are memory_type auto, status active, threshold 0.5, and prefer_working true.

```ts
async recallContext(query: string, options: RecallContextOptions = {}): Promise<Record<string, unknown>>

const context = await client.recallContext('current task and recent decisions', {
  scope: 'memory-os',
  maxItems: 6,
  maxTokens: 1200,
  preferWorking: true,
});
```

## recallContext return shape

The TypeScript return type is intentionally Record<string, unknown> because the REST response can gain additive fields. The current server-produced core shape is a structured context object with the following fields; items and budget values are data-dependent.

```json
{
  "version": "value-density-v2",
  "query": "current task and recent decisions",
  "retrieval_plan": {
    "query": "...",
    "selected_memory_types": ["working", "semantic"]
  },
  "budget": {
    "max_items": 6,
    "max_tokens": 1200,
    "used_items": <number>,
    "used_tokens": <number>,
    "skipped_items": <number>,
    "filtered_items": <number>,
    "structured_skipped_items": <number>,
    "budget_skipped_items": <number>,
    "truncated_items": <number>,
    "candidate_items": <number>,
    "selection_strategy": "top-primary-plus-stable-value-density",
    "budget_tradeoff": <boolean>,
    "token_scope": "context_text"
  },
  "items": [<structured context item>],
  "context_text": "<assembled context>",
  "answer_status": "primary_answer_available"
}
```

## SDK recallContext versus MCP recall_context

They use the same bounded recall-context semantics and the same underlying server assembly, but they are not the same wire surface. The SDK method is a REST client call to /v1/recall/context authenticated with X-API-Key and returns structured JSON. The MCP tool is called by an MCP client and renders a public text response beginning with ### XMemo Context; use the MCP tool name with an underscore only on the MCP surface.

- SDK caller: TypeScript code calling client.recallContext(query, options).
- MCP caller: an MCP client invoking recall_context with MCP arguments.
- SDK result: Record<string, unknown> with version, retrieval_plan, budget, items, context_text, and answer_status core fields.
- MCP result: public text with Reference, Type, Location lines and a Context section; retrieval internals are not exposed.

## Workflow sequence

A production hook can recall existing context, capture task lifecycle events, and close the task. Every workflow helper still returns CaptureResult, so the caller can inspect shouldCapture, action, reason, qualityScore, event_id, and optional memory_id.

```ts
const scope = 'memory-os';
const sessionId = crypto.randomUUID();

await client.recallContext('current task and recent decisions', { scope, preferWorking: true });
await client.taskStart('Implement production adapter hook', { source: 'codex', sessionId, scope });
await client.recordDecision('Use the hosted MCP transport', { source: 'codex', sessionId, scope });
await client.taskEnd('Production adapter hook implemented', { source: 'codex', sessionId, scope });
```

## Authentication error handling

MemoryOSClient sends X-API-Key on its REST requests. Its private request method throws a plain Error on every non-2xx response with the exact prefix `Memory OS request failed: <status> ` followed by the response body. Handle 401 and 403 without logging the key or response content that may contain sensitive data. The separately exported MemoryOSHttpError belongs to MemoryOSHttpClient, not these MemoryOSClient workflow methods.

```ts
try {
  await client.recallContext('connection check', { maxItems: 1 });
} catch (error) {
  if (error instanceof Error) {
    // The message starts: Memory OS request failed: <status> <response body>
    console.error('XMemo request failed; re-check XMEMO_KEY and required scope');
  }
}
```

## Capture policy result

captureEvent normalizes the event, applies the deterministic capture policy, and only then writes. Low-signal events return shouldCapture: false without writing, while token-like text is redacted before capture. Inspect the CaptureResult instead of assuming that a helper wrote a memory.

```ts
const result = await client.recordDecision('Remote agents should use network MCP, not SSH stdio', { source: 'codex' });
if (result.shouldCapture) {
  console.log(result.event_id, result.memory_id);
} else {
  console.log(result.reason, result.action);
}
```

