@humwick/server 0.130.1 → 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.
- package/README.md +5 -0
- package/docs/guides/tools-administration.md +4 -0
- package/docs/guides/tools-coordination.md +1 -0
- package/docs/guides/tools-tasks.md +8 -0
- package/docs/guides/workflows.md +58 -0
- package/docs/openapi.json +414 -1
- package/package.json +8 -17
- package/public/assets/{MessageBubble-BWpoBjl4.js → MessageBubble-BWLqMGKG.js} +2 -2
- package/public/assets/{PlanGraphPanel-C0auck3n.js → PlanGraphPanel-3TlIQpjk.js} +2 -2
- package/public/assets/{TraceView-DBnOx7AQ.js → TraceView-Wimm0Czv.js} +2 -2
- package/public/assets/{VaultDecisionCard-0kp2oNBC.js → VaultDecisionCard-Bau_vNax.js} +2 -2
- package/public/assets/{activity-yLztXEBt.js → activity-CrSurn-r.js} +2 -2
- package/public/assets/{agent-lineage-BbJDR1dW.js → agent-lineage-CknAJKcb.js} +2 -2
- package/public/assets/agent-liveness-CaNwK0Zf.js +2 -0
- package/public/assets/{agent-name-CTZBrSD9.js → agent-name-DKbiIhBo.js} +2 -2
- package/public/assets/agent-profiles-DwF6EQ4R.js +2 -0
- package/public/assets/{agent-project-badge-9TqzUUYE.js → agent-project-badge-Diz4Sjjb.js} +2 -2
- package/public/assets/{agent-tools-D4n40pn8.js → agent-tools-CRhpRQza.js} +2 -2
- package/public/assets/{agent-type-icon-D6FUarOf.js → agent-type-icon-LrUEU3lB.js} +2 -2
- package/public/assets/{agents-BYDslkJc.js → agents-B-v66C-4.js} +2 -2
- package/public/assets/{analytics-B-XSbEMC.js → analytics-BqCb-LKi.js} +2 -2
- package/public/assets/{artifact-viewer-DdWIfV6M.js → artifact-viewer-kf4pGuAR.js} +2 -2
- package/public/assets/{artifacts-oBOqks-2.js → artifacts-D32zyw4Y.js} +2 -2
- package/public/assets/{artifacts-DPN1q4pA.js → artifacts-DJevXcvg.js} +2 -2
- package/public/assets/{automation-4KcRFXEu.js → automation-BpRajx_5.js} +2 -2
- package/public/assets/{backlog-Cw9xPTCF.js → backlog-D_UjTdzX.js} +2 -2
- package/public/assets/{busy-health-CTu32MZt.js → busy-health-N3VEh4ao.js} +2 -2
- package/public/assets/{channels-BiXzg1G9.js → channels-9jopungq.js} +2 -2
- package/public/assets/{chat-Ctk2qwvA.js → chat-CSXrTB3C.js} +2 -2
- package/public/assets/{code-preview-Bm1wWqAp.js → code-preview-p1NnmqK0.js} +2 -2
- package/public/assets/{connectors-C36ax4Vw.js → connectors-CZdqlDNL.js} +2 -2
- package/public/assets/{cosmos-Jj9LK2Wf.js → cosmos-BgdsE4dG.js} +2 -2
- package/public/assets/{crate-agent-H6S7-B9T.js → crate-agent-30aXTKva.js} +2 -2
- package/public/assets/{dependency-graph-Cd1E23wa.js → dependency-graph-B_jvw2GE.js} +2 -2
- package/public/assets/{deploys-Bwfq146q.js → deploys-DoN6bAb5.js} +2 -2
- package/public/assets/{display-CfO7rTuF.js → display-BrKWFGJz.js} +2 -2
- package/public/assets/{files-DhhZI2Ih.js → files-D6XEu8Ng.js} +2 -2
- package/public/assets/{fleet-B_rFEbnL.js → fleet-C7jQWbw2.js} +2 -2
- package/public/assets/{fleet-metrics-BkHP-P3R.js → fleet-metrics-BiDs3MLx.js} +2 -2
- package/public/assets/{formatted-body-Cm1RrzmN.js → formatted-body-CQBwc1fb.js} +3 -3
- package/public/assets/{formatted-body-impl-DQiOuOD1.js → formatted-body-impl-DyG0WsQS.js} +2 -2
- package/public/assets/{host-providers-dialog-CHqwXTov.js → host-providers-dialog-DKaW31dN.js} +2 -2
- package/public/assets/index-CRHep3M9.js +15 -0
- package/public/assets/index-MYAIwYTX.css +2 -0
- package/public/assets/{insights-fH1EL-qZ.js → insights-CzrtkpOE.js} +2 -2
- package/public/assets/{integrations-C5Oix6ng.js → integrations-aVsUmigt.js} +2 -2
- package/public/assets/{issue-ref-chip-ODyondIa.js → issue-ref-chip-BnSlM_95.js} +2 -2
- package/public/assets/{log-viewer-Cvfy6PW7.js → log-viewer-qe7Ee47Z.js} +2 -2
- package/public/assets/{maintenance-D8CDH5wZ.js → maintenance-NfjqFZAk.js} +2 -2
- package/public/assets/{managed-agents-egllbpij.js → managed-agents-BiH0HgaE.js} +2 -2
- package/public/assets/{markdown-preview-CYSFgVgK.js → markdown-preview-BixaT3Xr.js} +3 -3
- package/public/assets/{markdown-preview-impl-BteVIjX4.js → markdown-preview-impl-DNGlWIS3.js} +2 -2
- package/public/assets/{memory-C8WmXsyr.js → memory-DNsCl6UX.js} +2 -2
- package/public/assets/{messages-BzQdqlO6.js → messages-DopU1L1X.js} +2 -2
- package/public/assets/{orchestrators-B1HxzkNw.js → orchestrators-Cq7ckLQT.js} +2 -2
- package/public/assets/{overview-DCUnI4Z7.js → overview-C1DCTzzV.js} +2 -2
- package/public/assets/{pairs-ftCd2XcD.js → pairs-DE_m6rrE.js} +2 -2
- package/public/assets/{profile-icon-B2zH8DMH.js → profile-icon-B3rfh77F.js} +2 -2
- package/public/assets/project-profile-instructions-DQWZE0kA.js +2 -0
- package/public/assets/projects-Br2Bd2h1.js +2 -0
- package/public/assets/{prompt-settings-Bftq9czX.js → prompt-settings-BKvFsvGD.js} +2 -2
- package/public/assets/provider-display-DsuWMvVL.js +2 -0
- package/public/assets/{registry-DWCU5c8q.js → registry-Cw38_6gW.js} +2 -2
- package/public/assets/run-card-CpKqRbhg.js +2 -0
- package/public/assets/scroll-text-BKXS1ahr.js +2 -0
- package/public/assets/{secrets-C7GIUATz.js → secrets-DxI-vzSq.js} +2 -2
- package/public/assets/{service-access-settings-C3aujqTG.js → service-access-settings-D9s_VJbd.js} +2 -2
- package/public/assets/{settings-C6sZ9eBa.js → settings-CKZTVGIx.js} +2 -2
- package/public/assets/{shipping-Bu8p1gEw.js → shipping-B9YtnwtG.js} +2 -2
- package/public/assets/{stats-V9Z-GxUd.js → stats-DyUBKyWI.js} +2 -2
- package/public/assets/{status-dot-CVv0iGHL.js → status-dot-DYqlEUhJ.js} +2 -2
- package/public/assets/{store-BvtFdlGX.js → store-DcClXLH0.js} +3 -3
- package/public/assets/{tasks-CXU75HRF.js → tasks-Bh7Chr4-.js} +2 -2
- package/public/assets/{teams-BPf976zb.js → teams-bowI7qVY.js} +2 -2
- package/public/assets/{terminal-viewer-DE9BOe-8.js → terminal-viewer-BgVLHdsU.js} +3 -3
- package/public/assets/{terminal-viewer-impl-D04pjmGh.js → terminal-viewer-impl-Dz22UPy6.js} +2 -2
- package/public/assets/token-estimate-Co-Jdphh.js +2 -0
- package/public/assets/{use-agent-terminal-B1yR456R.js → use-agent-terminal-DSQQ-iY7.js} +2 -2
- package/public/assets/{use-decisions-D-S4tpDf.js → use-decisions-samMQhQf.js} +2 -2
- package/public/assets/use-project-list-59LheVP7.js +2 -0
- package/public/assets/{use-selectors-CnrAk2YP.js → use-selectors-BZa25xEB.js} +2 -2
- package/public/assets/{user-avatar-nBFIcR4M.js → user-avatar-DHxlvUvV.js} +2 -2
- package/public/assets/versioned-prompt-editor-BmED5DnJ.js +8 -0
- package/public/assets/{work-queue-TCjJxeT-.js → work-queue-CSvpuNLz.js} +2 -2
- package/public/assets/{workflow-marker-Cf3Hf2fN.js → workflow-marker-DAZcj5qt.js} +2 -2
- package/public/assets/{workflows-CXQX20Qj.js → workflows-DdmxRSyg.js} +2 -2
- package/public/assets/{workspaces-qSYzKYMA.js → workspaces-BxeKfVuF.js} +2 -2
- package/public/cosmos.html +4 -4
- package/public/index.html +31 -29
- package/runner/plugins/claude/.claude-plugin/plugin.json +1 -1
- package/runner/plugins/claude/hooks/gate-run-guard.provisioned.mjs +31 -1
- package/runner/src/adapters/claude-effort.ts +2 -4
- package/runner/src/adapters/codex-launch-config.ts +4 -5
- package/runner/src/adapters/codex.ts +1 -1
- package/runner/src/adapters/opencode-provider.ts +7 -22
- package/runner/src/adapters/opencode.ts +4 -4
- package/runner/src/agent-runner/credentials.ts +42 -12
- package/runner/src/agent-runner/inbox.ts +27 -0
- package/runner/src/agent-runner/lifecycle-milestones.ts +0 -2
- package/runner/src/bin/claude.ts +4 -0
- package/runner/src/bin/codex.ts +4 -0
- package/runner/src/bin/opencode.ts +4 -0
- package/runner/src/index.ts +5 -2
- package/runner/src/provider-setup.ts +34 -0
- package/runner/src/providers.ts +2 -1
- package/runner/src/runner-core.ts +18 -23
- package/runner/src/spawn-input.ts +2 -1
- package/runner/src/test-run-dedup.ts +59 -56
- package/scripts/cd-config.ts +8 -0
- package/scripts/gate-run-lease.ts +10 -7
- package/scripts/gate.sh +5 -1
- package/src/branch-land-notice.ts +93 -0
- package/src/branch-landed.ts +7 -76
- package/src/bus.ts +19 -20
- package/src/cd-deploy-notify.ts +30 -2
- package/src/cd-host-lag-notify.ts +7 -6
- package/src/ci/incidents.ts +2 -2
- package/src/ci/monitor.ts +5 -8
- package/src/ci/root-missing.ts +2 -7
- package/src/ci/transition-notify.ts +7 -39
- package/src/cli/cd-pauses.ts +19 -0
- package/src/cli/orchestrator.ts +1 -1
- package/src/cli/state.ts +2 -19
- package/src/cli/task-edit.ts +22 -0
- package/src/cli/task.ts +3 -1
- package/src/cli/workflow.ts +8 -2
- package/src/commands-db.ts +3 -3
- package/src/config-store-write.ts +13 -1
- package/src/config-store.ts +25 -14
- package/src/db/activity-async.ts +6 -1
- package/src/db/activity-write-ops.ts +12 -1
- package/src/db/activity.ts +1 -14
- package/src/db/agent-search.ts +1 -15
- package/src/db/agents-async.ts +52 -5
- package/src/db/agents-write-ops.ts +12 -3
- package/src/db/agents.ts +81 -54
- package/src/db/ci-incidents-async.ts +5 -5
- package/src/db/ci-incidents-write-ops.ts +2 -2
- package/src/db/command-row.ts +10 -2
- package/src/db/commands-list-query.ts +2 -0
- package/src/db/commands-spawn-read.ts +1 -1
- package/src/db/config-store-write-ops.ts +7 -2
- package/src/db/continuation-archives-async.ts +5 -0
- package/src/db/continuation-archives-write-ops.ts +7 -0
- package/src/db/continuations.ts +2 -2
- package/src/db/db-writer-worker.ts +8 -1
- package/src/db/deploy-events-async.ts +1 -0
- package/src/db/deploy-events-read-ops.ts +2 -1
- package/src/db/deploy-events.ts +35 -4
- package/src/db/hosts-registration.ts +10 -2
- package/src/db/hosts.ts +9 -6
- package/src/db/inbox.ts +0 -14
- package/src/db/index.ts +2 -0
- package/src/db/integrations-async.ts +8 -0
- package/src/db/integrations-ops.ts +15 -0
- package/src/db/integrations.ts +15 -21
- package/src/db/issue-lifecycle.ts +3 -4
- package/src/db/issue-refs-async.ts +20 -0
- package/src/db/issue-refs-write-ops.ts +29 -0
- package/src/db/issue-refs.ts +7 -8
- package/src/db/long-running-work.ts +2 -2
- package/src/db/main-connection-writes.ts +23 -2
- package/src/db/maintenance-job-reconcile.ts +61 -0
- package/src/db/maintenance-job-runs-async.ts +14 -0
- package/src/db/maintenance-job-write-ops.ts +9 -0
- package/src/db/mappers.ts +2 -14
- package/src/db/merge-lease.ts +1 -15
- package/src/db/migrations.ts +7 -15
- package/src/db/orchestrators.ts +4 -72
- package/src/db/pairs.ts +1 -13
- package/src/db/project-profile-instructions.ts +50 -0
- package/src/db/prompt-versions.ts +9 -2
- package/src/db/provider-config-history.ts +8 -3
- package/src/db/provider-model-seed.ts +34 -0
- package/src/db/read-op-marshaling-fixtures.ts +5 -0
- package/src/db/read-ops.ts +4 -0
- package/src/db/routing-policy.ts +2 -3
- package/src/db/schema.ts +9 -3
- package/src/db/spawn-delegation-state.ts +11 -4
- package/src/db/stats-async.ts +4 -3
- package/src/db/task-entity.ts +23 -2
- package/src/db/task-list.ts +3 -1
- package/src/db/tasks.ts +1 -17
- package/src/db/timer-ingress.ts +7 -1
- package/src/db/token-db-write-ops.ts +17 -0
- package/src/db/token-standing.ts +15 -11
- package/src/db/workflow-contact.ts +1 -1
- package/src/db/workflow-task-read-ops.ts +30 -0
- package/src/db/write-ops.ts +6 -0
- package/src/db/writer-gateway.ts +17 -11
- package/src/dispatch-availability.ts +10 -6
- package/src/ephemeral/{landing-ingress-proof-worker.ts → landing-ingress-proof.test-fixture.ts} +4 -2
- package/src/host/disk-alert.ts +121 -0
- package/src/lifecycle-manager.ts +3 -2
- package/src/maintenance/cd-watcher.ts +3 -3
- package/src/maintenance/critical-job-watchdog.ts +16 -17
- package/src/maintenance/index.ts +1 -0
- package/src/maintenance/jobs.ts +3 -2
- package/src/maintenance/scheduler.ts +84 -128
- package/src/maintenance/stalled-delivery-pager.ts +6 -5
- package/src/maintenance/workspaces.ts +1 -1
- package/src/managed-policy.ts +31 -27
- package/src/mcp/index.ts +5 -2
- package/src/mcp/output-descriptions.ts +8 -8
- package/src/mcp/presentation.ts +4 -4
- package/src/mcp/project-profile-instructions.ts +32 -0
- package/src/mcp/tool-groups.ts +1 -1
- package/src/mcp/tools-spawn.ts +4 -3
- package/src/mcp-agent-action.ts +1 -2
- package/src/mcp-issue-ref-tools.ts +12 -11
- package/src/mcp-maintenance-tools.ts +2 -2
- package/src/mcp-profile-trust-tools.ts +4 -4
- package/src/mcp-provider-catalog.ts +2 -2
- package/src/mcp-task-tools.ts +11 -5
- package/src/mcp-team-template-tools.ts +3 -3
- package/src/mcp-workflow-tools.ts +10 -3
- package/src/notification-types.ts +2 -1
- package/src/operator-alarm.ts +26 -11
- package/src/project-instructions.ts +4 -0
- package/src/project-profile-instruction-text.ts +6 -0
- package/src/project-profile-instructions.ts +20 -0
- package/src/provider-catalog-store.ts +3 -49
- package/src/provider-config-store.ts +12 -11
- package/src/provider-model-seed.ts +1 -18
- package/src/provider-quota-config-store.ts +5 -4
- package/src/provider-secrets.test-fixture.ts +3 -3
- package/src/recipe-runner.ts +2 -2
- package/src/repo-config-source.ts +10 -4
- package/src/routes/agent-profiles.ts +6 -6
- package/src/routes/agents-spawn.ts +1 -1
- package/src/routes/agents.ts +6 -6
- package/src/routes/commands-listing.ts +4 -0
- package/src/routes/commands.ts +2 -2
- package/src/routes/continuation-archives.ts +2 -2
- package/src/routes/index.ts +10 -1
- package/src/routes/integrations.ts +24 -50
- package/src/routes/issue-refs.ts +7 -7
- package/src/routes/orchestrator-bootstrap.ts +1 -1
- package/src/routes/orchestrator-host-registration.ts +2 -2
- package/src/routes/orchestrator-host-utilization.ts +3 -1
- package/src/routes/orchestrator-proxy.ts +2 -2
- package/src/routes/orchestrator.ts +2 -2
- package/src/routes/project-profile-instructions.ts +27 -0
- package/src/routes/project-trust.ts +2 -2
- package/src/routes/provider-config.ts +6 -6
- package/src/routes/provider-quota-config.ts +2 -2
- package/src/routes/settings.ts +5 -5
- package/src/routes/stats.ts +6 -6
- package/src/routes/task-http.ts +15 -1
- package/src/routes/task-issues.ts +11 -26
- package/src/routes/tasks.ts +22 -53
- package/src/routes/team-templates.ts +5 -5
- package/src/routes/teams-config.ts +2 -2
- package/src/routes/tokens.ts +12 -5
- package/src/routes/workflows.ts +20 -5
- package/src/routing-policy-normalize.ts +1 -2
- package/src/routing-step-defaults.ts +0 -2
- package/src/runner-token-mint-limit.ts +63 -0
- package/src/runtime-grant.ts +4 -2
- package/src/runtime-tokens.ts +39 -7
- package/src/security.ts +13 -20
- package/src/server-background.ts +1 -1
- package/src/server.ts +5 -2
- package/src/services/host-utilization.ts +23 -0
- package/src/services/idle-epics.ts +5 -6
- package/src/services/issue-lifecycle.ts +57 -61
- package/src/services/issue-refs.ts +9 -13
- package/src/services/kernel-timers-cutover-chaos-worker.ts +10 -7
- package/src/services/kernel-timers-ingress.ts +1 -1
- package/src/services/landed-ancestry.ts +19 -3
- package/src/services/memory-areas.ts +15 -51
- package/src/services/plan-ci-notice.ts +57 -0
- package/src/services/plan-ci-recovery.ts +2 -55
- package/src/services/plan-wake-digest.ts +1 -0
- package/src/services/profile-trust.ts +4 -3
- package/src/services/project-config-read.ts +70 -0
- package/src/services/project-profile-instructions.ts +54 -0
- package/src/services/provisioning-harness/budget-baseline.json +4 -4
- package/src/services/restart-spawn.ts +61 -49
- package/src/services/self-care-state.ts +2 -1
- package/src/services/self-effort.ts +5 -5
- package/src/services/send-message.ts +5 -1
- package/src/services/spawn-agent.ts +13 -2
- package/src/services/spawn-chain-inspector.ts +6 -13
- package/src/services/spawn-policy.ts +68 -0
- package/src/services/spawn-tree-cleanup.ts +2 -2
- package/src/services/task-dispatch.ts +3 -0
- package/src/services/task-state-reconcile.ts +6 -14
- package/src/services/team-templates.ts +9 -8
- package/src/services/transient-agent-reaper.ts +3 -1
- package/src/services/workflow-auto-start.ts +25 -13
- package/src/services/workflow-ci-main.ts +1 -1
- package/src/services/workflow-deferred-cleanup.ts +4 -2
- package/src/services/workflow-effect-driver.ts +2 -0
- package/src/services/workflow-effects.ts +18 -11
- package/src/services/workflow-land-notice.ts +10 -0
- package/src/services/workflow-pause-text.ts +43 -0
- package/src/services/workflow-reconcile.ts +13 -11
- package/src/services/workflow-run-view.ts +2 -0
- package/src/services/workflow-runs.ts +29 -13
- package/src/services/workflow-supervisor-state.ts +5 -0
- package/src/services/workflow-supervisor.ts +7 -3
- package/src/settings/registry.ts +1 -2
- package/src/spawn-command.ts +4 -4
- package/src/spawn-models.ts +10 -12
- package/src/spawn-placement.ts +1 -1
- package/src/spawn-targets.ts +1 -1
- package/src/startup-context-injection.ts +1 -0
- package/src/state-adoption/cd-cutover.ts +53 -7
- package/src/state-adoption/fleet-cutover.ts +35 -0
- package/src/state-adoption/fleet-plan.ts +36 -0
- package/src/state-adoption/index.ts +2 -2
- package/src/state-adoption/linux-plan.ts +2 -2
- package/src/state-adoption/linux.ts +2 -2
- package/src/state-adoption/macos-plan.ts +2 -2
- package/src/state-adoption/macos.ts +1 -1
- package/src/steward.ts +7 -6
- package/src/task-dispatch-live.ts +1 -1
- package/src/task-dispatcher.ts +1 -1
- package/src/teams-config-store.ts +4 -3
- package/src/token-db-write.ts +10 -0
- package/src/workspace-command-settle.ts +2 -1
- package/src/workspace-steward-wake.ts +2 -2
- package/node_modules/@humwick/kernel/LICENSE +0 -167
- package/node_modules/@humwick/kernel/async-entity-persistence.ts +0 -344
- package/node_modules/@humwick/kernel/async-event-log.ts +0 -107
- package/node_modules/@humwick/kernel/automations.pilot.ts +0 -147
- package/node_modules/@humwick/kernel/config.ts +0 -111
- package/node_modules/@humwick/kernel/contract.ts +0 -572
- package/node_modules/@humwick/kernel/control-authz.ts +0 -281
- package/node_modules/@humwick/kernel/control.ts +0 -645
- package/node_modules/@humwick/kernel/effects.ts +0 -493
- package/node_modules/@humwick/kernel/event-log.ts +0 -319
- package/node_modules/@humwick/kernel/http-transport.ts +0 -384
- package/node_modules/@humwick/kernel/identity-grammar.ts +0 -20
- package/node_modules/@humwick/kernel/identity.ts +0 -248
- package/node_modules/@humwick/kernel/index.ts +0 -28
- package/node_modules/@humwick/kernel/kernel-persistence.ts +0 -181
- package/node_modules/@humwick/kernel/landing-incident.ts +0 -306
- package/node_modules/@humwick/kernel/landing.ts +0 -953
- package/node_modules/@humwick/kernel/manifest-validation.ts +0 -460
- package/node_modules/@humwick/kernel/mcp-transport.ts +0 -109
- package/node_modules/@humwick/kernel/message-authority-chaos-worker.ts +0 -200
- package/node_modules/@humwick/kernel/message-authority.ts +0 -281
- package/node_modules/@humwick/kernel/module-host.ts +0 -722
- package/node_modules/@humwick/kernel/openapi-init.ts +0 -14
- package/node_modules/@humwick/kernel/package.json +0 -39
- package/node_modules/@humwick/kernel/persistence-schema.ts +0 -248
- package/node_modules/@humwick/kernel/persistence.ts +0 -510
- package/node_modules/@humwick/kernel/registry.ts +0 -60
- package/node_modules/@humwick/kernel/sqlite-kernel-persistence.ts +0 -499
- package/node_modules/@humwick/kernel/sqlite-module-host.ts +0 -87
- package/node_modules/@humwick/kernel/timers-chaos-worker.ts +0 -186
- package/node_modules/@humwick/kernel/timers.pilot.ts +0 -1411
- package/node_modules/@humwick/kernel/workflow-activation.ts +0 -25
- package/node_modules/@humwick/kernel/workflow-backstop.ts +0 -40
- package/node_modules/@humwick/kernel/workflow-cancel-tree.ts +0 -20
- package/node_modules/@humwick/kernel/workflow-chaos-worker.ts +0 -213
- package/node_modules/@humwick/kernel/workflow-composition.ts +0 -231
- package/node_modules/@humwick/kernel/workflow-container.ts +0 -72
- package/node_modules/@humwick/kernel/workflow-control.test-fixture.ts +0 -42
- package/node_modules/@humwick/kernel/workflow-control.ts +0 -178
- package/node_modules/@humwick/kernel/workflow-definition.ts +0 -507
- package/node_modules/@humwick/kernel/workflow-dispatch.ts +0 -60
- package/node_modules/@humwick/kernel/workflow-foreach.ts +0 -167
- package/node_modules/@humwick/kernel/workflow-journal-codec.ts +0 -339
- package/node_modules/@humwick/kernel/workflow-journal.ts +0 -922
- package/node_modules/@humwick/kernel/workflow-lane.ts +0 -48
- package/node_modules/@humwick/kernel/workflow-lease-owner.ts +0 -60
- package/node_modules/@humwick/kernel/workflow-recovery.test-fixture.ts +0 -19
- package/node_modules/@humwick/kernel/workflow-runtime.ts +0 -938
- package/node_modules/@humwick/kernel/workflow-script.ts +0 -11
- package/node_modules/@humwick/kernel/workflow-state.ts +0 -299
- package/node_modules/@humwick/kernel/workflow-step-commit.ts +0 -195
- package/node_modules/@humwick/kernel/workflow-transitions.ts +0 -317
- package/node_modules/@humwick/kernel/workflow-values.ts +0 -153
- package/node_modules/@humwick/kernel/workflow.pilot.ts +0 -376
- package/node_modules/@humwick/offload/LICENSE +0 -167
- package/node_modules/@humwick/offload/README.md +0 -57
- package/node_modules/@humwick/offload/dist/async-runner.d.ts +0 -18
- package/node_modules/@humwick/offload/dist/async-runner.d.ts.map +0 -1
- package/node_modules/@humwick/offload/dist/async-runner.js +0 -158
- package/node_modules/@humwick/offload/dist/async-runner.js.map +0 -1
- package/node_modules/@humwick/offload/dist/envelope.d.ts +0 -25
- package/node_modules/@humwick/offload/dist/envelope.d.ts.map +0 -1
- package/node_modules/@humwick/offload/dist/envelope.js +0 -80
- package/node_modules/@humwick/offload/dist/envelope.js.map +0 -1
- package/node_modules/@humwick/offload/dist/fixtures/worker-task.d.ts +0 -10
- package/node_modules/@humwick/offload/dist/fixtures/worker-task.d.ts.map +0 -1
- package/node_modules/@humwick/offload/dist/fixtures/worker-task.js +0 -59
- package/node_modules/@humwick/offload/dist/fixtures/worker-task.js.map +0 -1
- package/node_modules/@humwick/offload/dist/index.d.ts +0 -5
- package/node_modules/@humwick/offload/dist/index.d.ts.map +0 -1
- package/node_modules/@humwick/offload/dist/index.js +0 -5
- package/node_modules/@humwick/offload/dist/index.js.map +0 -1
- package/node_modules/@humwick/offload/dist/subprocess-runner.d.ts +0 -62
- package/node_modules/@humwick/offload/dist/subprocess-runner.d.ts.map +0 -1
- package/node_modules/@humwick/offload/dist/subprocess-runner.js +0 -295
- package/node_modules/@humwick/offload/dist/subprocess-runner.js.map +0 -1
- package/node_modules/@humwick/offload/dist/types.d.ts +0 -111
- package/node_modules/@humwick/offload/dist/types.d.ts.map +0 -1
- package/node_modules/@humwick/offload/dist/types.js +0 -176
- package/node_modules/@humwick/offload/dist/types.js.map +0 -1
- package/node_modules/@humwick/offload/dist/worker-pool.d.ts +0 -23
- package/node_modules/@humwick/offload/dist/worker-pool.d.ts.map +0 -1
- package/node_modules/@humwick/offload/dist/worker-pool.js +0 -435
- package/node_modules/@humwick/offload/dist/worker-pool.js.map +0 -1
- package/node_modules/@humwick/offload/dist/worker-runtime.d.ts +0 -2
- package/node_modules/@humwick/offload/dist/worker-runtime.d.ts.map +0 -1
- package/node_modules/@humwick/offload/dist/worker-runtime.js +0 -27
- package/node_modules/@humwick/offload/dist/worker-runtime.js.map +0 -1
- package/node_modules/@humwick/offload/dist/worker.d.ts +0 -5
- package/node_modules/@humwick/offload/dist/worker.d.ts.map +0 -1
- package/node_modules/@humwick/offload/dist/worker.js +0 -1
- package/node_modules/@humwick/offload/dist/worker.js.map +0 -1
- package/node_modules/@humwick/offload/package.json +0 -42
- package/node_modules/@humwick/offload/src/async-runner.ts +0 -170
- package/node_modules/@humwick/offload/src/envelope.ts +0 -92
- package/node_modules/@humwick/offload/src/fixtures/worker-task.ts +0 -49
- package/node_modules/@humwick/offload/src/index.ts +0 -4
- package/node_modules/@humwick/offload/src/subprocess-runner.ts +0 -334
- package/node_modules/@humwick/offload/src/types.ts +0 -235
- package/node_modules/@humwick/offload/src/worker-pool.ts +0 -463
- package/node_modules/@humwick/offload/src/worker-runtime.ts +0 -32
- package/node_modules/@humwick/offload/src/worker.ts +0 -5
- package/public/assets/agent-liveness-DyqxfB-t.js +0 -2
- package/public/assets/agent-profiles-BpI_jTxq.js +0 -2
- package/public/assets/index-B7ZerHLY.css +0 -2
- package/public/assets/index-BU3uWo2t.js +0 -15
- package/public/assets/projects-B_lmcqfO.js +0 -2
- package/public/assets/provider-display-DbLWpP3j.js +0 -2
- package/public/assets/run-card-CO_bguQf.js +0 -2
- package/public/assets/use-project-list-B-T2Bnnn.js +0 -2
- package/public/assets/versioned-prompt-editor-Diu8BIaM.js +0 -8
- package/runner/src/gate-run-lease-probe.ts +0 -38
- package/src/db/claude-catalog-reconcile.ts +0 -27
- 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
|
-
}
|