@agentmemory/agentmemory 0.9.30 → 0.9.32

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 (334) hide show
  1. package/.env.example +57 -48
  2. package/AGENTS.md +8 -8
  3. package/README.md +256 -819
  4. package/dist/.env.example +57 -48
  5. package/dist/cli.d.mts.map +1 -1
  6. package/dist/cli.mjs +2257 -1027
  7. package/dist/cli.mjs.map +1 -1
  8. package/dist/compression-recovery-CZh63Jmz.mjs +5997 -0
  9. package/dist/compression-recovery-CZh63Jmz.mjs.map +1 -0
  10. package/dist/{connect-B4_ca6BD.mjs → connect-BX3njQ3Y.mjs} +897 -276
  11. package/dist/connect-BX3njQ3Y.mjs.map +1 -0
  12. package/dist/hooks/_capture.d.mts +5 -1
  13. package/dist/hooks/_capture.d.mts.map +1 -1
  14. package/dist/hooks/_capture.mjs +141 -26
  15. package/dist/hooks/_capture.mjs.map +1 -1
  16. package/dist/hooks/notification.mjs +20 -6
  17. package/dist/hooks/notification.mjs.map +1 -1
  18. package/dist/hooks/post-commit.mjs +83 -10
  19. package/dist/hooks/post-commit.mjs.map +1 -1
  20. package/dist/hooks/post-tool-failure.mjs +19 -6
  21. package/dist/hooks/post-tool-failure.mjs.map +1 -1
  22. package/dist/hooks/post-tool-use.mjs +19 -6
  23. package/dist/hooks/post-tool-use.mjs.map +1 -1
  24. package/dist/hooks/pre-compact.mjs +104 -22
  25. package/dist/hooks/pre-compact.mjs.map +1 -1
  26. package/dist/hooks/pre-tool-use.mjs +83 -10
  27. package/dist/hooks/pre-tool-use.mjs.map +1 -1
  28. package/dist/hooks/prompt-submit.mjs +12 -4
  29. package/dist/hooks/prompt-submit.mjs.map +1 -1
  30. package/dist/hooks/session-end.mjs +59 -17
  31. package/dist/hooks/session-end.mjs.map +1 -1
  32. package/dist/hooks/session-start.mjs +95 -15
  33. package/dist/hooks/session-start.mjs.map +1 -1
  34. package/dist/hooks/stop.mjs +75 -72
  35. package/dist/hooks/stop.mjs.map +1 -1
  36. package/dist/hooks/subagent-start.mjs +13 -5
  37. package/dist/hooks/subagent-start.mjs.map +1 -1
  38. package/dist/hooks/subagent-stop.mjs +32 -10
  39. package/dist/hooks/subagent-stop.mjs.map +1 -1
  40. package/dist/hooks/task-completed.mjs +21 -6
  41. package/dist/hooks/task-completed.mjs.map +1 -1
  42. package/dist/{image-refs-T_urbwHM.mjs → image-refs-Cyta4Lf9.mjs} +2 -2
  43. package/dist/{image-refs-T_urbwHM.mjs.map → image-refs-Cyta4Lf9.mjs.map} +1 -1
  44. package/dist/index.mjs +16390 -11530
  45. package/dist/index.mjs.map +1 -1
  46. package/dist/integrations/dsh.d.mts +55 -0
  47. package/dist/integrations/dsh.d.mts.map +1 -0
  48. package/dist/integrations/dsh.mjs +1172 -0
  49. package/dist/integrations/dsh.mjs.map +1 -0
  50. package/dist/{keyed-mutex-DchXsyfP.mjs → keyed-mutex-DCUh7PG-.mjs} +58 -9
  51. package/dist/keyed-mutex-DCUh7PG-.mjs.map +1 -0
  52. package/dist/{secret-store-BnY3A50C.mjs → secret-store-iYXRcrO8.mjs} +717 -474
  53. package/dist/secret-store-iYXRcrO8.mjs.map +1 -0
  54. package/dist/session-arguments-zxsbbd8y.mjs +113 -0
  55. package/dist/session-arguments-zxsbbd8y.mjs.map +1 -0
  56. package/dist/{src-Jsq9LCeH.mjs → src-C23nMpda.mjs} +5263 -4667
  57. package/dist/src-C23nMpda.mjs.map +1 -0
  58. package/dist/{standalone-DKCdrf1d.mjs → standalone-BYo5USDb.mjs} +379 -63
  59. package/dist/standalone-BYo5USDb.mjs.map +1 -0
  60. package/dist/standalone.d.mts +1 -1
  61. package/dist/standalone.d.mts.map +1 -1
  62. package/dist/standalone.mjs +864 -81
  63. package/dist/standalone.mjs.map +1 -1
  64. package/dist/viewer/index.html +337 -168
  65. package/integrations/hermes/README.md +94 -0
  66. package/integrations/hermes/__init__.py +670 -0
  67. package/integrations/hermes/plugin.yaml +27 -0
  68. package/integrations/omp/LICENSE +190 -0
  69. package/integrations/omp/README.md +76 -0
  70. package/integrations/omp/capture.ts +1 -0
  71. package/integrations/omp/dist/index.mjs +1477 -0
  72. package/integrations/omp/index.ts +488 -0
  73. package/integrations/omp/package.json +13 -0
  74. package/integrations/omp/security.ts +67 -0
  75. package/integrations/omp/skills/agentmemory-agents/REFERENCE.md +32 -0
  76. package/integrations/omp/skills/agentmemory-agents/SKILL.md +48 -0
  77. package/integrations/omp/skills/agentmemory-agents/TROUBLESHOOTING.md +70 -0
  78. package/integrations/omp/skills/agentmemory-architecture/SKILL.md +37 -0
  79. package/integrations/omp/skills/agentmemory-architecture/TROUBLESHOOTING.md +70 -0
  80. package/integrations/omp/skills/agentmemory-config/REFERENCE.md +91 -0
  81. package/integrations/omp/skills/agentmemory-config/SKILL.md +98 -0
  82. package/integrations/omp/skills/agentmemory-config/TROUBLESHOOTING.md +70 -0
  83. package/integrations/omp/skills/agentmemory-hooks/REFERENCE.md +20 -0
  84. package/integrations/omp/skills/agentmemory-hooks/SKILL.md +60 -0
  85. package/integrations/omp/skills/agentmemory-hooks/TROUBLESHOOTING.md +70 -0
  86. package/integrations/omp/skills/agentmemory-mcp-tools/REFERENCE.md +66 -0
  87. package/integrations/omp/skills/agentmemory-mcp-tools/SKILL.md +52 -0
  88. package/integrations/omp/skills/agentmemory-mcp-tools/TROUBLESHOOTING.md +70 -0
  89. package/integrations/omp/skills/agentmemory-rest-api/REFERENCE.md +159 -0
  90. package/integrations/omp/skills/agentmemory-rest-api/SKILL.md +49 -0
  91. package/integrations/omp/skills/agentmemory-rest-api/TROUBLESHOOTING.md +70 -0
  92. package/integrations/omp/skills/commit-context/EXAMPLES.md +71 -0
  93. package/integrations/omp/skills/commit-context/SKILL.md +64 -0
  94. package/integrations/omp/skills/commit-context/TROUBLESHOOTING.md +70 -0
  95. package/integrations/omp/skills/commit-history/EXAMPLES.md +71 -0
  96. package/integrations/omp/skills/commit-history/SKILL.md +62 -0
  97. package/integrations/omp/skills/commit-history/TROUBLESHOOTING.md +70 -0
  98. package/integrations/omp/skills/forget/EXAMPLES.md +40 -0
  99. package/integrations/omp/skills/forget/SKILL.md +73 -0
  100. package/integrations/omp/skills/forget/TROUBLESHOOTING.md +70 -0
  101. package/integrations/omp/skills/handoff/EXAMPLES.md +57 -0
  102. package/integrations/omp/skills/handoff/SKILL.md +75 -0
  103. package/integrations/omp/skills/handoff/TROUBLESHOOTING.md +70 -0
  104. package/integrations/omp/skills/lesson/SKILL.md +64 -0
  105. package/integrations/omp/skills/lesson/TROUBLESHOOTING.md +70 -0
  106. package/integrations/omp/skills/memory-discipline/SKILL.md +65 -0
  107. package/integrations/omp/skills/memory-discipline/TROUBLESHOOTING.md +70 -0
  108. package/integrations/omp/skills/recall/EXAMPLES.md +74 -0
  109. package/integrations/omp/skills/recall/SKILL.md +74 -0
  110. package/integrations/omp/skills/recall/TROUBLESHOOTING.md +70 -0
  111. package/integrations/omp/skills/recap/EXAMPLES.md +62 -0
  112. package/integrations/omp/skills/recap/SKILL.md +68 -0
  113. package/integrations/omp/skills/recap/TROUBLESHOOTING.md +70 -0
  114. package/integrations/omp/skills/remember/EXAMPLES.md +67 -0
  115. package/integrations/omp/skills/remember/SKILL.md +66 -0
  116. package/integrations/omp/skills/remember/TROUBLESHOOTING.md +70 -0
  117. package/integrations/omp/skills/session-history/EXAMPLES.md +59 -0
  118. package/integrations/omp/skills/session-history/SKILL.md +67 -0
  119. package/integrations/omp/skills/session-history/TROUBLESHOOTING.md +70 -0
  120. package/integrations/omp/skills/write-agentmemory-skill/SKILL.md +41 -0
  121. package/integrations/omp/skills/write-agentmemory-skill/TROUBLESHOOTING.md +70 -0
  122. package/integrations/openclaw/LICENSE +190 -0
  123. package/integrations/openclaw/README.md +98 -0
  124. package/integrations/openclaw/openclaw.plugin.json +71 -0
  125. package/integrations/openclaw/package.json +38 -0
  126. package/integrations/openclaw/plugin.mjs +425 -0
  127. package/integrations/openclaw/plugin.yaml +26 -0
  128. package/integrations/openclaw/skills/agentmemory-agents/REFERENCE.md +32 -0
  129. package/integrations/openclaw/skills/agentmemory-agents/SKILL.md +48 -0
  130. package/integrations/openclaw/skills/agentmemory-agents/TROUBLESHOOTING.md +70 -0
  131. package/integrations/openclaw/skills/agentmemory-architecture/SKILL.md +37 -0
  132. package/integrations/openclaw/skills/agentmemory-architecture/TROUBLESHOOTING.md +70 -0
  133. package/integrations/openclaw/skills/agentmemory-config/REFERENCE.md +91 -0
  134. package/integrations/openclaw/skills/agentmemory-config/SKILL.md +98 -0
  135. package/integrations/openclaw/skills/agentmemory-config/TROUBLESHOOTING.md +70 -0
  136. package/integrations/openclaw/skills/agentmemory-hooks/REFERENCE.md +20 -0
  137. package/integrations/openclaw/skills/agentmemory-hooks/SKILL.md +60 -0
  138. package/integrations/openclaw/skills/agentmemory-hooks/TROUBLESHOOTING.md +70 -0
  139. package/integrations/openclaw/skills/agentmemory-mcp-tools/REFERENCE.md +66 -0
  140. package/integrations/openclaw/skills/agentmemory-mcp-tools/SKILL.md +52 -0
  141. package/integrations/openclaw/skills/agentmemory-mcp-tools/TROUBLESHOOTING.md +70 -0
  142. package/integrations/openclaw/skills/agentmemory-rest-api/REFERENCE.md +159 -0
  143. package/integrations/openclaw/skills/agentmemory-rest-api/SKILL.md +49 -0
  144. package/integrations/openclaw/skills/agentmemory-rest-api/TROUBLESHOOTING.md +70 -0
  145. package/integrations/openclaw/skills/commit-context/EXAMPLES.md +71 -0
  146. package/integrations/openclaw/skills/commit-context/SKILL.md +64 -0
  147. package/integrations/openclaw/skills/commit-context/TROUBLESHOOTING.md +70 -0
  148. package/integrations/openclaw/skills/commit-history/EXAMPLES.md +71 -0
  149. package/integrations/openclaw/skills/commit-history/SKILL.md +62 -0
  150. package/integrations/openclaw/skills/commit-history/TROUBLESHOOTING.md +70 -0
  151. package/integrations/openclaw/skills/forget/EXAMPLES.md +40 -0
  152. package/integrations/openclaw/skills/forget/SKILL.md +73 -0
  153. package/integrations/openclaw/skills/forget/TROUBLESHOOTING.md +70 -0
  154. package/integrations/openclaw/skills/handoff/EXAMPLES.md +57 -0
  155. package/integrations/openclaw/skills/handoff/SKILL.md +75 -0
  156. package/integrations/openclaw/skills/handoff/TROUBLESHOOTING.md +70 -0
  157. package/integrations/openclaw/skills/lesson/SKILL.md +64 -0
  158. package/integrations/openclaw/skills/lesson/TROUBLESHOOTING.md +70 -0
  159. package/integrations/openclaw/skills/memory-discipline/SKILL.md +65 -0
  160. package/integrations/openclaw/skills/memory-discipline/TROUBLESHOOTING.md +70 -0
  161. package/integrations/openclaw/skills/recall/EXAMPLES.md +74 -0
  162. package/integrations/openclaw/skills/recall/SKILL.md +74 -0
  163. package/integrations/openclaw/skills/recall/TROUBLESHOOTING.md +70 -0
  164. package/integrations/openclaw/skills/recap/EXAMPLES.md +62 -0
  165. package/integrations/openclaw/skills/recap/SKILL.md +68 -0
  166. package/integrations/openclaw/skills/recap/TROUBLESHOOTING.md +70 -0
  167. package/integrations/openclaw/skills/remember/EXAMPLES.md +67 -0
  168. package/integrations/openclaw/skills/remember/SKILL.md +66 -0
  169. package/integrations/openclaw/skills/remember/TROUBLESHOOTING.md +70 -0
  170. package/integrations/openclaw/skills/session-history/EXAMPLES.md +59 -0
  171. package/integrations/openclaw/skills/session-history/SKILL.md +67 -0
  172. package/integrations/openclaw/skills/session-history/TROUBLESHOOTING.md +70 -0
  173. package/integrations/openclaw/skills/write-agentmemory-skill/SKILL.md +41 -0
  174. package/integrations/openclaw/skills/write-agentmemory-skill/TROUBLESHOOTING.md +70 -0
  175. package/integrations/pi/LICENSE +190 -0
  176. package/integrations/pi/README.md +46 -51
  177. package/integrations/pi/dist/index.mjs +1460 -0
  178. package/integrations/pi/index.ts +192 -175
  179. package/integrations/pi/package.json +17 -7
  180. package/integrations/pi/security.ts +6 -26
  181. package/integrations/pi/skills/agentmemory-agents/REFERENCE.md +32 -0
  182. package/integrations/pi/skills/agentmemory-agents/SKILL.md +48 -0
  183. package/integrations/pi/skills/agentmemory-agents/TROUBLESHOOTING.md +70 -0
  184. package/integrations/pi/skills/agentmemory-architecture/SKILL.md +37 -0
  185. package/integrations/pi/skills/agentmemory-architecture/TROUBLESHOOTING.md +70 -0
  186. package/integrations/pi/skills/agentmemory-config/REFERENCE.md +91 -0
  187. package/integrations/pi/skills/agentmemory-config/SKILL.md +98 -0
  188. package/integrations/pi/skills/agentmemory-config/TROUBLESHOOTING.md +70 -0
  189. package/integrations/pi/skills/agentmemory-hooks/REFERENCE.md +20 -0
  190. package/integrations/pi/skills/agentmemory-hooks/SKILL.md +60 -0
  191. package/integrations/pi/skills/agentmemory-hooks/TROUBLESHOOTING.md +70 -0
  192. package/integrations/pi/skills/agentmemory-mcp-tools/REFERENCE.md +66 -0
  193. package/integrations/pi/skills/agentmemory-mcp-tools/SKILL.md +52 -0
  194. package/integrations/pi/skills/agentmemory-mcp-tools/TROUBLESHOOTING.md +70 -0
  195. package/integrations/pi/skills/agentmemory-rest-api/REFERENCE.md +159 -0
  196. package/integrations/pi/skills/agentmemory-rest-api/SKILL.md +49 -0
  197. package/integrations/pi/skills/agentmemory-rest-api/TROUBLESHOOTING.md +70 -0
  198. package/integrations/pi/skills/commit-context/EXAMPLES.md +71 -0
  199. package/integrations/pi/skills/commit-context/SKILL.md +64 -0
  200. package/integrations/pi/skills/commit-context/TROUBLESHOOTING.md +70 -0
  201. package/integrations/pi/skills/commit-history/EXAMPLES.md +71 -0
  202. package/integrations/pi/skills/commit-history/SKILL.md +62 -0
  203. package/integrations/pi/skills/commit-history/TROUBLESHOOTING.md +70 -0
  204. package/integrations/pi/skills/forget/EXAMPLES.md +40 -0
  205. package/integrations/pi/skills/forget/SKILL.md +73 -0
  206. package/integrations/pi/skills/forget/TROUBLESHOOTING.md +70 -0
  207. package/integrations/pi/skills/handoff/EXAMPLES.md +57 -0
  208. package/integrations/pi/skills/handoff/SKILL.md +75 -0
  209. package/integrations/pi/skills/handoff/TROUBLESHOOTING.md +70 -0
  210. package/integrations/pi/skills/lesson/SKILL.md +64 -0
  211. package/integrations/pi/skills/lesson/TROUBLESHOOTING.md +70 -0
  212. package/integrations/pi/skills/memory-discipline/SKILL.md +65 -0
  213. package/integrations/pi/skills/memory-discipline/TROUBLESHOOTING.md +70 -0
  214. package/integrations/pi/skills/recall/EXAMPLES.md +74 -0
  215. package/integrations/pi/skills/recall/SKILL.md +74 -0
  216. package/integrations/pi/skills/recall/TROUBLESHOOTING.md +70 -0
  217. package/integrations/pi/skills/recap/EXAMPLES.md +62 -0
  218. package/integrations/pi/skills/recap/SKILL.md +68 -0
  219. package/integrations/pi/skills/recap/TROUBLESHOOTING.md +70 -0
  220. package/integrations/pi/skills/remember/EXAMPLES.md +67 -0
  221. package/integrations/pi/skills/remember/SKILL.md +66 -0
  222. package/integrations/pi/skills/remember/TROUBLESHOOTING.md +70 -0
  223. package/integrations/pi/skills/session-history/EXAMPLES.md +59 -0
  224. package/integrations/pi/skills/session-history/SKILL.md +67 -0
  225. package/integrations/pi/skills/session-history/TROUBLESHOOTING.md +70 -0
  226. package/integrations/pi/skills/write-agentmemory-skill/SKILL.md +41 -0
  227. package/integrations/pi/skills/write-agentmemory-skill/TROUBLESHOOTING.md +70 -0
  228. package/package.json +33 -11
  229. package/plugin/.claude-plugin/plugin.json +16 -3
  230. package/plugin/.codex-plugin/plugin.json +4 -3
  231. package/plugin/.devin-plugin/plugin.json +6 -5
  232. package/plugin/.mcp.json +5 -1
  233. package/plugin/README.md +71 -0
  234. package/plugin/cursor/hooks.json +23 -8
  235. package/plugin/hooks/hooks.json +24 -12
  236. package/plugin/hooks.json +67 -0
  237. package/plugin/opencode/README.md +1 -1
  238. package/plugin/opencode/agentmemory-capture.ts +25 -11
  239. package/plugin/opencode/plugin.json +1 -1
  240. package/plugin/plugin.json +1 -1
  241. package/plugin/scripts/_capture.d.mts +5 -1
  242. package/plugin/scripts/_capture.d.mts.map +1 -1
  243. package/plugin/scripts/_capture.mjs +141 -26
  244. package/plugin/scripts/_capture.mjs.map +1 -1
  245. package/plugin/scripts/cursor/run-detached.mjs +786 -0
  246. package/plugin/scripts/cursor/run-detached.mjs.map +1 -0
  247. package/plugin/scripts/cursor/run-hook.d.mts +1 -0
  248. package/plugin/scripts/cursor/run-hook.mjs +704 -0
  249. package/plugin/scripts/cursor/run-hook.mjs.map +1 -0
  250. package/plugin/scripts/notification.mjs +20 -6
  251. package/plugin/scripts/notification.mjs.map +1 -1
  252. package/plugin/scripts/plugin-bridge.mjs +61 -11
  253. package/plugin/scripts/post-tool-failure.mjs +19 -6
  254. package/plugin/scripts/post-tool-failure.mjs.map +1 -1
  255. package/plugin/scripts/post-tool-use.mjs +19 -6
  256. package/plugin/scripts/post-tool-use.mjs.map +1 -1
  257. package/plugin/scripts/pre-compact.mjs +104 -22
  258. package/plugin/scripts/pre-compact.mjs.map +1 -1
  259. package/plugin/scripts/pre-tool-use.mjs +83 -10
  260. package/plugin/scripts/pre-tool-use.mjs.map +1 -1
  261. package/plugin/scripts/prompt-submit.mjs +12 -4
  262. package/plugin/scripts/prompt-submit.mjs.map +1 -1
  263. package/plugin/scripts/session-end.mjs +59 -17
  264. package/plugin/scripts/session-end.mjs.map +1 -1
  265. package/plugin/scripts/session-start.mjs +95 -15
  266. package/plugin/scripts/session-start.mjs.map +1 -1
  267. package/plugin/scripts/stop.mjs +75 -72
  268. package/plugin/scripts/stop.mjs.map +1 -1
  269. package/plugin/scripts/subagent-start.mjs +13 -5
  270. package/plugin/scripts/subagent-start.mjs.map +1 -1
  271. package/plugin/scripts/subagent-stop.mjs +32 -10
  272. package/plugin/scripts/subagent-stop.mjs.map +1 -1
  273. package/plugin/scripts/task-completed.mjs +21 -6
  274. package/plugin/scripts/task-completed.mjs.map +1 -1
  275. package/plugin/skills/_shared/TROUBLESHOOTING.md +21 -7
  276. package/plugin/skills/agentmemory-agents/REFERENCE.md +8 -7
  277. package/plugin/skills/agentmemory-agents/SKILL.md +19 -5
  278. package/plugin/skills/agentmemory-agents/TROUBLESHOOTING.md +70 -0
  279. package/plugin/skills/agentmemory-architecture/SKILL.md +8 -4
  280. package/plugin/skills/agentmemory-architecture/TROUBLESHOOTING.md +70 -0
  281. package/plugin/skills/agentmemory-config/REFERENCE.md +13 -15
  282. package/plugin/skills/agentmemory-config/SKILL.md +34 -10
  283. package/plugin/skills/agentmemory-config/TROUBLESHOOTING.md +70 -0
  284. package/plugin/skills/agentmemory-hooks/SKILL.md +31 -10
  285. package/plugin/skills/agentmemory-hooks/TROUBLESHOOTING.md +70 -0
  286. package/plugin/skills/agentmemory-mcp-tools/REFERENCE.md +5 -5
  287. package/plugin/skills/agentmemory-mcp-tools/SKILL.md +16 -3
  288. package/plugin/skills/agentmemory-mcp-tools/TROUBLESHOOTING.md +70 -0
  289. package/plugin/skills/agentmemory-rest-api/REFERENCE.md +10 -1
  290. package/plugin/skills/agentmemory-rest-api/SKILL.md +12 -6
  291. package/plugin/skills/agentmemory-rest-api/TROUBLESHOOTING.md +70 -0
  292. package/plugin/skills/commit-context/SKILL.md +1 -1
  293. package/plugin/skills/commit-context/TROUBLESHOOTING.md +70 -0
  294. package/plugin/skills/commit-history/SKILL.md +1 -1
  295. package/plugin/skills/commit-history/TROUBLESHOOTING.md +70 -0
  296. package/plugin/skills/forget/EXAMPLES.md +19 -54
  297. package/plugin/skills/forget/SKILL.md +16 -12
  298. package/plugin/skills/forget/TROUBLESHOOTING.md +70 -0
  299. package/plugin/skills/handoff/EXAMPLES.md +5 -4
  300. package/plugin/skills/handoff/SKILL.md +10 -5
  301. package/plugin/skills/handoff/TROUBLESHOOTING.md +70 -0
  302. package/plugin/skills/lesson/SKILL.md +1 -1
  303. package/plugin/skills/lesson/TROUBLESHOOTING.md +70 -0
  304. package/plugin/skills/memory-discipline/SKILL.md +1 -1
  305. package/plugin/skills/memory-discipline/TROUBLESHOOTING.md +70 -0
  306. package/plugin/skills/recall/SKILL.md +18 -7
  307. package/plugin/skills/recall/TROUBLESHOOTING.md +70 -0
  308. package/plugin/skills/recap/EXAMPLES.md +5 -3
  309. package/plugin/skills/recap/SKILL.md +11 -6
  310. package/plugin/skills/recap/TROUBLESHOOTING.md +70 -0
  311. package/plugin/skills/remember/EXAMPLES.md +3 -3
  312. package/plugin/skills/remember/SKILL.md +1 -1
  313. package/plugin/skills/remember/TROUBLESHOOTING.md +70 -0
  314. package/plugin/skills/session-history/EXAMPLES.md +2 -2
  315. package/plugin/skills/session-history/SKILL.md +10 -4
  316. package/plugin/skills/session-history/TROUBLESHOOTING.md +70 -0
  317. package/plugin/skills/write-agentmemory-skill/SKILL.md +2 -1
  318. package/plugin/skills/write-agentmemory-skill/TROUBLESHOOTING.md +70 -0
  319. package/dist/connect-B4_ca6BD.mjs.map +0 -1
  320. package/dist/docker-compose.yml +0 -73
  321. package/dist/iii-config.docker.yaml +0 -53
  322. package/dist/iii-config.yaml +0 -63
  323. package/dist/keyed-mutex-DchXsyfP.mjs.map +0 -1
  324. package/dist/secret-store-BnY3A50C.mjs.map +0 -1
  325. package/dist/src-Jsq9LCeH.mjs.map +0 -1
  326. package/dist/standalone-DKCdrf1d.mjs.map +0 -1
  327. package/dist/status-IRz7ta8-.mjs +0 -2344
  328. package/dist/status-IRz7ta8-.mjs.map +0 -1
  329. package/docker-compose.yml +0 -73
  330. package/iii-config.docker.yaml +0 -53
  331. package/iii-config.yaml +0 -63
  332. package/plugin/scripts/post-commit.mjs +0 -184
  333. package/plugin/scripts/post-commit.mjs.map +0 -1
  334. /package/plugin/scripts/{post-commit.d.mts → cursor/run-detached.d.mts} +0 -0
