hippo-memory 1.70.0 → 1.71.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (629) hide show
  1. package/README.md +1 -1
  2. package/dist/agent-memories/claude-code.d.ts +4 -2
  3. package/dist/agent-memories/claude-code.js +14 -5
  4. package/dist/agent-memories/files.js +23 -12
  5. package/dist/agent-memories/markdown.d.ts +1 -3
  6. package/dist/agent-memories/markdown.js +1 -3
  7. package/dist/agent-memories/report.js +2 -1
  8. package/dist/agent-memories/sync.d.ts +4 -2
  9. package/dist/agent-memories/sync.js +10 -5
  10. package/dist/api/assemble.d.ts +7 -44
  11. package/dist/api/assemble.js +2 -22
  12. package/dist/api/audit.d.ts +5 -1
  13. package/dist/api/audit.js +9 -1
  14. package/dist/api/auth.d.ts +13 -22
  15. package/dist/api/auth.js +34 -24
  16. package/dist/api/auto-sleep.js +2 -2
  17. package/dist/api/availability.d.ts +2 -15
  18. package/dist/api/availability.js +2 -15
  19. package/dist/api/context-select.js +3 -4
  20. package/dist/api/context-types.d.ts +10 -35
  21. package/dist/api/context.d.ts +5 -22
  22. package/dist/api/context.js +21 -38
  23. package/dist/api/dormant.d.ts +5 -21
  24. package/dist/api/dormant.js +7 -26
  25. package/dist/api/drill-down.d.ts +7 -46
  26. package/dist/api/drill-down.js +5 -26
  27. package/dist/api/forget.d.ts +5 -24
  28. package/dist/api/forget.js +5 -28
  29. package/dist/api/graph.d.ts +8 -0
  30. package/dist/api/graph.js +7 -0
  31. package/dist/api/index.d.ts +2 -0
  32. package/dist/api/index.js +2 -0
  33. package/dist/api/instruction-detect.js +16 -5
  34. package/dist/api/ledger-db.d.ts +2 -1
  35. package/dist/api/ledger-db.js +5 -3
  36. package/dist/api/memories.d.ts +5 -0
  37. package/dist/api/memories.js +7 -0
  38. package/dist/api/objects.d.ts +9 -0
  39. package/dist/api/objects.js +11 -0
  40. package/dist/api/on-store.js +5 -3
  41. package/dist/api/predictions.d.ts +9 -0
  42. package/dist/api/predictions.js +18 -0
  43. package/dist/api/promote.d.ts +13 -17
  44. package/dist/api/promote.js +11 -19
  45. package/dist/api/prompt-hook.d.ts +4 -4
  46. package/dist/api/prompt-hook.js +10 -9
  47. package/dist/api/recall-cli.d.ts +55 -0
  48. package/dist/api/recall-cli.js +94 -0
  49. package/dist/api/recall-core.js +14 -6
  50. package/dist/api/recall-finish.d.ts +53 -0
  51. package/dist/api/recall-finish.js +30 -0
  52. package/dist/api/recall-history.d.ts +17 -84
  53. package/dist/api/recall-history.js +15 -88
  54. package/dist/api/recall-pipeline.d.ts +2 -3
  55. package/dist/api/recall-pipeline.js +16 -9
  56. package/dist/api/recall-types.d.ts +64 -304
  57. package/dist/api/recall.d.ts +5 -33
  58. package/dist/api/recall.js +59 -60
  59. package/dist/api/refine.d.ts +7 -0
  60. package/dist/api/refine.js +10 -0
  61. package/dist/api/remember-local.d.ts +57 -0
  62. package/dist/api/remember-local.js +90 -0
  63. package/dist/api/remember.d.ts +18 -1
  64. package/dist/api/remember.js +13 -1
  65. package/dist/api/sleep-run.d.ts +1 -3
  66. package/dist/api/sleep-run.js +17 -21
  67. package/dist/api/sleep.d.ts +9 -28
  68. package/dist/api/tokens.d.ts +3 -10
  69. package/dist/api/tokens.js +3 -10
  70. package/dist/api/types.d.ts +6 -29
  71. package/dist/api/types.js +4 -22
  72. package/dist/capture/command.d.ts +4 -12
  73. package/dist/capture/command.js +6 -22
  74. package/dist/capture/compact.d.ts +4 -15
  75. package/dist/capture/compact.js +16 -25
  76. package/dist/capture/compaction-record.js +101 -69
  77. package/dist/capture/compaction-spool.d.ts +1 -1
  78. package/dist/capture/compaction-spool.js +1 -1
  79. package/dist/capture/session-digest.d.ts +1 -1
  80. package/dist/capture/session-digest.js +13 -7
  81. package/dist/capture/transcript.d.ts +2 -10
  82. package/dist/capture/transcript.js +4 -18
  83. package/dist/cli/api-context.d.ts +3 -0
  84. package/dist/cli/api-context.js +6 -0
  85. package/dist/cli/audit-prune.d.ts +6 -39
  86. package/dist/cli/audit-prune.js +6 -39
  87. package/dist/cli/audit.d.ts +1 -1
  88. package/dist/cli/audit.js +33 -34
  89. package/dist/cli/auth.d.ts +2 -2
  90. package/dist/cli/auth.js +57 -39
  91. package/dist/cli/briefs.d.ts +4 -4
  92. package/dist/cli/briefs.js +18 -22
  93. package/dist/cli/card.d.ts +2 -2
  94. package/dist/cli/card.js +31 -32
  95. package/dist/cli/client.d.ts +7 -27
  96. package/dist/cli/client.js +24 -43
  97. package/dist/cli/compact-resume-payload.js +3 -2
  98. package/dist/cli/context.d.ts +1 -2
  99. package/dist/cli/context.js +12 -8
  100. package/dist/cli/continuity.d.ts +6 -6
  101. package/dist/cli/continuity.js +62 -64
  102. package/dist/cli/correction-latency.d.ts +2 -14
  103. package/dist/cli/correction-latency.js +2 -14
  104. package/dist/cli/curate.d.ts +13 -17
  105. package/dist/cli/curate.js +68 -92
  106. package/dist/cli/dag.d.ts +4 -4
  107. package/dist/cli/dag.js +33 -27
  108. package/dist/cli/decisions.d.ts +4 -4
  109. package/dist/cli/decisions.js +33 -47
  110. package/dist/cli/eval.js +15 -13
  111. package/dist/cli/exit.d.ts +6 -0
  112. package/dist/cli/exit.js +11 -0
  113. package/dist/cli/explain.d.ts +1 -2
  114. package/dist/cli/explain.js +6 -4
  115. package/dist/cli/flag-values.d.ts +6 -1
  116. package/dist/cli/flag-values.js +27 -10
  117. package/dist/cli/flags.d.ts +1 -0
  118. package/dist/cli/flags.js +1 -1
  119. package/dist/cli/github.d.ts +14 -0
  120. package/dist/{connectors/github/cli-impl.js → cli/github.js} +43 -71
  121. package/dist/cli/goals.d.ts +2 -2
  122. package/dist/cli/goals.js +46 -50
  123. package/dist/cli/hook-runtime.d.ts +9 -27
  124. package/dist/cli/hook-runtime.js +15 -31
  125. package/dist/cli/init.d.ts +2 -2
  126. package/dist/cli/init.js +6 -12
  127. package/dist/cli/install-steps.js +3 -12
  128. package/dist/cli/last-sleep.d.ts +2 -1
  129. package/dist/cli/last-sleep.js +3 -0
  130. package/dist/cli/maintenance.d.ts +4 -4
  131. package/dist/cli/maintenance.js +14 -19
  132. package/dist/cli/object-verbs.js +4 -3
  133. package/dist/cli/owner-validation.d.ts +5 -28
  134. package/dist/cli/owner-validation.js +5 -28
  135. package/dist/cli/playbooks.d.ts +4 -4
  136. package/dist/cli/playbooks.js +23 -28
  137. package/dist/cli/print.d.ts +2 -5
  138. package/dist/cli/print.js +1 -4
  139. package/dist/cli/projects.d.ts +2 -2
  140. package/dist/cli/projects.js +1 -3
  141. package/dist/cli/provenance-coverage.d.ts +2 -6
  142. package/dist/cli/provenance-coverage.js +2 -6
  143. package/dist/cli/quality-repair-once.js +2 -2
  144. package/dist/cli/recall.d.ts +2 -4
  145. package/dist/cli/recall.js +55 -131
  146. package/dist/cli/refine-llm.d.ts +5 -31
  147. package/dist/cli/refine-llm.js +9 -36
  148. package/dist/cli/remember.d.ts +3 -3
  149. package/dist/cli/remember.js +122 -180
  150. package/dist/cli/scheduler.d.ts +2 -17
  151. package/dist/cli/scheduler.js +12 -18
  152. package/dist/cli/serve.js +21 -12
  153. package/dist/cli/session-hooks.d.ts +10 -18
  154. package/dist/cli/session-hooks.js +112 -113
  155. package/dist/cli/setup.d.ts +4 -4
  156. package/dist/cli/setup.js +19 -21
  157. package/dist/cli/shared.d.ts +9 -23
  158. package/dist/cli/shared.js +9 -32
  159. package/dist/cli/slack.d.ts +2 -2
  160. package/dist/cli/slack.js +19 -23
  161. package/dist/cli/sleep.d.ts +3 -2
  162. package/dist/cli/sleep.js +15 -16
  163. package/dist/cli/status.d.ts +9 -12
  164. package/dist/cli/status.js +55 -98
  165. package/dist/cli/stdin.d.ts +4 -6
  166. package/dist/cli/stdin.js +2 -3
  167. package/dist/cli/transfer.d.ts +9 -9
  168. package/dist/cli/transfer.js +66 -89
  169. package/dist/cli/usage.js +11 -5
  170. package/dist/cli.d.ts +66 -448
  171. package/dist/cli.js +87 -263
  172. package/dist/connectors/github/backfill.d.ts +5 -25
  173. package/dist/connectors/github/backfill.js +14 -49
  174. package/dist/connectors/github/deletion.d.ts +4 -18
  175. package/dist/connectors/github/deletion.js +2 -15
  176. package/dist/connectors/github/dlq.d.ts +8 -33
  177. package/dist/connectors/github/dlq.js +12 -29
  178. package/dist/connectors/github/ingest.d.ts +2 -6
  179. package/dist/connectors/github/ingest.js +4 -10
  180. package/dist/connectors/github/octokit-client.d.ts +4 -16
  181. package/dist/connectors/github/octokit-client.js +27 -24
  182. package/dist/connectors/github/ratelimit.d.ts +3 -14
  183. package/dist/connectors/github/ratelimit.js +3 -14
  184. package/dist/connectors/github/scope.d.ts +3 -5
  185. package/dist/connectors/github/scope.js +3 -5
  186. package/dist/connectors/github/signature.d.ts +4 -26
  187. package/dist/connectors/github/signature.js +4 -26
  188. package/dist/connectors/github/tenant-routing.d.ts +2 -24
  189. package/dist/connectors/github/tenant-routing.js +4 -26
  190. package/dist/connectors/github/transform.js +2 -16
  191. package/dist/connectors/github/types.d.ts +3 -15
  192. package/dist/connectors/github/types.js +2 -9
  193. package/dist/connectors/github/webhook.d.ts +2 -16
  194. package/dist/connectors/github/webhook.js +6 -22
  195. package/dist/connectors/slack/backfill.d.ts +4 -17
  196. package/dist/connectors/slack/backfill.js +4 -13
  197. package/dist/connectors/slack/dlq.d.ts +2 -19
  198. package/dist/connectors/slack/dlq.js +2 -19
  199. package/dist/connectors/slack/ingest.d.ts +2 -3
  200. package/dist/connectors/slack/ingest.js +2 -3
  201. package/dist/connectors/slack/scope.d.ts +2 -8
  202. package/dist/connectors/slack/scope.js +2 -8
  203. package/dist/connectors/slack/signature.d.ts +1 -6
  204. package/dist/connectors/slack/tenant-routing.d.ts +2 -19
  205. package/dist/connectors/slack/tenant-routing.js +2 -21
  206. package/dist/connectors/slack/transform.d.ts +2 -18
  207. package/dist/connectors/slack/transform.js +2 -18
  208. package/dist/connectors/slack/types.d.ts +3 -9
  209. package/dist/connectors/slack/types.js +2 -4
  210. package/dist/connectors/slack/web-client.js +33 -27
  211. package/dist/connectors/slack/webhook.d.ts +2 -19
  212. package/dist/connectors/slack/webhook.js +5 -26
  213. package/dist/connectors/slack/workspaces.d.ts +4 -24
  214. package/dist/connectors/slack/workspaces.js +4 -24
  215. package/dist/consolidate/conflicts.js +6 -14
  216. package/dist/consolidate/dag-summary.d.ts +8 -0
  217. package/dist/consolidate/dag-summary.js +41 -0
  218. package/dist/consolidate/dag.d.ts +5 -28
  219. package/dist/consolidate/dag.js +14 -75
  220. package/dist/consolidate/decay.js +7 -19
  221. package/dist/consolidate/dedupe.d.ts +6 -52
  222. package/dist/consolidate/dedupe.js +13 -71
  223. package/dist/consolidate/flush-units.d.ts +3 -4
  224. package/dist/consolidate/flush-units.js +3 -3
  225. package/dist/consolidate/half-life-migration.d.ts +3 -26
  226. package/dist/consolidate/half-life-migration.js +11 -28
  227. package/dist/consolidate/llm-passes.js +4 -21
  228. package/dist/consolidate/memory-value-weights.d.ts +2 -13
  229. package/dist/consolidate/memory-value-weights.js +2 -13
  230. package/dist/consolidate/memory-value.d.ts +11 -84
  231. package/dist/consolidate/memory-value.js +16 -104
  232. package/dist/consolidate/merge.js +10 -31
  233. package/dist/consolidate/merged-row.js +2 -1
  234. package/dist/consolidate/physics-pass.js +1 -3
  235. package/dist/consolidate/replay.d.ts +6 -38
  236. package/dist/consolidate/replay.js +14 -52
  237. package/dist/consolidate/run.js +1 -1
  238. package/dist/consolidate/sleep.d.ts +1 -8
  239. package/dist/consolidate/sleep.js +5 -17
  240. package/dist/consolidate/trace.d.ts +3 -18
  241. package/dist/consolidate/trace.js +3 -18
  242. package/dist/consolidate/traces.js +6 -22
  243. package/dist/core/ablation.d.ts +4 -91
  244. package/dist/core/ablation.js +4 -91
  245. package/dist/core/ambient.d.ts +1 -8
  246. package/dist/core/ambient.js +1 -8
  247. package/dist/core/capture-contract.d.ts +4 -2
  248. package/dist/core/capture-contract.js +4 -2
  249. package/dist/core/card.d.ts +4 -1
  250. package/dist/core/card.js +8 -0
  251. package/dist/core/compare.d.ts +11 -67
  252. package/dist/core/compare.js +9 -63
  253. package/dist/core/config.d.ts +10 -24
  254. package/dist/core/config.js +13 -14
  255. package/dist/core/handoff.d.ts +1 -7
  256. package/dist/core/handoff.js +1 -7
  257. package/dist/core/memory-quality.js +1 -1
  258. package/dist/core/memory.d.ts +41 -164
  259. package/dist/core/memory.js +51 -221
  260. package/dist/core/package-root.d.ts +3 -0
  261. package/dist/core/package-root.js +5 -0
  262. package/dist/core/physics-config.d.ts +1 -4
  263. package/dist/core/physics-config.js +3 -8
  264. package/dist/core/physics.d.ts +15 -61
  265. package/dist/core/physics.js +59 -122
  266. package/dist/core/project-identity.d.ts +17 -58
  267. package/dist/core/project-identity.js +17 -38
  268. package/dist/core/project-remote.js +13 -2
  269. package/dist/core/raw-append-only.d.ts +5 -0
  270. package/dist/core/raw-append-only.js +7 -0
  271. package/dist/core/salience.d.ts +1 -7
  272. package/dist/core/salience.js +1 -7
  273. package/dist/dashboard/dashboard-actions.js +2 -2
  274. package/dist/dashboard/dashboard-snapshot.d.ts +2 -1
  275. package/dist/dashboard/dashboard-snapshot.js +11 -2
  276. package/dist/dashboard/dashboard-types.d.ts +2 -1
  277. package/dist/dashboard/dashboard.d.ts +1 -0
  278. package/dist/dashboard/dashboard.js +50 -4
  279. package/dist/db/busy.d.ts +1 -1
  280. package/dist/db/busy.js +17 -8
  281. package/dist/db/connect.d.ts +1 -0
  282. package/dist/db/connect.js +3 -3
  283. package/dist/db/migrate.js +3 -3
  284. package/dist/db/migrations/types.d.ts +2 -5
  285. package/dist/db/migrations/v14.js +2 -3
  286. package/dist/db/migrations/v15.js +2 -8
  287. package/dist/db/migrations/v16.js +2 -3
  288. package/dist/db/migrations/v17.js +2 -3
  289. package/dist/db/migrations/v19.js +2 -5
  290. package/dist/db/migrations/v20.js +2 -7
  291. package/dist/db/migrations/v22.js +4 -17
  292. package/dist/db/migrations/v23.js +3 -14
  293. package/dist/db/migrations/v24.js +2 -4
  294. package/dist/db/migrations/v25.js +3 -8
  295. package/dist/db/migrations/v26.js +2 -13
  296. package/dist/db/migrations/v27.js +3 -11
  297. package/dist/db/migrations/v28.js +2 -14
  298. package/dist/db/migrations/v29.js +4 -18
  299. package/dist/db/migrations/v30.js +4 -16
  300. package/dist/db/migrations/v31.js +2 -15
  301. package/dist/db/migrations/v32.js +4 -22
  302. package/dist/db/migrations/v33.js +2 -15
  303. package/dist/db/migrations/v34.js +2 -11
  304. package/dist/db/migrations/v35.js +2 -11
  305. package/dist/db/migrations/v36.js +2 -11
  306. package/dist/db/migrations/v37.js +8 -33
  307. package/dist/db/migrations/v38.js +5 -38
  308. package/dist/db/migrations/v39.js +9 -20
  309. package/dist/db/migrations/v40.js +2 -9
  310. package/dist/db/migrations/v41.js +2 -24
  311. package/dist/db/migrations/v43.js +1 -1
  312. package/dist/db/migrations/v44.js +2 -8
  313. package/dist/db/migrations/v45.js +2 -7
  314. package/dist/db/migrations/v47.js +2 -3
  315. package/dist/db/migrations/v51.js +1 -1
  316. package/dist/db/open.d.ts +2 -0
  317. package/dist/db/open.js +17 -1
  318. package/dist/db/physics-state.d.ts +6 -22
  319. package/dist/db/physics-state.js +25 -36
  320. package/dist/db/raw-archive-mirror-cleanup.d.ts +2 -17
  321. package/dist/db/raw-archive-mirror-cleanup.js +2 -17
  322. package/dist/db/vector-store.d.ts +2 -0
  323. package/dist/db/vector-store.js +8 -1
  324. package/dist/doctor.js +74 -19
  325. package/dist/entry/session-text.js +2 -1
  326. package/dist/eval/eval-stats.d.ts +15 -53
  327. package/dist/eval/eval-stats.js +12 -42
  328. package/dist/eval/eval-suite.d.ts +2 -13
  329. package/dist/eval/eval-suite.js +74 -34
  330. package/dist/eval/eval.d.ts +6 -24
  331. package/dist/eval/eval.js +7 -34
  332. package/dist/eval/judgment.d.ts +0 -3
  333. package/dist/eval/judgment.js +19 -11
  334. package/dist/extensions/openclaw-plugin/index.js +1 -1
  335. package/dist/graph/extract.d.ts +5 -27
  336. package/dist/graph/extract.js +7 -33
  337. package/dist/graph/recall.d.ts +7 -20
  338. package/dist/graph/recall.js +14 -76
  339. package/dist/graph/stream.d.ts +7 -39
  340. package/dist/graph/stream.js +11 -34
  341. package/dist/graph/view.d.ts +9 -35
  342. package/dist/graph/view.js +11 -39
  343. package/dist/hooks/codex-wrapper.js +2 -2
  344. package/dist/hooks/copilot.js +2 -1
  345. package/dist/hooks/hook-blocks.d.ts +6 -2
  346. package/dist/hooks/hook-blocks.js +37 -124
  347. package/dist/hooks/json-hooks.d.ts +4 -36
  348. package/dist/hooks/json-hooks.js +18 -57
  349. package/dist/hooks/opencode.d.ts +0 -38
  350. package/dist/hooks/opencode.js +9 -76
  351. package/dist/hooks/shared.d.ts +8 -17
  352. package/dist/hooks/shared.js +29 -20
  353. package/dist/importers/core.d.ts +12 -37
  354. package/dist/importers/core.js +9 -27
  355. package/dist/importers/markdown-parse.d.ts +8 -22
  356. package/dist/importers/markdown-parse.js +8 -25
  357. package/dist/importers/markdown.js +3 -12
  358. package/dist/importers/sources.js +7 -35
  359. package/dist/importers/vault.d.ts +2 -8
  360. package/dist/importers/vault.js +25 -81
  361. package/dist/index.d.ts +6 -2
  362. package/dist/index.js +6 -2
  363. package/dist/learn/autolearn.d.ts +14 -41
  364. package/dist/learn/autolearn.js +14 -41
  365. package/dist/learn/extract.js +2 -4
  366. package/dist/learn/forward-claim-detector.d.ts +6 -30
  367. package/dist/learn/forward-claim-detector.js +14 -66
  368. package/dist/learn/invalidation.d.ts +6 -18
  369. package/dist/learn/invalidation.js +9 -26
  370. package/dist/mcp/admin-tools.js +3 -5
  371. package/dist/mcp/memory-tools.js +4 -9
  372. package/dist/mcp/protocol.d.ts +8 -26
  373. package/dist/mcp/protocol.js +3 -6
  374. package/dist/mcp/recall-tools.js +3 -3
  375. package/dist/mcp/request.d.ts +3 -11
  376. package/dist/mcp/request.js +10 -26
  377. package/dist/mcp/server.d.ts +3 -9
  378. package/dist/mcp/server.js +5 -16
  379. package/dist/mcp/session-state.d.ts +1 -1
  380. package/dist/mcp/session-state.js +5 -11
  381. package/dist/mcp/stdio.d.ts +2 -6
  382. package/dist/mcp/stdio.js +48 -12
  383. package/dist/mcp/tools.js +5 -3
  384. package/dist/objects/customer-notes.d.ts +7 -42
  385. package/dist/objects/customer-notes.js +7 -48
  386. package/dist/objects/decisions.d.ts +12 -50
  387. package/dist/objects/decisions.js +15 -53
  388. package/dist/objects/incidents.d.ts +7 -38
  389. package/dist/objects/incidents.js +7 -41
  390. package/dist/objects/policies.d.ts +11 -89
  391. package/dist/objects/policies.js +11 -98
  392. package/dist/objects/processes.d.ts +7 -52
  393. package/dist/objects/processes.js +7 -61
  394. package/dist/objects/project-briefs.d.ts +11 -55
  395. package/dist/objects/project-briefs.js +14 -75
  396. package/dist/objects/skills.d.ts +7 -42
  397. package/dist/objects/skills.js +8 -54
  398. package/dist/postinstall.js +6 -19
  399. package/dist/predictions/planning-fallacy.d.ts +11 -34
  400. package/dist/rerankers/clef.js +3 -31
  401. package/dist/rerankers/cross-encoder.js +4 -6
  402. package/dist/rerankers/jev.js +9 -6
  403. package/dist/rerankers/llm.d.ts +2 -11
  404. package/dist/rerankers/llm.js +14 -14
  405. package/dist/rerankers/outage-warning.d.ts +2 -1
  406. package/dist/rerankers/outage-warning.js +2 -1
  407. package/dist/rerankers/remote.d.ts +1 -0
  408. package/dist/rerankers/remote.js +2 -0
  409. package/dist/rerankers/types.d.ts +2 -14
  410. package/dist/search/boosts.d.ts +0 -2
  411. package/dist/search/boosts.js +2 -2
  412. package/dist/search/goal-boost.d.ts +23 -0
  413. package/dist/search/goal-boost.js +90 -0
  414. package/dist/search/hybrid.d.ts +2 -0
  415. package/dist/search/multihop.js +2 -3
  416. package/dist/search/path-context.d.ts +5 -18
  417. package/dist/search/path-context.js +5 -18
  418. package/dist/search/physics-search.js +11 -8
  419. package/dist/search/rrf.d.ts +7 -35
  420. package/dist/search/rrf.js +5 -30
  421. package/dist/search/vector.d.ts +7 -0
  422. package/dist/search/vector.js +35 -5
  423. package/dist/server/auth.d.ts +9 -28
  424. package/dist/server/auth.js +32 -25
  425. package/dist/server/boot.d.ts +2 -21
  426. package/dist/server/boot.js +63 -96
  427. package/dist/server/client-ip.d.ts +3 -19
  428. package/dist/server/client-ip.js +5 -28
  429. package/dist/server/defaults.d.ts +3 -0
  430. package/dist/server/defaults.js +4 -0
  431. package/dist/server/key-check-bounds.d.ts +2 -1
  432. package/dist/server/lifecycle.d.ts +5 -4
  433. package/dist/server/lifecycle.js +19 -4
  434. package/dist/server/mcp-http.js +10 -44
  435. package/dist/server/rate-limit.d.ts +5 -18
  436. package/dist/server/rate-limit.js +4 -13
  437. package/dist/server/request.d.ts +4 -8
  438. package/dist/server/request.js +9 -17
  439. package/dist/server/route-table.js +42 -49
  440. package/dist/server/routes/admin.js +22 -28
  441. package/dist/server/routes/customer-notes.js +7 -12
  442. package/dist/server/routes/decisions.js +10 -17
  443. package/dist/server/routes/incidents.js +12 -20
  444. package/dist/server/routes/memories.js +12 -32
  445. package/dist/server/routes/object-routes.d.ts +0 -5
  446. package/dist/server/routes/object-routes.js +7 -15
  447. package/dist/server/routes/policies.js +10 -18
  448. package/dist/server/routes/predictions.js +13 -25
  449. package/dist/server/routes/processes.js +16 -26
  450. package/dist/server/routes/project-briefs.js +9 -16
  451. package/dist/server/routes/recall.js +10 -35
  452. package/dist/server/routes/skills.js +8 -18
  453. package/dist/server/server-detect.d.ts +9 -53
  454. package/dist/server/server-detect.js +24 -90
  455. package/dist/server/sleep-offload.js +4 -1
  456. package/dist/server/types.d.ts +12 -10
  457. package/dist/server/validation.d.ts +3 -1
  458. package/dist/server/validation.js +10 -23
  459. package/dist/server.d.ts +2 -2
  460. package/dist/server.js +3 -2
  461. package/dist/sharing/global-store.d.ts +19 -0
  462. package/dist/sharing/global-store.js +75 -0
  463. package/dist/sharing/global-sync.d.ts +6 -0
  464. package/dist/sharing/global-sync.js +80 -0
  465. package/dist/sharing/project-merge.d.ts +4 -5
  466. package/dist/sharing/project-merge.js +9 -7
  467. package/dist/sharing/scope.d.ts +4 -11
  468. package/dist/sharing/scope.js +4 -11
  469. package/dist/sharing/search-both.d.ts +60 -0
  470. package/dist/sharing/search-both.js +138 -0
  471. package/dist/sharing/share.d.ts +38 -0
  472. package/dist/sharing/share.js +226 -0
  473. package/dist/store/ambient.js +4 -9
  474. package/dist/store/audit-event.d.ts +4 -12
  475. package/dist/store/audit-event.js +4 -12
  476. package/dist/store/audit.d.ts +2 -5
  477. package/dist/store/audit.js +4 -12
  478. package/dist/store/auth.d.ts +6 -6
  479. package/dist/store/auth.js +10 -9
  480. package/dist/store/candidates.d.ts +20 -3
  481. package/dist/store/candidates.js +77 -48
  482. package/dist/store/card-detail.d.ts +2 -1
  483. package/dist/store/card-detail.js +2 -1
  484. package/dist/store/cards.d.ts +14 -9
  485. package/dist/store/cards.js +72 -126
  486. package/dist/store/conflicts.d.ts +4 -21
  487. package/dist/store/conflicts.js +71 -150
  488. package/dist/store/connectors/github.d.ts +10 -10
  489. package/dist/store/connectors/github.js +10 -10
  490. package/dist/store/dashboard-reads.js +4 -8
  491. package/dist/store/delete-and-batch.d.ts +11 -31
  492. package/dist/store/delete-and-batch.js +48 -64
  493. package/dist/store/delivery-recorder.d.ts +8 -4
  494. package/dist/store/delivery-recorder.js +25 -15
  495. package/dist/store/diagnostics.js +6 -13
  496. package/dist/store/dormant.d.ts +7 -29
  497. package/dist/store/dormant.js +15 -56
  498. package/dist/store/embeddings/index.d.ts +11 -80
  499. package/dist/store/embeddings/index.js +71 -207
  500. package/dist/store/embeddings/local.js +15 -32
  501. package/dist/store/embeddings/provider.d.ts +16 -53
  502. package/dist/store/embeddings/provider.js +41 -69
  503. package/dist/store/entry-reads.d.ts +21 -57
  504. package/dist/store/entry-reads.js +65 -126
  505. package/dist/store/entry-row.d.ts +6 -35
  506. package/dist/store/entry-row.js +9 -37
  507. package/dist/store/entry-writes.d.ts +10 -16
  508. package/dist/store/entry-writes.js +17 -35
  509. package/dist/store/goals.d.ts +3 -40
  510. package/dist/store/goals.js +21 -160
  511. package/dist/store/graph-queue.d.ts +6 -22
  512. package/dist/store/graph-queue.js +14 -45
  513. package/dist/store/graph-reads.d.ts +11 -35
  514. package/dist/store/graph-reads.js +20 -58
  515. package/dist/store/graph-rows.d.ts +3 -6
  516. package/dist/store/graph-rows.js +6 -9
  517. package/dist/store/graph-view-rows.d.ts +2 -1
  518. package/dist/store/graph-view-rows.js +2 -1
  519. package/dist/store/graph-writes.d.ts +6 -31
  520. package/dist/store/graph-writes.js +13 -37
  521. package/dist/store/half-life.d.ts +2 -1
  522. package/dist/store/half-life.js +14 -16
  523. package/dist/store/handoffs.d.ts +5 -11
  524. package/dist/store/handoffs.js +16 -34
  525. package/dist/store/index-and-stats.d.ts +6 -19
  526. package/dist/store/index-and-stats.js +34 -82
  527. package/dist/store/markdown.d.ts +2 -6
  528. package/dist/store/markdown.js +21 -20
  529. package/dist/store/mirrors.d.ts +2 -6
  530. package/dist/store/mirrors.js +14 -23
  531. package/dist/store/open.js +6 -13
  532. package/dist/store/planning-fallacy-evidence.d.ts +2 -30
  533. package/dist/store/planning-fallacy-evidence.js +8 -37
  534. package/dist/store/port.d.ts +82 -79
  535. package/dist/store/predictions.d.ts +11 -51
  536. package/dist/store/predictions.js +38 -119
  537. package/dist/{cli → store}/quality-repair.js +15 -31
  538. package/dist/store/raw-archive.d.ts +4 -16
  539. package/dist/store/raw-archive.js +14 -29
  540. package/dist/store/recall-scope.d.ts +19 -79
  541. package/dist/store/recall-scope.js +21 -82
  542. package/dist/store/recall-trace.d.ts +10 -77
  543. package/dist/store/recall-trace.js +19 -95
  544. package/dist/store/rejected-values.js +7 -19
  545. package/dist/store/rejection.d.ts +12 -58
  546. package/dist/store/rejection.js +13 -65
  547. package/dist/store/report-reads.d.ts +6 -0
  548. package/dist/store/report-reads.js +29 -0
  549. package/dist/store/rows.d.ts +9 -19
  550. package/dist/store/rows.js +36 -34
  551. package/dist/store/rule-sql.d.ts +5 -0
  552. package/dist/store/rule-sql.js +37 -0
  553. package/dist/store/search-rows.d.ts +7 -38
  554. package/dist/store/search-rows.js +14 -50
  555. package/dist/store/sessions.d.ts +10 -47
  556. package/dist/store/sessions.js +17 -73
  557. package/dist/store/sqlite/connector-events-group.js +2 -2
  558. package/dist/store/sqlite/entry-writes-group.js +2 -2
  559. package/dist/store/sqlite/error-codec.js +4 -2
  560. package/dist/store/sqlite/executor.d.ts +2 -1
  561. package/dist/store/sqlite/executor.js +2 -1
  562. package/dist/store/sqlite/local.d.ts +3 -3
  563. package/dist/store/sqlite/local.js +1 -0
  564. package/dist/store/sqlite/object-rows.js +6 -3
  565. package/dist/store/sqlite/objects-group.js +4 -2
  566. package/dist/store/sqlite/quarantine-group.js +2 -2
  567. package/dist/store/sqlite/store.d.ts +1 -4
  568. package/dist/store/sqlite/store.js +2 -3
  569. package/dist/store/sqlite/vector-reads-group.js +2 -2
  570. package/dist/store/sqlite/worker-jobs.js +1 -1
  571. package/dist/store/sqlite/worker-ops.d.ts +13 -2
  572. package/dist/store/sqlite/worker-ops.js +13 -2
  573. package/dist/store/sqlite/worker-store.js +5 -2
  574. package/dist/store/summaries.d.ts +10 -62
  575. package/dist/store/summaries.js +34 -146
  576. package/dist/store/summary-dirty.js +1 -1
  577. package/dist/store/tenant-lookup.d.ts +6 -1
  578. package/dist/store/tenant-lookup.js +7 -0
  579. package/dist/store/tenant.d.ts +2 -16
  580. package/dist/store/tenant.js +4 -19
  581. package/dist/store/token-ledger.d.ts +13 -43
  582. package/dist/store/token-ledger.js +19 -50
  583. package/dist/store/usage-reports.js +5 -13
  584. package/dist/store/vector-index.d.ts +46 -0
  585. package/dist/store/vector-index.js +170 -0
  586. package/dist/store/working-memory.d.ts +6 -25
  587. package/dist/store/working-memory.js +18 -52
  588. package/dist/store/yaml.d.ts +1 -5
  589. package/dist/store/yaml.js +2 -6
  590. package/dist/support-bundle.js +7 -5
  591. package/dist/trust/reject-flow.js +1 -3
  592. package/dist/util/anthropic-messages.js +14 -2
  593. package/dist/util/atomic-write.d.ts +2 -1
  594. package/dist/util/atomic-write.js +35 -4
  595. package/dist/util/capped-json.d.ts +6 -0
  596. package/dist/util/capped-json.js +46 -0
  597. package/dist/util/crash-handlers.d.ts +8 -1
  598. package/dist/util/crash-handlers.js +52 -10
  599. package/dist/util/env.d.ts +13 -6
  600. package/dist/util/env.js +14 -7
  601. package/dist/util/home-path.d.ts +2 -1
  602. package/dist/util/home-path.js +4 -2
  603. package/dist/util/http-retry.d.ts +3 -1
  604. package/dist/util/http-retry.js +26 -5
  605. package/dist/util/log.d.ts +3 -2
  606. package/dist/util/log.js +25 -9
  607. package/dist/util/request-scope.d.ts +2 -1
  608. package/dist/util/request-scope.js +2 -1
  609. package/dist/util/secret-detect.d.ts +5 -30
  610. package/dist/util/secret-detect.js +13 -39
  611. package/dist/util/stored-json.d.ts +1 -1
  612. package/dist/util/stored-json.js +1 -1
  613. package/dist/util/token-text.d.ts +1 -4
  614. package/dist/util/token-text.js +1 -4
  615. package/dist/util/transcript-tail.d.ts +2 -0
  616. package/dist/util/transcript-tail.js +24 -23
  617. package/dist/util/version.d.ts +5 -20
  618. package/dist/util/version.js +5 -20
  619. package/extensions/openclaw-plugin/index.ts +1 -1
  620. package/extensions/openclaw-plugin/openclaw.plugin.json +1 -1
  621. package/extensions/openclaw-plugin/package.json +1 -1
  622. package/openclaw.plugin.json +1 -1
  623. package/package.json +1 -1
  624. package/dist/connectors/github/cli-impl.d.ts +0 -25
  625. package/dist/sharing/shared.d.ts +0 -187
  626. package/dist/sharing/shared.js +0 -563
  627. package/dist/store/vector-writes.d.ts +0 -13
  628. package/dist/store/vector-writes.js +0 -89
  629. /package/dist/{cli → store}/quality-repair.d.ts +0 -0
