@humwick/server 0.130.0 → 0.130.2

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 (486) hide show
  1. package/README.md +5 -0
  2. package/docs/guides/tools-administration.md +6 -1
  3. package/docs/guides/tools-coordination.md +1 -0
  4. package/docs/guides/tools-tasks.md +8 -0
  5. package/docs/guides/workflows.md +58 -0
  6. package/docs/openapi.json +414 -1
  7. package/package.json +8 -17
  8. package/public/assets/{MessageBubble-BWpoBjl4.js → MessageBubble-BWLqMGKG.js} +2 -2
  9. package/public/assets/{PlanGraphPanel-C0auck3n.js → PlanGraphPanel-3TlIQpjk.js} +2 -2
  10. package/public/assets/{TraceView-DBnOx7AQ.js → TraceView-Wimm0Czv.js} +2 -2
  11. package/public/assets/{VaultDecisionCard-0kp2oNBC.js → VaultDecisionCard-Bau_vNax.js} +2 -2
  12. package/public/assets/{activity-yLztXEBt.js → activity-CrSurn-r.js} +2 -2
  13. package/public/assets/{agent-lineage-BbJDR1dW.js → agent-lineage-CknAJKcb.js} +2 -2
  14. package/public/assets/agent-liveness-CaNwK0Zf.js +2 -0
  15. package/public/assets/{agent-name-CTZBrSD9.js → agent-name-DKbiIhBo.js} +2 -2
  16. package/public/assets/agent-profiles-DwF6EQ4R.js +2 -0
  17. package/public/assets/{agent-project-badge-9TqzUUYE.js → agent-project-badge-Diz4Sjjb.js} +2 -2
  18. package/public/assets/{agent-tools-D4n40pn8.js → agent-tools-CRhpRQza.js} +2 -2
  19. package/public/assets/{agent-type-icon-D6FUarOf.js → agent-type-icon-LrUEU3lB.js} +2 -2
  20. package/public/assets/{agents-BYDslkJc.js → agents-B-v66C-4.js} +2 -2
  21. package/public/assets/{analytics-B-XSbEMC.js → analytics-BqCb-LKi.js} +2 -2
  22. package/public/assets/{artifact-viewer-DdWIfV6M.js → artifact-viewer-kf4pGuAR.js} +2 -2
  23. package/public/assets/{artifacts-oBOqks-2.js → artifacts-D32zyw4Y.js} +2 -2
  24. package/public/assets/{artifacts-DPN1q4pA.js → artifacts-DJevXcvg.js} +2 -2
  25. package/public/assets/{automation-4KcRFXEu.js → automation-BpRajx_5.js} +2 -2
  26. package/public/assets/{backlog-Cw9xPTCF.js → backlog-D_UjTdzX.js} +2 -2
  27. package/public/assets/{busy-health-CTu32MZt.js → busy-health-N3VEh4ao.js} +2 -2
  28. package/public/assets/{channels-BiXzg1G9.js → channels-9jopungq.js} +2 -2
  29. package/public/assets/{chat-Ctk2qwvA.js → chat-CSXrTB3C.js} +2 -2
  30. package/public/assets/{code-preview-Bm1wWqAp.js → code-preview-p1NnmqK0.js} +2 -2
  31. package/public/assets/{connectors-C36ax4Vw.js → connectors-CZdqlDNL.js} +2 -2
  32. package/public/assets/{cosmos-Jj9LK2Wf.js → cosmos-BgdsE4dG.js} +2 -2
  33. package/public/assets/{crate-agent-H6S7-B9T.js → crate-agent-30aXTKva.js} +2 -2
  34. package/public/assets/{dependency-graph-Cd1E23wa.js → dependency-graph-B_jvw2GE.js} +2 -2
  35. package/public/assets/{deploys-Bwfq146q.js → deploys-DoN6bAb5.js} +2 -2
  36. package/public/assets/{display-CfO7rTuF.js → display-BrKWFGJz.js} +2 -2
  37. package/public/assets/{files-DhhZI2Ih.js → files-D6XEu8Ng.js} +2 -2
  38. package/public/assets/fleet-C7jQWbw2.js +2 -0
  39. package/public/assets/{fleet-metrics-BkHP-P3R.js → fleet-metrics-BiDs3MLx.js} +2 -2
  40. package/public/assets/{formatted-body-Cm1RrzmN.js → formatted-body-CQBwc1fb.js} +3 -3
  41. package/public/assets/{formatted-body-impl-DQiOuOD1.js → formatted-body-impl-DyG0WsQS.js} +2 -2
  42. package/public/assets/{host-providers-dialog-CHqwXTov.js → host-providers-dialog-DKaW31dN.js} +2 -2
  43. package/public/assets/index-CRHep3M9.js +15 -0
  44. package/public/assets/index-MYAIwYTX.css +2 -0
  45. package/public/assets/{insights-fH1EL-qZ.js → insights-CzrtkpOE.js} +2 -2
  46. package/public/assets/{integrations-C5Oix6ng.js → integrations-aVsUmigt.js} +2 -2
  47. package/public/assets/{issue-ref-chip-ODyondIa.js → issue-ref-chip-BnSlM_95.js} +2 -2
  48. package/public/assets/{log-viewer-Cvfy6PW7.js → log-viewer-qe7Ee47Z.js} +2 -2
  49. package/public/assets/{maintenance-D8CDH5wZ.js → maintenance-NfjqFZAk.js} +2 -2
  50. package/public/assets/{managed-agents-egllbpij.js → managed-agents-BiH0HgaE.js} +2 -2
  51. package/public/assets/{markdown-preview-CYSFgVgK.js → markdown-preview-BixaT3Xr.js} +3 -3
  52. package/public/assets/{markdown-preview-impl-BteVIjX4.js → markdown-preview-impl-DNGlWIS3.js} +2 -2
  53. package/public/assets/{memory-C8WmXsyr.js → memory-DNsCl6UX.js} +2 -2
  54. package/public/assets/{messages-BzQdqlO6.js → messages-DopU1L1X.js} +2 -2
  55. package/public/assets/{orchestrators-B1HxzkNw.js → orchestrators-Cq7ckLQT.js} +2 -2
  56. package/public/assets/{overview-DCUnI4Z7.js → overview-C1DCTzzV.js} +2 -2
  57. package/public/assets/{pairs-ftCd2XcD.js → pairs-DE_m6rrE.js} +2 -2
  58. package/public/assets/{profile-icon-B2zH8DMH.js → profile-icon-B3rfh77F.js} +2 -2
  59. package/public/assets/project-profile-instructions-DQWZE0kA.js +2 -0
  60. package/public/assets/projects-Br2Bd2h1.js +2 -0
  61. package/public/assets/{prompt-settings-Bftq9czX.js → prompt-settings-BKvFsvGD.js} +2 -2
  62. package/public/assets/provider-display-DsuWMvVL.js +2 -0
  63. package/public/assets/{registry-DWCU5c8q.js → registry-Cw38_6gW.js} +2 -2
  64. package/public/assets/run-card-CpKqRbhg.js +2 -0
  65. package/public/assets/scroll-text-BKXS1ahr.js +2 -0
  66. package/public/assets/{secrets-C7GIUATz.js → secrets-DxI-vzSq.js} +2 -2
  67. package/public/assets/{service-access-settings-C3aujqTG.js → service-access-settings-D9s_VJbd.js} +2 -2
  68. package/public/assets/{settings-C6sZ9eBa.js → settings-CKZTVGIx.js} +2 -2
  69. package/public/assets/{shipping-Bu8p1gEw.js → shipping-B9YtnwtG.js} +2 -2
  70. package/public/assets/{stats-V9Z-GxUd.js → stats-DyUBKyWI.js} +2 -2
  71. package/public/assets/{status-dot-CVv0iGHL.js → status-dot-DYqlEUhJ.js} +2 -2
  72. package/public/assets/{store-BvtFdlGX.js → store-DcClXLH0.js} +3 -3
  73. package/public/assets/{tasks-CXU75HRF.js → tasks-Bh7Chr4-.js} +2 -2
  74. package/public/assets/{teams-BPf976zb.js → teams-bowI7qVY.js} +2 -2
  75. package/public/assets/{terminal-viewer-DE9BOe-8.js → terminal-viewer-BgVLHdsU.js} +3 -3
  76. package/public/assets/{terminal-viewer-impl-D04pjmGh.js → terminal-viewer-impl-Dz22UPy6.js} +2 -2
  77. package/public/assets/token-estimate-Co-Jdphh.js +2 -0
  78. package/public/assets/{use-agent-terminal-B1yR456R.js → use-agent-terminal-DSQQ-iY7.js} +2 -2
  79. package/public/assets/{use-decisions-D-S4tpDf.js → use-decisions-samMQhQf.js} +2 -2
  80. package/public/assets/use-project-list-59LheVP7.js +2 -0
  81. package/public/assets/{use-selectors-CnrAk2YP.js → use-selectors-BZa25xEB.js} +2 -2
  82. package/public/assets/{user-avatar-nBFIcR4M.js → user-avatar-DHxlvUvV.js} +2 -2
  83. package/public/assets/versioned-prompt-editor-BmED5DnJ.js +8 -0
  84. package/public/assets/{work-queue-TCjJxeT-.js → work-queue-CSvpuNLz.js} +2 -2
  85. package/public/assets/{workflow-marker-Cf3Hf2fN.js → workflow-marker-DAZcj5qt.js} +2 -2
  86. package/public/assets/{workflows-CXQX20Qj.js → workflows-DdmxRSyg.js} +2 -2
  87. package/public/assets/{workspaces-qSYzKYMA.js → workspaces-BxeKfVuF.js} +2 -2
  88. package/public/cosmos.html +4 -4
  89. package/public/index.html +31 -29
  90. package/runner/plugins/claude/.claude-plugin/plugin.json +1 -1
  91. package/runner/plugins/claude/hooks/gate-run-guard.provisioned.mjs +31 -1
  92. package/runner/src/adapters/claude-effort.ts +2 -4
  93. package/runner/src/adapters/codex-launch-config.ts +4 -5
  94. package/runner/src/adapters/codex.ts +1 -1
  95. package/runner/src/adapters/opencode-provider.ts +7 -22
  96. package/runner/src/adapters/opencode.ts +4 -4
  97. package/runner/src/agent-runner/credentials.ts +42 -12
  98. package/runner/src/agent-runner/inbox.ts +27 -0
  99. package/runner/src/agent-runner/lifecycle-milestones.ts +0 -2
  100. package/runner/src/bin/claude.ts +4 -0
  101. package/runner/src/bin/codex.ts +4 -0
  102. package/runner/src/bin/opencode.ts +4 -0
  103. package/runner/src/index.ts +5 -2
  104. package/runner/src/provider-setup.ts +34 -0
  105. package/runner/src/providers.ts +2 -1
  106. package/runner/src/runner-core.ts +18 -23
  107. package/runner/src/spawn-input.ts +2 -1
  108. package/runner/src/test-run-dedup.ts +59 -56
  109. package/scripts/cd-config.ts +24 -3
  110. package/scripts/gate-run-lease.ts +10 -7
  111. package/scripts/gate.sh +5 -1
  112. package/src/branch-land-notice.ts +93 -0
  113. package/src/branch-landed.ts +7 -76
  114. package/src/bus.ts +19 -20
  115. package/src/cd-deploy-notify.ts +30 -2
  116. package/src/cd-host-lag-notify.ts +7 -6
  117. package/src/ci/incidents.ts +2 -2
  118. package/src/ci/monitor.ts +5 -8
  119. package/src/ci/root-missing.ts +2 -7
  120. package/src/ci/transition-notify.ts +7 -39
  121. package/src/cli/cd-pauses.ts +19 -0
  122. package/src/cli/orchestrator.ts +1 -1
  123. package/src/cli/state.ts +2 -19
  124. package/src/cli/task-edit.ts +22 -0
  125. package/src/cli/task.ts +3 -1
  126. package/src/cli/workflow.ts +8 -2
  127. package/src/commands-db.ts +10 -6
  128. package/src/commands-write.ts +8 -0
  129. package/src/config-store-write.ts +13 -1
  130. package/src/config-store.ts +25 -14
  131. package/src/db/activity-async.ts +6 -1
  132. package/src/db/activity-write-ops.ts +13 -1
  133. package/src/db/activity.ts +1 -14
  134. package/src/db/agent-lifecycle-write-ops.ts +1 -0
  135. package/src/db/agent-meta.ts +2 -2
  136. package/src/db/agent-ownership-write-ops.ts +4 -0
  137. package/src/db/agent-search.ts +1 -15
  138. package/src/db/agents-async.ts +58 -7
  139. package/src/db/agents-write-ops.ts +28 -5
  140. package/src/db/agents.ts +81 -54
  141. package/src/db/artifacts-write-ops.ts +8 -3
  142. package/src/db/bus-outbox-write-ops.ts +1 -0
  143. package/src/db/channels-write-ops.ts +4 -0
  144. package/src/db/ci-incidents-async.ts +5 -5
  145. package/src/db/ci-incidents-write-ops.ts +2 -2
  146. package/src/db/claude-api-budget-ops.ts +5 -5
  147. package/src/db/claude-app-oauth.ts +4 -4
  148. package/src/db/codex-reset-consumes-write-ops.ts +3 -0
  149. package/src/db/command-row.ts +10 -2
  150. package/src/db/commands-list-query.ts +2 -0
  151. package/src/db/commands-spawn-read.ts +1 -1
  152. package/src/db/commands-write-ops.ts +21 -0
  153. package/src/db/config-store-write-ops.ts +9 -2
  154. package/src/db/continuation-archives-async.ts +5 -0
  155. package/src/db/continuation-archives-write-ops.ts +9 -0
  156. package/src/db/continuations.ts +2 -2
  157. package/src/db/db-writer-worker.ts +8 -1
  158. package/src/db/delivery-write-ops.ts +5 -0
  159. package/src/db/deploy-events-async.ts +1 -0
  160. package/src/db/deploy-events-read-ops.ts +2 -1
  161. package/src/db/deploy-events.ts +35 -4
  162. package/src/db/hosts-registration.ts +10 -2
  163. package/src/db/hosts-write-ops.ts +14 -0
  164. package/src/db/hosts.ts +9 -6
  165. package/src/db/inbox-write-ops.ts +4 -0
  166. package/src/db/inbox.ts +0 -14
  167. package/src/db/index.ts +2 -0
  168. package/src/db/insights-write-ops.ts +1 -0
  169. package/src/db/integrations-async.ts +8 -0
  170. package/src/db/integrations-ops.ts +15 -0
  171. package/src/db/integrations.ts +15 -21
  172. package/src/db/issue-lifecycle.ts +3 -4
  173. package/src/db/issue-refs-async.ts +20 -0
  174. package/src/db/issue-refs-write-ops.ts +29 -0
  175. package/src/db/issue-refs.ts +7 -8
  176. package/src/db/long-running-work.ts +2 -2
  177. package/src/db/macos-privacy.ts +1 -1
  178. package/src/db/main-connection-writes.ts +23 -2
  179. package/src/db/maintenance-job-reconcile.ts +61 -0
  180. package/src/db/maintenance-job-runs-async.ts +14 -0
  181. package/src/db/maintenance-job-write-ops.ts +9 -0
  182. package/src/db/mappers.ts +2 -14
  183. package/src/db/merge-lease.ts +1 -15
  184. package/src/db/message-reads-write-ops.ts +3 -0
  185. package/src/db/message-write-ops.ts +4 -0
  186. package/src/db/migrations.ts +7 -15
  187. package/src/db/orchestrators-async.ts +7 -3
  188. package/src/db/orchestrators-write-ops.ts +7 -1
  189. package/src/db/orchestrators.ts +21 -77
  190. package/src/db/pairs.ts +1 -13
  191. package/src/db/project-profile-instructions.ts +50 -0
  192. package/src/db/prompt-versions-ops.ts +1 -0
  193. package/src/db/prompt-versions.ts +9 -2
  194. package/src/db/provider-catalog-write-ops.ts +4 -4
  195. package/src/db/provider-config-history.ts +8 -3
  196. package/src/db/provider-flap-messages-write-ops.ts +1 -0
  197. package/src/db/provider-model-seed.ts +34 -0
  198. package/src/db/provider-quota-lease-write-ops.ts +1 -0
  199. package/src/db/provider-quota-write-ops.ts +2 -0
  200. package/src/db/provisioning-write-ops.ts +9 -8
  201. package/src/db/read-op-marshaling-fixtures.ts +5 -0
  202. package/src/db/read-ops.ts +4 -0
  203. package/src/db/routing-policy.ts +3 -4
  204. package/src/db/schema.ts +9 -3
  205. package/src/db/secrets-broker-write-ops.ts +5 -5
  206. package/src/db/secrets-vault-write.ts +7 -7
  207. package/src/db/services-write-ops.ts +3 -0
  208. package/src/db/spawn-delegation-state.ts +11 -4
  209. package/src/db/spawn-tree-cleanup.ts +3 -0
  210. package/src/db/stats-async.ts +4 -3
  211. package/src/db/task-entity.ts +23 -2
  212. package/src/db/task-list.ts +3 -1
  213. package/src/db/tasks.ts +1 -17
  214. package/src/db/team-spawn-delegation.ts +1 -1
  215. package/src/db/teams-write-ops.ts +4 -4
  216. package/src/db/timer-cutover-write-ops.ts +7 -0
  217. package/src/db/timer-ingress-write-ops.ts +9 -0
  218. package/src/db/timer-ingress.ts +7 -1
  219. package/src/db/timer-operator-write-ops.ts +14 -0
  220. package/src/db/timer-transfer-write-ops.ts +4 -0
  221. package/src/db/timer-write-ops.ts +14 -0
  222. package/src/db/token-db-write-ops.ts +21 -0
  223. package/src/db/token-standing.ts +15 -11
  224. package/src/db/user-settings-write-ops.ts +1 -0
  225. package/src/db/vault-backup.ts +2 -2
  226. package/src/db/vault-expiry.ts +2 -2
  227. package/src/db/vault-health.ts +3 -3
  228. package/src/db/vault-token-ops.ts +8 -8
  229. package/src/db/web-tool-call-ops.ts +3 -3
  230. package/src/db/workflow-contact.ts +1 -1
  231. package/src/db/workflow-task-read-ops.ts +30 -0
  232. package/src/db/write-ops.ts +15 -1
  233. package/src/db/writer-fault-seam.ts +5 -2
  234. package/src/db/writer-gateway.ts +21 -12
  235. package/src/dispatch-availability.ts +10 -6
  236. package/src/ephemeral/{landing-ingress-proof-worker.ts → landing-ingress-proof.test-fixture.ts} +4 -2
  237. package/src/host/disk-alert.ts +121 -0
  238. package/src/lifecycle-manager.ts +3 -2
  239. package/src/maintenance/cd-watcher.ts +3 -3
  240. package/src/maintenance/critical-job-watchdog.ts +16 -17
  241. package/src/maintenance/index.ts +1 -0
  242. package/src/maintenance/jobs.ts +3 -2
  243. package/src/maintenance/scheduler.ts +84 -128
  244. package/src/maintenance/stalled-delivery-pager.ts +6 -5
  245. package/src/maintenance/workspaces.ts +1 -1
  246. package/src/managed-policy.ts +31 -27
  247. package/src/mcp/index.ts +5 -2
  248. package/src/mcp/output-descriptions.ts +8 -8
  249. package/src/mcp/presentation.ts +4 -4
  250. package/src/mcp/project-profile-instructions.ts +32 -0
  251. package/src/mcp/tool-groups.ts +1 -1
  252. package/src/mcp/tools-spawn.ts +4 -3
  253. package/src/mcp-agent-action.ts +1 -2
  254. package/src/mcp-host-tools.ts +1 -1
  255. package/src/mcp-issue-ref-tools.ts +12 -11
  256. package/src/mcp-maintenance-tools.ts +2 -2
  257. package/src/mcp-profile-trust-tools.ts +4 -4
  258. package/src/mcp-provider-catalog.ts +2 -2
  259. package/src/mcp-task-tools.ts +11 -5
  260. package/src/mcp-team-template-tools.ts +3 -3
  261. package/src/mcp-workflow-tools.ts +10 -3
  262. package/src/notification-types.ts +2 -1
  263. package/src/operator-alarm.ts +26 -11
  264. package/src/package-pack.ts +42 -39
  265. package/src/project-instructions.ts +4 -0
  266. package/src/project-profile-instruction-text.ts +6 -0
  267. package/src/project-profile-instructions.ts +20 -0
  268. package/src/provider-catalog-store.ts +3 -49
  269. package/src/provider-config-store.ts +12 -11
  270. package/src/provider-model-seed.ts +1 -18
  271. package/src/provider-quota-config-store.ts +5 -4
  272. package/src/provider-secrets.test-fixture.ts +3 -3
  273. package/src/recipe-runner.ts +2 -2
  274. package/src/repo-config-source.ts +10 -4
  275. package/src/routes/agent-profiles.ts +6 -6
  276. package/src/routes/agents-spawn.ts +1 -1
  277. package/src/routes/agents.ts +6 -6
  278. package/src/routes/commands-listing.ts +4 -0
  279. package/src/routes/commands.ts +2 -2
  280. package/src/routes/continuation-archives.ts +2 -2
  281. package/src/routes/host-resource-control.ts +4 -1
  282. package/src/routes/index.ts +10 -1
  283. package/src/routes/integrations.ts +24 -50
  284. package/src/routes/issue-refs.ts +7 -7
  285. package/src/routes/orchestrator-bootstrap.ts +3 -3
  286. package/src/routes/orchestrator-host-registration.ts +2 -2
  287. package/src/routes/orchestrator-host-utilization.ts +3 -1
  288. package/src/routes/orchestrator-proxy.ts +2 -2
  289. package/src/routes/orchestrator.ts +4 -4
  290. package/src/routes/project-profile-instructions.ts +27 -0
  291. package/src/routes/project-trust.ts +2 -2
  292. package/src/routes/provider-config.ts +9 -8
  293. package/src/routes/provider-quota-config.ts +2 -2
  294. package/src/routes/settings.ts +5 -5
  295. package/src/routes/stats.ts +6 -6
  296. package/src/routes/task-http.ts +15 -1
  297. package/src/routes/task-issues.ts +11 -26
  298. package/src/routes/tasks.ts +22 -53
  299. package/src/routes/team-templates.ts +5 -5
  300. package/src/routes/teams-config.ts +2 -2
  301. package/src/routes/tokens.ts +12 -5
  302. package/src/routes/workflows.ts +20 -5
  303. package/src/routing-policy-normalize.ts +1 -2
  304. package/src/routing-step-defaults.ts +0 -2
  305. package/src/runner-token-mint-limit.ts +63 -0
  306. package/src/runtime-grant.ts +4 -2
  307. package/src/runtime-tokens.ts +50 -9
  308. package/src/security.ts +13 -20
  309. package/src/server-background.ts +1 -1
  310. package/src/server.ts +5 -2
  311. package/src/services/host-utilization.ts +23 -0
  312. package/src/services/idle-epics.ts +5 -6
  313. package/src/services/issue-lifecycle.ts +57 -61
  314. package/src/services/issue-refs.ts +9 -13
  315. package/src/services/kernel-timers-cutover-chaos-worker.ts +12 -7
  316. package/src/services/kernel-timers-ingress.ts +1 -1
  317. package/src/services/kernel-timers-request-key-migration-chaos-worker.ts +4 -1
  318. package/src/services/landed-ancestry.ts +19 -3
  319. package/src/services/memory-areas.ts +15 -51
  320. package/src/services/plan-ci-notice.ts +57 -0
  321. package/src/services/plan-ci-recovery.ts +2 -55
  322. package/src/services/plan-wake-digest.ts +1 -0
  323. package/src/services/profile-trust.ts +4 -3
  324. package/src/services/project-config-read.ts +70 -0
  325. package/src/services/project-profile-instructions.ts +54 -0
  326. package/src/services/provisioning-harness/budget-baseline.json +4 -4
  327. package/src/services/restart-spawn.ts +61 -49
  328. package/src/services/self-care-state.ts +2 -1
  329. package/src/services/self-effort.ts +5 -5
  330. package/src/services/send-message.ts +5 -1
  331. package/src/services/spawn-agent.ts +13 -2
  332. package/src/services/spawn-chain-inspector.ts +6 -13
  333. package/src/services/spawn-policy.ts +68 -0
  334. package/src/services/spawn-tree-cleanup.ts +2 -2
  335. package/src/services/task-dispatch.ts +3 -0
  336. package/src/services/task-state-reconcile.ts +6 -14
  337. package/src/services/team-templates.ts +9 -8
  338. package/src/services/transient-agent-reaper.ts +3 -1
  339. package/src/services/workflow-auto-start.ts +25 -13
  340. package/src/services/workflow-ci-main.ts +1 -1
  341. package/src/services/workflow-deferred-cleanup.ts +4 -2
  342. package/src/services/workflow-effect-driver.ts +2 -0
  343. package/src/services/workflow-effects.ts +18 -11
  344. package/src/services/workflow-land-notice.ts +10 -0
  345. package/src/services/workflow-pause-text.ts +43 -0
  346. package/src/services/workflow-reconcile.ts +13 -11
  347. package/src/services/workflow-run-view.ts +2 -0
  348. package/src/services/workflow-runs.ts +29 -13
  349. package/src/services/workflow-supervisor-state.ts +5 -0
  350. package/src/services/workflow-supervisor.ts +7 -3
  351. package/src/settings/registry.ts +1 -2
  352. package/src/spawn-command.ts +4 -4
  353. package/src/spawn-models.ts +10 -12
  354. package/src/spawn-placement.ts +1 -1
  355. package/src/spawn-targets.ts +1 -1
  356. package/src/startup-context-injection.ts +1 -0
  357. package/src/state-adoption/cd-cutover.ts +53 -7
  358. package/src/state-adoption/fleet-cutover.ts +35 -0
  359. package/src/state-adoption/fleet-plan.ts +36 -0
  360. package/src/state-adoption/index.ts +2 -2
  361. package/src/state-adoption/linux-plan.ts +2 -2
  362. package/src/state-adoption/linux.ts +2 -2
  363. package/src/state-adoption/macos-plan.ts +2 -2
  364. package/src/state-adoption/macos.ts +1 -1
  365. package/src/steward.ts +7 -6
  366. package/src/task-dispatch-live.ts +1 -1
  367. package/src/task-dispatcher.ts +1 -1
  368. package/src/teams-config-store.ts +4 -3
  369. package/src/token-db-write.ts +10 -0
  370. package/src/workspace-command-settle.ts +2 -1
  371. package/src/workspace-steward-wake.ts +2 -2
  372. package/node_modules/@humwick/kernel/LICENSE +0 -167
  373. package/node_modules/@humwick/kernel/async-entity-persistence.ts +0 -344
  374. package/node_modules/@humwick/kernel/async-event-log.ts +0 -107
  375. package/node_modules/@humwick/kernel/automations.pilot.ts +0 -147
  376. package/node_modules/@humwick/kernel/config.ts +0 -111
  377. package/node_modules/@humwick/kernel/contract.ts +0 -572
  378. package/node_modules/@humwick/kernel/control-authz.ts +0 -281
  379. package/node_modules/@humwick/kernel/control.ts +0 -645
  380. package/node_modules/@humwick/kernel/effects.ts +0 -493
  381. package/node_modules/@humwick/kernel/event-log.ts +0 -319
  382. package/node_modules/@humwick/kernel/http-transport.ts +0 -384
  383. package/node_modules/@humwick/kernel/identity-grammar.ts +0 -20
  384. package/node_modules/@humwick/kernel/identity.ts +0 -248
  385. package/node_modules/@humwick/kernel/index.ts +0 -28
  386. package/node_modules/@humwick/kernel/kernel-persistence.ts +0 -181
  387. package/node_modules/@humwick/kernel/landing-incident.ts +0 -306
  388. package/node_modules/@humwick/kernel/landing.ts +0 -953
  389. package/node_modules/@humwick/kernel/manifest-validation.ts +0 -460
  390. package/node_modules/@humwick/kernel/mcp-transport.ts +0 -109
  391. package/node_modules/@humwick/kernel/message-authority-chaos-worker.ts +0 -200
  392. package/node_modules/@humwick/kernel/message-authority.ts +0 -281
  393. package/node_modules/@humwick/kernel/module-host.ts +0 -722
  394. package/node_modules/@humwick/kernel/openapi-init.ts +0 -14
  395. package/node_modules/@humwick/kernel/package.json +0 -47
  396. package/node_modules/@humwick/kernel/persistence-schema.ts +0 -248
  397. package/node_modules/@humwick/kernel/persistence.ts +0 -510
  398. package/node_modules/@humwick/kernel/registry.ts +0 -60
  399. package/node_modules/@humwick/kernel/sqlite-kernel-persistence.ts +0 -499
  400. package/node_modules/@humwick/kernel/sqlite-module-host.ts +0 -87
  401. package/node_modules/@humwick/kernel/timers-chaos-worker.ts +0 -186
  402. package/node_modules/@humwick/kernel/timers.pilot.ts +0 -1411
  403. package/node_modules/@humwick/kernel/workflow-activation.ts +0 -25
  404. package/node_modules/@humwick/kernel/workflow-backstop.ts +0 -40
  405. package/node_modules/@humwick/kernel/workflow-cancel-tree.ts +0 -20
  406. package/node_modules/@humwick/kernel/workflow-chaos-worker.ts +0 -213
  407. package/node_modules/@humwick/kernel/workflow-composition.ts +0 -231
  408. package/node_modules/@humwick/kernel/workflow-container.ts +0 -72
  409. package/node_modules/@humwick/kernel/workflow-control.test-fixture.ts +0 -42
  410. package/node_modules/@humwick/kernel/workflow-control.ts +0 -178
  411. package/node_modules/@humwick/kernel/workflow-definition.ts +0 -507
  412. package/node_modules/@humwick/kernel/workflow-dispatch.ts +0 -60
  413. package/node_modules/@humwick/kernel/workflow-foreach.ts +0 -167
  414. package/node_modules/@humwick/kernel/workflow-journal-codec.ts +0 -339
  415. package/node_modules/@humwick/kernel/workflow-journal.ts +0 -922
  416. package/node_modules/@humwick/kernel/workflow-lane.ts +0 -48
  417. package/node_modules/@humwick/kernel/workflow-lease-owner.ts +0 -60
  418. package/node_modules/@humwick/kernel/workflow-recovery.test-fixture.ts +0 -19
  419. package/node_modules/@humwick/kernel/workflow-runtime.ts +0 -938
  420. package/node_modules/@humwick/kernel/workflow-script.ts +0 -11
  421. package/node_modules/@humwick/kernel/workflow-state.ts +0 -299
  422. package/node_modules/@humwick/kernel/workflow-step-commit.ts +0 -195
  423. package/node_modules/@humwick/kernel/workflow-transitions.ts +0 -317
  424. package/node_modules/@humwick/kernel/workflow-values.ts +0 -153
  425. package/node_modules/@humwick/kernel/workflow.pilot.ts +0 -376
  426. package/node_modules/@humwick/offload/LICENSE +0 -167
  427. package/node_modules/@humwick/offload/README.md +0 -57
  428. package/node_modules/@humwick/offload/dist/async-runner.d.ts +0 -18
  429. package/node_modules/@humwick/offload/dist/async-runner.d.ts.map +0 -1
  430. package/node_modules/@humwick/offload/dist/async-runner.js +0 -158
  431. package/node_modules/@humwick/offload/dist/async-runner.js.map +0 -1
  432. package/node_modules/@humwick/offload/dist/envelope.d.ts +0 -25
  433. package/node_modules/@humwick/offload/dist/envelope.d.ts.map +0 -1
  434. package/node_modules/@humwick/offload/dist/envelope.js +0 -80
  435. package/node_modules/@humwick/offload/dist/envelope.js.map +0 -1
  436. package/node_modules/@humwick/offload/dist/fixtures/worker-task.d.ts +0 -10
  437. package/node_modules/@humwick/offload/dist/fixtures/worker-task.d.ts.map +0 -1
  438. package/node_modules/@humwick/offload/dist/fixtures/worker-task.js +0 -59
  439. package/node_modules/@humwick/offload/dist/fixtures/worker-task.js.map +0 -1
  440. package/node_modules/@humwick/offload/dist/index.d.ts +0 -5
  441. package/node_modules/@humwick/offload/dist/index.d.ts.map +0 -1
  442. package/node_modules/@humwick/offload/dist/index.js +0 -5
  443. package/node_modules/@humwick/offload/dist/index.js.map +0 -1
  444. package/node_modules/@humwick/offload/dist/subprocess-runner.d.ts +0 -62
  445. package/node_modules/@humwick/offload/dist/subprocess-runner.d.ts.map +0 -1
  446. package/node_modules/@humwick/offload/dist/subprocess-runner.js +0 -295
  447. package/node_modules/@humwick/offload/dist/subprocess-runner.js.map +0 -1
  448. package/node_modules/@humwick/offload/dist/types.d.ts +0 -111
  449. package/node_modules/@humwick/offload/dist/types.d.ts.map +0 -1
  450. package/node_modules/@humwick/offload/dist/types.js +0 -176
  451. package/node_modules/@humwick/offload/dist/types.js.map +0 -1
  452. package/node_modules/@humwick/offload/dist/worker-pool.d.ts +0 -23
  453. package/node_modules/@humwick/offload/dist/worker-pool.d.ts.map +0 -1
  454. package/node_modules/@humwick/offload/dist/worker-pool.js +0 -435
  455. package/node_modules/@humwick/offload/dist/worker-pool.js.map +0 -1
  456. package/node_modules/@humwick/offload/dist/worker-runtime.d.ts +0 -2
  457. package/node_modules/@humwick/offload/dist/worker-runtime.d.ts.map +0 -1
  458. package/node_modules/@humwick/offload/dist/worker-runtime.js +0 -27
  459. package/node_modules/@humwick/offload/dist/worker-runtime.js.map +0 -1
  460. package/node_modules/@humwick/offload/dist/worker.d.ts +0 -5
  461. package/node_modules/@humwick/offload/dist/worker.d.ts.map +0 -1
  462. package/node_modules/@humwick/offload/dist/worker.js +0 -1
  463. package/node_modules/@humwick/offload/dist/worker.js.map +0 -1
  464. package/node_modules/@humwick/offload/package.json +0 -46
  465. package/node_modules/@humwick/offload/src/async-runner.ts +0 -170
  466. package/node_modules/@humwick/offload/src/envelope.ts +0 -92
  467. package/node_modules/@humwick/offload/src/fixtures/worker-task.ts +0 -49
  468. package/node_modules/@humwick/offload/src/index.ts +0 -4
  469. package/node_modules/@humwick/offload/src/subprocess-runner.ts +0 -334
  470. package/node_modules/@humwick/offload/src/types.ts +0 -235
  471. package/node_modules/@humwick/offload/src/worker-pool.ts +0 -463
  472. package/node_modules/@humwick/offload/src/worker-runtime.ts +0 -32
  473. package/node_modules/@humwick/offload/src/worker.ts +0 -5
  474. package/public/assets/agent-liveness-DyqxfB-t.js +0 -2
  475. package/public/assets/agent-profiles-BpI_jTxq.js +0 -2
  476. package/public/assets/fleet-BL4UXS_k.js +0 -2
  477. package/public/assets/index-B7ZerHLY.css +0 -2
  478. package/public/assets/index-BK0WV6hF.js +0 -15
  479. package/public/assets/projects-B_lmcqfO.js +0 -2
  480. package/public/assets/provider-display-DbLWpP3j.js +0 -2
  481. package/public/assets/run-card-CO_bguQf.js +0 -2
  482. package/public/assets/use-project-list-B-T2Bnnn.js +0 -2
  483. package/public/assets/versioned-prompt-editor-Diu8BIaM.js +0 -8
  484. package/runner/src/gate-run-lease-probe.ts +0 -38
  485. package/src/db/claude-catalog-reconcile.ts +0 -27
  486. package/src/routing-model-aliases.ts +0 -21
