@agentmemory/agentmemory 0.8.8 → 0.8.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -7,6 +7,14 @@
7
7
  Persistent memory for Claude Code, Cursor, Gemini CLI, OpenCode, and any MCP client.
8
8
  </p>
9
9
 
10
+ <p align="center">
11
+ <a href="https://gist.github.com/rohitg00/2067ab416f7bbe447c1977edaaa681e2"><img src="https://img.shields.io/badge/Viral%20GitHub%20Gist-669%20stars%20%2F%2091%20forks-FF6B35?style=for-the-badge&logo=github&logoColor=white&labelColor=1a1a1a" alt="Design doc: 686 stars / 94 forks on the gist" /></a>
12
+ </p>
13
+
14
+ <p align="center">
15
+ <strong>The gist extends Karpathy's LLM Wiki pattern with confidence scoring, lifecycle, knowledge graphs, and hybrid search.<br/> agentmemory is the implementation.</strong>
16
+ </p>
17
+
10
18
  <p align="center">
11
19
  <a href="https://www.npmjs.com/package/@agentmemory/agentmemory"><img src="https://img.shields.io/npm/v/@agentmemory/agentmemory?color=CB3837&label=npm&style=for-the-badge&logo=npm" alt="npm version" /></a>
12
20
  <a href="https://github.com/rohitg00/agentmemory/actions"><img src="https://img.shields.io/github/actions/workflow/status/rohitg00/agentmemory/ci.yml?label=tests&style=for-the-badge&logo=github" alt="CI" /></a>
@@ -295,7 +303,7 @@ Open `http://localhost:3113` to watch the memory build live.
295
303
  ### Claude Code (one block, paste it)
296
304
 
