hippo-memory 1.44.0 → 1.45.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 (528) hide show
  1. package/README.md +12 -10
  2. package/bin/hippo.js +3 -1
  3. package/dist/api.d.ts +21 -21
  4. package/dist/api.js +69 -42
  5. package/dist/audit.d.ts +1 -1
  6. package/dist/audit.js +9 -0
  7. package/dist/autolearn.d.ts +1 -1
  8. package/dist/autolearn.js +2 -1
  9. package/dist/cli.d.ts +3 -0
  10. package/dist/cli.js +151 -72
  11. package/dist/client.d.ts +4 -21
  12. package/dist/client.js +3 -112
  13. package/dist/config.js +2 -1
  14. package/dist/consolidate.d.ts +3 -0
  15. package/dist/consolidate.js +44 -42
  16. package/dist/dag.d.ts +1 -0
  17. package/dist/dag.js +10 -5
  18. package/dist/dashboard.js +7 -3
  19. package/dist/db.js +12 -23
  20. package/dist/dedupe.d.ts +1 -0
  21. package/dist/dedupe.js +4 -7
  22. package/dist/embedding-provider.js +2 -1
  23. package/dist/embeddings.js +81 -8
  24. package/dist/extract.d.ts +2 -0
  25. package/dist/extract.js +9 -4
  26. package/dist/mcp/server.js +21 -36
  27. package/dist/memory.d.ts +3 -0
  28. package/dist/memory.js +7 -1
  29. package/dist/refine-llm.js +3 -2
  30. package/dist/rerankers/jev.js +3 -2
  31. package/dist/rerankers/llm.js +3 -2
  32. package/dist/search.js +3 -3
  33. package/dist/server.d.ts +5 -0
  34. package/dist/server.js +39 -29
  35. package/dist/shared.js +1 -1
  36. package/dist/store.d.ts +18 -11
  37. package/dist/store.js +144 -63
  38. package/dist/version.d.ts +4 -6
  39. package/dist/version.js +4 -7
  40. package/extensions/openclaw-plugin/openclaw.plugin.json +1 -1
  41. package/extensions/openclaw-plugin/package.json +1 -1
  42. package/openclaw.plugin.json +1 -1
  43. package/package.json +6 -3
  44. package/dist/ablation.d.ts.map +0 -1
  45. package/dist/ablation.js.map +0 -1
  46. package/dist/ambient.d.ts.map +0 -1
  47. package/dist/ambient.js.map +0 -1
  48. package/dist/api.d.ts.map +0 -1
  49. package/dist/api.js.map +0 -1
  50. package/dist/audit-prune.d.ts.map +0 -1
  51. package/dist/audit-prune.js.map +0 -1
  52. package/dist/audit.d.ts.map +0 -1
  53. package/dist/audit.js.map +0 -1
  54. package/dist/auth.d.ts.map +0 -1
  55. package/dist/auth.js.map +0 -1
  56. package/dist/autolearn.d.ts.map +0 -1
  57. package/dist/autolearn.js.map +0 -1
  58. package/dist/availability.d.ts.map +0 -1
  59. package/dist/availability.js.map +0 -1
  60. package/dist/benchmarks/e1.3/incident-recall-eval.js +0 -78
  61. package/dist/benchmarks/e1.3/incident-recall-eval.js.map +0 -1
  62. package/dist/benchmarks/e1.3/slack-1000-event-smoke.js +0 -103
  63. package/dist/benchmarks/e1.3/slack-1000-event-smoke.js.map +0 -1
  64. package/dist/capture.d.ts.map +0 -1
  65. package/dist/capture.js.map +0 -1
  66. package/dist/card-detail.d.ts.map +0 -1
  67. package/dist/card-detail.js.map +0 -1
  68. package/dist/card.d.ts.map +0 -1
  69. package/dist/card.js.map +0 -1
  70. package/dist/cli.d.ts.map +0 -1
  71. package/dist/cli.js.map +0 -1
  72. package/dist/client.d.ts.map +0 -1
  73. package/dist/client.js.map +0 -1
  74. package/dist/compare.d.ts.map +0 -1
  75. package/dist/compare.js.map +0 -1
  76. package/dist/config.d.ts.map +0 -1
  77. package/dist/config.js.map +0 -1
  78. package/dist/connectors/github/backfill.d.ts.map +0 -1
  79. package/dist/connectors/github/backfill.js.map +0 -1
  80. package/dist/connectors/github/cli-impl.d.ts.map +0 -1
  81. package/dist/connectors/github/cli-impl.js.map +0 -1
  82. package/dist/connectors/github/deletion.d.ts.map +0 -1
  83. package/dist/connectors/github/deletion.js.map +0 -1
  84. package/dist/connectors/github/dlq.d.ts.map +0 -1
  85. package/dist/connectors/github/dlq.js.map +0 -1
  86. package/dist/connectors/github/idempotency.d.ts.map +0 -1
  87. package/dist/connectors/github/idempotency.js.map +0 -1
  88. package/dist/connectors/github/ingest.d.ts.map +0 -1
  89. package/dist/connectors/github/ingest.js.map +0 -1
  90. package/dist/connectors/github/octokit-client.d.ts.map +0 -1
  91. package/dist/connectors/github/octokit-client.js.map +0 -1
  92. package/dist/connectors/github/ratelimit.d.ts.map +0 -1
  93. package/dist/connectors/github/ratelimit.js.map +0 -1
  94. package/dist/connectors/github/scope.d.ts.map +0 -1
  95. package/dist/connectors/github/scope.js.map +0 -1
  96. package/dist/connectors/github/signature.d.ts.map +0 -1
  97. package/dist/connectors/github/signature.js.map +0 -1
  98. package/dist/connectors/github/tenant-routing.d.ts.map +0 -1
  99. package/dist/connectors/github/tenant-routing.js.map +0 -1
  100. package/dist/connectors/github/transform.d.ts.map +0 -1
  101. package/dist/connectors/github/transform.js.map +0 -1
  102. package/dist/connectors/github/types.d.ts.map +0 -1
  103. package/dist/connectors/github/types.js.map +0 -1
  104. package/dist/connectors/slack/backfill.d.ts.map +0 -1
  105. package/dist/connectors/slack/backfill.js.map +0 -1
  106. package/dist/connectors/slack/deletion.d.ts.map +0 -1
  107. package/dist/connectors/slack/deletion.js.map +0 -1
  108. package/dist/connectors/slack/dlq.d.ts.map +0 -1
  109. package/dist/connectors/slack/dlq.js.map +0 -1
  110. package/dist/connectors/slack/idempotency.d.ts.map +0 -1
  111. package/dist/connectors/slack/idempotency.js.map +0 -1
  112. package/dist/connectors/slack/ingest.d.ts.map +0 -1
  113. package/dist/connectors/slack/ingest.js.map +0 -1
  114. package/dist/connectors/slack/ratelimit.d.ts.map +0 -1
  115. package/dist/connectors/slack/ratelimit.js.map +0 -1
  116. package/dist/connectors/slack/scope.d.ts.map +0 -1
  117. package/dist/connectors/slack/scope.js.map +0 -1
  118. package/dist/connectors/slack/signature.d.ts.map +0 -1
  119. package/dist/connectors/slack/signature.js.map +0 -1
  120. package/dist/connectors/slack/tenant-routing.d.ts.map +0 -1
  121. package/dist/connectors/slack/tenant-routing.js.map +0 -1
  122. package/dist/connectors/slack/transform.d.ts.map +0 -1
  123. package/dist/connectors/slack/transform.js.map +0 -1
  124. package/dist/connectors/slack/types.d.ts.map +0 -1
  125. package/dist/connectors/slack/types.js.map +0 -1
  126. package/dist/connectors/slack/web-client.d.ts.map +0 -1
  127. package/dist/connectors/slack/web-client.js.map +0 -1
  128. package/dist/connectors/slack/workspaces.d.ts.map +0 -1
  129. package/dist/connectors/slack/workspaces.js.map +0 -1
  130. package/dist/consolidate.d.ts.map +0 -1
  131. package/dist/consolidate.js.map +0 -1
  132. package/dist/correction-latency.d.ts.map +0 -1
  133. package/dist/correction-latency.js.map +0 -1
  134. package/dist/customer-notes.d.ts.map +0 -1
  135. package/dist/customer-notes.js.map +0 -1
  136. package/dist/dag.d.ts.map +0 -1
  137. package/dist/dag.js.map +0 -1
  138. package/dist/dashboard.d.ts.map +0 -1
  139. package/dist/dashboard.js.map +0 -1
  140. package/dist/db.d.ts.map +0 -1
  141. package/dist/db.js.map +0 -1
  142. package/dist/decisions.d.ts.map +0 -1
  143. package/dist/decisions.js.map +0 -1
  144. package/dist/dedupe.d.ts.map +0 -1
  145. package/dist/dedupe.js.map +0 -1
  146. package/dist/embedding-provider.d.ts.map +0 -1
  147. package/dist/embedding-provider.js.map +0 -1
  148. package/dist/embeddings.d.ts.map +0 -1
  149. package/dist/embeddings.js.map +0 -1
  150. package/dist/eval-suite.d.ts.map +0 -1
  151. package/dist/eval-suite.js.map +0 -1
  152. package/dist/eval.d.ts.map +0 -1
  153. package/dist/eval.js.map +0 -1
  154. package/dist/extensions/openclaw-plugin/index.js.map +0 -1
  155. package/dist/extract.d.ts.map +0 -1
  156. package/dist/extract.js.map +0 -1
  157. package/dist/forward-claim-detector.d.ts.map +0 -1
  158. package/dist/forward-claim-detector.js.map +0 -1
  159. package/dist/goals.d.ts.map +0 -1
  160. package/dist/goals.js.map +0 -1
  161. package/dist/graph-extract.d.ts.map +0 -1
  162. package/dist/graph-extract.js.map +0 -1
  163. package/dist/graph-recall.d.ts.map +0 -1
  164. package/dist/graph-recall.js.map +0 -1
  165. package/dist/graph-stream.d.ts.map +0 -1
  166. package/dist/graph-stream.js.map +0 -1
  167. package/dist/graph-view.d.ts.map +0 -1
  168. package/dist/graph-view.js.map +0 -1
  169. package/dist/graph.d.ts.map +0 -1
  170. package/dist/graph.js.map +0 -1
  171. package/dist/handoff.d.ts.map +0 -1
  172. package/dist/handoff.js.map +0 -1
  173. package/dist/hooks.d.ts.map +0 -1
  174. package/dist/hooks.js.map +0 -1
  175. package/dist/importers.d.ts.map +0 -1
  176. package/dist/importers.js.map +0 -1
  177. package/dist/incidents.d.ts.map +0 -1
  178. package/dist/incidents.js.map +0 -1
  179. package/dist/index.d.ts.map +0 -1
  180. package/dist/index.js.map +0 -1
  181. package/dist/invalidation.d.ts.map +0 -1
  182. package/dist/invalidation.js.map +0 -1
  183. package/dist/mcp/framing.d.ts.map +0 -1
  184. package/dist/mcp/framing.js.map +0 -1
  185. package/dist/mcp/server.d.ts.map +0 -1
  186. package/dist/mcp/server.js.map +0 -1
  187. package/dist/memory-value-weights.d.ts.map +0 -1
  188. package/dist/memory-value-weights.js.map +0 -1
  189. package/dist/memory-value.d.ts.map +0 -1
  190. package/dist/memory-value.js.map +0 -1
  191. package/dist/memory.d.ts.map +0 -1
  192. package/dist/memory.js.map +0 -1
  193. package/dist/multihop.d.ts.map +0 -1
  194. package/dist/multihop.js.map +0 -1
  195. package/dist/owner-validation.d.ts.map +0 -1
  196. package/dist/owner-validation.js.map +0 -1
  197. package/dist/path-context.d.ts.map +0 -1
  198. package/dist/path-context.js.map +0 -1
  199. package/dist/physics-config.d.ts.map +0 -1
  200. package/dist/physics-config.js.map +0 -1
  201. package/dist/physics-state.d.ts.map +0 -1
  202. package/dist/physics-state.js.map +0 -1
  203. package/dist/physics.d.ts.map +0 -1
  204. package/dist/physics.js.map +0 -1
  205. package/dist/policies.d.ts.map +0 -1
  206. package/dist/policies.js.map +0 -1
  207. package/dist/postinstall.d.ts.map +0 -1
  208. package/dist/postinstall.js.map +0 -1
  209. package/dist/predictions.d.ts.map +0 -1
  210. package/dist/predictions.js.map +0 -1
  211. package/dist/processes.d.ts.map +0 -1
  212. package/dist/processes.js.map +0 -1
  213. package/dist/project-briefs.d.ts.map +0 -1
  214. package/dist/project-briefs.js.map +0 -1
  215. package/dist/project-identity.d.ts.map +0 -1
  216. package/dist/project-identity.js.map +0 -1
  217. package/dist/provenance-coverage.d.ts.map +0 -1
  218. package/dist/provenance-coverage.js.map +0 -1
  219. package/dist/rate-limit.d.ts.map +0 -1
  220. package/dist/rate-limit.js.map +0 -1
  221. package/dist/raw-archive-mirror-cleanup.d.ts.map +0 -1
  222. package/dist/raw-archive-mirror-cleanup.js.map +0 -1
  223. package/dist/raw-archive.d.ts.map +0 -1
  224. package/dist/raw-archive.js.map +0 -1
  225. package/dist/recall-history.d.ts.map +0 -1
  226. package/dist/recall-history.js.map +0 -1
  227. package/dist/recall-scope.d.ts.map +0 -1
  228. package/dist/recall-scope.js.map +0 -1
  229. package/dist/recall-trace.d.ts.map +0 -1
  230. package/dist/recall-trace.js.map +0 -1
  231. package/dist/refine-llm.d.ts.map +0 -1
  232. package/dist/refine-llm.js.map +0 -1
  233. package/dist/reject-flow.d.ts.map +0 -1
  234. package/dist/reject-flow.js.map +0 -1
  235. package/dist/rejection.d.ts.map +0 -1
  236. package/dist/rejection.js.map +0 -1
  237. package/dist/replay.d.ts.map +0 -1
  238. package/dist/replay.js.map +0 -1
  239. package/dist/rerankers/cross-encoder.d.ts.map +0 -1
  240. package/dist/rerankers/cross-encoder.js.map +0 -1
  241. package/dist/rerankers/index.d.ts.map +0 -1
  242. package/dist/rerankers/index.js.map +0 -1
  243. package/dist/rerankers/jev.d.ts.map +0 -1
  244. package/dist/rerankers/jev.js.map +0 -1
  245. package/dist/rerankers/llm.d.ts.map +0 -1
  246. package/dist/rerankers/llm.js.map +0 -1
  247. package/dist/rerankers/types.d.ts.map +0 -1
  248. package/dist/rerankers/types.js.map +0 -1
  249. package/dist/rrf.d.ts.map +0 -1
  250. package/dist/rrf.js.map +0 -1
  251. package/dist/salience.d.ts.map +0 -1
  252. package/dist/salience.js.map +0 -1
  253. package/dist/scheduler.d.ts.map +0 -1
  254. package/dist/scheduler.js.map +0 -1
  255. package/dist/scope.d.ts.map +0 -1
  256. package/dist/scope.js.map +0 -1
  257. package/dist/search.d.ts.map +0 -1
  258. package/dist/search.js.map +0 -1
  259. package/dist/secret-detect.d.ts.map +0 -1
  260. package/dist/secret-detect.js.map +0 -1
  261. package/dist/server-detect.d.ts.map +0 -1
  262. package/dist/server-detect.js.map +0 -1
  263. package/dist/server.d.ts.map +0 -1
  264. package/dist/server.js.map +0 -1
  265. package/dist/shared.d.ts.map +0 -1
  266. package/dist/shared.js.map +0 -1
  267. package/dist/skills.d.ts.map +0 -1
  268. package/dist/skills.js.map +0 -1
  269. package/dist/sleep-redact.d.ts +0 -58
  270. package/dist/sleep-redact.d.ts.map +0 -1
  271. package/dist/sleep-redact.js +0 -80
  272. package/dist/sleep-redact.js.map +0 -1
  273. package/dist/src/ablation.js +0 -138
  274. package/dist/src/ablation.js.map +0 -1
  275. package/dist/src/ambient.js +0 -148
  276. package/dist/src/ambient.js.map +0 -1
  277. package/dist/src/api.js +0 -2109
  278. package/dist/src/api.js.map +0 -1
  279. package/dist/src/audit-prune.js +0 -109
  280. package/dist/src/audit-prune.js.map +0 -1
  281. package/dist/src/audit.js +0 -213
  282. package/dist/src/audit.js.map +0 -1
  283. package/dist/src/auth.js +0 -97
  284. package/dist/src/auth.js.map +0 -1
  285. package/dist/src/autolearn.js +0 -174
  286. package/dist/src/autolearn.js.map +0 -1
  287. package/dist/src/availability.js +0 -94
  288. package/dist/src/availability.js.map +0 -1
  289. package/dist/src/capture.js +0 -1346
  290. package/dist/src/capture.js.map +0 -1
  291. package/dist/src/card-detail.js +0 -15
  292. package/dist/src/card-detail.js.map +0 -1
  293. package/dist/src/card.js +0 -19
  294. package/dist/src/card.js.map +0 -1
  295. package/dist/src/cli.js +0 -9387
  296. package/dist/src/cli.js.map +0 -1
  297. package/dist/src/client.js +0 -237
  298. package/dist/src/client.js.map +0 -1
  299. package/dist/src/compare.js +0 -122
  300. package/dist/src/compare.js.map +0 -1
  301. package/dist/src/config.js +0 -135
  302. package/dist/src/config.js.map +0 -1
  303. package/dist/src/connectors/github/backfill.js +0 -281
  304. package/dist/src/connectors/github/backfill.js.map +0 -1
  305. package/dist/src/connectors/github/cli-impl.js +0 -234
  306. package/dist/src/connectors/github/cli-impl.js.map +0 -1
  307. package/dist/src/connectors/github/deletion.js +0 -85
  308. package/dist/src/connectors/github/deletion.js.map +0 -1
  309. package/dist/src/connectors/github/dlq.js +0 -190
  310. package/dist/src/connectors/github/dlq.js.map +0 -1
  311. package/dist/src/connectors/github/idempotency.js +0 -27
  312. package/dist/src/connectors/github/idempotency.js.map +0 -1
  313. package/dist/src/connectors/github/ingest.js +0 -156
  314. package/dist/src/connectors/github/ingest.js.map +0 -1
  315. package/dist/src/connectors/github/octokit-client.js +0 -70
  316. package/dist/src/connectors/github/octokit-client.js.map +0 -1
  317. package/dist/src/connectors/github/ratelimit.js +0 -31
  318. package/dist/src/connectors/github/ratelimit.js.map +0 -1
  319. package/dist/src/connectors/github/scope.js +0 -13
  320. package/dist/src/connectors/github/scope.js.map +0 -1
  321. package/dist/src/connectors/github/signature.js +0 -81
  322. package/dist/src/connectors/github/signature.js.map +0 -1
  323. package/dist/src/connectors/github/tenant-routing.js +0 -69
  324. package/dist/src/connectors/github/tenant-routing.js.map +0 -1
  325. package/dist/src/connectors/github/transform.js +0 -103
  326. package/dist/src/connectors/github/transform.js.map +0 -1
  327. package/dist/src/connectors/github/types.js +0 -100
  328. package/dist/src/connectors/github/types.js.map +0 -1
  329. package/dist/src/connectors/slack/backfill.js +0 -78
  330. package/dist/src/connectors/slack/backfill.js.map +0 -1
  331. package/dist/src/connectors/slack/deletion.js +0 -59
  332. package/dist/src/connectors/slack/deletion.js.map +0 -1
  333. package/dist/src/connectors/slack/dlq.js +0 -224
  334. package/dist/src/connectors/slack/dlq.js.map +0 -1
  335. package/dist/src/connectors/slack/idempotency.js +0 -31
  336. package/dist/src/connectors/slack/idempotency.js.map +0 -1
  337. package/dist/src/connectors/slack/ingest.js +0 -109
  338. package/dist/src/connectors/slack/ingest.js.map +0 -1
  339. package/dist/src/connectors/slack/ratelimit.js +0 -18
  340. package/dist/src/connectors/slack/ratelimit.js.map +0 -1
  341. package/dist/src/connectors/slack/scope.js +0 -13
  342. package/dist/src/connectors/slack/scope.js.map +0 -1
  343. package/dist/src/connectors/slack/signature.js +0 -27
  344. package/dist/src/connectors/slack/signature.js.map +0 -1
  345. package/dist/src/connectors/slack/tenant-routing.js +0 -41
  346. package/dist/src/connectors/slack/tenant-routing.js.map +0 -1
  347. package/dist/src/connectors/slack/transform.js +0 -47
  348. package/dist/src/connectors/slack/transform.js.map +0 -1
  349. package/dist/src/connectors/slack/types.js +0 -35
  350. package/dist/src/connectors/slack/types.js.map +0 -1
  351. package/dist/src/connectors/slack/web-client.js +0 -43
  352. package/dist/src/connectors/slack/web-client.js.map +0 -1
  353. package/dist/src/connectors/slack/workspaces.js +0 -62
  354. package/dist/src/connectors/slack/workspaces.js.map +0 -1
  355. package/dist/src/consolidate.js +0 -978
  356. package/dist/src/consolidate.js.map +0 -1
  357. package/dist/src/correction-latency.js +0 -74
  358. package/dist/src/correction-latency.js.map +0 -1
  359. package/dist/src/customer-notes.js +0 -325
  360. package/dist/src/customer-notes.js.map +0 -1
  361. package/dist/src/dag.js +0 -352
  362. package/dist/src/dag.js.map +0 -1
  363. package/dist/src/dashboard.js +0 -258
  364. package/dist/src/dashboard.js.map +0 -1
  365. package/dist/src/db.js +0 -2731
  366. package/dist/src/db.js.map +0 -1
  367. package/dist/src/decisions.js +0 -304
  368. package/dist/src/decisions.js.map +0 -1
  369. package/dist/src/dedupe.js +0 -141
  370. package/dist/src/dedupe.js.map +0 -1
  371. package/dist/src/embedding-provider.js +0 -314
  372. package/dist/src/embedding-provider.js.map +0 -1
  373. package/dist/src/embeddings.js +0 -543
  374. package/dist/src/embeddings.js.map +0 -1
  375. package/dist/src/eval-suite.js +0 -294
  376. package/dist/src/eval-suite.js.map +0 -1
  377. package/dist/src/eval.js +0 -187
  378. package/dist/src/eval.js.map +0 -1
  379. package/dist/src/extract.js +0 -117
  380. package/dist/src/extract.js.map +0 -1
  381. package/dist/src/forward-claim-detector.js +0 -117
  382. package/dist/src/forward-claim-detector.js.map +0 -1
  383. package/dist/src/goals.js +0 -399
  384. package/dist/src/goals.js.map +0 -1
  385. package/dist/src/graph-extract.js +0 -314
  386. package/dist/src/graph-extract.js.map +0 -1
  387. package/dist/src/graph-recall.js +0 -277
  388. package/dist/src/graph-recall.js.map +0 -1
  389. package/dist/src/graph-stream.js +0 -176
  390. package/dist/src/graph-stream.js.map +0 -1
  391. package/dist/src/graph-view.js +0 -310
  392. package/dist/src/graph-view.js.map +0 -1
  393. package/dist/src/graph.js +0 -762
  394. package/dist/src/graph.js.map +0 -1
  395. package/dist/src/handoff.js +0 -65
  396. package/dist/src/handoff.js.map +0 -1
  397. package/dist/src/hooks.js +0 -926
  398. package/dist/src/hooks.js.map +0 -1
  399. package/dist/src/importers.js +0 -881
  400. package/dist/src/importers.js.map +0 -1
  401. package/dist/src/incidents.js +0 -336
  402. package/dist/src/incidents.js.map +0 -1
  403. package/dist/src/index.js +0 -32
  404. package/dist/src/index.js.map +0 -1
  405. package/dist/src/invalidation.js +0 -129
  406. package/dist/src/invalidation.js.map +0 -1
  407. package/dist/src/mcp/framing.js +0 -45
  408. package/dist/src/mcp/framing.js.map +0 -1
  409. package/dist/src/mcp/server.js +0 -1298
  410. package/dist/src/mcp/server.js.map +0 -1
  411. package/dist/src/memory-value-weights.js +0 -31
  412. package/dist/src/memory-value-weights.js.map +0 -1
  413. package/dist/src/memory-value.js +0 -261
  414. package/dist/src/memory-value.js.map +0 -1
  415. package/dist/src/memory.js +0 -425
  416. package/dist/src/memory.js.map +0 -1
  417. package/dist/src/multihop.js +0 -35
  418. package/dist/src/multihop.js.map +0 -1
  419. package/dist/src/owner-validation.js +0 -56
  420. package/dist/src/owner-validation.js.map +0 -1
  421. package/dist/src/path-context.js +0 -48
  422. package/dist/src/path-context.js.map +0 -1
  423. package/dist/src/physics-config.js +0 -26
  424. package/dist/src/physics-config.js.map +0 -1
  425. package/dist/src/physics-state.js +0 -172
  426. package/dist/src/physics-state.js.map +0 -1
  427. package/dist/src/physics.js +0 -378
  428. package/dist/src/physics.js.map +0 -1
  429. package/dist/src/policies.js +0 -414
  430. package/dist/src/policies.js.map +0 -1
  431. package/dist/src/postinstall.js +0 -101
  432. package/dist/src/postinstall.js.map +0 -1
  433. package/dist/src/predictions.js +0 -629
  434. package/dist/src/predictions.js.map +0 -1
  435. package/dist/src/processes.js +0 -349
  436. package/dist/src/processes.js.map +0 -1
  437. package/dist/src/project-briefs.js +0 -492
  438. package/dist/src/project-briefs.js.map +0 -1
  439. package/dist/src/project-identity.js +0 -200
  440. package/dist/src/project-identity.js.map +0 -1
  441. package/dist/src/provenance-coverage.js +0 -23
  442. package/dist/src/provenance-coverage.js.map +0 -1
  443. package/dist/src/rate-limit.js +0 -60
  444. package/dist/src/rate-limit.js.map +0 -1
  445. package/dist/src/raw-archive-mirror-cleanup.js +0 -55
  446. package/dist/src/raw-archive-mirror-cleanup.js.map +0 -1
  447. package/dist/src/raw-archive.js +0 -91
  448. package/dist/src/raw-archive.js.map +0 -1
  449. package/dist/src/recall-history.js +0 -235
  450. package/dist/src/recall-history.js.map +0 -1
  451. package/dist/src/recall-scope.js +0 -89
  452. package/dist/src/recall-scope.js.map +0 -1
  453. package/dist/src/recall-trace.js +0 -186
  454. package/dist/src/recall-trace.js.map +0 -1
  455. package/dist/src/refine-llm.js +0 -156
  456. package/dist/src/refine-llm.js.map +0 -1
  457. package/dist/src/reject-flow.js +0 -209
  458. package/dist/src/reject-flow.js.map +0 -1
  459. package/dist/src/rejection.js +0 -160
  460. package/dist/src/rejection.js.map +0 -1
  461. package/dist/src/replay.js +0 -122
  462. package/dist/src/replay.js.map +0 -1
  463. package/dist/src/rerankers/cross-encoder.js +0 -137
  464. package/dist/src/rerankers/cross-encoder.js.map +0 -1
  465. package/dist/src/rerankers/index.js +0 -20
  466. package/dist/src/rerankers/index.js.map +0 -1
  467. package/dist/src/rerankers/jev.js +0 -119
  468. package/dist/src/rerankers/jev.js.map +0 -1
  469. package/dist/src/rerankers/llm.js +0 -80
  470. package/dist/src/rerankers/llm.js.map +0 -1
  471. package/dist/src/rerankers/types.js +0 -2
  472. package/dist/src/rerankers/types.js.map +0 -1
  473. package/dist/src/rrf.js +0 -61
  474. package/dist/src/rrf.js.map +0 -1
  475. package/dist/src/salience.js +0 -74
  476. package/dist/src/salience.js.map +0 -1
  477. package/dist/src/scheduler.js +0 -77
  478. package/dist/src/scheduler.js.map +0 -1
  479. package/dist/src/scope.js +0 -35
  480. package/dist/src/scope.js.map +0 -1
  481. package/dist/src/search.js +0 -1003
  482. package/dist/src/search.js.map +0 -1
  483. package/dist/src/secret-detect.js +0 -95
  484. package/dist/src/secret-detect.js.map +0 -1
  485. package/dist/src/server-detect.js +0 -231
  486. package/dist/src/server-detect.js.map +0 -1
  487. package/dist/src/server.js +0 -3255
  488. package/dist/src/server.js.map +0 -1
  489. package/dist/src/shared.js +0 -502
  490. package/dist/src/shared.js.map +0 -1
  491. package/dist/src/skills.js +0 -346
  492. package/dist/src/skills.js.map +0 -1
  493. package/dist/src/sleep-redact.js +0 -80
  494. package/dist/src/sleep-redact.js.map +0 -1
  495. package/dist/src/sso.js +0 -22
  496. package/dist/src/sso.js.map +0 -1
  497. package/dist/src/stdin.js +0 -41
  498. package/dist/src/stdin.js.map +0 -1
  499. package/dist/src/store.js +0 -3749
  500. package/dist/src/store.js.map +0 -1
  501. package/dist/src/tenant.js +0 -17
  502. package/dist/src/tenant.js.map +0 -1
  503. package/dist/src/trace.js +0 -72
  504. package/dist/src/trace.js.map +0 -1
  505. package/dist/src/version.js +0 -40
  506. package/dist/src/version.js.map +0 -1
  507. package/dist/src/working-memory.js +0 -157
  508. package/dist/src/working-memory.js.map +0 -1
  509. package/dist/src/yaml.js +0 -107
  510. package/dist/src/yaml.js.map +0 -1
  511. package/dist/sso.d.ts +0 -13
  512. package/dist/sso.d.ts.map +0 -1
  513. package/dist/sso.js +0 -22
  514. package/dist/sso.js.map +0 -1
  515. package/dist/stdin.d.ts.map +0 -1
  516. package/dist/stdin.js.map +0 -1
  517. package/dist/store.d.ts.map +0 -1
  518. package/dist/store.js.map +0 -1
  519. package/dist/tenant.d.ts.map +0 -1
  520. package/dist/tenant.js.map +0 -1
  521. package/dist/trace.d.ts.map +0 -1
  522. package/dist/trace.js.map +0 -1
  523. package/dist/version.d.ts.map +0 -1
  524. package/dist/version.js.map +0 -1
  525. package/dist/working-memory.d.ts.map +0 -1
  526. package/dist/working-memory.js.map +0 -1
  527. package/dist/yaml.d.ts.map +0 -1
  528. package/dist/yaml.js.map +0 -1
