@humwick/server 0.130.2 → 0.130.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (587) hide show
  1. package/bundled/prompts/manifest.json +1 -0
  2. package/bundled/prompts/task.hydration.md +1 -1
  3. package/bundled/roles/builder.md +1 -1
  4. package/docs/guides/sub-coordinators.md +12 -5
  5. package/docs/guides/tools-coordination.md +1 -0
  6. package/docs/guides/workflows.md +16 -0
  7. package/docs/openapi.json +175 -4
  8. package/package.json +8 -7
  9. package/public/assets/{MessageBubble-BWLqMGKG.js → MessageBubble-BOkmEvgZ.js} +2 -2
  10. package/public/assets/{PlanGraphPanel-3TlIQpjk.js → PlanGraphPanel-BkaDJp0r.js} +2 -2
  11. package/public/assets/{TraceView-Wimm0Czv.js → TraceView-B99Uu2yT.js} +2 -2
  12. package/public/assets/{VaultDecisionCard-Bau_vNax.js → VaultDecisionCard-DUIfTTFF.js} +2 -2
  13. package/public/assets/{activity-CrSurn-r.js → activity-ZIX0N1El.js} +2 -2
  14. package/public/assets/{agent-lineage-CknAJKcb.js → agent-lineage-kq9TPYFw.js} +2 -2
  15. package/public/assets/agent-liveness-DSQY1Aa_.js +2 -0
  16. package/public/assets/{agent-name-DKbiIhBo.js → agent-name-C_7F4DFR.js} +2 -2
  17. package/public/assets/{agent-profiles-DwF6EQ4R.js → agent-profiles-BNOcrGcY.js} +2 -2
  18. package/public/assets/{agent-project-badge-Diz4Sjjb.js → agent-project-badge-B6yAh70a.js} +2 -2
  19. package/public/assets/{agent-tools-CRhpRQza.js → agent-tools-CcrhmOzF.js} +2 -2
  20. package/public/assets/{agent-type-icon-LrUEU3lB.js → agent-type-icon-CXSLrKiq.js} +2 -2
  21. package/public/assets/{agents-B-v66C-4.js → agents-DIZG97_X.js} +2 -2
  22. package/public/assets/{analytics-BqCb-LKi.js → analytics-rasmoPjR.js} +2 -2
  23. package/public/assets/{artifact-viewer-kf4pGuAR.js → artifact-viewer-tBpTrEPb.js} +2 -2
  24. package/public/assets/{artifacts-DJevXcvg.js → artifacts-BNkrynP1.js} +2 -2
  25. package/public/assets/{artifacts-D32zyw4Y.js → artifacts-Bjq-ZyBA.js} +2 -2
  26. package/public/assets/{automation-BpRajx_5.js → automation-Cx422L3L.js} +2 -2
  27. package/public/assets/{backlog-D_UjTdzX.js → backlog-RuFQ8PUu.js} +2 -2
  28. package/public/assets/{busy-health-N3VEh4ao.js → busy-health-BvNQ9g5x.js} +2 -2
  29. package/public/assets/{channels-9jopungq.js → channels-DNbKAS0I.js} +2 -2
  30. package/public/assets/{chat-CSXrTB3C.js → chat-Bp3Fx73h.js} +3 -3
  31. package/public/assets/{code-preview-p1NnmqK0.js → code-preview-KIRZA6cz.js} +2 -2
  32. package/public/assets/{connectors-CZdqlDNL.js → connectors-k7vO4vvc.js} +2 -2
  33. package/public/assets/{cosmos-BgdsE4dG.js → cosmos-BuVoq1aw.js} +2 -2
  34. package/public/assets/{crate-agent-30aXTKva.js → crate-agent-irqlt4NH.js} +2 -2
  35. package/public/assets/{dependency-graph-B_jvw2GE.js → dependency-graph-D_rurNBz.js} +2 -2
  36. package/public/assets/{deploys-DoN6bAb5.js → deploys-yzZqIpxE.js} +2 -2
  37. package/public/assets/{display-BrKWFGJz.js → display-BemJG1f_.js} +2 -2
  38. package/public/assets/{files-D6XEu8Ng.js → files-DXWcrxOz.js} +2 -2
  39. package/public/assets/{fleet-C7jQWbw2.js → fleet-TEuOCZXE.js} +2 -2
  40. package/public/assets/{fleet-metrics-BiDs3MLx.js → fleet-metrics-BGoDAFI-.js} +2 -2
  41. package/public/assets/{formatted-body-impl-DyG0WsQS.js → formatted-body-impl-BB9eq9nN.js} +2 -2
  42. package/public/assets/{formatted-body-CQBwc1fb.js → formatted-body-qy2nTD_1.js} +3 -3
  43. package/public/assets/{host-providers-dialog-DKaW31dN.js → host-providers-dialog-BGPHxuwT.js} +2 -2
  44. package/public/assets/{index-CRHep3M9.js → index-BC5XZc0z.js} +5 -5
  45. package/public/assets/{index-MYAIwYTX.css → index-D8Rp9pz2.css} +1 -1
  46. package/public/assets/{insights-CzrtkpOE.js → insights-C-KQG0h-.js} +2 -2
  47. package/public/assets/{integrations-aVsUmigt.js → integrations-ClPRPyKU.js} +2 -2
  48. package/public/assets/{issue-ref-chip-BnSlM_95.js → issue-ref-chip-DbH8J9Ak.js} +2 -2
  49. package/public/assets/{log-viewer-qe7Ee47Z.js → log-viewer-PH4zVn24.js} +2 -2
  50. package/public/assets/{maintenance-NfjqFZAk.js → maintenance-5lV43uTL.js} +2 -2
  51. package/public/assets/{managed-agents-BiH0HgaE.js → managed-agents-BdgU35aX.js} +2 -2
  52. package/public/assets/{markdown-preview-BixaT3Xr.js → markdown-preview-DNT4OUwQ.js} +3 -3
  53. package/public/assets/{markdown-preview-impl-DNGlWIS3.js → markdown-preview-impl-CnCcPy0A.js} +2 -2
  54. package/public/assets/{memory-DNsCl6UX.js → memory--GgWH1gH.js} +2 -2
  55. package/public/assets/{messages-DopU1L1X.js → messages-CzFl_97O.js} +2 -2
  56. package/public/assets/{orchestrators-Cq7ckLQT.js → orchestrators-CVvJVkzv.js} +2 -2
  57. package/public/assets/overview-DsrZvE86.js +2 -0
  58. package/public/assets/{pairs-DE_m6rrE.js → pairs-D2d0d2py.js} +2 -2
  59. package/public/assets/{profile-icon-B3rfh77F.js → profile-icon-_SMuSbYF.js} +2 -2
  60. package/public/assets/{project-profile-instructions-DQWZE0kA.js → project-profile-instructions-Apn15y43.js} +2 -2
  61. package/public/assets/{projects-Br2Bd2h1.js → projects-D3B6kto5.js} +2 -2
  62. package/public/assets/{prompt-settings-BKvFsvGD.js → prompt-settings-DnPB6Ez6.js} +2 -2
  63. package/public/assets/provider-display-bWwiuedV.js +2 -0
  64. package/public/assets/{registry-Cw38_6gW.js → registry-D790cIhN.js} +2 -2
  65. package/public/assets/run-card-BCro-Odj.js +2 -0
  66. package/public/assets/{secrets-DxI-vzSq.js → secrets-CVXvfvbU.js} +2 -2
  67. package/public/assets/{service-access-settings-D9s_VJbd.js → service-access-settings-D3Xxqb9w.js} +2 -2
  68. package/public/assets/{settings-CKZTVGIx.js → settings-CoUC7Jci.js} +2 -2
  69. package/public/assets/shipping-D_Lp9n_J.js +2 -0
  70. package/public/assets/{stats-DyUBKyWI.js → stats-CXiNjB9S.js} +2 -2
  71. package/public/assets/{status-dot-DYqlEUhJ.js → status-dot-C703YIy1.js} +2 -2
  72. package/public/assets/store-BgDALGIX.js +9 -0
  73. package/public/assets/{tasks-Bh7Chr4-.js → tasks-DO0WpJCx.js} +2 -2
  74. package/public/assets/{teams-bowI7qVY.js → teams-FGkdi_tL.js} +2 -2
  75. package/public/assets/{terminal-viewer-BgVLHdsU.js → terminal-viewer-Dh8dSC7e.js} +3 -3
  76. package/public/assets/{terminal-viewer-impl-Dz22UPy6.js → terminal-viewer-impl-B5Zriip3.js} +2 -2
  77. package/public/assets/{use-agent-terminal-DSQQ-iY7.js → use-agent-terminal-CRUVDvLC.js} +2 -2
  78. package/public/assets/{use-decisions-samMQhQf.js → use-decisions-CoaqRh4l.js} +2 -2
  79. package/public/assets/{use-project-list-59LheVP7.js → use-project-list-X-BgT53X.js} +2 -2
  80. package/public/assets/{use-selectors-BZa25xEB.js → use-selectors-0X_UZkk-.js} +2 -2
  81. package/public/assets/{user-avatar-DHxlvUvV.js → user-avatar-CkLhVBGu.js} +2 -2
  82. package/public/assets/{versioned-prompt-editor-BmED5DnJ.js → versioned-prompt-editor-BkTi7eim.js} +2 -2
  83. package/public/assets/{work-queue-CSvpuNLz.js → work-queue-DDOLEyKo.js} +2 -2
  84. package/public/assets/{workflow-marker-DAZcj5qt.js → workflow-marker-CzOsB8BM.js} +2 -2
  85. package/public/assets/{workflows-DdmxRSyg.js → workflows-DJilvmRM.js} +2 -2
  86. package/public/assets/{workspaces-BxeKfVuF.js → workspaces-CHcfbLR3.js} +2 -2
  87. package/public/cosmos.html +4 -4
  88. package/public/index.html +29 -29
  89. package/runner/plugins/claude/.claude-plugin/plugin.json +1 -1
  90. package/runner/src/adapters/claude-api.ts +1 -1
  91. package/runner/src/adapters/claude-headless.ts +1 -1
  92. package/runner/src/adapters/claude-launch-exit.ts +26 -0
  93. package/runner/src/adapters/claude-session-probe.ts +1 -1
  94. package/runner/src/adapters/claude-tmux.ts +38 -10
  95. package/runner/src/adapters/claude.ts +28 -75
  96. package/runner/src/adapters/provider-quota-harvest.ts +20 -71
  97. package/runner/src/agent-runner/lifecycle-milestones.ts +2 -2
  98. package/runner/src/agent-runner/liveness-exchange.ts +104 -0
  99. package/runner/src/claude-system-prompt.ts +49 -0
  100. package/runner/src/launch-assembly.ts +14 -10
  101. package/runner/src/profile-home.ts +1 -1
  102. package/runner/src/project-instructions.ts +2 -2
  103. package/runner/src/provider-command.ts +18 -0
  104. package/runner/src/provider-setup.ts +3 -2
  105. package/runner/src/runner-core.ts +14 -15
  106. package/runner/src/runner-helpers.ts +0 -8
  107. package/runner/src/session-scratch.ts +6 -2
  108. package/runner/test-support/provisioning-context.ts +4 -2
  109. package/runner/test-support/runner-harness.ts +2 -1
  110. package/scripts/steward-policy-cleanup.ts +2 -2
  111. package/src/agent-authz.ts +2 -2
  112. package/src/agent-drive-arbiter.ts +3 -8
  113. package/src/agent-exit-cleanup-async.ts +2 -2
  114. package/src/agent-exit-cleanup.ts +23 -19
  115. package/src/agent-lifecycle-events.ts +112 -85
  116. package/src/agent-ref.ts +3 -2
  117. package/src/agent-status-events.ts +43 -14
  118. package/src/auth/login-link.ts +35 -0
  119. package/src/auth/password.ts +4 -3
  120. package/src/auth/session-refresh.ts +1 -1
  121. package/src/auth/session.test-fixture.ts +2 -2
  122. package/src/auth/session.ts +7 -7
  123. package/src/automations/crud.ts +8 -81
  124. package/src/automations/dispatch.ts +33 -58
  125. package/src/automations/index.ts +1 -1
  126. package/src/automations/persistence.ts +65 -0
  127. package/src/automations/precheck.ts +18 -16
  128. package/src/automations/read-async.ts +11 -0
  129. package/src/automations/reconcile.ts +30 -39
  130. package/src/automations/storage.ts +33 -10
  131. package/src/automations/write-async.ts +33 -0
  132. package/src/branch-land-notice.ts +3 -3
  133. package/src/branch-landed.ts +25 -23
  134. package/src/bus.ts +21 -19
  135. package/src/cd-deploy-notify.ts +2 -2
  136. package/src/ci/notifications.ts +4 -4
  137. package/src/ci/root-missing.ts +3 -3
  138. package/src/ci/transition-notify.ts +5 -5
  139. package/src/claude-app/oauth-http.ts +3 -3
  140. package/src/cli/index.ts +7 -1
  141. package/src/cli/init.ts +53 -0
  142. package/src/cli/orchestrator.ts +1 -7
  143. package/src/cli/setup.ts +6 -4
  144. package/src/cli/workflow.ts +12 -3
  145. package/src/commands-db.ts +4 -4
  146. package/src/commands-write.ts +8 -0
  147. package/src/compaction-watch.ts +10 -8
  148. package/src/config-store-write.ts +2 -2
  149. package/src/config.ts +4 -11
  150. package/src/connectors.ts +10 -8
  151. package/src/context-advisory.ts +22 -21
  152. package/src/db/agent-action-write.ts +36 -0
  153. package/src/db/agent-control-async.ts +9 -0
  154. package/src/db/agent-control-write.ts +49 -0
  155. package/src/db/agent-effort-observe.ts +22 -0
  156. package/src/db/agent-meta.ts +14 -3
  157. package/src/db/agents-async.ts +8 -5
  158. package/src/db/agents.ts +15 -12
  159. package/src/db/auth-identities.ts +2 -2
  160. package/src/db/automation-read-ops.ts +11 -0
  161. package/src/db/automation-write-ops.ts +46 -0
  162. package/src/db/blackboard.ts +2 -2
  163. package/src/db/browser-session-write.ts +32 -0
  164. package/src/db/bus-outbox-write-ops.ts +3 -1
  165. package/src/db/checkout-refresh-schema.ts +47 -0
  166. package/src/db/checkout-refresh.ts +5 -5
  167. package/src/db/command-agent-profile-backfill.ts +39 -0
  168. package/src/db/command-agent-profiles.ts +5 -1
  169. package/src/db/command-retention.ts +126 -0
  170. package/src/db/commands-planner-stats.test-fixture.ts +8 -0
  171. package/src/db/commands-read-ops.ts +16 -0
  172. package/src/db/connection.ts +1 -182
  173. package/src/db/context-carry-write.ts +74 -0
  174. package/src/db/continuations.ts +4 -4
  175. package/src/db/db-error-registry.ts +12 -0
  176. package/src/db/gateway.ts +26 -133
  177. package/src/db/history-retention-ops.ts +13 -0
  178. package/src/db/inbox-async.ts +2 -2
  179. package/src/db/inbox-read-ops.ts +1 -1
  180. package/src/db/inbox.ts +9 -3
  181. package/src/db/issue-associations.ts +2 -2
  182. package/src/db/issue-refs-async.ts +4 -1
  183. package/src/db/issue-refs-read-ops.ts +6 -0
  184. package/src/db/issue-refs.ts +8 -8
  185. package/src/db/kernel-event-log-schema.ts +2 -0
  186. package/src/db/land-boundaries.ts +51 -5
  187. package/src/db/land-gate-attestations.ts +2 -2
  188. package/src/db/landing-receipts.ts +2 -2
  189. package/src/db/lifecycle-context-write.ts +5 -0
  190. package/src/db/lifecycle-tail-async.ts +76 -0
  191. package/src/db/lifecycle-tail-write-ops.ts +62 -0
  192. package/src/db/lifecycle-write-window.ts +2 -1
  193. package/src/db/lineage-pair-dedupe-sql.ts +3 -3
  194. package/src/db/main-connection-mode.ts +11 -0
  195. package/src/db/main-connection-writes.ts +20 -2
  196. package/src/db/maintenance-job-runs-async.ts +2 -2
  197. package/src/db/maintenance-job-write-ops.ts +1 -1
  198. package/src/db/maintenance-pauses.ts +2 -2
  199. package/src/db/message-send.ts +4 -0
  200. package/src/db/messages.ts +31 -25
  201. package/src/db/migrations.ts +18 -1
  202. package/src/db/notification-write-ops.ts +19 -0
  203. package/src/db/operator-answer-write.ts +19 -0
  204. package/src/db/orchestrators.ts +10 -4
  205. package/src/db/pairs-async.ts +22 -1
  206. package/src/db/pairs-write-ops.ts +8 -3
  207. package/src/db/pairs.ts +41 -48
  208. package/src/db/plan-dispatch-schema.ts +5 -1
  209. package/src/db/plan-ready-notices.ts +43 -0
  210. package/src/db/plan-tools-include-migration.ts +36 -0
  211. package/src/db/plans-write-ops.ts +2 -0
  212. package/src/db/presence-buffer.ts +5 -0
  213. package/src/db/presence-loop.ts +18 -23
  214. package/src/db/presence-write.ts +0 -6
  215. package/src/db/project-entries-async.ts +14 -0
  216. package/src/db/project-entries-read-ops.ts +5 -1
  217. package/src/db/project-entries-write-ops.ts +8 -0
  218. package/src/db/project-entries.ts +15 -12
  219. package/src/db/project-journal.ts +2 -2
  220. package/src/db/project-trust-approvals.ts +2 -2
  221. package/src/db/prompt-templates.ts +4 -4
  222. package/src/db/read-op-marshaling-fixtures.ts +18 -0
  223. package/src/db/read-ops.ts +4 -0
  224. package/src/db/retention.ts +13 -2
  225. package/src/db/review-publish-write.ts +8 -0
  226. package/src/db/runtime-admin-async.ts +19 -0
  227. package/src/db/runtime-admin-write-ops.ts +36 -0
  228. package/src/db/runtime-tail-async.ts +288 -0
  229. package/src/db/runtime-tail-write-ops.ts +97 -0
  230. package/src/db/schema.ts +4 -1
  231. package/src/db/spawn-budget.ts +5 -0
  232. package/src/db/spawn-delegation.ts +3 -12
  233. package/src/db/sql-audit-indexes.ts +3 -0
  234. package/src/db/stats-async.ts +5 -3
  235. package/src/db/task-entity.ts +5 -4
  236. package/src/db/team-claim-policy.ts +60 -0
  237. package/src/db/teams-async.ts +4 -0
  238. package/src/db/teams-write-ops.ts +4 -0
  239. package/src/db/terminal-events.ts +15 -0
  240. package/src/db/timer-cutover-bodies.ts +292 -0
  241. package/src/db/timer-cutover-core.ts +37 -278
  242. package/src/db/timer-cutover-state.ts +40 -0
  243. package/src/db/timer-cutover-write-ops.ts +1 -40
  244. package/src/db/token-db-write-ops.ts +2 -0
  245. package/src/db/token-standing-async.ts +12 -0
  246. package/src/db/token-standing.ts +6 -9
  247. package/src/db/token-usage-retention.ts +33 -0
  248. package/src/db/token-usage-write-ops.ts +1 -1
  249. package/src/db/token-usage.ts +4 -1
  250. package/src/db/user-invites-async.ts +13 -0
  251. package/src/db/user-invites.ts +3 -3
  252. package/src/db/users.ts +14 -6
  253. package/src/db/workflow-execution.ts +3 -3
  254. package/src/db/workspaces.ts +8 -5
  255. package/src/db/write-ops.ts +31 -12
  256. package/src/db/writer-fault-seam.ts +2 -9
  257. package/src/db/writer-fault-test-ops.ts +18 -0
  258. package/src/db/writer-gateway.ts +42 -46
  259. package/src/dev.ts +12 -16
  260. package/src/event-loop-lag.ts +4 -2
  261. package/src/event-loop-yield.ts +2 -2
  262. package/src/events.ts +10 -8
  263. package/src/host-provider-store.ts +5 -4
  264. package/src/hub-metrics-report.ts +5 -1
  265. package/src/hub-metrics.ts +38 -18
  266. package/src/init.ts +299 -0
  267. package/src/injection-sweep-pacing.ts +6 -8
  268. package/src/invite-link.ts +16 -9
  269. package/src/land-gate-notifications.ts +11 -10
  270. package/src/land-gate-queue-progress.ts +3 -3
  271. package/src/land-settle-dispatch.ts +52 -92
  272. package/src/lifecycle-context-pressure.ts +11 -10
  273. package/src/lifecycle-manager.ts +104 -98
  274. package/src/maintenance/breaker.ts +18 -16
  275. package/src/maintenance/checkout-refresh.ts +18 -1
  276. package/src/maintenance/critical-job-watchdog.ts +3 -3
  277. package/src/maintenance/events.ts +3 -3
  278. package/src/maintenance/history-retention-job.ts +30 -0
  279. package/src/maintenance/jobs.ts +29 -67
  280. package/src/maintenance/long-running-work.ts +3 -1
  281. package/src/maintenance/offline-agent-prune-job.ts +28 -0
  282. package/src/maintenance/orphaned-sessions.ts +5 -4
  283. package/src/maintenance/pauses.ts +4 -10
  284. package/src/maintenance/plan-ready-notice-job.ts +71 -0
  285. package/src/maintenance/scheduler.ts +18 -13
  286. package/src/maintenance/stalled-delivery-pager.ts +4 -4
  287. package/src/maintenance/teams.ts +19 -30
  288. package/src/maintenance/workspaces.ts +17 -27
  289. package/src/managed-policy-escalation.ts +7 -7
  290. package/src/managed-policy.ts +3 -3
  291. package/src/managed-provider-gate.ts +5 -4
  292. package/src/managed-spawn-reservation.ts +5 -4
  293. package/src/mcp/index.ts +8 -8
  294. package/src/mcp/output-descriptions.ts +1 -1
  295. package/src/mcp/presentation.ts +2 -1
  296. package/src/mcp/runtime-output.ts +6 -1
  297. package/src/mcp/self-care-visibility.ts +2 -2
  298. package/src/mcp/spawn-targets-matrix.ts +10 -1
  299. package/src/mcp/tool-groups.ts +3 -2
  300. package/src/mcp/tool-roles.ts +81 -0
  301. package/src/mcp/tools-workspace.ts +1 -1
  302. package/src/mcp-blackboard-tools.ts +6 -5
  303. package/src/mcp-decision-tools.ts +7 -7
  304. package/src/mcp-egress-tools.ts +10 -9
  305. package/src/mcp-issue-activity-tools.ts +2 -2
  306. package/src/mcp-issue-association-tools.ts +4 -4
  307. package/src/mcp-issue-ref-tools.ts +3 -3
  308. package/src/mcp-merge-tools.ts +5 -5
  309. package/src/mcp-notification-subscription-tools.ts +14 -15
  310. package/src/mcp-output-levels.test-fixture.ts +1 -1
  311. package/src/mcp-profile-trust-tools.ts +1 -1
  312. package/src/mcp-project-journal-tools.ts +4 -12
  313. package/src/mcp-project-tools.ts +17 -18
  314. package/src/mcp-routing-policy-tools.ts +3 -2
  315. package/src/mcp-team-tools.ts +6 -6
  316. package/src/mcp-workflow-tools.ts +5 -3
  317. package/src/memory-area-injection.ts +13 -12
  318. package/src/memory-boundary-capture.ts +6 -6
  319. package/src/memory-injection.ts +45 -43
  320. package/src/merge-pause-maintenance.ts +5 -4
  321. package/src/notification-router.ts +44 -17
  322. package/src/notify.ts +5 -5
  323. package/src/observed-reset-refresh.ts +4 -4
  324. package/src/operator-alarm.ts +2 -5
  325. package/src/project-ingest.ts +6 -6
  326. package/src/project-instructions.ts +8 -1
  327. package/src/project-trust.ts +12 -11
  328. package/src/prompt-resolver.ts +3 -3
  329. package/src/provider-quota-async.ts +5 -7
  330. package/src/provider-timeline-event.ts +4 -7
  331. package/src/quota-band-delivery.ts +2 -2
  332. package/src/recipe-runner.ts +4 -3
  333. package/src/retention-config-store.ts +4 -3
  334. package/src/reviewer-pipeline.ts +11 -10
  335. package/src/routes/_route.ts +4 -4
  336. package/src/routes/_shared.ts +13 -12
  337. package/src/routes/agent-effort.ts +5 -5
  338. package/src/routes/agent-heartbeat-quota.ts +23 -0
  339. package/src/routes/agent-keep.ts +1 -1
  340. package/src/routes/agent-reporting.ts +1 -1
  341. package/src/routes/agent-self-effort.ts +5 -5
  342. package/src/routes/agent-sessions.ts +24 -24
  343. package/src/routes/agents-spawn.ts +1 -1
  344. package/src/routes/agents.ts +17 -119
  345. package/src/routes/auth.ts +44 -17
  346. package/src/routes/authority-audit.ts +2 -2
  347. package/src/routes/automations.ts +6 -5
  348. package/src/routes/channel-binding-delete.ts +1 -1
  349. package/src/routes/channel-output.ts +3 -3
  350. package/src/routes/claude-api-budget.ts +5 -5
  351. package/src/routes/commands-listing.ts +1 -1
  352. package/src/routes/commands.ts +29 -27
  353. package/src/routes/context-resets.ts +2 -2
  354. package/src/routes/coordinator-teams.ts +1 -1
  355. package/src/routes/decisions.ts +2 -2
  356. package/src/routes/host-providers.ts +18 -0
  357. package/src/routes/host-secret-files.ts +1 -1
  358. package/src/routes/hosts.ts +11 -11
  359. package/src/routes/inbox.ts +3 -2
  360. package/src/routes/index.ts +7 -3
  361. package/src/routes/integrations.ts +9 -7
  362. package/src/routes/issue-activity.ts +2 -2
  363. package/src/routes/issue-associations.ts +4 -4
  364. package/src/routes/issue-refs.ts +3 -3
  365. package/src/routes/kernel-timers-operator.ts +1 -1
  366. package/src/routes/memory.ts +1 -1
  367. package/src/routes/merges.ts +11 -11
  368. package/src/routes/messages.ts +23 -17
  369. package/src/routes/orchestrator-bootstrap.ts +3 -1
  370. package/src/routes/orchestrator-host-registration.ts +2 -0
  371. package/src/routes/orchestrator-host-utilization.ts +2 -2
  372. package/src/routes/orchestrator-proxy.ts +2 -2
  373. package/src/routes/orchestrator.ts +27 -18
  374. package/src/routes/pairs.ts +8 -8
  375. package/src/routes/project-profile-instructions.ts +1 -1
  376. package/src/routes/project-trust.ts +1 -1
  377. package/src/routes/projects.ts +16 -16
  378. package/src/routes/prompt-templates.ts +12 -12
  379. package/src/routes/provider-quotas.ts +10 -10
  380. package/src/routes/run-command.ts +1 -1
  381. package/src/routes/secrets-backup.ts +4 -5
  382. package/src/routes/secrets-import.ts +3 -4
  383. package/src/routes/secrets-vault.ts +12 -14
  384. package/src/routes/settings.ts +1 -1
  385. package/src/routes/shipping.ts +9 -1
  386. package/src/routes/spawn-policy.ts +8 -7
  387. package/src/routes/sse.ts +2 -2
  388. package/src/routes/team-templates.ts +7 -7
  389. package/src/routes/teams.ts +10 -10
  390. package/src/routes/tokens.ts +18 -17
  391. package/src/routes/users.ts +24 -24
  392. package/src/routes/viewer-read.ts +3 -3
  393. package/src/routes/web-tool-calls.ts +4 -4
  394. package/src/routes/workflows.ts +2 -2
  395. package/src/routes/workspaces.ts +17 -16
  396. package/src/routing-policy-push.ts +6 -6
  397. package/src/runtime-grant.ts +17 -11
  398. package/src/runtime-install.ts +30 -0
  399. package/src/runtime-tokens.ts +20 -28
  400. package/src/secrets-broker/config.ts +1 -1
  401. package/src/secrets-broker/service.ts +2 -2
  402. package/src/security.ts +3 -1
  403. package/src/self-resume-carry.ts +21 -110
  404. package/src/self-resume-supersede.ts +4 -3
  405. package/src/self-resume.ts +54 -66
  406. package/src/server-background.ts +1 -1
  407. package/src/server.ts +25 -26
  408. package/src/services/agent-action.ts +12 -31
  409. package/src/services/agent-effort.ts +7 -32
  410. package/src/services/agent-hang-watchdog-job.ts +2 -2
  411. package/src/services/agent-heartbeat.ts +166 -0
  412. package/src/services/agent-reporting.ts +1 -1
  413. package/src/services/auth-context.ts +6 -0
  414. package/src/services/browser-inventory.ts +3 -3
  415. package/src/services/browser-session-state.ts +3 -2
  416. package/src/services/browser-session.ts +21 -27
  417. package/src/services/bus-command-update.ts +9 -9
  418. package/src/services/checkout-refresh-state.ts +1 -0
  419. package/src/services/checkout-refresh.test-fixture.ts +3 -2
  420. package/src/services/command-host.ts +7 -2
  421. package/src/services/coordinator-digest.ts +3 -2
  422. package/src/services/dispatch-command.ts +12 -13
  423. package/src/services/effective-launch-driver.ts +14 -9
  424. package/src/services/host-changed-paths.test-fixture.ts +5 -6
  425. package/src/services/host-project-readiness.ts +2 -2
  426. package/src/services/host-providers.ts +1 -1
  427. package/src/services/host-utilization.ts +9 -9
  428. package/src/services/idle-agent-digest.ts +17 -12
  429. package/src/services/idle-epics.ts +4 -4
  430. package/src/services/interrupt-agent.ts +12 -9
  431. package/src/services/issue-activity.ts +3 -3
  432. package/src/services/issue-associations.ts +15 -18
  433. package/src/services/issue-lifecycle-comments.ts +3 -2
  434. package/src/services/issue-lifecycle.ts +14 -13
  435. package/src/services/issue-refs.ts +4 -5
  436. package/src/services/kernel-timers-cutover-chaos-worker.ts +1 -1
  437. package/src/services/kernel-timers-ingress-chaos-worker.ts +6 -1
  438. package/src/services/kernel-timers-ingress.ts +3 -3
  439. package/src/services/kernel-timers-message-authority-chaos-worker.ts +3 -10
  440. package/src/services/kernel-timers-operator-chaos-worker.ts +3 -2
  441. package/src/services/kernel-timers-outbox.ts +1 -1
  442. package/src/services/kernel-timers-stage10-operator.ts +1 -1
  443. package/src/services/kernel-timers.ts +4 -3
  444. package/src/services/land-gate-placement.ts +11 -2
  445. package/src/services/land-issue-relations.ts +5 -4
  446. package/src/services/land-restart-retry.ts +7 -6
  447. package/src/services/landed-ancestry.ts +6 -5
  448. package/src/services/legacy-landing-host.ts +4 -1
  449. package/src/services/legacy-landing-recovery.ts +8 -6
  450. package/src/services/managed-running.ts +17 -17
  451. package/src/services/memory-areas.ts +3 -3
  452. package/src/services/merge-pause.ts +9 -15
  453. package/src/services/operator-decisions.ts +37 -41
  454. package/src/services/orchestrator-registration-recovery.ts +6 -2
  455. package/src/services/orchestrator-upgrade.ts +14 -12
  456. package/src/services/permission-decision.ts +4 -3
  457. package/src/services/plan-ci-notice.ts +1 -1
  458. package/src/services/plan-conformance.ts +4 -4
  459. package/src/services/plan-dispatch-policy.ts +10 -3
  460. package/src/services/plan-dispatch.ts +35 -14
  461. package/src/services/plan-graph.ts +39 -261
  462. package/src/services/plan-import.ts +1 -1
  463. package/src/services/plan-live-bindings.ts +8 -37
  464. package/src/services/plan-task-create.ts +1 -1
  465. package/src/services/plan-terminal-refs.ts +2 -12
  466. package/src/services/plan-wake-digest.ts +8 -3
  467. package/src/services/profile-trust.ts +2 -2
  468. package/src/services/project.ts +11 -10
  469. package/src/services/prompt-templates.ts +6 -5
  470. package/src/services/provider-api-key.ts +73 -0
  471. package/src/services/provider-hold-nudge.ts +9 -9
  472. package/src/services/provider-hold-record.ts +13 -12
  473. package/src/services/provider-state-audit.ts +19 -18
  474. package/src/services/provisioning-harness/driver.ts +1 -1
  475. package/src/services/provisioning-harness/fixture-matrix.ts +3 -3
  476. package/src/services/provisioning-harness/lifecycle.ts +24 -22
  477. package/src/services/provisioning-harness/prepare.ts +16 -17
  478. package/src/services/rate-limit-resume.ts +10 -9
  479. package/src/services/red-main-auto-revert.ts +7 -5
  480. package/src/services/red-main-controller-deps.ts +20 -3
  481. package/src/services/red-main-incident.ts +3 -1
  482. package/src/services/red-main-quarantine.ts +2 -3
  483. package/src/services/register-agent.ts +22 -22
  484. package/src/services/restart-project-role.ts +1 -1
  485. package/src/services/restart-spawn.ts +17 -16
  486. package/src/services/review-publish.ts +7 -24
  487. package/src/services/review-ref-reconcile.ts +29 -38
  488. package/src/services/runtime-dependency-alert.ts +27 -0
  489. package/src/services/scheduler-command.ts +11 -9
  490. package/src/services/scheduler.ts +5 -5
  491. package/src/services/self-effort-grant.ts +5 -25
  492. package/src/services/send-message.ts +16 -33
  493. package/src/services/shutdown-agent.ts +17 -14
  494. package/src/services/spawn-agent.ts +12 -41
  495. package/src/services/spawn-chain-inspector.ts +1 -1
  496. package/src/services/spawn-cwd-placement.ts +21 -5
  497. package/src/services/spawn-issue-activity.ts +8 -6
  498. package/src/services/spawn-launch.ts +17 -1
  499. package/src/services/spawn-loss-escalation.ts +1 -1
  500. package/src/services/spawn-registration-watchdog.ts +2 -2
  501. package/src/services/spawn-repo-probe.ts +7 -5
  502. package/src/services/spawn-tree-cleanup.ts +7 -8
  503. package/src/services/steward-reaper.ts +4 -4
  504. package/src/services/task-context-hydration.ts +25 -1
  505. package/src/services/task-delivery.ts +20 -34
  506. package/src/services/task-entity.ts +40 -203
  507. package/src/services/task-issue-link.ts +13 -20
  508. package/src/services/task-issue-weave.ts +12 -11
  509. package/src/services/task-lifecycle.ts +27 -31
  510. package/src/services/task-repo-cwd.ts +37 -0
  511. package/src/services/task-review-hold.ts +4 -17
  512. package/src/services/task-semantic-events.ts +13 -11
  513. package/src/services/task-state-reconcile.ts +3 -4
  514. package/src/services/task-validation.ts +2 -2
  515. package/src/services/team-templates.ts +5 -5
  516. package/src/services/team.ts +31 -98
  517. package/src/services/terminal-provider-exit.ts +6 -5
  518. package/src/services/timer-message-fire-chaos-worker.ts +7 -3
  519. package/src/services/timer-message-fire-outbox.ts +1 -1
  520. package/src/services/transient-agent-reaper.ts +22 -19
  521. package/src/services/turn-reconcile-audit.ts +9 -9
  522. package/src/services/user-onboarding.ts +8 -6
  523. package/src/services/user-profile.ts +6 -9
  524. package/src/services/work-finality.ts +16 -4
  525. package/src/services/workflow-auto-start.ts +2 -1
  526. package/src/services/workflow-caller.ts +61 -4
  527. package/src/services/workflow-child-proof-worker.ts +1 -1
  528. package/src/services/workflow-control-proof-worker.ts +1 -1
  529. package/src/services/workflow-coordinator.ts +8 -2
  530. package/src/services/workflow-decisions.ts +5 -5
  531. package/src/services/workflow-deferred-cleanup.ts +1 -1
  532. package/src/services/workflow-effect-driver.ts +42 -19
  533. package/src/services/workflow-effects.ts +41 -27
  534. package/src/services/workflow-pins.ts +7 -4
  535. package/src/services/workflow-reconcile.ts +1 -0
  536. package/src/services/workflow-resume-pins.ts +3 -3
  537. package/src/services/workflow-routing-defaults.ts +18 -0
  538. package/src/services/workflow-run-view.ts +4 -3
  539. package/src/services/workflow-run.test-fixture.ts +1 -1
  540. package/src/services/workflow-runs.ts +34 -22
  541. package/src/services/workflow-supervisor-state.ts +21 -1
  542. package/src/services/workflow-supervisor.ts +4 -3
  543. package/src/services/workspace-recovery.ts +4 -3
  544. package/src/setup.ts +47 -8
  545. package/src/spawn-admission.ts +10 -2
  546. package/src/spawn-placement.ts +27 -9
  547. package/src/spawn-targets.ts +8 -8
  548. package/src/sse-agent-status.ts +15 -6
  549. package/src/sse.ts +49 -24
  550. package/src/startup-context-injection.ts +61 -57
  551. package/src/steward-handoff.ts +4 -3
  552. package/src/steward-policy-cleanup.ts +9 -8
  553. package/src/steward.ts +2 -2
  554. package/src/task-dispatch-live.ts +5 -2
  555. package/src/task-dispatcher.ts +2 -2
  556. package/src/token-db.ts +2 -2
  557. package/src/token-usage-async.ts +2 -4
  558. package/src/user-awaiting-reply.ts +5 -5
  559. package/src/utils.ts +7 -0
  560. package/src/workspace-actions.ts +24 -29
  561. package/src/workspace-auto-merge.ts +14 -12
  562. package/src/workspace-changed-paths-client.ts +16 -5
  563. package/src/workspace-cleanup-stale.ts +30 -7
  564. package/src/workspace-command-settle.ts +24 -16
  565. package/src/workspace-config.ts +1 -1
  566. package/src/workspace-conflict-resolver.ts +9 -7
  567. package/src/workspace-dead-owner-recovery.ts +5 -4
  568. package/src/workspace-escalation.ts +12 -11
  569. package/src/workspace-human-escalation.ts +3 -3
  570. package/src/workspace-land-gate-watchdog.ts +3 -3
  571. package/src/workspace-land-refusal.ts +20 -0
  572. package/src/workspace-land-stopped.ts +1 -0
  573. package/src/workspace-merge.ts +10 -26
  574. package/src/workspace-nothing-to-land-notice.ts +2 -2
  575. package/src/workspace-orphans.ts +18 -37
  576. package/src/workspace-pr-completion.ts +10 -8
  577. package/src/workspace-rekey.ts +3 -3
  578. package/src/workspace-steward-failure.ts +2 -2
  579. package/src/workspace-steward-wake.ts +2 -2
  580. package/public/assets/agent-liveness-CaNwK0Zf.js +0 -2
  581. package/public/assets/overview-C1DCTzzV.js +0 -2
  582. package/public/assets/provider-display-DsuWMvVL.js +0 -2
  583. package/public/assets/run-card-CpKqRbhg.js +0 -2
  584. package/public/assets/shipping-B9YtnwtG.js +0 -2
  585. package/public/assets/store-DcClXLH0.js +0 -9
  586. package/runner/src/agent-runner/http-liveness.ts +0 -85
  587. package/src/db/on-loop-write-transaction.ts +0 -34
