@humwick/server 0.130.2 → 0.130.3

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 (570) 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 +73 -4
  8. package/package.json +8 -7
  9. package/public/assets/{MessageBubble-BWLqMGKG.js → MessageBubble-Bq9kr21N.js} +2 -2
  10. package/public/assets/{PlanGraphPanel-3TlIQpjk.js → PlanGraphPanel-Dqiroy06.js} +2 -2
  11. package/public/assets/{TraceView-Wimm0Czv.js → TraceView-DYU962-H.js} +2 -2
  12. package/public/assets/{VaultDecisionCard-Bau_vNax.js → VaultDecisionCard-DhPfzioE.js} +2 -2
  13. package/public/assets/{activity-CrSurn-r.js → activity-DzCcQTee.js} +2 -2
  14. package/public/assets/{agent-lineage-CknAJKcb.js → agent-lineage-SgDt9i3T.js} +2 -2
  15. package/public/assets/agent-liveness-Bq-s73jW.js +2 -0
  16. package/public/assets/{agent-name-DKbiIhBo.js → agent-name-CcY3Aijy.js} +2 -2
  17. package/public/assets/{agent-profiles-DwF6EQ4R.js → agent-profiles-cxtRBn6O.js} +2 -2
  18. package/public/assets/{agent-project-badge-Diz4Sjjb.js → agent-project-badge-DGYbIhaI.js} +2 -2
  19. package/public/assets/{agent-tools-CRhpRQza.js → agent-tools-DHbftV0N.js} +2 -2
  20. package/public/assets/{agent-type-icon-LrUEU3lB.js → agent-type-icon-CY113sp-.js} +2 -2
  21. package/public/assets/{agents-B-v66C-4.js → agents-BPzY7IlQ.js} +2 -2
  22. package/public/assets/{analytics-BqCb-LKi.js → analytics-DENFPv_a.js} +2 -2
  23. package/public/assets/{artifact-viewer-kf4pGuAR.js → artifact-viewer-KRvSc7sl.js} +2 -2
  24. package/public/assets/{artifacts-DJevXcvg.js → artifacts-C9gSmL3s.js} +2 -2
  25. package/public/assets/{artifacts-D32zyw4Y.js → artifacts-DT-HkI5O.js} +2 -2
  26. package/public/assets/{automation-BpRajx_5.js → automation-BsmjQhKD.js} +2 -2
  27. package/public/assets/{backlog-D_UjTdzX.js → backlog-8NYH_62t.js} +2 -2
  28. package/public/assets/{busy-health-N3VEh4ao.js → busy-health-D3hwvYb4.js} +2 -2
  29. package/public/assets/{channels-9jopungq.js → channels-DCJ5WL7O.js} +2 -2
  30. package/public/assets/{chat-CSXrTB3C.js → chat-DAIl357U.js} +3 -3
  31. package/public/assets/{code-preview-p1NnmqK0.js → code-preview-CzrZfr9U.js} +2 -2
  32. package/public/assets/{connectors-CZdqlDNL.js → connectors-xTxtlyou.js} +2 -2
  33. package/public/assets/{cosmos-BgdsE4dG.js → cosmos-BpebFLK2.js} +2 -2
  34. package/public/assets/{crate-agent-30aXTKva.js → crate-agent-BHvI5wXC.js} +2 -2
  35. package/public/assets/{dependency-graph-B_jvw2GE.js → dependency-graph-DGT0o3wJ.js} +2 -2
  36. package/public/assets/{deploys-DoN6bAb5.js → deploys-hqWOHSHE.js} +2 -2
  37. package/public/assets/{display-BrKWFGJz.js → display-CDeH4I8T.js} +2 -2
  38. package/public/assets/{files-D6XEu8Ng.js → files-BTeGVmTg.js} +2 -2
  39. package/public/assets/{fleet-C7jQWbw2.js → fleet-DgXtNgCa.js} +2 -2
  40. package/public/assets/{fleet-metrics-BiDs3MLx.js → fleet-metrics-BHEt5yc_.js} +2 -2
  41. package/public/assets/{formatted-body-CQBwc1fb.js → formatted-body-MLtodyPp.js} +3 -3
  42. package/public/assets/{formatted-body-impl-DyG0WsQS.js → formatted-body-impl-CAe664Z8.js} +2 -2
  43. package/public/assets/{host-providers-dialog-DKaW31dN.js → host-providers-dialog-mapF4Fj9.js} +2 -2
  44. package/public/assets/{index-CRHep3M9.js → index-CqqgOI5R.js} +5 -5
  45. package/public/assets/{insights-CzrtkpOE.js → insights-DNyf7IzF.js} +2 -2
  46. package/public/assets/{integrations-aVsUmigt.js → integrations-DlYOHq7T.js} +2 -2
  47. package/public/assets/{issue-ref-chip-BnSlM_95.js → issue-ref-chip-CeWBNWn7.js} +2 -2
  48. package/public/assets/{log-viewer-qe7Ee47Z.js → log-viewer-Cib8nqdE.js} +2 -2
  49. package/public/assets/{maintenance-NfjqFZAk.js → maintenance-DSn1cPm7.js} +2 -2
  50. package/public/assets/{managed-agents-BiH0HgaE.js → managed-agents-C3CkM81m.js} +2 -2
  51. package/public/assets/{markdown-preview-BixaT3Xr.js → markdown-preview-W4VQQPAv.js} +3 -3
  52. package/public/assets/{markdown-preview-impl-DNGlWIS3.js → markdown-preview-impl-CMsTdhjq.js} +2 -2
  53. package/public/assets/{memory-DNsCl6UX.js → memory-BV17qtO4.js} +2 -2
  54. package/public/assets/{messages-DopU1L1X.js → messages-Bm_1AqSA.js} +2 -2
  55. package/public/assets/{orchestrators-Cq7ckLQT.js → orchestrators-Dg-vfP09.js} +2 -2
  56. package/public/assets/{overview-C1DCTzzV.js → overview-DcYCrvlr.js} +2 -2
  57. package/public/assets/{pairs-DE_m6rrE.js → pairs-8o9nuG3V.js} +2 -2
  58. package/public/assets/{profile-icon-B3rfh77F.js → profile-icon-BsRyBMea.js} +2 -2
  59. package/public/assets/{project-profile-instructions-DQWZE0kA.js → project-profile-instructions-DETKFn_E.js} +2 -2
  60. package/public/assets/{projects-Br2Bd2h1.js → projects-M_m6AHws.js} +2 -2
  61. package/public/assets/{prompt-settings-BKvFsvGD.js → prompt-settings-C_mNCgeL.js} +2 -2
  62. package/public/assets/provider-display-BEeLXC_D.js +2 -0
  63. package/public/assets/{registry-Cw38_6gW.js → registry-DDHHHE5N.js} +2 -2
  64. package/public/assets/run-card-Dt4I4jaP.js +2 -0
  65. package/public/assets/{secrets-DxI-vzSq.js → secrets-BkVwxI5O.js} +2 -2
  66. package/public/assets/{service-access-settings-D9s_VJbd.js → service-access-settings-Crq-6a-B.js} +2 -2
  67. package/public/assets/{settings-CKZTVGIx.js → settings-CYcavMFu.js} +2 -2
  68. package/public/assets/shipping-H6gzEeZB.js +2 -0
  69. package/public/assets/{stats-DyUBKyWI.js → stats-BJVeWkpV.js} +2 -2
  70. package/public/assets/{status-dot-DYqlEUhJ.js → status-dot-BcHyw1Vw.js} +2 -2
  71. package/public/assets/store-DKPDIZBe.js +9 -0
  72. package/public/assets/{tasks-Bh7Chr4-.js → tasks-qJjlQPpx.js} +2 -2
  73. package/public/assets/{teams-bowI7qVY.js → teams-Cd5Sk1uX.js} +2 -2
  74. package/public/assets/{terminal-viewer-BgVLHdsU.js → terminal-viewer-CpY5_m7W.js} +3 -3
  75. package/public/assets/{terminal-viewer-impl-Dz22UPy6.js → terminal-viewer-impl-DI_2llFR.js} +2 -2
  76. package/public/assets/{use-agent-terminal-DSQQ-iY7.js → use-agent-terminal-0yJA_buM.js} +2 -2
  77. package/public/assets/{use-decisions-samMQhQf.js → use-decisions-uJXHf34W.js} +2 -2
  78. package/public/assets/{use-project-list-59LheVP7.js → use-project-list-CpWl6eHQ.js} +2 -2
  79. package/public/assets/{use-selectors-BZa25xEB.js → use-selectors-BdxBW8mL.js} +2 -2
  80. package/public/assets/{user-avatar-DHxlvUvV.js → user-avatar-3IndfpOc.js} +2 -2
  81. package/public/assets/{versioned-prompt-editor-BmED5DnJ.js → versioned-prompt-editor-CIVg7XB1.js} +2 -2
  82. package/public/assets/{work-queue-CSvpuNLz.js → work-queue-B9rnLwHZ.js} +2 -2
  83. package/public/assets/{workflow-marker-DAZcj5qt.js → workflow-marker-X8wxyhzC.js} +2 -2
  84. package/public/assets/{workflows-DdmxRSyg.js → workflows-o5YyMFW7.js} +2 -2
  85. package/public/assets/{workspaces-BxeKfVuF.js → workspaces-CzEGoAng.js} +2 -2
  86. package/public/cosmos.html +4 -4
  87. package/public/index.html +28 -28
  88. package/runner/plugins/claude/.claude-plugin/plugin.json +1 -1
  89. package/runner/src/adapters/claude-headless.ts +1 -1
  90. package/runner/src/adapters/claude-launch-exit.ts +26 -0
  91. package/runner/src/adapters/claude-session-probe.ts +1 -1
  92. package/runner/src/adapters/claude-tmux.ts +38 -10
  93. package/runner/src/adapters/claude.ts +28 -75
  94. package/runner/src/adapters/provider-quota-harvest.ts +20 -71
  95. package/runner/src/agent-runner/lifecycle-milestones.ts +2 -2
  96. package/runner/src/agent-runner/liveness-exchange.ts +104 -0
  97. package/runner/src/claude-system-prompt.ts +49 -0
  98. package/runner/src/launch-assembly.ts +14 -10
  99. package/runner/src/profile-home.ts +1 -1
  100. package/runner/src/project-instructions.ts +2 -2
  101. package/runner/src/provider-command.ts +18 -0
  102. package/runner/src/provider-setup.ts +2 -1
  103. package/runner/src/runner-core.ts +14 -15
  104. package/runner/src/runner-helpers.ts +0 -8
  105. package/runner/src/session-scratch.ts +6 -2
  106. package/runner/test-support/provisioning-context.ts +4 -2
  107. package/runner/test-support/runner-harness.ts +2 -1
  108. package/scripts/steward-policy-cleanup.ts +2 -2
  109. package/src/agent-authz.ts +2 -2
  110. package/src/agent-drive-arbiter.ts +3 -8
  111. package/src/agent-exit-cleanup-async.ts +2 -2
  112. package/src/agent-exit-cleanup.ts +23 -19
  113. package/src/agent-lifecycle-events.ts +112 -85
  114. package/src/agent-ref.ts +3 -2
  115. package/src/agent-status-events.ts +43 -14
  116. package/src/auth/password.ts +4 -3
  117. package/src/auth/session-refresh.ts +1 -1
  118. package/src/auth/session.test-fixture.ts +2 -2
  119. package/src/auth/session.ts +7 -7
  120. package/src/automations/crud.ts +8 -81
  121. package/src/automations/dispatch.ts +33 -58
  122. package/src/automations/index.ts +1 -1
  123. package/src/automations/persistence.ts +65 -0
  124. package/src/automations/precheck.ts +18 -16
  125. package/src/automations/read-async.ts +11 -0
  126. package/src/automations/reconcile.ts +30 -39
  127. package/src/automations/storage.ts +33 -10
  128. package/src/automations/write-async.ts +33 -0
  129. package/src/branch-land-notice.ts +3 -3
  130. package/src/branch-landed.ts +25 -23
  131. package/src/bus.ts +21 -19
  132. package/src/cd-deploy-notify.ts +2 -2
  133. package/src/ci/notifications.ts +4 -4
  134. package/src/ci/root-missing.ts +3 -3
  135. package/src/ci/transition-notify.ts +5 -5
  136. package/src/claude-app/oauth-http.ts +3 -3
  137. package/src/cli/workflow.ts +12 -3
  138. package/src/commands-db.ts +4 -4
  139. package/src/commands-write.ts +8 -0
  140. package/src/compaction-watch.ts +10 -8
  141. package/src/config-store-write.ts +2 -2
  142. package/src/config.ts +4 -11
  143. package/src/connectors.ts +10 -8
  144. package/src/context-advisory.ts +22 -21
  145. package/src/db/agent-action-write.ts +36 -0
  146. package/src/db/agent-control-async.ts +9 -0
  147. package/src/db/agent-control-write.ts +49 -0
  148. package/src/db/agent-effort-observe.ts +22 -0
  149. package/src/db/agent-meta.ts +14 -3
  150. package/src/db/agents-async.ts +8 -5
  151. package/src/db/agents.ts +15 -12
  152. package/src/db/auth-identities.ts +2 -2
  153. package/src/db/automation-read-ops.ts +11 -0
  154. package/src/db/automation-write-ops.ts +46 -0
  155. package/src/db/blackboard.ts +2 -2
  156. package/src/db/browser-session-write.ts +32 -0
  157. package/src/db/bus-outbox-write-ops.ts +3 -1
  158. package/src/db/checkout-refresh-schema.ts +47 -0
  159. package/src/db/checkout-refresh.ts +5 -5
  160. package/src/db/command-agent-profile-backfill.ts +39 -0
  161. package/src/db/command-agent-profiles.ts +5 -1
  162. package/src/db/command-retention.ts +126 -0
  163. package/src/db/commands-planner-stats.test-fixture.ts +8 -0
  164. package/src/db/commands-read-ops.ts +16 -0
  165. package/src/db/connection.ts +1 -182
  166. package/src/db/context-carry-write.ts +74 -0
  167. package/src/db/continuations.ts +4 -4
  168. package/src/db/db-error-registry.ts +12 -0
  169. package/src/db/gateway.ts +26 -133
  170. package/src/db/history-retention-ops.ts +13 -0
  171. package/src/db/inbox-async.ts +2 -2
  172. package/src/db/inbox-read-ops.ts +1 -1
  173. package/src/db/inbox.ts +9 -3
  174. package/src/db/issue-associations.ts +2 -2
  175. package/src/db/issue-refs-async.ts +4 -1
  176. package/src/db/issue-refs-read-ops.ts +6 -0
  177. package/src/db/issue-refs.ts +8 -8
  178. package/src/db/kernel-event-log-schema.ts +2 -0
  179. package/src/db/land-boundaries.ts +51 -5
  180. package/src/db/land-gate-attestations.ts +2 -2
  181. package/src/db/landing-receipts.ts +2 -2
  182. package/src/db/lifecycle-context-write.ts +5 -0
  183. package/src/db/lifecycle-tail-async.ts +76 -0
  184. package/src/db/lifecycle-tail-write-ops.ts +62 -0
  185. package/src/db/lifecycle-write-window.ts +2 -1
  186. package/src/db/lineage-pair-dedupe-sql.ts +3 -3
  187. package/src/db/main-connection-mode.ts +11 -0
  188. package/src/db/main-connection-writes.ts +20 -2
  189. package/src/db/maintenance-job-runs-async.ts +2 -2
  190. package/src/db/maintenance-job-write-ops.ts +1 -1
  191. package/src/db/maintenance-pauses.ts +2 -2
  192. package/src/db/message-send.ts +4 -0
  193. package/src/db/messages.ts +31 -25
  194. package/src/db/migrations.ts +18 -1
  195. package/src/db/notification-write-ops.ts +19 -0
  196. package/src/db/operator-answer-write.ts +19 -0
  197. package/src/db/orchestrators.ts +10 -4
  198. package/src/db/pairs-async.ts +22 -1
  199. package/src/db/pairs-write-ops.ts +8 -3
  200. package/src/db/pairs.ts +41 -48
  201. package/src/db/plan-dispatch-schema.ts +5 -1
  202. package/src/db/plan-ready-notices.ts +43 -0
  203. package/src/db/plan-tools-include-migration.ts +36 -0
  204. package/src/db/plans-write-ops.ts +2 -0
  205. package/src/db/presence-buffer.ts +5 -0
  206. package/src/db/presence-loop.ts +18 -23
  207. package/src/db/presence-write.ts +0 -6
  208. package/src/db/project-entries-async.ts +14 -0
  209. package/src/db/project-entries-read-ops.ts +5 -1
  210. package/src/db/project-entries-write-ops.ts +8 -0
  211. package/src/db/project-entries.ts +15 -12
  212. package/src/db/project-journal.ts +2 -2
  213. package/src/db/project-trust-approvals.ts +2 -2
  214. package/src/db/prompt-templates.ts +4 -4
  215. package/src/db/read-op-marshaling-fixtures.ts +18 -0
  216. package/src/db/read-ops.ts +4 -0
  217. package/src/db/retention.ts +13 -2
  218. package/src/db/review-publish-write.ts +8 -0
  219. package/src/db/runtime-admin-async.ts +19 -0
  220. package/src/db/runtime-admin-write-ops.ts +36 -0
  221. package/src/db/runtime-tail-async.ts +288 -0
  222. package/src/db/runtime-tail-write-ops.ts +97 -0
  223. package/src/db/schema.ts +4 -1
  224. package/src/db/spawn-budget.ts +5 -0
  225. package/src/db/spawn-delegation.ts +1 -11
  226. package/src/db/sql-audit-indexes.ts +3 -0
  227. package/src/db/stats-async.ts +5 -3
  228. package/src/db/task-entity.ts +5 -4
  229. package/src/db/team-claim-policy.ts +60 -0
  230. package/src/db/teams-async.ts +4 -0
  231. package/src/db/teams-write-ops.ts +4 -0
  232. package/src/db/terminal-events.ts +15 -0
  233. package/src/db/timer-cutover-bodies.ts +292 -0
  234. package/src/db/timer-cutover-core.ts +37 -278
  235. package/src/db/timer-cutover-state.ts +40 -0
  236. package/src/db/timer-cutover-write-ops.ts +1 -40
  237. package/src/db/token-db-write-ops.ts +2 -0
  238. package/src/db/token-standing-async.ts +12 -0
  239. package/src/db/token-standing.ts +6 -9
  240. package/src/db/token-usage-retention.ts +33 -0
  241. package/src/db/token-usage-write-ops.ts +1 -1
  242. package/src/db/token-usage.ts +4 -1
  243. package/src/db/user-invites-async.ts +13 -0
  244. package/src/db/user-invites.ts +3 -3
  245. package/src/db/users.ts +14 -6
  246. package/src/db/workspaces.ts +8 -5
  247. package/src/db/write-ops.ts +31 -12
  248. package/src/db/writer-fault-seam.ts +2 -9
  249. package/src/db/writer-fault-test-ops.ts +18 -0
  250. package/src/db/writer-gateway.ts +42 -46
  251. package/src/dev.ts +8 -3
  252. package/src/event-loop-lag.ts +4 -2
  253. package/src/event-loop-yield.ts +2 -2
  254. package/src/events.ts +10 -8
  255. package/src/host-provider-store.ts +5 -4
  256. package/src/hub-metrics-report.ts +5 -1
  257. package/src/hub-metrics.ts +38 -18
  258. package/src/injection-sweep-pacing.ts +6 -8
  259. package/src/land-gate-notifications.ts +11 -10
  260. package/src/land-gate-queue-progress.ts +3 -3
  261. package/src/land-settle-dispatch.ts +52 -92
  262. package/src/lifecycle-context-pressure.ts +11 -10
  263. package/src/lifecycle-manager.ts +104 -98
  264. package/src/maintenance/breaker.ts +18 -16
  265. package/src/maintenance/checkout-refresh.ts +18 -1
  266. package/src/maintenance/critical-job-watchdog.ts +3 -3
  267. package/src/maintenance/events.ts +3 -3
  268. package/src/maintenance/history-retention-job.ts +30 -0
  269. package/src/maintenance/jobs.ts +29 -67
  270. package/src/maintenance/long-running-work.ts +3 -1
  271. package/src/maintenance/offline-agent-prune-job.ts +28 -0
  272. package/src/maintenance/orphaned-sessions.ts +5 -4
  273. package/src/maintenance/pauses.ts +4 -10
  274. package/src/maintenance/plan-ready-notice-job.ts +71 -0
  275. package/src/maintenance/scheduler.ts +18 -13
  276. package/src/maintenance/stalled-delivery-pager.ts +4 -4
  277. package/src/maintenance/teams.ts +19 -30
  278. package/src/maintenance/workspaces.ts +17 -27
  279. package/src/managed-policy-escalation.ts +7 -7
  280. package/src/managed-policy.ts +3 -3
  281. package/src/managed-provider-gate.ts +5 -4
  282. package/src/managed-spawn-reservation.ts +5 -4
  283. package/src/mcp/index.ts +8 -8
  284. package/src/mcp/output-descriptions.ts +1 -1
  285. package/src/mcp/presentation.ts +2 -1
  286. package/src/mcp/runtime-output.ts +6 -1
  287. package/src/mcp/self-care-visibility.ts +2 -2
  288. package/src/mcp/spawn-targets-matrix.ts +10 -1
  289. package/src/mcp/tool-groups.ts +3 -2
  290. package/src/mcp/tool-roles.ts +81 -0
  291. package/src/mcp/tools-workspace.ts +1 -1
  292. package/src/mcp-blackboard-tools.ts +6 -5
  293. package/src/mcp-decision-tools.ts +7 -7
  294. package/src/mcp-egress-tools.ts +10 -9
  295. package/src/mcp-issue-activity-tools.ts +2 -2
  296. package/src/mcp-issue-association-tools.ts +4 -4
  297. package/src/mcp-issue-ref-tools.ts +3 -3
  298. package/src/mcp-merge-tools.ts +5 -5
  299. package/src/mcp-notification-subscription-tools.ts +14 -15
  300. package/src/mcp-output-levels.test-fixture.ts +1 -1
  301. package/src/mcp-profile-trust-tools.ts +1 -1
  302. package/src/mcp-project-journal-tools.ts +4 -12
  303. package/src/mcp-project-tools.ts +17 -18
  304. package/src/mcp-routing-policy-tools.ts +3 -2
  305. package/src/mcp-team-tools.ts +6 -6
  306. package/src/mcp-workflow-tools.ts +5 -3
  307. package/src/memory-area-injection.ts +13 -12
  308. package/src/memory-boundary-capture.ts +6 -6
  309. package/src/memory-injection.ts +45 -43
  310. package/src/merge-pause-maintenance.ts +5 -4
  311. package/src/notification-router.ts +44 -17
  312. package/src/notify.ts +5 -5
  313. package/src/observed-reset-refresh.ts +4 -4
  314. package/src/operator-alarm.ts +2 -5
  315. package/src/project-ingest.ts +6 -6
  316. package/src/project-instructions.ts +8 -1
  317. package/src/project-trust.ts +12 -11
  318. package/src/prompt-resolver.ts +3 -3
  319. package/src/provider-quota-async.ts +5 -7
  320. package/src/provider-timeline-event.ts +4 -7
  321. package/src/quota-band-delivery.ts +2 -2
  322. package/src/recipe-runner.ts +4 -3
  323. package/src/retention-config-store.ts +4 -3
  324. package/src/reviewer-pipeline.ts +11 -10
  325. package/src/routes/_route.ts +4 -4
  326. package/src/routes/_shared.ts +13 -12
  327. package/src/routes/agent-effort.ts +5 -5
  328. package/src/routes/agent-heartbeat-quota.ts +23 -0
  329. package/src/routes/agent-keep.ts +1 -1
  330. package/src/routes/agent-reporting.ts +1 -1
  331. package/src/routes/agent-self-effort.ts +5 -5
  332. package/src/routes/agent-sessions.ts +24 -24
  333. package/src/routes/agents-spawn.ts +1 -1
  334. package/src/routes/agents.ts +17 -119
  335. package/src/routes/auth.ts +14 -15
  336. package/src/routes/authority-audit.ts +2 -2
  337. package/src/routes/automations.ts +6 -5
  338. package/src/routes/channel-binding-delete.ts +1 -1
  339. package/src/routes/channel-output.ts +3 -3
  340. package/src/routes/claude-api-budget.ts +5 -5
  341. package/src/routes/commands-listing.ts +1 -1
  342. package/src/routes/commands.ts +29 -27
  343. package/src/routes/context-resets.ts +2 -2
  344. package/src/routes/coordinator-teams.ts +1 -1
  345. package/src/routes/decisions.ts +2 -2
  346. package/src/routes/host-secret-files.ts +1 -1
  347. package/src/routes/hosts.ts +11 -11
  348. package/src/routes/inbox.ts +3 -2
  349. package/src/routes/index.ts +2 -1
  350. package/src/routes/integrations.ts +9 -7
  351. package/src/routes/issue-activity.ts +2 -2
  352. package/src/routes/issue-associations.ts +4 -4
  353. package/src/routes/issue-refs.ts +3 -3
  354. package/src/routes/kernel-timers-operator.ts +1 -1
  355. package/src/routes/memory.ts +1 -1
  356. package/src/routes/merges.ts +11 -11
  357. package/src/routes/messages.ts +23 -17
  358. package/src/routes/orchestrator-bootstrap.ts +1 -1
  359. package/src/routes/orchestrator-host-registration.ts +2 -0
  360. package/src/routes/orchestrator-host-utilization.ts +2 -2
  361. package/src/routes/orchestrator-proxy.ts +2 -2
  362. package/src/routes/orchestrator.ts +27 -18
  363. package/src/routes/pairs.ts +8 -8
  364. package/src/routes/project-profile-instructions.ts +1 -1
  365. package/src/routes/project-trust.ts +1 -1
  366. package/src/routes/projects.ts +16 -16
  367. package/src/routes/prompt-templates.ts +12 -12
  368. package/src/routes/provider-quotas.ts +10 -10
  369. package/src/routes/run-command.ts +1 -1
  370. package/src/routes/secrets-backup.ts +2 -2
  371. package/src/routes/secrets-import.ts +1 -1
  372. package/src/routes/secrets-vault.ts +7 -7
  373. package/src/routes/settings.ts +1 -1
  374. package/src/routes/shipping.ts +9 -1
  375. package/src/routes/spawn-policy.ts +8 -7
  376. package/src/routes/sse.ts +2 -2
  377. package/src/routes/team-templates.ts +7 -7
  378. package/src/routes/teams.ts +10 -10
  379. package/src/routes/tokens.ts +18 -17
  380. package/src/routes/users.ts +24 -24
  381. package/src/routes/viewer-read.ts +3 -3
  382. package/src/routes/web-tool-calls.ts +4 -4
  383. package/src/routes/workflows.ts +2 -2
  384. package/src/routes/workspaces.ts +17 -16
  385. package/src/routing-policy-push.ts +6 -6
  386. package/src/runtime-grant.ts +17 -11
  387. package/src/runtime-tokens.ts +20 -28
  388. package/src/secrets-broker/config.ts +1 -1
  389. package/src/secrets-broker/service.ts +2 -2
  390. package/src/security.ts +3 -1
  391. package/src/self-resume-carry.ts +21 -110
  392. package/src/self-resume-supersede.ts +4 -3
  393. package/src/self-resume.ts +54 -66
  394. package/src/server-background.ts +1 -1
  395. package/src/server.ts +21 -22
  396. package/src/services/agent-action.ts +12 -31
  397. package/src/services/agent-effort.ts +7 -32
  398. package/src/services/agent-hang-watchdog-job.ts +2 -2
  399. package/src/services/agent-heartbeat.ts +166 -0
  400. package/src/services/agent-reporting.ts +1 -1
  401. package/src/services/browser-inventory.ts +3 -3
  402. package/src/services/browser-session-state.ts +3 -2
  403. package/src/services/browser-session.ts +21 -27
  404. package/src/services/bus-command-update.ts +9 -9
  405. package/src/services/checkout-refresh-state.ts +1 -0
  406. package/src/services/checkout-refresh.test-fixture.ts +3 -2
  407. package/src/services/command-host.ts +7 -2
  408. package/src/services/coordinator-digest.ts +3 -2
  409. package/src/services/dispatch-command.ts +12 -13
  410. package/src/services/effective-launch-driver.ts +14 -9
  411. package/src/services/host-changed-paths.test-fixture.ts +5 -6
  412. package/src/services/host-project-readiness.ts +2 -2
  413. package/src/services/host-providers.ts +1 -1
  414. package/src/services/host-utilization.ts +9 -9
  415. package/src/services/idle-agent-digest.ts +17 -12
  416. package/src/services/idle-epics.ts +4 -4
  417. package/src/services/interrupt-agent.ts +12 -9
  418. package/src/services/issue-activity.ts +3 -3
  419. package/src/services/issue-associations.ts +15 -18
  420. package/src/services/issue-lifecycle-comments.ts +3 -2
  421. package/src/services/issue-lifecycle.ts +14 -13
  422. package/src/services/issue-refs.ts +4 -5
  423. package/src/services/kernel-timers-cutover-chaos-worker.ts +1 -1
  424. package/src/services/kernel-timers-ingress-chaos-worker.ts +6 -1
  425. package/src/services/kernel-timers-ingress.ts +3 -3
  426. package/src/services/kernel-timers-message-authority-chaos-worker.ts +3 -10
  427. package/src/services/kernel-timers-operator-chaos-worker.ts +3 -2
  428. package/src/services/kernel-timers-outbox.ts +1 -1
  429. package/src/services/kernel-timers-stage10-operator.ts +1 -1
  430. package/src/services/kernel-timers.ts +4 -3
  431. package/src/services/land-gate-placement.ts +11 -2
  432. package/src/services/land-issue-relations.ts +5 -4
  433. package/src/services/land-restart-retry.ts +7 -6
  434. package/src/services/landed-ancestry.ts +6 -5
  435. package/src/services/legacy-landing-host.ts +4 -1
  436. package/src/services/legacy-landing-recovery.ts +8 -6
  437. package/src/services/managed-running.ts +17 -17
  438. package/src/services/memory-areas.ts +3 -3
  439. package/src/services/merge-pause.ts +9 -15
  440. package/src/services/operator-decisions.ts +37 -41
  441. package/src/services/orchestrator-registration-recovery.ts +6 -2
  442. package/src/services/orchestrator-upgrade.ts +14 -12
  443. package/src/services/permission-decision.ts +4 -3
  444. package/src/services/plan-ci-notice.ts +1 -1
  445. package/src/services/plan-conformance.ts +4 -4
  446. package/src/services/plan-dispatch-policy.ts +10 -3
  447. package/src/services/plan-dispatch.ts +35 -14
  448. package/src/services/plan-graph.ts +39 -261
  449. package/src/services/plan-import.ts +1 -1
  450. package/src/services/plan-live-bindings.ts +8 -37
  451. package/src/services/plan-task-create.ts +1 -1
  452. package/src/services/plan-terminal-refs.ts +2 -12
  453. package/src/services/plan-wake-digest.ts +8 -3
  454. package/src/services/profile-trust.ts +2 -2
  455. package/src/services/project.ts +11 -10
  456. package/src/services/prompt-templates.ts +6 -5
  457. package/src/services/provider-hold-nudge.ts +9 -9
  458. package/src/services/provider-hold-record.ts +13 -12
  459. package/src/services/provider-state-audit.ts +19 -18
  460. package/src/services/provisioning-harness/driver.ts +1 -1
  461. package/src/services/provisioning-harness/fixture-matrix.ts +3 -3
  462. package/src/services/provisioning-harness/lifecycle.ts +24 -22
  463. package/src/services/provisioning-harness/prepare.ts +16 -17
  464. package/src/services/rate-limit-resume.ts +10 -9
  465. package/src/services/red-main-auto-revert.ts +7 -5
  466. package/src/services/red-main-controller-deps.ts +20 -3
  467. package/src/services/red-main-incident.ts +3 -1
  468. package/src/services/red-main-quarantine.ts +2 -3
  469. package/src/services/register-agent.ts +22 -22
  470. package/src/services/restart-project-role.ts +1 -1
  471. package/src/services/restart-spawn.ts +17 -16
  472. package/src/services/review-publish.ts +7 -24
  473. package/src/services/review-ref-reconcile.ts +29 -38
  474. package/src/services/runtime-dependency-alert.ts +27 -0
  475. package/src/services/scheduler-command.ts +11 -9
  476. package/src/services/scheduler.ts +5 -5
  477. package/src/services/self-effort-grant.ts +5 -25
  478. package/src/services/send-message.ts +16 -33
  479. package/src/services/shutdown-agent.ts +17 -14
  480. package/src/services/spawn-agent.ts +12 -41
  481. package/src/services/spawn-chain-inspector.ts +1 -1
  482. package/src/services/spawn-cwd-placement.ts +21 -5
  483. package/src/services/spawn-issue-activity.ts +8 -6
  484. package/src/services/spawn-launch.ts +17 -1
  485. package/src/services/spawn-loss-escalation.ts +1 -1
  486. package/src/services/spawn-registration-watchdog.ts +2 -2
  487. package/src/services/spawn-repo-probe.ts +7 -5
  488. package/src/services/spawn-tree-cleanup.ts +7 -8
  489. package/src/services/steward-reaper.ts +4 -4
  490. package/src/services/task-context-hydration.ts +25 -1
  491. package/src/services/task-delivery.ts +20 -34
  492. package/src/services/task-entity.ts +40 -203
  493. package/src/services/task-issue-link.ts +13 -20
  494. package/src/services/task-issue-weave.ts +12 -11
  495. package/src/services/task-lifecycle.ts +27 -31
  496. package/src/services/task-repo-cwd.ts +37 -0
  497. package/src/services/task-review-hold.ts +4 -17
  498. package/src/services/task-semantic-events.ts +13 -11
  499. package/src/services/task-state-reconcile.ts +3 -4
  500. package/src/services/task-validation.ts +2 -2
  501. package/src/services/team-templates.ts +5 -5
  502. package/src/services/team.ts +31 -98
  503. package/src/services/terminal-provider-exit.ts +6 -5
  504. package/src/services/timer-message-fire-chaos-worker.ts +7 -3
  505. package/src/services/timer-message-fire-outbox.ts +1 -1
  506. package/src/services/transient-agent-reaper.ts +22 -19
  507. package/src/services/turn-reconcile-audit.ts +9 -9
  508. package/src/services/user-onboarding.ts +8 -6
  509. package/src/services/user-profile.ts +6 -9
  510. package/src/services/work-finality.ts +16 -4
  511. package/src/services/workflow-auto-start.ts +2 -1
  512. package/src/services/workflow-caller.ts +61 -4
  513. package/src/services/workflow-child-proof-worker.ts +1 -1
  514. package/src/services/workflow-control-proof-worker.ts +1 -1
  515. package/src/services/workflow-coordinator.ts +8 -2
  516. package/src/services/workflow-decisions.ts +5 -5
  517. package/src/services/workflow-deferred-cleanup.ts +1 -1
  518. package/src/services/workflow-effect-driver.ts +42 -19
  519. package/src/services/workflow-effects.ts +41 -27
  520. package/src/services/workflow-pins.ts +7 -4
  521. package/src/services/workflow-reconcile.ts +1 -0
  522. package/src/services/workflow-resume-pins.ts +3 -3
  523. package/src/services/workflow-routing-defaults.ts +18 -0
  524. package/src/services/workflow-run-view.ts +4 -3
  525. package/src/services/workflow-run.test-fixture.ts +1 -1
  526. package/src/services/workflow-runs.ts +34 -22
  527. package/src/services/workflow-supervisor-state.ts +21 -1
  528. package/src/services/workflow-supervisor.ts +4 -3
  529. package/src/services/workspace-recovery.ts +4 -3
  530. package/src/spawn-admission.ts +10 -2
  531. package/src/spawn-placement.ts +27 -9
  532. package/src/spawn-targets.ts +8 -8
  533. package/src/sse-agent-status.ts +15 -6
  534. package/src/sse.ts +49 -24
  535. package/src/startup-context-injection.ts +61 -57
  536. package/src/steward-handoff.ts +4 -3
  537. package/src/steward-policy-cleanup.ts +9 -8
  538. package/src/steward.ts +2 -2
  539. package/src/task-dispatch-live.ts +5 -2
  540. package/src/task-dispatcher.ts +2 -2
  541. package/src/token-db.ts +2 -2
  542. package/src/token-usage-async.ts +2 -4
  543. package/src/user-awaiting-reply.ts +5 -5
  544. package/src/workspace-actions.ts +24 -29
  545. package/src/workspace-auto-merge.ts +14 -12
  546. package/src/workspace-changed-paths-client.ts +16 -5
  547. package/src/workspace-cleanup-stale.ts +30 -7
  548. package/src/workspace-command-settle.ts +24 -16
  549. package/src/workspace-config.ts +1 -1
  550. package/src/workspace-conflict-resolver.ts +9 -7
  551. package/src/workspace-dead-owner-recovery.ts +5 -4
  552. package/src/workspace-escalation.ts +12 -11
  553. package/src/workspace-human-escalation.ts +3 -3
  554. package/src/workspace-land-gate-watchdog.ts +3 -3
  555. package/src/workspace-land-refusal.ts +20 -0
  556. package/src/workspace-land-stopped.ts +1 -0
  557. package/src/workspace-merge.ts +10 -26
  558. package/src/workspace-nothing-to-land-notice.ts +2 -2
  559. package/src/workspace-orphans.ts +18 -37
  560. package/src/workspace-pr-completion.ts +10 -8
  561. package/src/workspace-rekey.ts +3 -3
  562. package/src/workspace-steward-failure.ts +2 -2
  563. package/src/workspace-steward-wake.ts +2 -2
  564. package/public/assets/agent-liveness-CaNwK0Zf.js +0 -2
  565. package/public/assets/provider-display-DsuWMvVL.js +0 -2
  566. package/public/assets/run-card-CpKqRbhg.js +0 -2
  567. package/public/assets/shipping-B9YtnwtG.js +0 -2
  568. package/public/assets/store-DcClXLH0.js +0 -9
  569. package/runner/src/agent-runner/http-liveness.ts +0 -85
  570. package/src/db/on-loop-write-transaction.ts +0 -34
