hippo-memory 1.71.0 → 1.72.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 (555) hide show
  1. package/README.md +1 -1
  2. package/dist/agent-memories/apply.d.ts +0 -11
  3. package/dist/agent-memories/apply.js +6 -21
  4. package/dist/agent-memories/claude-code.js +1 -1
  5. package/dist/agent-memories/codex.js +1 -1
  6. package/dist/agent-memories/copilot.d.ts +1 -3
  7. package/dist/agent-memories/copilot.js +1 -17
  8. package/dist/agent-memories/project-folds.d.ts +10 -0
  9. package/dist/agent-memories/project-folds.js +34 -0
  10. package/dist/agent-memories/sync.js +8 -6
  11. package/dist/api/assemble.js +1 -1
  12. package/dist/api/audit.d.ts +29 -1
  13. package/dist/api/audit.js +44 -5
  14. package/dist/api/auth.d.ts +7 -4
  15. package/dist/api/auth.js +12 -8
  16. package/dist/api/auto-sleep.d.ts +5 -2
  17. package/dist/api/auto-sleep.js +7 -3
  18. package/dist/api/availability.js +2 -3
  19. package/dist/api/cards.d.ts +42 -0
  20. package/dist/api/cards.js +52 -0
  21. package/dist/api/conflicts.d.ts +28 -0
  22. package/dist/api/conflicts.js +61 -0
  23. package/dist/api/context-render.js +4 -3
  24. package/dist/api/context-select.js +9 -11
  25. package/dist/api/context.js +17 -8
  26. package/dist/api/continuity.d.ts +47 -0
  27. package/dist/api/continuity.js +66 -0
  28. package/dist/api/dormant.js +4 -2
  29. package/dist/api/drill-down.js +1 -1
  30. package/dist/api/embeddings.d.ts +17 -0
  31. package/dist/api/embeddings.js +28 -0
  32. package/dist/api/error-reply.d.ts +8 -0
  33. package/dist/api/error-reply.js +21 -0
  34. package/dist/api/forget.d.ts +6 -2
  35. package/dist/api/forget.js +18 -7
  36. package/dist/api/goals.d.ts +1 -1
  37. package/dist/api/goals.js +5 -9
  38. package/dist/api/index.d.ts +4 -2
  39. package/dist/api/index.js +4 -2
  40. package/dist/api/instruction-detect.js +5 -1
  41. package/dist/api/learn.d.ts +4 -0
  42. package/dist/api/learn.js +53 -27
  43. package/dist/api/ledger-db.d.ts +2 -6
  44. package/dist/api/ledger-db.js +4 -36
  45. package/dist/api/memories.d.ts +19 -1
  46. package/dist/api/memories.js +39 -0
  47. package/dist/api/objects.d.ts +1 -1
  48. package/dist/api/outcome.js +1 -1
  49. package/dist/api/pilot-arm.d.ts +1 -11
  50. package/dist/api/pilot-arm.js +5 -45
  51. package/dist/api/predictions.d.ts +3 -1
  52. package/dist/api/predictions.js +6 -0
  53. package/dist/api/projects.d.ts +21 -0
  54. package/dist/api/projects.js +24 -0
  55. package/dist/api/promote.js +1 -1
  56. package/dist/api/prompt-hook.d.ts +15 -2
  57. package/dist/api/prompt-hook.js +44 -4
  58. package/dist/api/recall-cli.d.ts +3 -0
  59. package/dist/api/recall-cli.js +13 -4
  60. package/dist/api/recall-core.js +3 -3
  61. package/dist/api/recall-pipeline.js +6 -7
  62. package/dist/api/recall-request.js +1 -1
  63. package/dist/api/recall.d.ts +0 -3
  64. package/dist/api/recall.js +19 -44
  65. package/dist/api/remember-local.js +1 -1
  66. package/dist/api/remember.d.ts +2 -1
  67. package/dist/api/remember.js +3 -2
  68. package/dist/api/session-end.d.ts +22 -0
  69. package/dist/api/session-end.js +101 -0
  70. package/dist/api/sleep-run.d.ts +1 -1
  71. package/dist/api/sleep-run.js +13 -9
  72. package/dist/api/status.d.ts +24 -0
  73. package/dist/api/status.js +40 -0
  74. package/dist/api/store-init.d.ts +4 -0
  75. package/dist/api/store-init.js +9 -0
  76. package/dist/api/tokens.js +1 -1
  77. package/dist/api/types.d.ts +2 -0
  78. package/dist/api/types.js +6 -1
  79. package/dist/capture/caller-session.d.ts +0 -3
  80. package/dist/capture/caller-session.js +1 -12
  81. package/dist/capture/capture-error.d.ts +2 -2
  82. package/dist/capture/capture-error.js +23 -18
  83. package/dist/capture/codex-patch.js +3 -1
  84. package/dist/capture/command.js +36 -95
  85. package/dist/capture/compact.js +10 -5
  86. package/dist/capture/compaction-items-caller.js +11 -20
  87. package/dist/capture/compaction-record.d.ts +7 -93
  88. package/dist/capture/compaction-record.js +29 -426
  89. package/dist/capture/compaction-spool.d.ts +4 -2
  90. package/dist/capture/compaction-spool.js +20 -6
  91. package/dist/capture/copilot-transcript.d.ts +2 -1
  92. package/dist/capture/copilot-transcript.js +2 -1
  93. package/dist/capture/extract.d.ts +5 -2
  94. package/dist/capture/extract.js +4 -3
  95. package/dist/capture/failure-caller.d.ts +1 -1
  96. package/dist/capture/failure-caller.js +21 -16
  97. package/dist/capture/failure-reading.d.ts +2 -1
  98. package/dist/capture/failure-reading.js +7 -9
  99. package/dist/capture/pre-compact-caller.js +5 -4
  100. package/dist/capture/session-digest.js +4 -3
  101. package/dist/capture/session-end-caller.js +2 -1
  102. package/dist/capture/session-worker.js +3 -4
  103. package/dist/capture/transcript.d.ts +2 -1
  104. package/dist/capture/transcript.js +6 -3
  105. package/dist/capture/working-state.d.ts +4 -2
  106. package/dist/capture/working-state.js +4 -2
  107. package/dist/cli/audit-prune.d.ts +0 -25
  108. package/dist/cli/audit-prune.js +1 -27
  109. package/dist/cli/audit.js +13 -13
  110. package/dist/cli/auth.d.ts +1 -1
  111. package/dist/cli/auth.js +12 -13
  112. package/dist/cli/briefs.d.ts +1 -4
  113. package/dist/cli/briefs.js +37 -292
  114. package/dist/cli/card.js +41 -33
  115. package/dist/cli/client.d.ts +5 -0
  116. package/dist/cli/client.js +24 -8
  117. package/dist/cli/compact-resume-payload.js +1 -1
  118. package/dist/cli/context.d.ts +1 -1
  119. package/dist/cli/context.js +12 -12
  120. package/dist/cli/continuity.d.ts +1 -1
  121. package/dist/cli/continuity.js +36 -39
  122. package/dist/cli/curate.d.ts +1 -1
  123. package/dist/cli/curate.js +23 -27
  124. package/dist/cli/dag.d.ts +1 -1
  125. package/dist/cli/dag.js +23 -8
  126. package/dist/cli/decisions.d.ts +0 -2
  127. package/dist/cli/decisions.js +4 -281
  128. package/dist/cli/eval.d.ts +1 -1
  129. package/dist/cli/eval.js +12 -13
  130. package/dist/cli/explain.d.ts +1 -1
  131. package/dist/cli/explain.js +21 -25
  132. package/dist/cli/flag-values.js +16 -6
  133. package/dist/cli/flags.d.ts +0 -289
  134. package/dist/cli/flags.js +3 -138
  135. package/dist/cli/graph.d.ts +3 -0
  136. package/dist/cli/graph.js +83 -0
  137. package/dist/cli/hook-runtime.d.ts +3 -5
  138. package/dist/cli/hook-runtime.js +7 -34
  139. package/dist/cli/incidents.d.ts +3 -0
  140. package/dist/cli/incidents.js +112 -0
  141. package/dist/cli/init.d.ts +1 -1
  142. package/dist/cli/init.js +28 -19
  143. package/dist/cli/install-steps.d.ts +2 -1
  144. package/dist/cli/install-steps.js +11 -3
  145. package/dist/cli/last-sleep.js +1 -1
  146. package/dist/cli/lenient-id.d.ts +2 -0
  147. package/dist/cli/lenient-id.js +13 -0
  148. package/dist/cli/maintenance.d.ts +3 -3
  149. package/dist/cli/maintenance.js +29 -30
  150. package/dist/cli/notes.d.ts +2 -0
  151. package/dist/cli/notes.js +41 -0
  152. package/dist/cli/open-browser.d.ts +14 -0
  153. package/dist/cli/open-browser.js +26 -0
  154. package/dist/cli/playbooks.d.ts +3 -4
  155. package/dist/cli/playbooks.js +126 -290
  156. package/dist/cli/predictions.d.ts +3 -0
  157. package/dist/cli/predictions.js +175 -0
  158. package/dist/cli/print.js +2 -1
  159. package/dist/cli/projects.js +30 -22
  160. package/dist/cli/quality-repair-once.d.ts +1 -1
  161. package/dist/cli/quality-repair-once.js +6 -5
  162. package/dist/cli/recall.js +13 -7
  163. package/dist/cli/refine-llm.js +17 -17
  164. package/dist/cli/remember.js +12 -12
  165. package/dist/cli/scheduler.js +2 -1
  166. package/dist/cli/serve.js +3 -3
  167. package/dist/cli/session-hooks.d.ts +1 -1
  168. package/dist/cli/session-hooks.js +45 -115
  169. package/dist/cli/setup.d.ts +1 -1
  170. package/dist/cli/setup.js +17 -10
  171. package/dist/cli/shared.d.ts +2 -6
  172. package/dist/cli/shared.js +9 -29
  173. package/dist/cli/sleep-render.js +6 -3
  174. package/dist/cli/sleep.js +6 -37
  175. package/dist/cli/status.d.ts +3 -3
  176. package/dist/cli/status.js +32 -35
  177. package/dist/cli/transfer.d.ts +1 -1
  178. package/dist/cli/transfer.js +33 -31
  179. package/dist/cli/usage.d.ts +0 -79
  180. package/dist/cli/usage.js +1 -695
  181. package/dist/cli/verb-row.d.ts +32 -0
  182. package/dist/cli/verb-row.js +6 -0
  183. package/dist/cli/verbs/health.d.ts +27 -0
  184. package/dist/cli/verbs/health.js +232 -0
  185. package/dist/cli/verbs/memory.d.ts +16 -0
  186. package/dist/cli/verbs/memory.js +244 -0
  187. package/dist/cli/verbs/objects.d.ts +20 -0
  188. package/dist/cli/verbs/objects.js +254 -0
  189. package/dist/cli/verbs/sessions.d.ts +31 -0
  190. package/dist/cli/verbs/sessions.js +312 -0
  191. package/dist/cli/verbs.d.ts +81 -0
  192. package/dist/cli/verbs.js +7 -0
  193. package/dist/cli/versioned-verbs.d.ts +51 -0
  194. package/dist/cli/versioned-verbs.js +79 -0
  195. package/dist/cli.d.ts +1 -272
  196. package/dist/cli.js +23 -313
  197. package/dist/connectors/dlq.d.ts +39 -3
  198. package/dist/connectors/dlq.js +37 -3
  199. package/dist/connectors/github/backfill.js +12 -3
  200. package/dist/connectors/github/dlq.js +31 -73
  201. package/dist/connectors/github/ingest.d.ts +2 -6
  202. package/dist/connectors/github/ingest.js +3 -28
  203. package/dist/connectors/github/webhook.d.ts +2 -1
  204. package/dist/connectors/github/webhook.js +33 -2
  205. package/dist/connectors/ingest.d.ts +10 -0
  206. package/dist/connectors/ingest.js +28 -0
  207. package/dist/connectors/slack/dlq.js +49 -71
  208. package/dist/connectors/slack/ingest.d.ts +2 -6
  209. package/dist/connectors/slack/ingest.js +2 -27
  210. package/dist/connectors/slack/webhook.d.ts +2 -1
  211. package/dist/connectors/slack/webhook.js +2 -1
  212. package/dist/consolidate/conflicts.js +1 -1
  213. package/dist/consolidate/dag.js +15 -9
  214. package/dist/consolidate/dedupe.d.ts +7 -0
  215. package/dist/consolidate/dedupe.js +5 -3
  216. package/dist/consolidate/llm-passes.js +45 -19
  217. package/dist/consolidate/memory-value.js +2 -1
  218. package/dist/consolidate/merge.js +2 -2
  219. package/dist/consolidate/physics-pass.d.ts +6 -1
  220. package/dist/consolidate/physics-pass.js +26 -6
  221. package/dist/consolidate/replay.js +11 -4
  222. package/dist/consolidate/sleep.js +8 -5
  223. package/dist/consolidate/traces.js +3 -3
  224. package/dist/core/ablation.d.ts +10 -0
  225. package/dist/core/ablation.js +12 -0
  226. package/dist/{sharing/scope.d.ts → core/active-scope.d.ts} +1 -1
  227. package/dist/{sharing/scope.js → core/active-scope.js} +1 -1
  228. package/dist/core/ambient.d.ts +4 -0
  229. package/dist/core/ambient.js +39 -18
  230. package/dist/core/api-errors.d.ts +16 -0
  231. package/dist/core/api-errors.js +20 -0
  232. package/dist/core/card.js +3 -1
  233. package/dist/core/compaction-timing.d.ts +7 -0
  234. package/dist/core/compaction-timing.js +9 -0
  235. package/dist/core/config.d.ts +1 -1
  236. package/dist/core/config.js +7 -2
  237. package/dist/core/cosine.d.ts +3 -0
  238. package/dist/core/cosine.js +19 -0
  239. package/dist/{store → core}/markdown.d.ts +1 -1
  240. package/dist/{store → core}/markdown.js +2 -2
  241. package/dist/core/memory-audit.d.ts +17 -0
  242. package/dist/core/memory-audit.js +54 -0
  243. package/dist/core/memory.d.ts +10 -4
  244. package/dist/core/memory.js +25 -18
  245. package/dist/{consolidate → core}/merged-row.d.ts +1 -1
  246. package/dist/{consolidate → core}/merged-row.js +1 -1
  247. package/dist/core/physics.js +6 -16
  248. package/dist/core/prediction.d.ts +3 -0
  249. package/dist/core/prediction.js +6 -0
  250. package/dist/core/project-identity.d.ts +3 -0
  251. package/dist/core/project-identity.js +10 -1
  252. package/dist/{store → core}/recall-scope.d.ts +1 -10
  253. package/dist/{store → core}/recall-scope.js +2 -19
  254. package/dist/db/busy.d.ts +2 -7
  255. package/dist/db/busy.js +21 -26
  256. package/dist/db/connect.d.ts +0 -1
  257. package/dist/db/connect.js +5 -6
  258. package/dist/db/index.d.ts +3 -3
  259. package/dist/db/index.js +3 -3
  260. package/dist/db/migrations/index.d.ts +1 -1
  261. package/dist/db/migrations/index.js +3 -1
  262. package/dist/db/migrations/v55.d.ts +3 -0
  263. package/dist/db/migrations/v55.js +17 -0
  264. package/dist/db/open.d.ts +0 -2
  265. package/dist/db/open.js +6 -10
  266. package/dist/db/physics-state.js +3 -3
  267. package/dist/db/tables.d.ts +2 -0
  268. package/dist/db/tables.js +6 -2
  269. package/dist/db/vector-store.js +17 -4
  270. package/dist/db/wal-checkpointer.js +2 -1
  271. package/dist/doctor.js +11 -8
  272. package/dist/embeddings/local.d.ts +62 -0
  273. package/dist/embeddings/local.js +146 -0
  274. package/dist/{store/embeddings → embeddings}/provider.d.ts +5 -2
  275. package/dist/{store/embeddings → embeddings}/provider.js +18 -15
  276. package/dist/embeddings/transformers.d.ts +26 -0
  277. package/dist/embeddings/transformers.js +89 -0
  278. package/dist/entry/session-text.d.ts +1 -1
  279. package/dist/entry/session-text.js +1 -1
  280. package/dist/eval/eval-suite.js +17 -11
  281. package/dist/eval/eval.js +1 -1
  282. package/dist/eval/judgment.js +11 -4
  283. package/dist/graph/extract.js +3 -3
  284. package/dist/graph/recall.js +1 -1
  285. package/dist/graph/view.js +13 -5
  286. package/dist/graph/write.d.ts +1 -1
  287. package/dist/graph/write.js +9 -9
  288. package/dist/hooks/codex-session.js +3 -2
  289. package/dist/hooks/codex-wrapper.js +2 -1
  290. package/dist/hooks/json-hooks.js +4 -2
  291. package/dist/hooks/opencode.js +8 -5
  292. package/dist/hooks/shared.d.ts +0 -6
  293. package/dist/hooks/shared.js +4 -15
  294. package/dist/importers/core.js +1 -1
  295. package/dist/importers/sources.js +5 -4
  296. package/dist/importers/vault.js +7 -5
  297. package/dist/index.d.ts +3 -2
  298. package/dist/index.js +3 -2
  299. package/dist/learn/autolearn.d.ts +10 -2
  300. package/dist/learn/autolearn.js +32 -12
  301. package/dist/learn/churn-git.js +13 -4
  302. package/dist/learn/extract.d.ts +3 -2
  303. package/dist/learn/extract.js +6 -4
  304. package/dist/learn/forward-claim-detector.js +4 -2
  305. package/dist/learn/invalidation.js +10 -6
  306. package/dist/mcp/admin-tools.d.ts +2 -2
  307. package/dist/mcp/admin-tools.js +27 -48
  308. package/dist/mcp/format.js +3 -2
  309. package/dist/mcp/framing.d.ts +1 -0
  310. package/dist/mcp/framing.js +1 -1
  311. package/dist/mcp/memory-tools.js +3 -1
  312. package/dist/mcp/protocol.d.ts +11 -7
  313. package/dist/mcp/protocol.js +25 -12
  314. package/dist/mcp/recall-tools.js +12 -9
  315. package/dist/mcp/request.js +2 -1
  316. package/dist/mcp/stdio.js +24 -8
  317. package/dist/mcp/tool-args.js +2 -1
  318. package/dist/mcp/tools.js +44 -21
  319. package/dist/objects/customer-notes.d.ts +2 -2
  320. package/dist/objects/decisions.d.ts +2 -2
  321. package/dist/objects/descriptor.d.ts +1 -1
  322. package/dist/objects/incidents.d.ts +2 -2
  323. package/dist/objects/lifecycle.d.ts +1 -1
  324. package/dist/objects/lifecycle.js +2 -1
  325. package/dist/objects/policies.d.ts +2 -2
  326. package/dist/objects/policies.js +2 -1
  327. package/dist/objects/processes.d.ts +2 -2
  328. package/dist/objects/project-briefs.d.ts +2 -2
  329. package/dist/objects/project-briefs.js +2 -3
  330. package/dist/objects/skills.d.ts +2 -2
  331. package/dist/postinstall.js +1 -1
  332. package/dist/rerankers/clef.js +3 -6
  333. package/dist/rerankers/cross-encoder.d.ts +31 -2
  334. package/dist/rerankers/cross-encoder.js +63 -97
  335. package/dist/rerankers/index.js +5 -4
  336. package/dist/rerankers/jev.js +2 -4
  337. package/dist/rerankers/outage-warning.d.ts +3 -1
  338. package/dist/rerankers/outage-warning.js +5 -3
  339. package/dist/search/bm25-search.js +1 -1
  340. package/dist/search/boosts.js +1 -1
  341. package/dist/search/embed-memories.d.ts +9 -0
  342. package/dist/search/embed-memories.js +377 -0
  343. package/dist/search/goal-boost.d.ts +1 -1
  344. package/dist/search/goal-boost.js +6 -3
  345. package/dist/search/hybrid.d.ts +9 -2
  346. package/dist/search/hybrid.js +9 -4
  347. package/dist/search/physics-search.d.ts +6 -3
  348. package/dist/search/physics-search.js +22 -15
  349. package/dist/search/rerank.js +1 -1
  350. package/dist/search/vector.d.ts +8 -6
  351. package/dist/search/vector.js +10 -13
  352. package/dist/server/auth.d.ts +2 -0
  353. package/dist/server/auth.js +17 -7
  354. package/dist/server/boot.js +7 -66
  355. package/dist/server/client-ip.d.ts +0 -2
  356. package/dist/server/client-ip.js +3 -2
  357. package/dist/server/deadline.d.ts +0 -2
  358. package/dist/server/deadline.js +1 -7
  359. package/dist/server/defaults.d.ts +1 -0
  360. package/dist/server/defaults.js +1 -0
  361. package/dist/server/mcp-http.js +16 -11
  362. package/dist/server/request.js +7 -2
  363. package/dist/server/route-checks.d.ts +7 -0
  364. package/dist/server/route-checks.js +42 -0
  365. package/dist/server/route-table.d.ts +9 -8
  366. package/dist/server/route-table.js +127 -132
  367. package/dist/server/routes/admin.d.ts +8 -8
  368. package/dist/server/routes/admin.js +13 -21
  369. package/dist/server/routes/customer-notes.d.ts +6 -6
  370. package/dist/server/routes/customer-notes.js +1 -2
  371. package/dist/server/routes/decisions.d.ts +6 -6
  372. package/dist/server/routes/decisions.js +2 -4
  373. package/dist/server/routes/incidents.d.ts +6 -6
  374. package/dist/server/routes/incidents.js +2 -5
  375. package/dist/server/routes/memories.d.ts +9 -9
  376. package/dist/server/routes/memories.js +15 -38
  377. package/dist/server/routes/object-routes.d.ts +6 -6
  378. package/dist/server/routes/object-routes.js +4 -7
  379. package/dist/server/routes/policies.d.ts +7 -7
  380. package/dist/server/routes/policies.js +2 -4
  381. package/dist/server/routes/predictions.d.ts +6 -6
  382. package/dist/server/routes/predictions.js +9 -13
  383. package/dist/server/routes/processes.d.ts +6 -6
  384. package/dist/server/routes/processes.js +1 -2
  385. package/dist/server/routes/project-briefs.d.ts +7 -7
  386. package/dist/server/routes/project-briefs.js +2 -4
  387. package/dist/server/routes/recall.d.ts +5 -5
  388. package/dist/server/routes/recall.js +6 -10
  389. package/dist/server/routes/skills.d.ts +7 -7
  390. package/dist/server/routes/skills.js +2 -4
  391. package/dist/server/server-detect.d.ts +7 -1
  392. package/dist/server/server-detect.js +36 -11
  393. package/dist/server/sleep-child.js +26 -7
  394. package/dist/server/sleep-offload.d.ts +11 -5
  395. package/dist/server/sleep-offload.js +17 -11
  396. package/dist/server/types.d.ts +22 -10
  397. package/dist/server/validation.d.ts +1 -2
  398. package/dist/server/validation.js +6 -11
  399. package/dist/server.d.ts +5 -4
  400. package/dist/server.js +5 -4
  401. package/dist/sharing/global-store.js +2 -2
  402. package/dist/sharing/global-sync.js +38 -45
  403. package/dist/sharing/project-merge.d.ts +7 -14
  404. package/dist/sharing/project-merge.js +57 -121
  405. package/dist/sharing/search-both.js +1 -1
  406. package/dist/sharing/share.d.ts +2 -0
  407. package/dist/sharing/share.js +37 -34
  408. package/dist/store/ambient.js +5 -6
  409. package/dist/store/audit-event.d.ts +1 -1
  410. package/dist/store/audit.d.ts +0 -16
  411. package/dist/store/audit.js +4 -55
  412. package/dist/store/auth.d.ts +4 -2
  413. package/dist/store/auth.js +7 -3
  414. package/dist/store/candidates.d.ts +3 -2
  415. package/dist/store/candidates.js +42 -23
  416. package/dist/store/capture-write.d.ts +17 -0
  417. package/dist/store/capture-write.js +35 -0
  418. package/dist/store/cards.js +1 -1
  419. package/dist/store/compaction-caller.d.ts +18 -0
  420. package/dist/store/compaction-caller.js +29 -0
  421. package/dist/store/compactions-record.d.ts +115 -0
  422. package/dist/store/compactions-record.js +417 -0
  423. package/dist/store/compactions.js +6 -4
  424. package/dist/store/conflicts.js +7 -4
  425. package/dist/store/connectors/dlq.d.ts +10 -0
  426. package/dist/store/connectors/dlq.js +32 -0
  427. package/dist/store/connectors/github.d.ts +7 -9
  428. package/dist/store/connectors/github.js +30 -53
  429. package/dist/store/connectors/slack.d.ts +2 -11
  430. package/dist/store/connectors/slack.js +18 -47
  431. package/dist/store/dashboard-reads.js +7 -6
  432. package/dist/store/delete-and-batch.d.ts +6 -0
  433. package/dist/store/delete-and-batch.js +36 -3
  434. package/dist/store/delivery-recorder.d.ts +10 -3
  435. package/dist/store/delivery-recorder.js +12 -5
  436. package/dist/store/descendant-page.js +6 -3
  437. package/dist/store/dormant.d.ts +1 -1
  438. package/dist/store/dormant.js +3 -1
  439. package/dist/store/embeddings/index.d.ts +2 -12
  440. package/dist/store/embeddings/index.js +5 -374
  441. package/dist/store/entry-reads.d.ts +13 -4
  442. package/dist/store/entry-reads.js +47 -11
  443. package/dist/store/entry-writes.d.ts +5 -5
  444. package/dist/store/entry-writes.js +32 -12
  445. package/dist/store/failure-log-at.d.ts +8 -0
  446. package/dist/store/failure-log-at.js +16 -0
  447. package/dist/store/failure-log.d.ts +2 -2
  448. package/dist/store/failure-log.js +5 -3
  449. package/dist/{trust → store}/gated-write.d.ts +3 -1
  450. package/dist/{trust → store}/gated-write.js +7 -5
  451. package/dist/store/goals.js +20 -18
  452. package/dist/store/graph-reads.js +4 -2
  453. package/dist/store/graph-writes.d.ts +6 -6
  454. package/dist/store/graph-writes.js +48 -71
  455. package/dist/store/handoffs.d.ts +1 -1
  456. package/dist/store/handoffs.js +6 -6
  457. package/dist/store/index-and-stats.js +2 -2
  458. package/dist/store/key-writes.js +1 -1
  459. package/dist/store/ledger-turn.d.ts +9 -0
  460. package/dist/store/ledger-turn.js +38 -0
  461. package/dist/store/mirrors.js +10 -6
  462. package/dist/store/open.d.ts +0 -4
  463. package/dist/store/open.js +40 -53
  464. package/dist/store/pilot-arm.d.ts +12 -0
  465. package/dist/store/pilot-arm.js +42 -0
  466. package/dist/store/port.d.ts +9 -3
  467. package/dist/store/port.js +8 -0
  468. package/dist/store/predictions.d.ts +1 -2
  469. package/dist/store/predictions.js +3 -6
  470. package/dist/store/project-tags.d.ts +62 -0
  471. package/dist/store/project-tags.js +67 -0
  472. package/dist/store/quarantine.js +2 -1
  473. package/dist/store/recall-trace.d.ts +2 -0
  474. package/dist/store/recall-trace.js +18 -2
  475. package/dist/store/rejected-values.js +2 -1
  476. package/dist/store/rejection-probe.d.ts +4 -0
  477. package/dist/store/rejection-probe.js +19 -0
  478. package/dist/store/rejection.d.ts +0 -17
  479. package/dist/store/rejection.js +1 -21
  480. package/dist/store/rule-sql.d.ts +9 -0
  481. package/dist/store/rule-sql.js +21 -2
  482. package/dist/store/search-rows.js +9 -4
  483. package/dist/store/sessions.js +4 -2
  484. package/dist/store/set-aside.d.ts +14 -0
  485. package/dist/store/set-aside.js +23 -0
  486. package/dist/store/sqlite/busy.d.ts +3 -0
  487. package/dist/store/sqlite/busy.js +8 -0
  488. package/dist/store/sqlite/connector-events-group.js +4 -7
  489. package/dist/store/sqlite/connector-writes-group.js +3 -3
  490. package/dist/store/sqlite/entry-writes-group.js +2 -2
  491. package/dist/store/sqlite/error-codec.js +4 -3
  492. package/dist/store/sqlite/executor.js +10 -4
  493. package/dist/store/sqlite/graph-reads-group.js +1 -1
  494. package/dist/store/sqlite/held-connection.d.ts +9 -0
  495. package/dist/store/sqlite/held-connection.js +41 -0
  496. package/dist/store/sqlite/object-rows.d.ts +1 -1
  497. package/dist/store/sqlite/objects-group.d.ts +1 -1
  498. package/dist/store/sqlite/objects-group.js +1 -1
  499. package/dist/store/sqlite/store.js +14 -12
  500. package/dist/store/summaries.d.ts +3 -3
  501. package/dist/store/summaries.js +5 -4
  502. package/dist/store/token-ledger.d.ts +0 -7
  503. package/dist/store/token-ledger.js +0 -35
  504. package/dist/store/vector-index.js +3 -2
  505. package/dist/support-bundle.js +2 -3
  506. package/dist/trust/reject-flow.js +2 -2
  507. package/dist/util/agent-homes.d.ts +15 -0
  508. package/dist/util/agent-homes.js +34 -0
  509. package/dist/util/anthropic-messages.d.ts +3 -0
  510. package/dist/util/anthropic-messages.js +22 -5
  511. package/dist/util/chunked.d.ts +3 -0
  512. package/dist/util/chunked.js +10 -0
  513. package/dist/util/ellipsize.d.ts +3 -0
  514. package/dist/util/ellipsize.js +5 -0
  515. package/dist/util/env.d.ts +2 -1
  516. package/dist/util/env.js +2 -1
  517. package/dist/util/format.d.ts +3 -0
  518. package/dist/util/format.js +6 -0
  519. package/dist/util/hook-payload.d.ts +8 -0
  520. package/dist/util/hook-payload.js +37 -0
  521. package/dist/util/http-retry.d.ts +6 -0
  522. package/dist/util/http-retry.js +43 -5
  523. package/dist/util/http-util.d.ts +0 -8
  524. package/dist/util/http-util.js +10 -21
  525. package/dist/util/limits.d.ts +7 -0
  526. package/dist/util/limits.js +14 -0
  527. package/dist/util/log.d.ts +4 -0
  528. package/dist/util/log.js +9 -0
  529. package/dist/util/request-scope.d.ts +3 -1
  530. package/dist/util/request-scope.js +11 -6
  531. package/dist/util/secret-detect.js +6 -1
  532. package/dist/util/sqlite-blocked.d.ts +2 -0
  533. package/dist/util/sqlite-blocked.js +5 -0
  534. package/dist/util/stream-tee.d.ts +3 -0
  535. package/dist/util/stream-tee.js +35 -0
  536. package/dist/util/time.d.ts +4 -0
  537. package/dist/util/time.js +4 -0
  538. package/dist/util/version.d.ts +1 -1
  539. package/dist/util/version.js +1 -1
  540. package/extensions/openclaw-plugin/openclaw.plugin.json +1 -1
  541. package/extensions/openclaw-plugin/package.json +1 -1
  542. package/openclaw.plugin.json +1 -1
  543. package/package.json +2 -2
  544. package/dist/store/embeddings/local.d.ts +0 -15
  545. package/dist/store/embeddings/local.js +0 -175
  546. /package/dist/{cli → api}/correction-latency.d.ts +0 -0
  547. /package/dist/{cli → api}/correction-latency.js +0 -0
  548. /package/dist/{cli → api}/provenance-coverage.d.ts +0 -0
  549. /package/dist/{cli → api}/provenance-coverage.js +0 -0
  550. /package/dist/{store → core}/object-types.d.ts +0 -0
  551. /package/dist/{store → core}/object-types.js +0 -0
  552. /package/dist/{capture → util}/compaction-items.d.ts +0 -0
  553. /package/dist/{capture → util}/compaction-items.js +0 -0
  554. /package/dist/{store → util}/yaml.d.ts +0 -0
  555. /package/dist/{store → util}/yaml.js +0 -0