@@ -1,9 +1,10 @@
1
- import { clearAgentNotice, hasAgentExitNotice, recordAgentNoticeOnce, recordAgentReadyNoticeOnce } from "./db/terminal-events";
1
+ import { clearAgentNoticeAsync, recordCrashReportAsync, recordTerminalEventAsync } from "./db/lifecycle-tail-async";
2
+ import { hasAgentExitNotice } from "./db/terminal-events";
2
3
  import { requestAgentExitCleanup } from "./agent-exit-cleanup";
3
4
  import { emitRelayEvent } from "./events";
4
- import { routeNotification } from "./notification-router";
5
+ import { routeAgentNotice, routeNotification } from "./notification-router";
5
6
  import { LIFECYCLE_STATE_WORKING, childSignalKey, stampLifecycleSignal, taskSignalKey } from "./lifecycle-signal-registry";
6
- import { currentCoordinatorOf, deliverableTaskIdsBySpawnRequestId, getAgent, markAgentReadyNotified, recordCrashReport, recordTerminalEvent, workflowRunIdOf } from "./db";
7
+ import { currentCoordinatorOf, deliverableTaskIdsBySpawnRequestId, getAgent, markAgentReadyNotifiedAsync, workflowRunIdOf } from "./db";
7
8
  import { crashReportFor } from "./agent-death-diagnosis";
