{"_id":"@cortadel/claude-agent-sdk","name":"@cortadel/claude-agent-sdk","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@cortadel/claude-agent-sdk","version":"0.1.0","description":"Cortadel long-term memory for the Claude Agent SDK — in-process MCP memory tools plus automatic recall/capture hooks.","license":"Apache-2.0","type":"module","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"types":"./dist/index.d.ts","engines":{"node":">=20"},"homepage":"https://cortadel.ai","bugs":{"url":"https://github.com/cortadel/cortadel/issues"},"repository":{"type":"git","url":"git+https://github.com/cortadel/cortadel.git","directory":"integrations/claude-agent-sdk"},"keywords":["claude","claude-agent-sdk","anthropic","cortadel","ai","memory","agents","mcp","knowledge-graph","llm","rag"],"publishConfig":{"access":"public","provenance":true},"dependencies":{"@cortadel/sdk":"^1.0.0"},"peerDependencies":{"@anthropic-ai/claude-agent-sdk":">=0.3.0 <0.4.0","zod":"^4.0.0"},"devDependencies":{"@anthropic-ai/claude-agent-sdk":"0.3.231","@modelcontextprotocol/sdk":"^1.29.0","@types/node":"^22.20.1","typescript":"^7.0.2","vitest":"^4.1.10","zod":"^4.0.0"},"scripts":{"build":"tsc","typecheck":"tsc --noEmit && tsc -p tsconfig.test.json","test":"vitest run"},"_id":"@cortadel/claude-agent-sdk@0.1.0","_integrity":"sha512-IYYBzi/G0rLgIM73K9EycvxqOKRAy8f8dHu3nZH2ejUzNDmGPKALiwiKmr6OLFIjYJFJSkktoMnJxb7o2BueLw==","_resolved":"/tmp/4b25170bd27dde0e9c1e7633e9f3edef/cortadel-claude-agent-sdk-0.1.0.tgz","_from":"file:cortadel-claude-agent-sdk-0.1.0.tgz","_nodeVersion":"24.19.0","_npmVersion":"11.17.0","dist":{"integrity":"sha512-IYYBzi/G0rLgIM73K9EycvxqOKRAy8f8dHu3nZH2ejUzNDmGPKALiwiKmr6OLFIjYJFJSkktoMnJxb7o2BueLw==","shasum":"0b1cd36ed3b1134ccacbf619fb1c0b05414553d1","tarball":"https://registry.npmjs.org/@cortadel/claude-agent-sdk/-/claude-agent-sdk-0.1.0.tgz","fileCount":18,"unpackedSize":97915,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@cortadel%2fclaude-agent-sdk@0.1.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCID/e0QcDPlHA3oWM8TgELOFya31nCnBOTkvCs1w/w4Y/AiBetyRS/QsumiLMvYvit0pPqN11nfIxfYXmKGgIzs7ReA=="}]},"_npmUser":{"name":"serhii-seletskyi","email":"admin@cortadel.com"},"directories":{},"maintainers":[{"name":"serhii-seletskyi","email":"admin@cortadel.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/claude-agent-sdk_0.1.0_1786714058287_0.8771055884663295"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-14T13:27:38.092Z","0.1.0":"2026-08-14T13:27:38.427Z","modified":"2026-08-14T13:27:38.840Z"},"maintainers":[{"name":"serhii-seletskyi","email":"admin@cortadel.com"}],"description":"Cortadel long-term memory for the Claude Agent SDK — in-process MCP memory tools plus automatic recall/capture hooks.","homepage":"https://cortadel.ai","keywords":["claude","claude-agent-sdk","anthropic","cortadel","ai","memory","agents","mcp","knowledge-graph","llm","rag"],"repository":{"type":"git","url":"git+https://github.com/cortadel/cortadel.git","directory":"integrations/claude-agent-sdk"},"bugs":{"url":"https://github.com/cortadel/cortadel/issues"},"license":"Apache-2.0","readme":"# Cortadel × Claude Agent SDK\n\n[Cortadel](https://cortadel.ai) is self-hosted long-term temporal graph memory for AI agents — a\nbi-temporal graph store with hybrid BM25 + vector search. The\n[Claude Agent SDK](https://github.com/anthropics/claude-agent-sdk-typescript) gives an agent a\nsession; it does not give it a memory that outlives one. This package closes that gap through the\nSDK's own two extension points: an **in-process MCP server** carrying memory tools the agent can\ncall on purpose, and **`UserPromptSubmit` / `Stop` hooks** that recall and persist without being\nasked. Both come off one object, and memory degrades rather than breaking your agent — if the\nCortadel server is unreachable the hooks return empty and the turn proceeds untouched, with the\nfailure handed to your `onError` callback (or logged). Flip `throwOnError: true` when you would\nrather know loudly.\n\n## Install\n\n```bash\nnpm install @cortadel/claude-agent-sdk\n# or: pnpm add @cortadel/claude-agent-sdk\n```\n\n`@anthropic-ai/claude-agent-sdk` and `zod` are **peer dependencies** — you almost certainly already\nhave both, since the agent SDK itself peers on zod. If your package manager does not install peers\nautomatically:\n\n```bash\nnpm install @anthropic-ai/claude-agent-sdk zod\n```\n\n## Quickstart\n\n```ts\nimport { query } from \"@anthropic-ai/claude-agent-sdk\";\nimport { CortadelMemory } from \"@cortadel/claude-agent-sdk\";\n\n// One CortadelMemory per user: a Cortadel client is bound to one user id at construction.\nconst memory = new CortadelMemory({\n  baseUrl: \"http://localhost:3001\",\n  userId: \"e2e-alice\",\n});\n\n// apply() returns a COPY of the options with the memory tools and both hooks merged in.\nconst options = memory.apply({ model: \"claude-sonnet-4-5\" });\n\nfor await (const message of query({ prompt: \"What did we decide about the schema?\", options })) {\n  if (message.type === \"result\" && message.subtype === \"success\") {\n    console.log(message.result);\n  }\n}\n```\n\nThat single `apply()` call is equivalent to writing all of this by hand:\n\n```ts\nconst options = {\n  model: \"claude-sonnet-4-5\",\n  mcpServers: { cortadel: memory.mcpServer },\n  allowedTools: [\"mcp__cortadel__search_memory\", \"mcp__cortadel__add_memories\"],\n  hooks: memory.hooks(),\n};\n```\n\nTake just one half with `memory.apply(options, { autoMemory: false })` (tools only — the agent\ndecides when to remember) or `memory.apply(options, { tools: false })` (hooks only — memory the\nagent never has to think about).\n\n## What you get\n\n### Memory tools (in-process MCP server)\n\nDefined with the SDK's `tool()` helper and packaged by `createSdkMcpServer()`, so they run in your\nprocess — no subprocess, no IPC.\n\n| Tool | Fully qualified name | Arguments | Does |\n|---|---|---|---|\n| `search_memory` | `mcp__cortadel__search_memory` | `query` (required), `top_k` | Hybrid BM25 + vector search over this user's memories. Annotated `readOnlyHint`, so Claude may batch it with other read-only calls. |\n| `add_memories` | `mcp__cortadel__add_memories` | `text` (required), `memory_type` | Stores a durable fact. The result reports Cortadel's pipeline `event` (`ADD`, `SKIP_DUPLICATE`, `SUPERSEDE`, …) — a successful call does not always mean a new memory. |\n\nThe `mcp__<server>__<tool>` prefix comes from the key in `mcpServers`; change it with `serverName`.\nFailures come back to the model as `isError` results with a readable message, so the agent loop\ncontinues. The raw definitions are on `memory.tools` if you would rather register them on an MCP\nserver of your own.\n\n### Automatic memory (hooks)\n\n| Hook event | What it does |\n|---|---|\n| `UserPromptSubmit` | Searches Cortadel with the submitted prompt and injects the hits as `hookSpecificOutput.additionalContext`. Skips prompts shorter than `minPromptChars` and anything starting with `/` or `!`. Within a session it never injects the same memory twice (`dedupeInjections`), so a long conversation does not keep re-spending context on facts the model has already seen. |\n| `Stop` | Reads the finished turn back off the session transcript and persists it with `addConversation`, tagged and scoped by `sessionId`, `project` and `transcriptPath`. Cortadel distils the durable facts server-side. Guards against re-entry via `stop_hook_active`. |\n\nThe `Stop` hook has to read the transcript because `StopHookInput` carries no user message. It uses\nthe SDK's own `getSessionMessages()` (which rebuilds the conversation chain and drops meta/sidechain\nentries), falls back to parsing the JSONL at `transcript_path` directly, and — only if both come up\nempty — pairs `StopHookInput.last_assistant_message` with the prompt this session last submitted.\nThe transcript paths are preferred because they carry message uuids, which Cortadel keeps as pointer\nanchors on every extracted fact.\n\n### Relationship to the `cortadel-memory` plugin\n\nThis repo also ships [`cortadel-plugin/`](../../cortadel-plugin), a Claude Code **plugin** that does\nthe same two things via command hooks in the CLI. Use the plugin for interactive Claude Code; use\nthis package when you are building an agent programmatically. They are independent — do not run both\nagainst the same session or every turn gets captured twice.\n\n## Configuration\n\n`new CortadelMemory(options)` takes one object. `baseUrl` and `userId` are required; everything else\nis optional.\n\n| Option | Type | Default | Meaning |\n|---|---|---|---|\n| `baseUrl` | `string` | *required* | Cortadel server, e.g. `http://localhost:3001` or `https://app.cortadel.ai`. |\n| `userId` | `string` | *required* | Namespace anchor. Every memory read or written belongs to it. |\n| `apiKey` | `string` | — | Sent as `Authorization: Bearer`. Omit when the server runs with auth disabled. |\n| `client` | `CortadelMemoryClient` | — | Supply a Cortadel client you already own, instead of `baseUrl`/`userId`/`apiKey`. Also reachable as `CortadelMemory.fromClient(client, tuning)`. |\n| `appName` | `string` | `\"@cortadel/claude-agent-sdk\"` | Recorded for access logging on searches, and stamped as the creating app on tool-written memories. |\n| `serverName` | `string` | `\"cortadel\"` | MCP server key; sets the `mcp__<serverName>__*` tool prefix. |\n| `project` | `string` | — | Project scope for captured conversations. Defaults to the basename of the session's `cwd`. |\n| `tags` | `readonly string[]` | `[\"claude-agent-sdk\"]` | Tags applied to every fact extracted from a captured conversation. |\n| `topK` | `number` | `5` | Memories fetched per recall, and the `search_memory` tool's default when the model does not pass its own `top_k`. One knob serves both paths, so the tool defaults to 5 rather than the Cortadel SDK's own `SearchOptions` default of 10 — automatic injection spends context on every turn, and 5 is the budget that survives a long session. |\n| `rerank` | `string` | — | Set to `\"cross_encoder\"` to rerank recall hits. Off by default — CPU reranking is too slow to sit in front of a prompt. |\n| `scopeRecallToSession` | `boolean` | `false` | Restrict automatic recall to the current SDK session (the hook's `session_id`). Off by default: recalling across sessions is the whole point of long-term memory. No effect on the `search_memory` tool, whose handler receives tool arguments and no session id. |\n| `minPromptChars` | `number` | `10` | Prompts shorter than this skip recall. |\n| `maxContextChars` | `number` | `4000` | Ceiling on the injected memory block. |\n| `captureMaxChars` | `number` | `16000` | Ceiling on the conversation text sent to `addConversation`. |\n| `recallTimeoutMs` | `number` | `10000` | Milliseconds the `UserPromptSubmit` hook may spend before giving up. |\n| `captureTimeoutMs` | `number` | `60000` | Milliseconds the `Stop` hook may spend before giving up. |\n| `awaitPersist` | `boolean` | `true` | Whether the `Stop` hook waits for the write before returning. **Defaults to `true`, against the repo-wide fire-and-forget default, because `Stop` fires as the turn winds down — hand the write to a floating promise and `query()` can close (taking its transports, and often the process, with it) before the write lands, silently losing the turn.** Set it to `false` only if you keep the process alive yourself, and call `flush()` to drain what is still in flight. |\n| `dedupeInjections` | `boolean` | `true` | Never inject the same memory twice in one session. |\n| `isAgentMemory` | `boolean` | `false` | Extract facts about the *assistant* instead of the user. |\n| `throwOnError` | `boolean` | `false` | Fail open: a memory failure inside a hook is swallowed and the turn proceeds. Set it to `true` to have the hook rethrow instead — what you want in tests and CI. |\n| `onError` | `(error: unknown) => void` | — | Called with the error on every memory failure, from either path. |\n\nEach matcher's `HookCallbackMatcher.timeout` (which is in **seconds**) is derived from\n`recallTimeoutMs` / `captureTimeoutMs` so the CLI-side timeout never fires before this package's own.\n\n### Error handling\n\nA Cortadel outage must never take the agent down, so `throwOnError` defaults to `false`.\n\n```ts\nconst memory = new CortadelMemory({\n  baseUrl: \"http://localhost:3001\",\n  userId: \"e2e-alice\",\n  onError: (error) => logger.warn({ error }, \"cortadel\"),\n});\n```\n\nThe three paths differ, deliberately:\n\n- **Hooks** (`UserPromptSubmit`, `Stop`) swallow the failure and return an empty result, so the turn\n  proceeds untouched. With `throwOnError: true` they rethrow instead.\n- **Tools** always report the failure to the model as an `isError` result with a readable message —\n  that surfaces it rather than swallowing it, so `throwOnError` does not change tool behaviour.\n- **A background write** (`awaitPersist: false`) can only be observed: the hook that started it\n  returned long ago, so there is nothing left to throw into.\n\n`onError` sees the error in all three cases. When it is undefined **and** the failure was swallowed,\na warning goes to `console.warn` instead. A callback that itself throws is caught and logged — it can\nnever break a turn.\n\n### Alternative constructors\n\n```ts\n// From the same env vars the cortadel-memory plugin reads:\n//   CORTADEL_URL, CORTADEL_USER_ID, and optionally CORTADEL_API_KEY / CORTADEL_CLIENT_NAME\nconst memory = CortadelMemory.fromEnv({ topK: 8 });\n\n// From a Cortadel client you already own (shared, wrapped, or a test double).\nconst memory = CortadelMemory.fromClient(myCortadelClient, { topK: 8 });\n```\n\nBoth accept every tuning option in the table above. `await memory.flush()` waits for any\nfire-and-forget write to land, which is what makes `awaitPersist: false` safe when you do want it.\nThe Cortadel TypeScript client holds no long-lived resource and has no `close()`, so there is nothing\nelse to release.\n\n## Examples\n\nBoth need a running Cortadel server, plus the Claude Code CLI and Anthropic credentials that the\nagent SDK itself requires. They import the package by its published name, so they are copy-pasteable\ninto your own project as they stand.\n\n- [`examples/auto-memory.ts`](./examples/auto-memory.ts) — hooks only. Run it twice: the second run\n  answers from memory in a brand-new session.\n- [`examples/memory-tools.ts`](./examples/memory-tools.ts) — tools only, printing every memory tool\n  call the model chooses to make.\n\n```bash\n# From a checkout: the examples import the package by name, which Node resolves through\n# `exports` to `dist/`, so build once first.\npnpm build && pnpm dlx tsx examples/auto-memory.ts\n```\n\n## Running the tests\n\nOffline unit tests — no network, no Cortadel server, no API keys. The Cortadel boundary is faked; the\nagent SDK is not, so the tools are driven through the real in-process MCP server over an in-memory\ntransport.\n\n```bash\ncd integrations/claude-agent-sdk\npnpm install\npnpm test          # vitest run\npnpm typecheck     # tsc --noEmit && tsc -p tsconfig.test.json — src, test AND examples\npnpm build         # tsc, src only: dist/ holds the shipped surface and nothing else\n```\n\n`tsconfig.test.json` maps `@cortadel/claude-agent-sdk` to `./src/index.ts`, so the examples are\ntype-checked against this folder's source rather than a stale `dist/` — an example that drifts from\nthe API fails `pnpm typecheck`.\n\n## Requirements\n\n- Node.js ≥ 20\n- `@anthropic-ai/claude-agent-sdk` ≥ 0.3.0 < 0.4.0 (developed and verified against **0.3.231**) and\n  `zod` ^4 — both peer dependencies. The agent SDK itself needs the Claude Code CLI and Anthropic\n  credentials to run an agent; the tests here need neither.\n- `@cortadel/sdk` ^1.0.0 (a direct dependency, installed for you)\n- A running Cortadel server: hosted at `https://app.cortadel.ai`, or self-hosted with\n  `docker compose up` → `http://localhost:3001`.\n\n## Links\n\n- [github.com/cortadel/cortadel](https://github.com/cortadel/cortadel)\n- [cortadel.ai](https://cortadel.ai)\n\nApache-2.0.\n","readmeFilename":"README.md","_rev":"1-6e56051505a02d9a019528a7ef1ced17"}