@@ -1,3 +1,4 @@
1
+ import { cancelPendingCommandAsync, recordReviewRefCleanupDispatchAsync, markReviewRefPublishedAsync, markReviewRefFailedAsync, markReviewRefClosedAsync, markReviewRefAnnouncedAsync, deleteReviewRefCleanupAsync } from "../db/runtime-tail-async";
1
2
  // #1452 round-4 — the durable, OFF-critical-path engine that settles the review-ref (refs/review/*)
2
3
  // lifecycle and cleans up leaked refs. It reads the review_ref_cleanups ledger (one row per EXACT
3
4
  // server-derived ref) — NOT a per-workspace marker — so it fixes every round-4 finding at once:
@@ -23,18 +24,8 @@
23
24
  // was never enabled, since rows are only written when a publish is dispatched.
24
25
 
25
26
  import { getWorkspace } from "../db";
26
- import {
27
- deleteReviewRefCleanup,
28
- getReviewRefFenceToken,
29
- listReviewRefCleanups,
30
- markReviewRefAnnounced,
31
- markReviewRefClosed,
32
- markReviewRefFailed,
33
- markReviewRefPublished,
34
- recordReviewRefCleanupDispatch,
35
- type ReviewRefCleanup,
36
- } from "../db/review-ref-cleanups";
37
- import { cancelPendingCommand, getCommand } from "../commands-db";
27
+ import { getReviewRefFenceToken, listReviewRefCleanups, type ReviewRefCleanup } from "../db/review-ref-cleanups";
28
+ import { getCommand } from "../commands-db";
38
29
  import { emitCommand } from "../maintenance/events";
