@humwick/server 0.130.1 → 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 (437) hide show
  1. package/README.md +5 -0
  2. package/docs/guides/tools-administration.md +4 -0
  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-B_rFEbnL.js → fleet-C7jQWbw2.js} +2 -2
  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 +8 -0
  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 +3 -3
  128. package/src/config-store-write.ts +13 -1
  129. package/src/config-store.ts +25 -14
  130. package/src/db/activity-async.ts +6 -1
  131. package/src/db/activity-write-ops.ts +12 -1
  132. package/src/db/activity.ts +1 -14
  133. package/src/db/agent-search.ts +1 -15
  134. package/src/db/agents-async.ts +52 -5
  135. package/src/db/agents-write-ops.ts +12 -3
  136. package/src/db/agents.ts +81 -54
  137. package/src/db/ci-incidents-async.ts +5 -5
  138. package/src/db/ci-incidents-write-ops.ts +2 -2
  139. package/src/db/command-row.ts +10 -2
  140. package/src/db/commands-list-query.ts +2 -0
  141. package/src/db/commands-spawn-read.ts +1 -1
  142. package/src/db/config-store-write-ops.ts +7 -2
  143. package/src/db/continuation-archives-async.ts +5 -0
  144. package/src/db/continuation-archives-write-ops.ts +7 -0
  145. package/src/db/continuations.ts +2 -2
  146. package/src/db/db-writer-worker.ts +8 -1
  147. package/src/db/deploy-events-async.ts +1 -0
  148. package/src/db/deploy-events-read-ops.ts +2 -1
  149. package/src/db/deploy-events.ts +35 -4
  150. package/src/db/hosts-registration.ts +10 -2
  151. package/src/db/hosts.ts +9 -6
  152. package/src/db/inbox.ts +0 -14
  153. package/src/db/index.ts +2 -0
  154. package/src/db/integrations-async.ts +8 -0
  155. package/src/db/integrations-ops.ts +15 -0
  156. package/src/db/integrations.ts +15 -21
  157. package/src/db/issue-lifecycle.ts +3 -4
  158. package/src/db/issue-refs-async.ts +20 -0
  159. package/src/db/issue-refs-write-ops.ts +29 -0
  160. package/src/db/issue-refs.ts +7 -8
  161. package/src/db/long-running-work.ts +2 -2
  162. package/src/db/main-connection-writes.ts +23 -2
  163. package/src/db/maintenance-job-reconcile.ts +61 -0
  164. package/src/db/maintenance-job-runs-async.ts +14 -0
  165. package/src/db/maintenance-job-write-ops.ts +9 -0
  166. package/src/db/mappers.ts +2 -14
  167. package/src/db/merge-lease.ts +1 -15
  168. package/src/db/migrations.ts +7 -15
  169. package/src/db/orchestrators.ts +4 -72
  170. package/src/db/pairs.ts +1 -13
  171. package/src/db/project-profile-instructions.ts +50 -0
  172. package/src/db/prompt-versions.ts +9 -2
  173. package/src/db/provider-config-history.ts +8 -3
  174. package/src/db/provider-model-seed.ts +34 -0
  175. package/src/db/read-op-marshaling-fixtures.ts +5 -0
  176. package/src/db/read-ops.ts +4 -0
  177. package/src/db/routing-policy.ts +2 -3
  178. package/src/db/schema.ts +9 -3
  179. package/src/db/spawn-delegation-state.ts +11 -4
  180. package/src/db/stats-async.ts +4 -3
  181. package/src/db/task-entity.ts +23 -2
  182. package/src/db/task-list.ts +3 -1
  183. package/src/db/tasks.ts +1 -17
  184. package/src/db/timer-ingress.ts +7 -1
  185. package/src/db/token-db-write-ops.ts +17 -0
  186. package/src/db/token-standing.ts +15 -11
  187. package/src/db/workflow-contact.ts +1 -1
  188. package/src/db/workflow-task-read-ops.ts +30 -0
  189. package/src/db/write-ops.ts +6 -0
  190. package/src/db/writer-gateway.ts +17 -11
  191. package/src/dispatch-availability.ts +10 -6
  192. package/src/ephemeral/{landing-ingress-proof-worker.ts → landing-ingress-proof.test-fixture.ts} +4 -2
  193. package/src/host/disk-alert.ts +121 -0
  194. package/src/lifecycle-manager.ts +3 -2
  195. package/src/maintenance/cd-watcher.ts +3 -3
  196. package/src/maintenance/critical-job-watchdog.ts +16 -17
  197. package/src/maintenance/index.ts +1 -0
  198. package/src/maintenance/jobs.ts +3 -2
  199. package/src/maintenance/scheduler.ts +84 -128
  200. package/src/maintenance/stalled-delivery-pager.ts +6 -5
  201. package/src/maintenance/workspaces.ts +1 -1
  202. package/src/managed-policy.ts +31 -27
  203. package/src/mcp/index.ts +5 -2
  204. package/src/mcp/output-descriptions.ts +8 -8
  205. package/src/mcp/presentation.ts +4 -4
  206. package/src/mcp/project-profile-instructions.ts +32 -0
  207. package/src/mcp/tool-groups.ts +1 -1
  208. package/src/mcp/tools-spawn.ts +4 -3
  209. package/src/mcp-agent-action.ts +1 -2
  210. package/src/mcp-issue-ref-tools.ts +12 -11
  211. package/src/mcp-maintenance-tools.ts +2 -2
  212. package/src/mcp-profile-trust-tools.ts +4 -4
  213. package/src/mcp-provider-catalog.ts +2 -2
  214. package/src/mcp-task-tools.ts +11 -5
  215. package/src/mcp-team-template-tools.ts +3 -3
  216. package/src/mcp-workflow-tools.ts +10 -3
  217. package/src/notification-types.ts +2 -1
  218. package/src/operator-alarm.ts +26 -11
  219. package/src/project-instructions.ts +4 -0
  220. package/src/project-profile-instruction-text.ts +6 -0
  221. package/src/project-profile-instructions.ts +20 -0
  222. package/src/provider-catalog-store.ts +3 -49
  223. package/src/provider-config-store.ts +12 -11
  224. package/src/provider-model-seed.ts +1 -18
  225. package/src/provider-quota-config-store.ts +5 -4
  226. package/src/provider-secrets.test-fixture.ts +3 -3
  227. package/src/recipe-runner.ts +2 -2
  228. package/src/repo-config-source.ts +10 -4
  229. package/src/routes/agent-profiles.ts +6 -6
  230. package/src/routes/agents-spawn.ts +1 -1
  231. package/src/routes/agents.ts +6 -6
  232. package/src/routes/commands-listing.ts +4 -0
  233. package/src/routes/commands.ts +2 -2
  234. package/src/routes/continuation-archives.ts +2 -2
  235. package/src/routes/index.ts +10 -1
  236. package/src/routes/integrations.ts +24 -50
  237. package/src/routes/issue-refs.ts +7 -7
  238. package/src/routes/orchestrator-bootstrap.ts +1 -1
  239. package/src/routes/orchestrator-host-registration.ts +2 -2
  240. package/src/routes/orchestrator-host-utilization.ts +3 -1
  241. package/src/routes/orchestrator-proxy.ts +2 -2
  242. package/src/routes/orchestrator.ts +2 -2
  243. package/src/routes/project-profile-instructions.ts +27 -0
  244. package/src/routes/project-trust.ts +2 -2
  245. package/src/routes/provider-config.ts +6 -6
  246. package/src/routes/provider-quota-config.ts +2 -2
  247. package/src/routes/settings.ts +5 -5
  248. package/src/routes/stats.ts +6 -6
  249. package/src/routes/task-http.ts +15 -1
  250. package/src/routes/task-issues.ts +11 -26
  251. package/src/routes/tasks.ts +22 -53
  252. package/src/routes/team-templates.ts +5 -5
  253. package/src/routes/teams-config.ts +2 -2
  254. package/src/routes/tokens.ts +12 -5
  255. package/src/routes/workflows.ts +20 -5
  256. package/src/routing-policy-normalize.ts +1 -2
  257. package/src/routing-step-defaults.ts +0 -2
  258. package/src/runner-token-mint-limit.ts +63 -0
  259. package/src/runtime-grant.ts +4 -2
  260. package/src/runtime-tokens.ts +39 -7
  261. package/src/security.ts +13 -20
  262. package/src/server-background.ts +1 -1
  263. package/src/server.ts +5 -2
  264. package/src/services/host-utilization.ts +23 -0
  265. package/src/services/idle-epics.ts +5 -6
  266. package/src/services/issue-lifecycle.ts +57 -61
  267. package/src/services/issue-refs.ts +9 -13
  268. package/src/services/kernel-timers-cutover-chaos-worker.ts +10 -7
  269. package/src/services/kernel-timers-ingress.ts +1 -1
  270. package/src/services/landed-ancestry.ts +19 -3
  271. package/src/services/memory-areas.ts +15 -51
  272. package/src/services/plan-ci-notice.ts +57 -0
  273. package/src/services/plan-ci-recovery.ts +2 -55
  274. package/src/services/plan-wake-digest.ts +1 -0
  275. package/src/services/profile-trust.ts +4 -3
  276. package/src/services/project-config-read.ts +70 -0
  277. package/src/services/project-profile-instructions.ts +54 -0
  278. package/src/services/provisioning-harness/budget-baseline.json +4 -4
  279. package/src/services/restart-spawn.ts +61 -49
  280. package/src/services/self-care-state.ts +2 -1
  281. package/src/services/self-effort.ts +5 -5
  282. package/src/services/send-message.ts +5 -1
  283. package/src/services/spawn-agent.ts +13 -2
  284. package/src/services/spawn-chain-inspector.ts +6 -13
  285. package/src/services/spawn-policy.ts +68 -0
  286. package/src/services/spawn-tree-cleanup.ts +2 -2
  287. package/src/services/task-dispatch.ts +3 -0
  288. package/src/services/task-state-reconcile.ts +6 -14
  289. package/src/services/team-templates.ts +9 -8
  290. package/src/services/transient-agent-reaper.ts +3 -1
  291. package/src/services/workflow-auto-start.ts +25 -13
  292. package/src/services/workflow-ci-main.ts +1 -1
  293. package/src/services/workflow-deferred-cleanup.ts +4 -2
  294. package/src/services/workflow-effect-driver.ts +2 -0
  295. package/src/services/workflow-effects.ts +18 -11
  296. package/src/services/workflow-land-notice.ts +10 -0
  297. package/src/services/workflow-pause-text.ts +43 -0
  298. package/src/services/workflow-reconcile.ts +13 -11
  299. package/src/services/workflow-run-view.ts +2 -0
  300. package/src/services/workflow-runs.ts +29 -13
  301. package/src/services/workflow-supervisor-state.ts +5 -0
  302. package/src/services/workflow-supervisor.ts +7 -3
  303. package/src/settings/registry.ts +1 -2
  304. package/src/spawn-command.ts +4 -4
  305. package/src/spawn-models.ts +10 -12
  306. package/src/spawn-placement.ts +1 -1
  307. package/src/spawn-targets.ts +1 -1
  308. package/src/startup-context-injection.ts +1 -0
  309. package/src/state-adoption/cd-cutover.ts +53 -7
  310. package/src/state-adoption/fleet-cutover.ts +35 -0
  311. package/src/state-adoption/fleet-plan.ts +36 -0
  312. package/src/state-adoption/index.ts +2 -2
  313. package/src/state-adoption/linux-plan.ts +2 -2
  314. package/src/state-adoption/linux.ts +2 -2
  315. package/src/state-adoption/macos-plan.ts +2 -2
  316. package/src/state-adoption/macos.ts +1 -1
  317. package/src/steward.ts +7 -6
  318. package/src/task-dispatch-live.ts +1 -1
  319. package/src/task-dispatcher.ts +1 -1
  320. package/src/teams-config-store.ts +4 -3
  321. package/src/token-db-write.ts +10 -0
  322. package/src/workspace-command-settle.ts +2 -1
  323. package/src/workspace-steward-wake.ts +2 -2
  324. package/node_modules/@humwick/kernel/LICENSE +0 -167
  325. package/node_modules/@humwick/kernel/async-entity-persistence.ts +0 -344
  326. package/node_modules/@humwick/kernel/async-event-log.ts +0 -107
  327. package/node_modules/@humwick/kernel/automations.pilot.ts +0 -147
  328. package/node_modules/@humwick/kernel/config.ts +0 -111
  329. package/node_modules/@humwick/kernel/contract.ts +0 -572
  330. package/node_modules/@humwick/kernel/control-authz.ts +0 -281
  331. package/node_modules/@humwick/kernel/control.ts +0 -645
  332. package/node_modules/@humwick/kernel/effects.ts +0 -493
  333. package/node_modules/@humwick/kernel/event-log.ts +0 -319
  334. package/node_modules/@humwick/kernel/http-transport.ts +0 -384
  335. package/node_modules/@humwick/kernel/identity-grammar.ts +0 -20
  336. package/node_modules/@humwick/kernel/identity.ts +0 -248
  337. package/node_modules/@humwick/kernel/index.ts +0 -28
  338. package/node_modules/@humwick/kernel/kernel-persistence.ts +0 -181
  339. package/node_modules/@humwick/kernel/landing-incident.ts +0 -306
  340. package/node_modules/@humwick/kernel/landing.ts +0 -953
  341. package/node_modules/@humwick/kernel/manifest-validation.ts +0 -460
  342. package/node_modules/@humwick/kernel/mcp-transport.ts +0 -109
  343. package/node_modules/@humwick/kernel/message-authority-chaos-worker.ts +0 -200
  344. package/node_modules/@humwick/kernel/message-authority.ts +0 -281
  345. package/node_modules/@humwick/kernel/module-host.ts +0 -722
  346. package/node_modules/@humwick/kernel/openapi-init.ts +0 -14
  347. package/node_modules/@humwick/kernel/package.json +0 -39
  348. package/node_modules/@humwick/kernel/persistence-schema.ts +0 -248
  349. package/node_modules/@humwick/kernel/persistence.ts +0 -510
  350. package/node_modules/@humwick/kernel/registry.ts +0 -60
  351. package/node_modules/@humwick/kernel/sqlite-kernel-persistence.ts +0 -499
  352. package/node_modules/@humwick/kernel/sqlite-module-host.ts +0 -87
  353. package/node_modules/@humwick/kernel/timers-chaos-worker.ts +0 -186
  354. package/node_modules/@humwick/kernel/timers.pilot.ts +0 -1411
  355. package/node_modules/@humwick/kernel/workflow-activation.ts +0 -25
  356. package/node_modules/@humwick/kernel/workflow-backstop.ts +0 -40
  357. package/node_modules/@humwick/kernel/workflow-cancel-tree.ts +0 -20
  358. package/node_modules/@humwick/kernel/workflow-chaos-worker.ts +0 -213
  359. package/node_modules/@humwick/kernel/workflow-composition.ts +0 -231
  360. package/node_modules/@humwick/kernel/workflow-container.ts +0 -72
  361. package/node_modules/@humwick/kernel/workflow-control.test-fixture.ts +0 -42
  362. package/node_modules/@humwick/kernel/workflow-control.ts +0 -178
  363. package/node_modules/@humwick/kernel/workflow-definition.ts +0 -507
  364. package/node_modules/@humwick/kernel/workflow-dispatch.ts +0 -60
  365. package/node_modules/@humwick/kernel/workflow-foreach.ts +0 -167
  366. package/node_modules/@humwick/kernel/workflow-journal-codec.ts +0 -339
  367. package/node_modules/@humwick/kernel/workflow-journal.ts +0 -922
  368. package/node_modules/@humwick/kernel/workflow-lane.ts +0 -48
  369. package/node_modules/@humwick/kernel/workflow-lease-owner.ts +0 -60
  370. package/node_modules/@humwick/kernel/workflow-recovery.test-fixture.ts +0 -19
  371. package/node_modules/@humwick/kernel/workflow-runtime.ts +0 -938
  372. package/node_modules/@humwick/kernel/workflow-script.ts +0 -11
  373. package/node_modules/@humwick/kernel/workflow-state.ts +0 -299
  374. package/node_modules/@humwick/kernel/workflow-step-commit.ts +0 -195
  375. package/node_modules/@humwick/kernel/workflow-transitions.ts +0 -317
  376. package/node_modules/@humwick/kernel/workflow-values.ts +0 -153
  377. package/node_modules/@humwick/kernel/workflow.pilot.ts +0 -376
  378. package/node_modules/@humwick/offload/LICENSE +0 -167
  379. package/node_modules/@humwick/offload/README.md +0 -57
  380. package/node_modules/@humwick/offload/dist/async-runner.d.ts +0 -18
  381. package/node_modules/@humwick/offload/dist/async-runner.d.ts.map +0 -1
  382. package/node_modules/@humwick/offload/dist/async-runner.js +0 -158
  383. package/node_modules/@humwick/offload/dist/async-runner.js.map +0 -1
  384. package/node_modules/@humwick/offload/dist/envelope.d.ts +0 -25
  385. package/node_modules/@humwick/offload/dist/envelope.d.ts.map +0 -1
  386. package/node_modules/@humwick/offload/dist/envelope.js +0 -80
  387. package/node_modules/@humwick/offload/dist/envelope.js.map +0 -1
  388. package/node_modules/@humwick/offload/dist/fixtures/worker-task.d.ts +0 -10
  389. package/node_modules/@humwick/offload/dist/fixtures/worker-task.d.ts.map +0 -1
  390. package/node_modules/@humwick/offload/dist/fixtures/worker-task.js +0 -59
  391. package/node_modules/@humwick/offload/dist/fixtures/worker-task.js.map +0 -1
  392. package/node_modules/@humwick/offload/dist/index.d.ts +0 -5
  393. package/node_modules/@humwick/offload/dist/index.d.ts.map +0 -1
  394. package/node_modules/@humwick/offload/dist/index.js +0 -5
  395. package/node_modules/@humwick/offload/dist/index.js.map +0 -1
  396. package/node_modules/@humwick/offload/dist/subprocess-runner.d.ts +0 -62
  397. package/node_modules/@humwick/offload/dist/subprocess-runner.d.ts.map +0 -1
  398. package/node_modules/@humwick/offload/dist/subprocess-runner.js +0 -295
  399. package/node_modules/@humwick/offload/dist/subprocess-runner.js.map +0 -1
  400. package/node_modules/@humwick/offload/dist/types.d.ts +0 -111
  401. package/node_modules/@humwick/offload/dist/types.d.ts.map +0 -1
  402. package/node_modules/@humwick/offload/dist/types.js +0 -176
  403. package/node_modules/@humwick/offload/dist/types.js.map +0 -1
  404. package/node_modules/@humwick/offload/dist/worker-pool.d.ts +0 -23
  405. package/node_modules/@humwick/offload/dist/worker-pool.d.ts.map +0 -1
  406. package/node_modules/@humwick/offload/dist/worker-pool.js +0 -435
  407. package/node_modules/@humwick/offload/dist/worker-pool.js.map +0 -1
  408. package/node_modules/@humwick/offload/dist/worker-runtime.d.ts +0 -2
  409. package/node_modules/@humwick/offload/dist/worker-runtime.d.ts.map +0 -1
  410. package/node_modules/@humwick/offload/dist/worker-runtime.js +0 -27
  411. package/node_modules/@humwick/offload/dist/worker-runtime.js.map +0 -1
  412. package/node_modules/@humwick/offload/dist/worker.d.ts +0 -5
  413. package/node_modules/@humwick/offload/dist/worker.d.ts.map +0 -1
  414. package/node_modules/@humwick/offload/dist/worker.js +0 -1
  415. package/node_modules/@humwick/offload/dist/worker.js.map +0 -1
  416. package/node_modules/@humwick/offload/package.json +0 -42
  417. package/node_modules/@humwick/offload/src/async-runner.ts +0 -170
  418. package/node_modules/@humwick/offload/src/envelope.ts +0 -92
  419. package/node_modules/@humwick/offload/src/fixtures/worker-task.ts +0 -49
  420. package/node_modules/@humwick/offload/src/index.ts +0 -4
  421. package/node_modules/@humwick/offload/src/subprocess-runner.ts +0 -334
  422. package/node_modules/@humwick/offload/src/types.ts +0 -235
  423. package/node_modules/@humwick/offload/src/worker-pool.ts +0 -463
  424. package/node_modules/@humwick/offload/src/worker-runtime.ts +0 -32
  425. package/node_modules/@humwick/offload/src/worker.ts +0 -5
  426. package/public/assets/agent-liveness-DyqxfB-t.js +0 -2
  427. package/public/assets/agent-profiles-BpI_jTxq.js +0 -2
  428. package/public/assets/index-B7ZerHLY.css +0 -2
  429. package/public/assets/index-BU3uWo2t.js +0 -15
  430. package/public/assets/projects-B_lmcqfO.js +0 -2
  431. package/public/assets/provider-display-DbLWpP3j.js +0 -2
  432. package/public/assets/run-card-CO_bguQf.js +0 -2
  433. package/public/assets/use-project-list-B-T2Bnnn.js +0 -2
  434. package/public/assets/versioned-prompt-editor-Diu8BIaM.js +0 -8
  435. package/runner/src/gate-run-lease-probe.ts +0 -38
  436. package/src/db/claude-catalog-reconcile.ts +0 -27
  437. package/src/routing-model-aliases.ts +0 -21
