@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/mcp/index.ts CHANGED
@@ -239,7 +239,7 @@ async function callTool(auth: VerifiedMcpAuthContext, params: unknown, signal?:
239
239
  else if (name === "relay_workspace_merge_resume") result = await relayWorkspaceMutation(auth, "resume-merge", args);
240
240
  else if (name === "relay_red_main_recover") result = await relayRedMainRecover(auth, args);
241
241
  else if (name === "relay_workspace_release") result = await relayWorkspaceMutation(auth, "release-claim", args);
242
- else if (name.startsWith("relay_merge_")) result = relayMergeTool(auth, name, args); else if (name.startsWith("relay_maintenance_")) result = await relayMaintenanceTool(auth, name, args); else if (name.startsWith("relay_team_")) result = await relayTeamTool(auth, name, args); else if (name === "relay_decision") result = relayDecisionTool(auth, args);
242
+ else if (name.startsWith("relay_merge_")) result = (await relayMergeTool(auth, name, args)); else if (name.startsWith("relay_maintenance_")) result = await relayMaintenanceTool(auth, name, args); else if (name.startsWith("relay_team_")) result = await relayTeamTool(auth, name, args); else if (name === "relay_decision") result = (await relayDecisionTool(auth, args));
243
243
  else if (name.startsWith("relay_plan_")) result = await relayPlanTool(auth, name, args);
244
244
  else if (name === "relay_task_deliver") result = await relayTaskDeliverTool(auth, name, args);
245
245
  else if (TASK_VALIDATION_TOOLS.some((tool) => tool.name === name)) result = await relayTaskValidationTool(auth, name, args);
@@ -252,16 +252,16 @@ async function callTool(auth: VerifiedMcpAuthContext, params: unknown, signal?:
252
252
  else if (name === "relay_task_comment" || name === "relay_task_close_bridge") result = await relayTaskWeaveTool(auth, name, args);
253
253
  else if (name === "relay_release_readiness") result = await relayReleaseTool(name, args);
254
254
  else if (name.startsWith("relay_task_")) result = await relayTaskTool(auth, name, args);
255
- else if (name.startsWith("relay_blackboard_")) result = relayBlackboardTool(auth, name, args);
255
+ else if (name.startsWith("relay_blackboard_")) result = (await relayBlackboardTool(auth, name, args));
256
256
  // #2591 — the journal tools must claim their prefix BEFORE the generic relay_project_
257
257
  // dispatcher, or they would fall through to relayProjectTool as "unknown tool".
258
- else if (name.startsWith("relay_project_journal_")) result = relayProjectJournalTool(auth, name, args);
259
- else if (name === "relay_project_ingest_claudemd") result = await relayProjectIngestClaudeMd(auth, args); else if (name.startsWith("relay_project_")) result = await relayProjectTool(auth, name, args); else if (name.startsWith("relay_issue_ref_")) result = await relayIssueRefTool(auth, name, args); else if (name.startsWith("relay_issue_association_")) result = relayIssueAssociationTool(auth, name, args); else if (name.startsWith("relay_issue_activity_")) result = await relayIssueActivityTool(auth, name, args); else if (name.startsWith("relay_issue_lifecycle_")) result = await relayIssueLifecycleTool(auth, name, args);
258
+ else if (name.startsWith("relay_project_journal_")) result = (await relayProjectJournalTool(auth, name, args));
259
+ else if (name === "relay_project_ingest_claudemd") result = await relayProjectIngestClaudeMd(auth, args); else if (name.startsWith("relay_project_")) result = await relayProjectTool(auth, name, args); else if (name.startsWith("relay_issue_ref_")) result = await relayIssueRefTool(auth, name, args); else if (name.startsWith("relay_issue_association_")) result = (await relayIssueAssociationTool(auth, name, args)); else if (name.startsWith("relay_issue_activity_")) result = await relayIssueActivityTool(auth, name, args); else if (name.startsWith("relay_issue_lifecycle_")) result = await relayIssueLifecycleTool(auth, name, args);
260
260
  else if (name === "relay_subscribe" || name === "relay_unsubscribe" || name === "relay_list_subscriptions" || name === "relay_why_notified" || name === "relay_notification_preview") result = await relayNotificationSubscriptionTool(auth, name, args);
261
- else if (name === "relay_notify_ntfy") result = relayNotifyNtfy(args);
261
+ else if (name === "relay_notify_ntfy") result = (await relayNotifyNtfy(args));
262
262
  else if (name === "relay_delete_channel_binding") result = await relayDeleteChannelBinding(args, auth);
263
263
  else if (name === "relay_channel_output") result = await relayChannelOutput(args, auth);
264
- else if (name === "relay_send_to_channel") result = relayEgressToChannel(args, auth);
264
+ else if (name === "relay_send_to_channel") result = (await relayEgressToChannel(args, auth));
265
265
  else throw new ValidationError(`unknown tool: ${wireName}`);
266
266
 
267
267
  return toolResult(presentToolOutput(name, result, verbosity, args));
@@ -295,9 +295,9 @@ async function visibleTools(auth: McpAuthContext, generation: ToolGeneration): P
295
295
  alwaysOn: auth.component?.role === "provider" && Boolean(constraints.profile || auth.component.sub.startsWith("runner:")),
296
296
  allowUnknownGroups: true,
297
297
  })
298
- : null;
298
+ : auth.component?.role === "provider" ? resolveRelayTools({}, { alwaysOn: false }) : null;
299
299
  if (auth.component?.role === "provider") {
300
- for (const name of RELAY_TOOL_GROUPS[SELF_CARE_TOOL_GROUP]!) allowedNames?.add(name);
300
+ for (const name of resolveRelayTools({ groups: [SELF_CARE_TOOL_GROUP] }, { alwaysOn: false })) allowedNames?.add(name);
301
301
  }
302
302
  // #1570 — an explicit per-tool allowlist narrows the surface further (still ∩ scopes ∩ groups). This
303
303
  // is the Codex-worker provider-split: it trims the always-loaded MCP set the client eager-loads every