39
30
  import { emitRelayEvent } from "../events";
40
31
  import { buildReviewRefUnpublishCommandFromFields } from "../workspace-actions";
@@ -81,16 +72,16 @@ interface ReconcileDeps {
81
72
  * pending publish can still be externally spoofed to succeeded{pushed:true}. Requiring the token here
82
73
  * refuses that phantom before it can announce a ref that was never pushed. Returns null when absent. */
83
74
  getFenceToken: (id: string) => string | null;
84
- markPublished: typeof markReviewRefPublished;
85
- markFailed: typeof markReviewRefFailed;
86
- markAnnounced: typeof markReviewRefAnnounced;
87
- markClosed: typeof markReviewRefClosed;
88
- recordDispatch: typeof recordReviewRefCleanupDispatch;
89
- deleteCleanup: typeof deleteReviewRefCleanup;
75
+ markPublished: typeof markReviewRefPublishedAsync;
76
+ markFailed: typeof markReviewRefFailedAsync;
77
+ markAnnounced: typeof markReviewRefAnnouncedAsync;
78
+ markClosed: typeof markReviewRefClosedAsync;
79
+ recordDispatch: typeof recordReviewRefCleanupDispatchAsync;
80
+ deleteCleanup: typeof deleteReviewRefCleanupAsync;
90
81
  buildUnpublish: typeof buildReviewRefUnpublishCommandFromFields;
91
82
  /** #1452 round-10 HIGH #1 — atomically cancel the publish command IFF still pending; true means it
92
83
  * was never dispatched (proven no push can occur), so a closed round can retire it without racing. */
93
- cancelPublish: (id: string) => boolean;
84
+ cancelPublish: (id: string) => boolean | Promise<boolean>;
94
85
  emit: (command: Command) => void;
95
86
  /** Durable publish-settled announcement: emitted for a published-but-unannounced row, then marked
96
87
  * announced only on success — so a crash/outbox failure re-emits next pass (round-6 H-atomicity). */
@@ -164,21 +155,21 @@ const realDeps: ReconcileDeps = {
164
155
  },
165
156
  getCommand,
166
157
  getFenceToken: getReviewRefFenceToken,
167
- markPublished: markReviewRefPublished,
168
- markFailed: markReviewRefFailed,
169
- markAnnounced: markReviewRefAnnounced,
170
- markClosed: markReviewRefClosed,
171
- recordDispatch: recordReviewRefCleanupDispatch,
172
- deleteCleanup: deleteReviewRefCleanup,
158
+ markPublished: markReviewRefPublishedAsync,
159
+ markFailed: markReviewRefFailedAsync,
160
+ markAnnounced: markReviewRefAnnouncedAsync,
161
+ markClosed: markReviewRefClosedAsync,
162
+ recordDispatch: recordReviewRefCleanupDispatchAsync,
163
+ deleteCleanup: deleteReviewRefCleanupAsync,
173
164
  buildUnpublish: buildReviewRefUnpublishCommandFromFields,
174
- cancelPublish: (id) => cancelPendingCommand(id, "workspace.publish-review", "review publish canceled: round closed before dispatch"),
165
+ cancelPublish: async (id) => (await cancelPendingCommandAsync(id, "workspace.publish-review", "review publish canceled: round closed before dispatch")),
175
166
  emit: emitCommand,
176
167
  emitPublished: emitPublishedEvent,
177
168
  now: () => Date.now(),
178
169
  };
