@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
@@ -0,0 +1,93 @@
1
+ import { isHumanAgentId } from "@humwick/sdk";
2
+ import type { BranchLandedInput, resolveLandedTask } from "./branch-landed";
3
+ import { getAgent, getTaskDetail } from "./db";
4
+ import { renderTemplate } from "./prompt-resolver";
5
+ import { routeNotification, INFORMATIONAL_TTL_MS } from "./notification-router";
6
+ import { stampLifecycleSignal, taskSignalKey } from "./lifecycle-signal-registry";
7
+ import { resolveCoordinatorAgentId } from "./steward-identity";
8
+ import { resolveTaskOwnerAgentId } from "./task-owner";
9
+ import { audienceWithTaskOwnerRole } from "./task-owner-notices";
10
+ import { notifyLandUnconfirmed, verifyPublishedLand } from "./services/landed-ancestry";
11
+
12
+ export async function announceVerifiedLand(input: BranchLandedInput, landedTask: ReturnType<typeof resolveLandedTask>, idempotencyKey?: string): Promise<void> {
13
+ const { workspace } = input;
14
+ const base = workspace.baseRef ?? "main";
15
+ const verification = input.mergedSha && input.landVerifiedSha === input.mergedSha
16
+ ? "confirmed" : await verifyPublishedLand(workspace.repoRoot, input.mergedSha, workspace.baseRef);
17
+ if (verification !== "confirmed") {
18
+ notifyLandUnconfirmed({ workspace, sha: input.mergedSha, targetRef: base.startsWith("origin/") ? base : `origin/${base}`, kind: verification });
19
+ return;
20
+ }
21
+ const landedBranch = workspace.branch;
22
+ const shortSha = input.mergedSha?.slice(0, 12);
23
+ const landedTaskPayload = landedTask ? { taskId: landedTask.taskId, taskLabel: landedTask.taskLabel } : {};
24
+ const author = workspace.ownerAgentId;
25
+ const shaLabel = shortSha ? ` as \`${shortSha}\`` : "";
26
+ const subjectLabel = input.subject ? ` — "${input.subject}"` : "";
27
+ const payload = {
28
+ verified: true,
29
+ kind: "branch.landed",
30
+ workspaceId: workspace.id,
31
+ repoRoot: workspace.repoRoot,
32
+ branch: landedBranch,
33
+ base,
34
+ sha: input.mergedSha,
35
+ author,
36
+ newBranch: input.newBranch,
37
+ pushed: input.pushed,
38
+ ...landedTaskPayload,
39
+ };
40
+
41
+ // The branch author cares most — push regardless of online (store-ahead delivers it on
42
+ // next poll if they've moved on, #234). A live owner continues in its worktree.
43
+ if (author) {
44
+ const branchLabel = landedBranch ? `\`${landedBranch}\`` : "Your branch";
45
+ const continueLabel = input.newBranch
46
+ ? ` You're now on \`${input.newBranch}\` — keep working there.`
47
+ : " Worktree reclaimed.";
48
+ // Durable (ttl null): the author's own branch landing must reach them even after they go
49
+ // offline (store-ahead #234) — not a bystander notice that goes stale.
50
+ const notices = routeNotification({
51
+ type: "branch.landed",
52
+ scope: { author, repoRoot: workspace.repoRoot, workspaceId: workspace.id },
53
+ recipients: [author],
54
+ ttlMs: null,
55
+ message: {
56
+ subject: "Your branch landed",
57
+ body: renderTemplate("notify.land.success", {
58
+ branch: branchLabel,
59
+ base,
60
+ sha: `${shaLabel}${subjectLabel}`,
61
+ publishLabel: "",
62
+ continueLabel,
63
+ }) + " verified: true.",
64
+ payload: { ...payload, notification: "landed-success" },
65
+ idempotencyKey,
66
+ replyExpected: false,
67
+ },
68
+ });
69
+ if (landedTask && input.mergedSha) {
70
+ for (const notice of notices) stampLifecycleSignal(notice.to, taskSignalKey(landedTask.taskId, "landed-on-main", input.mergedSha));
71
+ }
72
+ }
73
+
74
+ // Coordinator copy (#2837), informational. No explicit recipients: the router records the rule decision (#446).
75
+ const mainScope = { author, repoRoot: workspace.repoRoot, workspaceId: workspace.id, teamId: landedTask?.teamId ?? (author ? getAgent(author)?.teamId : undefined), coordinatorAgentIds: [...new Set([resolveCoordinatorAgentId(author), landedTask && resolveTaskOwnerAgentId(getTaskDetail(landedTask.taskId))].filter((id): id is string => !!id && !isHumanAgentId(id)))] };
76
+ const branchLabel = landedBranch ? `\`${landedBranch}\`` : "A branch";
77
+ const authorLabel = author ? ` by \`${author}\`` : "";
78
+ const notices = routeNotification({
79
+ type: "branch.landed",
80
+ scope: mainScope, ...audienceWithTaskOwnerRole("branch.landed", mainScope, landedTask?.taskId), // #2862
81
+ ttlMs: INFORMATIONAL_TTL_MS,
82
+ message: {
83
+ subject: `Merged to ${base}`,
84
+ body: `🔀 ${branchLabel}${authorLabel} merged to \`${base}\`${shaLabel}${subjectLabel}. verified: true.`,
85
+ payload: { ...payload, notification: "landed-success" },
86
+ idempotencyKey,
87
+ replyExpected: false,
88
+ },
89
+ });
90
+ if (landedTask && input.mergedSha) {
91
+ for (const notice of notices) stampLifecycleSignal(notice.to, taskSignalKey(landedTask.taskId, "landed-on-main", input.mergedSha));
92
+ }
93
+ }
@@ -1,3 +1,4 @@
1
+ import { announceVerifiedLand } from "./branch-land-notice";
1
2
  import { errMessage, isHumanAgentId, type BaseWorktreeSyncResult } from "@humwick/sdk";
