@vercel/factory 0.0.14 → 0.0.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +356 -0
- package/README.md +96 -2
- package/dist/agent-routes.d.mts +113 -0
- package/dist/agent-routes.mjs +91 -0
- package/dist/agent-routes.mjs.map +1 -0
- package/dist/api-contracts.d.mts +1768 -0
- package/dist/api-contracts.mjs +119 -0
- package/dist/api-contracts.mjs.map +1 -0
- package/dist/api-images.d.mts +30 -0
- package/dist/api-images.mjs +28 -0
- package/dist/api-images.mjs.map +1 -0
- package/dist/api-task-graph.mjs +57 -0
- package/dist/api-task-graph.mjs.map +1 -0
- package/dist/api.d.mts +111 -216
- package/dist/api.mjs +1063 -99
- package/dist/api.mjs.map +1 -1
- package/dist/approval-access.d.mts +39 -0
- package/dist/approval-access.mjs +62 -0
- package/dist/approval-access.mjs.map +1 -0
- package/dist/approval-contracts.d.mts +698 -0
- package/dist/approval-contracts.mjs +27 -0
- package/dist/approval-contracts.mjs.map +1 -0
- package/dist/approval.d.mts +49 -0
- package/dist/approval.mjs +39 -0
- package/dist/approval.mjs.map +1 -0
- package/dist/blob/artifacts.d.mts +37 -0
- package/dist/blob/artifacts.mjs +45 -0
- package/dist/blob/artifacts.mjs.map +1 -0
- package/dist/blob/index.d.mts +84 -12
- package/dist/blob/index.mjs +179 -45
- package/dist/blob/index.mjs.map +1 -1
- package/dist/budget.d.mts +115 -0
- package/dist/budget.mjs +147 -0
- package/dist/budget.mjs.map +1 -0
- package/dist/budgets.d.mts +4 -0
- package/dist/budgets.mjs +4 -0
- package/dist/build-factory.d.mts +7 -4
- package/dist/build-factory.mjs +2 -1
- package/dist/build-factory.mjs.map +1 -1
- package/dist/change-verification/brief.d.mts +9 -0
- package/dist/change-verification/brief.mjs +13 -0
- package/dist/change-verification/brief.mjs.map +1 -0
- package/dist/change-verification/dispatch.d.mts +37 -0
- package/dist/change-verification/dispatch.mjs +83 -0
- package/dist/change-verification/dispatch.mjs.map +1 -0
- package/dist/change-verification/eve-tool.d.mts +61 -0
- package/dist/change-verification/eve-tool.mjs +89 -0
- package/dist/change-verification/eve-tool.mjs.map +1 -0
- package/dist/change-verification/result.mjs +130 -0
- package/dist/change-verification/result.mjs.map +1 -0
- package/dist/changes/eve-record-change.d.mts +56 -0
- package/dist/changes/eve-record-change.mjs +131 -0
- package/dist/changes/eve-record-change.mjs.map +1 -0
- package/dist/changes/presentation.d.mts +14 -0
- package/dist/changes.d.mts +24 -0
- package/dist/changes.mjs +29 -0
- package/dist/changes.mjs.map +1 -0
- package/dist/client-events.d.mts +25 -0
- package/dist/client-events.mjs +45 -0
- package/dist/client-events.mjs.map +1 -0
- package/dist/client-stream.d.mts +28 -0
- package/dist/client-stream.mjs +210 -0
- package/dist/client-stream.mjs.map +1 -0
- package/dist/client-transcript.d.mts +41 -0
- package/dist/client-transcript.mjs +233 -0
- package/dist/client-transcript.mjs.map +1 -0
- package/dist/client.d.mts +343 -0
- package/dist/client.mjs +366 -0
- package/dist/client.mjs.map +1 -0
- package/dist/code-review/contracts.d.mts +27 -0
- package/dist/code-review/eve-post-review.d.mts +70 -0
- package/dist/code-review/eve-post-review.mjs +195 -0
- package/dist/code-review/eve-post-review.mjs.map +1 -0
- package/dist/code-review/eve-review-comments.d.mts +75 -0
- package/dist/code-review/eve-review-comments.mjs +193 -0
- package/dist/code-review/eve-review-comments.mjs.map +1 -0
- package/dist/code-review/github-contracts.d.mts +10 -0
- package/dist/code-review/github-reporter.d.mts +46 -0
- package/dist/code-review/github-reporter.mjs +262 -0
- package/dist/code-review/github-reporter.mjs.map +1 -0
- package/dist/code-review/preparation.d.mts +34 -0
- package/dist/code-review/preparation.mjs +23 -0
- package/dist/code-review/preparation.mjs.map +1 -0
- package/dist/code-review/stall.d.mts +17 -0
- package/dist/code-review/stall.mjs +30 -0
- package/dist/code-review/stall.mjs.map +1 -0
- package/dist/code-review.d.mts +7 -0
- package/dist/code-review.mjs +5 -0
- package/dist/communication.d.mts +9 -0
- package/dist/communication.mjs +9 -0
- package/dist/connectors/github.d.mts +26 -0
- package/dist/connectors/github.mjs +55 -0
- package/dist/connectors/github.mjs.map +1 -0
- package/dist/connectors/index.d.mts +35 -3
- package/dist/connectors/index.mjs +3 -52
- package/dist/connectors/index.mjs.map +1 -1
- package/dist/connectors/outbox.d.mts +34 -0
- package/dist/connectors/outbox.mjs +50 -0
- package/dist/connectors/outbox.mjs.map +1 -0
- package/dist/connectors/slack-approval-blocks.mjs +136 -0
- package/dist/connectors/slack-approval-blocks.mjs.map +1 -0
- package/dist/connectors/slack-approvals.d.mts +29 -0
- package/dist/connectors/slack-approvals.mjs +346 -0
- package/dist/connectors/slack-approvals.mjs.map +1 -0
- package/dist/connectors/slack.d.mts +35 -52
- package/dist/connectors/slack.mjs +13 -85
- package/dist/connectors/slack.mjs.map +1 -1
- package/dist/deepsec/eve-tool.d.mts +110 -0
- package/dist/deepsec/eve-tool.mjs +166 -0
- package/dist/deepsec/eve-tool.mjs.map +1 -0
- package/dist/deepsec/finding.d.mts +84 -0
- package/dist/deepsec/finding.mjs +56 -0
- package/dist/deepsec/finding.mjs.map +1 -0
- package/dist/deepsec.d.mts +3 -0
- package/dist/delivery-metrics/metrics.d.mts +93 -0
- package/dist/delivery-metrics/metrics.mjs +200 -0
- package/dist/delivery-metrics/metrics.mjs.map +1 -0
- package/dist/delivery-metrics.d.mts +2 -0
- package/dist/delivery-metrics.mjs +2 -0
- package/dist/dispatch.d.mts +152 -0
- package/dist/dispatch.mjs +199 -0
- package/dist/dispatch.mjs.map +1 -0
- package/dist/errors.d.mts +15 -0
- package/dist/errors.mjs +38 -0
- package/dist/errors.mjs.map +1 -0
- package/dist/eve/index.d.mts +209 -1277
- package/dist/eve/index.mjs +312 -1073
- package/dist/eve/index.mjs.map +1 -1
- package/dist/eve/invoke.d.mts +144 -0
- package/dist/eve/invoke.mjs +254 -0
- package/dist/eve/invoke.mjs.map +1 -0
- package/dist/eve/session-client.d.mts +186 -0
- package/dist/eve/session-client.mjs +245 -0
- package/dist/eve/session-client.mjs.map +1 -0
- package/dist/eve/task-execution.d.mts +380 -0
- package/dist/eve/task-execution.mjs +73 -0
- package/dist/eve/task-execution.mjs.map +1 -0
- package/dist/eve/task-session.d.mts +129 -0
- package/dist/eve/task-session.mjs +245 -0
- package/dist/eve/task-session.mjs.map +1 -0
- package/dist/eve/transcript.d.mts +37 -0
- package/dist/eve/transcript.mjs +69 -0
- package/dist/eve/transcript.mjs.map +1 -0
- package/dist/execution-public.d.mts +8 -0
- package/dist/execution-public.mjs +7 -0
- package/dist/execution.d.mts +183 -0
- package/dist/execution.mjs +223 -0
- package/dist/execution.mjs.map +1 -0
- package/dist/finding-remediation/admission.d.mts +53 -0
- package/dist/finding-remediation/admission.mjs +90 -0
- package/dist/finding-remediation/admission.mjs.map +1 -0
- package/dist/finding-remediation/brief.d.mts +14 -0
- package/dist/finding-remediation/brief.mjs +25 -0
- package/dist/finding-remediation/brief.mjs.map +1 -0
- package/dist/finding-remediation/eve-tool.d.mts +29 -0
- package/dist/finding-remediation/eve-tool.mjs +34 -0
- package/dist/finding-remediation/eve-tool.mjs.map +1 -0
- package/dist/findings/fingerprint.d.mts +14 -0
- package/dist/findings/fingerprint.mjs +28 -0
- package/dist/findings/fingerprint.mjs.map +1 -0
- package/dist/findings.d.mts +7 -0
- package/dist/findings.mjs +6 -0
- package/dist/github-app.d.mts +54 -0
- package/dist/github-app.mjs +121 -0
- package/dist/github-app.mjs.map +1 -0
- package/dist/github-publication.d.mts +199 -0
- package/dist/github-publication.mjs +442 -0
- package/dist/github-publication.mjs.map +1 -0
- package/dist/github-transfer.d.mts +81 -0
- package/dist/github-transfer.mjs +273 -0
- package/dist/github-transfer.mjs.map +1 -0
- package/dist/github.d.mts +446 -2
- package/dist/github.mjs +357 -56
- package/dist/github.mjs.map +1 -1
- package/dist/image-input.d.mts +51 -0
- package/dist/image-input.mjs +63 -0
- package/dist/image-input.mjs.map +1 -0
- package/dist/inbox-activity.d.mts +53 -0
- package/dist/inbox-activity.mjs +41 -0
- package/dist/inbox-activity.mjs.map +1 -0
- package/dist/index.d.mts +16 -484
- package/dist/index.mjs +15 -522
- package/dist/intake-contracts.d.mts +55 -0
- package/dist/intake-contracts.mjs +33 -0
- package/dist/intake-contracts.mjs.map +1 -0
- package/dist/integrations/deepsec.d.mts +5 -0
- package/dist/integrations/deepsec.mjs +3 -0
- package/dist/integrations/github.d.mts +11 -0
- package/dist/integrations/github.mjs +9 -0
- package/dist/integrations/slack.d.mts +5 -0
- package/dist/integrations/slack.mjs +5 -0
- package/dist/integrations/vercel.d.mts +5 -0
- package/dist/integrations/vercel.mjs +4 -0
- package/dist/merge-resolution/contracts.d.mts +17 -0
- package/dist/merge-resolution/contracts.mjs +19 -0
- package/dist/merge-resolution/contracts.mjs.map +1 -0
- package/dist/merge-resolution/eve-tools.d.mts +47 -0
- package/dist/merge-resolution/eve-tools.mjs +122 -0
- package/dist/merge-resolution/eve-tools.mjs.map +1 -0
- package/dist/model-settings.d.mts +41 -0
- package/dist/model-settings.mjs +35 -0
- package/dist/model-settings.mjs.map +1 -0
- package/dist/multi-repository-changes/coordination.d.mts +49 -0
- package/dist/multi-repository-changes/coordination.mjs +115 -0
- package/dist/multi-repository-changes/coordination.mjs.map +1 -0
- package/dist/planning/cancellation.mjs +82 -0
- package/dist/planning/cancellation.mjs.map +1 -0
- package/dist/planning/configuration.d.mts +23 -0
- package/dist/planning/configuration.mjs +56 -0
- package/dist/planning/configuration.mjs.map +1 -0
- package/dist/planning/contracts.d.mts +136 -0
- package/dist/planning/contracts.mjs +42 -0
- package/dist/planning/contracts.mjs.map +1 -0
- package/dist/planning/dependencies.mjs +44 -0
- package/dist/planning/dependencies.mjs.map +1 -0
- package/dist/planning/effects.mjs +122 -0
- package/dist/planning/effects.mjs.map +1 -0
- package/dist/planning/reconcile.mjs +279 -0
- package/dist/planning/reconcile.mjs.map +1 -0
- package/dist/planning/runtime.d.mts +27 -0
- package/dist/planning/runtime.mjs +25 -0
- package/dist/planning/runtime.mjs.map +1 -0
- package/dist/planning/state.mjs +92 -0
- package/dist/planning/state.mjs.map +1 -0
- package/dist/planning/turns.mjs +262 -0
- package/dist/planning/turns.mjs.map +1 -0
- package/dist/planning/validation.mjs +106 -0
- package/dist/planning/validation.mjs.map +1 -0
- package/dist/postgres/index.d.mts +48 -1
- package/dist/postgres/index.mjs +45 -3
- package/dist/postgres/index.mjs.map +1 -1
- package/dist/presets/software-development/approval.d.mts +14 -0
- package/dist/presets/software-development/approval.mjs +21 -0
- package/dist/presets/software-development/approval.mjs.map +1 -0
- package/dist/presets/software-development/deepsec-brief.d.mts +8 -0
- package/dist/presets/software-development/deepsec-brief.mjs +14 -0
- package/dist/presets/software-development/deepsec-brief.mjs.map +1 -0
- package/dist/presets/software-development/dispatch.d.mts +56 -0
- package/dist/presets/software-development/dispatch.mjs +67 -0
- package/dist/presets/software-development/dispatch.mjs.map +1 -0
- package/dist/presets/software-development/eve-delegation-policy.d.mts +7 -0
- package/dist/presets/software-development/eve-delegation-policy.mjs +34 -0
- package/dist/presets/software-development/eve-delegation-policy.mjs.map +1 -0
- package/dist/presets/software-development/eve.d.mts +18 -0
- package/dist/presets/software-development/eve.mjs +19 -0
- package/dist/presets/software-development/eve.mjs.map +1 -0
- package/dist/presets/software-development/finish-task-policy.d.mts +7 -0
- package/dist/presets/software-development/finish-task-policy.mjs +9 -0
- package/dist/presets/software-development/finish-task-policy.mjs.map +1 -0
- package/dist/presets/software-development/recovery.d.mts +32 -0
- package/dist/presets/software-development/recovery.mjs +120 -0
- package/dist/presets/software-development/recovery.mjs.map +1 -0
- package/dist/presets/software-development/task-communication.d.mts +24 -0
- package/dist/presets/software-development/task-communication.mjs +157 -0
- package/dist/presets/software-development/task-communication.mjs.map +1 -0
- package/dist/presets/software-development.d.mts +10 -0
- package/dist/presets/software-development.mjs +9 -0
- package/dist/pull-requests/contracts.d.mts +72 -0
- package/dist/pull-requests/eve-fetch.d.mts +57 -0
- package/dist/pull-requests/eve-fetch.mjs +151 -0
- package/dist/pull-requests/eve-fetch.mjs.map +1 -0
- package/dist/pull-requests/github-label-manager.d.mts +27 -0
- package/dist/pull-requests/github-label-manager.mjs +40 -0
- package/dist/pull-requests/github-label-manager.mjs.map +1 -0
- package/dist/pull-requests/github-publisher.d.mts +67 -0
- package/dist/pull-requests/github-publisher.mjs +405 -0
- package/dist/pull-requests/github-publisher.mjs.map +1 -0
- package/dist/pull-requests.d.mts +6 -0
- package/dist/pull-requests.mjs +4 -0
- package/dist/receipts.d.mts +4 -0
- package/dist/receipts.mjs +3 -0
- package/dist/repositories.d.mts +18 -0
- package/dist/repositories.mjs +33 -0
- package/dist/repositories.mjs.map +1 -0
- package/dist/sandbox/command.d.mts +18 -0
- package/dist/sandbox/execution-abort.d.mts +16 -0
- package/dist/sandbox/execution-abort.mjs +49 -0
- package/dist/sandbox/execution-abort.mjs.map +1 -0
- package/dist/sandbox/execution-environment.d.mts +25 -0
- package/dist/sandbox/execution-run-ledger.d.mts +115 -0
- package/dist/sandbox/execution-run-ledger.mjs +209 -0
- package/dist/sandbox/execution-run-ledger.mjs.map +1 -0
- package/dist/sandbox/execution.d.mts +54 -0
- package/dist/sandbox/execution.mjs +228 -0
- package/dist/sandbox/execution.mjs.map +1 -0
- package/dist/sandbox/harness-runner.d.mts +79 -0
- package/dist/sandbox/harness-runner.mjs +162 -0
- package/dist/sandbox/harness-runner.mjs.map +1 -0
- package/dist/sandbox/index.d.mts +10 -0
- package/dist/sandbox/index.mjs +7 -0
- package/dist/sandbox/task-environment.d.mts +50 -0
- package/dist/sandbox/task-environment.mjs +140 -0
- package/dist/sandbox/task-environment.mjs.map +1 -0
- package/dist/sandbox/workspace.d.mts +29 -0
- package/dist/sandbox/workspace.mjs +28 -0
- package/dist/sandbox/workspace.mjs.map +1 -0
- package/dist/schema/agent-route.d.mts +40 -0
- package/dist/schema/agent-route.mjs +33 -0
- package/dist/schema/agent-route.mjs.map +1 -0
- package/dist/schema/answer.d.mts +23 -0
- package/dist/schema/answer.mjs +21 -0
- package/dist/schema/answer.mjs.map +1 -0
- package/dist/schema/budget.d.mts +38 -0
- package/dist/schema/budget.mjs +30 -0
- package/dist/schema/budget.mjs.map +1 -0
- package/dist/schema/catalog.mjs +72 -0
- package/dist/schema/catalog.mjs.map +1 -0
- package/dist/schema/change.d.mts +117 -0
- package/dist/schema/change.mjs +68 -0
- package/dist/schema/change.mjs.map +1 -0
- package/dist/schema/common.d.mts +33 -0
- package/dist/{id-BLIkCVue.mjs → schema/common.mjs} +7 -16
- package/dist/schema/common.mjs.map +1 -0
- package/dist/schema/communication.d.mts +52 -0
- package/dist/{communication-Kd9nU5RP.mjs → schema/communication.mjs} +8 -3
- package/dist/schema/communication.mjs.map +1 -0
- package/dist/schema/execution.d.mts +101 -0
- package/dist/schema/execution.mjs +86 -0
- package/dist/schema/execution.mjs.map +1 -0
- package/dist/schema/factory-config.d.mts +122 -0
- package/dist/schema/factory-config.mjs +90 -0
- package/dist/schema/factory-config.mjs.map +1 -0
- package/dist/schema/finding.d.mts +125 -0
- package/dist/schema/finding.mjs +67 -0
- package/dist/schema/finding.mjs.map +1 -0
- package/dist/schema/id.d.mts +43 -0
- package/dist/schema/id.mjs +32 -0
- package/dist/schema/id.mjs.map +1 -0
- package/dist/schema/outbox.d.mts +240 -0
- package/dist/schema/outbox.mjs +53 -0
- package/dist/schema/outbox.mjs.map +1 -0
- package/dist/schema/question-route.mjs +16 -0
- package/dist/schema/question-route.mjs.map +1 -0
- package/dist/schema/receipt.d.mts +81 -0
- package/dist/schema/receipt.mjs +94 -0
- package/dist/schema/receipt.mjs.map +1 -0
- package/dist/schema/repository.d.mts +30 -0
- package/dist/schema/repository.mjs +29 -0
- package/dist/schema/repository.mjs.map +1 -0
- package/dist/schema/session.d.mts +136 -0
- package/dist/schema/session.mjs +54 -0
- package/dist/schema/session.mjs.map +1 -0
- package/dist/schema/signal.d.mts +105 -0
- package/dist/schema/signal.mjs +81 -0
- package/dist/schema/signal.mjs.map +1 -0
- package/dist/schema/slack-pr-notifications.d.mts +12 -0
- package/dist/schema/slack-pr-notifications.mjs +11 -0
- package/dist/schema/slack-pr-notifications.mjs.map +1 -0
- package/dist/schema/stored-session.mjs +10 -0
- package/dist/schema/stored-session.mjs.map +1 -0
- package/dist/schema/stored-task.mjs +20 -0
- package/dist/schema/stored-task.mjs.map +1 -0
- package/dist/schema/task-effect.d.mts +39 -0
- package/dist/schema/task-effect.mjs +29 -0
- package/dist/schema/task-effect.mjs.map +1 -0
- package/dist/schema/task-graph.d.mts +3173 -0
- package/dist/schema/task-graph.mjs +97 -0
- package/dist/schema/task-graph.mjs.map +1 -0
- package/dist/schema/task-message.d.mts +254 -0
- package/dist/schema/task-message.mjs +189 -0
- package/dist/schema/task-message.mjs.map +1 -0
- package/dist/schema/task.d.mts +273 -0
- package/dist/schema/task.mjs +200 -0
- package/dist/schema/task.mjs.map +1 -0
- package/dist/schema/transcript.d.mts +176 -0
- package/dist/schema/transcript.mjs +87 -0
- package/dist/schema/transcript.mjs.map +1 -0
- package/dist/schema/transitions.d.mts +50 -0
- package/dist/schema/transitions.mjs +303 -0
- package/dist/schema/transitions.mjs.map +1 -0
- package/dist/schema/work.d.mts +150 -0
- package/dist/schema/work.mjs +102 -0
- package/dist/schema/work.mjs.map +1 -0
- package/dist/session-previews.d.mts +76 -0
- package/dist/session-previews.mjs +55 -0
- package/dist/session-previews.mjs.map +1 -0
- package/dist/session-review.d.mts +120 -0
- package/dist/session-review.mjs +79 -0
- package/dist/session-review.mjs.map +1 -0
- package/dist/signal-triage.d.mts +20 -0
- package/dist/signal-triage.mjs +85 -0
- package/dist/signal-triage.mjs.map +1 -0
- package/dist/signals.d.mts +20 -0
- package/dist/signals.mjs +19 -0
- package/dist/signals.mjs.map +1 -0
- package/dist/stall.d.mts +41 -0
- package/dist/stall.mjs +83 -0
- package/dist/stall.mjs.map +1 -0
- package/dist/storage.d.mts +4 -0
- package/dist/storage.mjs +4 -0
- package/dist/store/driver.d.mts +65 -0
- package/dist/store/driver.mjs +42 -0
- package/dist/store/driver.mjs.map +1 -0
- package/dist/store/engine.d.mts +683 -0
- package/dist/store/engine.mjs +2309 -0
- package/dist/store/engine.mjs.map +1 -0
- package/dist/store/memory.d.mts +24 -0
- package/dist/store/memory.mjs +69 -0
- package/dist/store/memory.mjs.map +1 -0
- package/dist/store/slack-pr-notifications.d.mts +44 -0
- package/dist/store/slack-pr-notifications.mjs +121 -0
- package/dist/store/slack-pr-notifications.mjs.map +1 -0
- package/dist/store/task-effects.d.mts +38 -0
- package/dist/store/task-effects.mjs +118 -0
- package/dist/store/task-effects.mjs.map +1 -0
- package/dist/store/task-graphs.d.mts +60 -0
- package/dist/store/task-graphs.mjs +212 -0
- package/dist/store/task-graphs.mjs.map +1 -0
- package/dist/store/task-messages.d.mts +136 -0
- package/dist/store/task-messages.mjs +475 -0
- package/dist/store/task-messages.mjs.map +1 -0
- package/dist/store/task-work.d.mts +180 -0
- package/dist/store/task-work.mjs +137 -0
- package/dist/store/task-work.mjs.map +1 -0
- package/dist/store/work-notifications.d.mts +19 -0
- package/dist/store/work-notifications.mjs +48 -0
- package/dist/store/work-notifications.mjs.map +1 -0
- package/dist/sweep.d.mts +79 -0
- package/dist/sweep.mjs +171 -0
- package/dist/sweep.mjs.map +1 -0
- package/dist/task-communication.d.mts +35 -0
- package/dist/task-communication.mjs +47 -0
- package/dist/task-communication.mjs.map +1 -0
- package/dist/task-graph-view.d.mts +267 -0
- package/dist/task-graph-view.mjs +17 -0
- package/dist/task-graph-view.mjs.map +1 -0
- package/dist/task-message-processing.d.mts +60 -0
- package/dist/task-message-processing.mjs +200 -0
- package/dist/task-message-processing.mjs.map +1 -0
- package/dist/task-protocols.d.mts +80 -0
- package/dist/task-protocols.mjs +47 -0
- package/dist/task-protocols.mjs.map +1 -0
- package/dist/tasks.d.mts +17 -0
- package/dist/tasks.mjs +17 -0
- package/dist/vercel-git.d.mts +53 -0
- package/dist/vercel-git.mjs +344 -0
- package/dist/vercel-git.mjs.map +1 -0
- package/dist/vercel-github-api.d.mts +103 -0
- package/dist/vercel-github-api.mjs +363 -0
- package/dist/vercel-github-api.mjs.map +1 -0
- package/dist/vercel.d.mts +34 -2
- package/dist/vercel.mjs +49 -3
- package/dist/vercel.mjs.map +1 -1
- package/dist/work-triage/eve-tool.d.mts +29 -0
- package/dist/work-triage/eve-tool.mjs +32 -0
- package/dist/work-triage/eve-tool.mjs.map +1 -0
- package/dist/work-triage/find-existing-work.d.mts +32 -0
- package/dist/work-triage/find-existing-work.mjs +31 -0
- package/dist/work-triage/find-existing-work.mjs.map +1 -0
- package/dist/work-triage/github.d.mts +15 -0
- package/dist/work-triage/github.mjs +14 -0
- package/dist/work-triage/github.mjs.map +1 -0
- package/dist/work-triage.d.mts +4 -0
- package/dist/work-triage.mjs +3 -0
- package/dist/workflows.d.mts +146 -0
- package/dist/workflows.mjs +139 -0
- package/dist/workflows.mjs.map +1 -0
- package/dist/workspace-diff-assembly.d.mts +33 -0
- package/dist/workspace-diff-assembly.mjs +95 -0
- package/dist/workspace-diff-assembly.mjs.map +1 -0
- package/dist/workspace-diff.d.mts +90 -0
- package/dist/workspace-diff.mjs +61 -0
- package/dist/workspace-diff.mjs.map +1 -0
- package/dist/workspace-files-git.d.mts +15 -0
- package/dist/workspace-files-git.mjs +61 -0
- package/dist/workspace-files-git.mjs.map +1 -0
- package/dist/workspace-files.d.mts +107 -0
- package/dist/workspace-files.mjs +74 -0
- package/dist/workspace-files.mjs.map +1 -0
- package/dist/workspace-snapshot.d.mts +26 -0
- package/dist/workspace-snapshot.mjs +80 -0
- package/dist/workspace-snapshot.mjs.map +1 -0
- package/docs/getting-started.md +104 -0
- package/docs/index.md +100 -0
- package/docs/recipes/cancellation.md +215 -0
- package/docs/recipes/custom-workflow.md +153 -0
- package/docs/recipes/dependent-tasks.md +207 -0
- package/docs/recipes/eve-agent.md +277 -0
- package/docs/recipes/human-input.md +204 -0
- package/docs/recipes/persistence-recovery.md +268 -0
- package/docs/recipes/retry-recovery.md +241 -0
- package/docs/recipes/task-messaging.md +215 -0
- package/docs/recipes/typed-eve-result.md +161 -0
- package/docs/runtime-integration.md +137 -0
- package/package.json +142 -46
- package/dist/approval-JX2Px9IC.mjs +0 -132
- package/dist/approval-JX2Px9IC.mjs.map +0 -1
- package/dist/budget-DzSiIA4t.mjs +0 -42
- package/dist/budget-DzSiIA4t.mjs.map +0 -1
- package/dist/change-D2U6v7Ek.mjs +0 -554
- package/dist/change-D2U6v7Ek.mjs.map +0 -1
- package/dist/communication-Kd9nU5RP.mjs.map +0 -1
- package/dist/coordination-Cq08hb1l.mjs +0 -76
- package/dist/coordination-Cq08hb1l.mjs.map +0 -1
- package/dist/dispatch-BaZS3PT8.d.mts +0 -76
- package/dist/driver-BC0Mt5KG.d.mts +0 -34
- package/dist/driver-Bbd25rNM.mjs +0 -23
- package/dist/driver-Bbd25rNM.mjs.map +0 -1
- package/dist/engine-DFHU2Pe-.d.mts +0 -781
- package/dist/engine-WaUM2cGA.mjs +0 -1068
- package/dist/engine-WaUM2cGA.mjs.map +0 -1
- package/dist/factory-config-BoRXOw43.mjs +0 -50
- package/dist/factory-config-BoRXOw43.mjs.map +0 -1
- package/dist/factory-config-D2yU3G0c.d.mts +0 -58
- package/dist/github-C5HNBN6p.d.mts +0 -279
- package/dist/id-BLIkCVue.mjs.map +0 -1
- package/dist/index-Bw6hggoH.d.mts +0 -40
- package/dist/index.mjs.map +0 -1
- package/dist/task-CcOyOJ3k.mjs +0 -154
- package/dist/task-CcOyOJ3k.mjs.map +0 -1
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
import { createCheckRun, createPullRequestComment, listCheckRunsForRef, listPullRequestComments, readPullRequestHead, updateCheckRun, updatePullRequestComment } from "../github.mjs";
|
|
2
|
+
//#region src/code-review/github-reporter.ts
|
|
3
|
+
const summaryMarker = "<!-- factory-code-review-summary -->";
|
|
4
|
+
const statePrefix = "<!-- factory-code-review-state:v1:";
|
|
5
|
+
const stateSuffix = " -->";
|
|
6
|
+
const terminalReviewStates = /* @__PURE__ */ new Set(["cancelled", "failed"]);
|
|
7
|
+
const reviewCheckBatchSize = 20;
|
|
8
|
+
const reviewCheckPageIntervalMs = 3e5;
|
|
9
|
+
const reviewCheckEffectKey = {
|
|
10
|
+
namespace: "software",
|
|
11
|
+
name: "github_review_check"
|
|
12
|
+
};
|
|
13
|
+
function recordedCheckRunId(effect, headSha) {
|
|
14
|
+
if (effect?.reference === void 0) return void 0;
|
|
15
|
+
const { provider, resourceId } = effect.reference;
|
|
16
|
+
const checkRunId = Number(resourceId);
|
|
17
|
+
if (provider !== "github" || !Number.isSafeInteger(checkRunId) || checkRunId <= 0) throw new Error("Stored software review-check effect has an invalid GitHub check-run reference");
|
|
18
|
+
if (headSha !== void 0 && effect.reference.subject !== headSha) throw new Error(`Review task ${effect.taskId} has a check for another commit. Request a new review for ${headSha}.`);
|
|
19
|
+
return checkRunId;
|
|
20
|
+
}
|
|
21
|
+
/** Create the supplied GitHub check-run and pull-request summary reporter for code reviews. */
|
|
22
|
+
function createGitHubReviewReporter(options) {
|
|
23
|
+
const checkName = options.checkName ?? "Factory code review";
|
|
24
|
+
const summaryHeading = options.summaryHeading ?? "Factory code review";
|
|
25
|
+
const now = () => (options.now ?? (() => /* @__PURE__ */ new Date()))().toISOString();
|
|
26
|
+
async function startReviewCheck(input) {
|
|
27
|
+
const token = await options.tokenForRepository(input.repository);
|
|
28
|
+
if (input.reuseExisting) {
|
|
29
|
+
const effect = await options.stores.taskEffects.get({
|
|
30
|
+
taskId: input.taskId,
|
|
31
|
+
key: reviewCheckEffectKey
|
|
32
|
+
});
|
|
33
|
+
if (recordedCheckRunId(effect, input.headSha) !== void 0 || effect?.resolvedAt !== void 0) return;
|
|
34
|
+
const existing = (await listCheckRunsForRef({
|
|
35
|
+
repository: input.repository,
|
|
36
|
+
token,
|
|
37
|
+
fetch: options.fetch,
|
|
38
|
+
ref: input.headSha,
|
|
39
|
+
checkName
|
|
40
|
+
})).find((check) => check.externalId === input.taskId);
|
|
41
|
+
if (existing !== void 0) {
|
|
42
|
+
await recordReviewCheck(input.taskId, existing.id, input.headSha);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
const checkRun = await createCheckRun({
|
|
47
|
+
repository: input.repository,
|
|
48
|
+
token,
|
|
49
|
+
fetch: options.fetch,
|
|
50
|
+
name: checkName,
|
|
51
|
+
headSha: input.headSha,
|
|
52
|
+
status: "in_progress",
|
|
53
|
+
externalId: input.taskId,
|
|
54
|
+
detailsUrl: input.pullRequestUrl,
|
|
55
|
+
startedAt: now(),
|
|
56
|
+
output: {
|
|
57
|
+
title: "Review in progress",
|
|
58
|
+
summary: "The Factory code reviewer is inspecting this commit."
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
await recordReviewCheck(input.taskId, checkRun.id, input.headSha);
|
|
62
|
+
}
|
|
63
|
+
async function completeReviewCheck(review) {
|
|
64
|
+
const token = await options.tokenForRepository(review.repository);
|
|
65
|
+
let checkRunId = recordedCheckRunId(await options.stores.taskEffects.get({
|
|
66
|
+
taskId: review.taskId,
|
|
67
|
+
key: reviewCheckEffectKey
|
|
68
|
+
}), review.headSha);
|
|
69
|
+
if (checkRunId === void 0) {
|
|
70
|
+
checkRunId = (await findReviewCheck(review, token)).id;
|
|
71
|
+
await recordReviewCheck(review.taskId, checkRunId, review.headSha);
|
|
72
|
+
}
|
|
73
|
+
await updateCheckRun({
|
|
74
|
+
repository: review.repository,
|
|
75
|
+
token,
|
|
76
|
+
fetch: options.fetch,
|
|
77
|
+
checkRunId,
|
|
78
|
+
status: "completed",
|
|
79
|
+
conclusion: review.verdict === "request_changes" ? "failure" : "success",
|
|
80
|
+
completedAt: now(),
|
|
81
|
+
output: {
|
|
82
|
+
title: review.verdict === "request_changes" ? "Review found blocking issues" : "Review complete",
|
|
83
|
+
summary: review.summary,
|
|
84
|
+
annotations: review.comments.map((comment) => ({
|
|
85
|
+
path: comment.path,
|
|
86
|
+
startLine: comment.startLine ?? comment.line,
|
|
87
|
+
endLine: comment.line,
|
|
88
|
+
annotationLevel: review.verdict === "request_changes" ? "failure" : "notice",
|
|
89
|
+
title: comment.title,
|
|
90
|
+
message: comment.body
|
|
91
|
+
}))
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
await options.stores.taskEffects.resolve({
|
|
95
|
+
taskId: review.taskId,
|
|
96
|
+
key: reviewCheckEffectKey
|
|
97
|
+
});
|
|
98
|
+
if (typeof options.publishSummary === "function" ? await options.publishSummary(review) : options.publishSummary !== false) await updateReviewSummary(review, token);
|
|
99
|
+
}
|
|
100
|
+
async function reconcileReviewChecks(input) {
|
|
101
|
+
const { tasks, ...reconcileOptions } = input;
|
|
102
|
+
const reconciliationNow = reconcileOptions.now ?? options.now ?? (() => /* @__PURE__ */ new Date());
|
|
103
|
+
const batchSize = reconcileOptions.batchSize ?? reviewCheckBatchSize;
|
|
104
|
+
if (!Number.isSafeInteger(batchSize) || batchSize <= 0) throw new Error("Review-check batch size must be a positive safe integer");
|
|
105
|
+
const candidates = tasks.filter((task) => task.work.route?.id === "code-review" && terminalReviewStates.has(task.state)).sort((left, right) => left.id.localeCompare(right.id));
|
|
106
|
+
const candidatePage = reconcileOptions.candidatePage ?? Math.floor(reconciliationNow().getTime() / reviewCheckPageIntervalMs);
|
|
107
|
+
if (!Number.isSafeInteger(candidatePage) || candidatePage < 0) throw new Error("Review-check candidate page must be a nonnegative safe integer");
|
|
108
|
+
const pageStart = candidatePage % Math.max(1, Math.ceil(candidates.length / batchSize)) * batchSize;
|
|
109
|
+
const page = candidates.slice(pageStart, pageStart + batchSize);
|
|
110
|
+
const pending = (await Promise.all(page.map(async (task) => ({
|
|
111
|
+
task,
|
|
112
|
+
effect: await options.stores.taskEffects.get({
|
|
113
|
+
taskId: task.id,
|
|
114
|
+
key: reviewCheckEffectKey
|
|
115
|
+
})
|
|
116
|
+
})))).filter(({ effect }) => effect?.resolvedAt === void 0);
|
|
117
|
+
const concluded = [];
|
|
118
|
+
const stamped = [];
|
|
119
|
+
for (const { task, effect } of pending) try {
|
|
120
|
+
if (await concludeAbandonedCheck(task, effect, reconciliationNow)) concluded.push(task.id);
|
|
121
|
+
await options.stores.taskEffects.resolve({
|
|
122
|
+
taskId: task.id,
|
|
123
|
+
key: reviewCheckEffectKey,
|
|
124
|
+
at: reconciliationNow().toISOString()
|
|
125
|
+
});
|
|
126
|
+
stamped.push(task.id);
|
|
127
|
+
} catch {}
|
|
128
|
+
return {
|
|
129
|
+
concluded,
|
|
130
|
+
stamped
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
async function recordReviewCheck(taskId, checkRunId, headSha) {
|
|
134
|
+
await options.stores.taskEffects.record({
|
|
135
|
+
taskId,
|
|
136
|
+
key: reviewCheckEffectKey,
|
|
137
|
+
reference: {
|
|
138
|
+
provider: "github",
|
|
139
|
+
resourceId: String(checkRunId),
|
|
140
|
+
subject: headSha
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
async function updateReviewSummary(review, token) {
|
|
145
|
+
const summary = findSummaryComment(await listPullRequestComments({
|
|
146
|
+
repository: review.repository,
|
|
147
|
+
token,
|
|
148
|
+
fetch: options.fetch,
|
|
149
|
+
pullRequestNumber: review.pullRequestNumber
|
|
150
|
+
}));
|
|
151
|
+
if (summary === void 0 && review.comments.length === 0) return;
|
|
152
|
+
const body = renderSummary({ active: review.comments.map(toFinding).sort(compareFindings) });
|
|
153
|
+
if ((await readPullRequestHead({
|
|
154
|
+
repository: review.repository,
|
|
155
|
+
token,
|
|
156
|
+
fetch: options.fetch,
|
|
157
|
+
pullRequestNumber: review.pullRequestNumber
|
|
158
|
+
})).sha !== review.headSha) return;
|
|
159
|
+
if (summary === void 0) await createPullRequestComment({
|
|
160
|
+
repository: review.repository,
|
|
161
|
+
token,
|
|
162
|
+
fetch: options.fetch,
|
|
163
|
+
pullRequestNumber: review.pullRequestNumber,
|
|
164
|
+
body
|
|
165
|
+
});
|
|
166
|
+
else if (summary.body !== body) await updatePullRequestComment({
|
|
167
|
+
repository: review.repository,
|
|
168
|
+
token,
|
|
169
|
+
fetch: options.fetch,
|
|
170
|
+
commentId: summary.id,
|
|
171
|
+
body
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
function renderSummary(state) {
|
|
175
|
+
const lines = [
|
|
176
|
+
summaryMarker,
|
|
177
|
+
`## ${summaryHeading}`,
|
|
178
|
+
""
|
|
179
|
+
];
|
|
180
|
+
if (state.active.length > 0) lines.push(`${state.active.length} open`, "", "### Open findings", "", ...state.active.flatMap((finding) => [`- **${finding.title}** at \`${finding.path}:${finding.line}\``, ` ${finding.body.replaceAll("\n", "\n ")}`]));
|
|
181
|
+
else lines.push("✅ All findings resolved.");
|
|
182
|
+
lines.push("", encodeState(state));
|
|
183
|
+
return lines.join("\n");
|
|
184
|
+
}
|
|
185
|
+
async function findReviewCheck(review, token) {
|
|
186
|
+
const checkRun = (await listCheckRunsForRef({
|
|
187
|
+
repository: review.repository,
|
|
188
|
+
token,
|
|
189
|
+
fetch: options.fetch,
|
|
190
|
+
ref: review.headSha,
|
|
191
|
+
checkName
|
|
192
|
+
})).find((candidate) => candidate.externalId === review.taskId);
|
|
193
|
+
if (checkRun === void 0) throw new Error(`could not find ${checkName} check for task ${review.taskId} on ${review.headSha}`);
|
|
194
|
+
return checkRun;
|
|
195
|
+
}
|
|
196
|
+
async function concludeAbandonedCheck(task, effect, reconciliationNow) {
|
|
197
|
+
const change = task.changeId === void 0 ? null : await options.stores.changes.get(task.changeId);
|
|
198
|
+
if (change === null) return false;
|
|
199
|
+
const repository = await options.stores.repositories.get(change.repositoryId);
|
|
200
|
+
if (repository === null) return false;
|
|
201
|
+
const token = await options.tokenForRepository(repository.slug);
|
|
202
|
+
const checkRunId = recordedCheckRunId(effect) ?? await discoverCheckRun(repository.slug, token, task, change.prNumber);
|
|
203
|
+
if (checkRunId === void 0) return false;
|
|
204
|
+
const cancelled = task.state === "cancelled";
|
|
205
|
+
await updateCheckRun({
|
|
206
|
+
repository: repository.slug,
|
|
207
|
+
token,
|
|
208
|
+
fetch: options.fetch,
|
|
209
|
+
checkRunId,
|
|
210
|
+
status: "completed",
|
|
211
|
+
conclusion: cancelled ? "cancelled" : "neutral",
|
|
212
|
+
completedAt: reconciliationNow().toISOString(),
|
|
213
|
+
output: {
|
|
214
|
+
title: cancelled ? "Review cancelled" : "Review did not complete",
|
|
215
|
+
summary: `The Factory code reviewer ended as ${task.state} without recording a verdict. Push a new commit to request a fresh review.`
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
return true;
|
|
219
|
+
}
|
|
220
|
+
async function discoverCheckRun(repository, token, task, pullRequestNumber) {
|
|
221
|
+
const head = await readPullRequestHead({
|
|
222
|
+
repository,
|
|
223
|
+
token,
|
|
224
|
+
fetch: options.fetch,
|
|
225
|
+
pullRequestNumber
|
|
226
|
+
});
|
|
227
|
+
return (await listCheckRunsForRef({
|
|
228
|
+
repository,
|
|
229
|
+
token,
|
|
230
|
+
fetch: options.fetch,
|
|
231
|
+
ref: head.sha,
|
|
232
|
+
checkName
|
|
233
|
+
})).find((candidate) => candidate.externalId === task.id && candidate.status !== "completed")?.id;
|
|
234
|
+
}
|
|
235
|
+
return Object.freeze({
|
|
236
|
+
startReviewCheck,
|
|
237
|
+
completeReviewCheck,
|
|
238
|
+
reconcileReviewChecks
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
function findSummaryComment(comments) {
|
|
242
|
+
return comments.find((comment) => comment.authorType === "Bot" && comment.body.includes(summaryMarker));
|
|
243
|
+
}
|
|
244
|
+
function toFinding(finding) {
|
|
245
|
+
return {
|
|
246
|
+
fingerprint: finding.fingerprint,
|
|
247
|
+
title: finding.title.replaceAll(/\s+/g, " ").trim().slice(0, 60),
|
|
248
|
+
path: finding.path.replaceAll("`", "'").slice(0, 120),
|
|
249
|
+
line: finding.line,
|
|
250
|
+
body: finding.body.trim()
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
function encodeState(state) {
|
|
254
|
+
return `${statePrefix}${Buffer.from(JSON.stringify(state)).toString("base64url")}${stateSuffix}`;
|
|
255
|
+
}
|
|
256
|
+
function compareFindings(left, right) {
|
|
257
|
+
return left.path.localeCompare(right.path) || left.line - right.line;
|
|
258
|
+
}
|
|
259
|
+
//#endregion
|
|
260
|
+
export { createGitHubReviewReporter };
|
|
261
|
+
|
|
262
|
+
//# sourceMappingURL=github-reporter.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github-reporter.mjs","names":[],"sources":["../../src/code-review/github-reporter.ts"],"sourcesContent":["import {\n createCheckRun,\n createPullRequestComment,\n listCheckRunsForRef,\n listPullRequestComments,\n readPullRequestHead,\n updateCheckRun,\n updatePullRequestComment,\n type GitHubIssueComment,\n} from \"../github\";\nimport type { TaskId } from \"../schema/id\";\nimport type { Task } from \"../schema/task\";\nimport type { TaskEffect } from \"../schema/task-effect\";\nimport type { FactoryStores } from \"../store/engine\";\nimport type { DateClock } from \"../schema/common\";\nimport type { RepositorySlug } from \"../schema/repository\";\nimport type { CompletedReview, CompletedReviewFinding } from \"./contracts\";\nimport type { StartReviewCheckInput } from \"./preparation\";\n\n/** Storage, credentials, and presentation settings for GitHub review reporting. */\nexport interface GitHubReviewReporterOptions {\n stores: Pick<FactoryStores, \"taskEffects\" | \"changes\" | \"repositories\">;\n tokenForRepository(repositorySlug: RepositorySlug): Promise<string>;\n fetch?: typeof globalThis.fetch;\n checkName?: string;\n summaryHeading?: string;\n /** Whether to maintain a separate finding-summary comment. Defaults to true. */\n publishSummary?: boolean | ((review: CompletedReview) => boolean | Promise<boolean>);\n now?: DateClock;\n}\n\n/** Check-run and pull-request summary lifecycle for supplied code reviews. */\nexport interface GitHubReviewReporter {\n startReviewCheck(input: StartReviewCheckInput): Promise<void>;\n completeReviewCheck(review: CompletedReview): Promise<void>;\n reconcileReviewChecks(input: ReconcileReviewChecksInput): Promise<ReconcileReviewChecksResult>;\n}\n\n/** Bounded rotating-page controls for abandoned review-check recovery. */\nexport interface ReconcileReviewChecksOptions {\n batchSize?: number;\n /** Stable page selector; defaults to the current five-minute recovery window. */\n candidatePage?: number;\n now?: DateClock;\n}\n\n/** Review Tasks and bounded recovery controls for one reconciliation pass. */\nexport interface ReconcileReviewChecksInput extends ReconcileReviewChecksOptions {\n tasks: readonly Task[];\n}\n\n/** Review Tasks whose check effects were concluded or durably resolved in one pass. */\nexport interface ReconcileReviewChecksResult {\n concluded: readonly TaskId[];\n stamped: readonly TaskId[];\n}\n\ninterface ReviewFinding {\n fingerprint: string;\n title: string;\n path: string;\n line: number;\n body: string;\n}\n\ninterface ReviewSummaryState {\n active: ReviewFinding[];\n}\n\nconst summaryMarker = \"<!-- factory-code-review-summary -->\";\nconst statePrefix = \"<!-- factory-code-review-state:v1:\";\nconst stateSuffix = \" -->\";\nconst terminalReviewStates: ReadonlySet<Task[\"state\"]> = new Set([\"cancelled\", \"failed\"]);\nconst reviewCheckBatchSize = 20;\nconst reviewCheckPageIntervalMs = 300_000;\nconst reviewCheckEffectKey = {\n namespace: \"software\",\n name: \"github_review_check\",\n} as const;\n\nfunction recordedCheckRunId(effect: TaskEffect | null, headSha?: string): number | undefined {\n if (effect?.reference === undefined) return undefined;\n const { provider, resourceId } = effect.reference;\n const checkRunId = Number(resourceId);\n if (provider !== \"github\" || !Number.isSafeInteger(checkRunId) || checkRunId <= 0) {\n throw new Error(\n \"Stored software review-check effect has an invalid GitHub check-run reference\",\n );\n }\n if (headSha !== undefined && effect.reference.subject !== headSha) {\n throw new Error(\n `Review task ${effect.taskId} has a check for another commit. Request a new review for ${headSha}.`,\n );\n }\n return checkRunId;\n}\n\n/** Create the supplied GitHub check-run and pull-request summary reporter for code reviews. */\nexport function createGitHubReviewReporter(\n options: GitHubReviewReporterOptions,\n): GitHubReviewReporter {\n const checkName = options.checkName ?? \"Factory code review\";\n const summaryHeading = options.summaryHeading ?? \"Factory code review\";\n const now = () => (options.now ?? (() => new Date()))().toISOString();\n\n async function startReviewCheck(input: StartReviewCheckInput): Promise<void> {\n const token = await options.tokenForRepository(input.repository);\n if (input.reuseExisting) {\n const effect = await options.stores.taskEffects.get({\n taskId: input.taskId,\n key: reviewCheckEffectKey,\n });\n if (\n recordedCheckRunId(effect, input.headSha) !== undefined ||\n effect?.resolvedAt !== undefined\n )\n return;\n const checks = await listCheckRunsForRef({\n repository: input.repository,\n token,\n fetch: options.fetch,\n ref: input.headSha,\n checkName,\n });\n const existing = checks.find((check) => check.externalId === input.taskId);\n if (existing !== undefined) {\n await recordReviewCheck(input.taskId, existing.id, input.headSha);\n return;\n }\n }\n const checkRun = await createCheckRun({\n repository: input.repository,\n token,\n fetch: options.fetch,\n name: checkName,\n headSha: input.headSha,\n status: \"in_progress\",\n externalId: input.taskId,\n detailsUrl: input.pullRequestUrl,\n startedAt: now(),\n output: {\n title: \"Review in progress\",\n summary: \"The Factory code reviewer is inspecting this commit.\",\n },\n });\n await recordReviewCheck(input.taskId, checkRun.id, input.headSha);\n }\n\n async function completeReviewCheck(review: CompletedReview): Promise<void> {\n const token = await options.tokenForRepository(review.repository);\n const effect = await options.stores.taskEffects.get({\n taskId: review.taskId,\n key: reviewCheckEffectKey,\n });\n let checkRunId = recordedCheckRunId(effect, review.headSha);\n if (checkRunId === undefined) {\n checkRunId = (await findReviewCheck(review, token)).id;\n await recordReviewCheck(review.taskId, checkRunId, review.headSha);\n }\n await updateCheckRun({\n repository: review.repository,\n token,\n fetch: options.fetch,\n checkRunId,\n status: \"completed\",\n conclusion: review.verdict === \"request_changes\" ? \"failure\" : \"success\",\n completedAt: now(),\n output: {\n title:\n review.verdict === \"request_changes\" ? \"Review found blocking issues\" : \"Review complete\",\n summary: review.summary,\n annotations: review.comments.map((comment) => ({\n path: comment.path,\n startLine: comment.startLine ?? comment.line,\n endLine: comment.line,\n annotationLevel: review.verdict === \"request_changes\" ? \"failure\" : \"notice\",\n title: comment.title,\n message: comment.body,\n })),\n },\n });\n await options.stores.taskEffects.resolve({\n taskId: review.taskId,\n key: reviewCheckEffectKey,\n });\n const publishSummary =\n typeof options.publishSummary === \"function\"\n ? await options.publishSummary(review)\n : options.publishSummary !== false;\n if (publishSummary) await updateReviewSummary(review, token);\n }\n\n async function reconcileReviewChecks(\n input: ReconcileReviewChecksInput,\n ): Promise<ReconcileReviewChecksResult> {\n const { tasks, ...reconcileOptions } = input;\n const reconciliationNow = reconcileOptions.now ?? options.now ?? (() => new Date());\n const batchSize = reconcileOptions.batchSize ?? reviewCheckBatchSize;\n if (!Number.isSafeInteger(batchSize) || batchSize <= 0) {\n throw new Error(\"Review-check batch size must be a positive safe integer\");\n }\n const candidates = tasks\n .filter(\n (task) => task.work.route?.id === \"code-review\" && terminalReviewStates.has(task.state),\n )\n .sort((left, right) => left.id.localeCompare(right.id));\n const candidatePage =\n reconcileOptions.candidatePage ??\n Math.floor(reconciliationNow().getTime() / reviewCheckPageIntervalMs);\n if (!Number.isSafeInteger(candidatePage) || candidatePage < 0) {\n throw new Error(\"Review-check candidate page must be a nonnegative safe integer\");\n }\n const pageCount = Math.max(1, Math.ceil(candidates.length / batchSize));\n const pageStart = (candidatePage % pageCount) * batchSize;\n const page = candidates.slice(pageStart, pageStart + batchSize);\n const pending = (\n await Promise.all(\n page.map(async (task) => ({\n task,\n effect: await options.stores.taskEffects.get({\n taskId: task.id,\n key: reviewCheckEffectKey,\n }),\n })),\n )\n ).filter(({ effect }) => effect?.resolvedAt === undefined);\n const concluded: TaskId[] = [];\n const stamped: TaskId[] = [];\n for (const { task, effect } of pending) {\n try {\n if (await concludeAbandonedCheck(task, effect, reconciliationNow)) concluded.push(task.id);\n await options.stores.taskEffects.resolve({\n taskId: task.id,\n key: reviewCheckEffectKey,\n at: reconciliationNow().toISOString(),\n });\n stamped.push(task.id);\n } catch {\n // A failed provider operation leaves the stamp absent so the next pass retries it.\n }\n }\n return { concluded, stamped };\n }\n\n async function recordReviewCheck(taskId: TaskId, checkRunId: number, headSha: string) {\n await options.stores.taskEffects.record({\n taskId,\n key: reviewCheckEffectKey,\n reference: {\n provider: \"github\",\n resourceId: String(checkRunId),\n subject: headSha,\n },\n });\n }\n\n async function updateReviewSummary(review: CompletedReview, token: string): Promise<void> {\n const comments = await listPullRequestComments({\n repository: review.repository,\n token,\n fetch: options.fetch,\n pullRequestNumber: review.pullRequestNumber,\n });\n const summary = findSummaryComment(comments);\n if (summary === undefined && review.comments.length === 0) return;\n\n const body = renderSummary({ active: review.comments.map(toFinding).sort(compareFindings) });\n const head = await readPullRequestHead({\n repository: review.repository,\n token,\n fetch: options.fetch,\n pullRequestNumber: review.pullRequestNumber,\n });\n if (head.sha !== review.headSha) return;\n\n if (summary === undefined) {\n await createPullRequestComment({\n repository: review.repository,\n token,\n fetch: options.fetch,\n pullRequestNumber: review.pullRequestNumber,\n body,\n });\n } else if (summary.body !== body) {\n await updatePullRequestComment({\n repository: review.repository,\n token,\n fetch: options.fetch,\n commentId: summary.id,\n body,\n });\n }\n }\n\n function renderSummary(state: ReviewSummaryState): string {\n const lines = [summaryMarker, `## ${summaryHeading}`, \"\"];\n if (state.active.length > 0) {\n lines.push(\n `${state.active.length} open`,\n \"\",\n \"### Open findings\",\n \"\",\n ...state.active.flatMap((finding) => [\n `- **${finding.title}** at \\`${finding.path}:${finding.line}\\``,\n ` ${finding.body.replaceAll(\"\\n\", \"\\n \")}`,\n ]),\n );\n } else {\n lines.push(\"✅ All findings resolved.\");\n }\n lines.push(\"\", encodeState(state));\n return lines.join(\"\\n\");\n }\n\n async function findReviewCheck(review: CompletedReview, token: string) {\n const checkRuns = await listCheckRunsForRef({\n repository: review.repository,\n token,\n fetch: options.fetch,\n ref: review.headSha,\n checkName,\n });\n const checkRun = checkRuns.find((candidate) => candidate.externalId === review.taskId);\n if (checkRun === undefined) {\n throw new Error(\n `could not find ${checkName} check for task ${review.taskId} on ${review.headSha}`,\n );\n }\n return checkRun;\n }\n\n async function concludeAbandonedCheck(\n task: Task,\n effect: TaskEffect | null,\n reconciliationNow: () => Date,\n ) {\n const change =\n task.changeId === undefined ? null : await options.stores.changes.get(task.changeId);\n if (change === null) return false;\n const repository = await options.stores.repositories.get(change.repositoryId);\n if (repository === null) return false;\n const token = await options.tokenForRepository(repository.slug);\n const checkRunId =\n recordedCheckRunId(effect) ??\n (await discoverCheckRun(repository.slug, token, task, change.prNumber));\n if (checkRunId === undefined) return false;\n const cancelled = task.state === \"cancelled\";\n await updateCheckRun({\n repository: repository.slug,\n token,\n fetch: options.fetch,\n checkRunId,\n status: \"completed\",\n conclusion: cancelled ? \"cancelled\" : \"neutral\",\n completedAt: reconciliationNow().toISOString(),\n output: {\n title: cancelled ? \"Review cancelled\" : \"Review did not complete\",\n summary: `The Factory code reviewer ended as ${task.state} without recording a verdict. Push a new commit to request a fresh review.`,\n },\n });\n return true;\n }\n\n async function discoverCheckRun(\n repository: RepositorySlug,\n token: string,\n task: Task,\n pullRequestNumber: number,\n ): Promise<number | undefined> {\n const head = await readPullRequestHead({\n repository,\n token,\n fetch: options.fetch,\n pullRequestNumber,\n });\n const checkRuns = await listCheckRunsForRef({\n repository,\n token,\n fetch: options.fetch,\n ref: head.sha,\n checkName,\n });\n return checkRuns.find(\n (candidate) => candidate.externalId === task.id && candidate.status !== \"completed\",\n )?.id;\n }\n\n return Object.freeze({ startReviewCheck, completeReviewCheck, reconcileReviewChecks });\n}\n\nfunction findSummaryComment(\n comments: readonly GitHubIssueComment[],\n): GitHubIssueComment | undefined {\n return comments.find(\n (comment) => comment.authorType === \"Bot\" && comment.body.includes(summaryMarker),\n );\n}\n\nfunction toFinding(finding: CompletedReviewFinding): ReviewFinding {\n return {\n fingerprint: finding.fingerprint,\n title: finding.title.replaceAll(/\\s+/g, \" \").trim().slice(0, 60),\n path: finding.path.replaceAll(\"`\", \"'\").slice(0, 120),\n line: finding.line,\n body: finding.body.trim(),\n };\n}\n\nfunction encodeState(state: ReviewSummaryState): string {\n return `${statePrefix}${Buffer.from(JSON.stringify(state)).toString(\"base64url\")}${stateSuffix}`;\n}\n\nfunction compareFindings(left: ReviewFinding, right: ReviewFinding): number {\n return left.path.localeCompare(right.path) || left.line - right.line;\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { TaskId } from "../schema/id.mjs";
|
|
2
|
+
import { RepositorySlug } from "../schema/repository.mjs";
|
|
3
|
+
import { Task } from "../schema/task.mjs";
|
|
4
|
+
import { FactoryStores } from "../store/engine.mjs";
|
|
5
|
+
//#region src/code-review/preparation.d.ts
|
|
6
|
+
/** Commit-fenced pull-request details used to begin a review check run. */
|
|
7
|
+
interface StartReviewCheckInput {
|
|
8
|
+
repository: RepositorySlug;
|
|
9
|
+
headSha: string;
|
|
10
|
+
taskId: TaskId;
|
|
11
|
+
pullRequestUrl: string;
|
|
12
|
+
reuseExisting?: boolean;
|
|
13
|
+
}
|
|
14
|
+
/** Provider operations used to fence and initialize a pull-request review. */
|
|
15
|
+
interface PreparePullRequestReviewOptions {
|
|
16
|
+
readHead(input: ReadPullRequestReviewHeadInput): Promise<string>;
|
|
17
|
+
startCheck(input: Omit<StartReviewCheckInput, "reuseExisting">): Promise<void>;
|
|
18
|
+
}
|
|
19
|
+
/** Repository and pull-request identity used to read a review target head. */
|
|
20
|
+
interface ReadPullRequestReviewHeadInput {
|
|
21
|
+
repository: RepositorySlug;
|
|
22
|
+
pullRequestNumber: number;
|
|
23
|
+
}
|
|
24
|
+
/** Stores, Task, message, and provider operations used to prepare one review. */
|
|
25
|
+
interface PreparePullRequestReviewInput extends PreparePullRequestReviewOptions {
|
|
26
|
+
stores: Pick<FactoryStores, "changes" | "repositories">;
|
|
27
|
+
task: Task;
|
|
28
|
+
message: string;
|
|
29
|
+
}
|
|
30
|
+
/** Prepare the linked pull request head and reporting check before launching a reviewer. */
|
|
31
|
+
declare function preparePullRequestReview(input: PreparePullRequestReviewInput): Promise<string>;
|
|
32
|
+
//#endregion
|
|
33
|
+
export { PreparePullRequestReviewInput, PreparePullRequestReviewOptions, ReadPullRequestReviewHeadInput, StartReviewCheckInput, preparePullRequestReview };
|
|
34
|
+
//# sourceMappingURL=preparation.d.mts.map
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
//#region src/code-review/preparation.ts
|
|
2
|
+
/** Prepare the linked pull request head and reporting check before launching a reviewer. */
|
|
3
|
+
async function preparePullRequestReview(input) {
|
|
4
|
+
const change = input.task.changeId === void 0 ? null : await input.stores.changes.get(input.task.changeId);
|
|
5
|
+
if (change === null || input.task.repositoryIds.length !== 1 || change.repositoryId !== input.task.repositoryIds[0]) throw new Error("Code review requires its linked Change");
|
|
6
|
+
const repository = await input.stores.repositories.get(change.repositoryId);
|
|
7
|
+
if (repository === null || !repository.enabled) throw new Error("Code review repository is unavailable");
|
|
8
|
+
const headSha = await input.readHead({
|
|
9
|
+
repository: repository.slug,
|
|
10
|
+
pullRequestNumber: change.prNumber
|
|
11
|
+
});
|
|
12
|
+
await input.startCheck({
|
|
13
|
+
repository: repository.slug,
|
|
14
|
+
headSha,
|
|
15
|
+
taskId: input.task.id,
|
|
16
|
+
pullRequestUrl: change.prUrl
|
|
17
|
+
});
|
|
18
|
+
return `${input.message}\n\nPull request: ${change.prUrl}.\nExpected head commit: ${headSha}.\nReview only this head and finish with record_post_review.`;
|
|
19
|
+
}
|
|
20
|
+
//#endregion
|
|
21
|
+
export { preparePullRequestReview };
|
|
22
|
+
|
|
23
|
+
//# sourceMappingURL=preparation.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preparation.mjs","names":[],"sources":["../../src/code-review/preparation.ts"],"sourcesContent":["import type { TaskId } from \"../schema/id\";\nimport type { RepositorySlug } from \"../schema/repository\";\nimport type { Task } from \"../schema/task\";\nimport type { FactoryStores } from \"../store/engine\";\n\n/** Commit-fenced pull-request details used to begin a review check run. */\nexport interface StartReviewCheckInput {\n repository: RepositorySlug;\n headSha: string;\n taskId: TaskId;\n pullRequestUrl: string;\n reuseExisting?: boolean;\n}\n\n/** Provider operations used to fence and initialize a pull-request review. */\nexport interface PreparePullRequestReviewOptions {\n readHead(input: ReadPullRequestReviewHeadInput): Promise<string>;\n startCheck(input: Omit<StartReviewCheckInput, \"reuseExisting\">): Promise<void>;\n}\n\n/** Repository and pull-request identity used to read a review target head. */\nexport interface ReadPullRequestReviewHeadInput {\n repository: RepositorySlug;\n pullRequestNumber: number;\n}\n\n/** Stores, Task, message, and provider operations used to prepare one review. */\nexport interface PreparePullRequestReviewInput extends PreparePullRequestReviewOptions {\n stores: Pick<FactoryStores, \"changes\" | \"repositories\">;\n task: Task;\n message: string;\n}\n\n/** Prepare the linked pull request head and reporting check before launching a reviewer. */\nexport async function preparePullRequestReview(\n input: PreparePullRequestReviewInput,\n): Promise<string> {\n const change =\n input.task.changeId === undefined ? null : await input.stores.changes.get(input.task.changeId);\n if (\n change === null ||\n input.task.repositoryIds.length !== 1 ||\n change.repositoryId !== input.task.repositoryIds[0]\n ) {\n throw new Error(\"Code review requires its linked Change\");\n }\n const repository = await input.stores.repositories.get(change.repositoryId);\n if (repository === null || !repository.enabled) {\n throw new Error(\"Code review repository is unavailable\");\n }\n const headSha = await input.readHead({\n repository: repository.slug,\n pullRequestNumber: change.prNumber,\n });\n await input.startCheck({\n repository: repository.slug,\n headSha,\n taskId: input.task.id,\n pullRequestUrl: change.prUrl,\n });\n return `${input.message}\\n\\nPull request: ${change.prUrl}.\\nExpected head commit: ${headSha}.\\nReview only this head and finish with record_post_review.`;\n}\n"],"mappings":";;AAkCA,eAAsB,yBACpB,OACiB;CACjB,MAAM,SACJ,MAAM,KAAK,aAAa,KAAA,IAAY,OAAO,MAAM,MAAM,OAAO,QAAQ,IAAI,MAAM,KAAK,QAAQ;CAC/F,IACE,WAAW,QACX,MAAM,KAAK,cAAc,WAAW,KACpC,OAAO,iBAAiB,MAAM,KAAK,cAAc,IAEjD,MAAM,IAAI,MAAM,wCAAwC;CAE1D,MAAM,aAAa,MAAM,MAAM,OAAO,aAAa,IAAI,OAAO,YAAY;CAC1E,IAAI,eAAe,QAAQ,CAAC,WAAW,SACrC,MAAM,IAAI,MAAM,uCAAuC;CAEzD,MAAM,UAAU,MAAM,MAAM,SAAS;EACnC,YAAY,WAAW;EACvB,mBAAmB,OAAO;CAC5B,CAAC;CACD,MAAM,MAAM,WAAW;EACrB,YAAY,WAAW;EACvB;EACA,QAAQ,MAAM,KAAK;EACnB,gBAAgB,OAAO;CACzB,CAAC;CACD,OAAO,GAAG,MAAM,QAAQ,oBAAoB,OAAO,MAAM,2BAA2B,QAAQ;AAC9F"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { TaskId } from "../schema/id.mjs";
|
|
2
|
+
import { FactoryStores } from "../store/engine.mjs";
|
|
3
|
+
import { StallSweepOptions, TaskStallSweepResult } from "../stall.mjs";
|
|
4
|
+
//#region src/code-review/stall.d.ts
|
|
5
|
+
/** Task capability required by code-review stall supersession. */
|
|
6
|
+
type CodeReviewStallSweepStores = Pick<FactoryStores, "tasks">;
|
|
7
|
+
/** Generic stall-sweep controls with the supplied review supersession policy fixed. */
|
|
8
|
+
type SweepCodeReviewStallsOptions = Omit<StallSweepOptions, "cancelStalled">;
|
|
9
|
+
/** Generic stall results with obsolete code reviews reported as superseded. */
|
|
10
|
+
interface SweepCodeReviewStallsResult extends Omit<TaskStallSweepResult, "cancelled"> {
|
|
11
|
+
readonly superseded: readonly TaskId[];
|
|
12
|
+
}
|
|
13
|
+
/** Apply the supplied code-review supersession policy around the generic stall sweep. */
|
|
14
|
+
declare function sweepCodeReviewStalls(stores: CodeReviewStallSweepStores, options: SweepCodeReviewStallsOptions): Promise<SweepCodeReviewStallsResult>;
|
|
15
|
+
//#endregion
|
|
16
|
+
export { CodeReviewStallSweepStores, SweepCodeReviewStallsOptions, SweepCodeReviewStallsResult, sweepCodeReviewStalls };
|
|
17
|
+
//# sourceMappingURL=stall.d.mts.map
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { sweepTaskStalls } from "../stall.mjs";
|
|
2
|
+
//#region src/code-review/stall.ts
|
|
3
|
+
const supersedingReviewStates = /* @__PURE__ */ new Set([
|
|
4
|
+
"queued",
|
|
5
|
+
"running",
|
|
6
|
+
"stalled",
|
|
7
|
+
"needs_human"
|
|
8
|
+
]);
|
|
9
|
+
function supersedingCodeReview(task, tasks) {
|
|
10
|
+
if (task.work.route?.id !== "code-review" || task.changeId === void 0) return void 0;
|
|
11
|
+
return tasks.find((candidate) => candidate.id !== task.id && candidate.work.route?.id === "code-review" && !candidate.waitingForMessages && candidate.changeId === task.changeId && supersedingReviewStates.has(candidate.state) && Date.parse(candidate.createdAt) > Date.parse(task.createdAt));
|
|
12
|
+
}
|
|
13
|
+
/** Apply the supplied code-review supersession policy around the generic stall sweep. */
|
|
14
|
+
async function sweepCodeReviewStalls(stores, options) {
|
|
15
|
+
const { cancelled: superseded, ...generic } = await sweepTaskStalls(stores, {
|
|
16
|
+
...options,
|
|
17
|
+
cancelStalled(task, tasks) {
|
|
18
|
+
const successor = supersedingCodeReview(task, tasks);
|
|
19
|
+
return successor === void 0 ? null : `review superseded by newer commits: task ${successor.id} reviews the current head`;
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
return {
|
|
23
|
+
...generic,
|
|
24
|
+
superseded
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
//#endregion
|
|
28
|
+
export { sweepCodeReviewStalls };
|
|
29
|
+
|
|
30
|
+
//# sourceMappingURL=stall.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stall.mjs","names":[],"sources":["../../src/code-review/stall.ts"],"sourcesContent":["import type { TaskId } from \"../schema/id\";\nimport type { Task } from \"../schema/task\";\nimport type { FactoryStores } from \"../store/engine\";\nimport { sweepTaskStalls, type StallSweepOptions, type TaskStallSweepResult } from \"../stall\";\n\n/** Task capability required by code-review stall supersession. */\nexport type CodeReviewStallSweepStores = Pick<FactoryStores, \"tasks\">;\n\nconst supersedingReviewStates: ReadonlySet<Task[\"state\"]> = new Set([\n \"queued\",\n \"running\",\n \"stalled\",\n \"needs_human\",\n]);\n\nfunction supersedingCodeReview(task: Task, tasks: readonly Task[]): Task | undefined {\n if (task.work.route?.id !== \"code-review\" || task.changeId === undefined) return undefined;\n return tasks.find(\n (candidate) =>\n candidate.id !== task.id &&\n candidate.work.route?.id === \"code-review\" &&\n !candidate.waitingForMessages &&\n candidate.changeId === task.changeId &&\n supersedingReviewStates.has(candidate.state) &&\n Date.parse(candidate.createdAt) > Date.parse(task.createdAt),\n );\n}\n\n/** Generic stall-sweep controls with the supplied review supersession policy fixed. */\nexport type SweepCodeReviewStallsOptions = Omit<StallSweepOptions, \"cancelStalled\">;\n\n/** Generic stall results with obsolete code reviews reported as superseded. */\nexport interface SweepCodeReviewStallsResult extends Omit<TaskStallSweepResult, \"cancelled\"> {\n readonly superseded: readonly TaskId[];\n}\n\n/** Apply the supplied code-review supersession policy around the generic stall sweep. */\nexport async function sweepCodeReviewStalls(\n stores: CodeReviewStallSweepStores,\n options: SweepCodeReviewStallsOptions,\n): Promise<SweepCodeReviewStallsResult> {\n const result = await sweepTaskStalls(stores, {\n ...options,\n cancelStalled(task, tasks) {\n const successor = supersedingCodeReview(task, tasks);\n return successor === undefined\n ? null\n : `review superseded by newer commits: task ${successor.id} reviews the current head`;\n },\n });\n const { cancelled: superseded, ...generic } = result;\n return { ...generic, superseded };\n}\n"],"mappings":";;AAQA,MAAM,0CAAsD,IAAI,IAAI;CAClE;CACA;CACA;CACA;AACF,CAAC;AAED,SAAS,sBAAsB,MAAY,OAA0C;CACnF,IAAI,KAAK,KAAK,OAAO,OAAO,iBAAiB,KAAK,aAAa,KAAA,GAAW,OAAO,KAAA;CACjF,OAAO,MAAM,MACV,cACC,UAAU,OAAO,KAAK,MACtB,UAAU,KAAK,OAAO,OAAO,iBAC7B,CAAC,UAAU,sBACX,UAAU,aAAa,KAAK,YAC5B,wBAAwB,IAAI,UAAU,KAAK,KAC3C,KAAK,MAAM,UAAU,SAAS,IAAI,KAAK,MAAM,KAAK,SAAS,CAC/D;AACF;;AAWA,eAAsB,sBACpB,QACA,SACsC;CAUtC,MAAM,EAAE,WAAW,YAAY,GAAG,YAAY,MATzB,gBAAgB,QAAQ;EAC3C,GAAG;EACH,cAAc,MAAM,OAAO;GACzB,MAAM,YAAY,sBAAsB,MAAM,KAAK;GACnD,OAAO,cAAc,KAAA,IACjB,OACA,4CAA4C,UAAU,GAAG;EAC/D;CACF,CAAC;CAED,OAAO;EAAE,GAAG;EAAS;CAAW;AAClC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import { CompletedReview, CompletedReviewFinding } from "./code-review/contracts.mjs";
|
|
3
|
+
import { GitHubPostReviewCommentInput, GitHubPostReviewFailureCode, GitHubPostReviewInput, GitHubPostReviewResult, GitHubPostReviewTool, GitHubPostReviewToolOptions, GitHubPostReviewToolStores, createGitHubPostReviewTool } from "./code-review/eve-post-review.mjs";
|
|
4
|
+
import { GitHubAnswerReviewCommentInput, GitHubApplyReviewFixInput, GitHubApplyReviewFixResult, GitHubListPullRequestDiscussionInput, GitHubReadReviewCommentInput, GitHubReviewCommentTools, GitHubReviewCommentToolsOptions, GitHubReviewCommentToolsStores, GitHubSuggestReviewFixInput, createGitHubReviewCommentTools } from "./code-review/eve-review-comments.mjs";
|
|
5
|
+
import { PreparePullRequestReviewInput, PreparePullRequestReviewOptions, ReadPullRequestReviewHeadInput, StartReviewCheckInput, preparePullRequestReview } from "./code-review/preparation.mjs";
|
|
6
|
+
import { CodeReviewStallSweepStores, SweepCodeReviewStallsOptions, SweepCodeReviewStallsResult, sweepCodeReviewStalls } from "./code-review/stall.mjs";
|
|
7
|
+
export { type CodeReviewStallSweepStores, type CompletedReview, type CompletedReviewFinding, type GitHubAnswerReviewCommentInput, type GitHubApplyReviewFixInput, type GitHubApplyReviewFixResult, type GitHubListPullRequestDiscussionInput, type GitHubPostReviewCommentInput, type GitHubPostReviewFailureCode, type GitHubPostReviewInput, type GitHubPostReviewResult, type GitHubPostReviewTool, type GitHubPostReviewToolOptions, type GitHubPostReviewToolStores, type GitHubReadReviewCommentInput, type GitHubReviewCommentTools, type GitHubReviewCommentToolsOptions, type GitHubReviewCommentToolsStores, type GitHubSuggestReviewFixInput, type PreparePullRequestReviewInput, type PreparePullRequestReviewOptions, type ReadPullRequestReviewHeadInput, type StartReviewCheckInput, type SweepCodeReviewStallsOptions, type SweepCodeReviewStallsResult, createGitHubPostReviewTool, createGitHubReviewCommentTools, preparePullRequestReview, sweepCodeReviewStalls };
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { createGitHubPostReviewTool } from "./code-review/eve-post-review.mjs";
|
|
2
|
+
import { createGitHubReviewCommentTools } from "./code-review/eve-review-comments.mjs";
|
|
3
|
+
import { preparePullRequestReview } from "./code-review/preparation.mjs";
|
|
4
|
+
import { sweepCodeReviewStalls } from "./code-review/stall.mjs";
|
|
5
|
+
export { createGitHubPostReviewTool, createGitHubReviewCommentTools, preparePullRequestReview, sweepCodeReviewStalls };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { OutboxId, outboxIdSchema } from "./schema/id.mjs";
|
|
2
|
+
import { ReplyTo, replyToSchema } from "./schema/common.mjs";
|
|
3
|
+
import { CommunicationEvent, CommunicationOutcome, communicationEventSchema, communicationOutcomeSchema, communicationOutcomes } from "./schema/communication.mjs";
|
|
4
|
+
import { OutboxMessage, OutboxQueue, outboxMessageSchema, outboxQueueSchema, outboxStateSchema } from "./schema/outbox.mjs";
|
|
5
|
+
import { OutboxEventConflictError, RetryOutboxMessageInput } from "./store/engine.mjs";
|
|
6
|
+
import { CommunicationOutboxStores, CreateOutboxCommunicationRouterOptions, OutboxDeliveryOptions, OutboxSweepResult, SweepCommunicationOutboxOptions, createOutboxCommunicationRouter, sweepCommunicationOutbox } from "./connectors/outbox.mjs";
|
|
7
|
+
import { CommunicationAdapter, CommunicationRouter, NormalizedSignal, SignalNormalizer, WebhookEvent, createCommunicationRouter } from "./connectors/index.mjs";
|
|
8
|
+
import { TaskCommunicationProjection, TaskCommunicationReconciler, TaskCommunicationResult, TaskCommunicationStores, TaskLifecycleProjectionInput, createTaskCommunicationReconciler } from "./task-communication.mjs";
|
|
9
|
+
export { type CommunicationAdapter, type CommunicationEvent, type CommunicationOutboxStores, type CommunicationOutcome, type CommunicationRouter, type CreateOutboxCommunicationRouterOptions, type NormalizedSignal, type OutboxDeliveryOptions, OutboxEventConflictError, type OutboxId, type OutboxMessage, type OutboxQueue, type OutboxSweepResult, type ReplyTo, type RetryOutboxMessageInput, type SignalNormalizer, type SweepCommunicationOutboxOptions, type TaskCommunicationProjection, type TaskCommunicationReconciler, type TaskCommunicationResult, type TaskCommunicationStores, type TaskLifecycleProjectionInput, type WebhookEvent, communicationEventSchema, communicationOutcomeSchema, communicationOutcomes, createCommunicationRouter, createOutboxCommunicationRouter, createTaskCommunicationReconciler, outboxIdSchema, outboxMessageSchema, outboxQueueSchema, outboxStateSchema, replyToSchema, sweepCommunicationOutbox };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { outboxIdSchema } from "./schema/id.mjs";
|
|
2
|
+
import { replyToSchema } from "./schema/common.mjs";
|
|
3
|
+
import { communicationEventSchema, communicationOutcomeSchema, communicationOutcomes } from "./schema/communication.mjs";
|
|
4
|
+
import { outboxMessageSchema, outboxQueueSchema, outboxStateSchema } from "./schema/outbox.mjs";
|
|
5
|
+
import { OutboxEventConflictError } from "./store/engine.mjs";
|
|
6
|
+
import { createOutboxCommunicationRouter, sweepCommunicationOutbox } from "./connectors/outbox.mjs";
|
|
7
|
+
import { createCommunicationRouter } from "./connectors/index.mjs";
|
|
8
|
+
import { createTaskCommunicationReconciler } from "./task-communication.mjs";
|
|
9
|
+
export { OutboxEventConflictError, communicationEventSchema, communicationOutcomeSchema, communicationOutcomes, createCommunicationRouter, createOutboxCommunicationRouter, createTaskCommunicationReconciler, outboxIdSchema, outboxMessageSchema, outboxQueueSchema, outboxStateSchema, replyToSchema, sweepCommunicationOutbox };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { RepositorySlug } from "../schema/repository.mjs";
|
|
2
|
+
import { MaybePromise } from "../schema/common.mjs";
|
|
3
|
+
import { CommunicationEvent } from "../schema/communication.mjs";
|
|
4
|
+
import { CommunicationAdapter } from "./index.mjs";
|
|
5
|
+
//#region src/connectors/github.d.ts
|
|
6
|
+
/** A GitHub repository and issue or pull-request number encoded by a reply destination. */
|
|
7
|
+
interface GitHubReplyTarget {
|
|
8
|
+
repository: RepositorySlug;
|
|
9
|
+
number: number;
|
|
10
|
+
}
|
|
11
|
+
/** Parse a GitHub conversation address into its repository and issue or pull-request number. */
|
|
12
|
+
declare function parseGitHubReplyAddress(address: string): GitHubReplyTarget;
|
|
13
|
+
/** Render a provider-neutral communication event as a GitHub Markdown comment. */
|
|
14
|
+
declare function renderGitHubCommunicationEvent(event: CommunicationEvent): string;
|
|
15
|
+
/** Configures repository-scoped authentication, rendering, and transport for GitHub delivery. */
|
|
16
|
+
interface GitHubCommunicationAdapterOptions {
|
|
17
|
+
token(repository: RepositorySlug): MaybePromise<string>;
|
|
18
|
+
render?(event: CommunicationEvent): string;
|
|
19
|
+
fetch?: typeof globalThis.fetch;
|
|
20
|
+
userAgent?: string;
|
|
21
|
+
}
|
|
22
|
+
/** Deliver Factory communication events as GitHub issue or pull-request comments. */
|
|
23
|
+
declare function createGitHubCommunicationAdapter(options: GitHubCommunicationAdapterOptions): CommunicationAdapter;
|
|
24
|
+
//#endregion
|
|
25
|
+
export { GitHubCommunicationAdapterOptions, GitHubReplyTarget, createGitHubCommunicationAdapter, parseGitHubReplyAddress, renderGitHubCommunicationEvent };
|
|
26
|
+
//# sourceMappingURL=github.d.mts.map
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { repositorySlugSchema } from "../schema/repository.mjs";
|
|
2
|
+
import { communicationEventSchema } from "../schema/communication.mjs";
|
|
3
|
+
//#region src/connectors/github.ts
|
|
4
|
+
/** Parse a GitHub conversation address into its repository and issue or pull-request number. */
|
|
5
|
+
function parseGitHubReplyAddress(address) {
|
|
6
|
+
const match = /^(?<repository>[^/]+\/[^/#]+)(?:#|\/pull\/)(?<number>[1-9][0-9]*)$/u.exec(address);
|
|
7
|
+
if (match?.groups?.repository === void 0 || match.groups.number === void 0) throw new Error(`Invalid GitHub reply address: ${address}.`);
|
|
8
|
+
return {
|
|
9
|
+
repository: repositorySlugSchema.parse(match.groups.repository.toLowerCase()),
|
|
10
|
+
number: Number(match.groups.number)
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
/** Render a provider-neutral communication event as a GitHub Markdown comment. */
|
|
14
|
+
function renderGitHubCommunicationEvent(event) {
|
|
15
|
+
const detail = [
|
|
16
|
+
"question",
|
|
17
|
+
"summary",
|
|
18
|
+
"message",
|
|
19
|
+
"reason"
|
|
20
|
+
].map((field) => event.data[field]).find((value) => typeof value === "string" && value.trim() !== "");
|
|
21
|
+
const options = Array.isArray(event.data.options) ? event.data.options.filter((value) => typeof value === "string") : [];
|
|
22
|
+
return [detail ?? "Factory update", ...options.map((option) => `- ${option}`)].join("\n");
|
|
23
|
+
}
|
|
24
|
+
/** Deliver Factory communication events as GitHub issue or pull-request comments. */
|
|
25
|
+
function createGitHubCommunicationAdapter(options) {
|
|
26
|
+
const fetchImplementation = options.fetch ?? globalThis.fetch;
|
|
27
|
+
const render = options.render ?? renderGitHubCommunicationEvent;
|
|
28
|
+
return Object.freeze({
|
|
29
|
+
channel: "github",
|
|
30
|
+
async deliver(input) {
|
|
31
|
+
const event = communicationEventSchema.parse(input);
|
|
32
|
+
if (event.replyTo.channel !== "github") throw new Error(`GitHub communication adapter cannot deliver to ${event.replyTo.channel}.`);
|
|
33
|
+
const target = parseGitHubReplyAddress(event.replyTo.address);
|
|
34
|
+
const response = await fetchImplementation(`https://api.github.com/repos/${target.repository}/issues/${target.number}/comments`, {
|
|
35
|
+
method: "POST",
|
|
36
|
+
headers: {
|
|
37
|
+
accept: "application/vnd.github+json",
|
|
38
|
+
authorization: `Bearer ${await options.token(target.repository)}`,
|
|
39
|
+
"content-type": "application/json",
|
|
40
|
+
"user-agent": options.userAgent ?? "agent-factory"
|
|
41
|
+
},
|
|
42
|
+
body: JSON.stringify({ body: render(event) })
|
|
43
|
+
});
|
|
44
|
+
if (!response.ok) {
|
|
45
|
+
await response.body?.cancel();
|
|
46
|
+
throw new Error(`GitHub communication delivery failed: ${response.status}`);
|
|
47
|
+
}
|
|
48
|
+
await response.body?.cancel();
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
//#endregion
|
|
53
|
+
export { createGitHubCommunicationAdapter, parseGitHubReplyAddress, renderGitHubCommunicationEvent };
|
|
54
|
+
|
|
55
|
+
//# sourceMappingURL=github.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github.mjs","names":[],"sources":["../../src/connectors/github.ts"],"sourcesContent":["import {\n communicationEventSchema,\n type CommunicationAdapter,\n type CommunicationEvent,\n} from \"./index\";\nimport type { MaybePromise } from \"../schema/common\";\nimport { repositorySlugSchema, type RepositorySlug } from \"../schema/repository\";\n\n/** A GitHub repository and issue or pull-request number encoded by a reply destination. */\nexport interface GitHubReplyTarget {\n repository: RepositorySlug;\n number: number;\n}\n\n/** Parse a GitHub conversation address into its repository and issue or pull-request number. */\nexport function parseGitHubReplyAddress(address: string): GitHubReplyTarget {\n const match = /^(?<repository>[^/]+\\/[^/#]+)(?:#|\\/pull\\/)(?<number>[1-9][0-9]*)$/u.exec(address);\n if (match?.groups?.repository === undefined || match.groups.number === undefined) {\n throw new Error(`Invalid GitHub reply address: ${address}.`);\n }\n return {\n repository: repositorySlugSchema.parse(match.groups.repository.toLowerCase()),\n number: Number(match.groups.number),\n };\n}\n\n/** Render a provider-neutral communication event as a GitHub Markdown comment. */\nexport function renderGitHubCommunicationEvent(event: CommunicationEvent): string {\n const detail = [\"question\", \"summary\", \"message\", \"reason\"]\n .map((field) => event.data[field])\n .find((value): value is string => typeof value === \"string\" && value.trim() !== \"\");\n const options = Array.isArray(event.data.options)\n ? event.data.options.filter((value): value is string => typeof value === \"string\")\n : [];\n return [detail ?? \"Factory update\", ...options.map((option) => `- ${option}`)].join(\"\\n\");\n}\n\n/** Configures repository-scoped authentication, rendering, and transport for GitHub delivery. */\nexport interface GitHubCommunicationAdapterOptions {\n token(repository: RepositorySlug): MaybePromise<string>;\n render?(event: CommunicationEvent): string;\n fetch?: typeof globalThis.fetch;\n userAgent?: string;\n}\n\n/** Deliver Factory communication events as GitHub issue or pull-request comments. */\nexport function createGitHubCommunicationAdapter(\n options: GitHubCommunicationAdapterOptions,\n): CommunicationAdapter {\n const fetchImplementation = options.fetch ?? globalThis.fetch;\n const render = options.render ?? renderGitHubCommunicationEvent;\n return Object.freeze({\n channel: \"github\",\n async deliver(input: CommunicationEvent): Promise<void> {\n const event = communicationEventSchema.parse(input);\n if (event.replyTo.channel !== \"github\") {\n throw new Error(`GitHub communication adapter cannot deliver to ${event.replyTo.channel}.`);\n }\n const target = parseGitHubReplyAddress(event.replyTo.address);\n const response = await fetchImplementation(\n `https://api.github.com/repos/${target.repository}/issues/${target.number}/comments`,\n {\n method: \"POST\",\n headers: {\n accept: \"application/vnd.github+json\",\n authorization: `Bearer ${await options.token(target.repository)}`,\n \"content-type\": \"application/json\",\n \"user-agent\": options.userAgent ?? \"agent-factory\",\n },\n body: JSON.stringify({ body: render(event) }),\n },\n );\n if (!response.ok) {\n await response.body?.cancel();\n throw new Error(`GitHub communication delivery failed: ${response.status}`);\n }\n await response.body?.cancel();\n },\n });\n}\n"],"mappings":";;;;AAeA,SAAgB,wBAAwB,SAAoC;CAC1E,MAAM,QAAQ,sEAAsE,KAAK,OAAO;CAChG,IAAI,OAAO,QAAQ,eAAe,KAAA,KAAa,MAAM,OAAO,WAAW,KAAA,GACrE,MAAM,IAAI,MAAM,iCAAiC,QAAQ,EAAE;CAE7D,OAAO;EACL,YAAY,qBAAqB,MAAM,MAAM,OAAO,WAAW,YAAY,CAAC;EAC5E,QAAQ,OAAO,MAAM,OAAO,MAAM;CACpC;AACF;;AAGA,SAAgB,+BAA+B,OAAmC;CAChF,MAAM,SAAS;EAAC;EAAY;EAAW;EAAW;CAAQ,CAAC,CACxD,KAAK,UAAU,MAAM,KAAK,MAAM,CAAC,CACjC,MAAM,UAA2B,OAAO,UAAU,YAAY,MAAM,KAAK,MAAM,EAAE;CACpF,MAAM,UAAU,MAAM,QAAQ,MAAM,KAAK,OAAO,IAC5C,MAAM,KAAK,QAAQ,QAAQ,UAA2B,OAAO,UAAU,QAAQ,IAC/E,CAAC;CACL,OAAO,CAAC,UAAU,kBAAkB,GAAG,QAAQ,KAAK,WAAW,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,IAAI;AAC1F;;AAWA,SAAgB,iCACd,SACsB;CACtB,MAAM,sBAAsB,QAAQ,SAAS,WAAW;CACxD,MAAM,SAAS,QAAQ,UAAU;CACjC,OAAO,OAAO,OAAO;EACnB,SAAS;EACT,MAAM,QAAQ,OAA0C;GACtD,MAAM,QAAQ,yBAAyB,MAAM,KAAK;GAClD,IAAI,MAAM,QAAQ,YAAY,UAC5B,MAAM,IAAI,MAAM,kDAAkD,MAAM,QAAQ,QAAQ,EAAE;GAE5F,MAAM,SAAS,wBAAwB,MAAM,QAAQ,OAAO;GAC5D,MAAM,WAAW,MAAM,oBACrB,gCAAgC,OAAO,WAAW,UAAU,OAAO,OAAO,YAC1E;IACE,QAAQ;IACR,SAAS;KACP,QAAQ;KACR,eAAe,UAAU,MAAM,QAAQ,MAAM,OAAO,UAAU;KAC9D,gBAAgB;KAChB,cAAc,QAAQ,aAAa;IACrC;IACA,MAAM,KAAK,UAAU,EAAE,MAAM,OAAO,KAAK,EAAE,CAAC;GAC9C,CACF;GACA,IAAI,CAAC,SAAS,IAAI;IAChB,MAAM,SAAS,MAAM,OAAO;IAC5B,MAAM,IAAI,MAAM,yCAAyC,SAAS,QAAQ;GAC5E;GACA,MAAM,SAAS,MAAM,OAAO;EAC9B;CACF,CAAC;AACH"}
|
|
@@ -1,3 +1,35 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
1
|
+
import { SignalSource } from "../schema/signal.mjs";
|
|
2
|
+
import { ReplyTo } from "../schema/common.mjs";
|
|
3
|
+
import { CommunicationEvent, CommunicationOutcome, communicationEventSchema, communicationOutcomeSchema, communicationOutcomes } from "../schema/communication.mjs";
|
|
4
|
+
import { CommunicationOutboxStores, CreateOutboxCommunicationRouterOptions, OutboxDeliveryOptions, OutboxSweepResult, SweepCommunicationOutboxOptions, createOutboxCommunicationRouter, sweepCommunicationOutbox } from "./outbox.mjs";
|
|
5
|
+
//#region src/connectors/index.d.ts
|
|
6
|
+
/** An authenticated provider webhook envelope before connector-specific normalization. */
|
|
7
|
+
interface WebhookEvent {
|
|
8
|
+
deliveryId: string;
|
|
9
|
+
event: string;
|
|
10
|
+
payload: unknown;
|
|
11
|
+
}
|
|
12
|
+
/** Connector-neutral signal data ready for admission into Factory. */
|
|
13
|
+
interface NormalizedSignal<TSource extends SignalSource = SignalSource> {
|
|
14
|
+
source: TSource;
|
|
15
|
+
deliveryId: string;
|
|
16
|
+
replyTo: ReplyTo;
|
|
17
|
+
payload: unknown;
|
|
18
|
+
}
|
|
19
|
+
/** Converts a verified webhook into a Factory signal or deliberately ignores it. */
|
|
20
|
+
type SignalNormalizer<TSignal extends NormalizedSignal = NormalizedSignal> = (event: WebhookEvent) => TSignal | null;
|
|
21
|
+
/** Delivers communication events for one provider channel. */
|
|
22
|
+
interface CommunicationAdapter {
|
|
23
|
+
readonly channel: string;
|
|
24
|
+
deliver(event: CommunicationEvent): Promise<void>;
|
|
25
|
+
}
|
|
26
|
+
/** Routes validated communication events to adapters by reply channel. */
|
|
27
|
+
interface CommunicationRouter {
|
|
28
|
+
readonly channels: readonly string[];
|
|
29
|
+
deliver(event: CommunicationEvent): Promise<void>;
|
|
30
|
+
}
|
|
31
|
+
/** Create a channel router that rejects duplicate registrations and unsupported destinations. */
|
|
32
|
+
declare function createCommunicationRouter(adapters: readonly CommunicationAdapter[]): CommunicationRouter;
|
|
33
|
+
//#endregion
|
|
34
|
+
export { CommunicationAdapter, type CommunicationEvent, type CommunicationOutboxStores, type CommunicationOutcome, CommunicationRouter, type CreateOutboxCommunicationRouterOptions, NormalizedSignal, type OutboxDeliveryOptions, type OutboxSweepResult, SignalNormalizer, type SweepCommunicationOutboxOptions, WebhookEvent, communicationEventSchema, communicationOutcomeSchema, communicationOutcomes, createCommunicationRouter, createOutboxCommunicationRouter, sweepCommunicationOutbox };
|
|
35
|
+
//# sourceMappingURL=index.d.mts.map
|