hippo-memory 1.71.0 → 2.0.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 (625) hide show
  1. package/README.md +7 -5
  2. package/dist/agent-memories/apply.d.ts +0 -11
  3. package/dist/agent-memories/apply.js +11 -26
  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 +3 -2
  12. package/dist/api/audit.d.ts +30 -2
  13. package/dist/api/audit.js +48 -7
  14. package/dist/api/auth.d.ts +11 -9
  15. package/dist/api/auth.js +39 -40
  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-auto.js +4 -2
  24. package/dist/api/context-render.js +4 -3
  25. package/dist/api/context-select.js +15 -29
  26. package/dist/api/context.js +29 -18
  27. package/dist/api/continuity.d.ts +47 -0
  28. package/dist/api/continuity.js +66 -0
  29. package/dist/api/dormant.js +4 -2
  30. package/dist/api/drill-down.js +3 -2
  31. package/dist/api/embeddings.d.ts +17 -0
  32. package/dist/api/embeddings.js +28 -0
  33. package/dist/api/error-reply.d.ts +8 -0
  34. package/dist/api/error-reply.js +21 -0
  35. package/dist/api/forget.d.ts +7 -3
  36. package/dist/api/forget.js +24 -14
  37. package/dist/api/goals.d.ts +1 -1
  38. package/dist/api/goals.js +5 -9
  39. package/dist/api/graph.js +2 -1
  40. package/dist/api/index.d.ts +4 -2
  41. package/dist/api/index.js +4 -2
  42. package/dist/api/instruction-detect.js +5 -1
  43. package/dist/api/learn.d.ts +5 -1
  44. package/dist/api/learn.js +71 -44
  45. package/dist/api/ledger-db.d.ts +2 -6
  46. package/dist/api/ledger-db.js +4 -36
  47. package/dist/api/memories.d.ts +21 -1
  48. package/dist/api/memories.js +51 -1
  49. package/dist/api/objects.d.ts +1 -1
  50. package/dist/api/objects.js +2 -1
  51. package/dist/api/outcome.d.ts +5 -5
  52. package/dist/api/outcome.js +17 -16
  53. package/dist/api/pilot-arm.d.ts +1 -11
  54. package/dist/api/pilot-arm.js +5 -45
  55. package/dist/api/predictions.d.ts +3 -1
  56. package/dist/api/predictions.js +8 -1
  57. package/dist/api/projects.d.ts +21 -0
  58. package/dist/api/projects.js +24 -0
  59. package/dist/api/promote.d.ts +3 -3
  60. package/dist/api/promote.js +26 -28
  61. package/dist/api/prompt-hook.d.ts +17 -2
  62. package/dist/api/prompt-hook.js +45 -4
  63. package/dist/api/quarantine.js +2 -1
  64. package/dist/api/recall-cli.d.ts +3 -0
  65. package/dist/api/recall-cli.js +13 -4
  66. package/dist/api/recall-core.js +10 -5
  67. package/dist/api/recall-pipeline.js +23 -24
  68. package/dist/api/recall-request.js +1 -1
  69. package/dist/api/recall-types.d.ts +3 -1
  70. package/dist/api/recall.d.ts +0 -3
  71. package/dist/api/recall.js +51 -73
  72. package/dist/api/remember-local.d.ts +5 -2
  73. package/dist/api/remember-local.js +15 -7
  74. package/dist/api/remember.d.ts +7 -3
  75. package/dist/api/remember.js +19 -16
  76. package/dist/api/session-end.d.ts +46 -0
  77. package/dist/api/session-end.js +131 -0
  78. package/dist/api/sleep-run.d.ts +1 -1
  79. package/dist/api/sleep-run.js +16 -11
  80. package/dist/api/sleep.js +4 -3
  81. package/dist/api/status.d.ts +24 -0
  82. package/dist/api/status.js +40 -0
  83. package/dist/api/store-init.d.ts +4 -0
  84. package/dist/api/store-init.js +9 -0
  85. package/dist/api/tokens.js +2 -2
  86. package/dist/api/types.d.ts +3 -7
  87. package/dist/api/types.js +6 -1
  88. package/dist/capture/caller-session.d.ts +0 -3
  89. package/dist/capture/caller-session.js +1 -12
  90. package/dist/capture/capture-error.d.ts +2 -2
  91. package/dist/capture/capture-error.js +23 -18
  92. package/dist/capture/codex-patch.js +3 -1
  93. package/dist/capture/command.js +36 -95
  94. package/dist/capture/compact.js +10 -5
  95. package/dist/capture/compaction-items-caller.js +11 -20
  96. package/dist/capture/compaction-record.d.ts +7 -93
  97. package/dist/capture/compaction-record.js +29 -426
  98. package/dist/capture/compaction-spool.d.ts +7 -7
  99. package/dist/capture/compaction-spool.js +60 -51
  100. package/dist/capture/copilot-transcript.d.ts +2 -1
  101. package/dist/capture/copilot-transcript.js +2 -1
  102. package/dist/capture/extract.d.ts +5 -2
  103. package/dist/capture/extract.js +4 -3
  104. package/dist/capture/failure-caller.d.ts +1 -1
  105. package/dist/capture/failure-caller.js +21 -16
  106. package/dist/capture/failure-reading.d.ts +2 -1
  107. package/dist/capture/failure-reading.js +7 -9
  108. package/dist/capture/pre-compact-caller.js +5 -4
  109. package/dist/capture/session-digest.js +4 -3
  110. package/dist/capture/session-end-caller.js +3 -2
  111. package/dist/capture/session-worker.js +22 -10
  112. package/dist/capture/transcript.d.ts +2 -1
  113. package/dist/capture/transcript.js +6 -3
  114. package/dist/capture/working-state.d.ts +4 -2
  115. package/dist/capture/working-state.js +4 -2
  116. package/dist/cli/audit-prune.d.ts +0 -25
  117. package/dist/cli/audit-prune.js +1 -27
  118. package/dist/cli/audit.js +20 -20
  119. package/dist/cli/auth.d.ts +1 -1
  120. package/dist/cli/auth.js +16 -17
  121. package/dist/cli/briefs.d.ts +1 -4
  122. package/dist/cli/briefs.js +37 -292
  123. package/dist/cli/card.js +41 -33
  124. package/dist/cli/client.d.ts +6 -2
  125. package/dist/cli/client.js +37 -21
  126. package/dist/cli/compact-resume-payload.js +1 -1
  127. package/dist/cli/context.d.ts +1 -1
  128. package/dist/cli/context.js +13 -13
  129. package/dist/cli/continuity.d.ts +1 -1
  130. package/dist/cli/continuity.js +36 -39
  131. package/dist/cli/curate.d.ts +2 -2
  132. package/dist/cli/curate.js +32 -36
  133. package/dist/cli/dag.d.ts +1 -1
  134. package/dist/cli/dag.js +23 -8
  135. package/dist/cli/decisions.d.ts +0 -2
  136. package/dist/cli/decisions.js +7 -285
  137. package/dist/cli/eval.d.ts +1 -1
  138. package/dist/cli/eval.js +12 -13
  139. package/dist/cli/explain.d.ts +1 -1
  140. package/dist/cli/explain.js +21 -25
  141. package/dist/cli/flag-values.js +16 -6
  142. package/dist/cli/flags.d.ts +0 -289
  143. package/dist/cli/flags.js +3 -138
  144. package/dist/cli/graph.d.ts +3 -0
  145. package/dist/cli/graph.js +83 -0
  146. package/dist/cli/hook-runtime.d.ts +6 -6
  147. package/dist/cli/hook-runtime.js +9 -36
  148. package/dist/cli/incidents.d.ts +3 -0
  149. package/dist/cli/incidents.js +112 -0
  150. package/dist/cli/init.d.ts +1 -1
  151. package/dist/cli/init.js +28 -19
  152. package/dist/cli/install-steps.d.ts +2 -1
  153. package/dist/cli/install-steps.js +11 -3
  154. package/dist/cli/last-sleep.js +1 -1
  155. package/dist/cli/maintenance.d.ts +3 -3
  156. package/dist/cli/maintenance.js +29 -30
  157. package/dist/cli/notes.d.ts +2 -0
  158. package/dist/cli/notes.js +41 -0
  159. package/dist/cli/object-verbs.d.ts +2 -2
  160. package/dist/cli/object-verbs.js +4 -4
  161. package/dist/cli/open-browser.d.ts +14 -0
  162. package/dist/cli/open-browser.js +26 -0
  163. package/dist/cli/playbooks.d.ts +3 -4
  164. package/dist/cli/playbooks.js +126 -290
  165. package/dist/cli/predictions.d.ts +3 -0
  166. package/dist/cli/predictions.js +174 -0
  167. package/dist/cli/print.js +2 -1
  168. package/dist/cli/projects.js +30 -22
  169. package/dist/cli/quality-repair-once.d.ts +1 -1
  170. package/dist/cli/quality-repair-once.js +6 -5
  171. package/dist/cli/recall.js +13 -7
  172. package/dist/cli/refine-llm.js +18 -17
  173. package/dist/cli/remember.js +17 -17
  174. package/dist/cli/scheduler.js +2 -1
  175. package/dist/cli/serve.js +11 -6
  176. package/dist/cli/session-hooks.d.ts +1 -5
  177. package/dist/cli/session-hooks.js +89 -168
  178. package/dist/cli/setup.d.ts +1 -1
  179. package/dist/cli/setup.js +17 -10
  180. package/dist/cli/shared.d.ts +2 -6
  181. package/dist/cli/shared.js +9 -29
  182. package/dist/cli/sleep-render.js +6 -3
  183. package/dist/cli/sleep.js +6 -37
  184. package/dist/cli/status.d.ts +3 -3
  185. package/dist/cli/status.js +32 -35
  186. package/dist/cli/transfer.d.ts +3 -3
  187. package/dist/cli/transfer.js +64 -43
  188. package/dist/cli/usage.d.ts +1 -80
  189. package/dist/cli/usage.js +2 -696
  190. package/dist/cli/verb-row.d.ts +34 -0
  191. package/dist/cli/verb-row.js +6 -0
  192. package/dist/cli/verbs/health.d.ts +27 -0
  193. package/dist/cli/verbs/health.js +234 -0
  194. package/dist/cli/verbs/memory.d.ts +16 -0
  195. package/dist/cli/verbs/memory.js +236 -0
  196. package/dist/cli/verbs/objects.d.ts +20 -0
  197. package/dist/cli/verbs/objects.js +257 -0
  198. package/dist/cli/verbs/sessions.d.ts +31 -0
  199. package/dist/cli/verbs/sessions.js +314 -0
  200. package/dist/cli/verbs.d.ts +81 -0
  201. package/dist/cli/verbs.js +7 -0
  202. package/dist/cli/versioned-verbs.d.ts +51 -0
  203. package/dist/cli/versioned-verbs.js +79 -0
  204. package/dist/cli.d.ts +1 -272
  205. package/dist/cli.js +24 -315
  206. package/dist/connectors/dlq.d.ts +39 -3
  207. package/dist/connectors/dlq.js +39 -4
  208. package/dist/connectors/github/backfill.js +12 -3
  209. package/dist/connectors/github/deletion.js +2 -1
  210. package/dist/connectors/github/dlq.js +31 -73
  211. package/dist/connectors/github/ingest.d.ts +2 -6
  212. package/dist/connectors/github/ingest.js +5 -29
  213. package/dist/connectors/github/tenant-routing.js +2 -1
  214. package/dist/connectors/github/webhook.d.ts +2 -1
  215. package/dist/connectors/github/webhook.js +35 -2
  216. package/dist/connectors/ingest.d.ts +10 -0
  217. package/dist/connectors/ingest.js +29 -0
  218. package/dist/connectors/slack/deletion.js +2 -1
  219. package/dist/connectors/slack/dlq.js +49 -71
  220. package/dist/connectors/slack/ingest.d.ts +2 -6
  221. package/dist/connectors/slack/ingest.js +4 -28
  222. package/dist/connectors/slack/tenant-routing.js +2 -1
  223. package/dist/connectors/slack/webhook.d.ts +2 -1
  224. package/dist/connectors/slack/webhook.js +2 -1
  225. package/dist/consolidate/conflicts.js +2 -2
  226. package/dist/consolidate/dag.js +29 -18
  227. package/dist/consolidate/dedupe.d.ts +7 -0
  228. package/dist/consolidate/dedupe.js +6 -4
  229. package/dist/consolidate/llm-passes.js +45 -19
  230. package/dist/consolidate/memory-value.js +3 -2
  231. package/dist/consolidate/merge.js +8 -8
  232. package/dist/consolidate/physics-pass.d.ts +6 -1
  233. package/dist/consolidate/physics-pass.js +26 -6
  234. package/dist/consolidate/replay.js +11 -4
  235. package/dist/consolidate/sleep.js +9 -6
  236. package/dist/consolidate/traces.js +5 -5
  237. package/dist/core/ablation.d.ts +10 -0
  238. package/dist/core/ablation.js +12 -0
  239. package/dist/{sharing/scope.d.ts → core/active-scope.d.ts} +1 -1
  240. package/dist/{sharing/scope.js → core/active-scope.js} +1 -1
  241. package/dist/core/ambient.d.ts +4 -0
  242. package/dist/core/ambient.js +39 -18
  243. package/dist/core/api-errors.d.ts +16 -0
  244. package/dist/core/api-errors.js +20 -0
  245. package/dist/core/card.js +3 -1
  246. package/dist/core/compaction-timing.d.ts +7 -0
  247. package/dist/core/compaction-timing.js +9 -0
  248. package/dist/core/config.d.ts +1 -1
  249. package/dist/core/config.js +22 -18
  250. package/dist/core/cosine.d.ts +3 -0
  251. package/dist/core/cosine.js +19 -0
  252. package/dist/{store → core}/markdown.d.ts +1 -1
  253. package/dist/{store → core}/markdown.js +4 -4
  254. package/dist/core/memory-audit.d.ts +17 -0
  255. package/dist/core/memory-audit.js +54 -0
  256. package/dist/core/memory.d.ts +14 -8
  257. package/dist/core/memory.js +35 -23
  258. package/dist/{consolidate → core}/merged-row.d.ts +1 -1
  259. package/dist/{consolidate → core}/merged-row.js +1 -1
  260. package/dist/core/physics.d.ts +3 -1
  261. package/dist/core/physics.js +23 -18
  262. package/dist/core/prediction.d.ts +3 -0
  263. package/dist/core/prediction.js +6 -0
  264. package/dist/core/project-identity.d.ts +3 -0
  265. package/dist/core/project-identity.js +12 -2
  266. package/dist/{store → core}/recall-scope.d.ts +1 -10
  267. package/dist/{store → core}/recall-scope.js +2 -19
  268. package/dist/core/search-types.d.ts +1 -1
  269. package/dist/dashboard/dashboard-actions.d.ts +2 -2
  270. package/dist/dashboard/dashboard-actions.js +5 -5
  271. package/dist/dashboard/dashboard-queries.js +2 -7
  272. package/dist/dashboard/dashboard-snapshot.js +1 -3
  273. package/dist/dashboard/dashboard.js +16 -5
  274. package/dist/db/busy.d.ts +2 -7
  275. package/dist/db/busy.js +21 -26
  276. package/dist/db/connect.d.ts +2 -2
  277. package/dist/db/connect.js +7 -7
  278. package/dist/db/index.d.ts +3 -3
  279. package/dist/db/index.js +3 -3
  280. package/dist/db/migrate.js +14 -7
  281. package/dist/db/migrations/index.d.ts +1 -1
  282. package/dist/db/migrations/index.js +3 -1
  283. package/dist/db/migrations/v11.js +1 -0
  284. package/dist/db/migrations/v14.js +1 -0
  285. package/dist/db/migrations/v55.d.ts +3 -0
  286. package/dist/db/migrations/v55.js +17 -0
  287. package/dist/db/open.d.ts +0 -2
  288. package/dist/db/open.js +6 -10
  289. package/dist/db/physics-state.d.ts +0 -4
  290. package/dist/db/physics-state.js +7 -33
  291. package/dist/db/request-stores.d.ts +1 -0
  292. package/dist/db/request-stores.js +2 -1
  293. package/dist/db/tables.d.ts +2 -0
  294. package/dist/db/tables.js +6 -2
  295. package/dist/db/vector-store.js +19 -5
  296. package/dist/db/wal-checkpointer.js +2 -1
  297. package/dist/doctor.d.ts +3 -0
  298. package/dist/doctor.js +18 -15
  299. package/dist/embeddings/local.d.ts +62 -0
  300. package/dist/embeddings/local.js +146 -0
  301. package/dist/{store/embeddings → embeddings}/provider.d.ts +5 -2
  302. package/dist/{store/embeddings → embeddings}/provider.js +19 -25
  303. package/dist/embeddings/transformers.d.ts +26 -0
  304. package/dist/embeddings/transformers.js +89 -0
  305. package/dist/entry/session-text.d.ts +1 -1
  306. package/dist/entry/session-text.js +1 -1
  307. package/dist/entry/store-kit.d.ts +37 -0
  308. package/dist/entry/store-kit.js +25 -0
  309. package/dist/entry/testing.d.ts +6 -0
  310. package/dist/entry/testing.js +7 -0
  311. package/dist/eval/eval-suite.js +17 -11
  312. package/dist/eval/eval.js +1 -1
  313. package/dist/eval/judgment.js +12 -5
  314. package/dist/graph/extract.js +3 -3
  315. package/dist/graph/recall.js +1 -1
  316. package/dist/graph/view.js +13 -5
  317. package/dist/graph/write.d.ts +1 -1
  318. package/dist/graph/write.js +9 -9
  319. package/dist/hooks/codex-session.js +3 -2
  320. package/dist/hooks/codex-wrapper.js +2 -1
  321. package/dist/hooks/json-hooks.js +10 -4
  322. package/dist/hooks/opencode.js +8 -5
  323. package/dist/hooks/shared.d.ts +0 -6
  324. package/dist/hooks/shared.js +4 -15
  325. package/dist/importers/core.js +4 -4
  326. package/dist/importers/sources.js +5 -4
  327. package/dist/importers/vault.d.ts +1 -1
  328. package/dist/importers/vault.js +25 -23
  329. package/dist/index.d.ts +5 -4
  330. package/dist/index.js +4 -3
  331. package/dist/learn/autolearn.d.ts +15 -3
  332. package/dist/learn/autolearn.js +38 -16
  333. package/dist/learn/churn-git.js +13 -4
  334. package/dist/learn/extract.d.ts +3 -2
  335. package/dist/learn/extract.js +7 -5
  336. package/dist/learn/forward-claim-detector.js +4 -2
  337. package/dist/learn/invalidation.d.ts +2 -1
  338. package/dist/learn/invalidation.js +18 -9
  339. package/dist/mcp/admin-tools.d.ts +2 -2
  340. package/dist/mcp/admin-tools.js +27 -48
  341. package/dist/mcp/format.js +3 -2
  342. package/dist/mcp/framing.d.ts +1 -0
  343. package/dist/mcp/framing.js +1 -1
  344. package/dist/mcp/memory-tools.d.ts +1 -1
  345. package/dist/mcp/memory-tools.js +8 -9
  346. package/dist/mcp/protocol.d.ts +11 -7
  347. package/dist/mcp/protocol.js +25 -12
  348. package/dist/mcp/recall-tools.js +12 -9
  349. package/dist/mcp/request.js +22 -20
  350. package/dist/mcp/stdio.js +37 -16
  351. package/dist/mcp/tool-args.js +2 -1
  352. package/dist/mcp/tools.js +44 -21
  353. package/dist/objects/customer-notes.d.ts +2 -2
  354. package/dist/objects/decisions.d.ts +2 -2
  355. package/dist/objects/descriptor.d.ts +1 -1
  356. package/dist/objects/incidents.d.ts +2 -2
  357. package/dist/objects/lifecycle.d.ts +1 -1
  358. package/dist/objects/lifecycle.js +2 -1
  359. package/dist/objects/policies.d.ts +2 -2
  360. package/dist/objects/policies.js +2 -1
  361. package/dist/objects/processes.d.ts +2 -2
  362. package/dist/objects/project-briefs.d.ts +2 -2
  363. package/dist/objects/project-briefs.js +2 -3
  364. package/dist/objects/skills.d.ts +2 -2
  365. package/dist/postinstall.js +1 -1
  366. package/dist/rerankers/clef.js +3 -6
  367. package/dist/rerankers/cross-encoder.d.ts +31 -2
  368. package/dist/rerankers/cross-encoder.js +71 -98
  369. package/dist/rerankers/index.js +5 -4
  370. package/dist/rerankers/jev.js +2 -4
  371. package/dist/rerankers/outage-warning.d.ts +3 -1
  372. package/dist/rerankers/outage-warning.js +5 -3
  373. package/dist/search/bm25-search.js +1 -1
  374. package/dist/search/boosts.js +1 -1
  375. package/dist/search/embed-memories.d.ts +12 -0
  376. package/dist/search/embed-memories.js +389 -0
  377. package/dist/search/engine.d.ts +8 -0
  378. package/dist/search/engine.js +10 -0
  379. package/dist/search/goal-boost.d.ts +1 -1
  380. package/dist/search/goal-boost.js +6 -3
  381. package/dist/search/hybrid.d.ts +9 -2
  382. package/dist/search/hybrid.js +9 -4
  383. package/dist/search/physics-search.d.ts +6 -3
  384. package/dist/search/physics-search.js +22 -15
  385. package/dist/search/rerank.js +1 -1
  386. package/dist/search/vector.d.ts +8 -6
  387. package/dist/search/vector.js +14 -17
  388. package/dist/server/auth.d.ts +3 -1
  389. package/dist/server/auth.js +24 -15
  390. package/dist/server/boot.d.ts +2 -1
  391. package/dist/server/boot.js +34 -84
  392. package/dist/server/client-ip.d.ts +9 -6
  393. package/dist/server/client-ip.js +36 -17
  394. package/dist/server/deadline.d.ts +0 -2
  395. package/dist/server/deadline.js +1 -7
  396. package/dist/server/defaults.d.ts +1 -0
  397. package/dist/server/defaults.js +1 -0
  398. package/dist/server/mcp-http.js +16 -11
  399. package/dist/server/request.js +7 -2
  400. package/dist/server/route-checks.d.ts +7 -0
  401. package/dist/server/route-checks.js +42 -0
  402. package/dist/server/route-table.d.ts +9 -8
  403. package/dist/server/route-table.js +127 -133
  404. package/dist/server/routes/admin.d.ts +8 -8
  405. package/dist/server/routes/admin.js +13 -21
  406. package/dist/server/routes/customer-notes.d.ts +6 -6
  407. package/dist/server/routes/customer-notes.js +1 -2
  408. package/dist/server/routes/decisions.d.ts +6 -6
  409. package/dist/server/routes/decisions.js +2 -4
  410. package/dist/server/routes/incidents.d.ts +6 -6
  411. package/dist/server/routes/incidents.js +2 -5
  412. package/dist/server/routes/memories.d.ts +9 -9
  413. package/dist/server/routes/memories.js +19 -40
  414. package/dist/server/routes/object-routes.d.ts +6 -6
  415. package/dist/server/routes/object-routes.js +4 -7
  416. package/dist/server/routes/policies.d.ts +7 -7
  417. package/dist/server/routes/policies.js +2 -4
  418. package/dist/server/routes/predictions.d.ts +6 -6
  419. package/dist/server/routes/predictions.js +9 -13
  420. package/dist/server/routes/processes.d.ts +6 -6
  421. package/dist/server/routes/processes.js +1 -2
  422. package/dist/server/routes/project-briefs.d.ts +7 -7
  423. package/dist/server/routes/project-briefs.js +2 -4
  424. package/dist/server/routes/recall.d.ts +5 -5
  425. package/dist/server/routes/recall.js +6 -10
  426. package/dist/server/routes/skills.d.ts +7 -7
  427. package/dist/server/routes/skills.js +2 -4
  428. package/dist/server/server-detect.d.ts +7 -1
  429. package/dist/server/server-detect.js +36 -11
  430. package/dist/server/sleep-child.js +26 -7
  431. package/dist/server/sleep-offload.d.ts +11 -5
  432. package/dist/server/sleep-offload.js +17 -11
  433. package/dist/server/tls.d.ts +7 -2
  434. package/dist/server/tls.js +17 -5
  435. package/dist/server/types.d.ts +29 -12
  436. package/dist/server/validation.d.ts +1 -2
  437. package/dist/server/validation.js +6 -11
  438. package/dist/server.d.ts +4 -40
  439. package/dist/server.js +3 -29
  440. package/dist/sharing/global-store.d.ts +4 -5
  441. package/dist/sharing/global-store.js +8 -13
  442. package/dist/sharing/global-sync.js +38 -45
  443. package/dist/sharing/project-merge.d.ts +7 -14
  444. package/dist/sharing/project-merge.js +57 -121
  445. package/dist/sharing/search-both.js +10 -3
  446. package/dist/sharing/share.d.ts +2 -0
  447. package/dist/sharing/share.js +39 -38
  448. package/dist/store/ambient.js +7 -7
  449. package/dist/store/audit-event.d.ts +1 -1
  450. package/dist/store/audit.d.ts +0 -16
  451. package/dist/store/audit.js +4 -55
  452. package/dist/store/auth.d.ts +4 -2
  453. package/dist/store/auth.js +7 -3
  454. package/dist/store/candidates.d.ts +3 -2
  455. package/dist/store/candidates.js +59 -30
  456. package/dist/store/capture-write.d.ts +17 -0
  457. package/dist/store/capture-write.js +35 -0
  458. package/dist/store/cards.js +6 -5
  459. package/dist/store/compaction-caller.d.ts +18 -0
  460. package/dist/store/compaction-caller.js +29 -0
  461. package/dist/store/compactions-record.d.ts +115 -0
  462. package/dist/store/compactions-record.js +418 -0
  463. package/dist/store/compactions.js +6 -4
  464. package/dist/store/conflicts.d.ts +7 -1
  465. package/dist/store/conflicts.js +13 -9
  466. package/dist/store/connectors/dlq.d.ts +10 -0
  467. package/dist/store/connectors/dlq.js +33 -0
  468. package/dist/store/connectors/event-log.d.ts +13 -0
  469. package/dist/store/connectors/event-log.js +24 -0
  470. package/dist/store/connectors/github.d.ts +7 -9
  471. package/dist/store/connectors/github.js +35 -60
  472. package/dist/store/connectors/slack.d.ts +2 -11
  473. package/dist/store/connectors/slack.js +24 -57
  474. package/dist/store/dashboard-reads.js +8 -6
  475. package/dist/store/delete-and-batch.d.ts +6 -0
  476. package/dist/store/delete-and-batch.js +40 -6
  477. package/dist/store/delivery-recorder.d.ts +12 -5
  478. package/dist/store/delivery-recorder.js +13 -11
  479. package/dist/store/descendant-page.js +8 -4
  480. package/dist/store/dormant.d.ts +1 -1
  481. package/dist/store/dormant.js +10 -4
  482. package/dist/store/embeddings/index.d.ts +2 -12
  483. package/dist/store/embeddings/index.js +5 -374
  484. package/dist/store/entry-reads.d.ts +35 -4
  485. package/dist/store/entry-reads.js +86 -19
  486. package/dist/store/entry-row.js +9 -2
  487. package/dist/store/entry-writes.d.ts +15 -12
  488. package/dist/store/entry-writes.js +54 -22
  489. package/dist/store/failure-log-at.d.ts +8 -0
  490. package/dist/store/failure-log-at.js +16 -0
  491. package/dist/store/failure-log.d.ts +2 -2
  492. package/dist/store/failure-log.js +5 -3
  493. package/dist/{trust → store}/gated-write.d.ts +3 -1
  494. package/dist/{trust → store}/gated-write.js +7 -5
  495. package/dist/store/goals.js +22 -19
  496. package/dist/store/graph-reads.js +9 -6
  497. package/dist/store/graph-view-rows.js +2 -1
  498. package/dist/store/graph-writes.d.ts +6 -6
  499. package/dist/store/graph-writes.js +48 -71
  500. package/dist/store/half-life.js +2 -2
  501. package/dist/store/handoffs.d.ts +10 -4
  502. package/dist/store/handoffs.js +10 -9
  503. package/dist/store/index-and-stats.js +2 -2
  504. package/dist/store/index.d.ts +2 -2
  505. package/dist/store/index.js +1 -1
  506. package/dist/store/key-writes.js +1 -1
  507. package/dist/store/ledger-turn.d.ts +9 -0
  508. package/dist/store/ledger-turn.js +38 -0
  509. package/dist/store/mirrors.js +19 -10
  510. package/dist/store/open.d.ts +2 -5
  511. package/dist/store/open.js +42 -54
  512. package/dist/store/pilot-arm.d.ts +12 -0
  513. package/dist/store/pilot-arm.js +42 -0
  514. package/dist/store/port.d.ts +68 -6
  515. package/dist/store/port.js +8 -0
  516. package/dist/store/predictions.d.ts +1 -2
  517. package/dist/store/predictions.js +3 -6
  518. package/dist/store/project-tags.d.ts +62 -0
  519. package/dist/store/project-tags.js +67 -0
  520. package/dist/store/quality-repair.js +1 -1
  521. package/dist/store/quarantine.js +2 -1
  522. package/dist/store/recall-trace.d.ts +2 -0
  523. package/dist/store/recall-trace.js +20 -3
  524. package/dist/store/rejected-values.js +16 -10
  525. package/dist/store/rejection-probe.d.ts +4 -0
  526. package/dist/store/rejection-probe.js +19 -0
  527. package/dist/store/rejection.d.ts +0 -17
  528. package/dist/store/rejection.js +1 -21
  529. package/dist/store/rows.js +2 -2
  530. package/dist/store/rule-sql.d.ts +11 -0
  531. package/dist/store/rule-sql.js +31 -6
  532. package/dist/store/search-rows.d.ts +5 -5
  533. package/dist/store/search-rows.js +18 -16
  534. package/dist/store/sessions.js +4 -2
  535. package/dist/store/set-aside.d.ts +14 -0
  536. package/dist/store/set-aside.js +23 -0
  537. package/dist/store/sqlite/busy.d.ts +3 -0
  538. package/dist/store/sqlite/busy.js +8 -0
  539. package/dist/store/sqlite/connector-events-group.js +4 -7
  540. package/dist/store/sqlite/connector-writes-group.js +6 -4
  541. package/dist/store/sqlite/entry-writes-group.d.ts +3 -3
  542. package/dist/store/sqlite/entry-writes-group.js +3 -3
  543. package/dist/store/sqlite/error-codec.js +4 -3
  544. package/dist/store/sqlite/executor.js +10 -4
  545. package/dist/store/sqlite/graph-reads-group.js +1 -1
  546. package/dist/store/sqlite/held-connection.d.ts +11 -0
  547. package/dist/store/sqlite/held-connection.js +41 -0
  548. package/dist/store/sqlite/last-recall-group.d.ts +13 -0
  549. package/dist/store/sqlite/last-recall-group.js +57 -0
  550. package/dist/store/sqlite/local.d.ts +0 -17
  551. package/dist/store/sqlite/local.js +0 -31
  552. package/dist/store/sqlite/maintenance-groups.d.ts +6 -0
  553. package/dist/store/sqlite/maintenance-groups.js +60 -0
  554. package/dist/store/sqlite/object-rows.d.ts +1 -1
  555. package/dist/store/sqlite/objects-group.d.ts +1 -1
  556. package/dist/store/sqlite/objects-group.js +15 -7
  557. package/dist/store/sqlite/store.d.ts +1 -8
  558. package/dist/store/sqlite/store.js +13 -22
  559. package/dist/store/summaries.d.ts +10 -3
  560. package/dist/store/summaries.js +54 -11
  561. package/dist/store/token-ledger-rows.js +3 -2
  562. package/dist/store/token-ledger.d.ts +0 -7
  563. package/dist/store/token-ledger.js +0 -35
  564. package/dist/store/vector-index.js +8 -4
  565. package/dist/support-bundle.js +2 -3
  566. package/dist/trust/reject-flow.js +2 -2
  567. package/dist/util/agent-homes.d.ts +15 -0
  568. package/dist/util/agent-homes.js +34 -0
  569. package/dist/util/anthropic-messages.d.ts +6 -1
  570. package/dist/util/anthropic-messages.js +26 -7
  571. package/dist/util/atomic-write.d.ts +5 -2
  572. package/dist/util/atomic-write.js +12 -8
  573. package/dist/util/chunked.d.ts +3 -0
  574. package/dist/util/chunked.js +10 -0
  575. package/dist/util/ellipsize.d.ts +3 -0
  576. package/dist/util/ellipsize.js +5 -0
  577. package/dist/util/env.d.ts +12 -2
  578. package/dist/util/env.js +12 -2
  579. package/dist/util/format.d.ts +5 -0
  580. package/dist/util/format.js +10 -0
  581. package/dist/util/hook-payload.d.ts +8 -0
  582. package/dist/util/hook-payload.js +37 -0
  583. package/dist/util/http-retry.d.ts +6 -0
  584. package/dist/util/http-retry.js +43 -5
  585. package/dist/util/http-util.d.ts +0 -8
  586. package/dist/util/http-util.js +10 -21
  587. package/dist/util/limits.d.ts +7 -0
  588. package/dist/util/limits.js +14 -0
  589. package/dist/util/log.d.ts +4 -0
  590. package/dist/util/log.js +9 -0
  591. package/dist/util/request-scope.d.ts +9 -3
  592. package/dist/util/request-scope.js +18 -9
  593. package/dist/util/secret-detect.js +6 -1
  594. package/dist/util/spawn-argv.d.ts +9 -0
  595. package/dist/util/spawn-argv.js +57 -0
  596. package/dist/util/sql.d.ts +3 -0
  597. package/dist/util/sql.js +6 -0
  598. package/dist/util/sqlite-blocked.d.ts +2 -0
  599. package/dist/util/sqlite-blocked.js +5 -0
  600. package/dist/util/stream-tee.d.ts +3 -0
  601. package/dist/util/stream-tee.js +35 -0
  602. package/dist/util/time.d.ts +4 -0
  603. package/dist/util/time.js +4 -0
  604. package/dist/util/vector-codec.d.ts +4 -0
  605. package/dist/util/vector-codec.js +15 -0
  606. package/dist/util/version.d.ts +1 -1
  607. package/dist/util/version.js +1 -1
  608. package/extensions/openclaw-plugin/openclaw.plugin.json +1 -1
  609. package/extensions/openclaw-plugin/package.json +1 -1
  610. package/openclaw.plugin.json +1 -1
  611. package/package.json +4 -2
  612. package/dist/api/on-store.d.ts +0 -16
  613. package/dist/api/on-store.js +0 -38
  614. package/dist/store/embeddings/local.d.ts +0 -15
  615. package/dist/store/embeddings/local.js +0 -175
  616. /package/dist/{cli → api}/correction-latency.d.ts +0 -0
  617. /package/dist/{cli → api}/correction-latency.js +0 -0
  618. /package/dist/{cli → api}/provenance-coverage.d.ts +0 -0
  619. /package/dist/{cli → api}/provenance-coverage.js +0 -0
  620. /package/dist/{store → core}/object-types.d.ts +0 -0
  621. /package/dist/{store → core}/object-types.js +0 -0
  622. /package/dist/{capture → util}/compaction-items.d.ts +0 -0
  623. /package/dist/{capture → util}/compaction-items.js +0 -0
  624. /package/dist/{store → util}/yaml.d.ts +0 -0
  625. /package/dist/{store → util}/yaml.js +0 -0
