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,229 @@
1
+ import * as fs from 'fs';
2
+ import * as path from 'path';
3
+ import { importEntries, isJsonString, isJsonPlainObject } from './core.js';
4
+ import { parseFrontmatter, collectMarkdownFiles } from './markdown-parse.js';
5
+ /** Coerce one imported record (string, `{content|text: ...}` object, or
6
+ * anything else) into the plain-text memory chunk it represents. */
7
+ function extractMemoryText(candidate) {
8
+ if (isJsonString(candidate))
9
+ return candidate;
10
+ if (isJsonPlainObject(candidate)) {
11
+ return String(candidate['content'] ?? candidate['text'] ?? '');
12
+ }
13
+ return '';
14
+ }
15
+ /**
16
+ * Parse ChatGPT memory export file.
17
+ * Supports:
18
+ * - JSON array of strings: ["memory 1", "memory 2"]
19
+ * - JSON array of objects: [{"content": "...", "created": "..."}]
20
+ * - ChatGPT export format: {"memories": [{"content": "...", "created_at": "..."}]}
21
+ * - Plain text: one memory per line
22
+ */
23
+ function parseChatGPTFile(filePath) {
24
+ const raw = fs.readFileSync(filePath, 'utf8').trim();
25
+ // Try JSON first
26
+ if (raw.startsWith('[') || raw.startsWith('{')) {
27
+ try {
28
+ const parsed = JSON.parse(raw);
29
+ // {"memories": [...]} - ChatGPT export format
30
+ if (isJsonPlainObject(parsed) && Array.isArray(parsed.memories)) {
31
+ return parsed.memories.map(extractMemoryText).filter(Boolean);
32
+ }
33
+ // Array format
34
+ if (Array.isArray(parsed)) {
35
+ return parsed.map(extractMemoryText).filter(Boolean);
36
+ }
37
+ }
38
+ catch {
39
+ // Fall through to plain text
40
+ }
41
+ }
42
+ // Plain text: one memory per line
43
+ return raw.split('\n').map((l) => l.trim()).filter(Boolean);
44
+ }
45
+ export function importChatGPT(filePath, options) {
46
+ const chunks = parseChatGPTFile(filePath);
47
+ return importEntries(chunks, 'import:chatgpt', ['imported', 'chatgpt'], options);
48
+ }
49
+ // ---------------------------------------------------------------------------
50
+ // Claude importer
51
+ // ---------------------------------------------------------------------------
52
+ const HIPPO_START = '<!-- hippo:start -->';
53
+ const HIPPO_END = '<!-- hippo:end -->';
54
+ /**
55
+ * Strip the hippo hook block from markdown content.
56
+ */
57
+ function stripHippoBlock(content) {
58
+ const startIdx = content.indexOf(HIPPO_START);
59
+ const endIdx = content.indexOf(HIPPO_END);
60
+ if (startIdx === -1 || endIdx === -1)
61
+ return content;
62
+ return content.slice(0, startIdx) + content.slice(endIdx + HIPPO_END.length);
63
+ }
64
+ /**
65
+ * Split markdown into meaningful chunks (headings + bullet points).
66
+ */
67
+ function splitMarkdown(content) {
68
+ const chunks = [];
69
+ const lines = content.split('\n');
70
+ let current = '';
71
+ for (const line of lines) {
72
+ const trimmed = line.trim();
73
+ // Heading: start a new chunk
74
+ if (/^#{1,6}\s+/.test(trimmed)) {
75
+ if (current.trim())
76
+ chunks.push(current.trim());
77
+ current = trimmed;
78
+ continue;
79
+ }
80
+ // Bullet point: each bullet is its own chunk (flush previous if not a bullet context)
81
+ if (/^[-*+]\s+/.test(trimmed)) {
82
+ if (current.trim() && !/^[-*+]\s+/.test(current.split('\n')[0])) {
83
+ chunks.push(current.trim());
84
+ current = '';
85
+ }
86
+ if (current.trim()) {
87
+ chunks.push(current.trim());
88
+ current = '';
89
+ }
90
+ current = trimmed.replace(/^[-*+]\s+/, '').trim();
91
+ continue;
92
+ }
93
+ // Numbered list item
94
+ if (/^\d+\.\s+/.test(trimmed)) {
95
+ if (current.trim()) {
96
+ chunks.push(current.trim());
97
+ current = '';
98
+ }
99
+ current = trimmed.replace(/^\d+\.\s+/, '').trim();
100
+ continue;
101
+ }
102
+ // Empty line
103
+ if (!trimmed) {
104
+ if (current.trim()) {
105
+ chunks.push(current.trim());
106
+ current = '';
107
+ }
108
+ continue;
109
+ }
110
+ // Regular line: append to current
111
+ current = current ? current + ' ' + trimmed : trimmed;
112
+ }
113
+ if (current.trim())
114
+ chunks.push(current.trim());
115
+ return chunks.filter(Boolean);
116
+ }
117
+ /**
118
+ * Parse CLAUDE.md or Claude memory.json.
119
+ */
120
+ function parseClaudeFile(filePath) {
121
+ const raw = fs.readFileSync(filePath, 'utf8');
122
+ // JSON memory file
123
+ if (filePath.endsWith('.json')) {
124
+ try {
125
+ const parsed = JSON.parse(raw.trim());
126
+ if (Array.isArray(parsed)) {
127
+ return parsed.map(extractMemoryText).filter(Boolean);
128
+ }
129
+ }
130
+ catch {
131
+ // Fall through to markdown
132
+ }
133
+ }
134
+ // Markdown file: strip hippo block and split
135
+ const cleaned = stripHippoBlock(raw);
136
+ return splitMarkdown(cleaned);
137
+ }
138
+ export function importClaude(filePath, options) {
139
+ const chunks = parseClaudeFile(filePath);
140
+ return importEntries(chunks, 'import:claude', ['imported', 'claude'], options);
141
+ }
142
+ // ---------------------------------------------------------------------------
143
+ // Cursor importer
144
+ // ---------------------------------------------------------------------------
145
+ /**
146
+ * Split cursor rules file into chunks.
147
+ * Priority: numbered items, then bullet points, then double newlines.
148
+ */
149
+ function parseCursorFile(content) {
150
+ const chunks = [];
151
+ const lines = content.split('\n');
152
+ let current = '';
153
+ for (const line of lines) {
154
+ const trimmed = line.trim();
155
+ // Skip comment-only lines that are empty after stripping #
156
+ if (!trimmed || trimmed === '#') {
157
+ if (current.trim()) {
158
+ chunks.push(current.trim());
159
+ current = '';
160
+ }
161
+ continue;
162
+ }
163
+ // Numbered item: 1. 2. 3.
164
+ if (/^\d+\.\s+/.test(trimmed)) {
165
+ if (current.trim()) {
166
+ chunks.push(current.trim());
167
+ current = '';
168
+ }
169
+ current = trimmed.replace(/^\d+\.\s+/, '').trim();
170
+ continue;
171
+ }
172
+ // Bullet: - or *
173
+ if (/^[-*]\s+/.test(trimmed)) {
174
+ if (current.trim()) {
175
+ chunks.push(current.trim());
176
+ current = '';
177
+ }
178
+ current = trimmed.replace(/^[-*]\s+/, '').trim();
179
+ continue;
180
+ }
181
+ // Regular line
182
+ current = current ? current + ' ' + trimmed : trimmed;
183
+ }
184
+ if (current.trim())
185
+ chunks.push(current.trim());
186
+ // Also split on double newlines within chunks if they somehow ended up there
187
+ return chunks.flatMap((c) => {
188
+ const parts = c.split(/\n{2,}/);
189
+ return parts.map((p) => p.trim()).filter(Boolean);
190
+ });
191
+ }
192
+ const CURSOR_RULE_FILE = /\.mdc?$/i;
193
+ /** Import `.cursorrules`, one rule file, or a `.cursor/rules` tree of `.mdc` and `.md` rules. */
194
+ export function importCursor(sourcePath, options) {
195
+ const files = fs.statSync(sourcePath).isDirectory()
196
+ ? collectMarkdownFiles(sourcePath, options.hippoRoot, CURSOR_RULE_FILE).map((rel) => path.join(sourcePath, rel))
197
+ : [sourcePath];
198
+ const chunks = files.flatMap((file) => {
199
+ const raw = fs.readFileSync(file, 'utf8');
200
+ return parseCursorFile(CURSOR_RULE_FILE.test(file) ? parseFrontmatter(raw).body : raw);
201
+ });
202
+ return importEntries(chunks, 'import:cursor', ['imported', 'cursor'], options);
203
+ }
204
+ // ---------------------------------------------------------------------------
205
+ // Generic file importer
206
+ // ---------------------------------------------------------------------------
207
+ /**
208
+ * Split a generic file into chunks.
209
+ * Markdown: split on headings and bullet points.
210
+ * Plain text: split on double newlines or one-per-line.
211
+ */
212
+ function parseGenericFile(filePath) {
213
+ const raw = fs.readFileSync(filePath, 'utf8');
214
+ const isMarkdown = filePath.endsWith('.md') || filePath.endsWith('.mdx');
215
+ if (isMarkdown) {
216
+ return splitMarkdown(raw);
217
+ }
218
+ // Plain text: try double newlines first
219
+ const byParagraph = raw.split(/\n{2,}/).map((p) => p.trim()).filter(Boolean);
220
+ if (byParagraph.length > 1)
221
+ return byParagraph;
222
+ // Fall back to one-per-line
223
+ return raw.split('\n').map((l) => l.trim()).filter(Boolean);
224
+ }
225
+ export function importGenericFile(filePath, options) {
226
+ const chunks = parseGenericFile(filePath);
227
+ return importEntries(chunks, 'import:file', ['imported'], options);
228
+ }
229
+ //# sourceMappingURL=sources.js.map
@@ -0,0 +1,11 @@
1
+ import { type ImportResult, type ImportOptions } from './core.js';
2
+ /**
3
+ * Import a markdown vault FOLDER as `kind='raw'` memories.
4
+ *
5
+ * NOT re-entrant: idempotency rests on the in-memory `existing` Map loaded once
6
+ * at the top. Two concurrent importVault runs over the same vault could both see
7
+ * a note as absent and double-insert (the connector pattern relies on a single
8
+ * sequential writer; same caveat applies here).
9
+ */
10
+ export declare function importVault(folderPath: string, options: ImportOptions): ImportResult;
11
+ //# sourceMappingURL=vault.d.ts.map
@@ -0,0 +1,352 @@
1
+ import * as fs from 'fs';
2
+ import * as path from 'path';
3
+ import { createHash } from 'node:crypto';
4
+ import { createMemory } from '../memory.js';
5
+ import { initStore } from '../store/open.js';
6
+ import { remember, archiveRaw, isPrivateScope } from '../api.js';
7
+ import { openHippoDb, closeHippoDb } from '../db.js';
8
+ import { RejectedValueError, checkRejectionGuard } from '../rejection.js';
9
+ import { loadConfig } from '../config.js';
10
+ import { vetSecrets } from '../secret-detect.js';
11
+ import { log } from '../log.js';
12
+ import { isJsonString } from './core.js';
13
+ import { parseFrontmatter, frontmatterList, parseWikilinks, collectMarkdownFiles, realpathOrResolve } from './markdown-parse.js';
14
+ // ---------------------------------------------------------------------------
15
+ // K1 vault importer (markdown-vault FOLDER → kind='raw' memories)
16
+ //
17
+ // MIRRORS THE CONNECTOR PATTERN (src/connectors/slack|github), NOT the
18
+ // single-file importers above. Each note becomes a single kind='raw' row with
19
+ // provenance in TAGS (`source:vault` + `vault:<name>`), an artifactRef cursor
20
+ // key, and a content-hash tag. Changes APPEND a new raw row after archiveRaw of
21
+ // the old one; deletions archiveRaw the orphaned rows. We NEVER `supersede` a
22
+ // raw row (supersede yields kind='distilled', losing raw-append-only protection
23
+ // and escaping the kind='raw' deletion rescan) — all raw deletions route through
24
+ // `archiveRaw` (the only trigger-legit raw delete).
25
+ // ---------------------------------------------------------------------------
26
+ /** Escape LIKE wildcards in operator-supplied text (mirror of
27
+ * src/project-briefs.ts:477 / src/store/search-rows.ts, kept local since neither is
28
+ * exported). Used so a `%`/`_`/`\` in the vault name cannot over-match the
29
+ * loader prefix and archive another vault's rows. */
30
+ function escapeLike(term) {
31
+ return term.replace(/[%_\\]/g, '\\$&');
32
+ }
33
+ /**
34
+ * Import a markdown vault FOLDER as `kind='raw'` memories.
35
+ *
36
+ * NOT re-entrant: idempotency rests on the in-memory `existing` Map loaded once
37
+ * at the top. Two concurrent importVault runs over the same vault could both see
38
+ * a note as absent and double-insert (the connector pattern relies on a single
39
+ * sequential writer; same caveat applies here).
40
+ */
41
+ export function importVault(folderPath, options) {
42
+ const hippoRoot = options.hippoRoot;
43
+ const tenantId = options.tenantId ?? 'default';
44
+ const { vaultName, scope } = vaultIdentityOrThrow(options);
45
+ const extraTags = options.extraTags ?? [];
46
+ const dryRun = options.dryRun ?? false;
47
+ if (options.global) {
48
+ // The raw-archive path is tenant-local; global mode would put raw vault rows
49
+ // in the wrong store. Reject for SDK callers too (the CLI also rejects
50
+ // --global) rather than silently writing local (codex P2).
51
+ throw new Error('importVault does not support global mode (raw rows are tenant-local).');
52
+ }
53
+ // Self-store no-op guard (codex R8 P1). MUST run BEFORE the existing-rows load
54
+ // and the deletion-sync pass below. If the vault folder IS the store (or lives
55
+ // inside it), there are no real vault notes - only the store's own markdown
56
+ // mirror files. Letting collectMarkdownFiles return [] for this case is NOT
57
+ // safe: an empty scan is indistinguishable from "every note was deleted", so
58
+ // deletion-sync would archive every live vault:<name>:* row, and raw-archive
59
+ // content redaction makes that loss IRREVERSIBLE. The only safe reading of
60
+ // "import the store into itself" is "do nothing". Canonicalize both paths
61
+ // (realpath: dereference junctions/symlinks + normalize Windows case) so an
62
+ // aliased path to the store is still caught (codex R9 P2).
63
+ const resolvedStore = realpathOrResolve(hippoRoot);
64
+ const resolvedFolder = realpathOrResolve(folderPath);
65
+ if (resolvedFolder === resolvedStore || resolvedFolder.startsWith(resolvedStore + path.sep)) {
66
+ return { total: 0, imported: 0, skipped: 0, rejected: 0, archived: 0, entries: [] };
67
+ }
68
+ const ctx = {
69
+ hippoRoot,
70
+ tenantId,
71
+ // Process-local actor; the vault importer is a CLI/SDK ingestion path, not
72
+ // a bearer-authed request. archiveRaw / remember thread this into audit.
73
+ actor: { subject: 'connector:vault', role: 'admin' },
74
+ };
75
+ const existing = loadVaultRows(hippoRoot, tenantId, vaultName);
76
+ const relpaths = collectMarkdownFiles(folderPath, hippoRoot);
77
+ const tally = { total: 0, imported: 0, skipped: 0, rejected: 0, archived: 0, redacted: 0, entries: [] };
78
+ const baseHalfLifeDays = loadConfig(hippoRoot).defaultHalfLifeDays;
79
+ const seen = new Set();
80
+ // AT1 P2 fix: dry-run never called remember(), so it never probed
81
+ // tombstones — every note that reached the write step counted as
82
+ // `imported` even when a real run would refuse it. Probe (read-only) via
83
+ // the same guard remember()/writeEntry uses, without ever writing.
84
+ const dryRunDb = dryRun ? openHippoDb(hippoRoot) : null;
85
+ try {
86
+ const run = {
87
+ ctx, folderPath, vaultName, scope, extraTags, dryRun, dryRunDb, existing, seen, baseHalfLifeDays, tally,
88
+ };
89
+ for (const relpath of relpaths)
90
+ importVaultNote(run, relpath);
91
+ }
92
+ finally {
93
+ if (dryRunDb)
94
+ closeHippoDb(dryRunDb);
95
+ }
96
+ // Deletion-sync: any artifactRef present in the Map but NOT seen this run is a
97
+ // note that vanished from the source folder → archive its raw row. Per-file
98
+ // archiveRaw (own handle); no outer SAVEPOINT (no cross-file idempotency row
99
+ // to commit atomically, unlike github's multi-row case).
100
+ for (const [artifactRef, rows] of existing) {
101
+ if (seen.has(artifactRef))
102
+ continue;
103
+ for (const row of rows) {
104
+ tally.archived++; // count even in dryRun so the preview reflects destructive deletes
105
+ if (!dryRun)
106
+ archiveRaw(ctx, row.id, `source_deleted:${artifactRef}`);
107
+ }
108
+ }
109
+ const { total, imported, skipped, rejected, archived, redacted, entries } = tally;
110
+ return { total, imported, skipped, rejected, archived, redacted, entries };
111
+ }
112
+ function vaultIdentityOrThrow(options) {
113
+ // Vault NAME is the identity key for the destructive deletion-sync below, so it
114
+ // must be explicit. Defaulting to the folder basename meant two unrelated vaults
115
+ // sharing a basename (e.g. work/notes and personal/notes) collided on the same
116
+ // `vault:<name>:*` prefix - importing the second loaded the first's rows and the
117
+ // deletion-sync archived them (codex R10 P2). Require a deliberate name instead
118
+ // of inferring a path-unstable one.
119
+ const vaultName = options.name?.trim();
120
+ if (!vaultName) {
121
+ throw new Error('importVault requires an explicit vault name (options.name): it is the identity key for source-deletion sync and must not be inferred from the folder basename.');
122
+ }
123
+ if (vaultName.includes(':')) {
124
+ // ':' is the artifactRef delimiter (vault:<name>:<relpath>); a name
125
+ // containing it lets a different vault's prefix scan over-match and archive
126
+ // its rows (codex P2). Reject rather than silently corrupt the keys.
127
+ throw new Error(`vault name must not contain ':' (artifactRef delimiter): ${vaultName}`);
128
+ }
129
+ const scope = options.scope ?? null;
130
+ // Privacy footgun guard (codex R13 P2): hippo's recall filter only default-denies
131
+ // scopes shaped `<source>:private:*` (see isPrivateScope / PRIVATE_SCOPE_RE in
132
+ // scope.ts). A bare `private` (or `private:<x>`) first segment is NOT recognized
133
+ // as private, so notes a user believes are private would still be returned to
134
+ // no-scope recall callers. Reject the alias and point at the source-prefixed form
135
+ // rather than silently storing public-visible "private" notes. Use recall's own
136
+ // isPrivateScope as the single source of truth: reject a scope that names a
137
+ // `private` segment yet is NOT a valid `<source>:private:*` (catches `private`,
138
+ // `private:x`, and `vault:private` with a missing trailing segment).
139
+ if (scope !== null && scope.split(':').includes('private') && !isPrivateScope(scope)) {
140
+ throw new Error(`vault scope '${scope}' is not recognized as private by recall (only '<source>:private:*' scopes are default-denied). Use a source-prefixed scope such as 'vault:private:${vaultName}'.`);
141
+ }
142
+ return { vaultName, scope };
143
+ }
144
+ function loadVaultRows(hippoRoot, tenantId, vaultName) {
145
+ // Load ONCE: every existing raw row for this vault, tenant-scoped. The same
146
+ // Map serves both per-file idempotency AND the deletion diff (no second
147
+ // query). LIKE-escape the vault name so a `%`/`_` in it can't over-match.
148
+ initStore(hippoRoot);
149
+ // artifactRef -> ALL its live raw rows. >1 only after a concurrent double-insert
150
+ // (the importer is not re-entrant; see the JSDoc). The buckets matter: a later
151
+ // changed/deletion pass must archive EVERY matching row, not just the last one
152
+ // scanned, or older raw vault content lingers live + searchable (codex P2).
153
+ const existing = new Map();
154
+ const db = openHippoDb(hippoRoot);
155
+ try {
156
+ const likeParam = `vault:${escapeLike(vaultName)}:%`;
157
+ // SAFETY: query selects exactly the columns of VaultRow, in the same
158
+ // names, from the memories table this module owns.
159
+ const rows = db
160
+ .prepare(`SELECT id, artifact_ref, tags_json, scope FROM memories
161
+ WHERE artifact_ref LIKE ? ESCAPE '\\' AND tenant_id = ? AND kind = 'raw'`)
162
+ .all(likeParam, tenantId);
163
+ // SQLite LIKE is case-insensitive for ASCII, so the query over-fetches
164
+ // (vault 'A' also matches 'vault:a:%'). Filter to the EXACT-case prefix in
165
+ // JS so deletion-sync never archives a different-cased vault's rows (codex P2).
166
+ const exactPrefix = `vault:${vaultName}:`;
167
+ for (const row of rows) {
168
+ if (row.artifact_ref && row.artifact_ref.startsWith(exactPrefix)) {
169
+ const bucket = existing.get(row.artifact_ref);
170
+ if (bucket)
171
+ bucket.push(row);
172
+ else
173
+ existing.set(row.artifact_ref, [row]);
174
+ }
175
+ }
176
+ }
177
+ finally {
178
+ closeHippoDb(db);
179
+ }
180
+ return existing;
181
+ }
182
+ function importVaultNote(run, relpath) {
183
+ const { ctx, tally } = run;
184
+ tally.total++;
185
+ const artifactRef = `vault:${run.vaultName}:${relpath}`;
186
+ run.seen.add(artifactRef);
187
+ // Content-hash is computed from the RAW file bytes (deterministic; no
188
+ // Date/random in the content path) so idempotency survives frontmatter
189
+ // edits identically to body edits.
190
+ const rawFileContent = readVaultNote(run, relpath);
191
+ if (rawFileContent === null)
192
+ return;
193
+ const hash = createHash('sha256').update(rawFileContent).digest('hex');
194
+ const hashTag = `content-hash:${hash}`;
195
+ // Load every live raw row for this ref (>1 only after a concurrent double-
196
+ // insert). The idempotency decision happens below, AFTER the full tag set is
197
+ // built, so it can compare the complete envelope rather than a subset.
198
+ const priors = run.existing.get(artifactRef) ?? [];
199
+ const { fm, body } = parseFrontmatter(rawFileContent);
200
+ // Empty / frontmatter-only note: nothing storable (createMemory enforces a
201
+ // min content length). The note's CONTENT was deleted at source, so this is a
202
+ // content-deletion: archive any prior row(s) - the old body must not stay live
203
+ // and searchable after the source no longer holds it (codex R12 P2) - then
204
+ // skip the write. Archiving here is safe precisely because we then skip
205
+ // remember() entirely: there is no archive-then-throw-on-empty-body hazard
206
+ // (the original reason this branch did not archive). The note stays in `seen`
207
+ // so deletion-sync does not double-process it.
208
+ if (body.trim().length < 3) {
209
+ archivePriors(run, priors, `emptied:${artifactRef}`);
210
+ tally.skipped++;
211
+ return;
212
+ }
213
+ const tags = vaultNoteTags(run, hashTag, fm, body);
214
+ if (vaultEnvelopeUnchanged(priors, tags, run.scope)) {
215
+ // Unchanged file + envelope → skip (idempotent re-import).
216
+ tally.skipped++;
217
+ return;
218
+ }
219
+ // Changed file → archive EVERY old raw row for this ref (normally one; >1
220
+ // only after a concurrent double-insert), then append the new one. NEVER
221
+ // supersede (would yield kind='distilled'). archiveRaw commits + closes its
222
+ // handle before remember() runs, so there is no double-live row; a crash
223
+ // between them self-heals (file re-imported as fresh raw next run).
224
+ archivePriors(run, priors, `changed:${artifactRef}`);
225
+ // remember() owns the actual write. We build an `echo` of the SAME content +
226
+ // tags via createMemory purely for the ImportResult, then reconcile its id to
227
+ // remember()'s real row id so entries[] reflects the row that landed.
228
+ const content = vetSecrets(body, tags, true).content;
229
+ const echo = createMemory(content, {
230
+ kind: 'raw',
231
+ tags,
232
+ scope: run.scope,
233
+ owner: 'agent:vault-import',
234
+ artifact_ref: artifactRef,
235
+ tenantId: ctx.tenantId,
236
+ baseHalfLifeDays: run.baseHalfLifeDays,
237
+ });
238
+ if (!storeVaultNote(run, echo, content, tags, artifactRef)) {
239
+ tally.rejected++;
240
+ return;
241
+ }
242
+ tally.entries.push(echo);
243
+ tally.imported++;
244
+ if (content !== body)
245
+ tally.redacted++;
246
+ }
247
+ function readVaultNote(run, relpath) {
248
+ try {
249
+ return fs.readFileSync(path.join(run.folderPath, relpath), 'utf8');
250
+ }
251
+ catch {
252
+ // File vanished between enumeration and read (TOCTOU), or a transient
253
+ // IO/permission error. Skip this one file rather than aborting the whole
254
+ // import (incl. the deletion-sync pass); an idempotent re-run picks it up.
255
+ run.tally.skipped++;
256
+ return null;
257
+ }
258
+ }
259
+ function archivePriors(run, priors, reason) {
260
+ for (const p of priors) {
261
+ run.tally.archived++; // count the would-be archive even in dryRun (true preview)
262
+ if (!run.dryRun)
263
+ archiveRaw(run.ctx, p.id, reason);
264
+ }
265
+ }
266
+ function vaultNoteTags(run, hashTag, fm, body) {
267
+ // Build the FULL tag envelope this import would write, BEFORE the idempotency
268
+ // decision. De-duplicate (createMemory stores tags verbatim, so a collision
269
+ // between, e.g., a frontmatter tag and an extraTag would otherwise produce a
270
+ // duplicate). Order-preserving.
271
+ const frontmatterTags = [
272
+ ...frontmatterList(fm['tags']),
273
+ ...frontmatterList(fm['aliases']).map((a) => `alias:${a}`),
274
+ ];
275
+ const wikilinkTags = parseWikilinks(body).map((t) => `wikilink-candidate:${t}`);
276
+ return Array.from(new Set([
277
+ 'source:vault',
278
+ `vault:${run.vaultName}`,
279
+ hashTag,
280
+ ...frontmatterTags,
281
+ ...wikilinkTags,
282
+ ...run.extraTags,
283
+ ]));
284
+ }
285
+ function vaultEnvelopeUnchanged(priors, tags, scope) {
286
+ // Unchanged iff EVERY live raw row carries the EXACT same tag set AND scope.
287
+ // Comparing the COMPLETE set (not the content-hash + a subset of extra tags)
288
+ // means every envelope change registers: content (via the content-hash tag),
289
+ // frontmatter, wikilinks, an ADDED extra tag, or a REMOVED one - the earlier
290
+ // piecemeal checks missed scope (R10 P2) then tag removal (R11 P2). Set
291
+ // equality is order-independent and both sides are deduped. (`length > 0`
292
+ // guard: a never-seen file must import, not skip; archiving ALL priors on a
293
+ // mismatch also clears any concurrent-double-insert duplicates.)
294
+ const wantTags = new Set(tags);
295
+ return (priors.length > 0 &&
296
+ priors.every((p) => {
297
+ if ((p.scope ?? null) !== scope)
298
+ return false;
299
+ const priorTags = parseJsonArrayLoose(p.tags_json);
300
+ return priorTags.length === wantTags.size && priorTags.every((t) => wantTags.has(t));
301
+ }));
302
+ }
303
+ /** Returns false when the rejection guard refuses the note; any other error propagates. */
304
+ function storeVaultNote(run, echo, content, tags, artifactRef) {
305
+ try {
306
+ // dryRun preview: count what WOULD import, but make no writes (codex P2).
307
+ if (!run.dryRun) {
308
+ // AT1 (plan §3 containment): a rejected note must not abort the rest
309
+ // of the vault scan (deletion-sync pass included). The priors above
310
+ // are already archived by this point — same self-heal story as any
311
+ // other crash between archiveRaw and remember() (comment above): a
312
+ // re-run with the file still rejected hits the same refusal again,
313
+ // loud each time via the rejected count.
314
+ const result = remember(run.ctx, {
315
+ content,
316
+ kind: 'raw',
317
+ artifactRef,
318
+ owner: 'agent:vault-import',
319
+ scope: run.scope ?? undefined,
320
+ tags,
321
+ });
322
+ echo.id = result.id;
323
+ }
324
+ else if (run.dryRunDb) {
325
+ // AT1 P2 fix: probe the tombstone without writing so the preview's
326
+ // `rejected` count matches what a real run would refuse.
327
+ checkRejectionGuard(run.dryRunDb, run.ctx.tenantId, echo.id, echo.content);
328
+ }
329
+ return true;
330
+ }
331
+ catch (err) {
332
+ if (err instanceof RejectedValueError)
333
+ return false;
334
+ throw err;
335
+ }
336
+ }
337
+ /** Local tolerant JSON-array parse for the loader's `tags_json` column. The
338
+ * store's own `parseJsonArray` is not exported; this matches its contract
339
+ * (returns [] on null/garbage). */
340
+ function parseJsonArrayLoose(value) {
341
+ if (!value)
342
+ return [];
343
+ try {
344
+ const parsed = JSON.parse(value);
345
+ return Array.isArray(parsed) ? parsed.filter(isJsonString) : [];
346
+ }
347
+ catch (err) {
348
+ log.debug(`import: unreadable tags_json read as no tags: ${err instanceof Error ? err.message : String(err)}`);
349
+ return [];
350
+ }
351
+ }
352
+ //# sourceMappingURL=vault.js.map
@@ -25,6 +25,7 @@
25
25
  * the row, default `[]`. On save, every id must exist in the SAME tenant; a
