hippo-memory 1.60.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 (585) 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 -1262
  53. package/dist/api.js +25 -2727
  54. package/dist/audit.d.ts +3 -0
  55. package/dist/audit.js +6 -3
  56. package/dist/auth.d.ts +45 -4
  57. package/dist/auth.js +125 -48
  58. package/dist/autolearn.js +2 -2
  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 +71 -120
  123. package/dist/cli.js +170 -8469
  124. package/dist/client.js +15 -8
  125. package/dist/compaction-record.js +6 -4
  126. package/dist/connectors/github/backfill.js +94 -87
  127. package/dist/connectors/github/cli-impl.js +4 -3
  128. package/dist/connectors/github/dlq.js +67 -54
  129. package/dist/connectors/github/ingest.js +34 -35
  130. package/dist/connectors/github/tenant-routing.js +3 -2
  131. package/dist/connectors/github/webhook.js +135 -216
  132. package/dist/connectors/slack/dlq.js +49 -61
  133. package/dist/connectors/slack/ingest.js +55 -48
  134. package/dist/connectors/slack/tenant-routing.js +4 -3
  135. package/dist/connectors/slack/webhook.js +72 -74
  136. package/dist/consolidate/conflicts.d.ts +10 -0
  137. package/dist/consolidate/conflicts.js +178 -0
  138. package/dist/consolidate/decay.d.ts +12 -0
  139. package/dist/consolidate/decay.js +145 -0
  140. package/dist/consolidate/llm-passes.d.ts +3 -0
  141. package/dist/consolidate/llm-passes.js +141 -0
  142. package/dist/consolidate/merge.d.ts +7 -0
  143. package/dist/consolidate/merge.js +251 -0
  144. package/dist/consolidate/physics-pass.d.ts +3 -0
  145. package/dist/consolidate/physics-pass.js +60 -0
  146. package/dist/consolidate/run.d.ts +69 -0
  147. package/dist/consolidate/run.js +76 -0
  148. package/dist/consolidate/sleep.d.ts +18 -0
  149. package/dist/consolidate/sleep.js +209 -0
  150. package/dist/consolidate/traces.d.ts +4 -0
  151. package/dist/consolidate/traces.js +178 -0
  152. package/dist/context-auto.js +7 -11
  153. package/dist/context-render.d.ts +1 -1
  154. package/dist/context-render.js +1 -1
  155. package/dist/customer-notes.d.ts +3 -0
  156. package/dist/customer-notes.js +6 -4
  157. package/dist/dag.js +6 -5
  158. package/dist/dashboard-actions.d.ts +20 -0
  159. package/dist/dashboard-actions.js +88 -0
  160. package/dist/dashboard-params.d.ts +45 -0
  161. package/dist/dashboard-params.js +127 -0
  162. package/dist/dashboard-queries.d.ts +15 -0
  163. package/dist/dashboard-queries.js +355 -0
  164. package/dist/dashboard-snapshot.d.ts +138 -0
  165. package/dist/dashboard-snapshot.js +308 -0
  166. package/dist/dashboard-types.d.ts +163 -0
  167. package/dist/dashboard-types.js +3 -0
  168. package/dist/dashboard.d.ts +6 -7
  169. package/dist/dashboard.js +228 -202
  170. package/dist/db/busy.d.ts +5 -0
  171. package/dist/db/busy.js +24 -0
  172. package/dist/db/continuity.d.ts +5 -0
  173. package/dist/db/continuity.js +145 -0
  174. package/dist/db/meta.d.ts +8 -0
  175. package/dist/db/meta.js +35 -0
  176. package/dist/db/migrate.d.ts +9 -0
  177. package/dist/db/migrate.js +138 -0
  178. package/dist/db/migrations/index.d.ts +5 -0
  179. package/dist/db/migrations/index.js +109 -0
  180. package/dist/db/migrations/types.d.ts +14 -0
  181. package/dist/db/migrations/types.js +2 -0
  182. package/dist/db/migrations/v01.d.ts +3 -0
  183. package/dist/db/migrations/v01.js +38 -0
  184. package/dist/db/migrations/v02.d.ts +3 -0
  185. package/dist/db/migrations/v02.js +21 -0
  186. package/dist/db/migrations/v03.d.ts +3 -0
  187. package/dist/db/migrations/v03.js +22 -0
  188. package/dist/db/migrations/v04.d.ts +3 -0
  189. package/dist/db/migrations/v04.js +28 -0
  190. package/dist/db/migrations/v05.d.ts +3 -0
  191. package/dist/db/migrations/v05.js +21 -0
  192. package/dist/db/migrations/v06.d.ts +3 -0
  193. package/dist/db/migrations/v06.js +25 -0
  194. package/dist/db/migrations/v07.d.ts +3 -0
  195. package/dist/db/migrations/v07.js +13 -0
  196. package/dist/db/migrations/v08.d.ts +3 -0
  197. package/dist/db/migrations/v08.js +8 -0
  198. package/dist/db/migrations/v09.d.ts +3 -0
  199. package/dist/db/migrations/v09.js +13 -0
  200. package/dist/db/migrations/v10.d.ts +3 -0
  201. package/dist/db/migrations/v10.js +17 -0
  202. package/dist/db/migrations/v11.d.ts +3 -0
  203. package/dist/db/migrations/v11.js +15 -0
  204. package/dist/db/migrations/v12.d.ts +3 -0
  205. package/dist/db/migrations/v12.js +11 -0
  206. package/dist/db/migrations/v13.d.ts +3 -0
  207. package/dist/db/migrations/v13.js +15 -0
  208. package/dist/db/migrations/v14.d.ts +3 -0
  209. package/dist/db/migrations/v14.js +66 -0
  210. package/dist/db/migrations/v15.d.ts +3 -0
  211. package/dist/db/migrations/v15.js +42 -0
  212. package/dist/db/migrations/v16.d.ts +3 -0
  213. package/dist/db/migrations/v16.js +61 -0
  214. package/dist/db/migrations/v17.d.ts +3 -0
  215. package/dist/db/migrations/v17.js +46 -0
  216. package/dist/db/migrations/v18.d.ts +3 -0
  217. package/dist/db/migrations/v18.js +60 -0
  218. package/dist/db/migrations/v19.d.ts +3 -0
  219. package/dist/db/migrations/v19.js +28 -0
  220. package/dist/db/migrations/v20.d.ts +3 -0
  221. package/dist/db/migrations/v20.js +42 -0
  222. package/dist/db/migrations/v21.d.ts +3 -0
  223. package/dist/db/migrations/v21.js +16 -0
  224. package/dist/db/migrations/v22.d.ts +3 -0
  225. package/dist/db/migrations/v22.js +82 -0
  226. package/dist/db/migrations/v23.d.ts +3 -0
  227. package/dist/db/migrations/v23.js +48 -0
  228. package/dist/db/migrations/v24.d.ts +3 -0
  229. package/dist/db/migrations/v24.js +72 -0
  230. package/dist/db/migrations/v25.d.ts +3 -0
  231. package/dist/db/migrations/v25.js +46 -0
  232. package/dist/db/migrations/v26.d.ts +3 -0
  233. package/dist/db/migrations/v26.js +23 -0
  234. package/dist/db/migrations/v27.d.ts +3 -0
  235. package/dist/db/migrations/v27.js +57 -0
  236. package/dist/db/migrations/v28.d.ts +3 -0
  237. package/dist/db/migrations/v28.js +38 -0
  238. package/dist/db/migrations/v29.d.ts +3 -0
  239. package/dist/db/migrations/v29.js +78 -0
  240. package/dist/db/migrations/v30.d.ts +3 -0
  241. package/dist/db/migrations/v30.js +92 -0
  242. package/dist/db/migrations/v31.d.ts +3 -0
  243. package/dist/db/migrations/v31.js +74 -0
  244. package/dist/db/migrations/v32.d.ts +3 -0
  245. package/dist/db/migrations/v32.js +102 -0
  246. package/dist/db/migrations/v33.d.ts +3 -0
  247. package/dist/db/migrations/v33.js +104 -0
  248. package/dist/db/migrations/v34.d.ts +3 -0
  249. package/dist/db/migrations/v34.js +93 -0
  250. package/dist/db/migrations/v35.d.ts +3 -0
  251. package/dist/db/migrations/v35.js +98 -0
  252. package/dist/db/migrations/v36.d.ts +3 -0
  253. package/dist/db/migrations/v36.js +98 -0
  254. package/dist/db/migrations/v37.d.ts +3 -0
  255. package/dist/db/migrations/v37.js +219 -0
  256. package/dist/db/migrations/v38.d.ts +3 -0
  257. package/dist/db/migrations/v38.js +277 -0
  258. package/dist/db/migrations/v39.d.ts +3 -0
  259. package/dist/db/migrations/v39.js +59 -0
  260. package/dist/db/migrations/v40.d.ts +3 -0
  261. package/dist/db/migrations/v40.js +74 -0
  262. package/dist/db/migrations/v41.d.ts +3 -0
  263. package/dist/db/migrations/v41.js +43 -0
  264. package/dist/db/migrations/v42.d.ts +3 -0
  265. package/dist/db/migrations/v42.js +41 -0
  266. package/dist/db/migrations/v43.d.ts +3 -0
  267. package/dist/db/migrations/v43.js +67 -0
  268. package/dist/db/migrations/v44.d.ts +3 -0
  269. package/dist/db/migrations/v44.js +28 -0
  270. package/dist/db/migrations/v45.d.ts +3 -0
  271. package/dist/db/migrations/v45.js +30 -0
  272. package/dist/db/migrations/v46.d.ts +3 -0
  273. package/dist/db/migrations/v46.js +25 -0
  274. package/dist/db/migrations/v47.d.ts +3 -0
  275. package/dist/db/migrations/v47.js +17 -0
  276. package/dist/db/migrations/v48.d.ts +3 -0
  277. package/dist/db/migrations/v48.js +10 -0
  278. package/dist/db/migrations/v49.d.ts +3 -0
  279. package/dist/db/migrations/v49.js +31 -0
  280. package/dist/db/migrations/v50.d.ts +3 -0
  281. package/dist/db/migrations/v50.js +67 -0
  282. package/dist/db/migrations/v51.d.ts +3 -0
  283. package/dist/db/migrations/v51.js +14 -0
  284. package/dist/db/migrations/v52.d.ts +3 -0
  285. package/dist/db/migrations/v52.js +7 -0
  286. package/dist/db/open.d.ts +23 -0
  287. package/dist/db/open.js +146 -0
  288. package/dist/db/sqlite.d.ts +21 -0
  289. package/dist/db/sqlite.js +8 -0
  290. package/dist/db/tables.d.ts +7 -0
  291. package/dist/db/tables.js +38 -0
  292. package/dist/db.d.ts +6 -46
  293. package/dist/db.js +5 -3049
  294. package/dist/decisions.d.ts +4 -1
  295. package/dist/decisions.js +9 -7
  296. package/dist/dedupe.js +3 -2
  297. package/dist/delivery-recorder.js +4 -1
  298. package/dist/doctor.js +3 -3
  299. package/dist/embedding-provider.d.ts +1 -1
  300. package/dist/embedding-provider.js +4 -3
  301. package/dist/embeddings.d.ts +9 -52
  302. package/dist/embeddings.js +43 -297
  303. package/dist/env.d.ts +75 -0
  304. package/dist/env.js +119 -0
  305. package/dist/eval-suite.js +1 -1
  306. package/dist/eval.js +2 -2
  307. package/dist/extract.js +1 -1
  308. package/dist/gated-write.js +3 -1
  309. package/dist/goals.d.ts +3 -1
  310. package/dist/goals.js +18 -0
  311. package/dist/graph/read.d.ts +73 -0
  312. package/dist/graph/read.js +325 -0
  313. package/dist/graph/rows.d.ts +45 -0
  314. package/dist/graph/rows.js +51 -0
  315. package/dist/graph/types.d.ts +83 -0
  316. package/dist/graph/types.js +11 -0
  317. package/dist/graph/write.d.ts +93 -0
  318. package/dist/{graph.js → graph/write.js} +6 -384
  319. package/dist/graph-extract.js +2 -1
  320. package/dist/graph-recall.d.ts +1 -1
  321. package/dist/graph-recall.js +2 -2
  322. package/dist/graph-stream.js +1 -1
  323. package/dist/graph-view.d.ts +1 -1
  324. package/dist/graph-view.js +1 -1
  325. package/dist/half-life-migration.d.ts +1 -1
  326. package/dist/half-life-migration.js +2 -1
  327. package/dist/hooks/codex-session.d.ts +8 -0
  328. package/dist/hooks/codex-session.js +76 -0
  329. package/dist/hooks/codex-wrapper.d.ts +55 -0
  330. package/dist/hooks/codex-wrapper.js +288 -0
  331. package/dist/hooks/json-hooks.d.ts +63 -0
  332. package/dist/hooks/json-hooks.js +356 -0
  333. package/dist/hooks/opencode.d.ts +50 -0
  334. package/dist/hooks/opencode.js +202 -0
  335. package/dist/hooks/shared.d.ts +54 -0
  336. package/dist/hooks/shared.js +77 -0
  337. package/dist/http-retry.d.ts +2 -0
  338. package/dist/http-retry.js +4 -3
  339. package/dist/http-util.d.ts +3 -0
  340. package/dist/http-util.js +10 -0
  341. package/dist/{importers.d.ts → importers/core.d.ts} +13 -17
  342. package/dist/importers/core.js +141 -0
  343. package/dist/importers/markdown-parse.d.ts +41 -0
  344. package/dist/importers/markdown-parse.js +132 -0
  345. package/dist/importers/markdown.d.ts +3 -0
  346. package/dist/importers/markdown.js +92 -0
  347. package/dist/importers/sources.d.ts +7 -0
  348. package/dist/importers/sources.js +229 -0
  349. package/dist/importers/vault.d.ts +11 -0
  350. package/dist/importers/vault.js +352 -0
  351. package/dist/incidents.d.ts +3 -0
  352. package/dist/incidents.js +7 -5
  353. package/dist/index.d.ts +25 -6
  354. package/dist/index.js +23 -6
  355. package/dist/invalidation.js +2 -1
  356. package/dist/judgment.js +2 -1
  357. package/dist/keyset.d.ts +13 -0
  358. package/dist/keyset.js +8 -0
  359. package/dist/local-embedding.d.ts +13 -0
  360. package/dist/local-embedding.js +165 -0
  361. package/dist/log.d.ts +7 -0
  362. package/dist/log.js +19 -1
  363. package/dist/mcp/admin-tools.d.ts +8 -0
  364. package/dist/mcp/admin-tools.js +116 -0
  365. package/dist/mcp/format.d.ts +27 -0
  366. package/dist/mcp/format.js +135 -0
  367. package/dist/mcp/memory-tools.d.ts +5 -0
  368. package/dist/mcp/memory-tools.js +83 -0
  369. package/dist/mcp/protocol.d.ts +83 -0
  370. package/dist/mcp/protocol.js +55 -0
  371. package/dist/mcp/recall-tools.d.ts +6 -0
  372. package/dist/mcp/recall-tools.js +320 -0
  373. package/dist/mcp/request.d.ts +10 -0
  374. package/dist/mcp/request.js +163 -0
  375. package/dist/mcp/server.d.ts +4 -75
  376. package/dist/mcp/server.js +7 -1190
  377. package/dist/mcp/session-state.d.ts +11 -0
  378. package/dist/mcp/session-state.js +28 -0
  379. package/dist/mcp/stdio.d.ts +8 -0
  380. package/dist/mcp/stdio.js +79 -0
  381. package/dist/mcp/tools.d.ts +11 -0
  382. package/dist/mcp/tools.js +247 -0
  383. package/dist/memory.d.ts +3 -0
  384. package/dist/memory.js +27 -1
  385. package/dist/multihop.d.ts +1 -1
  386. package/dist/multihop.js +2 -1
  387. package/dist/owner-validation.js +2 -1
  388. package/dist/physics-state.js +10 -7
  389. package/dist/policies.d.ts +3 -0
  390. package/dist/policies.js +8 -6
  391. package/dist/postinstall.js +3 -2
  392. package/dist/predictions/planning-fallacy.d.ts +100 -0
  393. package/dist/predictions/planning-fallacy.js +190 -0
  394. package/dist/{predictions.d.ts → predictions/store.d.ts} +7 -102
  395. package/dist/predictions/store.js +434 -0
  396. package/dist/processes.d.ts +3 -0
  397. package/dist/processes.js +7 -5
  398. package/dist/project-briefs.d.ts +3 -0
  399. package/dist/project-briefs.js +7 -5
  400. package/dist/project-identity.js +3 -2
  401. package/dist/project-merge.js +3 -1
  402. package/dist/quarantine.d.ts +2 -1
  403. package/dist/quarantine.js +8 -5
  404. package/dist/raw-archive.js +1 -1
  405. package/dist/recall-history.js +3 -2
  406. package/dist/recall-pipeline.d.ts +2 -2
  407. package/dist/recall-pipeline.js +16 -8
  408. package/dist/recall-scope.js +1 -1
  409. package/dist/recall-trace.d.ts +1 -1
  410. package/dist/refine-llm.js +2 -1
  411. package/dist/reject-flow.js +6 -1
  412. package/dist/rerankers/clef.js +6 -5
  413. package/dist/rerankers/jev.d.ts +1 -1
  414. package/dist/rerankers/jev.js +4 -4
  415. package/dist/rerankers/llm.d.ts +4 -2
  416. package/dist/rerankers/llm.js +58 -38
  417. package/dist/rerankers/types.d.ts +1 -1
  418. package/dist/salience.js +1 -1
  419. package/dist/scheduler.d.ts +1 -0
  420. package/dist/scheduler.js +26 -4
  421. package/dist/scope.js +4 -3
  422. package/dist/search/as-of.d.ts +10 -0
  423. package/dist/search/as-of.js +22 -0
  424. package/dist/search/bm25-search.d.ts +14 -0
  425. package/dist/search/bm25-search.js +43 -0
  426. package/dist/search/bm25.d.ts +15 -0
  427. package/dist/search/bm25.js +54 -0
  428. package/dist/search/boosts.d.ts +54 -0
  429. package/dist/search/boosts.js +94 -0
  430. package/dist/search/breakdown.d.ts +7 -0
  431. package/dist/search/breakdown.js +20 -0
  432. package/dist/search/explain.d.ts +25 -0
  433. package/dist/search/explain.js +31 -0
  434. package/dist/search/finalize.d.ts +8 -0
  435. package/dist/search/finalize.js +52 -0
  436. package/dist/search/fusion.d.ts +27 -0
  437. package/dist/search/fusion.js +42 -0
  438. package/dist/search/hybrid-score.d.ts +20 -0
  439. package/dist/search/hybrid-score.js +73 -0
  440. package/dist/search/hybrid.d.ts +46 -0
  441. package/dist/search/hybrid.js +64 -0
  442. package/dist/search/physics-search.d.ts +29 -0
  443. package/dist/search/physics-search.js +162 -0
  444. package/dist/search/rerank.d.ts +10 -0
  445. package/dist/search/rerank.js +72 -0
  446. package/dist/search/temporal.d.ts +15 -0
  447. package/dist/search/temporal.js +45 -0
  448. package/dist/search/types.d.ts +91 -0
  449. package/dist/search/types.js +2 -0
  450. package/dist/search/vector.d.ts +30 -0
  451. package/dist/search/vector.js +71 -0
  452. package/dist/secret-detect.d.ts +2 -0
  453. package/dist/secret-detect.js +2 -1
  454. package/dist/server/auth.d.ts +52 -0
  455. package/dist/server/auth.js +221 -0
  456. package/dist/server/client-ip.d.ts +25 -0
  457. package/dist/server/client-ip.js +92 -0
  458. package/dist/server/cursor.d.ts +23 -0
  459. package/dist/server/cursor.js +58 -0
  460. package/dist/server/lifecycle.d.ts +7 -0
  461. package/dist/server/lifecycle.js +29 -0
  462. package/dist/server/mcp-http.d.ts +5 -0
  463. package/dist/server/mcp-http.js +199 -0
  464. package/dist/server/request.d.ts +33 -0
  465. package/dist/server/request.js +103 -0
  466. package/dist/server/routes/admin.d.ts +9 -0
  467. package/dist/server/routes/admin.js +158 -0
  468. package/dist/server/routes/customer-notes.d.ts +7 -0
  469. package/dist/server/routes/customer-notes.js +112 -0
  470. package/dist/server/routes/decisions.d.ts +7 -0
  471. package/dist/server/routes/decisions.js +133 -0
  472. package/dist/server/routes/incidents.d.ts +7 -0
  473. package/dist/server/routes/incidents.js +126 -0
  474. package/dist/server/routes/memories.d.ts +10 -0
  475. package/dist/server/routes/memories.js +179 -0
  476. package/dist/server/routes/policies.d.ts +8 -0
  477. package/dist/server/routes/policies.js +151 -0
  478. package/dist/server/routes/predictions.d.ts +7 -0
  479. package/dist/server/routes/predictions.js +164 -0
  480. package/dist/server/routes/processes.d.ts +7 -0
  481. package/dist/server/routes/processes.js +161 -0
  482. package/dist/server/routes/project-briefs.d.ts +8 -0
  483. package/dist/server/routes/project-briefs.js +136 -0
  484. package/dist/server/routes/recall.d.ts +8 -0
  485. package/dist/server/routes/recall.js +340 -0
  486. package/dist/server/routes/skills.d.ts +8 -0
  487. package/dist/server/routes/skills.js +150 -0
  488. package/dist/server/types.d.ts +56 -0
  489. package/dist/server/types.js +2 -0
  490. package/dist/server/validation.d.ts +14 -0
  491. package/dist/server/validation.js +91 -0
  492. package/dist/server.d.ts +7 -61
  493. package/dist/server.js +69 -2362
  494. package/dist/session-digest.d.ts +1 -1
  495. package/dist/session-digest.js +9 -2
  496. package/dist/shared.d.ts +3 -1
  497. package/dist/shared.js +26 -22
  498. package/dist/skills.d.ts +3 -0
  499. package/dist/skills.js +7 -5
  500. package/dist/stdin.js +2 -2
  501. package/dist/store/audit-event.d.ts +19 -0
  502. package/dist/store/audit-event.js +33 -0
  503. package/dist/store/candidates.d.ts +42 -0
  504. package/dist/store/candidates.js +152 -0
  505. package/dist/store/conflicts.d.ts +44 -0
  506. package/dist/store/conflicts.js +444 -0
  507. package/dist/store/delete-and-batch.d.ts +63 -0
  508. package/dist/store/delete-and-batch.js +310 -0
  509. package/dist/store/entry-reads.d.ts +92 -0
  510. package/dist/store/entry-reads.js +255 -0
  511. package/dist/store/entry-row.d.ts +67 -0
  512. package/dist/store/entry-row.js +208 -0
  513. package/dist/store/entry-writes.d.ts +46 -0
  514. package/dist/store/entry-writes.js +148 -0
  515. package/dist/store/handoffs.d.ts +26 -0
  516. package/dist/store/handoffs.js +184 -0
  517. package/dist/store/index-and-stats.d.ts +52 -0
  518. package/dist/store/index-and-stats.js +214 -0
  519. package/dist/store/markdown.d.ts +10 -0
  520. package/dist/store/markdown.js +108 -0
  521. package/dist/store/mirrors.d.ts +49 -0
  522. package/dist/store/mirrors.js +312 -0
  523. package/dist/store/open.d.ts +15 -0
  524. package/dist/store/open.js +223 -0
  525. package/dist/store/rows.d.ts +178 -0
  526. package/dist/store/rows.js +158 -0
  527. package/dist/store/search-rows.d.ts +86 -0
  528. package/dist/store/search-rows.js +254 -0
  529. package/dist/store/sessions.d.ts +83 -0
  530. package/dist/store/sessions.js +272 -0
  531. package/dist/store/summaries.d.ts +94 -0
  532. package/dist/store/summaries.js +377 -0
  533. package/dist/store/tenant-lookup.d.ts +12 -0
  534. package/dist/store/tenant-lookup.js +16 -0
  535. package/dist/store-cards.js +2 -1
  536. package/dist/summary-dirty.d.ts +4 -0
  537. package/dist/summary-dirty.js +32 -0
  538. package/dist/support-bundle.js +4 -3
  539. package/dist/tenant.js +2 -2
  540. package/dist/tokenize.d.ts +2 -0
  541. package/dist/tokenize.js +16 -0
  542. package/dist/transcript-tail.d.ts +7 -0
  543. package/dist/transcript-tail.js +48 -0
  544. package/dist/vector-store.d.ts +27 -0
  545. package/dist/vector-store.js +210 -0
  546. package/dist/version.d.ts +2 -2
  547. package/dist/version.js +2 -2
  548. package/dist/working-memory.js +1 -1
  549. package/dist/yaml.js +36 -11
  550. package/dist-ui/assets/ibm-plex-mono-latin-400-normal-CvHOgSBP.woff +0 -0
  551. package/dist-ui/assets/ibm-plex-mono-latin-400-normal-DMJ8VG8y.woff2 +0 -0
  552. package/dist-ui/assets/ibm-plex-mono-latin-500-normal-CB9ihrfo.woff +0 -0
  553. package/dist-ui/assets/ibm-plex-mono-latin-500-normal-DSY6xOcd.woff2 +0 -0
  554. package/dist-ui/assets/ibm-plex-mono-latin-600-normal-BgSNZQsw.woff2 +0 -0
  555. package/dist-ui/assets/ibm-plex-mono-latin-600-normal-DWFSQ4vo.woff +0 -0
  556. package/dist-ui/assets/ibm-plex-sans-latin-400-normal-CDDApCn2.woff2 +0 -0
  557. package/dist-ui/assets/ibm-plex-sans-latin-400-normal-CYLoc0-x.woff +0 -0
  558. package/dist-ui/assets/ibm-plex-sans-latin-500-normal-6ng42L7E.woff2 +0 -0
  559. package/dist-ui/assets/ibm-plex-sans-latin-500-normal-BgVn5rGT.woff +0 -0
  560. package/dist-ui/assets/ibm-plex-sans-latin-600-normal-Cu4Hd6ag.woff +0 -0
  561. package/dist-ui/assets/ibm-plex-sans-latin-600-normal-CuJfVYMP.woff2 +0 -0
  562. package/dist-ui/assets/index-DPN7cP19.js +33 -0
  563. package/dist-ui/assets/index-dFloKRVr.css +1 -0
  564. package/dist-ui/index.html +3 -25
  565. package/extensions/openclaw-plugin/openclaw.plugin.json +1 -1
  566. package/extensions/openclaw-plugin/package.json +1 -1
  567. package/openclaw.plugin.json +1 -1
  568. package/package.json +1 -1
  569. package/dist/capture.d.ts +0 -155
  570. package/dist/capture.js +0 -1295
  571. package/dist/consolidate.d.ts +0 -58
  572. package/dist/consolidate.js +0 -1124
  573. package/dist/graph.d.ts +0 -245
  574. package/dist/hooks.d.ts +0 -208
  575. package/dist/hooks.js +0 -1076
  576. package/dist/importers.js +0 -900
  577. package/dist/predictions.js +0 -620
  578. package/dist/search.d.ts +0 -320
  579. package/dist/search.js +0 -970
  580. package/dist/store.d.ts +0 -776
  581. package/dist/store.js +0 -3473
  582. package/dist-ui/assets/d3-BiWEKnn4.js +0 -1
  583. package/dist-ui/assets/index-BhT8RvO6.js +0 -61
  584. package/dist-ui/assets/index-RoXXJ5dq.css +0 -1
  585. package/dist-ui/assets/three-BDgTxR1l.js +0 -4112