@@ -1,8 +1,10 @@
1
+ import { handleGitHubEventsWebhook } from '../connectors/github/webhook.js';
2
+ import { handleSlackEventsWebhook } from '../connectors/slack/webhook.js';
1
3
  import { withSqliteOffLoop } from '../db/index.js';
2
- import { hasGroup } from '../store/index.js';
4
+ import { hasGroup, requireGroup } from '../store/index.js';
3
5
  import { runsOffLoop } from '../store/sqlite/worker-store.js';
4
6
  import { HttpError, JSON_HEADERS, sendJson, STORE_NOT_PORTED_MESSAGE } from '../util/http-util.js';
5
- import { buildContextWithAuth, requireAuth } from './auth.js';
7
+ import { assertCrossTenantAdmin, buildContextWithAuth, isLoopback } from './auth.js';
6
8
  import { matchPath, noteAccess } from './request.js';
7
9
  import { handleApproveQuarantine, handleCreateAuthKey, handleListAudit, handleListAuthKeys, handleListQuarantine, handleRejectQuarantine, handleRevokeAuthKey } from './routes/admin.js';
8
10
  import { handleCloseCustomerNote, handleCreateCustomerNote, handleGetCustomerNote, handleListCustomerNotes, handleSupersedeCustomerNote } from './routes/customer-notes.js';