@@ -16,7 +16,7 @@ import { resolveWhoamiRole } from "../mcp/identity-projection";
16
16
  import { listManagedOrchestratorsForAgent } from "../orchestrator-lookup";
17
17
  import { applyDefaultSharedMcp } from "../shared-mcp";
18
18
  import { buildSpawnCommand, generateSpawnRequestId, resolveSpawnModelParams, resolveSpawnModelParamsAsync } from "../spawn-command";
19
- import { SUCCESSOR_LAUNCH, runnerRuntimeTokenEnv } from "../runtime-tokens";
19
+ import { SUCCESSOR_LAUNCH, runnerRuntimeTokenEnv, runnerRuntimeTokenEnvAsync, type RunnerRuntimeTokenInput } from "../runtime-tokens";
20
20
  import { restartEffortState } from "./self-effort";
21
21
  // #2033 — from isolated-workspace-placement.ts, NOT ./spawn-identity: that module also exports
22
22
  // buildProspectiveSpawnIdentity, which imports mcp/identity.ts → self-resume.ts → restart-spawn.ts,
@@ -27,7 +27,7 @@ import { restartInstructionParamsForAgent, restartInstructionParamsForAgentAsync
27
27
  import { launchedProjectRole, projectRoleRestartError, refuseSyncProjectRoleRestart, restartProjectRole } from "./restart-project-role";
28
28
  import { authContextFromSystem } from "./auth-context";
29
29
  import { markManagedAgentRunning } from "./managed-running";
30
- import type { AgentCard, AgentProfile, Command, Orchestrator, SpawnApprovalMode } from "../types";
30
+ import type { AgentCard, AgentProfile, SpawnPolicy, Command, Orchestrator, SpawnApprovalMode } from "../types";
31
31
 
32
32
  export type SelfRestartReason = "upgrade" | "token-remint" | "manual" | "reset-unconfirmed";
33
33
  type RestartOptions = { resumeId?: string; continuation?: string; generation?: number; reason?: string };
@@ -116,30 +116,9 @@ function buildRestartSpawnParamsForAgent(
116
116
  requestedBy,
117
117
  // Override the policy builder's normal mint so stable team/project bindings are also
118
118
  // carried by the fresh token, not merely preserved incidentally on the agent row.
119
- env: runnerRuntimeTokenEnv({ authority: SUCCESSOR_LAUNCH,
120
- agentId: agent.id,
121
- orchestratorId: policy.value.orchestratorId,
122
- cwd: policy.value.cwd,
123
- provider: policy.value.provider,
124
- label: policy.value.label,
125
- policyName: policy.value.name,
126
- spawnRequestId: requestId,
127
- createdBy: requestedBy,
128
- profile: policy.value.profile || undefined,
129
- // #2245 — the successor's posture comes from the POLICY (server config, unforgeable), the
130
- // same value `buildManagedSpawnParams` launches it with; stamp it into the signed constraints.
131
- approvalMode: policy.value.permissionMode,
132
- ...(agent.teamId ? { teamId: agent.teamId } : {}),
133
- ...(projectId ? { projectId } : {}),
134
- ...([...stewardExtraCwdPrefixes, ...isolatedExtraCwdPrefixes].length
135
- ? { extraCwdPrefixes: [...stewardExtraCwdPrefixes, ...isolatedExtraCwdPrefixes] }
136
- : {}),
137
- }),
138
- ...(opts.continuation ? { prompt: opts.continuation } : {}),
139
- ...(restartResumeWorkspace ? { resumeWorkspace: restartResumeWorkspace } : {}),
140
- // F5 — carry the self-restart generation marker on BOTH successor spawns (policy and
141
- // dashboard/non-policy), so the audit lineage is symmetric regardless of branch.
142
- ...(opts.generation ? { extra: { selfRestart: { generation: opts.generation, reason: opts.reason } } } : {}),
119
+ env: runnerRuntimeTokenEnv(managedRestartTokenInput(agent, policy.value, requestId, requestedBy, projectId,
120
+ [...stewardExtraCwdPrefixes, ...isolatedExtraCwdPrefixes])),
121
+ ...managedRestartOptions(opts, restartResumeWorkspace),
143
122
  };
144
123
  return opts.resumeId ? withResumeParams(params, opts.resumeId, agent.id) : params;
145
124
  }
