clinkx 0.3.0 → 1.0.0
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 +33 -0
- package/LICENSE +10 -0
- package/README.md +82 -26
- package/clinkx-workflows/dist/allowed-roots.d.ts +1 -0
- package/clinkx-workflows/dist/allowed-roots.js +5 -0
- package/clinkx-workflows/dist/allowed-roots.js.map +1 -0
- package/clinkx-workflows/dist/artifacts.d.ts +20 -1
- package/clinkx-workflows/dist/artifacts.js +38 -9
- package/clinkx-workflows/dist/artifacts.js.map +1 -1
- package/clinkx-workflows/dist/backend.d.ts +4 -0
- package/clinkx-workflows/dist/citation-check.d.ts +40 -0
- package/clinkx-workflows/dist/citation-check.js +348 -0
- package/clinkx-workflows/dist/citation-check.js.map +1 -0
- package/clinkx-workflows/dist/clink-client.d.ts +2 -1
- package/clinkx-workflows/dist/clink-client.js +17 -4
- package/clinkx-workflows/dist/clink-client.js.map +1 -1
- package/clinkx-workflows/dist/config.d.ts +145 -16
- package/clinkx-workflows/dist/config.js +384 -26
- package/clinkx-workflows/dist/config.js.map +1 -1
- package/clinkx-workflows/dist/contained-file.d.ts +172 -0
- package/clinkx-workflows/dist/contained-file.js +483 -0
- package/clinkx-workflows/dist/contained-file.js.map +1 -0
- package/clinkx-workflows/dist/context-builder-lifecycle-contract.d.ts +416 -0
- package/clinkx-workflows/dist/context-builder-lifecycle-contract.js +328 -0
- package/clinkx-workflows/dist/context-builder-lifecycle-contract.js.map +1 -0
- package/clinkx-workflows/dist/context-builder-lifecycle-router.d.ts +24 -0
- package/clinkx-workflows/dist/context-builder-lifecycle-router.js +39 -0
- package/clinkx-workflows/dist/context-builder-lifecycle-router.js.map +1 -0
- package/clinkx-workflows/dist/context-cache-key.d.ts +53 -0
- package/clinkx-workflows/dist/context-cache-key.js +91 -0
- package/clinkx-workflows/dist/context-cache-key.js.map +1 -0
- package/clinkx-workflows/dist/context-mode-resolver.d.ts +27 -0
- package/clinkx-workflows/dist/context-mode-resolver.js +126 -0
- package/clinkx-workflows/dist/context-mode-resolver.js.map +1 -0
- package/clinkx-workflows/dist/context-preflight.d.ts +184 -0
- package/clinkx-workflows/dist/context-preflight.js +91 -0
- package/clinkx-workflows/dist/context-preflight.js.map +1 -0
- package/clinkx-workflows/dist/council-expander.d.ts +1 -1
- package/clinkx-workflows/dist/council-expander.js +2 -1
- package/clinkx-workflows/dist/council-expander.js.map +1 -1
- package/clinkx-workflows/dist/dashboard/activity-contract.d.ts +165 -0
- package/clinkx-workflows/dist/dashboard/activity-contract.js +105 -0
- package/clinkx-workflows/dist/dashboard/activity-contract.js.map +1 -0
- package/clinkx-workflows/dist/dashboard/activity-lease.d.ts +25 -0
- package/clinkx-workflows/dist/dashboard/activity-lease.js +34 -0
- package/clinkx-workflows/dist/dashboard/activity-lease.js.map +1 -0
- package/clinkx-workflows/dist/dashboard/activity-reader.d.ts +86 -0
- package/clinkx-workflows/dist/dashboard/activity-reader.js +480 -0
- package/clinkx-workflows/dist/dashboard/activity-reader.js.map +1 -0
- package/clinkx-workflows/dist/dashboard/api-routes.d.ts +36 -0
- package/clinkx-workflows/dist/dashboard/api-routes.js +1047 -0
- package/clinkx-workflows/dist/dashboard/api-routes.js.map +1 -0
- package/clinkx-workflows/dist/dashboard/artifact-index.d.ts +37 -0
- package/clinkx-workflows/dist/dashboard/artifact-index.js +145 -0
- package/clinkx-workflows/dist/dashboard/artifact-index.js.map +1 -0
- package/clinkx-workflows/dist/dashboard/auth/challenge-store.d.ts +31 -0
- package/clinkx-workflows/dist/dashboard/auth/challenge-store.js +65 -0
- package/clinkx-workflows/dist/dashboard/auth/challenge-store.js.map +1 -0
- package/clinkx-workflows/dist/dashboard/auth/controller.d.ts +26 -0
- package/clinkx-workflows/dist/dashboard/auth/controller.js +596 -0
- package/clinkx-workflows/dist/dashboard/auth/controller.js.map +1 -0
- package/clinkx-workflows/dist/dashboard/auth/credential-store.d.ts +60 -0
- package/clinkx-workflows/dist/dashboard/auth/credential-store.js +776 -0
- package/clinkx-workflows/dist/dashboard/auth/credential-store.js.map +1 -0
- package/clinkx-workflows/dist/dashboard/auth/enrollment-gate.d.ts +9 -0
- package/clinkx-workflows/dist/dashboard/auth/enrollment-gate.js +44 -0
- package/clinkx-workflows/dist/dashboard/auth/enrollment-gate.js.map +1 -0
- package/clinkx-workflows/dist/dashboard/auth/session-store.d.ts +17 -0
- package/clinkx-workflows/dist/dashboard/auth/session-store.js +77 -0
- package/clinkx-workflows/dist/dashboard/auth/session-store.js.map +1 -0
- package/clinkx-workflows/dist/dashboard/auth/webauthn-service.d.ts +25 -0
- package/clinkx-workflows/dist/dashboard/auth/webauthn-service.js +71 -0
- package/clinkx-workflows/dist/dashboard/auth/webauthn-service.js.map +1 -0
- package/clinkx-workflows/dist/dashboard/context-run-liveness.d.ts +37 -0
- package/clinkx-workflows/dist/dashboard/context-run-liveness.js +49 -0
- package/clinkx-workflows/dist/dashboard/context-run-liveness.js.map +1 -0
- package/clinkx-workflows/dist/dashboard/context-run-reader.d.ts +222 -0
- package/clinkx-workflows/dist/dashboard/context-run-reader.js +1178 -0
- package/clinkx-workflows/dist/dashboard/context-run-reader.js.map +1 -0
- package/clinkx-workflows/dist/dashboard/contracts.d.ts +8936 -0
- package/clinkx-workflows/dist/dashboard/contracts.js +1157 -0
- package/clinkx-workflows/dist/dashboard/contracts.js.map +1 -0
- package/clinkx-workflows/dist/dashboard/generation.d.ts +14 -0
- package/clinkx-workflows/dist/dashboard/generation.js +41 -0
- package/clinkx-workflows/dist/dashboard/generation.js.map +1 -0
- package/clinkx-workflows/dist/dashboard/index.d.ts +28 -0
- package/clinkx-workflows/dist/dashboard/index.js +16 -0
- package/clinkx-workflows/dist/dashboard/index.js.map +1 -0
- package/clinkx-workflows/dist/dashboard/lazy-content.d.ts +101 -0
- package/clinkx-workflows/dist/dashboard/lazy-content.js +748 -0
- package/clinkx-workflows/dist/dashboard/lazy-content.js.map +1 -0
- package/clinkx-workflows/dist/dashboard/progress-report-contract.d.ts +303 -0
- package/clinkx-workflows/dist/dashboard/progress-report-contract.js +221 -0
- package/clinkx-workflows/dist/dashboard/progress-report-contract.js.map +1 -0
- package/clinkx-workflows/dist/dashboard/progress-report-reader.d.ts +50 -0
- package/clinkx-workflows/dist/dashboard/progress-report-reader.js +581 -0
- package/clinkx-workflows/dist/dashboard/progress-report-reader.js.map +1 -0
- package/clinkx-workflows/dist/dashboard/progress-report-status-overlay.d.ts +89 -0
- package/clinkx-workflows/dist/dashboard/progress-report-status-overlay.js +71 -0
- package/clinkx-workflows/dist/dashboard/progress-report-status-overlay.js.map +1 -0
- package/clinkx-workflows/dist/dashboard/progress-report-status-store.d.ts +28 -0
- package/clinkx-workflows/dist/dashboard/progress-report-status-store.js +110 -0
- package/clinkx-workflows/dist/dashboard/progress-report-status-store.js.map +1 -0
- package/clinkx-workflows/dist/dashboard/read-model.d.ts +86 -0
- package/clinkx-workflows/dist/dashboard/read-model.js +440 -0
- package/clinkx-workflows/dist/dashboard/read-model.js.map +1 -0
- package/clinkx-workflows/dist/dashboard/request-admission.d.ts +31 -0
- package/clinkx-workflows/dist/dashboard/request-admission.js +126 -0
- package/clinkx-workflows/dist/dashboard/request-admission.js.map +1 -0
- package/clinkx-workflows/dist/dashboard/scanner.d.ts +75 -0
- package/clinkx-workflows/dist/dashboard/scanner.js +1110 -0
- package/clinkx-workflows/dist/dashboard/scanner.js.map +1 -0
- package/clinkx-workflows/dist/dashboard/server-factory.d.ts +50 -0
- package/clinkx-workflows/dist/dashboard/server-factory.js +431 -0
- package/clinkx-workflows/dist/dashboard/server-factory.js.map +1 -0
- package/clinkx-workflows/dist/dashboard/session-log-contract.d.ts +2611 -0
- package/clinkx-workflows/dist/dashboard/session-log-contract.js +480 -0
- package/clinkx-workflows/dist/dashboard/session-log-contract.js.map +1 -0
- package/clinkx-workflows/dist/dashboard/session-log-identity.d.ts +21 -0
- package/clinkx-workflows/dist/dashboard/session-log-identity.js +101 -0
- package/clinkx-workflows/dist/dashboard/session-log-identity.js.map +1 -0
- package/clinkx-workflows/dist/dashboard/session-log-journal.d.ts +43 -0
- package/clinkx-workflows/dist/dashboard/session-log-journal.js +522 -0
- package/clinkx-workflows/dist/dashboard/session-log-journal.js.map +1 -0
- package/clinkx-workflows/dist/dashboard/session-log-reader.d.ts +61 -0
- package/clinkx-workflows/dist/dashboard/session-log-reader.js +378 -0
- package/clinkx-workflows/dist/dashboard/session-log-reader.js.map +1 -0
- package/clinkx-workflows/dist/dashboard/session-log-redaction.d.ts +6 -0
- package/clinkx-workflows/dist/dashboard/session-log-redaction.js +367 -0
- package/clinkx-workflows/dist/dashboard/session-log-redaction.js.map +1 -0
- package/clinkx-workflows/dist/dashboard/session-log-retention.d.ts +23 -0
- package/clinkx-workflows/dist/dashboard/session-log-retention.js +212 -0
- package/clinkx-workflows/dist/dashboard/session-log-retention.js.map +1 -0
- package/clinkx-workflows/dist/dashboard/static-responses.d.ts +10 -0
- package/clinkx-workflows/dist/dashboard/static-responses.js +94 -0
- package/clinkx-workflows/dist/dashboard/static-responses.js.map +1 -0
- package/clinkx-workflows/dist/dashboard/worker-lock-observer.d.ts +28 -0
- package/clinkx-workflows/dist/dashboard/worker-lock-observer.js +241 -0
- package/clinkx-workflows/dist/dashboard/worker-lock-observer.js.map +1 -0
- package/clinkx-workflows/dist/dashboard-ui/assets/ibm-plex-mono-latin-400-normal-CvHOgSBP.woff +0 -0
- package/clinkx-workflows/dist/dashboard-ui/assets/ibm-plex-mono-latin-400-normal-DMJ8VG8y.woff2 +0 -0
- package/clinkx-workflows/dist/dashboard-ui/assets/ibm-plex-mono-latin-500-normal-CB9ihrfo.woff +0 -0
- package/clinkx-workflows/dist/dashboard-ui/assets/ibm-plex-mono-latin-500-normal-DSY6xOcd.woff2 +0 -0
- package/clinkx-workflows/dist/dashboard-ui/assets/ibm-plex-sans-latin-400-normal-CDDApCn2.woff2 +0 -0
- package/clinkx-workflows/dist/dashboard-ui/assets/ibm-plex-sans-latin-400-normal-CYLoc0-x.woff +0 -0
- package/clinkx-workflows/dist/dashboard-ui/assets/ibm-plex-sans-latin-500-normal-6ng42L7E.woff2 +0 -0
- package/clinkx-workflows/dist/dashboard-ui/assets/ibm-plex-sans-latin-500-normal-BgVn5rGT.woff +0 -0
- package/clinkx-workflows/dist/dashboard-ui/assets/ibm-plex-sans-latin-600-normal-Cu4Hd6ag.woff +0 -0
- package/clinkx-workflows/dist/dashboard-ui/assets/ibm-plex-sans-latin-600-normal-CuJfVYMP.woff2 +0 -0
- package/clinkx-workflows/dist/dashboard-ui/assets/index-DOkMXTfB.js +25 -0
- package/clinkx-workflows/dist/dashboard-ui/assets/jetbrains-mono-latin-400-normal-6-qcROiO.woff +0 -0
- package/clinkx-workflows/dist/dashboard-ui/assets/jetbrains-mono-latin-400-normal-V6pRDFza.woff2 +0 -0
- package/clinkx-workflows/dist/dashboard-ui/assets/jetbrains-mono-latin-500-normal-BWZEU5yA.woff2 +0 -0
- package/clinkx-workflows/dist/dashboard-ui/assets/jetbrains-mono-latin-500-normal-CJOVTJB7.woff +0 -0
- package/clinkx-workflows/dist/dashboard-ui/assets/style-ChJsn0KD.css +1 -0
- package/clinkx-workflows/dist/dashboard-ui/contracts/council-plan-review-input-v1.json +158 -0
- package/clinkx-workflows/dist/dashboard-ui/index.html +15 -0
- package/clinkx-workflows/dist/dashboard-ui/licenses/IBM-Plex-Mono-OFL.txt +93 -0
- package/clinkx-workflows/dist/dashboard-ui/licenses/IBM-Plex-Sans-OFL.txt +93 -0
- package/clinkx-workflows/dist/dashboard-ui/licenses/JetBrains-Mono-OFL.txt +93 -0
- package/clinkx-workflows/dist/dashboard-ui/licenses/NOTICE.md +32 -0
- package/clinkx-workflows/dist/dashboard-ui/theme-init.js +8 -0
- package/clinkx-workflows/dist/definition-normalizer.d.ts +19 -0
- package/clinkx-workflows/dist/definition-normalizer.js.map +1 -1
- package/clinkx-workflows/dist/delegate-validate.d.ts +78 -0
- package/clinkx-workflows/dist/delegate-validate.js +97 -0
- package/clinkx-workflows/dist/delegate-validate.js.map +1 -0
- package/clinkx-workflows/dist/engine.d.ts +141 -0
- package/clinkx-workflows/dist/engine.js +882 -28
- package/clinkx-workflows/dist/engine.js.map +1 -1
- package/clinkx-workflows/dist/errors.d.ts +2 -0
- package/clinkx-workflows/dist/errors.js.map +1 -1
- package/clinkx-workflows/dist/fingerprint.d.ts +1 -1
- package/clinkx-workflows/dist/fingerprint.js +1 -1
- package/clinkx-workflows/dist/index.d.ts +19 -0
- package/clinkx-workflows/dist/index.js +15 -0
- package/clinkx-workflows/dist/index.js.map +1 -1
- package/clinkx-workflows/dist/loader.d.ts +9 -3
- package/clinkx-workflows/dist/loader.js +92 -21
- package/clinkx-workflows/dist/loader.js.map +1 -1
- package/clinkx-workflows/dist/plan-review/admission.d.ts +160 -0
- package/clinkx-workflows/dist/plan-review/admission.js +290 -0
- package/clinkx-workflows/dist/plan-review/admission.js.map +1 -0
- package/clinkx-workflows/dist/plan-review/context-policy.d.ts +55 -0
- package/clinkx-workflows/dist/plan-review/context-policy.js +146 -0
- package/clinkx-workflows/dist/plan-review/context-policy.js.map +1 -0
- package/clinkx-workflows/dist/plan-review/contract.d.ts +763 -0
- package/clinkx-workflows/dist/plan-review/contract.js +289 -0
- package/clinkx-workflows/dist/plan-review/contract.js.map +1 -0
- package/clinkx-workflows/dist/plan-review/digest.d.ts +68 -0
- package/clinkx-workflows/dist/plan-review/digest.js +241 -0
- package/clinkx-workflows/dist/plan-review/digest.js.map +1 -0
- package/clinkx-workflows/dist/plan-review/discovery.d.ts +45 -0
- package/clinkx-workflows/dist/plan-review/discovery.js +36 -0
- package/clinkx-workflows/dist/plan-review/discovery.js.map +1 -0
- package/clinkx-workflows/dist/plan-review/execution-contract.d.ts +298 -0
- package/clinkx-workflows/dist/plan-review/execution-contract.js +194 -0
- package/clinkx-workflows/dist/plan-review/execution-contract.js.map +1 -0
- package/clinkx-workflows/dist/plan-review/expander.d.ts +48 -0
- package/clinkx-workflows/dist/plan-review/expander.js +243 -0
- package/clinkx-workflows/dist/plan-review/expander.js.map +1 -0
- package/clinkx-workflows/dist/plan-review/projector.d.ts +126 -0
- package/clinkx-workflows/dist/plan-review/projector.js +437 -0
- package/clinkx-workflows/dist/plan-review/projector.js.map +1 -0
- package/clinkx-workflows/dist/plan-review/publication.d.ts +95 -0
- package/clinkx-workflows/dist/plan-review/publication.js +182 -0
- package/clinkx-workflows/dist/plan-review/publication.js.map +1 -0
- package/clinkx-workflows/dist/plan-review/ranking-reducer.d.ts +34 -0
- package/clinkx-workflows/dist/plan-review/ranking-reducer.js +139 -0
- package/clinkx-workflows/dist/plan-review/ranking-reducer.js.map +1 -0
- package/clinkx-workflows/dist/plan-review/resume.d.ts +38 -0
- package/clinkx-workflows/dist/plan-review/resume.js +122 -0
- package/clinkx-workflows/dist/plan-review/resume.js.map +1 -0
- package/clinkx-workflows/dist/plan-review/rubric.d.ts +7 -0
- package/clinkx-workflows/dist/plan-review/rubric.js +24 -0
- package/clinkx-workflows/dist/plan-review/rubric.js.map +1 -0
- package/clinkx-workflows/dist/plan-review/run-state.d.ts +398 -0
- package/clinkx-workflows/dist/plan-review/run-state.js +183 -0
- package/clinkx-workflows/dist/plan-review/run-state.js.map +1 -0
- package/clinkx-workflows/dist/plan-review/runtime-policy.d.ts +74 -0
- package/clinkx-workflows/dist/plan-review/runtime-policy.js +186 -0
- package/clinkx-workflows/dist/plan-review/runtime-policy.js.map +1 -0
- package/clinkx-workflows/dist/plan-review/snapshot.d.ts +81 -0
- package/clinkx-workflows/dist/plan-review/snapshot.js +170 -0
- package/clinkx-workflows/dist/plan-review/snapshot.js.map +1 -0
- package/clinkx-workflows/dist/plan-review/stage-error.d.ts +19 -0
- package/clinkx-workflows/dist/plan-review/stage-error.js +20 -0
- package/clinkx-workflows/dist/plan-review/stage-error.js.map +1 -0
- package/clinkx-workflows/dist/post-processors/index.d.ts +20 -0
- package/clinkx-workflows/dist/post-processors/index.js +21 -0
- package/clinkx-workflows/dist/post-processors/index.js.map +1 -0
- package/clinkx-workflows/dist/prompt-budget.d.ts +7 -0
- package/clinkx-workflows/dist/prompt-budget.js +7 -1
- package/clinkx-workflows/dist/prompt-budget.js.map +1 -1
- package/clinkx-workflows/dist/reducers/index.js +2 -0
- package/clinkx-workflows/dist/reducers/index.js.map +1 -1
- package/clinkx-workflows/dist/run-lock-heartbeat.d.ts +2 -0
- package/clinkx-workflows/dist/run-lock-heartbeat.js +3 -0
- package/clinkx-workflows/dist/run-lock-heartbeat.js.map +1 -0
- package/clinkx-workflows/dist/run-summary/cards/council-plan-review.d.ts +34 -0
- package/clinkx-workflows/dist/run-summary/cards/council-plan-review.js +74 -0
- package/clinkx-workflows/dist/run-summary/cards/council-plan-review.js.map +1 -0
- package/clinkx-workflows/dist/run-summary/cards/index.js +5 -0
- package/clinkx-workflows/dist/run-summary/cards/index.js.map +1 -1
- package/clinkx-workflows/dist/run-summary.d.ts +13 -0
- package/clinkx-workflows/dist/run-summary.js +29 -1
- package/clinkx-workflows/dist/run-summary.js.map +1 -1
- package/clinkx-workflows/dist/schema.d.ts +76 -21
- package/clinkx-workflows/dist/schema.js +10 -1
- package/clinkx-workflows/dist/schema.js.map +1 -1
- package/clinkx-workflows/dist/seed-terms.d.ts +30 -0
- package/clinkx-workflows/dist/seed-terms.js +176 -0
- package/clinkx-workflows/dist/seed-terms.js.map +1 -0
- package/clinkx-workflows/dist/server.d.ts +5 -2
- package/clinkx-workflows/dist/server.js +82 -52
- package/clinkx-workflows/dist/server.js.map +1 -1
- package/clinkx-workflows/dist/shutdown.d.ts +18 -20
- package/clinkx-workflows/dist/shutdown.js +43 -61
- package/clinkx-workflows/dist/shutdown.js.map +1 -1
- package/clinkx-workflows/dist/state-schema.d.ts +38 -0
- package/clinkx-workflows/dist/state.d.ts +170 -3
- package/clinkx-workflows/dist/state.js +1275 -28
- package/clinkx-workflows/dist/state.js.map +1 -1
- package/clinkx-workflows/dist/transport.d.ts +15 -0
- package/clinkx-workflows/dist/transport.js +19 -10
- package/clinkx-workflows/dist/transport.js.map +1 -1
- package/clinkx-workflows/dist/types.d.ts +14 -2
- package/clinkx-workflows/dist/validators/council.d.ts +318 -85
- package/clinkx-workflows/dist/validators/council.js +52 -13
- package/clinkx-workflows/dist/validators/council.js.map +1 -1
- package/clinkx-workflows/dist/validators/index.d.ts +1 -0
- package/clinkx-workflows/dist/validators/index.js +3 -0
- package/clinkx-workflows/dist/validators/index.js.map +1 -1
- package/clinkx-workflows/dist/validators/plan-review.d.ts +398 -0
- package/clinkx-workflows/dist/validators/plan-review.js +316 -0
- package/clinkx-workflows/dist/validators/plan-review.js.map +1 -0
- package/clinkx-workflows/dist/workflow-receipt.js +188 -4
- package/clinkx-workflows/dist/workflow-receipt.js.map +1 -1
- package/clinkx-workflows/dist/workflow-tools.d.ts +48 -3
- package/clinkx-workflows/dist/workflow-tools.js +1402 -59
- package/clinkx-workflows/dist/workflow-tools.js.map +1 -1
- package/clinkx-workflows/dist/workspace-signature.d.ts +16 -0
- package/clinkx-workflows/dist/workspace-signature.js +388 -0
- package/clinkx-workflows/dist/workspace-signature.js.map +1 -0
- package/clinkx-workflows/templates/council/answer-specialist.txt +2 -1
- package/clinkx-workflows/templates/council/code-review-chairman.txt +13 -0
- package/clinkx-workflows/templates/council/code-review-cross-reviewer.txt +4 -1
- package/clinkx-workflows/templates/council/code-review-reviewer.txt +19 -3
- package/clinkx-workflows/templates/council/debug-debugger.txt +2 -1
- package/clinkx-workflows/templates/council/discover-analyst.txt +2 -1
- package/clinkx-workflows/templates/council/member.txt +2 -1
- package/clinkx-workflows/templates/council/plan-review-chairman.txt +68 -0
- package/clinkx-workflows/templates/council/plan-review-cross-reviewer.txt +51 -0
- package/clinkx-workflows/templates/council/plan-review-reviewer.txt +60 -0
- package/clinkx-workflows/templates/plan-review/chairman.txt +48 -0
- package/clinkx-workflows/templates/plan-review/cross-review.txt +52 -0
- package/clinkx-workflows/templates/plan-review/ranking.txt +16 -0
- package/clinkx-workflows/templates/plan-review/reviewer.txt +57 -0
- package/clinkx-workflows/workflows/council-answer.yaml +97 -8
- package/clinkx-workflows/workflows/council-code-review.yaml +103 -8
- package/clinkx-workflows/workflows/council-debug.yaml +97 -8
- package/clinkx-workflows/workflows/council-default.yaml +97 -8
- package/clinkx-workflows/workflows/council-discover.yaml +97 -8
- package/clinkx-workflows/workflows/council-plan-review.yaml +250 -0
- package/clinkx-workflows/workflows/plan-review-receipts.yaml +230 -0
- package/conf/adapters/agy.json +49 -0
- package/conf/adapters/claude.json +5 -0
- package/conf/adapters/codex-app-server.json +44 -0
- package/conf/adapters/codex.json +7 -2
- package/conf/adapters/glm.json +5 -0
- package/conf/adapters/grok.json +60 -0
- package/conf/adapters/hapi/agy.json +52 -0
- package/conf/adapters/hapi/claude.json +5 -0
- package/conf/adapters/hapi/codex.json +7 -2
- package/conf/adapters/hapi/glm.json +5 -0
- package/dist/activity-journal.d.ts +42 -0
- package/dist/activity-journal.js +381 -0
- package/dist/activity-journal.js.map +1 -0
- package/dist/allowed-roots.d.ts +41 -0
- package/dist/allowed-roots.js +125 -3
- package/dist/allowed-roots.js.map +1 -1
- package/dist/codex/activity-session-log.d.ts +14 -0
- package/dist/codex/activity-session-log.js +206 -0
- package/dist/codex/activity-session-log.js.map +1 -0
- package/dist/codex/agent-tools.d.ts +23 -0
- package/dist/codex/agent-tools.js +1270 -0
- package/dist/codex/agent-tools.js.map +1 -0
- package/dist/codex/app-server-client.d.ts +76 -0
- package/dist/codex/app-server-client.js +582 -0
- package/dist/codex/app-server-client.js.map +1 -0
- package/dist/codex/app-server-registry.d.ts +389 -0
- package/dist/codex/app-server-registry.js +1407 -0
- package/dist/codex/app-server-registry.js.map +1 -0
- package/dist/codex/client-types.d.ts +109 -0
- package/dist/codex/client-types.js +2 -0
- package/dist/codex/client-types.js.map +1 -0
- package/dist/codex/protocol.d.ts +5375 -0
- package/dist/codex/protocol.js +614 -0
- package/dist/codex/protocol.js.map +1 -0
- package/dist/codex/session.d.ts +263 -0
- package/dist/codex/session.js +466 -0
- package/dist/codex/session.js.map +1 -0
- package/dist/config.d.ts +185 -19
- package/dist/config.js +82 -16
- package/dist/config.js.map +1 -1
- package/dist/context-builder/anchors.d.ts +1 -0
- package/dist/context-builder/anchors.js +3 -0
- package/dist/context-builder/anchors.js.map +1 -1
- package/dist/context-builder/bundle.d.ts +35 -2
- package/dist/context-builder/bundle.js +342 -2417
- package/dist/context-builder/bundle.js.map +1 -1
- package/dist/context-builder/codemap.d.ts +5 -0
- package/dist/context-builder/codemap.js +387 -13
- package/dist/context-builder/codemap.js.map +1 -1
- package/dist/context-builder/config.d.ts +1 -0
- package/dist/context-builder/config.js +12 -0
- package/dist/context-builder/config.js.map +1 -1
- package/dist/context-builder/context-run-catalog.d.ts +17 -0
- package/dist/context-builder/context-run-catalog.js +99 -0
- package/dist/context-builder/context-run-catalog.js.map +1 -0
- package/dist/context-builder/delegate-packet.d.ts +37 -0
- package/dist/context-builder/delegate-packet.js +359 -67
- package/dist/context-builder/delegate-packet.js.map +1 -1
- package/dist/context-builder/delegate-validation.d.ts +10 -1
- package/dist/context-builder/delegate-validation.js +153 -41
- package/dist/context-builder/delegate-validation.js.map +1 -1
- package/dist/context-builder/delegate.d.ts +2 -0
- package/dist/context-builder/delegate.js +1 -0
- package/dist/context-builder/delegate.js.map +1 -1
- package/dist/context-builder/diagnostics.d.ts +11 -2
- package/dist/context-builder/diagnostics.js +39 -6
- package/dist/context-builder/diagnostics.js.map +1 -1
- package/dist/context-builder/discovery.d.ts +29 -1
- package/dist/context-builder/discovery.js +475 -175
- package/dist/context-builder/discovery.js.map +1 -1
- package/dist/context-builder/globs.js +10 -10
- package/dist/context-builder/globs.js.map +1 -1
- package/dist/context-builder/handler.d.ts +26 -2
- package/dist/context-builder/handler.js +1158 -2416
- package/dist/context-builder/handler.js.map +1 -1
- package/dist/context-builder/lifecycle-contract.d.ts +424 -0
- package/dist/context-builder/lifecycle-contract.js +523 -0
- package/dist/context-builder/lifecycle-contract.js.map +1 -0
- package/dist/context-builder/lifecycle-journal.d.ts +62 -0
- package/dist/context-builder/lifecycle-journal.js +88 -0
- package/dist/context-builder/lifecycle-journal.js.map +1 -0
- package/dist/context-builder/lifecycle-observer.d.ts +102 -0
- package/dist/context-builder/lifecycle-observer.js +258 -0
- package/dist/context-builder/lifecycle-observer.js.map +1 -0
- package/dist/context-builder/lifecycle-reconcile.d.ts +32 -0
- package/dist/context-builder/lifecycle-reconcile.js +191 -0
- package/dist/context-builder/lifecycle-reconcile.js.map +1 -0
- package/dist/context-builder/lifecycle-recovery.d.ts +2 -0
- package/dist/context-builder/lifecycle-recovery.js +14 -0
- package/dist/context-builder/lifecycle-recovery.js.map +1 -0
- package/dist/context-builder/path-signals.js +9 -5
- package/dist/context-builder/path-signals.js.map +1 -1
- package/dist/context-builder/progress.d.ts +3 -1
- package/dist/context-builder/progress.js +23 -0
- package/dist/context-builder/progress.js.map +1 -1
- package/dist/context-builder/receipt.d.ts +6 -4
- package/dist/context-builder/receipt.js +12 -1
- package/dist/context-builder/receipt.js.map +1 -1
- package/dist/context-builder/refine-handler.d.ts +14 -0
- package/dist/context-builder/refine-handler.js +790 -0
- package/dist/context-builder/refine-handler.js.map +1 -0
- package/dist/context-builder/review-coverage.d.ts +84 -0
- package/dist/context-builder/review-coverage.js +437 -0
- package/dist/context-builder/review-coverage.js.map +1 -0
- package/dist/context-builder/schema.d.ts +239 -1
- package/dist/context-builder/schema.js +112 -6
- package/dist/context-builder/schema.js.map +1 -1
- package/dist/context-builder/secrets.d.ts +28 -0
- package/dist/context-builder/secrets.js +1473 -44
- package/dist/context-builder/secrets.js.map +1 -1
- package/dist/context-builder/selection.d.ts +7 -3
- package/dist/context-builder/selection.js +200 -738
- package/dist/context-builder/selection.js.map +1 -1
- package/dist/context-builder/session-log-identity.d.ts +1 -0
- package/dist/context-builder/session-log-identity.js +66 -0
- package/dist/context-builder/session-log-identity.js.map +1 -0
- package/dist/context-builder/slice-range-math.d.ts +16 -0
- package/dist/context-builder/slice-range-math.js +137 -0
- package/dist/context-builder/slice-range-math.js.map +1 -0
- package/dist/context-builder/status.d.ts +22 -1
- package/dist/context-builder/status.js +59 -4
- package/dist/context-builder/status.js.map +1 -1
- package/dist/context-builder/storage.d.ts +35 -2
- package/dist/context-builder/storage.js +271 -14
- package/dist/context-builder/storage.js.map +1 -1
- package/dist/context-builder/token-estimate.d.ts +13 -2
- package/dist/context-builder/token-estimate.js +77 -4
- package/dist/context-builder/token-estimate.js.map +1 -1
- package/dist/context-builder/versions.d.ts +29 -0
- package/dist/context-builder/versions.js +30 -0
- package/dist/context-builder/versions.js.map +1 -0
- package/dist/context-builder/workflow-runner.d.ts +69 -0
- package/dist/context-builder/workflow-runner.js +1051 -0
- package/dist/context-builder/workflow-runner.js.map +1 -0
- package/dist/context-builder/write-watchdog.d.ts +14 -0
- package/dist/context-builder/write-watchdog.js +38 -0
- package/dist/context-builder/write-watchdog.js.map +1 -0
- package/dist/dashboard-adapter-catalog.d.ts +9 -0
- package/dist/dashboard-adapter-catalog.js +46 -0
- package/dist/dashboard-adapter-catalog.js.map +1 -0
- package/dist/dashboard-cli.d.ts +79 -0
- package/dist/dashboard-cli.js +585 -0
- package/dist/dashboard-cli.js.map +1 -0
- package/dist/dashboard-companion/client.d.ts +36 -0
- package/dist/dashboard-companion/client.js +210 -0
- package/dist/dashboard-companion/client.js.map +1 -0
- package/dist/dashboard-companion/config.d.ts +8 -0
- package/dist/dashboard-companion/config.js +52 -0
- package/dist/dashboard-companion/config.js.map +1 -0
- package/dist/dashboard-companion/http-proxy.d.ts +10 -0
- package/dist/dashboard-companion/http-proxy.js +234 -0
- package/dist/dashboard-companion/http-proxy.js.map +1 -0
- package/dist/dashboard-companion/protocol.d.ts +80 -0
- package/dist/dashboard-companion/protocol.js +455 -0
- package/dist/dashboard-companion/protocol.js.map +1 -0
- package/dist/dashboard-companion/standalone-assets.d.ts +12 -0
- package/dist/dashboard-companion/standalone-assets.js +72 -0
- package/dist/dashboard-companion/standalone-assets.js.map +1 -0
- package/dist/dashboard-companion/standalone-entry.d.ts +1 -0
- package/dist/dashboard-companion/standalone-entry.js +17 -0
- package/dist/dashboard-companion/standalone-entry.js.map +1 -0
- package/dist/dashboard-gateway/config.d.ts +9 -0
- package/dist/dashboard-gateway/config.js +44 -0
- package/dist/dashboard-gateway/config.js.map +1 -0
- package/dist/dashboard-gateway/index.d.ts +11 -0
- package/dist/dashboard-gateway/index.js +755 -0
- package/dist/dashboard-gateway/index.js.map +1 -0
- package/dist/dashboard-gateway/main.d.ts +1 -0
- package/dist/dashboard-gateway/main.js +25 -0
- package/dist/dashboard-gateway/main.js.map +1 -0
- package/dist/dashboard-pairing-token.d.ts +1 -0
- package/dist/dashboard-pairing-token.js +5 -0
- package/dist/dashboard-pairing-token.js.map +1 -0
- package/dist/elaris/client.d.ts +73 -0
- package/dist/elaris/client.js +825 -0
- package/dist/elaris/client.js.map +1 -0
- package/dist/elaris/config.d.ts +18 -0
- package/dist/elaris/config.js +75 -0
- package/dist/elaris/config.js.map +1 -0
- package/dist/elaris/errors.d.ts +41 -0
- package/dist/elaris/errors.js +205 -0
- package/dist/elaris/errors.js.map +1 -0
- package/dist/elaris/github-repo.d.ts +18 -0
- package/dist/elaris/github-repo.js +89 -0
- package/dist/elaris/github-repo.js.map +1 -0
- package/dist/elaris/index.d.ts +10 -0
- package/dist/elaris/index.js +11 -0
- package/dist/elaris/index.js.map +1 -0
- package/dist/elaris/redact.d.ts +89 -0
- package/dist/elaris/redact.js +423 -0
- package/dist/elaris/redact.js.map +1 -0
- package/dist/elaris/run-id.d.ts +5 -0
- package/dist/elaris/run-id.js +20 -0
- package/dist/elaris/run-id.js.map +1 -0
- package/dist/elaris/status.d.ts +45 -0
- package/dist/elaris/status.js +122 -0
- package/dist/elaris/status.js.map +1 -0
- package/dist/elaris/tool-schemas.d.ts +314 -0
- package/dist/elaris/tool-schemas.js +208 -0
- package/dist/elaris/tool-schemas.js.map +1 -0
- package/dist/elaris/tools.d.ts +73 -0
- package/dist/elaris/tools.js +1065 -0
- package/dist/elaris/tools.js.map +1 -0
- package/dist/elaris/types.d.ts +1541 -0
- package/dist/elaris/types.js +252 -0
- package/dist/elaris/types.js.map +1 -0
- package/dist/errors.d.ts +16 -0
- package/dist/errors.js +19 -0
- package/dist/errors.js.map +1 -1
- package/dist/handler.d.ts +4 -0
- package/dist/handler.js +10 -1
- package/dist/handler.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +13 -3
- package/dist/index.js.map +1 -1
- package/dist/local-clink-backend.d.ts +3 -0
- package/dist/local-clink-backend.js +4 -0
- package/dist/local-clink-backend.js.map +1 -1
- package/dist/package-info.d.ts +2 -0
- package/dist/package-info.js +18 -0
- package/dist/package-info.js.map +1 -0
- package/dist/parsers/codex-jsonl.js +48 -3
- package/dist/parsers/codex-jsonl.js.map +1 -1
- package/dist/parsers/extract.d.ts +10 -1
- package/dist/parsers/extract.js +11 -3
- package/dist/parsers/extract.js.map +1 -1
- package/dist/pipeline.d.ts +4 -0
- package/dist/pipeline.js +129 -8
- package/dist/pipeline.js.map +1 -1
- package/dist/run-dir.d.ts +24 -0
- package/dist/run-dir.js +201 -2
- package/dist/run-dir.js.map +1 -1
- package/dist/runner.d.ts +13 -0
- package/dist/runner.js +184 -15
- package/dist/runner.js.map +1 -1
- package/dist/safety.d.ts +4 -4
- package/dist/safety.js +4 -4
- package/dist/schema.d.ts +7 -7
- package/dist/server.d.ts +1 -0
- package/dist/server.js +359 -17
- package/dist/server.js.map +1 -1
- package/dist/session-log-stream.d.ts +18 -0
- package/dist/session-log-stream.js +319 -0
- package/dist/session-log-stream.js.map +1 -0
- package/package.json +46 -5
- package/conf/adapters/gemini.json +0 -44
- package/conf/adapters/hapi/gemini.json +0 -47
- package/dist/parsers/gemini-json.d.ts +0 -2
- package/dist/parsers/gemini-json.js +0 -72
- package/dist/parsers/gemini-json.js.map +0 -1
|
@@ -63,28 +63,165 @@
|
|
|
63
63
|
* workflow engine treats as a transient failure eligible for retry.
|
|
64
64
|
*/
|
|
65
65
|
import { stat as fsStat } from "node:fs/promises";
|
|
66
|
-
import {
|
|
66
|
+
import { ErrorCode, McpError } from "@modelcontextprotocol/sdk/types.js";
|
|
67
|
+
import { basename, isAbsolute, join, resolve, sep } from "node:path";
|
|
67
68
|
import { callClink } from "./clink-client.js";
|
|
68
|
-
import { getArtifactsDir, getChildMaxConcurrent, getMaxParallel, getMaxPromptBytes, getStateDir, resolveWorkspaceRoot, } from "./config.js";
|
|
69
|
+
import { getArtifactsDir, getChildMaxConcurrent, getWorkflowsContextDefault, getWorkflowsContextDefaultCurator, getMaxParallel, getMaxPromptBytes, getStateDir, resolveWorkspaceRoot, } from "./config.js";
|
|
70
|
+
import { validatePlanReviewCallBindings, validatePlanReviewPreparedInputPolicy, } from "./plan-review/runtime-policy.js";
|
|
71
|
+
import { CouncilPlanReviewErrorSchema, } from "./plan-review/publication.js";
|
|
72
|
+
import { PlanReviewStageError } from "./plan-review/stage-error.js";
|
|
69
73
|
import { CallExecutionError, InvalidCallError, WorkflowCancellationError, WorkflowError, } from "./errors.js";
|
|
70
74
|
import { analyzeOutputFidelity, enforceOutputFidelity } from "./fidelity.js";
|
|
71
75
|
import { validateDynamicPaths } from "./path-validation.js";
|
|
72
76
|
import { estimatePromptBudget, enforcePromptBudget, createPathContributor } from "./prompt-budget.js";
|
|
73
77
|
import { DispatchQueue } from "./queue.js";
|
|
74
78
|
import { renderCallPrompt } from "./template.js";
|
|
75
|
-
import { ArtifactStore } from "./artifacts.js";
|
|
79
|
+
import { ArtifactStore, OutputSizeError, SchemaValidationError } from "./artifacts.js";
|
|
80
|
+
import { projectPlanReviewSemanticBody, renderPlanSnapshotBlock, } from "./plan-review/expander.js";
|
|
81
|
+
import { filterExcludedPlanPaths, foldPlanReviewExcludeGlobs, } from "./plan-review/context-policy.js";
|
|
76
82
|
import { getReducer } from "./reducers/index.js";
|
|
83
|
+
import { getPostProcessor } from "./post-processors/index.js";
|
|
84
|
+
import { addPathWithBasename, buildAllowlistFromDiff } from "./citation-check.js";
|
|
77
85
|
import { logger } from "./logger.js";
|
|
86
|
+
import { resolveWorkflowContextMode, WorkflowContextModeResolverError, } from "./context-mode-resolver.js";
|
|
87
|
+
import { WorkflowDelegateValidationError } from "./delegate-validate.js";
|
|
88
|
+
import { contextSeedSourceValidationError, resolveSeedSource, synthesizeSeedTerms, } from "./seed-terms.js";
|
|
89
|
+
/**
|
|
90
|
+
* Declarations for variables the ENGINE derives at runtime (not declared in the
|
|
91
|
+
* workflow YAML or accepted as caller input). These are merged into the
|
|
92
|
+
* declaration map passed to the template renderer so that templates may
|
|
93
|
+
* reference them ({{context_available}}) without `template.ts` rejecting them as
|
|
94
|
+
* "Unknown variable", while genuinely-unknown placeholders are still rejected.
|
|
95
|
+
*
|
|
96
|
+
* The VALUES are produced by the engine (e.g. {@link deriveContextAvailable},
|
|
97
|
+
* which always overwrites any caller-supplied `context_available` before stages
|
|
98
|
+
* run), so listing them here does NOT widen the public caller-input surface.
|
|
99
|
+
*/
|
|
100
|
+
const ENGINE_DERIVED_VARIABLE_DECLS = {
|
|
101
|
+
context_available: {
|
|
102
|
+
type: "string",
|
|
103
|
+
required: false,
|
|
104
|
+
description: "Engine-derived: whether repository context is attached to the run",
|
|
105
|
+
},
|
|
106
|
+
context_quality: {
|
|
107
|
+
type: "string",
|
|
108
|
+
required: false,
|
|
109
|
+
description: "Engine-derived: effective context coverage quality",
|
|
110
|
+
},
|
|
111
|
+
};
|
|
78
112
|
/** Default parent-side timeout for a single tools/call RPC (10 minutes). */
|
|
79
113
|
const DEFAULT_PARENT_CALL_TIMEOUT_MS = 600_000;
|
|
80
114
|
/** Buffer added to per-call timeout to account for child-side teardown latency. */
|
|
81
115
|
const CALL_TIMEOUT_BUFFER_MS = 5_000;
|
|
116
|
+
const preflightFallbackAggregate = {
|
|
117
|
+
curated_count: 0,
|
|
118
|
+
fallback_count: 0,
|
|
119
|
+
};
|
|
120
|
+
function orderedUnique(values) {
|
|
121
|
+
const seen = new Set();
|
|
122
|
+
const result = [];
|
|
123
|
+
for (const value of values) {
|
|
124
|
+
if (seen.has(value)) {
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
seen.add(value);
|
|
128
|
+
result.push(value);
|
|
129
|
+
}
|
|
130
|
+
return result;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Maps a runner's {@link PreflightContextResult} to the persisted {@link WorkflowContext}.
|
|
134
|
+
* Single source of truth for this mapping: the transient `receipt` field is dropped, and
|
|
135
|
+
* optional `delegate` metadata is spread only when present.
|
|
136
|
+
*/
|
|
137
|
+
export function mapPreflightResultToWorkflowContext(result, contextMode, lightCaller) {
|
|
138
|
+
// Mirror the engine's bundle-attachment decision exactly: bundle artifacts are
|
|
139
|
+
// appended to context_bundle_files IFF files_selected > 0 (see ~line 1090). On a
|
|
140
|
+
// zero-selection run we proceed WITHOUT a bundle, so the reported copied_paths
|
|
141
|
+
// must be empty too — otherwise the receipt would imply a bundle was attached.
|
|
142
|
+
const copied_paths = result.metrics.files_selected > 0 ? result.copied_paths : [];
|
|
143
|
+
return {
|
|
144
|
+
context_builder: {
|
|
145
|
+
source_run_id: result.source_run_id,
|
|
146
|
+
source_artifact_dir: result.source_artifact_dir,
|
|
147
|
+
cache: result.cache,
|
|
148
|
+
schema_version: result.schema_version,
|
|
149
|
+
status: result.status,
|
|
150
|
+
...(contextMode !== undefined ? { context_mode: contextMode } : {}),
|
|
151
|
+
copied_paths,
|
|
152
|
+
...(result.coverage !== undefined ? { coverage: result.coverage } : {}),
|
|
153
|
+
metrics: result.metrics,
|
|
154
|
+
warning_count: result.warning_count,
|
|
155
|
+
omission_count: result.omission_count,
|
|
156
|
+
...(result.delegate !== undefined ? { delegate: result.delegate } : {}),
|
|
157
|
+
},
|
|
158
|
+
...(lightCaller !== undefined ? { light_caller: lightCaller } : {}),
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
export function classifyPreflightPartial(metrics) {
|
|
162
|
+
if (metrics.truncated ||
|
|
163
|
+
metrics.scan_cap_hit ||
|
|
164
|
+
metrics.search_cap_hit ||
|
|
165
|
+
metrics.discovery_byte_cap_hit ||
|
|
166
|
+
metrics.discovery_candidate_cap_hit ||
|
|
167
|
+
metrics.candidate_cap_hit) {
|
|
168
|
+
return { severity: "degraded", partial_reason: "cap_or_truncation" };
|
|
169
|
+
}
|
|
170
|
+
return { severity: "benign", partial_reason: "omissions_only" };
|
|
171
|
+
}
|
|
172
|
+
function recordPreflightFallbackAggregate(input) {
|
|
173
|
+
if (input.delegateEnabled) {
|
|
174
|
+
if (input.fallbackUsed) {
|
|
175
|
+
preflightFallbackAggregate.fallback_count += 1;
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
preflightFallbackAggregate.curated_count += 1;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
const total = preflightFallbackAggregate.curated_count +
|
|
182
|
+
preflightFallbackAggregate.fallback_count;
|
|
183
|
+
return {
|
|
184
|
+
curated_count: preflightFallbackAggregate.curated_count,
|
|
185
|
+
fallback_count: preflightFallbackAggregate.fallback_count,
|
|
186
|
+
total_delegate_count: total,
|
|
187
|
+
fallback_ratio: total === 0
|
|
188
|
+
? 0
|
|
189
|
+
: preflightFallbackAggregate.fallback_count / total,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
function firstReceiptWarningCode(result) {
|
|
193
|
+
const warnings = result.receipt["warnings"];
|
|
194
|
+
if (!Array.isArray(warnings)) {
|
|
195
|
+
return null;
|
|
196
|
+
}
|
|
197
|
+
for (const warning of warnings) {
|
|
198
|
+
if (warning !== null && typeof warning === "object") {
|
|
199
|
+
const code = warning["code"];
|
|
200
|
+
if (typeof code === "string" && code.trim() !== "") {
|
|
201
|
+
return code;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
return null;
|
|
206
|
+
}
|
|
207
|
+
function receiptDelegateFailureReason(result) {
|
|
208
|
+
const delegate = result.receipt["delegate"];
|
|
209
|
+
if (delegate === null || typeof delegate !== "object") {
|
|
210
|
+
return null;
|
|
211
|
+
}
|
|
212
|
+
const failureReason = delegate["failure_reason"];
|
|
213
|
+
return typeof failureReason === "string" && failureReason.trim() !== ""
|
|
214
|
+
? failureReason
|
|
215
|
+
: null;
|
|
216
|
+
}
|
|
82
217
|
export class WorkflowEngine {
|
|
83
218
|
definition;
|
|
84
219
|
session;
|
|
85
220
|
variables;
|
|
221
|
+
effectiveVariables;
|
|
86
222
|
validatorRegistry;
|
|
87
223
|
runId;
|
|
224
|
+
sessionLogGeneration;
|
|
88
225
|
runDir;
|
|
89
226
|
signal;
|
|
90
227
|
queueDepth;
|
|
@@ -95,14 +232,29 @@ export class WorkflowEngine {
|
|
|
95
232
|
resumeCompletedCalls;
|
|
96
233
|
resumePreparedInputs;
|
|
97
234
|
resumeFailedCalls;
|
|
235
|
+
resumeNextRetryCounts;
|
|
98
236
|
contextProducingCalls = new Set();
|
|
237
|
+
planReviewDeliveredAliases = new Map();
|
|
99
238
|
artifactStore;
|
|
239
|
+
contextBuilderRunner;
|
|
240
|
+
contextDefault;
|
|
241
|
+
defaultCurator;
|
|
242
|
+
commitPreflightContext;
|
|
243
|
+
restoredWorkflowContext;
|
|
244
|
+
requireContextPreflight;
|
|
245
|
+
initialContextPolicy;
|
|
246
|
+
planReviewContextPolicy;
|
|
247
|
+
planReviewCallBindings;
|
|
248
|
+
commitPlanReviewContextProvenance;
|
|
249
|
+
terminalResultPolicy;
|
|
100
250
|
constructor(options) {
|
|
101
251
|
this.definition = options.definition;
|
|
102
252
|
this.session = options.session;
|
|
103
253
|
this.variables = options.variables;
|
|
254
|
+
this.effectiveVariables = { ...options.variables };
|
|
104
255
|
this.validatorRegistry = options.validatorRegistry;
|
|
105
256
|
this.runId = options.runId;
|
|
257
|
+
this.sessionLogGeneration = options.sessionLogGeneration;
|
|
106
258
|
this.runDir = options.runDir;
|
|
107
259
|
this.signal = options.signal;
|
|
108
260
|
this.queueDepth = Math.min(options.maxParallel ?? getMaxParallel(), options.childMaxConcurrent ?? getChildMaxConcurrent());
|
|
@@ -113,7 +265,25 @@ export class WorkflowEngine {
|
|
|
113
265
|
this.resumeCompletedCalls = options.resume?.completedCalls ?? new Map();
|
|
114
266
|
this.resumePreparedInputs = options.resume?.preparedInputs ?? new Map();
|
|
115
267
|
this.resumeFailedCalls = options.resume?.failedCalls ?? new Map();
|
|
268
|
+
this.resumeNextRetryCounts = options.resume?.nextRetryCounts ?? new Map();
|
|
116
269
|
this.artifactStore = new ArtifactStore(options.runDir, options.validatorRegistry);
|
|
270
|
+
this.contextBuilderRunner = options.contextBuilderRunner;
|
|
271
|
+
this.contextDefault = options.contextDefault ?? getWorkflowsContextDefault();
|
|
272
|
+
this.defaultCurator = options.defaultCurator ?? getWorkflowsContextDefaultCurator();
|
|
273
|
+
this.commitPreflightContext = options.commitPreflightContext;
|
|
274
|
+
this.restoredWorkflowContext = options.restoredWorkflowContext;
|
|
275
|
+
this.requireContextPreflight = options.requireContextPreflight ?? false;
|
|
276
|
+
this.initialContextPolicy = options.initialContextPolicy;
|
|
277
|
+
this.planReviewContextPolicy = options.planReviewContextPolicy;
|
|
278
|
+
this.planReviewCallBindings = options.planReviewCallBindings === undefined
|
|
279
|
+
? new Map()
|
|
280
|
+
: validatePlanReviewCallBindings(options.definition, options.planReviewCallBindings);
|
|
281
|
+
validatePlanReviewPreparedInputPolicy(this.resumePreparedInputs, this.planReviewCallBindings, {
|
|
282
|
+
snapshotText: options.planReviewSnapshotText,
|
|
283
|
+
contextPolicy: options.planReviewContextPolicy,
|
|
284
|
+
});
|
|
285
|
+
this.commitPlanReviewContextProvenance = options.commitPlanReviewContextProvenance;
|
|
286
|
+
this.terminalResultPolicy = options.terminalResultPolicy ?? { kind: "final_call" };
|
|
117
287
|
this.validateFinalStage();
|
|
118
288
|
this.validateAdvertisedCliNames();
|
|
119
289
|
this.contextProducingCalls = collectContextProducingCalls(this.definition);
|
|
@@ -137,7 +307,7 @@ export class WorkflowEngine {
|
|
|
137
307
|
state: "running",
|
|
138
308
|
message: `Workflow ${this.definition.name} is running`,
|
|
139
309
|
});
|
|
140
|
-
await this.
|
|
310
|
+
await this.prepareContextPreflight(deadlineController.signal);
|
|
141
311
|
for (let stageIndex = 0; stageIndex < this.definition.stages.length; stageIndex += 1) {
|
|
142
312
|
this.throwIfAborted(deadlineController.signal);
|
|
143
313
|
const stage = this.definition.stages[stageIndex];
|
|
@@ -172,11 +342,7 @@ export class WorkflowEngine {
|
|
|
172
342
|
throw new WorkflowError(`Stage ${stage.id} failed and on_failure=abort`);
|
|
173
343
|
}
|
|
174
344
|
}
|
|
175
|
-
const
|
|
176
|
-
const finalCall = finalStage?.calls[0];
|
|
177
|
-
if (finalCall == null || !finalCall.ok || finalCall.outputText == null) {
|
|
178
|
-
throw new WorkflowError("Final stage did not produce a successful terminal call result");
|
|
179
|
-
}
|
|
345
|
+
const outputText = await this.resolveTerminalResult(stages);
|
|
180
346
|
const completedAt = new Date().toISOString();
|
|
181
347
|
await this.emitRunStateChange({
|
|
182
348
|
runId: this.runId,
|
|
@@ -193,7 +359,7 @@ export class WorkflowEngine {
|
|
|
193
359
|
message: `Workflow ${this.definition.name} succeeded`,
|
|
194
360
|
});
|
|
195
361
|
return {
|
|
196
|
-
outputText
|
|
362
|
+
outputText,
|
|
197
363
|
stages,
|
|
198
364
|
artifactsDir: this.artifactStore.artifactsDir,
|
|
199
365
|
};
|
|
@@ -219,6 +385,18 @@ export class WorkflowEngine {
|
|
|
219
385
|
});
|
|
220
386
|
}
|
|
221
387
|
else {
|
|
388
|
+
const planReviewError = error instanceof PlanReviewStageError
|
|
389
|
+
? CouncilPlanReviewErrorSchema.parse({
|
|
390
|
+
schema_version: "council-plan-review-error-v1",
|
|
391
|
+
run_id: this.runId,
|
|
392
|
+
execution_status: "failed",
|
|
393
|
+
stage: error.stage,
|
|
394
|
+
error: {
|
|
395
|
+
reason: error.reason,
|
|
396
|
+
detail: message.slice(0, 2_048),
|
|
397
|
+
},
|
|
398
|
+
})
|
|
399
|
+
: undefined;
|
|
222
400
|
await this.emitRunStateChange({
|
|
223
401
|
runId: this.runId,
|
|
224
402
|
workflowName: this.definition.name,
|
|
@@ -227,6 +405,7 @@ export class WorkflowEngine {
|
|
|
227
405
|
completedAt,
|
|
228
406
|
error: message,
|
|
229
407
|
failureClass: classifyFailureClass(error),
|
|
408
|
+
planReviewError,
|
|
230
409
|
});
|
|
231
410
|
await this.emitProgress({
|
|
232
411
|
runId: this.runId,
|
|
@@ -242,6 +421,42 @@ export class WorkflowEngine {
|
|
|
242
421
|
deadlineController.dispose();
|
|
243
422
|
}
|
|
244
423
|
}
|
|
424
|
+
/**
|
|
425
|
+
* Complete the workflow context gate without dispatching a provider call.
|
|
426
|
+
* Resume uses this before backend construction when workflow_context is absent.
|
|
427
|
+
*/
|
|
428
|
+
async prepareContextPreflight(signal) {
|
|
429
|
+
// Protect engine-derived variables from caller overrides before every
|
|
430
|
+
// branch (runner absent, off, zero-selection, or legacy result).
|
|
431
|
+
if (this.restoredWorkflowContext === undefined) {
|
|
432
|
+
this.setEffectiveVariable("context_quality", "not_evaluated");
|
|
433
|
+
}
|
|
434
|
+
try {
|
|
435
|
+
await this.runContextBuilderPreflight(signal);
|
|
436
|
+
}
|
|
437
|
+
catch (err) {
|
|
438
|
+
if (this.planReviewContextPolicy !== undefined &&
|
|
439
|
+
!(err instanceof PlanReviewStageError) &&
|
|
440
|
+
!(err instanceof WorkflowCancellationError)) {
|
|
441
|
+
throw new PlanReviewStageError("context_preflight", err instanceof Error ? err.message : String(err), "internal_error");
|
|
442
|
+
}
|
|
443
|
+
throw err;
|
|
444
|
+
}
|
|
445
|
+
this.enforcePlanReviewContextExclusions();
|
|
446
|
+
this.deriveContextAvailable();
|
|
447
|
+
await this.validateInputPathsExist();
|
|
448
|
+
}
|
|
449
|
+
async resolveTerminalResult(stages) {
|
|
450
|
+
if (this.terminalResultPolicy.kind === "plan_review_artifacts") {
|
|
451
|
+
return await this.terminalResultPolicy.project();
|
|
452
|
+
}
|
|
453
|
+
const finalStage = stages[stages.length - 1];
|
|
454
|
+
const finalCall = finalStage?.calls[0];
|
|
455
|
+
if (finalCall == null || !finalCall.ok || finalCall.outputText == null) {
|
|
456
|
+
throw new WorkflowError("Final stage did not produce a successful terminal call result");
|
|
457
|
+
}
|
|
458
|
+
return finalCall.outputText;
|
|
459
|
+
}
|
|
245
460
|
async executeParallelStage(stage, stageIndex, signal) {
|
|
246
461
|
const queue = new DispatchQueue(this.queueDepth);
|
|
247
462
|
const settled = await Promise.allSettled(stage.calls.map((call, callIndex) => queue.run(async () => this.executeSingleCall(stage, call, stageIndex, callIndex, signal))));
|
|
@@ -282,6 +497,15 @@ export class WorkflowEngine {
|
|
|
282
497
|
if (resumedCall != null) {
|
|
283
498
|
return resumedCall;
|
|
284
499
|
}
|
|
500
|
+
const resumedFailure = this.resumeFailedCalls.get(callKey);
|
|
501
|
+
if (resumedFailure?.terminal === true) {
|
|
502
|
+
return {
|
|
503
|
+
stageId: resumedFailure.stageId,
|
|
504
|
+
callId: resumedFailure.callId,
|
|
505
|
+
ok: false,
|
|
506
|
+
error: resumedFailure.error,
|
|
507
|
+
};
|
|
508
|
+
}
|
|
285
509
|
// Handle built-in reducers — no LLM call, no retry loop
|
|
286
510
|
if (call.reducer != null) {
|
|
287
511
|
const reducer = getReducer(call.reducer);
|
|
@@ -407,21 +631,29 @@ export class WorkflowEngine {
|
|
|
407
631
|
const retryMode = stage.retry.mode;
|
|
408
632
|
const backoffSeconds = stage.retry.backoff_seconds;
|
|
409
633
|
const maxBackoffSeconds = stage.retry.max_backoff_seconds;
|
|
634
|
+
// Resume never replenishes an already-consumed provider attempt.
|
|
635
|
+
const initialRetryCount = this.resumeNextRetryCounts.get(callKey) ?? 0;
|
|
410
636
|
// Emit call prepared ONCE (input is stable across retries)
|
|
411
637
|
await this.emitCallPrepared({
|
|
412
638
|
runId: this.runId,
|
|
413
639
|
workflowName: this.definition.name,
|
|
414
640
|
stageId: stage.id,
|
|
415
641
|
callId: call.id,
|
|
416
|
-
attempt: 1,
|
|
417
|
-
retryCount:
|
|
642
|
+
attempt: initialRetryCount + 1,
|
|
643
|
+
retryCount: initialRetryCount,
|
|
418
644
|
contextProducing,
|
|
419
645
|
input: structuredClone(input),
|
|
420
646
|
});
|
|
421
|
-
let lastError =
|
|
647
|
+
let lastError = initialRetryCount > maxRetries
|
|
648
|
+
? new Error("Retry budget exhausted before resume")
|
|
649
|
+
: undefined;
|
|
422
650
|
let lastFailureClass = "permanent";
|
|
423
|
-
let lastRetryCount =
|
|
424
|
-
|
|
651
|
+
let lastRetryCount = initialRetryCount;
|
|
652
|
+
// F4: set in the catch below for the NEXT attempt only; always derived from
|
|
653
|
+
// the original `input.prompt`, so it never stacks. In-memory only — a
|
|
654
|
+
// resumed retry has no lastError and simply gets no suffix.
|
|
655
|
+
let correctiveSuffix;
|
|
656
|
+
for (let retryCount = initialRetryCount; retryCount <= maxRetries; retryCount += 1) {
|
|
425
657
|
const attempt = retryCount + 1;
|
|
426
658
|
lastRetryCount = retryCount;
|
|
427
659
|
this.throwIfAborted(signal);
|
|
@@ -450,7 +682,13 @@ export class WorkflowEngine {
|
|
|
450
682
|
: `Call ${callKey} is retrying (attempt ${String(attempt)})`,
|
|
451
683
|
});
|
|
452
684
|
try {
|
|
453
|
-
const
|
|
685
|
+
const attemptInput = correctiveSuffix == null
|
|
686
|
+
? input
|
|
687
|
+
: {
|
|
688
|
+
...input,
|
|
689
|
+
prompt: `${typeof input["prompt"] === "string" ? input["prompt"] : ""}${correctiveSuffix}`,
|
|
690
|
+
};
|
|
691
|
+
const result = await this.invokeCall(stage, call, attemptInput, signal, attempt);
|
|
454
692
|
const fidelity = analyzeOutputFidelity(result.outputText, result.debugMetadata);
|
|
455
693
|
enforceOutputFidelity(fidelity, {
|
|
456
694
|
stageId: stage.id,
|
|
@@ -534,6 +772,10 @@ export class WorkflowEngine {
|
|
|
534
772
|
// Determine if this error is retryable
|
|
535
773
|
const canRetry = retryCount < maxRetries && isRetryable(error, failureClass, retryMode);
|
|
536
774
|
if (canRetry) {
|
|
775
|
+
// F4: corrective suffix rides only on the retry that was already
|
|
776
|
+
// decided above — it never changes retry eligibility. Unconditional
|
|
777
|
+
// reassignment clears a stale suffix after a non-JSON failure.
|
|
778
|
+
correctiveSuffix = this.buildCorrectiveRetrySuffix(stage, call, input, error);
|
|
537
779
|
// Compute backoff with jitter
|
|
538
780
|
const delayMs = computeRetryDelay(retryCount, backoffSeconds, maxBackoffSeconds);
|
|
539
781
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -608,11 +850,85 @@ export class WorkflowEngine {
|
|
|
608
850
|
error: message,
|
|
609
851
|
};
|
|
610
852
|
}
|
|
611
|
-
|
|
853
|
+
/**
|
|
854
|
+
* F4: bounded corrective suffix for the next already-scheduled retry after
|
|
855
|
+
* a JSON/schema-shaped failure. Returns undefined (no suffix) unless ALL of:
|
|
856
|
+
* - the call declares a JSON contract (mirrors the expect_json derivation
|
|
857
|
+
* in buildCallInput);
|
|
858
|
+
* - the run is NOT typed plan review (identity: the plan-review-receipts
|
|
859
|
+
* workflow name on the
|
|
860
|
+
* normalized definition, plus the plan-review runtime signals as
|
|
861
|
+
* defense-in-depth — plan-review dispatch bytes are pinned);
|
|
862
|
+
* - the failure is JSON/schema-shaped (SchemaValidationError,
|
|
863
|
+
* OutputSizeError as the second correctable class, or a child
|
|
864
|
+
* invalid_json_output rejection);
|
|
865
|
+
* - appending stays inside the same prompt-budget ceiling the dispatch-time
|
|
866
|
+
* gate enforces (best-effort re-estimate; skip with a warn on overflow or
|
|
867
|
+
* estimator failure — never a new failure mode).
|
|
868
|
+
* The suffix is always derived from the ORIGINAL prompt, so it can never
|
|
869
|
+
* stack across attempts, and it is never persisted to state.
|
|
870
|
+
*/
|
|
871
|
+
buildCorrectiveRetrySuffix(stage, call, input, error) {
|
|
872
|
+
if (call.response_contract !== "json" && call.validator_schema_id == null) {
|
|
873
|
+
return undefined;
|
|
874
|
+
}
|
|
875
|
+
if (this.definition.name === "plan-review-receipts" ||
|
|
876
|
+
this.planReviewContextPolicy !== undefined ||
|
|
877
|
+
call.plan_review_snapshot_text !== undefined) {
|
|
878
|
+
return undefined;
|
|
879
|
+
}
|
|
880
|
+
const detail = correctiveFailureDetail(error);
|
|
881
|
+
if (detail == null) {
|
|
882
|
+
return undefined;
|
|
883
|
+
}
|
|
884
|
+
const suffix = buildCorrectiveSuffixText(detail, error instanceof OutputSizeError ? CORRECTIVE_SIZE_SUFFIX_HEADER : CORRECTIVE_SUFFIX_HEADER);
|
|
885
|
+
try {
|
|
886
|
+
const basePrompt = typeof input["prompt"] === "string" ? input["prompt"] : "";
|
|
887
|
+
const files = Array.isArray(input["absolute_file_paths"])
|
|
888
|
+
? input["absolute_file_paths"].filter((path) => typeof path === "string")
|
|
889
|
+
: [];
|
|
890
|
+
const images = Array.isArray(input["images"])
|
|
891
|
+
? input["images"].filter((path) => typeof path === "string")
|
|
892
|
+
: [];
|
|
893
|
+
const estimate = estimatePromptBudget({
|
|
894
|
+
promptText: `${basePrompt}${suffix}`,
|
|
895
|
+
contextText: typeof input["context"] === "string" ? input["context"] : undefined,
|
|
896
|
+
fileContributors: files.map((path) => createPathContributor(`absolute_file_paths:${path}`, path, "file")),
|
|
897
|
+
imageContributors: images.map((path) => createPathContributor(`image:${path}`, path, "image")),
|
|
898
|
+
maxBytes: this.maxPromptBytes,
|
|
899
|
+
reserveBytes: this.promptReserveBytes,
|
|
900
|
+
});
|
|
901
|
+
if (estimate.exceeded) {
|
|
902
|
+
logger.warn({ run_id: this.runId, stage_id: stage.id, call_id: call.id }, "corrective retry suffix skipped: appending would exceed the prompt budget");
|
|
903
|
+
return undefined;
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
catch (budgetError) {
|
|
907
|
+
logger.warn({
|
|
908
|
+
run_id: this.runId,
|
|
909
|
+
stage_id: stage.id,
|
|
910
|
+
call_id: call.id,
|
|
911
|
+
err: budgetError instanceof Error ? budgetError.message : String(budgetError),
|
|
912
|
+
}, "corrective retry suffix skipped: prompt budget estimate failed");
|
|
913
|
+
return undefined;
|
|
914
|
+
}
|
|
915
|
+
return suffix;
|
|
916
|
+
}
|
|
917
|
+
async invokeCall(stage, call, input, signal, attempt) {
|
|
612
918
|
const timeoutMs = call.timeout_seconds != null
|
|
613
919
|
? Math.max((call.timeout_seconds * 1000) + CALL_TIMEOUT_BUFFER_MS, DEFAULT_PARENT_CALL_TIMEOUT_MS)
|
|
614
920
|
: undefined;
|
|
615
|
-
|
|
921
|
+
const sessionLogOwner = this.sessionLogGeneration !== undefined && this.session.supportsSessionLogOwner === true
|
|
922
|
+
? {
|
|
923
|
+
family: "workflow",
|
|
924
|
+
runId: this.runId,
|
|
925
|
+
runGeneration: this.sessionLogGeneration,
|
|
926
|
+
stageId: stage.id,
|
|
927
|
+
callId: call.id,
|
|
928
|
+
attempt,
|
|
929
|
+
}
|
|
930
|
+
: undefined;
|
|
931
|
+
return await callClink(this.session, input, signal, { stageId: stage.id, callId: call.id }, timeoutMs, sessionLogOwner);
|
|
616
932
|
}
|
|
617
933
|
maybeWriteArtifact(stage, call, stageIndex, callIndex, outputText) {
|
|
618
934
|
const requiresValidation = call.validator_schema_id != null;
|
|
@@ -624,19 +940,78 @@ export class WorkflowEngine {
|
|
|
624
940
|
throw new WorkflowError(`Validated call ${stage.id}.${call.id} must declare validator_schema_id`);
|
|
625
941
|
}
|
|
626
942
|
const validated = this.artifactStore.validateJsonOutput(stage.id, call.id, validatorSchemaId, outputText);
|
|
943
|
+
if (call.plan_review_anonymous_aliases != null) {
|
|
944
|
+
const parsed = validated.parsed;
|
|
945
|
+
const allowed = this.planReviewDeliveredAliases.get(`${stage.id}.${call.id}`) ?? new Set();
|
|
946
|
+
const assessmentIds = (parsed.peer_assessments ?? []).map((entry) => entry.candidate_id);
|
|
947
|
+
const rankingIds = (parsed.ranking ?? []).map((entry) => entry.candidate_id);
|
|
948
|
+
const exactAliasSet = (ids) => {
|
|
949
|
+
if (!ids.every((candidate) => typeof candidate === "string" && allowed.has(candidate)))
|
|
950
|
+
return false;
|
|
951
|
+
const unique = new Set(ids);
|
|
952
|
+
return ids.length === allowed.size && unique.size === allowed.size &&
|
|
953
|
+
[...allowed].every((candidate) => unique.has(candidate));
|
|
954
|
+
};
|
|
955
|
+
if (!exactAliasSet(assessmentIds) || !exactAliasSet(rankingIds)) {
|
|
956
|
+
throw new WorkflowError(`Validation failed for ${stage.id}.${call.id}: peer_assessments and ranking must each contain every bound peer alias exactly once`);
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
// Optional post-processing: a call may declare `post_processor: <name>` to
|
|
960
|
+
// transform the validated value before it is persisted. Unknown names are a
|
|
961
|
+
// load-time validation error (loader.ts:validatePostProcessors); we fail
|
|
962
|
+
// fast here too as defense-in-depth, since silently writing the
|
|
963
|
+
// un-transformed output through could ship un-scrubbed data (e.g. an
|
|
964
|
+
// un-run citation_check). The transform runs on the parsed data; the
|
|
965
|
+
// artifact is re-serialized so the persisted bytes reflect it.
|
|
966
|
+
let persisted = validated;
|
|
967
|
+
const postProcessorName = call.post_processor;
|
|
968
|
+
if (postProcessorName != null) {
|
|
969
|
+
const postProcessor = getPostProcessor(postProcessorName);
|
|
970
|
+
if (postProcessor == null) {
|
|
971
|
+
throw new WorkflowError(`Call ${stage.id}.${call.id} declares post_processor ` +
|
|
972
|
+
`"${postProcessorName}" which is not registered`);
|
|
973
|
+
}
|
|
974
|
+
const allowlist = this.buildPostProcessorAllowlist();
|
|
975
|
+
const processed = postProcessor(validated.parsed, { allowlist, schemaId: validatorSchemaId });
|
|
976
|
+
const serialized = `${JSON.stringify(processed, null, 2)}\n`;
|
|
977
|
+
persisted = {
|
|
978
|
+
parsed: processed,
|
|
979
|
+
serialized,
|
|
980
|
+
sizeBytes: Buffer.byteLength(serialized, "utf-8"),
|
|
981
|
+
};
|
|
982
|
+
}
|
|
627
983
|
return this.artifactStore.writeCallArtifact({
|
|
628
984
|
stage,
|
|
629
985
|
call,
|
|
630
986
|
stageIndex,
|
|
631
987
|
callIndex,
|
|
632
|
-
validated,
|
|
988
|
+
validated: persisted,
|
|
633
989
|
});
|
|
634
990
|
}
|
|
635
|
-
resolveDynamicPaths(
|
|
636
|
-
if (
|
|
991
|
+
resolveDynamicPaths(variableNames, context) {
|
|
992
|
+
if (variableNames == null) {
|
|
637
993
|
return [];
|
|
638
994
|
}
|
|
639
|
-
|
|
995
|
+
if (typeof variableNames === "string") {
|
|
996
|
+
const values = this.resolveDynamicPathVariable(variableNames, context);
|
|
997
|
+
validateDynamicPaths(values, this.allowedRoots, context);
|
|
998
|
+
return values;
|
|
999
|
+
}
|
|
1000
|
+
const values = [];
|
|
1001
|
+
const seen = new Set();
|
|
1002
|
+
for (const variableName of variableNames) {
|
|
1003
|
+
for (const value of this.resolveDynamicPathVariable(variableName, context)) {
|
|
1004
|
+
if (!seen.has(value)) {
|
|
1005
|
+
seen.add(value);
|
|
1006
|
+
values.push(value);
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
validateDynamicPaths(values, this.allowedRoots, context);
|
|
1011
|
+
return values;
|
|
1012
|
+
}
|
|
1013
|
+
resolveDynamicPathVariable(variableName, context) {
|
|
1014
|
+
const rawValue = this.effectiveVariables[variableName];
|
|
640
1015
|
if (!Array.isArray(rawValue)) {
|
|
641
1016
|
throw new WorkflowError(`Variable "${variableName}" for ${context} must be a string[]`);
|
|
642
1017
|
}
|
|
@@ -647,16 +1022,390 @@ export class WorkflowEngine {
|
|
|
647
1022
|
}
|
|
648
1023
|
return isAbsolute(value) ? value : resolve(workspaceRoot, value);
|
|
649
1024
|
});
|
|
650
|
-
validateDynamicPaths(values, this.allowedRoots, context);
|
|
651
1025
|
return values;
|
|
652
1026
|
}
|
|
1027
|
+
setEffectiveVariable(name, value) {
|
|
1028
|
+
this.effectiveVariables[name] = value;
|
|
1029
|
+
}
|
|
1030
|
+
/**
|
|
1031
|
+
* Derive the engine-computed `context_available` template variable after
|
|
1032
|
+
* context-builder preflight has had a chance to append to
|
|
1033
|
+
* `context_bundle_files`. True when the union of the effective
|
|
1034
|
+
* `context_files` and `context_bundle_files` path lists is non-empty.
|
|
1035
|
+
* Stored as the STRING "true"/"false" since the template engine stringifies
|
|
1036
|
+
* variable values. Engine-derived; setting it is expected.
|
|
1037
|
+
*/
|
|
1038
|
+
deriveContextAvailable() {
|
|
1039
|
+
const contextFiles = this.readStringArrayVariable("context_files");
|
|
1040
|
+
const bundleFiles = this.readStringArrayVariable("context_bundle_files");
|
|
1041
|
+
const available = contextFiles.length > 0 || bundleFiles.length > 0;
|
|
1042
|
+
this.setEffectiveVariable("context_available", available ? "true" : "false");
|
|
1043
|
+
}
|
|
1044
|
+
/**
|
|
1045
|
+
* Build the citation allowlist for a post-processor run: the union of files
|
|
1046
|
+
* parsed from the `diff_context` variable, the resolved `context_files`, and
|
|
1047
|
+
* the resolved `context_bundle_files`. Both full paths and basenames are
|
|
1048
|
+
* stored so citation matching is lenient.
|
|
1049
|
+
*/
|
|
1050
|
+
buildPostProcessorAllowlist() {
|
|
1051
|
+
const diffRaw = this.effectiveVariables["diff_context"];
|
|
1052
|
+
const allowlist = buildAllowlistFromDiff(typeof diffRaw === "string" ? diffRaw : "");
|
|
1053
|
+
for (const name of ["context_files", "context_bundle_files"]) {
|
|
1054
|
+
for (const path of this.resolvePathListLenient(name)) {
|
|
1055
|
+
addPathWithBasename(allowlist, path);
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
return allowlist;
|
|
1059
|
+
}
|
|
1060
|
+
/**
|
|
1061
|
+
* Resolve a path-list variable to absolute paths without enforcing allowed
|
|
1062
|
+
* roots (the post-processor allowlist is advisory, not a security boundary).
|
|
1063
|
+
* Both the raw and resolved forms are returned so basename matching works
|
|
1064
|
+
* regardless of how a citation spells the file.
|
|
1065
|
+
*/
|
|
1066
|
+
resolvePathListLenient(name) {
|
|
1067
|
+
const value = this.effectiveVariables[name];
|
|
1068
|
+
if (!Array.isArray(value)) {
|
|
1069
|
+
return [];
|
|
1070
|
+
}
|
|
1071
|
+
const workspaceRoot = resolveWorkspaceRoot();
|
|
1072
|
+
const out = [];
|
|
1073
|
+
for (const entry of value) {
|
|
1074
|
+
if (typeof entry !== "string") {
|
|
1075
|
+
continue;
|
|
1076
|
+
}
|
|
1077
|
+
out.push(entry);
|
|
1078
|
+
out.push(isAbsolute(entry) ? entry : resolve(workspaceRoot, entry));
|
|
1079
|
+
}
|
|
1080
|
+
return out;
|
|
1081
|
+
}
|
|
1082
|
+
async runContextBuilderPreflight(signal) {
|
|
1083
|
+
if (this.restoredWorkflowContext !== undefined) {
|
|
1084
|
+
return;
|
|
1085
|
+
}
|
|
1086
|
+
const policy = this.resolveContextPolicy();
|
|
1087
|
+
if (policy.effectiveMode === "off" || policy.effectiveMode === "disabled") {
|
|
1088
|
+
return;
|
|
1089
|
+
}
|
|
1090
|
+
const runner = this.contextBuilderRunner;
|
|
1091
|
+
if (runner == null) {
|
|
1092
|
+
if (this.requireContextPreflight) {
|
|
1093
|
+
throw new WorkflowError(`Workflow context preflight is required in ${policy.effectiveMode} mode but no runner is available`);
|
|
1094
|
+
}
|
|
1095
|
+
return;
|
|
1096
|
+
}
|
|
1097
|
+
// A-7(a): strip force-excluded plan anchors from the caller's focus BEFORE
|
|
1098
|
+
// anything reads them, so the curator is never even asked to select the plan.
|
|
1099
|
+
this.enforcePlanReviewContextExclusions();
|
|
1100
|
+
const focusPaths = this.readStringArrayVariable("cb_focus_paths");
|
|
1101
|
+
const callerSearchTerms = this.readStringArrayVariable("cb_search_terms");
|
|
1102
|
+
this.throwIfAborted(signal);
|
|
1103
|
+
// Optional budget overrides: 6 flat number vars folded into a single budget
|
|
1104
|
+
// object containing ONLY the keys actually supplied. When none are present
|
|
1105
|
+
// the object is undefined and omitted via conditional spread, keeping the
|
|
1106
|
+
// no-budget input byte-identical to the historical shape (cache equality).
|
|
1107
|
+
const budgetEntries = [
|
|
1108
|
+
["max_estimated_tokens", this.readOptionalNumberVariable("cb_budget_max_estimated_tokens")],
|
|
1109
|
+
["max_files", this.readOptionalNumberVariable("cb_budget_max_files")],
|
|
1110
|
+
["max_scan_files", this.readOptionalNumberVariable("cb_budget_max_scan_files")],
|
|
1111
|
+
["max_bytes_per_file", this.readOptionalNumberVariable("cb_budget_max_bytes_per_file")],
|
|
1112
|
+
["max_total_bytes", this.readOptionalNumberVariable("cb_budget_max_total_bytes")],
|
|
1113
|
+
["max_search_results", this.readOptionalNumberVariable("cb_budget_max_search_results")],
|
|
1114
|
+
];
|
|
1115
|
+
const budgetObject = {};
|
|
1116
|
+
for (const [key, value] of budgetEntries) {
|
|
1117
|
+
if (value !== undefined) {
|
|
1118
|
+
budgetObject[key] = value;
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
const budget = Object.keys(budgetObject).length > 0 ? budgetObject : undefined;
|
|
1122
|
+
const contextBudgetAdapter = this.readOptionalStringVariable("cb_budget_adapter");
|
|
1123
|
+
const hasCallerSearchTerms = callerSearchTerms.length > 0;
|
|
1124
|
+
const shouldSynthesizeSeedTerms = focusPaths.length === 0 && !hasCallerSearchTerms;
|
|
1125
|
+
// Match mapPreflightContextInput's nonblank rule: a whitespace-only cb_task
|
|
1126
|
+
// must fall through to user_request, else seed synthesis yields no terms and
|
|
1127
|
+
// allowRootWideDiscovery runs a broad scan with no search terms.
|
|
1128
|
+
const cbTaskSeed = this.readOptionalStringVariable("cb_task");
|
|
1129
|
+
const seedText = resolveSeedSource(cbTaskSeed, this.readOptionalStringVariable("user_request"));
|
|
1130
|
+
const seedSourceError = contextSeedSourceValidationError(seedText);
|
|
1131
|
+
if (shouldSynthesizeSeedTerms && seedSourceError != null) {
|
|
1132
|
+
throw new McpError(ErrorCode.InvalidParams, seedSourceError);
|
|
1133
|
+
}
|
|
1134
|
+
const synthesizedSearchTerms = shouldSynthesizeSeedTerms
|
|
1135
|
+
? synthesizeSeedTerms(seedText)
|
|
1136
|
+
: [];
|
|
1137
|
+
const searchTerms = shouldSynthesizeSeedTerms ? synthesizedSearchTerms : callerSearchTerms;
|
|
1138
|
+
const input = {
|
|
1139
|
+
runId: this.runId,
|
|
1140
|
+
runDir: this.runDir,
|
|
1141
|
+
cb_task: this.readOptionalStringVariable("cb_task") ?? "",
|
|
1142
|
+
user_request: this.readOptionalStringVariable("user_request") ?? "",
|
|
1143
|
+
roots: this.readStringArrayVariable("cb_roots"),
|
|
1144
|
+
focus_paths: focusPaths,
|
|
1145
|
+
include_globs: this.readStringArrayVariable("cb_include_globs"),
|
|
1146
|
+
// A-7(a): the enforced plan exclusions are folded in HERE — before the
|
|
1147
|
+
// delegate packet is built — so the default-on curator+finalizer never
|
|
1148
|
+
// sees the plan as a candidate. Caller `cb_exclude_globs:[]` cannot drop
|
|
1149
|
+
// them; the fold is unconditional.
|
|
1150
|
+
exclude_globs: this.planReviewContextPolicy == null
|
|
1151
|
+
? this.readStringArrayVariable("cb_exclude_globs")
|
|
1152
|
+
: foldPlanReviewExcludeGlobs(this.planReviewContextPolicy, this.readStringArrayVariable("cb_exclude_globs")),
|
|
1153
|
+
search_terms: searchTerms,
|
|
1154
|
+
profile: this.readOptionalStringVariable("cb_profile"),
|
|
1155
|
+
allow_secrets: this.readOptionalBooleanVariable("cb_allow_secrets"),
|
|
1156
|
+
context_mode: policy.effectiveMode,
|
|
1157
|
+
...(shouldSynthesizeSeedTerms ? { allowRootWideDiscovery: true } : {}),
|
|
1158
|
+
...(this.definition.settings.include_selected_context_artifact === true
|
|
1159
|
+
? { includeSelectedContext: true }
|
|
1160
|
+
: {}),
|
|
1161
|
+
...(contextBudgetAdapter == null || contextBudgetAdapter.trim() === ""
|
|
1162
|
+
? {}
|
|
1163
|
+
: { context_budget_adapter: contextBudgetAdapter }),
|
|
1164
|
+
...(budget == null ? {} : { budget }),
|
|
1165
|
+
...(policy.effectiveDelegate == null ? {} : { delegate: policy.effectiveDelegate }),
|
|
1166
|
+
};
|
|
1167
|
+
// Light-caller provenance: capture how anchors were derived. `root_wide_used`
|
|
1168
|
+
// records the REALIZED `input.allowRootWideDiscovery` flag actually passed to
|
|
1169
|
+
// the runner, while `anchor_source` reflects whether seed synthesis ran. Today
|
|
1170
|
+
// both flow from the same `shouldSynthesizeSeedTerms` decision.
|
|
1171
|
+
const lightCaller = {
|
|
1172
|
+
anchor_source: shouldSynthesizeSeedTerms ? "synthesized" : "caller",
|
|
1173
|
+
root_wide_used: input.allowRootWideDiscovery === true,
|
|
1174
|
+
synthesized_search_terms: shouldSynthesizeSeedTerms ? synthesizedSearchTerms : [],
|
|
1175
|
+
};
|
|
1176
|
+
let result;
|
|
1177
|
+
try {
|
|
1178
|
+
result = await runner.runOrReuse(input, { signal });
|
|
1179
|
+
}
|
|
1180
|
+
catch (error) {
|
|
1181
|
+
// The injected runner lives in the root package, which clinkx-workflows
|
|
1182
|
+
// must not import. We therefore cannot catch the root CancellationError by
|
|
1183
|
+
// type; instead, normalize at the engine boundary using the signal: if the
|
|
1184
|
+
// signal aborted, surface it as a WorkflowCancellationError (preserving any
|
|
1185
|
+
// existing abort reason). A genuine non-abort failure rethrows unchanged.
|
|
1186
|
+
this.throwIfAborted(signal);
|
|
1187
|
+
throw error;
|
|
1188
|
+
}
|
|
1189
|
+
this.throwIfAborted(signal);
|
|
1190
|
+
const effectiveCoverageStatus = result.metrics.files_selected > 0
|
|
1191
|
+
? result.coverage?.status ?? "not_evaluated"
|
|
1192
|
+
: "not_evaluated";
|
|
1193
|
+
this.setEffectiveVariable("context_quality", effectiveCoverageStatus);
|
|
1194
|
+
const delegateInput = policy.effectiveDelegate?.enabled === true ? policy.effectiveDelegate : undefined;
|
|
1195
|
+
const fallbackUsed = result.delegate?.fallback_used === true;
|
|
1196
|
+
const fallbackAggregate = recordPreflightFallbackAggregate({
|
|
1197
|
+
delegateEnabled: result.delegate?.enabled === true,
|
|
1198
|
+
fallbackUsed,
|
|
1199
|
+
});
|
|
1200
|
+
logger.info({
|
|
1201
|
+
run_id: this.runId,
|
|
1202
|
+
workflow: this.definition.name,
|
|
1203
|
+
source_run_id: result.source_run_id,
|
|
1204
|
+
mode: policy.effectiveMode,
|
|
1205
|
+
delegate_enabled: delegateInput != null,
|
|
1206
|
+
fallback_used: fallbackUsed,
|
|
1207
|
+
curator_ms: result.observability?.curator_ms ?? null,
|
|
1208
|
+
finalizer_ms: result.observability?.finalizer_ms ?? null,
|
|
1209
|
+
files_seen: result.metrics.files_seen,
|
|
1210
|
+
bytes_read: result.metrics.bytes_read,
|
|
1211
|
+
scan_cap_hit: result.metrics.scan_cap_hit,
|
|
1212
|
+
candidate_pool_size: result.observability?.candidate_pool_size ?? 0,
|
|
1213
|
+
delegate_packet_size: result.observability?.delegate_packet_size ?? 0,
|
|
1214
|
+
files_selected: result.metrics.files_selected,
|
|
1215
|
+
cache: {
|
|
1216
|
+
hit: result.cache.hit,
|
|
1217
|
+
write: result.cache.write,
|
|
1218
|
+
key_prefix: result.cache.key_prefix,
|
|
1219
|
+
},
|
|
1220
|
+
curator: delegateInput == null
|
|
1221
|
+
? null
|
|
1222
|
+
: {
|
|
1223
|
+
adapter: delegateInput.curator.cli_name,
|
|
1224
|
+
role: delegateInput.curator.role,
|
|
1225
|
+
},
|
|
1226
|
+
finalizer: delegateInput == null
|
|
1227
|
+
? null
|
|
1228
|
+
: {
|
|
1229
|
+
adapter: delegateInput.finalizer.cli_name,
|
|
1230
|
+
role: delegateInput.finalizer.role,
|
|
1231
|
+
},
|
|
1232
|
+
failure_reason: receiptDelegateFailureReason(result),
|
|
1233
|
+
warning_code: firstReceiptWarningCode(result),
|
|
1234
|
+
budget_caps: result.observability?.budget_caps ?? null,
|
|
1235
|
+
profile: input.profile ?? null,
|
|
1236
|
+
context_budget_adapter: input.context_budget_adapter ?? null,
|
|
1237
|
+
root_source: input.roots.length > 0 ? "explicit" : "fallback",
|
|
1238
|
+
seed_term_count: searchTerms.length,
|
|
1239
|
+
fallback_aggregate: fallbackAggregate,
|
|
1240
|
+
}, "context-builder preflight summary");
|
|
1241
|
+
if (result.metrics.files_selected === 0) {
|
|
1242
|
+
if (this.definition.settings.fail_on_empty_context_selection === true) {
|
|
1243
|
+
throw new WorkflowError("Context-builder preflight selected no files");
|
|
1244
|
+
}
|
|
1245
|
+
logger.warn({
|
|
1246
|
+
run_id: this.runId,
|
|
1247
|
+
source_run_id: result.source_run_id,
|
|
1248
|
+
files_selected: 0,
|
|
1249
|
+
}, "context-builder preflight selected no files; proceeding without bundle");
|
|
1250
|
+
}
|
|
1251
|
+
if (effectiveCoverageStatus === "degraded") {
|
|
1252
|
+
if (this.definition.settings.fail_on_degraded_context_coverage === true) {
|
|
1253
|
+
throw new WorkflowError("Context-builder preflight coverage is degraded");
|
|
1254
|
+
}
|
|
1255
|
+
logger.warn({
|
|
1256
|
+
run_id: this.runId,
|
|
1257
|
+
source_run_id: result.source_run_id,
|
|
1258
|
+
coverage_status: effectiveCoverageStatus,
|
|
1259
|
+
missing_required: result.coverage?.missing_required ?? [],
|
|
1260
|
+
}, "context-builder preflight coverage degraded; proceeding");
|
|
1261
|
+
}
|
|
1262
|
+
if (result.status === "partial") {
|
|
1263
|
+
const partial = classifyPreflightPartial(result.metrics);
|
|
1264
|
+
const payload = {
|
|
1265
|
+
run_id: this.runId,
|
|
1266
|
+
source_run_id: result.source_run_id,
|
|
1267
|
+
files_selected: result.metrics.files_selected,
|
|
1268
|
+
warning_count: result.warning_count,
|
|
1269
|
+
omission_count: result.omission_count,
|
|
1270
|
+
partial_reason: partial.partial_reason,
|
|
1271
|
+
};
|
|
1272
|
+
if (partial.severity === "degraded") {
|
|
1273
|
+
logger.warn(payload, "context-builder preflight returned partial context; proceeding");
|
|
1274
|
+
}
|
|
1275
|
+
else {
|
|
1276
|
+
logger.info(payload, "context-builder preflight returned partial context; proceeding");
|
|
1277
|
+
}
|
|
1278
|
+
}
|
|
1279
|
+
// Only attach the generated bundle artifacts when the preflight actually
|
|
1280
|
+
// selected files. On a zero-selection run we proceed WITHOUT a bundle
|
|
1281
|
+
// (matching the warn log above): the runner always emits two copied paths
|
|
1282
|
+
// (bundle.md + manifest.json) even for an empty selection, so appending
|
|
1283
|
+
// them would hand downstream seats an empty bundle that looks intentional.
|
|
1284
|
+
if (result.metrics.files_selected > 0) {
|
|
1285
|
+
const existingBundleFiles = this.readStringArrayVariable("context_bundle_files");
|
|
1286
|
+
this.setEffectiveVariable("context_bundle_files", orderedUnique([...existingBundleFiles, ...result.copied_paths]));
|
|
1287
|
+
}
|
|
1288
|
+
// A-7(b): post-filter the FINALIZED selection. This is not redundant with
|
|
1289
|
+
// the pre-dispatch fold — caller `cb_include_globs`/focus anchors can
|
|
1290
|
+
// re-admit exactly what the fold excluded, and this is the point where the
|
|
1291
|
+
// exclusion has to survive them. Runs BEFORE commitPreflightContext so the
|
|
1292
|
+
// persisted inputs snapshot never records an excluded path.
|
|
1293
|
+
const removed = this.enforcePlanReviewContextExclusions();
|
|
1294
|
+
if (this.planReviewContextPolicy != null) {
|
|
1295
|
+
await this.commitPlanReviewContextProvenance?.({
|
|
1296
|
+
enforced: true,
|
|
1297
|
+
...(policy.effectiveMode != null ? { context_mode: policy.effectiveMode } : {}),
|
|
1298
|
+
copied_paths: [...this.readStringArrayVariable("context_bundle_files")],
|
|
1299
|
+
filtered_out_paths: removed,
|
|
1300
|
+
});
|
|
1301
|
+
}
|
|
1302
|
+
await this.commitPreflightContext?.({
|
|
1303
|
+
variables: { ...this.effectiveVariables },
|
|
1304
|
+
workflow_context: this.buildWorkflowContext(result, policy.effectiveMode, lightCaller),
|
|
1305
|
+
});
|
|
1306
|
+
}
|
|
1307
|
+
/**
|
|
1308
|
+
* Strip every force-excluded plan path from the effective context path
|
|
1309
|
+
* variables (A-7). Idempotent and safe to call on any path — no policy means
|
|
1310
|
+
* no-op, so non-plan-review runs are byte-unchanged.
|
|
1311
|
+
*
|
|
1312
|
+
* Returns the paths that were removed, for provenance.
|
|
1313
|
+
*/
|
|
1314
|
+
enforcePlanReviewContextExclusions() {
|
|
1315
|
+
const planPolicy = this.planReviewContextPolicy;
|
|
1316
|
+
if (planPolicy == null) {
|
|
1317
|
+
return [];
|
|
1318
|
+
}
|
|
1319
|
+
const removedAll = [];
|
|
1320
|
+
for (const name of ["context_files", "context_bundle_files", "cb_focus_paths"]) {
|
|
1321
|
+
const values = this.readStringArrayVariable(name);
|
|
1322
|
+
if (values.length === 0)
|
|
1323
|
+
continue;
|
|
1324
|
+
const { kept, removed } = filterExcludedPlanPaths(planPolicy, values, resolveWorkspaceRoot());
|
|
1325
|
+
if (removed.length === 0)
|
|
1326
|
+
continue;
|
|
1327
|
+
removedAll.push(...removed);
|
|
1328
|
+
this.setEffectiveVariable(name, kept);
|
|
1329
|
+
logger.warn({ run_id: this.runId, variable: name, removed }, "plan-review context policy stripped excluded plan paths");
|
|
1330
|
+
}
|
|
1331
|
+
return removedAll;
|
|
1332
|
+
}
|
|
1333
|
+
buildWorkflowContext(result, contextMode, lightCaller) {
|
|
1334
|
+
return mapPreflightResultToWorkflowContext(result, contextMode, lightCaller);
|
|
1335
|
+
}
|
|
1336
|
+
resolveContextPolicy() {
|
|
1337
|
+
if (this.initialContextPolicy !== undefined) {
|
|
1338
|
+
return this.initialContextPolicy;
|
|
1339
|
+
}
|
|
1340
|
+
try {
|
|
1341
|
+
return resolveWorkflowContextMode(this.effectiveVariables["cb_context_mode"], this.contextDefault, true, this.effectiveVariables["cb_delegate"], this.defaultCurator);
|
|
1342
|
+
}
|
|
1343
|
+
catch (error) {
|
|
1344
|
+
if (error instanceof WorkflowContextModeResolverError ||
|
|
1345
|
+
error instanceof WorkflowDelegateValidationError) {
|
|
1346
|
+
throw new WorkflowError(error.message);
|
|
1347
|
+
}
|
|
1348
|
+
throw error;
|
|
1349
|
+
}
|
|
1350
|
+
}
|
|
1351
|
+
readOptionalStringVariable(name) {
|
|
1352
|
+
const value = this.effectiveVariables[name];
|
|
1353
|
+
if (value === undefined) {
|
|
1354
|
+
return undefined;
|
|
1355
|
+
}
|
|
1356
|
+
if (typeof value !== "string") {
|
|
1357
|
+
throw new WorkflowError(`Variable "${name}" for context-builder preflight must be a string`);
|
|
1358
|
+
}
|
|
1359
|
+
return value;
|
|
1360
|
+
}
|
|
1361
|
+
readOptionalNumberVariable(name) {
|
|
1362
|
+
const value = this.effectiveVariables[name];
|
|
1363
|
+
if (value === undefined) {
|
|
1364
|
+
return undefined;
|
|
1365
|
+
}
|
|
1366
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
1367
|
+
throw new WorkflowError(`Variable "${name}" for context-builder preflight must be a finite number`);
|
|
1368
|
+
}
|
|
1369
|
+
return value;
|
|
1370
|
+
}
|
|
1371
|
+
readOptionalBooleanVariable(name) {
|
|
1372
|
+
const value = this.effectiveVariables[name];
|
|
1373
|
+
if (value === undefined) {
|
|
1374
|
+
return undefined;
|
|
1375
|
+
}
|
|
1376
|
+
if (typeof value === "boolean") {
|
|
1377
|
+
return value;
|
|
1378
|
+
}
|
|
1379
|
+
if (value === "true" || value === "1") {
|
|
1380
|
+
return true;
|
|
1381
|
+
}
|
|
1382
|
+
if (value === "false" || value === "0") {
|
|
1383
|
+
return false;
|
|
1384
|
+
}
|
|
1385
|
+
throw new WorkflowError(`Variable "${name}" for context-builder preflight must be a boolean`);
|
|
1386
|
+
}
|
|
1387
|
+
readStringArrayVariable(name) {
|
|
1388
|
+
const value = this.effectiveVariables[name];
|
|
1389
|
+
if (value === undefined) {
|
|
1390
|
+
return [];
|
|
1391
|
+
}
|
|
1392
|
+
if (!Array.isArray(value) || !value.every((entry) => typeof entry === "string")) {
|
|
1393
|
+
throw new WorkflowError(`Variable "${name}" for context-builder preflight must be a string[]`);
|
|
1394
|
+
}
|
|
1395
|
+
return value;
|
|
1396
|
+
}
|
|
653
1397
|
createDeadlineController() {
|
|
654
1398
|
const controller = new AbortController();
|
|
655
1399
|
const timeout = setTimeout(() => {
|
|
656
1400
|
controller.abort(new Error(`Workflow timeout exceeded after ${String(this.definition.settings.timeout_seconds)} seconds`));
|
|
657
1401
|
}, this.definition.settings.timeout_seconds * 1000);
|
|
658
1402
|
if (this.signal != null) {
|
|
659
|
-
this.signal.
|
|
1403
|
+
if (this.signal.aborted) {
|
|
1404
|
+
controller.abort(new WorkflowCancellationError());
|
|
1405
|
+
}
|
|
1406
|
+
else {
|
|
1407
|
+
this.signal.addEventListener("abort", () => controller.abort(new WorkflowCancellationError()), { once: true });
|
|
1408
|
+
}
|
|
660
1409
|
}
|
|
661
1410
|
return {
|
|
662
1411
|
signal: controller.signal,
|
|
@@ -709,18 +1458,67 @@ export class WorkflowEngine {
|
|
|
709
1458
|
join(this.runDir, "artifacts"),
|
|
710
1459
|
];
|
|
711
1460
|
}
|
|
1461
|
+
recordPlanReviewDeliveredAliases(callKey, call, absoluteFilePaths) {
|
|
1462
|
+
if (call.plan_review_anonymous_aliases == null)
|
|
1463
|
+
return;
|
|
1464
|
+
const configured = new Set(Object.values(call.plan_review_anonymous_aliases));
|
|
1465
|
+
const delivered = new Set();
|
|
1466
|
+
const anonymousRoot = resolve(this.artifactStore.artifactsDir, "anonymous");
|
|
1467
|
+
if (Array.isArray(absoluteFilePaths)) {
|
|
1468
|
+
for (const path of absoluteFilePaths) {
|
|
1469
|
+
if (typeof path !== "string")
|
|
1470
|
+
continue;
|
|
1471
|
+
const absolute = resolve(path);
|
|
1472
|
+
if (!absolute.startsWith(anonymousRoot + sep))
|
|
1473
|
+
continue;
|
|
1474
|
+
const alias = basename(path, ".json");
|
|
1475
|
+
if (configured.has(alias))
|
|
1476
|
+
delivered.add(alias);
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
this.planReviewDeliveredAliases.set(callKey, delivered);
|
|
1480
|
+
}
|
|
712
1481
|
buildCallInput(stage, call, callKey, callIndex) {
|
|
713
1482
|
const resumedInput = this.resumePreparedInputs.get(callKey);
|
|
714
1483
|
if (resumedInput != null) {
|
|
715
|
-
|
|
1484
|
+
const input = structuredClone(resumedInput);
|
|
1485
|
+
this.recordPlanReviewDeliveredAliases(callKey, call, input["absolute_file_paths"]);
|
|
1486
|
+
const binding = this.planReviewCallBindings.get(callKey);
|
|
1487
|
+
if (binding !== undefined) {
|
|
1488
|
+
input["cli_name"] = binding.cli_name;
|
|
1489
|
+
}
|
|
1490
|
+
return input;
|
|
716
1491
|
}
|
|
717
|
-
const
|
|
1492
|
+
const renderedPrompt = renderCallPrompt(call.prompt, { ...this.definition.variables, ...ENGINE_DERIVED_VARIABLE_DECLS }, this.effectiveVariables, {
|
|
718
1493
|
workflow: { run_id: this.runId },
|
|
719
1494
|
stage: { id: stage.id },
|
|
720
1495
|
call: { id: call.id },
|
|
721
1496
|
});
|
|
1497
|
+
/**
|
|
1498
|
+
* A-6/D9: append the verified plan snapshot AFTER rendering, never before.
|
|
1499
|
+
*
|
|
1500
|
+
* The snapshot must not be an input to `renderCallPrompt`. A plan is
|
|
1501
|
+
* untrusted text that may legitimately contain `{{...}}` (plans in this
|
|
1502
|
+
* repo do), and `PLACEHOLDER_RE` scans the WHOLE string it is given: a
|
|
1503
|
+
* `{{call.id}}` inside the plan would be silently substituted — handing the
|
|
1504
|
+
* actor bytes that are NOT the verified snapshot, which is a D9 violation
|
|
1505
|
+
* requiring no tampering at all — and an undeclared `{{user_request}}`
|
|
1506
|
+
* would throw and brick an honest run. Appending after rendering is what
|
|
1507
|
+
* makes the snapshot reach the actor VERBATIM.
|
|
1508
|
+
*
|
|
1509
|
+
* This lands before `estimatePromptBudget` below on purpose: the snapshot
|
|
1510
|
+
* is real dispatched bytes (up to 512 KiB) and must be billed as such, or
|
|
1511
|
+
* the authoritative gate would under-count by the largest term in it.
|
|
1512
|
+
*/
|
|
1513
|
+
const prompt = call.plan_review_snapshot_text === undefined
|
|
1514
|
+
? renderedPrompt
|
|
1515
|
+
: `${renderedPrompt}${renderPlanSnapshotBlock(call.plan_review_snapshot_text)}`;
|
|
722
1516
|
const contextArtifacts = call.anonymize_context === true
|
|
723
|
-
? this.artifactStore.buildAnonymizedContext(this.definition, call.context_from, callIndex
|
|
1517
|
+
? this.artifactStore.buildAnonymizedContext(this.definition, call.context_from, callIndex, call.plan_review_semantic_context === true
|
|
1518
|
+
? projectPlanReviewSemanticBody
|
|
1519
|
+
: undefined, call.plan_review_semantic_context === true
|
|
1520
|
+
? `${stage.id}-${call.id}`
|
|
1521
|
+
: undefined, call.plan_review_anonymous_aliases)
|
|
724
1522
|
: this.artifactStore.buildContext(this.definition, call.context_from);
|
|
725
1523
|
const staticFiles = call.absolute_file_paths ?? [];
|
|
726
1524
|
const dynamicFiles = this.resolveDynamicPaths(call.absolute_file_paths_from, `${stage.id}.${call.id}.absolute_file_paths_from`);
|
|
@@ -731,6 +1529,7 @@ export class WorkflowEngine {
|
|
|
731
1529
|
...staticFiles,
|
|
732
1530
|
...dynamicFiles,
|
|
733
1531
|
];
|
|
1532
|
+
this.recordPlanReviewDeliveredAliases(callKey, call, contextArtifacts.absoluteFilePaths);
|
|
734
1533
|
const images = [...staticImages, ...dynamicImages];
|
|
735
1534
|
const estimate = estimatePromptBudget({
|
|
736
1535
|
promptText: prompt,
|
|
@@ -834,7 +1633,9 @@ export class WorkflowEngine {
|
|
|
834
1633
|
}
|
|
835
1634
|
}
|
|
836
1635
|
for (const failedCall of this.resumeFailedCalls.values()) {
|
|
837
|
-
|
|
1636
|
+
if (failedCall.terminal) {
|
|
1637
|
+
this.artifactStore.seedFailedCall(failedCall.stageId, failedCall.callId, failedCall.error);
|
|
1638
|
+
}
|
|
838
1639
|
}
|
|
839
1640
|
}
|
|
840
1641
|
/**
|
|
@@ -994,8 +1795,61 @@ function classifyFailureClass(error) {
|
|
|
994
1795
|
if (error instanceof WorkflowCancellationError) {
|
|
995
1796
|
return "permanent";
|
|
996
1797
|
}
|
|
1798
|
+
// Cross-package classification marker: errors thrown by the injected root
|
|
1799
|
+
// package (e.g. a context-builder preflight delegate timeout) cannot be
|
|
1800
|
+
// narrowed by instanceof here, so honor a well-formed `failureClass`
|
|
1801
|
+
// property when present. Anything else keeps the permanent default.
|
|
1802
|
+
const marker = crossPackageFailureClass(error);
|
|
1803
|
+
if (marker != null) {
|
|
1804
|
+
return marker;
|
|
1805
|
+
}
|
|
997
1806
|
return "permanent";
|
|
998
1807
|
}
|
|
1808
|
+
function crossPackageFailureClass(error) {
|
|
1809
|
+
if (error == null || typeof error !== "object") {
|
|
1810
|
+
return undefined;
|
|
1811
|
+
}
|
|
1812
|
+
const value = error.failureClass;
|
|
1813
|
+
return value === "transient" || value === "permanent" ? value : undefined;
|
|
1814
|
+
}
|
|
1815
|
+
/**
|
|
1816
|
+
* Bounded corrective suffix appended to the NEXT attempt's prompt after a
|
|
1817
|
+
* JSON/schema-shaped failure (SchemaValidationError, or OutputSizeError as
|
|
1818
|
+
* the second correctable class) that is already being retried. Total suffix
|
|
1819
|
+
* length is capped at CORRECTIVE_SUFFIX_MAX_CHARS; the failure detail is
|
|
1820
|
+
* truncated to fit. Never persisted — resume replays the original input.
|
|
1821
|
+
*/
|
|
1822
|
+
const CORRECTIVE_SUFFIX_MAX_CHARS = 1_500;
|
|
1823
|
+
const CORRECTIVE_SUFFIX_HEADER = "\n\n---\nCORRECTION: Your previous response was rejected because it was not schema-valid. Failure: ";
|
|
1824
|
+
const CORRECTIVE_SIZE_SUFFIX_HEADER = "\n\n---\nCORRECTION: Your previous response was rejected because the pretty-printed JSON exceeded the validator byte cap. Shrink the deliverable first. Failure: ";
|
|
1825
|
+
const CORRECTIVE_SUFFIX_FOOTER = "\nReturn ONLY a single JSON object conforming to the declared contract — no prose, no markdown, " +
|
|
1826
|
+
"no code fences, and nothing before or after the JSON object.";
|
|
1827
|
+
/**
|
|
1828
|
+
* Detail text for a JSON/schema-shaped failure, or undefined when the failure
|
|
1829
|
+
* is not one a corrective re-prompt could fix (transport death, timeout, …).
|
|
1830
|
+
*/
|
|
1831
|
+
function correctiveFailureDetail(error) {
|
|
1832
|
+
if (error instanceof SchemaValidationError) {
|
|
1833
|
+
return error.message;
|
|
1834
|
+
}
|
|
1835
|
+
if (error instanceof OutputSizeError) {
|
|
1836
|
+
return error.message;
|
|
1837
|
+
}
|
|
1838
|
+
if (error instanceof CallExecutionError &&
|
|
1839
|
+
error.childDebugMetadata?.rejection_reason === "invalid_json_output") {
|
|
1840
|
+
return "output did not contain valid JSON";
|
|
1841
|
+
}
|
|
1842
|
+
return undefined;
|
|
1843
|
+
}
|
|
1844
|
+
function buildCorrectiveSuffixText(detail, header = CORRECTIVE_SUFFIX_HEADER) {
|
|
1845
|
+
const budget = CORRECTIVE_SUFFIX_MAX_CHARS -
|
|
1846
|
+
header.length -
|
|
1847
|
+
CORRECTIVE_SUFFIX_FOOTER.length;
|
|
1848
|
+
const bounded = detail.length > budget
|
|
1849
|
+
? `${detail.slice(0, Math.max(0, budget - 1))}…`
|
|
1850
|
+
: detail;
|
|
1851
|
+
return `${header}${bounded}${CORRECTIVE_SUFFIX_FOOTER}`;
|
|
1852
|
+
}
|
|
999
1853
|
/**
|
|
1000
1854
|
* Determine whether an error is retryable based on the retry mode (5-1, 5-2).
|
|
1001
1855
|
*
|