@@ -63,7 +63,7 @@ export const OUTPUT_DESCRIPTIONS: Record<string, string> = {
63
63
  relay_why_notified: "Explain delivery.",
64
64
  relay_workflow_cancel: "Cancel run; reap agents.",
65
65
  relay_workflow_get: "Read run status; history opt-in.",
66
- relay_workflow_resume: "Resume, pause/unpause, or answer/escalate a decision.",
66
+ relay_workflow_resume: "Resume, pause/unpause, re-pin, or answer decisions.",
67
67
  relay_workflow_start: "Start Task workflow.",
68
68
  relay_workspace_deps: "Check or refresh workspace dependencies.",
69
69
  relay_workspace_list: "List workspaces.",
@@ -228,6 +228,7 @@ export function projectSpawnTargets(
228
228
  },
229
229
  baseline: matrix.baseline,
230
230
  orchestrators: matrix.orchestrators,
231
+ ...(matrix.apiBudgets ? { apiBudgets: matrix.apiBudgets } : {}),
231
232
  // #2656 — operator-disabled providers are absent from the matrix; name them so callers know why.
232
233
  ...(Array.isArray(full.disabledProviders) ? { disabledProviders: full.disabledProviders } : {}),
233
234
  // Profile prose stays in verbose; daily discovery needs only the spawnable names (#3594).
@@ -238,7 +239,7 @@ export function projectSpawnTargets(
238
239
  target: routingTargetLabel(asRecord(rule.target)),
239
240
  })),
240
241
  },
241
- guidance: "Choose an online host, provider, model, and effort above. Default to isSelf; prefer named profiles and use spawnRequestId for idempotency.",
242
+ guidance: "Default to isSelf; use named profiles and spawnRequestId for idempotency.",
242
243
  };
243
244
  }
244
245
 
@@ -27,7 +27,7 @@ export const RUNTIME_OUTPUT: Record<string, OutputProjection> = {
27
27
  relay_spawn_targets: [],
28
28
  relay_channel_output: [],
29
29
  relay_claude_api_budget: [],
30
- relay_routing_policy_get: [],
30
+ relay_routing_policy_get: [{ path: "diagnostics", brief: null, lean: null }],
31
31
  relay_service_access: [],
32
32
  relay_list_timers: ["body", "command.stdoutTail", "command.stderrTail"].map(path => ({ path: `timers.*.${path}`, brief: 160 })),
33
33
  relay_list_artifacts: [{ path: "artifacts.*.links.*.title", brief: null }],
@@ -74,6 +74,11 @@ function jobSummary(job: MaintenanceJob, verbosity: OutputVerbosity): Record<str
74
74
  }
75
75
 