@@ -160,7 +139,7 @@ function buildRestartSpawnParamsForAgent(
160
139
  const resolvedProfile = withResolvedProvisioning(profileName ? getAgentProfile(profileName)?.value : undefined, provider);
161
140
  const agentProfile = resolvedProfile ? applyDefaultSharedMcp(resolvedProfile) : undefined;
162
141
  const workspaceMode = stringValue(agent.meta?.workspaceMode) ?? "inherit";
163
- const resolvedModel = resolveSpawnModelParams(provider, model, effort, { modelSource: "runtime", skipDefaultWhenEmpty: true });
142
+ const resolvedModel = resolveSpawnModelParams(provider, model, effort, { modelSource: "runtime" });
164
143
  const projectedInstructions = restartInstructionParamsForAgent(agent, { spawnRequestId: predecessorSpawnRequestId, cwd }) ?? {};
165
144
  // #1598 — canonicalize so a worktree/subdir cwd binds to its owning checkout's project.
166
145
  const projectId = resolveProjectForCanonicalCwd(cwd).project?.id;
@@ -175,7 +154,7 @@ function buildRestartSpawnParamsForAgent(
175
154
  cwd,
176
155
  orchestrator.baseDir,
177
156
  );
178
- return buildRestartSpawnCommand(agent, predecessorSpawnRequestId, agentProfile, projectedInstructions, opts, {
157
+ const options = {
179
158
  provider,
180
159
  modelParams: resolvedModel,
181
160
  cwd,
@@ -185,7 +164,10 @@ function buildRestartSpawnParamsForAgent(
185
164
  policyName: policyName || undefined,
186
165
  spawnRequestId: requestId,
187
166
  requestedBy,
188
- }, { orchestratorId: orchestrator.id, projectId, extraCwdPrefixes: stewardExtraCwdPrefixes });
167
+ };
168
+ const tokenContext = { orchestratorId: orchestrator.id, projectId, extraCwdPrefixes: stewardExtraCwdPrefixes };
169
+ return buildRestartSpawnCommand(agent, predecessorSpawnRequestId, agentProfile, projectedInstructions, opts, options, tokenContext,
170
+ runnerRuntimeTokenEnv(restartTokenInput(agent, options, tokenContext)));
189
171
  }
190
172
 
191
173
  /** Worker-safe restart command construction used by HTTP/orchestrator/self-restart flows. */
@@ -233,14 +215,8 @@ async function buildRestartSpawnParamsForAgentAsync(
233
215
  const params = {
234
216
  ...await buildManagedSpawnParamsAsync(relaunchPolicy(policy.value, relaunch.effort), requestId, { createdBy: requestedBy, authority: SUCCESSOR_LAUNCH, identityAgentId: agent.id, gitRepoRoot }),
235
217
  ...relaunch.carried, agentId: agent.id, requestedBy,
236
- env: runnerRuntimeTokenEnv({ authority: SUCCESSOR_LAUNCH, agentId: agent.id, orchestratorId: policy.value.orchestratorId, cwd: policy.value.cwd,
237
- provider: policy.value.provider, label: policy.value.label, policyName: policy.value.name, spawnRequestId: requestId,
238
- createdBy: requestedBy, profile: policy.value.profile || undefined, ...(agent.teamId ? { teamId: agent.teamId } : {}),
239
- approvalMode: policy.value.permissionMode,
240
- ...(projectId ? { projectId } : {}), ...(combinedExtraCwdPrefixes.length ? { extraCwdPrefixes: combinedExtraCwdPrefixes } : {}), }),
241
- ...(opts.continuation ? { prompt: opts.continuation } : {}),
242
- ...(restartResumeWorkspace ? { resumeWorkspace: restartResumeWorkspace } : {}),
243
- ...(opts.generation ? { extra: { selfRestart: { generation: opts.generation, reason: opts.reason } } } : {}),
218
+ env: await runnerRuntimeTokenEnvAsync(managedRestartTokenInput(agent, policy.value, requestId, requestedBy, projectId, combinedExtraCwdPrefixes)),
219
+ ...managedRestartOptions(opts, restartResumeWorkspace),
244
220
  };
245
221
  return opts.resumeId ? withResumeParams(params, opts.resumeId, agent.id) : params;
246
222
  }
@@ -257,15 +233,55 @@ async function buildRestartSpawnParamsForAgentAsync(
257
233
  const profileName = role ? role.profileName : stringValue(agent.meta?.profile);
258
234
  const resolvedProfile = role ? undefined : await withResolvedProvisioningAsync(profileName ? getAgentProfile(profileName)?.value : undefined, provider);
259
235
  const agentProfile = role?.agentProfile ?? (resolvedProfile ? applyDefaultSharedMcp(resolvedProfile) : undefined);
260
- const resolvedModel = await resolveSpawnModelParamsAsync(provider, model, effort, { modelSource: "runtime", skipDefaultWhenEmpty: true });
236
+ const resolvedModel = await resolveSpawnModelParamsAsync(provider, model, effort, { modelSource: "runtime" });
261
237
  const projectedInstructions = await restartInstructionParamsForAgentAsync(agent, { spawnRequestId: predecessorSpawnRequestId, cwd, ...(role ? { profile: role.agentProfile } : {}) }) ?? {};
262
238
  const projectId = resolveProjectForCanonicalCwd(cwd).project?.id;
263
239
  const stewardExtraCwdPrefixes = stewardWorktreeCwdPrefixes(isProvenStewardAgent(agent.id, policyName), cwd, orchestrator.baseDir);
264
- return buildRestartSpawnCommand(agent, predecessorSpawnRequestId, agentProfile, projectedInstructions, opts, {
240
+ const options = {
265
241
  provider, modelParams: resolvedModel, cwd, workspaceMode, profile: profileName || undefined,
266
242
  ...relaunch.carried, ...role?.spawn, policyName: policyName || undefined, spawnRequestId: requestId,
267
243
  requestedBy,
268
- }, { orchestratorId: orchestrator.id, projectId, extraCwdPrefixes: stewardExtraCwdPrefixes });
244
+ };
245
+ const tokenContext = { orchestratorId: orchestrator.id, projectId, extraCwdPrefixes: stewardExtraCwdPrefixes };
246
+ return buildRestartSpawnCommand(agent, predecessorSpawnRequestId, agentProfile, projectedInstructions, opts, options, tokenContext,
247
+ await runnerRuntimeTokenEnvAsync(restartTokenInput(agent, options, tokenContext)));
248
+ }
249
+
250
+ function managedRestartOptions(opts: RestartOptions, restartResumeWorkspace: ReturnType<typeof resumeWorkspaceForRestart>) {
251
+ return {
252
+ ...(opts.continuation ? { prompt: opts.continuation } : {}),
253
+ ...(restartResumeWorkspace ? { resumeWorkspace: restartResumeWorkspace } : {}),
254
+ // F5 — carry the self-restart generation marker on BOTH successor spawns (policy and
255
+ // dashboard/non-policy), so the audit lineage is symmetric regardless of branch.
256
+ ...(opts.generation ? { extra: { selfRestart: { generation: opts.generation, reason: opts.reason } } } : {}),
257
+ };
258
+ }
259
+
260
+ function managedRestartTokenInput(agent: AgentCard, policy: SpawnPolicy, requestId: string, requestedBy: string, projectId: string | undefined, extraCwdPrefixes: string[]): RunnerRuntimeTokenInput {
261
+ return {
262
+ authority: SUCCESSOR_LAUNCH, agentId: agent.id, orchestratorId: policy.orchestratorId,
263
+ cwd: policy.cwd, provider: policy.provider, label: policy.label, policyName: policy.name,
264
+ spawnRequestId: requestId, createdBy: requestedBy, profile: policy.profile || undefined,
265
+ // #2245: both successor paths stamp the policy's current posture into signed constraints.
266
+ approvalMode: policy.permissionMode,
267
+ ...(agent.teamId ? { teamId: agent.teamId } : {}),
268
+ ...(projectId ? { projectId } : {}),
269
+ ...(extraCwdPrefixes.length ? { extraCwdPrefixes } : {}),
270
+ };
271
+ }
272
+
273
+ type RestartTokenContext = Pick<RunnerRuntimeTokenInput, "orchestratorId" | "projectId" | "extraCwdPrefixes">;
274
+ const restartApprovalMode = (agent: AgentCard): SpawnApprovalMode => (stringValue(agent.meta?.approvalMode) as SpawnApprovalMode | undefined) ?? "guarded";
275
+ const restartLabel = (agent: AgentCard): string | undefined => (stringValue(agent.meta?.label) ?? agent.label) || undefined;
276
+
277
+ /** The successor token a non-policy restart carries; the sync twin and the writer-op path mint the same input. */
278
+ function restartTokenInput(agent: AgentCard, options: Omit<Parameters<typeof buildSpawnCommand>[0], "env">, tokenContext: RestartTokenContext): RunnerRuntimeTokenInput {
279
+ return { ...tokenContext, authority: SUCCESSOR_LAUNCH,
280
+ agentId: agent.id, cwd: options.cwd, provider: options.provider, label: restartLabel(agent),
281
+ policyName: options.policyName, spawnRequestId: options.spawnRequestId,
282
+ createdBy: options.requestedBy, profile: options.profile, approvalMode: restartApprovalMode(agent),
283
+ ...(agent.teamId ? { teamId: agent.teamId } : {}),
284
+ };
269
285
  }
270
286
 
271
287
  function buildRestartSpawnCommand(
@@ -275,25 +291,21 @@ function buildRestartSpawnCommand(
275
291
  projectedInstructions: Record<string, unknown>,
276
292
  opts: RestartOptions,
277
293
  options: Omit<Parameters<typeof buildSpawnCommand>[0], "env">,
278
- tokenContext: Pick<Parameters<typeof runnerRuntimeTokenEnv>[0], "orchestratorId" | "projectId" | "extraCwdPrefixes">,
294
+ tokenContext: RestartTokenContext,
295
+ env: Record<string, string>,
279
296
  ): Record<string, unknown> {
280
297
  const memorySeam = parseMemorySeam(projectedInstructions.memorySeam);
281
298
  const resumeWorkspace = resumeWorkspaceForRestart(agent);
282
299
  const providerArgs = metaStringArray(agent.meta, "providerArgs");
283
- const approvalMode = (stringValue(agent.meta?.approvalMode) as SpawnApprovalMode | undefined) ?? "guarded";
284
- const label = (stringValue(agent.meta?.label) ?? agent.label) || undefined;
300
+ const approvalMode = restartApprovalMode(agent);
301
+ const label = restartLabel(agent);
285
302
  const params = buildSpawnCommand({
286
303
  ...options,
287
304
  orchestratorId: tokenContext.orchestratorId,
288
305
  headless: true, requestedAt: Date.now(),
289
306
  ...(opts.continuation ? { prompt: opts.continuation } : {}),
290
307
  ...(opts.generation ? { extra: { selfRestart: { generation: opts.generation, reason: opts.reason } } } : {}),
291
- env: runnerRuntimeTokenEnv({ ...tokenContext, authority: SUCCESSOR_LAUNCH,
292
- agentId: agent.id, cwd: options.cwd, provider: options.provider, label,
293
- policyName: options.policyName, spawnRequestId: options.spawnRequestId,
294
- createdBy: options.requestedBy, profile: options.profile, approvalMode,
295
- ...(agent.teamId ? { teamId: agent.teamId } : {}),
296
- }),
308
+ env,
297
309
  agentId: agent.id, tags: agent.tags, systemTags: agent.systemTags, capabilities: agent.capabilities,
298
310
  label,
299
311
  approvalMode, permissionMode: approvalMode,
@@ -2,9 +2,10 @@ import { renderLiveRunsSection } from "../live-runs-section";
2
2
  import { renderOpenDecisionsSection } from "../operator-decisions-section";
3
3
  import { renderIdleEpicsSection } from "./idle-epics";
4
4
  import { renderPlanWakeDigest } from "./plan-wake-digest";
5
+ import { renderPausedRunsSection } from "./workflow-pause-text";
5
6
 
6
7
  /** Stored live state survives every self-care boundary; tracker refresh must never block a wake. */
7
8
  export function renderSelfCareState(agentId: string): string {
8
9
  return [renderLiveRunsSection(agentId), renderOpenDecisionsSection(agentId),
9
- renderIdleEpicsSection(agentId, [], false), renderPlanWakeDigest(agentId)].filter(Boolean).join("\n\n");
10
+ renderIdleEpicsSection(agentId, [], false), renderPlanWakeDigest(agentId), renderPausedRunsSection(agentId)].filter(Boolean).join("\n\n");
10
11
  }
@@ -1,5 +1,5 @@
1
1
  import { stringValue, type SpawnProvider } from "@humwick/sdk";
2
- import { VALID_EFFORTS, findProviderCatalog, findProviderCatalogModel, type ProviderCatalogEntry } from "@humwick/sdk/provider-catalog";
2
+ import { findProviderCatalog, findProviderCatalogModel, type ProviderCatalogEntry } from "@humwick/sdk/provider-catalog";
3
3
  import { findConfirmedEffortSwitch, findLaunchEffort, type LaunchEffort } from "../db/commands-effort-read";
4
4
  import { getAgentEffortState } from "../db/agent-effort-state";
5
5
  import { SELF_EFFORT_BAND, getSelfEffortCeiling, type SelfEffortIdentity } from "../db/self-effort-permissions";
@@ -44,7 +44,7 @@ export function resolveSpawnSelfEffort(
44
44
  if (!(base ?? known?.defaultEffort)) throw new ValidationError("selfEffort without selfEffortCeiling needs a base effort: set effort, or pass selfEffortCeiling");
45
45
  return { band: true };
46
46
  }
47
- const levels = known?.efforts ?? VALID_EFFORTS;
47
+ const levels = known?.efforts ?? [];
48
48
  if (!levels.includes(ceiling)) {
49
49
  throw new ValidationError(`selfEffortCeiling ${ceiling} is not an effort ${known ? `${known.provider} model ${known.alias}` : provider} offers (${levels.join(", ")})`);
50
50
  }
@@ -90,7 +90,7 @@ export function selfEffortBase(agent: AgentCard): string | undefined {
90
90
 
91
91
  /** One step down and one step up from `base` on `ladder`, clamped at both ends. */
92
92
  export function selfEffortBand(base: string, ladder: readonly string[]): { floor: string; ceiling: string } {
93
- const levels = ladder.includes(base) ? ladder : VALID_EFFORTS;
93
+ const levels = ladder.includes(base) ? ladder : [base];
94
94
  const at = levels.indexOf(base);
95
95
  return { floor: levels[Math.max(0, at - 1)]!, ceiling: levels[Math.min(levels.length - 1, at + 1)]! };
96
96
  }
@@ -119,7 +119,7 @@ export function selfEffortPolicyFor(agent: AgentCard, grant: SelfEffortGrant, mo
119
119
  if ("ceiling" in grant) return { ceiling: grant.ceiling };
120
120
  const base = selfEffortBase(agent);
121
121
  if (!base) throw new ValidationError("your self-change band has no base effort; ask your coordinator to set your effort");
122
- return { band: true, base, ...selfEffortBand(base, modelEfforts ?? agentCatalogModel(agent)?.efforts ?? VALID_EFFORTS) };
122
+ return { band: true, base, ...selfEffortBand(base, modelEfforts ?? agentCatalogModel(agent)?.efforts ?? [base]) };
123
123
  }
124
124
 
125
125
  /** #2950 H3 — a restart successor launches at the agent's desired effort (#2984: applied or still pending),
@@ -138,7 +138,7 @@ export function restartEffortState(agent: AgentCard): { effort?: string; carried
138
138
  export function assertSelfEffortAllowed(agent: AgentCard, level: string, modelEfforts: readonly string[] | undefined): SelfEffortPolicy {
139
139
  const policy = spawnSelfEffortPolicy(agent, modelEfforts);
140
140
  if (!policy) throw new ValidationError("selfEffort is not enabled for you, so you cannot change your own effort; ask your coordinator");
141
- const ladder = modelEfforts?.includes(policy.ceiling) ? modelEfforts : VALID_EFFORTS;
141
+ const ladder = modelEfforts ?? agentCatalogModel(agent)?.efforts ?? [];
142
142
  const at = ladder.indexOf(level);
143
143
  if (at < 0 || at > ladder.indexOf(policy.ceiling)) {
144
144
  throw new ValidationError(policy.band
@@ -45,6 +45,7 @@ import {
45
45
  import { recentFinalResponseCaptureIds } from "../db/lineage-startup-captures";
46
46
  import { yieldToEventLoop } from "../event-loop-yield";
47
47
  import { markAgentWorkFinal } from "./work-finality";
48
+ import { agentProviderState } from "../provider-state";
48
49
  import { planSend, type DeliveryReceipt } from "../agent-ref";
49
50
  import { receiptWithChannelSuppression } from "./channel-suppression-receipt";
50
51
  import { emitMessageDeliveryUpdated, emitMessageQueued, emitNewMessages } from "../sse";
@@ -203,7 +204,8 @@ function resolveSendTarget(input: SendMessageInput, from: string): DeliveryRecei
203
204
  }
204
205
  // excludeId: a bare ref must never resolve back to its own author — that self-loop silently
205
206
  // swallows the message (the reddit-briefing → telegram bridge break, #290).
206
- const plan = planSend(input.to, listAgents(), { excludeId: from });
207
+ const agents = listAgents();
208
+ const plan = planSend(input.to, agents, { excludeId: from });
207
209
  // Ambiguous is a real "can't decide" → reject on every transport (categorically unlike
208
210
  // not_found, which stores). HTTP → 409, MCP → invalid-params.
209
211
  if (plan.kind === "ambiguous") throw new AmbiguousTargetError(plan.message);
@@ -215,6 +217,8 @@ function resolveSendTarget(input: SendMessageInput, from: string): DeliveryRecei
215
217
  // direct (incl. a single OFFLINE match — receipt already carries reason "recipient offline")
216
218
  // / fanout / passthrough: adopt the canonical (possibly rewritten) id + the receipt.
217
219
  input.to = plan.to;
220
+ const providerState = agentProviderState(agents.find(agent => agent.id === plan.to));
221
+ if (providerState?.reason === "providerSetupRequired") throw new ValidationError(providerState.message ?? providerState.recommendedAction ?? "Provider setup required; restart the runner after completing setup.");
218
222
  return plan.receipt;
219
223
  }
220
224
 
@@ -44,7 +44,7 @@ import { emitCommandEvent } from "../command-events";
44
44
  import { redactCommandSecrets } from "../command-secrets";
45
45
  import { buildSpawnCommand, generateSpawnRequestId } from "../spawn-command"; import { systemTagsForSpawn } from "../agent-system-tags";
46
46
  import { resolveSpawnLandBlocked } from "../land-block";
47
- import { runnerRuntimeTokenEnv } from "../runtime-tokens";
47
+ import { runnerRuntimeTokenEnvAsync } from "../runtime-tokens";
48
48
  import { liveSpawnQuota } from "../spawn-quota";
49
49
  import { resolveCanonicalHost } from "../host-identity";
50
50
  import { resolveSpawnHostAnchor } from "../spawn-targets";
@@ -59,6 +59,7 @@ import { isolatedSpawnGitRepoError, resolveSpawnRepoRoot } from "./spawn-repo-pr
59
59
  import { autoSeedProjectForCwdAsync } from "./project"; import { applyProjectCapabilityGates } from "../project-trust";
60
60
  import { assertProjectRoleMcpCwd, assertProjectRoleMcpTrusted, projectRoleProfile, projectRoleProvenance, projectRoleSpawnDefaults, withRoleProfile } from "../project-roles";
61
61
  import { spawnWarnings } from "./spawn-warnings";
62
+ import { enforceSpawnPolicy, spawnerKind, spawnPolicyTask, type AppliedSpawnPolicy } from "./spawn-policy";
62
63
  import { resolveSpawnProjectRole } from "./spawn-project-role";
63
64
  import { profileProviderPin, resolveSpawnLaunch } from "./spawn-launch";
64
65
  import { hydrateTaskContext, projectTaskHydration } from "./task-context-hydration";
@@ -210,6 +211,8 @@ export interface SpawnAgentInput extends SelfEffortSpawnInput {
210
211
  roleSnapshot?: import("../project-roles").ProjectRole;
211
212
  workflowCaller?: WorkflowSpawnQuota;
212
213
  roleSha?: string; workflowRunId?: string; roleProfile?: string; taskRepoCwd?: string; roleHost?: string;
214
+ /** #3736 — the workflows of the run tree spawning this step, for a spawn-policy `require.workflow`. Service-only. */
215
+ workflowNames?: string[];
213
216
  prompt?: string;
214
217
  systemPromptAppend?: string;
215
218
  /**
@@ -324,6 +327,8 @@ export interface SpawnAgentResult {
324
327
  dispatchDecision?: Awaited<ReturnType<typeof dispatchTaskAsync>>;
325
328
  /** #3043 — the spawn went ahead, but the caller should know (a role read from a stale default branch). */
326
329
  warnings?: string[];
330
+ /** #3736 — the project spawn-policy rules that applied to this spawn. */
331
+ spawnPolicy?: AppliedSpawnPolicy;
327
332
  }
328
333
 
329
334
  /** Reject when a component token's signed resource constraints disallow the target (#323). */
@@ -428,6 +433,11 @@ export async function spawnAgent(input: SpawnAgentInput, ctx: AuthContext): Prom
428
433
  });
429
434
 
430
435
  assertOrchestratorSupportsLaunchMode(orchestrator, launchMode, input.providerArgs);
436
+ // #3736 — the project's spawn policy, before any side effect: a refusal names the rule, the allowed set and the hint.
437
+ const spawnPolicy = await enforceSpawnPolicy(
438
+ { projectId: lazyRoot?.projectId ?? resolveProjectForCwd(resolvedCwd)?.id, ...(lazyRoot ? {} : { checkout: { cwd: resolvedCwd, orchestratorId: orchestrator.id } }) },
439
+ { spawner: spawnerKind(ctx, input, caller), task: spawnPolicyTask(linkedTask), subject: { role: projectRole?.name, profile: profileName, workflows: input.workflowNames } },
440
+ );
431
441
 
432
442
  // Resolve + validate the named agent profile server-side (was HTTP-only; the MCP path passed
433
443
  // the name through unvalidated). Omitted profile uses Relay's spawn default so coordinator
@@ -703,7 +713,7 @@ export async function spawnAgent(input: SpawnAgentInput, ctx: AuthContext): Prom
703
713
  // Child runner token. `agentInitiated` (set whenever an agent is the caller) forces a
704
714
  // non-spawn-capable child (no grandchildren); `spawnedBy` stamps authoritative lineage the
705
715
  // child can't forge (it registers with spawnedBy = caller, read from the signed token).
706
- const env = { ...callerEnv, ...runnerRuntimeTokenEnv({
716
+ const env = { ...callerEnv, ...await runnerRuntimeTokenEnvAsync({
707
717
  agentId: targetAgentId,
708
718
  orchestratorId: orchestrator.id,
709
719
  cwd: resolvedCwd,
@@ -900,6 +910,7 @@ export async function spawnAgent(input: SpawnAgentInput, ctx: AuthContext): Prom
900
910
  ...(autoScaffoldedTask ? { task: autoScaffoldedTask } : {}),
901
911
  ...(taskDispatchDecision ? { dispatchDecision: taskDispatchDecision } : {}),
902
912
  ...(warnings.length ? { warnings } : {}),
913
+ ...(spawnPolicy ? { spawnPolicy } : {}),
903
914
  };
904
915
  }
905
916
 
@@ -24,7 +24,7 @@ import { join } from "node:path";
24
24
  import type { LaunchMode, SpawnProvider } from "@humwick/sdk";
25
25
  import { withResolvedProvisioning } from "../db";
26
26
  import { getAgentProfile } from "../config-store";
27
- import { buildSpawnCommand } from "../spawn-command";
27
+ import { buildSpawnCommand, resolveSpawnModelParams } from "../spawn-command";
28
28
  import { spawnOptionsFromControl } from "../../orchestrator/src/control";
29
29
  import type { SpawnOptions } from "../../orchestrator/src/spawn/types";
30
30
  import { buildEnv, buildRunnerCommand } from "../../orchestrator/src/spawn/command";
@@ -139,18 +139,10 @@ export function prepareSpawnChain(opts: SpawnChainRequest, observe?: SpawnChainP
139
139
  prompt: opts.spawn?.prompt,
140
140
  systemPromptAppend: opts.spawn?.systemPromptAppend,
141
141
  headless: opts.spawn?.headless ?? true,
142
- ...(opts.spawn?.model || opts.spawn?.effort || opts.spawn?.contextWindowTokens !== undefined
143
- ? {
144
- modelParams: {
145
- ...(opts.spawn.model ? { model: opts.spawn.model, providerModel: opts.spawn.model } : {}),
146
- ...(opts.spawn.effort ? { effort: opts.spawn.effort } : {}),
147
- // #2595 — window passes through the same payload field production threads.
148
- ...(typeof opts.spawn.contextWindowTokens === "number" && Number.isSafeInteger(opts.spawn.contextWindowTokens) && opts.spawn.contextWindowTokens > 0
149
- ? { contextWindowTokens: opts.spawn.contextWindowTokens }
150
- : {}),
151
- },
152
- }
153
- : {}),
142
+ modelParams: {
143
+ ...resolveSpawnModelParams(provider, opts.spawn?.model, opts.spawn?.effort),
144
+ ...(opts.spawn?.contextWindowTokens !== undefined ? { contextWindowTokens: opts.spawn.contextWindowTokens } : {}),
145
+ },
154
146
  } as Parameters<typeof buildSpawnCommand>[0]);
155
147
 
156
148
  return prepareCommandChain(cmdParams, opts, observe);
@@ -216,6 +208,7 @@ export function prepareCommandChain(
216
208
  provider: cli.provider,
217
209
  ...(cli.model ? { model: cli.model } : {}),
218
210
  ...(cli.effort ? { effort: cli.effort } : {}),
211
+ ...(cli.modelRecord ? { modelRecord: cli.modelRecord } : {}),
219
212
  // #2595 — the real runner CLI carries the window as a flag; carry it through the same seam.
220
213
  ...(cli.contextWindowTokens !== undefined ? { contextWindowTokens: cli.contextWindowTokens } : {}),
221
214
  runnerId: "inspector",
@@ -0,0 +1,68 @@
1
+ // #3736 — the one spawn-policy check. spawnAgent runs it for every spawn (relay_spawn_agent,
2
+ // relay_task_dispatch, POST /api/agents/spawn, workflow steps, sub-coordinators); the workflow
3
+ // auto-start asks it which workflow a Task's work runs through. A project without the file, or with
4
+ // an invalid or unreadable one, is not restricted: stored config never fails a spawn.
5
+ import { stringValue, type AgentCard } from "@humwick/sdk";
6
+ import {
7
+ matchSpawnPolicy, SPAWN_POLICY_FILE, spawnPolicyAtCommit, spawnPolicyRefusal, spawnPolicyWorkflow,
8
+ type SpawnerKind, type SpawnPolicy, type SpawnPolicySubject, type SpawnPolicyTask,
9
+ } from "@humwick/sdk/spawn-policy";
10
+ import { ValidationError } from "../db";
11
+ import type { AuthContext } from "./auth-context";
12
+ import { readProjectConfigFirst, type ProjectCheckout } from "./project-config-read";
13
+
14
+ /** Where the policy is read: the spawn's own checkout first, then the project's registered roots. */
15
+ export interface SpawnPolicyWhere {
16
+ projectId?: string;
17
+ checkout?: ProjectCheckout;
18
+ }
19
+
20
+ /** The rules that applied, for the caller's receipt. */
21
+ export interface AppliedSpawnPolicy {
22
+ file: string;
23
+ ref: string;
24
+ sha: string;
25
+ rules: string[];
26
+ }
27
+
28
+ interface LoadedSpawnPolicy {
29
+ policy: SpawnPolicy;
30
+ ref: string;
31
+ sha: string;
32
+ }
33
+
34
+ async function loadSpawnPolicy(where: SpawnPolicyWhere): Promise<LoadedSpawnPolicy | undefined> {
35
+ if (!where.projectId) return undefined;
36
+ const { lookup } = await readProjectConfigFirst(where.projectId, where.checkout ? [where.checkout] : [], SPAWN_POLICY_FILE, spawnPolicyAtCommit, "spawns are not restricted until it is fixed");
37
+ return lookup?.status === "found" ? { policy: lookup.config, ref: lookup.ref, sha: lookup.sha } : undefined;
38
+ }
39
+
40
+ /** A workflow run's step, a sub-coordinator, any other agent, or a caller with no agent (dashboard, operator). */
41
+ export function spawnerKind(ctx: AuthContext, input: { workflowRunId?: string }, caller: AgentCard | undefined): SpawnerKind {
42
+ if (ctx.actor.kind === "system" && input.workflowRunId) return "workflow";
43
+ if (!caller) return "operator";
44
+ return stringValue(caller.meta?.profile) === "sub-coordinator" ? "sub-coordinator" : "coordinator";
45
+ }
46
+
47
+ export function spawnPolicyTask(task: { id: number; tags?: readonly string[]; paths?: readonly string[] } | undefined): SpawnPolicyTask | undefined {
48
+ return task ? { id: task.id, ...(task.tags ? { tags: task.tags } : {}), ...(task.paths ? { paths: task.paths } : {}) } : undefined;
49
+ }
50
+
51
+ /** Refuses a spawn the project's policy does not allow (a `ValidationError` naming the rule, the
52
+ * allowed set and the hint); otherwise the rules that applied, if any. */
53
+ export async function enforceSpawnPolicy(where: SpawnPolicyWhere, facts: { spawner: SpawnerKind; task?: SpawnPolicyTask; subject: SpawnPolicySubject }): Promise<AppliedSpawnPolicy | undefined> {
54
+ const loaded = await loadSpawnPolicy(where);
55
+ if (!loaded) return undefined;
56
+ const rules = matchSpawnPolicy(loaded.policy, facts);
57
+ if (!rules.length) return undefined;
58
+ const refusal = spawnPolicyRefusal(rules, facts.subject, facts.task);
59
+ if (refusal) throw new ValidationError(refusal);
60
+ return { file: SPAWN_POLICY_FILE, ref: loaded.ref, sha: loaded.sha, rules: rules.map((rule) => rule.name) };
61
+ }
62
+
63
+ /** The workflow the project's policy routes this Task's work through, and the rule that says so. */
64
+ export async function spawnPolicyWorkflowFor(where: SpawnPolicyWhere, facts: { spawner: SpawnerKind; task: SpawnPolicyTask }): Promise<{ workflow: string; rule: string } | undefined> {
65
+ const loaded = await loadSpawnPolicy(where);
66
+ const chosen = loaded && spawnPolicyWorkflow(matchSpawnPolicy(loaded.policy, facts));
67
+ return chosen ? { workflow: chosen.workflow, rule: chosen.rule.name } : undefined;
68
+ }
@@ -4,11 +4,11 @@ import { getGateway } from "../db/gateway";
4
4
  import { getAgentAsync } from "../db";
5
5
  import { shutdownAgent } from "./shutdown-agent";
6
6
  import { currentStop } from "./workflow-effects";
7
- import { invoke, testOptions, type Supervisor } from "./workflow-supervisor-state";
7
+ import { invoke, supervisorNow, type Supervisor } from "./workflow-supervisor-state";
8
8
 
9
9
  /** Every retry resumes the same outbox effect; only host witnesses release reservations. */
10
10
  export async function performSpawnTreeCleanup(sup: Supervisor, effect: WorkflowEffect): Promise<boolean> {
11
- const now = (testOptions.now ?? Date.now)();
11
+ const now = supervisorNow();
12
12
  if (typeof effect.result?.nextAttemptAt === "number" && effect.result.nextAttemptAt > now) return false;
13
13
  const agentId = String(effect.payload.agentId);
14
14
  await getGateway().write("spawnTree.prepareCleanup", { agentId });
@@ -105,6 +105,7 @@ export interface TaskDispatchSpawnInput {
105
105
  /** #3045 — the workflow run dispatching this step; #3091 the base profile its step pins, the run's checkout standing in
106
106
  * for the Task's repo root, and the host its role is read on first. Service-only. */
107
107
  workflowRunId?: string;
108
+ workflowNames?: string[];
108
109
  workflowCaller?: SpawnAgentInput["workflowCaller"];
109
110
  roleProfile?: string;
110
111
  taskRepoCwd?: string;
@@ -139,6 +140,7 @@ export async function spawnTaskDispatch(input: TaskDispatchSpawnInput, ctx: Auth
139
140
  role,
140
141
  roleSha, roleSnapshot,
141
142
  workflowRunId,
143
+ workflowNames: input.workflowNames,
142
144
  workflowCaller: input.workflowCaller,
143
145
  roleProfile,
144
146
  taskRepoCwd,
@@ -173,6 +175,7 @@ export async function spawnTaskDispatch(input: TaskDispatchSpawnInput, ctx: Auth
173
175
  ...(decision ? { decision } : {}),
174
176
  ...(reviewTaskId !== undefined ? { reviewTaskId } : {}),
175
177
  ...(spawn.warnings ? { warnings: spawn.warnings } : {}),
178
+ ...(spawn.spawnPolicy ? { spawnPolicy: spawn.spawnPolicy } : {}),
176
179
  ...(verbose ? { command: summarizeSpawnCommandForCaller(spawn.command) } : {}),
177
180
  };
178
181
  }
@@ -4,8 +4,9 @@
4
4
  // live in src/db/task-state-reconcile.ts; every change is applied by its conditional writer, which
5
5
  // re-checks the rule in the transaction, then audited here with one `task.reconciled` relay event.
6
6
  import { errMessage } from "@humwick/sdk";
7
- import { isSqliteBusyError, withFastFailConnection, writerLockAvailable } from "../db/connection";
8
- import { getIssueRef, updateIssueRef } from "../db/issue-refs";
7
+ import { writerLockAvailable } from "../db/connection";
8
+ import { getIssueRef } from "../db/issue-refs";
9
+ import { updateIssueRefAsync } from "../db/issue-refs-async";
9
10
  import { applyTaskStateReconcileAsync, listTaskStateReconcileCandidatesAsync } from "../db/task-state-reconcile-async";
10
11
  import { TASK_RECONCILE_ACTOR, type TaskReconcileRule, type TaskStateReconcileApplied } from "../db/task-state-reconcile";
11
12
  import { emitRelayEvent } from "../events";
@@ -104,19 +105,10 @@ async function refreshIssueRefs(refIds: string[], opts: TaskStateReconcileOption
104
105
  try {
105
106
  const live = await (opts.adapterResolver ?? resolveIssueTrackerAdapter)(ref).getStatus(ref);
106
107
  const status = live.status === "open" || live.status === "closed" ? live.status : ref.status;
107
- // The tracker read can take long: re-check the lock right before the write, and bound the write itself.
108
+ // The tracker read can take long: re-check the lock right before the write. The write itself waits on
109
+ // the writer thread, never on the loop (#1870 S7).
108
110
  if (steps.contended()) break;
109
- const written = await steps.timed(() => {
110
- try {
111
- withFastFailConnection(STEP_BUSY_TIMEOUT_MS, () => updateIssueRef(ref.id, { status, title: live.title, closeReason: live.closeReason, statusSyncedAt: Date.now() }));
112
- return true;
113
- } catch (error) {
114
- if (!isSqliteBusyError(error)) throw error;
115
- result.moreWork = true;
116
- return false;
117
- }
118
- });
119
- if (!written) break;
111
+ await updateIssueRefAsync(ref.id, { status, title: live.title, closeReason: live.closeReason, statusSyncedAt: Date.now() });
120
112
  closedAny ||= status === "closed";
121
113
  } catch (error) {
122
114
  refreshFailedAt.set(id, now);
@@ -1,6 +1,7 @@
1
1
  import { isRecord, SPAWN_PROVIDERS, VALID_WORKSPACE_MODES, APPROVAL_MODES, VALID_AGENT_LIFECYCLES } from "@humwick/sdk";
2
2
  import { getTeam, ValidationError } from "../db";
3
- import { deleteConfig, getConfig, listConfig, setConfig } from "../config-store";
3
+ import { getConfig, listConfig } from "../config-store";
4
+ import { deleteConfigAsync, setConfigAsync } from "../config-store-write";
4
5
  import { TEAM_TEMPLATE_NAMESPACE } from "../team-template-config";
5
6
  import { cleanString, optionalEnum } from "../validation";
6
7
  import type { AuthContext } from "./auth-context";
@@ -47,11 +48,11 @@ function withTimestamps(template: TeamTemplate, existing?: TeamTemplate): TeamTe
47
48
  };
48
49
  }
49
50
 
50
- export function saveTeamTemplate(input: Record<string, unknown>, updatedBy?: string): TeamTemplate {
51
+ export async function saveTeamTemplateAsync(input: Record<string, unknown>, updatedBy?: string): Promise<TeamTemplate> {
51
52
  if (input.teamId !== undefined && input.composition !== undefined) {
52
53
  throw new ValidationError("teamId capture and manual composition are mutually exclusive");
53
54
  }
54
- if (input.teamId !== undefined) return captureTeamTemplateFromTeam(cleanString(input.teamId, "teamId", { required: true, max: 120 })!, input, updatedBy);
55
+ if (input.teamId !== undefined) return captureTeamTemplateFromTeamAsync(cleanString(input.teamId, "teamId", { required: true, max: 120 })!, input, updatedBy);
55
56
  const slug = templateSlug(input);
56
57
  const existing = getConfig<TeamTemplate>(TEAM_TEMPLATE_NAMESPACE, slug)?.value;
57
58
  const template = withTimestamps({
@@ -64,7 +65,7 @@ export function saveTeamTemplate(input: Record<string, unknown>, updatedBy?: str
64
65
  ...(cleanString(input.definitionOfDone, "definitionOfDone", { max: 4000 }) ? { definitionOfDone: cleanString(input.definitionOfDone, "definitionOfDone", { max: 4000 }) } : {}),
65
66
  ...(input.planSkeleton !== undefined ? { planSkeleton: input.planSkeleton as TeamTemplate["planSkeleton"] } : {}),
66
67
  }, existing);
67
- return setConfig(TEAM_TEMPLATE_NAMESPACE, template.slug, template, updatedBy).value as TeamTemplate;
68
+ return (await setConfigAsync(TEAM_TEMPLATE_NAMESPACE, template.slug, template, updatedBy)).value as TeamTemplate;
68
69
  }
69
70
 
70
71
  export function listTeamTemplates(): TeamTemplate[] {
@@ -75,8 +76,8 @@ export function getTeamTemplate(slug: string): TeamTemplate | null {
75
76
  return getConfig<TeamTemplate>(TEAM_TEMPLATE_NAMESPACE, slug)?.value ?? null;
76
77
  }
77
78
 
78
- export function deleteTeamTemplate(slug: string, updatedBy?: string): boolean {
79
- return deleteConfig(TEAM_TEMPLATE_NAMESPACE, slug, updatedBy);
79
+ export function deleteTeamTemplateAsync(slug: string, updatedBy?: string): Promise<boolean> {
80
+ return deleteConfigAsync(TEAM_TEMPLATE_NAMESPACE, slug, updatedBy);
80
81
  }
81
82
 
82
83
  function parseFrozenComposition(raw: string): Array<Record<string, unknown>> {
@@ -107,7 +108,7 @@ function compositionFromFrozenSnapshot(raw: string): TeamTemplateCompositionEntr
107
108
  return composition;
108
109
  }
109
110
 
110
- export function captureTeamTemplateFromTeam(teamId: string, input: Record<string, unknown> = {}, updatedBy?: string): TeamTemplate {
111
+ export async function captureTeamTemplateFromTeamAsync(teamId: string, input: Record<string, unknown> = {}, updatedBy?: string): Promise<TeamTemplate> {
111
112
  const team = getTeam(teamId);
112
113
  if (!team) throw new ValidationError(`team ${teamId} not found`);
113
114
  if (!team.composition) throw new ValidationError("team has no frozen composition snapshot; dissolve it before capture");
@@ -123,7 +124,7 @@ export function captureTeamTemplateFromTeam(teamId: string, input: Record<string
123
124
  ...(team.brief ? { brief: team.brief } : {}),
124
125
  ...(team.definitionOfDone ? { definitionOfDone: team.definitionOfDone } : {}),
125
126
  }, existing);
126
- return setConfig(TEAM_TEMPLATE_NAMESPACE, template.slug, template, updatedBy).value as TeamTemplate;
127
+ return (await setConfigAsync(TEAM_TEMPLATE_NAMESPACE, template.slug, template, updatedBy)).value as TeamTemplate;
127
128
  }
128
129
 
129
130
  function cleanWait(value: unknown): number {
@@ -15,6 +15,7 @@ import { activeAgentKeep } from "./agent-keep";
15
15
  import { automaticDisposalBlocker } from "./automatic-disposal";
16
16
  import { agentGenerationKey } from "../db/agent-shutdown-in-flight";
17
17
  import { workflowAgentObligation } from "./workflow-agent-obligation";
18
+ import { workflowRunPaused } from "../db/workflow-task-read-ops";
18
19
  import type { AgentCard, Command, WorkspaceRecord } from "../types";
19
20
  import { emitCommandEvent } from "../command-events";
20
21
 
@@ -356,7 +357,8 @@ export async function reapTransientAgents(context: { signal?: AbortSignal } = {}
356
357
  if (completedProviderTurns(agent) === 0) { transientTracker.delete(agent.id); waiting.push(agent.id); continue; }
357
358
  // #3234 — an empty branch is not completion while the workflow still awaits this agent's delivery.
358
359
  const runId = workflowRunIdOf(agent);
359
- if (runId && await workflowAgentObligation(runId, agent.id)) { transientTracker.delete(agent.id); waiting.push(agent.id); continue; }
360
+ // #3757 — a paused run keeps all its step agents.
361
+ if (runId && (workflowRunPaused(runId) || await workflowAgentObligation(runId, agent.id))) { transientTracker.delete(agent.id); waiting.push(agent.id); continue; }
360
362
  const entry = transientTracker.get(agent.id) ?? { firstIdleAt: now };
361
363
  transientTracker.set(agent.id, entry);
362
364
  const idleForMs = now - entry.firstIdleAt;