hippo-memory 1.70.0 → 1.71.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 (629) hide show
  1. package/README.md +1 -1
  2. package/dist/agent-memories/claude-code.d.ts +4 -2
  3. package/dist/agent-memories/claude-code.js +14 -5
  4. package/dist/agent-memories/files.js +23 -12
  5. package/dist/agent-memories/markdown.d.ts +1 -3
  6. package/dist/agent-memories/markdown.js +1 -3
  7. package/dist/agent-memories/report.js +2 -1
  8. package/dist/agent-memories/sync.d.ts +4 -2
  9. package/dist/agent-memories/sync.js +10 -5
  10. package/dist/api/assemble.d.ts +7 -44
  11. package/dist/api/assemble.js +2 -22
  12. package/dist/api/audit.d.ts +5 -1
  13. package/dist/api/audit.js +9 -1
  14. package/dist/api/auth.d.ts +13 -22
  15. package/dist/api/auth.js +34 -24
  16. package/dist/api/auto-sleep.js +2 -2
  17. package/dist/api/availability.d.ts +2 -15
  18. package/dist/api/availability.js +2 -15
  19. package/dist/api/context-select.js +3 -4
  20. package/dist/api/context-types.d.ts +10 -35
  21. package/dist/api/context.d.ts +5 -22
  22. package/dist/api/context.js +21 -38
  23. package/dist/api/dormant.d.ts +5 -21
  24. package/dist/api/dormant.js +7 -26
  25. package/dist/api/drill-down.d.ts +7 -46
  26. package/dist/api/drill-down.js +5 -26
  27. package/dist/api/forget.d.ts +5 -24
  28. package/dist/api/forget.js +5 -28
  29. package/dist/api/graph.d.ts +8 -0
  30. package/dist/api/graph.js +7 -0
  31. package/dist/api/index.d.ts +2 -0
  32. package/dist/api/index.js +2 -0
  33. package/dist/api/instruction-detect.js +16 -5
  34. package/dist/api/ledger-db.d.ts +2 -1
  35. package/dist/api/ledger-db.js +5 -3
  36. package/dist/api/memories.d.ts +5 -0
  37. package/dist/api/memories.js +7 -0
  38. package/dist/api/objects.d.ts +9 -0
  39. package/dist/api/objects.js +11 -0
  40. package/dist/api/on-store.js +5 -3
  41. package/dist/api/predictions.d.ts +9 -0
  42. package/dist/api/predictions.js +18 -0
  43. package/dist/api/promote.d.ts +13 -17
  44. package/dist/api/promote.js +11 -19
  45. package/dist/api/prompt-hook.d.ts +4 -4
  46. package/dist/api/prompt-hook.js +10 -9
  47. package/dist/api/recall-cli.d.ts +55 -0
  48. package/dist/api/recall-cli.js +94 -0
  49. package/dist/api/recall-core.js +14 -6
  50. package/dist/api/recall-finish.d.ts +53 -0
  51. package/dist/api/recall-finish.js +30 -0
  52. package/dist/api/recall-history.d.ts +17 -84
  53. package/dist/api/recall-history.js +15 -88
  54. package/dist/api/recall-pipeline.d.ts +2 -3
  55. package/dist/api/recall-pipeline.js +16 -9
  56. package/dist/api/recall-types.d.ts +64 -304
  57. package/dist/api/recall.d.ts +5 -33
  58. package/dist/api/recall.js +59 -60
  59. package/dist/api/refine.d.ts +7 -0
  60. package/dist/api/refine.js +10 -0
  61. package/dist/api/remember-local.d.ts +57 -0
  62. package/dist/api/remember-local.js +90 -0
  63. package/dist/api/remember.d.ts +18 -1
  64. package/dist/api/remember.js +13 -1
  65. package/dist/api/sleep-run.d.ts +1 -3
  66. package/dist/api/sleep-run.js +17 -21
  67. package/dist/api/sleep.d.ts +9 -28
  68. package/dist/api/tokens.d.ts +3 -10
  69. package/dist/api/tokens.js +3 -10
  70. package/dist/api/types.d.ts +6 -29
  71. package/dist/api/types.js +4 -22
  72. package/dist/capture/command.d.ts +4 -12
  73. package/dist/capture/command.js +6 -22
  74. package/dist/capture/compact.d.ts +4 -15
  75. package/dist/capture/compact.js +16 -25
  76. package/dist/capture/compaction-record.js +101 -69
  77. package/dist/capture/compaction-spool.d.ts +1 -1
  78. package/dist/capture/compaction-spool.js +1 -1
  79. package/dist/capture/session-digest.d.ts +1 -1
  80. package/dist/capture/session-digest.js +13 -7
  81. package/dist/capture/transcript.d.ts +2 -10
  82. package/dist/capture/transcript.js +4 -18
  83. package/dist/cli/api-context.d.ts +3 -0
  84. package/dist/cli/api-context.js +6 -0
  85. package/dist/cli/audit-prune.d.ts +6 -39
  86. package/dist/cli/audit-prune.js +6 -39
  87. package/dist/cli/audit.d.ts +1 -1
  88. package/dist/cli/audit.js +33 -34
  89. package/dist/cli/auth.d.ts +2 -2
  90. package/dist/cli/auth.js +57 -39
  91. package/dist/cli/briefs.d.ts +4 -4
  92. package/dist/cli/briefs.js +18 -22
  93. package/dist/cli/card.d.ts +2 -2
  94. package/dist/cli/card.js +31 -32
  95. package/dist/cli/client.d.ts +7 -27
  96. package/dist/cli/client.js +24 -43
  97. package/dist/cli/compact-resume-payload.js +3 -2
  98. package/dist/cli/context.d.ts +1 -2
  99. package/dist/cli/context.js +12 -8
  100. package/dist/cli/continuity.d.ts +6 -6
  101. package/dist/cli/continuity.js +62 -64
  102. package/dist/cli/correction-latency.d.ts +2 -14
  103. package/dist/cli/correction-latency.js +2 -14
  104. package/dist/cli/curate.d.ts +13 -17
  105. package/dist/cli/curate.js +68 -92
  106. package/dist/cli/dag.d.ts +4 -4
  107. package/dist/cli/dag.js +33 -27
  108. package/dist/cli/decisions.d.ts +4 -4
  109. package/dist/cli/decisions.js +33 -47
  110. package/dist/cli/eval.js +15 -13
  111. package/dist/cli/exit.d.ts +6 -0
  112. package/dist/cli/exit.js +11 -0
  113. package/dist/cli/explain.d.ts +1 -2
  114. package/dist/cli/explain.js +6 -4
  115. package/dist/cli/flag-values.d.ts +6 -1
  116. package/dist/cli/flag-values.js +27 -10
  117. package/dist/cli/flags.d.ts +1 -0
  118. package/dist/cli/flags.js +1 -1
  119. package/dist/cli/github.d.ts +14 -0
  120. package/dist/{connectors/github/cli-impl.js → cli/github.js} +43 -71
  121. package/dist/cli/goals.d.ts +2 -2
  122. package/dist/cli/goals.js +46 -50
  123. package/dist/cli/hook-runtime.d.ts +9 -27
  124. package/dist/cli/hook-runtime.js +15 -31
  125. package/dist/cli/init.d.ts +2 -2
  126. package/dist/cli/init.js +6 -12
  127. package/dist/cli/install-steps.js +3 -12
  128. package/dist/cli/last-sleep.d.ts +2 -1
  129. package/dist/cli/last-sleep.js +3 -0
  130. package/dist/cli/maintenance.d.ts +4 -4
  131. package/dist/cli/maintenance.js +14 -19
  132. package/dist/cli/object-verbs.js +4 -3
  133. package/dist/cli/owner-validation.d.ts +5 -28
  134. package/dist/cli/owner-validation.js +5 -28
  135. package/dist/cli/playbooks.d.ts +4 -4
  136. package/dist/cli/playbooks.js +23 -28
  137. package/dist/cli/print.d.ts +2 -5
  138. package/dist/cli/print.js +1 -4
  139. package/dist/cli/projects.d.ts +2 -2
  140. package/dist/cli/projects.js +1 -3
  141. package/dist/cli/provenance-coverage.d.ts +2 -6
  142. package/dist/cli/provenance-coverage.js +2 -6
  143. package/dist/cli/quality-repair-once.js +2 -2
  144. package/dist/cli/recall.d.ts +2 -4
  145. package/dist/cli/recall.js +55 -131
  146. package/dist/cli/refine-llm.d.ts +5 -31
  147. package/dist/cli/refine-llm.js +9 -36
  148. package/dist/cli/remember.d.ts +3 -3
  149. package/dist/cli/remember.js +122 -180
  150. package/dist/cli/scheduler.d.ts +2 -17
  151. package/dist/cli/scheduler.js +12 -18
  152. package/dist/cli/serve.js +21 -12
  153. package/dist/cli/session-hooks.d.ts +10 -18
  154. package/dist/cli/session-hooks.js +112 -113
  155. package/dist/cli/setup.d.ts +4 -4
  156. package/dist/cli/setup.js +19 -21
  157. package/dist/cli/shared.d.ts +9 -23
  158. package/dist/cli/shared.js +9 -32
  159. package/dist/cli/slack.d.ts +2 -2
  160. package/dist/cli/slack.js +19 -23
  161. package/dist/cli/sleep.d.ts +3 -2
  162. package/dist/cli/sleep.js +15 -16
  163. package/dist/cli/status.d.ts +9 -12
  164. package/dist/cli/status.js +55 -98
  165. package/dist/cli/stdin.d.ts +4 -6
  166. package/dist/cli/stdin.js +2 -3
  167. package/dist/cli/transfer.d.ts +9 -9
  168. package/dist/cli/transfer.js +66 -89
  169. package/dist/cli/usage.js +11 -5
  170. package/dist/cli.d.ts +66 -448
  171. package/dist/cli.js +87 -263
  172. package/dist/connectors/github/backfill.d.ts +5 -25
  173. package/dist/connectors/github/backfill.js +14 -49
  174. package/dist/connectors/github/deletion.d.ts +4 -18
  175. package/dist/connectors/github/deletion.js +2 -15
  176. package/dist/connectors/github/dlq.d.ts +8 -33
  177. package/dist/connectors/github/dlq.js +12 -29
  178. package/dist/connectors/github/ingest.d.ts +2 -6
  179. package/dist/connectors/github/ingest.js +4 -10
  180. package/dist/connectors/github/octokit-client.d.ts +4 -16
  181. package/dist/connectors/github/octokit-client.js +27 -24
  182. package/dist/connectors/github/ratelimit.d.ts +3 -14
  183. package/dist/connectors/github/ratelimit.js +3 -14
  184. package/dist/connectors/github/scope.d.ts +3 -5
  185. package/dist/connectors/github/scope.js +3 -5
  186. package/dist/connectors/github/signature.d.ts +4 -26
  187. package/dist/connectors/github/signature.js +4 -26
  188. package/dist/connectors/github/tenant-routing.d.ts +2 -24
  189. package/dist/connectors/github/tenant-routing.js +4 -26
  190. package/dist/connectors/github/transform.js +2 -16
  191. package/dist/connectors/github/types.d.ts +3 -15
  192. package/dist/connectors/github/types.js +2 -9
  193. package/dist/connectors/github/webhook.d.ts +2 -16
  194. package/dist/connectors/github/webhook.js +6 -22
  195. package/dist/connectors/slack/backfill.d.ts +4 -17
  196. package/dist/connectors/slack/backfill.js +4 -13
  197. package/dist/connectors/slack/dlq.d.ts +2 -19
  198. package/dist/connectors/slack/dlq.js +2 -19
  199. package/dist/connectors/slack/ingest.d.ts +2 -3
  200. package/dist/connectors/slack/ingest.js +2 -3
  201. package/dist/connectors/slack/scope.d.ts +2 -8
  202. package/dist/connectors/slack/scope.js +2 -8
  203. package/dist/connectors/slack/signature.d.ts +1 -6
  204. package/dist/connectors/slack/tenant-routing.d.ts +2 -19
  205. package/dist/connectors/slack/tenant-routing.js +2 -21
  206. package/dist/connectors/slack/transform.d.ts +2 -18
  207. package/dist/connectors/slack/transform.js +2 -18
  208. package/dist/connectors/slack/types.d.ts +3 -9
  209. package/dist/connectors/slack/types.js +2 -4
  210. package/dist/connectors/slack/web-client.js +33 -27
  211. package/dist/connectors/slack/webhook.d.ts +2 -19
  212. package/dist/connectors/slack/webhook.js +5 -26
  213. package/dist/connectors/slack/workspaces.d.ts +4 -24
  214. package/dist/connectors/slack/workspaces.js +4 -24
  215. package/dist/consolidate/conflicts.js +6 -14
  216. package/dist/consolidate/dag-summary.d.ts +8 -0
  217. package/dist/consolidate/dag-summary.js +41 -0
  218. package/dist/consolidate/dag.d.ts +5 -28
  219. package/dist/consolidate/dag.js +14 -75
  220. package/dist/consolidate/decay.js +7 -19
  221. package/dist/consolidate/dedupe.d.ts +6 -52
  222. package/dist/consolidate/dedupe.js +13 -71
  223. package/dist/consolidate/flush-units.d.ts +3 -4
  224. package/dist/consolidate/flush-units.js +3 -3
  225. package/dist/consolidate/half-life-migration.d.ts +3 -26
  226. package/dist/consolidate/half-life-migration.js +11 -28
  227. package/dist/consolidate/llm-passes.js +4 -21
  228. package/dist/consolidate/memory-value-weights.d.ts +2 -13
  229. package/dist/consolidate/memory-value-weights.js +2 -13
  230. package/dist/consolidate/memory-value.d.ts +11 -84
  231. package/dist/consolidate/memory-value.js +16 -104
  232. package/dist/consolidate/merge.js +10 -31
  233. package/dist/consolidate/merged-row.js +2 -1
  234. package/dist/consolidate/physics-pass.js +1 -3
  235. package/dist/consolidate/replay.d.ts +6 -38
  236. package/dist/consolidate/replay.js +14 -52
  237. package/dist/consolidate/run.js +1 -1
  238. package/dist/consolidate/sleep.d.ts +1 -8
  239. package/dist/consolidate/sleep.js +5 -17
  240. package/dist/consolidate/trace.d.ts +3 -18
  241. package/dist/consolidate/trace.js +3 -18
  242. package/dist/consolidate/traces.js +6 -22
  243. package/dist/core/ablation.d.ts +4 -91
  244. package/dist/core/ablation.js +4 -91
  245. package/dist/core/ambient.d.ts +1 -8
  246. package/dist/core/ambient.js +1 -8
  247. package/dist/core/capture-contract.d.ts +4 -2
  248. package/dist/core/capture-contract.js +4 -2
  249. package/dist/core/card.d.ts +4 -1
  250. package/dist/core/card.js +8 -0
  251. package/dist/core/compare.d.ts +11 -67
  252. package/dist/core/compare.js +9 -63
  253. package/dist/core/config.d.ts +10 -24
  254. package/dist/core/config.js +13 -14
  255. package/dist/core/handoff.d.ts +1 -7
  256. package/dist/core/handoff.js +1 -7
  257. package/dist/core/memory-quality.js +1 -1
  258. package/dist/core/memory.d.ts +41 -164
  259. package/dist/core/memory.js +51 -221
  260. package/dist/core/package-root.d.ts +3 -0
  261. package/dist/core/package-root.js +5 -0
  262. package/dist/core/physics-config.d.ts +1 -4
  263. package/dist/core/physics-config.js +3 -8
  264. package/dist/core/physics.d.ts +15 -61
  265. package/dist/core/physics.js +59 -122
  266. package/dist/core/project-identity.d.ts +17 -58
  267. package/dist/core/project-identity.js +17 -38
  268. package/dist/core/project-remote.js +13 -2
  269. package/dist/core/raw-append-only.d.ts +5 -0
  270. package/dist/core/raw-append-only.js +7 -0
  271. package/dist/core/salience.d.ts +1 -7
  272. package/dist/core/salience.js +1 -7
  273. package/dist/dashboard/dashboard-actions.js +2 -2
  274. package/dist/dashboard/dashboard-snapshot.d.ts +2 -1
  275. package/dist/dashboard/dashboard-snapshot.js +11 -2
  276. package/dist/dashboard/dashboard-types.d.ts +2 -1
  277. package/dist/dashboard/dashboard.d.ts +1 -0
  278. package/dist/dashboard/dashboard.js +50 -4
  279. package/dist/db/busy.d.ts +1 -1
  280. package/dist/db/busy.js +17 -8
  281. package/dist/db/connect.d.ts +1 -0
  282. package/dist/db/connect.js +3 -3
  283. package/dist/db/migrate.js +3 -3
  284. package/dist/db/migrations/types.d.ts +2 -5
  285. package/dist/db/migrations/v14.js +2 -3
  286. package/dist/db/migrations/v15.js +2 -8
  287. package/dist/db/migrations/v16.js +2 -3
  288. package/dist/db/migrations/v17.js +2 -3
  289. package/dist/db/migrations/v19.js +2 -5
  290. package/dist/db/migrations/v20.js +2 -7
  291. package/dist/db/migrations/v22.js +4 -17
  292. package/dist/db/migrations/v23.js +3 -14
  293. package/dist/db/migrations/v24.js +2 -4
  294. package/dist/db/migrations/v25.js +3 -8
  295. package/dist/db/migrations/v26.js +2 -13
  296. package/dist/db/migrations/v27.js +3 -11
  297. package/dist/db/migrations/v28.js +2 -14
  298. package/dist/db/migrations/v29.js +4 -18
  299. package/dist/db/migrations/v30.js +4 -16
  300. package/dist/db/migrations/v31.js +2 -15
  301. package/dist/db/migrations/v32.js +4 -22
  302. package/dist/db/migrations/v33.js +2 -15
  303. package/dist/db/migrations/v34.js +2 -11
  304. package/dist/db/migrations/v35.js +2 -11
  305. package/dist/db/migrations/v36.js +2 -11
  306. package/dist/db/migrations/v37.js +8 -33
  307. package/dist/db/migrations/v38.js +5 -38
  308. package/dist/db/migrations/v39.js +9 -20
  309. package/dist/db/migrations/v40.js +2 -9
  310. package/dist/db/migrations/v41.js +2 -24
  311. package/dist/db/migrations/v43.js +1 -1
  312. package/dist/db/migrations/v44.js +2 -8
  313. package/dist/db/migrations/v45.js +2 -7
  314. package/dist/db/migrations/v47.js +2 -3
  315. package/dist/db/migrations/v51.js +1 -1
  316. package/dist/db/open.d.ts +2 -0
  317. package/dist/db/open.js +17 -1
  318. package/dist/db/physics-state.d.ts +6 -22
  319. package/dist/db/physics-state.js +25 -36
  320. package/dist/db/raw-archive-mirror-cleanup.d.ts +2 -17
  321. package/dist/db/raw-archive-mirror-cleanup.js +2 -17
  322. package/dist/db/vector-store.d.ts +2 -0
  323. package/dist/db/vector-store.js +8 -1
  324. package/dist/doctor.js +74 -19
  325. package/dist/entry/session-text.js +2 -1
  326. package/dist/eval/eval-stats.d.ts +15 -53
  327. package/dist/eval/eval-stats.js +12 -42
  328. package/dist/eval/eval-suite.d.ts +2 -13
  329. package/dist/eval/eval-suite.js +74 -34
  330. package/dist/eval/eval.d.ts +6 -24
  331. package/dist/eval/eval.js +7 -34
  332. package/dist/eval/judgment.d.ts +0 -3
  333. package/dist/eval/judgment.js +19 -11
  334. package/dist/extensions/openclaw-plugin/index.js +1 -1
  335. package/dist/graph/extract.d.ts +5 -27
  336. package/dist/graph/extract.js +7 -33
  337. package/dist/graph/recall.d.ts +7 -20
  338. package/dist/graph/recall.js +14 -76
  339. package/dist/graph/stream.d.ts +7 -39
  340. package/dist/graph/stream.js +11 -34
  341. package/dist/graph/view.d.ts +9 -35
  342. package/dist/graph/view.js +11 -39
  343. package/dist/hooks/codex-wrapper.js +2 -2
  344. package/dist/hooks/copilot.js +2 -1
  345. package/dist/hooks/hook-blocks.d.ts +6 -2
  346. package/dist/hooks/hook-blocks.js +37 -124
  347. package/dist/hooks/json-hooks.d.ts +4 -36
  348. package/dist/hooks/json-hooks.js +18 -57
  349. package/dist/hooks/opencode.d.ts +0 -38
  350. package/dist/hooks/opencode.js +9 -76
  351. package/dist/hooks/shared.d.ts +8 -17
  352. package/dist/hooks/shared.js +29 -20
  353. package/dist/importers/core.d.ts +12 -37
  354. package/dist/importers/core.js +9 -27
  355. package/dist/importers/markdown-parse.d.ts +8 -22
  356. package/dist/importers/markdown-parse.js +8 -25
  357. package/dist/importers/markdown.js +3 -12
  358. package/dist/importers/sources.js +7 -35
  359. package/dist/importers/vault.d.ts +2 -8
  360. package/dist/importers/vault.js +25 -81
  361. package/dist/index.d.ts +6 -2
  362. package/dist/index.js +6 -2
  363. package/dist/learn/autolearn.d.ts +14 -41
  364. package/dist/learn/autolearn.js +14 -41
  365. package/dist/learn/extract.js +2 -4
  366. package/dist/learn/forward-claim-detector.d.ts +6 -30
  367. package/dist/learn/forward-claim-detector.js +14 -66
  368. package/dist/learn/invalidation.d.ts +6 -18
  369. package/dist/learn/invalidation.js +9 -26
  370. package/dist/mcp/admin-tools.js +3 -5
  371. package/dist/mcp/memory-tools.js +4 -9
  372. package/dist/mcp/protocol.d.ts +8 -26
  373. package/dist/mcp/protocol.js +3 -6
  374. package/dist/mcp/recall-tools.js +3 -3
  375. package/dist/mcp/request.d.ts +3 -11
  376. package/dist/mcp/request.js +10 -26
  377. package/dist/mcp/server.d.ts +3 -9
  378. package/dist/mcp/server.js +5 -16
  379. package/dist/mcp/session-state.d.ts +1 -1
  380. package/dist/mcp/session-state.js +5 -11
  381. package/dist/mcp/stdio.d.ts +2 -6
  382. package/dist/mcp/stdio.js +48 -12
  383. package/dist/mcp/tools.js +5 -3
  384. package/dist/objects/customer-notes.d.ts +7 -42
  385. package/dist/objects/customer-notes.js +7 -48
  386. package/dist/objects/decisions.d.ts +12 -50
  387. package/dist/objects/decisions.js +15 -53
  388. package/dist/objects/incidents.d.ts +7 -38
  389. package/dist/objects/incidents.js +7 -41
  390. package/dist/objects/policies.d.ts +11 -89
  391. package/dist/objects/policies.js +11 -98
  392. package/dist/objects/processes.d.ts +7 -52
  393. package/dist/objects/processes.js +7 -61
  394. package/dist/objects/project-briefs.d.ts +11 -55
  395. package/dist/objects/project-briefs.js +14 -75
  396. package/dist/objects/skills.d.ts +7 -42
  397. package/dist/objects/skills.js +8 -54
  398. package/dist/postinstall.js +6 -19
  399. package/dist/predictions/planning-fallacy.d.ts +11 -34
  400. package/dist/rerankers/clef.js +3 -31
  401. package/dist/rerankers/cross-encoder.js +4 -6
  402. package/dist/rerankers/jev.js +9 -6
  403. package/dist/rerankers/llm.d.ts +2 -11
  404. package/dist/rerankers/llm.js +14 -14
  405. package/dist/rerankers/outage-warning.d.ts +2 -1
  406. package/dist/rerankers/outage-warning.js +2 -1
  407. package/dist/rerankers/remote.d.ts +1 -0
  408. package/dist/rerankers/remote.js +2 -0
  409. package/dist/rerankers/types.d.ts +2 -14
  410. package/dist/search/boosts.d.ts +0 -2
  411. package/dist/search/boosts.js +2 -2
  412. package/dist/search/goal-boost.d.ts +23 -0
  413. package/dist/search/goal-boost.js +90 -0
  414. package/dist/search/hybrid.d.ts +2 -0
  415. package/dist/search/multihop.js +2 -3
  416. package/dist/search/path-context.d.ts +5 -18
  417. package/dist/search/path-context.js +5 -18
  418. package/dist/search/physics-search.js +11 -8
  419. package/dist/search/rrf.d.ts +7 -35
  420. package/dist/search/rrf.js +5 -30
  421. package/dist/search/vector.d.ts +7 -0
  422. package/dist/search/vector.js +35 -5
  423. package/dist/server/auth.d.ts +9 -28
  424. package/dist/server/auth.js +32 -25
  425. package/dist/server/boot.d.ts +2 -21
  426. package/dist/server/boot.js +63 -96
  427. package/dist/server/client-ip.d.ts +3 -19
  428. package/dist/server/client-ip.js +5 -28
  429. package/dist/server/defaults.d.ts +3 -0
  430. package/dist/server/defaults.js +4 -0
  431. package/dist/server/key-check-bounds.d.ts +2 -1
  432. package/dist/server/lifecycle.d.ts +5 -4
  433. package/dist/server/lifecycle.js +19 -4
  434. package/dist/server/mcp-http.js +10 -44
  435. package/dist/server/rate-limit.d.ts +5 -18
  436. package/dist/server/rate-limit.js +4 -13
  437. package/dist/server/request.d.ts +4 -8
  438. package/dist/server/request.js +9 -17
  439. package/dist/server/route-table.js +42 -49
  440. package/dist/server/routes/admin.js +22 -28
  441. package/dist/server/routes/customer-notes.js +7 -12
  442. package/dist/server/routes/decisions.js +10 -17
  443. package/dist/server/routes/incidents.js +12 -20
  444. package/dist/server/routes/memories.js +12 -32
  445. package/dist/server/routes/object-routes.d.ts +0 -5
  446. package/dist/server/routes/object-routes.js +7 -15
  447. package/dist/server/routes/policies.js +10 -18
  448. package/dist/server/routes/predictions.js +13 -25
  449. package/dist/server/routes/processes.js +16 -26
  450. package/dist/server/routes/project-briefs.js +9 -16
  451. package/dist/server/routes/recall.js +10 -35
  452. package/dist/server/routes/skills.js +8 -18
  453. package/dist/server/server-detect.d.ts +9 -53
  454. package/dist/server/server-detect.js +24 -90
  455. package/dist/server/sleep-offload.js +4 -1
  456. package/dist/server/types.d.ts +12 -10
  457. package/dist/server/validation.d.ts +3 -1
  458. package/dist/server/validation.js +10 -23
  459. package/dist/server.d.ts +2 -2
  460. package/dist/server.js +3 -2
  461. package/dist/sharing/global-store.d.ts +19 -0
  462. package/dist/sharing/global-store.js +75 -0
  463. package/dist/sharing/global-sync.d.ts +6 -0
  464. package/dist/sharing/global-sync.js +80 -0
  465. package/dist/sharing/project-merge.d.ts +4 -5
  466. package/dist/sharing/project-merge.js +9 -7
  467. package/dist/sharing/scope.d.ts +4 -11
  468. package/dist/sharing/scope.js +4 -11
  469. package/dist/sharing/search-both.d.ts +60 -0
  470. package/dist/sharing/search-both.js +138 -0
  471. package/dist/sharing/share.d.ts +38 -0
  472. package/dist/sharing/share.js +226 -0
  473. package/dist/store/ambient.js +4 -9
  474. package/dist/store/audit-event.d.ts +4 -12
  475. package/dist/store/audit-event.js +4 -12
  476. package/dist/store/audit.d.ts +2 -5
  477. package/dist/store/audit.js +4 -12
  478. package/dist/store/auth.d.ts +6 -6
  479. package/dist/store/auth.js +10 -9
  480. package/dist/store/candidates.d.ts +20 -3
  481. package/dist/store/candidates.js +77 -48
  482. package/dist/store/card-detail.d.ts +2 -1
  483. package/dist/store/card-detail.js +2 -1
  484. package/dist/store/cards.d.ts +14 -9
  485. package/dist/store/cards.js +72 -126
  486. package/dist/store/conflicts.d.ts +4 -21
  487. package/dist/store/conflicts.js +71 -150
  488. package/dist/store/connectors/github.d.ts +10 -10
  489. package/dist/store/connectors/github.js +10 -10
  490. package/dist/store/dashboard-reads.js +4 -8
  491. package/dist/store/delete-and-batch.d.ts +11 -31
  492. package/dist/store/delete-and-batch.js +48 -64
  493. package/dist/store/delivery-recorder.d.ts +8 -4
  494. package/dist/store/delivery-recorder.js +25 -15
  495. package/dist/store/diagnostics.js +6 -13
  496. package/dist/store/dormant.d.ts +7 -29
  497. package/dist/store/dormant.js +15 -56
  498. package/dist/store/embeddings/index.d.ts +11 -80
  499. package/dist/store/embeddings/index.js +71 -207
  500. package/dist/store/embeddings/local.js +15 -32
  501. package/dist/store/embeddings/provider.d.ts +16 -53
  502. package/dist/store/embeddings/provider.js +41 -69
  503. package/dist/store/entry-reads.d.ts +21 -57
  504. package/dist/store/entry-reads.js +65 -126
  505. package/dist/store/entry-row.d.ts +6 -35
  506. package/dist/store/entry-row.js +9 -37
  507. package/dist/store/entry-writes.d.ts +10 -16
  508. package/dist/store/entry-writes.js +17 -35
  509. package/dist/store/goals.d.ts +3 -40
  510. package/dist/store/goals.js +21 -160
  511. package/dist/store/graph-queue.d.ts +6 -22
  512. package/dist/store/graph-queue.js +14 -45
  513. package/dist/store/graph-reads.d.ts +11 -35
  514. package/dist/store/graph-reads.js +20 -58
  515. package/dist/store/graph-rows.d.ts +3 -6
  516. package/dist/store/graph-rows.js +6 -9
  517. package/dist/store/graph-view-rows.d.ts +2 -1
  518. package/dist/store/graph-view-rows.js +2 -1
  519. package/dist/store/graph-writes.d.ts +6 -31
  520. package/dist/store/graph-writes.js +13 -37
  521. package/dist/store/half-life.d.ts +2 -1
  522. package/dist/store/half-life.js +14 -16
  523. package/dist/store/handoffs.d.ts +5 -11
  524. package/dist/store/handoffs.js +16 -34
  525. package/dist/store/index-and-stats.d.ts +6 -19
  526. package/dist/store/index-and-stats.js +34 -82
  527. package/dist/store/markdown.d.ts +2 -6
  528. package/dist/store/markdown.js +21 -20
  529. package/dist/store/mirrors.d.ts +2 -6
  530. package/dist/store/mirrors.js +14 -23
  531. package/dist/store/open.js +6 -13
  532. package/dist/store/planning-fallacy-evidence.d.ts +2 -30
  533. package/dist/store/planning-fallacy-evidence.js +8 -37
  534. package/dist/store/port.d.ts +82 -79
  535. package/dist/store/predictions.d.ts +11 -51
  536. package/dist/store/predictions.js +38 -119
  537. package/dist/{cli → store}/quality-repair.js +15 -31
  538. package/dist/store/raw-archive.d.ts +4 -16
  539. package/dist/store/raw-archive.js +14 -29
  540. package/dist/store/recall-scope.d.ts +19 -79
  541. package/dist/store/recall-scope.js +21 -82
  542. package/dist/store/recall-trace.d.ts +10 -77
  543. package/dist/store/recall-trace.js +19 -95
  544. package/dist/store/rejected-values.js +7 -19
  545. package/dist/store/rejection.d.ts +12 -58
  546. package/dist/store/rejection.js +13 -65
  547. package/dist/store/report-reads.d.ts +6 -0
  548. package/dist/store/report-reads.js +29 -0
  549. package/dist/store/rows.d.ts +9 -19
  550. package/dist/store/rows.js +36 -34
  551. package/dist/store/rule-sql.d.ts +5 -0
  552. package/dist/store/rule-sql.js +37 -0
  553. package/dist/store/search-rows.d.ts +7 -38
  554. package/dist/store/search-rows.js +14 -50
  555. package/dist/store/sessions.d.ts +10 -47
  556. package/dist/store/sessions.js +17 -73
  557. package/dist/store/sqlite/connector-events-group.js +2 -2
  558. package/dist/store/sqlite/entry-writes-group.js +2 -2
  559. package/dist/store/sqlite/error-codec.js +4 -2
  560. package/dist/store/sqlite/executor.d.ts +2 -1
  561. package/dist/store/sqlite/executor.js +2 -1
  562. package/dist/store/sqlite/local.d.ts +3 -3
  563. package/dist/store/sqlite/local.js +1 -0
  564. package/dist/store/sqlite/object-rows.js +6 -3
  565. package/dist/store/sqlite/objects-group.js +4 -2
  566. package/dist/store/sqlite/quarantine-group.js +2 -2
  567. package/dist/store/sqlite/store.d.ts +1 -4
  568. package/dist/store/sqlite/store.js +2 -3
  569. package/dist/store/sqlite/vector-reads-group.js +2 -2
  570. package/dist/store/sqlite/worker-jobs.js +1 -1
  571. package/dist/store/sqlite/worker-ops.d.ts +13 -2
  572. package/dist/store/sqlite/worker-ops.js +13 -2
  573. package/dist/store/sqlite/worker-store.js +5 -2
  574. package/dist/store/summaries.d.ts +10 -62
  575. package/dist/store/summaries.js +34 -146
  576. package/dist/store/summary-dirty.js +1 -1
  577. package/dist/store/tenant-lookup.d.ts +6 -1
  578. package/dist/store/tenant-lookup.js +7 -0
  579. package/dist/store/tenant.d.ts +2 -16
  580. package/dist/store/tenant.js +4 -19
  581. package/dist/store/token-ledger.d.ts +13 -43
  582. package/dist/store/token-ledger.js +19 -50
  583. package/dist/store/usage-reports.js +5 -13
  584. package/dist/store/vector-index.d.ts +46 -0
  585. package/dist/store/vector-index.js +170 -0
  586. package/dist/store/working-memory.d.ts +6 -25
  587. package/dist/store/working-memory.js +18 -52
  588. package/dist/store/yaml.d.ts +1 -5
  589. package/dist/store/yaml.js +2 -6
  590. package/dist/support-bundle.js +7 -5
  591. package/dist/trust/reject-flow.js +1 -3
  592. package/dist/util/anthropic-messages.js +14 -2
  593. package/dist/util/atomic-write.d.ts +2 -1
  594. package/dist/util/atomic-write.js +35 -4
  595. package/dist/util/capped-json.d.ts +6 -0
  596. package/dist/util/capped-json.js +46 -0
  597. package/dist/util/crash-handlers.d.ts +8 -1
  598. package/dist/util/crash-handlers.js +52 -10
  599. package/dist/util/env.d.ts +13 -6
  600. package/dist/util/env.js +14 -7
  601. package/dist/util/home-path.d.ts +2 -1
  602. package/dist/util/home-path.js +4 -2
  603. package/dist/util/http-retry.d.ts +3 -1
  604. package/dist/util/http-retry.js +26 -5
  605. package/dist/util/log.d.ts +3 -2
  606. package/dist/util/log.js +25 -9
  607. package/dist/util/request-scope.d.ts +2 -1
  608. package/dist/util/request-scope.js +2 -1
  609. package/dist/util/secret-detect.d.ts +5 -30
  610. package/dist/util/secret-detect.js +13 -39
  611. package/dist/util/stored-json.d.ts +1 -1
  612. package/dist/util/stored-json.js +1 -1
  613. package/dist/util/token-text.d.ts +1 -4
  614. package/dist/util/token-text.js +1 -4
  615. package/dist/util/transcript-tail.d.ts +2 -0
  616. package/dist/util/transcript-tail.js +24 -23
  617. package/dist/util/version.d.ts +5 -20
  618. package/dist/util/version.js +5 -20
  619. package/extensions/openclaw-plugin/index.ts +1 -1
  620. package/extensions/openclaw-plugin/openclaw.plugin.json +1 -1
  621. package/extensions/openclaw-plugin/package.json +1 -1
  622. package/openclaw.plugin.json +1 -1
  623. package/package.json +1 -1
  624. package/dist/connectors/github/cli-impl.d.ts +0 -25
  625. package/dist/sharing/shared.d.ts +0 -187
  626. package/dist/sharing/shared.js +0 -563
  627. package/dist/store/vector-writes.d.ts +0 -13
  628. package/dist/store/vector-writes.js +0 -89
  629. /package/dist/{cli → store}/quality-repair.d.ts +0 -0
