@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,134 @@
|
|
|
1
|
+
import { taskSchema } from "../schema/task.mjs";
|
|
2
|
+
import { InvalidTransitionError } from "../schema/transitions.mjs";
|
|
3
|
+
import { RecordNotFoundError, VersionConflictError } from "./driver.mjs";
|
|
4
|
+
import { assertTaskAdmission } from "./task-graphs.mjs";
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
import { isDeepStrictEqual } from "node:util";
|
|
7
|
+
//#region src/store/task-work.ts
|
|
8
|
+
/** Creates workflow-facing Task operations backed by canonical graph and Task stores. */
|
|
9
|
+
function createTaskWorkStore(options) {
|
|
10
|
+
const { driver, now, stores, workflows, update, restore } = options;
|
|
11
|
+
function parseTask(value) {
|
|
12
|
+
const task = taskSchema.parse(value);
|
|
13
|
+
workflows.validateStoredInput(task.work.workflow, task.work.input);
|
|
14
|
+
if (task.workResult) workflows.validateStoredOutput(task.work.workflow, task.workResult.output);
|
|
15
|
+
return task;
|
|
16
|
+
}
|
|
17
|
+
async function read(id) {
|
|
18
|
+
const record = await driver.get("tasks", id);
|
|
19
|
+
if (!record) throw new RecordNotFoundError("tasks", id);
|
|
20
|
+
const task = parseTask(record.value);
|
|
21
|
+
if (task.id !== id) throw new Error(`Task ${id} does not match its storage key`);
|
|
22
|
+
return {
|
|
23
|
+
task,
|
|
24
|
+
version: record.version,
|
|
25
|
+
work: task.work
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
async function recover(rootTaskId) {
|
|
29
|
+
const graph = await stores().graphs.get(rootTaskId);
|
|
30
|
+
if (!graph) throw new RecordNotFoundError("taskGraphs", rootTaskId);
|
|
31
|
+
const tasks = [];
|
|
32
|
+
for (const node of graph.nodes) {
|
|
33
|
+
const admission = parseTask(node.admission);
|
|
34
|
+
const task = parseTask(await stores().tasks.get(node.taskId) ?? await restore(admission));
|
|
35
|
+
assertTaskAdmission({
|
|
36
|
+
task,
|
|
37
|
+
admission
|
|
38
|
+
});
|
|
39
|
+
tasks.push(task);
|
|
40
|
+
}
|
|
41
|
+
return tasks;
|
|
42
|
+
}
|
|
43
|
+
function assertAttempt(task, expectAttempt) {
|
|
44
|
+
z.number().int().positive().parse(expectAttempt);
|
|
45
|
+
if (task.attempt !== expectAttempt) throw new Error(`Task ${task.id} is on attempt ${task.attempt}, not ${expectAttempt}`);
|
|
46
|
+
}
|
|
47
|
+
async function setWaiting(id, expectAttempt, waiting, phase) {
|
|
48
|
+
for (;;) {
|
|
49
|
+
const { task, version } = await read(id);
|
|
50
|
+
assertAttempt(task, expectAttempt);
|
|
51
|
+
if (task.state !== "running") throw new Error(`Task ${id} must be running to ${waiting ? "wait" : "resume"}`);
|
|
52
|
+
const next = taskSchema.parse({
|
|
53
|
+
...task,
|
|
54
|
+
waitingForMessages: waiting,
|
|
55
|
+
workflowPhase: phase ?? task.workflowPhase,
|
|
56
|
+
lastProgressAt: waiting ? task.lastProgressAt : now(),
|
|
57
|
+
updatedAt: now()
|
|
58
|
+
});
|
|
59
|
+
if (task.waitingForMessages === waiting && next.workflowPhase === task.workflowPhase) return task;
|
|
60
|
+
await stores().receipts.append({
|
|
61
|
+
taskId: id,
|
|
62
|
+
repositoryIds: task.repositoryIds,
|
|
63
|
+
kind: task.kind,
|
|
64
|
+
state: waiting ? "waiting" : "resumed",
|
|
65
|
+
phase: "workflow",
|
|
66
|
+
attempt: task.attempt,
|
|
67
|
+
reason: waiting ? "Workflow is waiting for Task messages" : "Workflow resumed message processing",
|
|
68
|
+
dedupeKey: `work:${id}:${version}:${waiting ? "wait" : "resume"}`
|
|
69
|
+
});
|
|
70
|
+
try {
|
|
71
|
+
await update(next, version);
|
|
72
|
+
return next;
|
|
73
|
+
} catch (error) {
|
|
74
|
+
if (!(error instanceof VersionConflictError)) throw error;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
async function complete(input, expectedWorkflow) {
|
|
79
|
+
const { task: { taskId: id, attempt: expectAttempt }, output: value } = input;
|
|
80
|
+
for (;;) {
|
|
81
|
+
const { task, work } = await read(id);
|
|
82
|
+
assertAttempt(task, expectAttempt);
|
|
83
|
+
if (expectedWorkflow !== void 0 && !isDeepStrictEqual(work.workflow, expectedWorkflow)) throw new Error(`Task ${id} uses workflow ${work.workflow.id}@${work.workflow.version}, not ${expectedWorkflow.id}@${expectedWorkflow.version}`);
|
|
84
|
+
if (task.state === "succeeded") {
|
|
85
|
+
const output = workflows.validateOutput(work.workflow, value);
|
|
86
|
+
if (!isDeepStrictEqual(task.workResult?.output, output)) throw new Error(`Task ${id} already recorded a different workflow output`);
|
|
87
|
+
return task;
|
|
88
|
+
}
|
|
89
|
+
try {
|
|
90
|
+
return await stores().tasks.transition(id, "succeeded", {
|
|
91
|
+
output: value,
|
|
92
|
+
expectAttempt,
|
|
93
|
+
expectFrom: task.state,
|
|
94
|
+
phase: "workflow"
|
|
95
|
+
});
|
|
96
|
+
} catch (error) {
|
|
97
|
+
if (error instanceof VersionConflictError) continue;
|
|
98
|
+
if (error instanceof InvalidTransitionError && (await read(id)).task.state === "succeeded") continue;
|
|
99
|
+
throw error;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return {
|
|
104
|
+
complete,
|
|
105
|
+
completeWorkflow: ({ task, workflow, output }) => complete({
|
|
106
|
+
task,
|
|
107
|
+
output
|
|
108
|
+
}, workflow),
|
|
109
|
+
waitForMessages: ({ task: { taskId, attempt }, phase }) => setWaiting(taskId, attempt, true, phase),
|
|
110
|
+
resume: ({ task: { taskId, attempt } }) => setWaiting(taskId, attempt, false),
|
|
111
|
+
recover,
|
|
112
|
+
async usage(rootTaskId) {
|
|
113
|
+
const tasks = await recover(rootTaskId);
|
|
114
|
+
let micros = 0;
|
|
115
|
+
let inputTokens = 0;
|
|
116
|
+
let outputTokens = 0;
|
|
117
|
+
for (const task of tasks) {
|
|
118
|
+
micros += Math.round((task.usage?.costUsd ?? task.settledUsd ?? 0) * 1e6);
|
|
119
|
+
inputTokens += task.usage?.inputTokens ?? 0;
|
|
120
|
+
outputTokens += task.usage?.outputTokens ?? 0;
|
|
121
|
+
}
|
|
122
|
+
return {
|
|
123
|
+
costUsd: micros / 1e6,
|
|
124
|
+
inputTokens,
|
|
125
|
+
outputTokens,
|
|
126
|
+
taskCount: tasks.length
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
//#endregion
|
|
132
|
+
export { createTaskWorkStore };
|
|
133
|
+
|
|
134
|
+
//# sourceMappingURL=task-work.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"task-work.mjs","names":[],"sources":["../../src/store/task-work.ts"],"sourcesContent":["import { isDeepStrictEqual } from \"node:util\";\nimport { z } from \"zod\";\nimport type { IsoDateTimeClock } from \"../schema/common\";\nimport type { TaskAttemptRef } from \"../schema/execution\";\nimport type { TaskId } from \"../schema/id\";\nimport { taskSchema, type Task } from \"../schema/task\";\nimport { InvalidTransitionError } from \"../schema/transitions\";\nimport type { JsonValue, WorkflowBinding } from \"../schema/work\";\nimport type { WorkflowRegistry } from \"../workflows\";\nimport { RecordNotFoundError, VersionConflictError, type StoreDriver } from \"./driver\";\nimport type { FactoryStores } from \"./engine\";\nimport { assertTaskAdmission } from \"./task-graphs\";\n\n/** Workflow-facing Task operations for completion, message waiting, recovery, and usage aggregation. */\nexport interface TaskWorkStore {\n /** Complete through the dynamic registry boundary, which validates unknown output at runtime. */\n complete(input: CompleteTaskWorkInput): Promise<Task>;\n /** Complete work while statically checking output against its exact workflow binding. */\n completeWorkflow<Output extends JsonValue>(\n input: CompleteWorkflowTaskWorkInput<Output>,\n ): Promise<Task>;\n waitForMessages(input: WaitForTaskMessagesInput): Promise<Task>;\n resume(input: ResumeTaskWorkInput): Promise<Task>;\n /** Repair admitted creation snapshots by direct graph IDs, never by scanning the bucket. */\n recover(rootTaskId: TaskId): Promise<readonly Task[]>;\n /** Each Task contributes local measured usage once; no parent roll-up is charged again. */\n usage(rootTaskId: TaskId): Promise<TaskWorkUsage>;\n}\n\n/** Exact Task attempt and candidate workflow output used to complete work. */\nexport interface CompleteTaskWorkInput {\n task: TaskAttemptRef;\n output: unknown;\n}\n\n/** Exact Task attempt, workflow contract, and statically checked output used to complete work. */\nexport interface CompleteWorkflowTaskWorkInput<Output extends JsonValue = JsonValue> {\n task: TaskAttemptRef;\n workflow: WorkflowBinding<JsonValue, Output>;\n output: NoInfer<Output>;\n}\n\n/** Aggregated measured usage across one canonical Task graph. */\nexport interface TaskWorkUsage {\n costUsd: number;\n inputTokens: number;\n outputTokens: number;\n taskCount: number;\n}\n\n/** Exact running Task attempt and optional workflow phase used to await messages. */\nexport interface WaitForTaskMessagesInput {\n task: TaskAttemptRef;\n phase?: string;\n}\n\n/** Exact running Task attempt to resume after message delivery. */\nexport interface ResumeTaskWorkInput {\n task: TaskAttemptRef;\n}\n\n/** Canonical Task graph, receipt, and Task capabilities used by workflow-facing operations. */\nexport type TaskWorkStores = Pick<FactoryStores, \"graphs\" | \"receipts\" | \"tasks\">;\n\n/** Engine-owned dependencies used to construct workflow-facing Task operations. */\nexport interface CreateTaskWorkStoreOptions {\n driver: StoreDriver;\n now: IsoDateTimeClock;\n stores: () => TaskWorkStores;\n workflows: WorkflowRegistry;\n update(task: Task, version: number): Promise<void>;\n restore(task: Task): Promise<Task>;\n}\n\n/** Creates workflow-facing Task operations backed by canonical graph and Task stores. */\nexport function createTaskWorkStore(options: CreateTaskWorkStoreOptions): TaskWorkStore {\n const { driver, now, stores, workflows, update, restore } = options;\n function parseTask(value: unknown): Task {\n const task = taskSchema.parse(value);\n workflows.validateStoredInput(task.work.workflow, task.work.input);\n if (task.workResult) workflows.validateStoredOutput(task.work.workflow, task.workResult.output);\n return task;\n }\n async function read(id: TaskId) {\n const record = await driver.get(\"tasks\", id);\n if (!record) throw new RecordNotFoundError(\"tasks\", id);\n const task = parseTask(record.value);\n if (task.id !== id) throw new Error(`Task ${id} does not match its storage key`);\n return { task, version: record.version, work: task.work };\n }\n async function recover(rootTaskId: TaskId): Promise<readonly Task[]> {\n const graph = await stores().graphs.get(rootTaskId);\n if (!graph) throw new RecordNotFoundError(\"taskGraphs\", rootTaskId);\n const tasks: Task[] = [];\n for (const node of graph.nodes) {\n const admission = parseTask(node.admission);\n const existing = await stores().tasks.get(node.taskId);\n const task = parseTask(existing ?? (await restore(admission)));\n assertTaskAdmission({ task, admission });\n tasks.push(task);\n }\n return tasks;\n }\n function assertAttempt(task: Task, expectAttempt: number) {\n z.number().int().positive().parse(expectAttempt);\n if (task.attempt !== expectAttempt)\n throw new Error(`Task ${task.id} is on attempt ${task.attempt}, not ${expectAttempt}`);\n }\n async function setWaiting(\n id: TaskId,\n expectAttempt: number,\n waiting: boolean,\n phase?: string,\n ): Promise<Task> {\n for (;;) {\n const { task, version } = await read(id);\n assertAttempt(task, expectAttempt);\n if (task.state !== \"running\")\n throw new Error(`Task ${id} must be running to ${waiting ? \"wait\" : \"resume\"}`);\n const next = taskSchema.parse({\n ...task,\n waitingForMessages: waiting,\n workflowPhase: phase ?? task.workflowPhase,\n lastProgressAt: waiting ? task.lastProgressAt : now(),\n updatedAt: now(),\n });\n if (task.waitingForMessages === waiting && next.workflowPhase === task.workflowPhase)\n return task;\n await stores().receipts.append({\n taskId: id,\n repositoryIds: task.repositoryIds,\n kind: task.kind,\n state: waiting ? \"waiting\" : \"resumed\",\n phase: \"workflow\",\n attempt: task.attempt,\n reason: waiting\n ? \"Workflow is waiting for Task messages\"\n : \"Workflow resumed message processing\",\n dedupeKey: `work:${id}:${version}:${waiting ? \"wait\" : \"resume\"}`,\n });\n try {\n await update(next, version);\n return next;\n } catch (error) {\n if (!(error instanceof VersionConflictError)) throw error;\n }\n }\n }\n async function complete(\n input: CompleteTaskWorkInput,\n expectedWorkflow?: WorkflowBinding,\n ): Promise<Task> {\n const {\n task: { taskId: id, attempt: expectAttempt },\n output: value,\n } = input;\n for (;;) {\n const { task, work } = await read(id);\n assertAttempt(task, expectAttempt);\n if (expectedWorkflow !== undefined && !isDeepStrictEqual(work.workflow, expectedWorkflow)) {\n throw new Error(\n `Task ${id} uses workflow ${work.workflow.id}@${work.workflow.version}, not ${expectedWorkflow.id}@${expectedWorkflow.version}`,\n );\n }\n if (task.state === \"succeeded\") {\n const output = workflows.validateOutput(work.workflow, value);\n if (!isDeepStrictEqual(task.workResult?.output, output)) {\n throw new Error(`Task ${id} already recorded a different workflow output`);\n }\n return task;\n }\n try {\n return await stores().tasks.transition(id, \"succeeded\", {\n output: value,\n expectAttempt,\n expectFrom: task.state,\n phase: \"workflow\",\n });\n } catch (error) {\n if (error instanceof VersionConflictError) continue;\n if (error instanceof InvalidTransitionError && (await read(id)).task.state === \"succeeded\")\n continue;\n throw error;\n }\n }\n }\n return {\n complete,\n completeWorkflow: ({ task, workflow, output }) => complete({ task, output }, workflow),\n waitForMessages: ({ task: { taskId, attempt }, phase }) =>\n setWaiting(taskId, attempt, true, phase),\n resume: ({ task: { taskId, attempt } }) => setWaiting(taskId, attempt, false),\n recover,\n async usage(rootTaskId) {\n const tasks = await recover(rootTaskId);\n let micros = 0;\n let inputTokens = 0;\n let outputTokens = 0;\n for (const task of tasks) {\n micros += Math.round((task.usage?.costUsd ?? task.settledUsd ?? 0) * 1_000_000);\n inputTokens += task.usage?.inputTokens ?? 0;\n outputTokens += task.usage?.outputTokens ?? 0;\n }\n return { costUsd: micros / 1_000_000, inputTokens, outputTokens, taskCount: tasks.length };\n },\n };\n}\n"],"mappings":";;;;;;;;AA2EA,SAAgB,oBAAoB,SAAoD;CACtF,MAAM,EAAE,QAAQ,KAAK,QAAQ,WAAW,QAAQ,YAAY;CAC5D,SAAS,UAAU,OAAsB;EACvC,MAAM,OAAO,WAAW,MAAM,KAAK;EACnC,UAAU,oBAAoB,KAAK,KAAK,UAAU,KAAK,KAAK,KAAK;EACjE,IAAI,KAAK,YAAY,UAAU,qBAAqB,KAAK,KAAK,UAAU,KAAK,WAAW,MAAM;EAC9F,OAAO;CACT;CACA,eAAe,KAAK,IAAY;EAC9B,MAAM,SAAS,MAAM,OAAO,IAAI,SAAS,EAAE;EAC3C,IAAI,CAAC,QAAQ,MAAM,IAAI,oBAAoB,SAAS,EAAE;EACtD,MAAM,OAAO,UAAU,OAAO,KAAK;EACnC,IAAI,KAAK,OAAO,IAAI,MAAM,IAAI,MAAM,QAAQ,GAAG,gCAAgC;EAC/E,OAAO;GAAE;GAAM,SAAS,OAAO;GAAS,MAAM,KAAK;EAAK;CAC1D;CACA,eAAe,QAAQ,YAA8C;EACnE,MAAM,QAAQ,MAAM,OAAO,CAAC,CAAC,OAAO,IAAI,UAAU;EAClD,IAAI,CAAC,OAAO,MAAM,IAAI,oBAAoB,cAAc,UAAU;EAClE,MAAM,QAAgB,CAAC;EACvB,KAAK,MAAM,QAAQ,MAAM,OAAO;GAC9B,MAAM,YAAY,UAAU,KAAK,SAAS;GAE1C,MAAM,OAAO,UAAU,MADA,OAAO,CAAC,CAAC,MAAM,IAAI,KAAK,MAAM,KACjB,MAAM,QAAQ,SAAS,CAAE;GAC7D,oBAAoB;IAAE;IAAM;GAAU,CAAC;GACvC,MAAM,KAAK,IAAI;EACjB;EACA,OAAO;CACT;CACA,SAAS,cAAc,MAAY,eAAuB;EACxD,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,CAAC,MAAM,aAAa;EAC/C,IAAI,KAAK,YAAY,eACnB,MAAM,IAAI,MAAM,QAAQ,KAAK,GAAG,iBAAiB,KAAK,QAAQ,QAAQ,eAAe;CACzF;CACA,eAAe,WACb,IACA,eACA,SACA,OACe;EACf,SAAS;GACP,MAAM,EAAE,MAAM,YAAY,MAAM,KAAK,EAAE;GACvC,cAAc,MAAM,aAAa;GACjC,IAAI,KAAK,UAAU,WACjB,MAAM,IAAI,MAAM,QAAQ,GAAG,sBAAsB,UAAU,SAAS,UAAU;GAChF,MAAM,OAAO,WAAW,MAAM;IAC5B,GAAG;IACH,oBAAoB;IACpB,eAAe,SAAS,KAAK;IAC7B,gBAAgB,UAAU,KAAK,iBAAiB,IAAI;IACpD,WAAW,IAAI;GACjB,CAAC;GACD,IAAI,KAAK,uBAAuB,WAAW,KAAK,kBAAkB,KAAK,eACrE,OAAO;GACT,MAAM,OAAO,CAAC,CAAC,SAAS,OAAO;IAC7B,QAAQ;IACR,eAAe,KAAK;IACpB,MAAM,KAAK;IACX,OAAO,UAAU,YAAY;IAC7B,OAAO;IACP,SAAS,KAAK;IACd,QAAQ,UACJ,0CACA;IACJ,WAAW,QAAQ,GAAG,GAAG,QAAQ,GAAG,UAAU,SAAS;GACzD,CAAC;GACD,IAAI;IACF,MAAM,OAAO,MAAM,OAAO;IAC1B,OAAO;GACT,SAAS,OAAO;IACd,IAAI,EAAE,iBAAiB,uBAAuB,MAAM;GACtD;EACF;CACF;CACA,eAAe,SACb,OACA,kBACe;EACf,MAAM,EACJ,MAAM,EAAE,QAAQ,IAAI,SAAS,iBAC7B,QAAQ,UACN;EACJ,SAAS;GACP,MAAM,EAAE,MAAM,SAAS,MAAM,KAAK,EAAE;GACpC,cAAc,MAAM,aAAa;GACjC,IAAI,qBAAqB,KAAA,KAAa,CAAC,kBAAkB,KAAK,UAAU,gBAAgB,GACtF,MAAM,IAAI,MACR,QAAQ,GAAG,iBAAiB,KAAK,SAAS,GAAG,GAAG,KAAK,SAAS,QAAQ,QAAQ,iBAAiB,GAAG,GAAG,iBAAiB,SACxH;GAEF,IAAI,KAAK,UAAU,aAAa;IAC9B,MAAM,SAAS,UAAU,eAAe,KAAK,UAAU,KAAK;IAC5D,IAAI,CAAC,kBAAkB,KAAK,YAAY,QAAQ,MAAM,GACpD,MAAM,IAAI,MAAM,QAAQ,GAAG,8CAA8C;IAE3E,OAAO;GACT;GACA,IAAI;IACF,OAAO,MAAM,OAAO,CAAC,CAAC,MAAM,WAAW,IAAI,aAAa;KACtD,QAAQ;KACR;KACA,YAAY,KAAK;KACjB,OAAO;IACT,CAAC;GACH,SAAS,OAAO;IACd,IAAI,iBAAiB,sBAAsB;IAC3C,IAAI,iBAAiB,2BAA2B,MAAM,KAAK,EAAE,EAAA,CAAG,KAAK,UAAU,aAC7E;IACF,MAAM;GACR;EACF;CACF;CACA,OAAO;EACL;EACA,mBAAmB,EAAE,MAAM,UAAU,aAAa,SAAS;GAAE;GAAM;EAAO,GAAG,QAAQ;EACrF,kBAAkB,EAAE,MAAM,EAAE,QAAQ,WAAW,YAC7C,WAAW,QAAQ,SAAS,MAAM,KAAK;EACzC,SAAS,EAAE,MAAM,EAAE,QAAQ,gBAAgB,WAAW,QAAQ,SAAS,KAAK;EAC5E;EACA,MAAM,MAAM,YAAY;GACtB,MAAM,QAAQ,MAAM,QAAQ,UAAU;GACtC,IAAI,SAAS;GACb,IAAI,cAAc;GAClB,IAAI,eAAe;GACnB,KAAK,MAAM,QAAQ,OAAO;IACxB,UAAU,KAAK,OAAO,KAAK,OAAO,WAAW,KAAK,cAAc,KAAK,GAAS;IAC9E,eAAe,KAAK,OAAO,eAAe;IAC1C,gBAAgB,KAAK,OAAO,gBAAgB;GAC9C;GACA,OAAO;IAAE,SAAS,SAAS;IAAW;IAAa;IAAc,WAAW,MAAM;GAAO;EAC3F;CACF;AACF"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import "../schema/task.mjs";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
//#region src/store/work-notifications.d.ts
|
|
4
|
+
/** Validates a stable wakeup that addresses canonical work by root graph. */
|
|
5
|
+
declare const workNotificationSchema: z.ZodObject<{
|
|
6
|
+
rootTaskId: z.ZodType<`task_${string}`, string, z.core.$ZodTypeInternals<`task_${string}`, string>>;
|
|
7
|
+
key: z.ZodString;
|
|
8
|
+
}, z.core.$strict>;
|
|
9
|
+
/** Retry-safe notification telling a consumer to reread one canonical Task graph. */
|
|
10
|
+
type WorkNotification = z.infer<typeof workNotificationSchema>;
|
|
11
|
+
/** Application transport for post-commit work wakeups and delivery-failure reporting. */
|
|
12
|
+
interface WorkNotifications {
|
|
13
|
+
publish(event: WorkNotification): Promise<void>;
|
|
14
|
+
/** The source write already committed. Report transport failure without pretending it rolled back. */
|
|
15
|
+
onError(error: unknown, event: WorkNotification): void;
|
|
16
|
+
}
|
|
17
|
+
//#endregion
|
|
18
|
+
export { WorkNotification, WorkNotifications, workNotificationSchema };
|
|
19
|
+
//# sourceMappingURL=work-notifications.d.mts.map
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { taskIdSchema } from "../schema/id.mjs";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { isDeepStrictEqual } from "node:util";
|
|
4
|
+
//#region src/store/work-notifications.ts
|
|
5
|
+
/** Validates a stable wakeup that addresses canonical work by root graph. */
|
|
6
|
+
const workNotificationSchema = z.strictObject({
|
|
7
|
+
rootTaskId: taskIdSchema.describe("Canonical graph to reconcile; never an execution authorization."),
|
|
8
|
+
key: z.string().min(1).max(256).describe("Stable identity of the committed source change.")
|
|
9
|
+
}).describe("Durable work wakeup. Consumers reread canonical state and tolerate duplicate delivery.");
|
|
10
|
+
/** Creates a best-effort post-commit notifier whose failures never roll back source writes. */
|
|
11
|
+
function createWorkNotifier(options) {
|
|
12
|
+
async function publish(event) {
|
|
13
|
+
if (!options) return;
|
|
14
|
+
try {
|
|
15
|
+
await options.publish(event);
|
|
16
|
+
} catch (error) {
|
|
17
|
+
try {
|
|
18
|
+
options.onError(error, event);
|
|
19
|
+
} catch {}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function schedulingState(task) {
|
|
23
|
+
return [
|
|
24
|
+
task.state,
|
|
25
|
+
task.attempt,
|
|
26
|
+
task.approval,
|
|
27
|
+
task.waitingForMessages,
|
|
28
|
+
task.workflowPhase,
|
|
29
|
+
task.changeId,
|
|
30
|
+
task.questionTaskId,
|
|
31
|
+
task.answerEventIds
|
|
32
|
+
];
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
publish,
|
|
36
|
+
async task(task, previous, version) {
|
|
37
|
+
if (previous && isDeepStrictEqual(schedulingState(task), schedulingState(previous))) return;
|
|
38
|
+
await publish({
|
|
39
|
+
rootTaskId: task.rootTaskId,
|
|
40
|
+
key: `task:${task.id}:${version}`
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
//#endregion
|
|
46
|
+
export { createWorkNotifier, workNotificationSchema };
|
|
47
|
+
|
|
48
|
+
//# sourceMappingURL=work-notifications.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"work-notifications.mjs","names":[],"sources":["../../src/store/work-notifications.ts"],"sourcesContent":["import { isDeepStrictEqual } from \"node:util\";\nimport { z } from \"zod\";\nimport { taskIdSchema } from \"../schema/id\";\nimport type { Task } from \"../schema/task\";\n\n/** Validates a stable wakeup that addresses canonical work by root graph. */\nexport const workNotificationSchema = z\n .strictObject({\n rootTaskId: taskIdSchema.describe(\n \"Canonical graph to reconcile; never an execution authorization.\",\n ),\n key: z.string().min(1).max(256).describe(\"Stable identity of the committed source change.\"),\n })\n .describe(\n \"Durable work wakeup. Consumers reread canonical state and tolerate duplicate delivery.\",\n );\n/** Retry-safe notification telling a consumer to reread one canonical Task graph. */\nexport type WorkNotification = z.infer<typeof workNotificationSchema>;\n/** Application transport for post-commit work wakeups and delivery-failure reporting. */\nexport interface WorkNotifications {\n publish(event: WorkNotification): Promise<void>;\n /** The source write already committed. Report transport failure without pretending it rolled back. */\n onError(error: unknown, event: WorkNotification): void;\n}\n\n/** Creates a best-effort post-commit notifier whose failures never roll back source writes. */\nexport function createWorkNotifier(options?: WorkNotifications) {\n async function publish(event: WorkNotification): Promise<void> {\n if (!options) return;\n try {\n await options.publish(event);\n } catch (error) {\n // Reporting must not turn a committed CAS write into an apparent write failure.\n try {\n options.onError(error, event);\n } catch {\n /* Recovery rediscovers canonical work. */\n }\n }\n }\n function schedulingState(task: Task) {\n return [\n task.state,\n task.attempt,\n task.approval,\n task.waitingForMessages,\n task.workflowPhase,\n task.changeId,\n task.questionTaskId,\n task.answerEventIds,\n ];\n }\n return {\n publish,\n async task(task: Task, previous: Task | null, version: number) {\n if (previous && isDeepStrictEqual(schedulingState(task), schedulingState(previous))) return;\n await publish({ rootTaskId: task.rootTaskId, key: `task:${task.id}:${version}` });\n },\n };\n}\n"],"mappings":";;;;;AAMA,MAAa,yBAAyB,EACnC,aAAa;CACZ,YAAY,aAAa,SACvB,iEACF;CACA,KAAK,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,SAAS,iDAAiD;AAC5F,CAAC,CAAC,CACD,SACC,wFACF;;AAWF,SAAgB,mBAAmB,SAA6B;CAC9D,eAAe,QAAQ,OAAwC;EAC7D,IAAI,CAAC,SAAS;EACd,IAAI;GACF,MAAM,QAAQ,QAAQ,KAAK;EAC7B,SAAS,OAAO;GAEd,IAAI;IACF,QAAQ,QAAQ,OAAO,KAAK;GAC9B,QAAQ,CAER;EACF;CACF;CACA,SAAS,gBAAgB,MAAY;EACnC,OAAO;GACL,KAAK;GACL,KAAK;GACL,KAAK;GACL,KAAK;GACL,KAAK;GACL,KAAK;GACL,KAAK;GACL,KAAK;EACP;CACF;CACA,OAAO;EACL;EACA,MAAM,KAAK,MAAY,UAAuB,SAAiB;GAC7D,IAAI,YAAY,kBAAkB,gBAAgB,IAAI,GAAG,gBAAgB,QAAQ,CAAC,GAAG;GACrF,MAAM,QAAQ;IAAE,YAAY,KAAK;IAAY,KAAK,QAAQ,KAAK,GAAG,GAAG;GAAU,CAAC;EAClF;CACF;AACF"}
|
package/dist/sweep.d.mts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { TaskId } from "./schema/id.mjs";
|
|
2
|
+
import { Repository } from "./schema/repository.mjs";
|
|
3
|
+
import { ExecutionReference, Task } from "./schema/task.mjs";
|
|
4
|
+
import { AgentRouteKey } from "./agent-routes.mjs";
|
|
5
|
+
import { FactoryStores } from "./store/engine.mjs";
|
|
6
|
+
import { DispatchBudget, DispatchRefusalCode } from "./dispatch.mjs";
|
|
7
|
+
//#region src/sweep.d.ts
|
|
8
|
+
/** Graph, receipt, repository, and Task capabilities required by queued-work dispatch. */
|
|
9
|
+
type SweepQueuedStores = Pick<FactoryStores, "graphs" | "receipts" | "repositories" | "tasks">;
|
|
10
|
+
/**
|
|
11
|
+
* Single-repository adapter for an exact agent route. Supplied by the app, which knows its
|
|
12
|
+
* agents' addresses. Returns the provider's execution reference when it has one.
|
|
13
|
+
*/
|
|
14
|
+
type TaskLauncher = (task: Task, repository: Repository) => Promise<ExecutionReference>;
|
|
15
|
+
/** Exact-route launchers, budget gate, and pass bounds for queued Task dispatch. */
|
|
16
|
+
interface SweepQueuedOptions {
|
|
17
|
+
/** Single-repository agent adapters keyed by routeKey(binding), e.g. code-change@1. */
|
|
18
|
+
launchers: Readonly<Partial<Record<AgentRouteKey, TaskLauncher>>>;
|
|
19
|
+
/** Reserve-before-execution limits, passed through to every dispatch. */
|
|
20
|
+
budget?: DispatchBudget;
|
|
21
|
+
/** Maximum task launches attempted in one pass; omitted means no limit. */
|
|
22
|
+
maxDispatches?: number;
|
|
23
|
+
/** A dispatcher-wide snapshot avoids another complete task read. */
|
|
24
|
+
tasks?: readonly Task[];
|
|
25
|
+
}
|
|
26
|
+
/** Tasks dispatched, deferred by a gate, or failed by one queued-work pass. */
|
|
27
|
+
interface SweepQueuedResult {
|
|
28
|
+
readonly dispatched: readonly TaskId[];
|
|
29
|
+
/** Tasks left in the queue because a declared gate may become eligible later. */
|
|
30
|
+
readonly skipped: readonly {
|
|
31
|
+
readonly taskId: TaskId;
|
|
32
|
+
readonly reasonCode: SweepSkipReasonCode;
|
|
33
|
+
readonly reason: string;
|
|
34
|
+
}[];
|
|
35
|
+
/** Tasks whose routing is invalid or whose launch threw. */
|
|
36
|
+
readonly failed: readonly {
|
|
37
|
+
readonly taskId: TaskId;
|
|
38
|
+
readonly reasonCode: SweepFailureReasonCode;
|
|
39
|
+
readonly reason: string;
|
|
40
|
+
}[];
|
|
41
|
+
}
|
|
42
|
+
/** Stable reason a queued Task remained eligible for a later dispatch pass. */
|
|
43
|
+
type SweepSkipReasonCode = DispatchRefusalCode | "workflow_owned" | "awaiting_approval" | "dispatch_limit" | "task_changed";
|
|
44
|
+
/** Stable reason a queued Task could not be routed or launched. */
|
|
45
|
+
type SweepFailureReasonCode = "invalid_repository_scope" | "repository_not_found" | "launcher_not_registered" | "launch_failed";
|
|
46
|
+
/**
|
|
47
|
+
* One pass of the factory's dispatcher, meant to run on a schedule inside the
|
|
48
|
+
* deployment: every queued task that is allowed to run is started, in
|
|
49
|
+
* creation order. Nothing outside the factory ever starts an agent; the CLI
|
|
50
|
+
* only writes tasks and approvals, and this picks them up.
|
|
51
|
+
* Idempotent by construction: dispatchTask's single-winner transition means
|
|
52
|
+
* two overlapping sweeps cannot start the same task twice.
|
|
53
|
+
*/
|
|
54
|
+
declare function sweepQueued(stores: SweepQueuedStores, options: SweepQueuedOptions): Promise<SweepQueuedResult>;
|
|
55
|
+
//#endregion
|
|
56
|
+
export { SweepFailureReasonCode, SweepQueuedOptions, SweepQueuedResult, SweepQueuedStores, SweepSkipReasonCode, TaskLauncher, sweepQueued };
|
|
57
|
+
//# sourceMappingURL=sweep.d.mts.map
|
package/dist/sweep.mjs
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { InvalidTransitionError } from "./schema/transitions.mjs";
|
|
2
|
+
import { routeKey } from "./agent-routes.mjs";
|
|
3
|
+
import { VersionConflictError } from "./store/driver.mjs";
|
|
4
|
+
import { DispatchRefusedError, dispatchTask } from "./dispatch.mjs";
|
|
5
|
+
//#region src/sweep.ts
|
|
6
|
+
async function failUnrouteable(stores, task, reasonCode, reason, result) {
|
|
7
|
+
try {
|
|
8
|
+
await stores.tasks.transition(task.id, "failed", {
|
|
9
|
+
reason,
|
|
10
|
+
expectFrom: "queued"
|
|
11
|
+
});
|
|
12
|
+
result.failed.push({
|
|
13
|
+
taskId: task.id,
|
|
14
|
+
reasonCode,
|
|
15
|
+
reason
|
|
16
|
+
});
|
|
17
|
+
} catch (error) {
|
|
18
|
+
if (error instanceof InvalidTransitionError || error instanceof VersionConflictError) {
|
|
19
|
+
result.skipped.push({
|
|
20
|
+
taskId: task.id,
|
|
21
|
+
reasonCode: "task_changed",
|
|
22
|
+
reason: "task changed during routing"
|
|
23
|
+
});
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
throw error;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* One pass of the factory's dispatcher, meant to run on a schedule inside the
|
|
31
|
+
* deployment: every queued task that is allowed to run is started, in
|
|
32
|
+
* creation order. Nothing outside the factory ever starts an agent; the CLI
|
|
33
|
+
* only writes tasks and approvals, and this picks them up.
|
|
34
|
+
* Idempotent by construction: dispatchTask's single-winner transition means
|
|
35
|
+
* two overlapping sweeps cannot start the same task twice.
|
|
36
|
+
*/
|
|
37
|
+
async function sweepQueued(stores, options) {
|
|
38
|
+
const result = {
|
|
39
|
+
dispatched: [],
|
|
40
|
+
skipped: [],
|
|
41
|
+
failed: []
|
|
42
|
+
};
|
|
43
|
+
const maxDispatches = options.maxDispatches ?? Number.POSITIVE_INFINITY;
|
|
44
|
+
if (!(Number.isInteger(maxDispatches) || maxDispatches === Number.POSITIVE_INFINITY) || maxDispatches < 1) throw new Error("maxDispatches must be a positive integer");
|
|
45
|
+
let attempted = 0;
|
|
46
|
+
const taskSnapshot = options.tasks ?? await stores.tasks.list();
|
|
47
|
+
const budgetSnapshot = options.budget?.tasks ?? (options.budget && options.tasks ? await stores.tasks.list() : taskSnapshot);
|
|
48
|
+
const budgetTasks = new Map(budgetSnapshot.map((task) => [task.id, task]));
|
|
49
|
+
const queued = taskSnapshot.filter((task) => task.state === "queued").sort((a, b) => b.attempt - a.attempt || a.createdAt.localeCompare(b.createdAt));
|
|
50
|
+
for (const task of queued) {
|
|
51
|
+
const route = task.work.route;
|
|
52
|
+
if (route === void 0) {
|
|
53
|
+
result.skipped.push({
|
|
54
|
+
taskId: task.id,
|
|
55
|
+
reasonCode: "workflow_owned",
|
|
56
|
+
reason: "no agent route; workflow owns execution"
|
|
57
|
+
});
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
const [repositoryId] = task.repositoryIds;
|
|
61
|
+
if (task.repositoryIds.length !== 1 || repositoryId === void 0) {
|
|
62
|
+
await failUnrouteable(stores, task, "invalid_repository_scope", "sweep launcher adapter requires exactly one repository", result);
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
if (task.approval === "required") {
|
|
66
|
+
result.skipped.push({
|
|
67
|
+
taskId: task.id,
|
|
68
|
+
reasonCode: "awaiting_approval",
|
|
69
|
+
reason: "awaiting human approval"
|
|
70
|
+
});
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
const key = routeKey(route);
|
|
74
|
+
const launcher = Object.hasOwn(options.launchers, key) ? options.launchers[key] : void 0;
|
|
75
|
+
if (launcher === void 0) {
|
|
76
|
+
await failUnrouteable(stores, task, "launcher_not_registered", `no registered launcher handles agent route ${key}`, result);
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
if (!await stores.graphs.dependenciesSatisfied({
|
|
80
|
+
rootTaskId: task.rootTaskId,
|
|
81
|
+
taskId: task.id
|
|
82
|
+
})) {
|
|
83
|
+
result.skipped.push({
|
|
84
|
+
taskId: task.id,
|
|
85
|
+
reasonCode: "dependencies_unsatisfied",
|
|
86
|
+
reason: "graph dependencies must all be succeeded before dispatch"
|
|
87
|
+
});
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
if (attempted >= maxDispatches) {
|
|
91
|
+
result.skipped.push({
|
|
92
|
+
taskId: task.id,
|
|
93
|
+
reasonCode: "dispatch_limit",
|
|
94
|
+
reason: "per-sweep dispatch limit reached"
|
|
95
|
+
});
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
const repository = await stores.repositories.get(repositoryId);
|
|
99
|
+
if (repository === null) {
|
|
100
|
+
await failUnrouteable(stores, task, "repository_not_found", "task has no repository", result);
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
try {
|
|
104
|
+
const dispatched = await dispatchTask(stores, {
|
|
105
|
+
taskId: task.id,
|
|
106
|
+
route,
|
|
107
|
+
launch: (dispatched) => {
|
|
108
|
+
attempted += 1;
|
|
109
|
+
return launcher(dispatched, repository);
|
|
110
|
+
},
|
|
111
|
+
budget: options.budget === void 0 ? void 0 : {
|
|
112
|
+
...options.budget,
|
|
113
|
+
tasks: [...budgetTasks.values()]
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
budgetTasks.set(dispatched.id, dispatched);
|
|
117
|
+
result.dispatched.push(task.id);
|
|
118
|
+
} catch (error) {
|
|
119
|
+
if (options.budget !== void 0) {
|
|
120
|
+
const current = await stores.tasks.get(task.id).catch(() => null);
|
|
121
|
+
if (current !== null) budgetTasks.set(current.id, current);
|
|
122
|
+
}
|
|
123
|
+
if (error instanceof DispatchRefusedError) {
|
|
124
|
+
result.skipped.push({
|
|
125
|
+
taskId: task.id,
|
|
126
|
+
reasonCode: error.code,
|
|
127
|
+
reason: error.reason
|
|
128
|
+
});
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
result.failed.push({
|
|
132
|
+
taskId: task.id,
|
|
133
|
+
reasonCode: "launch_failed",
|
|
134
|
+
reason: error instanceof Error ? error.message : String(error)
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return result;
|
|
139
|
+
}
|
|
140
|
+
//#endregion
|
|
141
|
+
export { sweepQueued };
|
|
142
|
+
|
|
143
|
+
//# sourceMappingURL=sweep.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sweep.mjs","names":[],"sources":["../src/sweep.ts"],"sourcesContent":["import { routeKey, type AgentRouteKey } from \"./agent-routes\";\nimport { dispatchTask, DispatchRefusedError, type DispatchBudget } from \"./dispatch\";\nimport type { DispatchRefusalCode } from \"./dispatch\";\nimport type { TaskId } from \"./schema/id\";\nimport type { Repository } from \"./schema/repository\";\nimport type { ExecutionReference, Task } from \"./schema/task\";\nimport { InvalidTransitionError } from \"./schema/transitions\";\nimport { VersionConflictError } from \"./store/driver\";\nimport type { FactoryStores } from \"./store/engine\";\n\n/** Graph, receipt, repository, and Task capabilities required by queued-work dispatch. */\nexport type SweepQueuedStores = Pick<\n FactoryStores,\n \"graphs\" | \"receipts\" | \"repositories\" | \"tasks\"\n>;\n\n/**\n * Single-repository adapter for an exact agent route. Supplied by the app, which knows its\n * agents' addresses. Returns the provider's execution reference when it has one.\n */\nexport type TaskLauncher = (task: Task, repository: Repository) => Promise<ExecutionReference>;\n\n/** Exact-route launchers, budget gate, and pass bounds for queued Task dispatch. */\nexport interface SweepQueuedOptions {\n /** Single-repository agent adapters keyed by routeKey(binding), e.g. code-change@1. */\n launchers: Readonly<Partial<Record<AgentRouteKey, TaskLauncher>>>;\n /** Reserve-before-execution limits, passed through to every dispatch. */\n budget?: DispatchBudget;\n /** Maximum task launches attempted in one pass; omitted means no limit. */\n maxDispatches?: number;\n /** A dispatcher-wide snapshot avoids another complete task read. */\n tasks?: readonly Task[];\n}\n\n/** Tasks dispatched, deferred by a gate, or failed by one queued-work pass. */\nexport interface SweepQueuedResult {\n readonly dispatched: readonly TaskId[];\n /** Tasks left in the queue because a declared gate may become eligible later. */\n readonly skipped: readonly {\n readonly taskId: TaskId;\n readonly reasonCode: SweepSkipReasonCode;\n readonly reason: string;\n }[];\n /** Tasks whose routing is invalid or whose launch threw. */\n readonly failed: readonly {\n readonly taskId: TaskId;\n readonly reasonCode: SweepFailureReasonCode;\n readonly reason: string;\n }[];\n}\n\n/** Stable reason a queued Task remained eligible for a later dispatch pass. */\nexport type SweepSkipReasonCode =\n | DispatchRefusalCode\n | \"workflow_owned\"\n | \"awaiting_approval\"\n | \"dispatch_limit\"\n | \"task_changed\";\n\n/** Stable reason a queued Task could not be routed or launched. */\nexport type SweepFailureReasonCode =\n | \"invalid_repository_scope\"\n | \"repository_not_found\"\n | \"launcher_not_registered\"\n | \"launch_failed\";\n\ntype MutableSweepQueuedResult = {\n dispatched: TaskId[];\n skipped: { taskId: TaskId; reasonCode: SweepSkipReasonCode; reason: string }[];\n failed: { taskId: TaskId; reasonCode: SweepFailureReasonCode; reason: string }[];\n};\n\nasync function failUnrouteable(\n stores: SweepQueuedStores,\n task: Task,\n reasonCode: SweepFailureReasonCode,\n reason: string,\n result: MutableSweepQueuedResult,\n): Promise<void> {\n try {\n await stores.tasks.transition(task.id, \"failed\", { reason, expectFrom: \"queued\" });\n result.failed.push({ taskId: task.id, reasonCode, reason });\n } catch (error) {\n if (error instanceof InvalidTransitionError || error instanceof VersionConflictError) {\n result.skipped.push({\n taskId: task.id,\n reasonCode: \"task_changed\",\n reason: \"task changed during routing\",\n });\n return;\n }\n throw error;\n }\n}\n\n/**\n * One pass of the factory's dispatcher, meant to run on a schedule inside the\n * deployment: every queued task that is allowed to run is started, in\n * creation order. Nothing outside the factory ever starts an agent; the CLI\n * only writes tasks and approvals, and this picks them up.\n * Idempotent by construction: dispatchTask's single-winner transition means\n * two overlapping sweeps cannot start the same task twice.\n */\nexport async function sweepQueued(\n stores: SweepQueuedStores,\n options: SweepQueuedOptions,\n): Promise<SweepQueuedResult> {\n const result: MutableSweepQueuedResult = { dispatched: [], skipped: [], failed: [] };\n const maxDispatches = options.maxDispatches ?? Number.POSITIVE_INFINITY;\n if (\n !(Number.isInteger(maxDispatches) || maxDispatches === Number.POSITIVE_INFINITY) ||\n maxDispatches < 1\n ) {\n throw new Error(\"maxDispatches must be a positive integer\");\n }\n let attempted = 0;\n const taskSnapshot = options.tasks ?? (await stores.tasks.list());\n const budgetSnapshot =\n options.budget?.tasks ??\n (options.budget && options.tasks ? await stores.tasks.list() : taskSnapshot);\n const budgetTasks = new Map(budgetSnapshot.map((task) => [task.id, task]));\n const queued = taskSnapshot\n .filter((task) => task.state === \"queued\")\n .sort((a, b) => b.attempt - a.attempt || a.createdAt.localeCompare(b.createdAt));\n\n for (const task of queued) {\n const route = task.work.route;\n if (route === undefined) {\n result.skipped.push({\n taskId: task.id,\n reasonCode: \"workflow_owned\",\n reason: \"no agent route; workflow owns execution\",\n });\n continue;\n }\n const [repositoryId] = task.repositoryIds;\n if (task.repositoryIds.length !== 1 || repositoryId === undefined) {\n await failUnrouteable(\n stores,\n task,\n \"invalid_repository_scope\",\n \"sweep launcher adapter requires exactly one repository\",\n result,\n );\n continue;\n }\n if (task.approval === \"required\") {\n result.skipped.push({\n taskId: task.id,\n reasonCode: \"awaiting_approval\",\n reason: \"awaiting human approval\",\n });\n continue;\n }\n const key = routeKey(route);\n const launcher = Object.hasOwn(options.launchers, key) ? options.launchers[key] : undefined;\n if (launcher === undefined) {\n await failUnrouteable(\n stores,\n task,\n \"launcher_not_registered\",\n `no registered launcher handles agent route ${key}`,\n result,\n );\n continue;\n }\n if (\n !(await stores.graphs.dependenciesSatisfied({\n rootTaskId: task.rootTaskId,\n taskId: task.id,\n }))\n ) {\n result.skipped.push({\n taskId: task.id,\n reasonCode: \"dependencies_unsatisfied\",\n reason: \"graph dependencies must all be succeeded before dispatch\",\n });\n continue;\n }\n if (attempted >= maxDispatches) {\n result.skipped.push({\n taskId: task.id,\n reasonCode: \"dispatch_limit\",\n reason: \"per-sweep dispatch limit reached\",\n });\n continue;\n }\n const repository = await stores.repositories.get(repositoryId);\n if (repository === null) {\n await failUnrouteable(stores, task, \"repository_not_found\", \"task has no repository\", result);\n continue;\n }\n try {\n const dispatched = await dispatchTask(stores, {\n taskId: task.id,\n route,\n launch: (dispatched) => {\n attempted += 1;\n return launcher(dispatched, repository);\n },\n budget:\n options.budget === undefined\n ? undefined\n : { ...options.budget, tasks: [...budgetTasks.values()] },\n });\n budgetTasks.set(dispatched.id, dispatched);\n result.dispatched.push(task.id);\n } catch (error) {\n if (options.budget !== undefined) {\n const current = await stores.tasks.get(task.id).catch(() => null);\n if (current !== null) budgetTasks.set(current.id, current);\n }\n if (error instanceof DispatchRefusedError) {\n // Dependencies not ready, or another sweep won the race. Leave it.\n result.skipped.push({ taskId: task.id, reasonCode: error.code, reason: error.reason });\n continue;\n }\n result.failed.push({\n taskId: task.id,\n reasonCode: \"launch_failed\",\n reason: error instanceof Error ? error.message : String(error),\n });\n }\n }\n return result;\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Task } from "./schema/task.mjs";
|
|
2
|
+
import { MaybePromise } from "./schema/common.mjs";
|
|
3
|
+
import { CommunicationEvent } from "./schema/communication.mjs";
|
|
4
|
+
import { Receipt } from "./schema/receipt.mjs";
|
|
5
|
+
import { FactoryStores } from "./store/engine.mjs";
|
|
6
|
+
//#region src/task-communication.d.ts
|
|
7
|
+
/** Outbox, receipt, and Task capabilities required by communication reconciliation. */
|
|
8
|
+
type TaskCommunicationStores = Pick<FactoryStores, "outbox" | "receipts" | "tasks">;
|
|
9
|
+
/** Number of connector-neutral events newly enqueued during reconciliation. */
|
|
10
|
+
interface TaskCommunicationResult {
|
|
11
|
+
enqueued: number;
|
|
12
|
+
}
|
|
13
|
+
/** Repairs pending Task communication projections against the durable outbox. */
|
|
14
|
+
type TaskCommunicationReconciler<Stores extends TaskCommunicationStores = TaskCommunicationStores> = (stores: Stores, snapshot?: readonly Task[]) => Promise<TaskCommunicationResult>;
|
|
15
|
+
/** Durable Task and receipt context supplied to a lifecycle event projection. */
|
|
16
|
+
interface TaskLifecycleProjectionInput<Stores extends TaskCommunicationStores = TaskCommunicationStores> {
|
|
17
|
+
stores: Stores;
|
|
18
|
+
tasks: readonly Task[];
|
|
19
|
+
task: Task;
|
|
20
|
+
receipt: Receipt;
|
|
21
|
+
}
|
|
22
|
+
/** Application policy for projecting durable task state into connector-neutral events. */
|
|
23
|
+
interface TaskCommunicationProjection<Stores extends TaskCommunicationStores = TaskCommunicationStores> {
|
|
24
|
+
question(task: Task): MaybePromise<readonly CommunicationEvent[]>;
|
|
25
|
+
lifecycle(input: TaskLifecycleProjectionInput<Stores>): MaybePromise<readonly CommunicationEvent[]>;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Build the generic durable receipt reconciler around an application's event
|
|
29
|
+
* projection. Events are acknowledged only after every projected event has
|
|
30
|
+
* reached the outbox.
|
|
31
|
+
*/
|
|
32
|
+
declare function createTaskCommunicationReconciler<Stores extends TaskCommunicationStores>(projection: TaskCommunicationProjection<Stores>): TaskCommunicationReconciler<Stores>;
|
|
33
|
+
//#endregion
|
|
34
|
+
export { TaskCommunicationProjection, TaskCommunicationReconciler, TaskCommunicationResult, TaskCommunicationStores, TaskLifecycleProjectionInput, createTaskCommunicationReconciler };
|
|
35
|
+
//# sourceMappingURL=task-communication.d.mts.map
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
//#region src/task-communication.ts
|
|
2
|
+
/**
|
|
3
|
+
* Build the generic durable receipt reconciler around an application's event
|
|
4
|
+
* projection. Events are acknowledged only after every projected event has
|
|
5
|
+
* reached the outbox.
|
|
6
|
+
*/
|
|
7
|
+
function createTaskCommunicationReconciler(projection) {
|
|
8
|
+
return async function reconcileTaskCommunications(stores, snapshot) {
|
|
9
|
+
const tasks = snapshot ?? await stores.tasks.list();
|
|
10
|
+
let enqueued = 0;
|
|
11
|
+
for (const task of tasks) {
|
|
12
|
+
const presentsQuestion = task.question !== void 0;
|
|
13
|
+
if (presentsQuestion && task.state === "needs_human" && task.needsHumanNotifiedAt !== void 0) await stores.tasks.recordNeedsHumanDelivery(task.id, { attempt: task.attempt });
|
|
14
|
+
if (presentsQuestion && task.state === "needs_human" && task.needsHumanNotifiedAt === void 0 && !task.pendingCommunicationReceiptIds?.length) for (const event of await projection.question(task)) {
|
|
15
|
+
await stores.outbox.enqueue(event);
|
|
16
|
+
enqueued += 1;
|
|
17
|
+
}
|
|
18
|
+
for (const receiptId of task.pendingCommunicationReceiptIds ?? []) {
|
|
19
|
+
const receipt = await stores.receipts.get(receiptId);
|
|
20
|
+
if (receipt === null || receipt.taskId !== task.id) throw new Error(`Task ${task.id} references missing lifecycle receipt ${receiptId}.`);
|
|
21
|
+
if (receipt.state === "needs_human" && presentsQuestion) {
|
|
22
|
+
if (task.state === "needs_human" && task.attempt === receipt.attempt) for (const event of await projection.question(task)) {
|
|
23
|
+
await stores.outbox.enqueue(event);
|
|
24
|
+
enqueued += 1;
|
|
25
|
+
}
|
|
26
|
+
await stores.tasks.acknowledgeCommunication(task.id, receiptId);
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
for (const event of await projection.lifecycle({
|
|
30
|
+
stores,
|
|
31
|
+
tasks,
|
|
32
|
+
task,
|
|
33
|
+
receipt
|
|
34
|
+
})) {
|
|
35
|
+
await stores.outbox.enqueue(event);
|
|
36
|
+
enqueued += 1;
|
|
37
|
+
}
|
|
38
|
+
await stores.tasks.acknowledgeCommunication(task.id, receiptId);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return { enqueued };
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
//#endregion
|
|
45
|
+
export { createTaskCommunicationReconciler };
|
|
46
|
+
|
|
47
|
+
//# sourceMappingURL=task-communication.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"task-communication.mjs","names":[],"sources":["../src/task-communication.ts"],"sourcesContent":["import type { CommunicationEvent } from \"./schema/communication\";\nimport type { MaybePromise } from \"./schema/common\";\nimport type { Receipt } from \"./schema/receipt\";\nimport type { Task } from \"./schema/task\";\nimport type { FactoryStores } from \"./store/engine\";\n\n/** Outbox, receipt, and Task capabilities required by communication reconciliation. */\nexport type TaskCommunicationStores = Pick<FactoryStores, \"outbox\" | \"receipts\" | \"tasks\">;\n\n/** Number of connector-neutral events newly enqueued during reconciliation. */\nexport interface TaskCommunicationResult {\n enqueued: number;\n}\n\n/** Repairs pending Task communication projections against the durable outbox. */\nexport type TaskCommunicationReconciler<\n Stores extends TaskCommunicationStores = TaskCommunicationStores,\n> = (stores: Stores, snapshot?: readonly Task[]) => Promise<TaskCommunicationResult>;\n\n/** Durable Task and receipt context supplied to a lifecycle event projection. */\nexport interface TaskLifecycleProjectionInput<\n Stores extends TaskCommunicationStores = TaskCommunicationStores,\n> {\n stores: Stores;\n tasks: readonly Task[];\n task: Task;\n receipt: Receipt;\n}\n\n/** Application policy for projecting durable task state into connector-neutral events. */\nexport interface TaskCommunicationProjection<\n Stores extends TaskCommunicationStores = TaskCommunicationStores,\n> {\n question(task: Task): MaybePromise<readonly CommunicationEvent[]>;\n lifecycle(\n input: TaskLifecycleProjectionInput<Stores>,\n ): MaybePromise<readonly CommunicationEvent[]>;\n}\n\n/**\n * Build the generic durable receipt reconciler around an application's event\n * projection. Events are acknowledged only after every projected event has\n * reached the outbox.\n */\nexport function createTaskCommunicationReconciler<Stores extends TaskCommunicationStores>(\n projection: TaskCommunicationProjection<Stores>,\n): TaskCommunicationReconciler<Stores> {\n return async function reconcileTaskCommunications(\n stores: Stores,\n snapshot?: readonly Task[],\n ): Promise<TaskCommunicationResult> {\n const tasks = snapshot ?? (await stores.tasks.list());\n let enqueued = 0;\n for (const task of tasks) {\n const presentsQuestion = task.question !== undefined;\n // A connector may confirm before the parent starts waiting, or before\n // its delivery stamp is persisted.\n if (\n presentsQuestion &&\n task.state === \"needs_human\" &&\n task.needsHumanNotifiedAt !== undefined\n ) {\n await stores.tasks.recordNeedsHumanDelivery(task.id, { attempt: task.attempt });\n }\n if (\n presentsQuestion &&\n task.state === \"needs_human\" &&\n task.needsHumanNotifiedAt === undefined &&\n !task.pendingCommunicationReceiptIds?.length\n ) {\n for (const event of await projection.question(task)) {\n await stores.outbox.enqueue(event);\n enqueued += 1;\n }\n }\n for (const receiptId of task.pendingCommunicationReceiptIds ?? []) {\n const receipt = await stores.receipts.get(receiptId);\n if (receipt === null || receipt.taskId !== task.id) {\n throw new Error(`Task ${task.id} references missing lifecycle receipt ${receiptId}.`);\n }\n if (receipt.state === \"needs_human\" && presentsQuestion) {\n if (task.state === \"needs_human\" && task.attempt === receipt.attempt) {\n for (const event of await projection.question(task)) {\n await stores.outbox.enqueue(event);\n enqueued += 1;\n }\n }\n await stores.tasks.acknowledgeCommunication(task.id, receiptId);\n continue;\n }\n for (const event of await projection.lifecycle({ stores, tasks, task, receipt })) {\n await stores.outbox.enqueue(event);\n enqueued += 1;\n }\n await stores.tasks.acknowledgeCommunication(task.id, receiptId);\n }\n }\n return { enqueued };\n };\n}\n"],"mappings":";;;;;;AA4CA,SAAgB,kCACd,YACqC;CACrC,OAAO,eAAe,4BACpB,QACA,UACkC;EAClC,MAAM,QAAQ,YAAa,MAAM,OAAO,MAAM,KAAK;EACnD,IAAI,WAAW;EACf,KAAK,MAAM,QAAQ,OAAO;GACxB,MAAM,mBAAmB,KAAK,aAAa,KAAA;GAG3C,IACE,oBACA,KAAK,UAAU,iBACf,KAAK,yBAAyB,KAAA,GAE9B,MAAM,OAAO,MAAM,yBAAyB,KAAK,IAAI,EAAE,SAAS,KAAK,QAAQ,CAAC;GAEhF,IACE,oBACA,KAAK,UAAU,iBACf,KAAK,yBAAyB,KAAA,KAC9B,CAAC,KAAK,gCAAgC,QAEtC,KAAK,MAAM,SAAS,MAAM,WAAW,SAAS,IAAI,GAAG;IACnD,MAAM,OAAO,OAAO,QAAQ,KAAK;IACjC,YAAY;GACd;GAEF,KAAK,MAAM,aAAa,KAAK,kCAAkC,CAAC,GAAG;IACjE,MAAM,UAAU,MAAM,OAAO,SAAS,IAAI,SAAS;IACnD,IAAI,YAAY,QAAQ,QAAQ,WAAW,KAAK,IAC9C,MAAM,IAAI,MAAM,QAAQ,KAAK,GAAG,wCAAwC,UAAU,EAAE;IAEtF,IAAI,QAAQ,UAAU,iBAAiB,kBAAkB;KACvD,IAAI,KAAK,UAAU,iBAAiB,KAAK,YAAY,QAAQ,SAC3D,KAAK,MAAM,SAAS,MAAM,WAAW,SAAS,IAAI,GAAG;MACnD,MAAM,OAAO,OAAO,QAAQ,KAAK;MACjC,YAAY;KACd;KAEF,MAAM,OAAO,MAAM,yBAAyB,KAAK,IAAI,SAAS;KAC9D;IACF;IACA,KAAK,MAAM,SAAS,MAAM,WAAW,UAAU;KAAE;KAAQ;KAAO;KAAM;IAAQ,CAAC,GAAG;KAChF,MAAM,OAAO,OAAO,QAAQ,KAAK;KACjC,YAAY;IACd;IACA,MAAM,OAAO,MAAM,yBAAyB,KAAK,IAAI,SAAS;GAChE;EACF;EACA,OAAO,EAAE,SAAS;CACpB;AACF"}
|