@claudexor/orchestrator 4.8.2 → 5.0.1
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/dist/account-pool.d.ts +4 -1
- package/dist/account-pool.d.ts.map +1 -1
- package/dist/account-pool.js +25 -19
- package/dist/account-pool.js.map +1 -1
- package/dist/account-resolution.d.ts +1 -4
- package/dist/account-resolution.d.ts.map +1 -1
- package/dist/account-resolution.js +19 -42
- package/dist/account-resolution.js.map +1 -1
- package/dist/attemptOutputMarkers.d.ts +7 -0
- package/dist/attemptOutputMarkers.d.ts.map +1 -1
- package/dist/attemptOutputMarkers.js +1 -1
- package/dist/attemptOutputMarkers.js.map +1 -1
- package/dist/attemptTelemetry.d.ts +4 -1
- package/dist/attemptTelemetry.d.ts.map +1 -1
- package/dist/attemptTelemetry.js +9 -26
- package/dist/attemptTelemetry.js.map +1 -1
- package/dist/candidateEvidence.d.ts +12 -9
- package/dist/candidateEvidence.d.ts.map +1 -1
- package/dist/candidateEvidence.js +38 -13
- package/dist/candidateEvidence.js.map +1 -1
- package/dist/candidateOutputs.d.ts +44 -22
- package/dist/candidateOutputs.d.ts.map +1 -1
- package/dist/candidateOutputs.js +116 -72
- package/dist/candidateOutputs.js.map +1 -1
- package/dist/candidateReview.d.ts.map +1 -1
- package/dist/candidateReview.js +44 -8
- package/dist/candidateReview.js.map +1 -1
- package/dist/candidateTerminals.d.ts +1 -0
- package/dist/candidateTerminals.d.ts.map +1 -1
- package/dist/candidateTerminals.js +11 -6
- package/dist/candidateTerminals.js.map +1 -1
- package/dist/candidateWorkProduct.d.ts +2 -1
- package/dist/candidateWorkProduct.d.ts.map +1 -1
- package/dist/candidateWorkProduct.js +6 -3
- package/dist/candidateWorkProduct.js.map +1 -1
- package/dist/captureRefusal.d.ts +29 -1
- package/dist/captureRefusal.d.ts.map +1 -1
- package/dist/captureRefusal.js +114 -8
- package/dist/captureRefusal.js.map +1 -1
- package/dist/continuation-chain.test-support.d.ts +473 -0
- package/dist/continuation-chain.test-support.d.ts.map +1 -0
- package/dist/continuation-chain.test-support.js +206 -0
- package/dist/continuation-chain.test-support.js.map +1 -0
- package/dist/continuation-custody.d.ts +4 -1
- package/dist/continuation-custody.d.ts.map +1 -1
- package/dist/continuation-custody.js +68 -28
- package/dist/continuation-custody.js.map +1 -1
- package/dist/continuation-evidence-io.d.ts +30 -4
- package/dist/continuation-evidence-io.d.ts.map +1 -1
- package/dist/continuation-evidence-io.js +162 -46
- package/dist/continuation-evidence-io.js.map +1 -1
- package/dist/continuation-evidence.d.ts +40 -3
- package/dist/continuation-evidence.d.ts.map +1 -1
- package/dist/continuation-evidence.js +85 -11
- package/dist/continuation-evidence.js.map +1 -1
- package/dist/continuation-input.d.ts +10 -1
- package/dist/continuation-input.d.ts.map +1 -1
- package/dist/continuation-input.js +21 -20
- package/dist/continuation-input.js.map +1 -1
- package/dist/continuation-later.d.ts +30 -0
- package/dist/continuation-later.d.ts.map +1 -0
- package/dist/continuation-later.js +108 -0
- package/dist/continuation-later.js.map +1 -0
- package/dist/continue-from.d.ts +4 -3
- package/dist/continue-from.d.ts.map +1 -1
- package/dist/continue-from.js +62 -26
- package/dist/continue-from.js.map +1 -1
- package/dist/continuity-notice.d.ts +3 -1
- package/dist/continuity-notice.d.ts.map +1 -1
- package/dist/continuity-notice.js +3 -1
- package/dist/continuity-notice.js.map +1 -1
- package/dist/continuity-terminal.d.ts +16 -0
- package/dist/continuity-terminal.d.ts.map +1 -1
- package/dist/continuity-terminal.js +21 -0
- package/dist/continuity-terminal.js.map +1 -1
- package/dist/continuity.d.ts +1 -1
- package/dist/continuity.d.ts.map +1 -1
- package/dist/contract-gates.d.ts.map +1 -1
- package/dist/contract-gates.js +11 -6
- package/dist/contract-gates.js.map +1 -1
- package/dist/credential-cooldown.d.ts +2 -1
- package/dist/credential-cooldown.d.ts.map +1 -1
- package/dist/credential-cooldown.js +16 -7
- package/dist/credential-cooldown.js.map +1 -1
- package/dist/credential-preflight.d.ts.map +1 -1
- package/dist/credential-preflight.js +10 -7
- package/dist/credential-preflight.js.map +1 -1
- package/dist/credential-profile-rotation.d.ts +10 -3
- package/dist/credential-profile-rotation.d.ts.map +1 -1
- package/dist/credential-profile-rotation.js +28 -26
- package/dist/credential-profile-rotation.js.map +1 -1
- package/dist/deepScanReducer.d.ts +0 -3
- package/dist/deepScanReducer.d.ts.map +1 -1
- package/dist/deepScanReducer.js +22 -26
- package/dist/deepScanReducer.js.map +1 -1
- package/dist/delegationFailure.d.ts +34 -5
- package/dist/delegationFailure.d.ts.map +1 -1
- package/dist/delegationFailure.js +6 -4
- package/dist/delegationFailure.js.map +1 -1
- package/dist/delegationToolEvidence.d.ts +2 -0
- package/dist/delegationToolEvidence.d.ts.map +1 -1
- package/dist/delegationToolEvidence.js +21 -0
- package/dist/delegationToolEvidence.js.map +1 -1
- package/dist/diffReview.d.ts +1 -8
- package/dist/diffReview.d.ts.map +1 -1
- package/dist/diffReview.js +3 -18
- package/dist/diffReview.js.map +1 -1
- package/dist/directoryCandidate.d.ts +1 -1
- package/dist/directoryCandidate.d.ts.map +1 -1
- package/dist/directoryCandidate.js +13 -13
- package/dist/directoryCandidate.js.map +1 -1
- package/dist/inrun-continuity-carrier.d.ts +11 -1
- package/dist/inrun-continuity-carrier.d.ts.map +1 -1
- package/dist/inrun-continuity-carrier.js +32 -10
- package/dist/inrun-continuity-carrier.js.map +1 -1
- package/dist/inrun-continuity-types.d.ts +17 -4
- package/dist/inrun-continuity-types.d.ts.map +1 -1
- package/dist/inrun-continuity.d.ts +8 -3
- package/dist/inrun-continuity.d.ts.map +1 -1
- package/dist/inrun-continuity.js +41 -37
- package/dist/inrun-continuity.js.map +1 -1
- package/dist/inrun-continuity.test-support.d.ts +61 -4
- package/dist/inrun-continuity.test-support.d.ts.map +1 -1
- package/dist/inrun-continuity.test-support.js +12 -2
- package/dist/inrun-continuity.test-support.js.map +1 -1
- package/dist/modelGovernance.d.ts +87 -9
- package/dist/modelGovernance.d.ts.map +1 -1
- package/dist/modelGovernance.js +110 -15
- package/dist/modelGovernance.js.map +1 -1
- package/dist/orchestrator-credentials.d.ts +9 -4
- package/dist/orchestrator-credentials.d.ts.map +1 -1
- package/dist/orchestrator-credentials.js +14 -6
- package/dist/orchestrator-credentials.js.map +1 -1
- package/dist/orchestrator.d.ts +0 -1
- package/dist/orchestrator.d.ts.map +1 -1
- package/dist/orchestrator.js +247 -223
- package/dist/orchestrator.js.map +1 -1
- package/dist/planFinalize.d.ts.map +1 -1
- package/dist/planFinalize.js +3 -7
- package/dist/planFinalize.js.map +1 -1
- package/dist/planRun.js +2 -2
- package/dist/planRun.js.map +1 -1
- package/dist/plannerAttempt.d.ts +0 -2
- package/dist/plannerAttempt.d.ts.map +1 -1
- package/dist/plannerAttempt.js +64 -30
- package/dist/plannerAttempt.js.map +1 -1
- package/dist/policyFindings.d.ts +3 -0
- package/dist/policyFindings.d.ts.map +1 -1
- package/dist/policyFindings.js +11 -1
- package/dist/policyFindings.js.map +1 -1
- package/dist/pool-attempt-evidence.d.ts +1 -1
- package/dist/pool-attempt-evidence.d.ts.map +1 -1
- package/dist/pool-attempt-evidence.js +1 -1
- package/dist/pool-attempt-evidence.js.map +1 -1
- package/dist/primary-divergence.d.ts +7 -2
- package/dist/primary-divergence.d.ts.map +1 -1
- package/dist/primary-divergence.js +13 -5
- package/dist/primary-divergence.js.map +1 -1
- package/dist/quota-refusal-scope.d.ts +39 -1
- package/dist/quota-refusal-scope.d.ts.map +1 -1
- package/dist/quota-refusal-scope.js +95 -1
- package/dist/quota-refusal-scope.js.map +1 -1
- package/dist/readonlyTerminals.d.ts +0 -1
- package/dist/readonlyTerminals.d.ts.map +1 -1
- package/dist/readonlyTerminals.js +5 -3
- package/dist/readonlyTerminals.js.map +1 -1
- package/dist/requestRequirements.d.ts +23 -0
- package/dist/requestRequirements.d.ts.map +1 -1
- package/dist/requestRequirements.js +22 -0
- package/dist/requestRequirements.js.map +1 -1
- package/dist/resultNotice.d.ts +14 -0
- package/dist/resultNotice.d.ts.map +1 -0
- package/dist/resultNotice.js +21 -0
- package/dist/resultNotice.js.map +1 -0
- package/dist/reviewerInventory.d.ts +47 -0
- package/dist/reviewerInventory.d.ts.map +1 -0
- package/dist/reviewerInventory.js +74 -0
- package/dist/reviewerInventory.js.map +1 -0
- package/dist/reviewerPanel.d.ts.map +1 -1
- package/dist/reviewerPanel.js +37 -60
- package/dist/reviewerPanel.js.map +1 -1
- package/dist/routing-failure.d.ts +2 -1
- package/dist/routing-failure.d.ts.map +1 -1
- package/dist/routing-failure.js +2 -0
- package/dist/routing-failure.js.map +1 -1
- package/dist/runAdmission.d.ts.map +1 -1
- package/dist/runAdmission.js +1 -14
- package/dist/runAdmission.js.map +1 -1
- package/dist/runEventLog.js +2 -2
- package/dist/runEventLog.js.map +1 -1
- package/dist/runFactsBuilder.d.ts.map +1 -1
- package/dist/runFactsBuilder.js +18 -10
- package/dist/runFactsBuilder.js.map +1 -1
- package/dist/runFactsDeliverable.d.ts.map +1 -1
- package/dist/runFactsDeliverable.js +7 -1
- package/dist/runFactsDeliverable.js.map +1 -1
- package/dist/runSupport.d.ts +5 -10
- package/dist/runSupport.d.ts.map +1 -1
- package/dist/runSupport.js +38 -40
- package/dist/runSupport.js.map +1 -1
- package/dist/runTerminalResults.d.ts +9 -1
- package/dist/runTerminalResults.d.ts.map +1 -1
- package/dist/runTerminalResults.js +27 -5
- package/dist/runTerminalResults.js.map +1 -1
- package/dist/savedCandidate.d.ts +43 -0
- package/dist/savedCandidate.d.ts.map +1 -0
- package/dist/savedCandidate.js +88 -0
- package/dist/savedCandidate.js.map +1 -0
- package/dist/secretDiff.d.ts +85 -46
- package/dist/secretDiff.d.ts.map +1 -1
- package/dist/secretDiff.js +123 -126
- package/dist/secretDiff.js.map +1 -1
- package/dist/task-contract-builder.d.ts.map +1 -1
- package/dist/task-contract-builder.js +5 -7
- package/dist/task-contract-builder.js.map +1 -1
- package/dist/workReportEnvelope.d.ts.map +1 -1
- package/dist/workReportEnvelope.js +4 -16
- package/dist/workReportEnvelope.js.map +1 -1
- package/package.json +17 -17
- package/dist/persistedPatch.d.ts +0 -55
- package/dist/persistedPatch.d.ts.map +0 -1
- package/dist/persistedPatch.js +0 -132
- package/dist/persistedPatch.js.map +0 -1
package/dist/orchestrator.js
CHANGED
|
@@ -6,12 +6,12 @@ import { requestRefusalFailure } from "./runTerminalResults.js";
|
|
|
6
6
|
import { openRunScopeCustody, scopedRouteContext, scopedSpecEnv, } from "./runScope.js";
|
|
7
7
|
import { delegationBeltFor } from "./delegationBelt.js";
|
|
8
8
|
import { routePromptCacheTtl, withReviewerPromptCacheTtl } from "./prompt-cache-routing.js";
|
|
9
|
-
import { bindProcessingAdmission, processingAttemptCostEvidence, processingAdmissionForLease, updateProcessingStreamHold,
|
|
9
|
+
import { bindProcessingAdmission, processingAttemptCostEvidence, processingAdmissionForLease, updateProcessingStreamHold, } from "./processing-dispatch.js";
|
|
10
10
|
import { cancelledCandidatesResult, emptyCandidateResult, failedCandidatesResult, failAfterCandidates, } from "./candidateTerminals.js";
|
|
11
11
|
import { deepScanFailureResult, readOnlyChainFailureResult, } from "./readonlyTerminals.js";
|
|
12
12
|
import { effectiveAuthPreference, observeNativeSessionEvent, resumeSessionForProfile, } from "./credential-profiles.js";
|
|
13
13
|
import { AdmissionProfileProbes, OrchestratorCredentials, reviewerProfileResolver, } from "./orchestrator-credentials.js";
|
|
14
|
-
import { continuityWorkspace, resumableOf, resumableTerminal, } from "./continuity-terminal.js";
|
|
14
|
+
import { continuityWorkspace, carryingAttempts, resumableOf, resumableTerminal, } from "./continuity-terminal.js";
|
|
15
15
|
import { openContinuity } from "./continue-from.js";
|
|
16
16
|
import { accountPoolRows } from "./account-pool.js";
|
|
17
17
|
import { PoolAttemptEvidence, terminalPoolAttempts, withPoolAttempts, } from "./pool-attempt-evidence.js";
|
|
@@ -33,8 +33,8 @@ import { globalConfigDir } from "@claudexor/config";
|
|
|
33
33
|
import { executionRunConfig } from "./run-defaults.js";
|
|
34
34
|
import { preflightOneShot } from "./one-shot-preflight.js";
|
|
35
35
|
import { prepareRoutedProcessing, processingCostEvidence, prepareReviewerProcessing, } from "./processing-routing.js";
|
|
36
|
-
import { acceptedTryOutput, AccessProfileIncompatibleError, AuxiliaryWritableRootsUnsupportedError, CLAUDEXOR_BROWSER_ARTIFACT_SUBDIR, countsAsAgentProgress, HarnessUnavailableError,
|
|
37
|
-
import { assertRouteModelsAllowed, runModelGovernedRoute } from "./modelGovernance.js";
|
|
36
|
+
import { acceptedTryOutput, AccessProfileIncompatibleError, AnswerAssembly, appendHarnessEvent, AuxiliaryWritableRootsUnsupportedError, CLAUDEXOR_BROWSER_ARTIFACT_SUBDIR, countsAsAgentProgress, HarnessUnavailableError, withInactivityWatchdog, } from "@claudexor/core";
|
|
37
|
+
import { assertRouteModelsAllowed, isDispatchRefusal, runModelGovernedRoute, } from "./modelGovernance.js";
|
|
38
38
|
import { authModeForCredentialRoute, authModeForPreference, authRouteEvidenceFor, } from "./auth-route-classification.js";
|
|
39
39
|
import { isFullAccess, RequestRequirementsResolver } from "./requestRequirements.js";
|
|
40
40
|
import { activateDelegationParent } from "./delegation-parent-activation.js";
|
|
@@ -51,7 +51,7 @@ import { budgetFailureRecord, classifyBudgetFailure } from "./budgetFailure.js";
|
|
|
51
51
|
import { finalizeStructuredOutput } from "./structuredOutput.js";
|
|
52
52
|
import { prepareRunAdmission } from "./runAdmission.js";
|
|
53
53
|
import { dropDeltaPastBudget, emitPlanProgress, emitTransientExhausted, observeReadonlySpend, recordAdapterThrow, } from "./laneStreamEvents.js";
|
|
54
|
-
import { promptWithEngineConstraints, sleep,
|
|
54
|
+
import { promptWithEngineConstraints, sleep, harnessEventPayload, safeErrorMessage, renderSummary, observeBudgetSignals, rotateOnStall, recordCleanAttemptMetrics, envInheritance, transientRetryPolicy, reviewerTimeoutMs, harnessInactivityTimeoutMs, observeAuthSwitch, emitPoolDegraded, deliveryRefusalFailure, winnerNeedsHuman, writeRaceDeliveryDecision, } from "./runSupport.js";
|
|
55
55
|
import { candidateStatusInRouteContext, resolveReadOnlyRouteContext, } from "./routeContext.js";
|
|
56
56
|
import { resolveAutoReviewerPanel, resolveExplicitReviewerPanel } from "./reviewerPanel.js";
|
|
57
57
|
import { ensureClaudeBridgeForRun, ensureWriteModeGitBoundary } from "./git-precondition.js";
|
|
@@ -72,16 +72,17 @@ import { finalVerifyRun, gateSpecsFromContract, renderTestsEvidence } from "./co
|
|
|
72
72
|
import { buildTaskContract } from "./task-contract-builder.js";
|
|
73
73
|
import { recordProcessingIntent } from "./retry-processing-intent.js";
|
|
74
74
|
import { prepareReviewEvidenceDir, recordReviewEvidenceCleanup } from "./reviewEvidence.js";
|
|
75
|
+
import { savedForSynthesis } from "./savedCandidate.js";
|
|
75
76
|
import { ArtifactStore } from "@claudexor/artifact-store";
|
|
76
|
-
import { buildContextPack,
|
|
77
|
-
import { WorkspaceManager, captureRawPatchEnvelope,
|
|
77
|
+
import { buildContextPack, rawContextSizer, writeEvidencePacket } from "@claudexor/context";
|
|
78
|
+
import { WorkspaceManager, captureRawPatchEnvelope, consumeRawPatchEnvelope, snapshotTree, } from "@claudexor/workspace";
|
|
78
79
|
import { blockedDecisionOverride, finalVerifyBlocks, verifyAndDeliverFiles, verifyAndDeliver, } from "@claudexor/delivery";
|
|
79
80
|
import { HarnessGateway } from "@claudexor/gateway";
|
|
80
81
|
import { ReadinessLedger, evaluateConvergence, failureSignature, gatesPassed, reviewCandidate, runGates, } from "@claudexor/review";
|
|
81
82
|
import { arbitrate } from "@claudexor/arbitration";
|
|
82
83
|
import { buildSynthesisPlan, decideSynthesis } from "@claudexor/synthesis";
|
|
83
|
-
import { billingKnowledgeForAuthRoute, attemptUsageCostSettlement, BudgetLedger, isBudgetTerminal,
|
|
84
|
-
import { readTextSafe,
|
|
84
|
+
import { billingKnowledgeForAuthRoute, attemptUsageCostSettlement, BudgetLedger, isBudgetTerminal, explainRanking, loadHarnessMetrics, promptFingerprint, rankHarnesses, } from "@claudexor/budget";
|
|
85
|
+
import { readTextSafe, hashJson, newId, noProjectRepoRoot, redactSecrets, safeInvoke, userConfigDir, } from "@claudexor/util";
|
|
85
86
|
const LABELS = "ABCDEFGHIJ".split("");
|
|
86
87
|
const NO_PROJECT_ROOT = noProjectRepoRoot();
|
|
87
88
|
export class Orchestrator {
|
|
@@ -188,20 +189,24 @@ export class Orchestrator {
|
|
|
188
189
|
await custody.settleUnannounced();
|
|
189
190
|
}
|
|
190
191
|
}
|
|
191
|
-
async resolveReviewers(
|
|
192
|
+
async resolveReviewers(run, onIgnoredSetting) {
|
|
192
193
|
if (this.deps.reviewers)
|
|
193
194
|
return this.deps.reviewers;
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
}
|
|
197
|
-
return resolveAutoReviewerPanel({
|
|
195
|
+
const { repoRoot: cwd, authPreference, signal } = run;
|
|
196
|
+
const panelDeps = {
|
|
198
197
|
cwd,
|
|
199
198
|
registry: this.deps.registry,
|
|
200
199
|
harnessSettings: this.config(cwd)?.global.harnesses ?? {},
|
|
201
|
-
authPreferenceFor: (id) => this.authPreferenceForHarness(cwd, id,
|
|
202
|
-
resolveReviewerProfile: reviewerProfileResolver(this.credentials, cwd),
|
|
200
|
+
authPreferenceFor: (id) => this.authPreferenceForHarness(cwd, id, authPreference),
|
|
201
|
+
resolveReviewerProfile: reviewerProfileResolver(this.credentials, cwd, signal),
|
|
203
202
|
onIgnoredSetting,
|
|
204
|
-
}
|
|
203
|
+
};
|
|
204
|
+
if (this.deps.reviewerPanel && this.deps.reviewerPanel.length > 0)
|
|
205
|
+
return resolveExplicitReviewerPanel(panelDeps, this.deps.reviewerPanel);
|
|
206
|
+
return resolveAutoReviewerPanel(panelDeps, {
|
|
207
|
+
reviewerModels: this.deps.reviewerModels,
|
|
208
|
+
reviewerEfforts: this.deps.reviewerEfforts,
|
|
209
|
+
});
|
|
205
210
|
}
|
|
206
211
|
/**
|
|
207
212
|
* Resolve reviewers INSIDE a strategy, after run-dir creation: an explicit
|
|
@@ -217,7 +222,7 @@ export class Orchestrator {
|
|
|
217
222
|
// Reviewer knobs a panel omitted or dropped → ignored-settings channel (QA-070):
|
|
218
223
|
const warn = (d) => void log.emit("review.preflight", { ignored_settings: [d] });
|
|
219
224
|
const config = this.config(input.repoRoot).global;
|
|
220
|
-
const reviewers = await this.resolveReviewers(input
|
|
225
|
+
const reviewers = await this.resolveReviewers(input, warn);
|
|
221
226
|
return {
|
|
222
227
|
reviewers: await prepareReviewerProcessing(withReviewerPromptCacheTtl(reviewers, input.promptCacheTtl, warn), input, config, input.paidBudget ?? this.deps.paidBudget ?? config.budget.paid_budget_per_run),
|
|
223
228
|
};
|
|
@@ -256,16 +261,6 @@ export class Orchestrator {
|
|
|
256
261
|
};
|
|
257
262
|
}
|
|
258
263
|
}
|
|
259
|
-
async resolveExplicitReviewerPanel(cwd, panel, runAuthPreference, onIgnoredSetting) {
|
|
260
|
-
return resolveExplicitReviewerPanel({
|
|
261
|
-
cwd,
|
|
262
|
-
registry: this.deps.registry,
|
|
263
|
-
harnessSettings: this.config(cwd)?.global.harnesses ?? {},
|
|
264
|
-
authPreferenceFor: (id) => this.authPreferenceForHarness(cwd, id, runAuthPreference),
|
|
265
|
-
resolveReviewerProfile: reviewerProfileResolver(this.credentials, cwd),
|
|
266
|
-
onIgnoredSetting,
|
|
267
|
-
}, panel);
|
|
268
|
-
}
|
|
269
264
|
authPreferenceForHarness(repoRoot, harnessId, runAuthPreference) {
|
|
270
265
|
const cfg = this.config(repoRoot)?.global;
|
|
271
266
|
return effectiveAuthPreference(runAuthPreference, cfg?.harnesses?.[harnessId]?.auth_preference, cfg?.routing?.auth_preference);
|
|
@@ -587,6 +582,11 @@ export class Orchestrator {
|
|
|
587
582
|
dropLane(id, "access", `${id} (cannot apply execution.auxiliaryWritableRoots under ${requiredAccess})`);
|
|
588
583
|
continue;
|
|
589
584
|
}
|
|
585
|
+
const readRefusal = this.requestRequirements.projectReadRefusal(intent, input, status);
|
|
586
|
+
if (readRefusal) {
|
|
587
|
+
dropLane(id, "capability", readRefusal);
|
|
588
|
+
continue;
|
|
589
|
+
}
|
|
590
590
|
const reason = status.reasons.length > 0 ? `: ${status.reasons.join("; ")}` : "";
|
|
591
591
|
if (status.enabledIntents.includes(intent) && accessSupported) {
|
|
592
592
|
pool.push({
|
|
@@ -720,7 +720,7 @@ export class Orchestrator {
|
|
|
720
720
|
throw primaryQuotaRefusal.error;
|
|
721
721
|
throw new HarnessUnavailableError(`no harness remains eligible for '${intent}' after budget and quota routing`);
|
|
722
722
|
}
|
|
723
|
-
emitPrimaryDivergence(log, input.primaryHarness, ordered, pool, dropped, browserOmissions);
|
|
723
|
+
emitPrimaryDivergence(log, input.primaryHarness, ordered, pool, dropped, browserOmissions, statusById);
|
|
724
724
|
const n = input.n ?? ordered.length;
|
|
725
725
|
const selectionOrder = ordered;
|
|
726
726
|
const out = [];
|
|
@@ -1268,7 +1268,6 @@ export class Orchestrator {
|
|
|
1268
1268
|
artifactRoot: join(paths.attemptsDir, attemptId),
|
|
1269
1269
|
observedPaths: [...observedPaths],
|
|
1270
1270
|
}));
|
|
1271
|
-
const rawContextPacket = await rawContextForEnvelope(routed.implementationTransport, envelope);
|
|
1272
1271
|
const sessionFields = runInput
|
|
1273
1272
|
? await this.sessionSpecFields(runInput, adapter.id, knobs.model, log, routed.authRouteEstimate, routed.quotaAdmission)
|
|
1274
1273
|
: undefined;
|
|
@@ -1334,11 +1333,20 @@ export class Orchestrator {
|
|
|
1334
1333
|
? { resume_session_id: sessionFields.resume_session_id }
|
|
1335
1334
|
: {}),
|
|
1336
1335
|
...(scopedEnv ? { env: scopedEnv } : {}),
|
|
1337
|
-
raw_context_packet: rawContextPacket,
|
|
1338
1336
|
stream_deltas: streamDeltas,
|
|
1339
1337
|
});
|
|
1340
1338
|
if (runInput)
|
|
1341
1339
|
this.credentials.stampProfileSelection(spec, runInput, adapter.id);
|
|
1340
|
+
// #147: bound to the project's context config; each try's dispatch sizes its own
|
|
1341
|
+
// packet for THIS request from the catalog read the gate makes for that try.
|
|
1342
|
+
const { context } = this.projectConfig(contract.repo.root);
|
|
1343
|
+
const sizeRawContext = rawContextSizer(routed.implementationTransport, envelope, context);
|
|
1344
|
+
let rawContextPacket = null;
|
|
1345
|
+
const dispatch = sizeRawContext
|
|
1346
|
+
? {
|
|
1347
|
+
rawContext: async (capacity) => (rawContextPacket = await sizeRawContext(capacity)),
|
|
1348
|
+
}
|
|
1349
|
+
: {};
|
|
1342
1350
|
bindProcessingAdmission(spec, ledger, processingLease.lease, adapter.id, attemptId, processingLease.onDenied, processingAdmission);
|
|
1343
1351
|
// Profile-less processing keeps its existing route estimate. Named rows
|
|
1344
1352
|
// receive their own verification at the shared physical-dispatch gate.
|
|
@@ -1369,7 +1377,7 @@ export class Orchestrator {
|
|
|
1369
1377
|
// QA-024: emit the belt-failure disclosure event at most once per attempt.
|
|
1370
1378
|
let beltFailureDisclosed = false;
|
|
1371
1379
|
const errors = [];
|
|
1372
|
-
let answer = new
|
|
1380
|
+
let answer = new AnswerAssembly();
|
|
1373
1381
|
let currentCapture;
|
|
1374
1382
|
const retryPolicy = transientRetryPolicy(this.config(contract.repo.root));
|
|
1375
1383
|
// QA-024: the delegation belt is the ONLY engine-owned extra MCP server
|
|
@@ -1398,13 +1406,15 @@ export class Orchestrator {
|
|
|
1398
1406
|
workOrder: prompt,
|
|
1399
1407
|
firstPrompt: spec.prompt,
|
|
1400
1408
|
poolEvidence,
|
|
1409
|
+
later: continuationPointer ? "checkpoint" : undefined, // repair/synthesis never re-plan
|
|
1410
|
+
explicitModel: modelHint !== undefined,
|
|
1401
1411
|
}), { spec, continuation: runInput?.continuation, store, telemetry }));
|
|
1402
1412
|
try {
|
|
1403
1413
|
for (let nativeTry = 0; !signal?.aborted; nativeTry += 1) {
|
|
1404
1414
|
currentCapture = undefined;
|
|
1405
1415
|
// A3 per-try isolation: no output, progress marker or model proof crosses tries.
|
|
1406
1416
|
if (nativeTry > 0) {
|
|
1407
|
-
answer = new
|
|
1417
|
+
answer = new AnswerAssembly();
|
|
1408
1418
|
beginNativeRetry(telemetry);
|
|
1409
1419
|
}
|
|
1410
1420
|
const clearFileBackedContext = stageFileBackedContext(envelope.worktree_path, fileBackedContext);
|
|
@@ -1422,8 +1432,9 @@ export class Orchestrator {
|
|
|
1422
1432
|
const transientStart = telemetry.transientFailures.length;
|
|
1423
1433
|
const rateLimitStart = telemetry.rateLimits.length;
|
|
1424
1434
|
let rawPatch = null;
|
|
1435
|
+
rawContextPacket = null; // a patch binds only the packet its own try sent
|
|
1425
1436
|
try {
|
|
1426
|
-
const watched = withInactivityWatchdog(runModelGovernedRoute(routed, runSpec), {
|
|
1437
|
+
const watched = withInactivityWatchdog(runModelGovernedRoute(routed, runSpec, dispatch), {
|
|
1427
1438
|
timeoutMs: inactivityMs,
|
|
1428
1439
|
countsAsProgress: countsAsAgentProgress,
|
|
1429
1440
|
onTimeout: () => {
|
|
@@ -1435,31 +1446,34 @@ export class Orchestrator {
|
|
|
1435
1446
|
// terminal/restart when the timeout is disabled.
|
|
1436
1447
|
isSuspended: () => (interaction?.pendingCount?.() ?? 0) > 0,
|
|
1437
1448
|
suspensionVersion: () => interaction?.suspensionVersion?.() ?? 0,
|
|
1449
|
+
stopSignal: signal, // after Stop: drained events are recorded, never used
|
|
1438
1450
|
});
|
|
1439
1451
|
for await (const ev of watched) {
|
|
1440
|
-
|
|
1452
|
+
// After Stop: stored evidence only, but file paths are still collected for the capture.
|
|
1453
|
+
const stopped = signal?.aborted === true;
|
|
1454
|
+
if (!stopped)
|
|
1455
|
+
rawPatch = captureRawPatchEnvelope(sizeRawContext !== null, rawPatch, ev);
|
|
1441
1456
|
if (ev.type === "patch_produced")
|
|
1442
1457
|
continue;
|
|
1443
|
-
const safeEv = redactHarnessEvent(ev);
|
|
1444
1458
|
if (directory)
|
|
1445
|
-
observeDirectoryPaths(observedPaths,
|
|
1446
|
-
const capped = dropDeltaPastBudget(
|
|
1447
|
-
const display = !capped &&
|
|
1448
|
-
log?.emit("harness.event", harnessEventPayload(adapter.id, attemptId,
|
|
1459
|
+
observeDirectoryPaths(observedPaths, ev, envelope.worktree_path);
|
|
1460
|
+
const capped = dropDeltaPastBudget(ev, deltaFlood, Orchestrator.MAX_DELTAS_PER_ATTEMPT, (t, p) => log?.emit(t, p), adapter.id, attemptId);
|
|
1461
|
+
const display = !stopped && !capped && ev.payload?.["buffered"] !== true;
|
|
1462
|
+
log?.emit("harness.event", harnessEventPayload(adapter.id, attemptId, ev), display);
|
|
1449
1463
|
if (signal?.aborted)
|
|
1450
|
-
|
|
1464
|
+
continue; // a live observer may itself have pressed Stop
|
|
1451
1465
|
if (display)
|
|
1452
|
-
safeInvoke(onHarnessEvent,
|
|
1453
|
-
observeAuthSwitch(log, adapter.id, attemptId,
|
|
1454
|
-
observeAttemptTelemetry(telemetry,
|
|
1455
|
-
const mismatch = continuity.observe(
|
|
1466
|
+
safeInvoke(onHarnessEvent, ev);
|
|
1467
|
+
observeAuthSwitch(log, adapter.id, attemptId, ev);
|
|
1468
|
+
observeAttemptTelemetry(telemetry, ev);
|
|
1469
|
+
const mismatch = continuity.observe(ev, runSpec, telemetry.outputMarkers);
|
|
1456
1470
|
if (mismatch) {
|
|
1457
1471
|
harnessErrored = true;
|
|
1458
1472
|
errors.push(mismatch);
|
|
1459
1473
|
break;
|
|
1460
1474
|
}
|
|
1461
1475
|
if (inPlaceEnvelope && continuity.mayPublishSession)
|
|
1462
|
-
observeNativeSessionEvent(runInput, adapter.id,
|
|
1476
|
+
observeNativeSessionEvent(runInput, adapter.id, ev);
|
|
1463
1477
|
// QA-024: the injected delegation belt's MCP server reported a
|
|
1464
1478
|
// terminal startup failure. Disclose it ONCE while live; recoverable
|
|
1465
1479
|
// exact tool-result failures are evaluated at attempt finalization.
|
|
@@ -1476,10 +1490,10 @@ export class Orchestrator {
|
|
|
1476
1490
|
reason: "mcp_server_failed_to_start",
|
|
1477
1491
|
});
|
|
1478
1492
|
}
|
|
1479
|
-
emitPlanProgress((t, p) => log?.emit(t, p), adapter.id, attemptId,
|
|
1480
|
-
if (
|
|
1493
|
+
emitPlanProgress((t, p) => log?.emit(t, p), adapter.id, attemptId, ev);
|
|
1494
|
+
if (ev.type === "usage") {
|
|
1481
1495
|
const usage = processAttemptUsage({
|
|
1482
|
-
event:
|
|
1496
|
+
event: ev,
|
|
1483
1497
|
telemetry,
|
|
1484
1498
|
harnessId: adapter.id,
|
|
1485
1499
|
attemptId,
|
|
@@ -1497,19 +1511,19 @@ export class Orchestrator {
|
|
|
1497
1511
|
break;
|
|
1498
1512
|
}
|
|
1499
1513
|
}
|
|
1500
|
-
if (
|
|
1514
|
+
if (ev.type === "error") {
|
|
1501
1515
|
harnessErrored = true;
|
|
1502
|
-
errors.push(redactSecrets(
|
|
1516
|
+
errors.push(redactSecrets(ev.error ?? ev.text ?? "harness emitted error"));
|
|
1503
1517
|
}
|
|
1504
1518
|
// Capture assistant prose so an answer-only turn (no file changes) still
|
|
1505
1519
|
// has an honest output artifact; a TYPED final message wins verbatim.
|
|
1506
|
-
answer.
|
|
1520
|
+
answer.observe(ev);
|
|
1507
1521
|
// Observe ALL budget/quota signals (one codex usage event carries
|
|
1508
1522
|
// BOTH spend and quota); pressure disclosed once per attempt.
|
|
1509
|
-
observeBudgetSignals(ledger, log, adapter.id, attemptId,
|
|
1510
|
-
this.deps.quotaEventSink?.(adapter.id,
|
|
1523
|
+
observeBudgetSignals(ledger, log, adapter.id, attemptId, ev, budgetSignalState);
|
|
1524
|
+
this.deps.quotaEventSink?.(adapter.id, ev);
|
|
1511
1525
|
}
|
|
1512
|
-
if (
|
|
1526
|
+
if (sizeRawContext && !harnessErrored)
|
|
1513
1527
|
await consumeRawPatchEnvelope({
|
|
1514
1528
|
repoRoot: envelope.repo_root,
|
|
1515
1529
|
worktreePath: envelope.worktree_path,
|
|
@@ -1525,8 +1539,7 @@ export class Orchestrator {
|
|
|
1525
1539
|
// gate and required-actions read a typed category, not a bare boolean.
|
|
1526
1540
|
harnessErrored = true;
|
|
1527
1541
|
errors.push(safeErrorMessage(err));
|
|
1528
|
-
if (err
|
|
1529
|
-
err instanceof RoutingPreflightError) {
|
|
1542
|
+
if (isDispatchRefusal(err)) {
|
|
1530
1543
|
processingRefusal = err;
|
|
1531
1544
|
break;
|
|
1532
1545
|
}
|
|
@@ -1557,11 +1570,11 @@ export class Orchestrator {
|
|
|
1557
1570
|
await captureDirectory();
|
|
1558
1571
|
currentCapture = directory
|
|
1559
1572
|
? undefined
|
|
1560
|
-
: await secretDiff.captureCandidateDiff(wsm, envelope);
|
|
1573
|
+
: await secretDiff.captureCandidateDiff(wsm, envelope, paths.root);
|
|
1561
1574
|
const currentDiff = secretDiff.capturedDiff(currentCapture);
|
|
1562
1575
|
const workspaceUnchanged = directory
|
|
1563
1576
|
? !isMutatingAccess(access) || directoryCapture.files?.noChanges === true
|
|
1564
|
-
:
|
|
1577
|
+
: secretDiff.captureUnchanged(currentCapture);
|
|
1565
1578
|
const deliverableEmpty = workspaceUnchanged && answer.text().length === 0;
|
|
1566
1579
|
const delivered = !harnessErrored && !deliverableEmpty && !signal?.aborted;
|
|
1567
1580
|
this.credentials.noteTryServed(refusal, telemetry.outputMarkers, delivered);
|
|
@@ -1617,21 +1630,20 @@ export class Orchestrator {
|
|
|
1617
1630
|
// D-16: answer.md persists only the deliverable. A3: an errored attempt
|
|
1618
1631
|
// with no typed final contributes no answer material (acceptedTryOutput).
|
|
1619
1632
|
const unwrapped = unwrapWorkReportEnvelope(acceptedTryOutput(answer, harnessErrored), workReportMode, { sideToolReport: telemetry.sideToolWorkReport ?? undefined });
|
|
1620
|
-
const
|
|
1621
|
-
// INV-062: the (already redacted) answer is kept whatever the patch holds.
|
|
1622
|
-
const answerText = redacted.trim().length > 0 ? redacted : undefined;
|
|
1633
|
+
const answerText = unwrapped.deliverable.trim().length > 0 ? unwrapped.deliverable : undefined;
|
|
1623
1634
|
if (directory)
|
|
1624
1635
|
await captureDirectory();
|
|
1625
1636
|
const captured = directory
|
|
1626
1637
|
? { diff: "", captureRefusal: directoryCapture.refusal }
|
|
1627
1638
|
: await secretDiff.captureCandidateWorkspace({
|
|
1628
|
-
...{ wsm, envelope, inPlace: inPlaceEnvelope,
|
|
1629
|
-
...{ answerText, answerMatches: answer.secretLikeMatches() },
|
|
1639
|
+
...{ wsm, envelope, inPlace: inPlaceEnvelope, contract },
|
|
1630
1640
|
holder: { runId: paths.runId, runDir: paths.root },
|
|
1631
1641
|
incompleteCapture: currentCapture?.captureIncomplete ? currentCapture : undefined,
|
|
1632
1642
|
});
|
|
1633
|
-
const {
|
|
1634
|
-
const deliverableEvidence =
|
|
1643
|
+
const { captureRefusal } = captured;
|
|
1644
|
+
const deliverableEvidence = secretDiff.hasCandidateChanges(captured) ||
|
|
1645
|
+
Boolean(answerText) ||
|
|
1646
|
+
directoryHasOutput(directoryCapture.files);
|
|
1635
1647
|
// Cancelled attempts skip gates: running a 600s-per-gate suite delays the ack
|
|
1636
1648
|
// and burns compute on a result nobody will adopt. Diff/attempt.yaml
|
|
1637
1649
|
// still land, so partial work stays inspectable.
|
|
@@ -1694,7 +1706,7 @@ export class Orchestrator {
|
|
|
1694
1706
|
const errored = harnessErrored || webBlocked || captureRefusal !== undefined;
|
|
1695
1707
|
const resumable = await continuity.finish({
|
|
1696
1708
|
runSpec: spec,
|
|
1697
|
-
errored,
|
|
1709
|
+
errored: harnessErrored || webBlocked, // a capture refusal is packaging, not work
|
|
1698
1710
|
nonSuccess: !gatesPassed(gates),
|
|
1699
1711
|
aborted: signal?.aborted === true,
|
|
1700
1712
|
cancelReason: typeof signal?.reason === "string" ? signal.reason : null,
|
|
@@ -1716,14 +1728,14 @@ export class Orchestrator {
|
|
|
1716
1728
|
aborted: signal?.aborted === true,
|
|
1717
1729
|
authMode: telemetry.authMode,
|
|
1718
1730
|
});
|
|
1719
|
-
const producedFiles = AC.withAttemptFailureCost(() => writeCandidateAttemptArtifacts({
|
|
1731
|
+
const { produced: producedFiles, omitted: producedOmitted } = AC.withAttemptFailureCost(() => writeCandidateAttemptArtifacts({
|
|
1720
1732
|
store,
|
|
1721
1733
|
attemptDir,
|
|
1722
1734
|
worktreePath: envelope.worktree_path,
|
|
1723
1735
|
artifactRelativeDir,
|
|
1724
|
-
...
|
|
1736
|
+
...secretDiff.attemptPatchInput(captured),
|
|
1725
1737
|
persistPatch: !directory && captureRefusal === undefined && isMutatingAccess(access),
|
|
1726
|
-
persistProducedMedia: !directory
|
|
1738
|
+
persistProducedMedia: !directory,
|
|
1727
1739
|
answerText,
|
|
1728
1740
|
record: {
|
|
1729
1741
|
attempt_id: attemptId,
|
|
@@ -1773,6 +1785,7 @@ export class Orchestrator {
|
|
|
1773
1785
|
reviewCwd: envelope.worktree_path,
|
|
1774
1786
|
baseSha: envelope.base_sha ?? undefined,
|
|
1775
1787
|
producedFiles,
|
|
1788
|
+
...(producedOmitted.length ? { producedOmitted } : {}),
|
|
1776
1789
|
gates,
|
|
1777
1790
|
cost,
|
|
1778
1791
|
errored,
|
|
@@ -1787,7 +1800,7 @@ export class Orchestrator {
|
|
|
1787
1800
|
: {}),
|
|
1788
1801
|
outcomeClass: finalized.outcomeClass,
|
|
1789
1802
|
applied,
|
|
1790
|
-
...continuity.terminalFacts(resumable),
|
|
1803
|
+
...continuity.terminalFacts(resumable, captureRefusal !== undefined),
|
|
1791
1804
|
};
|
|
1792
1805
|
}
|
|
1793
1806
|
continuityDeps(adapter, routed, contract, attemptId, paths, runInput, log, lane) {
|
|
@@ -1803,9 +1816,11 @@ export class Orchestrator {
|
|
|
1803
1816
|
workspace: continuityWorkspace(lane.envelope, cwd),
|
|
1804
1817
|
workOrder: lane.workOrder,
|
|
1805
1818
|
firstPrompt: lane.firstPrompt,
|
|
1819
|
+
later: lane.later,
|
|
1820
|
+
explicitModel: lane.explicitModel,
|
|
1806
1821
|
registry: this.config(repoRoot)?.global.credential_profiles ?? [],
|
|
1807
1822
|
policy: this.credentials.profilePolicy(repoRoot, adapter.id),
|
|
1808
|
-
|
|
1823
|
+
quotaSnapshots: () => this.deps.quotaSnapshots?.() ?? [],
|
|
1809
1824
|
retryPolicy: transientRetryPolicy(this.config(repoRoot)),
|
|
1810
1825
|
pinned: runInput?.credentialProfileId != null, // D-U6: a pin never rotates
|
|
1811
1826
|
defaultRouteWasVendorNative: routed.authRouteEstimate === "local_session",
|
|
@@ -1860,7 +1875,9 @@ export class Orchestrator {
|
|
|
1860
1875
|
// repoRoot. Both the WorkspaceManager and the git boundary resolve against
|
|
1861
1876
|
// this SINGLE root.
|
|
1862
1877
|
const execRoot = this.execRootOf(input);
|
|
1863
|
-
const
|
|
1878
|
+
const projectRoot = input.repoRoot;
|
|
1879
|
+
const wsm = new WorkspaceManager(execRoot, { projectRoot });
|
|
1880
|
+
const anchors = secretDiff.revertAnchors(execRoot, projectRoot);
|
|
1864
1881
|
store.writeYaml(join(paths.contextDir, "task.yaml"), contract);
|
|
1865
1882
|
log.emit("task.contract.created", { task_contract_hash: hashJson(contract) });
|
|
1866
1883
|
// Write modes need a git boundary for worktree isolation and honest diffs.
|
|
@@ -1908,7 +1925,7 @@ export class Orchestrator {
|
|
|
1908
1925
|
const reviewDir = join(paths.root, "review-evidence");
|
|
1909
1926
|
if (contract.review_requested !== false)
|
|
1910
1927
|
writeEvidencePacket(reviewDir, {
|
|
1911
|
-
userIntent:
|
|
1928
|
+
userIntent: input.prompt,
|
|
1912
1929
|
diff: "(per-candidate diffs are supplied to reviewers individually)\n",
|
|
1913
1930
|
tests: renderTestsEvidence(contract),
|
|
1914
1931
|
});
|
|
@@ -2125,7 +2142,7 @@ export class Orchestrator {
|
|
|
2125
2142
|
harness: adapter.id,
|
|
2126
2143
|
profileId: input.credentialProfileId ?? null,
|
|
2127
2144
|
priorPrompt: input.prompt,
|
|
2128
|
-
priorOutput: run.answerText ?? run.
|
|
2145
|
+
priorOutput: run.answerText ?? run.diff ?? "",
|
|
2129
2146
|
}));
|
|
2130
2147
|
// Reserve the continuation lease BEFORE any disclosure: a denied lease
|
|
2131
2148
|
// must never emit run.continuation (which claims a continuation
|
|
@@ -2287,6 +2304,7 @@ export class Orchestrator {
|
|
|
2287
2304
|
requestedSingleCandidate &&
|
|
2288
2305
|
runs[0]?.reviewCwd === execRoot,
|
|
2289
2306
|
execRoot,
|
|
2307
|
+
projectRoot: input.repoRoot,
|
|
2290
2308
|
preTurnSha,
|
|
2291
2309
|
postTurnSha: earlyPostTurnSha,
|
|
2292
2310
|
}
|
|
@@ -2321,6 +2339,7 @@ export class Orchestrator {
|
|
|
2321
2339
|
await disposeReviewEnvelopes();
|
|
2322
2340
|
const failedOutput = {
|
|
2323
2341
|
...{ store, log, paths, execRoot, preTurnSha, taskId, mode, run: failedDelegation },
|
|
2342
|
+
projectRoot: input.repoRoot,
|
|
2324
2343
|
live: input.inPlace === true &&
|
|
2325
2344
|
input.workspaceKind !== "directory" &&
|
|
2326
2345
|
failedDelegation.reviewCwd === execRoot,
|
|
@@ -2376,7 +2395,7 @@ export class Orchestrator {
|
|
|
2376
2395
|
// real verification). Compute the reviewable set first and emit a typed
|
|
2377
2396
|
// `review.skipped` when nothing is reviewable, so every start has a matching
|
|
2378
2397
|
// terminal and the no-diff path records `not_run` consistently.
|
|
2379
|
-
const reviewableRuns = workingRuns.filter((r) =>
|
|
2398
|
+
const reviewableRuns = workingRuns.filter((r) => secretDiff.hasCandidateChanges(r));
|
|
2380
2399
|
const configuredFamilies = new Set(reviewers.map((r) => r.providerFamily)).size;
|
|
2381
2400
|
if (reviewableRuns.length === 0 || reviewers.length === 0) {
|
|
2382
2401
|
log.emit("review.skipped", {
|
|
@@ -2407,18 +2426,15 @@ export class Orchestrator {
|
|
|
2407
2426
|
evidences = await this.reviewRuns(workingRuns, reviewers, reviewVerified, reviewDir, input.repoRoot, contract, store, paths, log, ledger, taskId, input.signal, this.reservationEstimateUsd(input), false, input.runScopeMarker);
|
|
2408
2427
|
}
|
|
2409
2428
|
catch (err) {
|
|
2410
|
-
// Review
|
|
2411
|
-
// never as an escaped throw that orphans the run dir.
|
|
2412
|
-
return failAfterCandidates(raceTerminal, "review", err);
|
|
2413
|
-
}
|
|
2414
|
-
finally {
|
|
2415
|
-
// Review preflight failures must not leak candidate worktrees.
|
|
2429
|
+
// Review failures end TERMINALLY with artifacts and leak no candidate worktree.
|
|
2416
2430
|
await disposeReviewEnvelopes();
|
|
2431
|
+
return failAfterCandidates(raceTerminal, "review", err);
|
|
2417
2432
|
}
|
|
2418
2433
|
if (input.signal?.aborted) {
|
|
2434
|
+
await disposeReviewEnvelopes();
|
|
2419
2435
|
return cancelledRaceResult();
|
|
2420
2436
|
}
|
|
2421
|
-
// Synthesis
|
|
2437
|
+
// Synthesis, if worthwhile: a NEW re-checked candidate; workspaces stay until it ends (#535).
|
|
2422
2438
|
const synth = mutatingRun
|
|
2423
2439
|
? decideSynthesis(evidences, input.synthesis ?? "auto")
|
|
2424
2440
|
: {
|
|
@@ -2426,86 +2442,95 @@ export class Orchestrator {
|
|
|
2426
2442
|
reason: "readonly access has no write-backed synthesis lifecycle",
|
|
2427
2443
|
sources: evidences.map((evidence) => evidence.attemptId),
|
|
2428
2444
|
};
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
const synthesisInput = buildFileBackedSynthesisInput({
|
|
2446
|
-
instructions: plan.instructions,
|
|
2447
|
-
findings: plan.fixFindings,
|
|
2448
|
-
candidates: workingRuns,
|
|
2449
|
-
});
|
|
2450
|
-
const synthAdapter = synthRouted.adapter;
|
|
2451
|
-
// Disclose against the PER-ROUTE policy (per-harness web defaults
|
|
2452
|
-
// included), exactly like the candidate slots do.
|
|
2453
|
-
const synthKnobs = this.routeSpecKnobs(synthRouted, contract, undefined, input.effort);
|
|
2454
|
-
const effectiveWeb = this.discloseWebUpgrade(log, synthRouted, synthKnobs.webPolicy, "synth");
|
|
2455
|
-
envelope = await wsm.create({
|
|
2456
|
-
taskId,
|
|
2457
|
-
attemptId: "synth",
|
|
2458
|
-
baseRef: contract.repo.base_ref,
|
|
2459
|
-
dirtyPolicy: "snapshot",
|
|
2460
|
-
accessProfile: candidateAccess,
|
|
2461
|
-
workspaceKind: input.workspaceKind,
|
|
2462
|
-
scopePaths: input.scopePaths,
|
|
2463
|
-
});
|
|
2464
|
-
const synthHome = this.harnessHomeFor(wsm, envelope, synthRouted, input);
|
|
2465
|
-
const run = await this.runCandidateInEnvelope(synthRouted, envelope, "synth", "Synthesis", contract, synthesisInput.prompt, store, paths, wsm, ledger, {
|
|
2466
|
-
lease: lease.lease,
|
|
2467
|
-
onDenied: (denial) => {
|
|
2468
|
-
budgetStopped = true;
|
|
2469
|
-
budgetDenial ??= denial;
|
|
2470
|
-
},
|
|
2471
|
-
}, candidateAccess, input.onHarnessEvent, input.signal, undefined, input.effort, "synthesize", log, effectiveWeb, interactionChannelFor(input, log, runId, taskId, "synth", synthAdapter.id, synthRouted.supportsInteractive), undefined, input, false, synthesisInput.content, undefined, synthHome, undefined, synthTried);
|
|
2472
|
-
ledger.settle(lease.lease?.lease_id ?? "", attemptUsageCostSettlement(run.cost, run.costEstimated, run.attemptId, run.harnessId, run.telemetry.authMode, run.telemetry.usageCost));
|
|
2473
|
-
reviewEnvelopes.push(envelope);
|
|
2474
|
-
envelope = undefined;
|
|
2475
|
-
// D-16 r8: only a WORKING synth is reviewed/adopted (same veto owner
|
|
2476
|
-
// as the race lane); `runs` still records it for telemetry.
|
|
2477
|
-
runs.push(run);
|
|
2445
|
+
try {
|
|
2446
|
+
store.writeYaml(join(paths.arbitrationDir, "synthesis.yaml"), synth);
|
|
2447
|
+
log.emit("synthesis.started", { synthesize: synth.synthesize, reason: synth.reason });
|
|
2448
|
+
if (synth.synthesize && !budgetStopped) {
|
|
2449
|
+
const synthRouted = adapters[0];
|
|
2450
|
+
const lease = ledger.reserve({
|
|
2451
|
+
taskId,
|
|
2452
|
+
attemptId: "synth",
|
|
2453
|
+
intent: "synthesize",
|
|
2454
|
+
harnessId: synthRouted.adapter.id,
|
|
2455
|
+
cost: processingAttemptCostEvidence(synthRouted.adapter.id, "synth", this.reservationEstimateUsd(input), this.routeBillingKnowledge(input, synthRouted.adapter.id, synthRouted), synthRouted.processing),
|
|
2456
|
+
});
|
|
2457
|
+
if (lease.granted) {
|
|
2458
|
+
let envelope;
|
|
2459
|
+
const synthTried = new PoolAttemptEvidence(synthRouted.adapter.id, "synth");
|
|
2460
|
+
let disposeSaved;
|
|
2478
2461
|
try {
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2462
|
+
const plan = buildSynthesisPlan(evidences);
|
|
2463
|
+
const saved = await savedForSynthesis(workingRuns, paths.root, contract.repo.root);
|
|
2464
|
+
disposeSaved = saved.dispose;
|
|
2465
|
+
const synthesisInput = buildFileBackedSynthesisInput({
|
|
2466
|
+
instructions: plan.instructions,
|
|
2467
|
+
findings: plan.fixFindings,
|
|
2468
|
+
candidates: saved.candidates,
|
|
2469
|
+
});
|
|
2470
|
+
const synthAdapter = synthRouted.adapter;
|
|
2471
|
+
// Disclose against the PER-ROUTE policy (per-harness web defaults
|
|
2472
|
+
// included), exactly like the candidate slots do.
|
|
2473
|
+
const synthKnobs = this.routeSpecKnobs(synthRouted, contract, undefined, input.effort);
|
|
2474
|
+
const effectiveWeb = this.discloseWebUpgrade(log, synthRouted, synthKnobs.webPolicy, "synth");
|
|
2475
|
+
envelope = await wsm.create({
|
|
2476
|
+
taskId,
|
|
2477
|
+
attemptId: "synth",
|
|
2478
|
+
baseRef: contract.repo.base_ref,
|
|
2479
|
+
dirtyPolicy: "snapshot",
|
|
2480
|
+
accessProfile: candidateAccess,
|
|
2481
|
+
workspaceKind: input.workspaceKind,
|
|
2482
|
+
scopePaths: input.scopePaths,
|
|
2483
|
+
});
|
|
2484
|
+
const synthHome = this.harnessHomeFor(wsm, envelope, synthRouted, input);
|
|
2485
|
+
const run = await this.runCandidateInEnvelope(synthRouted, envelope, "synth", "Synthesis", contract, synthesisInput.prompt, store, paths, wsm, ledger, {
|
|
2486
|
+
lease: lease.lease,
|
|
2487
|
+
onDenied: (denial) => {
|
|
2488
|
+
budgetStopped = true;
|
|
2489
|
+
budgetDenial ??= denial;
|
|
2490
|
+
},
|
|
2491
|
+
}, candidateAccess, input.onHarnessEvent, input.signal, undefined, input.effort, "synthesize", log, effectiveWeb, interactionChannelFor(input, log, runId, taskId, "synth", synthAdapter.id, synthRouted.supportsInteractive), undefined, input, false, synthesisInput.content, undefined, synthHome, undefined, synthTried);
|
|
2492
|
+
ledger.settle(lease.lease?.lease_id ?? "", attemptUsageCostSettlement(run.cost, run.costEstimated, run.attemptId, run.harnessId, run.telemetry.authMode, run.telemetry.usageCost));
|
|
2493
|
+
reviewEnvelopes.push(envelope);
|
|
2494
|
+
envelope = undefined;
|
|
2495
|
+
// D-16 r8: only a WORKING synth is reviewed/adopted (same veto owner
|
|
2496
|
+
// as the race lane); `runs` still records it for telemetry.
|
|
2497
|
+
runs.push(run);
|
|
2498
|
+
try {
|
|
2499
|
+
if (isWorkingCandidate(run)) {
|
|
2500
|
+
const synthEvidence = await this.reviewRuns([run], reviewers, reviewVerified, reviewDir, input.repoRoot, contract, store, paths, log, ledger, taskId, input.signal, this.reservationEstimateUsd(input), false, input.runScopeMarker);
|
|
2501
|
+
evidences.push(...synthEvidence);
|
|
2502
|
+
workingRuns.push(run);
|
|
2503
|
+
}
|
|
2504
|
+
if (input.signal?.aborted) {
|
|
2505
|
+
return cancelledResult(log, runId, taskId, mode, paths.root, candidateStatuses(runs), raceTerminal.writeTelemetry, ledger.spend(), input.signal, store, undefined, undefined, history());
|
|
2506
|
+
}
|
|
2483
2507
|
}
|
|
2484
|
-
|
|
2485
|
-
|
|
2508
|
+
finally {
|
|
2509
|
+
await disposeReviewEnvelopes();
|
|
2486
2510
|
}
|
|
2487
2511
|
}
|
|
2512
|
+
catch (err) {
|
|
2513
|
+
// Not yet in `runs`: the run goes on, so its tries' accounts stay in the history.
|
|
2514
|
+
if (!runs.some((run) => run.attemptId === "synth"))
|
|
2515
|
+
thrownSynthesis = { poolAttempts: synthTried.snapshot() };
|
|
2516
|
+
ledger.settle(lease.lease?.lease_id ?? "", AC.attemptFailureCost(err, "synthesis-error").settlement);
|
|
2517
|
+
log.emit("harness.completed", {
|
|
2518
|
+
attempt_id: "synth",
|
|
2519
|
+
status: "failed",
|
|
2520
|
+
error: safeErrorMessage(err),
|
|
2521
|
+
});
|
|
2522
|
+
}
|
|
2488
2523
|
finally {
|
|
2489
|
-
await
|
|
2524
|
+
await disposeSaved?.();
|
|
2525
|
+
if (envelope)
|
|
2526
|
+
await wsm.dispose(envelope);
|
|
2490
2527
|
}
|
|
2491
2528
|
}
|
|
2492
|
-
catch (err) {
|
|
2493
|
-
// Not yet in `runs`: the run goes on, so its tries' accounts stay in the history.
|
|
2494
|
-
if (!runs.some((run) => run.attemptId === "synth"))
|
|
2495
|
-
thrownSynthesis = { poolAttempts: synthTried.snapshot() };
|
|
2496
|
-
ledger.settle(lease.lease?.lease_id ?? "", AC.attemptFailureCost(err, "synthesis-error").settlement);
|
|
2497
|
-
log.emit("harness.completed", {
|
|
2498
|
-
attempt_id: "synth",
|
|
2499
|
-
status: "failed",
|
|
2500
|
-
error: safeErrorMessage(err),
|
|
2501
|
-
});
|
|
2502
|
-
}
|
|
2503
|
-
finally {
|
|
2504
|
-
if (envelope)
|
|
2505
|
-
await wsm.dispose(envelope);
|
|
2506
|
-
}
|
|
2507
2529
|
}
|
|
2508
2530
|
}
|
|
2531
|
+
finally {
|
|
2532
|
+
await disposeReviewEnvelopes();
|
|
2533
|
+
}
|
|
2509
2534
|
if (input.signal?.aborted) {
|
|
2510
2535
|
return cancelledRaceResult();
|
|
2511
2536
|
}
|
|
@@ -2578,7 +2603,7 @@ export class Orchestrator {
|
|
|
2578
2603
|
winnerRun &&
|
|
2579
2604
|
!inPlaceWinner &&
|
|
2580
2605
|
!deferredRaceVerify &&
|
|
2581
|
-
(winnerRun
|
|
2606
|
+
secretDiff.hasCandidateChanges(winnerRun) &&
|
|
2582
2607
|
facts.lifecycle === "succeeded" &&
|
|
2583
2608
|
facts.review !== "blocked" &&
|
|
2584
2609
|
!input.signal?.aborted) {
|
|
@@ -2667,8 +2692,8 @@ export class Orchestrator {
|
|
|
2667
2692
|
else if (mutatingRun) {
|
|
2668
2693
|
const savedPatch = secretDiff.persistFinalPatch(store, paths.finalDir, winnerRun);
|
|
2669
2694
|
const patchSha256 = savedPatch.patchSha256;
|
|
2670
|
-
const wstats =
|
|
2671
|
-
const hasDiff =
|
|
2695
|
+
const wstats = secretDiff.resultDiffStats(winnerRun);
|
|
2696
|
+
const hasDiff = secretDiff.hasCandidateChanges(winnerRun);
|
|
2672
2697
|
const blockers = winnerEvidence
|
|
2673
2698
|
? winnerEvidence.findings.filter((f) => isBlocking(f)).length
|
|
2674
2699
|
: 0;
|
|
@@ -2693,7 +2718,7 @@ export class Orchestrator {
|
|
|
2693
2718
|
}
|
|
2694
2719
|
else if (adoptable) {
|
|
2695
2720
|
// Protected apply preserves the live tree or reports tree_mutated.
|
|
2696
|
-
const applied = await verifyAndDeliver(execRoot, winnerRun
|
|
2721
|
+
const applied = await verifyAndDeliver(execRoot, secretDiff.deliverableOf(winnerRun), { mode: "apply", protectedApply: true, projectRoot, owner: paths.root }, gateSpecsFromContract(contract, input.runScopeMarker), (freshVerify) => {
|
|
2697
2722
|
finalVerify = freshVerify;
|
|
2698
2723
|
return finalVerifyBlocks(freshVerify)
|
|
2699
2724
|
? (freshVerify.reason ?? "final verify failed before race adoption")
|
|
@@ -2710,17 +2735,15 @@ export class Orchestrator {
|
|
|
2710
2735
|
patch_sha256: patchSha256,
|
|
2711
2736
|
winner: winnerRun.attemptId,
|
|
2712
2737
|
});
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
}
|
|
2716
|
-
catch {
|
|
2717
|
-
postTurnSha = null;
|
|
2718
|
-
}
|
|
2719
|
-
revertAnchorId = createRevertAnchorFromPatchOrNull(execRoot, winnerRun.diff);
|
|
2738
|
+
postTurnSha = await snapshotTree(execRoot).catch(() => null);
|
|
2739
|
+
revertAnchorId = await anchors.after(winnerRun, applied);
|
|
2720
2740
|
}
|
|
2721
2741
|
else {
|
|
2722
|
-
|
|
2723
|
-
|
|
2742
|
+
// A write that could not be taken back stays live and revertable (INV-114).
|
|
2743
|
+
adopted = applied.treeMutated === true;
|
|
2744
|
+
applyState = adopted ? "applied_review_blocked" : "not_applied";
|
|
2745
|
+
if (adopted)
|
|
2746
|
+
revertAnchorId = await anchors.after(winnerRun, applied);
|
|
2724
2747
|
deliveryFailureReason = applied.detail ?? "race adoption delivery was refused";
|
|
2725
2748
|
facts = { ...facts, checks: "failed", reason: "checks_failed" };
|
|
2726
2749
|
if (finalVerifyBlocks(finalVerify))
|
|
@@ -2743,7 +2766,7 @@ export class Orchestrator {
|
|
|
2743
2766
|
deliveryReceiptPath: raceDeliveryReceipt || directoryDeliveryReceipt ? "final/delivery_receipt.yaml" : null,
|
|
2744
2767
|
});
|
|
2745
2768
|
if (inPlaceWinner && requestedSingleCandidate && adopted === true) {
|
|
2746
|
-
revertAnchorId = await
|
|
2769
|
+
revertAnchorId = await anchors.turn(preTurnSha, postTurnSha);
|
|
2747
2770
|
}
|
|
2748
2771
|
store.writeYaml(join(paths.finalDir, "work_product.yaml"), {
|
|
2749
2772
|
id: newId("wp"),
|
|
@@ -2779,7 +2802,7 @@ export class Orchestrator {
|
|
|
2779
2802
|
},
|
|
2780
2803
|
});
|
|
2781
2804
|
}
|
|
2782
|
-
store.writeText(join(paths.finalDir, "summary.md"), withPoolAttempts(renderSummary(runId, mode, { ...result.decision, facts }, evidences, synth.reason, actualReviewVerified, secretDiff.
|
|
2805
|
+
store.writeText(join(paths.finalDir, "summary.md"), withPoolAttempts(renderSummary(runId, mode, { ...result.decision, facts }, evidences, synth.reason, actualReviewVerified, secretDiff.resultSummaryLines(winnerRun)), racePool()));
|
|
2783
2806
|
// summary.md is a DIAGNOSTIC artifact only (V8/PLAN addendum 2): it no
|
|
2784
2807
|
// longer carries primary-output authority. A clean applyable success or a
|
|
2785
2808
|
// winner answer still marks it ready for legacy INV-116 ordering; any
|
|
@@ -3033,7 +3056,8 @@ export class Orchestrator {
|
|
|
3033
3056
|
// local `execRoot`; this path previously ensured the boundary on repoRoot,
|
|
3034
3057
|
// which for an isolated thread is the project, not the mutated worktree.
|
|
3035
3058
|
const execRoot = this.execRootOf(input);
|
|
3036
|
-
const wsm = new WorkspaceManager(execRoot);
|
|
3059
|
+
const wsm = new WorkspaceManager(execRoot, { projectRoot: input.repoRoot });
|
|
3060
|
+
const anchors = secretDiff.revertAnchors(execRoot, input.repoRoot);
|
|
3037
3061
|
const readiness = new ReadinessLedger();
|
|
3038
3062
|
store.writeYaml(join(paths.contextDir, "task.yaml"), contract);
|
|
3039
3063
|
// Legacy Git envelopes need their boundary; explicit directory execution
|
|
@@ -3063,7 +3087,7 @@ export class Orchestrator {
|
|
|
3063
3087
|
const reviewDir = join(paths.root, "review-evidence");
|
|
3064
3088
|
if (contract.review_requested !== false)
|
|
3065
3089
|
writeEvidencePacket(reviewDir, {
|
|
3066
|
-
userIntent:
|
|
3090
|
+
userIntent: input.prompt,
|
|
3067
3091
|
diff: "(per-attempt)\n",
|
|
3068
3092
|
tests: renderTestsEvidence(contract),
|
|
3069
3093
|
});
|
|
@@ -3257,7 +3281,7 @@ export class Orchestrator {
|
|
|
3257
3281
|
exhausted = true;
|
|
3258
3282
|
processingBudgetDenial ??= denial;
|
|
3259
3283
|
},
|
|
3260
|
-
}, convergenceAccess, input.onHarnessEvent, input.signal, undefined, input.effort,
|
|
3284
|
+
}, convergenceAccess, input.onHarnessEvent, input.signal, undefined, input.effort, this.requestRequirements.convergenceIntent(routed, this.candidateIntent(input)), log, effectiveWeb, interactionChannelFor(input, log, runId, taskId, attemptId, adapter.id, routed.supportsInteractive), (streamedUsd) => {
|
|
3261
3285
|
ledger.updateHold(lease.lease?.lease_id ?? "", streamedUsd);
|
|
3262
3286
|
return ledger.tier() === "hard";
|
|
3263
3287
|
}, input, true, // convergence runs one candidate: live deltas on (W-C4)
|
|
@@ -3350,6 +3374,7 @@ export class Orchestrator {
|
|
|
3350
3374
|
const failure = delegateFailure.candidateFailureTerminal(run, "convergence");
|
|
3351
3375
|
await delegateFailure.persistFailedCandidateOutput({
|
|
3352
3376
|
...{ store, log, paths, execRoot, preTurnSha, taskId, mode, run },
|
|
3377
|
+
projectRoot: input.repoRoot,
|
|
3353
3378
|
live: input.inPlace === true && input.workspaceKind !== "directory",
|
|
3354
3379
|
kind: input.create === true ? "new_repo" : "patch",
|
|
3355
3380
|
attempts: attempt,
|
|
@@ -3398,7 +3423,8 @@ export class Orchestrator {
|
|
|
3398
3423
|
actualReviewVerified = evidence.reviewVerified ?? false;
|
|
3399
3424
|
lastDiffStable = run.files
|
|
3400
3425
|
? await directoryCandidateStable(wsm, envelope, run.files)
|
|
3401
|
-
:
|
|
3426
|
+
: secretDiff.captureIdentity(await wsm.captureDiff(envelope, paths.root)) ===
|
|
3427
|
+
secretDiff.captureIdentity(run);
|
|
3402
3428
|
const evaluated = evaluateConvergence({
|
|
3403
3429
|
predicate: contract.convergence,
|
|
3404
3430
|
gates: evidence.gates,
|
|
@@ -3448,7 +3474,7 @@ export class Orchestrator {
|
|
|
3448
3474
|
? run.files.manifestSha256
|
|
3449
3475
|
: input.workspaceKind === "directory"
|
|
3450
3476
|
? null
|
|
3451
|
-
:
|
|
3477
|
+
: secretDiff.captureIdentity(run);
|
|
3452
3478
|
if (requiredGateFailing && diffHash !== null && diffHash === lastFailingGateDiffHash) {
|
|
3453
3479
|
sameFailingGateDiffs += 1;
|
|
3454
3480
|
}
|
|
@@ -3461,7 +3487,7 @@ export class Orchestrator {
|
|
|
3461
3487
|
const failedGateIds = run.gates
|
|
3462
3488
|
.filter((g) => g.required && g.status !== "passed")
|
|
3463
3489
|
.map((g) => g.id);
|
|
3464
|
-
stuckNoProgressReason = `same candidate
|
|
3490
|
+
stuckNoProgressReason = `same candidate result (${diffHash}) produced ${sameFailingGateDiffs} consecutive failing required gate round(s): ${failedGateIds.join(", ") || "unknown gate"}`;
|
|
3465
3491
|
log.emit("finding.revalidated", {
|
|
3466
3492
|
attempt_id: attemptId,
|
|
3467
3493
|
stuck_no_progress: true,
|
|
@@ -3599,7 +3625,7 @@ export class Orchestrator {
|
|
|
3599
3625
|
let convFinalVerify = null;
|
|
3600
3626
|
if (input.inPlace !== true &&
|
|
3601
3627
|
lastRun &&
|
|
3602
|
-
(lastRun
|
|
3628
|
+
secretDiff.hasCandidateChanges(lastRun) &&
|
|
3603
3629
|
facts.lifecycle === "succeeded" &&
|
|
3604
3630
|
facts.review !== "blocked" &&
|
|
3605
3631
|
!input.signal?.aborted) {
|
|
@@ -3635,6 +3661,7 @@ export class Orchestrator {
|
|
|
3635
3661
|
// An incomplete capture has no exact patch: never publish an empty one.
|
|
3636
3662
|
await delegateFailure.persistFailedCandidateOutput({
|
|
3637
3663
|
...{ store, log, paths, execRoot, preTurnSha, taskId, mode, facts, run: lastRun },
|
|
3664
|
+
projectRoot: input.repoRoot,
|
|
3638
3665
|
live: input.inPlace === true && input.workspaceKind !== "directory",
|
|
3639
3666
|
kind: input.create === true ? "new_repo" : "patch",
|
|
3640
3667
|
attempts: attempt,
|
|
@@ -3646,16 +3673,14 @@ export class Orchestrator {
|
|
|
3646
3673
|
// Honest apply-state (parity with runRace single-candidate in-place): a
|
|
3647
3674
|
// convergence run with inPlace mutated the live tree directly across its
|
|
3648
3675
|
// attempts, so it is "applied" even when review blocked (Revert offered).
|
|
3649
|
-
const convHasDiff =
|
|
3676
|
+
const convHasDiff = secretDiff.hasCandidateChanges(lastRun);
|
|
3650
3677
|
const convAdopted = input.inPlace === true && convHasDiff ? true : null;
|
|
3651
3678
|
const convApplyState = convAdopted === true
|
|
3652
3679
|
? convAdoptable
|
|
3653
3680
|
? "applied"
|
|
3654
3681
|
: "applied_review_blocked"
|
|
3655
3682
|
: "not_applied";
|
|
3656
|
-
const revertAnchorId = convAdopted === true
|
|
3657
|
-
? await createRevertAnchorOrNull(execRoot, preTurnSha, lastPostTurnSha)
|
|
3658
|
-
: null;
|
|
3683
|
+
const revertAnchorId = convAdopted === true ? await anchors.turn(preTurnSha, lastPostTurnSha) : null;
|
|
3659
3684
|
store.writeYaml(join(paths.finalDir, "work_product.yaml"), {
|
|
3660
3685
|
id: newId("wp"),
|
|
3661
3686
|
kind: "patch",
|
|
@@ -3679,12 +3704,12 @@ export class Orchestrator {
|
|
|
3679
3704
|
},
|
|
3680
3705
|
});
|
|
3681
3706
|
}
|
|
3682
|
-
//
|
|
3707
|
+
// The exact answer is kept for every convergence result, patch or files.
|
|
3683
3708
|
if (lastRun.answerText) {
|
|
3684
3709
|
store.writeText(join(paths.finalDir, "answer.md"), lastRun.answerText);
|
|
3685
3710
|
log.emit("output.ready", { kind: "answer", path: "final/answer.md" });
|
|
3686
3711
|
}
|
|
3687
|
-
store.writeText(join(paths.finalDir, "summary.md"), `${withPoolAttempts(`# Run ${runId} (${mode})\n\n- Lifecycle: ${facts.lifecycle}${facts.reason ? ` (${facts.reason})` : ""}\n- Attempts: ${attempt}\n- Winner: ${lastRun.attemptId}\n- Review verified (cross-family): ${actualReviewVerified}\n- Apply recommendation: ${decision?.apply_recommendation ?? "inspect"}${stuckNoProgressReason ? `\n- No-progress reason: ${stuckNoProgressReason}` : ""}${secretDiff.
|
|
3712
|
+
store.writeText(join(paths.finalDir, "summary.md"), `${withPoolAttempts(`# Run ${runId} (${mode})\n\n- Lifecycle: ${facts.lifecycle}${facts.reason ? ` (${facts.reason})` : ""}\n- Attempts: ${attempt}\n- Winner: ${lastRun.attemptId}\n- Review verified (cross-family): ${actualReviewVerified}\n- Apply recommendation: ${decision?.apply_recommendation ?? "inspect"}${stuckNoProgressReason ? `\n- No-progress reason: ${stuckNoProgressReason}` : ""}${secretDiff.resultSummaryDisclosure(lastRun)}`, convPool)}\n`);
|
|
3688
3713
|
// Lifecycle invariant (all modes): output.ready precedes the terminal
|
|
3689
3714
|
// event so a client that applied the terminal event has the output.
|
|
3690
3715
|
log.emit("output.ready", {
|
|
@@ -3716,6 +3741,8 @@ export class Orchestrator {
|
|
|
3716
3741
|
code: convBudgetMapping?.code ?? convDeclared?.code ?? null,
|
|
3717
3742
|
resetsAt: convDeclared?.resetsAt ?? null,
|
|
3718
3743
|
requestRefusal: convDeclared?.requestRefusal,
|
|
3744
|
+
quotaRefusal: convDeclared?.quotaRefusal,
|
|
3745
|
+
rawContextShortfall: convDeclared?.rawContextShortfall,
|
|
3719
3746
|
harnessId: lastRun?.harnessId,
|
|
3720
3747
|
attemptId: lastRun?.attemptId,
|
|
3721
3748
|
runDir: paths.root,
|
|
@@ -4081,7 +4108,8 @@ export class Orchestrator {
|
|
|
4081
4108
|
}
|
|
4082
4109
|
const text = outcome.text ?? "(no output)";
|
|
4083
4110
|
plans.push({ id: outcome.harnessId, text });
|
|
4084
|
-
|
|
4111
|
+
// The planner's own saved copy keeps its exact text (INV-146).
|
|
4112
|
+
store.writeText(join(paths.root, "plans", `${outcome.harnessId}.md`), text + "\n");
|
|
4085
4113
|
// Solo planning (D31): the FIRST successful planner is the plan; later
|
|
4086
4114
|
// pool members are a sequential fallback chain (ask parity), not
|
|
4087
4115
|
// parallel co-authors. Council re-enables the multi-draft round.
|
|
@@ -4387,11 +4415,8 @@ export class Orchestrator {
|
|
|
4387
4415
|
// carries the denial reason so a refusal discloses run.continuation.denied.
|
|
4388
4416
|
onLaunch) => {
|
|
4389
4417
|
const adapter = routed.adapter;
|
|
4390
|
-
const
|
|
4391
|
-
|
|
4392
|
-
: modelOverride
|
|
4393
|
-
? `a${String(idx + 1).padStart(2, "0")}-fb`
|
|
4394
|
-
: `a${String(idx + 1).padStart(2, "0")}`;
|
|
4418
|
+
const suffix = continuationPointer ? "-cont" : modelOverride ? "-fb" : "";
|
|
4419
|
+
const attemptId = `a${String(idx + 1).padStart(2, "0")}${suffix}`;
|
|
4395
4420
|
const budgetSignalState = { quotaPressureDisclosed: false };
|
|
4396
4421
|
const lease = ledger.reserve({
|
|
4397
4422
|
taskId,
|
|
@@ -4490,7 +4515,7 @@ export class Orchestrator {
|
|
|
4490
4515
|
const readonlyWorkEnvelope = this.workReportEnvelopeFor(routed, contract, Boolean(reportInteraction));
|
|
4491
4516
|
const readonlyWorkMode = applyWorkEnvelope(spec, readonlyWorkEnvelope);
|
|
4492
4517
|
const attemptEventsPath = join(paths.attemptsDir, attemptId, "events.jsonl");
|
|
4493
|
-
const answer = new
|
|
4518
|
+
const answer = new AnswerAssembly();
|
|
4494
4519
|
const telemetry = createAttemptTelemetry(knobs.webPolicy, contract.external_context.web_required, effectiveWeb, [],
|
|
4495
4520
|
// Requested-model capture so ask/audit route receipts detect a silent
|
|
4496
4521
|
// model downgrade (typed model_mismatch), not just agent runs.
|
|
@@ -4555,6 +4580,8 @@ export class Orchestrator {
|
|
|
4555
4580
|
workOrder: input.prompt,
|
|
4556
4581
|
firstPrompt: spec.prompt,
|
|
4557
4582
|
poolEvidence: tried,
|
|
4583
|
+
later: continuationPointer ? "checkpoint" : "fallback",
|
|
4584
|
+
explicitModel: modelOverride !== undefined,
|
|
4558
4585
|
}), { spec, continuation: input.continuation, store, telemetry }));
|
|
4559
4586
|
const onAbort = () => void adapter.cancel?.(activeSessionId)?.catch(() => { });
|
|
4560
4587
|
if (input.signal) {
|
|
@@ -4574,7 +4601,7 @@ export class Orchestrator {
|
|
|
4574
4601
|
// A3 per-try isolation (candidate-lane parity): a failed try's output,
|
|
4575
4602
|
// progress markers and model proof never carry into the next try.
|
|
4576
4603
|
if (nativeTry > 0) {
|
|
4577
|
-
answer = new
|
|
4604
|
+
answer = new AnswerAssembly();
|
|
4578
4605
|
beginNativeRetry(telemetry);
|
|
4579
4606
|
}
|
|
4580
4607
|
const runSpec = nativeTry === 0
|
|
@@ -4611,30 +4638,33 @@ export class Orchestrator {
|
|
|
4611
4638
|
},
|
|
4612
4639
|
isSuspended: () => (reportInteraction?.pendingCount?.() ?? 0) > 0,
|
|
4613
4640
|
suspensionVersion: () => reportInteraction?.suspensionVersion?.() ?? 0,
|
|
4641
|
+
stopSignal: input.signal, // after Stop: drained events are recorded, never used
|
|
4614
4642
|
});
|
|
4615
4643
|
for await (const ev of watchedReport) {
|
|
4616
|
-
|
|
4617
|
-
|
|
4618
|
-
|
|
4619
|
-
|
|
4620
|
-
log
|
|
4621
|
-
|
|
4644
|
+
// A read after Stop is stored evidence only; a callback may itself press Stop.
|
|
4645
|
+
const shown = !input.signal?.aborted && ev.payload?.["buffered"] !== true;
|
|
4646
|
+
if (shown)
|
|
4647
|
+
safeInvoke(input.onHarnessEvent, ev);
|
|
4648
|
+
observeAuthSwitch(log, adapter.id, attemptId, ev);
|
|
4649
|
+
const live = shown && !input.signal?.aborted; // the callback may have pressed Stop
|
|
4650
|
+
log.emit("harness.event", harnessEventPayload(adapter.id, attemptId, ev), live);
|
|
4651
|
+
appendHarnessEvent(attemptEventsPath, ev);
|
|
4622
4652
|
if (input.signal?.aborted)
|
|
4623
|
-
|
|
4624
|
-
observeAttemptTelemetry(telemetry,
|
|
4625
|
-
const mismatch = continuity.observe(
|
|
4653
|
+
continue;
|
|
4654
|
+
observeAttemptTelemetry(telemetry, ev);
|
|
4655
|
+
const mismatch = continuity.observe(ev, runSpec, telemetry.outputMarkers);
|
|
4626
4656
|
if (mismatch) {
|
|
4627
4657
|
harnessError = mismatch;
|
|
4628
4658
|
break;
|
|
4629
4659
|
}
|
|
4630
4660
|
if (laneRun && continuity.mayPublishSession)
|
|
4631
|
-
observeNativeSessionEvent(input, adapter.id,
|
|
4632
|
-
emitPlanProgress((t, p) => log.emit(t, p), adapter.id, attemptId,
|
|
4661
|
+
observeNativeSessionEvent(input, adapter.id, ev);
|
|
4662
|
+
emitPlanProgress((t, p) => log.emit(t, p), adapter.id, attemptId, ev);
|
|
4633
4663
|
// read-only routes burn quota too (the orchestrate PLANNER is
|
|
4634
4664
|
// the loudest) — same single owner as the agent loop.
|
|
4635
|
-
observeBudgetSignals(ledger, log, adapter.id, attemptId,
|
|
4636
|
-
this.deps.quotaEventSink?.(adapter.id,
|
|
4637
|
-
const spend = observeReadonlySpend(
|
|
4665
|
+
observeBudgetSignals(ledger, log, adapter.id, attemptId, ev, budgetSignalState);
|
|
4666
|
+
this.deps.quotaEventSink?.(adapter.id, ev);
|
|
4667
|
+
const spend = observeReadonlySpend(ev, (t, p) => log.emit(t, p), adapter.id, attemptId);
|
|
4638
4668
|
cost += spend.costUsd;
|
|
4639
4669
|
costEstimated ||= spend.estimated;
|
|
4640
4670
|
const streamDenial = updateProcessingStreamHold(runSpec, telemetry.usageCost, ledger, lease.lease.lease_id, adapter.id, attemptId);
|
|
@@ -4647,17 +4677,14 @@ export class Orchestrator {
|
|
|
4647
4677
|
break;
|
|
4648
4678
|
}
|
|
4649
4679
|
// A TYPED final message wins verbatim over joined narration.
|
|
4650
|
-
answer.
|
|
4651
|
-
if (
|
|
4652
|
-
harnessError =
|
|
4653
|
-
? redactSecrets(safeEv.error)
|
|
4654
|
-
: "harness emitted an error";
|
|
4680
|
+
answer.observe(ev);
|
|
4681
|
+
if (ev.type === "error")
|
|
4682
|
+
harnessError = ev.error ? redactSecrets(ev.error) : "harness emitted an error";
|
|
4655
4683
|
}
|
|
4656
4684
|
}
|
|
4657
4685
|
catch (err) {
|
|
4658
4686
|
harnessError = safeErrorMessage(err);
|
|
4659
|
-
if (err
|
|
4660
|
-
err instanceof RoutingPreflightError) {
|
|
4687
|
+
if (isDispatchRefusal(err)) {
|
|
4661
4688
|
processingRefusal = err;
|
|
4662
4689
|
break;
|
|
4663
4690
|
}
|
|
@@ -4732,7 +4759,7 @@ export class Orchestrator {
|
|
|
4732
4759
|
// Trim symmetrically with the plan path: a whitespace-only answer is not
|
|
4733
4760
|
// a delivered report (the final-artifact wrapper heading would otherwise
|
|
4734
4761
|
// make it read as present content by construction).
|
|
4735
|
-
const report =
|
|
4762
|
+
const report = roUnwrapped.deliverable.trim();
|
|
4736
4763
|
const unrecovered = unrecoveredToolErrors(telemetry);
|
|
4737
4764
|
const webBlocked = webUnsatisfied(telemetry);
|
|
4738
4765
|
const reportPresent = report.length > 0;
|
|
@@ -4818,7 +4845,6 @@ export class Orchestrator {
|
|
|
4818
4845
|
harnessId: adapter.id,
|
|
4819
4846
|
status: scoutInterrupted ? "failed" : "success",
|
|
4820
4847
|
report: report || "(no output)",
|
|
4821
|
-
answerMatches: answer.secretLikeMatches(),
|
|
4822
4848
|
error: scoutInterrupted ? "context capacity exhausted before the scout completed" : null,
|
|
4823
4849
|
telemetry,
|
|
4824
4850
|
...(scoutInterrupted ? { interrupted: true } : {}),
|
|
@@ -4985,7 +5011,8 @@ export class Orchestrator {
|
|
|
4985
5011
|
harnessId: a.harnessId,
|
|
4986
5012
|
status: a.status,
|
|
4987
5013
|
}));
|
|
4988
|
-
const
|
|
5014
|
+
const carried = () => (opts.deepScan ? attempts : carryingAttempts(attempts, paths.root));
|
|
5015
|
+
const cancelledTerminal = () => cancelledResult(log, runId, taskId, opts.mode, paths.root, candidateSummaries, () => this.writeRunTelemetry(store, paths, contract, runId, taskId, opts.mode, attemptTelemetries, null), ledger.spend(), input.signal, store, undefined, resumableOf(carried(), "cancelled"), attempts);
|
|
4989
5016
|
if (input.signal?.aborted)
|
|
4990
5017
|
return cancelledTerminal();
|
|
4991
5018
|
const succeededReadonly = attempts.filter((a) => a.status === "success");
|
|
@@ -5007,9 +5034,8 @@ export class Orchestrator {
|
|
|
5007
5034
|
// whole decision + reducer spawn lives in deepScanReducer.ts (its owner).
|
|
5008
5035
|
let deepScanSynthesis = null;
|
|
5009
5036
|
let reducedReport = null;
|
|
5010
|
-
let answerMatches = succeeded.reduce((sum, a) => sum + (a.answerMatches ?? 0), 0);
|
|
5011
5037
|
if (opts.deepScan) {
|
|
5012
|
-
({ deepScanSynthesis, reducedReport
|
|
5038
|
+
({ deepScanSynthesis, reducedReport } = await resolveDeepScanSynthesis(this.deepScanReducerDeps(input, contract, log), {
|
|
5013
5039
|
succeeded,
|
|
5014
5040
|
adapters,
|
|
5015
5041
|
budgetStopped,
|
|
@@ -5101,10 +5127,9 @@ export class Orchestrator {
|
|
|
5101
5127
|
const harnessLabel = attempts
|
|
5102
5128
|
.map((a) => `${a.attemptId}:${a.harnessId}:${a.status}`)
|
|
5103
5129
|
.join(", ");
|
|
5104
|
-
const secretLike = secretDiff.answerSecretLikeFinding(answerMatches);
|
|
5105
5130
|
// A delivered report that still ends unsuccessfully or blocked keeps it and names every failed try.
|
|
5106
5131
|
const reportPool = terminalPoolAttempts(terminalFacts, attempts);
|
|
5107
|
-
store.writeText(join(paths.finalDir, "summary.md"), `# Run ${runId} (${opts.mode})\n\n- Harnesses: ${harnessLabel}\n- Lifecycle: ${terminalFacts.lifecycle}${terminalFacts.reason ? ` (${terminalFacts.reason})` : ""}
|
|
5132
|
+
store.writeText(join(paths.finalDir, "summary.md"), `# Run ${runId} (${opts.mode})\n\n- Harnesses: ${harnessLabel}\n- Lifecycle: ${terminalFacts.lifecycle}${terminalFacts.reason ? ` (${terminalFacts.reason})` : ""}\n\n${withPoolAttempts(report, reportPool)}\n`);
|
|
5108
5133
|
const reportProducerAttemptId = opts.deepScan &&
|
|
5109
5134
|
deepScanSynthesis?.status === "succeeded" &&
|
|
5110
5135
|
deepScanSynthesis.reducer_attempt_id
|
|
@@ -5121,7 +5146,6 @@ export class Orchestrator {
|
|
|
5121
5146
|
mode: opts.mode,
|
|
5122
5147
|
intent: opts.intent,
|
|
5123
5148
|
read_only: true,
|
|
5124
|
-
...(secretLike ? { secret_like: secretLike } : {}),
|
|
5125
5149
|
},
|
|
5126
5150
|
});
|
|
5127
5151
|
log.emit("work_product.emitted", { kind: "report", winner: reportProducerAttemptId });
|
|
@@ -5145,7 +5169,7 @@ export class Orchestrator {
|
|
|
5145
5169
|
phase: "executor",
|
|
5146
5170
|
pool_attempts: reportPool,
|
|
5147
5171
|
failure_ref: "final/failure.yaml",
|
|
5148
|
-
...resumableTerminal(store, paths,
|
|
5172
|
+
...resumableTerminal(store, paths, carried(), true),
|
|
5149
5173
|
});
|
|
5150
5174
|
}
|
|
5151
5175
|
else if (workVetoed) {
|
|
@@ -5154,7 +5178,7 @@ export class Orchestrator {
|
|
|
5154
5178
|
facts: terminalFacts,
|
|
5155
5179
|
reason: terminalFacts.reason,
|
|
5156
5180
|
pool_attempts: reportPool,
|
|
5157
|
-
...resumableTerminal(store, paths,
|
|
5181
|
+
...resumableTerminal(store, paths, carried(), true),
|
|
5158
5182
|
});
|
|
5159
5183
|
}
|
|
5160
5184
|
else {
|
|
@@ -5162,7 +5186,7 @@ export class Orchestrator {
|
|
|
5162
5186
|
lifecycle: terminalFacts.lifecycle,
|
|
5163
5187
|
facts: terminalFacts,
|
|
5164
5188
|
reason: terminalFacts.reason,
|
|
5165
|
-
...resumableTerminal(store, paths,
|
|
5189
|
+
...resumableTerminal(store, paths, carried()),
|
|
5166
5190
|
});
|
|
5167
5191
|
}
|
|
5168
5192
|
return {
|
|
@@ -5174,7 +5198,7 @@ export class Orchestrator {
|
|
|
5174
5198
|
facts: terminalFacts,
|
|
5175
5199
|
winner: null,
|
|
5176
5200
|
runDir: paths.root,
|
|
5177
|
-
summary: withPoolAttempts(
|
|
5201
|
+
summary: withPoolAttempts(report.slice(0, 400), reportPool),
|
|
5178
5202
|
candidates: candidateSummaries,
|
|
5179
5203
|
};
|
|
5180
5204
|
}
|