@agentmemory/agentmemory 0.9.29 → 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.
Files changed (352) hide show
  1. package/.env.example +102 -35
  2. package/AGENTS.md +19 -26
  3. package/README.md +417 -525
  4. package/dist/.env.example +102 -35
  5. package/dist/cli.d.mts.map +1 -1
  6. package/dist/cli.mjs +2316 -655
  7. package/dist/cli.mjs.map +1 -1
  8. package/dist/compression-recovery-CnhLQC6N.mjs +5912 -0
  9. package/dist/compression-recovery-CnhLQC6N.mjs.map +1 -0
  10. package/dist/{connect-Qaeq15TQ.mjs → connect-BSLIAMpw.mjs} +900 -302
  11. package/dist/connect-BSLIAMpw.mjs.map +1 -0
  12. package/dist/hooks/_capture.d.mts +25 -0
  13. package/dist/hooks/_capture.d.mts.map +1 -0
  14. package/dist/hooks/_capture.mjs +944 -0
  15. package/dist/hooks/_capture.mjs.map +1 -0
  16. package/dist/hooks/antigravity-bridge.d.mts +1 -1
  17. package/dist/hooks/antigravity-bridge.d.mts.map +1 -1
  18. package/dist/hooks/antigravity-bridge.mjs +24 -15
  19. package/dist/hooks/antigravity-bridge.mjs.map +1 -1
  20. package/dist/hooks/notification.mjs +35 -29
  21. package/dist/hooks/notification.mjs.map +1 -1
  22. package/dist/hooks/post-commit.mjs +91 -1
  23. package/dist/hooks/post-commit.mjs.map +1 -1
  24. package/dist/hooks/post-tool-failure.mjs +71 -29
  25. package/dist/hooks/post-tool-failure.mjs.map +1 -1
  26. package/dist/hooks/post-tool-use.mjs +99 -39
  27. package/dist/hooks/post-tool-use.mjs.map +1 -1
  28. package/dist/hooks/pre-compact.mjs +113 -6
  29. package/dist/hooks/pre-compact.mjs.map +1 -1
  30. package/dist/hooks/pre-tool-use.mjs +91 -1
  31. package/dist/hooks/pre-tool-use.mjs.map +1 -1
  32. package/dist/hooks/prompt-submit.mjs +25 -25
  33. package/dist/hooks/prompt-submit.mjs.map +1 -1
  34. package/dist/hooks/session-end.mjs +67 -31
  35. package/dist/hooks/session-end.mjs.map +1 -1
  36. package/dist/hooks/session-start.mjs +110 -6
  37. package/dist/hooks/session-start.mjs.map +1 -1
  38. package/dist/hooks/stop.mjs +80 -8
  39. package/dist/hooks/stop.mjs.map +1 -1
  40. package/dist/hooks/subagent-start.mjs +28 -29
  41. package/dist/hooks/subagent-start.mjs.map +1 -1
  42. package/dist/hooks/subagent-stop.mjs +33 -29
  43. package/dist/hooks/subagent-stop.mjs.map +1 -1
  44. package/dist/hooks/task-completed.mjs +31 -31
  45. package/dist/hooks/task-completed.mjs.map +1 -1
  46. package/dist/{image-refs-BKLmp_sD.mjs → image-refs-BaT-9oMU.mjs} +2 -2
  47. package/dist/image-refs-BaT-9oMU.mjs.map +1 -0
  48. package/dist/{image-refs-CJgZmb9X.mjs → image-refs-Cyta4Lf9.mjs} +3 -3
  49. package/dist/image-refs-Cyta4Lf9.mjs.map +1 -0
  50. package/dist/{keyed-mutex-DTHtcZrt.mjs → image-store-BsN4vyjr.mjs} +2 -14
  51. package/dist/image-store-BsN4vyjr.mjs.map +1 -0
  52. package/dist/index.mjs +15180 -4119
  53. package/dist/index.mjs.map +1 -1
  54. package/dist/integrations/dsh.d.mts +55 -0
  55. package/dist/integrations/dsh.d.mts.map +1 -0
  56. package/dist/integrations/dsh.mjs +1145 -0
  57. package/dist/integrations/dsh.mjs.map +1 -0
  58. package/dist/{schema-BFZhNTF7.mjs → keyed-mutex-DCUh7PG-.mjs} +81 -8
  59. package/dist/keyed-mutex-DCUh7PG-.mjs.map +1 -0
  60. package/dist/secret-store-D9GwO0xp.mjs +2449 -0
  61. package/dist/secret-store-D9GwO0xp.mjs.map +1 -0
  62. package/dist/session-arguments-zxsbbd8y.mjs +113 -0
  63. package/dist/session-arguments-zxsbbd8y.mjs.map +1 -0
  64. package/dist/{src-G7yt8gGm.mjs → src-DaMtDzt_.mjs} +8320 -4260
  65. package/dist/src-DaMtDzt_.mjs.map +1 -0
  66. package/dist/{standalone-BqNJDJ04.mjs → standalone-BlqshyZ5.mjs} +246 -62
  67. package/dist/standalone-BlqshyZ5.mjs.map +1 -0
  68. package/dist/standalone.d.mts +1 -1
  69. package/dist/standalone.d.mts.map +1 -1
  70. package/dist/standalone.mjs +866 -78
  71. package/dist/standalone.mjs.map +1 -1
  72. package/dist/viewer/index.html +7394 -2816
  73. package/integrations/hermes/README.md +84 -0
  74. package/integrations/hermes/__init__.py +669 -0
  75. package/integrations/hermes/plugin.yaml +27 -0
  76. package/integrations/omp/LICENSE +190 -0
  77. package/integrations/omp/README.md +76 -0
  78. package/integrations/omp/capture.ts +1 -0
  79. package/integrations/omp/dist/index.mjs +1450 -0
  80. package/integrations/omp/index.ts +488 -0
  81. package/integrations/omp/package.json +13 -0
  82. package/integrations/omp/security.ts +67 -0
  83. package/integrations/omp/skills/agentmemory-agents/REFERENCE.md +32 -0
  84. package/integrations/omp/skills/agentmemory-agents/SKILL.md +48 -0
  85. package/integrations/omp/skills/agentmemory-agents/TROUBLESHOOTING.md +70 -0
  86. package/integrations/omp/skills/agentmemory-architecture/SKILL.md +33 -0
  87. package/integrations/omp/skills/agentmemory-architecture/TROUBLESHOOTING.md +70 -0
  88. package/integrations/omp/skills/agentmemory-config/REFERENCE.md +90 -0
  89. package/integrations/omp/skills/agentmemory-config/SKILL.md +78 -0
  90. package/integrations/omp/skills/agentmemory-config/TROUBLESHOOTING.md +70 -0
  91. package/integrations/omp/skills/agentmemory-hooks/REFERENCE.md +20 -0
  92. package/integrations/omp/skills/agentmemory-hooks/SKILL.md +49 -0
  93. package/integrations/omp/skills/agentmemory-hooks/TROUBLESHOOTING.md +70 -0
  94. package/integrations/omp/skills/agentmemory-mcp-tools/REFERENCE.md +66 -0
  95. package/integrations/omp/skills/agentmemory-mcp-tools/SKILL.md +42 -0
  96. package/integrations/omp/skills/agentmemory-mcp-tools/TROUBLESHOOTING.md +70 -0
  97. package/integrations/omp/skills/agentmemory-rest-api/REFERENCE.md +158 -0
  98. package/integrations/omp/skills/agentmemory-rest-api/SKILL.md +49 -0
  99. package/integrations/omp/skills/agentmemory-rest-api/TROUBLESHOOTING.md +70 -0
  100. package/integrations/omp/skills/commit-context/EXAMPLES.md +71 -0
  101. package/integrations/omp/skills/commit-context/SKILL.md +64 -0
  102. package/integrations/omp/skills/commit-context/TROUBLESHOOTING.md +70 -0
  103. package/integrations/omp/skills/commit-history/EXAMPLES.md +71 -0
  104. package/integrations/omp/skills/commit-history/SKILL.md +62 -0
  105. package/integrations/omp/skills/commit-history/TROUBLESHOOTING.md +70 -0
  106. package/integrations/omp/skills/forget/EXAMPLES.md +40 -0
  107. package/integrations/omp/skills/forget/SKILL.md +73 -0
  108. package/integrations/omp/skills/forget/TROUBLESHOOTING.md +70 -0
  109. package/integrations/omp/skills/handoff/EXAMPLES.md +57 -0
  110. package/integrations/omp/skills/handoff/SKILL.md +75 -0
  111. package/integrations/omp/skills/handoff/TROUBLESHOOTING.md +70 -0
  112. package/integrations/omp/skills/lesson/SKILL.md +64 -0
  113. package/integrations/omp/skills/lesson/TROUBLESHOOTING.md +70 -0
  114. package/integrations/omp/skills/memory-discipline/SKILL.md +65 -0
  115. package/integrations/omp/skills/memory-discipline/TROUBLESHOOTING.md +70 -0
  116. package/integrations/omp/skills/recall/EXAMPLES.md +74 -0
  117. package/integrations/omp/skills/recall/SKILL.md +74 -0
  118. package/integrations/omp/skills/recall/TROUBLESHOOTING.md +70 -0
  119. package/integrations/omp/skills/recap/EXAMPLES.md +62 -0
  120. package/integrations/omp/skills/recap/SKILL.md +68 -0
  121. package/integrations/omp/skills/recap/TROUBLESHOOTING.md +70 -0
  122. package/integrations/omp/skills/remember/EXAMPLES.md +67 -0
  123. package/integrations/omp/skills/remember/SKILL.md +66 -0
  124. package/integrations/omp/skills/remember/TROUBLESHOOTING.md +70 -0
  125. package/integrations/omp/skills/session-history/EXAMPLES.md +59 -0
  126. package/integrations/omp/skills/session-history/SKILL.md +67 -0
  127. package/integrations/omp/skills/session-history/TROUBLESHOOTING.md +70 -0
  128. package/integrations/omp/skills/write-agentmemory-skill/SKILL.md +41 -0
  129. package/integrations/omp/skills/write-agentmemory-skill/TROUBLESHOOTING.md +70 -0
  130. package/integrations/openclaw/LICENSE +190 -0
  131. package/integrations/openclaw/README.md +88 -0
  132. package/integrations/openclaw/openclaw.plugin.json +71 -0
  133. package/integrations/openclaw/package.json +38 -0
  134. package/integrations/openclaw/plugin.mjs +425 -0
  135. package/integrations/openclaw/plugin.yaml +26 -0
  136. package/integrations/openclaw/skills/agentmemory-agents/REFERENCE.md +32 -0
  137. package/integrations/openclaw/skills/agentmemory-agents/SKILL.md +48 -0
  138. package/integrations/openclaw/skills/agentmemory-agents/TROUBLESHOOTING.md +70 -0
  139. package/integrations/openclaw/skills/agentmemory-architecture/SKILL.md +33 -0
  140. package/integrations/openclaw/skills/agentmemory-architecture/TROUBLESHOOTING.md +70 -0
  141. package/integrations/openclaw/skills/agentmemory-config/REFERENCE.md +90 -0
  142. package/integrations/openclaw/skills/agentmemory-config/SKILL.md +78 -0
  143. package/integrations/openclaw/skills/agentmemory-config/TROUBLESHOOTING.md +70 -0
  144. package/integrations/openclaw/skills/agentmemory-hooks/REFERENCE.md +20 -0
  145. package/integrations/openclaw/skills/agentmemory-hooks/SKILL.md +49 -0
  146. package/integrations/openclaw/skills/agentmemory-hooks/TROUBLESHOOTING.md +70 -0
  147. package/integrations/openclaw/skills/agentmemory-mcp-tools/REFERENCE.md +66 -0
  148. package/integrations/openclaw/skills/agentmemory-mcp-tools/SKILL.md +42 -0
  149. package/integrations/openclaw/skills/agentmemory-mcp-tools/TROUBLESHOOTING.md +70 -0
  150. package/integrations/openclaw/skills/agentmemory-rest-api/REFERENCE.md +158 -0
  151. package/integrations/openclaw/skills/agentmemory-rest-api/SKILL.md +49 -0
  152. package/integrations/openclaw/skills/agentmemory-rest-api/TROUBLESHOOTING.md +70 -0
  153. package/integrations/openclaw/skills/commit-context/EXAMPLES.md +71 -0
  154. package/integrations/openclaw/skills/commit-context/SKILL.md +64 -0
  155. package/integrations/openclaw/skills/commit-context/TROUBLESHOOTING.md +70 -0
  156. package/integrations/openclaw/skills/commit-history/EXAMPLES.md +71 -0
  157. package/integrations/openclaw/skills/commit-history/SKILL.md +62 -0
  158. package/integrations/openclaw/skills/commit-history/TROUBLESHOOTING.md +70 -0
  159. package/integrations/openclaw/skills/forget/EXAMPLES.md +40 -0
  160. package/integrations/openclaw/skills/forget/SKILL.md +73 -0
  161. package/integrations/openclaw/skills/forget/TROUBLESHOOTING.md +70 -0
  162. package/integrations/openclaw/skills/handoff/EXAMPLES.md +57 -0
  163. package/integrations/openclaw/skills/handoff/SKILL.md +75 -0
  164. package/integrations/openclaw/skills/handoff/TROUBLESHOOTING.md +70 -0
  165. package/integrations/openclaw/skills/lesson/SKILL.md +64 -0
  166. package/integrations/openclaw/skills/lesson/TROUBLESHOOTING.md +70 -0
  167. package/integrations/openclaw/skills/memory-discipline/SKILL.md +65 -0
  168. package/integrations/openclaw/skills/memory-discipline/TROUBLESHOOTING.md +70 -0
  169. package/integrations/openclaw/skills/recall/EXAMPLES.md +74 -0
  170. package/integrations/openclaw/skills/recall/SKILL.md +74 -0
  171. package/integrations/openclaw/skills/recall/TROUBLESHOOTING.md +70 -0
  172. package/integrations/openclaw/skills/recap/EXAMPLES.md +62 -0
  173. package/integrations/openclaw/skills/recap/SKILL.md +68 -0
  174. package/integrations/openclaw/skills/recap/TROUBLESHOOTING.md +70 -0
  175. package/integrations/openclaw/skills/remember/EXAMPLES.md +67 -0
  176. package/integrations/openclaw/skills/remember/SKILL.md +66 -0
  177. package/integrations/openclaw/skills/remember/TROUBLESHOOTING.md +70 -0
  178. package/integrations/openclaw/skills/session-history/EXAMPLES.md +59 -0
  179. package/integrations/openclaw/skills/session-history/SKILL.md +67 -0
  180. package/integrations/openclaw/skills/session-history/TROUBLESHOOTING.md +70 -0
  181. package/integrations/openclaw/skills/write-agentmemory-skill/SKILL.md +41 -0
  182. package/integrations/openclaw/skills/write-agentmemory-skill/TROUBLESHOOTING.md +70 -0
  183. package/integrations/pi/LICENSE +190 -0
  184. package/integrations/pi/README.md +45 -51
  185. package/integrations/pi/dist/index.mjs +1379 -0
  186. package/integrations/pi/index.ts +139 -173
  187. package/integrations/pi/package.json +16 -6
  188. package/integrations/pi/security.ts +54 -22
  189. package/integrations/pi/skills/agentmemory-agents/REFERENCE.md +32 -0
  190. package/integrations/pi/skills/agentmemory-agents/SKILL.md +48 -0
  191. package/integrations/pi/skills/agentmemory-agents/TROUBLESHOOTING.md +70 -0
  192. package/integrations/pi/skills/agentmemory-architecture/SKILL.md +33 -0
  193. package/integrations/pi/skills/agentmemory-architecture/TROUBLESHOOTING.md +70 -0
  194. package/integrations/pi/skills/agentmemory-config/REFERENCE.md +90 -0
  195. package/integrations/pi/skills/agentmemory-config/SKILL.md +78 -0
  196. package/integrations/pi/skills/agentmemory-config/TROUBLESHOOTING.md +70 -0
  197. package/integrations/pi/skills/agentmemory-hooks/REFERENCE.md +20 -0
  198. package/integrations/pi/skills/agentmemory-hooks/SKILL.md +49 -0
  199. package/integrations/pi/skills/agentmemory-hooks/TROUBLESHOOTING.md +70 -0
  200. package/integrations/pi/skills/agentmemory-mcp-tools/REFERENCE.md +66 -0
  201. package/integrations/pi/skills/agentmemory-mcp-tools/SKILL.md +42 -0
  202. package/integrations/pi/skills/agentmemory-mcp-tools/TROUBLESHOOTING.md +70 -0
  203. package/integrations/pi/skills/agentmemory-rest-api/REFERENCE.md +158 -0
  204. package/integrations/pi/skills/agentmemory-rest-api/SKILL.md +49 -0
  205. package/integrations/pi/skills/agentmemory-rest-api/TROUBLESHOOTING.md +70 -0
  206. package/integrations/pi/skills/commit-context/EXAMPLES.md +71 -0
  207. package/integrations/pi/skills/commit-context/SKILL.md +64 -0
  208. package/integrations/pi/skills/commit-context/TROUBLESHOOTING.md +70 -0
  209. package/integrations/pi/skills/commit-history/EXAMPLES.md +71 -0
  210. package/integrations/pi/skills/commit-history/SKILL.md +62 -0
  211. package/integrations/pi/skills/commit-history/TROUBLESHOOTING.md +70 -0
  212. package/integrations/pi/skills/forget/EXAMPLES.md +40 -0
  213. package/integrations/pi/skills/forget/SKILL.md +73 -0
  214. package/integrations/pi/skills/forget/TROUBLESHOOTING.md +70 -0
  215. package/integrations/pi/skills/handoff/EXAMPLES.md +57 -0
  216. package/integrations/pi/skills/handoff/SKILL.md +75 -0
  217. package/integrations/pi/skills/handoff/TROUBLESHOOTING.md +70 -0
  218. package/integrations/pi/skills/lesson/SKILL.md +64 -0
  219. package/integrations/pi/skills/lesson/TROUBLESHOOTING.md +70 -0
  220. package/integrations/pi/skills/memory-discipline/SKILL.md +65 -0
  221. package/integrations/pi/skills/memory-discipline/TROUBLESHOOTING.md +70 -0
  222. package/integrations/pi/skills/recall/EXAMPLES.md +74 -0
  223. package/integrations/pi/skills/recall/SKILL.md +74 -0
  224. package/integrations/pi/skills/recall/TROUBLESHOOTING.md +70 -0
  225. package/integrations/pi/skills/recap/EXAMPLES.md +62 -0
  226. package/integrations/pi/skills/recap/SKILL.md +68 -0
  227. package/integrations/pi/skills/recap/TROUBLESHOOTING.md +70 -0
  228. package/integrations/pi/skills/remember/EXAMPLES.md +67 -0
  229. package/integrations/pi/skills/remember/SKILL.md +66 -0
  230. package/integrations/pi/skills/remember/TROUBLESHOOTING.md +70 -0
  231. package/integrations/pi/skills/session-history/EXAMPLES.md +59 -0
  232. package/integrations/pi/skills/session-history/SKILL.md +67 -0
  233. package/integrations/pi/skills/session-history/TROUBLESHOOTING.md +70 -0
  234. package/integrations/pi/skills/write-agentmemory-skill/SKILL.md +41 -0
  235. package/integrations/pi/skills/write-agentmemory-skill/TROUBLESHOOTING.md +70 -0
  236. package/package.json +42 -12
  237. package/plugin/.claude-plugin/plugin.json +2 -2
  238. package/plugin/.codex-plugin/plugin.json +27 -4
  239. package/plugin/.devin-plugin/plugin.json +5 -3
  240. package/plugin/.mcp.codex.json +12 -0
  241. package/plugin/.mcp.copilot.json +2 -7
  242. package/plugin/.mcp.json +2 -7
  243. package/plugin/assets/icon.svg +35 -0
  244. package/plugin/cursor/hooks.json +23 -8
  245. package/plugin/hooks/hooks.copilot.json +11 -11
  246. package/plugin/hooks.json +67 -0
  247. package/plugin/opencode/README.md +267 -31
  248. package/plugin/opencode/agentmemory-capture.ts +594 -11
  249. package/plugin/opencode/plugin.json +1 -1
  250. package/plugin/plugin.json +1 -1
  251. package/plugin/scripts/_capture.d.mts +25 -0
  252. package/plugin/scripts/_capture.d.mts.map +1 -0
  253. package/plugin/scripts/_capture.mjs +944 -0
  254. package/plugin/scripts/_capture.mjs.map +1 -0
  255. package/plugin/scripts/antigravity-bridge.d.mts +1 -1
  256. package/plugin/scripts/antigravity-bridge.d.mts.map +1 -1
  257. package/plugin/scripts/antigravity-bridge.mjs +24 -15
  258. package/plugin/scripts/antigravity-bridge.mjs.map +1 -1
  259. package/plugin/scripts/cursor/run-detached.d.mts +1 -0
  260. package/plugin/scripts/cursor/run-detached.mjs +786 -0
  261. package/plugin/scripts/cursor/run-detached.mjs.map +1 -0
  262. package/plugin/scripts/cursor/run-hook.d.mts +1 -0
  263. package/plugin/scripts/cursor/run-hook.mjs +704 -0
  264. package/plugin/scripts/cursor/run-hook.mjs.map +1 -0
  265. package/plugin/scripts/notification.mjs +35 -29
  266. package/plugin/scripts/notification.mjs.map +1 -1
  267. package/plugin/scripts/plugin-bridge.mjs +409 -0
  268. package/plugin/scripts/post-commit.mjs +91 -1
  269. package/plugin/scripts/post-commit.mjs.map +1 -1
  270. package/plugin/scripts/post-tool-failure.mjs +71 -29
  271. package/plugin/scripts/post-tool-failure.mjs.map +1 -1
  272. package/plugin/scripts/post-tool-use.mjs +99 -39
  273. package/plugin/scripts/post-tool-use.mjs.map +1 -1
  274. package/plugin/scripts/pre-compact.mjs +113 -6
  275. package/plugin/scripts/pre-compact.mjs.map +1 -1
  276. package/plugin/scripts/pre-tool-use.mjs +91 -1
  277. package/plugin/scripts/pre-tool-use.mjs.map +1 -1
  278. package/plugin/scripts/prompt-submit.mjs +25 -25
  279. package/plugin/scripts/prompt-submit.mjs.map +1 -1
  280. package/plugin/scripts/session-end.mjs +67 -31
  281. package/plugin/scripts/session-end.mjs.map +1 -1
  282. package/plugin/scripts/session-start.mjs +110 -6
  283. package/plugin/scripts/session-start.mjs.map +1 -1
  284. package/plugin/scripts/stop.mjs +80 -8
  285. package/plugin/scripts/stop.mjs.map +1 -1
  286. package/plugin/scripts/subagent-start.mjs +28 -29
  287. package/plugin/scripts/subagent-start.mjs.map +1 -1
  288. package/plugin/scripts/subagent-stop.mjs +33 -29
  289. package/plugin/scripts/subagent-stop.mjs.map +1 -1
  290. package/plugin/scripts/task-completed.mjs +31 -31
  291. package/plugin/scripts/task-completed.mjs.map +1 -1
  292. package/plugin/skills/_shared/TROUBLESHOOTING.md +42 -12
  293. package/plugin/skills/agentmemory-agents/REFERENCE.md +10 -9
  294. package/plugin/skills/agentmemory-agents/SKILL.md +19 -5
  295. package/plugin/skills/agentmemory-agents/TROUBLESHOOTING.md +70 -0
  296. package/plugin/skills/agentmemory-architecture/SKILL.md +4 -4
  297. package/plugin/skills/agentmemory-architecture/TROUBLESHOOTING.md +70 -0
  298. package/plugin/skills/agentmemory-config/REFERENCE.md +48 -3
  299. package/plugin/skills/agentmemory-config/SKILL.md +51 -10
  300. package/plugin/skills/agentmemory-config/TROUBLESHOOTING.md +70 -0
  301. package/plugin/skills/agentmemory-hooks/SKILL.md +15 -5
  302. package/plugin/skills/agentmemory-hooks/TROUBLESHOOTING.md +70 -0
  303. package/plugin/skills/agentmemory-mcp-tools/REFERENCE.md +5 -5
  304. package/plugin/skills/agentmemory-mcp-tools/SKILL.md +6 -3
  305. package/plugin/skills/agentmemory-mcp-tools/TROUBLESHOOTING.md +70 -0
  306. package/plugin/skills/agentmemory-rest-api/REFERENCE.md +18 -2
  307. package/plugin/skills/agentmemory-rest-api/SKILL.md +12 -6
  308. package/plugin/skills/agentmemory-rest-api/TROUBLESHOOTING.md +70 -0
  309. package/plugin/skills/commit-context/SKILL.md +1 -1
  310. package/plugin/skills/commit-context/TROUBLESHOOTING.md +70 -0
  311. package/plugin/skills/commit-history/SKILL.md +1 -1
  312. package/plugin/skills/commit-history/TROUBLESHOOTING.md +70 -0
  313. package/plugin/skills/forget/EXAMPLES.md +19 -54
  314. package/plugin/skills/forget/SKILL.md +16 -12
  315. package/plugin/skills/forget/TROUBLESHOOTING.md +70 -0
  316. package/plugin/skills/handoff/EXAMPLES.md +5 -4
  317. package/plugin/skills/handoff/SKILL.md +13 -6
  318. package/plugin/skills/handoff/TROUBLESHOOTING.md +70 -0
  319. package/plugin/skills/lesson/SKILL.md +1 -1
  320. package/plugin/skills/lesson/TROUBLESHOOTING.md +70 -0
  321. package/plugin/skills/memory-discipline/SKILL.md +14 -6
  322. package/plugin/skills/memory-discipline/TROUBLESHOOTING.md +70 -0
  323. package/plugin/skills/recall/SKILL.md +18 -7
  324. package/plugin/skills/recall/TROUBLESHOOTING.md +70 -0
  325. package/plugin/skills/recap/EXAMPLES.md +5 -3
  326. package/plugin/skills/recap/SKILL.md +11 -6
  327. package/plugin/skills/recap/TROUBLESHOOTING.md +70 -0
  328. package/plugin/skills/remember/EXAMPLES.md +3 -3
  329. package/plugin/skills/remember/SKILL.md +1 -1
  330. package/plugin/skills/remember/TROUBLESHOOTING.md +70 -0
  331. package/plugin/skills/session-history/EXAMPLES.md +2 -2
  332. package/plugin/skills/session-history/SKILL.md +10 -4
  333. package/plugin/skills/session-history/TROUBLESHOOTING.md +70 -0
  334. package/plugin/skills/write-agentmemory-skill/SKILL.md +2 -1
  335. package/plugin/skills/write-agentmemory-skill/TROUBLESHOOTING.md +70 -0
  336. package/dist/connect-Qaeq15TQ.mjs.map +0 -1
  337. package/dist/docker-compose.yml +0 -47
  338. package/dist/iii-config.docker.yaml +0 -53
  339. package/dist/iii-config.yaml +0 -61
  340. package/dist/image-refs-BKLmp_sD.mjs.map +0 -1
  341. package/dist/image-refs-CJgZmb9X.mjs.map +0 -1
  342. package/dist/keyed-mutex-DTHtcZrt.mjs.map +0 -1
  343. package/dist/logger-BEB7pCI9.mjs +0 -43
  344. package/dist/logger-BEB7pCI9.mjs.map +0 -1
  345. package/dist/schema-BFZhNTF7.mjs.map +0 -1
  346. package/dist/src-G7yt8gGm.mjs.map +0 -1
  347. package/dist/standalone-BqNJDJ04.mjs.map +0 -1
  348. package/dist/tools-registry-BV_2bu7z.mjs +0 -1405
  349. package/dist/tools-registry-BV_2bu7z.mjs.map +0 -1
  350. package/docker-compose.yml +0 -47
  351. package/iii-config.docker.yaml +0 -53
  352. package/iii-config.yaml +0 -61
