@humwick/server 0.130.0 → 0.130.2

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 (486) hide show
  1. package/README.md +5 -0
  2. package/docs/guides/tools-administration.md +6 -1
  3. package/docs/guides/tools-coordination.md +1 -0
  4. package/docs/guides/tools-tasks.md +8 -0
  5. package/docs/guides/workflows.md +58 -0
  6. package/docs/openapi.json +414 -1
  7. package/package.json +8 -17
  8. package/public/assets/{MessageBubble-BWpoBjl4.js → MessageBubble-BWLqMGKG.js} +2 -2
  9. package/public/assets/{PlanGraphPanel-C0auck3n.js → PlanGraphPanel-3TlIQpjk.js} +2 -2
  10. package/public/assets/{TraceView-DBnOx7AQ.js → TraceView-Wimm0Czv.js} +2 -2
  11. package/public/assets/{VaultDecisionCard-0kp2oNBC.js → VaultDecisionCard-Bau_vNax.js} +2 -2
  12. package/public/assets/{activity-yLztXEBt.js → activity-CrSurn-r.js} +2 -2
  13. package/public/assets/{agent-lineage-BbJDR1dW.js → agent-lineage-CknAJKcb.js} +2 -2
  14. package/public/assets/agent-liveness-CaNwK0Zf.js +2 -0
  15. package/public/assets/{agent-name-CTZBrSD9.js → agent-name-DKbiIhBo.js} +2 -2
  16. package/public/assets/agent-profiles-DwF6EQ4R.js +2 -0
  17. package/public/assets/{agent-project-badge-9TqzUUYE.js → agent-project-badge-Diz4Sjjb.js} +2 -2
  18. package/public/assets/{agent-tools-D4n40pn8.js → agent-tools-CRhpRQza.js} +2 -2
  19. package/public/assets/{agent-type-icon-D6FUarOf.js → agent-type-icon-LrUEU3lB.js} +2 -2
  20. package/public/assets/{agents-BYDslkJc.js → agents-B-v66C-4.js} +2 -2
  21. package/public/assets/{analytics-B-XSbEMC.js → analytics-BqCb-LKi.js} +2 -2
  22. package/public/assets/{artifact-viewer-DdWIfV6M.js → artifact-viewer-kf4pGuAR.js} +2 -2
  23. package/public/assets/{artifacts-oBOqks-2.js → artifacts-D32zyw4Y.js} +2 -2
  24. package/public/assets/{artifacts-DPN1q4pA.js → artifacts-DJevXcvg.js} +2 -2
  25. package/public/assets/{automation-4KcRFXEu.js → automation-BpRajx_5.js} +2 -2
  26. package/public/assets/{backlog-Cw9xPTCF.js → backlog-D_UjTdzX.js} +2 -2
  27. package/public/assets/{busy-health-CTu32MZt.js → busy-health-N3VEh4ao.js} +2 -2
  28. package/public/assets/{channels-BiXzg1G9.js → channels-9jopungq.js} +2 -2
  29. package/public/assets/{chat-Ctk2qwvA.js → chat-CSXrTB3C.js} +2 -2
  30. package/public/assets/{code-preview-Bm1wWqAp.js → code-preview-p1NnmqK0.js} +2 -2
  31. package/public/assets/{connectors-C36ax4Vw.js → connectors-CZdqlDNL.js} +2 -2
  32. package/public/assets/{cosmos-Jj9LK2Wf.js → cosmos-BgdsE4dG.js} +2 -2
  33. package/public/assets/{crate-agent-H6S7-B9T.js → crate-agent-30aXTKva.js} +2 -2
  34. package/public/assets/{dependency-graph-Cd1E23wa.js → dependency-graph-B_jvw2GE.js} +2 -2
  35. package/public/assets/{deploys-Bwfq146q.js → deploys-DoN6bAb5.js} +2 -2
  36. package/public/assets/{display-CfO7rTuF.js → display-BrKWFGJz.js} +2 -2
  37. package/public/assets/{files-DhhZI2Ih.js → files-D6XEu8Ng.js} +2 -2
  38. package/public/assets/fleet-C7jQWbw2.js +2 -0
  39. package/public/assets/{fleet-metrics-BkHP-P3R.js → fleet-metrics-BiDs3MLx.js} +2 -2
  40. package/public/assets/{formatted-body-Cm1RrzmN.js → formatted-body-CQBwc1fb.js} +3 -3
  41. package/public/assets/{formatted-body-impl-DQiOuOD1.js → formatted-body-impl-DyG0WsQS.js} +2 -2
  42. package/public/assets/{host-providers-dialog-CHqwXTov.js → host-providers-dialog-DKaW31dN.js} +2 -2
  43. package/public/assets/index-CRHep3M9.js +15 -0
  44. package/public/assets/index-MYAIwYTX.css +2 -0
  45. package/public/assets/{insights-fH1EL-qZ.js → insights-CzrtkpOE.js} +2 -2
  46. package/public/assets/{integrations-C5Oix6ng.js → integrations-aVsUmigt.js} +2 -2
  47. package/public/assets/{issue-ref-chip-ODyondIa.js → issue-ref-chip-BnSlM_95.js} +2 -2
  48. package/public/assets/{log-viewer-Cvfy6PW7.js → log-viewer-qe7Ee47Z.js} +2 -2
  49. package/public/assets/{maintenance-D8CDH5wZ.js → maintenance-NfjqFZAk.js} +2 -2
  50. package/public/assets/{managed-agents-egllbpij.js → managed-agents-BiH0HgaE.js} +2 -2
  51. package/public/assets/{markdown-preview-CYSFgVgK.js → markdown-preview-BixaT3Xr.js} +3 -3
  52. package/public/assets/{markdown-preview-impl-BteVIjX4.js → markdown-preview-impl-DNGlWIS3.js} +2 -2
  53. package/public/assets/{memory-C8WmXsyr.js → memory-DNsCl6UX.js} +2 -2
  54. package/public/assets/{messages-BzQdqlO6.js → messages-DopU1L1X.js} +2 -2
  55. package/public/assets/{orchestrators-B1HxzkNw.js → orchestrators-Cq7ckLQT.js} +2 -2
  56. package/public/assets/{overview-DCUnI4Z7.js → overview-C1DCTzzV.js} +2 -2
  57. package/public/assets/{pairs-ftCd2XcD.js → pairs-DE_m6rrE.js} +2 -2
  58. package/public/assets/{profile-icon-B2zH8DMH.js → profile-icon-B3rfh77F.js} +2 -2
  59. package/public/assets/project-profile-instructions-DQWZE0kA.js +2 -0
  60. package/public/assets/projects-Br2Bd2h1.js +2 -0
  61. package/public/assets/{prompt-settings-Bftq9czX.js → prompt-settings-BKvFsvGD.js} +2 -2
  62. package/public/assets/provider-display-DsuWMvVL.js +2 -0
  63. package/public/assets/{registry-DWCU5c8q.js → registry-Cw38_6gW.js} +2 -2
  64. package/public/assets/run-card-CpKqRbhg.js +2 -0
  65. package/public/assets/scroll-text-BKXS1ahr.js +2 -0
  66. package/public/assets/{secrets-C7GIUATz.js → secrets-DxI-vzSq.js} +2 -2
  67. package/public/assets/{service-access-settings-C3aujqTG.js → service-access-settings-D9s_VJbd.js} +2 -2
  68. package/public/assets/{settings-C6sZ9eBa.js → settings-CKZTVGIx.js} +2 -2
  69. package/public/assets/{shipping-Bu8p1gEw.js → shipping-B9YtnwtG.js} +2 -2
  70. package/public/assets/{stats-V9Z-GxUd.js → stats-DyUBKyWI.js} +2 -2
  71. package/public/assets/{status-dot-CVv0iGHL.js → status-dot-DYqlEUhJ.js} +2 -2
  72. package/public/assets/{store-BvtFdlGX.js → store-DcClXLH0.js} +3 -3
  73. package/public/assets/{tasks-CXU75HRF.js → tasks-Bh7Chr4-.js} +2 -2
  74. package/public/assets/{teams-BPf976zb.js → teams-bowI7qVY.js} +2 -2
  75. package/public/assets/{terminal-viewer-DE9BOe-8.js → terminal-viewer-BgVLHdsU.js} +3 -3
  76. package/public/assets/{terminal-viewer-impl-D04pjmGh.js → terminal-viewer-impl-Dz22UPy6.js} +2 -2
  77. package/public/assets/token-estimate-Co-Jdphh.js +2 -0
  78. package/public/assets/{use-agent-terminal-B1yR456R.js → use-agent-terminal-DSQQ-iY7.js} +2 -2
  79. package/public/assets/{use-decisions-D-S4tpDf.js → use-decisions-samMQhQf.js} +2 -2
  80. package/public/assets/use-project-list-59LheVP7.js +2 -0
  81. package/public/assets/{use-selectors-CnrAk2YP.js → use-selectors-BZa25xEB.js} +2 -2
  82. package/public/assets/{user-avatar-nBFIcR4M.js → user-avatar-DHxlvUvV.js} +2 -2
  83. package/public/assets/versioned-prompt-editor-BmED5DnJ.js +8 -0
  84. package/public/assets/{work-queue-TCjJxeT-.js → work-queue-CSvpuNLz.js} +2 -2
  85. package/public/assets/{workflow-marker-Cf3Hf2fN.js → workflow-marker-DAZcj5qt.js} +2 -2
  86. package/public/assets/{workflows-CXQX20Qj.js → workflows-DdmxRSyg.js} +2 -2
  87. package/public/assets/{workspaces-qSYzKYMA.js → workspaces-BxeKfVuF.js} +2 -2
  88. package/public/cosmos.html +4 -4
  89. package/public/index.html +31 -29
  90. package/runner/plugins/claude/.claude-plugin/plugin.json +1 -1
  91. package/runner/plugins/claude/hooks/gate-run-guard.provisioned.mjs +31 -1
  92. package/runner/src/adapters/claude-effort.ts +2 -4
  93. package/runner/src/adapters/codex-launch-config.ts +4 -5
  94. package/runner/src/adapters/codex.ts +1 -1
  95. package/runner/src/adapters/opencode-provider.ts +7 -22
  96. package/runner/src/adapters/opencode.ts +4 -4
  97. package/runner/src/agent-runner/credentials.ts +42 -12
  98. package/runner/src/agent-runner/inbox.ts +27 -0
  99. package/runner/src/agent-runner/lifecycle-milestones.ts +0 -2
  100. package/runner/src/bin/claude.ts +4 -0
  101. package/runner/src/bin/codex.ts +4 -0
  102. package/runner/src/bin/opencode.ts +4 -0
  103. package/runner/src/index.ts +5 -2
  104. package/runner/src/provider-setup.ts +34 -0
  105. package/runner/src/providers.ts +2 -1
  106. package/runner/src/runner-core.ts +18 -23
  107. package/runner/src/spawn-input.ts +2 -1
  108. package/runner/src/test-run-dedup.ts +59 -56
  109. package/scripts/cd-config.ts +24 -3
  110. package/scripts/gate-run-lease.ts +10 -7
  111. package/scripts/gate.sh +5 -1
  112. package/src/branch-land-notice.ts +93 -0
  113. package/src/branch-landed.ts +7 -76
  114. package/src/bus.ts +19 -20
  115. package/src/cd-deploy-notify.ts +30 -2
  116. package/src/cd-host-lag-notify.ts +7 -6
  117. package/src/ci/incidents.ts +2 -2
  118. package/src/ci/monitor.ts +5 -8
  119. package/src/ci/root-missing.ts +2 -7
  120. package/src/ci/transition-notify.ts +7 -39
  121. package/src/cli/cd-pauses.ts +19 -0
  122. package/src/cli/orchestrator.ts +1 -1
  123. package/src/cli/state.ts +2 -19
  124. package/src/cli/task-edit.ts +22 -0
  125. package/src/cli/task.ts +3 -1
  126. package/src/cli/workflow.ts +8 -2
  127. package/src/commands-db.ts +10 -6
  128. package/src/commands-write.ts +8 -0
  129. package/src/config-store-write.ts +13 -1
  130. package/src/config-store.ts +25 -14
  131. package/src/db/activity-async.ts +6 -1
  132. package/src/db/activity-write-ops.ts +13 -1
  133. package/src/db/activity.ts +1 -14
  134. package/src/db/agent-lifecycle-write-ops.ts +1 -0
  135. package/src/db/agent-meta.ts +2 -2
  136. package/src/db/agent-ownership-write-ops.ts +4 -0
  137. package/src/db/agent-search.ts +1 -15
  138. package/src/db/agents-async.ts +58 -7
  139. package/src/db/agents-write-ops.ts +28 -5
  140. package/src/db/agents.ts +81 -54
  141. package/src/db/artifacts-write-ops.ts +8 -3
  142. package/src/db/bus-outbox-write-ops.ts +1 -0
  143. package/src/db/channels-write-ops.ts +4 -0
  144. package/src/db/ci-incidents-async.ts +5 -5
  145. package/src/db/ci-incidents-write-ops.ts +2 -2
  146. package/src/db/claude-api-budget-ops.ts +5 -5
  147. package/src/db/claude-app-oauth.ts +4 -4
  148. package/src/db/codex-reset-consumes-write-ops.ts +3 -0
  149. package/src/db/command-row.ts +10 -2
  150. package/src/db/commands-list-query.ts +2 -0
  151. package/src/db/commands-spawn-read.ts +1 -1
  152. package/src/db/commands-write-ops.ts +21 -0
  153. package/src/db/config-store-write-ops.ts +9 -2
  154. package/src/db/continuation-archives-async.ts +5 -0
  155. package/src/db/continuation-archives-write-ops.ts +9 -0
  156. package/src/db/continuations.ts +2 -2
  157. package/src/db/db-writer-worker.ts +8 -1
  158. package/src/db/delivery-write-ops.ts +5 -0
  159. package/src/db/deploy-events-async.ts +1 -0
  160. package/src/db/deploy-events-read-ops.ts +2 -1
  161. package/src/db/deploy-events.ts +35 -4
  162. package/src/db/hosts-registration.ts +10 -2
  163. package/src/db/hosts-write-ops.ts +14 -0
  164. package/src/db/hosts.ts +9 -6
  165. package/src/db/inbox-write-ops.ts +4 -0
  166. package/src/db/inbox.ts +0 -14
  167. package/src/db/index.ts +2 -0
  168. package/src/db/insights-write-ops.ts +1 -0
  169. package/src/db/integrations-async.ts +8 -0
  170. package/src/db/integrations-ops.ts +15 -0
  171. package/src/db/integrations.ts +15 -21
  172. package/src/db/issue-lifecycle.ts +3 -4
  173. package/src/db/issue-refs-async.ts +20 -0
  174. package/src/db/issue-refs-write-ops.ts +29 -0
  175. package/src/db/issue-refs.ts +7 -8
  176. package/src/db/long-running-work.ts +2 -2
  177. package/src/db/macos-privacy.ts +1 -1
  178. package/src/db/main-connection-writes.ts +23 -2
  179. package/src/db/maintenance-job-reconcile.ts +61 -0
  180. package/src/db/maintenance-job-runs-async.ts +14 -0
  181. package/src/db/maintenance-job-write-ops.ts +9 -0
  182. package/src/db/mappers.ts +2 -14
  183. package/src/db/merge-lease.ts +1 -15
  184. package/src/db/message-reads-write-ops.ts +3 -0
  185. package/src/db/message-write-ops.ts +4 -0
  186. package/src/db/migrations.ts +7 -15
  187. package/src/db/orchestrators-async.ts +7 -3
  188. package/src/db/orchestrators-write-ops.ts +7 -1
  189. package/src/db/orchestrators.ts +21 -77
  190. package/src/db/pairs.ts +1 -13
  191. package/src/db/project-profile-instructions.ts +50 -0
  192. package/src/db/prompt-versions-ops.ts +1 -0
  193. package/src/db/prompt-versions.ts +9 -2
  194. package/src/db/provider-catalog-write-ops.ts +4 -4
  195. package/src/db/provider-config-history.ts +8 -3
  196. package/src/db/provider-flap-messages-write-ops.ts +1 -0
  197. package/src/db/provider-model-seed.ts +34 -0
  198. package/src/db/provider-quota-lease-write-ops.ts +1 -0
  199. package/src/db/provider-quota-write-ops.ts +2 -0
  200. package/src/db/provisioning-write-ops.ts +9 -8
  201. package/src/db/read-op-marshaling-fixtures.ts +5 -0
  202. package/src/db/read-ops.ts +4 -0
  203. package/src/db/routing-policy.ts +3 -4
  204. package/src/db/schema.ts +9 -3
  205. package/src/db/secrets-broker-write-ops.ts +5 -5
  206. package/src/db/secrets-vault-write.ts +7 -7
  207. package/src/db/services-write-ops.ts +3 -0
  208. package/src/db/spawn-delegation-state.ts +11 -4
  209. package/src/db/spawn-tree-cleanup.ts +3 -0
  210. package/src/db/stats-async.ts +4 -3
  211. package/src/db/task-entity.ts +23 -2
  212. package/src/db/task-list.ts +3 -1
  213. package/src/db/tasks.ts +1 -17
  214. package/src/db/team-spawn-delegation.ts +1 -1
  215. package/src/db/teams-write-ops.ts +4 -4
  216. package/src/db/timer-cutover-write-ops.ts +7 -0
  217. package/src/db/timer-ingress-write-ops.ts +9 -0
  218. package/src/db/timer-ingress.ts +7 -1
  219. package/src/db/timer-operator-write-ops.ts +14 -0
  220. package/src/db/timer-transfer-write-ops.ts +4 -0
  221. package/src/db/timer-write-ops.ts +14 -0
  222. package/src/db/token-db-write-ops.ts +21 -0
  223. package/src/db/token-standing.ts +15 -11
  224. package/src/db/user-settings-write-ops.ts +1 -0
  225. package/src/db/vault-backup.ts +2 -2
  226. package/src/db/vault-expiry.ts +2 -2
  227. package/src/db/vault-health.ts +3 -3
  228. package/src/db/vault-token-ops.ts +8 -8
  229. package/src/db/web-tool-call-ops.ts +3 -3
  230. package/src/db/workflow-contact.ts +1 -1
  231. package/src/db/workflow-task-read-ops.ts +30 -0
  232. package/src/db/write-ops.ts +15 -1
  233. package/src/db/writer-fault-seam.ts +5 -2
  234. package/src/db/writer-gateway.ts +21 -12
  235. package/src/dispatch-availability.ts +10 -6
  236. package/src/ephemeral/{landing-ingress-proof-worker.ts → landing-ingress-proof.test-fixture.ts} +4 -2
  237. package/src/host/disk-alert.ts +121 -0
  238. package/src/lifecycle-manager.ts +3 -2
  239. package/src/maintenance/cd-watcher.ts +3 -3
  240. package/src/maintenance/critical-job-watchdog.ts +16 -17
  241. package/src/maintenance/index.ts +1 -0
  242. package/src/maintenance/jobs.ts +3 -2
  243. package/src/maintenance/scheduler.ts +84 -128
  244. package/src/maintenance/stalled-delivery-pager.ts +6 -5
  245. package/src/maintenance/workspaces.ts +1 -1
  246. package/src/managed-policy.ts +31 -27
  247. package/src/mcp/index.ts +5 -2
  248. package/src/mcp/output-descriptions.ts +8 -8
  249. package/src/mcp/presentation.ts +4 -4
  250. package/src/mcp/project-profile-instructions.ts +32 -0
  251. package/src/mcp/tool-groups.ts +1 -1
  252. package/src/mcp/tools-spawn.ts +4 -3
  253. package/src/mcp-agent-action.ts +1 -2
  254. package/src/mcp-host-tools.ts +1 -1
  255. package/src/mcp-issue-ref-tools.ts +12 -11
  256. package/src/mcp-maintenance-tools.ts +2 -2
  257. package/src/mcp-profile-trust-tools.ts +4 -4
  258. package/src/mcp-provider-catalog.ts +2 -2
  259. package/src/mcp-task-tools.ts +11 -5
  260. package/src/mcp-team-template-tools.ts +3 -3
  261. package/src/mcp-workflow-tools.ts +10 -3
  262. package/src/notification-types.ts +2 -1
  263. package/src/operator-alarm.ts +26 -11
  264. package/src/package-pack.ts +42 -39
  265. package/src/project-instructions.ts +4 -0
  266. package/src/project-profile-instruction-text.ts +6 -0
  267. package/src/project-profile-instructions.ts +20 -0
  268. package/src/provider-catalog-store.ts +3 -49
  269. package/src/provider-config-store.ts +12 -11
  270. package/src/provider-model-seed.ts +1 -18
  271. package/src/provider-quota-config-store.ts +5 -4
  272. package/src/provider-secrets.test-fixture.ts +3 -3
  273. package/src/recipe-runner.ts +2 -2
  274. package/src/repo-config-source.ts +10 -4
  275. package/src/routes/agent-profiles.ts +6 -6
  276. package/src/routes/agents-spawn.ts +1 -1
  277. package/src/routes/agents.ts +6 -6
  278. package/src/routes/commands-listing.ts +4 -0
  279. package/src/routes/commands.ts +2 -2
  280. package/src/routes/continuation-archives.ts +2 -2
  281. package/src/routes/host-resource-control.ts +4 -1
  282. package/src/routes/index.ts +10 -1
  283. package/src/routes/integrations.ts +24 -50
  284. package/src/routes/issue-refs.ts +7 -7
  285. package/src/routes/orchestrator-bootstrap.ts +3 -3
  286. package/src/routes/orchestrator-host-registration.ts +2 -2
  287. package/src/routes/orchestrator-host-utilization.ts +3 -1
  288. package/src/routes/orchestrator-proxy.ts +2 -2
  289. package/src/routes/orchestrator.ts +4 -4
  290. package/src/routes/project-profile-instructions.ts +27 -0
  291. package/src/routes/project-trust.ts +2 -2
  292. package/src/routes/provider-config.ts +9 -8
  293. package/src/routes/provider-quota-config.ts +2 -2
  294. package/src/routes/settings.ts +5 -5
  295. package/src/routes/stats.ts +6 -6
  296. package/src/routes/task-http.ts +15 -1
  297. package/src/routes/task-issues.ts +11 -26
  298. package/src/routes/tasks.ts +22 -53
  299. package/src/routes/team-templates.ts +5 -5
  300. package/src/routes/teams-config.ts +2 -2
  301. package/src/routes/tokens.ts +12 -5
  302. package/src/routes/workflows.ts +20 -5
  303. package/src/routing-policy-normalize.ts +1 -2
  304. package/src/routing-step-defaults.ts +0 -2
  305. package/src/runner-token-mint-limit.ts +63 -0
  306. package/src/runtime-grant.ts +4 -2
  307. package/src/runtime-tokens.ts +50 -9
  308. package/src/security.ts +13 -20
  309. package/src/server-background.ts +1 -1
  310. package/src/server.ts +5 -2
  311. package/src/services/host-utilization.ts +23 -0
  312. package/src/services/idle-epics.ts +5 -6
  313. package/src/services/issue-lifecycle.ts +57 -61
  314. package/src/services/issue-refs.ts +9 -13
  315. package/src/services/kernel-timers-cutover-chaos-worker.ts +12 -7
  316. package/src/services/kernel-timers-ingress.ts +1 -1
  317. package/src/services/kernel-timers-request-key-migration-chaos-worker.ts +4 -1
  318. package/src/services/landed-ancestry.ts +19 -3
  319. package/src/services/memory-areas.ts +15 -51
  320. package/src/services/plan-ci-notice.ts +57 -0
  321. package/src/services/plan-ci-recovery.ts +2 -55
  322. package/src/services/plan-wake-digest.ts +1 -0
  323. package/src/services/profile-trust.ts +4 -3
  324. package/src/services/project-config-read.ts +70 -0
  325. package/src/services/project-profile-instructions.ts +54 -0
  326. package/src/services/provisioning-harness/budget-baseline.json +4 -4
  327. package/src/services/restart-spawn.ts +61 -49
  328. package/src/services/self-care-state.ts +2 -1
  329. package/src/services/self-effort.ts +5 -5
  330. package/src/services/send-message.ts +5 -1
  331. package/src/services/spawn-agent.ts +13 -2
  332. package/src/services/spawn-chain-inspector.ts +6 -13
  333. package/src/services/spawn-policy.ts +68 -0
  334. package/src/services/spawn-tree-cleanup.ts +2 -2
  335. package/src/services/task-dispatch.ts +3 -0
  336. package/src/services/task-state-reconcile.ts +6 -14
  337. package/src/services/team-templates.ts +9 -8
  338. package/src/services/transient-agent-reaper.ts +3 -1
  339. package/src/services/workflow-auto-start.ts +25 -13
  340. package/src/services/workflow-ci-main.ts +1 -1
  341. package/src/services/workflow-deferred-cleanup.ts +4 -2
  342. package/src/services/workflow-effect-driver.ts +2 -0
  343. package/src/services/workflow-effects.ts +18 -11
  344. package/src/services/workflow-land-notice.ts +10 -0
  345. package/src/services/workflow-pause-text.ts +43 -0
  346. package/src/services/workflow-reconcile.ts +13 -11
  347. package/src/services/workflow-run-view.ts +2 -0
  348. package/src/services/workflow-runs.ts +29 -13
  349. package/src/services/workflow-supervisor-state.ts +5 -0
  350. package/src/services/workflow-supervisor.ts +7 -3
  351. package/src/settings/registry.ts +1 -2
  352. package/src/spawn-command.ts +4 -4
  353. package/src/spawn-models.ts +10 -12
  354. package/src/spawn-placement.ts +1 -1
  355. package/src/spawn-targets.ts +1 -1
  356. package/src/startup-context-injection.ts +1 -0
  357. package/src/state-adoption/cd-cutover.ts +53 -7
  358. package/src/state-adoption/fleet-cutover.ts +35 -0
  359. package/src/state-adoption/fleet-plan.ts +36 -0
  360. package/src/state-adoption/index.ts +2 -2
  361. package/src/state-adoption/linux-plan.ts +2 -2
  362. package/src/state-adoption/linux.ts +2 -2
  363. package/src/state-adoption/macos-plan.ts +2 -2
  364. package/src/state-adoption/macos.ts +1 -1
  365. package/src/steward.ts +7 -6
  366. package/src/task-dispatch-live.ts +1 -1
  367. package/src/task-dispatcher.ts +1 -1
  368. package/src/teams-config-store.ts +4 -3
  369. package/src/token-db-write.ts +10 -0
  370. package/src/workspace-command-settle.ts +2 -1
  371. package/src/workspace-steward-wake.ts +2 -2
  372. package/node_modules/@humwick/kernel/LICENSE +0 -167
  373. package/node_modules/@humwick/kernel/async-entity-persistence.ts +0 -344
  374. package/node_modules/@humwick/kernel/async-event-log.ts +0 -107
  375. package/node_modules/@humwick/kernel/automations.pilot.ts +0 -147
  376. package/node_modules/@humwick/kernel/config.ts +0 -111
  377. package/node_modules/@humwick/kernel/contract.ts +0 -572
  378. package/node_modules/@humwick/kernel/control-authz.ts +0 -281
  379. package/node_modules/@humwick/kernel/control.ts +0 -645
  380. package/node_modules/@humwick/kernel/effects.ts +0 -493
  381. package/node_modules/@humwick/kernel/event-log.ts +0 -319
  382. package/node_modules/@humwick/kernel/http-transport.ts +0 -384
  383. package/node_modules/@humwick/kernel/identity-grammar.ts +0 -20
  384. package/node_modules/@humwick/kernel/identity.ts +0 -248
  385. package/node_modules/@humwick/kernel/index.ts +0 -28
  386. package/node_modules/@humwick/kernel/kernel-persistence.ts +0 -181
  387. package/node_modules/@humwick/kernel/landing-incident.ts +0 -306
  388. package/node_modules/@humwick/kernel/landing.ts +0 -953
  389. package/node_modules/@humwick/kernel/manifest-validation.ts +0 -460
  390. package/node_modules/@humwick/kernel/mcp-transport.ts +0 -109
  391. package/node_modules/@humwick/kernel/message-authority-chaos-worker.ts +0 -200
  392. package/node_modules/@humwick/kernel/message-authority.ts +0 -281
  393. package/node_modules/@humwick/kernel/module-host.ts +0 -722
  394. package/node_modules/@humwick/kernel/openapi-init.ts +0 -14
  395. package/node_modules/@humwick/kernel/package.json +0 -47
  396. package/node_modules/@humwick/kernel/persistence-schema.ts +0 -248
  397. package/node_modules/@humwick/kernel/persistence.ts +0 -510
  398. package/node_modules/@humwick/kernel/registry.ts +0 -60
  399. package/node_modules/@humwick/kernel/sqlite-kernel-persistence.ts +0 -499
  400. package/node_modules/@humwick/kernel/sqlite-module-host.ts +0 -87
  401. package/node_modules/@humwick/kernel/timers-chaos-worker.ts +0 -186
  402. package/node_modules/@humwick/kernel/timers.pilot.ts +0 -1411
  403. package/node_modules/@humwick/kernel/workflow-activation.ts +0 -25
  404. package/node_modules/@humwick/kernel/workflow-backstop.ts +0 -40
  405. package/node_modules/@humwick/kernel/workflow-cancel-tree.ts +0 -20
  406. package/node_modules/@humwick/kernel/workflow-chaos-worker.ts +0 -213
  407. package/node_modules/@humwick/kernel/workflow-composition.ts +0 -231
  408. package/node_modules/@humwick/kernel/workflow-container.ts +0 -72
  409. package/node_modules/@humwick/kernel/workflow-control.test-fixture.ts +0 -42
  410. package/node_modules/@humwick/kernel/workflow-control.ts +0 -178
  411. package/node_modules/@humwick/kernel/workflow-definition.ts +0 -507
  412. package/node_modules/@humwick/kernel/workflow-dispatch.ts +0 -60
  413. package/node_modules/@humwick/kernel/workflow-foreach.ts +0 -167
  414. package/node_modules/@humwick/kernel/workflow-journal-codec.ts +0 -339
  415. package/node_modules/@humwick/kernel/workflow-journal.ts +0 -922
  416. package/node_modules/@humwick/kernel/workflow-lane.ts +0 -48
  417. package/node_modules/@humwick/kernel/workflow-lease-owner.ts +0 -60
  418. package/node_modules/@humwick/kernel/workflow-recovery.test-fixture.ts +0 -19
  419. package/node_modules/@humwick/kernel/workflow-runtime.ts +0 -938
  420. package/node_modules/@humwick/kernel/workflow-script.ts +0 -11
  421. package/node_modules/@humwick/kernel/workflow-state.ts +0 -299
  422. package/node_modules/@humwick/kernel/workflow-step-commit.ts +0 -195
  423. package/node_modules/@humwick/kernel/workflow-transitions.ts +0 -317
  424. package/node_modules/@humwick/kernel/workflow-values.ts +0 -153
  425. package/node_modules/@humwick/kernel/workflow.pilot.ts +0 -376
  426. package/node_modules/@humwick/offload/LICENSE +0 -167
  427. package/node_modules/@humwick/offload/README.md +0 -57
  428. package/node_modules/@humwick/offload/dist/async-runner.d.ts +0 -18
  429. package/node_modules/@humwick/offload/dist/async-runner.d.ts.map +0 -1
  430. package/node_modules/@humwick/offload/dist/async-runner.js +0 -158
  431. package/node_modules/@humwick/offload/dist/async-runner.js.map +0 -1
  432. package/node_modules/@humwick/offload/dist/envelope.d.ts +0 -25
  433. package/node_modules/@humwick/offload/dist/envelope.d.ts.map +0 -1
  434. package/node_modules/@humwick/offload/dist/envelope.js +0 -80
  435. package/node_modules/@humwick/offload/dist/envelope.js.map +0 -1
  436. package/node_modules/@humwick/offload/dist/fixtures/worker-task.d.ts +0 -10
  437. package/node_modules/@humwick/offload/dist/fixtures/worker-task.d.ts.map +0 -1
  438. package/node_modules/@humwick/offload/dist/fixtures/worker-task.js +0 -59
  439. package/node_modules/@humwick/offload/dist/fixtures/worker-task.js.map +0 -1
  440. package/node_modules/@humwick/offload/dist/index.d.ts +0 -5
  441. package/node_modules/@humwick/offload/dist/index.d.ts.map +0 -1
  442. package/node_modules/@humwick/offload/dist/index.js +0 -5
  443. package/node_modules/@humwick/offload/dist/index.js.map +0 -1
  444. package/node_modules/@humwick/offload/dist/subprocess-runner.d.ts +0 -62
  445. package/node_modules/@humwick/offload/dist/subprocess-runner.d.ts.map +0 -1
  446. package/node_modules/@humwick/offload/dist/subprocess-runner.js +0 -295
  447. package/node_modules/@humwick/offload/dist/subprocess-runner.js.map +0 -1
  448. package/node_modules/@humwick/offload/dist/types.d.ts +0 -111
  449. package/node_modules/@humwick/offload/dist/types.d.ts.map +0 -1
  450. package/node_modules/@humwick/offload/dist/types.js +0 -176
  451. package/node_modules/@humwick/offload/dist/types.js.map +0 -1
  452. package/node_modules/@humwick/offload/dist/worker-pool.d.ts +0 -23
  453. package/node_modules/@humwick/offload/dist/worker-pool.d.ts.map +0 -1
  454. package/node_modules/@humwick/offload/dist/worker-pool.js +0 -435
  455. package/node_modules/@humwick/offload/dist/worker-pool.js.map +0 -1
  456. package/node_modules/@humwick/offload/dist/worker-runtime.d.ts +0 -2
  457. package/node_modules/@humwick/offload/dist/worker-runtime.d.ts.map +0 -1
  458. package/node_modules/@humwick/offload/dist/worker-runtime.js +0 -27
  459. package/node_modules/@humwick/offload/dist/worker-runtime.js.map +0 -1
  460. package/node_modules/@humwick/offload/dist/worker.d.ts +0 -5
  461. package/node_modules/@humwick/offload/dist/worker.d.ts.map +0 -1
  462. package/node_modules/@humwick/offload/dist/worker.js +0 -1
  463. package/node_modules/@humwick/offload/dist/worker.js.map +0 -1
  464. package/node_modules/@humwick/offload/package.json +0 -46
  465. package/node_modules/@humwick/offload/src/async-runner.ts +0 -170
  466. package/node_modules/@humwick/offload/src/envelope.ts +0 -92
  467. package/node_modules/@humwick/offload/src/fixtures/worker-task.ts +0 -49
  468. package/node_modules/@humwick/offload/src/index.ts +0 -4
  469. package/node_modules/@humwick/offload/src/subprocess-runner.ts +0 -334
  470. package/node_modules/@humwick/offload/src/types.ts +0 -235
  471. package/node_modules/@humwick/offload/src/worker-pool.ts +0 -463
  472. package/node_modules/@humwick/offload/src/worker-runtime.ts +0 -32
  473. package/node_modules/@humwick/offload/src/worker.ts +0 -5
  474. package/public/assets/agent-liveness-DyqxfB-t.js +0 -2
  475. package/public/assets/agent-profiles-BpI_jTxq.js +0 -2
  476. package/public/assets/fleet-BL4UXS_k.js +0 -2
  477. package/public/assets/index-B7ZerHLY.css +0 -2
  478. package/public/assets/index-BK0WV6hF.js +0 -15
  479. package/public/assets/projects-B_lmcqfO.js +0 -2
  480. package/public/assets/provider-display-DbLWpP3j.js +0 -2
  481. package/public/assets/run-card-CO_bguQf.js +0 -2
  482. package/public/assets/use-project-list-B-T2Bnnn.js +0 -2
  483. package/public/assets/versioned-prompt-editor-Diu8BIaM.js +0 -8
  484. package/runner/src/gate-run-lease-probe.ts +0 -38
  485. package/src/db/claude-catalog-reconcile.ts +0 -27
  486. package/src/routing-model-aliases.ts +0 -21