@@ -1,14 +1,7 @@
1
- /**
2
- * Detect the current operational scope from environment signals.
3
- * Returns a scope name string (no prefix), or null if no scope detected.
4
- * Priority: HIPPO_SCOPE > GSTACK_SKILL > OPENCLAW_SKILL.
5
- *
6
- * Pure env var reads: no I/O, safe to call from hot paths (e.g. UserPromptSubmit hook).
7
- */
1
+ /** Detect the current operational scope from env (HIPPO_SCOPE > GSTACK_SKILL > OPENCLAW_SKILL); returns the name without prefix, or null.
2
+ * Pure env reads, no I/O, so it is safe on hot paths such as the UserPromptSubmit hook. */
8
3
  export declare function detectScope(): string | null;
9
- /**
10
- * Compute scope match between a memory's scope tags and the active scope.
11
- * Returns: 1 if matching scope, -1 if mismatching scope, 0 if neutral (no scope on either side).
12
- */
4
+ /** Compute scope match between a memory's scope tags and the active scope.
5
+ * Returns: 1 if matching scope, -1 if mismatching scope, 0 if neutral (no scope on either side). */
13
6
  export declare function scopeMatch(memoryTags: string[], activeScope: string | null): -1 | 0 | 1;
14
7
  //# sourceMappingURL=scope.d.ts.map