@@ -1,19 +1,36 @@
1
1
  /** The one Anthropic Messages client: sends one user prompt and returns the reply text or a typed failure the caller reacts to. */
2
- import { fetchWithRetry, llmTimeoutMs } from './http-retry.js';
2
+ import { fetchWithRetry, llmTimeoutMs, llmTotalMs } from './http-retry.js';
3
3
  import { errorMessage } from './log.js';
4
- import { readCappedJson } from './capped-json.js';
4
+ import { readCappedJson, readCappedText } from './capped-json.js';
5
5
  import { isJsonObject, isJsonString } from './json.js';
6
+ import { redactSecretsStrict } from './secret-detect.js';
6
7
  const MESSAGES_URL = 'https://api.anthropic.com/v1/messages';
7
8
  const API_VERSION = '2023-06-01';
8
9
  const DEFAULT_MODEL = 'claude-sonnet-4-6';
9
10
  // Callers ask for at most 1,200 tokens, a reply of a few KB; 1 MiB leaves room for a verbose envelope.
10
11
  const MAX_REPLY_BYTES = 1024 * 1024;
12
+ // The error body names the cause (bad key, no credit, unknown model) in its first line or two.
13
+ const ERROR_SNIPPET_CHARS = 256;
14
+ const UTF8_MAX_BYTES_PER_CHAR = 4;
15
+ // A bad or missing key, no credit, no permission, an unknown model: every later call gets the same answer.
16
+ const PERMANENT_STATUSES = new Set([401, 402, 403, 404]);
11
17
  /** The first content block's text, trimmed; empty when the reply has no such block. */
