@humwick/server 0.130.1 → 0.130.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (811) hide show
  1. package/README.md +5 -0
  2. package/bundled/prompts/manifest.json +1 -0
  3. package/bundled/prompts/task.hydration.md +1 -1
  4. package/bundled/roles/builder.md +1 -1
  5. package/docs/guides/sub-coordinators.md +12 -5
  6. package/docs/guides/tools-administration.md +4 -0
  7. package/docs/guides/tools-coordination.md +2 -0
  8. package/docs/guides/tools-tasks.md +8 -0
  9. package/docs/guides/workflows.md +74 -0
  10. package/docs/openapi.json +486 -4
  11. package/package.json +10 -18
  12. package/public/assets/{MessageBubble-BWpoBjl4.js → MessageBubble-Bq9kr21N.js} +2 -2
  13. package/public/assets/{PlanGraphPanel-C0auck3n.js → PlanGraphPanel-Dqiroy06.js} +2 -2
  14. package/public/assets/{TraceView-DBnOx7AQ.js → TraceView-DYU962-H.js} +2 -2
  15. package/public/assets/{VaultDecisionCard-0kp2oNBC.js → VaultDecisionCard-DhPfzioE.js} +2 -2
  16. package/public/assets/{activity-yLztXEBt.js → activity-DzCcQTee.js} +2 -2
  17. package/public/assets/{agent-lineage-BbJDR1dW.js → agent-lineage-SgDt9i3T.js} +2 -2
  18. package/public/assets/agent-liveness-Bq-s73jW.js +2 -0
  19. package/public/assets/{agent-name-CTZBrSD9.js → agent-name-CcY3Aijy.js} +2 -2
  20. package/public/assets/agent-profiles-cxtRBn6O.js +2 -0
  21. package/public/assets/{agent-project-badge-9TqzUUYE.js → agent-project-badge-DGYbIhaI.js} +2 -2
  22. package/public/assets/{agent-tools-D4n40pn8.js → agent-tools-DHbftV0N.js} +2 -2
  23. package/public/assets/{agent-type-icon-D6FUarOf.js → agent-type-icon-CY113sp-.js} +2 -2
  24. package/public/assets/{agents-BYDslkJc.js → agents-BPzY7IlQ.js} +2 -2
  25. package/public/assets/{analytics-B-XSbEMC.js → analytics-DENFPv_a.js} +2 -2
  26. package/public/assets/{artifact-viewer-DdWIfV6M.js → artifact-viewer-KRvSc7sl.js} +2 -2
  27. package/public/assets/{artifacts-DPN1q4pA.js → artifacts-C9gSmL3s.js} +2 -2
  28. package/public/assets/{artifacts-oBOqks-2.js → artifacts-DT-HkI5O.js} +2 -2
  29. package/public/assets/{automation-4KcRFXEu.js → automation-BsmjQhKD.js} +2 -2
  30. package/public/assets/{backlog-Cw9xPTCF.js → backlog-8NYH_62t.js} +2 -2
  31. package/public/assets/{busy-health-CTu32MZt.js → busy-health-D3hwvYb4.js} +2 -2
  32. package/public/assets/{channels-BiXzg1G9.js → channels-DCJ5WL7O.js} +2 -2
  33. package/public/assets/{chat-Ctk2qwvA.js → chat-DAIl357U.js} +3 -3
  34. package/public/assets/{code-preview-Bm1wWqAp.js → code-preview-CzrZfr9U.js} +2 -2
  35. package/public/assets/{connectors-C36ax4Vw.js → connectors-xTxtlyou.js} +2 -2
  36. package/public/assets/{cosmos-Jj9LK2Wf.js → cosmos-BpebFLK2.js} +2 -2
  37. package/public/assets/{crate-agent-H6S7-B9T.js → crate-agent-BHvI5wXC.js} +2 -2
  38. package/public/assets/{dependency-graph-Cd1E23wa.js → dependency-graph-DGT0o3wJ.js} +2 -2
  39. package/public/assets/{deploys-Bwfq146q.js → deploys-hqWOHSHE.js} +2 -2
  40. package/public/assets/{display-CfO7rTuF.js → display-CDeH4I8T.js} +2 -2
  41. package/public/assets/{files-DhhZI2Ih.js → files-BTeGVmTg.js} +2 -2
  42. package/public/assets/{fleet-B_rFEbnL.js → fleet-DgXtNgCa.js} +2 -2
  43. package/public/assets/{fleet-metrics-BkHP-P3R.js → fleet-metrics-BHEt5yc_.js} +2 -2
  44. package/public/assets/{formatted-body-Cm1RrzmN.js → formatted-body-MLtodyPp.js} +3 -3
  45. package/public/assets/{formatted-body-impl-DQiOuOD1.js → formatted-body-impl-CAe664Z8.js} +2 -2
  46. package/public/assets/{host-providers-dialog-CHqwXTov.js → host-providers-dialog-mapF4Fj9.js} +2 -2
  47. package/public/assets/index-CqqgOI5R.js +15 -0
  48. package/public/assets/index-MYAIwYTX.css +2 -0
  49. package/public/assets/{insights-fH1EL-qZ.js → insights-DNyf7IzF.js} +2 -2
  50. package/public/assets/{integrations-C5Oix6ng.js → integrations-DlYOHq7T.js} +2 -2
  51. package/public/assets/{issue-ref-chip-ODyondIa.js → issue-ref-chip-CeWBNWn7.js} +2 -2
  52. package/public/assets/{log-viewer-Cvfy6PW7.js → log-viewer-Cib8nqdE.js} +2 -2
  53. package/public/assets/{maintenance-D8CDH5wZ.js → maintenance-DSn1cPm7.js} +2 -2
  54. package/public/assets/{managed-agents-egllbpij.js → managed-agents-C3CkM81m.js} +2 -2
  55. package/public/assets/{markdown-preview-CYSFgVgK.js → markdown-preview-W4VQQPAv.js} +3 -3
  56. package/public/assets/{markdown-preview-impl-BteVIjX4.js → markdown-preview-impl-CMsTdhjq.js} +2 -2
  57. package/public/assets/{memory-C8WmXsyr.js → memory-BV17qtO4.js} +2 -2
  58. package/public/assets/{messages-BzQdqlO6.js → messages-Bm_1AqSA.js} +2 -2
  59. package/public/assets/{orchestrators-B1HxzkNw.js → orchestrators-Dg-vfP09.js} +2 -2
  60. package/public/assets/{overview-DCUnI4Z7.js → overview-DcYCrvlr.js} +2 -2
  61. package/public/assets/{pairs-ftCd2XcD.js → pairs-8o9nuG3V.js} +2 -2
  62. package/public/assets/{profile-icon-B2zH8DMH.js → profile-icon-BsRyBMea.js} +2 -2
  63. package/public/assets/project-profile-instructions-DETKFn_E.js +2 -0
  64. package/public/assets/projects-M_m6AHws.js +2 -0
  65. package/public/assets/{prompt-settings-Bftq9czX.js → prompt-settings-C_mNCgeL.js} +2 -2
  66. package/public/assets/provider-display-BEeLXC_D.js +2 -0
  67. package/public/assets/{registry-DWCU5c8q.js → registry-DDHHHE5N.js} +2 -2
  68. package/public/assets/run-card-Dt4I4jaP.js +2 -0
  69. package/public/assets/scroll-text-BKXS1ahr.js +2 -0
  70. package/public/assets/{secrets-C7GIUATz.js → secrets-BkVwxI5O.js} +2 -2
  71. package/public/assets/{service-access-settings-C3aujqTG.js → service-access-settings-Crq-6a-B.js} +2 -2
  72. package/public/assets/{settings-C6sZ9eBa.js → settings-CYcavMFu.js} +2 -2
  73. package/public/assets/shipping-H6gzEeZB.js +2 -0
  74. package/public/assets/{stats-V9Z-GxUd.js → stats-BJVeWkpV.js} +2 -2
  75. package/public/assets/{status-dot-CVv0iGHL.js → status-dot-BcHyw1Vw.js} +2 -2
  76. package/public/assets/store-DKPDIZBe.js +9 -0
  77. package/public/assets/{tasks-CXU75HRF.js → tasks-qJjlQPpx.js} +2 -2
  78. package/public/assets/{teams-BPf976zb.js → teams-Cd5Sk1uX.js} +2 -2
  79. package/public/assets/{terminal-viewer-DE9BOe-8.js → terminal-viewer-CpY5_m7W.js} +3 -3
  80. package/public/assets/{terminal-viewer-impl-D04pjmGh.js → terminal-viewer-impl-DI_2llFR.js} +2 -2
  81. package/public/assets/token-estimate-Co-Jdphh.js +2 -0
  82. package/public/assets/{use-agent-terminal-B1yR456R.js → use-agent-terminal-0yJA_buM.js} +2 -2
  83. package/public/assets/{use-decisions-D-S4tpDf.js → use-decisions-uJXHf34W.js} +2 -2
  84. package/public/assets/use-project-list-CpWl6eHQ.js +2 -0
  85. package/public/assets/{use-selectors-CnrAk2YP.js → use-selectors-BdxBW8mL.js} +2 -2
  86. package/public/assets/{user-avatar-nBFIcR4M.js → user-avatar-3IndfpOc.js} +2 -2
  87. package/public/assets/versioned-prompt-editor-CIVg7XB1.js +8 -0
  88. package/public/assets/{work-queue-TCjJxeT-.js → work-queue-B9rnLwHZ.js} +2 -2
  89. package/public/assets/{workflow-marker-Cf3Hf2fN.js → workflow-marker-X8wxyhzC.js} +2 -2
  90. package/public/assets/{workflows-CXQX20Qj.js → workflows-o5YyMFW7.js} +2 -2
  91. package/public/assets/{workspaces-qSYzKYMA.js → workspaces-CzEGoAng.js} +2 -2
  92. package/public/cosmos.html +4 -4
  93. package/public/index.html +31 -29
  94. package/runner/plugins/claude/.claude-plugin/plugin.json +1 -1
  95. package/runner/plugins/claude/hooks/gate-run-guard.provisioned.mjs +31 -1
  96. package/runner/src/adapters/claude-effort.ts +2 -4
  97. package/runner/src/adapters/claude-headless.ts +1 -1
  98. package/runner/src/adapters/claude-launch-exit.ts +26 -0
  99. package/runner/src/adapters/claude-session-probe.ts +1 -1
  100. package/runner/src/adapters/claude-tmux.ts +38 -10
  101. package/runner/src/adapters/claude.ts +28 -75
  102. package/runner/src/adapters/codex-launch-config.ts +4 -5
  103. package/runner/src/adapters/codex.ts +1 -1
  104. package/runner/src/adapters/opencode-provider.ts +7 -22
  105. package/runner/src/adapters/opencode.ts +4 -4
  106. package/runner/src/adapters/provider-quota-harvest.ts +20 -71
  107. package/runner/src/agent-runner/credentials.ts +42 -12
  108. package/runner/src/agent-runner/inbox.ts +27 -0
  109. package/runner/src/agent-runner/lifecycle-milestones.ts +2 -4
  110. package/runner/src/agent-runner/liveness-exchange.ts +104 -0
  111. package/runner/src/bin/claude.ts +4 -0
  112. package/runner/src/bin/codex.ts +4 -0
  113. package/runner/src/bin/opencode.ts +4 -0
  114. package/runner/src/claude-system-prompt.ts +49 -0
  115. package/runner/src/index.ts +5 -2
  116. package/runner/src/launch-assembly.ts +14 -10
  117. package/runner/src/profile-home.ts +1 -1
  118. package/runner/src/project-instructions.ts +2 -2
  119. package/runner/src/provider-command.ts +18 -0
  120. package/runner/src/provider-setup.ts +35 -0
  121. package/runner/src/providers.ts +2 -1
  122. package/runner/src/runner-core.ts +32 -38
  123. package/runner/src/runner-helpers.ts +0 -8
  124. package/runner/src/session-scratch.ts +6 -2
  125. package/runner/src/spawn-input.ts +2 -1
  126. package/runner/src/test-run-dedup.ts +59 -56
  127. package/runner/test-support/provisioning-context.ts +4 -2
  128. package/runner/test-support/runner-harness.ts +2 -1
  129. package/scripts/cd-config.ts +8 -0
  130. package/scripts/gate-run-lease.ts +10 -7
  131. package/scripts/gate.sh +5 -1
  132. package/scripts/steward-policy-cleanup.ts +2 -2
  133. package/src/agent-authz.ts +2 -2
  134. package/src/agent-drive-arbiter.ts +3 -8
  135. package/src/agent-exit-cleanup-async.ts +2 -2
  136. package/src/agent-exit-cleanup.ts +23 -19
  137. package/src/agent-lifecycle-events.ts +112 -85
  138. package/src/agent-ref.ts +3 -2
  139. package/src/agent-status-events.ts +43 -14
  140. package/src/auth/password.ts +4 -3
  141. package/src/auth/session-refresh.ts +1 -1
  142. package/src/auth/session.test-fixture.ts +2 -2
  143. package/src/auth/session.ts +7 -7
  144. package/src/automations/crud.ts +8 -81
  145. package/src/automations/dispatch.ts +33 -58
  146. package/src/automations/index.ts +1 -1
  147. package/src/automations/persistence.ts +65 -0
  148. package/src/automations/precheck.ts +18 -16
  149. package/src/automations/read-async.ts +11 -0
  150. package/src/automations/reconcile.ts +30 -39
  151. package/src/automations/storage.ts +33 -10
  152. package/src/automations/write-async.ts +33 -0
  153. package/src/branch-land-notice.ts +93 -0
  154. package/src/branch-landed.ts +31 -98
  155. package/src/bus.ts +40 -39
  156. package/src/cd-deploy-notify.ts +32 -4
  157. package/src/cd-host-lag-notify.ts +7 -6
  158. package/src/ci/incidents.ts +2 -2
  159. package/src/ci/monitor.ts +5 -8
  160. package/src/ci/notifications.ts +4 -4
  161. package/src/ci/root-missing.ts +4 -9
  162. package/src/ci/transition-notify.ts +12 -44
  163. package/src/claude-app/oauth-http.ts +3 -3
  164. package/src/cli/cd-pauses.ts +19 -0
  165. package/src/cli/orchestrator.ts +1 -1
  166. package/src/cli/state.ts +2 -19
  167. package/src/cli/task-edit.ts +22 -0
  168. package/src/cli/task.ts +3 -1
  169. package/src/cli/workflow.ts +19 -4
  170. package/src/commands-db.ts +7 -7
  171. package/src/commands-write.ts +8 -0
  172. package/src/compaction-watch.ts +10 -8
  173. package/src/config-store-write.ts +13 -1
  174. package/src/config-store.ts +25 -14
  175. package/src/config.ts +4 -11
  176. package/src/connectors.ts +10 -8
  177. package/src/context-advisory.ts +22 -21
  178. package/src/db/activity-async.ts +6 -1
  179. package/src/db/activity-write-ops.ts +12 -1
  180. package/src/db/activity.ts +1 -14
  181. package/src/db/agent-action-write.ts +36 -0
  182. package/src/db/agent-control-async.ts +9 -0
  183. package/src/db/agent-control-write.ts +49 -0
  184. package/src/db/agent-effort-observe.ts +22 -0
  185. package/src/db/agent-meta.ts +14 -3
  186. package/src/db/agent-search.ts +1 -15
  187. package/src/db/agents-async.ts +59 -9
  188. package/src/db/agents-write-ops.ts +12 -3
  189. package/src/db/agents.ts +95 -65
  190. package/src/db/auth-identities.ts +2 -2
  191. package/src/db/automation-read-ops.ts +11 -0
  192. package/src/db/automation-write-ops.ts +46 -0
  193. package/src/db/blackboard.ts +2 -2
  194. package/src/db/browser-session-write.ts +32 -0
  195. package/src/db/bus-outbox-write-ops.ts +3 -1
  196. package/src/db/checkout-refresh-schema.ts +47 -0
  197. package/src/db/checkout-refresh.ts +5 -5
  198. package/src/db/ci-incidents-async.ts +5 -5
  199. package/src/db/ci-incidents-write-ops.ts +2 -2
  200. package/src/db/command-agent-profile-backfill.ts +39 -0
  201. package/src/db/command-agent-profiles.ts +5 -1
  202. package/src/db/command-retention.ts +126 -0
  203. package/src/db/command-row.ts +10 -2
  204. package/src/db/commands-list-query.ts +2 -0
  205. package/src/db/commands-planner-stats.test-fixture.ts +8 -0
  206. package/src/db/commands-read-ops.ts +16 -0
  207. package/src/db/commands-spawn-read.ts +1 -1
  208. package/src/db/config-store-write-ops.ts +7 -2
  209. package/src/db/connection.ts +1 -182
  210. package/src/db/context-carry-write.ts +74 -0
  211. package/src/db/continuation-archives-async.ts +5 -0
  212. package/src/db/continuation-archives-write-ops.ts +7 -0
  213. package/src/db/continuations.ts +6 -6
  214. package/src/db/db-error-registry.ts +12 -0
  215. package/src/db/db-writer-worker.ts +8 -1
  216. package/src/db/deploy-events-async.ts +1 -0
  217. package/src/db/deploy-events-read-ops.ts +2 -1
  218. package/src/db/deploy-events.ts +35 -4
  219. package/src/db/gateway.ts +26 -133
  220. package/src/db/history-retention-ops.ts +13 -0
  221. package/src/db/hosts-registration.ts +10 -2
  222. package/src/db/hosts.ts +9 -6
  223. package/src/db/inbox-async.ts +2 -2
  224. package/src/db/inbox-read-ops.ts +1 -1
  225. package/src/db/inbox.ts +9 -17
  226. package/src/db/index.ts +2 -0
  227. package/src/db/integrations-async.ts +8 -0
  228. package/src/db/integrations-ops.ts +15 -0
  229. package/src/db/integrations.ts +15 -21
  230. package/src/db/issue-associations.ts +2 -2
  231. package/src/db/issue-lifecycle.ts +3 -4
  232. package/src/db/issue-refs-async.ts +23 -0
  233. package/src/db/issue-refs-read-ops.ts +6 -0
  234. package/src/db/issue-refs-write-ops.ts +29 -0
  235. package/src/db/issue-refs.ts +15 -16
  236. package/src/db/kernel-event-log-schema.ts +2 -0
  237. package/src/db/land-boundaries.ts +51 -5
  238. package/src/db/land-gate-attestations.ts +2 -2
  239. package/src/db/landing-receipts.ts +2 -2
  240. package/src/db/lifecycle-context-write.ts +5 -0
  241. package/src/db/lifecycle-tail-async.ts +76 -0
  242. package/src/db/lifecycle-tail-write-ops.ts +62 -0
  243. package/src/db/lifecycle-write-window.ts +2 -1
  244. package/src/db/lineage-pair-dedupe-sql.ts +3 -3
  245. package/src/db/long-running-work.ts +2 -2
  246. package/src/db/main-connection-mode.ts +11 -0
  247. package/src/db/main-connection-writes.ts +41 -2
  248. package/src/db/maintenance-job-reconcile.ts +61 -0
  249. package/src/db/maintenance-job-runs-async.ts +14 -0
  250. package/src/db/maintenance-job-write-ops.ts +9 -0
  251. package/src/db/maintenance-pauses.ts +2 -2
  252. package/src/db/mappers.ts +2 -14
  253. package/src/db/merge-lease.ts +1 -15
  254. package/src/db/message-send.ts +4 -0
  255. package/src/db/messages.ts +31 -25
  256. package/src/db/migrations.ts +25 -16
  257. package/src/db/notification-write-ops.ts +19 -0
  258. package/src/db/operator-answer-write.ts +19 -0
  259. package/src/db/orchestrators.ts +14 -76
  260. package/src/db/pairs-async.ts +22 -1
  261. package/src/db/pairs-write-ops.ts +8 -3
  262. package/src/db/pairs.ts +42 -61
  263. package/src/db/plan-dispatch-schema.ts +5 -1
  264. package/src/db/plan-ready-notices.ts +43 -0
  265. package/src/db/plan-tools-include-migration.ts +36 -0
  266. package/src/db/plans-write-ops.ts +2 -0
  267. package/src/db/presence-buffer.ts +5 -0
  268. package/src/db/presence-loop.ts +18 -23
  269. package/src/db/presence-write.ts +0 -6
  270. package/src/db/project-entries-async.ts +14 -0
  271. package/src/db/project-entries-read-ops.ts +5 -1
  272. package/src/db/project-entries-write-ops.ts +8 -0
  273. package/src/db/project-entries.ts +15 -12
  274. package/src/db/project-journal.ts +2 -2
  275. package/src/db/project-profile-instructions.ts +50 -0
  276. package/src/db/project-trust-approvals.ts +2 -2
  277. package/src/db/prompt-templates.ts +4 -4
  278. package/src/db/prompt-versions.ts +9 -2
  279. package/src/db/provider-config-history.ts +8 -3
  280. package/src/db/provider-model-seed.ts +34 -0
  281. package/src/db/read-op-marshaling-fixtures.ts +23 -0
  282. package/src/db/read-ops.ts +8 -0
  283. package/src/db/retention.ts +13 -2
  284. package/src/db/review-publish-write.ts +8 -0
  285. package/src/db/routing-policy.ts +2 -3
  286. package/src/db/runtime-admin-async.ts +19 -0
  287. package/src/db/runtime-admin-write-ops.ts +36 -0
  288. package/src/db/runtime-tail-async.ts +288 -0
  289. package/src/db/runtime-tail-write-ops.ts +97 -0
  290. package/src/db/schema.ts +13 -4
  291. package/src/db/spawn-budget.ts +5 -0
  292. package/src/db/spawn-delegation-state.ts +11 -4
  293. package/src/db/spawn-delegation.ts +1 -11
  294. package/src/db/sql-audit-indexes.ts +3 -0
  295. package/src/db/stats-async.ts +9 -6
  296. package/src/db/task-entity.ts +28 -6
  297. package/src/db/task-list.ts +3 -1
  298. package/src/db/tasks.ts +1 -17
  299. package/src/db/team-claim-policy.ts +60 -0
  300. package/src/db/teams-async.ts +4 -0
  301. package/src/db/teams-write-ops.ts +4 -0
  302. package/src/db/terminal-events.ts +15 -0
  303. package/src/db/timer-cutover-bodies.ts +292 -0
  304. package/src/db/timer-cutover-core.ts +37 -278
  305. package/src/db/timer-cutover-state.ts +40 -0
  306. package/src/db/timer-cutover-write-ops.ts +1 -40
  307. package/src/db/timer-ingress.ts +7 -1
  308. package/src/db/token-db-write-ops.ts +19 -0
  309. package/src/db/token-standing-async.ts +12 -0
  310. package/src/db/token-standing.ts +21 -20
  311. package/src/db/token-usage-retention.ts +33 -0
  312. package/src/db/token-usage-write-ops.ts +1 -1
  313. package/src/db/token-usage.ts +4 -1
  314. package/src/db/user-invites-async.ts +13 -0
  315. package/src/db/user-invites.ts +3 -3
  316. package/src/db/users.ts +14 -6
  317. package/src/db/workflow-contact.ts +1 -1
  318. package/src/db/workflow-task-read-ops.ts +30 -0
  319. package/src/db/workspaces.ts +8 -5
  320. package/src/db/write-ops.ts +37 -12
  321. package/src/db/writer-fault-seam.ts +2 -9
  322. package/src/db/writer-fault-test-ops.ts +18 -0
  323. package/src/db/writer-gateway.ts +58 -56
  324. package/src/dev.ts +8 -3
  325. package/src/dispatch-availability.ts +10 -6
  326. package/src/ephemeral/{landing-ingress-proof-worker.ts → landing-ingress-proof.test-fixture.ts} +4 -2
  327. package/src/event-loop-lag.ts +4 -2
  328. package/src/event-loop-yield.ts +2 -2
  329. package/src/events.ts +10 -8
  330. package/src/host/disk-alert.ts +121 -0
  331. package/src/host-provider-store.ts +5 -4
  332. package/src/hub-metrics-report.ts +5 -1
  333. package/src/hub-metrics.ts +38 -18
  334. package/src/injection-sweep-pacing.ts +6 -8
  335. package/src/land-gate-notifications.ts +11 -10
  336. package/src/land-gate-queue-progress.ts +3 -3
  337. package/src/land-settle-dispatch.ts +52 -92
  338. package/src/lifecycle-context-pressure.ts +11 -10
  339. package/src/lifecycle-manager.ts +107 -100
  340. package/src/maintenance/breaker.ts +18 -16
  341. package/src/maintenance/cd-watcher.ts +3 -3
  342. package/src/maintenance/checkout-refresh.ts +18 -1
  343. package/src/maintenance/critical-job-watchdog.ts +19 -20
  344. package/src/maintenance/events.ts +3 -3
  345. package/src/maintenance/history-retention-job.ts +30 -0
  346. package/src/maintenance/index.ts +1 -0
  347. package/src/maintenance/jobs.ts +32 -69
  348. package/src/maintenance/long-running-work.ts +3 -1
  349. package/src/maintenance/offline-agent-prune-job.ts +28 -0
  350. package/src/maintenance/orphaned-sessions.ts +5 -4
  351. package/src/maintenance/pauses.ts +4 -10
  352. package/src/maintenance/plan-ready-notice-job.ts +71 -0
  353. package/src/maintenance/scheduler.ts +98 -137
  354. package/src/maintenance/stalled-delivery-pager.ts +10 -9
  355. package/src/maintenance/teams.ts +19 -30
  356. package/src/maintenance/workspaces.ts +18 -28
  357. package/src/managed-policy-escalation.ts +7 -7
  358. package/src/managed-policy.ts +32 -28
  359. package/src/managed-provider-gate.ts +5 -4
  360. package/src/managed-spawn-reservation.ts +5 -4
  361. package/src/mcp/index.ts +12 -9
  362. package/src/mcp/output-descriptions.ts +8 -8
  363. package/src/mcp/presentation.ts +6 -5
  364. package/src/mcp/project-profile-instructions.ts +32 -0
  365. package/src/mcp/runtime-output.ts +6 -1
  366. package/src/mcp/self-care-visibility.ts +2 -2
  367. package/src/mcp/spawn-targets-matrix.ts +10 -1
  368. package/src/mcp/tool-groups.ts +4 -3
  369. package/src/mcp/tool-roles.ts +81 -0
  370. package/src/mcp/tools-spawn.ts +4 -3
  371. package/src/mcp/tools-workspace.ts +1 -1
  372. package/src/mcp-agent-action.ts +1 -2
  373. package/src/mcp-blackboard-tools.ts +6 -5
  374. package/src/mcp-decision-tools.ts +7 -7
  375. package/src/mcp-egress-tools.ts +10 -9
  376. package/src/mcp-issue-activity-tools.ts +2 -2
  377. package/src/mcp-issue-association-tools.ts +4 -4
  378. package/src/mcp-issue-ref-tools.ts +13 -12
  379. package/src/mcp-maintenance-tools.ts +2 -2
  380. package/src/mcp-merge-tools.ts +5 -5
  381. package/src/mcp-notification-subscription-tools.ts +14 -15
  382. package/src/mcp-output-levels.test-fixture.ts +1 -1
  383. package/src/mcp-profile-trust-tools.ts +5 -5
  384. package/src/mcp-project-journal-tools.ts +4 -12
  385. package/src/mcp-project-tools.ts +17 -18
  386. package/src/mcp-provider-catalog.ts +2 -2
  387. package/src/mcp-routing-policy-tools.ts +3 -2
  388. package/src/mcp-task-tools.ts +11 -5
  389. package/src/mcp-team-template-tools.ts +3 -3
  390. package/src/mcp-team-tools.ts +6 -6
  391. package/src/mcp-workflow-tools.ts +13 -4
  392. package/src/memory-area-injection.ts +13 -12
  393. package/src/memory-boundary-capture.ts +6 -6
  394. package/src/memory-injection.ts +45 -43
  395. package/src/merge-pause-maintenance.ts +5 -4
  396. package/src/notification-router.ts +44 -17
  397. package/src/notification-types.ts +2 -1
  398. package/src/notify.ts +5 -5
  399. package/src/observed-reset-refresh.ts +4 -4
  400. package/src/operator-alarm.ts +23 -11
  401. package/src/project-ingest.ts +6 -6
  402. package/src/project-instructions.ts +12 -1
  403. package/src/project-profile-instruction-text.ts +6 -0
  404. package/src/project-profile-instructions.ts +20 -0
  405. package/src/project-trust.ts +12 -11
  406. package/src/prompt-resolver.ts +3 -3
  407. package/src/provider-catalog-store.ts +3 -49
  408. package/src/provider-config-store.ts +12 -11
  409. package/src/provider-model-seed.ts +1 -18
  410. package/src/provider-quota-async.ts +5 -7
  411. package/src/provider-quota-config-store.ts +5 -4
  412. package/src/provider-secrets.test-fixture.ts +3 -3
  413. package/src/provider-timeline-event.ts +4 -7
  414. package/src/quota-band-delivery.ts +2 -2
  415. package/src/recipe-runner.ts +6 -5
  416. package/src/repo-config-source.ts +10 -4
  417. package/src/retention-config-store.ts +4 -3
  418. package/src/reviewer-pipeline.ts +11 -10
  419. package/src/routes/_route.ts +4 -4
  420. package/src/routes/_shared.ts +13 -12
  421. package/src/routes/agent-effort.ts +5 -5
  422. package/src/routes/agent-heartbeat-quota.ts +23 -0
  423. package/src/routes/agent-keep.ts +1 -1
  424. package/src/routes/agent-profiles.ts +6 -6
  425. package/src/routes/agent-reporting.ts +1 -1
  426. package/src/routes/agent-self-effort.ts +5 -5
  427. package/src/routes/agent-sessions.ts +24 -24
  428. package/src/routes/agents-spawn.ts +2 -2
  429. package/src/routes/agents.ts +19 -121
  430. package/src/routes/auth.ts +14 -15
  431. package/src/routes/authority-audit.ts +2 -2
  432. package/src/routes/automations.ts +6 -5
  433. package/src/routes/channel-binding-delete.ts +1 -1
  434. package/src/routes/channel-output.ts +3 -3
  435. package/src/routes/claude-api-budget.ts +5 -5
  436. package/src/routes/commands-listing.ts +5 -1
  437. package/src/routes/commands.ts +31 -29
  438. package/src/routes/context-resets.ts +2 -2
  439. package/src/routes/continuation-archives.ts +2 -2
  440. package/src/routes/coordinator-teams.ts +1 -1
  441. package/src/routes/decisions.ts +2 -2
  442. package/src/routes/host-secret-files.ts +1 -1
  443. package/src/routes/hosts.ts +11 -11
  444. package/src/routes/inbox.ts +3 -2
  445. package/src/routes/index.ts +12 -2
  446. package/src/routes/integrations.ts +32 -56
  447. package/src/routes/issue-activity.ts +2 -2
  448. package/src/routes/issue-associations.ts +4 -4
  449. package/src/routes/issue-refs.ts +9 -9
  450. package/src/routes/kernel-timers-operator.ts +1 -1
  451. package/src/routes/memory.ts +1 -1
  452. package/src/routes/merges.ts +11 -11
  453. package/src/routes/messages.ts +23 -17
  454. package/src/routes/orchestrator-bootstrap.ts +2 -2
  455. package/src/routes/orchestrator-host-registration.ts +4 -2
  456. package/src/routes/orchestrator-host-utilization.ts +5 -3
  457. package/src/routes/orchestrator-proxy.ts +4 -4
  458. package/src/routes/orchestrator.ts +28 -19
  459. package/src/routes/pairs.ts +8 -8
  460. package/src/routes/project-profile-instructions.ts +27 -0
  461. package/src/routes/project-trust.ts +3 -3
  462. package/src/routes/projects.ts +16 -16
  463. package/src/routes/prompt-templates.ts +12 -12
  464. package/src/routes/provider-config.ts +6 -6
  465. package/src/routes/provider-quota-config.ts +2 -2
  466. package/src/routes/provider-quotas.ts +10 -10
  467. package/src/routes/run-command.ts +1 -1
  468. package/src/routes/secrets-backup.ts +2 -2
  469. package/src/routes/secrets-import.ts +1 -1
  470. package/src/routes/secrets-vault.ts +7 -7
  471. package/src/routes/settings.ts +6 -6
  472. package/src/routes/shipping.ts +9 -1
  473. package/src/routes/spawn-policy.ts +8 -7
  474. package/src/routes/sse.ts +2 -2
  475. package/src/routes/stats.ts +6 -6
  476. package/src/routes/task-http.ts +15 -1
  477. package/src/routes/task-issues.ts +11 -26
  478. package/src/routes/tasks.ts +22 -53
  479. package/src/routes/team-templates.ts +12 -12
  480. package/src/routes/teams-config.ts +2 -2
  481. package/src/routes/teams.ts +10 -10
  482. package/src/routes/tokens.ts +30 -22
  483. package/src/routes/users.ts +24 -24
  484. package/src/routes/viewer-read.ts +3 -3
  485. package/src/routes/web-tool-calls.ts +4 -4
  486. package/src/routes/workflows.ts +22 -7
  487. package/src/routes/workspaces.ts +17 -16
  488. package/src/routing-policy-normalize.ts +1 -2
  489. package/src/routing-policy-push.ts +6 -6
  490. package/src/routing-step-defaults.ts +0 -2
  491. package/src/runner-token-mint-limit.ts +63 -0
  492. package/src/runtime-grant.ts +21 -13
  493. package/src/runtime-tokens.ts +59 -35
  494. package/src/secrets-broker/config.ts +1 -1
  495. package/src/secrets-broker/service.ts +2 -2
  496. package/src/security.ts +16 -21
  497. package/src/self-resume-carry.ts +21 -110
  498. package/src/self-resume-supersede.ts +4 -3
  499. package/src/self-resume.ts +54 -66
  500. package/src/server-background.ts +2 -2
  501. package/src/server.ts +26 -24
  502. package/src/services/agent-action.ts +12 -31
  503. package/src/services/agent-effort.ts +7 -32
  504. package/src/services/agent-hang-watchdog-job.ts +2 -2
  505. package/src/services/agent-heartbeat.ts +166 -0
  506. package/src/services/agent-reporting.ts +1 -1
  507. package/src/services/browser-inventory.ts +3 -3
  508. package/src/services/browser-session-state.ts +3 -2
  509. package/src/services/browser-session.ts +21 -27
  510. package/src/services/bus-command-update.ts +9 -9
  511. package/src/services/checkout-refresh-state.ts +1 -0
  512. package/src/services/checkout-refresh.test-fixture.ts +3 -2
  513. package/src/services/command-host.ts +7 -2
  514. package/src/services/coordinator-digest.ts +3 -2
  515. package/src/services/dispatch-command.ts +12 -13
  516. package/src/services/effective-launch-driver.ts +14 -9
  517. package/src/services/host-changed-paths.test-fixture.ts +5 -6
  518. package/src/services/host-project-readiness.ts +2 -2
  519. package/src/services/host-providers.ts +1 -1
  520. package/src/services/host-utilization.ts +27 -4
  521. package/src/services/idle-agent-digest.ts +17 -12
  522. package/src/services/idle-epics.ts +9 -10
  523. package/src/services/interrupt-agent.ts +12 -9
  524. package/src/services/issue-activity.ts +3 -3
  525. package/src/services/issue-associations.ts +15 -18
  526. package/src/services/issue-lifecycle-comments.ts +3 -2
  527. package/src/services/issue-lifecycle.ts +69 -72
  528. package/src/services/issue-refs.ts +10 -15
  529. package/src/services/kernel-timers-cutover-chaos-worker.ts +11 -8
  530. package/src/services/kernel-timers-ingress-chaos-worker.ts +6 -1
  531. package/src/services/kernel-timers-ingress.ts +4 -4
  532. package/src/services/kernel-timers-message-authority-chaos-worker.ts +3 -10
  533. package/src/services/kernel-timers-operator-chaos-worker.ts +3 -2
  534. package/src/services/kernel-timers-outbox.ts +1 -1
  535. package/src/services/kernel-timers-stage10-operator.ts +1 -1
  536. package/src/services/kernel-timers.ts +4 -3
  537. package/src/services/land-gate-placement.ts +11 -2
  538. package/src/services/land-issue-relations.ts +5 -4
  539. package/src/services/land-restart-retry.ts +7 -6
  540. package/src/services/landed-ancestry.ts +25 -8
  541. package/src/services/legacy-landing-host.ts +4 -1
  542. package/src/services/legacy-landing-recovery.ts +8 -6
  543. package/src/services/managed-running.ts +17 -17
  544. package/src/services/memory-areas.ts +17 -53
  545. package/src/services/merge-pause.ts +9 -15
  546. package/src/services/operator-decisions.ts +37 -41
  547. package/src/services/orchestrator-registration-recovery.ts +6 -2
  548. package/src/services/orchestrator-upgrade.ts +14 -12
  549. package/src/services/permission-decision.ts +4 -3
  550. package/src/services/plan-ci-notice.ts +57 -0
  551. package/src/services/plan-ci-recovery.ts +2 -55
  552. package/src/services/plan-conformance.ts +4 -4
  553. package/src/services/plan-dispatch-policy.ts +10 -3
  554. package/src/services/plan-dispatch.ts +35 -14
  555. package/src/services/plan-graph.ts +39 -261
  556. package/src/services/plan-import.ts +1 -1
  557. package/src/services/plan-live-bindings.ts +8 -37
  558. package/src/services/plan-task-create.ts +1 -1
  559. package/src/services/plan-terminal-refs.ts +2 -12
  560. package/src/services/plan-wake-digest.ts +9 -3
  561. package/src/services/profile-trust.ts +6 -5
  562. package/src/services/project-config-read.ts +70 -0
  563. package/src/services/project-profile-instructions.ts +54 -0
  564. package/src/services/project.ts +11 -10
  565. package/src/services/prompt-templates.ts +6 -5
  566. package/src/services/provider-hold-nudge.ts +9 -9
  567. package/src/services/provider-hold-record.ts +13 -12
  568. package/src/services/provider-state-audit.ts +19 -18
  569. package/src/services/provisioning-harness/budget-baseline.json +4 -4
  570. package/src/services/provisioning-harness/driver.ts +1 -1
  571. package/src/services/provisioning-harness/fixture-matrix.ts +3 -3
  572. package/src/services/provisioning-harness/lifecycle.ts +24 -22
  573. package/src/services/provisioning-harness/prepare.ts +16 -17
  574. package/src/services/rate-limit-resume.ts +10 -9
  575. package/src/services/red-main-auto-revert.ts +7 -5
  576. package/src/services/red-main-controller-deps.ts +20 -3
  577. package/src/services/red-main-incident.ts +3 -1
  578. package/src/services/red-main-quarantine.ts +2 -3
  579. package/src/services/register-agent.ts +22 -22
  580. package/src/services/restart-project-role.ts +1 -1
  581. package/src/services/restart-spawn.ts +74 -61
  582. package/src/services/review-publish.ts +7 -24
  583. package/src/services/review-ref-reconcile.ts +29 -38
  584. package/src/services/runtime-dependency-alert.ts +27 -0
  585. package/src/services/scheduler-command.ts +11 -9
  586. package/src/services/scheduler.ts +5 -5
  587. package/src/services/self-care-state.ts +2 -1
  588. package/src/services/self-effort-grant.ts +5 -25
  589. package/src/services/self-effort.ts +5 -5
  590. package/src/services/send-message.ts +21 -34
  591. package/src/services/shutdown-agent.ts +17 -14
  592. package/src/services/spawn-agent.ts +25 -43
  593. package/src/services/spawn-chain-inspector.ts +7 -14
  594. package/src/services/spawn-cwd-placement.ts +21 -5
  595. package/src/services/spawn-issue-activity.ts +8 -6
  596. package/src/services/spawn-launch.ts +17 -1
  597. package/src/services/spawn-loss-escalation.ts +1 -1
  598. package/src/services/spawn-policy.ts +68 -0
  599. package/src/services/spawn-registration-watchdog.ts +2 -2
  600. package/src/services/spawn-repo-probe.ts +7 -5
  601. package/src/services/spawn-tree-cleanup.ts +8 -9
  602. package/src/services/steward-reaper.ts +4 -4
  603. package/src/services/task-context-hydration.ts +25 -1
  604. package/src/services/task-delivery.ts +20 -34
  605. package/src/services/task-dispatch.ts +3 -0
  606. package/src/services/task-entity.ts +40 -203
  607. package/src/services/task-issue-link.ts +13 -20
  608. package/src/services/task-issue-weave.ts +12 -11
  609. package/src/services/task-lifecycle.ts +27 -31
  610. package/src/services/task-repo-cwd.ts +37 -0
  611. package/src/services/task-review-hold.ts +4 -17
  612. package/src/services/task-semantic-events.ts +13 -11
  613. package/src/services/task-state-reconcile.ts +8 -17
  614. package/src/services/task-validation.ts +2 -2
  615. package/src/services/team-templates.ts +12 -11
  616. package/src/services/team.ts +31 -98
  617. package/src/services/terminal-provider-exit.ts +6 -5
  618. package/src/services/timer-message-fire-chaos-worker.ts +7 -3
  619. package/src/services/timer-message-fire-outbox.ts +1 -1
  620. package/src/services/transient-agent-reaper.ts +25 -20
  621. package/src/services/turn-reconcile-audit.ts +9 -9
  622. package/src/services/user-onboarding.ts +8 -6
  623. package/src/services/user-profile.ts +6 -9
  624. package/src/services/work-finality.ts +16 -4
  625. package/src/services/workflow-auto-start.ts +26 -13
  626. package/src/services/workflow-caller.ts +61 -4
  627. package/src/services/workflow-child-proof-worker.ts +1 -1
  628. package/src/services/workflow-ci-main.ts +1 -1
  629. package/src/services/workflow-control-proof-worker.ts +1 -1
  630. package/src/services/workflow-coordinator.ts +8 -2
  631. package/src/services/workflow-decisions.ts +5 -5
  632. package/src/services/workflow-deferred-cleanup.ts +5 -3
  633. package/src/services/workflow-effect-driver.ts +42 -17
  634. package/src/services/workflow-effects.ts +58 -37
  635. package/src/services/workflow-land-notice.ts +10 -0
  636. package/src/services/workflow-pause-text.ts +43 -0
  637. package/src/services/workflow-pins.ts +7 -4
  638. package/src/services/workflow-reconcile.ts +14 -11
  639. package/src/services/workflow-resume-pins.ts +3 -3
  640. package/src/services/workflow-routing-defaults.ts +18 -0
  641. package/src/services/workflow-run-view.ts +6 -3
  642. package/src/services/workflow-run.test-fixture.ts +1 -1
  643. package/src/services/workflow-runs.ts +61 -33
  644. package/src/services/workflow-supervisor-state.ts +26 -1
  645. package/src/services/workflow-supervisor.ts +11 -6
  646. package/src/services/workspace-recovery.ts +4 -3
  647. package/src/settings/registry.ts +1 -2
  648. package/src/spawn-admission.ts +10 -2
  649. package/src/spawn-command.ts +4 -4
  650. package/src/spawn-models.ts +10 -12
  651. package/src/spawn-placement.ts +28 -10
  652. package/src/spawn-targets.ts +9 -9
  653. package/src/sse-agent-status.ts +15 -6
  654. package/src/sse.ts +49 -24
  655. package/src/startup-context-injection.ts +62 -57
  656. package/src/state-adoption/cd-cutover.ts +53 -7
  657. package/src/state-adoption/fleet-cutover.ts +35 -0
  658. package/src/state-adoption/fleet-plan.ts +36 -0
  659. package/src/state-adoption/index.ts +2 -2
  660. package/src/state-adoption/linux-plan.ts +2 -2
  661. package/src/state-adoption/linux.ts +2 -2
  662. package/src/state-adoption/macos-plan.ts +2 -2
  663. package/src/state-adoption/macos.ts +1 -1
  664. package/src/steward-handoff.ts +4 -3
  665. package/src/steward-policy-cleanup.ts +9 -8
  666. package/src/steward.ts +9 -8
  667. package/src/task-dispatch-live.ts +6 -3
  668. package/src/task-dispatcher.ts +3 -3
  669. package/src/teams-config-store.ts +4 -3
  670. package/src/token-db-write.ts +10 -0
  671. package/src/token-db.ts +2 -2
  672. package/src/token-usage-async.ts +2 -4
  673. package/src/user-awaiting-reply.ts +5 -5
  674. package/src/workspace-actions.ts +24 -29
  675. package/src/workspace-auto-merge.ts +14 -12
  676. package/src/workspace-changed-paths-client.ts +16 -5
  677. package/src/workspace-cleanup-stale.ts +30 -7
  678. package/src/workspace-command-settle.ts +26 -17
  679. package/src/workspace-config.ts +1 -1
  680. package/src/workspace-conflict-resolver.ts +9 -7
  681. package/src/workspace-dead-owner-recovery.ts +5 -4
  682. package/src/workspace-escalation.ts +12 -11
  683. package/src/workspace-human-escalation.ts +3 -3
  684. package/src/workspace-land-gate-watchdog.ts +3 -3
  685. package/src/workspace-land-refusal.ts +20 -0
  686. package/src/workspace-land-stopped.ts +1 -0
  687. package/src/workspace-merge.ts +10 -26
  688. package/src/workspace-nothing-to-land-notice.ts +2 -2
  689. package/src/workspace-orphans.ts +18 -37
  690. package/src/workspace-pr-completion.ts +10 -8
  691. package/src/workspace-rekey.ts +3 -3
  692. package/src/workspace-steward-failure.ts +2 -2
  693. package/src/workspace-steward-wake.ts +4 -4
  694. package/node_modules/@humwick/kernel/LICENSE +0 -167
  695. package/node_modules/@humwick/kernel/async-entity-persistence.ts +0 -344
  696. package/node_modules/@humwick/kernel/async-event-log.ts +0 -107
  697. package/node_modules/@humwick/kernel/automations.pilot.ts +0 -147
  698. package/node_modules/@humwick/kernel/config.ts +0 -111
  699. package/node_modules/@humwick/kernel/contract.ts +0 -572
  700. package/node_modules/@humwick/kernel/control-authz.ts +0 -281
  701. package/node_modules/@humwick/kernel/control.ts +0 -645
  702. package/node_modules/@humwick/kernel/effects.ts +0 -493
  703. package/node_modules/@humwick/kernel/event-log.ts +0 -319
  704. package/node_modules/@humwick/kernel/http-transport.ts +0 -384
  705. package/node_modules/@humwick/kernel/identity-grammar.ts +0 -20
  706. package/node_modules/@humwick/kernel/identity.ts +0 -248
  707. package/node_modules/@humwick/kernel/index.ts +0 -28
  708. package/node_modules/@humwick/kernel/kernel-persistence.ts +0 -181
  709. package/node_modules/@humwick/kernel/landing-incident.ts +0 -306
  710. package/node_modules/@humwick/kernel/landing.ts +0 -953
  711. package/node_modules/@humwick/kernel/manifest-validation.ts +0 -460
  712. package/node_modules/@humwick/kernel/mcp-transport.ts +0 -109
  713. package/node_modules/@humwick/kernel/message-authority-chaos-worker.ts +0 -200
  714. package/node_modules/@humwick/kernel/message-authority.ts +0 -281
  715. package/node_modules/@humwick/kernel/module-host.ts +0 -722
  716. package/node_modules/@humwick/kernel/openapi-init.ts +0 -14
  717. package/node_modules/@humwick/kernel/package.json +0 -39
  718. package/node_modules/@humwick/kernel/persistence-schema.ts +0 -248
  719. package/node_modules/@humwick/kernel/persistence.ts +0 -510
  720. package/node_modules/@humwick/kernel/registry.ts +0 -60
  721. package/node_modules/@humwick/kernel/sqlite-kernel-persistence.ts +0 -499
  722. package/node_modules/@humwick/kernel/sqlite-module-host.ts +0 -87
  723. package/node_modules/@humwick/kernel/timers-chaos-worker.ts +0 -186
  724. package/node_modules/@humwick/kernel/timers.pilot.ts +0 -1411
  725. package/node_modules/@humwick/kernel/workflow-activation.ts +0 -25
  726. package/node_modules/@humwick/kernel/workflow-backstop.ts +0 -40
  727. package/node_modules/@humwick/kernel/workflow-cancel-tree.ts +0 -20
  728. package/node_modules/@humwick/kernel/workflow-chaos-worker.ts +0 -213
  729. package/node_modules/@humwick/kernel/workflow-composition.ts +0 -231
  730. package/node_modules/@humwick/kernel/workflow-container.ts +0 -72
  731. package/node_modules/@humwick/kernel/workflow-control.test-fixture.ts +0 -42
  732. package/node_modules/@humwick/kernel/workflow-control.ts +0 -178
  733. package/node_modules/@humwick/kernel/workflow-definition.ts +0 -507
  734. package/node_modules/@humwick/kernel/workflow-dispatch.ts +0 -60
  735. package/node_modules/@humwick/kernel/workflow-foreach.ts +0 -167
  736. package/node_modules/@humwick/kernel/workflow-journal-codec.ts +0 -339
  737. package/node_modules/@humwick/kernel/workflow-journal.ts +0 -922
  738. package/node_modules/@humwick/kernel/workflow-lane.ts +0 -48
  739. package/node_modules/@humwick/kernel/workflow-lease-owner.ts +0 -60
  740. package/node_modules/@humwick/kernel/workflow-recovery.test-fixture.ts +0 -19
  741. package/node_modules/@humwick/kernel/workflow-runtime.ts +0 -938
  742. package/node_modules/@humwick/kernel/workflow-script.ts +0 -11
  743. package/node_modules/@humwick/kernel/workflow-state.ts +0 -299
  744. package/node_modules/@humwick/kernel/workflow-step-commit.ts +0 -195
  745. package/node_modules/@humwick/kernel/workflow-transitions.ts +0 -317
  746. package/node_modules/@humwick/kernel/workflow-values.ts +0 -153
  747. package/node_modules/@humwick/kernel/workflow.pilot.ts +0 -376
  748. package/node_modules/@humwick/offload/LICENSE +0 -167
  749. package/node_modules/@humwick/offload/README.md +0 -57
  750. package/node_modules/@humwick/offload/dist/async-runner.d.ts +0 -18
  751. package/node_modules/@humwick/offload/dist/async-runner.d.ts.map +0 -1
  752. package/node_modules/@humwick/offload/dist/async-runner.js +0 -158
  753. package/node_modules/@humwick/offload/dist/async-runner.js.map +0 -1
  754. package/node_modules/@humwick/offload/dist/envelope.d.ts +0 -25
  755. package/node_modules/@humwick/offload/dist/envelope.d.ts.map +0 -1
  756. package/node_modules/@humwick/offload/dist/envelope.js +0 -80
  757. package/node_modules/@humwick/offload/dist/envelope.js.map +0 -1
  758. package/node_modules/@humwick/offload/dist/fixtures/worker-task.d.ts +0 -10
  759. package/node_modules/@humwick/offload/dist/fixtures/worker-task.d.ts.map +0 -1
  760. package/node_modules/@humwick/offload/dist/fixtures/worker-task.js +0 -59
  761. package/node_modules/@humwick/offload/dist/fixtures/worker-task.js.map +0 -1
  762. package/node_modules/@humwick/offload/dist/index.d.ts +0 -5
  763. package/node_modules/@humwick/offload/dist/index.d.ts.map +0 -1
  764. package/node_modules/@humwick/offload/dist/index.js +0 -5
  765. package/node_modules/@humwick/offload/dist/index.js.map +0 -1
  766. package/node_modules/@humwick/offload/dist/subprocess-runner.d.ts +0 -62
  767. package/node_modules/@humwick/offload/dist/subprocess-runner.d.ts.map +0 -1
  768. package/node_modules/@humwick/offload/dist/subprocess-runner.js +0 -295
  769. package/node_modules/@humwick/offload/dist/subprocess-runner.js.map +0 -1
  770. package/node_modules/@humwick/offload/dist/types.d.ts +0 -111
  771. package/node_modules/@humwick/offload/dist/types.d.ts.map +0 -1
  772. package/node_modules/@humwick/offload/dist/types.js +0 -176
  773. package/node_modules/@humwick/offload/dist/types.js.map +0 -1
  774. package/node_modules/@humwick/offload/dist/worker-pool.d.ts +0 -23
  775. package/node_modules/@humwick/offload/dist/worker-pool.d.ts.map +0 -1
  776. package/node_modules/@humwick/offload/dist/worker-pool.js +0 -435
  777. package/node_modules/@humwick/offload/dist/worker-pool.js.map +0 -1
  778. package/node_modules/@humwick/offload/dist/worker-runtime.d.ts +0 -2
  779. package/node_modules/@humwick/offload/dist/worker-runtime.d.ts.map +0 -1
  780. package/node_modules/@humwick/offload/dist/worker-runtime.js +0 -27
  781. package/node_modules/@humwick/offload/dist/worker-runtime.js.map +0 -1
  782. package/node_modules/@humwick/offload/dist/worker.d.ts +0 -5
  783. package/node_modules/@humwick/offload/dist/worker.d.ts.map +0 -1
  784. package/node_modules/@humwick/offload/dist/worker.js +0 -1
  785. package/node_modules/@humwick/offload/dist/worker.js.map +0 -1
  786. package/node_modules/@humwick/offload/package.json +0 -42
  787. package/node_modules/@humwick/offload/src/async-runner.ts +0 -170
  788. package/node_modules/@humwick/offload/src/envelope.ts +0 -92
  789. package/node_modules/@humwick/offload/src/fixtures/worker-task.ts +0 -49
  790. package/node_modules/@humwick/offload/src/index.ts +0 -4
  791. package/node_modules/@humwick/offload/src/subprocess-runner.ts +0 -334
  792. package/node_modules/@humwick/offload/src/types.ts +0 -235
  793. package/node_modules/@humwick/offload/src/worker-pool.ts +0 -463
  794. package/node_modules/@humwick/offload/src/worker-runtime.ts +0 -32
  795. package/node_modules/@humwick/offload/src/worker.ts +0 -5
  796. package/public/assets/agent-liveness-DyqxfB-t.js +0 -2
  797. package/public/assets/agent-profiles-BpI_jTxq.js +0 -2
  798. package/public/assets/index-B7ZerHLY.css +0 -2
  799. package/public/assets/index-BU3uWo2t.js +0 -15
  800. package/public/assets/projects-B_lmcqfO.js +0 -2
  801. package/public/assets/provider-display-DbLWpP3j.js +0 -2
  802. package/public/assets/run-card-CO_bguQf.js +0 -2
  803. package/public/assets/shipping-Bu8p1gEw.js +0 -2
  804. package/public/assets/store-BvtFdlGX.js +0 -9
  805. package/public/assets/use-project-list-B-T2Bnnn.js +0 -2
  806. package/public/assets/versioned-prompt-editor-Diu8BIaM.js +0 -8
  807. package/runner/src/agent-runner/http-liveness.ts +0 -85
  808. package/runner/src/gate-run-lease-probe.ts +0 -38
  809. package/src/db/claude-catalog-reconcile.ts +0 -27
  810. package/src/db/on-loop-write-transaction.ts +0 -34
  811. package/src/routing-model-aliases.ts +0 -21