2
3
  import { emitRelayEvent } from "./events";
3
4
  import { routeNotification, INFORMATIONAL_TTL_MS } from "./notification-router";
@@ -32,6 +33,8 @@ export interface BranchLandedInput extends LandedTaskAttributionInput {
32
33
  workspace: Pick<WorkspaceRecord, "id" | "repoRoot" | "branch" | "baseRef" | "baseSha" | "ownerAgentId" | "orchestratorId">;
33
34
  /** SHA the base now points at after the land. */
34
35
  mergedSha?: string;
36
+ /** Landing host proof from its fresh origin ancestry check. Must match mergedSha. */
37
+ landVerifiedSha?: string;
35
38
  /** Original command settlement clock when recovering an older land. */
36
39
  landedAt?: number;
37
40
  /** Subject line of the landed commit, when the orchestrator reported it. */
@@ -168,7 +171,7 @@ async function landedCommitSubjects(input: BranchLandedInput, log: BranchLandedL
168
171
  }, log);
169
172
  }
170
173
 
171
- function resolveLandedTask(input: LandedTaskAttributionInput): {
174
+ export function resolveLandedTask(input: LandedTaskAttributionInput): {
172
175
  taskId: number;
173
176
  taskLabel: string;
174
177
  projectId?: string;
@@ -348,79 +351,7 @@ export function announceBranchLanded(input: BranchLandedInput): void {
348
351
  }
349
352
  })());
350
353
 
351
- const author = workspace.ownerAgentId;
352
- const shaLabel = shortSha ? ` as \`${shortSha}\`` : "";
353
- const subjectLabel = input.subject ? ` — "${input.subject}"` : "";
354
- const payload = {
355
- kind: "branch.landed",
356
- workspaceId: workspace.id,
357
- repoRoot: workspace.repoRoot,
358
- branch: landedBranch,
359
- base,
360
- sha: input.mergedSha,
361
- author,
362
- newBranch: input.newBranch,
363
- pushed: input.pushed,
364
- ...landedTaskPayload,
365
- };
366
-
367
- // A local-only land (push disabled, or base has no upstream) has NOT reached the
368
- // remote — say so rather than implying a published merge an agent would report as
369
- // shipped (#285). `undefined` (older orchestrators) keeps the message generic.
370
- const publishLabel = input.pushed === false ? ` Not yet pushed to \`origin/${base}\` — local only.` : "";
371
-
372
- // The branch author cares most — push regardless of online (store-ahead delivers it on
373
- // next poll if they've moved on, #234). A live owner continues in its worktree.
374
- if (author) {
375
- const branchLabel = landedBranch ? `\`${landedBranch}\`` : "Your branch";
376
- const continueLabel = input.newBranch
377
- ? ` You're now on \`${input.newBranch}\` — keep working there.`
378
- : " Worktree reclaimed.";
379
- // Durable (ttl null): the author's own branch landing must reach them even after they go
380
- // offline (store-ahead #234) — not a bystander notice that goes stale.
381
- const notices = routeNotification({
382
- type: "branch.landed",
383
- scope: { author, repoRoot: workspace.repoRoot, workspaceId: workspace.id },
384
- recipients: [author],
385
- ttlMs: null,
386
- message: {
387
- subject: "Your branch landed",
388
- body: renderTemplate("notify.land.success", {
389
- branch: branchLabel,
390
- base,
391
- sha: `${shaLabel}${subjectLabel}`,
392
- publishLabel,
393
- continueLabel,
394
- }),
395
- payload: { ...payload, notification: "landed-success" },
396
- idempotencyKey,
397
- replyExpected: false,
398
- },
399
- });
400
- if (landedTask && input.mergedSha) {
401
- for (const notice of notices) stampLifecycleSignal(notice.to, taskSignalKey(landedTask.taskId, "landed-on-main", input.mergedSha));
402
- }
403
- }
404
-
405
- // Coordinator copy (#2837), informational. No explicit recipients: the router records the rule decision (#446).
406
- const mainScope = { author, repoRoot: workspace.repoRoot, workspaceId: workspace.id, teamId: landedTask?.teamId ?? (author ? getAgent(author)?.teamId : undefined), coordinatorAgentIds: [...new Set([resolveCoordinatorAgentId(author), landedTask && resolveTaskOwnerAgentId(getTaskDetail(landedTask.taskId))].filter((id): id is string => !!id && !isHumanAgentId(id)))] };
407
- const branchLabel = landedBranch ? `\`${landedBranch}\`` : "A branch";
408
- const authorLabel = author ? ` by \`${author}\`` : "";
409
- const notices = routeNotification({
410
- type: "branch.landed",
411
- scope: mainScope, ...audienceWithTaskOwnerRole("branch.landed", mainScope, landedTask?.taskId), // #2862
412
- ttlMs: INFORMATIONAL_TTL_MS,
413
- message: {
414
- subject: `Merged to ${base}`,
415
- body: `🔀 ${branchLabel}${authorLabel} merged to \`${base}\`${shaLabel}${subjectLabel}.${publishLabel}`,
416
- payload: { ...payload, notification: "landed-success" },
417
- idempotencyKey,
418
- replyExpected: false,
419
- },
420
- });
421
- if (landedTask && input.mergedSha) {
422
- for (const notice of notices) stampLifecycleSignal(notice.to, taskSignalKey(landedTask.taskId, "landed-on-main", input.mergedSha));
423
- }
354
+ trackLandLifecycleDrive(announceVerifiedLand(input, landedTask, idempotencyKey));
424
355
  }