@@ -0,0 +1,254 @@
1
+ import { envForceLikePath } from '../env.js';
2
+ import { isFtsAvailable, closeHippoDb } from '../db.js';
3
+ import { tokenize } from '../tokenize.js';
4
+ import { RECALL_DEFAULT_DENY_SCOPES } from '../recall-scope.js';
5
+ import { RAREST_TERM_COUNT, rarestPromptTerms } from '../prompt-recall.js';
6
+ import { log } from '../log.js';
7
+ import { topVectorMatches } from '../vector-store.js';
8
+ import { MEMORY_SELECT_COLUMNS, MEMORY_SEARCH_COLUMNS, DEFAULT_SEARCH_CANDIDATE_LIMIT, rowToEntry, } from './rows.js';
9
+ import { openStore } from './open.js';
10
+ /** The recall scope rule for a table column prefix (`m.` or none); `passesScopeFilterForRecall` in recall-scope.ts is its JS twin. */
11
+ function recallScopeClause(col, scopeFilter) {
12
+ if (scopeFilter === undefined)
13
+ return { sql: '', params: [] };
14
+ if (scopeFilter.mode === 'exact')
15
+ return { sql: ` AND ${col}scope = ?`, params: [scopeFilter.value] };
16
+ // `IS NULL OR` admits NULL scopes, which NOT IN alone drops. NOT LIKE denies a superset of private scopes before the
17
+ // window cut so private rows cannot starve admitted ones; the anchored JS regex stays the exact post-filter.
18
+ const placeholders = RECALL_DEFAULT_DENY_SCOPES.map(() => '?').join(', ');
19
+ const admitted = `${col}scope IS NULL OR (${col}scope NOT IN (${placeholders}) AND ${col}scope NOT LIKE '%:private:%')`;
20
+ if (scopeFilter.mode === 'default-deny')
21
+ return { sql: ` AND (${admitted})`, params: [...RECALL_DEFAULT_DENY_SCOPES] };
22
+ // The trailing arm keeps a deliberately requested scope loadable, private or quarantined included.
23
+ return { sql: ` AND (${admitted} OR ${col}scope = ?)`, params: [...RECALL_DEFAULT_DENY_SCOPES, scopeFilter.value] };
24
+ }
25
+ /** Scope rule for recall: none requested is default-deny; 'exact' narrows to the request; 'additive' adds it to the default set. */
26
+ export function recallScopeFilter(requestedScope, mode) {
27
+ if (!requestedScope)
28
+ return { mode: 'default-deny' };
29
+ return mode === 'additive' ? { mode: 'default-deny-or-exact', value: requestedScope } : { mode: 'exact', value: requestedScope };
30
+ }
31
+ const FTS_QUERY_SYNTAX_RE = /fts5: syntax error|unterminated string/i;
32
+ function loadSearchRows(db, query, limit, tenantId, scopeFilter, includeSuperseded = true) {
33
+ const p = searchPredicates(tenantId, scopeFilter, includeSuperseded);
34
+ const terms = Array.from(new Set(tokenize(query)));
35
+ if (terms.length === 0) {
36
+ // F3 (v1.7.0) self-review: empty-query path is the second uncapped
37
+ // path (codex diff-pass caught the full-store fallback at the bottom;
38
+ // this no-terms path had the same shape). Apply LIMIT so all four
39
+ // candidate paths honour the caller's cap when set.
40
+ return selectAllCandidates(db, p, limit);
41
+ }
42
+ // v1.7.1 — test/diagnostic hook: `HIPPO_FORCE_LIKE_PATH=1` forces the
43
+ // LIKE-fallback path here only. Gated at the read-call site so writes
44
+ // (`syncFtsRow`, `deleteFtsRow`, `raw-archive.ts::archiveRaw`) keep using
45
+ // `isFtsAvailable` honestly and never silently skip FTS index sync.
46
+ // Lets tests exercise the LIKE branch deterministically without
47
+ // poisoning the on-disk FTS state.
48
+ const forceLikePath = envForceLikePath();
49
+ if (!forceLikePath && isFtsAvailable(db)) {
50
+ const rows = selectFtsCandidates(db, terms, p, limit);
51
+ if (rows.length > 0)
52
+ return rows;
53
+ }
54
+ const rows = selectLikeCandidates(db, terms, p, limit);
55
+ if (rows.length > 0)
56
+ return rows;
57
+ // F3 (v1.7.0) codex P1: pre-v1.7.0 the full-store fallback ignored
58
+ // `limit` and could return the whole tenant store. With scorerWindow
59
+ // now reported on RecallResult, an unbounded fallback would lie about
60
+ // candidate-pool size. Apply LIMIT here so all four paths honour the
61
+ // caller's cap.
62
+ return selectAllCandidates(db, p, limit);
63
+ }
64
+ function searchPredicates(tenantId, scopeFilter, includeSuperseded) {
65
+ // tenantId undefined = no tenant filter (legacy callers / cross-deployment
66
+ // helpers). tenantId set = strict tenant isolation, leveraging the composite
67
+ // idx_memories_tenant_created (leading column tenant_id, O(log n) lookup).
68
+ const tenantPredicate = tenantId !== undefined ? ` AND m.tenant_id = ?` : '';
69
+ const tenantPredicateNoAlias = tenantId !== undefined ? ` AND tenant_id = ?` : '';
70
+ const tenantOnlyPredicate = tenantId !== undefined ? ` WHERE tenant_id = ?` : '';
71
+ const tenantParams = tenantId !== undefined ? [tenantId] : [];
72
+ // v1.12.6 — belt-and-suspenders against `kind='archived'` leaking into recall.
73
+ // `kind='archived'` is a transient sentinel inside `archiveRawMemory`'s
74
+ // SAVEPOINT (src/raw-archive.ts:56): UPDATE kind = 'archived' immediately
75
+ // followed by DELETE, both inside one savepoint that commits or rolls back
76
+ // atomically. SQLite atomicity guarantees no concurrent reader sees the
77
+ // intermediate state. This filter is defensive-only against:
78
+ // (a) future bugs that drop the SAVEPOINT,
79
+ // (b) future bugs that introduce kind='archived' as a persisted state,
80
+ // (c) external direct-SQL writes that bypass archiveRawMemory.
81
+ // tenantOnlyPredicate starts with " WHERE tenant_id = ?" when tenant is set;
82
+ // when unset, we have no WHERE yet, so the archived clause needs both AND
83
+ // and WHERE forms. The "tenant-only" path always has WHERE (from tenant or
84
+ // we synthesize one).
85
+ const archivedClauseAlias = ` AND m.kind != 'archived'`;
86
+ const archivedClauseNoAlias = ` AND kind != 'archived'`;
87
+ // For the "tenant-only" path: if no tenant set, tenantOnlyPredicate is '',
88
+ // so prepend WHERE; if tenant set, append AND. handled in each call site
89
+ // by always joining `tenantOnlyPredicate + archivedClauseTenantOnly` where
90
+ // the latter switches between " AND" and " WHERE" based on caller context.
91
+ const archivedClauseTenantOnly = tenantId !== undefined ? ` AND kind != 'archived'` : ` WHERE kind != 'archived'`;
92
+ const aliasScope = recallScopeClause('m.', scopeFilter);
93
+ const plainScope = recallScopeClause('', scopeFilter);
94
+ const scopeParams = aliasScope.params;
95
+ const currentAlias = includeSuperseded ? '' : ' AND m.superseded_by IS NULL';
96
+ const currentNoAlias = includeSuperseded ? '' : ' AND superseded_by IS NULL';
97
+ return {
98
+ tenantPredicate, tenantPredicateNoAlias, tenantOnlyPredicate, tenantParams,
99
+ archivedClauseAlias, archivedClauseNoAlias, archivedClauseTenantOnly,
100
+ aliasScope, plainScope, scopeParams, currentAlias, currentNoAlias,
101
+ };
102
+ }
103
+ /** Every admitted row, oldest first; the no-terms path and the last-resort fallback share it. */
104
+ function selectAllCandidates(db, p, limit) {
105
+ const sql = `SELECT ${MEMORY_SELECT_COLUMNS} FROM memories${p.tenantOnlyPredicate}${p.archivedClauseTenantOnly}${p.plainScope.sql}${p.currentNoAlias} ORDER BY created ASC, id ASC LIMIT ?`;
106
+ // SAFETY: sql selects exactly MEMORY_SELECT_COLUMNS, whose column list
107
+ // matches MemoryRow's field set.
108
+ return db.prepare(sql).all(...p.tenantParams, ...p.scopeParams, limit);
109
+ }
110
+ /** FTS5 bm25 matches; empty when none match or FTS5 cannot run the query. */
111
+ function selectFtsCandidates(db, terms, p, limit) {
112
+ try {
113
+ const ftsQuery = terms.map((t) => `"${t.replace(/"/g, '""')}"`).join(' OR ');
114
+ // memories_fts virtual table has no tenant_id column; filter via the
115
+ // joined memories row (cheap with idx_memories_tenant_created leading
116
+ // on tenant_id).
117
+ // F1 (v1.7.0): MEMORY_SEARCH_COLUMNS adds bm25_score as the trailing
118
+ // result column. Every other column is m.<col> AS <col> so rowToEntry
119
+ // sees the same shape it always has.
120
+ // SAFETY: MEMORY_SEARCH_COLUMNS aliases every column to the same name
121
+ // MEMORY_SELECT_COLUMNS uses (plus bm25_score), matching MemoryRow.
122
+ return db.prepare(`
123
+ SELECT ${MEMORY_SEARCH_COLUMNS}
124
+ FROM memories m
125
+ JOIN memories_fts f ON f.id = m.id
126
+ WHERE memories_fts MATCH ?${p.tenantPredicate}${p.archivedClauseAlias}${p.aliasScope.sql}${p.currentAlias}
127
+ ORDER BY bm25(memories_fts), m.updated_at DESC, m.content ASC, m.id ASC
128
+ LIMIT ?
129
+ `).all(ftsQuery, ...p.tenantParams, ...p.scopeParams, limit);
130
+ }
131
+ catch (err) {
132
+ // A query FTS5 cannot parse is expected input; anything else means the index itself is broken.
133
+ const message = err instanceof Error ? err.message : String(err);
134
+ if (!FTS_QUERY_SYNTAX_RE.test(message))
135
+ log.once('fts-match-fallback', 'warn', `FTS search failed, using the slower LIKE match: ${message}`);
136
+ return [];
137
+ }
138
+ }
139
+ function selectLikeCandidates(db, terms, p, limit) {
140
+ const escapeLike = (term) => term.replace(/[%_\\]/g, '\\$&');
141
+ const where = terms.map(() => `(LOWER(content) LIKE ? ESCAPE '\\' OR LOWER(tags_json) LIKE ? ESCAPE '\\')`).join(' OR ');
142
+ const params = terms.flatMap((term) => {
143
+ const like = `%${escapeLike(term)}%`;
144
+ return [like, like];
145
+ });
146
+ // SAFETY: this query selects exactly MEMORY_SELECT_COLUMNS, matching
147
+ // MemoryRow's field set.
148
+ return db.prepare(`
149
+ SELECT ${MEMORY_SELECT_COLUMNS}
150
+ FROM memories
151
+ WHERE (${where})${p.tenantPredicateNoAlias}${p.archivedClauseNoAlias}${p.plainScope.sql}${p.currentNoAlias}
152
+ ORDER BY updated_at DESC, created DESC, content ASC, id ASC
153
+ LIMIT ?
154
+ `).all(...params, ...p.tenantParams, ...p.scopeParams, limit);
155
+ }
156
+ /**
157
+ * Load likely search candidates directly from SQLite.
158
+ * Uses FTS5 when available, falls back to LIKE matching, then full-store fallback.
159
+ *
160
+ * When `tenantId` is provided, every SELECT (FTS join, LIKE, fallback) filters
161
+ * by tenant_id. Cross-tenant memories never surface. Omitted = no filter.
162
+ */
163
+ export function loadSearchEntries(hippoRoot, query, limit = DEFAULT_SEARCH_CANDIDATE_LIMIT, tenantId) {
164
+ const db = openStore(hippoRoot);
165
+ try {
166
+ return loadSearchRows(db, query, limit, tenantId).map(rowToEntry);
167
+ }
168
+ finally {
169
+ closeHippoDb(db);
170
+ }
171
+ }
172
+ /**
173
+ * v1.7.1 — recall-mode loader. Pushes the recall-side scope predicate into
174
+ * SQL so `unknown:legacy` cannot leak via any consumer that hasn't remembered
175
+ * to re-filter (root-cause-over-patches: codex flagged this on v1.6.5 review).
176
+ *
177
+ * - `requestedScope` undefined / '': default-deny on `unknown:legacy`.
178
+ * - `requestedScope` non-empty string: exact match on `m.scope = requestedScope`.
179
+ *
180
+ * Private-scope (`<source>:private:*`) exclusion: SQL applies a conservative
181
+ * pre-window approximation (`NOT LIKE '%:private:%'`, v1.25.0 — codex P2:
182
+ * post-window-only filtering let private rows starve admitted candidates out
183
+ * of the LIMIT window); the exact anchored regex
184
+ * (`passesScopeFilterForRecall`) remains the authoritative JS post-filter in
185
+ * the recall consumers.
186
+ *
187
+ * Consumers: `api.recall` (v1.7.1+), `cmdRecall`/`cmdExplain` direct CLI paths
188
+ * and `searchBothHybrid` recall mode (v1.25.0). Background pipelines
189
+ * (`consolidate`, `embeddings`, `refine-llm`, ...) keep using
190
+ * `loadSearchEntries` so they can see quarantined rows when needed.
191
+ *
192
+ * `tenantId` widened to optional in v1.25.0 for the searchBothHybrid recall
193
+ * mode (its `tenantId` option is optional); `loadSearchRows` already treats
194
+ * undefined as "no tenant filter" for legacy callers.
195
+ */
196
+ export function loadRecallSearchEntries(hippoRoot, query, limit = DEFAULT_SEARCH_CANDIDATE_LIMIT, tenantId, requestedScope, explicitScopeMode = 'exact', includeSuperseded = true) {
197
+ const db = openStore(hippoRoot);
198
+ try {
199
+ return loadRecallSearchEntriesFromDb(db, query, limit, tenantId, requestedScope, explicitScopeMode, includeSuperseded);
200
+ }
201
+ finally {
202
+ closeHippoDb(db);
203
+ }
204
+ }
205
+ // Split out so callers with an already-open db (Z1 prompt-recall path) skip
206
+ // the initStore+open/close cycle per store per call.
207
+ export function loadRecallSearchEntriesFromDb(db, query, limit = DEFAULT_SEARCH_CANDIDATE_LIMIT, tenantId, requestedScope, explicitScopeMode = 'exact', includeSuperseded = true) {
208
+ return loadSearchRows(db, query, limit, tenantId, recallScopeFilter(requestedScope, explicitScopeMode), includeSuperseded).map(rowToEntry);
209
+ }
210
+ /** The rows nearest `queryVector` that pass `spec`, nearest first. */
211
+ export function loadVectorCandidateEntries(hippoRoot, queryVector, spec) {
212
+ const scope = recallScopeClause('m.', spec.scope);
213
+ const tenant = spec.tenantId !== undefined ? ' AND m.tenant_id = ?' : '';
214
+ const current = spec.includeSuperseded ? '' : ' AND m.superseded_by IS NULL';
215
+ const params = [...(spec.tenantId !== undefined ? [spec.tenantId] : []), ...scope.params];
216
+ const db = openStore(hippoRoot);
217
+ try {
218
+ const matches = topVectorMatches(db, queryVector, spec.limit ?? 50, `${tenant} AND m.kind != 'archived'${scope.sql}${current}`, params);
219
+ if (matches.length === 0)
220
+ return [];
221
+ // SAFETY: the SELECT names exactly MEMORY_SELECT_COLUMNS, matching MemoryRow's field set.
222
+ const rows = db.prepare(`SELECT ${MEMORY_SELECT_COLUMNS} FROM memories WHERE id IN (${matches.map(() => '?').join(', ')})`)
223
+ .all(...matches.map((m) => m.id));
224
+ const byId = new Map(rows.map((r) => [r.id, rowToEntry(r)]));
225
+ return matches.flatMap((m) => byId.get(m.id) ?? []);
226
+ }
227
+ finally {
228
+ closeHippoDb(db);
229
+ }
230
+ }
231
+ /** Rarest-K prompt terms for this connection's FTS index, as a space-joined query string.
232
+ * Without FTS, returns the first 32 terms, as before rarest-term selection. */
233
+ export function pickRarestFtsQuery(db, terms, maxTerms = RAREST_TERM_COUNT) {
234
+ // The LIKE path has no bm25 ranking to bound, so it keeps the pre-rarest 32-term query.
235
+ if (!isFtsAvailable(db))
236
+ return terms.slice(0, 32).join(' ');
237
+ db.exec(`CREATE VIRTUAL TABLE IF NOT EXISTS temp.z1_rarest_vocab USING fts5vocab(main, 'memories_fts', 'row')`);
238
+ // unicode61 splits `journal_mode` into two vocab terms; a term's count is its rarest part's (an upper bound).
239
+ const partsOf = (t) => t.split(/[^\p{L}\p{N}]+/u).filter(Boolean);
240
+ const vocab = Array.from(new Set(terms.flatMap(partsOf)));
241
+ if (vocab.length === 0)
242
+ return '';
243
+ // SAFETY: rows' shape matches the two columns named in the SELECT.
244
+ const rows = db
245
+ .prepare(`SELECT term, doc FROM temp.z1_rarest_vocab WHERE term IN (${vocab.map(() => '?').join(', ')})`)
246
+ .all(...vocab);
247
+ const counts = new Map(rows.map((r) => [r.term, r.doc]));
248
+ const docCount = (t) => {
249
+ const parts = partsOf(t);
250
+ return parts.length === 0 ? 0 : Math.min(...parts.map((x) => counts.get(x) ?? 0));
251
+ };
252
+ return rarestPromptTerms(terms, docCount, maxTerms).join(' ');
253
+ }
254
+ //# sourceMappingURL=search-rows.js.map
@@ -0,0 +1,83 @@
1
+ import { type TaskSnapshot, type SessionEvent } from './rows.js';
2
+ export declare function saveActiveTaskSnapshot(hippoRoot: string, tenantId: string, snapshot: {
3
+ task: string;
4
+ summary: string;
5
+ next_step: string;
6
+ source?: string;
7
+ session_id?: string | null;
8
+ scope?: string | null;
9
+ }): TaskSnapshot;
10
+ export declare function loadActiveTaskSnapshot(hippoRoot: string, tenantId: string): TaskSnapshot | null;
11
+ /**
12
+ * Default freshness bound for AMBIENT active-task-snapshot reads (DF1,
13
+ * docs/plans/2026-08-23-df1-snapshot-lifecycle.md): 72h, chosen over 48h so
14
+ * a Friday-evening orphan still offers continuity on Monday morning.
15
+ * Exported so callers can override via `loadFreshActiveTaskSnapshot`'s
16
+ * `opts.maxAgeMs`; deliberately no env knob (Simplicity First).
17
+ */
18
+ export declare const SNAPSHOT_AMBIENT_MAX_AGE_MS: number;
19
+ /**
20
+ * Bounded read for AMBIENT active-task-snapshot surfaces (UserPromptSubmit
21
+ * hook context, MCP recall block) — the never-expires fix for DF1. A
22
+ * snapshot written by `hippo pre-compact` has no death path tied to the
23
+ * session that owns it, so an orphaned row would otherwise inject into
24
+ * every prompt of every later session forever. Wraps `loadActiveTaskSnapshot`
25
+ * (unchanged, still the source of truth for explicit continuity surfaces),
26
+ * then applies, in order:
27
+ *
28
+ * 1. Owner match — ONLY when both `opts.sessionId` and the snapshot's
29
+ * `session_id` are non-null, non-empty strings and strictly equal
30
+ * (`===`). Owner reads are unbounded: the session that owns the snapshot
31
+ * can always see its own working state, regardless of age.
32
+ * 2. Age check — everything else, including absent-vs-absent ids. A
33
+ * null/undefined/empty id on EITHER side never counts as an owner match;
34
+ * it falls through here instead. (`runPreCompact` can legitimately save a
35
+ * snapshot with `session_id = null`; a null-equals-null "match" would
36
+ * reopen indefinite ambient injection for exactly those rows.) Returns
37
+ * the snapshot only when `age(updated_at) <= maxAgeMs` (default
38
+ * `SNAPSHOT_AMBIENT_MAX_AGE_MS`); otherwise null.
39
+ *
40
+ * No SQL change — age derives from the existing `updated_at` column.
41
+ */
42
+ export declare function loadFreshActiveTaskSnapshot(hippoRoot: string, tenantId: string, opts?: {
43
+ maxAgeMs?: number;
44
+ sessionId?: string | null;
45
+ }): TaskSnapshot | null;
46
+ export declare function clearActiveTaskSnapshot(hippoRoot: string, tenantId: string, clearedStatus?: string): boolean;
47
+ /**
48
+ * Close the `active` task snapshot(s) owned by `sessionId`, for the T3
49
+ * session-end death path (DF1, docs/plans/2026-08-23-df1-snapshot-lifecycle.md).
50
+ * Only one `active` row exists per tenant in practice (supersession happens
51
+ * at save), but the WHERE clause scopes on `session_id` too — not just
52
+ * `status='active' AND tenant_id=?` — so an ending session can never close a
53
+ * different, newer session's active snapshot. Returns the number of rows
54
+ * closed (0 when no active row is owned by `sessionId`).
55
+ */
56
+ export declare function closeTaskSnapshotsForSession(hippoRoot: string, tenantId: string, sessionId: string, status?: string): number;
57
+ export declare function appendSessionEvent(hippoRoot: string, tenantId: string, event: {
58
+ session_id: string;
59
+ event_type: string;
60
+ content: string;
61
+ task?: string | null;
62
+ source?: string;
63
+ scope?: string | null;
64
+ metadata?: Record<string, unknown>;
65
+ }): SessionEvent;
66
+ export declare function listSessionEvents(hippoRoot: string, tenantId: string, options?: {
67
+ session_id?: string;
68
+ task?: string;
69
+ limit?: number;
70
+ }): SessionEvent[];
71
+ /**
72
+ * Return session_ids with a `session_complete` event newer than `sinceMs`.
73
+ * Used by the sleep auto-promotion pass to bound scanning to a fixed window.
74
+ */
75
+ export declare function findPromotableSessions(hippoRoot: string, tenantId: string, sinceMs: number): Array<{
76
+ session_id: string;
77
+ }>;
78
+ /**
79
+ * Idempotency guard — true if a trace-layer memory with this source_session_id
80
+ * already exists.
81
+ */
82
+ export declare function traceExistsForSession(hippoRoot: string, tenantId: string, session_id: string): boolean;
83
+ //# sourceMappingURL=sessions.d.ts.map
@@ -0,0 +1,272 @@
1
+ import { closeHippoDb } from '../db.js';
2
+ import { assertTenantId } from '../tenant.js';
3
+ import { redactSecretsStrict } from '../secret-detect.js';
4
+ import { rowToTaskSnapshot, rowToSessionEvent, } from './rows.js';
5
+ import { writeActiveTaskMirror, removeActiveTaskMirror, writeRecentSessionMirror } from './mirrors.js';
6
+ import { openStore } from './open.js';
7
+ export function saveActiveTaskSnapshot(hippoRoot, tenantId, snapshot) {
8
+ assertTenantId('saveActiveTaskSnapshot', tenantId);
9
+ const db = openStore(hippoRoot);
10
+ const now = new Date().toISOString();
11
+ try {
12
+ db.exec('BEGIN');
13
+ db.prepare(`UPDATE task_snapshots SET status = 'superseded', updated_at = ? WHERE status = 'active' AND tenant_id = ?`).run(now, tenantId);
14
+ const result = db.prepare(`
15
+ INSERT INTO task_snapshots(task, summary, next_step, status, source, session_id, scope, tenant_id, created_at, updated_at)
16
+ VALUES (?, ?, ?, 'active', ?, ?, ?, ?, ?, ?)
17
+ `).run(redactSecretsStrict(snapshot.task), redactSecretsStrict(snapshot.summary), redactSecretsStrict(snapshot.next_step), snapshot.source ?? 'cli', snapshot.session_id ?? null, snapshot.scope ?? null, tenantId, now, now);
18
+ db.exec('COMMIT');
19
+ const id = Number(result.lastInsertRowid ?? 0);
20
+ // SAFETY: row's shape matches the ten columns named in the SELECT above.
21
+ const row = db.prepare(`
22
+ SELECT id, task, summary, next_step, status, source, session_id, scope, created_at, updated_at
23
+ FROM task_snapshots
24
+ WHERE id = ?
25
+ `).get(id);
26
+ if (!row) {
27
+ throw new Error('Failed to reload saved active task snapshot');
28
+ }
29
+ const loaded = rowToTaskSnapshot(row);
30
+ writeActiveTaskMirror(hippoRoot, tenantId, loaded);
31
+ return loaded;
32
+ }
33
+ catch (error) {
34
+ try {
35
+ db.exec('ROLLBACK');
36
+ }
37
+ catch {
38
+ // Ignore nested rollback failures.
39
+ }
40
+ throw error;
41
+ }
42
+ finally {
43
+ closeHippoDb(db);
44
+ }
45
+ }
46
+ export function loadActiveTaskSnapshot(hippoRoot, tenantId) {
47
+ assertTenantId('loadActiveTaskSnapshot', tenantId);
48
+ const db = openStore(hippoRoot);
49
+ try {
50
+ // SAFETY: row's shape matches the ten columns named in the SELECT above.
51
+ const row = db.prepare(`
52
+ SELECT id, task, summary, next_step, status, source, session_id, scope, created_at, updated_at
53
+ FROM task_snapshots
54
+ WHERE status = 'active' AND tenant_id = ?
55
+ ORDER BY updated_at DESC, id DESC
56
+ LIMIT 1
57
+ `).get(tenantId);
58
+ if (!row) {
59
+ removeActiveTaskMirror(hippoRoot, tenantId);
60
+ return null;
61
+ }
62
+ const loaded = rowToTaskSnapshot(row);
63
+ writeActiveTaskMirror(hippoRoot, tenantId, loaded);
64
+ return loaded;
65
+ }
66
+ finally {
67
+ closeHippoDb(db);
68
+ }
69
+ }
70
+ /**
71
+ * Default freshness bound for AMBIENT active-task-snapshot reads (DF1,
72
+ * docs/plans/2026-08-23-df1-snapshot-lifecycle.md): 72h, chosen over 48h so
73
+ * a Friday-evening orphan still offers continuity on Monday morning.
74
+ * Exported so callers can override via `loadFreshActiveTaskSnapshot`'s
75
+ * `opts.maxAgeMs`; deliberately no env knob (Simplicity First).
76
+ */
77
+ export const SNAPSHOT_AMBIENT_MAX_AGE_MS = 72 * 60 * 60 * 1000;
78
+ /** A usable session id: non-null, non-empty string. Named predicate (not an
79
+ * inline `typeof` check) so the owner-match rule in
80
+ * `loadFreshActiveTaskSnapshot` states its contract once. */
81
+ function isNonEmptySessionId(value) {
82
+ return typeof value === 'string' && value.length > 0;
83
+ }
84
+ /**
85
+ * Bounded read for AMBIENT active-task-snapshot surfaces (UserPromptSubmit
86
+ * hook context, MCP recall block) — the never-expires fix for DF1. A
87
+ * snapshot written by `hippo pre-compact` has no death path tied to the
88
+ * session that owns it, so an orphaned row would otherwise inject into
89
+ * every prompt of every later session forever. Wraps `loadActiveTaskSnapshot`
90
+ * (unchanged, still the source of truth for explicit continuity surfaces),
91
+ * then applies, in order:
92
+ *
93
+ * 1. Owner match — ONLY when both `opts.sessionId` and the snapshot's
94
+ * `session_id` are non-null, non-empty strings and strictly equal
95
+ * (`===`). Owner reads are unbounded: the session that owns the snapshot
96
+ * can always see its own working state, regardless of age.
97
+ * 2. Age check — everything else, including absent-vs-absent ids. A
98
+ * null/undefined/empty id on EITHER side never counts as an owner match;
99
+ * it falls through here instead. (`runPreCompact` can legitimately save a
100
+ * snapshot with `session_id = null`; a null-equals-null "match" would
101
+ * reopen indefinite ambient injection for exactly those rows.) Returns
102
+ * the snapshot only when `age(updated_at) <= maxAgeMs` (default
103
+ * `SNAPSHOT_AMBIENT_MAX_AGE_MS`); otherwise null.
104
+ *
105
+ * No SQL change — age derives from the existing `updated_at` column.
106
+ */
107
+ export function loadFreshActiveTaskSnapshot(hippoRoot, tenantId, opts = {}) {
108
+ const snapshot = loadActiveTaskSnapshot(hippoRoot, tenantId);
109
+ if (!snapshot)
110
+ return null;
111
+ const callerSessionId = opts.sessionId;
112
+ const isOwnerMatch = isNonEmptySessionId(callerSessionId) &&
113
+ isNonEmptySessionId(snapshot.session_id) &&
114
+ callerSessionId === snapshot.session_id;
115
+ if (isOwnerMatch)
116
+ return snapshot;
117
+ const maxAgeMs = opts.maxAgeMs ?? SNAPSHOT_AMBIENT_MAX_AGE_MS;
118
+ const ageMs = Date.now() - Date.parse(snapshot.updated_at);
119
+ return ageMs <= maxAgeMs ? snapshot : null;
120
+ }
121
+ export function clearActiveTaskSnapshot(hippoRoot, tenantId, clearedStatus = 'cleared') {
122
+ assertTenantId('clearActiveTaskSnapshot', tenantId);
123
+ const db = openStore(hippoRoot);
124
+ const now = new Date().toISOString();
125
+ try {
126
+ // SAFETY: active's shape matches the single `id` column selected above.
127
+ const active = db.prepare(`SELECT id FROM task_snapshots WHERE status = 'active' AND tenant_id = ? ORDER BY updated_at DESC, id DESC LIMIT 1`).get(tenantId);
128
+ if (!active?.id) {
129
+ removeActiveTaskMirror(hippoRoot, tenantId);
130
+ return false;
131
+ }
132
+ db.prepare(`UPDATE task_snapshots SET status = ?, updated_at = ? WHERE id = ? AND tenant_id = ?`).run(clearedStatus, now, active.id, tenantId);
133
+ removeActiveTaskMirror(hippoRoot, tenantId);
134
+ return true;
135
+ }
136
+ finally {
137
+ closeHippoDb(db);
138
+ }
139
+ }
140
+ /**
141
+ * Close the `active` task snapshot(s) owned by `sessionId`, for the T3
142
+ * session-end death path (DF1, docs/plans/2026-08-23-df1-snapshot-lifecycle.md).
143
+ * Only one `active` row exists per tenant in practice (supersession happens
144
+ * at save), but the WHERE clause scopes on `session_id` too — not just
145
+ * `status='active' AND tenant_id=?` — so an ending session can never close a
146
+ * different, newer session's active snapshot. Returns the number of rows
147
+ * closed (0 when no active row is owned by `sessionId`).
148
+ */
149
+ export function closeTaskSnapshotsForSession(hippoRoot, tenantId, sessionId, status = 'session-ended') {
150
+ assertTenantId('closeTaskSnapshotsForSession', tenantId);
151
+ const db = openStore(hippoRoot);
152
+ const now = new Date().toISOString();
153
+ try {
154
+ const result = db.prepare(`UPDATE task_snapshots SET status = ?, updated_at = ? WHERE status = 'active' AND tenant_id = ? AND session_id = ?`).run(status, now, tenantId, sessionId);
155
+ return Number(result.changes ?? 0);
156
+ }
157
+ finally {
158
+ closeHippoDb(db);
159
+ }
160
+ }
161
+ export function appendSessionEvent(hippoRoot, tenantId, event) {
162
+ assertTenantId('appendSessionEvent', tenantId);
163
+ const db = openStore(hippoRoot);
164
+ const now = new Date().toISOString();
165
+ // v1.2: scope is wired through. Default-deny in api.recall + cmdRecall
166
+ // continuity reads applies to slack:private:* and 'unknown:legacy' rows.
167
+ try {
168
+ const result = db.prepare(`
169
+ INSERT INTO session_events(session_id, task, event_type, content, source, scope, metadata_json, tenant_id, created_at)
170
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
171
+ `).run(event.session_id, event.task ?? null, event.event_type, event.content, event.source ?? 'cli', event.scope ?? null, JSON.stringify(event.metadata ?? {}), tenantId, now);
172
+ const id = Number(result.lastInsertRowid ?? 0);
173
+ // SAFETY: row's shape matches the nine columns named in the SELECT
174
+ // above.
175
+ const row = db.prepare(`
176
+ SELECT id, session_id, task, event_type, content, source, scope, metadata_json, created_at
177
+ FROM session_events
178
+ WHERE id = ?
179
+ `).get(id);
180
+ if (!row) {
181
+ throw new Error('Failed to reload saved session event');
182
+ }
183
+ const loaded = rowToSessionEvent(row);
184
+ // SAFETY: recentRows' shape matches the nine columns named in the
185
+ // SELECT above.
186
+ const recentRows = db.prepare(`
187
+ SELECT id, session_id, task, event_type, content, source, scope, metadata_json, created_at
188
+ FROM session_events
189
+ WHERE session_id = ? AND tenant_id = ?
190
+ ORDER BY created_at DESC, id DESC
191
+ LIMIT ?
192
+ `).all(loaded.session_id, tenantId, 20);
193
+ const recent = recentRows.map(rowToSessionEvent).reverse();
194
+ writeRecentSessionMirror(hippoRoot, tenantId, recent);
195
+ return loaded;
196
+ }
197
+ finally {
198
+ closeHippoDb(db);
199
+ }
200
+ }
201
+ export function listSessionEvents(hippoRoot, tenantId, options = {}) {
202
+ assertTenantId('listSessionEvents', tenantId);
203
+ const db = openStore(hippoRoot);
204
+ try {
205
+ const clauses = ['tenant_id = ?'];
206
+ const params = [tenantId];
207
+ if (options.session_id) {
208
+ clauses.push('session_id = ?');
209
+ params.push(options.session_id);
210
+ }
211
+ if (options.task) {
212
+ clauses.push('task = ?');
213
+ params.push(options.task);
214
+ }
215
+ const limit = Math.max(1, Math.trunc(options.limit ?? 8));
216
+ params.push(limit);
217
+ const where = `WHERE ${clauses.join(' AND ')}`;
218
+ // SAFETY: rows' shape matches the nine columns named in the SELECT
219
+ // above.
220
+ const rows = db.prepare(`
221
+ SELECT id, session_id, task, event_type, content, source, scope, metadata_json, created_at
222
+ FROM session_events
223
+ ${where}
224
+ ORDER BY created_at DESC, id DESC
225
+ LIMIT ?
226
+ `).all(...params);
227
+ return rows.map(rowToSessionEvent).reverse();
228
+ }
229
+ finally {
230
+ closeHippoDb(db);
231
+ }
232
+ }
233
+ /**
234
+ * Return session_ids with a `session_complete` event newer than `sinceMs`.
235
+ * Used by the sleep auto-promotion pass to bound scanning to a fixed window.
236
+ */
237
+ export function findPromotableSessions(hippoRoot, tenantId, sinceMs) {
238
+ assertTenantId('findPromotableSessions', tenantId);
239
+ const db = openStore(hippoRoot);
240
+ try {
241
+ // SAFETY: rows' shape matches the single `session_id` column selected
242
+ // above.
243
+ const rows = db.prepare(`
244
+ SELECT DISTINCT session_id FROM session_events
245
+ WHERE event_type = 'session_complete' AND created_at >= ? AND tenant_id = ?
246
+ `).all(new Date(sinceMs).toISOString(), tenantId);
247
+ return rows;
248
+ }
249
+ finally {
250
+ closeHippoDb(db);
251
+ }
252
+ }
253
+ /**
254
+ * Idempotency guard — true if a trace-layer memory with this source_session_id
255
+ * already exists.
256
+ */
257
+ export function traceExistsForSession(hippoRoot, tenantId, session_id) {
258
+ assertTenantId('traceExistsForSession', tenantId);
259
+ const db = openStore(hippoRoot);
260
+ try {
261
+ const row = db.prepare(`
262
+ SELECT 1 FROM memories
263
+ WHERE source_session_id = ? AND layer = 'trace' AND tenant_id = ?
264
+ LIMIT 1
265
+ `).get(session_id, tenantId);
266
+ return !!row;
267
+ }
268
+ finally {
269
+ closeHippoDb(db);
270
+ }
271
+ }
272
+ //# sourceMappingURL=sessions.js.map