179
170
 
180
171
  /** One reconcile pass over the durable ledger. Pure over its (injectable) deps for testing. */
181
- export function reconcileReviewRefs(deps: ReconcileDeps = realDeps, signal?: AbortSignal): ReviewRefReconcileResult {
172
+ export async function reconcileReviewRefs(deps: ReconcileDeps = realDeps, signal?: AbortSignal): Promise<ReviewRefReconcileResult> {
182
173
  const rows = deps.listCleanups();
183
174
  const published: string[] = [];
184
175
  const failed: string[] = [];
@@ -208,7 +199,7 @@ export function reconcileReviewRefs(deps: ReconcileDeps = realDeps, signal?: Abo
208
199
  const roundClosed = !ws
209
200
  || TERMINAL_WORKSPACE_STATUSES.has(ws.status as any)
210
201
  || Boolean(ws.branch && row.branch && ws.branch !== row.branch);
211
- if (roundClosed) { deps.markClosed(row.id, now); closed = true; }
202
+ if (roundClosed) { await deps.markClosed(row.id, now); closed = true; }
212
203
  }
213
204
 
214
205
  // 1) Settle the publish outcome (H2). A ref is only real once its push command TERMINALLY
@@ -229,8 +220,8 @@ export function reconcileReviewRefs(deps: ReconcileDeps = realDeps, signal?: Abo
229
220
  // push can occur) → retire with nothing on origin. If it was already claimed (accepted/running),
230
221
  // the host may push, so WAIT for its terminal settlement — cleanup then runs safely AFTER it.
231
222
  if (!closed) { inFlight.push(label()); continue; }
232
- if (!deps.cancelPublish(row.id)) { inFlight.push(label()); continue; } // already claimed → wait
233
- deps.markFailed(row.id, now, false); // canceled before dispatch → nothing on origin, drop the ref
223
+ if (!await deps.cancelPublish(row.id)) { inFlight.push(label()); continue; } // already claimed → wait
224
+ await deps.markFailed(row.id, now, false); // canceled before dispatch → nothing on origin, drop the ref
234
225
  confirmedRef = undefined;
235
226
  publishState = "failed";
236
227
  failed.push(label());
@@ -242,7 +233,7 @@ export function reconcileReviewRefs(deps: ReconcileDeps = realDeps, signal?: Abo
242
233
  // never-claimed pending publish spoofed to succeeded has none, so it never advances to published.
243
234
  const ref = confirmedPublishRef(cmd, row.requestedSha, row.ref, deps.getFenceToken(row.id));
244
235
  if (ref) {
245
- deps.markPublished(row.id, ref, now); // sets announced=0 → announced below (H-atomicity)
236
+ await deps.markPublished(row.id, ref, now); // sets announced=0 → announced below (H-atomicity)
246
237
  confirmedRef = ref;
247
238
  publishState = "published";
248
239
  announced = false;
@@ -253,7 +244,7 @@ export function reconcileReviewRefs(deps: ReconcileDeps = realDeps, signal?: Abo
253
244
  // timed-out/canceled, a missing/pruned result, or a malformed success) may have pushed before
254
245
  // its result was lost ⇒ RETAIN the pinned ref as a tombstone and unpublish before deletion.
255
246
  const notPushed = definitivelyNotPushed(cmd);
256
- deps.markFailed(row.id, now, !notPushed);
247
+ await deps.markFailed(row.id, now, !notPushed);
257
248
  if (notPushed) confirmedRef = undefined;
258
249
  publishState = "failed";
259
250
  failed.push(label());
@@ -267,7 +258,7 @@ export function reconcileReviewRefs(deps: ReconcileDeps = realDeps, signal?: Abo
267
258
  if (publishState === "published" && !announced && confirmedRef) {
268
259
  try {
269
260
  deps.emitPublished({ workspaceId: row.workspaceId, ref: confirmedRef, ...(row.requestedSha ? { sha: row.requestedSha } : {}), ...(row.branch ? { branch: row.branch } : {}) });
270
- deps.markAnnounced(row.id, now);
261
+ await deps.markAnnounced(row.id, now);
271
262
  announced = true;
272
263
  } catch { /* emit failed → leave announced=0 so the next pass re-emits (at-least-once) */ }
273
264
  }
@@ -285,7 +276,7 @@ export function reconcileReviewRefs(deps: ReconcileDeps = realDeps, signal?: Abo
285
276
 
286
277
  // A closed round with NOTHING on origin — a DEFINITIVELY-not-pushed outcome, or a legacy row that
287
278
  // never pinned a ref — has nothing to delete. Retire it directly, no host round-trip, no leak.
288
- if (!confirmedRef) { deps.deleteCleanup(row.id); cleared.push(label()); continue; }
279
+ if (!confirmedRef) { await deps.deleteCleanup(row.id); cleared.push(label()); continue; }
289
280
 
290
281
  // 3) Drive deletion (H4/M7) of the EXACT host-confirmed ref. CONFIRM a prior attempt first — a
291
282
  // succeeded delete clears the row regardless of the backoff window; the backoff gates only a
@@ -294,20 +285,20 @@ export function reconcileReviewRefs(deps: ReconcileDeps = realDeps, signal?: Abo
294
285
  const cmd = deps.getCommand(row.cleanupCommandId);
295
286
  if (cmd && !COMMAND_TERMINAL.has(cmd.status)) { inFlight.push(confirmedRef); continue; }
296
287
  if (cmd && cmd.status === "succeeded" && !cmd.error) {
297
- deps.deleteCleanup(row.id); // origin confirmed the ref is gone (deleted or already absent)
288
+ await deps.deleteCleanup(row.id); // origin confirmed the ref is gone (deleted or already absent)
298
289
  cleared.push(confirmedRef);
299
290
  continue;
300
291
  }
301
292
  // failed / timed_out / missing → re-dispatch below, subject to backoff.
302
293
  }
303
294
  if (row.nextAttemptAt != null && row.nextAttemptAt > now) { waiting.push(confirmedRef); continue; }
304
- const built = deps.buildUnpublish(
295
+ const built = await deps.buildUnpublish(
305
296
  { workspaceId: row.workspaceId, repoRoot: row.repoRoot, worktreePath: row.worktreePath, branch: row.branch, orchestratorId: row.orchestratorId, ref: confirmedRef },
306
297
  "review-ref-reconciler",
307
298
  );
308
299
  if (!built.ok) continue; // no owning host reachable yet — retry next tick (no backoff burned)
309
300
  deps.emit(built.command);
310
- deps.recordDispatch(row.id, built.command.id, now + cleanupBackoffMs(row.cleanupAttempts), now);
301
+ await deps.recordDispatch(row.id, built.command.id, now + cleanupBackoffMs(row.cleanupAttempts), now);
311
302
  dispatched.push(confirmedRef);
312
303
  }
313
304
 
@@ -326,7 +317,7 @@ export const REVIEW_REF_RECONCILE_JOB = {
326
317
  intervalMs: WORKSPACE_ORPHAN_REAPER_INTERVAL_MS,
327
318
  runOnStart: false,
328
319
  deadlineMs: 60 * 1000,
329
- handler: (context: { signal?: AbortSignal }) => reconcileReviewRefs(undefined, context.signal) as unknown as Record<string, unknown>,
320
+ handler: async (context: { signal?: AbortSignal }) => (await reconcileReviewRefs(undefined, context.signal)) as unknown as Record<string, unknown>,
330
321
  };
331
322
 
332
323
  // Test seam: backoff parameters so tests assert scheduling without duplicating literals.
@@ -0,0 +1,27 @@
1
+ import { createHash } from "node:crypto";
2
+ import { isRecord } from "@humwick/sdk";
3
+ import { createActivityEventByClientIdAsync } from "../db/activity-async";
4
+ import { notifyWorkspaceHumanEscalation } from "../workspace-human-escalation";
5
+ import { stewardFallbackTarget } from "../config";
6
+ import { notifyTarget } from "../maintenance/events";
7
+
8
+ /** Heartbeats already persist provider failures for Fleet; page once per broken checkout (#3791). */
9
+ export async function reportRuntimeDependencyFailure(hostId: string, hostname: string, body: unknown): Promise<void> {
10
+ if (!isRecord(body) || !Array.isArray(body.providerStatus)) return;
11
+ const failure = body.providerStatus.find((status) => isRecord(status) && status.available === false
12
+ && typeof status.reason === "string" && status.reason.startsWith("Runtime dependencies unavailable in "));
13
+ if (!failure) return;
14
+ const reason = failure.reason as string;
15
+ const checkout = reason.split(": ")[0]!;
16
+ const key = `runtime-dependencies:${hostId}:${createHash("sha256").update(checkout).digest("hex")}`;
17
+ const subject = `Runtime dependencies broken on ${hostname}`;
18
+ const message = `${hostname}: ${reason}. Providers are unavailable until this checkout is repaired.`;
19
+ const event = await createActivityEventByClientIdAsync({ clientId: key, kind: "state", title: subject, body: message,
20
+ meta: hostId, icon: "ti-alert-triangle", view: "orchestrators", metadata: { hostId, reason } });
21
+ if (!event.created) return;
22
+ const payload = { hostId, reason, kind: "runtime-dependencies.failed" };
23
+ const human = await notifyWorkspaceHumanEscalation({ subject, body: message, priority: "urgent", source: "runtime-dependencies", payload, idempotencyKey: key });
24
+ const target = stewardFallbackTarget();
25
+ const routed = target ? await notifyTarget("maintenance.degraded", {}, target, subject, message, payload, key) : null;
26
+ console.warn(`[relay] ${subject}: ${reason}${human.accepted || routed ? "" : " — no notification target configured"}`);
27
+ }
@@ -1,9 +1,11 @@
1
+ import { updateCommandAsync } from "../commands-write";
1
2
  import { writeChildEnv } from "@humwick/sdk/env";
2
3
  import { basename } from "node:path";
3
4
  import { SCHEDULER_RETRY_MS } from "../config";
4
5
  import { errMessage, stringValue } from "@humwick/sdk";
5
6
  import { hasScope } from "@humwick/sdk/types";
6
- import { findLatestCommandByCorrelation, getCommand, updateCommand } from "../commands-db";
7
+ import { getCommand } from "../commands-db";
8
+ import { findLatestCommandByCorrelationAsync } from "../commands-write";
7
9
  import { RELAY_RUN_DEADLINE_GRACE_MS } from "../relay-run-lease";
8
10
  import { storeTextArtifact } from "../artifact-text";
9
11
  import { gateRunIdentity, recordAgentGateRun } from "../gate-run-capture";
@@ -119,7 +121,7 @@ export async function dispatchScheduledCommand(timer: ScheduledTimer, ctx = pers
119
121
  // Freeze the agent generation that owns this command. Managed restarts reuse the
120
122
  // durable agent id, so agentId alone would let predecessor teardown kill a successor's command.
121
123
  const executionOwnerSpawnRequestId = spawnRequestIds?.length === 1 ? spawnRequestIds[0] : undefined;
122
- const command = findLatestCommandByCorrelation("scheduler.command", timer.correlationId) ?? await dispatchCommandAsync({
124
+ const command = await findLatestCommandByCorrelationAsync("scheduler.command", timer.correlationId) ?? await dispatchCommandAsync({
123
125
  type: "scheduler.command",
124
126
  source: timer.createdBy,
125
127
  target: `orchestrator-${timer.command.orchestratorId}`,
@@ -186,7 +188,7 @@ export async function fireScheduledCommandTimeout(timer: ScheduledTimer, now: nu
186
188
  return { status: "deferred" };
187
189
  }
188
190
  const neverStarted = !!command && (command.status === "pending" || command.status === "accepted") && commandResult(command).phase !== "running";
189
- if (command) updateCommand(command.id, { status: "timed_out", error: "background command timed out" });
191
+ if (command) (await updateCommandAsync(command.id, { status: "timed_out", error: "background command timed out" }));
190
192
  const result = command ? commandResult(command) : {};
191
193
  const admittedAt = numberValue(result.admittedAt) ?? timer.command.admittedAt;
192
194
  const durationMs = admittedAt === undefined ? 0 : Math.max(0, now - admittedAt);
@@ -202,7 +204,7 @@ export async function fireScheduledCommandTimeout(timer: ScheduledTimer, now: nu
202
204
  stdoutTail: stringValue(result.stdoutTail) ?? timer.command.stdoutTail ?? "",
203
205
  stderrTail: stringValue(result.stderrTail) ?? timer.command.stderrTail ?? (neverStarted ? queuedTimeoutReason(timer.command.orchestratorId) : "background command timed out"),
204
206
  };
205
- return fireScheduledCommandWake(timer, {
207
+ return (await fireScheduledCommandWake(timer, {
206
208
  status: "timed_out",
207
209
  finishedAt: timer.command.finishedAt ?? now,
208
210
  durationMs,
@@ -213,7 +215,7 @@ export async function fireScheduledCommandTimeout(timer: ScheduledTimer, now: nu
213
215
  exitCode: null,
214
216
  stdoutTail: timeoutResult.stdoutTail,
215
217
  stderrTail: timeoutResult.stderrTail,
216
- }, timeoutResult, now, inlineWakeSuppressed(timer.command, now));
218
+ }, timeoutResult, now, inlineWakeSuppressed(timer.command, now)));
217
219
  }
218
220
 
219
221
  /** #2776 — admission report: the fallback timer now guards the RUN (end + grace), not the queue ceiling. */
@@ -243,7 +245,7 @@ export async function settleScheduledCommandTimer(command: Command, now = Date.n
243
245
  : "failed";
244
246
  const stdout = clipTail(stringValue(result.stdoutTail) ?? stringValue(result.stdout) ?? "");
245
247
  const stderr = clipTail(stringValue(result.stderrTail) ?? stringValue(result.stderr) ?? command.error ?? "");
246
- return fireScheduledCommandWake(timer, {
248
+ return (await fireScheduledCommandWake(timer, {
247
249
  status,
248
250
  finishedAt: timer.command.finishedAt ?? now,
249
251
  startedAt: numberValue(result.startedAt),
@@ -262,7 +264,7 @@ export async function settleScheduledCommandTimer(command: Command, now = Date.n
262
264
  // Strictly stronger than truncation: the command was KILLED for flooding, it did not finish.
263
265
  outputLimitExceeded: result.outputLimitExceeded === true,
264
266
  logArtifactId: stringValue(result.logArtifactId),
265
- }, result, now, inlineWakeSuppressed(timer.command, now));
267
+ }, result, now, inlineWakeSuppressed(timer.command, now)));
266
268
  }
267
269
 
268
270
  /**
@@ -358,7 +360,7 @@ async function fireScheduledCommandWake(
358
360
  try {
359
361
  const runNumber = timerForSend.runCount + 1;
360
362
  const payloadResult = commandPayload(timerForSend.command!, rawResult, logArtifactId);
361
- const send = sendMessageService({
363
+ const send = (await sendMessageService({
362
364
  from: timerForSend.deliveryFrom,
363
365
  to: timerForSend.target,
364
366
  kind: timerForSend.messageKind as SendMessageInput["kind"] | undefined,
@@ -387,7 +389,7 @@ async function fireScheduledCommandWake(
387
389
  },
388
390
  // One stable key per command completion (not per run count): overlapping settlements dedupe.
389
391
  idempotencyKey: `${timerForSend.idempotencyKey ?? `scheduler:${timerForSend.id}`}:completion`,
390
- }, persistedCommandAuthContext(timerForSend));
392
+ }, persistedCommandAuthContext(timerForSend)));
391
393
  await completeTimerRunAsync(timerForSend, send.message.id, now);
392
394
  return { status: "fired", messageId: send.message.id };
393
395
  } catch (error) {
@@ -1,3 +1,4 @@
1
+ import { framed } from "../hub-metrics";
1
2
  import { createScheduledTimer } from "./scheduler-create";
2
3
  import { planScheduledTimerCreate, cleanOptionalString, canAdministerTimers, type ScheduleTimerInput } from "./scheduler-plan";
3
4
  export { type ScheduleTimerInput } from "./scheduler-plan";
@@ -84,9 +85,8 @@ export function startRelayScheduler(): void {
84
85
  if (isEphemeralMode()) return;
85
86
  if (schedulerTimer) return;
86
87
  void timePostBindPhase("runDueScheduledTimers", runDueScheduledTimers).catch((error) => console.warn(`relay scheduler tick failed: ${errMessage(error)}`));
87
- schedulerTimer = setInterval(() => {
88
- void timePostBindPhase("runDueScheduledTimers", runDueScheduledTimers).catch((error) => console.warn(`relay scheduler tick failed: ${errMessage(error)}`));
89
- }, SCHEDULER_TICK_MS);
88
+ schedulerTimer = setInterval(framed("timer scheduled-timers", () =>
89
+ timePostBindPhase("runDueScheduledTimers", runDueScheduledTimers).catch((error) => console.warn(`relay scheduler tick failed: ${errMessage(error)}`))), SCHEDULER_TICK_MS);
90
90
  // #1408 K7 Phase-2: additively light up the kernel-served timers supervisor iff the reversible
91
91
  // switch is on (default OFF ⇒ pure no-op). Legacy above stays authoritative; the kernel path is
92
92
  // not yet a functional replacement for message/command dispatch (see kernel-timers.ts).
@@ -292,7 +292,7 @@ async function fireClaimedTimer(
292
292
  await disableTimerAsync(timer.id, "timer expired before delivery");
293
293
  return { status: "disabled" };
294
294
  }
295
- return fireScheduledCommandTimeout(timer, now);
295
+ return (await fireScheduledCommandTimeout(timer, now));
296
296
  }
297
297
 
298
298
  try {
@@ -323,7 +323,7 @@ async function fireClaimedTimer(
323
323
  await disableTimerAsync(timer.id, "timer expired before delivery");
324
324
  return { status: "disabled" };
325
325
  }
326
- const result = sendMessageService(sendInput, persistedAuthContext(timer));
326
+ const result = (await sendMessageService(sendInput, persistedAuthContext(timer)));
327
327
  await completeTimerRunAsync(timer, result.message.id, now);
328
328
  return { status: "fired", messageId: result.message.id };
329
329
  } catch (error) {
@@ -1,3 +1,5 @@
1
+ import { grantSelfEffortAsync } from "../db/agent-control-async";
2
+ export { SELF_EFFORT_GRANT_COMMAND } from "../db/agent-control-write";
1
3
  import { randomUUID } from "node:crypto";
2
4
  import { stringValue } from "@humwick/sdk";
3
5
  import { createCommandInternal, updateCommandInternal } from "../commands-db";
@@ -14,7 +16,7 @@ import type { Command } from "../types";
14
16
 
15
17
  // #2966 — the operator turns selfEffort on or off for a running agent. The permission row is the
16
18
  // state; the settled command written beside it is the audit record.
17
- export const SELF_EFFORT_GRANT_COMMAND = "agent.grantSelfEffort";
19
+
18
20
 
19
21
  interface SelfEffortGrantInput extends SelfEffortSpawnInput {
20
22
  agentId: string;
@@ -27,7 +29,7 @@ interface SelfEffortGrantInput extends SelfEffortSpawnInput {
27
29
  export async function grantSelfEffort(input: SelfEffortGrantInput, ctx: AuthContext): Promise<{ selfEffort: SelfEffortPolicy | null; command: Command }> {
28
30
  const agent = getAgent(input.agentId);
29
31
  if (!agent) throw new AgentActionTargetError(`agent ${input.agentId} not found`);
30
- authorizeShutdown(ctx, { agentId: agent.id, requestedVia: input.requestedVia });
32
+ (await authorizeShutdown(ctx, { agentId: agent.id, requestedVia: input.requestedVia }));
31
33
  const spawnRequestId = stringValue(agent.meta?.spawnRequestId);
32
34
  if (typeof input.selfEffort !== "boolean") throw new ValidationError("selfEffort must be true or false");
33
35
  const model = agent.providerCapabilities?.model;
@@ -64,29 +66,7 @@ export async function grantSelfEffort(input: SelfEffortGrantInput, ctx: AuthCont
64
66
  revalidateAgentInstance(agent);
65
67
  // Nothing executes a grant: it is written settled with the permission in one transaction. The
66
68
  // identity is resolved inside it, so a stale writer cannot restore a deleted policy's row.
67
- const command = writeTransaction(() => {
68
- const now = Date.now();
69
- const identity = selfEffortIdentity(agent);
70
- setSelfEffortCeilingInternal(identity, !grant ? null : "band" in grant ? SELF_EFFORT_BAND : grant.ceiling, requestedBy, now);
71
- createCommandInternal({ id: commandId, input: {
72
- type: SELF_EFFORT_GRANT_COMMAND,
73
- source: "system",
74
- target: agent.id,
75
- ...(spawnRequestId ? { correlationId: spawnRequestId } : {}),
76
- params: {
77
- agentId: agent.id,
78
- ...(spawnRequestId ? { spawnRequestId } : {}),
79
- permission: identity,
80
- selfEffort,
81
- previousSelfEffort: previous,
82
- requestedBy,
83
- ...(input.requestedByUser ? { requestedByUser: input.requestedByUser } : {}),
84
- requestedVia: input.requestedVia,
85
- requestedAt: now,
86
- },
87
- } }, now);
88
- return updateCommandInternal(commandId, { status: "succeeded", result: { selfEffort } }, {}, now).command!;
89
- });
69
+ const command = await grantSelfEffortAsync({ agent, grant, requestedBy, commandId, spawnRequestId, input, selfEffort, previous });
90
70
  emitCommandEvent(command, "command.succeeded");
91
71
  emitActivityEvent(event);
92
72
  return { selfEffort, command };
@@ -1,7 +1,9 @@
1
+ import { promoteLineageCapturedResponseAsync } from "../db/runtime-tail-async";
2
+ import { sendMessageWithResultAsync } from "../db/message-send";
1
3
  import { refusedResponseReport } from "./response-report-admission";
2
4
  import { suppressResultsOnlyAck, isResultsOnlyAckCandidate } from "../db/message-send-suppression";
3
5
  import { getGateway } from "../db/gateway";
4
- import { getMessageAsync, channelObligationAnsweredByAsync } from "../db";
6
+ import { getMessageAsync, channelObligationAnsweredByAsync, ValidationError, getAgent, getMessage, isChannelAgentId, listAgents, sendMessagesBatchWithResultAsync, channelObligationAnsweredBy, agentPrincipalUserId, isMultiUserWorld, type SendMessageResult as DbSendMessageResult } from "../db";
5
7
  import { agentPrincipalSink } from "../db/agent-ownership";
6
8
  // Transport convergence (epic #342, slice #346) — the message-send service.
7
9
  //
@@ -27,21 +29,6 @@ import { agentPrincipalSink } from "../db/agent-ownership";
27
29
  // OUT OF SCOPE (transport-edge, excluded from the parity facet): automatic memory injection
28
30
  // (needs request-derived context) and per-transport audit (HTTP domain row vs MCP tool-call row).
29
31
  import { LEGACY_HUMAN_AGENT_ID, USER_NAMESPACE_PREFIX, userSinkId, canonicalHumanAgentId, heldForSender, isHumanAgentId, isMechanicalMessageKind, isRecord, isReservedAgentId, isTurnFinalSession } from "@humwick/sdk";
30
- import {
31
- ValidationError,
32
- getAgent,
33
- getMessage,
34
- isChannelAgentId,
35
- listAgents,
36
- promoteLineageCapturedResponse,
37
- sendMessagesBatchWithResultAsync,
38
- sendMessageWithResult,
39
- sendMessageWithResultAsync,
40
- channelObligationAnsweredBy,
41
- agentPrincipalUserId,
42
- isMultiUserWorld,
43
- type SendMessageResult as DbSendMessageResult,
44
- } from "../db";
45
32
  import { recentFinalResponseCaptureIds } from "../db/lineage-startup-captures";
46
33
  import { yieldToEventLoop } from "../event-loop-yield";
47
34
  import { markAgentWorkFinal } from "./work-finality";
@@ -367,18 +354,12 @@ function agentSendAllowed(input: SendMessageInput, ctx: AuthContext, sender: Mes
367
354
  return inSameMessagingRun(sender, getAgent(input.to));
368
355
  }
369
356
 
370
- export function sendMessageService(
357
+ export async function sendMessageService(
371
358
  input: SendMessageInput,
372
359
  ctx: AuthContext,
373
360
  opts: SendMessageOptions = {},
374
- ): SendMessageResult {
375
- const { receipt, suppressedParent } = prepareSendMessage(input, ctx, opts);
376
- if (suppressedParent) return { message: suppressedParent, created: false, receipt };
377
- const result = sendMessageWithResult(input, { allowUnregisteredSender: opts.allowUnregisteredSender });
378
- const sent = { message: result.message, created: result.created, receipt: finalizeReceipt(result.message, receipt) };
379
- if (opts.deferLiveEffects) return { ...sent, publish: () => emitSendResults([result]) };
380
- emitSendResults([result]);
381
- return sent;
361
+ ): Promise<SendMessageResult> {
362
+ return sendMessageServiceAsync(input, ctx, opts);
382
363
  }
383
364
 
384
365
  /** Async persistence and message-history reads; live target authorization and emits stay on main.
@@ -391,8 +372,10 @@ export async function sendMessageServiceAsync(
391
372
  const { receipt, suppressedParent } = await prepareSendMessageAsync(input, ctx, opts);
392
373
  if (suppressedParent) return { message: suppressedParent, created: false, receipt };
393
374
  const result = await sendMessageWithResultAsync(input, { allowUnregisteredSender: opts.allowUnregisteredSender });
394
- emitSendResults([result]);
395
- return { message: result.message, created: result.created, receipt: finalizeReceipt(result.message, receipt) };
375
+ const sent = { message: result.message, created: result.created, receipt: finalizeReceipt(result.message, receipt) };
376
+ if (opts.deferLiveEffects) return { ...sent, publish: async () => (await emitSendResults([result])) };
377
+ (await emitSendResults([result]));
378
+ return sent;
396
379
  }
397
380
 
398
381
  export async function sendMessagesBatchService(
@@ -426,7 +409,7 @@ export async function sendMessagesBatchService(
426
409
  receipt: finalizeReceipt(result.message, prepared[outputIndex]!.receipt),
427
410
  };
428
411
  }
429
- emitSendResults(stored);
412
+ (await emitSendResults(stored));
430
413
  return results;
431
414
  }
432
415
 
@@ -504,11 +487,11 @@ export async function sweepDeferredLineageReportsOnStartup(now: number = Date.no
504
487
  // A tagged capture was already flushed before boot (or by an earlier sweep). Skipping it avoids
505
488
  // re-arming the held ring, minting a second finality window, and re-emitting `message.finalized`.
506
489
  if (isRecord(capture.payload?.finality)) continue;
507
- const promoted = promoteLineageCapturedResponse(capture);
490
+ const promoted = (await promoteLineageCapturedResponseAsync(capture));
508
491
  if (promoted?.created) emitted.push(promoted.message);
509
492
  // The ordinary 10-minute cutoff protects live coalescing. Recovery is already bounded by the
510
493
  // 24-hour query, and persistence proves this capture's source turn completed before boot.
511
- const recovered = markAgentWorkFinal(capture.from, "edge-fallback", { includeStaleCompletedFinals: true });
494
+ const recovered = (await markAgentWorkFinal(capture.from, "edge-fallback", { includeStaleCompletedFinals: true }));
512
495
  recoveredCount += recovered.forwarded.filter((entry) => entry.created).length;
513
496
  }
514
497
  if (emitted.length > 0) emitNewMessages(emitted);
@@ -517,7 +500,7 @@ export async function sweepDeferredLineageReportsOnStartup(now: number = Date.no
517
500
  return { promoted: emitted.length + recoveredCount, scanned, ...(signal?.aborted ? { aborted: true as const } : {}) };
518
501
  }
519
502
 
520
- function emitSendResults(results: DbSendMessageResult[]): void {
503
+ export async function emitSendResults(results: DbSendMessageResult[]): Promise<void> {
521
504
  const liveMessages: Message[] = [];
522
505
  for (const result of results) {
523
506
  if (result.created) {
@@ -538,13 +521,13 @@ function emitSendResults(results: DbSendMessageResult[]): void {
538
521
  emitMessageDeliveryUpdated(result.message);
539
522
  }
540
523
  }
541
- const promoted = promoteLineageCapturedResponse(result.message);
524
+ const promoted = (await promoteLineageCapturedResponseAsync(result.message));
542
525
  if (promoted?.created) liveMessages.push(promoted.message);
543
526
  } else if (result.updated) {
544
527
  // Session-step upsert (#parity): the row changed in place (same DB id). Re-emit so the
545
528
  // dashboard's id-keyed merge replaces the existing step live — no duplicate row.
546
529
  liveMessages.push(result.message);
547
- const promoted = promoteLineageCapturedResponse(result.message);
530
+ const promoted = (await promoteLineageCapturedResponseAsync(result.message));
548
531
  if (promoted?.created) liveMessages.push(promoted.message);
549
532
  }
550
533
  }
@@ -1,3 +1,5 @@
1
+ import { createAgentControlAsync } from "../db/agent-control-async";
2
+ import type { SafeCandidate } from "./idle-agent-digest";
1
3
  import { spawnTreeContains } from "../db/spawn-tree";
2
4
  import { spawnDelegationState } from "../db/spawn-delegation-state";
3
5
  // Transport convergence (epic #342, slice #347) — the agent-shutdown service.
@@ -37,7 +39,6 @@ import { spawnDelegationState } from "../db/spawn-delegation-state";
37
39
  // system auth" — the steward-reaper's forceful escalation does; its graceful first attempt does not.
38
40
  import {
39
41
  ValidationError,
40
- inWriteTransaction,
41
42
  createActivityEventAsync,
42
43
  getAgent,
43
44
  getOrchestrator,
@@ -46,7 +47,7 @@ import {
46
47
  mergeAgentMetaAsync,
47
48
  } from "../db";
48
49
  import { randomUUID } from "node:crypto";
49
- import { createCommandInternal, findSpawnCommandByRequestId, getCommand } from "../commands-db";
50
+ import { findSpawnCommandByRequestId, getCommand } from "../commands-db";
50
51
  import { SPAWN_CANCELABLE_STATUSES } from "../spawn-intent";
51
52
  import { isRestartSensitiveCommand } from "../db/restart-sensitive";
52
53
  import { cancelCommandIfStatusAsync } from "../commands-write";
@@ -112,6 +113,7 @@ interface ShutdownAgentInput {
112
113
  /** #3428 — automatic disposal only: re-checked on the fresh card at command admission, after
113
114
  * every await; a returned reason refuses the shutdown. */
114
115
  admit?: (agent: AgentCard) => string | undefined;
116
+ idleCandidate?: SafeCandidate;
115
117
  }
116
118
 
117
119
  interface ShutdownAgentResult {
@@ -173,7 +175,7 @@ export interface ShutdownAuthorization {
173
175
 
174
176
  /** The #221 parent→child gate — the single home for shutdown authorization across all
175
177
  * three transports. Throws `ShutdownAuthError` on denial; full-reach callers bypass ownership. */
176
- export function authorizeShutdown(ctx: AuthContext, input: ShutdownAgentInput, action = "shutdown"): ShutdownAuthorization {
178
+ export async function authorizeShutdown(ctx: AuthContext, input: ShutdownAgentInput, action = "shutdown"): Promise<ShutdownAuthorization> {
177
179
  const callerId = ctx.callerAgentId;
178
180
  // A scoped agent caller may target only its OWN live spawned children — or (#2618) live members
179
181
  // of a team it currently coordinates, e.g. after claiming a dead coordinator's team. Two PRECISE
@@ -211,7 +213,7 @@ export function authorizeShutdown(ctx: AuthContext, input: ShutdownAgentInput, a
211
213
  }
212
214
  }
213
215
  // #3567 — the one lifecycle-control check (principal, role, coordinator authority).
214
- const denied = authorizeAgentControl(ctx, target, action);
216
+ const denied = (await authorizeAgentControl(ctx, target, action));
215
217
  if (denied) {
216
218
  throw new ShutdownAuthError(denied.reason === "coordinator" && !input.agentId
217
219
  ? `no spawned child of yours matches spawnRequestId ${input.spawnRequestId}` : denied.message);
@@ -315,7 +317,7 @@ async function reapInFlightSpawnCommand(
315
317
  return { command: canceled ?? command, orchestratorId: (canceled ?? command).target, agent: null };
316
318
  }
317
319
  emitCommandEvent(canceled, "command.canceled");
318
- notifyAgentSpawnFailed({
320
+ await notifyAgentSpawnFailed({
319
321
  parent: stringValue(canceled.params.spawnedBy) ?? ctx.callerAgentId ?? ctx.actor.id,
320
322
  spawnRequestId: input.spawnRequestId,
321
323
  provider: stringValue(canceled.params.provider),
@@ -334,11 +336,11 @@ async function reapInFlightSpawnCommand(
334
336
  * shutting-down (uniform across transports — MCP previously skipped this), and writes the
335
337
  * audit row. The single fat-service entry point the three thin transports call. */
336
338
  export function shutdownAgent(input: ShutdownAgentInput, ctx: AuthContext): Promise<ShutdownAgentResult> {
337
- return withRelayEventDrain(() => shutdownAgentBody(input, ctx));
339
+ return withRelayEventDrain(async () => (await shutdownAgentBody(input, ctx)));
338
340
  }
339
341
 
340
342
  async function shutdownAgentBody(input: ShutdownAgentInput, ctx: AuthContext): Promise<ShutdownAgentResult> {
341
- const authz = authorizeShutdown(ctx, input);
343
+ const authz = (await authorizeShutdown(ctx, input));
342
344
  if (authz.spawnCommand) return reapInFlightSpawnCommand(authz.spawnCommand, input, ctx);
343
345
 
344
346
  const agent = input.agentId ? getAgent(input.agentId) : null;
@@ -414,19 +416,20 @@ async function shutdownAgentBody(input: ShutdownAgentInput, ctx: AuthContext): P
414
416
  const priorCommand = input.commandId ? getCommand(commandId) : null;
415
417
  if (priorCommand) return { command: priorCommand, orchestratorId: orchestrator.id, agent: currentAgent };
416
418
  const admit = input.admit;
417
- // #3428 — automatic disposal re-decides on the fresh card in the same write transaction as the insert.
418
- const command = admit && currentAgent ? inWriteTransaction(() => {
419
- const refused = admit(getAgent(currentAgent.id) ?? currentAgent);
419
+ if (admit && currentAgent) {
420
+ const refused = admit(currentAgent);
420
421
  if (refused) throw new AutomaticShutdownRefused(`automatic shutdown of ${currentAgent.id} refused at admission: ${refused}`);
421
- return createCommandInternal({ input: commandInput, id: commandId });
422
- }) : createCommandInternal({ input: commandInput, id: commandId });
422
+ }
423
+ const admitted = await createAgentControlAsync({ agent: currentAgent, commandId, commandInput, idleCandidate: input.idleCandidate });
424
+ if (!admitted.command) throw new AutomaticShutdownRefused(`automatic shutdown of ${currentAgent?.id} refused at admission: ${admitted.refused}`);
425
+ const command = admitted.command;
423
426
  emitCommandEvent(command, "command.requested");
424
- if (targetAgentId) reapAgentBrowserSessions(targetAgentId); // #3489 — a requested stop takes its browser with it
427
+ if (targetAgentId) (await reapAgentBrowserSessions(targetAgentId)); // #3489 — a requested stop takes its browser with it
425
428
  // #1084 — an explicit shutdown is a platform-owned teardown signal: close the target's work
426
429
  // window now rather than let a held final group die silently with the process. `edge-fallback`
427
430
  // is the closest fit in the closed source enum — there is no cleaner workspace-ready/task-done
428
431
  // signal backing an arbitrary teardown (which may target an agent with neither).
429
- if (targetAgentId) markAgentWorkFinal(targetAgentId, "edge-fallback");
432
+ if (targetAgentId) (await markAgentWorkFinal(targetAgentId, "edge-fallback"));
430
433
 
431
434
  // Lifecycle indicator — uniform across transports. The MCP path previously skipped
432
435
  // this, so an MCP-initiated shutdown left the agent with no "shutting down" signal in