hippo-memory 1.71.0 → 2.0.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 (625) hide show
  1. package/README.md +7 -5
  2. package/dist/agent-memories/apply.d.ts +0 -11
  3. package/dist/agent-memories/apply.js +11 -26
  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 +3 -2
  12. package/dist/api/audit.d.ts +30 -2
  13. package/dist/api/audit.js +48 -7
  14. package/dist/api/auth.d.ts +11 -9
  15. package/dist/api/auth.js +39 -40
  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-auto.js +4 -2
  24. package/dist/api/context-render.js +4 -3
  25. package/dist/api/context-select.js +15 -29
  26. package/dist/api/context.js +29 -18
  27. package/dist/api/continuity.d.ts +47 -0
  28. package/dist/api/continuity.js +66 -0
  29. package/dist/api/dormant.js +4 -2
  30. package/dist/api/drill-down.js +3 -2
  31. package/dist/api/embeddings.d.ts +17 -0
  32. package/dist/api/embeddings.js +28 -0
  33. package/dist/api/error-reply.d.ts +8 -0
  34. package/dist/api/error-reply.js +21 -0
  35. package/dist/api/forget.d.ts +7 -3
  36. package/dist/api/forget.js +24 -14
  37. package/dist/api/goals.d.ts +1 -1
  38. package/dist/api/goals.js +5 -9
  39. package/dist/api/graph.js +2 -1
  40. package/dist/api/index.d.ts +4 -2
  41. package/dist/api/index.js +4 -2
  42. package/dist/api/instruction-detect.js +5 -1
  43. package/dist/api/learn.d.ts +5 -1
  44. package/dist/api/learn.js +71 -44
  45. package/dist/api/ledger-db.d.ts +2 -6
  46. package/dist/api/ledger-db.js +4 -36
  47. package/dist/api/memories.d.ts +21 -1
  48. package/dist/api/memories.js +51 -1
  49. package/dist/api/objects.d.ts +1 -1
  50. package/dist/api/objects.js +2 -1
  51. package/dist/api/outcome.d.ts +5 -5
  52. package/dist/api/outcome.js +17 -16
  53. package/dist/api/pilot-arm.d.ts +1 -11
  54. package/dist/api/pilot-arm.js +5 -45
  55. package/dist/api/predictions.d.ts +3 -1
  56. package/dist/api/predictions.js +8 -1
  57. package/dist/api/projects.d.ts +21 -0
  58. package/dist/api/projects.js +24 -0
  59. package/dist/api/promote.d.ts +3 -3
  60. package/dist/api/promote.js +26 -28
  61. package/dist/api/prompt-hook.d.ts +17 -2
  62. package/dist/api/prompt-hook.js +45 -4
  63. package/dist/api/quarantine.js +2 -1
  64. package/dist/api/recall-cli.d.ts +3 -0
  65. package/dist/api/recall-cli.js +13 -4
  66. package/dist/api/recall-core.js +10 -5
  67. package/dist/api/recall-pipeline.js +23 -24
  68. package/dist/api/recall-request.js +1 -1
  69. package/dist/api/recall-types.d.ts +3 -1
  70. package/dist/api/recall.d.ts +0 -3
  71. package/dist/api/recall.js +51 -73
  72. package/dist/api/remember-local.d.ts +5 -2
  73. package/dist/api/remember-local.js +15 -7
  74. package/dist/api/remember.d.ts +7 -3
  75. package/dist/api/remember.js +19 -16
  76. package/dist/api/session-end.d.ts +46 -0
  77. package/dist/api/session-end.js +131 -0
  78. package/dist/api/sleep-run.d.ts +1 -1
  79. package/dist/api/sleep-run.js +16 -11
  80. package/dist/api/sleep.js +4 -3
  81. package/dist/api/status.d.ts +24 -0
  82. package/dist/api/status.js +40 -0
  83. package/dist/api/store-init.d.ts +4 -0
  84. package/dist/api/store-init.js +9 -0
  85. package/dist/api/tokens.js +2 -2
  86. package/dist/api/types.d.ts +3 -7
  87. package/dist/api/types.js +6 -1
  88. package/dist/capture/caller-session.d.ts +0 -3
  89. package/dist/capture/caller-session.js +1 -12
  90. package/dist/capture/capture-error.d.ts +2 -2
  91. package/dist/capture/capture-error.js +23 -18
  92. package/dist/capture/codex-patch.js +3 -1
  93. package/dist/capture/command.js +36 -95
  94. package/dist/capture/compact.js +10 -5
  95. package/dist/capture/compaction-items-caller.js +11 -20
  96. package/dist/capture/compaction-record.d.ts +7 -93
  97. package/dist/capture/compaction-record.js +29 -426
  98. package/dist/capture/compaction-spool.d.ts +7 -7
  99. package/dist/capture/compaction-spool.js +60 -51
  100. package/dist/capture/copilot-transcript.d.ts +2 -1
  101. package/dist/capture/copilot-transcript.js +2 -1
  102. package/dist/capture/extract.d.ts +5 -2
  103. package/dist/capture/extract.js +4 -3
  104. package/dist/capture/failure-caller.d.ts +1 -1
  105. package/dist/capture/failure-caller.js +21 -16
  106. package/dist/capture/failure-reading.d.ts +2 -1
  107. package/dist/capture/failure-reading.js +7 -9
  108. package/dist/capture/pre-compact-caller.js +5 -4
  109. package/dist/capture/session-digest.js +4 -3
  110. package/dist/capture/session-end-caller.js +3 -2
  111. package/dist/capture/session-worker.js +22 -10
  112. package/dist/capture/transcript.d.ts +2 -1
  113. package/dist/capture/transcript.js +6 -3
  114. package/dist/capture/working-state.d.ts +4 -2
  115. package/dist/capture/working-state.js +4 -2
  116. package/dist/cli/audit-prune.d.ts +0 -25
  117. package/dist/cli/audit-prune.js +1 -27
  118. package/dist/cli/audit.js +20 -20
  119. package/dist/cli/auth.d.ts +1 -1
  120. package/dist/cli/auth.js +16 -17
  121. package/dist/cli/briefs.d.ts +1 -4
  122. package/dist/cli/briefs.js +37 -292
  123. package/dist/cli/card.js +41 -33
  124. package/dist/cli/client.d.ts +6 -2
  125. package/dist/cli/client.js +37 -21
  126. package/dist/cli/compact-resume-payload.js +1 -1
  127. package/dist/cli/context.d.ts +1 -1
  128. package/dist/cli/context.js +13 -13
  129. package/dist/cli/continuity.d.ts +1 -1
  130. package/dist/cli/continuity.js +36 -39
  131. package/dist/cli/curate.d.ts +2 -2
  132. package/dist/cli/curate.js +32 -36
  133. package/dist/cli/dag.d.ts +1 -1
  134. package/dist/cli/dag.js +23 -8
  135. package/dist/cli/decisions.d.ts +0 -2
  136. package/dist/cli/decisions.js +7 -285
  137. package/dist/cli/eval.d.ts +1 -1
  138. package/dist/cli/eval.js +12 -13
  139. package/dist/cli/explain.d.ts +1 -1
  140. package/dist/cli/explain.js +21 -25
  141. package/dist/cli/flag-values.js +16 -6
  142. package/dist/cli/flags.d.ts +0 -289
  143. package/dist/cli/flags.js +3 -138
  144. package/dist/cli/graph.d.ts +3 -0
  145. package/dist/cli/graph.js +83 -0
  146. package/dist/cli/hook-runtime.d.ts +6 -6
  147. package/dist/cli/hook-runtime.js +9 -36
  148. package/dist/cli/incidents.d.ts +3 -0
  149. package/dist/cli/incidents.js +112 -0
  150. package/dist/cli/init.d.ts +1 -1
  151. package/dist/cli/init.js +28 -19
  152. package/dist/cli/install-steps.d.ts +2 -1
  153. package/dist/cli/install-steps.js +11 -3
  154. package/dist/cli/last-sleep.js +1 -1
  155. package/dist/cli/maintenance.d.ts +3 -3
  156. package/dist/cli/maintenance.js +29 -30
  157. package/dist/cli/notes.d.ts +2 -0
  158. package/dist/cli/notes.js +41 -0
  159. package/dist/cli/object-verbs.d.ts +2 -2
  160. package/dist/cli/object-verbs.js +4 -4
  161. package/dist/cli/open-browser.d.ts +14 -0
  162. package/dist/cli/open-browser.js +26 -0
  163. package/dist/cli/playbooks.d.ts +3 -4
  164. package/dist/cli/playbooks.js +126 -290
  165. package/dist/cli/predictions.d.ts +3 -0
  166. package/dist/cli/predictions.js +174 -0
  167. package/dist/cli/print.js +2 -1
  168. package/dist/cli/projects.js +30 -22
  169. package/dist/cli/quality-repair-once.d.ts +1 -1
  170. package/dist/cli/quality-repair-once.js +6 -5
  171. package/dist/cli/recall.js +13 -7
  172. package/dist/cli/refine-llm.js +18 -17
  173. package/dist/cli/remember.js +17 -17
  174. package/dist/cli/scheduler.js +2 -1
  175. package/dist/cli/serve.js +11 -6
  176. package/dist/cli/session-hooks.d.ts +1 -5
  177. package/dist/cli/session-hooks.js +89 -168
  178. package/dist/cli/setup.d.ts +1 -1
  179. package/dist/cli/setup.js +17 -10
  180. package/dist/cli/shared.d.ts +2 -6
  181. package/dist/cli/shared.js +9 -29
  182. package/dist/cli/sleep-render.js +6 -3
  183. package/dist/cli/sleep.js +6 -37
  184. package/dist/cli/status.d.ts +3 -3
  185. package/dist/cli/status.js +32 -35
  186. package/dist/cli/transfer.d.ts +3 -3
  187. package/dist/cli/transfer.js +64 -43
  188. package/dist/cli/usage.d.ts +1 -80
  189. package/dist/cli/usage.js +2 -696
  190. package/dist/cli/verb-row.d.ts +34 -0
  191. package/dist/cli/verb-row.js +6 -0
  192. package/dist/cli/verbs/health.d.ts +27 -0
  193. package/dist/cli/verbs/health.js +234 -0
  194. package/dist/cli/verbs/memory.d.ts +16 -0
  195. package/dist/cli/verbs/memory.js +236 -0
  196. package/dist/cli/verbs/objects.d.ts +20 -0
  197. package/dist/cli/verbs/objects.js +257 -0
  198. package/dist/cli/verbs/sessions.d.ts +31 -0
  199. package/dist/cli/verbs/sessions.js +314 -0
  200. package/dist/cli/verbs.d.ts +81 -0
  201. package/dist/cli/verbs.js +7 -0
  202. package/dist/cli/versioned-verbs.d.ts +51 -0
  203. package/dist/cli/versioned-verbs.js +79 -0
  204. package/dist/cli.d.ts +1 -272
  205. package/dist/cli.js +24 -315
  206. package/dist/connectors/dlq.d.ts +39 -3
  207. package/dist/connectors/dlq.js +39 -4
  208. package/dist/connectors/github/backfill.js +12 -3
  209. package/dist/connectors/github/deletion.js +2 -1
  210. package/dist/connectors/github/dlq.js +31 -73
  211. package/dist/connectors/github/ingest.d.ts +2 -6
  212. package/dist/connectors/github/ingest.js +5 -29
  213. package/dist/connectors/github/tenant-routing.js +2 -1
  214. package/dist/connectors/github/webhook.d.ts +2 -1
  215. package/dist/connectors/github/webhook.js +35 -2
  216. package/dist/connectors/ingest.d.ts +10 -0
  217. package/dist/connectors/ingest.js +29 -0
  218. package/dist/connectors/slack/deletion.js +2 -1
  219. package/dist/connectors/slack/dlq.js +49 -71
  220. package/dist/connectors/slack/ingest.d.ts +2 -6
  221. package/dist/connectors/slack/ingest.js +4 -28
  222. package/dist/connectors/slack/tenant-routing.js +2 -1
  223. package/dist/connectors/slack/webhook.d.ts +2 -1
  224. package/dist/connectors/slack/webhook.js +2 -1
  225. package/dist/consolidate/conflicts.js +2 -2
  226. package/dist/consolidate/dag.js +29 -18
  227. package/dist/consolidate/dedupe.d.ts +7 -0
  228. package/dist/consolidate/dedupe.js +6 -4
  229. package/dist/consolidate/llm-passes.js +45 -19
  230. package/dist/consolidate/memory-value.js +3 -2
  231. package/dist/consolidate/merge.js +8 -8
  232. package/dist/consolidate/physics-pass.d.ts +6 -1
  233. package/dist/consolidate/physics-pass.js +26 -6
  234. package/dist/consolidate/replay.js +11 -4
  235. package/dist/consolidate/sleep.js +9 -6
  236. package/dist/consolidate/traces.js +5 -5
  237. package/dist/core/ablation.d.ts +10 -0
  238. package/dist/core/ablation.js +12 -0
  239. package/dist/{sharing/scope.d.ts → core/active-scope.d.ts} +1 -1
  240. package/dist/{sharing/scope.js → core/active-scope.js} +1 -1
  241. package/dist/core/ambient.d.ts +4 -0
  242. package/dist/core/ambient.js +39 -18
  243. package/dist/core/api-errors.d.ts +16 -0
  244. package/dist/core/api-errors.js +20 -0
  245. package/dist/core/card.js +3 -1
  246. package/dist/core/compaction-timing.d.ts +7 -0
  247. package/dist/core/compaction-timing.js +9 -0
  248. package/dist/core/config.d.ts +1 -1
  249. package/dist/core/config.js +22 -18
  250. package/dist/core/cosine.d.ts +3 -0
  251. package/dist/core/cosine.js +19 -0
  252. package/dist/{store → core}/markdown.d.ts +1 -1
  253. package/dist/{store → core}/markdown.js +4 -4
  254. package/dist/core/memory-audit.d.ts +17 -0
  255. package/dist/core/memory-audit.js +54 -0
  256. package/dist/core/memory.d.ts +14 -8
  257. package/dist/core/memory.js +35 -23
  258. package/dist/{consolidate → core}/merged-row.d.ts +1 -1
  259. package/dist/{consolidate → core}/merged-row.js +1 -1
  260. package/dist/core/physics.d.ts +3 -1
  261. package/dist/core/physics.js +23 -18
  262. package/dist/core/prediction.d.ts +3 -0
  263. package/dist/core/prediction.js +6 -0
  264. package/dist/core/project-identity.d.ts +3 -0
  265. package/dist/core/project-identity.js +12 -2
  266. package/dist/{store → core}/recall-scope.d.ts +1 -10
  267. package/dist/{store → core}/recall-scope.js +2 -19
  268. package/dist/core/search-types.d.ts +1 -1
  269. package/dist/dashboard/dashboard-actions.d.ts +2 -2
  270. package/dist/dashboard/dashboard-actions.js +5 -5
  271. package/dist/dashboard/dashboard-queries.js +2 -7
  272. package/dist/dashboard/dashboard-snapshot.js +1 -3
  273. package/dist/dashboard/dashboard.js +16 -5
  274. package/dist/db/busy.d.ts +2 -7
  275. package/dist/db/busy.js +21 -26
  276. package/dist/db/connect.d.ts +2 -2
  277. package/dist/db/connect.js +7 -7
  278. package/dist/db/index.d.ts +3 -3
  279. package/dist/db/index.js +3 -3
  280. package/dist/db/migrate.js +14 -7
  281. package/dist/db/migrations/index.d.ts +1 -1
  282. package/dist/db/migrations/index.js +3 -1
  283. package/dist/db/migrations/v11.js +1 -0
  284. package/dist/db/migrations/v14.js +1 -0
  285. package/dist/db/migrations/v55.d.ts +3 -0
  286. package/dist/db/migrations/v55.js +17 -0
  287. package/dist/db/open.d.ts +0 -2
  288. package/dist/db/open.js +6 -10
  289. package/dist/db/physics-state.d.ts +0 -4
  290. package/dist/db/physics-state.js +7 -33
  291. package/dist/db/request-stores.d.ts +1 -0
  292. package/dist/db/request-stores.js +2 -1
  293. package/dist/db/tables.d.ts +2 -0
  294. package/dist/db/tables.js +6 -2
  295. package/dist/db/vector-store.js +19 -5
  296. package/dist/db/wal-checkpointer.js +2 -1
  297. package/dist/doctor.d.ts +3 -0
  298. package/dist/doctor.js +18 -15
  299. package/dist/embeddings/local.d.ts +62 -0
  300. package/dist/embeddings/local.js +146 -0
  301. package/dist/{store/embeddings → embeddings}/provider.d.ts +5 -2
  302. package/dist/{store/embeddings → embeddings}/provider.js +19 -25
  303. package/dist/embeddings/transformers.d.ts +26 -0
  304. package/dist/embeddings/transformers.js +89 -0
  305. package/dist/entry/session-text.d.ts +1 -1
  306. package/dist/entry/session-text.js +1 -1
  307. package/dist/entry/store-kit.d.ts +37 -0
  308. package/dist/entry/store-kit.js +25 -0
  309. package/dist/entry/testing.d.ts +6 -0
  310. package/dist/entry/testing.js +7 -0
  311. package/dist/eval/eval-suite.js +17 -11
  312. package/dist/eval/eval.js +1 -1
  313. package/dist/eval/judgment.js +12 -5
  314. package/dist/graph/extract.js +3 -3
  315. package/dist/graph/recall.js +1 -1
  316. package/dist/graph/view.js +13 -5
  317. package/dist/graph/write.d.ts +1 -1
  318. package/dist/graph/write.js +9 -9
  319. package/dist/hooks/codex-session.js +3 -2
  320. package/dist/hooks/codex-wrapper.js +2 -1
  321. package/dist/hooks/json-hooks.js +10 -4
  322. package/dist/hooks/opencode.js +8 -5
  323. package/dist/hooks/shared.d.ts +0 -6
  324. package/dist/hooks/shared.js +4 -15
  325. package/dist/importers/core.js +4 -4
  326. package/dist/importers/sources.js +5 -4
  327. package/dist/importers/vault.d.ts +1 -1
  328. package/dist/importers/vault.js +25 -23
  329. package/dist/index.d.ts +5 -4
  330. package/dist/index.js +4 -3
  331. package/dist/learn/autolearn.d.ts +15 -3
  332. package/dist/learn/autolearn.js +38 -16
  333. package/dist/learn/churn-git.js +13 -4
  334. package/dist/learn/extract.d.ts +3 -2
  335. package/dist/learn/extract.js +7 -5
  336. package/dist/learn/forward-claim-detector.js +4 -2
  337. package/dist/learn/invalidation.d.ts +2 -1
  338. package/dist/learn/invalidation.js +18 -9
  339. package/dist/mcp/admin-tools.d.ts +2 -2
  340. package/dist/mcp/admin-tools.js +27 -48
  341. package/dist/mcp/format.js +3 -2
  342. package/dist/mcp/framing.d.ts +1 -0
  343. package/dist/mcp/framing.js +1 -1
  344. package/dist/mcp/memory-tools.d.ts +1 -1
  345. package/dist/mcp/memory-tools.js +8 -9
  346. package/dist/mcp/protocol.d.ts +11 -7
  347. package/dist/mcp/protocol.js +25 -12
  348. package/dist/mcp/recall-tools.js +12 -9
  349. package/dist/mcp/request.js +22 -20
  350. package/dist/mcp/stdio.js +37 -16
  351. package/dist/mcp/tool-args.js +2 -1
  352. package/dist/mcp/tools.js +44 -21
  353. package/dist/objects/customer-notes.d.ts +2 -2
  354. package/dist/objects/decisions.d.ts +2 -2
  355. package/dist/objects/descriptor.d.ts +1 -1
  356. package/dist/objects/incidents.d.ts +2 -2
  357. package/dist/objects/lifecycle.d.ts +1 -1
  358. package/dist/objects/lifecycle.js +2 -1
  359. package/dist/objects/policies.d.ts +2 -2
  360. package/dist/objects/policies.js +2 -1
  361. package/dist/objects/processes.d.ts +2 -2
  362. package/dist/objects/project-briefs.d.ts +2 -2
  363. package/dist/objects/project-briefs.js +2 -3
  364. package/dist/objects/skills.d.ts +2 -2
  365. package/dist/postinstall.js +1 -1
  366. package/dist/rerankers/clef.js +3 -6
  367. package/dist/rerankers/cross-encoder.d.ts +31 -2
  368. package/dist/rerankers/cross-encoder.js +71 -98
  369. package/dist/rerankers/index.js +5 -4
  370. package/dist/rerankers/jev.js +2 -4
  371. package/dist/rerankers/outage-warning.d.ts +3 -1
  372. package/dist/rerankers/outage-warning.js +5 -3
  373. package/dist/search/bm25-search.js +1 -1
  374. package/dist/search/boosts.js +1 -1
  375. package/dist/search/embed-memories.d.ts +12 -0
  376. package/dist/search/embed-memories.js +389 -0
  377. package/dist/search/engine.d.ts +8 -0
  378. package/dist/search/engine.js +10 -0
  379. package/dist/search/goal-boost.d.ts +1 -1
  380. package/dist/search/goal-boost.js +6 -3
  381. package/dist/search/hybrid.d.ts +9 -2
  382. package/dist/search/hybrid.js +9 -4
  383. package/dist/search/physics-search.d.ts +6 -3
  384. package/dist/search/physics-search.js +22 -15
  385. package/dist/search/rerank.js +1 -1
  386. package/dist/search/vector.d.ts +8 -6
  387. package/dist/search/vector.js +14 -17
  388. package/dist/server/auth.d.ts +3 -1
  389. package/dist/server/auth.js +24 -15
  390. package/dist/server/boot.d.ts +2 -1
  391. package/dist/server/boot.js +34 -84
  392. package/dist/server/client-ip.d.ts +9 -6
  393. package/dist/server/client-ip.js +36 -17
  394. package/dist/server/deadline.d.ts +0 -2
  395. package/dist/server/deadline.js +1 -7
  396. package/dist/server/defaults.d.ts +1 -0
  397. package/dist/server/defaults.js +1 -0
  398. package/dist/server/mcp-http.js +16 -11
  399. package/dist/server/request.js +7 -2
  400. package/dist/server/route-checks.d.ts +7 -0
  401. package/dist/server/route-checks.js +42 -0
  402. package/dist/server/route-table.d.ts +9 -8
  403. package/dist/server/route-table.js +127 -133
  404. package/dist/server/routes/admin.d.ts +8 -8
  405. package/dist/server/routes/admin.js +13 -21
  406. package/dist/server/routes/customer-notes.d.ts +6 -6
  407. package/dist/server/routes/customer-notes.js +1 -2
  408. package/dist/server/routes/decisions.d.ts +6 -6
  409. package/dist/server/routes/decisions.js +2 -4
  410. package/dist/server/routes/incidents.d.ts +6 -6
  411. package/dist/server/routes/incidents.js +2 -5
  412. package/dist/server/routes/memories.d.ts +9 -9
  413. package/dist/server/routes/memories.js +19 -40
  414. package/dist/server/routes/object-routes.d.ts +6 -6
  415. package/dist/server/routes/object-routes.js +4 -7
  416. package/dist/server/routes/policies.d.ts +7 -7
  417. package/dist/server/routes/policies.js +2 -4
  418. package/dist/server/routes/predictions.d.ts +6 -6
  419. package/dist/server/routes/predictions.js +9 -13
  420. package/dist/server/routes/processes.d.ts +6 -6
  421. package/dist/server/routes/processes.js +1 -2
  422. package/dist/server/routes/project-briefs.d.ts +7 -7
  423. package/dist/server/routes/project-briefs.js +2 -4
  424. package/dist/server/routes/recall.d.ts +5 -5
  425. package/dist/server/routes/recall.js +6 -10
  426. package/dist/server/routes/skills.d.ts +7 -7
  427. package/dist/server/routes/skills.js +2 -4
  428. package/dist/server/server-detect.d.ts +7 -1
  429. package/dist/server/server-detect.js +36 -11
  430. package/dist/server/sleep-child.js +26 -7
  431. package/dist/server/sleep-offload.d.ts +11 -5
  432. package/dist/server/sleep-offload.js +17 -11
  433. package/dist/server/tls.d.ts +7 -2
  434. package/dist/server/tls.js +17 -5
  435. package/dist/server/types.d.ts +29 -12
  436. package/dist/server/validation.d.ts +1 -2
  437. package/dist/server/validation.js +6 -11
  438. package/dist/server.d.ts +4 -40
  439. package/dist/server.js +3 -29
  440. package/dist/sharing/global-store.d.ts +4 -5
  441. package/dist/sharing/global-store.js +8 -13
  442. package/dist/sharing/global-sync.js +38 -45
  443. package/dist/sharing/project-merge.d.ts +7 -14
  444. package/dist/sharing/project-merge.js +57 -121
  445. package/dist/sharing/search-both.js +10 -3
  446. package/dist/sharing/share.d.ts +2 -0
  447. package/dist/sharing/share.js +39 -38
  448. package/dist/store/ambient.js +7 -7
  449. package/dist/store/audit-event.d.ts +1 -1
  450. package/dist/store/audit.d.ts +0 -16
  451. package/dist/store/audit.js +4 -55
  452. package/dist/store/auth.d.ts +4 -2
  453. package/dist/store/auth.js +7 -3
  454. package/dist/store/candidates.d.ts +3 -2
  455. package/dist/store/candidates.js +59 -30
  456. package/dist/store/capture-write.d.ts +17 -0
  457. package/dist/store/capture-write.js +35 -0
  458. package/dist/store/cards.js +6 -5
  459. package/dist/store/compaction-caller.d.ts +18 -0
  460. package/dist/store/compaction-caller.js +29 -0
  461. package/dist/store/compactions-record.d.ts +115 -0
  462. package/dist/store/compactions-record.js +418 -0
  463. package/dist/store/compactions.js +6 -4
  464. package/dist/store/conflicts.d.ts +7 -1
  465. package/dist/store/conflicts.js +13 -9
  466. package/dist/store/connectors/dlq.d.ts +10 -0
  467. package/dist/store/connectors/dlq.js +33 -0
  468. package/dist/store/connectors/event-log.d.ts +13 -0
  469. package/dist/store/connectors/event-log.js +24 -0
  470. package/dist/store/connectors/github.d.ts +7 -9
  471. package/dist/store/connectors/github.js +35 -60
  472. package/dist/store/connectors/slack.d.ts +2 -11
  473. package/dist/store/connectors/slack.js +24 -57
  474. package/dist/store/dashboard-reads.js +8 -6
  475. package/dist/store/delete-and-batch.d.ts +6 -0
  476. package/dist/store/delete-and-batch.js +40 -6
  477. package/dist/store/delivery-recorder.d.ts +12 -5
  478. package/dist/store/delivery-recorder.js +13 -11
  479. package/dist/store/descendant-page.js +8 -4
  480. package/dist/store/dormant.d.ts +1 -1
  481. package/dist/store/dormant.js +10 -4
  482. package/dist/store/embeddings/index.d.ts +2 -12
  483. package/dist/store/embeddings/index.js +5 -374
  484. package/dist/store/entry-reads.d.ts +35 -4
  485. package/dist/store/entry-reads.js +86 -19
  486. package/dist/store/entry-row.js +9 -2
  487. package/dist/store/entry-writes.d.ts +15 -12
  488. package/dist/store/entry-writes.js +54 -22
  489. package/dist/store/failure-log-at.d.ts +8 -0
  490. package/dist/store/failure-log-at.js +16 -0
  491. package/dist/store/failure-log.d.ts +2 -2
  492. package/dist/store/failure-log.js +5 -3
  493. package/dist/{trust → store}/gated-write.d.ts +3 -1
  494. package/dist/{trust → store}/gated-write.js +7 -5
  495. package/dist/store/goals.js +22 -19
  496. package/dist/store/graph-reads.js +9 -6
  497. package/dist/store/graph-view-rows.js +2 -1
  498. package/dist/store/graph-writes.d.ts +6 -6
  499. package/dist/store/graph-writes.js +48 -71
  500. package/dist/store/half-life.js +2 -2
  501. package/dist/store/handoffs.d.ts +10 -4
  502. package/dist/store/handoffs.js +10 -9
  503. package/dist/store/index-and-stats.js +2 -2
  504. package/dist/store/index.d.ts +2 -2
  505. package/dist/store/index.js +1 -1
  506. package/dist/store/key-writes.js +1 -1
  507. package/dist/store/ledger-turn.d.ts +9 -0
  508. package/dist/store/ledger-turn.js +38 -0
  509. package/dist/store/mirrors.js +19 -10
  510. package/dist/store/open.d.ts +2 -5
  511. package/dist/store/open.js +42 -54
  512. package/dist/store/pilot-arm.d.ts +12 -0
  513. package/dist/store/pilot-arm.js +42 -0
  514. package/dist/store/port.d.ts +68 -6
  515. package/dist/store/port.js +8 -0
  516. package/dist/store/predictions.d.ts +1 -2
  517. package/dist/store/predictions.js +3 -6
  518. package/dist/store/project-tags.d.ts +62 -0
  519. package/dist/store/project-tags.js +67 -0
  520. package/dist/store/quality-repair.js +1 -1
  521. package/dist/store/quarantine.js +2 -1
  522. package/dist/store/recall-trace.d.ts +2 -0
  523. package/dist/store/recall-trace.js +20 -3
  524. package/dist/store/rejected-values.js +16 -10
  525. package/dist/store/rejection-probe.d.ts +4 -0
  526. package/dist/store/rejection-probe.js +19 -0
  527. package/dist/store/rejection.d.ts +0 -17
  528. package/dist/store/rejection.js +1 -21
  529. package/dist/store/rows.js +2 -2
  530. package/dist/store/rule-sql.d.ts +11 -0
  531. package/dist/store/rule-sql.js +31 -6
  532. package/dist/store/search-rows.d.ts +5 -5
  533. package/dist/store/search-rows.js +18 -16
  534. package/dist/store/sessions.js +4 -2
  535. package/dist/store/set-aside.d.ts +14 -0
  536. package/dist/store/set-aside.js +23 -0
  537. package/dist/store/sqlite/busy.d.ts +3 -0
  538. package/dist/store/sqlite/busy.js +8 -0
  539. package/dist/store/sqlite/connector-events-group.js +4 -7
  540. package/dist/store/sqlite/connector-writes-group.js +6 -4
  541. package/dist/store/sqlite/entry-writes-group.d.ts +3 -3
  542. package/dist/store/sqlite/entry-writes-group.js +3 -3
  543. package/dist/store/sqlite/error-codec.js +4 -3
  544. package/dist/store/sqlite/executor.js +10 -4
  545. package/dist/store/sqlite/graph-reads-group.js +1 -1
  546. package/dist/store/sqlite/held-connection.d.ts +11 -0
  547. package/dist/store/sqlite/held-connection.js +41 -0
  548. package/dist/store/sqlite/last-recall-group.d.ts +13 -0
  549. package/dist/store/sqlite/last-recall-group.js +57 -0
  550. package/dist/store/sqlite/local.d.ts +0 -17
  551. package/dist/store/sqlite/local.js +0 -31
  552. package/dist/store/sqlite/maintenance-groups.d.ts +6 -0
  553. package/dist/store/sqlite/maintenance-groups.js +60 -0
  554. package/dist/store/sqlite/object-rows.d.ts +1 -1
  555. package/dist/store/sqlite/objects-group.d.ts +1 -1
  556. package/dist/store/sqlite/objects-group.js +15 -7
  557. package/dist/store/sqlite/store.d.ts +1 -8
  558. package/dist/store/sqlite/store.js +13 -22
  559. package/dist/store/summaries.d.ts +10 -3
  560. package/dist/store/summaries.js +54 -11
  561. package/dist/store/token-ledger-rows.js +3 -2
  562. package/dist/store/token-ledger.d.ts +0 -7
  563. package/dist/store/token-ledger.js +0 -35
  564. package/dist/store/vector-index.js +8 -4
  565. package/dist/support-bundle.js +2 -3
  566. package/dist/trust/reject-flow.js +2 -2
  567. package/dist/util/agent-homes.d.ts +15 -0
  568. package/dist/util/agent-homes.js +34 -0
  569. package/dist/util/anthropic-messages.d.ts +6 -1
  570. package/dist/util/anthropic-messages.js +26 -7
  571. package/dist/util/atomic-write.d.ts +5 -2
  572. package/dist/util/atomic-write.js +12 -8
  573. package/dist/util/chunked.d.ts +3 -0
  574. package/dist/util/chunked.js +10 -0
  575. package/dist/util/ellipsize.d.ts +3 -0
  576. package/dist/util/ellipsize.js +5 -0
  577. package/dist/util/env.d.ts +12 -2
  578. package/dist/util/env.js +12 -2
  579. package/dist/util/format.d.ts +5 -0
  580. package/dist/util/format.js +10 -0
  581. package/dist/util/hook-payload.d.ts +8 -0
  582. package/dist/util/hook-payload.js +37 -0
  583. package/dist/util/http-retry.d.ts +6 -0
  584. package/dist/util/http-retry.js +43 -5
  585. package/dist/util/http-util.d.ts +0 -8
  586. package/dist/util/http-util.js +10 -21
  587. package/dist/util/limits.d.ts +7 -0
  588. package/dist/util/limits.js +14 -0
  589. package/dist/util/log.d.ts +4 -0
  590. package/dist/util/log.js +9 -0
  591. package/dist/util/request-scope.d.ts +9 -3
  592. package/dist/util/request-scope.js +18 -9
  593. package/dist/util/secret-detect.js +6 -1
  594. package/dist/util/spawn-argv.d.ts +9 -0
  595. package/dist/util/spawn-argv.js +57 -0
  596. package/dist/util/sql.d.ts +3 -0
  597. package/dist/util/sql.js +6 -0
  598. package/dist/util/sqlite-blocked.d.ts +2 -0
  599. package/dist/util/sqlite-blocked.js +5 -0
  600. package/dist/util/stream-tee.d.ts +3 -0
  601. package/dist/util/stream-tee.js +35 -0
  602. package/dist/util/time.d.ts +4 -0
  603. package/dist/util/time.js +4 -0
  604. package/dist/util/vector-codec.d.ts +4 -0
  605. package/dist/util/vector-codec.js +15 -0
  606. package/dist/util/version.d.ts +1 -1
  607. package/dist/util/version.js +1 -1
  608. package/extensions/openclaw-plugin/openclaw.plugin.json +1 -1
  609. package/extensions/openclaw-plugin/package.json +1 -1
  610. package/openclaw.plugin.json +1 -1
  611. package/package.json +4 -2
  612. package/dist/api/on-store.d.ts +0 -16
  613. package/dist/api/on-store.js +0 -38
  614. package/dist/store/embeddings/local.d.ts +0 -15
  615. package/dist/store/embeddings/local.js +0 -175
  616. /package/dist/{cli → api}/correction-latency.d.ts +0 -0
  617. /package/dist/{cli → api}/correction-latency.js +0 -0
  618. /package/dist/{cli → api}/provenance-coverage.d.ts +0 -0
  619. /package/dist/{cli → api}/provenance-coverage.js +0 -0
  620. /package/dist/{store → core}/object-types.d.ts +0 -0
  621. /package/dist/{store → core}/object-types.js +0 -0
  622. /package/dist/{capture → util}/compaction-items.d.ts +0 -0
  623. /package/dist/{capture → util}/compaction-items.js +0 -0
  624. /package/dist/{store → util}/yaml.d.ts +0 -0
  625. /package/dist/{store → util}/yaml.js +0 -0
