@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.
- package/README.md +5 -0
- package/docs/guides/tools-administration.md +6 -1
- 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-C7jQWbw2.js +2 -0
- 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 +24 -3
- 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 +10 -6
- package/src/commands-write.ts +8 -0
- 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 +13 -1
- package/src/db/activity.ts +1 -14
- package/src/db/agent-lifecycle-write-ops.ts +1 -0
- package/src/db/agent-meta.ts +2 -2
- package/src/db/agent-ownership-write-ops.ts +4 -0
- package/src/db/agent-search.ts +1 -15
- package/src/db/agents-async.ts +58 -7
- package/src/db/agents-write-ops.ts +28 -5
- package/src/db/agents.ts +81 -54
- package/src/db/artifacts-write-ops.ts +8 -3
- package/src/db/bus-outbox-write-ops.ts +1 -0
- package/src/db/channels-write-ops.ts +4 -0
- package/src/db/ci-incidents-async.ts +5 -5
- package/src/db/ci-incidents-write-ops.ts +2 -2
- package/src/db/claude-api-budget-ops.ts +5 -5
- package/src/db/claude-app-oauth.ts +4 -4
- package/src/db/codex-reset-consumes-write-ops.ts +3 -0
- 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/commands-write-ops.ts +21 -0
- package/src/db/config-store-write-ops.ts +9 -2
- package/src/db/continuation-archives-async.ts +5 -0
- package/src/db/continuation-archives-write-ops.ts +9 -0
- package/src/db/continuations.ts +2 -2
- package/src/db/db-writer-worker.ts +8 -1
- package/src/db/delivery-write-ops.ts +5 -0
- 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-write-ops.ts +14 -0
- package/src/db/hosts.ts +9 -6
- package/src/db/inbox-write-ops.ts +4 -0
- package/src/db/inbox.ts +0 -14
- package/src/db/index.ts +2 -0
- package/src/db/insights-write-ops.ts +1 -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/macos-privacy.ts +1 -1
- 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/message-reads-write-ops.ts +3 -0
- package/src/db/message-write-ops.ts +4 -0
- package/src/db/migrations.ts +7 -15
- package/src/db/orchestrators-async.ts +7 -3
- package/src/db/orchestrators-write-ops.ts +7 -1
- package/src/db/orchestrators.ts +21 -77
- package/src/db/pairs.ts +1 -13
- package/src/db/project-profile-instructions.ts +50 -0
- package/src/db/prompt-versions-ops.ts +1 -0
- package/src/db/prompt-versions.ts +9 -2
- package/src/db/provider-catalog-write-ops.ts +4 -4
- package/src/db/provider-config-history.ts +8 -3
- package/src/db/provider-flap-messages-write-ops.ts +1 -0
- package/src/db/provider-model-seed.ts +34 -0
- package/src/db/provider-quota-lease-write-ops.ts +1 -0
- package/src/db/provider-quota-write-ops.ts +2 -0
- package/src/db/provisioning-write-ops.ts +9 -8
- package/src/db/read-op-marshaling-fixtures.ts +5 -0
- package/src/db/read-ops.ts +4 -0
- package/src/db/routing-policy.ts +3 -4
- package/src/db/schema.ts +9 -3
- package/src/db/secrets-broker-write-ops.ts +5 -5
- package/src/db/secrets-vault-write.ts +7 -7
- package/src/db/services-write-ops.ts +3 -0
- package/src/db/spawn-delegation-state.ts +11 -4
- package/src/db/spawn-tree-cleanup.ts +3 -0
- 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/team-spawn-delegation.ts +1 -1
- package/src/db/teams-write-ops.ts +4 -4
- package/src/db/timer-cutover-write-ops.ts +7 -0
- package/src/db/timer-ingress-write-ops.ts +9 -0
- package/src/db/timer-ingress.ts +7 -1
- package/src/db/timer-operator-write-ops.ts +14 -0
- package/src/db/timer-transfer-write-ops.ts +4 -0
- package/src/db/timer-write-ops.ts +14 -0
- package/src/db/token-db-write-ops.ts +21 -0
- package/src/db/token-standing.ts +15 -11
- package/src/db/user-settings-write-ops.ts +1 -0
- package/src/db/vault-backup.ts +2 -2
- package/src/db/vault-expiry.ts +2 -2
- package/src/db/vault-health.ts +3 -3
- package/src/db/vault-token-ops.ts +8 -8
- package/src/db/web-tool-call-ops.ts +3 -3
- package/src/db/workflow-contact.ts +1 -1
- package/src/db/workflow-task-read-ops.ts +30 -0
- package/src/db/write-ops.ts +15 -1
- package/src/db/writer-fault-seam.ts +5 -2
- package/src/db/writer-gateway.ts +21 -12
- 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-host-tools.ts +1 -1
- 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/package-pack.ts +42 -39
- 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/host-resource-control.ts +4 -1
- 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 +3 -3
- 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 +4 -4
- package/src/routes/project-profile-instructions.ts +27 -0
- package/src/routes/project-trust.ts +2 -2
- package/src/routes/provider-config.ts +9 -8
- 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 +50 -9
- 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 +12 -7
- package/src/services/kernel-timers-ingress.ts +1 -1
- package/src/services/kernel-timers-request-key-migration-chaos-worker.ts +4 -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 -47
- 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 -46
- 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/fleet-BL4UXS_k.js +0 -2
- package/public/assets/index-B7ZerHLY.css +0 -2
- package/public/assets/index-BK0WV6hF.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,722 +0,0 @@
|
|
|
1
|
-
import type { z } from "zod";
|
|
2
|
-
import {
|
|
3
|
-
type Clock,
|
|
4
|
-
type CommandClient,
|
|
5
|
-
type CommandTarget,
|
|
6
|
-
type Grant,
|
|
7
|
-
type HealthReporter,
|
|
8
|
-
type HostCommandDecl,
|
|
9
|
-
type HostIdentity,
|
|
10
|
-
type HostCommandRun,
|
|
11
|
-
type HostCommandStatus,
|
|
12
|
-
type Logger,
|
|
13
|
-
type JsonValue,
|
|
14
|
-
type ModuleCtx,
|
|
15
|
-
type ModuleDefinition,
|
|
16
|
-
type ModuleManifest,
|
|
17
|
-
type Principal,
|
|
18
|
-
type ResolvedIdentity,
|
|
19
|
-
type Rng,
|
|
20
|
-
type Scope,
|
|
21
|
-
type SecretsReader,
|
|
22
|
-
type TraceContext,
|
|
23
|
-
type Transport,
|
|
24
|
-
type WaitCondition,
|
|
25
|
-
type WorkflowClient,
|
|
26
|
-
type WorkflowDecl,
|
|
27
|
-
type WorkflowRun,
|
|
28
|
-
type WorkflowStartOptions,
|
|
29
|
-
OpError,
|
|
30
|
-
} from "./contract";
|
|
31
|
-
import { type ConfigResolveOptions, resolveConfig } from "./config";
|
|
32
|
-
import { createNetTransport, createRealClock, createSilentLogger, createSystemRng } from "./effects";
|
|
33
|
-
import { AuthorizationError, isAuthorized, resolveIdentity } from "./identity";
|
|
34
|
-
import {
|
|
35
|
-
EntityConflictError,
|
|
36
|
-
EntityNotFoundError,
|
|
37
|
-
} from "./persistence";
|
|
38
|
-
import type { ControlModuleDeps } from "./control";
|
|
39
|
-
import { createWorkflowRuntime, type PreparedWorkflowStart, type WaitEvaluator, type WorkflowRuntime } from "./workflow-runtime";
|
|
40
|
-
import type { WorkflowStepCommitFaults } from "./workflow-step-commit";
|
|
41
|
-
import {
|
|
42
|
-
beginMessageAuthoritySession,
|
|
43
|
-
type MessageAuthoritySession,
|
|
44
|
-
} from "./message-authority";
|
|
45
|
-
import { AsyncEntityPersistence, type AsyncEntitySession } from "./async-entity-persistence";
|
|
46
|
-
import { AsyncEventLogPersistence, type AsyncEventSession } from "./async-event-log";
|
|
47
|
-
import type { KernelPersistence } from "./kernel-persistence";
|
|
48
|
-
import { recoverIndeterminate } from "./kernel-persistence";
|
|
49
|
-
import { armCompatibilityOpFaults } from "./sqlite-kernel-persistence";
|
|
50
|
-
|
|
51
|
-
/** The single owning scope on the single-tenant path (§2.0). The kernel injects it; modules
|
|
52
|
-
* never see where it came from, and today it is always `local`. */
|
|
53
|
-
export const LOCAL_SCOPE = "local";
|
|
54
|
-
|
|
55
|
-
type AnyModuleDefinition = ModuleDefinition<ModuleManifest>;
|
|
56
|
-
|
|
57
|
-
/** The caller identity for one invocation. The owning scope is kernel-injected (`local`), so
|
|
58
|
-
* callers supply only who they are and what they may do. */
|
|
59
|
-
export interface InvocationIdentity {
|
|
60
|
-
readonly actor: string;
|
|
61
|
-
readonly grants: readonly Grant[];
|
|
62
|
-
/** Opaque server-resolved creator attributes the TRUSTED host binding attaches from the
|
|
63
|
-
* authenticated token (scopes/constraints/kind) so a module can freeze a durable effect's
|
|
64
|
-
* create-time identity under the creator's REAL reach (P2-7). Rides this identity — NOT the
|
|
65
|
-
* client-facing op input — so a client cannot supply or widen it. See {@link ResolvedIdentity}. */
|
|
66
|
-
readonly resolvedIdentity?: ResolvedIdentity;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/** Transport-owned commit identity for one mutation invocation. Supplying this lets an HTTP/MCP
|
|
70
|
-
* boundary carry its stable request key all the way to Kernel opCommit; callers cannot influence
|
|
71
|
-
* entity versions, only deduplicate an otherwise identical atomic commit. */
|
|
72
|
-
export interface InvocationOptions {
|
|
73
|
-
readonly requestKey?: string;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/** A capability the K6a in-process host does not yet provide (K6b / workflow-runtime milestones). */
|
|
77
|
-
export class KernelNotAvailableError extends Error {
|
|
78
|
-
constructor(readonly feature: string) {
|
|
79
|
-
super(`kernel feature '${feature}' is not available in the K6a in-process host`);
|
|
80
|
-
this.name = "KernelNotAvailableError";
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/** A module issued `ctx.commands.issue(name, …)` for a command its manifest never declared. Fail
|
|
85
|
-
* closed: an undeclared command has no grant/target/idempotency mapping, so it can never dispatch. */
|
|
86
|
-
export class UnknownHostCommandError extends Error {
|
|
87
|
-
constructor(readonly moduleId: string, readonly commandName: string) {
|
|
88
|
-
super(`module '${moduleId}' declares no command '${commandName}'`);
|
|
89
|
-
this.name = "UnknownHostCommandError";
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* The single, resolved shape the kernel hands the src side for ONE `ctx.commands.issue(...)` call.
|
|
95
|
-
* The kernel builds it from the module's {@link HostCommandDecl} (the declared `grant`/`timeoutMs`/
|
|
96
|
-
* `target`/`whenDegraded` and the computed `idempotencyKey`) plus the issue call-site (`name`/`input`/
|
|
97
|
-
* `target`). It is intentionally NARROW — everything the src adapter needs to route the issue through
|
|
98
|
-
* `dispatchCommand(...)` and nothing more; the module cannot smuggle extra authority through it.
|
|
99
|
-
*/
|
|
100
|
-
export interface HostCommandIssueRequest {
|
|
101
|
-
/** The declaring module's id — namespaces the idempotency key so two modules cannot collide. */
|
|
102
|
-
readonly moduleId: string;
|
|
103
|
-
/** The declared command name (the key in the manifest's `commands` map). */
|
|
104
|
-
readonly commandName: string;
|
|
105
|
-
/** The caller's input, ALREADY validated against the decl's `input` schema by the kernel. */
|
|
106
|
-
readonly input: unknown;
|
|
107
|
-
/** The call-site target host (`ctx.commands.issue`'s third arg). */
|
|
108
|
-
readonly target: CommandTarget;
|
|
109
|
-
/** The decl's target CLASS — whether the command runs on the host-agent or a runner. */
|
|
110
|
-
readonly targetKind: "host-agent" | "runner";
|
|
111
|
-
/** The decl's `grant` (e.g. `command:run-command`). The src adapter maps this to — and must never
|
|
112
|
-
* widen beyond — the legacy `command:write` + cwd-constrained authz that `dispatchCommand` gates. */
|
|
113
|
-
readonly grant: Grant;
|
|
114
|
-
/** `decl.idempotencyKey(input)` — the dedup token a re-drive re-presents so a crash before the
|
|
115
|
-
* journal memo dispatches onto the SAME orchestrator command instead of double-dispatching. */
|
|
116
|
-
readonly idempotencyKey: string;
|
|
117
|
-
readonly timeoutMs: number;
|
|
118
|
-
readonly whenDegraded: "queue" | "reject";
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* The narrow host seam backing `ctx.commands`. The kernel declares it; the SRC side implements it by
|
|
123
|
-
* mapping `issue` onto the authz-gated command dispatch (which runs `assertCommandAuthorized` → the
|
|
124
|
-
* #968 cwd gate) and `status` onto a command read. The kernel never imports src: it only ever holds
|
|
125
|
-
* this interface, so a module can reach the command bus ONLY through the authz-gated adapter.
|
|
126
|
-
*
|
|
127
|
-
* These command methods are ASYNC (#1910 K2): the src adapter routes both through the async DB gateway
|
|
128
|
-
* (`issue` atomically find-or-creates the correlation-keyed command off the loop; `status` reads it via
|
|
129
|
-
* the reader pool), so a command-timer firing workflow never touches a raw `Database` on the main loop.
|
|
130
|
-
* `ctx.commands` awaits them under the contract's Promise-returning {@link CommandClient}.
|
|
131
|
-
*/
|
|
132
|
-
export interface HostCommandDispatcher {
|
|
133
|
-
issue(request: HostCommandIssueRequest): Promise<HostCommandRun>;
|
|
134
|
-
status(commandId: string): Promise<HostCommandRun | undefined>;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/** A {@link HostCommandStatus} is TERMINAL once the orchestrator has settled the command — the only
|
|
138
|
-
* point a `{kind:"command"}` wait is allowed to fire. `done`/`failed` are the two terminal projections
|
|
139
|
-
* the src adapter maps every settled orchestrator status onto (succeeded → done; failed/timed_out/
|
|
140
|
-
* rejected/canceled → failed). */
|
|
141
|
-
const TERMINAL_HOST_COMMAND_STATUSES: ReadonlySet<HostCommandStatus> = new Set<HostCommandStatus>(["done", "failed"]);
|
|
142
|
-
|
|
143
|
-
function isTerminalHostCommandStatus(status: HostCommandStatus): boolean {
|
|
144
|
-
return TERMINAL_HOST_COMMAND_STATUSES.has(status);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
/** An event-log record matches a `{kind:"event"}` wait's `correlationId` when its payload is an
|
|
148
|
-
* object carrying a `correlationId` field equal to it. This is the single, documented convention the
|
|
149
|
-
* automations pilot emits under; a wait with no `correlationId` matches on topic alone. */
|
|
150
|
-
/** Whether the scoped event log holds ANY event matching a `{kind:"event"}` wait — its `topic` (the
|
|
151
|
-
* event name) and, when present, its `correlationId`. Pages through the whole scope so a match late
|
|
152
|
-
* in the log is still found; a pure, side-effect-free read the wakeup pass may run more than once. */
|
|
153
|
-
/** The effect providers bound into every ModuleCtx. */
|
|
154
|
-
export interface HostEffects {
|
|
155
|
-
readonly clock: Clock;
|
|
156
|
-
readonly rng: Rng;
|
|
157
|
-
readonly net: Transport;
|
|
158
|
-
readonly log: Logger;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
interface ModuleHostBaseOptions {
|
|
162
|
-
readonly clock?: Clock;
|
|
163
|
-
readonly rng?: Rng;
|
|
164
|
-
readonly net?: Transport;
|
|
165
|
-
readonly log?: Logger;
|
|
166
|
-
/** Executor lease TTL in ms for the workflow runtime; defaults to the runtime's own default. */
|
|
167
|
-
readonly leaseTtlMs?: number;
|
|
168
|
-
/** The src adapter backing `ctx.commands` — maps a module's `issue`/`status` onto the authz-gated
|
|
169
|
-
* `dispatchCommand`/`getCommand` machinery (see {@link HostCommandDispatcher}). Omitted → the
|
|
170
|
-
* in-process host has no command bus: `ctx.commands.issue`/`status` reject (fail-closed) and a
|
|
171
|
-
* `{kind:"command"}` wait never fires. */
|
|
172
|
-
readonly commands?: HostCommandDispatcher;
|
|
173
|
-
/** Overrides the wait evaluator the M3 wakeup pass consults ({@link WorkflowRuntime.wakeup}). When
|
|
174
|
-
* omitted the host builds the DEFAULT evaluator: a `{kind:"command"}` wait fires once the wired
|
|
175
|
-
* {@link HostCommandDispatcher} reports the command terminal, and a `{kind:"event"}` wait fires once
|
|
176
|
-
* the kernel event log holds a matching event. Provide this only to fully override that (tests). */
|
|
177
|
-
readonly workflowWaitEvaluator?: WaitEvaluator;
|
|
178
|
-
/** In-process step-commit fault injection (TEST/CHAOS only). The host arms the returned faults on the
|
|
179
|
-
* persistence port under the step's internally-generated request key; it is a no-op for a persistence
|
|
180
|
-
* backend that does not read the compatibility fault WeakMap (e.g. the async gateway adapter). */
|
|
181
|
-
readonly workflowCommitFaults?: (instanceId: string, stepName: string) => WorkflowStepCommitFaults | undefined;
|
|
182
|
-
/** In-process op-commit fault injection (TEST/CHAOS only). Same arming discipline as
|
|
183
|
-
* {@link workflowCommitFaults}; a no-op for a non-SqliteKernelPersistence backend. */
|
|
184
|
-
readonly opCommitFaults?: (moduleId: string, opName: string) => OpCommitFaults | undefined;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
/**
|
|
188
|
-
* The kernel host is ALWAYS built on an async {@link KernelPersistence} port (K3, #1910) — no raw
|
|
189
|
-
* `Database` is ever handed to the host. Production builds the gateway-backed adapter
|
|
190
|
-
* (`createGatewayKernelPersistence`); in-process unit tests and kill-9 chaos harnesses compose a
|
|
191
|
-
* `SqliteKernelPersistence` through the `createSqliteModuleHost` test-support helper. The old
|
|
192
|
-
* `{ db }` K0 compatibility branch is removed.
|
|
193
|
-
*/
|
|
194
|
-
export interface ModuleHostOptions extends ModuleHostBaseOptions {
|
|
195
|
-
readonly persistence: KernelPersistence;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
/** The armable faults for ONE op invocation (see {@link ModuleHostOptions.opCommitFaults}). */
|
|
199
|
-
export interface OpCommitFaults {
|
|
200
|
-
/** Awaited between the handler/output-validation and the atomic commit. A throw aborts the op
|
|
201
|
-
* pre-commit (sessions discarded, nothing persisted). */
|
|
202
|
-
readonly beforeCommit?: () => void | Promise<void>;
|
|
203
|
-
/** Fires in the tx after participant SQL and before the entity flush. */
|
|
204
|
-
readonly afterParticipantBeforeEntities?: () => void;
|
|
205
|
-
/** Fires in the tx between entity and event flushes. */
|
|
206
|
-
readonly betweenEntitiesAndEvents?: () => void;
|
|
207
|
-
/** Fires in the tx after event flush and before enlisted workflow seeds. */
|
|
208
|
-
readonly afterEventsBeforeWorkflowSeeds?: () => void;
|
|
209
|
-
/** Fires after enlisted workflow seed SQL, before COMMIT. */
|
|
210
|
-
readonly afterWorkflowSeedsBeforeCommit?: () => void;
|
|
211
|
-
/** Fires after the durable COMMIT and before the invocation ACK is returned. */
|
|
212
|
-
readonly afterCommit?: () => void;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
export interface ModuleRegisterOptions {
|
|
216
|
-
readonly config?: ConfigResolveOptions;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
interface HostedModule {
|
|
220
|
-
readonly module: AnyModuleDefinition;
|
|
221
|
-
readonly config: unknown;
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
function traceFor(rng: Rng): TraceContext {
|
|
225
|
-
const build = (traceId: string, spanId: string): TraceContext => ({
|
|
226
|
-
traceId,
|
|
227
|
-
spanId,
|
|
228
|
-
child: () => build(traceId, rng.uuid()),
|
|
229
|
-
});
|
|
230
|
-
return build(rng.uuid(), rng.uuid());
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
function errMessage(error: unknown): string {
|
|
234
|
-
return error instanceof Error ? error.message : String(error);
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
/** One `enlist: "op-commit"` workflow start staged during an op handler: its prepared seed executes
|
|
238
|
-
* inside the op's atomic commit; `instanceId` records the authoritative id the seed returned so the
|
|
239
|
-
* post-commit drive targets the deduped instance, not the provisional stub. */
|
|
240
|
-
interface PendingOpWorkflowStart {
|
|
241
|
-
readonly prepared: PreparedWorkflowStart;
|
|
242
|
-
instanceId?: string;
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
interface PendingOpWorkflowControl {
|
|
246
|
-
readonly operation: "cancel" | "fail" | "reopen";
|
|
247
|
-
readonly instanceId: string;
|
|
248
|
-
readonly failure?: JsonValue;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
function unavailableCommands(): CommandClient<unknown> {
|
|
252
|
-
return {
|
|
253
|
-
issue: () => Promise.reject(new KernelNotAvailableError("commands")),
|
|
254
|
-
status: () => Promise.reject(new KernelNotAvailableError("commands")),
|
|
255
|
-
} as unknown as CommandClient<unknown>;
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
/**
|
|
259
|
-
* The real `ctx.commands` bound to one module + the wired {@link HostCommandDispatcher}. `issue`
|
|
260
|
-
* looks up the named {@link HostCommandDecl}, validates the input against its schema (a parse failure
|
|
261
|
-
* rejects — fail-closed), computes the decl's `idempotencyKey`, and hands the resolved
|
|
262
|
-
* {@link HostCommandIssueRequest} to the dispatcher. The dispatcher is the ONLY path to the command
|
|
263
|
-
* bus, so every issue routes through its authz gate; an undeclared command name rejects before any
|
|
264
|
-
* dispatch. The contract's Promise-returning methods wrap the synchronous dispatcher.
|
|
265
|
-
*/
|
|
266
|
-
function commandClientFor(module: AnyModuleDefinition, dispatcher: HostCommandDispatcher): CommandClient<unknown> {
|
|
267
|
-
const decls = (module.commands ?? {}) as Record<string, HostCommandDecl>;
|
|
268
|
-
const client = {
|
|
269
|
-
issue: async (name: string, input: unknown, target: CommandTarget): Promise<HostCommandRun> => {
|
|
270
|
-
const decl = decls[name];
|
|
271
|
-
if (!decl) throw new UnknownHostCommandError(module.id, name);
|
|
272
|
-
// Validate against the decl schema BEFORE computing the idempotency key or dispatching, so the
|
|
273
|
-
// key is derived from a well-formed input and a malformed issue fails closed (never dispatches).
|
|
274
|
-
const parsed = decl.input.parse(input) as unknown;
|
|
275
|
-
const request: HostCommandIssueRequest = {
|
|
276
|
-
moduleId: module.id,
|
|
277
|
-
commandName: name,
|
|
278
|
-
input: parsed,
|
|
279
|
-
target,
|
|
280
|
-
targetKind: decl.target,
|
|
281
|
-
grant: decl.grant,
|
|
282
|
-
idempotencyKey: decl.idempotencyKey(parsed),
|
|
283
|
-
timeoutMs: decl.timeoutMs,
|
|
284
|
-
whenDegraded: decl.whenDegraded ?? "reject",
|
|
285
|
-
};
|
|
286
|
-
return dispatcher.issue(request);
|
|
287
|
-
},
|
|
288
|
-
status: async (id: string): Promise<HostCommandRun | undefined> => dispatcher.status(id),
|
|
289
|
-
};
|
|
290
|
-
return client as unknown as CommandClient<unknown>;
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
function unavailableSecrets(): SecretsReader<unknown> {
|
|
294
|
-
return { get: () => Promise.reject(new KernelNotAvailableError("secrets")) } as unknown as SecretsReader<unknown>;
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
/** No-op reporter: the host has nothing to serve health probes to yet, and no capabilities. */
|
|
298
|
-
function inertHealth(): HealthReporter {
|
|
299
|
-
return { report: () => {}, capability: () => Promise.resolve(false) };
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
function fieldsFromZodError(error: z.ZodError): Readonly<Record<string, readonly string[]>> {
|
|
303
|
-
const fields: Record<string, string[]> = {};
|
|
304
|
-
for (const issue of error.issues) {
|
|
305
|
-
const key = issue.path.map(String).join(".") || "(root)";
|
|
306
|
-
(fields[key] ??= []).push(issue.message);
|
|
307
|
-
}
|
|
308
|
-
return fields;
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
/** Translate the primitives' internal failures into the module-visible typed `OpError`. Unknown
|
|
312
|
-
* handler errors propagate unchanged (the rollback has already happened by the time we get here). */
|
|
313
|
-
function mapInvocationError(error: unknown): unknown {
|
|
314
|
-
if (error instanceof OpError) return error;
|
|
315
|
-
if (error instanceof AuthorizationError) return new OpError("unauthorized", 403, error.message);
|
|
316
|
-
if (error instanceof EntityConflictError) return new OpError("conflict", 409, error.message);
|
|
317
|
-
if (error instanceof EntityNotFoundError) return new OpError("not_found", 404, error.message);
|
|
318
|
-
return error;
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
/**
|
|
322
|
-
* The K6a in-process serving core. Registers `defineModule` manifests, resolves each one's
|
|
323
|
-
* config once (no live-reload), and serves its ops through {@link ModuleHost.invoke}: identity
|
|
324
|
-
* resolution with a kernel-injected `local` scope, default-deny authorization, Zod I/O
|
|
325
|
-
* validation, and an atomic entity+event commit. Transport derivation (HTTP/MCP/CLI) is K6b.
|
|
326
|
-
*/
|
|
327
|
-
export class ModuleHost {
|
|
328
|
-
/**
|
|
329
|
-
* This host's OPAQUE identity (#1506) — a JS-PRIVATE, set-once field, deliberately NOT a public
|
|
330
|
-
* `readonly` property. A TS `readonly` field is writable at runtime, so it could be REASSIGNED
|
|
331
|
-
* (`host.identity = …`) and a module that keyed private state by it would then bind to the replacement:
|
|
332
|
-
* that is a second seam. `#identity` cannot be reassigned or shadowed from outside the class, and the
|
|
333
|
-
* ctx below is built from it DIRECTLY, so shadowing the public {@link identity} getter can only produce
|
|
334
|
-
* an inert binding — never one this host will ever present.
|
|
335
|
-
*
|
|
336
|
-
* It is frozen and carries nothing; its only meaningful property is REFERENCE identity, so a look-alike
|
|
337
|
-
* object is never this host.
|
|
338
|
-
*/
|
|
339
|
-
readonly #identity: HostIdentity = Object.freeze({ hostIdentity: true as const });
|
|
340
|
-
readonly #kernelPersistence: KernelPersistence;
|
|
341
|
-
readonly #effects: HostEffects;
|
|
342
|
-
readonly #persistence: AsyncEntityPersistence;
|
|
343
|
-
readonly #eventLog: AsyncEventLogPersistence;
|
|
344
|
-
readonly #modules = new Map<string, HostedModule>();
|
|
345
|
-
readonly #leaseTtlMs: number | undefined;
|
|
346
|
-
readonly #commands: HostCommandDispatcher | undefined;
|
|
347
|
-
/** The resolved wait evaluator handed to every runtime: the caller's override if given, else the
|
|
348
|
-
* DEFAULT composite built from the command dispatcher (`command` waits) + event log (`event` waits). */
|
|
349
|
-
readonly #waitEvaluator: WaitEvaluator;
|
|
350
|
-
readonly #workflowCommitFaults: ((instanceId: string, stepName: string) => WorkflowStepCommitFaults | undefined) | undefined;
|
|
351
|
-
readonly #opCommitFaults: ((moduleId: string, opName: string) => OpCommitFaults | undefined) | undefined;
|
|
352
|
-
/** Per-module schema-install promises from {@link register}; {@link whenReady} awaits them all. */
|
|
353
|
-
readonly #registrations: Promise<void>[] = [];
|
|
354
|
-
|
|
355
|
-
/** Read-only view of {@link ModuleHost.#identity}, for a module that must bind private state to this
|
|
356
|
-
* host (the #1506 admission capability). There is NO setter: the backing field is JS-private, so this
|
|
357
|
-
* can be shadowed on an instance but never redirected — anything built from a shadow is inert, because
|
|
358
|
-
* every ctx carries the private field's value. */
|
|
359
|
-
get identity(): HostIdentity {
|
|
360
|
-
return this.#identity;
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
constructor(options: ModuleHostOptions) {
|
|
364
|
-
this.#leaseTtlMs = options.leaseTtlMs;
|
|
365
|
-
this.#commands = options.commands;
|
|
366
|
-
this.#workflowCommitFaults = options.workflowCommitFaults;
|
|
367
|
-
this.#opCommitFaults = options.opCommitFaults;
|
|
368
|
-
this.#effects = {
|
|
369
|
-
clock: options.clock ?? createRealClock(),
|
|
370
|
-
rng: options.rng ?? createSystemRng(),
|
|
371
|
-
net: options.net ?? createNetTransport(),
|
|
372
|
-
log: options.log ?? createSilentLogger(),
|
|
373
|
-
};
|
|
374
|
-
// The host holds ONLY the async persistence port — never a raw Database (K3, #1910). In-process
|
|
375
|
-
// tests/chaos compose a SqliteKernelPersistence via `createSqliteModuleHost`; production injects the
|
|
376
|
-
// gateway-backed adapter.
|
|
377
|
-
this.#kernelPersistence = options.persistence;
|
|
378
|
-
this.#persistence = new AsyncEntityPersistence(this.#kernelPersistence);
|
|
379
|
-
this.#eventLog = new AsyncEventLogPersistence(this.#kernelPersistence);
|
|
380
|
-
this.#waitEvaluator = this.#resolveWaitEvaluator(options.workflowWaitEvaluator);
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
/**
|
|
384
|
-
* Build the wait evaluator the M3 wakeup pass consults. An explicit override wins verbatim
|
|
385
|
-
* (tests / bespoke hosts). Otherwise the DEFAULT composite resolves the two ratified
|
|
386
|
-
* {@link WaitCondition} kinds: a `{kind:"command"}` fires once the wired {@link HostCommandDispatcher}
|
|
387
|
-
* reports the command terminal (never fires when no dispatcher is wired — fail-closed), and a
|
|
388
|
-
* `{kind:"event"}` fires once the kernel's own event log holds a matching event. Kept
|
|
389
|
-
* side-effect-free: the runtime re-invokes it while scanning AND under the acquired lease.
|
|
390
|
-
*/
|
|
391
|
-
#resolveWaitEvaluator(override: WaitEvaluator | undefined): WaitEvaluator {
|
|
392
|
-
if (override) return override;
|
|
393
|
-
const dispatcher = this.#commands;
|
|
394
|
-
const persistence = this.#kernelPersistence;
|
|
395
|
-
return async (waitFor: WaitCondition): Promise<boolean> => {
|
|
396
|
-
if (waitFor.kind === "command") {
|
|
397
|
-
if (!dispatcher) return false;
|
|
398
|
-
const run = await dispatcher.status(waitFor.commandId);
|
|
399
|
-
return run !== undefined && isTerminalHostCommandStatus(run.status);
|
|
400
|
-
}
|
|
401
|
-
return persistence.eventHasMatch(LOCAL_SCOPE as unknown as Scope, waitFor.topic, waitFor.correlationId);
|
|
402
|
-
};
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
/** Install a module: derive its tables (K4) and event schemas (K5) and resolve its config. Schema
|
|
406
|
-
* installation is async (it routes through the persistence port's writer); {@link invoke} awaits the
|
|
407
|
-
* per-module readiness before touching a table, so callers need not await `register`. A caller that
|
|
408
|
-
* observes the installed schema OUT OF BAND (not through an op) awaits {@link whenReady} first. */
|
|
409
|
-
register<M extends ModuleManifest>(module: ModuleDefinition<M>, options: ModuleRegisterOptions = {}): void {
|
|
410
|
-
if (this.#modules.has(module.id)) throw new Error(`module '${module.id}' is already registered with the host`);
|
|
411
|
-
this.#registrations.push(this.#persistence.register(module));
|
|
412
|
-
this.#eventLog.register(module);
|
|
413
|
-
const config = resolveConfig(module.config, options.config ?? {});
|
|
414
|
-
this.#modules.set(module.id, { module: module as AnyModuleDefinition, config });
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
/** Resolve once every registered module's async schema install has committed. Callers that build a
|
|
418
|
-
* host and then observe its tables OUT OF BAND (e.g. the control host's cross-module observability,
|
|
419
|
-
* or a test asserting the ON-gated schema exists) await this so the sync read cannot race the writer.
|
|
420
|
-
* Invocation self-gates on per-module readiness, so the op path does not need this. */
|
|
421
|
-
async whenReady(): Promise<void> {
|
|
422
|
-
await Promise.all(this.#registrations);
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
/** The resolved, validated config for a registered module (the value handed to `ctx.config`). */
|
|
426
|
-
configFor<M extends ModuleManifest>(module: ModuleDefinition<M>): unknown {
|
|
427
|
-
return this.#require(module.id).config;
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
/**
|
|
431
|
-
* The kernel-wide read/control capabilities the `control` module (KAPI-1 Wave 2) closes over.
|
|
432
|
-
* A module's `ctx` only ever sees its OWN entities/events, so cross-module observability +
|
|
433
|
-
* control cannot ride `ctx`; the control module is built from these injected reader/controller
|
|
434
|
-
* interfaces instead (KAPI-0 §6.1 decision 1 — "hang off the host"). Exposes the host's own
|
|
435
|
-
* journal/event/entity persistence narrowed to the read + control-primitive surface; the control
|
|
436
|
-
* module's ops apply owner-scoping (G-M1) + default-redact (K-C3) on top — these are raw/ungated.
|
|
437
|
-
*/
|
|
438
|
-
kernelControlDeps(): ControlModuleDeps {
|
|
439
|
-
const port = this.#kernelPersistence;
|
|
440
|
-
const control = (operation: "cancel" | "pause" | "resume" | "reopen", id: string): Promise<boolean> => {
|
|
441
|
-
const requestKey = this.#effects.rng.uuid();
|
|
442
|
-
const now = this.#effects.clock.now();
|
|
443
|
-
return recoverIndeterminate(() => port.controlWorkflow(requestKey, operation, id, now));
|
|
444
|
-
};
|
|
445
|
-
return {
|
|
446
|
-
journal: {
|
|
447
|
-
listInstances: (options) => port.listWorkflowInstances(options),
|
|
448
|
-
getInstance: (id) => port.getWorkflowInstance(id),
|
|
449
|
-
listSteps: (id) => port.listWorkflowSteps(id),
|
|
450
|
-
cancelInstance: (id) => control("cancel", id),
|
|
451
|
-
pauseInstance: (id) => control("pause", id),
|
|
452
|
-
resumeInstance: (id) => control("resume", id),
|
|
453
|
-
reopenInstance: (id) => control("reopen", id),
|
|
454
|
-
},
|
|
455
|
-
events: { replay: (scope, since, limit = 500, moduleId) => port.replayEvents(scope, since, limit, moduleId) },
|
|
456
|
-
entities: this.#persistence,
|
|
457
|
-
};
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
#require(moduleId: string): HostedModule {
|
|
461
|
-
const hosted = this.#modules.get(moduleId);
|
|
462
|
-
if (!hosted) throw new Error(`module '${moduleId}' is not registered with the host`);
|
|
463
|
-
return hosted;
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
/**
|
|
467
|
-
* A real {@link WorkflowClient} bound to one module's ctx/db/journal, replacing the K6a
|
|
468
|
-
* `unavailableWorkflows` stub. Its `beginStepContext` assembles a fresh `ModuleCtx` + K4/K5
|
|
469
|
-
* sessions per step exactly the way {@link ModuleHost.invoke} builds an op's ctx, so a workflow
|
|
470
|
-
* step runs with the same principal/scope, config, and effects an op does. Each executed step
|
|
471
|
-
* commits its effects + journal memo atomically through M0's `commitStepAttempt`.
|
|
472
|
-
*/
|
|
473
|
-
#buildRuntime<M extends ModuleManifest>(
|
|
474
|
-
module: ModuleDefinition<M>,
|
|
475
|
-
principal: Principal,
|
|
476
|
-
config: unknown,
|
|
477
|
-
): WorkflowRuntime {
|
|
478
|
-
return createWorkflowRuntime({
|
|
479
|
-
persistence: this.#kernelPersistence,
|
|
480
|
-
ready: this.#persistence.ready(module.id),
|
|
481
|
-
clock: this.#effects.clock,
|
|
482
|
-
rng: this.#effects.rng,
|
|
483
|
-
moduleName: module.id,
|
|
484
|
-
// Stamp the generic owner column (KAPI-1 / decision 3) from the invoking principal, so a
|
|
485
|
-
// `ctx.workflows.start` records who created the instance. A system-driven runtime carries the
|
|
486
|
-
// system actor here (accepted creator-invisible divergence for migrated/system starts).
|
|
487
|
-
createdBy: principal.actor,
|
|
488
|
-
workflows: (module.workflows ?? {}) as Readonly<Record<string, WorkflowDecl>>,
|
|
489
|
-
...(this.#leaseTtlMs !== undefined ? { leaseTtlMs: this.#leaseTtlMs } : {}),
|
|
490
|
-
isWaitSatisfied: this.#waitEvaluator,
|
|
491
|
-
...(this.#workflowCommitFaults !== undefined ? { commitFaults: this.#workflowCommitFaults } : {}),
|
|
492
|
-
beginStepContext: () => {
|
|
493
|
-
const entitySession = this.#persistence.begin(module, principal.scope);
|
|
494
|
-
const eventSession = this.#eventLog.begin(module, principal.scope);
|
|
495
|
-
const messageAuthoritySession = beginMessageAuthoritySession(this.#kernelPersistence.messageAuthorityAvailable);
|
|
496
|
-
const ctx = this.#assembleCtx(module, principal, config, entitySession, eventSession, messageAuthoritySession);
|
|
497
|
-
return {
|
|
498
|
-
ctx,
|
|
499
|
-
entitySession: entitySession as AsyncEntitySession<unknown>,
|
|
500
|
-
eventSession: eventSession as AsyncEventSession<unknown>,
|
|
501
|
-
messageAuthoritySession,
|
|
502
|
-
};
|
|
503
|
-
},
|
|
504
|
-
});
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
#workflowClientFor<M extends ModuleManifest>(
|
|
508
|
-
module: ModuleDefinition<M>,
|
|
509
|
-
principal: Principal,
|
|
510
|
-
config: unknown,
|
|
511
|
-
): WorkflowClient<NonNullable<M["workflows"]>> {
|
|
512
|
-
return this.#buildRuntime(module, principal, config) as unknown as WorkflowClient<NonNullable<M["workflows"]>>;
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
/**
|
|
516
|
-
* The module's {@link WorkflowClient} for a caller identity, for driving a workflow outside an
|
|
517
|
-
* op invocation (e.g. a `run-now` trigger or a test). Scope is kernel-injected (`local`) exactly
|
|
518
|
-
* as in {@link ModuleHost.invoke}; the caller supplies only who they are.
|
|
519
|
-
*/
|
|
520
|
-
workflows<M extends ModuleManifest>(module: ModuleDefinition<M>, identity: InvocationIdentity): WorkflowClient<NonNullable<M["workflows"]>> {
|
|
521
|
-
const hosted = this.#require(module.id);
|
|
522
|
-
const principal = resolveIdentity({ actor: identity.actor, scope: LOCAL_SCOPE, grants: identity.grants, resolvedIdentity: identity.resolvedIdentity });
|
|
523
|
-
return this.#workflowClientFor(module as ModuleDefinition<M>, principal, hosted.config);
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
/**
|
|
527
|
-
* A workflow DRIVER handle (the supervisor role): the concrete {@link WorkflowRuntime}, which
|
|
528
|
-
* adds the M2 recovery entry points ({@link WorkflowRuntime.resume} / {@link WorkflowRuntime.recover})
|
|
529
|
-
* on top of start/status/cancel. A supervisor calls `recover()` on boot and on a tick to reclaim
|
|
530
|
-
* instances orphaned by a crashed driver. Each call builds a fresh runtime with its own lease
|
|
531
|
-
* owner/token, so two driver handles genuinely contend on the single-writer lease.
|
|
532
|
-
*/
|
|
533
|
-
workflowDriver<M extends ModuleManifest>(module: ModuleDefinition<M>, identity: InvocationIdentity): WorkflowRuntime {
|
|
534
|
-
const hosted = this.#require(module.id);
|
|
535
|
-
const principal = resolveIdentity({ actor: identity.actor, scope: LOCAL_SCOPE, grants: identity.grants, resolvedIdentity: identity.resolvedIdentity });
|
|
536
|
-
return this.#buildRuntime(module as ModuleDefinition<M>, principal, hosted.config);
|
|
537
|
-
}
|
|
538
|
-
|
|
539
|
-
/**
|
|
540
|
-
* The workflow client an OP invocation sees: identical to the raw runtime, except that a start
|
|
541
|
-
* carrying `enlist: "op-commit"` is STAGED instead of committed — validated eagerly (so a schema
|
|
542
|
-
* failure still throws `invalid_input` from the handler), pushed onto the invocation's pending
|
|
543
|
-
* list for {@link commitAtomically} to seed inside the op's transaction, and driven post-commit.
|
|
544
|
-
* A start WITHOUT the option keeps the ratified immediate-commit semantics unchanged.
|
|
545
|
-
*/
|
|
546
|
-
#enlistableClient(
|
|
547
|
-
runtime: WorkflowRuntime,
|
|
548
|
-
pendingStarts: PendingOpWorkflowStart[],
|
|
549
|
-
pendingControls: PendingOpWorkflowControl[],
|
|
550
|
-
): WorkflowClient<unknown> {
|
|
551
|
-
const client = {
|
|
552
|
-
start: async (name: string, input: unknown, options: WorkflowStartOptions): Promise<WorkflowRun> => {
|
|
553
|
-
if (options?.enlist !== "op-commit") return runtime.start(name, input, options);
|
|
554
|
-
const prepared = await runtime.prepareStart(name, input, options);
|
|
555
|
-
pendingStarts.push({ prepared });
|
|
556
|
-
return prepared.provisional;
|
|
557
|
-
},
|
|
558
|
-
status: (id: string) => runtime.status(id),
|
|
559
|
-
cancel: async (id: string) => {
|
|
560
|
-
pendingControls.push({ operation: "cancel", instanceId: id });
|
|
561
|
-
return true;
|
|
562
|
-
},
|
|
563
|
-
control: async (operation: "cancel" | "fail" | "reopen", instanceId: string, failure?: JsonValue) => {
|
|
564
|
-
pendingControls.push({ operation, instanceId, ...(failure !== undefined ? { failure } : {}) });
|
|
565
|
-
return true;
|
|
566
|
-
},
|
|
567
|
-
};
|
|
568
|
-
return client as unknown as WorkflowClient<unknown>;
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
#assembleCtx<M extends ModuleManifest>(
|
|
572
|
-
module: ModuleDefinition<M>,
|
|
573
|
-
principal: Principal,
|
|
574
|
-
config: unknown,
|
|
575
|
-
entitySession: AsyncEntitySession<M["entities"]>,
|
|
576
|
-
eventSession: AsyncEventSession<NonNullable<M["events"]>>,
|
|
577
|
-
messageAuthoritySession: MessageAuthoritySession,
|
|
578
|
-
pendingStarts?: PendingOpWorkflowStart[],
|
|
579
|
-
pendingControls?: PendingOpWorkflowControl[],
|
|
580
|
-
): ModuleCtx<M> {
|
|
581
|
-
// A step/standalone ctx (no pending list) gets the raw runtime, whose `start` fails closed on
|
|
582
|
-
// `enlist: "op-commit"` — there is no op commit to join outside an op invocation.
|
|
583
|
-
const runtime = this.#buildRuntime(module, principal, config);
|
|
584
|
-
const ctx = {
|
|
585
|
-
hostIdentity: this.#identity,
|
|
586
|
-
principal,
|
|
587
|
-
trace: traceFor(this.#effects.rng),
|
|
588
|
-
config,
|
|
589
|
-
entities: entitySession.entities,
|
|
590
|
-
events: eventSession.events,
|
|
591
|
-
workflows: pendingStarts
|
|
592
|
-
? this.#enlistableClient(runtime, pendingStarts, pendingControls ?? [])
|
|
593
|
-
: (runtime as unknown as WorkflowClient<NonNullable<M["workflows"]>>),
|
|
594
|
-
commands: this.#commands ? commandClientFor(module as AnyModuleDefinition, this.#commands) : unavailableCommands(),
|
|
595
|
-
messageAuthority: messageAuthoritySession.participant,
|
|
596
|
-
health: inertHealth(),
|
|
597
|
-
secrets: unavailableSecrets(),
|
|
598
|
-
clock: this.#effects.clock,
|
|
599
|
-
rng: this.#effects.rng,
|
|
600
|
-
net: this.#effects.net,
|
|
601
|
-
log: this.#effects.log,
|
|
602
|
-
};
|
|
603
|
-
return ctx as unknown as ModuleCtx<M>;
|
|
604
|
-
}
|
|
605
|
-
|
|
606
|
-
/**
|
|
607
|
-
* Serve one op in-process. Order is load-bearing: authorize (default-deny) → validate input →
|
|
608
|
-
* open sessions → run handler → validate output → atomically commit. A denial, a validation
|
|
609
|
-
* failure, a handler throw, or a version conflict all leave the store and event log untouched,
|
|
610
|
-
* and surface as a typed {@link OpError} where the kernel owns the failure mode.
|
|
611
|
-
*/
|
|
612
|
-
async invoke<M extends ModuleManifest>(
|
|
613
|
-
module: ModuleDefinition<M>,
|
|
614
|
-
opName: string,
|
|
615
|
-
identity: InvocationIdentity,
|
|
616
|
-
rawInput: unknown,
|
|
617
|
-
options: InvocationOptions = {},
|
|
618
|
-
): Promise<unknown> {
|
|
619
|
-
const hosted = this.#require(module.id);
|
|
620
|
-
const op = module.ops?.[opName];
|
|
621
|
-
if (!op) throw new OpError("unknown_op", 404, `module '${module.id}' declares no op '${opName}'`);
|
|
622
|
-
|
|
623
|
-
// Scope is kernel-injected: the caller supplies identity, never ownership.
|
|
624
|
-
const principal = resolveIdentity({ actor: identity.actor, scope: LOCAL_SCOPE, grants: identity.grants, resolvedIdentity: identity.resolvedIdentity });
|
|
625
|
-
|
|
626
|
-
// Default-deny: an op is served only if the principal explicitly holds the op's grant.
|
|
627
|
-
if (!isAuthorized(principal, { moduleId: module.id, grant: op.grant })) {
|
|
628
|
-
throw new OpError(
|
|
629
|
-
"unauthorized",
|
|
630
|
-
403,
|
|
631
|
-
`actor '${principal.actor}' is not authorized for '${module.id}.${opName}' (requires '${op.grant}')`,
|
|
632
|
-
);
|
|
633
|
-
}
|
|
634
|
-
|
|
635
|
-
const input = op.input.safeParse(rawInput);
|
|
636
|
-
if (!input.success) throw new OpError("invalid_input", 400, `input validation failed for '${module.id}.${opName}'`, fieldsFromZodError(input.error));
|
|
637
|
-
|
|
638
|
-
await this.#persistence.ready(module.id);
|
|
639
|
-
|
|
640
|
-
const entitySession = this.#persistence.begin(module, principal.scope);
|
|
641
|
-
const eventSession = this.#eventLog.begin(module, principal.scope);
|
|
642
|
-
const messageAuthoritySession = beginMessageAuthoritySession(this.#kernelPersistence.messageAuthorityAvailable);
|
|
643
|
-
const pendingStarts: PendingOpWorkflowStart[] = [];
|
|
644
|
-
const pendingControls: PendingOpWorkflowControl[] = [];
|
|
645
|
-
const ctx = this.#assembleCtx(module, principal, hosted.config, entitySession, eventSession, messageAuthoritySession, pendingStarts, pendingControls);
|
|
646
|
-
|
|
647
|
-
let committing = false;
|
|
648
|
-
try {
|
|
649
|
-
const result = await op.handler(ctx, input.data);
|
|
650
|
-
const output = op.output.safeParse(result);
|
|
651
|
-
if (!output.success) {
|
|
652
|
-
throw new OpError("invalid_output", 500, `output validation failed for '${module.id}.${opName}'`, fieldsFromZodError(output.error));
|
|
653
|
-
}
|
|
654
|
-
// The T-0 chaos seam: hold a LIVE op exactly between its reads and its flush, so a concurrent
|
|
655
|
-
// process can commit first and this op's buffered writes lose their CAS. Test-only; a throw
|
|
656
|
-
// here aborts pre-commit (sessions + staged seeds all discarded).
|
|
657
|
-
const opFaults = this.#opCommitFaults?.(module.id, opName);
|
|
658
|
-
if (opFaults?.beforeCommit) await opFaults.beforeCommit();
|
|
659
|
-
committing = true;
|
|
660
|
-
entitySession.seal();
|
|
661
|
-
eventSession.seal();
|
|
662
|
-
messageAuthoritySession.seal();
|
|
663
|
-
const capturedAt = this.#effects.clock.now();
|
|
664
|
-
const entities = entitySession.mutations();
|
|
665
|
-
const events = eventSession.appends(capturedAt);
|
|
666
|
-
const messageAuthority = messageAuthoritySession.operations();
|
|
667
|
-
// Query/no-op attempts never enter the writer queue and therefore never take SQLite's write lock.
|
|
668
|
-
if (entities.length || events.length || messageAuthority.length || pendingStarts.length || pendingControls.length) {
|
|
669
|
-
const requestKey = options.requestKey?.trim() || this.#effects.rng.uuid();
|
|
670
|
-
if (opFaults) armCompatibilityOpFaults(this.#kernelPersistence, requestKey, opFaults);
|
|
671
|
-
const envelope = structuredClone({
|
|
672
|
-
requestKey,
|
|
673
|
-
capturedAt,
|
|
674
|
-
entities,
|
|
675
|
-
events,
|
|
676
|
-
messageAuthority,
|
|
677
|
-
workflowSeeds: pendingStarts.map((pending, resultIndex) => ({ seed: pending.prepared.seed, resultIndex })),
|
|
678
|
-
workflowControls: pendingControls.map((pending) => ({
|
|
679
|
-
operation: pending.operation,
|
|
680
|
-
instanceId: pending.instanceId,
|
|
681
|
-
...(pending.failure !== undefined ? { failure: pending.failure } : {}),
|
|
682
|
-
})),
|
|
683
|
-
});
|
|
684
|
-
const committed = await recoverIndeterminate(() => this.#kernelPersistence.opCommit(envelope));
|
|
685
|
-
committed.workflowInstanceIds.forEach((id, index) => {
|
|
686
|
-
const pending = pendingStarts[index];
|
|
687
|
-
if (pending) pending.instanceId = id;
|
|
688
|
-
});
|
|
689
|
-
}
|
|
690
|
-
opFaults?.afterCommit?.();
|
|
691
|
-
// Drive enlisted instances only AFTER their seeds durably committed. A drive failure is a
|
|
692
|
-
// runtime concern (the instance is durable and `recover()`/`wakeup()` converge it) — never a
|
|
693
|
-
// false op failure: the op's state HAS committed, and throwing here would tell the caller the
|
|
694
|
-
// opposite. Per-start isolation mirrors the runtime's drive passes.
|
|
695
|
-
for (const pending of pendingStarts) {
|
|
696
|
-
if (pending.instanceId === undefined) continue;
|
|
697
|
-
try {
|
|
698
|
-
await pending.prepared.drive(pending.instanceId);
|
|
699
|
-
} catch (error) {
|
|
700
|
-
this.#effects.log.warn(`post-commit workflow drive failed for '${module.id}.${opName}'`, {
|
|
701
|
-
instanceId: pending.instanceId,
|
|
702
|
-
error: errMessage(error),
|
|
703
|
-
});
|
|
704
|
-
}
|
|
705
|
-
}
|
|
706
|
-
return output.data;
|
|
707
|
-
} catch (error) {
|
|
708
|
-
// Only the pre-commit paths leave sessions open; a commit failure already finalized and
|
|
709
|
-
// rolled them back inside the transaction, so discarding again would double-finalize.
|
|
710
|
-
if (!committing) {
|
|
711
|
-
entitySession.discard();
|
|
712
|
-
eventSession.discard();
|
|
713
|
-
messageAuthoritySession.discard();
|
|
714
|
-
}
|
|
715
|
-
throw mapInvocationError(error);
|
|
716
|
-
}
|
|
717
|
-
}
|
|
718
|
-
}
|
|
719
|
-
|
|
720
|
-
export function createModuleHost(options: ModuleHostOptions): ModuleHost {
|
|
721
|
-
return new ModuleHost(options);
|
|
722
|
-
}
|