@@ -1,15 +1,22 @@
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";
5
- import { REST_URL, authHeaders, captureObservation, isDrainChild, runDrainChild, withEventId } from "./_capture.mjs";
4
+ import { basename, dirname, resolve } from "node:path";
5
+ import { REST_URL, authHeaders, captureObservation, isDrainChild, runDrainChild, transportBlocked, withEventId } from "./_capture.mjs";
6
+ //#region src/hooks/_capture-filter.ts
7
+ function truncateText(value, max) {
8
+ if (value.length <= max) return value;
9
+ const end = /[\uD800-\uDBFF]/.test(value[max - 1] ?? "") ? max - 1 : max;
10
+ return value.slice(0, end);
11
+ }
12
+ //#endregion
6
13
  //#region src/hooks/_project.ts
7
14
  function resolveProject(cwd) {
8
15
  const explicit = process.env["AGENTMEMORY_PROJECT_NAME"];
9
16
  if (explicit && explicit.trim()) return explicit.trim();
10
17
  const dir = cwd && cwd.trim() ? cwd : process.cwd();
11
18
  try {
12
- const top = execSync("git rev-parse --show-toplevel", {
19
+ const [top, gitDir, commonDir] = execSync("git rev-parse --show-toplevel --git-dir --git-common-dir", {
13
20
  cwd: dir,
14
21
  stdio: [
15
22
  "ignore",
@@ -17,11 +24,18 @@ function resolveProject(cwd) {
17
24
  "ignore"
18
25
  ],
19
26
  timeout: 500
20
- }).toString().trim();
21
- if (top) return basename(top);
27
+ }).toString().trim().split(/\r?\n/);
28
+ if (top) return basename(repositoryRoot(dir, top, gitDir, commonDir));
22
29
  } catch {}
23
30
  return basename(dir);
24
31
  }
32
+ function repositoryRoot(dir, top, gitDir, commonDir) {
33
+ if (!gitDir || !commonDir) return top;
34
+ const physical = realpathSync(dir);
35
+ const common = resolve(physical, commonDir);
36
+ if (resolve(physical, gitDir) === common || basename(common) !== ".git") return top;
37
+ return dirname(common);
38
+ }
25
39
  function hookCwd(data) {
26
40
  if (!data || typeof data !== "object") return void 0;
27
41
  if (typeof data.cwd === "string" && data.cwd.trim()) return data.cwd;
@@ -39,6 +53,23 @@ function isSdkChildContext(payload) {
39
53
  if (!payload || typeof payload !== "object") return false;
40
54
  return payload.entrypoint === "sdk-ts";
41
55
  }
56
+ 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)/;
57
+ function isUserTurn(msg) {
58
+ if (msg.isSidechain || msg.isMeta || msg.isCompactSummary) return false;
59
+ return msg.role === "user" || msg.type === "user" || msg.message?.role === "user";
60
+ }
61
+ function promptText(raw) {
62
+ const m = raw.match(/<user_query>\n?([\s\S]*?)\n?<\/user_query>/);
63
+ const text = (m ? m[1] : raw).trim();
64
+ return HARNESS_TEXT.test(text) ? "" : text;
65
+ }
66
+ function turnTexts(content) {
67
+ if (typeof content === "string") return [content];
68
+ if (!Array.isArray(content)) return [];
69
+ const texts = [];
70
+ for (const block of content) if (block?.type === "text" && typeof block.text === "string") texts.push(block.text);
71
+ return texts;
72
+ }
42
73
  function extractTranscriptPrompts(data) {
43
74
  const path = data.transcript_path;
44
75
  if (typeof path !== "string" || !path.endsWith(".jsonl")) return [];
@@ -61,16 +92,20 @@ function extractTranscriptPrompts(data) {
61
92
  if (prompts.length >= 50) return prompts;
62
93
  const match = msg.content.match(/<USER_REQUEST>\n?([\s\S]*?)\n?<\/USER_REQUEST>/);
63
94
  const text = (match ? match[1] : msg.content).trim();
64
- if (text) prompts.push(text.slice(0, 8e3));
95
+ if (text) prompts.push({ prompt: truncateText(text, 8e3) });
65
96
  continue;
66
97
  }
67
- if (msg.role !== "user") continue;
68
- for (const block of msg.message?.content ?? []) {
98
+ if (!isUserTurn(msg)) continue;
99
+ const texts = turnTexts(msg.message?.content).map(promptText).filter(Boolean);
100
+ const promptId = typeof msg.promptId === "string" ? msg.promptId : void 0;
101
+ const timestamp = typeof msg.timestamp === "string" ? msg.timestamp : void 0;
102
+ for (const text of promptId ? [texts.join("\n\n")].filter(Boolean) : texts) {
69
103
  if (prompts.length >= 50) return prompts;
70
- if (block.type !== "text" || typeof block.text !== "string") continue;
71
- const m = block.text.match(/<user_query>\n?([\s\S]*?)\n?<\/user_query>/);
72
- const text = (m ? m[1] : block.text).trim();
73
- if (text) prompts.push(text.slice(0, 8e3));
104
+ prompts.push({
105
+ prompt: truncateText(text, 8e3),
106
+ promptId,
107
+ timestamp
108
+ });
74
109
  }
75
110
  }
76
111
  return prompts;
@@ -93,27 +128,34 @@ async function main() {
93
128
  const cwd = hookCwd(data) || process.cwd();
94
129
  const project = resolveProject(cwd);
95
130
  const timestamp = (/* @__PURE__ */ new Date()).toISOString();
96
- await Promise.allSettled(transcriptPrompts.map((prompt, index) => captureObservation(withEventId({
131
+ await Promise.allSettled(transcriptPrompts.map(({ prompt, promptId, timestamp: at }, index) => captureObservation(withEventId({
97
132
  hookType: "prompt_submit",
98
133
  sessionId,
99
134
  project,
100
135
  cwd,
101
- timestamp,
136
+ timestamp: at ?? timestamp,
102
137
  data: {
103
138
  prompt,
104
139
  backfill: true
105
140
  }
106
- }, {}, {
141
+ }, promptId ? { prompt_id: promptId } : {}, {
107
142
  source: "transcript",
108
143
  transcript: data.transcript_path,
109
144
  index,
110
145
  prompt
111
146
  }, { stable: true }), 3e3)));
112
147
  }
148
+ if (transportBlocked()) {
149
+ setTimeout(() => process.exit(0), 1500).unref();
150
+ return;
151
+ }
113
152
  fetch(`${REST_URL}/agentmemory/session/end`, {
114
153
  method: "POST",
115
154
  headers: authHeaders(),
116
- body: JSON.stringify({ sessionId }),
155
+ body: JSON.stringify({
156
+ sessionId,
157
+ final: true
158
+ }),
117
159
  signal: AbortSignal.timeout(3e4)
118
160
  }).catch(() => {});
119
161
  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\";\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\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 type?: string;\n source?: string;\n content?: unknown;\n message?: { content?: Array<{ type?: string; text?: string }> };\n };\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(text.slice(0, 8000));\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 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, index) =>\n captureObservation(\n withEventId(\n { hookType: \"prompt_submit\", sessionId, project, cwd, timestamp, data: { prompt, backfill: true } },\n {},\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 }),\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
+ {"version":3,"file":"session-end.mjs","names":[],"sources":["../../src/hooks/_capture-filter.ts","../../src/hooks/_project.ts","../../src/hooks/session-end.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 truncateText(value: string, max: number): string {\n if (value.length <= max) return value;\n const end = /[\\uD800-\\uDBFF]/.test(value[max - 1] ?? \"\") ? max - 1 : max;\n return value.slice(0, end);\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 : truncateText(value, 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 : truncateText(str, 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 { readFileSync } from \"node:fs\";\nimport { truncateText } from \"./_capture-filter.js\";\nimport { resolveProject, hookCwd } from \"./_project.js\";\nimport { REST_URL, authHeaders, captureObservation, isDrainChild, runDrainChild, transportBlocked, 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: truncateText(text, 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: truncateText(text, 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 if (transportBlocked()) {\n setTimeout(() => process.exit(0), 1500).unref();\n return;\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,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(agentmemoryHomeDir(), ".env"), "utf-8");
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 readStoredSecret() {
49
+ function readSecretFile(path) {
50
50
  try {
51
- return usable(readFileSync(secretFilePath(), "utf-8"));
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,66 @@ 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");
91
+ }
92
+ //#endregion
93
+ //#region src/hooks/_endpoint.ts
94
+ const OPTION_PREFIX = "CLAUDE_PLUGIN_OPTION_";
95
+ const DEFAULT_URL = "http://localhost:3111";
96
+ function filled(value) {
97
+ const trimmed = value?.trim();
98
+ if (!trimmed || trimmed.startsWith("${") && trimmed.endsWith("}")) return void 0;
99
+ return value;
100
+ }
101
+ function hookSetting(name, env = process.env) {
102
+ const optionKey = `${OPTION_PREFIX}${name}`;
103
+ const option = filled(env[optionKey])?.trim();
104
+ if (option) return option;
105
+ if (name === "AGENTMEMORY_SECRET" && optionKey in env) return void 0;
106
+ return filled(env[name]);
107
+ }
108
+ function resolveSettingSecret(url, env = process.env) {
109
+ const secretEnv = { ...env };
110
+ const explicit = hookSetting("AGENTMEMORY_SECRET", env);
111
+ if (explicit === void 0) delete secretEnv["AGENTMEMORY_SECRET"];
112
+ else secretEnv["AGENTMEMORY_SECRET"] = explicit;
113
+ return resolveClientSecret(url, secretEnv);
114
+ }
115
+ function secretTransportAllowed(url) {
116
+ try {
117
+ return new URL(url).protocol === "https:" || isLoopbackUrl(url);
118
+ } catch {
119
+ return false;
120
+ }
121
+ }
122
+ function resolveHookEndpoint(env = process.env) {
123
+ const url = hookSetting("AGENTMEMORY_URL", env) || DEFAULT_URL;
124
+ const secret = resolveSettingSecret(url, env);
125
+ const blocked = !secretTransportAllowed(url);
126
+ return {
127
+ url,
128
+ secret: blocked ? "" : secret,
129
+ blocked
130
+ };
131
+ }
132
+ let blockedNoticeShown = false;
133
+ function reportBlockedTransport(endpoint) {
134
+ if (!endpoint.blocked) return false;
135
+ if (!blockedNoticeShown) {
136
+ blockedNoticeShown = true;
137
+ process.stderr.write("[agentmemory] AGENTMEMORY_URL must use HTTPS unless it is a loopback address. Nothing was sent.\n");
138
+ }
139
+ return true;
140
+ }
141
+ function hookAuthHeaders(endpoint) {
142
+ const h = { "Content-Type": "application/json" };
143
+ if (endpoint.secret) h["Authorization"] = `Bearer ${endpoint.secret}`;
144
+ return h;
71
145
  }
72
146
  //#endregion
73
147
  //#region src/hooks/_project.ts
@@ -76,7 +150,7 @@ function resolveProject(cwd) {
76
150
  if (explicit && explicit.trim()) return explicit.trim();
77
151
  const dir = cwd && cwd.trim() ? cwd : process.cwd();
78
152
  try {
79
- const top = execSync("git rev-parse --show-toplevel", {
153
+ const [top, gitDir, commonDir] = execSync("git rev-parse --show-toplevel --git-dir --git-common-dir", {
80
154
  cwd: dir,
81
155
  stdio: [
82
156
  "ignore",
@@ -84,11 +158,18 @@ function resolveProject(cwd) {
84
158
  "ignore"
85
159
  ],
86
160
  timeout: 500
87
- }).toString().trim();
88
- if (top) return basename(top);
161
+ }).toString().trim().split(/\r?\n/);
162
+ if (top) return basename(repositoryRoot(dir, top, gitDir, commonDir));
89
163
  } catch {}
90
164
  return basename(dir);
91
165
  }
166
+ function repositoryRoot(dir, top, gitDir, commonDir) {
167
+ if (!gitDir || !commonDir) return top;
168
+ const physical = realpathSync(dir);
169
+ const common = resolve(physical, commonDir);
170
+ if (resolve(physical, gitDir) === common || basename(common) !== ".git") return top;
171
+ return dirname(common);
172
+ }
92
173
  function hookCwd(data) {
93
174
  if (!data || typeof data !== "object") return void 0;
94
175
  if (typeof data.cwd === "string" && data.cwd.trim()) return data.cwd;
@@ -107,14 +188,12 @@ function isSdkChildContext(payload) {
107
188
  return payload.entrypoint === "sdk-ts";
108
189
  }
109
190
  const INJECT_CONTEXT = process.env["AGENTMEMORY_INJECT_CONTEXT"] === "true";
110
- const REST_URL = process.env["AGENTMEMORY_URL"] || "http://localhost:3111";
111
- const SECRET = resolveClientSecret(REST_URL);
191
+ const ENDPOINT = resolveHookEndpoint();
192
+ const REST_URL = ENDPOINT.url;
112
193
  const INJECT_TIMEOUT_MS = 1500;
113
194
  const REGISTER_TIMEOUT_MS = 800;
114
195
  function authHeaders() {
115
- const h = { "Content-Type": "application/json" };
116
- if (SECRET) h["Authorization"] = `Bearer ${SECRET}`;
117
- return h;
196
+ return hookAuthHeaders(ENDPOINT);
118
197
  }
119
198
  function isPlainTextHost() {
120
199
  return Boolean(process.env["FACTORY_PROJECT_DIR"] || process.env["DROID_PLUGIN_ROOT"]);
@@ -143,6 +222,7 @@ async function main() {
143
222
  }
144
223
  if (!data || typeof data !== "object") return;
145
224
  if (isSdkChildContext(data)) return;
225
+ if (reportBlockedTransport(ENDPOINT)) return;
146
226
  const sessionId = data.session_id || data.sessionId || data.conversation_id || `ses_${Date.now().toString(36)}`;
147
227
  const cwd = hookCwd(data) || process.cwd();
148
228
  const project = resolveProject(cwd);
@@ -1 +1 @@
1
- {"version":3,"file":"session-start.mjs","names":[],"sources":["../../src/secret-store.ts","../../src/hooks/_project.ts","../../src/hooks/session-start.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\";\nimport { resolveProject, hookCwd } from \"./_project.js\";\n\n// Inlined from ./sdk-guard so each hook bundles to a single self-contained\n// .mjs (matches the pattern used by every other hook entry in tsdown.config).\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\nconst INJECT_TIMEOUT_MS = 1500;\nconst REGISTER_TIMEOUT_MS = 800;\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 isPlainTextHost(): boolean {\n return Boolean(\n process.env[\"FACTORY_PROJECT_DIR\"] || process.env[\"DROID_PLUGIN_ROOT\"],\n );\n}\n\nfunction wantsStructuredOutput(data: Record<string, unknown>): boolean {\n if (process.env[\"DEVIN_PROJECT_DIR\"] || data.prompt_id !== undefined) {\n return true;\n }\n return data.hook_event_name === \"SessionStart\" && !isPlainTextHost();\n}\n\nfunction contextPayload(data: Record<string, unknown>, context: string): string {\n if (process.env[\"COPILOT_PLUGIN_ROOT\"] && !data.hook_event_name) {\n return JSON.stringify({ additionalContext: context });\n }\n if (\n typeof data.cursor_version === \"string\" ||\n data.hook_event_name === \"sessionStart\"\n ) {\n return JSON.stringify({ additional_context: context });\n }\n if (wantsStructuredOutput(data)) {\n return JSON.stringify({\n hookSpecificOutput: {\n hookEventName: \"SessionStart\",\n additionalContext: context,\n },\n });\n }\n return context;\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 =\n ((data.session_id || data.sessionId || data.conversation_id) as string) ||\n `ses_${Date.now().toString(36)}`;\n const cwd = hookCwd(data) || process.cwd();\n const project = resolveProject(cwd);\n\n const url = `${REST_URL}/agentmemory/session/start`;\n const init: RequestInit = {\n method: \"POST\",\n headers: authHeaders(),\n body: JSON.stringify({ sessionId, project, cwd }),\n };\n\n if (!INJECT_CONTEXT) {\n // Pure telemetry path: caller never reads the response, so don't\n // block on it. AbortSignal.timeout caps the wait the event loop\n // gives the pending socket before exit.\n fetch(url, {\n ...init,\n signal: AbortSignal.timeout(REGISTER_TIMEOUT_MS),\n }).catch(() => {});\n return;\n }\n\n try {\n const res = await fetch(url, {\n ...init,\n signal: AbortSignal.timeout(INJECT_TIMEOUT_MS),\n });\n if (res.ok) {\n const result = (await res.json()) as { context?: string };\n if (result.context) {\n process.stdout.write(contextPayload(data, result.context));\n }\n }\n } catch {\n // silently fail -- don't block Claude Code startup\n }\n}\n\nmain().catch(() => process.exit(0));\n"],"mappings":"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"}
1
+ {"version":3,"file":"session-start.mjs","names":[],"sources":["../../src/secret-store.ts","../../src/hooks/_endpoint.ts","../../src/hooks/_project.ts","../../src/hooks/session-start.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 { isLoopbackUrl, resolveClientSecret } from \"../secret-store.js\";\n\nconst OPTION_PREFIX = \"CLAUDE_PLUGIN_OPTION_\";\nconst DEFAULT_URL = \"http://localhost:3111\";\n\nexport interface HookEndpoint {\n url: string;\n secret: string;\n blocked: boolean;\n}\n\nfunction filled(value: string | undefined): string | undefined {\n const trimmed = value?.trim();\n if (!trimmed || (trimmed.startsWith(\"${\") && trimmed.endsWith(\"}\"))) return undefined;\n return value;\n}\n\nexport function hookSetting(\n name: string,\n env: NodeJS.ProcessEnv = process.env,\n): string | undefined {\n const optionKey = `${OPTION_PREFIX}${name}`;\n const option = filled(env[optionKey])?.trim();\n if (option) return option;\n if (name === \"AGENTMEMORY_SECRET\" && optionKey in env) return undefined;\n return filled(env[name]);\n}\n\nexport function resolveSettingSecret(url: string, env: NodeJS.ProcessEnv = process.env): string {\n const secretEnv: NodeJS.ProcessEnv = { ...env };\n const explicit = hookSetting(\"AGENTMEMORY_SECRET\", env);\n if (explicit === undefined) delete secretEnv[\"AGENTMEMORY_SECRET\"];\n else secretEnv[\"AGENTMEMORY_SECRET\"] = explicit;\n return resolveClientSecret(url, secretEnv);\n}\n\nexport function secretTransportAllowed(url: string): boolean {\n try {\n return new URL(url).protocol === \"https:\" || isLoopbackUrl(url);\n } catch {\n return false;\n }\n}\n\nexport function resolveHookEndpoint(env: NodeJS.ProcessEnv = process.env): HookEndpoint {\n const url = hookSetting(\"AGENTMEMORY_URL\", env) || DEFAULT_URL;\n const secret = resolveSettingSecret(url, env);\n const blocked = !secretTransportAllowed(url);\n return { url, secret: blocked ? \"\" : secret, blocked };\n}\n\nlet blockedNoticeShown = false;\n\nexport function reportBlockedTransport(endpoint: HookEndpoint): boolean {\n if (!endpoint.blocked) return false;\n if (!blockedNoticeShown) {\n blockedNoticeShown = true;\n process.stderr.write(\n \"[agentmemory] AGENTMEMORY_URL must use HTTPS unless it is a loopback address. Nothing was sent.\\n\",\n );\n }\n return true;\n}\n\nexport function hookAuthHeaders(endpoint: HookEndpoint): Record<string, string> {\n const h: Record<string, string> = { \"Content-Type\": \"application/json\" };\n if (endpoint.secret) h[\"Authorization\"] = `Bearer ${endpoint.secret}`;\n return h;\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 { hookAuthHeaders, reportBlockedTransport, resolveHookEndpoint } from \"./_endpoint.js\";\nimport { resolveProject, hookCwd } from \"./_project.js\";\n\n// Inlined from ./sdk-guard so each hook bundles to a single self-contained\n// .mjs (matches the pattern used by every other hook entry in tsdown.config).\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 ENDPOINT = resolveHookEndpoint();\nconst REST_URL = ENDPOINT.url;\n\nconst INJECT_TIMEOUT_MS = 1500;\nconst REGISTER_TIMEOUT_MS = 800;\n\nfunction authHeaders(): Record<string, string> {\n return hookAuthHeaders(ENDPOINT);\n}\n\nfunction isPlainTextHost(): boolean {\n return Boolean(\n process.env[\"FACTORY_PROJECT_DIR\"] || process.env[\"DROID_PLUGIN_ROOT\"],\n );\n}\n\nfunction wantsStructuredOutput(data: Record<string, unknown>): boolean {\n if (process.env[\"DEVIN_PROJECT_DIR\"] || data.prompt_id !== undefined) {\n return true;\n }\n return data.hook_event_name === \"SessionStart\" && !isPlainTextHost();\n}\n\nfunction contextPayload(data: Record<string, unknown>, context: string): string {\n if (process.env[\"COPILOT_PLUGIN_ROOT\"] && !data.hook_event_name) {\n return JSON.stringify({ additionalContext: context });\n }\n if (\n typeof data.cursor_version === \"string\" ||\n data.hook_event_name === \"sessionStart\"\n ) {\n return JSON.stringify({ additional_context: context });\n }\n if (wantsStructuredOutput(data)) {\n return JSON.stringify({\n hookSpecificOutput: {\n hookEventName: \"SessionStart\",\n additionalContext: context,\n },\n });\n }\n return context;\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 if (reportBlockedTransport(ENDPOINT)) return;\n\n const sessionId =\n ((data.session_id || data.sessionId || data.conversation_id) as string) ||\n `ses_${Date.now().toString(36)}`;\n const cwd = hookCwd(data) || process.cwd();\n const project = resolveProject(cwd);\n\n const url = `${REST_URL}/agentmemory/session/start`;\n const init: RequestInit = {\n method: \"POST\",\n headers: authHeaders(),\n body: JSON.stringify({ sessionId, project, cwd }),\n };\n\n if (!INJECT_CONTEXT) {\n // Pure telemetry path: caller never reads the response, so don't\n // block on it. AbortSignal.timeout caps the wait the event loop\n // gives the pending socket before exit.\n fetch(url, {\n ...init,\n signal: AbortSignal.timeout(REGISTER_TIMEOUT_MS),\n }).catch(() => {});\n return;\n }\n\n try {\n const res = await fetch(url, {\n ...init,\n signal: AbortSignal.timeout(INJECT_TIMEOUT_MS),\n });\n if (res.ok) {\n const result = (await res.json()) as { context?: string };\n if (result.context) {\n process.stdout.write(contextPayload(data, result.context));\n }\n }\n } catch {\n // silently fail -- don't block Claude Code startup\n }\n}\n\nmain().catch(() => process.exit(0));\n"],"mappings":"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"}
@@ -1,72 +1,43 @@
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 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;
2
+ import { execSync } from "node:child_process";
3
+ import { realpathSync } from "node:fs";
4
+ import { basename, dirname, resolve } from "node:path";
5
+ import { REST_URL, authHeaders, captureObservation, isDrainChild, runDrainChild, stripPrivateData, transportBlocked, withEventId } from "./_capture.mjs";
6
+ //#region src/hooks/_project.ts
7
+ function resolveProject(cwd) {
8
+ const explicit = process.env["AGENTMEMORY_PROJECT_NAME"];
9
+ if (explicit && explicit.trim()) return explicit.trim();
10
+ const dir = cwd && cwd.trim() ? cwd : process.cwd();
31
11
  try {
32
- content = readFileSync(join(agentmemoryHomeDir(), ".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;
12
+ const [top, gitDir, commonDir] = execSync("git rev-parse --show-toplevel --git-dir --git-common-dir", {
13
+ cwd: dir,
14
+ stdio: [
15
+ "ignore",
16
+ "pipe",
17
+ "ignore"
18
+ ],
19
+ timeout: 500
20
+ }).toString().trim().split(/\r?\n/);
21
+ if (top) return basename(repositoryRoot(dir, top, gitDir, commonDir));
22
+ } catch {}
23
+ return basename(dir);
47
24
  }
48
- function readStoredSecret() {
49
- try {
50
- return usable(readFileSync(secretFilePath(), "utf-8"));
51
- } catch {
52
- return "";
53
- }
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);
54
31
  }
55
- function isLoopbackUrl(url) {
56
- let hostname;
57
- try {
58
- hostname = new URL(url).hostname.toLowerCase();
59
- } catch {
60
- return false;
32
+ function hookCwd(data) {
33
+ if (!data || typeof data !== "object") return void 0;
34
+ if (typeof data.cwd === "string" && data.cwd.trim()) return data.cwd;
35
+ const roots = data.workspace_roots;
36
+ if (Array.isArray(roots)) {
37
+ for (const root of roots) if (typeof root === "string" && root.trim()) return root;
61
38
  }
62
- const bare = hostname.replace(/^\[|\]$/g, "");
63
- return bare === "localhost" || bare === "::1" || /^127(?:\.\d{1,3}){3}$/.test(bare);
64
- }
65
- function resolveClientSecret(baseUrl, env = process.env) {
66
- const fromEnv = usable(env[SECRET_KEY]);
67
- if (fromEnv) return fromEnv;
68
- if (!isLoopbackUrl(baseUrl)) return "";
69
- return readEnvFileSecret() || readStoredSecret();
39
+ const projectDir = process.env["DEVIN_PROJECT_DIR"] || process.env["CLAUDE_PROJECT_DIR"];
40
+ if (projectDir && projectDir.trim()) return projectDir;
70
41
  }
71
42
  //#endregion
72
43
  //#region src/hooks/stop.ts
@@ -75,14 +46,24 @@ function isSdkChildContext(payload) {
75
46
  if (!payload || typeof payload !== "object") return false;
76
47
  return payload.entrypoint === "sdk-ts";
77
48
  }
78
- const REST_URL = process.env["AGENTMEMORY_URL"] || "http://localhost:3111";
79
- const SECRET = resolveClientSecret(REST_URL);
80
- function authHeaders() {
81
- const h = { "Content-Type": "application/json" };
82
- if (SECRET) h["Authorization"] = `Bearer ${SECRET}`;
83
- return h;
49
+ const OBSERVE_TIMEOUT_MS = 2e3;
50
+ const EXIT_CAP_MS = 3500;
51
+ const END_CAP_MS = 1500;
52
+ const REPLY_MAX_CHARS = 4e3;
53
+ function boundReply(reply) {
54
+ const safe = stripPrivateData(reply);
55
+ if (safe.length <= REPLY_MAX_CHARS) return { text: safe };
56
+ let head = Math.ceil((REPLY_MAX_CHARS - 1) / 2);
57
+ let tail = safe.length - (REPLY_MAX_CHARS - 1 - head);
58
+ if (/[\uD800-\uDBFF]/.test(safe[head - 1])) head--;
59
+ if (/[\uDC00-\uDFFF]/.test(safe[tail])) tail++;
60
+ return {
61
+ text: `${safe.slice(0, head)}\u2026${safe.slice(tail)}`,
62
+ bytes: Buffer.byteLength(safe, "utf8")
63
+ };
84
64
  }
85
65
  async function main() {
66
+ if (isDrainChild()) return runDrainChild();
86
67
  let input = "";
87
68
  for await (const chunk of process.stdin) input += chunk;
88
69
  let data;
@@ -94,13 +75,35 @@ async function main() {
94
75
  if (!data || typeof data !== "object") return;
95
76
  if (isSdkChildContext(data)) return;
96
77
  const sessionId = data.session_id || data.sessionId || data.conversation_id || "unknown";
78
+ const reply = data.last_assistant_message;
79
+ if (typeof reply === "string" && reply.trim()) {
80
+ const capture = setTimeout(() => process.exit(0), EXIT_CAP_MS).unref();
81
+ const cwd = hookCwd(data) || process.cwd();
82
+ const bounded = boundReply(reply);
83
+ await captureObservation(withEventId({
84
+ hookType: "stop",
85
+ sessionId,
86
+ project: resolveProject(cwd),
87
+ cwd,
88
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
89
+ data: {
90
+ last_assistant_message: bounded.text,
91
+ ...bounded.bytes === void 0 ? {} : { last_assistant_message_bytes: bounded.bytes }
92
+ }
93
+ }, {}), OBSERVE_TIMEOUT_MS).catch(() => {});
94
+ clearTimeout(capture);
95
+ }
96
+ setTimeout(() => process.exit(0), END_CAP_MS).unref();
97
+ if (transportBlocked()) return;
97
98
  fetch(`${REST_URL}/agentmemory/session/end`, {
98
99
  method: "POST",
99
100
  headers: authHeaders(),
100
- body: JSON.stringify({ sessionId }),
101
+ body: JSON.stringify({
102
+ sessionId,
103
+ final: false
104
+ }),
101
105
  signal: AbortSignal.timeout(5e3)
102
106
  }).catch(() => {});
103
- setTimeout(() => process.exit(0), 1500).unref();
104
107
  }
105
108
  main().catch(() => process.exit(0));
106
109
  //#endregion