@@ -14,17 +14,18 @@ import { createRateLimiter } from './rate-limit.js';
14
14
  import { RecallContractError } from '../api/index.js';
15
15
  import { handleSlackEventsWebhook } from '../connectors/slack/webhook.js';
16
16
  import { handleGitHubEventsWebhook } from '../connectors/github/webhook.js';
17
- import { bodyDeadlineMs, BodyTimeoutError, BodyTooLargeError, closeAfterReply, DeadlineExceededError, HttpError, JSON_HEADERS, sendJson } from '../util/http-util.js';
17
+ import { BodyTimeoutError, BodyTooLargeError, closeAfterReply, DeadlineExceededError, HttpError, JSON_HEADERS, sendJson } from '../util/http-util.js';
18
18
  import { workerCounts } from '../store/sqlite/executor-counts.js';
19
- import { isLoopback, LIMITER_MAX_KEYS } from './auth.js';
19
+ import { isLocalCaller, LIMITER_MAX_KEYS } from './auth.js';
20
20
  import { enforceRateLimit, warnIfClientIpHeaderUnpinned } from './client-ip.js';
21
21
  import { answerAtDeadline, handlerDeadlineCount, isAbandoned, requestDeadlineFor } from './deadline.js';
22
- import { drainAndClose } from './lifecycle.js';
22
+ import { DEFAULT_SHUTDOWN_DRAIN_MS, drainAndClose, setKeepAliveTimeouts, shutdownBoundMs } from './lifecycle.js';
23
23
  import { readyProbeFor } from './ready.js';
