@humwick/server 0.130.2 → 0.130.4

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 (587) hide show
  1. package/bundled/prompts/manifest.json +1 -0
  2. package/bundled/prompts/task.hydration.md +1 -1
  3. package/bundled/roles/builder.md +1 -1
  4. package/docs/guides/sub-coordinators.md +12 -5
  5. package/docs/guides/tools-coordination.md +1 -0
  6. package/docs/guides/workflows.md +16 -0
  7. package/docs/openapi.json +175 -4
  8. package/package.json +8 -7
  9. package/public/assets/{MessageBubble-BWLqMGKG.js → MessageBubble-BOkmEvgZ.js} +2 -2
  10. package/public/assets/{PlanGraphPanel-3TlIQpjk.js → PlanGraphPanel-BkaDJp0r.js} +2 -2
  11. package/public/assets/{TraceView-Wimm0Czv.js → TraceView-B99Uu2yT.js} +2 -2
  12. package/public/assets/{VaultDecisionCard-Bau_vNax.js → VaultDecisionCard-DUIfTTFF.js} +2 -2
  13. package/public/assets/{activity-CrSurn-r.js → activity-ZIX0N1El.js} +2 -2
  14. package/public/assets/{agent-lineage-CknAJKcb.js → agent-lineage-kq9TPYFw.js} +2 -2
  15. package/public/assets/agent-liveness-DSQY1Aa_.js +2 -0
  16. package/public/assets/{agent-name-DKbiIhBo.js → agent-name-C_7F4DFR.js} +2 -2
  17. package/public/assets/{agent-profiles-DwF6EQ4R.js → agent-profiles-BNOcrGcY.js} +2 -2
  18. package/public/assets/{agent-project-badge-Diz4Sjjb.js → agent-project-badge-B6yAh70a.js} +2 -2
  19. package/public/assets/{agent-tools-CRhpRQza.js → agent-tools-CcrhmOzF.js} +2 -2
  20. package/public/assets/{agent-type-icon-LrUEU3lB.js → agent-type-icon-CXSLrKiq.js} +2 -2
  21. package/public/assets/{agents-B-v66C-4.js → agents-DIZG97_X.js} +2 -2
  22. package/public/assets/{analytics-BqCb-LKi.js → analytics-rasmoPjR.js} +2 -2
  23. package/public/assets/{artifact-viewer-kf4pGuAR.js → artifact-viewer-tBpTrEPb.js} +2 -2
  24. package/public/assets/{artifacts-DJevXcvg.js → artifacts-BNkrynP1.js} +2 -2
  25. package/public/assets/{artifacts-D32zyw4Y.js → artifacts-Bjq-ZyBA.js} +2 -2
  26. package/public/assets/{automation-BpRajx_5.js → automation-Cx422L3L.js} +2 -2
  27. package/public/assets/{backlog-D_UjTdzX.js → backlog-RuFQ8PUu.js} +2 -2
  28. package/public/assets/{busy-health-N3VEh4ao.js → busy-health-BvNQ9g5x.js} +2 -2
  29. package/public/assets/{channels-9jopungq.js → channels-DNbKAS0I.js} +2 -2
  30. package/public/assets/{chat-CSXrTB3C.js → chat-Bp3Fx73h.js} +3 -3
  31. package/public/assets/{code-preview-p1NnmqK0.js → code-preview-KIRZA6cz.js} +2 -2
  32. package/public/assets/{connectors-CZdqlDNL.js → connectors-k7vO4vvc.js} +2 -2
  33. package/public/assets/{cosmos-BgdsE4dG.js → cosmos-BuVoq1aw.js} +2 -2
  34. package/public/assets/{crate-agent-30aXTKva.js → crate-agent-irqlt4NH.js} +2 -2
  35. package/public/assets/{dependency-graph-B_jvw2GE.js → dependency-graph-D_rurNBz.js} +2 -2
  36. package/public/assets/{deploys-DoN6bAb5.js → deploys-yzZqIpxE.js} +2 -2
  37. package/public/assets/{display-BrKWFGJz.js → display-BemJG1f_.js} +2 -2
  38. package/public/assets/{files-D6XEu8Ng.js → files-DXWcrxOz.js} +2 -2
  39. package/public/assets/{fleet-C7jQWbw2.js → fleet-TEuOCZXE.js} +2 -2
  40. package/public/assets/{fleet-metrics-BiDs3MLx.js → fleet-metrics-BGoDAFI-.js} +2 -2
  41. package/public/assets/{formatted-body-impl-DyG0WsQS.js → formatted-body-impl-BB9eq9nN.js} +2 -2
  42. package/public/assets/{formatted-body-CQBwc1fb.js → formatted-body-qy2nTD_1.js} +3 -3
  43. package/public/assets/{host-providers-dialog-DKaW31dN.js → host-providers-dialog-BGPHxuwT.js} +2 -2
  44. package/public/assets/{index-CRHep3M9.js → index-BC5XZc0z.js} +5 -5
  45. package/public/assets/{index-MYAIwYTX.css → index-D8Rp9pz2.css} +1 -1
  46. package/public/assets/{insights-CzrtkpOE.js → insights-C-KQG0h-.js} +2 -2
  47. package/public/assets/{integrations-aVsUmigt.js → integrations-ClPRPyKU.js} +2 -2
  48. package/public/assets/{issue-ref-chip-BnSlM_95.js → issue-ref-chip-DbH8J9Ak.js} +2 -2
  49. package/public/assets/{log-viewer-qe7Ee47Z.js → log-viewer-PH4zVn24.js} +2 -2
  50. package/public/assets/{maintenance-NfjqFZAk.js → maintenance-5lV43uTL.js} +2 -2
  51. package/public/assets/{managed-agents-BiH0HgaE.js → managed-agents-BdgU35aX.js} +2 -2
  52. package/public/assets/{markdown-preview-BixaT3Xr.js → markdown-preview-DNT4OUwQ.js} +3 -3
  53. package/public/assets/{markdown-preview-impl-DNGlWIS3.js → markdown-preview-impl-CnCcPy0A.js} +2 -2
  54. package/public/assets/{memory-DNsCl6UX.js → memory--GgWH1gH.js} +2 -2
  55. package/public/assets/{messages-DopU1L1X.js → messages-CzFl_97O.js} +2 -2
  56. package/public/assets/{orchestrators-Cq7ckLQT.js → orchestrators-CVvJVkzv.js} +2 -2
  57. package/public/assets/overview-DsrZvE86.js +2 -0
  58. package/public/assets/{pairs-DE_m6rrE.js → pairs-D2d0d2py.js} +2 -2
  59. package/public/assets/{profile-icon-B3rfh77F.js → profile-icon-_SMuSbYF.js} +2 -2
  60. package/public/assets/{project-profile-instructions-DQWZE0kA.js → project-profile-instructions-Apn15y43.js} +2 -2
  61. package/public/assets/{projects-Br2Bd2h1.js → projects-D3B6kto5.js} +2 -2
  62. package/public/assets/{prompt-settings-BKvFsvGD.js → prompt-settings-DnPB6Ez6.js} +2 -2
  63. package/public/assets/provider-display-bWwiuedV.js +2 -0
  64. package/public/assets/{registry-Cw38_6gW.js → registry-D790cIhN.js} +2 -2
  65. package/public/assets/run-card-BCro-Odj.js +2 -0
  66. package/public/assets/{secrets-DxI-vzSq.js → secrets-CVXvfvbU.js} +2 -2
  67. package/public/assets/{service-access-settings-D9s_VJbd.js → service-access-settings-D3Xxqb9w.js} +2 -2
  68. package/public/assets/{settings-CKZTVGIx.js → settings-CoUC7Jci.js} +2 -2
  69. package/public/assets/shipping-D_Lp9n_J.js +2 -0
  70. package/public/assets/{stats-DyUBKyWI.js → stats-CXiNjB9S.js} +2 -2
  71. package/public/assets/{status-dot-DYqlEUhJ.js → status-dot-C703YIy1.js} +2 -2
  72. package/public/assets/store-BgDALGIX.js +9 -0
  73. package/public/assets/{tasks-Bh7Chr4-.js → tasks-DO0WpJCx.js} +2 -2
  74. package/public/assets/{teams-bowI7qVY.js → teams-FGkdi_tL.js} +2 -2
  75. package/public/assets/{terminal-viewer-BgVLHdsU.js → terminal-viewer-Dh8dSC7e.js} +3 -3
  76. package/public/assets/{terminal-viewer-impl-Dz22UPy6.js → terminal-viewer-impl-B5Zriip3.js} +2 -2
  77. package/public/assets/{use-agent-terminal-DSQQ-iY7.js → use-agent-terminal-CRUVDvLC.js} +2 -2
  78. package/public/assets/{use-decisions-samMQhQf.js → use-decisions-CoaqRh4l.js} +2 -2
  79. package/public/assets/{use-project-list-59LheVP7.js → use-project-list-X-BgT53X.js} +2 -2
  80. package/public/assets/{use-selectors-BZa25xEB.js → use-selectors-0X_UZkk-.js} +2 -2
  81. package/public/assets/{user-avatar-DHxlvUvV.js → user-avatar-CkLhVBGu.js} +2 -2
  82. package/public/assets/{versioned-prompt-editor-BmED5DnJ.js → versioned-prompt-editor-BkTi7eim.js} +2 -2
  83. package/public/assets/{work-queue-CSvpuNLz.js → work-queue-DDOLEyKo.js} +2 -2
  84. package/public/assets/{workflow-marker-DAZcj5qt.js → workflow-marker-CzOsB8BM.js} +2 -2
  85. package/public/assets/{workflows-DdmxRSyg.js → workflows-DJilvmRM.js} +2 -2
  86. package/public/assets/{workspaces-BxeKfVuF.js → workspaces-CHcfbLR3.js} +2 -2
  87. package/public/cosmos.html +4 -4
  88. package/public/index.html +29 -29
  89. package/runner/plugins/claude/.claude-plugin/plugin.json +1 -1
  90. package/runner/src/adapters/claude-api.ts +1 -1
  91. package/runner/src/adapters/claude-headless.ts +1 -1
  92. package/runner/src/adapters/claude-launch-exit.ts +26 -0
  93. package/runner/src/adapters/claude-session-probe.ts +1 -1
  94. package/runner/src/adapters/claude-tmux.ts +38 -10
  95. package/runner/src/adapters/claude.ts +28 -75
  96. package/runner/src/adapters/provider-quota-harvest.ts +20 -71
  97. package/runner/src/agent-runner/lifecycle-milestones.ts +2 -2
  98. package/runner/src/agent-runner/liveness-exchange.ts +104 -0
  99. package/runner/src/claude-system-prompt.ts +49 -0
  100. package/runner/src/launch-assembly.ts +14 -10
  101. package/runner/src/profile-home.ts +1 -1
  102. package/runner/src/project-instructions.ts +2 -2
  103. package/runner/src/provider-command.ts +18 -0
  104. package/runner/src/provider-setup.ts +3 -2
  105. package/runner/src/runner-core.ts +14 -15
  106. package/runner/src/runner-helpers.ts +0 -8
  107. package/runner/src/session-scratch.ts +6 -2
  108. package/runner/test-support/provisioning-context.ts +4 -2
  109. package/runner/test-support/runner-harness.ts +2 -1
  110. package/scripts/steward-policy-cleanup.ts +2 -2
  111. package/src/agent-authz.ts +2 -2
  112. package/src/agent-drive-arbiter.ts +3 -8
  113. package/src/agent-exit-cleanup-async.ts +2 -2
  114. package/src/agent-exit-cleanup.ts +23 -19
  115. package/src/agent-lifecycle-events.ts +112 -85
  116. package/src/agent-ref.ts +3 -2
  117. package/src/agent-status-events.ts +43 -14
  118. package/src/auth/login-link.ts +35 -0
  119. package/src/auth/password.ts +4 -3
  120. package/src/auth/session-refresh.ts +1 -1
  121. package/src/auth/session.test-fixture.ts +2 -2
  122. package/src/auth/session.ts +7 -7
  123. package/src/automations/crud.ts +8 -81
  124. package/src/automations/dispatch.ts +33 -58
  125. package/src/automations/index.ts +1 -1
  126. package/src/automations/persistence.ts +65 -0
  127. package/src/automations/precheck.ts +18 -16
  128. package/src/automations/read-async.ts +11 -0
  129. package/src/automations/reconcile.ts +30 -39
  130. package/src/automations/storage.ts +33 -10
  131. package/src/automations/write-async.ts +33 -0
  132. package/src/branch-land-notice.ts +3 -3
  133. package/src/branch-landed.ts +25 -23
  134. package/src/bus.ts +21 -19
  135. package/src/cd-deploy-notify.ts +2 -2
  136. package/src/ci/notifications.ts +4 -4
  137. package/src/ci/root-missing.ts +3 -3
  138. package/src/ci/transition-notify.ts +5 -5
  139. package/src/claude-app/oauth-http.ts +3 -3
  140. package/src/cli/index.ts +7 -1
  141. package/src/cli/init.ts +53 -0
  142. package/src/cli/orchestrator.ts +1 -7
  143. package/src/cli/setup.ts +6 -4
  144. package/src/cli/workflow.ts +12 -3
  145. package/src/commands-db.ts +4 -4
  146. package/src/commands-write.ts +8 -0
  147. package/src/compaction-watch.ts +10 -8
  148. package/src/config-store-write.ts +2 -2
  149. package/src/config.ts +4 -11
  150. package/src/connectors.ts +10 -8
  151. package/src/context-advisory.ts +22 -21
  152. package/src/db/agent-action-write.ts +36 -0
  153. package/src/db/agent-control-async.ts +9 -0
  154. package/src/db/agent-control-write.ts +49 -0
  155. package/src/db/agent-effort-observe.ts +22 -0
  156. package/src/db/agent-meta.ts +14 -3
  157. package/src/db/agents-async.ts +8 -5
  158. package/src/db/agents.ts +15 -12
  159. package/src/db/auth-identities.ts +2 -2
  160. package/src/db/automation-read-ops.ts +11 -0
  161. package/src/db/automation-write-ops.ts +46 -0
  162. package/src/db/blackboard.ts +2 -2
  163. package/src/db/browser-session-write.ts +32 -0
  164. package/src/db/bus-outbox-write-ops.ts +3 -1
  165. package/src/db/checkout-refresh-schema.ts +47 -0
  166. package/src/db/checkout-refresh.ts +5 -5
  167. package/src/db/command-agent-profile-backfill.ts +39 -0
  168. package/src/db/command-agent-profiles.ts +5 -1
  169. package/src/db/command-retention.ts +126 -0
  170. package/src/db/commands-planner-stats.test-fixture.ts +8 -0
  171. package/src/db/commands-read-ops.ts +16 -0
  172. package/src/db/connection.ts +1 -182
  173. package/src/db/context-carry-write.ts +74 -0
  174. package/src/db/continuations.ts +4 -4
  175. package/src/db/db-error-registry.ts +12 -0
  176. package/src/db/gateway.ts +26 -133
  177. package/src/db/history-retention-ops.ts +13 -0
  178. package/src/db/inbox-async.ts +2 -2
  179. package/src/db/inbox-read-ops.ts +1 -1
  180. package/src/db/inbox.ts +9 -3
  181. package/src/db/issue-associations.ts +2 -2
  182. package/src/db/issue-refs-async.ts +4 -1
  183. package/src/db/issue-refs-read-ops.ts +6 -0
  184. package/src/db/issue-refs.ts +8 -8
  185. package/src/db/kernel-event-log-schema.ts +2 -0
  186. package/src/db/land-boundaries.ts +51 -5
  187. package/src/db/land-gate-attestations.ts +2 -2
  188. package/src/db/landing-receipts.ts +2 -2
  189. package/src/db/lifecycle-context-write.ts +5 -0
  190. package/src/db/lifecycle-tail-async.ts +76 -0
  191. package/src/db/lifecycle-tail-write-ops.ts +62 -0
  192. package/src/db/lifecycle-write-window.ts +2 -1
  193. package/src/db/lineage-pair-dedupe-sql.ts +3 -3
  194. package/src/db/main-connection-mode.ts +11 -0
  195. package/src/db/main-connection-writes.ts +20 -2
  196. package/src/db/maintenance-job-runs-async.ts +2 -2
  197. package/src/db/maintenance-job-write-ops.ts +1 -1
  198. package/src/db/maintenance-pauses.ts +2 -2
  199. package/src/db/message-send.ts +4 -0
  200. package/src/db/messages.ts +31 -25
  201. package/src/db/migrations.ts +18 -1
  202. package/src/db/notification-write-ops.ts +19 -0
  203. package/src/db/operator-answer-write.ts +19 -0
  204. package/src/db/orchestrators.ts +10 -4
  205. package/src/db/pairs-async.ts +22 -1
  206. package/src/db/pairs-write-ops.ts +8 -3
  207. package/src/db/pairs.ts +41 -48
  208. package/src/db/plan-dispatch-schema.ts +5 -1
  209. package/src/db/plan-ready-notices.ts +43 -0
  210. package/src/db/plan-tools-include-migration.ts +36 -0
  211. package/src/db/plans-write-ops.ts +2 -0
  212. package/src/db/presence-buffer.ts +5 -0
  213. package/src/db/presence-loop.ts +18 -23
  214. package/src/db/presence-write.ts +0 -6
  215. package/src/db/project-entries-async.ts +14 -0
  216. package/src/db/project-entries-read-ops.ts +5 -1
  217. package/src/db/project-entries-write-ops.ts +8 -0
  218. package/src/db/project-entries.ts +15 -12
  219. package/src/db/project-journal.ts +2 -2
  220. package/src/db/project-trust-approvals.ts +2 -2
  221. package/src/db/prompt-templates.ts +4 -4
  222. package/src/db/read-op-marshaling-fixtures.ts +18 -0
  223. package/src/db/read-ops.ts +4 -0
  224. package/src/db/retention.ts +13 -2
  225. package/src/db/review-publish-write.ts +8 -0
  226. package/src/db/runtime-admin-async.ts +19 -0
  227. package/src/db/runtime-admin-write-ops.ts +36 -0
  228. package/src/db/runtime-tail-async.ts +288 -0
  229. package/src/db/runtime-tail-write-ops.ts +97 -0
  230. package/src/db/schema.ts +4 -1
  231. package/src/db/spawn-budget.ts +5 -0
  232. package/src/db/spawn-delegation.ts +3 -12
  233. package/src/db/sql-audit-indexes.ts +3 -0
  234. package/src/db/stats-async.ts +5 -3
  235. package/src/db/task-entity.ts +5 -4
  236. package/src/db/team-claim-policy.ts +60 -0
  237. package/src/db/teams-async.ts +4 -0
  238. package/src/db/teams-write-ops.ts +4 -0
  239. package/src/db/terminal-events.ts +15 -0
  240. package/src/db/timer-cutover-bodies.ts +292 -0
  241. package/src/db/timer-cutover-core.ts +37 -278
  242. package/src/db/timer-cutover-state.ts +40 -0
  243. package/src/db/timer-cutover-write-ops.ts +1 -40
  244. package/src/db/token-db-write-ops.ts +2 -0
  245. package/src/db/token-standing-async.ts +12 -0
  246. package/src/db/token-standing.ts +6 -9
  247. package/src/db/token-usage-retention.ts +33 -0
  248. package/src/db/token-usage-write-ops.ts +1 -1
  249. package/src/db/token-usage.ts +4 -1
  250. package/src/db/user-invites-async.ts +13 -0
  251. package/src/db/user-invites.ts +3 -3
  252. package/src/db/users.ts +14 -6
  253. package/src/db/workflow-execution.ts +3 -3
  254. package/src/db/workspaces.ts +8 -5
  255. package/src/db/write-ops.ts +31 -12
  256. package/src/db/writer-fault-seam.ts +2 -9
  257. package/src/db/writer-fault-test-ops.ts +18 -0
  258. package/src/db/writer-gateway.ts +42 -46
  259. package/src/dev.ts +12 -16
  260. package/src/event-loop-lag.ts +4 -2
  261. package/src/event-loop-yield.ts +2 -2
  262. package/src/events.ts +10 -8
  263. package/src/host-provider-store.ts +5 -4
  264. package/src/hub-metrics-report.ts +5 -1
  265. package/src/hub-metrics.ts +38 -18
  266. package/src/init.ts +299 -0
  267. package/src/injection-sweep-pacing.ts +6 -8
  268. package/src/invite-link.ts +16 -9
  269. package/src/land-gate-notifications.ts +11 -10
  270. package/src/land-gate-queue-progress.ts +3 -3
  271. package/src/land-settle-dispatch.ts +52 -92
  272. package/src/lifecycle-context-pressure.ts +11 -10
  273. package/src/lifecycle-manager.ts +104 -98
  274. package/src/maintenance/breaker.ts +18 -16
  275. package/src/maintenance/checkout-refresh.ts +18 -1
  276. package/src/maintenance/critical-job-watchdog.ts +3 -3
  277. package/src/maintenance/events.ts +3 -3
  278. package/src/maintenance/history-retention-job.ts +30 -0
  279. package/src/maintenance/jobs.ts +29 -67
  280. package/src/maintenance/long-running-work.ts +3 -1
  281. package/src/maintenance/offline-agent-prune-job.ts +28 -0
  282. package/src/maintenance/orphaned-sessions.ts +5 -4
  283. package/src/maintenance/pauses.ts +4 -10
  284. package/src/maintenance/plan-ready-notice-job.ts +71 -0
  285. package/src/maintenance/scheduler.ts +18 -13
  286. package/src/maintenance/stalled-delivery-pager.ts +4 -4
  287. package/src/maintenance/teams.ts +19 -30
  288. package/src/maintenance/workspaces.ts +17 -27
  289. package/src/managed-policy-escalation.ts +7 -7
  290. package/src/managed-policy.ts +3 -3
  291. package/src/managed-provider-gate.ts +5 -4
  292. package/src/managed-spawn-reservation.ts +5 -4
  293. package/src/mcp/index.ts +8 -8
  294. package/src/mcp/output-descriptions.ts +1 -1
  295. package/src/mcp/presentation.ts +2 -1
  296. package/src/mcp/runtime-output.ts +6 -1
  297. package/src/mcp/self-care-visibility.ts +2 -2
  298. package/src/mcp/spawn-targets-matrix.ts +10 -1
  299. package/src/mcp/tool-groups.ts +3 -2
  300. package/src/mcp/tool-roles.ts +81 -0
  301. package/src/mcp/tools-workspace.ts +1 -1
  302. package/src/mcp-blackboard-tools.ts +6 -5
  303. package/src/mcp-decision-tools.ts +7 -7
  304. package/src/mcp-egress-tools.ts +10 -9
  305. package/src/mcp-issue-activity-tools.ts +2 -2
  306. package/src/mcp-issue-association-tools.ts +4 -4
  307. package/src/mcp-issue-ref-tools.ts +3 -3
  308. package/src/mcp-merge-tools.ts +5 -5
  309. package/src/mcp-notification-subscription-tools.ts +14 -15
  310. package/src/mcp-output-levels.test-fixture.ts +1 -1
  311. package/src/mcp-profile-trust-tools.ts +1 -1
  312. package/src/mcp-project-journal-tools.ts +4 -12
  313. package/src/mcp-project-tools.ts +17 -18
  314. package/src/mcp-routing-policy-tools.ts +3 -2
  315. package/src/mcp-team-tools.ts +6 -6
  316. package/src/mcp-workflow-tools.ts +5 -3
  317. package/src/memory-area-injection.ts +13 -12
  318. package/src/memory-boundary-capture.ts +6 -6
  319. package/src/memory-injection.ts +45 -43
  320. package/src/merge-pause-maintenance.ts +5 -4
  321. package/src/notification-router.ts +44 -17
  322. package/src/notify.ts +5 -5
  323. package/src/observed-reset-refresh.ts +4 -4
  324. package/src/operator-alarm.ts +2 -5
  325. package/src/project-ingest.ts +6 -6
  326. package/src/project-instructions.ts +8 -1
  327. package/src/project-trust.ts +12 -11
  328. package/src/prompt-resolver.ts +3 -3
  329. package/src/provider-quota-async.ts +5 -7
  330. package/src/provider-timeline-event.ts +4 -7
  331. package/src/quota-band-delivery.ts +2 -2
  332. package/src/recipe-runner.ts +4 -3
  333. package/src/retention-config-store.ts +4 -3
  334. package/src/reviewer-pipeline.ts +11 -10
  335. package/src/routes/_route.ts +4 -4
  336. package/src/routes/_shared.ts +13 -12
  337. package/src/routes/agent-effort.ts +5 -5
  338. package/src/routes/agent-heartbeat-quota.ts +23 -0
  339. package/src/routes/agent-keep.ts +1 -1
  340. package/src/routes/agent-reporting.ts +1 -1
  341. package/src/routes/agent-self-effort.ts +5 -5
  342. package/src/routes/agent-sessions.ts +24 -24
  343. package/src/routes/agents-spawn.ts +1 -1
  344. package/src/routes/agents.ts +17 -119
  345. package/src/routes/auth.ts +44 -17
  346. package/src/routes/authority-audit.ts +2 -2
  347. package/src/routes/automations.ts +6 -5
  348. package/src/routes/channel-binding-delete.ts +1 -1
  349. package/src/routes/channel-output.ts +3 -3
  350. package/src/routes/claude-api-budget.ts +5 -5
  351. package/src/routes/commands-listing.ts +1 -1
  352. package/src/routes/commands.ts +29 -27
  353. package/src/routes/context-resets.ts +2 -2
  354. package/src/routes/coordinator-teams.ts +1 -1
  355. package/src/routes/decisions.ts +2 -2
  356. package/src/routes/host-providers.ts +18 -0
  357. package/src/routes/host-secret-files.ts +1 -1
  358. package/src/routes/hosts.ts +11 -11
  359. package/src/routes/inbox.ts +3 -2
  360. package/src/routes/index.ts +7 -3
  361. package/src/routes/integrations.ts +9 -7
  362. package/src/routes/issue-activity.ts +2 -2
  363. package/src/routes/issue-associations.ts +4 -4
  364. package/src/routes/issue-refs.ts +3 -3
  365. package/src/routes/kernel-timers-operator.ts +1 -1
  366. package/src/routes/memory.ts +1 -1
  367. package/src/routes/merges.ts +11 -11
  368. package/src/routes/messages.ts +23 -17
  369. package/src/routes/orchestrator-bootstrap.ts +3 -1
  370. package/src/routes/orchestrator-host-registration.ts +2 -0
  371. package/src/routes/orchestrator-host-utilization.ts +2 -2
  372. package/src/routes/orchestrator-proxy.ts +2 -2
  373. package/src/routes/orchestrator.ts +27 -18
  374. package/src/routes/pairs.ts +8 -8
  375. package/src/routes/project-profile-instructions.ts +1 -1
  376. package/src/routes/project-trust.ts +1 -1
  377. package/src/routes/projects.ts +16 -16
  378. package/src/routes/prompt-templates.ts +12 -12
  379. package/src/routes/provider-quotas.ts +10 -10
  380. package/src/routes/run-command.ts +1 -1
  381. package/src/routes/secrets-backup.ts +4 -5
  382. package/src/routes/secrets-import.ts +3 -4
  383. package/src/routes/secrets-vault.ts +12 -14
  384. package/src/routes/settings.ts +1 -1
  385. package/src/routes/shipping.ts +9 -1
  386. package/src/routes/spawn-policy.ts +8 -7
  387. package/src/routes/sse.ts +2 -2
  388. package/src/routes/team-templates.ts +7 -7
  389. package/src/routes/teams.ts +10 -10
  390. package/src/routes/tokens.ts +18 -17
  391. package/src/routes/users.ts +24 -24
  392. package/src/routes/viewer-read.ts +3 -3
  393. package/src/routes/web-tool-calls.ts +4 -4
  394. package/src/routes/workflows.ts +2 -2
  395. package/src/routes/workspaces.ts +17 -16
  396. package/src/routing-policy-push.ts +6 -6
  397. package/src/runtime-grant.ts +17 -11
  398. package/src/runtime-install.ts +30 -0
  399. package/src/runtime-tokens.ts +20 -28
  400. package/src/secrets-broker/config.ts +1 -1
  401. package/src/secrets-broker/service.ts +2 -2
  402. package/src/security.ts +3 -1
  403. package/src/self-resume-carry.ts +21 -110
  404. package/src/self-resume-supersede.ts +4 -3
  405. package/src/self-resume.ts +54 -66
  406. package/src/server-background.ts +1 -1
  407. package/src/server.ts +25 -26
  408. package/src/services/agent-action.ts +12 -31
  409. package/src/services/agent-effort.ts +7 -32
  410. package/src/services/agent-hang-watchdog-job.ts +2 -2
  411. package/src/services/agent-heartbeat.ts +166 -0
  412. package/src/services/agent-reporting.ts +1 -1
  413. package/src/services/auth-context.ts +6 -0
  414. package/src/services/browser-inventory.ts +3 -3
  415. package/src/services/browser-session-state.ts +3 -2
  416. package/src/services/browser-session.ts +21 -27
  417. package/src/services/bus-command-update.ts +9 -9
  418. package/src/services/checkout-refresh-state.ts +1 -0
  419. package/src/services/checkout-refresh.test-fixture.ts +3 -2
  420. package/src/services/command-host.ts +7 -2
  421. package/src/services/coordinator-digest.ts +3 -2
  422. package/src/services/dispatch-command.ts +12 -13
  423. package/src/services/effective-launch-driver.ts +14 -9
  424. package/src/services/host-changed-paths.test-fixture.ts +5 -6
  425. package/src/services/host-project-readiness.ts +2 -2
  426. package/src/services/host-providers.ts +1 -1
  427. package/src/services/host-utilization.ts +9 -9
  428. package/src/services/idle-agent-digest.ts +17 -12
  429. package/src/services/idle-epics.ts +4 -4
  430. package/src/services/interrupt-agent.ts +12 -9
  431. package/src/services/issue-activity.ts +3 -3
  432. package/src/services/issue-associations.ts +15 -18
  433. package/src/services/issue-lifecycle-comments.ts +3 -2
  434. package/src/services/issue-lifecycle.ts +14 -13
  435. package/src/services/issue-refs.ts +4 -5
  436. package/src/services/kernel-timers-cutover-chaos-worker.ts +1 -1
  437. package/src/services/kernel-timers-ingress-chaos-worker.ts +6 -1
  438. package/src/services/kernel-timers-ingress.ts +3 -3
  439. package/src/services/kernel-timers-message-authority-chaos-worker.ts +3 -10
  440. package/src/services/kernel-timers-operator-chaos-worker.ts +3 -2
  441. package/src/services/kernel-timers-outbox.ts +1 -1
  442. package/src/services/kernel-timers-stage10-operator.ts +1 -1
  443. package/src/services/kernel-timers.ts +4 -3
  444. package/src/services/land-gate-placement.ts +11 -2
  445. package/src/services/land-issue-relations.ts +5 -4
  446. package/src/services/land-restart-retry.ts +7 -6
  447. package/src/services/landed-ancestry.ts +6 -5
  448. package/src/services/legacy-landing-host.ts +4 -1
  449. package/src/services/legacy-landing-recovery.ts +8 -6
  450. package/src/services/managed-running.ts +17 -17
  451. package/src/services/memory-areas.ts +3 -3
  452. package/src/services/merge-pause.ts +9 -15
  453. package/src/services/operator-decisions.ts +37 -41
  454. package/src/services/orchestrator-registration-recovery.ts +6 -2
  455. package/src/services/orchestrator-upgrade.ts +14 -12
  456. package/src/services/permission-decision.ts +4 -3
  457. package/src/services/plan-ci-notice.ts +1 -1
  458. package/src/services/plan-conformance.ts +4 -4
  459. package/src/services/plan-dispatch-policy.ts +10 -3
  460. package/src/services/plan-dispatch.ts +35 -14
  461. package/src/services/plan-graph.ts +39 -261
  462. package/src/services/plan-import.ts +1 -1
  463. package/src/services/plan-live-bindings.ts +8 -37
  464. package/src/services/plan-task-create.ts +1 -1
  465. package/src/services/plan-terminal-refs.ts +2 -12
  466. package/src/services/plan-wake-digest.ts +8 -3
  467. package/src/services/profile-trust.ts +2 -2
  468. package/src/services/project.ts +11 -10
  469. package/src/services/prompt-templates.ts +6 -5
  470. package/src/services/provider-api-key.ts +73 -0
  471. package/src/services/provider-hold-nudge.ts +9 -9
  472. package/src/services/provider-hold-record.ts +13 -12
  473. package/src/services/provider-state-audit.ts +19 -18
  474. package/src/services/provisioning-harness/driver.ts +1 -1
  475. package/src/services/provisioning-harness/fixture-matrix.ts +3 -3
  476. package/src/services/provisioning-harness/lifecycle.ts +24 -22
  477. package/src/services/provisioning-harness/prepare.ts +16 -17
  478. package/src/services/rate-limit-resume.ts +10 -9
  479. package/src/services/red-main-auto-revert.ts +7 -5
  480. package/src/services/red-main-controller-deps.ts +20 -3
  481. package/src/services/red-main-incident.ts +3 -1
  482. package/src/services/red-main-quarantine.ts +2 -3
  483. package/src/services/register-agent.ts +22 -22
  484. package/src/services/restart-project-role.ts +1 -1
  485. package/src/services/restart-spawn.ts +17 -16
  486. package/src/services/review-publish.ts +7 -24
  487. package/src/services/review-ref-reconcile.ts +29 -38
  488. package/src/services/runtime-dependency-alert.ts +27 -0
  489. package/src/services/scheduler-command.ts +11 -9
  490. package/src/services/scheduler.ts +5 -5
  491. package/src/services/self-effort-grant.ts +5 -25
  492. package/src/services/send-message.ts +16 -33
  493. package/src/services/shutdown-agent.ts +17 -14
  494. package/src/services/spawn-agent.ts +12 -41
  495. package/src/services/spawn-chain-inspector.ts +1 -1
  496. package/src/services/spawn-cwd-placement.ts +21 -5
  497. package/src/services/spawn-issue-activity.ts +8 -6
  498. package/src/services/spawn-launch.ts +17 -1
  499. package/src/services/spawn-loss-escalation.ts +1 -1
  500. package/src/services/spawn-registration-watchdog.ts +2 -2
  501. package/src/services/spawn-repo-probe.ts +7 -5
  502. package/src/services/spawn-tree-cleanup.ts +7 -8
  503. package/src/services/steward-reaper.ts +4 -4
  504. package/src/services/task-context-hydration.ts +25 -1
  505. package/src/services/task-delivery.ts +20 -34
  506. package/src/services/task-entity.ts +40 -203
  507. package/src/services/task-issue-link.ts +13 -20
  508. package/src/services/task-issue-weave.ts +12 -11
  509. package/src/services/task-lifecycle.ts +27 -31
  510. package/src/services/task-repo-cwd.ts +37 -0
  511. package/src/services/task-review-hold.ts +4 -17
  512. package/src/services/task-semantic-events.ts +13 -11
  513. package/src/services/task-state-reconcile.ts +3 -4
  514. package/src/services/task-validation.ts +2 -2
  515. package/src/services/team-templates.ts +5 -5
  516. package/src/services/team.ts +31 -98
  517. package/src/services/terminal-provider-exit.ts +6 -5
  518. package/src/services/timer-message-fire-chaos-worker.ts +7 -3
  519. package/src/services/timer-message-fire-outbox.ts +1 -1
  520. package/src/services/transient-agent-reaper.ts +22 -19
  521. package/src/services/turn-reconcile-audit.ts +9 -9
  522. package/src/services/user-onboarding.ts +8 -6
  523. package/src/services/user-profile.ts +6 -9
  524. package/src/services/work-finality.ts +16 -4
  525. package/src/services/workflow-auto-start.ts +2 -1
  526. package/src/services/workflow-caller.ts +61 -4
  527. package/src/services/workflow-child-proof-worker.ts +1 -1
  528. package/src/services/workflow-control-proof-worker.ts +1 -1
  529. package/src/services/workflow-coordinator.ts +8 -2
  530. package/src/services/workflow-decisions.ts +5 -5
  531. package/src/services/workflow-deferred-cleanup.ts +1 -1
  532. package/src/services/workflow-effect-driver.ts +42 -19
  533. package/src/services/workflow-effects.ts +41 -27
  534. package/src/services/workflow-pins.ts +7 -4
  535. package/src/services/workflow-reconcile.ts +1 -0
  536. package/src/services/workflow-resume-pins.ts +3 -3
  537. package/src/services/workflow-routing-defaults.ts +18 -0
  538. package/src/services/workflow-run-view.ts +4 -3
  539. package/src/services/workflow-run.test-fixture.ts +1 -1
  540. package/src/services/workflow-runs.ts +34 -22
  541. package/src/services/workflow-supervisor-state.ts +21 -1
  542. package/src/services/workflow-supervisor.ts +4 -3
  543. package/src/services/workspace-recovery.ts +4 -3
  544. package/src/setup.ts +47 -8
  545. package/src/spawn-admission.ts +10 -2
  546. package/src/spawn-placement.ts +27 -9
  547. package/src/spawn-targets.ts +8 -8
  548. package/src/sse-agent-status.ts +15 -6
  549. package/src/sse.ts +49 -24
  550. package/src/startup-context-injection.ts +61 -57
  551. package/src/steward-handoff.ts +4 -3
  552. package/src/steward-policy-cleanup.ts +9 -8
  553. package/src/steward.ts +2 -2
  554. package/src/task-dispatch-live.ts +5 -2
  555. package/src/task-dispatcher.ts +2 -2
  556. package/src/token-db.ts +2 -2
  557. package/src/token-usage-async.ts +2 -4
  558. package/src/user-awaiting-reply.ts +5 -5
  559. package/src/utils.ts +7 -0
  560. package/src/workspace-actions.ts +24 -29
  561. package/src/workspace-auto-merge.ts +14 -12
  562. package/src/workspace-changed-paths-client.ts +16 -5
  563. package/src/workspace-cleanup-stale.ts +30 -7
  564. package/src/workspace-command-settle.ts +24 -16
  565. package/src/workspace-config.ts +1 -1
  566. package/src/workspace-conflict-resolver.ts +9 -7
  567. package/src/workspace-dead-owner-recovery.ts +5 -4
  568. package/src/workspace-escalation.ts +12 -11
  569. package/src/workspace-human-escalation.ts +3 -3
  570. package/src/workspace-land-gate-watchdog.ts +3 -3
  571. package/src/workspace-land-refusal.ts +20 -0
  572. package/src/workspace-land-stopped.ts +1 -0
  573. package/src/workspace-merge.ts +10 -26
  574. package/src/workspace-nothing-to-land-notice.ts +2 -2
  575. package/src/workspace-orphans.ts +18 -37
  576. package/src/workspace-pr-completion.ts +10 -8
  577. package/src/workspace-rekey.ts +3 -3
  578. package/src/workspace-steward-failure.ts +2 -2
  579. package/src/workspace-steward-wake.ts +2 -2
  580. package/public/assets/agent-liveness-CaNwK0Zf.js +0 -2
  581. package/public/assets/overview-C1DCTzzV.js +0 -2
  582. package/public/assets/provider-display-DsuWMvVL.js +0 -2
  583. package/public/assets/run-card-CpKqRbhg.js +0 -2
  584. package/public/assets/shipping-B9YtnwtG.js +0 -2
  585. package/public/assets/store-DcClXLH0.js +0 -9
  586. package/runner/src/agent-runner/http-liveness.ts +0 -85
  587. package/src/db/on-loop-write-transaction.ts +0 -34
