@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
@@ -0,0 +1,71 @@
1
+ import { createHash } from "node:crypto";
2
+ import { relayEnv } from "@humwick/sdk/env";
3
+ import { getGateway } from "../db/gateway";
4
+ import { getPlan, listPlansByTeam } from "../db/plans";
5
+ import type { DueReadyNodes, ReadyPlanScan } from "../db/plan-ready-notices";
6
+ import { listActiveTeams } from "../db/teams";
7
+ import { routeNotification } from "../notification-router";
8
+ import { compactText, liveFrontier } from "../services/plan-wake-digest";
9
+
10
+ const DEFAULT_READY_NOTICE_MINUTES = 30;
11
+
12
+ /** #3660 — minutes a ready node may sit unstarted before the coordinator hears about it. */
13
+ export function planReadyNoticeMs(): number {
14
+ const minutes = Number(relayEnv(process.env, "AGENT_RELAY_PLAN_READY_NOTICE_MINUTES"));
15
+ return (Number.isFinite(minutes) && minutes > 0 ? minutes : DEFAULT_READY_NOTICE_MINUTES) * 60_000;
16
+ }
17
+
18
+ /** Ready (all prerequisites landed), still pending after the Task projection, and nobody owns it. */
19
+ function scanTeamPlans(teamId: string, signal: AbortSignal): Array<ReadyPlanScan & { title: string }> {
20
+ const scans: Array<ReadyPlanScan & { title: string }> = [];
21
+ let cursor: string | undefined;
22
+ do {
23
+ const page = listPlansByTeam(teamId, { limit: 100, cursor });
24
+ for (const summary of page.plans) {
25
+ if (signal.aborted) return scans;
26
+ if (!summary.nodeCount || summary.completion === "done") continue;
27
+ const stored = getPlan(summary.id);
28
+ if (!stored) continue;
29
+ const { frontier } = liveFrontier(stored);
30
+ scans.push({ planId: stored.id, title: stored.title ?? "Untitled", nodeIds: frontier.ready.filter(node => !node.ownerRef).map(node => node.id) });
31
+ }
32
+ cursor = page.nextCursor;
33
+ } while (cursor);
34
+ return scans;
35
+ }
36
+
37
+ export async function noticeUnstartedReadyNodes(signal: AbortSignal, now = Date.now()): Promise<Record<string, unknown>> {
38
+ const scans: Array<ReadyPlanScan & { title: string; teamId: string }> = [];
39
+ for (const team of listActiveTeams()) {
40
+ if (signal.aborted) return { notices: 0 };
41
+ if (team.coordinatorId) scans.push(...scanTeamPlans(team.id, signal).map(scan => ({ ...scan, teamId: team.id })));
42
+ }
43
+ if (signal.aborted) return { notices: 0 };
44
+ const due = await getGateway().write("plans.readyNotices.sync", { scans: scans.map(({ planId, nodeIds }) => ({ planId, nodeIds })), now, dueMs: planReadyNoticeMs() }) as unknown as DueReadyNodes[];
45
+ const byPlan = new Map(scans.map(scan => [scan.planId, scan]));
46
+ let notices = 0;
47
+ for (const item of due) {
48
+ const scan = byPlan.get(item.planId)!;
49
+ if (signal.aborted) break;
50
+ const nodeIds = item.nodes.map(node => node.nodeId);
51
+ const minutes = Math.round(planReadyNoticeMs() / 60_000);
52
+ const key = createHash("sha1").update(item.nodes.map(node => `${node.nodeId}@${node.readySince}`).join(",")).digest("hex").slice(0, 12);
53
+ await routeNotification({
54
+ type: "team.idle", scope: { teamId: scan.teamId }, recipients: [`coordinator:${scan.teamId}`],
55
+ message: { kind: "system", from: "system", subject: "Plan nodes ready but not started", replyExpected: false,
56
+ body: `${compactText(scan.title, 56)} (${scan.planId}): ready for ${minutes}+ min, not started: ${nodeIds.slice(0, 8).map(id => compactText(id, 32)).join(",")}${nodeIds.length > 8 ? `,+${nodeIds.length - 8}` : ""}. Start them or mark them blocked (relay_plan_get mode=resume).`,
57
+ payload: { kind: "plan.ready-unstarted", teamId: scan.teamId, planId: scan.planId, nodeIds },
58
+ idempotencyKey: `plan-ready:${scan.planId}:${key}` },
59
+ });
60
+ await getGateway().write("plans.readyNotices.mark", { planId: item.planId, nodeIds, now });
61
+ notices++;
62
+ }
63
+ return { notices };
64
+ }
65
+
66
+ export const PLAN_READY_NOTICE_JOB = {
67
+ id: "plan-ready-notice", title: "Ready Plan nodes not started", critical: false,
68
+ description: "Tell each coordinator once when a Plan node has been ready with nothing started for the notice window.",
69
+ intervalMs: 5 * 60_000, deadlineMs: 60_000, runOnStart: false,
70
+ handler: (context: { signal: AbortSignal }) => noticeUnstartedReadyNodes(context.signal),
71
+ } satisfies import("./types").MaintenanceJobDefinition;
@@ -1,17 +1,17 @@
1
+ import { rescheduleRunOnStartJobsAsync, claimMaintenanceRunAsync, finishMaintenanceRunAsync, heartbeatMaintenanceRunAsync, ensureMaintenanceJobsAsync, reclaimDeadLeasesAsync } from "../db/maintenance-job-runs-async";
1
2
  import { timePostBindPhase } from "../post-bind-startup";
2
3
  import { randomUUID } from "node:crypto";
3
4
  import { errMessage, isRecord } from "@humwick/sdk";
4
5
  import { classifyPidLiveness } from "@humwick/sdk/process-utils";