@@ -5,6 +5,7 @@
5
5
  */
6
6
  import * as fs from 'fs';
7
7
  import * as path from 'path';
8
+ import { randomUUID } from 'crypto';
8
9
  import { createRequire } from 'module';
9
10
  import { loadAllEntries } from './store.js';
10
11
  import { openHippoDb, closeHippoDb, getMeta, setMeta } from './db.js';
@@ -244,13 +245,13 @@ export function saveStoredEmbeddingModel(hippoRoot, model) {
244
245
  closeHippoDb(db);
245
246
  }
246
247
  }
247
- export function resolveIndexedEmbeddingModel(hippoRoot, index = loadEmbeddingIndex(hippoRoot)) {
248
+ export function resolveIndexedEmbeddingModel(hippoRoot, index) {
248
249
  const stored = loadStoredEmbeddingModel(hippoRoot);
249
250
  if (stored)
250
251
  return stored;
251
- return Object.keys(index).length > 0 ? DEFAULT_EMBEDDING_MODEL : null;
252
+ return Object.keys(index ?? loadEmbeddingIndex(hippoRoot)).length > 0 ? DEFAULT_EMBEDDING_MODEL : null;
252
253
  }
253
- export function embeddingModelRequiresReindex(hippoRoot, model, index = loadEmbeddingIndex(hippoRoot)) {
254
+ export function embeddingModelRequiresReindex(hippoRoot, model, index) {
254
255
  const stored = resolveIndexedEmbeddingModel(hippoRoot, index);
255
256
  return stored !== null && stored !== embeddingIndexIdentity(model);
256
257
  }
@@ -374,16 +375,86 @@ export function saveEmbeddingIndex(hippoRoot, index) {
374
375
  throw err;
375
376
  }
376
377
  }
377
- // Mutex to serialize embedding writes and prevent read-modify-write races
378
+ const EMBED_LOCK_FILE = 'embeddings.lock';
379
+ const EMBED_LOCK_WAIT_MS = 10_000;
380
+ const EMBED_LOCK_OWNER = `${process.pid}:${randomUUID()}`;
381
+ // In-process mutex plus an O_EXCL "<pid>:<token>" lock file: our token is a lock we leaked; our PID with another token is a live worker thread, unless the lock predates this process (a reused PID).
378
382
  let _embedWriteLock = Promise.resolve();
379
- async function withEmbedLock(fn) {
383
+ function embedLockHolderAlive(lockPath) {
384
+ let raw;
385
+ try {
386
+ raw = fs.readFileSync(lockPath, 'utf8');
387
+ }
388
+ catch (err) {
389
+ return !(err instanceof Error && 'code' in err && err.code === 'ENOENT');
390
+ }
391
+ const mtimeMs = fs.statSync(lockPath, { throwIfNoEntry: false })?.mtimeMs ?? 0;
392
+ const pid = Number(raw.split(':')[0]);
393
+ // An empty lock is a holder between create and write; after 5 s it is a crashed one.
394
+ if (!Number.isInteger(pid) || pid <= 0)
395
+ return Date.now() - mtimeMs < 5_000;
396
+ if (raw === EMBED_LOCK_OWNER)
397
+ return false;
398
+ if (pid === process.pid)
399
+ return mtimeMs >= Date.now() - process.uptime() * 1000;
400
+ try {
401
+ process.kill(pid, 0);
402
+ return true;
403
+ }
404
+ catch (err) {
405
+ return err instanceof Error && 'code' in err && err.code === 'EPERM';
406
+ }
407
+ }
408
+ // After the double-break race below the file can be another writer's lock, so remove only our own.
409
+ function releaseEmbedFileLock(lockPath) {
410
+ let raw;
411
+ try {
412
+ raw = fs.readFileSync(lockPath, 'utf8');
413
+ }
414
+ catch (err) {
415
+ if (err instanceof Error && 'code' in err && err.code === 'ENOENT')
416
+ return;
417
+ throw err;
418
+ }
419
+ if (raw === EMBED_LOCK_OWNER)
420
+ fs.rmSync(lockPath, { force: true });
421
+ }
422
+ async function acquireEmbedFileLock(hippoRoot) {
423
+ const lockPath = path.join(hippoRoot, EMBED_LOCK_FILE);
424
+ const deadline = Date.now() + EMBED_LOCK_WAIT_MS;
425
+ for (;;) {
426
+ try {
427
+ fs.writeFileSync(lockPath, EMBED_LOCK_OWNER, { flag: 'wx' });
428
+ return () => releaseEmbedFileLock(lockPath);
429
+ }
430
+ catch (err) {
431
+ if (!(err instanceof Error && 'code' in err && err.code === 'EEXIST'))
432
+ throw err;
433
+ }
434
+ // SHORTCUT: two waiters can both break one dead holder's lock; a vector lost that way is backfilled by the next `hippo embed`.
435
+ if (!embedLockHolderAlive(lockPath)) {
436
+ fs.rmSync(lockPath, { force: true });
437
+ continue;
438
+ }
439
+ if (Date.now() >= deadline)
440
+ throw new Error(`embeddings.json is busy: another hippo process holds ${lockPath}`);
441
+ await new Promise((r) => setTimeout(r, 50));
442
+ }
443
+ }
444
+ async function withEmbedLock(hippoRoot, fn) {
380
445
  let resolve;
381
446
  const next = new Promise(r => { resolve = r; });
382
447
  const prev = _embedWriteLock;
383
448
  _embedWriteLock = next;
384
449
  await prev;
385
450
  try {
386
- return await fn();
451
+ const release = await acquireEmbedFileLock(hippoRoot);
452
+ try {
453
+ return await fn();
454
+ }
455
+ finally {
456
+ release();
457
+ }
387
458
  }
388
459
  finally {
389
460
  resolve();
@@ -396,7 +467,7 @@ export async function embedMemory(hippoRoot, entry, model) {
396
467
  const provider = resolveEmbeddingProvider(hippoRoot, { model });
397
468
  if (!provider.isAvailable())
398
469
  return;
399
- return withEmbedLock(async () => {
470
+ return withEmbedLock(hippoRoot, async () => {
400
471
  // embedMemory is best-effort: an embedding failure (API down / bad key / 5xx)
401
472
  // must not reject the caller's write — `getEmbedding` historically swallowed
402
473
  // failures and returned []. The explicit `hippo embed` / `embedAll` path is
@@ -445,6 +516,8 @@ export async function embedMemory(hippoRoot, entry, model) {
445
516
  catch {
446
517
  // Provider failure (API down / bad key). Best-effort: leave the index as-is.
447
518
  }
519
+ }).catch((err) => {
520
+ console.error(`hippo: skipped embedding ${entry.id} (${err instanceof Error ? err.message : String(err)}); run 'hippo embed' to backfill`);
448
521
  });
449
522
  }
450
523
  /**
@@ -468,7 +541,7 @@ export async function embedAll(hippoRoot, model) {
468
541
  }
469
542
  return 0;
470
543
  }
471
- return withEmbedLock(async () => {
544
+ return withEmbedLock(hippoRoot, async () => {
472
545
  const identity = provider.id;
473
546
  // L9: host-wide by design. embedAll backfills vectors for all tenants'
474
547
  // entries into the per-host embedding index. Per-tenant filtering would
package/dist/extract.d.ts CHANGED
@@ -8,6 +8,8 @@ export interface ExtractOptions {
8
8
  apiKey: string;
9
9
  model?: string;
10
10
  fetcher?: typeof fetch;
11
+ /** Told why a call produced nothing, so callers can surface it instead of guessing. */
12
+ onError?: (msg: string) => void;
11
13
  }
12
14
  export declare function extractFacts(text: string, opts: ExtractOptions): Promise<ExtractedFact[]>;
13
15
  export declare function storeExtractedFacts(hippoRoot: string, source: MemoryEntry, facts: ExtractedFact[]): MemoryEntry[];
package/dist/extract.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { Layer, createMemory } from './memory.js';
2
2
  import { writeEntry } from './store.js';
3
3
  import { RejectedValueError } from './rejection.js';
4
+ import { redactSecrets } from './secret-detect.js';
4
5
  function isJsonString(value) {
5
6
  return typeof value === 'string';
6
7
  }
@@ -31,15 +32,18 @@ export async function extractFacts(text, opts) {
31
32
  body: JSON.stringify({
32
33
  model,
33
34
  max_tokens: 1200,
34
- messages: [{ role: 'user', content: EXTRACTION_PROMPT + text }],
35
+ messages: [{ role: 'user', content: EXTRACTION_PROMPT + redactSecrets(text) }],
35
36
  }),
36
37
  });
37
38
  }
38
- catch {
39
+ catch (err) {
40
+ opts.onError?.(`request failed: ${err instanceof Error ? err.message : String(err)}`);
39
41
  return [];
40
42
  }
41
- if (!res.ok)
43
+ if (!res.ok) {
44
+ opts.onError?.(`HTTP ${res.status}`);
42
45
  return [];
46
+ }
43
47
  try {
44
48
  const data = await res.json();
45
49
  const raw = data.content?.[0]?.text?.trim() ?? '';
@@ -69,7 +73,8 @@ export async function extractFacts(text, opts) {
69
73
  }
70
74
  return facts;
71
75
  }
72
- catch {
76
+ catch (err) {
77
+ opts.onError?.(`unparseable response: ${err instanceof Error ? err.message : String(err)}`);
73
78
  return [];
74
79
  }
75
80
  }
@@ -10,9 +10,9 @@
10
10
  import * as fs from 'fs';
11
11
  import * as path from 'path';
12
12
  import { createMemory, Layer, calculateStrength, } from '../memory.js';
13
- import { hybridSearch, physicsSearch, markRetrieved } from '../search.js';
14
- import { isRecallBoostAblated, evalNow } from '../ablation.js';
15
- import { loadAllEntries, writeEntry, readEntry, loadFreshActiveTaskSnapshot, listMemoryConflicts, resolveConflict, RECALL_DEFAULT_DENY_SCOPES } from '../store.js';
13
+ import { hybridSearch, physicsSearch } from '../search.js';
14
+ import { evalNow } from '../ablation.js';
15
+ import { loadAllEntries, writeEntry, strengthenRetrieved, readEntry, loadFreshActiveTaskSnapshot, listMemoryConflicts, resolveConflict, RECALL_DEFAULT_DENY_SCOPES, countCreatedSinceLastSleep } from '../store.js';
16
16
  import { shareMemory, listPeers, getGlobalRoot } from '../shared.js';
17
17
  import { consolidate } from '../consolidate.js';
18
18
  import { execSync } from 'child_process';
@@ -359,6 +359,7 @@ const TOOLS = [
359
359
  // `${tenantId}:default` if a McpContext is constructed in tests without a
360
360
  // pid-bound transport.
361
361
  const lastRecalledIds = new Map();
362
+ const autoSleepInFlight = new Set();
362
363
  function resolveClientKey(ctx) {
363
364
  if (ctx?.clientKey)
364
365
  return ctx.clientKey;
@@ -527,15 +528,9 @@ async function executeTool(name, args, ctx) {
527
528
  closeHippoDb(dbForBoost);
528
529
  }
529
530
  }
530
- // Mark retrieved and persist
531
- const retrieved = markRetrieved(results.map((r) => r.entry));
532
- // EVAL-ONLY ablation (see ablation.ts): skip persistence under the recall
533
- // flag; ids below stay populated for outcome attribution.
534
- if (!isRecallBoostAblated()) {
535
- for (const entry of retrieved)
536
- writeEntry(hippoRoot, entry, { actor: ctx?.actor ?? 'mcp' });
537
- }
538
- lastRecalledIds.set(resolveClientKey(ctx), retrieved.map((e) => e.id));
531
+ const retrievedIds = results.map((r) => r.entry.id);
532
+ strengthenRetrieved(hippoRoot, retrievedIds);
533
+ lastRecalledIds.set(resolveClientKey(ctx), retrievedIds);
539
534
  // v0.33 / J1 — MCP per-pipeline anchoring detector. UNLIKE J3.2's
540
535
  // planningFallacyHint (which is pipeline-invariant because it
541
536
  // depends only on queryText + predictions table state), the
@@ -902,22 +897,17 @@ async function executeTool(name, args, ctx) {
902
897
  tags,
903
898
  });
904
899
  const entry = readEntry(hippoRoot, result.id, tenantId);
905
- // Auto-sleep check
906
- if (config.autoSleep.enabled) {
907
- const allEntries = loadAllEntries(hippoRoot, tenantId);
908
- const recentCount = allEntries.filter((e) => {
909
- const age = (Date.now() - new Date(e.created).getTime()) / (1000 * 60 * 60);
910
- return age < 24; // created in last 24 hours
911
- }).length;
912
- if (recentCount >= config.autoSleep.threshold) {
913
- // Fire-and-forget by design (never block the remember response), but
914
- // an unhandled rejection here can kill the MCP server process — any
915
- // consolidate error (DB contention, or the memoryValue fail-loud
916
- // throw) must land as a logged line, not a crash.
917
- consolidate(hippoRoot).catch((err) => {
918
- console.error(`auto-sleep consolidate failed: ${err instanceof Error ? err.message : err}`);
919
- });
920
- }
900
+ // Auto-sleep: one run per store at a time, triggered by what arrived since the last one.
901
+ if (config.autoSleep.enabled &&
902
+ !autoSleepInFlight.has(hippoRoot) &&
903
+ countCreatedSinceLastSleep(hippoRoot, tenantId) >= config.autoSleep.threshold) {
904
+ autoSleepInFlight.add(hippoRoot);
905
+ // Fire-and-forget (never block the response); an unhandled rejection would kill the server, so log it.
906
+ consolidate(hippoRoot)
907
+ .catch((err) => {
908
+ console.error(`auto-sleep consolidate failed (tenant ${tenantId}): ${err instanceof Error ? err.message : err}`);
909
+ })
910
+ .finally(() => autoSleepInFlight.delete(hippoRoot));
921
911
  }
922
912
  const halfLife = entry?.half_life_days ?? config.defaultHalfLifeDays;
923
913
  const tagStr = entry?.tags.join(', ') || tags.join(', ') || 'none';
@@ -1000,14 +990,9 @@ async function executeTool(name, args, ctx) {
1000
990
  const results = usePhysicsCtx
1001
991
  ? await physicsSearch(query, entries, { budget, hippoRoot, physicsConfig: config.physics })
1002
992
  : await hybridSearch(query, entries, { budget, hippoRoot });
1003
- const retrieved = markRetrieved(results.map((r) => r.entry));
1004
- // EVAL-ONLY ablation (see ablation.ts): skip persistence under the recall
1005
- // flag; ids below stay populated for outcome attribution.
1006
- if (!isRecallBoostAblated()) {
1007
- for (const entry of retrieved)
1008
- writeEntry(hippoRoot, entry, { actor: ctx?.actor ?? 'mcp' });
1009
- }
1010
- lastRecalledIds.set(resolveClientKey(ctx), retrieved.map((e) => e.id));
993
+ const retrievedIds = results.map((r) => r.entry.id);
994
+ strengthenRetrieved(hippoRoot, retrievedIds);
995
+ lastRecalledIds.set(resolveClientKey(ctx), retrievedIds);
1011
996
  // DF1 (docs/plans/2026-08-23-df1-snapshot-lifecycle.md, T2): bounded
1012
997
  // read, no session id available on this surface (freshness bound
1013
998
  // only) — an orphaned snapshot must age out here too, not just on the
package/dist/memory.d.ts CHANGED
@@ -203,6 +203,9 @@ export declare function confidenceLabel(entry: MemoryEntry, now?: Date): {
203
203
  * returns 'stale'. Otherwise returns the stored confidence value.
204
204
  */
205
205
  export declare function resolveConfidence(entry: MemoryEntry, now?: Date): ConfidenceLevel;
206
+ export declare const DEFAULT_HALF_LIFE_DAYS = 7;
207
+ export declare const AUTO_DELETABLE_SQL = "pinned = 0 AND kind != 'raw'";
208
+ export declare function canAutoDelete(entry: Pick<MemoryEntry, 'pinned' | 'kind'>): boolean;
206
209
  /**
207
210
  * Create a new memory entry with defaults.
208
211
  */
package/dist/memory.js CHANGED
@@ -296,6 +296,12 @@ export function confidenceLabel(entry, now = evalNow()) {
296
296
  export function resolveConfidence(entry, now = evalNow()) {
297
297
  return isAgedOut(entry, now) ? 'stale' : entry.confidence;
298
298
  }
299
+ export const DEFAULT_HALF_LIFE_DAYS = 7;
300
+ // Pinned means keep; raw rows leave only through archiveRawMemory. The SQL twin guards the DELETE itself.
301
+ export const AUTO_DELETABLE_SQL = "pinned = 0 AND kind != 'raw'";
302
+ export function canAutoDelete(entry) {
303
+ return !entry.pinned && entry.kind !== 'raw';
304
+ }
299
305
  /**
300
306
  * Create a new memory entry with defaults.
301
307
  */
@@ -314,7 +320,7 @@ export function createMemory(content, options = {}) {
314
320
  const emotional_valence = options.emotional_valence ?? inferValence(tags);
315
321
  const schema_fit = options.schema_fit ?? 0.5;
316
322
  const partial = { tags, schema_fit };
317
- const half_life_days = deriveHalfLife(options.baseHalfLifeDays ?? 7, partial);
323
+ const half_life_days = deriveHalfLife(options.baseHalfLifeDays ?? DEFAULT_HALF_LIFE_DAYS, partial);
318
324
  const entry = {
319
325
  id: generateId(layer === Layer.Semantic ? 'sem' : 'mem'),
320
326
  created: now,
@@ -14,6 +14,7 @@
14
14
  */
15
15
  import { Layer } from './memory.js';
16
16
  import { loadAllEntries, readEntry, writeEntry } from './store.js';
17
+ import { redactSecrets } from './secret-detect.js';
17
18
  const REFINED_TAG = 'llm-refined';
18
19
  const CONSOLIDATED_MARKERS = [
19
20
  '[Consolidated from',
@@ -29,7 +30,7 @@ export async function refineSemanticMemory(merged, sources, opts) {
29
30
  const fetchFn = opts.fetcher ?? fetch;
30
31
  const sourceBlock = sources
31
32
  .slice(0, 8)
32
- .map((s, i) => `[source ${i + 1}] ${s.content.slice(0, 400)}`)
33
+ .map((s, i) => `[source ${i + 1}] ${redactSecrets(s.content).slice(0, 400)}`)
33
34
  .join('\n\n');
34
35
  const prompt = `You are refining a semantic memory in an agent's memory store. The rule-based consolidator merged several related episodic memories into one, but the output is clumsy. Produce a single coherent semantic memory that captures the underlying principle.
35
36
 
@@ -41,7 +42,7 @@ Rules:
41
42
  - Do NOT include the "[Consolidated from N ...]" marker.
42
43
 
43
44
  Current merged content:
44
- ${merged}
45
+ ${redactSecrets(merged)}
45
46
 
46
47
  Source memories (up to 8 shown):
47
48
  ${sourceBlock}`;
@@ -1,4 +1,5 @@
1
1
  import { crossEncoderReranker } from './cross-encoder.js';
2
+ import { redactSecrets } from '../secret-detect.js';
2
3
  const ENDPOINT = 'https://api.typesafe.ai/v1/systemone';
3
4
  const DEFAULT_TIMEOUT_MS = 5_000;
4
5
  const TRUNCATE_CHARS = 1200;
@@ -32,8 +33,8 @@ async function requestScores(query, head) {
32
33
  const key = process.env.TYPESAFE_API_KEY;
33
34
  if (!key)
34
35
  throw new Error('TYPESAFE_API_KEY not set');
35
- const lines = head.map((r, i) => `[${i + 1}] ${truncate(r.entry.content, TRUNCATE_CHARS)}`);
36
- const state = `Query: ${query}\n\nNumbered candidate memories from an AI coding agent's project store:\n\n${lines.join('\n\n')}`;
36
+ const lines = head.map((r, i) => `[${i + 1}] ${truncate(redactSecrets(r.entry.content), TRUNCATE_CHARS)}`);
37
+ const state = `Query: ${redactSecrets(query)}\n\nNumbered candidate memories from an AI coding agent's project store:\n\n${lines.join('\n\n')}`;
37
38
  const questions = {};
38
39
  for (let i = 1; i <= head.length; i++) {
39
40
  questions[`c${i}`] = {
@@ -1,3 +1,4 @@
1
+ import { redactSecrets } from '../secret-detect.js';
1
2
  const DEFAULT_TIMEOUT_MS = 30_000;
2
3
  /**
3
4
  * Track 3 reranker: listwise LLM rerank. Uses a customer-supplied
@@ -21,8 +22,8 @@ export const llmReranker = async (query, results, options) => {
21
22
  const head = results.slice(0, topK);
22
23
  const prompt = [
23
24
  `Rerank the candidates below by relevance to the query. Output a JSON array of indices (zero-indexed) in best-first order.`,
24
- `Query: ${query}`,
25
- ...head.map((r, i) => `[${i}] ${r.entry.content}`),
25
+ `Query: ${redactSecrets(query)}`,
26
+ ...head.map((r, i) => `[${i}] ${redactSecrets(r.entry.content)}`),
26
27
  `Output format: [<int>, <int>, ...] with all ${head.length} indices.`,
27
28
  ].join('\n');
28
29
  const timeoutMs = Number.parseInt(process.env.HIPPO_LLM_RERANKER_TIMEOUT_MS ?? '', 10) || DEFAULT_TIMEOUT_MS;
package/dist/search.js CHANGED
@@ -237,8 +237,8 @@ export async function hybridSearch(query, entries, options = {}) {
237
237
  if (options.hippoRoot) {
238
238
  try {
239
239
  const provider = resolveEmbeddingProvider(options.hippoRoot);
240
- if (provider.isAvailable() && !embeddingModelRequiresReindex(options.hippoRoot, provider.id)) {
241
- const idx = loadEmbeddingIndex(options.hippoRoot);
240
+ const idx = provider.isAvailable() ? loadEmbeddingIndex(options.hippoRoot) : {};
241
+ if (provider.isAvailable() && !embeddingModelRequiresReindex(options.hippoRoot, provider.id, idx)) {
242
242
  // Only spend a (possibly paid, off-box) query embedding when at least one
243
243
  // of THIS search's entries has a cached vector to compare against. An
244
244
  // index of only orphaned / out-of-scope vectors yields a meaningless
@@ -511,7 +511,7 @@ export async function hybridSearch(query, entries, options = {}) {
511
511
  const head = ordered.slice(0, topK);
512
512
  const tail = ordered.slice(topK);
513
513
  const rerankInputWithRank = head.map((r, i) => ({ ...r, preRerankRank: i + 1 }));
514
- const reranked = await options.reranker(query, rerankInputWithRank, options.rerankerOptions);
514
+ const reranked = await options.reranker(query, rerankInputWithRank, { ...options.rerankerOptions, topK });
515
515
  const withPostRank = reranked.map((r, i) => ({ ...r, postRerankRank: i + 1 }));
516
516
  ordered = [...withPostRank, ...tail];
517
517
  }
package/dist/server.d.ts CHANGED
@@ -15,6 +15,8 @@ export interface ServeOpts {
15
15
  hippoRoot: string;
16
16
  port?: number;
17
17
  host?: string;
18
+ /** Stop and exit on SIGINT/SIGTERM. Only `hippo serve` owns the process, so only it sets this. */
19
+ handleSignals?: boolean;
18
20
  }
19
21
  /**
20
22
  * Recognise loopback remote addresses. Node reports IPv6-mapped IPv4 as
@@ -22,6 +24,9 @@ export interface ServeOpts {
22
24
  * the bare v4 and v6 loopbacks. Anything else is treated as remote.
23
25
  */
24
26
  export declare function isLoopback(remoteAddress: string | undefined): boolean;
27
+ export declare const LOOPBACK_HOST_HEADER: RegExp;
28
+ /** A browser request sent by another site. Non-browser clients send neither header and pass. */
29
+ export declare function isCrossSite(req: IncomingMessage): boolean;
25
30
  /**
26
31
  * Rate-limit key for a request. Defaults to the socket's remote address.
27
32
  *
package/dist/server.js CHANGED
@@ -22,7 +22,7 @@ export function __resetSessionRecallHistoryHttp() {
22
22
  import { PACKAGE_VERSION } from './version.js';
23
23
  import { validateApiKey } from './auth.js';
24
24
  import { createRateLimiter } from './rate-limit.js';
25
- import { remember, recall, RecallContractError, drillDown, assemble, forget, promote, supersede, archiveRaw, authCreate, authList, authRevoke, auditList, outcome, outcomeForLastRecall, getContext, sleep, adminActor, } from './api.js';
25
+ import { remember, retrieve, RecallContractError, ForbiddenError, drillDown, assemble, forget, promote, supersede, archiveRaw, authCreate, authList, authRevoke, auditList, outcome, outcomeForLastRecall, getContext, sleep, adminActor, } from './api.js';
26
26
  import { buildGraphModel } from './graph-view.js';
27
27
  import { MAX_ENTITY_NAME_LEN } from './graph.js';
28
28
  import { savePrediction, closePrediction, loadPredictionById, loadPredictionsByClass, loadOpenPredictions, computePredictionBaserate, VALID_CLOSURE_STATES, } from './predictions.js';
@@ -289,10 +289,13 @@ async function parseJsonBody(req) {
289
289
  * - /unknown/i → 404 (auth_revoke on unknown key_id)
290
290
  * - /already superseded/i → 409 (chain conflict)
291
291
  * - /not raw/i → 400 (archive_raw on non-raw row)
292
- * Everything else maps to 400 (bad input).
292
+ * ForbiddenError maps to 403; everything else to 400 (bad input).
293
293
  */
294
294
  function mapApiError(err) {
295
295
  const message = err instanceof Error ? err.message : String(err);
296
+ if (err instanceof ForbiddenError) {
297
+ return { status: 403, message };
298
+ }
296
299
  const lower = message.toLowerCase();
297
300
  if (/not found/.test(lower) || /^unknown /.test(lower)) {
298
301
  return { status: 404, message };
@@ -354,6 +357,25 @@ export function isLoopback(remoteAddress) {
354
357
  return true;
355
358
  return false;
356
359
  }
360
+ // Any other Host on a loopback socket is DNS rebinding: a hostile page resolved to 127.0.0.1.
361
+ export const LOOPBACK_HOST_HEADER = /^(localhost|127\.0\.0\.1|\[::1\])(:\d+)?$/i;
362
+ /** A browser request sent by another site. Non-browser clients send neither header and pass. */
363
+ export function isCrossSite(req) {
364
+ const site = req.headers['sec-fetch-site'];
365
+ if (site !== undefined && site !== 'same-origin' && site !== 'none')
366
+ return true;
367
+ const origin = req.headers.origin;
368
+ return origin !== undefined && origin !== `http://${req.headers.host}`;
369
+ }
370
+ // A browser on this machine is loopback too, so the no-key fallback also needs a local Host and a same-site caller.
371
+ function assertLocalCaller(req) {
372
+ if (!isLoopback(req.socket.remoteAddress))
373
+ throw new HttpError(401, 'auth required');
374
+ const host = req.headers.host;
375
+ if ((host !== undefined && !LOOPBACK_HOST_HEADER.test(host)) || isCrossSite(req)) {
376
+ throw new HttpError(403, 'cross-site or non-local request refused; send an API key');
377
+ }
378
+ }
357
379
  function readAuthHeader(req) {
358
380
  const raw = req.headers['authorization'];
359
381
  if (raw === undefined)
@@ -455,9 +477,7 @@ function buildContextWithAuth(req, hippoRoot) {
455
477
  if (process.env.HIPPO_REQUIRE_AUTH === '1') {
456
478
  throw new HttpError(401, 'auth required');
457
479
  }
458
- if (!isLoopback(req.socket.remoteAddress)) {
459
- throw new HttpError(401, 'auth required');
460
- }
480
+ assertLocalCaller(req);
461
481
  // v1.12.0: loopback fallback is process-local, treat as admin.
462
482
  return {
463
483
  hippoRoot,
@@ -492,9 +512,7 @@ function requireAuth(req, hippoRoot) {
492
512
  if (process.env.HIPPO_REQUIRE_AUTH === '1') {
493
513
  throw new HttpError(401, 'auth required');
494
514
  }
495
- if (!isLoopback(req.socket.remoteAddress)) {
496
- throw new HttpError(401, 'auth required');
497
- }
515
+ assertLocalCaller(req);
498
516
  }
499
517
  function getString(obj, key) {
500
518
  const v = obj[key];
@@ -632,10 +650,7 @@ async function handleRequest(req, res, opts, startedAt, limiter) {
632
650
  throw new HttpError(400, 'q is required');
633
651
  }
634
652
  const limitRaw = query.get('limit');
635
- const limit = limitRaw === null ? undefined : Number(limitRaw);
636
- if (limit !== undefined && (!Number.isFinite(limit) || limit <= 0)) {
637
- throw new HttpError(400, 'limit must be a positive number');
638
- }
653
+ const limit = limitRaw === null ? undefined : parseListLimit(limitRaw);
639
654
  const mode = query.get('mode');
640
655
  if (mode !== null && mode !== 'bm25' && mode !== 'hybrid' && mode !== 'physics') {
641
656
  throw new HttpError(400, "mode must be 'bm25', 'hybrid', or 'physics'");
@@ -671,11 +686,12 @@ async function handleRequest(req, res, opts, startedAt, limiter) {
671
686
  const summarizeOverflow = summarizeOverflowRaw === null
672
687
  ? undefined
673
688
  : (summarizeOverflowRaw === '1' || summarizeOverflowRaw === 'true');
674
- // v1.7.2 T4: forward as Number(...) — NaN, 0, negative all reach
675
- // recall() which throws RecallContractError with code='invalid_scorer_window'.
676
- // No transport-side validation; recall() owns the contract.
689
+ // recall() owns the shape rule (NaN, 0 and negatives throw invalid_scorer_window); the transport caps remote cost.
677
690
  const scorerWindowRaw = query.get('scorer_window');
678
691
  const scorerWindow = scorerWindowRaw === null ? undefined : Number(scorerWindowRaw);
692
+ if (scorerWindow !== undefined && scorerWindow > 1000) {
693
+ throw new HttpError(400, 'scorer_window must be <= 1000');
694
+ }
679
695
  // v1.7.4: session_id for the dlPFC goal-stack boost. 256-char cap mirrors
680
696
  // fresh_tail_session_id (above). Trim then drop if empty so api.recall
681
697
  // sees undefined when the param is omitted or whitespace-only.
@@ -757,7 +773,7 @@ async function handleRequest(req, res, opts, startedAt, limiter) {
757
773
  recallExtra.recallHistory = httpRecallHistory;
758
774
  if (explain)
759
775
  recallExtra.explain = explain;
760
- const result = recall(ctx, {
776
+ const result = await retrieve(ctx, {
761
777
  query: q,
762
778
  limit,
763
779
  mode: mode ?? undefined,
@@ -1038,7 +1054,8 @@ async function handleRequest(req, res, opts, startedAt, limiter) {
1038
1054
  // Tenant scope (Episode A follow-up tracked in TODOS.md): api.sleep operates
1039
1055
  // on the WHOLE hippoRoot (cross-tenant by design, matching CLI cmdSleep).
1040
1056
  // The loopback-only guard is the trust boundary today. Future non-loopback
1041
- // serving needs an admin-role gate before exposing this route.
1057
+ // serving must also zero the cross-tenant counters for other tenants
1058
+ // (D1 in docs/decisions/2026-05-24-blocked-items.md).
1042
1059
  if (method === 'POST' && path === '/v1/sleep') {
1043
1060
  // Defensive per-request loopback guard. Uses the canonical isLoopback()
1044
1061
  // helper above so any future extension (additional mapped/IPv6 forms,
@@ -1084,11 +1101,7 @@ async function handleRequest(req, res, opts, startedAt, limiter) {
1084
1101
  }
1085
1102
  // v1.12.3: optional body.role mirrors the --role CLI flag. Validated
1086
1103
  // strictly — anything other than 'admin'|'member' is a 400 (no silent
1087
- // fallback to admin). Required note: admin Bearer can mint a member
1088
- // key for the same tenant; member Bearer minting an admin key is NOT
1089
- // blocked here today (auth_create is currently unaudited per the A5 v2
1090
- // note in authCreate doc). The HTTP layer trusts the buildContextWithAuth
1091
- // role check at admin-gated routes; mint surface remains permissive.
1104
+ // fallback to admin). authCreate refuses a member caller with a 403.
1092
1105
  const roleRaw = body['role'];
1093
1106
  let role;
1094
1107
  if (roleRaw !== undefined) {
@@ -1128,10 +1141,9 @@ async function handleRequest(req, res, opts, startedAt, limiter) {
1128
1141
  sendJson(res, 200, result);
1129
1142
  return;
1130
1143
  }
1131
- // DELETE /v1/auth/keys/:keyId — revoke. authRevoke throws "Unknown key_id"
1132
- // for missing OR cross-tenant keys (no info leak), which mapApiError
1133
- // converts to 404. We return 200 with the result body rather than 204 to
1134
- // surface revokedAt to the caller.
1144
+ // DELETE /v1/auth/keys/:keyId — revoke. Missing or cross-tenant keys are 404
1145
+ // (no info leak); a member key targeting any key but its own is 403.
1146
+ // 200 with the body rather than 204 so the caller sees revokedAt.
1135
1147
  const keyMatch = matchPath('/v1/auth/keys/:keyId', path);
1136
1148
  if (method === 'DELETE' && keyMatch) {
1137
1149
  validateIdSegment(keyMatch.keyId, 'key id');
@@ -3228,9 +3240,7 @@ export async function serve(opts) {
3228
3240
  server.close(() => resolve());
3229
3241
  });
3230
3242
  };
3231
- // Skip signal handlers under vitest so each test run does not register a
3232
- // stray SIGTERM/SIGINT listener that survives until the runner exits.
3233
- if (!process.env.VITEST) {
3243
+ if (opts.handleSignals) {
3234
3244
  let shuttingDown = false;
3235
3245
  const gracefulShutdown = async (signal) => {
3236
3246
  if (shuttingDown)
package/dist/shared.js CHANGED
@@ -153,7 +153,7 @@ export async function searchBothHybrid(query, localRoot, globalRoot, options = {
153
153
  if (!fs.existsSync(root))
154
154
  return [];
155
155
  return recallScope
156
- ? loadRecallSearchEntries(root, query, searchWindow, tenantId, recallScope.requested, recallScope.additive ? 'additive' : 'exact')
156
+ ? loadRecallSearchEntries(root, query, searchWindow, tenantId, recallScope.requested, recallScope.additive ? 'additive' : 'exact', Boolean(includeSuperseded) || Boolean(asOf))
157
157
  : loadSearchEntries(root, query, searchWindow, tenantId);
158
158
  };
159
159
  let localEntries = loadEntries(localRoot);