@humwick/server 0.130.2 → 0.130.4

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 (587) hide show
  1. package/bundled/prompts/manifest.json +1 -0
  2. package/bundled/prompts/task.hydration.md +1 -1
  3. package/bundled/roles/builder.md +1 -1
  4. package/docs/guides/sub-coordinators.md +12 -5
  5. package/docs/guides/tools-coordination.md +1 -0
  6. package/docs/guides/workflows.md +16 -0
  7. package/docs/openapi.json +175 -4
  8. package/package.json +8 -7
  9. package/public/assets/{MessageBubble-BWLqMGKG.js → MessageBubble-BOkmEvgZ.js} +2 -2
  10. package/public/assets/{PlanGraphPanel-3TlIQpjk.js → PlanGraphPanel-BkaDJp0r.js} +2 -2
  11. package/public/assets/{TraceView-Wimm0Czv.js → TraceView-B99Uu2yT.js} +2 -2
  12. package/public/assets/{VaultDecisionCard-Bau_vNax.js → VaultDecisionCard-DUIfTTFF.js} +2 -2
  13. package/public/assets/{activity-CrSurn-r.js → activity-ZIX0N1El.js} +2 -2
  14. package/public/assets/{agent-lineage-CknAJKcb.js → agent-lineage-kq9TPYFw.js} +2 -2
  15. package/public/assets/agent-liveness-DSQY1Aa_.js +2 -0
  16. package/public/assets/{agent-name-DKbiIhBo.js → agent-name-C_7F4DFR.js} +2 -2
  17. package/public/assets/{agent-profiles-DwF6EQ4R.js → agent-profiles-BNOcrGcY.js} +2 -2
  18. package/public/assets/{agent-project-badge-Diz4Sjjb.js → agent-project-badge-B6yAh70a.js} +2 -2
  19. package/public/assets/{agent-tools-CRhpRQza.js → agent-tools-CcrhmOzF.js} +2 -2
  20. package/public/assets/{agent-type-icon-LrUEU3lB.js → agent-type-icon-CXSLrKiq.js} +2 -2
  21. package/public/assets/{agents-B-v66C-4.js → agents-DIZG97_X.js} +2 -2
  22. package/public/assets/{analytics-BqCb-LKi.js → analytics-rasmoPjR.js} +2 -2
  23. package/public/assets/{artifact-viewer-kf4pGuAR.js → artifact-viewer-tBpTrEPb.js} +2 -2
  24. package/public/assets/{artifacts-DJevXcvg.js → artifacts-BNkrynP1.js} +2 -2
  25. package/public/assets/{artifacts-D32zyw4Y.js → artifacts-Bjq-ZyBA.js} +2 -2
  26. package/public/assets/{automation-BpRajx_5.js → automation-Cx422L3L.js} +2 -2
  27. package/public/assets/{backlog-D_UjTdzX.js → backlog-RuFQ8PUu.js} +2 -2
  28. package/public/assets/{busy-health-N3VEh4ao.js → busy-health-BvNQ9g5x.js} +2 -2
  29. package/public/assets/{channels-9jopungq.js → channels-DNbKAS0I.js} +2 -2
  30. package/public/assets/{chat-CSXrTB3C.js → chat-Bp3Fx73h.js} +3 -3
  31. package/public/assets/{code-preview-p1NnmqK0.js → code-preview-KIRZA6cz.js} +2 -2
  32. package/public/assets/{connectors-CZdqlDNL.js → connectors-k7vO4vvc.js} +2 -2
  33. package/public/assets/{cosmos-BgdsE4dG.js → cosmos-BuVoq1aw.js} +2 -2
  34. package/public/assets/{crate-agent-30aXTKva.js → crate-agent-irqlt4NH.js} +2 -2
  35. package/public/assets/{dependency-graph-B_jvw2GE.js → dependency-graph-D_rurNBz.js} +2 -2
  36. package/public/assets/{deploys-DoN6bAb5.js → deploys-yzZqIpxE.js} +2 -2
  37. package/public/assets/{display-BrKWFGJz.js → display-BemJG1f_.js} +2 -2
  38. package/public/assets/{files-D6XEu8Ng.js → files-DXWcrxOz.js} +2 -2
  39. package/public/assets/{fleet-C7jQWbw2.js → fleet-TEuOCZXE.js} +2 -2
  40. package/public/assets/{fleet-metrics-BiDs3MLx.js → fleet-metrics-BGoDAFI-.js} +2 -2
  41. package/public/assets/{formatted-body-impl-DyG0WsQS.js → formatted-body-impl-BB9eq9nN.js} +2 -2
  42. package/public/assets/{formatted-body-CQBwc1fb.js → formatted-body-qy2nTD_1.js} +3 -3
  43. package/public/assets/{host-providers-dialog-DKaW31dN.js → host-providers-dialog-BGPHxuwT.js} +2 -2
  44. package/public/assets/{index-CRHep3M9.js → index-BC5XZc0z.js} +5 -5
  45. package/public/assets/{index-MYAIwYTX.css → index-D8Rp9pz2.css} +1 -1
  46. package/public/assets/{insights-CzrtkpOE.js → insights-C-KQG0h-.js} +2 -2
  47. package/public/assets/{integrations-aVsUmigt.js → integrations-ClPRPyKU.js} +2 -2
  48. package/public/assets/{issue-ref-chip-BnSlM_95.js → issue-ref-chip-DbH8J9Ak.js} +2 -2
  49. package/public/assets/{log-viewer-qe7Ee47Z.js → log-viewer-PH4zVn24.js} +2 -2
  50. package/public/assets/{maintenance-NfjqFZAk.js → maintenance-5lV43uTL.js} +2 -2
  51. package/public/assets/{managed-agents-BiH0HgaE.js → managed-agents-BdgU35aX.js} +2 -2
  52. package/public/assets/{markdown-preview-BixaT3Xr.js → markdown-preview-DNT4OUwQ.js} +3 -3
  53. package/public/assets/{markdown-preview-impl-DNGlWIS3.js → markdown-preview-impl-CnCcPy0A.js} +2 -2
  54. package/public/assets/{memory-DNsCl6UX.js → memory--GgWH1gH.js} +2 -2
  55. package/public/assets/{messages-DopU1L1X.js → messages-CzFl_97O.js} +2 -2
  56. package/public/assets/{orchestrators-Cq7ckLQT.js → orchestrators-CVvJVkzv.js} +2 -2
  57. package/public/assets/overview-DsrZvE86.js +2 -0
  58. package/public/assets/{pairs-DE_m6rrE.js → pairs-D2d0d2py.js} +2 -2
  59. package/public/assets/{profile-icon-B3rfh77F.js → profile-icon-_SMuSbYF.js} +2 -2
  60. package/public/assets/{project-profile-instructions-DQWZE0kA.js → project-profile-instructions-Apn15y43.js} +2 -2
  61. package/public/assets/{projects-Br2Bd2h1.js → projects-D3B6kto5.js} +2 -2
  62. package/public/assets/{prompt-settings-BKvFsvGD.js → prompt-settings-DnPB6Ez6.js} +2 -2
  63. package/public/assets/provider-display-bWwiuedV.js +2 -0
  64. package/public/assets/{registry-Cw38_6gW.js → registry-D790cIhN.js} +2 -2
  65. package/public/assets/run-card-BCro-Odj.js +2 -0
  66. package/public/assets/{secrets-DxI-vzSq.js → secrets-CVXvfvbU.js} +2 -2
  67. package/public/assets/{service-access-settings-D9s_VJbd.js → service-access-settings-D3Xxqb9w.js} +2 -2
  68. package/public/assets/{settings-CKZTVGIx.js → settings-CoUC7Jci.js} +2 -2
  69. package/public/assets/shipping-D_Lp9n_J.js +2 -0
  70. package/public/assets/{stats-DyUBKyWI.js → stats-CXiNjB9S.js} +2 -2
  71. package/public/assets/{status-dot-DYqlEUhJ.js → status-dot-C703YIy1.js} +2 -2
  72. package/public/assets/store-BgDALGIX.js +9 -0
  73. package/public/assets/{tasks-Bh7Chr4-.js → tasks-DO0WpJCx.js} +2 -2
  74. package/public/assets/{teams-bowI7qVY.js → teams-FGkdi_tL.js} +2 -2
  75. package/public/assets/{terminal-viewer-BgVLHdsU.js → terminal-viewer-Dh8dSC7e.js} +3 -3
  76. package/public/assets/{terminal-viewer-impl-Dz22UPy6.js → terminal-viewer-impl-B5Zriip3.js} +2 -2
  77. package/public/assets/{use-agent-terminal-DSQQ-iY7.js → use-agent-terminal-CRUVDvLC.js} +2 -2
  78. package/public/assets/{use-decisions-samMQhQf.js → use-decisions-CoaqRh4l.js} +2 -2
  79. package/public/assets/{use-project-list-59LheVP7.js → use-project-list-X-BgT53X.js} +2 -2
  80. package/public/assets/{use-selectors-BZa25xEB.js → use-selectors-0X_UZkk-.js} +2 -2
  81. package/public/assets/{user-avatar-DHxlvUvV.js → user-avatar-CkLhVBGu.js} +2 -2
  82. package/public/assets/{versioned-prompt-editor-BmED5DnJ.js → versioned-prompt-editor-BkTi7eim.js} +2 -2
  83. package/public/assets/{work-queue-CSvpuNLz.js → work-queue-DDOLEyKo.js} +2 -2
  84. package/public/assets/{workflow-marker-DAZcj5qt.js → workflow-marker-CzOsB8BM.js} +2 -2
  85. package/public/assets/{workflows-DdmxRSyg.js → workflows-DJilvmRM.js} +2 -2
  86. package/public/assets/{workspaces-BxeKfVuF.js → workspaces-CHcfbLR3.js} +2 -2
  87. package/public/cosmos.html +4 -4
  88. package/public/index.html +29 -29
  89. package/runner/plugins/claude/.claude-plugin/plugin.json +1 -1
  90. package/runner/src/adapters/claude-api.ts +1 -1
  91. package/runner/src/adapters/claude-headless.ts +1 -1
  92. package/runner/src/adapters/claude-launch-exit.ts +26 -0
  93. package/runner/src/adapters/claude-session-probe.ts +1 -1
  94. package/runner/src/adapters/claude-tmux.ts +38 -10
  95. package/runner/src/adapters/claude.ts +28 -75
  96. package/runner/src/adapters/provider-quota-harvest.ts +20 -71
  97. package/runner/src/agent-runner/lifecycle-milestones.ts +2 -2
  98. package/runner/src/agent-runner/liveness-exchange.ts +104 -0
  99. package/runner/src/claude-system-prompt.ts +49 -0
  100. package/runner/src/launch-assembly.ts +14 -10
  101. package/runner/src/profile-home.ts +1 -1
  102. package/runner/src/project-instructions.ts +2 -2
  103. package/runner/src/provider-command.ts +18 -0
  104. package/runner/src/provider-setup.ts +3 -2
  105. package/runner/src/runner-core.ts +14 -15
  106. package/runner/src/runner-helpers.ts +0 -8
  107. package/runner/src/session-scratch.ts +6 -2
  108. package/runner/test-support/provisioning-context.ts +4 -2
  109. package/runner/test-support/runner-harness.ts +2 -1
  110. package/scripts/steward-policy-cleanup.ts +2 -2
  111. package/src/agent-authz.ts +2 -2
  112. package/src/agent-drive-arbiter.ts +3 -8
  113. package/src/agent-exit-cleanup-async.ts +2 -2
  114. package/src/agent-exit-cleanup.ts +23 -19
  115. package/src/agent-lifecycle-events.ts +112 -85
  116. package/src/agent-ref.ts +3 -2
  117. package/src/agent-status-events.ts +43 -14
  118. package/src/auth/login-link.ts +35 -0
  119. package/src/auth/password.ts +4 -3
  120. package/src/auth/session-refresh.ts +1 -1
  121. package/src/auth/session.test-fixture.ts +2 -2
  122. package/src/auth/session.ts +7 -7
  123. package/src/automations/crud.ts +8 -81
  124. package/src/automations/dispatch.ts +33 -58
  125. package/src/automations/index.ts +1 -1
  126. package/src/automations/persistence.ts +65 -0
  127. package/src/automations/precheck.ts +18 -16
  128. package/src/automations/read-async.ts +11 -0
  129. package/src/automations/reconcile.ts +30 -39
  130. package/src/automations/storage.ts +33 -10
  131. package/src/automations/write-async.ts +33 -0
  132. package/src/branch-land-notice.ts +3 -3
  133. package/src/branch-landed.ts +25 -23
  134. package/src/bus.ts +21 -19
  135. package/src/cd-deploy-notify.ts +2 -2
  136. package/src/ci/notifications.ts +4 -4
  137. package/src/ci/root-missing.ts +3 -3
  138. package/src/ci/transition-notify.ts +5 -5
  139. package/src/claude-app/oauth-http.ts +3 -3
  140. package/src/cli/index.ts +7 -1
  141. package/src/cli/init.ts +53 -0
  142. package/src/cli/orchestrator.ts +1 -7
  143. package/src/cli/setup.ts +6 -4
  144. package/src/cli/workflow.ts +12 -3
  145. package/src/commands-db.ts +4 -4
  146. package/src/commands-write.ts +8 -0
  147. package/src/compaction-watch.ts +10 -8
  148. package/src/config-store-write.ts +2 -2
  149. package/src/config.ts +4 -11
  150. package/src/connectors.ts +10 -8
  151. package/src/context-advisory.ts +22 -21
  152. package/src/db/agent-action-write.ts +36 -0
  153. package/src/db/agent-control-async.ts +9 -0
  154. package/src/db/agent-control-write.ts +49 -0
  155. package/src/db/agent-effort-observe.ts +22 -0
  156. package/src/db/agent-meta.ts +14 -3
  157. package/src/db/agents-async.ts +8 -5
  158. package/src/db/agents.ts +15 -12
  159. package/src/db/auth-identities.ts +2 -2
  160. package/src/db/automation-read-ops.ts +11 -0
  161. package/src/db/automation-write-ops.ts +46 -0
  162. package/src/db/blackboard.ts +2 -2
  163. package/src/db/browser-session-write.ts +32 -0
  164. package/src/db/bus-outbox-write-ops.ts +3 -1
  165. package/src/db/checkout-refresh-schema.ts +47 -0
  166. package/src/db/checkout-refresh.ts +5 -5
  167. package/src/db/command-agent-profile-backfill.ts +39 -0
  168. package/src/db/command-agent-profiles.ts +5 -1
  169. package/src/db/command-retention.ts +126 -0
  170. package/src/db/commands-planner-stats.test-fixture.ts +8 -0
  171. package/src/db/commands-read-ops.ts +16 -0
  172. package/src/db/connection.ts +1 -182
  173. package/src/db/context-carry-write.ts +74 -0
  174. package/src/db/continuations.ts +4 -4
  175. package/src/db/db-error-registry.ts +12 -0
  176. package/src/db/gateway.ts +26 -133
  177. package/src/db/history-retention-ops.ts +13 -0
  178. package/src/db/inbox-async.ts +2 -2
  179. package/src/db/inbox-read-ops.ts +1 -1
  180. package/src/db/inbox.ts +9 -3
  181. package/src/db/issue-associations.ts +2 -2
  182. package/src/db/issue-refs-async.ts +4 -1
  183. package/src/db/issue-refs-read-ops.ts +6 -0
  184. package/src/db/issue-refs.ts +8 -8
  185. package/src/db/kernel-event-log-schema.ts +2 -0
  186. package/src/db/land-boundaries.ts +51 -5
  187. package/src/db/land-gate-attestations.ts +2 -2
  188. package/src/db/landing-receipts.ts +2 -2
  189. package/src/db/lifecycle-context-write.ts +5 -0
  190. package/src/db/lifecycle-tail-async.ts +76 -0
  191. package/src/db/lifecycle-tail-write-ops.ts +62 -0
  192. package/src/db/lifecycle-write-window.ts +2 -1
  193. package/src/db/lineage-pair-dedupe-sql.ts +3 -3
  194. package/src/db/main-connection-mode.ts +11 -0
  195. package/src/db/main-connection-writes.ts +20 -2
  196. package/src/db/maintenance-job-runs-async.ts +2 -2
  197. package/src/db/maintenance-job-write-ops.ts +1 -1
  198. package/src/db/maintenance-pauses.ts +2 -2
  199. package/src/db/message-send.ts +4 -0
  200. package/src/db/messages.ts +31 -25
  201. package/src/db/migrations.ts +18 -1
  202. package/src/db/notification-write-ops.ts +19 -0
  203. package/src/db/operator-answer-write.ts +19 -0
  204. package/src/db/orchestrators.ts +10 -4
  205. package/src/db/pairs-async.ts +22 -1
  206. package/src/db/pairs-write-ops.ts +8 -3
  207. package/src/db/pairs.ts +41 -48
  208. package/src/db/plan-dispatch-schema.ts +5 -1
  209. package/src/db/plan-ready-notices.ts +43 -0
  210. package/src/db/plan-tools-include-migration.ts +36 -0
  211. package/src/db/plans-write-ops.ts +2 -0
  212. package/src/db/presence-buffer.ts +5 -0
  213. package/src/db/presence-loop.ts +18 -23
  214. package/src/db/presence-write.ts +0 -6
  215. package/src/db/project-entries-async.ts +14 -0
  216. package/src/db/project-entries-read-ops.ts +5 -1
  217. package/src/db/project-entries-write-ops.ts +8 -0
  218. package/src/db/project-entries.ts +15 -12
  219. package/src/db/project-journal.ts +2 -2
  220. package/src/db/project-trust-approvals.ts +2 -2
  221. package/src/db/prompt-templates.ts +4 -4
  222. package/src/db/read-op-marshaling-fixtures.ts +18 -0
  223. package/src/db/read-ops.ts +4 -0
  224. package/src/db/retention.ts +13 -2
  225. package/src/db/review-publish-write.ts +8 -0
  226. package/src/db/runtime-admin-async.ts +19 -0
  227. package/src/db/runtime-admin-write-ops.ts +36 -0
  228. package/src/db/runtime-tail-async.ts +288 -0
  229. package/src/db/runtime-tail-write-ops.ts +97 -0
  230. package/src/db/schema.ts +4 -1
  231. package/src/db/spawn-budget.ts +5 -0
  232. package/src/db/spawn-delegation.ts +3 -12
  233. package/src/db/sql-audit-indexes.ts +3 -0
  234. package/src/db/stats-async.ts +5 -3
  235. package/src/db/task-entity.ts +5 -4
  236. package/src/db/team-claim-policy.ts +60 -0
  237. package/src/db/teams-async.ts +4 -0
  238. package/src/db/teams-write-ops.ts +4 -0
  239. package/src/db/terminal-events.ts +15 -0
  240. package/src/db/timer-cutover-bodies.ts +292 -0
  241. package/src/db/timer-cutover-core.ts +37 -278
  242. package/src/db/timer-cutover-state.ts +40 -0
  243. package/src/db/timer-cutover-write-ops.ts +1 -40
  244. package/src/db/token-db-write-ops.ts +2 -0
  245. package/src/db/token-standing-async.ts +12 -0
  246. package/src/db/token-standing.ts +6 -9
  247. package/src/db/token-usage-retention.ts +33 -0
  248. package/src/db/token-usage-write-ops.ts +1 -1
  249. package/src/db/token-usage.ts +4 -1
  250. package/src/db/user-invites-async.ts +13 -0
  251. package/src/db/user-invites.ts +3 -3
  252. package/src/db/users.ts +14 -6
  253. package/src/db/workflow-execution.ts +3 -3
  254. package/src/db/workspaces.ts +8 -5
  255. package/src/db/write-ops.ts +31 -12
  256. package/src/db/writer-fault-seam.ts +2 -9
  257. package/src/db/writer-fault-test-ops.ts +18 -0
  258. package/src/db/writer-gateway.ts +42 -46
  259. package/src/dev.ts +12 -16
  260. package/src/event-loop-lag.ts +4 -2
  261. package/src/event-loop-yield.ts +2 -2
  262. package/src/events.ts +10 -8
  263. package/src/host-provider-store.ts +5 -4
  264. package/src/hub-metrics-report.ts +5 -1
  265. package/src/hub-metrics.ts +38 -18
  266. package/src/init.ts +299 -0
  267. package/src/injection-sweep-pacing.ts +6 -8
  268. package/src/invite-link.ts +16 -9
  269. package/src/land-gate-notifications.ts +11 -10
  270. package/src/land-gate-queue-progress.ts +3 -3
  271. package/src/land-settle-dispatch.ts +52 -92
  272. package/src/lifecycle-context-pressure.ts +11 -10
  273. package/src/lifecycle-manager.ts +104 -98
  274. package/src/maintenance/breaker.ts +18 -16
  275. package/src/maintenance/checkout-refresh.ts +18 -1
  276. package/src/maintenance/critical-job-watchdog.ts +3 -3
  277. package/src/maintenance/events.ts +3 -3
  278. package/src/maintenance/history-retention-job.ts +30 -0
  279. package/src/maintenance/jobs.ts +29 -67
  280. package/src/maintenance/long-running-work.ts +3 -1
  281. package/src/maintenance/offline-agent-prune-job.ts +28 -0
  282. package/src/maintenance/orphaned-sessions.ts +5 -4
  283. package/src/maintenance/pauses.ts +4 -10
  284. package/src/maintenance/plan-ready-notice-job.ts +71 -0
  285. package/src/maintenance/scheduler.ts +18 -13
  286. package/src/maintenance/stalled-delivery-pager.ts +4 -4
  287. package/src/maintenance/teams.ts +19 -30
  288. package/src/maintenance/workspaces.ts +17 -27
  289. package/src/managed-policy-escalation.ts +7 -7
  290. package/src/managed-policy.ts +3 -3
  291. package/src/managed-provider-gate.ts +5 -4
  292. package/src/managed-spawn-reservation.ts +5 -4
  293. package/src/mcp/index.ts +8 -8
  294. package/src/mcp/output-descriptions.ts +1 -1
  295. package/src/mcp/presentation.ts +2 -1
  296. package/src/mcp/runtime-output.ts +6 -1
  297. package/src/mcp/self-care-visibility.ts +2 -2
  298. package/src/mcp/spawn-targets-matrix.ts +10 -1
  299. package/src/mcp/tool-groups.ts +3 -2
  300. package/src/mcp/tool-roles.ts +81 -0
  301. package/src/mcp/tools-workspace.ts +1 -1
  302. package/src/mcp-blackboard-tools.ts +6 -5
  303. package/src/mcp-decision-tools.ts +7 -7
  304. package/src/mcp-egress-tools.ts +10 -9
  305. package/src/mcp-issue-activity-tools.ts +2 -2
  306. package/src/mcp-issue-association-tools.ts +4 -4
  307. package/src/mcp-issue-ref-tools.ts +3 -3
  308. package/src/mcp-merge-tools.ts +5 -5
  309. package/src/mcp-notification-subscription-tools.ts +14 -15
  310. package/src/mcp-output-levels.test-fixture.ts +1 -1
  311. package/src/mcp-profile-trust-tools.ts +1 -1
  312. package/src/mcp-project-journal-tools.ts +4 -12
  313. package/src/mcp-project-tools.ts +17 -18
  314. package/src/mcp-routing-policy-tools.ts +3 -2
  315. package/src/mcp-team-tools.ts +6 -6
  316. package/src/mcp-workflow-tools.ts +5 -3
  317. package/src/memory-area-injection.ts +13 -12
  318. package/src/memory-boundary-capture.ts +6 -6
  319. package/src/memory-injection.ts +45 -43
  320. package/src/merge-pause-maintenance.ts +5 -4
  321. package/src/notification-router.ts +44 -17
  322. package/src/notify.ts +5 -5
  323. package/src/observed-reset-refresh.ts +4 -4
  324. package/src/operator-alarm.ts +2 -5
  325. package/src/project-ingest.ts +6 -6
  326. package/src/project-instructions.ts +8 -1
  327. package/src/project-trust.ts +12 -11
  328. package/src/prompt-resolver.ts +3 -3
  329. package/src/provider-quota-async.ts +5 -7
  330. package/src/provider-timeline-event.ts +4 -7
  331. package/src/quota-band-delivery.ts +2 -2
  332. package/src/recipe-runner.ts +4 -3
  333. package/src/retention-config-store.ts +4 -3
  334. package/src/reviewer-pipeline.ts +11 -10
  335. package/src/routes/_route.ts +4 -4
  336. package/src/routes/_shared.ts +13 -12
  337. package/src/routes/agent-effort.ts +5 -5
  338. package/src/routes/agent-heartbeat-quota.ts +23 -0
  339. package/src/routes/agent-keep.ts +1 -1
  340. package/src/routes/agent-reporting.ts +1 -1
  341. package/src/routes/agent-self-effort.ts +5 -5
  342. package/src/routes/agent-sessions.ts +24 -24
  343. package/src/routes/agents-spawn.ts +1 -1
  344. package/src/routes/agents.ts +17 -119
  345. package/src/routes/auth.ts +44 -17
  346. package/src/routes/authority-audit.ts +2 -2
  347. package/src/routes/automations.ts +6 -5
  348. package/src/routes/channel-binding-delete.ts +1 -1
  349. package/src/routes/channel-output.ts +3 -3
  350. package/src/routes/claude-api-budget.ts +5 -5
  351. package/src/routes/commands-listing.ts +1 -1
  352. package/src/routes/commands.ts +29 -27
  353. package/src/routes/context-resets.ts +2 -2
  354. package/src/routes/coordinator-teams.ts +1 -1
  355. package/src/routes/decisions.ts +2 -2
  356. package/src/routes/host-providers.ts +18 -0
  357. package/src/routes/host-secret-files.ts +1 -1
  358. package/src/routes/hosts.ts +11 -11
  359. package/src/routes/inbox.ts +3 -2
  360. package/src/routes/index.ts +7 -3
  361. package/src/routes/integrations.ts +9 -7
  362. package/src/routes/issue-activity.ts +2 -2
  363. package/src/routes/issue-associations.ts +4 -4
  364. package/src/routes/issue-refs.ts +3 -3
  365. package/src/routes/kernel-timers-operator.ts +1 -1
  366. package/src/routes/memory.ts +1 -1
  367. package/src/routes/merges.ts +11 -11
  368. package/src/routes/messages.ts +23 -17
  369. package/src/routes/orchestrator-bootstrap.ts +3 -1
  370. package/src/routes/orchestrator-host-registration.ts +2 -0
  371. package/src/routes/orchestrator-host-utilization.ts +2 -2
  372. package/src/routes/orchestrator-proxy.ts +2 -2
  373. package/src/routes/orchestrator.ts +27 -18
  374. package/src/routes/pairs.ts +8 -8
  375. package/src/routes/project-profile-instructions.ts +1 -1
  376. package/src/routes/project-trust.ts +1 -1
  377. package/src/routes/projects.ts +16 -16
  378. package/src/routes/prompt-templates.ts +12 -12
  379. package/src/routes/provider-quotas.ts +10 -10
  380. package/src/routes/run-command.ts +1 -1
  381. package/src/routes/secrets-backup.ts +4 -5
  382. package/src/routes/secrets-import.ts +3 -4
  383. package/src/routes/secrets-vault.ts +12 -14
  384. package/src/routes/settings.ts +1 -1
  385. package/src/routes/shipping.ts +9 -1
  386. package/src/routes/spawn-policy.ts +8 -7
  387. package/src/routes/sse.ts +2 -2
  388. package/src/routes/team-templates.ts +7 -7
  389. package/src/routes/teams.ts +10 -10
  390. package/src/routes/tokens.ts +18 -17
  391. package/src/routes/users.ts +24 -24
  392. package/src/routes/viewer-read.ts +3 -3
  393. package/src/routes/web-tool-calls.ts +4 -4
  394. package/src/routes/workflows.ts +2 -2
  395. package/src/routes/workspaces.ts +17 -16
  396. package/src/routing-policy-push.ts +6 -6
  397. package/src/runtime-grant.ts +17 -11
  398. package/src/runtime-install.ts +30 -0
  399. package/src/runtime-tokens.ts +20 -28
  400. package/src/secrets-broker/config.ts +1 -1
  401. package/src/secrets-broker/service.ts +2 -2
  402. package/src/security.ts +3 -1
  403. package/src/self-resume-carry.ts +21 -110
  404. package/src/self-resume-supersede.ts +4 -3
  405. package/src/self-resume.ts +54 -66
  406. package/src/server-background.ts +1 -1
  407. package/src/server.ts +25 -26
  408. package/src/services/agent-action.ts +12 -31
  409. package/src/services/agent-effort.ts +7 -32
  410. package/src/services/agent-hang-watchdog-job.ts +2 -2
  411. package/src/services/agent-heartbeat.ts +166 -0
  412. package/src/services/agent-reporting.ts +1 -1
  413. package/src/services/auth-context.ts +6 -0
  414. package/src/services/browser-inventory.ts +3 -3
  415. package/src/services/browser-session-state.ts +3 -2
  416. package/src/services/browser-session.ts +21 -27
  417. package/src/services/bus-command-update.ts +9 -9
  418. package/src/services/checkout-refresh-state.ts +1 -0
  419. package/src/services/checkout-refresh.test-fixture.ts +3 -2
  420. package/src/services/command-host.ts +7 -2
  421. package/src/services/coordinator-digest.ts +3 -2
  422. package/src/services/dispatch-command.ts +12 -13
  423. package/src/services/effective-launch-driver.ts +14 -9
  424. package/src/services/host-changed-paths.test-fixture.ts +5 -6
  425. package/src/services/host-project-readiness.ts +2 -2
  426. package/src/services/host-providers.ts +1 -1
  427. package/src/services/host-utilization.ts +9 -9
  428. package/src/services/idle-agent-digest.ts +17 -12
  429. package/src/services/idle-epics.ts +4 -4
  430. package/src/services/interrupt-agent.ts +12 -9
  431. package/src/services/issue-activity.ts +3 -3
  432. package/src/services/issue-associations.ts +15 -18
  433. package/src/services/issue-lifecycle-comments.ts +3 -2
  434. package/src/services/issue-lifecycle.ts +14 -13
  435. package/src/services/issue-refs.ts +4 -5
  436. package/src/services/kernel-timers-cutover-chaos-worker.ts +1 -1
  437. package/src/services/kernel-timers-ingress-chaos-worker.ts +6 -1
  438. package/src/services/kernel-timers-ingress.ts +3 -3
  439. package/src/services/kernel-timers-message-authority-chaos-worker.ts +3 -10
  440. package/src/services/kernel-timers-operator-chaos-worker.ts +3 -2
  441. package/src/services/kernel-timers-outbox.ts +1 -1
  442. package/src/services/kernel-timers-stage10-operator.ts +1 -1
  443. package/src/services/kernel-timers.ts +4 -3
  444. package/src/services/land-gate-placement.ts +11 -2
  445. package/src/services/land-issue-relations.ts +5 -4
  446. package/src/services/land-restart-retry.ts +7 -6
  447. package/src/services/landed-ancestry.ts +6 -5
  448. package/src/services/legacy-landing-host.ts +4 -1
  449. package/src/services/legacy-landing-recovery.ts +8 -6
  450. package/src/services/managed-running.ts +17 -17
  451. package/src/services/memory-areas.ts +3 -3
  452. package/src/services/merge-pause.ts +9 -15
  453. package/src/services/operator-decisions.ts +37 -41
  454. package/src/services/orchestrator-registration-recovery.ts +6 -2
  455. package/src/services/orchestrator-upgrade.ts +14 -12
  456. package/src/services/permission-decision.ts +4 -3
  457. package/src/services/plan-ci-notice.ts +1 -1
  458. package/src/services/plan-conformance.ts +4 -4
  459. package/src/services/plan-dispatch-policy.ts +10 -3
  460. package/src/services/plan-dispatch.ts +35 -14
  461. package/src/services/plan-graph.ts +39 -261
  462. package/src/services/plan-import.ts +1 -1
  463. package/src/services/plan-live-bindings.ts +8 -37
  464. package/src/services/plan-task-create.ts +1 -1
  465. package/src/services/plan-terminal-refs.ts +2 -12
  466. package/src/services/plan-wake-digest.ts +8 -3
  467. package/src/services/profile-trust.ts +2 -2
  468. package/src/services/project.ts +11 -10
  469. package/src/services/prompt-templates.ts +6 -5
  470. package/src/services/provider-api-key.ts +73 -0
  471. package/src/services/provider-hold-nudge.ts +9 -9
  472. package/src/services/provider-hold-record.ts +13 -12
  473. package/src/services/provider-state-audit.ts +19 -18
  474. package/src/services/provisioning-harness/driver.ts +1 -1
  475. package/src/services/provisioning-harness/fixture-matrix.ts +3 -3
  476. package/src/services/provisioning-harness/lifecycle.ts +24 -22
  477. package/src/services/provisioning-harness/prepare.ts +16 -17
  478. package/src/services/rate-limit-resume.ts +10 -9
  479. package/src/services/red-main-auto-revert.ts +7 -5
  480. package/src/services/red-main-controller-deps.ts +20 -3
  481. package/src/services/red-main-incident.ts +3 -1
  482. package/src/services/red-main-quarantine.ts +2 -3
  483. package/src/services/register-agent.ts +22 -22
  484. package/src/services/restart-project-role.ts +1 -1
  485. package/src/services/restart-spawn.ts +17 -16
  486. package/src/services/review-publish.ts +7 -24
  487. package/src/services/review-ref-reconcile.ts +29 -38
  488. package/src/services/runtime-dependency-alert.ts +27 -0
  489. package/src/services/scheduler-command.ts +11 -9
  490. package/src/services/scheduler.ts +5 -5
  491. package/src/services/self-effort-grant.ts +5 -25
  492. package/src/services/send-message.ts +16 -33
  493. package/src/services/shutdown-agent.ts +17 -14
  494. package/src/services/spawn-agent.ts +12 -41
  495. package/src/services/spawn-chain-inspector.ts +1 -1
  496. package/src/services/spawn-cwd-placement.ts +21 -5
  497. package/src/services/spawn-issue-activity.ts +8 -6
  498. package/src/services/spawn-launch.ts +17 -1
  499. package/src/services/spawn-loss-escalation.ts +1 -1
  500. package/src/services/spawn-registration-watchdog.ts +2 -2
  501. package/src/services/spawn-repo-probe.ts +7 -5
  502. package/src/services/spawn-tree-cleanup.ts +7 -8
  503. package/src/services/steward-reaper.ts +4 -4
  504. package/src/services/task-context-hydration.ts +25 -1
  505. package/src/services/task-delivery.ts +20 -34
  506. package/src/services/task-entity.ts +40 -203
  507. package/src/services/task-issue-link.ts +13 -20
  508. package/src/services/task-issue-weave.ts +12 -11
  509. package/src/services/task-lifecycle.ts +27 -31
  510. package/src/services/task-repo-cwd.ts +37 -0
  511. package/src/services/task-review-hold.ts +4 -17
  512. package/src/services/task-semantic-events.ts +13 -11
  513. package/src/services/task-state-reconcile.ts +3 -4
  514. package/src/services/task-validation.ts +2 -2
  515. package/src/services/team-templates.ts +5 -5
  516. package/src/services/team.ts +31 -98
  517. package/src/services/terminal-provider-exit.ts +6 -5
  518. package/src/services/timer-message-fire-chaos-worker.ts +7 -3
  519. package/src/services/timer-message-fire-outbox.ts +1 -1
  520. package/src/services/transient-agent-reaper.ts +22 -19
  521. package/src/services/turn-reconcile-audit.ts +9 -9
  522. package/src/services/user-onboarding.ts +8 -6
  523. package/src/services/user-profile.ts +6 -9
  524. package/src/services/work-finality.ts +16 -4
  525. package/src/services/workflow-auto-start.ts +2 -1
  526. package/src/services/workflow-caller.ts +61 -4
  527. package/src/services/workflow-child-proof-worker.ts +1 -1
  528. package/src/services/workflow-control-proof-worker.ts +1 -1
  529. package/src/services/workflow-coordinator.ts +8 -2
  530. package/src/services/workflow-decisions.ts +5 -5
  531. package/src/services/workflow-deferred-cleanup.ts +1 -1
  532. package/src/services/workflow-effect-driver.ts +42 -19
  533. package/src/services/workflow-effects.ts +41 -27
  534. package/src/services/workflow-pins.ts +7 -4
  535. package/src/services/workflow-reconcile.ts +1 -0
  536. package/src/services/workflow-resume-pins.ts +3 -3
  537. package/src/services/workflow-routing-defaults.ts +18 -0
  538. package/src/services/workflow-run-view.ts +4 -3
  539. package/src/services/workflow-run.test-fixture.ts +1 -1
  540. package/src/services/workflow-runs.ts +34 -22
  541. package/src/services/workflow-supervisor-state.ts +21 -1
  542. package/src/services/workflow-supervisor.ts +4 -3
  543. package/src/services/workspace-recovery.ts +4 -3
  544. package/src/setup.ts +47 -8
  545. package/src/spawn-admission.ts +10 -2
  546. package/src/spawn-placement.ts +27 -9
  547. package/src/spawn-targets.ts +8 -8
  548. package/src/sse-agent-status.ts +15 -6
  549. package/src/sse.ts +49 -24
  550. package/src/startup-context-injection.ts +61 -57
  551. package/src/steward-handoff.ts +4 -3
  552. package/src/steward-policy-cleanup.ts +9 -8
  553. package/src/steward.ts +2 -2
  554. package/src/task-dispatch-live.ts +5 -2
  555. package/src/task-dispatcher.ts +2 -2
  556. package/src/token-db.ts +2 -2
  557. package/src/token-usage-async.ts +2 -4
  558. package/src/user-awaiting-reply.ts +5 -5
  559. package/src/utils.ts +7 -0
  560. package/src/workspace-actions.ts +24 -29
  561. package/src/workspace-auto-merge.ts +14 -12
  562. package/src/workspace-changed-paths-client.ts +16 -5
  563. package/src/workspace-cleanup-stale.ts +30 -7
  564. package/src/workspace-command-settle.ts +24 -16
  565. package/src/workspace-config.ts +1 -1
  566. package/src/workspace-conflict-resolver.ts +9 -7
  567. package/src/workspace-dead-owner-recovery.ts +5 -4
  568. package/src/workspace-escalation.ts +12 -11
  569. package/src/workspace-human-escalation.ts +3 -3
  570. package/src/workspace-land-gate-watchdog.ts +3 -3
  571. package/src/workspace-land-refusal.ts +20 -0
  572. package/src/workspace-land-stopped.ts +1 -0
  573. package/src/workspace-merge.ts +10 -26
  574. package/src/workspace-nothing-to-land-notice.ts +2 -2
  575. package/src/workspace-orphans.ts +18 -37
  576. package/src/workspace-pr-completion.ts +10 -8
  577. package/src/workspace-rekey.ts +3 -3
  578. package/src/workspace-steward-failure.ts +2 -2
  579. package/src/workspace-steward-wake.ts +2 -2
  580. package/public/assets/agent-liveness-CaNwK0Zf.js +0 -2
  581. package/public/assets/overview-C1DCTzzV.js +0 -2
  582. package/public/assets/provider-display-DsuWMvVL.js +0 -2
  583. package/public/assets/run-card-CpKqRbhg.js +0 -2
  584. package/public/assets/shipping-B9YtnwtG.js +0 -2
  585. package/public/assets/store-DcClXLH0.js +0 -9
  586. package/runner/src/agent-runner/http-liveness.ts +0 -85
  587. package/src/db/on-loop-write-transaction.ts +0 -34