5
6
  import { CRITICAL_MAINTENANCE_PROGRESS_STALE_MS, createActivityEventAsync, getDb, MAINTENANCE_RUN_AUTORESET_GUARD_SQL, recordDrainConvoy, writeTransaction } from "../db";
6
- import { claimMaintenanceRunAsync, finishMaintenanceRunAsync, heartbeatMaintenanceRunAsync } from "../db/maintenance-job-runs-async";
7
- import { ensureMaintenanceJobsAsync, reclaimDeadLeasesAsync, rescheduleRunOnStartJobsAsync } from "../db/maintenance-job-runs-async";
8
- import { reclaimDeadLeases, rescheduleRunOnStartJobs, upsertMaintenanceJobs } from "../db/maintenance-job-reconcile";
7
+ import { reclaimDeadLeases, upsertMaintenanceJobs } from "../db/maintenance-job-reconcile";
9
8
  import type { DeadLease, MaintenanceJobSpec } from "../db/maintenance-job-reconcile";
10
9
  import type { MaintenanceRunOutcome } from "../db/maintenance-job-runs";
11
10
  import { withAbortDeadline } from "../async-deadline";
12
11
  import { MAINTENANCE_BREAKER_LAG_MS } from "../config";
13
12
  import { findPauseByJobId, getMaintenanceJobPause, notPausedSql } from "../db/maintenance-pauses";
14
13
  import { setLagBreakerObserver } from "../event-loop-lag";
14
+ import { framed, runInFrame } from "../hub-metrics";
15
15
  import { resetRateLimitResumeTrackerForTests } from "../services/rate-limit-resume";
16
16
  import { resetTransientTrackerForTests } from "../services/transient-agent-reaper";
17
17
  import { isEphemeralMode } from "../execution-mode";
@@ -79,7 +79,7 @@ const MAINTENANCE_PROGRESS_HEARTBEAT_MS = 60_000;
79
79
  // 3. HARD BLAST-RADIUS CAP: at most ONE new auto-pause per lag sample (operator Q4). If several
80
80
  // qualify, pause the single largest in-gap block and warn about the rest. One freeze may never
81
81
  // pause a fleet.