12
18
  function firstText(reply) {
13
19
  const first = Array.isArray(reply.content) ? reply.content[0] : undefined;
14
20
  const text = isJsonObject(first) ? first.text : undefined;
15
21
  return isJsonString(text) ? text.trim() : '';
16
22
  }
23
+ /** True for a failure the next call would repeat, so a caller looping over items should stop rather than retry. */
24
+ export function isPermanentFailure(failure) {
25
+ return failure.kind === 'http' && PERMANENT_STATUSES.has(failure.status);
26
+ }
27
+ /** The start of a failed reply's body, on one line with any key-shaped text masked; empty when the body cannot be read. */
28
+ async function errorSnippet(res) {
29
+ // The status is the failure reported; a body that cannot be read only costs its snippet.
30
+ const body = await readCappedText(res, ERROR_SNIPPET_CHARS * UTF8_MAX_BYTES_PER_CHAR).catch(() => '');
31
+ // Masked before the cut, so a key that straddles the cut is never left half shown.
32
+ return redactSecretsStrict(body.replace(/\s+/g, ' ').trim()).slice(0, ERROR_SNIPPET_CHARS);
33
+ }
17
34
  /** The reply's first text block, trimmed (empty when the reply has none); never throws. */
18
35
  export async function sendAnthropicMessage(req) {
19
36
  let res;
@@ -30,13 +47,13 @@ export async function sendAnthropicMessage(req) {
30
47
  max_tokens: req.maxTokens,
31
48
  messages: [{ role: 'user', content: req.prompt }],
32
49
  }),