@@ -2,15 +2,17 @@
2
2
  import { isRecord, withFileLock } from "@humwick/sdk";
3
3
  import { readFileSync } from "node:fs";
4
4
  import { join } from "node:path";
5
- import { currentPendingDeployEvent, finishDeployEvent, forceFailDeployEvent, getDeployEvent, recordDeployEventExecutor, tryBeginDeployEvent } from "../db";
5
+ import { currentPendingDeployEvent, finishDeployEvent, forceFailDeployEvent, getDeployEvent, getMaintenanceJobPause, recordDeployEventExecutor, recordDeployEventHosts, renewDeployEventLease, tryBeginDeployEvent } from "../db";
6
6
  import { localDeployExecutor, serializeDeployExecutor } from "../deploy-executor";
7
7
  import { atomicWrite, exists } from "./files";
8
8
  import { loadJournal, type AdoptionManifest } from "./index";
9
9
  import { parseLinuxPlan } from "./linux-plan";
10
10
  import { parseMacosPlan } from "./macos-plan";
11
+ import { parseFleetPlan, type FleetPlan } from "./fleet-plan";
11
12
 
12
13
  type Direction = "adopt" | "rollback";
13
- export interface CutoverManifest { version: 1; migrationId: string; target: string; platform: "linux" | "macos"; handoff: unknown }
14
+ export interface CutoverManifest { version: 1; migrationId: string; target: string; platform: "linux" | "macos"; handoff: unknown; fleet?: FleetPlan }
15
+ export interface CutoverProgress { eventId: string; has(step: string): boolean; complete(step: string): void; steps(): string[] }
14
16
  export interface CdPause { id: string; holder: string }