425
356
 
426
357
  /**
@@ -682,7 +613,7 @@ const DRAINED_LAND_STRAND_BOOKKEEPING = {
682
613
  * (record the receipt, announce a real land, drain the steward/strand bookkeeping so no
683
614
  * watchdog re-wakes or re-pages a branch with nothing left to land).
684
615
  */
685
- export async function reconcileLandedWorkspace(ws: WorkspaceRecord, preview: WorkspaceMergePreview): Promise<LandedWorkspaceReconciliation> {
616
+ export async function reconcileLandedWorkspace(ws: WorkspaceRecord, preview: WorkspaceMergePreview, landVerifiedSha?: string): Promise<LandedWorkspaceReconciliation> {
686
617
  const observedSha = typeof preview.prMergeSha === "string"
687
618
  ? preview.prMergeSha
688
619
  : typeof preview.landedSha === "string"
@@ -753,7 +684,7 @@ export async function reconcileLandedWorkspace(ws: WorkspaceRecord, preview: Wor
753
684
  metadata: { source: "server", maintenanceJobId: "workspace-conflict-scan", workspaceId: ws.id, fromStatus: ws.status, toStatus: nextStatus, ...(ownerAlive ? { ownerAlive: true, keptLive: true } : {}), ...(sha ? { landedSha: sha } : {}), ...(landedCommitCount === 0 ? { landedCommitCount: 0 } : {}) },
754
685
  });
755
686
  try {
756
- notifyBranchLanded({ workspace: ws, mergedSha: sha, landedTipSha: sha, commitShas: sha ? [sha] : [], pushed: true, prLand: preview.prMerged === true, ...(landedCommitCount === 0 ? { landedCommitCount } : {}) });
687
+ notifyBranchLanded({ workspace: ws, mergedSha: sha, landVerifiedSha, landedTipSha: sha, commitShas: sha ? [sha] : [], pushed: true, prLand: preview.prMerged === true, ...(landedCommitCount === 0 ? { landedCommitCount } : {}) });
757
688
  } catch {
758
689
  // Notification is best-effort; the status change + activity event still stand.
759
690
  }
package/src/bus.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { Server, ServerWebSocket } from "bun";
2
- import { ValidationError, applyAgentLifecycleUpdate, getAgent, getDb, orphanTasksForAgentAsync, recordHeartbeat, setStatus, validateAgentSession } from "./db";
2
+ import { ValidationError, applyAgentLifecycleUpdateAsync, getAgent, getDb, orphanTasksForAgentAsync, recordHeartbeatAsync, validateAgentSession } from "./db";
3
3
  import { getOutboxCursor, hasReplayGap, replayEventsSlice, type BusEvent } from "./bus-outbox";
4
4
  import { emitAgentStatus } from "./agent-status-events";
5
5
  import { AgentStatusSubscribers, matchesSubscription, wantsAgentStatus } from "./transport-subscriptions";
@@ -34,7 +34,7 @@ import {
34
34
  type BusFrame,
35
35
  type RegisterFrame,
36
36
  } from "@humwick/sdk/protocol";
37
- import { errMessage, isRecord } from "@humwick/sdk";
37
+ import { errMessage, fireAndForget, isRecord } from "@humwick/sdk";
38
38
  import { getComponentAuth, isComponentAuthorizedFor, isAuthorized, isOriginAllowed, unauthorized } from "./security";
39
39
  import type { AgentCard, ComponentToken, ContextState, Message, ProviderCapabilities, QuotaState, RegisterAgentInput, Task } from "./types";
40
40
 
@@ -138,8 +138,11 @@ export function busHandleClose(ws: BusWebSocket): void {
138
138
  // on the establishing socket id, so a foreign socket's close cannot evict the owner's binding and a stale
139
139
  // close cannot evict a newer register's binding).
140
140
  if (!conn.transportReconnect) {
141
- setStatus(conn.agentId, "stale", { instanceId: conn.instanceId, epoch: conn.epoch });
142
- queueAgentStatusEvent(conn.agentId);
141
+ const agentId = conn.agentId;
142
+ void fireAndForget(`[bus] mark ${agentId} stale on close`, async () => {
143
+ await applyAgentLifecycleUpdateAsync(agentId, { status: "stale" }, { instanceId: conn.instanceId, epoch: conn.epoch });
144
+ queueAgentStatusEvent(agentId);
145
+ });
143
146
  }
144
147
  }
145
148
  }
