@vercel/factory 0.0.13 → 0.0.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +307 -1
- package/dist/agent-routes.d.mts +69 -0
- package/dist/agent-routes.mjs +64 -0
- package/dist/agent-routes.mjs.map +1 -0
- package/dist/api-contracts.d.mts +1749 -0
- package/dist/api-contracts.mjs +118 -0
- package/dist/api-contracts.mjs.map +1 -0
- package/dist/api-images.d.mts +30 -0
- package/dist/api-images.mjs +28 -0
- package/dist/api-images.mjs.map +1 -0
- package/dist/api-task-graph.mjs +57 -0
- package/dist/api-task-graph.mjs.map +1 -0
- package/dist/api.d.mts +77 -0
- package/dist/api.mjs +1087 -0
- package/dist/api.mjs.map +1 -0
- package/dist/approval-access.d.mts +39 -0
- package/dist/approval-access.mjs +62 -0
- package/dist/approval-access.mjs.map +1 -0
- package/dist/approval-contracts.d.mts +692 -0
- package/dist/approval-contracts.mjs +27 -0
- package/dist/approval-contracts.mjs.map +1 -0
- package/dist/approval.d.mts +49 -0
- package/dist/approval.mjs +39 -0
- package/dist/approval.mjs.map +1 -0
- package/dist/blob/artifacts.d.mts +37 -0
- package/dist/blob/artifacts.mjs +45 -0
- package/dist/blob/artifacts.mjs.map +1 -0
- package/dist/blob/index.d.mts +44 -8
- package/dist/blob/index.mjs +159 -35
- package/dist/blob/index.mjs.map +1 -1
- package/dist/budget.d.mts +108 -0
- package/dist/budget.mjs +141 -0
- package/dist/budget.mjs.map +1 -0
- package/dist/budgets.d.mts +4 -0
- package/dist/budgets.mjs +4 -0
- package/dist/build-factory.d.mts +6 -4
- package/dist/build-factory.mjs +2 -1
- package/dist/build-factory.mjs.map +1 -1
- package/dist/change-verification/brief.d.mts +9 -0
- package/dist/change-verification/brief.mjs +13 -0
- package/dist/change-verification/brief.mjs.map +1 -0
- package/dist/change-verification/dispatch.d.mts +24 -0
- package/dist/change-verification/dispatch.mjs +45 -0
- package/dist/change-verification/dispatch.mjs.map +1 -0
- package/dist/change-verification/eve-tool.d.mts +60 -0
- package/dist/change-verification/eve-tool.mjs +101 -0
- package/dist/change-verification/eve-tool.mjs.map +1 -0
- package/dist/changes/eve-record-change.d.mts +56 -0
- package/dist/changes/eve-record-change.mjs +97 -0
- package/dist/changes/eve-record-change.mjs.map +1 -0
- package/dist/changes/presentation.d.mts +14 -0
- package/dist/changes.d.mts +23 -0
- package/dist/changes.mjs +29 -0
- package/dist/changes.mjs.map +1 -0
- package/dist/client-events.d.mts +18 -0
- package/dist/client-events.mjs +41 -0
- package/dist/client-events.mjs.map +1 -0
- package/dist/client-stream.d.mts +28 -0
- package/dist/client-stream.mjs +204 -0
- package/dist/client-stream.mjs.map +1 -0
- package/dist/client-transcript.d.mts +41 -0
- package/dist/client-transcript.mjs +229 -0
- package/dist/client-transcript.mjs.map +1 -0
- package/dist/client.d.mts +234 -0
- package/dist/client.mjs +258 -0
- package/dist/client.mjs.map +1 -0
- package/dist/code-review/contracts.d.mts +26 -0
- package/dist/code-review/eve-post-review.d.mts +70 -0
- package/dist/code-review/eve-post-review.mjs +183 -0
- package/dist/code-review/eve-post-review.mjs.map +1 -0
- package/dist/code-review/eve-review-comments.d.mts +64 -0
- package/dist/code-review/eve-review-comments.mjs +159 -0
- package/dist/code-review/eve-review-comments.mjs.map +1 -0
- package/dist/code-review/github-contracts.d.mts +10 -0
- package/dist/code-review/github-reporter.d.mts +44 -0
- package/dist/code-review/github-reporter.mjs +260 -0
- package/dist/code-review/github-reporter.mjs.map +1 -0
- package/dist/code-review/preparation.d.mts +34 -0
- package/dist/code-review/preparation.mjs +23 -0
- package/dist/code-review/preparation.mjs.map +1 -0
- package/dist/code-review/stall.d.mts +17 -0
- package/dist/code-review/stall.mjs +30 -0
- package/dist/code-review/stall.mjs.map +1 -0
- package/dist/code-review.d.mts +6 -0
- package/dist/code-review.mjs +5 -0
- package/dist/communication.d.mts +9 -0
- package/dist/communication.mjs +9 -0
- package/dist/connectors/github.d.mts +26 -0
- package/dist/connectors/github.mjs +55 -0
- package/dist/connectors/github.mjs.map +1 -0
- package/dist/connectors/index.d.mts +35 -2
- package/dist/connectors/index.mjs +4 -26
- package/dist/connectors/index.mjs.map +1 -1
- package/dist/connectors/outbox.d.mts +34 -0
- package/dist/connectors/outbox.mjs +50 -0
- package/dist/connectors/outbox.mjs.map +1 -0
- package/dist/connectors/slack-approval-blocks.mjs +136 -0
- package/dist/connectors/slack-approval-blocks.mjs.map +1 -0
- package/dist/connectors/slack-approvals.d.mts +29 -0
- package/dist/connectors/slack-approvals.mjs +346 -0
- package/dist/connectors/slack-approvals.mjs.map +1 -0
- package/dist/connectors/slack.d.mts +35 -41
- package/dist/connectors/slack.mjs +13 -69
- package/dist/connectors/slack.mjs.map +1 -1
- package/dist/deepsec/eve-tool.d.mts +110 -0
- package/dist/deepsec/eve-tool.mjs +164 -0
- package/dist/deepsec/eve-tool.mjs.map +1 -0
- package/dist/deepsec/finding.d.mts +84 -0
- package/dist/deepsec/finding.mjs +56 -0
- package/dist/deepsec/finding.mjs.map +1 -0
- package/dist/deepsec.d.mts +3 -0
- package/dist/delivery-metrics/metrics.d.mts +93 -0
- package/dist/delivery-metrics/metrics.mjs +200 -0
- package/dist/delivery-metrics/metrics.mjs.map +1 -0
- package/dist/delivery-metrics.d.mts +2 -0
- package/dist/delivery-metrics.mjs +2 -0
- package/dist/dispatch.d.mts +81 -0
- package/dist/dispatch.mjs +140 -0
- package/dist/dispatch.mjs.map +1 -0
- package/dist/errors.d.mts +15 -0
- package/dist/errors.mjs +38 -0
- package/dist/errors.mjs.map +1 -0
- package/dist/eve/index.d.mts +143 -1561
- package/dist/eve/index.mjs +244 -1063
- package/dist/eve/index.mjs.map +1 -1
- package/dist/eve/invoke.d.mts +144 -0
- package/dist/eve/invoke.mjs +241 -0
- package/dist/eve/invoke.mjs.map +1 -0
- package/dist/eve/session-client.d.mts +68 -0
- package/dist/eve/session-client.mjs +131 -0
- package/dist/eve/session-client.mjs.map +1 -0
- package/dist/eve/task-execution.mjs +18 -0
- package/dist/eve/task-execution.mjs.map +1 -0
- package/dist/eve/task-session.d.mts +87 -0
- package/dist/eve/task-session.mjs +203 -0
- package/dist/eve/task-session.mjs.map +1 -0
- package/dist/eve/transcript.d.mts +37 -0
- package/dist/eve/transcript.mjs +65 -0
- package/dist/eve/transcript.mjs.map +1 -0
- package/dist/execution-public.d.mts +8 -0
- package/dist/execution-public.mjs +7 -0
- package/dist/execution.d.mts +75 -0
- package/dist/execution.mjs +153 -0
- package/dist/execution.mjs.map +1 -0
- package/dist/finding-remediation/admission.d.mts +51 -0
- package/dist/finding-remediation/admission.mjs +87 -0
- package/dist/finding-remediation/admission.mjs.map +1 -0
- package/dist/finding-remediation/brief.d.mts +14 -0
- package/dist/finding-remediation/brief.mjs +25 -0
- package/dist/finding-remediation/brief.mjs.map +1 -0
- package/dist/finding-remediation/eve-tool.d.mts +29 -0
- package/dist/finding-remediation/eve-tool.mjs +34 -0
- package/dist/finding-remediation/eve-tool.mjs.map +1 -0
- package/dist/findings/fingerprint.d.mts +14 -0
- package/dist/findings/fingerprint.mjs +28 -0
- package/dist/findings/fingerprint.mjs.map +1 -0
- package/dist/findings.d.mts +6 -0
- package/dist/findings.mjs +6 -0
- package/dist/github-app.d.mts +54 -0
- package/dist/github-app.mjs +121 -0
- package/dist/github-app.mjs.map +1 -0
- package/dist/github-publication.d.mts +198 -0
- package/dist/github-publication.mjs +429 -0
- package/dist/github-publication.mjs.map +1 -0
- package/dist/github-transfer.d.mts +72 -0
- package/dist/github-transfer.mjs +125 -0
- package/dist/github-transfer.mjs.map +1 -0
- package/dist/github.d.mts +406 -2
- package/dist/github.mjs +250 -37
- package/dist/github.mjs.map +1 -1
- package/dist/image-input.d.mts +51 -0
- package/dist/image-input.mjs +63 -0
- package/dist/image-input.mjs.map +1 -0
- package/dist/index.d.mts +14 -314
- package/dist/index.mjs +14 -1124
- package/dist/intake-contracts.d.mts +56 -0
- package/dist/intake-contracts.mjs +33 -0
- package/dist/intake-contracts.mjs.map +1 -0
- package/dist/integrations/deepsec.d.mts +4 -0
- package/dist/integrations/deepsec.mjs +3 -0
- package/dist/integrations/github.d.mts +11 -0
- package/dist/integrations/github.mjs +9 -0
- package/dist/integrations/slack.d.mts +3 -0
- package/dist/integrations/slack.mjs +3 -0
- package/dist/integrations/vercel.d.mts +3 -0
- package/dist/integrations/vercel.mjs +3 -0
- package/dist/merge-resolution/contracts.d.mts +17 -0
- package/dist/merge-resolution/contracts.mjs +19 -0
- package/dist/merge-resolution/contracts.mjs.map +1 -0
- package/dist/merge-resolution/eve-tools.d.mts +46 -0
- package/dist/merge-resolution/eve-tools.mjs +117 -0
- package/dist/merge-resolution/eve-tools.mjs.map +1 -0
- package/dist/multi-repository-changes/coordination.d.mts +49 -0
- package/dist/multi-repository-changes/coordination.mjs +115 -0
- package/dist/multi-repository-changes/coordination.mjs.map +1 -0
- package/dist/planning/cancellation.mjs +82 -0
- package/dist/planning/cancellation.mjs.map +1 -0
- package/dist/planning/configuration.d.mts +23 -0
- package/dist/planning/configuration.mjs +56 -0
- package/dist/planning/configuration.mjs.map +1 -0
- package/dist/planning/contracts.d.mts +136 -0
- package/dist/planning/contracts.mjs +42 -0
- package/dist/planning/contracts.mjs.map +1 -0
- package/dist/planning/dependencies.mjs +44 -0
- package/dist/planning/dependencies.mjs.map +1 -0
- package/dist/planning/effects.mjs +122 -0
- package/dist/planning/effects.mjs.map +1 -0
- package/dist/planning/reconcile.mjs +273 -0
- package/dist/planning/reconcile.mjs.map +1 -0
- package/dist/planning/runtime.d.mts +27 -0
- package/dist/planning/runtime.mjs +25 -0
- package/dist/planning/runtime.mjs.map +1 -0
- package/dist/planning/state.mjs +92 -0
- package/dist/planning/state.mjs.map +1 -0
- package/dist/planning/turns.mjs +262 -0
- package/dist/planning/turns.mjs.map +1 -0
- package/dist/planning/validation.mjs +106 -0
- package/dist/planning/validation.mjs.map +1 -0
- package/dist/postgres/index.d.mts +7 -1
- package/dist/postgres/index.mjs +7 -3
- package/dist/postgres/index.mjs.map +1 -1
- package/dist/presets/software-development/approval.d.mts +14 -0
- package/dist/presets/software-development/approval.mjs +21 -0
- package/dist/presets/software-development/approval.mjs.map +1 -0
- package/dist/presets/software-development/deepsec-brief.d.mts +8 -0
- package/dist/presets/software-development/deepsec-brief.mjs +14 -0
- package/dist/presets/software-development/deepsec-brief.mjs.map +1 -0
- package/dist/presets/software-development/dispatch.d.mts +53 -0
- package/dist/presets/software-development/dispatch.mjs +66 -0
- package/dist/presets/software-development/dispatch.mjs.map +1 -0
- package/dist/presets/software-development/eve-delegation-policy.d.mts +7 -0
- package/dist/presets/software-development/eve-delegation-policy.mjs +34 -0
- package/dist/presets/software-development/eve-delegation-policy.mjs.map +1 -0
- package/dist/presets/software-development/eve.d.mts +18 -0
- package/dist/presets/software-development/eve.mjs +19 -0
- package/dist/presets/software-development/eve.mjs.map +1 -0
- package/dist/presets/software-development/finish-task-policy.d.mts +7 -0
- package/dist/presets/software-development/finish-task-policy.mjs +9 -0
- package/dist/presets/software-development/finish-task-policy.mjs.map +1 -0
- package/dist/presets/software-development/recovery.d.mts +32 -0
- package/dist/presets/software-development/recovery.mjs +120 -0
- package/dist/presets/software-development/recovery.mjs.map +1 -0
- package/dist/presets/software-development/task-communication.d.mts +23 -0
- package/dist/presets/software-development/task-communication.mjs +120 -0
- package/dist/presets/software-development/task-communication.mjs.map +1 -0
- package/dist/presets/software-development.d.mts +9 -0
- package/dist/presets/software-development.mjs +9 -0
- package/dist/pull-requests/contracts.d.mts +72 -0
- package/dist/pull-requests/eve-fetch.d.mts +57 -0
- package/dist/pull-requests/eve-fetch.mjs +151 -0
- package/dist/pull-requests/eve-fetch.mjs.map +1 -0
- package/dist/pull-requests/github-label-manager.d.mts +27 -0
- package/dist/pull-requests/github-label-manager.mjs +40 -0
- package/dist/pull-requests/github-label-manager.mjs.map +1 -0
- package/dist/pull-requests/github-publisher.d.mts +53 -0
- package/dist/pull-requests/github-publisher.mjs +345 -0
- package/dist/pull-requests/github-publisher.mjs.map +1 -0
- package/dist/pull-requests.d.mts +5 -0
- package/dist/pull-requests.mjs +4 -0
- package/dist/receipts.d.mts +4 -0
- package/dist/receipts.mjs +3 -0
- package/dist/repositories.d.mts +18 -0
- package/dist/repositories.mjs +33 -0
- package/dist/repositories.mjs.map +1 -0
- package/dist/sandbox/command.d.mts +18 -0
- package/dist/sandbox/execution-abort.d.mts +16 -0
- package/dist/sandbox/execution-abort.mjs +49 -0
- package/dist/sandbox/execution-abort.mjs.map +1 -0
- package/dist/sandbox/execution-environment.d.mts +25 -0
- package/dist/sandbox/execution-run-ledger.d.mts +115 -0
- package/dist/sandbox/execution-run-ledger.mjs +209 -0
- package/dist/sandbox/execution-run-ledger.mjs.map +1 -0
- package/dist/sandbox/execution.d.mts +54 -0
- package/dist/sandbox/execution.mjs +228 -0
- package/dist/sandbox/execution.mjs.map +1 -0
- package/dist/sandbox/harness-runner.d.mts +79 -0
- package/dist/sandbox/harness-runner.mjs +162 -0
- package/dist/sandbox/harness-runner.mjs.map +1 -0
- package/dist/sandbox/index.d.mts +9 -0
- package/dist/sandbox/index.mjs +7 -0
- package/dist/sandbox/task-environment.d.mts +50 -0
- package/dist/sandbox/task-environment.mjs +140 -0
- package/dist/sandbox/task-environment.mjs.map +1 -0
- package/dist/sandbox/workspace.d.mts +29 -0
- package/dist/sandbox/workspace.mjs +28 -0
- package/dist/sandbox/workspace.mjs.map +1 -0
- package/dist/schema/agent-route.d.mts +22 -0
- package/dist/schema/agent-route.mjs +15 -0
- package/dist/schema/agent-route.mjs.map +1 -0
- package/dist/schema/answer.d.mts +23 -0
- package/dist/schema/answer.mjs +21 -0
- package/dist/schema/answer.mjs.map +1 -0
- package/dist/schema/budget.d.mts +38 -0
- package/dist/schema/budget.mjs +30 -0
- package/dist/schema/budget.mjs.map +1 -0
- package/dist/schema/catalog.mjs +72 -0
- package/dist/schema/catalog.mjs.map +1 -0
- package/dist/schema/change.d.mts +117 -0
- package/dist/schema/change.mjs +68 -0
- package/dist/schema/change.mjs.map +1 -0
- package/dist/schema/common.d.mts +33 -0
- package/dist/{id-DwCihQr4.mjs → schema/common.mjs} +7 -14
- package/dist/schema/common.mjs.map +1 -0
- package/dist/schema/communication.d.mts +52 -0
- package/dist/schema/communication.mjs +35 -0
- package/dist/schema/communication.mjs.map +1 -0
- package/dist/schema/execution.d.mts +101 -0
- package/dist/schema/execution.mjs +86 -0
- package/dist/schema/execution.mjs.map +1 -0
- package/dist/schema/factory-config.d.mts +95 -0
- package/dist/schema/factory-config.mjs +60 -0
- package/dist/schema/factory-config.mjs.map +1 -0
- package/dist/schema/finding.d.mts +125 -0
- package/dist/schema/finding.mjs +67 -0
- package/dist/schema/finding.mjs.map +1 -0
- package/dist/schema/id.d.mts +43 -0
- package/dist/schema/id.mjs +32 -0
- package/dist/schema/id.mjs.map +1 -0
- package/dist/schema/outbox.d.mts +240 -0
- package/dist/schema/outbox.mjs +53 -0
- package/dist/schema/outbox.mjs.map +1 -0
- package/dist/schema/question-route.mjs +16 -0
- package/dist/schema/question-route.mjs.map +1 -0
- package/dist/schema/receipt.d.mts +81 -0
- package/dist/schema/receipt.mjs +94 -0
- package/dist/schema/receipt.mjs.map +1 -0
- package/dist/schema/repository.d.mts +26 -0
- package/dist/schema/repository.mjs +25 -0
- package/dist/schema/repository.mjs.map +1 -0
- package/dist/schema/session.d.mts +135 -0
- package/dist/schema/session.mjs +53 -0
- package/dist/schema/session.mjs.map +1 -0
- package/dist/schema/signal.d.mts +105 -0
- package/dist/schema/signal.mjs +81 -0
- package/dist/schema/signal.mjs.map +1 -0
- package/dist/schema/stored-session.mjs +10 -0
- package/dist/schema/stored-session.mjs.map +1 -0
- package/dist/schema/stored-task.mjs +20 -0
- package/dist/schema/stored-task.mjs.map +1 -0
- package/dist/schema/task-effect.d.mts +39 -0
- package/dist/schema/task-effect.mjs +29 -0
- package/dist/schema/task-effect.mjs.map +1 -0
- package/dist/schema/task-graph.d.mts +3134 -0
- package/dist/schema/task-graph.mjs +97 -0
- package/dist/schema/task-graph.mjs.map +1 -0
- package/dist/schema/task-message.d.mts +254 -0
- package/dist/schema/task-message.mjs +189 -0
- package/dist/schema/task-message.mjs.map +1 -0
- package/dist/schema/task.d.mts +272 -0
- package/dist/schema/task.mjs +199 -0
- package/dist/schema/task.mjs.map +1 -0
- package/dist/schema/transcript.d.mts +170 -0
- package/dist/schema/transcript.mjs +86 -0
- package/dist/schema/transcript.mjs.map +1 -0
- package/dist/schema/transitions.d.mts +50 -0
- package/dist/schema/transitions.mjs +303 -0
- package/dist/schema/transitions.mjs.map +1 -0
- package/dist/schema/work.d.mts +101 -0
- package/dist/schema/work.mjs +102 -0
- package/dist/schema/work.mjs.map +1 -0
- package/dist/signal-triage.d.mts +20 -0
- package/dist/signal-triage.mjs +85 -0
- package/dist/signal-triage.mjs.map +1 -0
- package/dist/signals.d.mts +19 -0
- package/dist/signals.mjs +19 -0
- package/dist/signals.mjs.map +1 -0
- package/dist/stall.d.mts +38 -0
- package/dist/stall.mjs +79 -0
- package/dist/stall.mjs.map +1 -0
- package/dist/storage.d.mts +4 -0
- package/dist/storage.mjs +4 -0
- package/dist/store/driver.d.mts +65 -0
- package/dist/store/driver.mjs +42 -0
- package/dist/store/driver.mjs.map +1 -0
- package/dist/store/engine.d.mts +485 -0
- package/dist/store/engine.mjs +2175 -0
- package/dist/store/engine.mjs.map +1 -0
- package/dist/store/memory.d.mts +7 -0
- package/dist/store/memory.mjs +52 -0
- package/dist/store/memory.mjs.map +1 -0
- package/dist/store/task-effects.d.mts +38 -0
- package/dist/store/task-effects.mjs +118 -0
- package/dist/store/task-effects.mjs.map +1 -0
- package/dist/store/task-graphs.d.mts +60 -0
- package/dist/store/task-graphs.mjs +212 -0
- package/dist/store/task-graphs.mjs.map +1 -0
- package/dist/store/task-messages.d.mts +136 -0
- package/dist/store/task-messages.mjs +475 -0
- package/dist/store/task-messages.mjs.map +1 -0
- package/dist/store/task-work.d.mts +65 -0
- package/dist/store/task-work.mjs +134 -0
- package/dist/store/task-work.mjs.map +1 -0
- package/dist/store/work-notifications.d.mts +19 -0
- package/dist/store/work-notifications.mjs +48 -0
- package/dist/store/work-notifications.mjs.map +1 -0
- package/dist/sweep.d.mts +57 -0
- package/dist/sweep.mjs +143 -0
- package/dist/sweep.mjs.map +1 -0
- package/dist/task-communication.d.mts +35 -0
- package/dist/task-communication.mjs +47 -0
- package/dist/task-communication.mjs.map +1 -0
- package/dist/task-graph-view.d.mts +264 -0
- package/dist/task-graph-view.mjs +17 -0
- package/dist/task-graph-view.mjs.map +1 -0
- package/dist/task-message-processing.d.mts +60 -0
- package/dist/task-message-processing.mjs +200 -0
- package/dist/task-message-processing.mjs.map +1 -0
- package/dist/task-protocols.d.mts +80 -0
- package/dist/task-protocols.mjs +47 -0
- package/dist/task-protocols.mjs.map +1 -0
- package/dist/tasks.d.mts +17 -0
- package/dist/tasks.mjs +17 -0
- package/dist/vercel-git.d.mts +21 -0
- package/dist/vercel-git.mjs +112 -0
- package/dist/vercel-git.mjs.map +1 -0
- package/dist/vercel.d.mts +33 -2
- package/dist/vercel.mjs +48 -3
- package/dist/vercel.mjs.map +1 -1
- package/dist/work-triage/eve-tool.d.mts +29 -0
- package/dist/work-triage/eve-tool.mjs +32 -0
- package/dist/work-triage/eve-tool.mjs.map +1 -0
- package/dist/work-triage/find-existing-work.d.mts +32 -0
- package/dist/work-triage/find-existing-work.mjs +31 -0
- package/dist/work-triage/find-existing-work.mjs.map +1 -0
- package/dist/work-triage/github.d.mts +15 -0
- package/dist/work-triage/github.mjs +14 -0
- package/dist/work-triage/github.mjs.map +1 -0
- package/dist/work-triage.d.mts +3 -0
- package/dist/work-triage.mjs +3 -0
- package/dist/workflows.d.mts +48 -0
- package/dist/workflows.mjs +86 -0
- package/dist/workflows.mjs.map +1 -0
- package/dist/workspace-diff-assembly.d.mts +33 -0
- package/dist/workspace-diff-assembly.mjs +95 -0
- package/dist/workspace-diff-assembly.mjs.map +1 -0
- package/dist/workspace-diff.d.mts +90 -0
- package/dist/workspace-diff.mjs +61 -0
- package/dist/workspace-diff.mjs.map +1 -0
- package/dist/workspace-snapshot.d.mts +26 -0
- package/dist/workspace-snapshot.mjs +80 -0
- package/dist/workspace-snapshot.mjs.map +1 -0
- package/package.json +129 -40
- package/dist/approval-CwhTP1XB.mjs +0 -269
- package/dist/approval-CwhTP1XB.mjs.map +0 -1
- package/dist/change-CzEAoFVE.mjs +0 -620
- package/dist/change-CzEAoFVE.mjs.map +0 -1
- package/dist/dispatch-B77cuPxh.d.mts +0 -713
- package/dist/driver-BJutB0PE.d.mts +0 -34
- package/dist/driver-Bbd25rNM.mjs +0 -23
- package/dist/driver-Bbd25rNM.mjs.map +0 -1
- package/dist/factory-config-BJcjsPKl.mjs +0 -86
- package/dist/factory-config-BJcjsPKl.mjs.map +0 -1
- package/dist/factory-config-D2yU3G0c.d.mts +0 -58
- package/dist/github-DXsxboUL.d.mts +0 -271
- package/dist/id-DwCihQr4.mjs.map +0 -1
- package/dist/index-Cy4jfXh5.d.mts +0 -67
- package/dist/index.mjs.map +0 -1
- package/dist/signal-CHVEmzvI.d.mts +0 -59
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { taskIdSchema } from "../schema/id.mjs";
|
|
2
|
+
import { isoDateTimeSchema } from "../schema/common.mjs";
|
|
3
|
+
import { RecordExistsError, RecordNotFoundError, VersionConflictError } from "./driver.mjs";
|
|
4
|
+
import { taskEffectKeySchema, taskEffectReferenceSchema, taskEffectSchema } from "../schema/task-effect.mjs";
|
|
5
|
+
import { createHash } from "node:crypto";
|
|
6
|
+
//#region src/store/task-effects.ts
|
|
7
|
+
function recordId(taskId, key) {
|
|
8
|
+
return `effect_${createHash("sha256").update(JSON.stringify([
|
|
9
|
+
taskId,
|
|
10
|
+
key.namespace,
|
|
11
|
+
key.name
|
|
12
|
+
])).digest("hex").slice(0, 32)}`;
|
|
13
|
+
}
|
|
14
|
+
/** Creates a CAS-protected store for idempotent Task effect reconciliation. */
|
|
15
|
+
function createTaskEffectStore(options) {
|
|
16
|
+
async function assertTask(taskId) {
|
|
17
|
+
if (await options.getTask(taskId) === null) throw new RecordNotFoundError("tasks", taskId);
|
|
18
|
+
}
|
|
19
|
+
async function read(taskId, key) {
|
|
20
|
+
const id = recordId(taskId, key);
|
|
21
|
+
const stored = await options.driver.get("taskEffects", id);
|
|
22
|
+
if (stored === null) return null;
|
|
23
|
+
const effect = taskEffectSchema.parse(stored.value);
|
|
24
|
+
if (effect.taskId !== taskId || effect.key.namespace !== key.namespace || effect.key.name !== key.name) throw new Error(`Task effect ${id} does not match its storage key`);
|
|
25
|
+
return {
|
|
26
|
+
effect,
|
|
27
|
+
id,
|
|
28
|
+
version: stored.version
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
return {
|
|
32
|
+
async get(input) {
|
|
33
|
+
return (await read(taskIdSchema.parse(input.taskId), taskEffectKeySchema.parse(input.key)))?.effect ?? null;
|
|
34
|
+
},
|
|
35
|
+
async record(input) {
|
|
36
|
+
const taskId = taskIdSchema.parse(input.taskId);
|
|
37
|
+
const key = taskEffectKeySchema.parse(input.key);
|
|
38
|
+
const reference = taskEffectReferenceSchema.parse(input.reference);
|
|
39
|
+
await assertTask(taskId);
|
|
40
|
+
for (;;) {
|
|
41
|
+
const existing = await read(taskId, key);
|
|
42
|
+
if (existing?.effect.reference !== void 0) return existing.effect;
|
|
43
|
+
const timestamp = options.now();
|
|
44
|
+
const next = taskEffectSchema.parse(existing === null ? {
|
|
45
|
+
schemaVersion: 1,
|
|
46
|
+
taskId,
|
|
47
|
+
key,
|
|
48
|
+
reference,
|
|
49
|
+
createdAt: timestamp,
|
|
50
|
+
updatedAt: timestamp
|
|
51
|
+
} : {
|
|
52
|
+
...existing.effect,
|
|
53
|
+
reference,
|
|
54
|
+
updatedAt: timestamp
|
|
55
|
+
});
|
|
56
|
+
try {
|
|
57
|
+
if (existing === null) await options.driver.insert({
|
|
58
|
+
collection: "taskEffects",
|
|
59
|
+
id: recordId(taskId, key),
|
|
60
|
+
value: next
|
|
61
|
+
});
|
|
62
|
+
else await options.driver.update({
|
|
63
|
+
collection: "taskEffects",
|
|
64
|
+
id: existing.id,
|
|
65
|
+
value: next,
|
|
66
|
+
expectedVersion: existing.version
|
|
67
|
+
});
|
|
68
|
+
return next;
|
|
69
|
+
} catch (error) {
|
|
70
|
+
if (!(error instanceof RecordExistsError || error instanceof VersionConflictError)) throw error;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
async resolve(input) {
|
|
75
|
+
const taskId = taskIdSchema.parse(input.taskId);
|
|
76
|
+
const key = taskEffectKeySchema.parse(input.key);
|
|
77
|
+
const resolvedAt = isoDateTimeSchema.parse(input.at ?? options.now());
|
|
78
|
+
await assertTask(taskId);
|
|
79
|
+
for (;;) {
|
|
80
|
+
const existing = await read(taskId, key);
|
|
81
|
+
if (existing?.effect.resolvedAt !== void 0) return existing.effect;
|
|
82
|
+
const timestamp = options.now();
|
|
83
|
+
const next = taskEffectSchema.parse(existing === null ? {
|
|
84
|
+
schemaVersion: 1,
|
|
85
|
+
taskId,
|
|
86
|
+
key,
|
|
87
|
+
resolvedAt,
|
|
88
|
+
createdAt: timestamp,
|
|
89
|
+
updatedAt: timestamp
|
|
90
|
+
} : {
|
|
91
|
+
...existing.effect,
|
|
92
|
+
resolvedAt,
|
|
93
|
+
updatedAt: timestamp
|
|
94
|
+
});
|
|
95
|
+
try {
|
|
96
|
+
if (existing === null) await options.driver.insert({
|
|
97
|
+
collection: "taskEffects",
|
|
98
|
+
id: recordId(taskId, key),
|
|
99
|
+
value: next
|
|
100
|
+
});
|
|
101
|
+
else await options.driver.update({
|
|
102
|
+
collection: "taskEffects",
|
|
103
|
+
id: existing.id,
|
|
104
|
+
value: next,
|
|
105
|
+
expectedVersion: existing.version
|
|
106
|
+
});
|
|
107
|
+
return next;
|
|
108
|
+
} catch (error) {
|
|
109
|
+
if (!(error instanceof RecordExistsError || error instanceof VersionConflictError)) throw error;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
//#endregion
|
|
116
|
+
export { createTaskEffectStore };
|
|
117
|
+
|
|
118
|
+
//# sourceMappingURL=task-effects.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"task-effects.mjs","names":[],"sources":["../../src/store/task-effects.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { SCHEMA_VERSION, isoDateTimeSchema, type IsoDateTimeClock } from \"../schema/common\";\nimport { taskIdSchema, type TaskId } from \"../schema/id\";\nimport type { Task } from \"../schema/task\";\nimport {\n taskEffectKeySchema,\n taskEffectReferenceSchema,\n taskEffectSchema,\n type TaskEffect,\n type TaskEffectKey,\n type TaskEffectReference,\n} from \"../schema/task-effect\";\nimport {\n RecordExistsError,\n RecordNotFoundError,\n VersionConflictError,\n type StoreDriver,\n} from \"./driver\";\n\n/** Persistence, clock, and Task lookup dependencies for the Task effect store. */\nexport interface CreateTaskEffectStoreOptions {\n driver: StoreDriver;\n now: IsoDateTimeClock;\n getTask: (id: TaskId) => Promise<Task | null>;\n}\n\n/** Task identity and namespaced key identifying one durable external effect. */\nexport interface TaskEffectLookupInput {\n readonly taskId: TaskId;\n readonly key: TaskEffectKey;\n}\n\n/** Task-scoped effect key and first external-resource reference to record. */\nexport interface RecordTaskEffectInput extends TaskEffectLookupInput {\n readonly reference: TaskEffectReference;\n}\n\n/** Task-scoped effect key and optional explicit resolution timestamp. */\nexport interface ResolveTaskEffectInput extends TaskEffectLookupInput {\n readonly at?: string;\n}\n\n/** Durable, Task-scoped external-resource references and write-once resolution stamps. */\nexport interface TaskEffectStore {\n get(input: TaskEffectLookupInput): Promise<TaskEffect | null>;\n /** Record the first external resource created for this key. Replays return that first record. */\n record(input: RecordTaskEffectInput): Promise<TaskEffect>;\n /** Stamp the effect resolved once, including when no external resource was found. */\n resolve(input: ResolveTaskEffectInput): Promise<TaskEffect>;\n}\n\nfunction recordId(taskId: TaskId, key: TaskEffectKey): string {\n const digest = createHash(\"sha256\")\n .update(JSON.stringify([taskId, key.namespace, key.name]))\n .digest(\"hex\");\n return `effect_${digest.slice(0, 32)}`;\n}\n\n/** Creates a CAS-protected store for idempotent Task effect reconciliation. */\nexport function createTaskEffectStore(options: CreateTaskEffectStoreOptions): TaskEffectStore {\n async function assertTask(taskId: TaskId): Promise<void> {\n if ((await options.getTask(taskId)) === null) throw new RecordNotFoundError(\"tasks\", taskId);\n }\n\n async function read(taskId: TaskId, key: TaskEffectKey) {\n const id = recordId(taskId, key);\n const stored = await options.driver.get(\"taskEffects\", id);\n if (stored === null) return null;\n const effect = taskEffectSchema.parse(stored.value);\n if (\n effect.taskId !== taskId ||\n effect.key.namespace !== key.namespace ||\n effect.key.name !== key.name\n ) {\n throw new Error(`Task effect ${id} does not match its storage key`);\n }\n return { effect, id, version: stored.version };\n }\n\n return {\n async get(input) {\n const taskId = taskIdSchema.parse(input.taskId);\n const key = taskEffectKeySchema.parse(input.key);\n return (await read(taskId, key))?.effect ?? null;\n },\n\n async record(input) {\n const taskId = taskIdSchema.parse(input.taskId);\n const key = taskEffectKeySchema.parse(input.key);\n const reference = taskEffectReferenceSchema.parse(input.reference);\n await assertTask(taskId);\n for (;;) {\n const existing = await read(taskId, key);\n if (existing?.effect.reference !== undefined) return existing.effect;\n const timestamp = options.now();\n const next = taskEffectSchema.parse(\n existing === null\n ? {\n schemaVersion: SCHEMA_VERSION,\n taskId,\n key,\n reference,\n createdAt: timestamp,\n updatedAt: timestamp,\n }\n : { ...existing.effect, reference, updatedAt: timestamp },\n );\n try {\n if (existing === null)\n await options.driver.insert({\n collection: \"taskEffects\",\n id: recordId(taskId, key),\n value: next,\n });\n else\n await options.driver.update({\n collection: \"taskEffects\",\n id: existing.id,\n value: next,\n expectedVersion: existing.version,\n });\n return next;\n } catch (error) {\n if (!(error instanceof RecordExistsError || error instanceof VersionConflictError))\n throw error;\n }\n }\n },\n\n async resolve(input) {\n const taskId = taskIdSchema.parse(input.taskId);\n const key = taskEffectKeySchema.parse(input.key);\n const resolvedAt = isoDateTimeSchema.parse(input.at ?? options.now());\n await assertTask(taskId);\n for (;;) {\n const existing = await read(taskId, key);\n if (existing?.effect.resolvedAt !== undefined) return existing.effect;\n const timestamp = options.now();\n const next = taskEffectSchema.parse(\n existing === null\n ? {\n schemaVersion: SCHEMA_VERSION,\n taskId,\n key,\n resolvedAt,\n createdAt: timestamp,\n updatedAt: timestamp,\n }\n : { ...existing.effect, resolvedAt, updatedAt: timestamp },\n );\n try {\n if (existing === null)\n await options.driver.insert({\n collection: \"taskEffects\",\n id: recordId(taskId, key),\n value: next,\n });\n else\n await options.driver.update({\n collection: \"taskEffects\",\n id: existing.id,\n value: next,\n expectedVersion: existing.version,\n });\n return next;\n } catch (error) {\n if (!(error instanceof RecordExistsError || error instanceof VersionConflictError))\n throw error;\n }\n }\n },\n };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { TaskId } from "../schema/id.mjs";
|
|
2
|
+
import { Task } from "../schema/task.mjs";
|
|
3
|
+
import { WorkNotification } from "./work-notifications.mjs";
|
|
4
|
+
import { IsoDateTimeClock } from "../schema/common.mjs";
|
|
5
|
+
import { StoreDriver } from "./driver.mjs";
|
|
6
|
+
import { TaskGraph } from "../schema/task-graph.mjs";
|
|
7
|
+
//#region src/store/task-graphs.d.ts
|
|
8
|
+
/** Engine-owned persistence and validation dependencies for canonical Task graphs. */
|
|
9
|
+
interface CreateTaskGraphStoreOptions {
|
|
10
|
+
driver: StoreDriver;
|
|
11
|
+
now: IsoDateTimeClock;
|
|
12
|
+
getTask: (id: TaskId) => Promise<Task | null>;
|
|
13
|
+
validateTask: (task: Task) => void;
|
|
14
|
+
notify?: (event: WorkNotification) => Promise<void>;
|
|
15
|
+
}
|
|
16
|
+
/** Canonical bounded graph store for Task membership, ownership, and dependency edges. */
|
|
17
|
+
interface TaskGraphStore {
|
|
18
|
+
admit(input: AdmitTaskGraphInput): Promise<void>;
|
|
19
|
+
get(rootTaskId: TaskId): Promise<TaskGraph | null>;
|
|
20
|
+
setDependencies(input: SetTaskGraphDependenciesInput): Promise<TaskGraph>;
|
|
21
|
+
dependenciesSatisfied(input: TaskGraphNodeRef): Promise<boolean>;
|
|
22
|
+
/** Call before every Task transition to running. First admission freezes edges, even if launch fails. */
|
|
23
|
+
seal(input: TaskGraphNodeRef): Promise<void>;
|
|
24
|
+
}
|
|
25
|
+
/** Canonical Task snapshot and initial dependency edges admitted atomically to its graph. */
|
|
26
|
+
interface AdmitTaskGraphInput {
|
|
27
|
+
task: Task;
|
|
28
|
+
dependencies?: readonly TaskId[];
|
|
29
|
+
}
|
|
30
|
+
/** Identifies one Task within its canonical root graph. */
|
|
31
|
+
interface TaskGraphNodeRef {
|
|
32
|
+
rootTaskId: TaskId;
|
|
33
|
+
taskId: TaskId;
|
|
34
|
+
}
|
|
35
|
+
/** Revision-fenced replacement of one queued Task's graph dependencies. */
|
|
36
|
+
interface SetTaskGraphDependenciesInput extends TaskGraphNodeRef {
|
|
37
|
+
dependencies: readonly TaskId[];
|
|
38
|
+
expectedRevision: number;
|
|
39
|
+
}
|
|
40
|
+
/** Error raised when a creation retry differs from immutable graph admission intent. */
|
|
41
|
+
declare class TaskGraphConflictError extends Error {
|
|
42
|
+
constructor(taskId: TaskId);
|
|
43
|
+
}
|
|
44
|
+
/** Canonical Task and immutable graph admission snapshot to compare. */
|
|
45
|
+
interface AssertTaskAdmissionInput {
|
|
46
|
+
task: Task;
|
|
47
|
+
admission: Task;
|
|
48
|
+
}
|
|
49
|
+
/** Verifies that a canonical Task preserves the immutable identity admitted to its graph. */
|
|
50
|
+
declare function assertTaskAdmission({ task, admission }: AssertTaskAdmissionInput): void;
|
|
51
|
+
/**
|
|
52
|
+
* Trusted engine/workflow capability, not an agent tool or HTTP boundary: callers own
|
|
53
|
+
* authentication and workflow binding validation. The engine seals dependencies before
|
|
54
|
+
* transitioning to running; sealing survives a failed launch and rechecks prerequisites
|
|
55
|
+
* on retries. Succeeded prerequisites are terminal under the engine's transition rules.
|
|
56
|
+
*/
|
|
57
|
+
declare function createTaskGraphStore({ driver, now, getTask, validateTask, notify }: CreateTaskGraphStoreOptions): TaskGraphStore;
|
|
58
|
+
//#endregion
|
|
59
|
+
export { AdmitTaskGraphInput, AssertTaskAdmissionInput, CreateTaskGraphStoreOptions, SetTaskGraphDependenciesInput, TaskGraphConflictError, TaskGraphNodeRef, TaskGraphStore, assertTaskAdmission, createTaskGraphStore };
|
|
60
|
+
//# sourceMappingURL=task-graphs.d.mts.map
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import { taskIdSchema } from "../schema/id.mjs";
|
|
2
|
+
import "../schema/common.mjs";
|
|
3
|
+
import { taskSchema } from "../schema/task.mjs";
|
|
4
|
+
import { RecordExistsError, RecordNotFoundError, VersionConflictError } from "./driver.mjs";
|
|
5
|
+
import { taskGraphAdmissionSchema, taskGraphDependenciesSchema, taskGraphSchema } from "../schema/task-graph.mjs";
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
import { isDeepStrictEqual } from "node:util";
|
|
8
|
+
//#region src/store/task-graphs.ts
|
|
9
|
+
/** Error raised when a creation retry differs from immutable graph admission intent. */
|
|
10
|
+
var TaskGraphConflictError = class extends Error {
|
|
11
|
+
constructor(taskId) {
|
|
12
|
+
super(`Task ${taskId} conflicts with its immutable graph admission; reuse the original creation intent`);
|
|
13
|
+
this.name = "TaskGraphConflictError";
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
function immutableTaskIdentity(task) {
|
|
17
|
+
const { id, rootTaskId, parentTaskId, repositoryIds, work, kind, origin, replyTo, area } = task;
|
|
18
|
+
return JSON.parse(JSON.stringify({
|
|
19
|
+
id,
|
|
20
|
+
rootTaskId,
|
|
21
|
+
parentTaskId,
|
|
22
|
+
repositoryIds,
|
|
23
|
+
work,
|
|
24
|
+
kind,
|
|
25
|
+
origin,
|
|
26
|
+
replyTo,
|
|
27
|
+
area
|
|
28
|
+
}));
|
|
29
|
+
}
|
|
30
|
+
/** Verifies that a canonical Task preserves the immutable identity admitted to its graph. */
|
|
31
|
+
function assertTaskAdmission({ task, admission }) {
|
|
32
|
+
if (!isDeepStrictEqual(immutableTaskIdentity(task), immutableTaskIdentity(admission))) throw new TaskGraphConflictError(admission.id);
|
|
33
|
+
}
|
|
34
|
+
function creationIntent({ createdAt: _createdAt, updatedAt: _updatedAt, ...intent }) {
|
|
35
|
+
return JSON.parse(JSON.stringify(intent));
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Trusted engine/workflow capability, not an agent tool or HTTP boundary: callers own
|
|
39
|
+
* authentication and workflow binding validation. The engine seals dependencies before
|
|
40
|
+
* transitioning to running; sealing survives a failed launch and rechecks prerequisites
|
|
41
|
+
* on retries. Succeeded prerequisites are terminal under the engine's transition rules.
|
|
42
|
+
*/
|
|
43
|
+
function createTaskGraphStore({ driver, now, getTask, validateTask, notify }) {
|
|
44
|
+
async function read(rootTaskId) {
|
|
45
|
+
taskIdSchema.parse(rootTaskId);
|
|
46
|
+
const record = await driver.get("taskGraphs", rootTaskId);
|
|
47
|
+
if (!record) return null;
|
|
48
|
+
const graph = taskGraphSchema.parse(record.value);
|
|
49
|
+
for (const node of graph.nodes) validateTask(node.admission);
|
|
50
|
+
if (graph.rootTaskId !== rootTaskId) throw new Error("Stored graph root does not match its storage key");
|
|
51
|
+
return {
|
|
52
|
+
graph,
|
|
53
|
+
version: record.version
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
async function canonical(node) {
|
|
57
|
+
const value = await getTask(node.taskId);
|
|
58
|
+
if (value === null) return null;
|
|
59
|
+
const task = taskSchema.parse(value);
|
|
60
|
+
validateTask(task);
|
|
61
|
+
assertTaskAdmission({
|
|
62
|
+
task,
|
|
63
|
+
admission: node.admission
|
|
64
|
+
});
|
|
65
|
+
return task;
|
|
66
|
+
}
|
|
67
|
+
async function satisfied(graph, node) {
|
|
68
|
+
for (const id of node.dependencies) {
|
|
69
|
+
const dependency = graph.nodes.find((entry) => entry.taskId === id);
|
|
70
|
+
if (!dependency || (await canonical(dependency))?.state !== "succeeded") return false;
|
|
71
|
+
}
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
return {
|
|
75
|
+
async admit(input) {
|
|
76
|
+
const task = structuredClone(taskGraphAdmissionSchema.parse(input.task));
|
|
77
|
+
validateTask(task);
|
|
78
|
+
const initialDependencies = taskGraphDependenciesSchema.parse(input.dependencies ?? []);
|
|
79
|
+
const rootTaskId = taskIdSchema.parse(task.rootTaskId);
|
|
80
|
+
const node = {
|
|
81
|
+
taskId: task.id,
|
|
82
|
+
...task.parentTaskId === void 0 ? {} : { parentTaskId: task.parentTaskId },
|
|
83
|
+
dependencies: initialDependencies,
|
|
84
|
+
sealed: false,
|
|
85
|
+
admissionDependencies: [...initialDependencies],
|
|
86
|
+
admission: task
|
|
87
|
+
};
|
|
88
|
+
await canonical(node);
|
|
89
|
+
for (;;) {
|
|
90
|
+
const current = await read(rootTaskId);
|
|
91
|
+
const existing = current?.graph.nodes.find((entry) => entry.taskId === task.id);
|
|
92
|
+
if (existing) {
|
|
93
|
+
if (!isDeepStrictEqual(creationIntent(existing.admission), creationIntent(task)) || !isDeepStrictEqual(existing.admissionDependencies, initialDependencies)) throw new TaskGraphConflictError(task.id);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
const timestamp = now();
|
|
97
|
+
const graph = taskGraphSchema.parse(current ? {
|
|
98
|
+
...current.graph,
|
|
99
|
+
revision: current.graph.revision + 1,
|
|
100
|
+
nodes: [...current.graph.nodes, node],
|
|
101
|
+
updatedAt: timestamp
|
|
102
|
+
} : {
|
|
103
|
+
schemaVersion: 1,
|
|
104
|
+
rootTaskId,
|
|
105
|
+
revision: 1,
|
|
106
|
+
nodes: [node],
|
|
107
|
+
createdAt: timestamp,
|
|
108
|
+
updatedAt: timestamp
|
|
109
|
+
});
|
|
110
|
+
try {
|
|
111
|
+
if (current) await driver.update({
|
|
112
|
+
collection: "taskGraphs",
|
|
113
|
+
id: rootTaskId,
|
|
114
|
+
value: graph,
|
|
115
|
+
expectedVersion: current.version
|
|
116
|
+
});
|
|
117
|
+
else await driver.insert({
|
|
118
|
+
collection: "taskGraphs",
|
|
119
|
+
id: rootTaskId,
|
|
120
|
+
value: graph
|
|
121
|
+
});
|
|
122
|
+
return;
|
|
123
|
+
} catch (error) {
|
|
124
|
+
if (!(error instanceof RecordExistsError || error instanceof VersionConflictError)) throw error;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
async get(rootTaskId) {
|
|
129
|
+
return (await read(rootTaskId))?.graph ?? null;
|
|
130
|
+
},
|
|
131
|
+
async setDependencies(input) {
|
|
132
|
+
const { rootTaskId, taskId, dependencies, expectedRevision } = input;
|
|
133
|
+
taskIdSchema.parse(taskId);
|
|
134
|
+
z.number().int().positive().parse(expectedRevision);
|
|
135
|
+
const edges = taskGraphDependenciesSchema.parse(dependencies);
|
|
136
|
+
const current = await read(rootTaskId);
|
|
137
|
+
if (!current) throw new RecordNotFoundError("taskGraphs", rootTaskId);
|
|
138
|
+
if (current.graph.revision !== expectedRevision) throw new VersionConflictError("taskGraphs", rootTaskId, expectedRevision);
|
|
139
|
+
const node = current.graph.nodes.find((entry) => entry.taskId === taskId);
|
|
140
|
+
if (!node) throw new Error(`Task ${taskId} is not registered in graph ${rootTaskId}`);
|
|
141
|
+
if (node.sealed) throw new Error(`Task ${taskId} dependencies are sealed by first execution admission and cannot be changed`);
|
|
142
|
+
const task = await canonical(node);
|
|
143
|
+
if (!task) throw new RecordNotFoundError("tasks", taskId);
|
|
144
|
+
if (task.state !== "queued") throw new Error(`Task ${taskId} must be queued to change dependencies; current state is ${task.state}`);
|
|
145
|
+
const graph = taskGraphSchema.parse({
|
|
146
|
+
...current.graph,
|
|
147
|
+
revision: current.graph.revision + 1,
|
|
148
|
+
nodes: current.graph.nodes.map((entry) => entry.taskId === taskId ? {
|
|
149
|
+
...entry,
|
|
150
|
+
dependencies: edges
|
|
151
|
+
} : entry),
|
|
152
|
+
updatedAt: now()
|
|
153
|
+
});
|
|
154
|
+
await driver.update({
|
|
155
|
+
collection: "taskGraphs",
|
|
156
|
+
id: rootTaskId,
|
|
157
|
+
value: graph,
|
|
158
|
+
expectedVersion: current.version
|
|
159
|
+
});
|
|
160
|
+
await notify?.({
|
|
161
|
+
rootTaskId,
|
|
162
|
+
key: `dependencies:${rootTaskId}:${graph.revision}`
|
|
163
|
+
});
|
|
164
|
+
return graph;
|
|
165
|
+
},
|
|
166
|
+
async dependenciesSatisfied({ rootTaskId, taskId }) {
|
|
167
|
+
taskIdSchema.parse(taskId);
|
|
168
|
+
const current = await read(rootTaskId);
|
|
169
|
+
const node = current?.graph.nodes.find((entry) => entry.taskId === taskId);
|
|
170
|
+
if (!current || !node || !await canonical(node)) return false;
|
|
171
|
+
return satisfied(current.graph, node);
|
|
172
|
+
},
|
|
173
|
+
async seal({ rootTaskId, taskId }) {
|
|
174
|
+
taskIdSchema.parse(taskId);
|
|
175
|
+
for (;;) {
|
|
176
|
+
const current = await read(rootTaskId);
|
|
177
|
+
if (!current) throw new RecordNotFoundError("taskGraphs", rootTaskId);
|
|
178
|
+
const node = current.graph.nodes.find((entry) => entry.taskId === taskId);
|
|
179
|
+
if (!node) throw new Error(`Task ${taskId} is not registered in graph ${rootTaskId}`);
|
|
180
|
+
const task = await canonical(node);
|
|
181
|
+
if (!task) throw new RecordNotFoundError("tasks", taskId);
|
|
182
|
+
if (!node.sealed && task.state !== "queued") throw new Error(`Task ${taskId} must be queued for first execution admission; current state is ${task.state}`);
|
|
183
|
+
if (!await satisfied(current.graph, node)) throw new Error(`Task ${taskId} dependencies must all be succeeded before execution admission`);
|
|
184
|
+
if (node.sealed) return;
|
|
185
|
+
const graph = taskGraphSchema.parse({
|
|
186
|
+
...current.graph,
|
|
187
|
+
revision: current.graph.revision + 1,
|
|
188
|
+
nodes: current.graph.nodes.map((entry) => entry.taskId === taskId ? {
|
|
189
|
+
...entry,
|
|
190
|
+
sealed: true
|
|
191
|
+
} : entry),
|
|
192
|
+
updatedAt: now()
|
|
193
|
+
});
|
|
194
|
+
try {
|
|
195
|
+
await driver.update({
|
|
196
|
+
collection: "taskGraphs",
|
|
197
|
+
id: rootTaskId,
|
|
198
|
+
value: graph,
|
|
199
|
+
expectedVersion: current.version
|
|
200
|
+
});
|
|
201
|
+
return;
|
|
202
|
+
} catch (error) {
|
|
203
|
+
if (!(error instanceof VersionConflictError)) throw error;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
//#endregion
|
|
210
|
+
export { TaskGraphConflictError, assertTaskAdmission, createTaskGraphStore };
|
|
211
|
+
|
|
212
|
+
//# sourceMappingURL=task-graphs.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"task-graphs.mjs","names":[],"sources":["../../src/store/task-graphs.ts"],"sourcesContent":["import { isDeepStrictEqual } from \"node:util\";\nimport type { WorkNotification } from \"./work-notifications\";\nimport { z } from \"zod\";\nimport { SCHEMA_VERSION } from \"../schema/common\";\nimport type { IsoDateTimeClock } from \"../schema/common\";\nimport { taskIdSchema, type TaskId } from \"../schema/id\";\nimport { taskSchema, type Task } from \"../schema/task\";\nimport {\n taskGraphAdmissionSchema,\n taskGraphDependenciesSchema,\n taskGraphSchema,\n type TaskGraph,\n type TaskGraphNode,\n} from \"../schema/task-graph\";\nimport {\n RecordExistsError,\n RecordNotFoundError,\n VersionConflictError,\n type StoreDriver,\n} from \"./driver\";\n\n/** Engine-owned persistence and validation dependencies for canonical Task graphs. */\nexport interface CreateTaskGraphStoreOptions {\n driver: StoreDriver;\n now: IsoDateTimeClock;\n getTask: (id: TaskId) => Promise<Task | null>;\n validateTask: (task: Task) => void;\n notify?: (event: WorkNotification) => Promise<void>;\n}\n\n/** Canonical bounded graph store for Task membership, ownership, and dependency edges. */\nexport interface TaskGraphStore {\n admit(input: AdmitTaskGraphInput): Promise<void>;\n get(rootTaskId: TaskId): Promise<TaskGraph | null>;\n setDependencies(input: SetTaskGraphDependenciesInput): Promise<TaskGraph>;\n dependenciesSatisfied(input: TaskGraphNodeRef): Promise<boolean>;\n /** Call before every Task transition to running. First admission freezes edges, even if launch fails. */\n seal(input: TaskGraphNodeRef): Promise<void>;\n}\n\n/** Canonical Task snapshot and initial dependency edges admitted atomically to its graph. */\nexport interface AdmitTaskGraphInput {\n task: Task;\n dependencies?: readonly TaskId[];\n}\n\n/** Identifies one Task within its canonical root graph. */\nexport interface TaskGraphNodeRef {\n rootTaskId: TaskId;\n taskId: TaskId;\n}\n\n/** Revision-fenced replacement of one queued Task's graph dependencies. */\nexport interface SetTaskGraphDependenciesInput extends TaskGraphNodeRef {\n dependencies: readonly TaskId[];\n expectedRevision: number;\n}\n\n/** Error raised when a creation retry differs from immutable graph admission intent. */\nexport class TaskGraphConflictError extends Error {\n constructor(taskId: TaskId) {\n super(\n `Task ${taskId} conflicts with its immutable graph admission; reuse the original creation intent`,\n );\n this.name = \"TaskGraphConflictError\";\n }\n}\n\n/** Canonical Task and immutable graph admission snapshot to compare. */\nexport interface AssertTaskAdmissionInput {\n task: Task;\n admission: Task;\n}\n\nfunction immutableTaskIdentity(task: Task): unknown {\n const { id, rootTaskId, parentTaskId, repositoryIds, work, kind, origin, replyTo, area } = task;\n // Omitted optional fields and explicit undefined have the same persisted identity.\n return JSON.parse(\n JSON.stringify({\n id,\n rootTaskId,\n parentTaskId,\n repositoryIds,\n work,\n kind,\n origin,\n replyTo,\n area,\n }),\n );\n}\n\n/** Verifies that a canonical Task preserves the immutable identity admitted to its graph. */\nexport function assertTaskAdmission({ task, admission }: AssertTaskAdmissionInput): void {\n if (!isDeepStrictEqual(immutableTaskIdentity(task), immutableTaskIdentity(admission))) {\n throw new TaskGraphConflictError(admission.id);\n }\n}\n\nfunction creationIntent({\n createdAt: _createdAt,\n updatedAt: _updatedAt,\n ...intent\n}: Task): unknown {\n // JSON persistence drops optional undefined fields; omitted and undefined have the same intent.\n const value: unknown = JSON.parse(JSON.stringify(intent));\n return value;\n}\n\n/**\n * Trusted engine/workflow capability, not an agent tool or HTTP boundary: callers own\n * authentication and workflow binding validation. The engine seals dependencies before\n * transitioning to running; sealing survives a failed launch and rechecks prerequisites\n * on retries. Succeeded prerequisites are terminal under the engine's transition rules.\n */\nexport function createTaskGraphStore({\n driver,\n now,\n getTask,\n validateTask,\n notify,\n}: CreateTaskGraphStoreOptions): TaskGraphStore {\n async function read(rootTaskId: TaskId) {\n taskIdSchema.parse(rootTaskId);\n const record = await driver.get(\"taskGraphs\", rootTaskId);\n if (!record) return null;\n const graph = taskGraphSchema.parse(record.value);\n for (const node of graph.nodes) validateTask(node.admission);\n if (graph.rootTaskId !== rootTaskId)\n throw new Error(\"Stored graph root does not match its storage key\");\n return { graph, version: record.version };\n }\n\n async function canonical(node: TaskGraphNode): Promise<Task | null> {\n const value = await getTask(node.taskId);\n if (value === null) return null;\n const task = taskSchema.parse(value);\n validateTask(task);\n assertTaskAdmission({ task, admission: node.admission });\n return task;\n }\n\n async function satisfied(graph: TaskGraph, node: TaskGraphNode): Promise<boolean> {\n for (const id of node.dependencies) {\n const dependency = graph.nodes.find((entry) => entry.taskId === id);\n if (!dependency || (await canonical(dependency))?.state !== \"succeeded\") return false;\n }\n return true;\n }\n\n return {\n async admit(input) {\n // Zod's custom JSON schema validates but does not clone work.input.\n const task = structuredClone(taskGraphAdmissionSchema.parse(input.task));\n validateTask(task);\n const initialDependencies = taskGraphDependenciesSchema.parse(input.dependencies ?? []);\n const rootTaskId = taskIdSchema.parse(task.rootTaskId);\n const node: TaskGraphNode = {\n taskId: task.id,\n ...(task.parentTaskId === undefined ? {} : { parentTaskId: task.parentTaskId }),\n dependencies: initialDependencies,\n sealed: false,\n admissionDependencies: [...initialDependencies],\n admission: task,\n };\n await canonical(node);\n for (;;) {\n const current = await read(rootTaskId);\n const existing = current?.graph.nodes.find((entry) => entry.taskId === task.id);\n if (existing) {\n if (\n !isDeepStrictEqual(creationIntent(existing.admission), creationIntent(task)) ||\n !isDeepStrictEqual(existing.admissionDependencies, initialDependencies)\n ) {\n throw new TaskGraphConflictError(task.id);\n }\n return;\n }\n const timestamp = now();\n const graph = taskGraphSchema.parse(\n current\n ? {\n ...current.graph,\n revision: current.graph.revision + 1,\n nodes: [...current.graph.nodes, node],\n updatedAt: timestamp,\n }\n : {\n schemaVersion: SCHEMA_VERSION,\n rootTaskId,\n revision: 1,\n nodes: [node],\n createdAt: timestamp,\n updatedAt: timestamp,\n },\n );\n try {\n if (current)\n await driver.update({\n collection: \"taskGraphs\",\n id: rootTaskId,\n value: graph,\n expectedVersion: current.version,\n });\n else await driver.insert({ collection: \"taskGraphs\", id: rootTaskId, value: graph });\n return;\n } catch (error) {\n if (!(error instanceof RecordExistsError || error instanceof VersionConflictError))\n throw error;\n }\n }\n },\n\n async get(rootTaskId) {\n return (await read(rootTaskId))?.graph ?? null;\n },\n\n async setDependencies(input) {\n const { rootTaskId, taskId, dependencies, expectedRevision } = input;\n taskIdSchema.parse(taskId);\n z.number().int().positive().parse(expectedRevision);\n const edges = taskGraphDependenciesSchema.parse(dependencies);\n const current = await read(rootTaskId);\n if (!current) throw new RecordNotFoundError(\"taskGraphs\", rootTaskId);\n if (current.graph.revision !== expectedRevision) {\n throw new VersionConflictError(\"taskGraphs\", rootTaskId, expectedRevision);\n }\n const node = current.graph.nodes.find((entry) => entry.taskId === taskId);\n if (!node) throw new Error(`Task ${taskId} is not registered in graph ${rootTaskId}`);\n if (node.sealed)\n throw new Error(\n `Task ${taskId} dependencies are sealed by first execution admission and cannot be changed`,\n );\n const task = await canonical(node);\n if (!task) throw new RecordNotFoundError(\"tasks\", taskId);\n if (task.state !== \"queued\")\n throw new Error(\n `Task ${taskId} must be queued to change dependencies; current state is ${task.state}`,\n );\n const graph = taskGraphSchema.parse({\n ...current.graph,\n revision: current.graph.revision + 1,\n nodes: current.graph.nodes.map((entry) =>\n entry.taskId === taskId ? { ...entry, dependencies: edges } : entry,\n ),\n updatedAt: now(),\n });\n // Deliberately do not retry: a losing editor must read the winning graph and replan.\n await driver.update({\n collection: \"taskGraphs\",\n id: rootTaskId,\n value: graph,\n expectedVersion: current.version,\n });\n await notify?.({ rootTaskId, key: `dependencies:${rootTaskId}:${graph.revision}` });\n return graph;\n },\n\n async dependenciesSatisfied({ rootTaskId, taskId }) {\n taskIdSchema.parse(taskId);\n const current = await read(rootTaskId);\n const node = current?.graph.nodes.find((entry) => entry.taskId === taskId);\n if (!current || !node || !(await canonical(node))) return false;\n return satisfied(current.graph, node);\n },\n\n async seal({ rootTaskId, taskId }) {\n taskIdSchema.parse(taskId);\n for (;;) {\n const current = await read(rootTaskId);\n if (!current) throw new RecordNotFoundError(\"taskGraphs\", rootTaskId);\n const node = current.graph.nodes.find((entry) => entry.taskId === taskId);\n if (!node) throw new Error(`Task ${taskId} is not registered in graph ${rootTaskId}`);\n const task = await canonical(node);\n if (!task) throw new RecordNotFoundError(\"tasks\", taskId);\n if (!node.sealed && task.state !== \"queued\") {\n throw new Error(\n `Task ${taskId} must be queued for first execution admission; current state is ${task.state}`,\n );\n }\n if (!(await satisfied(current.graph, node))) {\n throw new Error(\n `Task ${taskId} dependencies must all be succeeded before execution admission`,\n );\n }\n if (node.sealed) return;\n const graph = taskGraphSchema.parse({\n ...current.graph,\n revision: current.graph.revision + 1,\n nodes: current.graph.nodes.map((entry) =>\n entry.taskId === taskId ? { ...entry, sealed: true } : entry,\n ),\n updatedAt: now(),\n });\n try {\n await driver.update({\n collection: \"taskGraphs\",\n id: rootTaskId,\n value: graph,\n expectedVersion: current.version,\n });\n return;\n } catch (error) {\n // An edge edit may have won: re-read its dependencies and validate them before sealing.\n if (!(error instanceof VersionConflictError)) throw error;\n }\n }\n },\n };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { TaskId } from "../schema/id.mjs";
|
|
2
|
+
import { Task } from "../schema/task.mjs";
|
|
3
|
+
import { SendTaskMessageInput, TaskMessage, TaskMessageClaim, TaskMessageDiscard, TaskMessageId, TaskMessagePrincipal, TaskMessageProcessing, TaskMessageProtocolBinding, TaskMessageProtocols } from "../schema/task-message.mjs";
|
|
4
|
+
import { WorkNotification } from "./work-notifications.mjs";
|
|
5
|
+
import { DateClock } from "../schema/common.mjs";
|
|
6
|
+
import { TaskAttemptRef } from "../schema/execution.mjs";
|
|
7
|
+
import { StoreDriver } from "./driver.mjs";
|
|
8
|
+
//#region src/store/task-messages.d.ts
|
|
9
|
+
/** Operation categories evaluated by the Task message authorization boundary. */
|
|
10
|
+
type TaskMessageAction = "send" | "send_result" | "read" | "claim" | "complete" | "discard";
|
|
11
|
+
/** Principal, requested action, and validated Task scope for one authorization decision. */
|
|
12
|
+
interface TaskMessageAuthorizationInput {
|
|
13
|
+
readonly principal: TaskMessagePrincipal;
|
|
14
|
+
readonly action: TaskMessageAction;
|
|
15
|
+
readonly fromTask?: Task;
|
|
16
|
+
readonly toTask: Task;
|
|
17
|
+
}
|
|
18
|
+
/** Trusted application authorization, NOT HTTP authentication. Only literal true grants access. */
|
|
19
|
+
type TaskMessageAuthorize = (input: TaskMessageAuthorizationInput) => boolean | Promise<boolean>;
|
|
20
|
+
/** Engine-owned dependencies and deny-by-default policy used to create a Task message store. */
|
|
21
|
+
interface CreateTaskMessageStoreOptions {
|
|
22
|
+
readonly driver: StoreDriver;
|
|
23
|
+
readonly now: DateClock;
|
|
24
|
+
/**
|
|
25
|
+
* Main engine supplies schema-validated snapshots. Task reads and inbox CAS are separate:
|
|
26
|
+
* cancellation/retry can race send, claim, and caller effects. The envelope retains expectAttempt;
|
|
27
|
+
* consumers must fence effects at their application boundary, not assume atomic cancellation here.
|
|
28
|
+
*/
|
|
29
|
+
readonly getTask: (id: TaskId) => Promise<Task | null>;
|
|
30
|
+
readonly protocols: TaskMessageProtocols;
|
|
31
|
+
readonly authorize: TaskMessageAuthorize;
|
|
32
|
+
readonly notify?: (event: WorkNotification) => Promise<void>;
|
|
33
|
+
}
|
|
34
|
+
/** Authenticated principal and immutable message intent used for one send operation. */
|
|
35
|
+
interface TaskMessageSendInput<Type extends TaskMessageProtocolBinding = TaskMessageProtocolBinding> {
|
|
36
|
+
readonly principal: TaskMessagePrincipal;
|
|
37
|
+
readonly message: SendTaskMessageInput<Type>;
|
|
38
|
+
}
|
|
39
|
+
/** Authenticated principal and recipient identifying one Task inbox. */
|
|
40
|
+
interface TaskMessageRecipientInput {
|
|
41
|
+
readonly principal: TaskMessagePrincipal;
|
|
42
|
+
readonly toTaskId: TaskId;
|
|
43
|
+
}
|
|
44
|
+
/** Authenticated principal, recipient, and immutable message identifying one inbox record. */
|
|
45
|
+
interface TaskMessageLookupInput extends TaskMessageRecipientInput {
|
|
46
|
+
readonly messageId: TaskMessageId;
|
|
47
|
+
}
|
|
48
|
+
/** Recipient attempt and bounded lease requested when claiming one Task message. */
|
|
49
|
+
interface ClaimTaskMessageInput extends Pick<TaskMessageRecipientInput, "principal"> {
|
|
50
|
+
readonly task: TaskAttemptRef;
|
|
51
|
+
readonly leaseMs: number;
|
|
52
|
+
readonly recoverCompleted?: boolean;
|
|
53
|
+
}
|
|
54
|
+
/** Authenticated lease and committed-effects attestation used to acknowledge one message. */
|
|
55
|
+
interface CompleteTaskMessageInput {
|
|
56
|
+
readonly principal: TaskMessagePrincipal;
|
|
57
|
+
readonly claim: TaskMessageClaim;
|
|
58
|
+
readonly effectsCommitted: true;
|
|
59
|
+
}
|
|
60
|
+
/** Authenticated lease whose current validity must be checked before an external effect. */
|
|
61
|
+
interface AssertTaskMessageClaimInput {
|
|
62
|
+
readonly principal: TaskMessagePrincipal;
|
|
63
|
+
readonly claim: TaskMessageClaim;
|
|
64
|
+
}
|
|
65
|
+
/** Authorized reason for discarding one exact pending inbox head. */
|
|
66
|
+
interface DiscardTaskMessageInput extends TaskMessageLookupInput {
|
|
67
|
+
readonly reason: string;
|
|
68
|
+
}
|
|
69
|
+
/** Message identity and stable operation name used to derive an external-effect key. */
|
|
70
|
+
interface TaskMessageEffectKeyInput {
|
|
71
|
+
readonly messageId: TaskMessageId;
|
|
72
|
+
readonly operation: string;
|
|
73
|
+
}
|
|
74
|
+
/** Message and lease returned when one pending inbox head is claimed. */
|
|
75
|
+
interface ClaimTaskMessageResult {
|
|
76
|
+
readonly claim: TaskMessageClaim;
|
|
77
|
+
readonly message: TaskMessage;
|
|
78
|
+
}
|
|
79
|
+
/** Trusted API for immutable Task messages, recipient inboxes, leases, and acknowledgments. */
|
|
80
|
+
interface TaskMessageStore {
|
|
81
|
+
send<const Type extends TaskMessageProtocolBinding>(input: TaskMessageSendInput<Type>): Promise<TaskMessage<Type>>;
|
|
82
|
+
/** Trusted workflow delivery after the source outcome is persisted; requires separate send_result authorization. */
|
|
83
|
+
sendResult<const Type extends TaskMessageProtocolBinding>(input: TaskMessageSendInput<Type>): Promise<TaskMessage<Type>>;
|
|
84
|
+
get(input: TaskMessageLookupInput): Promise<TaskMessage | null>;
|
|
85
|
+
pending(input: TaskMessageRecipientInput): Promise<readonly TaskMessage[]>;
|
|
86
|
+
/**
|
|
87
|
+
* A stale attempt-fenced source or cancelled ordinary source raises TaskMessageSourceFenceError
|
|
88
|
+
* and leaves the head pending. Protocols default to attempt fencing; explicitly task-scoped
|
|
89
|
+
* protocols let workflow triggers survive source retries. Authorized discard can unblock the inbox.
|
|
90
|
+
* Source completion alone does not invalidate previously admitted messages from the same attempt.
|
|
91
|
+
* recoverCompleted defaults to false. It is a trusted application recovery capability for a
|
|
92
|
+
* succeeded/failed recipient: the caller must first validate an exact persisted decision for
|
|
93
|
+
* the pending message, then verify the claimed message matches it. It authorizes no new effects,
|
|
94
|
+
* never permits cancellation recovery, and does not bypass the authorization callback.
|
|
95
|
+
*/
|
|
96
|
+
claim(input: ClaimTaskMessageInput): Promise<ClaimTaskMessageResult | null>;
|
|
97
|
+
/**
|
|
98
|
+
* Call only after effects commit. A matching recipient attempt may acknowledge in any state,
|
|
99
|
+
* including cancellation; acknowledgment authorizes no new effects. Leases are not exactly-once.
|
|
100
|
+
*/
|
|
101
|
+
complete(input: CompleteTaskMessageInput): Promise<TaskMessageProcessing>;
|
|
102
|
+
/** Read-only claim/complete-authorized lease snapshot check, not a lease extension or external effect fence. Completed claims are invalid. */
|
|
103
|
+
assertClaim(input: AssertTaskMessageClaimInput): Promise<void>;
|
|
104
|
+
/** Discard only the pending head with no unexpired recipient lease, under explicit discard authorization. */
|
|
105
|
+
discard(input: DiscardTaskMessageInput): Promise<void>;
|
|
106
|
+
discarded(input: TaskMessageLookupInput): Promise<TaskMessageDiscard | null>;
|
|
107
|
+
processing(input: TaskMessageLookupInput): Promise<TaskMessageProcessing | null>;
|
|
108
|
+
/** Pure key derivation, not a read or an authorization capability. */
|
|
109
|
+
effectKey(input: TaskMessageEffectKeyInput): string;
|
|
110
|
+
}
|
|
111
|
+
/** Base error for Task message admission, authorization, and processing failures. */
|
|
112
|
+
declare class TaskMessageError extends Error {}
|
|
113
|
+
/** Error raised when an idempotency key is reused with different message intent. */
|
|
114
|
+
declare class TaskMessageConflictError extends TaskMessageError {}
|
|
115
|
+
/** Error raised when a configured Task message policy does not grant an operation. */
|
|
116
|
+
declare class TaskMessageAuthorizationError extends TaskMessageError {}
|
|
117
|
+
/** Error raised when a Task inbox claim is expired, replaced, or otherwise invalid. */
|
|
118
|
+
declare class TaskMessageLeaseError extends TaskMessageError {}
|
|
119
|
+
/** Error raised when an inbox head belongs to a stale or cancelled source attempt. */
|
|
120
|
+
declare class TaskMessageSourceFenceError extends TaskMessageError {
|
|
121
|
+
readonly messageId: TaskMessageId;
|
|
122
|
+
readonly source: TaskAttemptRef;
|
|
123
|
+
readonly currentAttempt: number;
|
|
124
|
+
readonly sourceState: Task["state"];
|
|
125
|
+
constructor(messageId: TaskMessageId, source: TaskAttemptRef, currentAttempt: number, sourceState: Task["state"]);
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Authorizes recipient-local operations and direct parent-child sends for a trusted
|
|
129
|
+
* Task-bound principal while denying result delivery and discard.
|
|
130
|
+
*/
|
|
131
|
+
declare const taskBoundMessagePolicy: TaskMessageAuthorize;
|
|
132
|
+
/** Creates the trusted, CAS-backed Task message and recipient-inbox store. */
|
|
133
|
+
declare function createTaskMessageStore(options: CreateTaskMessageStoreOptions): TaskMessageStore;
|
|
134
|
+
//#endregion
|
|
135
|
+
export { AssertTaskMessageClaimInput, ClaimTaskMessageInput, ClaimTaskMessageResult, CompleteTaskMessageInput, CreateTaskMessageStoreOptions, DiscardTaskMessageInput, TaskMessageAction, TaskMessageAuthorizationError, TaskMessageAuthorizationInput, TaskMessageAuthorize, TaskMessageConflictError, TaskMessageEffectKeyInput, TaskMessageError, TaskMessageLeaseError, TaskMessageLookupInput, TaskMessageRecipientInput, TaskMessageSendInput, TaskMessageSourceFenceError, TaskMessageStore, createTaskMessageStore, taskBoundMessagePolicy };
|
|
136
|
+
//# sourceMappingURL=task-messages.d.mts.map
|