package/src/sse.ts CHANGED
@@ -1,7 +1,8 @@
1
+ import { sendMessageWithResultAsync } from "./db/message-send";
1
2
  import { eventVisibleTo } from "./sse-visibility";
2
3
  import { taskVisibleTo, memoizeViewerScope } from "./db/viewer-scope";
3
4
  import { agentPrincipalSink } from "./db/agent-ownership";
4
- import { getAgent, getMessage, getOperatorDecision, getOrchestrator, matchingDeliveryAgents, onWorkspaceChange, sendMessageWithResult, setChannelSubscriberProbe } from "./db";
5
+ import { getAgent, getMessage, getOperatorDecision, getOrchestrator, matchingDeliveryAgents, onWorkspaceChange, setChannelSubscriberProbe } from "./db";
5
6
  import { hasReplayGap, type BusEvent } from "./bus-outbox";
6
7
  import { replayEventsSliceAsync } from "./bus-outbox-async";
7
8
  import { getFannedSeq } from "./sse-outbox-cursor";
@@ -14,10 +15,11 @@ import type { ActivityEvent, AgentCard, Message, RelayInjectionCategory, RelayNo
14
15
  import { isHumanAgentId } from "@humwick/sdk";
15
16
  import { messageVisibleTo, visibleMessages, type MessageViewer } from "./message-visibility";
16
17
  import { emitNotificationCreated, notificationForMessage, notificationForDelivery } from "./dashboard-notifications";
17
- import { AgentStatusDeltas } from "./sse-agent-status";
18
+ import { AgentStatusDeltas, type AgentStatusChange } from "./sse-agent-status";
19
+ import { getDbGeneration } from "./db/connection";
18
20
  import { AgentStatusSubscribers, wantsAgentStatus } from "./transport-subscriptions";
19
21
  import { withLoopFrameCapture } from "./loop-block-attribution";
20
- import { recordEmitBytes } from "./hub-metrics";
22
+ import { framed, recordEmitBytes } from "./hub-metrics";
21
23
 
22
24
  interface Connection {
23
25
  id: string;
@@ -27,7 +29,8 @@ interface Connection {
27
29
  queuedBytes: number;
28
30
  /** Bytes ever enqueued on the controller; the replay pump measures its byte budget against it. */
29
31
  writtenBytes: number;
30
- statusDeltas?: AgentStatusDeltas;
32
+ statusDeltas?: Map<string, number>;
33
+ replayDeltas?: AgentStatusDeltas;
31
34
  // Pending backpressure re-check. Set when a stream crosses the byte floor; cleared
32
35
  // once it drains or is closed after making no progress during the grace interval.
33
36
  drainCheck?: Timer;
@@ -46,6 +49,8 @@ interface Connection {
46
49
  const connections = new Map<string, Connection>();
47
50
  const statusSubscribers = new AgentStatusSubscribers<Connection>("sse");
48
51
  const encoder = new TextEncoder();
52
+ let sourceDeltas = new AgentStatusDeltas();
53
+ let statusGeneration = getDbGeneration();
49
54
 
50
55
  const SSE_KEEPALIVE_MS = 15_000;
51
56
  const SSE_RETRY_MS = 5_000;
@@ -65,7 +70,7 @@ subscribeRelayEvents((event) => {
65
70
  fanoutBytes = 0;
66
71
  withLoopFrameCapture(FANOUT_FRAME, () => fanout(event));
67
72
  recordEmitBytes(event.type, fanoutBytes);
68
- });
73
+ }, "sse.fanout");
69
74
 
70
75
  function fanoutAgent(agentId: string, cache: Map<string, AgentCard | null>): AgentCard | null {
71
76
  if (!cache.has(agentId)) cache.set(agentId, getAgent(agentId));
@@ -90,14 +95,14 @@ export function createSSEStream(agentId: string | null, lastEventId = 0, statusD
90
95
  {
91
96
  start(controller) {
92
97
  connId = crypto.randomUUID();
93
- const keepalive = setInterval(() => {
98
+ const keepalive = setInterval(framed("timer sse-keepalive", () => {
94
99
  const conn = connections.get(connId);
95
100
  if (!conn) return;
96
101
  sendEncoded(conn, encoder.encode(": keepalive\n\n"));
97
- }, SSE_KEEPALIVE_MS);
102
+ }), SSE_KEEPALIVE_MS);
98
103
 
99
104
  connections.set(connId, { id: connId, agentId, controller, keepalive, queuedBytes: 0, writtenBytes: 0,
100
- statusDeltas: statusDeltas ? new AgentStatusDeltas() : undefined, viewer });
105
+ statusDeltas: statusDeltas ? new Map() : undefined, viewer });
101
106
  statusSubscribers.add(connections.get(connId)!);
102
107
 
103
108
  const replay = replayPlan(lastEventId, agentId === null);
@@ -216,18 +221,34 @@ function sendDirect(conn: Connection, event: string, data: unknown, id?: number)
216
221
  writeFrame(conn, sseFrame(event, data, id));
217
222
  }
218
223
 
219
- // #2873/#3640 — one agent.status projection for live, buffered-live and replayed frames. Every frame goes
220
- // through it in the order the client receives it, so the per-connection delta baseline matches the client.
224
+ // #3608 — share live deltas and encoded frames; select full/delta in client order even during replay.
221
225
  function tracksStatus(conn: Connection, event: RelayEvent): boolean {
222
226
  return conn.statusDeltas !== undefined && (event.type === "agent.status" || event.type === "agent.removed");
223
227
  }
224
228
 
229
+ // An ephemeral frame carries no `id:` line: it must not move the client's Last-Event-ID resume cursor.
230
+ function frameId(event: RelayEvent): number | undefined {
231
+ return event.ephemeral ? undefined : event.seq;
232
+ }
233
+
234
+ // Ephemeral events all carry seq 0, so they take a unique negative baseline token: two live-only
235
+ // frames must never look like the same client-visible state.
236
+ let ephemeralStatusToken = 0;
237
+
225
238
  // Reads `conn.statusDeltas` when called, not when queued: a replay.gap swaps in a fresh baseline.
226
- function statusFrame(conn: Connection, event: RelayEvent, full?: Uint8Array): Uint8Array {
227
- const deltas = conn.statusDeltas!;
228
- if (event.type === "agent.status") return sseFrame(event.type, deltas.project(event.data), event.seq);
229
- deltas.remove(event.data.id as string);
230
- return full ?? sseFrame(event.type, event.data, event.seq);
239
+ function prepareStatusFrame(event: RelayEvent, deltas: AgentStatusDeltas, full = sseFrame(event.type, event.data, frameId(event))) {
240
+ const id = event.data.id as string;
241
+ const token = event.ephemeral ? --ephemeralStatusToken : event.seq;
242
+ const change: AgentStatusChange | undefined = event.type === "agent.status" ? deltas.project(event.data, token) : undefined;
243
+ if (!change) deltas.remove(id);
244
+ let delta: Uint8Array | undefined;
245
+ return (conn: Connection): Uint8Array => {
246
+ const previous = conn.statusDeltas!.get(id);
247
+ if (event.data.status === "offline" || !change) conn.statusDeltas!.delete(id);
248
+ else conn.statusDeltas!.set(id, token);
249
+ return change && previous !== undefined && previous === change.previous
250
+ ? (delta ??= change.delta === change.full ? full : sseFrame(event.type, change.delta, frameId(event))) : full;
251
+ };
231
252
  }
232
253
 
233
254
  function flushPendingLive(conn: Connection): void {
@@ -282,14 +303,17 @@ function fanout(event: RelayEvent): void {
282
303
  sendTaskChanged(event.data as unknown as Task, event.type, event.seq);
283
304
  return;
284
305
  }
285
- // An ephemeral frame carries no `id:` line: it must not move the client's Last-Event-ID resume cursor.
286
- const id = event.ephemeral ? undefined : event.seq;
287
- const chunk = sseFrame(event.type, event.data, id);
306
+ const chunk = sseFrame(event.type, event.data, frameId(event));
307
+ if (statusGeneration !== getDbGeneration()) {
308
+ sourceDeltas = new AgentStatusDeltas();
309
+ statusGeneration = getDbGeneration();
310
+ }
311
+ const status = event.type === "agent.status" || event.type === "agent.removed" ? prepareStatusFrame(event, sourceDeltas, chunk) : undefined;
288
312
  const recipients = event.type === "agent.status" ? statusSubscribers.forEvent(event.subject, event.data.id) : connections.values();
289
313
  const visible = memoizeViewerScope((viewer) => eventVisibleTo(viewer, event));
290
314
  for (const conn of recipients) {
291
315
  if (!visible(conn.viewer)) continue;
292
- sendEncoded(conn, chunk, tracksStatus(conn, event) ? () => statusFrame(conn, event, chunk) : undefined);
316
+ sendEncoded(conn, chunk, conn.statusDeltas && status ? () => status(conn) : undefined);
293
317
  }
294
318
  }
295
319
 
@@ -394,9 +418,9 @@ interface RelayInjectionTimelineInput {
394
418
  idempotencyKey?: string;
395
419
  }
396
420
 
397
- export function emitRelayInjectionEvent(input: RelayInjectionTimelineInput): Message {
421
+ export async function emitRelayInjectionEvent(input: RelayInjectionTimelineInput): Promise<Message> {
398
422
  const timestamp = input.timestamp ?? Date.now();
399
- const result = sendMessageWithResult({
423
+ const result = (await sendMessageWithResultAsync({
400
424
  from: input.source ?? input.agentId,
401
425
  to: input.source ? input.agentId : agentPrincipalSink(input.agentId),
402
426
  kind: "session",
@@ -416,7 +440,7 @@ export function emitRelayInjectionEvent(input: RelayInjectionTimelineInput): Mes
416
440
  },
417
441
  },
418
442
  },
419
- });
443
+ }));
420
444
  if (result.created || result.updated) emitNewMessage(result.message);
421
445
  return result.message;
422
446
  }
@@ -561,7 +585,7 @@ async function pumpReplay(connId: string, plan: ReplayPlan): Promise<void> {
561
585
  // The client's REST reload covers everything up to the ceiling; the buffered live tail follows.
562
586
  // That reload brings full cards, so restart the delta baseline as a fresh connection would.
563
587
  sendDirect(conn, "replay.gap", { since: plan.since }, ceiling);
564
- if (conn.statusDeltas) conn.statusDeltas = new AgentStatusDeltas();
588
+ conn.statusDeltas?.clear();
565
589
  break;
566
590
  }
567
591
  if (!slice.truncated) break;
@@ -570,6 +594,7 @@ async function pumpReplay(connId: string, plan: ReplayPlan): Promise<void> {
570
594
  drained = true;
571
595
  } finally {
572
596
  conn.replaying = false;
597
+ conn.replayDeltas = undefined;
573
598
  // Only flush into a still-open controller — a cancelled connection was already removed from
574
599
  // `connections` (and stopped receiving anything into `pendingLive` the moment it was removed,
575
600
  // since fanout only iterates live map entries), so there is nothing left to deliver.
@@ -615,7 +640,7 @@ function sendReplayEvent(conn: Connection, event: RelayEvent, agentCache: Map<st
615
640
  return;
616
641
  }
617
642
  if (tracksStatus(conn, event)) {
618
- writeFrame(conn, statusFrame(conn, event));
643
+ writeFrame(conn, prepareStatusFrame(event, conn.replayDeltas ??= new AgentStatusDeltas())(conn));
619
644
  return;
620
645
  }
621
646
  sendDirect(conn, event.type, event.data, event.seq);
@@ -1,3 +1,5 @@
1
+ import { setConfigAsync } from "./config-store-write";
2
+ import { createCommandAsync } from "./commands-write";
1
3
  import { contextInjectUnsupported, launchContextIsUserMessage, launchContextSurvivesCompact, launchSystemPromptAppend } from "./context-injection-channel";
2
4
  import { stripAgentCore } from "@humwick/sdk/prompt-template-defaults";
3
5
  import { renderAgentCore } from "./agent-core";
@@ -26,10 +28,10 @@ import { renderAgentCore } from "./agent-core";
26
28
  // inputs are gathered by the shared `resolveAgentSpawnInstructionInputs` (spawn-instruction-
27
29
  // inputs.ts), the same resolver the restart projection uses, so the two cannot drift.
28
30
  //
29
- // • DELTA-GATED AT COMPACT. A native compact PRESERVES the system prompt, so an unchanged
30
- // block is skipped (hashed; launch prompt read for the first boundary, like the memory
31
- // seam). No skip when launch text rides user messages (OpenCode family, Codex: compaction
32
- // drops them) or on `clear`. Re-injects on persistent-system-prompt providers strip the
31
+ // • DELTA-GATED AT CLEAR AND COMPACT. A persistent system prompt survives both a native compact
32
+ // and a /clear, so an unchanged block is skipped (hashed; launch prompt read, like the memory
33
+ // seam; #3782). No skip when launch text rides user messages (OpenCode family, Codex:
34
+ // compaction drops them). Re-injects on persistent-system-prompt providers strip the
33
35
  // core (their context re-delivers it); the rest re-injects.
34
36
  //
35
37
  // PROVIDER-AGNOSTIC: delivery is an `agent.injectContext` command (declared `context.inject`); a
@@ -38,15 +40,14 @@ import { renderAgentCore } from "./agent-core";
38
40
 
39
41
  import { createHash } from "node:crypto";
40
42
  import { errMessage, isRelayInjectionCategory, stringValue } from "@humwick/sdk";
41
- import { createCommand, findLaunchPromptCommandForAgent } from "./commands-db";
43
+ import { findLaunchPromptCommandForAgent } from "./commands-db";
42
44
  import { emitCommandEvent } from "./command-events";
43
45
  import { REAP_INTERVAL_MS } from "./config";
44
- import { getConfig, setConfig } from "./config-store";
46
+ import { getConfig } from "./config-store";
45
47
  import { agentPrincipalUserId, findMessageByIdempotencyKey, getAgent } from "./db";
46
48
  import { resolveCanonicalHost } from "./host-identity";
47
49
  import { memorySeamForAgent } from "./memory-seam";
48
- import { stripMemoryPreamble } from "./memory-preamble";
49
- import { composeSpawnInstructionsWithEvents, stripHumwickIdentitySection, type ComposedSpawnInstructions } from "./project-instructions";
50
+ import { composeSpawnInstructionsWithEvents, stripRelayOwnedSections, type ComposedSpawnInstructions } from "./project-instructions";
50
51
  import { resolveAgentSpawnInstructionInputs } from "./services/spawn-instruction-inputs";
51
52
  import { INJECTION_SWEEP_BACKLOG_INTERVAL_MS, pacedInjectionSweep } from "./injection-sweep-pacing";
52
53
  import { isSqliteBusyError } from "./db/connection";
@@ -106,8 +107,8 @@ export function readStartupContextInjectionState(agentId: string): StartupContex
106
107
  return getConfig<StartupContextInjectionState>(STARTUP_CONTEXT_NAMESPACE, agentId)?.value ?? null;
107
108
  }
108
109
 
109
- export function recordStartupContextInjectionState(agentId: string, state: StartupContextInjectionState): void {
110
- setConfig(STARTUP_CONTEXT_NAMESPACE, agentId, state, "startup-context-injection");
110
+ export async function recordStartupContextInjectionState(agentId: string, state: StartupContextInjectionState): Promise<void> {
111
+ (await setConfigAsync(STARTUP_CONTEXT_NAMESPACE, agentId, state, "startup-context-injection"));
111
112
  }
112
113
 
113
114
  /** #2171 — the machine this agent runs on, named the way the spawn named it (see the call site).
@@ -163,12 +164,14 @@ export function composeStartupContextForAgent(agent: AgentCard): ComposedSpawnIn
163
164
  * A prior inject rode the summarized transcript, so the recorded delivery hash proves delivery,
164
165
  * not survival — trusting it lets a summarized-away block skip forever. The launch prompt rode
165
166
  * the preserved system prompt, so only it proves the block is still there. Unresolvable ⇒ null
166
- * ⇒ re-inject, never skip. (Mirrors launchBakedMemoryHash/launchBakedJournalHash.)
167
+ * ⇒ re-inject, never skip. (Mirrors launchBakedMemoryHash/launchBakedJournalHash.) The strip must
168
+ * remove EVERY relay-owned section (identity, memory, journal): a leftover journal block made this
169
+ * hash never match a real coordinator launch, so every /clear re-injected the full charter (#3782).
167
170
  */
168
171
  function launchBakedStartupHash(agent: AgentCard): string | null {
169
172
  const prompt = launchSystemPromptAppend(findLaunchPromptCommandForAgent(agent.id, stringValue(agent.meta?.spawnRequestId)));
170
173
  if (!prompt) return null;
171
- const portion = normalizeStartupBlock(stripHumwickIdentitySection(stripMemoryPreamble(prompt)));
174
+ const portion = normalizeStartupBlock(stripRelayOwnedSections(prompt));
172
175
  return portion ? startupBlockHash(portion) : null;
173
176
  }
174
177
 
@@ -180,7 +183,7 @@ function launchBakedStartupHash(agent: AgentCard): string | null {
180
183
  * outcome (unchanged block, provider cannot inject, nothing composable, nothing to inject), always
181
184
  * reported in a `relay-injection` event rather than inferred from silence.
182
185
  */
183
- export function enqueueStartupContextInjectionAfterContextCommand(command: Command, now: number = Date.now()): Command | null {
186
+ export async function enqueueStartupContextInjectionAfterContextCommand(command: Command, now: number = Date.now()): Promise<Command | null> {
184
187
  // A lifecycle command's status is the runner's confirmation verdict, not proof that the
185
188
  // destructive boundary did not happen. In particular, a clear-inject that cannot confirm its
186
189
  // `/clear` may have committed and is deliberately settled failed so the continuation beside this
@@ -197,28 +200,29 @@ export function enqueueStartupContextInjectionAfterContextCommand(command: Comma
197
200
  const composed = composeStartupContextForAgent(agent);
198
201
 
199
202
  if (contextInjectUnsupported(agent)) {
200
- emitStartupSkip(agent, event, "inject-unsupported", command, now);
203
+ (await emitStartupSkip(agent, event, "inject-unsupported", command, now));
201
204
  return null;
202
205
  }
203
206
  if (!composed) {
204
207
  // No profile prompt to recompose (not a spawn provider). Reported once so an operator can tell
205
208
  // it apart from a broken seam.
206
- emitStartupSkip(agent, event, "not-composable", command, now);
209
+ (await emitStartupSkip(agent, event, "not-composable", command, now));
207
210
  return null;
208
211
  }
209
212
 
210
213
  const block = composed.systemPromptAppend?.trim() ? composed.systemPromptAppend : "";
211
214
  const hash = startupBlockHash(block);
212
215
  if (!block) {
213
- emitStartupSkip(agent, event, "nothing-to-inject", command, now);
216
+ (await emitStartupSkip(agent, event, "nothing-to-inject", command, now));
214
217
  return null;
215
218
  }
216
219
 
217
- // A native compact preserves a real system prompt, but not launch text parts. An unchanged block is already there. A clear
218
- // destroys the context — nothing survives it to be unchanged. User-message launch providers keep neither reliably
219
- // (Codex keeps only recent user messages), so compact always re-injects for them, like the summarizable family.
220
- if (event === "compact" && launchContextSurvivesCompact(agent) && launchBakedStartupHash(agent) === hash) {
221
- emitStartupSkip(agent, event, "unchanged", command, now);
220
+ // #3782 — a persistent system prompt (Claude's --append-system-prompt) survives BOTH a native
221
+ // compact and a /clear, so a block still equal to the launch-baked one is already above: skip.
222
+ // A changed charter (hash differs), an unresolvable launch prompt, or a provider whose launch
223
+ // text rides summarizable parts / user messages (OpenCode, Codex) re-injects at either boundary.
224
+ if (launchContextSurvivesCompact(agent) && launchBakedStartupHash(agent) === hash) {
225
+ (await emitStartupSkip(agent, event, "unchanged", command, now));
222
226
  return null;
223
227
  }
224
228
 
@@ -233,7 +237,7 @@ export function enqueueStartupContextInjectionAfterContextCommand(command: Comma
233
237
  // Delivered WITH the superseding note; hashed and gated on the canonical block (note excluded), so
234
238
  // the gate cannot loop.
235
239
  const content = `${STARTUP_CONTEXT_SUPERSEDE_NOTICE}\n\n${injectBlock}`;
236
- const inject = createCommand({
240
+ const inject = (await createCommandAsync({
237
241
  type: "agent.injectContext",
238
242
  source: "system",
239
243
  target: command.target,
@@ -252,13 +256,13 @@ export function enqueueStartupContextInjectionAfterContextCommand(command: Comma
252
256
  ...(command.type === "agent.clearContext" ? { afterClearCommandId: command.id } : { afterCompactCommandId: command.id }),
253
257
  },
254
258
  },
255
- });
259
+ }));
256
260
  emitCommandEvent(inject, "command.requested");
257
261
 
258
262
  // Anti-lie: queueing a command is not injecting a charter. The detailed per-block "injected"
259
263
  // events move to settleStartupContextInjectionCommand, where the runner has reported success.
260
264
  // This one row makes pending work visible without claiming the provider has it already.
261
- emitRelayInjectionEvent({
265
+ (await emitRelayInjectionEvent({
262
266
  agentId: agent.id,
263
267
  category: "context",
264
268
  summary: `startup context re-injection queued after ${event}`,
@@ -273,7 +277,7 @@ export function enqueueStartupContextInjectionAfterContextCommand(command: Comma
273
277
  },
274
278
  timestamp: now,
275
279
  idempotencyKey: `startup-context-injection-queued:${agent.id}:${inject.id}`,
276
- });
280
+ }));
277
281
  return inject;
278
282
  }
279
283
 
@@ -283,45 +287,45 @@ export function enqueueStartupContextInjectionAfterContextCommand(command: Comma
283
287
  * ownership and cwd→project resolution, so a throw here must NOT take the rest of the settle chain
284
288
  * down with it; it is reported on the agent's timeline instead.
285
289
  */
286
- export function settleStartupContextInjectionAfterContextCommand(command: Command, now: number = Date.now()): Command | null {
290
+ export async function settleStartupContextInjectionAfterContextCommand(command: Command, now: number = Date.now()): Promise<Command | null> {
287
291
  try {
288
- const inject = enqueueStartupContextInjectionAfterContextCommand(command, now);
289
- settleStartupContextInjectionCommand(command, now);
292
+ const inject = (await enqueueStartupContextInjectionAfterContextCommand(command, now));
293
+ (await settleStartupContextInjectionCommand(command, now));
290
294
  return inject;
291
295
  } catch (error) {
292
- emitRelayInjectionEvent({
296
+ (await emitRelayInjectionEvent({
293
297
  agentId: command.target,
294
298
  category: "context",
295
299
  summary: `startup-context re-injection failed to compose after ${command.type === "agent.compact" ? "compact" : command.type === "agent.clearContext" ? "clear" : command.type}: ${errMessage(error)}`,
296
300
  detail: { source: "startup-context", agentId: command.target, commandId: command.id, failed: true },
297
301
  timestamp: now,
298
302
  idempotencyKey: `startup-context-injection-error:${command.target}:${command.id}`,
299
- });
303
+ }));
300
304
  return null;
301
305
  }
302
306
  }
303
307
 
304
- function emitStartupSkip(
308
+ async function emitStartupSkip(
305
309
  agent: AgentCard,
306
310
  event: StartupContextInjectionEvent,
307
311
  skipped: StartupContextInjectionSkip,
308
312
  command: Command,
309
313
  now: number,
310
- ): void {
314
+ ): Promise<void> {
311
315
  const summaries: Record<StartupContextInjectionSkip, string> = {
312
316
  unchanged: `startup context unchanged after ${event} — re-injection skipped`,
313
317
  "inject-unsupported": `startup context NOT re-injected after ${event}: provider declares context.inject:false (spawn-time block only)`,
314
318
  "nothing-to-inject": `startup context resolved nothing to inject after ${event}`,
315
319
  "not-composable": `startup context not recomposable after ${event}: agent has no spawn-provider profile prompt`,
316
320
  };
317
- emitRelayInjectionEvent({
321
+ (await emitRelayInjectionEvent({
318
322
  agentId: agent.id,
319
323
  category: "context",
320
324
  summary: summaries[skipped],
321
325
  detail: { source: "startup-context", event, agentId: agent.id, skipped, commandId: command.id },
322
326
  timestamp: now,
323
327
  idempotencyKey: `startup-context-injection-skip:${agent.id}:${command.id}`,
324
- });
328
+ }));
325
329
  }
326
330
 
327
331
  /**
@@ -331,7 +335,7 @@ function emitStartupSkip(
331
335
  * Recording on success makes the gate self-healing — a failed delivery leaves the previous hash and
332
336
  * the next boundary retries.
333
337
  */
334
- export function settleStartupContextInjectionCommand(command: Command, now: number = Date.now()): void {
338
+ export async function settleStartupContextInjectionCommand(command: Command, now: number = Date.now()): Promise<void> {
335
339
  if (command.type !== "agent.injectContext" || command.status !== "succeeded") return;
336
340
  if (command.params.reason !== STARTUP_CONTEXT_INJECTION_REASON) return;
337
341
  const startup = command.params.startupContext;
@@ -339,12 +343,12 @@ export function settleStartupContextInjectionCommand(command: Command, now: numb
339
343
  const record = startup as Record<string, unknown>;
340
344
  const hash = typeof record.hash === "string" ? record.hash : undefined;
341
345
  if (hash === undefined) return;
342
- recordStartupContextInjectionState(command.target, {
346
+ (await recordStartupContextInjectionState(command.target, {
343
347
  hash,
344
348
  event: record.event === "clear" ? "clear" : "compact",
345
349
  at: now,
346
- });
347
- emitStartupContextDeliveredEvents(command, record, now);
350
+ }));
351
+ (await emitStartupContextDeliveredEvents(command, record, now));
348
352
  }
349
353
 
350
354
  /** Preserve only enough of the composition for the success-side audit event. The actual charter
@@ -379,12 +383,12 @@ function queuedStartupBlockEvents(record: Record<string, unknown>): Array<{ cate
379
383
  });
380
384
  }
381
385
 
382
- function emitStartupContextDeliveredEvents(command: Command, record: Record<string, unknown>, now: number): void {
386
+ async function emitStartupContextDeliveredEvents(command: Command, record: Record<string, unknown>, now: number): Promise<void> {
383
387
  const event: StartupContextInjectionEvent = record.event === "clear" ? "clear" : "compact";
384
388
  const boundaryCommandId = stringValue(record.afterClearCommandId) ?? stringValue(record.afterCompactCommandId) ?? command.correlationId;
385
389
  for (const [index, blockEvent] of queuedStartupBlockEvents(record).entries()) {
386
390
  const source = stringValue(blockEvent.detail.source) ?? "startup-context";
387
- emitRelayInjectionEvent({
391
+ (await emitRelayInjectionEvent({
388
392
  agentId: command.target,
389
393
  category: blockEvent.category,
390
394
  summary: `re-injected after ${event}: ${blockEvent.summary}`,
@@ -397,26 +401,26 @@ function emitStartupContextDeliveredEvents(command: Command, record: Record<stri
397
401
  },
398
402
  timestamp: now,
399
403
  idempotencyKey: `startup-context-injection-delivered:${command.target}:${command.id}:${source}:${index}`,
400
- });
404
+ }));
401
405
  }
402
406
  }
403
407
 
404
408
  /** A failed startup-context composer must not make a maintenance tick throw or block every later
405
409
  * candidate. The reactive settle has the same containment in its public wrapper. */
406
- function containedStartupContextInjection(command: Command, now: number): { inject: Command | null; composeFailed: boolean } {
410
+ async function containedStartupContextInjection(command: Command, now: number): Promise<{ inject: Command | null; composeFailed: boolean }> {
407
411
  try {
408
- return { inject: enqueueStartupContextInjectionAfterContextCommand(command, now), composeFailed: false };
412
+ return { inject: (await enqueueStartupContextInjectionAfterContextCommand(command, now)), composeFailed: false };
409
413
  } catch (error) {
410
414
  // #2712 — a held write lock is contention, not a compose failure: defer, never count it.
411
415
  if (isSqliteBusyError(error)) throw error;
412
- emitRelayInjectionEvent({
416
+ (await emitRelayInjectionEvent({
413
417
  agentId: command.target,
414
418
  category: "context",
415
419
  summary: `startup-context re-injection failed to compose after ${command.type === "agent.compact" ? "compact" : "clear"}: ${errMessage(error)}`,
416
420
  detail: { source: "startup-context", agentId: command.target, commandId: command.id, failed: true },
417
421
  timestamp: now,
418
422
  idempotencyKey: `startup-context-injection-error:${command.target}:${command.id}`,
419
- });
423
+ }));
420
424
  return { inject: null, composeFailed: true };
421
425
  }
422
426
  }
@@ -428,11 +432,11 @@ function containedStartupContextInjection(command: Command, now: number): { inje
428
432
  * prompt's context; collapsing the two would silently strand the clear's charter recovery. The
429
433
  * shared attempt ledger is keyed by boundary correlation id, so it is both the idempotency fence
430
434
  * and the retry state — there is no second per-agent collapse to keep in sync with it. */
431
- export function sweepMissedStartupContextInjections(now: number = Date.now(), signal?: AbortSignal): { enqueuedCommandIds: string[]; aborted?: true } {
435
+ export async function sweepMissedStartupContextInjections(now: number = Date.now(), signal?: AbortSignal): Promise<{ enqueuedCommandIds: string[]; aborted?: true }> {
432
436
  const enqueuedCommandIds: string[] = [];
433
437
  for (const command of sweepableBoundaryCommands(now, signal)) {
434
438
  if (signal?.aborted) break;
435
- sweepStartupBoundary(command, now, enqueuedCommandIds);
439
+ (await sweepStartupBoundary(command, now, enqueuedCommandIds));
436
440
  }
437
441
  return { enqueuedCommandIds, ...(signal?.aborted ? { aborted: true as const } : {}) };
438
442
  }
@@ -441,24 +445,24 @@ export function sweepMissedStartupContextInjections(now: number = Date.now(), si
441
445
  * macrotask each. */
442
446
  async function sweepMissedStartupContextInjectionsPaced(now: number, signal?: AbortSignal): Promise<{ enqueuedCommandIds: string[]; aborted?: true; moreWork?: true }> {
443
447
  const enqueuedCommandIds: string[] = [];
444
- const { moreWork } = await pacedInjectionSweep(() => sweepableBoundaryCommands(now, signal), (command) => sweepStartupBoundary(command, now, enqueuedCommandIds), signal);
448
+ const { moreWork } = await pacedInjectionSweep(() => sweepableBoundaryCommands(now, signal), async (command) => (await sweepStartupBoundary(command, now, enqueuedCommandIds)), signal);
445
449
  return { enqueuedCommandIds, ...(signal?.aborted ? { aborted: true as const } : {}), ...(moreWork ? { moreWork: true as const } : {}) };
446
450
  }
447
451
 
448
- function sweepStartupBoundary(command: Command, now: number, enqueuedCommandIds: string[]): void {
452
+ async function sweepStartupBoundary(command: Command, now: number, enqueuedCommandIds: string[]): Promise<void> {
449
453
  const { decision, attempts } = injectionAttemptDecision(command, STARTUP_CONTEXT_INJECTION_REASON);
450
454
  if (decision === "in-flight") return;
451
455
  if (decision === "exhausted") {
452
- notifyStartupContextInjectionExhausted(command, attempts, now);
456
+ (await notifyStartupContextInjectionExhausted(command, attempts, now));
453
457
  return;
454
458
  }
455
459
  if (attempts === 0 && (startupSweepAlreadyDecided(command) || startupBoundaryAlreadyDecided(command))) return;
456
- const { inject, composeFailed } = containedStartupContextInjection(command, now);
460
+ const { inject, composeFailed } = (await containedStartupContextInjection(command, now));
457
461
  if (inject) {
458
462
  enqueuedCommandIds.push(inject.id);
459
463
  return;
460
464
  }
461
- rememberStartupSweepDecision(command, composeFailed, now);
465
+ (await rememberStartupSweepDecision(command, composeFailed, now));
462
466
  }
463
467
 
464
468
  /**
@@ -477,32 +481,32 @@ function startupSweepAlreadyDecided(command: Command): boolean {
477
481
  return sweptStartupBoundaryDecisions.get(command.id)?.done ?? false;
478
482
  }
479
483
 
480
- function rememberStartupSweepDecision(command: Command, composeFailed: boolean, now: number): void {
484
+ async function rememberStartupSweepDecision(command: Command, composeFailed: boolean, now: number): Promise<void> {
481
485
  const held = sweptStartupBoundaryDecisions.get(command.id);
482
486
  const composeFailures = (held?.composeFailures ?? 0) + (composeFailed ? 1 : 0);
483
487
  const done = !composeFailed || composeFailures >= MAX_INJECTION_ATTEMPTS;
484
488
  if (sweptStartupBoundaryDecisions.size >= MAX_SWEPT_STARTUP_DECISIONS && !held) sweptStartupBoundaryDecisions.clear();
485
489
  sweptStartupBoundaryDecisions.set(command.id, { composeFailures, done });
486
- if (composeFailed && done) notifyStartupContextInjectionExhausted(command, composeFailures, now, "compose");
490
+ if (composeFailed && done) (await notifyStartupContextInjectionExhausted(command, composeFailures, now, "compose"));
487
491
  }
488
492
 
489
493
  function startupBoundaryAlreadyDecided(command: Command): boolean {
490
494
  return findMessageByIdempotencyKey(command.target, `startup-context-injection-skip:${command.target}:${command.id}`) !== null;
491
495
  }
492
496
 
493
- function notifyStartupContextInjectionExhausted(command: Command, attempts: number, now: number, kind: "delivery" | "compose" = "delivery"): void {
497
+ async function notifyStartupContextInjectionExhausted(command: Command, attempts: number, now: number, kind: "delivery" | "compose" = "delivery"): Promise<void> {
494
498
  const idempotencyKey = `startup-context-injection-exhausted${kind === "compose" ? "-compose" : ""}:${command.target}:${command.id}`;
495
499
  if (findMessageByIdempotencyKey(command.target, idempotencyKey)) return;
496
500
  const event = command.type === "agent.compact" ? "compact" : "clear";
497
501
  const what = kind === "compose" ? "failed to compose" : "could not be delivered";
498
- emitRelayInjectionEvent({
502
+ (await emitRelayInjectionEvent({
499
503
  agentId: command.target,
500
504
  category: "context",
501
505
  summary: `startup-context re-injection gave up after ${attempts} attempt(s) at a ${event} boundary — it ${what}, so this agent may be running without its current profile charter`,
502
506
  detail: { source: "startup-context", agentId: command.target, commandId: command.id, event, attempts, exhausted: kind },
503
507
  timestamp: now,
504
508
  idempotencyKey,
505
- });
509
+ }));
506
510
  }
507
511
 
508
512
  export const STARTUP_CONTEXT_INJECTION_SWEEP_JOB = {
@@ -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
@@ -153,7 +153,7 @@ async function collapseLegacyStewardPolicies(canonicalName: string, repoRoot: st
153
153
  }
154
154
  if (legacy.enabled === false) inheritedEnabled = false;
155
155
  await deleteConfigAsync("spawn-policy", legacyName, "steward-legacy-collapse");
156
- getLifecycleManager().onConfigChanged("spawn-policy", legacyName);
156
+ (await getLifecycleManager().onConfigChanged("spawn-policy", legacyName));
157
157
  console.warn(`[steward] collapsed legacy steward policy '${legacyName}' into '${canonicalName}' (#1955)`);
158
158
  }
159
159
  return inheritedEnabled;
@@ -195,6 +195,6 @@ export async function ensureRepoStewardAsync(repoRoot: string, owningOrchestrato
195
195
  if (existing && !stewardPolicyDiffers(existing.value, desired)) return name;
196
196
 
197
197
  await setConfigAsync("spawn-policy", name, desired, "steward-autoprovision");
198
- getLifecycleManager().onConfigChanged("spawn-policy", name);
198
+ (await getLifecycleManager().onConfigChanged("spawn-policy", name));
199
199
  return name;
200
200
  }