26
26
  * cross-tenant or nonexistent id is rejected (throw) before the insert.
27
27
  */
28
+ import { type KeysetPosition } from './keyset.js';
28
29
  export type IncidentStatus = 'open' | 'resolved' | 'closed';
29
30
  export declare const VALID_INCIDENT_STATES: ReadonlySet<IncidentStatus>;
30
31
  export interface Incident {
@@ -57,6 +58,8 @@ export interface SaveIncidentOpts {
57
58
  export interface ListIncidentsOpts {
58
59
  status?: IncidentStatus;
59
60
  limit?: number;
61
+ /** Resume after this row: the position the previous page ended on. */
62
+ after?: KeysetPosition;
60
63
  }
61
64
  /**
62
65
  * Create an incident. Writes the memory mirror + the incidents row atomically
package/dist/incidents.js CHANGED
@@ -27,11 +27,12 @@
27
27
  */
28
28
  import { BadRequestError, ConflictError, NotFoundError } from './api-errors.js';
29
29
  import { openHippoDb, closeHippoDb } from './db.js';
30
- import { writeEntry } from './store.js';
30
+ import { writeEntry } from './store/entry-writes.js';
31
31
  import { assertTenantId } from './tenant.js';
32
32
  import { createMemory, Layer } from './memory.js';
33
33
  import { appendAuditEvent } from './audit.js';
34
34
  import { objectHalfLifeDays } from './half-life-migration.js';
35
+ import { keysetAfter } from './keyset.js';
35
36
  export const VALID_INCIDENT_STATES = new Set([
36
37
  'open',
37
38
  'resolved',
@@ -286,6 +287,7 @@ export function loadIncidentById(hippoRoot, tenantId, id) {
286
287
  export function loadIncidents(hippoRoot, tenantId, opts = {}) {
287
288
  assertTenantId('loadIncidents', tenantId);
288
289
  const limit = opts.limit ?? 100;
290
+ const after = keysetAfter('created_at', 'id', opts.after);
289
291
  const db = openHippoDb(hippoRoot);
290
292
  try {
291
293
  let rows;
@@ -296,19 +298,19 @@ export function loadIncidents(hippoRoot, tenantId, opts = {}) {
296
298
  // SAFETY: rows' shape matches the columns named in INCIDENT_COLS above.
297
299
  rows = db.prepare(`
298
300
  SELECT ${INCIDENT_COLS} FROM incidents
299
- WHERE tenant_id = ? AND status = ?
301
+ WHERE tenant_id = ? AND status = ?${after.sql}
300
302
  ORDER BY created_at DESC, id DESC
301
303
  LIMIT ?
302
- `).all(tenantId, opts.status, limit);
304
+ `).all(tenantId, opts.status, ...after.params, limit);
303
305
  }
304
306
  else {
305
307
  // SAFETY: rows' shape matches the columns named in INCIDENT_COLS above.
306
308
  rows = db.prepare(`
307
309
  SELECT ${INCIDENT_COLS} FROM incidents
308
- WHERE tenant_id = ?
310
+ WHERE tenant_id = ?${after.sql}
309
311
  ORDER BY created_at DESC, id DESC
310
312
  LIMIT ?
311
- `).all(tenantId, limit);
313
+ `).all(tenantId, ...after.params, limit);
312
314
  }
313
315
  return rows.map(rowToIncident);
314
316
  }