@vercel/factory 0.0.14 → 0.0.16
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 +356 -0
- package/README.md +96 -2
- package/dist/agent-routes.d.mts +113 -0
- package/dist/agent-routes.mjs +91 -0
- package/dist/agent-routes.mjs.map +1 -0
- package/dist/api-contracts.d.mts +1768 -0
- package/dist/api-contracts.mjs +119 -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 +111 -216
- package/dist/api.mjs +1063 -99
- package/dist/api.mjs.map +1 -1
- 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 +698 -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 +84 -12
- package/dist/blob/index.mjs +179 -45
- package/dist/blob/index.mjs.map +1 -1
- package/dist/budget.d.mts +115 -0
- package/dist/budget.mjs +147 -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 +7 -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 +37 -0
- package/dist/change-verification/dispatch.mjs +83 -0
- package/dist/change-verification/dispatch.mjs.map +1 -0
- package/dist/change-verification/eve-tool.d.mts +61 -0
- package/dist/change-verification/eve-tool.mjs +89 -0
- package/dist/change-verification/eve-tool.mjs.map +1 -0
- package/dist/change-verification/result.mjs +130 -0
- package/dist/change-verification/result.mjs.map +1 -0
- package/dist/changes/eve-record-change.d.mts +56 -0
- package/dist/changes/eve-record-change.mjs +131 -0
- package/dist/changes/eve-record-change.mjs.map +1 -0
- package/dist/changes/presentation.d.mts +14 -0
- package/dist/changes.d.mts +24 -0
- package/dist/changes.mjs +29 -0
- package/dist/changes.mjs.map +1 -0
- package/dist/client-events.d.mts +25 -0
- package/dist/client-events.mjs +45 -0
- package/dist/client-events.mjs.map +1 -0
- package/dist/client-stream.d.mts +28 -0
- package/dist/client-stream.mjs +210 -0
- package/dist/client-stream.mjs.map +1 -0
- package/dist/client-transcript.d.mts +41 -0
- package/dist/client-transcript.mjs +233 -0
- package/dist/client-transcript.mjs.map +1 -0
- package/dist/client.d.mts +343 -0
- package/dist/client.mjs +366 -0
- package/dist/client.mjs.map +1 -0
- package/dist/code-review/contracts.d.mts +27 -0
- package/dist/code-review/eve-post-review.d.mts +70 -0
- package/dist/code-review/eve-post-review.mjs +195 -0
- package/dist/code-review/eve-post-review.mjs.map +1 -0
- package/dist/code-review/eve-review-comments.d.mts +75 -0
- package/dist/code-review/eve-review-comments.mjs +193 -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 +46 -0
- package/dist/code-review/github-reporter.mjs +262 -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 +7 -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 -3
- package/dist/connectors/index.mjs +3 -52
- 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 -52
- package/dist/connectors/slack.mjs +13 -85
- package/dist/connectors/slack.mjs.map +1 -1
- package/dist/deepsec/eve-tool.d.mts +110 -0
- package/dist/deepsec/eve-tool.mjs +166 -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 +152 -0
- package/dist/dispatch.mjs +199 -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 +209 -1277
- package/dist/eve/index.mjs +312 -1073
- package/dist/eve/index.mjs.map +1 -1
- package/dist/eve/invoke.d.mts +144 -0
- package/dist/eve/invoke.mjs +254 -0
- package/dist/eve/invoke.mjs.map +1 -0
- package/dist/eve/session-client.d.mts +186 -0
- package/dist/eve/session-client.mjs +245 -0
- package/dist/eve/session-client.mjs.map +1 -0
- package/dist/eve/task-execution.d.mts +380 -0
- package/dist/eve/task-execution.mjs +73 -0
- package/dist/eve/task-execution.mjs.map +1 -0
- package/dist/eve/task-session.d.mts +129 -0
- package/dist/eve/task-session.mjs +245 -0
- package/dist/eve/task-session.mjs.map +1 -0
- package/dist/eve/transcript.d.mts +37 -0
- package/dist/eve/transcript.mjs +69 -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 +183 -0
- package/dist/execution.mjs +223 -0
- package/dist/execution.mjs.map +1 -0
- package/dist/finding-remediation/admission.d.mts +53 -0
- package/dist/finding-remediation/admission.mjs +90 -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 +7 -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 +199 -0
- package/dist/github-publication.mjs +442 -0
- package/dist/github-publication.mjs.map +1 -0
- package/dist/github-transfer.d.mts +81 -0
- package/dist/github-transfer.mjs +273 -0
- package/dist/github-transfer.mjs.map +1 -0
- package/dist/github.d.mts +446 -2
- package/dist/github.mjs +357 -56
- 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/inbox-activity.d.mts +53 -0
- package/dist/inbox-activity.mjs +41 -0
- package/dist/inbox-activity.mjs.map +1 -0
- package/dist/index.d.mts +16 -484
- package/dist/index.mjs +15 -522
- package/dist/intake-contracts.d.mts +55 -0
- package/dist/intake-contracts.mjs +33 -0
- package/dist/intake-contracts.mjs.map +1 -0
- package/dist/integrations/deepsec.d.mts +5 -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 +5 -0
- package/dist/integrations/slack.mjs +5 -0
- package/dist/integrations/vercel.d.mts +5 -0
- package/dist/integrations/vercel.mjs +4 -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 +47 -0
- package/dist/merge-resolution/eve-tools.mjs +122 -0
- package/dist/merge-resolution/eve-tools.mjs.map +1 -0
- package/dist/model-settings.d.mts +41 -0
- package/dist/model-settings.mjs +35 -0
- package/dist/model-settings.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 +279 -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 +48 -1
- package/dist/postgres/index.mjs +45 -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 +56 -0
- package/dist/presets/software-development/dispatch.mjs +67 -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 +24 -0
- package/dist/presets/software-development/task-communication.mjs +157 -0
- package/dist/presets/software-development/task-communication.mjs.map +1 -0
- package/dist/presets/software-development.d.mts +10 -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 +67 -0
- package/dist/pull-requests/github-publisher.mjs +405 -0
- package/dist/pull-requests/github-publisher.mjs.map +1 -0
- package/dist/pull-requests.d.mts +6 -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 +10 -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 +40 -0
- package/dist/schema/agent-route.mjs +33 -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-BLIkCVue.mjs → schema/common.mjs} +7 -16
- package/dist/schema/common.mjs.map +1 -0
- package/dist/schema/communication.d.mts +52 -0
- package/dist/{communication-Kd9nU5RP.mjs → schema/communication.mjs} +8 -3
- 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 +122 -0
- package/dist/schema/factory-config.mjs +90 -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 +30 -0
- package/dist/schema/repository.mjs +29 -0
- package/dist/schema/repository.mjs.map +1 -0
- package/dist/schema/session.d.mts +136 -0
- package/dist/schema/session.mjs +54 -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/slack-pr-notifications.d.mts +12 -0
- package/dist/schema/slack-pr-notifications.mjs +11 -0
- package/dist/schema/slack-pr-notifications.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 +3173 -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 +273 -0
- package/dist/schema/task.mjs +200 -0
- package/dist/schema/task.mjs.map +1 -0
- package/dist/schema/transcript.d.mts +176 -0
- package/dist/schema/transcript.mjs +87 -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 +150 -0
- package/dist/schema/work.mjs +102 -0
- package/dist/schema/work.mjs.map +1 -0
- package/dist/session-previews.d.mts +76 -0
- package/dist/session-previews.mjs +55 -0
- package/dist/session-previews.mjs.map +1 -0
- package/dist/session-review.d.mts +120 -0
- package/dist/session-review.mjs +79 -0
- package/dist/session-review.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 +20 -0
- package/dist/signals.mjs +19 -0
- package/dist/signals.mjs.map +1 -0
- package/dist/stall.d.mts +41 -0
- package/dist/stall.mjs +83 -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 +683 -0
- package/dist/store/engine.mjs +2309 -0
- package/dist/store/engine.mjs.map +1 -0
- package/dist/store/memory.d.mts +24 -0
- package/dist/store/memory.mjs +69 -0
- package/dist/store/memory.mjs.map +1 -0
- package/dist/store/slack-pr-notifications.d.mts +44 -0
- package/dist/store/slack-pr-notifications.mjs +121 -0
- package/dist/store/slack-pr-notifications.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 +180 -0
- package/dist/store/task-work.mjs +137 -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 +79 -0
- package/dist/sweep.mjs +171 -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 +267 -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 +53 -0
- package/dist/vercel-git.mjs +344 -0
- package/dist/vercel-git.mjs.map +1 -0
- package/dist/vercel-github-api.d.mts +103 -0
- package/dist/vercel-github-api.mjs +363 -0
- package/dist/vercel-github-api.mjs.map +1 -0
- package/dist/vercel.d.mts +34 -2
- package/dist/vercel.mjs +49 -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 +4 -0
- package/dist/work-triage.mjs +3 -0
- package/dist/workflows.d.mts +146 -0
- package/dist/workflows.mjs +139 -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-files-git.d.mts +15 -0
- package/dist/workspace-files-git.mjs +61 -0
- package/dist/workspace-files-git.mjs.map +1 -0
- package/dist/workspace-files.d.mts +107 -0
- package/dist/workspace-files.mjs +74 -0
- package/dist/workspace-files.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/docs/getting-started.md +104 -0
- package/docs/index.md +100 -0
- package/docs/recipes/cancellation.md +215 -0
- package/docs/recipes/custom-workflow.md +153 -0
- package/docs/recipes/dependent-tasks.md +207 -0
- package/docs/recipes/eve-agent.md +277 -0
- package/docs/recipes/human-input.md +204 -0
- package/docs/recipes/persistence-recovery.md +268 -0
- package/docs/recipes/retry-recovery.md +241 -0
- package/docs/recipes/task-messaging.md +215 -0
- package/docs/recipes/typed-eve-result.md +161 -0
- package/docs/runtime-integration.md +137 -0
- package/package.json +142 -46
- package/dist/approval-JX2Px9IC.mjs +0 -132
- package/dist/approval-JX2Px9IC.mjs.map +0 -1
- package/dist/budget-DzSiIA4t.mjs +0 -42
- package/dist/budget-DzSiIA4t.mjs.map +0 -1
- package/dist/change-D2U6v7Ek.mjs +0 -554
- package/dist/change-D2U6v7Ek.mjs.map +0 -1
- package/dist/communication-Kd9nU5RP.mjs.map +0 -1
- package/dist/coordination-Cq08hb1l.mjs +0 -76
- package/dist/coordination-Cq08hb1l.mjs.map +0 -1
- package/dist/dispatch-BaZS3PT8.d.mts +0 -76
- package/dist/driver-BC0Mt5KG.d.mts +0 -34
- package/dist/driver-Bbd25rNM.mjs +0 -23
- package/dist/driver-Bbd25rNM.mjs.map +0 -1
- package/dist/engine-DFHU2Pe-.d.mts +0 -781
- package/dist/engine-WaUM2cGA.mjs +0 -1068
- package/dist/engine-WaUM2cGA.mjs.map +0 -1
- package/dist/factory-config-BoRXOw43.mjs +0 -50
- package/dist/factory-config-BoRXOw43.mjs.map +0 -1
- package/dist/factory-config-D2yU3G0c.d.mts +0 -58
- package/dist/github-C5HNBN6p.d.mts +0 -279
- package/dist/id-BLIkCVue.mjs.map +0 -1
- package/dist/index-Bw6hggoH.d.mts +0 -40
- package/dist/index.mjs.map +0 -1
- package/dist/task-CcOyOJ3k.mjs +0 -154
- package/dist/task-CcOyOJ3k.mjs.map +0 -1
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { RepositorySlug } from "../schema/repository.mjs";
|
|
2
|
+
import { StoreDriver } from "./driver.mjs";
|
|
3
|
+
import { SlackPrNotifications } from "../schema/slack-pr-notifications.mjs";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
//#region src/store/slack-pr-notifications.d.ts
|
|
6
|
+
declare const commandSchema: z.ZodObject<{
|
|
7
|
+
operationId: z.ZodString;
|
|
8
|
+
actor: z.ZodString;
|
|
9
|
+
preference: z.ZodUnion<readonly [z.ZodReadonly<z.ZodObject<{
|
|
10
|
+
channelId: z.ZodString;
|
|
11
|
+
mentionUserIds: z.ZodReadonly<z.ZodArray<z.ZodString>>;
|
|
12
|
+
}, z.core.$strict>>, z.ZodNull, z.ZodLiteral<"config">]>;
|
|
13
|
+
}, z.core.$strict>;
|
|
14
|
+
/** Effective PR notification settings and the source of the current choice. */
|
|
15
|
+
interface SlackPrNotificationSnapshot {
|
|
16
|
+
readonly revision: number;
|
|
17
|
+
readonly source: "config" | "slack";
|
|
18
|
+
readonly settings: SlackPrNotifications | null;
|
|
19
|
+
readonly updatedBy?: string;
|
|
20
|
+
readonly updatedAt?: string;
|
|
21
|
+
}
|
|
22
|
+
/** A trusted Slack command: an object enables notifications, null disables them, and config resets. */
|
|
23
|
+
type UpdateSlackPrNotificationsInput = z.infer<typeof commandSchema>;
|
|
24
|
+
/** Durable PR notification preferences scoped to a connector, workspace, and optional repository. */
|
|
25
|
+
interface SlackPrNotificationStore {
|
|
26
|
+
get(): Promise<SlackPrNotificationSnapshot>;
|
|
27
|
+
/** The caller authenticates the operator; operationId must identify the original Slack message. */
|
|
28
|
+
update(input: UpdateSlackPrNotificationsInput): Promise<SlackPrNotificationSnapshot>;
|
|
29
|
+
}
|
|
30
|
+
/** Persistence scope and Git-configured fallback for PR notification preferences. */
|
|
31
|
+
interface SlackPrNotificationStoreOptions {
|
|
32
|
+
readonly driver: StoreDriver;
|
|
33
|
+
readonly connector: string;
|
|
34
|
+
readonly workspaceId: string;
|
|
35
|
+
/** Omit for the factory-wide default; supply a slug to isolate one repository's preferences. */
|
|
36
|
+
readonly repository?: RepositorySlug;
|
|
37
|
+
readonly defaults?: SlackPrNotifications | null;
|
|
38
|
+
readonly now?: () => string;
|
|
39
|
+
}
|
|
40
|
+
/** Creates directly addressed settings with immutable command intents and compare-and-swap updates. */
|
|
41
|
+
declare function createSlackPrNotificationStore({ driver, connector, workspaceId, repository, defaults, now }: SlackPrNotificationStoreOptions): SlackPrNotificationStore;
|
|
42
|
+
//#endregion
|
|
43
|
+
export { SlackPrNotificationSnapshot, SlackPrNotificationStore, SlackPrNotificationStoreOptions, UpdateSlackPrNotificationsInput, createSlackPrNotificationStore };
|
|
44
|
+
//# sourceMappingURL=slack-pr-notifications.d.mts.map
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { repositorySlugSchema } from "../schema/repository.mjs";
|
|
2
|
+
import { slackPrNotificationsSchema } from "../schema/slack-pr-notifications.mjs";
|
|
3
|
+
import { createHash } from "node:crypto";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
//#region src/store/slack-pr-notifications.ts
|
|
6
|
+
const preferenceSchema = z.union([
|
|
7
|
+
slackPrNotificationsSchema,
|
|
8
|
+
z.null(),
|
|
9
|
+
z.literal("config")
|
|
10
|
+
]);
|
|
11
|
+
const commandSchema = z.strictObject({
|
|
12
|
+
operationId: z.string().min(1).max(512).describe("Stable identity of the authenticated Slack message."),
|
|
13
|
+
actor: z.string().regex(/^slack:T[A-Z0-9]+:[UW][A-Z0-9]+$/u).describe("Authenticated Slack operator making this choice."),
|
|
14
|
+
preference: preferenceSchema.describe("Destination override, null to disable, or config to restore the file default.")
|
|
15
|
+
});
|
|
16
|
+
const recordSchema = commandSchema.extend({
|
|
17
|
+
schemaVersion: z.literal(1).describe("Persisted settings format version."),
|
|
18
|
+
expectedRevision: z.number().int().nonnegative().describe("Settings revision observed when this command was first recorded."),
|
|
19
|
+
updatedAt: z.iso.datetime().describe("Time the command was first recorded.")
|
|
20
|
+
});
|
|
21
|
+
function isConflict(error, ...names) {
|
|
22
|
+
return error instanceof Error && names.includes(error.name);
|
|
23
|
+
}
|
|
24
|
+
/** Creates directly addressed settings with immutable command intents and compare-and-swap updates. */
|
|
25
|
+
function createSlackPrNotificationStore({ driver, connector, workspaceId, repository, defaults, now = () => (/* @__PURE__ */ new Date()).toISOString() }) {
|
|
26
|
+
z.string().regex(/^slack\/[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/u).parse(connector);
|
|
27
|
+
z.string().regex(/^T[A-Z0-9]+$/u).parse(workspaceId);
|
|
28
|
+
if (repository !== void 0) repositorySlugSchema.parse(repository);
|
|
29
|
+
const fallback = defaults == null ? null : slackPrNotificationsSchema.parse(defaults);
|
|
30
|
+
const hash = (value) => createHash("sha256").update(value).digest("hex");
|
|
31
|
+
const id = `slack-pr-${hash(JSON.stringify(repository === void 0 ? [connector, workspaceId] : [
|
|
32
|
+
connector,
|
|
33
|
+
workspaceId,
|
|
34
|
+
repository
|
|
35
|
+
]))}`;
|
|
36
|
+
const collection = "communicationSettings";
|
|
37
|
+
async function read() {
|
|
38
|
+
const record = await driver.get(collection, id);
|
|
39
|
+
return record === null ? null : {
|
|
40
|
+
version: record.version,
|
|
41
|
+
value: recordSchema.parse(record.value)
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
function snapshot(record) {
|
|
45
|
+
const preference = record === null ? "config" : record.value.preference;
|
|
46
|
+
return {
|
|
47
|
+
revision: record?.version ?? 0,
|
|
48
|
+
source: preference === "config" ? "config" : "slack",
|
|
49
|
+
settings: preference === "config" ? fallback : preference,
|
|
50
|
+
...record && {
|
|
51
|
+
updatedBy: record.value.actor,
|
|
52
|
+
updatedAt: record.value.updatedAt
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
async function update(value) {
|
|
57
|
+
const input = commandSchema.parse(value);
|
|
58
|
+
if (!input.actor.startsWith(`slack:${workspaceId}:`)) throw new Error("PR notification operator belongs to another Slack workspace.");
|
|
59
|
+
const commandId = `${id}-command-${hash(input.operationId)}`;
|
|
60
|
+
let command = await driver.get(collection, commandId);
|
|
61
|
+
if (command === null) {
|
|
62
|
+
const current = await read();
|
|
63
|
+
const intent = recordSchema.parse({
|
|
64
|
+
...input,
|
|
65
|
+
schemaVersion: 1,
|
|
66
|
+
expectedRevision: current?.version ?? 0,
|
|
67
|
+
updatedAt: now()
|
|
68
|
+
});
|
|
69
|
+
try {
|
|
70
|
+
await driver.insert({
|
|
71
|
+
collection,
|
|
72
|
+
id: commandId,
|
|
73
|
+
value: intent
|
|
74
|
+
});
|
|
75
|
+
} catch (error) {
|
|
76
|
+
if (!isConflict(error, "RecordExistsError")) throw error;
|
|
77
|
+
}
|
|
78
|
+
command = await driver.get(collection, commandId);
|
|
79
|
+
}
|
|
80
|
+
if (command === null) throw new Error("PR notification command was not saved.");
|
|
81
|
+
const intent = recordSchema.parse(command.value);
|
|
82
|
+
if (JSON.stringify({
|
|
83
|
+
operationId: intent.operationId,
|
|
84
|
+
actor: intent.actor,
|
|
85
|
+
preference: intent.preference
|
|
86
|
+
}) !== JSON.stringify(input)) throw new Error("This Slack message already requested different PR notification settings.");
|
|
87
|
+
const current = await read();
|
|
88
|
+
if (current?.value.operationId === input.operationId) return snapshot(current);
|
|
89
|
+
if ((current?.version ?? 0) !== intent.expectedRevision) throw new Error("PR notification settings changed after this command. Send a new Slack message to change them again.");
|
|
90
|
+
try {
|
|
91
|
+
if (current === null) await driver.insert({
|
|
92
|
+
collection,
|
|
93
|
+
id,
|
|
94
|
+
value: intent
|
|
95
|
+
});
|
|
96
|
+
else await driver.update({
|
|
97
|
+
collection,
|
|
98
|
+
id,
|
|
99
|
+
value: intent,
|
|
100
|
+
expectedVersion: current.version
|
|
101
|
+
});
|
|
102
|
+
} catch (error) {
|
|
103
|
+
if (!isConflict(error, "RecordExistsError", "VersionConflictError")) throw error;
|
|
104
|
+
const winner = await read();
|
|
105
|
+
if (winner?.value.operationId === input.operationId) return snapshot(winner);
|
|
106
|
+
throw new Error("Another command changed PR notification settings. Send a new Slack message to change them again.", { cause: error });
|
|
107
|
+
}
|
|
108
|
+
return snapshot({
|
|
109
|
+
value: intent,
|
|
110
|
+
version: intent.expectedRevision + 1
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
return {
|
|
114
|
+
get: async () => snapshot(await read()),
|
|
115
|
+
update
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
//#endregion
|
|
119
|
+
export { createSlackPrNotificationStore };
|
|
120
|
+
|
|
121
|
+
//# sourceMappingURL=slack-pr-notifications.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slack-pr-notifications.mjs","names":[],"sources":["../../src/store/slack-pr-notifications.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { z } from \"zod\";\nimport {\n slackPrNotificationsSchema,\n type SlackPrNotifications,\n} from \"../schema/slack-pr-notifications\";\nimport type { StoreDriver } from \"./driver\";\nimport { repositorySlugSchema, type RepositorySlug } from \"../schema/repository\";\n\nconst preferenceSchema = z.union([slackPrNotificationsSchema, z.null(), z.literal(\"config\")]);\nconst commandSchema = z.strictObject({\n operationId: z\n .string()\n .min(1)\n .max(512)\n .describe(\"Stable identity of the authenticated Slack message.\"),\n actor: z\n .string()\n .regex(/^slack:T[A-Z0-9]+:[UW][A-Z0-9]+$/u)\n .describe(\"Authenticated Slack operator making this choice.\"),\n preference: preferenceSchema.describe(\n \"Destination override, null to disable, or config to restore the file default.\",\n ),\n});\nconst recordSchema = commandSchema.extend({\n schemaVersion: z.literal(1).describe(\"Persisted settings format version.\"),\n expectedRevision: z\n .number()\n .int()\n .nonnegative()\n .describe(\"Settings revision observed when this command was first recorded.\"),\n updatedAt: z.iso.datetime().describe(\"Time the command was first recorded.\"),\n});\n\n// Drivers loaded through separate package entry points have distinct error constructors.\nfunction isConflict(error: unknown, ...names: string[]): boolean {\n return error instanceof Error && names.includes(error.name);\n}\n\n/** Effective PR notification settings and the source of the current choice. */\nexport interface SlackPrNotificationSnapshot {\n readonly revision: number;\n readonly source: \"config\" | \"slack\";\n readonly settings: SlackPrNotifications | null;\n readonly updatedBy?: string;\n readonly updatedAt?: string;\n}\n\n/** A trusted Slack command: an object enables notifications, null disables them, and config resets. */\nexport type UpdateSlackPrNotificationsInput = z.infer<typeof commandSchema>;\n\n/** Durable PR notification preferences scoped to a connector, workspace, and optional repository. */\nexport interface SlackPrNotificationStore {\n get(): Promise<SlackPrNotificationSnapshot>;\n /** The caller authenticates the operator; operationId must identify the original Slack message. */\n update(input: UpdateSlackPrNotificationsInput): Promise<SlackPrNotificationSnapshot>;\n}\n\n/** Persistence scope and Git-configured fallback for PR notification preferences. */\nexport interface SlackPrNotificationStoreOptions {\n readonly driver: StoreDriver;\n readonly connector: string;\n readonly workspaceId: string;\n /** Omit for the factory-wide default; supply a slug to isolate one repository's preferences. */\n readonly repository?: RepositorySlug;\n readonly defaults?: SlackPrNotifications | null;\n readonly now?: () => string;\n}\n\n/** Creates directly addressed settings with immutable command intents and compare-and-swap updates. */\nexport function createSlackPrNotificationStore({\n driver,\n connector,\n workspaceId,\n repository,\n defaults,\n now = () => new Date().toISOString(),\n}: SlackPrNotificationStoreOptions): SlackPrNotificationStore {\n z.string()\n .regex(/^slack\\/[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/u)\n .parse(connector);\n z.string()\n .regex(/^T[A-Z0-9]+$/u)\n .parse(workspaceId);\n if (repository !== undefined) repositorySlugSchema.parse(repository);\n const fallback = defaults == null ? null : slackPrNotificationsSchema.parse(defaults);\n const hash = (value: string) => createHash(\"sha256\").update(value).digest(\"hex\");\n const scope =\n repository === undefined ? [connector, workspaceId] : [connector, workspaceId, repository];\n const id = `slack-pr-${hash(JSON.stringify(scope))}`;\n const collection = \"communicationSettings\";\n\n async function read() {\n const record = await driver.get(collection, id);\n return record === null\n ? null\n : { version: record.version, value: recordSchema.parse(record.value) };\n }\n\n function snapshot(record: Awaited<ReturnType<typeof read>>): SlackPrNotificationSnapshot {\n const preference = record === null ? \"config\" : record.value.preference;\n return {\n revision: record?.version ?? 0,\n source: preference === \"config\" ? \"config\" : \"slack\",\n settings: preference === \"config\" ? fallback : preference,\n ...(record && { updatedBy: record.value.actor, updatedAt: record.value.updatedAt }),\n };\n }\n\n async function update(value: UpdateSlackPrNotificationsInput) {\n const input = commandSchema.parse(value);\n if (!input.actor.startsWith(`slack:${workspaceId}:`))\n throw new Error(\"PR notification operator belongs to another Slack workspace.\");\n const commandId = `${id}-command-${hash(input.operationId)}`;\n let command = await driver.get(collection, commandId);\n if (command === null) {\n const current = await read();\n const intent = recordSchema.parse({\n ...input,\n schemaVersion: 1,\n expectedRevision: current?.version ?? 0,\n updatedAt: now(),\n });\n try {\n await driver.insert({ collection, id: commandId, value: intent });\n } catch (error) {\n if (!isConflict(error, \"RecordExistsError\")) throw error;\n }\n command = await driver.get(collection, commandId);\n }\n if (command === null) throw new Error(\"PR notification command was not saved.\");\n const intent = recordSchema.parse(command.value);\n if (\n JSON.stringify({\n operationId: intent.operationId,\n actor: intent.actor,\n preference: intent.preference,\n }) !== JSON.stringify(input)\n )\n throw new Error(\"This Slack message already requested different PR notification settings.\");\n\n const current = await read();\n if (current?.value.operationId === input.operationId) return snapshot(current);\n if ((current?.version ?? 0) !== intent.expectedRevision)\n throw new Error(\n \"PR notification settings changed after this command. Send a new Slack message to change them again.\",\n );\n try {\n if (current === null) await driver.insert({ collection, id, value: intent });\n else await driver.update({ collection, id, value: intent, expectedVersion: current.version });\n } catch (error) {\n if (!isConflict(error, \"RecordExistsError\", \"VersionConflictError\")) throw error;\n const winner = await read();\n if (winner?.value.operationId === input.operationId) return snapshot(winner);\n throw new Error(\n \"Another command changed PR notification settings. Send a new Slack message to change them again.\",\n { cause: error },\n );\n }\n return snapshot({ value: intent, version: intent.expectedRevision + 1 });\n }\n\n return { get: async () => snapshot(await read()), update };\n}\n"],"mappings":";;;;;AASA,MAAM,mBAAmB,EAAE,MAAM;CAAC;CAA4B,EAAE,KAAK;CAAG,EAAE,QAAQ,QAAQ;AAAC,CAAC;AAC5F,MAAM,gBAAgB,EAAE,aAAa;CACnC,aAAa,EACV,OAAO,CAAC,CACR,IAAI,CAAC,CAAC,CACN,IAAI,GAAG,CAAC,CACR,SAAS,qDAAqD;CACjE,OAAO,EACJ,OAAO,CAAC,CACR,MAAM,mCAAmC,CAAC,CAC1C,SAAS,kDAAkD;CAC9D,YAAY,iBAAiB,SAC3B,+EACF;AACF,CAAC;AACD,MAAM,eAAe,cAAc,OAAO;CACxC,eAAe,EAAE,QAAQ,CAAC,CAAC,CAAC,SAAS,oCAAoC;CACzE,kBAAkB,EACf,OAAO,CAAC,CACR,IAAI,CAAC,CACL,YAAY,CAAC,CACb,SAAS,kEAAkE;CAC9E,WAAW,EAAE,IAAI,SAAS,CAAC,CAAC,SAAS,sCAAsC;AAC7E,CAAC;AAGD,SAAS,WAAW,OAAgB,GAAG,OAA0B;CAC/D,OAAO,iBAAiB,SAAS,MAAM,SAAS,MAAM,IAAI;AAC5D;;AAiCA,SAAgB,+BAA+B,EAC7C,QACA,WACA,aACA,YACA,UACA,6BAAY,IAAI,KAAK,EAAA,CAAE,YAAY,KACyB;CAC5D,EAAE,OAAO,CAAC,CACP,MAAM,2CAA2C,CAAC,CAClD,MAAM,SAAS;CAClB,EAAE,OAAO,CAAC,CACP,MAAM,eAAe,CAAC,CACtB,MAAM,WAAW;CACpB,IAAI,eAAe,KAAA,GAAW,qBAAqB,MAAM,UAAU;CACnE,MAAM,WAAW,YAAY,OAAO,OAAO,2BAA2B,MAAM,QAAQ;CACpF,MAAM,QAAQ,UAAkB,WAAW,QAAQ,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,OAAO,KAAK;CAG/E,MAAM,KAAK,YAAY,KAAK,KAAK,UAD/B,eAAe,KAAA,IAAY,CAAC,WAAW,WAAW,IAAI;EAAC;EAAW;EAAa;CAAU,CAC3C,CAAC;CACjD,MAAM,aAAa;CAEnB,eAAe,OAAO;EACpB,MAAM,SAAS,MAAM,OAAO,IAAI,YAAY,EAAE;EAC9C,OAAO,WAAW,OACd,OACA;GAAE,SAAS,OAAO;GAAS,OAAO,aAAa,MAAM,OAAO,KAAK;EAAE;CACzE;CAEA,SAAS,SAAS,QAAuE;EACvF,MAAM,aAAa,WAAW,OAAO,WAAW,OAAO,MAAM;EAC7D,OAAO;GACL,UAAU,QAAQ,WAAW;GAC7B,QAAQ,eAAe,WAAW,WAAW;GAC7C,UAAU,eAAe,WAAW,WAAW;GAC/C,GAAI,UAAU;IAAE,WAAW,OAAO,MAAM;IAAO,WAAW,OAAO,MAAM;GAAU;EACnF;CACF;CAEA,eAAe,OAAO,OAAwC;EAC5D,MAAM,QAAQ,cAAc,MAAM,KAAK;EACvC,IAAI,CAAC,MAAM,MAAM,WAAW,SAAS,YAAY,EAAE,GACjD,MAAM,IAAI,MAAM,8DAA8D;EAChF,MAAM,YAAY,GAAG,GAAG,WAAW,KAAK,MAAM,WAAW;EACzD,IAAI,UAAU,MAAM,OAAO,IAAI,YAAY,SAAS;EACpD,IAAI,YAAY,MAAM;GACpB,MAAM,UAAU,MAAM,KAAK;GAC3B,MAAM,SAAS,aAAa,MAAM;IAChC,GAAG;IACH,eAAe;IACf,kBAAkB,SAAS,WAAW;IACtC,WAAW,IAAI;GACjB,CAAC;GACD,IAAI;IACF,MAAM,OAAO,OAAO;KAAE;KAAY,IAAI;KAAW,OAAO;IAAO,CAAC;GAClE,SAAS,OAAO;IACd,IAAI,CAAC,WAAW,OAAO,mBAAmB,GAAG,MAAM;GACrD;GACA,UAAU,MAAM,OAAO,IAAI,YAAY,SAAS;EAClD;EACA,IAAI,YAAY,MAAM,MAAM,IAAI,MAAM,wCAAwC;EAC9E,MAAM,SAAS,aAAa,MAAM,QAAQ,KAAK;EAC/C,IACE,KAAK,UAAU;GACb,aAAa,OAAO;GACpB,OAAO,OAAO;GACd,YAAY,OAAO;EACrB,CAAC,MAAM,KAAK,UAAU,KAAK,GAE3B,MAAM,IAAI,MAAM,0EAA0E;EAE5F,MAAM,UAAU,MAAM,KAAK;EAC3B,IAAI,SAAS,MAAM,gBAAgB,MAAM,aAAa,OAAO,SAAS,OAAO;EAC7E,KAAK,SAAS,WAAW,OAAO,OAAO,kBACrC,MAAM,IAAI,MACR,qGACF;EACF,IAAI;GACF,IAAI,YAAY,MAAM,MAAM,OAAO,OAAO;IAAE;IAAY;IAAI,OAAO;GAAO,CAAC;QACtE,MAAM,OAAO,OAAO;IAAE;IAAY;IAAI,OAAO;IAAQ,iBAAiB,QAAQ;GAAQ,CAAC;EAC9F,SAAS,OAAO;GACd,IAAI,CAAC,WAAW,OAAO,qBAAqB,sBAAsB,GAAG,MAAM;GAC3E,MAAM,SAAS,MAAM,KAAK;GAC1B,IAAI,QAAQ,MAAM,gBAAgB,MAAM,aAAa,OAAO,SAAS,MAAM;GAC3E,MAAM,IAAI,MACR,oGACA,EAAE,OAAO,MAAM,CACjB;EACF;EACA,OAAO,SAAS;GAAE,OAAO;GAAQ,SAAS,OAAO,mBAAmB;EAAE,CAAC;CACzE;CAEA,OAAO;EAAE,KAAK,YAAY,SAAS,MAAM,KAAK,CAAC;EAAG;CAAO;AAC3D"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { TaskId } from "../schema/id.mjs";
|
|
2
|
+
import { Task } from "../schema/task.mjs";
|
|
3
|
+
import { IsoDateTimeClock } from "../schema/common.mjs";
|
|
4
|
+
import { StoreDriver } from "./driver.mjs";
|
|
5
|
+
import { TaskEffect, TaskEffectKey, TaskEffectReference } from "../schema/task-effect.mjs";
|
|
6
|
+
//#region src/store/task-effects.d.ts
|
|
7
|
+
/** Persistence, clock, and Task lookup dependencies for the Task effect store. */
|
|
8
|
+
interface CreateTaskEffectStoreOptions {
|
|
9
|
+
driver: StoreDriver;
|
|
10
|
+
now: IsoDateTimeClock;
|
|
11
|
+
getTask: (id: TaskId) => Promise<Task | null>;
|
|
12
|
+
}
|
|
13
|
+
/** Task identity and namespaced key identifying one durable external effect. */
|
|
14
|
+
interface TaskEffectLookupInput {
|
|
15
|
+
readonly taskId: TaskId;
|
|
16
|
+
readonly key: TaskEffectKey;
|
|
17
|
+
}
|
|
18
|
+
/** Task-scoped effect key and first external-resource reference to record. */
|
|
19
|
+
interface RecordTaskEffectInput extends TaskEffectLookupInput {
|
|
20
|
+
readonly reference: TaskEffectReference;
|
|
21
|
+
}
|
|
22
|
+
/** Task-scoped effect key and optional explicit resolution timestamp. */
|
|
23
|
+
interface ResolveTaskEffectInput extends TaskEffectLookupInput {
|
|
24
|
+
readonly at?: string;
|
|
25
|
+
}
|
|
26
|
+
/** Durable, Task-scoped external-resource references and write-once resolution stamps. */
|
|
27
|
+
interface TaskEffectStore {
|
|
28
|
+
get(input: TaskEffectLookupInput): Promise<TaskEffect | null>;
|
|
29
|
+
/** Record the first external resource created for this key. Replays return that first record. */
|
|
30
|
+
record(input: RecordTaskEffectInput): Promise<TaskEffect>;
|
|
31
|
+
/** Stamp the effect resolved once, including when no external resource was found. */
|
|
32
|
+
resolve(input: ResolveTaskEffectInput): Promise<TaskEffect>;
|
|
33
|
+
}
|
|
34
|
+
/** Creates a CAS-protected store for idempotent Task effect reconciliation. */
|
|
35
|
+
declare function createTaskEffectStore(options: CreateTaskEffectStoreOptions): TaskEffectStore;
|
|
36
|
+
//#endregion
|
|
37
|
+
export { CreateTaskEffectStoreOptions, RecordTaskEffectInput, ResolveTaskEffectInput, TaskEffectLookupInput, TaskEffectStore, createTaskEffectStore };
|
|
38
|
+
//# sourceMappingURL=task-effects.d.mts.map
|
|
@@ -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
|