@@ -3,10 +3,6 @@ import { type DatabaseSyncLike } from '../db/index.js';
3
3
  export declare const TENANT_IS = "+tenant_id = ?";
4
4
  /** Read a memory entry by ID. With `tenantId` the read is tenant-scoped (cross-tenant returns null); omitted, no tenant filter is applied. */
5
5
  export declare function readEntry(hippoRoot: string, id: string, tenantId?: string): MemoryEntry | null;
6
- /** Ids per `IN (...)` list: far under SQLite's bound-parameter limit, with room for the tenant filter. */
7
- export declare const ID_CHUNK = 500;
8
- /** `items` in consecutive slices of at most `size`. */
9
- export declare function chunked<T>(items: readonly T[], size?: number): T[][];
10
6
  /** Answers whether a tenant's rows in this store hold an id: `ids` are looked up now, one query per chunk, and any other id on its first ask. */
11
7
  export declare function heldIdLookup(hippoRoot: string, tenantId: string, ids: readonly string[]): (id: string) => boolean;
12
8
  /** Rows by id on the caller's handle, one query per chunk; an id missing or in another tenant is absent from the map. */
@@ -28,6 +24,19 @@ export declare function loadFreshRawMemories(hippoRoot: string, count: number, t
28
24
  export declare function loadAllEntries(hippoRoot: string, tenantId?: string): MemoryEntry[];
29
25
  /** Every tenant's memory ids in loadAllEntries' order, for a caller that reads the rows a page at a time instead of holding them all. */
30
26
  export declare function loadAllEntryIds(hippoRoot: string): string[];
27
+ /** How many memory rows the store holds across every tenant. */
28
+ export declare function countAllEntries(hippoRoot: string): number;
29
+ /** A superset, in loadAllEntries' order, of the rows whose lowercased content holds any of `tokens` or whose lowercased tags hold `tag`.
30
+ * The caller re-checks each row exactly; this only spares decoding rows that cannot match. */
31
+ export declare function loadEntriesMentioning(hippoRoot: string, tokens: readonly string[], tag: string, tenantId?: string): MemoryEntry[];
32
+ export interface EntryTextKey {
33
+ id: string;
34
+ tenant_id: string;
35
+ content: string;
36
+ source: string;
37
+ }
38
+ /** The rows in loadAllEntries' order (every tenant unless one is named), with only the columns that say whether another store already holds each. */
39
+ export declare function loadEntryTextKeys(hippoRoot: string, tenantId?: string): EntryTextKey[];
31
40
  export interface EntriesWithBase {
32
41
  entries: MemoryEntry[];
33
42
  base: {
@@ -36,8 +45,30 @@ export interface EntriesWithBase {
36
45
  }
37
46
  /** Every entry plus a by-id lookup of each as loaded, rebuilt from its row on first use, so no deep copy of the store is kept. */
38
47
  export declare function loadAllEntriesWithBase(hippoRoot: string): EntriesWithBase;
48
+ /** Where a page stopped: the `created`, `id` of its last row, in loadAllEntries' order. */
49
+ export interface EntryCursor {
50
+ created: string;
51
+ id: string;
52
+ }
53
+ export interface EntryPage {
54
+ entries: MemoryEntry[];
55
+ /** The cursor that reads the page after this one, or null when this was the last. */
56
+ next: EntryCursor | null;
57
+ }
58
+ /** Up to `limit` rows in loadAllEntries' order, starting after `after`; omitting `tenantId` reads every tenant. */
59
+ export declare function loadEntriesPage(hippoRoot: string, tenantId: string | undefined, page: {
60
+ limit: number;
61
+ after?: EntryCursor;
62
+ }): EntryPage;
39
63
  /** Every memory row on an open connection, so a caller can read inside its own transaction. */
40
64
  export declare function selectAllEntries(db: DatabaseSyncLike, tenantId?: string): MemoryEntry[];
65
+ /** The columns a text match needs, for every row of a tenant in loadAllEntries' order: callers read whole rows only for the ids that match. */
66
+ export declare function selectTenantTexts(db: DatabaseSyncLike, tenantId: string): Array<{
67
+ id: string;
68
+ content: string;
69
+ source: string;
70
+ scope: string | null;
71
+ }>;
41
72
  /** Live rows whose source starts with `prefix`, on the caller's handle; LIKE folds case, so the prefix is checked again exactly. */
42
73
  export declare function selectLiveEntriesBySourcePrefix(db: DatabaseSyncLike, tenantId: string, prefix: string): MemoryEntry[];
43
74
  export declare function loadContentsWithTag(hippoRoot: string, tenantId: string, tag: string, origins?: readonly string[]): string[];
@@ -3,7 +3,10 @@ import { MEMORY_SELECT_COLUMNS, rowToEntry, parseJsonArray } from './rows.js';
3
3
  import { onHandle, openStore } from './open.js';
4
4
  import { escapeLike } from '../util/escape.js';
5
5
  import { originInSql } from '../core/project-identity.js';
6
- import { scopeAdmitSql } from './recall-scope.js';
6
+ import { scopeAdmitSql } from './rule-sql.js';
7
+ import { chunked } from '../util/chunked.js';
8
+ import { DEFAULT_TENANT_ID } from '../util/env.js';
9
+ import { placeholders } from '../util/sql.js';
7
10
  const MAX_IDS_PER_READ = 500;
8
11
  // The plus keeps SQLite on the primary key for an id list: with a bare tenant_id it walks every row of the tenant instead.
9
12
  export const TENANT_IS = '+tenant_id = ?';
@@ -18,15 +21,6 @@ export function readEntry(hippoRoot, id, tenantId) {
18
21
  return row ? rowToEntry(row) : null;
19
22
  }, openStore);
20
23
  }
21
- /** Ids per `IN (...)` list: far under SQLite's bound-parameter limit, with room for the tenant filter. */
22
- export const ID_CHUNK = 500;
23
- /** `items` in consecutive slices of at most `size`. */
24
- export function chunked(items, size = ID_CHUNK) {
25
- const out = [];
26
- for (let i = 0; i < items.length; i += size)
27
- out.push(items.slice(i, i + size));
28
- return out;
29
- }
30
24
  /** Answers whether a tenant's rows in this store hold an id: `ids` are looked up now, one query per chunk, and any other id on its first ask. */
31
25
  export function heldIdLookup(hippoRoot, tenantId, ids) {
32
26
  const held = new Map();
@@ -36,7 +30,7 @@ export function heldIdLookup(hippoRoot, tenantId, ids) {
36
30
  for (const id of chunk)
37
31
  held.set(id, false);
38
32
  // SAFETY: rows' shape matches the single `id` column selected.
39
- const rows = db.prepare(`SELECT id FROM memories WHERE id IN (${chunk.map(() => '?').join(',')}) AND ${TENANT_IS}`).all(...chunk, tenantId);
33
+ const rows = db.prepare(`SELECT id FROM memories WHERE id IN (${placeholders(chunk.length)}) AND ${TENANT_IS}`).all(...chunk, tenantId);
40
34
  for (const row of rows)
41
35
  held.set(row.id, true);
42
36
  }
@@ -56,9 +50,9 @@ export function selectEntriesByIds(db, ids, tenantId) {
56
50
  const tenantClause = tenantId !== undefined ? ` AND ${TENANT_IS}` : '';
57
51
  const tenantArgs = tenantId !== undefined ? [tenantId] : [];
58
52
  for (const chunk of chunked([...new Set(ids)])) {
59
- const placeholders = chunk.map(() => '?').join(',');
53
+ const marks = placeholders(chunk.length);
60
54
  // SAFETY: selects exactly MEMORY_SELECT_COLUMNS, matching MemoryRow's field set.
61
- const rows = db.prepare(`SELECT ${MEMORY_SELECT_COLUMNS} FROM memories WHERE id IN (${placeholders})${tenantClause}`).all(...chunk, ...tenantArgs);
55
+ const rows = db.prepare(`SELECT ${MEMORY_SELECT_COLUMNS} FROM memories WHERE id IN (${marks})${tenantClause}`).all(...chunk, ...tenantArgs);
62
56
  for (const row of rows)
63
57
  byId.set(row.id, rowToEntry(row));
64
58
  }
@@ -70,9 +64,9 @@ export function selectChildrenByParent(db, parentIds, tenantId) {
70
64
  const tenantClause = tenantId !== undefined ? ` AND ${TENANT_IS}` : '';
71
65
  const tenantArgs = tenantId !== undefined ? [tenantId] : [];
72
66
  for (const chunk of chunked([...new Set(parentIds)])) {
73
- const placeholders = chunk.map(() => '?').join(',');
67
+ const marks = placeholders(chunk.length);
74
68
  // SAFETY: selects exactly MEMORY_SELECT_COLUMNS, matching MemoryRow's field set.
75
- const rows = db.prepare(`SELECT ${MEMORY_SELECT_COLUMNS} FROM memories WHERE dag_parent_id IN (${placeholders})${tenantClause} ORDER BY created ASC, id ASC`).all(...chunk, ...tenantArgs);
69
+ const rows = db.prepare(`SELECT ${MEMORY_SELECT_COLUMNS} FROM memories WHERE dag_parent_id IN (${marks})${tenantClause} ORDER BY created ASC, id ASC`).all(...chunk, ...tenantArgs);
76
70
  for (const row of rows) {
77
71
  const entry = rowToEntry(row);
78
72
  const parentId = entry.dag_parent_id ?? '';
@@ -91,12 +85,12 @@ export function loadEntriesByIds(hippoRoot, ids, tenantId) {
91
85
  return [];
92
86
  const capped = ids.slice(0, MAX_IDS_PER_READ);
93
87
  return onHandle(hippoRoot, (db) => {
94
- const placeholders = capped.map(() => '?').join(',');
88
+ const marks = placeholders(capped.length);
95
89
  // Without ORDER BY, rows follow SQLite's IN(...) scan order, undefined w.r.t. `ids`.
96
90
  // SAFETY: both branches select exactly MEMORY_SELECT_COLUMNS, matching MemoryRow's field set.
97
91
  const rows = tenantId !== undefined
98
- ? db.prepare(`SELECT ${MEMORY_SELECT_COLUMNS} FROM memories WHERE id IN (${placeholders}) AND ${TENANT_IS} ORDER BY created ASC, content ASC, id ASC`).all(...capped, tenantId)
99
- : db.prepare(`SELECT ${MEMORY_SELECT_COLUMNS} FROM memories WHERE id IN (${placeholders}) ORDER BY created ASC, content ASC, id ASC`).all(...capped);
92
+ ? db.prepare(`SELECT ${MEMORY_SELECT_COLUMNS} FROM memories WHERE id IN (${marks}) AND ${TENANT_IS} ORDER BY created ASC, content ASC, id ASC`).all(...capped, tenantId)
93
+ : db.prepare(`SELECT ${MEMORY_SELECT_COLUMNS} FROM memories WHERE id IN (${marks}) ORDER BY created ASC, content ASC, id ASC`).all(...capped);
100
94
  return rows.map(rowToEntry);
101
95
  }, openStore);
102
96
  }
@@ -201,6 +195,50 @@ export function loadAllEntryIds(hippoRoot) {
201
195
  return db.prepare('SELECT id FROM memories ORDER BY created ASC, id ASC').all().map((row) => row.id);
202
196
  }, openStore);
203
197
  }
198
+ /** How many memory rows the store holds across every tenant. */
199
+ export function countAllEntries(hippoRoot) {
200
+ return onHandle(hippoRoot, (db) => {
201
+ // SAFETY: row's shape matches the single `COUNT(*) AS c` column.
202
+ const row = db.prepare('SELECT COUNT(*) AS c FROM memories').get();
203
+ return Number(row?.c ?? 0);
204
+ }, openStore);
205
+ }
206
+ // The only code points whose JS toLowerCase yields ASCII, which SQLite's ASCII-only LIKE folding cannot see.
207
+ const FOLDS_TO_ASCII = ['\u0130', '\u212A'];
208
+ /** A superset, in loadAllEntries' order, of the rows whose lowercased content holds any of `tokens` or whose lowercased tags hold `tag`.
209
+ * The caller re-checks each row exactly; this only spares decoding rows that cannot match. */
210
+ export function loadEntriesMentioning(hippoRoot, tokens, tag, tenantId) {
211
+ const likes = [...new Set(tokens)].map((t) => `%${escapeLike(t)}%`);
212
+ const clauses = likes.map(() => `content LIKE ? ESCAPE '\\'`);
213
+ const params = [...likes];
214
+ // A tag needing JSON escapes or case folding past ASCII is not found by a substring of tags_json, so every tagged row stays in.
215
+ if (/^[\x20-\x7e]*$/.test(tag) && !/["\\]/.test(tag)) {
216
+ clauses.push(`tags_json LIKE ? ESCAPE '\\'`);
217
+ params.push(`%${escapeLike(tag)}%`);
218
+ }
219
+ else
220
+ clauses.push(`COALESCE(tags_json, '[]') != '[]'`);
221
+ for (const ch of FOLDS_TO_ASCII) {
222
+ clauses.push('instr(content, ?) > 0', 'instr(tags_json, ?) > 0');
223
+ params.push(ch, ch);
224
+ }
225
+ const tenantClause = tenantId !== undefined ? 'tenant_id = ? AND ' : '';
226
+ return onHandle(hippoRoot, (db) => {
227
+ // SAFETY: selects exactly MEMORY_SELECT_COLUMNS, matching MemoryRow's field set.
228
+ const rows = db.prepare(`SELECT ${MEMORY_SELECT_COLUMNS} FROM memories WHERE ${tenantClause}(${clauses.join(' OR ')}) ORDER BY created ASC, id ASC`).all(...(tenantId !== undefined ? [tenantId] : []), ...params);
229
+ return rows.map(rowToEntry);
230
+ }, openStore);
231
+ }
232
+ /** The rows in loadAllEntries' order (every tenant unless one is named), with only the columns that say whether another store already holds each. */
233
+ export function loadEntryTextKeys(hippoRoot, tenantId) {
234
+ return onHandle(hippoRoot, (db) => {
235
+ const where = tenantId !== undefined ? 'WHERE tenant_id = ? ' : '';
236
+ // SAFETY: rows' shape matches the four columns named in the SELECT.
237
+ const rows = db.prepare(`SELECT id, tenant_id, content, source FROM memories ${where}ORDER BY created ASC, id ASC`)
238
+ .all(...(tenantId !== undefined ? [tenantId] : []));
239
+ return rows.map((row) => ({ id: row.id, tenant_id: row.tenant_id ?? DEFAULT_TENANT_ID, content: row.content, source: row.source }));
240
+ }, openStore);
241
+ }
204
242
  /** Every entry plus a by-id lookup of each as loaded, rebuilt from its row on first use, so no deep copy of the store is kept. */
205
243
  export function loadAllEntriesWithBase(hippoRoot) {
206
244
  const db = openStore(hippoRoot);
@@ -229,10 +267,38 @@ function selectAllRows(db, tenantId) {
229
267
  ? db.prepare(`SELECT ${MEMORY_SELECT_COLUMNS} FROM memories WHERE tenant_id = ? ORDER BY created ASC, id ASC`).all(tenantId)
230
268
  : db.prepare(`SELECT ${MEMORY_SELECT_COLUMNS} FROM memories ORDER BY created ASC, id ASC`).all();
231
269
  }
270
+ /** Up to `limit` rows in loadAllEntries' order, starting after `after`; omitting `tenantId` reads every tenant. */
271
+ export function loadEntriesPage(hippoRoot, tenantId, page) {
272
+ const where = [];
273
+ const params = [];
274
+ if (tenantId !== undefined) {
275
+ where.push('tenant_id = ?');
276
+ params.push(tenantId);
277
+ }
278
+ if (page.after) {
279
+ where.push('(created > ? OR (created = ? AND id > ?))');
280
+ params.push(page.after.created, page.after.created, page.after.id);
281
+ }
282
+ const clause = where.length > 0 ? `WHERE ${where.join(' AND ')} ` : '';
283
+ // One extra row says whether another page follows without a count.
284
+ params.push(page.limit + 1);
285
+ return onHandle(hippoRoot, (db) => {
286
+ // SAFETY: selects exactly MEMORY_SELECT_COLUMNS, matching MemoryRow's field set.
287
+ const rows = db.prepare(`SELECT ${MEMORY_SELECT_COLUMNS} FROM memories ${clause}ORDER BY created ASC, id ASC LIMIT ?`).all(...params);
288
+ const entries = rows.slice(0, page.limit).map(rowToEntry);
289
+ const last = entries[entries.length - 1];
290
+ return { entries, next: rows.length > page.limit && last ? { created: last.created, id: last.id } : null };
291
+ }, openStore);
292
+ }
232
293
  /** Every memory row on an open connection, so a caller can read inside its own transaction. */
233
294
  export function selectAllEntries(db, tenantId) {
234
295
  return selectAllRows(db, tenantId).map(rowToEntry);
235
296
  }
297
+ /** The columns a text match needs, for every row of a tenant in loadAllEntries' order: callers read whole rows only for the ids that match. */
298
+ export function selectTenantTexts(db, tenantId) {
299
+ // SAFETY: rows' shape matches the four columns named in the SELECT.
300
+ return db.prepare('SELECT id, content, source, scope FROM memories WHERE tenant_id = ? ORDER BY created ASC, id ASC').all(tenantId);
301
+ }
236
302
  /** Live rows whose source starts with `prefix`, on the caller's handle; LIKE folds case, so the prefix is checked again exactly. */
237
303
  export function selectLiveEntriesBySourcePrefix(db, tenantId, prefix) {
238
304
  // SAFETY: selects exactly MEMORY_SELECT_COLUMNS, matching MemoryRow's field set.
@@ -265,7 +331,8 @@ export function loadVaultRawRows(hippoRoot, likeParam, tenantId) {
265
331
  export function selectPreviewRows(db, columns, tenantId) {
266
332
  const tenant = columns.has('tenant_id') ? ' WHERE tenant_id = ?' : '';
267
333
  const column = (name, fallback) => (columns.has(name) ? name : `${fallback} AS ${name}`);
268
- const statement = db.prepare(`SELECT id, content, ${column('source', "''")}, ${column('confidence', 'NULL')}, ${column('extracted_from', 'NULL')}, ${column('dag_level', '0')}, ${column('tags_json', "'[]'")} FROM memories${tenant}`);
334
+ const statement = db.prepare(`SELECT id, content, ${column('source', "''")}, ${column('confidence', 'NULL')}, ${column('extracted_from', 'NULL')}, ` +
335
+ `${column('dag_level', '0')}, ${column('tags_json', "'[]'")} FROM memories${tenant}`);
269
336
  // SAFETY: the SELECT names every PreviewRow field, each a literal fallback when its column is missing.
270
337
  return (tenant ? statement.all(tenantId) : statement.all());
271
338
  }
@@ -3,8 +3,15 @@ import { closeHippoDb, ftsRowCounts, openHippoDb, isFtsAvailable, repairFtsDrift
3
3
  import { fallbackOrigin, originFromSource } from '../core/project-identity.js';
4
4
  import { checkRejectionGuard } from './rejection.js';
5
5
  import { errorMessage, log } from '../util/log.js';
6
+ /** A 1.x JS caller's `tenantId` would otherwise fall through to the default tenant without a sound. */
7
+ function rejectLegacyTenantKey(entry) {
8
+ if (Object.hasOwn(entry, 'tenantId') && entry.tenant_id === undefined) {
9
+ throw new TypeError('tenantId was renamed to tenant_id in 2.0');
10
+ }
11
+ }
6
12
  export function upsertEntryRow(db, entry) {
7
- checkRejectionGuard(db, entry.tenantId ?? DEFAULT_TENANT_ID, entry.id, entry.content);
13
+ rejectLegacyTenantKey(entry);
14
+ checkRejectionGuard(db, entry.tenant_id ?? DEFAULT_TENANT_ID, entry.id, entry.content);
8
15
  syncFtsRow(db, entry, upsertMemoryRow(db, entry));
9
16
  }
10
17
  /** The row alone, with no rejection guard or full-text row, for `upsertEntryRow` and for
@@ -104,7 +111,7 @@ function memoryRowValues(entry) {
104
111
  entry.scope ?? null,
105
112
  entry.owner ?? null,
106
113
  entry.artifact_ref ?? null,
107
- entry.tenantId ?? DEFAULT_TENANT_ID,
114
+ entry.tenant_id ?? DEFAULT_TENANT_ID,
108
115
  entry.origin_project ?? null,
109
116
  entry.descendant_count ?? 0,
110
117
  entry.earliest_at ?? null,
@@ -2,28 +2,31 @@ import { type MemoryEntry } from '../core/memory.js';
2
2
  import { type DatabaseSyncLike } from '../db/index.js';
3
3
  export interface WriteEntryOptions {
4
4
  actor?: string;
5
- afterWrite?: (db: DatabaseSyncLike, memoryId: string) => void;
6
5
  /** Runs after the row commits and before the mirrors, on an idle connection; keep it best-effort. */
7
6
  afterCommit?: () => void;
8
7
  }
9
- /** Write an entry to SQLite and refresh mirrors; `opts.afterWrite` runs in the same SAVEPOINT as the INSERT, so a throw rolls the row back.
10
- * `opts.actor` defaults to 'cli'; the api layer passes the resolved actor so the audit log gets one row per write. */
8
+ /** Store-side only, never exported from a package entry: `afterWrite` hands the write's own db handle to the hook. */
9
+ export interface WriteEntryInternalOptions extends WriteEntryOptions {
10
+ /** Runs in the same SAVEPOINT as the INSERT, so a throw rolls the row back. */
11
+ afterWrite?: (db: DatabaseSyncLike, memoryId: string) => void;
12
+ }
13
+ /** Write an entry to SQLite and refresh mirrors. `opts.actor` defaults to 'cli'; the api layer passes the resolved actor,
14
+ * so the audit log gets one row per write. */
11
15
  export declare function writeEntry(hippoRoot: string, entry: MemoryEntry, opts?: WriteEntryOptions): void;
16
+ /** writeEntry for store code that needs the write's own handle: `opts.afterWrite` shares the INSERT's SAVEPOINT. */
17
+ export declare function writeEntryWithHook(hippoRoot: string, entry: MemoryEntry, opts?: WriteEntryInternalOptions): void;
12
18
  /** writeEntry for each of `entries` in one transaction, so all land or none; each
13
- * keeps its audit row, and mirrors follow the commit. Returns how many were written. */
19
+ * keeps its audit row, and mirrors follow the commit. Returns how many were written; an empty list opens no store. */
14
20
  export declare function writeEntriesTogether(hippoRoot: string, entries: readonly MemoryEntry[]): number;
15
- /** writeEntry for each of `entries` on one open store, so the open and its lock wait are
16
- * paid once; each row commits alone, and none opens the store when the list is empty. */
17
- export declare function writeEntriesSeparately(hippoRoot: string, entries: readonly MemoryEntry[]): void;
21
+ /** Runs `fill` in one transaction; each `put` writes one entry and returns false when the store rejects its value,
22
+ * leaving that row's refusal audit and the rest of the batch. Mirrors follow the commit. Returns how many were written. */
23
+ export declare function writeEntriesSkippingRejected(hippoRoot: string, fill: (put: (entry: MemoryEntry) => boolean) => void, actor?: string): number;
18
24
  /** Adds `tag` to each of a tenant's rows that lacks it, in `ids` order, each read fresh
19
25
  * on one open store and committed alone; an id the tenant does not hold is skipped. */
20
26
  export declare function addTagToEntries(hippoRoot: string, tenantId: string, ids: readonly string[], tag: string): void;
21
27
  /** DB-only write path: upsert, afterWrite hook and audit row inside one withWriteScope; the caller owns `db` and must call `writeEntryMirrors` after any
22
28
  * outer tx commits, so a rolled-back tx never leaves orphan markdown. The audit row commits atomically with the INSERT. */
23
- export declare function writeEntryDbOnly(db: DatabaseSyncLike, entry: MemoryEntry, opts?: {
24
- actor?: string;
25
- afterWrite?: (db: DatabaseSyncLike, memoryId: string) => void;
26
- }): void;
29
+ export declare function writeEntryDbOnly(db: DatabaseSyncLike, entry: MemoryEntry, opts?: Pick<WriteEntryInternalOptions, 'actor' | 'afterWrite'>): void;
27
30
  /** What follows a row's upsert inside its write scope: the remember row, last of the write's audit rows, then the summary parent's dirty mark. */
28
31
  export declare function auditEntryWrite(db: DatabaseSyncLike, entry: MemoryEntry, actor: string): void;
29
32
  /** Markdown mirror path, invoked AFTER commit (a rolled-back tx must leave no orphan markdown). */
@@ -36,7 +39,7 @@ export interface StrengthenOptions {
36
39
  /** Strengthen what a read returned: update only the four retrieval columns on the live row, never a stale copy.
37
40
  * Best effort: a failure logs and never fails the read. Returns the ids found in this store. */
38
41
  export declare function strengthenRetrieved(hippoRoot: string, ids: readonly string[], opts: StrengthenOptions): Set<string>;
39
- /** strengthenRetrieved on an open handle that holds no transaction, so a recall's last writes share one handle. */
42
+ /** strengthenRetrieved on an open handle in its own write scope, a savepoint inside a caller's transaction, so a recall's last writes share one handle. */
40
43
  export declare function strengthenRetrievedInOwnTx(db: DatabaseSyncLike, ids: readonly string[], opts: StrengthenOptions): Set<string>;
41
44
  /** strengthenRetrieved on the caller's handle, inside the caller's transaction. Throws; the caller decides. */
42
45
  export declare function strengthenRetrievedOn(db: DatabaseSyncLike, ids: readonly string[], opts: StrengthenOptions): Set<string>;
@@ -1,6 +1,9 @@
1
+ import { strengthAblation } from '../core/ablation.js';
1
2
  import { markRetrieved } from '../core/memory.js';
2
- import { closeHippoDb, openHippoDb, rethrowIfSqliteBlocked, withWriteScope } from '../db/index.js';
3
- import { RejectedValueError } from './rejection.js';
3
+ import { closeHippoDb, openHippoDb, withWriteScope } from '../db/index.js';
4
+ import { rethrowIfSqliteBlocked } from '../util/sqlite-blocked.js';
5
+ import { chunked } from '../util/chunked.js';
6
+ import { RejectedValueError } from '../core/api-errors.js';
4
7
  import { markSummaryDirtyInTx } from './summary-dirty.js';
5
8
  import { errorMessage, log } from '../util/log.js';
6
9
  import { auditRejectionRefusal, audit } from './audit-event.js';
@@ -8,16 +11,22 @@ import { selectEntriesByIds } from './entry-reads.js';
8
11
  import { stampOriginProject, upsertEntryRow, syncFtsRow, deleteFtsRow } from './entry-row.js';
9
12
  import { mirrorBestEffort, writeMarkdownMirror } from './mirrors.js';
10
13
  import { onHandle, openStore } from './open.js';
11
- /** Write an entry to SQLite and refresh mirrors; `opts.afterWrite` runs in the same SAVEPOINT as the INSERT, so a throw rolls the row back.
12
- * `opts.actor` defaults to 'cli'; the api layer passes the resolved actor so the audit log gets one row per write. */
14
+ /** Write an entry to SQLite and refresh mirrors. `opts.actor` defaults to 'cli'; the api layer passes the resolved actor,
15
+ * so the audit log gets one row per write. */
13
16
  export function writeEntry(hippoRoot, entry, opts) {
17
+ writeEntryWithHook(hippoRoot, entry, opts);
18
+ }
19
+ /** writeEntry for store code that needs the write's own handle: `opts.afterWrite` shares the INSERT's SAVEPOINT. */
20
+ export function writeEntryWithHook(hippoRoot, entry, opts) {
14
21
  onHandle(hippoRoot, (db) => {
15
22
  writeEntryOn(db, hippoRoot, entry, opts);
16
23
  }, openStore);
17
24
  }
18
25
  /** writeEntry for each of `entries` in one transaction, so all land or none; each
19
- * keeps its audit row, and mirrors follow the commit. Returns how many were written. */
26
+ * keeps its audit row, and mirrors follow the commit. Returns how many were written; an empty list opens no store. */
20
27
  export function writeEntriesTogether(hippoRoot, entries) {
28
+ if (entries.length === 0)
29
+ return 0;
21
30
  const stamped = entries.map((entry) => stampOriginProject(hippoRoot, entry));
22
31
  const db = openStore(hippoRoot);
23
32
  try {
@@ -39,15 +48,31 @@ export function writeEntriesTogether(hippoRoot, entries) {
39
48
  }
40
49
  return stamped.length;
41
50
  }
42
- /** writeEntry for each of `entries` on one open store, so the open and its lock wait are
43
- * paid once; each row commits alone, and none opens the store when the list is empty. */
44
- export function writeEntriesSeparately(hippoRoot, entries) {
45
- if (entries.length === 0)
46
- return;
51
+ /** Runs `fill` in one transaction; each `put` writes one entry and returns false when the store rejects its value,
52
+ * leaving that row's refusal audit and the rest of the batch. Mirrors follow the commit. Returns how many were written. */
53
+ export function writeEntriesSkippingRejected(hippoRoot, fill, actor = 'cli') {
54
+ const written = [];
47
55
  onHandle(hippoRoot, (db) => {
48
- for (const entry of entries)
49
- writeEntryOn(db, hippoRoot, entry);
56
+ withWriteScope(db, 'write_entries_skipping_rejected', () => {
57
+ fill((entry) => {
58
+ const stamped = stampOriginProject(hippoRoot, entry);
59
+ try {
60
+ writeEntryDbOnly(db, stamped, { actor });
61
+ }
62
+ catch (error) {
63
+ if (!(error instanceof RejectedValueError))
64
+ throw error;
65
+ auditRejectionRefusal(db, error, actor);
66
+ return false;
67
+ }
68
+ written.push(stamped);
69
+ return true;
70
+ });
71
+ });
50
72
  }, openStore);
73
+ for (const entry of written)
74
+ writeEntryMirrors(hippoRoot, entry);
75
+ return written.length;
51
76
  }
52
77
  /** Adds `tag` to each of a tenant's rows that lacks it, in `ids` order, each read fresh
53
78
  * on one open store and committed alone; an id the tenant does not hold is skipped. */
@@ -97,11 +122,11 @@ export function auditEntryWrite(db, entry, actor) {
97
122
  audit(db, 'remember', { targetId: entry.id, metadata: {
98
123
  kind: entry.kind ?? 'distilled',
99
124
  scope: entry.scope ?? null,
100
- }, actor, tenantId: entry.tenantId });
125
+ }, actor, tenantId: entry.tenant_id });
101
126
  // A child write marks its summary parent dirty for the sleep-cycle rebuild; most writes
102
127
  // have no parent, so the hot path pays one null check.
103
128
  if (entry.dag_parent_id) {
104
- markSummaryDirtyInTx(db, entry.dag_parent_id, entry.tenantId, actor);
129
+ markSummaryDirtyInTx(db, entry.dag_parent_id, entry.tenant_id, actor);
105
130
  }
106
131
  }
107
132
  /** Markdown mirror path, invoked AFTER commit (a rolled-back tx must leave no orphan markdown). */
@@ -129,7 +154,7 @@ export function strengthenRetrieved(hippoRoot, ids, opts) {
129
154
  closeHippoDb(db);
130
155
  }
131
156
  }
132
- /** strengthenRetrieved on an open handle that holds no transaction, so a recall's last writes share one handle. */
157
+ /** strengthenRetrieved on an open handle in its own write scope, a savepoint inside a caller's transaction, so a recall's last writes share one handle. */
133
158
  export function strengthenRetrievedInOwnTx(db, ids, opts) {
134
159
  if (ids.length === 0 || opts.recallBoostAblated)
135
160
  return new Set();
@@ -144,6 +169,8 @@ export function strengthenRetrievedInOwnTx(db, ids, opts) {
144
169
  function warnStrengthenFailed(error) {
145
170
  log.warn(`retrieval stats not saved (${errorMessage(error)})`);
146
171
  }
172
+ /** Rows per recall-write statement: five bound values each, far under SQLite's parameter limit. */
173
+ const RETRIEVAL_UPDATE_CHUNK = 100;
147
174
  /** strengthenRetrieved on the caller's handle, inside the caller's transaction. Throws; the caller decides. */
148
175
  export function strengthenRetrievedOn(db, ids, opts) {
149
176
  const found = new Set();
@@ -151,25 +178,30 @@ export function strengthenRetrievedOn(db, ids, opts) {
151
178
  return found;
152
179
  const byId = selectEntriesByIds(db, ids, opts.tenantId);
153
180
  const live = [...new Set(ids)].flatMap((id) => byId.get(id) ?? []);
154
- const update = db.prepare('UPDATE memories SET retrieval_count = ?, last_retrieved = ?, half_life_days = ?, strength = ? WHERE id = ?');
155
- for (const e of markRetrieved(live)) {
156
- update.run(e.retrieval_count, e.last_retrieved, e.half_life_days, e.strength, e.id);
157
- found.add(e.id);
181
+ const updated = markRetrieved(live, undefined, { ablation: strengthAblation() });
182
+ // One UPDATE ... FROM per chunk of rows instead of one statement per row.
183
+ for (const chunk of chunked(updated, RETRIEVAL_UPDATE_CHUNK)) {
184
+ const values = chunk.map(() => '(?, ?, ?, ?, ?)').join(', ');
185
+ db.prepare(`WITH v(id, rc, lr, hl, st) AS (VALUES ${values})
186
+ UPDATE memories SET retrieval_count = v.rc, last_retrieved = v.lr, half_life_days = v.hl, strength = v.st
187
+ FROM v WHERE memories.id = v.id`).run(...chunk.flatMap((e) => [e.id, e.retrieval_count, e.last_retrieved, e.half_life_days, e.strength]));
188
+ for (const e of chunk)
189
+ found.add(e.id);
158
190
  }
159
191
  return found;
160
192
  }
161
193
  /** Rewrites a live row's tags and its full-text row on the caller's transaction, with no audit row. */
162
194
  export function setEntryTagsInTx(db, entry) {
163
195
  db.prepare(`UPDATE memories SET tags_json = ?, updated_at = datetime('now') WHERE id = ? AND tenant_id = ?`)
164
- .run(JSON.stringify(entry.tags), entry.id, entry.tenantId);
196
+ .run(JSON.stringify(entry.tags), entry.id, entry.tenant_id);
165
197
  syncFtsRow(db, entry);
166
198
  }
167
199
  /** Removes a row from `memories` and full-text search on the caller's transaction, as sleep's dormant move does, and marks its summary parent dirty. */
168
200
  export function deleteEntryRowInTx(db, entry, actor) {
169
- db.prepare('DELETE FROM memories WHERE id = ? AND tenant_id = ?').run(entry.id, entry.tenantId);
201
+ db.prepare('DELETE FROM memories WHERE id = ? AND tenant_id = ?').run(entry.id, entry.tenant_id);
170
202
  deleteFtsRow(db, entry.id);
171
203
  if (entry.dag_parent_id)
172
- markSummaryDirtyInTx(db, entry.dag_parent_id, entry.tenantId, actor);
204
+ markSummaryDirtyInTx(db, entry.dag_parent_id, entry.tenant_id, actor);
173
205
  }
174
206
  /** One half-life per memory id, on the caller's transaction. */
175
207
  export function setHalfLivesAt(db, rows) {
@@ -0,0 +1,8 @@
1
+ import { type FailureEvent, type FailureOutcome } from './failure-log.js';
2
+ /** {@link recordFailure} on a handle of its own. */
3
+ export declare function recordFailureAt(hippoRoot: string, event: FailureEvent): number;
4
+ /** {@link requestOutcome} on a handle of its own. */
5
+ export declare function requestOutcomeAt(hippoRoot: string, tenantId: string, requestId: string, sessionId: string): FailureOutcome | null;
6
+ /** {@link settleFailureOutcome} on a handle of its own. */
7
+ export declare function settleFailureOutcomeAt(hippoRoot: string, tenantId: string, requestId: string, outcome: FailureOutcome): void;
8
+ //# sourceMappingURL=failure-log-at.d.ts.map
@@ -0,0 +1,16 @@
1
+ // The failure log on a handle of its own, for code that has a folder and no connection.
2
+ import { onHandle } from './open.js';
3
+ import { recordFailure, requestOutcome, settleFailureOutcome } from './failure-log.js';
4
+ /** {@link recordFailure} on a handle of its own. */
5
+ export function recordFailureAt(hippoRoot, event) {
6
+ return onHandle(hippoRoot, (db) => recordFailure(db, event));
7
+ }
8
+ /** {@link requestOutcome} on a handle of its own. */
9
+ export function requestOutcomeAt(hippoRoot, tenantId, requestId, sessionId) {
10
+ return onHandle(hippoRoot, (db) => requestOutcome(db, tenantId, requestId, sessionId));
11
+ }
12
+ /** {@link settleFailureOutcome} on a handle of its own. */
13
+ export function settleFailureOutcomeAt(hippoRoot, tenantId, requestId, outcome) {
14
+ onHandle(hippoRoot, (db) => settleFailureOutcome(db, tenantId, requestId, outcome));
15
+ }
16
+ //# sourceMappingURL=failure-log-at.js.map
@@ -28,8 +28,8 @@ export interface FailureEvent {
28
28
  /** Override the timestamp (tests). ISO string. */
29
29
  now?: string;
30
30
  }
31
- /** Append one failure row and prune rows past {@link FAILURE_LOG_RETENTION_DAYS}. */
32
- export declare function recordFailure(db: DatabaseSyncLike, event: FailureEvent): void;
31
+ /** Prune rows past {@link FAILURE_LOG_RETENTION_DAYS}, then append one failure row; returns the new row's id. */
32
+ export declare function recordFailure(db: DatabaseSyncLike, event: FailureEvent): number;
33
33
  /** The outcome logged for a caller's request id, or null, so a retried send finds the first one; another session's id is a ConflictError. */
34
34
  export declare function requestOutcome(db: DatabaseSyncLike, tenantId: string, requestId: string, sessionId: string): FailureOutcome | null;
35
35
  /** A retry that stored what the first try could not rewrites that row, since the request id allows one row per tenant. */
@@ -5,14 +5,16 @@ import { DAY_MS } from '../util/time.js';
5
5
  export const FAILURE_LOG_RETENTION_DAYS = 90;
6
6
  /** Longest session id or tool name kept; the hook payload is not trusted to be short. */
7
7
  const MAX_FIELD = 128;
8
- /** Append one failure row and prune rows past {@link FAILURE_LOG_RETENTION_DAYS}. */
8
+ /** Prune rows past {@link FAILURE_LOG_RETENTION_DAYS}, then append one failure row; returns the new row's id. */
9
9
  export function recordFailure(db, event) {
10
10
  // Normalised, because the window and prune compare timestamps as strings.
11
11
  const now = new Date(event.now ?? Date.now()).toISOString();
12
- db.prepare(`INSERT INTO failure_log (ts, tenant_id, session_id, tool, outcome, skip_rule, sig_hash, detail_hash, owner_subject, origin_project, request_id)
13
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(now, event.tenantId, event.sessionId?.slice(0, MAX_FIELD) ?? null, event.tool?.slice(0, MAX_FIELD) ?? null, event.outcome, event.rule ?? null, event.sigHash ?? null, event.detailHash ?? null, event.ownerSubject ?? null, event.originProject ?? null, event.requestId ?? null);
12
+ // Prune first: nothing after the insert can throw, so a returned id always means a committed row.
14
13
  const cutoff = new Date(Date.parse(now) - FAILURE_LOG_RETENTION_DAYS * DAY_MS).toISOString();
15
14
  db.prepare(`DELETE FROM failure_log WHERE ts < ?`).run(cutoff);
15
+ const inserted = db.prepare(`INSERT INTO failure_log (ts, tenant_id, session_id, tool, outcome, skip_rule, sig_hash, detail_hash, owner_subject, origin_project, request_id)
16
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(now, event.tenantId, event.sessionId?.slice(0, MAX_FIELD) ?? null, event.tool?.slice(0, MAX_FIELD) ?? null, event.outcome, event.rule ?? null, event.sigHash ?? null, event.detailHash ?? null, event.ownerSubject ?? null, event.originProject ?? null, event.requestId ?? null);
17
+ return Number(inserted.lastInsertRowid);
16
18
  }
17
19
  /** The outcome logged for a caller's request id, or null, so a retried send finds the first one; another session's id is a ConflictError. */
18
20
  export function requestOutcome(db, tenantId, requestId, sessionId) {
@@ -1,7 +1,9 @@
1
1
  import type { DatabaseSyncLike } from '../db/index.js';
2
2
  import type { MemoryEntry } from '../core/memory.js';
3
3
  export type GatedWriteResult = 'written' | 'skipped:not-worth-storing' | 'skipped:secret' | 'skipped:rejected';
4
- /** Runs on the caller's handle so it nests in the caller's transaction (writeEntry would open a second handle and wait on that lock); the caller mirrors after commit with an entry it stamped itself. The rejection audit lands inside that transaction, which is safe because a batch that rolls back stays `summarised` and replay writes the audit again. */
4
+ /** Runs on the caller's handle so it nests in the caller's transaction (writeEntry would open a second handle and wait on that lock); the
5
+ * caller mirrors after commit with an entry it stamped itself. The rejection audit lands inside that transaction, which is safe because
6
+ * a batch that rolls back stays `summarised` and replay writes the audit again. */
5
7
  export declare function gatedWrite(db: DatabaseSyncLike, hippoRoot: string, entry: MemoryEntry, opts?: {
6
8
  actor?: string;
7
9
  worthCheck?: boolean;
@@ -1,11 +1,13 @@
1
1
  // The one write path for text no person typed into hippo: capture, compaction items and imported agent memories.
2
2
  import { isContentWorthStoring } from '../core/memory-quality.js';
3
- import { RejectedValueError } from '../store/rejection.js';
3
+ import { RejectedValueError } from '../core/api-errors.js';
4
4
  import { detectSecret } from '../util/secret-detect.js';
5
- import { auditRejectionRefusal } from '../store/audit-event.js';
6
- import { stampOriginProject } from '../store/entry-row.js';
7
- import { writeEntryDbOnly } from '../store/entry-writes.js';
8
- /** Runs on the caller's handle so it nests in the caller's transaction (writeEntry would open a second handle and wait on that lock); the caller mirrors after commit with an entry it stamped itself. The rejection audit lands inside that transaction, which is safe because a batch that rolls back stays `summarised` and replay writes the audit again. */
5
+ import { auditRejectionRefusal } from './audit-event.js';
6
+ import { stampOriginProject } from './entry-row.js';
7
+ import { writeEntryDbOnly } from './entry-writes.js';
8
+ /** Runs on the caller's handle so it nests in the caller's transaction (writeEntry would open a second handle and wait on that lock); the
9
+ * caller mirrors after commit with an entry it stamped itself. The rejection audit lands inside that transaction, which is safe because
10
+ * a batch that rolls back stays `summarised` and replay writes the audit again. */
9
11
  export function gatedWrite(db, hippoRoot, entry, opts) {
10
12
  // Off for imported agent memories: a person wrote those notes, and a one-line preference fails the check.
11
13
  if (opts?.worthCheck !== false && !isContentWorthStoring(entry.content))
@@ -2,6 +2,7 @@
2
2
  import { randomUUID } from 'node:crypto';
3
3
  import { withWriteScope } from '../db/index.js';
4
4
  import { onHandle } from './open.js';
5
+ import { placeholders } from '../util/sql.js';
5
6
  const MAX_ACTIVE_GOAL_DEPTH = 3;
6
7
  export function rowToGoal(row) {
7
8
  return {
@@ -146,26 +147,28 @@ function getActiveGoalsWithDb(db, opts) {
146
147
  // Per-goal retrieval_policy multipliers compose onto the base goal-tag boost and are hard-capped at MAX_FINAL_MULTIPLIER (3.0x) before scoring.
147
148
  function loadGoalPolicies(db, active) {
148
149
  const policiesByGoalId = new Map();
150
+ const policyIds = [...new Set(active.flatMap((g) => (g.retrievalPolicyId ? [g.retrievalPolicyId] : [])))];
151
+ if (policyIds.length === 0)
152
+ return policiesByGoalId;
153
+ // SAFETY: the SELECT projects exactly these seven retrieval_policy columns; MAX_ACTIVE_GOAL_DEPTH bounds the IN list.
154
+ const rows = db.prepare(`
155
+ SELECT id, goal_id, policy_type, weight_schema_fit, weight_recency, weight_outcome, error_priority
156
+ FROM retrieval_policy WHERE id IN (${placeholders(policyIds.length)})
157
+ `).all(...policyIds);
158
+ const byId = new Map(rows.map((row) => [row.id, row]));
149
159
  for (const g of active) {
150
- if (!g.retrievalPolicyId)
160
+ const row = g.retrievalPolicyId ? byId.get(g.retrievalPolicyId) : undefined;
161
+ if (!row)
151
162
  continue;
152
- // SAFETY: the row comes from the SELECT above, which projects exactly
153
- // these seven retrieval_policy columns.
154
- const row = db.prepare(`
155
- SELECT id, goal_id, policy_type, weight_schema_fit, weight_recency, weight_outcome, error_priority
156
- FROM retrieval_policy WHERE id = ?
157
- `).get(g.retrievalPolicyId);
158
- if (row) {
159
- policiesByGoalId.set(g.id, {
160
- id: row.id,
161
- goalId: row.goal_id,
162
- policyType: row.policy_type,
163
- weightSchemaFit: row.weight_schema_fit,
164
- weightRecency: row.weight_recency,
165
- weightOutcome: row.weight_outcome,
166
- errorPriority: row.error_priority,
167
- });
168
- }
163
+ policiesByGoalId.set(g.id, {
164
+ id: row.id,
165
+ goalId: row.goal_id,
166
+ policyType: row.policy_type,
167
+ weightSchemaFit: row.weight_schema_fit,
168
+ weightRecency: row.weight_recency,
169
+ weightOutcome: row.weight_outcome,
170
+ errorPriority: row.error_priority,
171
+ });
169
172
  }
170
173
  return policiesByGoalId;
171
174
  }
@@ -176,7 +179,7 @@ export function localGoalRecallRows(db, rows) {
176
179
  return [];
177
180
  const ids = [...new Set(rows.map((r) => r.memoryId))];
178
181
  // SAFETY: the SELECT projects exactly one column, `id`, from memories.
179
- const local = db.prepare(`SELECT id FROM memories WHERE id IN (${ids.map(() => '?').join(',')})`).all(...ids);
182
+ const local = db.prepare(`SELECT id FROM memories WHERE id IN (${placeholders(ids.length)})`).all(...ids);
180
183
  const localIds = new Set(local.map((r) => r.id));
181
184
  return rows.filter((r) => localIds.has(r.memoryId));
182
185
  }