297
305
  ```
298
- Install agentmemory: run `npx @agentmemory/agentmemory` in a separate terminal to start the memory server. Then run `/plugin marketplace add rohitg00/agentmemory` and `/plugin install agentmemory` to register all 12 hooks, 4 skills, and 43 MCP tools. Verify with `curl http://localhost:3111/agentmemory/health`. The real-time viewer is at http://localhost:3113.
306
+ Install agentmemory: run `npx @agentmemory/agentmemory` in a separate terminal to start the memory server. Then run `/plugin marketplace add rohitg00/agentmemory` and `/plugin install agentmemory` the plugin registers all 12 hooks, 4 skills, AND auto-wires the `@agentmemory/mcp` stdio server via its `.mcp.json`, so you get 43 MCP tools (memory_smart_search, memory_save, memory_sessions, memory_governance_delete, etc.) without any extra config step. Verify with `curl http://localhost:3111/agentmemory/health`. The real-time viewer is at http://localhost:3113.
299
307
  ```
300
308
 
301
309
  <details>
@@ -723,6 +731,20 @@ Create `~/.agentmemory/.env`:
723
731
  # LLM provider to compress the
724
732
  # observation — expect significant
725
733
  # token spend on active sessions.
734
+ # AGENTMEMORY_INJECT_CONTEXT=false # OFF by default (#143). When on:
735
+ # - SessionStart may inject ~1-2K
736
+ # chars of project context into
737
+ # the first turn of each session
738
+ # (this is what actually reaches
739
+ # the model — Claude Code treats
740
+ # SessionStart stdout as context)
741
+ # - PreToolUse fires /agentmemory/enrich
742
+ # on every file-touching tool call
743
+ # (resource cleanup, not a token
744
+ # fix — PreToolUse stdout is debug
745
+ # log only per Claude Code docs)
746
+ # Observations are still captured via
747
+ # PostToolUse regardless of this flag.
726
748
  # GRAPH_EXTRACTION_ENABLED=false
727
749
  # CONSOLIDATION_ENABLED=true
728
750
  # LESSON_DECAY_ENABLED=true
package/dist/cli.mjs CHANGED
@@ -284,12 +284,12 @@ async function main() {
284
284
  p.intro("agentmemory");
285
285
  if (skipEngine) {
286
286
  p.log.info("Skipping engine check (--no-engine)");
287
- await import("./src-CneY0pgf.mjs");
287
+ await import("./src-65nK6f5B.mjs");
288
288
  return;
289
289
  }
290
290
  if (await isEngineRunning()) {
291
291
  p.log.success("iii-engine is running");
292
- await import("./src-CneY0pgf.mjs");
292
+ await import("./src-65nK6f5B.mjs");
293
293
  return;
294
294
  }
295
295
  if (!await startEngine()) {
@@ -333,7 +333,7 @@ async function main() {
333
333
  process.exit(1);
334
334
  }
335
335
  s.stop("iii-engine is ready");
336
- await import("./src-CneY0pgf.mjs");
336
+ await import("./src-65nK6f5B.mjs");
337
337
  }
338
338
  async function runStatus() {
339
339
  const port = getRestPort();
@@ -557,7 +557,7 @@ async function runDemo() {
557
557
  p.log.success("agentmemory is working. Point your agent at it and get back to coding.");
558
558
  }
559
559
  async function runMcp() {
560
- await import("./standalone-Qmvspmgi.mjs");
560
+ await import("./standalone-CdWMLSak.mjs");
561
561
  }
562
562
  ({
563
563
  status: runStatus,
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  //#region src/hooks/pre-tool-use.ts
3
+ const INJECT_CONTEXT = process.env["AGENTMEMORY_INJECT_CONTEXT"] === "true";
3
4
  const REST_URL = process.env["AGENTMEMORY_URL"] || "http://localhost:3111";
4
5
  const SECRET = process.env["AGENTMEMORY_SECRET"] || "";
5
6
  function authHeaders() {
@@ -8,6 +9,7 @@ function authHeaders() {
8
9
  return h;
9
10
  }
10
11
  async function main() {
12
+ if (!INJECT_CONTEXT) return;
11
13
  let input = "";
12
14
  for await (const chunk of process.stdin) input += chunk;
13
15
  let data;
@@ -1 +1 @@
1
- {"version":3,"file":"pre-tool-use.mjs","names":[],"sources":["../../src/hooks/pre-tool-use.ts"],"sourcesContent":["#!/usr/bin/env node\n\nconst REST_URL = process.env[\"AGENTMEMORY_URL\"] || \"http://localhost:3111\";\nconst SECRET = process.env[\"AGENTMEMORY_SECRET\"] || \"\";\n\nfunction authHeaders(): Record<string, string> {\n const h: Record<string, string> = { \"Content-Type\": \"application/json\" };\n if (SECRET) h[\"Authorization\"] = `Bearer ${SECRET}`;\n return h;\n}\n\nasync function main() {\n let input = \"\";\n for await (const chunk of process.stdin) {\n input += chunk;\n }\n\n let data: Record<string, unknown>;\n try {\n data = JSON.parse(input);\n } catch {\n return;\n }\n\n const toolName = data.tool_name as string;\n if (!toolName) return;\n\n const fileTools = [\"Edit\", \"Write\", \"Read\", \"Glob\", \"Grep\"];\n if (!fileTools.includes(toolName)) return;\n\n const toolInput = (data.tool_input || {}) as Record<string, unknown>;\n const files: string[] = [];\n const fileKeys =\n toolName === \"Grep\"\n ? [\"path\", \"file\"]\n : [\"file_path\", \"path\", \"file\", \"pattern\"];\n for (const key of fileKeys) {\n const val = toolInput[key];\n if (typeof val === \"string\" && val.length > 0) files.push(val);\n }\n if (files.length === 0) return;\n\n const terms: string[] = [];\n if (toolName === \"Grep\" || toolName === \"Glob\") {\n const pattern = toolInput[\"pattern\"];\n if (typeof pattern === \"string\" && pattern.length > 0) {\n terms.push(pattern);\n }\n }\n\n const sessionId = (data.session_id as string) || \"unknown\";\n\n try {\n const res = await fetch(`${REST_URL}/agentmemory/enrich`, {\n method: \"POST\",\n headers: authHeaders(),\n body: JSON.stringify({ sessionId, files, terms, toolName }),\n signal: AbortSignal.timeout(2000),\n });\n\n if (res.ok) {\n const result = (await res.json()) as { context?: string };\n if (result.context) {\n process.stdout.write(result.context);\n }\n }\n } catch {\n // don't block tool execution\n }\n}\n\nmain();\n"],"mappings":";;AAEA,MAAM,WAAW,QAAQ,IAAI,sBAAsB;AACnD,MAAM,SAAS,QAAQ,IAAI,yBAAyB;AAEpD,SAAS,cAAsC;CAC7C,MAAM,IAA4B,EAAE,gBAAgB,oBAAoB;AACxE,KAAI,OAAQ,GAAE,mBAAmB,UAAU;AAC3C,QAAO;;AAGT,eAAe,OAAO;CACpB,IAAI,QAAQ;AACZ,YAAW,MAAM,SAAS,QAAQ,MAChC,UAAS;CAGX,IAAI;AACJ,KAAI;AACF,SAAO,KAAK,MAAM,MAAM;SAClB;AACN;;CAGF,MAAM,WAAW,KAAK;AACtB,KAAI,CAAC,SAAU;AAGf,KAAI,CADc;EAAC;EAAQ;EAAS;EAAQ;EAAQ;EAAO,CAC5C,SAAS,SAAS,CAAE;CAEnC,MAAM,YAAa,KAAK,cAAc,EAAE;CACxC,MAAM,QAAkB,EAAE;CAC1B,MAAM,WACJ,aAAa,SACT,CAAC,QAAQ,OAAO,GAChB;EAAC;EAAa;EAAQ;EAAQ;EAAU;AAC9C,MAAK,MAAM,OAAO,UAAU;EAC1B,MAAM,MAAM,UAAU;AACtB,MAAI,OAAO,QAAQ,YAAY,IAAI,SAAS,EAAG,OAAM,KAAK,IAAI;;AAEhE,KAAI,MAAM,WAAW,EAAG;CAExB,MAAM,QAAkB,EAAE;AAC1B,KAAI,aAAa,UAAU,aAAa,QAAQ;EAC9C,MAAM,UAAU,UAAU;AAC1B,MAAI,OAAO,YAAY,YAAY,QAAQ,SAAS,EAClD,OAAM,KAAK,QAAQ;;CAIvB,MAAM,YAAa,KAAK,cAAyB;AAEjD,KAAI;EACF,MAAM,MAAM,MAAM,MAAM,GAAG,SAAS,sBAAsB;GACxD,QAAQ;GACR,SAAS,aAAa;GACtB,MAAM,KAAK,UAAU;IAAE;IAAW;IAAO;IAAO;IAAU,CAAC;GAC3D,QAAQ,YAAY,QAAQ,IAAK;GAClC,CAAC;AAEF,MAAI,IAAI,IAAI;GACV,MAAM,SAAU,MAAM,IAAI,MAAM;AAChC,OAAI,OAAO,QACT,SAAQ,OAAO,MAAM,OAAO,QAAQ;;SAGlC;;AAKV,MAAM"}
1
+ {"version":3,"file":"pre-tool-use.mjs","names":[],"sources":["../../src/hooks/pre-tool-use.ts"],"sourcesContent":["#!/usr/bin/env node\n\n// Pre-tool-use enrichment hook.\n//\n// THIS HOOK IS A NO-OP BY DEFAULT AS OF 0.8.10 (#143). Previously it\n// fired /agentmemory/enrich on every Edit/Write/Read/Glob/Grep tool call\n// and wrote up to 4000 chars of context to stdout. Claude Code reads\n// PreToolUse stdout and prepends it to the model's next turn, which meant\n// agentmemory was silently injecting ~1000 tokens into every tool turn\n// via the user's Claude Code session. On Claude Pro that burned entire\n// allocations in a handful of messages (@adrianricardo, #143).\n//\n// Users who explicitly want pre-tool enrichment opt in with:\n// AGENTMEMORY_INJECT_CONTEXT=true in ~/.agentmemory/.env\n// and restart Claude Code. Expect your session input token count to grow\n// proportionally with the number of file-touching tool calls per turn.\nconst INJECT_CONTEXT = process.env[\"AGENTMEMORY_INJECT_CONTEXT\"] === \"true\";\n\nconst REST_URL = process.env[\"AGENTMEMORY_URL\"] || \"http://localhost:3111\";\nconst SECRET = process.env[\"AGENTMEMORY_SECRET\"] || \"\";\n\nfunction authHeaders(): Record<string, string> {\n const h: Record<string, string> = { \"Content-Type\": \"application/json\" };\n if (SECRET) h[\"Authorization\"] = `Bearer ${SECRET}`;\n return h;\n}\n\nasync function main() {\n // Default off: exit immediately so we don't even open stdin. This keeps\n // Claude Code's tool-call hot path as cheap as possible.\n if (!INJECT_CONTEXT) return;\n\n let input = \"\";\n for await (const chunk of process.stdin) {\n input += chunk;\n }\n\n let data: Record<string, unknown>;\n try {\n data = JSON.parse(input);\n } catch {\n return;\n }\n\n const toolName = data.tool_name as string;\n if (!toolName) return;\n\n const fileTools = [\"Edit\", \"Write\", \"Read\", \"Glob\", \"Grep\"];\n if (!fileTools.includes(toolName)) return;\n\n const toolInput = (data.tool_input || {}) as Record<string, unknown>;\n const files: string[] = [];\n const fileKeys =\n toolName === \"Grep\"\n ? [\"path\", \"file\"]\n : [\"file_path\", \"path\", \"file\", \"pattern\"];\n for (const key of fileKeys) {\n const val = toolInput[key];\n if (typeof val === \"string\" && val.length > 0) files.push(val);\n }\n if (files.length === 0) return;\n\n const terms: string[] = [];\n if (toolName === \"Grep\" || toolName === \"Glob\") {\n const pattern = toolInput[\"pattern\"];\n if (typeof pattern === \"string\" && pattern.length > 0) {\n terms.push(pattern);\n }\n }\n\n const sessionId = (data.session_id as string) || \"unknown\";\n\n try {\n const res = await fetch(`${REST_URL}/agentmemory/enrich`, {\n method: \"POST\",\n headers: authHeaders(),\n body: JSON.stringify({ sessionId, files, terms, toolName }),\n signal: AbortSignal.timeout(2000),\n });\n\n if (res.ok) {\n const result = (await res.json()) as { context?: string };\n if (result.context) {\n process.stdout.write(result.context);\n }\n }\n } catch {\n // don't block tool execution\n }\n}\n\nmain();\n"],"mappings":";;AAgBA,MAAM,iBAAiB,QAAQ,IAAI,kCAAkC;AAErE,MAAM,WAAW,QAAQ,IAAI,sBAAsB;AACnD,MAAM,SAAS,QAAQ,IAAI,yBAAyB;AAEpD,SAAS,cAAsC;CAC7C,MAAM,IAA4B,EAAE,gBAAgB,oBAAoB;AACxE,KAAI,OAAQ,GAAE,mBAAmB,UAAU;AAC3C,QAAO;;AAGT,eAAe,OAAO;AAGpB,KAAI,CAAC,eAAgB;CAErB,IAAI,QAAQ;AACZ,YAAW,MAAM,SAAS,QAAQ,MAChC,UAAS;CAGX,IAAI;AACJ,KAAI;AACF,SAAO,KAAK,MAAM,MAAM;SAClB;AACN;;CAGF,MAAM,WAAW,KAAK;AACtB,KAAI,CAAC,SAAU;AAGf,KAAI,CADc;EAAC;EAAQ;EAAS;EAAQ;EAAQ;EAAO,CAC5C,SAAS,SAAS,CAAE;CAEnC,MAAM,YAAa,KAAK,cAAc,EAAE;CACxC,MAAM,QAAkB,EAAE;CAC1B,MAAM,WACJ,aAAa,SACT,CAAC,QAAQ,OAAO,GAChB;EAAC;EAAa;EAAQ;EAAQ;EAAU;AAC9C,MAAK,MAAM,OAAO,UAAU;EAC1B,MAAM,MAAM,UAAU;AACtB,MAAI,OAAO,QAAQ,YAAY,IAAI,SAAS,EAAG,OAAM,KAAK,IAAI;;AAEhE,KAAI,MAAM,WAAW,EAAG;CAExB,MAAM,QAAkB,EAAE;AAC1B,KAAI,aAAa,UAAU,aAAa,QAAQ;EAC9C,MAAM,UAAU,UAAU;AAC1B,MAAI,OAAO,YAAY,YAAY,QAAQ,SAAS,EAClD,OAAM,KAAK,QAAQ;;CAIvB,MAAM,YAAa,KAAK,cAAyB;AAEjD,KAAI;EACF,MAAM,MAAM,MAAM,MAAM,GAAG,SAAS,sBAAsB;GACxD,QAAQ;GACR,SAAS,aAAa;GACtB,MAAM,KAAK,UAAU;IAAE;IAAW;IAAO;IAAO;IAAU,CAAC;GAC3D,QAAQ,YAAY,QAAQ,IAAK;GAClC,CAAC;AAEF,MAAI,IAAI,IAAI;GACV,MAAM,SAAU,MAAM,IAAI,MAAM;AAChC,OAAI,OAAO,QACT,SAAQ,OAAO,MAAM,OAAO,QAAQ;;SAGlC;;AAKV,MAAM"}
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  //#region src/hooks/session-start.ts
3
+ const INJECT_CONTEXT = process.env["AGENTMEMORY_INJECT_CONTEXT"] === "true";
3
4
  const REST_URL = process.env["AGENTMEMORY_URL"] || "http://localhost:3111";
4
5
  const SECRET = process.env["AGENTMEMORY_SECRET"] || "";
5
6
  function authHeaders() {
@@ -29,7 +30,7 @@ async function main() {
29
30
  }),
30
31
  signal: AbortSignal.timeout(5e3)
31
32
  });
32
- if (res.ok) {
33
+ if (INJECT_CONTEXT && res.ok) {
33
34
  const result = await res.json();
34
35
  if (result.context) process.stdout.write(result.context);
35
36
  }
@@ -1 +1 @@
1
- {"version":3,"file":"session-start.mjs","names":[],"sources":["../../src/hooks/session-start.ts"],"sourcesContent":["#!/usr/bin/env node\n\nconst REST_URL = process.env[\"AGENTMEMORY_URL\"] || \"http://localhost:3111\";\nconst SECRET = process.env[\"AGENTMEMORY_SECRET\"] || \"\";\n\nfunction authHeaders(): Record<string, string> {\n const h: Record<string, string> = { \"Content-Type\": \"application/json\" };\n if (SECRET) h[\"Authorization\"] = `Bearer ${SECRET}`;\n return h;\n}\n\nasync function main() {\n let input = \"\";\n for await (const chunk of process.stdin) {\n input += chunk;\n }\n\n let data: Record<string, unknown>;\n try {\n data = JSON.parse(input);\n } catch {\n return;\n }\n\n const sessionId =\n (data.session_id as string) || `ses_${Date.now().toString(36)}`;\n const project = (data.cwd as string) || process.cwd();\n\n try {\n const res = await fetch(`${REST_URL}/agentmemory/session/start`, {\n method: \"POST\",\n headers: authHeaders(),\n body: JSON.stringify({ sessionId, project, cwd: project }),\n signal: AbortSignal.timeout(5000),\n });\n\n if (res.ok) {\n const result = (await res.json()) as { context?: string };\n if (result.context) {\n process.stdout.write(result.context);\n }\n }\n } catch {\n // silently fail -- don't block Claude Code startup\n }\n}\n\nmain();\n"],"mappings":";;AAEA,MAAM,WAAW,QAAQ,IAAI,sBAAsB;AACnD,MAAM,SAAS,QAAQ,IAAI,yBAAyB;AAEpD,SAAS,cAAsC;CAC7C,MAAM,IAA4B,EAAE,gBAAgB,oBAAoB;AACxE,KAAI,OAAQ,GAAE,mBAAmB,UAAU;AAC3C,QAAO;;AAGT,eAAe,OAAO;CACpB,IAAI,QAAQ;AACZ,YAAW,MAAM,SAAS,QAAQ,MAChC,UAAS;CAGX,IAAI;AACJ,KAAI;AACF,SAAO,KAAK,MAAM,MAAM;SAClB;AACN;;CAGF,MAAM,YACH,KAAK,cAAyB,OAAO,KAAK,KAAK,CAAC,SAAS,GAAG;CAC/D,MAAM,UAAW,KAAK,OAAkB,QAAQ,KAAK;AAErD,KAAI;EACF,MAAM,MAAM,MAAM,MAAM,GAAG,SAAS,6BAA6B;GAC/D,QAAQ;GACR,SAAS,aAAa;GACtB,MAAM,KAAK,UAAU;IAAE;IAAW;IAAS,KAAK;IAAS,CAAC;GAC1D,QAAQ,YAAY,QAAQ,IAAK;GAClC,CAAC;AAEF,MAAI,IAAI,IAAI;GACV,MAAM,SAAU,MAAM,IAAI,MAAM;AAChC,OAAI,OAAO,QACT,SAAQ,OAAO,MAAM,OAAO,QAAQ;;SAGlC;;AAKV,MAAM"}
1
+ {"version":3,"file":"session-start.mjs","names":[],"sources":["../../src/hooks/session-start.ts"],"sourcesContent":["#!/usr/bin/env node\n\n// Session-start hook.\n//\n// Always registers the session for observation tracking (so memories\n// captured on PostToolUse get attached to the right session). Only writes\n// project context to stdout — which Claude Code prepends to the very first\n// turn — when AGENTMEMORY_INJECT_CONTEXT=true. Default off as of 0.8.10\n// (#143); see pre-tool-use.ts for the full explanation.\nconst INJECT_CONTEXT = process.env[\"AGENTMEMORY_INJECT_CONTEXT\"] === \"true\";\n\nconst REST_URL = process.env[\"AGENTMEMORY_URL\"] || \"http://localhost:3111\";\nconst SECRET = process.env[\"AGENTMEMORY_SECRET\"] || \"\";\n\nfunction authHeaders(): Record<string, string> {\n const h: Record<string, string> = { \"Content-Type\": \"application/json\" };\n if (SECRET) h[\"Authorization\"] = `Bearer ${SECRET}`;\n return h;\n}\n\nasync function main() {\n let input = \"\";\n for await (const chunk of process.stdin) {\n input += chunk;\n }\n\n let data: Record<string, unknown>;\n try {\n data = JSON.parse(input);\n } catch {\n return;\n }\n\n const sessionId =\n (data.session_id as string) || `ses_${Date.now().toString(36)}`;\n const project = (data.cwd as string) || process.cwd();\n\n try {\n const res = await fetch(`${REST_URL}/agentmemory/session/start`, {\n method: \"POST\",\n headers: authHeaders(),\n body: JSON.stringify({ sessionId, project, cwd: project }),\n signal: AbortSignal.timeout(5000),\n });\n\n // Only write context to stdout when the user has explicitly opted\n // into injection. Registering the session is cheap and doesn't touch\n // Claude Code's input token window.\n if (INJECT_CONTEXT && res.ok) {\n const result = (await res.json()) as { context?: string };\n if (result.context) {\n process.stdout.write(result.context);\n }\n }\n } catch {\n // silently fail -- don't block Claude Code startup\n }\n}\n\nmain();\n"],"mappings":";;AASA,MAAM,iBAAiB,QAAQ,IAAI,kCAAkC;AAErE,MAAM,WAAW,QAAQ,IAAI,sBAAsB;AACnD,MAAM,SAAS,QAAQ,IAAI,yBAAyB;AAEpD,SAAS,cAAsC;CAC7C,MAAM,IAA4B,EAAE,gBAAgB,oBAAoB;AACxE,KAAI,OAAQ,GAAE,mBAAmB,UAAU;AAC3C,QAAO;;AAGT,eAAe,OAAO;CACpB,IAAI,QAAQ;AACZ,YAAW,MAAM,SAAS,QAAQ,MAChC,UAAS;CAGX,IAAI;AACJ,KAAI;AACF,SAAO,KAAK,MAAM,MAAM;SAClB;AACN;;CAGF,MAAM,YACH,KAAK,cAAyB,OAAO,KAAK,KAAK,CAAC,SAAS,GAAG;CAC/D,MAAM,UAAW,KAAK,OAAkB,QAAQ,KAAK;AAErD,KAAI;EACF,MAAM,MAAM,MAAM,MAAM,GAAG,SAAS,6BAA6B;GAC/D,QAAQ;GACR,SAAS,aAAa;GACtB,MAAM,KAAK,UAAU;IAAE;IAAW;IAAS,KAAK;IAAS,CAAC;GAC1D,QAAQ,YAAY,QAAQ,IAAK;GAClC,CAAC;AAKF,MAAI,kBAAkB,IAAI,IAAI;GAC5B,MAAM,SAAU,MAAM,IAAI,MAAM;AAChC,OAAI,OAAO,QACT,SAAQ,OAAO,MAAM,OAAO,QAAQ;;SAGlC;;AAKV,MAAM"}
package/dist/index.mjs CHANGED
@@ -159,6 +159,9 @@ function isConsolidationEnabled() {
159
159
  function isAutoCompressEnabled() {
160
160
  return getMergedEnv()["AGENTMEMORY_AUTO_COMPRESS"] === "true";
161
161
  }
162
+ function isContextInjectionEnabled() {
163
+ return getMergedEnv()["AGENTMEMORY_INJECT_CONTEXT"] === "true";
164
+ }
162
165
  function getConsolidationDecayDays() {
163
166
  return safeParseInt(getMergedEnv()["CONSOLIDATION_DECAY_DAYS"], 30);
164
167
  }
@@ -4127,7 +4130,7 @@ function registerAutoForgetFunction(sdk, kv) {
4127
4130
 
4128
4131
  //#endregion
4129
4132
  //#region src/version.ts
4130
- const VERSION = "0.8.8";
4133
+ const VERSION = "0.8.10";
4131
4134
 
4132
4135
  //#endregion
4133
4136
  //#region src/functions/export-import.ts
@@ -4243,7 +4246,9 @@ function registerExportImportFunction(sdk, kv) {
4243
4246
  "0.8.5",
4244
4247
  "0.8.6",
4245
4248
  "0.8.7",
4246
- "0.8.8"
4249
+ "0.8.8",
4250
+ "0.8.9",
4251
+ "0.8.10"
4247
4252
  ]).has(importData.version)) return {
4248
4253
  success: false,
4249
4254
  error: `Unsupported export version: ${importData.version}`
@@ -10108,6 +10113,9 @@ function registerRetentionFunctions(sdk, kv) {
10108
10113
  }
10109
10114
  const scores = [];
10110
10115
  const computeDecay = (createdAt) => Math.exp(-config.lambda * ((Date.now() - new Date(createdAt).getTime()) / (1e3 * 60 * 60 * 24)));
10116
+ const pendingWrites = [];
10117
+ let episodicScored = 0;
10118
+ let semanticScored = 0;
10111
10119
  for (const mem of memories) {
10112
10120
  if (!mem.isLatest) continue;
10113
10121
  const log = logsById.get(mem.id) ?? emptyAccessLog(mem.id);
@@ -10117,6 +10125,7 @@ function registerRetentionFunctions(sdk, kv) {
10117
10125
  const score = Math.min(1, salience * temporalDecay + reinforcementBoost);
10118
10126
  const entry = {
10119
10127
  memoryId: mem.id,
10128
+ source: "episodic",
10120
10129
  score,
10121
10130
  salience,
10122
10131
  temporalDecay,
@@ -10125,7 +10134,8 @@ function registerRetentionFunctions(sdk, kv) {
10125
10134
  accessCount: log.count
10126
10135
  };
10127
10136
  scores.push(entry);
10128
- await kv.set(KV.retentionScores, mem.id, entry);
10137
+ pendingWrites.push([mem.id, entry]);
10138
+ episodicScored++;
10129
10139
  }
10130
10140
  for (const sem of semanticMems) {
10131
10141
  const log = logsById.get(sem.id) ?? emptyAccessLog(sem.id);
@@ -10148,6 +10158,7 @@ function registerRetentionFunctions(sdk, kv) {
10148
10158
  const score = Math.min(1, salience * temporalDecay + reinforcementBoost);
10149
10159
  const entry = {
10150
10160
  memoryId: sem.id,
10161
+ source: "semantic",
10151
10162
  score,
10152
10163
  salience,
10153
10164
  temporalDecay,
@@ -10156,8 +10167,10 @@ function registerRetentionFunctions(sdk, kv) {
10156
10167
  accessCount: effectiveCount
10157
10168
  };
10158
10169
  scores.push(entry);
10159
- await kv.set(KV.retentionScores, sem.id, entry);
10170
+ pendingWrites.push([sem.id, entry]);
10171
+ semanticScored++;
10160
10172
  }
10173
+ await Promise.all(pendingWrites.map(([id, entry]) => kv.set(KV.retentionScores, id, entry)));
10161
10174
  scores.sort((a, b) => b.score - a.score);
10162
10175
  const tiers = {
10163
10176
  hot: scores.filter((s) => s.score >= config.tierThresholds.hot).length,
@@ -10169,6 +10182,13 @@ function registerRetentionFunctions(sdk, kv) {
10169
10182
  total: scores.length,
10170
10183
  ...tiers
10171
10184
  });
10185
+ if (scores.length > 0) await recordAudit(kv, "retention_score", "mem::retention-score", [], {
10186
+ total: scores.length,
10187
+ episodic: episodicScored,
10188
+ semantic: semanticScored,
10189
+ tiers,
10190
+ config
10191
+ });
10172
10192
  return {
10173
10193
  success: true,
10174
10194
  total: scores.length,
@@ -10194,21 +10214,53 @@ function registerRetentionFunctions(sdk, kv) {
10194
10214
  }))
10195
10215
  };
10196
10216
  let evicted = 0;
10217
+ let evictedEpisodic = 0;
10218
+ let evictedSemantic = 0;
10219
+ const evictedIds = [];
10197
10220
  for (const candidate of candidates) try {
10198
- await kv.delete(KV.memories, candidate.memoryId);
10221
+ let scope;
10222
+ let resolvedSource;
10223
+ if (candidate.source === "semantic") {
10224
+ scope = KV.semantic;
10225
+ resolvedSource = "semantic";
10226
+ } else if (candidate.source === "episodic") {
10227
+ scope = KV.memories;
10228
+ resolvedSource = "episodic";
10229
+ } else if (await kv.get(KV.memories, candidate.memoryId) !== null) {
10230
+ scope = KV.memories;
10231
+ resolvedSource = "episodic";
10232
+ } else {
10233
+ scope = KV.semantic;
10234
+ resolvedSource = "semantic";
10235
+ }
10236
+ await kv.delete(scope, candidate.memoryId);
10199
10237
  await kv.delete(KV.retentionScores, candidate.memoryId);
10200
10238
  await deleteAccessLog(kv, candidate.memoryId);
10201
10239
  evicted++;
10240
+ evictedIds.push(candidate.memoryId);
10241
+ if (resolvedSource === "semantic") evictedSemantic++;
10242
+ else evictedEpisodic++;
10202
10243
  } catch {
10203
10244
  continue;
10204
10245
  }
10246
+ if (evicted > 0) await recordAudit(kv, "delete", "mem::retention-evict", evictedIds, {
10247
+ threshold,
10248
+ evicted,
10249
+ evictedEpisodic,
10250
+ evictedSemantic,
10251
+ reason: "retention score below threshold"
10252
+ });
10205
10253
  ctx.logger.info("Retention-based eviction complete", {
10206
10254
  evicted,
10255
+ evictedEpisodic,
10256
+ evictedSemantic,
10207
10257
  threshold
10208
10258
  });
10209
10259
  return {
10210
10260
  success: true,
10211
- evicted
10261
+ evicted,
10262
+ evictedEpisodic,
10263
+ evictedSemantic
10212
10264
  };
10213
10265
  });
10214
10266
  }
@@ -15081,6 +15133,8 @@ async function main() {
15081
15133
  console.log(`[agentmemory] Consolidation pipeline: registered (CONSOLIDATION_ENABLED=${isConsolidationEnabled() ? "true" : "false"})`);
15082
15134
  if (isAutoCompressEnabled()) console.log(`[agentmemory] WARNING: AGENTMEMORY_AUTO_COMPRESS=true — every PostToolUse observation will be sent to your LLM provider for compression. This spends API tokens proportional to your session tool-use frequency (see #138). Set AGENTMEMORY_AUTO_COMPRESS=false to disable.`);
15083
15135
  else console.log(`[agentmemory] Auto-compress: OFF (default, #138) — observations indexed via zero-LLM synthetic compression. Set AGENTMEMORY_AUTO_COMPRESS=true to opt-in to LLM-powered summaries (uses your API key).`);
15136
+ if (isContextInjectionEnabled()) console.log(`[agentmemory] WARNING: AGENTMEMORY_INJECT_CONTEXT=true — the PreToolUse and SessionStart hooks will inject up to ~4000 chars of memory context into every tool turn. On Claude Pro this burns session tokens proportional to your tool-call frequency (see #143). Set AGENTMEMORY_INJECT_CONTEXT=false to disable.`);
15137
+ else console.log(`[agentmemory] Context injection: OFF (default, #143) — hooks capture observations but do not inject context into Claude Code's conversation. Set AGENTMEMORY_INJECT_CONTEXT=true to opt-in (warning: expect your Claude Pro allocation to drain faster).`);
15084
15138
  const teamConfig = loadTeamConfig();
15085
15139
  if (teamConfig) {
15086
15140
  registerTeamFunction(sdk, kv, teamConfig);