@humwick/server 0.130.2 → 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 (570) 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 +73 -4
  8. package/package.json +8 -7
  9. package/public/assets/{MessageBubble-BWLqMGKG.js → MessageBubble-Bq9kr21N.js} +2 -2
  10. package/public/assets/{PlanGraphPanel-3TlIQpjk.js → PlanGraphPanel-Dqiroy06.js} +2 -2
  11. package/public/assets/{TraceView-Wimm0Czv.js → TraceView-DYU962-H.js} +2 -2
  12. package/public/assets/{VaultDecisionCard-Bau_vNax.js → VaultDecisionCard-DhPfzioE.js} +2 -2
  13. package/public/assets/{activity-CrSurn-r.js → activity-DzCcQTee.js} +2 -2
  14. package/public/assets/{agent-lineage-CknAJKcb.js → agent-lineage-SgDt9i3T.js} +2 -2
  15. package/public/assets/agent-liveness-Bq-s73jW.js +2 -0
  16. package/public/assets/{agent-name-DKbiIhBo.js → agent-name-CcY3Aijy.js} +2 -2
  17. package/public/assets/{agent-profiles-DwF6EQ4R.js → agent-profiles-cxtRBn6O.js} +2 -2
  18. package/public/assets/{agent-project-badge-Diz4Sjjb.js → agent-project-badge-DGYbIhaI.js} +2 -2
  19. package/public/assets/{agent-tools-CRhpRQza.js → agent-tools-DHbftV0N.js} +2 -2
  20. package/public/assets/{agent-type-icon-LrUEU3lB.js → agent-type-icon-CY113sp-.js} +2 -2
  21. package/public/assets/{agents-B-v66C-4.js → agents-BPzY7IlQ.js} +2 -2
  22. package/public/assets/{analytics-BqCb-LKi.js → analytics-DENFPv_a.js} +2 -2
  23. package/public/assets/{artifact-viewer-kf4pGuAR.js → artifact-viewer-KRvSc7sl.js} +2 -2
  24. package/public/assets/{artifacts-DJevXcvg.js → artifacts-C9gSmL3s.js} +2 -2
  25. package/public/assets/{artifacts-D32zyw4Y.js → artifacts-DT-HkI5O.js} +2 -2
  26. package/public/assets/{automation-BpRajx_5.js → automation-BsmjQhKD.js} +2 -2
  27. package/public/assets/{backlog-D_UjTdzX.js → backlog-8NYH_62t.js} +2 -2
  28. package/public/assets/{busy-health-N3VEh4ao.js → busy-health-D3hwvYb4.js} +2 -2
  29. package/public/assets/{channels-9jopungq.js → channels-DCJ5WL7O.js} +2 -2
  30. package/public/assets/{chat-CSXrTB3C.js → chat-DAIl357U.js} +3 -3
  31. package/public/assets/{code-preview-p1NnmqK0.js → code-preview-CzrZfr9U.js} +2 -2
  32. package/public/assets/{connectors-CZdqlDNL.js → connectors-xTxtlyou.js} +2 -2
  33. package/public/assets/{cosmos-BgdsE4dG.js → cosmos-BpebFLK2.js} +2 -2
  34. package/public/assets/{crate-agent-30aXTKva.js → crate-agent-BHvI5wXC.js} +2 -2
  35. package/public/assets/{dependency-graph-B_jvw2GE.js → dependency-graph-DGT0o3wJ.js} +2 -2
  36. package/public/assets/{deploys-DoN6bAb5.js → deploys-hqWOHSHE.js} +2 -2
  37. package/public/assets/{display-BrKWFGJz.js → display-CDeH4I8T.js} +2 -2
  38. package/public/assets/{files-D6XEu8Ng.js → files-BTeGVmTg.js} +2 -2
  39. package/public/assets/{fleet-C7jQWbw2.js → fleet-DgXtNgCa.js} +2 -2
  40. package/public/assets/{fleet-metrics-BiDs3MLx.js → fleet-metrics-BHEt5yc_.js} +2 -2
  41. package/public/assets/{formatted-body-CQBwc1fb.js → formatted-body-MLtodyPp.js} +3 -3
  42. package/public/assets/{formatted-body-impl-DyG0WsQS.js → formatted-body-impl-CAe664Z8.js} +2 -2
  43. package/public/assets/{host-providers-dialog-DKaW31dN.js → host-providers-dialog-mapF4Fj9.js} +2 -2
  44. package/public/assets/{index-CRHep3M9.js → index-CqqgOI5R.js} +5 -5
  45. package/public/assets/{insights-CzrtkpOE.js → insights-DNyf7IzF.js} +2 -2
  46. package/public/assets/{integrations-aVsUmigt.js → integrations-DlYOHq7T.js} +2 -2
  47. package/public/assets/{issue-ref-chip-BnSlM_95.js → issue-ref-chip-CeWBNWn7.js} +2 -2
  48. package/public/assets/{log-viewer-qe7Ee47Z.js → log-viewer-Cib8nqdE.js} +2 -2
  49. package/public/assets/{maintenance-NfjqFZAk.js → maintenance-DSn1cPm7.js} +2 -2
  50. package/public/assets/{managed-agents-BiH0HgaE.js → managed-agents-C3CkM81m.js} +2 -2
  51. package/public/assets/{markdown-preview-BixaT3Xr.js → markdown-preview-W4VQQPAv.js} +3 -3
  52. package/public/assets/{markdown-preview-impl-DNGlWIS3.js → markdown-preview-impl-CMsTdhjq.js} +2 -2
  53. package/public/assets/{memory-DNsCl6UX.js → memory-BV17qtO4.js} +2 -2
  54. package/public/assets/{messages-DopU1L1X.js → messages-Bm_1AqSA.js} +2 -2
  55. package/public/assets/{orchestrators-Cq7ckLQT.js → orchestrators-Dg-vfP09.js} +2 -2
  56. package/public/assets/{overview-C1DCTzzV.js → overview-DcYCrvlr.js} +2 -2
  57. package/public/assets/{pairs-DE_m6rrE.js → pairs-8o9nuG3V.js} +2 -2
  58. package/public/assets/{profile-icon-B3rfh77F.js → profile-icon-BsRyBMea.js} +2 -2
  59. package/public/assets/{project-profile-instructions-DQWZE0kA.js → project-profile-instructions-DETKFn_E.js} +2 -2
  60. package/public/assets/{projects-Br2Bd2h1.js → projects-M_m6AHws.js} +2 -2
  61. package/public/assets/{prompt-settings-BKvFsvGD.js → prompt-settings-C_mNCgeL.js} +2 -2
  62. package/public/assets/provider-display-BEeLXC_D.js +2 -0
  63. package/public/assets/{registry-Cw38_6gW.js → registry-DDHHHE5N.js} +2 -2
  64. package/public/assets/run-card-Dt4I4jaP.js +2 -0
  65. package/public/assets/{secrets-DxI-vzSq.js → secrets-BkVwxI5O.js} +2 -2
  66. package/public/assets/{service-access-settings-D9s_VJbd.js → service-access-settings-Crq-6a-B.js} +2 -2
  67. package/public/assets/{settings-CKZTVGIx.js → settings-CYcavMFu.js} +2 -2
  68. package/public/assets/shipping-H6gzEeZB.js +2 -0
  69. package/public/assets/{stats-DyUBKyWI.js → stats-BJVeWkpV.js} +2 -2
  70. package/public/assets/{status-dot-DYqlEUhJ.js → status-dot-BcHyw1Vw.js} +2 -2
  71. package/public/assets/store-DKPDIZBe.js +9 -0
  72. package/public/assets/{tasks-Bh7Chr4-.js → tasks-qJjlQPpx.js} +2 -2
  73. package/public/assets/{teams-bowI7qVY.js → teams-Cd5Sk1uX.js} +2 -2
  74. package/public/assets/{terminal-viewer-BgVLHdsU.js → terminal-viewer-CpY5_m7W.js} +3 -3
  75. package/public/assets/{terminal-viewer-impl-Dz22UPy6.js → terminal-viewer-impl-DI_2llFR.js} +2 -2
  76. package/public/assets/{use-agent-terminal-DSQQ-iY7.js → use-agent-terminal-0yJA_buM.js} +2 -2
  77. package/public/assets/{use-decisions-samMQhQf.js → use-decisions-uJXHf34W.js} +2 -2
  78. package/public/assets/{use-project-list-59LheVP7.js → use-project-list-CpWl6eHQ.js} +2 -2
  79. package/public/assets/{use-selectors-BZa25xEB.js → use-selectors-BdxBW8mL.js} +2 -2
  80. package/public/assets/{user-avatar-DHxlvUvV.js → user-avatar-3IndfpOc.js} +2 -2
  81. package/public/assets/{versioned-prompt-editor-BmED5DnJ.js → versioned-prompt-editor-CIVg7XB1.js} +2 -2
  82. package/public/assets/{work-queue-CSvpuNLz.js → work-queue-B9rnLwHZ.js} +2 -2
  83. package/public/assets/{workflow-marker-DAZcj5qt.js → workflow-marker-X8wxyhzC.js} +2 -2
  84. package/public/assets/{workflows-DdmxRSyg.js → workflows-o5YyMFW7.js} +2 -2
  85. package/public/assets/{workspaces-BxeKfVuF.js → workspaces-CzEGoAng.js} +2 -2
  86. package/public/cosmos.html +4 -4
  87. package/public/index.html +28 -28
  88. package/runner/plugins/claude/.claude-plugin/plugin.json +1 -1
  89. package/runner/src/adapters/claude-headless.ts +1 -1
  90. package/runner/src/adapters/claude-launch-exit.ts +26 -0
  91. package/runner/src/adapters/claude-session-probe.ts +1 -1
  92. package/runner/src/adapters/claude-tmux.ts +38 -10
  93. package/runner/src/adapters/claude.ts +28 -75
  94. package/runner/src/adapters/provider-quota-harvest.ts +20 -71
  95. package/runner/src/agent-runner/lifecycle-milestones.ts +2 -2
  96. package/runner/src/agent-runner/liveness-exchange.ts +104 -0
  97. package/runner/src/claude-system-prompt.ts +49 -0
  98. package/runner/src/launch-assembly.ts +14 -10
  99. package/runner/src/profile-home.ts +1 -1
  100. package/runner/src/project-instructions.ts +2 -2
  101. package/runner/src/provider-command.ts +18 -0
  102. package/runner/src/provider-setup.ts +2 -1
  103. package/runner/src/runner-core.ts +14 -15
  104. package/runner/src/runner-helpers.ts +0 -8
  105. package/runner/src/session-scratch.ts +6 -2
  106. package/runner/test-support/provisioning-context.ts +4 -2
  107. package/runner/test-support/runner-harness.ts +2 -1
  108. package/scripts/steward-policy-cleanup.ts +2 -2
  109. package/src/agent-authz.ts +2 -2
  110. package/src/agent-drive-arbiter.ts +3 -8
  111. package/src/agent-exit-cleanup-async.ts +2 -2
  112. package/src/agent-exit-cleanup.ts +23 -19
  113. package/src/agent-lifecycle-events.ts +112 -85
  114. package/src/agent-ref.ts +3 -2
  115. package/src/agent-status-events.ts +43 -14
  116. package/src/auth/password.ts +4 -3
  117. package/src/auth/session-refresh.ts +1 -1
  118. package/src/auth/session.test-fixture.ts +2 -2
  119. package/src/auth/session.ts +7 -7
  120. package/src/automations/crud.ts +8 -81
  121. package/src/automations/dispatch.ts +33 -58
  122. package/src/automations/index.ts +1 -1
  123. package/src/automations/persistence.ts +65 -0
  124. package/src/automations/precheck.ts +18 -16
  125. package/src/automations/read-async.ts +11 -0
  126. package/src/automations/reconcile.ts +30 -39
  127. package/src/automations/storage.ts +33 -10
  128. package/src/automations/write-async.ts +33 -0
  129. package/src/branch-land-notice.ts +3 -3
  130. package/src/branch-landed.ts +25 -23
  131. package/src/bus.ts +21 -19
  132. package/src/cd-deploy-notify.ts +2 -2
  133. package/src/ci/notifications.ts +4 -4
  134. package/src/ci/root-missing.ts +3 -3
  135. package/src/ci/transition-notify.ts +5 -5
  136. package/src/claude-app/oauth-http.ts +3 -3
  137. package/src/cli/workflow.ts +12 -3
  138. package/src/commands-db.ts +4 -4
  139. package/src/commands-write.ts +8 -0
  140. package/src/compaction-watch.ts +10 -8
  141. package/src/config-store-write.ts +2 -2
  142. package/src/config.ts +4 -11
  143. package/src/connectors.ts +10 -8
  144. package/src/context-advisory.ts +22 -21
  145. package/src/db/agent-action-write.ts +36 -0
  146. package/src/db/agent-control-async.ts +9 -0
  147. package/src/db/agent-control-write.ts +49 -0
  148. package/src/db/agent-effort-observe.ts +22 -0
  149. package/src/db/agent-meta.ts +14 -3
  150. package/src/db/agents-async.ts +8 -5
  151. package/src/db/agents.ts +15 -12
  152. package/src/db/auth-identities.ts +2 -2
  153. package/src/db/automation-read-ops.ts +11 -0
  154. package/src/db/automation-write-ops.ts +46 -0
  155. package/src/db/blackboard.ts +2 -2
  156. package/src/db/browser-session-write.ts +32 -0
  157. package/src/db/bus-outbox-write-ops.ts +3 -1
  158. package/src/db/checkout-refresh-schema.ts +47 -0
  159. package/src/db/checkout-refresh.ts +5 -5
  160. package/src/db/command-agent-profile-backfill.ts +39 -0
  161. package/src/db/command-agent-profiles.ts +5 -1
  162. package/src/db/command-retention.ts +126 -0
  163. package/src/db/commands-planner-stats.test-fixture.ts +8 -0
  164. package/src/db/commands-read-ops.ts +16 -0
  165. package/src/db/connection.ts +1 -182
  166. package/src/db/context-carry-write.ts +74 -0
  167. package/src/db/continuations.ts +4 -4
  168. package/src/db/db-error-registry.ts +12 -0
  169. package/src/db/gateway.ts +26 -133
  170. package/src/db/history-retention-ops.ts +13 -0
  171. package/src/db/inbox-async.ts +2 -2
  172. package/src/db/inbox-read-ops.ts +1 -1
  173. package/src/db/inbox.ts +9 -3
  174. package/src/db/issue-associations.ts +2 -2
  175. package/src/db/issue-refs-async.ts +4 -1
  176. package/src/db/issue-refs-read-ops.ts +6 -0
  177. package/src/db/issue-refs.ts +8 -8
  178. package/src/db/kernel-event-log-schema.ts +2 -0
  179. package/src/db/land-boundaries.ts +51 -5
  180. package/src/db/land-gate-attestations.ts +2 -2
  181. package/src/db/landing-receipts.ts +2 -2
  182. package/src/db/lifecycle-context-write.ts +5 -0
  183. package/src/db/lifecycle-tail-async.ts +76 -0
  184. package/src/db/lifecycle-tail-write-ops.ts +62 -0
  185. package/src/db/lifecycle-write-window.ts +2 -1
  186. package/src/db/lineage-pair-dedupe-sql.ts +3 -3
  187. package/src/db/main-connection-mode.ts +11 -0
  188. package/src/db/main-connection-writes.ts +20 -2
  189. package/src/db/maintenance-job-runs-async.ts +2 -2
  190. package/src/db/maintenance-job-write-ops.ts +1 -1
  191. package/src/db/maintenance-pauses.ts +2 -2
  192. package/src/db/message-send.ts +4 -0
  193. package/src/db/messages.ts +31 -25
  194. package/src/db/migrations.ts +18 -1
  195. package/src/db/notification-write-ops.ts +19 -0
  196. package/src/db/operator-answer-write.ts +19 -0
  197. package/src/db/orchestrators.ts +10 -4
  198. package/src/db/pairs-async.ts +22 -1
  199. package/src/db/pairs-write-ops.ts +8 -3
  200. package/src/db/pairs.ts +41 -48
  201. package/src/db/plan-dispatch-schema.ts +5 -1
  202. package/src/db/plan-ready-notices.ts +43 -0
  203. package/src/db/plan-tools-include-migration.ts +36 -0
  204. package/src/db/plans-write-ops.ts +2 -0
  205. package/src/db/presence-buffer.ts +5 -0
  206. package/src/db/presence-loop.ts +18 -23
  207. package/src/db/presence-write.ts +0 -6
  208. package/src/db/project-entries-async.ts +14 -0
  209. package/src/db/project-entries-read-ops.ts +5 -1
  210. package/src/db/project-entries-write-ops.ts +8 -0
  211. package/src/db/project-entries.ts +15 -12
  212. package/src/db/project-journal.ts +2 -2
  213. package/src/db/project-trust-approvals.ts +2 -2
  214. package/src/db/prompt-templates.ts +4 -4
  215. package/src/db/read-op-marshaling-fixtures.ts +18 -0
  216. package/src/db/read-ops.ts +4 -0
  217. package/src/db/retention.ts +13 -2
  218. package/src/db/review-publish-write.ts +8 -0
  219. package/src/db/runtime-admin-async.ts +19 -0
  220. package/src/db/runtime-admin-write-ops.ts +36 -0
  221. package/src/db/runtime-tail-async.ts +288 -0
  222. package/src/db/runtime-tail-write-ops.ts +97 -0
  223. package/src/db/schema.ts +4 -1
  224. package/src/db/spawn-budget.ts +5 -0
  225. package/src/db/spawn-delegation.ts +1 -11
  226. package/src/db/sql-audit-indexes.ts +3 -0
  227. package/src/db/stats-async.ts +5 -3
  228. package/src/db/task-entity.ts +5 -4
  229. package/src/db/team-claim-policy.ts +60 -0
  230. package/src/db/teams-async.ts +4 -0
  231. package/src/db/teams-write-ops.ts +4 -0
  232. package/src/db/terminal-events.ts +15 -0
  233. package/src/db/timer-cutover-bodies.ts +292 -0
  234. package/src/db/timer-cutover-core.ts +37 -278
  235. package/src/db/timer-cutover-state.ts +40 -0
  236. package/src/db/timer-cutover-write-ops.ts +1 -40
  237. package/src/db/token-db-write-ops.ts +2 -0
  238. package/src/db/token-standing-async.ts +12 -0
  239. package/src/db/token-standing.ts +6 -9
  240. package/src/db/token-usage-retention.ts +33 -0
  241. package/src/db/token-usage-write-ops.ts +1 -1
  242. package/src/db/token-usage.ts +4 -1
  243. package/src/db/user-invites-async.ts +13 -0
  244. package/src/db/user-invites.ts +3 -3
  245. package/src/db/users.ts +14 -6
  246. package/src/db/workspaces.ts +8 -5
  247. package/src/db/write-ops.ts +31 -12
  248. package/src/db/writer-fault-seam.ts +2 -9
  249. package/src/db/writer-fault-test-ops.ts +18 -0
  250. package/src/db/writer-gateway.ts +42 -46
  251. package/src/dev.ts +8 -3
  252. package/src/event-loop-lag.ts +4 -2
  253. package/src/event-loop-yield.ts +2 -2
  254. package/src/events.ts +10 -8
  255. package/src/host-provider-store.ts +5 -4
  256. package/src/hub-metrics-report.ts +5 -1
  257. package/src/hub-metrics.ts +38 -18
  258. package/src/injection-sweep-pacing.ts +6 -8
  259. package/src/land-gate-notifications.ts +11 -10
  260. package/src/land-gate-queue-progress.ts +3 -3
  261. package/src/land-settle-dispatch.ts +52 -92
  262. package/src/lifecycle-context-pressure.ts +11 -10
  263. package/src/lifecycle-manager.ts +104 -98
  264. package/src/maintenance/breaker.ts +18 -16
  265. package/src/maintenance/checkout-refresh.ts +18 -1
  266. package/src/maintenance/critical-job-watchdog.ts +3 -3
  267. package/src/maintenance/events.ts +3 -3
  268. package/src/maintenance/history-retention-job.ts +30 -0
  269. package/src/maintenance/jobs.ts +29 -67
  270. package/src/maintenance/long-running-work.ts +3 -1
  271. package/src/maintenance/offline-agent-prune-job.ts +28 -0
  272. package/src/maintenance/orphaned-sessions.ts +5 -4
  273. package/src/maintenance/pauses.ts +4 -10
  274. package/src/maintenance/plan-ready-notice-job.ts +71 -0
  275. package/src/maintenance/scheduler.ts +18 -13
  276. package/src/maintenance/stalled-delivery-pager.ts +4 -4
  277. package/src/maintenance/teams.ts +19 -30
  278. package/src/maintenance/workspaces.ts +17 -27
  279. package/src/managed-policy-escalation.ts +7 -7
  280. package/src/managed-policy.ts +3 -3
  281. package/src/managed-provider-gate.ts +5 -4
  282. package/src/managed-spawn-reservation.ts +5 -4
  283. package/src/mcp/index.ts +8 -8
  284. package/src/mcp/output-descriptions.ts +1 -1
  285. package/src/mcp/presentation.ts +2 -1
  286. package/src/mcp/runtime-output.ts +6 -1
  287. package/src/mcp/self-care-visibility.ts +2 -2
  288. package/src/mcp/spawn-targets-matrix.ts +10 -1
  289. package/src/mcp/tool-groups.ts +3 -2
  290. package/src/mcp/tool-roles.ts +81 -0
  291. package/src/mcp/tools-workspace.ts +1 -1
  292. package/src/mcp-blackboard-tools.ts +6 -5
  293. package/src/mcp-decision-tools.ts +7 -7
  294. package/src/mcp-egress-tools.ts +10 -9
  295. package/src/mcp-issue-activity-tools.ts +2 -2
  296. package/src/mcp-issue-association-tools.ts +4 -4
  297. package/src/mcp-issue-ref-tools.ts +3 -3
  298. package/src/mcp-merge-tools.ts +5 -5
  299. package/src/mcp-notification-subscription-tools.ts +14 -15
  300. package/src/mcp-output-levels.test-fixture.ts +1 -1
  301. package/src/mcp-profile-trust-tools.ts +1 -1
  302. package/src/mcp-project-journal-tools.ts +4 -12
  303. package/src/mcp-project-tools.ts +17 -18
  304. package/src/mcp-routing-policy-tools.ts +3 -2
  305. package/src/mcp-team-tools.ts +6 -6
  306. package/src/mcp-workflow-tools.ts +5 -3
  307. package/src/memory-area-injection.ts +13 -12
  308. package/src/memory-boundary-capture.ts +6 -6
  309. package/src/memory-injection.ts +45 -43
  310. package/src/merge-pause-maintenance.ts +5 -4
  311. package/src/notification-router.ts +44 -17
  312. package/src/notify.ts +5 -5
  313. package/src/observed-reset-refresh.ts +4 -4
  314. package/src/operator-alarm.ts +2 -5
  315. package/src/project-ingest.ts +6 -6
  316. package/src/project-instructions.ts +8 -1
  317. package/src/project-trust.ts +12 -11
  318. package/src/prompt-resolver.ts +3 -3
  319. package/src/provider-quota-async.ts +5 -7
  320. package/src/provider-timeline-event.ts +4 -7
  321. package/src/quota-band-delivery.ts +2 -2
  322. package/src/recipe-runner.ts +4 -3
  323. package/src/retention-config-store.ts +4 -3
  324. package/src/reviewer-pipeline.ts +11 -10
  325. package/src/routes/_route.ts +4 -4
  326. package/src/routes/_shared.ts +13 -12
  327. package/src/routes/agent-effort.ts +5 -5
  328. package/src/routes/agent-heartbeat-quota.ts +23 -0
  329. package/src/routes/agent-keep.ts +1 -1
  330. package/src/routes/agent-reporting.ts +1 -1
  331. package/src/routes/agent-self-effort.ts +5 -5
  332. package/src/routes/agent-sessions.ts +24 -24
  333. package/src/routes/agents-spawn.ts +1 -1
  334. package/src/routes/agents.ts +17 -119
  335. package/src/routes/auth.ts +14 -15
  336. package/src/routes/authority-audit.ts +2 -2
  337. package/src/routes/automations.ts +6 -5
  338. package/src/routes/channel-binding-delete.ts +1 -1
  339. package/src/routes/channel-output.ts +3 -3
  340. package/src/routes/claude-api-budget.ts +5 -5
  341. package/src/routes/commands-listing.ts +1 -1
  342. package/src/routes/commands.ts +29 -27
  343. package/src/routes/context-resets.ts +2 -2
  344. package/src/routes/coordinator-teams.ts +1 -1
  345. package/src/routes/decisions.ts +2 -2
  346. package/src/routes/host-secret-files.ts +1 -1
  347. package/src/routes/hosts.ts +11 -11
  348. package/src/routes/inbox.ts +3 -2
  349. package/src/routes/index.ts +2 -1
  350. package/src/routes/integrations.ts +9 -7
  351. package/src/routes/issue-activity.ts +2 -2
  352. package/src/routes/issue-associations.ts +4 -4
  353. package/src/routes/issue-refs.ts +3 -3
  354. package/src/routes/kernel-timers-operator.ts +1 -1
  355. package/src/routes/memory.ts +1 -1
  356. package/src/routes/merges.ts +11 -11
  357. package/src/routes/messages.ts +23 -17
  358. package/src/routes/orchestrator-bootstrap.ts +1 -1
  359. package/src/routes/orchestrator-host-registration.ts +2 -0
  360. package/src/routes/orchestrator-host-utilization.ts +2 -2
  361. package/src/routes/orchestrator-proxy.ts +2 -2
  362. package/src/routes/orchestrator.ts +27 -18
  363. package/src/routes/pairs.ts +8 -8
  364. package/src/routes/project-profile-instructions.ts +1 -1
  365. package/src/routes/project-trust.ts +1 -1
  366. package/src/routes/projects.ts +16 -16
  367. package/src/routes/prompt-templates.ts +12 -12
  368. package/src/routes/provider-quotas.ts +10 -10
  369. package/src/routes/run-command.ts +1 -1
  370. package/src/routes/secrets-backup.ts +2 -2
  371. package/src/routes/secrets-import.ts +1 -1
  372. package/src/routes/secrets-vault.ts +7 -7
  373. package/src/routes/settings.ts +1 -1
  374. package/src/routes/shipping.ts +9 -1
  375. package/src/routes/spawn-policy.ts +8 -7
  376. package/src/routes/sse.ts +2 -2
  377. package/src/routes/team-templates.ts +7 -7
  378. package/src/routes/teams.ts +10 -10
  379. package/src/routes/tokens.ts +18 -17
  380. package/src/routes/users.ts +24 -24
  381. package/src/routes/viewer-read.ts +3 -3
  382. package/src/routes/web-tool-calls.ts +4 -4
  383. package/src/routes/workflows.ts +2 -2
  384. package/src/routes/workspaces.ts +17 -16
  385. package/src/routing-policy-push.ts +6 -6
  386. package/src/runtime-grant.ts +17 -11
  387. package/src/runtime-tokens.ts +20 -28
  388. package/src/secrets-broker/config.ts +1 -1
  389. package/src/secrets-broker/service.ts +2 -2
  390. package/src/security.ts +3 -1
  391. package/src/self-resume-carry.ts +21 -110
  392. package/src/self-resume-supersede.ts +4 -3
  393. package/src/self-resume.ts +54 -66
  394. package/src/server-background.ts +1 -1
  395. package/src/server.ts +21 -22
  396. package/src/services/agent-action.ts +12 -31
  397. package/src/services/agent-effort.ts +7 -32
  398. package/src/services/agent-hang-watchdog-job.ts +2 -2
  399. package/src/services/agent-heartbeat.ts +166 -0
  400. package/src/services/agent-reporting.ts +1 -1
  401. package/src/services/browser-inventory.ts +3 -3
  402. package/src/services/browser-session-state.ts +3 -2
  403. package/src/services/browser-session.ts +21 -27
  404. package/src/services/bus-command-update.ts +9 -9
  405. package/src/services/checkout-refresh-state.ts +1 -0
  406. package/src/services/checkout-refresh.test-fixture.ts +3 -2
  407. package/src/services/command-host.ts +7 -2
  408. package/src/services/coordinator-digest.ts +3 -2
  409. package/src/services/dispatch-command.ts +12 -13
  410. package/src/services/effective-launch-driver.ts +14 -9
  411. package/src/services/host-changed-paths.test-fixture.ts +5 -6
  412. package/src/services/host-project-readiness.ts +2 -2
  413. package/src/services/host-providers.ts +1 -1
  414. package/src/services/host-utilization.ts +9 -9
  415. package/src/services/idle-agent-digest.ts +17 -12
  416. package/src/services/idle-epics.ts +4 -4
  417. package/src/services/interrupt-agent.ts +12 -9
  418. package/src/services/issue-activity.ts +3 -3
  419. package/src/services/issue-associations.ts +15 -18
  420. package/src/services/issue-lifecycle-comments.ts +3 -2
  421. package/src/services/issue-lifecycle.ts +14 -13
  422. package/src/services/issue-refs.ts +4 -5
  423. package/src/services/kernel-timers-cutover-chaos-worker.ts +1 -1
  424. package/src/services/kernel-timers-ingress-chaos-worker.ts +6 -1
  425. package/src/services/kernel-timers-ingress.ts +3 -3
  426. package/src/services/kernel-timers-message-authority-chaos-worker.ts +3 -10
  427. package/src/services/kernel-timers-operator-chaos-worker.ts +3 -2
  428. package/src/services/kernel-timers-outbox.ts +1 -1
  429. package/src/services/kernel-timers-stage10-operator.ts +1 -1
  430. package/src/services/kernel-timers.ts +4 -3
  431. package/src/services/land-gate-placement.ts +11 -2
  432. package/src/services/land-issue-relations.ts +5 -4
  433. package/src/services/land-restart-retry.ts +7 -6
  434. package/src/services/landed-ancestry.ts +6 -5
  435. package/src/services/legacy-landing-host.ts +4 -1
  436. package/src/services/legacy-landing-recovery.ts +8 -6
  437. package/src/services/managed-running.ts +17 -17
  438. package/src/services/memory-areas.ts +3 -3
  439. package/src/services/merge-pause.ts +9 -15
  440. package/src/services/operator-decisions.ts +37 -41
  441. package/src/services/orchestrator-registration-recovery.ts +6 -2
  442. package/src/services/orchestrator-upgrade.ts +14 -12
  443. package/src/services/permission-decision.ts +4 -3
  444. package/src/services/plan-ci-notice.ts +1 -1
  445. package/src/services/plan-conformance.ts +4 -4
  446. package/src/services/plan-dispatch-policy.ts +10 -3
  447. package/src/services/plan-dispatch.ts +35 -14
  448. package/src/services/plan-graph.ts +39 -261
  449. package/src/services/plan-import.ts +1 -1
  450. package/src/services/plan-live-bindings.ts +8 -37
  451. package/src/services/plan-task-create.ts +1 -1
  452. package/src/services/plan-terminal-refs.ts +2 -12
  453. package/src/services/plan-wake-digest.ts +8 -3
  454. package/src/services/profile-trust.ts +2 -2
  455. package/src/services/project.ts +11 -10
  456. package/src/services/prompt-templates.ts +6 -5
  457. package/src/services/provider-hold-nudge.ts +9 -9
  458. package/src/services/provider-hold-record.ts +13 -12
  459. package/src/services/provider-state-audit.ts +19 -18
  460. package/src/services/provisioning-harness/driver.ts +1 -1
  461. package/src/services/provisioning-harness/fixture-matrix.ts +3 -3
  462. package/src/services/provisioning-harness/lifecycle.ts +24 -22
  463. package/src/services/provisioning-harness/prepare.ts +16 -17
  464. package/src/services/rate-limit-resume.ts +10 -9
  465. package/src/services/red-main-auto-revert.ts +7 -5
  466. package/src/services/red-main-controller-deps.ts +20 -3
  467. package/src/services/red-main-incident.ts +3 -1
  468. package/src/services/red-main-quarantine.ts +2 -3
  469. package/src/services/register-agent.ts +22 -22
  470. package/src/services/restart-project-role.ts +1 -1
  471. package/src/services/restart-spawn.ts +17 -16
  472. package/src/services/review-publish.ts +7 -24
  473. package/src/services/review-ref-reconcile.ts +29 -38
  474. package/src/services/runtime-dependency-alert.ts +27 -0
  475. package/src/services/scheduler-command.ts +11 -9
  476. package/src/services/scheduler.ts +5 -5
  477. package/src/services/self-effort-grant.ts +5 -25
  478. package/src/services/send-message.ts +16 -33
  479. package/src/services/shutdown-agent.ts +17 -14
  480. package/src/services/spawn-agent.ts +12 -41
  481. package/src/services/spawn-chain-inspector.ts +1 -1
  482. package/src/services/spawn-cwd-placement.ts +21 -5
  483. package/src/services/spawn-issue-activity.ts +8 -6
  484. package/src/services/spawn-launch.ts +17 -1
  485. package/src/services/spawn-loss-escalation.ts +1 -1
  486. package/src/services/spawn-registration-watchdog.ts +2 -2
  487. package/src/services/spawn-repo-probe.ts +7 -5
  488. package/src/services/spawn-tree-cleanup.ts +7 -8
  489. package/src/services/steward-reaper.ts +4 -4
  490. package/src/services/task-context-hydration.ts +25 -1
  491. package/src/services/task-delivery.ts +20 -34
  492. package/src/services/task-entity.ts +40 -203
  493. package/src/services/task-issue-link.ts +13 -20
  494. package/src/services/task-issue-weave.ts +12 -11
  495. package/src/services/task-lifecycle.ts +27 -31
  496. package/src/services/task-repo-cwd.ts +37 -0
  497. package/src/services/task-review-hold.ts +4 -17
  498. package/src/services/task-semantic-events.ts +13 -11
  499. package/src/services/task-state-reconcile.ts +3 -4
  500. package/src/services/task-validation.ts +2 -2
  501. package/src/services/team-templates.ts +5 -5
  502. package/src/services/team.ts +31 -98
  503. package/src/services/terminal-provider-exit.ts +6 -5
  504. package/src/services/timer-message-fire-chaos-worker.ts +7 -3
  505. package/src/services/timer-message-fire-outbox.ts +1 -1
  506. package/src/services/transient-agent-reaper.ts +22 -19
  507. package/src/services/turn-reconcile-audit.ts +9 -9
  508. package/src/services/user-onboarding.ts +8 -6
  509. package/src/services/user-profile.ts +6 -9
  510. package/src/services/work-finality.ts +16 -4
  511. package/src/services/workflow-auto-start.ts +2 -1
  512. package/src/services/workflow-caller.ts +61 -4
  513. package/src/services/workflow-child-proof-worker.ts +1 -1
  514. package/src/services/workflow-control-proof-worker.ts +1 -1
  515. package/src/services/workflow-coordinator.ts +8 -2
  516. package/src/services/workflow-decisions.ts +5 -5
  517. package/src/services/workflow-deferred-cleanup.ts +1 -1
  518. package/src/services/workflow-effect-driver.ts +42 -19
  519. package/src/services/workflow-effects.ts +41 -27
  520. package/src/services/workflow-pins.ts +7 -4
  521. package/src/services/workflow-reconcile.ts +1 -0
  522. package/src/services/workflow-resume-pins.ts +3 -3
  523. package/src/services/workflow-routing-defaults.ts +18 -0
  524. package/src/services/workflow-run-view.ts +4 -3
  525. package/src/services/workflow-run.test-fixture.ts +1 -1
  526. package/src/services/workflow-runs.ts +34 -22
  527. package/src/services/workflow-supervisor-state.ts +21 -1
  528. package/src/services/workflow-supervisor.ts +4 -3
  529. package/src/services/workspace-recovery.ts +4 -3
  530. package/src/spawn-admission.ts +10 -2
  531. package/src/spawn-placement.ts +27 -9
  532. package/src/spawn-targets.ts +8 -8
  533. package/src/sse-agent-status.ts +15 -6
  534. package/src/sse.ts +49 -24
  535. package/src/startup-context-injection.ts +61 -57
  536. package/src/steward-handoff.ts +4 -3
  537. package/src/steward-policy-cleanup.ts +9 -8
  538. package/src/steward.ts +2 -2
  539. package/src/task-dispatch-live.ts +5 -2
  540. package/src/task-dispatcher.ts +2 -2
  541. package/src/token-db.ts +2 -2
  542. package/src/token-usage-async.ts +2 -4
  543. package/src/user-awaiting-reply.ts +5 -5
  544. package/src/workspace-actions.ts +24 -29
  545. package/src/workspace-auto-merge.ts +14 -12
  546. package/src/workspace-changed-paths-client.ts +16 -5
  547. package/src/workspace-cleanup-stale.ts +30 -7
  548. package/src/workspace-command-settle.ts +24 -16
  549. package/src/workspace-config.ts +1 -1
  550. package/src/workspace-conflict-resolver.ts +9 -7
  551. package/src/workspace-dead-owner-recovery.ts +5 -4
  552. package/src/workspace-escalation.ts +12 -11
  553. package/src/workspace-human-escalation.ts +3 -3
  554. package/src/workspace-land-gate-watchdog.ts +3 -3
  555. package/src/workspace-land-refusal.ts +20 -0
  556. package/src/workspace-land-stopped.ts +1 -0
  557. package/src/workspace-merge.ts +10 -26
  558. package/src/workspace-nothing-to-land-notice.ts +2 -2
  559. package/src/workspace-orphans.ts +18 -37
  560. package/src/workspace-pr-completion.ts +10 -8
  561. package/src/workspace-rekey.ts +3 -3
  562. package/src/workspace-steward-failure.ts +2 -2
  563. package/src/workspace-steward-wake.ts +2 -2
  564. package/public/assets/agent-liveness-CaNwK0Zf.js +0 -2
  565. package/public/assets/provider-display-DsuWMvVL.js +0 -2
  566. package/public/assets/run-card-CpKqRbhg.js +0 -2
  567. package/public/assets/shipping-B9YtnwtG.js +0 -2
  568. package/public/assets/store-DcClXLH0.js +0 -9
  569. package/runner/src/agent-runner/http-liveness.ts +0 -85
  570. package/src/db/on-loop-write-transaction.ts +0 -34