@@ -70,6 +70,7 @@ export function renderPlanWakeDigest(agentId: string, now = Date.now()): string
70
70
  do {
71
71
  const page = listPlansByTeam(teamId, { limit: 8, cursor });
72
72
  for (const summary of page.plans) {
73
+ if (!summary.nodeCount) continue;
73
74
  const plan = getPlan(summary.id);
74
75
  if (!plan) continue;
75
76
  const line = planLine(plan, now);
@@ -1,7 +1,8 @@
1
1
  // #3636 — project trust edits without the dashboard: set a profile's review mode, acknowledge its
2
2
  // project hooks/MCP, approve an observed value. One seam for the HTTP routes and the MCP tool.
3
3
  import { ValidationError, type AgentProfile, type ProfileProjectReviewMode } from "@humwick/sdk";
4
- import { getAgentProfile, setAgentProfile, validateAgentProfile } from "../config-store";
4
+ import { getAgentProfile, validateAgentProfile } from "../config-store";
5
+ import { setAgentProfileAsync } from "../config-store-write";
5
6
  import type { ConfigEntry } from "../types";
6
7
  import type { ProjectTrustApproval, ProjectTrustCapability } from "../db/project-trust-approvals";
7
8
  import { approveProjectCapabilityReview, projectCellsHash } from "../project-trust";
@@ -38,9 +39,9 @@ export function withProjectTrust(profile: AgentProfile, update: ProjectTrustUpda
38
39
  return validated;
39
40
  }
40
41
 
41
- export function updateProfileProjectTrust(name: string, update: ProjectTrustUpdate, by: string): ConfigEntry<AgentProfile> {
42
+ export async function updateProfileProjectTrustAsync(name: string, update: ProjectTrustUpdate, by: string): Promise<ConfigEntry<AgentProfile>> {
42
43
  if (!update.review && !update.ack?.length) throw new ValidationError("nothing to update: pass review and/or ack");
43
- const saved = setAgentProfile(withProjectTrust(existingProfile(name).value, update, by), by, { overrideBuiltIn: true });
44
+ const saved = await setAgentProfileAsync(withProjectTrust(existingProfile(name).value, update, by), by, { overrideBuiltIn: true });
44
45
  emitConfigChanged(saved.namespace, saved.key, saved.version);
45
46
  return saved;
46
47
  }
@@ -0,0 +1,70 @@
1
+ // #3635/#3736 — one reader for `.agent-relay/*.json` project config (areas.json, spawn-policy.json): the
2
+ // file at the checkout's default branch (origin/<default>, fetched first), read on the checkout's host
3
+ // through the #3043 project-files reader. Never a working tree and never cached, so a stale checkout or
4
+ // an old negative answer cannot hide a file that landed.
5
+ import { readProjectFiles } from "@humwick/sdk";
6
+ import type { ConfigAtCommit } from "@humwick/sdk/repo-config-file";
7
+ import { errMessage } from "@humwick/sdk";
8
+ import { rootsForProject } from "../db";
9
+ import { localProjectGit, readHostProjectFiles } from "../project-roles";
10
+ import { hostIsRelayHost } from "../relay-host-locality";
11
+ import { resolveProjectFilesHost } from "./spawn-project-role";
12
+
13
+ /** A checkout to read from: a cwd on a host (a hostname or a registered orchestrator). */
14
+ export interface ProjectCheckout {
15
+ cwd: string;
16
+ host?: string;
17
+ orchestratorId?: string;
18
+ }
19
+
20
+ export type ConfigLookup<T> = ConfigAtCommit<T> | { status: "unreadable"; error: string };
21
+
22
+ type Snapshot = { ref: string; sha: string; files: Record<string, string | null> };
23
+
24
+ const warned = new Set<string>();
25
+
26
+ /** One warning per distinct problem, so a broken file does not flood the log on every read. */
27
+ export function warnConfigOnce(key: string, message: string): void {
28
+ if (warned.has(key)) return;
29
+ if (warned.size >= 1000) warned.clear();
30
+ warned.add(key);
31
+ console.warn(message);
32
+ }
33
+
34
+ /** `file` at the checkout's default branch; a read failure is `unreadable`, never a throw. Invalid and
35
+ * unreadable outcomes warn once, naming `file`, the checkout and why, with `consequence` appended. */
36
+ export async function readProjectConfig<T>(checkout: ProjectCheckout, file: string, parse: (snapshot: Snapshot) => ConfigAtCommit<T>, consequence: string): Promise<ConfigLookup<T>> {
37
+ const where = `${checkout.orchestratorId ?? checkout.host ?? "local"}:${checkout.cwd}`;
38
+ try {
39
+ const snapshot = !checkout.orchestratorId && hostIsRelayHost(checkout.host)
40
+ ? await readProjectFiles({ paths: [file] }, localProjectGit(checkout.cwd))
41
+ : await readHostProjectFiles(
42
+ resolveProjectFilesHost({ subject: file, cwd: checkout.cwd, orchestratorId: checkout.orchestratorId, preferHost: checkout.host }),
43
+ checkout.cwd, { paths: [file] }, file,
44
+ );
45
+ const lookup = parse(snapshot);
46
+ if (lookup.status === "invalid") warnConfigOnce(`${file}@${where}@${lookup.sha}`, `[project-config] ${file} for ${where} at ${lookup.sha.slice(0, 12)} is invalid: ${lookup.error}; ${consequence}`);
47
+ return lookup;
48
+ } catch (err) {
49
+ const error = errMessage(err);
50
+ warnConfigOnce(`${file}@${where}`, `[project-config] ${file} for ${where}: unreadable: ${error}; ${consequence}`);
51
+ return { status: "unreadable", error };
52
+ }
53
+ }
54
+
55
+ /** The first checkout that can be read answers: the preferred ones, then each registered root of the project. */
56
+ export async function readProjectConfigFirst<T>(
57
+ projectId: string | undefined, preferred: readonly ProjectCheckout[], file: string, parse: (snapshot: Snapshot) => ConfigAtCommit<T>, consequence: string,
58
+ ): Promise<{ lookup?: ConfigAtCommit<T>; failures: string[] }> {
59
+ const checkouts: ProjectCheckout[] = [
60
+ ...preferred,
61
+ ...(projectId ? rootsForProject(projectId).map((root) => ({ cwd: root.rootPath, ...(root.orchestratorId ? { orchestratorId: root.orchestratorId } : {}) })) : []),
62
+ ];
63
+ const failures: string[] = [];
64
+ for (const checkout of checkouts) {
65
+ const lookup = await readProjectConfig(checkout, file, parse, consequence);
66
+ if (lookup.status !== "unreadable") return { lookup, failures };
67
+ failures.push(lookup.error);
68
+ }
69
+ return { failures };
70
+ }
@@ -0,0 +1,54 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { hasScope, isRecord } from "@humwick/sdk";
3
+ import type { ProjectProfileInstructions } from "../types";
4
+ import { listAgentProfiles, resolveAgentProfileKey } from "../config-store";
5
+ import { getGateway } from "../db/gateway";
6
+ import { ValidationError } from "../db/connection";
7
+ import { promptActorFromContext, promptVersionNumber } from "./prompt-version-writer";
8
+ import type { AuthContext } from "./auth-context";
9
+ import { cleanString } from "../validation";
10
+ import { emitConfigChanged } from "../sse";
11
+
12
+ export async function projectProfileInstructionKey(projectId: string, profile: string) {
13
+ const canonical = resolveAgentProfileKey(profile);
14
+ if (!canonical) throw new ValidationError("profile not found");
15
+ if (!await getGateway().read("projects.get", { id: projectId })) throw new ValidationError("project not found");
16
+ return { projectId, profile: canonical };
17
+ }
18
+
19
+ export async function readProjectProfileInstructions(projectId: string, profile: string) {
20
+ return getGateway().read("projectProfileInstructions.get", await projectProfileInstructionKey(projectId, profile));
21
+ }
22
+
23
+ export async function projectProfileInstructionHistory(projectId: string, profile: string) {
24
+ return { versions: await getGateway().read("projectProfileInstructions.history", await projectProfileInstructionKey(projectId, profile)) };
25
+ }
26
+
27
+ export async function listProjectProfileInstructions(filter: { projectId?: string; profile?: string }) {
28
+ const gateway = getGateway();
29
+ const profile = filter.profile ? resolveAgentProfileKey(filter.profile) : undefined;
30
+ if (filter.profile && !profile) throw new ValidationError("profile not found");
31
+ if (filter.projectId && !await gateway.read("projects.get", { id: filter.projectId })) throw new ValidationError("project not found");
32
+ if (!filter.projectId && !profile) throw new ValidationError("projectId or profile required");
33
+ const stored = await gateway.read("projectProfileInstructions.list", { projectId: filter.projectId, profile });
34
+ const byKey = new Map(stored.map(entry => [JSON.stringify([entry.projectId, entry.profile]), entry]));
35
+ const entry = (projectId: string, profile: string): ProjectProfileInstructions => byKey.get(JSON.stringify([projectId, profile])) ?? { projectId, profile, template: "", version: 0, updatedAt: 0 };
36
+ return { instructions: filter.projectId
37
+ ? listAgentProfiles().filter(item => !profile || item.key === profile).map(item => entry(filter.projectId!, item.key))
38
+ : (await gateway.read("projects.list", { includeArchived: true })).map(project => ({ ...entry(project.id, profile!), projectTitle: project.title, projectSlug: project.slug })) };
39
+ }
40
+
41
+ export async function mutateProjectProfileInstructions(ctx: AuthContext, projectId: string, profile: string, input: unknown, restore = false) {
42
+ if (ctx.actor.kind !== "system" && !hasScope(ctx.scopes, "agent:write")) throw new ValidationError("agent:write scope required");
43
+ const key = await projectProfileInstructionKey(projectId, profile);
44
+ if (!isRecord(input)) throw new ValidationError("JSON object body required");
45
+ if (!Number.isSafeInteger(input.expectedVersion) || (input.expectedVersion as number) < 0) throw new ValidationError("expectedVersion must be a non-negative integer");
46
+ const result = await getGateway().write("projectProfileInstructions.write", {
47
+ ...key, expectedVersion: input.expectedVersion as number,
48
+ template: restore ? undefined : input.template as string,
49
+ restoreVersion: restore ? promptVersionNumber(input.version, "version") : undefined,
50
+ actor: promptActorFromContext(ctx), reason: cleanString(input.reason, "reason", { max: 2000 }),
51
+ }, { idempotencyKey: randomUUID() }) as unknown as ProjectProfileInstructions;
52
+ emitConfigChanged("project-profile-instructions", `${key.projectId}:${key.profile}`, result.version);
53
+ return result;
54
+ }
@@ -19,7 +19,7 @@
19
19
  "unknown": 0
20
20
  },
21
21
  "mcp:relay-schemas": {
22
- "residentKnown": 24523,
22
+ "residentKnown": 24574,
23
23
  "onDemandAvailable": 0,
24
24
  "unknown": 0
25
25
  },
@@ -114,7 +114,7 @@
114
114
  "unknown": 0
115
115
  },
116
116
  "mcp:relay-schemas": {
117
- "residentKnown": 24523,
117
+ "residentKnown": 24574,
118
118
  "onDemandAvailable": 0,
119
119
  "unknown": 0
120
120
  },
@@ -364,11 +364,11 @@
364
364
  "reviewer:codex": 11221,
365
365
  "sec-ops:claude": 11610,
366
366
  "sec-ops:codex": 10644,
367
- "ops-assistant:claude": 25307,
367
+ "ops-assistant:claude": 25390,
368
368
  "ops-assistant:codex": 25562,
369
369
  "sub-coordinator:claude": 20137,
370
370
  "sub-coordinator:codex": 20392,
371
- "coordinator:claude": 26501,
371
+ "coordinator:claude": 26584,
372
372
  "coordinator:codex": 26622,
373
373
  "isolated-research:claude": 2852,
374
374
  "isolated-research:codex": 2790,
@@ -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
+ }