hippo-memory 1.44.0 → 1.46.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 (554) hide show
  1. package/README.md +68 -24
  2. package/bin/hippo.js +3 -1
  3. package/dist/ablation.d.ts +10 -1
  4. package/dist/ablation.js +17 -1
  5. package/dist/api.d.ts +81 -22
  6. package/dist/api.js +258 -49
  7. package/dist/audit.d.ts +2 -2
  8. package/dist/audit.js +9 -0
  9. package/dist/autolearn.d.ts +1 -1
  10. package/dist/autolearn.js +2 -1
  11. package/dist/capture-error.d.ts +20 -0
  12. package/dist/capture-error.js +82 -0
  13. package/dist/capture.d.ts +25 -8
  14. package/dist/capture.js +100 -5
  15. package/dist/cli.d.ts +9 -1
  16. package/dist/cli.js +531 -119
  17. package/dist/client.d.ts +4 -21
  18. package/dist/client.js +3 -112
  19. package/dist/config.d.ts +20 -0
  20. package/dist/config.js +37 -1
  21. package/dist/consolidate.d.ts +9 -0
  22. package/dist/consolidate.js +133 -46
  23. package/dist/dag.d.ts +1 -0
  24. package/dist/dag.js +10 -5
  25. package/dist/dashboard.js +7 -3
  26. package/dist/db.js +69 -24
  27. package/dist/dedupe.d.ts +1 -0
  28. package/dist/dedupe.js +4 -7
  29. package/dist/doctor.d.ts +34 -0
  30. package/dist/doctor.js +174 -0
  31. package/dist/dormant.d.ts +91 -0
  32. package/dist/dormant.js +121 -0
  33. package/dist/embedding-provider.js +2 -1
  34. package/dist/embeddings.js +81 -8
  35. package/dist/eval-stats.d.ts +123 -0
  36. package/dist/eval-stats.js +187 -0
  37. package/dist/extract.d.ts +2 -0
  38. package/dist/extract.js +9 -4
  39. package/dist/half-life-migration.d.ts +55 -0
  40. package/dist/half-life-migration.js +111 -0
  41. package/dist/hooks.d.ts +4 -0
  42. package/dist/hooks.js +47 -0
  43. package/dist/mcp/server.d.ts +6 -0
  44. package/dist/mcp/server.js +90 -48
  45. package/dist/memory.d.ts +18 -1
  46. package/dist/memory.js +33 -5
  47. package/dist/physics-config.js +5 -1
  48. package/dist/recall-scope.d.ts +24 -0
  49. package/dist/recall-scope.js +41 -0
  50. package/dist/refine-llm.js +3 -2
  51. package/dist/reject-flow.d.ts +3 -3
  52. package/dist/reject-flow.js +10 -3
  53. package/dist/rerankers/jev.js +3 -2
  54. package/dist/rerankers/llm.js +3 -2
  55. package/dist/search.d.ts +4 -4
  56. package/dist/search.js +26 -21
  57. package/dist/server.d.ts +5 -0
  58. package/dist/server.js +49 -29
  59. package/dist/shared.js +1 -1
  60. package/dist/store.d.ts +29 -11
  61. package/dist/store.js +197 -70
  62. package/dist/token-ledger.d.ts +119 -0
  63. package/dist/token-ledger.js +181 -0
  64. package/dist/version.d.ts +4 -6
  65. package/dist/version.js +4 -7
  66. package/extensions/openclaw-plugin/openclaw.plugin.json +1 -1
  67. package/extensions/openclaw-plugin/package.json +1 -1
  68. package/openclaw.plugin.json +1 -1
  69. package/package.json +7 -3
  70. package/dist/ablation.d.ts.map +0 -1
  71. package/dist/ablation.js.map +0 -1
  72. package/dist/ambient.d.ts.map +0 -1
  73. package/dist/ambient.js.map +0 -1
  74. package/dist/api.d.ts.map +0 -1
  75. package/dist/api.js.map +0 -1
  76. package/dist/audit-prune.d.ts.map +0 -1
  77. package/dist/audit-prune.js.map +0 -1
  78. package/dist/audit.d.ts.map +0 -1
  79. package/dist/audit.js.map +0 -1
  80. package/dist/auth.d.ts.map +0 -1
  81. package/dist/auth.js.map +0 -1
  82. package/dist/autolearn.d.ts.map +0 -1
  83. package/dist/autolearn.js.map +0 -1
  84. package/dist/availability.d.ts.map +0 -1
  85. package/dist/availability.js.map +0 -1
  86. package/dist/benchmarks/e1.3/incident-recall-eval.js +0 -78
  87. package/dist/benchmarks/e1.3/incident-recall-eval.js.map +0 -1
  88. package/dist/benchmarks/e1.3/slack-1000-event-smoke.js +0 -103
  89. package/dist/benchmarks/e1.3/slack-1000-event-smoke.js.map +0 -1
  90. package/dist/capture.d.ts.map +0 -1
  91. package/dist/capture.js.map +0 -1
  92. package/dist/card-detail.d.ts.map +0 -1
  93. package/dist/card-detail.js.map +0 -1
  94. package/dist/card.d.ts.map +0 -1
  95. package/dist/card.js.map +0 -1
  96. package/dist/cli.d.ts.map +0 -1
  97. package/dist/cli.js.map +0 -1
  98. package/dist/client.d.ts.map +0 -1
  99. package/dist/client.js.map +0 -1
  100. package/dist/compare.d.ts.map +0 -1
  101. package/dist/compare.js.map +0 -1
  102. package/dist/config.d.ts.map +0 -1
  103. package/dist/config.js.map +0 -1
  104. package/dist/connectors/github/backfill.d.ts.map +0 -1
  105. package/dist/connectors/github/backfill.js.map +0 -1
  106. package/dist/connectors/github/cli-impl.d.ts.map +0 -1
  107. package/dist/connectors/github/cli-impl.js.map +0 -1
  108. package/dist/connectors/github/deletion.d.ts.map +0 -1
  109. package/dist/connectors/github/deletion.js.map +0 -1
  110. package/dist/connectors/github/dlq.d.ts.map +0 -1
  111. package/dist/connectors/github/dlq.js.map +0 -1
  112. package/dist/connectors/github/idempotency.d.ts.map +0 -1
  113. package/dist/connectors/github/idempotency.js.map +0 -1
  114. package/dist/connectors/github/ingest.d.ts.map +0 -1
  115. package/dist/connectors/github/ingest.js.map +0 -1
  116. package/dist/connectors/github/octokit-client.d.ts.map +0 -1
  117. package/dist/connectors/github/octokit-client.js.map +0 -1
  118. package/dist/connectors/github/ratelimit.d.ts.map +0 -1
  119. package/dist/connectors/github/ratelimit.js.map +0 -1
  120. package/dist/connectors/github/scope.d.ts.map +0 -1
  121. package/dist/connectors/github/scope.js.map +0 -1
  122. package/dist/connectors/github/signature.d.ts.map +0 -1
  123. package/dist/connectors/github/signature.js.map +0 -1
  124. package/dist/connectors/github/tenant-routing.d.ts.map +0 -1
  125. package/dist/connectors/github/tenant-routing.js.map +0 -1
  126. package/dist/connectors/github/transform.d.ts.map +0 -1
  127. package/dist/connectors/github/transform.js.map +0 -1
  128. package/dist/connectors/github/types.d.ts.map +0 -1
  129. package/dist/connectors/github/types.js.map +0 -1
  130. package/dist/connectors/slack/backfill.d.ts.map +0 -1
  131. package/dist/connectors/slack/backfill.js.map +0 -1
  132. package/dist/connectors/slack/deletion.d.ts.map +0 -1
  133. package/dist/connectors/slack/deletion.js.map +0 -1
  134. package/dist/connectors/slack/dlq.d.ts.map +0 -1
  135. package/dist/connectors/slack/dlq.js.map +0 -1
  136. package/dist/connectors/slack/idempotency.d.ts.map +0 -1
  137. package/dist/connectors/slack/idempotency.js.map +0 -1
  138. package/dist/connectors/slack/ingest.d.ts.map +0 -1
  139. package/dist/connectors/slack/ingest.js.map +0 -1
  140. package/dist/connectors/slack/ratelimit.d.ts.map +0 -1
  141. package/dist/connectors/slack/ratelimit.js.map +0 -1
  142. package/dist/connectors/slack/scope.d.ts.map +0 -1
  143. package/dist/connectors/slack/scope.js.map +0 -1
  144. package/dist/connectors/slack/signature.d.ts.map +0 -1
  145. package/dist/connectors/slack/signature.js.map +0 -1
  146. package/dist/connectors/slack/tenant-routing.d.ts.map +0 -1
  147. package/dist/connectors/slack/tenant-routing.js.map +0 -1
  148. package/dist/connectors/slack/transform.d.ts.map +0 -1
  149. package/dist/connectors/slack/transform.js.map +0 -1
  150. package/dist/connectors/slack/types.d.ts.map +0 -1
  151. package/dist/connectors/slack/types.js.map +0 -1
  152. package/dist/connectors/slack/web-client.d.ts.map +0 -1
  153. package/dist/connectors/slack/web-client.js.map +0 -1
  154. package/dist/connectors/slack/workspaces.d.ts.map +0 -1
  155. package/dist/connectors/slack/workspaces.js.map +0 -1
  156. package/dist/consolidate.d.ts.map +0 -1
  157. package/dist/consolidate.js.map +0 -1
  158. package/dist/correction-latency.d.ts.map +0 -1
  159. package/dist/correction-latency.js.map +0 -1
  160. package/dist/customer-notes.d.ts.map +0 -1
  161. package/dist/customer-notes.js.map +0 -1
  162. package/dist/dag.d.ts.map +0 -1
  163. package/dist/dag.js.map +0 -1
  164. package/dist/dashboard.d.ts.map +0 -1
  165. package/dist/dashboard.js.map +0 -1
  166. package/dist/db.d.ts.map +0 -1
  167. package/dist/db.js.map +0 -1
  168. package/dist/decisions.d.ts.map +0 -1
  169. package/dist/decisions.js.map +0 -1
  170. package/dist/dedupe.d.ts.map +0 -1
  171. package/dist/dedupe.js.map +0 -1
  172. package/dist/embedding-provider.d.ts.map +0 -1
  173. package/dist/embedding-provider.js.map +0 -1
  174. package/dist/embeddings.d.ts.map +0 -1
  175. package/dist/embeddings.js.map +0 -1
  176. package/dist/eval-suite.d.ts.map +0 -1
  177. package/dist/eval-suite.js.map +0 -1
  178. package/dist/eval.d.ts.map +0 -1
  179. package/dist/eval.js.map +0 -1
  180. package/dist/extensions/openclaw-plugin/index.js.map +0 -1
  181. package/dist/extract.d.ts.map +0 -1
  182. package/dist/extract.js.map +0 -1
  183. package/dist/forward-claim-detector.d.ts.map +0 -1
  184. package/dist/forward-claim-detector.js.map +0 -1
  185. package/dist/goals.d.ts.map +0 -1
  186. package/dist/goals.js.map +0 -1
  187. package/dist/graph-extract.d.ts.map +0 -1
  188. package/dist/graph-extract.js.map +0 -1
  189. package/dist/graph-recall.d.ts.map +0 -1
  190. package/dist/graph-recall.js.map +0 -1
  191. package/dist/graph-stream.d.ts.map +0 -1
  192. package/dist/graph-stream.js.map +0 -1
  193. package/dist/graph-view.d.ts.map +0 -1
  194. package/dist/graph-view.js.map +0 -1
  195. package/dist/graph.d.ts.map +0 -1
  196. package/dist/graph.js.map +0 -1
  197. package/dist/handoff.d.ts.map +0 -1
  198. package/dist/handoff.js.map +0 -1
  199. package/dist/hooks.d.ts.map +0 -1
  200. package/dist/hooks.js.map +0 -1
  201. package/dist/importers.d.ts.map +0 -1
  202. package/dist/importers.js.map +0 -1
  203. package/dist/incidents.d.ts.map +0 -1
  204. package/dist/incidents.js.map +0 -1
  205. package/dist/index.d.ts.map +0 -1
  206. package/dist/index.js.map +0 -1
  207. package/dist/invalidation.d.ts.map +0 -1
  208. package/dist/invalidation.js.map +0 -1
  209. package/dist/mcp/framing.d.ts.map +0 -1
  210. package/dist/mcp/framing.js.map +0 -1
  211. package/dist/mcp/server.d.ts.map +0 -1
  212. package/dist/mcp/server.js.map +0 -1
  213. package/dist/memory-value-weights.d.ts.map +0 -1
  214. package/dist/memory-value-weights.js.map +0 -1
  215. package/dist/memory-value.d.ts.map +0 -1
  216. package/dist/memory-value.js.map +0 -1
  217. package/dist/memory.d.ts.map +0 -1
  218. package/dist/memory.js.map +0 -1
  219. package/dist/multihop.d.ts.map +0 -1
  220. package/dist/multihop.js.map +0 -1
  221. package/dist/owner-validation.d.ts.map +0 -1
  222. package/dist/owner-validation.js.map +0 -1
  223. package/dist/path-context.d.ts.map +0 -1
  224. package/dist/path-context.js.map +0 -1
  225. package/dist/physics-config.d.ts.map +0 -1
  226. package/dist/physics-config.js.map +0 -1
  227. package/dist/physics-state.d.ts.map +0 -1
  228. package/dist/physics-state.js.map +0 -1
  229. package/dist/physics.d.ts.map +0 -1
  230. package/dist/physics.js.map +0 -1
  231. package/dist/policies.d.ts.map +0 -1
  232. package/dist/policies.js.map +0 -1
  233. package/dist/postinstall.d.ts.map +0 -1
  234. package/dist/postinstall.js.map +0 -1
  235. package/dist/predictions.d.ts.map +0 -1
  236. package/dist/predictions.js.map +0 -1
  237. package/dist/processes.d.ts.map +0 -1
  238. package/dist/processes.js.map +0 -1
  239. package/dist/project-briefs.d.ts.map +0 -1
  240. package/dist/project-briefs.js.map +0 -1
  241. package/dist/project-identity.d.ts.map +0 -1
  242. package/dist/project-identity.js.map +0 -1
  243. package/dist/provenance-coverage.d.ts.map +0 -1
  244. package/dist/provenance-coverage.js.map +0 -1
  245. package/dist/rate-limit.d.ts.map +0 -1
  246. package/dist/rate-limit.js.map +0 -1
  247. package/dist/raw-archive-mirror-cleanup.d.ts.map +0 -1
  248. package/dist/raw-archive-mirror-cleanup.js.map +0 -1
  249. package/dist/raw-archive.d.ts.map +0 -1
  250. package/dist/raw-archive.js.map +0 -1
  251. package/dist/recall-history.d.ts.map +0 -1
  252. package/dist/recall-history.js.map +0 -1
  253. package/dist/recall-scope.d.ts.map +0 -1
  254. package/dist/recall-scope.js.map +0 -1
  255. package/dist/recall-trace.d.ts.map +0 -1
  256. package/dist/recall-trace.js.map +0 -1
  257. package/dist/refine-llm.d.ts.map +0 -1
  258. package/dist/refine-llm.js.map +0 -1
  259. package/dist/reject-flow.d.ts.map +0 -1
  260. package/dist/reject-flow.js.map +0 -1
  261. package/dist/rejection.d.ts.map +0 -1
  262. package/dist/rejection.js.map +0 -1
  263. package/dist/replay.d.ts.map +0 -1
  264. package/dist/replay.js.map +0 -1
  265. package/dist/rerankers/cross-encoder.d.ts.map +0 -1
  266. package/dist/rerankers/cross-encoder.js.map +0 -1
  267. package/dist/rerankers/index.d.ts.map +0 -1
  268. package/dist/rerankers/index.js.map +0 -1
  269. package/dist/rerankers/jev.d.ts.map +0 -1
  270. package/dist/rerankers/jev.js.map +0 -1
  271. package/dist/rerankers/llm.d.ts.map +0 -1
  272. package/dist/rerankers/llm.js.map +0 -1
  273. package/dist/rerankers/types.d.ts.map +0 -1
  274. package/dist/rerankers/types.js.map +0 -1
  275. package/dist/rrf.d.ts.map +0 -1
  276. package/dist/rrf.js.map +0 -1
  277. package/dist/salience.d.ts.map +0 -1
  278. package/dist/salience.js.map +0 -1
  279. package/dist/scheduler.d.ts.map +0 -1
  280. package/dist/scheduler.js.map +0 -1
  281. package/dist/scope.d.ts.map +0 -1
  282. package/dist/scope.js.map +0 -1
  283. package/dist/search.d.ts.map +0 -1
  284. package/dist/search.js.map +0 -1
  285. package/dist/secret-detect.d.ts.map +0 -1
  286. package/dist/secret-detect.js.map +0 -1
  287. package/dist/server-detect.d.ts.map +0 -1
  288. package/dist/server-detect.js.map +0 -1
  289. package/dist/server.d.ts.map +0 -1
  290. package/dist/server.js.map +0 -1
  291. package/dist/shared.d.ts.map +0 -1
  292. package/dist/shared.js.map +0 -1
  293. package/dist/skills.d.ts.map +0 -1
  294. package/dist/skills.js.map +0 -1
  295. package/dist/sleep-redact.d.ts +0 -58
  296. package/dist/sleep-redact.d.ts.map +0 -1
  297. package/dist/sleep-redact.js +0 -80
  298. package/dist/sleep-redact.js.map +0 -1
  299. package/dist/src/ablation.js +0 -138
  300. package/dist/src/ablation.js.map +0 -1
  301. package/dist/src/ambient.js +0 -148
  302. package/dist/src/ambient.js.map +0 -1
  303. package/dist/src/api.js +0 -2109
  304. package/dist/src/api.js.map +0 -1
  305. package/dist/src/audit-prune.js +0 -109
  306. package/dist/src/audit-prune.js.map +0 -1
  307. package/dist/src/audit.js +0 -213
  308. package/dist/src/audit.js.map +0 -1
  309. package/dist/src/auth.js +0 -97
  310. package/dist/src/auth.js.map +0 -1
  311. package/dist/src/autolearn.js +0 -174
  312. package/dist/src/autolearn.js.map +0 -1
  313. package/dist/src/availability.js +0 -94
  314. package/dist/src/availability.js.map +0 -1
  315. package/dist/src/capture.js +0 -1346
  316. package/dist/src/capture.js.map +0 -1
  317. package/dist/src/card-detail.js +0 -15
  318. package/dist/src/card-detail.js.map +0 -1
  319. package/dist/src/card.js +0 -19
  320. package/dist/src/card.js.map +0 -1
  321. package/dist/src/cli.js +0 -9387
  322. package/dist/src/cli.js.map +0 -1
  323. package/dist/src/client.js +0 -237
  324. package/dist/src/client.js.map +0 -1
  325. package/dist/src/compare.js +0 -122
  326. package/dist/src/compare.js.map +0 -1
  327. package/dist/src/config.js +0 -135
  328. package/dist/src/config.js.map +0 -1
  329. package/dist/src/connectors/github/backfill.js +0 -281
  330. package/dist/src/connectors/github/backfill.js.map +0 -1
  331. package/dist/src/connectors/github/cli-impl.js +0 -234
  332. package/dist/src/connectors/github/cli-impl.js.map +0 -1
  333. package/dist/src/connectors/github/deletion.js +0 -85
  334. package/dist/src/connectors/github/deletion.js.map +0 -1
  335. package/dist/src/connectors/github/dlq.js +0 -190
  336. package/dist/src/connectors/github/dlq.js.map +0 -1
  337. package/dist/src/connectors/github/idempotency.js +0 -27
  338. package/dist/src/connectors/github/idempotency.js.map +0 -1
  339. package/dist/src/connectors/github/ingest.js +0 -156
  340. package/dist/src/connectors/github/ingest.js.map +0 -1
  341. package/dist/src/connectors/github/octokit-client.js +0 -70
  342. package/dist/src/connectors/github/octokit-client.js.map +0 -1
  343. package/dist/src/connectors/github/ratelimit.js +0 -31
  344. package/dist/src/connectors/github/ratelimit.js.map +0 -1
  345. package/dist/src/connectors/github/scope.js +0 -13
  346. package/dist/src/connectors/github/scope.js.map +0 -1
  347. package/dist/src/connectors/github/signature.js +0 -81
  348. package/dist/src/connectors/github/signature.js.map +0 -1
  349. package/dist/src/connectors/github/tenant-routing.js +0 -69
  350. package/dist/src/connectors/github/tenant-routing.js.map +0 -1
  351. package/dist/src/connectors/github/transform.js +0 -103
  352. package/dist/src/connectors/github/transform.js.map +0 -1
  353. package/dist/src/connectors/github/types.js +0 -100
  354. package/dist/src/connectors/github/types.js.map +0 -1
  355. package/dist/src/connectors/slack/backfill.js +0 -78
  356. package/dist/src/connectors/slack/backfill.js.map +0 -1
  357. package/dist/src/connectors/slack/deletion.js +0 -59
  358. package/dist/src/connectors/slack/deletion.js.map +0 -1
  359. package/dist/src/connectors/slack/dlq.js +0 -224
  360. package/dist/src/connectors/slack/dlq.js.map +0 -1
  361. package/dist/src/connectors/slack/idempotency.js +0 -31
  362. package/dist/src/connectors/slack/idempotency.js.map +0 -1
  363. package/dist/src/connectors/slack/ingest.js +0 -109
  364. package/dist/src/connectors/slack/ingest.js.map +0 -1
  365. package/dist/src/connectors/slack/ratelimit.js +0 -18
  366. package/dist/src/connectors/slack/ratelimit.js.map +0 -1
  367. package/dist/src/connectors/slack/scope.js +0 -13
  368. package/dist/src/connectors/slack/scope.js.map +0 -1
  369. package/dist/src/connectors/slack/signature.js +0 -27
  370. package/dist/src/connectors/slack/signature.js.map +0 -1
  371. package/dist/src/connectors/slack/tenant-routing.js +0 -41
  372. package/dist/src/connectors/slack/tenant-routing.js.map +0 -1
  373. package/dist/src/connectors/slack/transform.js +0 -47
  374. package/dist/src/connectors/slack/transform.js.map +0 -1
  375. package/dist/src/connectors/slack/types.js +0 -35
  376. package/dist/src/connectors/slack/types.js.map +0 -1
  377. package/dist/src/connectors/slack/web-client.js +0 -43
  378. package/dist/src/connectors/slack/web-client.js.map +0 -1
  379. package/dist/src/connectors/slack/workspaces.js +0 -62
  380. package/dist/src/connectors/slack/workspaces.js.map +0 -1
  381. package/dist/src/consolidate.js +0 -978
  382. package/dist/src/consolidate.js.map +0 -1
  383. package/dist/src/correction-latency.js +0 -74
  384. package/dist/src/correction-latency.js.map +0 -1
  385. package/dist/src/customer-notes.js +0 -325
  386. package/dist/src/customer-notes.js.map +0 -1
  387. package/dist/src/dag.js +0 -352
  388. package/dist/src/dag.js.map +0 -1
  389. package/dist/src/dashboard.js +0 -258
  390. package/dist/src/dashboard.js.map +0 -1
  391. package/dist/src/db.js +0 -2731
  392. package/dist/src/db.js.map +0 -1
  393. package/dist/src/decisions.js +0 -304
  394. package/dist/src/decisions.js.map +0 -1
  395. package/dist/src/dedupe.js +0 -141
  396. package/dist/src/dedupe.js.map +0 -1
  397. package/dist/src/embedding-provider.js +0 -314
  398. package/dist/src/embedding-provider.js.map +0 -1
  399. package/dist/src/embeddings.js +0 -543
  400. package/dist/src/embeddings.js.map +0 -1
  401. package/dist/src/eval-suite.js +0 -294
  402. package/dist/src/eval-suite.js.map +0 -1
  403. package/dist/src/eval.js +0 -187
  404. package/dist/src/eval.js.map +0 -1
  405. package/dist/src/extract.js +0 -117
  406. package/dist/src/extract.js.map +0 -1
  407. package/dist/src/forward-claim-detector.js +0 -117
  408. package/dist/src/forward-claim-detector.js.map +0 -1
  409. package/dist/src/goals.js +0 -399
  410. package/dist/src/goals.js.map +0 -1
  411. package/dist/src/graph-extract.js +0 -314
  412. package/dist/src/graph-extract.js.map +0 -1
  413. package/dist/src/graph-recall.js +0 -277
  414. package/dist/src/graph-recall.js.map +0 -1
  415. package/dist/src/graph-stream.js +0 -176
  416. package/dist/src/graph-stream.js.map +0 -1
  417. package/dist/src/graph-view.js +0 -310
  418. package/dist/src/graph-view.js.map +0 -1
  419. package/dist/src/graph.js +0 -762
  420. package/dist/src/graph.js.map +0 -1
  421. package/dist/src/handoff.js +0 -65
  422. package/dist/src/handoff.js.map +0 -1
  423. package/dist/src/hooks.js +0 -926
  424. package/dist/src/hooks.js.map +0 -1
  425. package/dist/src/importers.js +0 -881
  426. package/dist/src/importers.js.map +0 -1
  427. package/dist/src/incidents.js +0 -336
  428. package/dist/src/incidents.js.map +0 -1
  429. package/dist/src/index.js +0 -32
  430. package/dist/src/index.js.map +0 -1
  431. package/dist/src/invalidation.js +0 -129
  432. package/dist/src/invalidation.js.map +0 -1
  433. package/dist/src/mcp/framing.js +0 -45
  434. package/dist/src/mcp/framing.js.map +0 -1
  435. package/dist/src/mcp/server.js +0 -1298
  436. package/dist/src/mcp/server.js.map +0 -1
  437. package/dist/src/memory-value-weights.js +0 -31
  438. package/dist/src/memory-value-weights.js.map +0 -1
  439. package/dist/src/memory-value.js +0 -261
  440. package/dist/src/memory-value.js.map +0 -1
  441. package/dist/src/memory.js +0 -425
  442. package/dist/src/memory.js.map +0 -1
  443. package/dist/src/multihop.js +0 -35
  444. package/dist/src/multihop.js.map +0 -1
  445. package/dist/src/owner-validation.js +0 -56
  446. package/dist/src/owner-validation.js.map +0 -1
  447. package/dist/src/path-context.js +0 -48
  448. package/dist/src/path-context.js.map +0 -1
  449. package/dist/src/physics-config.js +0 -26
  450. package/dist/src/physics-config.js.map +0 -1
  451. package/dist/src/physics-state.js +0 -172
  452. package/dist/src/physics-state.js.map +0 -1
  453. package/dist/src/physics.js +0 -378
  454. package/dist/src/physics.js.map +0 -1
  455. package/dist/src/policies.js +0 -414
  456. package/dist/src/policies.js.map +0 -1
  457. package/dist/src/postinstall.js +0 -101
  458. package/dist/src/postinstall.js.map +0 -1
  459. package/dist/src/predictions.js +0 -629
  460. package/dist/src/predictions.js.map +0 -1
  461. package/dist/src/processes.js +0 -349
  462. package/dist/src/processes.js.map +0 -1
  463. package/dist/src/project-briefs.js +0 -492
  464. package/dist/src/project-briefs.js.map +0 -1
  465. package/dist/src/project-identity.js +0 -200
  466. package/dist/src/project-identity.js.map +0 -1
  467. package/dist/src/provenance-coverage.js +0 -23
  468. package/dist/src/provenance-coverage.js.map +0 -1
  469. package/dist/src/rate-limit.js +0 -60
  470. package/dist/src/rate-limit.js.map +0 -1
  471. package/dist/src/raw-archive-mirror-cleanup.js +0 -55
  472. package/dist/src/raw-archive-mirror-cleanup.js.map +0 -1
  473. package/dist/src/raw-archive.js +0 -91
  474. package/dist/src/raw-archive.js.map +0 -1
  475. package/dist/src/recall-history.js +0 -235
  476. package/dist/src/recall-history.js.map +0 -1
  477. package/dist/src/recall-scope.js +0 -89
  478. package/dist/src/recall-scope.js.map +0 -1
  479. package/dist/src/recall-trace.js +0 -186
  480. package/dist/src/recall-trace.js.map +0 -1
  481. package/dist/src/refine-llm.js +0 -156
  482. package/dist/src/refine-llm.js.map +0 -1
  483. package/dist/src/reject-flow.js +0 -209
  484. package/dist/src/reject-flow.js.map +0 -1
  485. package/dist/src/rejection.js +0 -160
  486. package/dist/src/rejection.js.map +0 -1
  487. package/dist/src/replay.js +0 -122
  488. package/dist/src/replay.js.map +0 -1
  489. package/dist/src/rerankers/cross-encoder.js +0 -137
  490. package/dist/src/rerankers/cross-encoder.js.map +0 -1
  491. package/dist/src/rerankers/index.js +0 -20
  492. package/dist/src/rerankers/index.js.map +0 -1
  493. package/dist/src/rerankers/jev.js +0 -119
  494. package/dist/src/rerankers/jev.js.map +0 -1
  495. package/dist/src/rerankers/llm.js +0 -80
  496. package/dist/src/rerankers/llm.js.map +0 -1
  497. package/dist/src/rerankers/types.js +0 -2
  498. package/dist/src/rerankers/types.js.map +0 -1
  499. package/dist/src/rrf.js +0 -61
  500. package/dist/src/rrf.js.map +0 -1
  501. package/dist/src/salience.js +0 -74
  502. package/dist/src/salience.js.map +0 -1
  503. package/dist/src/scheduler.js +0 -77
  504. package/dist/src/scheduler.js.map +0 -1
  505. package/dist/src/scope.js +0 -35
  506. package/dist/src/scope.js.map +0 -1
  507. package/dist/src/search.js +0 -1003
  508. package/dist/src/search.js.map +0 -1
  509. package/dist/src/secret-detect.js +0 -95
  510. package/dist/src/secret-detect.js.map +0 -1
  511. package/dist/src/server-detect.js +0 -231
  512. package/dist/src/server-detect.js.map +0 -1
  513. package/dist/src/server.js +0 -3255
  514. package/dist/src/server.js.map +0 -1
  515. package/dist/src/shared.js +0 -502
  516. package/dist/src/shared.js.map +0 -1
  517. package/dist/src/skills.js +0 -346
  518. package/dist/src/skills.js.map +0 -1
  519. package/dist/src/sleep-redact.js +0 -80
  520. package/dist/src/sleep-redact.js.map +0 -1
  521. package/dist/src/sso.js +0 -22
  522. package/dist/src/sso.js.map +0 -1
  523. package/dist/src/stdin.js +0 -41
  524. package/dist/src/stdin.js.map +0 -1
  525. package/dist/src/store.js +0 -3749
  526. package/dist/src/store.js.map +0 -1
  527. package/dist/src/tenant.js +0 -17
  528. package/dist/src/tenant.js.map +0 -1
  529. package/dist/src/trace.js +0 -72
  530. package/dist/src/trace.js.map +0 -1
  531. package/dist/src/version.js +0 -40
  532. package/dist/src/version.js.map +0 -1
  533. package/dist/src/working-memory.js +0 -157
  534. package/dist/src/working-memory.js.map +0 -1
  535. package/dist/src/yaml.js +0 -107
  536. package/dist/src/yaml.js.map +0 -1
  537. package/dist/sso.d.ts +0 -13
  538. package/dist/sso.d.ts.map +0 -1
  539. package/dist/sso.js +0 -22
  540. package/dist/sso.js.map +0 -1
  541. package/dist/stdin.d.ts.map +0 -1
  542. package/dist/stdin.js.map +0 -1
  543. package/dist/store.d.ts.map +0 -1
  544. package/dist/store.js.map +0 -1
  545. package/dist/tenant.d.ts.map +0 -1
  546. package/dist/tenant.js.map +0 -1
  547. package/dist/trace.d.ts.map +0 -1
  548. package/dist/trace.js.map +0 -1
  549. package/dist/version.d.ts.map +0 -1
  550. package/dist/version.js.map +0 -1
  551. package/dist/working-memory.d.ts.map +0 -1
  552. package/dist/working-memory.js.map +0 -1
  553. package/dist/yaml.d.ts.map +0 -1
  554. package/dist/yaml.js.map +0 -1