33
- }, { timeoutMs: llmTimeoutMs(), fetchFn: req.fetcher ?? fetch });
50
+ }, { timeoutMs: llmTimeoutMs(), totalMs: llmTotalMs(), fetchFn: req.fetcher ?? fetch });
34
51
  }
35
52
  catch (err) {
36
53
  return { ok: false, failure: { kind: 'request', message: errorMessage(err) } };
37
54
  }
38
55
  if (!res.ok)
39
- return { ok: false, failure: { kind: 'http', status: res.status } };
56
+ return { ok: false, failure: { kind: 'http', status: res.status, detail: await errorSnippet(res) } };
40
57
  try {
41
58
  const data = await readCappedJson(res, MAX_REPLY_BYTES);
42
59
  if (!isJsonObject(data))
@@ -51,7 +68,7 @@ export async function sendAnthropicMessage(req) {
51
68
  export function describeMessageFailure(failure) {
52
69
  switch (failure.kind) {
53
70
  case 'request': return `request failed: ${failure.message}`;
54
- case 'http': return `HTTP ${failure.status}`;
71
+ case 'http': return failure.detail ? `HTTP ${failure.status}: ${failure.detail}` : `HTTP ${failure.status}`;
55
72
  case 'unreadable': return `unparseable response: ${failure.message}`;
56
73
  }
57
74
  }
@@ -0,0 +1,3 @@
1
+ /** `items` in consecutive slices of at most `size`. */
2
+ export declare function chunked<T>(items: readonly T[], size?: number): T[][];
3
+ //# sourceMappingURL=chunked.d.ts.map
@@ -0,0 +1,10 @@
1
+ /** Ids per `IN (...)` list: far under SQLite's bound-parameter limit, with room for the tenant filter. */
2
+ const ID_CHUNK = 500;
3
+ /** `items` in consecutive slices of at most `size`. */
4
+ export function chunked(items, size = ID_CHUNK) {
5
+ const out = [];
6
+ for (let i = 0; i < items.length; i += size)
7
+ out.push(items.slice(i, i + size));
8
+ return out;
9
+ }
10
+ //# sourceMappingURL=chunked.js.map
@@ -0,0 +1,3 @@
1
+ /** `text` cut to `max` characters plus a marker, or unchanged when it already fits. */
2
+ export declare function truncateWithEllipsis(text: string, max: number, marker?: string): string;
3
+ //# sourceMappingURL=ellipsize.d.ts.map
@@ -0,0 +1,5 @@
1
+ /** `text` cut to `max` characters plus a marker, or unchanged when it already fits. */
2
+ export function truncateWithEllipsis(text, max, marker = '...') {
3
+ return text.length > max ? `${text.slice(0, max)}${marker}` : text;
4
+ }
5
+ //# sourceMappingURL=ellipsize.js.map
@@ -34,6 +34,7 @@ export declare const DEFAULT_TENANT_ID = "default";
34
34
  export declare function envTenant(): string;
35
35
  export declare function envSkipAutoIntegrations(): boolean;
36
36
  export declare function envSkipPostinstall(): boolean;
37
+ export declare function envSkipSchedule(): boolean;
37
38
  export declare function envMcpStdio(): boolean;
38
39
  /** `1` or `true`, so `0` and `false` turn it off; `true` stays on for anyone who set it that way. */
39
40
  export declare function envRequireServer(): boolean;
@@ -54,7 +55,7 @@ export declare function envBodyTimeoutMs(): number | undefined;
54
55
  /** How long POST /v1/sleep lets its consolidation run; server/sleep-offload.ts holds the default. */
55
56
  export declare function envSleepTimeoutMs(): number | undefined;
56
57
  /** How long a /v1 or /mcp request may run before the server answers 504, and a stdio MCP
57
- * request before its timeout error; 0 for no deadline; server/deadline.ts holds the default. */
58
+ * request before its timeout error; 0 for no deadline; util/request-scope.ts holds the default. */
58
59
  export declare function envRequestDeadlineMs(): number | undefined;
59
60
  /** How many store calls may wait for one worker thread before the next is refused; store/sqlite/executor.ts holds the default. */
60
61
  export declare function envStoreQueueMax(): number | undefined;
package/dist/util/env.js CHANGED
@@ -80,6 +80,7 @@ export function envTenant() {
80
80
  // Install and CLI switches.
81
81
  export function envSkipAutoIntegrations() { return isOne('HIPPO_SKIP_AUTO_INTEGRATIONS'); }
82
82
  export function envSkipPostinstall() { return isOne('HIPPO_SKIP_POSTINSTALL'); }
83
+ export function envSkipSchedule() { return isOne('HIPPO_SKIP_SCHEDULE'); }
83
84
  export function envMcpStdio() { return isOne('HIPPO_MCP_STDIO'); }
84
85
  /** `1` or `true`, so `0` and `false` turn it off; `true` stays on for anyone who set it that way. */
85
86
  export function envRequireServer() { return isOneOrTrue('HIPPO_REQUIRE_SERVER'); }
@@ -101,7 +102,7 @@ export function envBodyTimeoutMs() { return positiveInt('HIPPO_BODY_TIMEOUT_MS')
101
102
  /** How long POST /v1/sleep lets its consolidation run; server/sleep-offload.ts holds the default. */
102
103
  export function envSleepTimeoutMs() { return positiveInt('HIPPO_SLEEP_TIMEOUT_MS'); }
103
104
  /** How long a /v1 or /mcp request may run before the server answers 504, and a stdio MCP
104
- * request before its timeout error; 0 for no deadline; server/deadline.ts holds the default. */
105
+ * request before its timeout error; 0 for no deadline; util/request-scope.ts holds the default. */
105
106
  export function envRequestDeadlineMs() { return nonNegativeInt('HIPPO_REQUEST_DEADLINE_MS'); }
106
107
  /** How many store calls may wait for one worker thread before the next is refused; store/sqlite/executor.ts holds the default. */
107
108
  export function envStoreQueueMax() { return positiveInt('HIPPO_STORE_QUEUE_MAX'); }
@@ -0,0 +1,3 @@
1
+ /** A 0..1 fraction as a percentage with one decimal, e.g. 0.256 -> "25.6%". */
2
+ export declare function pct(fraction: number): string;
3
+ //# sourceMappingURL=format.d.ts.map
@@ -0,0 +1,6 @@
1
+ // Number-to-text helpers shared by report and eval output.
2
+ /** A 0..1 fraction as a percentage with one decimal, e.g. 0.256 -> "25.6%". */
3
+ export function pct(fraction) {
4
+ return (fraction * 100).toFixed(1) + '%';
5
+ }
6
+ //# sourceMappingURL=format.js.map
@@ -0,0 +1,8 @@
1
+ /** A hook payload's non-empty `session_id`, or null; with `requiredSource`, also null when its `source` differs. */
2
+ export declare function hookPayloadSessionId(stdinText: string | undefined, requiredSource?: string | null): string | null;
3
+ /** A hook payload's string `field` as sent, or null when the payload or the field is missing or not a string. */
4
+ export declare function hookPayloadString(stdinText: string | undefined, field: string): string | null;
5
+ /** Whether a hook fired inside a sub-agent, the only payload with `agent_id` (https://code.claude.com/docs/en/hooks#common-input-fields).
6
+ * Its `session_id` is the parent's, so a sub-agent's blocks and compactions must not count as the parent's. */
7
+ export declare function isSubagentPayload(stdinText: string | undefined): boolean;
8
+ //# sourceMappingURL=hook-payload.d.ts.map
@@ -0,0 +1,37 @@
1
+ import { isJsonObjectLiteral, isJsonString } from './json.js';
2
+ /** A Claude Code hook payload on stdin as a JSON object; null when empty, malformed or not an object. */
3
+ function parseHookPayload(stdinText) {
4
+ if (!stdinText || stdinText.trim() === '')
5
+ return null;
6
+ try {
7
+ // SAFETY: JSON.parse returns a JSON value by definition.
8
+ const payload = JSON.parse(stdinText.trim());
9
+ return isJsonObjectLiteral(payload) ? payload : null;
10
+ }
11
+ catch {
12
+ // Malformed is one of the null cases the docblock names.
13
+ return null;
14
+ }
15
+ }
16
+ /** A hook payload's non-empty `session_id`, or null; with `requiredSource`, also null when its `source` differs. */
17
+ export function hookPayloadSessionId(stdinText, requiredSource = null) {
18
+ const payload = parseHookPayload(stdinText);
19
+ const sessionId = payload?.session_id;
20
+ if (!payload || !isJsonString(sessionId) || sessionId.trim() === '')
21
+ return null;
22
+ if (requiredSource !== null && payload.source !== requiredSource)
23
+ return null;
24
+ return sessionId;
25
+ }
26
+ /** A hook payload's string `field` as sent, or null when the payload or the field is missing or not a string. */
27
+ export function hookPayloadString(stdinText, field) {
28
+ const value = parseHookPayload(stdinText)?.[field];
29
+ return isJsonString(value) ? value : null;
30
+ }
31
+ /** Whether a hook fired inside a sub-agent, the only payload with `agent_id` (https://code.claude.com/docs/en/hooks#common-input-fields).
32
+ * Its `session_id` is the parent's, so a sub-agent's blocks and compactions must not count as the parent's. */
33
+ export function isSubagentPayload(stdinText) {
34
+ const agentId = parseHookPayload(stdinText)?.agent_id;
35
+ return isJsonString(agentId) && agentId.trim() !== '';
36
+ }
37
+ //# sourceMappingURL=hook-payload.js.map
@@ -4,6 +4,8 @@ export interface RetryPolicy {
4
4
  timeoutMs: number;
5
5
  /** Total attempts including the first. */
6
6
  attempts?: number;
7
+ /** Limit on the whole call, waits included: an attempt is cut short at it and no retry starts whose wait would reach it. */
8
+ totalMs?: number;
7
9
  baseDelayMs?: number;
8
10
  /** A Retry-After longer than this hands the response back, so a caller with its own long pause keeps it. */
9
11
  maxDelayMs?: number;
@@ -15,9 +17,13 @@ export interface RetryPolicy {
15
17
  /** Replaces the timer between attempts. A caller `signal` that aborts still ends the wait at once. */
16
18
  sleep?: (ms: number) => Promise<void>;
17
19
  random?: () => number;
20
+ /** Replaces the clock `totalMs` is measured on. */
21
+ now?: () => number;
18
22
  }
19
23
  /** LLM calls (consolidation refine, DAG summaries, fact extraction) share one budget; `HIPPO_LLM_TIMEOUT_MS` overrides it. */
20
24
  export declare function llmTimeoutMs(): number;
25
+ /** One LLM call with its retries: two full attempts and the longest wait between them, where three slow failures would hold a sleep for 3x the timeout. */
26
+ export declare function llmTotalMs(): number;
21
27
  export declare function isRetryableStatus(status: number): boolean;
22
28
  /** Retry-After as milliseconds: delta-seconds or an HTTP date; null when absent or unreadable. */
23
29
  export declare function parseRetryAfterMs(header: string | null, now?: number): number | null;
@@ -1,13 +1,19 @@
1
1
  /** One retry policy for outbound HTTP: a timeout on every attempt, backoff on 429 and 5xx, and on a dropped connection where a replay is safe. */
2
+ import { MINUTE_MS } from './time.js';
2
3
  import { envLlmTimeoutMs } from './env.js';
4
+ import { errorCode, errorFields, log } from './log.js';
3
5
  const DEFAULT_ATTEMPTS = 3;
4
6
  const DEFAULT_BASE_DELAY_MS = 250;
5
7
  const DEFAULT_MAX_DELAY_MS = 8_000;
6
- const DEFAULT_LLM_TIMEOUT_MS = 60_000;
8
+ const DEFAULT_LLM_TIMEOUT_MS = MINUTE_MS;
7
9
  /** LLM calls (consolidation refine, DAG summaries, fact extraction) share one budget; `HIPPO_LLM_TIMEOUT_MS` overrides it. */
8
10
  export function llmTimeoutMs() {
9
11
  return envLlmTimeoutMs() ?? DEFAULT_LLM_TIMEOUT_MS;
10
12
  }
13
+ /** One LLM call with its retries: two full attempts and the longest wait between them, where three slow failures would hold a sleep for 3x the timeout. */
14
+ export function llmTotalMs() {
15
+ return 2 * llmTimeoutMs() + DEFAULT_MAX_DELAY_MS;
16
+ }
11
17
  export function isRetryableStatus(status) {
12
18
  return status === 429 || (status >= 500 && status <= 599);
13
19
  }
@@ -39,6 +45,22 @@ function isIdempotent(method) {
39
45
  const verb = (method ?? 'GET').toUpperCase();
40
46
  return verb === 'GET' || verb === 'HEAD';
41
47
  }
48
+ /** Host plus pathname only: a query string can carry a key. */
49
+ function redactedTarget(url) {
50
+ try {
51
+ const u = new URL(url);
52
+ return `${u.host}${u.pathname}`;
53
+ }
54
+ catch {
55
+ return 'unparseable-url';
56
+ }
57
+ }
58
+ function logRetry(init, url, reason, attempt, attempts, delayMs) {
59
+ // Debug: an attempt that may still succeed is not a warning, and the caller reports the final failure.
60
+ log.debug('http retry', {
61
+ method: (init.method ?? 'GET').toUpperCase(), target: redactedTarget(url), reason, attempt: `${attempt}/${attempts}`, delayMs: Math.round(delayMs),
62
+ });
63
+ }
42
64
  // `cancel` clears the timer, so a wait the caller left does not keep the process alive.
43
65
  const timerSleep = (ms, cancel) => new Promise((resolve) => {
44
66
  const timer = setTimeout(resolve, ms);
@@ -71,13 +93,16 @@ export async function fetchWithRetry(url, init, policy) {
71
93
  const random = policy.random ?? Math.random;
72
94
  const retryOn = policy.retryOn ?? ((res) => isRetryableStatus(res.status));
73
95
  const retryTransport = policy.retryTransport ?? isIdempotent(init.method);
96
+ const now = policy.now ?? Date.now;
97
+ const deadline = policy.totalMs === undefined ? Infinity : now() + policy.totalMs;
98
+ const fitsBudget = (delayMs) => now() + delayMs < deadline;
74
99
  // Jitter over the upper half keeps parallel callers from retrying in lockstep.
75
100
  const backoffMs = (attempt) => {
76
101
  const ceiling = Math.min(maxDelayMs, baseDelayMs * 2 ** (attempt - 1));
77
102
  return ceiling / 2 + random() * (ceiling / 2);
78
103
  };
79
104
  for (let attempt = 1;; attempt++) {
80
- const timeout = AbortSignal.timeout(policy.timeoutMs);
105
+ const timeout = AbortSignal.timeout(Math.max(1, Math.min(policy.timeoutMs, deadline - now())));
81
106
  const signal = init.signal ? AbortSignal.any([init.signal, timeout]) : timeout;
82
107
  let res;
83
108
  try {
@@ -87,7 +112,11 @@ export async function fetchWithRetry(url, init, policy) {
87
112
  // The caller's own abort is a decision, not a fault, so it ends the call.
88
113
  if (!retryTransport || attempt >= attempts || init.signal?.aborted || !isTransientTransportError(err))
89
114
  throw err;
90
- await backoffWait(backoffMs(attempt), policy.sleep, init.signal);
115
+ const delay = backoffMs(attempt);
116
+ if (!fitsBudget(delay))
117
+ throw err;
118
+ logRetry(init, url, errorCode(err) || (err instanceof Error ? err.name : 'transport'), attempt, attempts, delay);
119
+ await backoffWait(delay, policy.sleep, init.signal);
91
120
  continue;
92
121
  }
93
122
  if (!retryOn(res) || attempt >= attempts)
@@ -95,9 +124,18 @@ export async function fetchWithRetry(url, init, policy) {
95
124
  const retryAfter = parseRetryAfterMs(res.headers.get('retry-after'));
96
125
  if (retryAfter !== null && retryAfter > maxDelayMs)
97
126
  return res;
127
+ const delay = retryAfter ?? backoffMs(attempt);
128
+ if (!fitsBudget(delay))
129
+ return res;
98
130
  // Frees the pooled socket before the next attempt.
99
- await res.body?.cancel();
100
- await backoffWait(retryAfter ?? backoffMs(attempt), policy.sleep, init.signal);
131
+ try {
132
+ await res.body?.cancel();
133
+ }
134
+ catch (err) {
135
+ log.debug('http retry: body cancel failed', errorFields(err));
136
+ }
137
+ logRetry(init, url, `status ${res.status}`, attempt, attempts, delay);
138
+ await backoffWait(delay, policy.sleep, init.signal);
101
139
  }
102
140
  }
103
141
  //# sourceMappingURL=http-retry.js.map
@@ -1,7 +1,6 @@
1
1
  import type { IncomingMessage, ServerResponse } from 'node:http';
2
2
  export declare function isHeaderString(value: string | string[] | undefined): value is string;
3
3
  export declare const MAX_BODY_BYTES: number;
4
- export declare const MAX_ID_LEN = 256;
5
4
  export declare const JSON_HEADERS: {
6
5
  readonly 'content-type': "application/json";
7
6
  };
@@ -21,16 +20,9 @@ export declare class BodyTooLargeError extends Error {
21
20
  /** The body did not arrive in time. Its own class so mapApiError answers 408 and the server drops the socket. */
22
21
  export declare class BodyTimeoutError extends Error {
23
22
  }
24
- /** The status and client-facing message for one failed request. */
25
- export interface ApiErrorReply {
26
- status: number;
27
- message: string;
28
- }
29
23
  export declare const INTERNAL_ERROR_MESSAGE = "internal server error";
30
24
  /** The reply when a request under another store reaches code that still opens hippo.db. */
31
25
  export declare const STORE_NOT_PORTED_MESSAGE = "store_not_ported";
32
- /** Maps by class so rewording a message never moves a status; an untyped error is a 500 whose text stays in the server log. */
33
- export declare function mapApiError<E>(err: E): ApiErrorReply;
34
26
  /** Serialises before the head goes out, so a body that is not JSON still reaches the caller's error reply. */
35
27
  export declare function sendJson<T>(res: ServerResponse, status: number, body: T): void;
36
28
  /** How long any route waits for a request body, so a client that sends headers and then stalls cannot hold a request open: 30 s, or HIPPO_BODY_TIMEOUT_MS. */
@@ -1,5 +1,3 @@
1
- import { ApiError } from '../core/api-errors.js';
2
- import { SqliteBlockedError } from './sqlite-blocked.js';
3
1
  import { envBodyTimeoutMs } from './env.js';
4
2
  // Leaf module shared by server.ts and the connector webhook receivers; it must not import either.
5
3
  // node:http header values are `string | string[] | undefined` (never a bare
@@ -15,8 +13,6 @@ function sizeLabel(bytes) {
15
13
  }
16
14
  // How long a refused upload is still read and discarded after its reply, so a client that never stops cannot hold the socket.
17
15
  const REFUSED_UPLOAD_LINGER_MS = 2000;
18
- // Cap for id-shaped request fields (ids, tenant, session, scope, class): far above real values, small enough to bound logs and indexes.
19
- export const MAX_ID_LEN = 256;
20
16
  export const JSON_HEADERS = { 'content-type': 'application/json' };
21
17
  export class HttpError extends Error {
22
18
  status;
@@ -43,22 +39,6 @@ export class BodyTimeoutError extends Error {
43
39
  export const INTERNAL_ERROR_MESSAGE = 'internal server error';
44
40
  /** The reply when a request under another store reaches code that still opens hippo.db. */
45
41
  export const STORE_NOT_PORTED_MESSAGE = 'store_not_ported';
46
- /** Maps by class so rewording a message never moves a status; an untyped error is a 500 whose text stays in the server log. */
47
- export function mapApiError(err) {
48
- // An add-on can build an HttpError from any number, and writeHead throws on one outside 100-999.
49
- if (err instanceof HttpError && !(Number.isInteger(err.status) && err.status >= 100 && err.status <= 999)) {
50
- return { status: 500, message: INTERNAL_ERROR_MESSAGE };
51
- }
52
- if (err instanceof HttpError || err instanceof ApiError)
53
- return { status: err.status, message: err.message };
54
- if (err instanceof BodyTooLargeError)
55
- return { status: 413, message: err.message };
56
- if (err instanceof BodyTimeoutError)
57
- return { status: 408, message: err.message };
58
- if (err instanceof SqliteBlockedError)
59
- return { status: 501, message: STORE_NOT_PORTED_MESSAGE };
60
- return { status: 500, message: INTERNAL_ERROR_MESSAGE };
61
- }
62
42
  /** Serialises before the head goes out, so a body that is not JSON still reaches the caller's error reply. */
63
43
  export function sendJson(res, status, body) {
64
44
  const text = JSON.stringify(body);
@@ -121,7 +101,16 @@ export function isCrossSite(req) {
121
101
  if (site !== undefined && site !== 'same-origin' && site !== 'none')
122
102
  return true;
123
103
  const origin = req.headers.origin;
124
- return origin !== undefined && origin !== `http://${req.headers.host}`;
104
+ return origin !== undefined && originHost(origin) !== req.headers.host;
105
+ }
106
+ // Scheme is ignored so an https page behind a TLS-terminating proxy still matches its own Host.
107
+ function originHost(origin) {
108
+ try {
109
+ return new URL(origin).host;
110
+ }
111
+ catch {
112
+ return null;
113
+ }
125
114
  }
126
115
  // A webhook route takes no key, so anyone holding a signature header could otherwise keep a socket open with a body that never ends.
127
116
  const WEBHOOK_BODY_DEADLINE_MS = 10_000;
@@ -0,0 +1,7 @@
1
+ export declare const MAX_ID_LEN = 256;
2
+ export declare const DEFAULT_LIST_LIMIT = 100;
3
+ export declare const MAX_LIST_LIMIT = 1000;
4
+ /** The one list-limit rule: absent means the default; a positive integer up to the ceiling passes.
5
+ * Anything else returns undefined, so each surface words its own error. */
6
+ export declare function resolveListLimit(requested: number | undefined, defaultLimit?: number, maxLimit?: number): number | undefined;
7
+ //# sourceMappingURL=limits.d.ts.map
@@ -0,0 +1,14 @@
1
+ // Cap for id-shaped request fields (ids, tenant, session, scope, class): far above real values, small enough to bound logs and indexes.
2
+ export const MAX_ID_LEN = 256;
3
+ // Page size every list surface (CLI, HTTP, store) falls back to when the caller names none.
4
+ export const DEFAULT_LIST_LIMIT = 100;
5
+ // Ceiling for a caller-named list page; SQLite `LIMIT ?` and one response's size bound it, and every list surface shares it.
6
+ export const MAX_LIST_LIMIT = 1000;
7
+ /** The one list-limit rule: absent means the default; a positive integer up to the ceiling passes.
8
+ * Anything else returns undefined, so each surface words its own error. */
9
+ export function resolveListLimit(requested, defaultLimit = DEFAULT_LIST_LIMIT, maxLimit = Infinity) {
10
+ if (requested === undefined)
11
+ return defaultLimit;
12
+ return Number.isInteger(requested) && requested > 0 && requested <= maxLimit ? requested : undefined;
13
+ }
14
+ //# sourceMappingURL=limits.js.map
@@ -30,6 +30,10 @@ export declare const log: {
30
30
  };
31
31
  /** Message for a caught value of unknown shape. `cause` names the sanctioned unknown-input case (error-cause enrichment). */
32
32
  export declare function errorMessage(cause: unknown): string;
33
+ /** One post-compact failure, told once to the hook's output (`say`) and once to the structured log. */
34
+ export declare function reportCompactionFailure(say: (message: string) => void, step: string, reason: string): void;
35
+ /** The string `code` of a caught value (an errno name such as EACCES), or '' when it has none. */
36
+ export declare function errorCode(cause: unknown): string;
33
37
  /** Test hook: forget which once-keys have fired. */
34
38
  export declare function resetLogOnce(): void;
35
39
  export {};
package/dist/util/log.js CHANGED
@@ -83,6 +83,15 @@ export const log = {
83
83
  export function errorMessage(cause) {
84
84
  return cause instanceof Error ? cause.message : String(cause);
85
85
  }
86
+ /** One post-compact failure, told once to the hook's output (`say`) and once to the structured log. */
87
+ export function reportCompactionFailure(say, step, reason) {
88
+ say(`${step} failed: ${reason}`);
89
+ log.error(`post-compact: ${step} failed: ${reason}`);
90
+ }
91
+ /** The string `code` of a caught value (an errno name such as EACCES), or '' when it has none. */
92
+ export function errorCode(cause) {
93
+ return cause instanceof Error && 'code' in cause ? String(cause.code) : '';
94
+ }
86
95
  /** Test hook: forget which once-keys have fired. */
87
96
  export function resetLogOnce() {
88
97
  onceKeys.clear();
@@ -6,6 +6,8 @@ export interface RequestScope {
6
6
  /** Set for a served request that must be answered by a given time. */
7
7
  readonly deadline?: RequestDeadline | undefined;
8
8
  }
9
+ /** How long a request may run: 120 s, or HIPPO_REQUEST_DEADLINE_MS, where 0 turns the deadline off. */
10
+ export declare function requestDeadlineMs(): number;
9
11
  /** When a request must be answered, and the store calls it still waits on: the store ends each of those soon after `at`, with the outcome only it knows. */
10
12
  export declare class RequestDeadline {
11
13
  #private;
@@ -14,7 +16,7 @@ export declare class RequestDeadline {
14
16
  constructor(at: number);
15
17
  /** Counts `call` as one the request waits on until it settles. */
16
18
  track<T>(call: Promise<T>): Promise<T>;
17
- /** Runs `fn` now, or once the last store call the request waits on has settled. */
19
+ /** Runs `fn` now, or once the last store call the request waits on has settled; every function registered meanwhile runs, in order. */
18
20
  onceIdle(fn: () => void): void;
19
21
  }
20
22
  export declare const requestScopes: AsyncLocalStorage<RequestScope>;
@@ -1,11 +1,17 @@
1
1
  // The one ambient scope of a request. A leaf module, so the logger can read the request id without importing the store layer.
2
2
  import { AsyncLocalStorage } from 'node:async_hooks';
3
+ import { envRequestDeadlineMs } from './env.js';
4
+ const DEFAULT_REQUEST_DEADLINE_MS = 120_000;
5
+ /** How long a request may run: 120 s, or HIPPO_REQUEST_DEADLINE_MS, where 0 turns the deadline off. */
6
+ export function requestDeadlineMs() {
7
+ return envRequestDeadlineMs() ?? DEFAULT_REQUEST_DEADLINE_MS;
8
+ }
3
9
  /** When a request must be answered, and the store calls it still waits on: the store ends each of those soon after `at`, with the outcome only it knows. */
4
10
  export class RequestDeadline {
5
11
  /** Epoch milliseconds. */
6
12
  at;
7
13
  #storeCalls = 0;
8
- #whenIdle;
14
+ #whenIdle = [];
9
15
  constructor(at) {
10
16
  this.at = at;
11
17
  }
@@ -16,19 +22,18 @@ export class RequestDeadline {
16
22
  this.#storeCalls -= 1;
17
23
  if (this.#storeCalls > 0)
18
24
  return;
19
- const idle = this.#whenIdle;
20
- this.#whenIdle = undefined;
21
- idle?.();
25
+ for (const idle of this.#whenIdle.splice(0))
26
+ idle();
22
27
  };
23
28
  call.then(done, done);
24
29
  return call;
25
30
  }
26
- /** Runs `fn` now, or once the last store call the request waits on has settled. */
31
+ /** Runs `fn` now, or once the last store call the request waits on has settled; every function registered meanwhile runs, in order. */
27
32
  onceIdle(fn) {
28
33
  if (this.#storeCalls === 0)
29
34
  fn();
30
35
  else
31
- this.#whenIdle = fn;
36
+ this.#whenIdle.push(fn);
32
37
  }
33
38
  }
34
39
  export const requestScopes = new AsyncLocalStorage();
@@ -28,7 +28,12 @@ const SECRET_PATTERNS = [
28
28
  { name: 'sk-underscore-key', re: /\bsk_[A-Za-z0-9]+_[A-Za-z0-9_]{6,}\b/ },
29
29
  // Value needs 12+ token-safe chars and a digit, or `token = estimateTokens(...)` and user:password@ templates would hide code lessons from ambient context.
30
30
  // Secret names end in the keyword (dbPassword), so the match opens there, no prefix scan; pwd and pass need a _ (OLDPWD, bypass are not secrets).
31
- { name: 'secret-assignment', re: /(?:(?:api[_-]?key|access[_-]?key|private[_-]?key|secret|token|passw(?:or)?d|(?<=_)(?:pwd|pass))(?:[_-]?(?:key(?:[_-]?base)?|value|secret))?['"]?\s*(?::=?|=>?)\s*['"]?|(?<=--)password(?:=|\s+))(?=[A-Za-z0-9_\-+/]*\d)[A-Za-z0-9_\-+/=]{12,}/i },
31
+ {
32
+ name: 'secret-assignment',
33
+ re: new RegExp(String.raw `(?:(?:api[_-]?key|access[_-]?key|private[_-]?key|secret|token|passw(?:or)?d|(?<=_)(?:pwd|pass))` +
34
+ String.raw `(?:[_-]?(?:key(?:[_-]?base)?|value|secret))?['"]?\s*(?::=?|=>?)\s*['"]?|(?<=--)password(?:=|\s+))` +
35
+ String.raw `(?=[A-Za-z0-9_\-+/]*\d)[A-Za-z0-9_\-+/=]{12,}`, 'i'),
36
+ },
32
37
  { name: 'url-password', re: /(?<=[A-Za-z0-9]:\/\/)[^\s/:@?#]*:(?=[^\s/@?#]*\d)[^\s/@?#]+(?=@)/ },
33
38
  ];
34
39
  const KEYISH_CONTEXT_RE = /key|token|secret|credential|bearer|auth|password/i;
@@ -13,4 +13,6 @@ export declare class OtherStoreFolderError extends SqliteBlockedError {
13
13
  readonly markerPath: string;
14
14
  constructor(storeKind: string, markerPath: string);
15
15
  }
16
+ /** First line of a best-effort catch around a hippo.db open: an unported path must fail closed, not fall back silently. */
17
+ export declare function rethrowIfSqliteBlocked<E>(err: E): void;
16
18
  //# sourceMappingURL=sqlite-blocked.d.ts.map
@@ -26,4 +26,9 @@ export class OtherStoreFolderError extends SqliteBlockedError {
26
26
  this.name = 'OtherStoreFolderError';
27
27
  }
28
28
  }
29
+ /** First line of a best-effort catch around a hippo.db open: an unported path must fail closed, not fall back silently. */
30
+ export function rethrowIfSqliteBlocked(err) {
31
+ if (err instanceof SqliteBlockedError)
32
+ throw err;
33
+ }
29
34
  //# sourceMappingURL=sqlite-blocked.js.map
@@ -0,0 +1,3 @@
1
+ /** Mirrors every stdout and stderr chunk to `logFile` until the returned restore runs; an append failure warns once and never stops the real write. */
2
+ export declare function teeStdStreams(logFile: string): () => void;
3
+ //# sourceMappingURL=stream-tee.d.ts.map
@@ -0,0 +1,35 @@
1
+ import * as fs from 'fs';
2
+ import { errorMessage, log } from './log.js';
3
+ /** Mirrors every stdout and stderr chunk to `logFile` until the returned restore runs; an append failure warns once and never stops the real write. */
4
+ export function teeStdStreams(logFile) {
5
+ const origStdoutWrite = process.stdout.write.bind(process.stdout);
6
+ const origStderrWrite = process.stderr.write.bind(process.stderr);
7
+ let warned = false;
8
+ const tee = (chunk) => {
9
+ try {
10
+ fs.appendFileSync(logFile, chunk instanceof Uint8Array ? Buffer.from(chunk).toString('utf8') : chunk, 'utf8');
11
+ }
12
+ catch (err) {
13
+ if (warned)
14
+ return;
15
+ warned = true;
16
+ log.warn(`log file ${logFile} is no longer writable; output continues without it: ${errorMessage(err)}`);
17
+ }
18
+ };
19
+ const wrapWrite = (origWrite) => {
20
+ const wrapped = (...args) => {
21
+ tee(args[0]);
22
+ // SAFETY: forwarding the exact arguments the real overloaded `write` received is safe; `StreamWriteArgs` is the union of both overloads' parameter lists.
23
+ return origWrite(...args);
24
+ };
25
+ // SAFETY: `wrapped` matches both real `write` overload shapes; TS cannot verify one implementation covers an overloaded type.
26
+ return wrapped;
27
+ };
28
+ process.stdout.write = wrapWrite(origStdoutWrite);
29
+ process.stderr.write = wrapWrite(origStderrWrite);
30
+ return () => {
31
+ process.stdout.write = origStdoutWrite;
32
+ process.stderr.write = origStderrWrite;
33
+ };
34
+ }
35
+ //# sourceMappingURL=stream-tee.js.map
@@ -1,2 +1,6 @@
1
1
  export declare const DAY_MS = 86400000;
2
+ export declare const MINUTE_MS = 60000;
3
+ export declare const HOUR_MS = 3600000;
4
+ /** One 365-day year in seconds. */
5
+ export declare const YEAR_S = 31536000;
2
6
  //# sourceMappingURL=time.d.ts.map
package/dist/util/time.js CHANGED
@@ -1,3 +1,7 @@
1
1
  // Durations in the base layer, so no layer imports a higher one for a constant.
2
2
  export const DAY_MS = 86_400_000;
3
+ export const MINUTE_MS = 60_000;
4
+ export const HOUR_MS = 3_600_000;
5
+ /** One 365-day year in seconds. */
6
+ export const YEAR_S = 31_536_000;
3
7
  //# sourceMappingURL=time.js.map
@@ -1,6 +1,6 @@
1
1
  /** Single source of truth for the package version, written by scripts/sync-version.mjs; never edit by hand. Read by the db rollback guard, /health, MCP.
2
2
  * A constant, not a runtime package.json read: the published bundle may lack package.json on a path ESM can resolve. */
3
- export declare const PACKAGE_VERSION = "1.71.0";
3
+ export declare const PACKAGE_VERSION = "1.72.0";
4
4
  /** The floor a store takes on its first expiring key: the first release with schema
5
5
  * v53, set by hand in that release; scripts/check-expiring-keys-floor.mjs gates it. */
6
6
  export declare const EXPIRING_KEYS_MIN_BINARY = "1.64.0";
@@ -1,6 +1,6 @@
1
1
  /** Single source of truth for the package version, written by scripts/sync-version.mjs; never edit by hand. Read by the db rollback guard, /health, MCP.
2
2
  * A constant, not a runtime package.json read: the published bundle may lack package.json on a path ESM can resolve. */
3
- export const PACKAGE_VERSION = '1.71.0';
3
+ export const PACKAGE_VERSION = '1.72.0';
4
4
  /** The floor a store takes on its first expiring key: the first release with schema
5
5
  * v53, set by hand in that release; scripts/check-expiring-keys-floor.mjs gates it. */
6
6
  export const EXPIRING_KEYS_MIN_BINARY = '1.64.0';
@@ -2,7 +2,7 @@
2
2
  "id": "hippo-memory",
3
3
  "name": "Hippo Memory",
4
4
  "description": "Make your agent's memory work like a brain. Hippo is long-term memory for coding agents.",
5
- "version": "1.71.0",
5
+ "version": "1.72.0",
6
6
 
7
7
  "configSchema": {
8
8
  "type": "object",