15
17
  /** The existing maintenance pause API for the cd-watcher job. `pause` returns the active pause, ours or not. */
16
18
  export interface CdPauseControl { pause(holder: string, reason: string): Promise<CdPause>; current(): Promise<CdPause | null>; resume(): Promise<void> }
@@ -21,6 +23,8 @@ export interface CutoverContext {
21
23
  /** Runs a short deploy-ledger burst against the canonical DB. */
22
24
  ledger: <T>(fn: () => T) => T;
23
25
  interrupt?: (step: string) => void;
26
+ /** Complete an interrupted local hub handoff before using the API it supervises. */
27
+ recoverHandoff?: (rollback: boolean) => Promise<void>;
24
28
  }
25
29
  interface CutoverJournal { version: 1; manifest: CutoverManifest; events: Record<Direction, string>; pauseId?: string; completed: string[] }
26
30
 
@@ -28,7 +32,12 @@ export function parseCutoverManifest(value: unknown): { manifest: CutoverManifes
28
32
  if (!isRecord(value) || value.version !== 1 || typeof value.migrationId !== "string" || !/^[A-Za-z0-9._-]{1,64}$/.test(value.migrationId)
29
33
  || typeof value.target !== "string" || !/^[0-9a-f]{40}$/.test(value.target) || (value.platform !== "linux" && value.platform !== "macos")) throw new Error("Invalid CD cutover manifest");
30
34
  const adoption = (value.platform === "linux" ? parseLinuxPlan(value.handoff) : parseMacosPlan(value.handoff)).adoption;
31
- return { manifest: { version: 1, migrationId: value.migrationId, target: value.target, platform: value.platform, handoff: value.handoff }, adoption };
35
+ const fleet = value.fleet === undefined ? undefined : parseFleetPlan(value.fleet);
36
+ if (fleet) {
37
+ const hub = fleet.hosts.find(host => host.id === fleet.hub)!;
38
+ if (hub.platform !== value.platform || JSON.stringify(hub.handoff) !== JSON.stringify(value.handoff)) throw new Error("Hub handoff must match the control host manifest");
39
+ }
40
+ return { manifest: { version: 1, migrationId: value.migrationId, target: value.target, platform: value.platform, handoff: value.handoff, ...(fleet ? { fleet } : {}) }, adoption };
32
41
  }
