@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
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"post-commit.mjs","names":[],"sources":["../../src/secret-store.ts","../../src/hooks/_project.ts","../../src/hooks/post-commit.ts"],"sourcesContent":["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 { execSync } from \"node:child_process\";\nimport { basename } from \"node:path\";\n\n// Resolution order: AGENTMEMORY_PROJECT_NAME env → git toplevel basename → cwd basename.\nexport function resolveProject(cwd?: string): string {\n const explicit = process.env[\"AGENTMEMORY_PROJECT_NAME\"];\n if (explicit && explicit.trim()) return explicit.trim();\n const dir = cwd && cwd.trim() ? cwd : process.cwd();\n try {\n const top = execSync(\"git rev-parse --show-toplevel\", {\n cwd: dir,\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n timeout: 500,\n })\n .toString()\n .trim();\n if (top) return basename(top);\n } catch {}\n return basename(dir);\n}\n\nexport function hookCwd(data: Record<string, unknown> | null | undefined): string | undefined {\n if (!data || typeof data !== \"object\") return undefined;\n if (typeof data.cwd === \"string\" && data.cwd.trim()) return data.cwd;\n const roots = data.workspace_roots;\n if (Array.isArray(roots)) {\n for (const root of roots) {\n if (typeof root === \"string\" && root.trim()) return root;\n }\n }\n const projectDir =\n process.env[\"DEVIN_PROJECT_DIR\"] || process.env[\"CLAUDE_PROJECT_DIR\"];\n if (projectDir && projectDir.trim()) return projectDir;\n return undefined;\n}\n","#!/usr/bin/env node\nimport { resolveClientSecret } from \"../secret-store.js\";\n\nimport { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\nimport { hookCwd } from \"./_project.js\";\n\nconst exec = promisify(execFile);\n\nfunction isSdkChildContext(payload: unknown): boolean {\n if (process.env[\"AGENTMEMORY_SDK_CHILD\"] === \"1\") return true;\n if (!payload || typeof payload !== \"object\") return false;\n return (payload as { entrypoint?: unknown }).entrypoint === \"sdk-ts\";\n}\n\nconst REST_URL = process.env[\"AGENTMEMORY_URL\"] || \"http://localhost:3111\";\nconst SECRET = resolveClientSecret(REST_URL);\nconst TIMEOUT_MS = 1500;\n\nfunction authHeaders(): Record<string, string> {\n const h: Record<string, string> = { \"Content-Type\": \"application/json\" };\n if (SECRET) h[\"Authorization\"] = `Bearer ${SECRET}`;\n return h;\n}\n\nasync function git(args: string[], cwd: string): Promise<string | null> {\n try {\n const { stdout } = await exec(\"git\", args, { cwd, timeout: 1500 });\n return stdout.trim();\n } catch {\n return null;\n }\n}\n\nasync function main() {\n let input = \"\";\n for await (const chunk of process.stdin) {\n input += chunk;\n }\n\n let data: Record<string, unknown> = {};\n if (input.trim()) {\n try {\n data = JSON.parse(input);\n } catch {\n // Direct invocation from .git/hooks/post-commit may pass no stdin.\n }\n }\n\n if (!data || typeof data !== \"object\") data = {};\n if (isSdkChildContext(data)) return;\n\n const cwd =\n hookCwd(data) || process.env[\"AGENTMEMORY_CWD\"] || process.cwd();\n const sessionId =\n (data.session_id as string) ||\n process.env[\"AGENTMEMORY_SESSION_ID\"] ||\n undefined;\n\n const sha =\n process.env[\"AGENTMEMORY_COMMIT_SHA\"] ||\n (await git([\"rev-parse\", \"HEAD\"], cwd));\n if (!sha) return;\n\n const branch = await git([\"rev-parse\", \"--abbrev-ref\", \"HEAD\"], cwd);\n const repo = await git([\"config\", \"--get\", \"remote.origin.url\"], cwd);\n const message = await git([\"log\", \"-1\", \"--pretty=%B\", sha], cwd);\n const author = await git([\"log\", \"-1\", \"--pretty=%an <%ae>\", sha], cwd);\n const authoredAt = await git([\"log\", \"-1\", \"--pretty=%aI\", sha], cwd);\n const filesRaw = await git(\n [\"diff-tree\", \"--no-commit-id\", \"--name-only\", \"-r\", sha],\n cwd,\n );\n const files = filesRaw ? filesRaw.split(\"\\n\").filter(Boolean) : undefined;\n\n const body = {\n sessionId,\n sha,\n branch: branch || undefined,\n repo: repo || undefined,\n message: message || undefined,\n author: author || undefined,\n authoredAt: authoredAt || undefined,\n files,\n };\n\n try {\n await fetch(`${REST_URL}/agentmemory/session/commit`, {\n method: \"POST\",\n headers: authHeaders(),\n body: JSON.stringify(body),\n signal: AbortSignal.timeout(TIMEOUT_MS),\n });\n } catch {\n // best-effort\n }\n}\n\nmain().catch(() => process.exit(0));\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"post-commit.mjs","names":[],"sources":["../../src/secret-store.ts","../../src/hooks/_project.ts","../../src/hooks/post-commit.ts"],"sourcesContent":["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 { execSync } from \"node:child_process\";\nimport { realpathSync } from \"node:fs\";\nimport { basename, dirname, resolve } from \"node:path\";\n\n// Resolution order: AGENTMEMORY_PROJECT_NAME env → git repository basename → cwd basename.\nexport function resolveProject(cwd?: string): string {\n const explicit = process.env[\"AGENTMEMORY_PROJECT_NAME\"];\n if (explicit && explicit.trim()) return explicit.trim();\n const dir = cwd && cwd.trim() ? cwd : process.cwd();\n try {\n const [top, gitDir, commonDir] = execSync(\n \"git rev-parse --show-toplevel --git-dir --git-common-dir\",\n {\n cwd: dir,\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n timeout: 500,\n },\n )\n .toString()\n .trim()\n .split(/\\r?\\n/);\n if (top) return basename(repositoryRoot(dir, top, gitDir, commonDir));\n } catch {}\n return basename(dir);\n}\n\nfunction repositoryRoot(dir: string, top: string, gitDir?: string, commonDir?: string): string {\n if (!gitDir || !commonDir) return top;\n const physical = realpathSync(dir);\n const common = resolve(physical, commonDir);\n if (resolve(physical, gitDir) === common || basename(common) !== \".git\") return top;\n return dirname(common);\n}\n\nexport function hookCwd(data: Record<string, unknown> | null | undefined): string | undefined {\n if (!data || typeof data !== \"object\") return undefined;\n if (typeof data.cwd === \"string\" && data.cwd.trim()) return data.cwd;\n const roots = data.workspace_roots;\n if (Array.isArray(roots)) {\n for (const root of roots) {\n if (typeof root === \"string\" && root.trim()) return root;\n }\n }\n const projectDir =\n process.env[\"DEVIN_PROJECT_DIR\"] || process.env[\"CLAUDE_PROJECT_DIR\"];\n if (projectDir && projectDir.trim()) return projectDir;\n return undefined;\n}\n","#!/usr/bin/env node\nimport { resolveClientSecret } from \"../secret-store.js\";\n\nimport { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\nimport { hookCwd } from \"./_project.js\";\n\nconst exec = promisify(execFile);\n\nfunction isSdkChildContext(payload: unknown): boolean {\n if (process.env[\"AGENTMEMORY_SDK_CHILD\"] === \"1\") return true;\n if (!payload || typeof payload !== \"object\") return false;\n return (payload as { entrypoint?: unknown }).entrypoint === \"sdk-ts\";\n}\n\nconst REST_URL = process.env[\"AGENTMEMORY_URL\"] || \"http://localhost:3111\";\nconst SECRET = resolveClientSecret(REST_URL);\nconst TIMEOUT_MS = 1500;\n\nfunction authHeaders(): Record<string, string> {\n const h: Record<string, string> = { \"Content-Type\": \"application/json\" };\n if (SECRET) h[\"Authorization\"] = `Bearer ${SECRET}`;\n return h;\n}\n\nasync function git(args: string[], cwd: string): Promise<string | null> {\n try {\n const { stdout } = await exec(\"git\", args, { cwd, timeout: 1500 });\n return stdout.trim();\n } catch {\n return null;\n }\n}\n\nasync function main() {\n let input = \"\";\n for await (const chunk of process.stdin) {\n input += chunk;\n }\n\n let data: Record<string, unknown> = {};\n if (input.trim()) {\n try {\n data = JSON.parse(input);\n } catch {\n // Direct invocation from .git/hooks/post-commit may pass no stdin.\n }\n }\n\n if (!data || typeof data !== \"object\") data = {};\n if (isSdkChildContext(data)) return;\n\n const cwd =\n hookCwd(data) || process.env[\"AGENTMEMORY_CWD\"] || process.cwd();\n const sessionId =\n (data.session_id as string) ||\n process.env[\"AGENTMEMORY_SESSION_ID\"] ||\n undefined;\n\n const sha =\n process.env[\"AGENTMEMORY_COMMIT_SHA\"] ||\n (await git([\"rev-parse\", \"HEAD\"], cwd));\n if (!sha) return;\n\n const branch = await git([\"rev-parse\", \"--abbrev-ref\", \"HEAD\"], cwd);\n const repo = await git([\"config\", \"--get\", \"remote.origin.url\"], cwd);\n const message = await git([\"log\", \"-1\", \"--pretty=%B\", sha], cwd);\n const author = await git([\"log\", \"-1\", \"--pretty=%an <%ae>\", sha], cwd);\n const authoredAt = await git([\"log\", \"-1\", \"--pretty=%aI\", sha], cwd);\n const filesRaw = await git(\n [\"diff-tree\", \"--no-commit-id\", \"--name-only\", \"-r\", sha],\n cwd,\n );\n const files = filesRaw ? filesRaw.split(\"\\n\").filter(Boolean) : undefined;\n\n const body = {\n sessionId,\n sha,\n branch: branch || undefined,\n repo: repo || undefined,\n message: message || undefined,\n author: author || undefined,\n authoredAt: authoredAt || undefined,\n files,\n };\n\n try {\n await fetch(`${REST_URL}/agentmemory/session/commit`, {\n method: \"POST\",\n headers: authHeaders(),\n body: JSON.stringify(body),\n signal: AbortSignal.timeout(TIMEOUT_MS),\n });\n } catch {\n // best-effort\n }\n}\n\nmain().catch(() => process.exit(0));\n"],"mappings":"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"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { execSync } from "node:child_process";
|
|
3
|
-
import {
|
|
3
|
+
import { realpathSync } from "node:fs";
|
|
4
|
+
import { basename, dirname, resolve } from "node:path";
|
|
4
5
|
import { captureObservation, isDrainChild, runDrainChild, withEventId } from "./_capture.mjs";
|
|
5
6
|
//#region src/hooks/_capture-filter.ts
|
|
6
7
|
const DEFAULT_DENY_PATTERNS = [
|
|
@@ -49,7 +50,7 @@ function resolveProject(cwd) {
|
|
|
49
50
|
if (explicit && explicit.trim()) return explicit.trim();
|
|
50
51
|
const dir = cwd && cwd.trim() ? cwd : process.cwd();
|
|
51
52
|
try {
|
|
52
|
-
const top = execSync("git rev-parse --show-toplevel", {
|
|
53
|
+
const [top, gitDir, commonDir] = execSync("git rev-parse --show-toplevel --git-dir --git-common-dir", {
|
|
53
54
|
cwd: dir,
|
|
54
55
|
stdio: [
|
|
55
56
|
"ignore",
|
|
@@ -57,11 +58,18 @@ function resolveProject(cwd) {
|
|
|
57
58
|
"ignore"
|
|
58
59
|
],
|
|
59
60
|
timeout: 500
|
|
60
|
-
}).toString().trim();
|
|
61
|
-
if (top) return basename(top);
|
|
61
|
+
}).toString().trim().split(/\r?\n/);
|
|
62
|
+
if (top) return basename(repositoryRoot(dir, top, gitDir, commonDir));
|
|
62
63
|
} catch {}
|
|
63
64
|
return basename(dir);
|
|
64
65
|
}
|
|
66
|
+
function repositoryRoot(dir, top, gitDir, commonDir) {
|
|
67
|
+
if (!gitDir || !commonDir) return top;
|
|
68
|
+
const physical = realpathSync(dir);
|
|
69
|
+
const common = resolve(physical, commonDir);
|
|
70
|
+
if (resolve(physical, gitDir) === common || basename(common) !== ".git") return top;
|
|
71
|
+
return dirname(common);
|
|
72
|
+
}
|
|
65
73
|
function hookCwd(data) {
|
|
66
74
|
if (!data || typeof data !== "object") return void 0;
|
|
67
75
|
if (typeof data.cwd === "string" && data.cwd.trim()) return data.cwd;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"post-tool-failure.mjs","names":[],"sources":["../../src/hooks/_capture-filter.ts","../../src/hooks/_project.ts","../../src/hooks/post-tool-failure.ts"],"sourcesContent":["const DEFAULT_DENY_PATTERNS = [\n \"memory_*\",\n \"toolsearch\",\n \"listmcpresources\",\n \"fetchmcpresource\",\n];\n\nfunction parseEnvList(raw: string | undefined): string[] | undefined {\n if (!raw?.trim()) return undefined;\n return raw\n .split(/[,\\s]+/)\n .map((part) => part.trim())\n .filter(Boolean);\n}\n\nexport function bareToolName(toolName: string): string {\n const trimmed = toolName.trim();\n if (/^mcp__/i.test(trimmed)) {\n const parts = trimmed.split(\"__\");\n if (parts.length >= 3) return parts[parts.length - 1]!;\n }\n return trimmed;\n}\n\nfunction normalizePattern(pattern: string): string {\n return pattern.trim().toLowerCase();\n}\n\nfunction matchesPattern(toolName: string, pattern: string): boolean {\n const bare = bareToolName(toolName).toLowerCase();\n const full = toolName.trim().toLowerCase();\n const pat = normalizePattern(pattern);\n if (!pat.includes(\"*\")) return bare === pat || full === pat;\n const escaped = pat.replace(/[.+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n const re = new RegExp(`^${escaped.replace(/\\*/g, \".*\")}$`);\n return re.test(bare) || re.test(full);\n}\n\nfunction matchesAny(toolName: string, patterns: string[]): boolean {\n return patterns.some((pattern) => matchesPattern(toolName, pattern));\n}\n\nexport function shouldCaptureTool(toolName: unknown): boolean {\n if (typeof toolName !== \"string\" || !toolName.trim()) return true;\n\n const allow = parseEnvList(process.env[\"AGENTMEMORY_CAPTURE_ALLOW\"]);\n if (allow) return matchesAny(toolName, allow);\n\n const deny = [\n ...DEFAULT_DENY_PATTERNS,\n ...(parseEnvList(process.env[\"AGENTMEMORY_CAPTURE_DENY\"]) ?? []),\n ];\n return !matchesAny(toolName, deny);\n}\n\nexport function captureOutputMax(): number {\n const raw = process.env[\"AGENTMEMORY_CAPTURE_OUTPUT_MAX\"];\n if (!raw?.trim()) return 8000;\n const parsed = Number.parseInt(raw, 10);\n return Number.isFinite(parsed) && parsed > 0 ? parsed : 8000;\n}\n\nexport function truncateCaptureOutput(value: unknown, max: number): unknown {\n if (typeof value === \"string\" && value.length > max) {\n const suffix = \"\\n[...truncated]\";\n return max <= suffix.length\n ? suffix.slice(0, max)\n : value.slice(0, max - suffix.length) + suffix;\n }\n if (typeof value === \"object\" && value !== null) {\n const str = JSON.stringify(value);\n if (str.length > max) {\n const suffix = \"...[truncated]\";\n return max <= suffix.length\n ? suffix.slice(0, max)\n : str.slice(0, max - suffix.length) + suffix;\n }\n return value;\n }\n return value;\n}\n\nexport function preCompactBudget(): number {\n const raw = process.env[\"AGENTMEMORY_PRE_COMPACT_BUDGET\"];\n if (raw?.trim() === \"0\") return 0;\n if (!raw?.trim()) return 1500;\n const parsed = Number.parseInt(raw, 10);\n return Number.isFinite(parsed) && parsed >= 0 ? parsed : 1500;\n}\n","import { execSync } from \"node:child_process\";\nimport { basename } from \"node:path\";\n\n// Resolution order: AGENTMEMORY_PROJECT_NAME env → git toplevel basename → cwd basename.\nexport function resolveProject(cwd?: string): string {\n const explicit = process.env[\"AGENTMEMORY_PROJECT_NAME\"];\n if (explicit && explicit.trim()) return explicit.trim();\n const dir = cwd && cwd.trim() ? cwd : process.cwd();\n try {\n const top = execSync(\"git rev-parse --show-toplevel\", {\n cwd: dir,\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n timeout: 500,\n })\n .toString()\n .trim();\n if (top) return basename(top);\n } catch {}\n return basename(dir);\n}\n\nexport function hookCwd(data: Record<string, unknown> | null | undefined): string | undefined {\n if (!data || typeof data !== \"object\") return undefined;\n if (typeof data.cwd === \"string\" && data.cwd.trim()) return data.cwd;\n const roots = data.workspace_roots;\n if (Array.isArray(roots)) {\n for (const root of roots) {\n if (typeof root === \"string\" && root.trim()) return root;\n }\n }\n const projectDir =\n process.env[\"DEVIN_PROJECT_DIR\"] || process.env[\"CLAUDE_PROJECT_DIR\"];\n if (projectDir && projectDir.trim()) return projectDir;\n return undefined;\n}\n","#!/usr/bin/env node\nimport { shouldCaptureTool } from \"./_capture-filter.js\";\nimport { resolveProject, hookCwd } from \"./_project.js\";\nimport { captureObservation, isDrainChild, runDrainChild, withEventId } from \"./_capture.js\";\n\nfunction isSdkChildContext(payload: unknown): boolean {\n if (process.env[\"AGENTMEMORY_SDK_CHILD\"] === \"1\") return true;\n if (!payload || typeof payload !== \"object\") return false;\n return (payload as { entrypoint?: unknown }).entrypoint === \"sdk-ts\";\n}\n\nconst OBSERVE_TIMEOUT_MS = 3000;\nconst EXIT_CAP_MS = 3500;\n\nasync function main() {\n if (isDrainChild()) return runDrainChild();\n let input = \"\";\n for await (const chunk of process.stdin) {\n input += chunk;\n }\n\n let data: Record<string, unknown>;\n try {\n data = JSON.parse(input);\n } catch {\n return;\n }\n\n if (!data || typeof data !== \"object\") return;\n if (isSdkChildContext(data)) return;\n if (data.is_interrupt || data.isInterrupt) return;\n\n const sessionId = ((data.session_id || data.sessionId || data.conversation_id) as string) || \"unknown\";\n const toolName = data.tool_name ?? data.toolName;\n if (!shouldCaptureTool(toolName)) return;\n\n const toolInput = data.tool_input ?? data.toolArgs;\n const error = data.error ?? data.errorMessage;\n\n const cwd = hookCwd(data) || process.cwd();\n\n void captureObservation(\n withEventId(\n {\n hookType: \"post_tool_failure\",\n sessionId,\n project: resolveProject(cwd),\n cwd,\n timestamp: new Date().toISOString(),\n data: {\n tool_name: toolName,\n tool_input:\n typeof toolInput === \"string\"\n ? toolInput.slice(0, 4000)\n : JSON.stringify(toolInput ?? \"\").slice(0, 4000),\n error:\n typeof error === \"string\"\n ? error.slice(0, 4000)\n : JSON.stringify(error ?? \"\").slice(0, 4000),\n },\n },\n data,\n ),\n OBSERVE_TIMEOUT_MS,\n );\n setTimeout(() => process.exit(0), EXIT_CAP_MS).unref();\n}\n\nmain().catch(() => process.exit(0));\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"post-tool-failure.mjs","names":[],"sources":["../../src/hooks/_capture-filter.ts","../../src/hooks/_project.ts","../../src/hooks/post-tool-failure.ts"],"sourcesContent":["const DEFAULT_DENY_PATTERNS = [\n \"memory_*\",\n \"toolsearch\",\n \"listmcpresources\",\n \"fetchmcpresource\",\n];\n\nfunction parseEnvList(raw: string | undefined): string[] | undefined {\n if (!raw?.trim()) return undefined;\n return raw\n .split(/[,\\s]+/)\n .map((part) => part.trim())\n .filter(Boolean);\n}\n\nexport function bareToolName(toolName: string): string {\n const trimmed = toolName.trim();\n if (/^mcp__/i.test(trimmed)) {\n const parts = trimmed.split(\"__\");\n if (parts.length >= 3) return parts[parts.length - 1]!;\n }\n return trimmed;\n}\n\nfunction normalizePattern(pattern: string): string {\n return pattern.trim().toLowerCase();\n}\n\nfunction matchesPattern(toolName: string, pattern: string): boolean {\n const bare = bareToolName(toolName).toLowerCase();\n const full = toolName.trim().toLowerCase();\n const pat = normalizePattern(pattern);\n if (!pat.includes(\"*\")) return bare === pat || full === pat;\n const escaped = pat.replace(/[.+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n const re = new RegExp(`^${escaped.replace(/\\*/g, \".*\")}$`);\n return re.test(bare) || re.test(full);\n}\n\nfunction matchesAny(toolName: string, patterns: string[]): boolean {\n return patterns.some((pattern) => matchesPattern(toolName, pattern));\n}\n\nexport function shouldCaptureTool(toolName: unknown): boolean {\n if (typeof toolName !== \"string\" || !toolName.trim()) return true;\n\n const allow = parseEnvList(process.env[\"AGENTMEMORY_CAPTURE_ALLOW\"]);\n if (allow) return matchesAny(toolName, allow);\n\n const deny = [\n ...DEFAULT_DENY_PATTERNS,\n ...(parseEnvList(process.env[\"AGENTMEMORY_CAPTURE_DENY\"]) ?? []),\n ];\n return !matchesAny(toolName, deny);\n}\n\nexport function captureOutputMax(): number {\n const raw = process.env[\"AGENTMEMORY_CAPTURE_OUTPUT_MAX\"];\n if (!raw?.trim()) return 8000;\n const parsed = Number.parseInt(raw, 10);\n return Number.isFinite(parsed) && parsed > 0 ? parsed : 8000;\n}\n\nexport function truncateCaptureOutput(value: unknown, max: number): unknown {\n if (typeof value === \"string\" && value.length > max) {\n const suffix = \"\\n[...truncated]\";\n return max <= suffix.length\n ? suffix.slice(0, max)\n : value.slice(0, max - suffix.length) + suffix;\n }\n if (typeof value === \"object\" && value !== null) {\n const str = JSON.stringify(value);\n if (str.length > max) {\n const suffix = \"...[truncated]\";\n return max <= suffix.length\n ? suffix.slice(0, max)\n : str.slice(0, max - suffix.length) + suffix;\n }\n return value;\n }\n return value;\n}\n\nexport function preCompactBudget(): number {\n const raw = process.env[\"AGENTMEMORY_PRE_COMPACT_BUDGET\"];\n if (raw?.trim() === \"0\") return 0;\n if (!raw?.trim()) return 1500;\n const parsed = Number.parseInt(raw, 10);\n return Number.isFinite(parsed) && parsed >= 0 ? parsed : 1500;\n}\n","import { execSync } from \"node:child_process\";\nimport { realpathSync } from \"node:fs\";\nimport { basename, dirname, resolve } from \"node:path\";\n\n// Resolution order: AGENTMEMORY_PROJECT_NAME env → git repository basename → cwd basename.\nexport function resolveProject(cwd?: string): string {\n const explicit = process.env[\"AGENTMEMORY_PROJECT_NAME\"];\n if (explicit && explicit.trim()) return explicit.trim();\n const dir = cwd && cwd.trim() ? cwd : process.cwd();\n try {\n const [top, gitDir, commonDir] = execSync(\n \"git rev-parse --show-toplevel --git-dir --git-common-dir\",\n {\n cwd: dir,\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n timeout: 500,\n },\n )\n .toString()\n .trim()\n .split(/\\r?\\n/);\n if (top) return basename(repositoryRoot(dir, top, gitDir, commonDir));\n } catch {}\n return basename(dir);\n}\n\nfunction repositoryRoot(dir: string, top: string, gitDir?: string, commonDir?: string): string {\n if (!gitDir || !commonDir) return top;\n const physical = realpathSync(dir);\n const common = resolve(physical, commonDir);\n if (resolve(physical, gitDir) === common || basename(common) !== \".git\") return top;\n return dirname(common);\n}\n\nexport function hookCwd(data: Record<string, unknown> | null | undefined): string | undefined {\n if (!data || typeof data !== \"object\") return undefined;\n if (typeof data.cwd === \"string\" && data.cwd.trim()) return data.cwd;\n const roots = data.workspace_roots;\n if (Array.isArray(roots)) {\n for (const root of roots) {\n if (typeof root === \"string\" && root.trim()) return root;\n }\n }\n const projectDir =\n process.env[\"DEVIN_PROJECT_DIR\"] || process.env[\"CLAUDE_PROJECT_DIR\"];\n if (projectDir && projectDir.trim()) return projectDir;\n return undefined;\n}\n","#!/usr/bin/env node\nimport { shouldCaptureTool } from \"./_capture-filter.js\";\nimport { resolveProject, hookCwd } from \"./_project.js\";\nimport { captureObservation, isDrainChild, runDrainChild, withEventId } from \"./_capture.js\";\n\nfunction isSdkChildContext(payload: unknown): boolean {\n if (process.env[\"AGENTMEMORY_SDK_CHILD\"] === \"1\") return true;\n if (!payload || typeof payload !== \"object\") return false;\n return (payload as { entrypoint?: unknown }).entrypoint === \"sdk-ts\";\n}\n\nconst OBSERVE_TIMEOUT_MS = 3000;\nconst EXIT_CAP_MS = 3500;\n\nasync function main() {\n if (isDrainChild()) return runDrainChild();\n let input = \"\";\n for await (const chunk of process.stdin) {\n input += chunk;\n }\n\n let data: Record<string, unknown>;\n try {\n data = JSON.parse(input);\n } catch {\n return;\n }\n\n if (!data || typeof data !== \"object\") return;\n if (isSdkChildContext(data)) return;\n if (data.is_interrupt || data.isInterrupt) return;\n\n const sessionId = ((data.session_id || data.sessionId || data.conversation_id) as string) || \"unknown\";\n const toolName = data.tool_name ?? data.toolName;\n if (!shouldCaptureTool(toolName)) return;\n\n const toolInput = data.tool_input ?? data.toolArgs;\n const error = data.error ?? data.errorMessage;\n\n const cwd = hookCwd(data) || process.cwd();\n\n void captureObservation(\n withEventId(\n {\n hookType: \"post_tool_failure\",\n sessionId,\n project: resolveProject(cwd),\n cwd,\n timestamp: new Date().toISOString(),\n data: {\n tool_name: toolName,\n tool_input:\n typeof toolInput === \"string\"\n ? toolInput.slice(0, 4000)\n : JSON.stringify(toolInput ?? \"\").slice(0, 4000),\n error:\n typeof error === \"string\"\n ? error.slice(0, 4000)\n : JSON.stringify(error ?? \"\").slice(0, 4000),\n },\n },\n data,\n ),\n OBSERVE_TIMEOUT_MS,\n );\n setTimeout(() => process.exit(0), EXIT_CAP_MS).unref();\n}\n\nmain().catch(() => process.exit(0));\n"],"mappings":"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"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { execSync } from "node:child_process";
|
|
3
|
-
import {
|
|
3
|
+
import { realpathSync } from "node:fs";
|
|
4
|
+
import { basename, dirname, resolve } from "node:path";
|
|
4
5
|
import { captureObservation, isDrainChild, runDrainChild, withEventId } from "./_capture.mjs";
|
|
5
6
|
//#region src/hooks/_capture-filter.ts
|
|
6
7
|
const DEFAULT_DENY_PATTERNS = [
|
|
@@ -70,7 +71,7 @@ function resolveProject(cwd) {
|
|
|
70
71
|
if (explicit && explicit.trim()) return explicit.trim();
|
|
71
72
|
const dir = cwd && cwd.trim() ? cwd : process.cwd();
|
|
72
73
|
try {
|
|
73
|
-
const top = execSync("git rev-parse --show-toplevel", {
|
|
74
|
+
const [top, gitDir, commonDir] = execSync("git rev-parse --show-toplevel --git-dir --git-common-dir", {
|
|
74
75
|
cwd: dir,
|
|
75
76
|
stdio: [
|
|
76
77
|
"ignore",
|
|
@@ -78,11 +79,18 @@ function resolveProject(cwd) {
|
|
|
78
79
|
"ignore"
|
|
79
80
|
],
|
|
80
81
|
timeout: 500
|
|
81
|
-
}).toString().trim();
|
|
82
|
-
if (top) return basename(top);
|
|
82
|
+
}).toString().trim().split(/\r?\n/);
|
|
83
|
+
if (top) return basename(repositoryRoot(dir, top, gitDir, commonDir));
|
|
83
84
|
} catch {}
|
|
84
85
|
return basename(dir);
|
|
85
86
|
}
|
|
87
|
+
function repositoryRoot(dir, top, gitDir, commonDir) {
|
|
88
|
+
if (!gitDir || !commonDir) return top;
|
|
89
|
+
const physical = realpathSync(dir);
|
|
90
|
+
const common = resolve(physical, commonDir);
|
|
91
|
+
if (resolve(physical, gitDir) === common || basename(common) !== ".git") return top;
|
|
92
|
+
return dirname(common);
|
|
93
|
+
}
|
|
86
94
|
function hookCwd(data) {
|
|
87
95
|
if (!data || typeof data !== "object") return void 0;
|
|
88
96
|
if (typeof data.cwd === "string" && data.cwd.trim()) return data.cwd;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"post-tool-use.mjs","names":[],"sources":["../../src/hooks/_capture-filter.ts","../../src/hooks/_project.ts","../../src/hooks/post-tool-use.ts"],"sourcesContent":["const DEFAULT_DENY_PATTERNS = [\n \"memory_*\",\n \"toolsearch\",\n \"listmcpresources\",\n \"fetchmcpresource\",\n];\n\nfunction parseEnvList(raw: string | undefined): string[] | undefined {\n if (!raw?.trim()) return undefined;\n return raw\n .split(/[,\\s]+/)\n .map((part) => part.trim())\n .filter(Boolean);\n}\n\nexport function bareToolName(toolName: string): string {\n const trimmed = toolName.trim();\n if (/^mcp__/i.test(trimmed)) {\n const parts = trimmed.split(\"__\");\n if (parts.length >= 3) return parts[parts.length - 1]!;\n }\n return trimmed;\n}\n\nfunction normalizePattern(pattern: string): string {\n return pattern.trim().toLowerCase();\n}\n\nfunction matchesPattern(toolName: string, pattern: string): boolean {\n const bare = bareToolName(toolName).toLowerCase();\n const full = toolName.trim().toLowerCase();\n const pat = normalizePattern(pattern);\n if (!pat.includes(\"*\")) return bare === pat || full === pat;\n const escaped = pat.replace(/[.+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n const re = new RegExp(`^${escaped.replace(/\\*/g, \".*\")}$`);\n return re.test(bare) || re.test(full);\n}\n\nfunction matchesAny(toolName: string, patterns: string[]): boolean {\n return patterns.some((pattern) => matchesPattern(toolName, pattern));\n}\n\nexport function shouldCaptureTool(toolName: unknown): boolean {\n if (typeof toolName !== \"string\" || !toolName.trim()) return true;\n\n const allow = parseEnvList(process.env[\"AGENTMEMORY_CAPTURE_ALLOW\"]);\n if (allow) return matchesAny(toolName, allow);\n\n const deny = [\n ...DEFAULT_DENY_PATTERNS,\n ...(parseEnvList(process.env[\"AGENTMEMORY_CAPTURE_DENY\"]) ?? []),\n ];\n return !matchesAny(toolName, deny);\n}\n\nexport function captureOutputMax(): number {\n const raw = process.env[\"AGENTMEMORY_CAPTURE_OUTPUT_MAX\"];\n if (!raw?.trim()) return 8000;\n const parsed = Number.parseInt(raw, 10);\n return Number.isFinite(parsed) && parsed > 0 ? parsed : 8000;\n}\n\nexport function truncateCaptureOutput(value: unknown, max: number): unknown {\n if (typeof value === \"string\" && value.length > max) {\n const suffix = \"\\n[...truncated]\";\n return max <= suffix.length\n ? suffix.slice(0, max)\n : value.slice(0, max - suffix.length) + suffix;\n }\n if (typeof value === \"object\" && value !== null) {\n const str = JSON.stringify(value);\n if (str.length > max) {\n const suffix = \"...[truncated]\";\n return max <= suffix.length\n ? suffix.slice(0, max)\n : str.slice(0, max - suffix.length) + suffix;\n }\n return value;\n }\n return value;\n}\n\nexport function preCompactBudget(): number {\n const raw = process.env[\"AGENTMEMORY_PRE_COMPACT_BUDGET\"];\n if (raw?.trim() === \"0\") return 0;\n if (!raw?.trim()) return 1500;\n const parsed = Number.parseInt(raw, 10);\n return Number.isFinite(parsed) && parsed >= 0 ? parsed : 1500;\n}\n","import { execSync } from \"node:child_process\";\nimport { basename } from \"node:path\";\n\n// Resolution order: AGENTMEMORY_PROJECT_NAME env → git toplevel basename → cwd basename.\nexport function resolveProject(cwd?: string): string {\n const explicit = process.env[\"AGENTMEMORY_PROJECT_NAME\"];\n if (explicit && explicit.trim()) return explicit.trim();\n const dir = cwd && cwd.trim() ? cwd : process.cwd();\n try {\n const top = execSync(\"git rev-parse --show-toplevel\", {\n cwd: dir,\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n timeout: 500,\n })\n .toString()\n .trim();\n if (top) return basename(top);\n } catch {}\n return basename(dir);\n}\n\nexport function hookCwd(data: Record<string, unknown> | null | undefined): string | undefined {\n if (!data || typeof data !== \"object\") return undefined;\n if (typeof data.cwd === \"string\" && data.cwd.trim()) return data.cwd;\n const roots = data.workspace_roots;\n if (Array.isArray(roots)) {\n for (const root of roots) {\n if (typeof root === \"string\" && root.trim()) return root;\n }\n }\n const projectDir =\n process.env[\"DEVIN_PROJECT_DIR\"] || process.env[\"CLAUDE_PROJECT_DIR\"];\n if (projectDir && projectDir.trim()) return projectDir;\n return undefined;\n}\n","#!/usr/bin/env node\nimport {\n captureOutputMax,\n shouldCaptureTool,\n truncateCaptureOutput,\n} from \"./_capture-filter.js\";\nimport { resolveProject, hookCwd } from \"./_project.js\";\nimport { captureObservation, isDrainChild, runDrainChild, withEventId } from \"./_capture.js\";\n\nfunction isSdkChildContext(payload: unknown): boolean {\n if (process.env[\"AGENTMEMORY_SDK_CHILD\"] === \"1\") return true;\n if (!payload || typeof payload !== \"object\") return false;\n return (payload as { entrypoint?: unknown }).entrypoint === \"sdk-ts\";\n}\n\nconst OBSERVE_TIMEOUT_MS = 3000;\nconst EXIT_CAP_MS = 3500;\n\nasync function main() {\n if (isDrainChild()) return runDrainChild();\n let input = \"\";\n for await (const chunk of process.stdin) {\n input += chunk;\n }\n\n let data: Record<string, unknown>;\n try {\n data = JSON.parse(input);\n } catch {\n return;\n }\n\n if (!data || typeof data !== \"object\") return;\n if (isSdkChildContext(data)) return;\n\n const sessionId = ((data.session_id || data.sessionId || data.conversation_id) as string) || \"unknown\";\n const toolName = data.tool_name ?? data.toolName;\n if (!shouldCaptureTool(toolName)) return;\n\n const toolInput = data.tool_input ?? data.toolArgs;\n\n const { imageData, cleanOutput } = extractImageData(toolOutput(data));\n const cwd = hookCwd(data) || process.cwd();\n const outputMax = captureOutputMax();\n\n const toolOutputText = truncateCaptureOutput(cleanOutput, outputMax);\n const body = withEventId(\n {\n hookType: \"post_tool_use\",\n sessionId,\n project: resolveProject(cwd),\n cwd,\n timestamp: new Date().toISOString(),\n data: {\n tool_name: toolName,\n tool_input: toolInput,\n tool_output: toolOutputText,\n ...(imageData ? { image_data: imageData } : {}),\n },\n },\n data,\n { tool_name: toolName, tool_input: toolInput, tool_output: toolOutputText, image_data: imageData },\n );\n void captureObservation(body, OBSERVE_TIMEOUT_MS);\n setTimeout(() => process.exit(0), EXIT_CAP_MS).unref();\n}\n\nfunction toolOutput(data: Record<string, unknown>): unknown {\n if (data.tool_response !== undefined) return data.tool_response;\n if (data.tool_output !== undefined) return data.tool_output;\n const result = data.tool_result ?? data.toolResult;\n if (typeof result === \"object\" && result !== null) {\n const obj = result as Record<string, unknown>;\n return obj.text_result_for_llm ?? obj.textResultForLlm ?? result;\n }\n return result;\n}\n\nfunction isBase64Image(val: unknown): val is string {\n return typeof val === \"string\" && (\n val.startsWith(\"data:image/\") ||\n val.startsWith(\"iVBORw0KGgo\") ||\n val.startsWith(\"/9j/\")\n );\n}\n\nfunction extractImageData(output: unknown): { imageData: string | undefined; cleanOutput: unknown } {\n if (isBase64Image(output)) {\n return { imageData: output, cleanOutput: \"[image data extracted]\" };\n }\n\n if (typeof output === \"object\" && output !== null && !Array.isArray(output)) {\n const obj = output as Record<string, unknown>;\n let imageData: string | undefined;\n const clean: Record<string, unknown> = {};\n\n for (const [key, val] of Object.entries(obj)) {\n if (!imageData && isBase64Image(val)) {\n imageData = val;\n clean[key] = \"[image data extracted]\";\n } else {\n clean[key] = val;\n }\n }\n\n return { imageData, cleanOutput: clean };\n }\n\n return { imageData: undefined, cleanOutput: output };\n}\n\nmain().catch(() => process.exit(0));\n"],"mappings":";;;;;AAAA,MAAM,wBAAwB;CAC5B;CACA;CACA;CACA;CACD;AAED,SAAS,aAAa,KAA+C;AACnE,KAAI,CAAC,KAAK,MAAM,CAAE,QAAO,KAAA;AACzB,QAAO,IACJ,MAAM,SAAS,CACf,KAAK,SAAS,KAAK,MAAM,CAAC,CAC1B,OAAO,QAAQ;;AAGpB,SAAgB,aAAa,UAA0B;CACrD,MAAM,UAAU,SAAS,MAAM;AAC/B,KAAI,UAAU,KAAK,QAAQ,EAAE;EAC3B,MAAM,QAAQ,QAAQ,MAAM,KAAK;AACjC,MAAI,MAAM,UAAU,EAAG,QAAO,MAAM,MAAM,SAAS;;AAErD,QAAO;;AAGT,SAAS,iBAAiB,SAAyB;AACjD,QAAO,QAAQ,MAAM,CAAC,aAAa;;AAGrC,SAAS,eAAe,UAAkB,SAA0B;CAClE,MAAM,OAAO,aAAa,SAAS,CAAC,aAAa;CACjD,MAAM,OAAO,SAAS,MAAM,CAAC,aAAa;CAC1C,MAAM,MAAM,iBAAiB,QAAQ;AACrC,KAAI,CAAC,IAAI,SAAS,IAAI,CAAE,QAAO,SAAS,OAAO,SAAS;CACxD,MAAM,UAAU,IAAI,QAAQ,sBAAsB,OAAO;CACzD,MAAM,KAAK,IAAI,OAAO,IAAI,QAAQ,QAAQ,OAAO,KAAK,CAAC,GAAG;AAC1D,QAAO,GAAG,KAAK,KAAK,IAAI,GAAG,KAAK,KAAK;;AAGvC,SAAS,WAAW,UAAkB,UAA6B;AACjE,QAAO,SAAS,MAAM,YAAY,eAAe,UAAU,QAAQ,CAAC;;AAGtE,SAAgB,kBAAkB,UAA4B;AAC5D,KAAI,OAAO,aAAa,YAAY,CAAC,SAAS,MAAM,CAAE,QAAO;CAE7D,MAAM,QAAQ,aAAa,QAAQ,IAAI,6BAA6B;AACpE,KAAI,MAAO,QAAO,WAAW,UAAU,MAAM;AAM7C,QAAO,CAAC,WAAW,UAAU,CAH3B,GAAG,uBACH,GAAI,aAAa,QAAQ,IAAI,4BAA4B,IAAI,EAAE,CAEhC,CAAC;;AAGpC,SAAgB,mBAA2B;CACzC,MAAM,MAAM,QAAQ,IAAI;AACxB,KAAI,CAAC,KAAK,MAAM,CAAE,QAAO;CACzB,MAAM,SAAS,OAAO,SAAS,KAAK,GAAG;AACvC,QAAO,OAAO,SAAS,OAAO,IAAI,SAAS,IAAI,SAAS;;AAG1D,SAAgB,sBAAsB,OAAgB,KAAsB;AAC1E,KAAI,OAAO,UAAU,YAAY,MAAM,SAAS,KAAK;EACnD,MAAM,SAAS;AACf,SAAO,OAAO,KACV,OAAO,MAAM,GAAG,IAAI,GACpB,MAAM,MAAM,GAAG,MAAM,GAAc,GAAG;;AAE5C,KAAI,OAAO,UAAU,YAAY,UAAU,MAAM;EAC/C,MAAM,MAAM,KAAK,UAAU,MAAM;AACjC,MAAI,IAAI,SAAS,KAAK;GACpB,MAAM,SAAS;AACf,UAAO,OAAO,KACV,OAAO,MAAM,GAAG,IAAI,GACpB,IAAI,MAAM,GAAG,MAAM,GAAc,GAAG;;AAE1C,SAAO;;AAET,QAAO;;;;AC3ET,SAAgB,eAAe,KAAsB;CACnD,MAAM,WAAW,QAAQ,IAAI;AAC7B,KAAI,YAAY,SAAS,MAAM,CAAE,QAAO,SAAS,MAAM;CACvD,MAAM,MAAM,OAAO,IAAI,MAAM,GAAG,MAAM,QAAQ,KAAK;AACnD,KAAI;EACF,MAAM,MAAM,SAAS,iCAAiC;GACpD,KAAK;GACL,OAAO;IAAC;IAAU;IAAQ;IAAS;GACnC,SAAS;GACV,CAAC,CACC,UAAU,CACV,MAAM;AACT,MAAI,IAAK,QAAO,SAAS,IAAI;SACvB;AACR,QAAO,SAAS,IAAI;;AAGtB,SAAgB,QAAQ,MAAsE;AAC5F,KAAI,CAAC,QAAQ,OAAO,SAAS,SAAU,QAAO,KAAA;AAC9C,KAAI,OAAO,KAAK,QAAQ,YAAY,KAAK,IAAI,MAAM,CAAE,QAAO,KAAK;CACjE,MAAM,QAAQ,KAAK;AACnB,KAAI,MAAM,QAAQ,MAAM;OACjB,MAAM,QAAQ,MACjB,KAAI,OAAO,SAAS,YAAY,KAAK,MAAM,CAAE,QAAO;;CAGxD,MAAM,aACJ,QAAQ,IAAI,wBAAwB,QAAQ,IAAI;AAClD,KAAI,cAAc,WAAW,MAAM,CAAE,QAAO;;;;ACvB9C,SAAS,kBAAkB,SAA2B;AACpD,KAAI,QAAQ,IAAI,6BAA6B,IAAK,QAAO;AACzD,KAAI,CAAC,WAAW,OAAO,YAAY,SAAU,QAAO;AACpD,QAAQ,QAAqC,eAAe;;AAG9D,MAAM,qBAAqB;AAC3B,MAAM,cAAc;AAEpB,eAAe,OAAO;AACpB,KAAI,cAAc,CAAE,QAAO,eAAe;CAC1C,IAAI,QAAQ;AACZ,YAAW,MAAM,SAAS,QAAQ,MAChC,UAAS;CAGX,IAAI;AACJ,KAAI;AACF,SAAO,KAAK,MAAM,MAAM;SAClB;AACN;;AAGF,KAAI,CAAC,QAAQ,OAAO,SAAS,SAAU;AACvC,KAAI,kBAAkB,KAAK,CAAE;CAE7B,MAAM,YAAc,KAAK,cAAc,KAAK,aAAa,KAAK,mBAA+B;CAC7F,MAAM,WAAW,KAAK,aAAa,KAAK;AACxC,KAAI,CAAC,kBAAkB,SAAS,CAAE;CAElC,MAAM,YAAY,KAAK,cAAc,KAAK;CAE1C,MAAM,EAAE,WAAW,gBAAgB,iBAAiB,WAAW,KAAK,CAAC;CACrE,MAAM,MAAM,QAAQ,KAAK,IAAI,QAAQ,KAAK;CAG1C,MAAM,iBAAiB,sBAAsB,aAF3B,kBAEiD,CAAC;AAkB/D,oBAjBQ,YACX;EACE,UAAU;EACV;EACA,SAAS,eAAe,IAAI;EAC5B;EACA,4BAAW,IAAI,MAAM,EAAC,aAAa;EACnC,MAAM;GACJ,WAAW;GACX,YAAY;GACZ,aAAa;GACb,GAAI,YAAY,EAAE,YAAY,WAAW,GAAG,EAAE;GAC/C;EACF,EACD,MACA;EAAE,WAAW;EAAU,YAAY;EAAW,aAAa;EAAgB,YAAY;EAAW,CAExE,EAAE,mBAAmB;AACjD,kBAAiB,QAAQ,KAAK,EAAE,EAAE,YAAY,CAAC,OAAO;;AAGxD,SAAS,WAAW,MAAwC;AAC1D,KAAI,KAAK,kBAAkB,KAAA,EAAW,QAAO,KAAK;AAClD,KAAI,KAAK,gBAAgB,KAAA,EAAW,QAAO,KAAK;CAChD,MAAM,SAAS,KAAK,eAAe,KAAK;AACxC,KAAI,OAAO,WAAW,YAAY,WAAW,MAAM;EACjD,MAAM,MAAM;AACZ,SAAO,IAAI,uBAAuB,IAAI,oBAAoB;;AAE5D,QAAO;;AAGT,SAAS,cAAc,KAA6B;AAClD,QAAO,OAAO,QAAQ,aACpB,IAAI,WAAW,cAAc,IAC7B,IAAI,WAAW,cAAc,IAC7B,IAAI,WAAW,OAAO;;AAI1B,SAAS,iBAAiB,QAA0E;AAClG,KAAI,cAAc,OAAO,CACvB,QAAO;EAAE,WAAW;EAAQ,aAAa;EAA0B;AAGrE,KAAI,OAAO,WAAW,YAAY,WAAW,QAAQ,CAAC,MAAM,QAAQ,OAAO,EAAE;EAC3E,MAAM,MAAM;EACZ,IAAI;EACJ,MAAM,QAAiC,EAAE;AAEzC,OAAK,MAAM,CAAC,KAAK,QAAQ,OAAO,QAAQ,IAAI,CAC1C,KAAI,CAAC,aAAa,cAAc,IAAI,EAAE;AACpC,eAAY;AACZ,SAAM,OAAO;QAEb,OAAM,OAAO;AAIjB,SAAO;GAAE;GAAW,aAAa;GAAO;;AAG1C,QAAO;EAAE,WAAW,KAAA;EAAW,aAAa;EAAQ;;AAGtD,MAAM,CAAC,YAAY,QAAQ,KAAK,EAAE,CAAC"}
|
|
1
|
+
{"version":3,"file":"post-tool-use.mjs","names":[],"sources":["../../src/hooks/_capture-filter.ts","../../src/hooks/_project.ts","../../src/hooks/post-tool-use.ts"],"sourcesContent":["const DEFAULT_DENY_PATTERNS = [\n \"memory_*\",\n \"toolsearch\",\n \"listmcpresources\",\n \"fetchmcpresource\",\n];\n\nfunction parseEnvList(raw: string | undefined): string[] | undefined {\n if (!raw?.trim()) return undefined;\n return raw\n .split(/[,\\s]+/)\n .map((part) => part.trim())\n .filter(Boolean);\n}\n\nexport function bareToolName(toolName: string): string {\n const trimmed = toolName.trim();\n if (/^mcp__/i.test(trimmed)) {\n const parts = trimmed.split(\"__\");\n if (parts.length >= 3) return parts[parts.length - 1]!;\n }\n return trimmed;\n}\n\nfunction normalizePattern(pattern: string): string {\n return pattern.trim().toLowerCase();\n}\n\nfunction matchesPattern(toolName: string, pattern: string): boolean {\n const bare = bareToolName(toolName).toLowerCase();\n const full = toolName.trim().toLowerCase();\n const pat = normalizePattern(pattern);\n if (!pat.includes(\"*\")) return bare === pat || full === pat;\n const escaped = pat.replace(/[.+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n const re = new RegExp(`^${escaped.replace(/\\*/g, \".*\")}$`);\n return re.test(bare) || re.test(full);\n}\n\nfunction matchesAny(toolName: string, patterns: string[]): boolean {\n return patterns.some((pattern) => matchesPattern(toolName, pattern));\n}\n\nexport function shouldCaptureTool(toolName: unknown): boolean {\n if (typeof toolName !== \"string\" || !toolName.trim()) return true;\n\n const allow = parseEnvList(process.env[\"AGENTMEMORY_CAPTURE_ALLOW\"]);\n if (allow) return matchesAny(toolName, allow);\n\n const deny = [\n ...DEFAULT_DENY_PATTERNS,\n ...(parseEnvList(process.env[\"AGENTMEMORY_CAPTURE_DENY\"]) ?? []),\n ];\n return !matchesAny(toolName, deny);\n}\n\nexport function captureOutputMax(): number {\n const raw = process.env[\"AGENTMEMORY_CAPTURE_OUTPUT_MAX\"];\n if (!raw?.trim()) return 8000;\n const parsed = Number.parseInt(raw, 10);\n return Number.isFinite(parsed) && parsed > 0 ? parsed : 8000;\n}\n\nexport function truncateCaptureOutput(value: unknown, max: number): unknown {\n if (typeof value === \"string\" && value.length > max) {\n const suffix = \"\\n[...truncated]\";\n return max <= suffix.length\n ? suffix.slice(0, max)\n : value.slice(0, max - suffix.length) + suffix;\n }\n if (typeof value === \"object\" && value !== null) {\n const str = JSON.stringify(value);\n if (str.length > max) {\n const suffix = \"...[truncated]\";\n return max <= suffix.length\n ? suffix.slice(0, max)\n : str.slice(0, max - suffix.length) + suffix;\n }\n return value;\n }\n return value;\n}\n\nexport function preCompactBudget(): number {\n const raw = process.env[\"AGENTMEMORY_PRE_COMPACT_BUDGET\"];\n if (raw?.trim() === \"0\") return 0;\n if (!raw?.trim()) return 1500;\n const parsed = Number.parseInt(raw, 10);\n return Number.isFinite(parsed) && parsed >= 0 ? parsed : 1500;\n}\n","import { execSync } from \"node:child_process\";\nimport { realpathSync } from \"node:fs\";\nimport { basename, dirname, resolve } from \"node:path\";\n\n// Resolution order: AGENTMEMORY_PROJECT_NAME env → git repository basename → cwd basename.\nexport function resolveProject(cwd?: string): string {\n const explicit = process.env[\"AGENTMEMORY_PROJECT_NAME\"];\n if (explicit && explicit.trim()) return explicit.trim();\n const dir = cwd && cwd.trim() ? cwd : process.cwd();\n try {\n const [top, gitDir, commonDir] = execSync(\n \"git rev-parse --show-toplevel --git-dir --git-common-dir\",\n {\n cwd: dir,\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n timeout: 500,\n },\n )\n .toString()\n .trim()\n .split(/\\r?\\n/);\n if (top) return basename(repositoryRoot(dir, top, gitDir, commonDir));\n } catch {}\n return basename(dir);\n}\n\nfunction repositoryRoot(dir: string, top: string, gitDir?: string, commonDir?: string): string {\n if (!gitDir || !commonDir) return top;\n const physical = realpathSync(dir);\n const common = resolve(physical, commonDir);\n if (resolve(physical, gitDir) === common || basename(common) !== \".git\") return top;\n return dirname(common);\n}\n\nexport function hookCwd(data: Record<string, unknown> | null | undefined): string | undefined {\n if (!data || typeof data !== \"object\") return undefined;\n if (typeof data.cwd === \"string\" && data.cwd.trim()) return data.cwd;\n const roots = data.workspace_roots;\n if (Array.isArray(roots)) {\n for (const root of roots) {\n if (typeof root === \"string\" && root.trim()) return root;\n }\n }\n const projectDir =\n process.env[\"DEVIN_PROJECT_DIR\"] || process.env[\"CLAUDE_PROJECT_DIR\"];\n if (projectDir && projectDir.trim()) return projectDir;\n return undefined;\n}\n","#!/usr/bin/env node\nimport {\n captureOutputMax,\n shouldCaptureTool,\n truncateCaptureOutput,\n} from \"./_capture-filter.js\";\nimport { resolveProject, hookCwd } from \"./_project.js\";\nimport { captureObservation, isDrainChild, runDrainChild, withEventId } from \"./_capture.js\";\n\nfunction isSdkChildContext(payload: unknown): boolean {\n if (process.env[\"AGENTMEMORY_SDK_CHILD\"] === \"1\") return true;\n if (!payload || typeof payload !== \"object\") return false;\n return (payload as { entrypoint?: unknown }).entrypoint === \"sdk-ts\";\n}\n\nconst OBSERVE_TIMEOUT_MS = 3000;\nconst EXIT_CAP_MS = 3500;\n\nasync function main() {\n if (isDrainChild()) return runDrainChild();\n let input = \"\";\n for await (const chunk of process.stdin) {\n input += chunk;\n }\n\n let data: Record<string, unknown>;\n try {\n data = JSON.parse(input);\n } catch {\n return;\n }\n\n if (!data || typeof data !== \"object\") return;\n if (isSdkChildContext(data)) return;\n\n const sessionId = ((data.session_id || data.sessionId || data.conversation_id) as string) || \"unknown\";\n const toolName = data.tool_name ?? data.toolName;\n if (!shouldCaptureTool(toolName)) return;\n\n const toolInput = data.tool_input ?? data.toolArgs;\n\n const { imageData, cleanOutput } = extractImageData(toolOutput(data));\n const cwd = hookCwd(data) || process.cwd();\n const outputMax = captureOutputMax();\n\n const toolOutputText = truncateCaptureOutput(cleanOutput, outputMax);\n const body = withEventId(\n {\n hookType: \"post_tool_use\",\n sessionId,\n project: resolveProject(cwd),\n cwd,\n timestamp: new Date().toISOString(),\n data: {\n tool_name: toolName,\n tool_input: toolInput,\n tool_output: toolOutputText,\n ...(imageData ? { image_data: imageData } : {}),\n },\n },\n data,\n { tool_name: toolName, tool_input: toolInput, tool_output: toolOutputText, image_data: imageData },\n );\n void captureObservation(body, OBSERVE_TIMEOUT_MS);\n setTimeout(() => process.exit(0), EXIT_CAP_MS).unref();\n}\n\nfunction toolOutput(data: Record<string, unknown>): unknown {\n if (data.tool_response !== undefined) return data.tool_response;\n if (data.tool_output !== undefined) return data.tool_output;\n const result = data.tool_result ?? data.toolResult;\n if (typeof result === \"object\" && result !== null) {\n const obj = result as Record<string, unknown>;\n return obj.text_result_for_llm ?? obj.textResultForLlm ?? result;\n }\n return result;\n}\n\nfunction isBase64Image(val: unknown): val is string {\n return typeof val === \"string\" && (\n val.startsWith(\"data:image/\") ||\n val.startsWith(\"iVBORw0KGgo\") ||\n val.startsWith(\"/9j/\")\n );\n}\n\nfunction extractImageData(output: unknown): { imageData: string | undefined; cleanOutput: unknown } {\n if (isBase64Image(output)) {\n return { imageData: output, cleanOutput: \"[image data extracted]\" };\n }\n\n if (typeof output === \"object\" && output !== null && !Array.isArray(output)) {\n const obj = output as Record<string, unknown>;\n let imageData: string | undefined;\n const clean: Record<string, unknown> = {};\n\n for (const [key, val] of Object.entries(obj)) {\n if (!imageData && isBase64Image(val)) {\n imageData = val;\n clean[key] = \"[image data extracted]\";\n } else {\n clean[key] = val;\n }\n }\n\n return { imageData, cleanOutput: clean };\n }\n\n return { imageData: undefined, cleanOutput: output };\n}\n\nmain().catch(() => process.exit(0));\n"],"mappings":"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"}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { readFileSync } from "node:fs";
|
|
2
|
+
import { readFileSync, realpathSync } from "node:fs";
|
|
3
3
|
import { homedir } from "node:os";
|
|
4
|
-
import { basename, join } from "node:path";
|
|
4
|
+
import { basename, dirname, join, resolve } from "node:path";
|
|
5
5
|
import { execSync } from "node:child_process";
|
|
6
6
|
//#region src/secret-store.ts
|
|
7
7
|
const SECRET_KEY = "AGENTMEMORY_SECRET";
|
|
8
8
|
function agentmemoryHomeDir() {
|
|
9
|
-
return join(homedir(), ".agentmemory");
|
|
9
|
+
return process.env["AGENTMEMORY_DATA_DIR"] || join(homedir(), ".agentmemory");
|
|
10
10
|
}
|
|
11
11
|
function secretFilePath() {
|
|
12
12
|
return join(agentmemoryHomeDir(), "secret");
|
|
@@ -30,7 +30,7 @@ function unquote(value) {
|
|
|
30
30
|
function readEnvFileSecret() {
|
|
31
31
|
let content;
|
|
32
32
|
try {
|
|
33
|
-
content = readFileSync(join(
|
|
33
|
+
content = readFileSync(process.env["AGENTMEMORY_ENV_FILE"] || join(homedir(), ".agentmemory", ".env"), "utf-8");
|
|
34
34
|
} catch {
|
|
35
35
|
return "";
|
|
36
36
|
}
|
|
@@ -46,13 +46,29 @@ function readEnvFileSecret() {
|
|
|
46
46
|
}
|
|
47
47
|
return found;
|
|
48
48
|
}
|
|
49
|
-
function
|
|
49
|
+
function readSecretFile(path) {
|
|
50
50
|
try {
|
|
51
|
-
return usable(readFileSync(
|
|
51
|
+
return usable(readFileSync(path, "utf-8"));
|
|
52
52
|
} catch {
|
|
53
53
|
return "";
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
|
+
function legacyDataDirSecretPaths(env = process.env) {
|
|
57
|
+
const dataDir = env["AGENTMEMORY_DATA_DIR"];
|
|
58
|
+
if (!dataDir) return [];
|
|
59
|
+
const paths = [
|
|
60
|
+
join(dataDir, "secret"),
|
|
61
|
+
join(dataDir, ".hmac"),
|
|
62
|
+
join(dataDir, "..", ".hmac")
|
|
63
|
+
];
|
|
64
|
+
return [...new Set(paths.filter((path) => path !== secretFilePath()))];
|
|
65
|
+
}
|
|
66
|
+
function readStoredSecret(env = process.env) {
|
|
67
|
+
const canonicalPath = canonicalSecretPath();
|
|
68
|
+
const localPath = secretFilePath();
|
|
69
|
+
const legacyPaths = legacyDataDirSecretPaths(env);
|
|
70
|
+
return readSecretFile(canonicalPath) || (localPath === canonicalPath ? "" : readSecretFile(localPath)) || legacyPaths.map(readSecretFile).find(Boolean) || "";
|
|
71
|
+
}
|
|
56
72
|
function isLoopbackUrl(url) {
|
|
57
73
|
let hostname;
|
|
58
74
|
try {
|
|
@@ -66,8 +82,12 @@ function isLoopbackUrl(url) {
|
|
|
66
82
|
function resolveClientSecret(baseUrl, env = process.env) {
|
|
67
83
|
const fromEnv = usable(env[SECRET_KEY]);
|
|
68
84
|
if (fromEnv) return fromEnv;
|
|
85
|
+
if (env.AGENTMEMORY_CREDENTIAL_MODE === "explicit") return "";
|
|
69
86
|
if (!isLoopbackUrl(baseUrl)) return "";
|
|
70
|
-
return readEnvFileSecret() || readStoredSecret();
|
|
87
|
+
return readEnvFileSecret() || readStoredSecret(env);
|
|
88
|
+
}
|
|
89
|
+
function canonicalSecretPath() {
|
|
90
|
+
return join(homedir(), ".agentmemory", "secret");
|
|
71
91
|
}
|
|
72
92
|
//#endregion
|
|
73
93
|
//#region src/hooks/_capture-filter.ts
|
|
@@ -85,7 +105,7 @@ function resolveProject(cwd) {
|
|
|
85
105
|
if (explicit && explicit.trim()) return explicit.trim();
|
|
86
106
|
const dir = cwd && cwd.trim() ? cwd : process.cwd();
|
|
87
107
|
try {
|
|
88
|
-
const top = execSync("git rev-parse --show-toplevel", {
|
|
108
|
+
const [top, gitDir, commonDir] = execSync("git rev-parse --show-toplevel --git-dir --git-common-dir", {
|
|
89
109
|
cwd: dir,
|
|
90
110
|
stdio: [
|
|
91
111
|
"ignore",
|
|
@@ -93,11 +113,18 @@ function resolveProject(cwd) {
|
|
|
93
113
|
"ignore"
|
|
94
114
|
],
|
|
95
115
|
timeout: 500
|
|
96
|
-
}).toString().trim();
|
|
97
|
-
if (top) return basename(top);
|
|
116
|
+
}).toString().trim().split(/\r?\n/);
|
|
117
|
+
if (top) return basename(repositoryRoot(dir, top, gitDir, commonDir));
|
|
98
118
|
} catch {}
|
|
99
119
|
return basename(dir);
|
|
100
120
|
}
|
|
121
|
+
function repositoryRoot(dir, top, gitDir, commonDir) {
|
|
122
|
+
if (!gitDir || !commonDir) return top;
|
|
123
|
+
const physical = realpathSync(dir);
|
|
124
|
+
const common = resolve(physical, commonDir);
|
|
125
|
+
if (resolve(physical, gitDir) === common || basename(common) !== ".git") return top;
|
|
126
|
+
return dirname(common);
|
|
127
|
+
}
|
|
101
128
|
function hookCwd(data) {
|
|
102
129
|
if (!data || typeof data !== "object") return void 0;
|
|
103
130
|
if (typeof data.cwd === "string" && data.cwd.trim()) return data.cwd;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pre-compact.mjs","names":[],"sources":["../../src/secret-store.ts","../../src/hooks/_capture-filter.ts","../../src/hooks/_project.ts","../../src/hooks/pre-compact.ts"],"sourcesContent":["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","const DEFAULT_DENY_PATTERNS = [\n \"memory_*\",\n \"toolsearch\",\n \"listmcpresources\",\n \"fetchmcpresource\",\n];\n\nfunction parseEnvList(raw: string | undefined): string[] | undefined {\n if (!raw?.trim()) return undefined;\n return raw\n .split(/[,\\s]+/)\n .map((part) => part.trim())\n .filter(Boolean);\n}\n\nexport function bareToolName(toolName: string): string {\n const trimmed = toolName.trim();\n if (/^mcp__/i.test(trimmed)) {\n const parts = trimmed.split(\"__\");\n if (parts.length >= 3) return parts[parts.length - 1]!;\n }\n return trimmed;\n}\n\nfunction normalizePattern(pattern: string): string {\n return pattern.trim().toLowerCase();\n}\n\nfunction matchesPattern(toolName: string, pattern: string): boolean {\n const bare = bareToolName(toolName).toLowerCase();\n const full = toolName.trim().toLowerCase();\n const pat = normalizePattern(pattern);\n if (!pat.includes(\"*\")) return bare === pat || full === pat;\n const escaped = pat.replace(/[.+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n const re = new RegExp(`^${escaped.replace(/\\*/g, \".*\")}$`);\n return re.test(bare) || re.test(full);\n}\n\nfunction matchesAny(toolName: string, patterns: string[]): boolean {\n return patterns.some((pattern) => matchesPattern(toolName, pattern));\n}\n\nexport function shouldCaptureTool(toolName: unknown): boolean {\n if (typeof toolName !== \"string\" || !toolName.trim()) return true;\n\n const allow = parseEnvList(process.env[\"AGENTMEMORY_CAPTURE_ALLOW\"]);\n if (allow) return matchesAny(toolName, allow);\n\n const deny = [\n ...DEFAULT_DENY_PATTERNS,\n ...(parseEnvList(process.env[\"AGENTMEMORY_CAPTURE_DENY\"]) ?? []),\n ];\n return !matchesAny(toolName, deny);\n}\n\nexport function captureOutputMax(): number {\n const raw = process.env[\"AGENTMEMORY_CAPTURE_OUTPUT_MAX\"];\n if (!raw?.trim()) return 8000;\n const parsed = Number.parseInt(raw, 10);\n return Number.isFinite(parsed) && parsed > 0 ? parsed : 8000;\n}\n\nexport function truncateCaptureOutput(value: unknown, max: number): unknown {\n if (typeof value === \"string\" && value.length > max) {\n const suffix = \"\\n[...truncated]\";\n return max <= suffix.length\n ? suffix.slice(0, max)\n : value.slice(0, max - suffix.length) + suffix;\n }\n if (typeof value === \"object\" && value !== null) {\n const str = JSON.stringify(value);\n if (str.length > max) {\n const suffix = \"...[truncated]\";\n return max <= suffix.length\n ? suffix.slice(0, max)\n : str.slice(0, max - suffix.length) + suffix;\n }\n return value;\n }\n return value;\n}\n\nexport function preCompactBudget(): number {\n const raw = process.env[\"AGENTMEMORY_PRE_COMPACT_BUDGET\"];\n if (raw?.trim() === \"0\") return 0;\n if (!raw?.trim()) return 1500;\n const parsed = Number.parseInt(raw, 10);\n return Number.isFinite(parsed) && parsed >= 0 ? parsed : 1500;\n}\n","import { execSync } from \"node:child_process\";\nimport { basename } from \"node:path\";\n\n// Resolution order: AGENTMEMORY_PROJECT_NAME env → git toplevel basename → cwd basename.\nexport function resolveProject(cwd?: string): string {\n const explicit = process.env[\"AGENTMEMORY_PROJECT_NAME\"];\n if (explicit && explicit.trim()) return explicit.trim();\n const dir = cwd && cwd.trim() ? cwd : process.cwd();\n try {\n const top = execSync(\"git rev-parse --show-toplevel\", {\n cwd: dir,\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n timeout: 500,\n })\n .toString()\n .trim();\n if (top) return basename(top);\n } catch {}\n return basename(dir);\n}\n\nexport function hookCwd(data: Record<string, unknown> | null | undefined): string | undefined {\n if (!data || typeof data !== \"object\") return undefined;\n if (typeof data.cwd === \"string\" && data.cwd.trim()) return data.cwd;\n const roots = data.workspace_roots;\n if (Array.isArray(roots)) {\n for (const root of roots) {\n if (typeof root === \"string\" && root.trim()) return root;\n }\n }\n const projectDir =\n process.env[\"DEVIN_PROJECT_DIR\"] || process.env[\"CLAUDE_PROJECT_DIR\"];\n if (projectDir && projectDir.trim()) return projectDir;\n return undefined;\n}\n","#!/usr/bin/env node\nimport { resolveClientSecret } from \"../secret-store.js\";\nimport { preCompactBudget } from \"./_capture-filter.js\";\nimport { resolveProject, hookCwd } from \"./_project.js\";\n\nfunction isSdkChildContext(payload: unknown): boolean {\n if (process.env[\"AGENTMEMORY_SDK_CHILD\"] === \"1\") return true;\n if (!payload || typeof payload !== \"object\") return false;\n return (payload as { entrypoint?: unknown }).entrypoint === \"sdk-ts\";\n}\n\nconst REST_URL = process.env[\"AGENTMEMORY_URL\"] || \"http://localhost:3111\";\nconst SECRET = resolveClientSecret(REST_URL);\n\nfunction authHeaders(): Record<string, string> {\n const h: Record<string, string> = { \"Content-Type\": \"application/json\" };\n if (SECRET) h[\"Authorization\"] = `Bearer ${SECRET}`;\n return h;\n}\n\nasync function main() {\n let input = \"\";\n for await (const chunk of process.stdin) {\n input += chunk;\n }\n\n let data: Record<string, unknown>;\n try {\n data = JSON.parse(input);\n } catch {\n return;\n }\n\n if (!data || typeof data !== \"object\") return;\n if (isSdkChildContext(data)) return;\n\n const sessionId = ((data.session_id || data.sessionId || data.conversation_id) as string) || \"unknown\";\n const project = resolveProject(hookCwd(data));\n\n if (process.env[\"CLAUDE_MEMORY_BRIDGE\"] === \"true\") {\n try {\n await fetch(`${REST_URL}/agentmemory/claude-bridge/sync`, {\n method: \"POST\",\n headers: authHeaders(),\n body: JSON.stringify({}),\n signal: AbortSignal.timeout(5000),\n });\n } catch {\n // best-effort\n }\n }\n\n const budget = preCompactBudget();\n if (budget === 0) return;\n\n try {\n const res = await fetch(`${REST_URL}/agentmemory/context`, {\n method: \"POST\",\n headers: authHeaders(),\n body: JSON.stringify({ sessionId, project, budget }),\n signal: AbortSignal.timeout(5000),\n });\n\n if (res.ok) {\n const result = (await res.json()) as { context?: string };\n if (result.context) {\n process.stdout.write(result.context);\n }\n }\n } catch {\n // best effort -- don't block compaction\n }\n}\n\nmain().catch(() => process.exit(0));\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"pre-compact.mjs","names":[],"sources":["../../src/secret-store.ts","../../src/hooks/_capture-filter.ts","../../src/hooks/_project.ts","../../src/hooks/pre-compact.ts"],"sourcesContent":["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","const DEFAULT_DENY_PATTERNS = [\n \"memory_*\",\n \"toolsearch\",\n \"listmcpresources\",\n \"fetchmcpresource\",\n];\n\nfunction parseEnvList(raw: string | undefined): string[] | undefined {\n if (!raw?.trim()) return undefined;\n return raw\n .split(/[,\\s]+/)\n .map((part) => part.trim())\n .filter(Boolean);\n}\n\nexport function bareToolName(toolName: string): string {\n const trimmed = toolName.trim();\n if (/^mcp__/i.test(trimmed)) {\n const parts = trimmed.split(\"__\");\n if (parts.length >= 3) return parts[parts.length - 1]!;\n }\n return trimmed;\n}\n\nfunction normalizePattern(pattern: string): string {\n return pattern.trim().toLowerCase();\n}\n\nfunction matchesPattern(toolName: string, pattern: string): boolean {\n const bare = bareToolName(toolName).toLowerCase();\n const full = toolName.trim().toLowerCase();\n const pat = normalizePattern(pattern);\n if (!pat.includes(\"*\")) return bare === pat || full === pat;\n const escaped = pat.replace(/[.+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n const re = new RegExp(`^${escaped.replace(/\\*/g, \".*\")}$`);\n return re.test(bare) || re.test(full);\n}\n\nfunction matchesAny(toolName: string, patterns: string[]): boolean {\n return patterns.some((pattern) => matchesPattern(toolName, pattern));\n}\n\nexport function shouldCaptureTool(toolName: unknown): boolean {\n if (typeof toolName !== \"string\" || !toolName.trim()) return true;\n\n const allow = parseEnvList(process.env[\"AGENTMEMORY_CAPTURE_ALLOW\"]);\n if (allow) return matchesAny(toolName, allow);\n\n const deny = [\n ...DEFAULT_DENY_PATTERNS,\n ...(parseEnvList(process.env[\"AGENTMEMORY_CAPTURE_DENY\"]) ?? []),\n ];\n return !matchesAny(toolName, deny);\n}\n\nexport function captureOutputMax(): number {\n const raw = process.env[\"AGENTMEMORY_CAPTURE_OUTPUT_MAX\"];\n if (!raw?.trim()) return 8000;\n const parsed = Number.parseInt(raw, 10);\n return Number.isFinite(parsed) && parsed > 0 ? parsed : 8000;\n}\n\nexport function truncateCaptureOutput(value: unknown, max: number): unknown {\n if (typeof value === \"string\" && value.length > max) {\n const suffix = \"\\n[...truncated]\";\n return max <= suffix.length\n ? suffix.slice(0, max)\n : value.slice(0, max - suffix.length) + suffix;\n }\n if (typeof value === \"object\" && value !== null) {\n const str = JSON.stringify(value);\n if (str.length > max) {\n const suffix = \"...[truncated]\";\n return max <= suffix.length\n ? suffix.slice(0, max)\n : str.slice(0, max - suffix.length) + suffix;\n }\n return value;\n }\n return value;\n}\n\nexport function preCompactBudget(): number {\n const raw = process.env[\"AGENTMEMORY_PRE_COMPACT_BUDGET\"];\n if (raw?.trim() === \"0\") return 0;\n if (!raw?.trim()) return 1500;\n const parsed = Number.parseInt(raw, 10);\n return Number.isFinite(parsed) && parsed >= 0 ? parsed : 1500;\n}\n","import { execSync } from \"node:child_process\";\nimport { realpathSync } from \"node:fs\";\nimport { basename, dirname, resolve } from \"node:path\";\n\n// Resolution order: AGENTMEMORY_PROJECT_NAME env → git repository basename → cwd basename.\nexport function resolveProject(cwd?: string): string {\n const explicit = process.env[\"AGENTMEMORY_PROJECT_NAME\"];\n if (explicit && explicit.trim()) return explicit.trim();\n const dir = cwd && cwd.trim() ? cwd : process.cwd();\n try {\n const [top, gitDir, commonDir] = execSync(\n \"git rev-parse --show-toplevel --git-dir --git-common-dir\",\n {\n cwd: dir,\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n timeout: 500,\n },\n )\n .toString()\n .trim()\n .split(/\\r?\\n/);\n if (top) return basename(repositoryRoot(dir, top, gitDir, commonDir));\n } catch {}\n return basename(dir);\n}\n\nfunction repositoryRoot(dir: string, top: string, gitDir?: string, commonDir?: string): string {\n if (!gitDir || !commonDir) return top;\n const physical = realpathSync(dir);\n const common = resolve(physical, commonDir);\n if (resolve(physical, gitDir) === common || basename(common) !== \".git\") return top;\n return dirname(common);\n}\n\nexport function hookCwd(data: Record<string, unknown> | null | undefined): string | undefined {\n if (!data || typeof data !== \"object\") return undefined;\n if (typeof data.cwd === \"string\" && data.cwd.trim()) return data.cwd;\n const roots = data.workspace_roots;\n if (Array.isArray(roots)) {\n for (const root of roots) {\n if (typeof root === \"string\" && root.trim()) return root;\n }\n }\n const projectDir =\n process.env[\"DEVIN_PROJECT_DIR\"] || process.env[\"CLAUDE_PROJECT_DIR\"];\n if (projectDir && projectDir.trim()) return projectDir;\n return undefined;\n}\n","#!/usr/bin/env node\nimport { resolveClientSecret } from \"../secret-store.js\";\nimport { preCompactBudget } from \"./_capture-filter.js\";\nimport { resolveProject, hookCwd } from \"./_project.js\";\n\nfunction isSdkChildContext(payload: unknown): boolean {\n if (process.env[\"AGENTMEMORY_SDK_CHILD\"] === \"1\") return true;\n if (!payload || typeof payload !== \"object\") return false;\n return (payload as { entrypoint?: unknown }).entrypoint === \"sdk-ts\";\n}\n\nconst REST_URL = process.env[\"AGENTMEMORY_URL\"] || \"http://localhost:3111\";\nconst SECRET = resolveClientSecret(REST_URL);\n\nfunction authHeaders(): Record<string, string> {\n const h: Record<string, string> = { \"Content-Type\": \"application/json\" };\n if (SECRET) h[\"Authorization\"] = `Bearer ${SECRET}`;\n return h;\n}\n\nasync function main() {\n let input = \"\";\n for await (const chunk of process.stdin) {\n input += chunk;\n }\n\n let data: Record<string, unknown>;\n try {\n data = JSON.parse(input);\n } catch {\n return;\n }\n\n if (!data || typeof data !== \"object\") return;\n if (isSdkChildContext(data)) return;\n\n const sessionId = ((data.session_id || data.sessionId || data.conversation_id) as string) || \"unknown\";\n const project = resolveProject(hookCwd(data));\n\n if (process.env[\"CLAUDE_MEMORY_BRIDGE\"] === \"true\") {\n try {\n await fetch(`${REST_URL}/agentmemory/claude-bridge/sync`, {\n method: \"POST\",\n headers: authHeaders(),\n body: JSON.stringify({}),\n signal: AbortSignal.timeout(5000),\n });\n } catch {\n // best-effort\n }\n }\n\n const budget = preCompactBudget();\n if (budget === 0) return;\n\n try {\n const res = await fetch(`${REST_URL}/agentmemory/context`, {\n method: \"POST\",\n headers: authHeaders(),\n body: JSON.stringify({ sessionId, project, budget }),\n signal: AbortSignal.timeout(5000),\n });\n\n if (res.ok) {\n const result = (await res.json()) as { context?: string };\n if (result.context) {\n process.stdout.write(result.context);\n }\n }\n } catch {\n // best effort -- don't block compaction\n }\n}\n\nmain().catch(() => process.exit(0));\n"],"mappings":"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"}
|
|
@@ -5,7 +5,7 @@ import { join } from "node:path";
|
|
|
5
5
|
//#region src/secret-store.ts
|
|
6
6
|
const SECRET_KEY = "AGENTMEMORY_SECRET";
|
|
7
7
|
function agentmemoryHomeDir() {
|
|
8
|
-
return join(homedir(), ".agentmemory");
|
|
8
|
+
return process.env["AGENTMEMORY_DATA_DIR"] || join(homedir(), ".agentmemory");
|
|
9
9
|
}
|
|
10
10
|
function secretFilePath() {
|
|
11
11
|
return join(agentmemoryHomeDir(), "secret");
|
|
@@ -29,7 +29,7 @@ function unquote(value) {
|
|
|
29
29
|
function readEnvFileSecret() {
|
|
30
30
|
let content;
|
|
31
31
|
try {
|
|
32
|
-
content = readFileSync(join(
|
|
32
|
+
content = readFileSync(process.env["AGENTMEMORY_ENV_FILE"] || join(homedir(), ".agentmemory", ".env"), "utf-8");
|
|
33
33
|
} catch {
|
|
34
34
|
return "";
|
|
35
35
|
}
|
|
@@ -45,13 +45,29 @@ function readEnvFileSecret() {
|
|
|
45
45
|
}
|
|
46
46
|
return found;
|
|
47
47
|
}
|
|
48
|
-
function
|
|
48
|
+
function readSecretFile(path) {
|
|
49
49
|
try {
|
|
50
|
-
return usable(readFileSync(
|
|
50
|
+
return usable(readFileSync(path, "utf-8"));
|
|
51
51
|
} catch {
|
|
52
52
|
return "";
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
|
+
function legacyDataDirSecretPaths(env = process.env) {
|
|
56
|
+
const dataDir = env["AGENTMEMORY_DATA_DIR"];
|
|
57
|
+
if (!dataDir) return [];
|
|
58
|
+
const paths = [
|
|
59
|
+
join(dataDir, "secret"),
|
|
60
|
+
join(dataDir, ".hmac"),
|
|
61
|
+
join(dataDir, "..", ".hmac")
|
|
62
|
+
];
|
|
63
|
+
return [...new Set(paths.filter((path) => path !== secretFilePath()))];
|
|
64
|
+
}
|
|
65
|
+
function readStoredSecret(env = process.env) {
|
|
66
|
+
const canonicalPath = canonicalSecretPath();
|
|
67
|
+
const localPath = secretFilePath();
|
|
68
|
+
const legacyPaths = legacyDataDirSecretPaths(env);
|
|
69
|
+
return readSecretFile(canonicalPath) || (localPath === canonicalPath ? "" : readSecretFile(localPath)) || legacyPaths.map(readSecretFile).find(Boolean) || "";
|
|
70
|
+
}
|
|
55
71
|
function isLoopbackUrl(url) {
|
|
56
72
|
let hostname;
|
|
57
73
|
try {
|
|
@@ -65,8 +81,12 @@ function isLoopbackUrl(url) {
|
|
|
65
81
|
function resolveClientSecret(baseUrl, env = process.env) {
|
|
66
82
|
const fromEnv = usable(env[SECRET_KEY]);
|
|
67
83
|
if (fromEnv) return fromEnv;
|
|
84
|
+
if (env.AGENTMEMORY_CREDENTIAL_MODE === "explicit") return "";
|
|
68
85
|
if (!isLoopbackUrl(baseUrl)) return "";
|
|
69
|
-
return readEnvFileSecret() || readStoredSecret();
|
|
86
|
+
return readEnvFileSecret() || readStoredSecret(env);
|
|
87
|
+
}
|
|
88
|
+
function canonicalSecretPath() {
|
|
89
|
+
return join(homedir(), ".agentmemory", "secret");
|
|
70
90
|
}
|
|
71
91
|
//#endregion
|
|
72
92
|
//#region src/hooks/pre-tool-use.ts
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pre-tool-use.mjs","names":[],"sources":["../../src/secret-store.ts","../../src/hooks/pre-tool-use.ts"],"sourcesContent":["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","#!/usr/bin/env node\nimport { resolveClientSecret } from \"../secret-store.js\";\n\nfunction isSdkChildContext(payload: unknown): boolean {\n if (process.env[\"AGENTMEMORY_SDK_CHILD\"] === \"1\") return true;\n if (!payload || typeof payload !== \"object\") return false;\n return (payload as { entrypoint?: unknown }).entrypoint === \"sdk-ts\";\n}\n\nconst INJECT_CONTEXT = process.env[\"AGENTMEMORY_INJECT_CONTEXT\"] === \"true\";\n\nconst REST_URL = process.env[\"AGENTMEMORY_URL\"] || \"http://localhost:3111\";\nconst SECRET = resolveClientSecret(REST_URL);\n\nfunction authHeaders(): Record<string, string> {\n const h: Record<string, string> = { \"Content-Type\": \"application/json\" };\n if (SECRET) h[\"Authorization\"] = `Bearer ${SECRET}`;\n return h;\n}\n\nasync function main() {\n // Default off: exit immediately so we don't even open stdin. This keeps\n // Claude Code's tool-call hot path as cheap as possible.\n if (!INJECT_CONTEXT) return;\n\n let input = \"\";\n for await (const chunk of process.stdin) {\n input += chunk;\n }\n\n let data: Record<string, unknown>;\n try {\n data = JSON.parse(input);\n } catch {\n return;\n }\n\n if (!data || typeof data !== \"object\") return;\n if (isSdkChildContext(data)) return;\n\n const toolName =\n typeof data.tool_name === \"string\"\n ? data.tool_name\n : typeof data.toolName === \"string\"\n ? data.toolName\n : undefined;\n if (!toolName) return;\n\n const normalizedToolName = toolName.toLowerCase();\n const fileTools = [\"edit\", \"write\", \"create\", \"read\", \"view\", \"glob\", \"grep\"];\n if (!fileTools.includes(normalizedToolName)) return;\n\n const rawToolInput = data.tool_input ?? data.toolArgs;\n const toolInput =\n typeof rawToolInput === \"object\" &&\n rawToolInput !== null &&\n !Array.isArray(rawToolInput)\n ? (rawToolInput as Record<string, unknown>)\n : {};\n const files: string[] = [];\n const fileKeys =\n normalizedToolName === \"grep\"\n ? [\"path\", \"file\"]\n : [\"file_path\", \"path\", \"file\", \"pattern\"];\n for (const key of fileKeys) {\n const val = toolInput[key];\n if (typeof val === \"string\" && val.length > 0) files.push(val);\n }\n if (files.length === 0) return;\n\n const terms: string[] = [];\n if (normalizedToolName === \"grep\" || normalizedToolName === \"glob\") {\n const pattern = toolInput[\"pattern\"];\n if (typeof pattern === \"string\" && pattern.length > 0) {\n terms.push(pattern);\n }\n }\n\n const rawSessionId = data.session_id || data.sessionId || data.conversation_id;\n const sessionId =\n typeof rawSessionId === \"string\" && rawSessionId.length > 0\n ? rawSessionId\n : \"unknown\";\n const project =\n typeof data.project === \"string\" && data.project.trim().length > 0\n ? data.project.trim()\n : undefined;\n\n try {\n const res = await fetch(`${REST_URL}/agentmemory/enrich`, {\n method: \"POST\",\n headers: authHeaders(),\n body: JSON.stringify({\n sessionId,\n files,\n terms,\n toolName,\n ...(project !== undefined && { project }),\n }),\n signal: AbortSignal.timeout(2000),\n });\n\n if (res.ok) {\n const result = (await res.json()) as { context?: string };\n if (result.context) {\n process.stdout.write(result.context);\n }\n }\n } catch {\n // don't block tool execution\n }\n}\n\nmain().catch(() => process.exit(0));\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"pre-tool-use.mjs","names":[],"sources":["../../src/secret-store.ts","../../src/hooks/pre-tool-use.ts"],"sourcesContent":["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","#!/usr/bin/env node\nimport { resolveClientSecret } from \"../secret-store.js\";\n\nfunction isSdkChildContext(payload: unknown): boolean {\n if (process.env[\"AGENTMEMORY_SDK_CHILD\"] === \"1\") return true;\n if (!payload || typeof payload !== \"object\") return false;\n return (payload as { entrypoint?: unknown }).entrypoint === \"sdk-ts\";\n}\n\nconst INJECT_CONTEXT = process.env[\"AGENTMEMORY_INJECT_CONTEXT\"] === \"true\";\n\nconst REST_URL = process.env[\"AGENTMEMORY_URL\"] || \"http://localhost:3111\";\nconst SECRET = resolveClientSecret(REST_URL);\n\nfunction authHeaders(): Record<string, string> {\n const h: Record<string, string> = { \"Content-Type\": \"application/json\" };\n if (SECRET) h[\"Authorization\"] = `Bearer ${SECRET}`;\n return h;\n}\n\nasync function main() {\n // Default off: exit immediately so we don't even open stdin. This keeps\n // Claude Code's tool-call hot path as cheap as possible.\n if (!INJECT_CONTEXT) return;\n\n let input = \"\";\n for await (const chunk of process.stdin) {\n input += chunk;\n }\n\n let data: Record<string, unknown>;\n try {\n data = JSON.parse(input);\n } catch {\n return;\n }\n\n if (!data || typeof data !== \"object\") return;\n if (isSdkChildContext(data)) return;\n\n const toolName =\n typeof data.tool_name === \"string\"\n ? data.tool_name\n : typeof data.toolName === \"string\"\n ? data.toolName\n : undefined;\n if (!toolName) return;\n\n const normalizedToolName = toolName.toLowerCase();\n const fileTools = [\"edit\", \"write\", \"create\", \"read\", \"view\", \"glob\", \"grep\"];\n if (!fileTools.includes(normalizedToolName)) return;\n\n const rawToolInput = data.tool_input ?? data.toolArgs;\n const toolInput =\n typeof rawToolInput === \"object\" &&\n rawToolInput !== null &&\n !Array.isArray(rawToolInput)\n ? (rawToolInput as Record<string, unknown>)\n : {};\n const files: string[] = [];\n const fileKeys =\n normalizedToolName === \"grep\"\n ? [\"path\", \"file\"]\n : [\"file_path\", \"path\", \"file\", \"pattern\"];\n for (const key of fileKeys) {\n const val = toolInput[key];\n if (typeof val === \"string\" && val.length > 0) files.push(val);\n }\n if (files.length === 0) return;\n\n const terms: string[] = [];\n if (normalizedToolName === \"grep\" || normalizedToolName === \"glob\") {\n const pattern = toolInput[\"pattern\"];\n if (typeof pattern === \"string\" && pattern.length > 0) {\n terms.push(pattern);\n }\n }\n\n const rawSessionId = data.session_id || data.sessionId || data.conversation_id;\n const sessionId =\n typeof rawSessionId === \"string\" && rawSessionId.length > 0\n ? rawSessionId\n : \"unknown\";\n const project =\n typeof data.project === \"string\" && data.project.trim().length > 0\n ? data.project.trim()\n : undefined;\n\n try {\n const res = await fetch(`${REST_URL}/agentmemory/enrich`, {\n method: \"POST\",\n headers: authHeaders(),\n body: JSON.stringify({\n sessionId,\n files,\n terms,\n toolName,\n ...(project !== undefined && { project }),\n }),\n signal: AbortSignal.timeout(2000),\n });\n\n if (res.ok) {\n const result = (await res.json()) as { context?: string };\n if (result.context) {\n process.stdout.write(result.context);\n }\n }\n } catch {\n // don't block tool execution\n }\n}\n\nmain().catch(() => process.exit(0));\n"],"mappings":"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"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { execSync } from "node:child_process";
|
|
3
|
-
import {
|
|
3
|
+
import { realpathSync } from "node:fs";
|
|
4
|
+
import { basename, dirname, resolve } from "node:path";
|
|
4
5
|
import { captureObservation, isDrainChild, runDrainChild, withEventId } from "./_capture.mjs";
|
|
5
6
|
//#region src/hooks/_project.ts
|
|
6
7
|
function resolveProject(cwd) {
|
|
@@ -8,7 +9,7 @@ function resolveProject(cwd) {
|
|
|
8
9
|
if (explicit && explicit.trim()) return explicit.trim();
|
|
9
10
|
const dir = cwd && cwd.trim() ? cwd : process.cwd();
|
|
10
11
|
try {
|
|
11
|
-
const top = execSync("git rev-parse --show-toplevel", {
|
|
12
|
+
const [top, gitDir, commonDir] = execSync("git rev-parse --show-toplevel --git-dir --git-common-dir", {
|
|
12
13
|
cwd: dir,
|
|
13
14
|
stdio: [
|
|
14
15
|
"ignore",
|
|
@@ -16,11 +17,18 @@ function resolveProject(cwd) {
|
|
|
16
17
|
"ignore"
|
|
17
18
|
],
|
|
18
19
|
timeout: 500
|
|
19
|
-
}).toString().trim();
|
|
20
|
-
if (top) return basename(top);
|
|
20
|
+
}).toString().trim().split(/\r?\n/);
|
|
21
|
+
if (top) return basename(repositoryRoot(dir, top, gitDir, commonDir));
|
|
21
22
|
} catch {}
|
|
22
23
|
return basename(dir);
|
|
23
24
|
}
|
|
25
|
+
function repositoryRoot(dir, top, gitDir, commonDir) {
|
|
26
|
+
if (!gitDir || !commonDir) return top;
|
|
27
|
+
const physical = realpathSync(dir);
|
|
28
|
+
const common = resolve(physical, commonDir);
|
|
29
|
+
if (resolve(physical, gitDir) === common || basename(common) !== ".git") return top;
|
|
30
|
+
return dirname(common);
|
|
31
|
+
}
|
|
24
32
|
function hookCwd(data) {
|
|
25
33
|
if (!data || typeof data !== "object") return void 0;
|
|
26
34
|
if (typeof data.cwd === "string" && data.cwd.trim()) return data.cwd;
|