@@ -162,7 +165,7 @@ export async function expireStaleBusAgents(graceMs = busStaleGraceMs(), lagGrace
162
165
  // but do NOT revoke the token — a reconnect accepts the EXISTING token and re-marks
163
166
  // live. The #484 restart-strand revoke now fires on instance takeover in registerAgent
164
167
  // (a fresh instanceId → epoch bump), never on a heartbeat lapse.
165
- setStatus(row.id, "offline");
168
+ await applyAgentLifecycleUpdateAsync(row.id, { status: "offline" });
166
169
  await emitAgentStatus(row.id);
167
170
  const tasks = await orphanTasksForAgentAsync(row.id);
168
171
  for (const task of tasks) {
@@ -187,7 +190,7 @@ async function handleFrame(ws: BusWebSocket, frame: ReturnType<typeof validateCl
187
190
  await handleRegister(ws, frame);
188
191
  return;
189
192
  case "heartbeat":
190
- await withRegistered(ws, frame, (conn) => {
193
+ await withRegistered(ws, frame, async (conn) => {
191
194
  if (conn.agentId) {
192
195
  // #633 — a runner whose provider terminally exited stays alive and keeps sending idle
193
196
  // keepalives (semanticStatus is never "offline"). Those must NOT resurrect an agent the
@@ -199,11 +202,10 @@ async function handleFrame(ws: BusWebSocket, frame: ReturnType<typeof validateCl
199
202
  if (!heldOffline) {
200
203
  const wasUnavailable = current ? isAgentUnavailable(current) : false;
201
204
  const runtime = runtimeFromMeta(frame.payload.meta);
202
- // #1468 — coalesce the steady-state liveness write; recordHeartbeat writes through
203
- // synchronously only on a status change / resurrection (and persists last_seen + runtime
204
- // then). This replaces the old heartbeat()+setStatus() pair that rewrote the row twice
205
- // per beat even when nothing changed.
206
- recordHeartbeat(conn.agentId, { guard: { instanceId: conn.instanceId, epoch: conn.epoch }, status: frame.payload.status, runtime });
205
+ // #1468 — coalesce the steady-state liveness write; only a status change / resurrection
206
+ // writes (last_seen + runtime), on the writer thread (#1870 S7). This replaces the old
207
+ // heartbeat()+setStatus() pair that rewrote the row twice per beat even when nothing changed.
208
+ await recordHeartbeatAsync(conn.agentId, { guard: { instanceId: conn.instanceId, epoch: conn.epoch }, status: frame.payload.status, runtime });
207
209
  queueAgentStatusEvent(conn.agentId);
208
210
  // #416 — flush queued messages if the agent transitioned from unavailable to available.
209
211
  if (wasUnavailable) {
@@ -225,19 +227,16 @@ async function handleFrame(ws: BusWebSocket, frame: ReturnType<typeof validateCl
225
227
  await withRegistered(ws, frame, async (conn) => {
226
228
  if (!conn.agentId) return sendError(ws, frame.id, "AGENT_REQUIRED", "status frames require an agent registration");
227
229
  const guard = { instanceId: conn.instanceId, epoch: conn.epoch };
228
- // #1895 (S3.2 L10 review, D2) — DELIBERATELY left on the sync write path: this is the
229
- // per-status-frame hot WS seam (every heartbeat/status update from every live agent), not the
230
- // cold/admin lifecycle surface that leaf migrates. `withRegistered`'s callback runs with no
231
- // thread hop mid-frame, so routing these writes through their async facades would not move
232
- // them off-loop — it would only add a round-trip.
233
- // #2111 — but the three writes ARE now one. This used to be getAgent(before) + setStatus
230
+ // #1870 S7 — the per-status-frame write runs on the writer thread; this socket's FIFO frame
231
+ // queue keeps its frames ordered across the hop, and the lifecycle ordinal fences it against
232
+ // every other lifecycle writer. #2111 — the three writes are one. This used to be getAgent(before) + setStatus
234
233
  // (guard read + before read + UPDATE in its own BEGIN IMMEDIATE) + markReady (guard read +
235
234
  // UPDATE) + mergeAgentMeta (two reads + UPDATE) + getAgent(after): ~7 row reads and 3 UPDATEs
236
235
  // of one row per frame. `applyAgentLifecycleUpdate` does one guard read, one UPDATE and hands
237
236
  // back the before/after cards the audit calls below need. The flap ledger is different: it is
238
237
  // a cold durable history and must stay ordered with its status edge, so await it within this
239
238
  // socket's existing FIFO frame queue.
240
- const applied = applyAgentLifecycleUpdate(
239
+ const applied = await applyAgentLifecycleUpdateAsync(
241
240
  conn.agentId,
242
241
  { status: frame.payload.agentStatus, ready: frame.payload.ready, meta: frame.payload.meta },
243
242
  guard,
@@ -378,7 +377,7 @@ async function handleRegister(ws: BusWebSocket, frame: RegisterFrame): Promise<v
378
377
  machine: payload.machine,
379
378
  capabilities: payload.capabilities,
380
379
  status: runnerManaged ? "idle" : "online",
381
- ready: true,
380
+ ready: !runnerManaged,
382
381
  instanceId: payload.instanceId,
383
382
  ...(providerCapabilities ? { providerCapabilities } : {}),
384
383
  ...(context ? { context } : {}),
@@ -400,7 +399,7 @@ async function handleRegister(ws: BusWebSocket, frame: RegisterFrame): Promise<v
400
399
  // newly registered agent stale. Fence the install on liveness and clean up the provisional
401
400
  // registration with the same instance/epoch guard used by an established connection's close.
402
401
  if (!liveBusSocketIds.has(ws.data.id)) {
403
- if (payload.agentId && setStatus(payload.agentId, "stale", { instanceId: payload.instanceId, epoch })) {
402
+ if (payload.agentId && (await applyAgentLifecycleUpdateAsync(payload.agentId, { status: "stale" }, { instanceId: payload.instanceId, epoch })).changed) {
404
403
  queueAgentStatusEvent(payload.agentId);
405
404
  }
406
405
  return;
@@ -1,3 +1,7 @@
1
+ import { resolve } from "node:path";
2
+ import { getDeployEventForCommitAsync, deployChangesForCommitsAsync } from "./db/deploy-events-async";
3
+ import type { DeployChanges } from "./db/deploy-events";
4
+ import { runGit } from "./services/git-process";
1
5
  import { VERSION } from "./config";
2
6
  import { reportedGitSha } from "./git-sha";
3
7
  import { routeNotification } from "./notification-router";
@@ -17,6 +21,7 @@ registerDefaultAudienceRules("cd.deploy_live", [audiencePredicates.persistentCoo
17
21
  export interface CdDeployLiveInput {
18
22
  /** Defaults to this process's package version. */
19
23
  version?: string;
24
+ repoRoot?: string;
20
25
  /** Defaults to the running process's attested commit sha (undefined on an npm install). */
21
26
  gitSha?: string;
22
27
  }
@@ -32,7 +37,7 @@ export interface CdDeployLiveInput {
32
37
  * there is no stable identity to key the one-per-deploy dedup on, and this notice is specifically
33
38
  * about the CD-deploy restart glitch that only that deployment model produces.
34
39
  */
35
- export function notifyCdDeployLive(input: CdDeployLiveInput = {}): void {
40
+ export async function notifyCdDeployLive(input: CdDeployLiveInput = {}): Promise<void> {
36
41
  const version = input.version ?? VERSION;
37
42
  const gitSha = input.gitSha ?? reportedGitSha();
38
43
  if (!gitSha) {
@@ -43,6 +48,7 @@ export function notifyCdDeployLive(input: CdDeployLiveInput = {}): void {
43
48
  return;
44
49
  }
45
50
  const shortSha = gitSha.slice(0, 12);
51
+ const contents = await deployNoticeContents(gitSha, input.repoRoot ?? resolve(import.meta.dir, ".."));
46
52
 
47
53
  routeNotification({
48
54
  type: "cd.deploy_live",
@@ -50,10 +56,32 @@ export function notifyCdDeployLive(input: CdDeployLiveInput = {}): void {
50
56
  ttlMs: null,
51
57
  message: {
52
58
  subject: `Humwick ${version} is now running`,
53
- body: `Humwick v${version} (\`${shortSha}\`) is now running after a restart. Any tool-call failures in the last ~1-2 min were the deploy, not an incident — you're good now; retry anything that failed.`,
59
+ body: `Humwick v${version} (\`${shortSha}\`) is now running after a restart. Interrupted tool calls were the deploy, not an incident; retry them.${contents}`,
54
60
  payload: { kind: "cd.deploy-live", version, gitSha },
55
61
  idempotencyKey: `cd-deploy-live:${gitSha}`,
56
62
  replyExpected: false,
57
63
  },
58
64
  });
59
65
  }
66
+
67
+ async function deployNoticeContents(gitSha: string, repoRoot: string): Promise<string> {
68
+ const event = await getDeployEventForCommitAsync(gitSha);
69
+ if (!event) return "";
70
+ let changes: DeployChanges | undefined = event.changes;
71
+ if (changes?.state !== "ready") {
72
+ if (!event.previousSha) return "\nContents unavailable: no previous deployed SHA.";
73
+ const result = await runGit(["-C", repoRoot, "log", "--format=%H%x00%s", `${event.previousSha}..${gitSha}`, "--"], 5000, 1024 * 1024);
74
+ if (result.unavailable || result.exitCode !== 0) return "\nContents unavailable: deploy range could not be read.";
75
+ const commits = result.stdout.trim().split("\n").filter(Boolean).map(line => {
76
+ const [sha, ...subject] = line.split("\x00");
77
+ return { sha: sha!, subject: subject.join("\x00") };
78
+ });
79
+ changes = { state: "ready", ...await deployChangesForCommitsAsync(commits) };
80
+ }
81
+ const lines = changes.tasks.flatMap(task => [
82
+ `T${task.id} ${task.label.replace(/\s+/g, " ")}${task.issues.length ? ` — ${task.issues.map(issue => `${issue.repo}#${issue.externalKey} (${issue.relation ?? "references"})`).join(", ")}` : ""}`,
83
+ ...(task.onDeploy ? [` onDeploy: ${task.onDeploy.replace(/\s+/g, " ")}`] : []),
84
+ ]);
85
+ lines.push(`${changes.unmatchedCommits.length} non-Task commit(s).`);
86
+ return `\n${lines.join("\n")}`;
87
+ }
@@ -1,9 +1,10 @@
1
1
  import { resolveCdFleetAllowlist } from "../scripts/cd-config.ts";
2
2
  import { isCdFleetHostAllowlisted } from "../scripts/cd-fleet-membership.ts";
3
- import { getConfig, setConfig } from "./config-store.ts";
4
- import { createActivityEvent } from "./db/activity.ts";
3
+ import { getConfig } from "./config-store.ts";
4
+ import { setConfigAsync } from "./config-store-write.ts";
5
+ import { createActivityEventAsync } from "./db/activity-async.ts";
5
6
  import { computeFleetConvergenceAsync, type FleetConvergence } from "./fleet-convergence.ts";
6
- import { raiseOperatorAlarm } from "./operator-alarm.ts";
7
+ import { raiseOperatorAlarmAsync } from "./operator-alarm.ts";
7
8
  import { listOrchestratorsAsync } from "./db/orchestrators-async.ts";
8
9
 
9
10
  /** Server-side: a durable alarm per lag episode, and a recovery record when it clears. */
@@ -17,7 +18,7 @@ export async function notifyCdHostLag(convergence?: FleetConvergence, allowlist
17
18
  // Keep the episode's identity while targets advance, until this Host catches up.
18
19
  const since = previous[host.id] ?? host.lag.since;
19
20
  current[host.id] = since;
20
- raiseOperatorAlarm({ clientId: `cd-host-lag:${host.id}:${since}`, source: "cd-host-lag", view: "deploys",
21
+ await raiseOperatorAlarmAsync({ clientId: `cd-host-lag:${host.id}:${since}`, source: "cd-host-lag", view: "deploys",
21
22
  subject: `CD deploy lag on ${host.id}`, dedupeEvent: true,
22
23
  detail: `${host.id} is ${Math.floor(host.lag.ageMs / 60_000)} minutes behind ${status.targetSha.slice(0, 12)}: ${host.deferred?.reason ?? host.reason}`,
23
24
  metadata: { orchestratorId: host.id, since, targetSha: status.targetSha, deferred: host.deferred } });
@@ -29,8 +30,8 @@ export async function notifyCdHostLag(convergence?: FleetConvergence, allowlist
29
30
  if (!host || !isCdFleetHostAllowlisted(host, allowlist)) { delete current[id]; continue; }
30
31
  if (!status.targetSha || host.gitSha !== status.targetSha) { current[id] = since; continue; }
31
32
  delete current[id];
32
- createActivityEvent({ clientId: `cd-host-lag-resolved:${id}:${since}`, kind: "state", view: "deploys",
33
+ await createActivityEventAsync({ clientId: `cd-host-lag-resolved:${id}:${since}`, kind: "state", view: "deploys",
33
34
  title: `CD deploy lag cleared on ${id}`, metadata: { source: "cd-host-lag", orchestratorId: id, since, resolved: true } });
34
35
  }
35
- if (JSON.stringify(current) !== JSON.stringify(previous)) setConfig("cd-host-lag", "alarms", current);
36
+ if (JSON.stringify(current) !== JSON.stringify(previous)) await setConfigAsync("cd-host-lag", "alarms", current);
36
37
  }
@@ -169,7 +169,7 @@ export function ingestCiObservation(observation: CiObservation, now: number = Da
169
169
 
170
170
  /** Gateway-routed form for async-capable polling jobs. The complete observation transition is one
171
171
  * writer operation, so no gateway await exposes a stale watch state to a later incident decision. */
172
- export async function ingestCiObservationAsync(observation: CiObservation): Promise<CiIncidentIngestResult> {
172
+ export async function ingestCiObservationAsync(observation: CiObservation, now?: number): Promise<CiIncidentIngestResult> {
173
173
  const transition = transitionInput(observation);
174
- return formatTransitionResult(await ingestCiObservationTransitionAsync(transition.input), transition.watchKey, transition.fingerprint);
174
+ return formatTransitionResult(await ingestCiObservationTransitionAsync(transition.input, now), transition.watchKey, transition.fingerprint);
175
175
  }
package/src/ci/monitor.ts CHANGED
@@ -12,8 +12,8 @@ import { isPathWithinBase } from "../utils";
12
12
  import { computeCiWatchKey, isFailingJob } from "./incidents.ts";
13
13
  import { ghApi, type GitHubApi } from "../issue-tracker/github.ts";
14
14
  import { resolveCoordinatorAgentId } from "../steward-identity.ts";
15
- import { notifyCiRootMissing, notifyCiRootMissingAsync, type CiRootMissing } from "./root-missing.ts";
16
- import { notifyCiTransition, notifyCiTransitionAsync, type CiTransitionNotifyResult } from "./transition-notify.ts";
15
+ import { notifyCiRootMissing, type CiRootMissing } from "./root-missing.ts";
16
+ import { notifyCiTransition, type CiTransitionNotifyResult } from "./transition-notify.ts";
17
17
  import { workflowNotTriggered } from "./not-triggered.ts";
18
18
  import type { CiJobRef, CiObservation, CiObservationStatus, CiRunRef } from "./provider.ts";
19
19
 
@@ -343,7 +343,7 @@ export interface CiMonitorDeps {
343
343
  rootsForProject: (projectId: string) => ProjectRoot[];
344
344
  loadConfig: (root: ProjectRoot) => CiMonitorConfig | null | Promise<CiMonitorConfig | null>;
345
345
  api: GitHubApi;
346
- notify: typeof notifyCiTransition;
346
+ notify: (...args: Parameters<typeof notifyCiTransition>) => CiTransitionNotifyResult | Promise<CiTransitionNotifyResult>;
347
347
  resolveCoordinator: (rootPath: string) => string | undefined;
348
348
  /** Commits workflows wait on: each watch of their root reports its verdict on them too. */
349
349
  shaWaits: () => Promise<CiShaWait[]>;
@@ -419,7 +419,6 @@ export async function runCiMonitor(input: Partial<CiMonitorDeps> = {}): Promise<
419
419
  readinessMs: 0, historyMs: 0, historyApiCalls: 0,
420
420
  transitions: { went_red: 0, back_green: 0, never_matched: 0, config_mismatch: 0, none: 0 },
421
421
  };
422
- const useAsync = deps.notify === notifyCiTransition && !deps.now;
423
422
  // A repository counts as missing only if no root of it has the config, so a duplicate root cannot flap the notice.
424
423
  const missingRoots = new Map<string, CiRootMissing>();
425
424
  const presentRepositories = new Set<string>();
@@ -464,9 +463,7 @@ export async function runCiMonitor(input: Partial<CiMonitorDeps> = {}): Promise<
464
463
  const observation = await observeGitHubCiMonitorWatch(repository, watch, deps.api, runsPayload);
465
464
  if (deps.signal?.aborted) break;
466
465
  const notificationDeps = { resolveCoordinator: () => deps.resolveCoordinator(root.rootPath), ...(deps.now ? { now: deps.now() } : {}) };
467
- const transition = useAsync
468
- ? await notifyCiTransitionAsync(observation, notificationDeps)
469
- : deps.notify(observation, notificationDeps);
466
+ const transition = await deps.notify(observation, notificationDeps);
470
467
  result.transitions[transition.transition] += 1;
471
468
  // The branch-tip row keys the watch; the same key carries its verdict on each waited commit.
472
469
  const watchKey = computeCiWatchKey(observation);
@@ -495,7 +492,7 @@ export async function runCiMonitor(input: Partial<CiMonitorDeps> = {}): Promise<
495
492
  if (presentRepositories.has(key) || deps.signal?.aborted) continue;
496
493
  const notifyDeps = { resolveCoordinator: () => deps.resolveCoordinator(missing.rootPath), ...(deps.now ? { now: deps.now() } : {}) };
497
494
  try {
498
- if (useAsync ? await notifyCiRootMissingAsync(missing, notifyDeps) : notifyCiRootMissing(missing, notifyDeps)) result.missingRoots += 1;
495
+ if (await notifyCiRootMissing(missing, notifyDeps)) result.missingRoots += 1;
499
496
  } catch (error) {
500
497
  result.errors += 1;
501
498
  console.warn(`ci monitor ${missing.repository} root-missing notice skipped: ${errMessage(error)}`);
@@ -1,4 +1,3 @@
1
- import { markCiRootMissingNotified } from "../db/ci-incidents.ts";
2
1
  import { markCiRootMissingNotifiedAsync } from "../db/ci-incidents-async.ts";
3
2
  import { routeCiIncidentNotification } from "./notifications.ts";
4
3
  import { resolveCoordinator, type CiTransitionNotifyDeps } from "./transition-notify.ts";
@@ -26,10 +25,6 @@ function notify(input: CiRootMissing, deps: Deps, fired: boolean): boolean {
26
25
  return true;
27
26
  }
28
27
 
29
- export function notifyCiRootMissing(input: CiRootMissing, deps: Deps = {}): boolean {
30
- return notify(input, deps, markCiRootMissingNotified(input.repository, deps.now));
31
- }
32
-
33
- export async function notifyCiRootMissingAsync(input: CiRootMissing, deps: Deps = {}): Promise<boolean> {
34
- return notify(input, deps, await markCiRootMissingNotifiedAsync(input.repository));
28
+ export async function notifyCiRootMissing(input: CiRootMissing, deps: Deps = {}): Promise<boolean> {
29
+ return notify(input, deps, await markCiRootMissingNotifiedAsync(input.repository, deps.now));
35
30
  }
@@ -1,7 +1,7 @@
1
1
  import { stewardFallbackTarget } from "../config.ts";
2
2
  import { emitRelayEvent } from "../events.ts";
3
3
  import type { WorkspaceHumanEscalationResult } from "../workspace-human-escalation.ts";
4
- import { ingestCiObservation, ingestCiObservationAsync, type CiIncidentIngestResult } from "./incidents.ts";
4
+ import { ingestCiObservationAsync, type CiIncidentIngestResult } from "./incidents.ts";
5
5
  import {
6
6
  routeCiIncidentNotification,
7
7
  type CiFailedNotificationPayload,
@@ -41,7 +41,7 @@ import type { CiObservation, CiRunRef } from "./provider.ts";
41
41
  export const CI_MAIN_WATCH_COORDINATOR = "cd-watcher";
42
42
 
43
43
  export interface CiTransitionNotifyDeps {
44
- ingest?: (observation: CiObservation, now?: number) => CiIncidentIngestResult;
44
+ ingest?: (observation: CiObservation, now?: number) => CiIncidentIngestResult | Promise<CiIncidentIngestResult>;
45
45
  route?: (input: Parameters<typeof routeCiIncidentNotification>[0]) => RouteCiIncidentNotificationResult;
46
46
  /** Resolve the coordinator/owner agent the relay message addresses. Defaults to the configured
47
47
  * steward fallback, then the system watch actor. */
@@ -65,16 +65,17 @@ export interface CiTransitionNotifyResult {
65
65
  * Detect and (if a flip occurred) alert on a CI red/green transition for a single observed watch.
66
66
  * Idempotent per transition — safe to call every poll tick.
67
67
  */
68
- export function notifyCiTransition(
68
+ export async function notifyCiTransition(
69
69
  observation: CiObservation | null | undefined,
70
70
  deps: CiTransitionNotifyDeps = {},
71
- ): CiTransitionNotifyResult {
71
+ ): Promise<CiTransitionNotifyResult> {
72
72
  if (!observation) return { transition: "none", notified: false, messages: 0 };
73
- const ingest = deps.ingest ?? ingestCiObservation;
73
+ const ingest = deps.ingest ?? ingestCiObservationAsync;
74
74
  const route = deps.route ?? routeCiIncidentNotification;
75
75
  const now = deps.now ?? Date.now();
76
76
 
77
- const result = ingest(observation, now);
77
+ // Notification emission follows the committed transition (one writer op, #1870), never a pre-write observation.
78
+ const result = await ingest(observation, deps.now);
78
79
  emitCiStatus(result);
79
80
 
80
81
  if (result.kind === "opened") {
@@ -155,39 +156,6 @@ export function notifyCiTransition(
155
156
  return { transition: "none", ...(result.kind === "deduped" ? { incidentId: result.incident.id } : {}), notified: false, messages: 0 };
156
157
  }
157
158
 
158
- /** Gateway-routed companion for async maintenance polling. Notification emission follows the
159
- * committed transition, never a pre-write observation. */
160
- export async function notifyCiTransitionAsync(
161
- observation: CiObservation | null | undefined,
162
- deps: Omit<CiTransitionNotifyDeps, "ingest"> = {},
163
- ): Promise<CiTransitionNotifyResult> {
164
- if (!observation) return { transition: "none", notified: false, messages: 0 };
165
- const route = deps.route ?? routeCiIncidentNotification;
166
- const now = deps.now ?? Date.now();
167
- const result = await ingestCiObservationAsync(observation);
168
- emitCiStatus(result);
169
- if (result.kind === "opened") {
170
- const coordinatorAgentId = resolveCoordinator(deps);
171
- const routed = route({ type: "ci.failed", payload: { incidentId: result.incident.id, provider: observation.provider, repository: observation.repository, branch: observation.branch, workflow: observation.workflow, failingHeadSha: observation.headSha, coordinatorAgentId, observedAt: observation.observedAt, ...(observation.conclusion ? { conclusion: observation.conclusion } : {}), failingRunIds: observation.failingRuns.map((run) => run.id), failingJobNames: failingJobNames(observation) }, alertHuman: true, subject: redSubject(observation), body: redBody(observation, result.incident.id) });
172
- return { transition: "went_red", incidentId: result.incident.id, notified: true, messages: routed.messages.length, ...(routed.humanFallback ? { humanFallback: routed.humanFallback } : {}) };
173
- }
174
- if (result.kind === "resolved") {
175
- const coordinatorAgentId = resolveCoordinator(deps);
176
- const routed = route({ type: "ci.recovered", payload: { incidentId: result.incident.id, provider: observation.provider, repository: observation.repository, branch: observation.branch, workflow: observation.workflow, failingHeadSha: result.incident.openedHeadSha, coordinatorAgentId, recoveredAt: now, greenHeadSha: observation.headSha, ...(result.incident.resolution ? { resolution: result.incident.resolution } : {}) }, alertHuman: true, subject: greenSubject(observation), body: greenBody(observation, result.incident.id, result.incident.openedHeadSha) });
177
- return { transition: "back_green", incidentId: result.incident.id, notified: true, messages: routed.messages.length, ...(routed.humanFallback ? { humanFallback: routed.humanFallback } : {}) };
178
- }
179
- if (result.kind === "never_matched") {
180
- const coordinatorAgentId = resolveCoordinator(deps);
181
- const routed = route({ type: "ci.never_matched", payload: { provider: observation.provider, repository: observation.repository, branch: observation.branch, workflow: observation.workflow, coordinatorAgentId, firstSeenAt: result.firstSeenAt, observedAt: observation.observedAt }, alertHuman: true, subject: neverMatchedSubject(observation), body: neverMatchedBody(observation) });
182
- return { transition: "never_matched", notified: true, messages: routed.messages.length, ...(routed.humanFallback ? { humanFallback: routed.humanFallback } : {}) };
183
- }
184
- if (result.kind === "config_mismatch") {
185
- const routed = route(configMismatchRoute(observation, resolveCoordinator(deps), result.unmatchedJobs));
186
- return { transition: "config_mismatch", notified: true, messages: routed.messages.length, ...(routed.humanFallback ? { humanFallback: routed.humanFallback } : {}) };
187
- }
188
- return { transition: "none", ...(result.kind === "deduped" ? { incidentId: result.incident.id } : {}), notified: false, messages: 0 };
189
- }
190
-
191
159
  /** Live CI view (#3011): one `ci.status` event per watch status transition, never per poll. */
192
160
  function emitCiStatus(result: CiIncidentIngestResult): void {
193
161
  if (result.statusChange) emitRelayEvent({ type: "ci.status", source: "server", subject: result.watchKey, data: { ...result.statusChange } });
@@ -0,0 +1,19 @@
1
+ import type { CdPause, CdPauseControl } from "../state-adoption/cd-cutover";
2
+ import { apiRequest, ApiRequestError } from "./_shared";
3
+
4
+ /** The audited maintenance pause API (#1645) shared by local and fleet cutovers (#3470). */
5
+ export const cdWatcherPauses: CdPauseControl = {
6
+ async pause(holder, reason) {
7
+ try {
8
+ return ((await apiRequest("POST", "/api/maintenance/jobs/cd-watcher/pause", { holder, reason, indefinite: true })) as { pause: CdPause }).pause;
9
+ } catch (error) {
10
+ if (error instanceof ApiRequestError && error.status === 409) return (error.responseBody as { pause: CdPause }).pause;
11
+ throw error;
12
+ }
13
+ },
14
+ async current() {
15
+ const { pauses } = (await apiRequest("GET", "/api/maintenance/pauses")) as { pauses: Array<CdPause & { jobId: string }> };
16
+ return pauses.find((pause) => pause.jobId === "cd-watcher") ?? null;
17
+ },
18
+ async resume() { await apiRequest("POST", "/api/maintenance/jobs/cd-watcher/resume"); },
19
+ };
@@ -118,7 +118,7 @@ export async function handleOrchestratorCommand(args: string[]): Promise<void> {
118
118
  // Install the server package too so a real `humwick` bin lands in the
119
119
  // runtime .bin dir (#1421): under bunx there is no global install, so without
120
120
  // this the darwin shim below would point at a bin that does not exist.
121
- const packageCommand = skipInstall ? [] : ["npm", "install", "--prefix", runtimePrefix, `agent-relay-runner@${version}`, `agent-relay-orchestrator@${version}`, `agent-relay-server@${version}`, OPENCODE_HARNESS_PACKAGE];
121
+ const packageCommand = skipInstall ? [] : ["npm", "install", "--prefix", runtimePrefix, `@humwick/runner@${version}`, `@humwick/orchestrator@${version}`, `@humwick/server@${version}`, OPENCODE_HARNESS_PACKAGE];
122
122
  // W1: on macOS, put `humwick` on the interactive PATH (shim + profile
123
123
  // block). Gated to darwin — Linux global-npm installs already get the shim via
124
124
  // the postinstall, and we avoid editing Linux user profiles unnecessarily.