76
76
  export function projectRuntimeOutput(name: string, result: unknown, verbosity: OutputVerbosity): unknown {
77
+ // Migration provenance is already exposed by diagnostics; never send the notes twice (#3828).
78
+ if (name === "relay_routing_policy_get" && isRecord(result) && isRecord(result.policy)) {
79
+ const { migration, ...policy } = result.policy;
80
+ return { ...result, policy };
81
+ }
77
82
  if (name !== "relay_maintenance_jobs" || verbosity === "verbose" || !isRecord(result)) return result;
78
83
  if (verbosity === "brief" && isRecord(result.job)) return { job: jobSummary(result.job as unknown as MaintenanceJob, verbosity) };
79
84
  if (!Array.isArray(result.jobs)) return result;
@@ -1,7 +1,7 @@
1
1
  import { isRecord } from "@humwick/sdk";
2
2
  import type { AgentCard, ComponentToken } from "../types";
3
3
  import { hasComponentScope } from "../security";
4
- import { RELAY_TOOL_GROUPS, resolveRelayTools, SELF_CARE_TOOL_GROUP } from "./tool-groups";
4
+ import { resolveRelayTools, SELF_CARE_TOOL_GROUP } from "./tool-groups";
5
5
 
6
6
  export function supportsSelfResume(agent: AgentCard | null): boolean {
7
7
  const context = agent?.providerCapabilities?.context;
@@ -17,7 +17,7 @@ export function canDiscoverSelfResume(agent: AgentCard | null, token: ComponentT
17
17
  const constraints = token.constraints;
18
18
  const selection = { groups: constraints?.toolGroups, include: constraints?.toolInclude, exclude: constraints?.toolExclude };
19
19
  const tools = resolveRelayTools(selection, { alwaysOn: token.role === "provider" && Boolean(constraints?.profile || token.sub.startsWith("runner:")), allowUnknownGroups: true });
20
- if (token.role === "provider") for (const name of RELAY_TOOL_GROUPS[SELF_CARE_TOOL_GROUP]!) tools.add(name);
20
+ if (token.role === "provider") for (const name of resolveRelayTools({ groups: [SELF_CARE_TOOL_GROUP] }, { alwaysOn: false })) tools.add(name);
21
21
  if (!tools.has("relay_compact_and_resume")) return false;
22
22
  return !constraints?.toolAllowlist || constraints.toolAllowlist.includes("relay_compact_and_resume");
23
23
  }
@@ -31,7 +31,16 @@ export function projectSpawnTargetMatrix(full: Record<string, unknown>, enrich?:
31
31
  const baseline = choices.map(({ value }) => value);
32
32
  return {
33
33
  self: { orchestratorId: stringOrNull(self.orchestratorId), host: stringOrNull(self.host) },
34
- ...(Array.isArray(full.apiBudgets) ? { apiBudgets: full.apiBudgets } : {}),
34
+ ...(Array.isArray(full.apiBudgets) ? { apiBudgets: asRecords(full.apiBudgets).map((budget) => ({
35
+ budgetId: budget.budgetId, source: budget.source, month: budget.month,
36
+ headroomMicroUsd: budget.headroomMicroUsd,
37
+ ...(budget.baselineKnown ? {
38
+ startsAt: budget.startsAt, endsAt: budget.endsAt,
39
+ ceilingMicroUsd: budget.ceilingMicroUsd, estimatedSpentMicroUsd: budget.estimatedSpentMicroUsd,
40
+ enabled: budget.enabled,
41
+ ...(budget.refusalReason ? { refusalReason: budget.refusalReason } : {}),
42
+ } : {}),
43
+ })) } : {}),
35
44
  ...(Array.isArray(full.disabledProviders) ? { disabledProviders: full.disabledProviders } : {}),
36
45
  baseline: choices.map(({ value, original }) => ({ ...value, ...enrich?.(original) })),
37
46
  orchestrators: hosts.map(({ host, providers }) => {
@@ -1,5 +1,6 @@
1
1
  import { memoryRetirementMessage } from "../memory-retirement";
2
2
  import { registryToolName, type AgentProfile } from "@humwick/sdk";
3
+ import { ASSIGNED_RELAY_TOOLS } from "./tool-roles";
3
4
 
4
5
  // Tool group membership (#551). Each tool belongs to exactly one group; toolGroups in the token
5
6
  // constraints lists which groups are enabled (absent = all). This is the surface/eager layer on
@@ -93,11 +94,11 @@ export function resolveRelayTools(selection: AgentProfile["relay"]["tools"] = {}
93
94
  if (!RELAY_TOOL_NAMES.has(registryToolName(name))) throw new Error(memoryRetirementMessage(registryToolName(name)) ?? `${field}: unknown Relay tool "${name}"`);
94
95
  }
95
96
  }
96
- const tools = new Set((selection.groups ?? Object.keys(RELAY_TOOL_GROUPS)).flatMap((group) => (RELAY_TOOL_GROUPS[group] ?? [])));
97
+ const tools = new Set((selection.groups ?? Object.keys(RELAY_TOOL_GROUPS)).flatMap((group) => (RELAY_TOOL_GROUPS[group] ?? [])).filter(name => ASSIGNED_RELAY_TOOLS.has(name)));
97
98
  for (const name of selection.include ?? []) tools.add(registryToolName(name));
98
99
  for (const name of selection.exclude ?? []) tools.delete(registryToolName(name));
99
100
  for (const group of options.alwaysOn !== false ? ["core", SELF_CARE_TOOL_GROUP] : []) {
100
- for (const name of RELAY_TOOL_GROUPS[group]!) tools.add(name);
101
+ for (const name of RELAY_TOOL_GROUPS[group]!) if (ASSIGNED_RELAY_TOOLS.has(name)) tools.add(name);
101
102
  }
102
103
  return tools;
103
104
  }
@@ -0,0 +1,81 @@
1
+ import type { AgentProfile } from "@humwick/sdk";
2
+
3
+ // Explicit names, never registry/group expansion: adding a tool must not grow a role (#3817).
4
+ const COMMON_READ = ["relay_guide", "relay_whoami", "relay_get_message", "relay_get_thread", "relay_find_agents", "relay_task_get", "relay_recall", "web_search"];
5
+ const COMMON_WRITE = ["relay_secret_request", "relay_send_message", "relay_reply", "relay_task_deliver", "relay_compact_and_resume", "relay_run", "relay_schedule_timer"];
6
+ const MEMORY_READ = ["relay_project_read", "relay_project_search"];
7
+ const MEMORY_WRITE = ["relay_project_write_entry", "relay_project_retract"];
8
+ const ARTIFACT_READ = ["relay_list_artifacts", "relay_read_artifact"];
9
+ const ARTIFACT_WRITE = ["relay_upload_artifact", "relay_attach_artifact", "relay_pin_artifact"];
10
+ const TEAM_READ = ["relay_blackboard_get", "relay_blackboard_read", "relay_blackboard_search"];
11
+ const TEAM_WRITE = ["relay_blackboard_write", "relay_blackboard_retract"];
12
+ const TASK_READ = ["relay_task_list", "relay_task_history", "relay_task_memory_read"];
13
+ const PLAN_READ = ["relay_plan_get", "relay_plan_list", "relay_plan_conformance"];
14
+ const WORKSPACE_READ = ["relay_workspace_status", "relay_workspace_deps"];
15
+ const TIMER_READ = ["relay_list_timers"];
16
+ const TIMER_WRITE = ["relay_schedule_timer", "relay_cancel_timer"];
17
+ const WORK_READ = [...MEMORY_READ, ...ARTIFACT_READ, ...TEAM_READ, ...WORKSPACE_READ, ...TASK_READ, ...PLAN_READ, "relay_workflow_get"];
18
+ const WORK_WRITE = [...MEMORY_WRITE, ...ARTIFACT_WRITE, ...TEAM_WRITE];
19
+ const PLAN_WRITE = ["relay_plan_import", "relay_plan_create", "relay_plan_add_node", "relay_plan_update_node", "relay_plan_add_edge", "relay_plan_remove_node", "relay_plan_remove_edge", "relay_plan_archive", "relay_plan_delete", "relay_plan_bind_node", "relay_plan_start"];
20
+ const TASK_WRITE = ["relay_task_set_validation", "relay_task_needs_validation", "relay_task_create", "relay_task_update", "relay_task_set_status", "relay_task_set_stage", "relay_task_set_review_hold", "relay_task_withdraw_land", "relay_task_confirm_carry", "relay_task_record_gate", "relay_task_add_dependency", "relay_task_remove_dependency", "relay_task_add_ref", "relay_task_remove_ref", "relay_task_assign_agent", "relay_task_memory_replace_working_state", "relay_task_memory_append_ruled_out", "relay_task_memory_append_decision", "relay_task_dispatch", "relay_backlog_promote", "relay_task_comment", "relay_task_close_bridge"];
21
+ const SPAWN_READ = ["relay_spawn_targets", "relay_my_agents", "relay_agent_status", "relay_agent_crash_report", "relay_team_get"];
22
+ const SPAWN_WRITE = ["relay_spawn_agent", "relay_shutdown_agent", "relay_interrupt_agent", "relay_agent_keep", "relay_agent_action", "relay_permission_decision", "relay_team_set_spawn_delegation", "relay_team_set_brief", "relay_team_claim", "relay_team_dissolve"];
23
+ const WORKFLOW_WRITE = ["relay_workflow_start", "relay_workflow_resume", "relay_workflow_cancel"];
24
+
25
+ function role(why: string, read: readonly string[] = [], write: readonly string[] = []) {
26
+ return { why, read: [...new Set([...COMMON_READ, ...read])].sort(), write: [...new Set([...COMMON_WRITE, ...write])].sort() };
27
+ }
28
+
29
+ const implementer = role("Implement and deliver the hydrated bound Task. Bounded waits use relay_run.", [...MEMORY_READ, ...ARTIFACT_READ, ...TEAM_READ, ...WORKSPACE_READ], [...WORK_WRITE, "relay_set_effort"]);
30
+ const quality = role("Read Tasks and plans, execute checks and deliver findings; report through the bound Task.", WORK_READ, [...WORK_WRITE, ...TIMER_WRITE]);
31
+ const research = role("Research and report; keep findings and artifacts without workspace or orchestration administration.", [...MEMORY_READ, ...TEAM_READ, ...ARTIFACT_READ, "relay_web_tool_calls"], [...MEMORY_WRITE, ...TEAM_WRITE, ...ARTIFACT_WRITE]);
32
+
33
+ export const RELAY_TOOL_ROLES: Readonly<Record<string, ReturnType<typeof role>>> = {
34
+ coordinator: role("Own plan, Task, workflow and team lifecycle; request service access and delegate broker setup and operational work to specialists.",
35
+ [...WORK_READ, ...SPAWN_READ, ...TIMER_READ, "relay_backlog", "relay_release_readiness", "relay_hosts", "relay_merge_pauses", "relay_routing_policy_get", "relay_project_list", "relay_project_get", "relay_project_journal_get", "relay_team_template_list", "relay_team_template_get"],
36
+ [...WORK_WRITE, ...PLAN_WRITE, ...TASK_WRITE, ...SPAWN_WRITE, ...WORKFLOW_WRITE, ...TIMER_WRITE, "relay_decision", "relay_set_effort", "relay_service_access", "relay_red_main_recover", "relay_merge_pause", "relay_merge_renew", "relay_merge_resume", "relay_workspace_merge_hold", "relay_workspace_merge_resume", "relay_host_gate_budget", "relay_routing_policy_set", "relay_project_profile_instructions", "relay_prompt", "relay_project_onboard", "relay_project_checkout", "relay_project_add_root", "relay_project_add_edge", "relay_project_remove_edge", "relay_project_archive", "relay_project_promote_entry", "relay_project_ingest_claudemd", "relay_project_journal_update", "relay_team_template_save", "relay_team_template_delete", "relay_team_spawn_from_template"]),
37
+ "sub-coordinator": role("Coordinate a delegated project and its plan/workflow; parent owns fleet routing and project administration.",
38
+ [...WORK_READ, ...SPAWN_READ, ...TIMER_READ],
39
+ [...WORK_WRITE, ...PLAN_WRITE, ...WORKFLOW_WRITE, ...TIMER_WRITE, "relay_decision", "relay_set_effort", "relay_spawn_agent", "relay_shutdown_agent", "relay_interrupt_agent", "relay_agent_keep", "relay_agent_action", "relay_permission_decision", "relay_team_set_brief", "relay_team_dissolve", "relay_task_create", "relay_task_update", "relay_task_set_status", "relay_task_set_review_hold", "relay_task_withdraw_land", "relay_task_record_gate", "relay_task_add_dependency", "relay_task_remove_dependency", "relay_task_add_ref", "relay_task_remove_ref", "relay_task_assign_agent", "relay_task_memory_replace_working_state", "relay_task_memory_append_ruled_out", "relay_task_memory_append_decision", "relay_task_dispatch", "relay_task_comment"]),
40
+ worker: implementer,
41
+ "worker-gui": role("Implement GUI work from the hydrated Task and return artifacts; inspect its workspace.", [...MEMORY_READ, ...ARTIFACT_READ, ...WORKSPACE_READ], [...MEMORY_WRITE, ...ARTIFACT_WRITE, "relay_set_effort"]),
42
+ debugger: implementer,
43
+ reviewer: quality,
44
+ QA: quality,
45
+ "device-qa": quality,
46
+ "slice-qa": quality,
47
+ "plan-auditor": quality,
48
+ probe: role("Run a bounded proof and return its result; no project or workspace administration."),
49
+ "image-gen": role("Generate and deliver image artifacts for a bound Task.", [...MEMORY_READ, ...ARTIFACT_READ, ...WORKSPACE_READ], [...MEMORY_WRITE, ...ARTIFACT_WRITE]),
50
+ "software-design": role("Inspect architecture, Tasks and plans and deliver a design; coordinator authors orchestration.", WORK_READ, WORK_WRITE),
51
+ brainstorm: research,
52
+ explorer: research,
53
+ "isolated-research": research,
54
+ "social-media": role("Research and prepare channel output; no team or Task administration.", [...MEMORY_READ, ...ARTIFACT_READ, "relay_web_tool_calls"], [...MEMORY_WRITE, ...ARTIFACT_WRITE, "relay_channel_output", "relay_send_to_channel"]),
55
+ "ops-assistant": role("Operate gates, maintenance and delivery; inspect agent health without spawning or planning.",
56
+ [...MEMORY_READ, ...ARTIFACT_READ, ...WORKSPACE_READ, ...TIMER_READ, "relay_hosts", "relay_agent_status", "relay_agent_crash_report", "relay_maintenance_jobs", "relay_maintenance_pauses", "relay_merge_pauses", "relay_workspace_list", "relay_release_readiness"],
57
+ [...MEMORY_WRITE, ...ARTIFACT_WRITE, ...TIMER_WRITE, "relay_maintenance_pause", "relay_maintenance_resume", "relay_merge_pause", "relay_merge_renew", "relay_merge_resume", "relay_workspace_claim", "relay_workspace_release", "relay_workspace_merge_hold", "relay_workspace_merge_resume", "relay_red_main_recover", "relay_set_effort"]),
58
+ "sec-ops": role("Audit security and manage broker access; no orchestration or fleet mutation.",
59
+ [...MEMORY_READ, ...ARTIFACT_READ, ...WORKSPACE_READ, "relay_hosts", "relay_web_tool_calls"], [...MEMORY_WRITE, ...ARTIFACT_WRITE, "relay_service_access", "relay_secret_reader_create", "relay_secret_project_create", "relay_secret_set", "relay_notify_ntfy"]),
60
+ // These require admin/control tokens, not provider-agent grants. No hub agent profile is created.
61
+ operator: role("Operator/control-client tools that cannot be conferred by an agent profile.",
62
+ ["relay_kernel_listInstances", "relay_kernel_getInstance", "relay_kernel_getJournal", "relay_kernel_listEvents", "relay_kernel_listEntities", "relay_kernel_getEntity", "relay_issue_ref_get", "relay_issue_ref_list", "relay_issue_association_list_issues_for_entity", "relay_issue_association_list_entities_for_issue", "relay_issue_activity_by_issue", "relay_issue_activity_by_agent", "relay_list_subscriptions", "relay_why_notified", "relay_notification_preview"],
63
+ ["relay_kernel_cancel", "relay_kernel_pause", "relay_kernel_resume", "relay_kernel_retry", "relay_profile_trust", "relay_codex_reset_consume", "relay_claude_api_budget", "relay_delete_channel_binding", "relay_issue_ref_upsert", "relay_issue_ref_resolve", "relay_issue_ref_update", "relay_issue_ref_delete", "relay_issue_association_associate", "relay_issue_association_remove", "relay_issue_activity_record", "relay_issue_lifecycle_reopen", "relay_subscribe", "relay_unsubscribe", "relay_notify_ntfy", "relay_reevaluate_channel_delivery"]),
64
+ };
65
+
66
+ export const ASSIGNED_RELAY_TOOLS: ReadonlySet<string> = new Set(Object.values(RELAY_TOOL_ROLES).flatMap(role => [...role.read, ...role.write]));
67
+
68
+ /** Exact includes keep later group additions out of already-curated profiles. */
69
+ export function relayRoleSelection(name: string): NonNullable<AgentProfile["relay"]["tools"]> {
70
+ const assignment = RELAY_TOOL_ROLES[name];
71
+ if (!assignment) throw new Error(`No Relay tool role for profile ${name}`);
72
+ return { groups: [], include: [...new Set([...assignment.read, ...assignment.write])].sort(), exclude: [] };
73
+ }
74
+
75
+ export function relayToolRoleErrors(registered: readonly string[]): string[] {
76
+ const names = new Set(registered);
77
+ return [
78
+ ...registered.filter(name => !ASSIGNED_RELAY_TOOLS.has(name)).map(name => `${name}: assign this Relay tool to at least one role in src/mcp/tool-roles.ts`),
79
+ ...[...ASSIGNED_RELAY_TOOLS].filter(name => !names.has(name)).map(name => `${name}: role assignment names an unregistered Relay tool`),
80
+ ];
81
+ }
@@ -238,7 +238,7 @@ export async function relayWorkspaceMutation(auth: McpAuthContext, action: Works
238
238
  payload.guidance = describeWorkspacePhase(result.workspace);
239
239
  // Legacy workspace-ready route/CLI path is an explicit, platform-owned completion signal: the caller
240
240
  // is declaring its work window closed. Close it, forwarding only the held final group.
241
- markAgentWorkFinal(result.workspace.ownerAgentId ?? callerAgentId(auth), "workspace-ready");
241
+ (await markAgentWorkFinal(result.workspace.ownerAgentId ?? callerAgentId(auth), "workspace-ready"));
242
242
  }
243
243
  return payload;
244
244
  }
@@ -1,4 +1,5 @@
1
- import { getAgent, getEntry, getEntryChain, listEntries, retractEntry, searchEntries, ValidationError, writeEntry } from "./db";
1
+ import { writeEntryAsync, retractEntryAsync } from "./db/runtime-tail-async";
2
+ import { getAgent, getEntry, getEntryChain, listEntries, searchEntries, ValidationError } from "./db";
2
3
  import { McpNotFoundError } from "./mcp-errors";
3
4
  import { authContextFromMcp } from "./services/auth-context";
4
5
  import { cleanString, cleanStringArray, optionalEnum } from "./validation";
@@ -92,14 +93,14 @@ function ensureSameTeam(entryId: string, teamId: string): void {
92
93
  if (entry.teamId !== teamId) throw new McpNotFoundError("blackboard entry not found");
93
94
  }
94
95
 
95
- export function relayBlackboardTool(auth: McpAuthLike, name: string, args: Record<string, unknown>): Record<string, unknown> {
96
+ export async function relayBlackboardTool(auth: McpAuthLike, name: string, args: Record<string, unknown>): Promise<Record<string, unknown>> {
96
97
  const { agentId, teamId } = callerTeam(auth);
97
98
  if (name === "relay_blackboard_write") {
98
99
  const supersedes = cleanString(args.supersedes, "supersedes", { max: 120 });
99
100
  const kind = cleanKind(args.kind);
100
101
  if (!kind) throw new ValidationError("kind required");
101
102
  if (supersedes) ensureSameTeam(supersedes, teamId);
102
- return writeEntry({
103
+ return (await writeEntryAsync({
103
104
  teamId,
104
105
  authorId: agentId,
105
106
  kind,
@@ -107,7 +108,7 @@ export function relayBlackboardTool(auth: McpAuthLike, name: string, args: Recor
107
108
  body: cleanString(args.body, "body", { max: 16_000 }),
108
109
  tags: cleanStringArray(args.tags, "tags", { itemMax: 80, maxItems: 32 }),
109
110
  supersedes,
110
- }) as unknown as Record<string, unknown>;
111
+ })) as unknown as Record<string, unknown>;
111
112
  }
112
113
  if (name === "relay_blackboard_get") {
113
114
  const id = cleanString(args.id, "id", { required: true, max: 120 })!;
@@ -123,7 +124,7 @@ export function relayBlackboardTool(auth: McpAuthLike, name: string, args: Recor
123
124
  if (name === "relay_blackboard_retract") {
124
125
  const id = cleanString(args.id, "id", { required: true, max: 120 })!;
125
126
  ensureSameTeam(id, teamId);
126
- const entry = retractEntry(id);
127
+ const entry = (await retractEntryAsync(id));
127
128
  if (!entry) throw new McpNotFoundError("blackboard entry not found");
128
129
  return entry as unknown as Record<string, unknown>;
129
130
  }
@@ -66,16 +66,16 @@ function requiredId(args: Record<string, unknown>): number {
66
66
  return id;
67
67
  }
68
68
 
69
- function run(auth: McpAuthLike, args: Record<string, unknown>): Record<string, unknown> {
69
+ async function run(auth: McpAuthLike, args: Record<string, unknown>): Promise<Record<string, unknown>> {
70
70
  const ctx = authContextFromMcp(auth);
71
71
  const action = cleanEnum(args.action, "action", ACTIONS);
72
72
  if (action === "file") {
73
- const decision = fileDecision(ctx, args, callerProjectId(ctx));
73
+ const decision = (await fileDecision(ctx, args, callerProjectId(ctx)));
74
74
  return { id: decision.id, kind: decision.kind, status: decision.status, teamId: decision.teamId };
75
75
  }
76
- if (action === "update") return { id: updateDecision(ctx, requiredId(args), args).id, status: "open" };
77
- if (action === "withdraw") return { id: withdrawDecision(ctx, requiredId(args)).id, status: "withdrawn" };
78
- const teamId = callerDecisionTeam(ctx, false);
76
+ if (action === "update") return { id: (await updateDecision(ctx, requiredId(args), args)).id, status: "open" };
77
+ if (action === "withdraw") return { id: (await withdrawDecision(ctx, requiredId(args))).id, status: "withdrawn" };
78
+ const teamId = (await callerDecisionTeam(ctx, false));
79
79
  const id = optionalPositiveInt(args.id, "id");
80
80
  if (id !== undefined) return { decision: teamDecision(teamId, id) };
81
81
  const status = optionalEnum(args.status, "status", [...OPERATOR_DECISION_STATUSES, "all"] as const, "open");
@@ -84,9 +84,9 @@ function run(auth: McpAuthLike, args: Record<string, unknown>): Record<string, u
84
84
  return { lines: decisions.map((decision) => renderDecisionLine(decision)) };
85
85
  }
86
86
 
87
- export function relayDecisionTool(auth: McpAuthLike, args: Record<string, unknown>): Record<string, unknown> {
87
+ export async function relayDecisionTool(auth: McpAuthLike, args: Record<string, unknown>): Promise<Record<string, unknown>> {
88
88
  try {
89
- return run(auth, args);
89
+ return (await run(auth, args));
90
90
  } catch (error) {
91
91
  if (error instanceof ServiceAuthError) throw new McpAuthError(error.message);
92
92
  if (error instanceof DecisionNotFoundError) throw new McpNotFoundError(error.message);
@@ -1,3 +1,4 @@
1
+ import { sendMessageWithResultAsync } from "./db/message-send";
1
2
  // Generic egress MCP tool + relay_notify_ntfy shim (#777).
2
3
  //
3
4
  // relay_send_to_channel — resolves a registered channel agent by ID, builds a
@@ -15,7 +16,7 @@ import { channelOutputControl, changeChannelOutput } from "./channel-output-cont
15
16
  import { McpAuthError } from "./mcp-errors";
16
17
  import { isComponentAuthorizedFor, isIntegrationAllowed } from "./security";
17
18
  import { MAX_BODY_BYTES } from "./config";
18
- import { channelObligationAnsweredBy, getChannel, sendMessageWithResult, ValidationError } from "./db";
19
+ import { channelObligationAnsweredBy, getChannel, ValidationError } from "./db";
19
20
  import { validateChannelEnvelope } from "./routes/integrations";
20
21
  import { emitNewMessage } from "./sse";
21
22
  import { optionalEnum } from "./validation";
@@ -91,12 +92,12 @@ function egressBodyForEvent(event: Record<string, unknown>): string {
91
92
  return serialized;
92
93
  }
93
94
 
94
- function emitChannelEgress(
95
+ async function emitChannelEgress(
95
96
  channelId: string,
96
97
  event: Record<string, unknown>,
97
98
  callerId?: string,
98
99
  idempotencyKey?: string,
99
- ): { accepted: boolean; channelId: string; to: string } {
100
+ ): Promise<{ accepted: boolean; channelId: string; to: string }> {
100
101
  const channel = getChannel(channelId);
101
102
  if (!channel) throw new ValidationError(`channel ${channelId} not registered`);
102
103
  if (channel.direction === "inbound") {
@@ -123,7 +124,7 @@ function emitChannelEgress(
123
124
  // We DON'T route this through sendMessageService: that gate authorizes on message:send, but egress carries only channel:egress, so a component
124
125
  // token would 403; the raw path preserves the channel:egress scope model.
125
126
  const answeredObligation = callerId ? channelObligationAnsweredBy(callerId, channel.agentId) : null;
126
- const { message, created } = sendMessageWithResult({
127
+ const { message, created } = (await sendMessageWithResultAsync({
127
128
  from: callerId ?? "system",
128
129
  to: channel.agentId,
129
130
  kind: "channel.event",
@@ -132,19 +133,19 @@ function emitChannelEgress(
132
133
  replyExpected: false,
133
134
  ...(idempotencyKey ? { idempotencyKey } : {}),
134
135
  ...(answeredObligation !== null ? { replyTo: answeredObligation } : {}),
135
- });
136
+ }));
136
137
  if (created) emitNewMessage(message);
137
138
  return { accepted: true, channelId, to: channel.agentId };
138
139
  }
139
140
 
140
- export function relayEgressToChannel(args: Record<string, unknown>, auth?: McpAuthContext, idempotencyKey?: string): { accepted: boolean; channelId: string; to: string } {
141
+ export async function relayEgressToChannel(args: Record<string, unknown>, auth?: McpAuthContext, idempotencyKey?: string): Promise<{ accepted: boolean; channelId: string; to: string }> {
141
142
  const channelId = stringField(args.channelId, "channelId", { required: true, max: 200 });
142
143
  const event = recordField(args.event, "event");
143
144
  const callerId = auth ? callerAgentId(auth) : undefined;
144
- return emitChannelEgress(channelId, event, callerId, idempotencyKey);
145
+ return (await emitChannelEgress(channelId, event, callerId, idempotencyKey));
145
146
  }
146
147
 
147
- export function relayNotifyNtfy(args: Record<string, unknown>, idempotencyKey?: string): { accepted: boolean; channelId: string } {
148
+ export async function relayNotifyNtfy(args: Record<string, unknown>, idempotencyKey?: string): Promise<{ accepted: boolean; channelId: string }> {
148
149
  const title = stringField(args.title, "title", { required: true, max: 200 });
149
150
  const body = stringField(args.body, "body", { required: true, maxBytes: MAX_BODY_BYTES });
150
151
  const priority = optionalEnum(args.priority, "priority", NTFY_PRIORITIES) as NtfyPriority | undefined;
@@ -154,7 +155,7 @@ export function relayNotifyNtfy(args: Record<string, unknown>, idempotencyKey?:
154
155
  if (priority !== undefined) event.priority = priority;
155
156
  if (tags?.length) event.tags = tags;
156
157
  if (click !== undefined) event.click = click;
157
- const result = emitChannelEgress(NTFY_CHANNEL_ID, event, undefined, idempotencyKey);
158
+ const result = (await emitChannelEgress(NTFY_CHANNEL_ID, event, undefined, idempotencyKey));
158
159
  return { accepted: result.accepted, channelId: result.channelId };
159
160
  }
160
161
 
@@ -86,7 +86,7 @@ function limitArg(value: unknown): number | undefined {
86
86
 
87
87
  export async function relayIssueActivityTool(_auth: IssueActivityToolAuth, name: string, args: Record<string, unknown>): Promise<Record<string, unknown>> {
88
88
  if (name === "relay_issue_activity_record") {
89
- const event = recordAndEmitIssueActivity({
89
+ const event = (await recordAndEmitIssueActivity({
90
90
  agentId: cleanString(args.agentId, "agentId", { required: true, max: 200 })!,
91
91
  action: actionArg(args.action),
92
92
  issueRefId: cleanString(args.issueRefId, "issueRefId", { max: 120 }),
@@ -97,7 +97,7 @@ export async function relayIssueActivityTool(_auth: IssueActivityToolAuth, name:
97
97
  title: cleanString(args.title, "title", { max: 200 }),
98
98
  body: cleanString(args.body, "body", { max: 1000 }),
99
99
  metadata: cleanMeta(args.metadata),
100
- });
100
+ }));
101
101
  return { event };
102
102
  }
103
103
  if (name === "relay_issue_activity_by_issue") {
@@ -83,9 +83,9 @@ function associationInput(args: Record<string, unknown>) {
83
83
  };
84
84
  }
85
85
 
86
- export function relayIssueAssociationTool(_auth: IssueAssociationToolAuth, name: string, args: Record<string, unknown>): Record<string, unknown> {
86
+ export async function relayIssueAssociationTool(_auth: IssueAssociationToolAuth, name: string, args: Record<string, unknown>): Promise<Record<string, unknown>> {
87
87
  if (!isRecord(args)) throw new ValidationError("arguments must be an object");
88
- if (name === "relay_issue_association_associate") return associateIssueRef(associationInput(args)) as unknown as Record<string, unknown>;
88
+ if (name === "relay_issue_association_associate") return (await associateIssueRef(associationInput(args))) as unknown as Record<string, unknown>;
89
89
  if (name === "relay_issue_association_list_issues_for_entity") {
90
90
  return {
91
91
  issues: listIssueRefsForEntity(
@@ -95,9 +95,9 @@ export function relayIssueAssociationTool(_auth: IssueAssociationToolAuth, name:
95
95
  };
96
96
  }
97
97
  if (name === "relay_issue_association_list_entities_for_issue") {
98
- const issueRef = resolveAssociationIssueRef(issueInput(args));
98
+ const issueRef = (await resolveAssociationIssueRef(issueInput(args)));
99
99
  return { issueRef, entities: listIssueEntities(issueRef.id) };
100
100
  }
101
- if (name === "relay_issue_association_remove") return removeIssueAssociation(associationInput(args));
101
+ if (name === "relay_issue_association_remove") return (await removeIssueAssociation(associationInput(args)));
102
102
  throw new ValidationError(`unknown tool: ${name}`);
103
103
  }
@@ -1,5 +1,5 @@
1
- import { getIssueRef, getIssueRefByExternalKey, issueRefClosedCompleted, issueRefForRefString, listIssueRefs } from "./db/issue-refs";
2
- import { deleteIssueRefAsync, resolveIssueRefByExternalKeyAsync, resolveIssueRefFromStringAsync, updateIssueRefAsync, upsertIssueRefAsync } from "./db/issue-refs-async";
1
+ import { getIssueRef, getIssueRefByExternalKey, issueRefClosedCompleted, issueRefForRefString } from "./db/issue-refs";
2
+ import { deleteIssueRefAsync, listIssueRefsAsync, resolveIssueRefByExternalKeyAsync, resolveIssueRefFromStringAsync, updateIssueRefAsync, upsertIssueRefAsync } from "./db/issue-refs-async";
3
3
  import { McpNotFoundError } from "./mcp-errors";
4
4
  import { cleanEnum, cleanString, optionalEnum } from "./validation";
5
5
  import { ISSUE_REF_CLOSE_REASONS, ISSUE_PROVIDERS, ISSUE_REF_STATUSES, isRecord, type IssueProvider, type IssueRef, type IssueRefCloseReason, type IssueRefStatus } from "./types";
@@ -183,7 +183,7 @@ export async function relayIssueRefTool(_auth: IssueRefToolAuth, name: string, a
183
183
  }
184
184
  if (name === "relay_issue_ref_list") {
185
185
  return {
186
- issueRefs: listIssueRefs({
186
+ issueRefs: await listIssueRefsAsync({
187
187
  provider: optionalEnum(args.provider, "provider", ISSUE_PROVIDERS) as IssueProvider | undefined,
188
188
  repo: cleanString(args.repo, "repo", { max: 500 }),
189
189
  status: statusArg(args.status),
@@ -29,24 +29,24 @@ export const MERGE_TOOLS = [
29
29
 
30
30
  type McpAuthLike = Parameters<typeof authContextFromMcp>[0];
31
31
 
32
- export function relayMergeTool(auth: McpAuthLike, name: string, args: Record<string, unknown>): Record<string, unknown> {
32
+ export async function relayMergeTool(auth: McpAuthLike, name: string, args: Record<string, unknown>): Promise<Record<string, unknown>> {
33
33
  const ctx = authContextFromMcp(auth);
34
34
  if (name === "relay_merge_pauses") {
35
35
  const pauses = listAutoMergePauses();
36
36
  return { pauses, count: pauses.length };
37
37
  }
38
38
  if (name === "relay_merge_pause") {
39
- const result = pauseAutoMerge({
39
+ const result = (await pauseAutoMerge({
40
40
  repoRoot: cleanString(args.repoRoot, "repoRoot", { required: true, max: 1024 })!,
41
41
  baseRef: cleanString(args.baseRef, "baseRef", { max: 240 }),
42
42
  holder: cleanString(args.holder, "holder", { max: 240 }) ?? ctx.callerAgentId ?? ctx.actor.id,
43
43
  reason: cleanString(args.reason, "reason", { max: 1000 }),
44
44
  ttlMs: optionalPositiveInt(args.ttlMs, "ttlMs"),
45
- });
45
+ }));
46
46
  return result.ok ? { ok: true, pause: result.pause } : { ok: false, error: "merge target already paused", pause: result.pause };
47
47
  }
48
48
  if (name === "relay_merge_renew") {
49
- const pause = renewAutoMergePause(cleanString(args.id, "id", { required: true, max: 120 })!, optionalPositiveInt(args.ttlMs, "ttlMs"));
49
+ const pause = (await renewAutoMergePause(cleanString(args.id, "id", { required: true, max: 120 })!, optionalPositiveInt(args.ttlMs, "ttlMs")));
50
50
  if (!pause) throw new McpNotFoundError("merge pause not found or expired");
51
51
  return { pause };
52
52
  }
@@ -54,7 +54,7 @@ export function relayMergeTool(auth: McpAuthLike, name: string, args: Record<str
54
54
  const id = cleanString(args.id, "id", { max: 120 });
55
55
  const repoRoot = cleanString(args.repoRoot, "repoRoot", { max: 1024 });
56
56
  if (!id && !repoRoot) throw new ValidationError("id or repoRoot required");
57
- return { released: resumeAutoMerge({ id, repoRoot, baseRef: cleanString(args.baseRef, "baseRef", { max: 240 }) }) };
57
+ return { released: (await resumeAutoMerge({ id, repoRoot, baseRef: cleanString(args.baseRef, "baseRef", { max: 240 }) })) };
58
58
  }
59
59
  throw new ValidationError(`unknown tool: ${name}`);
60
60
  }
@@ -1,5 +1,5 @@
1
+ import { addSubscriptionAsync, addSubscriptionsAsync, removeSubscriptionAsync } from "./db/lifecycle-tail-async";
1
2
  import {
2
- addSubscription,
3
3
  childStreamFromSubscriptionType,
4
4
  childStreamSubscriptionType,
5
5
  cleanChildStream,
@@ -9,7 +9,6 @@ import {
9
9
  listSubscriptions,
10
10
  NOTIFICATION_SUBSCRIPTION_MODES,
11
11
  NOTIFICATION_SUBSCRIPTION_TYPES,
12
- removeSubscription,
13
12
  type ConfigurableChildStream,
14
13
  type ChildStream,
15
14
  type NotificationSubscriptionMode,
@@ -112,14 +111,14 @@ export async function relayNotificationSubscriptionTool(
112
111
  if (Object.prototype.hasOwnProperty.call(args, "streams")) {
113
112
  const scope = cleanChildStreamScope(args, agentId);
114
113
  const streams = cleanChildStreams(args.streams);
115
- const subscriptions = streams
114
+ const subscriptions = await addSubscriptionsAsync(streams
116
115
  .filter((stream): stream is ConfigurableChildStream => stream !== "responses")
117
- .map((stream) => addSubscription({
116
+ .map((stream) => ({
118
117
  agentId,
119
118
  type: childStreamSubscriptionType(stream),
120
119
  mode: cleanModeOrDefault(args.mode),
121
120
  scopeMatch: scope,
122
- }));
121
+ })));
123
122
  return {
124
123
  subscriptions,
125
124
  childStreamFilter: {
@@ -132,21 +131,21 @@ export async function relayNotificationSubscriptionTool(
132
131
  if (Object.prototype.hasOwnProperty.call(args, "taskId")) {
133
132
  const scope = await taskScope(ctx, args.taskId, args.scope);
134
133
  const mode = cleanModeOrDefault(args.mode);
135
- const subscriptions = TASK_NOTIFICATION_TYPES.map((type) => addSubscription({
134
+ const subscriptions = await addSubscriptionsAsync(TASK_NOTIFICATION_TYPES.map((type) => ({
136
135
  agentId,
137
136
  type,
138
137
  mode,
139
138
  scopeMatch: scope,
140
- }));
139
+ })));
141
140
  return { subscriptions, taskFilter: scope };
142
141
  }
143
142
  if (args.type === undefined || args.type === null) throw new ValidationError("type or streams required");
144
- const subscription = addSubscription({
143
+ const subscription = (await addSubscriptionAsync({
145
144
  agentId,
146
145
  type: cleanSubscriptionType(args.type),
147
146
  mode: cleanModeOrDefault(args.mode),
148
147
  scopeMatch: cleanScope(args.scope),
149
- });
148
+ }));
150
149
  return { subscription };
151
150
  }
152
151
  if (name === "relay_unsubscribe") {
@@ -157,12 +156,12 @@ export async function relayNotificationSubscriptionTool(
157
156
  let removed = 0;
158
157
  for (const stream of streams) {
159
158
  if (stream === "responses") continue;
160
- removed += removeSubscription({
159
+ removed += (await removeSubscriptionAsync({
161
160
  agentId,
162
161
  type: childStreamSubscriptionType(stream),
163
162
  mode: args.mode === undefined || args.mode === null ? undefined : cleanSubscriptionMode(args.mode),
164
163
  scopeMatch: scope,
165
- });
164
+ }));
166
165
  }
167
166
  return { removed, responsesAlwaysOn: true };
168
167
  }
@@ -173,24 +172,24 @@ export async function relayNotificationSubscriptionTool(
173
172
  : [cleanSubscriptionType(args.type)];
174
173
  let removed = 0;
175
174
  for (const type of types) {
176
- removed += removeSubscription({
175
+ removed += (await removeSubscriptionAsync({
177
176
  agentId,
178
177
  type,
179
178
  mode: args.mode === undefined || args.mode === null ? undefined : cleanSubscriptionMode(args.mode),
180
179
  scopeMatch: scope,
181
- });
180
+ }));
182
181
  }
183
182
  return { removed };
184
183
  }
185
184
  const type = args.type === undefined || args.type === null ? undefined : cleanSubscriptionType(args.type);
186
185
  if (!id && !type) throw new ValidationError("id or type required");
187
- const removed = removeSubscription({
186
+ const removed = (await removeSubscriptionAsync({
188
187
  agentId,
189
188
  id,
190
189
  type,
191
190
  mode: args.mode === undefined || args.mode === null ? undefined : cleanSubscriptionMode(args.mode),
192
191
  ...(Object.prototype.hasOwnProperty.call(args, "scope") ? { scopeMatch: cleanScope(args.scope) } : {}),
193
- });
192
+ }));
194
193
  return { removed };
195
194
  }
196
195
  if (name === "relay_list_subscriptions") {
@@ -58,7 +58,7 @@ export async function seedOutputFixture(database: ReturnType<typeof db.getDb>) {
58
58
  await call("relay_decision", { action: "file", title: "Choose output convention", context: OUTPUT_BODY, questions: [{ prompt: "Adopt canonical verbosity?", kind: "text" }] });
59
59
  db.upsertWorkspace({ id: "output-ws", repoRoot: process.cwd(), sourceCwd: process.cwd(), worktreePath: process.cwd(), branch: "agent/output", orchestratorId: "output-host", ownerAgentId: caller, mode: "isolated", status: "active", metadata: {} });
60
60
  db.recordCrashReport({ agentId: "output-worker-0", parentId: caller, reason: "Provider exited", diagnosis: { cause: "provider-crash", confidence: "high", summary: "Provider exited with nonzero status", evidence: [OUTPUT_BODY], detectedAt: 1000 }, createdAt: 1000 });
61
- const notification = routeNotification({ type: "agent.ready", scope: { agentId: "output-worker-0", parent: caller }, recipients: [caller], message: { body: "Worker ready" } })[0]!;
61
+ const notification = (await routeNotification({ type: "agent.ready", scope: { agentId: "output-worker-0", parent: caller }, recipients: [caller], message: { body: "Worker ready" } }))[0]!;
62
62
  const host = createSqliteModuleHost({ db: database, clock: createControllableClock(1000) });
63
63
  host.register(timers);
64
64
  const timer = await host.invoke(timers, "create", { actor: `runner:${caller}`, grants: ["timers:write"] }, { fireAt: 2000 }) as Timer;