@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 +23 -1
- package/dist/cli.mjs +4 -4
- package/dist/hooks/pre-tool-use.mjs +2 -0
- package/dist/hooks/pre-tool-use.mjs.map +1 -1
- package/dist/hooks/session-start.mjs +2 -1
- package/dist/hooks/session-start.mjs.map +1 -1
- package/dist/index.mjs +60 -6
- package/dist/index.mjs.map +1 -1
- package/dist/{src-CneY0pgf.mjs → src-65nK6f5B.mjs} +58 -7
- package/dist/src-65nK6f5B.mjs.map +1 -0
- package/dist/{standalone-Qmvspmgi.mjs → standalone-CdWMLSak.mjs} +59 -13
- package/dist/standalone-CdWMLSak.mjs.map +1 -0
- package/dist/standalone.d.mts.map +1 -1
- package/dist/standalone.mjs +58 -12
- package/dist/standalone.mjs.map +1 -1
- package/dist/{tools-registry-BuDo4gKj.mjs → tools-registry-DXnvw9ZI.mjs} +6 -3
- package/dist/tools-registry-DXnvw9ZI.mjs.map +1 -0
- package/package.json +1 -1
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/.mcp.json +8 -0
- package/plugin/scripts/pre-tool-use.mjs +2 -0
- package/plugin/scripts/pre-tool-use.mjs.map +1 -1
- package/plugin/scripts/session-start.mjs +2 -1
- package/plugin/scripts/session-start.mjs.map +1 -1
- package/plugin/skills/forget/SKILL.md +11 -17
- package/plugin/skills/recall/SKILL.md +9 -6
- package/plugin/skills/remember/SKILL.md +14 -14
- package/plugin/skills/session-history/SKILL.md +10 -9
- package/dist/src-CneY0pgf.mjs.map +0 -1
- package/dist/standalone-Qmvspmgi.mjs.map +0 -1
- package/dist/tools-registry-BuDo4gKj.mjs.map +0 -1
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`
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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":";;
|
|
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":";;
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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);
|