hippo-memory 1.71.0 → 1.72.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 (555) hide show
  1. package/README.md +1 -1
  2. package/dist/agent-memories/apply.d.ts +0 -11
  3. package/dist/agent-memories/apply.js +6 -21
  4. package/dist/agent-memories/claude-code.js +1 -1
  5. package/dist/agent-memories/codex.js +1 -1
  6. package/dist/agent-memories/copilot.d.ts +1 -3
  7. package/dist/agent-memories/copilot.js +1 -17
  8. package/dist/agent-memories/project-folds.d.ts +10 -0
  9. package/dist/agent-memories/project-folds.js +34 -0
  10. package/dist/agent-memories/sync.js +8 -6
  11. package/dist/api/assemble.js +1 -1
  12. package/dist/api/audit.d.ts +29 -1
  13. package/dist/api/audit.js +44 -5
  14. package/dist/api/auth.d.ts +7 -4
  15. package/dist/api/auth.js +12 -8
  16. package/dist/api/auto-sleep.d.ts +5 -2
  17. package/dist/api/auto-sleep.js +7 -3
  18. package/dist/api/availability.js +2 -3
  19. package/dist/api/cards.d.ts +42 -0
  20. package/dist/api/cards.js +52 -0
  21. package/dist/api/conflicts.d.ts +28 -0
  22. package/dist/api/conflicts.js +61 -0
  23. package/dist/api/context-render.js +4 -3
  24. package/dist/api/context-select.js +9 -11
  25. package/dist/api/context.js +17 -8
  26. package/dist/api/continuity.d.ts +47 -0
  27. package/dist/api/continuity.js +66 -0
  28. package/dist/api/dormant.js +4 -2
  29. package/dist/api/drill-down.js +1 -1
  30. package/dist/api/embeddings.d.ts +17 -0
  31. package/dist/api/embeddings.js +28 -0
  32. package/dist/api/error-reply.d.ts +8 -0
  33. package/dist/api/error-reply.js +21 -0
  34. package/dist/api/forget.d.ts +6 -2
  35. package/dist/api/forget.js +18 -7
  36. package/dist/api/goals.d.ts +1 -1
  37. package/dist/api/goals.js +5 -9
  38. package/dist/api/index.d.ts +4 -2
  39. package/dist/api/index.js +4 -2
  40. package/dist/api/instruction-detect.js +5 -1
  41. package/dist/api/learn.d.ts +4 -0
  42. package/dist/api/learn.js +53 -27
  43. package/dist/api/ledger-db.d.ts +2 -6
  44. package/dist/api/ledger-db.js +4 -36
  45. package/dist/api/memories.d.ts +19 -1
  46. package/dist/api/memories.js +39 -0
  47. package/dist/api/objects.d.ts +1 -1
  48. package/dist/api/outcome.js +1 -1
  49. package/dist/api/pilot-arm.d.ts +1 -11
  50. package/dist/api/pilot-arm.js +5 -45
  51. package/dist/api/predictions.d.ts +3 -1
  52. package/dist/api/predictions.js +6 -0
  53. package/dist/api/projects.d.ts +21 -0
  54. package/dist/api/projects.js +24 -0
  55. package/dist/api/promote.js +1 -1
  56. package/dist/api/prompt-hook.d.ts +15 -2
  57. package/dist/api/prompt-hook.js +44 -4
  58. package/dist/api/recall-cli.d.ts +3 -0
  59. package/dist/api/recall-cli.js +13 -4
  60. package/dist/api/recall-core.js +3 -3
  61. package/dist/api/recall-pipeline.js +6 -7
  62. package/dist/api/recall-request.js +1 -1
  63. package/dist/api/recall.d.ts +0 -3
  64. package/dist/api/recall.js +19 -44
  65. package/dist/api/remember-local.js +1 -1
  66. package/dist/api/remember.d.ts +2 -1
  67. package/dist/api/remember.js +3 -2
  68. package/dist/api/session-end.d.ts +22 -0
  69. package/dist/api/session-end.js +101 -0
  70. package/dist/api/sleep-run.d.ts +1 -1
  71. package/dist/api/sleep-run.js +13 -9
  72. package/dist/api/status.d.ts +24 -0
  73. package/dist/api/status.js +40 -0
  74. package/dist/api/store-init.d.ts +4 -0
  75. package/dist/api/store-init.js +9 -0
  76. package/dist/api/tokens.js +1 -1
  77. package/dist/api/types.d.ts +2 -0
  78. package/dist/api/types.js +6 -1
  79. package/dist/capture/caller-session.d.ts +0 -3
  80. package/dist/capture/caller-session.js +1 -12
  81. package/dist/capture/capture-error.d.ts +2 -2
  82. package/dist/capture/capture-error.js +23 -18
  83. package/dist/capture/codex-patch.js +3 -1
  84. package/dist/capture/command.js +36 -95
  85. package/dist/capture/compact.js +10 -5
  86. package/dist/capture/compaction-items-caller.js +11 -20
  87. package/dist/capture/compaction-record.d.ts +7 -93
  88. package/dist/capture/compaction-record.js +29 -426
  89. package/dist/capture/compaction-spool.d.ts +4 -2
  90. package/dist/capture/compaction-spool.js +20 -6
  91. package/dist/capture/copilot-transcript.d.ts +2 -1
  92. package/dist/capture/copilot-transcript.js +2 -1
  93. package/dist/capture/extract.d.ts +5 -2
  94. package/dist/capture/extract.js +4 -3
  95. package/dist/capture/failure-caller.d.ts +1 -1
  96. package/dist/capture/failure-caller.js +21 -16
  97. package/dist/capture/failure-reading.d.ts +2 -1
  98. package/dist/capture/failure-reading.js +7 -9
  99. package/dist/capture/pre-compact-caller.js +5 -4
  100. package/dist/capture/session-digest.js +4 -3
  101. package/dist/capture/session-end-caller.js +2 -1
  102. package/dist/capture/session-worker.js +3 -4
  103. package/dist/capture/transcript.d.ts +2 -1
  104. package/dist/capture/transcript.js +6 -3
  105. package/dist/capture/working-state.d.ts +4 -2
  106. package/dist/capture/working-state.js +4 -2
  107. package/dist/cli/audit-prune.d.ts +0 -25
  108. package/dist/cli/audit-prune.js +1 -27
  109. package/dist/cli/audit.js +13 -13
  110. package/dist/cli/auth.d.ts +1 -1
  111. package/dist/cli/auth.js +12 -13
  112. package/dist/cli/briefs.d.ts +1 -4
  113. package/dist/cli/briefs.js +37 -292
  114. package/dist/cli/card.js +41 -33
  115. package/dist/cli/client.d.ts +5 -0
  116. package/dist/cli/client.js +24 -8
  117. package/dist/cli/compact-resume-payload.js +1 -1
  118. package/dist/cli/context.d.ts +1 -1
  119. package/dist/cli/context.js +12 -12
  120. package/dist/cli/continuity.d.ts +1 -1
  121. package/dist/cli/continuity.js +36 -39
  122. package/dist/cli/curate.d.ts +1 -1
  123. package/dist/cli/curate.js +23 -27
  124. package/dist/cli/dag.d.ts +1 -1
  125. package/dist/cli/dag.js +23 -8
  126. package/dist/cli/decisions.d.ts +0 -2
  127. package/dist/cli/decisions.js +4 -281
  128. package/dist/cli/eval.d.ts +1 -1
  129. package/dist/cli/eval.js +12 -13
  130. package/dist/cli/explain.d.ts +1 -1
  131. package/dist/cli/explain.js +21 -25
  132. package/dist/cli/flag-values.js +16 -6
  133. package/dist/cli/flags.d.ts +0 -289
  134. package/dist/cli/flags.js +3 -138
  135. package/dist/cli/graph.d.ts +3 -0
  136. package/dist/cli/graph.js +83 -0
  137. package/dist/cli/hook-runtime.d.ts +3 -5
  138. package/dist/cli/hook-runtime.js +7 -34
  139. package/dist/cli/incidents.d.ts +3 -0
  140. package/dist/cli/incidents.js +112 -0
  141. package/dist/cli/init.d.ts +1 -1
  142. package/dist/cli/init.js +28 -19
  143. package/dist/cli/install-steps.d.ts +2 -1
  144. package/dist/cli/install-steps.js +11 -3
  145. package/dist/cli/last-sleep.js +1 -1
  146. package/dist/cli/lenient-id.d.ts +2 -0
  147. package/dist/cli/lenient-id.js +13 -0
  148. package/dist/cli/maintenance.d.ts +3 -3
  149. package/dist/cli/maintenance.js +29 -30
  150. package/dist/cli/notes.d.ts +2 -0
  151. package/dist/cli/notes.js +41 -0
  152. package/dist/cli/open-browser.d.ts +14 -0
  153. package/dist/cli/open-browser.js +26 -0
  154. package/dist/cli/playbooks.d.ts +3 -4
  155. package/dist/cli/playbooks.js +126 -290
  156. package/dist/cli/predictions.d.ts +3 -0
  157. package/dist/cli/predictions.js +175 -0
  158. package/dist/cli/print.js +2 -1
  159. package/dist/cli/projects.js +30 -22
  160. package/dist/cli/quality-repair-once.d.ts +1 -1
  161. package/dist/cli/quality-repair-once.js +6 -5
  162. package/dist/cli/recall.js +13 -7
  163. package/dist/cli/refine-llm.js +17 -17
  164. package/dist/cli/remember.js +12 -12
  165. package/dist/cli/scheduler.js +2 -1
  166. package/dist/cli/serve.js +3 -3
  167. package/dist/cli/session-hooks.d.ts +1 -1
  168. package/dist/cli/session-hooks.js +45 -115
  169. package/dist/cli/setup.d.ts +1 -1
  170. package/dist/cli/setup.js +17 -10
  171. package/dist/cli/shared.d.ts +2 -6
  172. package/dist/cli/shared.js +9 -29
  173. package/dist/cli/sleep-render.js +6 -3
  174. package/dist/cli/sleep.js +6 -37
  175. package/dist/cli/status.d.ts +3 -3
  176. package/dist/cli/status.js +32 -35
  177. package/dist/cli/transfer.d.ts +1 -1
  178. package/dist/cli/transfer.js +33 -31
  179. package/dist/cli/usage.d.ts +0 -79
  180. package/dist/cli/usage.js +1 -695
  181. package/dist/cli/verb-row.d.ts +32 -0
  182. package/dist/cli/verb-row.js +6 -0
  183. package/dist/cli/verbs/health.d.ts +27 -0
  184. package/dist/cli/verbs/health.js +232 -0
  185. package/dist/cli/verbs/memory.d.ts +16 -0
  186. package/dist/cli/verbs/memory.js +244 -0
  187. package/dist/cli/verbs/objects.d.ts +20 -0
  188. package/dist/cli/verbs/objects.js +254 -0
  189. package/dist/cli/verbs/sessions.d.ts +31 -0
  190. package/dist/cli/verbs/sessions.js +312 -0
  191. package/dist/cli/verbs.d.ts +81 -0
  192. package/dist/cli/verbs.js +7 -0
  193. package/dist/cli/versioned-verbs.d.ts +51 -0
  194. package/dist/cli/versioned-verbs.js +79 -0
  195. package/dist/cli.d.ts +1 -272
  196. package/dist/cli.js +23 -313
  197. package/dist/connectors/dlq.d.ts +39 -3
  198. package/dist/connectors/dlq.js +37 -3
  199. package/dist/connectors/github/backfill.js +12 -3
  200. package/dist/connectors/github/dlq.js +31 -73
  201. package/dist/connectors/github/ingest.d.ts +2 -6
  202. package/dist/connectors/github/ingest.js +3 -28
  203. package/dist/connectors/github/webhook.d.ts +2 -1
  204. package/dist/connectors/github/webhook.js +33 -2
  205. package/dist/connectors/ingest.d.ts +10 -0
  206. package/dist/connectors/ingest.js +28 -0
  207. package/dist/connectors/slack/dlq.js +49 -71
  208. package/dist/connectors/slack/ingest.d.ts +2 -6
  209. package/dist/connectors/slack/ingest.js +2 -27
  210. package/dist/connectors/slack/webhook.d.ts +2 -1
  211. package/dist/connectors/slack/webhook.js +2 -1
  212. package/dist/consolidate/conflicts.js +1 -1
  213. package/dist/consolidate/dag.js +15 -9
  214. package/dist/consolidate/dedupe.d.ts +7 -0
  215. package/dist/consolidate/dedupe.js +5 -3
  216. package/dist/consolidate/llm-passes.js +45 -19
  217. package/dist/consolidate/memory-value.js +2 -1
  218. package/dist/consolidate/merge.js +2 -2
  219. package/dist/consolidate/physics-pass.d.ts +6 -1
  220. package/dist/consolidate/physics-pass.js +26 -6
  221. package/dist/consolidate/replay.js +11 -4
  222. package/dist/consolidate/sleep.js +8 -5
  223. package/dist/consolidate/traces.js +3 -3
  224. package/dist/core/ablation.d.ts +10 -0
  225. package/dist/core/ablation.js +12 -0
  226. package/dist/{sharing/scope.d.ts → core/active-scope.d.ts} +1 -1
  227. package/dist/{sharing/scope.js → core/active-scope.js} +1 -1
  228. package/dist/core/ambient.d.ts +4 -0
  229. package/dist/core/ambient.js +39 -18
  230. package/dist/core/api-errors.d.ts +16 -0
  231. package/dist/core/api-errors.js +20 -0
  232. package/dist/core/card.js +3 -1
  233. package/dist/core/compaction-timing.d.ts +7 -0
  234. package/dist/core/compaction-timing.js +9 -0
  235. package/dist/core/config.d.ts +1 -1
  236. package/dist/core/config.js +7 -2
  237. package/dist/core/cosine.d.ts +3 -0
  238. package/dist/core/cosine.js +19 -0
  239. package/dist/{store → core}/markdown.d.ts +1 -1
  240. package/dist/{store → core}/markdown.js +2 -2
  241. package/dist/core/memory-audit.d.ts +17 -0
  242. package/dist/core/memory-audit.js +54 -0
  243. package/dist/core/memory.d.ts +10 -4
  244. package/dist/core/memory.js +25 -18
  245. package/dist/{consolidate → core}/merged-row.d.ts +1 -1
  246. package/dist/{consolidate → core}/merged-row.js +1 -1
  247. package/dist/core/physics.js +6 -16
  248. package/dist/core/prediction.d.ts +3 -0
  249. package/dist/core/prediction.js +6 -0
  250. package/dist/core/project-identity.d.ts +3 -0
  251. package/dist/core/project-identity.js +10 -1
  252. package/dist/{store → core}/recall-scope.d.ts +1 -10
  253. package/dist/{store → core}/recall-scope.js +2 -19
  254. package/dist/db/busy.d.ts +2 -7
  255. package/dist/db/busy.js +21 -26
  256. package/dist/db/connect.d.ts +0 -1
  257. package/dist/db/connect.js +5 -6
  258. package/dist/db/index.d.ts +3 -3
  259. package/dist/db/index.js +3 -3
  260. package/dist/db/migrations/index.d.ts +1 -1
  261. package/dist/db/migrations/index.js +3 -1
  262. package/dist/db/migrations/v55.d.ts +3 -0
  263. package/dist/db/migrations/v55.js +17 -0
  264. package/dist/db/open.d.ts +0 -2
  265. package/dist/db/open.js +6 -10
  266. package/dist/db/physics-state.js +3 -3
  267. package/dist/db/tables.d.ts +2 -0
  268. package/dist/db/tables.js +6 -2
  269. package/dist/db/vector-store.js +17 -4
  270. package/dist/db/wal-checkpointer.js +2 -1
  271. package/dist/doctor.js +11 -8
  272. package/dist/embeddings/local.d.ts +62 -0
  273. package/dist/embeddings/local.js +146 -0
  274. package/dist/{store/embeddings → embeddings}/provider.d.ts +5 -2
  275. package/dist/{store/embeddings → embeddings}/provider.js +18 -15
  276. package/dist/embeddings/transformers.d.ts +26 -0
  277. package/dist/embeddings/transformers.js +89 -0
  278. package/dist/entry/session-text.d.ts +1 -1
  279. package/dist/entry/session-text.js +1 -1
  280. package/dist/eval/eval-suite.js +17 -11
  281. package/dist/eval/eval.js +1 -1
  282. package/dist/eval/judgment.js +11 -4
  283. package/dist/graph/extract.js +3 -3
  284. package/dist/graph/recall.js +1 -1
  285. package/dist/graph/view.js +13 -5
  286. package/dist/graph/write.d.ts +1 -1
  287. package/dist/graph/write.js +9 -9
  288. package/dist/hooks/codex-session.js +3 -2
  289. package/dist/hooks/codex-wrapper.js +2 -1
  290. package/dist/hooks/json-hooks.js +4 -2
  291. package/dist/hooks/opencode.js +8 -5
  292. package/dist/hooks/shared.d.ts +0 -6
  293. package/dist/hooks/shared.js +4 -15
  294. package/dist/importers/core.js +1 -1
  295. package/dist/importers/sources.js +5 -4
  296. package/dist/importers/vault.js +7 -5
  297. package/dist/index.d.ts +3 -2
  298. package/dist/index.js +3 -2
  299. package/dist/learn/autolearn.d.ts +10 -2
  300. package/dist/learn/autolearn.js +32 -12
  301. package/dist/learn/churn-git.js +13 -4
  302. package/dist/learn/extract.d.ts +3 -2
  303. package/dist/learn/extract.js +6 -4
  304. package/dist/learn/forward-claim-detector.js +4 -2
  305. package/dist/learn/invalidation.js +10 -6
  306. package/dist/mcp/admin-tools.d.ts +2 -2
  307. package/dist/mcp/admin-tools.js +27 -48
  308. package/dist/mcp/format.js +3 -2
  309. package/dist/mcp/framing.d.ts +1 -0
  310. package/dist/mcp/framing.js +1 -1
  311. package/dist/mcp/memory-tools.js +3 -1
  312. package/dist/mcp/protocol.d.ts +11 -7
  313. package/dist/mcp/protocol.js +25 -12
  314. package/dist/mcp/recall-tools.js +12 -9
  315. package/dist/mcp/request.js +2 -1
  316. package/dist/mcp/stdio.js +24 -8
  317. package/dist/mcp/tool-args.js +2 -1
  318. package/dist/mcp/tools.js +44 -21
  319. package/dist/objects/customer-notes.d.ts +2 -2
  320. package/dist/objects/decisions.d.ts +2 -2
  321. package/dist/objects/descriptor.d.ts +1 -1
  322. package/dist/objects/incidents.d.ts +2 -2
  323. package/dist/objects/lifecycle.d.ts +1 -1
  324. package/dist/objects/lifecycle.js +2 -1
  325. package/dist/objects/policies.d.ts +2 -2
  326. package/dist/objects/policies.js +2 -1
  327. package/dist/objects/processes.d.ts +2 -2
  328. package/dist/objects/project-briefs.d.ts +2 -2
  329. package/dist/objects/project-briefs.js +2 -3
  330. package/dist/objects/skills.d.ts +2 -2
  331. package/dist/postinstall.js +1 -1
  332. package/dist/rerankers/clef.js +3 -6
  333. package/dist/rerankers/cross-encoder.d.ts +31 -2
  334. package/dist/rerankers/cross-encoder.js +63 -97
  335. package/dist/rerankers/index.js +5 -4
  336. package/dist/rerankers/jev.js +2 -4
  337. package/dist/rerankers/outage-warning.d.ts +3 -1
  338. package/dist/rerankers/outage-warning.js +5 -3
  339. package/dist/search/bm25-search.js +1 -1
  340. package/dist/search/boosts.js +1 -1
  341. package/dist/search/embed-memories.d.ts +9 -0
  342. package/dist/search/embed-memories.js +377 -0
  343. package/dist/search/goal-boost.d.ts +1 -1
  344. package/dist/search/goal-boost.js +6 -3
  345. package/dist/search/hybrid.d.ts +9 -2
  346. package/dist/search/hybrid.js +9 -4
  347. package/dist/search/physics-search.d.ts +6 -3
  348. package/dist/search/physics-search.js +22 -15
  349. package/dist/search/rerank.js +1 -1
  350. package/dist/search/vector.d.ts +8 -6
  351. package/dist/search/vector.js +10 -13
  352. package/dist/server/auth.d.ts +2 -0
  353. package/dist/server/auth.js +17 -7
  354. package/dist/server/boot.js +7 -66
  355. package/dist/server/client-ip.d.ts +0 -2
  356. package/dist/server/client-ip.js +3 -2
  357. package/dist/server/deadline.d.ts +0 -2
  358. package/dist/server/deadline.js +1 -7
  359. package/dist/server/defaults.d.ts +1 -0
  360. package/dist/server/defaults.js +1 -0
  361. package/dist/server/mcp-http.js +16 -11
  362. package/dist/server/request.js +7 -2
  363. package/dist/server/route-checks.d.ts +7 -0
  364. package/dist/server/route-checks.js +42 -0
  365. package/dist/server/route-table.d.ts +9 -8
  366. package/dist/server/route-table.js +127 -132
  367. package/dist/server/routes/admin.d.ts +8 -8
  368. package/dist/server/routes/admin.js +13 -21
  369. package/dist/server/routes/customer-notes.d.ts +6 -6
  370. package/dist/server/routes/customer-notes.js +1 -2
  371. package/dist/server/routes/decisions.d.ts +6 -6
  372. package/dist/server/routes/decisions.js +2 -4
  373. package/dist/server/routes/incidents.d.ts +6 -6
  374. package/dist/server/routes/incidents.js +2 -5
  375. package/dist/server/routes/memories.d.ts +9 -9
  376. package/dist/server/routes/memories.js +15 -38
  377. package/dist/server/routes/object-routes.d.ts +6 -6
  378. package/dist/server/routes/object-routes.js +4 -7
  379. package/dist/server/routes/policies.d.ts +7 -7
  380. package/dist/server/routes/policies.js +2 -4
  381. package/dist/server/routes/predictions.d.ts +6 -6
  382. package/dist/server/routes/predictions.js +9 -13
  383. package/dist/server/routes/processes.d.ts +6 -6
  384. package/dist/server/routes/processes.js +1 -2
  385. package/dist/server/routes/project-briefs.d.ts +7 -7
  386. package/dist/server/routes/project-briefs.js +2 -4
  387. package/dist/server/routes/recall.d.ts +5 -5
  388. package/dist/server/routes/recall.js +6 -10
  389. package/dist/server/routes/skills.d.ts +7 -7
  390. package/dist/server/routes/skills.js +2 -4
  391. package/dist/server/server-detect.d.ts +7 -1
  392. package/dist/server/server-detect.js +36 -11
  393. package/dist/server/sleep-child.js +26 -7
  394. package/dist/server/sleep-offload.d.ts +11 -5
  395. package/dist/server/sleep-offload.js +17 -11
  396. package/dist/server/types.d.ts +22 -10
  397. package/dist/server/validation.d.ts +1 -2
  398. package/dist/server/validation.js +6 -11
  399. package/dist/server.d.ts +5 -4
  400. package/dist/server.js +5 -4
  401. package/dist/sharing/global-store.js +2 -2
  402. package/dist/sharing/global-sync.js +38 -45
  403. package/dist/sharing/project-merge.d.ts +7 -14
  404. package/dist/sharing/project-merge.js +57 -121
  405. package/dist/sharing/search-both.js +1 -1
  406. package/dist/sharing/share.d.ts +2 -0
  407. package/dist/sharing/share.js +37 -34
  408. package/dist/store/ambient.js +5 -6
  409. package/dist/store/audit-event.d.ts +1 -1
  410. package/dist/store/audit.d.ts +0 -16
  411. package/dist/store/audit.js +4 -55
  412. package/dist/store/auth.d.ts +4 -2
  413. package/dist/store/auth.js +7 -3
  414. package/dist/store/candidates.d.ts +3 -2
  415. package/dist/store/candidates.js +42 -23
  416. package/dist/store/capture-write.d.ts +17 -0
  417. package/dist/store/capture-write.js +35 -0
  418. package/dist/store/cards.js +1 -1
  419. package/dist/store/compaction-caller.d.ts +18 -0
  420. package/dist/store/compaction-caller.js +29 -0
  421. package/dist/store/compactions-record.d.ts +115 -0
  422. package/dist/store/compactions-record.js +417 -0
  423. package/dist/store/compactions.js +6 -4
  424. package/dist/store/conflicts.js +7 -4
  425. package/dist/store/connectors/dlq.d.ts +10 -0
  426. package/dist/store/connectors/dlq.js +32 -0
  427. package/dist/store/connectors/github.d.ts +7 -9
  428. package/dist/store/connectors/github.js +30 -53
  429. package/dist/store/connectors/slack.d.ts +2 -11
  430. package/dist/store/connectors/slack.js +18 -47
  431. package/dist/store/dashboard-reads.js +7 -6
  432. package/dist/store/delete-and-batch.d.ts +6 -0
  433. package/dist/store/delete-and-batch.js +36 -3
  434. package/dist/store/delivery-recorder.d.ts +10 -3
  435. package/dist/store/delivery-recorder.js +12 -5
  436. package/dist/store/descendant-page.js +6 -3
  437. package/dist/store/dormant.d.ts +1 -1
  438. package/dist/store/dormant.js +3 -1
  439. package/dist/store/embeddings/index.d.ts +2 -12
  440. package/dist/store/embeddings/index.js +5 -374
  441. package/dist/store/entry-reads.d.ts +13 -4
  442. package/dist/store/entry-reads.js +47 -11
  443. package/dist/store/entry-writes.d.ts +5 -5
  444. package/dist/store/entry-writes.js +32 -12
  445. package/dist/store/failure-log-at.d.ts +8 -0
  446. package/dist/store/failure-log-at.js +16 -0
  447. package/dist/store/failure-log.d.ts +2 -2
  448. package/dist/store/failure-log.js +5 -3
  449. package/dist/{trust → store}/gated-write.d.ts +3 -1
  450. package/dist/{trust → store}/gated-write.js +7 -5
  451. package/dist/store/goals.js +20 -18
  452. package/dist/store/graph-reads.js +4 -2
  453. package/dist/store/graph-writes.d.ts +6 -6
  454. package/dist/store/graph-writes.js +48 -71
  455. package/dist/store/handoffs.d.ts +1 -1
  456. package/dist/store/handoffs.js +6 -6
  457. package/dist/store/index-and-stats.js +2 -2
  458. package/dist/store/key-writes.js +1 -1
  459. package/dist/store/ledger-turn.d.ts +9 -0
  460. package/dist/store/ledger-turn.js +38 -0
  461. package/dist/store/mirrors.js +10 -6
  462. package/dist/store/open.d.ts +0 -4
  463. package/dist/store/open.js +40 -53
  464. package/dist/store/pilot-arm.d.ts +12 -0
  465. package/dist/store/pilot-arm.js +42 -0
  466. package/dist/store/port.d.ts +9 -3
  467. package/dist/store/port.js +8 -0
  468. package/dist/store/predictions.d.ts +1 -2
  469. package/dist/store/predictions.js +3 -6
  470. package/dist/store/project-tags.d.ts +62 -0
  471. package/dist/store/project-tags.js +67 -0
  472. package/dist/store/quarantine.js +2 -1
  473. package/dist/store/recall-trace.d.ts +2 -0
  474. package/dist/store/recall-trace.js +18 -2
  475. package/dist/store/rejected-values.js +2 -1
  476. package/dist/store/rejection-probe.d.ts +4 -0
  477. package/dist/store/rejection-probe.js +19 -0
  478. package/dist/store/rejection.d.ts +0 -17
  479. package/dist/store/rejection.js +1 -21
  480. package/dist/store/rule-sql.d.ts +9 -0
  481. package/dist/store/rule-sql.js +21 -2
  482. package/dist/store/search-rows.js +9 -4
  483. package/dist/store/sessions.js +4 -2
  484. package/dist/store/set-aside.d.ts +14 -0
  485. package/dist/store/set-aside.js +23 -0
  486. package/dist/store/sqlite/busy.d.ts +3 -0
  487. package/dist/store/sqlite/busy.js +8 -0
  488. package/dist/store/sqlite/connector-events-group.js +4 -7
  489. package/dist/store/sqlite/connector-writes-group.js +3 -3
  490. package/dist/store/sqlite/entry-writes-group.js +2 -2
  491. package/dist/store/sqlite/error-codec.js +4 -3
  492. package/dist/store/sqlite/executor.js +10 -4
  493. package/dist/store/sqlite/graph-reads-group.js +1 -1
  494. package/dist/store/sqlite/held-connection.d.ts +9 -0
  495. package/dist/store/sqlite/held-connection.js +41 -0
  496. package/dist/store/sqlite/object-rows.d.ts +1 -1
  497. package/dist/store/sqlite/objects-group.d.ts +1 -1
  498. package/dist/store/sqlite/objects-group.js +1 -1
  499. package/dist/store/sqlite/store.js +14 -12
  500. package/dist/store/summaries.d.ts +3 -3
  501. package/dist/store/summaries.js +5 -4
  502. package/dist/store/token-ledger.d.ts +0 -7
  503. package/dist/store/token-ledger.js +0 -35
  504. package/dist/store/vector-index.js +3 -2
  505. package/dist/support-bundle.js +2 -3
  506. package/dist/trust/reject-flow.js +2 -2
  507. package/dist/util/agent-homes.d.ts +15 -0
  508. package/dist/util/agent-homes.js +34 -0
  509. package/dist/util/anthropic-messages.d.ts +3 -0
  510. package/dist/util/anthropic-messages.js +22 -5
  511. package/dist/util/chunked.d.ts +3 -0
  512. package/dist/util/chunked.js +10 -0
  513. package/dist/util/ellipsize.d.ts +3 -0
  514. package/dist/util/ellipsize.js +5 -0
  515. package/dist/util/env.d.ts +2 -1
  516. package/dist/util/env.js +2 -1
  517. package/dist/util/format.d.ts +3 -0
  518. package/dist/util/format.js +6 -0
  519. package/dist/util/hook-payload.d.ts +8 -0
  520. package/dist/util/hook-payload.js +37 -0
  521. package/dist/util/http-retry.d.ts +6 -0
  522. package/dist/util/http-retry.js +43 -5
  523. package/dist/util/http-util.d.ts +0 -8
  524. package/dist/util/http-util.js +10 -21
  525. package/dist/util/limits.d.ts +7 -0
  526. package/dist/util/limits.js +14 -0
  527. package/dist/util/log.d.ts +4 -0
  528. package/dist/util/log.js +9 -0
  529. package/dist/util/request-scope.d.ts +3 -1
  530. package/dist/util/request-scope.js +11 -6
  531. package/dist/util/secret-detect.js +6 -1
  532. package/dist/util/sqlite-blocked.d.ts +2 -0
  533. package/dist/util/sqlite-blocked.js +5 -0
  534. package/dist/util/stream-tee.d.ts +3 -0
  535. package/dist/util/stream-tee.js +35 -0
  536. package/dist/util/time.d.ts +4 -0
  537. package/dist/util/time.js +4 -0
  538. package/dist/util/version.d.ts +1 -1
  539. package/dist/util/version.js +1 -1
  540. package/extensions/openclaw-plugin/openclaw.plugin.json +1 -1
  541. package/extensions/openclaw-plugin/package.json +1 -1
  542. package/openclaw.plugin.json +1 -1
  543. package/package.json +2 -2
  544. package/dist/store/embeddings/local.d.ts +0 -15
  545. package/dist/store/embeddings/local.js +0 -175
  546. /package/dist/{cli → api}/correction-latency.d.ts +0 -0
  547. /package/dist/{cli → api}/correction-latency.js +0 -0
  548. /package/dist/{cli → api}/provenance-coverage.d.ts +0 -0
  549. /package/dist/{cli → api}/provenance-coverage.js +0 -0
  550. /package/dist/{store → core}/object-types.d.ts +0 -0
  551. /package/dist/{store → core}/object-types.js +0 -0
  552. /package/dist/{capture → util}/compaction-items.d.ts +0 -0
  553. /package/dist/{capture → util}/compaction-items.js +0 -0
  554. /package/dist/{store → util}/yaml.d.ts +0 -0
  555. /package/dist/{store → util}/yaml.js +0 -0