82
- setLagBreakerObserver((_gapStartAt, gapEndAt, lagMs) => {
82
+ setLagBreakerObserver(async (_gapStartAt, gapEndAt, lagMs) => {
83
83
  const reason = `blocked the event loop ~${Math.round(lagMs)}ms (measured block)`;
84
84
  // #1725 P1 F-A — the ACTUATOR uses the TIGHTENED window `[gapEndAt - lagMs, gapEndAt]`, the actual
85
85
  // freeze interval, NOT the sampler's diagnostic window `[_gapStartAt, gapEndAt]`. The sampler builds
@@ -143,7 +143,7 @@ setLagBreakerObserver((_gapStartAt, gapEndAt, lagMs) => {
143
143
  const blamed = strikable.reduce((worst, entry) => (entry.overlapMs > worst.overlapMs ? entry : worst));
144
144
  if (isRunActive(blamed.run.runId)) abortRun(blamed.run.runId, new MaintenanceBreakerError(`aborted by circuit breaker: ${reason}`));
145
145
  const critical = definitions.find((job) => job.id === blamed.run.jobId)?.critical ?? false;
146
- recordBreakerStrike({ runId: blamed.run.runId, jobId: blamed.run.jobId, critical, kind: "lag", reason });
146
+ (await recordBreakerStrike({ runId: blamed.run.runId, jobId: blamed.run.jobId, critical, kind: "lag", reason }));
147
147
  const others = strikable.filter((entry) => entry !== blamed);
148
148
  if (others.length > 0) {
149
149
  console.warn(`[relay] maintenance circuit breaker: ${strikable.length} runs each blocked >=${Math.round(shareThresholdMs)}ms of this ${Math.round(lagMs)}ms freeze — struck ${blamed.run.jobId} (in-freeze block ${Math.round(blamed.overlapMs)}ms), one-per-sample cap; not struck: ${others.map((entry) => `${entry.run.jobId} (${Math.round(entry.overlapMs)}ms)`).join(", ")}`);
@@ -166,8 +166,8 @@ const runBlockTotalMs = (run: MaintenanceRunRecord): number => Math.round((run.b
166
166
  /** Exposes the internal `runJob` directly, bypassing the `definitions` lookup `runMaintenanceJobNow`
167
167
  * does, so tests can drive the run-registry/timeout/crash paths with a synthetic handler.
168
168
  * Production code never calls this name. */
169
- export const runMaintenanceJobForTests = (definition: MaintenanceJobDefinition, options: { force?: boolean; overridePause?: boolean } = {}) =>
170
- runJob(definition, options);
169
+ export const runMaintenanceJobForTests = async (definition: MaintenanceJobDefinition, options: { force?: boolean; overridePause?: boolean } = {}) =>
170
+ (await runJob(definition, options));
171
171
 
172
172
  export function resetOrphanTrackerForTests(): void {
173
173
  resetOrphanSessionTrackerForTests();
@@ -185,7 +185,7 @@ export function startMaintenanceScheduler(): void {
185
185
  // never bubble to an unhandledRejection that exits the relay process.
186
186
  if (timer) return;
187
187
  timer = setInterval(
188
- () => void runDueMaintenanceJobs().catch((error) => console.warn(`maintenance tick failed: ${errMessage(error)}`)),
188
+ framed("timer maintenance", () => runDueMaintenanceJobs().catch((error) => console.warn(`maintenance tick failed: ${errMessage(error)}`))),
189
189
  SCHEDULER_TICK_MS,
190
190
  );
191
191
  }
@@ -221,8 +221,8 @@ async function bootMaintenanceJobs(): Promise<void> {
221
221
  * doing it from the periodic reconcile would make run-on-start jobs run every
222
222
  * scheduler tick.
223
223
  */
224
- export function scheduleRunOnStartJobs(now: number = Date.now()): void {
225
- rescheduleRunOnStartJobs(now);
224
+ export async function scheduleRunOnStartJobs(now: number = Date.now()): Promise<void> {
225
+ (await rescheduleRunOnStartJobsAsync(now));
226
226
  }
227
227
 
228
228
  function readMaintenanceJobs(): MaintenanceJob[] {
@@ -255,7 +255,7 @@ export async function runMaintenanceJobNow(id: string, options: { overridePause?
255
255
  await ensureMaintenanceJobsAsync(jobSpecs());
256
256
  const definition = definitions.find((job) => job.id === id);
257
257
  if (!definition) throw new Error("maintenance job not found");
258
- return runJob(definition, { force: true, overridePause: options.overridePause });
258
+ return (await runJob(definition, { force: true, overridePause: options.overridePause }));
259
259
  }
260
260
 
261
261
  export async function runLegacyMaintenanceReaper(): Promise<Record<string, unknown>> {
@@ -450,7 +450,12 @@ async function persistRunOutcome(outcome: MaintenanceRunOutcome): Promise<boolea
450
450
  }
451
451
  }
452
452
 
453
- async function runJob(definition: MaintenanceJobDefinition, options: { force?: boolean; overridePause?: boolean } = {}): Promise<MaintenanceJobRun> {
453
+ /** #3798 — each run is its own hub-metrics frame, so a job's main-thread CPU is named per job. */
454
+ function runJob(definition: MaintenanceJobDefinition, options: { force?: boolean; overridePause?: boolean } = {}): Promise<MaintenanceJobRun> {
455
+ return runInFrame({ callsite: `job ${definition.id}` }, () => executeJob(definition, options));
456
+ }
457
+
458
+ async function executeJob(definition: MaintenanceJobDefinition, options: { force?: boolean; overridePause?: boolean }): Promise<MaintenanceJobRun> {
454
459
  const startedAt = Date.now();
455
460
  // One execution boundary covers ticks, forced runs and already-queued definitions.
456
461
  if (isEphemeralMode() && definition.instanceSafe !== true) {
@@ -584,7 +589,7 @@ async function runJob(definition: MaintenanceJobDefinition, options: { force?: b
584
589
  if (persisted && (isTimeout || breakerAborted)) {
585
590
  // A breaker abort is a lag event that the lag observer has usually already struck (deduped by
586
591
  // runId here); a timeout is its own consecutive-overrun signal.
587
- recordBreakerStrike({ runId: run.runId, jobId: definition.id, critical: definition.critical, kind: isTimeout ? "timeout" : "lag", reason: isTimeout ? `timed out after ${deadlineMs}ms` : message });
592
+ (await recordBreakerStrike({ runId: run.runId, jobId: definition.id, critical: definition.critical, kind: isTimeout ? "timeout" : "lag", reason: isTimeout ? `timed out after ${deadlineMs}ms` : message }));
588
593
  }
589
594
  if (definition.logRuns) console.warn(`[relay] maintenance job ${definition.id} ${status} in ${durationMs}ms: ${message}`);
590
595
  return { id: definition.id, status, startedAt, finishedAt, durationMs, error: message, ...(result ? { result } : {}) };
@@ -69,7 +69,7 @@ export async function runStalledDeliveryPager(now: number = Date.now()): Promise
69
69
  state = { ...state, failStreak: 0, okStreak: state.stalled ? state.okStreak + 1 : 0 };
70
70
  if (state.stalled && state.okStreak >= STALL_PAGE_CONFIRM_CHECKS) {
71
71
  if (state.alerted) {
72
- const result = notifyWorkspaceHumanEscalation({
72
+ const result = (await notifyWorkspaceHumanEscalation({
73
73
  subject: "Message delivery recovered",
74
74
  body: `Messages are being delivered to live agents again. The stall began ${new Date(state.since).toISOString()}.`,
75
75
  priority: "default",
@@ -77,7 +77,7 @@ export async function runStalledDeliveryPager(now: number = Date.now()): Promise
77
77
  source: "stalled-delivery-pager",
78
78
  payload: { kind: "message-delivery.recovered", since: state.since, recoveredAt: now },
79
79
  idempotencyKey: `stalled-delivery:${state.since}:recovered`,
80
- });
80
+ }));
81
81
  recovered = result.accepted;
82
82
  if (!result.accepted) console.warn(`[relay] #3014 message delivery recovered — operator NOT paged: ${result.reason}`);
83
83
  }
@@ -89,7 +89,7 @@ export async function runStalledDeliveryPager(now: number = Date.now()): Promise
89
89
  if (state.stalled && !state.alerted) {
90
90
  // Persist the episode first so a crash after the send re-sends under the same key.
91
91
  await persist();
92
- const result = notifyWorkspaceHumanEscalation({
92
+ const result = (await notifyWorkspaceHumanEscalation({
93
93
  subject: "Message delivery stalled",
94
94
  body: [
95
95
  `${check.subjects?.length ?? 0} live, idle agents have had mail unattempted for over 2 minutes and no turn was delivered to any agent in 5 minutes.`,
@@ -102,7 +102,7 @@ export async function runStalledDeliveryPager(now: number = Date.now()): Promise
102
102
  source: "stalled-delivery-pager",
103
103
  payload: { kind: "message-delivery.stalled", since: state.since, count: check.count, subjects: check.subjects },
104
104
  idempotencyKey: `stalled-delivery:${state.since}:alert`,
105
- });
105
+ }));
106
106
  alerted = result.accepted;
107
107
  if (!result.accepted) console.warn(`[relay] #3014 message delivery stalled — operator NOT paged: ${result.reason}`);
108
108
  state = { ...state, alerted: true };
@@ -1,18 +1,7 @@
1
- import {
2
- clearTeamZeroMembersSince,
3
- countLiveTeamMembers,
4
- createActivityEventAsync,
5
- agentsWithPendingOwnRelayRun,
6
- getAgent,
7
- getCrashReport,
8
- hasTerminalEvent,
9
- getTeamBusyState,
10
- getTeamRoster,
11
- listActiveTeams,
12
- setTeamZeroMembersSince,
13
- TEAM_AUTO_REAP_SUMMARY_PREFIX,
14
- } from "../db";
15
- import { clearTeamIdleEntry, coordinatorsWithPendingTimers, getTeamIdleEntries, setTeamIdleEntry } from "../db/team-idle";
1
+ import { setTeamIdleEntryAsync, clearTeamIdleEntryAsync } from "../db/runtime-tail-async";
2
+ import { setTeamZeroMembersSinceAsync, clearTeamZeroMembersSinceAsync } from "../db/teams-async";
3
+ import { countLiveTeamMembers, createActivityEventAsync, agentsWithPendingOwnRelayRun, getAgent, getCrashReport, hasTerminalEvent, getTeamBusyState, getTeamRoster, listActiveTeams, TEAM_AUTO_REAP_SUMMARY_PREFIX } from "../db";
4
+ import { coordinatorsWithPendingTimers, getTeamIdleEntries } from "../db/team-idle";
16
5
  import { openWorkflowRunHolders } from "../db/workflow-task-read-ops";
17
6
  import { STALE_TTL_MS, stewardFallbackTarget } from "../config";
18
7
  import { agentProviderState } from "../provider-state";
@@ -48,13 +37,13 @@ function memberIsAlive(member: AgentCard, freshCutoff: number): boolean {
48
37
  * after its land, a graceful shutdown). This watchdog must surface ONLY a genuine silent death:
49
38
  * an agent that went quiet with NO terminal event of any kind.
50
39
  */
51
- function detectSilentMembers(
40
+ async function detectSilentMembers(
52
41
  teamId: string,
53
42
  coordinatorId: string | undefined,
54
43
  now: number,
55
44
  thresholdMs: number,
56
45
  out: { pingedMembers: string[]; watchedMembers: number },
57
- ): void {
46
+ ): Promise<void> {
58
47
  const freshCutoff = now - thresholdMs;
59
48
  const watch = teamMemberWatchTracker.get(teamId) ?? new Map<string, TeamMemberWatch>();
60
49
  teamMemberWatchTracker.set(teamId, watch);
@@ -85,7 +74,7 @@ function detectSilentMembers(
85
74
  if (getCrashReport(memberId) || hasTerminalEvent(memberId, entry.epoch)) continue;
86
75
  if (!coordinatorId) continue;
87
76
  const minutes = Math.floor(gapMs / 60_000);
88
- notifyTarget(
77
+ await notifyTarget(
89
78
  "team.member_silent",
90
79
  { teamId, agentId: memberId },
91
80
  coordinatorId,
@@ -179,7 +168,7 @@ async function detectGoneCoordinator(
179
168
  watch.escalated = true; // the committed no-target audit is the complete no-silent-dissolve action
180
169
  return;
181
170
  }
182
- const sent = notifyTarget(
171
+ const sent = await notifyTarget(
183
172
  "team.coordinator_gone",
184
173
  { teamId, agentId: coordinatorId, parent: resolveCoordinatorAgentId(coordinatorId) ?? undefined },
185
174
  escalationTarget,
@@ -222,10 +211,10 @@ function teamIdlePayload(teamId: string, coordinatorId: string, thresholdMinutes
222
211
  };
223
212
  }
224
213
 
225
- function notifyTeamIdle(teamId: string, coordinatorId: string, thresholdMinutes: number, idleForMs: number, firstNoBusyAt: number): string | null {
214
+ async function notifyTeamIdle(teamId: string, coordinatorId: string, thresholdMinutes: number, idleForMs: number, firstNoBusyAt: number): Promise<string | null> {
226
215
  const roundedIdleMinutes = Math.max(thresholdMinutes, Math.floor(idleForMs / 60_000));
227
216
  const payload = teamIdlePayload(teamId, coordinatorId, thresholdMinutes, idleForMs, firstNoBusyAt);
228
- return notifyTarget(
217
+ return await notifyTarget(
229
218
  "team.idle",
230
219
  { teamId, agentId: coordinatorId },
231
220
  coordinatorId,
@@ -270,7 +259,7 @@ export async function runTeamIdleWatchdog(input: { now?: number; signal?: AbortS
270
259
  seen.add(team.id);
271
260
  // #634 — runs every cycle, independent of the team-level idle gate below: a single silent
272
261
  // member must surface even while OTHER members keep the team "busy".
273
- detectSilentMembers(team.id, team.coordinatorId, now, thresholdMs, memberSilence);
262
+ await detectSilentMembers(team.id, team.coordinatorId, now, thresholdMs, memberSilence);
274
263
  // #412/#468 — coordinator-gone escalation, also every cycle and independent of the idle gate: a
275
264
  // dead coordinator must surface even if the team isn't (yet) idle, and the idle-ping path can't
276
265
  // reach it (it would just ping the void).
@@ -299,7 +288,7 @@ export async function runTeamIdleWatchdog(input: { now?: number; signal?: AbortS
299
288
  ? busy.busyMemberIds.filter((id) => id !== team.coordinatorId)
300
289
  : busy.busyMemberIds;
301
290
  if (busyMemberIds.length > 0) {
302
- if (existingEntry) clearTeamIdleEntry(team.id);
291
+ if (existingEntry) (await clearTeamIdleEntryAsync(team.id));
303
292
  teamIdleTracker.delete(team.id);
304
293
  busyTeamIds.push(team.id);
305
294
  continue;
@@ -311,7 +300,7 @@ export async function runTeamIdleWatchdog(input: { now?: number; signal?: AbortS
311
300
  }
312
301
 
313
302
  const entry = existingEntry ?? { firstNoBusyAt: now, pinged: false };
314
- if (!existingEntry) setTeamIdleEntry(team.id, entry);
303
+ if (!existingEntry) (await setTeamIdleEntryAsync(team.id, entry));
315
304
  teamIdleTracker.set(team.id, entry);
316
305
  const idleForMs = now - entry.firstNoBusyAt;
317
306
  if (idleForMs < thresholdMs) {
@@ -328,10 +317,10 @@ export async function runTeamIdleWatchdog(input: { now?: number; signal?: AbortS
328
317
  continue;
329
318
  }
330
319
 
331
- const sent = notifyTeamIdle(team.id, team.coordinatorId, config.thresholdMinutes, idleForMs, entry.firstNoBusyAt);
320
+ const sent = await notifyTeamIdle(team.id, team.coordinatorId, config.thresholdMinutes, idleForMs, entry.firstNoBusyAt);
332
321
  if (!sent) continue;
333
322
  entry.pinged = true;
334
- setTeamIdleEntry(team.id, entry);
323
+ (await setTeamIdleEntryAsync(team.id, entry));
335
324
  pingedTeamIds.push(team.id);
336
325
  }
337
326
 
@@ -341,7 +330,7 @@ export async function runTeamIdleWatchdog(input: { now?: number; signal?: AbortS
341
330
  // coordinator that was already told. Skip the sweep instead; the next full pass reclaims them.
342
331
  if (!signal?.aborted) {
343
332
  for (const key of teamIdleTracker.keys()) if (!seen.has(key)) {
344
- clearTeamIdleEntry(key);
333
+ (await clearTeamIdleEntryAsync(key));
345
334
  teamIdleTracker.delete(key);
346
335
  }
347
336
  for (const key of teamMemberWatchTracker.keys()) if (!seen.has(key)) teamMemberWatchTracker.delete(key);
@@ -385,7 +374,7 @@ export async function reapZeroMemberTeams(context: { signal?: AbortSignal } = {}
385
374
  const live = countLiveTeamMembers(team.id, cutoff);
386
375
  if (live > 0) {
387
376
  if (team.zeroMembersSince !== undefined) {
388
- clearTeamZeroMembersSince(team.id);
377
+ (await clearTeamZeroMembersSinceAsync(team.id));
389
378
  clearedTeamIds.push(team.id);
390
379
  }
391
380
  continue;
@@ -393,7 +382,7 @@ export async function reapZeroMemberTeams(context: { signal?: AbortSignal } = {}
393
382
 
394
383
  zeroMemberTeams++;
395
384
  if (team.zeroMembersSince === undefined) {
396
- setTeamZeroMembersSince(team.id, now);
385
+ (await setTeamZeroMembersSinceAsync(team.id, now));
397
386
  trackedTeamIds.push(team.id);
398
387
  continue;
399
388
  }
@@ -401,7 +390,7 @@ export async function reapZeroMemberTeams(context: { signal?: AbortSignal } = {}
401
390
 
402
391
  const outcomeSummary = `${TEAM_AUTO_REAP_SUMMARY_PREFIX} zero live members for ${grace}ms`;
403
392
  // Leave outcomeStatus null (unknown): an abandoned team is NOT a failed one.
404
- const dissolved = dissolveTeamById(team.id, { outcomeSummary });
393
+ const dissolved = await dissolveTeamById(team.id, { outcomeSummary });
405
394
  await createActivityEventAsync({
406
395
  clientId: `team-reap-${team.id}-${now}`,
407
396
  kind: "state",
@@ -1,19 +1,9 @@
1
+ import { releaseExpiredMergeLeasesAsync, reelectRepoStewardAsync } from "../db/runtime-tail-async";
2
+ import { deleteWorkspaceAsync } from "../db/lifecycle-tail-async";
3
+ import { createCommandAsync } from "../commands-write";
1
4
  import { releaseExpiredStewardHandbacks } from "./steward-handback";
2
- import { createCommand } from "../commands-db";
3
- import {
4
- agentsWithRunningCommandTimers,
5
- createActivityEventAsync,
6
- deleteWorkspace,
7
- getAgent,
8
- getRepoSteward,
9
- getWorkspace,
10
- listOrchestrators,
11
- listWorkspaces,
12
- patchWorkspaceMetadata,
13
- reelectRepoSteward,
14
- releaseExpiredMergeLeases,
15
- updateWorkspaceStatusAsync,
16
- setWorkspaceBranchAsync, patchWorkspaceMetadataAsync} from "../db";
5
+
6
+ import { agentsWithRunningCommandTimers, createActivityEventAsync, getAgent, getRepoSteward, getWorkspace, listOrchestrators, listWorkspaces, patchWorkspaceMetadata, updateWorkspaceStatusAsync, setWorkspaceBranchAsync, patchWorkspaceMetadataAsync } from "../db";
17
7
  import type { WorkspaceMergePreview, WorkspaceRecord, WorkspaceStatus } from "../types";
18
8
  import { reconcileLandedWorkspace } from "../branch-landed";
19
9
  import { workspaceStewardEligible } from "../steward-queue";
@@ -187,7 +177,7 @@ export async function scanWorkspaceConflicts(context: { signal?: AbortSignal } =
187
177
  // a landing steward would spawn, find nothing to resolve, and re-strand the row. Route it to
188
178
  // the branch agent's COORDINATOR (owner/fallback) via the custody path instead — they own the
189
179
  // decision to rebase or abandon. (The event-driven auto-merge hook escalates the same way.)
190
- const targets = routeWorkspaceCustodyEscalation({
180
+ const targets = await routeWorkspaceCustodyEscalation({
191
181
  workspace: ws,
192
182
  subject: "Workspace land keeps reverting (moved base)",
193
183
  body: `Workspace \`${ws.branch ?? ws.id}\` in ${ws.repoRoot} keeps getting its tip reverted as ${p.baseRef ?? "base"} moves under it (#1242); it can't auto-land. Please coordinate a rebase or clean-up.`,
@@ -360,7 +350,7 @@ export async function workspaceGC(context: { signal?: AbortSignal } = {}): Promi
360
350
  const now = Date.now();
361
351
  const cutoff = now - WORKSPACE_RETENTION_MS;
362
352
  const reviewCutoff = now - WORKSPACE_REVIEW_TTL_MS;
363
- const releasedLeaseRepos = releaseExpiredMergeLeases(now);
353
+ const releasedLeaseRepos = (await releaseExpiredMergeLeasesAsync(now));
364
354
 
365
355
  const all = listWorkspaces();
366
356
  // #1726 — reclaim expired-terminal rows in a BOUNDED batch. GC can be paused for long stretches
@@ -376,7 +366,7 @@ export async function workspaceGC(context: { signal?: AbortSignal } = {}): Promi
376
366
  const terminalIds: string[] = [];
377
367
  for (const ws of terminalBatch) {
378
368
  if (signal?.aborted) break;
379
- deleteWorkspace(ws.id);
369
+ (await deleteWorkspaceAsync(ws.id));
380
370
  terminalIds.push(ws.id);
381
371
  }
382
372
  // Rows the abort left undeleted are still expired and terminal, so they stay in the backlog the
@@ -394,7 +384,7 @@ export async function workspaceGC(context: { signal?: AbortSignal } = {}): Promi
394
384
  for (const ws of all) {
395
385
  if (signal?.aborted) break;
396
386
  if (!STRANDABLE_STATUSES.has(ws.status) || ws.mode !== "isolated" || !ws.worktreePath) continue;
397
- reelectRepoSteward(ws.repoRoot);
387
+ (await reelectRepoStewardAsync(ws.repoRoot));
398
388
  const fresh = getWorkspace(ws.id);
399
389
  if (!fresh || !STRANDABLE_STATUSES.has(fresh.status)) continue;
400
390
  const ownerAlive = isOwnerAlive(fresh.ownerAgentId);
@@ -518,7 +508,7 @@ export async function workspaceGC(context: { signal?: AbortSignal } = {}): Promi
518
508
  // resolve. (A stranded row that is genuinely in `conflict` status is already handed to a steward
519
509
  // by the conflict-scan path; the GC backstop's job here is custody, not conflict resolution.)
520
510
  const strandedHours = Math.round((now - strandedAt) / (60 * 60 * 1000));
521
- const targets = routeWorkspaceCustodyEscalation({
511
+ const targets = await routeWorkspaceCustodyEscalation({
522
512
  workspace: fresh,
523
513
  subject: "Stranded workspace needs an owner",
524
514
  body: `Workspace \`${fresh.branch ?? fresh.id}\` in ${fresh.repoRoot} is ${fresh.status} and has been stranded for ${strandedHours}h. Please coordinate ${fresh.status === "conflict" ? "conflict resolution" : "review/merge"} or clean up the worktree.`,
@@ -530,13 +520,13 @@ export async function workspaceGC(context: { signal?: AbortSignal } = {}): Promi
530
520
  // fallback configured). Marking this "escalated" would silently swallow the strand and
531
521
  // repeat every sweep forever. Instead leave it UNescalated (don't stamp escalatedAt, so
532
522
  // the next sweep retries once recipients exist) and raise a louder operator-visible alarm.
533
- raiseOperatorAlarm({
523
+ (await raiseOperatorAlarm({
534
524
  clientId: `workspace-gc-unescalatable-${fresh.id}:${strandedAt}:${Math.floor(now / escalationMs)}`,
535
525
  source: "workspace-gc",
536
526
  subject: "Stranded workspace has no one to escalate to",
537
527
  detail: `${fresh.branch ?? fresh.id} in ${fresh.repoRoot} has been ${fresh.status} for ${strandedHours}h but has no owner, coordinator, or fallback configured to escalate to. Configure AGENT_RELAY_WORKSPACE_STEWARD_FALLBACK or reclaim the worktree manually.`,
538
528
  metadata: { maintenanceJobId: "workspace-gc", workspaceId: fresh.id, repoRoot: fresh.repoRoot, branch: fresh.branch, status: fresh.status, strandedAt },
539
- });
529
+ }));
540
530
  continue;
541
531
  }
542
532
  escalationTargets.push(...targets);
@@ -589,7 +579,7 @@ export async function workspaceGC(context: { signal?: AbortSignal } = {}): Promi
589
579
  // operator alarm when it resolves to nobody) instead of `stewardAgentId ?? fallback`.
590
580
  // Most strandable workspaces have no steward, so the old notice silently dropped while
591
581
  // the state change still happened.
592
- const targets = routeWorkspaceCustodyEscalationOrAlarm({
582
+ const targets = await routeWorkspaceCustodyEscalationOrAlarm({
593
583
  source: "workspace-gc",
594
584
  workspace: fresh,
595
585
  type: "workspace.auto_abandoned",
@@ -620,12 +610,12 @@ export async function workspaceGC(context: { signal?: AbortSignal } = {}): Promi
620
610
  if (signal?.aborted) break;
621
611
  const orch = orchestrators.find((candidate) => workspacePathWithinBase(repoRoot, candidate.baseDir));
622
612
  if (!orch?.agentId) continue;
623
- const command = createCommand({
613
+ const command = (await createCommandAsync({
624
614
  type: "workspace.prune",
625
615
  source: "system",
626
616
  target: orch.agentId,
627
617
  params: { repoRoot, requestedBy: "workspace-gc", requestedAt: now },
628
- });
618
+ }));
629
619
  emitCommand(command);
630
620
  pruneCommands.push(command.id);
631
621
  }
@@ -696,7 +686,7 @@ export async function refreshIdleWorkspaces(context: { signal?: AbortSignal } =
696
686
  if (gitAhead !== undefined && gitAhead > 0) { skipped.push(`${ws.id}:ahead`); continue; }
697
687
  if (gitDirtyCount !== undefined && gitDirtyCount > 0) { skipped.push(`${ws.id}:dirty`); continue; }
698
688
 
699
- const command = createCommand({
689
+ const command = (await createCommandAsync({
700
690
  type: "workspace.idle-refresh",
701
691
  source: "system",
702
692
  target: orch.agentId,
@@ -711,7 +701,7 @@ export async function refreshIdleWorkspaces(context: { signal?: AbortSignal } =
711
701
  requestedBy: "workspace-idle-refresh",
712
702
  requestedAt: now,
713
703
  },
714
- });
704
+ }));
715
705
  emitCommand(command);
716
706
  dispatched.push(ws.id);
717
707
  }
@@ -28,11 +28,11 @@ function managedPolicyEscalationRecipients(policy: SpawnPolicy, state: ManagedAg
28
28
  // agent — often the repo steward that lands branches — is down. Route those to the same
29
29
  // recipients as the offline branch, and when there is no reachable recipient raise an
30
30
  // operator-visible alarm instead of silently dropping the signal.
31
- export function escalateManagedSpawnFailure(
31
+ export async function escalateManagedSpawnFailure(
32
32
  policy: SpawnPolicy,
33
33
  state: ManagedAgentState,
34
34
  opts: { phase: "failed" | "register-timeout" | "spawn-backoff"; error: string },
35
- ): void {
35
+ ): Promise<void> {
36
36
  const recipients = managedPolicyEscalationRecipients(policy, state);
37
37
  const queuedMessages = queuedPolicyMessageCount(policy.name);
38
38
  const subject = opts.phase === "failed"
@@ -55,17 +55,17 @@ export function escalateManagedSpawnFailure(
55
55
  error: opts.error,
56
56
  };
57
57
  if (!recipients.length) {
58
- raiseOperatorAlarm({
58
+ (await raiseOperatorAlarm({
59
59
  clientId: `managed-policy-alarm-${opts.phase}-${policy.name}-${policy.orchestratorId}`,
60
60
  source: "lifecycle-manager",
61
61
  subject,
62
62
  detail: `${body}\nNo owner, coordinator, or fallback is reachable to page.`,
63
63
  view: "managed-agents",
64
64
  metadata: payload,
65
- });
65
+ }));
66
66
  return;
67
67
  }
68
- routeNotification({
68
+ await routeNotification({
69
69
  type: "agent.spawn_failed",
70
70
  scope: { target: `policy:${policy.name}` },
71
71
  recipients,
@@ -85,14 +85,14 @@ export function escalateManagedSpawnFailure(
85
85
  });
86
86
  }
87
87
 
88
- export function escalateOfflineOrchestrator(policy: SpawnPolicy, state: ManagedAgentState, orch: Orchestrator | null | undefined): void {
88
+ export async function escalateOfflineOrchestrator(policy: SpawnPolicy, state: ManagedAgentState, orch: Orchestrator | null | undefined): Promise<void> {
89
89
  if (state.consecutiveFailures < ORCHESTRATOR_OFFLINE_ESCALATION_FAILURES) return;
90
90
  const recipients = managedPolicyEscalationRecipients(policy, state);
91
91
  if (!recipients.length) return;
92
92
  const host = orch?.hostname ? `${orch.hostname} (${policy.orchestratorId})` : policy.orchestratorId;
93
93
  const queuedMessages = queuedPolicyMessageCount(policy.name);
94
94
  const nextRetry = state.backoffUntil ? new Date(state.backoffUntil).toISOString() : "not scheduled";
95
- routeNotification({
95
+ await routeNotification({
96
96
  type: "agent.spawn_failed",
97
97
  scope: { target: `policy:${policy.name}` },
98
98
  recipients,
@@ -1,3 +1,4 @@
1
+ import { runnerRuntimeTokenEnvAsync, type RunnerLaunchAuthority, type RunnerRuntimeTokenInput } from "./runtime-tokens";
1
2
  import { getManifest } from "@humwick/providers";
2
3
  import { getAgentProfile, resolveAgentProfileKey } from "./config-store";
3
4
  import { effectiveProviderCatalogListAsync, ValidationError, withResolvedProvisioning, withResolvedProvisioningAsync } from "./db";
@@ -7,7 +8,6 @@ import { getAgent } from "./db/agents";
7
8
  import { getOrchestratorAsync } from "./db/orchestrators-async";
8
9
  import { composeProfileSpawnInstructions, composeProfileSpawnInstructionsAsync } from "./project-instructions";
9
10
  import { getSelfEffortCeiling } from "./db/self-effort-permissions";
10
- import { runnerRuntimeTokenEnv, runnerRuntimeTokenEnvAsync, type RunnerLaunchAuthority, type RunnerRuntimeTokenInput } from "./runtime-tokens";
11
11
  import { recordedLaunchMode, resolveHostLaunchMode } from "./launch-mode";
12
12
  import { buildSpawnCommand, generateSpawnAgentId, resolveSpawnModelParams, resolveSpawnModelParamsAsync } from "./spawn-command";
13
13
  import { applyDefaultSharedMcp } from "./shared-mcp";
@@ -65,7 +65,7 @@ function systemTagsForManagedPolicy(policy: SpawnPolicy): string[] {
65
65
  return tags;
66
66
  }
67
67
 
68
- export function buildManagedSpawnParams(policy: SpawnPolicy, requestId: string, ctx: ManagedSpawnContext): Record<string, unknown> {
68
+ export async function buildManagedSpawnParams(policy: SpawnPolicy, requestId: string, ctx: ManagedSpawnContext): Promise<Record<string, unknown>> {
69
69
  if (policy.profile) policy = { ...policy, profile: resolveAgentProfileKey(policy.profile) ?? policy.profile };
70
70
  assertManagedProviderEnabled(managedPolicyProviderDisabledMessage(policy));
71
71
  const agentId = ctx.identityAgentId ?? generateSpawnAgentId();
@@ -97,7 +97,7 @@ export function buildManagedSpawnParams(policy: SpawnPolicy, requestId: string,
97
97
 
98
98
  return buildManagedCommand({ policy, requestId, ctx, agentId, prompt, agentProfile, orchestrator, extraCwdPrefixes, composed,
99
99
  modelParams: resolveSpawnModelParams(policy.provider, policy.model, policy.effort),
100
- env: runnerRuntimeTokenEnv(managedTokenInput(policy, requestId, ctx, agentId, extraCwdPrefixes)),
100
+ env: (await runnerRuntimeTokenEnvAsync(managedTokenInput(policy, requestId, ctx, agentId, extraCwdPrefixes))),
101
101
  });
102
102
  }
103
103
 
@@ -1,9 +1,10 @@
1
+ import { upsertManagedAgentStateAsync, updateManagedAgentStateAsync } from "./db/lifecycle-tail-async";
1
2
  // #2656 — "off means nothing new starts on it": a managed policy whose provider is disabled (operator
2
3
  // switch or all models disabled) must not launch. The lifecycle manager parks the policy with a
3
4
  // visible reason instead of spawning — no backoff churn, no crash loop — and its normal reconcile
4
5
  // tick resumes the policy on its own once the provider is re-enabled.
5
6
  import { findProviderCatalog, providerDisabledMessage, type ProviderCatalogEntry } from "@humwick/sdk/provider-catalog";
6
- import { getManagedAgentState, updateManagedAgentState, upsertManagedAgentState } from "./config-store";
7
+ import { getManagedAgentState } from "./config-store";
7
8
  import { effectiveProviderCatalogList } from "./provider-catalog-store";
8
9
  import type { ManagedAgentState, SpawnPolicy } from "./types";
9
10
 
@@ -17,11 +18,11 @@ export function managedPolicyProviderDisabledMessage(policy: Pick<SpawnPolicy, "
17
18
  * Record the skipped launch on the policy's state (`stopped` + lastError) so the dashboard shows why.
18
19
  * Returns the new state only when it changed, so a caller emits once rather than every tick.
19
20
  */
20
- export function parkManagedPolicyForDisabledProvider(policy: SpawnPolicy, message: string): ManagedAgentState | undefined {
21
+ export async function parkManagedPolicyForDisabledProvider(policy: SpawnPolicy, message: string): Promise<ManagedAgentState | undefined> {
21
22
  const state = getManagedAgentState(policy.name);
22
23
  if (state?.status === "stopped" && state.lastError === message) return undefined;
23
24
  const next = state
24
- ? updateManagedAgentState(policy.name, { status: "stopped", spawnRequestId: undefined, lastError: message })
25
- : upsertManagedAgentState({ policyName: policy.name, status: "stopped", orchestratorId: policy.orchestratorId, provider: policy.provider, restartCount: 0, consecutiveFailures: 0, lastError: message });
25
+ ? (await updateManagedAgentStateAsync(policy.name, { status: "stopped", spawnRequestId: undefined, lastError: message }))
26
+ : (await upsertManagedAgentStateAsync({ policyName: policy.name, status: "stopped", orchestratorId: policy.orchestratorId, provider: policy.provider, restartCount: 0, consecutiveFailures: 0, lastError: message }));
26
27
  return next ?? undefined;
27
28
  }
@@ -1,4 +1,5 @@
1
- import { getManagedAgentState, getSpawnPolicy, upsertManagedAgentState } from "./config-store";
1
+ import { upsertManagedAgentStateAsync } from "./db/lifecycle-tail-async";
2
+ import { getManagedAgentState, getSpawnPolicy } from "./config-store";
2
3
  import type { ManagedAgentState, SpawnPolicy } from "./types";
3
4
 
4
5
  /** True only while an async preparation still owns the policy's starting reservation. */
@@ -15,9 +16,9 @@ export function managedSpawnReservationCurrent(policy: SpawnPolicy, policySnapsh
15
16
  }
16
17
 
17
18
  /** Return a preparation's own reservation to its pre-spawn state when the host drains. */
18
- export function restoreDrainedSpawnReservation(policy: SpawnPolicy, requestId: string, prior: ManagedAgentState | null): void {
19
+ export async function restoreDrainedSpawnReservation(policy: SpawnPolicy, requestId: string, prior: ManagedAgentState | null): Promise<void> {
19
20
  const current = getManagedAgentState(policy.name);
20
21
  if (current?.status !== "starting" || current.spawnRequestId !== requestId) return;
21
- upsertManagedAgentState(prior ?? { policyName: policy.name, status: "stopped", orchestratorId: policy.orchestratorId,
22
- provider: policy.provider, spawnRequestId: undefined, restartCount: 0, consecutiveFailures: 0 });
22
+ (await upsertManagedAgentStateAsync(prior ?? { policyName: policy.name, status: "stopped", orchestratorId: policy.orchestratorId,
23
+ provider: policy.provider, spawnRequestId: undefined, restartCount: 0, consecutiveFailures: 0 }));
23
24
  }