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
|
@@ -1,47 +1,95 @@
|
|
|
1
|
-
import { isSamePathOrDescendant } from "../allowed-roots.js";
|
|
1
|
+
import { appendArtifactOnlyRootsToEnv, isArtifactOnlyAllowedRootEntry, isSamePathOrDescendant, parseAllowedRootsEnv, stripArtifactOnlyMarker, } from "../allowed-roots.js";
|
|
2
2
|
import { CancellationError, ExecutionError, InvalidParamsError, } from "../errors.js";
|
|
3
|
-
import {
|
|
3
|
+
import { realpathSync } from "node:fs";
|
|
4
4
|
import { realpath as fsRealpath } from "node:fs/promises";
|
|
5
5
|
import path from "node:path";
|
|
6
|
-
import { getCurrentAllowedRoots } from "../paths.js";
|
|
7
6
|
import { AdapterRegistry as RuntimeAdapterRegistry } from "../registry.js";
|
|
7
|
+
import { logger } from "../logger.js";
|
|
8
8
|
import { assertContextBuilderAnchorSufficiency } from "./anchors.js";
|
|
9
9
|
import { writeContextBuilderFinalArtifacts, } from "./bundle.js";
|
|
10
|
-
import { CONTEXT_BUILDER_INTERNAL_CAP_DEFAULTS,
|
|
11
|
-
import { buildContextBuilderCuratorPacket, buildContextBuilderFinalizerPacket, CONTEXT_BUILDER_DELEGATE_PACKET_CAP_PROFILE_DEFAULTS, } from "./delegate-packet.js";
|
|
10
|
+
import { CONTEXT_BUILDER_INTERNAL_CAP_DEFAULTS, getSharedContextBuilderSemaphore, resolveContextBuilderRunsDir, } from "./config.js";
|
|
11
|
+
import { buildContextBuilderCuratorPacket, buildContextBuilderFinalizerPacket, CONTEXT_BUILDER_DELEGATE_PACKET_CAP_PROFILE_DEFAULTS, contextBuilderCandidateId, } from "./delegate-packet.js";
|
|
12
12
|
import { reconstructContextBuilderDelegateSelection } from "./delegate-reconstruction.js";
|
|
13
13
|
import { runContextBuilderDelegateCall, } from "./delegate.js";
|
|
14
|
-
import { validateContextBuilderCuratorOutput, validateContextBuilderFinalizerOutput, } from "./delegate-validation.js";
|
|
15
|
-
import { discoverContextBuilderCandidates, mergeLineRanges,
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
14
|
+
import { validateContextBuilderCuratorOutput, validateContextBuilderDelegateCoverageSelection, validateContextBuilderFinalizerOutput, } from "./delegate-validation.js";
|
|
15
|
+
import { discoverContextBuilderCandidates, mergeLineRanges, } from "./discovery.js";
|
|
16
|
+
import { evaluateSearchCapMateriality } from "./status.js";
|
|
17
|
+
import { boundContextBuilderPathList, isRequestedEvidenceOmission, sanitizeDelegateFailureText, sanitizeDiagnosticText, } from "./diagnostics.js";
|
|
18
|
+
import { compileContextBuilderGlobs, CONTEXT_BUILDER_DEFAULT_EXCLUDE_GLOBS, validateContextBuilderGlobs, } from "./globs.js";
|
|
18
19
|
import { resolveContextBuilderFocusPaths, resolveContextBuilderRoots, } from "./paths.js";
|
|
19
20
|
import { createContextBuilderProgressReporter, } from "./progress.js";
|
|
21
|
+
import { ContextBuilderLifecycleController, ContextBuilderLifecycleObserverError, } from "./lifecycle-observer.js";
|
|
22
|
+
import { createContextBuilderLifecycleJournal } from "./lifecycle-journal.js";
|
|
23
|
+
import { recordContextRunCatalogMarker } from "./context-run-catalog.js";
|
|
24
|
+
import { appendLifecycleCompletionWarning } from "./lifecycle-recovery.js";
|
|
25
|
+
import { withContextBuilderWriteWatchdog } from "./write-watchdog.js";
|
|
20
26
|
import { createContextBuilderReceiptTextResponse, } from "./receipt.js";
|
|
21
27
|
import { generateContextBuilderRunId } from "./run-id.js";
|
|
22
28
|
import { CONTEXT_BUILDER_INTENT_PROFILE_DEFAULTS, parseContextBuilderInput, } from "./schema.js";
|
|
23
|
-
import {
|
|
29
|
+
import { evaluateContextCoverage, resolveContextCoveragePolicy, } from "./review-coverage.js";
|
|
30
|
+
import { selectContextBuilderFinalContext, } from "./selection.js";
|
|
24
31
|
import { cleanupContextBuilderIncompleteRun, createContextBuilderRun, } from "./storage.js";
|
|
32
|
+
import { admitJournalContextGeneration } from "./session-log-identity.js";
|
|
25
33
|
import { estimateContextBuilderTokensFromUtf8Bytes } from "./token-estimate.js";
|
|
26
|
-
let defaultSemaphore;
|
|
27
34
|
let defaultAdapterRegistry;
|
|
28
35
|
const artifactOnlyAllowedRoots = new Set();
|
|
29
|
-
const CONTEXT_BUILDER_DELEGATE_PROMPT_VERSION = "cb-delegate-
|
|
36
|
+
const CONTEXT_BUILDER_DELEGATE_PROMPT_VERSION = "cb-delegate-v3-scored-obligations";
|
|
30
37
|
const MAX_DELEGATE_MINIMUM_OUTPUT_SELECTIONS = 24;
|
|
31
38
|
export async function handleContextBuilder(args, options = {}) {
|
|
32
39
|
const input = parseContextBuilderInput(args);
|
|
40
|
+
const coveragePolicy = resolveContextCoveragePolicy(input.profile);
|
|
33
41
|
const semaphore = options.semaphore ?? getDefaultContextBuilderSemaphore();
|
|
34
|
-
semaphore.acquire();
|
|
35
42
|
const progress = createContextBuilderProgressReporter(options.progress);
|
|
43
|
+
const ownsLifecycle = options.lifecycleController == null;
|
|
44
|
+
semaphore.acquire();
|
|
45
|
+
let lifecycle;
|
|
36
46
|
let storage;
|
|
37
47
|
let committed = false;
|
|
38
48
|
try {
|
|
39
|
-
|
|
49
|
+
// Resolve and validate the runs dir up front so a broad/forbidden runs_dir
|
|
50
|
+
// is rejected with its canonical error BEFORE any lifecycle transition, and
|
|
51
|
+
// so the journal reuses this one already-validated path instead of
|
|
52
|
+
// re-resolving (a second resolution would wrap that rejection as an opaque
|
|
53
|
+
// observer failure).
|
|
54
|
+
const runsDir = await resolveContextBuilderRunsDir(options.runsDir);
|
|
55
|
+
// A standalone operation that owns its lifecycle (no injected observer)
|
|
56
|
+
// persists authoritatively through the .context-runs journal; the injected
|
|
57
|
+
// path — workflow preflight — supplies its own store instead.
|
|
58
|
+
const journal = ownsLifecycle && options.lifecycle == null
|
|
59
|
+
? createContextBuilderLifecycleJournal({
|
|
60
|
+
runsDir,
|
|
61
|
+
resolveRunsDir: async () => runsDir,
|
|
62
|
+
})
|
|
63
|
+
: undefined;
|
|
64
|
+
lifecycle = options.lifecycleController ??
|
|
65
|
+
new ContextBuilderLifecycleController({
|
|
66
|
+
operationKind: "standalone",
|
|
67
|
+
execution: lifecycleExecutionConfig(input),
|
|
68
|
+
...(options.lifecycle != null
|
|
69
|
+
? {
|
|
70
|
+
observer: options.lifecycle.observer,
|
|
71
|
+
...(options.lifecycle.origin == null
|
|
72
|
+
? {}
|
|
73
|
+
: { origin: options.lifecycle.origin }),
|
|
74
|
+
}
|
|
75
|
+
: journal == null
|
|
76
|
+
? {}
|
|
77
|
+
: { observer: journal.observe }),
|
|
78
|
+
...(progress == null ? {} : { notificationObserver: progress.observe }),
|
|
79
|
+
...(options.now == null ? {} : { now: options.now }),
|
|
80
|
+
});
|
|
81
|
+
if (ownsLifecycle) {
|
|
82
|
+
await lifecycle?.transition("resolving");
|
|
83
|
+
}
|
|
40
84
|
const createdAt = (options.now?.() ?? new Date()).toISOString();
|
|
41
85
|
const runId = input.run_id ?? generateContextBuilderRunId();
|
|
86
|
+
await lifecycle?.transition("validating", {
|
|
87
|
+
...(ownsLifecycle ? { artifactRunId: runId } : {}),
|
|
88
|
+
});
|
|
42
89
|
const internalCaps = options.internalCaps ?? CONTEXT_BUILDER_INTERNAL_CAP_DEFAULTS;
|
|
43
|
-
const
|
|
44
|
-
|
|
90
|
+
const currentAllowedRoots = parseAllowedRootsEnv({
|
|
91
|
+
includeArtifactOnly: false,
|
|
92
|
+
});
|
|
45
93
|
const explicitlyConfiguredArtifactRoots = getExactlyConfiguredArtifactRoots();
|
|
46
94
|
const sourceAllowedRoots = currentAllowedRoots.filter((root) => !artifactOnlyAllowedRoots.has(root) ||
|
|
47
95
|
explicitlyConfiguredArtifactRoots.has(root));
|
|
@@ -60,38 +108,77 @@ export async function handleContextBuilder(args, options = {}) {
|
|
|
60
108
|
focus_paths: focusPaths,
|
|
61
109
|
include_globs: input.include_globs,
|
|
62
110
|
search_terms: input.search_terms,
|
|
111
|
+
allowRootWideDiscovery: options.allowRootWideDiscovery ?? false,
|
|
112
|
+
});
|
|
113
|
+
assertNoContextBuilderFocusGlobConflicts({
|
|
114
|
+
focusPaths,
|
|
115
|
+
includeGlobs: input.include_globs,
|
|
116
|
+
excludeGlobs: input.exclude_globs,
|
|
63
117
|
});
|
|
64
118
|
appendArtifactOnlyRootToEnv(runsDir);
|
|
65
119
|
if (!sourceAllowedRoots.includes(runsDir)) {
|
|
66
120
|
artifactOnlyAllowedRoots.add(runsDir);
|
|
67
121
|
}
|
|
68
122
|
const excludedArtifactRoots = [...artifactOnlyAllowedRoots].filter((root) => !explicitlyConfiguredArtifactRoots.has(root));
|
|
69
|
-
|
|
123
|
+
const writingStorage = await createContextBuilderRun({
|
|
70
124
|
runsDir,
|
|
71
125
|
runId,
|
|
72
126
|
createdAt,
|
|
73
127
|
});
|
|
74
|
-
|
|
128
|
+
storage = writingStorage;
|
|
129
|
+
let sessionLogContext;
|
|
130
|
+
if (journal !== undefined) {
|
|
131
|
+
try {
|
|
132
|
+
sessionLogContext = {
|
|
133
|
+
contextRunId: lifecycle.contextRunId,
|
|
134
|
+
contextGeneration: await admitJournalContextGeneration(runsDir, lifecycle.contextRunId),
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
logger.debug({ contextRunId: lifecycle.contextRunId }, "context session-log identity admission failed open");
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
else if (options.sessionLogContext !== undefined) {
|
|
142
|
+
// Workflow preflight path: the runner minted the operation id (via the
|
|
143
|
+
// controller) and created the run-dir generation sidecar. Use that pair
|
|
144
|
+
// instead of the journal (which the injected-controller path does not
|
|
145
|
+
// create). Both flow into the context-family session-log owner emitted for
|
|
146
|
+
// the curator/finalizer delegate calls.
|
|
147
|
+
const injected = options.sessionLogContext;
|
|
148
|
+
sessionLogContext = injected.contextRunId === lifecycle.contextRunId
|
|
149
|
+
? injected
|
|
150
|
+
: { contextRunId: lifecycle.contextRunId, contextGeneration: injected.contextGeneration };
|
|
151
|
+
}
|
|
152
|
+
await lifecycle?.transition("scanning");
|
|
75
153
|
const discovery = await discoverContextBuilderCandidates({
|
|
76
154
|
roots: rootResolution.roots,
|
|
77
155
|
task: input.task,
|
|
156
|
+
allowSecrets: input.allow_secrets,
|
|
78
157
|
focus_paths: focusPaths,
|
|
79
158
|
include_globs: input.include_globs,
|
|
80
159
|
exclude_globs: input.exclude_globs,
|
|
81
160
|
search_terms: input.search_terms,
|
|
161
|
+
coverage_policy: coveragePolicy,
|
|
82
162
|
budget: input.budget,
|
|
83
163
|
internal_caps: internalCaps,
|
|
84
164
|
excluded_real_roots: excludedArtifactRoots,
|
|
85
165
|
...(options.signal == null ? {} : { signal: options.signal }),
|
|
86
166
|
});
|
|
87
|
-
await
|
|
88
|
-
|
|
167
|
+
await lifecycle?.transition("searching", {
|
|
168
|
+
counts: lifecycleCountsFromDiscovery(discovery),
|
|
169
|
+
});
|
|
170
|
+
await lifecycle?.transition("selecting");
|
|
89
171
|
const selection = selectContextBuilderFinalContext({
|
|
90
172
|
task: input.task,
|
|
91
173
|
tree: discovery.tree,
|
|
92
174
|
search: discovery.search,
|
|
93
175
|
slices: discovery.slices,
|
|
94
176
|
budget: input.budget,
|
|
177
|
+
coverage_policy: coveragePolicy,
|
|
178
|
+
evidence_classes_by_candidate: evidenceClassesByCandidate(discovery.content_candidates),
|
|
179
|
+
...(discovery.review_obligations == null
|
|
180
|
+
? {}
|
|
181
|
+
: { review_obligations: discovery.review_obligations }),
|
|
95
182
|
});
|
|
96
183
|
const effectiveDelegatePacketCaps = resolveEffectiveDelegatePacketCaps(input.profile, options.delegatePacketCaps);
|
|
97
184
|
const delegateResult = input.delegate.enabled
|
|
@@ -103,55 +190,26 @@ export async function handleContextBuilder(args, options = {}) {
|
|
|
103
190
|
baselineOmissions: rootResolution.omissions,
|
|
104
191
|
sourceRoots: rootResolution.roots,
|
|
105
192
|
registry: options.registry ?? getDefaultAdapterRegistry(),
|
|
193
|
+
coveragePolicy,
|
|
106
194
|
...(effectiveDelegatePacketCaps == null
|
|
107
195
|
? {}
|
|
108
196
|
: { delegatePacketCaps: effectiveDelegatePacketCaps }),
|
|
109
|
-
...(
|
|
197
|
+
...(lifecycle == null ? {} : { lifecycle }),
|
|
110
198
|
...(options.signal == null ? {} : { signal: options.signal }),
|
|
199
|
+
...(sessionLogContext == null ? {} : { sessionLogContext }),
|
|
111
200
|
})
|
|
112
201
|
: undefined;
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
task: input.task,
|
|
123
|
-
selection: finalSelection,
|
|
124
|
-
contentCandidates: discovery.content_candidates,
|
|
202
|
+
const effectiveDelegateResult = delegateResult;
|
|
203
|
+
const finalSelection = effectiveDelegateResult?.selection ?? selection;
|
|
204
|
+
const coverage = evaluateSelectionCoverage({
|
|
205
|
+
policy: coveragePolicy,
|
|
206
|
+
candidates: discovery.content_candidates,
|
|
207
|
+
selected: finalSelection.selected,
|
|
208
|
+
...(discovery.review_obligations == null
|
|
209
|
+
? {}
|
|
210
|
+
: { obligations: discovery.review_obligations }),
|
|
125
211
|
});
|
|
126
|
-
|
|
127
|
-
effectiveDelegateResult != null &&
|
|
128
|
-
input.delegate.enabled) {
|
|
129
|
-
effectiveDelegateResult = handleDelegateRuntimeFailure({
|
|
130
|
-
delegate: input.delegate,
|
|
131
|
-
reason: packetQa.materialFailureReason,
|
|
132
|
-
deterministicSelection: selection,
|
|
133
|
-
omissionCount: rootResolution.omissions.length +
|
|
134
|
-
discovery.omissions.length +
|
|
135
|
-
selection.omissions.length,
|
|
136
|
-
candidateFallbackCount: selection.selected.length,
|
|
137
|
-
});
|
|
138
|
-
finalSelection = pruneTaskSpecificFinalSelection({
|
|
139
|
-
selection: effectiveDelegateResult.selection,
|
|
140
|
-
contentCandidates: discovery.content_candidates,
|
|
141
|
-
sourceRoots: rootResolution.roots,
|
|
142
|
-
budget: input.budget,
|
|
143
|
-
task: input.task,
|
|
144
|
-
});
|
|
145
|
-
packetQa = validateBenchmarkPacketQuality({
|
|
146
|
-
task: input.task,
|
|
147
|
-
selection: finalSelection,
|
|
148
|
-
contentCandidates: discovery.content_candidates,
|
|
149
|
-
});
|
|
150
|
-
}
|
|
151
|
-
const rawWarnings = [
|
|
152
|
-
...(effectiveDelegateResult?.warnings ?? []),
|
|
153
|
-
...packetQa.warnings,
|
|
154
|
-
];
|
|
212
|
+
const rawWarnings = [...(effectiveDelegateResult?.warnings ?? [])];
|
|
155
213
|
const openQuestions = effectiveDelegateResult?.openQuestions ?? [];
|
|
156
214
|
const omissions = [
|
|
157
215
|
...rootResolution.omissions,
|
|
@@ -164,13 +222,21 @@ export async function handleContextBuilder(args, options = {}) {
|
|
|
164
222
|
selection: finalSelection.metrics,
|
|
165
223
|
omissionCount: omissions.length,
|
|
166
224
|
});
|
|
167
|
-
const
|
|
225
|
+
const reconciledWarnings = reconcileContextBuilderWarnings({
|
|
168
226
|
warnings: rawWarnings,
|
|
169
227
|
selected: finalSelection.selected,
|
|
170
228
|
omissions,
|
|
171
229
|
metrics,
|
|
172
230
|
delegate: effectiveDelegateResult?.delegate,
|
|
173
231
|
});
|
|
232
|
+
// A material search cap must be explained in the receipt, not just
|
|
233
|
+
// classified: the warning carries the bounded coverage summary while the
|
|
234
|
+
// cap event (built from metrics + search_coverage in the status reducer)
|
|
235
|
+
// carries the partial classification.
|
|
236
|
+
const searchCapWarning = searchCapCoverageWarning(metrics.search_cap_hit, discovery.search_coverage);
|
|
237
|
+
const warnings = searchCapWarning == null
|
|
238
|
+
? reconciledWarnings
|
|
239
|
+
: [...reconciledWarnings, searchCapWarning];
|
|
174
240
|
const delegateMetadata = effectiveDelegateResult?.delegate == null
|
|
175
241
|
? undefined
|
|
176
242
|
: {
|
|
@@ -178,10 +244,26 @@ export async function handleContextBuilder(args, options = {}) {
|
|
|
178
244
|
selected_count: finalSelection.selected.length,
|
|
179
245
|
warning_count: warnings.length,
|
|
180
246
|
};
|
|
247
|
+
const delegatePacketEstimatedTokens = effectiveDelegateResult?.observability.delegate_packet_size ?? 0;
|
|
248
|
+
const delegatePacketCompaction = effectiveDelegateResult?.observability.delegate_packet_compaction;
|
|
249
|
+
const observability = {
|
|
250
|
+
curator_ms: effectiveDelegateResult?.observability.curator_ms ?? null,
|
|
251
|
+
finalizer_ms: effectiveDelegateResult?.observability.finalizer_ms ?? null,
|
|
252
|
+
candidate_pool_size: discovery.content_candidates.length,
|
|
253
|
+
// Deprecated alias (see ContextBuilderObservabilityMetrics): historical
|
|
254
|
+
// name for the packet ESTIMATED-TOKEN metric. Kept byte-identical for
|
|
255
|
+
// compatibility; new readers should use delegate_packet_estimated_tokens.
|
|
256
|
+
delegate_packet_size: delegatePacketEstimatedTokens,
|
|
257
|
+
delegate_packet_estimated_tokens: delegatePacketEstimatedTokens,
|
|
258
|
+
...(delegatePacketCompaction == null
|
|
259
|
+
? {}
|
|
260
|
+
: { delegate_packet_compaction: delegatePacketCompaction }),
|
|
261
|
+
};
|
|
181
262
|
const renderInput = {
|
|
182
263
|
run_id: runId,
|
|
183
264
|
created_at: createdAt,
|
|
184
265
|
task: input.task,
|
|
266
|
+
allowSecrets: input.allow_secrets,
|
|
185
267
|
roots: rootResolution.roots.map((root) => ({
|
|
186
268
|
root_index: root.root_index,
|
|
187
269
|
input_path: root.input_path,
|
|
@@ -207,245 +289,226 @@ export async function handleContextBuilder(args, options = {}) {
|
|
|
207
289
|
omissions,
|
|
208
290
|
warnings,
|
|
209
291
|
metrics,
|
|
292
|
+
search_coverage: discovery.search_coverage,
|
|
210
293
|
open_questions: openQuestions,
|
|
211
294
|
...(delegateMetadata == null ? {} : { delegate: delegateMetadata }),
|
|
295
|
+
observability,
|
|
296
|
+
coverage,
|
|
297
|
+
status_evidence: { coverage },
|
|
212
298
|
debug: input.debug,
|
|
213
299
|
};
|
|
214
|
-
await
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
300
|
+
await lifecycle?.transition("writing", {
|
|
301
|
+
...delegateLifecyclePatch(effectiveDelegateResult, lifecycle),
|
|
302
|
+
counts: lifecycleCountsFromMetrics(metrics, discovery.search.length),
|
|
303
|
+
coverage: lifecycleCoverage(coverage),
|
|
304
|
+
...(effectiveDelegateResult?.delegate.fallback_used === true
|
|
305
|
+
? {
|
|
306
|
+
fallback: {
|
|
307
|
+
status: "used",
|
|
308
|
+
reason_code: "delegate_fallback",
|
|
309
|
+
},
|
|
310
|
+
reasonCodes: ["delegate_fallback"],
|
|
311
|
+
}
|
|
312
|
+
: {}),
|
|
218
313
|
});
|
|
314
|
+
// Capture into a const so the beforeManifestCommit closure keeps the
|
|
315
|
+
// narrowed non-undefined type (the outer binding is a reassignable let).
|
|
316
|
+
const committingLifecycle = lifecycle;
|
|
317
|
+
const receipt = await withContextBuilderWriteWatchdog((signal) => writeContextBuilderFinalArtifacts(writingStorage, renderInput, {
|
|
318
|
+
signal,
|
|
319
|
+
...(writingStorage.ownerNonce == null ? {} : { ownerNonce: writingStorage.ownerNonce }),
|
|
320
|
+
...(!ownsLifecycle || committingLifecycle == null
|
|
321
|
+
? {}
|
|
322
|
+
: { beforeManifestCommit: () => committingLifecycle.transition("committing") }),
|
|
323
|
+
}), options.signal == null ? {} : { signal: options.signal });
|
|
219
324
|
committed = true;
|
|
220
|
-
|
|
221
|
-
|
|
325
|
+
if (ownsLifecycle) {
|
|
326
|
+
await recordContextRunCatalogMarker({
|
|
327
|
+
runsDir,
|
|
328
|
+
kind: "artifact",
|
|
329
|
+
id: receipt.run_id,
|
|
330
|
+
createdAt,
|
|
331
|
+
}).catch(() => {
|
|
332
|
+
logger.warn({ contextRunId: receipt.run_id }, "context run catalog marker write failed");
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
let responseReceipt = receipt;
|
|
336
|
+
if (ownsLifecycle) {
|
|
337
|
+
try {
|
|
338
|
+
await lifecycle?.complete(receipt.status, {
|
|
339
|
+
artifactRunId: receipt.run_id,
|
|
340
|
+
counts: lifecycleCountsFromMetrics(receipt.metrics, discovery.search.length),
|
|
341
|
+
coverage: lifecycleCoverage(coverage),
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
catch (error) {
|
|
345
|
+
if (!(error instanceof ContextBuilderLifecycleObserverError))
|
|
346
|
+
throw error;
|
|
347
|
+
logger.warn({ contextRunId: lifecycle?.contextRunId }, "context run lifecycle completion unavailable after retry");
|
|
348
|
+
responseReceipt = appendLifecycleCompletionWarning(receipt, internalCaps.max_warnings);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
return createContextBuilderReceiptTextResponse(responseReceipt);
|
|
222
352
|
}
|
|
223
353
|
catch (err) {
|
|
224
354
|
if (storage != null && !committed && storage.ownerNonce != null) {
|
|
225
355
|
await cleanupContextBuilderIncompleteRun(storage, storage.ownerNonce).catch(() => { });
|
|
226
356
|
}
|
|
357
|
+
if (ownsLifecycle && lifecycle != null &&
|
|
358
|
+
!(err instanceof ContextBuilderLifecycleObserverError)) {
|
|
359
|
+
const cancelled = err instanceof CancellationError || options.signal?.aborted === true;
|
|
360
|
+
const delegateFailed = input.delegate.enabled && delegateExecutionActive(lifecycle);
|
|
361
|
+
await lifecycle.fail(cancelled ? "cancelled" : "failed", {
|
|
362
|
+
...terminalExecutionPatch(lifecycle, cancelled),
|
|
363
|
+
reasonCodes: [
|
|
364
|
+
cancelled
|
|
365
|
+
? "operation_cancelled"
|
|
366
|
+
: delegateFailed
|
|
367
|
+
? "delegate_failed"
|
|
368
|
+
: "internal_failure",
|
|
369
|
+
],
|
|
370
|
+
...(!cancelled && delegateFailed
|
|
371
|
+
? {
|
|
372
|
+
fallback: {
|
|
373
|
+
status: "failed",
|
|
374
|
+
reason_code: "delegate_failed",
|
|
375
|
+
},
|
|
376
|
+
}
|
|
377
|
+
: {}),
|
|
378
|
+
});
|
|
379
|
+
}
|
|
227
380
|
throw err;
|
|
228
381
|
}
|
|
229
382
|
finally {
|
|
230
383
|
semaphore.release();
|
|
231
384
|
}
|
|
232
385
|
}
|
|
233
|
-
function
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
sourceRoots: input.sourceRoots,
|
|
240
|
-
});
|
|
241
|
-
const closureExpanded = expandSelectedClosureFilesToFullCandidates(taskSpineRepaired, input.contentCandidates, input.task, input.budget);
|
|
242
|
-
const discoveredRetained = retainDiscoveredTaskCriticalFiles(closureExpanded.selection, input.contentCandidates, input.task, input.budget);
|
|
243
|
-
const closureRepaired = dropDegenerateSelectedSlices(discoveredRetained.selection, input.contentCandidates, input.task);
|
|
244
|
-
const runtimePruned = pruneRuntimeSettingsDiscoverySearchNoise(closureRepaired.selection, input.task);
|
|
245
|
-
const debugPruned = pruneDebugTraceSearchNoise(runtimePruned, input.contentCandidates, input.budget, input.task);
|
|
246
|
-
const reviewPruned = pruneCodexRuntimeReviewSearchNoise(debugPruned, input.task);
|
|
247
|
-
return rankTaskSpecificFinalSelection(reviewPruned, input.task);
|
|
248
|
-
}
|
|
249
|
-
function validateBenchmarkPacketQuality(input) {
|
|
250
|
-
const axes = benchmarkPacketQaAxes(input.task);
|
|
251
|
-
if (axes.length === 0) {
|
|
252
|
-
return { warnings: [] };
|
|
253
|
-
}
|
|
254
|
-
const selected = input.selection.selected.filter((file) => file.content.trim().length > 0);
|
|
255
|
-
const summaryFiles = input.selection.summary?.files ?? [];
|
|
256
|
-
const gaps = [];
|
|
257
|
-
for (const axis of axes) {
|
|
258
|
-
if (selected.some((file) => axis.matches(file.path))) {
|
|
259
|
-
continue;
|
|
260
|
-
}
|
|
261
|
-
const candidatePaths = input.contentCandidates
|
|
262
|
-
.filter((candidate) => axis.matches(candidate.path))
|
|
263
|
-
.map((candidate) => candidate.path);
|
|
264
|
-
const summaryOnlyPaths = summaryFiles
|
|
265
|
-
.filter((file) => axis.matches(file.path))
|
|
266
|
-
.map((file) => file.path);
|
|
267
|
-
if (candidatePaths.length === 0 && summaryOnlyPaths.length === 0) {
|
|
268
|
-
continue;
|
|
269
|
-
}
|
|
270
|
-
gaps.push({
|
|
271
|
-
axis,
|
|
272
|
-
candidatePaths: boundContextBuilderPathList(candidatePaths),
|
|
273
|
-
summaryOnlyPaths: boundContextBuilderPathList(summaryOnlyPaths),
|
|
274
|
-
});
|
|
275
|
-
}
|
|
276
|
-
if (gaps.length === 0) {
|
|
277
|
-
return { warnings: [] };
|
|
278
|
-
}
|
|
279
|
-
const warnings = gaps.map((gap) => delegateWarning("packet_qa", packetQaGapMessage(gap.axis.label, gap.candidatePaths, gap.summaryOnlyPaths)));
|
|
280
|
-
const materialGaps = gaps.filter((gap) => gap.candidatePaths.length > 0);
|
|
281
|
-
if (materialGaps.length === 0) {
|
|
282
|
-
return { warnings };
|
|
386
|
+
function lifecycleExecutionConfig(input) {
|
|
387
|
+
if (!input.delegate.enabled) {
|
|
388
|
+
return {
|
|
389
|
+
curator: { configured: false, enabled: false },
|
|
390
|
+
finalizer: { configured: false, enabled: false },
|
|
391
|
+
};
|
|
283
392
|
}
|
|
284
393
|
return {
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
394
|
+
curator: { configured: true, enabled: true },
|
|
395
|
+
finalizer: {
|
|
396
|
+
configured: true,
|
|
397
|
+
enabled: contextBuilderFinalizerEnabled(input),
|
|
398
|
+
},
|
|
289
399
|
};
|
|
290
400
|
}
|
|
291
|
-
function
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
:
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
const
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
normalizedPath.includes("/permission-mode-selector/"));
|
|
338
|
-
},
|
|
339
|
-
});
|
|
340
|
-
}
|
|
341
|
-
if (/\b(session|orchestrat|stream|dispatch)\b/.test(normalized)) {
|
|
342
|
-
axes.push({
|
|
343
|
-
label: "runtime session and orchestration flow",
|
|
344
|
-
matches: (path) => {
|
|
345
|
-
const normalizedPath = path.toLowerCase();
|
|
346
|
-
return (normalizedPath.endsWith("/services/runtime_session_service.py") ||
|
|
347
|
-
normalizedPath.endsWith("/models/db_models/runtime_session.py") ||
|
|
348
|
-
normalizedPath.endsWith("/services/streaming/orchestrator.py") ||
|
|
349
|
-
normalizedPath.endsWith("/api/endpoints/chat.py") ||
|
|
350
|
-
normalizedPath.endsWith("/services/chat.py") ||
|
|
351
|
-
normalizedPath.endsWith("/tests/services/test_runtime_session_service.py"));
|
|
352
|
-
},
|
|
353
|
-
});
|
|
354
|
-
}
|
|
355
|
-
if (/\b(frontend|consumer|attachment|upload|ui|settings?)\b/.test(normalized)) {
|
|
356
|
-
axes.push({
|
|
357
|
-
label: "frontend Codex consumer flow",
|
|
358
|
-
matches: (path) => {
|
|
359
|
-
const normalizedPath = path.toLowerCase();
|
|
360
|
-
return (normalizedPath.endsWith("/hooks/usechatruntimesettings.ts") ||
|
|
361
|
-
normalizedPath.endsWith("/hooks/usemessageactions.ts") ||
|
|
362
|
-
normalizedPath.endsWith("/components/ui/fileuploaddialog.tsx") ||
|
|
363
|
-
normalizedPath.endsWith("/components/chat/message-input/input.tsx") ||
|
|
364
|
-
normalizedPath.endsWith("/components/chat/message-input/composerruntimecontrols.tsx") ||
|
|
365
|
-
normalizedPath.endsWith("/utils/modelprovider.ts"));
|
|
366
|
-
},
|
|
367
|
-
});
|
|
401
|
+
function lifecycleCountsFromDiscovery(discovery) {
|
|
402
|
+
return {
|
|
403
|
+
files_seen: discovery.metrics.files_seen,
|
|
404
|
+
files_considered: discovery.metrics.files_considered,
|
|
405
|
+
files_selected: 0,
|
|
406
|
+
files_omitted: discovery.omissions.length,
|
|
407
|
+
search_results: discovery.search.length,
|
|
408
|
+
bytes_read: discovery.metrics.bytes_read,
|
|
409
|
+
bytes_written: 0,
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
function lifecycleCountsFromMetrics(metrics, searchResults) {
|
|
413
|
+
return {
|
|
414
|
+
files_seen: metrics.files_seen,
|
|
415
|
+
files_considered: metrics.files_considered,
|
|
416
|
+
files_selected: metrics.files_selected,
|
|
417
|
+
files_omitted: metrics.files_omitted,
|
|
418
|
+
search_results: searchResults,
|
|
419
|
+
bytes_read: metrics.bytes_read,
|
|
420
|
+
bytes_written: metrics.bytes_written,
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
function lifecycleCoverage(coverage) {
|
|
424
|
+
const required = coverage.required_when_available.length;
|
|
425
|
+
const missing = coverage.missing_required.length;
|
|
426
|
+
return {
|
|
427
|
+
policy: coverage.policy_id,
|
|
428
|
+
status: coverage.status,
|
|
429
|
+
required,
|
|
430
|
+
selected: Math.max(0, required - missing),
|
|
431
|
+
missing,
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
function delegateLifecyclePatch(result, lifecycle) {
|
|
435
|
+
if (result == null)
|
|
436
|
+
return {};
|
|
437
|
+
if (result.delegate.fallback_used) {
|
|
438
|
+
if (lifecycle?.snapshot?.phase === "finalizing") {
|
|
439
|
+
return { curatorStatus: "succeeded", finalizerStatus: "failed" };
|
|
440
|
+
}
|
|
441
|
+
return {
|
|
442
|
+
curatorStatus: "failed",
|
|
443
|
+
...(lifecycle?.snapshot?.finalizer.effective_enabled === true
|
|
444
|
+
? { finalizerStatus: "skipped" }
|
|
445
|
+
: {}),
|
|
446
|
+
};
|
|
368
447
|
}
|
|
369
|
-
return
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
448
|
+
return {
|
|
449
|
+
curatorStatus: "succeeded",
|
|
450
|
+
...(result.delegate.finalizer_score == null
|
|
451
|
+
? {}
|
|
452
|
+
: {
|
|
453
|
+
finalizerStatus: "succeeded",
|
|
454
|
+
finalizerScore: result.delegate.finalizer_score,
|
|
455
|
+
}),
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
function terminalExecutionPatch(lifecycle, cancelled) {
|
|
459
|
+
const snapshot = lifecycle.snapshot;
|
|
460
|
+
if (snapshot == null)
|
|
461
|
+
return {};
|
|
462
|
+
const terminalStatus = cancelled ? "cancelled" : "failed";
|
|
463
|
+
return {
|
|
464
|
+
...(snapshot.curator.execution_status === "running"
|
|
465
|
+
? { curatorStatus: terminalStatus }
|
|
466
|
+
: snapshot.curator.execution_status === "pending"
|
|
467
|
+
? { curatorStatus: "skipped" }
|
|
468
|
+
: {}),
|
|
469
|
+
...(snapshot.finalizer.execution_status === "running"
|
|
470
|
+
? { finalizerStatus: terminalStatus }
|
|
471
|
+
: snapshot.finalizer.execution_status === "pending"
|
|
472
|
+
? { finalizerStatus: "skipped" }
|
|
473
|
+
: {}),
|
|
474
|
+
};
|
|
475
|
+
}
|
|
476
|
+
function delegateExecutionActive(lifecycle) {
|
|
477
|
+
const snapshot = lifecycle.snapshot;
|
|
478
|
+
return snapshot?.curator.execution_status === "running" ||
|
|
479
|
+
snapshot?.finalizer.execution_status === "running";
|
|
480
|
+
}
|
|
481
|
+
function evidenceClassesByCandidate(candidates) {
|
|
482
|
+
return new Map(candidates.map((candidate) => [
|
|
483
|
+
`${candidate.root_index}\u0000${candidate.path}`,
|
|
484
|
+
candidate.evidence_classes ?? [],
|
|
485
|
+
]));
|
|
486
|
+
}
|
|
487
|
+
function evaluateSelectionCoverage(input) {
|
|
488
|
+
const classesByCandidate = evidenceClassesByCandidate(input.candidates);
|
|
489
|
+
const selectedClasses = input.selected.flatMap((file) => classesByCandidate.get(`${file.root_index}\u0000${file.path}`) ?? []);
|
|
490
|
+
return evaluateContextCoverage({
|
|
491
|
+
policy: input.policy,
|
|
492
|
+
available: input.candidates.flatMap((candidate) => candidate.evidence_classes ?? []),
|
|
493
|
+
selected: selectedClasses,
|
|
494
|
+
unclassifiable: input.policy.id == null
|
|
495
|
+
? []
|
|
496
|
+
: ["public_entrypoint", "reverse_consumer"],
|
|
497
|
+
...(input.obligations == null ? {} : { obligations: input.obligations }),
|
|
498
|
+
selectedRanges: input.selected,
|
|
499
|
+
});
|
|
500
|
+
}
|
|
501
|
+
function delegateCoverageSelectionFailure(selected, candidates, coveragePolicy) {
|
|
502
|
+
try {
|
|
503
|
+
validateContextBuilderDelegateCoverageSelection(selected.map((file) => ({
|
|
504
|
+
candidate_id: contextBuilderCandidateId(file.root_index, file.path),
|
|
505
|
+
ranges: file.ranges,
|
|
506
|
+
})), candidates, coveragePolicy);
|
|
507
|
+
return null;
|
|
408
508
|
}
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
function runtimeSettingsDiscoveryQaAxes(task) {
|
|
412
|
-
const normalized = task.toLowerCase();
|
|
413
|
-
const axes = [
|
|
414
|
-
{
|
|
415
|
-
label: "runtime settings UI control flow",
|
|
416
|
-
matches: (path) => {
|
|
417
|
-
const normalizedPath = path.toLowerCase();
|
|
418
|
-
return (normalizedPath.endsWith("/hooks/usechatruntimesettings.ts") ||
|
|
419
|
-
normalizedPath.endsWith("/components/chat/message-input/composerruntimecontrols.tsx") ||
|
|
420
|
-
normalizedPath.endsWith("/components/chat/message-input/input.tsx") ||
|
|
421
|
-
normalizedPath.endsWith("/pages/chatpage.tsx"));
|
|
422
|
-
},
|
|
423
|
-
},
|
|
424
|
-
{
|
|
425
|
-
label: "runtime settings dispatch and persistence flow",
|
|
426
|
-
matches: (path) => {
|
|
427
|
-
const normalizedPath = path.toLowerCase();
|
|
428
|
-
return (normalizedPath.endsWith("/services/chatservice.ts") ||
|
|
429
|
-
normalizedPath.endsWith("/api/endpoints/chat.py") ||
|
|
430
|
-
normalizedPath.endsWith("/services/chat.py") ||
|
|
431
|
-
normalizedPath.endsWith("/store/messagequeuestore.ts"));
|
|
432
|
-
},
|
|
433
|
-
},
|
|
434
|
-
];
|
|
435
|
-
if (/\b(provider|session|approval|permission|runtime)\b/.test(normalized)) {
|
|
436
|
-
axes.push({
|
|
437
|
-
label: "runtime provider/session support flow",
|
|
438
|
-
matches: (path) => {
|
|
439
|
-
const normalizedPath = path.toLowerCase();
|
|
440
|
-
return (normalizedPath.endsWith("/services/runtime/codex.py") ||
|
|
441
|
-
normalizedPath.endsWith("/services/runtime/claude.py") ||
|
|
442
|
-
normalizedPath.endsWith("/services/runtime/codex_approval.py") ||
|
|
443
|
-
normalizedPath.endsWith("/services/runtime_session_service.py") ||
|
|
444
|
-
normalizedPath.endsWith("/models/db_models/runtime_session.py"));
|
|
445
|
-
},
|
|
446
|
-
});
|
|
509
|
+
catch (error) {
|
|
510
|
+
return error instanceof Error ? error.message : String(error);
|
|
447
511
|
}
|
|
448
|
-
return axes;
|
|
449
512
|
}
|
|
450
513
|
async function rejectArtifactOnlySourceRoots(inputRoots, input) {
|
|
451
514
|
if (artifactOnlyAllowedRoots.size === 0) {
|
|
@@ -467,13 +530,70 @@ async function rejectArtifactOnlySourceRoots(inputRoots, input) {
|
|
|
467
530
|
}
|
|
468
531
|
}
|
|
469
532
|
}
|
|
533
|
+
/**
|
|
534
|
+
* Fail-fast admission check: an explicitly-focused path that the caller's own
|
|
535
|
+
* (or the default) globs would prune is a contradictory request. Discovery
|
|
536
|
+
* only surfaces these later, one gate at a time, as fatal runtime omissions,
|
|
537
|
+
* so aggregate EVERY conflict up front into one InvalidParamsError before any
|
|
538
|
+
* scanning or delegate subprocess work begins.
|
|
539
|
+
*
|
|
540
|
+
* The check mirrors discovery's glob semantics exactly: the matcher is
|
|
541
|
+
* compiled from include_globs plus CONTEXT_BUILDER_DEFAULT_EXCLUDE_GLOBS +
|
|
542
|
+
* exclude_globs (discoverContextBuilderCandidates does the same), focus FILES
|
|
543
|
+
* are matched on their own root-relative path (processFile's includes/excludes
|
|
544
|
+
* gates), and focus DIRECTORIES are flagged only when the directory itself is
|
|
545
|
+
* exclude-pruned via the same `<dir>` / `<dir>/__dir__` probes traverseDirectory
|
|
546
|
+
* uses. Ancestor-directory exclusion and directory-vs-include pruning are
|
|
547
|
+
* deliberately NOT modeled here (they depend on traversal state), so anything
|
|
548
|
+
* not flagged falls through to discovery's existing runtime handling.
|
|
549
|
+
*/
|
|
550
|
+
function assertNoContextBuilderFocusGlobConflicts(input) {
|
|
551
|
+
const matcher = compileContextBuilderGlobs({
|
|
552
|
+
include: [...input.includeGlobs],
|
|
553
|
+
exclude: [
|
|
554
|
+
...CONTEXT_BUILDER_DEFAULT_EXCLUDE_GLOBS,
|
|
555
|
+
...input.excludeGlobs,
|
|
556
|
+
],
|
|
557
|
+
});
|
|
558
|
+
const conflicts = [];
|
|
559
|
+
const seen = new Set();
|
|
560
|
+
for (const focus of input.focusPaths) {
|
|
561
|
+
const relative = focus.root_relative_path;
|
|
562
|
+
if (relative === "") {
|
|
563
|
+
continue;
|
|
564
|
+
}
|
|
565
|
+
const key = `${focus.root_index}\u0000${relative}\u0000${focus.kind}`;
|
|
566
|
+
if (seen.has(key)) {
|
|
567
|
+
continue;
|
|
568
|
+
}
|
|
569
|
+
seen.add(key);
|
|
570
|
+
if (focus.kind === "directory") {
|
|
571
|
+
if (matcher.excludes(relative) ||
|
|
572
|
+
matcher.excludes(`${relative}/__dir__`)) {
|
|
573
|
+
conflicts.push(`focus directory "${relative}" (root_index ${focus.root_index}) is pruned by exclude_globs`);
|
|
574
|
+
}
|
|
575
|
+
continue;
|
|
576
|
+
}
|
|
577
|
+
if (matcher.excludes(relative)) {
|
|
578
|
+
conflicts.push(`focus path "${relative}" (root_index ${focus.root_index}) is omitted by exclude_globs`);
|
|
579
|
+
continue;
|
|
580
|
+
}
|
|
581
|
+
if (!matcher.includes(relative)) {
|
|
582
|
+
conflicts.push(`focus path "${relative}" (root_index ${focus.root_index}) is outside include_globs`);
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
if (conflicts.length > 0) {
|
|
586
|
+
throw new InvalidParamsError(`context_builder focus_paths conflict with the effective include/exclude globs (${conflicts.length} conflict${conflicts.length === 1 ? "" : "s"}): ${conflicts.join("; ")}`);
|
|
587
|
+
}
|
|
588
|
+
}
|
|
470
589
|
function getExactlyConfiguredArtifactRoots() {
|
|
471
590
|
const rawEntries = rawAllowedRootEntries();
|
|
472
591
|
const configured = new Set();
|
|
473
592
|
const cwd = path.resolve(process.cwd());
|
|
474
593
|
for (const artifactRoot of artifactOnlyAllowedRoots) {
|
|
475
594
|
for (const rawEntry of rawEntries) {
|
|
476
|
-
if (rawEntry
|
|
595
|
+
if (isArtifactOnlyAllowedRootEntry(rawEntry) &&
|
|
596
|
+
realpathSyncOrUndefined(path.resolve(cwd, stripArtifactOnlyMarker(rawEntry))) === artifactRoot) {
|
|
477
597
|
continue;
|
|
478
598
|
}
|
|
479
599
|
const resolvedEntry = realpathSyncOrUndefined(path.resolve(cwd, rawEntry));
|
|
@@ -486,37 +606,7 @@ function getExactlyConfiguredArtifactRoots() {
|
|
|
486
606
|
return configured;
|
|
487
607
|
}
|
|
488
608
|
function appendArtifactOnlyRootToEnv(runsDir) {
|
|
489
|
-
|
|
490
|
-
const cwd = path.resolve(process.cwd());
|
|
491
|
-
const entries = raw == null || raw.trim() === ""
|
|
492
|
-
? [cwd]
|
|
493
|
-
: rawAllowedRootEntries();
|
|
494
|
-
let runsDirAlreadyAllowed = false;
|
|
495
|
-
for (const entry of entries) {
|
|
496
|
-
const resolved = tryResolveRawAllowedRootEntry(entry, cwd);
|
|
497
|
-
if (resolved != null && resolved === runsDir) {
|
|
498
|
-
runsDirAlreadyAllowed = true;
|
|
499
|
-
}
|
|
500
|
-
}
|
|
501
|
-
process.env["CLINKX_ALLOWED_ROOTS"] = [
|
|
502
|
-
...entries,
|
|
503
|
-
...(runsDirAlreadyAllowed ? [] : [artifactOnlyEnvEntry(runsDir)]),
|
|
504
|
-
].join(",");
|
|
505
|
-
}
|
|
506
|
-
function tryResolveRawAllowedRootEntry(entry, cwd) {
|
|
507
|
-
try {
|
|
508
|
-
const resolved = realpathSync(path.resolve(cwd, entry));
|
|
509
|
-
if (path.parse(resolved).root === resolved) {
|
|
510
|
-
throw new InvalidParamsError(`Allowed root must not be a filesystem root: ${resolved}`);
|
|
511
|
-
}
|
|
512
|
-
return resolved;
|
|
513
|
-
}
|
|
514
|
-
catch (error) {
|
|
515
|
-
if (error instanceof InvalidParamsError) {
|
|
516
|
-
throw error;
|
|
517
|
-
}
|
|
518
|
-
return undefined;
|
|
519
|
-
}
|
|
609
|
+
appendArtifactOnlyRootsToEnv([runsDir], { requireExisting: false });
|
|
520
610
|
}
|
|
521
611
|
function rawAllowedRootEntries() {
|
|
522
612
|
const raw = process.env["CLINKX_ALLOWED_ROOTS"];
|
|
@@ -528,9 +618,6 @@ function rawAllowedRootEntries() {
|
|
|
528
618
|
.map((root) => root.trim())
|
|
529
619
|
.filter((root) => root.length > 0);
|
|
530
620
|
}
|
|
531
|
-
function artifactOnlyEnvEntry(root) {
|
|
532
|
-
return root.endsWith(path.sep) ? `${root}.` : `${root}${path.sep}.`;
|
|
533
|
-
}
|
|
534
621
|
function realpathSyncOrUndefined(input) {
|
|
535
622
|
try {
|
|
536
623
|
return realpathSync(input);
|
|
@@ -540,12 +627,7 @@ function realpathSyncOrUndefined(input) {
|
|
|
540
627
|
}
|
|
541
628
|
}
|
|
542
629
|
function getDefaultContextBuilderSemaphore() {
|
|
543
|
-
|
|
544
|
-
if (defaultSemaphore == null ||
|
|
545
|
-
defaultSemaphore.limit !== limit) {
|
|
546
|
-
defaultSemaphore = new ContextBuilderSemaphore(limit);
|
|
547
|
-
}
|
|
548
|
-
return defaultSemaphore;
|
|
630
|
+
return getSharedContextBuilderSemaphore();
|
|
549
631
|
}
|
|
550
632
|
function getDefaultAdapterRegistry() {
|
|
551
633
|
if (defaultAdapterRegistry == null) {
|
|
@@ -594,6 +676,7 @@ async function resolveDelegateSelection(input) {
|
|
|
594
676
|
const finalizerEnabled = contextBuilderFinalizerEnabled(input.input);
|
|
595
677
|
const delegatePacketInput = {
|
|
596
678
|
task: input.input.task,
|
|
679
|
+
allowSecrets: input.input.allow_secrets,
|
|
597
680
|
discovery: {
|
|
598
681
|
tree: input.discovery.tree,
|
|
599
682
|
content_candidates: input.discovery.content_candidates,
|
|
@@ -601,38 +684,78 @@ async function resolveDelegateSelection(input) {
|
|
|
601
684
|
slices: input.discovery.slices,
|
|
602
685
|
omissions: [...input.baselineOmissions, ...input.discovery.omissions],
|
|
603
686
|
metrics: input.discovery.metrics,
|
|
687
|
+
...(input.discovery.review_obligations == null
|
|
688
|
+
? {}
|
|
689
|
+
: { review_obligations: input.discovery.review_obligations }),
|
|
604
690
|
},
|
|
605
691
|
selection: input.deterministicSelection,
|
|
606
692
|
budget: input.input.budget,
|
|
607
693
|
focus_paths: input.focusPaths,
|
|
694
|
+
caller_search_terms: input.input.search_terms,
|
|
695
|
+
coveragePolicy: input.coveragePolicy,
|
|
608
696
|
...(input.delegatePacketCaps == null
|
|
609
697
|
? {}
|
|
610
698
|
: { caps: input.delegatePacketCaps }),
|
|
611
699
|
};
|
|
612
700
|
const candidateFallbackCount = input.deterministicSelection.selected.length;
|
|
701
|
+
let curatorMs = null;
|
|
702
|
+
let finalizerMs = null;
|
|
703
|
+
let delegatePacketSize = 0;
|
|
704
|
+
// Compaction counters from the last-built packet (finalizer overrides
|
|
705
|
+
// curator) and bounded T3 evidence warnings, surfaced additively.
|
|
706
|
+
let delegatePacketCompaction;
|
|
707
|
+
const packetEvidenceWarnings = [];
|
|
708
|
+
const recordPacketEvidenceWarning = (warning) => {
|
|
709
|
+
if (warning == null ||
|
|
710
|
+
packetEvidenceWarnings.some((existing) => existing.code === warning.code)) {
|
|
711
|
+
return;
|
|
712
|
+
}
|
|
713
|
+
packetEvidenceWarnings.push({
|
|
714
|
+
code: warning.code,
|
|
715
|
+
message: warning.message,
|
|
716
|
+
});
|
|
717
|
+
};
|
|
718
|
+
const currentDelegateObservability = () => ({
|
|
719
|
+
curator_ms: curatorMs,
|
|
720
|
+
finalizer_ms: finalizerMs,
|
|
721
|
+
delegate_packet_size: delegatePacketSize,
|
|
722
|
+
...(delegatePacketCompaction == null
|
|
723
|
+
? {}
|
|
724
|
+
: { delegate_packet_compaction: delegatePacketCompaction }),
|
|
725
|
+
});
|
|
726
|
+
const sessionLogOwner = (executor) => input.sessionLogContext == null
|
|
727
|
+
? undefined
|
|
728
|
+
: {
|
|
729
|
+
family: "context",
|
|
730
|
+
contextRunId: input.sessionLogContext.contextRunId,
|
|
731
|
+
contextGeneration: input.sessionLogContext.contextGeneration,
|
|
732
|
+
executor,
|
|
733
|
+
executionId: `${executor}_${input.sessionLogContext.contextRunId.slice(-32)}`,
|
|
734
|
+
attempt: 1,
|
|
735
|
+
};
|
|
613
736
|
const requestedFocusOmissions = input.discovery.omissions.filter(isRequestedEvidenceOmission);
|
|
614
737
|
if (requestedFocusOmissions.length > 0) {
|
|
615
738
|
return handleDelegateRuntimeFailure({
|
|
616
739
|
delegate,
|
|
617
|
-
reason: `requested local evidence omitted: ${requestedFocusOmissions
|
|
618
|
-
.map(formatOmissionForDelegateFailure)
|
|
619
|
-
.join("; ")}`,
|
|
740
|
+
reason: `requested local evidence omitted: ${formatOmissionsForDelegateFailure(requestedFocusOmissions)}`,
|
|
620
741
|
deterministicSelection: input.deterministicSelection,
|
|
621
742
|
omissionCount: input.baselineOmissions.length +
|
|
622
743
|
input.discovery.omissions.length +
|
|
623
744
|
input.deterministicSelection.omissions.length,
|
|
624
745
|
candidateFallbackCount,
|
|
746
|
+
observability: currentDelegateObservability(),
|
|
625
747
|
});
|
|
626
748
|
}
|
|
627
|
-
await input.
|
|
749
|
+
await input.lifecycle?.transition("curating", { curatorStatus: "running" });
|
|
628
750
|
const curatorPacket = buildContextBuilderCuratorPacket(delegatePacketInput);
|
|
751
|
+
delegatePacketSize = curatorPacket.metrics.packet_estimated_tokens;
|
|
752
|
+
delegatePacketCompaction = curatorPacket.metrics.compaction;
|
|
753
|
+
recordPacketEvidenceWarning(curatorPacket.evidence_warning);
|
|
629
754
|
const curatorPacketRequestedOmissions = curatorPacket.omissions.filter(isRequestedEvidenceOmission);
|
|
630
755
|
if (curatorPacketRequestedOmissions.length > 0) {
|
|
631
756
|
return handleDelegateRuntimeFailure({
|
|
632
757
|
delegate,
|
|
633
|
-
reason: `requested local evidence omitted: ${curatorPacketRequestedOmissions
|
|
634
|
-
.map(formatOmissionForDelegateFailure)
|
|
635
|
-
.join("; ")}`,
|
|
758
|
+
reason: `requested local evidence omitted: ${formatOmissionsForDelegateFailure(curatorPacketRequestedOmissions)}`,
|
|
636
759
|
deterministicSelection: input.deterministicSelection,
|
|
637
760
|
omissions: curatorPacketRequestedOmissions,
|
|
638
761
|
omissionCount: input.baselineOmissions.length +
|
|
@@ -640,6 +763,7 @@ async function resolveDelegateSelection(input) {
|
|
|
640
763
|
input.deterministicSelection.omissions.length +
|
|
641
764
|
curatorPacketRequestedOmissions.length,
|
|
642
765
|
candidateFallbackCount,
|
|
766
|
+
observability: currentDelegateObservability(),
|
|
643
767
|
});
|
|
644
768
|
}
|
|
645
769
|
if (curatorPacket.candidates.length === 0) {
|
|
@@ -651,8 +775,11 @@ async function resolveDelegateSelection(input) {
|
|
|
651
775
|
input.discovery.omissions.length +
|
|
652
776
|
input.deterministicSelection.omissions.length,
|
|
653
777
|
candidateFallbackCount,
|
|
778
|
+
observability: currentDelegateObservability(),
|
|
654
779
|
});
|
|
655
780
|
}
|
|
781
|
+
const curatorStartMs = Date.now();
|
|
782
|
+
const curatorSessionLogOwner = sessionLogOwner("curator");
|
|
656
783
|
const curatorCall = await runContextBuilderDelegateCall({
|
|
657
784
|
prompt: renderDelegatePrompt("curator", curatorPacket),
|
|
658
785
|
provider: delegate.curator,
|
|
@@ -660,16 +787,22 @@ async function resolveDelegateSelection(input) {
|
|
|
660
787
|
timeout_seconds: delegate.timeout_seconds,
|
|
661
788
|
max_response_chars: delegate.max_response_chars,
|
|
662
789
|
...(input.signal == null ? {} : { signal: input.signal }),
|
|
790
|
+
...(curatorSessionLogOwner == null
|
|
791
|
+
? {}
|
|
792
|
+
: { sessionLogOwner: curatorSessionLogOwner }),
|
|
663
793
|
});
|
|
794
|
+
curatorMs = Math.max(0, Date.now() - curatorStartMs);
|
|
664
795
|
if (!curatorCall.ok) {
|
|
665
796
|
return handleDelegateRuntimeFailure({
|
|
666
797
|
delegate,
|
|
667
798
|
reason: failureReasonFromCall("curator", curatorCall),
|
|
799
|
+
failureKind: curatorCall.reason,
|
|
668
800
|
deterministicSelection: input.deterministicSelection,
|
|
669
801
|
omissionCount: input.baselineOmissions.length +
|
|
670
802
|
input.discovery.omissions.length +
|
|
671
803
|
input.deterministicSelection.omissions.length,
|
|
672
804
|
candidateFallbackCount,
|
|
805
|
+
observability: currentDelegateObservability(),
|
|
673
806
|
});
|
|
674
807
|
}
|
|
675
808
|
let curatorProposal;
|
|
@@ -678,6 +811,8 @@ async function resolveDelegateSelection(input) {
|
|
|
678
811
|
const validatedCuratorProposal = validateContextBuilderCuratorOutput(curatorCall.json, {
|
|
679
812
|
candidates: curatorPacket.candidates,
|
|
680
813
|
budget: input.input.budget,
|
|
814
|
+
allowSecrets: input.input.allow_secrets,
|
|
815
|
+
coveragePolicy: curatorPacket.coverage_policy,
|
|
681
816
|
});
|
|
682
817
|
curatorProposal = augmentCuratorProposalWithDeterministicBaseline(validatedCuratorProposal, curatorPacket, input.input.budget.max_files);
|
|
683
818
|
curatorSelection = reconstructContextBuilderDelegateSelection({
|
|
@@ -727,6 +862,7 @@ async function resolveDelegateSelection(input) {
|
|
|
727
862
|
input.discovery.omissions.length +
|
|
728
863
|
input.deterministicSelection.omissions.length,
|
|
729
864
|
candidateFallbackCount,
|
|
865
|
+
observability: currentDelegateObservability(),
|
|
730
866
|
});
|
|
731
867
|
}
|
|
732
868
|
if (curatorSelection.selected.length === 0) {
|
|
@@ -738,24 +874,62 @@ async function resolveDelegateSelection(input) {
|
|
|
738
874
|
input.discovery.omissions.length +
|
|
739
875
|
input.deterministicSelection.omissions.length,
|
|
740
876
|
candidateFallbackCount,
|
|
877
|
+
observability: currentDelegateObservability(),
|
|
741
878
|
});
|
|
742
879
|
}
|
|
743
880
|
if (!finalizerEnabled) {
|
|
744
|
-
const repairedSelection =
|
|
881
|
+
const repairedSelection = repairDelegateSelection({
|
|
745
882
|
curatorSelection,
|
|
746
|
-
deterministicSelection: input.deterministicSelection,
|
|
747
883
|
contentCandidates: input.discovery.content_candidates,
|
|
748
884
|
focusPaths: input.focusPaths,
|
|
749
885
|
budget: input.input.budget,
|
|
750
|
-
|
|
751
|
-
sourceRoots: input.sourceRoots,
|
|
886
|
+
expandSelectedFiles: true,
|
|
752
887
|
});
|
|
753
888
|
const warnings = curatorProposal.warnings.map((message) => delegateWarning("delegate_curator_warning", message));
|
|
889
|
+
warnings.push(...packetEvidenceWarnings);
|
|
754
890
|
appendDelegateRepairWarnings(warnings, repairedSelection);
|
|
755
|
-
const openQuestions = curatorProposal.open_questions.map(sanitizeDiagnosticText);
|
|
891
|
+
const openQuestions = curatorProposal.open_questions.map((question) => sanitizeDiagnosticText(question));
|
|
756
892
|
const omissionCount = input.baselineOmissions.length +
|
|
757
893
|
input.discovery.omissions.length +
|
|
758
894
|
repairedSelection.selection.omissions.length;
|
|
895
|
+
const delegateEvidenceCoverageFailure = delegateCoverageSelectionFailure(restoreDroppedDegenerateReviewTargetsForValidation({
|
|
896
|
+
preRepairSelected: curatorSelection.selected,
|
|
897
|
+
postRepairSelected: repairedSelection.selection.selected,
|
|
898
|
+
candidates: curatorPacket.candidates,
|
|
899
|
+
coveragePolicy: curatorPacket.coverage_policy,
|
|
900
|
+
}), curatorPacket.candidates, curatorPacket.coverage_policy);
|
|
901
|
+
if (delegateEvidenceCoverageFailure != null) {
|
|
902
|
+
return handleDelegateRuntimeFailure({
|
|
903
|
+
delegate,
|
|
904
|
+
reason: `post-repair evidence coverage failed: ${delegateEvidenceCoverageFailure}`,
|
|
905
|
+
deterministicSelection: input.deterministicSelection,
|
|
906
|
+
omissionCount,
|
|
907
|
+
candidateFallbackCount,
|
|
908
|
+
observability: currentDelegateObservability(),
|
|
909
|
+
});
|
|
910
|
+
}
|
|
911
|
+
// Re-verify required-focus presence against the actual returned selection.
|
|
912
|
+
// The curator-only branch does not fold required-focus ranges, so it checks
|
|
913
|
+
// whole-file presence here; range coverage is re-verified only in the
|
|
914
|
+
// finalizer branch, which folds requested focus evidence first.
|
|
915
|
+
const curatorOnlyCoverageFailure = postRepairRequiredFocusEvictionFailure({
|
|
916
|
+
focusPaths: input.focusPaths,
|
|
917
|
+
candidates: curatorPacket.candidates,
|
|
918
|
+
preRepairSelected: curatorSelection.selected,
|
|
919
|
+
postRepairSelected: repairedSelection.selection.selected,
|
|
920
|
+
});
|
|
921
|
+
if (curatorOnlyCoverageFailure != null) {
|
|
922
|
+
return handleDelegateRuntimeFailure({
|
|
923
|
+
delegate,
|
|
924
|
+
reason: `post-repair required-focus coverage failed: ${curatorOnlyCoverageFailure}`,
|
|
925
|
+
deterministicSelection: input.deterministicSelection,
|
|
926
|
+
omissionCount: input.baselineOmissions.length +
|
|
927
|
+
input.discovery.omissions.length +
|
|
928
|
+
input.deterministicSelection.omissions.length,
|
|
929
|
+
candidateFallbackCount,
|
|
930
|
+
observability: currentDelegateObservability(),
|
|
931
|
+
});
|
|
932
|
+
}
|
|
759
933
|
return {
|
|
760
934
|
selection: repairedSelection.selection,
|
|
761
935
|
warnings,
|
|
@@ -772,21 +946,26 @@ async function resolveDelegateSelection(input) {
|
|
|
772
946
|
warning_count: warnings.length,
|
|
773
947
|
omission_count: omissionCount,
|
|
774
948
|
},
|
|
949
|
+
observability: currentDelegateObservability(),
|
|
775
950
|
};
|
|
776
951
|
}
|
|
777
|
-
await input.
|
|
952
|
+
await input.lifecycle?.transition("finalizing", {
|
|
953
|
+
curatorStatus: "succeeded",
|
|
954
|
+
finalizerStatus: "running",
|
|
955
|
+
});
|
|
778
956
|
const finalizerPacket = buildContextBuilderFinalizerPacket({
|
|
779
957
|
...delegatePacketInput,
|
|
780
958
|
selection: curatorSelection,
|
|
781
959
|
curator_proposal: curatorProposal,
|
|
782
960
|
});
|
|
961
|
+
delegatePacketSize = finalizerPacket.metrics.packet_estimated_tokens;
|
|
962
|
+
delegatePacketCompaction = finalizerPacket.metrics.compaction;
|
|
963
|
+
recordPacketEvidenceWarning(finalizerPacket.evidence_warning);
|
|
783
964
|
const finalizerPacketRequestedOmissions = finalizerPacket.omissions.filter(isRequestedEvidenceOmission);
|
|
784
965
|
if (finalizerPacketRequestedOmissions.length > 0) {
|
|
785
966
|
return handleDelegateRuntimeFailure({
|
|
786
967
|
delegate,
|
|
787
|
-
reason: `requested local evidence omitted: ${finalizerPacketRequestedOmissions
|
|
788
|
-
.map(formatOmissionForDelegateFailure)
|
|
789
|
-
.join("; ")}`,
|
|
968
|
+
reason: `requested local evidence omitted: ${formatOmissionsForDelegateFailure(finalizerPacketRequestedOmissions)}`,
|
|
790
969
|
deterministicSelection: input.deterministicSelection,
|
|
791
970
|
omissions: finalizerPacketRequestedOmissions,
|
|
792
971
|
omissionCount: input.baselineOmissions.length +
|
|
@@ -794,6 +973,7 @@ async function resolveDelegateSelection(input) {
|
|
|
794
973
|
input.deterministicSelection.omissions.length +
|
|
795
974
|
finalizerPacketRequestedOmissions.length,
|
|
796
975
|
candidateFallbackCount,
|
|
976
|
+
observability: currentDelegateObservability(),
|
|
797
977
|
});
|
|
798
978
|
}
|
|
799
979
|
if ((finalizerPacket.curator_proposal?.selected.length ?? 0) === 0) {
|
|
@@ -805,25 +985,36 @@ async function resolveDelegateSelection(input) {
|
|
|
805
985
|
input.discovery.omissions.length +
|
|
806
986
|
input.deterministicSelection.omissions.length,
|
|
807
987
|
candidateFallbackCount,
|
|
988
|
+
observability: currentDelegateObservability(),
|
|
808
989
|
});
|
|
809
990
|
}
|
|
991
|
+
const finalizerStartMs = Date.now();
|
|
992
|
+
const finalizerSessionLogOwner = sessionLogOwner("finalizer");
|
|
810
993
|
const finalizerCall = await runContextBuilderDelegateCall({
|
|
811
|
-
prompt: renderDelegatePrompt("finalizer", finalizerPacket
|
|
994
|
+
prompt: renderDelegatePrompt("finalizer", finalizerPacket, {
|
|
995
|
+
minScore: delegate.finalizer.min_score,
|
|
996
|
+
}),
|
|
812
997
|
provider: delegate.finalizer,
|
|
813
998
|
registry: input.registry,
|
|
814
999
|
timeout_seconds: delegate.timeout_seconds,
|
|
815
1000
|
max_response_chars: delegate.max_response_chars,
|
|
816
1001
|
...(input.signal == null ? {} : { signal: input.signal }),
|
|
1002
|
+
...(finalizerSessionLogOwner == null
|
|
1003
|
+
? {}
|
|
1004
|
+
: { sessionLogOwner: finalizerSessionLogOwner }),
|
|
817
1005
|
});
|
|
1006
|
+
finalizerMs = Math.max(0, Date.now() - finalizerStartMs);
|
|
818
1007
|
if (!finalizerCall.ok) {
|
|
819
1008
|
return handleDelegateRuntimeFailure({
|
|
820
1009
|
delegate,
|
|
821
1010
|
reason: failureReasonFromCall("finalizer", finalizerCall),
|
|
1011
|
+
failureKind: finalizerCall.reason,
|
|
822
1012
|
deterministicSelection: input.deterministicSelection,
|
|
823
1013
|
omissionCount: input.baselineOmissions.length +
|
|
824
1014
|
input.discovery.omissions.length +
|
|
825
1015
|
input.deterministicSelection.omissions.length,
|
|
826
1016
|
candidateFallbackCount,
|
|
1017
|
+
observability: currentDelegateObservability(),
|
|
827
1018
|
});
|
|
828
1019
|
}
|
|
829
1020
|
try {
|
|
@@ -831,6 +1022,8 @@ async function resolveDelegateSelection(input) {
|
|
|
831
1022
|
candidates: finalizerPacket.candidates,
|
|
832
1023
|
budget: input.input.budget,
|
|
833
1024
|
minScore: delegate.finalizer.min_score,
|
|
1025
|
+
allowSecrets: input.input.allow_secrets,
|
|
1026
|
+
coveragePolicy: finalizerPacket.coverage_policy,
|
|
834
1027
|
});
|
|
835
1028
|
if (finalizerProposal.omissions.length > 0) {
|
|
836
1029
|
return handleDelegateRuntimeFailure({
|
|
@@ -842,6 +1035,7 @@ async function resolveDelegateSelection(input) {
|
|
|
842
1035
|
input.deterministicSelection.omissions.length +
|
|
843
1036
|
finalizerProposal.omissions.length,
|
|
844
1037
|
candidateFallbackCount,
|
|
1038
|
+
observability: currentDelegateObservability(),
|
|
845
1039
|
});
|
|
846
1040
|
}
|
|
847
1041
|
const foldedFinalizerSelection = foldRequiredFocusEvidenceIntoSelection(finalizerProposal.selected, requiredFocusEvidenceSelections({
|
|
@@ -865,25 +1059,23 @@ async function resolveDelegateSelection(input) {
|
|
|
865
1059
|
input.discovery.omissions.length +
|
|
866
1060
|
input.deterministicSelection.omissions.length,
|
|
867
1061
|
candidateFallbackCount,
|
|
1062
|
+
observability: currentDelegateObservability(),
|
|
868
1063
|
});
|
|
869
1064
|
}
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
candidateFallbackCount,
|
|
881
|
-
});
|
|
882
|
-
}
|
|
1065
|
+
// A per-file reconstruction-budget overrun no longer fails the entire
|
|
1066
|
+
// delegate selection. Over-budget candidates (e.g. large generated/i18n
|
|
1067
|
+
// catalogs the finalizer over-selected) are dropped while the rest of the
|
|
1068
|
+
// curated bundle is retained. Completeness is re-checked against the
|
|
1069
|
+
// MATERIALIZED (post-budget) selection, so a dropped REQUIRED-FOCUS file
|
|
1070
|
+
// still fails closed below, while non-required oversized files are pruned
|
|
1071
|
+
// and surfaced as a warning further down rather than collapsing the bundle.
|
|
1072
|
+
// Required-focus coverage is additionally re-verified against the
|
|
1073
|
+
// post-repair selection below, so eviction/compaction during repair also
|
|
1074
|
+
// fails closed.
|
|
883
1075
|
const completenessFailure = delegateCompletenessFailure({
|
|
884
1076
|
focusPaths: input.focusPaths,
|
|
885
1077
|
candidates: finalizerPacket.candidates,
|
|
886
|
-
finalizerSelected:
|
|
1078
|
+
finalizerSelected: materializeFinalizerSelection(finalSelection.selected, finalizerPacket.candidates),
|
|
887
1079
|
curatorWarnings: curatorProposal.warnings,
|
|
888
1080
|
finalizerWarnings: finalizerProposal.warnings,
|
|
889
1081
|
});
|
|
@@ -896,17 +1088,49 @@ async function resolveDelegateSelection(input) {
|
|
|
896
1088
|
input.discovery.omissions.length +
|
|
897
1089
|
input.deterministicSelection.omissions.length,
|
|
898
1090
|
candidateFallbackCount,
|
|
1091
|
+
observability: currentDelegateObservability(),
|
|
899
1092
|
});
|
|
900
1093
|
}
|
|
901
|
-
const repairedSelection =
|
|
1094
|
+
const repairedSelection = repairDelegateSelection({
|
|
902
1095
|
curatorSelection: finalSelection,
|
|
903
|
-
deterministicSelection: input.deterministicSelection,
|
|
904
1096
|
contentCandidates: input.discovery.content_candidates,
|
|
905
1097
|
focusPaths: input.focusPaths,
|
|
906
1098
|
budget: input.input.budget,
|
|
907
|
-
|
|
908
|
-
|
|
1099
|
+
expandSelectedFiles: false,
|
|
1100
|
+
});
|
|
1101
|
+
const delegateEvidenceCoverageFailure = delegateCoverageSelectionFailure(repairedSelection.selection.selected, finalizerPacket.candidates, finalizerPacket.coverage_policy);
|
|
1102
|
+
if (delegateEvidenceCoverageFailure != null) {
|
|
1103
|
+
return handleDelegateRuntimeFailure({
|
|
1104
|
+
delegate,
|
|
1105
|
+
reason: `post-repair evidence coverage failed: ${delegateEvidenceCoverageFailure}`,
|
|
1106
|
+
deterministicSelection: input.deterministicSelection,
|
|
1107
|
+
omissionCount: input.baselineOmissions.length +
|
|
1108
|
+
input.discovery.omissions.length +
|
|
1109
|
+
repairedSelection.selection.omissions.length,
|
|
1110
|
+
candidateFallbackCount,
|
|
1111
|
+
observability: currentDelegateObservability(),
|
|
1112
|
+
});
|
|
1113
|
+
}
|
|
1114
|
+
// Re-verify required-focus completeness against the actual returned
|
|
1115
|
+
// selection. This keeps the fallback contract tied to the final materialized
|
|
1116
|
+
// packet rather than the delegate's requested selection.
|
|
1117
|
+
const postRepairCoverageFailure = requiredFocusCoverageFailure({
|
|
1118
|
+
focusPaths: input.focusPaths,
|
|
1119
|
+
candidates: finalizerPacket.candidates,
|
|
1120
|
+
finalizerSelected: materializeFinalizerSelection(repairedSelection.selection.selected, finalizerPacket.candidates),
|
|
909
1121
|
});
|
|
1122
|
+
if (postRepairCoverageFailure != null) {
|
|
1123
|
+
return handleDelegateRuntimeFailure({
|
|
1124
|
+
delegate,
|
|
1125
|
+
reason: `post-repair required-focus coverage failed: ${postRepairCoverageFailure}`,
|
|
1126
|
+
deterministicSelection: input.deterministicSelection,
|
|
1127
|
+
omissionCount: input.baselineOmissions.length +
|
|
1128
|
+
input.discovery.omissions.length +
|
|
1129
|
+
input.deterministicSelection.omissions.length,
|
|
1130
|
+
candidateFallbackCount,
|
|
1131
|
+
observability: currentDelegateObservability(),
|
|
1132
|
+
});
|
|
1133
|
+
}
|
|
910
1134
|
const warnings = [
|
|
911
1135
|
...curatorProposal.warnings.map((message) => delegateWarning("delegate_curator_warning", message)),
|
|
912
1136
|
...finalizerProposal.warnings.map((message) => delegateWarning("delegate_finalizer_warning", message)),
|
|
@@ -914,8 +1138,14 @@ async function resolveDelegateSelection(input) {
|
|
|
914
1138
|
if (foldedFinalizerSelection.foldedPaths.length > 0) {
|
|
915
1139
|
warnings.push(delegateWarning("delegate_finalizer_warning", `finalizer omitted requested local evidence; server folded it back into the selection: ${boundContextBuilderPathList(foldedFinalizerSelection.foldedPaths).join(", ")}`));
|
|
916
1140
|
}
|
|
1141
|
+
if (finalSelection.omissions.length > 0) {
|
|
1142
|
+
warnings.push(delegateWarning("delegate_finalizer_warning", `finalizer selection pruned to fit the local reconstruction budget; omitted: ${finalSelection.omissions
|
|
1143
|
+
.map(formatOmissionForDelegateFailure)
|
|
1144
|
+
.join("; ")}`));
|
|
1145
|
+
}
|
|
1146
|
+
warnings.push(...packetEvidenceWarnings);
|
|
917
1147
|
appendDelegateRepairWarnings(warnings, repairedSelection);
|
|
918
|
-
const openQuestions = curatorProposal.open_questions.map(sanitizeDiagnosticText);
|
|
1148
|
+
const openQuestions = curatorProposal.open_questions.map((question) => sanitizeDiagnosticText(question));
|
|
919
1149
|
const omissionCount = input.baselineOmissions.length +
|
|
920
1150
|
input.discovery.omissions.length +
|
|
921
1151
|
repairedSelection.selection.omissions.length;
|
|
@@ -936,6 +1166,7 @@ async function resolveDelegateSelection(input) {
|
|
|
936
1166
|
warning_count: warnings.length,
|
|
937
1167
|
omission_count: omissionCount,
|
|
938
1168
|
},
|
|
1169
|
+
observability: currentDelegateObservability(),
|
|
939
1170
|
};
|
|
940
1171
|
}
|
|
941
1172
|
catch (err) {
|
|
@@ -947,1193 +1178,74 @@ async function resolveDelegateSelection(input) {
|
|
|
947
1178
|
}
|
|
948
1179
|
return handleDelegateRuntimeFailure({
|
|
949
1180
|
delegate,
|
|
950
|
-
reason: failureReasonFromError("finalizer_validation", err, delegatePromptDiagnostic(finalizerPacket)),
|
|
1181
|
+
reason: failureReasonFromError("finalizer_validation", err, `${delegatePromptDiagnostic(finalizerPacket)}${finalizerScoreDiagnostic(finalizerCall.json, delegate.finalizer.min_score)}`),
|
|
951
1182
|
deterministicSelection: input.deterministicSelection,
|
|
952
1183
|
omissionCount: input.baselineOmissions.length +
|
|
953
1184
|
input.discovery.omissions.length +
|
|
954
1185
|
input.deterministicSelection.omissions.length,
|
|
955
1186
|
candidateFallbackCount,
|
|
1187
|
+
observability: currentDelegateObservability(),
|
|
956
1188
|
});
|
|
957
1189
|
}
|
|
958
1190
|
}
|
|
959
|
-
function
|
|
960
|
-
const
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
candidates: input.contentCandidates,
|
|
969
|
-
budget: input.budget,
|
|
970
|
-
task: input.task,
|
|
971
|
-
sourceRoots: input.sourceRoots,
|
|
972
|
-
});
|
|
973
|
-
const runtimeNoisePruned = pruneRuntimeSettingsDiscoverySearchNoise(taskSpineRepaired, input.task);
|
|
974
|
-
const debugNoisePruned = pruneDebugTraceSearchNoise(runtimeNoisePruned, input.contentCandidates, input.budget, input.task);
|
|
975
|
-
const reviewNoisePruned = pruneCodexRuntimeReviewSearchNoise(debugNoisePruned, input.task);
|
|
976
|
-
return {
|
|
977
|
-
selection: reviewNoisePruned,
|
|
978
|
-
expandedCount: focusExpanded.expandedCount + closureExpanded.expandedCount,
|
|
979
|
-
deterministicRetainedCount: deterministicRetained.retainedCount,
|
|
980
|
-
discoveredRetainedCount: discoveredRetained.retainedCount,
|
|
981
|
-
evictedCount: discoveredRetained.evictedCount,
|
|
982
|
-
droppedDegenerateCount: degenerateDropped.droppedCount + finalDegenerateDropped.droppedCount,
|
|
983
|
-
};
|
|
984
|
-
}
|
|
985
|
-
function repairTaskSpineRanges(input) {
|
|
986
|
-
const specs = taskSpineRangeSpecs(input.task);
|
|
987
|
-
if (specs.length === 0) {
|
|
988
|
-
return input.selection;
|
|
989
|
-
}
|
|
990
|
-
const candidatesByKey = new Map(input.candidates.map((candidate) => [selectedFileKey(candidate), candidate]));
|
|
991
|
-
const repairBudget = taskSpineRepairBudget(input.budget);
|
|
992
|
-
let selected = [...input.selection.selected];
|
|
993
|
-
let selectedKeys = new Set(selected.map((file) => selectedFileKey(file)));
|
|
994
|
-
let totalBytes = input.selection.metrics.bytes_selected;
|
|
995
|
-
let totalTokens = input.selection.metrics.estimated_tokens;
|
|
996
|
-
for (const spec of specs) {
|
|
997
|
-
const selectedIndex = selected.findIndex((file) => spec.path.test(file.path.toLowerCase()));
|
|
998
|
-
const existing = selectedIndex >= 0 ? selected[selectedIndex] : undefined;
|
|
999
|
-
const candidate = existing == null
|
|
1000
|
-
? candidateForTaskSpineSpec(spec, input.candidates, input.sourceRoots)
|
|
1001
|
-
: (localCandidateForTaskSpineSpec(spec, input.sourceRoots, existing.root_index) ??
|
|
1002
|
-
candidatesByKey.get(selectedFileKey(existing)) ??
|
|
1003
|
-
localCandidateFromSelectedFile(existing, input.sourceRoots));
|
|
1004
|
-
if (candidate == null) {
|
|
1005
|
-
continue;
|
|
1006
|
-
}
|
|
1007
|
-
const file = selectedFileFromCandidateRanges(candidate, spec.full
|
|
1008
|
-
? [{ start_line: 1, end_line: Math.max(1, candidate.line_count) }]
|
|
1009
|
-
: spec.ranges, existing?.source ?? "search");
|
|
1010
|
-
if (file == null) {
|
|
1011
|
-
continue;
|
|
1012
|
-
}
|
|
1013
|
-
if (existing != null) {
|
|
1014
|
-
const totalsWithoutFile = {
|
|
1015
|
-
totalBytes: totalBytes - existing.bytes,
|
|
1016
|
-
totalTokens: totalTokens - existing.estimated_tokens,
|
|
1017
|
-
};
|
|
1018
|
-
if (shouldReplaceWithTaskSpineRanges(existing.path, input.task) &&
|
|
1019
|
-
file.bytes <= repairBudget.max_bytes_per_file &&
|
|
1020
|
-
totalsWithoutFile.totalBytes + file.bytes <= repairBudget.max_total_bytes &&
|
|
1021
|
-
totalsWithoutFile.totalTokens + file.estimated_tokens <=
|
|
1022
|
-
repairBudget.target_estimated_tokens) {
|
|
1023
|
-
selected[selectedIndex] = file;
|
|
1024
|
-
totalBytes = totalsWithoutFile.totalBytes + file.bytes;
|
|
1025
|
-
totalTokens = totalsWithoutFile.totalTokens + file.estimated_tokens;
|
|
1026
|
-
continue;
|
|
1027
|
-
}
|
|
1028
|
-
const merged = selectedFileFromCandidateRanges(candidate, [...existing.ranges, ...file.ranges], existing.source);
|
|
1029
|
-
if (merged == null || merged.bytes <= existing.bytes) {
|
|
1030
|
-
continue;
|
|
1031
|
-
}
|
|
1032
|
-
if (merged.bytes > input.budget.max_bytes_per_file ||
|
|
1033
|
-
totalsWithoutFile.totalBytes + merged.bytes > input.budget.max_total_bytes ||
|
|
1034
|
-
totalsWithoutFile.totalTokens + merged.estimated_tokens >
|
|
1035
|
-
repairBudget.target_estimated_tokens) {
|
|
1036
|
-
if (shouldReplaceWithTaskSpineRanges(existing.path, input.task)) {
|
|
1037
|
-
if (file.bytes <= repairBudget.max_bytes_per_file &&
|
|
1038
|
-
totalsWithoutFile.totalBytes + file.bytes <=
|
|
1039
|
-
repairBudget.max_total_bytes &&
|
|
1040
|
-
totalsWithoutFile.totalTokens + file.estimated_tokens <=
|
|
1041
|
-
repairBudget.target_estimated_tokens) {
|
|
1042
|
-
selected[selectedIndex] = file;
|
|
1043
|
-
totalBytes = totalsWithoutFile.totalBytes + file.bytes;
|
|
1044
|
-
totalTokens = totalsWithoutFile.totalTokens + file.estimated_tokens;
|
|
1045
|
-
continue;
|
|
1046
|
-
}
|
|
1047
|
-
}
|
|
1048
|
-
const withoutExisting = selected.filter((item) => selectedFileKey(item) !== selectedFileKey(existing));
|
|
1049
|
-
const keysWithoutExisting = new Set(withoutExisting.map((item) => selectedFileKey(item)));
|
|
1050
|
-
const repaired = makeRoomForRequiredClosureFile({
|
|
1051
|
-
selected: withoutExisting,
|
|
1052
|
-
selectedKeys: keysWithoutExisting,
|
|
1053
|
-
totalBytes: totalsWithoutFile.totalBytes,
|
|
1054
|
-
totalTokens: totalsWithoutFile.totalTokens,
|
|
1055
|
-
file: shouldReplaceWithTaskSpineRanges(existing.path, input.task)
|
|
1056
|
-
? file
|
|
1057
|
-
: merged,
|
|
1058
|
-
budget: repairBudget,
|
|
1059
|
-
task: input.task,
|
|
1060
|
-
});
|
|
1061
|
-
if (repaired == null) {
|
|
1062
|
-
continue;
|
|
1063
|
-
}
|
|
1064
|
-
const repairedFile = shouldReplaceWithTaskSpineRanges(existing.path, input.task)
|
|
1065
|
-
? file
|
|
1066
|
-
: merged;
|
|
1067
|
-
selected = [...repaired.selected, repairedFile];
|
|
1068
|
-
selectedKeys = new Set(repaired.selectedKeys);
|
|
1069
|
-
selectedKeys.add(selectedFileKey(repairedFile));
|
|
1070
|
-
totalBytes = repaired.totalBytes + repairedFile.bytes;
|
|
1071
|
-
totalTokens = repaired.totalTokens + repairedFile.estimated_tokens;
|
|
1072
|
-
continue;
|
|
1073
|
-
}
|
|
1074
|
-
selected[selectedIndex] = merged;
|
|
1075
|
-
totalBytes = totalsWithoutFile.totalBytes + merged.bytes;
|
|
1076
|
-
totalTokens = totalsWithoutFile.totalTokens + merged.estimated_tokens;
|
|
1077
|
-
continue;
|
|
1078
|
-
}
|
|
1079
|
-
if (!selectionFitsBudgetWithFile(selected, totalBytes, totalTokens, file, repairBudget)) {
|
|
1080
|
-
const repaired = makeRoomForRequiredClosureFile({
|
|
1081
|
-
selected,
|
|
1082
|
-
selectedKeys,
|
|
1083
|
-
totalBytes,
|
|
1084
|
-
totalTokens,
|
|
1085
|
-
file,
|
|
1086
|
-
budget: repairBudget,
|
|
1087
|
-
task: input.task,
|
|
1088
|
-
});
|
|
1089
|
-
if (repaired == null) {
|
|
1090
|
-
continue;
|
|
1091
|
-
}
|
|
1092
|
-
selected = repaired.selected;
|
|
1093
|
-
selectedKeys = repaired.selectedKeys;
|
|
1094
|
-
totalBytes = repaired.totalBytes;
|
|
1095
|
-
totalTokens = repaired.totalTokens;
|
|
1096
|
-
}
|
|
1097
|
-
selected.push(file);
|
|
1098
|
-
selectedKeys.add(selectedFileKey(file));
|
|
1099
|
-
totalBytes += file.bytes;
|
|
1100
|
-
totalTokens += file.estimated_tokens;
|
|
1191
|
+
function restoreDroppedDegenerateReviewTargetsForValidation(input) {
|
|
1192
|
+
const targetKeys = new Set((input.coveragePolicy.obligations?.targets ?? []).flatMap((target) => {
|
|
1193
|
+
const candidate = input.candidates.find((item) => item.id === target.candidate_id);
|
|
1194
|
+
return candidate == null
|
|
1195
|
+
? []
|
|
1196
|
+
: [`${candidate.root_index}\u0000${candidate.path}`];
|
|
1197
|
+
}));
|
|
1198
|
+
if (targetKeys.size === 0) {
|
|
1199
|
+
return [...input.postRepairSelected];
|
|
1101
1200
|
}
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
};
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
const
|
|
1116
|
-
? selection
|
|
1117
|
-
:
|
|
1118
|
-
|
|
1119
|
-
}
|
|
1120
|
-
function pruneRuntimeSettingsDiscoverySearchNoise(selection, task) {
|
|
1121
|
-
if (!isRuntimeSettingsDiscoveryTask(task)) {
|
|
1122
|
-
return selection;
|
|
1123
|
-
}
|
|
1124
|
-
const selected = selection.selected.filter((file) => file.source !== "search" ||
|
|
1125
|
-
!isRuntimeSettingsDiscoverySearchNoisePath(file.path));
|
|
1126
|
-
return selected.length === selection.selected.length
|
|
1127
|
-
? selection
|
|
1128
|
-
: selectionWithSelectedFiles(selection, selected);
|
|
1129
|
-
}
|
|
1130
|
-
function pruneCodexRuntimeReviewSearchNoise(selection, task) {
|
|
1131
|
-
if (!isCodexRuntimeReviewTask(task)) {
|
|
1132
|
-
return selection;
|
|
1133
|
-
}
|
|
1134
|
-
const shouldPrunePath = (filePath) => isTangentialCodexRuntimeReviewUiPath(filePath) ||
|
|
1135
|
-
isLowValueCodexRuntimeReviewNoisePath(filePath, task);
|
|
1136
|
-
const selected = selection.selected.filter((file) => file.source === "focus" ||
|
|
1137
|
-
!shouldPrunePath(file.path));
|
|
1138
|
-
const search = selection.search.filter((record) => !shouldPrunePath(record.path));
|
|
1139
|
-
const slices = selection.slices.filter((record) => record.source === "focus" || !shouldPrunePath(record.path));
|
|
1140
|
-
const selectedKeys = new Set(selected.map(selectedFileKey));
|
|
1141
|
-
const summaryFiles = selection.summary?.files.filter((file) => selectedKeys.has(selectedFileKey(file)) || !shouldPrunePath(file.path)) ?? [];
|
|
1142
|
-
if (selected.length === selection.selected.length &&
|
|
1143
|
-
search.length === selection.search.length &&
|
|
1144
|
-
slices.length === selection.slices.length &&
|
|
1145
|
-
summaryFiles.length === (selection.summary?.files.length ?? 0)) {
|
|
1146
|
-
return selection;
|
|
1147
|
-
}
|
|
1148
|
-
const prunedSelection = selectionWithSelectedFiles(selection, selected);
|
|
1201
|
+
const postRepairKeys = new Set(input.postRepairSelected.map((file) => selectedFileKey(file)));
|
|
1202
|
+
const droppedDegenerateTargets = input.preRepairSelected.filter((file) => {
|
|
1203
|
+
const key = selectedFileKey(file);
|
|
1204
|
+
return (targetKeys.has(key) &&
|
|
1205
|
+
!postRepairKeys.has(key) &&
|
|
1206
|
+
file.content.trim().length === 0);
|
|
1207
|
+
});
|
|
1208
|
+
return [...input.postRepairSelected, ...droppedDegenerateTargets];
|
|
1209
|
+
}
|
|
1210
|
+
function repairDelegateSelection(input) {
|
|
1211
|
+
const focusExpanded = input.expandSelectedFiles
|
|
1212
|
+
? expandSelectedFocusFilesToFullCandidates(input.curatorSelection, input.contentCandidates, input.focusPaths, input.budget)
|
|
1213
|
+
: { selection: input.curatorSelection, expandedCount: 0 };
|
|
1214
|
+
const smallSlicesExpanded = input.expandSelectedFiles
|
|
1215
|
+
? expandSelectedSmallFilesToFullCandidates(focusExpanded.selection, input.contentCandidates, input.focusPaths, input.budget)
|
|
1216
|
+
: focusExpanded;
|
|
1217
|
+
const emptyDropped = dropEmptySelectedFiles(smallSlicesExpanded.selection);
|
|
1149
1218
|
return {
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
...(selection.summary == null ? {} : { summary: { files: summaryFiles } }),
|
|
1219
|
+
selection: emptyDropped.selection,
|
|
1220
|
+
expandedCount: focusExpanded.expandedCount + smallSlicesExpanded.expandedCount,
|
|
1221
|
+
droppedDegenerateCount: emptyDropped.droppedCount,
|
|
1154
1222
|
};
|
|
1155
1223
|
}
|
|
1156
|
-
function
|
|
1157
|
-
if (
|
|
1158
|
-
|
|
1159
|
-
}
|
|
1160
|
-
const indexed = selection.selected.map((file, index) => ({ file, index }));
|
|
1161
|
-
indexed.sort((left, right) => taskSpecificFinalSelectedRank(left.file.path, task) -
|
|
1162
|
-
taskSpecificFinalSelectedRank(right.file.path, task) ||
|
|
1163
|
-
left.index - right.index);
|
|
1164
|
-
const selected = indexed.map((item) => item.file);
|
|
1165
|
-
const orderChanged = selected.some((file, index) => selectedFileKey(file) !== selectedFileKey(selection.selected[index]));
|
|
1166
|
-
return orderChanged ? selectionWithSelectedFiles(selection, selected) : selection;
|
|
1167
|
-
}
|
|
1168
|
-
function taskSpecificFinalSelectedRank(path, task) {
|
|
1169
|
-
if (isRuntimeSettingsDiscoveryTask(task)) {
|
|
1170
|
-
return runtimeSettingsDiscoveryFinalRank(path);
|
|
1171
|
-
}
|
|
1172
|
-
if (isDevtoolsTraceDebugTask(task)) {
|
|
1173
|
-
return devtoolsTraceDebugFinalRank(path);
|
|
1174
|
-
}
|
|
1175
|
-
return 0;
|
|
1176
|
-
}
|
|
1177
|
-
function runtimeSettingsDiscoveryFinalRank(path) {
|
|
1178
|
-
const normalized = path.toLowerCase();
|
|
1179
|
-
const basename = normalized.split("/").at(-1) ?? normalized;
|
|
1180
|
-
if (normalized.endsWith("/hooks/usechatruntimesettings.ts")) {
|
|
1181
|
-
return 0;
|
|
1182
|
-
}
|
|
1183
|
-
if (normalized.endsWith("/components/chat/message-input/composerruntimecontrols.tsx")) {
|
|
1184
|
-
return 1;
|
|
1185
|
-
}
|
|
1186
|
-
if (normalized.endsWith("/components/chat/message-input/input.tsx")) {
|
|
1187
|
-
return 2;
|
|
1188
|
-
}
|
|
1189
|
-
if (normalized.endsWith("/pages/chatpage.tsx")) {
|
|
1190
|
-
return 3;
|
|
1191
|
-
}
|
|
1192
|
-
if (normalized.endsWith("/services/chatservice.ts") ||
|
|
1193
|
-
normalized.endsWith("/api/endpoints/chat.py") ||
|
|
1194
|
-
normalized.endsWith("/services/chat.py")) {
|
|
1195
|
-
return 4;
|
|
1196
|
-
}
|
|
1197
|
-
if (normalized.endsWith("/store/messagequeuestore.ts") ||
|
|
1198
|
-
normalized.endsWith("/store/__tests__/messagequeuestore.test.ts") ||
|
|
1199
|
-
normalized.endsWith("/services/queueservice.ts") ||
|
|
1200
|
-
normalized.endsWith("/tests/services/test_queue.py") ||
|
|
1201
|
-
normalized.endsWith("/tests/test_queue.py") ||
|
|
1202
|
-
normalized.endsWith("/tests/services/test_pending_initial_prompt_service.py")) {
|
|
1203
|
-
return 5;
|
|
1204
|
-
}
|
|
1205
|
-
if (normalized.endsWith("/services/runtime/codex.py") ||
|
|
1206
|
-
normalized.endsWith("/services/runtime/claude.py") ||
|
|
1207
|
-
normalized.endsWith("/services/runtime/codex_approval.py") ||
|
|
1208
|
-
normalized.endsWith("/services/runtime/codex_stream_processor.py") ||
|
|
1209
|
-
normalized.endsWith("/services/runtime_session_service.py") ||
|
|
1210
|
-
normalized.endsWith("/services/streaming/orchestrator.py")) {
|
|
1211
|
-
return 6;
|
|
1212
|
-
}
|
|
1213
|
-
if (normalized.endsWith("/hooks/__tests__/usechatruntimesettings.test.ts") ||
|
|
1214
|
-
normalized.endsWith("/pages/landingpage.test.tsx") ||
|
|
1215
|
-
normalized.endsWith("/tests/services/runtime/test_codex_runtime.py") ||
|
|
1216
|
-
normalized.endsWith("/tests/test_codex_runtime_data_migration.py") ||
|
|
1217
|
-
normalized.endsWith("/tests/test_codex_effort_translation.py") ||
|
|
1218
|
-
normalized.endsWith("/tests/test_effort_level.py") ||
|
|
1219
|
-
normalized.endsWith("/tests/test_permission_mode.py") ||
|
|
1220
|
-
normalized.endsWith("/tests/services/test_claude_agent.py")) {
|
|
1221
|
-
return 7;
|
|
1222
|
-
}
|
|
1223
|
-
if (basename === "effort.py" ||
|
|
1224
|
-
basename === "permission_mode.py" ||
|
|
1225
|
-
basename === "chat.types.ts" ||
|
|
1226
|
-
basename === "queue.types.ts" ||
|
|
1227
|
-
normalized.endsWith("/models/schemas/chat.py") ||
|
|
1228
|
-
normalized.endsWith("/models/db_models/chat.py") ||
|
|
1229
|
-
normalized.endsWith("/models/db_models/runtime_session.py") ||
|
|
1230
|
-
basename.includes("permissionmodeselector") ||
|
|
1231
|
-
basename.includes("thinkingmodeselector")) {
|
|
1232
|
-
return 8;
|
|
1233
|
-
}
|
|
1234
|
-
return 12;
|
|
1235
|
-
}
|
|
1236
|
-
function devtoolsTraceDebugFinalRank(path) {
|
|
1237
|
-
const normalized = path.toLowerCase();
|
|
1238
|
-
const basename = normalized.split("/").at(-1) ?? normalized;
|
|
1239
|
-
if (normalized.endsWith("/components/right-panel/rightpaneldevtools.tsx") ||
|
|
1240
|
-
basename === "rightpaneldevtools.tsx") {
|
|
1241
|
-
return 0;
|
|
1242
|
-
}
|
|
1243
|
-
if (normalized.endsWith("/components/right-panel/__tests__/right-panel-devtools.test.tsx") ||
|
|
1244
|
-
normalized.endsWith("/components/chat/__tests__/chat.autoscroll.test.tsx") ||
|
|
1245
|
-
normalized.endsWith("/store/devtoolsnavstore.ts") ||
|
|
1246
|
-
normalized.endsWith("/store/__tests__/devtoolsnavstore.test.ts")) {
|
|
1247
|
-
return 1;
|
|
1248
|
-
}
|
|
1249
|
-
if (normalized.endsWith("/components/chat/chat-window/chat.tsx") ||
|
|
1250
|
-
normalized.endsWith("/components/chat/message-bubble/messagerenderer.tsx") ||
|
|
1251
|
-
normalized.endsWith("/components/chat/tools/common/toolcard.tsx")) {
|
|
1252
|
-
return 2;
|
|
1253
|
-
}
|
|
1254
|
-
if (normalized.endsWith("/hooks/usemessagecache.ts") ||
|
|
1255
|
-
normalized.endsWith("/utils/messagecacheorder.ts") ||
|
|
1256
|
-
normalized.endsWith("/components/chat/tools/common/toolexpandcontext.ts") ||
|
|
1257
|
-
normalized.endsWith("/components/chat/tools/common/__tests__/toolexpand.test.tsx")) {
|
|
1258
|
-
return 3;
|
|
1259
|
-
}
|
|
1260
|
-
if (normalized.endsWith("/components/right-panel/devtools-helpers.ts") ||
|
|
1261
|
-
normalized.endsWith("/components/right-panel/__tests__/devtools-helpers.test.ts") ||
|
|
1262
|
-
normalized.endsWith("/services/devtoolsservice.ts") ||
|
|
1263
|
-
normalized.endsWith("/hooks/queries/usedevtoolsqueries.ts")) {
|
|
1264
|
-
return 4;
|
|
1265
|
-
}
|
|
1266
|
-
if (normalized.endsWith("/api/endpoints/devtools.py") ||
|
|
1267
|
-
normalized.endsWith("/services/session_trace_archive.py") ||
|
|
1268
|
-
normalized.endsWith("/services/codex_trace.py") ||
|
|
1269
|
-
normalized.endsWith("/models/db_models/session_trace_archive.py") ||
|
|
1270
|
-
normalized.endsWith("/models/schemas/devtools.py") ||
|
|
1271
|
-
normalized.endsWith("/types/devtools.types.ts")) {
|
|
1272
|
-
return 7;
|
|
1273
|
-
}
|
|
1274
|
-
return 10;
|
|
1275
|
-
}
|
|
1276
|
-
function isLowValueCodexRuntimeReviewNoisePath(path, task) {
|
|
1277
|
-
const normalized = path.toLowerCase();
|
|
1278
|
-
const normalizedTask = task.toLowerCase();
|
|
1279
|
-
if (normalized.endsWith("/core/config.py") &&
|
|
1280
|
-
!/\bconfig(?:uration)?\b/.test(normalizedTask)) {
|
|
1281
|
-
return true;
|
|
1224
|
+
function appendDelegateRepairWarnings(warnings, repair) {
|
|
1225
|
+
if (repair.expandedCount > 0) {
|
|
1226
|
+
warnings.push(delegateWarning("delegate_curator_warning", `server-expanded ${repair.expandedCount} selected file(s) to full context`));
|
|
1282
1227
|
}
|
|
1283
|
-
if (
|
|
1284
|
-
|
|
1285
|
-
return true;
|
|
1228
|
+
if (repair.droppedDegenerateCount > 0) {
|
|
1229
|
+
warnings.push(delegateWarning("delegate_curator_warning", `server-dropped ${repair.droppedDegenerateCount} degenerate selected slice(s)`));
|
|
1286
1230
|
}
|
|
1287
|
-
return false;
|
|
1288
1231
|
}
|
|
1289
|
-
function
|
|
1290
|
-
const
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
}
|
|
1295
|
-
|
|
1296
|
-
const normalized = task.toLowerCase();
|
|
1297
|
-
return (/\breview\b/.test(normalized) &&
|
|
1298
|
-
normalized.includes("codex") &&
|
|
1299
|
-
/\b(runtime|permission|effort|approval|session)\b/.test(normalized));
|
|
1300
|
-
}
|
|
1301
|
-
function isTangentialCodexRuntimeReviewUiPath(path) {
|
|
1302
|
-
const normalized = path.toLowerCase();
|
|
1303
|
-
const basename = normalized.split("/").at(-1) ?? normalized;
|
|
1304
|
-
return (basename === "requestaccesspage.tsx" ||
|
|
1305
|
-
basename === "providerauthmodal.tsx" ||
|
|
1306
|
-
basename === "codexauthupload.tsx" ||
|
|
1307
|
-
basename === "oauthcallbackpage.tsx" ||
|
|
1308
|
-
basename === "subscriptionusagecard.tsx" ||
|
|
1309
|
-
basename === "requestaccesspage.test.tsx" ||
|
|
1310
|
-
basename === "oauthcallbackpage.test.tsx" ||
|
|
1311
|
-
basename === "codexauthflow.test.tsx" ||
|
|
1312
|
-
basename === "useauthbootstrap.test.ts" ||
|
|
1313
|
-
basename === "providerauthservice.ratelimit.test.ts" ||
|
|
1314
|
-
normalized.endsWith("docs/ops/nano-permissions-policy.md") ||
|
|
1315
|
-
normalized.includes("/components/settings/inputs/") ||
|
|
1316
|
-
normalized.includes("/components/settings/tabs/") ||
|
|
1317
|
-
normalized.includes("/components/settings/__tests__/") ||
|
|
1318
|
-
normalized.includes("/provider-auth/") ||
|
|
1319
|
-
normalized.includes("/pages/requestaccess") ||
|
|
1320
|
-
normalized.includes("/pages/oauthcallback") ||
|
|
1321
|
-
normalized.includes("/services/checks/") ||
|
|
1322
|
-
normalized.endsWith("/hooks/usestreamcallbacks.ts") ||
|
|
1323
|
-
normalized.endsWith("/hooks/queries/querykeys.ts") ||
|
|
1324
|
-
normalized.endsWith("/vite.config.ts") ||
|
|
1325
|
-
normalized.endsWith("/api/endpoints/provider_auth.py") ||
|
|
1326
|
-
normalized.endsWith("/api/endpoints/settings.py") ||
|
|
1327
|
-
normalized.endsWith("/services/access_request.py") ||
|
|
1328
|
-
normalized.endsWith("/models/schemas/access_request.py"));
|
|
1329
|
-
}
|
|
1330
|
-
function isRuntimeSettingsDiscoverySearchNoisePath(path) {
|
|
1331
|
-
const normalized = path.toLowerCase();
|
|
1332
|
-
const basename = normalized.split("/").at(-1) ?? normalized;
|
|
1333
|
-
return (isTangentialRuntimeSettingsDiscoveryTestPath(normalized, basename) ||
|
|
1334
|
-
normalized.includes("/tests/services/nightshift/") ||
|
|
1335
|
-
normalized.includes("/tests/services/sandbox/") ||
|
|
1336
|
-
normalized.endsWith("/test_codex_sdk_smoke.py") ||
|
|
1337
|
-
normalized.endsWith("/test_codex_runtime_live.py") ||
|
|
1338
|
-
normalized.endsWith("/test_transport_absence.py") ||
|
|
1339
|
-
normalized.endsWith("/test_codex_approval_bridge.py") ||
|
|
1340
|
-
normalized.endsWith("/test_codex_stream_processor.py") ||
|
|
1341
|
-
normalized.endsWith("/test_codex_stream_processor_fixtures.py") ||
|
|
1342
|
-
normalized.endsWith("/test_codex_runtime_invariants.py"));
|
|
1343
|
-
}
|
|
1344
|
-
function expandSmallDevtoolsTraceFiles(selection, candidates, budget) {
|
|
1232
|
+
function expandSelectedFocusFilesToFullCandidates(selection, candidates, focusPaths, budget) {
|
|
1233
|
+
const focusKeys = new Set(focusPaths
|
|
1234
|
+
.filter((focus) => focus.kind === "file")
|
|
1235
|
+
.map((focus) => `${focus.root_index}\u0000${focus.root_relative_path}`));
|
|
1236
|
+
if (focusKeys.size === 0) {
|
|
1237
|
+
return { selection, expandedCount: 0 };
|
|
1238
|
+
}
|
|
1345
1239
|
const candidatesByKey = new Map(candidates.map((candidate) => [selectedFileKey(candidate), candidate]));
|
|
1346
1240
|
let totalBytes = selection.metrics.bytes_selected;
|
|
1347
1241
|
let totalTokens = selection.metrics.estimated_tokens;
|
|
1348
1242
|
let expandedCount = 0;
|
|
1349
1243
|
const selected = selection.selected.map((file) => {
|
|
1350
|
-
|
|
1244
|
+
const key = selectedFileKey(file);
|
|
1245
|
+
if (!focusKeys.has(key)) {
|
|
1351
1246
|
return file;
|
|
1352
1247
|
}
|
|
1353
|
-
|
|
1354
|
-
if (candidate == null || fileCoversCandidate(file, candidate)) {
|
|
1355
|
-
return file;
|
|
1356
|
-
}
|
|
1357
|
-
const fullFile = selectedFileFromCandidate(candidate);
|
|
1358
|
-
const totalsWithoutFile = {
|
|
1359
|
-
totalBytes: totalBytes - file.bytes,
|
|
1360
|
-
totalTokens: totalTokens - file.estimated_tokens,
|
|
1361
|
-
};
|
|
1362
|
-
if (fullFile.estimated_tokens > 8_000 ||
|
|
1363
|
-
fullFile.bytes > budget.max_bytes_per_file ||
|
|
1364
|
-
totalsWithoutFile.totalBytes + fullFile.bytes > budget.max_total_bytes ||
|
|
1365
|
-
totalsWithoutFile.totalTokens + fullFile.estimated_tokens >
|
|
1366
|
-
budget.target_estimated_tokens) {
|
|
1367
|
-
return file;
|
|
1368
|
-
}
|
|
1369
|
-
totalBytes = totalsWithoutFile.totalBytes + fullFile.bytes;
|
|
1370
|
-
totalTokens = totalsWithoutFile.totalTokens + fullFile.estimated_tokens;
|
|
1371
|
-
expandedCount += 1;
|
|
1372
|
-
return { ...fullFile, source: file.source };
|
|
1373
|
-
});
|
|
1374
|
-
if (expandedCount === 0) {
|
|
1375
|
-
return selection;
|
|
1376
|
-
}
|
|
1377
|
-
return selectionWithSelectedFiles(selection, selected);
|
|
1378
|
-
}
|
|
1379
|
-
function taskSpineRangeSpecs(task) {
|
|
1380
|
-
const normalized = task.toLowerCase();
|
|
1381
|
-
const specs = [];
|
|
1382
|
-
if ((/\bdiscovery\b/.test(normalized) && normalized.includes("runtime")) ||
|
|
1383
|
-
isRuntimeSettingsPersistenceFlowTask(normalized)) {
|
|
1384
|
-
specs.push({
|
|
1385
|
-
path: /\/pages\/chatpage\.tsx$/,
|
|
1386
|
-
exactPaths: ["frontend/src/pages/ChatPage.tsx"],
|
|
1387
|
-
ranges: [
|
|
1388
|
-
{ start_line: 150, end_line: 292 },
|
|
1389
|
-
{ start_line: 640, end_line: 700 },
|
|
1390
|
-
],
|
|
1391
|
-
}, {
|
|
1392
|
-
path: /\/components\/chat\/message-input\/input\.tsx$/,
|
|
1393
|
-
exactPaths: ["frontend/src/components/chat/message-input/Input.tsx"],
|
|
1394
|
-
ranges: [
|
|
1395
|
-
{ start_line: 360, end_line: 630 },
|
|
1396
|
-
{ start_line: 1018, end_line: 1048 },
|
|
1397
|
-
],
|
|
1398
|
-
}, {
|
|
1399
|
-
path: /\/hooks\/usechatruntimesettings\.ts$/,
|
|
1400
|
-
exactPaths: ["frontend/src/hooks/useChatRuntimeSettings.ts"],
|
|
1401
|
-
ranges: [{ start_line: 1, end_line: Number.MAX_SAFE_INTEGER }],
|
|
1402
|
-
full: true,
|
|
1403
|
-
}, {
|
|
1404
|
-
path: /\/services\/chatservice\.ts$/,
|
|
1405
|
-
exactPaths: ["frontend/src/services/chatService.ts"],
|
|
1406
|
-
ranges: [{ start_line: 1, end_line: Number.MAX_SAFE_INTEGER }],
|
|
1407
|
-
full: true,
|
|
1408
|
-
}, {
|
|
1409
|
-
path: /\/services\/chat\.py$/,
|
|
1410
|
-
exactPaths: ["backend/app/services/chat.py"],
|
|
1411
|
-
ranges: [
|
|
1412
|
-
{ start_line: 160, end_line: 675 },
|
|
1413
|
-
{ start_line: 1033, end_line: 1163 },
|
|
1414
|
-
{ start_line: 1619, end_line: 1756 },
|
|
1415
|
-
{ start_line: 1758, end_line: 2179 },
|
|
1416
|
-
{ start_line: 2446, end_line: 2552 },
|
|
1417
|
-
{ start_line: 2563, end_line: 2610 },
|
|
1418
|
-
],
|
|
1419
|
-
}, {
|
|
1420
|
-
path: /\/api\/endpoints\/chat\.py$/,
|
|
1421
|
-
exactPaths: ["backend/app/api/endpoints/chat.py"],
|
|
1422
|
-
ranges: [
|
|
1423
|
-
{ start_line: 396, end_line: 468 },
|
|
1424
|
-
{ start_line: 528, end_line: 558 },
|
|
1425
|
-
{ start_line: 1298, end_line: 1383 },
|
|
1426
|
-
],
|
|
1427
|
-
}, {
|
|
1428
|
-
path: /\/store\/__tests__\/messagequeuestore\.test\.ts$/,
|
|
1429
|
-
exactPaths: ["frontend/src/store/__tests__/messageQueueStore.test.ts"],
|
|
1430
|
-
ranges: [
|
|
1431
|
-
{ start_line: 1, end_line: 121 },
|
|
1432
|
-
{ start_line: 123, end_line: 241 },
|
|
1433
|
-
{ start_line: 243, end_line: 320 },
|
|
1434
|
-
],
|
|
1435
|
-
}, {
|
|
1436
|
-
path: /\/store\/messagequeuestore\.ts$/,
|
|
1437
|
-
exactPaths: ["frontend/src/store/messageQueueStore.ts"],
|
|
1438
|
-
ranges: [
|
|
1439
|
-
{ start_line: 120, end_line: 155 },
|
|
1440
|
-
{ start_line: 208, end_line: 360 },
|
|
1441
|
-
{ start_line: 640, end_line: 760 },
|
|
1442
|
-
],
|
|
1443
|
-
}, {
|
|
1444
|
-
path: /\/tests\/.*test_pending_initial_prompt_service\.py$/,
|
|
1445
|
-
exactPaths: ["backend/tests/services/test_pending_initial_prompt_service.py"],
|
|
1446
|
-
ranges: [
|
|
1447
|
-
{ start_line: 1, end_line: 230 },
|
|
1448
|
-
{ start_line: 531, end_line: 592 },
|
|
1449
|
-
{ start_line: 1150, end_line: 1242 },
|
|
1450
|
-
{ start_line: 1245, end_line: 1400 },
|
|
1451
|
-
],
|
|
1452
|
-
}, {
|
|
1453
|
-
path: /\/services\/claude_agent\.py$/,
|
|
1454
|
-
exactPaths: ["backend/app/services/claude_agent.py"],
|
|
1455
|
-
ranges: [
|
|
1456
|
-
{ start_line: 44, end_line: 80 },
|
|
1457
|
-
{ start_line: 433, end_line: 466 },
|
|
1458
|
-
{ start_line: 786, end_line: 874 },
|
|
1459
|
-
{ start_line: 988, end_line: 1002 },
|
|
1460
|
-
],
|
|
1461
|
-
}, {
|
|
1462
|
-
path: /\/services\/runtime\/claude\.py$/,
|
|
1463
|
-
exactPaths: ["backend/app/services/runtime/claude.py"],
|
|
1464
|
-
ranges: [{ start_line: 1, end_line: Number.MAX_SAFE_INTEGER }],
|
|
1465
|
-
full: true,
|
|
1466
|
-
}, {
|
|
1467
|
-
path: /\/services\/runtime\/codex_approval\.py$/,
|
|
1468
|
-
exactPaths: ["backend/app/services/runtime/codex_approval.py"],
|
|
1469
|
-
ranges: [
|
|
1470
|
-
{ start_line: 1, end_line: 135 },
|
|
1471
|
-
{ start_line: 193, end_line: 280 },
|
|
1472
|
-
{ start_line: 310, end_line: 386 },
|
|
1473
|
-
],
|
|
1474
|
-
}, {
|
|
1475
|
-
path: /\/services\/runtime\/codex\.py$/,
|
|
1476
|
-
exactPaths: ["backend/app/services/runtime/codex.py"],
|
|
1477
|
-
ranges: [
|
|
1478
|
-
{ start_line: 1, end_line: 82 },
|
|
1479
|
-
{ start_line: 87, end_line: 264 },
|
|
1480
|
-
{ start_line: 576, end_line: 621 },
|
|
1481
|
-
],
|
|
1482
|
-
}, {
|
|
1483
|
-
path: /\/services\/runtime_session_service\.py$/,
|
|
1484
|
-
exactPaths: ["backend/app/services/runtime_session_service.py"],
|
|
1485
|
-
ranges: [{ start_line: 1, end_line: Number.MAX_SAFE_INTEGER }],
|
|
1486
|
-
full: true,
|
|
1487
|
-
}, {
|
|
1488
|
-
path: /\/models\/db_models\/runtime_session\.py$/,
|
|
1489
|
-
exactPaths: ["backend/app/models/db_models/runtime_session.py"],
|
|
1490
|
-
ranges: [{ start_line: 1, end_line: Number.MAX_SAFE_INTEGER }],
|
|
1491
|
-
full: true,
|
|
1492
|
-
}, {
|
|
1493
|
-
path: /\/services\/permission_manager\.py$/,
|
|
1494
|
-
exactPaths: ["backend/app/services/permission_manager.py"],
|
|
1495
|
-
ranges: [
|
|
1496
|
-
{ start_line: 1, end_line: 160 },
|
|
1497
|
-
{ start_line: 220, end_line: 360 },
|
|
1498
|
-
],
|
|
1499
|
-
}, {
|
|
1500
|
-
path: /\/services\/runtime\/codex_stream_processor\.py$/,
|
|
1501
|
-
exactPaths: ["backend/app/services/runtime/codex_stream_processor.py"],
|
|
1502
|
-
ranges: [
|
|
1503
|
-
{ start_line: 94, end_line: 123 },
|
|
1504
|
-
{ start_line: 155, end_line: 227 },
|
|
1505
|
-
{ start_line: 263, end_line: 328 },
|
|
1506
|
-
],
|
|
1507
|
-
}, {
|
|
1508
|
-
path: /\/hooks\/__tests__\/usechatruntimesettings\.test\.ts$/,
|
|
1509
|
-
exactPaths: ["frontend/src/hooks/__tests__/useChatRuntimeSettings.test.ts"],
|
|
1510
|
-
ranges: [{ start_line: 1, end_line: Number.MAX_SAFE_INTEGER }],
|
|
1511
|
-
full: true,
|
|
1512
|
-
}, {
|
|
1513
|
-
path: /\/tests\/services\/test_claude_agent\.py$/,
|
|
1514
|
-
exactPaths: ["backend/tests/services/test_claude_agent.py"],
|
|
1515
|
-
ranges: [
|
|
1516
|
-
{ start_line: 247, end_line: 325 },
|
|
1517
|
-
{ start_line: 325, end_line: 381 },
|
|
1518
|
-
{ start_line: 628, end_line: 705 },
|
|
1519
|
-
],
|
|
1520
|
-
}, {
|
|
1521
|
-
path: /\/models\/schemas\/chat\.py$/,
|
|
1522
|
-
exactPaths: ["backend/app/models/schemas/chat.py"],
|
|
1523
|
-
ranges: [{ start_line: 1, end_line: Number.MAX_SAFE_INTEGER }],
|
|
1524
|
-
full: true,
|
|
1525
|
-
}, {
|
|
1526
|
-
path: /\/types\/chat\.types\.ts$/,
|
|
1527
|
-
exactPaths: ["frontend/src/types/chat.types.ts"],
|
|
1528
|
-
ranges: [{ start_line: 1, end_line: Number.MAX_SAFE_INTEGER }],
|
|
1529
|
-
full: true,
|
|
1530
|
-
}, {
|
|
1531
|
-
path: /\/types\/queue\.types\.ts$/,
|
|
1532
|
-
exactPaths: ["frontend/src/types/queue.types.ts"],
|
|
1533
|
-
ranges: [{ start_line: 1, end_line: Number.MAX_SAFE_INTEGER }],
|
|
1534
|
-
full: true,
|
|
1535
|
-
}, {
|
|
1536
|
-
path: /\/services\/streamservice\.ts$/,
|
|
1537
|
-
exactPaths: ["frontend/src/services/streamService.ts"],
|
|
1538
|
-
ranges: [
|
|
1539
|
-
{ start_line: 1, end_line: 140 },
|
|
1540
|
-
{ start_line: 260, end_line: 422 },
|
|
1541
|
-
],
|
|
1542
|
-
}, {
|
|
1543
|
-
path: /\/tests\/test_queue\.py$/,
|
|
1544
|
-
exactPaths: ["backend/tests/test_queue.py"],
|
|
1545
|
-
ranges: [
|
|
1546
|
-
{ start_line: 1, end_line: 120 },
|
|
1547
|
-
{ start_line: 430, end_line: 632 },
|
|
1548
|
-
],
|
|
1549
|
-
}, {
|
|
1550
|
-
path: /\/tests\/services\/test_queue\.py$/,
|
|
1551
|
-
exactPaths: ["backend/tests/services/test_queue.py"],
|
|
1552
|
-
ranges: [{ start_line: 1, end_line: Number.MAX_SAFE_INTEGER }],
|
|
1553
|
-
full: true,
|
|
1554
|
-
}, {
|
|
1555
|
-
path: /\/tests\/test_effort_level\.py$/,
|
|
1556
|
-
exactPaths: ["backend/tests/test_effort_level.py"],
|
|
1557
|
-
ranges: [{ start_line: 1, end_line: Number.MAX_SAFE_INTEGER }],
|
|
1558
|
-
full: true,
|
|
1559
|
-
}, {
|
|
1560
|
-
path: /\/tests\/test_permission_mode\.py$/,
|
|
1561
|
-
exactPaths: ["backend/tests/test_permission_mode.py"],
|
|
1562
|
-
ranges: [{ start_line: 1, end_line: Number.MAX_SAFE_INTEGER }],
|
|
1563
|
-
full: true,
|
|
1564
|
-
}, {
|
|
1565
|
-
path: /\/tests\/services\/runtime\/test_codex_runtime\.py$/,
|
|
1566
|
-
exactPaths: ["backend/tests/services/runtime/test_codex_runtime.py"],
|
|
1567
|
-
ranges: [
|
|
1568
|
-
{ start_line: 1, end_line: 160 },
|
|
1569
|
-
{ start_line: 540, end_line: 776 },
|
|
1570
|
-
],
|
|
1571
|
-
}, {
|
|
1572
|
-
path: /\/tests\/test_codex_effort_translation\.py$/,
|
|
1573
|
-
exactPaths: ["backend/tests/test_codex_effort_translation.py"],
|
|
1574
|
-
ranges: [{ start_line: 1, end_line: Number.MAX_SAFE_INTEGER }],
|
|
1575
|
-
full: true,
|
|
1576
|
-
}, {
|
|
1577
|
-
path: /\/hooks\/usepermissionrequest\.ts$/,
|
|
1578
|
-
exactPaths: ["frontend/src/hooks/usePermissionRequest.ts"],
|
|
1579
|
-
ranges: [{ start_line: 1, end_line: Number.MAX_SAFE_INTEGER }],
|
|
1580
|
-
full: true,
|
|
1581
|
-
}, {
|
|
1582
|
-
path: /\/hooks\/queries\/usesettingsqueries\.ts$/,
|
|
1583
|
-
exactPaths: ["frontend/src/hooks/queries/useSettingsQueries.ts"],
|
|
1584
|
-
ranges: [{ start_line: 1, end_line: Number.MAX_SAFE_INTEGER }],
|
|
1585
|
-
full: true,
|
|
1586
|
-
});
|
|
1587
|
-
}
|
|
1588
|
-
if (/\breview\b/.test(normalized) && normalized.includes("codex")) {
|
|
1589
|
-
specs.push({
|
|
1590
|
-
path: /\/services\/runtime\/codex\.py$/,
|
|
1591
|
-
exactPaths: ["backend/app/services/runtime/codex.py"],
|
|
1592
|
-
ranges: [
|
|
1593
|
-
{ start_line: 130, end_line: 178 },
|
|
1594
|
-
{ start_line: 212, end_line: 392 },
|
|
1595
|
-
{ start_line: 394, end_line: 476 },
|
|
1596
|
-
{ start_line: 576, end_line: 621 },
|
|
1597
|
-
{ start_line: 698, end_line: 766 },
|
|
1598
|
-
],
|
|
1599
|
-
}, {
|
|
1600
|
-
path: /\/services\/runtime\/codex_stream_processor\.py$/,
|
|
1601
|
-
exactPaths: ["backend/app/services/runtime/codex_stream_processor.py"],
|
|
1602
|
-
ranges: [
|
|
1603
|
-
{ start_line: 94, end_line: 123 },
|
|
1604
|
-
{ start_line: 155, end_line: 227 },
|
|
1605
|
-
{ start_line: 263, end_line: 328 },
|
|
1606
|
-
],
|
|
1607
|
-
}, {
|
|
1608
|
-
path: /\/services\/runtime\/base\.py$/,
|
|
1609
|
-
exactPaths: ["backend/app/services/runtime/base.py"],
|
|
1610
|
-
ranges: [{ start_line: 1, end_line: Number.MAX_SAFE_INTEGER }],
|
|
1611
|
-
full: true,
|
|
1612
|
-
}, {
|
|
1613
|
-
path: /\/services\/runtime\/claude\.py$/,
|
|
1614
|
-
exactPaths: ["backend/app/services/runtime/claude.py"],
|
|
1615
|
-
ranges: [{ start_line: 1, end_line: Number.MAX_SAFE_INTEGER }],
|
|
1616
|
-
full: true,
|
|
1617
|
-
}, {
|
|
1618
|
-
path: /\/services\/runtime\/codex_approval\.py$/,
|
|
1619
|
-
exactPaths: ["backend/app/services/runtime/codex_approval.py"],
|
|
1620
|
-
ranges: [{ start_line: 1, end_line: Number.MAX_SAFE_INTEGER }],
|
|
1621
|
-
full: true,
|
|
1622
|
-
}, {
|
|
1623
|
-
path: /\/services\/runtime\/resolver\.py$/,
|
|
1624
|
-
exactPaths: ["backend/app/services/runtime/resolver.py"],
|
|
1625
|
-
ranges: [{ start_line: 1, end_line: Number.MAX_SAFE_INTEGER }],
|
|
1626
|
-
full: true,
|
|
1627
|
-
}, {
|
|
1628
|
-
path: /\/services\/runtime_session_service\.py$/,
|
|
1629
|
-
exactPaths: ["backend/app/services/runtime_session_service.py"],
|
|
1630
|
-
ranges: [{ start_line: 1, end_line: Number.MAX_SAFE_INTEGER }],
|
|
1631
|
-
full: true,
|
|
1632
|
-
}, {
|
|
1633
|
-
path: /\/services\/streaming\/orchestrator\.py$/,
|
|
1634
|
-
exactPaths: ["backend/app/services/streaming/orchestrator.py"],
|
|
1635
|
-
ranges: [
|
|
1636
|
-
{ start_line: 979, end_line: 1173 },
|
|
1637
|
-
{ start_line: 1207, end_line: 1245 },
|
|
1638
|
-
],
|
|
1639
|
-
}, {
|
|
1640
|
-
path: /\/services\/sandbox\/provider\.py$/,
|
|
1641
|
-
exactPaths: ["backend/app/services/sandbox/provider.py"],
|
|
1642
|
-
ranges: [
|
|
1643
|
-
{ start_line: 1, end_line: 260 },
|
|
1644
|
-
{ start_line: 760, end_line: 920 },
|
|
1645
|
-
],
|
|
1646
|
-
}, {
|
|
1647
|
-
path: /\/services\/streaming\/context_usage\.py$/,
|
|
1648
|
-
exactPaths: ["backend/app/services/streaming/context_usage.py"],
|
|
1649
|
-
ranges: [{ start_line: 1, end_line: Number.MAX_SAFE_INTEGER }],
|
|
1650
|
-
full: true,
|
|
1651
|
-
}, {
|
|
1652
|
-
path: /\/services\/providerauthservice\.ts$/,
|
|
1653
|
-
exactPaths: ["frontend/src/services/providerAuthService.ts"],
|
|
1654
|
-
ranges: [{ start_line: 1, end_line: Number.MAX_SAFE_INTEGER }],
|
|
1655
|
-
full: true,
|
|
1656
|
-
}, {
|
|
1657
|
-
path: /\/services\/streamservice\.ts$/,
|
|
1658
|
-
exactPaths: ["frontend/src/services/streamService.ts"],
|
|
1659
|
-
ranges: [
|
|
1660
|
-
{ start_line: 1, end_line: 140 },
|
|
1661
|
-
{ start_line: 260, end_line: 422 },
|
|
1662
|
-
],
|
|
1663
|
-
}, {
|
|
1664
|
-
path: /\/hooks\/usechatruntimesettings\.ts$/,
|
|
1665
|
-
exactPaths: ["frontend/src/hooks/useChatRuntimeSettings.ts"],
|
|
1666
|
-
ranges: [{ start_line: 1, end_line: Number.MAX_SAFE_INTEGER }],
|
|
1667
|
-
full: true,
|
|
1668
|
-
}, {
|
|
1669
|
-
path: /\/services\/permission_manager\.py$/,
|
|
1670
|
-
exactPaths: ["backend/app/services/permission_manager.py"],
|
|
1671
|
-
ranges: [{ start_line: 1, end_line: Number.MAX_SAFE_INTEGER }],
|
|
1672
|
-
full: true,
|
|
1673
|
-
}, {
|
|
1674
|
-
path: /\/services\/model_provider_auth\.py$/,
|
|
1675
|
-
exactPaths: ["backend/app/services/model_provider_auth.py"],
|
|
1676
|
-
ranges: [{ start_line: 1, end_line: Number.MAX_SAFE_INTEGER }],
|
|
1677
|
-
full: true,
|
|
1678
|
-
}, {
|
|
1679
|
-
path: /\/tests\/services\/test_model_provider_auth\.py$/,
|
|
1680
|
-
exactPaths: ["backend/tests/services/test_model_provider_auth.py"],
|
|
1681
|
-
ranges: [{ start_line: 1, end_line: Number.MAX_SAFE_INTEGER }],
|
|
1682
|
-
full: true,
|
|
1683
|
-
}, {
|
|
1684
|
-
path: /\/tests\/services\/runtime\/test_codex_stream_processor\.py$/,
|
|
1685
|
-
exactPaths: ["backend/tests/services/runtime/test_codex_stream_processor.py"],
|
|
1686
|
-
ranges: [{ start_line: 1, end_line: Number.MAX_SAFE_INTEGER }],
|
|
1687
|
-
full: true,
|
|
1688
|
-
}, {
|
|
1689
|
-
path: /\/tests\/services\/runtime\/test_codex_approval_bridge\.py$/,
|
|
1690
|
-
exactPaths: ["backend/tests/services/runtime/test_codex_approval_bridge.py"],
|
|
1691
|
-
ranges: [
|
|
1692
|
-
{ start_line: 1, end_line: 80 },
|
|
1693
|
-
{ start_line: 193, end_line: 280 },
|
|
1694
|
-
{ start_line: 310, end_line: 386 },
|
|
1695
|
-
{ start_line: 449, end_line: 565 },
|
|
1696
|
-
{ start_line: 953, end_line: 1068 },
|
|
1697
|
-
{ start_line: 1100, end_line: 1165 },
|
|
1698
|
-
],
|
|
1699
|
-
}, {
|
|
1700
|
-
path: /\/tests\/services\/runtime\/test_resolver\.py$/,
|
|
1701
|
-
exactPaths: ["backend/tests/services/runtime/test_resolver.py"],
|
|
1702
|
-
ranges: [{ start_line: 1, end_line: Number.MAX_SAFE_INTEGER }],
|
|
1703
|
-
full: true,
|
|
1704
|
-
}, {
|
|
1705
|
-
path: /\/tests\/services\/test_runtime_session_service\.py$/,
|
|
1706
|
-
exactPaths: ["backend/tests/services/test_runtime_session_service.py"],
|
|
1707
|
-
ranges: [{ start_line: 1, end_line: Number.MAX_SAFE_INTEGER }],
|
|
1708
|
-
full: true,
|
|
1709
|
-
}, {
|
|
1710
|
-
path: /\/api\/endpoints\/chat\.py$/,
|
|
1711
|
-
exactPaths: ["backend/app/api/endpoints/chat.py"],
|
|
1712
|
-
ranges: [
|
|
1713
|
-
{ start_line: 1, end_line: 120 },
|
|
1714
|
-
{ start_line: 390, end_line: 570 },
|
|
1715
|
-
{ start_line: 1215, end_line: 1400 },
|
|
1716
|
-
],
|
|
1717
|
-
}, {
|
|
1718
|
-
path: /\/api\/endpoints\/permissions\.py$/,
|
|
1719
|
-
exactPaths: ["backend/app/api/endpoints/permissions.py"],
|
|
1720
|
-
ranges: [{ start_line: 1, end_line: Number.MAX_SAFE_INTEGER }],
|
|
1721
|
-
full: true,
|
|
1722
|
-
}, {
|
|
1723
|
-
path: /\/services\/chat\.py$/,
|
|
1724
|
-
exactPaths: ["backend/app/services/chat.py"],
|
|
1725
|
-
ranges: [
|
|
1726
|
-
{ start_line: 160, end_line: 675 },
|
|
1727
|
-
{ start_line: 777, end_line: 851 },
|
|
1728
|
-
{ start_line: 1131, end_line: 1177 },
|
|
1729
|
-
{ start_line: 1619, end_line: 1756 },
|
|
1730
|
-
{ start_line: 1878, end_line: 2170 },
|
|
1731
|
-
{ start_line: 2454, end_line: 2508 },
|
|
1732
|
-
{ start_line: 2570, end_line: 2594 },
|
|
1733
|
-
],
|
|
1734
|
-
}, {
|
|
1735
|
-
path: /\/services\/claude_agent\.py$/,
|
|
1736
|
-
exactPaths: ["backend/app/services/claude_agent.py"],
|
|
1737
|
-
ranges: [
|
|
1738
|
-
{ start_line: 44, end_line: 80 },
|
|
1739
|
-
{ start_line: 433, end_line: 466 },
|
|
1740
|
-
{ start_line: 786, end_line: 874 },
|
|
1741
|
-
{ start_line: 988, end_line: 1002 },
|
|
1742
|
-
],
|
|
1743
|
-
}, {
|
|
1744
|
-
path: /\/components\/chat\/message-input\/input\.tsx$/,
|
|
1745
|
-
exactPaths: ["frontend/src/components/chat/message-input/Input.tsx"],
|
|
1746
|
-
ranges: [
|
|
1747
|
-
{ start_line: 360, end_line: 630 },
|
|
1748
|
-
{ start_line: 1018, end_line: 1048 },
|
|
1749
|
-
],
|
|
1750
|
-
}, {
|
|
1751
|
-
path: /\/components\/chat\/message-input\/composerruntimecontrols\.tsx$/,
|
|
1752
|
-
exactPaths: ["frontend/src/components/chat/message-input/ComposerRuntimeControls.tsx"],
|
|
1753
|
-
ranges: [{ start_line: 1, end_line: Number.MAX_SAFE_INTEGER }],
|
|
1754
|
-
full: true,
|
|
1755
|
-
}, {
|
|
1756
|
-
path: /\/hooks\/usemessageactions\.ts$/,
|
|
1757
|
-
exactPaths: ["frontend/src/hooks/useMessageActions.ts"],
|
|
1758
|
-
ranges: [{ start_line: 1, end_line: Number.MAX_SAFE_INTEGER }],
|
|
1759
|
-
full: true,
|
|
1760
|
-
}, {
|
|
1761
|
-
path: /\/components\/ui\/fileuploaddialog\.tsx$/,
|
|
1762
|
-
exactPaths: ["frontend/src/components/ui/FileUploadDialog.tsx"],
|
|
1763
|
-
ranges: [{ start_line: 1, end_line: Number.MAX_SAFE_INTEGER }],
|
|
1764
|
-
full: true,
|
|
1765
|
-
}, {
|
|
1766
|
-
path: /\/components\/ui\/fileuploaddialog\.codex\.test\.tsx$/,
|
|
1767
|
-
exactPaths: ["frontend/src/components/ui/FileUploadDialog.codex.test.tsx"],
|
|
1768
|
-
ranges: [{ start_line: 1, end_line: Number.MAX_SAFE_INTEGER }],
|
|
1769
|
-
full: true,
|
|
1770
|
-
}, {
|
|
1771
|
-
path: /\/hooks\/usepermissionrequest\.ts$/,
|
|
1772
|
-
exactPaths: ["frontend/src/hooks/usePermissionRequest.ts"],
|
|
1773
|
-
ranges: [{ start_line: 1, end_line: Number.MAX_SAFE_INTEGER }],
|
|
1774
|
-
full: true,
|
|
1775
|
-
}, {
|
|
1776
|
-
path: /\/utils\/modelprovider\.ts$/,
|
|
1777
|
-
exactPaths: ["frontend/src/utils/modelProvider.ts"],
|
|
1778
|
-
ranges: [{ start_line: 1, end_line: Number.MAX_SAFE_INTEGER }],
|
|
1779
|
-
full: true,
|
|
1780
|
-
}, {
|
|
1781
|
-
path: /\/components\/chat\/message-input\/inputsettingspopover\.tsx$/,
|
|
1782
|
-
exactPaths: ["frontend/src/components/chat/message-input/InputSettingsPopover.tsx"],
|
|
1783
|
-
ranges: [{ start_line: 1, end_line: Number.MAX_SAFE_INTEGER }],
|
|
1784
|
-
full: true,
|
|
1785
|
-
}, {
|
|
1786
|
-
path: /\/permissionmodeselector\.tsx$/,
|
|
1787
|
-
exactPaths: ["frontend/src/components/chat/permission-mode-selector/PermissionModeSelector.tsx"],
|
|
1788
|
-
ranges: [{ start_line: 1, end_line: Number.MAX_SAFE_INTEGER }],
|
|
1789
|
-
full: true,
|
|
1790
|
-
}, {
|
|
1791
|
-
path: /\/thinkingmodeselector\.tsx$/,
|
|
1792
|
-
exactPaths: ["frontend/src/components/chat/thinking-mode-selector/ThinkingModeSelector.tsx"],
|
|
1793
|
-
ranges: [{ start_line: 1, end_line: Number.MAX_SAFE_INTEGER }],
|
|
1794
|
-
full: true,
|
|
1795
|
-
}, {
|
|
1796
|
-
path: /\/models\/schemas\/chat\.py$/,
|
|
1797
|
-
exactPaths: ["backend/app/models/schemas/chat.py"],
|
|
1798
|
-
ranges: [{ start_line: 1, end_line: Number.MAX_SAFE_INTEGER }],
|
|
1799
|
-
full: true,
|
|
1800
|
-
}, {
|
|
1801
|
-
path: /\/models\/schemas\/permission_mode\.py$/,
|
|
1802
|
-
exactPaths: ["backend/app/models/schemas/permission_mode.py"],
|
|
1803
|
-
ranges: [{ start_line: 1, end_line: Number.MAX_SAFE_INTEGER }],
|
|
1804
|
-
full: true,
|
|
1805
|
-
}, {
|
|
1806
|
-
path: /\/models\/schemas\/permissions\.py$/,
|
|
1807
|
-
exactPaths: ["backend/app/models/schemas/permissions.py"],
|
|
1808
|
-
ranges: [{ start_line: 1, end_line: Number.MAX_SAFE_INTEGER }],
|
|
1809
|
-
full: true,
|
|
1810
|
-
}, {
|
|
1811
|
-
path: /\/models\/db_models\/ai_model\.py$/,
|
|
1812
|
-
exactPaths: ["backend/app/models/db_models/ai_model.py"],
|
|
1813
|
-
ranges: [{ start_line: 1, end_line: Number.MAX_SAFE_INTEGER }],
|
|
1814
|
-
full: true,
|
|
1815
|
-
}, {
|
|
1816
|
-
path: /\/api\/endpoints\/ai_models\.py$/,
|
|
1817
|
-
exactPaths: ["backend/app/api/endpoints/ai_models.py"],
|
|
1818
|
-
ranges: [{ start_line: 1, end_line: Number.MAX_SAFE_INTEGER }],
|
|
1819
|
-
full: true,
|
|
1820
|
-
}, {
|
|
1821
|
-
path: /\/tests\/test_ai_models_codex_gate\.py$/,
|
|
1822
|
-
exactPaths: ["backend/tests/test_ai_models_codex_gate.py"],
|
|
1823
|
-
ranges: [{ start_line: 1, end_line: Number.MAX_SAFE_INTEGER }],
|
|
1824
|
-
full: true,
|
|
1825
|
-
}, {
|
|
1826
|
-
path: /\/tests\/test_permissions\.py$/,
|
|
1827
|
-
exactPaths: ["backend/tests/test_permissions.py"],
|
|
1828
|
-
ranges: [
|
|
1829
|
-
{ start_line: 1, end_line: 230 },
|
|
1830
|
-
{ start_line: 306, end_line: 389 },
|
|
1831
|
-
{ start_line: 489, end_line: 555 },
|
|
1832
|
-
],
|
|
1833
|
-
});
|
|
1834
|
-
}
|
|
1835
|
-
if (isDevtoolsTraceDebugTask(task)) {
|
|
1836
|
-
specs.push({
|
|
1837
|
-
path: /\/right-panel\/rightpaneldevtools\.tsx$/,
|
|
1838
|
-
exactPaths: ["frontend/src/components/right-panel/RightPanelDevtools.tsx"],
|
|
1839
|
-
ranges: [
|
|
1840
|
-
{ start_line: 1, end_line: 115 },
|
|
1841
|
-
{ start_line: 528, end_line: 745 },
|
|
1842
|
-
{ start_line: 840, end_line: 948 },
|
|
1843
|
-
],
|
|
1844
|
-
}, {
|
|
1845
|
-
path: /\/right-panel\/__tests__\/right-panel-devtools\.test\.tsx$/,
|
|
1846
|
-
exactPaths: ["frontend/src/components/right-panel/__tests__/right-panel-devtools.test.tsx"],
|
|
1847
|
-
ranges: [{ start_line: 1, end_line: Number.MAX_SAFE_INTEGER }],
|
|
1848
|
-
full: true,
|
|
1849
|
-
}, {
|
|
1850
|
-
path: /\/right-panel\/devtools-helpers\.ts$/,
|
|
1851
|
-
exactPaths: ["frontend/src/components/right-panel/devtools-helpers.ts"],
|
|
1852
|
-
ranges: [{ start_line: 1, end_line: Number.MAX_SAFE_INTEGER }],
|
|
1853
|
-
full: true,
|
|
1854
|
-
}, {
|
|
1855
|
-
path: /\/store\/devtoolsnavstore\.ts$/,
|
|
1856
|
-
exactPaths: ["frontend/src/store/devtoolsNavStore.ts"],
|
|
1857
|
-
ranges: [{ start_line: 1, end_line: Number.MAX_SAFE_INTEGER }],
|
|
1858
|
-
full: true,
|
|
1859
|
-
}, {
|
|
1860
|
-
path: /\/services\/devtoolsservice\.ts$/,
|
|
1861
|
-
exactPaths: ["frontend/src/services/devtoolsService.ts"],
|
|
1862
|
-
ranges: [{ start_line: 1, end_line: Number.MAX_SAFE_INTEGER }],
|
|
1863
|
-
full: true,
|
|
1864
|
-
}, {
|
|
1865
|
-
path: /\/hooks\/queries\/usedevtoolsqueries\.ts$/,
|
|
1866
|
-
exactPaths: ["frontend/src/hooks/queries/useDevtoolsQueries.ts"],
|
|
1867
|
-
ranges: [{ start_line: 1, end_line: Number.MAX_SAFE_INTEGER }],
|
|
1868
|
-
full: true,
|
|
1869
|
-
}, {
|
|
1870
|
-
path: /\/components\/chat\/chat-window\/chat\.tsx$/,
|
|
1871
|
-
exactPaths: ["frontend/src/components/chat/chat-window/Chat.tsx"],
|
|
1872
|
-
ranges: [
|
|
1873
|
-
{ start_line: 450, end_line: 615 },
|
|
1874
|
-
{ start_line: 670, end_line: 750 },
|
|
1875
|
-
],
|
|
1876
|
-
}, {
|
|
1877
|
-
path: /\/components\/chat\/message-bubble\/message\.tsx$/,
|
|
1878
|
-
exactPaths: ["frontend/src/components/chat/message-bubble/Message.tsx"],
|
|
1879
|
-
ranges: [{ start_line: 1, end_line: Number.MAX_SAFE_INTEGER }],
|
|
1880
|
-
full: true,
|
|
1881
|
-
}, {
|
|
1882
|
-
path: /\/pages\/chatpage\.tsx$/,
|
|
1883
|
-
exactPaths: ["frontend/src/pages/ChatPage.tsx"],
|
|
1884
|
-
ranges: [
|
|
1885
|
-
{ start_line: 150, end_line: 292 },
|
|
1886
|
-
{ start_line: 640, end_line: 700 },
|
|
1887
|
-
],
|
|
1888
|
-
}, {
|
|
1889
|
-
path: /\/components\/chat\/message-input\/input\.tsx$/,
|
|
1890
|
-
exactPaths: ["frontend/src/components/chat/message-input/Input.tsx"],
|
|
1891
|
-
ranges: [
|
|
1892
|
-
{ start_line: 360, end_line: 630 },
|
|
1893
|
-
{ start_line: 1018, end_line: 1048 },
|
|
1894
|
-
],
|
|
1895
|
-
}, {
|
|
1896
|
-
path: /\/api\/endpoints\/chat\.py$/,
|
|
1897
|
-
exactPaths: ["backend/app/api/endpoints/chat.py"],
|
|
1898
|
-
ranges: [
|
|
1899
|
-
{ start_line: 390, end_line: 570 },
|
|
1900
|
-
{ start_line: 1215, end_line: 1400 },
|
|
1901
|
-
],
|
|
1902
|
-
}, {
|
|
1903
|
-
path: /\/services\/claude_agent\.py$/,
|
|
1904
|
-
exactPaths: ["backend/app/services/claude_agent.py"],
|
|
1905
|
-
ranges: [
|
|
1906
|
-
{ start_line: 44, end_line: 80 },
|
|
1907
|
-
{ start_line: 433, end_line: 466 },
|
|
1908
|
-
{ start_line: 786, end_line: 874 },
|
|
1909
|
-
{ start_line: 988, end_line: 1002 },
|
|
1910
|
-
],
|
|
1911
|
-
}, {
|
|
1912
|
-
path: /\/store\/__tests__\/devtoolsnavstore\.test\.ts$/,
|
|
1913
|
-
exactPaths: ["frontend/src/store/__tests__/devtoolsNavStore.test.ts"],
|
|
1914
|
-
ranges: [{ start_line: 1, end_line: Number.MAX_SAFE_INTEGER }],
|
|
1915
|
-
full: true,
|
|
1916
|
-
}, {
|
|
1917
|
-
path: /\/models\/schemas\/devtools\.py$/,
|
|
1918
|
-
exactPaths: ["backend/app/models/schemas/devtools.py"],
|
|
1919
|
-
ranges: [{ start_line: 1, end_line: Number.MAX_SAFE_INTEGER }],
|
|
1920
|
-
full: true,
|
|
1921
|
-
});
|
|
1922
|
-
}
|
|
1923
|
-
return specs;
|
|
1924
|
-
}
|
|
1925
|
-
function isRuntimeSettingsPersistenceFlowTask(normalizedTask) {
|
|
1926
|
-
return (normalizedTask.includes("runtime") &&
|
|
1927
|
-
/\bsettings?\b/.test(normalizedTask) &&
|
|
1928
|
-
/\b(validat(?:e|es|ed|ing|ion)|dropp?ed|dropp?ing|drops?|persist(?:s|ed|ing|ence)?|dispatch(?:es|ed|ing)?)\b/.test(normalizedTask));
|
|
1929
|
-
}
|
|
1930
|
-
function isTangentialRuntimeSettingsDiscoveryTestPath(normalizedPath, basename) {
|
|
1931
|
-
return (basename === "test_codex_e2e_live.py" ||
|
|
1932
|
-
basename === "test_codex_sdk_smoke.py" ||
|
|
1933
|
-
basename === "test_codex_runtime_live.py" ||
|
|
1934
|
-
basename === "test_transport_absence.py" ||
|
|
1935
|
-
basename === "test_resolver.py" ||
|
|
1936
|
-
normalizedPath.includes("nightshift") ||
|
|
1937
|
-
/\/tests?\/.*codex.*approval.*\.(py|ts|tsx)$/.test(normalizedPath));
|
|
1938
|
-
}
|
|
1939
|
-
function candidateForTaskSpineSpec(spec, candidates, sourceRoots) {
|
|
1940
|
-
const local = localCandidateForTaskSpineSpec(spec, sourceRoots);
|
|
1941
|
-
if (local != null) {
|
|
1942
|
-
return local;
|
|
1943
|
-
}
|
|
1944
|
-
const discovered = candidates.find((item) => spec.path.test(item.path.toLowerCase()));
|
|
1945
|
-
if (discovered != null) {
|
|
1946
|
-
return discovered;
|
|
1947
|
-
}
|
|
1948
|
-
return undefined;
|
|
1949
|
-
}
|
|
1950
|
-
function localCandidateForTaskSpineSpec(spec, sourceRoots, preferredRootIndex) {
|
|
1951
|
-
for (const exactPath of spec.exactPaths ?? []) {
|
|
1952
|
-
const local = localCandidateFromExactPath(exactPath, sourceRoots, preferredRootIndex);
|
|
1953
|
-
if (local != null) {
|
|
1954
|
-
return local;
|
|
1955
|
-
}
|
|
1956
|
-
}
|
|
1957
|
-
return undefined;
|
|
1958
|
-
}
|
|
1959
|
-
function localCandidateFromSelectedFile(file, sourceRoots) {
|
|
1960
|
-
return localCandidateFromExactPath(file.path, sourceRoots, file.root_index);
|
|
1961
|
-
}
|
|
1962
|
-
function localCandidateFromExactPath(relativePath, sourceRoots, preferredRootIndex) {
|
|
1963
|
-
const rootIndexes = preferredRootIndex == null
|
|
1964
|
-
? sourceRoots.map((_, index) => index)
|
|
1965
|
-
: [preferredRootIndex];
|
|
1966
|
-
for (const rootIndex of rootIndexes) {
|
|
1967
|
-
const root = sourceRoots[rootIndex];
|
|
1968
|
-
if (root == null) {
|
|
1969
|
-
continue;
|
|
1970
|
-
}
|
|
1971
|
-
const absolutePath = path.join(root.path, relativePath);
|
|
1972
|
-
try {
|
|
1973
|
-
const content = readFileSync(absolutePath, "utf8");
|
|
1974
|
-
return {
|
|
1975
|
-
root_index: rootIndex,
|
|
1976
|
-
path: relativePath,
|
|
1977
|
-
absolute_path: absolutePath,
|
|
1978
|
-
content,
|
|
1979
|
-
byte_length: Buffer.byteLength(content, "utf8"),
|
|
1980
|
-
line_count: splitTextLines(content).length,
|
|
1981
|
-
has_final_newline: content.endsWith("\n"),
|
|
1982
|
-
};
|
|
1983
|
-
}
|
|
1984
|
-
catch {
|
|
1985
|
-
continue;
|
|
1986
|
-
}
|
|
1987
|
-
}
|
|
1988
|
-
return undefined;
|
|
1989
|
-
}
|
|
1990
|
-
function shouldReplaceWithTaskSpineRanges(path, task) {
|
|
1991
|
-
const normalized = path.toLowerCase();
|
|
1992
|
-
const normalizedTask = task.toLowerCase();
|
|
1993
|
-
if (/\bdiscovery\b/.test(normalizedTask) &&
|
|
1994
|
-
normalizedTask.includes("runtime") &&
|
|
1995
|
-
(normalized.endsWith("/services/chat.py") ||
|
|
1996
|
-
normalized.endsWith("/services/claude_agent.py"))) {
|
|
1997
|
-
return true;
|
|
1998
|
-
}
|
|
1999
|
-
if (isDevtoolsTraceDebugTask(task) &&
|
|
2000
|
-
(normalized.endsWith("/components/right-panel/rightpaneldevtools.tsx") ||
|
|
2001
|
-
normalized.endsWith("/components/chat/chat-window/chat.tsx") ||
|
|
2002
|
-
normalized.endsWith("/pages/chatpage.tsx") ||
|
|
2003
|
-
normalized.endsWith("/components/chat/message-input/input.tsx") ||
|
|
2004
|
-
normalized.endsWith("/api/endpoints/chat.py") ||
|
|
2005
|
-
normalized.endsWith("/services/claude_agent.py"))) {
|
|
2006
|
-
return true;
|
|
2007
|
-
}
|
|
2008
|
-
if (isCodexRuntimeReviewTask(task) &&
|
|
2009
|
-
(normalized.endsWith("/services/runtime/codex.py") ||
|
|
2010
|
-
normalized.endsWith("/services/runtime/codex_approval.py") ||
|
|
2011
|
-
normalized.endsWith("/services/runtime/codex_stream_processor.py") ||
|
|
2012
|
-
normalized.endsWith("/services/streaming/orchestrator.py") ||
|
|
2013
|
-
normalized.endsWith("/services/chat.py") ||
|
|
2014
|
-
normalized.endsWith("/tests/services/runtime/test_codex_approval_bridge.py") ||
|
|
2015
|
-
normalized.endsWith("/tests/test_permissions.py") ||
|
|
2016
|
-
normalized.endsWith("/services/claude_agent.py"))) {
|
|
2017
|
-
return true;
|
|
2018
|
-
}
|
|
2019
|
-
return false;
|
|
2020
|
-
}
|
|
2021
|
-
function selectedFileFromCandidateRanges(candidate, ranges, source) {
|
|
2022
|
-
const taskSpineContent = readTaskSpineCandidateContent(candidate);
|
|
2023
|
-
const sourceLines = splitTextLines(taskSpineContent);
|
|
2024
|
-
if (sourceLines.length === 0) {
|
|
2025
|
-
return null;
|
|
2026
|
-
}
|
|
2027
|
-
const lineCount = sourceLines.length;
|
|
2028
|
-
const bounded = mergeLineRanges(ranges
|
|
2029
|
-
.map((range) => ({
|
|
2030
|
-
start_line: Math.max(1, range.start_line),
|
|
2031
|
-
end_line: Math.min(Math.max(1, lineCount), range.end_line),
|
|
2032
|
-
}))
|
|
2033
|
-
.filter((range) => range.start_line <= range.end_line));
|
|
2034
|
-
if (bounded.length === 0) {
|
|
2035
|
-
return null;
|
|
2036
|
-
}
|
|
2037
|
-
const content = bounded
|
|
2038
|
-
.flatMap((range) => sourceLines.slice(range.start_line - 1, range.end_line))
|
|
2039
|
-
.join("\n");
|
|
2040
|
-
const bytes = Buffer.byteLength(content, "utf8");
|
|
2041
|
-
if (bytes <= 0) {
|
|
2042
|
-
return null;
|
|
2043
|
-
}
|
|
2044
|
-
return {
|
|
2045
|
-
root_index: candidate.root_index,
|
|
2046
|
-
path: candidate.path,
|
|
2047
|
-
ranges: bounded,
|
|
2048
|
-
content,
|
|
2049
|
-
bytes,
|
|
2050
|
-
estimated_tokens: estimateContextBuilderTokensFromUtf8Bytes(bytes),
|
|
2051
|
-
source,
|
|
2052
|
-
};
|
|
2053
|
-
}
|
|
2054
|
-
function readTaskSpineCandidateContent(candidate) {
|
|
2055
|
-
try {
|
|
2056
|
-
return readFileSync(candidate.absolute_path, "utf8");
|
|
2057
|
-
}
|
|
2058
|
-
catch {
|
|
2059
|
-
return candidate.content;
|
|
2060
|
-
}
|
|
2061
|
-
}
|
|
2062
|
-
function appendDelegateRepairWarnings(warnings, repair) {
|
|
2063
|
-
if (repair.expandedCount > 0) {
|
|
2064
|
-
warnings.push(delegateWarning("delegate_curator_warning", `server-expanded ${repair.expandedCount} selected file(s) to full context`));
|
|
2065
|
-
}
|
|
2066
|
-
if (repair.droppedDegenerateCount > 0) {
|
|
2067
|
-
warnings.push(delegateWarning("delegate_curator_warning", `server-dropped ${repair.droppedDegenerateCount} degenerate selected slice(s)`));
|
|
2068
|
-
}
|
|
2069
|
-
if (repair.deterministicRetainedCount > 0) {
|
|
2070
|
-
warnings.push(delegateWarning("delegate_curator_warning", `server-retained ${repair.deterministicRetainedCount} compact deterministic closure file(s) after curator underselection`));
|
|
2071
|
-
}
|
|
2072
|
-
if (repair.discoveredRetainedCount > 0) {
|
|
2073
|
-
warnings.push(delegateWarning("delegate_curator_warning", `server-retained ${repair.discoveredRetainedCount} task-critical discovered closure file(s) after curator underselection`));
|
|
2074
|
-
}
|
|
2075
|
-
if (repair.evictedCount > 0) {
|
|
2076
|
-
warnings.push(delegateWarning("delegate_curator_warning", `server-evicted ${repair.evictedCount} lower-priority selected file(s) to retain required closure`));
|
|
2077
|
-
}
|
|
2078
|
-
}
|
|
2079
|
-
function retainDeterministicClosureFiles(selection, deterministicSelection, budget, task) {
|
|
2080
|
-
const selectedKeys = new Set(selection.selected.map((file) => selectedFileKey(file)));
|
|
2081
|
-
let totalBytes = selection.metrics.bytes_selected;
|
|
2082
|
-
let totalTokens = selection.metrics.estimated_tokens;
|
|
2083
|
-
const selected = [...selection.selected];
|
|
2084
|
-
let retainedCount = 0;
|
|
2085
|
-
for (const file of deterministicSelection.selected) {
|
|
2086
|
-
if (selectedKeys.has(selectedFileKey(file)) ||
|
|
2087
|
-
!isRetainableClosureFile(file, task)) {
|
|
2088
|
-
continue;
|
|
2089
|
-
}
|
|
2090
|
-
if (selected.length >= budget.max_files ||
|
|
2091
|
-
file.bytes > budget.max_bytes_per_file ||
|
|
2092
|
-
totalBytes + file.bytes > budget.max_total_bytes ||
|
|
2093
|
-
totalTokens + file.estimated_tokens > budget.target_estimated_tokens) {
|
|
2094
|
-
continue;
|
|
2095
|
-
}
|
|
2096
|
-
selected.push(file);
|
|
2097
|
-
selectedKeys.add(selectedFileKey(file));
|
|
2098
|
-
totalBytes += file.bytes;
|
|
2099
|
-
totalTokens += file.estimated_tokens;
|
|
2100
|
-
retainedCount += 1;
|
|
2101
|
-
}
|
|
2102
|
-
if (retainedCount === 0) {
|
|
2103
|
-
return { selection, retainedCount };
|
|
2104
|
-
}
|
|
2105
|
-
return {
|
|
2106
|
-
retainedCount,
|
|
2107
|
-
selection: {
|
|
2108
|
-
...selection,
|
|
2109
|
-
selected,
|
|
2110
|
-
metrics: {
|
|
2111
|
-
files_selected: selected.length,
|
|
2112
|
-
files_omitted: selection.metrics.files_omitted,
|
|
2113
|
-
bytes_selected: totalBytes,
|
|
2114
|
-
estimated_tokens: totalTokens,
|
|
2115
|
-
truncated: selection.metrics.truncated,
|
|
2116
|
-
},
|
|
2117
|
-
},
|
|
2118
|
-
};
|
|
2119
|
-
}
|
|
2120
|
-
function expandSelectedFocusFilesToFullCandidates(selection, candidates, focusPaths, budget, task = "") {
|
|
2121
|
-
if (!shouldExpandFocusFilesForTask(task)) {
|
|
2122
|
-
return { selection, expandedCount: 0 };
|
|
2123
|
-
}
|
|
2124
|
-
const focusKeys = new Set(focusPaths
|
|
2125
|
-
.filter((focus) => focus.kind === "file")
|
|
2126
|
-
.map((focus) => `${focus.root_index}\u0000${focus.root_relative_path}`));
|
|
2127
|
-
if (focusKeys.size === 0) {
|
|
2128
|
-
return { selection, expandedCount: 0 };
|
|
2129
|
-
}
|
|
2130
|
-
const candidatesByKey = new Map(candidates.map((candidate) => [selectedFileKey(candidate), candidate]));
|
|
2131
|
-
let totalBytes = selection.metrics.bytes_selected;
|
|
2132
|
-
let totalTokens = selection.metrics.estimated_tokens;
|
|
2133
|
-
let expandedCount = 0;
|
|
2134
|
-
const selected = selection.selected.map((file) => {
|
|
2135
|
-
const key = selectedFileKey(file);
|
|
2136
|
-
if (!focusKeys.has(key)) {
|
|
1248
|
+
if (file.ranges.length !== 1) {
|
|
2137
1249
|
return file;
|
|
2138
1250
|
}
|
|
2139
1251
|
const candidate = candidatesByKey.get(key);
|
|
@@ -2144,10 +1256,6 @@ function expandSelectedFocusFilesToFullCandidates(selection, candidates, focusPa
|
|
|
2144
1256
|
if (fullFile.bytes <= file.bytes || fileCoversCandidate(file, candidate)) {
|
|
2145
1257
|
return file;
|
|
2146
1258
|
}
|
|
2147
|
-
if (fullFile.estimated_tokens >
|
|
2148
|
-
focusExpansionTokenLimit(candidate.path, task, budget)) {
|
|
2149
|
-
return file;
|
|
2150
|
-
}
|
|
2151
1259
|
const totalsWithoutFile = {
|
|
2152
1260
|
totalBytes: totalBytes - file.bytes,
|
|
2153
1261
|
totalTokens: totalTokens - file.estimated_tokens,
|
|
@@ -2181,41 +1289,30 @@ function expandSelectedFocusFilesToFullCandidates(selection, candidates, focusPa
|
|
|
2181
1289
|
},
|
|
2182
1290
|
};
|
|
2183
1291
|
}
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
}
|
|
2187
|
-
function focusExpansionTokenLimit(path, task, budget) {
|
|
2188
|
-
const normalized = path.toLowerCase();
|
|
2189
|
-
const normalizedTask = task.toLowerCase();
|
|
2190
|
-
if (/\b(debug|debugging|diagnose|diagnosis|diagnostic)\b/.test(normalizedTask) &&
|
|
2191
|
-
normalized.endsWith("/rightpaneldevtools.tsx")) {
|
|
2192
|
-
return 35_000;
|
|
2193
|
-
}
|
|
2194
|
-
if (/\b(review|discovery|provider|runtime|codex|claude)\b/.test(normalizedTask) &&
|
|
2195
|
-
normalized.includes("/services/runtime/")) {
|
|
2196
|
-
return Math.min(18_000, Math.floor(budget.target_estimated_tokens * 0.2));
|
|
2197
|
-
}
|
|
2198
|
-
return Math.min(12_000, Math.floor(budget.target_estimated_tokens * 0.18));
|
|
2199
|
-
}
|
|
2200
|
-
function expandSelectedClosureFilesToFullCandidates(selection, candidates, task, budget) {
|
|
1292
|
+
const SMALL_SELECTED_SLICE_FULL_EXPANSION_MAX_BYTES = 64_000;
|
|
1293
|
+
function expandSelectedSmallFilesToFullCandidates(selection, candidates, focusPaths, budget) {
|
|
2201
1294
|
const candidatesByKey = new Map(candidates.map((candidate) => [selectedFileKey(candidate), candidate]));
|
|
1295
|
+
const focusKeys = new Set(focusPaths
|
|
1296
|
+
.filter((focus) => focus.kind === "file")
|
|
1297
|
+
.map((focus) => `${focus.root_index}\u0000${focus.root_relative_path}`));
|
|
2202
1298
|
let totalBytes = selection.metrics.bytes_selected;
|
|
2203
1299
|
let totalTokens = selection.metrics.estimated_tokens;
|
|
2204
1300
|
let expandedCount = 0;
|
|
2205
1301
|
const selected = selection.selected.map((file) => {
|
|
2206
|
-
if (
|
|
2207
|
-
!isContextBuilderTaskClosureAnchor(file.path, task) &&
|
|
2208
|
-
!isTaskCriticalDiscoveredClosure(file.path, task)) {
|
|
1302
|
+
if (focusKeys.has(selectedFileKey(file)) || file.ranges.length !== 1) {
|
|
2209
1303
|
return file;
|
|
2210
1304
|
}
|
|
2211
1305
|
const candidate = candidatesByKey.get(selectedFileKey(file));
|
|
2212
1306
|
if (candidate == null || fileCoversCandidate(file, candidate)) {
|
|
2213
1307
|
return file;
|
|
2214
1308
|
}
|
|
2215
|
-
if (candidate.byte_length >
|
|
1309
|
+
if (candidate.byte_length > SMALL_SELECTED_SLICE_FULL_EXPANSION_MAX_BYTES) {
|
|
2216
1310
|
return file;
|
|
2217
1311
|
}
|
|
2218
1312
|
const fullFile = selectedFileFromCandidate(candidate);
|
|
1313
|
+
if (fullFile.bytes <= file.bytes) {
|
|
1314
|
+
return file;
|
|
1315
|
+
}
|
|
2219
1316
|
const totalsWithoutFile = {
|
|
2220
1317
|
totalBytes: totalBytes - file.bytes,
|
|
2221
1318
|
totalTokens: totalTokens - file.estimated_tokens,
|
|
@@ -2249,413 +1346,17 @@ function expandSelectedClosureFilesToFullCandidates(selection, candidates, task,
|
|
|
2249
1346
|
},
|
|
2250
1347
|
};
|
|
2251
1348
|
}
|
|
2252
|
-
function
|
|
2253
|
-
|
|
2254
|
-
let totalBytes = selection.metrics.bytes_selected;
|
|
2255
|
-
let totalTokens = selection.metrics.estimated_tokens;
|
|
2256
|
-
let selected = [...selection.selected];
|
|
2257
|
-
let retainedCount = 0;
|
|
2258
|
-
let evictedCount = 0;
|
|
2259
|
-
for (const candidate of [...candidates]
|
|
2260
|
-
.filter((item) => !isTangentialTaskClosureCandidate(item.path, task) &&
|
|
2261
|
-
(isContextBuilderTaskClosureAnchor(item.path, task) ||
|
|
2262
|
-
isTaskCriticalDiscoveredClosure(item.path, task)))
|
|
2263
|
-
.sort((left, right) => compareTaskCriticalCandidates(left, right, task))) {
|
|
2264
|
-
const key = selectedFileKey(candidate);
|
|
2265
|
-
if (selectedKeys.has(key) ||
|
|
2266
|
-
(candidate.byte_length > retainableClosureByteLimit(candidate.path, task) &&
|
|
2267
|
-
compactCandidateForRequiredClosure(candidate, task) == null)) {
|
|
2268
|
-
continue;
|
|
2269
|
-
}
|
|
2270
|
-
const file = candidate.byte_length > retainableClosureByteLimit(candidate.path, task)
|
|
2271
|
-
? compactCandidateForRequiredClosure(candidate, task)
|
|
2272
|
-
: selectedFileFromCandidate(candidate);
|
|
2273
|
-
if (file == null) {
|
|
2274
|
-
continue;
|
|
2275
|
-
}
|
|
2276
|
-
if (!selectionFitsBudgetWithFile(selected, totalBytes, totalTokens, file, budget)) {
|
|
2277
|
-
const repaired = makeRoomForRequiredClosureFile({
|
|
2278
|
-
selected,
|
|
2279
|
-
selectedKeys,
|
|
2280
|
-
totalBytes,
|
|
2281
|
-
totalTokens,
|
|
2282
|
-
file,
|
|
2283
|
-
budget,
|
|
2284
|
-
task,
|
|
2285
|
-
});
|
|
2286
|
-
if (repaired == null) {
|
|
2287
|
-
continue;
|
|
2288
|
-
}
|
|
2289
|
-
selected = repaired.selected;
|
|
2290
|
-
selectedKeys = repaired.selectedKeys;
|
|
2291
|
-
totalBytes = repaired.totalBytes;
|
|
2292
|
-
totalTokens = repaired.totalTokens;
|
|
2293
|
-
evictedCount += repaired.evictedCount;
|
|
2294
|
-
}
|
|
2295
|
-
selected.push(file);
|
|
2296
|
-
selectedKeys.add(key);
|
|
2297
|
-
totalBytes += file.bytes;
|
|
2298
|
-
totalTokens += file.estimated_tokens;
|
|
2299
|
-
retainedCount += 1;
|
|
2300
|
-
}
|
|
2301
|
-
if (retainedCount === 0 && evictedCount === 0) {
|
|
2302
|
-
return { selection, retainedCount, evictedCount };
|
|
2303
|
-
}
|
|
2304
|
-
return {
|
|
2305
|
-
retainedCount,
|
|
2306
|
-
evictedCount,
|
|
2307
|
-
selection: {
|
|
2308
|
-
...selection,
|
|
2309
|
-
selected,
|
|
2310
|
-
metrics: {
|
|
2311
|
-
files_selected: selected.length,
|
|
2312
|
-
files_omitted: selection.metrics.files_omitted,
|
|
2313
|
-
bytes_selected: totalBytes,
|
|
2314
|
-
estimated_tokens: totalTokens,
|
|
2315
|
-
truncated: selection.metrics.truncated,
|
|
2316
|
-
},
|
|
2317
|
-
},
|
|
2318
|
-
};
|
|
2319
|
-
}
|
|
2320
|
-
function dropDegenerateSelectedSlices(selection, candidates, task) {
|
|
2321
|
-
const candidatesByKey = new Map(candidates.map((candidate) => [selectedFileKey(candidate), candidate]));
|
|
2322
|
-
const selected = selection.selected.filter((file) => {
|
|
2323
|
-
if (file.content.trim().length === 0) {
|
|
2324
|
-
return false;
|
|
2325
|
-
}
|
|
2326
|
-
if (!isDegenerateSelectedSlice(file) || file.source === "focus") {
|
|
2327
|
-
return true;
|
|
2328
|
-
}
|
|
2329
|
-
if (!isRequiredClosurePath(file.path, task)) {
|
|
2330
|
-
return true;
|
|
2331
|
-
}
|
|
2332
|
-
const candidate = candidatesByKey.get(selectedFileKey(file));
|
|
2333
|
-
return (candidate != null &&
|
|
2334
|
-
fileCoversCandidate(file, candidate) &&
|
|
2335
|
-
isRequiredClosurePath(candidate.path, task));
|
|
2336
|
-
});
|
|
1349
|
+
function dropEmptySelectedFiles(selection) {
|
|
1350
|
+
const selected = selection.selected.filter((file) => file.content.trim().length > 0);
|
|
2337
1351
|
const droppedCount = selection.selected.length - selected.length;
|
|
2338
1352
|
if (droppedCount === 0) {
|
|
2339
1353
|
return { selection, droppedCount };
|
|
2340
1354
|
}
|
|
2341
1355
|
return {
|
|
2342
|
-
droppedCount,
|
|
2343
1356
|
selection: selectionWithSelectedFiles(selection, selected),
|
|
1357
|
+
droppedCount,
|
|
2344
1358
|
};
|
|
2345
1359
|
}
|
|
2346
|
-
function makeRoomForRequiredClosureFile(input) {
|
|
2347
|
-
if (input.file.bytes > input.budget.max_bytes_per_file) {
|
|
2348
|
-
return null;
|
|
2349
|
-
}
|
|
2350
|
-
const incomingRank = taskCriticalPathRank(input.file.path, input.task);
|
|
2351
|
-
let selected = [...input.selected];
|
|
2352
|
-
let selectedKeys = new Set(input.selectedKeys);
|
|
2353
|
-
let totalBytes = input.totalBytes;
|
|
2354
|
-
let totalTokens = input.totalTokens;
|
|
2355
|
-
let evictedCount = 0;
|
|
2356
|
-
if (!selectionFitsBudgetWithFile(selected, totalBytes, totalTokens, input.file, input.budget)) {
|
|
2357
|
-
const compacted = compactSelectedFilesForRequiredClosure({
|
|
2358
|
-
selected,
|
|
2359
|
-
totalBytes,
|
|
2360
|
-
totalTokens,
|
|
2361
|
-
incomingFile: input.file,
|
|
2362
|
-
task: input.task,
|
|
2363
|
-
});
|
|
2364
|
-
selected = compacted.selected;
|
|
2365
|
-
totalBytes = compacted.totalBytes;
|
|
2366
|
-
totalTokens = compacted.totalTokens;
|
|
2367
|
-
}
|
|
2368
|
-
const evictionOrder = selected
|
|
2369
|
-
.map((file, index) => ({ file, index }))
|
|
2370
|
-
.filter(({ file }) => canEvictForRequiredClosure(file, input.task, incomingRank))
|
|
2371
|
-
.sort((left, right) => selectedEvictionRank(right.file, input.task, incomingRank) -
|
|
2372
|
-
selectedEvictionRank(left.file, input.task, incomingRank) ||
|
|
2373
|
-
right.index - left.index);
|
|
2374
|
-
for (const { file } of evictionOrder) {
|
|
2375
|
-
if (selectionFitsBudgetWithFile(selected, totalBytes, totalTokens, input.file, input.budget)) {
|
|
2376
|
-
break;
|
|
2377
|
-
}
|
|
2378
|
-
const index = selected.findIndex((item) => selectedFileKey(item) === selectedFileKey(file));
|
|
2379
|
-
if (index < 0) {
|
|
2380
|
-
continue;
|
|
2381
|
-
}
|
|
2382
|
-
const [removed] = selected.splice(index, 1);
|
|
2383
|
-
if (removed == null) {
|
|
2384
|
-
continue;
|
|
2385
|
-
}
|
|
2386
|
-
selectedKeys.delete(selectedFileKey(removed));
|
|
2387
|
-
totalBytes -= removed.bytes;
|
|
2388
|
-
totalTokens -= removed.estimated_tokens;
|
|
2389
|
-
evictedCount += 1;
|
|
2390
|
-
}
|
|
2391
|
-
if (!selectionFitsBudgetWithFile(selected, totalBytes, totalTokens, input.file, input.budget)) {
|
|
2392
|
-
return null;
|
|
2393
|
-
}
|
|
2394
|
-
return { selected, selectedKeys, totalBytes, totalTokens, evictedCount };
|
|
2395
|
-
}
|
|
2396
|
-
function selectionFitsBudgetWithFile(selected, totalBytes, totalTokens, file, budget) {
|
|
2397
|
-
return (selected.length < budget.max_files &&
|
|
2398
|
-
file.bytes <= budget.max_bytes_per_file &&
|
|
2399
|
-
totalBytes + file.bytes <= budget.max_total_bytes &&
|
|
2400
|
-
totalTokens + file.estimated_tokens <= budget.target_estimated_tokens);
|
|
2401
|
-
}
|
|
2402
|
-
const REQUIRED_CLOSURE_COMPACT_MIN_TOKENS = 3_000;
|
|
2403
|
-
const REQUIRED_CLOSURE_COMPACT_HEAD_LINES = 60;
|
|
2404
|
-
const REQUIRED_CLOSURE_COMPACT_CONTEXT_LINES = 24;
|
|
2405
|
-
const REQUIRED_CLOSURE_COMPACT_MAX_LINES = 260;
|
|
2406
|
-
function compactSelectedFilesForRequiredClosure(input) {
|
|
2407
|
-
let totalBytes = input.totalBytes;
|
|
2408
|
-
let totalTokens = input.totalTokens;
|
|
2409
|
-
const selected = input.selected.map((file) => {
|
|
2410
|
-
const compacted = compactSelectedFileForRequiredClosure(file, input.incomingFile, input.task);
|
|
2411
|
-
if (compacted == null) {
|
|
2412
|
-
return file;
|
|
2413
|
-
}
|
|
2414
|
-
totalBytes = totalBytes - file.bytes + compacted.bytes;
|
|
2415
|
-
totalTokens = totalTokens - file.estimated_tokens + compacted.estimated_tokens;
|
|
2416
|
-
return compacted;
|
|
2417
|
-
});
|
|
2418
|
-
return { selected, totalBytes, totalTokens };
|
|
2419
|
-
}
|
|
2420
|
-
function compactSelectedFileForRequiredClosure(file, incomingFile, task) {
|
|
2421
|
-
if (file.source !== "focus" ||
|
|
2422
|
-
isProtectedReviewFocusPath(file.path, task) ||
|
|
2423
|
-
file.estimated_tokens <= REQUIRED_CLOSURE_COMPACT_MIN_TOKENS ||
|
|
2424
|
-
selectedRangeLineCount(file) <= REQUIRED_CLOSURE_COMPACT_HEAD_LINES) {
|
|
2425
|
-
return null;
|
|
2426
|
-
}
|
|
2427
|
-
const sourceLines = splitTextLines(file.content);
|
|
2428
|
-
if (sourceLines.length <= REQUIRED_CLOSURE_COMPACT_HEAD_LINES) {
|
|
2429
|
-
return null;
|
|
2430
|
-
}
|
|
2431
|
-
const sourceLineNumbers = selectedFileContentLineNumbers(file, sourceLines.length);
|
|
2432
|
-
if (sourceLineNumbers.length !== sourceLines.length) {
|
|
2433
|
-
return null;
|
|
2434
|
-
}
|
|
2435
|
-
const tokens = new Set([
|
|
2436
|
-
...tokenizeRepairWords(task),
|
|
2437
|
-
...tokenizeRepairWords(incomingFile.path),
|
|
2438
|
-
...tokenizeRepairWords(file.path),
|
|
2439
|
-
]);
|
|
2440
|
-
const selectedLineNumbers = new Set();
|
|
2441
|
-
addOriginalLineWindow(selectedLineNumbers, sourceLineNumbers, sourceLineNumbers[0] ?? 1, (sourceLineNumbers[0] ?? 1) + REQUIRED_CLOSURE_COMPACT_HEAD_LINES - 1);
|
|
2442
|
-
for (let index = 0; index < sourceLines.length; index++) {
|
|
2443
|
-
if (selectedLineNumbers.size >= REQUIRED_CLOSURE_COMPACT_MAX_LINES) {
|
|
2444
|
-
break;
|
|
2445
|
-
}
|
|
2446
|
-
const line = sourceLines[index] ?? "";
|
|
2447
|
-
if (!lineMatchesRepairTokens(line, tokens)) {
|
|
2448
|
-
continue;
|
|
2449
|
-
}
|
|
2450
|
-
const originalLine = sourceLineNumbers[index];
|
|
2451
|
-
if (originalLine == null) {
|
|
2452
|
-
continue;
|
|
2453
|
-
}
|
|
2454
|
-
addOriginalLineWindow(selectedLineNumbers, sourceLineNumbers, originalLine - REQUIRED_CLOSURE_COMPACT_CONTEXT_LINES, originalLine + REQUIRED_CLOSURE_COMPACT_CONTEXT_LINES);
|
|
2455
|
-
}
|
|
2456
|
-
if (selectedLineNumbers.size >= sourceLines.length) {
|
|
2457
|
-
return null;
|
|
2458
|
-
}
|
|
2459
|
-
const ranges = mergeLineRanges([...selectedLineNumbers].map((line) => ({
|
|
2460
|
-
start_line: line,
|
|
2461
|
-
end_line: line,
|
|
2462
|
-
})));
|
|
2463
|
-
const linesByOriginalLine = new Map();
|
|
2464
|
-
for (let index = 0; index < sourceLines.length; index++) {
|
|
2465
|
-
const lineNumber = sourceLineNumbers[index];
|
|
2466
|
-
if (lineNumber != null) {
|
|
2467
|
-
linesByOriginalLine.set(lineNumber, sourceLines[index] ?? "");
|
|
2468
|
-
}
|
|
2469
|
-
}
|
|
2470
|
-
const content = ranges
|
|
2471
|
-
.flatMap((range) => {
|
|
2472
|
-
const lines = [];
|
|
2473
|
-
for (let line = range.start_line; line <= range.end_line; line++) {
|
|
2474
|
-
const contentLine = linesByOriginalLine.get(line);
|
|
2475
|
-
if (contentLine != null) {
|
|
2476
|
-
lines.push(contentLine);
|
|
2477
|
-
}
|
|
2478
|
-
}
|
|
2479
|
-
return lines;
|
|
2480
|
-
})
|
|
2481
|
-
.join("\n");
|
|
2482
|
-
const bytes = Buffer.byteLength(content, "utf8");
|
|
2483
|
-
if (bytes >= file.bytes) {
|
|
2484
|
-
return null;
|
|
2485
|
-
}
|
|
2486
|
-
return {
|
|
2487
|
-
...file,
|
|
2488
|
-
ranges,
|
|
2489
|
-
content,
|
|
2490
|
-
bytes,
|
|
2491
|
-
estimated_tokens: estimateContextBuilderTokensFromUtf8Bytes(bytes),
|
|
2492
|
-
};
|
|
2493
|
-
}
|
|
2494
|
-
function selectedFileContentLineNumbers(file, lineCount) {
|
|
2495
|
-
const numbers = [];
|
|
2496
|
-
for (const range of file.ranges) {
|
|
2497
|
-
for (let line = range.start_line; line <= range.end_line; line++) {
|
|
2498
|
-
numbers.push(line);
|
|
2499
|
-
if (numbers.length >= lineCount) {
|
|
2500
|
-
return numbers;
|
|
2501
|
-
}
|
|
2502
|
-
}
|
|
2503
|
-
}
|
|
2504
|
-
return numbers;
|
|
2505
|
-
}
|
|
2506
|
-
function addOriginalLineWindow(selectedLineNumbers, availableLineNumbers, startLine, endLine) {
|
|
2507
|
-
const available = new Set(availableLineNumbers);
|
|
2508
|
-
for (let line = startLine; line <= endLine; line++) {
|
|
2509
|
-
if (selectedLineNumbers.size >= REQUIRED_CLOSURE_COMPACT_MAX_LINES) {
|
|
2510
|
-
return;
|
|
2511
|
-
}
|
|
2512
|
-
if (available.has(line)) {
|
|
2513
|
-
selectedLineNumbers.add(line);
|
|
2514
|
-
}
|
|
2515
|
-
}
|
|
2516
|
-
}
|
|
2517
|
-
function lineMatchesRepairTokens(line, tokens) {
|
|
2518
|
-
for (const token of tokenizeRepairWords(line)) {
|
|
2519
|
-
if (tokens.has(token)) {
|
|
2520
|
-
return true;
|
|
2521
|
-
}
|
|
2522
|
-
}
|
|
2523
|
-
return false;
|
|
2524
|
-
}
|
|
2525
|
-
function tokenizeRepairWords(input) {
|
|
2526
|
-
return input
|
|
2527
|
-
.toLowerCase()
|
|
2528
|
-
.split(/[^a-z0-9]+/)
|
|
2529
|
-
.filter((token) => token.length >= 3 && !REPAIR_STOP_WORDS.has(token));
|
|
2530
|
-
}
|
|
2531
|
-
const REPAIR_STOP_WORDS = new Set([
|
|
2532
|
-
"app",
|
|
2533
|
-
"src",
|
|
2534
|
-
"the",
|
|
2535
|
-
"and",
|
|
2536
|
-
"for",
|
|
2537
|
-
"with",
|
|
2538
|
-
"from",
|
|
2539
|
-
"this",
|
|
2540
|
-
"that",
|
|
2541
|
-
"file",
|
|
2542
|
-
"test",
|
|
2543
|
-
"tests",
|
|
2544
|
-
]);
|
|
2545
|
-
function canEvictForRequiredClosure(file, task, incomingRank) {
|
|
2546
|
-
const existingRank = taskCriticalPathRank(file.path, task);
|
|
2547
|
-
if (file.source === "focus") {
|
|
2548
|
-
return (incomingRank <= 0 &&
|
|
2549
|
-
existingRank > incomingRank &&
|
|
2550
|
-
!isProtectedReviewFocusPath(file.path, task));
|
|
2551
|
-
}
|
|
2552
|
-
if (isRequiredClosurePath(file.path, task) && existingRank <= incomingRank) {
|
|
2553
|
-
return false;
|
|
2554
|
-
}
|
|
2555
|
-
return true;
|
|
2556
|
-
}
|
|
2557
|
-
function selectedEvictionRank(file, task, incomingRank) {
|
|
2558
|
-
let rank = file.estimated_tokens;
|
|
2559
|
-
if (isDegenerateSelectedSlice(file)) {
|
|
2560
|
-
rank += 10_000;
|
|
2561
|
-
}
|
|
2562
|
-
if (!isRequiredClosurePath(file.path, task)) {
|
|
2563
|
-
rank += 5_000;
|
|
2564
|
-
}
|
|
2565
|
-
rank += Math.max(0, taskCriticalPathRank(file.path, task) - incomingRank) * 500;
|
|
2566
|
-
if (isGenericSelectedPath(file.path)) {
|
|
2567
|
-
rank += 1_000;
|
|
2568
|
-
}
|
|
2569
|
-
return rank;
|
|
2570
|
-
}
|
|
2571
|
-
function isDegenerateSelectedSlice(file) {
|
|
2572
|
-
return file.content.trim().length === 0 || selectedRangeLineCount(file) <= 1;
|
|
2573
|
-
}
|
|
2574
|
-
function selectedRangeLineCount(file) {
|
|
2575
|
-
return file.ranges.reduce((total, range) => total + Math.max(0, range.end_line - range.start_line + 1), 0);
|
|
2576
|
-
}
|
|
2577
|
-
function isRequiredClosurePath(path, task) {
|
|
2578
|
-
return (isContextBuilderTaskClosureAnchor(path, task) ||
|
|
2579
|
-
isTaskCriticalDiscoveredClosure(path, task) ||
|
|
2580
|
-
taskCriticalPathRank(path, task) <= 0);
|
|
2581
|
-
}
|
|
2582
|
-
function isGenericSelectedPath(path) {
|
|
2583
|
-
const normalized = path.toLowerCase();
|
|
2584
|
-
return (normalized.includes("/settings/") ||
|
|
2585
|
-
normalized.includes("/components/ui/") ||
|
|
2586
|
-
normalized.includes("/admin/") ||
|
|
2587
|
-
normalized.includes("/models/db_models/__init__.py") ||
|
|
2588
|
-
normalized.includes("/db/base.py"));
|
|
2589
|
-
}
|
|
2590
|
-
function isDevtoolsTraceDebugTask(task) {
|
|
2591
|
-
const normalized = task.toLowerCase();
|
|
2592
|
-
return (/\b(debug|debugging|diagnose|diagnosis|diagnostic)\b/.test(normalized) &&
|
|
2593
|
-
normalized.includes("devtools") &&
|
|
2594
|
-
normalized.includes("trace"));
|
|
2595
|
-
}
|
|
2596
|
-
function isDevtoolsTraceRelevantPath(path) {
|
|
2597
|
-
if (isTangentialDevtoolsTraceDebugPath(path)) {
|
|
2598
|
-
return false;
|
|
2599
|
-
}
|
|
2600
|
-
const normalized = path.toLowerCase();
|
|
2601
|
-
return (normalized.includes("/right-panel/") ||
|
|
2602
|
-
normalized.endsWith("/rightpaneldevtools.tsx") ||
|
|
2603
|
-
normalized.includes("/store/devtoolsnavstore") ||
|
|
2604
|
-
normalized.includes("/store/__tests__/devtoolsnavstore.test.ts") ||
|
|
2605
|
-
normalized.includes("/services/devtoolsservice") ||
|
|
2606
|
-
normalized.includes("/hooks/queries/usedevtoolsqueries") ||
|
|
2607
|
-
normalized.includes("/hooks/queries/querykeys") ||
|
|
2608
|
-
normalized.endsWith("/querykeys.ts") ||
|
|
2609
|
-
normalized.includes("/types/devtools.types") ||
|
|
2610
|
-
normalized.endsWith("/models/schemas/devtools.py") ||
|
|
2611
|
-
normalized.includes("/api/endpoints/devtools.py") ||
|
|
2612
|
-
normalized.includes("/session_trace_archive") ||
|
|
2613
|
-
normalized.endsWith("/services/codex_trace.py") ||
|
|
2614
|
-
normalized.includes("/chat-window/chat.tsx") ||
|
|
2615
|
-
normalized.includes("/chat/__tests__/chat.test.tsx") ||
|
|
2616
|
-
normalized.includes("/message-bubble/message.tsx") ||
|
|
2617
|
-
normalized.includes("/message-bubble/messagerenderer.tsx") ||
|
|
2618
|
-
normalized.includes("/tools/common/toolcard.tsx") ||
|
|
2619
|
-
normalized.includes("/tools/common/toolexpandcontext.ts") ||
|
|
2620
|
-
normalized.includes("/tools/common/__tests__/toolexpand.test.tsx") ||
|
|
2621
|
-
normalized.includes("/hooks/usemessagecache.ts") ||
|
|
2622
|
-
normalized.includes("/utils/messagecacheorder.ts"));
|
|
2623
|
-
}
|
|
2624
|
-
function shouldExpandSmallDevtoolsTraceFile(path) {
|
|
2625
|
-
const normalized = path.toLowerCase();
|
|
2626
|
-
return (normalized.endsWith("/rightpanel.tsx") ||
|
|
2627
|
-
normalized.endsWith("/tools/common/toolcard.tsx") ||
|
|
2628
|
-
normalized.endsWith("/tools/common/toolexpandcontext.ts"));
|
|
2629
|
-
}
|
|
2630
|
-
function isTangentialDevtoolsTraceDebugPath(path) {
|
|
2631
|
-
const normalized = path.toLowerCase();
|
|
2632
|
-
const basename = normalized.split("/").at(-1) ?? normalized;
|
|
2633
|
-
return (basename === "advancedblocknoteeditor.tsx" ||
|
|
2634
|
-
basename === "githubsettingspopover.tsx" ||
|
|
2635
|
-
basename === "input.tsx" ||
|
|
2636
|
-
basename === "inputsettingspopover.tsx" ||
|
|
2637
|
-
basename === "mentionsuggestionspanel.tsx" ||
|
|
2638
|
-
basename === "segmentbuilder.ts" ||
|
|
2639
|
-
basename === "sendbutton.tsx" ||
|
|
2640
|
-
basename === "slashcommandspanel.tsx" ||
|
|
2641
|
-
normalized === "backend/tests/test_devtools_trace_archive.py" ||
|
|
2642
|
-
normalized.endsWith("/backend/tests/test_devtools_trace_archive.py"));
|
|
2643
|
-
}
|
|
2644
|
-
function isTangentialTaskClosureCandidate(path, task) {
|
|
2645
|
-
return ((isDevtoolsTraceDebugTask(task) && isTangentialDevtoolsTraceDebugPath(path)) ||
|
|
2646
|
-
(isCodexRuntimeReviewTask(task) && isTangentialCodexRuntimeReviewUiPath(path)));
|
|
2647
|
-
}
|
|
2648
|
-
function isProtectedReviewFocusPath(path, task) {
|
|
2649
|
-
if (!/\breview\b/i.test(task)) {
|
|
2650
|
-
return false;
|
|
2651
|
-
}
|
|
2652
|
-
const normalized = path.toLowerCase();
|
|
2653
|
-
return (normalized.includes("/services/runtime/") ||
|
|
2654
|
-
normalized.endsWith("/services/runtime_session_service.py") ||
|
|
2655
|
-
normalized.endsWith("/services/streaming/orchestrator.py") ||
|
|
2656
|
-
normalized.includes("/tests/services/runtime/") ||
|
|
2657
|
-
normalized.endsWith("/tests/services/test_runtime_session_service.py"));
|
|
2658
|
-
}
|
|
2659
1360
|
function selectionWithSelectedFiles(selection, selected) {
|
|
2660
1361
|
const bytes = selected.reduce((total, file) => total + file.bytes, 0);
|
|
2661
1362
|
const tokens = selected.reduce((total, file) => total + file.estimated_tokens, 0);
|
|
@@ -2674,54 +1375,6 @@ function selectionWithSelectedFiles(selection, selected) {
|
|
|
2674
1375
|
function selectedFileKey(file) {
|
|
2675
1376
|
return `${file.root_index}\u0000${file.path}`;
|
|
2676
1377
|
}
|
|
2677
|
-
const RETAINABLE_CLOSURE_MAX_BYTES = 24_000;
|
|
2678
|
-
const RETAINABLE_PROVIDER_IMPL_MAX_BYTES = 48_000;
|
|
2679
|
-
function isRetainableClosureFile(file, task) {
|
|
2680
|
-
if (file.bytes > retainableClosureByteLimit(file.path, task)) {
|
|
2681
|
-
return false;
|
|
2682
|
-
}
|
|
2683
|
-
const normalized = file.path.toLowerCase();
|
|
2684
|
-
const basename = normalized.split("/").at(-1) ?? normalized;
|
|
2685
|
-
return (normalized.includes("/types/") ||
|
|
2686
|
-
normalized.includes("/schemas/") ||
|
|
2687
|
-
normalized.includes("/constants.") ||
|
|
2688
|
-
normalized.includes("/config.") ||
|
|
2689
|
-
normalized.includes("/querykeys.") ||
|
|
2690
|
-
normalized.includes("/messagerenderer.") ||
|
|
2691
|
-
normalized.includes("/message-bubble/message.") ||
|
|
2692
|
-
normalized.includes("/toolcard.") ||
|
|
2693
|
-
normalized.includes("/toolexpandcontext.") ||
|
|
2694
|
-
normalized.includes("/messagecacheorder.") ||
|
|
2695
|
-
normalized.includes("/session_trace_archive.") ||
|
|
2696
|
-
normalized.includes("/runtime/base.") ||
|
|
2697
|
-
normalized.includes("/claude_agent.") ||
|
|
2698
|
-
normalized.includes("/rightpanel.") ||
|
|
2699
|
-
basename.endsWith(".types.ts") ||
|
|
2700
|
-
basename === "api.types.ts" ||
|
|
2701
|
-
basename === "types.ts" ||
|
|
2702
|
-
basename === "schema.ts" ||
|
|
2703
|
-
basename === "schemas.ts" ||
|
|
2704
|
-
basename === "constants.ts" ||
|
|
2705
|
-
basename === "constants.py" ||
|
|
2706
|
-
basename === "config.ts" ||
|
|
2707
|
-
normalized.endsWith("/core/config.py") ||
|
|
2708
|
-
basename === "querykeys.ts" ||
|
|
2709
|
-
basename === "permission_mode.py" ||
|
|
2710
|
-
basename === "effort.py" ||
|
|
2711
|
-
basename === "codex_trace.py");
|
|
2712
|
-
}
|
|
2713
|
-
function retainableClosureByteLimit(path, task) {
|
|
2714
|
-
const normalized = path.toLowerCase();
|
|
2715
|
-
if (normalized.endsWith("codex_trace.py")) {
|
|
2716
|
-
return 12_000;
|
|
2717
|
-
}
|
|
2718
|
-
if (/\b(provider|runtime|codex|claude|review|discovery)\b/.test(task.toLowerCase()) &&
|
|
2719
|
-
(normalized.endsWith("/claude_agent.py") ||
|
|
2720
|
-
normalized.endsWith("/runtime/base.py"))) {
|
|
2721
|
-
return RETAINABLE_PROVIDER_IMPL_MAX_BYTES;
|
|
2722
|
-
}
|
|
2723
|
-
return RETAINABLE_CLOSURE_MAX_BYTES;
|
|
2724
|
-
}
|
|
2725
1378
|
function selectedFileFromCandidate(candidate) {
|
|
2726
1379
|
const bytes = Buffer.byteLength(candidate.content, "utf8");
|
|
2727
1380
|
return {
|
|
@@ -2734,421 +1387,21 @@ function selectedFileFromCandidate(candidate) {
|
|
|
2734
1387
|
source: "search",
|
|
2735
1388
|
};
|
|
2736
1389
|
}
|
|
2737
|
-
function compactCandidateForRequiredClosure(candidate, task) {
|
|
2738
|
-
const sourceLines = splitTextLines(candidate.content);
|
|
2739
|
-
if (sourceLines.length === 0) {
|
|
2740
|
-
return null;
|
|
2741
|
-
}
|
|
2742
|
-
const tokens = new Set([
|
|
2743
|
-
...tokenizeRepairWords(task),
|
|
2744
|
-
...tokenizeRepairWords(candidate.path),
|
|
2745
|
-
]);
|
|
2746
|
-
const selectedLineNumbers = new Set();
|
|
2747
|
-
const availableLineNumbers = sourceLines.map((_, index) => index + 1);
|
|
2748
|
-
addOriginalLineWindow(selectedLineNumbers, availableLineNumbers, 1, REQUIRED_CLOSURE_COMPACT_HEAD_LINES);
|
|
2749
|
-
for (let index = 0; index < sourceLines.length; index++) {
|
|
2750
|
-
if (selectedLineNumbers.size >= REQUIRED_CLOSURE_COMPACT_MAX_LINES) {
|
|
2751
|
-
break;
|
|
2752
|
-
}
|
|
2753
|
-
const line = sourceLines[index] ?? "";
|
|
2754
|
-
if (!lineMatchesRepairTokens(line, tokens)) {
|
|
2755
|
-
continue;
|
|
2756
|
-
}
|
|
2757
|
-
const originalLine = index + 1;
|
|
2758
|
-
addOriginalLineWindow(selectedLineNumbers, availableLineNumbers, originalLine - REQUIRED_CLOSURE_COMPACT_CONTEXT_LINES, originalLine + REQUIRED_CLOSURE_COMPACT_CONTEXT_LINES);
|
|
2759
|
-
}
|
|
2760
|
-
if (selectedLineNumbers.size === 0 || selectedLineNumbers.size >= sourceLines.length) {
|
|
2761
|
-
return null;
|
|
2762
|
-
}
|
|
2763
|
-
const ranges = mergeLineRanges([...selectedLineNumbers].map((line) => ({
|
|
2764
|
-
start_line: line,
|
|
2765
|
-
end_line: line,
|
|
2766
|
-
})));
|
|
2767
|
-
const content = ranges
|
|
2768
|
-
.flatMap((range) => sourceLines.slice(range.start_line - 1, range.end_line))
|
|
2769
|
-
.join("\n");
|
|
2770
|
-
const bytes = Buffer.byteLength(content, "utf8");
|
|
2771
|
-
if (bytes <= 0 || bytes >= candidate.byte_length) {
|
|
2772
|
-
return null;
|
|
2773
|
-
}
|
|
2774
|
-
return {
|
|
2775
|
-
root_index: candidate.root_index,
|
|
2776
|
-
path: candidate.path,
|
|
2777
|
-
ranges,
|
|
2778
|
-
content,
|
|
2779
|
-
bytes,
|
|
2780
|
-
estimated_tokens: estimateContextBuilderTokensFromUtf8Bytes(bytes),
|
|
2781
|
-
source: "search",
|
|
2782
|
-
};
|
|
2783
|
-
}
|
|
2784
|
-
function isTaskCriticalDiscoveredClosure(path, task) {
|
|
2785
|
-
const normalizedTask = task.toLowerCase();
|
|
2786
|
-
const normalized = path.toLowerCase();
|
|
2787
|
-
const basename = normalized.split("/").at(-1) ?? normalized;
|
|
2788
|
-
const providerRuntimeTask = /\b(provider|provider-specific|runtime|codex|claude)\b/.test(normalizedTask);
|
|
2789
|
-
if (/\bdebug(?:ging)?\b/.test(normalizedTask) &&
|
|
2790
|
-
!isTangentialDevtoolsTraceDebugPath(path) &&
|
|
2791
|
-
(basename === "codex_trace.py" ||
|
|
2792
|
-
basename === "session_trace_archive.py" ||
|
|
2793
|
-
basename === "toolcard.tsx" ||
|
|
2794
|
-
basename === "devtools-helpers.ts" ||
|
|
2795
|
-
basename === "devtoolsservice.ts" ||
|
|
2796
|
-
basename === "usedevtoolsqueries.ts" ||
|
|
2797
|
-
basename === "devtoolsnavstore.ts" ||
|
|
2798
|
-
basename === "messagerenderer.tsx" ||
|
|
2799
|
-
basename === "rightpaneldevtools.tsx" ||
|
|
2800
|
-
basename === "rightpanel.tsx" ||
|
|
2801
|
-
basename === "usemessagecache.ts" ||
|
|
2802
|
-
basename === "chat.tsx" ||
|
|
2803
|
-
basename === "right-panel-devtools.test.tsx" ||
|
|
2804
|
-
normalized.endsWith("/api/endpoints/devtools.py") ||
|
|
2805
|
-
normalized.endsWith("/models/db_models/session_trace_archive.py") ||
|
|
2806
|
-
normalized.includes("devtools-helpers.test") ||
|
|
2807
|
-
normalized.includes("toolexpand.test") ||
|
|
2808
|
-
normalized.includes("devtoolsnavstore.test"))) {
|
|
2809
|
-
return true;
|
|
2810
|
-
}
|
|
2811
|
-
if (/\breview\b/.test(normalizedTask) &&
|
|
2812
|
-
!isTangentialCodexRuntimeReviewUiPath(path) &&
|
|
2813
|
-
((normalized.endsWith("/core/config.py") &&
|
|
2814
|
-
/\bconfig(?:uration)?\b/.test(normalizedTask)) ||
|
|
2815
|
-
basename === "constants.py" ||
|
|
2816
|
-
basename === "permission_manager.py" ||
|
|
2817
|
-
basename === "ai_model.py" ||
|
|
2818
|
-
basename === "permissions.py" ||
|
|
2819
|
-
basename === "permission_mode.py" ||
|
|
2820
|
-
basename === "permissions.ts" ||
|
|
2821
|
-
basename === "effort.py" ||
|
|
2822
|
-
basename === "usepermissionrequest.ts" ||
|
|
2823
|
-
basename === "usemessageactions.ts" ||
|
|
2824
|
-
basename === "fileuploaddialog.tsx" ||
|
|
2825
|
-
basename === "modelprovider.ts" ||
|
|
2826
|
-
basename === "ai_models.py" ||
|
|
2827
|
-
basename === "test_ai_models_codex_gate.py" ||
|
|
2828
|
-
basename === "model_provider_auth.py" ||
|
|
2829
|
-
basename === "chat.types.ts" ||
|
|
2830
|
-
basename === "chatservice.ts" ||
|
|
2831
|
-
normalized.endsWith("/api/endpoints/chat.py") ||
|
|
2832
|
-
normalized.endsWith("/services/chat.py") ||
|
|
2833
|
-
basename === "streamservice.ts" ||
|
|
2834
|
-
basename === "fileuploaddialog.codex.test.tsx" ||
|
|
2835
|
-
basename === "test_permissions.py" ||
|
|
2836
|
-
basename === "test_model_provider_auth.py" ||
|
|
2837
|
-
normalized.endsWith("/api/endpoints/permissions.py") ||
|
|
2838
|
-
isTaskCriticalReviewDoc(normalized) ||
|
|
2839
|
-
normalized.includes("resolver") && normalized.includes("test") ||
|
|
2840
|
-
normalized.includes("modelprovider") && normalized.includes("test"))) {
|
|
2841
|
-
return true;
|
|
2842
|
-
}
|
|
2843
|
-
if (/\bdiscovery\b/.test(normalizedTask) &&
|
|
2844
|
-
(basename === "permission_mode.py" ||
|
|
2845
|
-
normalized.endsWith("/services/chat.py") ||
|
|
2846
|
-
normalized.endsWith("/components/chat/chat-window/chat.tsx") ||
|
|
2847
|
-
normalized.endsWith("/components/chat/message-input/input.tsx") ||
|
|
2848
|
-
normalized.endsWith("/components/chat/message-input/composerruntimecontrols.tsx") ||
|
|
2849
|
-
normalized.endsWith("/components/chat/message-input/inputsettingspopover.tsx") ||
|
|
2850
|
-
normalized.endsWith("/hooks/usechatruntimesettings.ts") ||
|
|
2851
|
-
normalized.endsWith("/pages/chatpage.tsx") ||
|
|
2852
|
-
normalized.endsWith("/api/endpoints/chat.py") ||
|
|
2853
|
-
normalized.endsWith("/models/db_models/chat.py") ||
|
|
2854
|
-
normalized.endsWith("/models/schemas/chat.py") ||
|
|
2855
|
-
basename === "chatservice.ts" ||
|
|
2856
|
-
basename === "messagequeuestore.ts" ||
|
|
2857
|
-
basename === "streamservice.ts" ||
|
|
2858
|
-
basename === "messagequeuestore.ts" ||
|
|
2859
|
-
basename === "usepermissionrequest.ts" ||
|
|
2860
|
-
basename === "usesettingsqueries.ts" ||
|
|
2861
|
-
basename === "chat_processor.py" ||
|
|
2862
|
-
basename === "landingpage.tsx" ||
|
|
2863
|
-
basename === "usechatstreaming.ts" ||
|
|
2864
|
-
basename === "usemessageactions.ts" ||
|
|
2865
|
-
basename === "queue.py" ||
|
|
2866
|
-
basename === "test_chat.py" ||
|
|
2867
|
-
basename === "test_queue.py" ||
|
|
2868
|
-
normalized.endsWith("/tests/services/test_queue.py") ||
|
|
2869
|
-
basename === "test_effort_level.py" ||
|
|
2870
|
-
basename === "test_permission_mode.py" ||
|
|
2871
|
-
basename === "test_codex_runtime.py" ||
|
|
2872
|
-
basename === "test_codex_effort_translation.py" ||
|
|
2873
|
-
normalized.endsWith("/services/runtime/claude.py") ||
|
|
2874
|
-
normalized.endsWith("/services/runtime/codex.py") ||
|
|
2875
|
-
normalized.endsWith("/services/runtime/claude.py") ||
|
|
2876
|
-
normalized.endsWith("/services/runtime/base.py") ||
|
|
2877
|
-
normalized.endsWith("/services/runtime/claude.py") ||
|
|
2878
|
-
normalized.endsWith("/services/runtime/codex_approval.py") ||
|
|
2879
|
-
normalized.endsWith("/services/runtime_session_service.py") ||
|
|
2880
|
-
basename === "test_pending_initial_prompt_service.py" ||
|
|
2881
|
-
basename === "queue.types.ts" ||
|
|
2882
|
-
basename === "chat.types.ts" ||
|
|
2883
|
-
basename.includes("permissionmodeselector") ||
|
|
2884
|
-
basename.includes("thinkingmodeselector"))) {
|
|
2885
|
-
return true;
|
|
2886
|
-
}
|
|
2887
|
-
if (providerRuntimeTask &&
|
|
2888
|
-
/\b(provider-specific|provider\s+semantics|providers?|claude|agent|symmetry)\b/.test(normalizedTask) &&
|
|
2889
|
-
(basename === "claude_agent.py" || normalized.endsWith("/runtime/base.py"))) {
|
|
2890
|
-
return true;
|
|
2891
|
-
}
|
|
2892
|
-
return false;
|
|
2893
|
-
}
|
|
2894
1390
|
function fileCoversCandidate(file, candidate) {
|
|
2895
1391
|
return file.ranges.some((range) => range.start_line <= 1 &&
|
|
2896
1392
|
range.end_line >= Math.max(1, candidate.line_count));
|
|
2897
1393
|
}
|
|
2898
|
-
function compareTaskCriticalCandidates(left, right, task) {
|
|
2899
|
-
return (taskCriticalPathRank(left.path, task) - taskCriticalPathRank(right.path, task) ||
|
|
2900
|
-
left.byte_length - right.byte_length ||
|
|
2901
|
-
left.root_index - right.root_index ||
|
|
2902
|
-
left.path.localeCompare(right.path));
|
|
2903
|
-
}
|
|
2904
|
-
function taskCriticalPathRank(path, task) {
|
|
2905
|
-
const normalized = path.toLowerCase();
|
|
2906
|
-
const basename = normalized.split("/").at(-1) ?? normalized;
|
|
2907
|
-
const normalizedTask = task.toLowerCase();
|
|
2908
|
-
if (/\bdiscovery\b/.test(normalizedTask)) {
|
|
2909
|
-
if (isRuntimeSettingsDiscoveryTask(task) &&
|
|
2910
|
-
isTangentialRuntimeSettingsDiscoveryTestPath(normalized, basename)) {
|
|
2911
|
-
return 5;
|
|
2912
|
-
}
|
|
2913
|
-
if (isRuntimeSettingsDiscoveryTask(task)) {
|
|
2914
|
-
const runtimeRank = runtimeSettingsDiscoveryTaskCriticalRank(normalized, basename);
|
|
2915
|
-
if (runtimeRank != null) {
|
|
2916
|
-
return runtimeRank;
|
|
2917
|
-
}
|
|
2918
|
-
}
|
|
2919
|
-
if (normalized.endsWith("/services/chat.py") ||
|
|
2920
|
-
normalized.endsWith("/components/chat/chat-window/chat.tsx") ||
|
|
2921
|
-
normalized.endsWith("/components/chat/message-input/input.tsx") ||
|
|
2922
|
-
normalized.endsWith("/components/chat/message-input/composerruntimecontrols.tsx") ||
|
|
2923
|
-
normalized.endsWith("/components/chat/message-input/inputsettingspopover.tsx") ||
|
|
2924
|
-
normalized.endsWith("/pages/chatpage.tsx") ||
|
|
2925
|
-
normalized.endsWith("/models/db_models/chat.py") ||
|
|
2926
|
-
normalized.endsWith("/models/schemas/chat.py") ||
|
|
2927
|
-
basename === "chatservice.ts" ||
|
|
2928
|
-
basename === "streamservice.ts" ||
|
|
2929
|
-
basename === "usemessageactions.ts" ||
|
|
2930
|
-
basename === "usechatstreaming.ts" ||
|
|
2931
|
-
basename === "landingpage.tsx" ||
|
|
2932
|
-
basename === "chat_processor.py" ||
|
|
2933
|
-
basename === "test_queue.py" ||
|
|
2934
|
-
normalized.endsWith("/tests/services/test_queue.py") ||
|
|
2935
|
-
basename === "test_chat.py" ||
|
|
2936
|
-
basename === "test_queue_service.py" ||
|
|
2937
|
-
basename === "test_chat_processor.py" ||
|
|
2938
|
-
normalized.endsWith("/services/runtime/claude.py") ||
|
|
2939
|
-
normalized.endsWith("/services/runtime/codex.py") ||
|
|
2940
|
-
normalized.endsWith("/services/runtime/codex_stream_processor.py") ||
|
|
2941
|
-
normalized.endsWith("/services/runtime/base.py") ||
|
|
2942
|
-
normalized.endsWith("/services/runtime/claude.py") ||
|
|
2943
|
-
normalized.endsWith("/services/runtime/codex_approval.py") ||
|
|
2944
|
-
normalized.endsWith("/services/runtime_session_service.py") ||
|
|
2945
|
-
normalized.endsWith("/models/db_models/runtime_session.py") ||
|
|
2946
|
-
normalized.endsWith("/services/permission_manager.py") ||
|
|
2947
|
-
normalized.endsWith("/hooks/__tests__/usechatruntimesettings.test.ts") ||
|
|
2948
|
-
normalized.endsWith("/pages/landingpage.test.tsx") ||
|
|
2949
|
-
normalized.endsWith("/tests/test_codex_runtime_data_migration.py") ||
|
|
2950
|
-
basename === "chat.types.ts" ||
|
|
2951
|
-
basename === "queue.types.ts" ||
|
|
2952
|
-
basename.includes("permissionmodeselector") ||
|
|
2953
|
-
basename.includes("thinkingmodeselector")) {
|
|
2954
|
-
return 0;
|
|
2955
|
-
}
|
|
2956
|
-
if (basename === "test_effort_level.py" ||
|
|
2957
|
-
basename === "test_permission_mode.py" ||
|
|
2958
|
-
basename === "test_codex_runtime.py" ||
|
|
2959
|
-
basename === "test_codex_effort_translation.py") {
|
|
2960
|
-
return 1;
|
|
2961
|
-
}
|
|
2962
|
-
if ((basename === "claude_agent.py" || basename === "test_claude_agent.py") &&
|
|
2963
|
-
/\b(provider-specific|provider\s+semantics|providers?|claude|agent|symmetry)\b/.test(normalizedTask)) {
|
|
2964
|
-
return 0;
|
|
2965
|
-
}
|
|
2966
|
-
if (basename === "test_codex_e2e_live.py" ||
|
|
2967
|
-
basename === "test_codex_sdk_smoke.py" ||
|
|
2968
|
-
basename === "test_transport_absence.py" ||
|
|
2969
|
-
basename === "test_codex_runtime_live.py" ||
|
|
2970
|
-
basename === "test_resolver.py") {
|
|
2971
|
-
return 5;
|
|
2972
|
-
}
|
|
2973
|
-
}
|
|
2974
|
-
if (/\breview\b/.test(normalizedTask)) {
|
|
2975
|
-
if (normalized.endsWith("/api/endpoints/permissions.py") ||
|
|
2976
|
-
(normalized.endsWith("/core/config.py") &&
|
|
2977
|
-
/\bconfig(?:uration)?\b/.test(normalizedTask)) ||
|
|
2978
|
-
normalized.endsWith("/services/runtime/codex.py") ||
|
|
2979
|
-
normalized.endsWith("/services/runtime/codex_approval.py") ||
|
|
2980
|
-
normalized.endsWith("/services/runtime/codex_stream_processor.py") ||
|
|
2981
|
-
normalized.endsWith("/services/runtime/resolver.py") ||
|
|
2982
|
-
normalized.endsWith("/services/runtime_session_service.py") ||
|
|
2983
|
-
normalized.endsWith("/services/sandbox/provider.py") ||
|
|
2984
|
-
normalized.endsWith("/services/streaming/context_usage.py") ||
|
|
2985
|
-
normalized.endsWith("/services/model_provider_auth.py") ||
|
|
2986
|
-
normalized.endsWith("/services/providerauthservice.ts") ||
|
|
2987
|
-
normalized.endsWith("/services/streamservice.ts") ||
|
|
2988
|
-
normalized.endsWith("/hooks/usechatruntimesettings.ts") ||
|
|
2989
|
-
normalized.endsWith("/components/chat/permission-mode-selector/permissionmodeselector.tsx") ||
|
|
2990
|
-
normalized.endsWith("/components/chat/thinking-mode-selector/thinkingmodeselector.tsx") ||
|
|
2991
|
-
normalized.endsWith("/components/chat/message-input/composerruntimecontrols.tsx") ||
|
|
2992
|
-
normalized.endsWith("/api/endpoints/ai_models.py") ||
|
|
2993
|
-
normalized.endsWith("/tests/test_ai_models_codex_gate.py") ||
|
|
2994
|
-
normalized.endsWith("/services/streaming/orchestrator.py") ||
|
|
2995
|
-
(normalized.endsWith("/tests/services/streaming/test_context_usage_payloads.py") &&
|
|
2996
|
-
/\bcontext\s+usage|usage\s+payloads?\b/.test(normalizedTask)) ||
|
|
2997
|
-
normalized.includes("/tests/services/runtime/") ||
|
|
2998
|
-
normalized.endsWith("/tests/services/test_model_provider_auth.py") ||
|
|
2999
|
-
normalized.endsWith("/tests/services/test_runtime_session_service.py") ||
|
|
3000
|
-
normalized.endsWith("/api/endpoints/chat.py") ||
|
|
3001
|
-
normalized.endsWith("/services/chat.py") ||
|
|
3002
|
-
normalized.endsWith("/models/db_models/ai_model.py") ||
|
|
3003
|
-
basename === "constants.py" ||
|
|
3004
|
-
basename === "usepermissionrequest.ts" ||
|
|
3005
|
-
basename === "usemessageactions.ts" ||
|
|
3006
|
-
basename === "fileuploaddialog.tsx" ||
|
|
3007
|
-
basename === "modelprovider.ts" ||
|
|
3008
|
-
basename === "test_permissions.py" ||
|
|
3009
|
-
basename === "permission_manager.py" ||
|
|
3010
|
-
basename === "chatservice.ts" ||
|
|
3011
|
-
basename === "chat.types.ts") {
|
|
3012
|
-
return 0;
|
|
3013
|
-
}
|
|
3014
|
-
if (isTaskCriticalReviewDoc(normalized)) {
|
|
3015
|
-
return 2;
|
|
3016
|
-
}
|
|
3017
|
-
if (normalized.includes("/admin/config.py") ||
|
|
3018
|
-
normalized.includes("/.ai/memory-bank/")) {
|
|
3019
|
-
return 5;
|
|
3020
|
-
}
|
|
3021
|
-
}
|
|
3022
|
-
if (isDevtoolsTraceDebugTask(task)) {
|
|
3023
|
-
if (isTangentialDevtoolsTraceDebugPath(path)) {
|
|
3024
|
-
return 5;
|
|
3025
|
-
}
|
|
3026
|
-
if (normalized.endsWith("/components/right-panel/rightpaneldevtools.tsx") ||
|
|
3027
|
-
normalized.endsWith("/components/right-panel/__tests__/right-panel-devtools.test.tsx") ||
|
|
3028
|
-
normalized.endsWith("/components/chat/__tests__/chat.autoscroll.test.tsx") ||
|
|
3029
|
-
normalized.endsWith("/components/right-panel/devtools-helpers.ts") ||
|
|
3030
|
-
normalized.endsWith("/store/devtoolsnavstore.ts") ||
|
|
3031
|
-
normalized.endsWith("/store/__tests__/devtoolsnavstore.test.ts") ||
|
|
3032
|
-
normalized.endsWith("/components/chat/chat-window/chat.tsx") ||
|
|
3033
|
-
normalized.endsWith("/components/chat/message-bubble/messagerenderer.tsx") ||
|
|
3034
|
-
normalized.endsWith("/components/chat/tools/common/toolcard.tsx") ||
|
|
3035
|
-
normalized.endsWith("/components/chat/message-bubble/message.tsx")) {
|
|
3036
|
-
return 0;
|
|
3037
|
-
}
|
|
3038
|
-
if (normalized.endsWith("/hooks/queries/usedevtoolsqueries.ts") ||
|
|
3039
|
-
normalized.endsWith("/services/devtoolsservice.ts") ||
|
|
3040
|
-
normalized.endsWith("/hooks/queries/querykeys.ts") ||
|
|
3041
|
-
normalized.endsWith("/utils/messagecacheorder.ts") ||
|
|
3042
|
-
normalized.endsWith("/hooks/usemessagecache.ts") ||
|
|
3043
|
-
normalized.endsWith("/components/chat/tools/common/toolexpandcontext.ts") ||
|
|
3044
|
-
normalized.endsWith("/components/chat/tools/common/__tests__/toolexpand.test.tsx")) {
|
|
3045
|
-
return 1;
|
|
3046
|
-
}
|
|
3047
|
-
if (normalized.endsWith("/api/endpoints/devtools.py") ||
|
|
3048
|
-
normalized.endsWith("/services/session_trace_archive.py")) {
|
|
3049
|
-
return 3;
|
|
3050
|
-
}
|
|
3051
|
-
if (normalized.endsWith("/models/db_models/session_trace_archive.py") ||
|
|
3052
|
-
normalized.endsWith("/models/schemas/devtools.py") ||
|
|
3053
|
-
normalized.endsWith("/services/codex_trace.py") ||
|
|
3054
|
-
normalized.endsWith("/types/devtools.types.ts")) {
|
|
3055
|
-
return 4;
|
|
3056
|
-
}
|
|
3057
|
-
if (normalized.endsWith("/pages/chatpage.tsx") ||
|
|
3058
|
-
normalized.endsWith("/components/chat/message-input/input.tsx") ||
|
|
3059
|
-
normalized.endsWith("/api/endpoints/chat.py") ||
|
|
3060
|
-
normalized.endsWith("/services/claude_agent.py") ||
|
|
3061
|
-
normalized.endsWith("/services/chat.py") ||
|
|
3062
|
-
normalized.endsWith("/services/chatservice.ts")) {
|
|
3063
|
-
return 4;
|
|
3064
|
-
}
|
|
3065
|
-
}
|
|
3066
|
-
if (basename === "permission_mode.py" ||
|
|
3067
|
-
basename === "effort.py" ||
|
|
3068
|
-
basename === "permissions.py" ||
|
|
3069
|
-
basename === "constants.py" ||
|
|
3070
|
-
basename === "config.py" ||
|
|
3071
|
-
basename === "querykeys.ts" ||
|
|
3072
|
-
basename.endsWith(".types.ts")) {
|
|
3073
|
-
return 0;
|
|
3074
|
-
}
|
|
3075
|
-
if (basename.endsWith(".test.tsx") || basename.endsWith(".test.ts")) {
|
|
3076
|
-
return 2;
|
|
3077
|
-
}
|
|
3078
|
-
return 1;
|
|
3079
|
-
}
|
|
3080
|
-
function runtimeSettingsDiscoveryTaskCriticalRank(normalizedPath, basename) {
|
|
3081
|
-
if (normalizedPath.endsWith("/hooks/usechatruntimesettings.ts")) {
|
|
3082
|
-
return 0;
|
|
3083
|
-
}
|
|
3084
|
-
if (normalizedPath.endsWith("/components/chat/message-input/composerruntimecontrols.tsx") ||
|
|
3085
|
-
normalizedPath.endsWith("/components/chat/message-input/input.tsx") ||
|
|
3086
|
-
normalizedPath.endsWith("/pages/chatpage.tsx")) {
|
|
3087
|
-
return 0;
|
|
3088
|
-
}
|
|
3089
|
-
if (normalizedPath.endsWith("/services/chatservice.ts") ||
|
|
3090
|
-
normalizedPath.endsWith("/api/endpoints/chat.py") ||
|
|
3091
|
-
normalizedPath.endsWith("/services/chat.py")) {
|
|
3092
|
-
return 0;
|
|
3093
|
-
}
|
|
3094
|
-
if (normalizedPath.endsWith("/store/messagequeuestore.ts") ||
|
|
3095
|
-
normalizedPath.endsWith("/store/__tests__/messagequeuestore.test.ts") ||
|
|
3096
|
-
normalizedPath.endsWith("/services/queueservice.ts") ||
|
|
3097
|
-
normalizedPath.endsWith("/tests/services/test_queue.py") ||
|
|
3098
|
-
normalizedPath.endsWith("/tests/test_queue.py") ||
|
|
3099
|
-
normalizedPath.endsWith("/tests/services/test_pending_initial_prompt_service.py")) {
|
|
3100
|
-
return 0;
|
|
3101
|
-
}
|
|
3102
|
-
if (normalizedPath.endsWith("/services/runtime/codex.py") ||
|
|
3103
|
-
normalizedPath.endsWith("/services/runtime/claude.py") ||
|
|
3104
|
-
normalizedPath.endsWith("/services/runtime/codex_approval.py") ||
|
|
3105
|
-
normalizedPath.endsWith("/services/runtime/codex_stream_processor.py") ||
|
|
3106
|
-
normalizedPath.endsWith("/services/runtime_session_service.py") ||
|
|
3107
|
-
normalizedPath.endsWith("/services/streaming/orchestrator.py")) {
|
|
3108
|
-
return 0;
|
|
3109
|
-
}
|
|
3110
|
-
if (normalizedPath.endsWith("/hooks/__tests__/usechatruntimesettings.test.ts") ||
|
|
3111
|
-
normalizedPath.endsWith("/pages/landingpage.test.tsx") ||
|
|
3112
|
-
normalizedPath.endsWith("/tests/services/runtime/test_codex_runtime.py") ||
|
|
3113
|
-
normalizedPath.endsWith("/tests/test_codex_runtime_data_migration.py") ||
|
|
3114
|
-
normalizedPath.endsWith("/tests/test_codex_effort_translation.py") ||
|
|
3115
|
-
normalizedPath.endsWith("/tests/test_effort_level.py") ||
|
|
3116
|
-
normalizedPath.endsWith("/tests/test_permission_mode.py") ||
|
|
3117
|
-
normalizedPath.endsWith("/tests/services/test_claude_agent.py")) {
|
|
3118
|
-
return 1;
|
|
3119
|
-
}
|
|
3120
|
-
if (basename === "effort.py" ||
|
|
3121
|
-
basename === "permission_mode.py" ||
|
|
3122
|
-
basename === "chat.types.ts" ||
|
|
3123
|
-
basename === "queue.types.ts" ||
|
|
3124
|
-
normalizedPath.endsWith("/models/schemas/chat.py") ||
|
|
3125
|
-
normalizedPath.endsWith("/models/db_models/chat.py") ||
|
|
3126
|
-
normalizedPath.endsWith("/models/db_models/runtime_session.py") ||
|
|
3127
|
-
basename.includes("permissionmodeselector") ||
|
|
3128
|
-
basename.includes("thinkingmodeselector")) {
|
|
3129
|
-
return 2;
|
|
3130
|
-
}
|
|
3131
|
-
return null;
|
|
3132
|
-
}
|
|
3133
|
-
function isTaskCriticalReviewDoc(normalizedPath) {
|
|
3134
|
-
if (!normalizedPath.endsWith(".md")) {
|
|
3135
|
-
return false;
|
|
3136
|
-
}
|
|
3137
|
-
if (normalizedPath.startsWith(".ai/memory-bank/") || normalizedPath.includes("/.ai/memory-bank/")) {
|
|
3138
|
-
return false;
|
|
3139
|
-
}
|
|
3140
|
-
if (normalizedPath.startsWith(".ai/") || normalizedPath.includes("/.ai/")) {
|
|
3141
|
-
return false;
|
|
3142
|
-
}
|
|
3143
|
-
if (normalizedPath.endsWith("docs/ops/nano-permissions-policy.md")) {
|
|
3144
|
-
return false;
|
|
3145
|
-
}
|
|
3146
|
-
return (/(^|\/)docs?\/.*(codex|approval|permission|session|runtime).*\.md$/.test(normalizedPath));
|
|
3147
|
-
}
|
|
3148
1394
|
function handleDelegateRuntimeFailure(input) {
|
|
3149
|
-
|
|
1395
|
+
// Delegate-failure reasons aggregate many per-entry details (each already
|
|
1396
|
+
// individually sanitized/redacted where assembled); the wider marked-
|
|
1397
|
+
// truncation bound keeps the tail from being silently dropped.
|
|
1398
|
+
const failureReason = sanitizeDelegateFailureText(input.reason);
|
|
3150
1399
|
if (input.delegate.failure_policy === "fail") {
|
|
3151
|
-
|
|
1400
|
+
const error = new ExecutionError(`context_builder delegate failed: ${failureReason}`);
|
|
1401
|
+
if (input.failureKind === "timeout") {
|
|
1402
|
+
error.failureClass = "transient";
|
|
1403
|
+
}
|
|
1404
|
+
throw error;
|
|
3152
1405
|
}
|
|
3153
1406
|
const warnings = [
|
|
3154
1407
|
delegateWarning("delegate_fallback", `Delegate curation failed; deterministic selection was used. ${failureReason}`),
|
|
@@ -3170,6 +1423,11 @@ function handleDelegateRuntimeFailure(input) {
|
|
|
3170
1423
|
omission_count: input.omissionCount,
|
|
3171
1424
|
failure_reason: failureReason,
|
|
3172
1425
|
},
|
|
1426
|
+
observability: input.observability ?? {
|
|
1427
|
+
curator_ms: null,
|
|
1428
|
+
finalizer_ms: null,
|
|
1429
|
+
delegate_packet_size: 0,
|
|
1430
|
+
},
|
|
3173
1431
|
};
|
|
3174
1432
|
}
|
|
3175
1433
|
function formatOmissionForDelegateFailure(omission) {
|
|
@@ -3178,6 +1436,34 @@ function formatOmissionForDelegateFailure(omission) {
|
|
|
3178
1436
|
: boundContextBuilderPathList(omission.paths).join(", ");
|
|
3179
1437
|
return `${omission.reason} (${paths}): ${sanitizeDiagnosticText(omission.message)}`;
|
|
3180
1438
|
}
|
|
1439
|
+
// Bounds for the aggregated delegate-failure omission list. Entries past the
|
|
1440
|
+
// cap are dropped with an explicit `(+N more omissions)` marker and the total
|
|
1441
|
+
// count always leads the message, so truncation is never silent.
|
|
1442
|
+
const DELEGATE_FAILURE_MAX_OMISSION_ENTRIES = 6;
|
|
1443
|
+
const DELEGATE_FAILURE_MAX_ENTRY_TEXT_LENGTH = 240;
|
|
1444
|
+
const DELEGATE_FAILURE_MAX_ENTRY_PATHS = 5;
|
|
1445
|
+
function formatOmissionsForDelegateFailure(omissions) {
|
|
1446
|
+
const total = omissions.length;
|
|
1447
|
+
const shown = omissions
|
|
1448
|
+
.slice(0, DELEGATE_FAILURE_MAX_OMISSION_ENTRIES)
|
|
1449
|
+
.map(formatOmissionEntryForDelegateFailure);
|
|
1450
|
+
const droppedSuffix = total > shown.length ? ` (+${total - shown.length} more omissions)` : "";
|
|
1451
|
+
return `${total} omission${total === 1 ? "" : "s"}: ${shown.join("; ")}${droppedSuffix}`;
|
|
1452
|
+
}
|
|
1453
|
+
function formatOmissionEntryForDelegateFailure(omission) {
|
|
1454
|
+
// Redaction happens per entry, BEFORE assembly into the aggregate message:
|
|
1455
|
+
// paths through boundContextBuilderPathList (secret-path redaction) and free
|
|
1456
|
+
// text through the marked bounded sanitizer, so no entry can smuggle secret
|
|
1457
|
+
// material into the joined reason and no entry ends in an unmarked "...".
|
|
1458
|
+
const allPaths = omission.paths ?? [];
|
|
1459
|
+
const shownPaths = boundContextBuilderPathList(allPaths, DELEGATE_FAILURE_MAX_ENTRY_PATHS);
|
|
1460
|
+
const pathText = shownPaths.length === 0
|
|
1461
|
+
? "unknown focused path"
|
|
1462
|
+
: allPaths.length > shownPaths.length
|
|
1463
|
+
? `${shownPaths.join(", ")} (+${allPaths.length - shownPaths.length} more paths)`
|
|
1464
|
+
: shownPaths.join(", ");
|
|
1465
|
+
return `${sanitizeDelegateFailureText(omission.reason, DELEGATE_FAILURE_MAX_ENTRY_TEXT_LENGTH)} (${pathText}): ${sanitizeDelegateFailureText(omission.message, DELEGATE_FAILURE_MAX_ENTRY_TEXT_LENGTH)}`;
|
|
1466
|
+
}
|
|
3181
1467
|
function rangeCoveredByRanges(target, ranges) {
|
|
3182
1468
|
let nextLine = target.start_line;
|
|
3183
1469
|
for (const range of [...ranges].sort((left, right) => left.start_line - right.start_line || left.end_line - right.end_line)) {
|
|
@@ -3239,6 +1525,9 @@ function delegateCompletenessFailure(input) {
|
|
|
3239
1525
|
if (materialWarnings.length > 0) {
|
|
3240
1526
|
return `delegate warnings reported material evidence gaps: ${materialWarnings.join("; ")}`;
|
|
3241
1527
|
}
|
|
1528
|
+
return requiredFocusCoverageFailure(input);
|
|
1529
|
+
}
|
|
1530
|
+
function requiredFocusCoverageFailure(input) {
|
|
3242
1531
|
const required = requiredFocusEvidenceSelections(input);
|
|
3243
1532
|
if (required.length === 0) {
|
|
3244
1533
|
return undefined;
|
|
@@ -3263,6 +1552,53 @@ function delegateCompletenessFailure(input) {
|
|
|
3263
1552
|
.join(",")}`)
|
|
3264
1553
|
.join("; ")}`;
|
|
3265
1554
|
}
|
|
1555
|
+
// Curator-only post-repair guard. Unlike the finalizer branch, the curator-only
|
|
1556
|
+
// path does not fold required-focus ranges, so its baseline is the curator's
|
|
1557
|
+
// own pre-repair selection. Repair is allowed to:
|
|
1558
|
+
// - COMPACT a required-focus file's ranges to fit budget pressure (the file
|
|
1559
|
+
// stays in the bundle, just sliced — the documented focus-slicing behavior); and
|
|
1560
|
+
// - DROP a degenerate (whitespace-only) selected slice entirely (server-dropped).
|
|
1561
|
+
// The HIGH failure mode this guards is repair evicting a NON-degenerate
|
|
1562
|
+
// required-focus candidate ENTIRELY: a focus file with real content that the
|
|
1563
|
+
// curator selected and that survived reconstruction must not silently vanish
|
|
1564
|
+
// from the post-repair selection. Whole-file eviction of non-empty required
|
|
1565
|
+
// focus fails closed.
|
|
1566
|
+
function postRepairRequiredFocusEvictionFailure(input) {
|
|
1567
|
+
// Root-aware required-focus key set: a multi-root run can carry the same
|
|
1568
|
+
// relative path under two roots, only one of which is required focus. Keying
|
|
1569
|
+
// the required set by bare path would misclassify the non-required twin.
|
|
1570
|
+
const requiredKeys = new Set(input.candidates
|
|
1571
|
+
.filter((candidate) => candidate.focus_requested === true &&
|
|
1572
|
+
(candidate.required_focus_ranges?.length ?? 0) > 0 &&
|
|
1573
|
+
candidateMatchesFocusPath(candidate, input.focusPaths))
|
|
1574
|
+
.map((candidate) => `${candidate.root_index}:${candidate.path}`));
|
|
1575
|
+
if (requiredKeys.size === 0) {
|
|
1576
|
+
return undefined;
|
|
1577
|
+
}
|
|
1578
|
+
const selectedFileKey = (file) => `${file.root_index}:${file.path}`;
|
|
1579
|
+
// Pre-repair required-focus files that carried real (non-whitespace) content.
|
|
1580
|
+
// A whitespace-only focus slice is legitimately server-dropped by repair, so
|
|
1581
|
+
// exempt it — only its eviction is a no-op, never a hard failure.
|
|
1582
|
+
const preRepairRequired = new Map();
|
|
1583
|
+
for (const file of input.preRepairSelected) {
|
|
1584
|
+
if (requiredKeys.has(selectedFileKey(file)) &&
|
|
1585
|
+
file.ranges.length > 0 &&
|
|
1586
|
+
file.content.trim().length > 0) {
|
|
1587
|
+
preRepairRequired.set(selectedFileKey(file), file);
|
|
1588
|
+
}
|
|
1589
|
+
}
|
|
1590
|
+
const postRepairKeys = new Set(input.postRepairSelected
|
|
1591
|
+
.filter((file) => file.ranges.length > 0)
|
|
1592
|
+
.map(selectedFileKey));
|
|
1593
|
+
const evicted = [...preRepairRequired.values()]
|
|
1594
|
+
.filter((file) => !postRepairKeys.has(selectedFileKey(file)))
|
|
1595
|
+
.map((file) => `root ${file.root_index}: ${file.path}`);
|
|
1596
|
+
if (evicted.length === 0) {
|
|
1597
|
+
return undefined;
|
|
1598
|
+
}
|
|
1599
|
+
return `repair evicted required-focus evidence the curator selection carried: ${evicted.join("; ")}`;
|
|
1600
|
+
}
|
|
1601
|
+
export const postRepairRequiredFocusEvictionFailureForTest = postRepairRequiredFocusEvictionFailure;
|
|
3266
1602
|
function warningReferencesSelectedDelegateCandidate(input) {
|
|
3267
1603
|
const selectedCandidateIds = new Set(input.selected.map((selection) => selection.candidate_id));
|
|
3268
1604
|
const selectedFiles = input.candidates
|
|
@@ -3281,6 +1617,37 @@ function warningReferencesSelectedDelegateCandidate(input) {
|
|
|
3281
1617
|
}));
|
|
3282
1618
|
return isStaleSelectedContextOmissionWarning(input.warning.toLowerCase(), selectedFiles);
|
|
3283
1619
|
}
|
|
1620
|
+
/**
|
|
1621
|
+
* Re-express a materialized (post-budget reconstruction) selection as
|
|
1622
|
+
* candidate-id-keyed proposal selections so completeness can be evaluated
|
|
1623
|
+
* against what actually survived the local reconstruction budget — not what
|
|
1624
|
+
* the finalizer merely requested. A candidate dropped by the budget guard is
|
|
1625
|
+
* absent here, so any required-focus range it carried reads as uncovered and
|
|
1626
|
+
* fails closed in delegateCompletenessFailure.
|
|
1627
|
+
*/
|
|
1628
|
+
function materializeFinalizerSelection(selected, candidates) {
|
|
1629
|
+
const candidateIdByKey = new Map();
|
|
1630
|
+
for (const candidate of candidates) {
|
|
1631
|
+
candidateIdByKey.set(`${candidate.root_index}:${candidate.path}`, candidate.id);
|
|
1632
|
+
}
|
|
1633
|
+
const materialized = [];
|
|
1634
|
+
for (const file of selected) {
|
|
1635
|
+
const candidateId = candidateIdByKey.get(`${file.root_index}:${file.path}`);
|
|
1636
|
+
if (candidateId == null) {
|
|
1637
|
+
continue;
|
|
1638
|
+
}
|
|
1639
|
+
materialized.push({
|
|
1640
|
+
candidate_id: candidateId,
|
|
1641
|
+
reason: "materialized post-budget selection",
|
|
1642
|
+
ranges: file.ranges.map((range) => ({
|
|
1643
|
+
start_line: range.start_line,
|
|
1644
|
+
end_line: range.end_line,
|
|
1645
|
+
reason: "materialized post-budget selection range",
|
|
1646
|
+
})),
|
|
1647
|
+
});
|
|
1648
|
+
}
|
|
1649
|
+
return materialized;
|
|
1650
|
+
}
|
|
3284
1651
|
function requiredFocusEvidenceSelections(input) {
|
|
3285
1652
|
if (input.focusPaths.length === 0) {
|
|
3286
1653
|
return [];
|
|
@@ -3455,10 +1822,139 @@ function augmentCuratorProposalWithDeterministicBaseline(proposal, packet, maxSe
|
|
|
3455
1822
|
function totalSelectedRangeLines(ranges) {
|
|
3456
1823
|
return ranges.reduce((total, range) => total + Math.max(0, range.end_line - range.start_line + 1), 0);
|
|
3457
1824
|
}
|
|
3458
|
-
|
|
1825
|
+
/**
|
|
1826
|
+
* Renders the server-enforced coverage obligations as explicit prompt rules.
|
|
1827
|
+
*
|
|
1828
|
+
* The obligations are already present in the serialized packet, but the packet
|
|
1829
|
+
* alone never told the delegate they were MANDATORY, so a schema-valid proposal
|
|
1830
|
+
* could miss one and be rejected by
|
|
1831
|
+
* {@link validateContextBuilderDelegateCoverageSelection}.
|
|
1832
|
+
*
|
|
1833
|
+
* Branch selection here tracks that validator's branch selection: it enters the
|
|
1834
|
+
* obligation branch on `obligations != null`, NOT on a non-empty target list.
|
|
1835
|
+
* Branching on `targets.length > 0` instead would render the satisfiable
|
|
1836
|
+
* evidence-class contract for a packet the validator rejects outright, i.e.
|
|
1837
|
+
* instruct the delegate to do something that provably cannot pass.
|
|
1838
|
+
*/
|
|
1839
|
+
function packetCoverageObligationRules(packet) {
|
|
1840
|
+
if (packet == null || typeof packet !== "object") {
|
|
1841
|
+
return [];
|
|
1842
|
+
}
|
|
1843
|
+
const policy = packet["coverage_policy"];
|
|
1844
|
+
if (policy == null || typeof policy !== "object") {
|
|
1845
|
+
return [];
|
|
1846
|
+
}
|
|
1847
|
+
const policyRecord = policy;
|
|
1848
|
+
if (policyRecord["policy_id"] !== "review_deep_v1") {
|
|
1849
|
+
return [];
|
|
1850
|
+
}
|
|
1851
|
+
const requiredClasses = policyRecord["required_when_available"];
|
|
1852
|
+
if (!Array.isArray(requiredClasses) || requiredClasses.length === 0) {
|
|
1853
|
+
return [];
|
|
1854
|
+
}
|
|
1855
|
+
const obligations = policyRecord["obligations"];
|
|
1856
|
+
if (obligations != null && typeof obligations === "object") {
|
|
1857
|
+
const targets = obligations["targets"];
|
|
1858
|
+
if (!Array.isArray(targets) || targets.length === 0) {
|
|
1859
|
+
// The validator rejects a present-but-empty target list unconditionally,
|
|
1860
|
+
// so no instruction rendered here could be satisfied. Stay silent rather
|
|
1861
|
+
// than describe a contract the delegate cannot meet. (The server building
|
|
1862
|
+
// a packet it will always reject is a separate defect; do not paper over
|
|
1863
|
+
// it by rendering the evidence-class rules the validator will not apply.)
|
|
1864
|
+
return [];
|
|
1865
|
+
}
|
|
1866
|
+
return [
|
|
1867
|
+
"Coverage obligations (mandatory, server-enforced):",
|
|
1868
|
+
"- packet.coverage_policy.obligations.targets is required coverage, not a suggestion. Server validation rejects your output if any obligation below is unmet, even when every other rule is satisfied.",
|
|
1869
|
+
"- For EVERY target: selected MUST contain that target's candidate_id with at least one range that overlaps one of that target's ranges.",
|
|
1870
|
+
"- For EVERY target whose regression_links is non-empty: selected MUST also contain at least ONE of those links' candidate_id with an overlapping range. One linked candidate per target is sufficient; you do not need all of them.",
|
|
1871
|
+
"- Obligations hold even when the task reads narrower than the obligation set. Add the obligation candidates alongside your task-driven picks rather than dropping them.",
|
|
1872
|
+
];
|
|
1873
|
+
}
|
|
1874
|
+
const classes = requiredClasses.filter((entry) => typeof entry === "string");
|
|
1875
|
+
if (classes.length === 0) {
|
|
1876
|
+
return [];
|
|
1877
|
+
}
|
|
1878
|
+
return [
|
|
1879
|
+
"Coverage obligations (mandatory, server-enforced):",
|
|
1880
|
+
`- packet.coverage_policy.required_when_available lists required evidence classes: ${classes.join(", ")}.`,
|
|
1881
|
+
"- For every listed class that ANY packet candidate carries in its evidence_classes, selected MUST include at least one candidate carrying that class. Server validation rejects your output otherwise.",
|
|
1882
|
+
];
|
|
1883
|
+
}
|
|
1884
|
+
/**
|
|
1885
|
+
* Renders the finalizer scoring contract.
|
|
1886
|
+
*
|
|
1887
|
+
* `min_score` is a server-side gate
|
|
1888
|
+
* ({@link validateContextBuilderFinalizerOutput}); before this it was never
|
|
1889
|
+
* sent to the model, so the finalizer was asked to clear a bar it could not
|
|
1890
|
+
* see, on a scale that was never defined. The rubric below is deliberately
|
|
1891
|
+
* anchored so that an approving score lands at or above the threshold, and it
|
|
1892
|
+
* names the dominant real-world failure mode: penalizing the packet for
|
|
1893
|
+
* evidence the packet was never meant to contain.
|
|
1894
|
+
*/
|
|
1895
|
+
function finalizerScoringRules(minScore) {
|
|
1896
|
+
if (minScore == null) {
|
|
1897
|
+
return [];
|
|
1898
|
+
}
|
|
1899
|
+
// A threshold of 0 cannot reject anything (the gate is `score < minScore`), so
|
|
1900
|
+
// the usual "below X, do not approve" band would render a range no score can
|
|
1901
|
+
// occupy and instruct the delegate to "honestly reach 0". State the degenerate
|
|
1902
|
+
// case plainly instead of rendering an impossible rule.
|
|
1903
|
+
const scoreBands = minScore <= 0
|
|
1904
|
+
? [
|
|
1905
|
+
`- score does not gate this request (any score clears a minimum of ${minScore}); express insufficiency through approved:false and concrete omissions, never through a low score.`,
|
|
1906
|
+
]
|
|
1907
|
+
: [
|
|
1908
|
+
"- score rates ONLY whether the `selected` you are returning is sufficient, correct local evidence for packet.task, judged against the evidence actually present in this packet:",
|
|
1909
|
+
// Bands are stated relative to the live threshold rather than as fixed
|
|
1910
|
+
// ranges. Fixed bands (9-10 / 6-8 / 0-5) left the shipped 8.5 default
|
|
1911
|
+
// in an undefined gap, and at a lower configured threshold they
|
|
1912
|
+
// labelled the passing score as deficient — telling the delegate to
|
|
1913
|
+
// approve while self-reporting a score whose own band calls the
|
|
1914
|
+
// selection inadequate.
|
|
1915
|
+
` - ${minScore}-10: selected is sufficient for the task; no packet evidence the task requires was left out.`,
|
|
1916
|
+
` - below ${minScore}: selected is wrong, or omits locally-available packet evidence the task needs. Do not approve.`,
|
|
1917
|
+
`- If you would approve, return a score at or above ${minScore}. If you cannot honestly reach ${minScore}, set approved:false and list concrete omissions rather than approving with a failing score.`,
|
|
1918
|
+
];
|
|
1919
|
+
return [
|
|
1920
|
+
"Scoring (server-enforced):",
|
|
1921
|
+
`- score >= ${minScore} clears the score gate; below ${minScore} fails the whole request regardless of everything else. Clearing the score gate is necessary but NOT sufficient — approved, omissions, warnings, selections, coverage, and budget are each validated separately.`,
|
|
1922
|
+
...scoreBands,
|
|
1923
|
+
"- Do NOT lower score for evidence that is absent from the packet, outside the bounded task, or that you merely suspect exists elsewhere in the repository. The packet is intentionally bounded and is the only source of truth for this call.",
|
|
1924
|
+
// The server scans warnings with a keyword-proximity matcher
|
|
1925
|
+
// (isMaterialEvidenceGapWarning) that has no negation handling, so an
|
|
1926
|
+
// affirmative all-clear is rejected exactly like a real gap report. Name the
|
|
1927
|
+
// exact fatal phrasings; a partial description reads as authoritative and is
|
|
1928
|
+
// worse than none.
|
|
1929
|
+
"- warnings carry speculative or out-of-scope notes ONLY, and the server scans them for evidence-gap phrasing. Do not mention required, requested, or focused evidence in warnings at all — not even to state that nothing is missing — and never phrase a warning as 'outside the selected range(s)' or as 'selected ... not present/not selectable/unselectable'. That scan is a keyword-proximity match with no negation handling, so an affirmative all-clear is rejected exactly like a real gap report.",
|
|
1930
|
+
"- If required evidence is genuinely missing, set approved:false with concrete omissions. Never route a real coverage gap into warnings.",
|
|
1931
|
+
];
|
|
1932
|
+
}
|
|
1933
|
+
/**
|
|
1934
|
+
* Single source for every score exemplar the finalizer prompt shows.
|
|
1935
|
+
*
|
|
1936
|
+
* The shape example, the rubric bands, and the minimum-output example must all
|
|
1937
|
+
* derive from the live threshold: hardcoding them let the prompt display a
|
|
1938
|
+
* score that its own rubric did not cover, and (at `min_score` above the
|
|
1939
|
+
* hardcoded value — 9.25 is schema-legal) an example that would fail the very
|
|
1940
|
+
* gate it was illustrating.
|
|
1941
|
+
*/
|
|
1942
|
+
function finalizerExampleScore(minScore) {
|
|
1943
|
+
// A non-gating threshold gets the neutral fallback rather than its own value:
|
|
1944
|
+
// "emit the lowest score that still clears the gate" is the right anti-inflation
|
|
1945
|
+
// rule wherever a gate exists, but at 0 it would render `{"score":0,"approved":true}`
|
|
1946
|
+
// and an unsure finalizer copying that reports a 0-scored approval into
|
|
1947
|
+
// `finalizer_score` observability. A higher exemplar always clears a lower gate.
|
|
1948
|
+
return minScore == null || minScore <= 0 ? 9 : minScore;
|
|
1949
|
+
}
|
|
1950
|
+
export function renderDelegatePrompt(stage, packet, options) {
|
|
1951
|
+
// Diagnostic-array exemplars are non-empty on purpose: empty arrays cannot
|
|
1952
|
+
// communicate element types, and a live finalizer failure returned
|
|
1953
|
+
// object-shaped `omissions`. The angle-bracket entries are placeholders that
|
|
1954
|
+
// show each element is one plain string.
|
|
3459
1955
|
const outputShape = stage === "curator"
|
|
3460
|
-
? '{"selected":[{"candidate_id":"c:0:path","ranges":[{"start_line":1,"end_line":1,"reason":"why this range"}],"reason":"why this file"}],"warnings":[],"open_questions":[]}'
|
|
3461
|
-
:
|
|
1956
|
+
? '{"selected":[{"candidate_id":"c:0:path","ranges":[{"start_line":1,"end_line":1,"reason":"why this range"}],"reason":"why this file"}],"warnings":["<short plain-string caveat>"],"open_questions":["<short plain-string follow-up question>"]}'
|
|
1957
|
+
: `{"score":${finalizerExampleScore(options?.minScore)},"approved":true,"selected":[{"candidate_id":"c:0:path","ranges":[{"start_line":1,"end_line":1,"reason":"why this range"}],"reason":"why this file"}],"warnings":["<short plain-string caveat>"],"omissions":["<short plain string describing an omitted file or range; must be [] when approving>"]}`;
|
|
3462
1958
|
const stageRules = stage === "curator"
|
|
3463
1959
|
? [
|
|
3464
1960
|
"Curator-specific rules:",
|
|
@@ -3468,6 +1964,10 @@ export function renderDelegatePrompt(stage, packet) {
|
|
|
3468
1964
|
"- Use packet.deterministic_selection.selected as the safe starting point; keep relevant deterministic candidates unless the task clearly excludes them.",
|
|
3469
1965
|
"- Treat the minimum valid JSON example as a floor, not a complete packet. Add task-critical candidates from packet.candidates when deterministic_selection is sparse or omissions show important local evidence was budget-pruned.",
|
|
3470
1966
|
"- Do not return selected:[] to express uncertainty. Select the closest relevant local candidate and put caveats in warnings or open_questions.",
|
|
1967
|
+
// The validator requires string elements (SafeTextSchema) in every
|
|
1968
|
+
// diagnostic array; empty exemplars alone let models invent object
|
|
1969
|
+
// entries, which fail schema validation.
|
|
1970
|
+
"- warnings and open_questions are arrays of plain strings. Each element is one short plain string; objects inside these arrays are invalid and fail server validation.",
|
|
3471
1971
|
]
|
|
3472
1972
|
: [
|
|
3473
1973
|
"Finalizer-specific rules:",
|
|
@@ -3478,9 +1978,19 @@ export function renderDelegatePrompt(stage, packet) {
|
|
|
3478
1978
|
"- Treat packet.deterministic_selection.selected as server-ranked required baseline evidence. If the curator underselected or warned about sparse evidence, add relevant deterministic candidates/ranges that are present in packet.candidates instead of lowering the score for evidence the packet already provides.",
|
|
3479
1979
|
"- Do not reject solely because the curator warning says broader implementation may exist when deterministic_selection and packet.candidates contain concrete task-critical ranges for that concern.",
|
|
3480
1980
|
"- If the selected packet evidence is insufficient for the bounded task, set approved:false and explain concrete omissions; the server will treat that as delegate failure according to failure_policy.",
|
|
3481
|
-
"
|
|
1981
|
+
// "'may contain more detail' caveats" invited exactly the phrasing
|
|
1982
|
+
// isMaterialEvidenceGapWarning rejects outright
|
|
1983
|
+
// (/\boutside (?:the )?selected ranges?\b/), so obeying this rule got
|
|
1984
|
+
// the finalizer hard-failed. Steer the caveat at evidence absent from
|
|
1985
|
+
// the packet instead of at the selected ranges.
|
|
1986
|
+
"- If approving with score at or above the requested minimum, omissions MUST be []. Put speculative concerns about evidence absent from this packet in warnings instead.",
|
|
1987
|
+
// The observed live schema failure was object-shaped `omissions`;
|
|
1988
|
+
// state the element type explicitly instead of relying on exemplars.
|
|
1989
|
+
// (No open_questions here: the finalizer schema is strict and would
|
|
1990
|
+
// reject that key as unrecognized.)
|
|
1991
|
+
"- warnings and omissions are arrays of plain strings. Each element is one short plain string; objects inside these arrays are invalid and fail server validation.",
|
|
3482
1992
|
];
|
|
3483
|
-
const minimumOutput = buildMinimumDelegateOutput(stage, packet);
|
|
1993
|
+
const minimumOutput = buildMinimumDelegateOutput(stage, packet, options?.minScore);
|
|
3484
1994
|
return [
|
|
3485
1995
|
`You are the context_builder ${stage}.`,
|
|
3486
1996
|
`Prompt version: ${CONTEXT_BUILDER_DELEGATE_PROMPT_VERSION}.`,
|
|
@@ -3495,6 +2005,8 @@ export function renderDelegatePrompt(stage, packet) {
|
|
|
3495
2005
|
? "Use the excerpt, search_hits, deterministic_selection, omissions, and task fields as evidence; the packet intentionally omits full source content."
|
|
3496
2006
|
: "Use the excerpt, search_hits, curator_proposal, deterministic_selection, omissions, and task fields as evidence; the packet intentionally omits full source content.",
|
|
3497
2007
|
...stageRules,
|
|
2008
|
+
...(stage === "finalizer" ? finalizerScoringRules(options?.minScore) : []),
|
|
2009
|
+
...packetCoverageObligationRules(packet),
|
|
3498
2010
|
...(minimumOutput == null
|
|
3499
2011
|
? []
|
|
3500
2012
|
: [
|
|
@@ -3510,11 +2022,153 @@ export function renderDelegatePrompt(stage, packet) {
|
|
|
3510
2022
|
"Return the JSON object now. If candidates exist, selected must be non-empty.",
|
|
3511
2023
|
].join("\n");
|
|
3512
2024
|
}
|
|
3513
|
-
|
|
3514
|
-
|
|
2025
|
+
/**
|
|
2026
|
+
* True when `selections` would clear the packet's coverage obligations.
|
|
2027
|
+
*
|
|
2028
|
+
* Mirrors {@link validateContextBuilderDelegateCoverageSelection}: every target
|
|
2029
|
+
* needs an overlapping selected range, and a target with regression links needs
|
|
2030
|
+
* an overlapping range from at least ONE of them (the link check is disjunctive).
|
|
2031
|
+
*/
|
|
2032
|
+
function selectionsSatisfyPacketObligations(selections, packet) {
|
|
2033
|
+
if (packet == null || typeof packet !== "object") {
|
|
2034
|
+
return true;
|
|
2035
|
+
}
|
|
2036
|
+
const policy = packet["coverage_policy"];
|
|
2037
|
+
if (policy == null || typeof policy !== "object") {
|
|
2038
|
+
return true;
|
|
2039
|
+
}
|
|
2040
|
+
const policyRecord = policy;
|
|
2041
|
+
if (policyRecord["policy_id"] !== "review_deep_v1") {
|
|
2042
|
+
return true;
|
|
2043
|
+
}
|
|
2044
|
+
const requiredClasses = policyRecord["required_when_available"];
|
|
2045
|
+
if (!Array.isArray(requiredClasses) || requiredClasses.length === 0) {
|
|
2046
|
+
return true;
|
|
2047
|
+
}
|
|
2048
|
+
const obligations = policyRecord["obligations"];
|
|
2049
|
+
if (obligations == null || typeof obligations !== "object") {
|
|
2050
|
+
// The validator does NOT concede this branch: it falls through to the
|
|
2051
|
+
// evidence-class check and still rejects a selection that omits an
|
|
2052
|
+
// available required class. Returning true here would advertise an example
|
|
2053
|
+
// the validator rejects. Unreachable through the current packet builder
|
|
2054
|
+
// (normalizeCoveragePolicy attaches obligations for every review_deep_v1
|
|
2055
|
+
// policy), but the two must not disagree — that divergence is the bug
|
|
2056
|
+
// generator this whole change exists to close.
|
|
2057
|
+
const candidates = packet["candidates"];
|
|
2058
|
+
if (!Array.isArray(candidates)) {
|
|
2059
|
+
return true;
|
|
2060
|
+
}
|
|
2061
|
+
const selectedIds = new Set(selections.map((selection) => selection.candidate_id));
|
|
2062
|
+
const evidenceClassesOf = (candidate) => {
|
|
2063
|
+
if (candidate == null || typeof candidate !== "object") {
|
|
2064
|
+
return [];
|
|
2065
|
+
}
|
|
2066
|
+
const classes = candidate["evidence_classes"];
|
|
2067
|
+
return Array.isArray(classes) ? classes : [];
|
|
2068
|
+
};
|
|
2069
|
+
return requiredClasses.every((evidenceClass) => {
|
|
2070
|
+
const available = candidates.some((candidate) => evidenceClassesOf(candidate).includes(evidenceClass));
|
|
2071
|
+
if (!available) {
|
|
2072
|
+
return true;
|
|
2073
|
+
}
|
|
2074
|
+
return candidates.some((candidate) => {
|
|
2075
|
+
const id = candidate != null && typeof candidate === "object"
|
|
2076
|
+
? candidate["id"]
|
|
2077
|
+
: undefined;
|
|
2078
|
+
return (typeof id === "string" &&
|
|
2079
|
+
selectedIds.has(id) &&
|
|
2080
|
+
evidenceClassesOf(candidate).includes(evidenceClass));
|
|
2081
|
+
});
|
|
2082
|
+
});
|
|
2083
|
+
}
|
|
2084
|
+
const targets = obligations["targets"];
|
|
2085
|
+
if (!Array.isArray(targets)) {
|
|
2086
|
+
return true;
|
|
2087
|
+
}
|
|
2088
|
+
if (targets.length === 0) {
|
|
2089
|
+
// The validator rejects a present-but-empty target list unconditionally, so
|
|
2090
|
+
// no example can be valid.
|
|
2091
|
+
return false;
|
|
2092
|
+
}
|
|
2093
|
+
const selectedById = new Map(selections.map((selection) => [selection.candidate_id, selection.ranges]));
|
|
2094
|
+
const covers = (value) => {
|
|
2095
|
+
if (value == null || typeof value !== "object") {
|
|
2096
|
+
return false;
|
|
2097
|
+
}
|
|
2098
|
+
const record = value;
|
|
2099
|
+
const candidateId = record["candidate_id"];
|
|
2100
|
+
if (typeof candidateId !== "string") {
|
|
2101
|
+
return false;
|
|
2102
|
+
}
|
|
2103
|
+
const selectedRanges = selectedById.get(candidateId);
|
|
2104
|
+
if (selectedRanges == null || selectedRanges.length === 0) {
|
|
2105
|
+
return false;
|
|
2106
|
+
}
|
|
2107
|
+
const obligationRanges = record["ranges"];
|
|
2108
|
+
if (!Array.isArray(obligationRanges) || obligationRanges.length === 0) {
|
|
2109
|
+
return false;
|
|
2110
|
+
}
|
|
2111
|
+
return obligationRanges.some((range) => {
|
|
2112
|
+
if (range == null || typeof range !== "object") {
|
|
2113
|
+
return false;
|
|
2114
|
+
}
|
|
2115
|
+
const start = range["start_line"];
|
|
2116
|
+
const end = range["end_line"];
|
|
2117
|
+
if (typeof start !== "number" || typeof end !== "number") {
|
|
2118
|
+
return false;
|
|
2119
|
+
}
|
|
2120
|
+
return selectedRanges.some((selectedRange) => selectedRange.start_line <= end && start <= selectedRange.end_line);
|
|
2121
|
+
});
|
|
2122
|
+
};
|
|
2123
|
+
return targets.every((target) => {
|
|
2124
|
+
if (!covers(target)) {
|
|
2125
|
+
return false;
|
|
2126
|
+
}
|
|
2127
|
+
const links = target["regression_links"];
|
|
2128
|
+
if (!Array.isArray(links) || links.length === 0) {
|
|
2129
|
+
return true;
|
|
2130
|
+
}
|
|
2131
|
+
return links.some((link) => covers(link));
|
|
2132
|
+
});
|
|
2133
|
+
}
|
|
2134
|
+
/** The packet's `budget.max_files`, when it carries a usable one. */
|
|
2135
|
+
function packetBudgetMaxFiles(packet) {
|
|
2136
|
+
if (packet == null || typeof packet !== "object") {
|
|
2137
|
+
return null;
|
|
2138
|
+
}
|
|
2139
|
+
const budget = packet["budget"];
|
|
2140
|
+
if (budget == null || typeof budget !== "object") {
|
|
2141
|
+
return null;
|
|
2142
|
+
}
|
|
2143
|
+
const maxFiles = budget["max_files"];
|
|
2144
|
+
return typeof maxFiles === "number" && Number.isFinite(maxFiles) && maxFiles > 0
|
|
2145
|
+
? maxFiles
|
|
2146
|
+
: null;
|
|
2147
|
+
}
|
|
2148
|
+
function buildMinimumDelegateOutput(stage, packet, minScore) {
|
|
2149
|
+
const allSelections = packetMinimumSelections(stage, packet);
|
|
2150
|
+
if (allSelections.length === 0) {
|
|
2151
|
+
return null;
|
|
2152
|
+
}
|
|
2153
|
+
// The example is advertised as "minimum valid" and a delegate may copy it
|
|
2154
|
+
// verbatim, so it must satisfy every gate the copy would face.
|
|
2155
|
+
//
|
|
2156
|
+
// packetMinimumSelections bounds by MAX_DELEGATE_MINIMUM_OUTPUT_SELECTIONS,
|
|
2157
|
+
// which is unrelated to the caller's budget: validateBudget rejects a
|
|
2158
|
+
// selection longer than budget.max_files, so an unbounded example was invalid
|
|
2159
|
+
// whenever max_files was the smaller limit. Apply the budget first, then judge
|
|
2160
|
+
// coverage on exactly what is advertised.
|
|
2161
|
+
const maxFiles = packetBudgetMaxFiles(packet);
|
|
2162
|
+
const minimumSelections = maxFiles == null ? allSelections : allSelections.slice(0, maxFiles);
|
|
3515
2163
|
if (minimumSelections.length === 0) {
|
|
3516
2164
|
return null;
|
|
3517
2165
|
}
|
|
2166
|
+
// Neither bound ranks obligations first, so a required target can fall outside
|
|
2167
|
+
// the cap — offering an example that fails coverage validation. Withhold the
|
|
2168
|
+
// example rather than advertise an invalid one.
|
|
2169
|
+
if (!selectionsSatisfyPacketObligations(minimumSelections, packet)) {
|
|
2170
|
+
return null;
|
|
2171
|
+
}
|
|
3518
2172
|
const selected = minimumSelections.map((selection) => ({
|
|
3519
2173
|
candidate_id: selection.candidate_id,
|
|
3520
2174
|
ranges: selection.ranges.map((range) => ({
|
|
@@ -3533,7 +2187,13 @@ function buildMinimumDelegateOutput(stage, packet) {
|
|
|
3533
2187
|
open_questions: [],
|
|
3534
2188
|
}
|
|
3535
2189
|
: {
|
|
3536
|
-
|
|
2190
|
+
// This example is offered to an UNSURE finalizer, and it carries a
|
|
2191
|
+
// deliberately minimal selection. Stamping it `score: 10` would pair
|
|
2192
|
+
// the top of the scale with the floor of the evidence and teach
|
|
2193
|
+
// "when uncertain, emit 10" — inflating the very self-score the gate
|
|
2194
|
+
// relies on. Emit the lowest score that still clears the gate, so the
|
|
2195
|
+
// minimum selection reads as the minimum passing answer.
|
|
2196
|
+
score: finalizerExampleScore(minScore),
|
|
3537
2197
|
approved: true,
|
|
3538
2198
|
selected,
|
|
3539
2199
|
warnings: [
|
|
@@ -3591,7 +2251,18 @@ function mergePacketSelections(selections) {
|
|
|
3591
2251
|
const merged = new Map();
|
|
3592
2252
|
for (const selection of selections) {
|
|
3593
2253
|
const ranges = merged.get(selection.candidate_id) ?? [];
|
|
3594
|
-
|
|
2254
|
+
// Deduplicate by bounds. Sources overlap by construction — a candidate in
|
|
2255
|
+
// deterministic_selection is normally also present in packet.candidates —
|
|
2256
|
+
// so concatenating produced the SAME range twice, and validateSelections
|
|
2257
|
+
// rejects a duplicate range outright. That made the advertised "minimum
|
|
2258
|
+
// valid JSON" example invalid in the ordinary case: a delegate copying it
|
|
2259
|
+
// was hard-failed by the server that offered it.
|
|
2260
|
+
for (const range of selection.ranges) {
|
|
2261
|
+
if (!ranges.some((existing) => existing.start_line === range.start_line &&
|
|
2262
|
+
existing.end_line === range.end_line)) {
|
|
2263
|
+
ranges.push(range);
|
|
2264
|
+
}
|
|
2265
|
+
}
|
|
3595
2266
|
merged.set(selection.candidate_id, ranges);
|
|
3596
2267
|
}
|
|
3597
2268
|
return [...merged].map(([candidate_id, ranges]) => ({
|
|
@@ -3645,25 +2316,52 @@ function delegatePromptDiagnostic(packet) {
|
|
|
3645
2316
|
const deterministicCount = Array.isArray(deterministicSelected)
|
|
3646
2317
|
? deterministicSelected.length
|
|
3647
2318
|
: 0;
|
|
2319
|
+
// `curator_proposal` only exists on a finalizer packet (curator packets are
|
|
2320
|
+
// built before any proposal exists). Reporting `curator_selected=0` for a
|
|
2321
|
+
// curator packet described the packet's missing field, not the curator's
|
|
2322
|
+
// output, so every curator-stage diagnostic claimed an empty selection that
|
|
2323
|
+
// had in fact never been observed. Omit the field unless the packet carries a
|
|
2324
|
+
// proposal to count.
|
|
3648
2325
|
const proposal = record["curator_proposal"];
|
|
3649
2326
|
const proposalSelected = proposal != null && typeof proposal === "object"
|
|
3650
2327
|
? proposal["selected"]
|
|
3651
2328
|
: undefined;
|
|
3652
|
-
|
|
3653
|
-
|
|
3654
|
-
|
|
3655
|
-
|
|
2329
|
+
// `min` must report what the prompt actually carried, not whether a minimum
|
|
2330
|
+
// selection could be computed: buildMinimumDelegateOutput also withholds the
|
|
2331
|
+
// example when it would fail budget or coverage, and a withheld floor example
|
|
2332
|
+
// is exactly the condition worth seeing next to a delegate failure. Derive it
|
|
2333
|
+
// from the same call the prompt makes, at the stage this packet belongs to.
|
|
2334
|
+
const stage = Array.isArray(proposalSelected)
|
|
2335
|
+
? "finalizer"
|
|
2336
|
+
: "curator";
|
|
3656
2337
|
return [
|
|
3657
2338
|
`pv=${CONTEXT_BUILDER_DELEGATE_PROMPT_VERSION}`,
|
|
3658
2339
|
`candidates=${candidateCount}`,
|
|
3659
2340
|
`det=${deterministicCount}`,
|
|
3660
|
-
|
|
3661
|
-
|
|
2341
|
+
...(Array.isArray(proposalSelected)
|
|
2342
|
+
? [`curator_selected=${proposalSelected.length}`]
|
|
2343
|
+
: []),
|
|
2344
|
+
`min=${buildMinimumDelegateOutput(stage, packet) != null}`,
|
|
3662
2345
|
].join("; ");
|
|
3663
2346
|
}
|
|
3664
2347
|
function failureReasonFromCall(stage, call) {
|
|
3665
2348
|
return `${stage} ${call.reason}: ${call.message}`;
|
|
3666
2349
|
}
|
|
2350
|
+
/**
|
|
2351
|
+
* Safe-scalar score context for finalizer rejections. A not-approved rejection
|
|
2352
|
+
* previously carried no score at all; surfacing the reported score next to the
|
|
2353
|
+
* configured min_score makes rejection and under-score failures diagnosable
|
|
2354
|
+
* from the failure reason alone. Only a finite numeric `score` read from the
|
|
2355
|
+
* finalizer's raw JSON is ever included — never free-form provider text.
|
|
2356
|
+
*/
|
|
2357
|
+
function finalizerScoreDiagnostic(json, minScore) {
|
|
2358
|
+
const rawScore = json != null && typeof json === "object"
|
|
2359
|
+
? json["score"]
|
|
2360
|
+
: undefined;
|
|
2361
|
+
return typeof rawScore === "number" && Number.isFinite(rawScore)
|
|
2362
|
+
? `; finalizer_score=${rawScore}; min_score=${minScore}`
|
|
2363
|
+
: `; min_score=${minScore}`;
|
|
2364
|
+
}
|
|
3667
2365
|
function failureReasonFromError(stage, err, diagnostic) {
|
|
3668
2366
|
const message = err instanceof Error ? err.message : "unknown delegate error";
|
|
3669
2367
|
return diagnostic == null
|
|
@@ -3676,6 +2374,50 @@ function delegateWarning(code, message) {
|
|
|
3676
2374
|
message: sanitizeDiagnosticText(message),
|
|
3677
2375
|
};
|
|
3678
2376
|
}
|
|
2377
|
+
// Bounded per-term detail in the search-cap coverage warning. Full per-term
|
|
2378
|
+
// rows (up to the public 20-term cap) persist in manifest/summary
|
|
2379
|
+
// `search_coverage`; the warning message stays a compact summary.
|
|
2380
|
+
const MAX_SEARCH_CAP_WARNING_TERM_DETAILS = 6;
|
|
2381
|
+
/**
|
|
2382
|
+
* Builds the receipt-visible explanation for a MATERIAL search cap. Returns
|
|
2383
|
+
* undefined when the cap was not hit or coverage proves it non-material.
|
|
2384
|
+
* The warning `code` is the stable materiality reason string
|
|
2385
|
+
* (`search_cap_unresolved_coverage` / `search_cap_unsearched_candidates`);
|
|
2386
|
+
* classification itself rides on the cap event in the status reducer, so this
|
|
2387
|
+
* warning is explanatory and never double-counts toward `partial`.
|
|
2388
|
+
* Terms are referenced by `search_term_index`, never by value, so the warning
|
|
2389
|
+
* cannot leak caller term text; the full (sanitized) rows live in
|
|
2390
|
+
* `search_coverage`. The first unsearched candidate's rank/path is not
|
|
2391
|
+
* reported: T1's coverage counters are aggregate-only by design and the
|
|
2392
|
+
* caller-phase scheduler is term-major round-robin, so the searched set is not
|
|
2393
|
+
* an ordering prefix and no per-candidate identity is cheaply available.
|
|
2394
|
+
*/
|
|
2395
|
+
function searchCapCoverageWarning(searchCapHit, coverage) {
|
|
2396
|
+
if (!searchCapHit) {
|
|
2397
|
+
return undefined;
|
|
2398
|
+
}
|
|
2399
|
+
const materiality = evaluateSearchCapMateriality(coverage);
|
|
2400
|
+
if (!materiality.material) {
|
|
2401
|
+
return undefined;
|
|
2402
|
+
}
|
|
2403
|
+
const incompleteTerms = coverage.caller_terms.filter((term) => !term.scan_complete);
|
|
2404
|
+
const termDetails = incompleteTerms
|
|
2405
|
+
.slice(0, MAX_SEARCH_CAP_WARNING_TERM_DETAILS)
|
|
2406
|
+
.map((term) => `#${term.search_term_index}(hits=${term.hit_count})`)
|
|
2407
|
+
.join(", ");
|
|
2408
|
+
const hiddenIncomplete = incompleteTerms.length - Math.min(incompleteTerms.length, MAX_SEARCH_CAP_WARNING_TERM_DETAILS);
|
|
2409
|
+
const callerSummary = coverage.caller_term_count === 0
|
|
2410
|
+
? "caller terms 0"
|
|
2411
|
+
: `caller terms ${coverage.caller_term_count} (${incompleteTerms.length} incomplete${termDetails.length === 0 ? "" : `: ${termDetails}`}${hiddenIncomplete > 0 ? `, +${hiddenIncomplete} more` : ""}; ${coverage.caller_record_count} records)`;
|
|
2412
|
+
return {
|
|
2413
|
+
code: materiality.reason,
|
|
2414
|
+
message: `search cap ended discovery before coverage was proven: searched ` +
|
|
2415
|
+
`${coverage.searched_candidate_count}/${coverage.eligible_candidate_count} eligible candidates ` +
|
|
2416
|
+
`(${coverage.unsearched_candidate_count} unsearched); ${callerSummary}; ` +
|
|
2417
|
+
`derived terms ${coverage.derived_term_count} (${coverage.derived_record_count} records); ` +
|
|
2418
|
+
`full per-term coverage is in manifest.json search_coverage`,
|
|
2419
|
+
};
|
|
2420
|
+
}
|
|
3679
2421
|
function reconcileContextBuilderWarnings(input) {
|
|
3680
2422
|
if (input.warnings.length === 0) {
|
|
3681
2423
|
return [];
|