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
@@ -27,7 +27,8 @@ export function replyFor(err) {
27
27
  }
28
28
  // Above the 30 s body deadline and the 5 s resolver deadline, so a request those govern is answered before it is called slow.
29
29
  const SLOW_REQUEST_WARN_MS = 60_000;
30
- /** Logs one warn when `res` has sent nothing after `afterMs`. It never ends the request or touches the reply; the timer goes when the reply finishes or the socket closes. */
30
+ /** Logs one warn when `res` has sent nothing after `afterMs`. It never ends the request
31
+ * or touches the reply; the timer goes when the reply finishes or the socket closes. */
31
32
  function watchSlowRequest(req, res, requestId, afterMs = SLOW_REQUEST_WARN_MS) {
32
33
  const startedAt = Date.now();
33
34
  const timer = setTimeout(() => {
@@ -51,6 +52,10 @@ export function noteAccess(req, note) {
51
52
  if (held)
52
53
  Object.assign(held, note);
53
54
  }
55
+ /** The route name the request's access line carries, for a line about the same request logged after its reply. */
56
+ export function accessRouteOf(req) {
57
+ return accessNotes.get(req)?.route ?? UNMATCHED_ROUTE;
58
+ }
54
59
  /** One info line per finished request. A path id and a query string can both hold caller data, so the line carries neither. */
55
60
  function logAccessWhenFinished(req, res, requestId) {
56
61
  const note = {};
@@ -97,14 +102,7 @@ export function parseRequest(req) {
97
102
  query: url.searchParams,
98
103
  };
99
104
  }
100
- /**
101
- * Lightweight pattern matcher for /v1/memories/:id/<action>. Avoids pulling
102
- * in a router dependency for the half-dozen patterns we actually use.
103
- *
104
- * Returns null if `path` does not match `pattern`. Otherwise returns an object
105
- * mapping each :param name to its value. Path segments are exact-matched
106
- * except for parameter slots.
107
- */
105
+ /** Pattern matcher for /v1/memories/:id/<action> (no router dependency); null if `path` does not match, else each :param name mapped to its value. */
108
106
  function decodePathSegment(segment) {
109
107
  try {
110
108
  return decodeURIComponent(segment);
@@ -135,14 +133,8 @@ export function matchPath(pattern, path) {
135
133
  }
136
134
  return params;
137
135
  }
138
- /**
139
- * Reject URL-encoded slashes in path segments BEFORE the URL parser decodes
140
- * them — otherwise `%2F` becomes `/`, path-split runs, and the route either
141
- * silently 404s or matches the wrong template.
142
- *
143
- * Only the PATHNAME is scanned (split on the first `?`), so recall queries
144
- * containing URLs like `?q=https%3A%2F%2Fexample.com` are not rejected.
145
- */
136
+ /** Rejects URL-encoded slashes in path segments BEFORE the URL parser decodes them (`%2F` would become `/` and 404 or match the wrong template).
137
+ * Only the PATHNAME is scanned, so recall queries like `?q=https%3A%2F%2Fexample.com` are not rejected. */
146
138
  export function rejectEncodedSlash(rawUrl) {
147
139
  const queryIdx = rawUrl.indexOf('?');
148
140
  const pathname = queryIdx === -1 ? rawUrl : rawUrl.slice(0, queryIdx);
@@ -16,16 +16,8 @@ import { handleCloseProjectBrief, handleCreateProjectBrief, handleGetProjectBrie
16
16
  import { handleAssembleSession, handleDrillRecall, handleGetContext, handleRecallMemories } from './routes/recall.js';
17
17
  import { handleCloseSkill, handleCreateSkill, handleExportSkills, handleGetSkill, handleListSkills, handleSupersedeSkill } from './routes/skills.js';
18
18
  import { parseJsonBody } from './validation.js';
19
- // Review patch #2: explicit allow-list for unauthenticated /v1/* routes.
20
- // New unauth routes MUST be added here AND get a corresponding entry in
21
- // tests/server-bearer-lockdown.test.ts. Do not gate auth elsewhere by
22
- // `path.startsWith` — pattern-positional auth is bypass-by-accident.
23
- //
24
- // The route handlers consult `isPublicRoute` before invoking
25
- // `buildContextWithAuth` / `requireAuth`. Adding a route here without
26
- // adding the corresponding `isPublicRoute` short-circuit in a handler is
27
- // a no-op (auth still applies), so the failure mode is fail-closed.
28
- // The only other unauthenticated paths are ServeOpts.publicJson's: GET only, checked at boot by assertPublicJson.
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.
29
21
  const PUBLIC_ROUTES = new Set([
30
22
  'POST /v1/connectors/slack/events',
31
23
  'POST /v1/connectors/github/events',
@@ -59,44 +51,44 @@ const V1_ROUTES = [
59
51
  { method: 'GET', path: '/v1/predictions/stats', storeReady: 'predictions', loop: 'off', handler: handlePredictionStats },
60
52
  { method: 'GET', regex: /^\/v1\/predictions\/(\d+)$/, storeReady: 'predictions', loop: 'off', handler: handleGetPrediction },
61
53
  { method: 'POST', regex: /^\/v1\/predictions\/(\d+)\/close$/, storeReady: 'predictions', loop: 'off', handler: handleClosePrediction },
62
- { method: 'POST', path: '/v1/decisions', storeReady: 'objects', handler: handleCreateDecision },
63
- { method: 'GET', path: '/v1/decisions', storeReady: 'objects', handler: handleListDecisions },
64
- { method: 'POST', regex: /^\/v1\/decisions\/(\d+)\/supersede$/, storeReady: 'objects', handler: handleSupersedeDecision },
65
- { method: 'POST', regex: /^\/v1\/decisions\/(\d+)\/close$/, storeReady: 'objects', handler: handleCloseDecision },
66
- { method: 'GET', regex: /^\/v1\/decisions\/(\d+)$/, storeReady: 'objects', handler: handleGetDecision },
67
- { method: 'POST', path: '/v1/incidents', storeReady: 'objects', handler: handleCreateIncident },
68
- { method: 'GET', path: '/v1/incidents', storeReady: 'objects', handler: handleListIncidents },
69
- { method: 'POST', regex: /^\/v1\/incidents\/(\d+)\/resolve$/, storeReady: 'objects', handler: handleResolveIncident },
70
- { method: 'POST', regex: /^\/v1\/incidents\/(\d+)\/close$/, storeReady: 'objects', handler: handleCloseIncident },
71
- { method: 'GET', regex: /^\/v1\/incidents\/(\d+)$/, storeReady: 'objects', handler: handleGetIncident },
72
- { method: 'POST', path: '/v1/processes', storeReady: 'objects', handler: handleCreateProcess },
73
- { method: 'GET', path: '/v1/processes', storeReady: 'objects', handler: handleListProcesses },
74
- { method: 'POST', regex: /^\/v1\/processes\/(\d+)\/supersede$/, storeReady: 'objects', handler: handleSupersedeProcess },
75
- { method: 'POST', regex: /^\/v1\/processes\/(\d+)\/close$/, storeReady: 'objects', handler: handleCloseProcess },
76
- { method: 'GET', regex: /^\/v1\/processes\/(\d+)$/, storeReady: 'objects', handler: handleGetProcess },
77
- { method: 'POST', path: '/v1/policies', storeReady: 'objects', handler: handleCreatePolicy },
78
- { method: 'GET', path: '/v1/policies', storeReady: 'objects', handler: handleListPolicies },
79
- { method: 'GET', path: '/v1/policies/asof', storeReady: 'objects', handler: handlePoliciesAsOf },
80
- { method: 'POST', regex: /^\/v1\/policies\/(\d+)\/supersede$/, storeReady: 'objects', handler: handleSupersedePolicy },
81
- { method: 'POST', regex: /^\/v1\/policies\/(\d+)\/close$/, storeReady: 'objects', handler: handleClosePolicy },
82
- { method: 'GET', regex: /^\/v1\/policies\/(\d+)$/, storeReady: 'objects', handler: handleGetPolicy },
83
- { method: 'POST', path: '/v1/skills', storeReady: 'objects', handler: handleCreateSkill },
84
- { method: 'GET', path: '/v1/skills', storeReady: 'objects', handler: handleListSkills },
85
- { method: 'GET', path: '/v1/skills/export', storeReady: 'objects', handler: handleExportSkills },
86
- { method: 'POST', regex: /^\/v1\/skills\/(\d+)\/supersede$/, storeReady: 'objects', handler: handleSupersedeSkill },
87
- { method: 'POST', regex: /^\/v1\/skills\/(\d+)\/close$/, storeReady: 'objects', handler: handleCloseSkill },
88
- { method: 'GET', regex: /^\/v1\/skills\/(\d+)$/, storeReady: 'objects', handler: handleGetSkill },
89
- { method: 'POST', path: '/v1/project-briefs', storeReady: 'objects', handler: handleCreateProjectBrief },
90
- { method: 'GET', path: '/v1/project-briefs', storeReady: 'objects', handler: handleListProjectBriefs },
91
- { method: 'POST', path: '/v1/project-briefs/refresh', storeReady: 'objects', handler: handleRefreshProjectBrief },
92
- { method: 'POST', regex: /^\/v1\/project-briefs\/(\d+)\/supersede$/, storeReady: 'objects', handler: handleSupersedeProjectBrief },
93
- { method: 'POST', regex: /^\/v1\/project-briefs\/(\d+)\/close$/, storeReady: 'objects', handler: handleCloseProjectBrief },
94
- { method: 'GET', regex: /^\/v1\/project-briefs\/(\d+)$/, storeReady: 'objects', handler: handleGetProjectBrief },
95
- { method: 'POST', path: '/v1/customer-notes', storeReady: 'objects', handler: handleCreateCustomerNote },
96
- { method: 'GET', path: '/v1/customer-notes', storeReady: 'objects', handler: handleListCustomerNotes },
97
- { method: 'POST', regex: /^\/v1\/customer-notes\/(\d+)\/supersede$/, storeReady: 'objects', handler: handleSupersedeCustomerNote },
98
- { method: 'POST', regex: /^\/v1\/customer-notes\/(\d+)\/close$/, storeReady: 'objects', handler: handleCloseCustomerNote },
99
- { method: 'GET', regex: /^\/v1\/customer-notes\/(\d+)$/, storeReady: 'objects', handler: handleGetCustomerNote },
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 },
100
92
  ];
101
93
  /** The route's handler bound to this request's path params, or null when method or path differ. The matcher runs before the
102
94
  * method check, as the inline route blocks did, so a malformed `%` escape still throws from matchPath on any method. */
@@ -199,7 +191,8 @@ export function assertSqliteStore(opts) {
199
191
  if (opts.store.kind !== 'sqlite')
200
192
  throw new HttpError(501, STORE_NOT_PORTED_MESSAGE);
201
193
  }
202
- /** Under another store, a route that names no group, or one the store lacks, answers 501 without running; the caller is checked first, so a bad key is still a 401. */
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. */
203
196
  async function refuseUnportedRoute(req, opts, group) {
204
197
  if (opts.store.kind === 'sqlite' || (group !== undefined && hasGroup(opts.store, group)))
205
198
  return;
@@ -2,25 +2,20 @@
2
2
  import { AUDIT_OPS } from '../../store/audit.js';
3
3
  import { auditList, authCreate, authListRows, authRevoke, quarantineApprove, quarantineList, quarantineReject } from '../../api/index.js';
4
4
  import { HttpError, readBody, sendJson } from '../../util/http-util.js';
5
- import { log } from '../../util/log.js';
6
5
  import { assertCrossTenantAdmin, buildContextWithAuth } from '../auth.js';
7
6
  import { pageOf, parseCursor, setNextCursorHeader } from '../cursor.js';
8
7
  import { isSetMember, parseJsonObjectText, parseListLimit, validateIdSegment } from '../validation.js';
9
- import { isJsonString } from '../../util/json.js';
8
+ import { isJsonBoolean, isJsonNumber, isJsonString } from '../../util/json.js';
10
9
  const VALID_AUDIT_OPS = new Set(AUDIT_OPS);
11
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.
12
11
  // ServeOpts.mintBodyDeadlineMs overrides the wait for tests only, so a 408 test need not sit through it.
13
12
  const MINT_BODY_MAX_BYTES = 4 * 1024;
14
13
  const MINT_BODY_DEADLINE_MS = 10_000;
15
- // Cap on GET /v1/audit?limit=. Matches docs/api.md (when written) and is large
16
- // enough to dump a small deployment's full audit log without paginating, but
17
- // small enough that a malicious client can't ask for the world.
14
+ // Cap on GET /v1/audit?limit=: large enough to dump a small deployment's audit log unpaginated, small enough that a client cannot ask for the world.
18
15
  const MAX_AUDIT_LIMIT = 10000;
19
16
  // GET /v1/auth/keys had no limit, so its default page is the cap: a deployment below 1000 keys sees no change.
20
17
  const MAX_AUTH_KEYS_PAGE = 1000;
21
- // POST /v1/auth/keys — mint a new API key. Plaintext lands in the response
22
- // body: the HTTP layer hands it to the client; the user-facing
23
- // "store this somewhere safe" warning belongs in the CLI client, not here.
18
+ // 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).
24
19
  export async function handleCreateAuthKey({ req, res, opts }) {
25
20
  // Body first, so the resolver's check (and any gate in it) runs right before the mint with no wait between.
26
21
  const raw = await readBody(req, { maxBytes: MINT_BODY_MAX_BYTES, deadlineMs: opts.mintBodyDeadlineMs ?? MINT_BODY_DEADLINE_MS });
@@ -30,9 +25,8 @@ export async function handleCreateAuthKey({ req, res, opts }) {
30
25
  if (labelRaw !== undefined && !isJsonString(labelRaw)) {
31
26
  throw new HttpError(400, 'label must be a string');
32
27
  }
33
- // Optional body.role mirrors the --role CLI flag. Validated
34
- // strictly — anything other than 'admin'|'member' is a 400 (no silent
35
- // fallback to admin). authCreate refuses a member caller with a 403.
28
+ // Optional body.role mirrors the --role CLI flag and is validated strictly: anything but 'admin'|'member' is a 400; authCreate refuses a member caller
29
+ // with a 403.
36
30
  const roleRaw = body['role'];
37
31
  let role;
38
32
  if (roleRaw !== undefined) {
@@ -41,24 +35,27 @@ export async function handleCreateAuthKey({ req, res, opts }) {
41
35
  }
42
36
  role = roleRaw;
43
37
  }
44
- // Security: any `tenantId` in the body is IGNORED. The minted key is
45
- // bound to the caller's authenticated tenant (ctx.tenantId, resolved
46
- // from the Bearer token). Forwarding body.tenantId here would let
47
- // tenant A mint a key for tenant B — see authCreate doc comment.
38
+ // A null is a 400 too: a client that serialises an unset field as null must not get a key that never expires.
39
+ const ttlDays = body['ttlDays'];
40
+ if (ttlDays !== undefined && !isJsonNumber(ttlDays)) {
41
+ throw new HttpError(400, 'ttlDays must be a number');
42
+ }
43
+ const noExpiry = body['noExpiry'];
44
+ if (noExpiry !== undefined && !isJsonBoolean(noExpiry)) {
45
+ throw new HttpError(400, 'noExpiry must be true or false');
46
+ }
47
+ // Security: any body `tenantId` is IGNORED; the key is bound to the caller's authenticated tenant, or tenant A could mint a key for tenant B (see
48
+ // authCreate).
48
49
  const result = await authCreate(ctx, {
49
50
  label: labelRaw,
50
51
  role,
52
+ ttlDays,
53
+ noExpiry,
51
54
  });
52
- // The reply cannot change shape, so the server log is where a defaulted admin key gets noticed.
53
- if (role === undefined && result.role === 'admin') {
54
- log.warn(`auth key ${result.keyId} was minted with no role in the body, so it is an admin key, and it never expires; send "role": "member" for a narrower one`);
55
- }
56
55
  sendJson(res, 200, result);
57
56
  return;
58
57
  }
59
- // GET /v1/auth/keys?active=true&limit=&cursor=: list keys visible to ctx.tenantId.
60
- // `active` defaults to true so the common case (show me usable keys) is
61
- // a single GET; ?active=false includes revoked and expired rows.
58
+ // 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.
62
59
  export async function handleListAuthKeys({ req, res, opts, query }) {
63
60
  const activeRaw = query.get('active');
64
61
  let active = true;
@@ -78,9 +75,8 @@ export async function handleListAuthKeys({ req, res, opts, query }) {
78
75
  sendJson(res, 200, page.items.map((r) => r.key));
79
76
  return;
80
77
  }
81
- // DELETE /v1/auth/keys/:keyId — revoke. Missing or cross-tenant keys are 404 (no info leak); a member
82
- // is 403 on any key but its own key or, signed in through the resolver, the keys it minted.
83
- // 200 with the body rather than 204 so the caller sees revokedAt.
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
+ // minted. Returns 200 with the body rather than 204 so the caller sees revokedAt.
84
80
  export async function handleRevokeAuthKey({ req, res, opts }, keyMatch) {
85
81
  validateIdSegment(keyMatch.keyId, 'key id');
86
82
  const ctx = await buildContextWithAuth(req, opts);
@@ -124,9 +120,7 @@ export async function handleRejectQuarantine({ req, res, opts }, quarantineRejec
124
120
  sendJson(res, 200, { rejected: quarantineRejectMatch.id });
125
121
  return;
126
122
  }
127
- // GET /v1/audit?op=&since=&limit=&cursor=: read audit events. All three filters
128
- // validated at the route boundary so an invalid value lands a 400 before
129
- // we hit the DB.
123
+ // 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.
130
124
  export async function handleListAudit({ req, res, opts, query }) {
131
125
  const opRaw = query.get('op');
132
126
  let op;
@@ -1,9 +1,10 @@
1
1
  // /v1/customer-notes routes.
2
- import { CUSTOMER_NOTE, MAX_CUSTOMER_LEN } from '../../objects/customer-notes.js';
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
4
  import { buildContextWithAuth } from '../auth.js';
5
5
  import { parseJsonBody } from '../validation.js';
6
- import { closeRoute, getRoute, listRoute, optionalString, requiredString, saveFor, supersedeRoute } from './object-routes.js';
6
+ import { saveFor } from '../../api/objects.js';
7
+ import { closeRoute, getRoute, listRoute, optionalString, requiredString, supersedeRoute } from './object-routes.js';
7
8
  const noteRoutes = {
8
9
  noun: 'customer note',
9
10
  field: 'note',
@@ -12,22 +13,16 @@ const noteRoutes = {
12
13
  filterParam: 'customer',
13
14
  revise: (body) => {
14
15
  const note = requiredString(body, 'note', { max: 8192 });
15
- const changeSummary = optionalString(body, 'changeSummary', 4096);
16
+ const changeSummary = optionalString(body, 'changeSummary', MAX_CHANGE_SUMMARY_LEN);
16
17
  return (existing, id) => ({ customer: existing.customer, note, changeSummary, supersedesNoteId: id });
17
18
  },
18
19
  };
19
- // ── customer_note routes ──
20
- //
21
- // 5 routes (no assembler/refresh): POST /v1/customer-notes (new; body customer +
22
- // note), GET /v1/customer-notes (list; status + customer filter; shared
23
- // parseListLimit), GET /v1/customer-notes/:id, POST /v1/customer-notes/:id/supersede,
24
- // POST /v1/customer-notes/:id/close. DoS caps: customer 256, note 8192,
25
- // changeSummary 4096. The store validates + throws; the boundary maps validation ->
26
- // 400, not-found -> 404, not-active -> 409. Mirrors /v1/project-briefs.
20
+ // Routes: customer notes under /v1/customer-notes (create, list, show, supersede, close), mirroring /v1/project-briefs. The store validates and throws;
21
+ // the boundary maps validation -> 400, not-found -> 404, not-active -> 409.
27
22
  export async function handleCreateCustomerNote(rr) {
28
23
  const ctx = await buildContextWithAuth(rr.req, rr.opts);
29
24
  const body = await parseJsonBody(rr.req, ctx);
30
- const customerNote = await saveFor(rr, CUSTOMER_NOTE, ctx.tenantId, ctx.actor.subject, {
25
+ const customerNote = await saveFor(ctx, CUSTOMER_NOTE, {
31
26
  customer: requiredString(body, 'customer', { max: MAX_CUSTOMER_LEN }),
32
27
  note: requiredString(body, 'note', { max: 8192 }),
33
28
  });
@@ -4,10 +4,11 @@ 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
6
  import { buildContextWithAuth } from '../auth.js';
7
- import { parseJsonBody } from '../validation.js';
8
- import { closeRoute, getRoute, listRoute, optionalString, requiredString, saveFor } from './object-routes.js';
7
+ import { MAX_SHORT_FIELD_LEN, parseJsonBody } from '../validation.js';
8
+ import { saveFor } from '../../api/objects.js';
9
+ import { closeRoute, getRoute, listRoute, optionalString, requiredString } from './object-routes.js';
9
10
  const decisionRoutes = { noun: 'decision', field: 'decision', listField: 'decisions', object: DECISION };
10
- const TEXT = { max: 4096, untrimmed: true };
11
+ const TEXT = { max: MAX_SHORT_FIELD_LEN, untrimmed: true };
11
12
  function supersededId(body) {
12
13
  const raw = body['supersedesDecisionId'];
13
14
  if (raw === undefined || raw === null)
@@ -17,27 +18,19 @@ function supersededId(body) {
17
18
  }
18
19
  return raw;
19
20
  }
20
- // ── decisions (first-class object) ──
21
- //
22
- // 5 routes: POST /v1/decisions (create, optional supersedesDecisionId),
23
- // GET /v1/decisions (list, status filter), GET /v1/decisions/:id (show),
24
- // POST /v1/decisions/:id/supersede (create a successor + supersede :id),
25
- // POST /v1/decisions/:id/close (retire). Bearer-authed + tenant-scoped via
26
- // buildContextWithAuth. status validated against VALID_DECISION_STATES.
27
- // DoS caps: text 4096, context 4096. The HTTP surface is
28
- // new (no legacy --supersedes <memory-id> constraint), so it supersedes by
29
- // table id and never weakens a memory mirror.
21
+ // Routes: /v1/decisions (create, list, show, supersede, close), Bearer-authed and tenant-scoped; status is validated against VALID_DECISION_STATES.
22
+ // The HTTP surface has no legacy --supersedes <memory-id> constraint, so it supersedes by table id and never weakens a memory mirror.
30
23
  export async function handleCreateDecision(rr) {
31
24
  const { req, res } = rr;
32
25
  const ctx = await buildContextWithAuth(req, rr.opts);
33
26
  const body = await parseJsonBody(req, ctx);
34
27
  const write = {
35
28
  decisionText: requiredString(body, 'text', TEXT),
36
- context: optionalString(body, 'context', 4096),
29
+ context: optionalString(body, 'context', MAX_SHORT_FIELD_LEN),
37
30
  supersedesDecisionId: supersededId(body),
38
31
  };
39
32
  try {
40
- const decision = await saveFor(rr, DECISION, ctx.tenantId, ctx.actor.subject, write);
33
+ const decision = await saveFor(ctx, DECISION, write);
41
34
  sendJson(res, 201, { decision });
42
35
  }
43
36
  catch (e) {
@@ -55,9 +48,9 @@ export async function handleSupersedeDecision(rr, match) {
55
48
  const oldId = parseInt(match[1], 10);
56
49
  const ctx = await buildContextWithAuth(rr.req, rr.opts);
57
50
  const body = await parseJsonBody(rr.req, ctx);
58
- const decision = await saveFor(rr, DECISION, ctx.tenantId, ctx.actor.subject, {
51
+ const decision = await saveFor(ctx, DECISION, {
59
52
  decisionText: requiredString(body, 'text', TEXT),
60
- context: optionalString(body, 'context', 4096),
53
+ context: optionalString(body, 'context', MAX_SHORT_FIELD_LEN),
61
54
  supersedesDecisionId: oldId,
62
55
  });
63
56
  sendJson(rr.res, 201, { decision });
@@ -3,9 +3,10 @@ import { INCIDENT, openIncident, resolveOpenIncident } from '../../objects/incid
3
3
  import { HttpError, sendJson } from '../../util/http-util.js';
4
4
  import { NotFoundError } from '../../core/api-errors.js';
5
5
  import { buildContextWithAuth } from '../auth.js';
6
- import { parseJsonBody } from '../validation.js';
6
+ import { MAX_SHORT_FIELD_LEN, parseJsonBody } from '../validation.js';
7
7
  import { isJsonString } from '../../util/json.js';
8
- import { closeRoute, getRoute, listRoute, objectsOf, optionalString, requiredString } from './object-routes.js';
8
+ import { objectsOf } from '../../api/objects.js';
9
+ import { closeRoute, getRoute, listRoute, optionalString, requiredString } from './object-routes.js';
9
10
  const MAX_LINKED_MEMORY_IDS = 256;
10
11
  const incidentRoutes = { noun: 'incident', field: 'incident', listField: 'incidents', object: INCIDENT };
11
12
  function linkedMemoryIds(body) {
@@ -18,34 +19,25 @@ function linkedMemoryIds(body) {
18
19
  if (raw.length > MAX_LINKED_MEMORY_IDS) {
19
20
  throw new HttpError(400, `linkedMemoryIds exceeds ${MAX_LINKED_MEMORY_IDS}-item cap`);
20
21
  }
21
- const isValidMemoryId = (item) => isJsonString(item) && item.length > 0 && item.length <= 4096;
22
+ const isValidMemoryId = (item) => isJsonString(item) && item.length > 0 && item.length <= MAX_SHORT_FIELD_LEN;
22
23
  if (!raw.every(isValidMemoryId)) {
23
- throw new HttpError(400, 'each linkedMemoryIds entry must be a non-empty string <= 4096 chars');
24
+ throw new HttpError(400, `each linkedMemoryIds entry must be a non-empty string <= ${MAX_SHORT_FIELD_LEN} chars`);
24
25
  }
25
26
  return raw;
26
27
  }
27
- // ── incidents (first-class object) ──
28
- //
29
- // 5 routes: POST /v1/incidents (open; body text + context + linkedMemoryIds[]),
30
- // GET /v1/incidents (list, status filter), GET /v1/incidents/:id (show),
31
- // POST /v1/incidents/:id/resolve (open -> resolved; body resolutionText),
32
- // POST /v1/incidents/:id/close (open|resolved -> closed). Bearer-authed +
33
- // tenant-scoped via buildContextWithAuth. status validated against
34
- // VALID_INCIDENT_STATES. DoS caps: text 4096, context 4096, resolutionText
35
- // 4096. Mirrors /v1/decisions; lifecycle is
36
- // open->resolved->closed (no supersede), so linkedMemoryIds replaces
37
- // supersedesDecisionId on create.
28
+ // Routes: /v1/incidents (open, list, show, resolve, close), mirroring /v1/decisions; status is validated against VALID_INCIDENT_STATES.
29
+ // Lifecycle is open->resolved->closed (no supersede), so linkedMemoryIds replaces supersedesDecisionId on create.
38
30
  export async function handleCreateIncident(rr) {
39
31
  const { req, res, opts } = rr;
40
32
  const ctx = await buildContextWithAuth(req, opts);
41
33
  const body = await parseJsonBody(req, ctx);
42
34
  const write = {
43
- incidentText: requiredString(body, 'text', { max: 4096, untrimmed: true }),
44
- context: optionalString(body, 'context', 4096),
35
+ incidentText: requiredString(body, 'text', { max: MAX_SHORT_FIELD_LEN, untrimmed: true }),
36
+ context: optionalString(body, 'context', MAX_SHORT_FIELD_LEN),
45
37
  linkedMemoryIds: linkedMemoryIds(body),
46
38
  };
47
39
  try {
48
- const incident = await openIncident(objectsOf(rr), opts.hippoRoot, ctx.tenantId, write, ctx.actor.subject);
40
+ const incident = await openIncident(objectsOf(ctx), opts.hippoRoot, ctx.tenantId, write, ctx.actor.subject);
49
41
  sendJson(res, 201, { incident });
50
42
  }
51
43
  catch (e) {
@@ -62,8 +54,8 @@ export async function handleResolveIncident(rr, match) {
62
54
  const { req, res, opts } = rr;
63
55
  const id = parseInt(match[1], 10);
64
56
  const ctx = await buildContextWithAuth(req, opts);
65
- const resolutionText = requiredString(await parseJsonBody(req, ctx), 'resolutionText', { max: 4096 });
66
- const incident = await resolveOpenIncident(objectsOf(rr), ctx.tenantId, id, resolutionText, ctx.actor.subject);
57
+ const resolutionText = requiredString(await parseJsonBody(req, ctx), 'resolutionText', { max: MAX_SHORT_FIELD_LEN });
58
+ const incident = await resolveOpenIncident(objectsOf(ctx), ctx.tenantId, id, resolutionText, ctx.actor.subject);
67
59
  sendJson(res, 200, { incident });
68
60
  }
69
61
  export function handleCloseIncident(rr, match) {
@@ -1,9 +1,9 @@
1
1
  // Memory write routes: create, graph, archive, supersede, promote, forget, outcome, sleep.
2
+ import { graphRows } from '../../api/graph.js';
2
3
  import { archiveRaw, forget, outcome, outcomeForLastRecall, promote, remember, supersede } from '../../api/index.js';
3
4
  import { graphModelOf } from '../../graph/view.js';
4
5
  import { MAX_ENTITY_NAME_LEN } from '../../store/graph-rows.js';
5
6
  import { HttpError, sendJson } from '../../util/http-util.js';
6
- import { requireGroup } from '../../store/port.js';
7
7
  import { assertCrossTenantAdmin, buildContextWithAuth, isLoopback } from '../auth.js';
8
8
  import { sleepInChild } from '../sleep-offload.js';
9
9
  import { getCallerProject, getString, getStringArray, isSetMember, parseJsonBody, parseListLimit, validateIdSegment } from '../validation.js';
@@ -53,8 +53,7 @@ export async function handleGetGraph({ req, res, opts, query }) {
53
53
  }
54
54
  const limit = parseListLimit(query.get('limit'));
55
55
  const ctx = await buildContextWithAuth(req, opts);
56
- const { role, scopes, owner } = ctx.actor;
57
- const rows = await requireGroup(opts.store, 'graphReads').graphRows(ctx.tenantId, { entity: entityRaw ?? undefined, limit, reader: { role, scopes, owner } });
56
+ const rows = await graphRows(ctx, { entity: entityRaw ?? undefined, limit });
58
57
  sendJson(res, 200, graphModelOf(rows));
59
58
  return;
60
59
  }
@@ -97,12 +96,8 @@ export async function handleForgetMemory({ req, res, opts }, idMatch) {
97
96
  sendJson(res, 200, result);
98
97
  return;
99
98
  }
100
- // POST /v1/outcome — apply a positive/negative outcome to memory ids.
101
- // Body: {ids?: string[], good: boolean}. If ids omitted, falls back to
102
- // the last-recall path (api.outcomeForLastRecall); returned shape is
103
- // {applied, ids} in that case so callers can disambiguate "no recent
104
- // recall" from "all ids skipped". Each applied id writes one audit_log
105
- // row (op='outcome', actor from Bearer).
99
+ // POST /v1/outcome: applies a positive/negative outcome to memory ids; without ids it falls back to the last-recall path (api.outcomeForLastRecall)
100
+ // and returns {applied, ids}, so callers can tell "no recent recall" from "all ids skipped". Each applied id writes one audit_log row (op='outcome').
106
101
  export async function handleApplyOutcome({ req, res, opts }) {
107
102
  const ctx = await buildContextWithAuth(req, opts);
108
103
  const body = await parseJsonBody(req, ctx);
@@ -120,10 +115,8 @@ export async function handleApplyOutcome({ req, res, opts }) {
120
115
  if (!idsRaw.every(isNonEmptyId)) {
121
116
  throw new HttpError(400, 'ids must be an array of non-empty strings');
122
117
  }
123
- // DoS cap on ids.length. Each id triggers ~3 DB ops (readEntry +
124
- // writeEntry + appendAuditEvent). N=1000 keeps per-request work bounded
125
- // to sub-second wall time on SQLite hot path. Cap BEFORE buildContextWithAuth
126
- // so attack traffic doesn't pay the api-key lookup cost.
118
+ // DoS cap on ids.length (each id costs ~3 DB ops): N=1000 keeps per-request work sub-second on SQLite.
119
+ // Checked BEFORE buildContextWithAuth so attack traffic does not pay the api-key lookup.
127
120
  if (idsRaw.length > 1000) {
128
121
  throw new HttpError(400, 'ids exceeds 1000-id cap');
129
122
  }
@@ -142,29 +135,16 @@ export async function handleApplyOutcome({ req, res, opts }) {
142
135
  }
143
136
  return;
144
137
  }
145
- // POST /v1/sleep — host-wide consolidation pipeline (consolidate + dedup +
146
- // audit + share + ambient). serve() refuses non-loopback hosts at boot, AND
147
- // this per-request loopback assertion makes the host-wide semantic fail-
148
- // closed regardless of any future serve() boot-config change. Body:
149
- // {dry_run?, no_share?}. Returns SleepResult JSON.
150
- //
151
- // Tenant scope (Episode A follow-up tracked in TODOS.md): api.sleep operates
152
- // on the WHOLE hippoRoot (cross-tenant by design, matching CLI cmdSleep).
153
- // The loopback-only guard is the trust boundary today. Future non-loopback
154
- // serving must also zero the cross-tenant counters for other tenants.
138
+ // POST /v1/sleep: host-wide consolidation (consolidate, dedup, audit, share, ambient); api.sleep spans the WHOLE hippoRoot, cross-tenant by design, so the
139
+ // loopback-only guard is the trust boundary; non-loopback serving must also zero cross-tenant counters. Body {dry_run?, no_share?}; returns SleepResult JSON.
155
140
  export async function handleSleep({ req, res, opts }) {
156
- // Defensive per-request loopback guard. Uses the canonical isLoopback()
157
- // helper above so any future extension (additional mapped/IPv6 forms,
158
- // NAT64 prefixes) flows through without drift. serve()'s boot-time host
159
- // check is the primary trust boundary; this is belt-and-suspenders.
141
+ // Defensive per-request loopback guard using isLoopback(), so future mapped/IPv6/NAT64 forms flow through without drift;
142
+ // serve()'s boot-time host check is the primary trust boundary.
160
143
  if (!isLoopback(req.socket.remoteAddress)) {
161
144
  throw new HttpError(403, '/v1/sleep is loopback-only (host-wide consolidation; see CHANGELOG v1.11.4)');
162
145
  }
163
- // Admin-role gate. Forward-defensive: exists today
164
- // under loopback-only enforcement (loopback fallback is admin by default;
165
- // any Bearer-authed caller now carries an explicit role from the api_keys
166
- // row). When non-loopback serving lands, this gate is the actual auth
167
- // boundary on host-wide sleep.
146
+ // Admin-role gate, forward-defensive: loopback fallback is admin by default and Bearer callers carry an explicit role from the api_keys row;
147
+ // when non-loopback serving lands, this gate is the real auth boundary on host-wide sleep.
168
148
  const sleepCtx = await buildContextWithAuth(req, opts);
169
149
  // Sleep consolidates every tenant under hippoRoot, so it is a cross-tenant action.
170
150
  assertCrossTenantAdmin(sleepCtx, '/v1/sleep');
@@ -1,7 +1,6 @@
1
1
  import { type JsonValue } from '../../util/json.js';
2
2
  import type { ObjectDescriptor, SavableDescriptor } from '../../objects/descriptor.js';
3
3
  import type { ObjectByKind, ObjectKind, SavableKind } from '../../store/object-types.js';
4
- import { type Objects } from '../../store/port.js';
5
4
  import type { RouteRequest } from '../types.js';
6
5
  export interface ObjectRouteConfig<K extends ObjectKind> {
7
6
  /** Names the object in the 404 text, e.g. "customer note". */
@@ -29,10 +28,6 @@ export interface RequiredStringRule {
29
28
  }
30
29
  export declare function requiredString(body: Record<string, JsonValue>, key: string, rule: RequiredStringRule): string;
31
30
  export declare function optionalString(body: Record<string, JsonValue>, key: string, max: number): string | undefined;
32
- /** The request store's object group; the dispatcher has already answered 501 for a store without it. */
33
- export declare function objectsOf({ opts }: RouteRequest): Objects;
34
- /** Saves through the request's store as the authenticated caller. */
35
- export declare function saveFor<K extends SavableKind, W>(rr: RouteRequest, d: SavableDescriptor<K, W>, tenantId: string, actor: string, write: W): Promise<ObjectByKind[K]>;
36
31
  export declare function listRoute<K extends ObjectKind>(cfg: ObjectRouteConfig<K>, rr: RouteRequest): Promise<void>;
37
32
  export declare function getRoute<K extends ObjectKind>(cfg: ObjectRouteConfig<K>, rr: RouteRequest, match: RegExpMatchArray): Promise<void>;
38
33
  export declare function closeRoute<K extends ObjectKind>(cfg: ObjectRouteConfig<K>, rr: RouteRequest, match: RegExpMatchArray): Promise<void>;
@@ -1,8 +1,8 @@
1
1
  // List, get, close and supersede handlers shared by the typed-object routes; each route file passes one config.
2
2
  import { HttpError, sendJson } from '../../util/http-util.js';
3
3
  import { isJsonString } from '../../util/json.js';
4
- import { closeObject, listObjects, objectById, saveObject } from '../../objects/lifecycle.js';
5
- import { requireGroup } from '../../store/port.js';
4
+ import { objectsOf, saveFor } from '../../api/objects.js';
5
+ import { closeObject, listObjects, objectById } from '../../objects/lifecycle.js';
6
6
  import { buildContextWithAuth } from '../auth.js';
7
7
  import { byCreatedAt, pageOf, parseCursor } from '../cursor.js';
8
8
  import { isSetMember, parseJsonBody, parseListLimit } from '../validation.js';
@@ -26,14 +26,6 @@ export function optionalString(body, key, max) {
26
26
  throw new HttpError(400, `${key} exceeds ${max}-character cap`);
27
27
  return value;
28
28
  }
29
- /** The request store's object group; the dispatcher has already answered 501 for a store without it. */
30
- export function objectsOf({ opts }) {
31
- return requireGroup(opts.store, 'objects');
32
- }
33
- /** Saves through the request's store as the authenticated caller. */
34
- export function saveFor(rr, d, tenantId, actor, write) {
35
- return saveObject(objectsOf(rr), d, { hippoRoot: rr.opts.hippoRoot, tenantId, actor }, write);
36
- }
37
29
  async function found(cfg, objects, tenantId, id) {
38
30
  const row = await objectById(objects, cfg.object, tenantId, id);
39
31
  if (!row)
@@ -56,25 +48,25 @@ export async function listRoute(cfg, rr) {
56
48
  }
57
49
  listOpts.status = status;
58
50
  }
59
- const page = pageOf(await listObjects(objectsOf(rr), cfg.object, ctx.tenantId, listOpts), limit, byCreatedAt);
51
+ const page = pageOf(await listObjects(objectsOf(ctx), cfg.object, ctx.tenantId, listOpts), limit, byCreatedAt);
60
52
  sendJson(res, 200, { [cfg.listField]: page.items, next_cursor: page.nextCursor });
61
53
  }
62
54
  export async function getRoute(cfg, rr, match) {
63
55
  const id = parseInt(match[1], 10);
64
56
  const ctx = await buildContextWithAuth(rr.req, rr.opts);
65
- sendJson(rr.res, 200, { [cfg.field]: await found(cfg, objectsOf(rr), ctx.tenantId, id) });
57
+ sendJson(rr.res, 200, { [cfg.field]: await found(cfg, objectsOf(ctx), ctx.tenantId, id) });
66
58
  }
67
59
  export async function closeRoute(cfg, rr, match) {
68
60
  const id = parseInt(match[1], 10);
69
61
  const ctx = await buildContextWithAuth(rr.req, rr.opts);
70
- sendJson(rr.res, 200, { [cfg.field]: await closeObject(objectsOf(rr), cfg.object, ctx.tenantId, id, ctx.actor.subject) });
62
+ sendJson(rr.res, 200, { [cfg.field]: await closeObject(objectsOf(ctx), cfg.object, ctx.tenantId, id, ctx.actor.subject) });
71
63
  }
72
64
  export async function supersedeRoute(cfg, rr, match) {
73
65
  const id = parseInt(match[1], 10);
74
66
  const ctx = await buildContextWithAuth(rr.req, rr.opts);
75
67
  const successor = cfg.revise(await parseJsonBody(rr.req, ctx));
76
- const existing = await found(cfg, objectsOf(rr), ctx.tenantId, id);
77
- const saved = await saveFor(rr, cfg.object, ctx.tenantId, ctx.actor.subject, successor(existing, id));
68
+ const existing = await found(cfg, objectsOf(ctx), ctx.tenantId, id);
69
+ const saved = await saveFor(ctx, cfg.object, successor(existing, id));
78
70
  sendJson(rr.res, 200, { [cfg.field]: saved });
79
71
  }
80
72
  //# sourceMappingURL=object-routes.js.map