@caupulican/pi-adaptative 0.81.39 → 0.81.41
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/CHANGELOG.md +77 -0
- package/README.md +2 -0
- package/dist/bundled-resources/extensions/tmux-agent-manager/dispatch-grant.d.ts +2 -0
- package/dist/bundled-resources/extensions/tmux-agent-manager/dispatch-grant.d.ts.map +1 -1
- package/dist/bundled-resources/extensions/tmux-agent-manager/dispatch-grant.js +3 -0
- package/dist/bundled-resources/extensions/tmux-agent-manager/dispatch-grant.js.map +1 -1
- package/dist/bundled-resources/extensions/tmux-agent-manager/dispatch-grant.ts +4 -0
- package/dist/bundled-resources/extensions/tmux-agent-manager/index.d.ts +9 -0
- package/dist/bundled-resources/extensions/tmux-agent-manager/index.d.ts.map +1 -1
- package/dist/bundled-resources/extensions/tmux-agent-manager/index.js +290 -107
- package/dist/bundled-resources/extensions/tmux-agent-manager/index.js.map +1 -1
- package/dist/bundled-resources/extensions/tmux-agent-manager/index.ts +357 -122
- package/dist/bundled-resources/skills/tool-call-repair/references/repair-catalogue.md +2 -2
- package/dist/cli/args.d.ts +4 -0
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +31 -1
- package/dist/cli/args.js.map +1 -1
- package/dist/core/agent-directory-layout.d.ts +15 -0
- package/dist/core/agent-directory-layout.d.ts.map +1 -0
- package/dist/core/agent-directory-layout.js +72 -0
- package/dist/core/agent-directory-layout.js.map +1 -0
- package/dist/core/agent-paths.d.ts +25 -2
- package/dist/core/agent-paths.d.ts.map +1 -1
- package/dist/core/agent-paths.js +79 -4
- package/dist/core/agent-paths.js.map +1 -1
- package/dist/core/agent-session-contracts.d.ts +293 -0
- package/dist/core/agent-session-contracts.d.ts.map +1 -0
- package/dist/core/agent-session-contracts.js +26 -0
- package/dist/core/agent-session-contracts.js.map +1 -0
- package/dist/core/agent-session-runtime.d.ts +52 -8
- package/dist/core/agent-session-runtime.d.ts.map +1 -1
- package/dist/core/agent-session-runtime.js +400 -79
- package/dist/core/agent-session-runtime.js.map +1 -1
- package/dist/core/agent-session-services.d.ts +1 -0
- package/dist/core/agent-session-services.d.ts.map +1 -1
- package/dist/core/agent-session-services.js +1 -0
- package/dist/core/agent-session-services.js.map +1 -1
- package/dist/core/agent-session.d.ts +74 -518
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +370 -1432
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/auth-storage.d.ts.map +1 -1
- package/dist/core/auth-storage.js +3 -1
- package/dist/core/auth-storage.js.map +1 -1
- package/dist/core/autonomy/approval-gate.d.ts +3 -2
- package/dist/core/autonomy/approval-gate.d.ts.map +1 -1
- package/dist/core/autonomy/approval-gate.js +6 -28
- package/dist/core/autonomy/approval-gate.js.map +1 -1
- package/dist/core/autonomy/bounded-completion.d.ts +1 -1
- package/dist/core/autonomy/bounded-completion.d.ts.map +1 -1
- package/dist/core/autonomy/bounded-completion.js +1 -1
- package/dist/core/autonomy/bounded-completion.js.map +1 -1
- package/dist/core/autonomy/contracts.d.ts +20 -12
- package/dist/core/autonomy/contracts.d.ts.map +1 -1
- package/dist/core/autonomy/contracts.js.map +1 -1
- package/dist/core/autonomy/foreground-envelope.d.ts +8 -2
- package/dist/core/autonomy/foreground-envelope.d.ts.map +1 -1
- package/dist/core/autonomy/foreground-envelope.js +8 -25
- package/dist/core/autonomy/foreground-envelope.js.map +1 -1
- package/dist/core/autonomy/gates.d.ts.map +1 -1
- package/dist/core/autonomy/gates.js +8 -10
- package/dist/core/autonomy/gates.js.map +1 -1
- package/dist/core/autonomy/lane-private-paths.d.ts +2 -0
- package/dist/core/autonomy/lane-private-paths.d.ts.map +1 -0
- package/dist/core/autonomy/lane-private-paths.js +16 -0
- package/dist/core/autonomy/lane-private-paths.js.map +1 -0
- package/dist/core/autonomy/lane-tool-surface.d.ts +10 -0
- package/dist/core/autonomy/lane-tool-surface.d.ts.map +1 -1
- package/dist/core/autonomy/lane-tool-surface.js +48 -9
- package/dist/core/autonomy/lane-tool-surface.js.map +1 -1
- package/dist/core/autonomy/lane-tracker.d.ts +14 -0
- package/dist/core/autonomy/lane-tracker.d.ts.map +1 -1
- package/dist/core/autonomy/lane-tracker.js +38 -0
- package/dist/core/autonomy/lane-tracker.js.map +1 -1
- package/dist/core/autonomy/risk-assessment.d.ts +1 -2
- package/dist/core/autonomy/risk-assessment.d.ts.map +1 -1
- package/dist/core/autonomy/risk-assessment.js +1 -123
- package/dist/core/autonomy/risk-assessment.js.map +1 -1
- package/dist/core/autonomy/session-lane-record.d.ts +4 -4
- package/dist/core/autonomy/session-lane-record.d.ts.map +1 -1
- package/dist/core/autonomy/session-lane-record.js +16 -29
- package/dist/core/autonomy/session-lane-record.js.map +1 -1
- package/dist/core/autonomy/telemetry-events.d.ts +1 -1
- package/dist/core/autonomy/telemetry-events.d.ts.map +1 -1
- package/dist/core/autonomy/telemetry-events.js +1 -1
- package/dist/core/autonomy/telemetry-events.js.map +1 -1
- package/dist/core/autonomy-telemetry.d.ts +7 -7
- package/dist/core/autonomy-telemetry.d.ts.map +1 -1
- package/dist/core/autonomy-telemetry.js +9 -9
- package/dist/core/autonomy-telemetry.js.map +1 -1
- package/dist/core/background-lane-controller.d.ts +56 -236
- package/dist/core/background-lane-controller.d.ts.map +1 -1
- package/dist/core/background-lane-controller.js +141 -1273
- package/dist/core/background-lane-controller.js.map +1 -1
- package/dist/core/bedrock-sso-login.d.ts +16 -0
- package/dist/core/bedrock-sso-login.d.ts.map +1 -0
- package/dist/core/bedrock-sso-login.js +98 -0
- package/dist/core/bedrock-sso-login.js.map +1 -0
- package/dist/core/capability-contract.d.ts +4 -0
- package/dist/core/capability-contract.d.ts.map +1 -0
- package/dist/core/capability-contract.js +28 -0
- package/dist/core/capability-contract.js.map +1 -0
- package/dist/core/compaction-controller.d.ts +90 -0
- package/dist/core/compaction-controller.d.ts.map +1 -0
- package/dist/core/compaction-controller.js +452 -0
- package/dist/core/compaction-controller.js.map +1 -0
- package/dist/core/compaction-support.d.ts +13 -17
- package/dist/core/compaction-support.d.ts.map +1 -1
- package/dist/core/compaction-support.js +3 -3
- package/dist/core/compaction-support.js.map +1 -1
- package/dist/core/context/context-artifacts.d.ts +6 -2
- package/dist/core/context/context-artifacts.d.ts.map +1 -1
- package/dist/core/context/context-artifacts.js +34 -2
- package/dist/core/context/context-artifacts.js.map +1 -1
- package/dist/core/context/context-store-retention.d.ts +8 -2
- package/dist/core/context/context-store-retention.d.ts.map +1 -1
- package/dist/core/context/context-store-retention.js +158 -35
- package/dist/core/context/context-store-retention.js.map +1 -1
- package/dist/core/context/okf-memory.d.ts.map +1 -1
- package/dist/core/context/okf-memory.js +9 -11
- package/dist/core/context/okf-memory.js.map +1 -1
- package/dist/core/context/sqlite-runtime-index.d.ts.map +1 -1
- package/dist/core/context/sqlite-runtime-index.js +6 -8
- package/dist/core/context/sqlite-runtime-index.js.map +1 -1
- package/dist/core/context-gc.d.ts +3 -0
- package/dist/core/context-gc.d.ts.map +1 -1
- package/dist/core/context-gc.js +10 -5
- package/dist/core/context-gc.js.map +1 -1
- package/dist/core/context-pipeline.d.ts +1 -1
- package/dist/core/context-pipeline.d.ts.map +1 -1
- package/dist/core/context-pipeline.js +16 -37
- package/dist/core/context-pipeline.js.map +1 -1
- package/dist/core/cost/cost-summary.d.ts.map +1 -1
- package/dist/core/cost/cost-summary.js +5 -3
- package/dist/core/cost/cost-summary.js.map +1 -1
- package/dist/core/cost/daily-usage.d.ts.map +1 -1
- package/dist/core/cost/daily-usage.js +4 -5
- package/dist/core/cost/daily-usage.js.map +1 -1
- package/dist/core/default-tool-surface.d.ts.map +1 -1
- package/dist/core/default-tool-surface.js +2 -0
- package/dist/core/default-tool-surface.js.map +1 -1
- package/dist/core/delegation/managed-lane-controller.d.ts +34 -0
- package/dist/core/delegation/managed-lane-controller.d.ts.map +1 -0
- package/dist/core/delegation/managed-lane-controller.js +217 -0
- package/dist/core/delegation/managed-lane-controller.js.map +1 -0
- package/dist/core/delegation/session-worker-claim.d.ts +44 -0
- package/dist/core/delegation/session-worker-claim.d.ts.map +1 -0
- package/dist/core/delegation/session-worker-claim.js +98 -0
- package/dist/core/delegation/session-worker-claim.js.map +1 -0
- package/dist/core/delegation/worker-actions.d.ts +7 -5
- package/dist/core/delegation/worker-actions.d.ts.map +1 -1
- package/dist/core/delegation/worker-actions.js +20 -4
- package/dist/core/delegation/worker-actions.js.map +1 -1
- package/dist/core/delegation/{worker-result.d.ts → worker-claim.d.ts} +13 -13
- package/dist/core/delegation/worker-claim.d.ts.map +1 -0
- package/dist/core/delegation/{worker-result.js → worker-claim.js} +61 -51
- package/dist/core/delegation/worker-claim.js.map +1 -0
- package/dist/core/delegation/worker-delegation-controller.d.ts +110 -0
- package/dist/core/delegation/worker-delegation-controller.d.ts.map +1 -0
- package/dist/core/delegation/worker-delegation-controller.js +861 -0
- package/dist/core/delegation/worker-delegation-controller.js.map +1 -0
- package/dist/core/delegation/worker-delegation-request.d.ts +11 -0
- package/dist/core/delegation/worker-delegation-request.d.ts.map +1 -0
- package/dist/core/delegation/worker-delegation-request.js +2 -0
- package/dist/core/delegation/worker-delegation-request.js.map +1 -0
- package/dist/core/delegation/worker-dispatch-scheduler.d.ts +40 -0
- package/dist/core/delegation/worker-dispatch-scheduler.d.ts.map +1 -0
- package/dist/core/delegation/worker-dispatch-scheduler.js +93 -0
- package/dist/core/delegation/worker-dispatch-scheduler.js.map +1 -0
- package/dist/core/delegation/worker-execution-policy.d.ts +62 -0
- package/dist/core/delegation/worker-execution-policy.d.ts.map +1 -0
- package/dist/core/delegation/worker-execution-policy.js +160 -0
- package/dist/core/delegation/worker-execution-policy.js.map +1 -0
- package/dist/core/delegation/worker-lane-projection.d.ts +14 -0
- package/dist/core/delegation/worker-lane-projection.d.ts.map +1 -0
- package/dist/core/delegation/worker-lane-projection.js +100 -0
- package/dist/core/delegation/worker-lane-projection.js.map +1 -0
- package/dist/core/delegation/worker-lifecycle.d.ts +97 -0
- package/dist/core/delegation/worker-lifecycle.d.ts.map +1 -0
- package/dist/core/delegation/worker-lifecycle.js +339 -0
- package/dist/core/delegation/worker-lifecycle.js.map +1 -0
- package/dist/core/delegation/worker-notification-coordinator.d.ts +36 -0
- package/dist/core/delegation/worker-notification-coordinator.d.ts.map +1 -0
- package/dist/core/delegation/worker-notification-coordinator.js +90 -0
- package/dist/core/delegation/worker-notification-coordinator.js.map +1 -0
- package/dist/core/delegation/worker-profile-resolver.d.ts +55 -0
- package/dist/core/delegation/worker-profile-resolver.d.ts.map +1 -0
- package/dist/core/delegation/worker-profile-resolver.js +113 -0
- package/dist/core/delegation/worker-profile-resolver.js.map +1 -0
- package/dist/core/delegation/worker-runner.d.ts +25 -14
- package/dist/core/delegation/worker-runner.d.ts.map +1 -1
- package/dist/core/delegation/worker-runner.js +122 -53
- package/dist/core/delegation/worker-runner.js.map +1 -1
- package/dist/core/delegation/worker-task-label.d.ts +3 -0
- package/dist/core/delegation/worker-task-label.d.ts.map +1 -0
- package/dist/core/delegation/worker-task-label.js +11 -0
- package/dist/core/delegation/worker-task-label.js.map +1 -0
- package/dist/core/doctor.d.ts +8 -0
- package/dist/core/doctor.d.ts.map +1 -1
- package/dist/core/doctor.js +32 -1
- package/dist/core/doctor.js.map +1 -1
- package/dist/core/extension-import-authority.d.ts +6 -0
- package/dist/core/extension-import-authority.d.ts.map +1 -0
- package/dist/core/extension-import-authority.js +10 -0
- package/dist/core/extension-import-authority.js.map +1 -0
- package/dist/core/extensions/index.d.ts +1 -1
- package/dist/core/extensions/index.d.ts.map +1 -1
- package/dist/core/extensions/index.js.map +1 -1
- package/dist/core/extensions/loader.d.ts +5 -1
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +52 -18
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/extensions/runner.d.ts +3 -0
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +20 -5
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/stale-context.d.ts +2 -0
- package/dist/core/extensions/stale-context.d.ts.map +1 -0
- package/dist/core/extensions/stale-context.js +2 -0
- package/dist/core/extensions/stale-context.js.map +1 -0
- package/dist/core/extensions/storage.d.ts +8 -0
- package/dist/core/extensions/storage.d.ts.map +1 -0
- package/dist/core/extensions/storage.js +28 -0
- package/dist/core/extensions/storage.js.map +1 -0
- package/dist/core/extensions/types.d.ts +78 -21
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/failure-corpus.d.ts +1 -15
- package/dist/core/failure-corpus.d.ts.map +1 -1
- package/dist/core/failure-corpus.js +9 -37
- package/dist/core/failure-corpus.js.map +1 -1
- package/dist/core/foreground-recovery-controller.d.ts +53 -0
- package/dist/core/foreground-recovery-controller.d.ts.map +1 -0
- package/dist/core/foreground-recovery-controller.js +111 -0
- package/dist/core/foreground-recovery-controller.js.map +1 -0
- package/dist/core/gateways/channel-provider.d.ts +3 -0
- package/dist/core/gateways/channel-provider.d.ts.map +1 -1
- package/dist/core/gateways/channel-provider.js +31 -6
- package/dist/core/gateways/channel-provider.js.map +1 -1
- package/dist/core/goal-loop-controller.d.ts +14 -9
- package/dist/core/goal-loop-controller.d.ts.map +1 -1
- package/dist/core/goal-loop-controller.js +47 -75
- package/dist/core/goal-loop-controller.js.map +1 -1
- package/dist/core/goals/compact-goal-context.d.ts +11 -0
- package/dist/core/goals/compact-goal-context.d.ts.map +1 -0
- package/dist/core/goals/compact-goal-context.js +59 -0
- package/dist/core/goals/compact-goal-context.js.map +1 -0
- package/dist/core/goals/goal-acceptance.d.ts +11 -0
- package/dist/core/goals/goal-acceptance.d.ts.map +1 -0
- package/dist/core/goals/goal-acceptance.js +27 -0
- package/dist/core/goals/goal-acceptance.js.map +1 -0
- package/dist/core/goals/goal-auto-continue-controller.d.ts +28 -0
- package/dist/core/goals/goal-auto-continue-controller.d.ts.map +1 -0
- package/dist/core/goals/goal-auto-continue-controller.js +90 -0
- package/dist/core/goals/goal-auto-continue-controller.js.map +1 -0
- package/dist/core/goals/goal-continuation-controller.d.ts +2 -2
- package/dist/core/goals/goal-continuation-controller.d.ts.map +1 -1
- package/dist/core/goals/goal-continuation-controller.js +52 -11
- package/dist/core/goals/goal-continuation-controller.js.map +1 -1
- package/dist/core/goals/goal-continuation-defaults.d.ts +4 -29
- package/dist/core/goals/goal-continuation-defaults.d.ts.map +1 -1
- package/dist/core/goals/goal-continuation-defaults.js +4 -32
- package/dist/core/goals/goal-continuation-defaults.js.map +1 -1
- package/dist/core/goals/goal-continuation-prompt.d.ts +8 -18
- package/dist/core/goals/goal-continuation-prompt.d.ts.map +1 -1
- package/dist/core/goals/goal-continuation-prompt.js +9 -179
- package/dist/core/goals/goal-continuation-prompt.js.map +1 -1
- package/dist/core/goals/goal-lifecycle.d.ts +43 -0
- package/dist/core/goals/goal-lifecycle.d.ts.map +1 -0
- package/dist/core/goals/goal-lifecycle.js +102 -0
- package/dist/core/goals/goal-lifecycle.js.map +1 -0
- package/dist/core/goals/goal-record.d.ts +17 -0
- package/dist/core/goals/goal-record.d.ts.map +1 -0
- package/dist/core/goals/goal-record.js +31 -0
- package/dist/core/goals/goal-record.js.map +1 -0
- package/dist/core/goals/goal-runtime-snapshot.d.ts +11 -9
- package/dist/core/goals/goal-runtime-snapshot.d.ts.map +1 -1
- package/dist/core/goals/goal-runtime-snapshot.js +10 -8
- package/dist/core/goals/goal-runtime-snapshot.js.map +1 -1
- package/dist/core/goals/goal-session-controller.d.ts +42 -0
- package/dist/core/goals/goal-session-controller.d.ts.map +1 -0
- package/dist/core/goals/goal-session-controller.js +128 -0
- package/dist/core/goals/goal-session-controller.js.map +1 -0
- package/dist/core/goals/goal-state.d.ts +46 -25
- package/dist/core/goals/goal-state.d.ts.map +1 -1
- package/dist/core/goals/goal-state.js +91 -24
- package/dist/core/goals/goal-state.js.map +1 -1
- package/dist/core/goals/goal-tool-core.d.ts +9 -10
- package/dist/core/goals/goal-tool-core.d.ts.map +1 -1
- package/dist/core/goals/goal-tool-core.js +60 -75
- package/dist/core/goals/goal-tool-core.js.map +1 -1
- package/dist/core/goals/session-goal-state.d.ts +20 -12
- package/dist/core/goals/session-goal-state.d.ts.map +1 -1
- package/dist/core/goals/session-goal-state.js +133 -33
- package/dist/core/goals/session-goal-state.js.map +1 -1
- package/dist/core/human-input-controller.d.ts +47 -0
- package/dist/core/human-input-controller.d.ts.map +1 -0
- package/dist/core/human-input-controller.js +181 -0
- package/dist/core/human-input-controller.js.map +1 -0
- package/dist/core/human-input.d.ts +100 -0
- package/dist/core/human-input.d.ts.map +1 -0
- package/dist/core/human-input.js +362 -0
- package/dist/core/human-input.js.map +1 -0
- package/dist/core/index.d.ts +9 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +9 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/{telemetry.d.ts → install-telemetry.d.ts} +1 -1
- package/dist/core/install-telemetry.d.ts.map +1 -0
- package/dist/core/{telemetry.js → install-telemetry.js} +1 -1
- package/dist/core/install-telemetry.js.map +1 -0
- package/dist/core/keybindings.d.ts +17 -1
- package/dist/core/keybindings.d.ts.map +1 -1
- package/dist/core/keybindings.js +24 -6
- package/dist/core/keybindings.js.map +1 -1
- package/dist/core/learning/learning-audit.d.ts +2 -4
- package/dist/core/learning/learning-audit.d.ts.map +1 -1
- package/dist/core/learning/learning-audit.js +9 -29
- package/dist/core/learning/learning-audit.js.map +1 -1
- package/dist/core/learning/learning-gate.d.ts.map +1 -1
- package/dist/core/learning/learning-gate.js +1 -6
- package/dist/core/learning/learning-gate.js.map +1 -1
- package/dist/core/learning/session-learning-decision.d.ts +2 -4
- package/dist/core/learning/session-learning-decision.d.ts.map +1 -1
- package/dist/core/learning/session-learning-decision.js +9 -29
- package/dist/core/learning/session-learning-decision.js.map +1 -1
- package/dist/core/local-runtime-controller.d.ts +1 -1
- package/dist/core/local-runtime-controller.d.ts.map +1 -1
- package/dist/core/local-runtime-controller.js.map +1 -1
- package/dist/core/memory-controller.d.ts.map +1 -1
- package/dist/core/memory-controller.js +4 -4
- package/dist/core/memory-controller.js.map +1 -1
- package/dist/core/model-capability.d.ts +0 -19
- package/dist/core/model-capability.d.ts.map +1 -1
- package/dist/core/model-capability.js +1 -19
- package/dist/core/model-capability.js.map +1 -1
- package/dist/core/model-registry.d.ts +6 -1
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +13 -5
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/model-resolver.d.ts +17 -2
- package/dist/core/model-resolver.d.ts.map +1 -1
- package/dist/core/model-resolver.js +38 -25
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/model-router/intent-classifier.d.ts +1 -4
- package/dist/core/model-router/intent-classifier.d.ts.map +1 -1
- package/dist/core/model-router/intent-classifier.js +1 -156
- package/dist/core/model-router/intent-classifier.js.map +1 -1
- package/dist/core/model-router-controller.d.ts +1 -1
- package/dist/core/model-router-controller.d.ts.map +1 -1
- package/dist/core/model-router-controller.js +2 -2
- package/dist/core/model-router-controller.js.map +1 -1
- package/dist/core/model-selection-controller.d.ts +5 -5
- package/dist/core/model-selection-controller.d.ts.map +1 -1
- package/dist/core/model-selection-controller.js.map +1 -1
- package/dist/core/model-tool-protocol.d.ts +23 -0
- package/dist/core/model-tool-protocol.d.ts.map +1 -0
- package/dist/core/model-tool-protocol.js +43 -0
- package/dist/core/model-tool-protocol.js.map +1 -0
- package/dist/core/models/adaptation-store.d.ts +4 -6
- package/dist/core/models/adaptation-store.d.ts.map +1 -1
- package/dist/core/models/adaptation-store.js +95 -83
- package/dist/core/models/adaptation-store.js.map +1 -1
- package/dist/core/models/fitness-store.d.ts +2 -12
- package/dist/core/models/fitness-store.d.ts.map +1 -1
- package/dist/core/models/fitness-store.js +84 -57
- package/dist/core/models/fitness-store.js.map +1 -1
- package/dist/core/models/host-state-store.d.ts +34 -0
- package/dist/core/models/host-state-store.d.ts.map +1 -0
- package/dist/core/models/host-state-store.js +86 -0
- package/dist/core/models/host-state-store.js.map +1 -0
- package/dist/core/orchestration/agent-resume.d.ts +24 -0
- package/dist/core/orchestration/agent-resume.d.ts.map +1 -0
- package/dist/core/orchestration/agent-resume.js +90 -0
- package/dist/core/orchestration/agent-resume.js.map +1 -0
- package/dist/core/orchestration/capability-gateway.d.ts +61 -0
- package/dist/core/orchestration/capability-gateway.d.ts.map +1 -0
- package/dist/core/orchestration/capability-gateway.js +152 -0
- package/dist/core/orchestration/capability-gateway.js.map +1 -0
- package/dist/core/orchestration/contracts.d.ts +343 -0
- package/dist/core/orchestration/contracts.d.ts.map +1 -0
- package/dist/core/orchestration/contracts.js +135 -0
- package/dist/core/orchestration/contracts.js.map +1 -0
- package/dist/core/orchestration/delegation-ledger.d.ts +56 -0
- package/dist/core/orchestration/delegation-ledger.d.ts.map +1 -0
- package/dist/core/orchestration/delegation-ledger.js +200 -0
- package/dist/core/orchestration/delegation-ledger.js.map +1 -0
- package/dist/core/orchestration/event-store.d.ts +83 -0
- package/dist/core/orchestration/event-store.d.ts.map +1 -0
- package/dist/core/orchestration/event-store.js +560 -0
- package/dist/core/orchestration/event-store.js.map +1 -0
- package/dist/core/orchestration/lane-tool-manifests.d.ts +9 -0
- package/dist/core/orchestration/lane-tool-manifests.d.ts.map +1 -0
- package/dist/core/orchestration/lane-tool-manifests.js +37 -0
- package/dist/core/orchestration/lane-tool-manifests.js.map +1 -0
- package/dist/core/orchestration/model-binding.d.ts +15 -0
- package/dist/core/orchestration/model-binding.d.ts.map +1 -0
- package/dist/core/orchestration/model-binding.js +29 -0
- package/dist/core/orchestration/model-binding.js.map +1 -0
- package/dist/core/orchestration/policy-compiler.d.ts +49 -0
- package/dist/core/orchestration/policy-compiler.d.ts.map +1 -0
- package/dist/core/orchestration/policy-compiler.js +191 -0
- package/dist/core/orchestration/policy-compiler.js.map +1 -0
- package/dist/core/orchestration/policy-gate.d.ts +18 -0
- package/dist/core/orchestration/policy-gate.d.ts.map +1 -0
- package/dist/core/orchestration/policy-gate.js +24 -0
- package/dist/core/orchestration/policy-gate.js.map +1 -0
- package/dist/core/orchestration/profile-registry.d.ts +51 -0
- package/dist/core/orchestration/profile-registry.d.ts.map +1 -0
- package/dist/core/orchestration/profile-registry.js +393 -0
- package/dist/core/orchestration/profile-registry.js.map +1 -0
- package/dist/core/orchestration/profile-store.d.ts +31 -0
- package/dist/core/orchestration/profile-store.d.ts.map +1 -0
- package/dist/core/orchestration/profile-store.js +136 -0
- package/dist/core/orchestration/profile-store.js.map +1 -0
- package/dist/core/orchestration/risk-budget.d.ts +9 -0
- package/dist/core/orchestration/risk-budget.d.ts.map +1 -0
- package/dist/core/orchestration/risk-budget.js +48 -0
- package/dist/core/orchestration/risk-budget.js.map +1 -0
- package/dist/core/orchestration/task-runtime.d.ts +168 -0
- package/dist/core/orchestration/task-runtime.d.ts.map +1 -0
- package/dist/core/orchestration/task-runtime.js +1600 -0
- package/dist/core/orchestration/task-runtime.js.map +1 -0
- package/dist/core/orchestration/work-state-projection.d.ts +32 -0
- package/dist/core/orchestration/work-state-projection.d.ts.map +1 -0
- package/dist/core/orchestration/work-state-projection.js +116 -0
- package/dist/core/orchestration/work-state-projection.js.map +1 -0
- package/dist/core/orchestration/worker-execution-contract.d.ts +21 -0
- package/dist/core/orchestration/worker-execution-contract.d.ts.map +1 -0
- package/dist/core/orchestration/worker-execution-contract.js +179 -0
- package/dist/core/orchestration/worker-execution-contract.js.map +1 -0
- package/dist/core/orchestration/worker-result-adapter.d.ts +21 -0
- package/dist/core/orchestration/worker-result-adapter.d.ts.map +1 -0
- package/dist/core/orchestration/worker-result-adapter.js +109 -0
- package/dist/core/orchestration/worker-result-adapter.js.map +1 -0
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js +20 -3
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/process-identity.d.ts +11 -0
- package/dist/core/process-identity.d.ts.map +1 -0
- package/dist/core/process-identity.js +26 -0
- package/dist/core/process-identity.js.map +1 -0
- package/dist/core/process-matrix/codes.d.ts +14 -3
- package/dist/core/process-matrix/codes.d.ts.map +1 -1
- package/dist/core/process-matrix/codes.js.map +1 -1
- package/dist/core/process-matrix/resume-launcher.d.ts +26 -0
- package/dist/core/process-matrix/resume-launcher.d.ts.map +1 -0
- package/dist/core/process-matrix/resume-launcher.js +62 -0
- package/dist/core/process-matrix/resume-launcher.js.map +1 -0
- package/dist/core/process-matrix/runtime.d.ts +37 -22
- package/dist/core/process-matrix/runtime.d.ts.map +1 -1
- package/dist/core/process-matrix/runtime.js +399 -76
- package/dist/core/process-matrix/runtime.js.map +1 -1
- package/dist/core/process-matrix/store.d.ts +5 -2
- package/dist/core/process-matrix/store.d.ts.map +1 -1
- package/dist/core/process-matrix/store.js +123 -14
- package/dist/core/process-matrix/store.js.map +1 -1
- package/dist/core/process-matrix/supervisor.d.ts +10 -7
- package/dist/core/process-matrix/supervisor.d.ts.map +1 -1
- package/dist/core/process-matrix/supervisor.js +45 -14
- package/dist/core/process-matrix/supervisor.js.map +1 -1
- package/dist/core/profile-filter-controller.d.ts +8 -1
- package/dist/core/profile-filter-controller.d.ts.map +1 -1
- package/dist/core/profile-filter-controller.js +23 -5
- package/dist/core/profile-filter-controller.js.map +1 -1
- package/dist/core/profile-registry.d.ts.map +1 -1
- package/dist/core/profile-registry.js +6 -8
- package/dist/core/profile-registry.js.map +1 -1
- package/dist/core/prompt-templates.d.ts +1 -0
- package/dist/core/prompt-templates.d.ts.map +1 -1
- package/dist/core/prompt-templates.js +18 -29
- package/dist/core/prompt-templates.js.map +1 -1
- package/dist/core/reflection-controller.d.ts +5 -5
- package/dist/core/reflection-controller.d.ts.map +1 -1
- package/dist/core/reflection-controller.js +5 -2
- package/dist/core/reflection-controller.js.map +1 -1
- package/dist/core/reload-blockers.d.ts.map +1 -1
- package/dist/core/reload-blockers.js +6 -8
- package/dist/core/reload-blockers.js.map +1 -1
- package/dist/core/request-auth.d.ts +7 -0
- package/dist/core/request-auth.d.ts.map +1 -0
- package/dist/core/request-auth.js +8 -0
- package/dist/core/request-auth.js.map +1 -0
- package/dist/core/research/evidence-bundle.d.ts.map +1 -1
- package/dist/core/research/evidence-bundle.js +1 -6
- package/dist/core/research/evidence-bundle.js.map +1 -1
- package/dist/core/research/lane-model-resolver.d.ts +33 -0
- package/dist/core/research/lane-model-resolver.d.ts.map +1 -0
- package/dist/core/research/lane-model-resolver.js +65 -0
- package/dist/core/research/lane-model-resolver.js.map +1 -0
- package/dist/core/research/model-fitness-controller.d.ts +38 -0
- package/dist/core/research/model-fitness-controller.d.ts.map +1 -0
- package/dist/core/research/model-fitness-controller.js +106 -0
- package/dist/core/research/model-fitness-controller.js.map +1 -0
- package/dist/core/research/model-fitness.d.ts +1 -1
- package/dist/core/research/model-fitness.d.ts.map +1 -1
- package/dist/core/research/model-fitness.js +4 -4
- package/dist/core/research/model-fitness.js.map +1 -1
- package/dist/core/research/research-gate.d.ts.map +1 -1
- package/dist/core/research/research-gate.js +7 -7
- package/dist/core/research/research-gate.js.map +1 -1
- package/dist/core/research/research-lane-controller.d.ts +62 -0
- package/dist/core/research/research-lane-controller.d.ts.map +1 -0
- package/dist/core/research/research-lane-controller.js +292 -0
- package/dist/core/research/research-lane-controller.js.map +1 -0
- package/dist/core/research/session-evidence-bundle.d.ts +2 -4
- package/dist/core/research/session-evidence-bundle.d.ts.map +1 -1
- package/dist/core/research/session-evidence-bundle.js +10 -46
- package/dist/core/research/session-evidence-bundle.js.map +1 -1
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +23 -8
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/risk-classifier.d.ts +6 -0
- package/dist/core/risk-classifier.d.ts.map +1 -0
- package/dist/core/risk-classifier.js +233 -0
- package/dist/core/risk-classifier.js.map +1 -0
- package/dist/core/runtime-builder.d.ts +41 -23
- package/dist/core/runtime-builder.d.ts.map +1 -1
- package/dist/core/runtime-builder.js +365 -253
- package/dist/core/runtime-builder.js.map +1 -1
- package/dist/core/sdk.d.ts +6 -3
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +68 -25
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/secrets/credential-exposure-guard.d.ts +9 -0
- package/dist/core/secrets/credential-exposure-guard.d.ts.map +1 -0
- package/dist/core/secrets/credential-exposure-guard.js +241 -0
- package/dist/core/secrets/credential-exposure-guard.js.map +1 -0
- package/dist/core/secrets/secret-dotenv.d.ts +27 -0
- package/dist/core/secrets/secret-dotenv.d.ts.map +1 -0
- package/dist/core/secrets/secret-dotenv.js +183 -0
- package/dist/core/secrets/secret-dotenv.js.map +1 -0
- package/dist/core/secrets/secret-vault-format.d.ts +112 -0
- package/dist/core/secrets/secret-vault-format.d.ts.map +1 -0
- package/dist/core/secrets/secret-vault-format.js +397 -0
- package/dist/core/secrets/secret-vault-format.js.map +1 -0
- package/dist/core/secrets/secret-vault.d.ts +72 -0
- package/dist/core/secrets/secret-vault.d.ts.map +1 -0
- package/dist/core/secrets/secret-vault.js +671 -0
- package/dist/core/secrets/secret-vault.js.map +1 -0
- package/dist/core/session-analytics.d.ts +4 -23
- package/dist/core/session-analytics.d.ts.map +1 -1
- package/dist/core/session-analytics.js +67 -70
- package/dist/core/session-analytics.js.map +1 -1
- package/dist/core/session-image-store.d.ts +33 -0
- package/dist/core/session-image-store.d.ts.map +1 -0
- package/dist/core/session-image-store.js +283 -0
- package/dist/core/session-image-store.js.map +1 -0
- package/dist/core/session-role.d.ts +11 -4
- package/dist/core/session-role.d.ts.map +1 -1
- package/dist/core/session-role.js +24 -5
- package/dist/core/session-role.js.map +1 -1
- package/dist/core/session-snapshot.d.ts +34 -0
- package/dist/core/session-snapshot.d.ts.map +1 -0
- package/dist/core/session-snapshot.js +68 -0
- package/dist/core/session-snapshot.js.map +1 -0
- package/dist/core/session-supervision-runtime.d.ts +31 -0
- package/dist/core/session-supervision-runtime.d.ts.map +1 -0
- package/dist/core/session-supervision-runtime.js +97 -0
- package/dist/core/session-supervision-runtime.js.map +1 -0
- package/dist/core/session-tree-navigator.d.ts +4 -6
- package/dist/core/session-tree-navigator.d.ts.map +1 -1
- package/dist/core/session-tree-navigator.js +4 -1
- package/dist/core/session-tree-navigator.js.map +1 -1
- package/dist/core/settings-manager.d.ts +10 -5
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +81 -29
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +2 -2
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/core/tasks/session-task-state.d.ts +2 -4
- package/dist/core/tasks/session-task-state.d.ts.map +1 -1
- package/dist/core/tasks/session-task-state.js +10 -28
- package/dist/core/tasks/session-task-state.js.map +1 -1
- package/dist/core/tasks/task-projection.d.ts +20 -0
- package/dist/core/tasks/task-projection.d.ts.map +1 -0
- package/dist/core/tasks/task-projection.js +47 -0
- package/dist/core/tasks/task-projection.js.map +1 -0
- package/dist/core/tasks/task-state.d.ts +4 -0
- package/dist/core/tasks/task-state.d.ts.map +1 -1
- package/dist/core/tasks/task-state.js +28 -11
- package/dist/core/tasks/task-state.js.map +1 -1
- package/dist/core/tool-capability-policy.d.ts +13 -0
- package/dist/core/tool-capability-policy.d.ts.map +1 -0
- package/dist/core/tool-capability-policy.js +86 -0
- package/dist/core/tool-capability-policy.js.map +1 -0
- package/dist/core/tool-gate-controller.d.ts.map +1 -1
- package/dist/core/tool-gate-controller.js +8 -2
- package/dist/core/tool-gate-controller.js.map +1 -1
- package/dist/core/tool-protocol-controller.d.ts +89 -0
- package/dist/core/tool-protocol-controller.d.ts.map +1 -0
- package/dist/core/tool-protocol-controller.js +512 -0
- package/dist/core/tool-protocol-controller.js.map +1 -0
- package/dist/core/tool-recovery-log-records.d.ts +2 -3
- package/dist/core/tool-recovery-log-records.d.ts.map +1 -1
- package/dist/core/tool-recovery-log-records.js +9 -35
- package/dist/core/tool-recovery-log-records.js.map +1 -1
- package/dist/core/tool-recovery-stats.d.ts +21 -1
- package/dist/core/tool-recovery-stats.d.ts.map +1 -1
- package/dist/core/tool-recovery-stats.js.map +1 -1
- package/dist/core/tool-selection/tool-performance-store.d.ts +3 -7
- package/dist/core/tool-selection/tool-performance-store.d.ts.map +1 -1
- package/dist/core/tool-selection/tool-performance-store.js +97 -138
- package/dist/core/tool-selection/tool-performance-store.js.map +1 -1
- package/dist/core/tools/ask-question.d.ts +124 -0
- package/dist/core/tools/ask-question.d.ts.map +1 -0
- package/dist/core/tools/ask-question.js +693 -0
- package/dist/core/tools/ask-question.js.map +1 -0
- package/dist/core/tools/bash.d.ts +5 -0
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +59 -5
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/delegate-status.d.ts +31 -3
- package/dist/core/tools/delegate-status.d.ts.map +1 -1
- package/dist/core/tools/delegate-status.js +148 -29
- package/dist/core/tools/delegate-status.js.map +1 -1
- package/dist/core/tools/delegate.d.ts +15 -13
- package/dist/core/tools/delegate.d.ts.map +1 -1
- package/dist/core/tools/delegate.js +106 -23
- package/dist/core/tools/delegate.js.map +1 -1
- package/dist/core/tools/extensionify.d.ts.map +1 -1
- package/dist/core/tools/extensionify.js +1 -0
- package/dist/core/tools/extensionify.js.map +1 -1
- package/dist/core/tools/git-filter.d.ts +1 -8
- package/dist/core/tools/git-filter.d.ts.map +1 -1
- package/dist/core/tools/git-filter.js +2 -63
- package/dist/core/tools/git-filter.js.map +1 -1
- package/dist/core/tools/goal.d.ts +10 -8
- package/dist/core/tools/goal.d.ts.map +1 -1
- package/dist/core/tools/goal.js +106 -28
- package/dist/core/tools/goal.js.map +1 -1
- package/dist/core/tools/index.d.ts +1 -0
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js +1 -0
- package/dist/core/tools/index.js.map +1 -1
- package/dist/core/tools/model-fitness.d.ts.map +1 -1
- package/dist/core/tools/model-fitness.js +2 -2
- package/dist/core/tools/model-fitness.js.map +1 -1
- package/dist/core/tools/orchestration-panel.d.ts +40 -0
- package/dist/core/tools/orchestration-panel.d.ts.map +1 -0
- package/dist/core/tools/orchestration-panel.js +124 -0
- package/dist/core/tools/orchestration-panel.js.map +1 -0
- package/dist/core/tools/output-accumulator.d.ts +11 -0
- package/dist/core/tools/output-accumulator.d.ts.map +1 -1
- package/dist/core/tools/output-accumulator.js +36 -3
- package/dist/core/tools/output-accumulator.js.map +1 -1
- package/dist/core/tools/python.d.ts +2 -0
- package/dist/core/tools/python.d.ts.map +1 -1
- package/dist/core/tools/python.js +3 -1
- package/dist/core/tools/python.js.map +1 -1
- package/dist/core/tools/read.d.ts.map +1 -1
- package/dist/core/tools/read.js +1 -1
- package/dist/core/tools/read.js.map +1 -1
- package/dist/core/tools/run-process.d.ts +26 -0
- package/dist/core/tools/run-process.d.ts.map +1 -0
- package/dist/core/tools/run-process.js +138 -0
- package/dist/core/tools/run-process.js.map +1 -0
- package/dist/core/tools/search-command-guard.d.ts +14 -0
- package/dist/core/tools/search-command-guard.d.ts.map +1 -0
- package/dist/core/tools/search-command-guard.js +333 -0
- package/dist/core/tools/search-command-guard.js.map +1 -0
- package/dist/core/tools/secret-store.d.ts +38 -0
- package/dist/core/tools/secret-store.d.ts.map +1 -0
- package/dist/core/tools/secret-store.js +412 -0
- package/dist/core/tools/secret-store.js.map +1 -0
- package/dist/core/tools/shell-command-parser.d.ts +16 -0
- package/dist/core/tools/shell-command-parser.d.ts.map +1 -0
- package/dist/core/tools/shell-command-parser.js +113 -0
- package/dist/core/tools/shell-command-parser.js.map +1 -0
- package/dist/core/tools/task-steps.d.ts +2 -0
- package/dist/core/tools/task-steps.d.ts.map +1 -1
- package/dist/core/tools/task-steps.js +99 -0
- package/dist/core/tools/task-steps.js.map +1 -1
- package/dist/core/tools/tmux-dispatch.d.ts +3 -5
- package/dist/core/tools/tmux-dispatch.d.ts.map +1 -1
- package/dist/core/tools/tmux-dispatch.js +4 -4
- package/dist/core/tools/tmux-dispatch.js.map +1 -1
- package/dist/core/tools/windows-shell-engine.d.ts.map +1 -1
- package/dist/core/tools/windows-shell-engine.js +4 -6
- package/dist/core/tools/windows-shell-engine.js.map +1 -1
- package/dist/core/util/atomic-file.d.ts +6 -2
- package/dist/core/util/atomic-file.d.ts.map +1 -1
- package/dist/core/util/atomic-file.js +4 -4
- package/dist/core/util/atomic-file.js.map +1 -1
- package/dist/core/util/bounded-jsonl.d.ts +10 -0
- package/dist/core/util/bounded-jsonl.d.ts.map +1 -0
- package/dist/core/util/bounded-jsonl.js +75 -0
- package/dist/core/util/bounded-jsonl.js.map +1 -0
- package/dist/core/util/value-guards.d.ts +5 -0
- package/dist/core/util/value-guards.d.ts.map +1 -0
- package/dist/core/util/value-guards.js +12 -0
- package/dist/core/util/value-guards.js.map +1 -0
- package/dist/core/worktree-sync/store.d.ts +3 -3
- package/dist/core/worktree-sync/store.d.ts.map +1 -1
- package/dist/core/worktree-sync/store.js +10 -5
- package/dist/core/worktree-sync/store.js.map +1 -1
- package/dist/index.d.ts +15 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14 -3
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +149 -51
- package/dist/main.js.map +1 -1
- package/dist/migrations.d.ts +4 -6
- package/dist/migrations.d.ts.map +1 -1
- package/dist/migrations.js +186 -28
- package/dist/migrations.js.map +1 -1
- package/dist/modes/interactive/auth-dialogs-controller.d.ts +4 -0
- package/dist/modes/interactive/auth-dialogs-controller.d.ts.map +1 -1
- package/dist/modes/interactive/auth-dialogs-controller.js +60 -1
- package/dist/modes/interactive/auth-dialogs-controller.js.map +1 -1
- package/dist/modes/interactive/clipboard-input.d.ts +7 -1
- package/dist/modes/interactive/clipboard-input.d.ts.map +1 -1
- package/dist/modes/interactive/clipboard-input.js +44 -6
- package/dist/modes/interactive/clipboard-input.js.map +1 -1
- package/dist/modes/interactive/components/activity-lane.d.ts +55 -0
- package/dist/modes/interactive/components/activity-lane.d.ts.map +1 -0
- package/dist/modes/interactive/components/activity-lane.js +304 -0
- package/dist/modes/interactive/components/activity-lane.js.map +1 -0
- package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/assistant-message.js +19 -10
- package/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/dist/modes/interactive/components/expandable-text.d.ts +12 -0
- package/dist/modes/interactive/components/expandable-text.d.ts.map +1 -0
- package/dist/modes/interactive/components/expandable-text.js +17 -0
- package/dist/modes/interactive/components/expandable-text.js.map +1 -0
- package/dist/modes/interactive/components/extension-editor.d.ts +15 -1
- package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
- package/dist/modes/interactive/components/extension-editor.js +30 -21
- package/dist/modes/interactive/components/extension-editor.js.map +1 -1
- package/dist/modes/interactive/components/extension-input.d.ts +1 -0
- package/dist/modes/interactive/components/extension-input.d.ts.map +1 -1
- package/dist/modes/interactive/components/extension-input.js +6 -1
- package/dist/modes/interactive/components/extension-input.js.map +1 -1
- package/dist/modes/interactive/components/scoped-models-selector.d.ts +2 -2
- package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/scoped-models-selector.js +23 -14
- package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +6 -6
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/components/tool-execution.d.ts +19 -11
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-execution.js +53 -20
- package/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/dist/modes/interactive/components/tool-group.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-group.js +3 -0
- package/dist/modes/interactive/components/tool-group.js.map +1 -1
- package/dist/modes/interactive/components/usage-action-selector.d.ts +16 -0
- package/dist/modes/interactive/components/usage-action-selector.d.ts.map +1 -0
- package/dist/modes/interactive/components/usage-action-selector.js +46 -0
- package/dist/modes/interactive/components/usage-action-selector.js.map +1 -0
- package/dist/modes/interactive/config-backup.d.ts.map +1 -1
- package/dist/modes/interactive/config-backup.js +5 -4
- package/dist/modes/interactive/config-backup.js.map +1 -1
- package/dist/modes/interactive/extension-ui-host.d.ts +14 -0
- package/dist/modes/interactive/extension-ui-host.d.ts.map +1 -1
- package/dist/modes/interactive/extension-ui-host.js +77 -15
- package/dist/modes/interactive/extension-ui-host.js.map +1 -1
- package/dist/modes/interactive/external-editor.d.ts +15 -0
- package/dist/modes/interactive/external-editor.d.ts.map +1 -1
- package/dist/modes/interactive/external-editor.js +27 -19
- package/dist/modes/interactive/external-editor.js.map +1 -1
- package/dist/modes/interactive/history-reload-math.d.ts +6 -2
- package/dist/modes/interactive/history-reload-math.d.ts.map +1 -1
- package/dist/modes/interactive/history-reload-math.js +10 -5
- package/dist/modes/interactive/history-reload-math.js.map +1 -1
- package/dist/modes/interactive/interactive-event-controller.d.ts +74 -0
- package/dist/modes/interactive/interactive-event-controller.d.ts.map +1 -0
- package/dist/modes/interactive/interactive-event-controller.js +328 -0
- package/dist/modes/interactive/interactive-event-controller.js.map +1 -0
- package/dist/modes/interactive/interactive-mode.d.ts +22 -24
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +245 -595
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/key-handlers.d.ts.map +1 -1
- package/dist/modes/interactive/key-handlers.js +1 -1
- package/dist/modes/interactive/key-handlers.js.map +1 -1
- package/dist/modes/interactive/loaded-resources-view.d.ts +20 -0
- package/dist/modes/interactive/loaded-resources-view.d.ts.map +1 -0
- package/dist/modes/interactive/loaded-resources-view.js +147 -0
- package/dist/modes/interactive/loaded-resources-view.js.map +1 -0
- package/dist/modes/interactive/profile-menu-controller.d.ts.map +1 -1
- package/dist/modes/interactive/profile-menu-controller.js +7 -7
- package/dist/modes/interactive/profile-menu-controller.js.map +1 -1
- package/dist/modes/interactive/report-commands.d.ts.map +1 -1
- package/dist/modes/interactive/report-commands.js +21 -0
- package/dist/modes/interactive/report-commands.js.map +1 -1
- package/dist/modes/interactive/resource-display.d.ts.map +1 -1
- package/dist/modes/interactive/resource-display.js +8 -2
- package/dist/modes/interactive/resource-display.js.map +1 -1
- package/dist/modes/interactive/resource-shell-commands.d.ts +1 -5
- package/dist/modes/interactive/resource-shell-commands.d.ts.map +1 -1
- package/dist/modes/interactive/resource-shell-commands.js +0 -5
- package/dist/modes/interactive/resource-shell-commands.js.map +1 -1
- package/dist/modes/interactive/session-flow-commands.d.ts +27 -8
- package/dist/modes/interactive/session-flow-commands.d.ts.map +1 -1
- package/dist/modes/interactive/session-flow-commands.js +150 -64
- package/dist/modes/interactive/session-flow-commands.js.map +1 -1
- package/dist/modes/interactive/session-io-commands.d.ts.map +1 -1
- package/dist/modes/interactive/session-io-commands.js +16 -6
- package/dist/modes/interactive/session-io-commands.js.map +1 -1
- package/dist/modes/interactive/session-replacement-errors.d.ts +7 -0
- package/dist/modes/interactive/session-replacement-errors.d.ts.map +1 -0
- package/dist/modes/interactive/session-replacement-errors.js +15 -0
- package/dist/modes/interactive/session-replacement-errors.js.map +1 -0
- package/dist/modes/interactive/startup-checks.d.ts.map +1 -1
- package/dist/modes/interactive/startup-checks.js +1 -1
- package/dist/modes/interactive/startup-checks.js.map +1 -1
- package/dist/modes/interactive/usage-commands.d.ts +34 -0
- package/dist/modes/interactive/usage-commands.d.ts.map +1 -0
- package/dist/modes/interactive/usage-commands.js +225 -0
- package/dist/modes/interactive/usage-commands.js.map +1 -0
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +82 -23
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-types.d.ts +15 -0
- package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-types.js +138 -1
- package/dist/modes/rpc/rpc-types.js.map +1 -1
- package/dist/utils/clipboard-image.d.ts.map +1 -1
- package/dist/utils/clipboard-image.js +2 -13
- package/dist/utils/clipboard-image.js.map +1 -1
- package/dist/utils/clipboard-native.d.ts +1 -0
- package/dist/utils/clipboard-native.d.ts.map +1 -1
- package/dist/utils/clipboard-native.js.map +1 -1
- package/dist/utils/clipboard.d.ts +1 -0
- package/dist/utils/clipboard.d.ts.map +1 -1
- package/dist/utils/clipboard.js +52 -1
- package/dist/utils/clipboard.js.map +1 -1
- package/dist/utils/platform.d.ts +3 -0
- package/dist/utils/platform.d.ts.map +1 -0
- package/dist/utils/platform.js +8 -0
- package/dist/utils/platform.js.map +1 -0
- package/dist/utils/shell.d.ts.map +1 -1
- package/dist/utils/shell.js +5 -3
- package/dist/utils/shell.js.map +1 -1
- package/dist/utils/version-check.d.ts.map +1 -1
- package/dist/utils/version-check.js +3 -1
- package/dist/utils/version-check.js.map +1 -1
- package/dist/utils/work-directory.d.ts +11 -0
- package/dist/utils/work-directory.d.ts.map +1 -1
- package/dist/utils/work-directory.js +30 -9
- package/dist/utils/work-directory.js.map +1 -1
- package/docs/UI_POLISH_ORCHESTRATION.md +38 -0
- package/docs/UI_QUALITY_ORCHESTRATION.md +28 -0
- package/docs/docs.json +4 -0
- package/docs/extensions.md +62 -2
- package/docs/harness-architecture.md +156 -0
- package/docs/keybindings.md +11 -1
- package/docs/process-matrix.md +59 -26
- package/docs/providers.md +9 -1
- package/docs/rpc.md +45 -2
- package/docs/sessions.md +15 -0
- package/docs/settings.md +125 -11
- package/docs/task-steps.md +8 -0
- package/docs/telemetry.md +19 -0
- package/docs/tmux-agent-manager.md +10 -3
- package/docs/ui-reference/README.md +9 -0
- package/docs/ui-reference/orchestration-after.txt +14 -0
- package/docs/ui-reference/orchestration-before.txt +13 -0
- package/docs/ui-reference/orchestration-reference.txt +16 -0
- package/docs/usage.md +22 -2
- package/docs/work-directory.md +19 -8
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/question.ts +34 -245
- package/examples/extensions/questionnaire.ts +41 -407
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/npm-shrinkwrap.json +13 -13
- package/package.json +4 -4
- package/dist/core/delegation/session-worker-result.d.ts +0 -46
- package/dist/core/delegation/session-worker-result.d.ts.map +0 -1
- package/dist/core/delegation/session-worker-result.js +0 -113
- package/dist/core/delegation/session-worker-result.js.map +0 -1
- package/dist/core/delegation/worker-result.d.ts.map +0 -1
- package/dist/core/delegation/worker-result.js.map +0 -1
- package/dist/core/telemetry.d.ts.map +0 -1
- package/dist/core/telemetry.js.map +0 -1
|
@@ -0,0 +1,1600 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { isDeepStrictEqual } from "node:util";
|
|
3
|
+
import { createAgentIdentity } from "./agent-resume.js";
|
|
4
|
+
import { isHarnessCapability, ORCHESTRATION_SCHEMA_VERSION, toJsonObject, } from "./contracts.js";
|
|
5
|
+
import { OrchestrationSnapshotRequiredError } from "./event-store.js";
|
|
6
|
+
import { validateRiskBudget } from "./risk-budget.js";
|
|
7
|
+
import { parseWorkerExecutionContract } from "./worker-execution-contract.js";
|
|
8
|
+
export class DurableTaskRuntimeError extends Error {
|
|
9
|
+
constructor(message) {
|
|
10
|
+
super(message);
|
|
11
|
+
this.name = "DurableTaskRuntimeError";
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
function emptyProjection() {
|
|
15
|
+
return {
|
|
16
|
+
lastOrdinal: 0,
|
|
17
|
+
agents: {},
|
|
18
|
+
objectives: {},
|
|
19
|
+
tasks: {},
|
|
20
|
+
attempts: {},
|
|
21
|
+
checkpoints: {},
|
|
22
|
+
approvals: {},
|
|
23
|
+
notifications: {},
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
function cloneProjection(state) {
|
|
27
|
+
return structuredClone(state);
|
|
28
|
+
}
|
|
29
|
+
function projectionFromSnapshot(projection, throughOrdinal) {
|
|
30
|
+
const candidate = record(projection, "orchestration projection snapshot");
|
|
31
|
+
if (candidate.lastOrdinal !== throughOrdinal) {
|
|
32
|
+
throw new DurableTaskRuntimeError("Orchestration projection snapshot ordinal does not match its baseline.");
|
|
33
|
+
}
|
|
34
|
+
for (const field of [
|
|
35
|
+
"agents",
|
|
36
|
+
"objectives",
|
|
37
|
+
"tasks",
|
|
38
|
+
"attempts",
|
|
39
|
+
"checkpoints",
|
|
40
|
+
"approvals",
|
|
41
|
+
"notifications",
|
|
42
|
+
]) {
|
|
43
|
+
record(candidate[field], `orchestration projection snapshot.${field}`);
|
|
44
|
+
}
|
|
45
|
+
return structuredClone(candidate);
|
|
46
|
+
}
|
|
47
|
+
function record(value, label) {
|
|
48
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
49
|
+
throw new DurableTaskRuntimeError(`${label} is not an object.`);
|
|
50
|
+
}
|
|
51
|
+
return value;
|
|
52
|
+
}
|
|
53
|
+
function string(value, label) {
|
|
54
|
+
if (typeof value !== "string" || value.length === 0)
|
|
55
|
+
throw new DurableTaskRuntimeError(`${label} is required.`);
|
|
56
|
+
return value;
|
|
57
|
+
}
|
|
58
|
+
function number(value, label) {
|
|
59
|
+
if (typeof value !== "number" || !Number.isFinite(value))
|
|
60
|
+
throw new DurableTaskRuntimeError(`${label} is invalid.`);
|
|
61
|
+
return value;
|
|
62
|
+
}
|
|
63
|
+
function boolean(value, label) {
|
|
64
|
+
if (typeof value !== "boolean")
|
|
65
|
+
throw new DurableTaskRuntimeError(`${label} is invalid.`);
|
|
66
|
+
return value;
|
|
67
|
+
}
|
|
68
|
+
function assertRiskBudget(budget, label) {
|
|
69
|
+
try {
|
|
70
|
+
validateRiskBudget(budget ?? {}, label);
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
throw new DurableTaskRuntimeError(error instanceof Error ? error.message : String(error));
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
function stringArray(value, label) {
|
|
77
|
+
if (!Array.isArray(value) || !value.every((entry) => typeof entry === "string")) {
|
|
78
|
+
throw new DurableTaskRuntimeError(`${label} must be a string array.`);
|
|
79
|
+
}
|
|
80
|
+
return [...value];
|
|
81
|
+
}
|
|
82
|
+
const DISPATCH_FIELDS = new Set([
|
|
83
|
+
"taskId",
|
|
84
|
+
"profileId",
|
|
85
|
+
"instructions",
|
|
86
|
+
"resourcePointerIds",
|
|
87
|
+
"executionKind",
|
|
88
|
+
"logicalLaneId",
|
|
89
|
+
"dispatchSequence",
|
|
90
|
+
"provider",
|
|
91
|
+
"authorizationId",
|
|
92
|
+
"worktreeLaneKey",
|
|
93
|
+
"executionContract",
|
|
94
|
+
]);
|
|
95
|
+
function dispatchFromValue(value, label) {
|
|
96
|
+
const dispatch = record(value, label);
|
|
97
|
+
const unsupportedField = Object.keys(dispatch).find((field) => !DISPATCH_FIELDS.has(field));
|
|
98
|
+
if (unsupportedField)
|
|
99
|
+
throw new DurableTaskRuntimeError(`${label}.${unsupportedField} is unsupported.`);
|
|
100
|
+
const executionKind = dispatch.executionKind;
|
|
101
|
+
if (executionKind !== undefined && executionKind !== "in-process" && executionKind !== "managed-process") {
|
|
102
|
+
throw new DurableTaskRuntimeError(`${label}.executionKind is invalid.`);
|
|
103
|
+
}
|
|
104
|
+
for (const field of ["logicalLaneId", "provider", "authorizationId", "worktreeLaneKey"]) {
|
|
105
|
+
if (dispatch[field] !== undefined && typeof dispatch[field] !== "string") {
|
|
106
|
+
throw new DurableTaskRuntimeError(`${label}.${field} is invalid.`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
if (dispatch.dispatchSequence !== undefined &&
|
|
110
|
+
(!Number.isSafeInteger(dispatch.dispatchSequence) || Number(dispatch.dispatchSequence) < 1)) {
|
|
111
|
+
throw new DurableTaskRuntimeError(`${label}.dispatchSequence is invalid.`);
|
|
112
|
+
}
|
|
113
|
+
let executionContract;
|
|
114
|
+
try {
|
|
115
|
+
executionContract = dispatch.executionContract
|
|
116
|
+
? parseWorkerExecutionContract(dispatch.executionContract)
|
|
117
|
+
: undefined;
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
throw new DurableTaskRuntimeError(error instanceof Error ? error.message : String(error));
|
|
121
|
+
}
|
|
122
|
+
if (executionContract && executionKind === "managed-process") {
|
|
123
|
+
throw new DurableTaskRuntimeError(`${label} cannot combine a worker execution contract with managed execution.`);
|
|
124
|
+
}
|
|
125
|
+
return {
|
|
126
|
+
taskId: string(dispatch.taskId, `${label}.taskId`),
|
|
127
|
+
profileId: string(dispatch.profileId, `${label}.profileId`),
|
|
128
|
+
instructions: string(dispatch.instructions, `${label}.instructions`),
|
|
129
|
+
resourcePointerIds: stringArray(dispatch.resourcePointerIds, `${label}.resourcePointerIds`),
|
|
130
|
+
...(executionKind ? { executionKind } : {}),
|
|
131
|
+
...(typeof dispatch.logicalLaneId === "string" ? { logicalLaneId: dispatch.logicalLaneId } : {}),
|
|
132
|
+
...(typeof dispatch.dispatchSequence === "number" ? { dispatchSequence: dispatch.dispatchSequence } : {}),
|
|
133
|
+
...(typeof dispatch.provider === "string" ? { provider: dispatch.provider } : {}),
|
|
134
|
+
...(typeof dispatch.authorizationId === "string" ? { authorizationId: dispatch.authorizationId } : {}),
|
|
135
|
+
...(typeof dispatch.worktreeLaneKey === "string" ? { worktreeLaneKey: dispatch.worktreeLaneKey } : {}),
|
|
136
|
+
...(executionContract ? { executionContract } : {}),
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
function objectiveFromPayload(payload) {
|
|
140
|
+
return structuredClone(record(payload.objective, "objective"));
|
|
141
|
+
}
|
|
142
|
+
function evidenceFromPayload(payload) {
|
|
143
|
+
return structuredClone(record(payload.evidence, "evidence"));
|
|
144
|
+
}
|
|
145
|
+
function assertAcceptanceCriteria(criteria) {
|
|
146
|
+
const criterionIds = criteria.map((criterion) => criterion.id);
|
|
147
|
+
if (criterionIds.some((id) => !id.trim()) ||
|
|
148
|
+
new Set(criterionIds).size !== criterionIds.length ||
|
|
149
|
+
criteria.some((criterion) => !criterion.description.trim())) {
|
|
150
|
+
throw new DurableTaskRuntimeError("Objective acceptance criteria require unique ids and descriptions.");
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
function taskFromPayload(payload) {
|
|
154
|
+
return structuredClone(record(payload.task, "task"));
|
|
155
|
+
}
|
|
156
|
+
function leaseFromPayload(payload) {
|
|
157
|
+
const lease = record(payload.lease, "lease");
|
|
158
|
+
return {
|
|
159
|
+
leaseId: string(lease.leaseId, "lease.leaseId"),
|
|
160
|
+
attemptId: string(lease.attemptId, "lease.attemptId"),
|
|
161
|
+
ownerId: string(lease.ownerId, "lease.ownerId"),
|
|
162
|
+
fencingToken: number(lease.fencingToken, "lease.fencingToken"),
|
|
163
|
+
issuedAt: string(lease.issuedAt, "lease.issuedAt"),
|
|
164
|
+
expiresAt: string(lease.expiresAt, "lease.expiresAt"),
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
function checkpointFromPayload(payload) {
|
|
168
|
+
const checkpoint = record(payload.checkpoint, "checkpoint");
|
|
169
|
+
return {
|
|
170
|
+
checkpointId: string(checkpoint.checkpointId, "checkpoint.checkpointId"),
|
|
171
|
+
attemptId: string(checkpoint.attemptId, "checkpoint.attemptId"),
|
|
172
|
+
fencingToken: number(checkpoint.fencingToken, "checkpoint.fencingToken"),
|
|
173
|
+
summary: string(checkpoint.summary, "checkpoint.summary"),
|
|
174
|
+
artifactIds: stringArray(checkpoint.artifactIds, "checkpoint.artifactIds"),
|
|
175
|
+
evidenceIds: stringArray(checkpoint.evidenceIds, "checkpoint.evidenceIds"),
|
|
176
|
+
createdAt: string(checkpoint.createdAt, "checkpoint.createdAt"),
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
function resultFromPayload(payload) {
|
|
180
|
+
return structuredClone(record(payload.result, "result"));
|
|
181
|
+
}
|
|
182
|
+
function agentFromPayload(payload) {
|
|
183
|
+
return structuredClone(record(payload.agent, "agent"));
|
|
184
|
+
}
|
|
185
|
+
function approvalFromPayload(payload) {
|
|
186
|
+
const approval = record(payload.approval, "approval");
|
|
187
|
+
const capabilities = stringArray(approval.requestedCapabilities, "approval.requestedCapabilities");
|
|
188
|
+
if (!capabilities.every(isHarnessCapability)) {
|
|
189
|
+
throw new DurableTaskRuntimeError("Approval contains an unknown capability.");
|
|
190
|
+
}
|
|
191
|
+
const requestedBudget = approval.requestedBudget
|
|
192
|
+
? structuredClone(record(approval.requestedBudget, "approval.requestedBudget"))
|
|
193
|
+
: undefined;
|
|
194
|
+
assertRiskBudget(requestedBudget, "approval.requestedBudget");
|
|
195
|
+
if (capabilities.length === 0 && !requestedBudget) {
|
|
196
|
+
throw new DurableTaskRuntimeError("Approval must request capabilities or budget.");
|
|
197
|
+
}
|
|
198
|
+
if (approval.schemaVersion !== ORCHESTRATION_SCHEMA_VERSION) {
|
|
199
|
+
throw new DurableTaskRuntimeError("Approval schema version is invalid.");
|
|
200
|
+
}
|
|
201
|
+
return {
|
|
202
|
+
schemaVersion: ORCHESTRATION_SCHEMA_VERSION,
|
|
203
|
+
approvalId: string(approval.approvalId, "approval.approvalId"),
|
|
204
|
+
objectiveId: string(approval.objectiveId, "approval.objectiveId"),
|
|
205
|
+
...(typeof approval.taskId === "string" ? { taskId: string(approval.taskId, "approval.taskId") } : {}),
|
|
206
|
+
...(typeof approval.attemptId === "string"
|
|
207
|
+
? { attemptId: string(approval.attemptId, "approval.attemptId") }
|
|
208
|
+
: {}),
|
|
209
|
+
reasonCode: string(approval.reasonCode, "approval.reasonCode"),
|
|
210
|
+
summary: string(approval.summary, "approval.summary"),
|
|
211
|
+
requestedCapabilities: capabilities,
|
|
212
|
+
...(requestedBudget ? { requestedBudget } : {}),
|
|
213
|
+
reversible: boolean(approval.reversible, "approval.reversible"),
|
|
214
|
+
createdAt: string(approval.createdAt, "approval.createdAt"),
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
function approvalResolutionFromPayload(payload) {
|
|
218
|
+
const resolution = record(payload.resolution, "resolution");
|
|
219
|
+
if (resolution.schemaVersion !== ORCHESTRATION_SCHEMA_VERSION) {
|
|
220
|
+
throw new DurableTaskRuntimeError("Approval resolution schema version is invalid.");
|
|
221
|
+
}
|
|
222
|
+
const outcome = string(resolution.outcome, "resolution.outcome");
|
|
223
|
+
if (outcome !== "approved" && outcome !== "rejected") {
|
|
224
|
+
throw new DurableTaskRuntimeError(`Unknown approval outcome '${outcome}'.`);
|
|
225
|
+
}
|
|
226
|
+
return {
|
|
227
|
+
schemaVersion: ORCHESTRATION_SCHEMA_VERSION,
|
|
228
|
+
approvalId: string(resolution.approvalId, "resolution.approvalId"),
|
|
229
|
+
outcome,
|
|
230
|
+
reasonCode: string(resolution.reasonCode, "resolution.reasonCode"),
|
|
231
|
+
resolvedAt: string(resolution.resolvedAt, "resolution.resolvedAt"),
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
function updateObjectiveStatus(state, objectiveId, status, at) {
|
|
235
|
+
const current = state.objectives[objectiveId];
|
|
236
|
+
if (!current)
|
|
237
|
+
throw new DurableTaskRuntimeError(`Unknown objective '${objectiveId}'.`);
|
|
238
|
+
state.objectives[objectiveId] = {
|
|
239
|
+
...current,
|
|
240
|
+
objective: { ...current.objective, status, updatedAt: at },
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
function terminalAttemptStatus(status) {
|
|
244
|
+
return ["completed", "partial", "blocked", "failed", "cancelled", "expired"].includes(status);
|
|
245
|
+
}
|
|
246
|
+
function cancelOpenObjectiveWork(state, objectiveId, at, reasonCode) {
|
|
247
|
+
const objective = state.objectives[objectiveId];
|
|
248
|
+
const tasks = state.tasks;
|
|
249
|
+
const attempts = state.attempts;
|
|
250
|
+
for (const taskId of objective?.taskIds ?? []) {
|
|
251
|
+
const taskState = tasks[taskId];
|
|
252
|
+
if (taskState && !["completed", "failed", "cancelled"].includes(taskState.task.status)) {
|
|
253
|
+
tasks[taskId] = {
|
|
254
|
+
...taskState,
|
|
255
|
+
task: { ...taskState.task, status: "cancelled", updatedAt: at },
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
for (const attemptId of taskState?.attemptIds ?? []) {
|
|
259
|
+
const attempt = attempts[attemptId];
|
|
260
|
+
if (attempt && !terminalAttemptStatus(attempt.status)) {
|
|
261
|
+
attempts[attemptId] = { ...attempt, status: "cancelled", reasonCode, updatedAt: at };
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
function taskStatusForResult(status) {
|
|
267
|
+
if (status === "completed")
|
|
268
|
+
return "completed";
|
|
269
|
+
if (status === "failed")
|
|
270
|
+
return "failed";
|
|
271
|
+
if (status === "cancelled")
|
|
272
|
+
return "cancelled";
|
|
273
|
+
return "blocked";
|
|
274
|
+
}
|
|
275
|
+
function missingTrustedCriteria(result, criterionIds) {
|
|
276
|
+
const proven = new Set(result.evidence.flatMap((evidence) => (evidence.trusted && evidence.criterionId ? [evidence.criterionId] : [])));
|
|
277
|
+
return criterionIds.filter((criterionId) => !proven.has(criterionId));
|
|
278
|
+
}
|
|
279
|
+
function refreshReadyTasks(state, objectiveId, at) {
|
|
280
|
+
const objective = state.objectives[objectiveId];
|
|
281
|
+
if (!objective || objective.objective.status !== "active")
|
|
282
|
+
return;
|
|
283
|
+
const mutableTasks = state.tasks;
|
|
284
|
+
for (const taskId of objective.taskIds) {
|
|
285
|
+
const current = mutableTasks[taskId];
|
|
286
|
+
if (!current || current.task.status !== "pending")
|
|
287
|
+
continue;
|
|
288
|
+
if (current.task.dependsOn.every((dependencyId) => mutableTasks[dependencyId]?.task.status === "completed")) {
|
|
289
|
+
mutableTasks[taskId] = { ...current, task: { ...current.task, status: "ready", updatedAt: at } };
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
export function reduceOrchestrationEvent(projection, event) {
|
|
294
|
+
if (event.ordinal <= projection.lastOrdinal)
|
|
295
|
+
return projection;
|
|
296
|
+
const state = cloneProjection(projection);
|
|
297
|
+
const agents = state.agents;
|
|
298
|
+
const objectives = state.objectives;
|
|
299
|
+
const tasks = state.tasks;
|
|
300
|
+
const attempts = state.attempts;
|
|
301
|
+
const checkpoints = state.checkpoints;
|
|
302
|
+
const approvals = state.approvals;
|
|
303
|
+
const notifications = state.notifications;
|
|
304
|
+
switch (event.type) {
|
|
305
|
+
case "objective.created": {
|
|
306
|
+
const objective = objectiveFromPayload(event.payload);
|
|
307
|
+
if (objectives[objective.objectiveId]) {
|
|
308
|
+
throw new DurableTaskRuntimeError(`Objective '${objective.objectiveId}' was created more than once.`);
|
|
309
|
+
}
|
|
310
|
+
objectives[objective.objectiveId] = { objective, taskIds: [], evidence: [] };
|
|
311
|
+
break;
|
|
312
|
+
}
|
|
313
|
+
case "objective.updated": {
|
|
314
|
+
const objective = objectiveFromPayload(event.payload);
|
|
315
|
+
const current = objectives[event.aggregateId];
|
|
316
|
+
if (!current)
|
|
317
|
+
throw new DurableTaskRuntimeError(`Unknown objective '${event.aggregateId}'.`);
|
|
318
|
+
if (objective.objectiveId !== event.aggregateId) {
|
|
319
|
+
throw new DurableTaskRuntimeError(`Updated objective id '${objective.objectiveId}' does not match aggregate.`);
|
|
320
|
+
}
|
|
321
|
+
objectives[event.aggregateId] = { ...current, objective };
|
|
322
|
+
break;
|
|
323
|
+
}
|
|
324
|
+
case "objective.evidence_recorded": {
|
|
325
|
+
const evidence = evidenceFromPayload(event.payload);
|
|
326
|
+
const current = objectives[event.aggregateId];
|
|
327
|
+
if (!current)
|
|
328
|
+
throw new DurableTaskRuntimeError(`Unknown objective '${event.aggregateId}'.`);
|
|
329
|
+
const existing = current.evidence.find((candidate) => candidate.evidenceId === evidence.evidenceId);
|
|
330
|
+
if (existing && !isDeepStrictEqual(existing, evidence)) {
|
|
331
|
+
throw new DurableTaskRuntimeError(`Objective evidence '${evidence.evidenceId}' has conflicting content.`);
|
|
332
|
+
}
|
|
333
|
+
if (!existing)
|
|
334
|
+
objectives[event.aggregateId] = { ...current, evidence: [...current.evidence, evidence] };
|
|
335
|
+
break;
|
|
336
|
+
}
|
|
337
|
+
case "objective.paused":
|
|
338
|
+
updateObjectiveStatus(state, event.aggregateId, "paused", event.occurredAt);
|
|
339
|
+
break;
|
|
340
|
+
case "objective.resumed":
|
|
341
|
+
updateObjectiveStatus(state, event.aggregateId, "active", event.occurredAt);
|
|
342
|
+
refreshReadyTasks(state, event.aggregateId, event.occurredAt);
|
|
343
|
+
break;
|
|
344
|
+
case "objective.completed":
|
|
345
|
+
updateObjectiveStatus(state, event.aggregateId, "completed", event.occurredAt);
|
|
346
|
+
cancelOpenObjectiveWork(state, event.aggregateId, event.occurredAt, "objective_completed");
|
|
347
|
+
break;
|
|
348
|
+
case "objective.cancelled": {
|
|
349
|
+
updateObjectiveStatus(state, event.aggregateId, "cancelled", event.occurredAt);
|
|
350
|
+
cancelOpenObjectiveWork(state, event.aggregateId, event.occurredAt, "objective_cancelled");
|
|
351
|
+
break;
|
|
352
|
+
}
|
|
353
|
+
case "task.created": {
|
|
354
|
+
const task = taskFromPayload(event.payload);
|
|
355
|
+
const objective = objectives[task.objectiveId];
|
|
356
|
+
if (!objective)
|
|
357
|
+
throw new DurableTaskRuntimeError(`Task '${task.taskId}' references an unknown objective.`);
|
|
358
|
+
if (tasks[task.taskId])
|
|
359
|
+
throw new DurableTaskRuntimeError(`Task '${task.taskId}' was created more than once.`);
|
|
360
|
+
tasks[task.taskId] = { task, attemptIds: [] };
|
|
361
|
+
objectives[task.objectiveId] = { ...objective, taskIds: [...objective.taskIds, task.taskId] };
|
|
362
|
+
refreshReadyTasks(state, task.objectiveId, event.occurredAt);
|
|
363
|
+
break;
|
|
364
|
+
}
|
|
365
|
+
case "task.ready": {
|
|
366
|
+
const taskId = string(event.payload.taskId, "task.ready.taskId");
|
|
367
|
+
const current = tasks[taskId];
|
|
368
|
+
if (!current)
|
|
369
|
+
throw new DurableTaskRuntimeError(`Unknown task '${taskId}'.`);
|
|
370
|
+
tasks[taskId] = { ...current, task: { ...current.task, status: "ready", updatedAt: event.occurredAt } };
|
|
371
|
+
break;
|
|
372
|
+
}
|
|
373
|
+
case "task.failed": {
|
|
374
|
+
const taskId = string(event.payload.taskId, "task.failed.taskId");
|
|
375
|
+
const current = tasks[taskId];
|
|
376
|
+
if (!current)
|
|
377
|
+
throw new DurableTaskRuntimeError(`Unknown task '${taskId}'.`);
|
|
378
|
+
tasks[taskId] = { ...current, task: { ...current.task, status: "failed", updatedAt: event.occurredAt } };
|
|
379
|
+
break;
|
|
380
|
+
}
|
|
381
|
+
case "task.verification_finished": {
|
|
382
|
+
const taskId = string(event.payload.taskId, "task.verification_finished.taskId");
|
|
383
|
+
const verifierTaskId = string(event.payload.verifierTaskId, "task.verification_finished.verifierTaskId");
|
|
384
|
+
const verifierAttemptId = string(event.payload.verifierAttemptId, "task.verification_finished.verifierAttemptId");
|
|
385
|
+
const verdict = string(event.payload.verdict, "task.verification_finished.verdict");
|
|
386
|
+
if (verdict !== "accepted" && verdict !== "rejected" && verdict !== "inconclusive") {
|
|
387
|
+
throw new DurableTaskRuntimeError(`Unknown verification verdict '${verdict}'.`);
|
|
388
|
+
}
|
|
389
|
+
const current = tasks[taskId];
|
|
390
|
+
if (!current)
|
|
391
|
+
throw new DurableTaskRuntimeError(`Unknown task '${taskId}'.`);
|
|
392
|
+
const reasonCode = string(event.payload.reasonCode, "task.verification_finished.reasonCode");
|
|
393
|
+
tasks[taskId] = {
|
|
394
|
+
...current,
|
|
395
|
+
task: {
|
|
396
|
+
...current.task,
|
|
397
|
+
status: verdict === "accepted" ? "completed" : "blocked",
|
|
398
|
+
updatedAt: event.occurredAt,
|
|
399
|
+
},
|
|
400
|
+
verification: {
|
|
401
|
+
verifierTaskId,
|
|
402
|
+
verifierAttemptId,
|
|
403
|
+
verdict,
|
|
404
|
+
reasonCode,
|
|
405
|
+
completedAt: event.occurredAt,
|
|
406
|
+
},
|
|
407
|
+
};
|
|
408
|
+
refreshReadyTasks(state, current.task.objectiveId, event.occurredAt);
|
|
409
|
+
break;
|
|
410
|
+
}
|
|
411
|
+
case "agent.registered": {
|
|
412
|
+
const agent = agentFromPayload(event.payload);
|
|
413
|
+
if (agents[agent.agentId])
|
|
414
|
+
throw new DurableTaskRuntimeError(`Agent '${agent.agentId}' was registered more than once.`);
|
|
415
|
+
agents[agent.agentId] = agent;
|
|
416
|
+
break;
|
|
417
|
+
}
|
|
418
|
+
case "agent.suspended": {
|
|
419
|
+
const agentId = string(event.payload.agentId, "agent.suspended.agentId");
|
|
420
|
+
const agent = agents[agentId];
|
|
421
|
+
if (!agent)
|
|
422
|
+
throw new DurableTaskRuntimeError(`Unknown agent '${agentId}'.`);
|
|
423
|
+
agents[agentId] = { ...agent, status: "suspended", updatedAt: event.occurredAt };
|
|
424
|
+
break;
|
|
425
|
+
}
|
|
426
|
+
case "agent.resume_requested": {
|
|
427
|
+
const agentId = string(event.payload.agentId, "agent.resume_requested.agentId");
|
|
428
|
+
const agent = agents[agentId];
|
|
429
|
+
if (!agent)
|
|
430
|
+
throw new DurableTaskRuntimeError(`Unknown agent '${agentId}'.`);
|
|
431
|
+
agents[agentId] = { ...agent, status: "resuming", updatedAt: event.occurredAt };
|
|
432
|
+
break;
|
|
433
|
+
}
|
|
434
|
+
case "agent.resumed": {
|
|
435
|
+
const agentId = string(event.payload.agentId, "agent.resumed.agentId");
|
|
436
|
+
const attemptId = string(event.payload.attemptId, "agent.resumed.attemptId");
|
|
437
|
+
const agent = agents[agentId];
|
|
438
|
+
if (!agent)
|
|
439
|
+
throw new DurableTaskRuntimeError(`Unknown agent '${agentId}'.`);
|
|
440
|
+
agents[agentId] = { ...agent, status: "active", activeAttemptId: attemptId, updatedAt: event.occurredAt };
|
|
441
|
+
break;
|
|
442
|
+
}
|
|
443
|
+
case "attempt.queued": {
|
|
444
|
+
const attemptId = string(event.payload.attemptId, "attempt.queued.attemptId");
|
|
445
|
+
const taskId = string(event.payload.taskId, "attempt.queued.taskId");
|
|
446
|
+
const task = tasks[taskId];
|
|
447
|
+
if (!task)
|
|
448
|
+
throw new DurableTaskRuntimeError(`Attempt '${attemptId}' references an unknown task.`);
|
|
449
|
+
if (attempts[attemptId])
|
|
450
|
+
throw new DurableTaskRuntimeError(`Attempt '${attemptId}' was queued more than once.`);
|
|
451
|
+
const dispatch = dispatchFromValue(event.payload.dispatch, "attempt.queued.dispatch");
|
|
452
|
+
if (dispatch.taskId !== taskId) {
|
|
453
|
+
throw new DurableTaskRuntimeError(`Attempt '${attemptId}' dispatch task does not match its task.`);
|
|
454
|
+
}
|
|
455
|
+
if (dispatch.executionContract &&
|
|
456
|
+
(dispatch.executionContract.worker.profile.profileId !== dispatch.profileId ||
|
|
457
|
+
dispatch.executionContract.worker.profile.role !== task.task.role)) {
|
|
458
|
+
throw new DurableTaskRuntimeError(`Attempt '${attemptId}' execution contract does not match its dispatch.`);
|
|
459
|
+
}
|
|
460
|
+
attempts[attemptId] = {
|
|
461
|
+
attemptId,
|
|
462
|
+
taskId,
|
|
463
|
+
dispatch,
|
|
464
|
+
status: "queued",
|
|
465
|
+
...(typeof event.payload.grantId === "string" ? { grantId: event.payload.grantId } : {}),
|
|
466
|
+
checkpointIds: [],
|
|
467
|
+
createdAt: event.occurredAt,
|
|
468
|
+
updatedAt: event.occurredAt,
|
|
469
|
+
};
|
|
470
|
+
tasks[taskId] = {
|
|
471
|
+
...task,
|
|
472
|
+
task: { ...task.task, status: "running", updatedAt: event.occurredAt },
|
|
473
|
+
attemptIds: [...task.attemptIds, attemptId],
|
|
474
|
+
};
|
|
475
|
+
break;
|
|
476
|
+
}
|
|
477
|
+
case "attempt.grant_bound": {
|
|
478
|
+
const attemptId = string(event.payload.attemptId, "attempt.grant_bound.attemptId");
|
|
479
|
+
const attempt = attempts[attemptId];
|
|
480
|
+
if (!attempt)
|
|
481
|
+
throw new DurableTaskRuntimeError(`Unknown attempt '${attemptId}'.`);
|
|
482
|
+
const grant = event.payload.grant
|
|
483
|
+
? structuredClone(record(event.payload.grant, "attempt.grant_bound.grant"))
|
|
484
|
+
: undefined;
|
|
485
|
+
attempts[attemptId] = {
|
|
486
|
+
...attempt,
|
|
487
|
+
grantId: grant?.grantId ?? string(event.payload.grantId, "attempt.grant_bound.grantId"),
|
|
488
|
+
...(grant ? { grant } : {}),
|
|
489
|
+
updatedAt: event.occurredAt,
|
|
490
|
+
};
|
|
491
|
+
break;
|
|
492
|
+
}
|
|
493
|
+
case "attempt.leased": {
|
|
494
|
+
const lease = leaseFromPayload(event.payload);
|
|
495
|
+
const attempt = attempts[lease.attemptId];
|
|
496
|
+
if (!attempt)
|
|
497
|
+
throw new DurableTaskRuntimeError(`Unknown attempt '${lease.attemptId}'.`);
|
|
498
|
+
const agentId = typeof event.payload.agentId === "string" ? event.payload.agentId : undefined;
|
|
499
|
+
if (agentId) {
|
|
500
|
+
const agent = agents[agentId];
|
|
501
|
+
if (!agent)
|
|
502
|
+
throw new DurableTaskRuntimeError(`Unknown agent '${agentId}'.`);
|
|
503
|
+
agents[agentId] = {
|
|
504
|
+
...agent,
|
|
505
|
+
status: "active",
|
|
506
|
+
activeAttemptId: lease.attemptId,
|
|
507
|
+
updatedAt: event.occurredAt,
|
|
508
|
+
};
|
|
509
|
+
}
|
|
510
|
+
attempts[lease.attemptId] = {
|
|
511
|
+
...attempt,
|
|
512
|
+
status: "leased",
|
|
513
|
+
lease,
|
|
514
|
+
...(agentId ? { agentId } : {}),
|
|
515
|
+
updatedAt: event.occurredAt,
|
|
516
|
+
};
|
|
517
|
+
break;
|
|
518
|
+
}
|
|
519
|
+
case "attempt.started": {
|
|
520
|
+
const attemptId = string(event.payload.attemptId, "attempt.started.attemptId");
|
|
521
|
+
const attempt = attempts[attemptId];
|
|
522
|
+
if (!attempt)
|
|
523
|
+
throw new DurableTaskRuntimeError(`Unknown attempt '${attemptId}'.`);
|
|
524
|
+
attempts[attemptId] = { ...attempt, status: "running", updatedAt: event.occurredAt };
|
|
525
|
+
break;
|
|
526
|
+
}
|
|
527
|
+
case "attempt.checkpointed": {
|
|
528
|
+
const checkpoint = checkpointFromPayload(event.payload);
|
|
529
|
+
const attempt = attempts[checkpoint.attemptId];
|
|
530
|
+
if (!attempt)
|
|
531
|
+
throw new DurableTaskRuntimeError(`Unknown attempt '${checkpoint.attemptId}'.`);
|
|
532
|
+
checkpoints[checkpoint.checkpointId] = checkpoint;
|
|
533
|
+
attempts[checkpoint.attemptId] = {
|
|
534
|
+
...attempt,
|
|
535
|
+
checkpointIds: [...attempt.checkpointIds, checkpoint.checkpointId],
|
|
536
|
+
updatedAt: event.occurredAt,
|
|
537
|
+
};
|
|
538
|
+
if (attempt.agentId) {
|
|
539
|
+
const agent = agents[attempt.agentId];
|
|
540
|
+
if (agent) {
|
|
541
|
+
agents[attempt.agentId] = {
|
|
542
|
+
...agent,
|
|
543
|
+
resumeContext: { ...agent.resumeContext, latestCheckpointId: checkpoint.checkpointId },
|
|
544
|
+
updatedAt: event.occurredAt,
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
break;
|
|
549
|
+
}
|
|
550
|
+
case "attempt.suspended": {
|
|
551
|
+
const attemptId = string(event.payload.attemptId, "attempt.suspended.attemptId");
|
|
552
|
+
const attempt = attempts[attemptId];
|
|
553
|
+
if (!attempt)
|
|
554
|
+
throw new DurableTaskRuntimeError(`Unknown attempt '${attemptId}'.`);
|
|
555
|
+
attempts[attemptId] = {
|
|
556
|
+
...attempt,
|
|
557
|
+
status: "suspended",
|
|
558
|
+
...(typeof event.payload.reasonCode === "string" ? { reasonCode: event.payload.reasonCode } : {}),
|
|
559
|
+
updatedAt: event.occurredAt,
|
|
560
|
+
};
|
|
561
|
+
if (attempt.agentId) {
|
|
562
|
+
const agent = agents[attempt.agentId];
|
|
563
|
+
if (agent)
|
|
564
|
+
agents[attempt.agentId] = { ...agent, status: "suspended", updatedAt: event.occurredAt };
|
|
565
|
+
}
|
|
566
|
+
break;
|
|
567
|
+
}
|
|
568
|
+
case "attempt.resumed": {
|
|
569
|
+
const lease = leaseFromPayload(event.payload);
|
|
570
|
+
const agentId = string(event.payload.agentId, "attempt.resumed.agentId");
|
|
571
|
+
const attempt = attempts[lease.attemptId];
|
|
572
|
+
const agent = agents[agentId];
|
|
573
|
+
if (!attempt)
|
|
574
|
+
throw new DurableTaskRuntimeError(`Unknown attempt '${lease.attemptId}'.`);
|
|
575
|
+
if (!agent)
|
|
576
|
+
throw new DurableTaskRuntimeError(`Unknown agent '${agentId}'.`);
|
|
577
|
+
attempts[lease.attemptId] = {
|
|
578
|
+
...attempt,
|
|
579
|
+
status: "leased",
|
|
580
|
+
agentId,
|
|
581
|
+
lease,
|
|
582
|
+
updatedAt: event.occurredAt,
|
|
583
|
+
};
|
|
584
|
+
agents[agentId] = {
|
|
585
|
+
...agent,
|
|
586
|
+
status: "active",
|
|
587
|
+
activeAttemptId: lease.attemptId,
|
|
588
|
+
updatedAt: event.occurredAt,
|
|
589
|
+
};
|
|
590
|
+
break;
|
|
591
|
+
}
|
|
592
|
+
case "attempt.cancelled": {
|
|
593
|
+
const attemptId = string(event.payload.attemptId, "attempt.cancelled.attemptId");
|
|
594
|
+
const attempt = attempts[attemptId];
|
|
595
|
+
if (!attempt)
|
|
596
|
+
throw new DurableTaskRuntimeError(`Unknown attempt '${attemptId}'.`);
|
|
597
|
+
attempts[attemptId] = {
|
|
598
|
+
...attempt,
|
|
599
|
+
status: "cancelled",
|
|
600
|
+
reasonCode: string(event.payload.reasonCode, "attempt.cancelled.reasonCode"),
|
|
601
|
+
updatedAt: event.occurredAt,
|
|
602
|
+
};
|
|
603
|
+
const task = tasks[attempt.taskId];
|
|
604
|
+
if (task) {
|
|
605
|
+
tasks[attempt.taskId] = {
|
|
606
|
+
...task,
|
|
607
|
+
task: { ...task.task, status: "cancelled", updatedAt: event.occurredAt },
|
|
608
|
+
};
|
|
609
|
+
}
|
|
610
|
+
if (attempt.agentId) {
|
|
611
|
+
const agent = agents[attempt.agentId];
|
|
612
|
+
if (agent) {
|
|
613
|
+
const next = { ...agent, status: "registered", updatedAt: event.occurredAt };
|
|
614
|
+
delete next.activeAttemptId;
|
|
615
|
+
agents[attempt.agentId] = next;
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
break;
|
|
619
|
+
}
|
|
620
|
+
case "attempt.finished": {
|
|
621
|
+
const result = resultFromPayload(event.payload);
|
|
622
|
+
const attempt = attempts[result.attemptId];
|
|
623
|
+
if (!attempt)
|
|
624
|
+
throw new DurableTaskRuntimeError(`Unknown attempt '${result.attemptId}'.`);
|
|
625
|
+
attempts[result.attemptId] = { ...attempt, status: result.status, result, updatedAt: event.occurredAt };
|
|
626
|
+
const task = tasks[result.taskId];
|
|
627
|
+
if (!task)
|
|
628
|
+
throw new DurableTaskRuntimeError(`Unknown task '${result.taskId}'.`);
|
|
629
|
+
tasks[result.taskId] = {
|
|
630
|
+
...task,
|
|
631
|
+
task: { ...task.task, status: taskStatusForResult(result.status), updatedAt: event.occurredAt },
|
|
632
|
+
};
|
|
633
|
+
if (attempt.agentId) {
|
|
634
|
+
const agent = agents[attempt.agentId];
|
|
635
|
+
if (agent) {
|
|
636
|
+
const next = { ...agent, status: "registered", updatedAt: event.occurredAt };
|
|
637
|
+
delete next.activeAttemptId;
|
|
638
|
+
agents[attempt.agentId] = next;
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
refreshReadyTasks(state, task.task.objectiveId, event.occurredAt);
|
|
642
|
+
break;
|
|
643
|
+
}
|
|
644
|
+
case "attempt.lease_expired": {
|
|
645
|
+
const attemptId = string(event.payload.attemptId, "attempt.lease_expired.attemptId");
|
|
646
|
+
const attempt = attempts[attemptId];
|
|
647
|
+
if (!attempt)
|
|
648
|
+
throw new DurableTaskRuntimeError(`Unknown attempt '${attemptId}'.`);
|
|
649
|
+
attempts[attemptId] = {
|
|
650
|
+
...attempt,
|
|
651
|
+
status: "expired",
|
|
652
|
+
...(typeof event.payload.reasonCode === "string" ? { reasonCode: event.payload.reasonCode } : {}),
|
|
653
|
+
updatedAt: event.occurredAt,
|
|
654
|
+
};
|
|
655
|
+
const task = tasks[attempt.taskId];
|
|
656
|
+
if (task)
|
|
657
|
+
tasks[attempt.taskId] = { ...task, task: { ...task.task, status: "ready", updatedAt: event.occurredAt } };
|
|
658
|
+
break;
|
|
659
|
+
}
|
|
660
|
+
case "approval.requested": {
|
|
661
|
+
const approval = approvalFromPayload(event.payload);
|
|
662
|
+
if (approvals[approval.approvalId]) {
|
|
663
|
+
throw new DurableTaskRuntimeError(`Approval '${approval.approvalId}' was requested more than once.`);
|
|
664
|
+
}
|
|
665
|
+
approvals[approval.approvalId] = { request: approval, status: "pending" };
|
|
666
|
+
const notificationId = `approval-requested:${approval.approvalId}`;
|
|
667
|
+
if (notifications[notificationId]) {
|
|
668
|
+
throw new DurableTaskRuntimeError(`Notification '${notificationId}' already exists.`);
|
|
669
|
+
}
|
|
670
|
+
notifications[notificationId] = {
|
|
671
|
+
notificationId,
|
|
672
|
+
objectiveId: approval.objectiveId,
|
|
673
|
+
...(approval.attemptId ? { attemptId: approval.attemptId } : {}),
|
|
674
|
+
status: "pending",
|
|
675
|
+
message: approval.summary,
|
|
676
|
+
createdAt: event.occurredAt,
|
|
677
|
+
};
|
|
678
|
+
break;
|
|
679
|
+
}
|
|
680
|
+
case "approval.resolved": {
|
|
681
|
+
const resolution = approvalResolutionFromPayload(event.payload);
|
|
682
|
+
const approval = approvals[resolution.approvalId];
|
|
683
|
+
if (!approval)
|
|
684
|
+
throw new DurableTaskRuntimeError(`Unknown approval '${resolution.approvalId}'.`);
|
|
685
|
+
approvals[resolution.approvalId] = {
|
|
686
|
+
...approval,
|
|
687
|
+
status: resolution.outcome,
|
|
688
|
+
resolution,
|
|
689
|
+
};
|
|
690
|
+
const approvalNotification = notifications[`approval-requested:${resolution.approvalId}`];
|
|
691
|
+
if (approvalNotification?.status === "pending") {
|
|
692
|
+
notifications[approvalNotification.notificationId] = {
|
|
693
|
+
...approvalNotification,
|
|
694
|
+
status: "delivered",
|
|
695
|
+
deliveredAt: event.occurredAt,
|
|
696
|
+
};
|
|
697
|
+
}
|
|
698
|
+
if (resolution.outcome === "rejected" && approval.request.attemptId) {
|
|
699
|
+
const attempt = attempts[approval.request.attemptId];
|
|
700
|
+
if (attempt && !terminalAttemptStatus(attempt.status)) {
|
|
701
|
+
attempts[attempt.attemptId] = {
|
|
702
|
+
...attempt,
|
|
703
|
+
status: "blocked",
|
|
704
|
+
reasonCode: "approval_rejected",
|
|
705
|
+
updatedAt: event.occurredAt,
|
|
706
|
+
};
|
|
707
|
+
const task = tasks[attempt.taskId];
|
|
708
|
+
if (task) {
|
|
709
|
+
tasks[attempt.taskId] = {
|
|
710
|
+
...task,
|
|
711
|
+
task: { ...task.task, status: "blocked", updatedAt: event.occurredAt },
|
|
712
|
+
};
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
break;
|
|
717
|
+
}
|
|
718
|
+
case "notification.enqueued": {
|
|
719
|
+
const notificationId = string(event.payload.notificationId, "notification.enqueued.notificationId");
|
|
720
|
+
notifications[notificationId] = {
|
|
721
|
+
notificationId,
|
|
722
|
+
objectiveId: string(event.payload.objectiveId, "notification.enqueued.objectiveId"),
|
|
723
|
+
...(typeof event.payload.attemptId === "string" ? { attemptId: event.payload.attemptId } : {}),
|
|
724
|
+
status: "pending",
|
|
725
|
+
message: string(event.payload.message, "notification.enqueued.message"),
|
|
726
|
+
createdAt: event.occurredAt,
|
|
727
|
+
};
|
|
728
|
+
break;
|
|
729
|
+
}
|
|
730
|
+
case "notification.delivered": {
|
|
731
|
+
const notificationId = string(event.payload.notificationId, "notification.delivered.notificationId");
|
|
732
|
+
const notification = notifications[notificationId];
|
|
733
|
+
if (!notification)
|
|
734
|
+
throw new DurableTaskRuntimeError(`Unknown notification '${notificationId}'.`);
|
|
735
|
+
notifications[notificationId] = { ...notification, status: "delivered", deliveredAt: event.occurredAt };
|
|
736
|
+
break;
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
return { ...state, lastOrdinal: event.ordinal };
|
|
740
|
+
}
|
|
741
|
+
export function projectOrchestrationEvents(events) {
|
|
742
|
+
return events.reduce(reduceOrchestrationEvent, emptyProjection());
|
|
743
|
+
}
|
|
744
|
+
export class DurableTaskRuntime {
|
|
745
|
+
store;
|
|
746
|
+
now;
|
|
747
|
+
createId;
|
|
748
|
+
state;
|
|
749
|
+
constructor(options) {
|
|
750
|
+
this.store = options.store;
|
|
751
|
+
this.now = options.now ?? Date.now;
|
|
752
|
+
this.createId = options.createId ?? randomUUID;
|
|
753
|
+
const snapshot = this.store.readProjectionSnapshot();
|
|
754
|
+
if (snapshot) {
|
|
755
|
+
this.state = projectionFromSnapshot(snapshot.projection, snapshot.throughOrdinal);
|
|
756
|
+
}
|
|
757
|
+
else {
|
|
758
|
+
const events = this.store.readAll();
|
|
759
|
+
// Compaction may install a baseline between the first snapshot read and readAll(). In that
|
|
760
|
+
// case readAll() returns only the new tail, which must be applied to that baseline rather
|
|
761
|
+
// than projected as a standalone history.
|
|
762
|
+
const snapshotAfterRead = this.store.readProjectionSnapshot();
|
|
763
|
+
this.state = snapshotAfterRead
|
|
764
|
+
? events
|
|
765
|
+
.filter((event) => event.ordinal > snapshotAfterRead.throughOrdinal)
|
|
766
|
+
.reduce(reduceOrchestrationEvent, projectionFromSnapshot(snapshotAfterRead.projection, snapshotAfterRead.throughOrdinal))
|
|
767
|
+
: projectOrchestrationEvents(events);
|
|
768
|
+
}
|
|
769
|
+
this.refresh();
|
|
770
|
+
}
|
|
771
|
+
getSnapshot() {
|
|
772
|
+
this.refresh();
|
|
773
|
+
return cloneProjection(this.state);
|
|
774
|
+
}
|
|
775
|
+
registerAgent(input) {
|
|
776
|
+
this.refresh();
|
|
777
|
+
const now = this.nowIso();
|
|
778
|
+
let identity;
|
|
779
|
+
try {
|
|
780
|
+
identity = createAgentIdentity(input.agentId ?? `agent-${this.createId()}`, input.resumeContext);
|
|
781
|
+
}
|
|
782
|
+
catch (error) {
|
|
783
|
+
throw new DurableTaskRuntimeError(error instanceof Error ? error.message : String(error));
|
|
784
|
+
}
|
|
785
|
+
const agent = {
|
|
786
|
+
schemaVersion: ORCHESTRATION_SCHEMA_VERSION,
|
|
787
|
+
...identity,
|
|
788
|
+
role: input.role,
|
|
789
|
+
status: "registered",
|
|
790
|
+
createdAt: now,
|
|
791
|
+
updatedAt: now,
|
|
792
|
+
};
|
|
793
|
+
if (this.state.agents[agent.agentId])
|
|
794
|
+
throw new DurableTaskRuntimeError(`Agent '${agent.agentId}' already exists.`);
|
|
795
|
+
this.commit({
|
|
796
|
+
type: "agent.registered",
|
|
797
|
+
aggregateId: agent.agentId,
|
|
798
|
+
actor: "runtime",
|
|
799
|
+
idempotencyKey: `agent-registered:${agent.agentId}`,
|
|
800
|
+
payload: toJsonObject({ agent }),
|
|
801
|
+
});
|
|
802
|
+
return structuredClone(agent);
|
|
803
|
+
}
|
|
804
|
+
createObjective(input) {
|
|
805
|
+
this.refresh();
|
|
806
|
+
assertRiskBudget(input.riskBudget, "objective.riskBudget");
|
|
807
|
+
const now = this.nowIso();
|
|
808
|
+
const objective = {
|
|
809
|
+
schemaVersion: ORCHESTRATION_SCHEMA_VERSION,
|
|
810
|
+
objectiveId: input.objectiveId ?? `objective-${this.createId()}`,
|
|
811
|
+
title: input.title.trim(),
|
|
812
|
+
description: input.description.trim(),
|
|
813
|
+
status: "active",
|
|
814
|
+
constraints: [...(input.constraints ?? [])],
|
|
815
|
+
acceptanceCriteria: structuredClone(input.acceptanceCriteria ?? []),
|
|
816
|
+
riskBudget: { ...(input.riskBudget ?? {}) },
|
|
817
|
+
createdAt: now,
|
|
818
|
+
updatedAt: now,
|
|
819
|
+
};
|
|
820
|
+
if (!objective.title || !objective.description)
|
|
821
|
+
throw new DurableTaskRuntimeError("Objective title and description are required.");
|
|
822
|
+
assertAcceptanceCriteria(objective.acceptanceCriteria);
|
|
823
|
+
if (this.state.objectives[objective.objectiveId]) {
|
|
824
|
+
throw new DurableTaskRuntimeError(`Objective '${objective.objectiveId}' already exists.`);
|
|
825
|
+
}
|
|
826
|
+
this.commit({
|
|
827
|
+
type: "objective.created",
|
|
828
|
+
aggregateId: objective.objectiveId,
|
|
829
|
+
actor: "kernel",
|
|
830
|
+
idempotencyKey: `objective-created:${objective.objectiveId}`,
|
|
831
|
+
payload: toJsonObject({ objective }),
|
|
832
|
+
});
|
|
833
|
+
return structuredClone(objective);
|
|
834
|
+
}
|
|
835
|
+
/** Create or synchronize owner-authored objective metadata without disturbing lifecycle or task state. */
|
|
836
|
+
ensureObjective(input) {
|
|
837
|
+
this.refresh();
|
|
838
|
+
const current = this.state.objectives[input.objectiveId];
|
|
839
|
+
if (!current)
|
|
840
|
+
return this.createObjective(input);
|
|
841
|
+
assertRiskBudget(input.riskBudget, "objective.riskBudget");
|
|
842
|
+
const acceptanceCriteria = structuredClone(input.acceptanceCriteria ?? []);
|
|
843
|
+
assertAcceptanceCriteria(acceptanceCriteria);
|
|
844
|
+
const nextFields = {
|
|
845
|
+
title: input.title.trim(),
|
|
846
|
+
description: input.description.trim(),
|
|
847
|
+
constraints: [...(input.constraints ?? [])],
|
|
848
|
+
acceptanceCriteria,
|
|
849
|
+
riskBudget: { ...(input.riskBudget ?? {}) },
|
|
850
|
+
};
|
|
851
|
+
if (!nextFields.title || !nextFields.description) {
|
|
852
|
+
throw new DurableTaskRuntimeError("Objective title and description are required.");
|
|
853
|
+
}
|
|
854
|
+
const currentFields = {
|
|
855
|
+
title: current.objective.title,
|
|
856
|
+
description: current.objective.description,
|
|
857
|
+
constraints: current.objective.constraints,
|
|
858
|
+
acceptanceCriteria: current.objective.acceptanceCriteria,
|
|
859
|
+
riskBudget: current.objective.riskBudget,
|
|
860
|
+
};
|
|
861
|
+
if (isDeepStrictEqual(currentFields, nextFields))
|
|
862
|
+
return structuredClone(current.objective);
|
|
863
|
+
const retainedCriterionIds = new Set(acceptanceCriteria.map((criterion) => criterion.id));
|
|
864
|
+
const referencedRemovedIds = current.taskIds.flatMap((taskId) => (this.state.tasks[taskId]?.task.acceptanceCriterionIds ?? []).filter((criterionId) => !retainedCriterionIds.has(criterionId)));
|
|
865
|
+
if (referencedRemovedIds.length > 0) {
|
|
866
|
+
throw new DurableTaskRuntimeError(`Cannot remove acceptance criteria referenced by tasks: ${[...new Set(referencedRemovedIds)].join(", ")}.`);
|
|
867
|
+
}
|
|
868
|
+
const objective = {
|
|
869
|
+
...current.objective,
|
|
870
|
+
...nextFields,
|
|
871
|
+
updatedAt: this.nowIso(),
|
|
872
|
+
};
|
|
873
|
+
this.commit({
|
|
874
|
+
type: "objective.updated",
|
|
875
|
+
aggregateId: objective.objectiveId,
|
|
876
|
+
actor: "kernel",
|
|
877
|
+
payload: toJsonObject({ objective }),
|
|
878
|
+
});
|
|
879
|
+
return structuredClone(objective);
|
|
880
|
+
}
|
|
881
|
+
recordObjectiveEvidence(objectiveId, evidence) {
|
|
882
|
+
this.refresh();
|
|
883
|
+
const objective = this.requireObjective(objectiveId);
|
|
884
|
+
const existing = objective.evidence.find((candidate) => candidate.evidenceId === evidence.evidenceId);
|
|
885
|
+
if (existing) {
|
|
886
|
+
if (!isDeepStrictEqual(existing, evidence)) {
|
|
887
|
+
throw new DurableTaskRuntimeError(`Objective evidence '${evidence.evidenceId}' has conflicting content.`);
|
|
888
|
+
}
|
|
889
|
+
return structuredClone(existing);
|
|
890
|
+
}
|
|
891
|
+
if (!evidence.evidenceId.trim() || !evidence.summary.trim() || !evidence.createdAt.trim()) {
|
|
892
|
+
throw new DurableTaskRuntimeError("Objective evidence requires an id, summary, and creation time.");
|
|
893
|
+
}
|
|
894
|
+
if (evidence.criterionId &&
|
|
895
|
+
!objective.objective.acceptanceCriteria.some((criterion) => criterion.id === evidence.criterionId)) {
|
|
896
|
+
throw new DurableTaskRuntimeError(`Objective evidence references unknown acceptance criterion '${evidence.criterionId}'.`);
|
|
897
|
+
}
|
|
898
|
+
this.commit({
|
|
899
|
+
type: "objective.evidence_recorded",
|
|
900
|
+
aggregateId: objectiveId,
|
|
901
|
+
actor: "kernel",
|
|
902
|
+
idempotencyKey: `objective-evidence-recorded:${objectiveId}:${evidence.evidenceId}`,
|
|
903
|
+
payload: toJsonObject({ evidence }),
|
|
904
|
+
});
|
|
905
|
+
return structuredClone(evidence);
|
|
906
|
+
}
|
|
907
|
+
createTask(input) {
|
|
908
|
+
this.refresh();
|
|
909
|
+
assertRiskBudget(input.riskBudget, "task.riskBudget");
|
|
910
|
+
const objectiveState = this.state.objectives[input.objectiveId];
|
|
911
|
+
if (!objectiveState)
|
|
912
|
+
throw new DurableTaskRuntimeError(`Unknown objective '${input.objectiveId}'.`);
|
|
913
|
+
if (objectiveState.objective.status !== "active") {
|
|
914
|
+
throw new DurableTaskRuntimeError(`Objective '${input.objectiveId}' is not active.`);
|
|
915
|
+
}
|
|
916
|
+
const dependsOn = [...new Set(input.dependsOn ?? [])];
|
|
917
|
+
const acceptanceCriterionIds = [...new Set(input.acceptanceCriterionIds ?? [])];
|
|
918
|
+
const objectiveCriterionIds = new Set(objectiveState.objective.acceptanceCriteria.map((criterion) => criterion.id));
|
|
919
|
+
const unknownCriterionIds = acceptanceCriterionIds.filter((criterionId) => !objectiveCriterionIds.has(criterionId));
|
|
920
|
+
if (unknownCriterionIds.length > 0) {
|
|
921
|
+
throw new DurableTaskRuntimeError(`Task references unknown acceptance criteria: ${unknownCriterionIds.join(", ")}.`);
|
|
922
|
+
}
|
|
923
|
+
for (const dependencyId of dependsOn) {
|
|
924
|
+
const dependency = this.state.tasks[dependencyId];
|
|
925
|
+
if (!dependency || dependency.task.objectiveId !== input.objectiveId) {
|
|
926
|
+
throw new DurableTaskRuntimeError(`Task dependency '${dependencyId}' is not in objective '${input.objectiveId}'.`);
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
if (input.verificationOfTaskId) {
|
|
930
|
+
const subject = this.state.tasks[input.verificationOfTaskId];
|
|
931
|
+
if (!subject || subject.task.objectiveId !== input.objectiveId) {
|
|
932
|
+
throw new DurableTaskRuntimeError(`Verification subject '${input.verificationOfTaskId}' is not in objective '${input.objectiveId}'.`);
|
|
933
|
+
}
|
|
934
|
+
if (input.role !== "verifier") {
|
|
935
|
+
throw new DurableTaskRuntimeError("Only verifier tasks may declare verificationOfTaskId.");
|
|
936
|
+
}
|
|
937
|
+
if (subject.task.role === "verifier") {
|
|
938
|
+
throw new DurableTaskRuntimeError("A verifier task cannot independently verify another verifier task.");
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
const now = this.nowIso();
|
|
942
|
+
const task = {
|
|
943
|
+
schemaVersion: ORCHESTRATION_SCHEMA_VERSION,
|
|
944
|
+
taskId: input.taskId ?? `task-${this.createId()}`,
|
|
945
|
+
objectiveId: input.objectiveId,
|
|
946
|
+
title: input.title.trim(),
|
|
947
|
+
description: input.description.trim(),
|
|
948
|
+
role: input.role,
|
|
949
|
+
status: dependsOn.length === 0 ? "ready" : "pending",
|
|
950
|
+
dependsOn,
|
|
951
|
+
requiredCapabilities: [...new Set(input.requiredCapabilities ?? [])],
|
|
952
|
+
acceptanceCriterionIds,
|
|
953
|
+
...(input.verificationOfTaskId ? { verificationOfTaskId: input.verificationOfTaskId } : {}),
|
|
954
|
+
riskBudget: { ...(input.riskBudget ?? {}) },
|
|
955
|
+
createdAt: now,
|
|
956
|
+
updatedAt: now,
|
|
957
|
+
};
|
|
958
|
+
if (!task.title || !task.description)
|
|
959
|
+
throw new DurableTaskRuntimeError("Task title and description are required.");
|
|
960
|
+
if (this.state.tasks[task.taskId])
|
|
961
|
+
throw new DurableTaskRuntimeError(`Task '${task.taskId}' already exists.`);
|
|
962
|
+
this.commit({
|
|
963
|
+
type: "task.created",
|
|
964
|
+
aggregateId: task.objectiveId,
|
|
965
|
+
actor: "runtime",
|
|
966
|
+
idempotencyKey: `task-created:${task.taskId}`,
|
|
967
|
+
payload: toJsonObject({ task }),
|
|
968
|
+
});
|
|
969
|
+
return structuredClone(this.state.tasks[task.taskId].task);
|
|
970
|
+
}
|
|
971
|
+
queueAttempt(taskId, dispatch, grantId) {
|
|
972
|
+
this.refresh();
|
|
973
|
+
const task = this.requireDispatchableTask(taskId);
|
|
974
|
+
const pendingApproval = this.pendingApprovalForTask(taskId);
|
|
975
|
+
if (pendingApproval) {
|
|
976
|
+
throw new DurableTaskRuntimeError(`Task '${taskId}' is awaiting approval '${pendingApproval.request.approvalId}'.`);
|
|
977
|
+
}
|
|
978
|
+
const normalizedDispatch = dispatchFromValue(dispatch, "dispatch");
|
|
979
|
+
if (normalizedDispatch.taskId !== taskId) {
|
|
980
|
+
throw new DurableTaskRuntimeError("Dispatch taskId does not match the queued task.");
|
|
981
|
+
}
|
|
982
|
+
if (normalizedDispatch.executionContract &&
|
|
983
|
+
(normalizedDispatch.executionContract.worker.profile.profileId !== normalizedDispatch.profileId ||
|
|
984
|
+
normalizedDispatch.executionContract.worker.profile.role !== task.task.role)) {
|
|
985
|
+
throw new DurableTaskRuntimeError("Execution contract does not match the dispatch profile and task role.");
|
|
986
|
+
}
|
|
987
|
+
const objective = this.state.objectives[task.task.objectiveId].objective;
|
|
988
|
+
const attemptCeilings = [task.task.riskBudget.maxAttempts, objective.riskBudget.maxAttempts].filter((value) => value !== undefined);
|
|
989
|
+
const maxAttempts = attemptCeilings.length > 0 ? Math.min(...attemptCeilings) : undefined;
|
|
990
|
+
if (maxAttempts !== undefined && task.attemptIds.length >= maxAttempts) {
|
|
991
|
+
throw new DurableTaskRuntimeError(`Task '${taskId}' exhausted its ${maxAttempts} attempt budget.`);
|
|
992
|
+
}
|
|
993
|
+
const attemptId = `attempt-${this.createId()}`;
|
|
994
|
+
this.commit({
|
|
995
|
+
type: "attempt.queued",
|
|
996
|
+
aggregateId: taskId,
|
|
997
|
+
actor: "runtime",
|
|
998
|
+
idempotencyKey: `attempt-queued:${attemptId}`,
|
|
999
|
+
payload: toJsonObject({ attemptId, taskId, dispatch: normalizedDispatch, ...(grantId ? { grantId } : {}) }),
|
|
1000
|
+
});
|
|
1001
|
+
return structuredClone(this.state.attempts[attemptId]);
|
|
1002
|
+
}
|
|
1003
|
+
bindAttemptGrant(attemptId, grant) {
|
|
1004
|
+
this.refresh();
|
|
1005
|
+
const attempt = this.requireAttempt(attemptId);
|
|
1006
|
+
if (!["queued", "leased", "running"].includes(attempt.status)) {
|
|
1007
|
+
throw new DurableTaskRuntimeError(`Attempt '${attemptId}' cannot bind a grant from '${attempt.status}'.`);
|
|
1008
|
+
}
|
|
1009
|
+
if (!grant.grantId.trim())
|
|
1010
|
+
throw new DurableTaskRuntimeError("Grant id is required.");
|
|
1011
|
+
const task = this.state.tasks[attempt.taskId];
|
|
1012
|
+
if (!task ||
|
|
1013
|
+
grant.schemaVersion !== ORCHESTRATION_SCHEMA_VERSION ||
|
|
1014
|
+
grant.attemptId !== attemptId ||
|
|
1015
|
+
grant.taskId !== attempt.taskId ||
|
|
1016
|
+
grant.objectiveId !== task.task.objectiveId ||
|
|
1017
|
+
grant.role !== task.task.role) {
|
|
1018
|
+
throw new DurableTaskRuntimeError("Execution grant target does not match the attempt.");
|
|
1019
|
+
}
|
|
1020
|
+
const approval = this.approvalForAttempt(attemptId);
|
|
1021
|
+
if (approval?.status === "pending") {
|
|
1022
|
+
throw new DurableTaskRuntimeError(`Attempt '${attemptId}' is awaiting approval '${approval.request.approvalId}'.`);
|
|
1023
|
+
}
|
|
1024
|
+
if (approval?.status === "rejected") {
|
|
1025
|
+
throw new DurableTaskRuntimeError(`Approval '${approval.request.approvalId}' was rejected.`);
|
|
1026
|
+
}
|
|
1027
|
+
if (attempt.grantId === grant.grantId) {
|
|
1028
|
+
if (attempt.grant && !isDeepStrictEqual(attempt.grant, grant)) {
|
|
1029
|
+
throw new DurableTaskRuntimeError(`Attempt '${attemptId}' grant has conflicting content.`);
|
|
1030
|
+
}
|
|
1031
|
+
return structuredClone(attempt);
|
|
1032
|
+
}
|
|
1033
|
+
if (attempt.grantId)
|
|
1034
|
+
throw new DurableTaskRuntimeError(`Attempt '${attemptId}' already has a different grant.`);
|
|
1035
|
+
this.commit({
|
|
1036
|
+
type: "attempt.grant_bound",
|
|
1037
|
+
aggregateId: attemptId,
|
|
1038
|
+
actor: "policy",
|
|
1039
|
+
idempotencyKey: `attempt-grant-bound:${attemptId}:${grant.grantId}`,
|
|
1040
|
+
payload: toJsonObject({ attemptId, grant }),
|
|
1041
|
+
});
|
|
1042
|
+
return structuredClone(this.state.attempts[attemptId]);
|
|
1043
|
+
}
|
|
1044
|
+
leaseAttempt(attemptId, ownerId, ttlMs, agentId) {
|
|
1045
|
+
this.refresh();
|
|
1046
|
+
const attempt = this.requireAttempt(attemptId);
|
|
1047
|
+
if (attempt.status !== "queued")
|
|
1048
|
+
throw new DurableTaskRuntimeError(`Attempt '${attemptId}' is not queued.`);
|
|
1049
|
+
this.requireActiveObjectiveForAttempt(attempt);
|
|
1050
|
+
const approval = this.approvalForAttempt(attemptId);
|
|
1051
|
+
if (approval?.status === "pending") {
|
|
1052
|
+
throw new DurableTaskRuntimeError(`Attempt '${attemptId}' is awaiting approval '${approval.request.approvalId}'.`);
|
|
1053
|
+
}
|
|
1054
|
+
if (!attempt.grantId) {
|
|
1055
|
+
throw new DurableTaskRuntimeError(`Attempt '${attemptId}' requires an execution grant before leasing.`);
|
|
1056
|
+
}
|
|
1057
|
+
if (agentId) {
|
|
1058
|
+
const agent = this.state.agents[agentId];
|
|
1059
|
+
if (!agent)
|
|
1060
|
+
throw new DurableTaskRuntimeError(`Unknown agent '${agentId}'.`);
|
|
1061
|
+
if (agent.status !== "registered")
|
|
1062
|
+
throw new DurableTaskRuntimeError(`Agent '${agentId}' is not idle.`);
|
|
1063
|
+
const task = this.state.tasks[attempt.taskId];
|
|
1064
|
+
if (!task || task.task.role !== agent.role)
|
|
1065
|
+
throw new DurableTaskRuntimeError(`Agent '${agentId}' role does not match task.`);
|
|
1066
|
+
}
|
|
1067
|
+
if (!Number.isFinite(ttlMs) || ttlMs <= 0)
|
|
1068
|
+
throw new DurableTaskRuntimeError("Lease TTL must be positive.");
|
|
1069
|
+
const issuedAtMs = this.now();
|
|
1070
|
+
const lease = {
|
|
1071
|
+
leaseId: `lease-${this.createId()}`,
|
|
1072
|
+
attemptId,
|
|
1073
|
+
ownerId,
|
|
1074
|
+
fencingToken: (attempt.lease?.fencingToken ?? 0) + 1,
|
|
1075
|
+
issuedAt: new Date(issuedAtMs).toISOString(),
|
|
1076
|
+
expiresAt: new Date(issuedAtMs + ttlMs).toISOString(),
|
|
1077
|
+
};
|
|
1078
|
+
this.commit({
|
|
1079
|
+
type: "attempt.leased",
|
|
1080
|
+
aggregateId: attemptId,
|
|
1081
|
+
actor: "runtime",
|
|
1082
|
+
idempotencyKey: `attempt-leased:${lease.leaseId}`,
|
|
1083
|
+
payload: toJsonObject({ lease, ...(agentId ? { agentId } : {}) }),
|
|
1084
|
+
});
|
|
1085
|
+
return structuredClone(lease);
|
|
1086
|
+
}
|
|
1087
|
+
startAttempt(attemptId, leaseId, fencingToken) {
|
|
1088
|
+
this.refresh();
|
|
1089
|
+
const attempt = this.requireLiveLease(attemptId, leaseId, fencingToken);
|
|
1090
|
+
this.requireActiveObjectiveForAttempt(attempt);
|
|
1091
|
+
if (attempt.status !== "leased")
|
|
1092
|
+
throw new DurableTaskRuntimeError(`Attempt '${attemptId}' is not leased.`);
|
|
1093
|
+
this.commit({
|
|
1094
|
+
type: "attempt.started",
|
|
1095
|
+
aggregateId: attemptId,
|
|
1096
|
+
actor: "worker",
|
|
1097
|
+
idempotencyKey: `attempt-started:${attemptId}:${fencingToken}`,
|
|
1098
|
+
payload: toJsonObject({ attemptId, leaseId, fencingToken }),
|
|
1099
|
+
});
|
|
1100
|
+
return structuredClone(this.state.attempts[attemptId]);
|
|
1101
|
+
}
|
|
1102
|
+
checkpointAttempt(args) {
|
|
1103
|
+
this.refresh();
|
|
1104
|
+
const attempt = this.requireLiveLease(args.attemptId, args.leaseId, args.fencingToken);
|
|
1105
|
+
if (attempt.status !== "running")
|
|
1106
|
+
throw new DurableTaskRuntimeError(`Attempt '${args.attemptId}' is not running.`);
|
|
1107
|
+
const checkpoint = {
|
|
1108
|
+
checkpointId: `checkpoint-${this.createId()}`,
|
|
1109
|
+
attemptId: args.attemptId,
|
|
1110
|
+
fencingToken: args.fencingToken,
|
|
1111
|
+
summary: args.summary.trim(),
|
|
1112
|
+
artifactIds: [...(args.artifactIds ?? [])],
|
|
1113
|
+
evidenceIds: [...(args.evidenceIds ?? [])],
|
|
1114
|
+
createdAt: this.nowIso(),
|
|
1115
|
+
};
|
|
1116
|
+
if (!checkpoint.summary)
|
|
1117
|
+
throw new DurableTaskRuntimeError("Checkpoint summary is required.");
|
|
1118
|
+
this.commit({
|
|
1119
|
+
type: "attempt.checkpointed",
|
|
1120
|
+
aggregateId: args.attemptId,
|
|
1121
|
+
actor: "worker",
|
|
1122
|
+
idempotencyKey: `attempt-checkpointed:${checkpoint.checkpointId}`,
|
|
1123
|
+
payload: toJsonObject({ checkpoint, leaseId: args.leaseId }),
|
|
1124
|
+
});
|
|
1125
|
+
return structuredClone(checkpoint);
|
|
1126
|
+
}
|
|
1127
|
+
finishAttempt(result) {
|
|
1128
|
+
this.refresh();
|
|
1129
|
+
const attempt = this.requireLiveLease(result.attemptId, result.leaseId, result.fencingToken);
|
|
1130
|
+
if (attempt.status !== "running" && attempt.status !== "leased") {
|
|
1131
|
+
throw new DurableTaskRuntimeError(`Attempt '${result.attemptId}' cannot finish from '${attempt.status}'.`);
|
|
1132
|
+
}
|
|
1133
|
+
if (attempt.taskId !== result.taskId)
|
|
1134
|
+
throw new DurableTaskRuntimeError("Worker result taskId does not match attempt.");
|
|
1135
|
+
const task = this.state.tasks[attempt.taskId];
|
|
1136
|
+
if (!task || task.task.objectiveId !== result.objectiveId) {
|
|
1137
|
+
throw new DurableTaskRuntimeError("Worker result objectiveId does not match attempt.");
|
|
1138
|
+
}
|
|
1139
|
+
if (result.status === "completed") {
|
|
1140
|
+
const missingCriteria = missingTrustedCriteria(result, task.task.acceptanceCriterionIds);
|
|
1141
|
+
if (missingCriteria.length > 0) {
|
|
1142
|
+
throw new DurableTaskRuntimeError(`Completed result lacks trusted evidence for acceptance criteria: ${missingCriteria.join(", ")}.`);
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
this.commit({
|
|
1146
|
+
type: "attempt.finished",
|
|
1147
|
+
aggregateId: result.attemptId,
|
|
1148
|
+
actor: "worker",
|
|
1149
|
+
idempotencyKey: `attempt-finished:${result.resultId}`,
|
|
1150
|
+
payload: toJsonObject({ result }),
|
|
1151
|
+
});
|
|
1152
|
+
return structuredClone(this.state.attempts[result.attemptId]);
|
|
1153
|
+
}
|
|
1154
|
+
finishVerification(args) {
|
|
1155
|
+
this.refresh();
|
|
1156
|
+
const subject = this.state.tasks[args.taskId];
|
|
1157
|
+
const verifierTask = this.state.tasks[args.verifierTaskId];
|
|
1158
|
+
const verifierAttempt = this.state.attempts[args.verifierAttemptId];
|
|
1159
|
+
if (!subject)
|
|
1160
|
+
throw new DurableTaskRuntimeError(`Unknown verification subject '${args.taskId}'.`);
|
|
1161
|
+
if (!verifierTask || verifierTask.task.verificationOfTaskId !== args.taskId) {
|
|
1162
|
+
throw new DurableTaskRuntimeError(`Task '${args.verifierTaskId}' is not the verifier for '${args.taskId}'.`);
|
|
1163
|
+
}
|
|
1164
|
+
if (!verifierAttempt || verifierAttempt.taskId !== args.verifierTaskId) {
|
|
1165
|
+
throw new DurableTaskRuntimeError(`Unknown verifier attempt '${args.verifierAttemptId}'.`);
|
|
1166
|
+
}
|
|
1167
|
+
if (args.verdict === "inconclusive") {
|
|
1168
|
+
if (verifierAttempt.status === "queued" ||
|
|
1169
|
+
verifierAttempt.status === "leased" ||
|
|
1170
|
+
verifierAttempt.status === "running") {
|
|
1171
|
+
throw new DurableTaskRuntimeError(`Verifier attempt '${args.verifierAttemptId}' is not terminal.`);
|
|
1172
|
+
}
|
|
1173
|
+
}
|
|
1174
|
+
else if (verifierAttempt.status !== "completed" || !verifierAttempt.result) {
|
|
1175
|
+
throw new DurableTaskRuntimeError(`Verifier attempt '${args.verifierAttemptId}' is not completed.`);
|
|
1176
|
+
}
|
|
1177
|
+
if (subject.task.status !== "blocked") {
|
|
1178
|
+
throw new DurableTaskRuntimeError(`Verification subject '${args.taskId}' cannot reconcile from '${subject.task.status}'.`);
|
|
1179
|
+
}
|
|
1180
|
+
if (!args.reasonCode.trim())
|
|
1181
|
+
throw new DurableTaskRuntimeError("Verification reason code is required.");
|
|
1182
|
+
if (args.verdict === "accepted" &&
|
|
1183
|
+
!verifierAttempt.result?.evidence.some((evidence) => evidence.trusted && evidence.kind === "review" && evidence.metadata?.subjectTaskId === args.taskId)) {
|
|
1184
|
+
throw new DurableTaskRuntimeError("Accepted verification requires trusted review evidence for the subject task.");
|
|
1185
|
+
}
|
|
1186
|
+
this.commit({
|
|
1187
|
+
type: "task.verification_finished",
|
|
1188
|
+
aggregateId: args.taskId,
|
|
1189
|
+
actor: "runtime",
|
|
1190
|
+
idempotencyKey: `task-verification-finished:${args.taskId}:${args.verifierAttemptId}`,
|
|
1191
|
+
payload: toJsonObject({ ...args, reasonCode: args.reasonCode.trim() }),
|
|
1192
|
+
});
|
|
1193
|
+
return structuredClone(this.state.tasks[args.taskId]);
|
|
1194
|
+
}
|
|
1195
|
+
cancelAttempt(attemptId, reasonCode) {
|
|
1196
|
+
this.refresh();
|
|
1197
|
+
const attempt = this.requireAttempt(attemptId);
|
|
1198
|
+
if (terminalAttemptStatus(attempt.status))
|
|
1199
|
+
return structuredClone(attempt);
|
|
1200
|
+
if (!reasonCode.trim())
|
|
1201
|
+
throw new DurableTaskRuntimeError("Cancellation reason is required.");
|
|
1202
|
+
this.commit({
|
|
1203
|
+
type: "attempt.cancelled",
|
|
1204
|
+
aggregateId: attemptId,
|
|
1205
|
+
actor: "runtime",
|
|
1206
|
+
idempotencyKey: `attempt-cancelled:${attemptId}`,
|
|
1207
|
+
payload: toJsonObject({ attemptId, reasonCode: reasonCode.trim() }),
|
|
1208
|
+
});
|
|
1209
|
+
return structuredClone(this.state.attempts[attemptId]);
|
|
1210
|
+
}
|
|
1211
|
+
failTask(taskId, reasonCode) {
|
|
1212
|
+
this.refresh();
|
|
1213
|
+
const task = this.state.tasks[taskId];
|
|
1214
|
+
if (!task)
|
|
1215
|
+
throw new DurableTaskRuntimeError(`Unknown task '${taskId}'.`);
|
|
1216
|
+
if (["completed", "failed", "cancelled"].includes(task.task.status))
|
|
1217
|
+
return structuredClone(task.task);
|
|
1218
|
+
if (!reasonCode.trim())
|
|
1219
|
+
throw new DurableTaskRuntimeError("Task failure reason is required.");
|
|
1220
|
+
this.commit({
|
|
1221
|
+
type: "task.failed",
|
|
1222
|
+
aggregateId: taskId,
|
|
1223
|
+
actor: "runtime",
|
|
1224
|
+
idempotencyKey: `task-failed:${taskId}:${reasonCode.trim()}`,
|
|
1225
|
+
payload: toJsonObject({ taskId, reasonCode: reasonCode.trim() }),
|
|
1226
|
+
});
|
|
1227
|
+
return structuredClone(this.state.tasks[taskId].task);
|
|
1228
|
+
}
|
|
1229
|
+
/**
|
|
1230
|
+
* Recover unbound in-process work after a process restart. A completion has no resumable model
|
|
1231
|
+
* transcript, so its old lease is fenced and the task becomes dispatchable for a fresh attempt.
|
|
1232
|
+
* Agent-bound attempts are intentionally excluded: those must wake the same logical agent.
|
|
1233
|
+
*/
|
|
1234
|
+
recoverInterruptedUnboundAttempts(shouldRecover = () => true) {
|
|
1235
|
+
this.refresh();
|
|
1236
|
+
const recovered = [];
|
|
1237
|
+
for (const attempt of Object.values(this.state.attempts)) {
|
|
1238
|
+
if ((attempt.status !== "leased" && attempt.status !== "running") ||
|
|
1239
|
+
attempt.agentId ||
|
|
1240
|
+
!shouldRecover(attempt)) {
|
|
1241
|
+
continue;
|
|
1242
|
+
}
|
|
1243
|
+
this.commit({
|
|
1244
|
+
type: "attempt.lease_expired",
|
|
1245
|
+
aggregateId: attempt.attemptId,
|
|
1246
|
+
actor: "runtime",
|
|
1247
|
+
idempotencyKey: `attempt-process-interrupted:${attempt.attemptId}:${attempt.lease?.leaseId ?? "none"}`,
|
|
1248
|
+
payload: toJsonObject({
|
|
1249
|
+
attemptId: attempt.attemptId,
|
|
1250
|
+
...(attempt.lease ? { leaseId: attempt.lease.leaseId, fencingToken: attempt.lease.fencingToken } : {}),
|
|
1251
|
+
reasonCode: "worker_process_interrupted",
|
|
1252
|
+
}),
|
|
1253
|
+
});
|
|
1254
|
+
recovered.push(attempt.attemptId);
|
|
1255
|
+
}
|
|
1256
|
+
return recovered;
|
|
1257
|
+
}
|
|
1258
|
+
expireLeases(at = this.now()) {
|
|
1259
|
+
this.refresh();
|
|
1260
|
+
const expired = [];
|
|
1261
|
+
for (const attempt of Object.values(this.state.attempts)) {
|
|
1262
|
+
if ((attempt.status !== "leased" && attempt.status !== "running") || !attempt.lease)
|
|
1263
|
+
continue;
|
|
1264
|
+
if (Date.parse(attempt.lease.expiresAt) > at)
|
|
1265
|
+
continue;
|
|
1266
|
+
this.commit({
|
|
1267
|
+
type: attempt.agentId ? "attempt.suspended" : "attempt.lease_expired",
|
|
1268
|
+
aggregateId: attempt.attemptId,
|
|
1269
|
+
actor: "runtime",
|
|
1270
|
+
idempotencyKey: `attempt-lease-expired:${attempt.lease.leaseId}`,
|
|
1271
|
+
payload: toJsonObject({
|
|
1272
|
+
attemptId: attempt.attemptId,
|
|
1273
|
+
leaseId: attempt.lease.leaseId,
|
|
1274
|
+
fencingToken: attempt.lease.fencingToken,
|
|
1275
|
+
}),
|
|
1276
|
+
});
|
|
1277
|
+
expired.push(attempt.attemptId);
|
|
1278
|
+
}
|
|
1279
|
+
return expired;
|
|
1280
|
+
}
|
|
1281
|
+
requestAgentResume(agentId) {
|
|
1282
|
+
this.refresh();
|
|
1283
|
+
const agent = this.state.agents[agentId];
|
|
1284
|
+
if (!agent)
|
|
1285
|
+
throw new DurableTaskRuntimeError(`Unknown agent '${agentId}'.`);
|
|
1286
|
+
if (agent.status !== "suspended")
|
|
1287
|
+
throw new DurableTaskRuntimeError(`Agent '${agentId}' is not suspended.`);
|
|
1288
|
+
this.commit({
|
|
1289
|
+
type: "agent.resume_requested",
|
|
1290
|
+
aggregateId: agentId,
|
|
1291
|
+
actor: "runtime",
|
|
1292
|
+
idempotencyKey: `agent-resume-requested:${agentId}:${this.state.lastOrdinal}`,
|
|
1293
|
+
payload: toJsonObject({ agentId }),
|
|
1294
|
+
});
|
|
1295
|
+
return structuredClone(this.state.agents[agentId]);
|
|
1296
|
+
}
|
|
1297
|
+
resumeAttempt(attemptId, agentId, ttlMs) {
|
|
1298
|
+
this.refresh();
|
|
1299
|
+
const attempt = this.requireAttempt(attemptId);
|
|
1300
|
+
const agent = this.state.agents[agentId];
|
|
1301
|
+
if (attempt.status !== "suspended" || attempt.agentId !== agentId) {
|
|
1302
|
+
throw new DurableTaskRuntimeError(`Attempt '${attemptId}' is not suspended for agent '${agentId}'.`);
|
|
1303
|
+
}
|
|
1304
|
+
if (!agent || agent.status !== "resuming")
|
|
1305
|
+
throw new DurableTaskRuntimeError(`Agent '${agentId}' is not resuming.`);
|
|
1306
|
+
this.requireActiveObjectiveForAttempt(attempt);
|
|
1307
|
+
if (!Number.isFinite(ttlMs) || ttlMs <= 0)
|
|
1308
|
+
throw new DurableTaskRuntimeError("Lease TTL must be positive.");
|
|
1309
|
+
const issuedAtMs = this.now();
|
|
1310
|
+
const lease = {
|
|
1311
|
+
leaseId: `lease-${this.createId()}`,
|
|
1312
|
+
attemptId,
|
|
1313
|
+
ownerId: agentId,
|
|
1314
|
+
fencingToken: (attempt.lease?.fencingToken ?? 0) + 1,
|
|
1315
|
+
issuedAt: new Date(issuedAtMs).toISOString(),
|
|
1316
|
+
expiresAt: new Date(issuedAtMs + ttlMs).toISOString(),
|
|
1317
|
+
};
|
|
1318
|
+
this.commit({
|
|
1319
|
+
type: "attempt.resumed",
|
|
1320
|
+
aggregateId: attemptId,
|
|
1321
|
+
actor: "runtime",
|
|
1322
|
+
idempotencyKey: `attempt-resumed:${lease.leaseId}`,
|
|
1323
|
+
payload: toJsonObject({ agentId, lease }),
|
|
1324
|
+
});
|
|
1325
|
+
return structuredClone(lease);
|
|
1326
|
+
}
|
|
1327
|
+
pauseObjective(objectiveId) {
|
|
1328
|
+
this.transitionObjective(objectiveId, "objective.paused", "paused");
|
|
1329
|
+
}
|
|
1330
|
+
resumeObjective(objectiveId) {
|
|
1331
|
+
this.transitionObjective(objectiveId, "objective.resumed", "active");
|
|
1332
|
+
}
|
|
1333
|
+
cancelObjective(objectiveId) {
|
|
1334
|
+
this.transitionObjective(objectiveId, "objective.cancelled", "cancelled");
|
|
1335
|
+
}
|
|
1336
|
+
completeObjective(objectiveId) {
|
|
1337
|
+
this.refresh();
|
|
1338
|
+
const objective = this.requireObjective(objectiveId);
|
|
1339
|
+
const incomplete = objective.taskIds.filter((taskId) => this.state.tasks[taskId]?.task.status !== "completed");
|
|
1340
|
+
if (incomplete.length > 0) {
|
|
1341
|
+
throw new DurableTaskRuntimeError(`Objective '${objectiveId}' has incomplete tasks: ${incomplete.join(", ")}.`);
|
|
1342
|
+
}
|
|
1343
|
+
const requiredCriterionIds = objective.objective.acceptanceCriteria
|
|
1344
|
+
.filter((criterion) => criterion.required)
|
|
1345
|
+
.map((criterion) => criterion.id);
|
|
1346
|
+
const evidence = [
|
|
1347
|
+
...objective.evidence,
|
|
1348
|
+
...objective.taskIds.flatMap((taskId) => {
|
|
1349
|
+
const task = this.state.tasks[taskId];
|
|
1350
|
+
return (task?.attemptIds.flatMap((attemptId) => this.state.attempts[attemptId]?.result?.evidence ?? []) ?? []);
|
|
1351
|
+
}),
|
|
1352
|
+
];
|
|
1353
|
+
const provenCriterionIds = new Set(evidence.flatMap((item) => (item.trusted && item.criterionId ? [item.criterionId] : [])));
|
|
1354
|
+
const unproven = requiredCriterionIds.filter((criterionId) => !provenCriterionIds.has(criterionId));
|
|
1355
|
+
if (unproven.length > 0) {
|
|
1356
|
+
throw new DurableTaskRuntimeError(`Objective '${objectiveId}' lacks trusted evidence for required criteria: ${unproven.join(", ")}.`);
|
|
1357
|
+
}
|
|
1358
|
+
this.transitionObjective(objectiveId, "objective.completed", "completed");
|
|
1359
|
+
}
|
|
1360
|
+
completeObjectiveFromOwner(objectiveId, acceptanceOverride) {
|
|
1361
|
+
this.refresh();
|
|
1362
|
+
const objective = this.requireObjective(objectiveId);
|
|
1363
|
+
if (objective.objective.status === "completed")
|
|
1364
|
+
return;
|
|
1365
|
+
if (objective.objective.status === "cancelled") {
|
|
1366
|
+
throw new DurableTaskRuntimeError(`Objective '${objectiveId}' is terminal.`);
|
|
1367
|
+
}
|
|
1368
|
+
if (!acceptanceOverride) {
|
|
1369
|
+
const provenCriterionIds = new Set(objective.evidence.flatMap((evidence) => evidence.trusted && evidence.criterionId ? [evidence.criterionId] : []));
|
|
1370
|
+
const unproven = objective.objective.acceptanceCriteria
|
|
1371
|
+
.filter((criterion) => criterion.required && !provenCriterionIds.has(criterion.id))
|
|
1372
|
+
.map((criterion) => criterion.id);
|
|
1373
|
+
if (unproven.length > 0) {
|
|
1374
|
+
throw new DurableTaskRuntimeError(`Objective '${objectiveId}' lacks trusted owner evidence for required criteria: ${unproven.join(", ")}.`);
|
|
1375
|
+
}
|
|
1376
|
+
}
|
|
1377
|
+
this.transitionObjective(objectiveId, "objective.completed", "completed");
|
|
1378
|
+
}
|
|
1379
|
+
requestApproval(approval) {
|
|
1380
|
+
this.refresh();
|
|
1381
|
+
const normalized = approvalFromPayload(toJsonObject({ approval }));
|
|
1382
|
+
const existing = this.state.approvals[normalized.approvalId];
|
|
1383
|
+
if (existing) {
|
|
1384
|
+
if (!isDeepStrictEqual(existing.request, normalized)) {
|
|
1385
|
+
throw new DurableTaskRuntimeError(`Approval id '${normalized.approvalId}' has conflicting content.`);
|
|
1386
|
+
}
|
|
1387
|
+
return structuredClone(existing);
|
|
1388
|
+
}
|
|
1389
|
+
this.validateApprovalRequest(normalized);
|
|
1390
|
+
const pendingForTarget = Object.values(this.state.approvals).find((candidate) => candidate.status === "pending" &&
|
|
1391
|
+
candidate.request.objectiveId === normalized.objectiveId &&
|
|
1392
|
+
candidate.request.taskId === normalized.taskId &&
|
|
1393
|
+
candidate.request.attemptId === normalized.attemptId);
|
|
1394
|
+
if (pendingForTarget) {
|
|
1395
|
+
throw new DurableTaskRuntimeError(`Target already awaits approval '${pendingForTarget.request.approvalId}'.`);
|
|
1396
|
+
}
|
|
1397
|
+
this.commit({
|
|
1398
|
+
type: "approval.requested",
|
|
1399
|
+
aggregateId: normalized.attemptId ?? normalized.taskId ?? normalized.objectiveId,
|
|
1400
|
+
actor: "policy",
|
|
1401
|
+
idempotencyKey: `approval-requested:${normalized.approvalId}`,
|
|
1402
|
+
payload: toJsonObject({ approval: normalized }),
|
|
1403
|
+
});
|
|
1404
|
+
return structuredClone(this.state.approvals[normalized.approvalId]);
|
|
1405
|
+
}
|
|
1406
|
+
resolveApproval(approvalId, outcome, reasonCode) {
|
|
1407
|
+
this.refresh();
|
|
1408
|
+
const approval = this.state.approvals[approvalId];
|
|
1409
|
+
if (!approval)
|
|
1410
|
+
throw new DurableTaskRuntimeError(`Unknown approval '${approvalId}'.`);
|
|
1411
|
+
if (approval.status !== "pending") {
|
|
1412
|
+
if (approval.status === outcome)
|
|
1413
|
+
return structuredClone(approval);
|
|
1414
|
+
throw new DurableTaskRuntimeError(`Approval '${approvalId}' was already ${approval.status}.`);
|
|
1415
|
+
}
|
|
1416
|
+
if (outcome !== "approved" && outcome !== "rejected") {
|
|
1417
|
+
throw new DurableTaskRuntimeError(`Unknown approval outcome '${String(outcome)}'.`);
|
|
1418
|
+
}
|
|
1419
|
+
if (!reasonCode.trim())
|
|
1420
|
+
throw new DurableTaskRuntimeError("Approval resolution reason is required.");
|
|
1421
|
+
const resolution = {
|
|
1422
|
+
schemaVersion: ORCHESTRATION_SCHEMA_VERSION,
|
|
1423
|
+
approvalId,
|
|
1424
|
+
outcome,
|
|
1425
|
+
reasonCode: reasonCode.trim(),
|
|
1426
|
+
resolvedAt: this.nowIso(),
|
|
1427
|
+
};
|
|
1428
|
+
this.commit({
|
|
1429
|
+
type: "approval.resolved",
|
|
1430
|
+
aggregateId: approval.request.attemptId ?? approval.request.taskId ?? approval.request.objectiveId,
|
|
1431
|
+
actor: "human",
|
|
1432
|
+
idempotencyKey: `approval-resolved:${approvalId}`,
|
|
1433
|
+
payload: toJsonObject({ resolution }),
|
|
1434
|
+
});
|
|
1435
|
+
return structuredClone(this.state.approvals[approvalId]);
|
|
1436
|
+
}
|
|
1437
|
+
enqueueNotification(args) {
|
|
1438
|
+
this.refresh();
|
|
1439
|
+
this.requireObjective(args.objectiveId);
|
|
1440
|
+
const notificationId = args.notificationId ?? `notification-${this.createId()}`;
|
|
1441
|
+
const existing = this.state.notifications[notificationId];
|
|
1442
|
+
if (existing)
|
|
1443
|
+
return structuredClone(existing);
|
|
1444
|
+
this.commit({
|
|
1445
|
+
type: "notification.enqueued",
|
|
1446
|
+
aggregateId: args.objectiveId,
|
|
1447
|
+
actor: "runtime",
|
|
1448
|
+
idempotencyKey: `notification-enqueued:${notificationId}`,
|
|
1449
|
+
payload: toJsonObject({ ...args, notificationId }),
|
|
1450
|
+
});
|
|
1451
|
+
return structuredClone(this.state.notifications[notificationId]);
|
|
1452
|
+
}
|
|
1453
|
+
markNotificationDelivered(notificationId) {
|
|
1454
|
+
this.refresh();
|
|
1455
|
+
const notification = this.state.notifications[notificationId];
|
|
1456
|
+
if (!notification)
|
|
1457
|
+
throw new DurableTaskRuntimeError(`Unknown notification '${notificationId}'.`);
|
|
1458
|
+
if (notification.status === "delivered")
|
|
1459
|
+
return structuredClone(notification);
|
|
1460
|
+
this.commit({
|
|
1461
|
+
type: "notification.delivered",
|
|
1462
|
+
aggregateId: notification.objectiveId,
|
|
1463
|
+
actor: "runtime",
|
|
1464
|
+
idempotencyKey: `notification-delivered:${notificationId}`,
|
|
1465
|
+
payload: toJsonObject({ notificationId }),
|
|
1466
|
+
});
|
|
1467
|
+
return structuredClone(this.state.notifications[notificationId]);
|
|
1468
|
+
}
|
|
1469
|
+
transitionObjective(objectiveId, type, target) {
|
|
1470
|
+
this.refresh();
|
|
1471
|
+
const objective = this.requireObjective(objectiveId);
|
|
1472
|
+
if (objective.objective.status === target)
|
|
1473
|
+
return;
|
|
1474
|
+
if (["completed", "cancelled"].includes(objective.objective.status)) {
|
|
1475
|
+
throw new DurableTaskRuntimeError(`Objective '${objectiveId}' is terminal.`);
|
|
1476
|
+
}
|
|
1477
|
+
this.commit({
|
|
1478
|
+
type,
|
|
1479
|
+
aggregateId: objectiveId,
|
|
1480
|
+
actor: "human",
|
|
1481
|
+
idempotencyKey: `${type}:${objectiveId}:${this.state.lastOrdinal}`,
|
|
1482
|
+
payload: {},
|
|
1483
|
+
});
|
|
1484
|
+
}
|
|
1485
|
+
requireObjective(objectiveId) {
|
|
1486
|
+
const objective = this.state.objectives[objectiveId];
|
|
1487
|
+
if (!objective)
|
|
1488
|
+
throw new DurableTaskRuntimeError(`Unknown objective '${objectiveId}'.`);
|
|
1489
|
+
return objective;
|
|
1490
|
+
}
|
|
1491
|
+
requireDispatchableTask(taskId) {
|
|
1492
|
+
const task = this.state.tasks[taskId];
|
|
1493
|
+
if (!task)
|
|
1494
|
+
throw new DurableTaskRuntimeError(`Unknown task '${taskId}'.`);
|
|
1495
|
+
const objective = this.requireObjective(task.task.objectiveId);
|
|
1496
|
+
if (objective.objective.status !== "active") {
|
|
1497
|
+
throw new DurableTaskRuntimeError(`Objective '${objective.objective.objectiveId}' is not active.`);
|
|
1498
|
+
}
|
|
1499
|
+
if (!["ready", "blocked", "failed"].includes(task.task.status)) {
|
|
1500
|
+
throw new DurableTaskRuntimeError(`Task '${taskId}' is not dispatchable from '${task.task.status}'.`);
|
|
1501
|
+
}
|
|
1502
|
+
return task;
|
|
1503
|
+
}
|
|
1504
|
+
requireAttempt(attemptId) {
|
|
1505
|
+
const attempt = this.state.attempts[attemptId];
|
|
1506
|
+
if (!attempt)
|
|
1507
|
+
throw new DurableTaskRuntimeError(`Unknown attempt '${attemptId}'.`);
|
|
1508
|
+
return attempt;
|
|
1509
|
+
}
|
|
1510
|
+
approvalForAttempt(attemptId) {
|
|
1511
|
+
return Object.values(this.state.approvals).find((approval) => approval.request.attemptId === attemptId);
|
|
1512
|
+
}
|
|
1513
|
+
pendingApprovalForTask(taskId) {
|
|
1514
|
+
const task = this.state.tasks[taskId];
|
|
1515
|
+
if (!task)
|
|
1516
|
+
return undefined;
|
|
1517
|
+
return Object.values(this.state.approvals).find((approval) => approval.status === "pending" &&
|
|
1518
|
+
approval.request.attemptId === undefined &&
|
|
1519
|
+
approval.request.objectiveId === task.task.objectiveId &&
|
|
1520
|
+
(approval.request.taskId === undefined || approval.request.taskId === taskId));
|
|
1521
|
+
}
|
|
1522
|
+
requireActiveObjectiveForAttempt(attempt) {
|
|
1523
|
+
const task = this.state.tasks[attempt.taskId];
|
|
1524
|
+
if (!task)
|
|
1525
|
+
throw new DurableTaskRuntimeError(`Unknown task '${attempt.taskId}'.`);
|
|
1526
|
+
const objective = this.requireObjective(task.task.objectiveId).objective;
|
|
1527
|
+
if (objective.status !== "active") {
|
|
1528
|
+
throw new DurableTaskRuntimeError(`Objective '${objective.objectiveId}' is not active.`);
|
|
1529
|
+
}
|
|
1530
|
+
}
|
|
1531
|
+
validateApprovalRequest(approval) {
|
|
1532
|
+
const objective = this.requireObjective(approval.objectiveId).objective;
|
|
1533
|
+
if (objective.status !== "active") {
|
|
1534
|
+
throw new DurableTaskRuntimeError(`Objective '${approval.objectiveId}' is not active.`);
|
|
1535
|
+
}
|
|
1536
|
+
if (approval.attemptId && !approval.taskId) {
|
|
1537
|
+
throw new DurableTaskRuntimeError("Attempt-scoped approval requires a taskId.");
|
|
1538
|
+
}
|
|
1539
|
+
if (approval.taskId) {
|
|
1540
|
+
const task = this.state.tasks[approval.taskId];
|
|
1541
|
+
if (!task || task.task.objectiveId !== approval.objectiveId) {
|
|
1542
|
+
throw new DurableTaskRuntimeError(`Approval task '${approval.taskId}' does not belong to its objective.`);
|
|
1543
|
+
}
|
|
1544
|
+
}
|
|
1545
|
+
if (approval.attemptId) {
|
|
1546
|
+
const attempt = this.state.attempts[approval.attemptId];
|
|
1547
|
+
if (!attempt || attempt.taskId !== approval.taskId) {
|
|
1548
|
+
throw new DurableTaskRuntimeError(`Approval attempt '${approval.attemptId}' does not belong to its task.`);
|
|
1549
|
+
}
|
|
1550
|
+
if (attempt.status !== "queued" || attempt.grantId) {
|
|
1551
|
+
throw new DurableTaskRuntimeError(`Approval attempt '${approval.attemptId}' is not awaiting policy.`);
|
|
1552
|
+
}
|
|
1553
|
+
}
|
|
1554
|
+
}
|
|
1555
|
+
requireLiveLease(attemptId, leaseId, fencingToken) {
|
|
1556
|
+
const attempt = this.requireAttempt(attemptId);
|
|
1557
|
+
if (!attempt.lease || attempt.lease.leaseId !== leaseId || attempt.lease.fencingToken !== fencingToken) {
|
|
1558
|
+
throw new DurableTaskRuntimeError(`Attempt '${attemptId}' lease or fencing token is stale.`);
|
|
1559
|
+
}
|
|
1560
|
+
if (Date.parse(attempt.lease.expiresAt) <= this.now()) {
|
|
1561
|
+
throw new DurableTaskRuntimeError(`Attempt '${attemptId}' lease expired.`);
|
|
1562
|
+
}
|
|
1563
|
+
return attempt;
|
|
1564
|
+
}
|
|
1565
|
+
refresh() {
|
|
1566
|
+
let events;
|
|
1567
|
+
try {
|
|
1568
|
+
events = this.store.readAfter(this.state.lastOrdinal);
|
|
1569
|
+
}
|
|
1570
|
+
catch (error) {
|
|
1571
|
+
if (!(error instanceof OrchestrationSnapshotRequiredError))
|
|
1572
|
+
throw error;
|
|
1573
|
+
const snapshot = this.store.readProjectionSnapshot();
|
|
1574
|
+
if (!snapshot || snapshot.throughOrdinal < error.throughOrdinal) {
|
|
1575
|
+
throw new DurableTaskRuntimeError("Required orchestration projection snapshot is unavailable.");
|
|
1576
|
+
}
|
|
1577
|
+
this.state = projectionFromSnapshot(snapshot.projection, snapshot.throughOrdinal);
|
|
1578
|
+
events = this.store.readAfter(this.state.lastOrdinal);
|
|
1579
|
+
}
|
|
1580
|
+
for (const event of events) {
|
|
1581
|
+
this.state = reduceOrchestrationEvent(this.state, event);
|
|
1582
|
+
}
|
|
1583
|
+
this.compactCurrentProjection();
|
|
1584
|
+
}
|
|
1585
|
+
commit(input) {
|
|
1586
|
+
this.compactCurrentProjection();
|
|
1587
|
+
const event = this.store.append(input, { expectedLastOrdinal: this.state.lastOrdinal });
|
|
1588
|
+
if (event.ordinal > this.state.lastOrdinal)
|
|
1589
|
+
this.state = reduceOrchestrationEvent(this.state, event);
|
|
1590
|
+
}
|
|
1591
|
+
compactCurrentProjection() {
|
|
1592
|
+
if (this.state.lastOrdinal > 0) {
|
|
1593
|
+
this.store.compactIfNeeded(this.state.lastOrdinal, () => toJsonObject(this.state));
|
|
1594
|
+
}
|
|
1595
|
+
}
|
|
1596
|
+
nowIso() {
|
|
1597
|
+
return new Date(this.now()).toISOString();
|
|
1598
|
+
}
|
|
1599
|
+
}
|
|
1600
|
+
//# sourceMappingURL=task-runtime.js.map
|