@@ -0,0 +1,377 @@
1
+ // Embeds memories into the vector index: picks the configured provider, computes each vector and hands it to the store,
2
+ // which only keeps it (src/store/port.ts VectorWrites).
3
+ import * as fs from 'fs';
4
+ import * as path from 'path';
5
+ import { randomUUID } from 'crypto';
6
+ import { loadConfig } from '../core/config.js';
7
+ import { initializeParticle } from '../db/physics-state.js';
8
+ import { resolveEmbeddingProvider } from '../embeddings/provider.js';
9
+ import { embeddingIndexIdentity, embeddingInputText, embeddingModelRequiresReindex, indexNeedsRebuild, saveStoredEmbeddingModel, } from '../store/embeddings/index.js';
10
+ import { loadAllEntryIds, loadEntriesByIds } from '../store/entry-reads.js';
11
+ import { requireGroup, } from '../store/port.js';
12
+ import { indexedModel, pruneStoredVectors, replacesIndex, resetStoredParticles, saveEmbeddingIndex, saveStoredVectors, seedStoredParticle, } from '../store/vector-index.js';
13
+ import { chunked } from '../util/chunked.js';
14
+ import { envByName } from '../util/env.js';
15
+ import { errorCode, errorMessage, log } from '../util/log.js';
16
+ import { redactSecretsStrict } from '../util/secret-detect.js';
17
+ const STORE_PAGE = 64;
18
+ /** The rows for `ids`, a page at a time and in that order; an id deleted since it was listed is skipped. */
19
+ function* entryPages(hippoRoot, ids) {
20
+ for (const page of chunked(ids, STORE_PAGE)) {
21
+ const byId = new Map(loadEntriesByIds(hippoRoot, page).map((e) => [e.id, e]));
22
+ const entries = page.flatMap((id) => byId.get(id) ?? []);
23
+ if (entries.length > 0)
24
+ yield entries;
25
+ }
26
+ }
27
+ async function rebuildEmbeddingIndex(hippoRoot, ids, provider) {
28
+ const rebuilt = {};
29
+ for (const entries of entryPages(hippoRoot, ids)) {
30
+ // On a hard transport/auth failure provider.embed throws, so the caller aborts WITHOUT saving
31
+ // a partial index (atomic reindex: the old index + stored identity are preserved).
32
+ const vectors = await provider.embed(entries.map((e) => embeddingInputText(e)), 'passage');
33
+ for (let i = 0; i < entries.length; i++) {
34
+ const vec = vectors[i];
35
+ if (vec && vec.length > 0) {
36
+ rebuilt[entries[i].id] = vec;
37
+ }
38
+ else {
39
+ noteSkippedEmbedding(entries[i].id);
40
+ }
41
+ }
42
+ }
43
+ return rebuilt;
44
+ }
45
+ function resetPhysicsFromIndex(hippoRoot, ids, index) {
46
+ try {
47
+ resetStoredParticles(hippoRoot, ids, index);
48
+ }
49
+ catch (err) {
50
+ // Best effort: retrieval still falls back without physics state.
51
+ log.warn(`physics reset after reindex failed: ${errorMessage(err)}`);
52
+ }
53
+ }
54
+ /** A provider's `[]` row is a swallowed per-item failure; name the memory so the gap can be traced. */
55
+ function noteSkippedEmbedding(id) {
56
+ log.warn('memory not embedded; the next embed run retries it', { id });
57
+ }
58
+ const EMBED_LOCK_FILE = 'embeddings.lock';
59
+ const EMBED_LOCK_WAIT_MS = 10_000;
60
+ const EMBED_LOCK_OWNER = `${process.pid}:${randomUUID()}`;
61
+ // In-process mutex plus an O_EXCL "<pid>:<token>" lock file: our token is a lock we leaked; our PID
62
+ // with another token is a live worker thread, unless the lock predates this process (a reused PID).
63
+ let _embedWriteLock = Promise.resolve();
64
+ function embedLockHolderAlive(lockPath) {
65
+ let raw;
66
+ try {
67
+ raw = fs.readFileSync(lockPath, 'utf8');
68
+ }
69
+ catch (err) {
70
+ return !(err instanceof Error && 'code' in err && err.code === 'ENOENT');
71
+ }
72
+ const mtimeMs = fs.statSync(lockPath, { throwIfNoEntry: false })?.mtimeMs ?? 0;
73
+ const pid = Number(raw.split(':')[0]);
74
+ // An empty lock is a holder between create and write; after 5 s it is a crashed one.
75
+ if (!Number.isInteger(pid) || pid <= 0)
76
+ return Date.now() - mtimeMs < 5_000;
77
+ if (raw === EMBED_LOCK_OWNER)
78
+ return false;
79
+ if (pid === process.pid)
80
+ return mtimeMs >= Date.now() - process.uptime() * 1000;
81
+ try {
82
+ process.kill(pid, 0);
83
+ return true;
84
+ }
85
+ catch (err) {
86
+ return err instanceof Error && 'code' in err && err.code === 'EPERM';
87
+ }
88
+ }
89
+ // After the double-break race below the file can be another writer's lock, so remove only our own.
90
+ function releaseEmbedFileLock(lockPath) {
91
+ let raw;
92
+ try {
93
+ raw = fs.readFileSync(lockPath, 'utf8');
94
+ }
95
+ catch (err) {
96
+ if (err instanceof Error && 'code' in err && err.code === 'ENOENT')
97
+ return;
98
+ throw err;
99
+ }
100
+ if (raw === EMBED_LOCK_OWNER)
101
+ fs.rmSync(lockPath, { force: true });
102
+ }
103
+ async function acquireEmbedFileLock(hippoRoot) {
104
+ const lockPath = path.join(hippoRoot, EMBED_LOCK_FILE);
105
+ const deadline = Date.now() + EMBED_LOCK_WAIT_MS;
106
+ for (;;) {
107
+ try {
108
+ fs.writeFileSync(lockPath, EMBED_LOCK_OWNER, { flag: 'wx' });
109
+ return () => releaseEmbedFileLock(lockPath);
110
+ }
111
+ catch (err) {
112
+ if (!(err instanceof Error && 'code' in err && err.code === 'EEXIST'))
113
+ throw err;
114
+ }
115
+ // SHORTCUT: two waiters can both break one dead holder's lock; a vector lost that way is backfilled by the next `hippo embed`.
116
+ if (!embedLockHolderAlive(lockPath)) {
117
+ fs.rmSync(lockPath, { force: true });
118
+ continue;
119
+ }
120
+ if (Date.now() >= deadline)
121
+ throw new Error(`the embedding index is busy: another hippo process holds ${lockPath}`);
122
+ await new Promise((r) => setTimeout(r, 50));
123
+ }
124
+ }
125
+ async function withEmbedLock(hippoRoot, fn) {
126
+ return withProcessEmbedLock(async () => {
127
+ const release = await acquireEmbedFileLock(hippoRoot);
128
+ try {
129
+ return await fn();
130
+ }
131
+ finally {
132
+ release();
133
+ }
134
+ });
135
+ }
136
+ // A store keeps each write whole in its own transaction, and a lock file in hippoRoot would not reach a worker on another host.
137
+ async function withProcessEmbedLock(fn) {
138
+ let resolve;
139
+ const next = new Promise(r => { resolve = r; });
140
+ const prev = _embedWriteLock;
141
+ _embedWriteLock = next;
142
+ await prev;
143
+ try {
144
+ return await fn();
145
+ }
146
+ finally {
147
+ resolve();
148
+ }
149
+ }
150
+ // A bad key fails every write; one full warning per distinct failure tells the user, and a count of the repeats follows at most once per interval.
151
+ const EMBED_WARN_KEY_CAP = 64;
152
+ const EMBED_SUPPRESSED_SUMMARY_INTERVAL_MS = 60_000;
153
+ const embedFailureSeen = new Map();
154
+ function embedFailureKey(source, cause) {
155
+ const kind = errorCode(cause) || (cause instanceof Error ? cause.constructor.name : 'NonError');
156
+ return `${source}:${kind}`;
157
+ }
158
+ function warnEmbedFailureOnce(source, rawMessage, cause) {
159
+ const key = embedFailureKey(source, cause);
160
+ const now = Date.now();
161
+ const seen = embedFailureSeen.get(key);
162
+ if (seen) {
163
+ seen.suppressed += 1;
164
+ if (now - seen.lastNoticeAt >= EMBED_SUPPRESSED_SUMMARY_INTERVAL_MS) {
165
+ log.warn(`${seen.suppressed} more embedding errors from ${source} since the last notice; memories are still stored without embeddings.`);
166
+ seen.suppressed = 0;
167
+ seen.lastNoticeAt = now;
168
+ }
169
+ return;
170
+ }
171
+ // Oldest key out, so a full map still lets a new failure warn.
172
+ if (embedFailureSeen.size >= EMBED_WARN_KEY_CAP) {
173
+ for (const oldest of embedFailureSeen.keys()) {
174
+ embedFailureSeen.delete(oldest);
175
+ break;
176
+ }
177
+ }
178
+ embedFailureSeen.set(key, { lastNoticeAt: now, suppressed: 0 });
179
+ // Strict scrub: this line can land in a hook log file, and an API may echo the key back in its error body.
180
+ const message = redactSecretsStrict(rawMessage).replace(/\s+/g, ' ').replace(/\.+$/, '');
181
+ log.warn(`embedding failed (${source}): ${message}. Memories are stored without embeddings until this is fixed.`);
182
+ }
183
+ function vectorGroups(store) {
184
+ return [requireGroup(store, 'vectors'), requireGroup(store, 'vectorWrites')];
185
+ }
186
+ /** Writes one page's vectors, one writeVectors per tenant, and stops at the first refused write; `withParticles` seeds each memory's particle. */
187
+ async function writeVectorPage(writes, write, page, vectors, withParticles) {
188
+ const byTenant = new Map();
189
+ page.forEach((entry, i) => {
190
+ const vector = vectors[i];
191
+ if (!vector || vector.length === 0)
192
+ return noteSkippedEmbedding(entry.id);
193
+ const row = withParticles ? { memoryId: entry.id, vector, particle: initializeParticle(entry, vector) } : { memoryId: entry.id, vector };
194
+ byTenant.set(entry.tenantId, [...(byTenant.get(entry.tenantId) ?? []), row]);
195
+ });
196
+ let written = 0;
197
+ for (const [tenantId, rows] of byTenant) {
198
+ const result = await writes.writeVectors({ ...write, tenantId, rows });
199
+ if (result.modelMismatch)
200
+ return { written, modelMismatch: true };
201
+ written += result.written;
202
+ }
203
+ return { written, modelMismatch: false };
204
+ }
205
+ const OTHER_MODEL_INDEX = "the vector index was built by another embedding model; run 'hippo embed' to rebuild it";
206
+ /** Embeds every memory with no vector under `model`, page by page, so a provider failure keeps the pages written; a rebuild reseeds the particles it drops. */
207
+ async function backfillInStore(writes, provider, model, rebuild) {
208
+ let count = 0;
209
+ let afterId;
210
+ for (;;) {
211
+ const page = await writes.entriesWithoutVector({ model, afterId, limit: STORE_PAGE });
212
+ if (page.length === 0)
213
+ return count;
214
+ afterId = page[page.length - 1].id;
215
+ const vectors = await provider.embed(page.map((e) => embeddingInputText(e)), 'passage');
216
+ const result = await writeVectorPage(writes, { model, replaceIndex: rebuild }, page, vectors, rebuild);
217
+ // Another process rebuilt the index mid-run; every later page would be refused too, after paying the provider for it.
218
+ if (result.modelMismatch)
219
+ throw new Error(OTHER_MODEL_INDEX);
220
+ count += result.written;
221
+ }
222
+ }
223
+ /** embedAll on a store: a model change re-embeds every memory, and the first page written drops the old index. */
224
+ async function embedAllInStore(store, provider) {
225
+ const [reads, writes] = vectorGroups(store);
226
+ const rebuild = indexNeedsRebuild(indexedModel(await reads.embeddingIndexState()), provider.id);
227
+ return backfillInStore(writes, provider, embeddingIndexIdentity(provider.id), rebuild);
228
+ }
229
+ /** embedMemory on a store; like the hippo.db path it resolves whatever fails. Unlike it, it leaves a model change to embedAll, so a stale
230
+ * read here never drops another process's rebuild; the read only saves a paid embed call, as the write refuses another model itself. */
231
+ function embedMemoryInStore(store, provider, entry) {
232
+ return withProcessEmbedLock(async () => {
233
+ const [reads, writes] = vectorGroups(store);
234
+ const model = embeddingIndexIdentity(provider.id);
235
+ try {
236
+ const refused = replacesIndex(await reads.embeddingIndexState(), model)
237
+ || (await writeVectorPage(writes, { model, replaceIndex: false }, [entry], await provider.embed([embeddingInputText(entry)], 'passage'), true)).modelMismatch;
238
+ if (refused)
239
+ warnEmbedFailureOnce('index', OTHER_MODEL_INDEX);
240
+ }
241
+ catch (err) {
242
+ warnEmbedFailureOnce(provider.kind, errorMessage(err), err);
243
+ }
244
+ }).catch((err) => {
245
+ log.warn(`skipped embedding ${entry.id} (${errorMessage(err)})`);
246
+ });
247
+ }
248
+ /** Embed a single memory entry and cache the result in the embedding index; with `store`, through its vectorWrites, never opening hippo.db. */
249
+ export async function embedMemory(hippoRoot, entry, model, store) {
250
+ let provider;
251
+ try {
252
+ provider = resolveEmbeddingProvider(hippoRoot, { model });
253
+ }
254
+ catch (err) {
255
+ // Callers fire and forget, so this must resolve: a bad config warns once instead of rejecting.
256
+ warnEmbedFailureOnce('config', errorMessage(err), err);
257
+ return;
258
+ }
259
+ if (!provider.isAvailable())
260
+ return;
261
+ if (store)
262
+ return embedMemoryInStore(store, provider, entry);
263
+ return withEmbedLock(hippoRoot, async () => {
264
+ // Best-effort: an embedding failure (API down, bad key, 5xx) must not reject the caller's write; the existing index is left as-is.
265
+ // Failures surface on the explicit `hippo embed` / `embedAll` path.
266
+ try {
267
+ const identity = provider.id;
268
+ if (embeddingModelRequiresReindex(hippoRoot, identity)) {
269
+ await rebuildIndexForProvider(hippoRoot, provider);
270
+ return;
271
+ }
272
+ const text = embeddingInputText(entry);
273
+ const [vector] = await provider.embed([text], 'passage');
274
+ if (!vector || vector.length === 0)
275
+ return;
276
+ saveStoredVectors(hippoRoot, [[entry.id, vector]], embeddingIndexIdentity(identity));
277
+ saveStoredEmbeddingModel(hippoRoot, identity);
278
+ // Initialize physics state for this memory
279
+ initializePhysicsIfMissing(hippoRoot, entry, vector);
280
+ }
281
+ catch (err) {
282
+ // Provider failure (API down / bad key). Best-effort: leave the index as-is, but say so once.
283
+ warnEmbedFailureOnce(provider.kind, errorMessage(err), err);
284
+ }
285
+ }).catch((err) => {
286
+ log.warn(`skipped embedding ${entry.id} (${errorMessage(err)}); run 'hippo embed' to backfill`);
287
+ });
288
+ }
289
+ // Every page is embedded before one transaction replaces the index, and the identity is saved only after that: a provider failure or a crash
290
+ // at any earlier point leaves the old identity, so embeddingModelRequiresReindex has the next run rebuild from the start.
291
+ async function rebuildIndexForProvider(hippoRoot, provider) {
292
+ const identity = provider.id;
293
+ // Host-wide rebuild: one embedding index per hippoRoot, keyed by tenant-scoped entry.id; per-tenant indices would be a larger architecture change.
294
+ const ids = loadAllEntryIds(hippoRoot);
295
+ const rebuiltIndex = await rebuildEmbeddingIndex(hippoRoot, ids, provider);
296
+ saveEmbeddingIndex(hippoRoot, rebuiltIndex, embeddingIndexIdentity(identity));
297
+ saveStoredEmbeddingModel(hippoRoot, identity);
298
+ resetPhysicsFromIndex(hippoRoot, ids, rebuiltIndex);
299
+ return Object.keys(rebuiltIndex).length;
300
+ }
301
+ function initializePhysicsIfMissing(hippoRoot, entry, vector) {
302
+ try {
303
+ seedStoredParticle(hippoRoot, entry, vector);
304
+ }
305
+ catch (err) {
306
+ // Physics init is best-effort and must not fail the embedding that just landed.
307
+ log.debug(`physics state not initialised for ${entry.id}: ${errorMessage(err)}`);
308
+ }
309
+ }
310
+ /** Throws when an unavailable provider is a misconfiguration rather than an intentional no-op. */
311
+ function throwIfProviderKeyMissing(hippoRoot, provider) {
312
+ // A configured API provider with a missing key is a misconfiguration: surface it so callers of the exported embedAll() learn nothing was written.
313
+ // Local-not-installed and an explicit enabled=false stay silent no-ops.
314
+ const cfg = loadConfig(hippoRoot).embeddings;
315
+ if (provider.kind !== 'local' &&
316
+ cfg.enabled !== false &&
317
+ provider.keyEnv &&
318
+ !envByName(provider.keyEnv)?.trim()) {
319
+ throw new Error(`Embedding provider '${provider.kind}' is configured but ${provider.keyEnv} is not set.`);
320
+ }
321
+ }
322
+ // Embeds entries without a cached vector in save-checkpointed chunks; a `[]` row means that item could not be embedded and is retried next run.
323
+ // On a hard provider failure mid-backfill the chunks already embedded are persisted (paid progress), then it stops and resumes next run.
324
+ async function backfillPending(hippoRoot, provider, pending, model) {
325
+ let count = 0;
326
+ // Initialized to `undefined` so it stays a plain `unknown` binding for the arbitrary caught value; falsy either way.
327
+ let backfillError = undefined;
328
+ for (const chunk of entryPages(hippoRoot, pending)) {
329
+ let vectors;
330
+ try {
331
+ vectors = await provider.embed(chunk.map((e) => embeddingInputText(e)), 'passage');
332
+ }
333
+ catch (err) {
334
+ // Preserve the chunks already saved this run, then surface the failure
335
+ // below so the explicit `hippo embed` path never reports a false success.
336
+ backfillError = err;
337
+ break;
338
+ }
339
+ const rows = [];
340
+ for (let j = 0; j < chunk.length; j++) {
341
+ const vec = vectors[j];
342
+ if (vec && vec.length > 0)
343
+ rows.push([chunk[j].id, vec]);
344
+ else
345
+ noteSkippedEmbedding(chunk[j].id);
346
+ }
347
+ count += saveStoredVectors(hippoRoot, rows, model);
348
+ }
349
+ return { count, backfillError };
350
+ }
351
+ /** Embeds every entry with no cached vector, prunes vectors of deleted memories, and returns how many it embedded; `provider` defaults to the
352
+ * configured one. With `store`, every tenant's memories with no vector for the provider's model go through its `vectorWrites`, never hippo.db. */
353
+ export async function embedAll(hippoRoot, model, provider = resolveEmbeddingProvider(hippoRoot, { model }), store) {
354
+ if (!provider.isAvailable()) {
355
+ throwIfProviderKeyMissing(hippoRoot, provider);
356
+ return 0;
357
+ }
358
+ if (store)
359
+ return withProcessEmbedLock(() => embedAllInStore(store, provider));
360
+ return withEmbedLock(hippoRoot, async () => {
361
+ const identity = provider.id;
362
+ if (embeddingModelRequiresReindex(hippoRoot, identity))
363
+ return rebuildIndexForProvider(hippoRoot, provider);
364
+ // Host-wide by design: embedAll backfills all tenants' entries into the per-host index; per-tenant filtering would produce partial indices and break
365
+ // recall.
366
+ const embedded = pruneStoredVectors(hippoRoot);
367
+ const pending = loadAllEntryIds(hippoRoot).filter((id) => !embedded.has(id));
368
+ const { count, backfillError } = await backfillPending(hippoRoot, provider, pending, embeddingIndexIdentity(identity));
369
+ saveStoredEmbeddingModel(hippoRoot, identity);
370
+ // Partial progress is now persisted; surface a hard backfill failure so the
371
+ // explicit embed path reports it (best-effort callers go via embedMemory).
372
+ if (backfillError)
373
+ throw backfillError;
374
+ return count;
375
+ });
376
+ }
377
+ //# sourceMappingURL=embed-memories.js.map
@@ -1,4 +1,4 @@
1
- import type { MemoryEntry } from '../core/memory.js';
1
+ import { type MemoryEntry } from '../core/memory.js';
2
2
  import type { RerankStep } from '../core/search-types.js';