@@ -1,167 +0,0 @@
1
- import { z } from "zod";
2
- import { OpError, type ModuleCtx } from "./contract";
3
- import { actionKind } from "./workflow-lane";
4
- import { parkDecision, rootRun, valueContext } from "./workflow-composition";
5
- import { itemResult, settleDependents } from "./workflow-container";
6
- import { waitTimeoutMs } from "./workflow-definition";
7
- import { workflowActivationSchema, type WorkflowActivation } from "./workflow-activation";
8
- import { ADMIN_GRANT, SIGNAL_TOPIC, stores, requireRun, usedEffects, type WorkflowRunRecord } from "./workflow-state";
9
-
10
- async function continueAncestors(ctx: ModuleCtx, run: WorkflowRunRecord | undefined, generation: number): Promise<void> {
11
- while (run) {
12
- await stores(ctx).run.update(run.id, { status: "running", reason: undefined, escalation: undefined, decisionChildRunId: undefined, generation });
13
- if (run.activationId) await stores(ctx).activation.update(run.activationId, { state: "waiting", error: undefined, generation });
14
- run = run.parentRunId ? await requireRun(ctx, run.parentRunId) : undefined;
15
- }
16
- }
17
-
18
- export async function resumeContainer(ctx: ModuleCtx, run: WorkflowRunRecord): Promise<boolean> {
19
- if (run.status !== "escalated" || run.awaiting?.kind !== "for-each") return false;
20
- const root = await rootRun(ctx, run);
21
- const deadline = ctx.clock.now() + waitTimeoutMs(run.definition, run.step);
22
- await stores(ctx).run.update(run.id, { awaiting: { ...run.awaiting, deadline } });
23
- await stores(ctx).activation.update(run.activationId!, { deadline });
24
- await continueAncestors(ctx, run, (root.generation ?? 0) + 1);
25
- return true;
26
- }
27
-
28
- export async function retryItem(ctx: ModuleCtx, run: WorkflowRunRecord, activationId: string | undefined): Promise<void> {
29
- const slot = await stores(ctx).activation.get(run.id);
30
- if (!slot?.parentId || activationId !== slot.id) throw new OpError("invalid_input", 400, "select the failed activationId to explicitly retry its work");
31
- const container = (await stores(ctx).activation.get(slot.parentId))!;
32
- const counts = container.container!;
33
- const root = await rootRun(ctx, run);
34
- if (root.budget!.activations >= (root.limits?.activations ?? 4096)) throw new OpError("conflict", 409, "ACTIVATION_LIMIT: retry budget exhausted");
35
- const used = await usedEffects(ctx, run);
36
- await stores(ctx).activation.update(slot.id, { state: "ready", active: true, attempt: slot.attempt + 1, outcome: undefined, outputs: undefined, error: undefined, childRunId: undefined,
37
- lane: { status: "running", waitSeq: used.seq, round: slot.attempt + 1 }, generation: (root.generation ?? 0) + 1 });
38
- await stores(ctx).activation.update(container.id, { state: "waiting", scheduling: true, container: { ...counts, stopped: counts.failed > 1, decision: false, live: counts.live + 1, settled: counts.settled - 1, failed: counts.failed - 1 } });
39
- await stores(ctx).run.update(root.id, { budget: { ...root.budget!, activations: root.budget!.activations + 1 } });
40
- await continueAncestors(ctx, run.parentRunId ? await requireRun(ctx, run.parentRunId) : undefined, (root.generation ?? 0) + 1);
41
- await ctx.workflows.start("run", { runId: slot.id }, { idempotencyKey: `${slot.id}:retry:${slot.attempt + 1}`, enlist: "op-commit" });
42
- }
43
-
44
- async function slots(ctx: ModuleCtx, id: string, where: Partial<WorkflowActivation> = {}): Promise<WorkflowActivation[]> {
45
- const rows: WorkflowActivation[] = [];
46
- let cursor: string | undefined;
47
- do {
48
- const page = await stores(ctx).activation.list({ where: { parentId: id, ...where }, limit: 200, ...(cursor ? { cursor } : {}) });
49
- rows.push(...page.items); cursor = page.cursor;
50
- } while (cursor);
51
- return rows;
52
- }
53
-
54
- async function tickContainer(ctx: ModuleCtx, container: WorkflowActivation, admit: boolean): Promise<{ admitted: boolean; cancel: string[] }> {
55
- const run = await requireRun(ctx, container.parentId ? container.id : container.runId);
56
- const root = await rootRun(ctx, run);
57
- const loop = run.definition.steps[run.step]?.forEach;
58
- if (!loop || run.activationId !== container.id || !container.container) {
59
- await stores(ctx).activation.update(container.id, { scheduling: false });
60
- return { admitted: false, cancel: [] };
61
- }
62
- const counts = { ...container.container };
63
- if (!run.cancel && !root.cancel && root.status === "running" && run.status === "running" && container.deadline !== undefined && ctx.clock.now() >= container.deadline) {
64
- await parkDecision(ctx, run, run, `CONTAINER_TIMEOUT: ${container.path} exceeded ${container.deadline}`);
65
- return { admitted: false, cancel: [] };
66
- }
67
- const items = await slots(ctx, container.id, { active: true });
68
- const cancel: string[] = [];
69
- for (const slot of items) {
70
- if (!slot.lane || slot.lane.counted === true) continue;
71
- const status = slot.lane.status;
72
- if (status !== "done" && status !== "failed" && status !== "cancelled") continue;
73
- if (status === "cancelled" && (slot.lane.cleanup as { status?: string } | undefined)?.status !== "complete") continue;
74
- const failed = status === "failed";
75
- if (status === "done" && loop.join === "any" && !counts.winner && slot.outcome !== "skipped") counts.winner = slot.key ?? String(slot.index);
76
- slot.state = status === "done" ? slot.outcome === "skipped" ? "skipped" : "succeeded" : status;
77
- slot.outcome = status === "done" ? slot.state === "skipped" ? "skipped" : "complete" : status;
78
- if (failed) slot.error ??= { code: "ITEM_FAILED", message: slot.lane.reason };
79
- slot.lane = { ...slot.lane, counted: true };
80
- await stores(ctx).activation.update(slot.id, { active: false, state: slot.state, outcome: slot.outcome, error: slot.error, lane: slot.lane });
81
- counts.live--; counts.settled++; if (failed) counts.failed++;
82
- if (status === "done" && slot.state !== "skipped") await settleDependents(ctx, slot, false);
83
- else if (slot.state === "skipped" || loop.onFailure === "continue") counts.settled += await settleDependents(ctx, slot, true);
84
- if (failed && loop.onFailure !== "continue") counts.stopped = true;
85
- if (failed && loop.onFailure === "escalate" && !counts.decision) {
86
- await parkDecision(ctx, await requireRun(ctx, slot.id), run, `ITEM_FAILED: ${slot.path}; explicitly retry this slot or cancel the group`);
87
- counts.decision = true;
88
- }
89
- }
90
- if (counts.failed && loop.onFailure === "escalate" && !counts.decision && !items.some(slot => slot.attempt > 1 && slot.lane?.counted !== true)) {
91
- const unresolved = (await slots(ctx, container.id, { state: "failed" }))[0];
92
- if (unresolved) {
93
- await parkDecision(ctx, await requireRun(ctx, unresolved.id), run, `ITEM_FAILED: ${unresolved.path}; explicitly retry this slot or cancel the group`);
94
- counts.stopped = true; counts.decision = true;
95
- }
96
- }
97
- const stopping = Boolean(counts.winner || root.cancel || run.cancel || (counts.stopped && loop.onFailure === "fail-fast"));
98
- if (stopping) {
99
- counts.stopped = true;
100
- for (const slot of [...await slots(ctx, container.id, { state: "pending" }), ...await slots(ctx, container.id, { state: "ready" })].filter(slot => !slot.active)) { await stores(ctx).activation.update(slot.id, { state: "skipped", outcome: "skipped", ...(counts.winner ? { error: { code: "JOIN_LOST", winner: counts.winner } } : {}) }); counts.settled++; }
101
- for (const slot of items) if (slot.lane?.counted !== true && slot.lane) {
102
- if (counts.winner) await stores(ctx).activation.update(slot.id, { error: { code: "JOIN_LOST", winner: counts.winner } });
103
- cancel.push(slot.id);
104
- }
105
- counts.next = counts.total;
106
- }
107
- let admitted = false;
108
- if (admit && !counts.stopped && !root.cancel && root.status === "running" && run.status === "running" && counts.live < loop.concurrency && counts.next < counts.total) {
109
- const slot = (await stores(ctx).activation.list({ where: { parentId: container.id, state: "ready", active: false }, limit: 1 })).items[0]
110
- ?? (await stores(ctx).activation.list({ where: { parentId: container.id, state: "pending" }, limit: 1 })).items.find(slot => slot.remaining === undefined);
111
- if (slot) {
112
- await stores(ctx).activation.update(slot.id, { state: "running", active: true, lane: { status: "running" } });
113
- await ctx.workflows.start("run", { runId: slot.id }, { idempotencyKey: slot.id, enlist: "op-commit" });
114
- counts.next++; counts.live++; admitted = true;
115
- }
116
- }
117
- await stores(ctx).activation.update(container.id, { container: counts });
118
- if (stopping && counts.live === 0) await stores(ctx).activation.update(container.id, { scheduling: false });
119
- // A parked decision retains its original wait even when every item has settled.
120
- if (!counts.decision && counts.settled === counts.total && run.status === "running" && !run.cancel && run.awaiting?.kind === "for-each" && !await stores(ctx).signal.get(run.awaiting.key)) {
121
- const results = (await slots(ctx, container.id)).map(itemResult);
122
- const ok = loop.join === "any" ? counts.winner !== undefined : counts.failed === 0;
123
- const base = { ok, ...(counts.winner ? { winner: counts.winner } : {}), resultsRef: container.id };
124
- const complete = { ...base, results: loop.branches ? Object.fromEntries(results.map(result => [result.key, result])) : results };
125
- const outputs = new TextEncoder().encode(JSON.stringify(complete)).length <= 65536 ? complete : base;
126
- const outcome = loop.join === "any" ? ok ? "complete" : "failed" : counts.failed && loop.onFailure !== "continue" ? "failed" : "complete";
127
- await stores(ctx).signal.create({ id: run.awaiting.key, runId: run.id, kind: "for-each", payload: { outcome, outputs }, createdAt: ctx.clock.now() });
128
- await stores(ctx).activation.update(container.id, { scheduling: false });
129
- await ctx.events.emit(SIGNAL_TOPIC, { runId: run.id, correlationId: run.awaiting.key });
130
- }
131
- return { admitted, cancel };
132
- }
133
-
134
- export const foreachOps = {
135
- tickItems: {
136
- kind: "mutation", idempotent: true, grant: ADMIN_GRANT, summary: "Settle item slots and fairly admit a bounded batch across containers.", expose: { http: false, mcp: false, cli: false },
137
- input: z.object({}), output: z.object({ admitted: z.number(), cancel: z.array(z.string()) }),
138
- handler: async (ctx: ModuleCtx) => {
139
- const cursor = await stores(ctx).admission.get("foreach");
140
- const page = await stores(ctx).activation.list({ where: { scheduling: true }, limit: 50, ...(cursor?.cursor ? { cursor: cursor.cursor } : {}) });
141
- let admitted = 0;
142
- const cancel: string[] = [];
143
- for (const container of page.items) {
144
- const tick = await tickContainer(ctx, container, true);
145
- admitted += Number(tick.admitted); cancel.push(...tick.cancel);
146
- }
147
- if (cursor) await stores(ctx).admission.update(cursor.id, { cursor: page.cursor });
148
- else await stores(ctx).admission.create({ id: "foreach", cursor: page.cursor });
149
- return { admitted, cancel };
150
- },
151
- },
152
- itemLanes: {
153
- kind: "query", grant: ADMIN_GRANT, summary: "Page active item lanes for ordinary wait reconciliation.", expose: { http: false, mcp: false, cli: false },
154
- input: z.object({ runId: z.string().optional(), containerId: z.string().optional(), cursor: z.string().optional() }), output: z.object({ items: z.array(workflowActivationSchema), cursor: z.string().optional() }),
155
- handler: async (ctx: ModuleCtx, input: { runId?: string; containerId?: string; cursor?: string }) => stores(ctx).activation.list({ where: { active: true, ...(input.runId ? { runId: input.runId } : {}), ...(input.containerId ? { parentId: input.containerId } : {}) }, limit: 100, ...(input.cursor ? { cursor: input.cursor } : {}) }),
156
- },
157
- itemResults: {
158
- kind: "query", grant: ADMIN_GRANT, summary: "Page input-ordered item results from a container reference.", expose: { http: false, mcp: false, cli: false },
159
- input: z.object({ runId: z.string(), resultsRef: z.string(), cursor: z.string().optional() }), output: z.object({ items: z.array(z.record(z.string(), z.unknown())), cursor: z.string().optional() }),
160
- handler: async (ctx: ModuleCtx, input: { runId: string; resultsRef: string; cursor?: string }) => {
161
- const container = await stores(ctx).activation.get(input.resultsRef);
162
- if (!container || container.runId !== input.runId || !container.container) throw new OpError("not_found", 404, "item collection not found");
163
- const page = await stores(ctx).activation.list({ where: { parentId: container.id }, limit: 100, ...(input.cursor ? { cursor: input.cursor } : {}) });
164
- return { items: page.items.map(itemResult), ...(page.cursor ? { cursor: page.cursor } : {}) };
165
- },
166
- },
167
- } as const;
@@ -1,339 +0,0 @@
1
- import { isDeepStrictEqual } from "node:util";
2
- import type { WorkflowInstanceRow, WorkflowStepJournalRow } from "./workflow-journal";
3
-
4
- /**
5
- * Strict durable-journal codecs + the step-journal fold, re-homed into the kernel from
6
- * `src/db/workflow-journal.ts` (epic #1408 / M1). These are the pure functions that validate and
7
- * serialize journal rows and reconstruct an instance's resume state from its immutable journal;
8
- * the DB-bound helpers that used the `src/` connection singleton stay in `src/` and now delegate
9
- * here through the strangler re-export. The kernel never imports `src/`, so the two small SDK
10
- * helpers this logic relied on (`isRecord`, `errMessage`) are inlined below.
11
- */
12
-
13
- /** True for a non-null, non-array object (inlined from `@humwick/sdk`). */
14
- function isRecord(value: unknown): value is Record<string, unknown> {
15
- return typeof value === "object" && value !== null && !Array.isArray(value);
16
- }
17
-
18
- /** Human-readable message from any thrown value (inlined from `@humwick/sdk`). */
19
- function errMessage(error: unknown): string {
20
- return error instanceof Error ? error.message : String(error);
21
- }
22
-
23
- export const WORKFLOW_INSTANCE_STATUSES = [
24
- "pending", "running", "sleeping", "waiting", "done", "failed", "canceled",
25
- ] as const;
26
- export type WorkflowInstanceStatus = typeof WORKFLOW_INSTANCE_STATUSES[number];
27
- export type WorkflowAttemptOutcome = "completed" | "failed";
28
- export type WorkflowWaitCondition = Record<string, unknown>;
29
-
30
- /** The camelCase, decoded view of a `workflow_instances` row. */
31
- export interface WorkflowInstance {
32
- id: string;
33
- moduleName: string;
34
- workflowName: string;
35
- idempotencyKey: string;
36
- input: unknown;
37
- initialState: unknown;
38
- startStep: string;
39
- status: WorkflowInstanceStatus;
40
- sleepUntil?: number;
41
- waitFor?: WorkflowWaitCondition;
42
- output?: unknown;
43
- failure?: unknown;
44
- journalCursor: number;
45
- journalPrunedThrough: number;
46
- executorLeaseOwner?: string;
47
- executorLeaseToken?: string;
48
- executorLeaseVersion: number;
49
- executorLeaseAcquiredAt?: number;
50
- executorLeaseExpiresAt?: number;
51
- createdAt: number;
52
- updatedAt: number;
53
- closedAt?: number;
54
- retentionUntil?: number;
55
- }
56
-
57
- /** The decoded shape of a memoized step result stored in `workflow_step_journal.result`. */
58
- export interface WorkflowStepResult {
59
- state: unknown;
60
- next: string;
61
- output?: unknown;
62
- sleepUntil?: number;
63
- waitFor?: WorkflowWaitCondition;
64
- }
65
-
66
- /** The camelCase, decoded view of an immutable `workflow_step_journal` attempt row. */
67
- export interface WorkflowJournalRecord {
68
- instanceId: string;
69
- seq: number;
70
- stepIndex: number;
71
- stepName: string;
72
- attempt: number;
73
- outcome: WorkflowAttemptOutcome;
74
- stateSnapshot: unknown;
75
- result: unknown;
76
- sleepUntil?: number;
77
- waitFor?: WorkflowWaitCondition;
78
- startedAt: number;
79
- completedAt: number;
80
- }
81
-
82
- export type WorkflowResumeStatus = "ready" | "sleeping" | "waiting" | "done" | "failed" | "canceled";
83
-
84
- /** Where a folded journal says execution resumes, and the state it resumes with. */
85
- export interface WorkflowResumePoint {
86
- instanceId: string;
87
- status: WorkflowResumeStatus;
88
- state: unknown;
89
- stepIndex: number;
90
- stepName?: string;
91
- nextAttempt: number;
92
- completedSteps: number;
93
- cursor: number;
94
- sleepUntil?: number;
95
- waitFor?: WorkflowWaitCondition;
96
- output?: unknown;
97
- failure?: unknown;
98
- }
99
-
100
- /** Raised when durable journal state is malformed; replay must fail loudly, never invent state. */
101
- export class WorkflowJournalCorruptionError extends Error {
102
- constructor(message: string) {
103
- super(`workflow journal corruption: ${message}`);
104
- this.name = "WorkflowJournalCorruptionError";
105
- }
106
- }
107
-
108
- /** Serialize with a lossless round-trip guard; throws for non-JSON-safe values. */
109
- export function encodeJson(value: unknown, label: string): string {
110
- try {
111
- const encoded = JSON.stringify(value);
112
- if (encoded === undefined) throw new TypeError("value is not JSON-serializable");
113
- if (!isDeepStrictEqual(JSON.parse(encoded), value)) throw new TypeError("value changes during JSON encoding");
114
- return encoded;
115
- } catch (error) {
116
- throw new TypeError(`${label} must be JSON-serializable: ${errMessage(error)}`);
117
- }
118
- }
119
-
120
- /** Parse durable JSON, converting a parse failure into a loud corruption error. */
121
- export function decodeJson(raw: string, label: string): unknown {
122
- try {
123
- return JSON.parse(raw);
124
- } catch {
125
- throw new WorkflowJournalCorruptionError(`${label} is not valid JSON`);
126
- }
127
- }
128
-
129
- function decodeWaitCondition(value: unknown, label: string): WorkflowWaitCondition {
130
- if (!isRecord(value)) throw new WorkflowJournalCorruptionError(`${label} must be a JSON object`);
131
- return value;
132
- }
133
-
134
- /** Strict decode of a memoized step result: a JSON object with state + a non-empty `next`. */
135
- export function decodeStepResult(value: unknown, label: string): WorkflowStepResult {
136
- if (!isRecord(value) || !("state" in value) || typeof value.next !== "string" || value.next.length === 0) {
137
- throw new WorkflowJournalCorruptionError(`${label} must contain a state and non-empty next step`);
138
- }
139
- if (value.sleepUntil !== undefined && (typeof value.sleepUntil !== "number" || !Number.isFinite(value.sleepUntil))) {
140
- throw new WorkflowJournalCorruptionError(`${label}.sleepUntil must be a finite number`);
141
- }
142
- const waitFor = value.waitFor === undefined ? undefined : decodeWaitCondition(value.waitFor, `${label}.waitFor`);
143
- if (value.sleepUntil !== undefined && waitFor !== undefined) {
144
- throw new WorkflowJournalCorruptionError(`${label} cannot contain both sleepUntil and waitFor`);
145
- }
146
- if (value.next === "done" && (value.sleepUntil !== undefined || waitFor !== undefined)) {
147
- throw new WorkflowJournalCorruptionError(`${label} cannot schedule work after next=done`);
148
- }
149
- return {
150
- state: value.state,
151
- next: value.next,
152
- ...(Object.hasOwn(value, "output") ? { output: value.output } : {}),
153
- ...(value.sleepUntil !== undefined ? { sleepUntil: value.sleepUntil } : {}),
154
- ...(waitFor !== undefined ? { waitFor } : {}),
155
- };
156
- }
157
-
158
- /** Strict row codec: durable replay must fail loudly rather than invent state from malformed JSON. */
159
- export function workflowInstanceFromRow(row: WorkflowInstanceRow): WorkflowInstance {
160
- if (!WORKFLOW_INSTANCE_STATUSES.includes(row.status as WorkflowInstanceStatus)) {
161
- throw new WorkflowJournalCorruptionError(`instance ${row.id} has unknown status ${row.status}`);
162
- }
163
- if (![row.journal_cursor, row.journal_pruned_through, row.executor_lease_version].every(Number.isSafeInteger)
164
- || row.journal_cursor < 0 || row.journal_pruned_through < 0 || row.journal_pruned_through > row.journal_cursor
165
- || row.executor_lease_version < 0) {
166
- throw new WorkflowJournalCorruptionError(`instance ${row.id} has invalid journal/lease cursors`);
167
- }
168
- const waitForValue = row.wait_for === null ? undefined : decodeWaitCondition(decodeJson(row.wait_for, `instance ${row.id} wait_for`), `instance ${row.id} wait_for`);
169
- return {
170
- id: row.id,
171
- moduleName: row.module_name,
172
- workflowName: row.workflow_name,
173
- idempotencyKey: row.idempotency_key,
174
- input: decodeJson(row.input, `instance ${row.id} input`),
175
- initialState: decodeJson(row.initial_state, `instance ${row.id} initial_state`),
176
- startStep: row.start_step,
177
- status: row.status as WorkflowInstanceStatus,
178
- ...(row.sleep_until !== null ? { sleepUntil: row.sleep_until } : {}),
179
- ...(waitForValue !== undefined ? { waitFor: waitForValue } : {}),
180
- ...(row.output !== null ? { output: decodeJson(row.output, `instance ${row.id} output`) } : {}),
181
- ...(row.failure !== null ? { failure: decodeJson(row.failure, `instance ${row.id} failure`) } : {}),
182
- journalCursor: row.journal_cursor,
183
- journalPrunedThrough: row.journal_pruned_through,
184
- ...(row.executor_lease_owner !== null ? { executorLeaseOwner: row.executor_lease_owner } : {}),
185
- ...(row.executor_lease_token !== null ? { executorLeaseToken: row.executor_lease_token } : {}),
186
- executorLeaseVersion: row.executor_lease_version,
187
- ...(row.executor_lease_acquired_at !== null ? { executorLeaseAcquiredAt: row.executor_lease_acquired_at } : {}),
188
- ...(row.executor_lease_expires_at !== null ? { executorLeaseExpiresAt: row.executor_lease_expires_at } : {}),
189
- createdAt: row.created_at,
190
- updatedAt: row.updated_at,
191
- ...(row.closed_at !== null ? { closedAt: row.closed_at } : {}),
192
- ...(row.retention_until !== null ? { retentionUntil: row.retention_until } : {}),
193
- };
194
- }
195
-
196
- /** Strict codec for the immutable per-attempt journal row. */
197
- export function workflowJournalRecordFromRow(row: WorkflowStepJournalRow): WorkflowJournalRecord {
198
- if (row.outcome !== "completed" && row.outcome !== "failed") {
199
- throw new WorkflowJournalCorruptionError(`instance ${row.instance_id} seq ${row.seq} has unknown outcome ${row.outcome}`);
200
- }
201
- if (![row.seq, row.step_index, row.attempt, row.started_at, row.completed_at].every(Number.isSafeInteger)
202
- || row.seq < 1 || row.step_index < 0 || row.attempt < 1 || row.completed_at < row.started_at) {
203
- throw new WorkflowJournalCorruptionError(`instance ${row.instance_id} has invalid journal coordinates`);
204
- }
205
- const label = `instance ${row.instance_id} seq ${row.seq}`;
206
- const stateSnapshot = decodeJson(row.state_snapshot, `${label} state_snapshot`);
207
- const result = decodeJson(row.result, `${label} result`);
208
- const waitFor = row.wait_for === null ? undefined : decodeWaitCondition(decodeJson(row.wait_for, `${label} wait_for`), `${label} wait_for`);
209
- if (row.outcome === "completed") {
210
- const completed = decodeStepResult(result, `${label} result`);
211
- if (!isDeepStrictEqual(completed.state, stateSnapshot)) {
212
- throw new WorkflowJournalCorruptionError(`${label} state_snapshot does not match result.state`);
213
- }
214
- if ((completed.sleepUntil ?? null) !== row.sleep_until || !isDeepStrictEqual(completed.waitFor, waitFor)) {
215
- throw new WorkflowJournalCorruptionError(`${label} timer/wait columns do not match result`);
216
- }
217
- } else if (row.sleep_until !== null || waitFor !== undefined) {
218
- throw new WorkflowJournalCorruptionError(`${label} failed attempt cannot schedule a timer or wait`);
219
- }
220
- return {
221
- instanceId: row.instance_id,
222
- seq: row.seq,
223
- stepIndex: row.step_index,
224
- stepName: row.step_name,
225
- attempt: row.attempt,
226
- outcome: row.outcome,
227
- stateSnapshot,
228
- result,
229
- ...(row.sleep_until !== null ? { sleepUntil: row.sleep_until } : {}),
230
- ...(waitFor !== undefined ? { waitFor } : {}),
231
- startedAt: row.started_at,
232
- completedAt: row.completed_at,
233
- };
234
- }
235
-
236
- /** Fold a complete immutable journal into the exact step/attempt at which execution resumes. */
237
- export function foldWorkflowJournal(instance: WorkflowInstance, records: WorkflowJournalRecord[]): WorkflowResumePoint {
238
- if (instance.journalPrunedThrough > 0) {
239
- throw new WorkflowJournalCorruptionError(`instance ${instance.id} was pruned through seq ${instance.journalPrunedThrough}`);
240
- }
241
- let state = instance.initialState;
242
- let stepName = instance.startStep;
243
- let stepIndex = 0;
244
- let nextAttempt = 1;
245
- let status: WorkflowResumeStatus = "ready";
246
- let sleepUntil: number | undefined;
247
- let waitFor: WorkflowWaitCondition | undefined;
248
- let output: unknown;
249
- let hasOutput = false;
250
-
251
- for (let index = 0; index < records.length; index += 1) {
252
- const record = records[index]!;
253
- const expectedSeq = index + 1;
254
- if (record.instanceId !== instance.id || record.seq !== expectedSeq) {
255
- throw new WorkflowJournalCorruptionError(`instance ${instance.id} expected seq ${expectedSeq}`);
256
- }
257
- if (record.stepIndex !== stepIndex || record.stepName !== stepName || record.attempt !== nextAttempt) {
258
- throw new WorkflowJournalCorruptionError(
259
- `instance ${instance.id} seq ${record.seq} expected step ${stepIndex}:${stepName} attempt ${nextAttempt}`,
260
- );
261
- }
262
- // Seeing an attempt proves any timer/wait before this step was satisfied.
263
- status = "ready";
264
- sleepUntil = undefined;
265
- waitFor = undefined;
266
- if (record.outcome === "failed") {
267
- if (!isDeepStrictEqual(record.stateSnapshot, state)) {
268
- throw new WorkflowJournalCorruptionError(`instance ${instance.id} seq ${record.seq} failed attempt changed state`);
269
- }
270
- nextAttempt += 1;
271
- continue;
272
- }
273
- const result = decodeStepResult(record.result, `instance ${instance.id} seq ${record.seq} result`);
274
- if (!isDeepStrictEqual(result.state, record.stateSnapshot)) {
275
- throw new WorkflowJournalCorruptionError(`instance ${instance.id} seq ${record.seq} state snapshot mismatch`);
276
- }
277
- state = record.stateSnapshot;
278
- stepIndex += 1;
279
- nextAttempt = 1;
280
- if (result.next === "done") {
281
- status = "done";
282
- stepName = "";
283
- output = result.output;
284
- hasOutput = Object.hasOwn(result, "output");
285
- if (index !== records.length - 1) throw new WorkflowJournalCorruptionError(`instance ${instance.id} has records after done`);
286
- continue;
287
- }
288
- stepName = result.next;
289
- if (result.sleepUntil !== undefined) {
290
- status = "sleeping";
291
- sleepUntil = result.sleepUntil;
292
- } else if (result.waitFor !== undefined) {
293
- status = "waiting";
294
- waitFor = result.waitFor;
295
- }
296
- }
297
- if (records.length !== instance.journalCursor) {
298
- throw new WorkflowJournalCorruptionError(`instance ${instance.id} cursor ${instance.journalCursor} has ${records.length} records`);
299
- }
300
- if (instance.status === "done" && status !== "done") {
301
- throw new WorkflowJournalCorruptionError(`instance ${instance.id} is done without a terminal checkpoint`);
302
- }
303
- if (instance.status === "failed" || instance.status === "canceled") {
304
- status = instance.status;
305
- } else if (instance.status === "sleeping") {
306
- // The instance row is authoritative for a parked schedule. Two sources land here: a completed
307
- // step whose result carried `sleepUntil` (also reconstructable from the record), AND a retry
308
- // backoff parked after a FAILED attempt — which the immutable journal record cannot itself
309
- // carry (a failed record's timer/wait columns must be null), so its due-time lives only on the
310
- // row. Trusting the row unifies both and keeps the fold's resume schedule correct for retries.
311
- if (instance.sleepUntil === undefined) {
312
- throw new WorkflowJournalCorruptionError(`instance ${instance.id} is sleeping without a sleep_until`);
313
- }
314
- status = "sleeping";
315
- sleepUntil = instance.sleepUntil;
316
- waitFor = undefined;
317
- } else if (instance.status === "waiting") {
318
- if (instance.waitFor === undefined) {
319
- throw new WorkflowJournalCorruptionError(`instance ${instance.id} is waiting without a wait_for`);
320
- }
321
- status = "waiting";
322
- waitFor = instance.waitFor;
323
- sleepUntil = undefined;
324
- }
325
- return {
326
- instanceId: instance.id,
327
- status,
328
- state,
329
- stepIndex,
330
- ...(stepName ? { stepName } : {}),
331
- nextAttempt,
332
- completedSteps: stepIndex,
333
- cursor: instance.journalCursor,
334
- ...(sleepUntil !== undefined ? { sleepUntil } : {}),
335
- ...(waitFor !== undefined ? { waitFor } : {}),
336
- ...(status === "done" && hasOutput ? { output } : {}),
337
- ...(instance.failure !== undefined ? { failure: instance.failure } : {}),
338
- };
339
- }