@agentmemory/agentmemory 0.9.30 → 0.9.31
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/.env.example +39 -33
- package/AGENTS.md +8 -8
- package/README.md +173 -558
- package/dist/.env.example +39 -33
- package/dist/cli.d.mts.map +1 -1
- package/dist/cli.mjs +1741 -966
- package/dist/cli.mjs.map +1 -1
- package/dist/compression-recovery-CnhLQC6N.mjs +5912 -0
- package/dist/compression-recovery-CnhLQC6N.mjs.map +1 -0
- package/dist/{connect-B4_ca6BD.mjs → connect-BSLIAMpw.mjs} +874 -267
- package/dist/connect-BSLIAMpw.mjs.map +1 -0
- package/dist/hooks/_capture.d.mts +5 -2
- package/dist/hooks/_capture.d.mts.map +1 -1
- package/dist/hooks/_capture.mjs +55 -19
- package/dist/hooks/_capture.mjs.map +1 -1
- package/dist/hooks/notification.mjs +20 -6
- package/dist/hooks/notification.mjs.map +1 -1
- package/dist/hooks/post-commit.mjs +25 -5
- package/dist/hooks/post-commit.mjs.map +1 -1
- package/dist/hooks/post-tool-failure.mjs +12 -4
- package/dist/hooks/post-tool-failure.mjs.map +1 -1
- package/dist/hooks/post-tool-use.mjs +12 -4
- package/dist/hooks/post-tool-use.mjs.map +1 -1
- package/dist/hooks/pre-compact.mjs +37 -10
- package/dist/hooks/pre-compact.mjs.map +1 -1
- package/dist/hooks/pre-tool-use.mjs +25 -5
- package/dist/hooks/pre-tool-use.mjs.map +1 -1
- package/dist/hooks/prompt-submit.mjs +12 -4
- package/dist/hooks/prompt-submit.mjs.map +1 -1
- package/dist/hooks/session-end.mjs +47 -16
- package/dist/hooks/session-end.mjs.map +1 -1
- package/dist/hooks/session-start.mjs +37 -10
- package/dist/hooks/session-start.mjs.map +1 -1
- package/dist/hooks/stop.mjs +74 -72
- package/dist/hooks/stop.mjs.map +1 -1
- package/dist/hooks/subagent-start.mjs +13 -5
- package/dist/hooks/subagent-start.mjs.map +1 -1
- package/dist/hooks/subagent-stop.mjs +18 -6
- package/dist/hooks/subagent-stop.mjs.map +1 -1
- package/dist/hooks/task-completed.mjs +13 -5
- package/dist/hooks/task-completed.mjs.map +1 -1
- package/dist/{image-refs-T_urbwHM.mjs → image-refs-Cyta4Lf9.mjs} +2 -2
- package/dist/{image-refs-T_urbwHM.mjs.map → image-refs-Cyta4Lf9.mjs.map} +1 -1
- package/dist/index.mjs +8539 -4203
- package/dist/index.mjs.map +1 -1
- package/dist/integrations/dsh.d.mts +55 -0
- package/dist/integrations/dsh.d.mts.map +1 -0
- package/dist/integrations/dsh.mjs +1145 -0
- package/dist/integrations/dsh.mjs.map +1 -0
- package/dist/{keyed-mutex-DchXsyfP.mjs → keyed-mutex-DCUh7PG-.mjs} +58 -9
- package/dist/keyed-mutex-DCUh7PG-.mjs.map +1 -0
- package/dist/{secret-store-BnY3A50C.mjs → secret-store-D9GwO0xp.mjs} +535 -434
- package/dist/secret-store-D9GwO0xp.mjs.map +1 -0
- package/dist/session-arguments-zxsbbd8y.mjs +113 -0
- package/dist/session-arguments-zxsbbd8y.mjs.map +1 -0
- package/dist/{src-Jsq9LCeH.mjs → src-DaMtDzt_.mjs} +4087 -3788
- package/dist/src-DaMtDzt_.mjs.map +1 -0
- package/dist/{standalone-DKCdrf1d.mjs → standalone-BlqshyZ5.mjs} +213 -56
- package/dist/standalone-BlqshyZ5.mjs.map +1 -0
- package/dist/standalone.d.mts +1 -1
- package/dist/standalone.d.mts.map +1 -1
- package/dist/standalone.mjs +626 -73
- package/dist/standalone.mjs.map +1 -1
- package/dist/viewer/index.html +288 -140
- package/integrations/hermes/README.md +84 -0
- package/integrations/hermes/__init__.py +669 -0
- package/integrations/hermes/plugin.yaml +27 -0
- package/integrations/omp/LICENSE +190 -0
- package/integrations/omp/README.md +76 -0
- package/integrations/omp/capture.ts +1 -0
- package/integrations/omp/dist/index.mjs +1450 -0
- package/integrations/omp/index.ts +488 -0
- package/integrations/omp/package.json +13 -0
- package/integrations/omp/security.ts +67 -0
- package/integrations/omp/skills/agentmemory-agents/REFERENCE.md +32 -0
- package/integrations/omp/skills/agentmemory-agents/SKILL.md +48 -0
- package/integrations/omp/skills/agentmemory-agents/TROUBLESHOOTING.md +70 -0
- package/integrations/omp/skills/agentmemory-architecture/SKILL.md +33 -0
- package/integrations/omp/skills/agentmemory-architecture/TROUBLESHOOTING.md +70 -0
- package/integrations/omp/skills/agentmemory-config/REFERENCE.md +90 -0
- package/integrations/omp/skills/agentmemory-config/SKILL.md +78 -0
- package/integrations/omp/skills/agentmemory-config/TROUBLESHOOTING.md +70 -0
- package/integrations/omp/skills/agentmemory-hooks/REFERENCE.md +20 -0
- package/integrations/omp/skills/agentmemory-hooks/SKILL.md +49 -0
- package/integrations/omp/skills/agentmemory-hooks/TROUBLESHOOTING.md +70 -0
- package/integrations/omp/skills/agentmemory-mcp-tools/REFERENCE.md +66 -0
- package/integrations/omp/skills/agentmemory-mcp-tools/SKILL.md +42 -0
- package/integrations/omp/skills/agentmemory-mcp-tools/TROUBLESHOOTING.md +70 -0
- package/integrations/omp/skills/agentmemory-rest-api/REFERENCE.md +158 -0
- package/integrations/omp/skills/agentmemory-rest-api/SKILL.md +49 -0
- package/integrations/omp/skills/agentmemory-rest-api/TROUBLESHOOTING.md +70 -0
- package/integrations/omp/skills/commit-context/EXAMPLES.md +71 -0
- package/integrations/omp/skills/commit-context/SKILL.md +64 -0
- package/integrations/omp/skills/commit-context/TROUBLESHOOTING.md +70 -0
- package/integrations/omp/skills/commit-history/EXAMPLES.md +71 -0
- package/integrations/omp/skills/commit-history/SKILL.md +62 -0
- package/integrations/omp/skills/commit-history/TROUBLESHOOTING.md +70 -0
- package/integrations/omp/skills/forget/EXAMPLES.md +40 -0
- package/integrations/omp/skills/forget/SKILL.md +73 -0
- package/integrations/omp/skills/forget/TROUBLESHOOTING.md +70 -0
- package/integrations/omp/skills/handoff/EXAMPLES.md +57 -0
- package/integrations/omp/skills/handoff/SKILL.md +75 -0
- package/integrations/omp/skills/handoff/TROUBLESHOOTING.md +70 -0
- package/integrations/omp/skills/lesson/SKILL.md +64 -0
- package/integrations/omp/skills/lesson/TROUBLESHOOTING.md +70 -0
- package/integrations/omp/skills/memory-discipline/SKILL.md +65 -0
- package/integrations/omp/skills/memory-discipline/TROUBLESHOOTING.md +70 -0
- package/integrations/omp/skills/recall/EXAMPLES.md +74 -0
- package/integrations/omp/skills/recall/SKILL.md +74 -0
- package/integrations/omp/skills/recall/TROUBLESHOOTING.md +70 -0
- package/integrations/omp/skills/recap/EXAMPLES.md +62 -0
- package/integrations/omp/skills/recap/SKILL.md +68 -0
- package/integrations/omp/skills/recap/TROUBLESHOOTING.md +70 -0
- package/integrations/omp/skills/remember/EXAMPLES.md +67 -0
- package/integrations/omp/skills/remember/SKILL.md +66 -0
- package/integrations/omp/skills/remember/TROUBLESHOOTING.md +70 -0
- package/integrations/omp/skills/session-history/EXAMPLES.md +59 -0
- package/integrations/omp/skills/session-history/SKILL.md +67 -0
- package/integrations/omp/skills/session-history/TROUBLESHOOTING.md +70 -0
- package/integrations/omp/skills/write-agentmemory-skill/SKILL.md +41 -0
- package/integrations/omp/skills/write-agentmemory-skill/TROUBLESHOOTING.md +70 -0
- package/integrations/openclaw/LICENSE +190 -0
- package/integrations/openclaw/README.md +88 -0
- package/integrations/openclaw/openclaw.plugin.json +71 -0
- package/integrations/openclaw/package.json +38 -0
- package/integrations/openclaw/plugin.mjs +425 -0
- package/integrations/openclaw/plugin.yaml +26 -0
- package/integrations/openclaw/skills/agentmemory-agents/REFERENCE.md +32 -0
- package/integrations/openclaw/skills/agentmemory-agents/SKILL.md +48 -0
- package/integrations/openclaw/skills/agentmemory-agents/TROUBLESHOOTING.md +70 -0
- package/integrations/openclaw/skills/agentmemory-architecture/SKILL.md +33 -0
- package/integrations/openclaw/skills/agentmemory-architecture/TROUBLESHOOTING.md +70 -0
- package/integrations/openclaw/skills/agentmemory-config/REFERENCE.md +90 -0
- package/integrations/openclaw/skills/agentmemory-config/SKILL.md +78 -0
- package/integrations/openclaw/skills/agentmemory-config/TROUBLESHOOTING.md +70 -0
- package/integrations/openclaw/skills/agentmemory-hooks/REFERENCE.md +20 -0
- package/integrations/openclaw/skills/agentmemory-hooks/SKILL.md +49 -0
- package/integrations/openclaw/skills/agentmemory-hooks/TROUBLESHOOTING.md +70 -0
- package/integrations/openclaw/skills/agentmemory-mcp-tools/REFERENCE.md +66 -0
- package/integrations/openclaw/skills/agentmemory-mcp-tools/SKILL.md +42 -0
- package/integrations/openclaw/skills/agentmemory-mcp-tools/TROUBLESHOOTING.md +70 -0
- package/integrations/openclaw/skills/agentmemory-rest-api/REFERENCE.md +158 -0
- package/integrations/openclaw/skills/agentmemory-rest-api/SKILL.md +49 -0
- package/integrations/openclaw/skills/agentmemory-rest-api/TROUBLESHOOTING.md +70 -0
- package/integrations/openclaw/skills/commit-context/EXAMPLES.md +71 -0
- package/integrations/openclaw/skills/commit-context/SKILL.md +64 -0
- package/integrations/openclaw/skills/commit-context/TROUBLESHOOTING.md +70 -0
- package/integrations/openclaw/skills/commit-history/EXAMPLES.md +71 -0
- package/integrations/openclaw/skills/commit-history/SKILL.md +62 -0
- package/integrations/openclaw/skills/commit-history/TROUBLESHOOTING.md +70 -0
- package/integrations/openclaw/skills/forget/EXAMPLES.md +40 -0
- package/integrations/openclaw/skills/forget/SKILL.md +73 -0
- package/integrations/openclaw/skills/forget/TROUBLESHOOTING.md +70 -0
- package/integrations/openclaw/skills/handoff/EXAMPLES.md +57 -0
- package/integrations/openclaw/skills/handoff/SKILL.md +75 -0
- package/integrations/openclaw/skills/handoff/TROUBLESHOOTING.md +70 -0
- package/integrations/openclaw/skills/lesson/SKILL.md +64 -0
- package/integrations/openclaw/skills/lesson/TROUBLESHOOTING.md +70 -0
- package/integrations/openclaw/skills/memory-discipline/SKILL.md +65 -0
- package/integrations/openclaw/skills/memory-discipline/TROUBLESHOOTING.md +70 -0
- package/integrations/openclaw/skills/recall/EXAMPLES.md +74 -0
- package/integrations/openclaw/skills/recall/SKILL.md +74 -0
- package/integrations/openclaw/skills/recall/TROUBLESHOOTING.md +70 -0
- package/integrations/openclaw/skills/recap/EXAMPLES.md +62 -0
- package/integrations/openclaw/skills/recap/SKILL.md +68 -0
- package/integrations/openclaw/skills/recap/TROUBLESHOOTING.md +70 -0
- package/integrations/openclaw/skills/remember/EXAMPLES.md +67 -0
- package/integrations/openclaw/skills/remember/SKILL.md +66 -0
- package/integrations/openclaw/skills/remember/TROUBLESHOOTING.md +70 -0
- package/integrations/openclaw/skills/session-history/EXAMPLES.md +59 -0
- package/integrations/openclaw/skills/session-history/SKILL.md +67 -0
- package/integrations/openclaw/skills/session-history/TROUBLESHOOTING.md +70 -0
- package/integrations/openclaw/skills/write-agentmemory-skill/SKILL.md +41 -0
- package/integrations/openclaw/skills/write-agentmemory-skill/TROUBLESHOOTING.md +70 -0
- package/integrations/pi/LICENSE +190 -0
- package/integrations/pi/README.md +45 -51
- package/integrations/pi/dist/index.mjs +1379 -0
- package/integrations/pi/index.ts +138 -171
- package/integrations/pi/package.json +16 -6
- package/integrations/pi/security.ts +6 -26
- package/integrations/pi/skills/agentmemory-agents/REFERENCE.md +32 -0
- package/integrations/pi/skills/agentmemory-agents/SKILL.md +48 -0
- package/integrations/pi/skills/agentmemory-agents/TROUBLESHOOTING.md +70 -0
- package/integrations/pi/skills/agentmemory-architecture/SKILL.md +33 -0
- package/integrations/pi/skills/agentmemory-architecture/TROUBLESHOOTING.md +70 -0
- package/integrations/pi/skills/agentmemory-config/REFERENCE.md +90 -0
- package/integrations/pi/skills/agentmemory-config/SKILL.md +78 -0
- package/integrations/pi/skills/agentmemory-config/TROUBLESHOOTING.md +70 -0
- package/integrations/pi/skills/agentmemory-hooks/REFERENCE.md +20 -0
- package/integrations/pi/skills/agentmemory-hooks/SKILL.md +49 -0
- package/integrations/pi/skills/agentmemory-hooks/TROUBLESHOOTING.md +70 -0
- package/integrations/pi/skills/agentmemory-mcp-tools/REFERENCE.md +66 -0
- package/integrations/pi/skills/agentmemory-mcp-tools/SKILL.md +42 -0
- package/integrations/pi/skills/agentmemory-mcp-tools/TROUBLESHOOTING.md +70 -0
- package/integrations/pi/skills/agentmemory-rest-api/REFERENCE.md +158 -0
- package/integrations/pi/skills/agentmemory-rest-api/SKILL.md +49 -0
- package/integrations/pi/skills/agentmemory-rest-api/TROUBLESHOOTING.md +70 -0
- package/integrations/pi/skills/commit-context/EXAMPLES.md +71 -0
- package/integrations/pi/skills/commit-context/SKILL.md +64 -0
- package/integrations/pi/skills/commit-context/TROUBLESHOOTING.md +70 -0
- package/integrations/pi/skills/commit-history/EXAMPLES.md +71 -0
- package/integrations/pi/skills/commit-history/SKILL.md +62 -0
- package/integrations/pi/skills/commit-history/TROUBLESHOOTING.md +70 -0
- package/integrations/pi/skills/forget/EXAMPLES.md +40 -0
- package/integrations/pi/skills/forget/SKILL.md +73 -0
- package/integrations/pi/skills/forget/TROUBLESHOOTING.md +70 -0
- package/integrations/pi/skills/handoff/EXAMPLES.md +57 -0
- package/integrations/pi/skills/handoff/SKILL.md +75 -0
- package/integrations/pi/skills/handoff/TROUBLESHOOTING.md +70 -0
- package/integrations/pi/skills/lesson/SKILL.md +64 -0
- package/integrations/pi/skills/lesson/TROUBLESHOOTING.md +70 -0
- package/integrations/pi/skills/memory-discipline/SKILL.md +65 -0
- package/integrations/pi/skills/memory-discipline/TROUBLESHOOTING.md +70 -0
- package/integrations/pi/skills/recall/EXAMPLES.md +74 -0
- package/integrations/pi/skills/recall/SKILL.md +74 -0
- package/integrations/pi/skills/recall/TROUBLESHOOTING.md +70 -0
- package/integrations/pi/skills/recap/EXAMPLES.md +62 -0
- package/integrations/pi/skills/recap/SKILL.md +68 -0
- package/integrations/pi/skills/recap/TROUBLESHOOTING.md +70 -0
- package/integrations/pi/skills/remember/EXAMPLES.md +67 -0
- package/integrations/pi/skills/remember/SKILL.md +66 -0
- package/integrations/pi/skills/remember/TROUBLESHOOTING.md +70 -0
- package/integrations/pi/skills/session-history/EXAMPLES.md +59 -0
- package/integrations/pi/skills/session-history/SKILL.md +67 -0
- package/integrations/pi/skills/session-history/TROUBLESHOOTING.md +70 -0
- package/integrations/pi/skills/write-agentmemory-skill/SKILL.md +41 -0
- package/integrations/pi/skills/write-agentmemory-skill/TROUBLESHOOTING.md +70 -0
- package/package.json +32 -11
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/.codex-plugin/plugin.json +4 -3
- package/plugin/.devin-plugin/plugin.json +5 -3
- package/plugin/cursor/hooks.json +23 -8
- package/plugin/hooks.json +67 -0
- package/plugin/opencode/README.md +1 -1
- package/plugin/opencode/agentmemory-capture.ts +25 -11
- package/plugin/opencode/plugin.json +1 -1
- package/plugin/plugin.json +1 -1
- package/plugin/scripts/_capture.d.mts +5 -2
- package/plugin/scripts/_capture.d.mts.map +1 -1
- package/plugin/scripts/_capture.mjs +55 -19
- package/plugin/scripts/_capture.mjs.map +1 -1
- package/plugin/scripts/cursor/run-detached.d.mts +1 -0
- package/plugin/scripts/cursor/run-detached.mjs +786 -0
- package/plugin/scripts/cursor/run-detached.mjs.map +1 -0
- package/plugin/scripts/cursor/run-hook.d.mts +1 -0
- package/plugin/scripts/cursor/run-hook.mjs +704 -0
- package/plugin/scripts/cursor/run-hook.mjs.map +1 -0
- package/plugin/scripts/notification.mjs +20 -6
- package/plugin/scripts/notification.mjs.map +1 -1
- package/plugin/scripts/plugin-bridge.mjs +37 -6
- package/plugin/scripts/post-commit.mjs +25 -5
- package/plugin/scripts/post-commit.mjs.map +1 -1
- package/plugin/scripts/post-tool-failure.mjs +12 -4
- package/plugin/scripts/post-tool-failure.mjs.map +1 -1
- package/plugin/scripts/post-tool-use.mjs +12 -4
- package/plugin/scripts/post-tool-use.mjs.map +1 -1
- package/plugin/scripts/pre-compact.mjs +37 -10
- package/plugin/scripts/pre-compact.mjs.map +1 -1
- package/plugin/scripts/pre-tool-use.mjs +25 -5
- package/plugin/scripts/pre-tool-use.mjs.map +1 -1
- package/plugin/scripts/prompt-submit.mjs +12 -4
- package/plugin/scripts/prompt-submit.mjs.map +1 -1
- package/plugin/scripts/session-end.mjs +47 -16
- package/plugin/scripts/session-end.mjs.map +1 -1
- package/plugin/scripts/session-start.mjs +37 -10
- package/plugin/scripts/session-start.mjs.map +1 -1
- package/plugin/scripts/stop.mjs +74 -72
- package/plugin/scripts/stop.mjs.map +1 -1
- package/plugin/scripts/subagent-start.mjs +13 -5
- package/plugin/scripts/subagent-start.mjs.map +1 -1
- package/plugin/scripts/subagent-stop.mjs +18 -6
- package/plugin/scripts/subagent-stop.mjs.map +1 -1
- package/plugin/scripts/task-completed.mjs +13 -5
- package/plugin/scripts/task-completed.mjs.map +1 -1
- package/plugin/skills/_shared/TROUBLESHOOTING.md +21 -7
- package/plugin/skills/agentmemory-agents/REFERENCE.md +7 -6
- package/plugin/skills/agentmemory-agents/SKILL.md +19 -5
- package/plugin/skills/agentmemory-agents/TROUBLESHOOTING.md +70 -0
- package/plugin/skills/agentmemory-architecture/SKILL.md +4 -4
- package/plugin/skills/agentmemory-architecture/TROUBLESHOOTING.md +70 -0
- package/plugin/skills/agentmemory-config/REFERENCE.md +12 -15
- package/plugin/skills/agentmemory-config/SKILL.md +7 -3
- package/plugin/skills/agentmemory-config/TROUBLESHOOTING.md +70 -0
- package/plugin/skills/agentmemory-hooks/SKILL.md +15 -5
- package/plugin/skills/agentmemory-hooks/TROUBLESHOOTING.md +70 -0
- package/plugin/skills/agentmemory-mcp-tools/REFERENCE.md +4 -4
- package/plugin/skills/agentmemory-mcp-tools/SKILL.md +6 -3
- package/plugin/skills/agentmemory-mcp-tools/TROUBLESHOOTING.md +70 -0
- package/plugin/skills/agentmemory-rest-api/REFERENCE.md +9 -1
- package/plugin/skills/agentmemory-rest-api/SKILL.md +12 -6
- package/plugin/skills/agentmemory-rest-api/TROUBLESHOOTING.md +70 -0
- package/plugin/skills/commit-context/SKILL.md +1 -1
- package/plugin/skills/commit-context/TROUBLESHOOTING.md +70 -0
- package/plugin/skills/commit-history/SKILL.md +1 -1
- package/plugin/skills/commit-history/TROUBLESHOOTING.md +70 -0
- package/plugin/skills/forget/EXAMPLES.md +19 -54
- package/plugin/skills/forget/SKILL.md +16 -12
- package/plugin/skills/forget/TROUBLESHOOTING.md +70 -0
- package/plugin/skills/handoff/EXAMPLES.md +5 -4
- package/plugin/skills/handoff/SKILL.md +10 -5
- package/plugin/skills/handoff/TROUBLESHOOTING.md +70 -0
- package/plugin/skills/lesson/SKILL.md +1 -1
- package/plugin/skills/lesson/TROUBLESHOOTING.md +70 -0
- package/plugin/skills/memory-discipline/SKILL.md +1 -1
- package/plugin/skills/memory-discipline/TROUBLESHOOTING.md +70 -0
- package/plugin/skills/recall/SKILL.md +18 -7
- package/plugin/skills/recall/TROUBLESHOOTING.md +70 -0
- package/plugin/skills/recap/EXAMPLES.md +5 -3
- package/plugin/skills/recap/SKILL.md +11 -6
- package/plugin/skills/recap/TROUBLESHOOTING.md +70 -0
- package/plugin/skills/remember/EXAMPLES.md +3 -3
- package/plugin/skills/remember/SKILL.md +1 -1
- package/plugin/skills/remember/TROUBLESHOOTING.md +70 -0
- package/plugin/skills/session-history/EXAMPLES.md +2 -2
- package/plugin/skills/session-history/SKILL.md +10 -4
- package/plugin/skills/session-history/TROUBLESHOOTING.md +70 -0
- package/plugin/skills/write-agentmemory-skill/SKILL.md +2 -1
- package/plugin/skills/write-agentmemory-skill/TROUBLESHOOTING.md +70 -0
- package/dist/connect-B4_ca6BD.mjs.map +0 -1
- package/dist/docker-compose.yml +0 -73
- package/dist/iii-config.docker.yaml +0 -53
- package/dist/iii-config.yaml +0 -63
- package/dist/keyed-mutex-DchXsyfP.mjs.map +0 -1
- package/dist/secret-store-BnY3A50C.mjs.map +0 -1
- package/dist/src-Jsq9LCeH.mjs.map +0 -1
- package/dist/standalone-DKCdrf1d.mjs.map +0 -1
- package/dist/status-IRz7ta8-.mjs +0 -2344
- package/dist/status-IRz7ta8-.mjs.map +0 -1
- package/docker-compose.yml +0 -73
- package/iii-config.docker.yaml +0 -53
- package/iii-config.yaml +0 -63
package/dist/standalone.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"standalone.mjs","names":[],"sources":["../src/mcp/in-memory-kv.ts","../src/mcp/transport.ts","../src/mcp/tools-registry.ts","../src/config.ts","../src/version.ts","../src/state/cjk-segmenter.ts","../src/state/schema.ts","../src/logger.ts","../src/functions/audit.ts","../src/secret-store.ts","../src/mcp/rest-proxy.ts","../src/mcp/standalone.ts"],"sourcesContent":["import { existsSync, readFileSync, writeFileSync, mkdirSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\n\nexport class InMemoryKV {\n private store = new Map<string, Map<string, unknown>>();\n\n constructor(private persistPath?: string) {\n if (persistPath && existsSync(persistPath)) {\n try {\n const data = JSON.parse(readFileSync(persistPath, \"utf-8\"));\n for (const [scope, entries] of Object.entries(data)) {\n const map = new Map<string, unknown>();\n for (const [key, value] of Object.entries(\n entries as Record<string, unknown>,\n )) {\n map.set(key, value);\n }\n this.store.set(scope, map);\n }\n } catch {\n // start fresh\n }\n }\n }\n\n async get<T = unknown>(scope: string, key: string): Promise<T | null> {\n return (this.store.get(scope)?.get(key) as T) ?? null;\n }\n\n async set<T = unknown>(scope: string, key: string, data: T): Promise<T> {\n if (!this.store.has(scope)) this.store.set(scope, new Map());\n this.store.get(scope)!.set(key, data);\n return data;\n }\n\n async delete(scope: string, key: string): Promise<void> {\n this.store.get(scope)?.delete(key);\n }\n\n async list<T = unknown>(scope: string): Promise<T[]> {\n const entries = this.store.get(scope);\n return entries ? (Array.from(entries.values()) as T[]) : [];\n }\n\n persist(): void {\n if (!this.persistPath) return;\n try {\n const dir = dirname(this.persistPath);\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true });\n const data: Record<string, Record<string, unknown>> = {};\n for (const [scope, entries] of this.store) {\n data[scope] = Object.fromEntries(entries);\n }\n writeFileSync(this.persistPath, JSON.stringify(data), \"utf-8\");\n } catch (err) {\n process.stderr.write(\n `[@agentmemory/mcp] Persist failed: ${err instanceof Error ? err.message : String(err)}\\n`,\n );\n }\n }\n}\n","export interface JsonRpcRequest {\n jsonrpc: \"2.0\";\n id?: string | number;\n method: string;\n params?: Record<string, unknown>;\n}\n\nexport interface JsonRpcResponse {\n jsonrpc: \"2.0\";\n id: string | number | null;\n result?: unknown;\n error?: { code: number; message: string; data?: unknown };\n}\n\nexport type RequestHandler = (\n method: string,\n params: Record<string, unknown>,\n) => Promise<unknown>;\n\nexport class JsonRpcError extends Error {\n constructor(public readonly code: number, message: string) {\n super(message);\n this.name = \"JsonRpcError\";\n }\n}\n\nexport interface StdioMessageParser {\n push: (chunk: Buffer | string) => void;\n isFramed: () => boolean;\n}\n\n// JSON-RPC 2.0 notifications are messages without an `id` field. The spec\n// (and the MCP transport contract) requires the server to NOT send a\n// response for notifications. Some clients tolerate spurious responses;\n// stricter clients (e.g. Codex CLI) treat them as protocol violations and\n// close the transport. See agentmemory#129.\nfunction isNotification(req: JsonRpcRequest): boolean {\n return req.id === undefined || req.id === null;\n}\n\n// Per JSON-RPC 2.0 §4, a valid request id must be a String, Number, or Null\n// (Null is technically only allowed in responses; in requests, omitting id\n// is the convention for notifications, which we treat the same as null).\n// Any other runtime type (object, array, boolean) is an Invalid Request.\nfunction isValidId(id: unknown): id is string | number | null | undefined {\n return (\n id === undefined ||\n id === null ||\n typeof id === \"string\" ||\n typeof id === \"number\"\n );\n}\n\n// Exported for unit tests so the line-handling logic is exercised\n// independently of process.stdin / process.stdout.\nexport async function processLine(\n line: string,\n handler: RequestHandler,\n writeOut: (response: JsonRpcResponse) => void,\n writeErr: (msg: string) => void = (msg) => process.stderr.write(msg),\n): Promise<void> {\n const trimmed = line.trim();\n if (!trimmed) return;\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(trimmed);\n } catch {\n writeOut({\n jsonrpc: \"2.0\",\n id: null,\n error: { code: -32700, message: \"Parse error\" },\n });\n return;\n }\n\n const request = parsed as JsonRpcRequest;\n const rawId = (request as { id?: unknown } | null)?.id;\n\n // Invalid request shape (missing/wrong jsonrpc, non-string method).\n if (\n !request ||\n typeof request !== \"object\" ||\n request.jsonrpc !== \"2.0\" ||\n typeof request.method !== \"string\"\n ) {\n // Echo the id back only if it's a valid string/number. Notifications\n // (missing/null id) and malformed ids both drop silently — we don't\n // want to respond to something that could be a notification, and we\n // can't invent an id for a malformed one.\n if (typeof rawId === \"string\" || typeof rawId === \"number\") {\n writeOut({\n jsonrpc: \"2.0\",\n id: rawId,\n error: { code: -32600, message: \"Invalid Request\" },\n });\n }\n return;\n }\n\n // Request shape is valid but id may still be of the wrong type\n // (object, array, boolean). Per the spec, that's an Invalid Request.\n // Respond with id: null because we can't safely echo a non-JSON-RPC id.\n if (!isValidId(rawId)) {\n writeOut({\n jsonrpc: \"2.0\",\n id: null,\n error: { code: -32600, message: \"Invalid Request: id must be string, number, or null\" },\n });\n return;\n }\n\n const notification = isNotification(request);\n\n try {\n const result = await handler(request.method, request.params || {});\n if (notification) return;\n writeOut({\n jsonrpc: \"2.0\",\n id: request.id as string | number,\n result,\n });\n } catch (err) {\n if (notification) {\n writeErr(\n `[mcp-transport] notification handler error for ${request.method}: ${\n err instanceof Error ? err.message : String(err)\n }\\n`,\n );\n return;\n }\n writeOut({\n jsonrpc: \"2.0\",\n id: request.id as string | number,\n error: {\n code: err instanceof JsonRpcError ? err.code : -32603,\n message: err instanceof Error ? err.message : String(err),\n },\n });\n }\n}\n\nfunction findHeaderEnd(buffer: Buffer): { headerEnd: number; bodyStart: number } | null {\n const crlf = buffer.indexOf(\"\\r\\n\\r\\n\");\n const lf = buffer.indexOf(\"\\n\\n\");\n if (crlf === -1 && lf === -1) return null;\n if (crlf !== -1 && (lf === -1 || crlf <= lf)) {\n return { headerEnd: crlf, bodyStart: crlf + 4 };\n }\n return { headerEnd: lf, bodyStart: lf + 2 };\n}\n\nfunction parseContentLength(header: string): number | null {\n for (const line of header.split(/\\r?\\n/)) {\n const match = line.match(/^content-length:\\s*(\\d+)\\s*$/i);\n if (match) return Number(match[1]);\n }\n return null;\n}\n\nexport function formatResponse(\n response: JsonRpcResponse,\n framed: boolean,\n): string | Buffer[] {\n const body = JSON.stringify(response);\n if (!framed) return `${body}\\n`;\n const bytes = Buffer.from(body, \"utf8\");\n return [Buffer.from(`Content-Length: ${bytes.length}\\r\\n\\r\\n`, \"ascii\"), bytes];\n}\n\nexport function createMessageParser(\n onMessage: (message: string) => void,\n writeErr: (msg: string) => void = (msg) => process.stderr.write(msg),\n): StdioMessageParser {\n let buffer = Buffer.alloc(0);\n let framed = false;\n\n function processBuffer(): void {\n while (buffer.length > 0) {\n if (buffer[0] === 10 || buffer[0] === 13) {\n buffer = buffer.subarray(1);\n continue;\n }\n\n const preview = buffer.toString(\"ascii\", 0, Math.min(buffer.length, 32));\n if (/^content-length:/i.test(preview)) {\n const header = findHeaderEnd(buffer);\n if (!header) return;\n\n const headerText = buffer.subarray(0, header.headerEnd).toString(\"ascii\");\n const contentLength = parseContentLength(headerText);\n if (contentLength === null) {\n writeErr(\"[mcp-transport] missing Content-Length header\\n\");\n buffer = buffer.subarray(header.bodyStart);\n continue;\n }\n\n const messageEnd = header.bodyStart + contentLength;\n if (buffer.length < messageEnd) return;\n\n framed = true;\n const message = buffer.subarray(header.bodyStart, messageEnd).toString(\"utf8\");\n buffer = buffer.subarray(messageEnd);\n onMessage(message);\n continue;\n }\n\n const newline = buffer.indexOf(10);\n if (newline === -1) return;\n const line = buffer\n .subarray(0, newline)\n .toString(\"utf8\")\n .replace(/\\r$/, \"\");\n buffer = buffer.subarray(newline + 1);\n onMessage(line);\n }\n }\n\n return {\n push(chunk) {\n const bytes = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk, \"utf8\");\n buffer = Buffer.concat([buffer, bytes]);\n processBuffer();\n },\n isFramed() {\n return framed;\n },\n };\n}\n\nexport function createStdioTransport(handler: RequestHandler): {\n start: () => void;\n stop: () => void;\n} {\n let parser: StdioMessageParser | null = null;\n let queue = Promise.resolve();\n\n const writeResponse = (response: JsonRpcResponse) => {\n const formatted = formatResponse(response, parser?.isFramed() ?? false);\n if (typeof formatted === \"string\") {\n process.stdout.write(formatted);\n return;\n }\n for (const chunk of formatted) {\n process.stdout.write(chunk);\n }\n };\n\n const onData = (chunk: Buffer) => parser?.push(chunk);\n\n return {\n start() {\n parser = createMessageParser((message) => {\n queue = queue.then(() => processLine(message, handler, writeResponse));\n void queue.catch((err) => {\n process.stderr.write(\n `[mcp-transport] request processing failed: ${\n err instanceof Error ? err.message : String(err)\n }\\n`,\n );\n });\n });\n process.stdin.on(\"data\", onData);\n },\n stop() {\n process.stdin.off(\"data\", onData);\n parser = null;\n },\n };\n}\n","export type McpToolDef = {\n name: string;\n description: string;\n inputSchema: {\n type: \"object\";\n properties: Record<string, { type: string; description: string }>;\n required?: string[];\n };\n};\n\nexport const CORE_TOOLS: McpToolDef[] = [\n {\n name: \"memory_recall\",\n description:\n \"Search past session observations for relevant context. Use when you need to recall what happened in previous sessions, find past decisions, or look up how a file was modified before.\",\n inputSchema: {\n type: \"object\",\n properties: {\n query: {\n type: \"string\",\n description: \"Search query (keywords, file names, concepts)\",\n },\n limit: {\n type: \"number\",\n description: \"Max results to return (default 10)\",\n },\n format: {\n type: \"string\",\n description: \"Result format: full, compact, or narrative (default full)\",\n },\n token_budget: {\n type: \"number\",\n description: \"Optional token budget to trim returned results\",\n },\n },\n required: [\"query\"],\n },\n },\n {\n name: \"memory_compress_file\",\n description:\n \"Compress a markdown file to reduce token usage while preserving headings, URLs, and code blocks. Creates a .original.md backup before writing.\",\n inputSchema: {\n type: \"object\",\n properties: {\n filePath: {\n type: \"string\",\n description: \"Path to the markdown file to compress\",\n },\n },\n required: [\"filePath\"],\n },\n },\n {\n name: \"memory_save\",\n description:\n \"Explicitly save an important insight, decision, or pattern to long-term memory.\",\n inputSchema: {\n type: \"object\",\n properties: {\n content: {\n type: \"string\",\n description: \"The insight or decision to remember\",\n },\n type: {\n type: \"string\",\n description:\n \"Memory type: pattern, preference, architecture, bug, workflow, or fact\",\n },\n concepts: {\n type: \"string\",\n description: \"Comma-separated key concepts\",\n },\n files: {\n type: \"string\",\n description: \"Comma-separated relevant file paths\",\n },\n project: {\n type: \"string\",\n description:\n \"Stable canonical project identifier this memory belongs to (e.g. a slug, \" +\n \"UUID, or registry key). Must match the value used when the session was \" +\n \"started. Do not use filesystem paths or ad-hoc display names — those \" +\n \"change across machines and will silently break project scoping.\",\n },\n agentId: {\n type: \"string\",\n description:\n \"Agent identity to scope this memory to. When set, agent-scoped recall \" +\n \"and search only surface it for the same agentId. Omit for shared memory.\",\n },\n },\n required: [\"content\"],\n },\n },\n {\n name: \"memory_file_history\",\n description: \"Get past observations about specific files.\",\n inputSchema: {\n type: \"object\",\n properties: {\n files: { type: \"string\", description: \"Comma-separated file paths\" },\n sessionId: {\n type: \"string\",\n description: \"Current session ID to exclude\",\n },\n },\n required: [\"files\"],\n },\n },\n {\n name: \"memory_patterns\",\n description: \"Detect recurring patterns across sessions.\",\n inputSchema: {\n type: \"object\",\n properties: {\n project: { type: \"string\", description: \"Project path to analyze\" },\n limit: {\n type: \"integer\",\n description:\n \"Most recent sessions to scan (default 50, max 500)\",\n },\n },\n },\n },\n {\n name: \"memory_sessions\",\n description:\n \"List recent sessions with their status and observation counts.\",\n inputSchema: { type: \"object\", properties: {} },\n },\n {\n name: \"memory_smart_search\",\n description: \"Hybrid semantic+keyword search with progressive disclosure.\",\n inputSchema: {\n type: \"object\",\n properties: {\n query: { type: \"string\", description: \"Search query\" },\n expandIds: {\n type: \"string\",\n description: \"Comma-separated observation IDs to expand\",\n },\n limit: { type: \"number\", description: \"Max results (default 10)\" },\n },\n required: [\"query\"],\n },\n },\n {\n name: \"memory_vision_search\",\n description:\n \"Cross-modal image search via CLIP embeddings. Pass queryText to find screenshots matching a description, or queryImageBase64/queryImageRef to find similar images. Requires AGENTMEMORY_IMAGE_EMBEDDINGS=true.\",\n inputSchema: {\n type: \"object\",\n properties: {\n queryText: { type: \"string\", description: \"Text query (e.g. 'login form with error banner')\" },\n queryImageRef: { type: \"string\", description: \"Absolute path to a stored image to match against\" },\n queryImageBase64: { type: \"string\", description: \"Raw base64 image bytes or data URL\" },\n topK: { type: \"number\", description: \"Max results (default 10, max 50)\" },\n sessionId: { type: \"string\", description: \"Filter to a single session\" },\n },\n },\n },\n {\n name: \"memory_timeline\",\n description: \"Chronological observations around an anchor point.\",\n inputSchema: {\n type: \"object\",\n properties: {\n anchor: {\n type: \"string\",\n description: \"Anchor point: ISO date or keyword\",\n },\n project: { type: \"string\", description: \"Filter by project path\" },\n before: {\n type: \"number\",\n description: \"Observations before anchor (default 5)\",\n },\n after: {\n type: \"number\",\n description: \"Observations after anchor (default 5)\",\n },\n },\n required: [\"anchor\"],\n },\n },\n {\n name: \"memory_profile\",\n description: \"User/project profile with top concepts and file patterns.\",\n inputSchema: {\n type: \"object\",\n properties: {\n project: { type: \"string\", description: \"Project path\" },\n refresh: {\n type: \"string\",\n description: \"Set to 'true' to force rebuild\",\n },\n },\n required: [\"project\"],\n },\n },\n {\n name: \"memory_export\",\n description: \"Export all memory data as JSON.\",\n inputSchema: { type: \"object\", properties: {} },\n },\n {\n name: \"memory_relations\",\n description: \"Query the memory relationship graph.\",\n inputSchema: {\n type: \"object\",\n properties: {\n memoryId: {\n type: \"string\",\n description: \"Memory ID to find relations for\",\n },\n maxHops: {\n type: \"number\",\n description: \"Max traversal depth (default 2)\",\n },\n minConfidence: {\n type: \"number\",\n description: \"Min confidence (0-1, default 0)\",\n },\n },\n required: [\"memoryId\"],\n },\n },\n {\n name: \"memory_commit_lookup\",\n description:\n \"Look up the agent session(s) that produced a specific git commit, given its SHA. Returns the commit metadata and linked sessions.\",\n inputSchema: {\n type: \"object\",\n properties: {\n sha: { type: \"string\", description: \"Full git commit SHA\" },\n },\n required: [\"sha\"],\n },\n },\n {\n name: \"memory_commits\",\n description:\n \"List recent commits linked to agent sessions, optionally filtered by branch or repo.\",\n inputSchema: {\n type: \"object\",\n properties: {\n branch: { type: \"string\", description: \"Filter by branch name\" },\n repo: { type: \"string\", description: \"Filter by remote URL\" },\n limit: { type: \"number\", description: \"Max results (default 100, max 500)\" },\n },\n },\n },\n];\n\nexport const V040_TOOLS: McpToolDef[] = [\n {\n name: \"memory_claude_bridge_sync\",\n description:\n \"Sync memory state to/from Claude Code's native MEMORY.md file.\",\n inputSchema: {\n type: \"object\",\n properties: {\n direction: {\n type: \"string\",\n description:\n \"'read' to import from MEMORY.md, 'write' to export to MEMORY.md\",\n },\n },\n required: [\"direction\"],\n },\n },\n {\n name: \"memory_graph_query\",\n description: \"Query the knowledge graph for entities and relationships.\",\n inputSchema: {\n type: \"object\",\n properties: {\n startNodeId: {\n type: \"string\",\n description: \"Starting node ID for traversal\",\n },\n nodeType: { type: \"string\", description: \"Filter by node type\" },\n maxDepth: {\n type: \"number\",\n description: \"Max BFS depth (default 3, max 5)\",\n },\n query: { type: \"string\", description: \"Search nodes by name\" },\n },\n },\n },\n {\n name: \"memory_consolidate\",\n description:\n \"Run the 4-tier memory consolidation pipeline (working -> episodic -> semantic -> procedural).\",\n inputSchema: {\n type: \"object\",\n properties: {\n tier: {\n type: \"string\",\n description: \"Target tier: episodic, semantic, or procedural\",\n },\n },\n },\n },\n {\n name: \"memory_team_share\",\n description: \"Share a memory or observation with team members.\",\n inputSchema: {\n type: \"object\",\n properties: {\n itemId: {\n type: \"string\",\n description: \"ID of memory or observation to share\",\n },\n itemType: {\n type: \"string\",\n description: \"Type: observation, memory, or pattern\",\n },\n },\n required: [\"itemId\", \"itemType\"],\n },\n },\n {\n name: \"memory_team_feed\",\n description: \"Get recent shared items from all team members.\",\n inputSchema: {\n type: \"object\",\n properties: {\n limit: { type: \"number\", description: \"Max items (default 20)\" },\n },\n },\n },\n {\n name: \"memory_audit\",\n description: \"View the audit trail of memory operations.\",\n inputSchema: {\n type: \"object\",\n properties: {\n operation: { type: \"string\", description: \"Filter by operation type\" },\n limit: { type: \"number\", description: \"Max entries (default 50)\" },\n },\n },\n },\n {\n name: \"memory_governance_delete\",\n description: \"Delete specific memories with audit trail.\",\n inputSchema: {\n type: \"object\",\n properties: {\n memoryIds: {\n type: \"string\",\n description: \"Comma-separated memory IDs to delete\",\n },\n reason: { type: \"string\", description: \"Reason for deletion\" },\n },\n required: [\"memoryIds\"],\n },\n },\n {\n name: \"memory_snapshot_create\",\n description: \"Create a git-versioned snapshot of current memory state.\",\n inputSchema: {\n type: \"object\",\n properties: {\n message: { type: \"string\", description: \"Snapshot description\" },\n },\n },\n },\n];\n\nexport const V050_TOOLS: McpToolDef[] = [\n {\n name: \"memory_action_create\",\n description:\n \"Create an actionable work item with typed dependencies. Actions track what agents need to do and how work items relate to each other.\",\n inputSchema: {\n type: \"object\",\n properties: {\n title: { type: \"string\", description: \"Action title\" },\n description: {\n type: \"string\",\n description: \"Detailed description of the work\",\n },\n priority: {\n type: \"number\",\n description: \"Priority 1-10 (10 highest)\",\n },\n project: { type: \"string\", description: \"Project path\" },\n tags: {\n type: \"string\",\n description: \"Comma-separated tags\",\n },\n parentId: {\n type: \"string\",\n description: \"Parent action ID for hierarchical actions\",\n },\n requires: {\n type: \"string\",\n description:\n \"Comma-separated action IDs that must complete before this\",\n },\n },\n required: [\"title\"],\n },\n },\n {\n name: \"memory_action_update\",\n description:\n \"Update an action's status, priority, or details. Set status to 'done' to complete it and unblock dependent actions.\",\n inputSchema: {\n type: \"object\",\n properties: {\n actionId: { type: \"string\", description: \"Action ID to update\" },\n status: {\n type: \"string\",\n description: \"New status: pending, active, done, blocked, cancelled\",\n },\n result: {\n type: \"string\",\n description: \"Outcome description (when completing)\",\n },\n priority: { type: \"number\", description: \"New priority 1-10\" },\n },\n required: [\"actionId\"],\n },\n },\n {\n name: \"memory_frontier\",\n description:\n \"Get all unblocked actions ranked by priority and urgency. Returns the frontier of actionable work with no unsatisfied dependencies.\",\n inputSchema: {\n type: \"object\",\n properties: {\n project: { type: \"string\", description: \"Filter by project\" },\n agentId: {\n type: \"string\",\n description: \"Agent ID to check lease conflicts\",\n },\n limit: { type: \"number\", description: \"Max results (default 20)\" },\n },\n },\n },\n {\n name: \"memory_next\",\n description:\n \"Get the single most important next action to work on. Combines dependency resolution, priority, and recency into a score.\",\n inputSchema: {\n type: \"object\",\n properties: {\n project: { type: \"string\", description: \"Filter by project\" },\n agentId: { type: \"string\", description: \"Current agent ID\" },\n },\n },\n },\n {\n name: \"memory_lease\",\n description:\n \"Acquire, release, or renew an exclusive lease on an action. Prevents multiple agents from working on the same thing.\",\n inputSchema: {\n type: \"object\",\n properties: {\n actionId: { type: \"string\", description: \"Action ID\" },\n agentId: { type: \"string\", description: \"Agent claiming the action\" },\n operation: {\n type: \"string\",\n description: \"acquire, release, or renew\",\n },\n result: {\n type: \"string\",\n description: \"Result when releasing (marks action done)\",\n },\n ttlMs: {\n type: \"number\",\n description: \"Lease duration in ms (default 10min, max 1hr)\",\n },\n },\n required: [\"actionId\", \"agentId\", \"operation\"],\n },\n },\n {\n name: \"memory_routine_run\",\n description:\n \"Instantiate a frozen workflow routine, creating actions for each step with proper dependencies.\",\n inputSchema: {\n type: \"object\",\n properties: {\n routineId: { type: \"string\", description: \"Routine template ID\" },\n project: { type: \"string\", description: \"Project context\" },\n initiatedBy: { type: \"string\", description: \"Agent starting the run\" },\n },\n required: [\"routineId\"],\n },\n },\n {\n name: \"memory_signal_send\",\n description:\n \"Send a message to another agent or broadcast. Supports threading, typed messages, and TTL expiration.\",\n inputSchema: {\n type: \"object\",\n properties: {\n from: { type: \"string\", description: \"Sender agent ID\" },\n to: {\n type: \"string\",\n description: \"Recipient agent ID (omit for broadcast)\",\n },\n content: { type: \"string\", description: \"Message content\" },\n type: {\n type: \"string\",\n description: \"Message type: info, request, response, alert, handoff\",\n },\n replyTo: {\n type: \"string\",\n description: \"Signal ID to reply to (auto-threads)\",\n },\n },\n required: [\"from\", \"content\"],\n },\n },\n {\n name: \"memory_signal_read\",\n description:\n \"Read messages for an agent. Marks delivered messages as read.\",\n inputSchema: {\n type: \"object\",\n properties: {\n agentId: { type: \"string\", description: \"Agent to read messages for\" },\n unreadOnly: {\n type: \"string\",\n description: \"Set to 'true' for unread only\",\n },\n threadId: {\n type: \"string\",\n description: \"Filter by conversation thread\",\n },\n limit: { type: \"number\", description: \"Max messages (default 50)\" },\n },\n required: [\"agentId\"],\n },\n },\n {\n name: \"memory_checkpoint\",\n description:\n \"Create or resolve an external checkpoint (CI result, approval, deploy status) that gates action progress.\",\n inputSchema: {\n type: \"object\",\n properties: {\n operation: {\n type: \"string\",\n description: \"create, resolve, or list\",\n },\n name: { type: \"string\", description: \"Checkpoint name (for create)\" },\n checkpointId: {\n type: \"string\",\n description: \"Checkpoint ID (for resolve)\",\n },\n status: {\n type: \"string\",\n description: \"passed or failed (for resolve)\",\n },\n type: {\n type: \"string\",\n description: \"Checkpoint type: ci, approval, deploy, external, timer\",\n },\n linkedActionIds: {\n type: \"string\",\n description:\n \"Comma-separated action IDs this checkpoint gates (for create)\",\n },\n },\n required: [\"operation\"],\n },\n },\n {\n name: \"memory_mesh_sync\",\n description:\n \"Sync memories and actions with peer agentmemory instances for multi-agent collaboration.\",\n inputSchema: {\n type: \"object\",\n properties: {\n peerId: {\n type: \"string\",\n description: \"Specific peer ID (omit for all)\",\n },\n direction: {\n type: \"string\",\n description: \"push, pull, or both (default both)\",\n },\n },\n },\n },\n];\n\nexport const V051_TOOLS: McpToolDef[] = [\n {\n name: \"memory_sentinel_create\",\n description:\n \"Create an event-driven sentinel that watches for conditions (webhook, timer, threshold, pattern, approval) and auto-unblocks gated actions when triggered.\",\n inputSchema: {\n type: \"object\",\n properties: {\n name: { type: \"string\", description: \"Sentinel name\" },\n type: {\n type: \"string\",\n description: \"Type: webhook, timer, threshold, pattern, approval, custom\",\n },\n config: {\n type: \"string\",\n description: \"JSON config (timer: {durationMs}, threshold: {metric,operator,value}, pattern: {pattern}, webhook: {path})\",\n },\n linkedActionIds: {\n type: \"string\",\n description: \"Comma-separated action IDs to gate\",\n },\n expiresInMs: { type: \"number\", description: \"Auto-expire after ms\" },\n },\n required: [\"name\", \"type\"],\n },\n },\n {\n name: \"memory_sentinel_trigger\",\n description:\n \"Externally fire a sentinel, providing an optional result payload. Unblocks any gated actions.\",\n inputSchema: {\n type: \"object\",\n properties: {\n sentinelId: { type: \"string\", description: \"Sentinel ID to trigger\" },\n result: { type: \"string\", description: \"JSON result payload\" },\n },\n required: [\"sentinelId\"],\n },\n },\n {\n name: \"memory_sketch_create\",\n description:\n \"Create an ephemeral action graph for exploratory work. Auto-expires after TTL. Can be promoted to permanent actions or discarded.\",\n inputSchema: {\n type: \"object\",\n properties: {\n title: { type: \"string\", description: \"Sketch title\" },\n description: { type: \"string\", description: \"What this sketch explores\" },\n expiresInMs: { type: \"number\", description: \"TTL in ms (default 1 hour)\" },\n project: { type: \"string\", description: \"Project context\" },\n },\n required: [\"title\"],\n },\n },\n {\n name: \"memory_sketch_promote\",\n description:\n \"Promote a sketch's ephemeral actions to permanent actions. Makes the exploratory work official.\",\n inputSchema: {\n type: \"object\",\n properties: {\n sketchId: { type: \"string\", description: \"Sketch ID to promote\" },\n project: { type: \"string\", description: \"Override project for promoted actions\" },\n },\n required: [\"sketchId\"],\n },\n },\n {\n name: \"memory_crystallize\",\n description:\n \"Compress completed action chains into compact crystal digests using LLM summarization. Extracts narrative, key outcomes, files affected, and lessons.\",\n inputSchema: {\n type: \"object\",\n properties: {\n actionIds: {\n type: \"string\",\n description: \"Comma-separated completed action IDs to crystallize\",\n },\n project: { type: \"string\", description: \"Project context\" },\n sessionId: { type: \"string\", description: \"Session context\" },\n },\n required: [\"actionIds\"],\n },\n },\n {\n name: \"memory_diagnose\",\n description:\n \"Run health checks across all subsystems (actions, leases, sentinels, sketches, signals, sessions, memories, mesh). Identifies stuck, orphaned, and inconsistent state.\",\n inputSchema: {\n type: \"object\",\n properties: {\n categories: {\n type: \"string\",\n description: \"Comma-separated categories to check (default all)\",\n },\n },\n },\n },\n {\n name: \"memory_heal\",\n description:\n \"Auto-fix all fixable issues found by diagnostics. Unblocks stuck actions, expires stale leases, cleans up orphaned data.\",\n inputSchema: {\n type: \"object\",\n properties: {\n categories: {\n type: \"string\",\n description: \"Comma-separated categories to heal (default all)\",\n },\n dryRun: {\n type: \"string\",\n description: \"Set to 'true' for dry run (report but don't fix)\",\n },\n },\n },\n },\n {\n name: \"memory_facet_tag\",\n description:\n \"Attach a structured tag (dimension:value) to an action, memory, or observation for multi-dimensional categorization.\",\n inputSchema: {\n type: \"object\",\n properties: {\n targetId: { type: \"string\", description: \"ID of the target to tag\" },\n targetType: {\n type: \"string\",\n description: \"Type: action, memory, or observation\",\n },\n dimension: { type: \"string\", description: \"Tag dimension (e.g., priority, team, status)\" },\n value: { type: \"string\", description: \"Tag value (e.g., urgent, backend, reviewed)\" },\n },\n required: [\"targetId\", \"targetType\", \"dimension\", \"value\"],\n },\n },\n {\n name: \"memory_facet_query\",\n description:\n \"Query targets by facet tags with AND/OR logic. Find all actions tagged priority:urgent AND team:backend.\",\n inputSchema: {\n type: \"object\",\n properties: {\n matchAll: {\n type: \"string\",\n description: \"Comma-separated dimension:value pairs (AND logic)\",\n },\n matchAny: {\n type: \"string\",\n description: \"Comma-separated dimension:value pairs (OR logic)\",\n },\n targetType: {\n type: \"string\",\n description: \"Filter by type: action, memory, or observation\",\n },\n },\n },\n },\n];\n\nexport const V061_TOOLS: McpToolDef[] = [\n {\n name: \"memory_verify\",\n description:\n \"Verify a memory or observation by tracing its citation chain back to source observations and session context. Returns provenance information including confidence scores.\",\n inputSchema: {\n type: \"object\",\n properties: {\n id: {\n type: \"string\",\n description: \"Memory ID or observation ID to verify\",\n },\n },\n required: [\"id\"],\n },\n },\n];\n\nexport const V070_TOOLS: McpToolDef[] = [\n {\n name: \"memory_lesson_save\",\n description:\n \"Save a lesson learned from this session. Lessons have confidence scores that strengthen when reinforced and decay when not used. Duplicate content auto-strengthens the existing lesson.\",\n inputSchema: {\n type: \"object\",\n properties: {\n content: {\n type: \"string\",\n description: \"The lesson learned (what worked, what to avoid, when to use X approach)\",\n },\n context: {\n type: \"string\",\n description: \"When/where this lesson applies\",\n },\n confidence: {\n type: \"number\",\n description: \"Initial confidence 0.0-1.0 (default 0.5)\",\n },\n project: { type: \"string\", description: \"Project this lesson is about\" },\n tags: { type: \"string\", description: \"Comma-separated tags\" },\n },\n required: [\"content\"],\n },\n },\n {\n name: \"memory_lesson_recall\",\n description:\n \"Search lessons by query. Returns lessons sorted by confidence and recency. Use to check what the agent has learned before making decisions.\",\n inputSchema: {\n type: \"object\",\n properties: {\n query: { type: \"string\", description: \"Search query\" },\n project: { type: \"string\", description: \"Filter by project\" },\n minConfidence: {\n type: \"number\",\n description: \"Minimum confidence threshold (default 0.1)\",\n },\n limit: { type: \"number\", description: \"Max results (default 10)\" },\n },\n required: [\"query\"],\n },\n },\n {\n name: \"memory_lesson_delete\",\n description:\n \"Soft-delete a lesson by id. Deleted lessons are excluded from recall and list; re-saving the same content creates a fresh lesson.\",\n inputSchema: {\n type: \"object\",\n properties: {\n lessonId: { type: \"string\", description: \"The lesson id (lsn_...)\" },\n },\n required: [\"lessonId\"],\n },\n },\n {\n name: \"memory_obsidian_export\",\n description:\n \"Export memories, lessons, and crystals as Obsidian-compatible Markdown files with YAML frontmatter and wikilinks for graph view.\",\n inputSchema: {\n type: \"object\",\n properties: {\n vaultDir: {\n type: \"string\",\n description: \"Output directory (default ~/.agentmemory/vault/)\",\n },\n types: {\n type: \"string\",\n description: \"Comma-separated types to export: memories,lessons,crystals,sessions (default all)\",\n },\n },\n },\n },\n];\n\nexport const V073_TOOLS: McpToolDef[] = [\n {\n name: \"memory_reflect\",\n description:\n \"Traverse the knowledge graph, group related memories by concept clusters, and synthesize higher-order insights via LLM. Returns new and reinforced insights.\",\n inputSchema: {\n type: \"object\",\n properties: {\n project: { type: \"string\", description: \"Filter by project\" },\n maxClusters: {\n type: \"number\",\n description: \"Max concept clusters to process (default 10, max 20)\",\n },\n },\n },\n },\n {\n name: \"memory_insight_list\",\n description:\n \"List synthesized insights — higher-order observations derived from patterns across memories, lessons, and crystals.\",\n inputSchema: {\n type: \"object\",\n properties: {\n project: { type: \"string\", description: \"Filter by project\" },\n minConfidence: {\n type: \"number\",\n description: \"Minimum confidence threshold (default 0)\",\n },\n limit: { type: \"number\", description: \"Max results (default 50)\" },\n },\n },\n },\n];\n\nexport const V010_SLOTS_TOOLS: McpToolDef[] = [\n {\n name: \"memory_slot_list\",\n description:\n \"List all memory slots (pinned + project + global). Slots are editable, size-limited memory units the agent can read and modify across sessions.\",\n inputSchema: { type: \"object\", properties: {} },\n },\n {\n name: \"memory_slot_get\",\n description: \"Read a single slot by label.\",\n inputSchema: {\n type: \"object\",\n properties: {\n label: { type: \"string\", description: \"Slot label (e.g. 'persona', 'pending_items')\" },\n },\n required: [\"label\"],\n },\n },\n {\n name: \"memory_slot_create\",\n description: \"Create a new slot. Reject if a slot with the same label already exists.\",\n inputSchema: {\n type: \"object\",\n properties: {\n label: { type: \"string\", description: \"Slot label — lowercase, starts with letter, [a-z0-9_]\" },\n content: { type: \"string\", description: \"Initial content (default empty)\" },\n sizeLimit: { type: \"number\", description: \"Max chars (default 2000, hard cap 20000)\" },\n description: { type: \"string\", description: \"What this slot is for\" },\n pinned: { type: \"string\", description: \"'false' to exclude from context injection; default true\" },\n scope: { type: \"string\", description: \"'project' (default) or 'global' (shared across projects)\" },\n },\n required: [\"label\"],\n },\n },\n {\n name: \"memory_slot_append\",\n description:\n \"Append text to an existing slot. Fails with 413 if the append would exceed the slot's sizeLimit — agent must compact via memory_slot_replace first.\",\n inputSchema: {\n type: \"object\",\n properties: {\n label: { type: \"string\", description: \"Slot label\" },\n text: { type: \"string\", description: \"Text to append\" },\n },\n required: [\"label\", \"text\"],\n },\n },\n {\n name: \"memory_slot_replace\",\n description: \"Replace slot content in place. Fails if content exceeds sizeLimit.\",\n inputSchema: {\n type: \"object\",\n properties: {\n label: { type: \"string\", description: \"Slot label\" },\n content: { type: \"string\", description: \"New full content\" },\n },\n required: [\"label\", \"content\"],\n },\n },\n {\n name: \"memory_slot_delete\",\n description: \"Delete a slot. Seeded default slots can be deleted unless marked readOnly.\",\n inputSchema: {\n type: \"object\",\n properties: {\n label: { type: \"string\", description: \"Slot label\" },\n },\n required: [\"label\"],\n },\n },\n];\n\nexport const ESSENTIAL_TOOLS = new Set([\n \"memory_save\",\n \"memory_recall\",\n \"memory_consolidate\",\n \"memory_smart_search\",\n \"memory_sessions\",\n \"memory_diagnose\",\n \"memory_lesson_save\",\n \"memory_reflect\",\n]);\n\nexport function getAllTools(): McpToolDef[] {\n return [\n ...CORE_TOOLS,\n ...V040_TOOLS,\n ...V050_TOOLS,\n ...V051_TOOLS,\n ...V061_TOOLS,\n ...V070_TOOLS,\n ...V073_TOOLS,\n ...V010_SLOTS_TOOLS,\n ];\n}\n\n// default switched from \"core\" (8 essential tools) to \"all\"\n// (full 54-tool surface). README and plugin manifests have always\n// advertised 54 tools \"in proxy mode\"; the old default left OpenCode /\n// Claude Code users seeing 8 with no indication the other tools existed.\n// Users who want the lean essentials can still set AGENTMEMORY_TOOLS=core.\nexport function getVisibleTools(): McpToolDef[] {\n const mode = process.env[\"AGENTMEMORY_TOOLS\"] || \"all\";\n if (mode === \"core\") return getAllTools().filter((t) => ESSENTIAL_TOOLS.has(t.name));\n return getAllTools();\n}\n","import { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport pc from \"picocolors\";\nimport type {\n AgentMemoryConfig,\n ProviderConfig,\n EmbeddingConfig,\n FallbackConfig,\n ClaudeBridgeConfig,\n TeamConfig,\n} from \"./types.js\";\n\nfunction safeParseInt(value: string | undefined, fallback: number): number {\n if (!value) return fallback;\n const parsed = parseInt(value, 10);\n return Number.isNaN(parsed) ? fallback : parsed;\n}\n\nconst DATA_DIR = join(homedir(), \".agentmemory\");\nconst ENV_FILE = join(DATA_DIR, \".env\");\n\nlet warnPremiumModelShown = false;\nlet providerNoticeShown = false;\n\nfunction writeProviderNoticeOnce(text: string): void {\n if (providerNoticeShown) return;\n providerNoticeShown = true;\n process.stderr.write(text);\n}\n\n// Parsed ~/.agentmemory/.env, memoized for the process lifetime. getMergedEnv()\n// runs on every config getter (~20 of them), so without this cache a single\n// request would readFileSync + reparse the file dozens of times. The file is\n// boot-static, so read it from disk once and reuse the result. Tests that\n// mutate the file between cases reset the module (clearing this via reload) or\n// call __resetEnvFileCache().\nlet envFileCache: Record<string, string> | undefined;\n\nfunction loadEnvFile(): Record<string, string> {\n if (envFileCache) return envFileCache;\n if (!existsSync(ENV_FILE)) {\n envFileCache = {};\n return envFileCache;\n }\n const content = readFileSync(ENV_FILE, \"utf-8\");\n const vars: Record<string, string> = {};\n for (const line of content.split(\"\\n\")) {\n const trimmed = line.trim();\n if (!trimmed || trimmed.startsWith(\"#\")) continue;\n const eqIdx = trimmed.indexOf(\"=\");\n if (eqIdx === -1) continue;\n const key = trimmed.slice(0, eqIdx).trim();\n let val = trimmed.slice(eqIdx + 1).trim();\n const quoteChar = val[0] === '\"' || val[0] === \"'\" ? val[0] : \"\";\n if (quoteChar) {\n const closeIdx = val.indexOf(quoteChar, 1);\n if (closeIdx !== -1) val = val.slice(1, closeIdx);\n } else {\n const hashIdx = val.indexOf(\" #\");\n if (hashIdx !== -1) val = val.slice(0, hashIdx).trim();\n }\n vars[key] = val;\n }\n envFileCache = vars;\n return envFileCache;\n}\n\n// Test hook: clears the memoized .env so the next loadEnvFile() re-reads disk\n// within the same module instance. vi.resetModules() reloads this module and\n// resets the cache on its own; this exists for tests that mutate the file\n// without a module reload.\nexport function __resetEnvFileCache(): void {\n envFileCache = undefined;\n}\n\nfunction hasRealValue(v: string | undefined): v is string {\n return typeof v === \"string\" && v.trim().length > 0;\n}\n\n// Hydrate ~/.agentmemory/.env into process.env at boot. loadEnvFile() is\n// otherwise only consumed via getMergedEnv(), which the many modules that\n// read raw process.env[\"X\"] never call — so .env-only values were silently\n// ignored by them. Copy the file's vars into process.env, but only when the\n// key is currently unset so a real process.env value still wins (this\n// preserves the {...fileEnv, ...process.env} precedence getMergedEnv uses).\nexport function hydrateProcessEnvFromFile(): void {\n for (const [k, v] of Object.entries(loadEnvFile())) {\n if (process.env[k] === undefined) process.env[k] = v;\n }\n}\n\nfunction detectProvider(env: Record<string, string>): ProviderConfig {\n const maxTokens = parseInt(env[\"MAX_TOKENS\"] || \"4096\", 10);\n\n // OpenAI-compatible: supports OpenAI, DeepSeek, SiliconFlow, Azure, vLLM, LM Studio\n if (hasRealValue(env[\"OPENAI_API_KEY\"]) && env[\"OPENAI_API_KEY_FOR_LLM\"] !== \"false\") {\n return {\n provider: \"openai\",\n model: env[\"OPENAI_MODEL\"] || \"gpt-5.6-luna\",\n maxTokens,\n baseURL: env[\"OPENAI_BASE_URL\"],\n };\n }\n\n // MiniMax: Anthropic-compatible API, requires raw fetch to avoid SDK stainless headers\n if (hasRealValue(env[\"MINIMAX_API_KEY\"])) {\n return {\n provider: \"minimax\",\n model: env[\"MINIMAX_MODEL\"] || \"MiniMax-M3\",\n maxTokens,\n };\n }\n\n if (hasRealValue(env[\"ANTHROPIC_API_KEY\"])) {\n return {\n provider: \"anthropic\",\n model: env[\"ANTHROPIC_MODEL\"] || \"claude-sonnet-5\",\n maxTokens,\n baseURL: env[\"ANTHROPIC_BASE_URL\"],\n };\n }\n if (hasRealValue(env[\"GEMINI_API_KEY\"]) || hasRealValue(env[\"GOOGLE_API_KEY\"])) {\n if (!hasRealValue(env[\"GEMINI_API_KEY\"]) && hasRealValue(env[\"GOOGLE_API_KEY\"])) {\n process.stderr.write(\n \"[agentmemory] GOOGLE_API_KEY detected — treating as GEMINI_API_KEY. \" +\n \"Set GEMINI_API_KEY in ~/.agentmemory/.env to silence this warning.\\n\",\n );\n }\n return {\n provider: \"gemini\",\n model: env[\"GEMINI_MODEL\"] || \"gemini-3.7-flash\",\n maxTokens,\n };\n }\n if (hasRealValue(env[\"OPENROUTER_API_KEY\"])) {\n const model = env[\"OPENROUTER_MODEL\"] || \"anthropic/claude-sonnet-5\";\n // warn when the configured OpenRouter model is in the\n // premium tier and likely to burn money on background compression.\n // Captured workload data shows ~$5/35h on claude-sonnet-4 vs\n // ~$0.46/35h on deepseek-v4-pro for the same compression mix.\n // Heuristic match avoids hard-coding a pricing table.\n if (\n !warnPremiumModelShown &&\n /sonnet|opus|gpt-5\\.\\d+-sol|gpt-4o(?!.*mini)|gpt-4-turbo/i.test(model) &&\n env[\"AGENTMEMORY_SUPPRESS_COST_WARNING\"] !== \"1\" &&\n env[\"AGENTMEMORY_SUPPRESS_COST_WARNING\"] !== \"true\"\n ) {\n warnPremiumModelShown = true;\n process.stderr.write(\n `[agentmemory] OPENROUTER_MODEL=${model} is in the premium tier. ` +\n `Background compression on this model can cost $5+/day under active use. ` +\n `Cheaper alternatives with comparable quality for memory compression: ` +\n `deepseek/deepseek-v4-flash-0731, deepseek/deepseek-v4-pro, qwen/qwen3-coder. ` +\n `See README \"Cost-aware model selection\" for the full table. ` +\n `Set AGENTMEMORY_SUPPRESS_COST_WARNING=1 to silence.\\n`,\n );\n }\n return {\n provider: \"openrouter\",\n model,\n maxTokens,\n };\n }\n\n const allowAgentSdk = env[\"AGENTMEMORY_ALLOW_AGENT_SDK\"] === \"true\";\n if (!allowAgentSdk) {\n writeProviderNoticeOnce(\n pc.dim(\n \"[agentmemory] No LLM provider key set — running zero-LLM: no LLM compression or summaries; search uses BM25 plus any configured embedding provider. \" +\n \"Set EMBEDDING_PROVIDER=local for on-device semantic embeddings if none is configured. \" +\n \"Set ANTHROPIC_API_KEY (or GEMINI/OPENAI/OPENROUTER/MINIMAX) in ~/.agentmemory/.env for LLM compression and summaries. \" +\n \"Agent-SDK fallback stays off by default to avoid a Stop-hook recursion loop; opt in with AGENTMEMORY_AUTO_COMPRESS=true + AGENTMEMORY_ALLOW_AGENT_SDK=true.\\n\",\n ),\n );\n return {\n provider: \"noop\",\n model: \"noop\",\n maxTokens,\n };\n }\n\n writeProviderNoticeOnce(\n \"[agentmemory] WARNING: agent-sdk fallback enabled via AGENTMEMORY_ALLOW_AGENT_SDK=true. \" +\n \"This spawns @anthropic-ai/claude-agent-sdk child sessions that can trigger the Stop-hook \" +\n \"recursion loop. A SDK-child env marker is set to block re-entry, \" +\n \"but prefer setting a real API key in ~/.agentmemory/.env instead.\\n\",\n );\n return {\n provider: \"agent-sdk\",\n model: \"claude-sonnet-5\",\n maxTokens,\n };\n}\n\nexport function loadConfig(): AgentMemoryConfig {\n const env = getMergedEnv();\n\n const provider = detectProvider(env);\n\n // Port quartet: REST is the anchor; streams/engine derive from it\n // unless individually overridden. Default anchor 3111 yields the\n // canonical 3112 streams / 49134 engine pair, but `III_REST_PORT=3211`\n // auto-picks 3212 + 49234 so a second instance doesn't collide.\n const restPort = parseInt(env[\"III_REST_PORT\"] || \"3111\", 10) || 3111;\n const streamsPort =\n parseInt(env[\"III_STREAM_PORT\"] || env[\"III_STREAMS_PORT\"] || \"\", 10) ||\n restPort + 1;\n const viewerPort =\n parseInt(env[\"III_VIEWER_PORT\"] || \"\", 10) || restPort + 2;\n const engineUrl =\n env[\"III_ENGINE_URL\"] ||\n `ws://localhost:${\n parseInt(env[\"III_ENGINE_PORT\"] || \"\", 10) || restPort + 46023\n }`;\n\n return {\n engineUrl,\n restPort,\n streamsPort,\n viewerPort,\n provider,\n tokenBudget: safeParseInt(env[\"TOKEN_BUDGET\"], 2000),\n maxObservationsPerSession: safeParseInt(env[\"MAX_OBS_PER_SESSION\"], 500),\n compressionModel: provider.model,\n dataDir: DATA_DIR,\n };\n}\n\nfunction getMergedEnv(\n overrides?: Record<string, string>,\n): Record<string, string> {\n const fileEnv = loadEnvFile();\n return { ...fileEnv, ...process.env, ...overrides } as Record<string, string>;\n}\n\nexport function getEnvVar(key: string): string | undefined {\n return getMergedEnv()[key];\n}\n\nexport function isSessionSweepEnabled(): boolean {\n return getMergedEnv()[\"AGENTMEMORY_SESSION_SWEEP_ENABLED\"] !== \"false\";\n}\n\nexport function getSessionSweepStaleHours(): number {\n const raw = getMergedEnv()[\"AGENTMEMORY_SESSION_SWEEP_STALE_HOURS\"];\n if (!raw || !/^\\d+$/.test(raw.trim())) return 24;\n const parsed = parseInt(raw.trim(), 10);\n return parsed > 0 ? parsed : 24;\n}\n\nexport function isGraphCompactOnBootEnabled(): boolean {\n return getMergedEnv()[\"AGENTMEMORY_GRAPH_COMPACT_ON_BOOT\"] !== \"false\";\n}\n\nexport function isDropStaleIndexEnabled(): boolean {\n return getMergedEnv()[\"AGENTMEMORY_DROP_STALE_INDEX\"] === \"true\";\n}\n\nconst VALID_STATE_BACKENDS = new Set([\"file\", \"redis\"]);\n\nexport function getStateBackend(): \"file\" | \"redis\" {\n const raw = (getMergedEnv()[\"AGENTMEMORY_STATE_BACKEND\"] || \"\")\n .trim()\n .toLowerCase();\n if (!raw) return \"file\";\n if (!VALID_STATE_BACKENDS.has(raw)) {\n throw new Error(\n `AGENTMEMORY_STATE_BACKEND=\"${raw}\" is not a recognized state backend. Use \"file\" (the default) or \"redis\", or unset it to keep the default file store.`,\n );\n }\n return raw as \"file\" | \"redis\";\n}\n\nexport function getRedisUrl(): string | undefined {\n const value = getMergedEnv()[\"AGENTMEMORY_REDIS_URL\"];\n return hasRealValue(value) ? value.trim() : undefined;\n}\n\nexport function getAuditRetentionMonths(): number {\n return safeParseInt(getMergedEnv()[\"AGENTMEMORY_AUDIT_RETENTION_MONTHS\"], 0);\n}\n\nexport function getAuditMigrateMaxBytes(): number {\n return safeParseInt(\n getMergedEnv()[\"AGENTMEMORY_AUDIT_MIGRATE_MAX_BYTES\"],\n 32 * 1024 * 1024,\n );\n}\n\nexport function detectLlmProviderKind(\n env: Record<string, string | undefined> = getMergedEnv(),\n): \"llm\" | \"noop\" {\n if (\n hasRealValue(env[\"ANTHROPIC_API_KEY\"]) ||\n hasRealValue(env[\"GEMINI_API_KEY\"]) ||\n hasRealValue(env[\"GOOGLE_API_KEY\"]) ||\n hasRealValue(env[\"OPENROUTER_API_KEY\"]) ||\n hasRealValue(env[\"MINIMAX_API_KEY\"]) ||\n (hasRealValue(env[\"OPENAI_API_KEY\"]) &&\n env[\"OPENAI_API_KEY_FOR_LLM\"] !== \"false\") ||\n env[\"AGENTMEMORY_ALLOW_AGENT_SDK\"] === \"true\"\n ) {\n return \"llm\";\n }\n return \"noop\";\n}\n\nexport function loadEmbeddingConfig(): EmbeddingConfig {\n const env = getMergedEnv();\n let bm25Weight = parseFloat(env[\"BM25_WEIGHT\"] || \"0.4\");\n let vectorWeight = parseFloat(env[\"VECTOR_WEIGHT\"] || \"0.6\");\n bm25Weight =\n isNaN(bm25Weight) || bm25Weight < 0 ? 0.4 : Math.min(bm25Weight, 1);\n vectorWeight =\n isNaN(vectorWeight) || vectorWeight < 0 ? 0.6 : Math.min(vectorWeight, 1);\n return {\n provider: env[\"EMBEDDING_PROVIDER\"] || undefined,\n bm25Weight,\n vectorWeight,\n };\n}\n\nexport function detectEmbeddingProvider(\n env?: Record<string, string>,\n): string | null {\n const source = env ?? getMergedEnv();\n const forced = source[\"EMBEDDING_PROVIDER\"];\n if (forced) return forced;\n\n if (source[\"GEMINI_API_KEY\"]) return \"gemini\";\n if (source[\"OPENAI_API_KEY\"] || source[\"OPENAI_EMBEDDING_API_KEY\"]) return \"openai\";\n if (source[\"VOYAGE_API_KEY\"]) return \"voyage\";\n if (source[\"COHERE_API_KEY\"]) return \"cohere\";\n if (source[\"OPENROUTER_API_KEY\"]) return \"openrouter\";\n return null;\n}\n\nexport function loadClaudeBridgeConfig(): ClaudeBridgeConfig {\n const env = getMergedEnv();\n const enabled = env[\"CLAUDE_MEMORY_BRIDGE\"] === \"true\";\n const projectPath = env[\"CLAUDE_PROJECT_PATH\"] || \"\";\n const lineBudget = safeParseInt(env[\"CLAUDE_MEMORY_LINE_BUDGET\"], 200);\n let memoryFilePath = \"\";\n if (enabled && projectPath) {\n // Claude Code stores project memory at\n // ~/.claude/projects/<slug>/memory/MEMORY.md\n // where <slug> is the project path with `/` and `\\` swapped for `-`.\n // The leading `-` from an absolute POSIX path is preserved (Claude\n // Code keeps it; stripping it produced a slug Claude never reads).\n // The `memory/` subdirectory holds MEMORY.md (the index) plus one\n // per-topic `.md` file per memory (verified against Claude Code 2.x).\n const safePath = projectPath.replace(/[/\\\\]/g, \"-\");\n memoryFilePath = join(\n homedir(),\n \".claude\",\n \"projects\",\n safePath,\n \"memory\",\n \"MEMORY.md\",\n );\n }\n return { enabled, projectPath, memoryFilePath, lineBudget };\n}\n\nexport function loadTeamConfig(): TeamConfig | null {\n const env = getMergedEnv();\n const teamId = env[\"TEAM_ID\"];\n const userId = env[\"USER_ID\"];\n if (!teamId || !userId) return null;\n const mode = env[\"TEAM_MODE\"] === \"shared\" ? \"shared\" : \"private\";\n return { teamId, userId, mode };\n}\n\n// optional AGENT_ID env for multi-agent memory isolation.\n// Returns null when unset so memory stays unscoped (legacy behavior).\n// Trimmed + length-capped to keep KV writes well-formed.\n//\n// Filtering is gated by AGENTMEMORY_AGENT_SCOPE:\n// \"shared\" (default) — tag everything, do not filter recall paths\n// \"isolated\" — tag everything AND filter recall paths\nexport function loadAgentScope(): {\n agentId: string;\n mode: \"shared\" | \"isolated\";\n} | null {\n const env = getMergedEnv();\n const raw = env[\"AGENT_ID\"];\n if (!raw) return null;\n const agentId = raw.trim().slice(0, 128);\n if (!agentId) return null;\n const mode = env[\"AGENTMEMORY_AGENT_SCOPE\"] === \"isolated\"\n ? \"isolated\"\n : \"shared\";\n return { agentId, mode };\n}\n\nexport function getAgentId(): string | undefined {\n return loadAgentScope()?.agentId;\n}\n\n// True only when AGENT_ID is set AND scope=isolated. Recall paths\n// consult this to decide whether to filter.\nexport function isAgentScopeIsolated(): boolean {\n return loadAgentScope()?.mode === \"isolated\";\n}\n\n// Floor for the git-snapshot timer. A zero/negative SNAPSHOT_INTERVAL would\n// make setInterval fire on roughly every event-loop tick, saturating the\n// worker with back-to-back full-state snapshots + git commits. Anything below\n// this floor is treated as a misconfiguration and falls back to the default.\nconst SNAPSHOT_INTERVAL_DEFAULT_SECONDS = 3600;\nconst MIN_SNAPSHOT_INTERVAL_SECONDS = 1;\n\nexport function loadSnapshotConfig(): {\n enabled: boolean;\n interval: number;\n dir: string;\n} {\n const env = getMergedEnv();\n const rawInterval = safeParseInt(\n env[\"SNAPSHOT_INTERVAL\"],\n SNAPSHOT_INTERVAL_DEFAULT_SECONDS,\n );\n const interval =\n rawInterval >= MIN_SNAPSHOT_INTERVAL_SECONDS\n ? rawInterval\n : SNAPSHOT_INTERVAL_DEFAULT_SECONDS;\n return {\n enabled: env[\"SNAPSHOT_ENABLED\"] === \"true\",\n interval,\n dir: env[\"SNAPSHOT_DIR\"] || join(homedir(), \".agentmemory\", \"snapshots\"),\n };\n}\n\nexport function isGraphExtractionEnabled(): boolean {\n return getMergedEnv()[\"GRAPH_EXTRACTION_ENABLED\"] === \"true\";\n}\n\nexport function getGraphBatchSize(): number {\n return safeParseInt(getMergedEnv()[\"GRAPH_EXTRACTION_BATCH_SIZE\"], 10);\n}\n\n// window for the smart-search followup-rate diagnostic. A second\n// search arriving within this many seconds (with disjoint results)\n// counts as a \"follow-up\" — a directional signal that the first result\n// set didn't satisfy. Long values overcount (legitimate refinement\n// looks like a follow-up); short values undercount.\nconst FOLLOWUP_WINDOW_DEFAULT_SECONDS = 30;\n\nexport function getFollowupWindowSeconds(): number {\n return safeParseInt(\n getMergedEnv()[\"AGENTMEMORY_FOLLOWUP_WINDOW_SECONDS\"],\n FOLLOWUP_WINDOW_DEFAULT_SECONDS,\n );\n}\n\nconst VIEWER_STREAM_MAX_DEFAULT = 500;\nconst VIEWER_STREAM_MAX_FLOOR = 200;\n\nfunction parseExactInt(value: string | undefined, fallback: number): number {\n if (!value) return fallback;\n const trimmed = value.trim();\n if (!/^-?\\d+$/.test(trimmed)) return fallback;\n const parsed = Number(trimmed);\n return Number.isSafeInteger(parsed) ? parsed : fallback;\n}\n\nexport function getViewerStreamMax(): number {\n const parsed = parseExactInt(\n getMergedEnv()[\"AGENTMEMORY_VIEWER_STREAM_MAX\"],\n VIEWER_STREAM_MAX_DEFAULT,\n );\n if (parsed < 0) return VIEWER_STREAM_MAX_DEFAULT;\n return Math.max(parsed, VIEWER_STREAM_MAX_FLOOR);\n}\n\nexport function isConsolidationEnabled(): boolean {\n const env = getMergedEnv();\n const explicit = env[\"CONSOLIDATION_ENABLED\"];\n if (explicit === \"false\" || explicit === \"0\") return false;\n if (explicit === \"true\" || explicit === \"1\") return true;\n if ((env[\"AGENTMEMORY_PROVIDER\"] || \"\").toLowerCase() === \"noop\") return false;\n return detectLlmProviderKind(env) === \"llm\";\n}\n\n// Per-observation LLM compression is OFF by default as of 0.8.8.\n// When disabled, observations are captured and indexed via a synthetic\n// (zero-LLM) compression path so recall/search still works. Users who want\n// richer LLM-generated summaries can set AGENTMEMORY_AUTO_COMPRESS=true in\n// ~/.agentmemory/.env — but should expect their Claude API token usage to\n// climb proportionally with session tool-use frequency.\nexport function isAutoCompressEnabled(): boolean {\n return getMergedEnv()[\"AGENTMEMORY_AUTO_COMPRESS\"] === \"true\";\n}\n\n// Hook-level context injection into Claude Code's conversation is OFF by\n// default as of 0.8.10. When disabled, pre-tool-use and\n// session-start hooks still POST observations for background capture, but\n// never write context to stdout — so Claude Code doesn't inject an extra\n// ~4000-char blob into every tool turn. 0.8.8 stopped the agentmemory-side\n// Claude calls (via ANTHROPIC_API_KEY); this stops the Claude Code-side\n// token burn where every tool call silently grew the model input window.\n// Users who want the in-conversation context injection explicitly opt in\n// with AGENTMEMORY_INJECT_CONTEXT=true and get a loud startup warning.\nexport function isContextInjectionEnabled(): boolean {\n return getMergedEnv()[\"AGENTMEMORY_INJECT_CONTEXT\"] === \"true\";\n}\n\nexport const CONSOLIDATION_INTERVAL_DEFAULT_MS = 7200000;\n\nexport function getConsolidationIntervalMs(): number {\n const raw = parseInt(process.env.CONSOLIDATION_INTERVAL_MS || String(CONSOLIDATION_INTERVAL_DEFAULT_MS), 10);\n return Number.isFinite(raw) && raw > 0 ? raw : CONSOLIDATION_INTERVAL_DEFAULT_MS;\n}\n\nexport function getConsolidationDecayDays(): number {\n return safeParseInt(getMergedEnv()[\"CONSOLIDATION_DECAY_DAYS\"], 30);\n}\n\n// Cooldown between corpus consolidations triggered by session stop. The Stop\n// hook fires per agent turn and posts /session/end, so without this every turn\n// would kick a full LLM semantic-merge + reflect + crystallize. Debounced to at\n// most once per window. Set to 0 to disable the debounce (consolidate on every\n// stop). Default 5 minutes.\nconst CONSOLIDATION_COOLDOWN_DEFAULT_MS = 300000;\n\nexport function getConsolidationCooldownMs(): number {\n const raw = safeParseInt(\n getMergedEnv()[\"AGENTMEMORY_CONSOLIDATION_COOLDOWN_MS\"],\n CONSOLIDATION_COOLDOWN_DEFAULT_MS,\n );\n return raw >= 0 ? raw : CONSOLIDATION_COOLDOWN_DEFAULT_MS;\n}\n\nexport const INDEX_SAVE_INTERVAL_DEFAULT_MS = 600_000;\n\nexport function getIndexSaveIntervalMs(): number {\n const raw = safeParseInt(\n getMergedEnv()[\"AGENTMEMORY_INDEX_SAVE_INTERVAL_MS\"],\n INDEX_SAVE_INTERVAL_DEFAULT_MS,\n );\n return raw > 0 ? raw : INDEX_SAVE_INTERVAL_DEFAULT_MS;\n}\n\nexport function getStateSaveIntervalMs(): number | undefined {\n const raw = safeParseInt(getMergedEnv()[\"AGENTMEMORY_STATE_SAVE_INTERVAL_MS\"], 0);\n return raw > 0 ? raw : undefined;\n}\n\nexport const VECTOR_BUCKET_SIZE_DEFAULT = 500;\n\nexport function getVectorBucketSize(): number {\n const raw = safeParseInt(getMergedEnv()[\"AGENTMEMORY_VECTOR_BUCKET_SIZE\"], VECTOR_BUCKET_SIZE_DEFAULT);\n return raw > 0 ? raw : VECTOR_BUCKET_SIZE_DEFAULT;\n}\n\nexport const VECTOR_BACKFILL_MAX_DEFAULT = 500;\n\nexport function getVectorBackfillMax(): number {\n const raw = safeParseInt(getMergedEnv()[\"AGENTMEMORY_VECTOR_BACKFILL_MAX\"], VECTOR_BACKFILL_MAX_DEFAULT);\n return raw > 0 ? raw : VECTOR_BACKFILL_MAX_DEFAULT;\n}\n\nexport function isVectorBackfillAllEnabled(): boolean {\n return getMergedEnv()[\"AGENTMEMORY_VECTOR_BACKFILL\"] === \"all\";\n}\n\nexport function isStandaloneMcp(): boolean {\n return getMergedEnv()[\"STANDALONE_MCP\"] === \"true\";\n}\n\nexport function getStandalonePersistPath(): string {\n const env = getMergedEnv();\n return (\n env[\"STANDALONE_PERSIST_PATH\"] ||\n join(homedir(), \".agentmemory\", \"standalone.json\")\n );\n}\n\nconst VALID_PROVIDERS = new Set([\n \"anthropic\",\n \"gemini\",\n \"openrouter\",\n \"agent-sdk\",\n \"minimax\",\n \"openai\",\n]);\n\nexport function loadFallbackConfig(): FallbackConfig {\n const env = getMergedEnv();\n const raw = env[\"FALLBACK_PROVIDERS\"] || \"\";\n const allowAgentSdk = env[\"AGENTMEMORY_ALLOW_AGENT_SDK\"] === \"true\";\n const providers = raw\n .split(\",\")\n .map((p) => p.trim())\n .filter(\n (p): p is FallbackConfig[\"providers\"][number] =>\n Boolean(p) && VALID_PROVIDERS.has(p),\n )\n .filter((p) => {\n // Honor the same safety gate as detectProvider: agent-sdk is only\n // permitted as a fallback target when the user has explicitly opted\n // in. Without this filter, a user could set FALLBACK_PROVIDERS=agent-sdk\n // and re-introduce the Stop-hook recursion loop even though\n // detectProvider() returned the noop provider.\n if (p === \"agent-sdk\" && !allowAgentSdk) {\n process.stderr.write(\n \"[agentmemory] Ignoring FALLBACK_PROVIDERS entry 'agent-sdk' \" +\n \"(AGENTMEMORY_ALLOW_AGENT_SDK is not 'true'). The agent-sdk \" +\n \"fallback can spawn Claude Agent SDK child sessions that trigger \" +\n \"the Stop-hook recursion loop. Opt in explicitly \" +\n \"with AGENTMEMORY_ALLOW_AGENT_SDK=true if this is intentional.\\n\",\n );\n return false;\n }\n return true;\n });\n return { providers };\n}\n","export const VERSION = \"0.9.30\";\nexport const III_PINNED_VERSION = \"0.22.1\";\n","import { createRequire } from \"node:module\";\n\nconst cjkRequire = createRequire(import.meta.url);\n\nconst CJK_RE = /[\\p{Script=Han}\\p{Script=Hiragana}\\p{Script=Katakana}\\p{Script=Hangul}]/u;\nconst HAN_RE = /\\p{Script=Han}/u;\nconst KANA_RE = /[\\p{Script=Hiragana}\\p{Script=Katakana}]/u;\nconst HANGUL_RE = /\\p{Script=Hangul}/u;\nconst CJK_RUN_RE = /[\\p{Script=Han}\\p{Script=Hiragana}\\p{Script=Katakana}\\p{Script=Hangul}]+/gu;\nconst HANGUL_BLOCK_RE = /[가-]+/g;\n\ntype Script = \"han\" | \"kana\" | \"hangul\" | \"other\";\n\nconst hintShown = new Set<string>();\n\nexport function hasCjk(text: string): boolean {\n return CJK_RE.test(text);\n}\n\nexport function detectScript(text: string): Script {\n if (HAN_RE.test(text)) return \"han\";\n if (KANA_RE.test(text)) return \"kana\";\n if (HANGUL_RE.test(text)) return \"hangul\";\n return \"other\";\n}\n\nfunction showHintOnce(key: string, message: string): void {\n if (hintShown.has(key)) return;\n hintShown.add(key);\n if (typeof process !== \"undefined\" && process.stderr?.write) {\n process.stderr.write(`agentmemory: ${message}\\n`);\n }\n}\n\ninterface JiebaInstance {\n cut(text: string, hmm?: boolean): string[];\n}\n\nlet jiebaInstance: JiebaInstance | null = null;\nlet jiebaLoaded = false;\n\nfunction getJieba(): JiebaInstance | null {\n if (jiebaLoaded) return jiebaInstance;\n jiebaLoaded = true;\n try {\n const mod = cjkRequire(\"@node-rs/jieba\") as {\n Jieba: {\n new (): JiebaInstance;\n withDict(dict: Uint8Array): JiebaInstance;\n };\n };\n try {\n const dictMod = cjkRequire(\"@node-rs/jieba/dict\") as { dict: Uint8Array };\n jiebaInstance = mod.Jieba.withDict(dictMod.dict);\n } catch {\n jiebaInstance = new mod.Jieba();\n }\n return jiebaInstance;\n } catch {\n showHintOnce(\n \"jieba\",\n \"install @node-rs/jieba to improve Chinese search; falling back to whole-string tokenization\",\n );\n return null;\n }\n}\n\ninterface JaSegmenter {\n segment(text: string): string[];\n}\n\nlet jaSegmenterInstance: JaSegmenter | null = null;\nlet jaSegmenterLoaded = false;\n\nfunction getJaSegmenter(): JaSegmenter | null {\n if (jaSegmenterLoaded) return jaSegmenterInstance;\n jaSegmenterLoaded = true;\n try {\n const Ctor = cjkRequire(\"tiny-segmenter\") as new () => JaSegmenter;\n jaSegmenterInstance = new Ctor();\n return jaSegmenterInstance;\n } catch {\n showHintOnce(\n \"tiny-segmenter\",\n \"install tiny-segmenter to improve Japanese search; falling back to whole-string tokenization\",\n );\n return null;\n }\n}\n\nfunction cleanTokens(tokens: string[]): string[] {\n const out: string[] = [];\n for (const t of tokens) {\n const trimmed = t.trim();\n if (trimmed) out.push(trimmed);\n }\n return out;\n}\n\nfunction segmentHan(text: string): string[] {\n const j = getJieba();\n if (!j) return [text];\n try {\n return cleanTokens(j.cut(text, true));\n } catch {\n return [text];\n }\n}\n\nfunction segmentKana(text: string): string[] {\n const s = getJaSegmenter();\n if (!s) return [text];\n try {\n return cleanTokens(s.segment(text));\n } catch {\n return [text];\n }\n}\n\nfunction segmentHangul(text: string): string[] {\n const out: string[] = [];\n for (const m of text.matchAll(HANGUL_BLOCK_RE)) {\n if (m[0]) out.push(m[0]);\n }\n return out;\n}\n\nexport function segmentCjk(text: string): string[] {\n if (!hasCjk(text)) return [text];\n\n const out: string[] = [];\n let cursor = 0;\n\n for (const m of text.matchAll(CJK_RUN_RE)) {\n const start = m.index ?? 0;\n const run = m[0];\n const end = start + run.length;\n\n if (start > cursor) {\n const piece = text.slice(cursor, start).trim();\n if (piece) out.push(piece);\n }\n\n if (HANGUL_RE.test(run)) {\n out.push(...segmentHangul(run));\n } else if (KANA_RE.test(run)) {\n out.push(...segmentKana(run));\n } else {\n out.push(...segmentHan(run));\n }\n\n cursor = end;\n }\n\n if (cursor < text.length) {\n const trailing = text.slice(cursor).trim();\n if (trailing) out.push(trailing);\n }\n\n return out;\n}\n\nexport function __resetCjkSegmenterStateForTests(): void {\n hintShown.clear();\n jiebaInstance = null;\n jiebaLoaded = false;\n jaSegmenterInstance = null;\n jaSegmenterLoaded = false;\n}\n","import { createHash } from \"node:crypto\";\nimport { hasCjk, segmentCjk } from \"./cjk-segmenter.js\";\n\nexport const KV = {\n sessions: \"mem:sessions\",\n observations: (sessionId: string) => `mem:obs:${sessionId}`,\n memories: \"mem:memories\",\n summaries: \"mem:summaries\",\n config: \"mem:config\",\n metrics: \"mem:metrics\",\n health: \"mem:health\",\n embeddings: (obsId: string) => `mem:emb:${obsId}`,\n bm25Index: \"mem:index:bm25\",\n vectorPendingLog: \"mem:index:vec-pending\",\n relations: \"mem:relations\",\n profiles: \"mem:profiles\",\n claudeBridge: \"mem:claude-bridge\",\n graphNodes: \"mem:graph:nodes\",\n graphEdges: \"mem:graph:edges\",\n graphSnapshot: \"mem:graph:snapshot\",\n graphNameIndex: \"mem:graph:name-index\",\n graphEdgeKey: \"mem:graph:edge-key\",\n graphNodeDegree: \"mem:graph:node-degree\",\n semantic: \"mem:semantic\",\n procedural: \"mem:procedural\",\n teamShared: (teamId: string) => `mem:team:${teamId}:shared`,\n teamUsers: (teamId: string, userId: string) =>\n `mem:team:${teamId}:users:${userId}`,\n teamProfile: (teamId: string) => `mem:team:${teamId}:profile`,\n audit: \"mem:audit\",\n auditMonth: (month: string) => `mem:audit:${month}`,\n auditMonths: \"mem:audit:months\",\n actions: \"mem:actions\",\n actionEdges: \"mem:action-edges\",\n leases: \"mem:leases\",\n routines: \"mem:routines\",\n routineRuns: \"mem:routine-runs\",\n signals: \"mem:signals\",\n checkpoints: \"mem:checkpoints\",\n mesh: \"mem:mesh\",\n sketches: \"mem:sketches\",\n facets: \"mem:facets\",\n sentinels: \"mem:sentinels\",\n crystals: \"mem:crystals\",\n lessons: \"mem:lessons\",\n insights: \"mem:insights\",\n graphEdgeHistory: \"mem:graph:edge-history\",\n enrichedChunks: (sessionId: string) => `mem:enriched:${sessionId}`,\n latentEmbeddings: (obsId: string) => `mem:latent:${obsId}`,\n retentionScores: \"mem:retention\",\n accessLog: \"mem:access\",\n imageRefs: \"mem:image-refs\",\n imageEmbeddings: \"mem:image-embeddings\",\n slots: \"mem:slots\",\n globalSlots: \"mem:slots:global\",\n state: \"mem:state\",\n commits: \"mem:commits\",\n recentSearches: \"mem:recent-searches\",\n projectSessionsIndex: \"mem:idx:project-sessions\",\n obsSessionIndex: (shard: number) => `mem:idx:obs:${shard}`,\n captureInbox: \"mem:capture:inbox\",\n captureEvents: (shard: string) => `mem:capture:events:${shard}`,\n capturePrompts: \"mem:capture:prompts\",\n} as const;\n\nexport const STREAM = {\n name: \"mem-live\",\n viewerGroup: \"viewer\",\n} as const;\n\nexport function generateId(prefix: string): string {\n const ts = Date.now().toString(36);\n const rand = crypto.randomUUID().replace(/-/g, \"\").slice(0, 12);\n return `${prefix}_${ts}_${rand}`;\n}\n\nexport function fingerprintId(prefix: string, content: string): string {\n const hash = createHash(\"sha256\").update(content).digest(\"hex\");\n return `${prefix}_${hash.slice(0, 16)}`;\n}\n\n// CJK/Japanese/Thai text carries no inter-word whitespace, so a plain\n// split(/\\s+/) collapses a whole sentence into one token and the two token\n// sets never overlap — dedup silently stops working. When either input\n// contains CJK we segment the CJK runs (word tokens via the shared\n// segmenter, plus character bigram shingles so near-identical strings still\n// overlap even when the optional segmenter deps fall back to whole-string).\n// ASCII text keeps the original word-level behavior.\nfunction jaccardTokens(text: string): Set<string> {\n if (!hasCjk(text)) {\n return new Set(text.split(/\\s+/).filter((t) => t.length > 2));\n }\n const tokens = new Set<string>();\n for (const raw of text.split(/\\s+/)) {\n if (!raw) continue;\n if (hasCjk(raw)) {\n for (const seg of segmentCjk(raw)) {\n if (seg) tokens.add(seg);\n }\n // Character bigram shingles keep the CJK signal even when the\n // segmenter returns the whole run: overlapping bigrams make\n // near-identical strings match while unrelated ones (\"北京\" vs\n // \"上海\") share none. Never drop short CJK tokens.\n const chars = Array.from(raw);\n if (chars.length === 1) {\n tokens.add(chars[0]);\n } else {\n for (let i = 0; i < chars.length - 1; i++) {\n tokens.add(chars[i] + chars[i + 1]);\n }\n }\n } else if (raw.length > 2) {\n tokens.add(raw);\n }\n }\n return tokens;\n}\n\nexport function jaccardSimilarity(a: string, b: string): number {\n const na = a.normalize(\"NFC\");\n const nb = b.normalize(\"NFC\");\n const setA = jaccardTokens(na);\n const setB = jaccardTokens(nb);\n // An empty token set carries no signal for the overlap metric. Very short\n // ASCII text (\"AI\", \"a b\", \"go\") tokenizes to nothing because words <=2\n // chars are dropped. Fall back to exact normalized equality so re-saving\n // the identical short memory still supersedes, while unrelated short\n // strings (\"AI\" vs \"ML\") correctly score 0. Returning 1 unconditionally\n // here (as an \"both empty\" shortcut once did) would let any short memory\n // falsely supersede another, silently marking a real memory not-latest.\n if (setA.size === 0 || setB.size === 0) {\n return na.trim().replace(/\\s+/g, \" \") === nb.trim().replace(/\\s+/g, \" \")\n ? 1\n : 0;\n }\n let intersection = 0;\n for (const word of setA) {\n if (setB.has(word)) intersection++;\n }\n return intersection / (setA.size + setB.size - intersection);\n}\n","type Fields = Record<string, unknown> | undefined;\n\nfunction fmt(level: string, msg: string, fields: Fields): string {\n if (!fields || Object.keys(fields).length === 0) {\n return `[agentmemory] ${level} ${msg}`;\n }\n try {\n return `[agentmemory] ${level} ${msg} ${JSON.stringify(fields)}`;\n } catch {\n // Fields contained a circular reference or a BigInt — fall back\n // to the plain message so a log line never throws.\n return `[agentmemory] ${level} ${msg}`;\n }\n}\n\nfunction emit(level: string, msg: string, fields: Fields): void {\n try {\n process.stderr.write(fmt(level, msg, fields) + \"\\n\");\n } catch {\n // stderr is unavailable in some weird test/worker contexts — swallow\n // so no log line can ever crash a handler.\n }\n}\n\nexport const logger = {\n info(msg: string, fields?: Fields): void {\n emit(\"info\", msg, fields);\n },\n warn(msg: string, fields?: Fields): void {\n emit(\"warn\", msg, fields);\n },\n error(msg: string, fields?: Fields): void {\n emit(\"error\", msg, fields);\n },\n};\n\n// ---------- boot log ----------\n//\n// `bootLog` is for the one-shot status lines that every register-*\n// function used to dump via `console.log` during engine startup. On a\n// fresh install that's ~25 lines of `[agentmemory] X enabled` noise\n// before the user can see a prompt. In quiet mode (default), each\n// line is captured into a buffer and discarded; the CLI surfaces a\n// single compressed summary instead. In verbose mode (set by\n// `--verbose` or `AGENTMEMORY_VERBOSE=1`) the lines pass straight\n// through to stderr exactly like the old console.log calls.\n\nlet bootVerbose =\n process.env[\"AGENTMEMORY_VERBOSE\"] === \"1\" ||\n process.env[\"AGENTMEMORY_VERBOSE\"] === \"true\";\n\nconst bootBuffer: string[] = [];\n\nexport function setBootVerbose(enabled: boolean): void {\n bootVerbose = enabled;\n}\n\nexport function isBootVerbose(): boolean {\n return bootVerbose;\n}\n\nexport function bootLog(msg: string): void {\n if (bootVerbose) {\n try {\n process.stderr.write(`[agentmemory] ${msg}\\n`);\n } catch {\n // stderr unavailable — drop.\n }\n return;\n }\n if (bootBuffer.length < 500) bootBuffer.push(msg);\n}\n\nexport function bootWarn(msg: string): void {\n // Warnings always surface; they're rare and the user needs to see\n // them even when the rest of the boot log is suppressed.\n try {\n process.stderr.write(`[agentmemory] warn ${msg}\\n`);\n } catch {}\n}\n\nexport function getBootBuffer(): readonly string[] {\n return bootBuffer;\n}\n","import { stat, readFile } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type { AuditEntry, AuditMigrationState, AuditQueryResult } from \"../types.js\";\nimport { KV, generateId } from \"../state/schema.js\";\nimport type { StateKV } from \"../state/kv.js\";\nimport { logger } from \"../logger.js\";\nimport { withKeyedLock } from \"../state/keyed-mutex.js\";\nimport { getEnvVar, getAuditMigrateMaxBytes } from \"../config.js\";\nimport { runtimeConfigPath } from \"../cli/engine-launch.js\";\nimport { ENGINE_DEFAULT_SAVE_INTERVAL_MS, engineSaveIntervalMs, engineStateConfigPaths } from \"../cli/engine-config.js\";\n\nconst AUDIT_MIGRATE_FUNCTION_ID = \"mem::audit-migrate\";\nconst AUDIT_MIGRATION_COPY_CONCURRENCY = 32;\nconst AUDIT_MIGRATION_DELETE_CONCURRENCY = 8;\nconst AUDIT_MONTHS_LOCK = KV.auditMonths;\nconst AUDIT_MIGRATION_LOCK = \"mem:audit:migration-lock\";\nexport const AUDIT_MIGRATION_STATE_KEY = \"migration\";\nconst AUDIT_MIGRATION_DELETE_WAIT_INTERVALS = 2;\nconst STATE_STORE_DIR_NAME = \"state_store.db\";\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction encodeStateIndexSegment(index: string): string {\n let out = \"\";\n for (const byte of Buffer.from(index, \"utf8\")) {\n const ch = String.fromCharCode(byte);\n out += /^[A-Za-z0-9\\-_.]$/.test(ch)\n ? ch\n : `%${byte.toString(16).toUpperCase().padStart(2, \"0\")}`;\n }\n return out;\n}\n\nfunction legacyAuditStateFilePath(dataDir: string): string {\n return join(dataDir, STATE_STORE_DIR_NAME, `${encodeStateIndexSegment(KV.audit)}.bin`);\n}\n\nexport type LegacyAuditProbe =\n | { safe: true; empty: true }\n | { safe: true; empty: false; sizeBytes: number }\n | { safe: false; reason: \"too-large\"; sizeBytes: number }\n | { safe: false; reason: \"unreadable\" };\n\nexport async function probeLegacyAuditFile(): Promise<LegacyAuditProbe> {\n const dataDir = getEnvVar(\"AGENTMEMORY_DATA_DIR\");\n if (!dataDir) return { safe: false, reason: \"unreadable\" };\n\n const storeDir = join(dataDir, STATE_STORE_DIR_NAME);\n try {\n await stat(storeDir);\n } catch {\n return { safe: false, reason: \"unreadable\" };\n }\n\n let fileStat;\n try {\n fileStat = await stat(legacyAuditStateFilePath(dataDir));\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") {\n return { safe: true, empty: true };\n }\n return { safe: false, reason: \"unreadable\" };\n }\n\n if (fileStat.size > getAuditMigrateMaxBytes()) {\n return { safe: false, reason: \"too-large\", sizeBytes: fileStat.size };\n }\n return { safe: true, empty: false, sizeBytes: fileStat.size };\n}\n\nexport async function probeLegacyAuditScope(kv: StateKV): Promise<LegacyAuditProbe> {\n let rows: AuditEntry[];\n try {\n rows = await kv.list<AuditEntry>(KV.audit);\n } catch {\n return { safe: false, reason: \"unreadable\" };\n }\n if (!Array.isArray(rows) || rows.length === 0) return { safe: true, empty: true };\n const sizeBytes = Buffer.byteLength(JSON.stringify(rows), \"utf8\");\n if (sizeBytes > getAuditMigrateMaxBytes()) {\n return { safe: false, reason: \"too-large\", sizeBytes };\n }\n return { safe: true, empty: false, sizeBytes };\n}\n\nasync function auditMigrationDeleteDelayMs(): Promise<number> {\n const dataDir = getEnvVar(\"AGENTMEMORY_DATA_DIR\");\n if (!dataDir) return ENGINE_DEFAULT_SAVE_INTERVAL_MS * AUDIT_MIGRATION_DELETE_WAIT_INTERVALS;\n const configTexts: string[] = [];\n for (const path of engineStateConfigPaths(join(homedir(), \".agentmemory\"), runtimeConfigPath(dataDir))) {\n try {\n configTexts.push(await readFile(path, \"utf-8\"));\n } catch {}\n }\n return engineSaveIntervalMs(configTexts) * AUDIT_MIGRATION_DELETE_WAIT_INTERVALS;\n}\n\nasync function readAuditMigrationState(kv: StateKV): Promise<AuditMigrationState | null> {\n return kv.get<AuditMigrationState>(KV.auditMonths, AUDIT_MIGRATION_STATE_KEY);\n}\n\nasync function writeAuditMigrationState(\n kv: StateKV,\n state: AuditMigrationState,\n): Promise<void> {\n await kv.set(KV.auditMonths, AUDIT_MIGRATION_STATE_KEY, state);\n}\n\nexport function auditMonthOf(timestamp: string): string {\n const parsed = new Date(timestamp);\n const iso = Number.isNaN(parsed.getTime())\n ? new Date().toISOString()\n : parsed.toISOString();\n return iso.slice(0, 7);\n}\n\nfunction monthStartMs(month: string): number {\n const [year, mon] = month.split(\"-\").map(Number);\n return Date.UTC(year, mon - 1, 1);\n}\n\nfunction monthEndMs(month: string): number {\n const [year, mon] = month.split(\"-\").map(Number);\n return Date.UTC(year, mon, 1) - 1;\n}\n\nfunction monthNMonthsAgo(count: number): string {\n const now = new Date();\n const cutoff = new Date(\n Date.UTC(now.getUTCFullYear(), now.getUTCMonth() - count, 1),\n );\n return cutoff.toISOString().slice(0, 7);\n}\n\nasync function readAuditMonthIndex(kv: StateKV): Promise<string[]> {\n const index = await kv.get<{ months: string[] }>(KV.auditMonths, \"index\");\n return index?.months ?? [];\n}\n\nasync function markAuditMonth(kv: StateKV, month: string): Promise<void> {\n await withKeyedLock(AUDIT_MONTHS_LOCK, async () => {\n const months = await readAuditMonthIndex(kv);\n if (months.includes(month)) return;\n const next = [...months, month].sort();\n await kv.set(KV.auditMonths, \"index\", { months: next });\n });\n}\n\nexport async function listAuditMonthsDesc(kv: StateKV): Promise<string[]> {\n const months = await readAuditMonthIndex(kv);\n return [...months].sort().reverse();\n}\n\nlet auditRecordedListener: ((entry: AuditEntry) => void) | null = null;\n\nexport function setAuditRecordedListener(listener: ((entry: AuditEntry) => void) | null): void {\n auditRecordedListener = listener;\n}\n\nexport async function recordAudit(\n kv: StateKV,\n operation: AuditEntry[\"operation\"],\n functionId: string,\n targetIds: string[],\n details: Record<string, unknown> = {},\n qualityScore?: number,\n userId?: string,\n): Promise<AuditEntry> {\n const entry: AuditEntry = {\n id: generateId(\"aud\"),\n timestamp: new Date().toISOString(),\n operation,\n userId,\n functionId,\n targetIds,\n details,\n qualityScore,\n };\n const month = auditMonthOf(entry.timestamp);\n await kv.set(KV.auditMonth(month), entry.id, entry);\n await markAuditMonth(kv, month);\n try {\n auditRecordedListener?.(entry);\n } catch {}\n return entry;\n}\n\nexport async function safeAudit(\n kv: StateKV,\n operation: AuditEntry[\"operation\"],\n functionId: string,\n targetIds: string[],\n details: Record<string, unknown> = {},\n qualityScore?: number,\n userId?: string,\n): Promise<void> {\n try {\n await recordAudit(kv, operation, functionId, targetIds, details, qualityScore, userId);\n } catch (err) {\n try {\n logger.warn(\"audit write failed\", {\n functionId,\n operation,\n targetIds,\n error: err instanceof Error ? err.message : String(err),\n });\n } catch {}\n }\n}\n\nexport async function queryAudit(\n kv: StateKV,\n filter?: {\n operation?: AuditEntry[\"operation\"];\n dateFrom?: string;\n dateTo?: string;\n query?: string;\n limit?: number;\n },\n): Promise<AuditQueryResult> {\n const limit = filter?.limit || 100;\n\n let fromMs: number | undefined;\n if (filter?.dateFrom) {\n fromMs = new Date(filter.dateFrom).getTime();\n if (Number.isNaN(fromMs)) {\n throw new Error(`Invalid dateFrom: ${filter.dateFrom}`);\n }\n }\n const query = filter?.query?.trim().toLowerCase();\n\n let toMs: number | undefined;\n if (filter?.dateTo) {\n toMs = new Date(filter.dateTo).getTime();\n if (Number.isNaN(toMs)) {\n throw new Error(`Invalid dateTo: ${filter.dateTo}`);\n }\n }\n\n const matches = (entry: AuditEntry): boolean => {\n if (filter?.operation && entry.operation !== filter.operation) return false;\n const t = new Date(entry.timestamp).getTime();\n if (fromMs !== undefined && t < fromMs) return false;\n if (toMs !== undefined && t > toMs) return false;\n if (\n query &&\n ![entry.functionId, ...(entry.targetIds || [])].some((v) =>\n String(v || \"\").toLowerCase().includes(query),\n )\n ) {\n return false;\n }\n return true;\n };\n\n const seenIds = new Set<string>();\n const collected: AuditEntry[] = [];\n const addIfNew = (row: AuditEntry): void => {\n if (seenIds.has(row.id)) return;\n seenIds.add(row.id);\n if (matches(row)) collected.push(row);\n };\n\n const months = await listAuditMonthsDesc(kv);\n for (const month of months) {\n if (collected.length >= limit) break;\n if (toMs !== undefined && monthStartMs(month) > toMs) continue;\n if (fromMs !== undefined && monthEndMs(month) < fromMs) break;\n const rows = await kv.list<AuditEntry>(KV.auditMonth(month));\n for (const row of rows) addIfNew(row);\n }\n\n let legacyFrozen = false;\n let legacyFrozenBytes: number | undefined;\n\n if (collected.length < limit) {\n const migrationState = await readAuditMigrationState(kv);\n if (migrationState?.safeToListLegacy) {\n const legacyRows = await kv.list<AuditEntry>(KV.audit);\n for (const row of legacyRows) addIfNew(row);\n } else if (migrationState?.status === \"too-large\" || migrationState?.status === \"unreadable\") {\n legacyFrozen = true;\n legacyFrozenBytes = migrationState.legacySizeBytes;\n }\n }\n\n collected.sort(\n (a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime(),\n );\n return {\n entries: collected.slice(0, limit),\n legacyFrozen,\n legacyFrozenBytes,\n };\n}\n\nasync function inParallel<T>(\n items: T[],\n concurrency: number,\n work: (item: T) => Promise<void>,\n): Promise<void> {\n let next = 0;\n const runners = Array.from({ length: Math.min(concurrency, items.length) }, async () => {\n while (next < items.length) {\n const item = items[next++];\n await work(item);\n }\n });\n await Promise.all(runners);\n}\n\nexport interface AuditMigrationDeps {\n probe?: () => Promise<LegacyAuditProbe>;\n deleteDelayMs?: () => Promise<number>;\n}\n\nexport async function startAuditMigration(\n kv: StateKV,\n deps: AuditMigrationDeps = {},\n): Promise<void> {\n const onRedis = kv.backend === \"redis\";\n const runProbe =\n deps.probe ?? (onRedis ? () => probeLegacyAuditScope(kv) : probeLegacyAuditFile);\n const runDeleteDelayMs =\n deps.deleteDelayMs ?? (onRedis ? async () => 0 : auditMigrationDeleteDelayMs);\n\n const marker = await readAuditMigrationState(kv);\n if (marker?.status === \"done\") return;\n\n const probe = await runProbe();\n\n if (probe.safe && probe.empty) {\n await writeAuditMigrationState(kv, {\n status: \"done\",\n safeToListLegacy: true,\n legacySizeBytes: 0,\n migrated: marker?.migrated ?? 0,\n purged: marker?.purged ?? 0,\n summaryWritten: marker?.summaryWritten ?? true,\n checkedAt: new Date().toISOString(),\n });\n return;\n }\n\n if (!probe.safe) {\n await writeAuditMigrationState(kv, {\n status: probe.reason,\n safeToListLegacy: false,\n legacySizeBytes: probe.reason === \"too-large\" ? probe.sizeBytes : marker?.legacySizeBytes,\n migrated: marker?.migrated ?? 0,\n purged: marker?.purged ?? 0,\n summaryWritten: marker?.summaryWritten ?? false,\n checkedAt: new Date().toISOString(),\n });\n logger.warn(\"legacy audit log left frozen; migration only reads it locally and under a size cap\", {\n reason: probe.reason,\n sizeBytes: probe.reason === \"too-large\" ? probe.sizeBytes : undefined,\n });\n return;\n }\n\n let migrated = marker?.migrated ?? 0;\n let purged = marker?.purged ?? 0;\n\n if (marker?.status !== \"copied\") {\n let legacy: AuditEntry[];\n try {\n legacy = await kv.list<AuditEntry>(KV.audit);\n } catch (err) {\n logger.warn(\"audit migration could not read the legacy audit log\", {\n error: err instanceof Error ? err.message : String(err),\n });\n return;\n }\n if (legacy.length === 0) {\n await writeAuditMigrationState(kv, {\n status: \"done\",\n safeToListLegacy: true,\n legacySizeBytes: 0,\n migrated,\n purged,\n summaryWritten: marker?.summaryWritten ?? true,\n checkedAt: new Date().toISOString(),\n });\n return;\n }\n\n const seen = new Set<string>();\n const deduped = legacy.filter((entry) => {\n if (seen.has(entry.id)) return false;\n seen.add(entry.id);\n return true;\n });\n const kept = deduped.filter((entry) => entry.operation !== \"index_persist\");\n purged = deduped.length - kept.length;\n migrated = kept.length;\n\n try {\n const months = new Set<string>();\n await inParallel(kept, AUDIT_MIGRATION_COPY_CONCURRENCY, async (entry) => {\n const month = auditMonthOf(entry.timestamp);\n await kv.set(KV.auditMonth(month), entry.id, entry);\n months.add(month);\n });\n for (const month of months) await markAuditMonth(kv, month);\n } catch (err) {\n logger.warn(\n \"audit migration stopped before finishing the copy pass; it resumes on the next boot\",\n { error: err instanceof Error ? err.message : String(err) },\n );\n return;\n }\n\n await writeAuditMigrationState(kv, {\n status: \"copied\",\n safeToListLegacy: false,\n legacySizeBytes: probe.sizeBytes,\n migrated,\n purged,\n summaryWritten: false,\n checkedAt: new Date().toISOString(),\n });\n }\n\n await sleep(await runDeleteDelayMs());\n\n let remaining: AuditEntry[];\n try {\n remaining = await kv.list<AuditEntry>(KV.audit);\n } catch (err) {\n logger.warn(\n \"audit migration could not re-read the legacy audit log before deleting; it resumes on the next boot\",\n { error: err instanceof Error ? err.message : String(err) },\n );\n return;\n }\n\n try {\n await inParallel(\n remaining.slice().reverse(),\n AUDIT_MIGRATION_DELETE_CONCURRENCY,\n async (entry) => {\n await kv.delete(KV.audit, entry.id);\n },\n );\n } catch (err) {\n logger.warn(\n \"audit migration stopped before finishing the delete pass; it resumes on the next boot\",\n { error: err instanceof Error ? err.message : String(err) },\n );\n return;\n }\n\n await withKeyedLock(AUDIT_MIGRATION_LOCK, async () => {\n const current = await readAuditMigrationState(kv);\n if (current?.summaryWritten) return;\n if (purged > 0) {\n await safeAudit(kv, \"audit_migrate\", AUDIT_MIGRATE_FUNCTION_ID, [], {\n reason: \"index_persist rows predate opt-in auditing\",\n purged,\n migrated,\n });\n }\n await writeAuditMigrationState(kv, {\n status: \"done\",\n safeToListLegacy: true,\n legacySizeBytes: 0,\n migrated,\n purged,\n summaryWritten: true,\n checkedAt: new Date().toISOString(),\n });\n });\n\n logger.info(\"audit log moved to monthly scopes\", { migrated, purged });\n}\n\nexport async function runAuditRetentionSweep(\n kv: StateKV,\n retentionMonths: number,\n): Promise<{ droppedMonths: string[]; droppedRows: number }> {\n if (!Number.isFinite(retentionMonths) || retentionMonths <= 0) {\n return { droppedMonths: [], droppedRows: 0 };\n }\n\n const months = await readAuditMonthIndex(kv);\n const cutoff = monthNMonthsAgo(retentionMonths);\n const toDrop = months.filter((month) => month < cutoff).sort();\n if (toDrop.length === 0) {\n return { droppedMonths: [], droppedRows: 0 };\n }\n\n let droppedRows = 0;\n for (const month of toDrop) {\n const rows = await kv.list<AuditEntry>(KV.auditMonth(month));\n for (const row of rows.slice().reverse()) {\n await kv.delete(KV.auditMonth(month), row.id);\n droppedRows++;\n }\n }\n\n await withKeyedLock(AUDIT_MONTHS_LOCK, async () => {\n const current = await readAuditMonthIndex(kv);\n const remaining = current.filter((month) => !toDrop.includes(month)).sort();\n await kv.set(KV.auditMonths, \"index\", { months: remaining });\n });\n\n return { droppedMonths: toDrop, droppedRows };\n}\n","import {\n chmodSync,\n closeSync,\n linkSync,\n mkdirSync,\n openSync,\n readFileSync,\n unlinkSync,\n writeSync,\n} from \"node:fs\";\nimport { randomBytes } from \"node:crypto\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\nconst SECRET_KEY = \"AGENTMEMORY_SECRET\";\n\nexport function agentmemoryHomeDir(): string {\n return join(homedir(), \".agentmemory\");\n}\n\nexport function secretFilePath(): string {\n return join(agentmemoryHomeDir(), \"secret\");\n}\n\nfunction usable(value: string | undefined): string {\n if (typeof value !== \"string\") return \"\";\n const trimmed = value.trim();\n if (!trimmed) return \"\";\n if (trimmed.startsWith(\"${\") && trimmed.endsWith(\"}\")) return \"\";\n return trimmed;\n}\n\nfunction unquote(value: string): string {\n const quote = value[0];\n if ((quote === '\"' || quote === \"'\") && value.length > 1) {\n const close = value.indexOf(quote, 1);\n if (close !== -1) return value.slice(1, close);\n }\n const hash = value.indexOf(\" #\");\n return hash === -1 ? value : value.slice(0, hash).trim();\n}\n\nexport function readEnvFileSecret(): string {\n let content: unknown;\n try {\n content = readFileSync(join(agentmemoryHomeDir(), \".env\"), \"utf-8\");\n } catch {\n return \"\";\n }\n if (typeof content !== \"string\") return \"\";\n let found = \"\";\n for (const line of content.split(\"\\n\")) {\n const trimmed = line.trim();\n if (trimmed.startsWith(\"#\")) continue;\n const eq = trimmed.indexOf(\"=\");\n if (eq === -1) continue;\n const key = trimmed.slice(0, eq).replace(/^export\\s+/, \"\").trim();\n if (key !== SECRET_KEY) continue;\n found = usable(unquote(trimmed.slice(eq + 1).trim()));\n }\n return found;\n}\n\nexport function readStoredSecret(): string {\n try {\n return usable(readFileSync(secretFilePath(), \"utf-8\"));\n } catch {\n return \"\";\n }\n}\n\nexport function explicitSecret(env: NodeJS.ProcessEnv = process.env): string {\n return usable(env[SECRET_KEY]) || readEnvFileSecret();\n}\n\nexport function isLoopbackUrl(url: string): boolean {\n let hostname: string;\n try {\n hostname = new URL(url).hostname.toLowerCase();\n } catch {\n return false;\n }\n const bare = hostname.replace(/^\\[|\\]$/g, \"\");\n return bare === \"localhost\" || bare === \"::1\" || /^127(?:\\.\\d{1,3}){3}$/.test(bare);\n}\n\nexport function resolveClientSecret(\n baseUrl: string,\n env: NodeJS.ProcessEnv = process.env,\n): string {\n const fromEnv = usable(env[SECRET_KEY]);\n if (fromEnv) return fromEnv;\n if (!isLoopbackUrl(baseUrl)) return \"\";\n return readEnvFileSecret() || readStoredSecret();\n}\n\nexport function ensureServerSecret(env: NodeJS.ProcessEnv = process.env): {\n secret: string;\n source: \"env\" | \"file\" | \"generated\";\n} {\n const configured = explicitSecret(env);\n if (configured) return { secret: configured, source: \"env\" };\n const stored = readStoredSecret();\n if (stored) return { secret: stored, source: \"file\" };\n const generated = randomBytes(32).toString(\"hex\");\n const path = secretFilePath();\n mkdirSync(agentmemoryHomeDir(), { recursive: true, mode: 0o700 });\n const staging = `${path}.${process.pid}.${randomBytes(4).toString(\"hex\")}.tmp`;\n const fd = openSync(staging, \"wx\", 0o600);\n try {\n writeSync(fd, `${generated}\\n`);\n } finally {\n closeSync(fd);\n }\n try {\n chmodSync(staging, 0o600);\n linkSync(staging, path);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== \"EEXIST\") throw err;\n const raced = readStoredSecret();\n if (!raced) throw err;\n return { secret: raced, source: \"file\" };\n } finally {\n try {\n unlinkSync(staging);\n } catch {}\n }\n return { secret: generated, source: \"generated\" };\n}\n\nexport function bearerHeaders(\n baseUrl: string,\n env: NodeJS.ProcessEnv = process.env,\n): Record<string, string> {\n const secret = resolveClientSecret(baseUrl, env);\n return secret ? { Authorization: `Bearer ${secret}` } : {};\n}\n","import { resolveClientSecret } from \"../secret-store.js\";\nconst DEFAULT_URL = \"http://localhost:3111\";\nconst DEFAULT_HEALTH_PROBE_TIMEOUT_MS = 2_000;\nconst CALL_TIMEOUT_MS = 15_000;\nconst LOCAL_MODE_TTL_MS = 30_000;\n\nfunction probeTimeoutMs(): number {\n const raw = process.env[\"AGENTMEMORY_PROBE_TIMEOUT_MS\"];\n if (!raw) return DEFAULT_HEALTH_PROBE_TIMEOUT_MS;\n const n = Number(raw);\n return Number.isFinite(n) && n > 0 ? Math.floor(n) : DEFAULT_HEALTH_PROBE_TIMEOUT_MS;\n}\n\nfunction forceProxy(): boolean {\n const raw = process.env[\"AGENTMEMORY_FORCE_PROXY\"];\n return raw === \"1\" || raw === \"true\";\n}\n\nexport interface ProxyHandle {\n mode: \"proxy\";\n baseUrl: string;\n call: (path: string, init?: RequestInit) => Promise<unknown>;\n}\n\nexport class ProxyCallError extends Error {\n status: number;\n body: unknown;\n\n constructor(message: string, status: number, body: unknown) {\n super(message);\n this.name = \"ProxyCallError\";\n this.status = status;\n this.body = body;\n }\n}\n\nexport interface LocalHandle {\n mode: \"local\";\n}\n\nexport type Handle = ProxyHandle | LocalHandle;\n\nlet cached: Handle | null = null;\nlet cachedAt = 0;\nlet probeInFlight: Promise<Handle> | null = null;\n\n// `${VAR}`-style placeholders ship in plugin/.mcp.json so MCP hosts that\n// expand them (Claude Code, Cursor) substitute the user's shell value.\n// Hosts that DON'T expand pass the literal string `\"${AGENTMEMORY_URL}\"`\n// through to our subprocess — that string is truthy, defeats the `||`\n// fallback, and would have us POST to `${AGENTMEMORY_URL}/agentmemory/...`\n// (DNS failure). Strip any literal placeholder we see so the fallback\n// engages instead.\nexport function resolveEnvOrEmpty(name: string): string {\n const raw = process.env[name];\n if (!raw) return \"\";\n if (raw.startsWith(\"${\") && raw.endsWith(\"}\")) return \"\";\n return raw;\n}\n\nfunction baseUrl(): string {\n return (resolveEnvOrEmpty(\"AGENTMEMORY_URL\") || DEFAULT_URL).replace(/\\/+$/, \"\");\n}\n\nfunction authHeader(): Record<string, string> {\n const secret = resolveClientSecret(baseUrl());\n return secret ? { authorization: `Bearer ${secret}` } : {};\n}\n\nexport type LivezProbe = (\n url: string,\n timeoutMs: number,\n headers: Record<string, string>,\n) => Promise<{ ok: boolean; status?: number; statusText?: string }>;\n\nconst defaultLivezProbe: LivezProbe = async (url, timeoutMs, headers) => {\n const res = await fetch(`${url}/agentmemory/livez`, {\n method: \"GET\",\n headers,\n signal: AbortSignal.timeout(timeoutMs),\n });\n return { ok: res.ok, status: res.status, statusText: res.statusText };\n};\n\nlet livezProbe: LivezProbe = defaultLivezProbe;\n\n/**\n * Override the livez probe. Intended for tests — production code should rely\n * on the default fetch-based probe. Calling without an argument restores the\n * default. Pair with {@link resetHandleForTests} so the cached handle is\n * dropped before the next call.\n */\nexport function setLivezProbe(fn?: LivezProbe): void {\n livezProbe = fn ?? defaultLivezProbe;\n}\n\nasync function probe(url: string): Promise<boolean> {\n const timeout = probeTimeoutMs();\n try {\n const res = await livezProbe(url, timeout, authHeader());\n if (!res.ok) {\n process.stderr.write(\n `[@agentmemory/mcp] livez probe ${url}/agentmemory/livez -> ${res.status ?? \"?\"} ${res.statusText ?? \"\"}; falling back to local InMemoryKV (set AGENTMEMORY_FORCE_PROXY=1 to skip the probe)\\n`,\n );\n }\n return res.ok;\n } catch (err) {\n process.stderr.write(\n `[@agentmemory/mcp] livez probe ${url}/agentmemory/livez failed in ${timeout}ms: ${err instanceof Error ? err.message : String(err)}; falling back to local InMemoryKV (set AGENTMEMORY_FORCE_PROXY=1 to skip the probe, or raise AGENTMEMORY_PROBE_TIMEOUT_MS)\\n`,\n );\n return false;\n }\n}\n\nexport function invalidateHandle(): void {\n cached = null;\n cachedAt = 0;\n}\n\nexport async function resolveHandle(): Promise<Handle> {\n const now = Date.now();\n if (cached) {\n if (cached.mode === \"local\" && now - cachedAt >= LOCAL_MODE_TTL_MS) {\n cached = null;\n cachedAt = 0;\n } else {\n return cached;\n }\n }\n if (probeInFlight) return probeInFlight;\n const url = baseUrl();\n const skipProbe = forceProxy();\n probeInFlight = (async () => {\n const up = skipProbe ? true : await probe(url);\n if (skipProbe) {\n process.stderr.write(\n `[@agentmemory/mcp] AGENTMEMORY_FORCE_PROXY set; skipping livez probe and trusting ${url}\\n`,\n );\n }\n if (up) {\n const handle: ProxyHandle = {\n mode: \"proxy\",\n baseUrl: url,\n call: async (path, init) => {\n const res = await fetch(`${url}${path}`, {\n ...init,\n headers: {\n \"content-type\": \"application/json\",\n ...authHeader(),\n ...(init?.headers as Record<string, string> | undefined),\n },\n signal: AbortSignal.timeout(CALL_TIMEOUT_MS),\n });\n if (!res.ok) {\n const errText = await res.text().catch(() => \"\");\n let errBody: unknown = undefined;\n if (errText) {\n try {\n errBody = JSON.parse(errText);\n } catch {\n errBody = undefined;\n }\n }\n throw new ProxyCallError(\n `${init?.method || \"GET\"} ${path} -> ${res.status} ${res.statusText}`,\n res.status,\n errBody,\n );\n }\n const text = await res.text();\n return text ? JSON.parse(text) : null;\n },\n };\n cached = handle;\n cachedAt = Date.now();\n return handle;\n }\n const local: LocalHandle = { mode: \"local\" };\n cached = local;\n cachedAt = Date.now();\n return local;\n })();\n try {\n return await probeInFlight;\n } finally {\n probeInFlight = null;\n }\n}\n\nexport function resetHandleForTests(): void {\n cached = null;\n cachedAt = 0;\n probeInFlight = null;\n livezProbe = defaultLivezProbe;\n}\n","#!/usr/bin/env node\n\nimport { InMemoryKV } from \"./in-memory-kv.js\";\nimport { createStdioTransport } from \"./transport.js\";\nimport { getAllTools } from \"./tools-registry.js\";\nimport { getStandalonePersistPath } from \"../config.js\";\nimport { VERSION } from \"../version.js\";\nimport { generateId } from \"../state/schema.js\";\nimport { queryAudit } from \"../functions/audit.js\";\nimport {\n resolveHandle,\n invalidateHandle,\n ProxyCallError,\n type Handle,\n type ProxyHandle,\n} from \"./rest-proxy.js\";\n\nconst IMPLEMENTED_TOOLS = new Set([\n \"memory_save\",\n \"memory_recall\",\n \"memory_smart_search\",\n \"memory_sessions\",\n \"memory_export\",\n \"memory_audit\",\n \"memory_governance_delete\",\n]);\n\nconst SUPPORTED_PROTOCOL_VERSIONS = [\n \"2025-11-25\",\n \"2025-06-18\",\n \"2025-03-26\",\n \"2024-11-05\",\n];\n\nconst SERVER_INFO = {\n name: \"agentmemory\",\n version: VERSION,\n};\n\nconst kv = new InMemoryKV(getStandalonePersistPath());\nlet modeAnnounced = false;\n\nfunction displayAgentmemoryUrl(): string {\n // Match the literal-placeholder guard in rest-proxy.ts so log lines\n // don't show `${AGENTMEMORY_URL}` when an MCP host passed the\n // placeholder through unexpanded.\n const raw = process.env[\"AGENTMEMORY_URL\"];\n if (!raw || (raw.startsWith(\"${\") && raw.endsWith(\"}\"))) {\n return \"http://localhost:3111\";\n }\n return raw;\n}\n\nfunction announceMode(handle: Handle): void {\n if (modeAnnounced) return;\n modeAnnounced = true;\n if (handle.mode === \"proxy\") {\n process.stderr.write(\n `[@agentmemory/mcp] proxying to agentmemory server at ${handle.baseUrl}\\n`,\n );\n } else {\n const fullToolCount = getAllTools().length;\n process.stderr.write(\n `[@agentmemory/mcp] no server reachable at ${displayAgentmemoryUrl()}; running reduced LOCAL FALLBACK with ${IMPLEMENTED_TOOLS.size} of ${fullToolCount} tools. Start 'npx @agentmemory/agentmemory' (and point AGENTMEMORY_URL at it) to unlock all ${fullToolCount} tools.\\n`,\n );\n }\n}\n\nfunction normalizeList(value: unknown): string[] {\n if (!value) return [];\n if (Array.isArray(value)) {\n return value\n .map((v) => (typeof v === \"string\" ? v.trim() : \"\"))\n .filter((v) => v.length > 0);\n }\n if (typeof value === \"string\") {\n return value\n .split(\",\")\n .map((s) => s.trim())\n .filter((s) => s.length > 0);\n }\n return [];\n}\n\nconst DEFAULT_LIMIT = 10;\nconst MAX_LIMIT = 100;\nfunction parseLimit(raw: unknown, fallback = DEFAULT_LIMIT): number {\n if (typeof raw !== \"number\" && typeof raw !== \"string\") return fallback;\n const n = Number(raw);\n if (!Number.isFinite(n) || n <= 0) return fallback;\n return Math.min(Math.floor(n), MAX_LIMIT);\n}\n\nfunction textResponse(payload: unknown, pretty = false): {\n content: Array<{ type: string; text: string }>;\n} {\n return {\n content: [\n { type: \"text\", text: JSON.stringify(payload, null, pretty ? 2 : 0) },\n ],\n };\n}\n\ninterface Validated {\n tool: string;\n content?: string;\n type?: string;\n concepts?: string[];\n files?: string[];\n project?: string;\n agentId?: string;\n query?: string;\n limit?: number;\n format?: string;\n tokenBudget?: number;\n memoryIds?: string[];\n reason?: string;\n}\n\nfunction validate(toolName: string, args: Record<string, unknown>): Validated {\n if (!IMPLEMENTED_TOOLS.has(toolName)) {\n throw new Error(`Unknown tool: ${toolName}`);\n }\n const v: Validated = { tool: toolName };\n switch (toolName) {\n case \"memory_save\": {\n const content = args[\"content\"];\n if (typeof content !== \"string\" || !content.trim()) {\n throw new Error(\"content is required\");\n }\n v.content = content;\n v.type = (args[\"type\"] as string) || \"fact\";\n v.concepts = normalizeList(args[\"concepts\"]);\n v.files = normalizeList(args[\"files\"]);\n // The tool schema exposes project (and now agentId); dropping them\n // here silently broke project/agent scoping through the stdio\n // package specifically.\n if (typeof args[\"project\"] === \"string\" && args[\"project\"].trim()) {\n v.project = args[\"project\"].trim();\n }\n if (typeof args[\"agentId\"] === \"string\" && args[\"agentId\"].trim()) {\n v.agentId = args[\"agentId\"].trim();\n }\n return v;\n }\n case \"memory_recall\":\n case \"memory_smart_search\": {\n const query = args[\"query\"];\n if (typeof query !== \"string\" || !query.trim()) {\n throw new Error(\"query is required\");\n }\n v.query = query.trim();\n v.limit = parseLimit(args[\"limit\"]);\n const fmt = args[\"format\"];\n if (typeof fmt === \"string\" && fmt.trim()) {\n v.format = fmt.trim().toLowerCase();\n }\n const budget = args[\"token_budget\"];\n if (typeof budget === \"number\" && Number.isFinite(budget) && budget > 0) {\n v.tokenBudget = Math.floor(budget);\n } else if (typeof budget === \"string\" && budget.trim()) {\n const n = Number(budget);\n if (Number.isFinite(n) && n > 0) v.tokenBudget = Math.floor(n);\n }\n return v;\n }\n case \"memory_sessions\": {\n v.limit = parseLimit(args[\"limit\"], 20);\n return v;\n }\n case \"memory_governance_delete\": {\n const ids = normalizeList(args[\"memoryIds\"]);\n if (ids.length === 0) throw new Error(\"memoryIds is required\");\n v.memoryIds = ids;\n v.reason = (args[\"reason\"] as string) || \"plugin skill request\";\n return v;\n }\n case \"memory_export\":\n return v;\n case \"memory_audit\": {\n v.limit = parseLimit(args[\"limit\"], 50);\n return v;\n }\n default:\n throw new Error(`Unknown tool: ${toolName}`);\n }\n}\n\nasync function handleProxy(\n v: Validated,\n handle: ProxyHandle,\n): Promise<{ content: Array<{ type: string; text: string }> }> {\n switch (v.tool) {\n case \"memory_save\": {\n const result = await handle.call(\"/agentmemory/remember\", {\n method: \"POST\",\n body: JSON.stringify({\n content: v.content,\n type: v.type,\n concepts: v.concepts,\n files: v.files,\n ...(v.project !== undefined && { project: v.project }),\n ...(v.agentId !== undefined && { agentId: v.agentId }),\n }),\n });\n return textResponse(result);\n }\n case \"memory_recall\": {\n const body: Record<string, unknown> = {\n query: v.query,\n limit: v.limit,\n format: v.format ?? \"full\",\n };\n if (v.tokenBudget != null) body[\"token_budget\"] = v.tokenBudget;\n const result = await handle.call(\"/agentmemory/search\", {\n method: \"POST\",\n body: JSON.stringify(body),\n });\n return textResponse(result, true);\n }\n case \"memory_smart_search\": {\n const body: Record<string, unknown> = { query: v.query, limit: v.limit };\n if (v.format != null) body[\"format\"] = v.format;\n if (v.tokenBudget != null) body[\"token_budget\"] = v.tokenBudget;\n const result = await handle.call(\"/agentmemory/smart-search\", {\n method: \"POST\",\n body: JSON.stringify(body),\n });\n return textResponse(result, true);\n }\n case \"memory_sessions\": {\n const result = await handle.call(\n `/agentmemory/sessions?limit=${v.limit}`,\n { method: \"GET\" },\n );\n return textResponse(result, true);\n }\n case \"memory_governance_delete\": {\n const result = await handle.call(\"/agentmemory/governance/memories\", {\n method: \"DELETE\",\n body: JSON.stringify({ memoryIds: v.memoryIds, reason: v.reason }),\n });\n return textResponse(result);\n }\n case \"memory_export\": {\n try {\n const result = await handle.call(\"/agentmemory/export\", { method: \"GET\" });\n return textResponse(result, true);\n } catch (err) {\n if (\n err instanceof ProxyCallError &&\n err.body != null &&\n typeof err.body === \"object\" &&\n (err.body as { oversized?: boolean }).oversized === true\n ) {\n return textResponse(err.body, true);\n }\n throw err;\n }\n }\n case \"memory_audit\": {\n const result = await handle.call(\n `/agentmemory/audit?limit=${v.limit}`,\n { method: \"GET\" },\n );\n return textResponse(result, true);\n }\n default:\n throw new Error(`Unknown tool: ${v.tool}`);\n }\n}\n\nasync function handleLocal(\n v: Validated,\n kvInstance: InMemoryKV,\n): Promise<{ content: Array<{ type: string; text: string }> }> {\n switch (v.tool) {\n case \"memory_save\": {\n const id = generateId(\"mem\");\n const isoNow = new Date().toISOString();\n await kvInstance.set(\"mem:memories\", id, {\n id,\n type: v.type,\n title: (v.content || \"\").slice(0, 80),\n content: v.content,\n concepts: v.concepts,\n files: v.files,\n createdAt: isoNow,\n updatedAt: isoNow,\n strength: 7,\n version: 1,\n isLatest: true,\n sessionIds: [],\n });\n kvInstance.persist();\n return textResponse({ saved: id });\n }\n\n case \"memory_recall\":\n case \"memory_smart_search\": {\n const query = (v.query || \"\").toLowerCase();\n const limit = v.limit ?? DEFAULT_LIMIT;\n const all =\n await kvInstance.list<Record<string, unknown>>(\"mem:memories\");\n const results = all\n .filter((m) => {\n const text = [\n typeof m[\"title\"] === \"string\" ? m[\"title\"] : \"\",\n typeof m[\"content\"] === \"string\" ? m[\"content\"] : \"\",\n Array.isArray(m[\"files\"]) ? m[\"files\"].join(\" \") : \"\",\n Array.isArray(m[\"concepts\"]) ? m[\"concepts\"].join(\" \") : \"\",\n Array.isArray(m[\"sessionIds\"]) ? m[\"sessionIds\"].join(\" \") : \"\",\n typeof m[\"id\"] === \"string\" ? m[\"id\"] : \"\",\n ]\n .join(\" \")\n .toLowerCase();\n return query.split(/\\s+/).every((word) => text.includes(word));\n })\n .slice(0, limit);\n return textResponse({ mode: \"compact\", results }, true);\n }\n\n case \"memory_sessions\": {\n const sessions =\n await kvInstance.list<Record<string, unknown>>(\"mem:sessions\");\n const limit = v.limit ?? 20;\n return textResponse({ sessions: sessions.slice(0, limit) }, true);\n }\n\n case \"memory_governance_delete\": {\n let deleted = 0;\n for (const id of v.memoryIds || []) {\n const existing = await kvInstance.get(\"mem:memories\", id);\n if (existing) {\n await kvInstance.delete(\"mem:memories\", id);\n deleted++;\n }\n }\n kvInstance.persist();\n return textResponse({\n deleted,\n requested: (v.memoryIds || []).length,\n reason: v.reason,\n });\n }\n\n case \"memory_export\": {\n const memories = await kvInstance.list(\"mem:memories\");\n const sessions = await kvInstance.list(\"mem:sessions\");\n return textResponse({ version: VERSION, memories, sessions }, true);\n }\n\n case \"memory_audit\": {\n const result = await queryAudit(kvInstance as never, {\n limit: v.limit ?? 50,\n });\n return textResponse(result, true);\n }\n\n default:\n throw new Error(`Unknown tool: ${v.tool}`);\n }\n}\n\nasync function handleProxyGeneric(\n toolName: string,\n args: Record<string, unknown>,\n handle: ProxyHandle,\n): Promise<{ content: Array<{ type: string; text: string }> }> {\n // Forward to the server's full MCP surface so non-Claude clients can\n // reach all 54 tools (lessons, sentinels, slots, signals, graph, …)\n // instead of being capped at the 7 IMPLEMENTED_TOOLS set baked into\n // this shim. The server validates arguments per tool.\n const result = (await handle.call(\"/agentmemory/mcp/call\", {\n method: \"POST\",\n body: JSON.stringify({ name: toolName, arguments: args }),\n })) as { content?: Array<{ type: string; text: string }> } | null;\n if (result && Array.isArray(result.content)) {\n return { content: result.content };\n }\n return textResponse(result, true);\n}\n\nexport async function handleToolCall(\n toolName: string,\n args: Record<string, unknown>,\n kvInstance: InMemoryKV = kv,\n): Promise<{ content: Array<{ type: string; text: string }> }> {\n const handle = await resolveHandle();\n announceMode(handle);\n\n if (!IMPLEMENTED_TOOLS.has(toolName)) {\n if (handle.mode === \"proxy\") {\n try {\n return await handleProxyGeneric(toolName, args, handle);\n } catch (err) {\n process.stderr.write(\n `[@agentmemory/mcp] proxy call failed for ${toolName}: ${err instanceof Error ? err.message : String(err)}\\n`,\n );\n invalidateHandle();\n throw err;\n }\n }\n throw new Error(\n `Unknown tool: ${toolName} (local fallback supports only ${[...IMPLEMENTED_TOOLS].join(\", \")}; start an agentmemory server and set AGENTMEMORY_URL to use the full tool set)`,\n );\n }\n\n const validated = validate(toolName, args);\n if (handle.mode === \"proxy\") {\n try {\n return await handleProxy(validated, handle);\n } catch (err) {\n process.stderr.write(\n `[@agentmemory/mcp] proxy call failed for ${toolName}: ${err instanceof Error ? err.message : String(err)}; invalidating handle and falling back to local KV\\n`,\n );\n invalidateHandle();\n }\n }\n return handleLocal(validated, kvInstance);\n}\n\nexport async function handleToolsList(): Promise<{ tools: unknown[] }> {\n const debug = process.env[\"AGENTMEMORY_DEBUG\"] === \"1\" || process.env[\"AGENTMEMORY_DEBUG\"] === \"true\";\n const handle = await resolveHandle();\n announceMode(handle);\n if (debug) {\n process.stderr.write(\n `[@agentmemory/mcp] tools/list: handle.mode=${handle.mode}${handle.mode === \"proxy\" ? ` baseUrl=${handle.baseUrl}` : \"\"}\\n`,\n );\n }\n if (handle.mode === \"proxy\") {\n try {\n const remote = (await handle.call(\"/agentmemory/mcp/tools\", {\n method: \"GET\",\n })) as { tools?: unknown } | null;\n if (debug) {\n const shape = remote === null\n ? \"null\"\n : typeof remote !== \"object\"\n ? typeof remote\n : `keys=${Object.keys(remote as object).join(\",\")} toolsType=${Array.isArray((remote as { tools?: unknown }).tools) ? `array(len=${((remote as { tools: unknown[] }).tools).length})` : typeof (remote as { tools?: unknown }).tools}`;\n process.stderr.write(\n `[@agentmemory/mcp] tools/list: remote response shape: ${shape}\\n`,\n );\n }\n if (remote && Array.isArray(remote.tools)) {\n if (debug) {\n process.stderr.write(\n `[@agentmemory/mcp] tools/list: returning ${remote.tools.length} tools from server\\n`,\n );\n }\n return { tools: remote.tools };\n }\n process.stderr.write(\n `[@agentmemory/mcp] tools/list: server returned unexpected shape (no .tools array); falling back to local IMPLEMENTED_TOOLS list. Set AGENTMEMORY_DEBUG=1 to inspect response.\\n`,\n );\n } catch (err) {\n process.stderr.write(\n `[@agentmemory/mcp] tools/list proxy failed: ${err instanceof Error ? err.message : String(err)}; falling back to local list\\n`,\n );\n invalidateHandle();\n }\n }\n const fallback = getAllTools().filter((t) => IMPLEMENTED_TOOLS.has(t.name));\n if (debug) {\n process.stderr.write(\n `[@agentmemory/mcp] tools/list: returning ${fallback.length} local fallback tools (${fallback.map((t) => t.name).join(\",\")})\\n`,\n );\n }\n return { tools: fallback };\n}\n\nconst transport = createStdioTransport(async (method, params) => {\n switch (method) {\n case \"initialize\": {\n const requested = (params as { protocolVersion?: unknown } | undefined)\n ?.protocolVersion;\n const protocolVersion =\n typeof requested === \"string\" &&\n SUPPORTED_PROTOCOL_VERSIONS.includes(requested)\n ? requested\n : SUPPORTED_PROTOCOL_VERSIONS[0];\n return {\n protocolVersion,\n capabilities: { tools: { listChanged: false } },\n serverInfo: {\n name: SERVER_INFO.name,\n version: SERVER_INFO.version,\n },\n };\n }\n\n case \"notifications/initialized\":\n return {};\n\n case \"tools/list\":\n return handleToolsList();\n\n case \"tools/call\": {\n const toolName = params.name as string;\n const toolArgs = (params.arguments as Record<string, unknown>) || {};\n try {\n return await handleToolCall(toolName, toolArgs);\n } catch (err) {\n return {\n content: [\n {\n type: \"text\",\n text: `Error: ${err instanceof Error ? err.message : String(err)}`,\n },\n ],\n isError: true,\n };\n }\n }\n\n default:\n throw new Error(`Unknown method: ${method}`);\n }\n});\n\nprocess.stderr.write(\n `[@agentmemory/mcp] Standalone MCP server v${SERVER_INFO.version} starting...\\n`,\n);\ntransport.start();\n\nprocess.on(\"SIGINT\", () => {\n kv.persist();\n process.exit(0);\n});\nprocess.on(\"SIGTERM\", () => {\n kv.persist();\n process.exit(0);\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"standalone.mjs","names":[],"sources":["../src/mcp/in-memory-kv.ts","../src/mcp/transport.ts","../src/mcp/tools-registry.ts","../src/config.ts","../src/state/search-layer.ts","../src/state/memory-utils.ts","../src/functions/recall-response.ts","../src/mcp/search-arguments.ts","../src/state/list-query.ts","../src/mcp/session-arguments.ts","../src/version.ts","../src/state/cjk-segmenter.ts","../src/state/schema.ts","../src/logger.ts","../src/state/keyed-mutex.ts","../src/functions/audit.ts","../src/secret-store.ts","../src/mcp/rest-proxy.ts","../src/mcp/standalone.ts"],"sourcesContent":["import { existsSync, readFileSync, writeFileSync, mkdirSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\n\nexport class InMemoryKV {\n private store = new Map<string, Map<string, unknown>>();\n\n constructor(private persistPath?: string) {\n if (persistPath && existsSync(persistPath)) {\n try {\n const data = JSON.parse(readFileSync(persistPath, \"utf-8\"));\n for (const [scope, entries] of Object.entries(data)) {\n const map = new Map<string, unknown>();\n for (const [key, value] of Object.entries(\n entries as Record<string, unknown>,\n )) {\n map.set(key, value);\n }\n this.store.set(scope, map);\n }\n } catch {\n // start fresh\n }\n }\n }\n\n async get<T = unknown>(scope: string, key: string): Promise<T | null> {\n return (this.store.get(scope)?.get(key) as T) ?? null;\n }\n\n async set<T = unknown>(scope: string, key: string, data: T): Promise<T> {\n if (!this.store.has(scope)) this.store.set(scope, new Map());\n this.store.get(scope)!.set(key, data);\n return data;\n }\n\n async delete(scope: string, key: string): Promise<void> {\n this.store.get(scope)?.delete(key);\n }\n\n async list<T = unknown>(scope: string): Promise<T[]> {\n const entries = this.store.get(scope);\n return entries ? (Array.from(entries.values()) as T[]) : [];\n }\n\n persist(): boolean {\n if (!this.persistPath) return false;\n try {\n const dir = dirname(this.persistPath);\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true });\n const data: Record<string, Record<string, unknown>> = {};\n for (const [scope, entries] of this.store) {\n data[scope] = Object.fromEntries(entries);\n }\n writeFileSync(this.persistPath, JSON.stringify(data), \"utf-8\");\n return true;\n } catch (err) {\n process.stderr.write(\n `[@agentmemory/mcp] Persist failed: ${err instanceof Error ? err.message : String(err)}\\n`,\n );\n return false;\n }\n }\n}\n","export interface JsonRpcRequest {\n jsonrpc: \"2.0\";\n id?: string | number;\n method: string;\n params?: Record<string, unknown>;\n}\n\nexport interface JsonRpcResponse {\n jsonrpc: \"2.0\";\n id: string | number | null;\n result?: unknown;\n error?: { code: number; message: string; data?: unknown };\n}\n\nexport type RequestHandler = (\n method: string,\n params: Record<string, unknown>,\n) => Promise<unknown>;\n\nlet activeStdioTransports = 0;\n\nexport class JsonRpcError extends Error {\n constructor(public readonly code: number, message: string) {\n super(message);\n this.name = \"JsonRpcError\";\n }\n}\n\nexport interface StdioMessageParser {\n push: (chunk: Buffer | string) => void;\n isFramed: () => boolean;\n}\n\n// JSON-RPC 2.0 notifications are messages without an `id` field. The spec\n// (and the MCP transport contract) requires the server to NOT send a\n// response for notifications. Some clients tolerate spurious responses;\n// stricter clients (e.g. Codex CLI) treat them as protocol violations and\n// close the transport. See agentmemory#129.\nfunction isNotification(req: JsonRpcRequest): boolean {\n return req.id === undefined || req.id === null;\n}\n\n// Per JSON-RPC 2.0 §4, a valid request id must be a String, Number, or Null\n// (Null is technically only allowed in responses; in requests, omitting id\n// is the convention for notifications, which we treat the same as null).\n// Any other runtime type (object, array, boolean) is an Invalid Request.\nfunction isValidId(id: unknown): id is string | number | null | undefined {\n return (\n id === undefined ||\n id === null ||\n typeof id === \"string\" ||\n typeof id === \"number\"\n );\n}\n\n// Exported for unit tests so the line-handling logic is exercised\n// independently of process.stdin / process.stdout.\nexport async function processLine(\n line: string,\n handler: RequestHandler,\n writeOut: (response: JsonRpcResponse) => void,\n writeErr: (msg: string) => void = (msg) => process.stderr.write(msg),\n): Promise<void> {\n const trimmed = line.trim();\n if (!trimmed) return;\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(trimmed);\n } catch {\n writeOut({\n jsonrpc: \"2.0\",\n id: null,\n error: { code: -32700, message: \"Parse error\" },\n });\n return;\n }\n\n const request = parsed as JsonRpcRequest;\n const rawId = (request as { id?: unknown } | null)?.id;\n\n // Invalid request shape (missing/wrong jsonrpc, non-string method).\n if (\n !request ||\n typeof request !== \"object\" ||\n request.jsonrpc !== \"2.0\" ||\n typeof request.method !== \"string\"\n ) {\n // Echo the id back only if it's a valid string/number. Notifications\n // (missing/null id) and malformed ids both drop silently — we don't\n // want to respond to something that could be a notification, and we\n // can't invent an id for a malformed one.\n if (typeof rawId === \"string\" || typeof rawId === \"number\") {\n writeOut({\n jsonrpc: \"2.0\",\n id: rawId,\n error: { code: -32600, message: \"Invalid Request\" },\n });\n }\n return;\n }\n\n // Request shape is valid but id may still be of the wrong type\n // (object, array, boolean). Per the spec, that's an Invalid Request.\n // Respond with id: null because we can't safely echo a non-JSON-RPC id.\n if (!isValidId(rawId)) {\n writeOut({\n jsonrpc: \"2.0\",\n id: null,\n error: { code: -32600, message: \"Invalid Request: id must be string, number, or null\" },\n });\n return;\n }\n\n const notification = isNotification(request);\n\n try {\n const result = await handler(request.method, request.params || {});\n if (notification) return;\n writeOut({\n jsonrpc: \"2.0\",\n id: request.id as string | number,\n result,\n });\n } catch (err) {\n if (notification) {\n writeErr(\n `[mcp-transport] notification handler error for ${request.method}: ${\n err instanceof Error ? err.message : String(err)\n }\\n`,\n );\n return;\n }\n writeOut({\n jsonrpc: \"2.0\",\n id: request.id as string | number,\n error: {\n code: err instanceof JsonRpcError ? err.code : -32603,\n message: err instanceof Error ? err.message : String(err),\n },\n });\n }\n}\n\nfunction findHeaderEnd(buffer: Buffer): { headerEnd: number; bodyStart: number } | null {\n const crlf = buffer.indexOf(\"\\r\\n\\r\\n\");\n const lf = buffer.indexOf(\"\\n\\n\");\n if (crlf === -1 && lf === -1) return null;\n if (crlf !== -1 && (lf === -1 || crlf <= lf)) {\n return { headerEnd: crlf, bodyStart: crlf + 4 };\n }\n return { headerEnd: lf, bodyStart: lf + 2 };\n}\n\nfunction parseContentLength(header: string): number | null {\n for (const line of header.split(/\\r?\\n/)) {\n const match = line.match(/^content-length:\\s*(\\d+)\\s*$/i);\n if (match) return Number(match[1]);\n }\n return null;\n}\n\nexport function formatResponse(\n response: JsonRpcResponse,\n framed: boolean,\n): string | Buffer[] {\n const body = JSON.stringify(response);\n if (!framed) return `${body}\\n`;\n const bytes = Buffer.from(body, \"utf8\");\n return [Buffer.from(`Content-Length: ${bytes.length}\\r\\n\\r\\n`, \"ascii\"), bytes];\n}\n\nexport function createMessageParser(\n onMessage: (message: string) => void,\n writeErr: (msg: string) => void = (msg) => process.stderr.write(msg),\n): StdioMessageParser {\n let buffer = Buffer.alloc(0);\n let framed = false;\n\n function processBuffer(): void {\n while (buffer.length > 0) {\n if (buffer[0] === 10 || buffer[0] === 13) {\n buffer = buffer.subarray(1);\n continue;\n }\n\n const preview = buffer.toString(\"ascii\", 0, Math.min(buffer.length, 32));\n if (/^content-length:/i.test(preview)) {\n const header = findHeaderEnd(buffer);\n if (!header) return;\n\n const headerText = buffer.subarray(0, header.headerEnd).toString(\"ascii\");\n const contentLength = parseContentLength(headerText);\n if (contentLength === null) {\n writeErr(\"[mcp-transport] missing Content-Length header\\n\");\n buffer = buffer.subarray(header.bodyStart);\n continue;\n }\n\n const messageEnd = header.bodyStart + contentLength;\n if (buffer.length < messageEnd) return;\n\n framed = true;\n const message = buffer.subarray(header.bodyStart, messageEnd).toString(\"utf8\");\n buffer = buffer.subarray(messageEnd);\n onMessage(message);\n continue;\n }\n\n const newline = buffer.indexOf(10);\n if (newline === -1) return;\n const line = buffer\n .subarray(0, newline)\n .toString(\"utf8\")\n .replace(/\\r$/, \"\");\n buffer = buffer.subarray(newline + 1);\n onMessage(line);\n }\n }\n\n return {\n push(chunk) {\n const bytes = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk, \"utf8\");\n buffer = Buffer.concat([buffer, bytes]);\n processBuffer();\n },\n isFramed() {\n return framed;\n },\n };\n}\n\nexport function createStdioTransport(handler: RequestHandler): {\n start: () => void;\n stop: () => void;\n} {\n let parser: StdioMessageParser | null = null;\n let queue = Promise.resolve();\n let active = false;\n\n const writeResponse = (response: JsonRpcResponse) => {\n const formatted = formatResponse(response, parser?.isFramed() ?? false);\n if (typeof formatted === \"string\") {\n process.stdout.write(formatted);\n return;\n }\n for (const chunk of formatted) {\n process.stdout.write(chunk);\n }\n };\n\n const onData = (chunk: Buffer) => parser?.push(chunk);\n\n return {\n start() {\n if (active) return;\n active = true;\n activeStdioTransports += 1;\n process.env[\"AGENTMEMORY_MCP_STDIO__\"] = \"1\";\n parser = createMessageParser((message) => {\n queue = queue.then(() => processLine(message, handler, writeResponse));\n void queue.catch((err) => {\n process.stderr.write(\n `[mcp-transport] request processing failed: ${\n err instanceof Error ? err.message : String(err)\n }\\n`,\n );\n });\n });\n process.stdin.on(\"data\", onData);\n },\n stop() {\n process.stdin.off(\"data\", onData);\n if (active) {\n active = false;\n activeStdioTransports -= 1;\n if (activeStdioTransports === 0) {\n delete process.env[\"AGENTMEMORY_MCP_STDIO__\"];\n }\n }\n parser = null;\n },\n };\n}\n","export type McpToolDef = {\n name: string;\n description: string;\n inputSchema: {\n type: \"object\";\n properties: Record<string, { type: string; description: string; enum?: string[] }>;\n required?: string[];\n };\n};\n\nexport const CORE_TOOLS: McpToolDef[] = [\n {\n name: \"memory_recall\",\n description:\n \"Search stored memories and session observations for relevant context. Use when you need to recall what happened in previous sessions, find past decisions, or look up how a file was modified before.\",\n inputSchema: {\n type: \"object\",\n properties: {\n query: {\n type: \"string\",\n description: \"Search query (keywords, file names, concepts)\",\n },\n limit: {\n type: \"number\",\n description: \"Max results to return (default 10)\",\n },\n format: {\n type: \"string\",\n description: \"Result format: full, compact, or narrative (default full)\",\n },\n token_budget: {\n type: \"number\",\n description: \"Optional estimated content token budget; response includes truncation and recovery metadata\",\n },\n targetLayer: {\n type: \"string\",\n enum: [\"all\", \"memory\", \"observation\"],\n description: \"Search all indexed layers (default), durable memories, or raw observations before ranking\",\n },\n project: { type: \"string\", description: \"Filter by project path\" },\n cwd: { type: \"string\", description: \"Filter by session working directory\" },\n agentId: { type: \"string\", description: \"Filter by owning agent; '*' reads across agents\" },\n },\n required: [\"query\"],\n },\n },\n {\n name: \"memory_compress_file\",\n description:\n \"Compress a markdown file to reduce token usage while preserving headings, URLs, and code blocks. Creates a .original.md backup before writing.\",\n inputSchema: {\n type: \"object\",\n properties: {\n filePath: {\n type: \"string\",\n description: \"Path to the markdown file to compress\",\n },\n },\n required: [\"filePath\"],\n },\n },\n {\n name: \"memory_save\",\n description:\n \"Explicitly save an important insight, decision, or pattern to long-term memory.\",\n inputSchema: {\n type: \"object\",\n properties: {\n content: {\n type: \"string\",\n description: \"The insight or decision to remember\",\n },\n type: {\n type: \"string\",\n description:\n \"Memory type: pattern, preference, architecture, bug, workflow, or fact\",\n },\n concepts: {\n type: \"string\",\n description: \"Comma-separated key concepts\",\n },\n files: {\n type: \"string\",\n description: \"Comma-separated relevant file paths\",\n },\n project: {\n type: \"string\",\n description:\n \"Stable canonical project identifier this memory belongs to (e.g. a slug, \" +\n \"UUID, or registry key). Must match the value used when the session was \" +\n \"started. Do not use filesystem paths or ad-hoc display names — those \" +\n \"change across machines and will silently break project scoping.\",\n },\n agentId: {\n type: \"string\",\n description:\n \"Agent identity to scope this memory to. When set, agent-scoped recall \" +\n \"and search only surface it for the same agentId. Omit for shared memory.\",\n },\n },\n required: [\"content\"],\n },\n },\n {\n name: \"memory_file_history\",\n description: \"Get past observations about specific files.\",\n inputSchema: {\n type: \"object\",\n properties: {\n files: { type: \"string\", description: \"Comma-separated file paths\" },\n sessionId: {\n type: \"string\",\n description: \"Current session ID to exclude\",\n },\n },\n required: [\"files\"],\n },\n },\n {\n name: \"memory_patterns\",\n description: \"Detect recurring patterns across sessions.\",\n inputSchema: {\n type: \"object\",\n properties: {\n project: { type: \"string\", description: \"Project path to analyze\" },\n limit: {\n type: \"integer\",\n description:\n \"Most recent sessions to scan (default 50, max 500)\",\n },\n },\n },\n },\n {\n name: \"memory_sessions\",\n description:\n \"List recent sessions with status, observation counts, summaries and a continuation cursor. Filters apply before pagination; retain them when requesting the next page.\",\n inputSchema: {\n type: \"object\",\n properties: {\n project: { type: \"string\", description: \"Exact project identifier used when sessions were started\" },\n agentId: { type: \"string\", description: \"Filter by owning agent; '*' reads across agents\" },\n limit: { type: \"integer\", description: \"Page size (default 20, max 100)\" },\n cursor: { type: \"string\", description: \"nextCursor from the previous page; omit for the first page\" },\n },\n },\n },\n {\n name: \"memory_smart_search\",\n description: \"Hybrid semantic+keyword search with progressive disclosure. Pass query to search, or expandIds to recover full results.\",\n inputSchema: {\n type: \"object\",\n properties: {\n query: { type: \"string\", description: \"Search query\" },\n expandIds: {\n type: \"string\",\n description: \"Comma-separated memory or observation IDs to expand; query is optional when provided\",\n },\n limit: { type: \"number\", description: \"Max results (default 10)\" },\n targetLayer: {\n type: \"string\",\n enum: [\"all\", \"memory\", \"observation\"],\n description: \"Search all indexed layers (default), durable memories, or raw observations before ranking\",\n },\n project: { type: \"string\", description: \"Filter by project path\" },\n agentId: { type: \"string\", description: \"Filter by owning agent; '*' reads across agents\" },\n includeLessons: { type: \"boolean\", description: \"Include lesson matches when targetLayer is all (default true)\" },\n },\n },\n },\n {\n name: \"memory_vision_search\",\n description:\n \"Cross-modal image search via CLIP embeddings. Pass queryText to find screenshots matching a description, or queryImageBase64/queryImageRef to find similar images. Requires AGENTMEMORY_IMAGE_EMBEDDINGS=true.\",\n inputSchema: {\n type: \"object\",\n properties: {\n queryText: { type: \"string\", description: \"Text query (e.g. 'login form with error banner')\" },\n queryImageRef: { type: \"string\", description: \"Absolute path to a stored image to match against\" },\n queryImageBase64: { type: \"string\", description: \"Raw base64 image bytes or data URL\" },\n topK: { type: \"number\", description: \"Max results (default 10, max 50)\" },\n sessionId: { type: \"string\", description: \"Filter to a single session\" },\n },\n },\n },\n {\n name: \"memory_timeline\",\n description: \"Chronological observations around an anchor point.\",\n inputSchema: {\n type: \"object\",\n properties: {\n anchor: {\n type: \"string\",\n description: \"Anchor point: ISO date or keyword\",\n },\n project: { type: \"string\", description: \"Filter by project path\" },\n before: {\n type: \"number\",\n description: \"Observations before anchor (default 5)\",\n },\n after: {\n type: \"number\",\n description: \"Observations after anchor (default 5)\",\n },\n },\n required: [\"anchor\"],\n },\n },\n {\n name: \"memory_profile\",\n description: \"User/project profile with top concepts and file patterns.\",\n inputSchema: {\n type: \"object\",\n properties: {\n project: { type: \"string\", description: \"Project path\" },\n refresh: {\n type: \"string\",\n description: \"Set to 'true' to force rebuild\",\n },\n },\n required: [\"project\"],\n },\n },\n {\n name: \"memory_export\",\n description:\n \"Export memory data as JSON. Past the transport size limit the export is refused, so use the paging arguments on a large store.\",\n inputSchema: {\n type: \"object\",\n properties: {\n maxSessions: {\n type: \"number\",\n description: \"Sessions per page (with their observations)\",\n },\n offset: { type: \"number\", description: \"Session offset\" },\n collectionLimit: {\n type: \"number\",\n description:\n \"Rows per page for memories, summaries, graphNodes, lessons and the other top-level collections; access logs follow the memories on the page. Sessions and their observations are paged only by maxSessions and offset, so pass collections to leave them out of each page\",\n },\n collectionOffset: {\n type: \"number\",\n description: \"Row offset for the top-level collections\",\n },\n collections: {\n type: \"string\",\n description:\n \"Comma-separated collections to return, e.g. memories,lessons. Sessions and their observations come back only when the list names sessions, access logs only together with memories. Unknown names are ignored. Omit for everything. A paged or selected export cannot be imported with strategy replace.\",\n },\n },\n },\n },\n {\n name: \"memory_relations\",\n description: \"Query the memory relationship graph.\",\n inputSchema: {\n type: \"object\",\n properties: {\n memoryId: {\n type: \"string\",\n description: \"Memory ID to find relations for\",\n },\n maxHops: {\n type: \"number\",\n description: \"Max traversal depth (default 2)\",\n },\n minConfidence: {\n type: \"number\",\n description: \"Min confidence (0-1, default 0)\",\n },\n },\n required: [\"memoryId\"],\n },\n },\n {\n name: \"memory_commit_lookup\",\n description:\n \"Look up the agent session(s) that produced a specific git commit, given its SHA. Returns the commit metadata and linked sessions.\",\n inputSchema: {\n type: \"object\",\n properties: {\n sha: { type: \"string\", description: \"Full git commit SHA\" },\n },\n required: [\"sha\"],\n },\n },\n {\n name: \"memory_commits\",\n description:\n \"List recent commits linked to agent sessions, optionally filtered by branch or repo.\",\n inputSchema: {\n type: \"object\",\n properties: {\n branch: { type: \"string\", description: \"Filter by branch name\" },\n repo: { type: \"string\", description: \"Filter by remote URL\" },\n limit: { type: \"number\", description: \"Max results (default 100, max 500)\" },\n },\n },\n },\n];\n\nexport const V040_TOOLS: McpToolDef[] = [\n {\n name: \"memory_claude_bridge_sync\",\n description:\n \"Sync memory state to/from Claude Code's native MEMORY.md file.\",\n inputSchema: {\n type: \"object\",\n properties: {\n direction: {\n type: \"string\",\n description:\n \"'read' to import from MEMORY.md, 'write' to export to MEMORY.md\",\n },\n },\n required: [\"direction\"],\n },\n },\n {\n name: \"memory_graph_query\",\n description: \"Query the knowledge graph for entities and relationships.\",\n inputSchema: {\n type: \"object\",\n properties: {\n startNodeId: {\n type: \"string\",\n description: \"Starting node ID for traversal\",\n },\n nodeType: { type: \"string\", description: \"Filter by node type\" },\n maxDepth: {\n type: \"number\",\n description: \"Max BFS depth (default 3, max 5)\",\n },\n query: { type: \"string\", description: \"Search nodes by name\" },\n },\n },\n },\n {\n name: \"memory_consolidate\",\n description:\n \"Run the 4-tier memory consolidation pipeline (working -> episodic -> semantic -> procedural).\",\n inputSchema: {\n type: \"object\",\n properties: {\n tier: {\n type: \"string\",\n description: \"Target tier: episodic, semantic, or procedural\",\n },\n },\n },\n },\n {\n name: \"memory_team_share\",\n description: \"Share a memory or observation with team members.\",\n inputSchema: {\n type: \"object\",\n properties: {\n itemId: {\n type: \"string\",\n description: \"ID of memory or observation to share\",\n },\n itemType: {\n type: \"string\",\n description: \"Type: observation, memory, or pattern\",\n },\n },\n required: [\"itemId\", \"itemType\"],\n },\n },\n {\n name: \"memory_team_feed\",\n description: \"Get recent shared items from all team members.\",\n inputSchema: {\n type: \"object\",\n properties: {\n limit: { type: \"number\", description: \"Max items (default 20)\" },\n },\n },\n },\n {\n name: \"memory_audit\",\n description: \"View the audit trail of memory operations.\",\n inputSchema: {\n type: \"object\",\n properties: {\n operation: { type: \"string\", description: \"Filter by operation type\" },\n limit: { type: \"number\", description: \"Max entries (default 50)\" },\n },\n },\n },\n {\n name: \"memory_governance_delete\",\n description: \"Delete specific memories with audit trail.\",\n inputSchema: {\n type: \"object\",\n properties: {\n memoryIds: {\n type: \"string\",\n description: \"Comma-separated memory IDs to delete\",\n },\n reason: { type: \"string\", description: \"Reason for deletion\" },\n },\n required: [\"memoryIds\"],\n },\n },\n {\n name: \"memory_snapshot_create\",\n description: \"Create a git-versioned snapshot of current memory state.\",\n inputSchema: {\n type: \"object\",\n properties: {\n message: { type: \"string\", description: \"Snapshot description\" },\n },\n },\n },\n];\n\nexport const V050_TOOLS: McpToolDef[] = [\n {\n name: \"memory_action_create\",\n description:\n \"Create an actionable work item with typed dependencies. Actions track what agents need to do and how work items relate to each other.\",\n inputSchema: {\n type: \"object\",\n properties: {\n title: { type: \"string\", description: \"Action title\" },\n description: {\n type: \"string\",\n description: \"Detailed description of the work\",\n },\n priority: {\n type: \"number\",\n description: \"Priority 1-10 (10 highest)\",\n },\n project: { type: \"string\", description: \"Project path\" },\n tags: {\n type: \"string\",\n description: \"Comma-separated tags\",\n },\n parentId: {\n type: \"string\",\n description: \"Parent action ID for hierarchical actions\",\n },\n requires: {\n type: \"string\",\n description:\n \"Comma-separated action IDs that must complete before this\",\n },\n },\n required: [\"title\"],\n },\n },\n {\n name: \"memory_action_update\",\n description:\n \"Update an action's status, priority, or details. Set status to 'done' to complete it and unblock dependent actions.\",\n inputSchema: {\n type: \"object\",\n properties: {\n actionId: { type: \"string\", description: \"Action ID to update\" },\n status: {\n type: \"string\",\n description: \"New status: pending, active, done, blocked, cancelled\",\n },\n result: {\n type: \"string\",\n description: \"Outcome description (when completing)\",\n },\n priority: { type: \"number\", description: \"New priority 1-10\" },\n },\n required: [\"actionId\"],\n },\n },\n {\n name: \"memory_frontier\",\n description:\n \"Get all unblocked actions ranked by priority and urgency. Returns the frontier of actionable work with no unsatisfied dependencies.\",\n inputSchema: {\n type: \"object\",\n properties: {\n project: { type: \"string\", description: \"Filter by project\" },\n agentId: {\n type: \"string\",\n description: \"Agent ID to check lease conflicts\",\n },\n limit: { type: \"number\", description: \"Max results (default 20)\" },\n },\n },\n },\n {\n name: \"memory_next\",\n description:\n \"Get the single most important next action to work on. Combines dependency resolution, priority, and recency into a score.\",\n inputSchema: {\n type: \"object\",\n properties: {\n project: { type: \"string\", description: \"Filter by project\" },\n agentId: { type: \"string\", description: \"Current agent ID\" },\n },\n },\n },\n {\n name: \"memory_lease\",\n description:\n \"Acquire, release, or renew an exclusive lease on an action. Prevents multiple agents from working on the same thing.\",\n inputSchema: {\n type: \"object\",\n properties: {\n actionId: { type: \"string\", description: \"Action ID\" },\n agentId: { type: \"string\", description: \"Agent claiming the action\" },\n operation: {\n type: \"string\",\n description: \"acquire, release, or renew\",\n },\n result: {\n type: \"string\",\n description: \"Result when releasing (marks action done)\",\n },\n ttlMs: {\n type: \"number\",\n description: \"Lease duration in ms (default 10min, max 1hr)\",\n },\n },\n required: [\"actionId\", \"agentId\", \"operation\"],\n },\n },\n {\n name: \"memory_routine_run\",\n description:\n \"Instantiate a frozen workflow routine, creating actions for each step with proper dependencies.\",\n inputSchema: {\n type: \"object\",\n properties: {\n routineId: { type: \"string\", description: \"Routine template ID\" },\n project: { type: \"string\", description: \"Project context\" },\n initiatedBy: { type: \"string\", description: \"Agent starting the run\" },\n },\n required: [\"routineId\"],\n },\n },\n {\n name: \"memory_signal_send\",\n description:\n \"Send a message to another agent or broadcast. Supports threading, typed messages, and TTL expiration.\",\n inputSchema: {\n type: \"object\",\n properties: {\n from: { type: \"string\", description: \"Sender agent ID\" },\n to: {\n type: \"string\",\n description: \"Recipient agent ID (omit for broadcast)\",\n },\n content: { type: \"string\", description: \"Message content\" },\n type: {\n type: \"string\",\n description: \"Message type: info, request, response, alert, handoff\",\n },\n replyTo: {\n type: \"string\",\n description: \"Signal ID to reply to (auto-threads)\",\n },\n },\n required: [\"from\", \"content\"],\n },\n },\n {\n name: \"memory_signal_read\",\n description:\n \"Read messages for an agent. Marks delivered messages as read.\",\n inputSchema: {\n type: \"object\",\n properties: {\n agentId: { type: \"string\", description: \"Agent to read messages for\" },\n unreadOnly: {\n type: \"string\",\n description: \"Set to 'true' for unread only\",\n },\n threadId: {\n type: \"string\",\n description: \"Filter by conversation thread\",\n },\n limit: { type: \"number\", description: \"Max messages (default 50)\" },\n },\n required: [\"agentId\"],\n },\n },\n {\n name: \"memory_checkpoint\",\n description:\n \"Create or resolve an external checkpoint (CI result, approval, deploy status) that gates action progress.\",\n inputSchema: {\n type: \"object\",\n properties: {\n operation: {\n type: \"string\",\n description: \"create, resolve, or list\",\n },\n name: { type: \"string\", description: \"Checkpoint name (for create)\" },\n checkpointId: {\n type: \"string\",\n description: \"Checkpoint ID (for resolve)\",\n },\n status: {\n type: \"string\",\n description: \"passed or failed (for resolve)\",\n },\n type: {\n type: \"string\",\n description: \"Checkpoint type: ci, approval, deploy, external, timer\",\n },\n linkedActionIds: {\n type: \"string\",\n description:\n \"Comma-separated action IDs this checkpoint gates (for create)\",\n },\n },\n required: [\"operation\"],\n },\n },\n {\n name: \"memory_mesh_sync\",\n description:\n \"Sync memories and actions with peer agentmemory instances for multi-agent collaboration.\",\n inputSchema: {\n type: \"object\",\n properties: {\n peerId: {\n type: \"string\",\n description: \"Specific peer ID (omit for all)\",\n },\n direction: {\n type: \"string\",\n description: \"push, pull, or both (default both)\",\n },\n },\n },\n },\n];\n\nexport const V051_TOOLS: McpToolDef[] = [\n {\n name: \"memory_sentinel_create\",\n description:\n \"Create an event-driven sentinel that watches for conditions (webhook, timer, threshold, pattern, approval) and auto-unblocks gated actions when triggered.\",\n inputSchema: {\n type: \"object\",\n properties: {\n name: { type: \"string\", description: \"Sentinel name\" },\n type: {\n type: \"string\",\n description: \"Type: webhook, timer, threshold, pattern, approval, custom\",\n },\n config: {\n type: \"string\",\n description: \"JSON config (timer: {durationMs}, threshold: {metric,operator,value}, pattern: {pattern}, webhook: {path})\",\n },\n linkedActionIds: {\n type: \"string\",\n description: \"Comma-separated action IDs to gate\",\n },\n expiresInMs: { type: \"number\", description: \"Auto-expire after ms\" },\n },\n required: [\"name\", \"type\"],\n },\n },\n {\n name: \"memory_sentinel_trigger\",\n description:\n \"Externally fire a sentinel, providing an optional result payload. Unblocks any gated actions.\",\n inputSchema: {\n type: \"object\",\n properties: {\n sentinelId: { type: \"string\", description: \"Sentinel ID to trigger\" },\n result: { type: \"string\", description: \"JSON result payload\" },\n },\n required: [\"sentinelId\"],\n },\n },\n {\n name: \"memory_sketch_create\",\n description:\n \"Create an ephemeral action graph for exploratory work. Auto-expires after TTL. Can be promoted to permanent actions or discarded.\",\n inputSchema: {\n type: \"object\",\n properties: {\n title: { type: \"string\", description: \"Sketch title\" },\n description: { type: \"string\", description: \"What this sketch explores\" },\n expiresInMs: { type: \"number\", description: \"TTL in ms (default 1 hour)\" },\n project: { type: \"string\", description: \"Project context\" },\n },\n required: [\"title\"],\n },\n },\n {\n name: \"memory_sketch_promote\",\n description:\n \"Promote a sketch's ephemeral actions to permanent actions. Makes the exploratory work official.\",\n inputSchema: {\n type: \"object\",\n properties: {\n sketchId: { type: \"string\", description: \"Sketch ID to promote\" },\n project: { type: \"string\", description: \"Override project for promoted actions\" },\n },\n required: [\"sketchId\"],\n },\n },\n {\n name: \"memory_crystallize\",\n description:\n \"Compress completed action chains into compact crystal digests using LLM summarization. Extracts narrative, key outcomes, files affected, and lessons.\",\n inputSchema: {\n type: \"object\",\n properties: {\n actionIds: {\n type: \"string\",\n description: \"Comma-separated completed action IDs to crystallize\",\n },\n project: { type: \"string\", description: \"Project context\" },\n sessionId: { type: \"string\", description: \"Session context\" },\n },\n required: [\"actionIds\"],\n },\n },\n {\n name: \"memory_diagnose\",\n description:\n \"Run health checks across all subsystems (actions, leases, sentinels, sketches, signals, sessions, memories, mesh). Identifies stuck, orphaned, and inconsistent state.\",\n inputSchema: {\n type: \"object\",\n properties: {\n categories: {\n type: \"string\",\n description: \"Comma-separated categories to check (default all)\",\n },\n },\n },\n },\n {\n name: \"memory_heal\",\n description:\n \"Auto-fix all fixable issues found by diagnostics. Unblocks stuck actions, expires stale leases, cleans up orphaned data.\",\n inputSchema: {\n type: \"object\",\n properties: {\n categories: {\n type: \"string\",\n description: \"Comma-separated categories to heal (default all)\",\n },\n dryRun: {\n type: \"string\",\n description: \"Set to 'true' for dry run (report but don't fix)\",\n },\n },\n },\n },\n {\n name: \"memory_facet_tag\",\n description:\n \"Attach a structured tag (dimension:value) to an action, memory, or observation for multi-dimensional categorization.\",\n inputSchema: {\n type: \"object\",\n properties: {\n targetId: { type: \"string\", description: \"ID of the target to tag\" },\n targetType: {\n type: \"string\",\n description: \"Type: action, memory, or observation\",\n },\n dimension: { type: \"string\", description: \"Tag dimension (e.g., priority, team, status)\" },\n value: { type: \"string\", description: \"Tag value (e.g., urgent, backend, reviewed)\" },\n },\n required: [\"targetId\", \"targetType\", \"dimension\", \"value\"],\n },\n },\n {\n name: \"memory_facet_query\",\n description:\n \"Query targets by facet tags with AND/OR logic. Find all actions tagged priority:urgent AND team:backend.\",\n inputSchema: {\n type: \"object\",\n properties: {\n matchAll: {\n type: \"string\",\n description: \"Comma-separated dimension:value pairs (AND logic)\",\n },\n matchAny: {\n type: \"string\",\n description: \"Comma-separated dimension:value pairs (OR logic)\",\n },\n targetType: {\n type: \"string\",\n description: \"Filter by type: action, memory, or observation\",\n },\n },\n },\n },\n];\n\nexport const V061_TOOLS: McpToolDef[] = [\n {\n name: \"memory_verify\",\n description:\n \"Verify a memory or observation by tracing its citation chain back to source observations and session context. Returns provenance information including confidence scores.\",\n inputSchema: {\n type: \"object\",\n properties: {\n id: {\n type: \"string\",\n description: \"Memory ID or observation ID to verify\",\n },\n },\n required: [\"id\"],\n },\n },\n];\n\nexport const V070_TOOLS: McpToolDef[] = [\n {\n name: \"memory_lesson_save\",\n description:\n \"Save a lesson learned from this session. Lessons have confidence scores that strengthen when reinforced and decay when not used. Duplicate content auto-strengthens the existing lesson.\",\n inputSchema: {\n type: \"object\",\n properties: {\n content: {\n type: \"string\",\n description: \"The lesson learned (what worked, what to avoid, when to use X approach)\",\n },\n context: {\n type: \"string\",\n description: \"When/where this lesson applies\",\n },\n confidence: {\n type: \"number\",\n description: \"Initial confidence 0.0-1.0 (default 0.5)\",\n },\n project: { type: \"string\", description: \"Project this lesson is about\" },\n tags: { type: \"string\", description: \"Comma-separated tags\" },\n },\n required: [\"content\"],\n },\n },\n {\n name: \"memory_lesson_recall\",\n description:\n \"Search lessons by query. Returns lessons sorted by confidence and recency. Use to check what the agent has learned before making decisions.\",\n inputSchema: {\n type: \"object\",\n properties: {\n query: { type: \"string\", description: \"Search query\" },\n project: { type: \"string\", description: \"Filter by project\" },\n minConfidence: {\n type: \"number\",\n description: \"Minimum confidence threshold (default 0.1)\",\n },\n limit: { type: \"number\", description: \"Max results (default 10)\" },\n },\n required: [\"query\"],\n },\n },\n {\n name: \"memory_lesson_delete\",\n description:\n \"Soft-delete a lesson by id. Deleted lessons are excluded from recall and list; re-saving the same content creates a fresh lesson.\",\n inputSchema: {\n type: \"object\",\n properties: {\n lessonId: { type: \"string\", description: \"The lesson id (lsn_...)\" },\n },\n required: [\"lessonId\"],\n },\n },\n {\n name: \"memory_obsidian_export\",\n description:\n \"Export memories, lessons, and crystals as Obsidian-compatible Markdown files with YAML frontmatter and wikilinks for graph view.\",\n inputSchema: {\n type: \"object\",\n properties: {\n vaultDir: {\n type: \"string\",\n description: \"Output directory (default ~/.agentmemory/vault/)\",\n },\n types: {\n type: \"string\",\n description: \"Comma-separated types to export: memories,lessons,crystals,sessions (default all)\",\n },\n },\n },\n },\n];\n\nexport const V073_TOOLS: McpToolDef[] = [\n {\n name: \"memory_reflect\",\n description:\n \"Traverse the knowledge graph, group related memories by concept clusters, and synthesize higher-order insights via LLM. Returns new and reinforced insights.\",\n inputSchema: {\n type: \"object\",\n properties: {\n project: { type: \"string\", description: \"Filter by project\" },\n maxClusters: {\n type: \"number\",\n description: \"Max concept clusters to process (default 10, max 20)\",\n },\n },\n },\n },\n {\n name: \"memory_insight_list\",\n description:\n \"List synthesized insights — higher-order observations derived from patterns across memories, lessons, and crystals.\",\n inputSchema: {\n type: \"object\",\n properties: {\n project: { type: \"string\", description: \"Filter by project\" },\n minConfidence: {\n type: \"number\",\n description: \"Minimum confidence threshold (default 0)\",\n },\n limit: { type: \"number\", description: \"Max results (default 50)\" },\n },\n },\n },\n];\n\nexport const V010_SLOTS_TOOLS: McpToolDef[] = [\n {\n name: \"memory_slot_list\",\n description:\n \"List all memory slots (pinned + project + global). Slots are editable, size-limited memory units the agent can read and modify across sessions.\",\n inputSchema: { type: \"object\", properties: {} },\n },\n {\n name: \"memory_slot_get\",\n description: \"Read a single slot by label.\",\n inputSchema: {\n type: \"object\",\n properties: {\n label: { type: \"string\", description: \"Slot label (e.g. 'persona', 'pending_items')\" },\n },\n required: [\"label\"],\n },\n },\n {\n name: \"memory_slot_create\",\n description: \"Create a new slot. Reject if a slot with the same label already exists.\",\n inputSchema: {\n type: \"object\",\n properties: {\n label: { type: \"string\", description: \"Slot label — lowercase, starts with letter, [a-z0-9_]\" },\n content: { type: \"string\", description: \"Initial content (default empty)\" },\n sizeLimit: { type: \"number\", description: \"Max chars (default 2000, hard cap 20000)\" },\n description: { type: \"string\", description: \"What this slot is for\" },\n pinned: { type: \"string\", description: \"'false' to exclude from context injection; default true\" },\n scope: { type: \"string\", description: \"'project' (default) or 'global' (shared across projects)\" },\n },\n required: [\"label\"],\n },\n },\n {\n name: \"memory_slot_append\",\n description:\n \"Append text to an existing slot. Fails with 413 if the append would exceed the slot's sizeLimit — agent must compact via memory_slot_replace first.\",\n inputSchema: {\n type: \"object\",\n properties: {\n label: { type: \"string\", description: \"Slot label\" },\n text: { type: \"string\", description: \"Text to append\" },\n },\n required: [\"label\", \"text\"],\n },\n },\n {\n name: \"memory_slot_replace\",\n description: \"Replace slot content in place. Fails if content exceeds sizeLimit.\",\n inputSchema: {\n type: \"object\",\n properties: {\n label: { type: \"string\", description: \"Slot label\" },\n content: { type: \"string\", description: \"New full content\" },\n },\n required: [\"label\", \"content\"],\n },\n },\n {\n name: \"memory_slot_delete\",\n description: \"Delete a slot. Seeded default slots can be deleted unless marked readOnly.\",\n inputSchema: {\n type: \"object\",\n properties: {\n label: { type: \"string\", description: \"Slot label\" },\n },\n required: [\"label\"],\n },\n },\n];\n\nexport const ESSENTIAL_TOOLS = new Set([\n \"memory_save\",\n \"memory_recall\",\n \"memory_consolidate\",\n \"memory_smart_search\",\n \"memory_sessions\",\n \"memory_diagnose\",\n \"memory_lesson_save\",\n \"memory_reflect\",\n]);\n\nexport function getAllTools(): McpToolDef[] {\n return [\n ...CORE_TOOLS,\n ...V040_TOOLS,\n ...V050_TOOLS,\n ...V051_TOOLS,\n ...V061_TOOLS,\n ...V070_TOOLS,\n ...V073_TOOLS,\n ...V010_SLOTS_TOOLS,\n ];\n}\n\n// default switched from \"core\" (8 essential tools) to \"all\"\n// (full 54-tool surface). README and plugin manifests have always\n// advertised 54 tools \"in proxy mode\"; the old default left OpenCode /\n// Claude Code users seeing 8 with no indication the other tools existed.\n// Users who want the lean essentials can still set AGENTMEMORY_TOOLS=core.\nexport function getVisibleTools(): McpToolDef[] {\n const mode = process.env[\"AGENTMEMORY_TOOLS\"] || \"all\";\n if (mode === \"core\") return getAllTools().filter((t) => ESSENTIAL_TOOLS.has(t.name));\n return getAllTools();\n}\n","import { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport pc from \"picocolors\";\nimport type {\n AgentMemoryConfig,\n ProviderConfig,\n EmbeddingConfig,\n FallbackConfig,\n ClaudeBridgeConfig,\n TeamConfig,\n} from \"./types.js\";\n\n// Import-side ceiling for observations in a single session (enforced by mem::import).\n// This is independent of MAX_OBS_PER_SESSION, which controls runtime writes.\nexport const MAX_IMPORT_OBS_PER_SESSION = 5_000;\n\nfunction safeParseInt(value: string | undefined, fallback: number): number {\n if (!value) return fallback;\n const parsed = parseInt(value, 10);\n return Number.isNaN(parsed) ? fallback : parsed;\n}\n\nconst DATA_DIR = process.env[\"AGENTMEMORY_DATA_DIR\"] || join(homedir(), \".agentmemory\");\nconst ENV_FILE = process.env[\"AGENTMEMORY_ENV_FILE\"] || join(homedir(), \".agentmemory\", \".env\");\n\nlet warnPremiumModelShown = false;\nlet providerNoticeShown = false;\nlet obsCapImportCeilingWarned = false;\n\nfunction writeProviderNoticeOnce(text: string): void {\n if (providerNoticeShown) return;\n providerNoticeShown = true;\n process.stderr.write(text);\n}\n\n// Parsed ~/.agentmemory/.env, memoized for the process lifetime. getMergedEnv()\n// runs on every config getter (~20 of them), so without this cache a single\n// request would readFileSync + reparse the file dozens of times. The file is\n// boot-static, so read it from disk once and reuse the result. Tests that\n// mutate the file between cases reset the module (clearing this via reload) or\n// call __resetEnvFileCache().\nlet envFileCache: Record<string, string> | undefined;\n\nfunction loadEnvFile(): Record<string, string> {\n if (envFileCache) return envFileCache;\n if (!existsSync(ENV_FILE)) {\n envFileCache = {};\n return envFileCache;\n }\n envFileCache = parseEnvContent(readFileSync(ENV_FILE, \"utf-8\"));\n return envFileCache;\n}\n\nexport function parseEnvContent(content: string): Record<string, string> {\n const vars: Record<string, string> = {};\n for (const line of content.split(\"\\n\")) {\n const trimmed = line.trim();\n if (!trimmed || trimmed.startsWith(\"#\")) continue;\n const eqIdx = trimmed.indexOf(\"=\");\n if (eqIdx === -1) continue;\n const key = trimmed.slice(0, eqIdx).trim();\n let val = trimmed.slice(eqIdx + 1).trim();\n const quoteChar = val[0] === '\"' || val[0] === \"'\" ? val[0] : \"\";\n if (quoteChar) {\n const closeIdx = val.indexOf(quoteChar, 1);\n if (closeIdx !== -1) val = val.slice(1, closeIdx);\n } else {\n const hashIdx = val.indexOf(\" #\");\n if (hashIdx !== -1) val = val.slice(0, hashIdx).trim();\n }\n vars[key] = val;\n }\n return vars;\n}\n\n// Test hook: clears the memoized .env so the next loadEnvFile() re-reads disk\n// within the same module instance. vi.resetModules() reloads this module and\n// resets the cache on its own; this exists for tests that mutate the file\n// without a module reload.\nexport function __resetEnvFileCache(): void {\n envFileCache = undefined;\n}\n\nfunction hasRealValue(v: string | undefined): v is string {\n return typeof v === \"string\" && v.trim().length > 0;\n}\n\n// Hydrate ~/.agentmemory/.env into process.env at boot. loadEnvFile() is\n// otherwise only consumed via getMergedEnv(), which the many modules that\n// read raw process.env[\"X\"] never call — so .env-only values were silently\n// ignored by them. Copy the file's vars into process.env, but only when the\n// key is currently unset so a real process.env value still wins (this\n// preserves the {...fileEnv, ...process.env} precedence getMergedEnv uses).\nexport function hydrateProcessEnvFromFile(): Record<string, string | undefined> {\n const beforeHydration = { ...process.env };\n for (const [k, v] of Object.entries(loadEnvFile())) {\n if (process.env[k] === undefined) process.env[k] = v;\n }\n return beforeHydration;\n}\n\n// Warn once when a configured model is in the premium tier and likely to burn\n// money on background compression. Captured workload data shows ~$5/35h on\n// claude-sonnet-4 vs ~$0.46/35h on deepseek-v4-pro for the same compression\n// mix. Heuristic match avoids hard-coding a pricing table.\nfunction warnIfPremiumModel(env: Record<string, string>, envName: string, model: string): void {\n if (\n warnPremiumModelShown ||\n !/sonnet|opus|gpt-5\\.\\d+-sol|gpt-4o(?!.*mini)|gpt-4-turbo/i.test(model) ||\n env[\"AGENTMEMORY_SUPPRESS_COST_WARNING\"] === \"1\" ||\n env[\"AGENTMEMORY_SUPPRESS_COST_WARNING\"] === \"true\"\n ) {\n return;\n }\n warnPremiumModelShown = true;\n process.stderr.write(\n `[agentmemory] ${envName}=${model} is in the premium tier. ` +\n `Background compression on this model can cost $5+/day under active use. ` +\n `Cheaper alternatives with comparable quality for memory compression: ` +\n `deepseek/deepseek-v4-flash-0731, deepseek/deepseek-v4-pro, qwen/qwen3-coder. ` +\n `See README \"Cost-aware model selection\" for the full table. ` +\n `Set AGENTMEMORY_SUPPRESS_COST_WARNING=1 to silence.\\n`,\n );\n}\n\nfunction detectProvider(env: Record<string, string>): ProviderConfig {\n const maxTokens = parseInt(env[\"MAX_TOKENS\"] || \"4096\", 10);\n\n // OpenAI-compatible: supports OpenAI, DeepSeek, SiliconFlow, Azure, vLLM, LM Studio\n if (hasRealValue(env[\"OPENAI_API_KEY\"]) && env[\"OPENAI_API_KEY_FOR_LLM\"] !== \"false\") {\n const model = env[\"OPENAI_MODEL\"] || \"gpt-5.6-luna\";\n warnIfPremiumModel(env, \"OPENAI_MODEL\", model);\n return {\n provider: \"openai\",\n model,\n maxTokens,\n baseURL: env[\"OPENAI_BASE_URL\"],\n };\n }\n\n // MiniMax: Anthropic-compatible API, requires raw fetch to avoid SDK stainless headers\n if (hasRealValue(env[\"MINIMAX_API_KEY\"])) {\n return {\n provider: \"minimax\",\n model: env[\"MINIMAX_MODEL\"] || \"MiniMax-M3\",\n maxTokens,\n };\n }\n\n if (hasRealValue(env[\"ANTHROPIC_API_KEY\"])) {\n return {\n provider: \"anthropic\",\n model: env[\"ANTHROPIC_MODEL\"] || \"claude-sonnet-5\",\n maxTokens,\n baseURL: env[\"ANTHROPIC_BASE_URL\"],\n };\n }\n if (hasRealValue(env[\"GEMINI_API_KEY\"]) || hasRealValue(env[\"GOOGLE_API_KEY\"])) {\n if (!hasRealValue(env[\"GEMINI_API_KEY\"]) && hasRealValue(env[\"GOOGLE_API_KEY\"])) {\n process.stderr.write(\n \"[agentmemory] GOOGLE_API_KEY detected — treating as GEMINI_API_KEY. \" +\n \"Set GEMINI_API_KEY in ~/.agentmemory/.env to silence this warning.\\n\",\n );\n }\n return {\n provider: \"gemini\",\n model: env[\"GEMINI_MODEL\"] || \"gemini-3.7-flash\",\n maxTokens,\n };\n }\n if (hasRealValue(env[\"OPENROUTER_API_KEY\"])) {\n const model = env[\"OPENROUTER_MODEL\"] || \"anthropic/claude-sonnet-5\";\n warnIfPremiumModel(env, \"OPENROUTER_MODEL\", model);\n return {\n provider: \"openrouter\",\n model,\n maxTokens,\n };\n }\n\n const allowAgentSdk = env[\"AGENTMEMORY_ALLOW_AGENT_SDK\"] === \"true\";\n if (!allowAgentSdk) {\n writeProviderNoticeOnce(\n pc.dim(\n \"[agentmemory] No LLM provider key set — running zero-LLM: no LLM compression or summaries; search uses BM25 plus any configured embedding provider. \" +\n \"Set EMBEDDING_PROVIDER=local for on-device semantic embeddings if none is configured. \" +\n \"Set ANTHROPIC_API_KEY (or GEMINI/OPENAI/OPENROUTER/MINIMAX) in ~/.agentmemory/.env for LLM compression and summaries. \" +\n \"Agent-SDK fallback stays off by default to avoid a Stop-hook recursion loop; opt in with AGENTMEMORY_AUTO_COMPRESS=true + AGENTMEMORY_ALLOW_AGENT_SDK=true.\\n\",\n ),\n );\n return {\n provider: \"noop\",\n model: \"noop\",\n maxTokens,\n };\n }\n\n writeProviderNoticeOnce(\n \"[agentmemory] WARNING: agent-sdk fallback enabled via AGENTMEMORY_ALLOW_AGENT_SDK=true. \" +\n \"This spawns @anthropic-ai/claude-agent-sdk child sessions that can trigger the Stop-hook \" +\n \"recursion loop. A SDK-child env marker is set to block re-entry, \" +\n \"but prefer setting a real API key in ~/.agentmemory/.env instead.\\n\",\n );\n return {\n provider: \"agent-sdk\",\n model: \"claude-sonnet-5\",\n maxTokens,\n };\n}\n\nexport function loadConfig(): AgentMemoryConfig {\n const env = getMergedEnv();\n\n const provider = detectProvider(env);\n\n // REST is the anchor; viewer and engine ports derive from it unless individually overridden.\n const restPort = parseInt(env[\"III_REST_PORT\"] || \"3111\", 10) || 3111;\n const viewerPort =\n parseInt(env[\"III_VIEWER_PORT\"] || \"\", 10) || restPort + 2;\n const engineUrl =\n env[\"III_ENGINE_URL\"] ||\n env[\"III_URL\"] ||\n `ws://localhost:${\n parseInt(env[\"III_ENGINE_PORT\"] || \"\", 10) || restPort + 46023\n }`;\n\n const maxObservationsPerSession = safeParseInt(env[\"MAX_OBS_PER_SESSION\"], 500);\n if (\n !obsCapImportCeilingWarned &&\n (maxObservationsPerSession === 0 || maxObservationsPerSession > MAX_IMPORT_OBS_PER_SESSION)\n ) {\n obsCapImportCeilingWarned = true;\n const capDesc = maxObservationsPerSession === 0 ? \"uncapped (0)\" : String(maxObservationsPerSession);\n process.stderr.write(\n `[agentmemory] WARNING: MAX_OBS_PER_SESSION is ${capDesc}, above the ` +\n `${MAX_IMPORT_OBS_PER_SESSION}-per-session import ceiling (MAX_IMPORT_OBS_PER_SESSION). ` +\n `Sessions past that ceiling make every mem::export -> mem::import restore fail wholesale. ` +\n `Keep MAX_OBS_PER_SESSION at or below ${MAX_IMPORT_OBS_PER_SESSION} if you rely on export/import backups.\\n`,\n );\n }\n\n return {\n engineUrl,\n restPort,\n viewerPort,\n provider,\n tokenBudget: safeParseInt(env[\"TOKEN_BUDGET\"], 2000),\n maxObservationsPerSession,\n compressionModel: provider.model,\n dataDir: DATA_DIR,\n };\n}\n\nfunction getMergedEnv(\n overrides?: Record<string, string>,\n): Record<string, string> {\n const fileEnv = loadEnvFile();\n return { ...fileEnv, ...process.env, ...overrides } as Record<string, string>;\n}\n\nexport function getEnvVar(key: string): string | undefined {\n return getMergedEnv()[key];\n}\n\nexport function isSessionSweepEnabled(): boolean {\n return getMergedEnv()[\"AGENTMEMORY_SESSION_SWEEP_ENABLED\"] !== \"false\";\n}\n\nexport function getSessionSweepStaleHours(): number {\n const raw = getMergedEnv()[\"AGENTMEMORY_SESSION_SWEEP_STALE_HOURS\"];\n if (!raw || !/^\\d+$/.test(raw.trim())) return 24;\n const parsed = parseInt(raw.trim(), 10);\n return parsed > 0 ? parsed : 24;\n}\n\nexport function getFinalizeIdleMs(): number {\n const raw = getMergedEnv()[\"AGENTMEMORY_FINALIZE_IDLE_MS\"];\n if (!raw || !/^\\d+$/.test(raw.trim())) return 120000;\n return parseInt(raw.trim(), 10);\n}\n\nexport function isGraphCompactOnBootEnabled(): boolean {\n return getMergedEnv()[\"AGENTMEMORY_GRAPH_COMPACT_ON_BOOT\"] !== \"false\";\n}\n\nexport function isDropStaleIndexEnabled(): boolean {\n return getMergedEnv()[\"AGENTMEMORY_DROP_STALE_INDEX\"] === \"true\";\n}\n\nconst VALID_STATE_BACKENDS = new Set([\"file\", \"redis\"]);\n\nexport function getStateBackend(): \"file\" | \"redis\" {\n const raw = (getMergedEnv()[\"AGENTMEMORY_STATE_BACKEND\"] || \"\")\n .trim()\n .toLowerCase();\n if (!raw) return \"file\";\n if (!VALID_STATE_BACKENDS.has(raw)) {\n throw new Error(\n `AGENTMEMORY_STATE_BACKEND=\"${raw}\" is not a recognized state backend. Use \"file\" (the default) or \"redis\", or unset it to keep the default file store.`,\n );\n }\n return raw as \"file\" | \"redis\";\n}\n\nexport function getRedisUrl(): string | undefined {\n const value = getMergedEnv()[\"AGENTMEMORY_REDIS_URL\"];\n return hasRealValue(value) ? value.trim() : undefined;\n}\n\nexport function getAuditRetentionMonths(): number {\n return safeParseInt(getMergedEnv()[\"AGENTMEMORY_AUDIT_RETENTION_MONTHS\"], 0);\n}\n\nexport function getAuditMigrateMaxBytes(): number {\n return safeParseInt(\n getMergedEnv()[\"AGENTMEMORY_AUDIT_MIGRATE_MAX_BYTES\"],\n 32 * 1024 * 1024,\n );\n}\n\nexport function detectLlmProviderKind(\n env: Record<string, string | undefined> = getMergedEnv(),\n): \"llm\" | \"noop\" {\n if (\n hasRealValue(env[\"ANTHROPIC_API_KEY\"]) ||\n hasRealValue(env[\"GEMINI_API_KEY\"]) ||\n hasRealValue(env[\"GOOGLE_API_KEY\"]) ||\n hasRealValue(env[\"OPENROUTER_API_KEY\"]) ||\n hasRealValue(env[\"MINIMAX_API_KEY\"]) ||\n (hasRealValue(env[\"OPENAI_API_KEY\"]) &&\n env[\"OPENAI_API_KEY_FOR_LLM\"] !== \"false\") ||\n env[\"AGENTMEMORY_ALLOW_AGENT_SDK\"] === \"true\"\n ) {\n return \"llm\";\n }\n return \"noop\";\n}\n\nexport function describeLlmRoute(provider: ProviderConfig): string | null {\n if (provider.provider === \"noop\") return null;\n const parts = [provider.provider, provider.model];\n if (provider.baseURL) {\n try {\n parts.push(new URL(provider.baseURL).host);\n } catch {\n parts.push(\"custom base URL\");\n }\n }\n return parts.join(\" · \");\n}\n\nexport function loadEmbeddingConfig(): EmbeddingConfig {\n const env = getMergedEnv();\n let bm25Weight = parseFloat(env[\"BM25_WEIGHT\"] || \"0.4\");\n let vectorWeight = parseFloat(env[\"VECTOR_WEIGHT\"] || \"0.6\");\n bm25Weight =\n isNaN(bm25Weight) || bm25Weight < 0 ? 0.4 : Math.min(bm25Weight, 1);\n vectorWeight =\n isNaN(vectorWeight) || vectorWeight < 0 ? 0.6 : Math.min(vectorWeight, 1);\n return {\n provider: env[\"EMBEDDING_PROVIDER\"] || undefined,\n bm25Weight,\n vectorWeight,\n };\n}\n\nexport function detectEmbeddingProvider(\n env?: Record<string, string>,\n): string | null {\n const source = env ?? getMergedEnv();\n const forced = source[\"EMBEDDING_PROVIDER\"];\n if (forced) return forced;\n\n if (source[\"GEMINI_API_KEY\"]) return \"gemini\";\n if (source[\"OPENAI_API_KEY\"] || source[\"OPENAI_EMBEDDING_API_KEY\"]) return \"openai\";\n if (source[\"VOYAGE_API_KEY\"]) return \"voyage\";\n if (source[\"COHERE_API_KEY\"]) return \"cohere\";\n if (source[\"OPENROUTER_API_KEY\"]) return \"openrouter\";\n return null;\n}\n\nexport function loadClaudeBridgeConfig(): ClaudeBridgeConfig {\n const env = getMergedEnv();\n const enabled = env[\"CLAUDE_MEMORY_BRIDGE\"] === \"true\";\n const projectPath = env[\"CLAUDE_PROJECT_PATH\"] || \"\";\n const lineBudget = safeParseInt(env[\"CLAUDE_MEMORY_LINE_BUDGET\"], 200);\n let memoryFilePath = \"\";\n if (enabled && projectPath) {\n // Claude Code stores project memory at\n // ~/.claude/projects/<slug>/memory/MEMORY.md\n // where <slug> is the project path with `/` and `\\` swapped for `-`.\n // The leading `-` from an absolute POSIX path is preserved (Claude\n // Code keeps it; stripping it produced a slug Claude never reads).\n // The `memory/` subdirectory holds MEMORY.md (the index) plus one\n // per-topic `.md` file per memory (verified against Claude Code 2.x).\n const safePath = projectPath.replace(/[/\\\\]/g, \"-\");\n memoryFilePath = join(\n homedir(),\n \".claude\",\n \"projects\",\n safePath,\n \"memory\",\n \"MEMORY.md\",\n );\n }\n return { enabled, projectPath, memoryFilePath, lineBudget };\n}\n\nexport function loadTeamConfig(): TeamConfig | null {\n const env = getMergedEnv();\n const teamId = env[\"TEAM_ID\"];\n const userId = env[\"USER_ID\"];\n if (!teamId || !userId) return null;\n const mode = env[\"TEAM_MODE\"] === \"shared\" ? \"shared\" : \"private\";\n return { teamId, userId, mode };\n}\n\n// optional AGENT_ID env for multi-agent memory isolation.\n// Returns null when unset so memory stays unscoped (legacy behavior).\n// Trimmed + length-capped to keep KV writes well-formed.\n//\n// Filtering is gated by AGENTMEMORY_AGENT_SCOPE:\n// \"shared\" (default) — tag everything, do not filter recall paths\n// \"isolated\" — tag everything AND filter recall paths\nexport function loadAgentScope(): {\n agentId: string;\n mode: \"shared\" | \"isolated\";\n} | null {\n const env = getMergedEnv();\n const raw = env[\"AGENT_ID\"];\n if (!raw) return null;\n const agentId = raw.trim().slice(0, 128);\n if (!agentId) return null;\n const mode = env[\"AGENTMEMORY_AGENT_SCOPE\"] === \"isolated\"\n ? \"isolated\"\n : \"shared\";\n return { agentId, mode };\n}\n\nexport function getAgentId(): string | undefined {\n return loadAgentScope()?.agentId;\n}\n\n// True only when AGENT_ID is set AND scope=isolated. Recall paths\n// consult this to decide whether to filter.\nexport function isAgentScopeIsolated(): boolean {\n return loadAgentScope()?.mode === \"isolated\";\n}\n\n// Floor for the git-snapshot timer. A zero/negative SNAPSHOT_INTERVAL would\n// make setInterval fire on roughly every event-loop tick, saturating the\n// worker with back-to-back full-state snapshots + git commits. Anything below\n// this floor is treated as a misconfiguration and falls back to the default.\nconst SNAPSHOT_INTERVAL_DEFAULT_SECONDS = 3600;\nconst MIN_SNAPSHOT_INTERVAL_SECONDS = 1;\n\nexport function loadSnapshotConfig(): {\n enabled: boolean;\n interval: number;\n dir: string;\n} {\n const env = getMergedEnv();\n const rawInterval = safeParseInt(\n env[\"SNAPSHOT_INTERVAL\"],\n SNAPSHOT_INTERVAL_DEFAULT_SECONDS,\n );\n const interval =\n rawInterval >= MIN_SNAPSHOT_INTERVAL_SECONDS\n ? rawInterval\n : SNAPSHOT_INTERVAL_DEFAULT_SECONDS;\n return {\n enabled: env[\"SNAPSHOT_ENABLED\"] === \"true\",\n interval,\n dir: env[\"SNAPSHOT_DIR\"] || join(homedir(), \".agentmemory\", \"snapshots\"),\n };\n}\n\nexport function isGraphExtractionEnabled(): boolean {\n return getMergedEnv()[\"GRAPH_EXTRACTION_ENABLED\"] === \"true\";\n}\n\nexport function getGraphBatchSize(): number {\n return safeParseInt(getMergedEnv()[\"GRAPH_EXTRACTION_BATCH_SIZE\"], 10);\n}\n\n// window for the smart-search followup-rate diagnostic. A second\n// search arriving within this many seconds (with disjoint results)\n// counts as a \"follow-up\" — a directional signal that the first result\n// set didn't satisfy. Long values overcount (legitimate refinement\n// looks like a follow-up); short values undercount.\nconst FOLLOWUP_WINDOW_DEFAULT_SECONDS = 30;\n\nexport function getFollowupWindowSeconds(): number {\n return safeParseInt(\n getMergedEnv()[\"AGENTMEMORY_FOLLOWUP_WINDOW_SECONDS\"],\n FOLLOWUP_WINDOW_DEFAULT_SECONDS,\n );\n}\n\nexport function isConsolidationEnabled(): boolean {\n const env = getMergedEnv();\n const explicit = env[\"CONSOLIDATION_ENABLED\"];\n if (explicit === \"false\" || explicit === \"0\") return false;\n if (explicit === \"true\" || explicit === \"1\") return true;\n if ((env[\"AGENTMEMORY_PROVIDER\"] || \"\").toLowerCase() === \"noop\") return false;\n return detectLlmProviderKind(env) === \"llm\";\n}\n\n// Per-observation LLM compression is OFF by default as of 0.8.8.\n// When disabled, observations are captured and indexed via a synthetic\n// (zero-LLM) compression path so recall/search still works. Users who want\n// richer LLM-generated summaries can set AGENTMEMORY_AUTO_COMPRESS=true in\n// ~/.agentmemory/.env — but should expect their Claude API token usage to\n// climb proportionally with session tool-use frequency.\nexport function isAutoCompressEnabled(): boolean {\n return getMergedEnv()[\"AGENTMEMORY_AUTO_COMPRESS\"] === \"true\";\n}\n\n// Hook-level context injection into Claude Code's conversation is OFF by\n// default as of 0.8.10. When disabled, pre-tool-use and\n// session-start hooks still POST observations for background capture, but\n// never write context to stdout — so Claude Code doesn't inject an extra\n// ~4000-char blob into every tool turn. 0.8.8 stopped the agentmemory-side\n// Claude calls (via ANTHROPIC_API_KEY); this stops the Claude Code-side\n// token burn where every tool call silently grew the model input window.\n// Users who want the in-conversation context injection explicitly opt in\n// with AGENTMEMORY_INJECT_CONTEXT=true and get a loud startup warning.\nexport function isContextInjectionEnabled(): boolean {\n return getMergedEnv()[\"AGENTMEMORY_INJECT_CONTEXT\"] === \"true\";\n}\n\nexport const CONSOLIDATION_INTERVAL_DEFAULT_MS = 7200000;\n\nexport function getConsolidationIntervalMs(): number {\n const raw = parseInt(process.env.CONSOLIDATION_INTERVAL_MS || String(CONSOLIDATION_INTERVAL_DEFAULT_MS), 10);\n return Number.isFinite(raw) && raw > 0 ? raw : CONSOLIDATION_INTERVAL_DEFAULT_MS;\n}\n\nexport function getConsolidationDecayDays(): number {\n return safeParseInt(getMergedEnv()[\"CONSOLIDATION_DECAY_DAYS\"], 30);\n}\n\n// Cooldown between corpus consolidations triggered by session stop. The Stop\n// hook fires per agent turn and posts /session/end, so without this every turn\n// would kick a full LLM semantic-merge + reflect + crystallize. Debounced to at\n// most once per window. Set to 0 to disable the debounce (consolidate on every\n// stop). Default 5 minutes.\nconst CONSOLIDATION_COOLDOWN_DEFAULT_MS = 300000;\n\nexport function getConsolidationCooldownMs(): number {\n const raw = safeParseInt(\n getMergedEnv()[\"AGENTMEMORY_CONSOLIDATION_COOLDOWN_MS\"],\n CONSOLIDATION_COOLDOWN_DEFAULT_MS,\n );\n return raw >= 0 ? raw : CONSOLIDATION_COOLDOWN_DEFAULT_MS;\n}\n\nexport const INDEX_SAVE_INTERVAL_DEFAULT_MS = 600_000;\n\nexport function getIndexSaveIntervalMs(): number {\n const raw = safeParseInt(\n getMergedEnv()[\"AGENTMEMORY_INDEX_SAVE_INTERVAL_MS\"],\n INDEX_SAVE_INTERVAL_DEFAULT_MS,\n );\n return raw > 0 ? raw : INDEX_SAVE_INTERVAL_DEFAULT_MS;\n}\n\nexport const STATE_SAVE_INTERVAL_DEFAULT_MS = 2000;\nexport const STATE_SAVE_INTERVAL_MIN_MS = 100;\nexport const STATE_SAVE_INTERVAL_MAX_MS = 3_600_000;\n\nexport function getStateSaveIntervalMs(): number {\n const value = getMergedEnv()[\"AGENTMEMORY_STATE_SAVE_INTERVAL_MS\"];\n if (value === undefined || value.trim() === \"\") return STATE_SAVE_INTERVAL_DEFAULT_MS;\n if (!/^\\d+$/.test(value.trim())) return STATE_SAVE_INTERVAL_DEFAULT_MS;\n const raw = Number(value);\n return Number.isSafeInteger(raw) && raw >= STATE_SAVE_INTERVAL_MIN_MS && raw <= STATE_SAVE_INTERVAL_MAX_MS\n ? raw\n : STATE_SAVE_INTERVAL_DEFAULT_MS;\n}\n\nexport const VECTOR_BUCKET_SIZE_DEFAULT = 500;\n\nexport function getVectorBucketSize(): number {\n const raw = safeParseInt(getMergedEnv()[\"AGENTMEMORY_VECTOR_BUCKET_SIZE\"], VECTOR_BUCKET_SIZE_DEFAULT);\n return raw > 0 ? raw : VECTOR_BUCKET_SIZE_DEFAULT;\n}\n\nexport const VECTOR_BACKFILL_MAX_DEFAULT = 500;\n\nexport function getVectorBackfillMax(): number {\n const raw = safeParseInt(getMergedEnv()[\"AGENTMEMORY_VECTOR_BACKFILL_MAX\"], VECTOR_BACKFILL_MAX_DEFAULT);\n return raw > 0 ? raw : VECTOR_BACKFILL_MAX_DEFAULT;\n}\n\nexport function isVectorBackfillAllEnabled(): boolean {\n return getMergedEnv()[\"AGENTMEMORY_VECTOR_BACKFILL\"] === \"all\";\n}\n\nexport function isStandaloneMcp(): boolean {\n return getMergedEnv()[\"STANDALONE_MCP\"] === \"true\";\n}\n\nexport function getStandalonePersistPath(): string {\n const env = getMergedEnv();\n return (\n env[\"STANDALONE_PERSIST_PATH\"] ||\n join(homedir(), \".agentmemory\", \"standalone.json\")\n );\n}\n\nconst VALID_PROVIDERS = new Set([\n \"anthropic\",\n \"gemini\",\n \"openrouter\",\n \"agent-sdk\",\n \"minimax\",\n \"openai\",\n]);\n\nexport function loadFallbackConfig(): FallbackConfig {\n const env = getMergedEnv();\n const raw = env[\"FALLBACK_PROVIDERS\"] || \"\";\n const allowAgentSdk = env[\"AGENTMEMORY_ALLOW_AGENT_SDK\"] === \"true\";\n const providers = raw\n .split(\",\")\n .map((p) => p.trim())\n .filter(\n (p): p is FallbackConfig[\"providers\"][number] =>\n Boolean(p) && VALID_PROVIDERS.has(p),\n )\n .filter((p) => {\n // Honor the same safety gate as detectProvider: agent-sdk is only\n // permitted as a fallback target when the user has explicitly opted\n // in. Without this filter, a user could set FALLBACK_PROVIDERS=agent-sdk\n // and re-introduce the Stop-hook recursion loop even though\n // detectProvider() returned the noop provider.\n if (p === \"agent-sdk\" && !allowAgentSdk) {\n process.stderr.write(\n \"[agentmemory] Ignoring FALLBACK_PROVIDERS entry 'agent-sdk' \" +\n \"(AGENTMEMORY_ALLOW_AGENT_SDK is not 'true'). The agent-sdk \" +\n \"fallback can spawn Claude Agent SDK child sessions that trigger \" +\n \"the Stop-hook recursion loop. Opt in explicitly \" +\n \"with AGENTMEMORY_ALLOW_AGENT_SDK=true if this is intentional.\\n\",\n );\n return false;\n }\n return true;\n });\n return { providers };\n}\n","export type SearchLayer = \"all\" | \"memory\" | \"observation\";\nexport type SearchResultLayer = \"memory\" | \"observation\" | \"lesson\" | \"insight\";\nexport type SearchLayerResolver = (obsId: string, sessionId: string) => SearchResultLayer | undefined;\n\nexport function isSearchLayer(value: unknown): value is SearchLayer {\n return value === \"all\" || value === \"memory\" || value === \"observation\";\n}\n\nexport function isSearchResultLayer(value: unknown): value is SearchResultLayer {\n return value === \"memory\" || value === \"observation\" || value === \"lesson\" || value === \"insight\";\n}\n\nexport function getSearchResultLayer(obsId: string, sessionId: string): SearchResultLayer {\n if (obsId.startsWith(\"mem_\")) return \"memory\";\n if (sessionId === \"lesson\" || obsId.startsWith(\"lsn_\")) return \"lesson\";\n if (sessionId === \"insight\" || obsId.startsWith(\"ins_\")) return \"insight\";\n return \"observation\";\n}\n\nexport function matchesSearchLayer(\n obsId: string,\n sessionId: string,\n targetLayer: SearchLayer = \"all\",\n layer?: SearchResultLayer,\n): boolean {\n return targetLayer === \"all\" || (layer ?? getSearchResultLayer(obsId, sessionId)) === targetLayer;\n}\n","import type { CompressedObservation, Lesson, Memory } from \"../types.js\";\n\n// Wraps a Memory record in the CompressedObservation shape that\n// SearchIndex / VectorIndex / enrichment paths consume. Memories share\n// the same searchable fields as observations (title + content +\n// concepts + files); type is normalized to \"decision\" so memories stay\n// distinguishable in result metadata without colliding with observation\n// enums (file_read, command_run, …). The synthetic sessionId\n// (\"memory\" or memory.sessionIds[0]) is what enrich-side fallbacks key\n// off of when looking up the source record in KV.memories.\nexport function memoryToObservation(memory: Memory): CompressedObservation {\n return {\n id: memory.id,\n sessionId: memory.sessionIds?.[0] ?? \"memory\",\n timestamp: memory.createdAt,\n type: \"decision\",\n title: memory.title,\n facts: [memory.content],\n narrative: memory.content,\n concepts: memory.concepts,\n files: memory.files,\n importance: memory.strength,\n // Carry the owning agent through so agent-scoped search filters see\n // memories, not just raw observations. Dropping it made every memory\n // invisible to any agentId-scoped query.\n ...(memory.agentId ? { agentId: memory.agentId } : {}),\n };\n}\n\n// Same adapter for lessons, kept beside memoryToObservation so a new\n// CompressedObservation field has one obvious place to be threaded\n// through both record kinds.\nexport function lessonToObservation(l: Lesson): CompressedObservation {\n return {\n id: l.id,\n sessionId: \"lesson\",\n timestamp: l.createdAt,\n type: \"decision\",\n title: l.content.slice(0, 120),\n facts: [l.content],\n narrative: l.context || \"\",\n concepts: l.tags,\n files: [],\n importance: l.confidence,\n };\n}\n","import type { CompactSearchResult, SearchResult } from \"../types.js\";\n\nexport type RecallFormat = \"full\" | \"compact\" | \"narrative\";\n\ntype ClippedResult = { content_truncated?: true };\ntype FullResult = SearchResult & ClippedResult;\ntype CompactResult = CompactSearchResult & ClippedResult;\ntype NarrativeResult = Omit<CompactResult, \"type\"> & { narrative: string };\ntype RecallResult = FullResult | CompactResult | NarrativeResult;\n\ninterface RecallMetadata {\n tokens_used: number;\n tokens_budget?: number;\n truncated: boolean;\n matched_count: number;\n excluded_by_budget: number;\n excluded_results: Array<{ obsId: string; sessionId: string; title: string }>;\n excluded_results_truncated: boolean;\n minimum_budget?: number;\n}\n\nexport type RecallResponse = RecallMetadata & (\n | { format: \"full\"; results: FullResult[] }\n | { format: \"compact\"; results: CompactResult[] }\n | { format: \"narrative\"; results: NarrativeResult[]; text: string }\n);\n\nfunction estimateTokens(value: unknown): number {\n return Math.max(1, Math.ceil(JSON.stringify(value).length / 3));\n}\n\nfunction prefix(text: string, length: number): string {\n let end = Math.min(length, text.length);\n if (end > 0 && end < text.length && /[\\uD800-\\uDBFF]/.test(text[end - 1])) end--;\n return text.slice(0, end);\n}\n\nfunction fitText<T>(text: string, budget: number, makeResult: (text: string) => T): T {\n let low = 0;\n let high = text.length;\n while (low < high) {\n const middle = Math.ceil((low + high) / 2);\n if (estimateTokens(makeResult(prefix(text, middle))) <= budget) low = middle;\n else high = middle - 1;\n }\n return makeResult(prefix(text, low));\n}\n\nfunction formatResult(result: SearchResult, format: RecallFormat): RecallResult {\n if (format === \"full\") return result;\n const observation = result.observation;\n const compact: CompactResult = {\n obsId: observation.id,\n sessionId: result.sessionId,\n title: observation.title,\n type: observation.type,\n score: result.score,\n timestamp: observation.timestamp,\n ...(result.layer !== undefined && { layer: result.layer }),\n };\n if (format === \"compact\") return compact;\n const { type: _type, ...narrative } = compact;\n return { ...narrative, narrative: observation.narrative };\n}\n\nfunction clipResult(\n result: SearchResult,\n format: RecallFormat,\n budget: number,\n): { result?: RecallResult; minimum: number } {\n const observation = result.observation;\n const layer = result.layer !== undefined ? { layer: result.layer } : {};\n if (format === \"compact\") {\n const base: CompactResult = {\n obsId: observation.id,\n sessionId: result.sessionId,\n title: \"\",\n type: observation.type,\n score: result.score,\n timestamp: observation.timestamp,\n ...layer,\n content_truncated: true,\n };\n const minimum = estimateTokens(base);\n if (minimum > budget) return { minimum };\n return {\n result: fitText(observation.title, budget, (title) => ({ ...base, title })),\n minimum,\n };\n }\n\n const makeResult = (title: string, narrative: string): FullResult | NarrativeResult => {\n if (format === \"narrative\") {\n return {\n obsId: observation.id,\n sessionId: result.sessionId,\n title,\n narrative,\n score: result.score,\n timestamp: observation.timestamp,\n ...layer,\n content_truncated: true,\n };\n }\n return {\n observation: {\n id: observation.id,\n sessionId: observation.sessionId,\n timestamp: observation.timestamp,\n type: observation.type,\n title,\n facts: [],\n narrative,\n concepts: [],\n files: [],\n importance: observation.importance,\n ...(observation.confidence !== undefined && { confidence: observation.confidence }),\n ...(observation.agentId !== undefined && { agentId: observation.agentId }),\n ...(observation.origin && {\n origin: { channel: observation.origin.channel, capturedAt: observation.origin.capturedAt },\n }),\n },\n score: result.score,\n sessionId: result.sessionId,\n ...layer,\n content_truncated: true,\n };\n };\n const minimum = estimateTokens(makeResult(\"\", \"\"));\n if (minimum > budget) return { minimum };\n\n const titleResult = fitText(prefix(observation.title, 120), budget, (title) => makeResult(title, \"\"));\n const title = \"observation\" in titleResult ? titleResult.observation.title : titleResult.title;\n const narrative = observation.narrative || observation.facts.join(\"\\n\");\n return {\n result: fitText(narrative, budget, (text) => makeResult(title, text)),\n minimum,\n };\n}\n\nexport function buildRecallResponse(\n results: SearchResult[],\n format: RecallFormat,\n tokenBudget?: number,\n): RecallResponse {\n const selected: RecallResult[] = [];\n let used = 0;\n let clipped = false;\n let minimumBudget: number | undefined;\n for (const result of results) {\n const formatted = formatResult(result, format);\n const tokens = estimateTokens(formatted);\n if (tokenBudget === undefined || used + tokens <= tokenBudget) {\n selected.push(formatted);\n used += tokens;\n continue;\n }\n if (selected.length === 0) {\n const preview = clipResult(result, format, tokenBudget);\n if (preview.result) {\n selected.push(preview.result);\n used = estimateTokens(preview.result);\n clipped = true;\n continue;\n } else {\n minimumBudget = Math.min(preview.minimum, tokens);\n }\n }\n break;\n }\n\n const excluded = results.slice(selected.length);\n const metadata: RecallMetadata = {\n tokens_used: used,\n tokens_budget: tokenBudget,\n truncated: clipped || excluded.length > 0,\n matched_count: results.length,\n excluded_by_budget: excluded.length,\n excluded_results: excluded.slice(0, 10).map((result) => ({\n obsId: result.observation.id,\n sessionId: result.sessionId,\n title: prefix(result.observation.title, 120),\n })),\n excluded_results_truncated: excluded.length > 10,\n ...(minimumBudget !== undefined && { minimum_budget: minimumBudget }),\n };\n if (format === \"narrative\") {\n const narrativeResults = selected as NarrativeResult[];\n return {\n ...metadata,\n format,\n results: narrativeResults,\n text: narrativeResults.map((result, index) => `${index + 1}. ${result.title}\\n${result.narrative}`).join(\"\\n\\n\"),\n };\n }\n if (format === \"compact\") return { ...metadata, format, results: selected as CompactResult[] };\n return { ...metadata, format, results: selected as FullResult[] };\n}\n","export type SearchExpansionId = string | { obsId: string; sessionId: string };\n\nexport function parseSearchExpansionIds(value: unknown): SearchExpansionId[] {\n const entries = typeof value === \"string\" ? value.split(\",\") : Array.isArray(value) ? value : [];\n return entries.flatMap((entry): SearchExpansionId[] => {\n if (typeof entry === \"string\") return entry.trim() ? [entry.trim()] : [];\n if (!entry || typeof entry !== \"object\" || typeof entry.obsId !== \"string\" || !entry.obsId.trim()) return [];\n const obsId = entry.obsId.trim();\n return typeof entry.sessionId === \"string\" && entry.sessionId.trim()\n ? [{ obsId, sessionId: entry.sessionId.trim() }]\n : [obsId];\n });\n}\n","export const LIST_PAGE_MAX = 500;\n\nexport interface ListQuery {\n limit?: number;\n cursor?: string;\n project?: string;\n type?: string;\n status?: string;\n q?: string;\n}\n\ntype QueryParams = Record<string, string | string[] | undefined> | undefined;\n\nfunction firstParam(params: QueryParams, name: string): string | undefined {\n const raw = params?.[name];\n const value = Array.isArray(raw) ? raw[0] : raw;\n if (typeof value !== \"string\") return undefined;\n const trimmed = value.trim();\n return trimmed.length > 0 ? trimmed : undefined;\n}\n\nexport function parseListQuery(params: QueryParams): ListQuery {\n const rawLimit = firstParam(params, \"limit\");\n const parsedLimit = rawLimit === undefined ? Number.NaN : Number(rawLimit);\n return {\n limit:\n Number.isFinite(parsedLimit) && parsedLimit >= 1\n ? Math.min(Math.floor(parsedLimit), LIST_PAGE_MAX)\n : undefined,\n cursor: firstParam(params, \"cursor\"),\n project: firstParam(params, \"project\"),\n type: firstParam(params, \"type\"),\n status: firstParam(params, \"status\"),\n q: firstParam(params, \"q\"),\n };\n}\n\ninterface CursorPosition {\n key?: string;\n id?: string;\n offset?: number;\n}\n\nexport function encodeCursor(position: CursorPosition): string {\n return Buffer.from(JSON.stringify(position), \"utf-8\").toString(\"base64url\");\n}\n\nexport function decodeCursor(cursor: string | undefined): CursorPosition | null {\n if (!cursor) return null;\n try {\n const parsed = JSON.parse(Buffer.from(cursor, \"base64url\").toString(\"utf-8\"));\n if (!parsed || typeof parsed !== \"object\") return null;\n const position: CursorPosition = {};\n if (typeof parsed.key === \"string\") position.key = parsed.key;\n if (typeof parsed.id === \"string\") position.id = parsed.id;\n if (Number.isInteger(parsed.offset) && parsed.offset >= 0) position.offset = parsed.offset;\n return position;\n } catch {\n return null;\n }\n}\n\nfunction compareDesc(aKey: string, aId: string, bKey: string, bId: string): number {\n if (aKey !== bKey) return aKey < bKey ? 1 : -1;\n if (aId === bId) return 0;\n return aId < bId ? 1 : -1;\n}\n\nexport function sortByKeyDesc<T>(items: T[], keyOf: (item: T) => string, idOf: (item: T) => string): T[] {\n return items.sort((a, b) => compareDesc(keyOf(a), idOf(a), keyOf(b), idOf(b)));\n}\n\nexport function pageAfterCursor<T>(\n sortedDesc: T[],\n keyOf: (item: T) => string,\n idOf: (item: T) => string,\n cursor: string | undefined,\n limit: number,\n): { page: T[]; nextCursor: string | null } {\n const position = decodeCursor(cursor);\n let start = 0;\n if (position?.key !== undefined && position.id !== undefined) {\n const { key, id } = position;\n start = sortedDesc.findIndex((item) => compareDesc(keyOf(item), idOf(item), key, id) > 0);\n if (start < 0) start = sortedDesc.length;\n }\n const page = sortedDesc.slice(start, start + limit);\n const last = page[page.length - 1];\n const nextCursor =\n last !== undefined && start + limit < sortedDesc.length\n ? encodeCursor({ key: keyOf(last), id: idOf(last) })\n : null;\n return { page, nextCursor };\n}\n\nexport function pageByOffset<T>(\n ordered: T[],\n cursor: string | undefined,\n limit: number,\n): { page: T[]; nextCursor: string | null } {\n const start = decodeCursor(cursor)?.offset ?? 0;\n const page = ordered.slice(start, start + limit);\n const nextCursor =\n start + limit < ordered.length ? encodeCursor({ offset: start + limit }) : null;\n return { page, nextCursor };\n}\n\nexport function matchesText(q: string | undefined, ...fields: Array<string | undefined | null>): boolean {\n if (!q) return true;\n const needle = q.toLowerCase();\n return fields.some((field) => typeof field === \"string\" && field.toLowerCase().includes(needle));\n}\n","import { decodeCursor } from \"../state/list-query.js\";\n\nexport function parseSessionArguments(args: Record<string, unknown>): Record<string, string> {\n const limit = args.limit === undefined ? 20 : args.limit;\n if (typeof limit !== \"number\" || !Number.isInteger(limit) || limit < 1) {\n throw new Error(\"limit must be a positive integer\");\n }\n const query: Record<string, string> = { limit: String(Math.min(limit, 100)) };\n for (const field of [\"project\", \"agentId\", \"cursor\"] as const) {\n if (args[field] === undefined) continue;\n if (field === \"cursor\" && typeof args[field] === \"string\" && !args[field].trim()) continue;\n if (typeof args[field] !== \"string\" || !args[field].trim()) {\n throw new Error(`${field} must be a non-empty string`);\n }\n query[field] = args[field].trim();\n }\n if (query.cursor) {\n const position = decodeCursor(query.cursor);\n if (!position || typeof position.key !== \"string\" || typeof position.id !== \"string\") {\n throw new Error(\"cursor must be a session continuation token\");\n }\n }\n return query;\n}\n","export const VERSION = \"0.9.31\";\nexport const III_PINNED_VERSION = \"0.25.0\";\n","import { createRequire } from \"node:module\";\n\nconst cjkRequire = createRequire(import.meta.url);\n\nconst CJK_RE = /[\\p{Script=Han}\\p{Script=Hiragana}\\p{Script=Katakana}\\p{Script=Hangul}]/u;\nconst HAN_RE = /\\p{Script=Han}/u;\nconst KANA_RE = /[\\p{Script=Hiragana}\\p{Script=Katakana}]/u;\nconst HANGUL_RE = /\\p{Script=Hangul}/u;\nconst CJK_RUN_RE = /[\\p{Script=Han}\\p{Script=Hiragana}\\p{Script=Katakana}\\p{Script=Hangul}]+/gu;\nconst HANGUL_BLOCK_RE = /[가-]+/g;\n\ntype Script = \"han\" | \"kana\" | \"hangul\" | \"other\";\n\nconst hintShown = new Set<string>();\n\nexport function hasCjk(text: string): boolean {\n return CJK_RE.test(text);\n}\n\nexport function detectScript(text: string): Script {\n if (HAN_RE.test(text)) return \"han\";\n if (KANA_RE.test(text)) return \"kana\";\n if (HANGUL_RE.test(text)) return \"hangul\";\n return \"other\";\n}\n\nfunction showHintOnce(key: string, message: string): void {\n if (hintShown.has(key)) return;\n hintShown.add(key);\n if (typeof process !== \"undefined\" && process.stderr?.write) {\n process.stderr.write(`agentmemory: ${message}\\n`);\n }\n}\n\ninterface JiebaInstance {\n cut(text: string, hmm?: boolean): string[];\n}\n\nlet jiebaInstance: JiebaInstance | null = null;\nlet jiebaLoaded = false;\n\nfunction getJieba(): JiebaInstance | null {\n if (jiebaLoaded) return jiebaInstance;\n jiebaLoaded = true;\n try {\n const mod = cjkRequire(\"@node-rs/jieba\") as {\n Jieba: {\n new (): JiebaInstance;\n withDict(dict: Uint8Array): JiebaInstance;\n };\n };\n try {\n const dictMod = cjkRequire(\"@node-rs/jieba/dict\") as { dict: Uint8Array };\n jiebaInstance = mod.Jieba.withDict(dictMod.dict);\n } catch {\n jiebaInstance = new mod.Jieba();\n }\n return jiebaInstance;\n } catch {\n showHintOnce(\n \"jieba\",\n \"install @node-rs/jieba to improve Chinese search; falling back to whole-string tokenization\",\n );\n return null;\n }\n}\n\ninterface JaSegmenter {\n segment(text: string): string[];\n}\n\nlet jaSegmenterInstance: JaSegmenter | null = null;\nlet jaSegmenterLoaded = false;\n\nfunction getJaSegmenter(): JaSegmenter | null {\n if (jaSegmenterLoaded) return jaSegmenterInstance;\n jaSegmenterLoaded = true;\n try {\n const Ctor = cjkRequire(\"tiny-segmenter\") as new () => JaSegmenter;\n jaSegmenterInstance = new Ctor();\n return jaSegmenterInstance;\n } catch {\n showHintOnce(\n \"tiny-segmenter\",\n \"install tiny-segmenter to improve Japanese search; falling back to whole-string tokenization\",\n );\n return null;\n }\n}\n\nfunction cleanTokens(tokens: string[]): string[] {\n const out: string[] = [];\n for (const t of tokens) {\n const trimmed = t.trim();\n if (trimmed) out.push(trimmed);\n }\n return out;\n}\n\nfunction segmentHan(text: string): string[] {\n const j = getJieba();\n if (!j) return [text];\n try {\n return cleanTokens(j.cut(text, true));\n } catch {\n return [text];\n }\n}\n\nfunction segmentKana(text: string): string[] {\n const s = getJaSegmenter();\n if (!s) return [text];\n try {\n return cleanTokens(s.segment(text));\n } catch {\n return [text];\n }\n}\n\nfunction segmentHangul(text: string): string[] {\n const out: string[] = [];\n for (const m of text.matchAll(HANGUL_BLOCK_RE)) {\n if (m[0]) out.push(m[0]);\n }\n return out;\n}\n\nexport function segmentCjk(text: string): string[] {\n if (!hasCjk(text)) return [text];\n\n const out: string[] = [];\n let cursor = 0;\n\n for (const m of text.matchAll(CJK_RUN_RE)) {\n const start = m.index ?? 0;\n const run = m[0];\n const end = start + run.length;\n\n if (start > cursor) {\n const piece = text.slice(cursor, start).trim();\n if (piece) out.push(piece);\n }\n\n if (HANGUL_RE.test(run)) {\n out.push(...segmentHangul(run));\n } else if (KANA_RE.test(run)) {\n out.push(...segmentKana(run));\n } else {\n out.push(...segmentHan(run));\n }\n\n cursor = end;\n }\n\n if (cursor < text.length) {\n const trailing = text.slice(cursor).trim();\n if (trailing) out.push(trailing);\n }\n\n return out;\n}\n\nexport function __resetCjkSegmenterStateForTests(): void {\n hintShown.clear();\n jiebaInstance = null;\n jiebaLoaded = false;\n jaSegmenterInstance = null;\n jaSegmenterLoaded = false;\n}\n","import { createHash } from \"node:crypto\";\nimport { hasCjk, segmentCjk } from \"./cjk-segmenter.js\";\n\nexport const KV = {\n sessions: \"mem:sessions\",\n observations: (sessionId: string) => `mem:obs:${sessionId}`,\n memories: \"mem:memories\",\n summaries: \"mem:summaries\",\n summaryRepairs: \"mem:summary:repairs\",\n config: \"mem:config\",\n metrics: \"mem:metrics\",\n health: \"mem:health\",\n embeddings: (obsId: string) => `mem:emb:${obsId}`,\n bm25Index: \"mem:index:bm25\",\n vectorPendingLog: \"mem:index:vec-pending\",\n relations: \"mem:relations\",\n profiles: \"mem:profiles\",\n claudeBridge: \"mem:claude-bridge\",\n graphNodes: \"mem:graph:nodes\",\n graphControl: \"mem:graph:control\",\n graphEdges: \"mem:graph:edges\",\n graphSnapshot: \"mem:graph:snapshot\",\n graphNameIndex: \"mem:graph:name-index\",\n graphEdgeKey: \"mem:graph:edge-key\",\n graphNodeDegree: \"mem:graph:node-degree\",\n graphMembership: \"mem:graph:membership\",\n graphRetrievalControl: \"mem:graph:retrieval-control\",\n graphRetrievalData: \"mem:graph:retrieval-data\",\n graphIndexMigration: \"mem:graph:index-migration\",\n graphIndexManifest: \"mem:graph:index-manifest\",\n semantic: \"mem:semantic\",\n procedural: \"mem:procedural\",\n teamShared: (teamId: string) => `mem:team:${teamId}:shared`,\n teamUsers: (teamId: string, userId: string) =>\n `mem:team:${teamId}:users:${userId}`,\n teamProfile: (teamId: string) => `mem:team:${teamId}:profile`,\n audit: \"mem:audit\",\n auditMonth: (month: string) => `mem:audit:${month}`,\n auditMonths: \"mem:audit:months\",\n actions: \"mem:actions\",\n actionEdges: \"mem:action-edges\",\n leases: \"mem:leases\",\n routines: \"mem:routines\",\n routineRuns: \"mem:routine-runs\",\n signals: \"mem:signals\",\n checkpoints: \"mem:checkpoints\",\n mesh: \"mem:mesh\",\n sketches: \"mem:sketches\",\n facets: \"mem:facets\",\n sentinels: \"mem:sentinels\",\n crystals: \"mem:crystals\",\n lessons: \"mem:lessons\",\n insights: \"mem:insights\",\n graphEdgeHistory: \"mem:graph:edge-history\",\n enrichedChunks: (sessionId: string) => `mem:enriched:${sessionId}`,\n latentEmbeddings: (obsId: string) => `mem:latent:${obsId}`,\n retentionScores: \"mem:retention\",\n accessLog: \"mem:access\",\n imageRefs: \"mem:image-refs\",\n imageEmbeddings: \"mem:image-embeddings\",\n slots: \"mem:slots\",\n globalSlots: \"mem:slots:global\",\n state: \"mem:state\",\n commits: \"mem:commits\",\n recentSearches: \"mem:recent-searches\",\n projectSessionsIndex: \"mem:idx:project-sessions\",\n obsSessionIndex: (shard: number) => `mem:idx:obs:${shard}`,\n captureInbox: \"mem:capture:inbox\",\n captureEvents: (shard: string) => `mem:capture:events:${shard}`,\n capturePrompts: \"mem:capture:prompts\",\n} as const;\n\nexport function generateId(prefix: string): string {\n const ts = Date.now().toString(36);\n const rand = crypto.randomUUID().replace(/-/g, \"\").slice(0, 12);\n return `${prefix}_${ts}_${rand}`;\n}\n\nexport function fingerprintId(prefix: string, content: string): string {\n const hash = createHash(\"sha256\").update(content).digest(\"hex\");\n return `${prefix}_${hash.slice(0, 16)}`;\n}\n\n// CJK/Japanese/Thai text carries no inter-word whitespace, so a plain\n// split(/\\s+/) collapses a whole sentence into one token and the two token\n// sets never overlap — dedup silently stops working. When either input\n// contains CJK we segment the CJK runs (word tokens via the shared\n// segmenter, plus character bigram shingles so near-identical strings still\n// overlap even when the optional segmenter deps fall back to whole-string).\n// ASCII text keeps the original word-level behavior.\nfunction jaccardTokens(text: string): Set<string> {\n if (!hasCjk(text)) {\n return new Set(text.split(/\\s+/).filter((t) => t.length > 2));\n }\n const tokens = new Set<string>();\n for (const raw of text.split(/\\s+/)) {\n if (!raw) continue;\n if (hasCjk(raw)) {\n for (const seg of segmentCjk(raw)) {\n if (seg) tokens.add(seg);\n }\n // Character bigram shingles keep the CJK signal even when the\n // segmenter returns the whole run: overlapping bigrams make\n // near-identical strings match while unrelated ones (\"北京\" vs\n // \"上海\") share none. Never drop short CJK tokens.\n const chars = Array.from(raw);\n if (chars.length === 1) {\n tokens.add(chars[0]);\n } else {\n for (let i = 0; i < chars.length - 1; i++) {\n tokens.add(chars[i] + chars[i + 1]);\n }\n }\n } else if (raw.length > 2) {\n tokens.add(raw);\n }\n }\n return tokens;\n}\n\nexport function jaccardSimilarity(a: string, b: string): number {\n const na = a.normalize(\"NFC\");\n const nb = b.normalize(\"NFC\");\n const setA = jaccardTokens(na);\n const setB = jaccardTokens(nb);\n // An empty token set carries no signal for the overlap metric. Very short\n // ASCII text (\"AI\", \"a b\", \"go\") tokenizes to nothing because words <=2\n // chars are dropped. Fall back to exact normalized equality so re-saving\n // the identical short memory still supersedes, while unrelated short\n // strings (\"AI\" vs \"ML\") correctly score 0. Returning 1 unconditionally\n // here (as an \"both empty\" shortcut once did) would let any short memory\n // falsely supersede another, silently marking a real memory not-latest.\n if (setA.size === 0 || setB.size === 0) {\n return na.trim().replace(/\\s+/g, \" \") === nb.trim().replace(/\\s+/g, \" \")\n ? 1\n : 0;\n }\n let intersection = 0;\n for (const word of setA) {\n if (setB.has(word)) intersection++;\n }\n return intersection / (setA.size + setB.size - intersection);\n}\n","type Fields = Record<string, unknown> | undefined;\n\nfunction fmt(level: string, msg: string, fields: Fields): string {\n if (!fields || Object.keys(fields).length === 0) {\n return `[agentmemory] ${level} ${msg}`;\n }\n try {\n return `[agentmemory] ${level} ${msg} ${JSON.stringify(fields)}`;\n } catch {\n // Fields contained a circular reference or a BigInt — fall back\n // to the plain message so a log line never throws.\n return `[agentmemory] ${level} ${msg}`;\n }\n}\n\nfunction emit(level: string, msg: string, fields: Fields): void {\n try {\n const line = fmt(level, msg, fields) + \"\\n\";\n if (level === \"error\" || process.env[\"AGENTMEMORY_MCP_STDIO__\"] === \"1\") {\n process.stderr.write(line);\n }\n else process.stdout.write(line);\n } catch {\n // stdout or stderr is unavailable in some test/worker contexts — swallow\n // so no log line can ever crash a handler.\n }\n}\n\nexport const logger = {\n info(msg: string, fields?: Fields): void {\n emit(\"info\", msg, fields);\n },\n warn(msg: string, fields?: Fields): void {\n emit(\"warn\", msg, fields);\n },\n error(msg: string, fields?: Fields): void {\n emit(\"error\", msg, fields);\n },\n};\n\n// ---------- boot log ----------\n//\n// `bootLog` is for the one-shot status lines that every register-*\n// function used to dump via `console.log` during engine startup. On a\n// fresh install that's ~25 lines of `[agentmemory] X enabled` noise\n// before the user can see a prompt. In quiet mode (default), each\n// line is captured into a buffer and discarded; the CLI surfaces a\n// single compressed summary instead. In verbose mode (set by\n// `--verbose` or `AGENTMEMORY_VERBOSE=1`) the lines pass straight\n// through to stderr exactly like the old console.log calls.\n\nlet bootVerbose =\n process.env[\"AGENTMEMORY_VERBOSE\"] === \"1\" ||\n process.env[\"AGENTMEMORY_VERBOSE\"] === \"true\";\n\nconst bootBuffer: string[] = [];\n\nexport function setBootVerbose(enabled: boolean): void {\n bootVerbose = enabled;\n}\n\nexport function isBootVerbose(): boolean {\n return bootVerbose;\n}\n\nexport function bootLog(msg: string): void {\n if (bootVerbose) {\n try {\n process.stderr.write(`[agentmemory] ${msg}\\n`);\n } catch {\n // stderr unavailable — drop.\n }\n return;\n }\n if (bootBuffer.length < 500) bootBuffer.push(msg);\n}\n\nexport function bootWarn(msg: string): void {\n // Warnings always surface; they're rare and the user needs to see\n // them even when the rest of the boot log is suppressed.\n try {\n process.stderr.write(`[agentmemory] warn ${msg}\\n`);\n } catch {}\n}\n\nexport function getBootBuffer(): readonly string[] {\n return bootBuffer;\n}\n","import { AsyncLocalStorage } from \"node:async_hooks\";\n\ntype LockContext = { active: boolean; after: Map<string | symbol, () => Promise<void>> };\ntype LockFrame = { key: string; context: LockContext; parent?: LockFrame };\nconst held = new AsyncLocalStorage<LockFrame>();\n\nfunction heldContext(key: string): LockContext | undefined {\n for (let frame = held.getStore(); frame; frame = frame.parent) {\n if (frame.key === key) return frame.context;\n }\n}\n\nexport function isKeyedLockHeld(key: string): boolean {\n return heldContext(key)?.active ?? false;\n}\n\nexport function afterKeyedLock(key: string, fn: () => Promise<void>, taskKey?: string): boolean {\n const context = heldContext(key);\n if (!context?.active) return false;\n context.after.set(taskKey ?? Symbol(), fn);\n return true;\n}\n\nconst locks = new Map<string, Promise<void>>();\n\nexport function withKeyedLock<T>(\n key: string,\n fn: () => Promise<T>,\n): Promise<T> {\n const prev = locks.get(key) ?? Promise.resolve();\n const context: LockContext = { active: true, after: new Map() };\n const run = () => held.run({ key, context, parent: held.getStore() }, fn);\n const next = prev.then(run, run);\n const cleanup = next.then(\n () => { context.active = false; },\n () => { context.active = false; },\n );\n locks.set(key, cleanup);\n cleanup.then(() => {\n if (locks.get(key) === cleanup) locks.delete(key);\n });\n const flush = async () => {\n await cleanup;\n const errors: unknown[] = [];\n for (const notify of context.after.values()) {\n try { await notify(); } catch (error) { errors.push(error); }\n }\n return errors;\n };\n return next.then(\n async (value) => {\n const errors = await flush();\n if (errors.length === 1) throw errors[0];\n if (errors.length > 1) throw new AggregateError(errors, \"Deferred lock callbacks failed\");\n return value;\n },\n async (error) => { await flush(); throw error; },\n );\n}\n\nexport function withKeyedLocks<T>(keys: string[], fn: () => Promise<T>): Promise<T> {\n const ordered = [...new Set(keys)].sort();\n const acquire = (index: number): Promise<T> => index === ordered.length\n ? fn()\n : withKeyedLock(ordered[index], () => acquire(index + 1));\n return acquire(0);\n}\n","import { stat, readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport type { AuditEntry, AuditMigrationState, AuditQueryResult } from \"../types.js\";\nimport { KV, generateId } from \"../state/schema.js\";\nimport type { StateKV } from \"../state/kv.js\";\nimport { logger } from \"../logger.js\";\nimport { withKeyedLock } from \"../state/keyed-mutex.js\";\nimport { getEnvVar, getAuditMigrateMaxBytes } from \"../config.js\";\nimport { runtimeConfigPath } from \"../cli/engine-launch.js\";\nimport { ENGINE_DEFAULT_SAVE_INTERVAL_MS, engineSaveIntervalMs, engineStateConfigPaths } from \"../cli/engine-config.js\";\n\nconst AUDIT_MIGRATE_FUNCTION_ID = \"mem::audit-migrate\";\nconst AUDIT_MIGRATION_COPY_CONCURRENCY = 32;\nconst AUDIT_MIGRATION_DELETE_CONCURRENCY = 8;\nconst AUDIT_MONTHS_LOCK = KV.auditMonths;\nconst AUDIT_MIGRATION_LOCK = \"mem:audit:migration-lock\";\nexport const AUDIT_MIGRATION_STATE_KEY = \"migration\";\nconst AUDIT_MIGRATION_DELETE_WAIT_INTERVALS = 2;\nconst STATE_STORE_DIR_NAME = \"state_store.db\";\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction encodeStateIndexSegment(index: string): string {\n let out = \"\";\n for (const byte of Buffer.from(index, \"utf8\")) {\n const ch = String.fromCharCode(byte);\n out += /^[A-Za-z0-9\\-_.]$/.test(ch)\n ? ch\n : `%${byte.toString(16).toUpperCase().padStart(2, \"0\")}`;\n }\n return out;\n}\n\nfunction legacyAuditStateFilePath(dataDir: string): string {\n return join(dataDir, STATE_STORE_DIR_NAME, `${encodeStateIndexSegment(KV.audit)}.bin`);\n}\n\nexport type LegacyAuditProbe =\n | { safe: true; empty: true }\n | { safe: true; empty: false; sizeBytes: number }\n | { safe: false; reason: \"too-large\"; sizeBytes: number }\n | { safe: false; reason: \"unreadable\" };\n\nexport async function probeLegacyAuditFile(): Promise<LegacyAuditProbe> {\n const dataDir = getEnvVar(\"AGENTMEMORY_DATA_DIR\");\n if (!dataDir) return { safe: false, reason: \"unreadable\" };\n\n const storeDir = join(dataDir, STATE_STORE_DIR_NAME);\n try {\n await stat(storeDir);\n } catch {\n return { safe: false, reason: \"unreadable\" };\n }\n\n let fileStat;\n try {\n fileStat = await stat(legacyAuditStateFilePath(dataDir));\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") {\n return { safe: true, empty: true };\n }\n return { safe: false, reason: \"unreadable\" };\n }\n\n if (fileStat.size > getAuditMigrateMaxBytes()) {\n return { safe: false, reason: \"too-large\", sizeBytes: fileStat.size };\n }\n return { safe: true, empty: false, sizeBytes: fileStat.size };\n}\n\nexport async function probeLegacyAuditScope(kv: StateKV): Promise<LegacyAuditProbe> {\n let rows: AuditEntry[];\n try {\n rows = await kv.list<AuditEntry>(KV.audit);\n } catch {\n return { safe: false, reason: \"unreadable\" };\n }\n if (!Array.isArray(rows) || rows.length === 0) return { safe: true, empty: true };\n const sizeBytes = Buffer.byteLength(JSON.stringify(rows), \"utf8\");\n if (sizeBytes > getAuditMigrateMaxBytes()) {\n return { safe: false, reason: \"too-large\", sizeBytes };\n }\n return { safe: true, empty: false, sizeBytes };\n}\n\nasync function auditMigrationDeleteDelayMs(): Promise<number> {\n const dataDir = getEnvVar(\"AGENTMEMORY_DATA_DIR\");\n if (!dataDir) return ENGINE_DEFAULT_SAVE_INTERVAL_MS * AUDIT_MIGRATION_DELETE_WAIT_INTERVALS;\n const configTexts: string[] = [];\n for (const path of engineStateConfigPaths(dataDir, runtimeConfigPath(dataDir))) {\n try {\n configTexts.push(await readFile(path, \"utf-8\"));\n } catch {}\n }\n return engineSaveIntervalMs(configTexts) * AUDIT_MIGRATION_DELETE_WAIT_INTERVALS;\n}\n\nasync function readAuditMigrationState(kv: StateKV): Promise<AuditMigrationState | null> {\n return kv.get<AuditMigrationState>(KV.auditMonths, AUDIT_MIGRATION_STATE_KEY);\n}\n\nasync function writeAuditMigrationState(\n kv: StateKV,\n state: AuditMigrationState,\n): Promise<void> {\n await kv.set(KV.auditMonths, AUDIT_MIGRATION_STATE_KEY, state);\n}\n\nexport function auditMonthOf(timestamp: string): string {\n const parsed = new Date(timestamp);\n const iso = Number.isNaN(parsed.getTime())\n ? new Date().toISOString()\n : parsed.toISOString();\n return iso.slice(0, 7);\n}\n\nfunction monthStartMs(month: string): number {\n const [year, mon] = month.split(\"-\").map(Number);\n return Date.UTC(year, mon - 1, 1);\n}\n\nfunction monthEndMs(month: string): number {\n const [year, mon] = month.split(\"-\").map(Number);\n return Date.UTC(year, mon, 1) - 1;\n}\n\nfunction monthNMonthsAgo(count: number): string {\n const now = new Date();\n const cutoff = new Date(\n Date.UTC(now.getUTCFullYear(), now.getUTCMonth() - count, 1),\n );\n return cutoff.toISOString().slice(0, 7);\n}\n\nasync function readAuditMonthIndex(kv: StateKV): Promise<string[]> {\n const index = await kv.get<{ months: string[] }>(KV.auditMonths, \"index\");\n return index?.months ?? [];\n}\n\nasync function markAuditMonth(kv: StateKV, month: string): Promise<void> {\n await withKeyedLock(AUDIT_MONTHS_LOCK, async () => {\n const months = await readAuditMonthIndex(kv);\n if (months.includes(month)) return;\n const next = [...months, month].sort();\n await kv.set(KV.auditMonths, \"index\", { months: next });\n });\n}\n\nexport async function listAuditMonthsDesc(kv: StateKV): Promise<string[]> {\n const months = await readAuditMonthIndex(kv);\n return [...months].sort().reverse();\n}\n\nlet auditRecordedListener: ((entry: AuditEntry) => void) | null = null;\n\nexport function setAuditRecordedListener(listener: ((entry: AuditEntry) => void) | null): void {\n auditRecordedListener = listener;\n}\n\nexport async function recordAudit(\n kv: StateKV,\n operation: AuditEntry[\"operation\"],\n functionId: string,\n targetIds: string[],\n details: Record<string, unknown> = {},\n qualityScore?: number,\n userId?: string,\n): Promise<AuditEntry> {\n const entry: AuditEntry = {\n id: generateId(\"aud\"),\n timestamp: new Date().toISOString(),\n operation,\n userId,\n functionId,\n targetIds,\n details,\n qualityScore,\n };\n const month = auditMonthOf(entry.timestamp);\n await kv.set(KV.auditMonth(month), entry.id, entry);\n await markAuditMonth(kv, month);\n try {\n auditRecordedListener?.(entry);\n } catch {}\n return entry;\n}\n\nexport async function safeAudit(\n kv: StateKV,\n operation: AuditEntry[\"operation\"],\n functionId: string,\n targetIds: string[],\n details: Record<string, unknown> = {},\n qualityScore?: number,\n userId?: string,\n): Promise<void> {\n try {\n await recordAudit(kv, operation, functionId, targetIds, details, qualityScore, userId);\n } catch (err) {\n try {\n logger.warn(\"audit write failed\", {\n functionId,\n operation,\n targetIds,\n error: err instanceof Error ? err.message : String(err),\n });\n } catch {}\n }\n}\n\nexport async function queryAudit(\n kv: StateKV,\n filter?: {\n operation?: AuditEntry[\"operation\"];\n dateFrom?: string;\n dateTo?: string;\n query?: string;\n limit?: number;\n },\n): Promise<AuditQueryResult> {\n const limit = filter?.limit || 100;\n\n let fromMs: number | undefined;\n if (filter?.dateFrom) {\n fromMs = new Date(filter.dateFrom).getTime();\n if (Number.isNaN(fromMs)) {\n throw new Error(`Invalid dateFrom: ${filter.dateFrom}`);\n }\n }\n const query = filter?.query?.trim().toLowerCase();\n\n let toMs: number | undefined;\n if (filter?.dateTo) {\n toMs = new Date(filter.dateTo).getTime();\n if (Number.isNaN(toMs)) {\n throw new Error(`Invalid dateTo: ${filter.dateTo}`);\n }\n }\n\n const matches = (entry: AuditEntry): boolean => {\n if (filter?.operation && entry.operation !== filter.operation) return false;\n const t = new Date(entry.timestamp).getTime();\n if (fromMs !== undefined && t < fromMs) return false;\n if (toMs !== undefined && t > toMs) return false;\n if (\n query &&\n ![entry.functionId, ...(entry.targetIds || [])].some((v) =>\n String(v || \"\").toLowerCase().includes(query),\n )\n ) {\n return false;\n }\n return true;\n };\n\n const seenIds = new Set<string>();\n const collected: AuditEntry[] = [];\n const addIfNew = (row: AuditEntry): void => {\n if (seenIds.has(row.id)) return;\n seenIds.add(row.id);\n if (matches(row)) collected.push(row);\n };\n\n const months = await listAuditMonthsDesc(kv);\n for (const month of months) {\n if (collected.length >= limit) break;\n if (toMs !== undefined && monthStartMs(month) > toMs) continue;\n if (fromMs !== undefined && monthEndMs(month) < fromMs) break;\n const rows = await kv.list<AuditEntry>(KV.auditMonth(month));\n for (const row of rows) addIfNew(row);\n }\n\n let legacyFrozen = false;\n let legacyFrozenBytes: number | undefined;\n\n if (collected.length < limit) {\n const migrationState = await readAuditMigrationState(kv);\n if (migrationState?.safeToListLegacy) {\n const legacyRows = await kv.list<AuditEntry>(KV.audit);\n for (const row of legacyRows) addIfNew(row);\n } else if (migrationState?.status === \"too-large\" || migrationState?.status === \"unreadable\") {\n legacyFrozen = true;\n legacyFrozenBytes = migrationState.legacySizeBytes;\n }\n }\n\n collected.sort(\n (a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime(),\n );\n return {\n entries: collected.slice(0, limit),\n legacyFrozen,\n legacyFrozenBytes,\n };\n}\n\nasync function inParallel<T>(\n items: T[],\n concurrency: number,\n work: (item: T) => Promise<void>,\n): Promise<void> {\n let next = 0;\n const runners = Array.from({ length: Math.min(concurrency, items.length) }, async () => {\n while (next < items.length) {\n const item = items[next++];\n await work(item);\n }\n });\n await Promise.all(runners);\n}\n\nexport interface AuditMigrationDeps {\n probe?: () => Promise<LegacyAuditProbe>;\n deleteDelayMs?: () => Promise<number>;\n}\n\nexport async function startAuditMigration(\n kv: StateKV,\n deps: AuditMigrationDeps = {},\n): Promise<void> {\n const onRedis = kv.backend === \"redis\";\n const runProbe =\n deps.probe ?? (onRedis ? () => probeLegacyAuditScope(kv) : probeLegacyAuditFile);\n const runDeleteDelayMs =\n deps.deleteDelayMs ?? (onRedis ? async () => 0 : auditMigrationDeleteDelayMs);\n\n const marker = await readAuditMigrationState(kv);\n if (marker?.status === \"done\") return;\n\n const probe = await runProbe();\n\n if (probe.safe && probe.empty) {\n await writeAuditMigrationState(kv, {\n status: \"done\",\n safeToListLegacy: true,\n legacySizeBytes: 0,\n migrated: marker?.migrated ?? 0,\n purged: marker?.purged ?? 0,\n summaryWritten: marker?.summaryWritten ?? true,\n checkedAt: new Date().toISOString(),\n });\n return;\n }\n\n if (!probe.safe) {\n await writeAuditMigrationState(kv, {\n status: probe.reason,\n safeToListLegacy: false,\n legacySizeBytes: probe.reason === \"too-large\" ? probe.sizeBytes : marker?.legacySizeBytes,\n migrated: marker?.migrated ?? 0,\n purged: marker?.purged ?? 0,\n summaryWritten: marker?.summaryWritten ?? false,\n checkedAt: new Date().toISOString(),\n });\n logger.warn(\"legacy audit log left frozen; migration only reads it locally and under a size cap\", {\n reason: probe.reason,\n sizeBytes: probe.reason === \"too-large\" ? probe.sizeBytes : undefined,\n });\n return;\n }\n\n let migrated = marker?.migrated ?? 0;\n let purged = marker?.purged ?? 0;\n\n if (marker?.status !== \"copied\") {\n let legacy: AuditEntry[];\n try {\n legacy = await kv.list<AuditEntry>(KV.audit);\n } catch (err) {\n logger.warn(\"audit migration could not read the legacy audit log\", {\n error: err instanceof Error ? err.message : String(err),\n });\n return;\n }\n if (legacy.length === 0) {\n await writeAuditMigrationState(kv, {\n status: \"done\",\n safeToListLegacy: true,\n legacySizeBytes: 0,\n migrated,\n purged,\n summaryWritten: marker?.summaryWritten ?? true,\n checkedAt: new Date().toISOString(),\n });\n return;\n }\n\n const seen = new Set<string>();\n const deduped = legacy.filter((entry) => {\n if (seen.has(entry.id)) return false;\n seen.add(entry.id);\n return true;\n });\n const kept = deduped.filter((entry) => entry.operation !== \"index_persist\");\n purged = deduped.length - kept.length;\n migrated = kept.length;\n\n try {\n const months = new Set<string>();\n await inParallel(kept, AUDIT_MIGRATION_COPY_CONCURRENCY, async (entry) => {\n const month = auditMonthOf(entry.timestamp);\n await kv.set(KV.auditMonth(month), entry.id, entry);\n months.add(month);\n });\n for (const month of months) await markAuditMonth(kv, month);\n } catch (err) {\n logger.warn(\n \"audit migration stopped before finishing the copy pass; it resumes on the next boot\",\n { error: err instanceof Error ? err.message : String(err) },\n );\n return;\n }\n\n await writeAuditMigrationState(kv, {\n status: \"copied\",\n safeToListLegacy: false,\n legacySizeBytes: probe.sizeBytes,\n migrated,\n purged,\n summaryWritten: false,\n checkedAt: new Date().toISOString(),\n });\n }\n\n await sleep(await runDeleteDelayMs());\n\n let remaining: AuditEntry[];\n try {\n remaining = await kv.list<AuditEntry>(KV.audit);\n } catch (err) {\n logger.warn(\n \"audit migration could not re-read the legacy audit log before deleting; it resumes on the next boot\",\n { error: err instanceof Error ? err.message : String(err) },\n );\n return;\n }\n\n try {\n await inParallel(\n remaining.slice().reverse(),\n AUDIT_MIGRATION_DELETE_CONCURRENCY,\n async (entry) => {\n await kv.delete(KV.audit, entry.id);\n },\n );\n } catch (err) {\n logger.warn(\n \"audit migration stopped before finishing the delete pass; it resumes on the next boot\",\n { error: err instanceof Error ? err.message : String(err) },\n );\n return;\n }\n\n await withKeyedLock(AUDIT_MIGRATION_LOCK, async () => {\n const current = await readAuditMigrationState(kv);\n if (current?.summaryWritten) return;\n if (purged > 0) {\n await safeAudit(kv, \"audit_migrate\", AUDIT_MIGRATE_FUNCTION_ID, [], {\n reason: \"index_persist rows predate opt-in auditing\",\n purged,\n migrated,\n });\n }\n await writeAuditMigrationState(kv, {\n status: \"done\",\n safeToListLegacy: true,\n legacySizeBytes: 0,\n migrated,\n purged,\n summaryWritten: true,\n checkedAt: new Date().toISOString(),\n });\n });\n\n logger.info(\"audit log moved to monthly scopes\", { migrated, purged });\n}\n\nexport async function runAuditRetentionSweep(\n kv: StateKV,\n retentionMonths: number,\n): Promise<{ droppedMonths: string[]; droppedRows: number }> {\n if (!Number.isFinite(retentionMonths) || retentionMonths <= 0) {\n return { droppedMonths: [], droppedRows: 0 };\n }\n\n const months = await readAuditMonthIndex(kv);\n const cutoff = monthNMonthsAgo(retentionMonths);\n const toDrop = months.filter((month) => month < cutoff).sort();\n if (toDrop.length === 0) {\n return { droppedMonths: [], droppedRows: 0 };\n }\n\n let droppedRows = 0;\n for (const month of toDrop) {\n const rows = await kv.list<AuditEntry>(KV.auditMonth(month));\n for (const row of rows.slice().reverse()) {\n await kv.delete(KV.auditMonth(month), row.id);\n droppedRows++;\n }\n }\n\n await withKeyedLock(AUDIT_MONTHS_LOCK, async () => {\n const current = await readAuditMonthIndex(kv);\n const remaining = current.filter((month) => !toDrop.includes(month)).sort();\n await kv.set(KV.auditMonths, \"index\", { months: remaining });\n });\n\n return { droppedMonths: toDrop, droppedRows };\n}\n","import {\n chmodSync,\n closeSync,\n linkSync,\n mkdirSync,\n openSync,\n readFileSync,\n renameSync,\n unlinkSync,\n writeSync,\n} from \"node:fs\";\nimport { randomBytes } from \"node:crypto\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\nconst SECRET_KEY = \"AGENTMEMORY_SECRET\";\n\nexport function agentmemoryHomeDir(): string {\n return process.env[\"AGENTMEMORY_DATA_DIR\"] || join(homedir(), \".agentmemory\");\n}\n\nexport function secretFilePath(): string {\n return join(agentmemoryHomeDir(), \"secret\");\n}\n\nfunction usable(value: string | undefined): string {\n if (typeof value !== \"string\") return \"\";\n const trimmed = value.trim();\n if (!trimmed) return \"\";\n if (trimmed.startsWith(\"${\") && trimmed.endsWith(\"}\")) return \"\";\n return trimmed;\n}\n\nfunction unquote(value: string): string {\n const quote = value[0];\n if ((quote === '\"' || quote === \"'\") && value.length > 1) {\n const close = value.indexOf(quote, 1);\n if (close !== -1) return value.slice(1, close);\n }\n const hash = value.indexOf(\" #\");\n return hash === -1 ? value : value.slice(0, hash).trim();\n}\n\nexport function readEnvFileSecret(): string {\n let content: unknown;\n try {\n content = readFileSync(process.env[\"AGENTMEMORY_ENV_FILE\"] || join(homedir(), \".agentmemory\", \".env\"), \"utf-8\");\n } catch {\n return \"\";\n }\n if (typeof content !== \"string\") return \"\";\n let found = \"\";\n for (const line of content.split(\"\\n\")) {\n const trimmed = line.trim();\n if (trimmed.startsWith(\"#\")) continue;\n const eq = trimmed.indexOf(\"=\");\n if (eq === -1) continue;\n const key = trimmed.slice(0, eq).replace(/^export\\s+/, \"\").trim();\n if (key !== SECRET_KEY) continue;\n found = usable(unquote(trimmed.slice(eq + 1).trim()));\n }\n return found;\n}\n\nfunction readSecretFile(path: string): string {\n try {\n return usable(readFileSync(path, \"utf-8\"));\n } catch {\n return \"\";\n }\n}\n\nfunction legacyDataDirSecretPaths(env: NodeJS.ProcessEnv = process.env): string[] {\n const dataDir = env[\"AGENTMEMORY_DATA_DIR\"];\n if (!dataDir) return [];\n const paths = [join(dataDir, \"secret\"), join(dataDir, \".hmac\"), join(dataDir, \"..\", \".hmac\")];\n return [...new Set(paths.filter((path) => path !== secretFilePath()))];\n}\n\nexport function readStoredSecret(env: NodeJS.ProcessEnv = process.env): string {\n const canonicalPath = canonicalSecretPath();\n const localPath = secretFilePath();\n const legacyPaths = legacyDataDirSecretPaths(env);\n return readSecretFile(canonicalPath) ||\n (localPath === canonicalPath ? \"\" : readSecretFile(localPath)) ||\n legacyPaths.map(readSecretFile).find(Boolean) || \"\";\n}\n\nexport function explicitSecret(env: NodeJS.ProcessEnv = process.env): string {\n return usable(env[SECRET_KEY]) || readEnvFileSecret();\n}\n\nexport function isLoopbackUrl(url: string): boolean {\n let hostname: string;\n try {\n hostname = new URL(url).hostname.toLowerCase();\n } catch {\n return false;\n }\n const bare = hostname.replace(/^\\[|\\]$/g, \"\");\n return bare === \"localhost\" || bare === \"::1\" || /^127(?:\\.\\d{1,3}){3}$/.test(bare);\n}\n\nexport function resolveClientSecret(\n baseUrl: string,\n env: NodeJS.ProcessEnv = process.env,\n): string {\n const fromEnv = usable(env[SECRET_KEY]);\n if (fromEnv) return fromEnv;\n if (env.AGENTMEMORY_CREDENTIAL_MODE === \"explicit\") return \"\";\n if (!isLoopbackUrl(baseUrl)) return \"\";\n return readEnvFileSecret() || readStoredSecret(env);\n}\n\nfunction canonicalSecretPath(): string {\n return join(homedir(), \".agentmemory\", \"secret\");\n}\n\nfunction writeSecretFile(path: string, secret: string): void {\n mkdirSync(join(path, \"..\"), { recursive: true, mode: 0o700 });\n const staging = `${path}.${process.pid}.${randomBytes(4).toString(\"hex\")}.tmp`;\n const fd = openSync(staging, \"wx\", 0o600);\n try {\n writeSync(fd, `${secret}\\n`);\n } finally {\n closeSync(fd);\n }\n try {\n chmodSync(staging, 0o600);\n renameSync(staging, path);\n chmodSync(path, 0o600);\n } finally {\n try { unlinkSync(staging); } catch {}\n }\n}\n\nfunction writeSecretIfAbsent(path: string, secret: string): string {\n mkdirSync(join(path, \"..\"), { recursive: true, mode: 0o700 });\n const staging = `${path}.${process.pid}.${randomBytes(4).toString(\"hex\")}.tmp`;\n const fd = openSync(staging, \"wx\", 0o600);\n try {\n writeSync(fd, `${secret}\\n`);\n } finally {\n closeSync(fd);\n }\n try {\n chmodSync(staging, 0o600);\n linkSync(staging, path);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== \"EEXIST\") throw err;\n } finally {\n try { unlinkSync(staging); } catch {}\n }\n const stored = readSecretFile(path);\n if (!stored) throw new Error(`Could not read stored API secret at ${path}.`);\n return stored;\n}\n\nexport function ensureServerSecret(env: NodeJS.ProcessEnv = process.env): {\n secret: string;\n source: \"env\" | \"file\" | \"generated\";\n} {\n const configured = explicitSecret(env);\n if (configured) return { secret: configured, source: \"env\" };\n\n const localPath = secretFilePath();\n const canonicalPath = canonicalSecretPath();\n const canonical = localPath === canonicalPath ? \"\" : readSecretFile(canonicalPath);\n if (canonical) {\n if (localPath !== canonicalPath && readSecretFile(localPath) !== canonical) {\n writeSecretFile(localPath, canonical);\n }\n return { secret: canonical, source: \"file\" };\n }\n\n const legacy = readSecretFile(localPath) || legacyDataDirSecretPaths(env).map(readSecretFile).find(Boolean) || \"\";\n const candidate = legacy || randomBytes(32).toString(\"hex\");\n const localSecret = writeSecretIfAbsent(localPath, candidate);\n const secret = localPath === canonicalPath\n ? localSecret\n : writeSecretIfAbsent(canonicalPath, localSecret);\n if (localPath !== canonicalPath && localSecret !== secret) writeSecretFile(localPath, secret);\n return { secret, source: legacy ? \"file\" : \"generated\" };\n}\n\nexport function bearerHeaders(\n baseUrl: string,\n env: NodeJS.ProcessEnv = process.env,\n): Record<string, string> {\n const secret = resolveClientSecret(baseUrl, env);\n return secret ? { Authorization: `Bearer ${secret}` } : {};\n}\n","import { resolveClientSecret } from \"../secret-store.js\";\nconst DEFAULT_URL = \"http://localhost:3111\";\nconst DEFAULT_HEALTH_PROBE_TIMEOUT_MS = 2_000;\nconst CALL_TIMEOUT_MS = 15_000;\nconst LOCAL_MODE_TTL_MS = 30_000;\n\nfunction probeTimeoutMs(): number {\n const raw = process.env[\"AGENTMEMORY_PROBE_TIMEOUT_MS\"];\n if (!raw) return DEFAULT_HEALTH_PROBE_TIMEOUT_MS;\n const n = Number(raw);\n return Number.isFinite(n) && n > 0 ? Math.floor(n) : DEFAULT_HEALTH_PROBE_TIMEOUT_MS;\n}\n\nexport function isProxyRequired(): boolean {\n const raw = process.env[\"AGENTMEMORY_FORCE_PROXY\"];\n return raw === \"1\" || raw === \"true\";\n}\n\nexport interface ProxyHandle {\n mode: \"proxy\";\n baseUrl: string;\n call: (path: string, init?: RequestInit) => Promise<unknown>;\n}\n\nexport class ProxyCallError extends Error {\n status: number;\n body: unknown;\n\n constructor(message: string, status: number, body: unknown) {\n super(message);\n this.name = \"ProxyCallError\";\n this.status = status;\n this.body = body;\n }\n}\n\nexport interface LocalHandle {\n mode: \"local\";\n}\n\nexport type Handle = ProxyHandle | LocalHandle;\n\nlet cached: Handle | null = null;\nlet cachedUrl: string | null = null;\nlet cachedAt = 0;\nlet probeInFlight: { url: string; forceProxy: boolean; promise: Promise<Handle> } | null = null;\n\n// `${VAR}`-style placeholders ship in plugin/.mcp.json so MCP hosts that\n// expand them (Claude Code, Cursor) substitute the user's shell value.\n// Hosts that DON'T expand pass the literal string `\"${AGENTMEMORY_URL}\"`\n// through to our subprocess — that string is truthy, defeats the `||`\n// fallback, and would have us POST to `${AGENTMEMORY_URL}/agentmemory/...`\n// (DNS failure). Strip any literal placeholder we see so the fallback\n// engages instead.\nexport function resolveEnvOrEmpty(name: string): string {\n const raw = process.env[name];\n if (!raw) return \"\";\n if (raw.startsWith(\"${\") && raw.endsWith(\"}\")) return \"\";\n return raw;\n}\n\nfunction baseUrl(): string {\n const value = resolveEnvOrEmpty(\"AGENTMEMORY_URL\") || DEFAULT_URL;\n try {\n const parsed = new URL(value);\n if (![\"http:\", \"https:\"].includes(parsed.protocol) || !parsed.hostname ||\n parsed.username || parsed.password || /[?#\\s]/.test(value)) {\n throw new Error();\n }\n return value.replace(/\\/+$/, \"\");\n } catch {\n throw new Error(\"AGENTMEMORY_URL must be an HTTP(S) URL without credentials, query, or fragment\");\n }\n}\n\nfunction authHeader(url: string): Record<string, string> {\n const secret = resolveClientSecret(url);\n return secret ? { authorization: `Bearer ${secret}` } : {};\n}\n\nexport type LivezProbe = (\n url: string,\n timeoutMs: number,\n headers: Record<string, string>,\n) => Promise<{ ok: boolean; status?: number; statusText?: string }>;\n\nconst defaultLivezProbe: LivezProbe = async (url, timeoutMs, headers) => {\n const res = await fetch(`${url}/agentmemory/livez`, {\n method: \"GET\",\n headers,\n signal: AbortSignal.timeout(timeoutMs),\n redirect: \"error\",\n });\n return { ok: res.ok, status: res.status, statusText: res.statusText };\n};\n\nlet livezProbe: LivezProbe = defaultLivezProbe;\n\n/**\n * Override the livez probe. Intended for tests — production code should rely\n * on the default fetch-based probe. Calling without an argument restores the\n * default. Pair with {@link resetHandleForTests} so the cached handle is\n * dropped before the next call.\n */\nexport function setLivezProbe(fn?: LivezProbe): void {\n livezProbe = fn ?? defaultLivezProbe;\n}\n\nasync function probe(url: string): Promise<boolean> {\n const timeout = probeTimeoutMs();\n try {\n const res = await livezProbe(url, timeout, authHeader(url));\n if (!res.ok) {\n process.stderr.write(\n `[@agentmemory/mcp] livez probe ${url}/agentmemory/livez -> ${res.status ?? \"?\"} ${res.statusText ?? \"\"}; falling back to local InMemoryKV (set AGENTMEMORY_FORCE_PROXY=1 to skip the probe)\\n`,\n );\n }\n return res.ok;\n } catch (err) {\n process.stderr.write(\n `[@agentmemory/mcp] livez probe ${url}/agentmemory/livez failed in ${timeout}ms: ${err instanceof Error ? err.message : String(err)}; falling back to local InMemoryKV (set AGENTMEMORY_FORCE_PROXY=1 to skip the probe, or raise AGENTMEMORY_PROBE_TIMEOUT_MS)\\n`,\n );\n return false;\n }\n}\n\nexport function invalidateHandle(): void {\n cached = null;\n cachedUrl = null;\n cachedAt = 0;\n}\n\nexport async function resolveHandle(): Promise<Handle> {\n const now = Date.now();\n const url = baseUrl();\n if (cached && (cachedUrl !== url || (cached.mode === \"local\" && isProxyRequired()))) invalidateHandle();\n if (cached) {\n if (cached.mode === \"local\" && now - cachedAt >= LOCAL_MODE_TTL_MS) {\n cached = null;\n cachedAt = 0;\n } else {\n return cached;\n }\n }\n const skipProbe = isProxyRequired();\n if (probeInFlight?.url === url && probeInFlight.forceProxy === skipProbe) return probeInFlight.promise;\n const pending = (async () => {\n const up = skipProbe ? true : await probe(url);\n if (baseUrl() !== url || isProxyRequired() !== skipProbe) return resolveHandle();\n if (skipProbe) {\n process.stderr.write(\n `[@agentmemory/mcp] AGENTMEMORY_FORCE_PROXY set; skipping livez probe and trusting ${url}\\n`,\n );\n }\n if (up) {\n const handle: ProxyHandle = {\n mode: \"proxy\",\n baseUrl: url,\n call: async (path, init) => {\n if (baseUrl() !== url) {\n throw new Error(\"AgentMemory endpoint changed; resolve a new proxy handle before retrying.\");\n }\n const res = await fetch(`${url}${path}`, {\n ...init,\n headers: {\n \"content-type\": \"application/json\",\n ...authHeader(url),\n ...(init?.headers as Record<string, string> | undefined),\n },\n signal: AbortSignal.timeout(CALL_TIMEOUT_MS),\n redirect: \"error\",\n });\n if (!res.ok) {\n const errText = await res.text().catch(() => \"\");\n let errBody: unknown = undefined;\n if (errText) {\n try {\n errBody = JSON.parse(errText);\n } catch {\n errBody = undefined;\n }\n }\n throw new ProxyCallError(\n `${init?.method || \"GET\"} ${path} -> ${res.status} ${res.statusText}`,\n res.status,\n errBody,\n );\n }\n const text = await res.text();\n return text ? JSON.parse(text) : null;\n },\n };\n cached = handle;\n cachedUrl = url;\n cachedAt = Date.now();\n return handle;\n }\n const local: LocalHandle = { mode: \"local\" };\n cached = local;\n cachedUrl = url;\n cachedAt = Date.now();\n return local;\n })();\n probeInFlight = { url, forceProxy: skipProbe, promise: pending };\n try {\n return await pending;\n } finally {\n if (probeInFlight?.promise === pending) probeInFlight = null;\n }\n}\n\nexport function resetHandleForTests(): void {\n cached = null;\n cachedUrl = null;\n cachedAt = 0;\n probeInFlight = null;\n livezProbe = defaultLivezProbe;\n}\n","#!/usr/bin/env node\n\nimport { InMemoryKV } from \"./in-memory-kv.js\";\nimport { createStdioTransport } from \"./transport.js\";\nimport { getAllTools } from \"./tools-registry.js\";\nimport { getAgentId, getStandalonePersistPath, isAgentScopeIsolated } from \"../config.js\";\nimport { isSearchLayer, type SearchLayer } from \"../state/search-layer.js\";\nimport { memoryToObservation } from \"../state/memory-utils.js\";\nimport { buildRecallResponse } from \"../functions/recall-response.js\";\nimport { parseSearchExpansionIds, type SearchExpansionId } from \"./search-arguments.js\";\nimport { parseSessionArguments } from \"./session-arguments.js\";\nimport { pageAfterCursor, sortByKeyDesc } from \"../state/list-query.js\";\nimport type { Memory, SearchResult, Session } from \"../types.js\";\nimport { VERSION } from \"../version.js\";\nimport { generateId } from \"../state/schema.js\";\nimport { queryAudit } from \"../functions/audit.js\";\nimport {\n resolveHandle,\n invalidateHandle,\n isProxyRequired,\n ProxyCallError,\n type Handle,\n type ProxyHandle,\n} from \"./rest-proxy.js\";\n\nconst IMPLEMENTED_TOOLS = new Set([\n \"memory_save\",\n \"memory_recall\",\n \"memory_smart_search\",\n \"memory_sessions\",\n \"memory_export\",\n \"memory_audit\",\n \"memory_governance_delete\",\n]);\n\nconst SUPPORTED_PROTOCOL_VERSIONS = [\n \"2025-11-25\",\n \"2025-06-18\",\n \"2025-03-26\",\n \"2024-11-05\",\n];\n\nconst SERVER_INFO = {\n name: \"agentmemory\",\n version: VERSION,\n};\n\nconst kv = new InMemoryKV(getStandalonePersistPath());\nlet modeAnnounced = false;\n\nfunction displayAgentmemoryUrl(): string {\n // Match the literal-placeholder guard in rest-proxy.ts so log lines\n // don't show `${AGENTMEMORY_URL}` when an MCP host passed the\n // placeholder through unexpanded.\n const raw = process.env[\"AGENTMEMORY_URL\"];\n if (!raw || (raw.startsWith(\"${\") && raw.endsWith(\"}\"))) {\n return \"http://localhost:3111\";\n }\n return raw;\n}\n\nfunction announceMode(handle: Handle): void {\n if (modeAnnounced) return;\n modeAnnounced = true;\n if (handle.mode === \"proxy\") {\n process.stderr.write(\n `[@agentmemory/mcp] proxying to agentmemory server at ${handle.baseUrl}\\n`,\n );\n } else {\n const fullToolCount = getAllTools().length;\n process.stderr.write(\n `[@agentmemory/mcp] no server reachable at ${displayAgentmemoryUrl()}; running reduced LOCAL FALLBACK with ${IMPLEMENTED_TOOLS.size} of ${fullToolCount} tools. Start 'npx @agentmemory/agentmemory' (and point AGENTMEMORY_URL at it) to unlock all ${fullToolCount} tools.\\n`,\n );\n }\n}\n\nfunction normalizeList(value: unknown): string[] {\n if (!value) return [];\n if (Array.isArray(value)) {\n return value\n .map((v) => (typeof v === \"string\" ? v.trim() : \"\"))\n .filter((v) => v.length > 0);\n }\n if (typeof value === \"string\") {\n return value\n .split(\",\")\n .map((s) => s.trim())\n .filter((s) => s.length > 0);\n }\n return [];\n}\n\nconst DEFAULT_LIMIT = 10;\nconst MAX_LIMIT = 100;\nfunction parseLimit(raw: unknown, fallback = DEFAULT_LIMIT): number {\n if (typeof raw !== \"number\" && typeof raw !== \"string\") return fallback;\n const n = Number(raw);\n if (!Number.isFinite(n) || n <= 0) return fallback;\n return Math.min(Math.floor(n), MAX_LIMIT);\n}\n\nfunction textResponse(payload: unknown, pretty = false): {\n content: Array<{ type: string; text: string }>;\n} {\n return {\n content: [\n { type: \"text\", text: JSON.stringify(payload, null, pretty ? 2 : 0) },\n ],\n };\n}\n\nfunction isAuthenticationError(error: unknown): boolean {\n return error instanceof ProxyCallError && (error.status === 401 || error.status === 403);\n}\n\ninterface Validated {\n tool: string;\n content?: string;\n type?: string;\n concepts?: string[];\n files?: string[];\n project?: string;\n agentId?: string;\n query?: string;\n limit?: number;\n format?: \"full\" | \"compact\" | \"narrative\";\n tokenBudget?: number;\n targetLayer?: SearchLayer;\n cwd?: string;\n expandIds?: SearchExpansionId[];\n sessionId?: string;\n source?: string;\n includeLessons?: boolean;\n memoryIds?: string[];\n reason?: string;\n exportQuery?: string;\n sessionQuery?: Record<string, string>;\n}\n\nfunction validate(toolName: string, args: Record<string, unknown>): Validated {\n if (!IMPLEMENTED_TOOLS.has(toolName)) {\n throw new Error(`Unknown tool: ${toolName}`);\n }\n const v: Validated = { tool: toolName };\n switch (toolName) {\n case \"memory_save\": {\n const content = args[\"content\"];\n if (typeof content !== \"string\" || !content.trim()) {\n throw new Error(\"content is required\");\n }\n v.content = content;\n v.type = (args[\"type\"] as string) || \"fact\";\n v.concepts = normalizeList(args[\"concepts\"]);\n v.files = normalizeList(args[\"files\"]);\n // The tool schema exposes project (and now agentId); dropping them\n // here silently broke project/agent scoping through the stdio\n // package specifically.\n if (typeof args[\"project\"] === \"string\" && args[\"project\"].trim()) {\n v.project = args[\"project\"].trim();\n }\n if (typeof args[\"agentId\"] === \"string\" && args[\"agentId\"].trim()) {\n v.agentId = args[\"agentId\"].trim();\n }\n return v;\n }\n case \"memory_recall\":\n case \"memory_smart_search\": {\n if (args.targetLayer !== undefined && !isSearchLayer(args.targetLayer)) {\n throw new Error(\"targetLayer must be one of: all, memory, observation\");\n }\n v.targetLayer = args.targetLayer ?? \"all\";\n for (const field of [\"project\", \"cwd\", \"agentId\", \"sessionId\", \"source\"] as const) {\n if (args[field] !== undefined && typeof args[field] !== \"string\") {\n throw new Error(`${field} must be a string`);\n }\n if (typeof args[field] === \"string\" && args[field].trim()) v[field] = args[field].trim();\n }\n if (toolName === \"memory_smart_search\") {\n v.expandIds = parseSearchExpansionIds(args.expandIds);\n if (typeof args.includeLessons === \"boolean\") v.includeLessons = args.includeLessons;\n }\n const query = args[\"query\"];\n if (typeof query !== \"string\" || !query.trim()) {\n if (!v.expandIds?.length) throw new Error(\"query is required unless expandIds is provided\");\n } else {\n v.query = query.trim();\n }\n v.limit = parseLimit(args[\"limit\"]);\n if (toolName === \"memory_recall\") {\n const format = typeof args.format === \"string\" ? args.format.trim().toLowerCase() : args.format ?? \"full\";\n if (format !== \"full\" && format !== \"compact\" && format !== \"narrative\") {\n throw new Error(\"format must be one of: full, compact, narrative\");\n }\n v.format = format;\n if (args.token_budget !== undefined) {\n const budget = typeof args.token_budget === \"string\" ? Number(args.token_budget) : args.token_budget;\n if (typeof budget !== \"number\" || !Number.isInteger(budget) || budget < 1) {\n throw new Error(\"token_budget must be a positive integer\");\n }\n v.tokenBudget = budget;\n }\n }\n return v;\n }\n case \"memory_sessions\": {\n v.sessionQuery = parseSessionArguments(args);\n return v;\n }\n case \"memory_governance_delete\": {\n const ids = normalizeList(args[\"memoryIds\"]);\n if (ids.length === 0) throw new Error(\"memoryIds is required\");\n v.memoryIds = ids;\n v.reason = (args[\"reason\"] as string) || \"plugin skill request\";\n return v;\n }\n case \"memory_export\": {\n const query = new URLSearchParams();\n for (const key of [\"maxSessions\", \"offset\", \"collectionLimit\", \"collectionOffset\"]) {\n const n = Number(args[key]);\n const min = key === \"maxSessions\" || key === \"collectionLimit\" ? 1 : 0;\n if (args[key] !== undefined && Number.isInteger(n) && n >= min) query.set(key, String(n));\n }\n if (typeof args[\"collections\"] === \"string\") query.set(\"collections\", args[\"collections\"]);\n v.exportQuery = query.toString();\n return v;\n }\n case \"memory_audit\": {\n v.limit = parseLimit(args[\"limit\"], 50);\n return v;\n }\n default:\n throw new Error(`Unknown tool: ${toolName}`);\n }\n}\n\nasync function handleProxy(\n v: Validated,\n handle: ProxyHandle,\n): Promise<{ content: Array<{ type: string; text: string }> }> {\n switch (v.tool) {\n case \"memory_save\": {\n const result = await handle.call(\"/agentmemory/remember\", {\n method: \"POST\",\n body: JSON.stringify({\n content: v.content,\n type: v.type,\n concepts: v.concepts,\n files: v.files,\n ...(v.project !== undefined && { project: v.project }),\n ...(v.agentId !== undefined && { agentId: v.agentId }),\n }),\n });\n return textResponse(result);\n }\n case \"memory_recall\": {\n const body: Record<string, unknown> = {\n query: v.query,\n limit: v.limit,\n format: v.format ?? \"full\",\n targetLayer: v.targetLayer,\n project: v.project,\n cwd: v.cwd,\n agentId: v.agentId,\n };\n if (v.tokenBudget != null) body[\"token_budget\"] = v.tokenBudget;\n const result = await handle.call(\"/agentmemory/search\", {\n method: \"POST\",\n body: JSON.stringify(body),\n });\n return textResponse(result, true);\n }\n case \"memory_smart_search\": {\n const body: Record<string, unknown> = {\n query: v.query,\n limit: v.limit,\n targetLayer: v.targetLayer,\n project: v.project,\n agentId: v.agentId,\n expandIds: v.expandIds,\n sessionId: v.sessionId,\n source: v.source,\n includeLessons: v.includeLessons,\n };\n const result = await handle.call(\"/agentmemory/smart-search\", {\n method: \"POST\",\n body: JSON.stringify(body),\n });\n return textResponse(result, true);\n }\n case \"memory_sessions\": {\n const result = await handle.call(\n `/agentmemory/sessions?${new URLSearchParams(v.sessionQuery)}`,\n { method: \"GET\" },\n );\n return textResponse(result, true);\n }\n case \"memory_governance_delete\": {\n const result = await handle.call(\"/agentmemory/governance/memories\", {\n method: \"DELETE\",\n body: JSON.stringify({ memoryIds: v.memoryIds, reason: v.reason }),\n });\n return textResponse(result);\n }\n case \"memory_export\": {\n try {\n const result = await handle.call(\n `/agentmemory/export${v.exportQuery ? `?${v.exportQuery}` : \"\"}`,\n { method: \"GET\" },\n );\n return textResponse(result, true);\n } catch (err) {\n if (\n err instanceof ProxyCallError &&\n err.status === 413 &&\n err.body != null &&\n typeof err.body === \"object\" &&\n (err.body as { oversized?: boolean }).oversized === true\n ) {\n return textResponse(err.body, true);\n }\n throw err;\n }\n }\n case \"memory_audit\": {\n const result = await handle.call(\n `/agentmemory/audit?limit=${v.limit}`,\n { method: \"GET\" },\n );\n return textResponse(result, true);\n }\n default:\n throw new Error(`Unknown tool: ${v.tool}`);\n }\n}\n\nfunction localFallbackNotice(persisted: boolean) {\n const store = persisted\n ? \"the local fallback file, which the server does not read\"\n : \"this process's in-memory fallback store, which is lost when the process exits\";\n return {\n storage: \"local\",\n warning: `agentmemory server at ${displayAgentmemoryUrl()} did not confirm this call, so it ran against ${store}.`,\n };\n}\n\nasync function handleLocal(\n v: Validated,\n kvInstance: InMemoryKV,\n): Promise<{ content: Array<{ type: string; text: string }> }> {\n switch (v.tool) {\n case \"memory_save\": {\n const id = generateId(\"mem\");\n const isoNow = new Date().toISOString();\n await kvInstance.set(\"mem:memories\", id, {\n id,\n type: v.type,\n title: (v.content || \"\").slice(0, 80),\n content: v.content,\n concepts: v.concepts,\n files: v.files,\n createdAt: isoNow,\n updatedAt: isoNow,\n strength: 7,\n version: 1,\n isLatest: true,\n sessionIds: [],\n ...(v.project !== undefined && { project: v.project }),\n ...((v.agentId ?? getAgentId()) !== undefined && { agentId: v.agentId ?? getAgentId() }),\n });\n const persisted = kvInstance.persist();\n return textResponse({ saved: id, ...localFallbackNotice(persisted) });\n }\n\n case \"memory_recall\":\n case \"memory_smart_search\": {\n const isolated = isAgentScopeIsolated();\n const agentId = v.agentId === \"*\" ? undefined : v.agentId ?? (isolated ? getAgentId() : undefined);\n if (isolated && v.agentId !== \"*\" && !agentId) {\n throw new Error(\"AGENTMEMORY_AGENT_SCOPE=isolated requires an agentId; pass '*' to read across agents\");\n }\n const query = (v.query || \"\").toLowerCase();\n const limit = v.limit ?? DEFAULT_LIMIT;\n const all = v.targetLayer === \"observation\" ? [] : await kvInstance.list<Memory>(\"mem:memories\");\n const sessions = v.project || v.cwd ? await kvInstance.list<Session>(\"mem:sessions\") : [];\n const sessionsById = new Map(sessions.map((session) => [session.id, session]));\n const scoped = all.filter((memory) => {\n if (memory.isLatest === false || (agentId && memory.agentId !== agentId)) return false;\n const linkedSessions = (memory.sessionIds ?? []).map((id) => sessionsById.get(id));\n if (v.project && (memory.project ? memory.project !== v.project : !linkedSessions.some((session) => session?.project === v.project))) return false;\n if (v.cwd && !linkedSessions.some((session) => session?.cwd === v.cwd)) return false;\n return true;\n });\n if (v.tool === \"memory_smart_search\" && v.expandIds?.length) {\n const ids = v.expandIds.map((entry) => typeof entry === \"string\" ? entry : entry.obsId);\n const byId = new Map(scoped.map((memory) => [memory.id, memory]));\n const eligible = ids.filter((id) => byId.has(id));\n const results = eligible.slice(0, 20).map((id) => {\n const observation = memoryToObservation(byId.get(id)!);\n return { obsId: id, sessionId: observation.sessionId, observation, layer: \"memory\" };\n });\n return textResponse({ mode: \"expanded\", results, truncated: eligible.length > 20 }, true);\n }\n const results = scoped.filter((memory) => {\n const text = [memory.title, memory.content, ...(memory.files ?? []), ...(memory.concepts ?? []), ...(memory.sessionIds ?? []), memory.id]\n .join(\" \").toLowerCase();\n return query.split(/\\s+/).every((word) => text.includes(word));\n }).slice(0, limit);\n if (v.tool === \"memory_recall\") {\n const matches: SearchResult[] = results.map((memory) => {\n const observation = memoryToObservation(memory);\n return { sessionId: observation.sessionId, observation, score: 1, layer: \"memory\" };\n });\n return textResponse(buildRecallResponse(matches, v.format ?? \"full\", v.tokenBudget), true);\n }\n return textResponse({ mode: \"compact\", results: results.map((memory) => ({\n ...memory, obsId: memory.id, sessionId: memory.sessionIds?.[0] ?? \"memory\", layer: \"memory\",\n })) }, true);\n }\n\n case \"memory_sessions\": {\n const query = v.sessionQuery!;\n const agentId = query.agentId === \"*\" ? undefined : query.agentId ?? (isAgentScopeIsolated() ? getAgentId() : undefined);\n const sessions = (await kvInstance.list<Session>(\"mem:sessions\"))\n .filter((session) => session && typeof session.id === \"string\" && session.id.length > 0)\n .filter((session) => !query.project || session.project === query.project)\n .filter((session) => !agentId || session.agentId === agentId);\n const key = (session: Session) => session.startedAt || \"\";\n const { page, nextCursor } = pageAfterCursor(\n sortByKeyDesc(sessions, key, (session) => session.id), key,\n (session) => session.id, query.cursor, Number(query.limit),\n );\n const withSummary = await Promise.all(page.map(async (session) => {\n const summary = await kvInstance.get(\"mem:summaries\", session.id);\n return summary ? { ...session, summary } : session;\n }));\n return textResponse({ sessions: withSummary, nextCursor }, true);\n }\n\n case \"memory_governance_delete\": {\n let deleted = 0;\n for (const id of v.memoryIds || []) {\n const existing = await kvInstance.get(\"mem:memories\", id);\n if (existing) {\n await kvInstance.delete(\"mem:memories\", id);\n deleted++;\n }\n }\n const persisted = kvInstance.persist();\n return textResponse({\n deleted,\n requested: (v.memoryIds || []).length,\n reason: v.reason,\n ...localFallbackNotice(persisted),\n });\n }\n\n case \"memory_export\": {\n const memories = await kvInstance.list(\"mem:memories\");\n const sessions = await kvInstance.list(\"mem:sessions\");\n return textResponse({ version: VERSION, memories, sessions }, true);\n }\n\n case \"memory_audit\": {\n const result = await queryAudit(kvInstance as never, {\n limit: v.limit ?? 50,\n });\n return textResponse(result, true);\n }\n\n default:\n throw new Error(`Unknown tool: ${v.tool}`);\n }\n}\n\nasync function handleProxyGeneric(\n toolName: string,\n args: Record<string, unknown>,\n handle: ProxyHandle,\n): Promise<{ content: Array<{ type: string; text: string }> }> {\n // Forward to the server's full MCP surface so non-Claude clients can\n // reach all 54 tools (lessons, sentinels, slots, signals, graph, …)\n // instead of being capped at the 7 IMPLEMENTED_TOOLS set baked into\n // this shim. The server validates arguments per tool.\n const result = (await handle.call(\"/agentmemory/mcp/call\", {\n method: \"POST\",\n body: JSON.stringify({ name: toolName, arguments: args }),\n })) as { content?: Array<{ type: string; text: string }> } | null;\n if (result && Array.isArray(result.content)) {\n return { content: result.content };\n }\n return textResponse(result, true);\n}\n\nexport async function handleToolCall(\n toolName: string,\n args: Record<string, unknown>,\n kvInstance: InMemoryKV = kv,\n): Promise<{ content: Array<{ type: string; text: string }> }> {\n const handle = await resolveHandle();\n announceMode(handle);\n\n if (!IMPLEMENTED_TOOLS.has(toolName)) {\n if (handle.mode === \"proxy\") {\n try {\n return await handleProxyGeneric(toolName, args, handle);\n } catch (err) {\n process.stderr.write(\n `[@agentmemory/mcp] proxy call failed for ${toolName}: ${err instanceof Error ? err.message : String(err)}\\n`,\n );\n invalidateHandle();\n throw err;\n }\n }\n throw new Error(\n `Unknown tool: ${toolName} (local fallback supports only ${[...IMPLEMENTED_TOOLS].join(\", \")}; start an agentmemory server and set AGENTMEMORY_URL to use the full tool set)`,\n );\n }\n\n const validated = validate(toolName, args);\n if (handle.mode === \"proxy\") {\n try {\n return await handleProxy(validated, handle);\n } catch (err) {\n if (toolName === \"memory_sessions\" || isAuthenticationError(err) || isProxyRequired()) {\n invalidateHandle();\n throw err;\n }\n process.stderr.write(\n `[@agentmemory/mcp] proxy call failed for ${toolName}: ${err instanceof Error ? err.message : String(err)}; invalidating handle and falling back to local KV\\n`,\n );\n invalidateHandle();\n }\n }\n return handleLocal(validated, kvInstance);\n}\n\nexport async function handleToolsList(): Promise<{ tools: unknown[] }> {\n const debug = process.env[\"AGENTMEMORY_DEBUG\"] === \"1\" || process.env[\"AGENTMEMORY_DEBUG\"] === \"true\";\n const handle = await resolveHandle();\n announceMode(handle);\n if (debug) {\n process.stderr.write(\n `[@agentmemory/mcp] tools/list: handle.mode=${handle.mode}${handle.mode === \"proxy\" ? ` baseUrl=${handle.baseUrl}` : \"\"}\\n`,\n );\n }\n if (handle.mode === \"proxy\") {\n try {\n const remote = (await handle.call(\"/agentmemory/mcp/tools\", {\n method: \"GET\",\n })) as { tools?: unknown } | null;\n if (debug) {\n const shape = remote === null\n ? \"null\"\n : typeof remote !== \"object\"\n ? typeof remote\n : `keys=${Object.keys(remote as object).join(\",\")} toolsType=${Array.isArray((remote as { tools?: unknown }).tools) ? `array(len=${((remote as { tools: unknown[] }).tools).length})` : typeof (remote as { tools?: unknown }).tools}`;\n process.stderr.write(\n `[@agentmemory/mcp] tools/list: remote response shape: ${shape}\\n`,\n );\n }\n if (remote && Array.isArray(remote.tools)) {\n if (debug) {\n process.stderr.write(\n `[@agentmemory/mcp] tools/list: returning ${remote.tools.length} tools from server\\n`,\n );\n }\n return { tools: remote.tools };\n }\n if (isProxyRequired()) throw new Error(\"AgentMemory returned an invalid tool inventory\");\n process.stderr.write(\n `[@agentmemory/mcp] tools/list: server returned unexpected shape (no .tools array); falling back to local IMPLEMENTED_TOOLS list. Set AGENTMEMORY_DEBUG=1 to inspect response.\\n`,\n );\n } catch (err) {\n invalidateHandle();\n if (isAuthenticationError(err) || isProxyRequired()) throw err;\n process.stderr.write(\n `[@agentmemory/mcp] tools/list proxy failed: ${err instanceof Error ? err.message : String(err)}; falling back to local list\\n`,\n );\n }\n }\n const fallback = getAllTools().filter((t) => IMPLEMENTED_TOOLS.has(t.name));\n if (debug) {\n process.stderr.write(\n `[@agentmemory/mcp] tools/list: returning ${fallback.length} local fallback tools (${fallback.map((t) => t.name).join(\",\")})\\n`,\n );\n }\n return { tools: fallback };\n}\n\nconst transport = createStdioTransport(async (method, params) => {\n switch (method) {\n case \"initialize\": {\n const requested = (params as { protocolVersion?: unknown } | undefined)\n ?.protocolVersion;\n const protocolVersion =\n typeof requested === \"string\" &&\n SUPPORTED_PROTOCOL_VERSIONS.includes(requested)\n ? requested\n : SUPPORTED_PROTOCOL_VERSIONS[0];\n return {\n protocolVersion,\n capabilities: { tools: { listChanged: false } },\n serverInfo: {\n name: SERVER_INFO.name,\n version: SERVER_INFO.version,\n },\n };\n }\n\n case \"notifications/initialized\":\n return {};\n\n case \"tools/list\":\n return handleToolsList();\n\n case \"tools/call\": {\n const toolName = params.name as string;\n const toolArgs = (params.arguments as Record<string, unknown>) || {};\n try {\n return await handleToolCall(toolName, toolArgs);\n } catch (err) {\n return {\n content: [\n {\n type: \"text\",\n text: `Error: ${err instanceof Error ? err.message : String(err)}`,\n },\n ],\n isError: true,\n };\n }\n }\n\n default:\n throw new Error(`Unknown method: ${method}`);\n }\n});\n\nprocess.stderr.write(\n `[@agentmemory/mcp] Standalone MCP server v${SERVER_INFO.version} starting...\\n`,\n);\ntransport.start();\n\nprocess.on(\"SIGINT\", () => {\n kv.persist();\n process.exit(0);\n});\nprocess.on(\"SIGTERM\", () => {\n kv.persist();\n process.exit(0);\n});\n"],"mappings":"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AM;GACN,YAAY;IACV,OAAO;KACL,MAAM;KACN,aAAa;KACd;IACD,OAAO;KACL,MAAM;KACN,aAAa;KACd;IACD,QAAQ;KACN,MAAM;KACN,aAAa;KACd;IACD,cAAc;KACZ,MAAM;KACN,aAAa;KACd;IACD,aAAa;KACX,MAAM;KACN,MAAM;MAAC;MAAO;MAAU;MAAc;KACtC,aAAa;KACd;IACD,SAAS;KAAE,MAAM;KAAU,aAAa;KAA0B;IAClE,KAAK;KAAE,MAAM;KAAU,aAAa;KAAuC;IAC3E,SAAS;KAAE,MAAM;KAAU,aAAa;KAAmD;IAC5F;GACD,UAAU,CAAC,QAAQ;GACpB;EACF;CACD;EACE,MAAM;EACN,aACE;EACF,aAAa;GACX,MAAM;GACN,YAAY,EACV,UAAU;IACR,MAAM;IACN,aAAa;IACd,EACF;GACD,UAAU,CAAC,WAAW;GACvB;EACF;CACD;EACE,MAAM;EACN,aACE;EACF,aAAa;GACX,MAAM;GACN,YAAY;IACV,SAAS;KACP,MAAM;KACN,aAAa;KACd;IACD,MAAM;KACJ,MAAM;KACN,aACE;KACH;IACD,UAAU;KACR,MAAM;KACN,aAAa;KACd;IACD,OAAO;KACL,MAAM;KACN,aAAa;KACd;IACD,SAAS;KACP,MAAM;KACN,aACE;KAIH;IACD,SAAS;KACP,MAAM;KACN,aACE;KAEH;IACF;GACD,UAAU,CAAC,UAAU;GACtB;EACF;CACD;EACE,MAAM;EACN,aAAa;EACb,aAAa;GACX,MAAM;GACN,YAAY;IACV,OAAO;KAAE,MAAM;KAAU,aAAa;KAA8B;IACpE,WAAW;KACT,MAAM;KACN,aAAa;KACd;IACF;GACD,UAAU,CAAC,QAAQ;GACpB;EACF;CACD;EACE,MAAM;EACN,aAAa;EACb,aAAa;GACX,MAAM;GACN,YAAY;IACV,SAAS;KAAE,MAAM;KAAU,aAAa;KAA2B;IACnE,OAAO;KACL,MAAM;KACN,aACE;KACH;IACF;GACF;EACF;CACD;EACE,MAAM;EACN,aACE;EACF,aAAa;GACX,MAAM;GACN,YAAY;IACV,SAAS;KAAE,MAAM;KAAU,aAAa;KAA4D;IACpG,SAAS;KAAE,MAAM;KAAU,aAAa;KAAmD;IAC3F,OAAO;KAAE,MAAM;KAAW,aAAa;KAAmC;IAC1E,QAAQ;KAAE,MAAM;KAAU,aAAa;KAA8D;IACtG;GACF;EACF;CACD;EACE,MAAM;EACN,aAAa;EACb,aAAa;GACX,MAAM;GACN,YAAY;IACV,OAAO;KAAE,MAAM;KAAU,aAAa;KAAgB;IACtD,WAAW;KACT,MAAM;KACN,aAAa;KACd;IACD,OAAO;KAAE,MAAM;KAAU,aAAa;KAA4B;IAClE,aAAa;KACX,MAAM;KACN,MAAM;MAAC;MAAO;MAAU;MAAc;KACtC,aAAa;KACd;IACD,SAAS;KAAE,MAAM;KAAU,aAAa;KAA0B;IAClE,SAAS;KAAE,MAAM;KAAU,aAAa;KAAmD;IAC3F,gBAAgB;KAAE,MAAM;KAAW,aAAa;KAAiE;IAClH;GACF;EACF;CACD;EACE,MAAM;EACN,aACE;EACF,aAAa;GACX,MAAM;GACN,YAAY;IACV,WAAW;KAAE,MAAM;KAAU,aAAa;KAAoD;IAC9F,eAAe;KAAE,MAAM;KAAU,aAAa;KAAoD;IAClG,kBAAkB;KAAE,MAAM;KAAU,aAAa;KAAsC;IACvF,MAAM;KAAE,MAAM;KAAU,aAAa;KAAoC;IACzE,WAAW;KAAE,MAAM;KAAU,aAAa;KAA8B;IACzE;GACF;EACF;CACD;EACE,MAAM;EACN,aAAa;EACb,aAAa;GACX,MAAM;GACN,YAAY;IACV,QAAQ;KACN,MAAM;KACN,aAAa;KACd;IACD,SAAS;KAAE,MAAM;KAAU,aAAa;KAA0B;IAClE,QAAQ;KACN,MAAM;KACN,aAAa;KACd;IACD,OAAO;KACL,MAAM;KACN,aAAa;KACd;IACF;GACD,UAAU,CAAC,SAAS;GACrB;EACF;CACD;EACE,MAAM;EACN,aAAa;EACb,aAAa;GACX,MAAM;GACN,YAAY;IACV,SAAS;KAAE,MAAM;KAAU,aAAa;KAAgB;IACxD,SAAS;KACP,MAAM;KACN,aAAa;KACd;IACF;GACD,UAAU,CAAC,UAAU;GACtB;EACF;CACD;EACE,MAAM;EACN,aACE;EACF,aAAa;GACX,MAAM;GACN,YAAY;IACV,aAAa;KACX,MAAM;KACN,aAAa;KACd;IACD,QAAQ;KAAE,MAAM;KAAU,aAAa;KAAkB;IACzD,iBAAiB;KACf,MAAM;KACN,aACE;KACH;IACD,kBAAkB;KAChB,MAAM;KACN,aAAa;KACd;IACD,aAAa;KACX,MAAM;KACN,aACE;KACH;IACF;GACF;EACF;CACD;EACE,MAAM;EACN,aAAa;EACb,aAAa;GACX,MAAM;GACN,YAAY;IACV,UAAU;KACR,MAAM;KACN,aAAa;KACd;IACD,SAAS;KACP,MAAM;KACN,aAAa;KACd;IACD,eAAe;KACb,MAAM;KACN,aAAa;KACd;IACF;GACD,UAAU,CAAC,WAAW;GACvB;EACF;CACD;EACE,MAAM;EACN,aACE;EACF,aAAa;GACX,MAAM;GACN,YAAY,EACV,KAAK;IAAE,MAAM;IAAU,aAAa;IAAuB,EAC5D;GACD,UAAU,CAAC,MAAM;GAClB;EACF;CACD;EACE,MAAM;EACN,aACE;EACF,aAAa;GACX,MAAM;GACN,YAAY;IACV,QAAQ;KAAE,MAAM;KAAU,aAAa;KAAyB;IAChE,MAAM;KAAE,MAAM;KAAU,aAAa;KAAwB;IAC7D,OAAO;KAAE,MAAM;KAAU,aAAa;KAAsC;IAC7E;GACF;EACF;CACF;AAED,MAAa,aAA2B;CACtC;EACE,MAAM;EACN,aACE;EACF,aAAa;GACX,MAAM;GACN,YAAY,EACV,WAAW;IACT,MAAM;IACN,aACE;IACH,EACF;GACD,UAAU,CAAC,YAAY;GACxB;EACF;CACD;EACE,MAAM;EACN,aAAa;EACb,aAAa;GACX,MAAM;GACN,YAAY;IACV,aAAa;KACX,MAAM;KACN,aAAa;KACd;IACD,UAAU;KAAE,MAAM;KAAU,aAAa;KAAuB;IAChE,UAAU;KACR,MAAM;KACN,aAAa;KACd;IACD,OAAO;KAAE,MAAM;KAAU,aAAa;KAAwB;IAC/D;GACF;EACF;CACD;EACE,MAAM;EACN,aACE;EACF,aAAa;GACX,MAAM;GACN,YAAY,EACV,MAAM;IACJ,MAAM;IACN,aAAa;IACd,EACF;GACF;EACF;CACD;EACE,MAAM;EACN,aAAa;EACb,aAAa;GACX,MAAM;GACN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"}
|