33
42
 
34
43
  async function withJournal<T>(input: unknown, create: boolean, fn: (journal: CutoverJournal, save: () => void, adoption: AdoptionManifest) => Promise<T>): Promise<T> {
@@ -55,7 +64,7 @@ const directionOf = (journal: CutoverJournal): Direction => journal.completed.so
55
64
  * Every step reconciles on retry: the pause is re-ensured, the journaled lease row is re-attached
56
65
  * and re-stamped with this process as executor, and the supervisor handoff resumes from its journal.
57
66
  */
58
- export async function runCutover(input: unknown, context: CutoverContext & { rollback?: boolean; handoff: (rollback: boolean) => Promise<unknown> }) {
67
+ export async function runCutover(input: unknown, context: CutoverContext & { rollback?: boolean; handoff: (rollback: boolean, progress: CutoverProgress) => Promise<unknown> }) {
59
68
  return withJournal(input, !context.rollback, async (journal, save, adoption) => {
60
69
  const direction: Direction = context.rollback ? "rollback" : "adopt";
61
70
  const { manifest } = journal;
@@ -69,6 +78,20 @@ export async function runCutover(input: unknown, context: CutoverContext & { rol
69
78
  if (journal.completed.includes(`${direction}:complete`)) return result();
70
79
  acknowledge("prepared");
71
80
 
81
+ const hubPrefix = manifest.fleet && `host:${manifest.fleet.hub}:handoff`;
82
+ const interruptedDirection = (["rollback", "adopt"] as const).find(prior => hubPrefix
83
+ && journal.completed.includes(`${prior}:${hubPrefix}-started`) && !journal.completed.includes(`${prior}:${hubPrefix}`));
84
+ if (interruptedDirection && context.recoverHandoff) {
85
+ context.ledger(() => {
86
+ const lease = getDeployEvent(journal.events[interruptedDirection]);
87
+ const pause = getMaintenanceJobPause("cd-watcher");
88
+ if (lease?.status !== "pending" || lease.initiatedBy !== holder || !pause || pause.id !== journal.pauseId || pause.holder !== holder) throw new Error("Interrupted hub handoff no longer owns its pause and deploy lease");
89
+ recordDeployEventExecutor({ id: lease.id, executor: serializeDeployExecutor(localDeployExecutor(context.executorHost)) });
90
+ renewDeployEventLease({ id: lease.id });
91
+ });
92
+ await context.recoverHandoff(direction === "rollback");
93
+ }
94
+
72
95
  // A pause does not cancel an in-flight deploy; the lease below proves the previous executor settled.
73
96
  const pause = await context.pauses.pause(holder, `Humwick cutover ${manifest.migrationId} (${direction}) to ${manifest.target}; CD stays paused until explicit resume`);
74
97
  if (pause.holder !== holder) throw new Error(`cd-watcher is paused by ${pause.holder}; refusing the cutover`);
@@ -81,7 +104,7 @@ export async function runCutover(input: unknown, context: CutoverContext & { rol
81
104
  if (direction === "rollback" && getDeployEvent(journal.events.adopt)?.status === "pending") {
82
105
  forceFailDeployEvent({ id: journal.events.adopt, reason: `superseded by rollback of ${manifest.migrationId}` });
83
106
  }
84
- const event = getDeployEvent(id) ?? tryBeginDeployEvent({ id, commitSha: manifest.target, hosts: [{ id: context.executorHost, status: "pending" }], initiatedBy: holder });
107
+ const event = getDeployEvent(id) ?? tryBeginDeployEvent({ id, commitSha: manifest.target, hosts: (manifest.fleet?.hosts ?? [{ id: context.executorHost }]).map(host => ({ id: host.id, status: "pending" })), initiatedBy: holder });
85
108
  if (!event) {
86
109
  const held = currentPendingDeployEvent();
87
110
  throw new Error(held ? `deploy lease ${held.id} is held by ${held.initiatedBy}; retry once it settles` : "an unreleased restart epoch blocks the deploy lease; retry once it settles");
@@ -94,14 +117,37 @@ export async function runCutover(input: unknown, context: CutoverContext & { rol
94
117
  acknowledge("lease");
95
118
 
96
119
  if (status === "pending") {
97
- if (direction === "adopt" || handoffBegun(adoption, manifest.platform)) await context.handoff(direction === "rollback");
120
+ const progress: CutoverProgress = {
121
+ eventId: id,
122
+ has: (step) => journal.completed.includes(`${direction}:${step}`),
123
+ steps: () => [...journal.completed],
124
+ complete(step) {
125
+ acknowledge(step);
126
+ if (manifest.fleet && !context.ledger(() => recordDeployEventHosts(id, fleetStatuses()))) throw new Error(`Lost deploy lease ${id}`);
127
+ },
128
+ };
129
+ const heartbeat = setInterval(() => {
130
+ try { context.ledger(() => renewDeployEventLease({ id })); }
131
+ catch (error) { console.error(`Cutover lease heartbeat failed: ${error}`); }
132
+ }, 60_000);
133
+ heartbeat.unref();
134
+ try {
135
+ if (direction === "adopt" || manifest.fleet || handoffBegun(adoption, manifest.platform)) await context.handoff(direction === "rollback", progress);
136
+ } finally { clearInterval(heartbeat); }
98
137
  acknowledge("handoff");
99
138
  context.ledger(() => {
100
- if (getDeployEvent(id)?.status === "pending") finishDeployEvent({ id, status: terminal, hosts: [{ id: context.executorHost, status: terminal }] });
139
+ const event = getDeployEvent(id);
140
+ if (event?.status === "pending") finishDeployEvent({ id, status: terminal, hosts: manifest.fleet ? fleetStatuses() : [{ id: context.executorHost, status: terminal }] });
141
+ else if (event?.status !== terminal) throw new Error(`Lost deploy lease ${id}; CD remains paused`);
101
142
  });
102
143
  }
103
144
  acknowledge("complete");
104
145
  return result();
146
+ function fleetStatuses() {
147
+ return manifest.fleet!.hosts.map(host => ({ id: host.id, status: direction === "rollback" && !journal.completed.includes(`adopt:host:${host.id}:deploy-started`) ? "skipped"
148
+ : journal.completed.includes(`${direction}:host:${host.id}:verified`) ? terminal : "pending",
149
+ reason: journal.completed.filter(step => step.startsWith(`${direction}:host:${host.id}:`)).map(step => step.split(":").at(-1)).join(", ") }));
150
+ }
105
151
  });
106
152
  }
107
153
 
@@ -0,0 +1,35 @@
1
+ // #3470: the fleet is one P4d cutover, with one pause/lease and durable progress per host.
2
+ import { parseCutoverManifest, runCutover, type CutoverContext, type CutoverProgress } from "./cd-cutover";
3
+ import { orderedFleetHosts, type FleetHost } from "./fleet-plan";
4
+
5
+ export interface FleetCutoverContext extends CutoverContext {
6
+ rollback?: boolean;
7
+ deploy(host: FleetHost, sha: string, version: string | undefined, eventId: string): Promise<void>;
8
+ handoff(host: FleetHost, rollback: boolean, progress: CutoverProgress): Promise<void>;
9
+ attest(host: FleetHost, sha: string): Promise<void>;
10
+ }
11
+ export async function runFleetCutover(input: unknown, context: FleetCutoverContext) {
12
+ const { manifest } = parseCutoverManifest(input);
13
+ if (!manifest.fleet || manifest.fleet.hub !== context.executorHost) throw new Error("Fleet cutover must run on its pinned hub");
14
+ return runCutover(manifest, { ...context, handoff: async (rollback, progress) => {
15
+ const ordered = orderedFleetHosts(manifest.fleet!);
16
+ for (const host of rollback ? ordered.reverse() : ordered) {
17
+ const prefix = `host:${host.id}:`;
18
+ const touched = (phase: string) => progress.steps().includes(`adopt:${prefix}${phase}-started`);
19
+ if (rollback && !touched("deploy") && !touched("handoff")) continue;
20
+ const phase = async (name: string, action: () => Promise<void>) => {
21
+ if (progress.has(`${prefix}${name}`)) return;
22
+ progress.complete(`${prefix}${name}-started`);
23
+ await action();
24
+ progress.complete(`${prefix}${name}`);
25
+ };
26
+ const sha = rollback ? host.previous.sha : manifest.target;
27
+ // Restore supervisor names before the old executor resolves its legacy units on rollback.
28
+ if (rollback && touched("handoff")) await phase("handoff", () => context.handoff(host, true, progress));
29
+ if (!rollback || touched("deploy")) await phase("deploy", () => context.deploy(host, sha, rollback ? host.previous.version : undefined, progress.eventId));
30
+ if (!rollback) await phase("handoff", () => context.handoff(host, false, progress));
31
+ await context.attest(host, sha);
32
+ progress.complete(`${prefix}verified`);
33
+ }
34
+ } });
35
+ }
@@ -0,0 +1,36 @@
1
+ // #3470 P7b: host identities and rollback artifacts are pinned before acquiring the deployment lease.
2
+ import { isRecord } from "@humwick/sdk";
3
+ import { absolute } from "./files";
4
+ import { parseLinuxPlan } from "./linux-plan";
5
+ import { parseMacosPlan } from "./macos-plan";
6
+
7
+ export interface FleetHost {
8
+ id: string;
9
+ hostname: string;
10
+ platform: "linux" | "macos";
11
+ handoff: unknown;
12
+ cli: string;
13
+ manifestPath: string;
14
+ cwd: string;
15
+ previous: { sha: string; version: string };
16
+ }
17
+ export interface FleetPlan { canary: string; hub: string; hosts: FleetHost[] }
18
+ export function parseFleetPlan(value: unknown): FleetPlan {
19
+ if (!isRecord(value) || typeof value.canary !== "string" || typeof value.hub !== "string" || value.canary === value.hub
20
+ || !Array.isArray(value.hosts) || value.hosts.length < 2) throw new Error("Invalid fleet cutover plan");
21
+ const hosts = value.hosts.map((host): FleetHost => {
22
+ if (!isRecord(host) || typeof host.id !== "string" || !/^[A-Za-z0-9._-]+$/.test(host.id) || typeof host.hostname !== "string" || !host.hostname || !["linux", "macos"].includes(String(host.platform))
23
+ || !isRecord(host.previous) || typeof host.previous.sha !== "string" || !/^[0-9a-f]{40}$/.test(host.previous.sha)
24
+ || typeof host.previous.version !== "string" || !/^\d+\.\d+\.\d+$/.test(host.previous.version)) throw new Error("Invalid fleet host or rollback artifact");
25
+ (host.platform === "linux" ? parseLinuxPlan : parseMacosPlan)(host.handoff, host.hostname);
26
+ return { id: host.id, hostname: host.hostname, platform: host.platform as FleetHost["platform"], handoff: host.handoff,
27
+ cli: absolute(host.cli), manifestPath: absolute(host.manifestPath), cwd: absolute(host.cwd),
28
+ previous: { sha: host.previous.sha, version: host.previous.version } };
29
+ });
30
+ if (new Set(hosts.map(host => host.id)).size !== hosts.length || !hosts.some(host => host.id === value.hub)
31
+ || !hosts.some(host => host.id === value.canary && host.platform === "linux")) throw new Error("Fleet needs a unique hub and Linux canary");
32
+ return { canary: value.canary, hub: value.hub, hosts };
33
+ }
34
+ export function orderedFleetHosts(plan: FleetPlan): FleetHost[] {
35
+ return [plan.hosts.find(host => host.id === plan.canary)!, ...plan.hosts.filter(host => host.id !== plan.canary && host.id !== plan.hub), plan.hosts.find(host => host.id === plan.hub)!];
36
+ }
@@ -18,8 +18,8 @@ export interface AdoptionJournal {
18
18
  db?: Identity; env: EnvSnapshot[]; completed: string[];
19
19
  }
20
20
 
21
- export function parseAdoptionManifest(value: unknown): AdoptionManifest {
22
- if (!isRecord(value) || value.version !== 1 || value.host !== hostname()
21
+ export function parseAdoptionManifest(value: unknown, expectedHost = hostname()): AdoptionManifest {
22
+ if (!isRecord(value) || value.version !== 1 || value.host !== expectedHost
23
23
  || !Array.isArray(value.roots) || !value.roots.length || !Array.isArray(value.envFiles)) throw new Error("Invalid adoption manifest or wrong host");
24
24
  const roots = value.roots.map(pair => {
25
25
  if (!isRecord(pair)) throw new Error("Invalid directory pair");
@@ -13,10 +13,10 @@ export interface LinuxPlan {
13
13
  adoption: AdoptionManifest; scope: SystemdUnitScope; unitDirectory: string;
14
14
  services: { legacy: string; canonical: string }[]; files: LinuxArtifact[]; preserve: { unit: string; mainPid: number }[];
15
15
  }
16
- export function parseLinuxPlan(value: unknown): LinuxPlan {
16
+ export function parseLinuxPlan(value: unknown, expectedHost?: string): LinuxPlan {
17
17
  if (!isRecord(value) || !["user", "system"].includes(value.scope as string) || !Array.isArray(value.services) || !value.services.length
18
18
  || !Array.isArray(value.files) || !Array.isArray(value.preserve)) throw new Error("Invalid Linux handoff manifest");
19
- const adoption = parseAdoptionManifest(value.adoption);
19
+ const adoption = parseAdoptionManifest(value.adoption, expectedHost);
20
20
  const unitDirectory = absolute(value.unitDirectory);
21
21
  const services = value.services.map(item => {
22
22
  if (!isRecord(item) || typeof item.legacy !== "string" || !/^agent-relay(?:-orchestrator)?(?:-dev)?\.service$/.test(item.legacy)
@@ -26,7 +26,7 @@ export async function adoptLinux(input: unknown, options: LinuxHandoffOptions =
26
26
  const inspect = async (unit: string): Promise<UnitState> => {
27
27
  const output = await manager("show", unit, "--property=ActiveState,UnitFileState,MainPID,InvocationID");
28
28
  const props = Object.fromEntries(output.trim().split("\n").map(line => { const i = line.indexOf("="); return [line.slice(0, i), line.slice(i + 1)]; }));
29
- if (!["active", "inactive", "failed"].includes(props.ActiveState!) || !["enabled", "disabled", "static", "", "not-found"].includes(props.UnitFileState!)) throw new Error(`Unsupported service state: ${unit}`);
29
+ if (!["active", "inactive", "failed"].includes(props.ActiveState!) || !["enabled", "disabled", "static", "transient", "", "not-found"].includes(props.UnitFileState!)) throw new Error(`Unsupported service state: ${unit}`);
30
30
  return { active: props.ActiveState === "active", enabled: props.UnitFileState === "enabled", mainPid: Number(props.MainPID || 0), invocationId: props.InvocationID! };
31
31
  };
32
32
  return withFileLock(`${journalPath}.lock`, async () => {
@@ -34,7 +34,7 @@ export async function adoptLinux(input: unknown, options: LinuxHandoffOptions =
34
34
  const saved = prior?.linux as LinuxJournal | undefined;
35
35
  if (saved && (JSON.stringify(saved.plan) !== JSON.stringify(plan) || saved.artifacts?.length !== plan.files.length || saved.services?.length !== plan.services.length
36
36
  || saved.preserved?.length !== plan.preserve.length || !Array.isArray(saved.completed))) throw new Error("Linux journal does not match manifest");
37
- if (options.rollback && !saved) throw new Error("No Linux handoff to roll back");
37
+ if (options.rollback && !saved) return { journal: journalPath, direction: "rollback", preserved: [], unchanged: true };
38
38
  const artifacts = checkArtifacts(plan, saved?.artifacts);
39
39
  const services = saved?.services ?? await Promise.all(plan.services.map(async service => {
40
40
  const canonical = await inspect(service.canonical);
@@ -15,10 +15,10 @@ export function plistLabel(content: string, expected: string): void {
15
15
  const labels = [...content.matchAll(/<key>\s*Label\s*<\/key>\s*<string>([^<]*)<\/string>/g)];
16
16
  if (labels.length !== 1 || labels[0]![1] !== expected) throw new Error(`Plist Label must match ${expected}`);
17
17
  }
18
- export function parseMacosPlan(value: unknown): MacosPlan {
18
+ export function parseMacosPlan(value: unknown, expectedHost?: string): MacosPlan {
19
19
  if (!isRecord(value) || typeof value.domain !== "string" || !/^gui\/\d+$/.test(value.domain)
20
20
  || !Array.isArray(value.services) || !value.services.length || !Array.isArray(value.files) || !Array.isArray(value.preserve)) throw new Error("Invalid macOS handoff manifest");
21
- const adoption = parseAdoptionManifest(value.adoption);
21
+ const adoption = parseAdoptionManifest(value.adoption, expectedHost);
22
22
  const plistDirectory = absolute(value.plistDirectory);
23
23
  const services = value.services.map(item => {
24
24
  if (!isRecord(item) || typeof item.legacy !== "string"
@@ -47,7 +47,7 @@ export async function adoptMacos(input: unknown, options: MacosHandoffOptions =
47
47
  if (saved && (JSON.stringify(saved.plan) !== JSON.stringify(plan) || saved.artifacts?.length !== plan.files.length || saved.services?.length !== plan.services.length
48
48
  || !saved.services.every(state => typeof state.loaded === "boolean" && typeof state.disabled === "boolean" && typeof state.canonicalDisabled === "boolean")
49
49
  || !Array.isArray(saved.completed))) throw new Error("macOS journal does not match manifest");
50
- if (options.rollback && !saved) throw new Error("No macOS handoff to roll back");
50
+ if (options.rollback && !saved) return { journal: journalPath, direction: "rollback", preserved: [], unchanged: true };
51
51
  const artifacts = checkMacosArtifacts(plan, saved?.artifacts);
52
52
  const flags = await disabled();
53
53
  const services = saved?.services ?? await Promise.all(plan.services.map(async service => {
@@ -1,6 +1,7 @@
1
+ import { mergeAgentMetaAsync } from "./db/agents-async";
1
2
  import { stringValue } from "@humwick/sdk";
2
3
  import { getManagedAgentState, getSpawnPolicy } from "./config-store";
3
- import { getAgent, getWorkspace, listWorkspaces, mergeAgentMeta, patchWorkspaceMetadataAsync } from "./db";
4
+ import { getAgent, getWorkspace, listWorkspaces, patchWorkspaceMetadataAsync } from "./db";
4
5
  import { agentGenerationKey } from "./db/agent-shutdown-in-flight";
5
6
  import { getLifecycleManager } from "./lifecycle-manager";
6
7
  import { isProvenStewardAgent, stewardHandoffReleased } from "./steward-provenance";
@@ -23,6 +24,6 @@ export async function finishWorkspaceStewardHandoff(workspace: WorkspaceRecord):
23
24
  if (!boundAgent && !boundSpawn && claimant !== agent.id) return;
24
25
  await patchWorkspaceMetadataAsync(workspace.id, { stewardClaim: null, stewardHandoffEndedAt: Date.now() });
25
26
  if (listWorkspaces().some(ws => ws.id !== workspace.id && workspaceActiveClaim(ws)?.by === agent.id)) return;
26
- const command = getLifecycleManager().stopAgent(policy, true, "steward-handoff-ended");
27
- if (command) mergeAgentMeta(agent.id, { stewardReleasedGeneration: agentGenerationKey(agent) }, { instanceId: agent.instanceId, epoch: agent.epoch });
27
+ const command = await getLifecycleManager().stopAgent(policy, true, "steward-handoff-ended");
28
+ if (command) (await mergeAgentMetaAsync(agent.id, { stewardReleasedGeneration: agentGenerationKey(agent) }, { instanceId: agent.instanceId, epoch: agent.epoch }));
28
29
  }
@@ -1,3 +1,4 @@
1
+ import { setConfigAsync, deleteConfigAsync } from "./config-store-write";
1
2
  // #1955 — ONE-SHOT, IDEMPOTENT cleanup of the steward spawn-policy rows a live relay accumulated
2
3
  // before #1623/#1953/#1955. Pure planning here; `scripts/steward-policy-cleanup.ts` is the operator
3
4
  // CLI (DRY RUN by default). Nothing in the running relay calls this — it is deliberately manual,
@@ -21,7 +22,7 @@
21
22
  // so a second run over the applied state plans nothing. Safe to re-run.
22
23
 
23
24
  import { canonicalRepoRoot, legacyStewardPolicyNames, repoStewardPolicyName } from "./steward-identity";
24
- import { deleteConfig, getSpawnPolicy, listSpawnPolicies, setConfig } from "./config-store";
25
+ import { getSpawnPolicy, listSpawnPolicies } from "./config-store";
25
26
  import { listWorkspaces } from "./db";
26
27
  import { basename } from "node:path";
27
28
  import type { SpawnPolicy } from "./types";
@@ -151,10 +152,10 @@ export interface StewardPolicyCleanupResult {
151
152
  * precondition, so a plan built against a state the live relay has since changed degrades to a skip
152
153
  * rather than clobbering the newer row.
153
154
  */
154
- export function applyStewardPolicyCleanup(
155
+ export async function applyStewardPolicyCleanup(
155
156
  plan: StewardPolicyCleanupPlan,
156
157
  opts: { dryRun: boolean; updatedBy?: string },
157
- ): StewardPolicyCleanupResult {
158
+ ): Promise<StewardPolicyCleanupResult> {
158
159
  const updatedBy = opts.updatedBy ?? "steward-policy-cleanup-1955";
159
160
  const log: string[] = [];
160
161
  let applied = 0;
@@ -184,9 +185,9 @@ export function applyStewardPolicyCleanup(
184
185
  // A stop recorded on EITHER half survives the merge — never lift an operator's stop (same
185
186
  // rule as the provision-time collapse in steward.ts).
186
187
  if (action.carriesStop && into.enabled !== false) {
187
- setConfig("spawn-policy", into.name, { ...into, enabled: false }, updatedBy);
188
+ (await setConfigAsync("spawn-policy", into.name, { ...into, enabled: false }, updatedBy));
188
189
  }
189
- deleteConfig("spawn-policy", action.policy, updatedBy);
190
+ (await deleteConfigAsync("spawn-policy", action.policy, updatedBy));
190
191
  applied++;
191
192
  }
192
193
  continue;
@@ -199,8 +200,8 @@ export function applyStewardPolicyCleanup(
199
200
  }
200
201
  log.push(`RENAME ${action.policy} -> ${action.into} (cwd ${action.cwdFrom} -> ${action.cwdTo})`);
201
202
  if (!opts.dryRun) {
202
- setConfig("spawn-policy", action.into, { ...current, name: action.into, ...repoDerivedFields(action.repoRoot) }, updatedBy);
203
- deleteConfig("spawn-policy", action.policy, updatedBy);
203
+ (await setConfigAsync("spawn-policy", action.into, { ...current, name: action.into, ...repoDerivedFields(action.repoRoot) }, updatedBy));
204
+ (await deleteConfigAsync("spawn-policy", action.policy, updatedBy));
204
205
  applied++;
205
206
  }
206
207
  continue;
@@ -213,7 +214,7 @@ export function applyStewardPolicyCleanup(
213
214
  }
214
215
  log.push(`CWD ${action.policy}: ${action.cwdFrom} -> ${action.cwdTo}`);
215
216
  if (!opts.dryRun) {
216
- setConfig("spawn-policy", action.policy, { ...current, ...repoDerivedFields(action.repoRoot) }, updatedBy);
217
+ (await setConfigAsync("spawn-policy", action.policy, { ...current, ...repoDerivedFields(action.repoRoot) }, updatedBy));
217
218
  applied++;
218
219
  }
219
220
  }
package/src/steward.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { basename } from "node:path";
2
2
  import { stewardFallbackTarget } from "./config";
3
- import { deleteConfig, getManagedAgentState, getSpawnPolicy, getStewardConfig, setConfig } from "./config-store";
3
+ import { getManagedAgentState, getSpawnPolicy, getStewardConfig } from "./config-store";
4
+ import { deleteConfigAsync, setConfigAsync } from "./config-store-write";
4
5
  import { listOrchestrators } from "./db";
5
6
  import { getLifecycleManager } from "./lifecycle-manager";
6
7
  import { renderTemplate } from "./prompt-resolver";
@@ -137,7 +138,7 @@ function stewardPolicyDiffers(existing: SpawnPolicy, desired: SpawnPolicy): bool
137
138
  * - it still has a live/transitioning managed process. Deleting the policy under a running process
138
139
  * would orphan it: `onConfigChanged` finds no policy for a deleted key, so nothing would stop it.
139
140
  */
140
- function collapseLegacyStewardPolicies(canonicalName: string, repoRoot: string, owner: Orchestrator): boolean | undefined {
141
+ async function collapseLegacyStewardPolicies(canonicalName: string, repoRoot: string, owner: Orchestrator): Promise<boolean | undefined> {
141
142
  let inheritedEnabled: boolean | undefined;
142
143
  for (const legacyName of legacyStewardPolicyNames(repoRoot, owner.id)) {
143
144
  if (legacyName === canonicalName) continue;
@@ -151,8 +152,8 @@ function collapseLegacyStewardPolicies(canonicalName: string, repoRoot: string,
151
152
  continue;
152
153
  }
153
154
  if (legacy.enabled === false) inheritedEnabled = false;
154
- deleteConfig("spawn-policy", legacyName, "steward-legacy-collapse");
155
- getLifecycleManager().onConfigChanged("spawn-policy", legacyName);
155
+ await deleteConfigAsync("spawn-policy", legacyName, "steward-legacy-collapse");
156
+ (await getLifecycleManager().onConfigChanged("spawn-policy", legacyName));
156
157
  console.warn(`[steward] collapsed legacy steward policy '${legacyName}' into '${canonicalName}' (#1955)`);
157
158
  }
158
159
  return inheritedEnabled;
@@ -165,7 +166,7 @@ function collapseLegacyStewardPolicies(canonicalName: string, repoRoot: string,
165
166
  * owns the repo. Idempotent — refreshes the policy only when config-derived fields
166
167
  * change, so it can be called on every sweep without churn.
167
168
  */
168
- export function ensureRepoSteward(repoRoot: string, owningOrchestrator?: Orchestrator): string | null {
169
+ export async function ensureRepoStewardAsync(repoRoot: string, owningOrchestrator?: Orchestrator): Promise<string | null> {
169
170
  const config = getStewardConfig();
170
171
  if (!config.enabled) return null;
171
172
  const owner = owningOrchestrator ?? listOrchestrators().find((orch) =>
@@ -177,7 +178,7 @@ export function ensureRepoSteward(repoRoot: string, owningOrchestrator?: Orchest
177
178
  const name = repoStewardPolicyName(repoRoot, owner.id);
178
179
  // #1955 mechanism 1 — merge a legacy suffix-less twin for this (repo, host) BEFORE reading the
179
180
  // canonical row, so the pair converges on one policy instead of coexisting as two.
180
- const inheritedEnabled = collapseLegacyStewardPolicies(name, repoRoot, owner);
181
+ const inheritedEnabled = await collapseLegacyStewardPolicies(name, repoRoot, owner);
181
182
  const baseDesired = desiredStewardPolicy(repoRoot, owner, config);
182
183
  const existing = getSpawnPolicy(name);
183
184
  // #1761 (9) — respect an operator's per-policy stop. desiredStewardPolicy hardcodes `enabled:true`;
@@ -193,7 +194,7 @@ export function ensureRepoSteward(repoRoot: string, owningOrchestrator?: Orchest
193
194
  : { ...baseDesired, ...(inheritedEnabled === undefined ? {} : { enabled: inheritedEnabled }) };
194
195
  if (existing && !stewardPolicyDiffers(existing.value, desired)) return name;
195
196
 
196
- setConfig("spawn-policy", name, desired, "steward-autoprovision");
197
- getLifecycleManager().onConfigChanged("spawn-policy", name);
197
+ await setConfigAsync("spawn-policy", name, desired, "steward-autoprovision");
198
+ (await getLifecycleManager().onConfigChanged("spawn-policy", name));
198
199
  return name;
199
200
  }
@@ -5,18 +5,20 @@ import { listOrchestratorsAsync } from "./db/orchestrators-async";
5
5
  import { listHostViewsAsync } from "./db/hosts-async";
6
6
  import { providerQuotaBandsFor, advertisedProvidersFrom } from "./spawn-targets";
7
7
  import { dispatchPolicyFor } from "./routing-policy-store";
8
- import { providerPlacementReady } from "./spawn-placement";
8
+ import { providerPlacementReady, spawnHostCapAdmission } from "./spawn-placement";
9
9
  import { apiOverflowContextAsync, CLAUDE_API_PROVIDER } from "./claude-api-overflow";
10
10
  import { apiExpiryPaceAsync } from "./claude-api-expiry";
11
11
  import { liveTargetAdmission } from "./dispatch-availability";
12
12
 
13
13
  /** Reader-gateway shell for production callers. The ranking core remains the pure sync function. */
14
- export type DispatchSnapshot = DispatchContext & Required<Pick<DispatchContext, "now" | "catalog" | "bands" | "policy" | "preferences" | "advertisedProviders" | "targetAvailable">> & { probeUncertainty: Set<string> };
14
+ export type DispatchSnapshot = DispatchContext & Required<Pick<DispatchContext, "now" | "catalog" | "bands" | "policy" | "preferences" | "advertisedProviders" | "targetAvailable">> & { probeUncertainty: Set<string>; pinnedHostAtAgentCap?: boolean };
15
15
 
16
16
  export async function captureDispatchContext(ctx: Omit<DispatchContext, "policy" | "bands" | "catalog"> = {}): Promise<DispatchSnapshot> {
17
17
  const now = ctx.now ?? Date.now();
18
18
  const [snapshot, orchestrators, hostViews] = await Promise.all([providerQuotaRoutingSnapshotAsync(now), listOrchestratorsAsync(), listHostViewsAsync()]);
19
- const { targetAdmission: assess, targetUnavailableReason } = liveTargetAdmission(orchestrators, hostViews, ctx.orchestratorId, ctx.spawnCap);
19
+ const { targetAdmission: assess, targetUnavailableReason } = liveTargetAdmission(orchestrators, hostViews, snapshot.catalog, ctx.orchestratorId, ctx.spawnCap);
20
+ const pinnedHost = orchestrators.find(host => host.id === ctx.orchestratorId);
21
+ const pinnedView = pinnedHost && hostViews.find(view => view.id === pinnedHost.hostId);
20
22
  const probeUncertainty = new Set<string>();
21
23
  const targetAdmission = (target: Parameters<typeof assess>[0]) => {
22
24
  const result = assess(target);
@@ -42,6 +44,7 @@ export async function captureDispatchContext(ctx: Omit<DispatchContext, "policy"
42
44
  targetUnavailableReason,
43
45
  targetAdmission,
44
46
  probeUncertainty,
47
+ pinnedHostAtAgentCap: Boolean(pinnedView && spawnHostCapAdmission(ctx.spawnCap)(pinnedView)),
45
48
  bands,
46
49
  policy: dispatchPolicyFor(snapshot.policy),
47
50
  catalog: snapshot.catalog,
@@ -1,4 +1,4 @@
1
- import { isOpenAiApiModel } from "@humwick/sdk";
1
+ import { isOpenAiApiModel, ValidationError } from "@humwick/sdk";
2
2
  // #839 — the declarative task dispatcher.
3
3
  //
4
4
  // A pure function from (Task work-shape + live system state) → a concrete spawn
@@ -310,8 +310,8 @@ export function dispatchTask(shape: TaskDispatch | undefined, pin: DispatchPin =
310
310
  if (eligibility(target.provider)) throw routingResolutionError(preferences, catalog, eligibility);
311
311
  if (ctx.mustResolveAvailable && bandRank(bandFor(target.provider, bands)) > bandRank("normal")) warnings.push(`[CRITICAL] mustResolveAvailable: degraded to ${target.provider} (${bandFor(target.provider, bands)} band). Contention likely.`);
312
312
  }
313
- if (!modelPinned && ctx.targetAvailable && !ctx.targetAvailable(target)) {
314
- throw new Error(ctx.targetUnavailableReason?.(target) ?? `No eligible online host runs ${target.provider}/${target.model ?? "default"}${target.effort ? ` at effort ${target.effort}` : ""}.`);
313
+ if (ctx.targetAvailable && !ctx.targetAvailable(target)) {
314
+ throw new ValidationError(ctx.targetUnavailableReason?.(target) ?? `No eligible online host runs ${target.provider}/${target.model ?? "default"}${target.effort ? ` at effort ${target.effort}` : ""}.`);
315
315
  }
316
316
  if (factors.preferences) factors.preferences = { ...factors.preferences, excluded: preferenceExclusions(preferences, factors.ranking, (p) => eligibility(p) ?? (!advertised.has(p) ? "not-advertised" : undefined)) };
317
317
  // Record the FINAL provider's band + the chosen model's window for transparency.
@@ -1,4 +1,5 @@
1
- import { getConfig, setConfig } from "./config-store";
1
+ import { getConfig } from "./config-store";
2
+ import { setConfigAsync } from "./config-store-write";
2
3
  import { emitConfigChanged } from "./sse";
3
4
  import {
4
5
  DEFAULT_TEAMS_CONFIG,
@@ -14,9 +15,9 @@ export function getTeamsConfig(): TeamsConfig {
14
15
  return entry ? normalizeTeamsConfig(entry.value) : { ...DEFAULT_TEAMS_CONFIG };
15
16
  }
16
17
 
17
- export function setTeamsConfig(value: unknown, opts: { updatedBy?: string } = {}): TeamsConfig {
18
+ export async function setTeamsConfigAsync(value: unknown, opts: { updatedBy?: string } = {}): Promise<TeamsConfig> {
18
19
  const normalized = normalizeTeamsConfig(value);
19
- const entry = setConfig(TEAMS_CONFIG_NAMESPACE, TEAMS_CONFIG_KEY, normalized, opts.updatedBy);
20
+ const entry = await setConfigAsync(TEAMS_CONFIG_NAMESPACE, TEAMS_CONFIG_KEY, normalized, opts.updatedBy);
20
21
  emitConfigChanged(entry.namespace, entry.key, entry.version);
21
22
  return normalized;
22
23
  }
@@ -13,6 +13,7 @@ import { getGateway } from "./db/gateway.ts";
13
13
  import type { CreateTokenInput, HandoffTokenInput, PruneExpiredTokenRecordsInput, TokenHandoff } from "./token-db.ts";
14
14
  import type { TokenRevokeReason } from "./security.ts";
15
15
  import type { TokenRecord } from "./types.ts";
16
+ import type { LaunchFact } from "./db/launch-facts.ts";
16
17
 
17
18
  /** Mint a component token off-loop. The `jti` is generated here so it is BOTH the token's id and the
18
19
  * gateway idempotency key that satisfies the non-idempotent-op retry-safety gate (H5 §3.7.1). */
@@ -24,6 +25,15 @@ export function createTokenAsync(input: CreateTokenInput): Promise<{ token: stri
24
25
  }>;
25
26
  }
26
27
 
28
+ /** Mint a runner token and, for a fresh launch, record its launch fact, in one writer op (#1870 S7). */
29
+ export function createRunnerTokenAsync(input: CreateTokenInput, launch?: { agentId: string; fact: LaunchFact }): Promise<{ token: string; record: TokenRecord }> {
30
+ const jti = randomUUID();
31
+ return getGateway().write("tokens.createRunner", { input, jti, ...(launch ? { launch } : {}) }, { idempotencyKey: jti }) as unknown as Promise<{
32
+ token: string;
33
+ record: TokenRecord;
34
+ }>;
35
+ }
36
+
27
37
  /** Hand a token off to its successor off-loop (#1245): fold the eligibility read, then mint and link the
28
38
  * successor or replay the pending one — atomic in one op (ADR §3.4). Resolves `null` when ineligible. */
29
39
  export function handoffTokenAsync(input: HandoffTokenInput): Promise<TokenHandoff | null> {
package/src/token-db.ts CHANGED
@@ -324,8 +324,8 @@ export function getTokenProfile(id: string): TokenProfile | null {
324
324
  return row ? rowToProfile(row) : null;
325
325
  }
326
326
 
327
- export function upsertTokenProfile(input: CreateTokenProfileInput): TokenProfile {
328
- const now = Date.now();
327
+ export function upsertTokenProfile(input: CreateTokenProfileInput, submittedAt: number = Date.now()): TokenProfile {
328
+ const now = submittedAt;
329
329
  const id = input.id ?? slugify(input.name);
330
330
  const existing = getTokenProfile(id);
331
331
  if (existing?.builtIn) throw new Error("built-in token profiles cannot be modified");
@@ -5,12 +5,10 @@ import type { Message } from "./types";
5
5
  // of src/db directly (the transport-thinness ratchet, #348).
6
6
  import { getGateway } from "./db/gateway.ts";
7
7
  import type { SpawnBudgetReadback, WorkflowRunBudgetReadback } from "./db/spawn-budget.ts";
8
- import type { aggregateSessionTokenUsage, recordSessionTokenUsage, SessionTokenUsageAggregate, SessionTokenUsageRecord } from "./db/token-usage.ts";
8
+ import type { aggregateSessionTokenUsage, recordSessionTokenUsage, SessionTokenUsageAggregate } from "./db/token-usage.ts";
9
9
 
10
10
  export const recordSessionTokenUsageAsync = async (input: Parameters<typeof recordSessionTokenUsage>[0]) => {
11
- const { budgetNotices, ...result } = await getGateway().write("tokenUsage.record", { input }, { critical: false }) as unknown as {
12
- record: SessionTokenUsageRecord;
13
- inserted: boolean;
11
+ const { budgetNotices, ...result } = await getGateway().write("tokenUsage.record", { input }, { critical: false }) as unknown as ReturnType<typeof recordSessionTokenUsage> & {
14
12
  budgetNotices: Message[];
15
13
  };
16
14
  publishBudgetNotices(budgetNotices);
@@ -1,7 +1,7 @@
1
+ import { framed } from "./hub-metrics";
1
2
  import { userSinkId, canonicalHumanAgentId } from "@humwick/sdk";
2
3
  import { listUsers } from "./db/users";
3
- import { listPendingReplyObligationsAsync } from "./db/message-reads-async";
4
- import { getLatestMessageIdAsync } from "./db/message-reads-async";
4
+ import { listPendingReplyObligationsAsync, getLatestMessageIdAsync } from "./db/message-reads-async";
5
5
  import { optedInNotificationRecipients } from "./db/notification-subscriptions";
6
6
  import { routeNotification } from "./notification-router";
7
7
  import { subscribeRelayEvents } from "./events";
@@ -12,7 +12,7 @@ export async function reconcileUserAwaitingReply(previous?: string): Promise<str
12
12
  const [pending, latestId] = await Promise.all([Promise.all(listUsers().map(user => listPendingReplyObligationsAsync(canonicalHumanAgentId(userSinkId(user.id)), 1, true))).then(rows => rows.flat()), getLatestMessageIdAsync()]);
13
13
  const signature = JSON.stringify([recipients, pending.length > 0]);
14
14
  if (signature === previous) return signature;
15
- routeNotification({
15
+ await routeNotification({
16
16
  type: "user.awaiting_reply", scope: {}, recipients: [],
17
17
  message: {
18
18
  body: pending.length ? "The operator has an unanswered message." : "The operator has no unanswered messages.",
@@ -30,13 +30,13 @@ export function startUserAwaitingReply(): () => void {
30
30
  const unsubscribe = subscribeRelayEvents(event => {
31
31
  if (event.type.startsWith("message.") && event.data.to !== "hue:office") dirty = true;
32
32
  });
33
- const timer = setInterval(() => {
33
+ const timer = setInterval(framed("timer user-awaiting-reply", () => {
34
34
  ticks++;
35
35
  if ((!dirty && ticks % 30 !== 0) || running) return;
36
36
  dirty = false; running = true;
37
37
  void reconcileUserAwaitingReply(signature).then(value => { signature = value; }).catch(error => {
38
38
  dirty = true; console.error("user.awaiting_reply reconciliation failed", error);
39
39
  }).finally(() => { running = false; });
40
- }, 1000);
40
+ }), 1000);
41
41
  return () => { clearInterval(timer); unsubscribe(); };
42
42
  }