hippo-memory 1.59.0 → 1.61.0

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 (596) hide show
  1. package/README.md +1 -1
  2. package/dist/ablation.js +9 -27
  3. package/dist/agent-memories/apply.js +4 -1
  4. package/dist/agent-memories/claude-code.js +1 -1
  5. package/dist/agent-memories/codex.js +1 -1
  6. package/dist/agent-memories/gemini.js +1 -1
  7. package/dist/agent-memories/legacy.js +1 -1
  8. package/dist/agent-memories/source.js +1 -1
  9. package/dist/agent-memories/sync.js +8 -3
  10. package/dist/ambient-store.d.ts +14 -0
  11. package/dist/ambient-store.js +90 -0
  12. package/dist/ambient.d.ts +23 -0
  13. package/dist/ambient.js +72 -50
  14. package/dist/api/assemble.d.ts +93 -0
  15. package/dist/api/assemble.js +152 -0
  16. package/dist/api/audit.d.ts +17 -0
  17. package/dist/api/audit.js +23 -0
  18. package/dist/api/auth.d.ts +79 -0
  19. package/dist/api/auth.js +178 -0
  20. package/dist/api/context-types.d.ts +105 -0
  21. package/dist/api/context-types.js +3 -0
  22. package/dist/api/context.d.ts +31 -0
  23. package/dist/api/context.js +705 -0
  24. package/dist/api/dormant.d.ts +30 -0
  25. package/dist/api/dormant.js +140 -0
  26. package/dist/api/drill-down.d.ts +84 -0
  27. package/dist/api/drill-down.js +123 -0
  28. package/dist/api/forget.d.ts +57 -0
  29. package/dist/api/forget.js +87 -0
  30. package/dist/api/goals.d.ts +18 -0
  31. package/dist/api/goals.js +33 -0
  32. package/dist/api/learn.d.ts +31 -0
  33. package/dist/api/learn.js +88 -0
  34. package/dist/api/outcome.d.ts +61 -0
  35. package/dist/api/outcome.js +66 -0
  36. package/dist/api/promote.d.ts +54 -0
  37. package/dist/api/promote.js +203 -0
  38. package/dist/api/quarantine.d.ts +24 -0
  39. package/dist/api/quarantine.js +121 -0
  40. package/dist/api/recall-types.d.ts +390 -0
  41. package/dist/api/recall-types.js +3 -0
  42. package/dist/api/recall.d.ts +36 -0
  43. package/dist/api/recall.js +634 -0
  44. package/dist/api/remember.d.ts +35 -0
  45. package/dist/api/remember.js +43 -0
  46. package/dist/api/sleep.d.ts +136 -0
  47. package/dist/api/sleep.js +271 -0
  48. package/dist/api/tokens.d.ts +26 -0
  49. package/dist/api/tokens.js +60 -0
  50. package/dist/api/types.d.ts +56 -0
  51. package/dist/api/types.js +38 -0
  52. package/dist/api.d.ts +20 -1260
  53. package/dist/api.js +25 -2711
  54. package/dist/audit.d.ts +3 -0
  55. package/dist/audit.js +7 -3
  56. package/dist/auth.d.ts +45 -4
  57. package/dist/auth.js +125 -48
  58. package/dist/autolearn.js +7 -4
  59. package/dist/capture/command.d.ts +33 -0
  60. package/dist/capture/command.js +264 -0
  61. package/dist/capture/compact.d.ts +44 -0
  62. package/dist/capture/compact.js +354 -0
  63. package/dist/capture/extract.d.ts +21 -0
  64. package/dist/capture/extract.js +464 -0
  65. package/dist/capture/transcript.d.ts +40 -0
  66. package/dist/capture/transcript.js +193 -0
  67. package/dist/capture-error.js +2 -1
  68. package/dist/churn-git.js +4 -2
  69. package/dist/cli/audit.d.ts +3 -0
  70. package/dist/cli/audit.js +159 -0
  71. package/dist/cli/auth.d.ts +2 -0
  72. package/dist/cli/auth.js +171 -0
  73. package/dist/cli/briefs.d.ts +4 -0
  74. package/dist/cli/briefs.js +435 -0
  75. package/dist/cli/card.d.ts +3 -0
  76. package/dist/cli/card.js +333 -0
  77. package/dist/cli/context.d.ts +15 -0
  78. package/dist/cli/context.js +366 -0
  79. package/dist/cli/continuity.d.ts +6 -0
  80. package/dist/cli/continuity.js +445 -0
  81. package/dist/cli/curate.d.ts +19 -0
  82. package/dist/cli/curate.js +555 -0
  83. package/dist/cli/dag.d.ts +5 -0
  84. package/dist/cli/dag.js +177 -0
  85. package/dist/cli/decisions.d.ts +4 -0
  86. package/dist/cli/decisions.js +528 -0
  87. package/dist/cli/eval.d.ts +5 -0
  88. package/dist/cli/eval.js +213 -0
  89. package/dist/cli/explain.d.ts +4 -0
  90. package/dist/cli/explain.js +150 -0
  91. package/dist/cli/goals.d.ts +2 -0
  92. package/dist/cli/goals.js +196 -0
  93. package/dist/cli/hook-blocks.d.ts +18 -0
  94. package/dist/cli/hook-blocks.js +233 -0
  95. package/dist/cli/init.d.ts +2 -0
  96. package/dist/cli/init.js +305 -0
  97. package/dist/cli/maintenance.d.ts +4 -0
  98. package/dist/cli/maintenance.js +179 -0
  99. package/dist/cli/playbooks.d.ts +4 -0
  100. package/dist/cli/playbooks.js +556 -0
  101. package/dist/cli/projects.js +1 -1
  102. package/dist/cli/recall.d.ts +7 -0
  103. package/dist/cli/recall.js +597 -0
  104. package/dist/cli/remember.d.ts +5 -0
  105. package/dist/cli/remember.js +442 -0
  106. package/dist/cli/serve.d.ts +5 -0
  107. package/dist/cli/serve.js +40 -0
  108. package/dist/cli/session-hooks.d.ts +29 -0
  109. package/dist/cli/session-hooks.js +637 -0
  110. package/dist/cli/setup.d.ts +4 -0
  111. package/dist/cli/setup.js +376 -0
  112. package/dist/cli/shared.d.ts +15 -19
  113. package/dist/cli/shared.js +50 -341
  114. package/dist/cli/slack.d.ts +2 -0
  115. package/dist/cli/slack.js +171 -0
  116. package/dist/cli/status.d.ts +18 -0
  117. package/dist/cli/status.js +400 -0
  118. package/dist/cli/transfer.d.ts +10 -0
  119. package/dist/cli/transfer.js +438 -0
  120. package/dist/cli/usage.d.ts +85 -0
  121. package/dist/cli/usage.js +741 -0
  122. package/dist/cli.d.ts +319 -13
  123. package/dist/cli.js +436 -8580
  124. package/dist/client.js +15 -8
  125. package/dist/compaction-record.js +12 -9
  126. package/dist/config.js +12 -11
  127. package/dist/connectors/github/backfill.js +94 -87
  128. package/dist/connectors/github/cli-impl.js +5 -3
  129. package/dist/connectors/github/dlq.js +67 -54
  130. package/dist/connectors/github/ingest.js +34 -35
  131. package/dist/connectors/github/tenant-routing.js +3 -2
  132. package/dist/connectors/github/webhook.js +135 -216
  133. package/dist/connectors/slack/dlq.js +49 -61
  134. package/dist/connectors/slack/ingest.js +55 -48
  135. package/dist/connectors/slack/tenant-routing.js +4 -3
  136. package/dist/connectors/slack/webhook.js +72 -74
  137. package/dist/consolidate/conflicts.d.ts +10 -0
  138. package/dist/consolidate/conflicts.js +178 -0
  139. package/dist/consolidate/decay.d.ts +12 -0
  140. package/dist/consolidate/decay.js +145 -0
  141. package/dist/consolidate/llm-passes.d.ts +3 -0
  142. package/dist/consolidate/llm-passes.js +141 -0
  143. package/dist/consolidate/merge.d.ts +7 -0
  144. package/dist/consolidate/merge.js +251 -0
  145. package/dist/consolidate/physics-pass.d.ts +3 -0
  146. package/dist/consolidate/physics-pass.js +60 -0
  147. package/dist/consolidate/run.d.ts +69 -0
  148. package/dist/consolidate/run.js +76 -0
  149. package/dist/consolidate/sleep.d.ts +18 -0
  150. package/dist/consolidate/sleep.js +209 -0
  151. package/dist/consolidate/traces.d.ts +4 -0
  152. package/dist/consolidate/traces.js +178 -0
  153. package/dist/context-auto.js +7 -11
  154. package/dist/context-render.d.ts +1 -1
  155. package/dist/context-render.js +1 -1
  156. package/dist/customer-notes.d.ts +3 -0
  157. package/dist/customer-notes.js +6 -4
  158. package/dist/dag.js +13 -12
  159. package/dist/dashboard-actions.d.ts +20 -0
  160. package/dist/dashboard-actions.js +88 -0
  161. package/dist/dashboard-params.d.ts +45 -0
  162. package/dist/dashboard-params.js +127 -0
  163. package/dist/dashboard-queries.d.ts +15 -0
  164. package/dist/dashboard-queries.js +355 -0
  165. package/dist/dashboard-snapshot.d.ts +138 -0
  166. package/dist/dashboard-snapshot.js +308 -0
  167. package/dist/dashboard-types.d.ts +163 -0
  168. package/dist/dashboard-types.js +3 -0
  169. package/dist/dashboard.d.ts +6 -7
  170. package/dist/dashboard.js +231 -203
  171. package/dist/db/busy.d.ts +5 -0
  172. package/dist/db/busy.js +24 -0
  173. package/dist/db/continuity.d.ts +5 -0
  174. package/dist/db/continuity.js +145 -0
  175. package/dist/db/meta.d.ts +8 -0
  176. package/dist/db/meta.js +35 -0
  177. package/dist/db/migrate.d.ts +9 -0
  178. package/dist/db/migrate.js +138 -0
  179. package/dist/db/migrations/index.d.ts +5 -0
  180. package/dist/db/migrations/index.js +109 -0
  181. package/dist/db/migrations/types.d.ts +14 -0
  182. package/dist/db/migrations/types.js +2 -0
  183. package/dist/db/migrations/v01.d.ts +3 -0
  184. package/dist/db/migrations/v01.js +38 -0
  185. package/dist/db/migrations/v02.d.ts +3 -0
  186. package/dist/db/migrations/v02.js +21 -0
  187. package/dist/db/migrations/v03.d.ts +3 -0
  188. package/dist/db/migrations/v03.js +22 -0
  189. package/dist/db/migrations/v04.d.ts +3 -0
  190. package/dist/db/migrations/v04.js +28 -0
  191. package/dist/db/migrations/v05.d.ts +3 -0
  192. package/dist/db/migrations/v05.js +21 -0
  193. package/dist/db/migrations/v06.d.ts +3 -0
  194. package/dist/db/migrations/v06.js +25 -0
  195. package/dist/db/migrations/v07.d.ts +3 -0
  196. package/dist/db/migrations/v07.js +13 -0
  197. package/dist/db/migrations/v08.d.ts +3 -0
  198. package/dist/db/migrations/v08.js +8 -0
  199. package/dist/db/migrations/v09.d.ts +3 -0
  200. package/dist/db/migrations/v09.js +13 -0
  201. package/dist/db/migrations/v10.d.ts +3 -0
  202. package/dist/db/migrations/v10.js +17 -0
  203. package/dist/db/migrations/v11.d.ts +3 -0
  204. package/dist/db/migrations/v11.js +15 -0
  205. package/dist/db/migrations/v12.d.ts +3 -0
  206. package/dist/db/migrations/v12.js +11 -0
  207. package/dist/db/migrations/v13.d.ts +3 -0
  208. package/dist/db/migrations/v13.js +15 -0
  209. package/dist/db/migrations/v14.d.ts +3 -0
  210. package/dist/db/migrations/v14.js +66 -0
  211. package/dist/db/migrations/v15.d.ts +3 -0
  212. package/dist/db/migrations/v15.js +42 -0
  213. package/dist/db/migrations/v16.d.ts +3 -0
  214. package/dist/db/migrations/v16.js +61 -0
  215. package/dist/db/migrations/v17.d.ts +3 -0
  216. package/dist/db/migrations/v17.js +46 -0
  217. package/dist/db/migrations/v18.d.ts +3 -0
  218. package/dist/db/migrations/v18.js +60 -0
  219. package/dist/db/migrations/v19.d.ts +3 -0
  220. package/dist/db/migrations/v19.js +28 -0
  221. package/dist/db/migrations/v20.d.ts +3 -0
  222. package/dist/db/migrations/v20.js +42 -0
  223. package/dist/db/migrations/v21.d.ts +3 -0
  224. package/dist/db/migrations/v21.js +16 -0
  225. package/dist/db/migrations/v22.d.ts +3 -0
  226. package/dist/db/migrations/v22.js +82 -0
  227. package/dist/db/migrations/v23.d.ts +3 -0
  228. package/dist/db/migrations/v23.js +48 -0
  229. package/dist/db/migrations/v24.d.ts +3 -0
  230. package/dist/db/migrations/v24.js +72 -0
  231. package/dist/db/migrations/v25.d.ts +3 -0
  232. package/dist/db/migrations/v25.js +46 -0
  233. package/dist/db/migrations/v26.d.ts +3 -0
  234. package/dist/db/migrations/v26.js +23 -0
  235. package/dist/db/migrations/v27.d.ts +3 -0
  236. package/dist/db/migrations/v27.js +57 -0
  237. package/dist/db/migrations/v28.d.ts +3 -0
  238. package/dist/db/migrations/v28.js +38 -0
  239. package/dist/db/migrations/v29.d.ts +3 -0
  240. package/dist/db/migrations/v29.js +78 -0
  241. package/dist/db/migrations/v30.d.ts +3 -0
  242. package/dist/db/migrations/v30.js +92 -0
  243. package/dist/db/migrations/v31.d.ts +3 -0
  244. package/dist/db/migrations/v31.js +74 -0
  245. package/dist/db/migrations/v32.d.ts +3 -0
  246. package/dist/db/migrations/v32.js +102 -0
  247. package/dist/db/migrations/v33.d.ts +3 -0
  248. package/dist/db/migrations/v33.js +104 -0
  249. package/dist/db/migrations/v34.d.ts +3 -0
  250. package/dist/db/migrations/v34.js +93 -0
  251. package/dist/db/migrations/v35.d.ts +3 -0
  252. package/dist/db/migrations/v35.js +98 -0
  253. package/dist/db/migrations/v36.d.ts +3 -0
  254. package/dist/db/migrations/v36.js +98 -0
  255. package/dist/db/migrations/v37.d.ts +3 -0
  256. package/dist/db/migrations/v37.js +219 -0
  257. package/dist/db/migrations/v38.d.ts +3 -0
  258. package/dist/db/migrations/v38.js +277 -0
  259. package/dist/db/migrations/v39.d.ts +3 -0
  260. package/dist/db/migrations/v39.js +59 -0
  261. package/dist/db/migrations/v40.d.ts +3 -0
  262. package/dist/db/migrations/v40.js +74 -0
  263. package/dist/db/migrations/v41.d.ts +3 -0
  264. package/dist/db/migrations/v41.js +43 -0
  265. package/dist/db/migrations/v42.d.ts +3 -0
  266. package/dist/db/migrations/v42.js +41 -0
  267. package/dist/db/migrations/v43.d.ts +3 -0
  268. package/dist/db/migrations/v43.js +67 -0
  269. package/dist/db/migrations/v44.d.ts +3 -0
  270. package/dist/db/migrations/v44.js +28 -0
  271. package/dist/db/migrations/v45.d.ts +3 -0
  272. package/dist/db/migrations/v45.js +30 -0
  273. package/dist/db/migrations/v46.d.ts +3 -0
  274. package/dist/db/migrations/v46.js +25 -0
  275. package/dist/db/migrations/v47.d.ts +3 -0
  276. package/dist/db/migrations/v47.js +17 -0
  277. package/dist/db/migrations/v48.d.ts +3 -0
  278. package/dist/db/migrations/v48.js +10 -0
  279. package/dist/db/migrations/v49.d.ts +3 -0
  280. package/dist/db/migrations/v49.js +31 -0
  281. package/dist/db/migrations/v50.d.ts +3 -0
  282. package/dist/db/migrations/v50.js +67 -0
  283. package/dist/db/migrations/v51.d.ts +3 -0
  284. package/dist/db/migrations/v51.js +14 -0
  285. package/dist/db/migrations/v52.d.ts +3 -0
  286. package/dist/db/migrations/v52.js +7 -0
  287. package/dist/db/open.d.ts +23 -0
  288. package/dist/db/open.js +146 -0
  289. package/dist/db/sqlite.d.ts +21 -0
  290. package/dist/db/sqlite.js +8 -0
  291. package/dist/db/tables.d.ts +7 -0
  292. package/dist/db/tables.js +38 -0
  293. package/dist/db.d.ts +6 -46
  294. package/dist/db.js +5 -3036
  295. package/dist/decisions.d.ts +4 -1
  296. package/dist/decisions.js +9 -7
  297. package/dist/dedupe.js +3 -2
  298. package/dist/delivery-recorder.js +5 -1
  299. package/dist/doctor.js +4 -3
  300. package/dist/dormant.js +1 -0
  301. package/dist/embedding-provider.d.ts +1 -1
  302. package/dist/embedding-provider.js +7 -5
  303. package/dist/embeddings.d.ts +9 -52
  304. package/dist/embeddings.js +47 -298
  305. package/dist/env.d.ts +75 -0
  306. package/dist/env.js +119 -0
  307. package/dist/eval-suite.js +1 -1
  308. package/dist/eval.js +2 -2
  309. package/dist/extract.js +5 -4
  310. package/dist/gated-write.js +3 -1
  311. package/dist/goals.d.ts +3 -1
  312. package/dist/goals.js +18 -0
  313. package/dist/graph/read.d.ts +73 -0
  314. package/dist/graph/read.js +325 -0
  315. package/dist/graph/rows.d.ts +45 -0
  316. package/dist/graph/rows.js +51 -0
  317. package/dist/graph/types.d.ts +83 -0
  318. package/dist/graph/types.js +11 -0
  319. package/dist/graph/write.d.ts +93 -0
  320. package/dist/{graph.js → graph/write.js} +8 -391
  321. package/dist/graph-extract.js +2 -1
  322. package/dist/graph-recall.d.ts +1 -1
  323. package/dist/graph-recall.js +2 -2
  324. package/dist/graph-stream.js +1 -1
  325. package/dist/graph-view.d.ts +1 -1
  326. package/dist/graph-view.js +1 -1
  327. package/dist/half-life-migration.d.ts +1 -1
  328. package/dist/half-life-migration.js +2 -1
  329. package/dist/handoff.js +3 -0
  330. package/dist/hooks/codex-session.d.ts +8 -0
  331. package/dist/hooks/codex-session.js +76 -0
  332. package/dist/hooks/codex-wrapper.d.ts +55 -0
  333. package/dist/hooks/codex-wrapper.js +288 -0
  334. package/dist/hooks/json-hooks.d.ts +63 -0
  335. package/dist/hooks/json-hooks.js +356 -0
  336. package/dist/hooks/opencode.d.ts +50 -0
  337. package/dist/hooks/opencode.js +202 -0
  338. package/dist/hooks/shared.d.ts +54 -0
  339. package/dist/hooks/shared.js +77 -0
  340. package/dist/http-retry.d.ts +2 -0
  341. package/dist/http-retry.js +4 -3
  342. package/dist/http-util.d.ts +3 -0
  343. package/dist/http-util.js +10 -0
  344. package/dist/{importers.d.ts → importers/core.d.ts} +13 -17
  345. package/dist/importers/core.js +141 -0
  346. package/dist/importers/markdown-parse.d.ts +41 -0
  347. package/dist/importers/markdown-parse.js +132 -0
  348. package/dist/importers/markdown.d.ts +3 -0
  349. package/dist/importers/markdown.js +92 -0
  350. package/dist/importers/sources.d.ts +7 -0
  351. package/dist/importers/sources.js +229 -0
  352. package/dist/importers/vault.d.ts +11 -0
  353. package/dist/importers/vault.js +352 -0
  354. package/dist/incidents.d.ts +3 -0
  355. package/dist/incidents.js +8 -5
  356. package/dist/index.d.ts +25 -6
  357. package/dist/index.js +23 -6
  358. package/dist/invalidation.js +2 -1
  359. package/dist/judgment.js +7 -3
  360. package/dist/keyset.d.ts +13 -0
  361. package/dist/keyset.js +8 -0
  362. package/dist/local-embedding.d.ts +13 -0
  363. package/dist/local-embedding.js +165 -0
  364. package/dist/log.d.ts +7 -0
  365. package/dist/log.js +19 -1
  366. package/dist/mcp/admin-tools.d.ts +8 -0
  367. package/dist/mcp/admin-tools.js +116 -0
  368. package/dist/mcp/format.d.ts +27 -0
  369. package/dist/mcp/format.js +135 -0
  370. package/dist/mcp/memory-tools.d.ts +5 -0
  371. package/dist/mcp/memory-tools.js +83 -0
  372. package/dist/mcp/protocol.d.ts +83 -0
  373. package/dist/mcp/protocol.js +55 -0
  374. package/dist/mcp/recall-tools.d.ts +6 -0
  375. package/dist/mcp/recall-tools.js +320 -0
  376. package/dist/mcp/request.d.ts +10 -0
  377. package/dist/mcp/request.js +163 -0
  378. package/dist/mcp/server.d.ts +4 -70
  379. package/dist/mcp/server.js +8 -1172
  380. package/dist/mcp/session-state.d.ts +11 -0
  381. package/dist/mcp/session-state.js +28 -0
  382. package/dist/mcp/stdio.d.ts +8 -0
  383. package/dist/mcp/stdio.js +79 -0
  384. package/dist/mcp/tools.d.ts +11 -0
  385. package/dist/mcp/tools.js +247 -0
  386. package/dist/memory.d.ts +8 -3
  387. package/dist/memory.js +27 -1
  388. package/dist/multihop.d.ts +1 -1
  389. package/dist/multihop.js +2 -1
  390. package/dist/owner-validation.js +2 -1
  391. package/dist/physics-state.js +10 -7
  392. package/dist/policies.d.ts +3 -0
  393. package/dist/policies.js +8 -6
  394. package/dist/postinstall.js +3 -2
  395. package/dist/predictions/planning-fallacy.d.ts +100 -0
  396. package/dist/predictions/planning-fallacy.js +190 -0
  397. package/dist/{predictions.d.ts → predictions/store.d.ts} +7 -102
  398. package/dist/predictions/store.js +434 -0
  399. package/dist/processes.d.ts +3 -0
  400. package/dist/processes.js +8 -5
  401. package/dist/project-briefs.d.ts +3 -0
  402. package/dist/project-briefs.js +7 -5
  403. package/dist/project-identity.d.ts +1 -1
  404. package/dist/project-identity.js +12 -6
  405. package/dist/project-merge.js +3 -1
  406. package/dist/quarantine.d.ts +2 -1
  407. package/dist/quarantine.js +8 -5
  408. package/dist/raw-archive-mirror-cleanup.js +2 -1
  409. package/dist/raw-archive.js +1 -1
  410. package/dist/recall-history.js +3 -2
  411. package/dist/recall-pipeline.d.ts +2 -2
  412. package/dist/recall-pipeline.js +16 -8
  413. package/dist/recall-scope.js +1 -1
  414. package/dist/recall-trace.d.ts +3 -3
  415. package/dist/recall-trace.js +10 -14
  416. package/dist/refine-llm.js +20 -11
  417. package/dist/reject-flow.js +6 -1
  418. package/dist/rerankers/clef.d.ts +2 -2
  419. package/dist/rerankers/clef.js +72 -30
  420. package/dist/rerankers/cross-encoder.js +6 -4
  421. package/dist/rerankers/jev.d.ts +4 -2
  422. package/dist/rerankers/jev.js +24 -19
  423. package/dist/rerankers/llm.d.ts +4 -2
  424. package/dist/rerankers/llm.js +61 -41
  425. package/dist/rerankers/types.d.ts +1 -1
  426. package/dist/salience.js +1 -1
  427. package/dist/same-text.d.ts +2 -0
  428. package/dist/same-text.js +4 -0
  429. package/dist/scheduler.d.ts +1 -0
  430. package/dist/scheduler.js +26 -3
  431. package/dist/scope.js +4 -3
  432. package/dist/search/as-of.d.ts +10 -0
  433. package/dist/search/as-of.js +22 -0
  434. package/dist/search/bm25-search.d.ts +14 -0
  435. package/dist/search/bm25-search.js +43 -0
  436. package/dist/search/bm25.d.ts +15 -0
  437. package/dist/search/bm25.js +54 -0
  438. package/dist/search/boosts.d.ts +54 -0
  439. package/dist/search/boosts.js +94 -0
  440. package/dist/search/breakdown.d.ts +7 -0
  441. package/dist/search/breakdown.js +20 -0
  442. package/dist/search/explain.d.ts +25 -0
  443. package/dist/search/explain.js +31 -0
  444. package/dist/search/finalize.d.ts +8 -0
  445. package/dist/search/finalize.js +52 -0
  446. package/dist/search/fusion.d.ts +27 -0
  447. package/dist/search/fusion.js +42 -0
  448. package/dist/search/hybrid-score.d.ts +20 -0
  449. package/dist/search/hybrid-score.js +73 -0
  450. package/dist/search/hybrid.d.ts +46 -0
  451. package/dist/search/hybrid.js +64 -0
  452. package/dist/search/physics-search.d.ts +29 -0
  453. package/dist/search/physics-search.js +162 -0
  454. package/dist/search/rerank.d.ts +10 -0
  455. package/dist/search/rerank.js +72 -0
  456. package/dist/search/temporal.d.ts +15 -0
  457. package/dist/search/temporal.js +45 -0
  458. package/dist/search/types.d.ts +91 -0
  459. package/dist/search/types.js +2 -0
  460. package/dist/search/vector.d.ts +30 -0
  461. package/dist/search/vector.js +71 -0
  462. package/dist/secret-detect.d.ts +2 -0
  463. package/dist/secret-detect.js +3 -1
  464. package/dist/server/auth.d.ts +52 -0
  465. package/dist/server/auth.js +221 -0
  466. package/dist/server/client-ip.d.ts +25 -0
  467. package/dist/server/client-ip.js +92 -0
  468. package/dist/server/cursor.d.ts +23 -0
  469. package/dist/server/cursor.js +58 -0
  470. package/dist/server/lifecycle.d.ts +7 -0
  471. package/dist/server/lifecycle.js +29 -0
  472. package/dist/server/mcp-http.d.ts +5 -0
  473. package/dist/server/mcp-http.js +199 -0
  474. package/dist/server/request.d.ts +33 -0
  475. package/dist/server/request.js +103 -0
  476. package/dist/server/routes/admin.d.ts +9 -0
  477. package/dist/server/routes/admin.js +158 -0
  478. package/dist/server/routes/customer-notes.d.ts +7 -0
  479. package/dist/server/routes/customer-notes.js +112 -0
  480. package/dist/server/routes/decisions.d.ts +7 -0
  481. package/dist/server/routes/decisions.js +133 -0
  482. package/dist/server/routes/incidents.d.ts +7 -0
  483. package/dist/server/routes/incidents.js +126 -0
  484. package/dist/server/routes/memories.d.ts +10 -0
  485. package/dist/server/routes/memories.js +179 -0
  486. package/dist/server/routes/policies.d.ts +8 -0
  487. package/dist/server/routes/policies.js +151 -0
  488. package/dist/server/routes/predictions.d.ts +7 -0
  489. package/dist/server/routes/predictions.js +164 -0
  490. package/dist/server/routes/processes.d.ts +7 -0
  491. package/dist/server/routes/processes.js +161 -0
  492. package/dist/server/routes/project-briefs.d.ts +8 -0
  493. package/dist/server/routes/project-briefs.js +136 -0
  494. package/dist/server/routes/recall.d.ts +8 -0
  495. package/dist/server/routes/recall.js +340 -0
  496. package/dist/server/routes/skills.d.ts +8 -0
  497. package/dist/server/routes/skills.js +150 -0
  498. package/dist/server/types.d.ts +56 -0
  499. package/dist/server/types.js +2 -0
  500. package/dist/server/validation.d.ts +14 -0
  501. package/dist/server/validation.js +91 -0
  502. package/dist/server.d.ts +7 -61
  503. package/dist/server.js +92 -2368
  504. package/dist/session-digest.d.ts +1 -1
  505. package/dist/session-digest.js +9 -2
  506. package/dist/shared.d.ts +3 -1
  507. package/dist/shared.js +46 -40
  508. package/dist/skills.d.ts +3 -0
  509. package/dist/skills.js +7 -5
  510. package/dist/stdin.js +3 -2
  511. package/dist/store/audit-event.d.ts +19 -0
  512. package/dist/store/audit-event.js +33 -0
  513. package/dist/store/candidates.d.ts +42 -0
  514. package/dist/store/candidates.js +152 -0
  515. package/dist/store/conflicts.d.ts +44 -0
  516. package/dist/store/conflicts.js +444 -0
  517. package/dist/store/delete-and-batch.d.ts +63 -0
  518. package/dist/store/delete-and-batch.js +310 -0
  519. package/dist/store/entry-reads.d.ts +92 -0
  520. package/dist/store/entry-reads.js +255 -0
  521. package/dist/store/entry-row.d.ts +67 -0
  522. package/dist/store/entry-row.js +208 -0
  523. package/dist/store/entry-writes.d.ts +46 -0
  524. package/dist/store/entry-writes.js +148 -0
  525. package/dist/store/handoffs.d.ts +26 -0
  526. package/dist/store/handoffs.js +184 -0
  527. package/dist/store/index-and-stats.d.ts +52 -0
  528. package/dist/store/index-and-stats.js +214 -0
  529. package/dist/store/markdown.d.ts +10 -0
  530. package/dist/store/markdown.js +108 -0
  531. package/dist/store/mirrors.d.ts +49 -0
  532. package/dist/store/mirrors.js +312 -0
  533. package/dist/store/open.d.ts +15 -0
  534. package/dist/store/open.js +223 -0
  535. package/dist/store/rows.d.ts +178 -0
  536. package/dist/store/rows.js +158 -0
  537. package/dist/store/search-rows.d.ts +86 -0
  538. package/dist/store/search-rows.js +254 -0
  539. package/dist/store/sessions.d.ts +83 -0
  540. package/dist/store/sessions.js +272 -0
  541. package/dist/store/summaries.d.ts +94 -0
  542. package/dist/store/summaries.js +377 -0
  543. package/dist/store/tenant-lookup.d.ts +12 -0
  544. package/dist/store/tenant-lookup.js +16 -0
  545. package/dist/store-cards.js +2 -1
  546. package/dist/summary-dirty.d.ts +4 -0
  547. package/dist/summary-dirty.js +32 -0
  548. package/dist/support-bundle.js +4 -3
  549. package/dist/tenant.js +2 -2
  550. package/dist/token-ledger.js +1 -0
  551. package/dist/tokenize.d.ts +2 -0
  552. package/dist/tokenize.js +16 -0
  553. package/dist/transcript-tail.d.ts +7 -0
  554. package/dist/transcript-tail.js +48 -0
  555. package/dist/vector-store.d.ts +27 -0
  556. package/dist/vector-store.js +210 -0
  557. package/dist/version.d.ts +2 -2
  558. package/dist/version.js +2 -2
  559. package/dist/working-memory.js +1 -1
  560. package/dist/yaml.js +36 -11
  561. package/dist-ui/assets/ibm-plex-mono-latin-400-normal-CvHOgSBP.woff +0 -0
  562. package/dist-ui/assets/ibm-plex-mono-latin-400-normal-DMJ8VG8y.woff2 +0 -0
  563. package/dist-ui/assets/ibm-plex-mono-latin-500-normal-CB9ihrfo.woff +0 -0
  564. package/dist-ui/assets/ibm-plex-mono-latin-500-normal-DSY6xOcd.woff2 +0 -0
  565. package/dist-ui/assets/ibm-plex-mono-latin-600-normal-BgSNZQsw.woff2 +0 -0
  566. package/dist-ui/assets/ibm-plex-mono-latin-600-normal-DWFSQ4vo.woff +0 -0
  567. package/dist-ui/assets/ibm-plex-sans-latin-400-normal-CDDApCn2.woff2 +0 -0
  568. package/dist-ui/assets/ibm-plex-sans-latin-400-normal-CYLoc0-x.woff +0 -0
  569. package/dist-ui/assets/ibm-plex-sans-latin-500-normal-6ng42L7E.woff2 +0 -0
  570. package/dist-ui/assets/ibm-plex-sans-latin-500-normal-BgVn5rGT.woff +0 -0
  571. package/dist-ui/assets/ibm-plex-sans-latin-600-normal-Cu4Hd6ag.woff +0 -0
  572. package/dist-ui/assets/ibm-plex-sans-latin-600-normal-CuJfVYMP.woff2 +0 -0
  573. package/dist-ui/assets/index-DPN7cP19.js +33 -0
  574. package/dist-ui/assets/index-dFloKRVr.css +1 -0
  575. package/dist-ui/index.html +3 -25
  576. package/extensions/openclaw-plugin/openclaw.plugin.json +1 -1
  577. package/extensions/openclaw-plugin/package.json +1 -1
  578. package/openclaw.plugin.json +1 -1
  579. package/package.json +2 -1
  580. package/dist/capture.d.ts +0 -155
  581. package/dist/capture.js +0 -1293
  582. package/dist/consolidate.d.ts +0 -58
  583. package/dist/consolidate.js +0 -1123
  584. package/dist/graph.d.ts +0 -245
  585. package/dist/hooks.d.ts +0 -208
  586. package/dist/hooks.js +0 -1073
  587. package/dist/importers.js +0 -897
  588. package/dist/predictions.js +0 -620
  589. package/dist/search.d.ts +0 -320
  590. package/dist/search.js +0 -968
  591. package/dist/store.d.ts +0 -744
  592. package/dist/store.js +0 -3372
  593. package/dist-ui/assets/d3-BiWEKnn4.js +0 -1
  594. package/dist-ui/assets/index-BhT8RvO6.js +0 -61
  595. package/dist-ui/assets/index-RoXXJ5dq.css +0 -1
  596. package/dist-ui/assets/three-BDgTxR1l.js +0 -4112