package/src/events.ts CHANGED
@@ -19,7 +19,7 @@ export interface RelayEvent {
19
19
 
20
20
  type Listener = (event: RelayEvent) => void;
21
21
 
22
- const listeners = new Set<Listener>();
22
+ const listeners = new Map<Listener, string>();
23
23
  const fanoutQueue: Array<{ event: RelayEvent; lastSeq: number }> = [];
24
24
  let notifying = false;
25
25
 
@@ -33,12 +33,13 @@ export function emitRelayEvent(input: Omit<RelayEvent, "seq" | "timestamp">): Pr
33
33
  const appended = appendRelayEventInternal(input.type, input.source, input.data, input.subject, timestamp)!;
34
34
  return Promise.resolve(fanOutAppendedEvent(input, appended, timestamp));
35
35
  }
36
- return emitRelayEventAsync(structuredClone(input), { idempotencyKey: crypto.randomUUID() });
36
+ // #3614 — keyless: nothing resubmits this append, so a ledger row would only cost writes.
37
+ return trackRelayEvent(appendAndFanOut(structuredClone(input), {}), input.type);
37
38
  });
38
39
  }
39
40
 
40
41
  /** #3606 — fan out to the current listeners only: no bus_outbox row, no idempotency-ledger row, no
41
- * replay. For state the next tick supersedes anyway (dashboard viewer presence). */
42
+ * replay. For state the next tick supersedes anyway (dashboard viewer presence, agent liveness ticks). */
42
43
  export function emitEphemeralRelayEvent(input: Omit<RelayEvent, "seq" | "timestamp" | "ephemeral">): RelayEvent {
43
44
  return fanOutAppendedEvent({ ...input, ephemeral: true }, { seq: 0, lastSeq: 0 }, Date.now());
44
45
  }