@@ -1,4 +1,94 @@
1
1
  #!/usr/bin/env node
2
+ import { readFileSync } from "node:fs";
3
+ import { homedir } from "node:os";
4
+ import { join } from "node:path";
5
+ //#region src/secret-store.ts
6
+ const SECRET_KEY = "AGENTMEMORY_SECRET";
7
+ function agentmemoryHomeDir() {
8
+ return process.env["AGENTMEMORY_DATA_DIR"] || join(homedir(), ".agentmemory");
9
+ }
10
+ function secretFilePath() {
11
+ return join(agentmemoryHomeDir(), "secret");
12
+ }
13
+ function usable(value) {
14
+ if (typeof value !== "string") return "";
15
+ const trimmed = value.trim();
16
+ if (!trimmed) return "";
17
+ if (trimmed.startsWith("${") && trimmed.endsWith("}")) return "";
18
+ return trimmed;
19
+ }
20
+ function unquote(value) {
21
+ const quote = value[0];
22
+ if ((quote === "\"" || quote === "'") && value.length > 1) {
23
+ const close = value.indexOf(quote, 1);
24
+ if (close !== -1) return value.slice(1, close);
25
+ }
26
+ const hash = value.indexOf(" #");
27
+ return hash === -1 ? value : value.slice(0, hash).trim();
28
+ }
29
+ function readEnvFileSecret() {
30
+ let content;
31
+ try {
32
+ content = readFileSync(process.env["AGENTMEMORY_ENV_FILE"] || join(homedir(), ".agentmemory", ".env"), "utf-8");
33
+ } catch {
34
+ return "";
35
+ }
36
+ if (typeof content !== "string") return "";
37
+ let found = "";
38
+ for (const line of content.split("\n")) {
39
+ const trimmed = line.trim();
40
+ if (trimmed.startsWith("#")) continue;
41
+ const eq = trimmed.indexOf("=");
42
+ if (eq === -1) continue;
43
+ if (trimmed.slice(0, eq).replace(/^export\s+/, "").trim() !== SECRET_KEY) continue;
44
+ found = usable(unquote(trimmed.slice(eq + 1).trim()));
45
+ }
46
+ return found;
47
+ }
48
+ function readSecretFile(path) {
49
+ try {
50
+ return usable(readFileSync(path, "utf-8"));
51
+ } catch {
52
+ return "";
53
+ }
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
+ }
71
+ function isLoopbackUrl(url) {
72
+ let hostname;
73
+ try {
74
+ hostname = new URL(url).hostname.toLowerCase();
75
+ } catch {
76
+ return false;
77
+ }
78
+ const bare = hostname.replace(/^\[|\]$/g, "");
79
+ return bare === "localhost" || bare === "::1" || /^127(?:\.\d{1,3}){3}$/.test(bare);
80
+ }
81
+ function resolveClientSecret(baseUrl, env = process.env) {
82
+ const fromEnv = usable(env[SECRET_KEY]);
83
+ if (fromEnv) return fromEnv;
84
+ if (env.AGENTMEMORY_CREDENTIAL_MODE === "explicit") return "";
85
+ if (!isLoopbackUrl(baseUrl)) return "";
86
+ return readEnvFileSecret() || readStoredSecret(env);
87
+ }
88
+ function canonicalSecretPath() {
89
+ return join(homedir(), ".agentmemory", "secret");
90
+ }
91
+ //#endregion
2
92
  //#region src/hooks/pre-tool-use.ts
