clinkx 1.0.0 → 1.1.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 +86 -0
- package/README.md +72 -0
- package/clinkx-workflows/dist/chairman-knobs.d.ts +24 -0
- package/clinkx-workflows/dist/chairman-knobs.js +27 -0
- package/clinkx-workflows/dist/chairman-knobs.js.map +1 -0
- package/clinkx-workflows/dist/clink-client.d.ts +10 -0
- package/clinkx-workflows/dist/clink-client.js +39 -1
- package/clinkx-workflows/dist/clink-client.js.map +1 -1
- package/clinkx-workflows/dist/config.d.ts +29 -1
- package/clinkx-workflows/dist/config.js +35 -2
- package/clinkx-workflows/dist/config.js.map +1 -1
- package/clinkx-workflows/dist/context-preflight.d.ts +5 -0
- package/clinkx-workflows/dist/context-preflight.js.map +1 -1
- package/clinkx-workflows/dist/council-execution-policy.d.ts +68 -0
- package/clinkx-workflows/dist/council-execution-policy.js +110 -0
- package/clinkx-workflows/dist/council-execution-policy.js.map +1 -0
- package/clinkx-workflows/dist/council-expander.d.ts +9 -8
- package/clinkx-workflows/dist/council-expander.js +18 -9
- package/clinkx-workflows/dist/council-expander.js.map +1 -1
- package/clinkx-workflows/dist/dashboard/activity-contract.d.ts +5557 -4
- package/clinkx-workflows/dist/dashboard/activity-contract.js +55 -3
- package/clinkx-workflows/dist/dashboard/activity-contract.js.map +1 -1
- package/clinkx-workflows/dist/dashboard/activity-reader.d.ts +9 -5
- package/clinkx-workflows/dist/dashboard/activity-reader.js +15 -2
- package/clinkx-workflows/dist/dashboard/activity-reader.js.map +1 -1
- package/clinkx-workflows/dist/dashboard/api-routes.d.ts +4 -0
- package/clinkx-workflows/dist/dashboard/api-routes.js +133 -25
- package/clinkx-workflows/dist/dashboard/api-routes.js.map +1 -1
- package/clinkx-workflows/dist/dashboard/context-builder-invocation-contract.d.ts +3865 -0
- package/clinkx-workflows/dist/dashboard/context-builder-invocation-contract.js +69 -0
- package/clinkx-workflows/dist/dashboard/context-builder-invocation-contract.js.map +1 -0
- package/clinkx-workflows/dist/dashboard/context-run-reader.d.ts +36 -2
- package/clinkx-workflows/dist/dashboard/context-run-reader.js +142 -19
- package/clinkx-workflows/dist/dashboard/context-run-reader.js.map +1 -1
- package/clinkx-workflows/dist/dashboard/contracts.d.ts +20027 -480
- package/clinkx-workflows/dist/dashboard/contracts.js +133 -6
- package/clinkx-workflows/dist/dashboard/contracts.js.map +1 -1
- package/clinkx-workflows/dist/dashboard/index.d.ts +4 -0
- package/clinkx-workflows/dist/dashboard/index.js +4 -0
- package/clinkx-workflows/dist/dashboard/index.js.map +1 -1
- package/clinkx-workflows/dist/dashboard/invocation-contract.d.ts +786 -0
- package/clinkx-workflows/dist/dashboard/invocation-contract.js +201 -0
- package/clinkx-workflows/dist/dashboard/invocation-contract.js.map +1 -0
- package/clinkx-workflows/dist/dashboard/lazy-content.d.ts +14 -1
- package/clinkx-workflows/dist/dashboard/lazy-content.js +53 -2
- package/clinkx-workflows/dist/dashboard/lazy-content.js.map +1 -1
- package/clinkx-workflows/dist/dashboard/native-transcript/contract.d.ts +90 -0
- package/clinkx-workflows/dist/dashboard/native-transcript/contract.js +31 -0
- package/clinkx-workflows/dist/dashboard/native-transcript/contract.js.map +1 -0
- package/clinkx-workflows/dist/dashboard/native-transcript/projection.d.ts +12 -0
- package/clinkx-workflows/dist/dashboard/native-transcript/projection.js +286 -0
- package/clinkx-workflows/dist/dashboard/native-transcript/projection.js.map +1 -0
- package/clinkx-workflows/dist/dashboard/progress-report-contract.d.ts +147 -4
- package/clinkx-workflows/dist/dashboard/progress-report-contract.js +11 -11
- package/clinkx-workflows/dist/dashboard/progress-report-contract.js.map +1 -1
- package/clinkx-workflows/dist/dashboard/progress-report-reader.js +4 -56
- package/clinkx-workflows/dist/dashboard/progress-report-reader.js.map +1 -1
- package/clinkx-workflows/dist/dashboard/progress-report-status-store.d.ts +2 -1
- package/clinkx-workflows/dist/dashboard/progress-report-status-store.js +6 -5
- package/clinkx-workflows/dist/dashboard/progress-report-status-store.js.map +1 -1
- package/clinkx-workflows/dist/dashboard/prompt-builder-library-contract.d.ts +194 -0
- package/clinkx-workflows/dist/dashboard/prompt-builder-library-contract.js +89 -0
- package/clinkx-workflows/dist/dashboard/prompt-builder-library-contract.js.map +1 -0
- package/clinkx-workflows/dist/dashboard/prompt-builder-library-store.d.ts +26 -0
- package/clinkx-workflows/dist/dashboard/prompt-builder-library-store.js +102 -0
- package/clinkx-workflows/dist/dashboard/prompt-builder-library-store.js.map +1 -0
- package/clinkx-workflows/dist/dashboard/request-admission.js +4 -0
- package/clinkx-workflows/dist/dashboard/request-admission.js.map +1 -1
- package/clinkx-workflows/dist/dashboard/scanner.d.ts +3 -0
- package/clinkx-workflows/dist/dashboard/scanner.js +52 -8
- package/clinkx-workflows/dist/dashboard/scanner.js.map +1 -1
- package/clinkx-workflows/dist/dashboard/server-factory.d.ts +14 -1
- package/clinkx-workflows/dist/dashboard/server-factory.js +73 -21
- package/clinkx-workflows/dist/dashboard/server-factory.js.map +1 -1
- package/clinkx-workflows/dist/dashboard/session-log-contract.d.ts +223 -56
- package/clinkx-workflows/dist/dashboard/session-log-contract.js +26 -0
- package/clinkx-workflows/dist/dashboard/session-log-contract.js.map +1 -1
- package/clinkx-workflows/dist/dashboard/session-log-journal.js +10 -3
- package/clinkx-workflows/dist/dashboard/session-log-journal.js.map +1 -1
- package/clinkx-workflows/dist/dashboard/session-log-redaction.d.ts +9 -1
- package/clinkx-workflows/dist/dashboard/session-log-redaction.js +793 -59
- package/clinkx-workflows/dist/dashboard/session-log-redaction.js.map +1 -1
- package/clinkx-workflows/dist/dashboard-ui/assets/index-JPHQVtn9.js +106 -0
- package/clinkx-workflows/dist/dashboard-ui/assets/style-DOMHk-j6.css +1 -0
- package/clinkx-workflows/dist/dashboard-ui/index.html +2 -2
- package/clinkx-workflows/dist/delegate-finalizer-policy.d.ts +26 -0
- package/clinkx-workflows/dist/delegate-finalizer-policy.js +72 -0
- package/clinkx-workflows/dist/delegate-finalizer-policy.js.map +1 -0
- package/clinkx-workflows/dist/delegate-signature.d.ts +14 -0
- package/clinkx-workflows/dist/delegate-signature.js +14 -0
- package/clinkx-workflows/dist/delegate-signature.js.map +1 -0
- package/clinkx-workflows/dist/delegate-validate.d.ts +32 -2
- package/clinkx-workflows/dist/delegate-validate.js +51 -0
- package/clinkx-workflows/dist/delegate-validate.js.map +1 -1
- package/clinkx-workflows/dist/engine.d.ts +113 -3
- package/clinkx-workflows/dist/engine.js +476 -80
- package/clinkx-workflows/dist/engine.js.map +1 -1
- package/clinkx-workflows/dist/errors.d.ts +22 -2
- package/clinkx-workflows/dist/errors.js +30 -2
- package/clinkx-workflows/dist/errors.js.map +1 -1
- package/clinkx-workflows/dist/fidelity.d.ts +47 -1
- package/clinkx-workflows/dist/fidelity.js +54 -1
- package/clinkx-workflows/dist/fidelity.js.map +1 -1
- package/clinkx-workflows/dist/fingerprint.d.ts +24 -6
- package/clinkx-workflows/dist/fingerprint.js +32 -9
- package/clinkx-workflows/dist/fingerprint.js.map +1 -1
- package/clinkx-workflows/dist/index.js +53 -1
- package/clinkx-workflows/dist/index.js.map +1 -1
- package/clinkx-workflows/dist/logger.d.ts +1 -1
- package/clinkx-workflows/dist/logger.js +17 -3
- package/clinkx-workflows/dist/logger.js.map +1 -1
- package/clinkx-workflows/dist/monotonic-clock.d.ts +19 -0
- package/clinkx-workflows/dist/monotonic-clock.js +22 -0
- package/clinkx-workflows/dist/monotonic-clock.js.map +1 -0
- package/clinkx-workflows/dist/plan-review/contract.d.ts +52 -52
- package/clinkx-workflows/dist/plan-review/publication.d.ts +4 -4
- package/clinkx-workflows/dist/plan-review/run-state.d.ts +16 -16
- package/clinkx-workflows/dist/roster-contract.d.ts +889 -0
- package/clinkx-workflows/dist/roster-contract.js +180 -0
- package/clinkx-workflows/dist/roster-contract.js.map +1 -0
- package/clinkx-workflows/dist/rosters.d.ts +14 -0
- package/clinkx-workflows/dist/rosters.js +169 -0
- package/clinkx-workflows/dist/rosters.js.map +1 -0
- package/clinkx-workflows/dist/run-health.d.ts +7 -0
- package/clinkx-workflows/dist/run-health.js +25 -0
- package/clinkx-workflows/dist/run-health.js.map +1 -0
- package/clinkx-workflows/dist/schema.d.ts +64 -0
- package/clinkx-workflows/dist/schema.js +24 -0
- package/clinkx-workflows/dist/schema.js.map +1 -1
- package/clinkx-workflows/dist/server.js +2 -0
- package/clinkx-workflows/dist/server.js.map +1 -1
- package/clinkx-workflows/dist/shutdown.d.ts +15 -2
- package/clinkx-workflows/dist/shutdown.js +60 -16
- package/clinkx-workflows/dist/shutdown.js.map +1 -1
- package/clinkx-workflows/dist/state-schema.d.ts +23 -0
- package/clinkx-workflows/dist/state.d.ts +5 -0
- package/clinkx-workflows/dist/state.js +128 -21
- package/clinkx-workflows/dist/state.js.map +1 -1
- package/clinkx-workflows/dist/stdio-watchdog.d.ts +52 -0
- package/clinkx-workflows/dist/stdio-watchdog.js +212 -0
- package/clinkx-workflows/dist/stdio-watchdog.js.map +1 -0
- package/clinkx-workflows/dist/types.d.ts +6 -0
- package/clinkx-workflows/dist/validators/council.d.ts +50 -50
- package/clinkx-workflows/dist/validators/plan-review.d.ts +3 -3
- package/clinkx-workflows/dist/workflow-tools.d.ts +70 -0
- package/clinkx-workflows/dist/workflow-tools.js +760 -168
- package/clinkx-workflows/dist/workflow-tools.js.map +1 -1
- package/clinkx-workflows/templates/council/answer-chairman.txt +3 -0
- package/clinkx-workflows/templates/council/answer-cross-reviewer.txt +3 -0
- package/clinkx-workflows/templates/council/answer-specialist.txt +3 -0
- package/clinkx-workflows/templates/council/chairman.txt +3 -0
- package/clinkx-workflows/templates/council/code-review-chairman.txt +3 -0
- package/clinkx-workflows/templates/council/code-review-cross-reviewer.txt +3 -0
- package/clinkx-workflows/templates/council/code-review-reviewer.txt +3 -0
- package/clinkx-workflows/templates/council/debug-chairman.txt +3 -0
- package/clinkx-workflows/templates/council/debug-cross-reviewer.txt +3 -0
- package/clinkx-workflows/templates/council/debug-debugger.txt +3 -0
- package/clinkx-workflows/templates/council/discover-analyst.txt +3 -0
- package/clinkx-workflows/templates/council/discover-chairman.txt +3 -0
- package/clinkx-workflows/templates/council/discover-cross-reviewer.txt +3 -0
- package/clinkx-workflows/templates/council/member.txt +3 -0
- package/clinkx-workflows/templates/council/plan-review-chairman.txt +3 -0
- package/clinkx-workflows/templates/council/plan-review-cross-reviewer.txt +3 -0
- package/clinkx-workflows/templates/council/plan-review-reviewer.txt +3 -0
- package/clinkx-workflows/templates/council/reviewer.txt +3 -0
- package/clinkx-workflows/workflows/council-answer.yaml +3 -0
- package/clinkx-workflows/workflows/council-code-review.yaml +3 -0
- package/clinkx-workflows/workflows/council-debug.yaml +3 -0
- package/clinkx-workflows/workflows/council-default.yaml +7 -0
- package/clinkx-workflows/workflows/council-discover.yaml +3 -0
- package/clinkx-workflows/workflows/council-plan-review.yaml +3 -0
- package/conf/adapters/agy.json +9 -2
- package/conf/adapters/claude.json +7 -0
- package/conf/adapters/codex.json +7 -0
- package/conf/adapters/glm.json +19 -3
- package/conf/adapters/grok.json +9 -2
- package/conf/adapters/hapi/agy.json +9 -2
- package/conf/adapters/hapi/claude.json +7 -0
- package/conf/adapters/hapi/codex.json +7 -0
- package/conf/adapters/hapi/glm.json +20 -4
- package/conf/rosters/default-council.json +21 -0
- package/dist/activity-journal.d.ts +17 -3
- package/dist/activity-journal.js +404 -79
- package/dist/activity-journal.js.map +1 -1
- package/dist/codex/agent-tools.js +36 -0
- package/dist/codex/agent-tools.js.map +1 -1
- package/dist/codex/app-server-client.d.ts +3 -0
- package/dist/codex/app-server-client.js +11 -0
- package/dist/codex/app-server-client.js.map +1 -1
- package/dist/codex/app-server-registry.d.ts +18 -0
- package/dist/codex/app-server-registry.js +88 -2
- package/dist/codex/app-server-registry.js.map +1 -1
- package/dist/codex/client-types.d.ts +7 -1
- package/dist/codex/protocol.d.ts +4 -4
- package/dist/config.d.ts +220 -23
- package/dist/config.js +104 -1
- package/dist/config.js.map +1 -1
- package/dist/context-builder/bundle.d.ts +13 -0
- package/dist/context-builder/bundle.js +46 -0
- package/dist/context-builder/bundle.js.map +1 -1
- package/dist/context-builder/delegate-finalizer-effective.d.ts +33 -0
- package/dist/context-builder/delegate-finalizer-effective.js +82 -0
- package/dist/context-builder/delegate-finalizer-effective.js.map +1 -0
- package/dist/context-builder/delegate-packet.d.ts +18 -0
- package/dist/context-builder/delegate-packet.js +73 -9
- package/dist/context-builder/delegate-packet.js.map +1 -1
- package/dist/context-builder/delegate-reconstruction.d.ts +29 -0
- package/dist/context-builder/delegate-reconstruction.js +110 -41
- package/dist/context-builder/delegate-reconstruction.js.map +1 -1
- package/dist/context-builder/delegate-validation.d.ts +31 -0
- package/dist/context-builder/delegate-validation.js +87 -10
- package/dist/context-builder/delegate-validation.js.map +1 -1
- package/dist/context-builder/delegate.d.ts +9 -0
- package/dist/context-builder/delegate.js +210 -59
- package/dist/context-builder/delegate.js.map +1 -1
- package/dist/context-builder/diagnostics.d.ts +1 -1
- package/dist/context-builder/diagnostics.js.map +1 -1
- package/dist/context-builder/discovery-progress.d.ts +25 -0
- package/dist/context-builder/discovery-progress.js +64 -0
- package/dist/context-builder/discovery-progress.js.map +1 -0
- package/dist/context-builder/discovery-watchdog.d.ts +15 -0
- package/dist/context-builder/discovery-watchdog.js +39 -0
- package/dist/context-builder/discovery-watchdog.js.map +1 -0
- package/dist/context-builder/discovery.d.ts +7 -0
- package/dist/context-builder/discovery.js +119 -7
- package/dist/context-builder/discovery.js.map +1 -1
- package/dist/context-builder/external-gaps.d.ts +73 -0
- package/dist/context-builder/external-gaps.js +248 -0
- package/dist/context-builder/external-gaps.js.map +1 -0
- package/dist/context-builder/globs.d.ts +1 -0
- package/dist/context-builder/globs.js +15 -0
- package/dist/context-builder/globs.js.map +1 -1
- package/dist/context-builder/handler.d.ts +42 -1
- package/dist/context-builder/handler.js +690 -62
- package/dist/context-builder/handler.js.map +1 -1
- package/dist/context-builder/invocation-sidecar.d.ts +41 -0
- package/dist/context-builder/invocation-sidecar.js +189 -0
- package/dist/context-builder/invocation-sidecar.js.map +1 -0
- package/dist/context-builder/lifecycle-contract.d.ts +51 -51
- package/dist/context-builder/mutation-lock.d.ts +32 -0
- package/dist/context-builder/mutation-lock.js +321 -0
- package/dist/context-builder/mutation-lock.js.map +1 -0
- package/dist/context-builder/review-coverage.d.ts +32 -0
- package/dist/context-builder/review-coverage.js +130 -23
- package/dist/context-builder/review-coverage.js.map +1 -1
- package/dist/context-builder/schema.d.ts +76 -1
- package/dist/context-builder/schema.js +21 -1
- package/dist/context-builder/schema.js.map +1 -1
- package/dist/context-builder/secrets.d.ts +31 -0
- package/dist/context-builder/secrets.js +865 -83
- package/dist/context-builder/secrets.js.map +1 -1
- package/dist/context-builder/status.d.ts +1 -1
- package/dist/context-builder/status.js +1 -0
- package/dist/context-builder/status.js.map +1 -1
- package/dist/context-builder/storage.d.ts +29 -5
- package/dist/context-builder/storage.js +311 -236
- package/dist/context-builder/storage.js.map +1 -1
- package/dist/context-builder/versions.d.ts +19 -4
- package/dist/context-builder/versions.js +19 -4
- package/dist/context-builder/versions.js.map +1 -1
- package/dist/context-builder/workflow-runner.d.ts +6 -0
- package/dist/context-builder/workflow-runner.js +363 -173
- package/dist/context-builder/workflow-runner.js.map +1 -1
- package/dist/dashboard-adapter-catalog.js +7 -0
- package/dist/dashboard-adapter-catalog.js.map +1 -1
- package/dist/dashboard-cli.d.ts +2 -0
- package/dist/dashboard-cli.js +40 -2
- package/dist/dashboard-cli.js.map +1 -1
- package/dist/dashboard-companion/client.d.ts +1 -0
- package/dist/dashboard-companion/client.js +22 -3
- package/dist/dashboard-companion/client.js.map +1 -1
- package/dist/dashboard-companion/protocol.js +4 -1
- package/dist/dashboard-companion/protocol.js.map +1 -1
- package/dist/dashboard-gateway/index.d.ts +1 -0
- package/dist/dashboard-gateway/index.js +33 -2
- package/dist/dashboard-gateway/index.js.map +1 -1
- package/dist/handler.js +5 -1
- package/dist/handler.js.map +1 -1
- package/dist/index.d.ts +18 -1
- package/dist/index.js +145 -7
- package/dist/index.js.map +1 -1
- package/dist/logger.d.ts +1 -1
- package/dist/logger.js +18 -3
- package/dist/logger.js.map +1 -1
- package/dist/parsers/agy-stream-json.d.ts +14 -0
- package/dist/parsers/agy-stream-json.js +172 -0
- package/dist/parsers/agy-stream-json.js.map +1 -0
- package/dist/parsers/extract.js +5 -1
- package/dist/parsers/extract.js.map +1 -1
- package/dist/parsers/observed-invocation.d.ts +20 -0
- package/dist/parsers/observed-invocation.js +42 -0
- package/dist/parsers/observed-invocation.js.map +1 -0
- package/dist/pipeline.d.ts +13 -0
- package/dist/pipeline.js +60 -10
- package/dist/pipeline.js.map +1 -1
- package/dist/progress-report/lock.d.ts +74 -0
- package/dist/progress-report/lock.js +343 -0
- package/dist/progress-report/lock.js.map +1 -0
- package/dist/progress-report/schema.d.ts +808 -0
- package/dist/progress-report/schema.js +119 -0
- package/dist/progress-report/schema.js.map +1 -0
- package/dist/progress-report/store.d.ts +66 -0
- package/dist/progress-report/store.js +766 -0
- package/dist/progress-report/store.js.map +1 -0
- package/dist/progress-report/tools.d.ts +20 -0
- package/dist/progress-report/tools.js +141 -0
- package/dist/progress-report/tools.js.map +1 -0
- package/dist/prompt-mode.d.ts +1 -1
- package/dist/prompt-mode.js +7 -2
- package/dist/prompt-mode.js.map +1 -1
- package/dist/runner.d.ts +7 -0
- package/dist/runner.js +10 -3
- package/dist/runner.js.map +1 -1
- package/dist/safety.d.ts +50 -0
- package/dist/safety.js +182 -1
- package/dist/safety.js.map +1 -1
- package/dist/schema.d.ts +8 -0
- package/dist/schema.js +20 -0
- package/dist/schema.js.map +1 -1
- package/dist/server.d.ts +6 -1
- package/dist/server.js +94 -8
- package/dist/server.js.map +1 -1
- package/dist/session-log-stream.d.ts +1 -1
- package/dist/session-log-stream.js +280 -33
- package/dist/session-log-stream.js.map +1 -1
- package/dist/stdio-activity.d.ts +28 -0
- package/dist/stdio-activity.js +72 -0
- package/dist/stdio-activity.js.map +1 -0
- package/package.json +2 -1
- package/clinkx-workflows/dist/dashboard-ui/assets/index-DOkMXTfB.js +0 -25
- package/clinkx-workflows/dist/dashboard-ui/assets/style-ChJsn0KD.css +0 -1
|
@@ -1,18 +1,25 @@
|
|
|
1
1
|
import { appendArtifactOnlyRootsToEnv, isArtifactOnlyAllowedRootEntry, isSamePathOrDescendant, parseAllowedRootsEnv, stripArtifactOnlyMarker, } from "../allowed-roots.js";
|
|
2
2
|
import { CancellationError, ExecutionError, InvalidParamsError, } from "../errors.js";
|
|
3
|
+
import { deriveInvocationAudit, resolveInvocationOverrides } from "../safety.js";
|
|
3
4
|
import { realpathSync } from "node:fs";
|
|
4
5
|
import { realpath as fsRealpath } from "node:fs/promises";
|
|
5
6
|
import path from "node:path";
|
|
6
7
|
import { AdapterRegistry as RuntimeAdapterRegistry } from "../registry.js";
|
|
7
8
|
import { logger } from "../logger.js";
|
|
9
|
+
import { invocationAuditV2PersistenceEnabled } from "../../clinkx-workflows/dist/config.js";
|
|
8
10
|
import { assertContextBuilderAnchorSufficiency } from "./anchors.js";
|
|
9
11
|
import { writeContextBuilderFinalArtifacts, } from "./bundle.js";
|
|
10
12
|
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
|
-
import { reconstructContextBuilderDelegateSelection } from "./delegate-reconstruction.js";
|
|
13
|
+
import { buildContextBuilderCuratorPacket, buildContextBuilderFinalizerPacket, CONTEXT_BUILDER_DELEGATE_PACKET_CAP_PROFILE_DEFAULTS, contextBuilderCandidateId, countDelegatePacketObligationTruncation, } from "./delegate-packet.js";
|
|
14
|
+
import { reconstructContextBuilderDelegateSelection, reconstructContextBuilderDelegateSelectionWithReport, } from "./delegate-reconstruction.js";
|
|
15
|
+
import { assertNoDisabledFinalizerInvocationOverrides, resolveContextBuilderFinalizerEffectiveEnabled, } from "./delegate-finalizer-effective.js";
|
|
13
16
|
import { runContextBuilderDelegateCall, } from "./delegate.js";
|
|
14
|
-
import {
|
|
17
|
+
import { recordContextInvocationAudit } from "./invocation-sidecar.js";
|
|
18
|
+
import { classifyFinalizerExternalGaps, } from "./external-gaps.js";
|
|
19
|
+
import { ContextBuilderFinalizerRejectionError, MAX_FINALIZER_EXTERNAL_GAPS, validateContextBuilderCuratorOutput, validateContextBuilderDelegateCoverageSelection, validateContextBuilderFinalizerOutput, } from "./delegate-validation.js";
|
|
15
20
|
import { discoverContextBuilderCandidates, mergeLineRanges, } from "./discovery.js";
|
|
21
|
+
import { createScanProgressPublisher } from "./discovery-progress.js";
|
|
22
|
+
import { withContextBuilderDiscoveryWatchdog } from "./discovery-watchdog.js";
|
|
16
23
|
import { evaluateSearchCapMateriality } from "./status.js";
|
|
17
24
|
import { boundContextBuilderPathList, isRequestedEvidenceOmission, sanitizeDelegateFailureText, sanitizeDiagnosticText, } from "./diagnostics.js";
|
|
18
25
|
import { compileContextBuilderGlobs, CONTEXT_BUILDER_DEFAULT_EXCLUDE_GLOBS, validateContextBuilderGlobs, } from "./globs.js";
|
|
@@ -25,18 +32,23 @@ import { appendLifecycleCompletionWarning } from "./lifecycle-recovery.js";
|
|
|
25
32
|
import { withContextBuilderWriteWatchdog } from "./write-watchdog.js";
|
|
26
33
|
import { createContextBuilderReceiptTextResponse, } from "./receipt.js";
|
|
27
34
|
import { generateContextBuilderRunId } from "./run-id.js";
|
|
28
|
-
import {
|
|
35
|
+
import { CONTEXT_BUILDER_PUBLIC_BUDGET_HARD_LIMITS, parseContextBuilderInput, } from "./schema.js";
|
|
29
36
|
import { evaluateContextCoverage, resolveContextCoveragePolicy, } from "./review-coverage.js";
|
|
30
37
|
import { selectContextBuilderFinalContext, } from "./selection.js";
|
|
31
|
-
import { cleanupContextBuilderIncompleteRun, createContextBuilderRun, } from "./storage.js";
|
|
38
|
+
import { cleanupContextBuilderIncompleteRun, createContextBuilderRun, withContextBuilderRunMutationLease, } from "./storage.js";
|
|
32
39
|
import { admitJournalContextGeneration } from "./session-log-identity.js";
|
|
33
40
|
import { estimateContextBuilderTokensFromUtf8Bytes } from "./token-estimate.js";
|
|
34
41
|
let defaultAdapterRegistry;
|
|
35
42
|
const artifactOnlyAllowedRoots = new Set();
|
|
36
|
-
const CONTEXT_BUILDER_DELEGATE_PROMPT_VERSION = "cb-delegate-
|
|
43
|
+
const CONTEXT_BUILDER_DELEGATE_PROMPT_VERSION = "cb-delegate-v6-gap-ancestors";
|
|
37
44
|
const MAX_DELEGATE_MINIMUM_OUTPUT_SELECTIONS = 24;
|
|
38
45
|
export async function handleContextBuilder(args, options = {}) {
|
|
39
46
|
const input = parseContextBuilderInput(args);
|
|
47
|
+
if (input.delegate.enabled) {
|
|
48
|
+
validateDelegateInvocationOverrides(input, options.registry, {
|
|
49
|
+
minScoreExplicit: options.standaloneMinScoreExplicit === true,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
40
52
|
const coveragePolicy = resolveContextCoveragePolicy(input.profile);
|
|
41
53
|
const semaphore = options.semaphore ?? getDefaultContextBuilderSemaphore();
|
|
42
54
|
const progress = createContextBuilderProgressReporter(options.progress);
|
|
@@ -150,7 +162,8 @@ export async function handleContextBuilder(args, options = {}) {
|
|
|
150
162
|
: { contextRunId: lifecycle.contextRunId, contextGeneration: injected.contextGeneration };
|
|
151
163
|
}
|
|
152
164
|
await lifecycle?.transition("scanning");
|
|
153
|
-
const
|
|
165
|
+
const scanProgressPublisher = createScanProgressPublisher(lifecycle);
|
|
166
|
+
const discovery = await withContextBuilderDiscoveryWatchdog((signal) => discoverContextBuilderCandidates({
|
|
154
167
|
roots: rootResolution.roots,
|
|
155
168
|
task: input.task,
|
|
156
169
|
allowSecrets: input.allow_secrets,
|
|
@@ -162,8 +175,9 @@ export async function handleContextBuilder(args, options = {}) {
|
|
|
162
175
|
budget: input.budget,
|
|
163
176
|
internal_caps: internalCaps,
|
|
164
177
|
excluded_real_roots: excludedArtifactRoots,
|
|
165
|
-
...(
|
|
166
|
-
|
|
178
|
+
...(signal == null ? {} : { signal }),
|
|
179
|
+
onProgress: scanProgressPublisher,
|
|
180
|
+
}), { signal: options.signal });
|
|
167
181
|
await lifecycle?.transition("searching", {
|
|
168
182
|
counts: lifecycleCountsFromDiscovery(discovery),
|
|
169
183
|
});
|
|
@@ -189,6 +203,8 @@ export async function handleContextBuilder(args, options = {}) {
|
|
|
189
203
|
focusPaths,
|
|
190
204
|
baselineOmissions: rootResolution.omissions,
|
|
191
205
|
sourceRoots: rootResolution.roots,
|
|
206
|
+
excludedRealRoots: excludedArtifactRoots,
|
|
207
|
+
artifactRunDir: writingStorage.runDir,
|
|
192
208
|
registry: options.registry ?? getDefaultAdapterRegistry(),
|
|
193
209
|
coveragePolicy,
|
|
194
210
|
...(effectiveDelegatePacketCaps == null
|
|
@@ -234,9 +250,14 @@ export async function handleContextBuilder(args, options = {}) {
|
|
|
234
250
|
// cap event (built from metrics + search_coverage in the status reducer)
|
|
235
251
|
// carries the partial classification.
|
|
236
252
|
const searchCapWarning = searchCapCoverageWarning(metrics.search_cap_hit, discovery.search_coverage);
|
|
237
|
-
const
|
|
238
|
-
|
|
239
|
-
|
|
253
|
+
const coverageAfterDelegateWarning = coverageDegradedAfterDelegateWarning(coverage, effectiveDelegateResult?.delegate);
|
|
254
|
+
const warnings = [
|
|
255
|
+
...reconciledWarnings,
|
|
256
|
+
...(searchCapWarning == null ? [] : [searchCapWarning]),
|
|
257
|
+
...(coverageAfterDelegateWarning == null
|
|
258
|
+
? []
|
|
259
|
+
: [coverageAfterDelegateWarning]),
|
|
260
|
+
];
|
|
240
261
|
const delegateMetadata = effectiveDelegateResult?.delegate == null
|
|
241
262
|
? undefined
|
|
242
263
|
: {
|
|
@@ -291,6 +312,15 @@ export async function handleContextBuilder(args, options = {}) {
|
|
|
291
312
|
metrics,
|
|
292
313
|
search_coverage: discovery.search_coverage,
|
|
293
314
|
open_questions: openQuestions,
|
|
315
|
+
...(effectiveDelegateResult?.knownMissingEvidence == null
|
|
316
|
+
? {}
|
|
317
|
+
: {
|
|
318
|
+
known_missing_evidence: effectiveDelegateResult.knownMissingEvidence.map((gap) => ({
|
|
319
|
+
path: gap.path,
|
|
320
|
+
provenance: gap.provenance,
|
|
321
|
+
reason: gap.reason,
|
|
322
|
+
})),
|
|
323
|
+
}),
|
|
294
324
|
...(delegateMetadata == null ? {} : { delegate: delegateMetadata }),
|
|
295
325
|
observability,
|
|
296
326
|
coverage,
|
|
@@ -314,14 +344,28 @@ export async function handleContextBuilder(args, options = {}) {
|
|
|
314
344
|
// Capture into a const so the beforeManifestCommit closure keeps the
|
|
315
345
|
// narrowed non-undefined type (the outer binding is a reassignable let).
|
|
316
346
|
const committingLifecycle = lifecycle;
|
|
317
|
-
const
|
|
318
|
-
signal,
|
|
319
|
-
|
|
320
|
-
|
|
347
|
+
const writeCommittedRun = async (leasedOps) => {
|
|
348
|
+
const receipt = await withContextBuilderWriteWatchdog((signal) => writeContextBuilderFinalArtifacts(writingStorage, renderInput, {
|
|
349
|
+
signal,
|
|
350
|
+
...(writingStorage.ownerNonce == null
|
|
351
|
+
? {}
|
|
352
|
+
: { ownerNonce: writingStorage.ownerNonce }),
|
|
353
|
+
...(leasedOps == null ? {} : { ops: leasedOps }),
|
|
354
|
+
...(!ownsLifecycle || committingLifecycle == null
|
|
355
|
+
? {}
|
|
356
|
+
: { beforeManifestCommit: () => committingLifecycle.transition("committing") }),
|
|
357
|
+
}), options.signal == null ? {} : { signal: options.signal });
|
|
358
|
+
committed = true;
|
|
359
|
+
if (options.retainCommittedRun !== undefined && leasedOps !== undefined) {
|
|
360
|
+
await options.retainCommittedRun(createContextBuilderReceiptTextResponse(receipt), writingStorage, leasedOps);
|
|
361
|
+
}
|
|
362
|
+
return receipt;
|
|
363
|
+
};
|
|
364
|
+
const receipt = options.retainCommittedRun == null
|
|
365
|
+
? await writeCommittedRun()
|
|
366
|
+
: await withContextBuilderRunMutationLease(writingStorage, writeCommittedRun, undefined, writingStorage.ownerNonce == null
|
|
321
367
|
? {}
|
|
322
|
-
: {
|
|
323
|
-
}), options.signal == null ? {} : { signal: options.signal });
|
|
324
|
-
committed = true;
|
|
368
|
+
: { ownerNonce: writingStorage.ownerNonce });
|
|
325
369
|
if (ownsLifecycle) {
|
|
326
370
|
await recordContextRunCatalogMarker({
|
|
327
371
|
runsDir,
|
|
@@ -570,12 +614,19 @@ function assertNoContextBuilderFocusGlobConflicts(input) {
|
|
|
570
614
|
if (focus.kind === "directory") {
|
|
571
615
|
if (matcher.excludes(relative) ||
|
|
572
616
|
matcher.excludes(`${relative}/__dir__`)) {
|
|
573
|
-
|
|
617
|
+
const defaultGlob = matchingDefaultExcludeGlob(relative) ??
|
|
618
|
+
matchingDefaultExcludeGlob(`${relative}/__dir__`);
|
|
619
|
+
conflicts.push(defaultGlob == null
|
|
620
|
+
? `focus directory "${relative}" (root_index ${focus.root_index}) is pruned by exclude_globs`
|
|
621
|
+
: `focus directory "${relative}" (root_index ${focus.root_index}) matched default exclude ${defaultGlob} (not overridable)`);
|
|
574
622
|
}
|
|
575
623
|
continue;
|
|
576
624
|
}
|
|
577
625
|
if (matcher.excludes(relative)) {
|
|
578
|
-
|
|
626
|
+
const defaultGlob = matchingDefaultExcludeGlob(relative);
|
|
627
|
+
conflicts.push(defaultGlob == null
|
|
628
|
+
? `focus path "${relative}" (root_index ${focus.root_index}) is omitted by exclude_globs`
|
|
629
|
+
: `focus path "${relative}" (root_index ${focus.root_index}) matched default exclude ${defaultGlob} (not overridable)`);
|
|
579
630
|
continue;
|
|
580
631
|
}
|
|
581
632
|
if (!matcher.includes(relative)) {
|
|
@@ -586,6 +637,17 @@ function assertNoContextBuilderFocusGlobConflicts(input) {
|
|
|
586
637
|
throw new InvalidParamsError(`context_builder focus_paths conflict with the effective include/exclude globs (${conflicts.length} conflict${conflicts.length === 1 ? "" : "s"}): ${conflicts.join("; ")}`);
|
|
587
638
|
}
|
|
588
639
|
}
|
|
640
|
+
// Default excludes are always prepended and cannot be removed by callers, so a
|
|
641
|
+
// conflict against one needs to say so — "omitted by exclude_globs" sends the
|
|
642
|
+
// caller hunting through globs they never passed.
|
|
643
|
+
let defaultExcludeGlobMatchers;
|
|
644
|
+
function matchingDefaultExcludeGlob(relative) {
|
|
645
|
+
defaultExcludeGlobMatchers ??= CONTEXT_BUILDER_DEFAULT_EXCLUDE_GLOBS.map((glob) => ({
|
|
646
|
+
glob,
|
|
647
|
+
matcher: compileContextBuilderGlobs({ include: [], exclude: [glob] }),
|
|
648
|
+
}));
|
|
649
|
+
return defaultExcludeGlobMatchers.find(({ matcher }) => matcher.excludes(relative))?.glob;
|
|
650
|
+
}
|
|
589
651
|
function getExactlyConfiguredArtifactRoots() {
|
|
590
652
|
const rawEntries = rawAllowedRootEntries();
|
|
591
653
|
const configured = new Set();
|
|
@@ -635,6 +697,38 @@ function getDefaultAdapterRegistry() {
|
|
|
635
697
|
}
|
|
636
698
|
return defaultAdapterRegistry;
|
|
637
699
|
}
|
|
700
|
+
function validateDelegateInvocationOverrides(input, registry, overrides = {}) {
|
|
701
|
+
if (!input.delegate.enabled) {
|
|
702
|
+
return;
|
|
703
|
+
}
|
|
704
|
+
assertNoDisabledFinalizerInvocationOverrides(input.delegate.finalizer.enabled, input.profile, input.delegate.finalizer.model, input.delegate.finalizer.effort, overrides.minScoreExplicit === true
|
|
705
|
+
? input.delegate.finalizer.min_score
|
|
706
|
+
: undefined);
|
|
707
|
+
const finalizerEnabled = contextBuilderFinalizerEnabled(input);
|
|
708
|
+
const curatorHasOverrides = delegateProviderHasInvocationOverrides(input.delegate.curator);
|
|
709
|
+
const finalizerHasOverrides = finalizerEnabled &&
|
|
710
|
+
delegateProviderHasInvocationOverrides(input.delegate.finalizer);
|
|
711
|
+
if (!curatorHasOverrides && !finalizerHasOverrides) {
|
|
712
|
+
return;
|
|
713
|
+
}
|
|
714
|
+
const resolvedRegistry = registry ?? getDefaultAdapterRegistry();
|
|
715
|
+
if (curatorHasOverrides) {
|
|
716
|
+
validateDelegateProviderInvocationOverrides(input.delegate.curator, resolvedRegistry);
|
|
717
|
+
}
|
|
718
|
+
if (finalizerHasOverrides) {
|
|
719
|
+
validateDelegateProviderInvocationOverrides(input.delegate.finalizer, resolvedRegistry);
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
function delegateProviderHasInvocationOverrides(provider) {
|
|
723
|
+
return provider.model !== undefined || provider.effort !== undefined;
|
|
724
|
+
}
|
|
725
|
+
function validateDelegateProviderInvocationOverrides(provider, registry) {
|
|
726
|
+
const adapter = registry.resolveAdapter(provider.cli_name);
|
|
727
|
+
resolveInvocationOverrides(adapter, adapter.args, {
|
|
728
|
+
...(provider.model === undefined ? {} : { model: provider.model }),
|
|
729
|
+
...(provider.effort === undefined ? {} : { effort: provider.effort }),
|
|
730
|
+
});
|
|
731
|
+
}
|
|
638
732
|
function resolveEffectiveDelegatePacketCaps(profile, explicitCaps) {
|
|
639
733
|
const profileCaps = profile == null
|
|
640
734
|
? undefined
|
|
@@ -651,15 +745,7 @@ function contextBuilderFinalizerEnabled(input) {
|
|
|
651
745
|
if (!input.delegate.enabled) {
|
|
652
746
|
return false;
|
|
653
747
|
}
|
|
654
|
-
|
|
655
|
-
if (explicit != null) {
|
|
656
|
-
return explicit;
|
|
657
|
-
}
|
|
658
|
-
if (input.profile != null) {
|
|
659
|
-
return CONTEXT_BUILDER_INTENT_PROFILE_DEFAULTS[input.profile]
|
|
660
|
-
.finalizer_enabled;
|
|
661
|
-
}
|
|
662
|
-
return true;
|
|
748
|
+
return resolveContextBuilderFinalizerEffectiveEnabled(input.delegate.finalizer.enabled, input.profile);
|
|
663
749
|
}
|
|
664
750
|
function delegateFinalizerReceiptMetadata(finalizer) {
|
|
665
751
|
return {
|
|
@@ -668,6 +754,41 @@ function delegateFinalizerReceiptMetadata(finalizer) {
|
|
|
668
754
|
min_score: finalizer.min_score,
|
|
669
755
|
};
|
|
670
756
|
}
|
|
757
|
+
function delegateProviderReceiptMetadata(provider) {
|
|
758
|
+
return {
|
|
759
|
+
cli_name: provider.cli_name,
|
|
760
|
+
role: provider.role,
|
|
761
|
+
};
|
|
762
|
+
}
|
|
763
|
+
async function recordDelegateSeatInvocationAudit(artifactRunDir, seat, provider, registry) {
|
|
764
|
+
if (!invocationAuditV2PersistenceEnabled())
|
|
765
|
+
return;
|
|
766
|
+
try {
|
|
767
|
+
const adapter = registry.resolveAdapter(provider.cli_name);
|
|
768
|
+
const extraArgs = adapter.roles[provider.role]?.extra_args;
|
|
769
|
+
const mergedArgs = extraArgs == null
|
|
770
|
+
? adapter.args
|
|
771
|
+
: [...adapter.args, ...extraArgs];
|
|
772
|
+
const audit = deriveInvocationAudit(adapter, mergedArgs, {
|
|
773
|
+
model: provider.model,
|
|
774
|
+
effort: provider.effort,
|
|
775
|
+
});
|
|
776
|
+
await recordContextInvocationAudit(artifactRunDir, seat, audit);
|
|
777
|
+
}
|
|
778
|
+
catch (err) {
|
|
779
|
+
// Adapter resolution failures are raised by the subsequent delegate
|
|
780
|
+
// call; do not recast them as sidecar problems.
|
|
781
|
+
if (err instanceof InvalidParamsError) {
|
|
782
|
+
return;
|
|
783
|
+
}
|
|
784
|
+
try {
|
|
785
|
+
logger.warn({ err, seat }, "failed to record context_builder invocation audit sidecar");
|
|
786
|
+
}
|
|
787
|
+
catch {
|
|
788
|
+
// Sidecar diagnostics must not fail the delegate.
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
}
|
|
671
792
|
async function resolveDelegateSelection(input) {
|
|
672
793
|
const delegate = input.input.delegate;
|
|
673
794
|
if (!delegate.enabled) {
|
|
@@ -737,6 +858,7 @@ async function resolveDelegateSelection(input) {
|
|
|
737
858
|
if (requestedFocusOmissions.length > 0) {
|
|
738
859
|
return handleDelegateRuntimeFailure({
|
|
739
860
|
delegate,
|
|
861
|
+
discoveryMetrics: input.discovery.metrics,
|
|
740
862
|
reason: `requested local evidence omitted: ${formatOmissionsForDelegateFailure(requestedFocusOmissions)}`,
|
|
741
863
|
deterministicSelection: input.deterministicSelection,
|
|
742
864
|
omissionCount: input.baselineOmissions.length +
|
|
@@ -751,10 +873,12 @@ async function resolveDelegateSelection(input) {
|
|
|
751
873
|
delegatePacketSize = curatorPacket.metrics.packet_estimated_tokens;
|
|
752
874
|
delegatePacketCompaction = curatorPacket.metrics.compaction;
|
|
753
875
|
recordPacketEvidenceWarning(curatorPacket.evidence_warning);
|
|
876
|
+
recordPacketEvidenceWarning(packetObligationTruncationWarning(input.discovery.review_obligations, curatorPacket.coverage_policy));
|
|
754
877
|
const curatorPacketRequestedOmissions = curatorPacket.omissions.filter(isRequestedEvidenceOmission);
|
|
755
878
|
if (curatorPacketRequestedOmissions.length > 0) {
|
|
756
879
|
return handleDelegateRuntimeFailure({
|
|
757
880
|
delegate,
|
|
881
|
+
discoveryMetrics: input.discovery.metrics,
|
|
758
882
|
reason: `requested local evidence omitted: ${formatOmissionsForDelegateFailure(curatorPacketRequestedOmissions)}`,
|
|
759
883
|
deterministicSelection: input.deterministicSelection,
|
|
760
884
|
omissions: curatorPacketRequestedOmissions,
|
|
@@ -769,6 +893,7 @@ async function resolveDelegateSelection(input) {
|
|
|
769
893
|
if (curatorPacket.candidates.length === 0) {
|
|
770
894
|
return handleDelegateRuntimeFailure({
|
|
771
895
|
delegate,
|
|
896
|
+
discoveryMetrics: input.discovery.metrics,
|
|
772
897
|
reason: `curator packet contained no selectable candidates; ${delegatePromptDiagnostic(curatorPacket)}`,
|
|
773
898
|
deterministicSelection: input.deterministicSelection,
|
|
774
899
|
omissionCount: input.baselineOmissions.length +
|
|
@@ -778,8 +903,35 @@ async function resolveDelegateSelection(input) {
|
|
|
778
903
|
observability: currentDelegateObservability(),
|
|
779
904
|
});
|
|
780
905
|
}
|
|
906
|
+
// Required focus is admitted first after the finalizer, so if it cannot fit
|
|
907
|
+
// the local reconstruction budget ON ITS OWN no curator/finalizer answer can
|
|
908
|
+
// rescue the run. Dry-run the same reconstruction now and fail in ~1s
|
|
909
|
+
// instead of after minutes of provider time. Finalizer mode only: curator-
|
|
910
|
+
// only mode may legitimately compact required ranges under budget pressure.
|
|
911
|
+
if (finalizerEnabled) {
|
|
912
|
+
const preDispatchFailure = requiredFocusPreDispatchFailure({
|
|
913
|
+
focusPaths: input.focusPaths,
|
|
914
|
+
candidates: curatorPacket.candidates,
|
|
915
|
+
discovery: input.discovery,
|
|
916
|
+
budget: input.input.budget,
|
|
917
|
+
});
|
|
918
|
+
if (preDispatchFailure != null) {
|
|
919
|
+
return handleDelegateRuntimeFailure({
|
|
920
|
+
delegate,
|
|
921
|
+
discoveryMetrics: input.discovery.metrics,
|
|
922
|
+
reason: preDispatchFailure,
|
|
923
|
+
deterministicSelection: input.deterministicSelection,
|
|
924
|
+
omissionCount: input.baselineOmissions.length +
|
|
925
|
+
input.discovery.omissions.length +
|
|
926
|
+
input.deterministicSelection.omissions.length,
|
|
927
|
+
candidateFallbackCount,
|
|
928
|
+
observability: currentDelegateObservability(),
|
|
929
|
+
});
|
|
930
|
+
}
|
|
931
|
+
}
|
|
781
932
|
const curatorStartMs = Date.now();
|
|
782
933
|
const curatorSessionLogOwner = sessionLogOwner("curator");
|
|
934
|
+
await recordDelegateSeatInvocationAudit(input.artifactRunDir, "curator", delegate.curator, input.registry);
|
|
783
935
|
const curatorCall = await runContextBuilderDelegateCall({
|
|
784
936
|
prompt: renderDelegatePrompt("curator", curatorPacket),
|
|
785
937
|
provider: delegate.curator,
|
|
@@ -795,6 +947,7 @@ async function resolveDelegateSelection(input) {
|
|
|
795
947
|
if (!curatorCall.ok) {
|
|
796
948
|
return handleDelegateRuntimeFailure({
|
|
797
949
|
delegate,
|
|
950
|
+
discoveryMetrics: input.discovery.metrics,
|
|
798
951
|
reason: failureReasonFromCall("curator", curatorCall),
|
|
799
952
|
failureKind: curatorCall.reason,
|
|
800
953
|
deterministicSelection: input.deterministicSelection,
|
|
@@ -856,6 +1009,7 @@ async function resolveDelegateSelection(input) {
|
|
|
856
1009
|
}
|
|
857
1010
|
return handleDelegateRuntimeFailure({
|
|
858
1011
|
delegate,
|
|
1012
|
+
discoveryMetrics: input.discovery.metrics,
|
|
859
1013
|
reason: failureReasonFromError("curator_validation", err, delegatePromptDiagnostic(curatorPacket)),
|
|
860
1014
|
deterministicSelection: input.deterministicSelection,
|
|
861
1015
|
omissionCount: input.baselineOmissions.length +
|
|
@@ -868,6 +1022,7 @@ async function resolveDelegateSelection(input) {
|
|
|
868
1022
|
if (curatorSelection.selected.length === 0) {
|
|
869
1023
|
return handleDelegateRuntimeFailure({
|
|
870
1024
|
delegate,
|
|
1025
|
+
discoveryMetrics: input.discovery.metrics,
|
|
871
1026
|
reason: "curator produced empty local selection",
|
|
872
1027
|
deterministicSelection: input.deterministicSelection,
|
|
873
1028
|
omissionCount: input.baselineOmissions.length +
|
|
@@ -886,6 +1041,7 @@ async function resolveDelegateSelection(input) {
|
|
|
886
1041
|
expandSelectedFiles: true,
|
|
887
1042
|
});
|
|
888
1043
|
const warnings = curatorProposal.warnings.map((message) => delegateWarning("delegate_curator_warning", message));
|
|
1044
|
+
warnings.push(...correctiveRetryWarnings("curator", curatorCall));
|
|
889
1045
|
warnings.push(...packetEvidenceWarnings);
|
|
890
1046
|
appendDelegateRepairWarnings(warnings, repairedSelection);
|
|
891
1047
|
const openQuestions = curatorProposal.open_questions.map((question) => sanitizeDiagnosticText(question));
|
|
@@ -901,6 +1057,7 @@ async function resolveDelegateSelection(input) {
|
|
|
901
1057
|
if (delegateEvidenceCoverageFailure != null) {
|
|
902
1058
|
return handleDelegateRuntimeFailure({
|
|
903
1059
|
delegate,
|
|
1060
|
+
discoveryMetrics: input.discovery.metrics,
|
|
904
1061
|
reason: `post-repair evidence coverage failed: ${delegateEvidenceCoverageFailure}`,
|
|
905
1062
|
deterministicSelection: input.deterministicSelection,
|
|
906
1063
|
omissionCount,
|
|
@@ -921,6 +1078,7 @@ async function resolveDelegateSelection(input) {
|
|
|
921
1078
|
if (curatorOnlyCoverageFailure != null) {
|
|
922
1079
|
return handleDelegateRuntimeFailure({
|
|
923
1080
|
delegate,
|
|
1081
|
+
discoveryMetrics: input.discovery.metrics,
|
|
924
1082
|
reason: `post-repair required-focus coverage failed: ${curatorOnlyCoverageFailure}`,
|
|
925
1083
|
deterministicSelection: input.deterministicSelection,
|
|
926
1084
|
omissionCount: input.baselineOmissions.length +
|
|
@@ -940,7 +1098,7 @@ async function resolveDelegateSelection(input) {
|
|
|
940
1098
|
status: "succeeded",
|
|
941
1099
|
failure_policy: delegate.failure_policy,
|
|
942
1100
|
fallback_used: false,
|
|
943
|
-
curator:
|
|
1101
|
+
curator: delegateProviderReceiptMetadata(delegate.curator),
|
|
944
1102
|
finalizer: delegateFinalizerReceiptMetadata(delegate.finalizer),
|
|
945
1103
|
selected_count: repairedSelection.selection.selected.length,
|
|
946
1104
|
warning_count: warnings.length,
|
|
@@ -961,10 +1119,12 @@ async function resolveDelegateSelection(input) {
|
|
|
961
1119
|
delegatePacketSize = finalizerPacket.metrics.packet_estimated_tokens;
|
|
962
1120
|
delegatePacketCompaction = finalizerPacket.metrics.compaction;
|
|
963
1121
|
recordPacketEvidenceWarning(finalizerPacket.evidence_warning);
|
|
1122
|
+
recordPacketEvidenceWarning(packetObligationTruncationWarning(input.discovery.review_obligations, finalizerPacket.coverage_policy));
|
|
964
1123
|
const finalizerPacketRequestedOmissions = finalizerPacket.omissions.filter(isRequestedEvidenceOmission);
|
|
965
1124
|
if (finalizerPacketRequestedOmissions.length > 0) {
|
|
966
1125
|
return handleDelegateRuntimeFailure({
|
|
967
1126
|
delegate,
|
|
1127
|
+
discoveryMetrics: input.discovery.metrics,
|
|
968
1128
|
reason: `requested local evidence omitted: ${formatOmissionsForDelegateFailure(finalizerPacketRequestedOmissions)}`,
|
|
969
1129
|
deterministicSelection: input.deterministicSelection,
|
|
970
1130
|
omissions: finalizerPacketRequestedOmissions,
|
|
@@ -979,6 +1139,7 @@ async function resolveDelegateSelection(input) {
|
|
|
979
1139
|
if ((finalizerPacket.curator_proposal?.selected.length ?? 0) === 0) {
|
|
980
1140
|
return handleDelegateRuntimeFailure({
|
|
981
1141
|
delegate,
|
|
1142
|
+
discoveryMetrics: input.discovery.metrics,
|
|
982
1143
|
reason: `finalizer packet lost curator proposal selection; ${delegatePromptDiagnostic(finalizerPacket)}`,
|
|
983
1144
|
deterministicSelection: input.deterministicSelection,
|
|
984
1145
|
omissionCount: input.baselineOmissions.length +
|
|
@@ -990,9 +1151,11 @@ async function resolveDelegateSelection(input) {
|
|
|
990
1151
|
}
|
|
991
1152
|
const finalizerStartMs = Date.now();
|
|
992
1153
|
const finalizerSessionLogOwner = sessionLogOwner("finalizer");
|
|
1154
|
+
await recordDelegateSeatInvocationAudit(input.artifactRunDir, "finalizer", delegate.finalizer, input.registry);
|
|
993
1155
|
const finalizerCall = await runContextBuilderDelegateCall({
|
|
994
1156
|
prompt: renderDelegatePrompt("finalizer", finalizerPacket, {
|
|
995
1157
|
minScore: delegate.finalizer.min_score,
|
|
1158
|
+
externalGapPolicy: delegate.external_gap_policy,
|
|
996
1159
|
}),
|
|
997
1160
|
provider: delegate.finalizer,
|
|
998
1161
|
registry: input.registry,
|
|
@@ -1007,6 +1170,7 @@ async function resolveDelegateSelection(input) {
|
|
|
1007
1170
|
if (!finalizerCall.ok) {
|
|
1008
1171
|
return handleDelegateRuntimeFailure({
|
|
1009
1172
|
delegate,
|
|
1173
|
+
discoveryMetrics: input.discovery.metrics,
|
|
1010
1174
|
reason: failureReasonFromCall("finalizer", finalizerCall),
|
|
1011
1175
|
failureKind: finalizerCall.reason,
|
|
1012
1176
|
deterministicSelection: input.deterministicSelection,
|
|
@@ -1025,43 +1189,64 @@ async function resolveDelegateSelection(input) {
|
|
|
1025
1189
|
allowSecrets: input.input.allow_secrets,
|
|
1026
1190
|
coveragePolicy: finalizerPacket.coverage_policy,
|
|
1027
1191
|
});
|
|
1028
|
-
|
|
1192
|
+
const externalGaps = classifyFinalizerExternalGaps({
|
|
1193
|
+
gaps: finalizerProposal.external_gaps,
|
|
1194
|
+
sourceRoots: input.sourceRoots,
|
|
1195
|
+
focusPaths: input.focusPaths,
|
|
1196
|
+
packetCandidates: finalizerPacket.candidates,
|
|
1197
|
+
discovery: input.discovery,
|
|
1198
|
+
includeGlobs: input.input.include_globs,
|
|
1199
|
+
excludeGlobs: input.input.exclude_globs,
|
|
1200
|
+
...(input.excludedRealRoots == null
|
|
1201
|
+
? {}
|
|
1202
|
+
: { excludedRealRoots: input.excludedRealRoots }),
|
|
1203
|
+
});
|
|
1204
|
+
// A rejected gap means the finalizer mis-described the packet (it named
|
|
1205
|
+
// in-packet or focused evidence, or a path that is not local at all);
|
|
1206
|
+
// that is invalid output under either gap policy.
|
|
1207
|
+
const externalGapFailure = externalGaps.rejected.length > 0
|
|
1208
|
+
? formatExternalGapRejectionFailure(externalGaps.rejected)
|
|
1209
|
+
: externalGaps.external.length > 0 &&
|
|
1210
|
+
delegate.external_gap_policy === "fail"
|
|
1211
|
+
? formatExternalGapPolicyFailure(externalGaps.external)
|
|
1212
|
+
: null;
|
|
1213
|
+
if (externalGapFailure != null) {
|
|
1029
1214
|
return handleDelegateRuntimeFailure({
|
|
1030
1215
|
delegate,
|
|
1031
|
-
|
|
1216
|
+
discoveryMetrics: input.discovery.metrics,
|
|
1217
|
+
reason: externalGapFailure,
|
|
1032
1218
|
deterministicSelection: input.deterministicSelection,
|
|
1033
1219
|
omissionCount: input.baselineOmissions.length +
|
|
1034
1220
|
input.discovery.omissions.length +
|
|
1035
|
-
input.deterministicSelection.omissions.length
|
|
1036
|
-
finalizerProposal.omissions.length,
|
|
1221
|
+
input.deterministicSelection.omissions.length,
|
|
1037
1222
|
candidateFallbackCount,
|
|
1038
1223
|
observability: currentDelegateObservability(),
|
|
1039
1224
|
});
|
|
1040
1225
|
}
|
|
1041
|
-
const
|
|
1226
|
+
const requiredFocus = requiredFocusEvidenceSelections({
|
|
1042
1227
|
focusPaths: input.focusPaths,
|
|
1043
1228
|
candidates: finalizerPacket.candidates,
|
|
1044
|
-
})
|
|
1045
|
-
const
|
|
1229
|
+
});
|
|
1230
|
+
const foldedFinalizerSelection = foldRequiredFocusEvidenceIntoSelection(finalizerProposal.selected, requiredFocus);
|
|
1231
|
+
// Required-first admission: the fold appends finalizer-dropped required
|
|
1232
|
+
// focus at the END of selection order, so a preference-free first-fit
|
|
1233
|
+
// prune would evict exactly that evidence under cap pressure. Admitting
|
|
1234
|
+
// required candidates first means a required-focus file can only be
|
|
1235
|
+
// missing here when required focus ALONE exceeds the budget.
|
|
1236
|
+
const finalReconstruction = reconstructContextBuilderDelegateSelectionWithReport({
|
|
1046
1237
|
selected: foldedFinalizerSelection.selected,
|
|
1047
1238
|
candidates: finalizerPacket.candidates,
|
|
1048
1239
|
slices: input.discovery.slices,
|
|
1049
1240
|
tree: input.discovery.tree,
|
|
1050
1241
|
search: input.discovery.search,
|
|
1051
1242
|
budget: input.input.budget,
|
|
1243
|
+
...(requiredFocus.length === 0
|
|
1244
|
+
? {}
|
|
1245
|
+
: {
|
|
1246
|
+
requiredFocus: new Map(requiredFocus.map((required) => [required.candidate_id, required.ranges])),
|
|
1247
|
+
}),
|
|
1052
1248
|
});
|
|
1053
|
-
|
|
1054
|
-
return handleDelegateRuntimeFailure({
|
|
1055
|
-
delegate,
|
|
1056
|
-
reason: "finalizer produced empty local selection",
|
|
1057
|
-
deterministicSelection: input.deterministicSelection,
|
|
1058
|
-
omissionCount: input.baselineOmissions.length +
|
|
1059
|
-
input.discovery.omissions.length +
|
|
1060
|
-
input.deterministicSelection.omissions.length,
|
|
1061
|
-
candidateFallbackCount,
|
|
1062
|
-
observability: currentDelegateObservability(),
|
|
1063
|
-
});
|
|
1064
|
-
}
|
|
1249
|
+
const finalSelection = finalReconstruction.selection;
|
|
1065
1250
|
// A per-file reconstruction-budget overrun no longer fails the entire
|
|
1066
1251
|
// delegate selection. Over-budget candidates (e.g. large generated/i18n
|
|
1067
1252
|
// catalogs the finalizer over-selected) are dropped while the rest of the
|
|
@@ -1078,10 +1263,16 @@ async function resolveDelegateSelection(input) {
|
|
|
1078
1263
|
finalizerSelected: materializeFinalizerSelection(finalSelection.selected, finalizerPacket.candidates),
|
|
1079
1264
|
curatorWarnings: curatorProposal.warnings,
|
|
1080
1265
|
finalizerWarnings: finalizerProposal.warnings,
|
|
1266
|
+
budgetDiagnostics: {
|
|
1267
|
+
omissions: finalSelection.omissions,
|
|
1268
|
+
foldedPaths: foldedFinalizerSelection.foldedPaths,
|
|
1269
|
+
budget: input.input.budget,
|
|
1270
|
+
},
|
|
1081
1271
|
});
|
|
1082
1272
|
if (completenessFailure != null) {
|
|
1083
1273
|
return handleDelegateRuntimeFailure({
|
|
1084
1274
|
delegate,
|
|
1275
|
+
discoveryMetrics: input.discovery.metrics,
|
|
1085
1276
|
reason: completenessFailure,
|
|
1086
1277
|
deterministicSelection: input.deterministicSelection,
|
|
1087
1278
|
omissionCount: input.baselineOmissions.length +
|
|
@@ -1091,6 +1282,24 @@ async function resolveDelegateSelection(input) {
|
|
|
1091
1282
|
observability: currentDelegateObservability(),
|
|
1092
1283
|
});
|
|
1093
1284
|
}
|
|
1285
|
+
// Ordered after the completeness gate: when a binding cap prunes every
|
|
1286
|
+
// candidate — including required focus — the lever-bearing diagnostic
|
|
1287
|
+
// above must win over this generic catch. An empty selection without
|
|
1288
|
+
// focus paths (or with only non-material delegate warnings) still lands
|
|
1289
|
+
// here as the terminal catch.
|
|
1290
|
+
if (finalSelection.selected.length === 0) {
|
|
1291
|
+
return handleDelegateRuntimeFailure({
|
|
1292
|
+
delegate,
|
|
1293
|
+
discoveryMetrics: input.discovery.metrics,
|
|
1294
|
+
reason: "finalizer produced empty local selection",
|
|
1295
|
+
deterministicSelection: input.deterministicSelection,
|
|
1296
|
+
omissionCount: input.baselineOmissions.length +
|
|
1297
|
+
input.discovery.omissions.length +
|
|
1298
|
+
input.deterministicSelection.omissions.length,
|
|
1299
|
+
candidateFallbackCount,
|
|
1300
|
+
observability: currentDelegateObservability(),
|
|
1301
|
+
});
|
|
1302
|
+
}
|
|
1094
1303
|
const repairedSelection = repairDelegateSelection({
|
|
1095
1304
|
curatorSelection: finalSelection,
|
|
1096
1305
|
contentCandidates: input.discovery.content_candidates,
|
|
@@ -1102,6 +1311,7 @@ async function resolveDelegateSelection(input) {
|
|
|
1102
1311
|
if (delegateEvidenceCoverageFailure != null) {
|
|
1103
1312
|
return handleDelegateRuntimeFailure({
|
|
1104
1313
|
delegate,
|
|
1314
|
+
discoveryMetrics: input.discovery.metrics,
|
|
1105
1315
|
reason: `post-repair evidence coverage failed: ${delegateEvidenceCoverageFailure}`,
|
|
1106
1316
|
deterministicSelection: input.deterministicSelection,
|
|
1107
1317
|
omissionCount: input.baselineOmissions.length +
|
|
@@ -1111,6 +1321,29 @@ async function resolveDelegateSelection(input) {
|
|
|
1111
1321
|
observability: currentDelegateObservability(),
|
|
1112
1322
|
});
|
|
1113
1323
|
}
|
|
1324
|
+
// D1 backstop: required-first reconstruction plus the completeness gate
|
|
1325
|
+
// above should make this unreachable. It fails closed if a server-side
|
|
1326
|
+
// change ever lets a non-degenerate required-focus file vanish while
|
|
1327
|
+
// non-required evidence stays selected.
|
|
1328
|
+
const finalizerEvictionFailure = finalizerRequiredFocusEvictionFailure({
|
|
1329
|
+
focusPaths: input.focusPaths,
|
|
1330
|
+
candidates: finalizerPacket.candidates,
|
|
1331
|
+
contentCandidates: input.discovery.content_candidates,
|
|
1332
|
+
selected: repairedSelection.selection.selected,
|
|
1333
|
+
});
|
|
1334
|
+
if (finalizerEvictionFailure != null) {
|
|
1335
|
+
return handleDelegateRuntimeFailure({
|
|
1336
|
+
delegate,
|
|
1337
|
+
discoveryMetrics: input.discovery.metrics,
|
|
1338
|
+
reason: `post-repair required-focus coverage failed: ${finalizerEvictionFailure}`,
|
|
1339
|
+
deterministicSelection: input.deterministicSelection,
|
|
1340
|
+
omissionCount: input.baselineOmissions.length +
|
|
1341
|
+
input.discovery.omissions.length +
|
|
1342
|
+
input.deterministicSelection.omissions.length,
|
|
1343
|
+
candidateFallbackCount,
|
|
1344
|
+
observability: currentDelegateObservability(),
|
|
1345
|
+
});
|
|
1346
|
+
}
|
|
1114
1347
|
// Re-verify required-focus completeness against the actual returned
|
|
1115
1348
|
// selection. This keeps the fallback contract tied to the final materialized
|
|
1116
1349
|
// packet rather than the delegate's requested selection.
|
|
@@ -1122,6 +1355,7 @@ async function resolveDelegateSelection(input) {
|
|
|
1122
1355
|
if (postRepairCoverageFailure != null) {
|
|
1123
1356
|
return handleDelegateRuntimeFailure({
|
|
1124
1357
|
delegate,
|
|
1358
|
+
discoveryMetrics: input.discovery.metrics,
|
|
1125
1359
|
reason: `post-repair required-focus coverage failed: ${postRepairCoverageFailure}`,
|
|
1126
1360
|
deterministicSelection: input.deterministicSelection,
|
|
1127
1361
|
omissionCount: input.baselineOmissions.length +
|
|
@@ -1134,6 +1368,8 @@ async function resolveDelegateSelection(input) {
|
|
|
1134
1368
|
const warnings = [
|
|
1135
1369
|
...curatorProposal.warnings.map((message) => delegateWarning("delegate_curator_warning", message)),
|
|
1136
1370
|
...finalizerProposal.warnings.map((message) => delegateWarning("delegate_finalizer_warning", message)),
|
|
1371
|
+
...correctiveRetryWarnings("curator", curatorCall),
|
|
1372
|
+
...correctiveRetryWarnings("finalizer", finalizerCall),
|
|
1137
1373
|
];
|
|
1138
1374
|
if (foldedFinalizerSelection.foldedPaths.length > 0) {
|
|
1139
1375
|
warnings.push(delegateWarning("delegate_finalizer_warning", `finalizer omitted requested local evidence; server folded it back into the selection: ${boundContextBuilderPathList(foldedFinalizerSelection.foldedPaths).join(", ")}`));
|
|
@@ -1143,8 +1379,15 @@ async function resolveDelegateSelection(input) {
|
|
|
1143
1379
|
.map(formatOmissionForDelegateFailure)
|
|
1144
1380
|
.join("; ")}`));
|
|
1145
1381
|
}
|
|
1382
|
+
warnings.push(...finalizerDropProvenanceWarnings({
|
|
1383
|
+
finalizerSelected: finalizerProposal.selected,
|
|
1384
|
+
requiredFocus,
|
|
1385
|
+
foldedPaths: foldedFinalizerSelection.foldedPaths,
|
|
1386
|
+
reconstruction: finalReconstruction,
|
|
1387
|
+
}));
|
|
1146
1388
|
warnings.push(...packetEvidenceWarnings);
|
|
1147
1389
|
appendDelegateRepairWarnings(warnings, repairedSelection);
|
|
1390
|
+
warnings.push(...externalGapWarnings(externalGaps.external));
|
|
1148
1391
|
const openQuestions = curatorProposal.open_questions.map((question) => sanitizeDiagnosticText(question));
|
|
1149
1392
|
const omissionCount = input.baselineOmissions.length +
|
|
1150
1393
|
input.discovery.omissions.length +
|
|
@@ -1154,12 +1397,18 @@ async function resolveDelegateSelection(input) {
|
|
|
1154
1397
|
warnings,
|
|
1155
1398
|
omissions: [],
|
|
1156
1399
|
openQuestions,
|
|
1400
|
+
...(externalGaps.external.length === 0
|
|
1401
|
+
? {}
|
|
1402
|
+
: { knownMissingEvidence: externalGaps.external }),
|
|
1157
1403
|
delegate: {
|
|
1158
1404
|
enabled: true,
|
|
1159
|
-
|
|
1405
|
+
// partial WITHOUT fallback is the degrade signature: the curated
|
|
1406
|
+
// selection is used as-is, but conclusions resting on the reported
|
|
1407
|
+
// external gaps are conditional.
|
|
1408
|
+
status: externalGaps.external.length === 0 ? "succeeded" : "partial",
|
|
1160
1409
|
failure_policy: delegate.failure_policy,
|
|
1161
1410
|
fallback_used: false,
|
|
1162
|
-
curator:
|
|
1411
|
+
curator: delegateProviderReceiptMetadata(delegate.curator),
|
|
1163
1412
|
finalizer: delegateFinalizerReceiptMetadata(delegate.finalizer),
|
|
1164
1413
|
finalizer_score: finalizerProposal.score,
|
|
1165
1414
|
selected_count: repairedSelection.selection.selected.length,
|
|
@@ -1178,7 +1427,10 @@ async function resolveDelegateSelection(input) {
|
|
|
1178
1427
|
}
|
|
1179
1428
|
return handleDelegateRuntimeFailure({
|
|
1180
1429
|
delegate,
|
|
1181
|
-
|
|
1430
|
+
discoveryMetrics: input.discovery.metrics,
|
|
1431
|
+
reason: err instanceof ContextBuilderFinalizerRejectionError
|
|
1432
|
+
? formatFinalizerRejectionFailure(err, delegatePromptDiagnostic(finalizerPacket))
|
|
1433
|
+
: failureReasonFromError("finalizer_validation", err, `${delegatePromptDiagnostic(finalizerPacket)}${finalizerScoreDiagnostic(finalizerCall.json, delegate.finalizer.min_score)}`),
|
|
1182
1434
|
deterministicSelection: input.deterministicSelection,
|
|
1183
1435
|
omissionCount: input.baselineOmissions.length +
|
|
1184
1436
|
input.discovery.omissions.length +
|
|
@@ -1391,11 +1643,14 @@ function fileCoversCandidate(file, candidate) {
|
|
|
1391
1643
|
return file.ranges.some((range) => range.start_line <= 1 &&
|
|
1392
1644
|
range.end_line >= Math.max(1, candidate.line_count));
|
|
1393
1645
|
}
|
|
1646
|
+
const DISCOVERY_BYTE_CAP_FAILURE_SUFFIX = "; discovery_byte_cap_hit=true (discovery read cap reached; narrow roots/focus or add exclude_globs (workflow: cb_exclude_globs))";
|
|
1394
1647
|
function handleDelegateRuntimeFailure(input) {
|
|
1395
1648
|
// Delegate-failure reasons aggregate many per-entry details (each already
|
|
1396
1649
|
// individually sanitized/redacted where assembled); the wider marked-
|
|
1397
1650
|
// truncation bound keeps the tail from being silently dropped.
|
|
1398
|
-
const failureReason = sanitizeDelegateFailureText(input.
|
|
1651
|
+
const failureReason = sanitizeDelegateFailureText(input.discoveryMetrics?.discovery_byte_cap_hit === true
|
|
1652
|
+
? `${input.reason}${DISCOVERY_BYTE_CAP_FAILURE_SUFFIX}`
|
|
1653
|
+
: input.reason);
|
|
1399
1654
|
if (input.delegate.failure_policy === "fail") {
|
|
1400
1655
|
const error = new ExecutionError(`context_builder delegate failed: ${failureReason}`);
|
|
1401
1656
|
if (input.failureKind === "timeout") {
|
|
@@ -1416,7 +1671,7 @@ function handleDelegateRuntimeFailure(input) {
|
|
|
1416
1671
|
status: "partial",
|
|
1417
1672
|
failure_policy: input.delegate.failure_policy,
|
|
1418
1673
|
fallback_used: true,
|
|
1419
|
-
curator:
|
|
1674
|
+
curator: delegateProviderReceiptMetadata(input.delegate.curator),
|
|
1420
1675
|
finalizer: delegateFinalizerReceiptMetadata(input.delegate.finalizer),
|
|
1421
1676
|
selected_count: input.candidateFallbackCount,
|
|
1422
1677
|
warning_count: warnings.length,
|
|
@@ -1527,6 +1782,45 @@ function delegateCompletenessFailure(input) {
|
|
|
1527
1782
|
}
|
|
1528
1783
|
return requiredFocusCoverageFailure(input);
|
|
1529
1784
|
}
|
|
1785
|
+
// Feasibility dry-run with the exact post-finalizer reconstruction call (same
|
|
1786
|
+
// required-first admission and estimator), fed only the required ranges. Only
|
|
1787
|
+
// a BUDGET-explained miss fails here; anything else is left to the normal
|
|
1788
|
+
// post-finalizer gates so this check can never be stricter than they are.
|
|
1789
|
+
function requiredFocusPreDispatchFailure(input) {
|
|
1790
|
+
const requiredFocus = requiredFocusEvidenceSelections(input);
|
|
1791
|
+
if (requiredFocus.length === 0) {
|
|
1792
|
+
return undefined;
|
|
1793
|
+
}
|
|
1794
|
+
const dryRun = reconstructContextBuilderDelegateSelectionWithReport({
|
|
1795
|
+
selected: foldRequiredFocusEvidenceIntoSelection([], requiredFocus).selected,
|
|
1796
|
+
candidates: input.candidates,
|
|
1797
|
+
slices: input.discovery.slices,
|
|
1798
|
+
tree: input.discovery.tree,
|
|
1799
|
+
search: input.discovery.search,
|
|
1800
|
+
budget: input.budget,
|
|
1801
|
+
requiredFocus: new Map(requiredFocus.map((required) => [required.candidate_id, required.ranges])),
|
|
1802
|
+
});
|
|
1803
|
+
const selectedByCandidate = new Map();
|
|
1804
|
+
for (const selection of materializeFinalizerSelection(dryRun.selection.selected, input.candidates)) {
|
|
1805
|
+
selectedByCandidate.set(selection.candidate_id, [
|
|
1806
|
+
...(selectedByCandidate.get(selection.candidate_id) ?? []),
|
|
1807
|
+
...selection.ranges,
|
|
1808
|
+
]);
|
|
1809
|
+
}
|
|
1810
|
+
const missing = requiredFocus.filter((required) => {
|
|
1811
|
+
const selectedRanges = selectedByCandidate.get(required.candidate_id) ?? [];
|
|
1812
|
+
return required.ranges.some((range) => !rangeCoveredByRanges(range, selectedRanges));
|
|
1813
|
+
});
|
|
1814
|
+
if (missing.length === 0) {
|
|
1815
|
+
return undefined;
|
|
1816
|
+
}
|
|
1817
|
+
return formatRequiredFocusBudgetFailure(missing, input.candidates, {
|
|
1818
|
+
omissions: dryRun.selection.omissions,
|
|
1819
|
+
foldedPaths: [],
|
|
1820
|
+
budget: input.budget,
|
|
1821
|
+
preDispatch: true,
|
|
1822
|
+
});
|
|
1823
|
+
}
|
|
1530
1824
|
function requiredFocusCoverageFailure(input) {
|
|
1531
1825
|
const required = requiredFocusEvidenceSelections(input);
|
|
1532
1826
|
if (required.length === 0) {
|
|
@@ -1546,12 +1840,163 @@ function requiredFocusCoverageFailure(input) {
|
|
|
1546
1840
|
if (missing.length === 0) {
|
|
1547
1841
|
return undefined;
|
|
1548
1842
|
}
|
|
1843
|
+
const budgetFailure = input.budgetDiagnostics == null
|
|
1844
|
+
? undefined
|
|
1845
|
+
: formatRequiredFocusBudgetFailure(missing, input.candidates, input.budgetDiagnostics);
|
|
1846
|
+
if (budgetFailure != null) {
|
|
1847
|
+
return budgetFailure;
|
|
1848
|
+
}
|
|
1549
1849
|
return `delegate finalizer omitted requested local evidence: ${missing
|
|
1550
1850
|
.map((selection) => `${selection.path}#${selection.ranges
|
|
1551
1851
|
.map((range) => `${range.start_line}-${range.end_line}`)
|
|
1552
1852
|
.join(",")}`)
|
|
1553
1853
|
.join("; ")}`;
|
|
1554
1854
|
}
|
|
1855
|
+
// Public budget key a caller raises for each local reconstruction cap.
|
|
1856
|
+
// target_estimated_tokens is derived (0.9 x max_estimated_tokens), so its
|
|
1857
|
+
// lever is the max key.
|
|
1858
|
+
const REQUIRED_FOCUS_BUDGET_LEVER_KEYS = {
|
|
1859
|
+
max_files: "max_files",
|
|
1860
|
+
max_bytes_per_file: "max_bytes_per_file",
|
|
1861
|
+
max_total_bytes: "max_total_bytes",
|
|
1862
|
+
target_estimated_tokens: "max_estimated_tokens",
|
|
1863
|
+
};
|
|
1864
|
+
// Lever-first failure text for required focus that does not fit the local
|
|
1865
|
+
// reconstruction budget on its own. The grep-stable prefix leads, the binding
|
|
1866
|
+
// cap(s) and levers follow immediately (sanitizeDelegateFailureText keeps the
|
|
1867
|
+
// head), and the per-path causes trail with marked truncation. Returns
|
|
1868
|
+
// undefined when no missing path is explained by a budget omission, so the
|
|
1869
|
+
// caller keeps the legacy range-coverage message.
|
|
1870
|
+
function formatRequiredFocusBudgetFailure(missing, candidates, diagnostics) {
|
|
1871
|
+
const rootByCandidate = new Map(candidates.map((candidate) => [candidate.id, candidate.root_index]));
|
|
1872
|
+
const omissionReasonByKey = new Map();
|
|
1873
|
+
for (const omission of diagnostics.omissions) {
|
|
1874
|
+
for (const path of omission.paths ?? []) {
|
|
1875
|
+
const key = `${omission.root_index ?? 0}:${path}`;
|
|
1876
|
+
if (!omissionReasonByKey.has(key)) {
|
|
1877
|
+
omissionReasonByKey.set(key, omission.reason);
|
|
1878
|
+
}
|
|
1879
|
+
}
|
|
1880
|
+
}
|
|
1881
|
+
const folded = new Set(diagnostics.foldedPaths);
|
|
1882
|
+
const bindingCaps = [];
|
|
1883
|
+
const entries = missing.map((selection) => {
|
|
1884
|
+
const reason = omissionReasonByKey.get(`${rootByCandidate.get(selection.candidate_id) ?? 0}:${selection.path}`);
|
|
1885
|
+
if (reason != null && !bindingCaps.includes(reason)) {
|
|
1886
|
+
bindingCaps.push(reason);
|
|
1887
|
+
}
|
|
1888
|
+
const ranges = selection.ranges
|
|
1889
|
+
.map((range) => `${range.start_line}-${range.end_line}`)
|
|
1890
|
+
.join(",");
|
|
1891
|
+
const provenance = diagnostics.preDispatch === true
|
|
1892
|
+
? "required focus"
|
|
1893
|
+
: folded.has(selection.path)
|
|
1894
|
+
? "omitted by finalizer, server-folded"
|
|
1895
|
+
: "selected by finalizer";
|
|
1896
|
+
const cause = reason == null
|
|
1897
|
+
? `${provenance}; required ranges uncovered`
|
|
1898
|
+
: `${provenance}; pruned by ${reason}`;
|
|
1899
|
+
return `${selection.path}#${ranges} (${cause})`;
|
|
1900
|
+
});
|
|
1901
|
+
if (bindingCaps.length === 0) {
|
|
1902
|
+
return undefined;
|
|
1903
|
+
}
|
|
1904
|
+
// Advising "raise" for a key already at its public hard maximum sends the
|
|
1905
|
+
// caller into a guaranteed -32602; only focus reduction can help there.
|
|
1906
|
+
const raisable = [];
|
|
1907
|
+
const atHardMax = [];
|
|
1908
|
+
for (const cap of bindingCaps) {
|
|
1909
|
+
const key = REQUIRED_FOCUS_BUDGET_LEVER_KEYS[cap] ?? cap;
|
|
1910
|
+
const current = budgetValue(diagnostics.budget, key);
|
|
1911
|
+
const hardMax = hardLimitValue(key);
|
|
1912
|
+
if (current != null && hardMax != null && current >= hardMax) {
|
|
1913
|
+
atHardMax.push(`budget.${key} is already at its hard maximum (${current})`);
|
|
1914
|
+
}
|
|
1915
|
+
else {
|
|
1916
|
+
// " / " would read as an absolute path to the diagnostic redactor.
|
|
1917
|
+
raisable.push(`budget.${key} (workflow: cb_budget_${key})`);
|
|
1918
|
+
}
|
|
1919
|
+
}
|
|
1920
|
+
const leverText = atHardMax.length === 0
|
|
1921
|
+
? `raise ${raisable.join(" and ")} or remove the path from focus_paths (workflow: cb_focus_paths)`
|
|
1922
|
+
: `${raisable.length === 0 ? "" : `raise ${raisable.join(" and ")}; `}${atHardMax.join("; ")}; reduce or split focus_paths (workflow: cb_focus_paths)`;
|
|
1923
|
+
const shown = entries.slice(0, DELEGATE_FAILURE_MAX_OMISSION_ENTRIES);
|
|
1924
|
+
const droppedSuffix = entries.length > shown.length
|
|
1925
|
+
? ` (+${entries.length - shown.length} more)`
|
|
1926
|
+
: "";
|
|
1927
|
+
return `delegate finalizer omitted requested local evidence: required focus alone exceeds the local reconstruction budget${diagnostics.preDispatch === true ? " (pre-dispatch)" : ""} (binding cap: ${bindingCaps.join(", ")}); ${leverText}. ${entries.length} required path(s) not retained: ${shown.join("; ")}${droppedSuffix}`;
|
|
1928
|
+
}
|
|
1929
|
+
function budgetValue(budget, key) {
|
|
1930
|
+
const value = budget?.[key];
|
|
1931
|
+
return typeof value === "number" ? value : undefined;
|
|
1932
|
+
}
|
|
1933
|
+
function hardLimitValue(key) {
|
|
1934
|
+
const value = CONTEXT_BUILDER_PUBLIC_BUDGET_HARD_LIMITS[key];
|
|
1935
|
+
return typeof value === "number" ? value : undefined;
|
|
1936
|
+
}
|
|
1937
|
+
// Bounded per-path provenance for the finalizer success path. Warnings are
|
|
1938
|
+
// capped at the diagnostic text length, so each path gets its own entry. The
|
|
1939
|
+
// wording deliberately avoids the omission/range vocabulary that
|
|
1940
|
+
// reconcileContextBuilderWarnings treats as stale for selected paths.
|
|
1941
|
+
const FINALIZER_DROP_PROVENANCE_MAX_WARNINGS = 12;
|
|
1942
|
+
function finalizerDropProvenanceWarnings(input) {
|
|
1943
|
+
const finalizerSelectedIds = new Set(input.finalizerSelected.map((selection) => selection.candidate_id));
|
|
1944
|
+
const folded = new Set(input.foldedPaths);
|
|
1945
|
+
const entries = [];
|
|
1946
|
+
for (const required of input.requiredFocus) {
|
|
1947
|
+
if (!folded.has(required.path)) {
|
|
1948
|
+
continue;
|
|
1949
|
+
}
|
|
1950
|
+
entries.push(`${required.path} = ${finalizerSelectedIds.has(required.candidate_id)
|
|
1951
|
+
? "finalizer_range_narrowed"
|
|
1952
|
+
: "finalizer_unselected"} (server-folded)`);
|
|
1953
|
+
}
|
|
1954
|
+
for (const omission of input.reconstruction.selection.omissions) {
|
|
1955
|
+
for (const path of omission.paths ?? []) {
|
|
1956
|
+
entries.push(`${path} = budget:${omission.reason}`);
|
|
1957
|
+
}
|
|
1958
|
+
}
|
|
1959
|
+
for (const item of input.reconstruction.compacted) {
|
|
1960
|
+
entries.push(`${item.path} = compacted_to_required_ranges`);
|
|
1961
|
+
}
|
|
1962
|
+
const shown = entries.slice(0, FINALIZER_DROP_PROVENANCE_MAX_WARNINGS);
|
|
1963
|
+
const warnings = shown.map((entry) => delegateWarning("delegate_finalizer_warning", `finalizer drop provenance: ${entry}`));
|
|
1964
|
+
if (entries.length > shown.length) {
|
|
1965
|
+
warnings.push(delegateWarning("delegate_finalizer_warning", `finalizer drop provenance: +${entries.length - shown.length} more path(s) not itemized`));
|
|
1966
|
+
}
|
|
1967
|
+
return warnings;
|
|
1968
|
+
}
|
|
1969
|
+
// Finalizer-path D1 backstop, mirroring postRepairRequiredFocusEvictionFailure
|
|
1970
|
+
// for the curator-only branch: a non-degenerate required-focus file must never
|
|
1971
|
+
// be absent from the returned selection while any non-required candidate is
|
|
1972
|
+
// still selected. A whitespace-only required file is exempt (repair drops
|
|
1973
|
+
// degenerate slices; the range re-verify still reports it).
|
|
1974
|
+
function finalizerRequiredFocusEvictionFailure(input) {
|
|
1975
|
+
const requiredKeys = new Set(requiredFocusEvidenceSelections(input).flatMap((required) => {
|
|
1976
|
+
const candidate = input.candidates.find((item) => item.id === required.candidate_id);
|
|
1977
|
+
return candidate == null ? [] : [`${candidate.root_index}:${candidate.path}`];
|
|
1978
|
+
}));
|
|
1979
|
+
if (requiredKeys.size === 0) {
|
|
1980
|
+
return undefined;
|
|
1981
|
+
}
|
|
1982
|
+
const fileKey = (file) => `${file.root_index}:${file.path}`;
|
|
1983
|
+
const selectedKeys = new Set(input.selected.filter((file) => file.ranges.length > 0).map(fileKey));
|
|
1984
|
+
if (![...selectedKeys].some((key) => !requiredKeys.has(key))) {
|
|
1985
|
+
return undefined;
|
|
1986
|
+
}
|
|
1987
|
+
const contentByKey = new Map(input.contentCandidates.map((candidate) => [fileKey(candidate), candidate.content]));
|
|
1988
|
+
const evicted = [...requiredKeys].filter((key) => !selectedKeys.has(key) && (contentByKey.get(key) ?? "x").trim().length > 0);
|
|
1989
|
+
if (evicted.length === 0) {
|
|
1990
|
+
return undefined;
|
|
1991
|
+
}
|
|
1992
|
+
return `finalizer selection evicted required-focus evidence while non-required evidence remained selected: ${evicted
|
|
1993
|
+
.map((key) => {
|
|
1994
|
+
const separator = key.indexOf(":");
|
|
1995
|
+
return `root ${key.slice(0, separator)}: ${key.slice(separator + 1)}`;
|
|
1996
|
+
})
|
|
1997
|
+
.join("; ")}`;
|
|
1998
|
+
}
|
|
1999
|
+
export const finalizerRequiredFocusEvictionFailureForTest = finalizerRequiredFocusEvictionFailure;
|
|
1555
2000
|
// Curator-only post-repair guard. Unlike the finalizer branch, the curator-only
|
|
1556
2001
|
// path does not fold required-focus ranges, so its baseline is the curator's
|
|
1557
2002
|
// own pre-repair selection. Repair is allowed to:
|
|
@@ -1722,6 +2167,9 @@ function isSameRootRelativePathOrDescendant(candidatePath, parentPath) {
|
|
|
1722
2167
|
}
|
|
1723
2168
|
return candidatePath === parentPath || candidatePath.startsWith(`${parentPath}/`);
|
|
1724
2169
|
}
|
|
2170
|
+
export function isMaterialDelegateEvidenceWarningForTest(message) {
|
|
2171
|
+
return isMaterialDelegateEvidenceWarning(message);
|
|
2172
|
+
}
|
|
1725
2173
|
function isMaterialDelegateEvidenceWarning(message) {
|
|
1726
2174
|
const text = message.toLowerCase();
|
|
1727
2175
|
if (isNonBlockingDelegateWarning(text)) {
|
|
@@ -1881,6 +2329,53 @@ function packetCoverageObligationRules(packet) {
|
|
|
1881
2329
|
"- 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
2330
|
];
|
|
1883
2331
|
}
|
|
2332
|
+
/**
|
|
2333
|
+
* Renders the caller-anchored required-focus contract.
|
|
2334
|
+
*
|
|
2335
|
+
* Rendered only when the packet carries a candidate with focus_requested:true
|
|
2336
|
+
* and non-empty required_focus_ranges, i.e. exactly the candidates
|
|
2337
|
+
* requiredFocusEvidenceSelections enforces. Without it the finalizer had no
|
|
2338
|
+
* signal that anchored files are mandatory and deduplicated near-duplicate
|
|
2339
|
+
* anchors as a quality judgment.
|
|
2340
|
+
*
|
|
2341
|
+
* Phrasing guard: the finalizer may echo these lines into `warnings`, which
|
|
2342
|
+
* isMaterialDelegateEvidenceWarning scans with a negation-blind proximity
|
|
2343
|
+
* match. Keep gap vocabulary (missing, omitted, unavailable, not selected, ...)
|
|
2344
|
+
* out of these lines; tests/context-builder/delegate-prompt.test.ts runs every
|
|
2345
|
+
* rendered line through that matcher.
|
|
2346
|
+
*/
|
|
2347
|
+
export function packetRequiredFocusRules(stage, packet) {
|
|
2348
|
+
if (packet == null || typeof packet !== "object") {
|
|
2349
|
+
return [];
|
|
2350
|
+
}
|
|
2351
|
+
const candidates = packet["candidates"];
|
|
2352
|
+
const hasRequiredFocus = Array.isArray(candidates) &&
|
|
2353
|
+
candidates.some((candidate) => {
|
|
2354
|
+
if (candidate == null || typeof candidate !== "object") {
|
|
2355
|
+
return false;
|
|
2356
|
+
}
|
|
2357
|
+
const record = candidate;
|
|
2358
|
+
const ranges = record["required_focus_ranges"];
|
|
2359
|
+
return (record["focus_requested"] === true &&
|
|
2360
|
+
Array.isArray(ranges) &&
|
|
2361
|
+
ranges.length > 0);
|
|
2362
|
+
});
|
|
2363
|
+
if (!hasRequiredFocus) {
|
|
2364
|
+
return [];
|
|
2365
|
+
}
|
|
2366
|
+
if (stage === "curator") {
|
|
2367
|
+
return [
|
|
2368
|
+
"- Candidates with focus_requested:true and a non-empty required_focus_ranges are caller-anchored: include each one in selected with ranges covering its required_focus_ranges, and do not deduplicate them.",
|
|
2369
|
+
];
|
|
2370
|
+
}
|
|
2371
|
+
return [
|
|
2372
|
+
"Caller-anchored focus (mandatory, server-enforced):",
|
|
2373
|
+
"- Every packet.candidates entry with focus_requested:true and a non-empty required_focus_ranges MUST appear in selected, with ranges that cover every one of its required_focus_ranges entries.",
|
|
2374
|
+
"- You may add ranges to those candidates. Never drop one of them and never narrow its ranges below its required_focus_ranges.",
|
|
2375
|
+
"- Do not deduplicate those candidates, even when two of them look like near-duplicates (for example the same document at two paths). Keep all of them.",
|
|
2376
|
+
"- The server folds a dropped anchored candidate back into selected and admits anchored candidates before any other candidate, so dropping one never removes it; it only displaces lower-priority evidence from the local budget.",
|
|
2377
|
+
];
|
|
2378
|
+
}
|
|
1884
2379
|
/**
|
|
1885
2380
|
* Renders the finalizer scoring contract.
|
|
1886
2381
|
*
|
|
@@ -1947,6 +2442,23 @@ function finalizerExampleScore(minScore) {
|
|
|
1947
2442
|
// `finalizer_score` observability. A higher exemplar always clears a lower gate.
|
|
1948
2443
|
return minScore == null || minScore <= 0 ? 9 : minScore;
|
|
1949
2444
|
}
|
|
2445
|
+
// Role prompts (conf/prompts) tell a reviewer to inspect files and to reject
|
|
2446
|
+
// work lacking regression tests; a finalizer obeying that either stalls on
|
|
2447
|
+
// tool use or vetoes a bounded packet for evidence it was never meant to hold.
|
|
2448
|
+
// Rendered for the finalizer only — the curator has no veto to misuse.
|
|
2449
|
+
const FINALIZER_ROLE_OVERLAY = "These finalizer rules override any role instruction to read or inspect files or to reject packets missing regression tests: do not read files; the packet is the only evidence. Report task-relevant evidence absent from packet.candidates in external_gaps, not as grounds for rejection.";
|
|
2450
|
+
function finalizerExternalGapRules(policy) {
|
|
2451
|
+
return [
|
|
2452
|
+
"External gaps:",
|
|
2453
|
+
`- Evidence the task needs that is ABSENT from packet.candidates goes in external_gaps as [{"path":"<root-relative path>","reason":"<why the task needs it>"}] (at most ${MAX_FINALIZER_EXTERNAL_GAPS} entries). Never lower score for it.`,
|
|
2454
|
+
"- Never list a path that is in packet.candidates or under a focus path in external_gaps; the server verifies every entry and rejects the whole answer if one is wrong.",
|
|
2455
|
+
"- Never list a directory that contains a packet.candidates path or a focus path; name the specific missing file instead.",
|
|
2456
|
+
"- If evidence present in packet.candidates is insufficient, that is still approved:false with concrete omissions, not an external gap.",
|
|
2457
|
+
policy === "degrade"
|
|
2458
|
+
? "- Absent evidence reported in external_gaps is NOT grounds for approved:false; the server delivers the selection with those gaps marked as conditional."
|
|
2459
|
+
: "- The server will fail this request if external_gaps is non-empty; report absent evidence there honestly anyway rather than hiding it.",
|
|
2460
|
+
];
|
|
2461
|
+
}
|
|
1950
2462
|
export function renderDelegatePrompt(stage, packet, options) {
|
|
1951
2463
|
// Diagnostic-array exemplars are non-empty on purpose: empty arrays cannot
|
|
1952
2464
|
// communicate element types, and a live finalizer failure returned
|
|
@@ -1954,7 +2466,7 @@ export function renderDelegatePrompt(stage, packet, options) {
|
|
|
1954
2466
|
// show each element is one plain string.
|
|
1955
2467
|
const outputShape = stage === "curator"
|
|
1956
2468
|
? '{"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>"]}`;
|
|
2469
|
+
: `{"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>"],"external_gaps":[]}`;
|
|
1958
2470
|
const stageRules = stage === "curator"
|
|
1959
2471
|
? [
|
|
1960
2472
|
"Curator-specific rules:",
|
|
@@ -1978,12 +2490,10 @@ export function renderDelegatePrompt(stage, packet, options) {
|
|
|
1978
2490
|
"- 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.",
|
|
1979
2491
|
"- 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.",
|
|
1980
2492
|
"- 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.",
|
|
1981
|
-
//
|
|
1982
|
-
//
|
|
1983
|
-
//
|
|
1984
|
-
|
|
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.",
|
|
2493
|
+
// Evidence absent from the packet is routed to external_gaps (see
|
|
2494
|
+
// finalizerExternalGapRules), not warnings: warnings are scanned by
|
|
2495
|
+
// isMaterialEvidenceGapWarning and a gap phrased there hard-fails.
|
|
2496
|
+
"- If approving with score at or above the requested minimum, omissions MUST be [].",
|
|
1987
2497
|
// The observed live schema failure was object-shaped `omissions`;
|
|
1988
2498
|
// state the element type explicitly instead of relying on exemplars.
|
|
1989
2499
|
// (No open_questions here: the finalizer schema is strict and would
|
|
@@ -1994,6 +2504,7 @@ export function renderDelegatePrompt(stage, packet, options) {
|
|
|
1994
2504
|
return [
|
|
1995
2505
|
`You are the context_builder ${stage}.`,
|
|
1996
2506
|
`Prompt version: ${CONTEXT_BUILDER_DELEGATE_PROMPT_VERSION}.`,
|
|
2507
|
+
...(stage === "finalizer" ? [FINALIZER_ROLE_OVERLAY] : []),
|
|
1997
2508
|
"Return exactly one strict JSON object and no prose.",
|
|
1998
2509
|
"For this tool call, the JSON packet below is the only source of truth.",
|
|
1999
2510
|
stage === "curator"
|
|
@@ -2005,7 +2516,11 @@ export function renderDelegatePrompt(stage, packet, options) {
|
|
|
2005
2516
|
? "Use the excerpt, search_hits, deterministic_selection, omissions, and task fields as evidence; the packet intentionally omits full source content."
|
|
2006
2517
|
: "Use the excerpt, search_hits, curator_proposal, deterministic_selection, omissions, and task fields as evidence; the packet intentionally omits full source content.",
|
|
2007
2518
|
...stageRules,
|
|
2519
|
+
...packetRequiredFocusRules(stage, packet),
|
|
2008
2520
|
...(stage === "finalizer" ? finalizerScoringRules(options?.minScore) : []),
|
|
2521
|
+
...(stage === "finalizer"
|
|
2522
|
+
? finalizerExternalGapRules(options?.externalGapPolicy ?? "fail")
|
|
2523
|
+
: []),
|
|
2009
2524
|
...packetCoverageObligationRules(packet),
|
|
2010
2525
|
...(minimumOutput == null
|
|
2011
2526
|
? []
|
|
@@ -2200,6 +2715,7 @@ function buildMinimumDelegateOutput(stage, packet, minScore) {
|
|
|
2200
2715
|
"minimum curator proposal selection used; review packet evidence for more context",
|
|
2201
2716
|
],
|
|
2202
2717
|
omissions: [],
|
|
2718
|
+
external_gaps: [],
|
|
2203
2719
|
};
|
|
2204
2720
|
return JSON.stringify(output);
|
|
2205
2721
|
}
|
|
@@ -2362,12 +2878,115 @@ function finalizerScoreDiagnostic(json, minScore) {
|
|
|
2362
2878
|
? `; finalizer_score=${rawScore}; min_score=${minScore}`
|
|
2363
2879
|
: `; min_score=${minScore}`;
|
|
2364
2880
|
}
|
|
2881
|
+
const FINALIZER_REJECTION_LEVERS = 'levers: add missing path(s) to focus_paths (workflow: cb_focus_paths); exclude vendor/generated dirs via exclude_globs (workflow: cb_exclude_globs); or set external_gap_policy:"degrade" (workflow: cb_delegate.external_gap_policy)';
|
|
2882
|
+
// Verdict-first text for a finalizer veto: what the finalizer said (approval,
|
|
2883
|
+
// its own omission list) leads, because that is what the caller acts on; the
|
|
2884
|
+
// score and packet diagnostics trail. Each omission is provider text, so it is
|
|
2885
|
+
// redacted on its own before joining — the whole-string redactor would
|
|
2886
|
+
// otherwise blank the entire reason when any single entry carries a secret.
|
|
2887
|
+
function formatFinalizerRejectionFailure(err, packetDiagnostic) {
|
|
2888
|
+
const lead = err.reasonKind === "score"
|
|
2889
|
+
? `score=${err.score} below min_score=${err.minScore}`
|
|
2890
|
+
: `approved=${err.approved}`;
|
|
2891
|
+
const entries = err.reasonKind === "material_warning"
|
|
2892
|
+
? { label: "material_warnings", values: err.materialWarnings }
|
|
2893
|
+
: { label: "omissions", values: err.omissions };
|
|
2894
|
+
const shown = entries.values
|
|
2895
|
+
.slice(0, DELEGATE_FAILURE_MAX_OMISSION_ENTRIES)
|
|
2896
|
+
.map((entry) => sanitizeDelegateFailureText(entry, DELEGATE_FAILURE_MAX_ENTRY_TEXT_LENGTH));
|
|
2897
|
+
const dropped = entries.values.length - shown.length;
|
|
2898
|
+
const entryText = shown.length === 0
|
|
2899
|
+
? `${entries.label}=0`
|
|
2900
|
+
: `${entries.label}=${entries.values.length}: ${shown.join("; ")}${dropped > 0 ? ` (+${dropped} more omissions)` : ""}`;
|
|
2901
|
+
return [
|
|
2902
|
+
`finalizer_validation: ${lead}`,
|
|
2903
|
+
entryText,
|
|
2904
|
+
`finalizer_score=${err.score}`,
|
|
2905
|
+
`min_score=${err.minScore}`,
|
|
2906
|
+
FINALIZER_REJECTION_LEVERS,
|
|
2907
|
+
packetDiagnostic,
|
|
2908
|
+
err.message,
|
|
2909
|
+
].join("; ");
|
|
2910
|
+
}
|
|
2911
|
+
const EXTERNAL_GAP_POLICY_LEVERS = 'levers: add path(s) to focus_paths (workflow: cb_focus_paths) or set external_gap_policy:"degrade" (workflow: cb_delegate.external_gap_policy)';
|
|
2912
|
+
function formatExternalGapEntries(entries) {
|
|
2913
|
+
const shown = entries
|
|
2914
|
+
.slice(0, DELEGATE_FAILURE_MAX_OMISSION_ENTRIES)
|
|
2915
|
+
.map((entry) => sanitizeDelegateFailureText(`${entry.path} (${entry.detail})`, DELEGATE_FAILURE_MAX_ENTRY_TEXT_LENGTH));
|
|
2916
|
+
const dropped = entries.length - shown.length;
|
|
2917
|
+
return `${entries.length}: ${shown.join("; ")}${dropped > 0 ? ` (+${dropped} more external gaps)` : ""}`;
|
|
2918
|
+
}
|
|
2919
|
+
function formatExternalGapRejectionFailure(rejected) {
|
|
2920
|
+
return [
|
|
2921
|
+
`finalizer_validation: external_gaps rejected=${formatExternalGapEntries(rejected.map((gap) => ({ path: gap.path, detail: gap.why })))}`,
|
|
2922
|
+
FINALIZER_REJECTION_LEVERS,
|
|
2923
|
+
].join("; ");
|
|
2924
|
+
}
|
|
2925
|
+
function formatExternalGapPolicyFailure(external) {
|
|
2926
|
+
return [
|
|
2927
|
+
`finalizer_validation: external_gap_policy=fail; external_gaps=${formatExternalGapEntries(external.map((gap) => ({ path: gap.path, detail: gap.provenance })))}`,
|
|
2928
|
+
EXTERNAL_GAP_POLICY_LEVERS,
|
|
2929
|
+
].join("; ");
|
|
2930
|
+
}
|
|
2931
|
+
// Wording deliberately avoids the truncation/omission vocabulary that
|
|
2932
|
+
// reconcileContextBuilderWarnings filters as stale; the codes are also
|
|
2933
|
+
// exempted there because the per-gap reason is provider prose.
|
|
2934
|
+
function externalGapWarnings(external) {
|
|
2935
|
+
if (external.length === 0) {
|
|
2936
|
+
return [];
|
|
2937
|
+
}
|
|
2938
|
+
return [
|
|
2939
|
+
delegateWarning("delegate_external_gaps", `finalizer reported ${external.length} evidence gap(s) outside the delegate packet; conclusions depending on them are conditional`),
|
|
2940
|
+
...external.map((gap) => delegateWarning("delegate_external_gap", `external evidence gap: ${gap.path} (${gap.provenance}): ${gap.reason}`)),
|
|
2941
|
+
];
|
|
2942
|
+
}
|
|
2365
2943
|
function failureReasonFromError(stage, err, diagnostic) {
|
|
2366
2944
|
const message = err instanceof Error ? err.message : "unknown delegate error";
|
|
2367
2945
|
return diagnostic == null
|
|
2368
2946
|
? `${stage}: ${message}`
|
|
2369
2947
|
: `${stage}: ${diagnostic}; ${message}`;
|
|
2370
2948
|
}
|
|
2949
|
+
// P1b observability. The packet serializes a sliced obligation set while
|
|
2950
|
+
// terminal coverage evaluates the full one; name the gap so a succeeded
|
|
2951
|
+
// delegate next to degraded coverage is explainable. Wording avoids the
|
|
2952
|
+
// truncation/omission vocabulary reconcileContextBuilderWarnings filters.
|
|
2953
|
+
function packetObligationTruncationWarning(fullObligations, packetPolicy) {
|
|
2954
|
+
const counts = countDelegatePacketObligationTruncation(fullObligations, packetPolicy);
|
|
2955
|
+
if (counts == null) {
|
|
2956
|
+
return undefined;
|
|
2957
|
+
}
|
|
2958
|
+
return {
|
|
2959
|
+
code: "delegate_packet_obligations_truncated",
|
|
2960
|
+
message: `delegate packet carried ${counts.targets_included} of ${counts.targets_total} review obligation targets (${counts.ranges_included} of ${counts.ranges_total} ranges, ${counts.regression_links_included} of ${counts.regression_links_total} regression links); final coverage evaluates the full set`,
|
|
2961
|
+
};
|
|
2962
|
+
}
|
|
2963
|
+
export const packetObligationTruncationWarningForTest = packetObligationTruncationWarning;
|
|
2964
|
+
function coverageDegradedAfterDelegateWarning(coverage, delegate) {
|
|
2965
|
+
if (coverage.status !== "degraded" ||
|
|
2966
|
+
delegate?.status !== "succeeded" ||
|
|
2967
|
+
delegate.fallback_used) {
|
|
2968
|
+
return undefined;
|
|
2969
|
+
}
|
|
2970
|
+
const unclassifiableRegression = coverage.missing_required.includes("regression_test") &&
|
|
2971
|
+
coverage.unclassifiable.includes("regression_test");
|
|
2972
|
+
return {
|
|
2973
|
+
code: "coverage_degraded_after_delegate",
|
|
2974
|
+
message: `delegate succeeded but final coverage is degraded; unmet required classes: ${coverage.missing_required.join(", ") || "none"}; unclassifiable regression: ${unclassifiableRegression
|
|
2975
|
+
? "yes (no confident regression link, so no selection could satisfy it)"
|
|
2976
|
+
: "no"}`,
|
|
2977
|
+
};
|
|
2978
|
+
}
|
|
2979
|
+
const DELEGATE_JSON_CORRECTIVE_RETRY_WARNING_CODE = "delegate_json_corrective_retry";
|
|
2980
|
+
// Informational only: the retried call produced valid JSON, so the run is not
|
|
2981
|
+
// degraded; the warning keeps the recovered formatting failure observable.
|
|
2982
|
+
function correctiveRetryWarnings(stage, call) {
|
|
2983
|
+
if (call.corrective_retry?.outcome !== "succeeded") {
|
|
2984
|
+
return [];
|
|
2985
|
+
}
|
|
2986
|
+
return [
|
|
2987
|
+
delegateWarning(DELEGATE_JSON_CORRECTIVE_RETRY_WARNING_CODE, `${stage} returned malformed JSON once; one corrective retry succeeded`),
|
|
2988
|
+
];
|
|
2989
|
+
}
|
|
2371
2990
|
function delegateWarning(code, message) {
|
|
2372
2991
|
return {
|
|
2373
2992
|
code: sanitizeDiagnosticText(code),
|
|
@@ -2423,6 +3042,15 @@ function reconcileContextBuilderWarnings(input) {
|
|
|
2423
3042
|
return [];
|
|
2424
3043
|
}
|
|
2425
3044
|
return input.warnings.filter((warning) => {
|
|
3045
|
+
// External-gap warnings name paths the packet never carried, so they can
|
|
3046
|
+
// never be stale against the selection; their reason text is provider
|
|
3047
|
+
// prose and may happen to use omission vocabulary. The corrective-retry
|
|
3048
|
+
// notice names no evidence at all, so it is never stale either.
|
|
3049
|
+
if (warning.code === "delegate_external_gaps" ||
|
|
3050
|
+
warning.code === "delegate_external_gap" ||
|
|
3051
|
+
warning.code === DELEGATE_JSON_CORRECTIVE_RETRY_WARNING_CODE) {
|
|
3052
|
+
return true;
|
|
3053
|
+
}
|
|
2426
3054
|
const text = warning.message.toLowerCase();
|
|
2427
3055
|
if (isStaleTruncationWarning(text, input)) {
|
|
2428
3056
|
return false;
|