8
9
  import { renderTemplate } from "./prompt-resolver";
9
10
  import { markAgentWorkFinal } from "./services/work-finality";
@@ -57,6 +58,19 @@ function diagnosisSuffix(diagnosis?: AgentDeathDiagnosis): string {
57
58
  // it guards is same-moment, so a relay restart between the two is not a concern.
58
59
  type SpawnTerminal = "agent.ready" | "agent.spawn_failed";
59
60
  const spawnTerminalBySpawnRequest = new Map<string, SpawnTerminal>();
61
+ const pendingSpawnNotices = new Map<string, Promise<void>>();
62
+ function spawnQueueKey(agentId: string): string {
63
+ const child = getAgent(agentId);
64
+ return (child && spawnRequestIdOf(child)) || agentId;
65
+ }
66
+ // #1870 — terminal guards advance only after commit; serialize one generation across writer awaits.
67
+ function serializeSpawnNotice(key: string, work: () => Promise<void>): Promise<void> {
68
+ const prior = pendingSpawnNotices.get(key);
69
+ const result = prior ? prior.catch(() => {}).then(work) : work();
70
+ pendingSpawnNotices.set(key, result);
71
+ void result.finally(() => { if (pendingSpawnNotices.get(key) === result) pendingSpawnNotices.delete(key); }).catch(() => {});
72
+ return result;
73
+ }
60
74
 
61
75
  // #1513 — spawns we've already told the spawner are queued-for-host-memory, keyed by spawnRequestId
62
76
  // (falling back to the parent id when a request has no id). The orchestrator re-reports the whole
@@ -76,13 +90,6 @@ function recordSpawnTerminal(spawnRequestId: string | undefined, outcome: SpawnT
76
90
  spawnTerminalBySpawnRequest.set(spawnRequestId, outcome);
77
91
  }
78
92
 
79
- function tryRecordSpawnFailed(spawnRequestId: string | undefined): boolean {
80
- if (spawnRequestId === undefined) return true;
81
- if (spawnTerminalBySpawnRequest.has(spawnRequestId)) return false;
82
- spawnTerminalBySpawnRequest.set(spawnRequestId, "agent.spawn_failed");
83
- return true;
84
- }
85
-
86
93
  /** A child counts as "had become ready" once its durable readiness stamp exists. */
87
94
  function everReady(child: AgentCard): boolean {
88
95
  return typeof child.meta?.readyNotifiedAt === "number";
@@ -128,50 +135,49 @@ function stampChildLifecycleSignal(parent: string, child: AgentCard): void {
128
135
  * `spawned_by` parent a one-shot notice so it can stop block-polling. No-op for non-spawned agents
129
136
  * and for a child whose ready was already announced (reconnect / heartbeat re-registration).
130
137
  */
131
- export function notifyAgentReady(childId: string): void {
138
+ export async function notifyAgentReady(childId: string): Promise<void> {
139
+ return serializeSpawnNotice(spawnQueueKey(childId), () => notifyAgentReadyBody(childId));
140
+ }
141
+
142
+ async function notifyAgentReadyBody(childId: string): Promise<void> {
132
143
  const child = getAgent(childId);
133
144
  const parent = child?.spawnedBy;
134
145
  // A workflow step agent with no spawner (a run a dashboard user started) still records its ready edge on its run (#3160).
135
146
  if (!child || (!parent && !workflowRunIdOf(child))) return;
136
147
  if (isSpawnIntentCanceled(child.meta?.spawnRequestId)) return;
137
148
  // Idempotent — only stamped on first ready; classifies a later offline edge as exited.
138
- markAgentReadyNotified(childId);
149
+ await markAgentReadyNotifiedAsync(childId);
139
150
  // #2899 — a self-compact flips ready again; the generation slot keeps the push one-shot.
140
- recordAgentReadyNoticeOnce(child, () => announceReady(child, parent));
151
+ await announceReady(child, parent);
141
152
  }
142
153
 
143
- function announceReady(child: AgentCard, spawner: string | undefined): void {
154
+ async function announceReady(child: AgentCard, spawner: string | undefined): Promise<void> {
144
155
  const childId = child.id;
145
156
  const spawnRequestId = spawnRequestIdOf(child);
146
157
  // #648 — if a spawn_failed was already emitted for this request, this ready RETRACTS it: the agent
147
158
  // actually came up, so the earlier failure was a false negative. Emit a corrective recovery instead
148
159
  // of a plain ready notice so a coordinator that acted on the failure can reconcile.
149
160
  const recovered = spawnTerminalOf(spawnRequestId) === "agent.spawn_failed";
150
- recordSpawnTerminal(spawnRequestId, "agent.ready");
151
161
 
152
162
  const workflowRunId = workflowRunIdOf(child);
153
- emitRelayEvent({
154
- type: "agent.ready",
155
- source: "server",
156
- subject: childId,
157
- data: { agentId: childId, parent: spawner, provider: providerOf(child), label: child.label, spawnRequestId, ...(workflowRunId ? { workflowRunId } : {}) },
158
- });
159
- if (!spawner) return;
160
- const parent = noticeRecipient(child, spawner);
161
-
162
- if (recovered) {
163
- emitRelayEvent({
164
- type: "agent.spawn_recovered",
165
- source: "server",
166
- subject: childId,
167
- data: { agentId: childId, parent: spawner, provider: providerOf(child), label: child.label, spawnRequestId, supersedes: "agent.spawn_failed" },
168
- });
163
+ const readyEvent = () => {
164
+ emitRelayEvent({ type: "agent.ready", source: "server", subject: childId,
165
+ data: { agentId: childId, parent: spawner, provider: providerOf(child), label: child.label, spawnRequestId, ...(workflowRunId ? { workflowRunId } : {}) } });
166
+ if (recovered && spawner) emitRelayEvent({ type: "agent.spawn_recovered", source: "server", subject: childId,
167
+ data: { agentId: childId, parent: spawner, provider: providerOf(child), label: child.label, spawnRequestId, supersedes: "agent.spawn_failed" } });
168
+ };
169
+ const slot = { child, kind: "agent.ready" };
170
+ if (!spawner) {
171
+ const created = await routeAgentNotice(slot);
172
+ recordSpawnTerminal(spawnRequestId, "agent.ready");
173
+ if (created) readyEvent();
174
+ return;
169
175
  }
170
-
176
+ const parent = noticeRecipient(child, spawner);
171
177
  if (recovered) {
172
178
  // A correction must always reach a parent that got the (false) failure — gate only on the global
173
179
  // switch, NOT the agentReady sub-toggle, so the retraction lands even if ready pushes are off.
174
- routeNotification({
180
+ const created = await routeAgentNotice(slot, {
175
181
  type: "agent.spawn_recovered",
176
182
  scope: { agentId: childId, parent },
177
183
  recipients: [parent],
@@ -186,9 +192,11 @@ function announceReady(child: AgentCard, spawner: string | undefined): void {
186
192
  replyExpected: false,
187
193
  },
188
194
  });
195
+ recordSpawnTerminal(spawnRequestId, "agent.ready");
196
+ if (created) readyEvent();
189
197
  return;
190
198
  }
191
- routeNotification({
199
+ const created = await routeAgentNotice(slot, {
192
200
  type: "agent.ready",
193
201
  scope: { agentId: childId, parent },
194
202
  recipients: [parent],
@@ -199,7 +207,8 @@ function announceReady(child: AgentCard, spawner: string | undefined): void {
199
207
  replyExpected: false,
200
208
  },
201
209
  });
202
- stampChildLifecycleSignal(parent, child);
210
+ recordSpawnTerminal(spawnRequestId, "agent.ready");
211
+ if (created) { readyEvent(); stampChildLifecycleSignal(parent, child); }
203
212
  }
204
213
 
205
214
  /**
@@ -209,16 +218,20 @@ function announceReady(child: AgentCard, spawner: string | undefined): void {
209
218
  * No-op for non-spawned agents.
210
219
  * `reason` is the offline cause (e.g. "heartbeat lost", "shutdown").
211
220
  */
212
- export function notifyAgentOffline(childId: string, reason: string, diagnosis?: AgentDeathDiagnosis): void {
213
- requestAgentExitCleanup(childId);
221
+ export async function notifyAgentOffline(childId: string, reason: string, diagnosis?: AgentDeathDiagnosis): Promise<void> {
222
+ return serializeSpawnNotice(spawnQueueKey(childId), () => notifyAgentOfflineBody(childId, reason, diagnosis));
223
+ }
224
+
225
+ async function notifyAgentOfflineBody(childId: string, reason: string, diagnosis?: AgentDeathDiagnosis): Promise<void> {
226
+ (await requestAgentExitCleanup(childId));
214
227
  const child = getAgent(childId);
215
228
  const parent = child?.spawnedBy;
216
229
  if (!child) return;
217
230
  if (!parent) {
218
231
  // A workflow step agent with no spawner records its exit on its run (#3160); nobody else is told.
219
232
  if (workflowRunIdOf(child) && everReady(child)) {
220
- clearAgentNotice(child, "agent.ready");
221
- notifyAgentExit(child, reason, diagnosis);
233
+ await clearAgentNoticeAsync(child, "agent.ready");
234
+ await notifyAgentExit(child, reason, diagnosis);
222
235
  }
223
236
  return;
224
237
  }
@@ -228,38 +241,32 @@ export function notifyAgentOffline(childId: string, reason: string, diagnosis?:
228
241
  // later signal (relay_workspace_ready, task→done) is coming from a process that no longer exists.
229
242
  // Idempotent (flushWorkFinalReports no-ops once the ring is empty), so a redundant call from a
230
243
  // second offline classification of the same agent is harmless.
231
- markAgentWorkFinal(childId, "edge-fallback");
244
+ (await markAgentWorkFinal(childId, "edge-fallback"));
232
245
  // Classify from the durable readiness stamp; re-arm this generation's ready push for a return.
233
246
  const wasReady = everReady(child);
234
- clearAgentNotice(child, "agent.ready");
247
+ await clearAgentNoticeAsync(child, "agent.ready");
235
248
  // #636 — persist the post-mortem keyed by agentId so the WHY survives the reaper, regardless of
236
249
  // whether the parent push is enabled. Recorded once per terminal-event emission.
237
- if (diagnosis) recordCrashReport(crashReportFor(child, reason, diagnosis));
250
+ if (diagnosis) await recordCrashReportAsync(crashReportFor(child, reason, diagnosis));
238
251
  if (hasAgentExitNotice(child)) return;
239
252
 
240
253
  // #645 — persist a terminal marker for ANY offline terminus (clean exit or spawn-fail) keyed by
241
254
  // agentId so it outlives the reaper. The #634 watchdog reads this to skip a benign shutdown; it
242
255
  // must fire ONLY on a genuine silent death that emitted no terminal event at all.
243
- recordTerminalEvent({ agentId: childId, reason, kind: wasReady ? "agent.exited" : "agent.spawn_failed", epoch: child.epoch, createdAt: Date.now() });
256
+ await recordTerminalEventAsync({ agentId: childId, reason, kind: wasReady ? "agent.exited" : "agent.spawn_failed", epoch: child.epoch, createdAt: Date.now() });
244
257
 
245
258
  if (wasReady) {
246
- notifyAgentExit(child, reason, diagnosis);
259
+ await notifyAgentExit(child, reason, diagnosis);
247
260
  return;
248
261
  }
249
262
 
250
- recordAgentNoticeOnce(child, () => {
263
+
251
264
  const recipient = noticeRecipient(child, parent);
252
265
  // #648 — record the terminal outcome so a (defensive) later ready for the same request is detected
253
266
  // as a recovery and corrected, keeping the one-terminal-per-spawn invariant across both fail paths.
254
267
  const spawnRequestId = spawnRequestIdOf(child);
255
- if (!tryRecordSpawnFailed(spawnRequestId)) return;
256
- emitRelayEvent({
257
- type: "agent.spawn_failed",
258
- source: "server",
259
- subject: childId,
260
- data: { agentId: childId, parent, reason, provider: providerOf(child), spawnRequestId, neverReady: true, ...(diagnosis ? { diagnosis } : {}) },
261
- });
262
- routeNotification({
268
+ if (spawnTerminalOf(spawnRequestId)) return;
269
+ const created = await routeAgentNotice({ child, kind: "agent.spawn_failed" }, {
263
270
  type: "agent.spawn_failed",
264
271
  scope: { agentId: childId, parent: recipient },
265
272
  recipients: [recipient],
@@ -274,7 +281,15 @@ export function notifyAgentOffline(childId: string, reason: string, diagnosis?:
274
281
  replyExpected: false,
275
282
  },
276
283
  });
277
- }, "agent.spawn_failed");
284
+ recordSpawnTerminal(spawnRequestId, "agent.spawn_failed");
285
+ if (created) {
286
+ emitRelayEvent({
287
+ type: "agent.spawn_failed",
288
+ source: "server",
289
+ subject: childId,
290
+ data: { agentId: childId, parent, reason, provider: providerOf(child), spawnRequestId, neverReady: true, ...(diagnosis ? { diagnosis } : {}) },
291
+ });
292
+ }
278
293
  }
279
294
 
280
295
  /**
@@ -286,21 +301,19 @@ export function notifyAgentOffline(childId: string, reason: string, diagnosis?:
286
301
  * the subsequent agent.ready / agent.spawn_failed — no separate "admitted" event needed. Non-terminal:
287
302
  * it deliberately does NOT touch the one-terminal-per-spawn guard.
288
303
  */
289
- export function notifyAgentSpawnQueued(input: { parent: string; spawnRequestId?: string; provider?: string; reason: string }): void {
304
+ export async function notifyAgentSpawnQueued(input: { parent: string; spawnRequestId?: string; provider?: string; reason: string }): Promise<void> {
305
+ return serializeSpawnNotice(input.spawnRequestId ?? `parent:${input.parent}`, () => notifyAgentSpawnQueuedBody(input));
306
+ }
307
+
308
+ async function notifyAgentSpawnQueuedBody(input: { parent: string; spawnRequestId?: string; provider?: string; reason: string }): Promise<void> {
290
309
  const dedupeKey = input.spawnRequestId ?? `parent:${input.parent}`;
291
310
  if (spawnQueuedNotified.has(dedupeKey)) return;
292
311
  // A queued spawn whose terminal outcome already landed (a fast admit→ready, or a fail) must not now
293
312
  // emit a stale "queued" notice out of order. Ready/failed is authoritative; suppress a late queue.
294
313
  if (spawnTerminalOf(input.spawnRequestId)) return;
295
- spawnQueuedNotified.add(dedupeKey);
296
- emitRelayEvent({
297
- type: "agent.spawn_queued",
298
- source: "server",
299
- subject: input.spawnRequestId ?? input.parent,
300
- data: { parent: input.parent, spawnRequestId: input.spawnRequestId, provider: input.provider, reason: input.reason },
301
- });
314
+
302
315
  const recipient = spawnerRecipient(input.parent, input.spawnRequestId);
303
- routeNotification({
316
+ await routeNotification({
304
317
  type: "agent.spawn_queued",
305
318
  scope: { parent: recipient },
306
319
  recipients: [recipient],
@@ -314,6 +327,13 @@ export function notifyAgentSpawnQueued(input: { parent: string; spawnRequestId?:
314
327
  replyExpected: false,
315
328
  },
316
329
  });
330
+ spawnQueuedNotified.add(dedupeKey);
331
+ emitRelayEvent({
332
+ type: "agent.spawn_queued",
333
+ source: "server",
334
+ subject: input.spawnRequestId ?? input.parent,
335
+ data: { parent: input.parent, spawnRequestId: input.spawnRequestId, provider: input.provider, reason: input.reason },
336
+ });
317
337
  }
318
338
 
319
339
  /**
@@ -321,18 +341,17 @@ export function notifyAgentSpawnQueued(input: { parent: string; spawnRequestId?:
321
341
  * (provider unavailable, bad cwd, launch error) before any child agent registered. Routes directly
322
342
  * to the spawning parent threaded on the command, since there is no child agent record to resolve.
323
343
  */
324
- export function notifyAgentSpawnFailed(input: { parent: string; spawnRequestId?: string; provider?: string; reason: string }): void {
344
+ export async function notifyAgentSpawnFailed(input: { parent: string; spawnRequestId?: string; provider?: string; reason: string }): Promise<void> {
345
+ return serializeSpawnNotice(input.spawnRequestId ?? `parent:${input.parent}`, () => notifyAgentSpawnFailedBody(input));
346
+ }
347
+
348
+ async function notifyAgentSpawnFailedBody(input: { parent: string; spawnRequestId?: string; provider?: string; reason: string }): Promise<void> {
325
349
  // #648/#864 — if the spawn already reached any terminal outcome, a later command-failure report
326
350
  // for the same request is stale or duplicate. Ready is authoritative; failed is already reported.
327
- if (!tryRecordSpawnFailed(input.spawnRequestId)) return;
328
- emitRelayEvent({
329
- type: "agent.spawn_failed",
330
- source: "server",
331
- subject: input.spawnRequestId ?? input.parent,
332
- data: { parent: input.parent, spawnRequestId: input.spawnRequestId, provider: input.provider, reason: input.reason },
333
- });
351
+ if (spawnTerminalOf(input.spawnRequestId)) return;
352
+
334
353
  const recipient = spawnerRecipient(input.parent, input.spawnRequestId);
335
- routeNotification({
354
+ await routeNotification({
336
355
  type: "agent.spawn_failed",
337
356
  scope: { parent: recipient },
338
357
  recipients: [recipient],
@@ -346,6 +365,13 @@ export function notifyAgentSpawnFailed(input: { parent: string; spawnRequestId?:
346
365
  replyExpected: false,
347
366
  },
348
367
  });
368
+ recordSpawnTerminal(input.spawnRequestId, "agent.spawn_failed");
369
+ emitRelayEvent({
370
+ type: "agent.spawn_failed",
371
+ source: "server",
372
+ subject: input.spawnRequestId ?? input.parent,
373
+ data: { parent: input.parent, spawnRequestId: input.spawnRequestId, provider: input.provider, reason: input.reason },
374
+ });
349
375
  }
350
376
 
351
377
  /**
@@ -353,7 +379,7 @@ export function notifyAgentSpawnFailed(input: { parent: string; spawnRequestId?:
353
379
  * deliberately non-terminal: the runner can still register after the watchdog fires, so callers
354
380
  * must not retry or infer that a new spawn is safe.
355
381
  */
356
- export function notifyAgentSpawnUnconfirmed(input: { parent: string; spawnRequestId: string; provider?: string; reason: string; phase: "dispatch_unacknowledged" | "registration_unconfirmed" }): void {
382
+ export async function notifyAgentSpawnUnconfirmed(input: { parent: string; spawnRequestId: string; provider?: string; reason: string; phase: "dispatch_unacknowledged" | "registration_unconfirmed" }): Promise<void> {
357
383
  emitRelayEvent({
358
384
  type: "agent.spawn_unconfirmed",
359
385
  source: "server",
@@ -361,7 +387,7 @@ export function notifyAgentSpawnUnconfirmed(input: { parent: string; spawnReques
361
387
  data: input,
362
388
  });
363
389
  const recipient = spawnerRecipient(input.parent, input.spawnRequestId);
364
- routeNotification({
390
+ await routeNotification({
365
391
  type: "agent.spawn_unconfirmed",
366
392
  scope: { parent: recipient },
367
393
  recipients: [recipient],
@@ -392,24 +418,18 @@ export function notifyAgentRegistered(agent: AgentCard): void {
392
418
  }
393
419
 
394
420
  /** Emit one parent exit notice per generation, including automatic reaps before the offline report. */
395
- export function notifyAgentExit(child: AgentCard, reason: string, diagnosis?: AgentDeathDiagnosis, context: { workspaceId?: string; workspaceStatus?: string } = {}): void {
421
+ export async function notifyAgentExit(child: AgentCard, reason: string, diagnosis?: AgentDeathDiagnosis, context: { workspaceId?: string; workspaceStatus?: string } = {}): Promise<void> {
396
422
  const parent = child.spawnedBy;
397
423
  const workflowRunId = workflowRunIdOf(child);
398
- recordAgentNoticeOnce(child, () => {
399
- emitRelayEvent({
400
- type: "agent.exited",
401
- source: "server",
402
- subject: child.id,
403
- data: { ...context, agentId: child.id, parent, reason, provider: providerOf(child), spawnRequestId: spawnRequestIdOf(child), ...(workflowRunId ? { workflowRunId } : {}), ...(diagnosis ? { diagnosis } : {}) },
404
- });
424
+
405
425
  // The parent already got a shutdown command receipt for requesting this exit, so the
406
426
  // exit itself is expected — including a non-zero code from a force-reap kill. Keep the
407
427
  // terminal event for timeline consumers; only skip the parent wake-up. An exit nobody
408
428
  // requested (no shutdown command from the parent) still wakes: crashes and spawn failures
409
429
  // keep their notice.
410
- if (parent && parentRequestedShutdown(child.id, spawnRequestIdOf(child) ?? "", parent, Date.now() - 5 * 60_000)) return;
430
+ const suppressed = parent && parentRequestedShutdown(child.id, spawnRequestIdOf(child) ?? "", parent, Date.now() - 5 * 60_000);
411
431
  const recipient = parent ? noticeRecipient(child, parent) : undefined;
412
- routeNotification({
432
+ const created = await routeAgentNotice({ child, kind: "agent.exited" }, suppressed ? undefined : {
413
433
  type: "agent.exited",
414
434
  scope: { agentId: child.id, parent: recipient },
415
435
  recipients: [recipient],
@@ -424,5 +444,12 @@ export function notifyAgentExit(child: AgentCard, reason: string, diagnosis?: Ag
424
444
  replyExpected: false,
425
445
  },
426
446
  });
427
- });
447
+ if (created) {
448
+ emitRelayEvent({
449
+ type: "agent.exited",
450
+ source: "server",
451
+ subject: child.id,
452
+ data: { ...context, agentId: child.id, parent, reason, provider: providerOf(child), spawnRequestId: spawnRequestIdOf(child), ...(workflowRunId ? { workflowRunId } : {}), ...(diagnosis ? { diagnosis } : {}) },
453
+ });
454
+ }
428
455
  }
package/src/agent-ref.ts CHANGED
@@ -160,8 +160,9 @@ export function resolveAgentRef(ref: string, agents: AgentCard[], opts: ResolveO
160
160
  return { status: "not_found", offlineMatches: matches };
161
161
  }
162
162
 
163
- export function targetMatchesAgent(target: string, agentId: string, agent?: AgentCard | null): boolean {
164
- if (!agent) return false;
163
+ export function targetMatchesAgent(target: string | null | undefined, agentId: string, agent?: AgentCard | null): boolean {
164
+ // Untargeted task events have no agent recipient, on live streams or replay (#3801).
165
+ if (!agent || typeof target !== "string" || !target) return false;
165
166
  if (target.startsWith("coordinator:")) return resolveTeamCoordinatorAgentId(target.slice(12)) === agentId;
166
167
  return targetMatchesAgentIdentity(target, {
167
168
  id: agentId,
@@ -1,18 +1,41 @@
1
- // The ONE agent.status emitter (#3606): every seam lands here, so "unchanged card → no event" holds on all.
2
- // A card equal to the last one emitted, ignoring lastSeen and statusSeq, is dropped before persistence:
3
- // liveness ticks only moved those two (39% of prod agent.status events), the dashboard reads heartbeat
4
- // age from its 5–15 s roster poll, and no consumer orders by statusSeq.
1
+ // The ONE agent.status emitter (#3606): every seam lands here. A card unchanged but for lastSeen and
2
+ // statusSeq is dropped (the dashboard reads heartbeat age from its roster poll; nothing orders by
3
+ // statusSeq). #3614 — a change only in liveness telemetry (timestamps, ages, gauges the next tick
4
+ // supersedes) goes out live-only, with no outbox row; replay keeps every real state change.
5
5
 
6
6
  import { getAgentRuntimeAsync } from "./db";
7
7
  import { projectAgentStatusData } from "./agent-card-projection";
8
- import { emitRelayEvent } from "./events";
8
+ import { emitEphemeralRelayEvent, emitRelayEvent } from "./events";
9
9
  import { emitNotificationCreated, notificationForAgentStatus } from "./dashboard-notifications";
10
10
 
11
- const lastEmitted = new Map<string, number | bigint>();
11
+ type Fingerprint = number | bigint;
12
12
 
13
- function cardFingerprint(data: Record<string, unknown>): number | bigint {
14
- const { lastSeen: _lastSeen, statusSeq: _statusSeq, ...card } = data;
15
- return Bun.hash(JSON.stringify(card));
13
+ const UNEMITTED_KEYS = [["lastSeen"], ["statusSeq"]] as const;
14
+ const LIVENESS_KEYS = [
15
+ ...UNEMITTED_KEYS,
16
+ ["context", "lastUpdatedAt"], ["context", "tokensUsed"], ["context", "utilization"],
17
+ ["meta", "unreportedTokenUsage"], ["meta", "lastProviderEventAt"],
18
+ ["meta", "transport", "lastServerFrameAt"], ["meta", "transport", "lastHeartbeatAckAt"],
19
+ ["meta", "liveness", "transportLiveAt"], ["meta", "liveness", "lastProgressAt"],
20
+ ["meta", "busyHealth", "activeWorkAgeMs"], ["meta", "busyHealth", "staleForMs"], ["meta", "busyHealth", "lastProgressAt"],
21
+ ["meta", "busyHealth", "transportLiveAt"], ["meta", "busyHealth", "transportStaleForMs"],
22
+ ] as const;
23
+
24
+ const lastEmitted = new Map<string, { card: Fingerprint; durable: Fingerprint }>();
25
+
26
+ /** Hash of the card without `paths`; copies only the objects on a stripped path. */
27
+ function fingerprint(data: Record<string, unknown>, paths: ReadonlyArray<readonly string[]>): Fingerprint {
28
+ const root = { ...data };
29
+ for (const path of paths) {
30
+ let parent: Record<string, unknown> = root;
31
+ for (const key of path.slice(0, -1)) {
32
+ const child = parent[key];
33
+ if (!child || typeof child !== "object" || Array.isArray(child)) { parent = {}; break; }
34
+ parent = parent[key] = { ...child as Record<string, unknown> };
35
+ }
36
+ delete parent[path[path.length - 1]!];
37
+ }
38
+ return Bun.hash(JSON.stringify(root));
16
39
  }
17
40
 
18
41
  export async function emitAgentStatus(agentId: string): Promise<void> {
@@ -20,15 +43,21 @@ export async function emitAgentStatus(agentId: string): Promise<void> {
20
43
  // Enrich with the branch-state badge fields (#236) and the resolved issueRepo (#533) so
21
44
  // the dashboard's live agent updates carry the same projection as GET /api/agents (#560).
22
45
  const data = await projectAgentStatusData(agentId, agent) as unknown as Record<string, unknown>;
23
- const fingerprint = cardFingerprint(data);
24
- if (lastEmitted.get(agentId) === fingerprint) return;
25
- lastEmitted.set(agentId, fingerprint);
46
+ const next = { card: fingerprint(data, UNEMITTED_KEYS), durable: fingerprint(data, LIVENESS_KEYS) };
47
+ const previous = lastEmitted.get(agentId);
48
+ if (previous?.card === next.card) return;
49
+ lastEmitted.set(agentId, next);
50
+ const event = { type: "agent.status", source: "server", subject: agentId, data };
51
+ if (previous?.durable === next.durable) {
52
+ emitEphemeralRelayEvent(event);
53
+ return;
54
+ }
26
55
  try {
27
56
  // Awaited: a bus registration must not install its socket before its own status event is durable.
28
- await emitRelayEvent({ type: "agent.status", source: "server", subject: agentId, data });
57
+ await emitRelayEvent(event);
29
58
  } catch (error) {
30
59
  // Never let an append that did not happen suppress the next identical card.
31
- if (lastEmitted.get(agentId) === fingerprint) lastEmitted.delete(agentId);
60
+ if (lastEmitted.get(agentId) === next) lastEmitted.delete(agentId);
32
61
  throw error;
33
62
  }
34
63
  const notification = agent ? notificationForAgentStatus(agent) : undefined;
@@ -0,0 +1,35 @@
1
+ // One-time dashboard login links (#2680): `humwick init` hands the first user a signed-in dashboard
2
+ // without an env-file edit or a copied admin token. A link carries a short-lived token whose only
3
+ // scope matches no route, so it cannot act as an access token; redeeming revokes it (single use)
4
+ // and mints a normal user session.
5
+ import { userSinkId, isHumanAgentId, userIdFromAddress, type User } from "@humwick/sdk";
6
+ import { createTokenAsync, revokeTokenAsync } from "../token-db-write.ts";
7
+ import { verifyComponentToken } from "../security.ts";
8
+ import { getUser } from "../db/users.ts";
9
+ import { USER_TOKEN_ROLE } from "./admin-routes.ts";
10
+
11
+ export const LOGIN_LINK_SCOPE = "auth:login-link";
12
+ export const LOGIN_LINK_TTL_SECONDS = 15 * 60;
13
+
14
+ /** Mint a single-use login link for `user`. The path opens the dashboard signed in as them. */
15
+ export async function mintLoginLink(user: User, createdBy: string): Promise<{ path: string; expiresAt: number }> {
16
+ const { token, record } = await createTokenAsync({
17
+ sub: userSinkId(user.id),
18
+ role: USER_TOKEN_ROLE,
19
+ scope: [LOGIN_LINK_SCOPE],
20
+ ttlSeconds: LOGIN_LINK_TTL_SECONDS,
21
+ createdBy,
22
+ });
23
+ return { path: `/login/${token}`, expiresAt: record.expiresAt! };
24
+ }
25
+
26
+ /** The active user a valid, unused login link names, consuming the link; null otherwise. */
27
+ export async function redeemLoginLink(code: string): Promise<User | null> {
28
+ const payload = verifyComponentToken(code);
29
+ if (!payload?.jti || !payload.scope.includes(LOGIN_LINK_SCOPE) || !isHumanAgentId(payload.sub)) return null;
30
+ const userId = userIdFromAddress(payload.sub);
31
+ const user = userId ? getUser(userId) : undefined;
32
+ if (!user || user.status !== "active") return null;
33
+ // The revoke is the single-use gate: only the first redeemer flips the row.
34
+ return (await revokeTokenAsync(payload.jti, "rotation")) ? user : null;
35
+ }
@@ -1,9 +1,10 @@
1
+ import { createAuthIdentityAsync } from "../db/runtime-tail-async";
1
2
  // Password authentication provider (#389). The only built-in provider in Phase 1. Secrets are
2
3
  // hashed with scrypt (node:crypto — no dependency) and compared with `timingSafeEqual`, the same
3
4
  // constant-time primitive the token layer uses. The hash is stored in `auth_identities.secret_hash`
4
5
  // keyed by (provider="password", provider_subject=<username>).
5
6
  import { randomBytes, scrypt, timingSafeEqual } from "node:crypto";
6
- import { createAuthIdentity, getAuthIdentity, type AuthIdentity } from "../db/auth-identities.ts";
7
+ import { getAuthIdentity, type AuthIdentity } from "../db/auth-identities.ts";
7
8
  import { isRecord } from "@humwick/sdk";
8
9
  import type { AuthProvider, AuthVerifyResult } from "./provider.ts";
9
10
 
@@ -98,13 +99,13 @@ export async function createPasswordIdentity(input: {
98
99
  password: string;
99
100
  metadata?: Record<string, unknown>;
100
101
  }): Promise<AuthIdentity> {
101
- return createAuthIdentity({
102
+ return (await createAuthIdentityAsync({
102
103
  userId: input.userId,
103
104
  provider: PASSWORD_PROVIDER_ID,
104
105
  providerSubject: input.username,
105
106
  secretHash: await hashPassword(input.password),
106
107
  metadata: input.metadata,
107
- });
108
+ }));
108
109
  }
109
110
 
110
111
  /** Parse `{ username, password }` from a login request body. */
@@ -31,7 +31,7 @@ export async function refreshUserSession(refreshToken: string, nowMs = Date.now(
31
31
  expiresAt: Math.min(nowMs + REFRESH_GRACE_MS, (resolved.payload.exp ?? Infinity) * 1000),
32
32
  result: (async () => {
33
33
  if (!(await revokeTokenAsync(jti, "rotation"))) return null;
34
- const session = mintUserSession(resolved.user, resolved.payload.constraints?.sessionId);
34
+ const session = (await mintUserSession(resolved.user, resolved.payload.constraints?.sessionId));
35
35
  return { accessJti: session.accessJti, refreshJti: session.refreshJti };
36
36
  })(),
37
37
  };
@@ -4,7 +4,7 @@ import { mintUserSession } from "./session.ts";
4
4
 
5
5
  /** An `authorization` header carrying a fresh access token for `userId`. An unknown id models a
6
6
  * session whose user no longer exists (it carries operator scopes). */
7
- export function sessionAuthHeader(userId: string): Record<string, string> {
7
+ export async function sessionAuthHeader(userId: string): Promise<Record<string, string>> {
8
8
  const user = getUser(userId) ?? { id: userId, handle: userId, displayName: userId, role: "operator" as const, status: "active" as const, tenantId: "default", avatarArtifactId: null, avatarUrl: null, createdAt: 0, updatedAt: 0 };
9
- return { authorization: `Bearer ${mintUserSession(user).accessToken}` };
9
+ return { authorization: `Bearer ${(await mintUserSession(user)).accessToken}` };
10
10
  }
@@ -5,8 +5,8 @@
5
5
  // `role` ("user"), so no machine-token path branches on it.
6
6
  import { userSinkId, userIdFromAddress, isHumanAgentId, type User, type UserRole } from "@humwick/sdk";
7
7
  import { randomUUID } from "node:crypto";
8
- import { revokeTokenAsync } from "../token-db-write.ts";
9
- import { createToken, liveTokenJtisForSession } from "../token-db.ts";
8
+ import { createTokenAsync, revokeTokenAsync } from "../token-db-write.ts";
9
+ import { liveTokenJtisForSession } from "../token-db.ts";
10
10
  import { verifyComponentToken } from "../security.ts";
11
11
  import { getUser } from "../db/users.ts";
12
12
  import { scopesForRole } from "./role-scopes";
@@ -50,25 +50,25 @@ export interface MintedSession {
50
50
  * SaaS seam (identity-only — enforces nothing). Both tokens share the `user:<id>` sub so a single
51
51
  * "sign out everywhere" revoke (by sub) kills the whole session family.
52
52
  */
53
- export function mintUserSession(user: User, sessionId: string = randomUUID()): MintedSession {
53
+ export async function mintUserSession(user: User, sessionId: string = randomUUID()): Promise<MintedSession> {
54
54
  const sub = userSinkId(user.id);
55
55
  const constraints = { sessionId, ...(user.tenantId ? { tenantId: user.tenantId } : {}) };
56
- const access = createToken({
56
+ const access = (await createTokenAsync({
57
57
  sub,
58
58
  role: USER_TOKEN_ROLE,
59
59
  scope: scopesForUserRole(user.role),
60
60
  constraints,
61
61
  ttlSeconds: ACCESS_TTL_SECONDS,
62
62
  createdBy: `auth:login:${user.id}`,
63
- });
64
- const refresh = createToken({
63
+ }));
64
+ const refresh = (await createTokenAsync({
65
65
  sub,
66
66
  role: USER_TOKEN_ROLE,
67
67
  scope: [REFRESH_SCOPE],
68
68
  constraints,
69
69
  ttlSeconds: REFRESH_TTL_SECONDS,
70
70
  createdBy: `auth:refresh:${user.id}`,
71
- });
71
+ }));
72
72
  return {
73
73
  accessToken: access.token,
74
74
  refreshToken: refresh.token,