@@ -1,11 +1,6 @@
1
1
  import { envGstackSkill, envOpenclawSkill, envScope } from '../util/env.js';
2
- /**
3
- * Detect the current operational scope from environment signals.
4
- * Returns a scope name string (no prefix), or null if no scope detected.
5
- * Priority: HIPPO_SCOPE > GSTACK_SKILL > OPENCLAW_SKILL.
6
- *
7
- * Pure env var reads: no I/O, safe to call from hot paths (e.g. UserPromptSubmit hook).
8
- */
2
+ /** Detect the current operational scope from env (HIPPO_SCOPE > GSTACK_SKILL > OPENCLAW_SKILL); returns the name without prefix, or null.
3
+ * Pure env reads, no I/O, so it is safe on hot paths such as the UserPromptSubmit hook. */
9
4
  export function detectScope() {
10
5
  const explicit = envScope();
11
6
  if (explicit && explicit.trim())
@@ -18,10 +13,8 @@ export function detectScope() {
18
13
  return openclawSkill.trim();
19
14
  return null;
20
15
  }
21
- /**
22
- * Compute scope match between a memory's scope tags and the active scope.
23
- * Returns: 1 if matching scope, -1 if mismatching scope, 0 if neutral (no scope on either side).
24
- */
16
+ /** Compute scope match between a memory's scope tags and the active scope.
17
+ * Returns: 1 if matching scope, -1 if mismatching scope, 0 if neutral (no scope on either side). */
25
18
  export function scopeMatch(memoryTags, activeScope) {
26
19
  const scopeTags = memoryTags.filter(t => t.startsWith('scope:'));
27
20
  if (scopeTags.length === 0)
@@ -0,0 +1,60 @@
1
+ import { MemoryEntry } from '../core/memory.js';
2
+ import { type SearchResult, type ResultCost } from '../core/search-types.js';
3
+ import { type HybridVectorCandidates } from '../search/vector.js';
4
+ export declare function logEmbedAllFailure<E>(caller: string, err: E): void;
5
+ export interface SearchOptions {
6
+ budget?: number;
7
+ now?: Date;
8
+ minResults?: number;
9
+ /** Tenant scope for both stores. Undefined = no filter (legacy single-tenant). */
10
+ tenantId?: string;
11
+ }
12
+ /** Search both local and global stores and merge: local results are boosted 1.2x to prefer project context.
13
+ * Returns results sorted by adjusted score within the combined token budget. */
14
+ export declare function searchBoth(query: string, localRoot: string, globalRoot: string, options?: SearchOptions): SearchResult[];
15
+ export interface HybridSearchOptions extends SearchOptions {
16
+ embeddingWeight?: number;
17
+ explain?: boolean;
18
+ mmr?: boolean;
19
+ mmrLambda?: number;
20
+ /** Multiplier applied to local-store scores when merging with global.
21
+ * Defaults to 1.2. Use 1.0 to remove the local bias for eval comparisons. */
22
+ localBump?: number;
23
+ /** Active scope for scope-boost scoring. Auto-detected if not provided. */
24
+ scope?: string | null;
25
+ /** Include superseded memories in results. Default false. */
26
+ includeSuperseded?: boolean;
27
+ /** Filter to memories current at this ISO date string. */
28
+ asOf?: string;
29
+ /** Budget cost per result, spent the same way in each store and in the merged list. */
30
+ cost?: ResultCost;
31
+ /** Propagated to underlying hybridSearch calls.
32
+ * Per-call > env HIPPO_SUMMARY_DEBOOST > 0.85 default. */
33
+ summaryDeboost?: number;
34
+ /** Propagated. Default true (1.05 boost if rebuilt within 7d). */
35
+ summaryFreshness?: boolean;
36
+ /** Optional admission predicate applied to the loaded candidates of BOTH stores before ranking, cross-store dedupe and budgeting,
37
+ * so an excluded row cannot shadow its admitted duplicate or saturate the budget. Undefined (recall paths never set it) means no filter. */
38
+ entryFilter?: (entry: MemoryEntry) => boolean;
39
+ /** Recall scope filter for `searchBothHybrid`: absent is the only unfiltered mode; present uses `loadRecallSearchEntries` plus a JS post-filter.
40
+ * `{}` = default-deny (legacy and `<source>:private:*` excluded); `{ requested: 'X' }` = exact; `{ requested: 'X', additive: true }` = default plus X.
41
+ * An object, not `string | null`, because the sibling `scope` option already gives null a different meaning.
42
+ * Do not pass `{}` casually from non-recall paths. */
43
+ recallScope?: {
44
+ requested?: string;
45
+ additive?: boolean;
46
+ ownScope?: string;
47
+ };
48
+ }
49
+ /** Hybrid search across both local and global stores, using embeddings when available.
50
+ * Async version of searchBoth that calls hybridSearch instead of search. */
51
+ export declare function searchBothHybrid(query: string, localRoot: string, globalRoot: string, options?: HybridSearchOptions): Promise<SearchResult[]>;
52
+ /** Hybrid ranking of rows already loaded from each store: the local bump, one copy per text, then the shared budget. */
53
+ export declare function rankBothStores(query: string, roots: {
54
+ local: string;
55
+ global: string;
56
+ }, entries: {
57
+ local: MemoryEntry[];
58
+ global: MemoryEntry[];
59
+ }, vectorCandidates: HybridVectorCandidates, options?: HybridSearchOptions): Promise<SearchResult[]>;
60
+ //# sourceMappingURL=search-both.d.ts.map
@@ -0,0 +1,138 @@
1
+ // Search across the local and global stores, merged into one ranked, budgeted list.
2
+ import { compareScoresDesc } from '../core/compare.js';
3
+ import * as fs from 'fs';
4
+ import { loadSearchEntries, loadRecallSearchEntries, recallScopeFilter } from '../store/search-rows.js';
5
+ import { passesScopeFilterForRecall, passesCliRecallScopeFilter } from '../store/recall-scope.js';
6
+ import { search } from '../search/bm25-search.js';
7
+ import { hybridSearch } from '../search/hybrid.js';
8
+ import { fitBudget } from '../search/finalize.js';
9
+ import { DEFAULT_LOCAL_BUMP, DEFAULT_RECALL_BUDGET } from '../core/search-types.js';
10
+ import { startQueryEmbedDeadline } from '../search/vector.js';
11
+ import { evalNow } from '../core/ablation.js';
12
+ import { duplicateKey } from '../util/same-text.js';
13
+ import { errorMessage, log } from '../util/log.js';
14
+ // The rows are already copied; a failed background embed only delays vectors, so it warns instead of throwing.
15
+ export function logEmbedAllFailure(caller, err) {
16
+ log.warn(`${caller}: background embed failed (${errorMessage(err)}); run 'hippo embed' to backfill`);
17
+ }
18
+ /** Search both local and global stores and merge: local results are boosted 1.2x to prefer project context.
19
+ * Returns results sorted by adjusted score within the combined token budget. */
20
+ export function searchBoth(query, localRoot, globalRoot, options = {}) {
21
+ const { budget = DEFAULT_RECALL_BUDGET, now = evalNow(), minResults, tenantId } = options;
22
+ const effectiveMin = minResults ?? 1;
23
+ const localEntries = fs.existsSync(localRoot) ? loadSearchEntries(localRoot, query, undefined, tenantId) : [];
24
+ const globalEntries = fs.existsSync(globalRoot) ? loadSearchEntries(globalRoot, query, undefined, tenantId) : [];
25
+ if (localEntries.length === 0 && globalEntries.length === 0)
26
+ return [];
27
+ // Search each store with full budget, then blend
28
+ const localResults = search(query, localEntries, { budget, now, minResults });
29
+ const globalResults = search(query, globalEntries, { budget, now, minResults });
30
+ const deduped = dedupeByContent(tagLocalAndGlobal(localResults, globalResults));
31
+ // PLAIN stable score sort on purpose: both inputs are deterministically ordered,
32
+ // and an exact tie keeps the LOCAL result ahead of the global one (concat order).
33
+ deduped.sort((a, b) => compareScoresDesc(a.score, b.score));
34
+ // Apply combined token budget (guarantee at least minResults items)
35
+ const results = [];
36
+ let usedTokens = 0;
37
+ for (let i = 0; i < deduped.length; i++) {
38
+ if (results.length >= effectiveMin && usedTokens + deduped[i].tokens > budget)
39
+ continue;
40
+ usedTokens += deduped[i].tokens;
41
+ results.push(deduped[i]);
42
+ }
43
+ return results;
44
+ }
45
+ function tagLocalAndGlobal(localResults, globalResults) {
46
+ // Tag global results. Local memories get a configurable priority bump.
47
+ return [
48
+ ...localResults.map((r) => ({
49
+ ...r,
50
+ isGlobal: false,
51
+ score: r.score * DEFAULT_LOCAL_BUMP,
52
+ breakdown: r.breakdown
53
+ ? { ...r.breakdown, sourceBump: DEFAULT_LOCAL_BUMP, final: r.breakdown.final * DEFAULT_LOCAL_BUMP }
54
+ : undefined,
55
+ })),
56
+ ...globalResults.map((r) => ({ ...r, isGlobal: true })),
57
+ ];
58
+ }
59
+ function dedupeByContent(tagged) {
60
+ // Remove duplicates by content (local/global IDs differ after promote/share)
61
+ const seen = new Set();
62
+ return tagged.filter((r) => {
63
+ const key = duplicateKey(r.entry.content);
64
+ if (seen.has(key))
65
+ return false;
66
+ seen.add(key);
67
+ return true;
68
+ });
69
+ }
70
+ /** Hybrid search across both local and global stores, using embeddings when available.
71
+ * Async version of searchBoth that calls hybridSearch instead of search. */
72
+ export async function searchBothHybrid(query, localRoot, globalRoot, options = {}) {
73
+ const { includeSuperseded, asOf, tenantId, entryFilter, recallScope } = options;
74
+ // With an admission filter, lift the per-store candidate cap (default 200) to a bounded 5000: excluded rows could otherwise fill the window first.
75
+ const searchWindow = entryFilter ? 5000 : undefined;
76
+ // Recall mode: push the scope predicate into SQL like api.recall, so quarantine/private rows never enter the candidates, shadow duplicates or use budget.
77
+ // The JS post-filter below is the regex-only `<source>:private:*` half plus defense in depth on exact match.
78
+ const loadEntries = (root) => {
79
+ if (!fs.existsSync(root))
80
+ return [];
81
+ return recallScope
82
+ ? loadRecallSearchEntries(root, query, searchWindow, tenantId, recallScope.requested, recallScope.additive ? 'additive' : 'exact', Boolean(includeSuperseded) || Boolean(asOf), undefined, recallScope.ownScope)
83
+ : loadSearchEntries(root, query, searchWindow, tenantId);
84
+ };
85
+ const passesScope = (e) => !recallScope || (recallScope.additive
86
+ ? passesCliRecallScopeFilter(e.scope ?? null, recallScope.requested) || passesScopeFilterForRecall(e.scope ?? null, undefined, recallScope.ownScope)
87
+ : passesScopeFilterForRecall(e.scope ?? null, recallScope.requested, recallScope.ownScope));
88
+ const admit = (e) => passesScope(e) && (!entryFilter || entryFilter(e));
89
+ const localEntries = loadEntries(localRoot).filter(admit);
90
+ const globalEntries = loadEntries(globalRoot).filter(admit);
91
+ // The vector arm loads under the same SQL rules as loadEntries, then the same JS admission.
92
+ const vectorCandidates = {
93
+ tenantId,
94
+ scope: recallScope ? recallScopeFilter(recallScope.requested, recallScope.additive ? 'additive' : 'exact', recallScope.ownScope) : undefined,
95
+ includeSuperseded: !recallScope || Boolean(includeSuperseded) || Boolean(asOf),
96
+ admit,
97
+ };
98
+ return rankBothStores(query, { local: localRoot, global: globalRoot }, { local: localEntries, global: globalEntries }, vectorCandidates, options);
99
+ }
100
+ /** Hybrid ranking of rows already loaded from each store: the local bump, one copy per text, then the shared budget. */
101
+ export async function rankBothStores(query, roots, entries, vectorCandidates, options = {}) {
102
+ const { budget = DEFAULT_RECALL_BUDGET, now = evalNow(), embeddingWeight, explain, mmr, mmrLambda, localBump = DEFAULT_LOCAL_BUMP, minResults, cost, scope, includeSuperseded, asOf, summaryDeboost, summaryFreshness } = options;
103
+ if (entries.local.length === 0 && entries.global.length === 0)
104
+ return [];
105
+ // One deadline for both stores, so a provider that stalls on the first is not waited on again for the second.
106
+ const shared = {
107
+ budget, now, embeddingWeight, explain, mmr, mmrLambda, minResults, cost, scope, includeSuperseded, asOf, summaryDeboost, summaryFreshness,
108
+ vectorCandidates, queryEmbedDeadline: startQueryEmbedDeadline(),
109
+ };
110
+ const localResults = await hybridSearch(query, entries.local, { ...shared, hippoRoot: roots.local });
111
+ const globalResults = await hybridSearch(query, entries.global, { ...shared, hippoRoot: roots.global });
112
+ // Tag global results. Local memories get a configurable priority bump.
113
+ const tagged = [
114
+ ...localResults.map((r) => ({
115
+ ...r,
116
+ isGlobal: false,
117
+ score: r.score * localBump,
118
+ breakdown: r.breakdown
119
+ ? { ...r.breakdown, sourceBump: localBump, final: r.breakdown.final * localBump }
120
+ : undefined,
121
+ })),
122
+ ...globalResults.map((r) => ({ ...r, isGlobal: true })),
123
+ ];
124
+ // Remove duplicates by content (local/global IDs differ after promote/share)
125
+ const seen = new Set();
126
+ const deduped = tagged.filter((r) => {
127
+ const key = duplicateKey(r.entry.content);
128
+ if (seen.has(key))
129
+ return false;
130
+ seen.add(key);
131
+ return true;
132
+ });
133
+ // PLAIN stable score sort on purpose -- see searchBoth above;
134
+ // same rationale (deterministic inputs + stability; local-first on ties).
135
+ deduped.sort((a, b) => compareScoresDesc(a.score, b.score));
136
+ return fitBudget(deduped, budget, minResults ?? 1, cost);
137
+ }
138
+ //# sourceMappingURL=search-both.js.map
@@ -0,0 +1,38 @@
1
+ import { MemoryEntry } from '../core/memory.js';
2
+ /** Tags whose rows only a hand-run share or promote may copy to the global store; derived rows inherit them. */
3
+ export declare const NEVER_AUTO_SHARE_TAGS: ReadonlySet<string>;
4
+ export declare function neverAutoShareTags(sources: readonly MemoryEntry[]): string[];
5
+ /** Tags whose rows sleep keeps as written: never merged, never sent to LLM extraction. Conflict detection keeps its own list. */
6
+ export declare const NO_MERGE_TAGS: ReadonlySet<string>;
7
+ /** Estimate how well a memory would transfer to other projects.
8
+ * Returns 0..1 where >0.5 = good candidate for sharing. */
9
+ export declare function transferScore(entry: MemoryEntry): number;
10
+ /** Share a memory to the global store with attribution (source enriched with project path and timestamp).
11
+ * Returns the new entry, or null if the transfer score is too low. */
12
+ export declare function shareMemory(localRoot: string, id: string, options?: {
13
+ force?: boolean;
14
+ tenantId?: string;
15
+ skipEmbed?: boolean;
16
+ }): MemoryEntry | null;
17
+ /** List all projects that contributed memories to the global store, parsed from 'shared:<project>:' or 'promoted:<path>' sources.
18
+ * `tenantId` filters to one tenant; undefined is host-wide (legacy callers: CLI standalone, dashboard). */
19
+ export declare function listPeers(globalRoot?: string, tenantId?: string): Array<{
20
+ project: string;
21
+ count: number;
22
+ latest: string;
23
+ }>;
24
+ type AutoShareStats = {
25
+ secretSkipped: number;
26
+ rejectedSkipped?: number;
27
+ neverAutoShareSkipped?: number;
28
+ };
29
+ /** Auto-share local memories with high transfer scores, not already global and without a NEVER_AUTO_SHARE_TAGS tag; `options.tenantId` scopes the LOCAL read.
30
+ * `options.stats` is an opt-in out-param: `secretSkipped` counts shares the secret veto prevented, `rejectedSkipped` rejection refusals (0 under `dryRun`). */
31
+ export declare function autoShare(localRoot: string, options?: {
32
+ minScore?: number;
33
+ dryRun?: boolean;
34
+ tenantId?: string;
35
+ stats?: AutoShareStats;
36
+ }): MemoryEntry[];
37
+ export {};
38
+ //# sourceMappingURL=share.d.ts.map
@@ -0,0 +1,226 @@
1
+ // Sharing local memories to the global store: transfer scoring, share, peers and auto-share.
2
+ import { BadRequestError, NotFoundError } from '../core/api-errors.js';
3
+ import * as fs from 'fs';
4
+ import * as path from 'path';
5
+ import { generateId, COMPACTION_MEMORY_TAG } from '../core/memory.js';
6
+ import { AGENT_MEMORY_SOURCE_PREFIX, AGENT_MEMORY_TAGS } from '../core/agent-memory-tools.js';
7
+ import { writeEntry } from '../store/entry-writes.js';
8
+ import { loadAllEntries, readEntry } from '../store/entry-reads.js';
9
+ import { tallySources } from '../store/candidates.js';
10
+ import { isPersonalScope } from '../store/recall-scope.js';
11
+ import { fallbackOrigin } from '../core/project-identity.js';
12
+ import { detectSecret } from '../util/secret-detect.js';
13
+ import { isQuarantineScope } from '../trust/quarantine.js';
14
+ import { RejectedValueError } from '../store/rejection.js';
15
+ import { embedMemory, embedAll } from '../store/embeddings/index.js';
16
+ import { duplicateKey, storedTextKeys } from '../util/same-text.js';
17
+ import { isReusable } from '../core/memory-quality.js';
18
+ import { log } from '../util/log.js';
19
+ import { getGlobalRoot, initGlobal } from './global-store.js';
20
+ import { logEmbedAllFailure } from './search-both.js';
21
+ /** Tags that indicate project-specific memories (poor transfer candidates) */
22
+ const PROJECT_SPECIFIC_TAGS = new Set([
23
+ 'file-path', 'config', 'deploy', 'cron', 'url', 'auth',
24
+ 'field-names', 'column-names', 'api-key', 'endpoint',
25
+ ]);
26
+ /** Tags that indicate transferable memories (good transfer candidates) */
27
+ const TRANSFERABLE_TAGS = new Set([
28
+ 'error', 'platform', 'windows', 'encoding', 'python', 'shell',
29
+ 'powershell', 'quant', 'backtest', 'pattern', 'rule', 'gotcha',
30
+ 'sub-agent', 'review', 'best-practice',
31
+ ]);
32
+ /** Tags whose rows only a hand-run share or promote may copy to the global store; derived rows inherit them. */
33
+ export const NEVER_AUTO_SHARE_TAGS = new Set([
34
+ 'git-learned',
35
+ 'session-digest',
36
+ ...AGENT_MEMORY_TAGS,
37
+ ]);
38
+ export function neverAutoShareTags(sources) {
39
+ return [...NEVER_AUTO_SHARE_TAGS].filter((tag) => sources.some((s) => s.tags.includes(tag)));
40
+ }
41
+ /** Tags whose rows sleep keeps as written: never merged, never sent to LLM extraction. Conflict detection keeps its own list. */
42
+ export const NO_MERGE_TAGS = new Set([
43
+ 'extracted',
44
+ 'session-digest',
45
+ COMPACTION_MEMORY_TAG,
46
+ ...AGENT_MEMORY_TAGS,
47
+ ]);
48
+ /** Estimate how well a memory would transfer to other projects.
49
+ * Returns 0..1 where >0.5 = good candidate for sharing. */
50
+ export function transferScore(entry) {
51
+ let score = 0.5; // neutral default
52
+ // Boost for transferable tags
53
+ const transferableCount = entry.tags.filter((t) => TRANSFERABLE_TAGS.has(t)).length;
54
+ score += transferableCount * 0.1;
55
+ // Penalize for project-specific tags
56
+ const specificCount = entry.tags.filter((t) => PROJECT_SPECIFIC_TAGS.has(t)).length;
57
+ score -= specificCount * 0.15;
58
+ // High-retrieval memories are more likely to be universally useful
59
+ if (entry.retrieval_count >= 3)
60
+ score += 0.1;
61
+ // Pinned memories are important to their owner
62
+ if (entry.pinned)
63
+ score += 0.1;
64
+ // Error-tagged memories often encode universal lessons
65
+ if (entry.emotional_valence === 'negative' || entry.emotional_valence === 'critical')
66
+ score += 0.05;
67
+ return Math.min(1, Math.max(0, score));
68
+ }
69
+ /** Share a memory to the global store with attribution (source enriched with project path and timestamp).
70
+ * Returns the new entry, or null if the transfer score is too low. */
71
+ export function shareMemory(localRoot, id, options = {}) {
72
+ // tenantId is optional for single-tenant callers (autoShare passes only `{ force: true }`); MCP/REST hosts MUST pass it so tenant A cannot share
73
+ // tenant B's memory to global (readEntry returns null on a cross-tenant lookup).
74
+ const entry = readEntry(localRoot, id, options.tenantId);
75
+ if (!entry)
76
+ throw new NotFoundError(`Memory not found: ${id}`);
77
+ assertShareable(entry, id);
78
+ const score = transferScore(entry);
79
+ if (score < 0.3 && !options.force)
80
+ return null;
81
+ initGlobal();
82
+ const globalRoot = getGlobalRoot();
83
+ // The label keeps the folder name for a user-global row; a NULL row gets `shared::`, which originFromSource reads as no project.
84
+ const fallbackName = path.basename(path.resolve(localRoot, '..'));
85
+ const originName = entry.origin_project ?? fallbackOrigin(localRoot);
86
+ const label = originName === '' ? fallbackName : (originName ?? '');
87
+ const globalEntry = {
88
+ ...entry,
89
+ id: generateId('g'),
90
+ source: `shared:${label}:${new Date().toISOString()}`,
91
+ origin_project: originName,
92
+ };
93
+ writeEntry(globalRoot, globalEntry);
94
+ // Embed here unless the caller opts out: autoShare sets skipEmbed to batch one embedAll(), since embedMemory rewrites the whole index per call.
95
+ if (!options.skipEmbed) {
96
+ void embedMemory(globalRoot, globalEntry);
97
+ }
98
+ return globalEntry;
99
+ }
100
+ function assertShareable(entry, id) {
101
+ // Secret veto: secrets never reach the global store, even with --force; throw loudly, since a silent null reads as "low transfer score" and invites retries.
102
+ const secret = detectSecret(entry);
103
+ if (secret.flagged) {
104
+ throw new BadRequestError(`Refusing to share ${id} to the global store: content matches secret material (${secret.reason}). ` +
105
+ `Secrets stay in their owning project's store.`);
106
+ }
107
+ // A quarantined row is unreviewed input, not a lesson; sharing it would spread poison globally.
108
+ if (isQuarantineScope(entry.scope)) {
109
+ throw new BadRequestError(`Refusing to share ${id}: it is quarantined pending review. Approve it first via 'hippo quarantine approve ${id}'.`);
110
+ }
111
+ if (isPersonalScope(entry.scope ?? null)) {
112
+ throw new BadRequestError(`Refusing to share ${id}: it is a personal memory and stays with its owner on this server.`);
113
+ }
114
+ }
115
+ /** List all projects that contributed memories to the global store, parsed from 'shared:<project>:' or 'promoted:<path>' sources.
116
+ * `tenantId` filters to one tenant; undefined is host-wide (legacy callers: CLI standalone, dashboard). */
117
+ export function listPeers(globalRoot, tenantId) {
118
+ const root = globalRoot ?? getGlobalRoot();
119
+ if (!fs.existsSync(root))
120
+ return [];
121
+ // Tenant-scoped by default when tenantId provided. Host-wide when
122
+ // undefined (preserves back-compat).
123
+ const tallies = tallySources(root, tenantId).sort((a, b) => (a.first < b.first ? -1 : a.first > b.first ? 1 : 0));
124
+ const peerMap = new Map();
125
+ for (const tally of tallies) {
126
+ let project = 'unknown';
127
+ if (tally.source.startsWith('shared:')) {
128
+ const parts = tally.source.split(':');
129
+ project = parts[1] || 'unknown';
130
+ }
131
+ else if (tally.source.startsWith('promoted:')) {
132
+ const promotedPath = tally.source.slice('promoted:'.length);
133
+ project = path.basename(path.resolve(promotedPath, '..'));
134
+ }
135
+ else if (tally.source === 'cli-global') {
136
+ project = 'global-cli';
137
+ }
138
+ const existing = peerMap.get(project);
139
+ if (!existing) {
140
+ peerMap.set(project, { count: tally.count, latest: tally.latest });
141
+ }
142
+ else {
143
+ existing.count += tally.count;
144
+ if (tally.latest > existing.latest)
145
+ existing.latest = tally.latest;
146
+ }
147
+ }
148
+ return Array.from(peerMap.entries())
149
+ .map(([project, data]) => ({ project, ...data }))
150
+ .sort((a, b) => b.count - a.count);
151
+ }
152
+ function isAutoShareCandidate(entry, globalContentSet, minScore, stats) {
153
+ // shareMemory refuses quarantined and personal rows; filtering here keeps sleep from aborting on one.
154
+ if (isQuarantineScope(entry.scope ?? null) || isPersonalScope(entry.scope ?? null) || !isReusable(entry))
155
+ return false;
156
+ // Before the score: these rows describe one project only, and a git seed's 'error' tag clears the bar.
157
+ if (entry.tags.some((t) => NEVER_AUTO_SHARE_TAGS.has(t)) || entry.source.startsWith(AGENT_MEMORY_SOURCE_PREFIX)) {
158
+ if (stats)
159
+ stats.neverAutoShareSkipped = (stats.neverAutoShareSkipped ?? 0) + 1;
160
+ return false;
161
+ }
162
+ const score = transferScore(entry);
163
+ if (score < minScore)
164
+ return false;
165
+ // Skip if already shared (same text apart from spacing)
166
+ if (globalContentSet.has(duplicateKey(entry.content)))
167
+ return false;
168
+ // Secret veto, checked LAST so the stats counter only counts rows it withheld (a row failing the score or dedupe gate was never going to share).
169
+ // shareMemory would throw here; filtering keeps the sleep pipeline fail-safe.
170
+ if (detectSecret(entry).flagged) {
171
+ if (stats)
172
+ stats.secretSkipped++;
173
+ return false;
174
+ }
175
+ return true;
176
+ }
177
+ // Rejection containment: shareMemory -> writeEntry can throw RejectedValueError against the GLOBAL store tombstones and abort the autoShare sleep phase.
178
+ // Catch per item like syncGlobalToLocal; writeEntry already audits reject_refusal before rethrowing, so count and continue.
179
+ function shareCandidates(localRoot, candidates, stats) {
180
+ const shared = [];
181
+ let rejectedSkipped = 0;
182
+ for (const entry of candidates) {
183
+ try {
184
+ // skipEmbed: batching invariant, this is a batch producer, so it embeds
185
+ // once via embedAll() below rather than once per row inside shareMemory.
186
+ const result = shareMemory(localRoot, entry.id, { force: true, skipEmbed: true });
187
+ if (result)
188
+ shared.push(result);
189
+ }
190
+ catch (err) {
191
+ if (err instanceof RejectedValueError) {
192
+ rejectedSkipped++;
193
+ if (stats)
194
+ stats.rejectedSkipped = (stats.rejectedSkipped ?? 0) + 1;
195
+ continue;
196
+ }
197
+ throw err;
198
+ }
199
+ }
200
+ if (rejectedSkipped > 0) {
201
+ log.warn(`autoShare: skipped ${rejectedSkipped} candidate(s) refused by the global store's rejection tombstone`);
202
+ }
203
+ return shared;
204
+ }
205
+ /** Auto-share local memories with high transfer scores, not already global and without a NEVER_AUTO_SHARE_TAGS tag; `options.tenantId` scopes the LOCAL read.
206
+ * `options.stats` is an opt-in out-param: `secretSkipped` counts shares the secret veto prevented, `rejectedSkipped` rejection refusals (0 under `dryRun`). */
207
+ export function autoShare(localRoot, options = {}) {
208
+ const { minScore = 0.6, dryRun = false } = options;
209
+ const localEntries = loadAllEntries(localRoot, options.tenantId);
210
+ initGlobal();
211
+ const globalRoot = getGlobalRoot();
212
+ // Host-wide read. The global store IS the union across all tenants;
213
+ // per-tenant filtering on the global root would defeat the purpose.
214
+ const globalEntries = loadAllEntries(globalRoot);
215
+ // Build set of global content hashes to avoid duplicates
216
+ const globalContentSet = storedTextKeys(globalEntries);
217
+ const candidates = localEntries.filter((entry) => isAutoShareCandidate(entry, globalContentSet, minScore, options.stats));
218
+ if (dryRun)
219
+ return candidates;
220
+ const shared = shareCandidates(localRoot, candidates, options.stats);
221
+ if (shared.length > 0) {
222
+ void embedAll(globalRoot).catch((err) => logEmbedAllFailure('autoShare', err));
223
+ }
224
+ return shared;
225
+ }
226
+ //# sourceMappingURL=share.js.map
@@ -1,8 +1,7 @@
1
- import { closeHippoDb } from '../db/index.js';
2
- import { strengthSql } from '../core/memory.js';
1
+ import { strengthSql } from './rule-sql.js';
3
2
  import { scopeAdmitSql } from './recall-scope.js';
4
3
  import { SECRET_TAGS } from '../util/secret-detect.js';
5
- import { openStore } from './open.js';
4
+ import { onHandle, openStore } from './open.js';
6
5
  import { jsonList } from './candidates.js';
7
6
  import { originInSql } from '../core/project-identity.js';
8
7
  import { isErrorTagged } from '../core/ambient.js';
@@ -41,8 +40,7 @@ export function loadAmbientTallies(hippoRoot, tenantId, filter) {
41
40
  const { where, params } = contextRowsWhere(tenantId, filter);
42
41
  const secret = secretTaggedSql();
43
42
  const sevenDaysAgo = new Date(filter.now.getTime() - 7 * DAY_MS).toISOString();
44
- const db = openStore(hippoRoot);
45
- try {
43
+ return onHandle(hippoRoot, (db) => {
46
44
  // SAFETY: one aggregate row whose columns are the aliases named below. Tag lists come back as one JSON array of
47
45
  // arrays and are counted in JS, which maps items through String() as parseJsonArray does.
48
46
  const row = db.prepare(`SELECT
@@ -84,9 +82,6 @@ export function loadAmbientTallies(hippoRoot, tenantId, filter) {
84
82
  maxDagLevel: Number(row.maxDagLevel),
85
83
  tagCounts,
86
84
  };
87
- }
88
- finally {
89
- closeHippoDb(db);
90
- }
85
+ }, openStore);
91
86
  }
92
87
  //# sourceMappingURL=ambient.js.map
@@ -8,18 +8,10 @@ export interface AuditOptions {
8
8
  readonly actor?: string;
9
9
  readonly tenantId?: string;
10
10
  }
11
- /**
12
- * Emit an audit event for a mutation against `db`. Wrapped so a broken audit
13
- * log can never crash the surrounding mutation — the SQLite store is still the
14
- * source of truth and audit failures are diagnosable from the missing rows.
15
- */
11
+ /** Emit an audit event for a mutation. Never throws: the SQLite store stays the source of truth,
12
+ * and a failed audit write shows up as missing rows. */
16
13
  export declare function audit(db: ReturnType<typeof openHippoDb>, op: AuditOp, options?: AuditOptions): void;
17
- /**
18
- * Refusal audit for the rejected-value guard. Written by the
19
- * transaction OWNER post-rollback — writeEntry's catch (no outer tx exists
20
- * there, so this lands in a fresh implicit transaction) and commitSupersede's
21
- * catch (after its own ROLLBACK) — never inside a scope the caller's own
22
- * rollback could claw back. Best-effort `audit()` semantics: never throws.
23
- */
14
+ /** Refusal audit for the rejected-value guard, written by the transaction owner after rollback so the caller's own rollback cannot claw it back.
15
+ * Best-effort like `audit()`: never throws. */
24
16
  export declare function auditRejectionRefusal(db: ReturnType<typeof openHippoDb>, err: RejectedValueError, actor: string): void;
25
17
  //# sourceMappingURL=audit-event.d.ts.map
@@ -1,10 +1,7 @@
1
1
  import { resolveTenantId } from './tenant.js';
2
2
  import { appendAuditEvent, reportAuditWriteFailure } from './audit.js';
3
- /**
4
- * Emit an audit event for a mutation against `db`. Wrapped so a broken audit
5
- * log can never crash the surrounding mutation — the SQLite store is still the
6
- * source of truth and audit failures are diagnosable from the missing rows.
7
- */
3
+ /** Emit an audit event for a mutation. Never throws: the SQLite store stays the source of truth,
4
+ * and a failed audit write shows up as missing rows. */
8
5
  export function audit(db, op, options = {}) {
9
6
  const { targetId, metadata, actor = 'cli', tenantId } = options;
10
7
  try {
@@ -21,13 +18,8 @@ export function audit(db, op, options = {}) {
21
18
  reportAuditWriteFailure(op, String(error), targetId);
22
19
  }
23
20
  }
24
- /**
25
- * Refusal audit for the rejected-value guard. Written by the
26
- * transaction OWNER post-rollback — writeEntry's catch (no outer tx exists
27
- * there, so this lands in a fresh implicit transaction) and commitSupersede's
28
- * catch (after its own ROLLBACK) — never inside a scope the caller's own
29
- * rollback could claw back. Best-effort `audit()` semantics: never throws.
30
- */
21
+ /** Refusal audit for the rejected-value guard, written by the transaction owner after rollback so the caller's own rollback cannot claw it back.
22
+ * Best-effort like `audit()`: never throws. */
31
23
  export function auditRejectionRefusal(db, err, actor) {
32
24
  audit(db, 'reject_refusal', { targetId: err.entryId, metadata: { digest: err.digest, reason: err.reason }, actor, tenantId: err.tenantId });
33
25
  }
@@ -66,11 +66,8 @@ export interface ListAuditAfterOpts {
66
66
  /** Omit for every tenant (deployment-wide export). */
67
67
  tenantId?: string;
68
68
  }
69
- /**
70
- * Cursor read: events with id > afterId, ascending by id. Ids are AUTOINCREMENT
71
- * and never reused, but deletes (retention prune) leave gaps, so resume from
72
- * the last id returned, never from a count.
73
- */
69
+ /** Cursor read: events with id > afterId, ascending. Retention prune leaves gaps in the ids,
70
+ * so resume from the last id returned, never from a count. */
74
71
  export declare function listAuditEventsAfter(db: DatabaseSyncLike, opts: ListAuditAfterOpts): AuditEvent[];
75
72
  /** Latest time each target got a good outcome, from the audit rows, read on a handle of its own; unlike queryAuditEvents it has no row cap. */
76
73
  export declare function confirmedOutcomeTimes(hippoRoot: string, tenantId: string): Map<string, string>;
@@ -55,9 +55,6 @@ export function auditMemories(entries, backing) {
55
55
  clean: entries.length - issues.length,
56
56
  };
57
57
  }
58
- // ---------------------------------------------------------------------------
59
- // Audit log primitives (append-only mutation trail)
60
- // ---------------------------------------------------------------------------
61
58
  // The one list of audit ops: the AuditOp type, `hippo audit list --op` and GET /v1/audit?op= all read it.
62
59
  export const AUDIT_OPS = [
63
60
  'remember',
@@ -182,11 +179,8 @@ export function queryAuditEvents(db, opts) {
182
179
  .all(...params, ...after.params, limit);
183
180
  return rows.map(rowToAuditEvent);
184
181
  }
185
- /**
186
- * Cursor read: events with id > afterId, ascending by id. Ids are AUTOINCREMENT
187
- * and never reused, but deletes (retention prune) leave gaps, so resume from
188
- * the last id returned, never from a count.
189
- */
182
+ /** Cursor read: events with id > afterId, ascending. Retention prune leaves gaps in the ids,
183
+ * so resume from the last id returned, never from a count. */
190
184
  export function listAuditEventsAfter(db, opts) {
191
185
  if (!Number.isInteger(opts.afterId) || opts.afterId < 0) {
192
186
  throw new RangeError('afterId must be a non-negative integer');
@@ -255,10 +249,8 @@ function rowToAuditEvent(r) {
255
249
  function safeJsonParse(raw, id) {
256
250
  try {
257
251
  const v = JSON.parse(raw);
258
- // SAFETY: JSON.parse only ever returns a plain object, array, string, number,
259
- // boolean, or null; `v instanceof Object` is true for exactly the first two
260
- // (both are valid JsonObject shapes for our purposes), matching the prior
261
- // `typeof v === 'object' && v !== null` check without using typeof.
252
+ // SAFETY: JSON.parse returns only an object, array or primitive; `instanceof Object` is true for the first two,
253
+ // both valid JsonObject shapes here.
262
254
  return v instanceof Object ? v : {};
263
255
  }
264
256
  catch {