@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
@@ -14,13 +14,26 @@ interface CredentialsDeps {
14
14
  publishRenewalFailure(): void;
15
15
  logRunnerDiagnostic(message: string): void;
16
16
  reportBackgroundRejection(scope: string, status: string, title: string, error: unknown): void;
17
+ /** Recovery gave up: the runner must exit so Relay sees a terminal failure, not a mint loop (#3775). */
18
+ onTerminalAuthFailure(reason: string): void;
17
19
  }
18
20
 
19
21
  const TOKEN_RENEW_LOG_INTERVAL_MS = 5 * 60 * 1000;
20
22
  const TOKEN_RENEW_RETRY_MS = 60_000;
21
- // Debounce reactive token recovery so a burst of 401-ing calls in the same window
22
- // triggers a single re-mint attempt, not one per failing request.
23
- const REACTIVE_TOKEN_RECOVERY_DEBOUNCE_MS = 10_000;
23
+ // #3775 — a successful mint does not prove the hub accepts the runner: an orphaned runner kept
24
+ // getting 401 on its fresh token and re-minted ~2/s. Auth recoveries back off (1 s doubling to
25
+ // 5 min, jittered) and stop after a bounded count; only a quiet spell resets the count.
26
+ export const AUTH_RECOVERY_MAX_ATTEMPTS = 10;
27
+ const AUTH_RECOVERY_BASE_MS = 1_000;
28
+ const AUTH_RECOVERY_MAX_DELAY_MS = 5 * 60_000;
29
+ const AUTH_RECOVERY_QUIET_RESET_MS = 30 * 60_000;
30
+
31
+ /** Delay before auth recovery `attempt` (0-based): the first heals at once, later ones back off with equal jitter. */
32
+ export function authRecoveryDelayMs(attempt: number, random: () => number = Math.random): number {
33
+ if (attempt <= 0) return 0;
34
+ const ceiling = Math.min(AUTH_RECOVERY_MAX_DELAY_MS, AUTH_RECOVERY_BASE_MS * 2 ** (attempt - 1));
35
+ return Math.round(ceiling / 2 + random() * (ceiling / 2));
36
+ }
24
37
 
