@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
@@ -1,3 +1,5 @@
1
+ import { pauseMaintenanceJobAsync } from "../db/runtime-tail-async";
2
+ import { createActivityEventAsync } from "../db/activity-async";
1
3
  // #1724 stage 1 — the maintenance circuit breaker's bookkeeping and trip actions.
2
4
  //
3
5
  // The design (§5.4, §8.5) asks for a breaker that, when a job repeatedly starves the event loop (or
@@ -15,7 +17,7 @@
15
17
 
16
18
  import { errMessage } from "@humwick/sdk";
17
19
  import { MAINTENANCE_BREAKER_MAX_STRIKES } from "../config";
18
- import { AUTO_BREAKER_PAUSE_HOLDER, createActivityEvent, pauseMaintenanceJob } from "../db";
20
+ import { AUTO_BREAKER_PAUSE_HOLDER } from "../db";
19
21
 
20
22
  // Two strike counters per job, because the two failure signals want different reset semantics — and
21
23
  // getting this wrong silently defeats the breaker:
@@ -81,7 +83,7 @@ export interface BreakerStrikeResult {
81
83
  * and, on reaching MAINTENANCE_BREAKER_MAX_STRIKES, trips: a non-critical job is durably auto-paused;
82
84
  * a critical one is escalated LOUDLY but never paused. Idempotent per runId.
83
85
  */
84
- export function recordBreakerStrike(input: BreakerStrikeInput): BreakerStrikeResult {
86
+ export async function recordBreakerStrike(input: BreakerStrikeInput): Promise<BreakerStrikeResult> {
85
87
  if (!markStruck(input.runId)) {
86
88
  return { struck: false, strikes: breakerStrikeCount(input.jobId), tripped: false };
87
89
  }
@@ -92,7 +94,7 @@ export function recordBreakerStrike(input: BreakerStrikeInput): BreakerStrikeRes
92
94
  if (count < MAINTENANCE_BREAKER_MAX_STRIKES) {
93
95
  return { struck: true, strikes: breakerStrikeCount(input.jobId), tripped: false };
94
96
  }
95
- const action = trip(input.jobId, input.critical, input.reason);
97
+ const action = (await trip(input.jobId, input.critical, input.reason));
96
98
  return { struck: true, strikes: 0, tripped: true, action };
97
99
  }
98
100
 
@@ -119,34 +121,34 @@ export function isRunStruck(runId: string): boolean {
119
121
  return struckRunIds.has(runId);
120
122
  }
121
123
 
122
- function trip(jobId: string, critical: boolean, reason: string): "paused" | "escalated" | "pause-conflict" {
124
+ async function trip(jobId: string, critical: boolean, reason: string): Promise<"paused" | "escalated" | "pause-conflict"> {
123
125
  // Reset BOTH counters first: after a trip the job is either paused (won't run) or escalated; either
124
126
  // way the next strike should start a fresh count rather than instantly re-trip on every later run.
125
127
  timeoutStrikes.delete(jobId);
126
128
  lagStrikes.delete(jobId);
127
129
  if (critical) {
128
- escalateCritical(jobId, reason);
130
+ (await escalateCritical(jobId, reason));
129
131
  return "escalated";
130
132
  }
131
- return autoPause(jobId, reason);
133
+ return (await autoPause(jobId, reason));
132
134
  }
133
135
 
134
- function autoPause(jobId: string, reason: string): "paused" | "pause-conflict" {
136
+ async function autoPause(jobId: string, reason: string): Promise<"paused" | "pause-conflict"> {
135
137
  const pauseReason = `auto-paused by circuit breaker: ${reason}`;
136
138
  console.warn(`[relay] maintenance circuit breaker TRIPPED — durably auto-pausing ${jobId}: ${reason}`);
137
139
  try {
138
140
  // Indefinite on purpose: a job that was starving the loop must NOT quietly resume itself on a
139
141
  // timer. A human resumes it once it is fixed — and the `maintenance-auto-paused` health arm is
140
142
  // what makes sure a human sees it (this pause is otherwise invisible to the critical-only arm).
141
- const result = pauseMaintenanceJob({ jobId, holder: AUTO_BREAKER_PAUSE_HOLDER, reason: pauseReason, indefinite: true });
142
- auditBreakerEvent({
143
+ const result = (await pauseMaintenanceJobAsync({ jobId, holder: AUTO_BREAKER_PAUSE_HOLDER, reason: pauseReason, indefinite: true }));
144
+ (await auditBreakerEvent({
143
145
  jobId,
144
146
  clientId: `server-maintenance-breaker-pause-${jobId}-${result.pause.createdAt}`,
145
147
  title: `Maintenance circuit breaker auto-paused ${jobId}`,
146
148
  body: `${jobId} was auto-paused by the event-loop-lag circuit breaker after ${MAINTENANCE_BREAKER_MAX_STRIKES} strikes — ${reason}. It will stay paused until a human resumes it.`,
147
149
  icon: "ti-alert-octagon",
148
150
  metadata: { breaker: true, pauseId: result.pause.id, alreadyHeld: !result.ok },
149
- });
151
+ }));
150
152
  return result.ok ? "paused" : "pause-conflict";
151
153
  } catch (error) {
152
154
  console.warn(`[relay] maintenance circuit breaker could not pause ${jobId}: ${errMessage(error)}`);
@@ -154,27 +156,27 @@ function autoPause(jobId: string, reason: string): "paused" | "pause-conflict" {
154
156
  }
155
157
  }
156
158
 
157
- function escalateCritical(jobId: string, reason: string): void {
159
+ async function escalateCritical(jobId: string, reason: string): Promise<void> {
158
160
  // §8.5 — the one thing the breaker must never do is silence a critical job the way a bare
159
161
  // `enabled=0` silenced cd-watcher for nine hours. So a critical job is escalated, never paused: it
160
162
  // KEEPS running (and keeps being reported by criticalMaintenanceJobsCheck if it is genuinely
161
163
  // failing), and a loud, attributed event tells a human to intervene deliberately.
162
164
  console.warn(`[relay] maintenance circuit breaker: CRITICAL job ${jobId} repeatedly misbehaving (${reason}) — NOT auto-paused (see #1645/§8.5), escalating for a human`);
163
- auditBreakerEvent({
165
+ (await auditBreakerEvent({
164
166
  jobId,
165
167
  clientId: `server-maintenance-breaker-escalate-${jobId}-${Date.now()}`,
166
168
  title: `Maintenance circuit breaker: critical job ${jobId} needs a human`,
167
169
  body: `${jobId} repeatedly starved the event loop or overran its deadline (${reason}). Because it is CRITICAL, the breaker did NOT auto-pause it — a machine silencing a critical job is the #1645 blackout. A human must decide what to do.`,
168
170
  icon: "ti-alert-triangle",
169
171
  metadata: { breaker: true, critical: true, escalated: true },
170
- });
172
+ }));
171
173
  }
172
174
 
173
- function auditBreakerEvent(input: { jobId: string; clientId: string; title: string; body: string; icon: string; metadata: Record<string, unknown> }): void {
175
+ async function auditBreakerEvent(input: { jobId: string; clientId: string; title: string; body: string; icon: string; metadata: Record<string, unknown> }): Promise<void> {
174
176
  try {
175
177
  // #1912 S3.2: sync-retained — the lag sampler invokes recordBreakerStrike synchronously and
176
178
  // cannot await without redesigning the event-loop attribution callback contract.
177
- createActivityEvent({
179
+ (await createActivityEventAsync({
178
180
  clientId: input.clientId,
179
181
  kind: "operator",
180
182
  title: input.title,
@@ -183,7 +185,7 @@ function auditBreakerEvent(input: { jobId: string; clientId: string; title: stri
183
185
  icon: input.icon,
184
186
  view: "activity",
185
187
  metadata: { source: "server", maintenanceJobId: input.jobId, ...input.metadata },
186
- });
188
+ }));
187
189
  } catch (error) {
188
190
  // The pause/escalation itself already happened; losing the timeline entry must not throw back
189
191
  // into the scheduler's tick.
@@ -9,6 +9,7 @@ import { isRecord } from "@humwick/sdk";
9
9
  import { createHash } from "node:crypto";
10
10
  import { sendMessageServiceAsync } from "../services/send-message";
11
11
  import { checkoutRefreshEnabled } from "../config";
12
+ import { runGit } from "../services/git-process";
12
13
 
13
14
  async function notice(projectId: string, projectSlug: string, host: string, rootPath: string, reason: string): Promise<void> {
14
15
  const key = createHash("sha256").update(JSON.stringify([projectId, host, rootPath, reason])).digest("hex");
@@ -28,6 +29,7 @@ export function reconcileCheckoutRefresh(signal?: AbortSignal): Promise<Record<s
28
29
  async function reconcile(signal?: AbortSignal): Promise<Record<string, unknown>> {
29
30
  const dispatched: string[] = [];
30
31
  const skipped: string[] = [];
32
+ const remoteTips = new Map<string, Promise<string | undefined>>();
31
33
  for (const project of await listProjectsAsync()) {
32
34
  if (signal?.aborted) break;
33
35
  const roots = await rootsForProjectAsync(project.id);
@@ -55,8 +57,23 @@ async function reconcile(signal?: AbortSignal): Promise<Record<string, unknown>>
55
57
  if (prior && ["pending", "accepted", "running"].includes(prior.status)) continue;
56
58
  // The host reported once that it cannot refresh; re-asking every tick only repeats that (#3629).
57
59
  if (health === "unsupported") { skipped.push(`${project.slug}: unsupported host ${hostId} for ${root.rootPath}`); continue; }
60
+ // Completed receipts remain valid until the remote default branch moves (#3813).
61
+ if (prior?.status === "succeeded" && prior.result?.targetSha && ["current", "refreshed", "disabled"].includes(prior.result.status)) {
62
+ let tip = remoteTips.get(root.remoteUrl!);
63
+ if (!tip) {
64
+ tip = runGit(["ls-remote", "--symref", "--", root.remoteUrl!, "HEAD"], 10_000, 4096).then(result => {
65
+ if (result.exitCode !== 0 || result.unavailable || !/^ref: refs\/heads\/.+\tHEAD$/m.test(result.stdout)) return undefined;
66
+ return result.stdout.match(/^([0-9a-f]{40,64})\tHEAD$/m)?.[1];
67
+ });
68
+ remoteTips.set(root.remoteUrl!, tip);
69
+ }
70
+ const sha = await tip;
71
+ if (!sha) { skipped.push(`${project.slug}: remote default branch unavailable`); continue; }
72
+ if (sha === prior.result.targetSha) continue;
73
+ }
58
74
  // Leave one tick of backoff after terminal failure, including restart/lease recovery (#3691).
59
75
  if (prior && (["failed", "timed_out"].includes(prior.status) || prior.result?.status === "error") && Date.now() < (prior.updatedAt ?? 0) + 60_000) continue;
76
+ if (signal?.aborted) break;
60
77
  const command = await createCommandAsync({ type: "project.checkout.refresh", source: "system", target: `orchestrator-${hostId}`,
61
78
  params: { projectId: project.id, rootPath: root.rootPath, remoteUrl: root.remoteUrl, orchestratorId: hostId, enabled: true, ...(prior?.result ? { previousResult: prior.result } : {}) } });
62
79
  emitCommandEvent(command, "command.requested");
@@ -66,7 +83,7 @@ async function reconcile(signal?: AbortSignal): Promise<Record<string, unknown>>
66
83
  return { enabled: true, dispatched, skipped };
67
84
  }
68
85
 
69
- // A confirmed publish accelerates the same periodic reconciliation; external pushes converge on ticks.
86
+ // Publish hints and external pushes use the same remote-tip comparison.
70
87
  subscribeRelayEvents((event) => {
71
88
  if (!event.type.startsWith("command.") || !isRecord(event.data.command)) return;
72
89
  const command = event.data.command;
@@ -128,18 +128,18 @@ export async function runCriticalJobWatchdog(now: number = Date.now()): Promise<
128
128
 
129
129
  const { subject, body, payload } = composeNotice(degraded, resetRuns, legacyHungRuns, heldRestartEpoch, now);
130
130
  const recipients: string[] = [];
131
- const human = notifyWorkspaceHumanEscalation({
131
+ const human = (await notifyWorkspaceHumanEscalation({
132
132
  subject,
133
133
  body,
134
134
  payload,
135
135
  source: "maintenance-critical-job-watchdog",
136
136
  tags: ["agent-relay", "maintenance"],
137
- });
137
+ }));
138
138
  if (human.accepted && human.channelId) recipients.push(human.channelId);
139
139
 
140
140
  const agentTarget = stewardFallbackTarget();
141
141
  if (agentTarget) {
142
- const routed = notifyTarget("maintenance.degraded", {}, agentTarget, subject, body, payload);
142
+ const routed = await notifyTarget("maintenance.degraded", {}, agentTarget, subject, body, payload);
143
143
  if (routed) recipients.push(routed);
144
144
  }
145
145
 
@@ -9,7 +9,7 @@ export function emitCommand(command: Command): void {
9
9
 
10
10
  // Route a maintenance notification, swallowing failures (a stale/missing/misconfigured
11
11
  // target must never break the sweep). Returns the target on success, null otherwise.
12
- export function notifyTarget(
12
+ export async function notifyTarget(
13
13
  type: NotificationType,
14
14
  scope: NotificationScope,
15
15
  target: string,
@@ -17,10 +17,10 @@ export function notifyTarget(
17
17
  body: string,
18
18
  payload: Record<string, unknown>,
19
19
  idempotencyKey?: string,
20
- ): string | null {
20
+ ): Promise<string | null> {
21
21
  if (!target) return null;
22
22
  try {
23
- routeNotification({ type, scope, recipients: [target], message: { subject, body, payload, idempotencyKey } });
23
+ await routeNotification({ type, scope, recipients: [target], message: { subject, body, payload, idempotencyKey } });
24
24
  return target;
25
25
  } catch {
26
26
  return null;
@@ -0,0 +1,30 @@
1
+ import { getGateway } from "../db/gateway";
2
+ import type { MaintenanceJobDefinition } from "./types";
3
+
4
+ export const HISTORY_RETENTION_JOB: MaintenanceJobDefinition = {
5
+ id: "history-retention", critical: false,
6
+ title: "Command, kernel replay and token history retention",
7
+ description: "Expire superseded checkout refresh commands after one hour, other non-spawn terminal commands and kernel replay receipts after 30 days, and raw usage after 90 days. Preserve references, expired replay keys and permanent budget spend (#3619, #3797).",
8
+ intervalMs: 60 * 60_000, backlogIntervalMs: 1_000, deadlineMs: 30_000, runOnStart: true,
9
+ async handler(context) {
10
+ const gateway = getGateway();
11
+ const result = { prunedCommands: 0, prunedKernelReceipts: 0, prunedTokenReports: 0, moreWork: false };
12
+ for (const [op, key] of [
13
+ ["historyRetention.commands", "prunedCommands"],
14
+ ["historyRetention.kernel", "prunedKernelReceipts"],
15
+ ["historyRetention.tokens", "prunedTokenReports"],
16
+ ] as const) {
17
+ if (context.signal.aborted) return { ...result, aborted: true, moreWork: true };
18
+ const start = performance.now();
19
+ let batch: { pruned: number; moreWork: boolean };
20
+ do {
21
+ if (context.signal.aborted) return { ...result, aborted: true, moreWork: true };
22
+ batch = await gateway.write(op, {}) as typeof batch;
23
+ result[key] += batch.pruned;
24
+ // Each await releases the writer for foreground work; bound the whole catch-up run (#3807).
25
+ } while (op === "historyRetention.commands" && batch.moreWork && performance.now() - start < 250);
26
+ result.moreWork ||= batch.moreWork;
27
+ }
28
+ return result;
29
+ },
30
+ };
@@ -1,5 +1,9 @@
1
+ import { OFFLINE_AGENT_PRUNE_JOB } from "./offline-agent-prune-job";
2
+ import { pruneOrphanedSharedWorkspacesAsync, releaseExpiredDriveLeasesAsync } from "../db/runtime-tail-async";
3
+ import { HISTORY_RETENTION_JOB } from "./history-retention-job";
1
4
  import { isHumanAgentId } from "@humwick/sdk";
2
- import { IDLE_EPICS_JOB, PENDING_ISSUE_CLOSE_JOB } from "./long-running-work";
5
+ import { REAP_INTERVAL_MS, STALE_TTL_MS } from "../config";
6
+ import { IDLE_EPICS_JOB, PENDING_ISSUE_CLOSE_JOB, PLAN_READY_NOTICE_JOB } from "./long-running-work";
3
7
  import { CHECKOUT_REFRESH_JOB } from "./checkout-refresh";
4
8
  import { CLAUDE_API_BUDGET_RECONCILE_JOB } from "./claude-api-budget-job";
5
9
  import { SERVICE_ACCESS_JOBS } from "./service-access-job";
@@ -15,27 +19,9 @@ import { deriveAgentBusyHealth } from "../agent-busy-health";
15
19
  import { expireStaleBusAgents } from "../bus";
16
20
  import { eventLoopLagGraceMs } from "../event-loop-lag";
17
21
  import { pruneOutboxAsync } from "../bus-outbox-async";
18
- import { expireCommands } from "../commands-db";
22
+ import { expireCommandsAsync } from "../db/runtime-admin-async";
19
23
  import { getRetentionConfig } from "../retention-config-store";
20
- import { OFFLINE_PRUNE_MS, REAP_INTERVAL_MS, STALE_TTL_MS } from "../config";
21
- import {
22
- createActivityEventAsync,
23
- evaluatePoolBindingsAsync,
24
- getAgent,
25
- listAgents,
26
- listStaleQueuedDirectTargetsAsync,
27
- pruneOrphanedSharedWorkspaces,
28
- pruneOfflineAgentsAsync,
29
- pruneRetentionBatchAsync,
30
- pruneProviderQuotaHistoryAsync,
31
- reapStaleOrchestratorsAsync,
32
- releaseExpiredClaimsAsync,
33
- releaseExpiredDriveLeases,
34
- releaseOrphanedTasksAsync,
35
- reapStaleAgentsAsync,
36
- revokeOrphanedRuntimeTokensAsync,
37
- runDbMaintenanceAsync,
38
- } from "../db";
24
+ import { createActivityEventAsync, evaluatePoolBindingsAsync, getAgent, listAgents, listStaleQueuedDirectTargetsAsync, pruneRetentionBatchAsync, pruneProviderQuotaHistoryAsync, reapStaleOrchestratorsAsync, releaseExpiredClaimsAsync, releaseOrphanedTasksAsync, reapStaleAgentsAsync, revokeOrphanedRuntimeTokensAsync, runDbMaintenanceAsync } from "../db";
39
25
  import { isAgentUnavailable } from "../db/agent-predicates";
40
26
  import { resolveTeamCoordinatorAgentId } from "../coordinator-authority";
41
27
  import { flushQueuedDirectMessages } from "../services/managed-running";
@@ -49,7 +35,6 @@ import { getLifecycleManager } from "../lifecycle-manager";
49
35
  import { applyCommandToRecipe } from "../recipe-runner";
50
36
  import { settleWorkspaceCommand } from "../workspace-command-settle";
51
37
  import {
52
- emitAgentRemoved,
53
38
  emitAgentStatus,
54
39
  emitMessageClaimReleased,
55
40
  emitOrchestratorStatus,
@@ -64,7 +49,6 @@ import { runIdleAgentDigest } from "../services/idle-agent-digest";
64
49
  import { runShutdownWatchdog } from "../services/shutdown-watchdog";
65
50
  import { AGENT_HANG_WATCHDOG_MAINTENANCE_JOB } from "../services/agent-hang-watchdog-job";
66
51
  import { reapIdleStewards } from "../services/steward-reaper";
67
- import { emitWorkFinalityResult } from "../services/work-finality";
68
52
  import { escalateExpiredSpawnCommands } from "../services/spawn-loss-escalation";
69
53
  import { reapUnregisteredSpawnCommands } from "../services/spawn-registration-watchdog";
70
54
  import { resumeRateLimitedAgents } from "../services/rate-limit-resume";
@@ -182,7 +166,7 @@ export async function runStuckBusyWatchdog(input: { now?: number; signal?: Abort
182
166
  },
183
167
  });
184
168
 
185
- const messages = routeNotification({
169
+ const messages = await routeNotification({
186
170
  type: "agent.stuck_busy",
187
171
  scope: { agentId: agent.id, parent: agent.spawnedBy, teamId: agent.teamId },
188
172
  message: {
@@ -300,9 +284,9 @@ export const definitions: MaintenanceJobDefinition[] = [
300
284
  runOnStart: true,
301
285
  // #1819 — SYNCHRONOUS, one atomic statement, no fan-out: nothing to split, so the pre-flight
302
286
  // check is the whole of its cooperation.
303
- handler(context) {
287
+ async handler(context) {
304
288
  if (context.signal?.aborted) return { releasedDriveLeaseAgentIds: [], aborted: true };
305
- return { releasedDriveLeaseAgentIds: releaseExpiredDriveLeases() };
289
+ return { releasedDriveLeaseAgentIds: (await releaseExpiredDriveLeasesAsync()) };
306
290
  },
307
291
  },
308
292
  {
@@ -339,7 +323,7 @@ export const definitions: MaintenanceJobDefinition[] = [
339
323
  // message belongs in the queue (this is hooked to agent availability,
340
324
  // not pool-slot reclaim).
341
325
  if (!agent || isAgentUnavailable(agent)) continue;
342
- const flushed = flushQueuedDirectMessages(agent!.id);
326
+ const flushed = await flushQueuedDirectMessages(agent!.id);
343
327
  if (flushed.length > 0) reswept[target] = flushed.length;
344
328
  }
345
329
  return { resweptTargets: reswept };
@@ -359,19 +343,14 @@ export const definitions: MaintenanceJobDefinition[] = [
359
343
  // by heartbeat and deleted on removal, so a missing/offline agent record for the
360
344
  // target means it will never poll this command again. A merely-busy (online) host is
361
345
  // left alone, preserving the #930/#953 "don't sweep before the host can claim it".
362
- const commands = expireCommands(Date.now(), {
363
- isTargetGone: (command) => {
364
- if (!command.target || command.target === "system") return false;
365
- const agent = getAgent(command.target);
366
- return !agent || agent.status === "offline";
367
- },
368
- });
346
+ const commands = await expireCommandsAsync();
347
+
369
348
  // #1819 — deliberately NOT checkpointed: `expireCommands` already moved these rows terminal
370
349
  // and nothing re-runs this settle, so bailing would strand their workspace and recipe forever.
371
350
  // Bounded by one expiry sweep.
372
351
  for (const command of commands) {
373
- notifySelfResumeNoOp(command);
374
- settleContextResetCarry(command);
352
+ await notifySelfResumeNoOp(command);
353
+ (await settleContextResetCarry(command));
375
354
  noteCompactionResult(command);
376
355
  await settleWorkspaceCommand(command, { onMergeSettled: scheduleRepoMerge });
377
356
  await applyCommandToRecipe(command);
@@ -412,7 +391,7 @@ export const definitions: MaintenanceJobDefinition[] = [
412
391
  // #1819 — deliberately NOT checkpointed, as in command-expiration: the attachment is already
413
392
  // expired, and this is the one place a parent is told its agent died.
414
393
  for (const id of expired.agentIds) {
415
- getLifecycleManager().onAgentDisappeared(id);
394
+ (await getLifecycleManager().onAgentDisappeared(id));
416
395
  // #636/#659 — only after the stale reconnect grace expires do we turn a bare
417
396
  // heartbeat gap into a terminal parent notification. A live runner that
418
397
  // reconnects before this point heals from `stale` without a false exit.
@@ -420,7 +399,7 @@ export const definitions: MaintenanceJobDefinition[] = [
420
399
  // when a succeeded agent.shutdown command explains the silence, instead of the
421
400
  // heartbeat-lost/transport-loss verdict a graceful reap used to receive.
422
401
  const { reason, diagnosis } = classifyBusExpirationDeath(id);
423
- notifyAgentOffline(id, reason, diagnosis);
402
+ (await notifyAgentOffline(id, reason, diagnosis));
424
403
  }
425
404
  for (const task of expired.orphanedTasks) emitTaskChanged(task, "task.updated");
426
405
  return {
@@ -464,26 +443,7 @@ export const definitions: MaintenanceJobDefinition[] = [
464
443
  return { reapedAgentIds };
465
444
  },
466
445
  },
467
- {
468
- id: "offline-agent-prune",
469
- critical: false,
470
- title: "Offline agent prune",
471
- description: "Delete long-offline provider agents and release their claims.",
472
- intervalMs: REAP_INTERVAL_MS,
473
- runOnStart: false,
474
- async handler(context) {
475
- if (context.signal?.aborted) return { prunedAgentIds: [], aborted: true };
476
- // #1084 crash-final-loss fix-forward — defense-in-depth: `notifyAgentOffline` is the primary
477
- // flush point when an agent first goes offline, so this is normally a no-op by the time an
478
- // agent reaches prune horizon; the async facade snapshots its held queue and performs the DB
479
- // finalization in the SAME worker transaction before deleting the agent row.
480
- const prunedAgentIds = await pruneOfflineAgentsAsync(OFFLINE_PRUNE_MS, {
481
- onFinality: emitWorkFinalityResult,
482
- });
483
- for (const id of prunedAgentIds) emitAgentRemoved(id);
484
- return { prunedAgentIds };
485
- },
486
- },
446
+ OFFLINE_AGENT_PRUNE_JOB,
487
447
  AGENT_IDENTITY_ARCHIVE_PRUNE_JOB,
488
448
  {
489
449
  id: "orphan-token-revoker",
@@ -510,12 +470,12 @@ export const definitions: MaintenanceJobDefinition[] = [
510
470
  description: "Stop spawned sessions whose relay agent is offline/gone but whose process the orchestrator still reports running, after a grace period for self-heal.",
511
471
  intervalMs: ORPHAN_REAPER_INTERVAL_MS,
512
472
  runOnStart: false,
513
- handler: (context) => reapOrphanedSessions({ signal: context.signal }),
473
+ handler: async (context) => (await reapOrphanedSessions({ signal: context.signal })),
514
474
  },
515
- { id: "transient-agent-reaper", critical: false, title: "Transient agent reaper", description: "Cleanly shut down transient spawned agents once they are idle and any isolated branch work has landed; also flushes a persistent agent's held work-final report once it has settled idle (#1084 HIGH fix-forward).", intervalMs: REAP_INTERVAL_MS, runOnStart: false, handler: (context) => reapTransientAgents({ signal: context.signal }) },
475
+ { id: "transient-agent-reaper", critical: false, title: "Transient agent reaper", description: "Cleanly shut down transient spawned agents once they are idle and any isolated branch work has landed; also flushes a persistent agent's held work-final report once it has settled idle (#1084 HIGH fix-forward).", intervalMs: REAP_INTERVAL_MS, runOnStart: false, handler: async (context) => (await reapTransientAgents({ signal: context.signal })) },
516
476
  { id: "idle-agent-digest", critical: false, title: "Idle-agent digest", description: "One digest per coordinator window listing idle children; stops provably safe ones when the team allows it (#3428). Off until the idle-digest setting enables it.", intervalMs: REAP_INTERVAL_MS, runOnStart: false, handler: (context) => runIdleAgentDigest({ signal: context.signal }) },
517
477
  { id: "shutdown-watchdog", critical: false, title: "Shutdown kill-switch watchdog", description: "#1746 — recover a NON-policy agent stuck `shutting-down` past its grace whose shutdown command never executed (owning host offline / command expired): re-issue exactly one forceful shutdown, then surface a terminal `kill-failed` if it is still alive a further grace window later. Policy/steward agents are excluded (the lifecycle-manager owns their retry ladder, #1761).", intervalMs: REAP_INTERVAL_MS, runOnStart: false, handler: (context) => runShutdownWatchdog({ signal: context.signal }) },
518
- { id: "steward-reaper", critical: false, title: "Orphan steward reaper", description: "Independent backstop (#1761) for the lifecycle reconcile: reap ORPHANED stewards ONLY — a live steward agent no managed policy state references (left when a respawn overwrote the state's agentId, or handed off after the lifecycle exhausted its forceful stop budget). Reaps an orphan once idle past the dashboard keepalive grace, or immediately when steward spawning is disabled. Never reaps a busy/provider-blocked/mid-land (claim-holding) steward, reaps each orphan at most once (idempotent), and never touches a still-tracked steward — that one is the lifecycle reconcile's (its keepalive idle-timeout, policy-disabled stop, and forceful stopping-wedge escalation), so the two can never race.", intervalMs: REAP_INTERVAL_MS, runOnStart: false, handler: (context) => reapIdleStewards({ signal: context.signal }) },
478
+ { id: "steward-reaper", critical: false, title: "Orphan steward reaper", description: "Independent backstop (#1761) for the lifecycle reconcile: reap ORPHANED stewards ONLY — a live steward agent no managed policy state references (left when a respawn overwrote the state's agentId, or handed off after the lifecycle exhausted its forceful stop budget). Reaps an orphan once idle past the dashboard keepalive grace, or immediately when steward spawning is disabled. Never reaps a busy/provider-blocked/mid-land (claim-holding) steward, reaps each orphan at most once (idempotent), and never touches a still-tracked steward — that one is the lifecycle reconcile's (its keepalive idle-timeout, policy-disabled stop, and forceful stopping-wedge escalation), so the two can never race.", intervalMs: REAP_INTERVAL_MS, runOnStart: false, handler: async (context) => (await reapIdleStewards({ signal: context.signal })) },
519
479
  {
520
480
  id: "restart-epoch-reconcile",
521
481
  // CRITICAL: if this stops running, an epoch opened by a deploy is never released — every
@@ -556,7 +516,7 @@ export const definitions: MaintenanceJobDefinition[] = [
556
516
  description: "Auto-resume agents held on a provider usage/rate-limit stall once their reset window has passed (#286).",
557
517
  intervalMs: REAP_INTERVAL_MS,
558
518
  runOnStart: false,
559
- handler: (context) => resumeRateLimitedAgents({ signal: context.signal }),
519
+ handler: async (context) => (await resumeRateLimitedAgents({ signal: context.signal })),
560
520
  },
561
521
  { id: "provider-still-held-nudge", critical: false, title: "Provider still-held nudge", description: "Notify a child's parent while it remains provider-blocked, and page the operator once a top-level provider block passes its threshold.", intervalMs: REAP_INTERVAL_MS, runOnStart: false, handler: async (context) => await sendStillHeldNudges(Date.now(), context.signal) },
562
522
  {
@@ -648,6 +608,7 @@ export const definitions: MaintenanceJobDefinition[] = [
648
608
  prunedMessages: result.prunedMessages,
649
609
  prunedActivityEvents: result.prunedActivityEvents,
650
610
  prunedSpawnCommands: result.prunedSpawnCommands,
611
+ slimmedSpawnProfiles: result.slimmedSpawnProfiles,
651
612
  offThread: result.offThread,
652
613
  moreWork: result.moreWork, // #1758 — bounded-progress signal; drives backlogIntervalMs reschedule.
653
614
  };
@@ -662,6 +623,7 @@ export const definitions: MaintenanceJobDefinition[] = [
662
623
  SNAPSHOT_LEDGER_PRUNE_JOB,
663
624
  PROVIDER_SECRET_SCRUB_JOB,
664
625
  WEB_TOOL_CALL_PRUNE_JOB,
626
+ HISTORY_RETENTION_JOB,
665
627
  CONTINUATION_ARCHIVE_SEARCH_SYNC_JOB,
666
628
  SHIPPING_STATS_ROLLUP_JOB,
667
629
  REPLY_OBLIGATION_PARITY_JOB,
@@ -762,9 +724,9 @@ export const definitions: MaintenanceJobDefinition[] = [
762
724
  intervalMs: REAP_INTERVAL_MS,
763
725
  runOnStart: true,
764
726
  // #1819 — SYNCHRONOUS, one atomic sweep: pre-flight check only, nothing to split.
765
- handler(context) {
727
+ async handler(context) {
766
728
  if (context.signal?.aborted) return { prunedSharedWorkspaceIds: [], aborted: true };
767
- const prunedSharedWorkspaceIds = pruneOrphanedSharedWorkspaces();
729
+ const prunedSharedWorkspaceIds = (await pruneOrphanedSharedWorkspacesAsync());
768
730
  return { prunedSharedWorkspaceIds };
769
731
  },
770
732
  },
@@ -830,7 +792,7 @@ export const definitions: MaintenanceJobDefinition[] = [
830
792
  intervalMs: WORKSPACE_IDLE_REFRESH_INTERVAL_MS,
831
793
  runOnStart: false,
832
794
  deadlineMs: 60 * 1000,
833
- handler: (context) => refreshIdleWorkspaces({ signal: context.signal }),
795
+ handler: async (context) => (await refreshIdleWorkspaces({ signal: context.signal })),
834
796
  },
835
797
  {
836
798
  id: "workspace-gc",
@@ -844,14 +806,14 @@ export const definitions: MaintenanceJobDefinition[] = [
844
806
  // otherwise never run it at all — the backstop effectively off exactly when it's needed.
845
807
  runOnStart: true,
846
808
  deadlineMs: 60 * 1000,
847
- handler: (context) => workspaceGC({ signal: context.signal }),
809
+ handler: async (context) => (await workspaceGC({ signal: context.signal })),
848
810
  },
849
811
  { id: "agent-exit-cleanup", critical: false, title: "Agent exit cleanup",
850
812
  description: "Save exited owners on origin, then remove their workspace, branch, outbox and provider home on each host (#2749).",
851
813
  intervalMs: 15 * 60_000, runOnStart: true, logRuns: true, deadlineMs: 60_000,
852
814
  handler: (context) => requestAgentExitCleanupSweep(context.signal),
853
815
  },
854
- IDLE_EPICS_JOB, PENDING_ISSUE_CLOSE_JOB,
816
+ IDLE_EPICS_JOB, PENDING_ISSUE_CLOSE_JOB, PLAN_READY_NOTICE_JOB,
855
817
  REVIEW_REF_RECONCILE_JOB,
856
818
  CHECKOUT_REFRESH_JOB,
857
819
  ARTIFACT_CHECKOUT_GC_JOB,
@@ -5,6 +5,8 @@ import { routeNotification } from "../notification-router";
5
5
  import { DAY_MS } from "./constants";
6
6
  import type { MaintenanceJobDefinition } from "./types";
7
7
 
8
+ export { PLAN_READY_NOTICE_JOB } from "./plan-ready-notice-job";
9
+
8
10
  export const PENDING_ISSUE_CLOSE_JOB: MaintenanceJobDefinition = {
9
11
  id: "pending-issue-closes", title: "Issues awaiting Task completion", critical: false,
10
12
  description: "Close landed delivery issues when their Tasks reach doneWhen and pass validation.",
@@ -23,7 +25,7 @@ export async function remindIdleEpics(signal: AbortSignal, now = Date.now()): Pr
23
25
  if (signal.aborted) break;
24
26
  const lines = [...renderTeamIdleEpics(team.id), ...teamFailures.map(failure => `- ${failure}`)];
25
27
  if (!lines.length) continue;
26
- routeNotification({
28
+ await routeNotification({
27
29
  type: "team.idle", scope: { teamId: team.id }, recipients: [`coordinator:${team.id}`],
28
30
  message: { kind: "system", from: "system", subject: "Open idle epics and Plans", body: lines.join("\n"), replyExpected: false,
29
31
  payload: { kind: "team.idle-epics", teamId: team.id }, idempotencyKey: `idle-epics:${team.id}:${Math.floor(now / DAY_MS)}` },
@@ -0,0 +1,28 @@
1
+ import { OFFLINE_PRUNE_MS, REAP_INTERVAL_MS } from "../config";
2
+ import { pruneOfflineAgentsAsync } from "../db/agents-async";
3
+ import { emitAgentRemoved } from "../sse";
4
+ import { emitWorkFinalityResult } from "../services/work-finality";
5
+ import type { MaintenanceJobDefinition } from "./types";
6
+
7
+ export const OFFLINE_AGENT_PRUNE_JOB: MaintenanceJobDefinition = {
8
+ id: "offline-agent-prune",
9
+ critical: false,
10
+ title: "Offline agent prune",
11
+ description: "Delete long-offline provider agents and release their claims.",
12
+ intervalMs: REAP_INTERVAL_MS,
13
+ runOnStart: false,
14
+ async handler(context) {
15
+ if (context.signal?.aborted) return { prunedAgentIds: [], aborted: true };
16
+ // #1084 crash-final-loss fix-forward — defense-in-depth: `notifyAgentOffline` is the primary
17
+ // flush point when an agent first goes offline, so this is normally a no-op by the time an
18
+ // agent reaches prune horizon; the async facade snapshots its held queue and performs the DB
19
+ // finalization in the SAME worker transaction before deleting the agent row.
20
+ let archivePurgeFailures = 0;
21
+ const prunedAgentIds = await pruneOfflineAgentsAsync(OFFLINE_PRUNE_MS, {
22
+ onFinality: emitWorkFinalityResult,
23
+ onArchivePurgeFailure: () => { archivePurgeFailures++; },
24
+ });
25
+ for (const id of prunedAgentIds) emitAgentRemoved(id);
26
+ return { prunedAgentIds, archivePurgeFailures };
27
+ },
28
+ };
@@ -1,4 +1,5 @@
1
- import { createCommand, listCommands } from "../commands-db";
1
+ import { createCommandAsync } from "../commands-write";
2
+ import { listCommands } from "../commands-db";
2
3
  import { createActivityEventAsync, getAgent, listOrchestrators } from "../db";
3
4
  import type { Command, ManagedAgent } from "../types";
4
5
  import { orphanGraceMs, orphanReapCooldownMs, orphanReapEnabled } from "./constants";
@@ -44,7 +45,7 @@ export async function reapOrphanedSessions(context: { signal?: AbortSignal } = {
44
45
  let orphaned = 0;
45
46
 
46
47
  for (const orch of listOrchestrators()) {
47
- // #1819 — checkpoint per orchestrator and per managed session: a reap is `createCommand` +
48
+ // #1819 — checkpoint per orchestrator and per managed session: a reap is `createCommandAsync` +
48
49
  // `emitCommandEventIfAbsent` + activity event + `lastReapAt` stamp, indivisible once started.
49
50
  if (signal?.aborted) break;
50
51
  if (orch.status !== "online") continue;
@@ -70,7 +71,7 @@ export async function reapOrphanedSessions(context: { signal?: AbortSignal } = {
70
71
  // logical orphan reap has one command id and one command.requested event.
71
72
  const pending = listCommands({ target: orch.agentId, type: "agent.shutdown", limit: 500 })
72
73
  .find((command) => isPendingReapForSession(command, session));
73
- const command = pending ?? createCommand({
74
+ const command = pending ?? (await createCommandAsync({
74
75
  type: "agent.shutdown",
75
76
  source: "system",
76
77
  target: orch.agentId,
@@ -89,7 +90,7 @@ export async function reapOrphanedSessions(context: { signal?: AbortSignal } = {
89
90
  requestedAt: now,
90
91
  orchestratorId: orch.id,
91
92
  },
92
- });
93
+ }));
93
94
  // The command is durable but not externally observable until its audit row commits. A failed
94
95
  // audit must never let an orchestrator execute an unaudited reap (#1912 S3.2 class B).
95
96
  await createActivityEventAsync({
@@ -1,3 +1,4 @@
1
+ import { resumeMaintenanceJobAsync, pauseMaintenanceJobAsync } from "../db/runtime-tail-async";
1
2
  // #1645 — behavior layer for maintenance-job pause/resume: validation, audit, and the drift report.
2
3
  //
3
4
  // Persistence is src/db/maintenance-pauses.ts; the scheduler gate reads from there. This module is
@@ -6,14 +7,7 @@
6
7
  // #1638 pause's justification existed only in one agent's session context and one memory file.
7
8
 
8
9
  import { errMessage } from "@humwick/sdk";
9
- import {
10
- createActivityEventAsync,
11
- latestSuccessfulDeployAsync,
12
- listMaintenanceJobPauses,
13
- pauseMaintenanceJob,
14
- resumeMaintenanceJob,
15
- type MaintenanceJobPauseRecord,
16
- } from "../db";
10
+ import { createActivityEventAsync, latestSuccessfulDeployAsync, listMaintenanceJobPauses, type MaintenanceJobPauseRecord } from "../db";
17
11
  import { openRestartEpochsAsync } from "../db/restart-epoch-async.ts";
18
12
  import { ValidationError } from "../db/connection.ts";
19
13
  import { definitions } from "./jobs";
@@ -61,7 +55,7 @@ export async function pauseJob(input: PauseJobRequest): Promise<PauseJobResult>
61
55
  throw new ValidationError("pass either ttlMs or indefinite, not both");
62
56
  }
63
57
 
64
- const result = pauseMaintenanceJob({ ...input, reason });
58
+ const result = (await pauseMaintenanceJobAsync({ ...input, reason }));
65
59
  if (!result.ok) return { ok: false, conflict: true, pause: result.pause };
66
60
 
67
61
  const ownedRestartEpoch = await ownedOpenRestartEpoch(definition.id);
@@ -126,7 +120,7 @@ export interface ResumeJobResult {
126
120
  export async function resumeJobAsync(opts: { jobId?: string; id?: string; now?: number }): Promise<ResumeJobResult> {
127
121
  if (opts.jobId) requireKnownJob(opts.jobId);
128
122
  const now = opts.now ?? Date.now();
129
- const released = resumeMaintenanceJob({ jobId: opts.jobId, id: opts.id });
123
+ const released = (await resumeMaintenanceJobAsync({ jobId: opts.jobId, id: opts.id }));
130
124
  if (!released) return { resumed: false };
131
125
  const heldForMs = Math.max(0, now - released.createdAt);
132
126
  const lapsed = released.expiresAt !== undefined && released.expiresAt <= now;