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
@@ -7,12 +7,14 @@
7
7
  * 3. Stats tracking
8
8
  */
9
9
  import { evalNow, isRecallBoostAblated } from './ablation.js';
10
- import { Layer, calculateStrength, createMemory } from './memory.js';
10
+ import { Layer, calculateStrength, canAutoDelete, createMemory } from './memory.js';
11
11
  import { loadAllEntries, batchWriteAndDelete, appendConsolidationRun, replaceDetectedConflicts, loadSessionDecayContext, incrementSleepCount, findPromotableSessions, traceExistsForSession, listSessionEvents, } from './store.js';
12
12
  import { textOverlap, markRetrieved } from './search.js';
13
13
  import { compareEntryIdentity } from './compare.js';
14
14
  import { openHippoDb, closeHippoDb } from './db.js';
15
15
  import { rejectionDigest, findRejectedValue } from './rejection.js';
16
+ import { countExpiredDormant, purgeExpiredDormant } from './dormant.js';
17
+ import { detectSecret } from './secret-detect.js';
16
18
  import { loadPhysicsState, savePhysicsState, refreshParticleProperties } from './physics-state.js';
17
19
  import { simulate } from './physics.js';
18
20
  import { loadConfig } from './config.js';
@@ -22,6 +24,7 @@ import { resolveTenantId } from './tenant.js';
22
24
  import { rescueSet, rankNonPinnedByTenant, validateWeights } from './memory-value.js';
23
25
  import { MEMORY_VALUE_WEIGHTS, SOURCE_ARTIFACT_SHA256 } from './memory-value-weights.js';
24
26
  import { appendAuditEvent } from './audit.js';
27
+ import { migrateDefaultHalfLife } from './half-life-migration.js';
25
28
  const DECAY_THRESHOLD = 0.05;
26
29
  const MERGE_OVERLAP_THRESHOLD = 0.35; // Jaccard similarity for "related"
27
30
  const MERGE_MIN_CLUSTER = 2; // minimum cluster size to merge
@@ -59,6 +62,37 @@ const REPLAY_COUNT_DEFAULT = 5;
59
62
  function isJsonString(value) {
60
63
  return typeof value === 'string';
61
64
  }
65
+ /** Tables whose rows keep a first-class object's backing memory in `memory_id` (ON DELETE SET NULL). */
66
+ const MEMORY_BACKED_TABLES = ['predictions', 'decisions', 'processes', 'policies', 'skills', 'project_briefs', 'customer_notes'];
67
+ /**
68
+ * Ids of memories that back a first-class object (a decision, prediction,
69
+ * process, policy, skill, project brief or customer note). Sleep never
70
+ * retires these: deleting or moving one to dormant storage fires the
71
+ * object's ON DELETE SET NULL and a restore cannot repair the link. Their
72
+ * lifecycle belongs to the object. A table missing from an older schema is
73
+ * skipped.
74
+ */
75
+ function memoriesBackingObjects(hippoRoot) {
76
+ const ids = new Set();
77
+ const db = openHippoDb(hippoRoot);
78
+ try {
79
+ for (const table of MEMORY_BACKED_TABLES) {
80
+ try {
81
+ // SAFETY: SELECT of one nullable TEXT column, filtered to non-null.
82
+ const rows = db.prepare(`SELECT memory_id FROM ${table} WHERE memory_id IS NOT NULL`).all();
83
+ for (const r of rows)
84
+ ids.add(r.memory_id);
85
+ }
86
+ catch {
87
+ // Table not present in this schema version.
88
+ }
89
+ }
90
+ }
91
+ finally {
92
+ closeHippoDb(db);
93
+ }
94
+ return ids;
95
+ }
62
96
  /**
63
97
  * Run a full consolidation pass.
64
98
  */