package/dist/store.js CHANGED
@@ -6,12 +6,13 @@
6
6
  */
7
7
  import * as fs from 'fs';
8
8
  import * as path from 'path';
9
- import { Layer, generateId } from './memory.js';
9
+ import { Layer, generateId, AUTO_DELETABLE_SQL, DEFAULT_HALF_LIFE_DAYS } from './memory.js';
10
10
  import { dumpFrontmatter, parseFrontmatter } from './yaml.js';
11
11
  import { openHippoDb, closeHippoDb, getMeta, setMeta, isFtsAvailable, pruneConsolidationRuns, getHippoDbPath, } from './db.js';
12
12
  import { rowToSessionHandoff, isHandoffOutcome } from './handoff.js';
13
13
  import { CARD_TRANSITIONS, CARD_LEASE_MS } from './card.js';
14
- import { tokenize } from './search.js';
14
+ import { tokenize, markRetrieved } from './search.js';
15
+ import { isRecallBoostAblated } from './ablation.js';
15
16
  import { appendAuditEvent } from './audit.js';
16
17
  import { resolveTenantId } from './tenant.js';
17
18
  import { deriveOriginProject, originFromSource, findHippoStoreDir, realpathOrResolve } from './project-identity.js';
@@ -22,6 +23,7 @@ import { checkRejectionGuard, RejectedValueError, rejectionDigest, normalizeValu
22
23
  // inside function bodies (never at module-evaluation time), so the cycle
23
24
  // is the standard safe mutual-function-reference shape under NodeNext ESM.
24
25
  import { archiveRawMemory } from './raw-archive.js';
26
+ import { insertDormantRow } from './dormant.js';
25
27
  /**
26
28
  * Emit an audit event for a mutation against `db`. Wrapped so a broken audit
27
29
  * log can never crash the surrounding mutation — the SQLite store is still the
@@ -118,11 +120,27 @@ export function initStore(hippoRoot) {
118
120
  if (bootstrapped) {
119
121
  syncMirrorFiles(hippoRoot, db);
120
122
  }
123
+ recordHalfLifeBaseForNewStore(db);
121
124
  }
122
125
  finally {
123
126
  closeHippoDb(db);
124
127
  }
125
128
  }
129
+ /** `meta` key holding the default half-life base a store's memories are on (src/half-life-migration.ts). */
130
+ export const HALF_LIFE_BASE_META_KEY = 'default_half_life_base';
131
+ /**
132
+ * A store with no memories starts on the current default half-life base, so
133
+ * `hippo sleep` never migrates it. A store that already holds memories and
134
+ * no recorded base predates the record, and keeps reading as the legacy
135
+ * 7-day base until sleep migrates it.
136
+ */
137
+ function recordHalfLifeBaseForNewStore(db) {
138
+ if (getMeta(db, HALF_LIFE_BASE_META_KEY, '') !== '')
139
+ return;
140
+ if (db.prepare(`SELECT 1 AS x FROM memories LIMIT 1`).get() !== undefined)
141
+ return;
142
+ setMeta(db, HALF_LIFE_BASE_META_KEY, String(DEFAULT_HALF_LIFE_DAYS));
143
+ }
126
144
  function ensureMirrorDirectories(hippoRoot) {
127
145
  const dirs = [
128
146
  hippoRoot,
@@ -504,7 +522,7 @@ function canonicalConflictPair(aId, bId) {
504
522
  ? { memory_a_id: aId, memory_b_id: bId }
505
523
  : { memory_a_id: bId, memory_b_id: aId };
506
524
  }
507
- function loadSearchRows(db, query, limit, tenantId, scopeFilter) {
525
+ function loadSearchRows(db, query, limit, tenantId, scopeFilter, includeSuperseded = true) {
508
526
  // tenantId undefined = no tenant filter (legacy callers / cross-deployment
509
527
  // helpers). tenantId set = strict tenant isolation, leveraging the composite
510
528
  // idx_memories_tenant_created (leading column tenant_id, O(log n) lookup).
@@ -597,13 +615,15 @@ function loadSearchRows(db, query, limit, tenantId, scopeFilter) {
597
615
  scopeParams.push(scopeFilter.value);
598
616
  }
599
617
  }
618
+ const currentAlias = includeSuperseded ? '' : ' AND m.superseded_by IS NULL';
619
+ const currentNoAlias = includeSuperseded ? '' : ' AND superseded_by IS NULL';
600
620
  const terms = Array.from(new Set(tokenize(query)));
601
621
  if (terms.length === 0) {
602
622
  // F3 (v1.7.0) self-review: empty-query path is the second uncapped
603
623
  // path (codex diff-pass caught the full-store fallback at the bottom;
604
624
  // this no-terms path had the same shape). Apply LIMIT so all four
605
625
  // candidate paths honour the caller's cap when set.
606
- const sql = `SELECT ${MEMORY_SELECT_COLUMNS} FROM memories${tenantOnlyPredicate}${archivedClauseTenantOnly}${scopeClauseTenantOnly} ORDER BY created ASC, id ASC LIMIT ?`;
626
+ const sql = `SELECT ${MEMORY_SELECT_COLUMNS} FROM memories${tenantOnlyPredicate}${archivedClauseTenantOnly}${scopeClauseTenantOnly}${currentNoAlias} ORDER BY created ASC, id ASC LIMIT ?`;
607
627
  // SAFETY: sql selects exactly MEMORY_SELECT_COLUMNS, whose column list
608
628
  // matches MemoryRow's field set.
609
629
  return db.prepare(sql).all(...tenantParams, ...scopeParams, limit);
@@ -630,7 +650,7 @@ function loadSearchRows(db, query, limit, tenantId, scopeFilter) {
630
650
  SELECT ${MEMORY_SEARCH_COLUMNS}
631
651
  FROM memories m
632
652
  JOIN memories_fts f ON f.id = m.id
633
- WHERE memories_fts MATCH ?${tenantPredicate}${archivedClauseAlias}${scopeClauseAlias}
653
+ WHERE memories_fts MATCH ?${tenantPredicate}${archivedClauseAlias}${scopeClauseAlias}${currentAlias}
634
654
  ORDER BY bm25(memories_fts), m.updated_at DESC, m.content ASC, m.id ASC
635
655
  LIMIT ?
636
656
  `).all(ftsQuery, ...tenantParams, ...scopeParams, limit);
@@ -652,7 +672,7 @@ function loadSearchRows(db, query, limit, tenantId, scopeFilter) {
652
672
  const rows = db.prepare(`
653
673
  SELECT ${MEMORY_SELECT_COLUMNS}
654
674
  FROM memories
655
- WHERE (${where})${tenantPredicateNoAlias}${archivedClauseNoAlias}${scopeClauseNoAlias}
675
+ WHERE (${where})${tenantPredicateNoAlias}${archivedClauseNoAlias}${scopeClauseNoAlias}${currentNoAlias}
656
676
  ORDER BY updated_at DESC, created DESC, content ASC, id ASC
657
677
  LIMIT ?
658
678
  `).all(...params, ...tenantParams, ...scopeParams, limit);
@@ -663,7 +683,7 @@ function loadSearchRows(db, query, limit, tenantId, scopeFilter) {
663
683
  // now reported on RecallResult, an unbounded fallback would lie about
664
684
  // candidate-pool size. Apply LIMIT here so all four paths honour the
665
685
  // caller's cap.
666
- const fallback = `SELECT ${MEMORY_SELECT_COLUMNS} FROM memories${tenantOnlyPredicate}${archivedClauseTenantOnly}${scopeClauseTenantOnly} ORDER BY created ASC, id ASC LIMIT ?`;
686
+ const fallback = `SELECT ${MEMORY_SELECT_COLUMNS} FROM memories${tenantOnlyPredicate}${archivedClauseTenantOnly}${scopeClauseTenantOnly}${currentNoAlias} ORDER BY created ASC, id ASC LIMIT ?`;
667
687
  // SAFETY: fallback selects exactly MEMORY_SELECT_COLUMNS, matching
668
688
  // MemoryRow's field set.
669
689
  return db.prepare(fallback).all(...tenantParams, ...scopeParams, limit);
@@ -1064,18 +1084,28 @@ function buildStatsFromDb(db) {
1064
1084
  * `buildIndexFromDb`'s query.
1065
1085
  */
1066
1086
  export function writeIndexMirror(hippoRoot, index) {
1067
- fs.writeFileSync(path.join(hippoRoot, 'index.json'), JSON.stringify(index, null, 2), 'utf8');
1087
+ mirrorBestEffort('index.json', () => fs.writeFileSync(path.join(hippoRoot, 'index.json'), JSON.stringify(index, null, 2), 'utf8'));
1068
1088
  }
1069
1089
  function writeStatsMirror(hippoRoot, stats) {
1070
- fs.writeFileSync(path.join(hippoRoot, 'stats.json'), JSON.stringify(stats, null, 2), 'utf8');
1090
+ mirrorBestEffort('stats.json', () => fs.writeFileSync(path.join(hippoRoot, 'stats.json'), JSON.stringify(stats, null, 2), 'utf8'));
1091
+ }
1092
+ /** Mirrors are derived from SQLite and written after COMMIT, so a failed write warns instead of failing a committed change. */
1093
+ function mirrorBestEffort(what, write) {
1094
+ try {
1095
+ write();
1096
+ }
1097
+ catch (err) {
1098
+ console.error(`hippo: ${what} not refreshed (${err instanceof Error ? err.message : String(err)}); the database write succeeded`);
1099
+ }
1071
1100
  }
1072
1101
  function syncMirrorFiles(hippoRoot, db) {
1073
1102
  // SAFETY: this query selects exactly MEMORY_SELECT_COLUMNS, matching
1074
1103
  // MemoryRow's field set.
1075
1104
  const entries = db.prepare(`SELECT ${MEMORY_SELECT_COLUMNS} FROM memories ORDER BY created ASC, id ASC`).all();
1076
- for (const entry of entries.map(rowToEntry)) {
1077
- writeMarkdownMirror(hippoRoot, entry);
1078
- }
1105
+ mirrorBestEffort('markdown mirrors', () => {
1106
+ for (const entry of entries.map(rowToEntry))
1107
+ writeMarkdownMirror(hippoRoot, entry);
1108
+ });
1079
1109
  // SAFETY: conflicts' shape matches the eight columns named in the SELECT
1080
1110
  // above.
1081
1111
  const conflicts = db.prepare(`
@@ -1084,20 +1114,16 @@ function syncMirrorFiles(hippoRoot, db) {
1084
1114
  WHERE status = 'open'
1085
1115
  ORDER BY updated_at DESC, id DESC
1086
1116
  `).all();
1087
- writeConflictMirrors(hippoRoot, conflicts.map(rowToMemoryConflict));
1117
+ mirrorBestEffort('conflict mirrors', () => writeConflictMirrors(hippoRoot, conflicts.map(rowToMemoryConflict)));
1088
1118
  writeIndexMirror(hippoRoot, buildIndexFromDb(db));
1089
1119
  writeStatsMirror(hippoRoot, buildStatsFromDb(db));
1090
1120
  }
1091
- /**
1092
- * Load the current derived index from SQLite and refresh the mirror file.
1093
- */
1121
+ /** Load the derived index from SQLite. Read-only: writers refresh index.json, so readers never race on it. */
1094
1122
  export function loadIndex(hippoRoot) {
1095
1123
  initStore(hippoRoot);
1096
1124
  const db = openHippoDb(hippoRoot);
1097
1125
  try {
1098
- const index = buildIndexFromDb(db);
1099
- writeIndexMirror(hippoRoot, index);
1100
- return index;
1126
+ return buildIndexFromDb(db);
1101
1127
  }
1102
1128
  finally {
1103
1129
  closeHippoDb(db);
@@ -1267,7 +1293,7 @@ export function writeEntryDbOnly(db, entry, opts) {
1267
1293
  * during a tx that subsequently rolls back would leave orphan markdown.
1268
1294
  */
1269
1295
  export function writeEntryMirrors(hippoRoot, db, entry) {
1270
- writeMarkdownMirror(hippoRoot, entry);
1296
+ mirrorBestEffort(`${entry.id}.md`, () => writeMarkdownMirror(hippoRoot, entry));
1271
1297
  writeIndexMirror(hippoRoot, buildIndexFromDb(db));
1272
1298
  }
1273
1299
  /**
@@ -1320,6 +1346,46 @@ export function loadEntriesByIds(hippoRoot, ids, tenantId) {
1320
1346
  closeHippoDb(db);
1321
1347
  }
1322
1348
  }
1349
+ /** Strengthen what a read returned: update only the four retrieval columns on the live row, never a stale copy.
1350
+ * Best effort: a failure logs and never fails the read. Returns the ids found in this store. */
1351
+ export function strengthenRetrieved(hippoRoot, ids, tenantId) {
1352
+ const found = new Set();
1353
+ if (ids.length === 0 || isRecallBoostAblated())
1354
+ return found;
1355
+ let db;
1356
+ try {
1357
+ db = openHippoDb(hippoRoot);
1358
+ db.exec('BEGIN IMMEDIATE');
1359
+ const tenantClause = tenantId !== undefined ? ' AND tenant_id = ?' : '';
1360
+ const select = db.prepare(`SELECT ${MEMORY_SELECT_COLUMNS} FROM memories WHERE id = ?${tenantClause}`);
1361
+ const live = [];
1362
+ for (const id of ids) {
1363
+ // SAFETY: the SELECT names exactly MEMORY_SELECT_COLUMNS, matching MemoryRow's field set.
1364
+ const row = (tenantId !== undefined ? select.get(id, tenantId) : select.get(id));
1365
+ if (row)
1366
+ live.push(rowToEntry(row));
1367
+ }
1368
+ const update = db.prepare('UPDATE memories SET retrieval_count = ?, last_retrieved = ?, half_life_days = ?, strength = ? WHERE id = ?');
1369
+ for (const e of markRetrieved(live)) {
1370
+ update.run(e.retrieval_count, e.last_retrieved, e.half_life_days, e.strength, e.id);
1371
+ found.add(e.id);
1372
+ }
1373
+ db.exec('COMMIT');
1374
+ }
1375
+ catch (error) {
1376
+ try {
1377
+ db?.exec('ROLLBACK');
1378
+ }
1379
+ catch { /* already rolled back; keep the original error */ }
1380
+ console.error(`hippo: retrieval stats not saved (${error instanceof Error ? error.message : String(error)})`);
1381
+ found.clear();
1382
+ }
1383
+ finally {
1384
+ if (db)
1385
+ closeHippoDb(db);
1386
+ }
1387
+ return found;
1388
+ }
1323
1389
  /**
1324
1390
  * All `kind='raw'` rows for a given session, tenant-scoped, returned
1325
1391
  * oldest-first. Used by `api.assemble` to walk a session's chronological
@@ -1499,8 +1565,8 @@ export function loadChildrenOf(hippoRoot, parentId, tenantId) {
1499
1565
  * removed) and `resolveConflict` (`conflict_resolve` row per resolution).
1500
1566
  * Default keeps `deleteEntry` byte-identical to its pre-split behavior.
1501
1567
  *
1502
- * Returns `{tenantId, dagParentId}` for the removed row, or `null` if no row
1503
- * with `id` existed.
1568
+ * Returns `{tenantId, dagParentId}` for the removed row, or `null` if no row with `id`
1569
+ * existed or `automatic` refused it (pinned or raw at DELETE time, so a late pin wins).
1504
1570
  */
1505
1571
  export function deleteEntryCore(db, id, opts) {
1506
1572
  // SAFETY: row's shape matches the three columns named in the SELECT above.
@@ -1509,10 +1575,12 @@ export function deleteEntryCore(db, id, opts) {
1509
1575
  .get(id);
1510
1576
  if (!row?.id)
1511
1577
  return null;
1512
- db.prepare(`DELETE FROM memories WHERE id = ?`).run(id);
1578
+ const guard = opts?.automatic ? ` AND ${AUTO_DELETABLE_SQL}` : '';
1579
+ if (Number(db.prepare(`DELETE FROM memories WHERE id = ?${guard}`).run(id).changes ?? 0) === 0)
1580
+ return null;
1513
1581
  deleteFtsRow(db, id);
1514
1582
  if (!opts?.suppressForgetAudit) {
1515
- audit(db, 'forget', id, undefined, opts?.actor ?? 'cli', row.tenant_id);
1583
+ audit(db, 'forget', id, opts?.reason ? { reason: opts.reason } : undefined, opts?.actor ?? 'cli', row.tenant_id);
1516
1584
  }
1517
1585
  // v0.30 / E2 — DAG live-coupling: forget of a child under a level-2
1518
1586
  // summary marks parent dirty. Non-atomic with the DELETE (no SAVEPOINT
@@ -1542,7 +1610,7 @@ export function deleteEntry(hippoRoot, id, opts) {
1542
1610
  const result = deleteEntryCore(db, id, opts);
1543
1611
  if (!result)
1544
1612
  return false;
1545
- removeEntryMirrors(hippoRoot, id);
1613
+ purgeMirrorBestEffort(hippoRoot, id, false, 'deleteEntry');
1546
1614
  writeIndexMirror(hippoRoot, buildIndexFromDb(db));
1547
1615
  return true;
1548
1616
  }
@@ -1550,13 +1618,28 @@ export function deleteEntry(hippoRoot, id, opts) {
1550
1618
  closeHippoDb(db);
1551
1619
  }
1552
1620
  }
1553
- /**
1554
- * Batch-write and batch-delete entries in a single transaction.
1555
- * Used by consolidation to avoid N open/close cycles.
1556
- */
1557
- export function batchWriteAndDelete(hippoRoot, toWrite, toDeleteIds) {
1558
- if (toWrite.length === 0 && toDeleteIds.length === 0)
1559
- return;
1621
+ // The child fields a level-2/3 summary is built from (loadChildrenOfSummary, generateDagSummary).
1622
+ const SUMMARY_INPUTS = ['content', 'created', 'dag_parent_id', 'kind'];
1623
+ function mergeOwnChanges(base, ours, live) {
1624
+ const row = { ...live };
1625
+ const loaded = new Map(Object.entries(base));
1626
+ for (const [key, value] of Object.entries(ours)) {
1627
+ if (JSON.stringify(value) !== JSON.stringify(loaded.get(key)))
1628
+ Object.assign(row, { [key]: value });
1629
+ }
1630
+ return row;
1631
+ }
1632
+ /** Consolidation's flush, one transaction. With `snapshot` (rows as the caller loaded them), a write keeps only
1633
+ * the fields the caller changed, takes the rest from the live row, and never resurrects a row that is gone.
1634
+ *
1635
+ * `dormant` (src/dormant.ts): each move's snapshot is inserted into `dormant_memories` and its `memories` row
1636
+ * leaves exactly like a delete (FTS row, DAG parent dirty-mark, mirrors), in the same transaction, so a memory
1637
+ * is never in both places or in neither. Deletes and moves both skip rows that are no longer auto-deletable
1638
+ * (pinned or raw since the caller decided). Returns the ids that left `memories`, deleted or moved. */
1639
+ export function batchWriteAndDelete(hippoRoot, toWrite, toDeleteIds, opts) {
1640
+ const dormantMoves = opts?.dormant ?? [];
1641
+ if (toWrite.length === 0 && toDeleteIds.length === 0 && dormantMoves.length === 0)
1642
+ return [];
1560
1643
  initStore(hippoRoot);
1561
1644
  const db = openHippoDb(hippoRoot);
1562
1645
  try {
@@ -1576,23 +1659,23 @@ export function batchWriteAndDelete(hippoRoot, toWrite, toDeleteIds) {
1576
1659
  // dirty for the dominant mutation source (decay, merge, garbage-collect).
1577
1660
  const dirtyParents = new Set();
1578
1661
  const tenantById = new Map();
1662
+ const deletableIds = [];
1579
1663
  if (toDeleteIds.length > 0) {
1580
1664
  const placeholders = toDeleteIds.map(() => '?').join(',');
1581
- // SAFETY: rows' shape matches the two columns named in the SELECT
1582
- // above.
1583
- const rows = db.prepare(`SELECT dag_parent_id, tenant_id FROM memories WHERE id IN (${placeholders})`).all(...toDeleteIds);
1665
+ // A row pinned after the caller decided to delete it survives.
1666
+ // SAFETY: rows' shape matches the three columns named in the SELECT.
1667
+ const rows = db.prepare(`SELECT id, dag_parent_id, tenant_id FROM memories WHERE id IN (${placeholders}) AND ${AUTO_DELETABLE_SQL}`).all(...toDeleteIds);
1584
1668
  for (const row of rows) {
1669
+ deletableIds.push(row.id);
1585
1670
  if (row.dag_parent_id) {
1586
1671
  dirtyParents.add(row.dag_parent_id);
1587
1672
  tenantById.set(row.dag_parent_id, row.tenant_id ?? 'default');
1588
1673
  }
1589
1674
  }
1590
1675
  }
1591
- // v39: consolidate's new semantic rows (and any other batch writer)
1592
- // bypass writeEntry, so stamp store-derived origins here too - a NULL
1593
- // origin would make freshly consolidated memories vanish from ambient
1594
- // context (codex gating review P1).
1595
- const stampedWrites = toWrite.map((e) => stampOriginProject(hippoRoot, e));
1676
+ // v39: batch writers bypass writeEntry, so stamp store-derived origins here too (a NULL origin hides new
1677
+ // memories from ambient context). A row queued twice keeps only its last version, the one the merge compares.
1678
+ const stampedWrites = [...new Map(toWrite.map((e) => [e.id, stampOriginProject(hippoRoot, e)])).values()];
1596
1679
  // AT1 P1 fix (codex, batch-transaction rejection race): the producer-side
1597
1680
  // check (e.g. consolidate.ts's merge pass) runs BEFORE this transaction,
1598
1681
  // on a different connection. A `hippo reject X` that commits in that
@@ -1611,9 +1694,15 @@ export function batchWriteAndDelete(hippoRoot, toWrite, toDeleteIds) {
1611
1694
  // demotion/replay re-persist of a rejected-removed row means it stays
1612
1695
  // gone, which is the entire point of the tombstone.
1613
1696
  let batchRejectedSkips = 0;
1614
- const skippedWriteIds = new Set();
1697
+ const written = [];
1698
+ const readLiveRow = db.prepare(`SELECT ${MEMORY_SELECT_COLUMNS} FROM memories WHERE id = ?`);
1615
1699
  for (const entry of stampedWrites) {
1616
- const entryTenantId = entry.tenantId ?? 'default';
1700
+ const base = opts?.snapshot?.get(entry.id);
1701
+ // SAFETY: MEMORY_SELECT_COLUMNS is the MemoryRow shape rowToEntry reads.
1702
+ const liveRow = readLiveRow.get(entry.id);
1703
+ const live = liveRow ? rowToEntry(liveRow) : undefined;
1704
+ const row = base && live ? mergeOwnChanges(base, entry, live) : entry;
1705
+ const entryTenantId = row.tenantId ?? 'default';
1617
1706
  // Codex delta-review P2 fix: reuse checkRejectionGuard rather than a
1618
1707
  // bare tombstone probe — the guard's content-INTRODUCTION
1619
1708
  // classification must apply here too. A tombstone can legitimately
@@ -1624,17 +1713,19 @@ export function batchWriteAndDelete(hippoRoot, toWrite, toDeleteIds) {
1624
1713
  // changes content TO the rejected value; unchanged same-id re-persists
1625
1714
  // pass through, exactly as on the writeEntry path.
1626
1715
  try {
1627
- checkRejectionGuard(db, entryTenantId, entry.id, entry.content);
1716
+ checkRejectionGuard(db, entryTenantId, row.id, row.content);
1628
1717
  }
1629
1718
  catch (err) {
1630
1719
  if (err instanceof RejectedValueError) {
1631
1720
  batchRejectedSkips++;
1632
- skippedWriteIds.add(entry.id);
1633
- audit(db, 'reject_refusal', entry.id, { digest: err.digest, reason: err.reason }, 'sleep-batch', entryTenantId);
1721
+ audit(db, 'reject_refusal', row.id, { digest: err.digest, reason: err.reason }, 'sleep-batch', entryTenantId);
1634
1722
  continue;
1635
1723
  }
1636
1724
  throw err;
1637
1725
  }
1726
+ if (base && !live)
1727
+ continue;
1728
+ written.push(row);
1638
1729
  // AT1 (plan §3, corrected): bypass the rejection guard here.
1639
1730
  // Consolidation merges are DETERMINISTIC CONCATENATION (mergeContents,
1640
1731
  // consolidate.ts:736-751) of already-guarded leaf facts, not an LLM
@@ -1647,14 +1738,33 @@ export function batchWriteAndDelete(hippoRoot, toWrite, toDeleteIds) {
1647
1738
  // check and this COMMIT. The guard itself still belongs on leaf
1648
1739
  // inserts, which write through writeEntry / writeEntryDbOnly and stay
1649
1740
  // guarded (bypassRejectionGuard defaults false).
1650
- upsertEntryRow(db, entry, true);
1651
- // Hook for writes: child upserted under a level-2 summary marks parent dirty.
1652
- if (entry.dag_parent_id) {
1653
- dirtyParents.add(entry.dag_parent_id);
1654
- tenantById.set(entry.dag_parent_id, entry.tenantId);
1741
+ upsertEntryRow(db, row, true);
1742
+ // Hook for writes: a new child, or a change to what its summary reads, marks the parent dirty; decay alone does not.
1743
+ if (row.dag_parent_id && (!live || SUMMARY_INPUTS.some((k) => row[k] !== live[k]))) {
1744
+ dirtyParents.add(row.dag_parent_id);
1745
+ tenantById.set(row.dag_parent_id, row.tenantId);
1655
1746
  }
1656
1747
  }
1657
- for (const id of toDeleteIds) {
1748
+ // Dormant moves: same eligibility and DAG bookkeeping as deletes.
1749
+ const movable = [];
1750
+ if (dormantMoves.length > 0) {
1751
+ const byId = new Map(dormantMoves.map((m) => [m.entry.id, m]));
1752
+ const placeholders = dormantMoves.map(() => '?').join(',');
1753
+ // SAFETY: rows' shape matches the three columns named in the SELECT.
1754
+ const rows = db.prepare(`SELECT id, dag_parent_id, tenant_id FROM memories WHERE id IN (${placeholders}) AND ${AUTO_DELETABLE_SQL}`).all(...byId.keys());
1755
+ for (const row of rows) {
1756
+ movable.push(byId.get(row.id));
1757
+ if (row.dag_parent_id) {
1758
+ dirtyParents.add(row.dag_parent_id);
1759
+ tenantById.set(row.dag_parent_id, row.tenant_id ?? 'default');
1760
+ }
1761
+ }
1762
+ }
1763
+ for (const move of movable) {
1764
+ insertDormantRow(db, move);
1765
+ }
1766
+ const removedIds = [...deletableIds, ...movable.map((m) => m.entry.id)];
1767
+ for (const id of removedIds) {
1658
1768
  db.prepare('DELETE FROM memories WHERE id = ?').run(id);
1659
1769
  deleteFtsRow(db, id);
1660
1770
  }
@@ -1670,15 +1780,14 @@ export function batchWriteAndDelete(hippoRoot, toWrite, toDeleteIds) {
1670
1780
  // Sync mirrors once after all DB writes. Entries skipped above were
1671
1781
  // never inserted — writing their markdown mirror would resurrect the
1672
1782
  // exact content the skip just kept out of the DB.
1673
- for (const entry of stampedWrites) {
1674
- if (skippedWriteIds.has(entry.id))
1675
- continue;
1676
- writeMarkdownMirror(hippoRoot, entry);
1677
- }
1678
- for (const id of toDeleteIds) {
1679
- removeEntryMirrors(hippoRoot, id);
1680
- }
1783
+ mirrorBestEffort('markdown mirrors', () => {
1784
+ for (const entry of written)
1785
+ writeMarkdownMirror(hippoRoot, entry);
1786
+ });
1787
+ for (const id of removedIds)
1788
+ purgeMirrorBestEffort(hippoRoot, id, false, 'batchWriteAndDelete');
1681
1789
  writeIndexMirror(hippoRoot, buildIndexFromDb(db));
1790
+ return removedIds;
1682
1791
  }
1683
1792
  catch (error) {
1684
1793
  try {
@@ -1702,17 +1811,21 @@ export function loadAllEntries(hippoRoot, tenantId) {
1702
1811
  initStore(hippoRoot);
1703
1812
  const db = openHippoDb(hippoRoot);
1704
1813
  try {
1705
- // SAFETY: both branches select exactly MEMORY_SELECT_COLUMNS, matching
1706
- // MemoryRow's field set.
1707
- const rows = tenantId !== undefined
1708
- ? db.prepare(`SELECT ${MEMORY_SELECT_COLUMNS} FROM memories WHERE tenant_id = ? ORDER BY created ASC, id ASC`).all(tenantId)
1709
- : db.prepare(`SELECT ${MEMORY_SELECT_COLUMNS} FROM memories ORDER BY created ASC, id ASC`).all();
1710
- return rows.map(rowToEntry);
1814
+ return selectAllEntries(db, tenantId);
1711
1815
  }
1712
1816
  finally {
1713
1817
  closeHippoDb(db);
1714
1818
  }
1715
1819
  }
1820
+ /** Every memory row on an open connection, so a caller can read inside its own transaction. */
1821
+ export function selectAllEntries(db, tenantId) {
1822
+ // SAFETY: both branches select exactly MEMORY_SELECT_COLUMNS, matching
1823
+ // MemoryRow's field set.
1824
+ const rows = tenantId !== undefined
1825
+ ? db.prepare(`SELECT ${MEMORY_SELECT_COLUMNS} FROM memories WHERE tenant_id = ? ORDER BY created ASC, id ASC`).all(tenantId)
1826
+ : db.prepare(`SELECT ${MEMORY_SELECT_COLUMNS} FROM memories ORDER BY created ASC, id ASC`).all();
1827
+ return rows.map(rowToEntry);
1828
+ }
1716
1829
  // The pins plus the `recentNeeded` newest rows that pass `admit`, for ambient
1717
1830
  // injection. One connection: opening one costs ~5.8ms on a warm 1896-row store,
1718
1831
  // so a second handle loses more than the narrower scan saves.
@@ -1799,7 +1912,7 @@ export function loadSearchEntries(hippoRoot, query, limit = DEFAULT_SEARCH_CANDI
1799
1912
  * mode (its `tenantId` option is optional); `loadSearchRows` already treats
1800
1913
  * undefined as "no tenant filter" for legacy callers.
1801
1914
  */
1802
- export function loadRecallSearchEntries(hippoRoot, query, limit = DEFAULT_SEARCH_CANDIDATE_LIMIT, tenantId, requestedScope, explicitScopeMode = 'exact') {
1915
+ export function loadRecallSearchEntries(hippoRoot, query, limit = DEFAULT_SEARCH_CANDIDATE_LIMIT, tenantId, requestedScope, explicitScopeMode = 'exact', includeSuperseded = true) {
1803
1916
  initStore(hippoRoot);
1804
1917
  const db = openHippoDb(hippoRoot);
1805
1918
  try {
@@ -1812,7 +1925,7 @@ export function loadRecallSearchEntries(hippoRoot, query, limit = DEFAULT_SEARCH
1812
1925
  ? { mode: 'default-deny-or-exact', value: requestedScope }
1813
1926
  : { mode: 'exact', value: requestedScope }
1814
1927
  : { mode: 'default-deny' };
1815
- return loadSearchRows(db, query, limit, tenantId, scopeFilter).map(rowToEntry);
1928
+ return loadSearchRows(db, query, limit, tenantId, scopeFilter, includeSuperseded).map(rowToEntry);
1816
1929
  }
1817
1930
  finally {
1818
1931
  closeHippoDb(db);
@@ -1903,9 +2016,7 @@ export function loadStats(hippoRoot) {
1903
2016
  initStore(hippoRoot);
1904
2017
  const db = openHippoDb(hippoRoot);
1905
2018
  try {
1906
- const stats = buildStatsFromDb(db);
1907
- writeStatsMirror(hippoRoot, stats);
1908
- return stats;
2019
+ return buildStatsFromDb(db);
1909
2020
  }
1910
2021
  finally {
1911
2022
  closeHippoDb(db);
@@ -1923,6 +2034,20 @@ export function appendConsolidationRun(hippoRoot, run) {
1923
2034
  closeHippoDb(db);
1924
2035
  }
1925
2036
  }
2037
+ /** Rows a tenant created since the last sleep (runs are host-wide), looking back at most 24 hours. */
2038
+ export function countCreatedSinceLastSleep(hippoRoot, tenantId, now = new Date()) {
2039
+ initStore(hippoRoot);
2040
+ const db = openHippoDb(hippoRoot);
2041
+ try {
2042
+ const dayAgo = new Date(now.getTime() - 86_400_000).toISOString();
2043
+ const row = db.prepare(`SELECT COUNT(*) AS n FROM memories WHERE tenant_id = ?
2044
+ AND created > MAX(?, COALESCE((SELECT MAX(timestamp) FROM consolidation_runs), ''))`).get(tenantId, dayAgo);
2045
+ return row.n;
2046
+ }
2047
+ finally {
2048
+ closeHippoDb(db);
2049
+ }
2050
+ }
1926
2051
  /**
1927
2052
  * Load the session decay context from the store.
1928
2053
  * Uses consolidation_runs timestamps to compute session intervals.
@@ -3492,6 +3617,7 @@ export function loadAllL2Summaries(hippoRoot) {
3492
3617
  WHERE dag_level = 2
3493
3618
  AND dag_parent_id IS NULL
3494
3619
  AND kind != 'archived'
3620
+ AND superseded_by IS NULL
3495
3621
  ORDER BY created ASC, id ASC
3496
3622
  `).all();
3497
3623
  return rows.map(rowToEntry);
@@ -3548,6 +3674,7 @@ export function loadChildrenOfSummary(hippoRoot, summaryId, tenantId) {
3548
3674
  WHERE dag_parent_id = ?
3549
3675
  AND tenant_id = ?
3550
3676
  AND kind != 'archived'
3677
+ AND superseded_by IS NULL
3551
3678
  ORDER BY created ASC
3552
3679
  `).all(summaryId, tenantId);
3553
3680
  return rows.map(rowToEntry);
@@ -0,0 +1,119 @@
1
+ import type { DatabaseSyncLike } from './db.js';
2
+ /**
3
+ * Where a block of memory text was sent.
4
+ * - `hook`: the per-prompt `UserPromptSubmit` hook (`hippo context --pinned-only`).
5
+ * - `context`, `recall`: the CLI commands.
6
+ * - `mcp_recall`, `mcp_context`: the MCP tools.
7
+ * - `http_recall`, `http_context`, `http_assemble`: the HTTP API.
8
+ */
9
+ export type TokenSurface = 'hook' | 'context' | 'recall' | 'mcp_recall' | 'mcp_context' | 'http_recall' | 'http_context' | 'http_assemble';
10
+ /** All surfaces, in report order. */
11
+ export declare const TOKEN_SURFACES: readonly TokenSurface[];
12
+ /**
13
+ * What happened to a block.
14
+ * - `inject`: sent to the agent.
15
+ * - `skip`: identical to the session's last injected block, so not sent again.
16
+ * - `reset`: the host compacted its context, so the next block must be sent.
17
+ */
18
+ export type TokenEvent = 'inject' | 'skip' | 'reset';
19
+ /** Rows older than this are pruned on write. */
20
+ export declare const TOKEN_LEDGER_RETENTION_DAYS = 90;
21
+ /**
22
+ * Rough token estimate: characters / 4. The single estimate behind every
23
+ * token budget and ledger count in hippo.
24
+ */
25
+ export declare function estimateTokens(text: string): number;
26
+ /** Stable 16-hex-char hash of a rendered block, for change detection. */
27
+ export declare function blockHash(text: string): string;
28
+ /** One ledger write. */
29
+ export interface TokenUse {
30
+ tenantId: string;
31
+ /** Host session id when known (hook payload, `HIPPO_SESSION_ID`); null otherwise. */
32
+ sessionId?: string | null;
33
+ surface: TokenSurface;
34
+ event: TokenEvent;
35
+ /** Memories (and continuity blocks) in the text. */
36
+ items: number;
37
+ /** Estimated tokens of the text; for a `skip`, the tokens not sent. */
38
+ tokens: number;
39
+ /** {@link blockHash} of the text, when the caller wants change detection. */
40
+ hash?: string | null;
41
+ /** Override the timestamp (tests). ISO string. */
42
+ now?: string;
43
+ }
44
+ /**
45
+ * Append one ledger row and prune rows past {@link TOKEN_LEDGER_RETENTION_DAYS}.
46
+ */
47
+ export declare function recordTokenUse(db: DatabaseSyncLike, use: TokenUse): void;
48
+ /** What a session last sent on a surface, for {@link lastSentState}. */
49
+ export interface LastSent {
50
+ /** {@link blockHash} of the last injected block. */
51
+ hash: string;
52
+ /** Consecutive `skip` rows since that injection. */
53
+ skipsSince: number;
54
+ }
55
+ /**
56
+ * The last block this session injected on `surface`, or null when the
57
+ * session has injected nothing, a `reset` came after it, or there is no
58
+ * session id (without one, the caller always injects).
59
+ */
60
+ export declare function lastSentState(db: DatabaseSyncLike, tenantId: string, sessionId: string | null | undefined, surface: TokenSurface): LastSent | null;
61
+ /**
62
+ * Whether a block identical to the session's last injection should be
63
+ * skipped. `refreshTurns` resends an unchanged block after that many
64
+ * consecutive skips, so a long session still sees its pinned rules near the
65
+ * latest turn; 0 never resends an unchanged block.
66
+ */
67
+ export declare function shouldSkipUnchanged(last: LastSent | null, hash: string, refreshTurns: number): boolean;
68
+ /** Per-surface totals for {@link summarizeTokenUse}. */
69
+ export interface TokenSurfaceSummary {
70
+ surface: TokenSurface;
71
+ /** Blocks sent. */
72
+ injected: number;
73
+ /** Tokens sent. */
74
+ tokens: number;
75
+ /** Blocks not sent because they were unchanged. */
76
+ skipped: number;
77
+ /** Tokens those skipped blocks would have cost. */
78
+ tokensAvoided: number;
79
+ /** Distinct session ids seen (rows without one are not counted). */
80
+ sessions: number;
81
+ }
82
+ /** Ledger totals over a window. */
83
+ export interface TokenSummary {
84
+ /** ISO start of the window (inclusive). */
85
+ since: string;
86
+ surfaces: TokenSurfaceSummary[];
87
+ totalTokens: number;
88
+ totalTokensAvoided: number;
89
+ /** Mean tokens sent per session, over rows that carry a session id. */
90
+ meanTokensPerSession: number;
91
+ }
92
+ /**
93
+ * Sum the ledger for one tenant since `sinceIso`. Surfaces with no rows are
94
+ * omitted.
95
+ */
96
+ export declare function summarizeTokenUse(db: DatabaseSyncLike, tenantId: string, sinceIso: string): TokenSummary;
97
+ /**
98
+ * The `session_id` of a Claude Code hook payload on stdin, or null when the
99
+ * text is empty, malformed, has no non-empty session id, or (with
100
+ * `requiredSource`) a different `source`.
101
+ */
102
+ export declare function hookPayloadSessionId(stdinText: string | undefined, requiredSource?: string | null): string | null;
103
+ /** Tokens hippo sent and skipped in one session, for {@link tokensBySession}. */
104
+ export interface SessionTokens {
105
+ sessionId: string;
106
+ /** Tokens of memory text sent to the agent. */
107
+ sent: number;
108
+ /** Tokens of unchanged blocks not sent. */
109
+ skipped: number;
110
+ /** Blocks sent. */
111
+ injections: number;
112
+ }
113
+ /**
114
+ * Ledger totals per session id since `sinceIso`, across every surface.
115
+ * Claude Code hook rows carry the host's session id, which is also the
116
+ * transcript file name, so these join to the host's own usage records.
117
+ */
118
+ export declare function tokensBySession(db: DatabaseSyncLike, tenantId: string, sinceIso: string): SessionTokens[];
119
+ //# sourceMappingURL=token-ledger.d.ts.map