@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,203 @@
|
|
|
1
|
+
import { repositoryIdSchema, taskIdSchema } from "../schema/id.mjs";
|
|
2
|
+
import { replyToSchema } from "../schema/common.mjs";
|
|
3
|
+
import { taskAttemptRef } from "../schema/execution.mjs";
|
|
4
|
+
import { ForbiddenError } from "eve/channels/auth";
|
|
5
|
+
//#region src/eve/task-session.ts
|
|
6
|
+
/** HTTP header that carries the authenticated Factory Task ID into an Eve session. */
|
|
7
|
+
const factoryTaskHeader = "x-factory-task-id";
|
|
8
|
+
/** Eve authentication attribute that stores the bound Factory Task ID. */
|
|
9
|
+
const factoryTaskAttribute = "factoryTaskId";
|
|
10
|
+
/** HTTP header that carries the Task attempt fence into an Eve session. */
|
|
11
|
+
const factoryTaskAttemptHeader = "x-factory-task-attempt";
|
|
12
|
+
/** Eve authentication attribute that stores the bound Task attempt fence. */
|
|
13
|
+
const factoryTaskAttemptAttribute = "factoryTaskAttempt";
|
|
14
|
+
/** HTTP header that carries the authenticated repository authorization envelope. */
|
|
15
|
+
const factoryRepositoryIdsHeader = "x-factory-repository-ids";
|
|
16
|
+
/** HTTP header that carries the authenticated conversation reply channel. */
|
|
17
|
+
const factoryReplyChannelHeader = "x-factory-reply-channel";
|
|
18
|
+
/** HTTP header that carries the authenticated conversation reply address. */
|
|
19
|
+
const factoryReplyAddressHeader = "x-factory-reply-address";
|
|
20
|
+
/** Eve authentication attribute that stores the authorized repository IDs. */
|
|
21
|
+
const factoryRepositoryIdsAttribute = "factoryRepositoryIds";
|
|
22
|
+
/** Eve authentication attribute that stores the conversation reply channel. */
|
|
23
|
+
const factoryReplyChannelAttribute = "factoryReplyChannel";
|
|
24
|
+
/** Eve authentication attribute that stores the conversation reply address. */
|
|
25
|
+
const factoryReplyAddressAttribute = "factoryReplyAddress";
|
|
26
|
+
/** Test whether authenticated Eve session state declares a Factory Task binding. */
|
|
27
|
+
function hasFactoryTaskBinding(auth) {
|
|
28
|
+
return auth?.attributes[factoryTaskAttribute] !== void 0;
|
|
29
|
+
}
|
|
30
|
+
/** Encode a Task ID and attempt fence as headers for an Eve session request. */
|
|
31
|
+
function taskHeaders(task) {
|
|
32
|
+
return {
|
|
33
|
+
[factoryTaskHeader]: task.taskId,
|
|
34
|
+
[factoryTaskAttemptHeader]: String(task.attempt)
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
/** Encode a conversation's repository scope and reply destination as Eve request headers. */
|
|
38
|
+
function conversationHeaders(input) {
|
|
39
|
+
return {
|
|
40
|
+
[factoryRepositoryIdsHeader]: input.repositoryIds.join(","),
|
|
41
|
+
[factoryReplyChannelHeader]: input.replyTo.channel,
|
|
42
|
+
[factoryReplyAddressHeader]: input.replyTo.address
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
/** Add a validated Task ID and attempt fence to an authenticated Eve initiator. */
|
|
46
|
+
function taskSessionAuth(auth, task) {
|
|
47
|
+
return {
|
|
48
|
+
...auth,
|
|
49
|
+
attributes: {
|
|
50
|
+
...auth.attributes,
|
|
51
|
+
[factoryTaskAttribute]: task.taskId,
|
|
52
|
+
[factoryTaskAttemptAttribute]: String(task.attempt)
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
/** Require valid Factory Task headers and bind them into the authenticated Eve initiator. */
|
|
57
|
+
function withFactoryTask(authenticate) {
|
|
58
|
+
return async (request) => {
|
|
59
|
+
const auth = await authenticate(request);
|
|
60
|
+
if (auth == null) return auth;
|
|
61
|
+
const parsedTask = taskIdSchema.safeParse(request.headers.get(factoryTaskHeader));
|
|
62
|
+
const parsedAttempt = parseTaskAttempt(request.headers.get(factoryTaskAttemptHeader));
|
|
63
|
+
if (!parsedTask.success || parsedAttempt === null) throw new ForbiddenError();
|
|
64
|
+
return taskSessionAuth(auth, taskAttemptRef(parsedTask.data, parsedAttempt));
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
/** Bind optional Task and conversation headers while rejecting partial or malformed context. */
|
|
68
|
+
function withOptionalFactoryTask(authenticate) {
|
|
69
|
+
return async (request) => {
|
|
70
|
+
const auth = await authenticate(request);
|
|
71
|
+
if (auth == null) return auth;
|
|
72
|
+
const taskId = request.headers.get(factoryTaskHeader);
|
|
73
|
+
const taskAttempt = request.headers.get(factoryTaskAttemptHeader);
|
|
74
|
+
const repositoryIds = request.headers.get(factoryRepositoryIdsHeader);
|
|
75
|
+
const replyChannel = request.headers.get(factoryReplyChannelHeader);
|
|
76
|
+
const replyAddress = request.headers.get(factoryReplyAddressHeader);
|
|
77
|
+
const hasConversationBinding = repositoryIds !== null || replyChannel !== null || replyAddress !== null;
|
|
78
|
+
if (taskId === null && taskAttempt === null && !hasConversationBinding) return auth;
|
|
79
|
+
const parsedTask = taskId === null ? null : taskIdSchema.safeParse(taskId);
|
|
80
|
+
const parsedAttempt = parseTaskAttempt(taskAttempt);
|
|
81
|
+
const parsedRepositories = repositoryIds === null ? null : repositoryIdSchema.array().min(1).safeParse(repositoryIds.split(",").filter(Boolean));
|
|
82
|
+
const parsedReplyTo = replyChannel === null || replyAddress === null ? null : replyToSchema.safeParse({
|
|
83
|
+
channel: replyChannel,
|
|
84
|
+
address: replyAddress
|
|
85
|
+
});
|
|
86
|
+
if (parsedTask !== null && !parsedTask.success || parsedTask === null !== (parsedAttempt === null) || hasConversationBinding && (parsedRepositories === null || !parsedRepositories.success || parsedReplyTo === null || !parsedReplyTo.success)) throw new ForbiddenError();
|
|
87
|
+
const conversationAttributes = parsedRepositories?.success === true && parsedReplyTo?.success === true ? {
|
|
88
|
+
[factoryRepositoryIdsAttribute]: parsedRepositories.data.join(","),
|
|
89
|
+
[factoryReplyChannelAttribute]: parsedReplyTo.data.channel,
|
|
90
|
+
[factoryReplyAddressAttribute]: parsedReplyTo.data.address
|
|
91
|
+
} : {};
|
|
92
|
+
return {
|
|
93
|
+
...auth,
|
|
94
|
+
attributes: {
|
|
95
|
+
...auth.attributes,
|
|
96
|
+
...parsedTask === null || parsedAttempt === null ? {} : {
|
|
97
|
+
[factoryTaskAttribute]: parsedTask.data,
|
|
98
|
+
[factoryTaskAttemptAttribute]: String(parsedAttempt)
|
|
99
|
+
},
|
|
100
|
+
...conversationAttributes
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
/** Read the required Factory Task ID from authenticated Eve session state. */
|
|
106
|
+
function requireTaskIdForSession(auth) {
|
|
107
|
+
return taskIdSchema.parse(auth?.attributes[factoryTaskAttribute]);
|
|
108
|
+
}
|
|
109
|
+
/** Read an optional Task ID from authenticated session state, rejecting malformed values. */
|
|
110
|
+
function optionalTaskIdForSession(auth) {
|
|
111
|
+
const value = auth?.attributes[factoryTaskAttribute];
|
|
112
|
+
return value === void 0 ? null : taskIdSchema.parse(value);
|
|
113
|
+
}
|
|
114
|
+
/** Read the complete, attempt-fenced Task binding established by Factory authentication. */
|
|
115
|
+
function taskBindingForSession(auth) {
|
|
116
|
+
const taskId = requireTaskIdForSession(auth);
|
|
117
|
+
const rawAttempt = auth?.attributes[factoryTaskAttemptAttribute];
|
|
118
|
+
const attempt = parseTaskAttempt(typeof rawAttempt === "string" ? rawAttempt : null);
|
|
119
|
+
if (attempt === null) throw new Error("Factory session Task attempt binding is invalid.");
|
|
120
|
+
return {
|
|
121
|
+
taskId,
|
|
122
|
+
attempt
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
/** Read the repository authorization envelope, or null when the session has none. */
|
|
126
|
+
function repositoryIdsForSession(auth) {
|
|
127
|
+
const value = auth?.attributes[factoryRepositoryIdsAttribute];
|
|
128
|
+
if (value === void 0) return null;
|
|
129
|
+
return repositoryIdSchema.array().min(1).parse(typeof value === "string" ? value.split(",").filter(Boolean) : value);
|
|
130
|
+
}
|
|
131
|
+
/** Read the authenticated reply destination, or null when the session has none. */
|
|
132
|
+
function replyToForSession(auth) {
|
|
133
|
+
const channel = auth?.attributes[factoryReplyChannelAttribute];
|
|
134
|
+
const address = auth?.attributes[factoryReplyAddressAttribute];
|
|
135
|
+
if (channel === void 0 && address === void 0) return null;
|
|
136
|
+
return replyToSchema.parse({
|
|
137
|
+
channel,
|
|
138
|
+
address
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
/** Read the complete conversation binding, rejecting partial authentication state. */
|
|
142
|
+
function conversationBindingForSession(auth) {
|
|
143
|
+
const repositoryIds = repositoryIdsForSession(auth);
|
|
144
|
+
const replyTo = replyToForSession(auth);
|
|
145
|
+
if (repositoryIds === null && replyTo === null) return null;
|
|
146
|
+
if (repositoryIds === null || replyTo === null) throw new Error("Factory session conversation binding is incomplete.");
|
|
147
|
+
return {
|
|
148
|
+
repositoryIds,
|
|
149
|
+
replyTo
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
const conversationBindingAttributes = [
|
|
153
|
+
factoryReplyChannelAttribute,
|
|
154
|
+
factoryReplyAddressAttribute,
|
|
155
|
+
factoryTaskAttribute,
|
|
156
|
+
factoryTaskAttemptAttribute
|
|
157
|
+
];
|
|
158
|
+
/**
|
|
159
|
+
* Read and validate the conversation binding carried on a session's auth pair.
|
|
160
|
+
* The initiator's attributes are authoritative; any binding attribute the
|
|
161
|
+
* current auth restates must agree with them. This is the only reader of the
|
|
162
|
+
* binding attribute encoding; callers never touch `attributes` themselves.
|
|
163
|
+
*
|
|
164
|
+
* Distinct from `conversationBindingForSession`, which reads the initiator
|
|
165
|
+
* alone: admission needs every envelope the pair carries, so that work cannot
|
|
166
|
+
* be admitted into a repository only one side of the pair authorizes.
|
|
167
|
+
*/
|
|
168
|
+
function conversationBinding(auth) {
|
|
169
|
+
const attributes = auth.initiator.attributes;
|
|
170
|
+
const replyTo = replyToSchema.parse({
|
|
171
|
+
channel: attributes[factoryReplyChannelAttribute],
|
|
172
|
+
address: attributes[factoryReplyAddressAttribute]
|
|
173
|
+
});
|
|
174
|
+
for (const key of conversationBindingAttributes) if (auth.current.attributes[key] !== void 0 && auth.current.attributes[key] !== attributes[key]) throw new Error("Conversation binding does not match the authenticated conversation");
|
|
175
|
+
const repositoryEnvelopes = [];
|
|
176
|
+
for (const [index, entry] of [auth.initiator, auth.current].entries()) {
|
|
177
|
+
const raw = entry.attributes[factoryRepositoryIdsAttribute];
|
|
178
|
+
if (index === 1 && raw === void 0) continue;
|
|
179
|
+
repositoryEnvelopes.push(repositoryIdSchema.array().min(1).parse(typeof raw === "string" ? raw.split(",") : raw));
|
|
180
|
+
}
|
|
181
|
+
let parentTask;
|
|
182
|
+
if (attributes["factoryTaskId"] !== void 0 || attributes["factoryTaskAttempt"] !== void 0) {
|
|
183
|
+
const taskId = taskIdSchema.parse(attributes[factoryTaskAttribute]);
|
|
184
|
+
const rawAttempt = attributes[factoryTaskAttemptAttribute];
|
|
185
|
+
const attempt = typeof rawAttempt === "string" ? parseTaskAttempt(rawAttempt) : null;
|
|
186
|
+
if (attempt === null) throw new Error("Conversation binding carries an invalid parent attempt");
|
|
187
|
+
parentTask = taskAttemptRef(taskId, attempt);
|
|
188
|
+
}
|
|
189
|
+
return {
|
|
190
|
+
replyTo,
|
|
191
|
+
repositoryEnvelopes,
|
|
192
|
+
parentTask
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
function parseTaskAttempt(value) {
|
|
196
|
+
if (value === null || !/^[1-9]\d*$/u.test(value)) return null;
|
|
197
|
+
const attempt = Number(value);
|
|
198
|
+
return Number.isSafeInteger(attempt) ? attempt : null;
|
|
199
|
+
}
|
|
200
|
+
//#endregion
|
|
201
|
+
export { conversationBinding, conversationBindingForSession, conversationHeaders, factoryReplyAddressAttribute, factoryReplyAddressHeader, factoryReplyChannelAttribute, factoryReplyChannelHeader, factoryRepositoryIdsAttribute, factoryRepositoryIdsHeader, factoryTaskAttemptAttribute, factoryTaskAttemptHeader, factoryTaskAttribute, factoryTaskHeader, hasFactoryTaskBinding, optionalTaskIdForSession, replyToForSession, repositoryIdsForSession, requireTaskIdForSession, taskBindingForSession, taskHeaders, taskSessionAuth, withFactoryTask, withOptionalFactoryTask };
|
|
202
|
+
|
|
203
|
+
//# sourceMappingURL=task-session.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"task-session.mjs","names":[],"sources":["../../src/eve/task-session.ts"],"sourcesContent":["import { repositoryIdSchema, taskIdSchema } from \"../schema/id\";\nimport { replyToSchema, type ReplyTo } from \"../schema/common\";\nimport { taskAttemptRef, type TaskAttemptRef } from \"../schema/execution\";\nimport type { RepositoryId, TaskId } from \"../schema/id\";\nimport { ForbiddenError, type AuthFn } from \"eve/channels/auth\";\nimport type { SessionContext } from \"eve/context\";\n\n/** HTTP header that carries the authenticated Factory Task ID into an Eve session. */\nexport const factoryTaskHeader = \"x-factory-task-id\";\n/** Eve authentication attribute that stores the bound Factory Task ID. */\nexport const factoryTaskAttribute = \"factoryTaskId\";\n/** HTTP header that carries the Task attempt fence into an Eve session. */\nexport const factoryTaskAttemptHeader = \"x-factory-task-attempt\";\n/** Eve authentication attribute that stores the bound Task attempt fence. */\nexport const factoryTaskAttemptAttribute = \"factoryTaskAttempt\";\n/** HTTP header that carries the authenticated repository authorization envelope. */\nexport const factoryRepositoryIdsHeader = \"x-factory-repository-ids\";\n/** HTTP header that carries the authenticated conversation reply channel. */\nexport const factoryReplyChannelHeader = \"x-factory-reply-channel\";\n/** HTTP header that carries the authenticated conversation reply address. */\nexport const factoryReplyAddressHeader = \"x-factory-reply-address\";\n/** Eve authentication attribute that stores the authorized repository IDs. */\nexport const factoryRepositoryIdsAttribute = \"factoryRepositoryIds\";\n/** Eve authentication attribute that stores the conversation reply channel. */\nexport const factoryReplyChannelAttribute = \"factoryReplyChannel\";\n/** Eve authentication attribute that stores the conversation reply address. */\nexport const factoryReplyAddressAttribute = \"factoryReplyAddress\";\n\ntype SessionInitiator = SessionContext[\"session\"][\"auth\"][\"initiator\"];\ntype AuthenticatedSessionInitiator = NonNullable<SessionInitiator>;\n\n/** The repository authorization envelope and reply destination bound to a conversation. */\nexport interface FactoryConversationSessionBinding {\n repositoryIds: readonly RepositoryId[];\n replyTo: ReplyTo;\n}\n\n/** Test whether authenticated Eve session state declares a Factory Task binding. */\nexport function hasFactoryTaskBinding(auth: SessionInitiator): boolean {\n return auth?.attributes[factoryTaskAttribute] !== undefined;\n}\n\n/** Encode a Task ID and attempt fence as headers for an Eve session request. */\nexport function taskHeaders(task: TaskAttemptRef): Record<string, string> {\n return {\n [factoryTaskHeader]: task.taskId,\n [factoryTaskAttemptHeader]: String(task.attempt),\n };\n}\n\n/** Encode a conversation's repository scope and reply destination as Eve request headers. */\nexport function conversationHeaders(input: {\n repositoryIds: readonly RepositoryId[];\n replyTo: ReplyTo;\n}): Record<string, string> {\n return {\n [factoryRepositoryIdsHeader]: input.repositoryIds.join(\",\"),\n [factoryReplyChannelHeader]: input.replyTo.channel,\n [factoryReplyAddressHeader]: input.replyTo.address,\n };\n}\n\n/** Add a validated Task ID and attempt fence to an authenticated Eve initiator. */\nexport function taskSessionAuth(\n auth: AuthenticatedSessionInitiator,\n task: TaskAttemptRef,\n): AuthenticatedSessionInitiator {\n return {\n ...auth,\n attributes: {\n ...auth.attributes,\n [factoryTaskAttribute]: task.taskId,\n [factoryTaskAttemptAttribute]: String(task.attempt),\n },\n };\n}\n\n/** Require valid Factory Task headers and bind them into the authenticated Eve initiator. */\nexport function withFactoryTask(authenticate: AuthFn<Request>): AuthFn<Request> {\n return async (request) => {\n const auth = await authenticate(request);\n if (auth == null) return auth;\n const parsedTask = taskIdSchema.safeParse(request.headers.get(factoryTaskHeader));\n const parsedAttempt = parseTaskAttempt(request.headers.get(factoryTaskAttemptHeader));\n if (!parsedTask.success || parsedAttempt === null) throw new ForbiddenError();\n return taskSessionAuth(auth, taskAttemptRef(parsedTask.data, parsedAttempt));\n };\n}\n\n/** Bind optional Task and conversation headers while rejecting partial or malformed context. */\nexport function withOptionalFactoryTask(authenticate: AuthFn<Request>): AuthFn<Request> {\n return async (request) => {\n const auth = await authenticate(request);\n if (auth == null) return auth;\n const taskId = request.headers.get(factoryTaskHeader);\n const taskAttempt = request.headers.get(factoryTaskAttemptHeader);\n const repositoryIds = request.headers.get(factoryRepositoryIdsHeader);\n const replyChannel = request.headers.get(factoryReplyChannelHeader);\n const replyAddress = request.headers.get(factoryReplyAddressHeader);\n const hasConversationBinding =\n repositoryIds !== null || replyChannel !== null || replyAddress !== null;\n if (taskId === null && taskAttempt === null && !hasConversationBinding) return auth;\n const parsedTask = taskId === null ? null : taskIdSchema.safeParse(taskId);\n const parsedAttempt = parseTaskAttempt(taskAttempt);\n const parsedRepositories =\n repositoryIds === null\n ? null\n : repositoryIdSchema.array().min(1).safeParse(repositoryIds.split(\",\").filter(Boolean));\n const parsedReplyTo =\n replyChannel === null || replyAddress === null\n ? null\n : replyToSchema.safeParse({ channel: replyChannel, address: replyAddress });\n if (\n (parsedTask !== null && !parsedTask.success) ||\n (parsedTask === null) !== (parsedAttempt === null) ||\n (hasConversationBinding &&\n (parsedRepositories === null ||\n !parsedRepositories.success ||\n parsedReplyTo === null ||\n !parsedReplyTo.success))\n ) {\n throw new ForbiddenError();\n }\n const conversationAttributes: Record<string, string> =\n parsedRepositories?.success === true && parsedReplyTo?.success === true\n ? {\n [factoryRepositoryIdsAttribute]: parsedRepositories.data.join(\",\"),\n [factoryReplyChannelAttribute]: parsedReplyTo.data.channel,\n [factoryReplyAddressAttribute]: parsedReplyTo.data.address,\n }\n : {};\n return {\n ...auth,\n attributes: {\n ...auth.attributes,\n ...(parsedTask === null || parsedAttempt === null\n ? {}\n : {\n [factoryTaskAttribute]: parsedTask.data,\n [factoryTaskAttemptAttribute]: String(parsedAttempt),\n }),\n ...conversationAttributes,\n },\n };\n };\n}\n\n/** Read the required Factory Task ID from authenticated Eve session state. */\nexport function requireTaskIdForSession(auth: SessionInitiator): TaskId {\n return taskIdSchema.parse(auth?.attributes[factoryTaskAttribute]);\n}\n\n/** Read an optional Task ID from authenticated session state, rejecting malformed values. */\nexport function optionalTaskIdForSession(auth: SessionInitiator): TaskId | null {\n const value = auth?.attributes[factoryTaskAttribute];\n return value === undefined ? null : taskIdSchema.parse(value);\n}\n\n/** Read the complete, attempt-fenced Task binding established by Factory authentication. */\nexport function taskBindingForSession(auth: SessionInitiator): TaskAttemptRef {\n const taskId = requireTaskIdForSession(auth);\n const rawAttempt = auth?.attributes[factoryTaskAttemptAttribute];\n const attempt = parseTaskAttempt(typeof rawAttempt === \"string\" ? rawAttempt : null);\n if (attempt === null) throw new Error(\"Factory session Task attempt binding is invalid.\");\n return { taskId, attempt };\n}\n\n/** Read the repository authorization envelope, or null when the session has none. */\nexport function repositoryIdsForSession(auth: SessionInitiator): readonly RepositoryId[] | null {\n const value = auth?.attributes[factoryRepositoryIdsAttribute];\n if (value === undefined) return null;\n return repositoryIdSchema\n .array()\n .min(1)\n .parse(typeof value === \"string\" ? value.split(\",\").filter(Boolean) : value);\n}\n\n/** Read the authenticated reply destination, or null when the session has none. */\nexport function replyToForSession(auth: SessionInitiator): ReplyTo | null {\n const channel = auth?.attributes[factoryReplyChannelAttribute];\n const address = auth?.attributes[factoryReplyAddressAttribute];\n if (channel === undefined && address === undefined) return null;\n return replyToSchema.parse({ channel, address });\n}\n\n/** Read the complete conversation binding, rejecting partial authentication state. */\nexport function conversationBindingForSession(\n auth: SessionInitiator,\n): FactoryConversationSessionBinding | null {\n const repositoryIds = repositoryIdsForSession(auth);\n const replyTo = replyToForSession(auth);\n if (repositoryIds === null && replyTo === null) return null;\n if (repositoryIds === null || replyTo === null) {\n throw new Error(\"Factory session conversation binding is incomplete.\");\n }\n return { repositoryIds, replyTo };\n}\n\n/** Authenticated conversation authority assembled from both initiator and current session state. */\nexport interface ConversationBinding {\n replyTo: ReplyTo;\n /**\n * Repository envelopes the binding carries: the initiator's, plus the current\n * auth's when it restates one. Admitted work must sit inside every envelope.\n */\n repositoryEnvelopes: readonly (readonly RepositoryId[])[];\n parentTask?: TaskAttemptRef;\n}\n\nconst conversationBindingAttributes = [\n factoryReplyChannelAttribute,\n factoryReplyAddressAttribute,\n factoryTaskAttribute,\n factoryTaskAttemptAttribute,\n] as const;\n\n/**\n * Read and validate the conversation binding carried on a session's auth pair.\n * The initiator's attributes are authoritative; any binding attribute the\n * current auth restates must agree with them. This is the only reader of the\n * binding attribute encoding; callers never touch `attributes` themselves.\n *\n * Distinct from `conversationBindingForSession`, which reads the initiator\n * alone: admission needs every envelope the pair carries, so that work cannot\n * be admitted into a repository only one side of the pair authorizes.\n */\nexport function conversationBinding(auth: {\n initiator: AuthenticatedSessionInitiator;\n current: AuthenticatedSessionInitiator;\n}): ConversationBinding {\n const attributes = auth.initiator.attributes;\n const replyTo = replyToSchema.parse({\n channel: attributes[factoryReplyChannelAttribute],\n address: attributes[factoryReplyAddressAttribute],\n });\n for (const key of conversationBindingAttributes) {\n if (\n auth.current.attributes[key] !== undefined &&\n auth.current.attributes[key] !== attributes[key]\n )\n throw new Error(\"Conversation binding does not match the authenticated conversation\");\n }\n const repositoryEnvelopes: (readonly RepositoryId[])[] = [];\n for (const [index, entry] of [auth.initiator, auth.current].entries()) {\n const raw = entry.attributes[factoryRepositoryIdsAttribute];\n if (index === 1 && raw === undefined) continue;\n repositoryEnvelopes.push(\n repositoryIdSchema\n .array()\n .min(1)\n .parse(typeof raw === \"string\" ? raw.split(\",\") : raw),\n );\n }\n let parentTask: ConversationBinding[\"parentTask\"];\n if (\n attributes[factoryTaskAttribute] !== undefined ||\n attributes[factoryTaskAttemptAttribute] !== undefined\n ) {\n const taskId = taskIdSchema.parse(attributes[factoryTaskAttribute]);\n const rawAttempt = attributes[factoryTaskAttemptAttribute];\n const attempt = typeof rawAttempt === \"string\" ? parseTaskAttempt(rawAttempt) : null;\n if (attempt === null) throw new Error(\"Conversation binding carries an invalid parent attempt\");\n parentTask = taskAttemptRef(taskId, attempt);\n }\n return { replyTo, repositoryEnvelopes, parentTask };\n}\n\nfunction parseTaskAttempt(value: string | null): number | null {\n if (value === null || !/^[1-9]\\d*$/u.test(value)) return null;\n const attempt = Number(value);\n return Number.isSafeInteger(attempt) ? attempt : null;\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { RepositoryId } from "../schema/id.mjs";
|
|
2
|
+
import { MaybePromise, ReplyTo } from "../schema/common.mjs";
|
|
3
|
+
import { TaskAttemptRef } from "../schema/execution.mjs";
|
|
4
|
+
import { FactoryStores } from "../store/engine.mjs";
|
|
5
|
+
//#region src/eve/transcript.d.ts
|
|
6
|
+
/** Identifies the Eve execution and authenticated attributes to project into a transcript. */
|
|
7
|
+
interface TranscriptMaterializationInput {
|
|
8
|
+
agent: string;
|
|
9
|
+
execution: {
|
|
10
|
+
provider: "eve";
|
|
11
|
+
sessionId: string;
|
|
12
|
+
};
|
|
13
|
+
attributes: Readonly<Record<string, string | readonly string[]>>;
|
|
14
|
+
}
|
|
15
|
+
/** The Task and conversation authorization context forwarded when reading an Eve transcript. */
|
|
16
|
+
interface EveTranscriptRequestScope {
|
|
17
|
+
task?: TaskAttemptRef;
|
|
18
|
+
conversation?: {
|
|
19
|
+
repositoryIds: readonly RepositoryId[];
|
|
20
|
+
replyTo: ReplyTo;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
/** Configures durable transcript storage and authenticated access to Eve session events. */
|
|
24
|
+
interface EveTranscriptMaterializerOptions {
|
|
25
|
+
stores: Pick<FactoryStores, "transcripts">;
|
|
26
|
+
origin: string | (() => string);
|
|
27
|
+
headers?(scope: EveTranscriptRequestScope): MaybePromise<HeadersInit>;
|
|
28
|
+
fetch?: typeof globalThis.fetch;
|
|
29
|
+
timeoutMs?: number;
|
|
30
|
+
}
|
|
31
|
+
/** Materializes one Eve execution stream into Factory's browser-safe transcript store. */
|
|
32
|
+
type EveTranscriptMaterializer = (input: TranscriptMaterializationInput) => Promise<void>;
|
|
33
|
+
/** Materialize a browser-safe transcript projection from one durable Eve session stream. */
|
|
34
|
+
declare function createEveTranscriptMaterializer(options: EveTranscriptMaterializerOptions): EveTranscriptMaterializer;
|
|
35
|
+
//#endregion
|
|
36
|
+
export { EveTranscriptMaterializer, EveTranscriptMaterializerOptions, EveTranscriptRequestScope, TranscriptMaterializationInput, createEveTranscriptMaterializer };
|
|
37
|
+
//# sourceMappingURL=transcript.d.mts.map
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { repositoryIdSchema, taskIdSchema } from "../schema/id.mjs";
|
|
2
|
+
import { replyToSchema } from "../schema/common.mjs";
|
|
3
|
+
import { taskAttemptRef } from "../schema/execution.mjs";
|
|
4
|
+
import { factoryReplyAddressAttribute, factoryReplyChannelAttribute, factoryRepositoryIdsAttribute, factoryTaskAttemptAttribute, factoryTaskAttribute } from "./task-session.mjs";
|
|
5
|
+
import { readEvents } from "../client-events.mjs";
|
|
6
|
+
import { Transcript } from "../client-transcript.mjs";
|
|
7
|
+
//#region src/eve/transcript.ts
|
|
8
|
+
function attribute(attributes, name) {
|
|
9
|
+
const value = attributes[name];
|
|
10
|
+
return typeof value === "string" ? value : void 0;
|
|
11
|
+
}
|
|
12
|
+
function scopeFor(input) {
|
|
13
|
+
const taskId = taskIdSchema.safeParse(attribute(input.attributes, factoryTaskAttribute));
|
|
14
|
+
const attempt = Number(attribute(input.attributes, factoryTaskAttemptAttribute));
|
|
15
|
+
const repositories = repositoryIdSchema.array().min(1).safeParse(attribute(input.attributes, factoryRepositoryIdsAttribute)?.split(",").filter(Boolean));
|
|
16
|
+
const replyTo = replyToSchema.safeParse({
|
|
17
|
+
channel: attribute(input.attributes, factoryReplyChannelAttribute),
|
|
18
|
+
address: attribute(input.attributes, factoryReplyAddressAttribute)
|
|
19
|
+
});
|
|
20
|
+
return {
|
|
21
|
+
...taskId.success && Number.isSafeInteger(attempt) && attempt > 0 ? { task: taskAttemptRef(taskId.data, attempt) } : {},
|
|
22
|
+
...repositories.success && replyTo.success ? { conversation: {
|
|
23
|
+
repositoryIds: repositories.data,
|
|
24
|
+
replyTo: replyTo.data
|
|
25
|
+
} } : {}
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
/** Materialize a browser-safe transcript projection from one durable Eve session stream. */
|
|
29
|
+
function createEveTranscriptMaterializer(options) {
|
|
30
|
+
const fetchImplementation = options.fetch ?? globalThis.fetch;
|
|
31
|
+
const timeoutMs = options.timeoutMs ?? 3e4;
|
|
32
|
+
return async (input) => {
|
|
33
|
+
const previous = await options.stores.transcripts.get(input.execution);
|
|
34
|
+
const transcript = new Transcript();
|
|
35
|
+
if (previous !== null) transcript.hydrate(previous);
|
|
36
|
+
const query = new URLSearchParams({
|
|
37
|
+
startIndex: String(transcript.cursor),
|
|
38
|
+
includeTailIndex: "1"
|
|
39
|
+
});
|
|
40
|
+
const origin = typeof options.origin === "string" ? options.origin : options.origin();
|
|
41
|
+
const response = await fetchImplementation(`${origin.replace(/\/+$/u, "")}/eve/agents/${encodeURIComponent(input.agent)}/eve/v1/session/${encodeURIComponent(input.execution.sessionId)}/stream?${query}`, {
|
|
42
|
+
headers: await options.headers?.(scopeFor(input)),
|
|
43
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
44
|
+
redirect: "error"
|
|
45
|
+
});
|
|
46
|
+
if (!response.ok || !response.body) {
|
|
47
|
+
await response.body?.cancel();
|
|
48
|
+
throw new Error(`Eve refused transcript materialization: ${response.status}`);
|
|
49
|
+
}
|
|
50
|
+
const tailValue = response.headers.get("x-eve-stream-tail-index");
|
|
51
|
+
const tail = tailValue === null ? NaN : Number(tailValue);
|
|
52
|
+
if (!Number.isSafeInteger(tail) || tail < -1) {
|
|
53
|
+
await response.body.cancel();
|
|
54
|
+
throw new Error("Eve transcript materialization did not include a valid durable cursor");
|
|
55
|
+
}
|
|
56
|
+
if (transcript.cursor <= tail) for await (const event of readEvents(response.body)) transcript.accept(event);
|
|
57
|
+
else await response.body.cancel();
|
|
58
|
+
if (transcript.cursor !== tail + 1) throw new Error("Eve transcript materialization closed before its durable cursor");
|
|
59
|
+
await options.stores.transcripts.put(input.execution, transcript.snapshot());
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
//#endregion
|
|
63
|
+
export { createEveTranscriptMaterializer };
|
|
64
|
+
|
|
65
|
+
//# sourceMappingURL=transcript.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transcript.mjs","names":[],"sources":["../../src/eve/transcript.ts"],"sourcesContent":["import { readEvents } from \"../client-events\";\nimport { Transcript } from \"../client-transcript\";\nimport { replyToSchema, type MaybePromise, type ReplyTo } from \"../schema/common\";\nimport { taskAttemptRef, type TaskAttemptRef } from \"../schema/execution\";\nimport { repositoryIdSchema, taskIdSchema, type RepositoryId } from \"../schema/id\";\nimport type { FactoryStores } from \"../store/engine\";\nimport {\n factoryReplyAddressAttribute,\n factoryReplyChannelAttribute,\n factoryRepositoryIdsAttribute,\n factoryTaskAttemptAttribute,\n factoryTaskAttribute,\n} from \"./task-session\";\n\n/** Identifies the Eve execution and authenticated attributes to project into a transcript. */\nexport interface TranscriptMaterializationInput {\n agent: string;\n execution: { provider: \"eve\"; sessionId: string };\n attributes: Readonly<Record<string, string | readonly string[]>>;\n}\n\n/** The Task and conversation authorization context forwarded when reading an Eve transcript. */\nexport interface EveTranscriptRequestScope {\n task?: TaskAttemptRef;\n conversation?: { repositoryIds: readonly RepositoryId[]; replyTo: ReplyTo };\n}\n\n/** Configures durable transcript storage and authenticated access to Eve session events. */\nexport interface EveTranscriptMaterializerOptions {\n stores: Pick<FactoryStores, \"transcripts\">;\n origin: string | (() => string);\n headers?(scope: EveTranscriptRequestScope): MaybePromise<HeadersInit>;\n fetch?: typeof globalThis.fetch;\n timeoutMs?: number;\n}\n\n/** Materializes one Eve execution stream into Factory's browser-safe transcript store. */\nexport type EveTranscriptMaterializer = (input: TranscriptMaterializationInput) => Promise<void>;\n\nfunction attribute(\n attributes: TranscriptMaterializationInput[\"attributes\"],\n name: string,\n): string | undefined {\n const value = attributes[name];\n return typeof value === \"string\" ? value : undefined;\n}\n\nfunction scopeFor(input: TranscriptMaterializationInput): EveTranscriptRequestScope {\n const taskId = taskIdSchema.safeParse(attribute(input.attributes, factoryTaskAttribute));\n const attempt = Number(attribute(input.attributes, factoryTaskAttemptAttribute));\n const repositories = repositoryIdSchema\n .array()\n .min(1)\n .safeParse(\n attribute(input.attributes, factoryRepositoryIdsAttribute)?.split(\",\").filter(Boolean),\n );\n const replyTo = replyToSchema.safeParse({\n channel: attribute(input.attributes, factoryReplyChannelAttribute),\n address: attribute(input.attributes, factoryReplyAddressAttribute),\n });\n return {\n ...(taskId.success && Number.isSafeInteger(attempt) && attempt > 0\n ? { task: taskAttemptRef(taskId.data, attempt) }\n : {}),\n ...(repositories.success && replyTo.success\n ? { conversation: { repositoryIds: repositories.data, replyTo: replyTo.data } }\n : {}),\n };\n}\n\n/** Materialize a browser-safe transcript projection from one durable Eve session stream. */\nexport function createEveTranscriptMaterializer(\n options: EveTranscriptMaterializerOptions,\n): EveTranscriptMaterializer {\n const fetchImplementation = options.fetch ?? globalThis.fetch;\n const timeoutMs = options.timeoutMs ?? 30_000;\n return async (input: TranscriptMaterializationInput): Promise<void> => {\n const previous = await options.stores.transcripts.get(input.execution);\n const transcript = new Transcript();\n if (previous !== null) transcript.hydrate(previous);\n const query = new URLSearchParams({\n startIndex: String(transcript.cursor),\n includeTailIndex: \"1\",\n });\n const origin = typeof options.origin === \"string\" ? options.origin : options.origin();\n const response = await fetchImplementation(\n `${origin.replace(/\\/+$/u, \"\")}/eve/agents/${encodeURIComponent(input.agent)}/eve/v1/session/${encodeURIComponent(input.execution.sessionId)}/stream?${query}`,\n {\n headers: await options.headers?.(scopeFor(input)),\n signal: AbortSignal.timeout(timeoutMs),\n redirect: \"error\",\n },\n );\n if (!response.ok || !response.body) {\n await response.body?.cancel();\n throw new Error(`Eve refused transcript materialization: ${response.status}`);\n }\n const tailValue = response.headers.get(\"x-eve-stream-tail-index\");\n const tail = tailValue === null ? Number.NaN : Number(tailValue);\n if (!Number.isSafeInteger(tail) || tail < -1) {\n await response.body.cancel();\n throw new Error(\"Eve transcript materialization did not include a valid durable cursor\");\n }\n if (transcript.cursor <= tail) {\n for await (const event of readEvents(response.body)) transcript.accept(event);\n } else {\n await response.body.cancel();\n }\n if (transcript.cursor !== tail + 1) {\n throw new Error(\"Eve transcript materialization closed before its durable cursor\");\n }\n await options.stores.transcripts.put(input.execution, transcript.snapshot());\n };\n}\n"],"mappings":";;;;;;;AAuCA,SAAS,UACP,YACA,MACoB;CACpB,MAAM,QAAQ,WAAW;CACzB,OAAO,OAAO,UAAU,WAAW,QAAQ,KAAA;AAC7C;AAEA,SAAS,SAAS,OAAkE;CAClF,MAAM,SAAS,aAAa,UAAU,UAAU,MAAM,YAAY,oBAAoB,CAAC;CACvF,MAAM,UAAU,OAAO,UAAU,MAAM,YAAY,2BAA2B,CAAC;CAC/E,MAAM,eAAe,mBAClB,MAAM,CAAC,CACP,IAAI,CAAC,CAAC,CACN,UACC,UAAU,MAAM,YAAY,6BAA6B,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,OAAO,OAAO,CACvF;CACF,MAAM,UAAU,cAAc,UAAU;EACtC,SAAS,UAAU,MAAM,YAAY,4BAA4B;EACjE,SAAS,UAAU,MAAM,YAAY,4BAA4B;CACnE,CAAC;CACD,OAAO;EACL,GAAI,OAAO,WAAW,OAAO,cAAc,OAAO,KAAK,UAAU,IAC7D,EAAE,MAAM,eAAe,OAAO,MAAM,OAAO,EAAE,IAC7C,CAAC;EACL,GAAI,aAAa,WAAW,QAAQ,UAChC,EAAE,cAAc;GAAE,eAAe,aAAa;GAAM,SAAS,QAAQ;EAAK,EAAE,IAC5E,CAAC;CACP;AACF;;AAGA,SAAgB,gCACd,SAC2B;CAC3B,MAAM,sBAAsB,QAAQ,SAAS,WAAW;CACxD,MAAM,YAAY,QAAQ,aAAa;CACvC,OAAO,OAAO,UAAyD;EACrE,MAAM,WAAW,MAAM,QAAQ,OAAO,YAAY,IAAI,MAAM,SAAS;EACrE,MAAM,aAAa,IAAI,WAAW;EAClC,IAAI,aAAa,MAAM,WAAW,QAAQ,QAAQ;EAClD,MAAM,QAAQ,IAAI,gBAAgB;GAChC,YAAY,OAAO,WAAW,MAAM;GACpC,kBAAkB;EACpB,CAAC;EACD,MAAM,SAAS,OAAO,QAAQ,WAAW,WAAW,QAAQ,SAAS,QAAQ,OAAO;EACpF,MAAM,WAAW,MAAM,oBACrB,GAAG,OAAO,QAAQ,SAAS,EAAE,EAAE,cAAc,mBAAmB,MAAM,KAAK,EAAE,kBAAkB,mBAAmB,MAAM,UAAU,SAAS,EAAE,UAAU,SACvJ;GACE,SAAS,MAAM,QAAQ,UAAU,SAAS,KAAK,CAAC;GAChD,QAAQ,YAAY,QAAQ,SAAS;GACrC,UAAU;EACZ,CACF;EACA,IAAI,CAAC,SAAS,MAAM,CAAC,SAAS,MAAM;GAClC,MAAM,SAAS,MAAM,OAAO;GAC5B,MAAM,IAAI,MAAM,2CAA2C,SAAS,QAAQ;EAC9E;EACA,MAAM,YAAY,SAAS,QAAQ,IAAI,yBAAyB;EAChE,MAAM,OAAO,cAAc,OAAO,MAAa,OAAO,SAAS;EAC/D,IAAI,CAAC,OAAO,cAAc,IAAI,KAAK,OAAO,IAAI;GAC5C,MAAM,SAAS,KAAK,OAAO;GAC3B,MAAM,IAAI,MAAM,uEAAuE;EACzF;EACA,IAAI,WAAW,UAAU,MACvB,WAAW,MAAM,SAAS,WAAW,SAAS,IAAI,GAAG,WAAW,OAAO,KAAK;OAE5E,MAAM,SAAS,KAAK,OAAO;EAE7B,IAAI,WAAW,WAAW,OAAO,GAC/B,MAAM,IAAI,MAAM,iEAAiE;EAEnF,MAAM,QAAQ,OAAO,YAAY,IAAI,MAAM,WAAW,WAAW,SAAS,CAAC;CAC7E;AACF"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { WorkNotification, WorkNotifications, workNotificationSchema } from "./store/work-notifications.mjs";
|
|
2
|
+
import { CodingExecutionRequest, ExecutionArtifact, ExecutionLaunchKey, ExecutionResult, TaskAttemptRef, codingExecutionRequestSchema, executionArtifactSchema, executionLaunchKeySchema, executionResultSchema, taskAttemptRef, taskAttemptRefFromTask, taskAttemptRefSchema } from "./schema/execution.mjs";
|
|
3
|
+
import { RecordTaskExecutionInput, RecordTaskUsageInput, StepUsage, TaskExecutionFence } from "./store/engine.mjs";
|
|
4
|
+
import { BudgetExhaustedError, DEFAULT_MAX_LAUNCH_ATTEMPTS, DispatchBudget, DispatchOptions, DispatchRefusalCode, DispatchRefusedError, DispatchStores, DispatchTaskInput, ExecutionLaunchPendingError, dispatchTask } from "./dispatch.mjs";
|
|
5
|
+
import { StallSweepOptions, TaskStallSweepResult, TaskStallSweepStores, stallWindowMinutes, sweepTaskStalls } from "./stall.mjs";
|
|
6
|
+
import { CancelExecutionOptions, CancelExecutionStores, ExecutionAdapter, ExecutionFenceError, ExecutionInspectionResult, InspectExecutionOptions, InspectExecutionStores, LaunchExecutionOptions, LaunchExecutionStores, RecordExecutionUsageOptions, RecordExecutionUsageStores, StartExecutionInput, cancelExecution, executionLaunchKey, inspectExecution, launchExecution, recordExecutionUsage } from "./execution.mjs";
|
|
7
|
+
import { SweepFailureReasonCode, SweepQueuedOptions, SweepQueuedResult, SweepQueuedStores, SweepSkipReasonCode, TaskLauncher, sweepQueued } from "./sweep.mjs";
|
|
8
|
+
export { BudgetExhaustedError, type CancelExecutionOptions, type CancelExecutionStores, type CodingExecutionRequest, DEFAULT_MAX_LAUNCH_ATTEMPTS, type DispatchBudget, type DispatchOptions, type DispatchRefusalCode, DispatchRefusedError, type DispatchStores, type DispatchTaskInput, type ExecutionAdapter, type ExecutionArtifact, ExecutionFenceError, type ExecutionInspectionResult, type ExecutionLaunchKey, ExecutionLaunchPendingError, type ExecutionResult, type InspectExecutionOptions, type InspectExecutionStores, type LaunchExecutionOptions, type LaunchExecutionStores, type RecordExecutionUsageOptions, type RecordExecutionUsageStores, type RecordTaskExecutionInput, type RecordTaskUsageInput, type StallSweepOptions, type StartExecutionInput, type StepUsage, type SweepFailureReasonCode, type SweepQueuedOptions, type SweepQueuedResult, type SweepQueuedStores, type SweepSkipReasonCode, type TaskAttemptRef, type TaskExecutionFence, type TaskLauncher, type TaskStallSweepResult, type TaskStallSweepStores, type WorkNotification, type WorkNotifications, cancelExecution, codingExecutionRequestSchema, dispatchTask, executionArtifactSchema, executionLaunchKey, executionLaunchKeySchema, executionResultSchema, inspectExecution, launchExecution, recordExecutionUsage, stallWindowMinutes, sweepQueued, sweepTaskStalls, taskAttemptRef, taskAttemptRefFromTask, taskAttemptRefSchema, workNotificationSchema };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { codingExecutionRequestSchema, executionArtifactSchema, executionLaunchKeySchema, executionResultSchema, taskAttemptRef, taskAttemptRefFromTask, taskAttemptRefSchema } from "./schema/execution.mjs";
|
|
2
|
+
import { BudgetExhaustedError, DEFAULT_MAX_LAUNCH_ATTEMPTS, DispatchRefusedError, ExecutionLaunchPendingError, dispatchTask } from "./dispatch.mjs";
|
|
3
|
+
import { workNotificationSchema } from "./store/work-notifications.mjs";
|
|
4
|
+
import { ExecutionFenceError, cancelExecution, executionLaunchKey, inspectExecution, launchExecution, recordExecutionUsage } from "./execution.mjs";
|
|
5
|
+
import { sweepQueued } from "./sweep.mjs";
|
|
6
|
+
import { stallWindowMinutes, sweepTaskStalls } from "./stall.mjs";
|
|
7
|
+
export { BudgetExhaustedError, DEFAULT_MAX_LAUNCH_ATTEMPTS, DispatchRefusedError, ExecutionFenceError, ExecutionLaunchPendingError, cancelExecution, codingExecutionRequestSchema, dispatchTask, executionArtifactSchema, executionLaunchKey, executionLaunchKeySchema, executionResultSchema, inspectExecution, launchExecution, recordExecutionUsage, stallWindowMinutes, sweepQueued, sweepTaskStalls, taskAttemptRef, taskAttemptRefFromTask, taskAttemptRefSchema, workNotificationSchema };
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { ExecutionReference, Task } from "./schema/task.mjs";
|
|
2
|
+
import { CodingExecutionRequest, ExecutionLaunchKey, ExecutionResult, TaskAttemptRef } from "./schema/execution.mjs";
|
|
3
|
+
import { FactoryStores } from "./store/engine.mjs";
|
|
4
|
+
//#region src/execution.d.ts
|
|
5
|
+
/** Validated request and stable idempotency key passed to an execution provider. */
|
|
6
|
+
interface StartExecutionInput {
|
|
7
|
+
readonly request: CodingExecutionRequest;
|
|
8
|
+
readonly launchKey: ExecutionLaunchKey;
|
|
9
|
+
}
|
|
10
|
+
/** Current provider state when inspecting one recorded execution. */
|
|
11
|
+
type ExecutionInspectionResult = {
|
|
12
|
+
status: "active";
|
|
13
|
+
} | {
|
|
14
|
+
status: "expired";
|
|
15
|
+
} | {
|
|
16
|
+
status: "finished";
|
|
17
|
+
result: ExecutionResult;
|
|
18
|
+
};
|
|
19
|
+
/** Provider-neutral boundary for retry-safe execution start, inspection, and cancellation. */
|
|
20
|
+
interface ExecutionAdapter {
|
|
21
|
+
/** Retry-safe by key. Throw ExecutionLaunchPendingError only for ambiguous acceptance; other errors mean no launch. */
|
|
22
|
+
start(input: StartExecutionInput): Promise<ExecutionReference>;
|
|
23
|
+
/** Only active confirms that reattachment is safe. Missing/expired execution must never start implicitly. */
|
|
24
|
+
inspect(execution: ExecutionReference): Promise<ExecutionInspectionResult>;
|
|
25
|
+
/** Idempotent stop, returning final observed usage. May report cancellation even if interruption is unsupported. */
|
|
26
|
+
stop(execution: ExecutionReference): Promise<ExecutionResult>;
|
|
27
|
+
}
|
|
28
|
+
/** Error raised when an execution no longer matches the current running Task attempt. */
|
|
29
|
+
declare class ExecutionFenceError extends Error {}
|
|
30
|
+
/** Store capabilities required to launch or reattach a coding execution. */
|
|
31
|
+
type LaunchExecutionStores = Pick<FactoryStores, "repositories" | "tasks">;
|
|
32
|
+
/** Store capabilities required to inspect a coding execution. */
|
|
33
|
+
type InspectExecutionStores = Pick<FactoryStores, "tasks">;
|
|
34
|
+
/** Store capabilities required to persist final execution usage. */
|
|
35
|
+
type RecordExecutionUsageStores = Pick<FactoryStores, "receipts" | "tasks">;
|
|
36
|
+
/** Store capabilities required to cancel and settle a coding execution. */
|
|
37
|
+
type CancelExecutionStores = RecordExecutionUsageStores;
|
|
38
|
+
/** Construct the stable provider idempotency key for one exact Task attempt. */
|
|
39
|
+
declare function executionLaunchKey(ref: TaskAttemptRef): ExecutionLaunchKey;
|
|
40
|
+
/** Dependencies and request used to start or reattach one coding execution. */
|
|
41
|
+
interface LaunchExecutionOptions {
|
|
42
|
+
readonly stores: LaunchExecutionStores;
|
|
43
|
+
readonly request: CodingExecutionRequest;
|
|
44
|
+
readonly adapter: ExecutionAdapter;
|
|
45
|
+
}
|
|
46
|
+
/** Called inside a durable workflow step after dispatch gates. No provider lifecycle is stored here. */
|
|
47
|
+
declare function launchExecution(options: LaunchExecutionOptions): Promise<ExecutionReference>;
|
|
48
|
+
/** Dependencies and Task-attempt identity used to inspect one execution. */
|
|
49
|
+
interface InspectExecutionOptions {
|
|
50
|
+
readonly stores: InspectExecutionStores;
|
|
51
|
+
readonly task: TaskAttemptRef;
|
|
52
|
+
readonly adapter: ExecutionAdapter;
|
|
53
|
+
}
|
|
54
|
+
/** Read a result without granting it lifecycle authority; workflows validate evidence and choose the next state. */
|
|
55
|
+
declare function inspectExecution(options: InspectExecutionOptions): Promise<ExecutionInspectionResult>;
|
|
56
|
+
/** Dependencies, Task-attempt identity, and provider result used to record usage. */
|
|
57
|
+
interface RecordExecutionUsageOptions {
|
|
58
|
+
readonly stores: RecordExecutionUsageStores;
|
|
59
|
+
readonly task: TaskAttemptRef;
|
|
60
|
+
readonly result: ExecutionResult;
|
|
61
|
+
}
|
|
62
|
+
/** For adapters reporting one immutable final usage packet instead of per-step usage hooks. */
|
|
63
|
+
declare function recordExecutionUsage(options: RecordExecutionUsageOptions): Promise<Task>;
|
|
64
|
+
/** Dependencies and reason used to cancel one exact Task execution attempt. */
|
|
65
|
+
interface CancelExecutionOptions {
|
|
66
|
+
readonly stores: CancelExecutionStores;
|
|
67
|
+
readonly task: TaskAttemptRef;
|
|
68
|
+
readonly adapter: ExecutionAdapter;
|
|
69
|
+
readonly reason: string;
|
|
70
|
+
}
|
|
71
|
+
/** Canonical cancellation precedes provider interruption. Retry this same call after an ambiguous stop. */
|
|
72
|
+
declare function cancelExecution(options: CancelExecutionOptions): Promise<Task>;
|
|
73
|
+
//#endregion
|
|
74
|
+
export { CancelExecutionOptions, CancelExecutionStores, ExecutionAdapter, ExecutionFenceError, ExecutionInspectionResult, InspectExecutionOptions, InspectExecutionStores, LaunchExecutionOptions, LaunchExecutionStores, RecordExecutionUsageOptions, RecordExecutionUsageStores, StartExecutionInput, cancelExecution, executionLaunchKey, inspectExecution, launchExecution, recordExecutionUsage };
|
|
75
|
+
//# sourceMappingURL=execution.d.mts.map
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { executionReferenceSchema } from "./schema/task.mjs";
|
|
2
|
+
import { codingExecutionRequestSchema, executionLaunchKeySchema, executionResultSchema, taskAttemptRefSchema } from "./schema/execution.mjs";
|
|
3
|
+
import { ExecutionLaunchPendingError } from "./dispatch.mjs";
|
|
4
|
+
import { isDeepStrictEqual } from "node:util";
|
|
5
|
+
//#region src/execution.ts
|
|
6
|
+
/** Error raised when an execution no longer matches the current running Task attempt. */
|
|
7
|
+
var ExecutionFenceError = class extends Error {};
|
|
8
|
+
function sameExecution(left, right) {
|
|
9
|
+
return isDeepStrictEqual(left === void 0 ? void 0 : JSON.parse(JSON.stringify(left)), right === void 0 ? void 0 : JSON.parse(JSON.stringify(right)));
|
|
10
|
+
}
|
|
11
|
+
async function currentAttempt(stores, ref) {
|
|
12
|
+
const { taskId, attempt } = taskAttemptRefSchema.parse(ref);
|
|
13
|
+
const task = await stores.tasks.get(taskId);
|
|
14
|
+
if (!task || task.attempt !== attempt || task.state !== "running") throw new ExecutionFenceError(`Execution requires running Task ${taskId} at attempt ${attempt}`);
|
|
15
|
+
return task;
|
|
16
|
+
}
|
|
17
|
+
/** Construct the stable provider idempotency key for one exact Task attempt. */
|
|
18
|
+
function executionLaunchKey(ref) {
|
|
19
|
+
const { taskId, attempt } = taskAttemptRefSchema.parse(ref);
|
|
20
|
+
return executionLaunchKeySchema.parse(`factory-task:${taskId}:${attempt}`);
|
|
21
|
+
}
|
|
22
|
+
/** Called inside a durable workflow step after dispatch gates. No provider lifecycle is stored here. */
|
|
23
|
+
async function launchExecution(options) {
|
|
24
|
+
const { stores, adapter } = options;
|
|
25
|
+
const request = codingExecutionRequestSchema.parse(options.request);
|
|
26
|
+
const ref = {
|
|
27
|
+
taskId: request.taskId,
|
|
28
|
+
attempt: request.attempt
|
|
29
|
+
};
|
|
30
|
+
const task = await currentAttempt(stores, ref);
|
|
31
|
+
if (task.repositoryIds.length !== 1) throw new Error("Execution requires exactly one repository");
|
|
32
|
+
const repository = await stores.repositories.get(task.repositoryIds[0]);
|
|
33
|
+
if (!repository?.enabled || repository.slug !== request.repository.slug) throw new Error("Execution repository must match the Task's enabled repository");
|
|
34
|
+
if (task.execution) {
|
|
35
|
+
const observed = await adapter.inspect(task.execution);
|
|
36
|
+
if (!sameExecution((await currentAttempt(stores, ref)).execution, task.execution)) throw new ExecutionFenceError("Execution changed during reattachment");
|
|
37
|
+
if (observed.status !== "active") throw new Error(`Execution is ${observed.status}; workflow must reconcile its outcome before continuing`);
|
|
38
|
+
return task.execution;
|
|
39
|
+
}
|
|
40
|
+
const execution = executionReferenceSchema.parse(await adapter.start({
|
|
41
|
+
request,
|
|
42
|
+
launchKey: executionLaunchKey(ref)
|
|
43
|
+
}));
|
|
44
|
+
try {
|
|
45
|
+
await stores.tasks.recordExecution(task.id, {
|
|
46
|
+
execution,
|
|
47
|
+
expectAttempt: task.attempt
|
|
48
|
+
});
|
|
49
|
+
} catch (error) {
|
|
50
|
+
let current;
|
|
51
|
+
try {
|
|
52
|
+
current = await stores.tasks.get(task.id);
|
|
53
|
+
} catch (cause) {
|
|
54
|
+
throw new ExecutionLaunchPendingError("Execution binding requires recovery with its original key", { cause });
|
|
55
|
+
}
|
|
56
|
+
if (!current || current.attempt !== task.attempt || current.state !== "running" || current.execution && !sameExecution(current.execution, execution)) {
|
|
57
|
+
await adapter.stop(execution);
|
|
58
|
+
throw error;
|
|
59
|
+
}
|
|
60
|
+
throw new ExecutionLaunchPendingError("Execution binding requires recovery with its original key", { cause: error });
|
|
61
|
+
}
|
|
62
|
+
return execution;
|
|
63
|
+
}
|
|
64
|
+
/** Read a result without granting it lifecycle authority; workflows validate evidence and choose the next state. */
|
|
65
|
+
async function inspectExecution(options) {
|
|
66
|
+
const { stores, adapter } = options;
|
|
67
|
+
const task = await currentAttempt(stores, options.task);
|
|
68
|
+
if (!task.execution) throw new Error(`Task ${options.task.taskId} has no recorded execution`);
|
|
69
|
+
const observed = await adapter.inspect(task.execution);
|
|
70
|
+
if (!sameExecution((await currentAttempt(stores, options.task)).execution, task.execution)) throw new ExecutionFenceError("Execution changed during inspection");
|
|
71
|
+
if (observed.status !== "finished") return observed;
|
|
72
|
+
const result = executionResultSchema.parse(observed.result);
|
|
73
|
+
if (!sameExecution(result.execution, task.execution)) throw new ExecutionFenceError("Result belongs to another execution");
|
|
74
|
+
return {
|
|
75
|
+
status: "finished",
|
|
76
|
+
result
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
/** For adapters reporting one immutable final usage packet instead of per-step usage hooks. */
|
|
80
|
+
async function recordExecutionUsage(options) {
|
|
81
|
+
const { stores } = options;
|
|
82
|
+
const { taskId, attempt } = taskAttemptRefSchema.parse(options.task);
|
|
83
|
+
const result = executionResultSchema.parse(options.result);
|
|
84
|
+
const task = await stores.tasks.get(taskId);
|
|
85
|
+
if (!task || task.attempt !== attempt || !sameExecution(task.execution, result.execution)) throw new ExecutionFenceError("Usage belongs to a different Task attempt or execution");
|
|
86
|
+
if (result.usage.costUsd === void 0) {
|
|
87
|
+
await stores.receipts.append({
|
|
88
|
+
taskId,
|
|
89
|
+
repositoryIds: task.repositoryIds,
|
|
90
|
+
kind: task.kind,
|
|
91
|
+
state: "usage_unresolved",
|
|
92
|
+
phase: "budget",
|
|
93
|
+
attempt,
|
|
94
|
+
execution: result.execution,
|
|
95
|
+
reason: "Execution cost is unknown; retain the reservation until measured usage is available",
|
|
96
|
+
dedupeKey: `execution:${taskId}:${attempt}:usage-unresolved`
|
|
97
|
+
});
|
|
98
|
+
return task;
|
|
99
|
+
}
|
|
100
|
+
return stores.tasks.recordUsage(taskId, {
|
|
101
|
+
step: {
|
|
102
|
+
turnId: `execution:${attempt}:${result.execution.provider}:${result.execution.sessionId}`,
|
|
103
|
+
stepIndex: 0,
|
|
104
|
+
costUsd: result.usage.costUsd,
|
|
105
|
+
inputTokens: result.usage.inputTokens,
|
|
106
|
+
outputTokens: result.usage.outputTokens
|
|
107
|
+
},
|
|
108
|
+
fence: {
|
|
109
|
+
attempt,
|
|
110
|
+
execution: result.execution
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
/** Canonical cancellation precedes provider interruption. Retry this same call after an ambiguous stop. */
|
|
115
|
+
async function cancelExecution(options) {
|
|
116
|
+
const { stores, adapter, reason } = options;
|
|
117
|
+
const { taskId, attempt } = taskAttemptRefSchema.parse(options.task);
|
|
118
|
+
let task = await stores.tasks.get(taskId);
|
|
119
|
+
if (!task || task.attempt !== attempt) throw new ExecutionFenceError("Cancellation belongs to a replaced attempt");
|
|
120
|
+
if (task.state !== "cancelled") task = await stores.tasks.transition(taskId, "cancelled", {
|
|
121
|
+
expectFrom: task.state,
|
|
122
|
+
expectAttempt: attempt,
|
|
123
|
+
reason
|
|
124
|
+
});
|
|
125
|
+
if (!task.execution) {
|
|
126
|
+
const reason = "Cancellation is recorded; execution discovery is still required before stop and settlement";
|
|
127
|
+
await stores.receipts.append({
|
|
128
|
+
taskId,
|
|
129
|
+
repositoryIds: task.repositoryIds,
|
|
130
|
+
kind: task.kind,
|
|
131
|
+
state: "cancellation_unresolved",
|
|
132
|
+
phase: "cleanup",
|
|
133
|
+
attempt,
|
|
134
|
+
reason,
|
|
135
|
+
dedupeKey: `execution:${taskId}:${attempt}:cancellation-unresolved`
|
|
136
|
+
});
|
|
137
|
+
throw new Error(reason);
|
|
138
|
+
}
|
|
139
|
+
const result = executionResultSchema.parse(await adapter.stop(task.execution));
|
|
140
|
+
task = await recordExecutionUsage({
|
|
141
|
+
stores,
|
|
142
|
+
task: {
|
|
143
|
+
taskId,
|
|
144
|
+
attempt
|
|
145
|
+
},
|
|
146
|
+
result
|
|
147
|
+
});
|
|
148
|
+
return result.usage.costUsd === void 0 ? task : stores.tasks.settle(taskId, { cost: { totalUsd: task.usage.costUsd } });
|
|
149
|
+
}
|
|
150
|
+
//#endregion
|
|
151
|
+
export { ExecutionFenceError, cancelExecution, executionLaunchKey, inspectExecution, launchExecution, recordExecutionUsage };
|
|
152
|
+
|
|
153
|
+
//# sourceMappingURL=execution.mjs.map
|