@@ -71,7 +72,8 @@ async function appendAndFanOut(
71
72
  } catch (error) {
72
73
  // H5: a lost acknowledgement says nothing about commit. Re-submit the exact logical append
73
74
  // with the same durable key; the writer returns the first result without a second insert.
74
- if (!(error instanceof WriteIndeterminateError)) throw error;
75
+ // A keyless emit is at-most-once: it surfaces the error, and a committed row stays replayable.
76
+ if (!(error instanceof WriteIndeterminateError) || !options.idempotencyKey) throw error;
75
77
  appended = await getGateway().write("bus.append", args, { critical: true, idempotencyKey: options.idempotencyKey }) as unknown as typeof appended;
76
78
  }
77
79
  // A retry ledger result proves durable storage, not that this process owns a fresh live edge.
@@ -122,19 +124,19 @@ export function fanOutAppendedEvent(
122
124
 
123
125
  function notify(event: RelayEvent): void {
124
126
  measureEmit(event.type, true, () => {
125
- for (const listener of listeners) {
127
+ for (const [listener, name] of listeners) {
126
128
  try {
127
129
  listener(event);
128
130
  } catch (error) {
129
131
  // Event projections are isolated so one transport cannot block another; a failed one (e.g. a
130
132
  // BUSY write in a projection, #3643) is logged, not swallowed.
131
- console.warn(`[events] ${event.type} listener failed: ${errMessage(error)}`);
133
+ console.warn(`[events] ${event.type} listener ${name} failed: ${errMessage(error)}`, error instanceof Error ? error.stack : error);
132
134
  }
133
135
  }
134
136
  });
135
137
  }
136
138
 
137
- export function subscribeRelayEvents(listener: Listener): () => void {
138
- listeners.add(listener);
139
+ export function subscribeRelayEvents(listener: Listener, name = listener.name || "<anonymous>"): () => void {
140
+ listeners.set(listener, name);
139
141
  return () => listeners.delete(listener);
140
142
  }
@@ -1,8 +1,9 @@
1
+ import { setConfigAsync } from "./config-store-write";
1
2
  // #2855 — DB-backed readers for per-host provider enablement. Separate from
2
3
  // host-provider-enablement.ts (a config-store-imported leaf) so this module can
3
4
  // import getConfig/listConfig without closing a cycle.
4
5
  import { errMessage, type SpawnProvider } from "@humwick/sdk";
5
- import { getConfig, listConfig, setConfig } from "./config-store";
6
+ import { getConfig, listConfig } from "./config-store";
6
7
  import { listOrchestratorIds } from "./db";
7
8
  import {
8
9
  HOST_PROVIDER_ENABLEMENT_NAMESPACE,
@@ -75,11 +76,11 @@ export function hostProviderRefusal(hostId: string, provider: SpawnProvider): st
75
76
  /** One-time env migration: missing/unmarked rows merge omitted disables
76
77
  * without touching operator entries. In-env opt-ins store explicit true;
77
78
  * everything else outside env seeds as disabled. */
78
- export function seedHostProvidersFromEnv(
79
+ export async function seedHostProvidersFromEnv(
79
80
  hostId: string,
80
81
  envProviders: readonly string[],
81
82
  switchableCatalog: readonly SpawnProvider[],
82
- ): HostProviderEnablementSetting | null {
83
+ ): Promise<HostProviderEnablementSetting | null> {
83
84
  const existing = getHostProviderSetting(hostId);
84
85
  if (existing?.seeded) return null;
85
86
  const env = new Set(envProviders);
@@ -98,5 +99,5 @@ export function seedHostProvidersFromEnv(
98
99
  providers[provider] = { enabled: false, reason: "migrated from host env list" };
99
100
  }
100
101
  }
101
- return setConfig(HOST_PROVIDER_ENABLEMENT_NAMESPACE, hostId, { providers, seeded: true }, "migration-seed").value;
102
+ return (await setConfigAsync(HOST_PROVIDER_ENABLEMENT_NAMESPACE, hostId, { providers, seeded: true }, "migration-seed")).value;
102
103
  }
@@ -20,6 +20,7 @@ export function hubMetricsReport(now = Date.now()) {
20
20
  const emits = emitTable.snapshot(now);
21
21
  const readerOps = readerOpTable.snapshot(now);
22
22
  const cpuMs = callers.reduce((total, row) => total + row.cpuMs, 0);
23
+ const backgroundCpuMs = callers.find((row) => row.key === BACKGROUND_KEY)?.cpuMs ?? 0;
23
24
  const calls = callers.reduce((total, row) => total + row.count, 0);
24
25
  return {
25
26
  generatedAt: now,
@@ -28,7 +29,9 @@ export function hubMetricsReport(now = Date.now()) {
28
29
  mainThread: {
29
30
  cpuMs: Math.round(cpuMs),
30
31
  cpuPct: Math.round((cpuMs / windowMs) * 10_000) / 100,
31
- backgroundCpuMs: Math.round(callers.find((row) => row.key === BACKGROUND_KEY)?.cpuMs ?? 0),
32
+ backgroundCpuMs: Math.round(backgroundCpuMs),
33
+ // #3798 — the share of main-thread CPU a named frame (route, bus frame, timer, job) accounts for.
34
+ namedCpuPct: cpuMs > 0 ? Math.round(((cpuMs - backgroundCpuMs) / cpuMs) * 10_000) / 100 : 100,
32
35
  },
33
36
  callers: {
34
37
  count: calls,
@@ -62,6 +65,7 @@ export function hubLoadCheck(now = Date.now()): HealthCheck {
62
65
  busConnections: report.connections.bus,
63
66
  callsPerSec: report.callers.perSec,
64
67
  mainThreadCpuPct: report.mainThread.cpuPct,
68
+ mainThreadNamedCpuPct: report.mainThread.namedCpuPct,
65
69
  },
66
70
  };
67
71
  }
@@ -4,7 +4,7 @@
4
4
  // so caller CPU is split at marks (caller start/end, reader resumes): the CPU between two marks goes
5
5
  // to the caller that ran. docs/operations.md "Hub load metrics" describes the model and its limits.
6
6
 
7
- import { runWithLoopCallsite } from "./loop-block-attribution";
7
+ import { resolveLoopCallsite, runWithLoopCallsite, type LoopCallsiteContext } from "./loop-block-attribution";
8
8
 
9
9
  const BUCKET_MS = 10_000;
10
10
  const BUCKETS = 6;
@@ -109,25 +109,45 @@ export function markCallerResume(owner: string | undefined): void {
109
109
  ledgerOwner = owner;
110
110
  }
111
111
 
112
- /** Run `run` as caller `key`: one request or frame, its wall time and its main-thread CPU segments. */
113
- export async function trackCaller<T>(key: string, run: () => T | Promise<T>): Promise<T> {
114
- const startedAt = performance.now();
115
- markCallerResume(key);
116
- try {
117
- return await run();
118
- } finally {
119
- // The code finishing the caller is the caller itself, so the segment since the last mark is its own.
120
- const now = Date.now();
121
- settleCpu(key, now);
122
- ledgerOwner = undefined;
123
- callerTable.add(key, now, 1, performance.now() - startedAt);
124
- }
112
+ /** #3798 — run `run` as named frame `ctx.callsite` (request, bus frame, timer tick, job): its count, wall
113
+ * time and main-thread CPU. Opens the loop callsite scope, so reader and writer results inside it are
114
+ * charged back to it. Returns what `run` returns (a sync body stays sync); on exit the ledger goes back
115
+ * to the enclosing frame (a job inside the scheduler tick). */
116
+ export function runInFrame<T>(ctx: LoopCallsiteContext & { callsite: string }, run: () => T): T {
117
+ const outer = resolveLoopCallsite();
118
+ return runWithLoopCallsite(ctx, () => {
119
+ const key = ctx.callsite;
120
+ const startedAt = performance.now();
121
+ markCallerResume(key);
122
+ const end = () => {
123
+ // The code finishing the frame is the frame itself, so the segment since the last mark is its own.
124
+ const now = Date.now();
125
+ settleCpu(key, now);
126
+ ledgerOwner = outer;
127
+ callerTable.add(key, now, 1, performance.now() - startedAt);
128
+ };
129
+ let result: T;
130
+ try {
131
+ result = run();
132
+ } catch (error) {
133
+ end();
134
+ throw error;
135
+ }
136
+ if (result instanceof Promise) return result.finally(end) as T;
137
+ end();
138
+ return result;
139
+ });
140
+ }
141
+
142
+ /** `runInFrame` as a callback, for a timer or interval body: `setInterval(framed("timer x", tick), ms)`. */
143
+ export function framed<A extends unknown[], T>(key: string, fn: (...args: A) => T): (...args: A) => T {
144
+ const ctx = { callsite: key };
145
+ return (...args: A) => runInFrame(ctx, () => fn(...args));
125
146
  }
126
147
 
127
- /** `trackCaller` for a caller with no request scope of its own (a bus frame): also opens the loop
128
- * callsite scope, so reader resumes and on-loop frames inside it are attributed to it. */
129
- export function runAsCaller<T>(callsite: string, run: () => T | Promise<T>): Promise<T> {
130
- return runWithLoopCallsite({ callsite }, () => trackCaller(callsite, run));
148
+ /** A bus frame: always a promise, so a sync throw reaches the frame queue's `catch`. */
149
+ export async function runAsCaller<T>(callsite: string, run: () => T | Promise<T>): Promise<T> {
150
+ return runInFrame({ callsite }, run);
131
151
  }
132
152
 
133
153
  // ── Emits ─────────────────────────────────────────────────────────────────────────────────────────
@@ -2,23 +2,21 @@
2
2
  // one boundary per macrotask (`forEachPaced`); a backlog (a long pause, a crash that lost many settles)
3
3
  // is drained in runs of this budget, well inside the 30s job deadline, re-run this soon while a run
4
4
  // reports `moreWork`.
5
- import { isSqliteBusyError, withFastFailConnection, writerLockAvailable } from "./db/connection";
5
+ import { isSqliteBusyError } from "./db/connection";
6
6
  import { forEachPaced } from "./event-loop-yield";
7
7
 
8
8
  export const INJECTION_SWEEP_RUN_BUDGET_MS = 10_000;
9
9
  export const INJECTION_SWEEP_BACKLOG_INTERVAL_MS = 5_000;
10
- const SWEEP_BUSY_TIMEOUT_MS = 40;
11
10
 
12
- /** Each unit runs on the fast-fail connection, so a write under another writer's lock fails in 40ms
13
- * instead of the shared connection's ~1s; the run then stops with `moreWork` and retries soon. */
14
- export async function pacedInjectionSweep<T>(items: () => Iterable<T>, unit: (item: T) => void, signal?: AbortSignal): Promise<{ moreWork: boolean }> {
11
+ /** The gateway owns write contention; wait for each unit before continuing the bounded sweep. */
12
+ export async function pacedInjectionSweep<T>(items: () => Iterable<T>, unit: (item: T) => unknown, signal?: AbortSignal): Promise<{ moreWork: boolean }> {
15
13
  let contended = false;
16
- return forEachPaced(items, (item) => {
14
+ return forEachPaced(items, async (item) => {
17
15
  try {
18
- withFastFailConnection(SWEEP_BUSY_TIMEOUT_MS, () => unit(item));
16
+ await unit(item);
19
17
  } catch (error) {
20
18
  if (!isSqliteBusyError(error)) throw error;
21
19
  contended = true;
22
20
  }
23
- }, { signal, budgetMs: INJECTION_SWEEP_RUN_BUDGET_MS, ready: () => !contended && writerLockAvailable(SWEEP_BUSY_TIMEOUT_MS) });
21
+ }, { signal, budgetMs: INJECTION_SWEEP_RUN_BUDGET_MS, ready: () => !contended });
24
22
  }
@@ -1,7 +1,8 @@
1
+ import { createActivityEventAsync } from "./db/activity-async";
1
2
  import type { LandGateRunResult, WorkspaceRecord } from "@humwick/sdk";
2
3
  import { emitRelayEvent } from "./events";
3
4
  import { routeNotification, INFORMATIONAL_TTL_MS } from "./notification-router";
4
- import { createActivityEvent } from "./db";
5
+
5
6
  import { resolveCoordinatorAgentId } from "./steward-identity";
6
7
  import { stewardFallbackTarget } from "./config";
7
8
  import { routeLandGateFailure } from "./land-gate-failure-routing";
@@ -51,7 +52,7 @@ export async function notifyLandGateFailed(input: {
51
52
 
52
53
  emitRelayEvent({ type: "workspace.land_gate_failed", source: "server", subject: workspace.id, data: payload });
53
54
 
54
- createActivityEvent({
55
+ (await createActivityEventAsync({
55
56
  clientId: `workspace-land-gate-failed-${workspace.id}-${Date.now()}`,
56
57
  kind: "state",
57
58
  title: "Land blocked by gate failure",
@@ -60,11 +61,11 @@ export async function notifyLandGateFailed(input: {
60
61
  icon: "ti-alert-octagon",
61
62
  view: "orchestrators",
62
63
  metadata: { source: "server", maintenanceJobId: "workspace-land-gate", workspaceId: workspace.id, repoRoot: workspace.repoRoot, gateName: gate.name, ...(input.artifactId ? { artifactId: input.artifactId } : {}) },
63
- });
64
+ }));
64
65
 
65
66
  const routing = await routeLandGateFailure({ workspaceId: workspace.id, branch: workspace.branch, failedAt: input.failedAt, owner, coordinator, fallback, gateName: gate.name });
66
67
  if (routing.stale) return;
67
- routeNotification({
68
+ await routeNotification({
68
69
  type: "workspace.land_gate_failed",
69
70
  scope: { author: owner, parent: routing.parent, repoRoot: workspace.repoRoot, workspaceId: workspace.id },
70
71
  recipients: routing.recipients,
@@ -80,14 +81,14 @@ export async function notifyLandGateFailed(input: {
80
81
  }
81
82
 
82
83
  /** #902 — gate failures wake both owner and coordinator durably; full logs stay in artifacts. */
83
- export function notifyLandGateWarnings(input: {
84
+ export async function notifyLandGateWarnings(input: {
84
85
  workspace: Pick<WorkspaceRecord, "id" | "repoRoot" | "branch" | "baseRef" | "ownerAgentId">;
85
86
  warnings: LandGateRunResult[];
86
87
  /** #2474 R3 — false when any warning's wire output is a folded tail: the combined artifact is
87
88
  * then an index (tails + per-gate full-log refs), not full output, and the label must not
88
89
  * claim otherwise. Defaults to true (nothing folded). */
89
90
  artifactId?: string; fullOutput?: boolean;
90
- }): void {
91
+ }): Promise<void> {
91
92
  const { workspace, warnings } = input;
92
93
  if (warnings.length === 0) return;
93
94
  const base = workspace.baseRef ?? "base";
@@ -110,7 +111,7 @@ export function notifyLandGateWarnings(input: {
110
111
  emitRelayEvent({ type: "workspace.land_gate_warning", source: "server", subject: workspace.id, data: payload });
111
112
 
112
113
  if (!owner) return;
113
- routeNotification({
114
+ await routeNotification({
114
115
  type: "workspace.land_gate_warning",
115
116
  scope: { author: owner, repoRoot: workspace.repoRoot, workspaceId: workspace.id },
116
117
  recipients: [owner],
@@ -124,13 +125,13 @@ export function notifyLandGateWarnings(input: {
124
125
  });
125
126
  }
126
127
 
127
- export function notifyLandGateSkipped(input: {
128
+ export async function notifyLandGateSkipped(input: {
128
129
  workspace: Pick<WorkspaceRecord, "id" | "repoRoot" | "branch" | "baseRef" | "ownerAgentId">;
129
130
  level: "subset" | "none";
130
131
  reason: string;
131
132
  by: string;
132
133
  commit?: string;
133
- }): void {
134
+ }): Promise<void> {
134
135
  const { workspace } = input;
135
136
  const base = workspace.baseRef ?? "base";
136
137
  const owner = workspace.ownerAgentId;
@@ -157,7 +158,7 @@ export function notifyLandGateSkipped(input: {
157
158
 
158
159
  const recipients = [...new Set([owner, coordinator ?? fallback].filter((r): r is string => Boolean(r)))];
159
160
  if (!recipients.length) return;
160
- routeNotification({
161
+ await routeNotification({
161
162
  type: "workspace.land_gate_skipped",
162
163
  scope: { author: owner, parent: coordinator ?? undefined, repoRoot: workspace.repoRoot, workspaceId: workspace.id },
163
164
  recipients,
@@ -6,14 +6,14 @@ import { stewardFallbackTarget } from "./config";
6
6
  import type { Command, WorkspaceRecord } from "./types";
7
7
 
8
8
  /** An offline host is uncertain, never positive evidence to release its lane (#2777). */
9
- export function alertUnprovenLandLaneHolder(command: Command): void {
9
+ export async function alertUnprovenLandLaneHolder(command: Command): Promise<void> {
10
10
  const workspaceId = command.correlationId ?? command.params.workspaceId;
11
11
  const workspace = typeof workspaceId === "string" ? getWorkspace(workspaceId) : null;
12
12
  if (!workspace) return;
13
13
  const coordinator = resolveCoordinatorAgentId(workspace.ownerAgentId);
14
14
  const target = coordinator ?? stewardFallbackTarget() ?? workspace.ownerAgentId;
15
15
  if (!target) return;
16
- routeNotification({
16
+ await routeNotification({
17
17
  type: "workspace.land_gate_queue_stalled",
18
18
  scope: { author: workspace.ownerAgentId, parent: coordinator ?? undefined, repoRoot: workspace.repoRoot, workspaceId: workspace.id },
19
19
  recipients: [target], forceDelivery: true, ttlMs: null,
@@ -39,7 +39,7 @@ export async function recordLandGateQueueProgress(workspace: WorkspaceRecord, co
39
39
  const coordinator = resolveCoordinatorAgentId(workspace.ownerAgentId);
40
40
  const target = coordinator ?? stewardFallbackTarget() ?? workspace.ownerAgentId;
41
41
  if (!target) return;
42
- routeNotification({
42
+ await routeNotification({
43
43
  type: "workspace.land_gate_queue_stalled",
44
44
  scope: { author: workspace.ownerAgentId, parent: coordinator ?? undefined, repoRoot: workspace.repoRoot, workspaceId: workspace.id },
45
45
  recipients: [target], forceDelivery: true, ttlMs: null,
@@ -1,56 +1,7 @@
1
1
  import { errMessage } from "@humwick/sdk";
2
+ import { trackRelayEvent } from "./relay-event-drain";
2
3
 
3
- /**
4
- * #1790 — the land settle path (`settleWorkspaceMergeCommand`) fires 6–12 serialized SYNCHRONOUS
5
- * DB writes per land: the `branch.landed` + `landed-success` events, the per-recipient notification
6
- * fan-out (each a `sendMessage` + an activity-event audit, over an all-agents audience scan), the
7
- * issue/Task lifecycle drives, and — on the reviewer path — an eager policy spawn dispatch. NONE use
8
- * the async busy-retry wrapper, so each raw write can block the event-loop thread up to `busy_timeout`
9
- * (1s, `db/connection.ts`) under lock contention. A land BURST piles this onto the contended loop and
10
- * amplifies the ~2.4s event-loop lag that made the relay HTTP server miss a concurrent `spawn` (the
11
- * "socket connection closed" failures in #1790).
12
- *
13
- * None of that work belongs on the synchronous settle critical path. The AUTHORITATIVE land STATE —
14
- * the workspace status transition (ancestry-gated, #1713) and the landed-sha stamp — plus the durable
15
- * landing RECEIPT (`recordLandingReceipt`, one indexed INSERT) are written synchronously and ARE the
16
- * source of truth; the events, notifications, lifecycle drives and reviewer spawn are ANNOUNCEMENTS
17
- * derived from that state.
18
- *
19
- * ## Serialized, one-item-per-tick drain (#1790 review F2)
20
- * Deferring N announcements as N `setImmediate` callbacks does NOT relieve a burst: Node/Bun drain a
21
- * whole `setImmediate` batch as one contiguous group within a single check phase, BEFORE the poll
22
- * phase accepts the next connection — so an N×fan-out block still refuses a `spawn` arriving during
23
- * it. Instead this runs a SERIALIZED queue: each drain executes exactly ONE item and, only if the
24
- * queue is still non-empty, re-schedules itself. That guarantees a full event-loop iteration — poll
25
- * phase included, so incoming connections are accepted — between every two fan-outs.
26
- *
27
- * ## Shutdown drain (#1790 review F1)
28
- * A graceful stop is a SIGTERM/SIGINT (CD deploys are graceful SIGTERMs, often triggered BY a land —
29
- * a bad correlation). {@link drainPendingLandSettleSideEffects} runs every still-queued callback
30
- * synchronously; the process shutdown handler (a SINGLE `once` per signal in `db/agents.ts`) calls it
31
- * before re-raising, so an announcement already QUEUED by the time the signal is handled is never
32
- * dropped. Accepted residual: a HARD crash (SIGKILL / power loss) still loses any un-flushed deferred
33
- * announcement, but the durable landing RECEIPT is written synchronously right after the settle's own
34
- * `await` resolves (`recordLandingReceipt`, before `markWorkspaceLanded`), so a land whose settle has
35
- * fully RUN is never left unrecorded on a clean stop — only its wake/lifecycle drive would need a
36
- * re-poll to recover.
37
- *
38
- * #1830 (async land-settle git probes) narrowed this guarantee: `settleWorkspaceMergeCommand` now
39
- * awaits a bounded (up to ~12s worst case) async git ancestry check BEFORE it writes the land state,
40
- * the receipt, or queues any announcement here. A graceful stop that lands mid-await — the event loop
41
- * is genuinely free during that await, unlike the old fully-synchronous settle a signal handler could
42
- * never interrupt mid-function — sees NOTHING queued for this land yet, so `drainPendingLandSettleSideEffects`
43
- * flushes nothing for it and the settle can be lost in its entirety (state + receipt + announcement),
44
- * not just its announcement. The land is not silently mis-recorded (no state was written either), but
45
- * it is a real durability gap this module cannot close on its own — a full fix needs the settle path
46
- * itself to survive/resume across a restart (a fast re-settle on startup, or a bounded shutdown drain
47
- * that waits out in-flight settles). Tracked as #1831; out of scope here.
48
- *
49
- * Errors are isolated: a deferred announcement runs AFTER the land it announces has already settled,
50
- * so a throw here can never unwind or mask the land. This is a deliberate swallow-and-log (the inline
51
- * callers propagated most of these throws to the settle caller; moving them off-thread means there is
52
- * no caller left to catch them, so isolation is a behavior improvement, not parity).
53
- */
4
+ // #1790/#1870 — serialize post-commit announcements, yielding between items and awaiting them at shutdown.
54
5
  type LandSettleScheduler = (run: () => void) => void;
55
6
 
56
7
  const defaultScheduler: LandSettleScheduler = (run) => {
@@ -61,7 +12,8 @@ let scheduler: LandSettleScheduler = defaultScheduler;
61
12
 
62
13
  interface PendingSideEffect {
63
14
  label: string;
64
- fn: () => void;
15
+ fn: () => unknown;
16
+ complete: () => void;
65
17
  }
66
18
 
67
19
  // One global FIFO queue drained one item per scheduler tick (F2). `scheduled` guards against
@@ -69,25 +21,36 @@ interface PendingSideEffect {
69
21
  const queue: PendingSideEffect[] = [];
70
22
  let scheduled = false;
71
23
 
72
- function runOne(item: PendingSideEffect): void {
73
- try {
74
- item.fn();
75
- } catch (error) {
76
- console.warn(`[land-settle] deferred ${item.label} threw (land already settled; announcement only): ${errMessage(error)}`);
77
- }
24
+ let active: Promise<void> | undefined;
25
+ let shutdownDrain: Promise<void> | undefined;
26
+ let executing = false;
27
+ let draining = false;
28
+
29
+ async function runOne(item: PendingSideEffect): Promise<void> {
30
+ try { await item.fn(); }
31
+ catch (error) { console.warn(`[land-settle] deferred ${item.label} threw (land already settled; announcement only): ${errMessage(error)}`); }
32
+ finally { item.complete(); }
33
+ }
34
+
35
+ function scheduleNext(): void {
36
+ if (!queue.length || scheduled || executing || draining) return;
37
+ scheduled = true;
38
+ scheduler(drainOne);
78
39
  }
79
40
 
80
41
  function drainOne(): void {
81
42
  scheduled = false;
43
+ if (executing || draining) return;
82
44
  const item = queue.shift();
83
- if (item) runOne(item);
84
- // Re-schedule for the NEXT item only — one item per loop iteration, so the poll phase runs (and a
85
- // pending spawn connection is accepted) between fan-outs. A continuously-fed queue still terminates
86
- // each drain in O(1); it just keeps re-arming while work remains.
87
- if (queue.length > 0 && !scheduled) {
88
- scheduled = true;
89
- scheduler(drainOne);
90
- }
45
+ if (!item) return;
46
+ executing = true;
47
+ const running = runOne(item);
48
+ active = running;
49
+ void running.finally(() => {
50
+ if (active === running) active = undefined;
51
+ executing = false;
52
+ scheduleNext();
53
+ }).catch(error => console.warn(`[land-settle] drain failed: ${errMessage(error)}`));
91
54
  }
92
55
 
93
56
  /**
@@ -96,39 +59,36 @@ function drainOne(): void {
96
59
  * path and DOES run once flushed. Pass `undefined` to restore the `setImmediate` default. Also drains
97
60
  * any queued work so state never leaks across tests.
98
61
  */
99
- export function __setLandSettleSchedulerForTests(fn: LandSettleScheduler | undefined): void {
100
- drainPendingLandSettleSideEffects();
62
+ export async function __setLandSettleSchedulerForTests(fn: LandSettleScheduler | undefined): Promise<void> {
63
+ await drainPendingLandSettleSideEffects();
101
64
  scheduler = fn ?? defaultScheduler;
102
65
  }
103
66
 
104
67
  /** Schedule a land-settle announcement/side-effect off the synchronous settle critical path (#1790). */
105
- export function deferLandSettleSideEffect(label: string, fn: () => void): void {
106
- queue.push({ label, fn });
107
- if (!scheduled) {
108
- scheduled = true;
109
- scheduler(drainOne);
110
- }
68
+ export function deferLandSettleSideEffect(label: string, fn: () => unknown): void {
69
+ let complete!: () => void;
70
+ const pending = new Promise<void>(resolve => { complete = resolve; });
71
+ trackRelayEvent(pending, `land-settle.${label}`);
72
+ queue.push({ label, fn, complete });
73
+ scheduleNext();
111
74
  }
112
75
 
113
- /**
114
- * #1790 review F1 — run every still-queued side-effect synchronously, for the graceful-shutdown
115
- * (SIGTERM/SIGINT) drain so a deferred landing announcement is never dropped on a clean stop.
116
- * Re-entrant-safe: an announcement that itself defers more work is picked up by the same loop (the
117
- * loop re-reads `queue.length`), and each item's throw is swallowed so one bad announcement can never
118
- * block shutdown. A pathologically self-feeding queue is bounded by MAX_SHUTDOWN_DRAIN so shutdown
119
- * always terminates.
120
- */
121
76
  const MAX_SHUTDOWN_DRAIN = 100_000;
122
- export function drainPendingLandSettleSideEffects(): void {
77
+ export function drainPendingLandSettleSideEffects(): Promise<void> {
78
+ if (shutdownDrain) return shutdownDrain;
123
79
  scheduled = false;
124
- let drained = 0;
125
- while (queue.length > 0) {
126
- const item = queue.shift()!;
127
- runOne(item);
128
- if (++drained >= MAX_SHUTDOWN_DRAIN) {
129
- console.warn(`[land-settle] shutdown drain hit ${MAX_SHUTDOWN_DRAIN} items; abandoning ${queue.length} still-queued side-effect(s)`);
130
- queue.length = 0;
131
- break;
80
+ draining = true;
81
+ shutdownDrain = (async () => {
82
+ if (active) await active;
83
+ let drained = 0;
84
+ while (queue.length) {
85
+ await runOne(queue.shift()!);
86
+ if (++drained >= MAX_SHUTDOWN_DRAIN) {
87
+ console.warn(`[land-settle] shutdown drain hit ${MAX_SHUTDOWN_DRAIN} items; abandoning ${queue.length} still-queued side-effect(s)`);
88
+ for (const item of queue.splice(0)) item.complete();
89
+ break;
90
+ }
132
91
  }
133
- }
92
+ })().finally(() => { shutdownDrain = undefined; draining = false; scheduleNext(); });
93
+ return shutdownDrain;
134
94
  }
@@ -1,5 +1,6 @@
1
+ import { createCommandAsync } from "./commands-write";
1
2
  import { errMessage } from "@humwick/sdk";
2
- import { createCommand, listCommands } from "./commands-db";
3
+ import { listCommands } from "./commands-db";
3
4
  import { getAgent, listAgents } from "./db";
4
5
  import { getManagedAgentState } from "./config-store";
5
6
  import { emitCommandEvent } from "./command-events";
@@ -22,7 +23,7 @@ interface CompactAgentInput {
22
23
  hasActiveCommand: (target: string, type: string) => boolean;
23
24
  }
24
25
 
25
- export function reconcileLifecycleContextPressure(input: LifecycleContextPressureInput): Set<string> {
26
+ export async function reconcileLifecycleContextPressure(input: LifecycleContextPressureInput): Promise<Set<string>> {
26
27
  const byPolicy = new Map(input.policies.map((policy) => [policy.name, policy]));
27
28
  const candidates = new Map<string, { agent: AgentCard; policy?: SpawnPolicy }>();
28
29
 
@@ -42,26 +43,26 @@ export function reconcileLifecycleContextPressure(input: LifecycleContextPressur
42
43
 
43
44
  const handled = new Set<string>();
44
45
  for (const { agent, policy } of candidates.values()) {
45
- if (handleContextPressure({
46
+ if ((await handleContextPressure({
46
47
  agent,
47
48
  now: input.now,
48
49
  hasActiveCommand: input.hasActiveCommand,
49
- compact: (reason) => requestAgentCompact({
50
+ compact: async (reason) => (await requestAgentCompact({
50
51
  agent,
51
52
  reason,
52
53
  policyName: policy?.name,
53
54
  alwaysReload: alwaysReloadTags(agent.tags),
54
55
  now: input.now,
55
56
  hasActiveCommand: input.hasActiveCommand,
56
- }),
57
- })) {
57
+ })),
58
+ }))) {
58
59
  handled.add(agent.id);
59
60
  }
60
61
  }
61
62
  return handled;
62
63
  }
63
64
 
64
- export function requestAgentCompact(input: CompactAgentInput): Command | null {
65
+ export async function requestAgentCompact(input: CompactAgentInput): Promise<Command | null> {
65
66
  // #2004 — dedup BEFORE building the resume plan (which persists a recovery message): a compaction
66
67
  // may already be in flight as EITHER an agent.compact (native providers) or an agent.clearContext
67
68
  // (clear-inject providers), so guard against both shapes rather than only the legacy agent.compact.
@@ -81,7 +82,7 @@ export function requestAgentCompact(input: CompactAgentInput): Command | null {
81
82
  // whose identity/startup hooks still reinject the charter — never a clear, never nothing.
82
83
  let resume: ForcedResumePlan | null = null;
83
84
  try {
84
- resume = buildForcedResumePlan(input.agent.id, now);
85
+ resume = (await buildForcedResumePlan(input.agent.id, now));
85
86
  } catch (error) {
86
87
  console.warn(`[lifecycle] forced resume plan for '${input.agent.id}' failed; degrading to a bare compact: ${errMessage(error)}`);
87
88
  resume = null;
@@ -90,7 +91,7 @@ export function requestAgentCompact(input: CompactAgentInput): Command | null {
90
91
  const spawnRequestId = typeof input.agent.meta?.spawnRequestId === "string" ? input.agent.meta.spawnRequestId : undefined;
91
92
  const sessionName = typeof input.agent.meta?.sessionName === "string" ? input.agent.meta.sessionName : undefined;
92
93
  const tmuxSession = typeof input.agent.meta?.tmuxSession === "string" ? input.agent.meta.tmuxSession : undefined;
93
- const command = createCommand({
94
+ const command = (await createCommandAsync({
94
95
  type: commandType,
95
96
  source: "system",
96
97
  target: input.agent.id,
@@ -108,7 +109,7 @@ export function requestAgentCompact(input: CompactAgentInput): Command | null {
108
109
  requestedAt: now,
109
110
  ...(resume?.params ?? {}),
110
111
  },
111
- });
112
+ }));
112
113
  emitCommandEvent(command, "command.requested");
113
114
  return command;
114
115
  }