25
38
  export class Credentials {
26
39
  private currentToken?: string;
@@ -30,7 +43,10 @@ export class Credentials {
30
43
  private tokenRenewTimer?: Timer;
31
44
  private readonly tokenRotation = new TokenRotation();
32
45
  private tokenRenewLastLog?: { key: string; at: number };
33
- private reactiveTokenRecoveryAt?: number;
46
+ private authRecoveryAttempts = 0;
47
+ private authRecoveryLastAt?: number;
48
+ private authRecoveryTimer?: Timer;
49
+ private authRecoveryExhausted = false;
34
50
 
35
51
  constructor(options: { token?: string; tokenJti?: string; tokenProfileId?: string; tokenExpiresAt?: number }, private readonly deps: CredentialsDeps) {
36
52
  this.currentToken = options.token;
@@ -47,6 +63,8 @@ export class Credentials {
47
63
  stop(): void {
48
64
  if (this.tokenRenewTimer) clearTimeout(this.tokenRenewTimer);
49
65
  this.tokenRenewTimer = undefined;
66
+ if (this.authRecoveryTimer) clearTimeout(this.authRecoveryTimer);
67
+ this.authRecoveryTimer = undefined;
50
68
  }
51
69
 
52
70
  // Timer/edge callers cannot await renewal; report an escaping rejection so it cannot
@@ -55,22 +73,35 @@ export class Credentials {
55
73
  void this.renewRuntimeToken().catch((error) => this.deps.reportBackgroundRejection("runtime token renewal", "runtime-token-renewal-failed", "Runtime token renewal aborted", error));
56
74
  }
57
75
 
58
- // The TTL timer cannot detect revocation: recover on auth failure, coalescing a burst.
76
+ // The TTL timer cannot detect revocation: recover on auth failure, coalescing a burst into the
77
+ // one recovery in flight or scheduled.
59
78
  // Orchestrator re-mint heals stale/expired tokens but preserves hard revocation (#484).
60
79
  recoverRuntimeTokenAfterAuthFailure(source: string): void {
61
- if (this.deps.isStopped() || this.tokenRotation.inFlight) return;
80
+ if (this.deps.isStopped() || this.authRecoveryExhausted || this.tokenRotation.inFlight || this.authRecoveryTimer) return;
62
81
  if (!this.isRuntimeTokenRenewable() && !this.canRemintViaOrchestrator()) return;
63
82
  const now = Date.now();
64
- if (this.reactiveTokenRecoveryAt && now - this.reactiveTokenRecoveryAt < REACTIVE_TOKEN_RECOVERY_DEBOUNCE_MS) return;
65
- this.reactiveTokenRecoveryAt = now;
66
- this.deps.logRunnerDiagnostic(`[runner] relay auth failure on ${source}; recovering runtime token`);
67
- this.renewRuntimeTokenInBackground();
83
+ if (this.authRecoveryLastAt !== undefined && now - this.authRecoveryLastAt >= AUTH_RECOVERY_QUIET_RESET_MS) this.authRecoveryAttempts = 0;
84
+ if (this.authRecoveryAttempts >= AUTH_RECOVERY_MAX_ATTEMPTS) {
85
+ this.authRecoveryExhausted = true;
86
+ this.stop();
87
+ this.deps.onTerminalAuthFailure(`runtime-token-auth-failed: relay still rejects the runner (last on ${source}) after ${AUTH_RECOVERY_MAX_ATTEMPTS} token recoveries`);
88
+ return;
89
+ }
90
+ const delayMs = authRecoveryDelayMs(this.authRecoveryAttempts);
91
+ this.authRecoveryAttempts += 1;
92
+ this.authRecoveryLastAt = now;
93
+ this.deps.logRunnerDiagnostic(`[runner] relay auth failure on ${source}; recovering runtime token (attempt ${this.authRecoveryAttempts}/${AUTH_RECOVERY_MAX_ATTEMPTS}${delayMs ? ` in ${Math.ceil(delayMs / 1000)}s` : ""})`);
94
+ if (!delayMs) return this.renewRuntimeTokenInBackground();
95
+ this.authRecoveryTimer = setTimeout(() => {
96
+ this.authRecoveryTimer = undefined;
97
+ this.renewRuntimeTokenInBackground();
98
+ }, delayMs);
68
99
  }
69
100
 
70
101
  scheduleRuntimeTokenRenewal(delayMs?: number): void {
71
102
  if (this.tokenRenewTimer) clearTimeout(this.tokenRenewTimer);
72
103
  this.tokenRenewTimer = undefined;
73
- if (this.deps.isStopped()) return;
104
+ if (this.deps.isStopped() || this.authRecoveryExhausted) return;
74
105
  const canSelfRenew = this.isRuntimeTokenRenewable();
75
106
  const canRemint = this.canRemintViaOrchestrator();
76
107
  // Keep the renewal clock ticking as long as the session can recover its token
@@ -152,7 +183,6 @@ export class Credentials {
152
183
  this.currentTokenProfileId = record.profileId ?? this.currentTokenProfileId;
153
184
  this.currentTokenExpiresAt = record.expiresAt;
154
185
  this.deps.onTokenChanged(token, record);
155
- this.reactiveTokenRecoveryAt = undefined;
156
186
  this.deps.resumeLivenessAndPublishRotation(status, record.jti);
157
187
  this.scheduleRuntimeTokenRenewal();
158
188
  }
@@ -0,0 +1,27 @@
1
+ import type { Message } from "@humwick/sdk";
2
+ import type { HumwickHttpClient } from "@humwick/sdk/http-client";
3
+ import { isNotificationOnlyInput } from "../inbox-carrier";
4
+ import { logger } from "../logger";
5
+
6
+ export async function bootstrapUnreadMessages(http: Pick<HumwickHttpClient, "pollMessages">, agentId: string, enqueue: (message: Message) => void): Promise<void> {
7
+ try {
8
+ const messages = await http.pollMessages({ for: agentId, unread: true, limit: 100, turnDelivery: true });
9
+ for (const message of messages) enqueue(message);
10
+ } catch (error) { logger.error("runner", `inbox bootstrap failed: ${error}`); }
11
+ }
12
+
13
+ /** Keep startup failure visible to each sender, including mail queued before the check (#3751). */
14
+ export async function reportUnavailableMessages(
15
+ http: Pick<HumwickHttpClient, "sendMessage" | "markRead">, agentId: string,
16
+ pending: Map<number, Message>, settled: Set<number>, reason: string,
17
+ ): Promise<void> {
18
+ for (const [id, message] of pending) {
19
+ try {
20
+ if (!isNotificationOnlyInput(message)) await http.sendMessage({ from: agentId, to: message.from, replyTo: id,
21
+ body: reason, idempotencyKey: `provider-startup-${agentId}-${id}`, payload: { kind: "notification" } });
22
+ await http.markRead(id, agentId);
23
+ pending.delete(id);
24
+ settled.add(id);
25
+ } catch (error) { logger.error("runner", `startup failure reply failed: ${error}`); }
26
+ }
27
+ }
@@ -15,7 +15,6 @@ interface LifecycleMilestoneDeps {
15
15
  process(): ManagedProcess | undefined;
16
16
  stopAttachmentCacheSweep(): void;
17
17
  stopTestRunDedupSweep(): void;
18
- stopGateRunLeaseProbe(): void;
19
18
  disposeEmptyTurns(): void;
20
19
  resetEmptyTurns(): void;
21
20
  resetLateClearRecovery(): void;
@@ -92,7 +91,6 @@ export class LifecycleMilestones {
92
91
  private backgroundStopped(): void {
93
92
  this.deps.stopAttachmentCacheSweep();
94
93
  this.deps.stopTestRunDedupSweep();
95
- this.deps.stopGateRunLeaseProbe();
96
94
  // #2389 — before the shutdown await, not after: an escalation or settle timer firing during a
97
95
  // graceful provider shutdown would publish a "produced nothing" verdict about a turn the
98
96
  // shutdown itself ended.
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env -S bun --no-env-file
2
+ import { runCli } from "../index";
3
+
4
+ runCli("claude");
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env -S bun --no-env-file
2
+ import { runCli } from "../index";
3
+
4
+ runCli("codex");
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env -S bun --no-env-file
2
+ import { runCli } from "../index";
3
+
4
+ runCli("opencode");
@@ -375,8 +375,9 @@ export function detectApprovalModeFromArgs(provider: string, defaultArgs: string
375
375
  return detector ? detector([...defaultArgs, ...providerArgs]) : undefined;
376
376
  }
377
377
 
378
- if (import.meta.main) {
379
- main().catch((error) => {
378
+ export function runCli(provider?: string): void {
379
+ const argv = provider ? [...process.argv.slice(0, 2), provider, ...process.argv.slice(2)] : process.argv;
380
+ main(argv).catch((error) => {
380
381
  // A fatal startup failure (e.g. the runner could not register with the relay within the
381
382
  // deadline) must exit non-zero, not hang or swallow — the orchestrator's exit detection
382
383
  // then reports it and the spawn-parent is notified instead of waiting on a silent zombie.
@@ -384,3 +385,5 @@ if (import.meta.main) {
384
385
  exitRunner(1, `runner fatal: ${errMessage(error)}`);
385
386
  });
386
387
  }
388
+
389
+ if (import.meta.main) runCli();
@@ -0,0 +1,34 @@
1
+ import { errMessage } from "@humwick/sdk";
2
+ import { checkProviderAuth } from "@humwick/sdk/provider-probe";
3
+ import { selectProviderConfigEnv } from "@humwick/sdk/child-env";
4
+ import type { ProviderAdapter, ProviderStatusEvent } from "./adapter";
5
+ import { logger } from "./logger";
6
+ import { hostSourcesFor } from "./host-asset-plan";
7
+
8
+ /** All real adapters share the managed-spawn login check, including replacements (#3751). */
9
+ export function withProviderSetup(adapter: ProviderAdapter): ProviderAdapter {
10
+ const spawn = adapter.spawn.bind(adapter);
11
+ adapter.spawn = async (config) => {
12
+ const env = selectProviderConfigEnv(config.providerConfig.env, hostSourcesFor(config).settings);
13
+ const auth = await checkProviderAuth(config.provider, undefined, env, config.providerConfig.command);
14
+ if (!auth.ok) throw new Error(auth.reason);
15
+ return spawn(config);
16
+ };
17
+ return adapter;
18
+ }
19
+
20
+ export function providerStartupFailure(error: unknown): ProviderStatusEvent {
21
+ const message = `Provider startup failed: ${errMessage(error)}`;
22
+ return { status: "error", reason: "provider-turn", providerState: {
23
+ state: "blocked", terminal: true, reason: "providerSetupRequired", label: "Provider setup required", message,
24
+ recommendedAction: "Complete the provider login and first-run setup on this host, then restart the runner.",
25
+ } };
26
+ }
27
+
28
+ export async function attemptProviderStartup(start: () => Promise<void>, failed: (status: ProviderStatusEvent) => void): Promise<void> {
29
+ try { await start(); }
30
+ catch (error) {
31
+ logger.error("startup", errMessage(error));
32
+ failed(providerStartupFailure(error));
33
+ }
34
+ }
@@ -1,3 +1,4 @@
1
+ import { withProviderSetup } from "./provider-setup";
1
2
  import { ClaudeApiAdapter } from "./adapters/claude-api";
2
3
  import type { LaunchMode, SpawnProvider } from "@humwick/sdk";
3
4
  import { getAllManifests, supportedLaunchModesForProvider } from "@humwick/providers";
@@ -31,5 +32,5 @@ export function createProviderAdapter(provider: SpawnProvider, providerArgs: str
31
32
  if (launchMode && !supportedLaunchModesForProvider(provider).includes(launchMode)) {
32
33
  throw new Error(`${provider} does not support launchMode=${launchMode}`);
33
34
  }
34
- return registration.createAdapter(providerArgs, launchMode);
35
+ return withProviderSetup(registration.createAdapter(providerArgs, launchMode));
35
36
  }
@@ -1,3 +1,5 @@
1
+ import { attemptProviderStartup } from "./provider-setup";
2
+ import { bootstrapUnreadMessages, reportUnavailableMessages } from "./agent-runner/inbox";
1
3
  import { writeChildEnv } from "@humwick/sdk/env";
2
4
  import { normalizeDeliverResult } from "./delivery-result";
3
5
  import { TokenUsageReportCoverage } from "./agent-runner/token-usage-coverage";
@@ -34,7 +36,6 @@ import { AttachmentCacheSweep } from "./attachment-cache-sweep";
34
36
  import { resolveTestRunRootPid, TEST_RUN_DEDUP_SWEEP_MS, TestRunDedupSweep } from "./test-run-dedup";
35
37
  import { cachedGateRunCommandsForCwd } from "./gate-run-config";
36
38
  import { AreaMemoryTracker } from "./area-memory";
37
- import { GateRunLeaseProbe } from "./gate-run-lease-probe";
38
39
  import { ClaimTracker } from "./claim-tracker";
39
40
  import { ProviderQuotaHarvest } from "./adapters/provider-quota-harvest";
40
41
  import { agentShellLaunchEnv } from "./adapters/project-shell";
@@ -224,7 +225,6 @@ export class AgentRunner {
224
225
  private livenessReconcileTimer?: Timer;
225
226
  private readonly attachmentCacheSweep: AttachmentCacheSweep;
226
227
  private readonly testRunDedupSweep: TestRunDedupSweep;
227
- private readonly gateRunLeaseProbe: GateRunLeaseProbe;
228
228
  private processStartedAt = 0;
229
229
  private providerSessionId = crypto.randomUUID();
230
230
  // Last transcript path seen this session — used by end-of-session Insights (#184)
@@ -363,7 +363,7 @@ export class AgentRunner {
363
363
  });
364
364
  this.lifecycleMilestones = new LifecycleMilestones({
365
365
  options: this.options, agentId: this.agentId, process: () => this.process,
366
- stopAttachmentCacheSweep: () => this.attachmentCacheSweep.stop(), stopTestRunDedupSweep: () => this.testRunDedupSweep.stop(), stopGateRunLeaseProbe: () => this.gateRunLeaseProbe.stop(),
366
+ stopAttachmentCacheSweep: () => this.attachmentCacheSweep.stop(), stopTestRunDedupSweep: () => this.testRunDedupSweep.stop(),
367
367
  disposeEmptyTurns: () => this.emptyTurns.dispose(), resetEmptyTurns: () => this.emptyTurns.reset(), resetLateClearRecovery: () => { this.lateClearRecovery.reset(); this.continuationFence.reset(); },
368
368
  clearLifecycleAction: () => this.lifecycle.setLifecycleAction(undefined), setOffline: () => this.claims.setTerminalStatus("offline"), publishStatus: () => this.publishStatus(),
369
369
  stopDrainTimer: () => { if (this.drainTimer) clearTimeout(this.drainTimer); this.drainTimer = undefined; }, stopSubmitRecoveryPoll: () => this.stopSubmitRecoveryPoll(),
@@ -409,14 +409,12 @@ export class AgentRunner {
409
409
  const sessionProviderPid = () => resolveTestRunRootPid(this.process, (managedProcess) => this.options.adapter.resolveBusyPid?.(managedProcess));
410
410
  // #2461 — ancestry alone made this sweep a no-op for `relay_run` gate runs (a disjoint tree
411
411
  // under the orchestrator). It now also owns anything env-tagged with this agent's id, classifies
412
- // against the repo's CONFIGURED gate commands, and defers to the launch-time lease holder. The
412
+ // against the repo's CONFIGURED gate commands, and reads live leases during each sweep. The
413
413
  // config read is cached for the session — it is a repo file, so re-reading it every 15s for the
414
- // agent's whole life buys nothing. All three helpers live in their own leaves.
415
- this.gateRunLeaseProbe = new GateRunLeaseProbe(this.agentId, TEST_RUN_DEDUP_SWEEP_MS);
414
+ // agent's whole life buys nothing.
416
415
  this.testRunDedupSweep = new TestRunDedupSweep({
417
416
  rootPid: sessionProviderPid, agentId: () => this.agentId,
418
417
  gateCommands: () => cachedGateRunCommandsForCwd(this.options.cwd),
419
- leaseHolderPid: () => this.gateRunLeaseProbe.pid(),
420
418
  protectedPids: () => [process.pid], log: (message) => this.logRunnerDiagnostic(message),
421
419
  });
422
420
  this.context.configureLogger();
@@ -450,6 +448,7 @@ export class AgentRunner {
450
448
  },
451
449
  logRunnerDiagnostic: (message) => this.logRunnerDiagnostic(message),
452
450
  reportBackgroundRejection: (scope, status, title, error) => this.reportBackgroundRejection(scope, status, title, error),
451
+ onTerminalAuthFailure: (reason) => { logger.error("runner", `exiting: ${reason}`); this.options.onProviderExit?.(1, reason); },
453
452
  });
454
453
  const token = this.credentials.current();
455
454
  if (token) publishRuntimeTokenForCli(token, this.credentials.expiresAt, (message) => this.logRunnerDiagnostic(message));
@@ -733,16 +732,17 @@ export class AgentRunner {
733
732
  this.ensureScratch();
734
733
  void sweepRunnerStaleScratch({ listAgents: () => this.http.listAgents(), agentId: this.agentId, cwd: this.options.cwd, fallbackBaseDir: orchestratorBaseDirFromEnv(), log: (message) => this.logRunnerDiagnostic(message) });
735
734
  this.scheduleAttachmentCacheSweep();
736
- this.adoptProcess(await this.spawnProvider());
737
- this.registration.writeRunnerInfoFile();
738
- await this.promptCommands.startLaunchSeededTurn();
739
- this.publishStatus();
740
- await this.promptCommands.deliverInitialPrompt();
735
+ await attemptProviderStartup(async () => {
736
+ this.adoptProcess(await this.spawnProvider());
737
+ this.registration.writeRunnerInfoFile();
738
+ await this.promptCommands.startLaunchSeededTurn();
739
+ this.publishStatus();
740
+ await this.promptCommands.deliverInitialPrompt();
741
+ }, (status) => this.setProviderStatus(status));
741
742
  await this.bootstrapUnreadMessages();
742
743
  this.taskClaims.start();
743
744
  this.startHttpLiveness();
744
745
  this.testRunDedupSweep.start();
745
- this.gateRunLeaseProbe.start();
746
746
  this.credentials.scheduleRuntimeTokenRenewal();
747
747
  this.scheduleDrain();
748
748
  }
@@ -790,6 +790,7 @@ export class AgentRunner {
790
790
  }
791
791
 
792
792
  private handleBusRegistered(): void {
793
+ if (this.process) this.publishStatus();
793
794
  void this.nativeSelfResume.reconcile((id) => this.http.request("GET", `/api/commands/${encodeURIComponent(id)}`)).catch((error) => this.sessionLog(`compact settlement reconcile: ${errMessage(error)}`));
794
795
  // #2973 — a re-register follows a relay restart (a deploy): re-list so changed tools reach the agent.
795
796
  void this.proxy?.refreshTools().catch(() => {}); this.effort.trigger();
@@ -941,14 +942,7 @@ export class AgentRunner {
941
942
  this.scheduleDrain();
942
943
  }
943
944
 
944
- private async bootstrapUnreadMessages(): Promise<void> {
945
- try {
946
- const messages = await this.http.pollMessages({ for: this.agentId, unread: true, limit: 100, turnDelivery: true });
947
- for (const message of messages) this.enqueueMessage(message);
948
- } catch (error) {
949
- logger.error("runner", `inbox bootstrap failed: ${error}`);
950
- }
951
- }
945
+ private bootstrapUnreadMessages = () => bootstrapUnreadMessages(this.http, this.agentId, (message) => this.enqueueMessage(message));
952
946
 
953
947
  private scheduleDrain(delayMs = 0): void {
954
948
  if (this.lifecycle.stopped || this.drainTimer || this.deliverySubmitHold || this.submitResendInFlight) return;
@@ -1363,7 +1357,8 @@ export class AgentRunner {
1363
1357
  private async drainMessages(): Promise<void> {
1364
1358
  if (this.lifecycle.stopped || this.delivering || this.deliverySubmitHold || this.submitResendInFlight || this.pendingMessages.size === 0) return;
1365
1359
  if (!this.process) {
1366
- this.scheduleDrain(500);
1360
+ if (this.terminalFailure) await reportUnavailableMessages(this.http, this.agentId, this.pendingMessages, this.settledMessageIds, this.terminalFailure.message);
1361
+ if (this.pendingMessages.size) this.scheduleDrain(500);
1367
1362
  return;
1368
1363
  }
1369
1364
  this.delivering = true;
@@ -2822,7 +2817,7 @@ export class AgentRunner {
2822
2817
  ?? (activeWork.length === 0 ? this.emptyTurns.providerState() ?? null : null);
2823
2818
  const published = this.publishRunnerStatus({
2824
2819
  agentStatus,
2825
- ready: agentStatus !== "offline" && !this.lifecycle.stopped && !teardownLifecycle && !this.startupPaneDialog && !this.deliverySubmitHold,
2820
+ ready: Boolean(this.process) && agentStatus !== "offline" && !this.lifecycle.stopped && !teardownLifecycle && !this.startupPaneDialog && !this.deliverySubmitHold,
2826
2821
  meta: {
2827
2822
  ...runnerIdentityMeta(this.options, { tmuxSession: providerTerminalSession(this.process), tmuxSocket: providerTerminalSocket(this.process), auth: { profileId: this.credentials.profileId, jti: this.credentials.jti, expiresAt: this.credentials.expiresAt } }, "status"),
2828
2823
  lifecycleAction: this.lifecycle.lifecycleAction ?? null,
@@ -105,6 +105,7 @@ export function parseRunnerCliArgs(
105
105
  else if (arg === "--model-record-provider-model" && ownArgs[i + 1]) modelRecordRaw.providerModel = ownArgs[++i];
106
106
  else if (arg === "--model-record-qualified" && ownArgs[i + 1]) modelRecordRaw.qualifiedModel = ownArgs[++i];
107
107
  else if (arg === "--model-record-effort" && ownArgs[i + 1]) modelRecordRaw.effort = ownArgs[++i];
108
+ else if (arg === "--model-record-efforts" && ownArgs[i + 1]) { try { modelRecordRaw.efforts = JSON.parse(ownArgs[++i]!); } catch {} }
108
109
  else if (arg === "--model-record-window" && ownArgs[i + 1]) {
109
110
  const parsed = Number(ownArgs[++i]);
110
111
  if (Number.isSafeInteger(parsed) && parsed > 0) modelRecordRaw.contextWindowTokens = parsed;
@@ -143,5 +144,5 @@ export function parseRunnerCliArgs(
143
144
  }
144
145
 
145
146
  export function runnerHelp(provider: string): string {
146
- return `${provider}-relay [--headless] [--launch-mode interactive|headless] [--model ALIAS] [--effort LEVEL] [--context-window-tokens N] [--model-record-alias A --model-record-provider-model M --model-record-qualified Q --model-record-effort E --model-record-window N --model-record-max-output N --model-record-threshold JSON] [--rig NAME] [--profile NAME] [--cwd PATH] [--relay-url URL] [--approval MODE] [--label NAME] [--agent-id ID] [--prompt TEXT] [--system-prompt-append TEXT] [--tags a,b] [--caps x,y] [-- provider-args...]`;
147
+ return `${provider}-humwick [--headless] [--launch-mode interactive|headless] [--model ALIAS] [--effort LEVEL] [--context-window-tokens N] [--model-record-alias A --model-record-provider-model M --model-record-qualified Q --model-record-effort E --model-record-window N --model-record-max-output N --model-record-threshold JSON] [--rig NAME] [--profile NAME] [--cwd PATH] [--relay-url URL] [--approval MODE] [--label NAME] [--agent-id ID] [--prompt TEXT] [--system-prompt-append TEXT] [--tags a,b] [--caps x,y] [-- provider-args...]`;
147
148
  }
@@ -1,41 +1,14 @@
1
1
  import { relayEnv } from "@humwick/sdk/env";
2
- // #2142 — per-agent test-run dedup ("latest wins").
3
- //
4
- // Workers run `bun run test:affected` after every file change. When a change touches something
5
- // broad, a single run takes 10-15 minutes — and the agent starts the NEXT run long before the
6
- // previous one finishes. The stacked runs saturate the host: each one holds a gate slot, forks
7
- // its own shard processes, and the oldest run's result is thrown away the moment it lands.
8
- // Instructions don't fix this (an agent rationalizes around them), so the bound has to be
9
- // structural and live BELOW the agent: the runner watches its own session's process tree and,
10
- // whenever it sees more than one independent test run, kills every run except the newest.
11
- //
12
- // Scoping is per-agent BY CONSTRUCTION: the sweep only ever looks at processes it can PROVE belong
13
- // to this agent — descendants of this session's provider process (the tmux pane pid / direct child
14
- // pid the adapter already resolves for busy-liveness), plus processes carrying this agent's
15
- // AGENT_RELAY_ID in their environment. A sibling agent's runs satisfy neither test, so this can
16
- // never fratricide the fleet the way a host-wide `pkill -f` would (#753). It is also
17
- // provider-agnostic — nothing here knows whether the tree belongs to Claude or Codex.
18
- //
19
- // #2461 — the env-tag half of that ownership test is new, and it is why this file exists at all
20
- // after the lease landed. Ancestry ALONE made this a no-op for the case the issue reports: a
21
- // `relay_run` gate run executes under the ORCHESTRATOR daemon, a tree that shares no ancestor with
22
- // the provider pane, so a descendant-scoped sweep could never see it. The orchestrator now stamps
23
- // AGENT_RELAY_ID onto those children, which is what makes them attributable here.
24
- //
25
- // This sweep is now the BACKSTOP, not the primary mechanism. Kill-replace happens at LAUNCH, in
26
- // sdk/src/gate-run-lease.ts, entered from `scripts/gate-slot` and the orchestrator. What is left
27
- // for the sweep is the residual shape neither front door sees: a gate run started straight from the
28
- // provider shell in a repo that does not route through `gate.sh`. It is 15s-late by construction —
29
- // that is the documented coverage gap, tracked for a launch-exact PATH shim in the follow-up issue.
30
- // When a live lease holder exists it is AUTHORITATIVE: the sweep keeps it and kills the rest, so
31
- // the two mechanisms can never disagree about which run survives.
2
+ // #3754 — the lease owns launch-time replacement. The sweep only deduplicates
3
+ // unleased runs with the same captured checkout/revision; ancestry proves ownership,
4
+ // but cannot prove two commands tested the same tree. Live leases protect whole runs.
32
5
  import { execFile } from "node:child_process";
33
6
  import { readFileSync, readdirSync } from "node:fs";
34
7
  import { promisify } from "node:util";
35
8
  import { errMessage } from "@humwick/sdk";
36
9
  import { killPid, waitForPidsExit } from "@humwick/sdk/process-utils";
37
10
  import { classifyGateRun, isTestRunCommand, type GateRunCommands } from "@humwick/sdk/gate-run-classify";
38
- import { GATE_RUN_AGENT_ENV } from "@humwick/sdk/gate-run-lease";
11
+ import { GATE_RUN_AGENT_ENV, GATE_RUN_CONTEXT_ENV, GATE_RUN_HELD_ENV, readLiveGateRunHolderPids } from "@humwick/sdk/gate-run-lease";
39
12
  import type { ManagedProcess } from "./adapter";
40
13
 
41
14
  // Re-exported so the sweep and both lease front doors share ONE definition of "is this a test run".
@@ -215,6 +188,9 @@ export interface TestRunDedupScope {
215
188
  /** Classifier override. Defaults to the argv-shape heuristic; the sweep passes a classifier bound
216
189
  * to the repo's CONFIGURED gate commands so a foreign runner (`pnpm tier:run …`) is matched too. */
217
190
  isGateRun?: (args: string) => boolean;
191
+ /** Launch-time checkout/revision identity. Missing identity cannot prove a duplicate. */
192
+ contextByPid?: ReadonlyMap<number, string>;
193
+ leasedPids?: readonly number[];
218
194
  }
219
195
 
220
196
  export function planTestRunDedup(
@@ -278,29 +254,34 @@ export function planTestRunDedup(
278
254
  }
279
255
  if (runRoots.length < 2) return EMPTY_PLAN;
280
256
 
281
- // Lower elapsed = started later = the run the agent actually wants. `ps etime` only has
282
- // second precision, though, and a Codex agent can launch two commands within that second.
283
- // Pids are allocated in start order within the host pid namespace, so the higher pid is the
284
- // newer root for this otherwise-unresolvable tie. Without this deterministic tie-breaker, two
285
- // same-second delivery runs remain tied forever and #2142 fails to bound the exact burst it
286
- // was added for. The scope is still this session's process tree only.
287
- // A live lease holder wins outright — it is the run the launch-time mechanism already chose. Only
288
- // when there is none (an ungated run in a repo without gate.sh) does newest-wins decide.
289
- const leaseKeep = scope.keepPid !== undefined && runRoots.includes(scope.keepPid) ? scope.keepPid : undefined;
290
- const keptPid = leaseKeep ?? runRoots.reduce((newest, pid) => {
291
- const candidate = byPid.get(pid)!;
292
- const current = byPid.get(newest)!;
293
- return candidate.elapsedSeconds < current.elapsedSeconds
294
- || (candidate.elapsedSeconds === current.elapsedSeconds && pid > newest)
295
- ? pid
296
- : newest;
297
- });
298
- const newestElapsed = byPid.get(keptPid)!.elapsedSeconds;
299
- const staleRoots = runRoots
300
- // With a lease holder the age comparison is meaningless — the holder may be the OLDER run, and
301
- // it still wins. Without one, keep the original guard so a mis-ranked row is never killed.
302
- .filter((pid) => pid !== keptPid && (leaseKeep !== undefined || byPid.get(pid)!.elapsedSeconds >= newestElapsed))
303
- .sort((a, b) => a - b);
257
+ // A lease may belong to a child of the outer shell. Spare the WHOLE run, including
258
+ // ancestors and descendants, rather than signalling its owner indirectly (#3754).
259
+ const leased = new Set(scope.leasedPids ?? []);
260
+ const groups = new Map<string, number[]>();
261
+ const leasedRoots = new Set<number>();
262
+ for (const pid of runRoots) {
263
+ if (subtree(pid).some((child) => leased.has(child))) leasedRoots.add(pid);
264
+ const context = scope.contextByPid ? scope.contextByPid.get(pid) : "legacy";
265
+ if (!context) continue;
266
+ const group = groups.get(context) ?? [];
267
+ group.push(pid);
268
+ groups.set(context, group);
269
+ }
270
+ const staleRoots: number[] = [];
271
+ let keptPid: number | undefined;
272
+ for (const group of groups.values()) {
273
+ if (group.length < 2) continue;
274
+ const leaseKeep = scope.keepPid !== undefined && group.includes(scope.keepPid) ? scope.keepPid : undefined;
275
+ const newest = group.find((pid) => leasedRoots.has(pid)) ?? leaseKeep ?? group.reduce((current, pid) => {
276
+ const candidate = byPid.get(pid)!;
277
+ const prior = byPid.get(current)!;
278
+ return candidate.elapsedSeconds < prior.elapsedSeconds
279
+ || (candidate.elapsedSeconds === prior.elapsedSeconds && pid > current) ? pid : current;
280
+ });
281
+ keptPid = newest;
282
+ staleRoots.push(...group.filter((pid) => pid !== newest && !leasedRoots.has(pid)));
283
+ }
284
+ staleRoots.sort((a, b) => a - b);
304
285
  if (staleRoots.length === 0) return EMPTY_PLAN;
305
286
 
306
287
  const protectedSet = new Set([rootPid, ...protectedPids]);
@@ -334,6 +315,17 @@ export async function readProcessTable(timeoutMs: number = PROCESS_TABLE_TIMEOUT
334
315
  return stdout;
335
316
  }
336
317
 
318
+ /** Only launch-time metadata can distinguish revisions; today's HEAD cannot (#3754). */
319
+ export function readTestRunContext(pid: number): { context?: string; leased?: boolean } {
320
+ try {
321
+ const env = Object.fromEntries(readFileSync(`/proc/${pid}/environ`, "utf8").split("\0").map((entry) => {
322
+ const equals = entry.indexOf("=");
323
+ return [entry.slice(0, equals), entry.slice(equals + 1)];
324
+ }));
325
+ return { context: relayEnv(env, GATE_RUN_CONTEXT_ENV), leased: !!relayEnv(env, GATE_RUN_HELD_ENV) };
326
+ } catch { return {}; }
327
+ }
328
+
337
329
  export interface TestRunDedupDeps {
338
330
  /** Resolve this session's provider pid. undefined ⇒ ancestry proves nothing; the env tag may. */
339
331
  rootPid: () => number | undefined;
@@ -344,6 +336,8 @@ export interface TestRunDedupDeps {
344
336
  gateCommands?: () => GateRunCommands;
345
337
  /** The live lease holder pid, when one exists — authoritative over newest-wins. */
346
338
  leaseHolderPid?: () => number | undefined;
339
+ readRunContext?: (pid: number) => { context?: string; leased?: boolean };
340
+ liveLeasePids?: () => Promise<number[]>;
347
341
  collectOwnedPids?: (agentId: string) => Promise<number[]>;
348
342
  readProcessTable?: () => Promise<string>;
349
343
  kill?: (pid: number, signal: "SIGTERM" | "SIGKILL") => void;
@@ -374,8 +368,17 @@ export async function runTestRunDedupSweep(deps: TestRunDedupDeps): Promise<numb
374
368
  }
375
369
 
376
370
  const gateCommands = deps.gateCommands?.() ?? {};
377
- const plan = planTestRunDedup(parseProcessTable(table), rootPid, deps.protectedPids?.() ?? [], {
378
- ownedPids,
371
+ const rows = parseProcessTable(table);
372
+ const contextByPid = new Map<number, string>();
373
+ const leasedPids = await (deps.liveLeasePids ?? (() => agentId ? readLiveGateRunHolderPids(agentId) : Promise.resolve([])))();
374
+ for (const row of rows) {
375
+ if (classifyGateRun(row.args, gateCommands) === null) continue;
376
+ const context = (deps.readRunContext ?? readTestRunContext)(row.pid);
377
+ if (context.context) contextByPid.set(row.pid, context.context);
378
+ if (context.leased) leasedPids.push(row.pid);
379
+ }
380
+ const plan = planTestRunDedup(rows, rootPid, deps.protectedPids?.() ?? [], {
381
+ ownedPids, contextByPid, leasedPids,
379
382
  keepPid: deps.leaseHolderPid?.(),
380
383
  isGateRun: (args) => classifyGateRun(args, gateCommands) !== null,
381
384
  });
@@ -172,6 +172,14 @@ export function claimCanonicalDeployLease(input: { commitSha: string; version: s
172
172
  return tryBeginDeployEvent(input);
173
173
  }
174
174
 
175
+ /** A scoped release step borrows its parent's lease, including when deploying a rollback SHA (#3470). */
176
+ export function attachCutoverDeployLease(id: string): DeployEvent {
177
+ openCanonicalDeployLedger();
178
+ const event = getDeployEvent(id);
179
+ if (!event || event.status !== "pending" || !event.initiatedBy.startsWith("humwick-cutover:")) throw new Error(`Not a pending cutover lease: ${id}`);
180
+ return event;
181
+ }
182
+
175
183
  /**
176
184
  * #2105 — stamp THIS driver process onto the lease it just claimed, so cd-watcher's reconcile can
177
185
  * reclaim a stranded row on executor liveness (minutes) instead of the 2h lease TTL. Best-effort by
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env bun
2
- import { relayEnv } from "@humwick/sdk/env";
2
+ import { relayEnv, writeChildEnv } from "@humwick/sdk/env";
3
3
  /**
4
4
  * #2461 — the gate-run lease CLI. ONE implementation of the per-agent single-run invariant.
5
5
  *
@@ -49,6 +49,8 @@ import { readFileSync } from "node:fs";
49
49
  import { join } from "node:path";
50
50
  import {
51
51
  claimGateRunLease,
52
+ gateRunContext,
53
+ GATE_RUN_CONTEXT_ENV,
52
54
  gateRunLeaseDir,
53
55
  gateRunLeaseEnabled,
54
56
  gateRunWasSuperseded,
@@ -136,17 +138,18 @@ export async function claim(options: {
136
138
  // here, which is one read and therefore no disagreement.
137
139
  const category = carried ?? classifyGateRun(options.command, gateCommandsForCwd(options.cwd));
138
140
  if (!category) return { state: "skip", dir: "", epoch: "" };
139
- // The directory is a pure function of the agent id and the environment, so it cannot drift
140
- // between two calls in the same shell — but a caller that names one is asserting which snapshot
141
- // it decided in, and an assertion that does not hold must never publish.
142
- if (options.dir && options.dir !== gateRunLeaseDir(agentId, env)) return { state: "skip", dir: "", epoch: "" };
141
+ // Preflight captured HEAD. Reuse that identity even if the checkout moves before claim (#3754).
142
+ const carriedContext = options.dir?.split("/").at(-1);
143
+ const context = options.dir ? (/^[a-f0-9]{64}$/.test(carriedContext ?? "") ? carriedContext : undefined) : gateRunContext(options.cwd);
144
+ const scopedEnv = writeChildEnv({ ...env }, { [GATE_RUN_CONTEXT_ENV]: context });
145
+ if (options.dir && options.dir !== gateRunLeaseDir(agentId, scopedEnv)) return { state: "skip", dir: "", epoch: "" };
143
146
 
144
147
  const handle = await claimGateRunLease({
145
148
  agentId,
146
149
  category,
147
150
  command: options.command,
148
151
  selfPid: options.pid,
149
- env,
152
+ env: scopedEnv,
150
153
  ...(options.epoch ? { epoch: options.epoch } : {}),
151
154
  ...clampedLockWait(env),
152
155
  ...(options.log ? { log: options.log } : {}),
@@ -190,7 +193,7 @@ export function preflight(options: { cwd: string; command: string; env?: Record<
190
193
  if ((relayEnv(env, GATE_RUN_HELD_ENV) ?? "").trim()) return { state: "nested", dir: gateRunLeaseDir(agentId, env), category: "" };
191
194
  const category = classifyGateRun(options.command, gateCommandsForCwd(options.cwd));
192
195
  if (!category) return { state: "skip", dir: "", category: "" };
193
- return { state: "claim", dir: gateRunLeaseDir(agentId, env), category };
196
+ return { state: "claim", dir: gateRunLeaseDir(agentId, env, options.cwd), category };
194
197
  }
195
198
 
196
199
  /** A carried category is honoured only if it is one this protocol actually has. Anything else is
package/scripts/gate.sh CHANGED
@@ -1,6 +1,10 @@
1
1
  #!/usr/bin/env sh
2
2
  # Explicit override, managed native delegate, then the bundled compatibility launcher.
3
+ # CI runs this checkout's own launcher: the managed delegate execs the host's DEPLOYED runtime,
4
+ # so a broken deploy would turn every CI run red (T7319). Both share the host admission ledger.
3
5
  GS="${GATE_SLOT_BIN:-}"
6
+ BUNDLED="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)/gate-slot"
7
+ [ -x "$GS" ] || { [ "${GITHUB_ACTIONS:-}" = true ] && GS="$BUNDLED"; }
4
8
  [ -x "$GS" ] || GS="$HOME/.local/bin/gate-slot"
5
- [ -x "$GS" ] || GS="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)/gate-slot"
9
+ [ -x "$GS" ] || GS="$BUNDLED"
6
10
  exec "$GS" "$@"