@@ -68,6 +102,8 @@ export async function consolidate(hippoRoot, options = {}) {
68
102
  const result = {
69
103
  decayed: 0,
70
104
  removed: 0,
105
+ dormant: 0,
106
+ dormantExpired: 0,
71
107
  merged: 0,
72
108
  semanticCreated: 0,
73
109
  replayed: 0,
@@ -86,11 +122,24 @@ export async function consolidate(hippoRoot, options = {}) {
86
122
  details: [],
87
123
  physicsSimulated: 0,
88
124
  };
125
+ // A changed default half-life moves memories still on the old base first,
126
+ // so this pass decays them at the new one (src/half-life-migration.ts).
127
+ const halfLife = migrateDefaultHalfLife(hippoRoot, loadConfig(hippoRoot).defaultHalfLifeDays, { dryRun });
128
+ if (halfLife.rescaled > 0) {
129
+ result.details.push(` ⏳ ${dryRun ? 'would move' : 'moved'} ${halfLife.rescaled} memories from the ${halfLife.from}-day to the ${halfLife.to}-day half-life`);
130
+ }
89
131
  // L9: host-wide by design. Consolidation runs across all tenants in one
90
132
  // pass — per-tenant filtering would create N consolidation runs per host
91
133
  // with no cross-tenant dedup. The api.sleep audit row tags this with the
92
134
  // admin synthetic actor; see api.ts:2050 for the rationale.
93
135
  const all = loadAllEntries(hippoRoot);
136
+ if (dryRun)
137
+ for (const e of all)
138
+ e.half_life_days = halfLife.halfLives.get(e.id) ?? e.half_life_days;
139
+ const backingObjects = memoriesBackingObjects(hippoRoot);
140
+ // Retirable: auto-deletable (never pinned, never raw) and not backing a first-class object.
141
+ const retirable = (entry) => canAutoDelete(entry) && !backingObjects.has(entry.id);
142
+ const snapshot = new Map(structuredClone(all).map((e) => [e.id, e]));
94
143
  // Load decay options from config + session context
95
144
  const config = loadConfig(hippoRoot);
96
145
  const sessionCtx = loadSessionDecayContext(hippoRoot);
@@ -102,6 +151,28 @@ export async function consolidate(hippoRoot, options = {}) {
102
151
  // Collect all writes/deletes and batch them at the end
103
152
  const pendingWrites = [];
104
153
  const pendingDeletes = [];
154
+ const pendingDormant = [];
155
+ // A faded, unpinned, unrescued memory leaves active memory one of three
156
+ // ways. A raw receipt is append-only: trg_memories_raw_append_only aborts
157
+ // a DELETE, and with it this whole cycle's batch and every later sleep,
158
+ // so it stays where it is (stored strength refreshed) but sits out the
159
+ // rest of this cycle the way a deleted row would. Anything else goes
160
+ // dormant when config.dormant is on, and is deleted otherwise.
161
+ // Only called for rows `retirable` allows (never pinned, never raw, never backing a first-class object).
162
+ const retireFaded = (entry, strength) => {
163
+ const why = `(strength ${strength.toFixed(4)} < ${DECAY_THRESHOLD})`;
164
+ // A faded secret is deleted, never kept dormant: keeping it would hold a
165
+ // credential on disk that the user reasonably expects forgetting removed.
166
+ if (config.dormant.enabled && !detectSecret(entry).flagged) {
167
+ result.dormant++;
168
+ result.details.push(` 💤 dormant ${entry.id} ${why}`);
169
+ pendingDormant.push({ entry: { ...entry, strength }, strength, reason: 'decay', dormantAt: now.toISOString() });
170
+ return;
171
+ }
172
+ result.removed++;
173
+ result.details.push(` 🗑 removed ${entry.id} ${why}`);
174
+ pendingDeletes.push(entry.id);
175
+ };
105
176
  // -------------------------------------------------------------------------
106
177
  // 1. Decay pass
107
178
  // -------------------------------------------------------------------------
@@ -131,12 +202,12 @@ export async function consolidate(hippoRoot, options = {}) {
131
202
  for (const entry of all) {
132
203
  const strength = calculateStrength(entry, now, decayOpts);
133
204
  strengthById.set(entry.id, strength);
134
- if (!entry.pinned && strength < DECAY_THRESHOLD) {
205
+ if (retirable(entry) && strength < DECAY_THRESHOLD) {
135
206
  condemned.push(entry);
136
207
  }
137
208
  }
138
209
  // --- Phase 2a: rescue decision (pure compute) ---
139
- // Runs under --dry-run too (only pendingDeletes/the audit write in
210
+ // Runs under --dry-run too (only the pendingDeletes flush and the audit write in
140
211
  // "4. Log run" below stay !dryRun-gated), so the preview matches what a
141
212
  // real run would decide.
142
213
  const condemnedIds = new Set(condemned.map((e) => e.id));
@@ -173,7 +244,7 @@ export async function consolidate(hippoRoot, options = {}) {
173
244
  // preserves flag-off's ordering semantics exactly.)
174
245
  for (const entry of all) {
175
246
  const strength = strengthById.get(entry.id);
176
- if (!entry.pinned && strength < DECAY_THRESHOLD) {
247
+ if (retirable(entry) && strength < DECAY_THRESHOLD) {
177
248
  if (rescuedIds.has(entry.id)) {
178
249
  // Rescued (D1): standard survivor stored-strength refresh (P2-1).
179
250
  // Confidence is left alone here: it is an epistemic tier, not a
@@ -192,11 +263,7 @@ export async function consolidate(hippoRoot, options = {}) {
192
263
  result.details.push(` 🛟 ${entry.id} (strength ${strength.toFixed(4)} < ${DECAY_THRESHOLD})${rankNote}`);
193
264
  }
194
265
  else {
195
- result.removed++;
196
- result.details.push(` 🗑 removed ${entry.id} (strength ${strength.toFixed(4)} < ${DECAY_THRESHOLD})`);
197
- if (!dryRun) {
198
- pendingDeletes.push(entry.id);
199
- }
266
+ retireFaded(entry, strength);
200
267
  }
201
268
  }
202
269
  else {
@@ -212,12 +279,8 @@ export async function consolidate(hippoRoot, options = {}) {
212
279
  else {
213
280
  for (const entry of all) {
214
281
  const strength = calculateStrength(entry, now, decayOpts);
215
- if (!entry.pinned && strength < DECAY_THRESHOLD) {
216
- result.removed++;
217
- result.details.push(` 🗑 removed ${entry.id} (strength ${strength.toFixed(4)} < ${DECAY_THRESHOLD})`);
218
- if (!dryRun) {
219
- pendingDeletes.push(entry.id);
220
- }
282
+ if (retirable(entry) && strength < DECAY_THRESHOLD) {
283
+ retireFaded(entry, strength);
221
284
  }
222
285
  else {
223
286
  // Only strength is a cached computation; confidence stays as stored.
@@ -423,26 +486,34 @@ export async function consolidate(hippoRoot, options = {}) {
423
486
  // 1.6. Batch extraction — extract facts from episodic memories missing them
424
487
  // -------------------------------------------------------------------------
425
488
  const extractedFromIds = new Set(survivors.filter((e) => e.extracted_from).map((e) => e.extracted_from));
426
- const extractionCandidates = survivors.filter((e) => e.layer === Layer.Episodic && !extractedFromIds.has(e.id));
489
+ const extractionCandidates = survivors.filter((e) => e.layer === Layer.Episodic && !e.superseded_by && !extractedFromIds.has(e.id));
427
490
  result.extractionCandidates = extractionCandidates.length;
428
- const apiKey = process.env.ANTHROPIC_API_KEY ?? '';
491
+ // extraction.enabled=false is the opt-out for every LLM phase below, key or no key.
492
+ const apiKey = config.extraction.enabled !== false ? (process.env.ANTHROPIC_API_KEY ?? '') : '';
493
+ const llmErrorsSeen = new Set();
494
+ const llmError = (phase) => (msg) => {
495
+ const line = ` ⚠️ ${phase}: ${msg}`;
496
+ if (llmErrorsSeen.has(line))
497
+ return;
498
+ llmErrorsSeen.add(line);
499
+ result.details.push(line);
500
+ console.error(`consolidate ${phase}: ${msg}`);
501
+ };
502
+ const llmOpts = { apiKey, model: config.extraction.model, fetcher: options.fetcher };
429
503
  if (apiKey && extractionCandidates.length > 0 && !dryRun) {
430
504
  const { extractFacts, storeExtractedFacts } = await import('./extract.js');
431
505
  const batchLimit = 20;
432
506
  let extractedCount = 0;
433
507
  for (const candidate of extractionCandidates.slice(0, batchLimit)) {
434
508
  try {
435
- const facts = await extractFacts(candidate.content, {
436
- apiKey,
437
- model: config.extraction.model,
438
- });
509
+ const facts = await extractFacts(candidate.content, { ...llmOpts, onError: llmError('extraction') });
439
510
  if (facts.length > 0) {
440
511
  storeExtractedFacts(hippoRoot, candidate, facts);
441
512
  extractedCount += facts.length;
442
513
  }
443
514
  }
444
- catch {
445
- // Best-effort — continue with next candidate
515
+ catch (err) {
516
+ llmError('extraction')(String(err));
446
517
  }
447
518
  }
448
519
  result.extracted = extractedCount;
@@ -450,22 +521,19 @@ export async function consolidate(hippoRoot, options = {}) {
450
521
  // -------------------------------------------------------------------------
451
522
  // 1.7. DAG summarization — cluster extracted facts and generate summaries
452
523
  // -------------------------------------------------------------------------
453
- const extractedFacts = survivors.filter((e) => e.tags.includes('extracted') && e.dag_level === 1);
524
+ const extractedFacts = survivors.filter((e) => e.tags.includes('extracted') && e.dag_level === 1 && !e.superseded_by);
454
525
  if (apiKey && extractedFacts.length >= 3 && !dryRun) {
455
526
  try {
456
527
  const { buildDag } = await import('./dag.js');
457
- const dagResult = await buildDag(hippoRoot, extractedFacts, {
458
- apiKey,
459
- model: config.extraction.model,
460
- });
528
+ const dagResult = await buildDag(hippoRoot, extractedFacts, { ...llmOpts, onError: llmError('dag') });
461
529
  result.dagCandidateClusters = dagResult.candidateClusters;
462
530
  result.dagSummariesCreated = dagResult.summariesCreated;
463
531
  if (dagResult.summariesCreated > 0) {
464
532
  result.details.push(` 🌳 DAG: ${dagResult.summariesCreated} summaries created, ${dagResult.factsLinked} facts linked`);
465
533
  }
466
534
  }
467
- catch {
468
- // Best-effort
535
+ catch (err) {
536
+ llmError('dag')(String(err));
469
537
  }
470
538
  }
471
539
  // -------------------------------------------------------------------------
@@ -485,11 +553,7 @@ export async function consolidate(hippoRoot, options = {}) {
485
553
  const cap = Number.isFinite(rawCap) && rawCap > 0
486
554
  ? Math.min(rawCap, 1000)
487
555
  : 20;
488
- const rebuildResult = await rebuildDirtySummaries(hippoRoot, {
489
- apiKey,
490
- model: config.extraction.model,
491
- cap,
492
- });
556
+ const rebuildResult = await rebuildDirtySummaries(hippoRoot, { ...llmOpts, onError: llmError('dag rebuild'), cap });
493
557
  result.summariesRebuilt = rebuildResult.rebuilt;
494
558
  result.summariesRebuildFailed = rebuildResult.failed;
495
559
  result.summariesZeroChildSkipped = rebuildResult.zeroChildSkipped;
@@ -510,8 +574,8 @@ export async function consolidate(hippoRoot, options = {}) {
510
574
  result.details.push(` 🌳 DAG rebuild: ${parts.join(', ')}`);
511
575
  }
512
576
  }
513
- catch {
514
- // Best-effort — same posture as buildDag block above.
577
+ catch (err) {
578
+ llmError('dag rebuild')(String(err));
515
579
  }
516
580
  }
517
581
  // -------------------------------------------------------------------------
@@ -529,18 +593,15 @@ export async function consolidate(hippoRoot, options = {}) {
529
593
  const { loadAllL2Summaries } = await import('./store.js');
530
594
  const l2Summaries = loadAllL2Summaries(hippoRoot);
531
595
  if (l2Summaries.length >= 2) {
532
- const profileResult = await buildEntityProfiles(hippoRoot, l2Summaries, {
533
- apiKey,
534
- model: config.extraction.model,
535
- });
596
+ const profileResult = await buildEntityProfiles(hippoRoot, l2Summaries, { ...llmOpts, onError: llmError('dag profiles') });
536
597
  result.entityProfilesCreated = profileResult.profilesCreated;
537
598
  if (profileResult.profilesCreated > 0) {
538
599
  result.details.push(` 🌲 DAG L3: ${profileResult.profilesCreated} entity profiles, ${profileResult.l2sLinked} L2s linked`);
539
600
  }
540
601
  }
541
602
  }
542
- catch {
543
- // Best-effort.
603
+ catch (err) {
604
+ llmError('dag profiles')(String(err));
544
605
  }
545
606
  }
546
607
  // -------------------------------------------------------------------------
@@ -599,7 +660,7 @@ export async function consolidate(hippoRoot, options = {}) {
599
660
  // -------------------------------------------------------------------------
600
661
  // 3. Merge pass - episodic entries only
601
662
  // -------------------------------------------------------------------------
602
- const mergeCandidates = survivors.filter((e) => e.layer === Layer.Episodic && !e.tags.includes('extracted'));
663
+ const mergeCandidates = survivors.filter((e) => e.layer === Layer.Episodic && !e.superseded_by && !e.tags.includes('extracted'));
603
664
  const used = new Set();
604
665
  // T1 fix (2026-08-15 hardening pass): partition by tenantId BEFORE the
605
666
  // overlap loop so a cluster can never span tenants. Previously textOverlap
@@ -735,9 +796,35 @@ export async function consolidate(hippoRoot, options = {}) {
735
796
  if (mergesSkippedRejected > 0) {
736
797
  console.error(`consolidate: skipped ${mergesSkippedRejected} merge(s) whose content matches a rejected value`);
737
798
  }
738
- // Flush all writes/deletes in a single transaction
799
+ result.removedIds = pendingDeletes;
800
+ // One transaction; the snapshot keeps what the DAG passes and other writers changed while sleep ran.
801
+ // Dormant moves ride in the same transaction (src/dormant.ts).
739
802
  if (!dryRun) {
740
- batchWriteAndDelete(hippoRoot, pendingWrites, pendingDeletes);
803
+ const left = new Set(batchWriteAndDelete(hippoRoot, pendingWrites, pendingDeletes, { snapshot, dormant: pendingDormant }));
804
+ for (const id of [...pendingDeletes, ...pendingDormant.map((m) => m.entry.id)]) {
805
+ if (!left.has(id))
806
+ result.details.push(` ↩ ${id} not removed: pinned or already gone before sleep saved`);
807
+ }
808
+ result.removedIds = pendingDeletes.filter((id) => left.has(id));
809
+ result.removed = result.removedIds.length;
810
+ result.dormant = pendingDormant.filter((m) => left.has(m.entry.id)).length;
811
+ }
812
+ // Dormant retention: a dormant memory nobody restored within
813
+ // dormant.retentionDays is deleted for good (0 keeps them forever). Runs
814
+ // even when dormant.enabled is off, so turning it off still ages out what
815
+ // earlier sleeps kept.
816
+ if (config.dormant.retentionDays > 0) {
817
+ const cutoff = new Date(now.getTime() - config.dormant.retentionDays * 24 * 60 * 60 * 1000).toISOString();
818
+ const db = openHippoDb(hippoRoot);
819
+ try {
820
+ result.dormantExpired = dryRun ? countExpiredDormant(db, cutoff) : purgeExpiredDormant(db, cutoff);
821
+ }
822
+ finally {
823
+ closeHippoDb(db);
824
+ }
825
+ if (result.dormantExpired > 0) {
826
+ result.details.push(` ⌛ ${dryRun ? 'would expire' : 'expired'} ${result.dormantExpired} dormant memor${result.dormantExpired === 1 ? 'y' : 'ies'} older than ${config.dormant.retentionDays} days`);
827
+ }
741
828
  }
742
829
  // -------------------------------------------------------------------------
743
830
  // 4. Log run
package/dist/dag.d.ts CHANGED
@@ -9,6 +9,7 @@ export interface DagSummaryOptions {
9
9
  apiKey: string;
10
10
  model?: string;
11
11
  fetcher?: typeof fetch;
12
+ onError?: (msg: string) => void;
12
13
  }
13
14
  export declare function generateDagSummary(label: string, factContents: string[], opts: DagSummaryOptions): Promise<string | null>;
14
15
  export interface DagBuildResult {
package/dist/dag.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { createMemory, Layer } from './memory.js';
2
2
  import { writeEntry, loadAllDirtySummaries, loadChildrenOfSummary, applyRebuildResult, clearSummaryDirtyAfterBuild, } from './store.js';
3
3
  import { RejectedValueError } from './rejection.js';
4
+ import { redactSecrets } from './secret-detect.js';
4
5
  export function clusterFacts(facts) {
5
6
  if (facts.length === 0)
6
7
  return [];
@@ -42,9 +43,9 @@ Write a single concise paragraph (2-4 sentences) that captures all the key infor
42
43
  export async function generateDagSummary(label, factContents, opts) {
43
44
  const model = opts.model ?? 'claude-sonnet-4-6';
44
45
  const fetchFn = opts.fetcher ?? fetch;
45
- const factsBlock = factContents.map((f, i) => `${i + 1}. ${f}`).join('\n');
46
+ const factsBlock = factContents.map((f, i) => `${i + 1}. ${redactSecrets(f)}`).join('\n');
46
47
  const prompt = DAG_SUMMARY_PROMPT
47
- .replace('{label}', label)
48
+ .replace('{label}', redactSecrets(label))
48
49
  .replace('{facts}', factsBlock);
49
50
  let res;
50
51
  try {
@@ -62,17 +63,21 @@ export async function generateDagSummary(label, factContents, opts) {
62
63
  }),
63
64
  });
64
65
  }
65
- catch {
66
+ catch (err) {
67
+ opts.onError?.(`request failed: ${err instanceof Error ? err.message : String(err)}`);
66
68
  return null;
67
69
  }
68
- if (!res.ok)
70
+ if (!res.ok) {
71
+ opts.onError?.(`HTTP ${res.status}`);
69
72
  return null;
73
+ }
70
74
  try {
71
75
  const data = await res.json();
72
76
  const text = data.content?.[0]?.text?.trim() ?? '';
73
77
  return text.length >= 20 ? text : null;
74
78
  }
75
- catch {
79
+ catch (err) {
80
+ opts.onError?.(`unparseable response: ${err instanceof Error ? err.message : String(err)}`);
76
81
  return null;
77
82
  }
78
83
  }
package/dist/dashboard.js CHANGED
@@ -14,6 +14,7 @@ import { listPeers } from './shared.js';
14
14
  import { loadEmbeddingIndex } from './embeddings.js';
15
15
  import { resolveTenantId } from './tenant.js';
16
16
  import { loadCardDetail } from './card-detail.js';
17
+ import { isCrossSite, LOOPBACK_HOST_HEADER } from './server.js';
17
18
  function buildDashboardData(hippoRoot) {
18
19
  // A5: scope dashboard to active tenant. Without this, the UI surfaces every
19
20
  // tenant's memories on a multi-tenant deployment.
@@ -122,8 +123,6 @@ const MIME_TYPES = {
122
123
  '.ico': 'image/x-icon',
123
124
  '.woff2': 'font/woff2',
124
125
  };
125
- // Binds 127.0.0.1 only; refusing other Hosts closes the DNS-rebinding route.
126
- const LOOPBACK_HOST = /^(localhost|127\.0\.0\.1)(:\d+)?$/i;
127
126
  function isStaticFileExtension(ext) {
128
127
  return Object.hasOwn(MIME_TYPES, ext);
129
128
  }
@@ -156,7 +155,7 @@ export function serveDashboard(hippoRoot, port = 3333) {
156
155
  const hasDistUi = fs.existsSync(path.join(distUiDir, 'index.html'));
157
156
  const handleRequest = (req, res) => {
158
157
  const host = req.headers.host;
159
- if (host !== undefined && !LOOPBACK_HOST.test(host)) {
158
+ if (host !== undefined && !LOOPBACK_HOST_HEADER.test(host)) {
160
159
  res.writeHead(403, { 'Content-Type': 'text/plain' });
161
160
  res.end('Forbidden');
162
161
  return;
@@ -168,6 +167,11 @@ export function serveDashboard(hippoRoot, port = 3333) {
168
167
  // POST /api/star/:id - toggle starred on a memory
169
168
  const starMatch = pathname.match(/^\/api\/star\/([A-Za-z0-9_\-]+)$/);
170
169
  if (starMatch && req.method === 'POST') {
170
+ if (isCrossSite(req)) {
171
+ res.writeHead(403, { 'Content-Type': 'text/plain' });
172
+ res.end('Forbidden');
173
+ return;
174
+ }
171
175
  const id = starMatch[1];
172
176
  const entry = readEntry(hippoRoot, id, resolveTenantId({}));
173
177
  if (!entry)
package/dist/db.js CHANGED
@@ -11,7 +11,7 @@ const require = createRequire(import.meta.url);
11
11
  // runtime (Node's built-in synchronous SQLite module); there are no bundled
12
12
  // types for it here, so this require + cast is the module's documented boundary.
13
13
  const { DatabaseSync } = require('node:sqlite');
14
- const CURRENT_SCHEMA_VERSION = 43;
14
+ const CURRENT_SCHEMA_VERSION = 45;
15
15
  const MIGRATIONS = [
16
16
  {
17
17
  version: 1,
@@ -2367,6 +2367,62 @@ const MIGRATIONS = [
2367
2367
  `);
2368
2368
  },
2369
2369
  },
2370
+ {
2371
+ version: 44,
2372
+ up: (db) => {
2373
+ // Dormant memories (src/dormant.ts): with `dormant.enabled`, the sleep
2374
+ // decay pass moves a faded memory here instead of deleting it. The row
2375
+ // leaves `memories` in the same transaction, so recall, context and
2376
+ // every sleep pass stop seeing it; entry_json is the full MemoryEntry
2377
+ // snapshot `hippo dormant restore` writes back. No FK to memories (the
2378
+ // memories row is gone by design). Additive only, v41 precedent: no
2379
+ // min_compatible_binary bump. An older binary ignores the table and
2380
+ // keeps deleting faded memories as it always did.
2381
+ db.exec(`
2382
+ CREATE TABLE IF NOT EXISTS dormant_memories (
2383
+ tenant_id TEXT NOT NULL DEFAULT 'default',
2384
+ id TEXT NOT NULL,
2385
+ content TEXT NOT NULL,
2386
+ entry_json TEXT NOT NULL,
2387
+ reason TEXT NOT NULL,
2388
+ strength REAL NOT NULL,
2389
+ dormant_at TEXT NOT NULL,
2390
+ PRIMARY KEY (tenant_id, id)
2391
+ ) WITHOUT ROWID;
2392
+ CREATE INDEX IF NOT EXISTS idx_dormant_memories_tenant_time
2393
+ ON dormant_memories(tenant_id, dormant_at DESC);
2394
+ `);
2395
+ },
2396
+ },
2397
+ {
2398
+ version: 45,
2399
+ up: (db) => {
2400
+ // Token ledger (src/token-ledger.ts, ROADMAP TE0): one row per block of
2401
+ // memory text hippo hands an agent (hook, CLI, MCP, HTTP). `event` is
2402
+ // 'inject' (sent), 'skip' (unchanged since the session's last inject,
2403
+ // not sent) or 'reset' (compaction dropped earlier injections, so the
2404
+ // next one must be sent). block_hash lets the per-prompt hook skip an
2405
+ // unchanged block. Rows older than the retention window are pruned on
2406
+ // write. Additive only: no min_compatible_binary bump.
2407
+ db.exec(`
2408
+ CREATE TABLE IF NOT EXISTS token_ledger (
2409
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
2410
+ ts TEXT NOT NULL,
2411
+ tenant_id TEXT NOT NULL DEFAULT 'default',
2412
+ session_id TEXT,
2413
+ surface TEXT NOT NULL,
2414
+ event TEXT NOT NULL,
2415
+ items INTEGER NOT NULL DEFAULT 0,
2416
+ tokens INTEGER NOT NULL DEFAULT 0,
2417
+ block_hash TEXT
2418
+ );
2419
+ CREATE INDEX IF NOT EXISTS idx_token_ledger_session
2420
+ ON token_ledger(tenant_id, session_id, surface, id DESC);
2421
+ CREATE INDEX IF NOT EXISTS idx_token_ledger_ts
2422
+ ON token_ledger(ts);
2423
+ `);
2424
+ },
2425
+ },
2370
2426
  ];
2371
2427
  function tableHasColumn(db, tableName, columnName) {
2372
2428
  if (!/^[a-z_]+$/i.test(tableName))
@@ -2389,16 +2445,12 @@ export function getHippoDbPath(hippoRoot) {
2389
2445
  export function getCurrentSchemaVersion() {
2390
2446
  return CURRENT_SCHEMA_VERSION;
2391
2447
  }
2392
- function readMinCompatibleBinary(db) {
2393
- // Tolerate the meta table not yet existing on a fresh DB.
2394
- try {
2395
- // SAFETY: row's shape matches the single `value` column named in the
2396
- // SELECT above.
2397
- const row = db.prepare(`SELECT value FROM meta WHERE key = 'min_compatible_binary'`).get();
2398
- return row?.value ?? null;
2399
- }
2400
- catch {
2401
- return null;
2448
+ /** Refuse a store stamped for a newer binary. Fails closed: runMigrations creates meta first, so a failed read is a real error. */
2449
+ function assertBinaryCompatible(db) {
2450
+ const minRequired = getMeta(db, 'min_compatible_binary');
2451
+ if (minRequired && compareSemver(minRequired, PACKAGE_VERSION) > 0) {
2452
+ throw new Error(`hippo-memory: this database requires hippo-memory >= ${minRequired}, but the running binary is ${PACKAGE_VERSION}. ` +
2453
+ `Upgrade hippo-memory to open it; an older binary does not know this schema and could expose private rows or damage the store.`);
2402
2454
  }
2403
2455
  }
2404
2456
  function isSqliteBusy(error) {
@@ -2456,17 +2508,8 @@ export function openHippoDb(hippoRoot) {
2456
2508
  }
2457
2509
  function runMigrations(db, hippoRoot) {
2458
2510
  ensureMetaTable(db);
2459
- // v1.3.1 rollback-safety guard. Schema v24 stamped meta.min_compatible_binary;
2460
- // older binaries that lack the generic *:private:* default-deny filter would
2461
- // leak github:private:* rows on no-scope recall. Refuse to open a DB stamped
2462
- // with a min newer than this binary's version. Read BEFORE migrations so a
2463
- // stale v1.2.0 binary cannot apply unknown future migrations either.
2464
- const minRequired = readMinCompatibleBinary(db);
2465
- if (minRequired && compareSemver(minRequired, PACKAGE_VERSION) > 0) {
2466
- throw new Error(`hippo-memory: this database requires hippo-memory >= ${minRequired}, but the running binary is ${PACKAGE_VERSION}. ` +
2467
- `Upgrade hippo-memory to open this database. Running an older binary against this DB would leak private rows that the ` +
2468
- `older filter does not recognize.`);
2469
- }
2511
+ // Before anything writes, so a stale binary never repairs or migrates a store it does not understand.
2512
+ assertBinaryCompatible(db);
2470
2513
  let currentVersion = getSchemaVersion(db);
2471
2514
  if (currentVersion > 0)
2472
2515
  ensureContinuityTables(db);
@@ -2475,6 +2518,8 @@ function runMigrations(db, hippoRoot) {
2475
2518
  continue;
2476
2519
  execWithBusyRetry(db, 'BEGIN IMMEDIATE');
2477
2520
  try {
2521
+ // A newer binary may have migrated and raised the minimum while we waited for the lock.
2522
+ assertBinaryCompatible(db);
2478
2523
  // Re-read under the write lock: another process may have applied this
2479
2524
  // migration while we waited, and re-running one is not idempotent.
2480
2525
  const applied = getSchemaVersion(db);
@@ -2672,8 +2717,8 @@ function ensureOptionalFts(db) {
2672
2717
  backfillFtsIndex(db);
2673
2718
  available = true;
2674
2719
  }
2675
- catch {
2676
- available = false;
2720
+ catch (err) {
2721
+ console.error(`hippo: full-text index unavailable (${err instanceof Error ? err.message : String(err)}); search falls back to slower LIKE matching`);
2677
2722
  }
2678
2723
  // Read-first: only write when the flag actually changed, so a healthy
2679
2724
  // store's open doesn't take a write lock for a same-value upsert.
package/dist/dedupe.d.ts CHANGED
@@ -70,5 +70,6 @@ export declare function strengthBucket(strength: number | null | undefined): num
70
70
  export declare function deduplicateStore(hippoRoot: string, options?: {
71
71
  threshold?: number;
72
72
  dryRun?: boolean;
73
+ actor?: string;
73
74
  }): DedupResult;
74
75
  //# sourceMappingURL=dedupe.d.ts.map
package/dist/dedupe.js CHANGED
@@ -23,6 +23,7 @@
23
23
  import { textOverlap } from './search.js';
24
24
  import { loadAllEntries, deleteEntry } from './store.js';
25
25
  import { compareEntryIdentity } from './compare.js';
26
+ import { canAutoDelete } from './memory.js';
26
27
  /** Quantization step for strength-tie comparisons. The historical 0.01
27
28
  * epsilon (see `strengthBucket` below) applied via rounding instead of a
28
29
  * raw abs-diff threshold, so the tiebreak is transitive. */
@@ -111,7 +112,7 @@ export function deduplicateStore(hippoRoot, options = {}) {
111
112
  if (removed.has(tenantEntries[i].id))
112
113
  continue;
113
114
  for (let j = i + 1; j < tenantEntries.length; j++) {
114
- if (removed.has(tenantEntries[j].id))
115
+ if (removed.has(tenantEntries[j].id) || !canAutoDelete(tenantEntries[j]))
115
116
  continue;
116
117
  const similarity = textOverlap(tenantEntries[i].content, tenantEntries[j].content);
117
118
  if (similarity <= threshold)
@@ -131,11 +132,7 @@ export function deduplicateStore(hippoRoot, options = {}) {
131
132
  }
132
133
  }
133
134
  }
134
- if (!dryRun) {
135
- for (const id of removed) {
136
- deleteEntry(hippoRoot, id);
137
- }
138
- }
139
- return { removed: removed.size, pairs };
135
+ const done = dryRun ? pairs : pairs.filter((p) => deleteEntry(hippoRoot, p.removed, { actor: options.actor, reason: `dedup: duplicate of ${p.kept}`, automatic: true }));
136
+ return { removed: done.length, pairs: done };
140
137
  }
141
138
  //# sourceMappingURL=dedupe.js.map
@@ -0,0 +1,34 @@
1
+ /** Outcome of one check. `fail` makes `hippo doctor` exit 1. */
2
+ export type DoctorStatus = 'pass' | 'warn' | 'fail' | 'info';
3
+ /** One check in a {@link DoctorReport}. */
4
+ export interface DoctorCheck {
5
+ id: string;
6
+ status: DoctorStatus;
7
+ detail: string;
8
+ /** Command or step that resolves a warn or fail. */
9
+ fix?: string;
10
+ }
11
+ /** Result of {@link runDoctor}. */
12
+ export interface DoctorReport {
13
+ ok: boolean;
14
+ version: string;
15
+ /** The store the checks ran against, or null when none exists. */
16
+ store: string | null;
17
+ checks: DoctorCheck[];
18
+ }
19
+ /** Inputs for {@link runDoctor}; defaults come from the process. */
20
+ export interface DoctorOpts {
21
+ cwd?: string;
22
+ /** Home directory used to find agent configuration (~/.claude). */
23
+ home?: string;
24
+ version: string;
25
+ nodeVersion?: string;
26
+ now?: Date;
27
+ }
28
+ /** Minimum Node.js version hippo supports (package.json engines). */
29
+ export declare const MIN_NODE = "22.16.0";
30
+ /** Run every check. Never throws for a broken install; broken parts become failed checks. */
31
+ export declare function runDoctor(opts: DoctorOpts): DoctorReport;
32
+ /** Human-readable rendering of a {@link DoctorReport}. */
33
+ export declare function formatDoctor(report: DoctorReport): string;
34
+ //# sourceMappingURL=doctor.d.ts.map