24
- import { installCrashHandlers } from '../util/crash-handlers.js';
24
+ import { DEFAULT_SERVER_HOST, DEFAULT_SERVER_PORT } from './defaults.js';
25
+ import { installCrashHandlers, installSignalHandlers } from '../util/crash-handlers.js';
25
26
  import { handleMcpPost, handleMcpStream } from './mcp-http.js';
26
27
  import { MCP_PROJECT_SCOPED_HEADER } from '../core/project-identity.js';
27
- import { logRequestFailure, noteAccess, openRequest, parseRequest, rejectEncodedSlash, replyFor, sendError } from './request.js';
28
+ import { accessRouteOf, logRequestFailure, noteAccess, openRequest, parseRequest, rejectEncodedSlash, replyFor, sendError } from './request.js';
28
29
  import { createListener, warnIfCleartext } from './tls.js';
29
30
  import { assertAddonRoutes, assertPublicJson, dispatchAddonRoute, dispatchPublicJson, dispatchV1Route, isPublicRoute } from './route-table.js';
30
31
  // server.address() returns AddressInfo once a TCP socket is bound; null before
@@ -32,10 +33,8 @@ import { assertAddonRoutes, assertPublicJson, dispatchAddonRoute, dispatchPublic
32
33
  function isAddressInfo(a) {
33
34
  return a !== null && typeof a !== 'string';
34
35
  }
35
- // Pinned at module load. Bumped alongside package.json on releases. The
36
- // HTTP /health response uses this; reading package.json synchronously here
37
- // would couple the daemon to its on-disk install path, which we want to
38
- // avoid for tests that mkdtemp a hippoRoot.
36
+ // Pinned at module load, bumped with package.json on releases; reading package.json synchronously here would couple the daemon to its install path,
37
+ // which tests that mkdtemp a hippoRoot avoid.
39
38
  const VERSION = PACKAGE_VERSION;
40
39
  const LOOPBACK_HOSTS = new Set(['127.0.0.1', '::1', 'localhost']);
41
40
  async function handleRequest(req, res, opts, options) {
@@ -61,7 +60,8 @@ async function handleRequest(req, res, opts, options) {
61
60
  const routeRequest = { req, res, opts, query };
62
61
  if (await runWithRequestStores(() => dispatchScopedRoute(routeRequest, method, path), { busyWaitMs: SERVER_DB_WAIT_MS }))
63
62
  return;
64
- // A scope of its own, which the heartbeat timer keeps after it closes, so the key check and every heartbeat wait the server's lock wait. Store-ready: the stream only authenticates, through the port.
63
+ // A scope of its own, which the heartbeat timer keeps after it closes, so the key check and every
64
+ // heartbeat wait the server's lock wait. Store-ready: the stream only authenticates, through the port.
65
65
  if (method === 'GET' && path === '/mcp/stream') {
66
66
  noteAccess(req, { route: path });
67
67
  await runWithRequestStores(() => handleMcpStream(req, res, opts, streamSlots), { busyWaitMs: SERVER_DB_WAIT_MS });
@@ -114,11 +114,9 @@ async function dispatchScopedRoute(r, method, path) {
114
114
  return false;
115
115
  }
116
116
  function sendHealth(req, res, startedAt) {
117
- // Loopback callers (detectServer's stale-pidfile probe reads version and
118
- // pid) get the full body. Non-loopback callers get liveness only: the
119
- // version string would fingerprint the build for the public internet and
120
- // the pid is noise. Platform health checks only need the 200.
121
- if (isLoopback(req.socket.remoteAddress)) {
117
+ // A caller on this machine (detectServer's probe reads version and pid) gets the full body; anyone else, even a proxied caller or a browser page on loopback,
118
+ // gets liveness only: the version would fingerprint the build and the pid is noise. Platform health checks only need the 200.
119
+ if (isLocalCaller(req)) {
122
120
  sendJson(res, 200, {
123
121
  ok: true,
124
122
  version: VERSION,
@@ -135,7 +133,8 @@ function sendHealth(req, res, startedAt) {
135
133
  sendJson(res, 200, { ok: true });
136
134
  }
137
135
  }
138
- /** Readiness: one cheap read on the served store under the server's lock wait. /health stays liveness only, so a probe can tell a store that does not answer from a dead process. */
136
+ /** Readiness: one cheap read on the served store under the server's lock wait. /health stays
137
+ * liveness only, so a probe can tell a store that does not answer from a dead process. */
139
138
  async function sendReady(res, store) {
140
139
  const { readiness } = store;
141
140
  if (readiness === undefined) {
@@ -156,10 +155,8 @@ function assertBindable(host) {
156
155
  }
157
156
  }
158
157
  async function assertNoLiveServer(hippoRoot) {
159
- // Refuse to start if a live hippo server already serves this hippoRoot.
160
- // detectServer probes the recorded /health — a stale pidfile is unlinked and
161
- // ignored, but a live peer means a concurrent `hippo serve` would race for
162
- // the port and clobber the pidfile.
158
+ // Refuse to start if a live hippo server already serves this hippoRoot (detectServer probes the recorded /health and unlinks a stale pidfile):
159
+ // a concurrent `hippo serve` would race for the port and clobber the pidfile.
163
160
  const existing = await detectServer(hippoRoot);
164
161
  if (existing) {
165
162
  throw new Error(`hippo serve: already running on port ${existing.port} (pid ${existing.pid}). ` +
@@ -175,10 +172,8 @@ function bootRateLimiter(perAddress) {
175
172
  return undefined;
176
173
  if (perAddress !== undefined)
177
174
  return limiterFor(perAddress);
178
- // Per-IP rate limiter for /v1/* and /mcp*. Built here (not at module scope) so
179
- // HIPPO_V1_RPS is read at boot, matching HIPPO_PORT above and letting a test
180
- // set the rate before serve(). A non-positive or non-finite value disables
181
- // limiting (the opt-out knob).
175
+ // Per-IP rate limiter for /v1/* and /mcp*, built here (not at module scope) so HIPPO_V1_RPS is read at boot and a test can set it before serve().
176
+ // A non-positive or non-finite value disables limiting (the opt-out knob).
182
177
  const v1Rps = Number(envV1Rps() ?? 20);
183
178
  return Number.isFinite(v1Rps) && v1Rps > 0 ? limiterFor({ ratePerSec: v1Rps, burst: v1Rps * 2 }) : undefined;
184
179
  }
@@ -218,8 +213,7 @@ function createStoreHolder(hippoRoot, store) {
218
213
  if (heldDb || stopHolding || !existsSync(getHippoDbPath(hippoRoot)))
219
214
  return;
220
215
  try {
221
- // The 'finish' listener can fire inside a request scope, which would close this connection with the request,
222
- // and inside the block of a `loop: 'off'` route, which would refuse the open.
216
+ // The 'finish' listener can fire inside a request scope (closing this connection with the request) and inside a `loop: 'off'` block (refusing the open).
223
217
  // The server's lock wait: this open runs on the event loop, where SQLite's 5 s default and the 30 s journal-mode retry would stall every request.
224
218
  heldDb = outsideSqliteOffLoop(() => outsideRequestStores(() => openHippoDb(hippoRoot, { busyWaitMs: SERVER_DB_WAIT_MS })));
225
219
  checkpointer = startWalCheckpointer(getHippoDbPath(hippoRoot));
@@ -281,9 +275,23 @@ function replyWithFailure(req, res, err, requestId) {
281
275
  sendError(res, mapped.status, mapped.message);
282
276
  }
283
277
  const HANDLER_LATE = 'the request did not finish by its deadline and was abandoned; a write it started may or may not be saved';
284
- // The caller already has its 504, so a second reply would be written into a finished response.
285
- function logLateFailure(req, err) {
286
- log.debug(`${req.method ?? 'GET'} ${(req.url ?? '/').split('?')[0]} ended after its deadline reply: ${errorMessage(err)}`);
278
+ // The caller already has its 504, so this line is the only trace of the failure. The path and the error text can hold caller data, so it carries neither.
279
+ function logLateFailure(req, err, requestId) {
280
+ const fields = { requestId, method: req.method ?? 'GET', route: accessRouteOf(req), failureStatus: replyFor(err).status };
281
+ log.warn('request failed after its deadline reply', { ...fields, errorClass: errorFields(err).errorClass });
282
+ }
283
+ // A handler that ends after the 504 finds the response finished, so sending its own reply throws this: its work ran to the end.
284
+ function isDroppedLateReply(err) {
285
+ return err instanceof Error && 'code' in err && err.code === 'ERR_HTTP_HEADERS_SENT';
286
+ }
287
+ /** A GET only reads; every other method this server routes can write. */
288
+ const isRead = (method) => method === 'GET';
289
+ // A read that ends late changed nothing. A write that ends late ran to its end after its caller was told it may not have been saved.
290
+ function logLateFinish(req, requestId) {
291
+ const method = req.method ?? 'GET';
292
+ if (isRead(method))
293
+ return;
294
+ log.info('request finished after its deadline reply; its own reply was dropped', { requestId, method, route: accessRouteOf(req) });
287
295
  }
288
296
  function replyOrClose(req, res, err, requestId) {
289
297
  try {
@@ -291,7 +299,7 @@ function replyOrClose(req, res, err, requestId) {
291
299
  }
292
300
  catch (replyErr) {
293
301
  // A throw here would be an unhandled rejection, which stops the daemon for every caller.
294
- log.error(`serve: failure reply not sent, socket closed: ${errorMessage(replyErr)}`);
302
+ log.error(`serve: failure reply not sent, socket closed: ${errorMessage(replyErr)}`, errorFields(replyErr));
295
303
  res.destroy();
296
304
  }
297
305
  }
@@ -303,24 +311,19 @@ function answerRequest(req, res, handle, slowWarnMs) {
303
311
  runWithRequestId(requestId, () => {
304
312
  if (deadline)
305
313
  answerAtDeadline(res, deadline, () => replyOrClose(req, res, new DeadlineExceededError(HANDLER_LATE), requestId));
306
- handle().catch((err) => {
314
+ handle().then(() => {
307
315
  if (isAbandoned(res))
308
- logLateFailure(req, err);
309
- else
316
+ logLateFinish(req, requestId);
317
+ }, (err) => {
318
+ if (!isAbandoned(res))
310
319
  replyOrClose(req, res, err, requestId);
320
+ else if (isDroppedLateReply(err))
321
+ logLateFinish(req, requestId);
322
+ else
323
+ logLateFailure(req, err, requestId);
311
324
  });
312
325
  }, deadline);
313
326
  }
314
- // Node's own default, named so the three socket deadlines read together. It times the request arriving, never the handler, so a 10 minute sleep is not cut.
315
- const REQUEST_RECEIVE_TIMEOUT_MS = 300_000;
316
- function setKeepAliveTimeouts(server) {
317
- // The default 5s keepAliveTimeout closes idle sockets just as clients reuse them (ECONNRESET).
318
- // headersTimeout must stay ABOVE keepAliveTimeout + keepAliveTimeoutBuffer (1s), or it closes idle reused sockets itself.
319
- server.keepAliveTimeout = 65_000;
320
- server.headersTimeout = 70_000;
321
- // Never below what a raised body deadline allows, or Node's bare 408 would come before the route's own.
322
- server.requestTimeout = Math.max(REQUEST_RECEIVE_TIMEOUT_MS, server.headersTimeout + bodyDeadlineMs());
323
- }
324
327
  function listenOn(server, port, host) {
325
328
  return new Promise((resolve, reject) => {
326
329
  const onError = (err) => {
@@ -338,57 +341,23 @@ function listenOn(server, port, host) {
338
341
  server.listen(port, host);
339
342
  });
340
343
  }
341
- function installSignalHandlers(stop) {
342
- let shuttingDown = false;
343
- const gracefulShutdown = async (signal) => {
344
- if (shuttingDown)
345
- return;
346
- shuttingDown = true;
347
- log.warn(`received ${signal}, shutting down`);
348
- try {
349
- await stop();
350
- process.exit(0);
351
- }
352
- catch (err) {
353
- log.error(`error during stop: ${errorMessage(err)}`, errorFields(err));
354
- process.exit(1);
355
- }
356
- };
357
- process.once('SIGTERM', () => { void gracefulShutdown('SIGTERM'); });
358
- process.once('SIGINT', () => { void gracefulShutdown('SIGINT'); });
344
+ function exitOnSignalOrCrash(stop, drainMs) {
345
+ const shutdown = { run: stop, boundMs: shutdownBoundMs(drainMs) };
346
+ installSignalHandlers('serve', shutdown);
347
+ installCrashHandlers('serve', shutdown);
359
348
  }
360
- /**
361
- * Boot the HTTP daemon on host:port and write the pidfile under hippoRoot.
362
- *
363
- * Refuses non-loopback hosts at boot unless
364
- * HIPPO_REQUIRE_AUTH=1 is set. The auth middleware (buildContextWithAuth /
365
- * requireAuth) has shipped and every route checks it except GET /health
366
- * (public by design for platform health checks) and the two connector
367
- * webhooks in PUBLIC_ROUTES, which are HMAC-gated by their own signing
368
- * secrets and 404 when those secrets are unset, and any publicJson GET path. But the loopback
369
- * no-auth fallback inside buildContextWithAuth still admits unauthenticated
370
- * requests from a loopback remote address (unless they carry Forwarded,
371
- * X-Forwarded-For/-Host/-Proto, X-Real-IP, Cf-Connecting-Ip, True-Client-Ip or Fly-Client-Ip, which mark a same-host proxy and get
372
- * a 401 like any keyless remote request), so binding to a non-loopback host
373
- * is only safe once that fallback is disabled with HIPPO_REQUIRE_AUTH=1,
374
- * which forces every request (loopback or not) through Bearer-token
375
- * validation. Without that env var set, a non-loopback bind would expose the
376
- * DB to the network with no auth, so we fail fast instead.
377
- *
378
- * Use port: 0 in tests to bind to an ephemeral port and read the actual
379
- * port back via server.address() after listen.
380
- */
349
+ /** Boots the HTTP daemon on host:port and writes the pidfile under hippoRoot; refuses non-loopback hosts unless HIPPO_REQUIRE_AUTH=1.
350
+ * Use port: 0 in tests for an ephemeral port. See docs/ARCHITECTURE.md#srcserverbootts. */
381
351
  export async function serve(opts) {
382
- const host = opts.host ?? '127.0.0.1';
383
- const requestedPort = opts.port ?? Number(envPort() ?? 6789);
352
+ const host = opts.host ?? DEFAULT_SERVER_HOST;
353
+ const requestedPort = opts.port ?? Number(envPort() ?? DEFAULT_SERVER_PORT);
384
354
  const routes = frozenAddonRoutes(opts.routes);
385
355
  const publicJsonBodies = assertPublicJson(opts.publicJson ?? {});
386
356
  const limiters = bootLimiters(opts.rateLimits);
387
357
  assertBindable(host);
388
358
  await assertNoLiveServer(opts.hippoRoot);
389
- // The server's start time. Single source of truth: it is returned by every
390
- // GET /health response and (below) written into the pidfile, so detectServer
391
- // can match the two and prove a pid-reusing impostor is not the real server.
359
+ // Single source of truth for the start time: returned by GET /health and written into the pidfile, so detectServer can prove a pid-reusing impostor is not
360
+ // this server.
392
361
  const startedAt = new Date().toISOString();
393
362
  // Open /mcp/stream count per client key, so the cap is per server rather than per process.
394
363
  const streamSlots = new Map();
@@ -414,15 +383,15 @@ export async function serve(opts) {
414
383
  await served.store.close();
415
384
  await holder.release();
416
385
  };
417
- if (opts.handleSignals) {
418
- installSignalHandlers(stop);
419
- installCrashHandlers('serve', stop);
420
- }
386
+ if (opts.handleSignals)
387
+ exitOnSignalOrCrash(stop, opts.shutdownDrainMs ?? DEFAULT_SHUTDOWN_DRAIN_MS);
421
388
  return { port: actualPort, url, stop, server };
422
389
  }
423
390
  function frozenAddonRoutes(addonRoutes) {
424
391
  // A frozen copy, so a route the caller adds or renames after boot never skips the check below.
425
- const routes = Object.freeze((addonRoutes ?? []).map(({ path, handler, storeReady }) => Object.freeze(storeReady === undefined ? { path, handler } : { path, handler, storeReady })));
392
+ const routes = Object.freeze((addonRoutes ?? []).map(({ path, handler, storeReady }) => Object.freeze(storeReady === undefined
393
+ ? { path, handler }
394
+ : { path, handler, storeReady })));
426
395
  assertAddonRoutes(routes);
427
396
  return routes;
428
397
  }
@@ -456,10 +425,8 @@ async function listenAndDescribe(server, port, host, tls) {
456
425
  return { port: actualPort, url };
457
426
  }
458
427
  async function stopListening(opts, server, inflight, startedAt) {
459
- // Remove the pidfile only if it still names this server. A newer server
460
- // may have started on this hippoRoot and rewritten the pidfile; an
461
- // unconditional unlink here would orphan it.
428
+ // Remove the pidfile only if it still names this server: a newer server may have rewritten it, and an unconditional unlink would orphan it.
462
429
  removePidfileIfOwned(opts.hippoRoot, { pid: process.pid, startedAt });
463
- await drainAndClose(server, inflight, opts.shutdownDrainMs ?? 5000);
430
+ await drainAndClose(server, inflight, opts.shutdownDrainMs ?? DEFAULT_SHUTDOWN_DRAIN_MS);
464
431
  }
465
432
  //# sourceMappingURL=boot.js.map
@@ -1,25 +1,9 @@
1
1
  import type { IncomingMessage } from 'node:http';
2
2
  import type { RateLimiter } from './rate-limit.js';
3
3
  /**
4
- * Rate-limit key for a request. Defaults to the socket's remote address.
5
- *
6
- * Behind a TLS-terminating proxy (Fly, most PaaS ingress) every socket
7
- * carries the proxy's address, so per-IP buckets collapse into one global
8
- * bucket that unauthenticated traffic can drain before auth runs. Set
9
- * HIPPO_CLIENT_IP_HEADER to the header the proxy stamps with the real
10
- * client address (fly-client-ip on Fly, which the edge always overwrites)
11
- * to key buckets per client instead.
12
- *
13
- * Only set this when a trusted proxy fronts EVERY request: a directly
14
- * reachable server honoring the header would let clients mint a fresh
15
- * bucket per request and bypass the limiter entirely.
16
- *
17
- * HIPPO_TRUSTED_PROXIES (comma-separated IPs or CIDRs) pins which peers count
18
- * as that proxy: the header is read only when the socket peer is listed, and
19
- * listed hops are skipped. In a comma-joined chain (X-Forwarded-For) the key is
20
- * the rightmost hop no trusted proxy added; entries to its left are whatever
21
- * the client sent, so they never pick the bucket.
22
- */
4
+ * Rate-limit key: the socket's remote address, which behind a TLS proxy collapses into one bucket unauthenticated traffic can drain.
5
+ * Set HIPPO_CLIENT_IP_HEADER (fly-client-ip on Fly) only when a trusted proxy fronts EVERY request, or clients could mint a fresh bucket per request.
6
+ * HIPPO_TRUSTED_PROXIES (IPs or CIDRs) pins which peers count as that proxy; in a comma-joined chain the key is the rightmost hop no trusted proxy added. */
23
7
  export declare function clientIpForRateLimit(req: IncomingMessage): string;
24
8
  /** Said once at boot: with no proxy list, a caller that reaches the port without the proxy picks its own bucket by sending the header. */
25
9
  export declare function warnIfClientIpHeaderUnpinned(): void;
@@ -5,25 +5,9 @@ import { log } from '../util/log.js';
5
5
  import { HttpError } from '../util/http-util.js';
6
6
  const IPV6_PREFIX_GROUPS = 4;
7
7
  /**
8
- * Rate-limit key for a request. Defaults to the socket's remote address.
9
- *
10
- * Behind a TLS-terminating proxy (Fly, most PaaS ingress) every socket
11
- * carries the proxy's address, so per-IP buckets collapse into one global
12
- * bucket that unauthenticated traffic can drain before auth runs. Set
13
- * HIPPO_CLIENT_IP_HEADER to the header the proxy stamps with the real
14
- * client address (fly-client-ip on Fly, which the edge always overwrites)
15
- * to key buckets per client instead.
16
- *
17
- * Only set this when a trusted proxy fronts EVERY request: a directly
18
- * reachable server honoring the header would let clients mint a fresh
19
- * bucket per request and bypass the limiter entirely.
20
- *
21
- * HIPPO_TRUSTED_PROXIES (comma-separated IPs or CIDRs) pins which peers count
22
- * as that proxy: the header is read only when the socket peer is listed, and
23
- * listed hops are skipped. In a comma-joined chain (X-Forwarded-For) the key is
24
- * the rightmost hop no trusted proxy added; entries to its left are whatever
25
- * the client sent, so they never pick the bucket.
26
- */
8
+ * Rate-limit key: the socket's remote address, which behind a TLS proxy collapses into one bucket unauthenticated traffic can drain.
9
+ * Set HIPPO_CLIENT_IP_HEADER (fly-client-ip on Fly) only when a trusted proxy fronts EVERY request, or clients could mint a fresh bucket per request.
10
+ * HIPPO_TRUSTED_PROXIES (IPs or CIDRs) pins which peers count as that proxy; in a comma-joined chain the key is the rightmost hop no trusted proxy added. */
27
11
  export function clientIpForRateLimit(req) {
28
12
  const socketIp = req.socket.remoteAddress ?? 'unknown';
29
13
  const header = envClientIpHeader();
@@ -101,15 +85,8 @@ function isTrustedProxy(list, ip) {
101
85
  return family !== 0 && list.check(bare, family === 6 ? 'ipv6' : 'ipv4');
102
86
  }
103
87
  export function enforceRateLimit(req, path, limiter) {
104
- // Per-IP rate limit on /v1/* and /mcp* to bound api-key-id enumeration. /health
105
- // (a liveness probe) and other paths are never throttled. A 429 thrown
106
- // here lands in the createServer catch like any other HttpError.
107
- //
108
- // Keyed on the socket's remote address by default. Behind a TLS-terminating
109
- // proxy every socket carries the proxy's address, collapsing the per-IP
110
- // buckets into one global bucket that pre-auth traffic can drain; set
111
- // HIPPO_CLIENT_IP_HEADER there so each real client gets its own bucket
112
- // (see clientIpForRateLimit).
88
+ // Per-IP rate limit on /v1/* and /mcp* to bound api-key-id enumeration; /health and other paths are never throttled, and a 429 lands in the createServer
89
+ // catch. Keyed per clientIpForRateLimit (set HIPPO_CLIENT_IP_HEADER behind a TLS-terminating proxy, or all clients share one bucket).
113
90
  if (limiter && (path.startsWith('/v1/') || path === '/mcp' || path === '/mcp/stream')) {
114
91
  if (!limiter.check(clientLimitKey(req))) {
115
92
  throw new HttpError(429, 'rate limit exceeded', limiter.retryAfterSec);
@@ -0,0 +1,3 @@
1
+ export declare const DEFAULT_SERVER_PORT = 6789;
2
+ export declare const DEFAULT_SERVER_HOST = "127.0.0.1";
3
+ //# sourceMappingURL=defaults.d.ts.map
@@ -0,0 +1,4 @@
1
+ // `hippo serve` reads these without loading the server, so the CLI and serve() cannot disagree on a default.
2
+ export const DEFAULT_SERVER_PORT = 6789;
3
+ export const DEFAULT_SERVER_HOST = '127.0.0.1';
4
+ //# sourceMappingURL=defaults.js.map
@@ -1,5 +1,6 @@
1
1
  export interface KeyCheckBounds {
2
- /** Throws the 429 a refused key check answers, before any derivation: no room left to wait (Retry-After 1), or `address` has spent its tries on `keyId` (Retry-After 12). */
2
+ /** Throws the 429 a refused key check answers, before any derivation: no room left to
3
+ * wait (Retry-After 1), or `address` has spent its tries on `keyId` (Retry-After 12). */
3
4
  admit(keyId: string, address: string): void;
4
5
  /** Runs `derive` now or once a slot frees; `admit` in the same tick keeps the queue within its cap. */
5
6
  run<T>(derive: () => Promise<T>): Promise<T>;
@@ -1,7 +1,8 @@
1
1
  import type { Server, ServerResponse } from 'node:http';
2
- /**
3
- * Stop accepting, end streams at once (they never finish on their own), give other in-flight
4
- * requests up to `drainMs`, then close whatever is left.
5
- */
2
+ export declare function setKeepAliveTimeouts(server: Server): void;
3
+ export declare const DEFAULT_SHUTDOWN_DRAIN_MS = 5000;
4
+ /** How long a signal or crash shutdown may run before the process is ended without it. */
5
+ export declare function shutdownBoundMs(drainMs: number): number;
6
+ /** Stops accepting, ends streams at once (they never finish on their own), gives other in-flight requests up to `drainMs`, then closes what is left. */
6
7
  export declare function drainAndClose(server: Server, inflight: ReadonlySet<ServerResponse>, drainMs: number): Promise<void>;
7
8
  //# sourceMappingURL=lifecycle.d.ts.map
@@ -1,8 +1,23 @@
1
+ import { bodyDeadlineMs } from '../util/http-util.js';
1
2
  import { log } from '../util/log.js';
2
- /**
3
- * Stop accepting, end streams at once (they never finish on their own), give other in-flight
4
- * requests up to `drainMs`, then close whatever is left.
5
- */
3
+ // Node's own default, named so the three socket deadlines read together. It times the request arriving, never the handler, so a 10 minute sleep is not cut.
4
+ const REQUEST_RECEIVE_TIMEOUT_MS = 300_000;
5
+ export function setKeepAliveTimeouts(server) {
6
+ // The default 5s keepAliveTimeout closes idle sockets just as clients reuse them (ECONNRESET).
7
+ // headersTimeout must stay ABOVE keepAliveTimeout + keepAliveTimeoutBuffer (1s), or it closes idle reused sockets itself.
8
+ server.keepAliveTimeout = 65_000;
9
+ server.headersTimeout = 70_000;
10
+ // Never below what a raised body deadline allows, or Node's bare 408 would come before the route's own.
11
+ server.requestTimeout = Math.max(REQUEST_RECEIVE_TIMEOUT_MS, server.headersTimeout + bodyDeadlineMs());
12
+ }
13
+ export const DEFAULT_SHUTDOWN_DRAIN_MS = 5000;
14
+ // Past the request drain, a stop only waits for each store thread to end its statement and close: seconds at most, so longer means a thread that will not end.
15
+ const STORE_CLOSE_GRACE_MS = 10_000;
16
+ /** How long a signal or crash shutdown may run before the process is ended without it. */
17
+ export function shutdownBoundMs(drainMs) {
18
+ return drainMs + STORE_CLOSE_GRACE_MS;
19
+ }
20
+ /** Stops accepting, ends streams at once (they never finish on their own), gives other in-flight requests up to `drainMs`, then closes what is left. */
6
21
  export async function drainAndClose(server, inflight, drainMs) {
7
22
  const closed = new Promise((resolve, reject) => {
8
23
  server.close((err) => (err ? reject(err) : resolve()));
@@ -12,17 +12,8 @@ import { isJsonString, isJsonObject } from '../util/json.js';
12
12
  import { FINGERPRINT_HEX_CHARS } from '../util/token-text.js';
13
13
  const DEFAULT_SSE_HEARTBEAT_MS = 60000;
14
14
  const DEFAULT_SSE_MAX_AGE_SEC = 3600;
15
- /**
16
- * Build a per-client key for MCP state isolation under HTTP-MCP. Used by
17
- * mcp/server.ts to scope `lastRecalledIds` to the calling client so two
18
- * clients on the same tenant cannot poison each other's outcome feedback.
19
- *
20
- * Token is hashed (sha256, 16-hex-char prefix) so we never log or persist
21
- * the raw bearer. Combined with remoteAddress so two clients sharing a key
22
- * (e.g. on a shared Postman environment) are still separable in the common
23
- * case. 'noauth' covers loopback no-auth and is acceptable because that
24
- * path is single-host single-user.
25
- */
15
+ /** Per-client key for MCP state isolation under HTTP-MCP (scopes `lastRecalledIds`): sha256 of the token (16 hex chars, so the raw bearer is never logged)
16
+ * plus remoteAddress, so clients sharing a key stay separable; 'noauth' covers loopback no-auth, which is single-host single-user. */
26
17
  function buildMcpClientKey(req) {
27
18
  const auth = readAuthHeader(req);
28
19
  const tokenHash = auth.kind === 'bearer'
@@ -31,24 +22,8 @@ function buildMcpClientKey(req) {
31
22
  const addr = subscriberKey(req.socket.remoteAddress ?? 'unknown');
32
23
  return `http:${tokenHash}:${addr}`;
33
24
  }
34
- // ── MCP-over-HTTP/SSE transport ──
35
- //
36
- // Two routes implement an MCP HTTP transport alongside the stdio one. Both
37
- // dispatch to the same `handleMcpRequest` as the stdio loop in src/mcp/server.ts.
38
- //
39
- // POST /mcp — Send a JSON-RPC request, get a JSON-RPC response synchronously
40
- // in the body. Content-type: application/json both ways.
41
- // GET /mcp/stream — Open an SSE stream for server-initiated messages.
42
- // v1 simplification: this stream is keepalive-only. Clients
43
- // that need server-pushed notifications/progress will see
44
- // only `: ping` comments every 30s. All real responses come
45
- // back synchronously on POST /mcp. This matches the
46
- // "synchronous JSON in body" leg of the MCP HTTP spec and
47
- // is enough for `tools/list` / `tools/call` round-trips.
48
- // Server-initiated SSE messages will be wired in a later task.
49
- //
50
- // Auth: same as /v1/* — Bearer token validated via `requireAuth`, with the
51
- // loopback no-auth fallback. SSE check runs once at stream-open.
25
+ // MCP-over-HTTP/SSE: POST /mcp answers a JSON-RPC request synchronously; GET /mcp/stream is a keepalive-only SSE stream (`: ping` every 30s) in v1.
26
+ // Both dispatch to handleMcpRequest as stdio does. Auth matches /v1/* (Bearer via `requireAuth`, loopback fallback); SSE is checked once at stream-open.
52
27
  // Percent-encoded by the client, so any lowercase Unicode name fits a Latin-1 header.
53
28
  const HEADER_PIECE = /^[A-Za-z0-9\-_.!~*'()%]+$/;
54
29
  function decodeHeaderPiece(raw, header) {
@@ -101,11 +76,8 @@ function mcpContextFor(ctx, clientKey, autoSleep, project) {
101
76
  return mcpCtx;
102
77
  }
103
78
  export async function handleMcpPost(req, res, opts) {
104
- // Build the same Context the /v1/* routes use so MCP tool calls inherit
105
- // the server's bound hippoRoot and the auth-resolved tenantId / actor.
106
- // Without this, executeTool would walk from cwd via findHippoRoot() and
107
- // pull tenant from HIPPO_TENANT, dropping a valid Bearer for tenant B
108
- // back to whatever the env says.
79
+ // Same Context the /v1/* routes use, so MCP tool calls inherit the bound hippoRoot and the auth-resolved tenantId/actor; otherwise executeTool would walk
80
+ // from cwd and read HIPPO_TENANT, dropping a valid Bearer for tenant B to the env's tenant.
109
81
  const ctx = await buildContextWithAuth(req, opts);
110
82
  noteAccess(req, { tenant: ctx.tenantId });
111
83
  const project = callerProjectFromHeaders(req, ctx.hippoRoot);
@@ -120,9 +92,8 @@ export async function handleMcpPost(req, res, opts) {
120
92
  if (!isJsonObject(mcpReq) || !isJsonString(mcpReq.method)) {
121
93
  throw new HttpError(400, 'JSON-RPC body must include a method string');
122
94
  }
123
- // SAFETY: validated above as a plain JSON object carrying a string method;
124
- // the remaining McpRequest wire fields (jsonrpc, id, params) are checked or
125
- // safely defaulted inside handleMcpRequest's JSON-RPC dispatch.
95
+ // SAFETY: validated above as a plain JSON object with a string method; the other McpRequest wire fields (jsonrpc, id, params) are checked or defaulted in
96
+ // handleMcpRequest.
126
97
  const rpcReq = mcpReq;
127
98
  let mcpRes;
128
99
  try {
@@ -178,13 +149,8 @@ export async function handleMcpStream(req, res, opts, streamSlots) {
178
149
  // Initial ping so smoke tests can confirm the stream is live without
179
150
  // waiting for the first keepalive interval.
180
151
  res.write(': ping\n\n');
181
- // SSE hardening:
182
- // - Heartbeat re-validates the bearer (default 60s). If the key was
183
- // revoked or rotated, close the stream with reason='auth_revoked'.
184
- // - MCP_SSE_MAX_AGE_SEC (default 3600) caps stream lifetime; close
185
- // with reason='max_age_exceeded' when reached.
186
- // - MCP_SSE_HEARTBEAT_MS (default 60000) lets tests run with a short
187
- // interval without waiting a full minute.
152
+ // SSE hardening: the heartbeat re-validates the bearer (MCP_SSE_HEARTBEAT_MS, default 60000) and closes with 'auth_revoked' if the key was revoked or
153
+ // rotated; MCP_SSE_MAX_AGE_SEC (default 3600) caps stream lifetime and closes with 'max_age_exceeded'.
188
154
  keepStreamAlive(req, res, opts);
189
155
  }
190
156
  function keepStreamAlive(req, res, opts) {
@@ -1,16 +1,7 @@
1
- /**
2
- * Per-key token-bucket rate limiter for inbound /v1/* requests.
3
- *
4
- * Bounds api-key-id enumeration: a
5
- * client that drains its bucket is denied until it refills. Dependency-free
6
- * and unit-testable in isolation via the injectable `now`.
7
- */
1
+ /** Per-key token-bucket rate limiter for inbound /v1/* requests, bounding api-key-id enumeration. Dependency-free; the injectable `now` makes it
2
+ * unit-testable. */
8
3
  export interface RateLimiter {
9
- /**
10
- * Consume one token for `key`. Returns true if the request is allowed,
11
- * false if the key's bucket is exhausted. `now` (epoch ms) is injectable
12
- * for deterministic tests.
13
- */
4
+ /** Consume one token for `key`: true if allowed, false if the bucket is exhausted; `now` (epoch ms) is injectable for deterministic tests. */
14
5
  check(key: string, now?: number): boolean;
15
6
  readonly retryAfterSec: number;
16
7
  }
@@ -24,11 +15,7 @@ export interface RateLimiterOpts {
24
15
  /** Hard cap on tracked keys; the least-recently-used key is evicted on overflow. */
25
16
  maxKeys: number;
26
17
  }
27
- /**
28
- * Build a token-bucket limiter. Memory is bounded two ways: a sweep (throttled
29
- * to once per `idleEvictMs`) drops idle buckets, and a hard `maxKeys` cap evicts
30
- * the least-recently-used key, so a client rotating source addresses cannot
31
- * grow the map without bound between sweeps.
32
- */
18
+ /** Token-bucket limiter with bounded memory: a sweep (at most once per `idleEvictMs`) drops idle buckets and a `maxKeys` cap evicts the least-recently-used
19
+ * key, so a client rotating source addresses cannot grow the map without bound. */
33
20
  export declare function createRateLimiter(opts: RateLimiterOpts): RateLimiter;
34
21
  //# sourceMappingURL=rate-limit.d.ts.map
@@ -1,16 +1,7 @@
1
- /**
2
- * Per-key token-bucket rate limiter for inbound /v1/* requests.
3
- *
4
- * Bounds api-key-id enumeration: a
5
- * client that drains its bucket is denied until it refills. Dependency-free
6
- * and unit-testable in isolation via the injectable `now`.
7
- */
8
- /**
9
- * Build a token-bucket limiter. Memory is bounded two ways: a sweep (throttled
10
- * to once per `idleEvictMs`) drops idle buckets, and a hard `maxKeys` cap evicts
11
- * the least-recently-used key, so a client rotating source addresses cannot
12
- * grow the map without bound between sweeps.
13
- */
1
+ /** Per-key token-bucket rate limiter for inbound /v1/* requests, bounding api-key-id enumeration. Dependency-free; the injectable `now` makes it
2
+ * unit-testable. */
3
+ /** Token-bucket limiter with bounded memory: a sweep (at most once per `idleEvictMs`) drops idle buckets and a `maxKeys` cap evicts the least-recently-used
4
+ * key, so a client rotating source addresses cannot grow the map without bound. */
14
5
  export function createRateLimiter(opts) {
15
6
  const { ratePerSec, burst, idleEvictMs, maxKeys } = opts;
16
7
  const buckets = new Map();
@@ -13,6 +13,8 @@ interface AccessNote {
13
13
  }
14
14
  /** Names the matched route (the table's pattern, never the path sent) or the caller's tenant for the request's access line. */
15
15
  export declare function noteAccess(req: IncomingMessage, note: AccessNote): void;
16
+ /** The route name the request's access line carries, for a line about the same request logged after its reply. */
17
+ export declare function accessRouteOf(req: IncomingMessage): string;
16
18
  /** The request's id, after stamping the headers every reply carries and starting the slow-request watchdog and the access line. */
17
19
  export declare function openRequest(req: IncomingMessage, res: ServerResponse, slowWarnMs?: number): string;
18
20
  export declare function sendError(res: ServerResponse, status: number, message: string): void;
@@ -23,14 +25,8 @@ interface ParsedRoute {
23
25
  }
24
26
  export declare function parseRequest(req: IncomingMessage): ParsedRoute;
25
27
  export declare function matchPath(pattern: string, path: string): Record<string, string> | null;
26
- /**
27
- * Reject URL-encoded slashes in path segments BEFORE the URL parser decodes
28
- * them — otherwise `%2F` becomes `/`, path-split runs, and the route either
29
- * silently 404s or matches the wrong template.
30
- *
31
- * Only the PATHNAME is scanned (split on the first `?`), so recall queries
32
- * containing URLs like `?q=https%3A%2F%2Fexample.com` are not rejected.
33
- */
28
+ /** Rejects URL-encoded slashes in path segments BEFORE the URL parser decodes them (`%2F` would become `/` and 404 or match the wrong template).
29
+ * Only the PATHNAME is scanned, so recall queries like `?q=https%3A%2F%2Fexample.com` are not rejected. */
34
30
  export declare function rejectEncodedSlash(rawUrl: string): void;
35
31
  export {};
36
32
  //# sourceMappingURL=request.d.ts.map