@@ -4,7 +4,7 @@ import { isHumanAgentId, PROVIDER_SUBMIT_UNCONFIRMED, RELAY_TOKEN_HEADER, SUBMIT
4
4
  import { authorizeAgentControl } from "../agent-authz";
5
5
  import { VALID_AGENT_ACTIONS, agentControlActionIcon, auditEvent, authAuditMetadata, authorizeAgentRoute, authorizeRoute, canViewAgentRoute, cleanAttachmentRefs, dashboardAttribution, emitCommand, error, json, metaString, parseBody, requestUserAddress, requestUserId, spawnRequestId, type AgentControlAction, type Handler } from "./_shared";
6
6
  import { arbitrateDrive, currentDriver, drivingMessage, releaseDrive, type DriveArbitration } from "../agent-drive-arbiter";
7
- import { ValidationError, getAgent, getAgentTimelineAsync, getOrchestrator, markReadyAsync, mergeAgentMetaAsync, sendMessage } from "../db";
7
+ import { sendMessageAsync, ValidationError, getAgent, getAgentTimelineAsync, getOrchestrator, markReadyAsync, mergeAgentMetaAsync } from "../db";
8
8
  import { announcePresence, viewersForAgent } from "../dashboard-presence";
9
9
  import { cleanString, optionalEnum } from "../validation";
10
10
  import { createCommandAsync } from "../commands-write";
@@ -40,10 +40,10 @@ export const postAgentSelfRestartRecheck: Handler = async (req, params) => {
40
40
  // orchestrator's own runtime token authorizes this exactly as it authorizes the agent.restart it
41
41
  // is executing — no over-refusal that would silently abort every self-restart.
42
42
  const orchestrator = agent ? managedControlOrchestrator(agent) : null;
43
- const denied = authorizeRoute(req, {
43
+ const denied = (await authorizeRoute(req, {
44
44
  scope: "command:write",
45
45
  resource: { agentId: params.id!, orchestratorId: orchestrator?.id, policyName: metaString(agent?.meta, "policyName"), spawnRequestId: metaString(agent?.meta, "spawnRequestId") },
46
- });
46
+ }));
47
47
  if (denied) return denied;
48
48
  if (!agent) return json({ ok: false, reason: `self-restart safe-point re-check: agent ${params.id} not found` });
49
49
  const violation = await evaluateSelfRestartSafePoint(agent);
@@ -161,11 +161,11 @@ export const postAgentAction: Handler = async (req, params) => {
161
161
  if (!agent) return error("agent not found", 404);
162
162
  // #392: the acting user must be allowed to drive this agent (fail-open single-user). Gated
163
163
  // before the capability probe so an unauthorized user learns nothing about what the agent supports.
164
- const driveDenied = authorizeAgentRoute(req, agent.id, "drive", action);
164
+ const driveDenied = (await authorizeAgentRoute(req, agent.id, "drive", action));
165
165
  if (driveDenied) return driveDenied;
166
166
  // #3567 — the one lifecycle-control check (the services re-run it for bus/MCP); self excepted.
167
167
  const actionCtx = authContextFromRequest(req);
168
- const controlDenied = actionCtx.callerAgentId === agent.id ? null : authorizeAgentControl(actionCtx, agent, action);
168
+ const controlDenied = actionCtx.callerAgentId === agent.id ? null : (await authorizeAgentControl(actionCtx, agent, action));
169
169
  if (controlDenied) return error(controlDenied.message, 403);
170
170
  // #2664 — releasing a hold that is already gone (recovered, or a double click) is an idempotent no-op.
171
171
  if (action === "releaseSubmitHold" && agentIsControlEligible(agent) && !agentSubmitHoldActive(agent)) return json({ ok: true, action, released: false }, 200);
@@ -181,10 +181,10 @@ export const postAgentAction: Handler = async (req, params) => {
181
181
  if (action === "interrupt") {
182
182
  const arb = await arbitrateDrive(requestUserId(req), agent.id);
183
183
  if (!arb.allowed) return driveBlockedResponse(arb);
184
- const denied = authorizeRoute(req, {
184
+ const denied = (await authorizeRoute(req, {
185
185
  scope: "agent:write",
186
186
  resource: controlResource,
187
- });
187
+ }));
188
188
  if (denied) return denied;
189
189
  try {
190
190
  const result = await dispatchInterrupt({
@@ -208,10 +208,10 @@ export const postAgentAction: Handler = async (req, params) => {
208
208
  // bus + MCP surfaces. The route still enforces the coarse `agent:write` scope +
209
209
  // constraint-list; the service adds the fine parent→child gate.
210
210
  if (action === "shutdown") {
211
- const denied = authorizeRoute(req, {
211
+ const denied = (await authorizeRoute(req, {
212
212
  scope: "agent:write",
213
213
  resource: controlResource,
214
- });
214
+ }));
215
215
  if (denied) return denied;
216
216
  try {
217
217
  if (parsed.body.hard !== undefined && typeof parsed.body.hard !== "boolean") throw new ValidationError("hard must be a boolean");
@@ -243,10 +243,10 @@ export const postAgentAction: Handler = async (req, params) => {
243
243
  const resumeId = action === "resume" ? await latestResumeIdForAgent(agent) : undefined;
244
244
  if (action === "resume" && !orchestrator) return error("no online orchestrator available to resume agent", 409);
245
245
  if (action === "resume" && !resumeId) return error("no resume id recorded for agent", 422);
246
- const denied = authorizeRoute(req, {
246
+ const denied = (await authorizeRoute(req, {
247
247
  scope: "agent:write",
248
248
  resource: { ...controlResource, orchestratorId: orchestrator?.id },
249
- });
249
+ }));
250
250
  if (denied) return denied;
251
251
  // #1953 RC3 — a dashboard restart/resume of a disabled steward is refused (see helper).
252
252
  // #1955 (#1953-review nit 2) — AFTER authorizeRoute: run pre-auth, the 409 disclosed to an
@@ -320,7 +320,7 @@ export const postAgentPrompt: Handler = async (req, params) => {
320
320
  const agent = getAgent(params.id!);
321
321
  if (!agent) return error("agent not found", 404);
322
322
  // #392: only a user authorized to drive this agent may prompt it (fail-open single-user).
323
- const driveDenied = authorizeAgentRoute(req, agent.id, "drive");
323
+ const driveDenied = (await authorizeAgentRoute(req, agent.id, "drive"));
324
324
  if (driveDenied) return driveDenied;
325
325
  // #394: submitting a prompt takes/refreshes the drive lease. A second human driving the same
326
326
  // agent is blocked here (single-user is a no-op), so two operators can't double-submit into one
@@ -328,10 +328,10 @@ export const postAgentPrompt: Handler = async (req, params) => {
328
328
  const arb = await arbitrateDrive(requestUserId(req), agent.id);
329
329
  if (!arb.allowed) return driveBlockedResponse(arb);
330
330
  if (agent.status === "offline") return error("agent is offline", 422);
331
- const denied = authorizeRoute(req, {
331
+ const denied = (await authorizeRoute(req, {
332
332
  scope: "message:send",
333
333
  resource: { target: agent.id, agentId: requestUserAddress(req) },
334
- });
334
+ }));
335
335
  if (denied) return denied;
336
336
  // #2664 — the runner holds exactly one unconfirmed prompt for the operator's resend; a new one
337
337
  // must not queue behind (or replace) it. Refused before any message row exists.
@@ -341,13 +341,13 @@ export const postAgentPrompt: Handler = async (req, params) => {
341
341
  // canonical `user` sink, so today's single operator routes/renders exactly as before; once
342
342
  // auth (#389) sets the real session user, the bubble carries the real `user:<id>`.
343
343
  const fromUser = requestUserAddress(req);
344
- const message = sendMessage({
344
+ const message = (await sendMessageAsync({
345
345
  from: fromUser,
346
346
  to: agent.id,
347
347
  kind: "session",
348
348
  body,
349
349
  ...(attachments ? { payload: { attachments } } : {}),
350
- });
350
+ }));
351
351
  const command = await createCommandAsync({
352
352
  type: "prompt.inject",
353
353
  source: "dashboard",
@@ -397,7 +397,7 @@ export const postAgentPermissionDecision: Handler = async (req, params) => {
397
397
  const agent = getAgent(params.id!);
398
398
  if (!agent) return error("agent not found", 404);
399
399
  // #392: approving/denying a permission is driving the agent (fail-open single-user).
400
- const driveDenied = authorizeAgentRoute(req, agent.id, "drive");
400
+ const driveDenied = (await authorizeAgentRoute(req, agent.id, "drive"));
401
401
  if (driveDenied) return driveDenied;
402
402
  // #394: a plan-approve / AskUserQuestion answer drives the turn — only the lease holder (or an
403
403
  // admin override) may answer, so two humans can't race competing decisions into one prompt.
@@ -405,10 +405,10 @@ export const postAgentPermissionDecision: Handler = async (req, params) => {
405
405
  if (!arb.allowed) return driveBlockedResponse(arb);
406
406
  const metaPolicyName = typeof agent.meta?.policyName === "string" ? agent.meta.policyName : undefined;
407
407
  const metaSpawnRequestId = typeof agent.meta?.spawnRequestId === "string" ? agent.meta.spawnRequestId : undefined;
408
- const denied = authorizeRoute(req, {
408
+ const denied = (await authorizeRoute(req, {
409
409
  scope: "agent:write",
410
410
  resource: { agentId: agent.id, policyName: metaPolicyName, spawnRequestId: metaSpawnRequestId },
411
- });
411
+ }));
412
412
  if (denied) return denied;
413
413
  const result = await dispatchAgentPermissionDecision({
414
414
  agentId: agent.id,
@@ -487,7 +487,7 @@ export const postAgentDrive: Handler = async (req, params) => {
487
487
  const agent = getAgent(params.id!);
488
488
  if (!agent) return error("agent not found", 404);
489
489
  // Driving the wheel requires the same #392 drive authorization the prompt/interrupt routes use.
490
- const driveDenied = authorizeAgentRoute(req, agent.id, "drive");
490
+ const driveDenied = (await authorizeAgentRoute(req, agent.id, "drive"));
491
491
  if (driveDenied) return driveDenied;
492
492
  const userId = requestUserId(req);
493
493
 
@@ -545,10 +545,10 @@ export const postAgentTerminalSession: Handler = async (req, params) => {
545
545
  const metaTmuxSession = typeof agent.meta?.tmuxSession === "string" ? agent.meta.tmuxSession : undefined;
546
546
  const metaPolicyName = typeof agent.meta?.policyName === "string" ? agent.meta.policyName : undefined;
547
547
  const metaSpawnRequestId = typeof agent.meta?.spawnRequestId === "string" ? agent.meta.spawnRequestId : undefined;
548
- const denied = authorizeRoute(req, {
548
+ const denied = (await authorizeRoute(req, {
549
549
  scope: "terminal:attach",
550
550
  resource: { agentId: agent.id, orchestratorId: orchestrator.id, policyName: metaPolicyName, spawnRequestId: metaSpawnRequestId, terminalAttach: true },
551
- });
551
+ }));
552
552
  if (denied) return denied;
553
553
  // Provider-owned attach surfaces are more specific than the runner's tmux pane.
554
554
  // Codex runs its real interactive TUI as a guest attached to the app-server thread;
@@ -588,7 +588,7 @@ export const deleteAgentTerminalSession: Handler = async (req, params) => {
588
588
  if (!agent) return error("agent not found", 404);
589
589
  const orchestrator = managedControlOrchestrator(agent);
590
590
  if (!orchestrator) return error("agent does not have an online orchestrator", 422);
591
- const denied = authorizeRoute(req, {
591
+ const denied = (await authorizeRoute(req, {
592
592
  scope: "terminal:attach",
593
593
  resource: {
594
594
  agentId: agent.id,
@@ -597,7 +597,7 @@ export const deleteAgentTerminalSession: Handler = async (req, params) => {
597
597
  spawnRequestId: typeof agent.meta?.spawnRequestId === "string" ? agent.meta.spawnRequestId : undefined,
598
598
  terminalAttach: true,
599
599
  },
600
- });
600
+ }));
601
601
  if (denied) return denied;
602
602
  const session = params.session!;
603
603
  return proxyOrchestratorDelete(req, orchestrator.id, `/api/terminal-guests/${encodeURIComponent(session)}`);
@@ -85,7 +85,7 @@ export const postAgent: Handler = async (req) => {
85
85
  // timeline note, parent-wake, audit) live in src/services/register-agent.ts so the HTTP,
86
86
  // bus, and orchestrator-report paths cannot drift (epic #342).
87
87
  const input = normalizeAgentInput(parsed.body);
88
- const agent = registerAgent(input, authContextFromRequest(req));
88
+ const agent = await registerAgent(input, authContextFromRequest(req));
89
89
  return json(agent, 201);
90
90
  } catch (e) {
91
91
  if (e instanceof ValidationError) return error(e.message, 400);
@@ -1,8 +1,8 @@
1
1
  // Auto-split from routes.ts (#299). Domain: agents.
2
2
  import { messageViewerForRequest, visibleMessages } from "./message-viewer";
3
3
  import { attachPrincipal, attachPrincipals } from "../agent-principal";
4
- import { VALID_AGENT_STATUSES, agentSessionStatus, auditEvent, canViewAgentRoute, error, json, normalizeAgentSessionGuard, parseBody, parseId, parseQueryInt, visibleAgentIdsForRequest, type Handler } from "./_shared";
5
- import { ValidationError, deleteAgentAsync, getAgent, getAgentAsync, getAgentIdentityAsync, getAgentQuotaSnapshotsAsync, getAgentTimelineAsync, getCrashReport, recordHeartbeatAsync, listAgentChatHistoryAsync, listAgents, listAgentsAsync, listArtifactsForEntityAsync, listContextSnapshotsAsync, listPendingReplyObligationsAsync, markReadyAsync, searchAgentsAsync, setLabelAsync, setStatusAsync, setTagsAsync, validateAgentSession } from "../db";
4
+ import { agentSessionStatus, auditEvent, authorizeRoute, canViewAgentRoute, error, json, normalizeAgentSessionGuard, parseBody, parseId, parseQueryInt, visibleAgentIdsForRequest, type Handler } from "./_shared";
5
+ import { ValidationError, deleteAgentAsync, getAgent, getAgentAsync, getAgentIdentityAsync, getAgentQuotaSnapshotsAsync, getAgentTimelineAsync, getCrashReport, listAgentChatHistoryAsync, listAgents, listAgentsAsync, listArtifactsForEntityAsync, listContextSnapshotsAsync, listPendingReplyObligationsAsync, searchAgentsAsync, setLabelAsync, setTagsAsync, validateAgentSession } from "../db";
6
6
  import { attachBranchState, attachBranchStates } from "../agent-branch-state";
7
7
  import { redactAgentCardLaunchShape } from "../agent-launch-shape-redaction";
8
8
  import { attachChannelBinding, attachChannelBindings } from "../agent-channel-binding";
@@ -10,47 +10,16 @@ import { attachCommandActivities, attachCommandActivity } from "../agent-command
10
10
  import { attachIssueRepo, attachIssueRepos } from "../agent-issue-repo";
11
11
  import { cleanStringArray } from "../validation";
12
12
  import { emitAgentRemoved, emitAgentStatus } from "../sse";
13
- import { announceSpawnedAgentReady } from "../services/register-agent";
14
- import { flushQueuedDirectMessages } from "../services/managed-running";
15
- import { acquireTeamOnReady } from "../services/team";
16
13
  import { artifactReadScopeForRequest } from "../services/artifact-access";
17
- import { isAgentUnavailable } from "../db/agent-predicates";
18
14
  import { getCompactionWatch } from "../compaction-watch";
19
15
  import { isRecord } from "@humwick/sdk";
20
16
  import { getComponentAuth, hasComponentScope } from "../security";
21
- import { type AgentCard, type AgentSessionGuard } from "../types";
17
+ import { type AgentCard } from "../types";
22
18
  import { attachToolchain } from "../project-toolchain";
23
19
  import { listEffortChangesAsync } from "../commands-write";
24
20
  import { markAgentWorkFinal } from "../services/work-finality";
25
-
26
- type AgentActivityState = {
27
- key: "available" | "busy" | "not-ready" | "offline";
28
- title: string;
29
- icon: string;
30
- };
31
-
32
- function agentActivityState(agent?: Pick<AgentCard, "ready" | "status"> | null): AgentActivityState | null {
33
- if (!agent) return null;
34
- if (agent.status === "offline") return { key: "offline", title: "Agent offline", icon: "ti-plug-off" };
35
- if (!agent.ready) return { key: "not-ready", title: "Agent not ready", icon: "ti-loader" };
36
- if (agent.status === "busy") return { key: "busy", title: "Agent busy", icon: "ti-activity" };
37
- return { key: "available", title: "Agent available", icon: "ti-circle-check" };
38
- }
39
-
40
- async function auditAgentStateTransition(agentId: string, before: AgentCard | null | undefined, after: AgentCard | null | undefined): Promise<void> {
41
- const previous = agentActivityState(before);
42
- const next = agentActivityState(after);
43
- if (!next || previous?.key === next.key) return;
44
- await auditEvent({
45
- clientId: "server-agent-" + agentId + "-state-" + next.key + "-" + Date.now(),
46
- kind: "state",
47
- title: next.title,
48
- meta: agentId,
49
- icon: next.icon,
50
- view: "agents",
51
- agentId,
52
- });
53
- }
21
+ import { authorizeProviderQuotaWrite } from "./provider-quotas";
22
+ import { applyAgentHeartbeat, applyAgentReady, applyAgentStatus, cleanAgentStatus, cleanHeartbeatReport, noteUnchangedLifecycle } from "../services/agent-heartbeat";
54
23
 
55
24
  // #1797 round 2 (F1b) — the agent roster carried `meta.policyName` + `meta.spawnRequestId` in the
56
25
  // clear to any `agent:read` holder. Those two strings are the ADDRESSING for every managed-agent
@@ -248,56 +217,6 @@ export const getTaskArtifactsRoute: Handler = async (req, params) => {
248
217
  return json(await listArtifactsForEntityAsync("task", id, artifactReadScopeForRequest(req)));
249
218
  };
250
219
 
251
- type AgentStatusValue = (typeof VALID_AGENT_STATUSES)[number];
252
-
253
- function cleanAgentStatus(value: unknown): AgentStatusValue {
254
- if (!VALID_AGENT_STATUSES.includes(value as AgentStatusValue)) throw new ValidationError(`status must be one of: ${VALID_AGENT_STATUSES.join(", ")}`);
255
- return value as AgentStatusValue;
256
- }
257
-
258
- // #3606 — a reported status/readiness equal to the live card is a liveness tick, not a transition: no
259
- // row write, no agent.status event. Presence still advances through the coalescing heartbeat buffer,
260
- // except for a terminal row, which a beat would resurrect.
261
- async function noteUnchangedLifecycle(agentId: string, current: AgentCard, guard: AgentSessionGuard | undefined): Promise<void> {
262
- if (current.status !== "offline" && current.status !== "stale") await recordHeartbeatAsync(agentId, { guard });
263
- }
264
-
265
- async function applyAgentStatus(agentId: string, status: AgentStatusValue, guard: AgentSessionGuard | undefined, before: AgentCard | null): Promise<boolean> {
266
- // #1895 (S3.2 L10, review finding 3) — routed through the async gateway facade.
267
- if (!(await setStatusAsync(agentId, status, guard))) return false;
268
- await auditAgentStateTransition(agentId, before, getAgent(agentId));
269
- await emitAgentStatus(agentId);
270
- return true;
271
- }
272
-
273
- async function applyAgentReady(agentId: string, ready: boolean, guard: AgentSessionGuard | undefined, before: AgentCard | null): Promise<boolean> {
274
- // #1895 (S3.2 L10, review finding 3) — routed through the async gateway facade.
275
- if (!(await markReadyAsync(agentId, ready, guard))) return false;
276
- await auditAgentStateTransition(agentId, before, getAgent(agentId));
277
- // #308 — a spawned child flipping false→true ready is "genuinely ready" (past onboarding):
278
- // wake its parent so it can stop block-polling and stamp the durable readiness. #2432 —
279
- // announceSpawnedAgentReady is the shared seam; it no-ops for non-spawned agents and dedups
280
- // repeat readies (e.g. a later bus status frame or PATCH for an already-announced child).
281
- // #1927 review round 2 (HIGH 1) — the audit above now awaits the async gateway, widening the
282
- // window for a concurrent same-instance PATCH to flip ready back to false before we get here.
283
- // Re-read the LIVE card (and re-check the session guard) right before firing these effects
284
- // instead of trusting the pre-audit snapshot, so a superseded transition never fires a false
285
- // durable agent.ready.
286
- if (ready && !before?.ready) {
287
- const live = getAgent(agentId);
288
- const stillCurrentInstance = validateAgentSession(agentId, guard).ok;
289
- if (live?.ready === true && stillCurrentInstance) {
290
- announceSpawnedAgentReady(agentId, "agent-ready");
291
- acquireTeamOnReady(agentId); // #3107 — before the flush, so seat mail reaches a new holder
292
- // #416 — also flush queued direct messages: a not-ready agent that becomes ready
293
- // may have queued messages waiting (e.g. starting up, clearing context).
294
- flushQueuedDirectMessages(agentId);
295
- }
296
- }
297
- await emitAgentStatus(agentId);
298
- return true;
299
- }
300
-
301
220
  export const patchAgentStatus: Handler = async (req, params) => {
302
221
  const parsed = await parseBody<{ status: string }>(req);
303
222
  if (!parsed.ok) return error(parsed.error, parsed.status);
@@ -322,40 +241,19 @@ export const postHeartbeat: Handler = async (req, params) => {
322
241
  const parsed = await parseBody<unknown>(req);
323
242
  if (!parsed.ok) return error(parsed.error, parsed.status);
324
243
  try {
325
- // #3606 — the runner's liveness tick reports status and readiness on this one call (it used to
326
- // be status PATCH + ready PATCH + heartbeat). Only a value that differs from the live card takes
327
- // the transition path; the rest is a coalesced beat.
328
- const body = isRecord(parsed.body) ? parsed.body : {};
329
- const status = body.status === undefined ? undefined : cleanAgentStatus(body.status);
330
- if (body.ready !== undefined && typeof body.ready !== "boolean") return error("ready must be a boolean");
331
- const ready = body.ready as boolean | undefined;
332
- const guard = normalizeAgentSessionGuard(req, parsed.body);
333
- const session = validateAgentSession(params.id!, guard);
334
- if (!session.ok) return error(session.error!, agentSessionStatus(session.error));
335
- // #416 — capture unavailability before heartbeat to detect the transition.
336
- const beforeHeartbeat = getAgent(params.id!);
337
- if (beforeHeartbeat?.kind === "channel" && !beforeHeartbeat.ready) {
338
- return error("channel agent not ready; re-register required", 409);
339
- }
340
- const wasUnavailable = beforeHeartbeat ? isAgentUnavailable(beforeHeartbeat) : false;
341
- let current = beforeHeartbeat;
342
- if (status !== undefined && status !== current?.status) {
343
- if (!(await applyAgentStatus(params.id!, status, guard, current))) return error("agent not found", 404);
344
- current = getAgent(params.id!);
345
- }
346
- if (ready !== undefined && ready !== current?.ready) {
347
- if (!(await applyAgentReady(params.id!, ready, guard, current))) return error("agent not found", 404);
348
- }
349
- // A reported terminal status must not be undone by the beat's offline/stale resurrection.
350
- if (status !== "offline" && status !== "stale") {
351
- // #1468 — route the API heartbeat through the same coalescing buffer as the bus path.
352
- if (!(await recordHeartbeatAsync(params.id!, { guard }))) return error("agent not found", 404);
244
+ // #3606 — the runner's liveness tick reports status and readiness on this one call; the bus
245
+ // `agent.heartbeat` frame (#3618) is the same exchange through the same service.
246
+ const report = cleanHeartbeatReport(params.id!, parsed.body);
247
+ if (report.unread) {
248
+ const denied = await authorizeRoute(req, { scope: "message:read", resource: { agentId: params.id!, target: params.id! } });
249
+ if (denied) return denied;
353
250
  }
354
- if (wasUnavailable) {
355
- const after = getAgent(params.id!);
356
- if (after && !isAgentUnavailable(after)) flushQueuedDirectMessages(params.id!);
251
+ if (report.providerQuota) {
252
+ const denied = (await authorizeRoute(req, { scope: "quota:write", resource: { agentId: params.id! } })) ?? await authorizeProviderQuotaWrite(req, report.providerQuota);
253
+ if (denied) return denied;
357
254
  }
358
- return json({ ok: true });
255
+ const outcome = await applyAgentHeartbeat(params.id!, report, normalizeAgentSessionGuard(req, parsed.body), messageViewerForRequest(req));
256
+ return outcome.ok ? json(outcome.result) : error(outcome.error, outcome.status);
359
257
  } catch (e) {
360
258
  if (e instanceof ValidationError) return error(e.message, 400);
361
259
  throw e;
@@ -439,7 +337,7 @@ export const patchAgentReady: Handler = async (req, params) => {
439
337
  };
440
338
 
441
339
  export const deleteAgentById: Handler = async (_req, params) => {
442
- markAgentWorkFinal(params.id!, "edge-fallback");
340
+ (await markAgentWorkFinal(params.id!, "edge-fallback"));
443
341
  // #1895 (S3.2 L10, review finding 3) — routed through the async gateway facade.
444
342
  const result = await deleteAgentAsync(params.id!);
445
343
  if (!result.ok) {
@@ -3,10 +3,10 @@ import { authorityDenied, recordUserManagement } from "../authority-audit";
3
3
  // (src/index.ts) so they are reachable unauthenticated — each handler owns its own auth: login
4
4
  // verifies credentials, refresh validates the refresh token, logout revokes the presented session.
5
5
  // Everything else on the relay still flows through the existing component-token gate.
6
- import { isRecord, type User } from "@humwick/sdk";
7
- import { error, json, parseBody, type Handler } from "./_shared";
6
+ import { DEFAULT_USER_ID, isRecord, type User } from "@humwick/sdk";
7
+ import { error, isAdminRequest, json, parseBody, requestUserId, type Handler } from "./_shared";
8
8
  import { getAuthProvider, listAuthProviders, mintUserSession, type MintedSession } from "../auth";
9
- import { getAuthIdentity, getUser } from "../db";
9
+ import { getAuthIdentity, getUser, InviteUnavailableError, ValidationError } from "../db";
10
10
  import { revokeTokenAsync } from "../token-db-write.ts";
11
11
  import { refreshUserSession } from "../auth/session-refresh";
12
12
  import { revokeUserSession } from "../auth/session";
@@ -14,7 +14,7 @@ import { readSessionCookie, sessionOriginAllowed, withSessionCookie } from "../a
14
14
  import { getComponentAuth } from "../security";
15
15
  import { acceptInvite, userInvitesEnabled } from "../services/user-onboarding";
16
16
  import { invitesDisabled } from "./users";
17
- import { InviteUnavailableError, ValidationError } from "../db";
17
+ import { mintLoginLink, redeemLoginLink } from "../auth/login-link";
18
18
 
19
19
  /** Public projection of a user for login/refresh responses — never leaks internal-only columns. */
20
20
  function userView(user: User): Record<string, unknown> {
@@ -60,7 +60,7 @@ export const getAuthProvidersRoute: Handler = () =>
60
60
  * session. Every failure returns the same generic 401 (no provider/user/credential enumeration).
61
61
  */
62
62
  export const postAuthLoginRoute: Handler = async (req) => {
63
- if (!sessionOriginAllowed(req, false)) return authorityDenied(req, "origin not allowed", 403);
63
+ if (!sessionOriginAllowed(req, false)) return (await authorityDenied(req, "origin not allowed", 403));
64
64
  const parsed = await parseBody<unknown>(req);
65
65
  if (!parsed.ok) return error(parsed.error, parsed.status);
66
66
  if (!isRecord(parsed.body)) return error("login body required", 400);
@@ -68,33 +68,33 @@ export const postAuthLoginRoute: Handler = async (req) => {
68
68
  if (typeof providerId !== "string" || !providerId) return error("provider required", 400);
69
69
 
70
70
  const provider = getAuthProvider(providerId);
71
- if (!provider) return authorityDenied(req, "invalid credentials", 401);
71
+ if (!provider) return (await authorityDenied(req, "invalid credentials", 401));
72
72
 
73
73
  const result = await provider.verify(parsed.body);
74
- if (!result) return authorityDenied(req, "invalid credentials", 401);
74
+ if (!result) return (await authorityDenied(req, "invalid credentials", 401));
75
75
 
76
76
  const identity = getAuthIdentity(provider.id, result.providerSubject);
77
- if (!identity) return authorityDenied(req, "invalid credentials", 401);
77
+ if (!identity) return (await authorityDenied(req, "invalid credentials", 401));
78
78
 
79
79
  const user = getUser(identity.userId);
80
- if (!user || user.status !== "active") return authorityDenied(req, "invalid credentials", 401);
80
+ if (!user || user.status !== "active") return (await authorityDenied(req, "invalid credentials", 401));
81
81
 
82
- return sessionResponse(req, user, mintUserSession(user));
82
+ return sessionResponse(req, user, (await mintUserSession(user)));
83
83
  };
84
84
 
85
85
  /** POST /api/auth/refresh — cookie only, same-origin, with a short concurrent-request grace. */
86
86
  export const postAuthRefreshRoute: Handler = async (req) => {
87
- if (!sessionOriginAllowed(req)) return authorityDenied(req, "origin not allowed", 403);
87
+ if (!sessionOriginAllowed(req)) return (await authorityDenied(req, "origin not allowed", 403));
88
88
  const refreshToken = readSessionCookie(req);
89
89
  const result = refreshToken ? await refreshUserSession(refreshToken) : null;
90
90
  // A stale response must never delete the cookie just rotated by another tab (#3567).
91
- if (!result) return authorityDenied(req, "invalid refresh token", 401);
91
+ if (!result) return (await authorityDenied(req, "invalid refresh token", 401));
92
92
  return sessionResponse(req, result.user, result.session);
93
93
  };
94
94
 
95
95
  /** POST /api/auth/logout — bearer authenticates the session; the refresh-only cookie is cleared. */
96
96
  export const postAuthLogoutRoute: Handler = async (req) => {
97
- if (!sessionOriginAllowed(req, false)) return authorityDenied(req, "origin not allowed", 403);
97
+ if (!sessionOriginAllowed(req, false)) return (await authorityDenied(req, "origin not allowed", 403));
98
98
  const component = getComponentAuth(req);
99
99
  if (component?.jti && !(await revokeUserSession(component))) await revokeTokenAsync(component.jti, "admin");
100
100
  return withSessionCookie(req, new Response(null, { status: 204 }));
@@ -105,7 +105,7 @@ export const postAuthLogoutRoute: Handler = async (req) => {
105
105
  * the single-use code is the credential. Creates the user and returns their first session.
106
106
  */
107
107
  export const postAcceptInviteRoute: Handler = async (req) => {
108
- if (!sessionOriginAllowed(req, false)) return authorityDenied(req, "origin not allowed", 403);
108
+ if (!sessionOriginAllowed(req, false)) return (await authorityDenied(req, "origin not allowed", 403));
109
109
  if (!userInvitesEnabled()) return invitesDisabled();
110
110
  const parsed = await parseBody<unknown>(req);
111
111
  if (!parsed.ok) return error(parsed.error, parsed.status);
@@ -113,11 +113,38 @@ export const postAcceptInviteRoute: Handler = async (req) => {
113
113
  const body = parsed.body;
114
114
  try {
115
115
  const user = await acceptInvite({ code: body.code, handle: body.handle, password: body.password, displayName: typeof body.displayName === "string" ? body.displayName.trim().slice(0, 120) || undefined : undefined });
116
- recordUserManagement(req, user.id, "accept invite", { role: user.role }, user.id);
117
- return sessionResponse(req, user, mintUserSession(user), 201);
116
+ (await recordUserManagement(req, user.id, "accept invite", { role: user.role }, user.id));
117
+ return sessionResponse(req, user, (await mintUserSession(user)), 201);
118
118
  } catch (e) {
119
- if (e instanceof InviteUnavailableError) return authorityDenied(req, e.message, 410);
119
+ if (e instanceof InviteUnavailableError) return (await authorityDenied(req, e.message, 410));
120
120
  if (e instanceof ValidationError) return error(e.message, 400);
121
121
  throw e;
122
122
  }
123
123
  };
124
+
125
+ /**
126
+ * POST /api/auth/login-links — admin only (#2680). Body `{ userId? }`, default the seeded admin user.
127
+ * `humwick init` calls this with the admin token so the first login needs no token at all.
128
+ */
129
+ export const postLoginLinkRoute: Handler = async (req) => {
130
+ if (!isAdminRequest(req)) return (await authorityDenied(req, "only an admin can mint a login link"));
131
+ const parsed = await parseBody<unknown>(req);
132
+ if (!parsed.ok) return error(parsed.error, parsed.status);
133
+ const userId = isRecord(parsed.body) && typeof parsed.body.userId === "string" ? parsed.body.userId : DEFAULT_USER_ID;
134
+ const user = getUser(userId);
135
+ if (!user || user.status !== "active") return error("user not found or not active", 404);
136
+ const link = await mintLoginLink(user, `auth:login-link:${requestUserId(req)}`);
137
+ recordUserManagement(req, user.id, "mint login link");
138
+ return json(link, 201);
139
+ };
140
+
141
+ /** POST /api/auth/login-links/redeem — public; the single-use link code is the credential. */
142
+ export const postRedeemLoginLinkRoute: Handler = async (req) => {
143
+ if (!sessionOriginAllowed(req, false)) return (await authorityDenied(req, "origin not allowed", 403));
144
+ const parsed = await parseBody<unknown>(req);
145
+ if (!parsed.ok) return error(parsed.error, parsed.status);
146
+ const code = isRecord(parsed.body) ? parsed.body.code : undefined;
147
+ const user = typeof code === "string" && code ? await redeemLoginLink(code) : null;
148
+ if (!user) return (await authorityDenied(req, "login link is invalid, expired or already used", 401));
149
+ return sessionResponse(req, user, (await mintUserSession(user)));
150
+ };
@@ -5,9 +5,9 @@ import { isAdminRequest } from "./users";
5
5
 
6
6
  /** GET /api/users/authority-denials — private, bounded admin audit list. */
7
7
  export const getAuthorityDenials: Handler = async (req) => {
8
- const denied = authorizeRoute(req, { scope: "system:admin" });
8
+ const denied = (await authorizeRoute(req, { scope: "system:admin" }));
9
9
  if (denied) return denied;
10
- if (!isAdminRequest(req)) return authorityDenied(req, "only admins can read authority denials");
10
+ if (!isAdminRequest(req)) return (await authorityDenied(req, "only admins can read authority denials"));
11
11
  const limit = parseQueryInt(new URL(req.url).searchParams.get("limit"), { min: 1, max: 500 });
12
12
  if (Number.isNaN(limit)) return error("limit must be an integer between 1 and 500");
13
13
  return json(await listActivityEventsAsync({ title: AUTHORITY_DENIED, limit: limit ?? 100 }));
@@ -1,6 +1,7 @@
1
1
  // Auto-split from routes.ts (#299). Domain: automations.
2
2
  import { ValidationError } from "../db";
3
- import { createAutomationAsync, deleteAutomation, getAutomation, listAutomationRuns, listAutomations, runAutomationNowAsync, updateAutomationAsync, type AutomationDispatchResult } from "../automations";
3
+ import { listAutomationRunsAsync } from "../automations/read-async";
4
+ import { createAutomationAsync, deleteAutomationAsync, getAutomation, listAutomations, runAutomationNowAsync, updateAutomationAsync, type AutomationDispatchResult } from "../automations";
4
5
  import { emitCommand, error, json, parseBody, parseQueryInt, type Handler } from "./_shared";
5
6
  import { emitNewMessage, emitTaskChanged } from "../sse";
6
7
  import { emitRelayEvent } from "../events";
@@ -53,8 +54,8 @@ export const patchAutomation: Handler = async (req, params) => {
53
54
  }
54
55
  };
55
56
 
56
- export const deleteAutomationById: Handler = (_req, params) => {
57
- if (!deleteAutomation(params.id!)) return error("automation not found", 404);
57
+ export const deleteAutomationById: Handler = async (_req, params) => {
58
+ if (!(await deleteAutomationAsync(params.id!))) return error("automation not found", 404);
58
59
  emitRelayEvent({ type: "automation.deleted", source: "server", subject: params.id!, data: { automationId: params.id! } });
59
60
  return json({ ok: true });
60
61
  };
@@ -71,11 +72,11 @@ export const postAutomationRun: Handler = async (_req, params) => {
71
72
  }
72
73
  };
73
74
 
74
- export const getAutomationRuns: Handler = (req) => {
75
+ export const getAutomationRuns: Handler = async (req) => {
75
76
  const url = new URL(req.url);
76
77
  const limitRaw = parseQueryInt(url.searchParams.get("limit"), { min: 1, max: 500 });
77
78
  if (Number.isNaN(limitRaw)) return error("limit must be an integer between 1 and 500");
78
- return json(listAutomationRuns({
79
+ return json(await listAutomationRunsAsync({
79
80
  automationId: url.searchParams.get("automationId") ?? undefined,
80
81
  status: url.searchParams.get("status") ?? undefined,
81
82
  limit: limitRaw ?? 100,
@@ -5,7 +5,7 @@ import { ValidationError } from "../db/connection";
5
5
  import { authorizeRoute, error, json, normalizeAgentSessionGuard, parseBody, type Handler } from "./_shared";
6
6
 
7
7
  export const deleteChannelBinding: Handler = async (req, params) => {
8
- const denied = authorizeRoute(req, { scope: "channel:write" });
8
+ const denied = (await authorizeRoute(req, { scope: "channel:write" }));
9
9
  if (denied) return denied;
10
10
  const parsed = await parseBody<unknown>(req);
11
11
  if (!parsed.ok) return error(parsed.error, parsed.status);
@@ -3,12 +3,12 @@ import { authContextFromRequest } from "../services/auth-context";
3
3
  import { ValidationError } from "../db/connection";
4
4
  import { authorizeRoute, error, json, parseBody, type Handler } from "./_shared";
5
5
 
6
- export const getChannelOutput: Handler = (req, params) => {
7
- const denied = authorizeRoute(req, { scope: "channel:read", resource: { channel: params.id } });
6
+ export const getChannelOutput: Handler = async (req, params) => {
7
+ const denied = (await authorizeRoute(req, { scope: "channel:read", resource: { channel: params.id } }));
8
8
  return denied ?? json(channelOutputControl(params.id!));
9
9
  };
10
10
  export const postChannelOutput: Handler = async (req, params) => {
11
- const denied = authorizeRoute(req, { scope: "channel:egress", resource: { channel: params.id } });
11
+ const denied = (await authorizeRoute(req, { scope: "channel:egress", resource: { channel: params.id } }));
12
12
  if (denied) return denied;
13
13
  const parsed = await parseBody<unknown>(req);
14
14
  if (!parsed.ok) return error(parsed.error, parsed.status);
@@ -5,7 +5,7 @@ import { authorizeRoute, error, json, parseBody, type Handler } from "./_shared"
5
5
  import { apiBudgetId, isRecord } from "@humwick/sdk";
6
6
 
7
7
  export const apiBudgetStatusRoute: Handler = async (req, params) => {
8
- const denied = authorizeRoute(req, { scope: "system:admin" });
8
+ const denied = (await authorizeRoute(req, { scope: "system:admin" }));
9
9
  if (denied) return denied;
10
10
  try { return json(await apiBudgetStatusAsync(apiBudgetId(params.budgetId))); }
11
11
  catch { return error("Unknown API budget", 404); }
@@ -16,7 +16,7 @@ export const apiBudgetStatusRoute: Handler = async (req, params) => {
16
16
  * it returns the status snapshot only and exposes no mutation.
17
17
  */
18
18
  export const apiBudgetSummaryRoute: Handler = async (req, params) => {
19
- const denied = authorizeRoute(req, { scope: "agent:read" });
19
+ const denied = (await authorizeRoute(req, { scope: "agent:read" }));
20
20
  if (denied) return denied;
21
21
  try {
22
22
  const summary: ApiBudgetSummary = await apiBudgetStatusAsync(apiBudgetId(params.budgetId));
@@ -24,7 +24,7 @@ export const apiBudgetSummaryRoute: Handler = async (req, params) => {
24
24
  } catch { return error("Unknown API budget", 404); }
25
25
  };
26
26
  export const apiBudgetConfigureRoute: Handler = async (req, params) => {
27
- const denied = authorizeRoute(req, { scope: "system:admin" });
27
+ const denied = (await authorizeRoute(req, { scope: "system:admin" }));
28
28
  if (denied) return denied;
29
29
  const body = await parseBody<unknown>(req);
30
30
  if (!body.ok) return error(body.error, body.status);
@@ -37,7 +37,7 @@ export const apiBudgetConfigureRoute: Handler = async (req, params) => {
37
37
  export const apiBudgetSessionRoute: Handler = async (req, params) => {
38
38
  if (params.action !== "reserve" && params.action !== "report") return error("Unknown budget operation", 404);
39
39
  const agentId = params.id!;
40
- const denied = authorizeRoute(req, { scope: "agent:write", resource: { agentId } });
40
+ const denied = (await authorizeRoute(req, { scope: "agent:write", resource: { agentId } }));
41
41
  if (denied) return denied;
42
42
  const body = await parseBody<unknown>(req);
43
43
  if (!body.ok) return error(body.error, body.status);
@@ -55,7 +55,7 @@ export const apiBudgetSessionRoute: Handler = async (req, params) => {
55
55
  };
56
56
 
57
57
  export const apiBudgetAgentRoute: Handler = async (req, params) => {
58
- const denied = authorizeRoute(req, { scope: "agent:read", resource: { agentId: params.id! } });
58
+ const denied = (await authorizeRoute(req, { scope: "agent:read", resource: { agentId: params.id! } }));
59
59
  if (denied) return denied;
60
60
  try {
61
61
  const budgetId = apiBudgetId(new URL(req.url).searchParams.get("budgetId") ?? "openai-api");
@@ -42,7 +42,7 @@ export const getCommands: Handler = async (req) => {
42
42
 
43
43
  // The host only needs existence, not historical command params/results (#3063).
44
44
  export const getActiveCleanup: Handler = async (req) => {
45
- const denied = authorizeRoute(req, { scope: "command:read" });
45
+ const denied = (await authorizeRoute(req, { scope: "command:read" }));
46
46
  if (denied) return denied;
47
47
  try {
48
48
  const target = cleanString(new URL(req.url).searchParams.get("target"), "target", { required: true, max: 240 });