3
3
  import type { ActiveGoals, GoalRecallLogRow } from '../store/goals.js';
4
4
  /** Options of {@link boostByGoals}. */
@@ -1,10 +1,13 @@
1
1
  // src/search/goal-boost.ts: the goal-stack boost over goals already read; it ranks rows and touches no store.
2
2
  import { compareScoresDesc } from '../core/compare.js';
3
+ import { DEFAULT_SCHEMA_FIT } from '../core/memory.js';
3
4
  const MAX_FINAL_MULTIPLIER = 3.0;
5
+ const FIRST_MATCH_BOOST = 2.0;
6
+ const EXTRA_MATCH_BOOST = 0.5;
4
7
  /** The capped boost for one row whose tags match `matches` active goals. */
5
8
  function goalBoostMultiplier(entry, tags, matches, goalsByTag, policiesByGoalId) {
6
- // Base 2.0x for first match, +0.5x per additional, capped at 3.0x.
7
- let multiplier = Math.min(2.0 + 0.5 * (matches.length - 1), MAX_FINAL_MULTIPLIER);
9
+ // First match boosts by FIRST_MATCH_BOOST, each further one adds EXTRA_MATCH_BOOST, capped at MAX_FINAL_MULTIPLIER.
10
+ let multiplier = Math.min(FIRST_MATCH_BOOST + EXTRA_MATCH_BOOST * (matches.length - 1), MAX_FINAL_MULTIPLIER);
8
11
  // Compose per-policy multipliers per matched tag.
9
12
  for (const tag of matches) {
10
13
  const goal = goalsByTag.get(tag);
@@ -20,7 +23,7 @@ function goalBoostMultiplier(entry, tags, matches, goalsByTag, policiesByGoalId)
20
23
  multiplier *=
21
24
  1.0 +
22
25
  Math.max(0, policy.weightSchemaFit - 1.0) *
23
- (entry.schema_fit ?? 0.5);
26
+ (entry.schema_fit ?? DEFAULT_SCHEMA_FIT);
24
27
  }
25
28
  else if (policy.policyType === 'recency-first') {
26
29
  multiplier *= policy.weightRecency;
@@ -1,10 +1,10 @@
1
1
  import type { MemoryEntry } from '../core/memory.js';
2
2
  import type { RerankerFn, RerankerOptions } from '../rerankers/types.js';
3
3
  import { type BM25Corpus } from './bm25.js';
4
- import { type HybridVectorCandidates } from './vector.js';
4
+ import { type HybridVectorCandidates, type VectorSource } from './vector.js';
5
5
  import { type GraphStreamOptions } from './fusion.js';
6
6
  import { type ResultCost, type SearchResult } from '../core/search-types.js';
7
- import type { HippoStore } from '../store/index.js';
7
+ import { type HippoStore } from '../store/index.js';
8
8
  export interface HybridSearchOptions {
9
9
  budget?: number;
10
10
  now?: Date;
@@ -46,6 +46,13 @@ export interface HybridSearchOptions {
46
46
  /** The deadline a caller already started for this recall's query embedding; a fresh budget when unset. */
47
47
  queryEmbedDeadline?: AbortSignal;
48
48
  }
49
+ export declare const DEFAULT_EMBEDDING_WEIGHT = 0.6;
50
+ export declare const DEFAULT_MMR_LAMBDA = 0.7;
49
51
  /** BM25 blended with cosine similarity when stored vectors and a provider are available, BM25 * strength * recency otherwise. */
50
52
  export declare function hybridSearch(query: string, entries: MemoryEntry[], options?: HybridSearchOptions): Promise<SearchResult[]>;
53
+ /** hippo.db under `hippoRoot` when the caller passed no store: the default lives on this exported entry, never inside the vector arm. */
54
+ export declare function vectorSourceOf({ hippoRoot, store }: {
55
+ hippoRoot?: string;
56
+ store?: HippoStore;
57
+ }): VectorSource | undefined;
51
58
  //# sourceMappingURL=hybrid.d.ts.map
@@ -1,7 +1,7 @@
1
1
  import { tokenize } from '../util/tokenize.js';
2
2
  import { evalNow } from '../core/ablation.js';
3
3
  import { extractPathTags } from './path-context.js';
4
- import { detectScope } from '../sharing/scope.js';
4
+ import { detectScope } from '../core/active-scope.js';
5
5
  import { compareScoredResults } from '../core/compare.js';
6
6
  import { bm25Score, buildCorpus, entryText } from './bm25.js';
7
7
  import { currentEntries } from './as-of.js';
@@ -13,9 +13,10 @@ import { scoreHybridPool } from './hybrid-score.js';
13
13
  import { applyMmrWindow, applyReranker } from './rerank.js';
14
14
  import { dedupeExtracted, fitBudget, withDagChildren } from './finalize.js';
15
15
  import { DEFAULT_RECALL_BUDGET } from '../core/search-types.js';
16
+ import { sqliteStore } from '../store/index.js';
16
17
  // Share of the blended score that cosine takes, and the relevance side of the MMR relevance-versus-diversity trade.
17
- const DEFAULT_EMBEDDING_WEIGHT = 0.6;
18
- const DEFAULT_MMR_LAMBDA = 0.7;
18
+ export const DEFAULT_EMBEDDING_WEIGHT = 0.6;
19
+ export const DEFAULT_MMR_LAMBDA = 0.7;
19
20
  /** BM25 blended with cosine similarity when stored vectors and a provider are available, BM25 * strength * recency otherwise. */
20
21
  export async function hybridSearch(query, entries, options = {}) {
21
22
  const now = options.now ?? evalNow(); // honors HIPPO_FAKE_NOW (eval-only; see ablation.ts)
@@ -26,7 +27,7 @@ export async function hybridSearch(query, entries, options = {}) {
26
27
  const queryTerms = tokenize(query);
27
28
  if (queryTerms.length === 0)
28
29
  return [];
29
- const arm = await resolveVectorArm(query, pool, options);
30
+ const arm = await resolveVectorArm(query, pool, vectorSourceOf(options), options);
30
31
  // A prepared corpus covers the caller's entries only, so rows the vector arm added force a rebuild.
31
32
  const corpus = (arm.addedRows ? undefined : options.preparedCorpus) ?? buildCorpus(arm.entries.map(entryText));
32
33
  const bm25Scores = arm.entries.map((_, i) => bm25Score(corpus, i, queryTerms));
@@ -48,6 +49,10 @@ export async function hybridSearch(query, entries, options = {}) {
48
49
  const ordered = await orderHybrid(query, withDagChildren(dedupeExtracted(scored), arm.entries), arm, options);
49
50
  return fitBudget(ordered, options.budget ?? DEFAULT_RECALL_BUDGET, options.minResults ?? 1, options.cost);
50
51
  }
52
+ /** hippo.db under `hippoRoot` when the caller passed no store: the default lives on this exported entry, never inside the vector arm. */
53
+ export function vectorSourceOf({ hippoRoot, store }) {
54
+ return hippoRoot ? { root: hippoRoot, store: store ?? sqliteStore(hippoRoot) } : undefined;
55
+ }
51
56
  function hybridBoostContext(query, pool, now, options) {
52
57
  return {
53
58
  now,
@@ -1,7 +1,7 @@
1
1
  import { type MemoryEntry } from '../core/memory.js';
2
2
  import { type PhysicsConfig } from '../core/physics-config.js';
3
- import { type HippoStore } from '../store/index.js';
4
- import { type HybridVectorCandidates } from './vector.js';
3
+ import type { HippoStore } from '../store/index.js';
4
+ import { type HybridVectorCandidates, type VectorSource } from './vector.js';
5
5
  import { type ResultCost, type SearchResult } from '../core/search-types.js';
6
6
  export interface PhysicsSearchOptions {
7
7
  budget?: number;
@@ -27,6 +27,9 @@ export interface PhysicsSearchOptions {
27
27
  /** Where vectors and particles are read; hippo.db under `hippoRoot` when unset. */
28
28
  store?: HippoStore;
29
29
  }
30
- /** Scores memories by gravity, momentum and cluster amplification; rows without physics state rank through hybridSearch. */
30
+ /** Scores memories by gravity, momentum and cluster amplification; rows without physics state rank through hybridSearch.
31
+ * Reads hippo.db under `hippoRoot` when no store is passed. */
31
32
  export declare function physicsSearch(query: string, entries: MemoryEntry[], options?: PhysicsSearchOptions): Promise<SearchResult[]>;
33
+ /** physicsSearch on the store the caller names, so a caller holding its request's store never falls back to hippo.db. */
34
+ export declare function physicsSearchIn(source: VectorSource, query: string, entries: MemoryEntry[], options?: PhysicsSearchOptions): Promise<SearchResult[]>;
32
35
  //# sourceMappingURL=physics-search.d.ts.map
@@ -1,50 +1,55 @@
1
1
  import { calculateStrength } from '../core/memory.js';
2
2
  import { estimateTokens } from '../util/token-text.js';
3
- import { evalNow, isRecallBoostAblated } from '../core/ablation.js';
3
+ import { evalNow, isRecallBoostAblated, strengthAblation } from '../core/ablation.js';
4
4
  import { indexNeedsRebuild } from '../store/embeddings/index.js';
5
5
  import { indexedModel } from '../store/vector-index.js';
6
- import { resolveEmbeddingProvider } from '../store/embeddings/provider.js';
6
+ import { resolveEmbeddingProvider } from '../embeddings/provider.js';
7
7
  import { physicsScore as computePhysicsScores, computeMass } from '../core/physics.js';
8
8
  import { DEFAULT_PHYSICS_CONFIG } from '../core/physics-config.js';
9
- import { rethrowIfSqliteBlocked } from '../db/index.js';
9
+ import { rethrowIfSqliteBlocked } from '../util/sqlite-blocked.js';
10
10
  import { compareScoredResults } from '../core/compare.js';
11
11
  import { errorMessage, log } from '../util/log.js';
12
- import { sqliteStore } from '../store/index.js';
13
12
  import { churnStaleFactor, summaryMultipliers, summaryScoring } from './boosts.js';
14
13
  import { addDagFields, ageInDays } from './breakdown.js';
15
14
  import { fitBudget } from './finalize.js';
16
- import { hybridSearch } from './hybrid.js';
15
+ import { hybridSearch, vectorSourceOf } from './hybrid.js';
17
16
  import { currentEntries } from './as-of.js';
18
- import { embedQueryBy, requireVectorReads, startQueryEmbedDeadline, vectorCandidatesOutside } from './vector.js';
17
+ import { embedQueryBy, requireVectorReads, startQueryEmbedDeadline, vectorCandidatesOutside, } from './vector.js';
19
18
  import { DEFAULT_RECALL_BUDGET } from '../core/search-types.js';
20
- /** Scores memories by gravity, momentum and cluster amplification; rows without physics state rank through hybridSearch. */
19
+ /** Scores memories by gravity, momentum and cluster amplification; rows without physics state rank through hybridSearch.
20
+ * Reads hippo.db under `hippoRoot` when no store is passed. */
21
21
  export async function physicsSearch(query, entries, options = {}) {
22
+ const source = entries.length > 0 ? vectorSourceOf(options) : undefined;
23
+ return source ? physicsSearchIn(source, query, entries, options) : [];
24
+ }
25
+ /** physicsSearch on the store the caller names, so a caller holding its request's store never falls back to hippo.db. */
26
+ export async function physicsSearchIn(source, query, entries, options = {}) {
22
27
  const now = options.now ?? evalNow(); // honors HIPPO_FAKE_NOW (eval-only; see ablation.ts)
23
28
  const explain = options.explain ?? false;
24
29
  const scoring = { now, explain, summary: summaryScoring(options) };
25
- if (entries.length === 0 || !options.hippoRoot)
30
+ if (entries.length === 0)
26
31
  return [];
32
+ const { root, store } = source;
33
+ const hybrid = { ...options, hippoRoot: root, store };
27
34
  // memory_physics keeps only current positions and masses, so a past-dated query must rank without them.
28
35
  if (options.asOf)
29
- return hybridSearch(query, entries, options);
30
- const root = options.hippoRoot;
31
- const store = options.store ?? sqliteStore(root);
36
+ return hybridSearch(query, entries, hybrid);
32
37
  // Shared with every hybrid ranking below, so a provider that stalls here is not waited on a second time.
33
38
  const queryEmbedDeadline = startQueryEmbedDeadline();
34
39
  const queryVector = await physicsQueryVector(query, root, store, queryEmbedDeadline, options.queryEmbedding);
35
40
  if (!queryVector)
36
- return hybridSearch(query, entries, { ...options, queryEmbedDeadline });
41
+ return hybridSearch(query, entries, { ...hybrid, queryEmbedDeadline });
37
42
  // Checked here too, since a caller's own query vector skips the check inside physicsQueryVector.
38
43
  const reads = requireVectorReads(store);
39
44
  const pool = currentEntries(await withVectorCandidates(reads, entries, queryVector, options.vectorCandidates), options);
40
45
  const physicsMap = await loadCandidateParticles(reads, pool);
41
46
  if (!physicsMap)
42
- return hybridSearch(query, pool, { ...options, queryEmbedDeadline });
47
+ return hybridSearch(query, pool, { ...hybrid, queryEmbedDeadline });
43
48
  const pools = splitByParticle(pool, physicsMap, queryVector, now);
44
49
  const config = options.physicsConfig ?? DEFAULT_PHYSICS_CONFIG;
45
50
  const physicsResults = scorePhysicsPool(pools, queryVector, config, scoring);
46
51
  const classicResults = pools.classic.length > 0
47
- ? await hybridSearch(query, pools.classic, { ...options, queryEmbedDeadline, vectorCandidates: undefined, budget: Infinity, explain })
52
+ ? await hybridSearch(query, pools.classic, { ...hybrid, queryEmbedDeadline, vectorCandidates: undefined, budget: Infinity, explain })
48
53
  : [];
49
54
  const merged = mergeScorePools(physicsResults, classicResults);
50
55
  merged.sort(compareScoredResults);
@@ -107,7 +112,9 @@ function splitByParticle(pool, physicsMap, queryVector, now) {
107
112
  }
108
113
  pools.entries.push(entry);
109
114
  // EVAL-ONLY ablation (see ablation.ts): persisted masses bake in recall history twice, so the flag recomputes mass live.
110
- pools.particles.push(isRecallBoostAblated() ? { ...particle, mass: computeMass(calculateStrength(entry, now), entry.retrieval_count) } : particle);
115
+ pools.particles.push(isRecallBoostAblated()
116
+ ? { ...particle, mass: computeMass(calculateStrength(entry, now, { ablation: strengthAblation() }), entry.retrieval_count) }
117
+ : particle);
111
118
  }
112
119
  return pools;
113
120
  }
@@ -1,4 +1,4 @@
1
- import { cosineOf } from '../store/embeddings/index.js';
1
+ import { cosineOf } from '../core/cosine.js';
2
2
  const DEFAULT_RERANK_TOP_K = 50;
3
3
  // MMR is O(K^2) cosine ops; candidates below this window never survive budget filtering anyway.
4
4
  const MMR_CANDIDATE_CAP = 100;
@@ -1,6 +1,6 @@
1
1
  import type { MemoryEntry } from '../core/memory.js';
2
2
  import type { VectorCandidateSpec } from '../store/search-rows.js';
3
- import { type EmbeddingProvider } from '../store/embeddings/provider.js';
3
+ import { type EmbeddingProvider } from '../embeddings/provider.js';
4
4
  import { type HippoStore, type VectorReads } from '../store/index.js';
5
5
  import { type CurrentnessOptions } from './as-of.js';
6
6
  /** hybridSearch's vector arm: which rows it may add, plus the caller's JS admission rules (exact private regex, entry filters). */
@@ -15,24 +15,26 @@ export interface VectorArm {
15
15
  embeddingIndex: Record<string, ArrayLike<number>>;
16
16
  queryVector: number[];
17
17
  }
18
+ /** Where the vector arm reads: the embedding provider configured under `root`, and the vectors `store` holds. */
19
+ export interface VectorSource {
20
+ readonly root: string;
21
+ readonly store: HippoStore;
22
+ }
18
23
  export interface VectorArmOptions extends CurrentnessOptions {
19
- hippoRoot?: string;
20
24
  vectorCandidates?: HybridVectorCandidates;
21
- /** Where vectors are read; hippo.db under `hippoRoot` when unset. */
22
- store?: HippoStore;
23
25
  /** The deadline a caller already started for this recall's query embedding; a fresh budget when unset. */
24
26
  queryEmbedDeadline?: AbortSignal;
25
27
  }
26
28
  /** One deadline for every query embedding a recall makes, so two stores or a physics fallback share the budget. */
27
29
  export declare function startQueryEmbedDeadline(): AbortSignal;
28
- /** The query's vector, or null when an API provider outlasts `deadline`; any other provider failure throws as before. */
30
+ /** The query's vector, or null when the provider outlasts `deadline`: an API call, or a local model still downloading. Any other failure throws. */
29
31
  export declare function embedQueryBy(deadline: AbortSignal, provider: EmbeddingProvider, query: string): Promise<number[] | null>;
30
32
  /** The store's vector reads; a store without them answers 501, as any unported path does. */
31
33
  export declare function requireVectorReads(store: HippoStore): VectorReads;
32
34
  /** The nearest admitted rows not already in `entries`. */
33
35
  export declare function vectorCandidatesOutside(reads: VectorReads, entries: readonly MemoryEntry[], queryVector: readonly number[], spec: HybridVectorCandidates): Promise<MemoryEntry[]>;
34
36
  /** Embeds the query and loads stored vectors; any failure leaves BM25 to rank alone. */
35
- export declare function resolveVectorArm(query: string, entries: MemoryEntry[], options: VectorArmOptions): Promise<VectorArm>;
37
+ export declare function resolveVectorArm(query: string, entries: MemoryEntry[], source: VectorSource | undefined, options: VectorArmOptions): Promise<VectorArm>;
36
38
  export interface DenseScores {
37
39
  cosine: number[];
38
40
  /** Whether each entry had a cached doc vector to compare against. */