@@ -0,0 +1,264 @@
1
+ import * as fs from 'fs';
2
+ import * as path from 'path';
3
+ import { createMemory, Layer } from '../memory.js';
4
+ import { duplicateKey, storedTextKeys } from '../same-text.js';
5
+ import { stampOriginProject } from '../store/entry-row.js';
6
+ import { isInitialized, openStore } from '../store/open.js';
7
+ import { writeEntryMirrors } from '../store/entry-writes.js';
8
+ import { loadAllEntries } from '../store/entry-reads.js';
9
+ import { updateStats } from '../store/index-and-stats.js';
10
+ import { gatedWrite } from '../gated-write.js';
11
+ import { getGlobalRoot, initGlobal } from '../shared.js';
12
+ import { embedMemory } from '../embeddings.js';
13
+ import { maskEmails, redactSecretsStrict } from '../secret-detect.js';
14
+ import { RejectedValueError, checkRejectionGuard } from '../rejection.js';
15
+ import { openHippoDb, closeHippoDb } from '../db.js';
16
+ import { loadConfig } from '../config.js';
17
+ import { classifyOriginProject } from '../project-identity.js';
18
+ import { isStringValue } from '../capture-contract.js';
19
+ import { errorMessage, log } from '../log.js';
20
+ import { extractFromText } from './extract.js';
21
+ import { collectSessionTurns, summariseSessionTurns, resolveLastSessionTranscript } from './transcript.js';
22
+ export function cmdCapture(hippoRoot, options) {
23
+ // Tee stdout/stderr to a log file when --log-file is set. Used by the
24
+ // SessionEnd hook so output (otherwise swallowed by TUI teardown) surfaces
25
+ // on the next session start via `hippo last-sleep`. Runs second in the
26
+ // SessionEnd sequence after `hippo sleep`, so we APPEND rather than
27
+ // truncate — sleep already wrote its own header + body to this file.
28
+ const restoreStdio = options.logFile ? beginLogTee(options.logFile) : null;
29
+ try {
30
+ cmdCaptureCore(hippoRoot, options);
31
+ if (options.logFile)
32
+ console.log('[hippo] capture complete');
33
+ }
34
+ catch (err) {
35
+ if (options.logFile)
36
+ console.log(`[hippo] capture failed: ${errorMessage(err)}`);
37
+ throw err;
38
+ }
39
+ finally {
40
+ if (restoreStdio)
41
+ restoreStdio();
42
+ }
43
+ }
44
+ /**
45
+ * Append-mode tee: writes a banner line then mirrors every stdout/stderr
46
+ * chunk to `logFile` until the returned restore function is called.
47
+ * Failures to write the log are non-fatal; the real streams still get
48
+ * the data.
49
+ */
50
+ function beginLogTee(logFile) {
51
+ try {
52
+ fs.mkdirSync(path.dirname(logFile), { recursive: true });
53
+ fs.appendFileSync(logFile, `[hippo] ${new Date().toISOString()} capturing session...\n`, 'utf8');
54
+ }
55
+ catch (err) {
56
+ log.warn(`could not open log file ${logFile}: ${errorMessage(err)}`);
57
+ return () => { };
58
+ }
59
+ const origStdoutWrite = process.stdout.write.bind(process.stdout);
60
+ const origStderrWrite = process.stderr.write.bind(process.stderr);
61
+ const tee = (chunk) => {
62
+ try {
63
+ const buf = isStringValue(chunk) ? chunk : Buffer.from(chunk).toString('utf8');
64
+ fs.appendFileSync(logFile, buf, 'utf8');
65
+ }
66
+ catch {
67
+ // log failures are non-fatal
68
+ }
69
+ };
70
+ const wrapWrite = (origWrite) => {
71
+ const wrapped = (...args) => {
72
+ tee(args[0]);
73
+ // SAFETY: forwarding the exact arguments Node's real overloaded
74
+ // `write` received is safe regardless of which overload the call
75
+ // site used — Node dispatches on the actual argument shapes at
76
+ // runtime, and `StreamWriteArgs` is the union of both overloads'
77
+ // parameter lists.
78
+ return origWrite(...args);
79
+ };
80
+ // SAFETY: `wrapped` matches both real `write` overload shapes it's
81
+ // assigned to; TS can't verify a single implementation covers an
82
+ // overloaded type, but this one forwards to the real stream method.
83
+ return wrapped;
84
+ };
85
+ process.stdout.write = wrapWrite(origStdoutWrite);
86
+ process.stderr.write = wrapWrite(origStderrWrite);
87
+ return () => {
88
+ process.stdout.write = origStdoutWrite;
89
+ process.stderr.write = origStderrWrite;
90
+ };
91
+ }
92
+ // Console lines here are the `hippo capture` command's printed result, so they stay off the logger.
93
+ function cmdCaptureCore(hippoRoot, options) {
94
+ const useGlobal = options.global;
95
+ const targetRoot = useGlobal ? getGlobalRoot() : hippoRoot;
96
+ if (useGlobal) {
97
+ initGlobal();
98
+ }
99
+ else {
100
+ if (!isInitialized(hippoRoot)) {
101
+ console.error(`No hippo store at ${hippoRoot} (searched ${process.cwd()} and its parents up to your home directory). Run \`hippo init\` first.`);
102
+ process.exit(1);
103
+ }
104
+ }
105
+ // Read input text
106
+ const text = readCaptureText(options);
107
+ if (text === null)
108
+ return;
109
+ if (!text || text.trim().length === 0) {
110
+ console.log('No text to capture from.');
111
+ return;
112
+ }
113
+ // Scrub once here, as the snapshot fields are: every source can carry a pasted token (AGENTS.md: no secrets in memories).
114
+ const extracted = extractFromText(maskEmails(redactSecretsStrict(text)));
115
+ if (extracted.length === 0) {
116
+ console.log('No actionable items found in the input.');
117
+ return;
118
+ }
119
+ // Dedup only against rows this capture's reader sees: another tenant's rows (L9), or another
120
+ // project's, are hidden from it, so they must not stop its own copy.
121
+ const stored = loadAllEntries(targetRoot, useGlobal ? undefined : options.tenantId);
122
+ const origin = options.originProject;
123
+ const keys = storedTextKeys(origin === undefined
124
+ ? stored
125
+ : stored.filter((e) => classifyOriginProject(e.origin_project, origin) !== 'cross-project'));
126
+ const { captured, skipped, rejected } = captureExtractedItems(targetRoot, options, extracted, keys);
127
+ const prefix = options.dryRun ? '[dry-run] ' : '';
128
+ const globalPrefix = useGlobal ? '[global] ' : '';
129
+ console.log(`\n${prefix}${globalPrefix}Captured ${captured} items (${skipped} skipped as duplicates` +
130
+ (rejected > 0 ? `, ${rejected} rejected` : '') +
131
+ ')');
132
+ }
133
+ /** The raw text for the chosen source; null after printing why a last-session capture has nothing. */
134
+ function readCaptureText(options) {
135
+ switch (options.source) {
136
+ case 'stdin': {
137
+ try {
138
+ return fs.readFileSync(0, 'utf8');
139
+ }
140
+ catch {
141
+ console.error('No input on stdin. Pipe text in or use --file <path>.');
142
+ process.exit(1);
143
+ }
144
+ }
145
+ case 'file': {
146
+ if (!options.filePath) {
147
+ console.error('Missing file path. Usage: hippo capture --file <path>');
148
+ process.exit(1);
149
+ }
150
+ if (!fs.existsSync(options.filePath)) {
151
+ console.error(`File not found: ${options.filePath}`);
152
+ process.exit(1);
153
+ }
154
+ return fs.readFileSync(options.filePath, 'utf8');
155
+ }
156
+ case 'last-session': {
157
+ let turns = options.sessionTurns;
158
+ if (!turns) {
159
+ const resolved = resolveLastSessionTranscript(options.transcriptPath, options.stdinText, { mayScan: !options.stdinTimedOut });
160
+ if (!resolved) {
161
+ console.log('No transcript found. Pass --transcript <path> or run from a SessionEnd hook.');
162
+ return null;
163
+ }
164
+ turns = collectSessionTurns(fs.readFileSync(resolved, 'utf8'));
165
+ }
166
+ const text = summariseSessionTurns(turns);
167
+ if (!text) {
168
+ console.log('Transcript had no user/assistant messages to summarise.');
169
+ return null;
170
+ }
171
+ return text;
172
+ }
173
+ }
174
+ }
175
+ function captureExtractedItems(targetRoot, options, extracted, keys) {
176
+ const tally = { captured: 0, skipped: 0, rejected: 0 };
177
+ const baseHalfLifeDays = loadConfig(targetRoot).defaultHalfLifeDays;
178
+ // AT1 P2 fix (dry-run parity, docs/plans/2026-08-15-at1-rejected-value-tombstone.md):
179
+ // dry-run used to skip the guarded write branch ENTIRELY, so a tombstoned
180
+ // extraction printed as `[capture]` and counted toward `captured` — the
181
+ // preview lied about what a real run would do. Mirrors importers.ts's
182
+ // importEntries dry-run probe (commit 6146e82): open a read-only handle
183
+ // once, run the same checkRejectionGuard the real write path uses via
184
+ // writeEntry, never write anything.
185
+ const dryRunDb = options.dryRun ? openHippoDb(targetRoot) : null;
186
+ const writeDb = options.dryRun ? null : openStore(targetRoot);
187
+ try {
188
+ for (const item of extracted) {
189
+ if (keys.has(duplicateKey(item.content))) {
190
+ tally.skipped++;
191
+ if (options.dryRun) {
192
+ console.log(` [skip] (${item.category}) ${item.content.slice(0, 80)}`);
193
+ }
194
+ continue;
195
+ }
196
+ const entry = captureEntry(item, options, baseHalfLifeDays);
197
+ tally[captureOne({ targetRoot, options, dryRunDb, writeDb, keys }, item, entry)]++;
198
+ }
199
+ }
200
+ finally {
201
+ if (dryRunDb)
202
+ closeHippoDb(dryRunDb);
203
+ if (writeDb)
204
+ closeHippoDb(writeDb);
205
+ }
206
+ return tally;
207
+ }
208
+ function captureEntry(item, options, baseHalfLifeDays) {
209
+ const useGlobal = options.global;
210
+ // A3: kind defaults to 'distilled'. capture.ts extracts curated items from
211
+ // session output (not raw transcript chunks), so distilled is correct. If a
212
+ // future variant captures full raw session text, it MUST set kind: 'raw'
213
+ // and route deletions through archiveRawMemory(). See MEMORY_ENVELOPE.md.
214
+ // L9: the dedup read above is scoped by options.tenantId — the WRITE
215
+ // must match, or scoped-dedup-passes-then-default-tenant-write breaks
216
+ // the per-tenant contract. Mirror the dedup-read guard: when
217
+ // global: true, the global store is host-wide and tenant is irrelevant
218
+ // (createMemory's default 'default' applies). When global: false,
219
+ // options.tenantId scopes the write to the same tenant as the dedup.
220
+ const created = createMemory(item.content, {
221
+ layer: Layer.Episodic,
222
+ tags: item.tags,
223
+ source: 'capture',
224
+ confidence: 'observed',
225
+ tenantId: useGlobal ? undefined : options.tenantId,
226
+ baseHalfLifeDays,
227
+ });
228
+ return options.originProject === undefined ? created : { ...created, origin_project: options.originProject };
229
+ }
230
+ /** Previews or writes one non-duplicate item and names the tally it counts toward. */
231
+ function captureOne(ctx, item, entry) {
232
+ const { targetRoot, options, dryRunDb, writeDb, keys } = ctx;
233
+ if (options.dryRun) {
234
+ if (dryRunDb) {
235
+ try {
236
+ checkRejectionGuard(dryRunDb, entry.tenantId ?? 'default', entry.id, entry.content);
237
+ }
238
+ catch (err) {
239
+ if (err instanceof RejectedValueError) {
240
+ console.log(` [reject] (${item.category}) ${item.content.slice(0, 80)} - matches a rejected value`);
241
+ return 'rejected';
242
+ }
243
+ throw err;
244
+ }
245
+ }
246
+ console.log(` [capture] (${item.category}) ${item.content}`);
247
+ }
248
+ else if (writeDb !== null) {
249
+ // AT1 (plan §3 containment): one rejected item must not abort the
250
+ // rest of this capture's items.
251
+ const stamped = stampOriginProject(targetRoot, entry);
252
+ const outcome = gatedWrite(writeDb, targetRoot, stamped);
253
+ if (outcome === 'skipped:rejected')
254
+ return 'rejected';
255
+ if (outcome !== 'written')
256
+ return 'skipped';
257
+ writeEntryMirrors(targetRoot, stamped);
258
+ updateStats(targetRoot, { remembered: 1 });
259
+ keys.add(duplicateKey(item.content)); // within-batch dedup
260
+ void embedMemory(targetRoot, entry);
261
+ }
262
+ return 'captured';
263
+ }
264
+ //# sourceMappingURL=command.js.map
@@ -0,0 +1,44 @@
1
+ import { type TaskSnapshot } from '../store/rows.js';
2
+ export declare const PRE_COMPACT_TASK_CAP = 200;
3
+ export declare const PRE_COMPACT_SUMMARY_CAP = 2000;
4
+ export declare const PRE_COMPACT_NEXT_STEP_CAP = 500;
5
+ /**
6
+ * Cap from the RECENT end: summariseTranscript emits user turns oldest-to-
7
+ * newest with assistant responses after them, so a head-first cap keeps
8
+ * stale context and drops exactly the newest working state this feature
9
+ * exists to preserve. Keep the LAST maxChars instead, aligned forward to a
10
+ * nearby line start, with a trim marker (codex round 3).
11
+ */
12
+ export declare function truncateKeepNewest(text: string, maxChars: number): string;
13
+ /**
14
+ * Log-forgery guard: messages here interpolate payload-controlled values
15
+ * (transcript paths, session ids). Strip C0 control chars — newlines above
16
+ * all — so a crafted value can't inject fake `[hippo] ...` log lines.
17
+ * Exported for every `[hippo]`-prefixed log writer that interpolates
18
+ * payload-controlled values (cli.ts appendSessionEndCloseLog) — one shared
19
+ * guard, not per-file copies.
20
+ */
21
+ export declare function sanitizeLogMessage(message: string): string;
22
+ /** A session's task, summary and next step from its transcript tail, secrets scrubbed and capped, '' where none; null with a logged reason when nothing is derivable. */
23
+ export declare function transcriptWorkingState(transcriptPath: string, log: (message: string) => void): Pick<TaskSnapshot, 'task' | 'summary' | 'next_step'> | null;
24
+ export interface PreCompactOptions {
25
+ stdinText?: string;
26
+ stdinTimedOut?: boolean;
27
+ logFile?: string;
28
+ }
29
+ /**
30
+ * PreCompact hook entry point. Exit code 2 on PreCompact BLOCKS compaction,
31
+ * so this verb must exit 0 on every path — malformed stdin, missing
32
+ * transcript, and store errors all degrade to a logged no-op rather than a
33
+ * thrown error. Callers (src/cli.ts) must not wrap this in anything that
34
+ * could turn a caught-and-logged failure back into a non-zero exit.
35
+ */
36
+ export declare function cmdPreCompact(hippoRoot: string, options: PreCompactOptions): Promise<void>;
37
+ export interface PostCompactOptions {
38
+ stdinText?: string;
39
+ logFile?: string;
40
+ afterSave?: (transcriptPath: string, originProject: string, log: (message: string) => void) => void;
41
+ }
42
+ /** PostCompact entry point: saves the summary and its items, replays earlier leftovers, returns the one line to show. Never throws, so the hook exits 0. */
43
+ export declare function cmdPostCompact(hippoRoot: string, options: PostCompactOptions): string | null;
44
+ //# sourceMappingURL=compact.d.ts.map
@@ -0,0 +1,354 @@
1
+ import * as fs from 'fs';
2
+ import * as path from 'path';
3
+ import { isInitialized } from '../store/open.js';
4
+ import { saveActiveTaskSnapshot, loadActiveTaskSnapshot } from '../store/sessions.js';
5
+ import { PRE_COMPACT_INSTRUCTION, compactionOrigin, parsePostCompactPayload, postCompactLine, recordCompactionStart, recordSnapshotSaved, saveCompaction, replayCompactionsAt, COMPACTION_DB_WAIT_MS, } from '../compaction-record.js';
6
+ import { resolveTenantId } from '../tenant.js';
7
+ import { defaultPreCompactLogPath } from '../hooks/shared.js';
8
+ import { maskEmails, redactSecretsStrict } from '../secret-detect.js';
9
+ import { isObjectLike, isStringValue, readClaudeCodePreCompact } from '../capture-contract.js';
10
+ import { errorMessage } from '../log.js';
11
+ import { PRE_COMPACT_TAIL_BYTES, readTranscriptTail, truncateCodePointSafe } from '../transcript-tail.js';
12
+ import { humanUserText, summariseTranscript, resolveLastSessionTranscript } from './transcript.js';
13
+ // ---------------------------------------------------------------------------
14
+ // `hippo pre-compact` — PreCompact hook producer
15
+ // ---------------------------------------------------------------------------
16
+ export const PRE_COMPACT_TASK_CAP = 200;
17
+ export const PRE_COMPACT_SUMMARY_CAP = 2000;
18
+ export const PRE_COMPACT_NEXT_STEP_CAP = 500;
19
+ /**
20
+ * Cap from the RECENT end: summariseTranscript emits user turns oldest-to-
21
+ * newest with assistant responses after them, so a head-first cap keeps
22
+ * stale context and drops exactly the newest working state this feature
23
+ * exists to preserve. Keep the LAST maxChars instead, aligned forward to a
24
+ * nearby line start, with a trim marker (codex round 3).
25
+ */
26
+ export function truncateKeepNewest(text, maxChars) {
27
+ if (text.length <= maxChars)
28
+ return text;
29
+ let start = text.length - maxChars;
30
+ const code = text.charCodeAt(start);
31
+ if (code >= 0xdc00 && code <= 0xdfff)
32
+ start += 1; // never start on a low surrogate
33
+ const nl = text.indexOf('\n', start);
34
+ if (nl !== -1 && nl + 1 < text.length && nl - start < 200)
35
+ start = nl + 1;
36
+ return '[...earlier turns trimmed]\n' + text.slice(start);
37
+ }
38
+ /** Most recent plain-text user message in a JSONL tail. Claude Code transcript shape only (PreCompact is claude-code-only). */
39
+ function lastPlainUserMessage(jsonl) {
40
+ const lines = jsonl.split('\n').filter((l) => l.trim());
41
+ for (let i = lines.length - 1; i >= 0; i--) {
42
+ let entry;
43
+ try {
44
+ entry = JSON.parse(lines[i]);
45
+ }
46
+ catch {
47
+ continue; // the tail read can start mid-line; skip the torn fragment
48
+ }
49
+ if (!isObjectLike(entry))
50
+ continue;
51
+ if (!('type' in entry) || entry.type !== 'user')
52
+ continue;
53
+ // Meta/sidechain lines carry type:'user' but are not the human: after a
54
+ // FIRST compaction the transcript holds the compact summary as an isMeta
55
+ // user line, and sub-agent turns are isSidechain — deriving "task" from
56
+ // either yields junk on every later compaction.
57
+ if (('isMeta' in entry && entry.isMeta === true) || ('isSidechain' in entry && entry.isSidechain === true))
58
+ continue;
59
+ const message = 'message' in entry && isObjectLike(entry.message) ? entry.message : undefined;
60
+ if (!message)
61
+ continue;
62
+ const text = humanUserText(entry, message);
63
+ if (text)
64
+ return text;
65
+ }
66
+ return '';
67
+ }
68
+ /** Last assistant text block in a JSONL tail (skips thinking + tool_use, same as summariseTranscript). */
69
+ function lastAssistantTextBlock(jsonl) {
70
+ const lines = jsonl.split('\n').filter((l) => l.trim());
71
+ for (let i = lines.length - 1; i >= 0; i--) {
72
+ let entry;
73
+ try {
74
+ entry = JSON.parse(lines[i]);
75
+ }
76
+ catch {
77
+ continue; // the tail read can start mid-line; skip the torn fragment
78
+ }
79
+ if (!isObjectLike(entry))
80
+ continue;
81
+ if (!('type' in entry) || entry.type !== 'assistant')
82
+ continue;
83
+ // Same meta/sidechain guard as lastPlainUserMessage: sub-agent turns
84
+ // (isSidechain) are not this session's next step.
85
+ if (('isMeta' in entry && entry.isMeta === true) || ('isSidechain' in entry && entry.isSidechain === true))
86
+ continue;
87
+ const message = 'message' in entry && isObjectLike(entry.message) ? entry.message : undefined;
88
+ if (!message)
89
+ continue;
90
+ const content = 'content' in message ? message.content : undefined;
91
+ if (!Array.isArray(content))
92
+ continue;
93
+ for (let j = content.length - 1; j >= 0; j--) {
94
+ const block = content[j];
95
+ if (isObjectLike(block)) {
96
+ const blockText = 'type' in block && block.type === 'text' && 'text' in block ? block.text : undefined;
97
+ if (isStringValue(blockText) && blockText.trim()) {
98
+ return blockText.trim();
99
+ }
100
+ }
101
+ }
102
+ }
103
+ return '';
104
+ }
105
+ // Diagnostic-only log; a long-lived install must not grow it unbounded.
106
+ const PRE_COMPACT_LOG_MAX_BYTES = 256 * 1024;
107
+ /**
108
+ * Log-forgery guard: messages here interpolate payload-controlled values
109
+ * (transcript paths, session ids). Strip C0 control chars — newlines above
110
+ * all — so a crafted value can't inject fake `[hippo] ...` log lines.
111
+ * Exported for every `[hippo]`-prefixed log writer that interpolates
112
+ * payload-controlled values (cli.ts appendSessionEndCloseLog) — one shared
113
+ * guard, not per-file copies.
114
+ */
115
+ export function sanitizeLogMessage(message) {
116
+ // eslint-disable-next-line no-control-regex
117
+ return message.replace(/[\x00-\x1f]/g, '');
118
+ }
119
+ function appendPreCompactLog(logFile, message) {
120
+ try {
121
+ fs.mkdirSync(path.dirname(logFile), { recursive: true });
122
+ const stat = fs.existsSync(logFile) ? fs.statSync(logFile) : null;
123
+ if (stat && stat.size > PRE_COMPACT_LOG_MAX_BYTES) {
124
+ fs.writeFileSync(logFile, '', 'utf8'); // start fresh — dumb cap, no rotation
125
+ }
126
+ fs.appendFileSync(logFile, `[hippo] ${new Date().toISOString()} ${sanitizeLogMessage(message)}\n`, 'utf8');
127
+ }
128
+ catch {
129
+ // Diagnostic-only; a log write failure must never affect the exit-0 contract.
130
+ }
131
+ }
132
+ /** True iff `filePath` exists and is readable — checks both in one call. */
133
+ function isReadableFile(filePath) {
134
+ try {
135
+ fs.accessSync(filePath, fs.constants.R_OK);
136
+ return true;
137
+ }
138
+ catch {
139
+ return false; // missing and unreadable both mean "no file" to the caller
140
+ }
141
+ }
142
+ /** PreCompact stdout is the summariser's instructions; sent before the snapshot work because a locked store can run the hook past its 30 s limit, and via writeSync because process.exit drops buffered pipe output. */
143
+ function printPreCompactInstruction(logFile) {
144
+ try {
145
+ fs.writeSync(1, `${PRE_COMPACT_INSTRUCTION}\n`);
146
+ }
147
+ catch (err) {
148
+ appendPreCompactLog(logFile, `instruction not printed: ${errorMessage(err)}`);
149
+ }
150
+ }
151
+ /** A session's task, summary and next step from its transcript tail, secrets scrubbed and capped, '' where none; null with a logged reason when nothing is derivable. */
152
+ export function transcriptWorkingState(transcriptPath, log) {
153
+ let tail = '';
154
+ let rawTask = '';
155
+ let rawNextStep = '';
156
+ try {
157
+ // Compaction fires when a big tool_result lands (CX7), so the last human and assistant turns can sit
158
+ // megabytes back: grow the window a bounded number of times until both turns are in it.
159
+ const size = fs.statSync(transcriptPath).size;
160
+ for (const cap of [PRE_COMPACT_TAIL_BYTES, PRE_COMPACT_TAIL_BYTES * 4, PRE_COMPACT_TAIL_BYTES * 16]) {
161
+ if (cap > PRE_COMPACT_TAIL_BYTES)
162
+ log(`tail window grown to ${cap} bytes (last ${rawTask ? 'assistant' : 'user'} turn is further back)`);
163
+ tail = readTranscriptTail(transcriptPath, cap);
164
+ rawTask = lastPlainUserMessage(tail);
165
+ rawNextStep = lastAssistantTextBlock(tail);
166
+ if ((rawTask && rawNextStep) || size <= cap)
167
+ break;
168
+ }
169
+ }
170
+ catch (err) {
171
+ log(`skip: could not read transcript tail: ${errorMessage(err)}`);
172
+ return null;
173
+ }
174
+ const rawSummary = summariseTranscript(tail);
175
+ if (!rawTask.trim() && !rawSummary.trim() && !rawNextStep.trim()) {
176
+ log('skip: empty summary');
177
+ return null;
178
+ }
179
+ // X9: these fields skip the capture content gate and reach a prompt, so the strict scrub runs. The caps protect the
180
+ // re-injection token budget and never split a surrogate pair (X2); `hippo snapshot save` stays uncapped.
181
+ const task = maskEmails(redactSecretsStrict(rawTask));
182
+ const summary = maskEmails(redactSecretsStrict(rawSummary));
183
+ const nextStep = maskEmails(redactSecretsStrict(rawNextStep));
184
+ return {
185
+ task: task.trim() ? truncateCodePointSafe(task, PRE_COMPACT_TASK_CAP) : '',
186
+ summary: summary.trim() ? truncateKeepNewest(summary, PRE_COMPACT_SUMMARY_CAP) : '',
187
+ next_step: nextStep.trim() ? truncateCodePointSafe(nextStep, PRE_COMPACT_NEXT_STEP_CAP) : '',
188
+ };
189
+ }
190
+ /** Runs the PreCompact producer: records the compaction, asks the summariser for memories, saves a working-state snapshot. Never extracts memories itself; SessionEnd capture owns that. */
191
+ function runPreCompact(hippoRoot, stdinText, stdinTimedOut, logFile) {
192
+ // X3: the PreCompact hook fires in every Claude Code project, including
193
+ // ones that never ran `hippo init`, so gate before any store-opening call
194
+ // (saveActiveTaskSnapshot etc. call initStore, which would create one).
195
+ if (!isInitialized(hippoRoot)) {
196
+ appendPreCompactLog(logFile, 'skip: store not initialized');
197
+ return;
198
+ }
199
+ const receipt = readClaudeCodePreCompact(stdinText, stdinTimedOut);
200
+ if (receipt.status !== 'received') {
201
+ appendPreCompactLog(logFile, `skip: ${receipt.reason}`);
202
+ return;
203
+ }
204
+ const { sessionId, transcriptPath: payloadTranscriptPath, cwd: payloadCwd, trigger: payloadTrigger } = receipt.input;
205
+ // The record is the "something saved before every compaction", so it lands even when no snapshot is derivable below.
206
+ let recordId = null;
207
+ if (sessionId !== null && sessionId !== '') {
208
+ recordId = recordCompactionStart(hippoRoot, { sessionId, trigger: payloadTrigger, cwd: payloadCwd, transcriptPath: payloadTranscriptPath }, (message) => appendPreCompactLog(logFile, message));
209
+ printPreCompactInstruction(logFile);
210
+ }
211
+ const transcriptPath = resolvePreCompactTranscript(payloadTranscriptPath, stdinText, logFile);
212
+ if (!transcriptPath)
213
+ return;
214
+ // Nothing derivable skips the write, so a user-authored active snapshot is never clobbered with junk.
215
+ const derived = transcriptWorkingState(transcriptPath, (message) => appendPreCompactLog(logFile, message));
216
+ if (!derived)
217
+ return;
218
+ saveDerivedSnapshot(hippoRoot, logFile, sessionId, recordId, derived);
219
+ }
220
+ /** The transcript to snapshot, or null after logging why there is none. */
221
+ function resolvePreCompactTranscript(payloadTranscriptPath, stdinText, logFile) {
222
+ // A payload transcript_path is EXCLUSIVE: never fall back to
223
+ // newest-transcript auto-discovery when it's missing/unreadable. That
224
+ // fallback would snapshot a DIFFERENT session's transcript under THIS
225
+ // payload's session_id — cross-session contamination with wrong linkage
226
+ // (verify-stage E2E finding, 2026-08-03). Auto-discovery only applies
227
+ // on a true manual invocation (no payload at all).
228
+ let transcriptPath;
229
+ if (payloadTranscriptPath !== null) {
230
+ if (isReadableFile(payloadTranscriptPath)) {
231
+ transcriptPath = payloadTranscriptPath;
232
+ }
233
+ else {
234
+ appendPreCompactLog(logFile, `skip: payload transcript_path unreadable: ${payloadTranscriptPath}`);
235
+ return null;
236
+ }
237
+ }
238
+ else {
239
+ transcriptPath = resolveLastSessionTranscript(undefined, stdinText, { mayScan: true });
240
+ }
241
+ if (!transcriptPath) {
242
+ appendPreCompactLog(logFile, 'skip: no transcript resolved');
243
+ return null;
244
+ }
245
+ return transcriptPath;
246
+ }
247
+ function saveDerivedSnapshot(hippoRoot, logFile, sessionId, recordId, derived) {
248
+ const tenantId = resolveTenantId({});
249
+ // Per-field merge (X1): a tool-heavy tail whose only user turns are
250
+ // tool_result arrays derives an empty task even though the summary is
251
+ // non-empty. Loading the existing snapshot first lets each field fall
252
+ // back independently instead of the whole write clobbering a
253
+ // user-authored field with blank text.
254
+ let existing = null;
255
+ try {
256
+ existing = loadActiveTaskSnapshot(hippoRoot, tenantId);
257
+ }
258
+ catch {
259
+ // No existing snapshot to merge against — proceed with derived-only.
260
+ }
261
+ // CX6 (codex round 2): field fallback must never move content across
262
+ // sessions — session A's task carried into a snapshot saved under session
263
+ // B's id would pass compact-resume's session gate wearing the wrong
264
+ // badge. Fall back only when the existing snapshot has no session, this
265
+ // payload has none, or they match.
266
+ const fallback = existing !== null &&
267
+ (existing.session_id === null || sessionId === null || existing.session_id === sessionId)
268
+ ? existing
269
+ : null;
270
+ // Carried-over fields are not re-capped, as `hippo snapshot save` stays uncapped; saveActiveTaskSnapshot scrubs every field.
271
+ const task = derived.task || (fallback?.task ?? '');
272
+ const summary = derived.summary || (fallback?.summary ?? '');
273
+ const nextStep = derived.next_step || (fallback?.next_step ?? '');
274
+ // All-empty fields (cross-session tail with nothing derivable) skip the
275
+ // write so a foreign session's junk never displaces the owning snapshot.
276
+ if (!task && !summary && !nextStep) {
277
+ appendPreCompactLog(logFile, 'skip: no snapshot content for this session (nothing derivable; fallback blocked or empty)');
278
+ }
279
+ else {
280
+ try {
281
+ saveActiveTaskSnapshot(hippoRoot, tenantId, {
282
+ task,
283
+ summary,
284
+ next_step: nextStep,
285
+ source: 'pre-compact',
286
+ session_id: sessionId,
287
+ });
288
+ appendPreCompactLog(logFile, 'snapshot saved');
289
+ if (recordId !== null)
290
+ recordSnapshotSaved(hippoRoot, recordId, (message) => appendPreCompactLog(logFile, message));
291
+ }
292
+ catch (err) {
293
+ appendPreCompactLog(logFile, `snapshot save failed: ${errorMessage(err)}`);
294
+ }
295
+ }
296
+ }
297
+ /**
298
+ * PreCompact hook entry point. Exit code 2 on PreCompact BLOCKS compaction,
299
+ * so this verb must exit 0 on every path — malformed stdin, missing
300
+ * transcript, and store errors all degrade to a logged no-op rather than a
301
+ * thrown error. Callers (src/cli.ts) must not wrap this in anything that
302
+ * could turn a caught-and-logged failure back into a non-zero exit.
303
+ */
304
+ export async function cmdPreCompact(hippoRoot, options) {
305
+ const logFile = options.logFile ?? defaultPreCompactLogPath();
306
+ try {
307
+ runPreCompact(hippoRoot, options.stdinText, options.stdinTimedOut ?? false, logFile);
308
+ }
309
+ catch (err) {
310
+ appendPreCompactLog(logFile, `pre-compact failed: ${errorMessage(err)}`);
311
+ }
312
+ process.exit(0);
313
+ }
314
+ /** A PostCompact hook has 10 s in all; replay stops starting new records after this. */
315
+ const POST_COMPACT_REPLAY_BUDGET_MS = 6000;
316
+ /** PostCompact entry point: saves the summary and its items, replays earlier leftovers, returns the one line to show. Never throws, so the hook exits 0. */
317
+ export function cmdPostCompact(hippoRoot, options) {
318
+ const logFile = options.logFile ?? defaultPreCompactLogPath();
319
+ const log = (message) => appendPreCompactLog(logFile, `post-compact: ${message}`);
320
+ const deadline = Date.now() + POST_COMPACT_REPLAY_BUDGET_MS;
321
+ try {
322
+ if (!isInitialized(hippoRoot)) {
323
+ log('skip: no hippo store');
324
+ return null;
325
+ }
326
+ const payload = parsePostCompactPayload(options.stdinText);
327
+ let line = null;
328
+ let storeBusy = false;
329
+ if (payload === null) {
330
+ log('skip: no PostCompact payload naming a session');
331
+ }
332
+ else {
333
+ const saved = saveCompaction(hippoRoot, payload, log);
334
+ line = postCompactLine(saved);
335
+ storeBusy = saved.deferred;
336
+ if (!storeBusy && payload.transcriptPath !== null && options.afterSave) {
337
+ try {
338
+ options.afterSave(payload.transcriptPath, compactionOrigin(hippoRoot, payload.cwd), log);
339
+ }
340
+ catch (err) {
341
+ log(`agent memory import failed: ${errorMessage(err)}`);
342
+ }
343
+ }
344
+ }
345
+ if (!storeBusy)
346
+ replayCompactionsAt(hippoRoot, log, { busyWaitMs: COMPACTION_DB_WAIT_MS, deadline });
347
+ return line;
348
+ }
349
+ catch (err) {
350
+ log(`failed: ${errorMessage(err)}`);
351
+ return null;
352
+ }
353
+ }
354
+ //# sourceMappingURL=compact.js.map