3
93
  function isSdkChildContext(payload) {
4
94
  if (process.env["AGENTMEMORY_SDK_CHILD"] === "1") return true;
@@ -7,7 +97,7 @@ function isSdkChildContext(payload) {
7
97
  }
8
98
  const INJECT_CONTEXT = process.env["AGENTMEMORY_INJECT_CONTEXT"] === "true";
9
99
  const REST_URL = process.env["AGENTMEMORY_URL"] || "http://localhost:3111";
10
- const SECRET = process.env["AGENTMEMORY_SECRET"] || "";
100
+ const SECRET = resolveClientSecret(REST_URL);
11
101
  function authHeaders() {
12
102
  const h = { "Content-Type": "application/json" };
13
103
  if (SECRET) h["Authorization"] = `Bearer ${SECRET}`;
@@ -1 +1 @@
1
- {"version":3,"file":"pre-tool-use.mjs","names":[],"sources":["../../src/hooks/pre-tool-use.ts"],"sourcesContent":["#!/usr/bin/env node\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\n// Pre-tool-use enrichment hook.\n//\n// THIS HOOK IS A NO-OP BY DEFAULT AS OF 0.8.10 (#143). Previously it\n// fired /agentmemory/enrich on every Edit/Write/Read/Glob/Grep tool call\n// and wrote up to 4000 chars of context to stdout. Claude Code reads\n// PreToolUse stdout and prepends it to the model's next turn, which meant\n// agentmemory was silently injecting ~1000 tokens into every tool turn\n// via the user's Claude Code session. On Claude Pro that burned entire\n// allocations in a handful of messages (@adrianricardo, #143).\n//\n// Users who explicitly want pre-tool enrichment opt in with:\n// AGENTMEMORY_INJECT_CONTEXT=true in ~/.agentmemory/.env\n// and restart Claude Code. Expect your session input token count to grow\n// proportionally with the number of file-touching tool calls per turn.\nconst INJECT_CONTEXT = process.env[\"AGENTMEMORY_INJECT_CONTEXT\"] === \"true\";\n\nconst REST_URL = process.env[\"AGENTMEMORY_URL\"] || \"http://localhost:3111\";\nconst SECRET = process.env[\"AGENTMEMORY_SECRET\"] || \"\";\n\nfunction authHeaders(): Record<string, string> {\n const h: Record<string, string> = { \"Content-Type\": \"application/json\" };\n if (SECRET) h[\"Authorization\"] = `Bearer ${SECRET}`;\n return h;\n}\n\nasync function main() {\n // Default off: exit immediately so we don't even open stdin. This keeps\n // Claude Code's tool-call hot path as cheap as possible.\n if (!INJECT_CONTEXT) return;\n\n let input = \"\";\n for await (const chunk of process.stdin) {\n input += chunk;\n }\n\n let data: Record<string, unknown>;\n try {\n data = JSON.parse(input);\n } catch {\n return;\n }\n\n 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":";;AAEA,SAAS,kBAAkB,SAA2B;AACpD,KAAI,QAAQ,IAAI,6BAA6B,IAAK,QAAO;AACzD,KAAI,CAAC,WAAW,OAAO,YAAY,SAAU,QAAO;AACpD,QAAQ,QAAqC,eAAe;;AAiB9D,MAAM,iBAAiB,QAAQ,IAAI,kCAAkC;AAErE,MAAM,WAAW,QAAQ,IAAI,sBAAsB;AACnD,MAAM,SAAS,QAAQ,IAAI,yBAAyB;AAEpD,SAAS,cAAsC;CAC7C,MAAM,IAA4B,EAAE,gBAAgB,oBAAoB;AACxE,KAAI,OAAQ,GAAE,mBAAmB,UAAU;AAC3C,QAAO;;AAGT,eAAe,OAAO;AAGpB,KAAI,CAAC,eAAgB;CAErB,IAAI,QAAQ;AACZ,YAAW,MAAM,SAAS,QAAQ,MAChC,UAAS;CAGX,IAAI;AACJ,KAAI;AACF,SAAO,KAAK,MAAM,MAAM;SAClB;AACN;;AAGF,KAAI,CAAC,QAAQ,OAAO,SAAS,SAAU;AACvC,KAAI,kBAAkB,KAAK,CAAE;CAE7B,MAAM,WACJ,OAAO,KAAK,cAAc,WACtB,KAAK,YACL,OAAO,KAAK,aAAa,WACvB,KAAK,WACL,KAAA;AACR,KAAI,CAAC,SAAU;CAEf,MAAM,qBAAqB,SAAS,aAAa;AAEjD,KAAI,CAAC;EADc;EAAQ;EAAS;EAAU;EAAQ;EAAQ;EAAQ;EACxD,CAAC,SAAS,mBAAmB,CAAE;CAE7C,MAAM,eAAe,KAAK,cAAc,KAAK;CAC7C,MAAM,YACJ,OAAO,iBAAiB,YACxB,iBAAiB,QACjB,CAAC,MAAM,QAAQ,aAAa,GACvB,eACD,EAAE;CACR,MAAM,QAAkB,EAAE;CAC1B,MAAM,WACJ,uBAAuB,SACnB,CAAC,QAAQ,OAAO,GAChB;EAAC;EAAa;EAAQ;EAAQ;EAAU;AAC9C,MAAK,MAAM,OAAO,UAAU;EAC1B,MAAM,MAAM,UAAU;AACtB,MAAI,OAAO,QAAQ,YAAY,IAAI,SAAS,EAAG,OAAM,KAAK,IAAI;;AAEhE,KAAI,MAAM,WAAW,EAAG;CAExB,MAAM,QAAkB,EAAE;AAC1B,KAAI,uBAAuB,UAAU,uBAAuB,QAAQ;EAClE,MAAM,UAAU,UAAU;AAC1B,MAAI,OAAO,YAAY,YAAY,QAAQ,SAAS,EAClD,OAAM,KAAK,QAAQ;;CAIvB,MAAM,eAAe,KAAK,cAAc,KAAK,aAAa,KAAK;CAC/D,MAAM,YACJ,OAAO,iBAAiB,YAAY,aAAa,SAAS,IACtD,eACA;CACN,MAAM,UACJ,OAAO,KAAK,YAAY,YAAY,KAAK,QAAQ,MAAM,CAAC,SAAS,IAC7D,KAAK,QAAQ,MAAM,GACnB,KAAA;AAEN,KAAI;EACF,MAAM,MAAM,MAAM,MAAM,GAAG,SAAS,sBAAsB;GACxD,QAAQ;GACR,SAAS,aAAa;GACtB,MAAM,KAAK,UAAU;IACnB;IACA;IACA;IACA;IACA,GAAI,YAAY,KAAA,KAAa,EAAE,SAAS;IACzC,CAAC;GACF,QAAQ,YAAY,QAAQ,IAAK;GAClC,CAAC;AAEF,MAAI,IAAI,IAAI;GACV,MAAM,SAAU,MAAM,IAAI,MAAM;AAChC,OAAI,OAAO,QACT,SAAQ,OAAO,MAAM,OAAO,QAAQ;;SAGlC;;AAKV,MAAM,CAAC,YAAY,QAAQ,KAAK,EAAE,CAAC"}
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,13 +1,15 @@
1
1
  #!/usr/bin/env node
2
2
  import { execSync } from "node:child_process";
3
- import { basename } from "node:path";
3
+ import { realpathSync } from "node:fs";
4
+ import { basename, dirname, resolve } from "node:path";
5
+ import { captureObservation, isDrainChild, runDrainChild, withEventId } from "./_capture.mjs";
4
6
  //#region src/hooks/_project.ts
5
7
  function resolveProject(cwd) {
6
8
  const explicit = process.env["AGENTMEMORY_PROJECT_NAME"];
7
9
  if (explicit && explicit.trim()) return explicit.trim();
8
10
  const dir = cwd && cwd.trim() ? cwd : process.cwd();
9
11
  try {
10
- const top = execSync("git rev-parse --show-toplevel", {
12
+ const [top, gitDir, commonDir] = execSync("git rev-parse --show-toplevel --git-dir --git-common-dir", {
11
13
  cwd: dir,
12
14
  stdio: [
13
15
  "ignore",
@@ -15,11 +17,18 @@ function resolveProject(cwd) {
15
17
  "ignore"
16
18
  ],
17
19
  timeout: 500
18
- }).toString().trim();
19
- if (top) return basename(top);
20
+ }).toString().trim().split(/\r?\n/);
21
+ if (top) return basename(repositoryRoot(dir, top, gitDir, commonDir));
20
22
  } catch {}
21
23
  return basename(dir);
22
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
+ }
23
32
  function hookCwd(data) {
24
33
  if (!data || typeof data !== "object") return void 0;
25
34
  if (typeof data.cwd === "string" && data.cwd.trim()) return data.cwd;
@@ -37,14 +46,10 @@ function isSdkChildContext(payload) {
37
46
  if (!payload || typeof payload !== "object") return false;
38
47
  return payload.entrypoint === "sdk-ts";
39
48
  }
40
- const REST_URL = process.env["AGENTMEMORY_URL"] || "http://localhost:3111";
41
- const SECRET = process.env["AGENTMEMORY_SECRET"] || "";
42
- function authHeaders() {
43
- const h = { "Content-Type": "application/json" };
44
- if (SECRET) h["Authorization"] = `Bearer ${SECRET}`;
45
- return h;
46
- }
49
+ const OBSERVE_TIMEOUT_MS = 3e3;
50
+ const EXIT_CAP_MS = 3500;
47
51
  async function main() {
52
+ if (isDrainChild()) return runDrainChild();
48
53
  let input = "";
49
54
  for await (const chunk of process.stdin) input += chunk;
50
55
  let data;
@@ -57,20 +62,15 @@ async function main() {
57
62
  if (isSdkChildContext(data)) return;
58
63
  const sessionId = data.session_id || data.sessionId || data.conversation_id || "unknown";
59
64
  const cwd = hookCwd(data) || process.cwd();
60
- fetch(`${REST_URL}/agentmemory/observe`, {
61
- method: "POST",
62
- headers: authHeaders(),
63
- body: JSON.stringify({
64
- hookType: "prompt_submit",
65
- sessionId,
66
- project: resolveProject(cwd),
67
- cwd,
68
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
69
- data: { prompt: data.prompt ?? data.userPrompt }
70
- }),
71
- signal: AbortSignal.timeout(3e3)
72
- }).catch(() => {});
73
- setTimeout(() => process.exit(0), 500).unref();
65
+ captureObservation(withEventId({
66
+ hookType: "prompt_submit",
67
+ sessionId,
68
+ project: resolveProject(cwd),
69
+ cwd,
70
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
71
+ data: { prompt: data.prompt ?? data.userPrompt }
72
+ }, data), OBSERVE_TIMEOUT_MS);
73
+ setTimeout(() => process.exit(0), EXIT_CAP_MS).unref();
74
74
  }
75
75
  main().catch(() => process.exit(0));
76
76
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"prompt-submit.mjs","names":[],"sources":["../../src/hooks/_project.ts","../../src/hooks/prompt-submit.ts"],"sourcesContent":["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 { 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 = process.env[\"AGENTMEMORY_SECRET\"] || \"\";\n\nfunction authHeaders(): Record<string, string> {\n const h: Record<string, string> = { \"Content-Type\": \"application/json\" };\n if (SECRET) h[\"Authorization\"] = `Bearer ${SECRET}`;\n return h;\n}\n\nasync function main() {\n let input = \"\";\n for await (const chunk of process.stdin) {\n input += chunk;\n }\n\n let data: Record<string, unknown>;\n try {\n data = JSON.parse(input);\n } catch {\n return;\n }\n\n 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\n const cwd = hookCwd(data) || process.cwd();\n\n fetch(`${REST_URL}/agentmemory/observe`, {\n method: \"POST\",\n headers: authHeaders(),\n body: JSON.stringify({\n hookType: \"prompt_submit\",\n sessionId,\n project: resolveProject(cwd),\n cwd,\n timestamp: new Date().toISOString(),\n data: { prompt: data.prompt ?? data.userPrompt },\n }),\n signal: AbortSignal.timeout(3000),\n }).catch(() => {});\n setTimeout(() => process.exit(0), 500).unref();\n}\n\nmain().catch(() => process.exit(0));\n"],"mappings":";;;;AAIA,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;;;;AC7B9C,SAAS,kBAAkB,SAA2B;AACpD,KAAI,QAAQ,IAAI,6BAA6B,IAAK,QAAO;AACzD,KAAI,CAAC,WAAW,OAAO,YAAY,SAAU,QAAO;AACpD,QAAQ,QAAqC,eAAe;;AAG9D,MAAM,WAAW,QAAQ,IAAI,sBAAsB;AACnD,MAAM,SAAS,QAAQ,IAAI,yBAAyB;AAEpD,SAAS,cAAsC;CAC7C,MAAM,IAA4B,EAAE,gBAAgB,oBAAoB;AACxE,KAAI,OAAQ,GAAE,mBAAmB,UAAU;AAC3C,QAAO;;AAGT,eAAe,OAAO;CACpB,IAAI,QAAQ;AACZ,YAAW,MAAM,SAAS,QAAQ,MAChC,UAAS;CAGX,IAAI;AACJ,KAAI;AACF,SAAO,KAAK,MAAM,MAAM;SAClB;AACN;;AAGF,KAAI,CAAC,QAAQ,OAAO,SAAS,SAAU;AACvC,KAAI,kBAAkB,KAAK,CAAE;CAE7B,MAAM,YAAc,KAAK,cAAc,KAAK,aAAa,KAAK,mBAA+B;CAE7F,MAAM,MAAM,QAAQ,KAAK,IAAI,QAAQ,KAAK;AAE1C,OAAM,GAAG,SAAS,uBAAuB;EACvC,QAAQ;EACR,SAAS,aAAa;EACtB,MAAM,KAAK,UAAU;GACnB,UAAU;GACV;GACA,SAAS,eAAe,IAAI;GAC5B;GACA,4BAAW,IAAI,MAAM,EAAC,aAAa;GACnC,MAAM,EAAE,QAAQ,KAAK,UAAU,KAAK,YAAY;GACjD,CAAC;EACF,QAAQ,YAAY,QAAQ,IAAK;EAClC,CAAC,CAAC,YAAY,GAAG;AAClB,kBAAiB,QAAQ,KAAK,EAAE,EAAE,IAAI,CAAC,OAAO;;AAGhD,MAAM,CAAC,YAAY,QAAQ,KAAK,EAAE,CAAC"}
1
+ {"version":3,"file":"prompt-submit.mjs","names":[],"sources":["../../src/hooks/_project.ts","../../src/hooks/prompt-submit.ts"],"sourcesContent":["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 { 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\n const cwd = hookCwd(data) || process.cwd();\n\n void captureObservation(\n withEventId(\n {\n hookType: \"prompt_submit\",\n sessionId,\n project: resolveProject(cwd),\n cwd,\n timestamp: new Date().toISOString(),\n data: { prompt: data.prompt ?? data.userPrompt },\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":";;;;;;AAKA,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,CAAC,KAAK,QAAQ,aAAa,SAC/B,4DACA;GACE,KAAK;GACL,OAAO;IAAC;IAAU;IAAQ;IAAS;GACnC,SAAS;GACV,CACF,CACE,UAAU,CACV,MAAM,CACN,MAAM,QAAQ;AACjB,MAAI,IAAK,QAAO,SAAS,eAAe,KAAK,KAAK,QAAQ,UAAU,CAAC;SAC/D;AACR,QAAO,SAAS,IAAI;;AAGtB,SAAS,eAAe,KAAa,KAAa,QAAiB,WAA4B;AAC7F,KAAI,CAAC,UAAU,CAAC,UAAW,QAAO;CAClC,MAAM,WAAW,aAAa,IAAI;CAClC,MAAM,SAAS,QAAQ,UAAU,UAAU;AAC3C,KAAI,QAAQ,UAAU,OAAO,KAAK,UAAU,SAAS,OAAO,KAAK,OAAQ,QAAO;AAChF,QAAO,QAAQ,OAAO;;AAGxB,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;;;;ACzC9C,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;CAE7F,MAAM,MAAM,QAAQ,KAAK,IAAI,QAAQ,KAAK;AAErC,oBACH,YACE;EACE,UAAU;EACV;EACA,SAAS,eAAe,IAAI;EAC5B;EACA,4BAAW,IAAI,MAAM,EAAC,aAAa;EACnC,MAAM,EAAE,QAAQ,KAAK,UAAU,KAAK,YAAY;EACjD,EACD,KACD,EACD,mBACD;AACD,kBAAiB,QAAQ,KAAK,EAAE,EAAE,YAAY,CAAC,OAAO;;AAGxD,MAAM,CAAC,YAAY,QAAQ,KAAK,EAAE,CAAC"}
@@ -1,14 +1,15 @@
1
1
  #!/usr/bin/env node
2
- import { readFileSync } from "node:fs";
2
+ import { readFileSync, realpathSync } from "node:fs";
3
3
  import { execSync } from "node:child_process";
4
- import { basename } from "node:path";
4
+ import { basename, dirname, resolve } from "node:path";
5
+ import { REST_URL, authHeaders, captureObservation, isDrainChild, runDrainChild, withEventId } from "./_capture.mjs";
5
6
  //#region src/hooks/_project.ts
6
7
  function resolveProject(cwd) {
7
8
  const explicit = process.env["AGENTMEMORY_PROJECT_NAME"];
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;
@@ -38,12 +46,22 @@ function isSdkChildContext(payload) {
38
46
  if (!payload || typeof payload !== "object") return false;
39
47
  return payload.entrypoint === "sdk-ts";
40
48
  }
41
- const REST_URL = process.env["AGENTMEMORY_URL"] || "http://localhost:3111";
42
- const SECRET = process.env["AGENTMEMORY_SECRET"] || "";
43
- function authHeaders() {
44
- const h = { "Content-Type": "application/json" };
45
- if (SECRET) h["Authorization"] = `Bearer ${SECRET}`;
46
- return h;
49
+ const HARNESS_TEXT = /^(?:<(?:command-name|command-message|command-args|local-command-stdout|local-command-stderr|local-command-caveat|bash-input|bash-stdout|bash-stderr|task-notification|system-reminder|ci-monitor-event|cross-session-message|scheduled-task)(?=[\s>])|\[Request interrupted)/;
50
+ function isUserTurn(msg) {
51
+ if (msg.isSidechain || msg.isMeta || msg.isCompactSummary) return false;
52
+ return msg.role === "user" || msg.type === "user" || msg.message?.role === "user";
53
+ }
54
+ function promptText(raw) {
55
+ const m = raw.match(/<user_query>\n?([\s\S]*?)\n?<\/user_query>/);
56
+ const text = (m ? m[1] : raw).trim();
57
+ return HARNESS_TEXT.test(text) ? "" : text;
58
+ }
59
+ function turnTexts(content) {
60
+ if (typeof content === "string") return [content];
61
+ if (!Array.isArray(content)) return [];
62
+ const texts = [];
63
+ for (const block of content) if (block?.type === "text" && typeof block.text === "string") texts.push(block.text);
64
+ return texts;
47
65
  }
48
66
  function extractTranscriptPrompts(data) {
49
67
  const path = data.transcript_path;
@@ -63,18 +81,30 @@ function extractTranscriptPrompts(data) {
63
81
  } catch {
64
82
  continue;
65
83
  }
66
- if (msg.role !== "user") continue;
67
- for (const block of msg.message?.content ?? []) {
84
+ if (msg.type === "USER_INPUT" && msg.source === "USER_EXPLICIT" && typeof msg.content === "string") {
85
+ if (prompts.length >= 50) return prompts;
86
+ const match = msg.content.match(/<USER_REQUEST>\n?([\s\S]*?)\n?<\/USER_REQUEST>/);
87
+ const text = (match ? match[1] : msg.content).trim();
88
+ if (text) prompts.push({ prompt: text.slice(0, 8e3) });
89
+ continue;
90
+ }
91
+ if (!isUserTurn(msg)) continue;
92
+ const texts = turnTexts(msg.message?.content).map(promptText).filter(Boolean);
93
+ const promptId = typeof msg.promptId === "string" ? msg.promptId : void 0;
94
+ const timestamp = typeof msg.timestamp === "string" ? msg.timestamp : void 0;
95
+ for (const text of promptId ? [texts.join("\n\n")].filter(Boolean) : texts) {
68
96
  if (prompts.length >= 50) return prompts;
69
- if (block.type !== "text" || typeof block.text !== "string") continue;
70
- const m = block.text.match(/<user_query>\n?([\s\S]*?)\n?<\/user_query>/);
71
- const text = (m ? m[1] : block.text).trim();
72
- if (text) prompts.push(text.slice(0, 8e3));
97
+ prompts.push({
98
+ prompt: text.slice(0, 8e3),
99
+ promptId,
100
+ timestamp
101
+ });
73
102
  }
74
103
  }
75
104
  return prompts;
76
105
  }
77
106
  async function main() {
107
+ if (isDrainChild()) return runDrainChild();
78
108
  let input = "";
79
109
  for await (const chunk of process.stdin) input += chunk;
80
110
  let data;
@@ -91,24 +121,30 @@ async function main() {
91
121
  const cwd = hookCwd(data) || process.cwd();
92
122
  const project = resolveProject(cwd);
93
123
  const timestamp = (/* @__PURE__ */ new Date()).toISOString();
94
- await Promise.allSettled(transcriptPrompts.map((prompt) => fetch(`${REST_URL}/agentmemory/observe`, {
95
- method: "POST",
96
- headers: authHeaders(),
97
- body: JSON.stringify({
98
- hookType: "prompt_submit",
99
- sessionId,
100
- project,
101
- cwd,
102
- timestamp,
103
- data: { prompt }
104
- }),
105
- signal: AbortSignal.timeout(3e3)
106
- })));
124
+ await Promise.allSettled(transcriptPrompts.map(({ prompt, promptId, timestamp: at }, index) => captureObservation(withEventId({
125
+ hookType: "prompt_submit",
126
+ sessionId,
127
+ project,
128
+ cwd,
129
+ timestamp: at ?? timestamp,
130
+ data: {
131
+ prompt,
132
+ backfill: true
133
+ }
134
+ }, promptId ? { prompt_id: promptId } : {}, {
135
+ source: "transcript",
136
+ transcript: data.transcript_path,
137
+ index,
138
+ prompt
139
+ }, { stable: true }), 3e3)));
107
140
  }
108
141
  fetch(`${REST_URL}/agentmemory/session/end`, {
109
142
  method: "POST",
110
143
  headers: authHeaders(),
111
- body: JSON.stringify({ sessionId }),
144
+ body: JSON.stringify({
145
+ sessionId,
146
+ final: true
147
+ }),
112
148
  signal: AbortSignal.timeout(3e4)
113
149
  }).catch(() => {});
114
150
  if (process.env["CLAUDE_MEMORY_BRIDGE"] === "true") fetch(`${REST_URL}/agentmemory/claude-bridge/sync`, {
@@ -1 +1 @@
1
- {"version":3,"file":"session-end.mjs","names":[],"sources":["../../src/hooks/_project.ts","../../src/hooks/session-end.ts"],"sourcesContent":["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 { readFileSync } from \"node:fs\";\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 = process.env[\"AGENTMEMORY_SECRET\"] || \"\";\n\nfunction authHeaders(): Record<string, string> {\n const h: Record<string, string> = { \"Content-Type\": \"application/json\" };\n if (SECRET) h[\"Authorization\"] = `Bearer ${SECRET}`;\n return h;\n}\n\nfunction extractTranscriptPrompts(data: Record<string, unknown>): string[] {\n const path = data.transcript_path;\n if (typeof path !== \"string\" || !path.endsWith(\".jsonl\")) return [];\n let raw: string;\n try {\n raw = readFileSync(path, \"utf-8\");\n } catch {\n return [];\n }\n const prompts: string[] = [];\n for (const line of raw.split(\"\\n\")) {\n if (!line.trim()) continue;\n let msg: {\n role?: string;\n message?: { content?: Array<{ type?: string; text?: string }> };\n };\n try {\n msg = JSON.parse(line);\n } catch {\n continue;\n }\n if (msg.role !== \"user\") continue;\n for (const block of msg.message?.content ?? []) {\n if (prompts.length >= 50) return prompts;\n if (block.type !== \"text\" || typeof block.text !== \"string\") continue;\n const m = block.text.match(/<user_query>\\n?([\\s\\S]*?)\\n?<\\/user_query>/);\n const text = (m ? m[1] : block.text).trim();\n if (text) prompts.push(text.slice(0, 8000));\n }\n }\n return prompts;\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\n const transcriptPrompts = extractTranscriptPrompts(data);\n if (transcriptPrompts.length > 0) {\n const cwd = hookCwd(data) || process.cwd();\n const project = resolveProject(cwd);\n const timestamp = new Date().toISOString();\n await Promise.allSettled(\n transcriptPrompts.map((prompt) =>\n fetch(`${REST_URL}/agentmemory/observe`, {\n method: \"POST\",\n headers: authHeaders(),\n body: JSON.stringify({\n hookType: \"prompt_submit\",\n sessionId,\n project,\n cwd,\n timestamp,\n data: { prompt },\n }),\n signal: AbortSignal.timeout(3000),\n }),\n ),\n );\n }\n\n fetch(`${REST_URL}/agentmemory/session/end`, {\n method: \"POST\",\n headers: authHeaders(),\n body: JSON.stringify({ sessionId }),\n signal: AbortSignal.timeout(30000),\n }).catch(() => {});\n\n if (process.env[\"CLAUDE_MEMORY_BRIDGE\"] === \"true\") {\n fetch(`${REST_URL}/agentmemory/claude-bridge/sync`, {\n method: \"POST\",\n headers: authHeaders(),\n signal: AbortSignal.timeout(30000),\n }).catch(() => {});\n }\n\n setTimeout(() => process.exit(0), 1500).unref();\n}\n\nmain().catch(() => process.exit(0));\n"],"mappings":";;;;;AAIA,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;;;;AC5B9C,SAAS,kBAAkB,SAA2B;AACpD,KAAI,QAAQ,IAAI,6BAA6B,IAAK,QAAO;AACzD,KAAI,CAAC,WAAW,OAAO,YAAY,SAAU,QAAO;AACpD,QAAQ,QAAqC,eAAe;;AAG9D,MAAM,WAAW,QAAQ,IAAI,sBAAsB;AACnD,MAAM,SAAS,QAAQ,IAAI,yBAAyB;AAEpD,SAAS,cAAsC;CAC7C,MAAM,IAA4B,EAAE,gBAAgB,oBAAoB;AACxE,KAAI,OAAQ,GAAE,mBAAmB,UAAU;AAC3C,QAAO;;AAGT,SAAS,yBAAyB,MAAyC;CACzE,MAAM,OAAO,KAAK;AAClB,KAAI,OAAO,SAAS,YAAY,CAAC,KAAK,SAAS,SAAS,CAAE,QAAO,EAAE;CACnE,IAAI;AACJ,KAAI;AACF,QAAM,aAAa,MAAM,QAAQ;SAC3B;AACN,SAAO,EAAE;;CAEX,MAAM,UAAoB,EAAE;AAC5B,MAAK,MAAM,QAAQ,IAAI,MAAM,KAAK,EAAE;AAClC,MAAI,CAAC,KAAK,MAAM,CAAE;EAClB,IAAI;AAIJ,MAAI;AACF,SAAM,KAAK,MAAM,KAAK;UAChB;AACN;;AAEF,MAAI,IAAI,SAAS,OAAQ;AACzB,OAAK,MAAM,SAAS,IAAI,SAAS,WAAW,EAAE,EAAE;AAC9C,OAAI,QAAQ,UAAU,GAAI,QAAO;AACjC,OAAI,MAAM,SAAS,UAAU,OAAO,MAAM,SAAS,SAAU;GAC7D,MAAM,IAAI,MAAM,KAAK,MAAM,6CAA6C;GACxE,MAAM,QAAQ,IAAI,EAAE,KAAK,MAAM,MAAM,MAAM;AAC3C,OAAI,KAAM,SAAQ,KAAK,KAAK,MAAM,GAAG,IAAK,CAAC;;;AAG/C,QAAO;;AAGT,eAAe,OAAO;CACpB,IAAI,QAAQ;AACZ,YAAW,MAAM,SAAS,QAAQ,MAChC,UAAS;CAGX,IAAI;AACJ,KAAI;AACF,SAAO,KAAK,MAAM,MAAM;SAClB;AACN;;AAGF,KAAI,CAAC,QAAQ,OAAO,SAAS,SAAU;AACvC,KAAI,kBAAkB,KAAK,CAAE;CAE7B,MAAM,YAAc,KAAK,cAAc,KAAK,aAAa,KAAK,mBAA+B;CAE7F,MAAM,oBAAoB,yBAAyB,KAAK;AACxD,KAAI,kBAAkB,SAAS,GAAG;EAChC,MAAM,MAAM,QAAQ,KAAK,IAAI,QAAQ,KAAK;EAC1C,MAAM,UAAU,eAAe,IAAI;EACnC,MAAM,6BAAY,IAAI,MAAM,EAAC,aAAa;AAC1C,QAAM,QAAQ,WACZ,kBAAkB,KAAK,WACrB,MAAM,GAAG,SAAS,uBAAuB;GACvC,QAAQ;GACR,SAAS,aAAa;GACtB,MAAM,KAAK,UAAU;IACnB,UAAU;IACV;IACA;IACA;IACA;IACA,MAAM,EAAE,QAAQ;IACjB,CAAC;GACF,QAAQ,YAAY,QAAQ,IAAK;GAClC,CAAC,CACH,CACF;;AAGH,OAAM,GAAG,SAAS,2BAA2B;EAC3C,QAAQ;EACR,SAAS,aAAa;EACtB,MAAM,KAAK,UAAU,EAAE,WAAW,CAAC;EACnC,QAAQ,YAAY,QAAQ,IAAM;EACnC,CAAC,CAAC,YAAY,GAAG;AAElB,KAAI,QAAQ,IAAI,4BAA4B,OAC1C,OAAM,GAAG,SAAS,kCAAkC;EAClD,QAAQ;EACR,SAAS,aAAa;EACtB,QAAQ,YAAY,QAAQ,IAAM;EACnC,CAAC,CAAC,YAAY,GAAG;AAGpB,kBAAiB,QAAQ,KAAK,EAAE,EAAE,KAAK,CAAC,OAAO;;AAGjD,MAAM,CAAC,YAAY,QAAQ,KAAK,EAAE,CAAC"}
1
+ {"version":3,"file":"session-end.mjs","names":[],"sources":["../../src/hooks/_project.ts","../../src/hooks/session-end.ts"],"sourcesContent":["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 { readFileSync } from \"node:fs\";\nimport { resolveProject, hookCwd } from \"./_project.js\";\nimport { REST_URL, authHeaders, 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\ntype TranscriptBlock = { type?: string; text?: string };\ntype TranscriptLine = {\n role?: string;\n type?: string;\n source?: string;\n content?: unknown;\n isSidechain?: boolean;\n isMeta?: boolean;\n isCompactSummary?: boolean;\n promptId?: string;\n timestamp?: string;\n message?: { role?: string; content?: string | TranscriptBlock[] };\n};\n\ntype TranscriptPrompt = { prompt: string; promptId?: string; timestamp?: string };\n\nconst HARNESS_TEXT =\n /^(?:<(?:command-name|command-message|command-args|local-command-stdout|local-command-stderr|local-command-caveat|bash-input|bash-stdout|bash-stderr|task-notification|system-reminder|ci-monitor-event|cross-session-message|scheduled-task)(?=[\\s>])|\\[Request interrupted)/;\n\nfunction isUserTurn(msg: TranscriptLine): boolean {\n if (msg.isSidechain || msg.isMeta || msg.isCompactSummary) return false;\n return (\n msg.role === \"user\" || msg.type === \"user\" || msg.message?.role === \"user\"\n );\n}\n\nfunction promptText(raw: string): string {\n const m = raw.match(/<user_query>\\n?([\\s\\S]*?)\\n?<\\/user_query>/);\n const text = (m ? m[1] : raw).trim();\n return HARNESS_TEXT.test(text) ? \"\" : text;\n}\n\nfunction turnTexts(content: string | TranscriptBlock[] | undefined): string[] {\n if (typeof content === \"string\") return [content];\n if (!Array.isArray(content)) return [];\n const texts: string[] = [];\n for (const block of content) {\n if (block?.type === \"text\" && typeof block.text === \"string\") {\n texts.push(block.text);\n }\n }\n return texts;\n}\n\nfunction extractTranscriptPrompts(data: Record<string, unknown>): TranscriptPrompt[] {\n const path = data.transcript_path;\n if (typeof path !== \"string\" || !path.endsWith(\".jsonl\")) return [];\n let raw: string;\n try {\n raw = readFileSync(path, \"utf-8\");\n } catch {\n return [];\n }\n const prompts: TranscriptPrompt[] = [];\n for (const line of raw.split(\"\\n\")) {\n if (!line.trim()) continue;\n let msg: TranscriptLine;\n try {\n msg = JSON.parse(line);\n } catch {\n continue;\n }\n if (msg.type === \"USER_INPUT\" && msg.source === \"USER_EXPLICIT\" && typeof msg.content === \"string\") {\n if (prompts.length >= 50) return prompts;\n const match = msg.content.match(/<USER_REQUEST>\\n?([\\s\\S]*?)\\n?<\\/USER_REQUEST>/);\n const text = (match ? match[1] : msg.content).trim();\n if (text) prompts.push({ prompt: text.slice(0, 8000) });\n continue;\n }\n if (!isUserTurn(msg)) continue;\n const texts = turnTexts(msg.message?.content).map(promptText).filter(Boolean);\n const promptId = typeof msg.promptId === \"string\" ? msg.promptId : undefined;\n const timestamp = typeof msg.timestamp === \"string\" ? msg.timestamp : undefined;\n for (const text of promptId ? [texts.join(\"\\n\\n\")].filter(Boolean) : texts) {\n if (prompts.length >= 50) return prompts;\n prompts.push({ prompt: text.slice(0, 8000), promptId, timestamp });\n }\n }\n return prompts;\n}\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\n const transcriptPrompts = extractTranscriptPrompts(data);\n if (transcriptPrompts.length > 0) {\n const cwd = hookCwd(data) || process.cwd();\n const project = resolveProject(cwd);\n const timestamp = new Date().toISOString();\n await Promise.allSettled(\n transcriptPrompts.map(({ prompt, promptId, timestamp: at }, index) =>\n captureObservation(\n withEventId(\n { hookType: \"prompt_submit\", sessionId, project, cwd, timestamp: at ?? timestamp, data: { prompt, backfill: true } },\n promptId ? { prompt_id: promptId } : {},\n { source: \"transcript\", transcript: data.transcript_path, index, prompt },\n { stable: true },\n ),\n 3000,\n ),\n ),\n );\n }\n\n fetch(`${REST_URL}/agentmemory/session/end`, {\n method: \"POST\",\n headers: authHeaders(),\n body: JSON.stringify({ sessionId, final: true }),\n signal: AbortSignal.timeout(30000),\n }).catch(() => {});\n\n if (process.env[\"CLAUDE_MEMORY_BRIDGE\"] === \"true\") {\n fetch(`${REST_URL}/agentmemory/claude-bridge/sync`, {\n method: \"POST\",\n headers: authHeaders(),\n signal: AbortSignal.timeout(30000),\n }).catch(() => {});\n }\n\n setTimeout(() => process.exit(0), 1500).unref();\n}\n\nmain().catch(() => process.exit(0));\n"],"mappings":"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"}
@@ -1,13 +1,102 @@
1
1
  #!/usr/bin/env node
2
+ import { readFileSync, realpathSync } from "node:fs";
3
+ import { homedir } from "node:os";
4
+ import { basename, dirname, join, resolve } from "node:path";
2
5
  import { execSync } from "node:child_process";
3
- import { basename } from "node:path";
6
+ //#region src/secret-store.ts
7
+ const SECRET_KEY = "AGENTMEMORY_SECRET";
8
+ function agentmemoryHomeDir() {
9
+ return process.env["AGENTMEMORY_DATA_DIR"] || join(homedir(), ".agentmemory");
10
+ }
11
+ function secretFilePath() {
12
+ return join(agentmemoryHomeDir(), "secret");
13
+ }
14
+ function usable(value) {
15
+ if (typeof value !== "string") return "";
16
+ const trimmed = value.trim();
17
+ if (!trimmed) return "";
18
+ if (trimmed.startsWith("${") && trimmed.endsWith("}")) return "";
19
+ return trimmed;
20
+ }
21
+ function unquote(value) {
22
+ const quote = value[0];
23
+ if ((quote === "\"" || quote === "'") && value.length > 1) {
24
+ const close = value.indexOf(quote, 1);
25
+ if (close !== -1) return value.slice(1, close);
26
+ }
27
+ const hash = value.indexOf(" #");
28
+ return hash === -1 ? value : value.slice(0, hash).trim();
29
+ }
30
+ function readEnvFileSecret() {
31
+ let content;
32
+ try {
33
+ content = readFileSync(process.env["AGENTMEMORY_ENV_FILE"] || join(homedir(), ".agentmemory", ".env"), "utf-8");
34
+ } catch {
35
+ return "";
36
+ }
37
+ if (typeof content !== "string") return "";
38
+ let found = "";
39
+ for (const line of content.split("\n")) {
40
+ const trimmed = line.trim();
41
+ if (trimmed.startsWith("#")) continue;
42
+ const eq = trimmed.indexOf("=");
43
+ if (eq === -1) continue;
44
+ if (trimmed.slice(0, eq).replace(/^export\s+/, "").trim() !== SECRET_KEY) continue;
45
+ found = usable(unquote(trimmed.slice(eq + 1).trim()));
46
+ }
47
+ return found;
48
+ }
49
+ function readSecretFile(path) {
50
+ try {
51
+ return usable(readFileSync(path, "utf-8"));
52
+ } catch {
53
+ return "";
54
+ }
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
+ }
72
+ function isLoopbackUrl(url) {
73
+ let hostname;
74
+ try {
75
+ hostname = new URL(url).hostname.toLowerCase();
76
+ } catch {
77
+ return false;
78
+ }
79
+ const bare = hostname.replace(/^\[|\]$/g, "");
80
+ return bare === "localhost" || bare === "::1" || /^127(?:\.\d{1,3}){3}$/.test(bare);
81
+ }
82
+ function resolveClientSecret(baseUrl, env = process.env) {
83
+ const fromEnv = usable(env[SECRET_KEY]);
84
+ if (fromEnv) return fromEnv;
85
+ if (env.AGENTMEMORY_CREDENTIAL_MODE === "explicit") return "";
86
+ if (!isLoopbackUrl(baseUrl)) return "";
87
+ return readEnvFileSecret() || readStoredSecret(env);
88
+ }
89
+ function canonicalSecretPath() {
90
+ return join(homedir(), ".agentmemory", "secret");
91
+ }
92
+ //#endregion
4
93
  //#region src/hooks/_project.ts
5
94
  function resolveProject(cwd) {
6
95
  const explicit = process.env["AGENTMEMORY_PROJECT_NAME"];
7
96
  if (explicit && explicit.trim()) return explicit.trim();
8
97
  const dir = cwd && cwd.trim() ? cwd : process.cwd();
9
98
  try {
10
- const top = execSync("git rev-parse --show-toplevel", {
99
+ const [top, gitDir, commonDir] = execSync("git rev-parse --show-toplevel --git-dir --git-common-dir", {
11
100
  cwd: dir,
12
101
  stdio: [
13
102
  "ignore",
@@ -15,11 +104,18 @@ function resolveProject(cwd) {
15
104
  "ignore"
16
105
  ],
17
106
  timeout: 500
18
- }).toString().trim();
19
- if (top) return basename(top);
107
+ }).toString().trim().split(/\r?\n/);
108
+ if (top) return basename(repositoryRoot(dir, top, gitDir, commonDir));
20
109
  } catch {}
21
110
  return basename(dir);
22
111
  }
112
+ function repositoryRoot(dir, top, gitDir, commonDir) {
113
+ if (!gitDir || !commonDir) return top;
114
+ const physical = realpathSync(dir);
115
+ const common = resolve(physical, commonDir);
116
+ if (resolve(physical, gitDir) === common || basename(common) !== ".git") return top;
117
+ return dirname(common);
118
+ }
23
119
  function hookCwd(data) {
24
120
  if (!data || typeof data !== "object") return void 0;
25
121
  if (typeof data.cwd === "string" && data.cwd.trim()) return data.cwd;
@@ -39,7 +135,7 @@ function isSdkChildContext(payload) {
39
135
  }
40
136
  const INJECT_CONTEXT = process.env["AGENTMEMORY_INJECT_CONTEXT"] === "true";
41
137
  const REST_URL = process.env["AGENTMEMORY_URL"] || "http://localhost:3111";
42
- const SECRET = process.env["AGENTMEMORY_SECRET"] || "";
138
+ const SECRET = resolveClientSecret(REST_URL);
43
139
  const INJECT_TIMEOUT_MS = 1500;
44
140
  const REGISTER_TIMEOUT_MS = 800;
45
141
  function authHeaders() {
@@ -47,9 +143,17 @@ function authHeaders() {
47
143
  if (SECRET) h["Authorization"] = `Bearer ${SECRET}`;
48
144
  return h;
49
145
  }
146
+ function isPlainTextHost() {
147
+ return Boolean(process.env["FACTORY_PROJECT_DIR"] || process.env["DROID_PLUGIN_ROOT"]);
148
+ }
149
+ function wantsStructuredOutput(data) {
150
+ if (process.env["DEVIN_PROJECT_DIR"] || data.prompt_id !== void 0) return true;
151
+ return data.hook_event_name === "SessionStart" && !isPlainTextHost();
152
+ }
50
153
  function contextPayload(data, context) {
154
+ if (process.env["COPILOT_PLUGIN_ROOT"] && !data.hook_event_name) return JSON.stringify({ additionalContext: context });
51
155
  if (typeof data.cursor_version === "string" || data.hook_event_name === "sessionStart") return JSON.stringify({ additional_context: context });
52
- if (process.env["DEVIN_PROJECT_DIR"] || data.prompt_id !== void 0) return JSON.stringify({ hookSpecificOutput: {
156
+ if (wantsStructuredOutput(data)) return JSON.stringify({ hookSpecificOutput: {
53
157
  hookEventName: "SessionStart",
54
158
  additionalContext: context
55
159
  } });