@@ -16,94 +18,134 @@ import { handleCloseProjectBrief, handleCreateProjectBrief, handleGetProjectBrie
16
18
  import { handleAssembleSession, handleDrillRecall, handleGetContext, handleRecallMemories } from './routes/recall.js';
17
19
  import { handleCloseSkill, handleCreateSkill, handleExportSkills, handleGetSkill, handleListSkills, handleSupersedeSkill } from './routes/skills.js';
18
20
  import { parseJsonBody } from './validation.js';
19
- // Explicit allow-list for unauthenticated /v1/* routes: add a new one here AND in tests/server-bearer-lockdown.test.ts; never gate auth by `path.startsWith`.
20
- // Handlers check `isPublicRoute` before auth, so an entry without that check is a no-op (fail-closed); other open paths are ServeOpts.publicJson GETs.
21
- const PUBLIC_ROUTES = new Set([
22
- 'POST /v1/connectors/slack/events',
23
- 'POST /v1/connectors/github/events',
24
- ]);
25
- export function isPublicRoute(method, path) {
26
- return PUBLIC_ROUTES.has(`${method} ${path}`);
21
+ const WEBHOOK_GROUPS = ['entryWrites', 'connectorWrites', 'connectorEvents'];
22
+ /** A store missing a group a delivery writes through refuses the webhook up front, so no delivery is acknowledged and then half stored. */
23
+ function webhook(handle) {
24
+ return async ({ req, res, opts }) => {
25
+ for (const group of WEBHOOK_GROUPS)
26
+ requireGroup(opts.store, group);
27
+ await handle({ req, res, opts }, opts.store);
28
+ };
27
29
  }
28
- /** The /v1 routes in dispatch order; the first entry whose method and path match handles the request. */
29
- const V1_ROUTES = [
30
- { method: 'POST', path: '/v1/memories', storeReady: 'entryWrites', loop: 'off', handler: handleCreateMemory },
31
- { method: 'GET', path: '/v1/graph', storeReady: 'graphReads', loop: 'off', handler: handleGetGraph },
32
- { method: 'GET', path: '/v1/memories', storeReady: 'base', loop: 'off', handler: handleRecallMemories },
33
- { method: 'GET', pattern: '/v1/sessions/:id/assemble', storeReady: 'dagReads', loop: 'off', handler: handleAssembleSession },
34
- { method: 'GET', pattern: '/v1/recall/drill/:id', storeReady: 'dagReads', handler: handleDrillRecall },
35
- { method: 'POST', pattern: '/v1/memories/:id/archive', storeReady: 'entryWrites', loop: 'off', handler: handleArchiveMemory },
36
- { method: 'POST', pattern: '/v1/memories/:id/supersede', storeReady: 'entryWrites', loop: 'off', handler: handleSupersedeMemory },
37
- { method: 'POST', pattern: '/v1/memories/:id/promote', sqliteOnly: 'copies a memory between the two local hippo.db files', handler: handlePromoteMemory },
38
- { method: 'DELETE', pattern: '/v1/memories/:id', storeReady: 'entryWrites', loop: 'off', handler: handleForgetMemory },
39
- { method: 'POST', path: '/v1/outcome', storeReady: 'entryWrites', handler: handleApplyOutcome },
40
- { method: 'GET', path: '/v1/context', storeReady: 'contextReads', handler: handleGetContext },
41
- { method: 'POST', path: '/v1/sleep', sqliteOnly: 'consolidates every tenant under the local hippo root in this process', handler: handleSleep },
42
- { method: 'POST', path: '/v1/auth/keys', storeReady: 'keyWrites', loop: 'off', handler: handleCreateAuthKey },
43
- { method: 'GET', path: '/v1/auth/keys', storeReady: 'keyWrites', loop: 'off', handler: handleListAuthKeys },
44
- { method: 'DELETE', pattern: '/v1/auth/keys/:keyId', storeReady: 'keyAudit', loop: 'off', handler: handleRevokeAuthKey },
45
- { method: 'GET', path: '/v1/quarantine', storeReady: 'quarantine', loop: 'off', handler: handleListQuarantine },
46
- { method: 'POST', pattern: '/v1/quarantine/:id/approve', storeReady: 'quarantine', loop: 'off', handler: handleApproveQuarantine },
47
- { method: 'POST', pattern: '/v1/quarantine/:id/reject', storeReady: 'quarantine', loop: 'off', handler: handleRejectQuarantine },
48
- { method: 'GET', path: '/v1/audit', storeReady: 'auditLog', loop: 'off', handler: handleListAudit },
49
- { method: 'POST', path: '/v1/predictions', storeReady: 'predictions', loop: 'off', handler: handleCreatePrediction },
50
- { method: 'GET', path: '/v1/predictions', storeReady: 'predictions', loop: 'off', handler: handleListPredictions },
51
- { method: 'GET', path: '/v1/predictions/stats', storeReady: 'predictions', loop: 'off', handler: handlePredictionStats },
52
- { method: 'GET', regex: /^\/v1\/predictions\/(\d+)$/, storeReady: 'predictions', loop: 'off', handler: handleGetPrediction },
53
- { method: 'POST', regex: /^\/v1\/predictions\/(\d+)\/close$/, storeReady: 'predictions', loop: 'off', handler: handleClosePrediction },
54
- { method: 'POST', path: '/v1/decisions', storeReady: 'objects', loop: 'off', handler: handleCreateDecision },
55
- { method: 'GET', path: '/v1/decisions', storeReady: 'objects', loop: 'off', handler: handleListDecisions },
56
- { method: 'POST', regex: /^\/v1\/decisions\/(\d+)\/supersede$/, storeReady: 'objects', loop: 'off', handler: handleSupersedeDecision },
57
- { method: 'POST', regex: /^\/v1\/decisions\/(\d+)\/close$/, storeReady: 'objects', loop: 'off', handler: handleCloseDecision },
58
- { method: 'GET', regex: /^\/v1\/decisions\/(\d+)$/, storeReady: 'objects', loop: 'off', handler: handleGetDecision },
59
- { method: 'POST', path: '/v1/incidents', storeReady: 'objects', loop: 'off', handler: handleCreateIncident },
60
- { method: 'GET', path: '/v1/incidents', storeReady: 'objects', loop: 'off', handler: handleListIncidents },
61
- { method: 'POST', regex: /^\/v1\/incidents\/(\d+)\/resolve$/, storeReady: 'objects', loop: 'off', handler: handleResolveIncident },
62
- { method: 'POST', regex: /^\/v1\/incidents\/(\d+)\/close$/, storeReady: 'objects', loop: 'off', handler: handleCloseIncident },
63
- { method: 'GET', regex: /^\/v1\/incidents\/(\d+)$/, storeReady: 'objects', loop: 'off', handler: handleGetIncident },
64
- { method: 'POST', path: '/v1/processes', storeReady: 'objects', loop: 'off', handler: handleCreateProcess },
65
- { method: 'GET', path: '/v1/processes', storeReady: 'objects', loop: 'off', handler: handleListProcesses },
66
- { method: 'POST', regex: /^\/v1\/processes\/(\d+)\/supersede$/, storeReady: 'objects', loop: 'off', handler: handleSupersedeProcess },
67
- { method: 'POST', regex: /^\/v1\/processes\/(\d+)\/close$/, storeReady: 'objects', loop: 'off', handler: handleCloseProcess },
68
- { method: 'GET', regex: /^\/v1\/processes\/(\d+)$/, storeReady: 'objects', loop: 'off', handler: handleGetProcess },
69
- { method: 'POST', path: '/v1/policies', storeReady: 'objects', loop: 'off', handler: handleCreatePolicy },
70
- { method: 'GET', path: '/v1/policies', storeReady: 'objects', loop: 'off', handler: handleListPolicies },
71
- { method: 'GET', path: '/v1/policies/asof', storeReady: 'objects', loop: 'off', handler: handlePoliciesAsOf },
72
- { method: 'POST', regex: /^\/v1\/policies\/(\d+)\/supersede$/, storeReady: 'objects', loop: 'off', handler: handleSupersedePolicy },
73
- { method: 'POST', regex: /^\/v1\/policies\/(\d+)\/close$/, storeReady: 'objects', loop: 'off', handler: handleClosePolicy },
74
- { method: 'GET', regex: /^\/v1\/policies\/(\d+)$/, storeReady: 'objects', loop: 'off', handler: handleGetPolicy },
75
- { method: 'POST', path: '/v1/skills', storeReady: 'objects', loop: 'off', handler: handleCreateSkill },
76
- { method: 'GET', path: '/v1/skills', storeReady: 'objects', loop: 'off', handler: handleListSkills },
77
- { method: 'GET', path: '/v1/skills/export', storeReady: 'objects', loop: 'off', handler: handleExportSkills },
78
- { method: 'POST', regex: /^\/v1\/skills\/(\d+)\/supersede$/, storeReady: 'objects', loop: 'off', handler: handleSupersedeSkill },
79
- { method: 'POST', regex: /^\/v1\/skills\/(\d+)\/close$/, storeReady: 'objects', loop: 'off', handler: handleCloseSkill },
80
- { method: 'GET', regex: /^\/v1\/skills\/(\d+)$/, storeReady: 'objects', loop: 'off', handler: handleGetSkill },
81
- { method: 'POST', path: '/v1/project-briefs', storeReady: 'objects', loop: 'off', handler: handleCreateProjectBrief },
82
- { method: 'GET', path: '/v1/project-briefs', storeReady: 'objects', loop: 'off', handler: handleListProjectBriefs },
83
- { method: 'POST', path: '/v1/project-briefs/refresh', storeReady: 'objects', loop: 'off', handler: handleRefreshProjectBrief },
84
- { method: 'POST', regex: /^\/v1\/project-briefs\/(\d+)\/supersede$/, storeReady: 'objects', loop: 'off', handler: handleSupersedeProjectBrief },
85
- { method: 'POST', regex: /^\/v1\/project-briefs\/(\d+)\/close$/, storeReady: 'objects', loop: 'off', handler: handleCloseProjectBrief },
86
- { method: 'GET', regex: /^\/v1\/project-briefs\/(\d+)$/, storeReady: 'objects', loop: 'off', handler: handleGetProjectBrief },
87
- { method: 'POST', path: '/v1/customer-notes', storeReady: 'objects', loop: 'off', handler: handleCreateCustomerNote },
88
- { method: 'GET', path: '/v1/customer-notes', storeReady: 'objects', loop: 'off', handler: handleListCustomerNotes },
89
- { method: 'POST', regex: /^\/v1\/customer-notes\/(\d+)\/supersede$/, storeReady: 'objects', loop: 'off', handler: handleSupersedeCustomerNote },
90
- { method: 'POST', regex: /^\/v1\/customer-notes\/(\d+)\/close$/, storeReady: 'objects', loop: 'off', handler: handleCloseCustomerNote },
91
- { method: 'GET', regex: /^\/v1\/customer-notes\/(\d+)$/, storeReady: 'objects', loop: 'off', handler: handleGetCustomerNote },
30
+ /** The /v1 routes in dispatch order; the first entry whose method and path match handles the request. Each row's `auth` is what the
31
+ * dispatch gate proves before its handler runs, so a handler never checks the caller itself. */
32
+ export const V1_ROUTES = [
33
+ { method: 'POST', path: '/v1/memories', auth: 'caller', storeReady: 'entryWrites', loop: 'off', handler: handleCreateMemory },
34
+ { method: 'GET', path: '/v1/graph', auth: 'caller', storeReady: 'graphReads', loop: 'off', handler: handleGetGraph },
35
+ { method: 'GET', path: '/v1/memories', auth: 'caller', storeReady: 'base', loop: 'off', handler: handleRecallMemories },
36
+ { method: 'GET', pattern: '/v1/sessions/:id/assemble', auth: 'caller', storeReady: 'dagReads', loop: 'off', handler: handleAssembleSession },
37
+ { method: 'GET', pattern: '/v1/recall/drill/:id', auth: 'caller', storeReady: 'dagReads', handler: handleDrillRecall },
38
+ { method: 'POST', pattern: '/v1/memories/:id/archive', auth: 'caller', storeReady: 'entryWrites', loop: 'off', handler: handleArchiveMemory },
39
+ { method: 'POST', pattern: '/v1/memories/:id/supersede', auth: 'caller', storeReady: 'entryWrites', loop: 'off', handler: handleSupersedeMemory },
40
+ { method: 'POST', pattern: '/v1/memories/:id/promote', auth: 'caller', sqliteOnly: 'copies a memory between the two local hippo.db files',
41
+ handler: handlePromoteMemory },
42
+ { method: 'DELETE', pattern: '/v1/memories/:id', auth: 'caller', storeReady: 'entryWrites', loop: 'off', handler: handleForgetMemory },
43
+ { method: 'POST', path: '/v1/outcome', auth: 'caller', storeReady: 'entryWrites', handler: handleApplyOutcome },
44
+ { method: 'GET', path: '/v1/context', auth: 'caller', storeReady: 'contextReads', handler: handleGetContext },
45
+ { method: 'POST', path: '/v1/sleep', auth: 'host-admin-local', sqliteOnly: 'consolidates every tenant under the local hippo root in this process',
46
+ handler: handleSleep },
47
+ { method: 'POST', path: '/v1/auth/keys', auth: 'caller', storeReady: 'keyWrites', loop: 'off', handler: handleCreateAuthKey },
48
+ { method: 'GET', path: '/v1/auth/keys', auth: 'caller', storeReady: 'keyWrites', loop: 'off', handler: handleListAuthKeys },
49
+ { method: 'DELETE', pattern: '/v1/auth/keys/:keyId', auth: 'caller', storeReady: 'keyAudit', loop: 'off', handler: handleRevokeAuthKey },
50
+ { method: 'GET', path: '/v1/quarantine', auth: 'admin', storeReady: 'quarantine', loop: 'off', handler: handleListQuarantine },
51
+ { method: 'POST', pattern: '/v1/quarantine/:id/approve', auth: 'admin', storeReady: 'quarantine', loop: 'off', handler: handleApproveQuarantine },
52
+ { method: 'POST', pattern: '/v1/quarantine/:id/reject', auth: 'admin', storeReady: 'quarantine', loop: 'off', handler: handleRejectQuarantine },
53
+ { method: 'GET', path: '/v1/audit', auth: 'admin', storeReady: 'auditLog', loop: 'off', handler: handleListAudit },
54
+ { method: 'POST', path: '/v1/predictions', auth: 'caller', storeReady: 'predictions', loop: 'off', handler: handleCreatePrediction },
55
+ { method: 'GET', path: '/v1/predictions', auth: 'caller', storeReady: 'predictions', loop: 'off', handler: handleListPredictions },
56
+ { method: 'GET', path: '/v1/predictions/stats', auth: 'caller', storeReady: 'predictions', loop: 'off', handler: handlePredictionStats },
57
+ { method: 'GET', regex: /^\/v1\/predictions\/(\d+)$/, auth: 'caller', storeReady: 'predictions', loop: 'off', handler: handleGetPrediction },
58
+ { method: 'POST', regex: /^\/v1\/predictions\/(\d+)\/close$/, auth: 'caller', storeReady: 'predictions', loop: 'off', handler: handleClosePrediction },
59
+ { method: 'POST', path: '/v1/decisions', auth: 'caller', storeReady: 'objects', loop: 'off', handler: handleCreateDecision },
60
+ { method: 'GET', path: '/v1/decisions', auth: 'caller', storeReady: 'objects', loop: 'off', handler: handleListDecisions },
61
+ { method: 'POST', regex: /^\/v1\/decisions\/(\d+)\/supersede$/, auth: 'caller', storeReady: 'objects', loop: 'off', handler: handleSupersedeDecision },
62
+ { method: 'POST', regex: /^\/v1\/decisions\/(\d+)\/close$/, auth: 'caller', storeReady: 'objects', loop: 'off', handler: handleCloseDecision },
63
+ { method: 'GET', regex: /^\/v1\/decisions\/(\d+)$/, auth: 'caller', storeReady: 'objects', loop: 'off', handler: handleGetDecision },
64
+ { method: 'POST', path: '/v1/incidents', auth: 'caller', storeReady: 'objects', loop: 'off', handler: handleCreateIncident },
65
+ { method: 'GET', path: '/v1/incidents', auth: 'caller', storeReady: 'objects', loop: 'off', handler: handleListIncidents },
66
+ { method: 'POST', regex: /^\/v1\/incidents\/(\d+)\/resolve$/, auth: 'caller', storeReady: 'objects', loop: 'off', handler: handleResolveIncident },
67
+ { method: 'POST', regex: /^\/v1\/incidents\/(\d+)\/close$/, auth: 'caller', storeReady: 'objects', loop: 'off', handler: handleCloseIncident },
68
+ { method: 'GET', regex: /^\/v1\/incidents\/(\d+)$/, auth: 'caller', storeReady: 'objects', loop: 'off', handler: handleGetIncident },
69
+ { method: 'POST', path: '/v1/processes', auth: 'caller', storeReady: 'objects', loop: 'off', handler: handleCreateProcess },
70
+ { method: 'GET', path: '/v1/processes', auth: 'caller', storeReady: 'objects', loop: 'off', handler: handleListProcesses },
71
+ { method: 'POST', regex: /^\/v1\/processes\/(\d+)\/supersede$/, auth: 'caller', storeReady: 'objects', loop: 'off', handler: handleSupersedeProcess },
72
+ { method: 'POST', regex: /^\/v1\/processes\/(\d+)\/close$/, auth: 'caller', storeReady: 'objects', loop: 'off', handler: handleCloseProcess },
73
+ { method: 'GET', regex: /^\/v1\/processes\/(\d+)$/, auth: 'caller', storeReady: 'objects', loop: 'off', handler: handleGetProcess },
74
+ { method: 'POST', path: '/v1/policies', auth: 'caller', storeReady: 'objects', loop: 'off', handler: handleCreatePolicy },
75
+ { method: 'GET', path: '/v1/policies', auth: 'caller', storeReady: 'objects', loop: 'off', handler: handleListPolicies },
76
+ { method: 'GET', path: '/v1/policies/asof', auth: 'caller', storeReady: 'objects', loop: 'off', handler: handlePoliciesAsOf },
77
+ { method: 'POST', regex: /^\/v1\/policies\/(\d+)\/supersede$/, auth: 'caller', storeReady: 'objects', loop: 'off', handler: handleSupersedePolicy },
78
+ { method: 'POST', regex: /^\/v1\/policies\/(\d+)\/close$/, auth: 'caller', storeReady: 'objects', loop: 'off', handler: handleClosePolicy },
79
+ { method: 'GET', regex: /^\/v1\/policies\/(\d+)$/, auth: 'caller', storeReady: 'objects', loop: 'off', handler: handleGetPolicy },
80
+ { method: 'POST', path: '/v1/skills', auth: 'caller', storeReady: 'objects', loop: 'off', handler: handleCreateSkill },
81
+ { method: 'GET', path: '/v1/skills', auth: 'caller', storeReady: 'objects', loop: 'off', handler: handleListSkills },
82
+ { method: 'GET', path: '/v1/skills/export', auth: 'caller', storeReady: 'objects', loop: 'off', handler: handleExportSkills },
83
+ { method: 'POST', regex: /^\/v1\/skills\/(\d+)\/supersede$/, auth: 'caller', storeReady: 'objects', loop: 'off', handler: handleSupersedeSkill },
84
+ { method: 'POST', regex: /^\/v1\/skills\/(\d+)\/close$/, auth: 'caller', storeReady: 'objects', loop: 'off', handler: handleCloseSkill },
85
+ { method: 'GET', regex: /^\/v1\/skills\/(\d+)$/, auth: 'caller', storeReady: 'objects', loop: 'off', handler: handleGetSkill },
86
+ { method: 'POST', path: '/v1/project-briefs', auth: 'caller', storeReady: 'objects', loop: 'off', handler: handleCreateProjectBrief },
87
+ { method: 'GET', path: '/v1/project-briefs', auth: 'caller', storeReady: 'objects', loop: 'off', handler: handleListProjectBriefs },
88
+ { method: 'POST', path: '/v1/project-briefs/refresh', auth: 'caller', storeReady: 'objects', loop: 'off', handler: handleRefreshProjectBrief },
89
+ { method: 'POST', regex: /^\/v1\/project-briefs\/(\d+)\/supersede$/, auth: 'caller', storeReady: 'objects', loop: 'off',
90
+ handler: handleSupersedeProjectBrief },
91
+ { method: 'POST', regex: /^\/v1\/project-briefs\/(\d+)\/close$/, auth: 'caller', storeReady: 'objects', loop: 'off', handler: handleCloseProjectBrief },
92
+ { method: 'GET', regex: /^\/v1\/project-briefs\/(\d+)$/, auth: 'caller', storeReady: 'objects', loop: 'off', handler: handleGetProjectBrief },
93
+ { method: 'POST', path: '/v1/customer-notes', auth: 'caller', storeReady: 'objects', loop: 'off', handler: handleCreateCustomerNote },
94
+ { method: 'GET', path: '/v1/customer-notes', auth: 'caller', storeReady: 'objects', loop: 'off', handler: handleListCustomerNotes },
95
+ { method: 'POST', regex: /^\/v1\/customer-notes\/(\d+)\/supersede$/, auth: 'caller', storeReady: 'objects', loop: 'off',
96
+ handler: handleSupersedeCustomerNote },
97
+ { method: 'POST', regex: /^\/v1\/customer-notes\/(\d+)\/close$/, auth: 'caller', storeReady: 'objects', loop: 'off', handler: handleCloseCustomerNote },
98
+ { method: 'GET', regex: /^\/v1\/customer-notes\/(\d+)$/, auth: 'caller', storeReady: 'objects', loop: 'off', handler: handleGetCustomerNote },
99
+ { method: 'POST', path: '/v1/connectors/slack/events', auth: 'webhook-signature', storeReady: 'connectorEvents', handler: webhook(handleSlackEventsWebhook) },
100
+ { method: 'POST', path: '/v1/connectors/github/events', auth: 'webhook-signature', storeReady: 'connectorEvents',
101
+ handler: webhook(handleGitHubEventsWebhook) },
92
102
  ];
93
- /** The route's handler bound to this request's path params, or null when method or path differ. The matcher runs before the
94
- * method check, as the inline route blocks did, so a malformed `%` escape still throws from matchPath on any method. */
95
- function routeMatches(route, method, path) {
103
+ /** The handler bound to this path's params, or null when the path differs. */
104
+ function bindPath(route, path) {
96
105
  if ('path' in route)
97
- return method === route.method && path === route.path ? route.handler : null;
106
+ return path === route.path ? route.handler : null;
98
107
  if ('pattern' in route) {
99
108
  const params = matchPath(route.pattern, path);
100
- return method === route.method && params ? (r) => route.handler(r, params) : null;
109
+ return params ? (r) => route.handler(r, params) : null;
101
110
  }
102
111
  const match = path.match(route.regex);
103
- return method === route.method && match ? (r) => route.handler(r, match) : null;
112
+ return match ? (r) => route.handler(r, match) : null;
113
+ }
114
+ /** The route's run for this request, gate first, or null when method or path differ. The matcher runs before the method check,
115
+ * as the inline route blocks did, so a malformed `%` escape still throws from matchPath on any method. */
116
+ export function routeMatches(route, method, path) {
117
+ if (route.auth === 'webhook-signature') {
118
+ const run = bindPath(route, path);
119
+ if (run === null || method !== route.method)
120
+ return null;
121
+ return async (r) => {
122
+ assertRoutePorted(r.opts, route.storeReady);
123
+ await run(r);
124
+ };
125
+ }
126
+ const run = bindPath(route, path);
127
+ if (run === null || method !== route.method)
128
+ return null;
129
+ return async (r) => run({ ...r, ctx: await passAuthGate(route, r) });
130
+ }
131
+ /** The one caller check a gated route passes: sleep refuses a caller off this host before any key work, then one charged auth,
132
+ * then 501 for an unported route, so a bad key is still a 401 there, and the row's role last. */
133
+ async function passAuthGate(route, { req, opts }) {
134
+ const label = routeLabel(route);
135
+ // Defence in depth, not the boundary: a socket address says where a request came from, never who sent it.
136
+ if (route.auth === 'host-admin-local' && !isLoopback(req.socket.remoteAddress)) {
137
+ throw new HttpError(403, `${label} is loopback-only (host-wide consolidation; see CHANGELOG v1.11.4)`);
138
+ }
139
+ const ctx = await buildContextWithAuth(req, opts);
140
+ assertRoutePorted(opts, route.storeReady);
141
+ if (route.auth === 'admin' && ctx.actor.role !== 'admin')
142
+ throw new HttpError(403, `${label} requires admin role`);
143
+ if (route.auth === 'host-admin-local')
144
+ assertCrossTenantAdmin(ctx, label);
145
+ return ctx;
104
146
  }
105
147
  /** The route as the access line names it; a regex route reads as its pattern would, so no line carries a caller's id. */
106
- function routeLabel(route) {
148
+ export function routeLabel(route) {
107
149
  if ('path' in route)
108
150
  return route.path;
109
151
  if ('pattern' in route)
@@ -117,61 +159,20 @@ export async function dispatchV1Route(r, method, path) {
117
159
  if (run === null)
118
160
  continue;
119
161
  noteAccess(r.req, { route: routeLabel(route) });
120
- await refuseUnportedRoute(r.req, r.opts, route.storeReady);
121
162
  // Only a store with worker threads has moved the route's SQLite work; under any other the handler still opens hippo.db itself.
122
163
  await (route.loop === 'off' && runsOffLoop(r.opts.store) ? withSqliteOffLoop(() => run(r)) : run(r));
123
164
  return true;
124
165
  }
125
166
  return false;
126
167
  }
127
- const PLAIN_SEGMENT_RE = /^[A-Za-z0-9._~-]+$/;
128
- function isPlainV1Path(path) {
129
- return path.startsWith('/v1/') && new URL(path, 'http://h').pathname === path
130
- && path.slice('/v1/'.length).split('/').every((segment) => PLAIN_SEGMENT_RE.test(segment));
131
- }
132
- // A plain path never holds a `%`, so matchPath cannot throw here.
133
- function isCorePath(method, path) {
134
- return PUBLIC_ROUTES.has(`${method} ${path}`) || V1_ROUTES.some((route) => routeMatches(route, method, path) !== null);
135
- }
136
- /** Boot-time check: an add-on path must be plain, unique and not one core serves, so no add-on shadows a core route or hides from dispatch. */
137
- export function assertAddonRoutes(routes) {
138
- const seen = new Set();
139
- for (const { path } of routes) {
140
- if (!isPlainV1Path(path))
141
- throw new Error(`add-on route '${path}' is not a plain /v1/ path (segments use A-Z a-z 0-9 . _ ~ -)`);
142
- if (seen.has(path))
143
- throw new Error(`add-on route '${path}' is registered twice`);
144
- if (isCorePath('POST', path))
145
- throw new Error(`add-on route '${path}' is already served by core`);
146
- seen.add(path);
147
- }
148
- }
149
- const PUBLIC_JSON_MAX_BYTES = 64 * 1024;
150
- /** Boot-time check and serialization: a public path that a core GET route serves would open that route to anyone. */
151
- export function assertPublicJson(publicJson) {
152
- const bodies = new Map();
153
- for (const [path, value] of Object.entries(publicJson)) {
154
- if (!isPlainV1Path(path))
155
- throw new Error(`public JSON path '${path}' is not a plain /v1/ path (segments use A-Z a-z 0-9 . _ ~ -)`);
156
- if (isCorePath('GET', path))
157
- throw new Error(`public JSON path '${path}' is already served by core`);
158
- const text = JSON.stringify(value);
159
- if (text === undefined)
160
- throw new Error(`public JSON at '${path}' is not JSON`);
161
- if (Buffer.byteLength(text) > PUBLIC_JSON_MAX_BYTES)
162
- throw new Error(`public JSON at '${path}' is over 64 KiB`);
163
- bodies.set(path, text);
164
- }
165
- return bodies;
166
- }
167
168
  /** Core authenticates and parses before the handler runs, so an add-on route gets the same 401, 400 and 501 as a core one. */
168
169
  export async function dispatchAddonRoute({ req, res, opts }, method, path) {
169
170
  const route = method === 'POST' ? opts.routes?.find((r) => r.path === path) : undefined;
170
171
  if (!route)
171
172
  return false;
172
173
  noteAccess(req, { route: route.path });
173
- await refuseUnportedRoute(req, opts, route.storeReady);
174
174
  const ctx = await buildContextWithAuth(req, opts);
175
+ assertRoutePorted(opts, route.storeReady);
175
176
  noteAccess(req, { tenant: ctx.tenantId });
176
177
  const body = await parseJsonBody(req, ctx);
177
178
  sendJson(res, 200, await route.handler({ ctx, body }));
@@ -187,16 +188,9 @@ export function dispatchPublicJson({ req, res, opts }, method, path) {
187
188
  res.end(text);
188
189
  return true;
189
190
  }
190
- export function assertSqliteStore(opts) {
191
- if (opts.store.kind !== 'sqlite')
191
+ /** A route answers 501 without running when the store lacks its group; a route that names none, a sqliteOnly one among them, needs hippo.db's local files. */
192
+ function assertRoutePorted(opts, group = 'localFiles') {
193
+ if (!hasGroup(opts.store, group))
192
194
  throw new HttpError(501, STORE_NOT_PORTED_MESSAGE);
193
195
  }
194
- /** Under another store, a route that names no group, or one the store lacks, answers
195
- * 501 without running; the caller is checked first, so a bad key is still a 401. */
196
- async function refuseUnportedRoute(req, opts, group) {
197
- if (opts.store.kind === 'sqlite' || (group !== undefined && hasGroup(opts.store, group)))
198
- return;
199
- await requireAuth(req, opts);
200
- assertSqliteStore(opts);
201
- }
202
196
  //# sourceMappingURL=route-table.js.map
@@ -1,9 +1,9 @@
1
- import type { RouteRequest } from '../types.js';
2
- export declare function handleCreateAuthKey({ req, res, opts }: RouteRequest): Promise<void>;
3
- export declare function handleListAuthKeys({ req, res, opts, query }: RouteRequest): Promise<void>;
4
- export declare function handleRevokeAuthKey({ req, res, opts }: RouteRequest, keyMatch: Record<string, string>): Promise<void>;
5
- export declare function handleListQuarantine({ req, res, opts, query }: RouteRequest): Promise<void>;
6
- export declare function handleApproveQuarantine({ req, res, opts }: RouteRequest, quarantineApproveMatch: Record<string, string>): Promise<void>;
7
- export declare function handleRejectQuarantine({ req, res, opts }: RouteRequest, quarantineRejectMatch: Record<string, string>): Promise<void>;
8
- export declare function handleListAudit({ req, res, opts, query }: RouteRequest): Promise<void>;
1
+ import type { AuthedRouteRequest } from '../types.js';
2
+ export declare function handleCreateAuthKey({ req, res, opts }: AuthedRouteRequest): Promise<void>;
3
+ export declare function handleListAuthKeys({ res, ctx, query }: AuthedRouteRequest): Promise<void>;
4
+ export declare function handleRevokeAuthKey({ res, ctx }: AuthedRouteRequest, keyMatch: Record<string, string>): Promise<void>;
5
+ export declare function handleListQuarantine({ res, ctx, query }: AuthedRouteRequest): Promise<void>;
6
+ export declare function handleApproveQuarantine({ res, ctx }: AuthedRouteRequest, quarantineApproveMatch: Record<string, string>): Promise<void>;
7
+ export declare function handleRejectQuarantine({ res, ctx }: AuthedRouteRequest, quarantineRejectMatch: Record<string, string>): Promise<void>;
8
+ export declare function handleListAudit({ res, ctx, query }: AuthedRouteRequest): Promise<void>;
9
9
  //# sourceMappingURL=admin.d.ts.map
@@ -1,13 +1,14 @@
1
1
  // Admin routes: API keys, quarantine and the audit log.
2
+ import { DEFAULT_LIST_LIMIT } from '../../util/limits.js';
2
3
  import { AUDIT_OPS } from '../../store/audit.js';
3
4
  import { auditList, authCreate, authListRows, authRevoke, quarantineApprove, quarantineList, quarantineReject } from '../../api/index.js';
4
5
  import { HttpError, readBody, sendJson } from '../../util/http-util.js';
5
- import { assertCrossTenantAdmin, buildContextWithAuth } from '../auth.js';
6
+ import { assertCrossTenantAdmin, recheckContextWithAuth } from '../auth.js';
6
7
  import { pageOf, parseCursor, setNextCursorHeader } from '../cursor.js';
7
8
  import { isSetMember, parseJsonObjectText, parseListLimit, validateIdSegment } from '../validation.js';
8
9
  import { isJsonBoolean, isJsonNumber, isJsonString } from '../../util/json.js';
9
10
  const VALID_AUDIT_OPS = new Set(AUDIT_OPS);
10
- // POST /v1/auth/keys reads its body before auth, so an unauthenticated caller can make the server buffer at most this many bytes and wait at most this many ms.
11
+ // A key mint is a few short fields, so even an authenticated caller gets a small cap and a short wait.
11
12
  // ServeOpts.mintBodyDeadlineMs overrides the wait for tests only, so a 408 test need not sit through it.
12
13
  const MINT_BODY_MAX_BYTES = 4 * 1024;
13
14
  const MINT_BODY_DEADLINE_MS = 10_000;
@@ -17,9 +18,9 @@ const MAX_AUDIT_LIMIT = 10000;
17
18
  const MAX_AUTH_KEYS_PAGE = 1000;
18
19
  // POST /v1/auth/keys: mints a key; plaintext is in the response body (the CLI client, not this layer, carries the "store this somewhere safe" warning).
19
20
  export async function handleCreateAuthKey({ req, res, opts }) {
20
- // Body first, so the resolver's check (and any gate in it) runs right before the mint with no wait between.
21
+ // The dispatch gate authenticated before the body, so an unknown caller costs no read; this recheck runs the resolver right before the mint.
21
22
  const raw = await readBody(req, { maxBytes: MINT_BODY_MAX_BYTES, deadlineMs: opts.mintBodyDeadlineMs ?? MINT_BODY_DEADLINE_MS });
22
- const ctx = await buildContextWithAuth(req, opts);
23
+ const ctx = await recheckContextWithAuth(req, opts);
23
24
  const body = parseJsonObjectText(raw);
24
25
  const labelRaw = body['label'];
25
26
  if (labelRaw !== undefined && !isJsonString(labelRaw)) {
@@ -56,7 +57,7 @@ export async function handleCreateAuthKey({ req, res, opts }) {
56
57
  return;
57
58
  }
58
59
  // GET /v1/auth/keys?active=true&limit=&cursor=: lists keys visible to ctx.tenantId; `active` defaults to true, ?active=false includes revoked and expired rows.
59
- export async function handleListAuthKeys({ req, res, opts, query }) {
60
+ export async function handleListAuthKeys({ res, ctx, query }) {
60
61
  const activeRaw = query.get('active');
61
62
  let active = true;
62
63
  if (activeRaw !== null) {
@@ -69,7 +70,6 @@ export async function handleListAuthKeys({ req, res, opts, query }) {
69
70
  }
70
71
  const limit = parseListLimit(query.get('limit'), MAX_AUTH_KEYS_PAGE, MAX_AUTH_KEYS_PAGE);
71
72
  const after = parseCursor(query.get('cursor'), 'integer', 'integer');
72
- const ctx = await buildContextWithAuth(req, opts);
73
73
  const page = pageOf(await authListRows(ctx, { active, limit: limit + 1, after }), limit, (r) => ({ key: r.rowId, id: r.rowId }));
74
74
  setNextCursorHeader(res, page.nextCursor);
75
75
  sendJson(res, 200, page.items.map((r) => r.key));
@@ -77,19 +77,14 @@ export async function handleListAuthKeys({ req, res, opts, query }) {
77
77
  }
78
78
  // DELETE /v1/auth/keys/:keyId revokes: missing or cross-tenant keys are 404 (no leak); a member is 403 on any key but its own or, via the resolver, those it
79
79
  // minted. Returns 200 with the body rather than 204 so the caller sees revokedAt.
80
- export async function handleRevokeAuthKey({ req, res, opts }, keyMatch) {
80
+ export async function handleRevokeAuthKey({ res, ctx }, keyMatch) {
81
81
  validateIdSegment(keyMatch.keyId, 'key id');
82
- const ctx = await buildContextWithAuth(req, opts);
83
82
  const result = await authRevoke(ctx, keyMatch.keyId);
84
83
  sendJson(res, 200, result);
85
84
  return;
86
85
  }
87
- // GET /v1/quarantine?status=&limit=&cursor=: quarantine review queue. quarantineList carries no role gate itself, so it's checked here.
88
- export async function handleListQuarantine({ req, res, opts, query }) {
89
- const ctx = await buildContextWithAuth(req, opts);
90
- if (ctx.actor.role !== 'admin') {
91
- throw new HttpError(403, '/v1/quarantine requires admin role');
92
- }
86
+ // GET /v1/quarantine?status=&limit=&cursor=: quarantine review queue. quarantineList carries no role gate itself, so its row's admin gate is the check.
87
+ export async function handleListQuarantine({ res, ctx, query }) {
93
88
  const statusRaw = query.get('status');
94
89
  let status = 'pending';
95
90
  if (statusRaw !== null) {
@@ -105,23 +100,21 @@ export async function handleListQuarantine({ req, res, opts, query }) {
105
100
  return;
106
101
  }
107
102
  // POST /v1/quarantine/:id/approve: admin only; ForbiddenError falls through to mapApiError's 403.
108
- export async function handleApproveQuarantine({ req, res, opts }, quarantineApproveMatch) {
103
+ export async function handleApproveQuarantine({ res, ctx }, quarantineApproveMatch) {
109
104
  validateIdSegment(quarantineApproveMatch.id, 'memory id');
110
- const ctx = await buildContextWithAuth(req, opts);
111
105
  await quarantineApprove(ctx, quarantineApproveMatch.id);
112
106
  sendJson(res, 200, { approved: quarantineApproveMatch.id });
113
107
  return;
114
108
  }
115
109
  // POST /v1/quarantine/:id/reject: admin only; ForbiddenError falls through to mapApiError's 403.
116
- export async function handleRejectQuarantine({ req, res, opts }, quarantineRejectMatch) {
110
+ export async function handleRejectQuarantine({ res, ctx }, quarantineRejectMatch) {
117
111
  validateIdSegment(quarantineRejectMatch.id, 'memory id');
118
- const ctx = await buildContextWithAuth(req, opts);
119
112
  await quarantineReject(ctx, quarantineRejectMatch.id);
120
113
  sendJson(res, 200, { rejected: quarantineRejectMatch.id });
121
114
  return;
122
115
  }
123
116
  // GET /v1/audit?op=&since=&limit=&cursor=: all three filters are validated at the route boundary so an invalid value is a 400 before the DB is hit.
124
- export async function handleListAudit({ req, res, opts, query }) {
117
+ export async function handleListAudit({ res, ctx, query }) {
125
118
  const opRaw = query.get('op');
126
119
  let op;
127
120
  if (opRaw !== null) {
@@ -140,7 +133,7 @@ export async function handleListAudit({ req, res, opts, query }) {
140
133
  since = sinceRaw;
141
134
  }
142
135
  const limitRaw = query.get('limit');
143
- let limit = 100;
136
+ let limit = DEFAULT_LIST_LIMIT;
144
137
  if (limitRaw !== null) {
145
138
  const parsed = Number(limitRaw);
146
139
  if (!Number.isFinite(parsed) || !Number.isInteger(parsed) || parsed < 1 || parsed > MAX_AUDIT_LIMIT) {
@@ -149,7 +142,6 @@ export async function handleListAudit({ req, res, opts, query }) {
149
142
  limit = parsed;
150
143
  }
151
144
  const after = parseCursor(query.get('cursor'), 'string', 'integer');
152
- const ctx = await buildContextWithAuth(req, opts);
153
145
  // ?tenant=<t> reads another tenant (e.g. '__host__' for consolidate rows); admin only.
154
146
  const tenantOverride = query.get('tenant');
155
147
  const crossTenant = tenantOverride !== null && tenantOverride !== '' && tenantOverride !== ctx.tenantId;
@@ -1,7 +1,7 @@
1
- import type { RouteRequest } from '../types.js';
2
- export declare function handleCreateCustomerNote(rr: RouteRequest): Promise<void>;
3
- export declare function handleListCustomerNotes(rr: RouteRequest): Promise<void>;
4
- export declare function handleSupersedeCustomerNote(rr: RouteRequest, match: RegExpMatchArray): Promise<void>;
5
- export declare function handleCloseCustomerNote(rr: RouteRequest, match: RegExpMatchArray): Promise<void>;
6
- export declare function handleGetCustomerNote(rr: RouteRequest, match: RegExpMatchArray): Promise<void>;
1
+ import type { AuthedRouteRequest } from '../types.js';
2
+ export declare function handleCreateCustomerNote(rr: AuthedRouteRequest): Promise<void>;
3
+ export declare function handleListCustomerNotes(rr: AuthedRouteRequest): Promise<void>;
4
+ export declare function handleSupersedeCustomerNote(rr: AuthedRouteRequest, match: RegExpMatchArray): Promise<void>;
5
+ export declare function handleCloseCustomerNote(rr: AuthedRouteRequest, match: RegExpMatchArray): Promise<void>;
6
+ export declare function handleGetCustomerNote(rr: AuthedRouteRequest, match: RegExpMatchArray): Promise<void>;
7
7
  //# sourceMappingURL=customer-notes.d.ts.map
@@ -1,7 +1,6 @@
1
1
  // /v1/customer-notes routes.
2
2
  import { CUSTOMER_NOTE, MAX_CHANGE_SUMMARY_LEN, MAX_CUSTOMER_LEN } from '../../objects/customer-notes.js';
3
3
  import { sendJson } from '../../util/http-util.js';
4
- import { buildContextWithAuth } from '../auth.js';
5
4
  import { parseJsonBody } from '../validation.js';
6
5
  import { saveFor } from '../../api/objects.js';
7
6
  import { closeRoute, getRoute, listRoute, optionalString, requiredString, supersedeRoute } from './object-routes.js';
@@ -20,7 +19,7 @@ const noteRoutes = {
20
19
  // Routes: customer notes under /v1/customer-notes (create, list, show, supersede, close), mirroring /v1/project-briefs. The store validates and throws;
21
20
  // the boundary maps validation -> 400, not-found -> 404, not-active -> 409.
22
21
  export async function handleCreateCustomerNote(rr) {
23
- const ctx = await buildContextWithAuth(rr.req, rr.opts);
22
+ const { ctx } = rr;
24
23
  const body = await parseJsonBody(rr.req, ctx);
25
24
  const customerNote = await saveFor(ctx, CUSTOMER_NOTE, {
26
25
  customer: requiredString(body, 'customer', { max: MAX_CUSTOMER_LEN }),
@@ -1,7 +1,7 @@
1
- import type { RouteRequest } from '../types.js';
2
- export declare function handleCreateDecision(rr: RouteRequest): Promise<void>;
3
- export declare function handleListDecisions(rr: RouteRequest): Promise<void>;
4
- export declare function handleSupersedeDecision(rr: RouteRequest, match: RegExpMatchArray): Promise<void>;
5
- export declare function handleCloseDecision(rr: RouteRequest, match: RegExpMatchArray): Promise<void>;
6
- export declare function handleGetDecision(rr: RouteRequest, match: RegExpMatchArray): Promise<void>;
1
+ import type { AuthedRouteRequest } from '../types.js';
2
+ export declare function handleCreateDecision(rr: AuthedRouteRequest): Promise<void>;
3
+ export declare function handleListDecisions(rr: AuthedRouteRequest): Promise<void>;
4
+ export declare function handleSupersedeDecision(rr: AuthedRouteRequest, match: RegExpMatchArray): Promise<void>;
5
+ export declare function handleCloseDecision(rr: AuthedRouteRequest, match: RegExpMatchArray): Promise<void>;
6
+ export declare function handleGetDecision(rr: AuthedRouteRequest, match: RegExpMatchArray): Promise<void>;
7
7
  //# sourceMappingURL=decisions.d.ts.map
@@ -3,7 +3,6 @@ import { DECISION } from '../../objects/decisions.js';
3
3
  import { HttpError, sendJson } from '../../util/http-util.js';
4
4
  import { NotFoundError } from '../../core/api-errors.js';
5
5
  import { isJsonNumber } from '../../util/json.js';
6
- import { buildContextWithAuth } from '../auth.js';
7
6
  import { MAX_SHORT_FIELD_LEN, parseJsonBody } from '../validation.js';
8
7
  import { saveFor } from '../../api/objects.js';
9
8
  import { closeRoute, getRoute, listRoute, optionalString, requiredString } from './object-routes.js';
@@ -21,8 +20,7 @@ function supersededId(body) {
21
20
  // Routes: /v1/decisions (create, list, show, supersede, close), Bearer-authed and tenant-scoped; status is validated against VALID_DECISION_STATES.
22
21
  // The HTTP surface has no legacy --supersedes <memory-id> constraint, so it supersedes by table id and never weakens a memory mirror.
23
22
  export async function handleCreateDecision(rr) {
24
- const { req, res } = rr;
25
- const ctx = await buildContextWithAuth(req, rr.opts);
23
+ const { req, res, ctx } = rr;
26
24
  const body = await parseJsonBody(req, ctx);
27
25
  const write = {
28
26
  decisionText: requiredString(body, 'text', TEXT),
@@ -46,7 +44,7 @@ export function handleListDecisions(rr) {
46
44
  // Not on supersedeRoute: this one answers 201 and leaves the missing-row 404 to the store, where the other five answer 200 after their own lookup.
47
45
  export async function handleSupersedeDecision(rr, match) {
48
46
  const oldId = parseInt(match[1], 10);
49
- const ctx = await buildContextWithAuth(rr.req, rr.opts);
47
+ const { ctx } = rr;
50
48
  const body = await parseJsonBody(rr.req, ctx);
51
49
  const decision = await saveFor(ctx, DECISION, {
52
50
  decisionText: requiredString(body, 'text', TEXT),
@@ -1,7 +1,7 @@
1
- import type { RouteRequest } from '../types.js';
2
- export declare function handleCreateIncident(rr: RouteRequest): Promise<void>;
3
- export declare function handleListIncidents(rr: RouteRequest): Promise<void>;
4
- export declare function handleResolveIncident(rr: RouteRequest, match: RegExpMatchArray): Promise<void>;
5
- export declare function handleCloseIncident(rr: RouteRequest, match: RegExpMatchArray): Promise<void>;
6
- export declare function handleGetIncident(rr: RouteRequest, match: RegExpMatchArray): Promise<void>;
1
+ import type { AuthedRouteRequest } from '../types.js';
2
+ export declare function handleCreateIncident(rr: AuthedRouteRequest): Promise<void>;
3
+ export declare function handleListIncidents(rr: AuthedRouteRequest): Promise<void>;
4
+ export declare function handleResolveIncident(rr: AuthedRouteRequest, match: RegExpMatchArray): Promise<void>;
5
+ export declare function handleCloseIncident(rr: AuthedRouteRequest, match: RegExpMatchArray): Promise<void>;
6
+ export declare function handleGetIncident(rr: AuthedRouteRequest, match: RegExpMatchArray): Promise<void>;
7
7
  //# sourceMappingURL=incidents.d.ts.map
@@ -2,7 +2,6 @@
2
2
  import { INCIDENT, openIncident, resolveOpenIncident } from '../../objects/incidents.js';
3
3
  import { HttpError, sendJson } from '../../util/http-util.js';
4
4
  import { NotFoundError } from '../../core/api-errors.js';
5
- import { buildContextWithAuth } from '../auth.js';
6
5
  import { MAX_SHORT_FIELD_LEN, parseJsonBody } from '../validation.js';
7
6
  import { isJsonString } from '../../util/json.js';
8
7
  import { objectsOf } from '../../api/objects.js';
@@ -28,8 +27,7 @@ function linkedMemoryIds(body) {
28
27
  // Routes: /v1/incidents (open, list, show, resolve, close), mirroring /v1/decisions; status is validated against VALID_INCIDENT_STATES.
29
28
  // Lifecycle is open->resolved->closed (no supersede), so linkedMemoryIds replaces supersedesDecisionId on create.
30
29
  export async function handleCreateIncident(rr) {
31
- const { req, res, opts } = rr;
32
- const ctx = await buildContextWithAuth(req, opts);
30
+ const { req, res, opts, ctx } = rr;
33
31
  const body = await parseJsonBody(req, ctx);
34
32
  const write = {
35
33
  incidentText: requiredString(body, 'text', { max: MAX_SHORT_FIELD_LEN, untrimmed: true }),
@@ -51,9 +49,8 @@ export function handleListIncidents(rr) {
51
49
  return listRoute(incidentRoutes, rr);
52
50
  }
53
51
  export async function handleResolveIncident(rr, match) {
54
- const { req, res, opts } = rr;
52
+ const { req, res, ctx } = rr;
55
53
  const id = parseInt(match[1], 10);
56
- const ctx = await buildContextWithAuth(req, opts);
57
54
  const resolutionText = requiredString(await parseJsonBody(req, ctx), 'resolutionText', { max: MAX_SHORT_FIELD_LEN });
58
55
  const incident = await resolveOpenIncident(objectsOf(ctx), ctx.tenantId, id, resolutionText, ctx.actor.subject);
59
56
  sendJson(res, 200, { incident });