@claudexor/orchestrator 3.19.0 → 3.22.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/dist/account-evidence.d.ts +13 -0
- package/dist/account-evidence.d.ts.map +1 -0
- package/dist/account-evidence.js +55 -0
- package/dist/account-evidence.js.map +1 -0
- package/dist/account-resolution.d.ts.map +1 -1
- package/dist/account-resolution.js +22 -5
- package/dist/account-resolution.js.map +1 -1
- package/dist/attemptFinalize.d.ts +14 -101
- package/dist/attemptFinalize.d.ts.map +1 -1
- package/dist/attemptFinalize.js +7 -328
- package/dist/attemptFinalize.js.map +1 -1
- package/dist/candidateEvidence.d.ts +15 -6
- package/dist/candidateEvidence.d.ts.map +1 -1
- package/dist/candidateEvidence.js +1 -1
- package/dist/candidateEvidence.js.map +1 -1
- package/dist/candidateOutputs.d.ts +7 -3
- package/dist/candidateOutputs.d.ts.map +1 -1
- package/dist/candidateOutputs.js +4 -1
- package/dist/candidateOutputs.js.map +1 -1
- package/dist/candidateReview.d.ts.map +1 -1
- package/dist/candidateReview.js +3 -1
- package/dist/candidateReview.js.map +1 -1
- package/dist/candidateTerminals.d.ts +10 -0
- package/dist/candidateTerminals.d.ts.map +1 -1
- package/dist/candidateTerminals.js +44 -5
- package/dist/candidateTerminals.js.map +1 -1
- package/dist/candidateWorkProduct.d.ts +21 -0
- package/dist/candidateWorkProduct.d.ts.map +1 -0
- package/dist/candidateWorkProduct.js +43 -0
- package/dist/candidateWorkProduct.js.map +1 -0
- package/dist/carrier-planner.d.ts +94 -0
- package/dist/carrier-planner.d.ts.map +1 -0
- package/dist/carrier-planner.js +111 -0
- package/dist/carrier-planner.js.map +1 -0
- package/dist/continuation-custody.d.ts +57 -0
- package/dist/continuation-custody.d.ts.map +1 -0
- package/dist/continuation-custody.js +151 -0
- package/dist/continuation-custody.js.map +1 -0
- package/dist/continuation-evidence-io.d.ts +18 -0
- package/dist/continuation-evidence-io.d.ts.map +1 -0
- package/dist/continuation-evidence-io.js +167 -0
- package/dist/continuation-evidence-io.js.map +1 -0
- package/dist/continuation-evidence.d.ts +81 -0
- package/dist/continuation-evidence.d.ts.map +1 -0
- package/dist/continuation-evidence.js +122 -0
- package/dist/continuation-evidence.js.map +1 -0
- package/dist/continuation-input.d.ts +10 -0
- package/dist/continuation-input.d.ts.map +1 -0
- package/dist/continuation-input.js +69 -0
- package/dist/continuation-input.js.map +1 -0
- package/dist/continue-from.d.ts +41 -0
- package/dist/continue-from.d.ts.map +1 -0
- package/dist/continue-from.js +292 -0
- package/dist/continue-from.js.map +1 -0
- package/dist/continuity-facts.d.ts +1 -0
- package/dist/continuity-facts.d.ts.map +1 -1
- package/dist/continuity-facts.js +1 -0
- package/dist/continuity-facts.js.map +1 -1
- package/dist/continuity-notice.d.ts +42 -0
- package/dist/continuity-notice.d.ts.map +1 -0
- package/dist/continuity-notice.js +42 -0
- package/dist/continuity-notice.js.map +1 -0
- package/dist/continuity-summary.d.ts +2 -1
- package/dist/continuity-summary.d.ts.map +1 -1
- package/dist/continuity-summary.js +2 -2
- package/dist/continuity-summary.js.map +1 -1
- package/dist/continuity-terminal.d.ts +19 -0
- package/dist/continuity-terminal.d.ts.map +1 -0
- package/dist/continuity-terminal.js +34 -0
- package/dist/continuity-terminal.js.map +1 -0
- package/dist/credential-cooldown.d.ts +0 -10
- package/dist/credential-cooldown.d.ts.map +1 -1
- package/dist/credential-cooldown.js +8 -27
- package/dist/credential-cooldown.js.map +1 -1
- package/dist/credential-differential.d.ts +13 -1
- package/dist/credential-differential.d.ts.map +1 -1
- package/dist/credential-differential.js +71 -14
- package/dist/credential-differential.js.map +1 -1
- package/dist/credential-execution.d.ts +4 -0
- package/dist/credential-execution.d.ts.map +1 -0
- package/dist/credential-execution.js +101 -0
- package/dist/credential-execution.js.map +1 -0
- package/dist/credential-profile-rotation.d.ts.map +1 -1
- package/dist/credential-profile-rotation.js +8 -2
- package/dist/credential-profile-rotation.js.map +1 -1
- package/dist/credential-profiles.d.ts +3 -4
- package/dist/credential-profiles.d.ts.map +1 -1
- package/dist/credential-profiles.js +28 -6
- package/dist/credential-profiles.js.map +1 -1
- package/dist/deepScanReducer.d.ts +4 -1
- package/dist/deepScanReducer.d.ts.map +1 -1
- package/dist/deepScanReducer.js +14 -8
- package/dist/deepScanReducer.js.map +1 -1
- package/dist/delegationFailure.d.ts +4 -3
- package/dist/delegationFailure.d.ts.map +1 -1
- package/dist/delegationFailure.js +32 -56
- package/dist/delegationFailure.js.map +1 -1
- package/dist/directoryCandidate.d.ts +2 -2
- package/dist/directoryCandidate.d.ts.map +1 -1
- package/dist/directoryCandidate.js +3 -0
- package/dist/directoryCandidate.js.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/inrun-continuity-carrier.d.ts +76 -0
- package/dist/inrun-continuity-carrier.d.ts.map +1 -0
- package/dist/inrun-continuity-carrier.js +218 -0
- package/dist/inrun-continuity-carrier.js.map +1 -0
- package/dist/inrun-continuity-thread.d.ts +8 -0
- package/dist/inrun-continuity-thread.d.ts.map +1 -0
- package/dist/inrun-continuity-thread.js +23 -0
- package/dist/inrun-continuity-thread.js.map +1 -0
- package/dist/inrun-continuity-types.d.ts +105 -0
- package/dist/inrun-continuity-types.d.ts.map +1 -0
- package/dist/inrun-continuity-types.js +2 -0
- package/dist/inrun-continuity-types.js.map +1 -0
- package/dist/inrun-continuity.d.ts +79 -0
- package/dist/inrun-continuity.d.ts.map +1 -0
- package/dist/inrun-continuity.js +465 -0
- package/dist/inrun-continuity.js.map +1 -0
- package/dist/inrun-continuity.test-support.d.ts +374 -0
- package/dist/inrun-continuity.test-support.d.ts.map +1 -0
- package/dist/inrun-continuity.test-support.js +198 -0
- package/dist/inrun-continuity.test-support.js.map +1 -0
- package/dist/inrun-retained-output.d.ts +9 -0
- package/dist/inrun-retained-output.d.ts.map +1 -0
- package/dist/inrun-retained-output.js +16 -0
- package/dist/inrun-retained-output.js.map +1 -0
- package/dist/interaction.d.ts +1 -1
- package/dist/interaction.d.ts.map +1 -1
- package/dist/interaction.js +6 -1
- package/dist/interaction.js.map +1 -1
- package/dist/laneStreamEvents.d.ts +2 -4
- package/dist/laneStreamEvents.d.ts.map +1 -1
- package/dist/laneStreamEvents.js +3 -5
- package/dist/laneStreamEvents.js.map +1 -1
- package/dist/modelGovernance.d.ts +4 -0
- package/dist/modelGovernance.d.ts.map +1 -1
- package/dist/modelGovernance.js +29 -3
- package/dist/modelGovernance.js.map +1 -1
- package/dist/orchestrator-credentials.d.ts +5 -1
- package/dist/orchestrator-credentials.d.ts.map +1 -1
- package/dist/orchestrator-credentials.js +32 -8
- package/dist/orchestrator-credentials.js.map +1 -1
- package/dist/orchestrator.d.ts +7 -5
- package/dist/orchestrator.d.ts.map +1 -1
- package/dist/orchestrator.js +297 -267
- package/dist/orchestrator.js.map +1 -1
- package/dist/persistedPatch.d.ts +55 -0
- package/dist/persistedPatch.d.ts.map +1 -0
- package/dist/persistedPatch.js +132 -0
- package/dist/persistedPatch.js.map +1 -0
- package/dist/planFinalize.d.ts +37 -0
- package/dist/planFinalize.d.ts.map +1 -0
- package/dist/planFinalize.js +94 -0
- package/dist/planFinalize.js.map +1 -0
- package/dist/planRun.d.ts +3 -30
- package/dist/planRun.d.ts.map +1 -1
- package/dist/planRun.js +9 -88
- package/dist/planRun.js.map +1 -1
- package/dist/plannerAttempt.d.ts +4 -1
- package/dist/plannerAttempt.d.ts.map +1 -1
- package/dist/plannerAttempt.js +14 -8
- package/dist/plannerAttempt.js.map +1 -1
- package/dist/prompt-framing.d.ts +1 -1
- package/dist/prompt-framing.d.ts.map +1 -1
- package/dist/prompt-framing.js.map +1 -1
- package/dist/retainedOutput.d.ts +4 -0
- package/dist/retainedOutput.d.ts.map +1 -0
- package/dist/retainedOutput.js +31 -0
- package/dist/retainedOutput.js.map +1 -0
- package/dist/reviewerEffort.d.ts +41 -0
- package/dist/reviewerEffort.d.ts.map +1 -0
- package/dist/reviewerEffort.js +37 -0
- package/dist/reviewerEffort.js.map +1 -0
- package/dist/reviewerPanel.d.ts +5 -5
- package/dist/reviewerPanel.d.ts.map +1 -1
- package/dist/reviewerPanel.js +22 -50
- package/dist/reviewerPanel.js.map +1 -1
- package/dist/run-input-resolution.d.ts.map +1 -1
- package/dist/run-input-resolution.js +10 -0
- package/dist/run-input-resolution.js.map +1 -1
- package/dist/runEventLog.d.ts.map +1 -1
- package/dist/runEventLog.js +5 -0
- package/dist/runEventLog.js.map +1 -1
- package/dist/runFactsBuilder.d.ts.map +1 -1
- package/dist/runFactsBuilder.js +11 -2
- package/dist/runFactsBuilder.js.map +1 -1
- package/dist/runFactsPresentation.d.ts.map +1 -1
- package/dist/runFactsPresentation.js +6 -1
- package/dist/runFactsPresentation.js.map +1 -1
- package/dist/runSupport.d.ts +2 -1
- package/dist/runSupport.d.ts.map +1 -1
- package/dist/runSupport.js +2 -1
- package/dist/runSupport.js.map +1 -1
- package/dist/runTelemetryWriter.d.ts.map +1 -1
- package/dist/runTelemetryWriter.js +7 -2
- package/dist/runTelemetryWriter.js.map +1 -1
- package/dist/runTerminalResults.d.ts +6 -2
- package/dist/runTerminalResults.d.ts.map +1 -1
- package/dist/runTerminalResults.js +28 -3
- package/dist/runTerminalResults.js.map +1 -1
- package/dist/runTerminals.d.ts.map +1 -1
- package/dist/runTerminals.js +15 -7
- package/dist/runTerminals.js.map +1 -1
- package/dist/secretDiff.d.ts +67 -27
- package/dist/secretDiff.d.ts.map +1 -1
- package/dist/secretDiff.js +143 -113
- package/dist/secretDiff.js.map +1 -1
- package/dist/session-capsule.d.ts +29 -0
- package/dist/session-capsule.d.ts.map +1 -0
- package/dist/session-capsule.js +86 -0
- package/dist/session-capsule.js.map +1 -0
- package/dist/workReportEnvelope.d.ts +123 -0
- package/dist/workReportEnvelope.d.ts.map +1 -0
- package/dist/workReportEnvelope.js +362 -0
- package/dist/workReportEnvelope.js.map +1 -0
- package/package.json +17 -17
- package/dist/effortGovernance.d.ts +0 -13
- package/dist/effortGovernance.d.ts.map +0 -1
- package/dist/effortGovernance.js +0 -7
- package/dist/effortGovernance.js.map +0 -1
package/dist/orchestrator.js
CHANGED
|
@@ -2,10 +2,12 @@ import { applyWorkEnvelope, promptWithPointers as renderPromptWithPointers, thre
|
|
|
2
2
|
import { requestRefusalFailure } from "./runTerminalResults.js";
|
|
3
3
|
import { delegationBeltFor } from "./delegationBelt.js";
|
|
4
4
|
import { bindProcessingAdmission, processingAttemptCostEvidence, processingAdmissionForLease, updateProcessingStreamHold, ProcessingBudgetAdmissionError, } from "./processing-dispatch.js";
|
|
5
|
-
import { cancelledCandidatesResult, emptyCandidateResult, failedCandidatesResult, } from "./candidateTerminals.js";
|
|
5
|
+
import { cancelledCandidatesResult, emptyCandidateResult, failedCandidatesResult, failAfterCandidates, } from "./candidateTerminals.js";
|
|
6
6
|
import { existsSync } from "node:fs";
|
|
7
|
-
import { effectiveAuthPreference, observeNativeSessionEvent, resumeSessionForProfile,
|
|
8
|
-
import { AdmissionProfileProbes, OrchestratorCredentials, reviewerProfileResolver,
|
|
7
|
+
import { effectiveAuthPreference, observeNativeSessionEvent, resumeSessionForProfile, } from "./credential-profiles.js";
|
|
8
|
+
import { AdmissionProfileProbes, OrchestratorCredentials, reviewerProfileResolver, } from "./orchestrator-credentials.js";
|
|
9
|
+
import { continuityWorkspace, resumableOf, resumableTerminal, } from "./continuity-terminal.js";
|
|
10
|
+
import { openContinuity } from "./continue-from.js";
|
|
9
11
|
import { accountPoolRows } from "./account-pool.js";
|
|
10
12
|
import { writeRunTelemetryArtifact } from "./runTelemetryWriter.js";
|
|
11
13
|
import { buildFileBackedSynthesisInput, materializeWinnerOutputs, stageFileBackedContext, writeCandidateAttemptArtifacts, } from "./candidateOutputs.js";
|
|
@@ -13,6 +15,7 @@ import { processAttemptUsage } from "./attemptUsage.js";
|
|
|
13
15
|
import { captureDirectoryCandidate, directoryHasOutput, directoryCandidateStable, observeDirectoryPaths, publishDirectoryCandidate, } from "./directoryCandidate.js";
|
|
14
16
|
import { appliedAttemptFacts, assertDelegatedEvidence, isMutatingAccess, outerBoundaryNotice, scopedHarnessHome, } from "./delegatedHome.js";
|
|
15
17
|
import * as AC from "./attemptUsageCost.js";
|
|
18
|
+
import * as custody from "./continuation-custody.js";
|
|
16
19
|
import { candidateRoster, candidateStatuses, convergenceOutcomeFacts, isWorkingCandidate, partitionCandidates, toCandidateEvidence, unanimousDeclaredFailure, } from "./candidateEvidence.js";
|
|
17
20
|
import { capabilityIntents } from "@claudexor/gateway";
|
|
18
21
|
import { reviewCandidateRuns, resolveEngineReview, unreviewedCandidateEvidence, evaluateUnreviewedConvergence, } from "./candidateReview.js";
|
|
@@ -22,10 +25,9 @@ import { runPlannerAttempt as executePlannerAttempt, plannerAttemptSummary, } fr
|
|
|
22
25
|
import { HarnessRunSpec, MAX_COUNCIL_MEMBERS_DEFAULT, ModeKind as ModeKindSchema, QuotaSnapshot as QuotaSnapshotSchema, isBlocking, makeOutcomeFacts, reviewAllowsApply, workStateVetoes, strictifyOutputSchema, estimateEffectiveAuthRoute, } from "@claudexor/schema";
|
|
23
26
|
import { globalConfigDir, loadConfig } from "@claudexor/config";
|
|
24
27
|
import { prepareRoutedProcessing, processingCostEvidence, prepareReviewerProcessing, } from "./processing-routing.js";
|
|
25
|
-
import { acceptedTryOutput, AccessProfileIncompatibleError,
|
|
28
|
+
import { acceptedTryOutput, AccessProfileIncompatibleError, CLAUDEXOR_BROWSER_ARTIFACT_SUBDIR, countsAsAgentProgress, HarnessUnavailableError, summarizeDiffPaths as diffStats, withInactivityWatchdog, } from "@claudexor/core";
|
|
26
29
|
import { assertRouteModelsAllowed, runModelGovernedRoute } from "./modelGovernance.js";
|
|
27
30
|
import { authModeForCredentialRoute, authModeForPreference, authRouteEvidenceFor, } from "./auth-route-classification.js";
|
|
28
|
-
import { governRouteEffort } from "./effortGovernance.js";
|
|
29
31
|
import { isFullAccess, RequestRequirementsResolver } from "./requestRequirements.js";
|
|
30
32
|
import { activateDelegationParent } from "./delegation-parent-activation.js";
|
|
31
33
|
import { writeRoutingFailureTerminal } from "./routing-failure.js";
|
|
@@ -41,7 +43,7 @@ import { cancelledResult, declaredFailure, failTerminally, guardAnnouncedRun, wr
|
|
|
41
43
|
import { budgetFailureRecord, classifyBudgetFailure } from "./budgetFailure.js";
|
|
42
44
|
import { finalizeStructuredOutput } from "./structuredOutput.js";
|
|
43
45
|
import { admitRun } from "./runAdmission.js";
|
|
44
|
-
import { dropDeltaPastBudget, emitPlanProgress, emitTransientExhausted,
|
|
46
|
+
import { dropDeltaPastBudget, emitPlanProgress, emitTransientExhausted, observeReadonlySpend, recordAdapterThrow, } from "./laneStreamEvents.js";
|
|
45
47
|
import { promptWithEngineConstraints, sleep, redactHarnessEvent, harnessEventPayload, safeErrorMessage, renderSummary, observeBudgetSignals, rotateOnStall, recordCleanAttemptMetrics, envInheritance, transientRetryPolicy, reviewerTimeoutMs, harnessInactivityTimeoutMs, observeAuthSwitch, emitPrimaryDivergence, emitPoolDegraded, deliveryRefusalFailure, winnerNeedsHuman, writeRaceDeliveryDecision, } from "./runSupport.js";
|
|
46
48
|
import { candidateStatusInRouteContext, resolveReadOnlyRouteContext, } from "./routeContext.js";
|
|
47
49
|
import { resolveAutoReviewerPanel, resolveExplicitReviewerPanel } from "./reviewerPanel.js";
|
|
@@ -55,7 +57,8 @@ import { newAttemptOutputMarkers } from "./attemptOutputMarkers.js";
|
|
|
55
57
|
import * as delegateFailure from "./delegationFailure.js";
|
|
56
58
|
import * as secretDiff from "./secretDiff.js";
|
|
57
59
|
import { dominantHarnessFailureCategory, harnessFailureNextActions } from "./harnessFailure.js";
|
|
58
|
-
import { finalizeAttempt, readOnlyNoSuccessTerminal,
|
|
60
|
+
import { finalizeAttempt, readOnlyNoSuccessTerminal, unrecoveredToolErrorFailure, webEvidenceFailure, } from "./attemptFinalize.js";
|
|
61
|
+
import { resolveWorkReportEnvelope, unwrapWorkReportEnvelope, } from "./workReportEnvelope.js";
|
|
59
62
|
import { buildContinuationPacket, decideContinuation, synthesizeContinuationRequest, } from "./continuation.js";
|
|
60
63
|
import { interactionChannelFor } from "./interaction.js";
|
|
61
64
|
import { liveAttempt } from "./live-input.js";
|
|
@@ -73,8 +76,6 @@ import { billingKnowledgeForAuthRoute, attemptUsageCostSettlement, BudgetLedger,
|
|
|
73
76
|
import { readTextSafe, appendLine, assertNoInlineSecretValues, hashJson, newId, noProjectRepoRoot, redactSecrets, safeInvoke, sha256, userConfigDir, } from "@claudexor/util";
|
|
74
77
|
const LABELS = "ABCDEFGHIJ".split("");
|
|
75
78
|
const NO_PROJECT_ROOT = noProjectRepoRoot();
|
|
76
|
-
/** Default wait for one interactive answer before a benign decline. */
|
|
77
|
-
const DEFAULT_INTERACTION_TIMEOUT_MS = 900_000;
|
|
78
79
|
export class Orchestrator {
|
|
79
80
|
deps;
|
|
80
81
|
gateway;
|
|
@@ -210,7 +211,7 @@ export class Orchestrator {
|
|
|
210
211
|
if (this.deps.reviewers)
|
|
211
212
|
return this.deps.reviewers;
|
|
212
213
|
if (this.deps.reviewerPanel && this.deps.reviewerPanel.length > 0) {
|
|
213
|
-
return this.resolveExplicitReviewerPanel(cwd, this.deps.reviewerPanel, runAuthPreference);
|
|
214
|
+
return this.resolveExplicitReviewerPanel(cwd, this.deps.reviewerPanel, runAuthPreference, onIgnoredSetting);
|
|
214
215
|
}
|
|
215
216
|
return resolveAutoReviewerPanel({
|
|
216
217
|
cwd,
|
|
@@ -232,7 +233,7 @@ export class Orchestrator {
|
|
|
232
233
|
if (input.review === false)
|
|
233
234
|
return { reviewers: [] };
|
|
234
235
|
try {
|
|
235
|
-
//
|
|
236
|
+
// Reviewer knobs a panel omitted or dropped → ignored-settings channel (QA-070):
|
|
236
237
|
const warn = (d) => void log.emit("review.preflight", { ignored_settings: [d] });
|
|
237
238
|
const config = this.config(input.repoRoot).global;
|
|
238
239
|
const reviewers = await this.resolveReviewers(input.repoRoot, input.authPreference, warn);
|
|
@@ -274,13 +275,14 @@ export class Orchestrator {
|
|
|
274
275
|
};
|
|
275
276
|
}
|
|
276
277
|
}
|
|
277
|
-
async resolveExplicitReviewerPanel(cwd, panel, runAuthPreference) {
|
|
278
|
+
async resolveExplicitReviewerPanel(cwd, panel, runAuthPreference, onIgnoredSetting) {
|
|
278
279
|
return resolveExplicitReviewerPanel({
|
|
279
280
|
cwd,
|
|
280
281
|
registry: this.deps.registry,
|
|
281
282
|
harnessSettings: this.config(cwd)?.global.harnesses ?? {},
|
|
282
283
|
authPreferenceFor: (id) => this.authPreferenceForHarness(cwd, id, runAuthPreference),
|
|
283
284
|
resolveReviewerProfile: reviewerProfileResolver(this.credentials, cwd),
|
|
285
|
+
onIgnoredSetting,
|
|
284
286
|
}, panel);
|
|
285
287
|
}
|
|
286
288
|
authPreferenceForHarness(repoRoot, harnessId, runAuthPreference) {
|
|
@@ -368,6 +370,8 @@ export class Orchestrator {
|
|
|
368
370
|
quotaAbsences: () => this.deps.quotaAbsences?.() ?? [],
|
|
369
371
|
credentialUnusable: () => this.deps.credentialUnusable?.() ?? [],
|
|
370
372
|
recordCredentialUnusable: (obs) => this.deps.recordCredentialUnusable?.(obs),
|
|
373
|
+
credentialObserverFactory: () => this.deps.credentialObserverFactory,
|
|
374
|
+
credentialEvidence: () => this.deps.credentialEvidence,
|
|
371
375
|
preProgressRefusals: () => this.deps.preProgressRefusals,
|
|
372
376
|
authPreferenceForHarness: (repoRoot, harnessId, runPreference) => this.authPreferenceForHarness(repoRoot, harnessId, runPreference),
|
|
373
377
|
});
|
|
@@ -657,7 +661,6 @@ export class Orchestrator {
|
|
|
657
661
|
requiresFullAccess: manifest.capability_profile.mcp_injection_requires_full_access,
|
|
658
662
|
fullAccess: isFullAccess(requiredAccess),
|
|
659
663
|
}),
|
|
660
|
-
effortLevels: manifest.capabilities.effort_levels,
|
|
661
664
|
knownModels: manifest.capabilities.known_models,
|
|
662
665
|
modelInventory: manifest.capabilities,
|
|
663
666
|
// A selected profile's credential_kind IS the route (round-18 #2);
|
|
@@ -817,7 +820,7 @@ export class Orchestrator {
|
|
|
817
820
|
// settles clean), so a schema run keeps its interaction channel — since
|
|
818
821
|
// the daemon always arms one, the refusal was denying claude every
|
|
819
822
|
// daemon/CLI structured-output run. The WorkReport side_tool envelope
|
|
820
|
-
// stays interactive-gated separately (
|
|
823
|
+
// stays interactive-gated separately (workReportEnvelope) as a deliberate
|
|
821
824
|
// scope choice; the caller schema itself rides regardless.
|
|
822
825
|
if (input.outputSchema !== undefined && input.outputSchema !== null) {
|
|
823
826
|
const incapable = out.filter((lane) => !lane.supportsJsonSchemaOutput);
|
|
@@ -1139,16 +1142,14 @@ export class Orchestrator {
|
|
|
1139
1142
|
// per-harness map, which beats the per-harness settings default. There is
|
|
1140
1143
|
// no run-global model.
|
|
1141
1144
|
const model = overrideModel ?? contract.routing_models[routed.adapter.id] ?? s?.defaultModel ?? null;
|
|
1142
|
-
//
|
|
1143
|
-
//
|
|
1144
|
-
//
|
|
1145
|
-
//
|
|
1146
|
-
//
|
|
1147
|
-
//
|
|
1148
|
-
|
|
1149
|
-
const effort =
|
|
1150
|
-
if (governed.ignored)
|
|
1151
|
-
ignored.push(governed.ignored);
|
|
1145
|
+
// The effort PREFERENCE travels to the route unchanged (INV-105): discovery
|
|
1146
|
+
// describes the DEFAULT account, so only the final route may resolve it — a
|
|
1147
|
+
// knob adapter against the catalog of the profile env its child runs in, the
|
|
1148
|
+
// engine as `omitted` for a route without a knob — and that one resolution is
|
|
1149
|
+
// what the receipt and the disclosure record. The contract's FROZEN per-lane
|
|
1150
|
+
// effort (QA-035) wins so Exact Retry replays it without re-reading settings;
|
|
1151
|
+
// `effortHint`/settings apply only to an unfrozen lane.
|
|
1152
|
+
const effort = contract.routing_efforts[routed.adapter.id] ?? effortHint ?? s?.effort ?? null;
|
|
1152
1153
|
return {
|
|
1153
1154
|
model,
|
|
1154
1155
|
effort,
|
|
@@ -1222,6 +1223,7 @@ export class Orchestrator {
|
|
|
1222
1223
|
processing,
|
|
1223
1224
|
processingAdmission,
|
|
1224
1225
|
physicalDispatchStarted,
|
|
1226
|
+
credentialObserverFactory: this.deps.credentialObserverFactory,
|
|
1225
1227
|
billingVerificationForProfile: (profile) => this.credentials.selectedProfileBillingVerification(profile),
|
|
1226
1228
|
paidFallback: this.config(runInput.repoRoot).global.routing.paid_fallback,
|
|
1227
1229
|
});
|
|
@@ -1382,17 +1384,17 @@ export class Orchestrator {
|
|
|
1382
1384
|
let applied = appliedNow();
|
|
1383
1385
|
const attemptStartedMs = Date.now();
|
|
1384
1386
|
const budgetSignalState = { quotaPressureDisclosed: false };
|
|
1385
|
-
const triedProfiles = new Set(); // W5.4 failover: each profile at most once
|
|
1386
1387
|
let cost = 0;
|
|
1387
1388
|
let costEstimated = false;
|
|
1388
1389
|
let harnessErrored = false;
|
|
1390
|
+
let streamBudgetDenied = false;
|
|
1389
1391
|
let processingRefusal = null;
|
|
1390
1392
|
let poolExhausted = null; // A5: typed pool-exhausted refusal
|
|
1391
1393
|
const deltaFlood = { count: 0, disclosed: false }; // W-C4 per-attempt delta budget
|
|
1392
1394
|
// QA-024: emit the belt-failure disclosure event at most once per attempt.
|
|
1393
1395
|
let beltFailureDisclosed = false;
|
|
1394
1396
|
const errors = [];
|
|
1395
|
-
let answer = new
|
|
1397
|
+
let answer = new secretDiff.CountedAnswerAssembly();
|
|
1396
1398
|
const retryPolicy = transientRetryPolicy(this.config(contract.repo.root));
|
|
1397
1399
|
// QA-024: the delegation belt is the ONLY engine-owned extra MCP server
|
|
1398
1400
|
// injected into an agent lane (the browser MCP rides its own field), so its
|
|
@@ -1413,28 +1415,31 @@ export class Orchestrator {
|
|
|
1413
1415
|
onAbort();
|
|
1414
1416
|
else
|
|
1415
1417
|
signal?.addEventListener("abort", onAbort, { once: true });
|
|
1418
|
+
let continuity;
|
|
1419
|
+
({ continuity, spec } = await openContinuity(this.continuityDeps(adapter, routed, contract, attemptId, paths, runInput, log, {
|
|
1420
|
+
envelope,
|
|
1421
|
+
harnessHome,
|
|
1422
|
+
workOrder: prompt,
|
|
1423
|
+
firstPrompt: spec.prompt,
|
|
1424
|
+
}), { spec, continuation: runInput?.continuation, store, telemetry }));
|
|
1416
1425
|
try {
|
|
1417
1426
|
for (let nativeTry = 0; !signal?.aborted; nativeTry += 1) {
|
|
1418
1427
|
// A3 per-try isolation: neither output nor progress markers leak across tries.
|
|
1419
1428
|
if (nativeTry > 0) {
|
|
1420
|
-
answer = new
|
|
1429
|
+
answer = new secretDiff.CountedAnswerAssembly();
|
|
1421
1430
|
telemetry.outputMarkers = newAttemptOutputMarkers();
|
|
1422
1431
|
}
|
|
1423
1432
|
const clearFileBackedContext = stageFileBackedContext(envelope.worktree_path, fileBackedContext);
|
|
1424
1433
|
const runSpec = nativeTry === 0
|
|
1425
1434
|
? spec
|
|
1426
1435
|
: HarnessRunSpec.parse({ ...spec, session_id: newId("ses"), extra: { ...spec.extra } });
|
|
1427
|
-
//
|
|
1428
|
-
// stream (killing the process group through the existing abort
|
|
1429
|
-
// plumbing) without touching the run-level cancel signal, so a timeout
|
|
1430
|
-
// and a user cancel stay distinguishable. Recreated per
|
|
1431
|
-
// nativeTry — a transient retry must get a LIVE signal, not the
|
|
1432
|
-
// previous try's already-aborted composite.
|
|
1436
|
+
// The watchdog aborts only this try; run cancellation also prevents successors.
|
|
1433
1437
|
const attemptAbort = new AbortController();
|
|
1434
1438
|
runSpec.extra["abortSignal"] = signal
|
|
1435
1439
|
? AbortSignal.any([signal, attemptAbort.signal])
|
|
1436
1440
|
: attemptAbort.signal;
|
|
1437
1441
|
activeSessionId = runSpec.session_id;
|
|
1442
|
+
continuity.beginTry(runSpec, nativeTry, attemptAbort);
|
|
1438
1443
|
// #363: bound before the spawn — a credential change mid-try voids it.
|
|
1439
1444
|
const refusal = this.credentials.bindPreProgressRefusal(adapter.id, runSpec);
|
|
1440
1445
|
const transientStart = telemetry.transientFailures.length;
|
|
@@ -1455,26 +1460,29 @@ export class Orchestrator {
|
|
|
1455
1460
|
suspensionVersion: () => interaction?.suspensionVersion?.() ?? 0,
|
|
1456
1461
|
});
|
|
1457
1462
|
for await (const ev of watched) {
|
|
1458
|
-
if (signal?.aborted)
|
|
1459
|
-
break;
|
|
1460
1463
|
rawPatch = captureRawPatchEnvelope(rawContextPacket !== null, rawPatch, ev);
|
|
1461
1464
|
if (ev.type === "patch_produced")
|
|
1462
1465
|
continue;
|
|
1463
1466
|
const safeEv = redactHarnessEvent(ev);
|
|
1464
1467
|
if (directory)
|
|
1465
1468
|
observeDirectoryPaths(observedPaths, safeEv, envelope.worktree_path);
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
// unreachable ids — skip it there.
|
|
1474
|
-
if (inPlaceEnvelope)
|
|
1475
|
-
observeNativeSessionEvent(runInput, adapter.id, safeEv);
|
|
1469
|
+
const capped = dropDeltaPastBudget(safeEv, deltaFlood, Orchestrator.MAX_DELTAS_PER_ATTEMPT, (t, p) => log?.emit(t, p), adapter.id, attemptId);
|
|
1470
|
+
const display = !capped && safeEv.payload?.["buffered"] !== true;
|
|
1471
|
+
log?.emit("harness.event", harnessEventPayload(adapter.id, attemptId, safeEv), display);
|
|
1472
|
+
if (signal?.aborted)
|
|
1473
|
+
break;
|
|
1474
|
+
if (display)
|
|
1475
|
+
safeInvoke(onHarnessEvent, safeEv);
|
|
1476
1476
|
observeAuthSwitch(log, adapter.id, attemptId, safeEv);
|
|
1477
1477
|
observeAttemptTelemetry(telemetry, safeEv);
|
|
1478
|
+
const mismatch = continuity.observe(safeEv, runSpec, telemetry.outputMarkers);
|
|
1479
|
+
if (mismatch) {
|
|
1480
|
+
harnessErrored = true;
|
|
1481
|
+
errors.push(mismatch);
|
|
1482
|
+
break;
|
|
1483
|
+
}
|
|
1484
|
+
if (inPlaceEnvelope && continuity.mayPublishSession)
|
|
1485
|
+
observeNativeSessionEvent(runInput, adapter.id, safeEv);
|
|
1478
1486
|
// QA-024: the injected delegation belt's MCP server reported a
|
|
1479
1487
|
// terminal startup failure. Disclose it ONCE while live; recoverable
|
|
1480
1488
|
// exact tool-result failures are evaluated at attempt finalization.
|
|
@@ -1507,7 +1515,7 @@ export class Orchestrator {
|
|
|
1507
1515
|
cost = usage.cost;
|
|
1508
1516
|
costEstimated = usage.costEstimated;
|
|
1509
1517
|
if (usage.hardCapReached) {
|
|
1510
|
-
harnessErrored = true;
|
|
1518
|
+
harnessErrored = streamBudgetDenied = true;
|
|
1511
1519
|
errors.push("budget hard cap reached mid-attempt; stream aborted");
|
|
1512
1520
|
break;
|
|
1513
1521
|
}
|
|
@@ -1518,7 +1526,7 @@ export class Orchestrator {
|
|
|
1518
1526
|
}
|
|
1519
1527
|
// Capture assistant prose so an answer-only turn (no file changes) still
|
|
1520
1528
|
// has an honest output artifact; a TYPED final message wins verbatim.
|
|
1521
|
-
answer.
|
|
1529
|
+
answer.observeCounted(ev, safeEv);
|
|
1522
1530
|
// Observe ALL budget/quota signals (one codex usage event carries
|
|
1523
1531
|
// BOTH spend and quota); pressure disclosed once per attempt.
|
|
1524
1532
|
observeBudgetSignals(ledger, log, adapter.id, attemptId, safeEv, budgetSignalState);
|
|
@@ -1566,64 +1574,37 @@ export class Orchestrator {
|
|
|
1566
1574
|
const deliverableEmpty = workspaceUnchanged && answer.text().length === 0;
|
|
1567
1575
|
const delivered = !harnessErrored && !deliverableEmpty && !signal?.aborted;
|
|
1568
1576
|
this.credentials.noteTryServed(refusal, telemetry.outputMarkers, delivered);
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
lastLimit: telemetry.rateLimits.at(-1) ?? null,
|
|
1593
|
-
emit: (type, payload) => log?.emit(type, payload),
|
|
1594
|
-
newSessionId: () => newId("ses"),
|
|
1595
|
-
defaultRouteWasVendorNative: routed.authRouteEstimate === "local_session",
|
|
1596
|
-
// D-U6: an explicit pin never rotates; pool-selected rows do.
|
|
1597
|
-
pinned: runInput.credentialProfileId != null,
|
|
1598
|
-
});
|
|
1599
|
-
// A5 ordering: an exhausted pool terminalizes TYPED here, BEFORE the
|
|
1600
|
-
// transient gate below burns same-profile retries (limits classify
|
|
1601
|
-
// as retryable) on the already-refused subject.
|
|
1602
|
-
if (rotated && "poolExhausted" in rotated) {
|
|
1603
|
-
poolExhausted = rotated.poolExhausted;
|
|
1604
|
-
errors.push(safeErrorMessage(poolExhausted));
|
|
1605
|
-
break;
|
|
1606
|
-
}
|
|
1607
|
-
if (rotated) {
|
|
1608
|
-
// INV-137: the rotated row runs in ITS OWN lane home (no-op for
|
|
1609
|
-
// isolated envelopes and native-env in-place turns).
|
|
1610
|
-
spec = rotatedSpecInLaneHome(spec, rotated, (id) => this.laneHomeEnvFor(runInput, adapter.id, id), runInput.credentialProfileId ?? null);
|
|
1611
|
-
applied = appliedNow();
|
|
1612
|
-
errors.length = 0;
|
|
1613
|
-
harnessErrored = false;
|
|
1614
|
-
continue;
|
|
1615
|
-
}
|
|
1577
|
+
const verdict = await continuity.afterTry({
|
|
1578
|
+
runSpec,
|
|
1579
|
+
nativeTry,
|
|
1580
|
+
harnessErrored,
|
|
1581
|
+
budgetStopped: streamBudgetDenied,
|
|
1582
|
+
aborted: signal?.aborted === true,
|
|
1583
|
+
requestRefused: Boolean(telemetry.requestRefusal),
|
|
1584
|
+
newTransients,
|
|
1585
|
+
sawTypedLimit,
|
|
1586
|
+
sawRetryable,
|
|
1587
|
+
answer,
|
|
1588
|
+
rawDeliverableEmpty: deliverableEmpty,
|
|
1589
|
+
workspaceDiffNonEmpty: !workspaceUnchanged,
|
|
1590
|
+
currentDiff,
|
|
1591
|
+
markers: telemetry.outputMarkers,
|
|
1592
|
+
lastLimit: telemetry.rateLimits.at(-1) ?? null,
|
|
1593
|
+
refusal,
|
|
1594
|
+
telemetry,
|
|
1595
|
+
});
|
|
1596
|
+
if (verdict.kind === "terminal") {
|
|
1597
|
+
poolExhausted = verdict.error;
|
|
1598
|
+
errors.push(safeErrorMessage(poolExhausted));
|
|
1599
|
+
break;
|
|
1616
1600
|
}
|
|
1617
|
-
if (
|
|
1618
|
-
!sawRetryable ||
|
|
1619
|
-
!deliverableEmpty ||
|
|
1620
|
-
nativeTry >= retryPolicy.maxRetries ||
|
|
1621
|
-
signal?.aborted)
|
|
1601
|
+
if (verdict.kind === "break")
|
|
1622
1602
|
break;
|
|
1623
|
-
|
|
1603
|
+
spec = verdict.spec;
|
|
1604
|
+
applied = appliedNow();
|
|
1624
1605
|
errors.length = 0;
|
|
1625
1606
|
harnessErrored = false;
|
|
1626
|
-
await sleep(delayMs);
|
|
1607
|
+
await sleep(verdict.delayMs);
|
|
1627
1608
|
}
|
|
1628
1609
|
}
|
|
1629
1610
|
finally {
|
|
@@ -1643,19 +1624,23 @@ export class Orchestrator {
|
|
|
1643
1624
|
// with no typed final contributes no answer material (acceptedTryOutput).
|
|
1644
1625
|
const unwrapped = unwrapWorkReportEnvelope(acceptedTryOutput(answer, harnessErrored), workReportMode, { sideToolReport: telemetry.sideToolWorkReport ?? undefined });
|
|
1645
1626
|
const redacted = redactSecrets(unwrapped.deliverable);
|
|
1646
|
-
|
|
1627
|
+
// INV-062: the (already redacted) answer is kept whatever the patch holds.
|
|
1628
|
+
const answerText = redacted.trim().length > 0 ? redacted : undefined;
|
|
1647
1629
|
if (directory)
|
|
1648
1630
|
await captureDirectory();
|
|
1649
|
-
const
|
|
1650
|
-
? { diff: "",
|
|
1651
|
-
: await secretDiff.
|
|
1652
|
-
|
|
1653
|
-
|
|
1631
|
+
const captured = directory
|
|
1632
|
+
? { diff: "", captureRefusal: directoryCapture.refusal }
|
|
1633
|
+
: await secretDiff.captureCandidateWorkspace({
|
|
1634
|
+
...{ wsm, envelope, inPlace: inPlaceEnvelope, projectRoot: store.repoRoot },
|
|
1635
|
+
...{ answerText, answerMatches: answer.secretLikeMatches() },
|
|
1636
|
+
});
|
|
1637
|
+
const { diff, captureRefusal } = captured;
|
|
1638
|
+
harnessErrored = secretDiff.recordCaptureRefusal(captureRefusal, errors, harnessErrored);
|
|
1654
1639
|
const deliverableEvidence = diff.trim().length > 0 || Boolean(answerText) || directoryHasOutput(directoryCapture.files);
|
|
1655
1640
|
// Cancelled attempts skip gates: running a 600s-per-gate suite delays the ack
|
|
1656
1641
|
// and burns compute on a result nobody will adopt. Diff/attempt.yaml
|
|
1657
1642
|
// still land, so partial work stays inspectable.
|
|
1658
|
-
const gateSignalAborted = signal?.aborted === true ||
|
|
1643
|
+
const gateSignalAborted = signal?.aborted === true || captureRefusal !== undefined;
|
|
1659
1644
|
if (!gateSignalAborted) {
|
|
1660
1645
|
log?.emit("gate.started", {
|
|
1661
1646
|
attempt_id: attemptId,
|
|
@@ -1702,6 +1687,7 @@ export class Orchestrator {
|
|
|
1702
1687
|
workReport: unwrapped.workReport,
|
|
1703
1688
|
workReportSource: unwrapped.source,
|
|
1704
1689
|
workReportViolation: unwrapped.contractViolation,
|
|
1690
|
+
workReportUnverified: unwrapped.unverified ?? null,
|
|
1705
1691
|
contextTerminalExhausted: telemetry.contextExhausted,
|
|
1706
1692
|
});
|
|
1707
1693
|
harnessErrored = finalized.harnessErrored;
|
|
@@ -1709,6 +1695,14 @@ export class Orchestrator {
|
|
|
1709
1695
|
errors.push(`work_report contract: ${unwrapped.contractViolation}`);
|
|
1710
1696
|
const deliverablePresent = finalized.deliverablePresent;
|
|
1711
1697
|
const errored = harnessErrored || webBlocked;
|
|
1698
|
+
const resumable = await continuity.finish({
|
|
1699
|
+
runSpec: spec,
|
|
1700
|
+
errored,
|
|
1701
|
+
nonSuccess: !gatesPassed(gates),
|
|
1702
|
+
aborted: signal?.aborted === true,
|
|
1703
|
+
cancelReason: typeof signal?.reason === "string" ? signal.reason : null,
|
|
1704
|
+
workState: finalized.workState,
|
|
1705
|
+
});
|
|
1712
1706
|
setAttemptOutcome(telemetry, {
|
|
1713
1707
|
deliverablePresent,
|
|
1714
1708
|
gatesPassed: gates.length > 0 ? gatesPassed(gates) : null,
|
|
@@ -1729,9 +1723,9 @@ export class Orchestrator {
|
|
|
1729
1723
|
attemptDir,
|
|
1730
1724
|
worktreePath: envelope.worktree_path,
|
|
1731
1725
|
artifactRelativeDir,
|
|
1732
|
-
diff,
|
|
1733
|
-
persistPatch: !directory &&
|
|
1734
|
-
persistProducedMedia: !directory &&
|
|
1726
|
+
...{ diff, persistedDiff: captured.persistedDiff },
|
|
1727
|
+
persistPatch: !directory && captureRefusal === undefined && isMutatingAccess(access),
|
|
1728
|
+
persistProducedMedia: !directory && captureRefusal === undefined,
|
|
1735
1729
|
answerText,
|
|
1736
1730
|
record: {
|
|
1737
1731
|
attempt_id: attemptId,
|
|
@@ -1743,7 +1737,7 @@ export class Orchestrator {
|
|
|
1743
1737
|
errors: errors.slice(0, 5),
|
|
1744
1738
|
...telemetrySummary(telemetry),
|
|
1745
1739
|
outcome: telemetry.outcome,
|
|
1746
|
-
...(
|
|
1740
|
+
...secretDiff.attemptDisclosure(captured),
|
|
1747
1741
|
gates: gates.map((g) => ({ id: g.id, status: g.status })),
|
|
1748
1742
|
branch: envelope.branch_name,
|
|
1749
1743
|
...(directoryCapture.files
|
|
@@ -1765,11 +1759,17 @@ export class Orchestrator {
|
|
|
1765
1759
|
estimated: costEstimated,
|
|
1766
1760
|
settlement: attemptUsageCostSettlement(cost, costEstimated, attemptId, adapter.id, telemetry.authMode, telemetry.usageCost),
|
|
1767
1761
|
});
|
|
1762
|
+
for (const path of producedFiles)
|
|
1763
|
+
log?.emit("output.ready", {
|
|
1764
|
+
kind: "artifact",
|
|
1765
|
+
path: `attempts/${attemptId}/produced/${path}`,
|
|
1766
|
+
state: "diagnostic",
|
|
1767
|
+
});
|
|
1768
1768
|
return {
|
|
1769
1769
|
attemptId,
|
|
1770
1770
|
harnessId: adapter.id,
|
|
1771
1771
|
label,
|
|
1772
|
-
|
|
1772
|
+
...captured,
|
|
1773
1773
|
...(directoryCapture.files ? { files: directoryCapture.files } : {}),
|
|
1774
1774
|
answerText,
|
|
1775
1775
|
reviewCwd: envelope.worktree_path,
|
|
@@ -1781,7 +1781,6 @@ export class Orchestrator {
|
|
|
1781
1781
|
costEstimated,
|
|
1782
1782
|
errors: errors.slice(0, 8),
|
|
1783
1783
|
telemetry,
|
|
1784
|
-
...(secretDiffRefusal ? { secretDiffRefusal } : {}),
|
|
1785
1784
|
// A5: the typed refusal survives NORMAL attempt finalization (no throw).
|
|
1786
1785
|
...(telemetry.requestRefusal
|
|
1787
1786
|
? { declaredFailure: requestRefusalFailure(telemetry.requestRefusal) }
|
|
@@ -1790,14 +1789,47 @@ export class Orchestrator {
|
|
|
1790
1789
|
: {}),
|
|
1791
1790
|
outcomeClass: finalized.outcomeClass,
|
|
1792
1791
|
applied,
|
|
1792
|
+
...continuity.terminalFacts(resumable),
|
|
1793
1793
|
};
|
|
1794
1794
|
}
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1795
|
+
continuityDeps(adapter, routed, contract, attemptId, paths, runInput, log, lane) {
|
|
1796
|
+
const repoRoot = contract.repo.root;
|
|
1797
|
+
const cwd = lane.envelope?.worktree_path ?? (runInput ? this.execRootOf(runInput) : repoRoot);
|
|
1798
|
+
return {
|
|
1799
|
+
adapter,
|
|
1800
|
+
runId: paths.runId,
|
|
1801
|
+
attemptId,
|
|
1802
|
+
runDir: paths.root,
|
|
1803
|
+
attemptDir: join(paths.attemptsDir, attemptId),
|
|
1804
|
+
cwd,
|
|
1805
|
+
workspace: continuityWorkspace(lane.envelope, cwd),
|
|
1806
|
+
workOrder: lane.workOrder,
|
|
1807
|
+
firstPrompt: lane.firstPrompt,
|
|
1808
|
+
registry: this.config(repoRoot)?.global.credential_profiles ?? [],
|
|
1809
|
+
policy: this.credentials.profilePolicy(repoRoot, adapter.id),
|
|
1810
|
+
snapshots: this.deps.quotaSnapshots?.() ?? [],
|
|
1811
|
+
retryPolicy: transientRetryPolicy(this.config(repoRoot)),
|
|
1812
|
+
pinned: runInput?.credentialProfileId != null, // D-U6: a pin never rotates
|
|
1813
|
+
defaultRouteWasVendorNative: routed.authRouteEstimate === "local_session",
|
|
1814
|
+
requestedProfileId: runInput?.credentialProfileId ?? null,
|
|
1815
|
+
rotationEnabled: runInput !== undefined,
|
|
1816
|
+
laneEnvFor: (id) => (runInput ? this.laneHomeEnvFor(runInput, adapter.id, id) : null),
|
|
1817
|
+
probeReadyProfiles: (spec, tried) => runInput
|
|
1818
|
+
? this.credentials.readyProfileIdsForRotation(runInput, adapter.id, spec.credential_profile ?? null, tried, spec.model_hint ?? null)
|
|
1819
|
+
: Promise.resolve(new Set()),
|
|
1820
|
+
rotationObservations: (spec, transients, refusal) => this.credentials.rotationObservations(adapter, spec, transients, refusal),
|
|
1821
|
+
emit: (type, payload) => log?.emit(type, payload),
|
|
1822
|
+
route: routed,
|
|
1823
|
+
newSessionId: () => newId("ses"),
|
|
1824
|
+
thread: runInput?.threadId
|
|
1825
|
+
? {
|
|
1826
|
+
threadId: runInput.threadId,
|
|
1827
|
+
turnId: runInput.threadContinuity?.turnId ?? null,
|
|
1828
|
+
onSessionObserved: runInput.onSessionObserved,
|
|
1829
|
+
onContinuityResolved: runInput.onContinuityResolved,
|
|
1830
|
+
}
|
|
1831
|
+
: null,
|
|
1832
|
+
};
|
|
1801
1833
|
}
|
|
1802
1834
|
async runRace(input, mode, announce) {
|
|
1803
1835
|
const taskId = input.taskId ?? newId("task");
|
|
@@ -1917,7 +1949,7 @@ export class Orchestrator {
|
|
|
1917
1949
|
const disposeReviewEnvelopes = async () => {
|
|
1918
1950
|
const envelopes = reviewEnvelopes.splice(0);
|
|
1919
1951
|
for (const env of envelopes)
|
|
1920
|
-
await
|
|
1952
|
+
await kept.settle(wsm, env, runsBySlot);
|
|
1921
1953
|
};
|
|
1922
1954
|
const candidateAccess = contract.access.effective_profile;
|
|
1923
1955
|
let budgetStopped = false;
|
|
@@ -1926,6 +1958,7 @@ export class Orchestrator {
|
|
|
1926
1958
|
let budgetDenial = null;
|
|
1927
1959
|
let softWarned = false;
|
|
1928
1960
|
const requestedSingleCandidate = adapters.length === 1;
|
|
1961
|
+
const kept = custody.forRun(input, requestedSingleCandidate && mutatingRun, runId, paths, log);
|
|
1929
1962
|
const slots = [];
|
|
1930
1963
|
for (let i = 0; i < adapters.length; i++) {
|
|
1931
1964
|
const routed = adapters[i];
|
|
@@ -2025,7 +2058,7 @@ export class Orchestrator {
|
|
|
2025
2058
|
external_context_policy: knobs.webPolicy,
|
|
2026
2059
|
...(knobs.ignored.length > 0 ? { ignored_settings: knobs.ignored } : {}),
|
|
2027
2060
|
});
|
|
2028
|
-
envelope = await
|
|
2061
|
+
envelope = await kept.envelope(wsm, {
|
|
2029
2062
|
taskId,
|
|
2030
2063
|
attemptId: slot.attemptId,
|
|
2031
2064
|
baseRef: contract.repo.base_ref,
|
|
@@ -2047,11 +2080,7 @@ export class Orchestrator {
|
|
|
2047
2080
|
budgetStopped = true;
|
|
2048
2081
|
budgetDenial ??= denial;
|
|
2049
2082
|
},
|
|
2050
|
-
}, candidateAccess, (
|
|
2051
|
-
const safeEv = redactHarnessEvent(ev);
|
|
2052
|
-
safeInvoke(input.onHarnessEvent, safeEv);
|
|
2053
|
-
log.emit("harness.event", harnessEventPayload(adapter.id, slot.attemptId, safeEv));
|
|
2054
|
-
}, input.signal, downgradeModel ?? undefined, input.effort, this.candidateIntent(input), log, effectiveWeb, this.interactionChannelFor(input, log, runId, taskId, slot.attemptId, adapter.id, slot.routed.supportsInteractive), (streamedUsd) => {
|
|
2083
|
+
}, candidateAccess, input.onHarnessEvent, input.signal, downgradeModel ?? undefined, input.effort, this.candidateIntent(input), log, effectiveWeb, interactionChannelFor(input, log, runId, taskId, slot.attemptId, adapter.id, slot.routed.supportsInteractive), (streamedUsd) => {
|
|
2055
2084
|
ledger.updateHold(slot.lease.lease_id, streamedUsd);
|
|
2056
2085
|
if (ledger.tier() !== "hard")
|
|
2057
2086
|
return false;
|
|
@@ -2094,7 +2123,7 @@ export class Orchestrator {
|
|
|
2094
2123
|
harness: adapter.id,
|
|
2095
2124
|
profileId: input.credentialProfileId ?? null,
|
|
2096
2125
|
priorPrompt: input.prompt,
|
|
2097
|
-
priorOutput: run.answerText ?? run.diff ?? "",
|
|
2126
|
+
priorOutput: run.answerText ?? run.persistedDiff ?? run.diff ?? "",
|
|
2098
2127
|
}));
|
|
2099
2128
|
// Reserve the continuation lease BEFORE any disclosure: a denied lease
|
|
2100
2129
|
// must never emit run.continuation (which claims a continuation
|
|
@@ -2130,11 +2159,7 @@ export class Orchestrator {
|
|
|
2130
2159
|
budgetStopped = true;
|
|
2131
2160
|
budgetDenial ??= denial;
|
|
2132
2161
|
},
|
|
2133
|
-
}, candidateAccess, (
|
|
2134
|
-
const safeEv = redactHarnessEvent(ev);
|
|
2135
|
-
safeInvoke(input.onHarnessEvent, safeEv);
|
|
2136
|
-
log.emit("harness.event", harnessEventPayload(adapter.id, contAttemptId, safeEv));
|
|
2137
|
-
}, input.signal, downgradeModel ?? undefined, input.effort, this.candidateIntent(input), log, effectiveWeb, this.interactionChannelFor(input, log, runId, taskId, contAttemptId, adapter.id, slot.routed.supportsInteractive), (streamedUsd) => {
|
|
2162
|
+
}, candidateAccess, input.onHarnessEvent, input.signal, downgradeModel ?? undefined, input.effort, this.candidateIntent(input), log, effectiveWeb, interactionChannelFor(input, log, runId, taskId, contAttemptId, adapter.id, slot.routed.supportsInteractive), (streamedUsd) => {
|
|
2138
2163
|
ledger.updateHold(contLeaseId, streamedUsd);
|
|
2139
2164
|
if (ledger.tier() !== "hard")
|
|
2140
2165
|
return false;
|
|
@@ -2221,7 +2246,7 @@ export class Orchestrator {
|
|
|
2221
2246
|
}
|
|
2222
2247
|
finally {
|
|
2223
2248
|
if (envelope)
|
|
2224
|
-
await
|
|
2249
|
+
await kept.settle(wsm, envelope, runsBySlot); // no leak on create/run error
|
|
2225
2250
|
}
|
|
2226
2251
|
};
|
|
2227
2252
|
await runParallelCandidates(slots, this.deps.runtimeConcurrencyCaps, runSlot);
|
|
@@ -2238,6 +2263,16 @@ export class Orchestrator {
|
|
|
2238
2263
|
mode,
|
|
2239
2264
|
ledger,
|
|
2240
2265
|
runs,
|
|
2266
|
+
git: input.workspaceKind !== "directory"
|
|
2267
|
+
? {
|
|
2268
|
+
live: input.inPlace === true &&
|
|
2269
|
+
requestedSingleCandidate &&
|
|
2270
|
+
runs[0]?.reviewCwd === execRoot,
|
|
2271
|
+
execRoot,
|
|
2272
|
+
preTurnSha,
|
|
2273
|
+
postTurnSha: earlyPostTurnSha,
|
|
2274
|
+
}
|
|
2275
|
+
: undefined,
|
|
2241
2276
|
signal: input.signal,
|
|
2242
2277
|
writeTelemetry: () => this.writeRunTelemetry(store, paths, contract, runId, taskId, mode, candidateRoster(runs), null),
|
|
2243
2278
|
});
|
|
@@ -2252,7 +2287,7 @@ export class Orchestrator {
|
|
|
2252
2287
|
input.workspaceKind !== "directory" &&
|
|
2253
2288
|
input.inPlace &&
|
|
2254
2289
|
requestedSingleCandidate &&
|
|
2255
|
-
runs.every((run) => !run.
|
|
2290
|
+
runs.every((run) => !run.captureRefusal)) {
|
|
2256
2291
|
try {
|
|
2257
2292
|
earlyPostTurnSha = await snapshotTree(execRoot);
|
|
2258
2293
|
}
|
|
@@ -2365,7 +2400,7 @@ export class Orchestrator {
|
|
|
2365
2400
|
catch (err) {
|
|
2366
2401
|
// Review preflight/evidence failures end TERMINALLY with artifacts —
|
|
2367
2402
|
// never as an escaped throw that orphans the run dir.
|
|
2368
|
-
return
|
|
2403
|
+
return failAfterCandidates({ log, store, paths, runId, taskId, mode, ledger, runs }, "review", err);
|
|
2369
2404
|
}
|
|
2370
2405
|
finally {
|
|
2371
2406
|
// Review preflight failures must not leak candidate worktrees.
|
|
@@ -2423,11 +2458,7 @@ export class Orchestrator {
|
|
|
2423
2458
|
budgetStopped = true;
|
|
2424
2459
|
budgetDenial ??= denial;
|
|
2425
2460
|
},
|
|
2426
|
-
}, candidateAccess, (
|
|
2427
|
-
const safeEv = redactHarnessEvent(ev);
|
|
2428
|
-
safeInvoke(input.onHarnessEvent, safeEv);
|
|
2429
|
-
log.emit("harness.event", harnessEventPayload(synthAdapter.id, "synth", safeEv));
|
|
2430
|
-
}, input.signal, undefined, input.effort, "synthesize", log, effectiveWeb, this.interactionChannelFor(input, log, runId, taskId, "synth", synthAdapter.id, synthRouted.supportsInteractive), undefined, input, false, synthesisInput.content, undefined, synthHome);
|
|
2461
|
+
}, 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);
|
|
2431
2462
|
ledger.settle(lease.lease?.lease_id ?? "", attemptUsageCostSettlement(run.cost, run.costEstimated, run.attemptId, run.harnessId, run.telemetry.authMode, run.telemetry.usageCost));
|
|
2432
2463
|
reviewEnvelopes.push(envelope);
|
|
2433
2464
|
envelope = undefined;
|
|
@@ -2474,7 +2505,7 @@ export class Orchestrator {
|
|
|
2474
2505
|
}
|
|
2475
2506
|
catch (err) {
|
|
2476
2507
|
// Arbitration throws end terminally with artifacts, never as an orphan.
|
|
2477
|
-
return
|
|
2508
|
+
return failAfterCandidates({ log, store, paths, runId, taskId, mode, ledger, runs }, "arbitration", err);
|
|
2478
2509
|
}
|
|
2479
2510
|
log.emit("arbitration.completed", {
|
|
2480
2511
|
winner: result.decision.winner,
|
|
@@ -2620,9 +2651,8 @@ export class Orchestrator {
|
|
|
2620
2651
|
});
|
|
2621
2652
|
}
|
|
2622
2653
|
else if (mutatingRun) {
|
|
2623
|
-
secretDiff.
|
|
2624
|
-
const patchSha256 =
|
|
2625
|
-
store.writeText(join(paths.finalDir, "patch.diff"), winnerRun.diff);
|
|
2654
|
+
const savedPatch = secretDiff.persistFinalPatch(store, paths.finalDir, winnerRun);
|
|
2655
|
+
const patchSha256 = savedPatch.patchSha256;
|
|
2626
2656
|
const wstats = diffStats(winnerRun.diff);
|
|
2627
2657
|
const hasDiff = winnerRun.diff.trim().length > 0;
|
|
2628
2658
|
const blockers = winnerEvidence
|
|
@@ -2718,7 +2748,7 @@ export class Orchestrator {
|
|
|
2718
2748
|
outcome_facts: facts,
|
|
2719
2749
|
review_verified: actualReviewVerified,
|
|
2720
2750
|
budget_stopped: budgetStopped,
|
|
2721
|
-
patch_sha256: patchSha256,
|
|
2751
|
+
...{ patch_sha256: patchSha256, ...savedPatch.meta },
|
|
2722
2752
|
result_kind: resultKind,
|
|
2723
2753
|
diffstat: {
|
|
2724
2754
|
files: wstats.paths.length,
|
|
@@ -2731,10 +2761,11 @@ export class Orchestrator {
|
|
|
2731
2761
|
pre_turn_sha: preTurnSha,
|
|
2732
2762
|
post_turn_sha: postTurnSha,
|
|
2733
2763
|
revert_anchor_id: revertAnchorId,
|
|
2764
|
+
execution_root: adopted === true ? execRoot : null,
|
|
2734
2765
|
},
|
|
2735
2766
|
});
|
|
2736
2767
|
}
|
|
2737
|
-
store.writeText(join(paths.finalDir, "summary.md"), renderSummary(runId, mode, { ...result.decision, facts }, evidences, synth.reason, actualReviewVerified));
|
|
2768
|
+
store.writeText(join(paths.finalDir, "summary.md"), renderSummary(runId, mode, { ...result.decision, facts }, evidences, synth.reason, actualReviewVerified, secretDiff.secretLikeSummaryLine(winnerRun.secretLike)));
|
|
2738
2769
|
// summary.md is a DIAGNOSTIC artifact only (V8/PLAN addendum 2): it no
|
|
2739
2770
|
// longer carries primary-output authority. A clean applyable success or a
|
|
2740
2771
|
// winner answer still marks it ready for legacy INV-116 ordering; any
|
|
@@ -2839,7 +2870,12 @@ export class Orchestrator {
|
|
|
2839
2870
|
log.emit("work_product.emitted", { winner: result.decision.winner });
|
|
2840
2871
|
}
|
|
2841
2872
|
if (!isFailureTerminal) {
|
|
2842
|
-
log.emit("run.completed", {
|
|
2873
|
+
log.emit("run.completed", {
|
|
2874
|
+
lifecycle: facts.lifecycle,
|
|
2875
|
+
facts,
|
|
2876
|
+
reason: facts.reason,
|
|
2877
|
+
...resumableTerminal(store, paths, winnerRun ? [winnerRun] : runs, isFailureTerminal),
|
|
2878
|
+
});
|
|
2843
2879
|
}
|
|
2844
2880
|
else if (facts.lifecycle === "succeeded") {
|
|
2845
2881
|
// needsDecision at terminal — the event's phase must agree with
|
|
@@ -2847,6 +2883,7 @@ export class Orchestrator {
|
|
|
2847
2883
|
log.emit("run.blocked", {
|
|
2848
2884
|
lifecycle: facts.lifecycle,
|
|
2849
2885
|
facts,
|
|
2886
|
+
...resumableTerminal(store, paths, runs, isFailureTerminal),
|
|
2850
2887
|
phase: deliveryFailureReason && !finalVerifyFailed
|
|
2851
2888
|
? "delivery"
|
|
2852
2889
|
: finalVerifyFailed
|
|
@@ -2862,6 +2899,7 @@ export class Orchestrator {
|
|
|
2862
2899
|
reason: facts.reason,
|
|
2863
2900
|
phase: "arbitration",
|
|
2864
2901
|
failure_ref: "final/failure.yaml",
|
|
2902
|
+
...resumableTerminal(store, paths, runs, isFailureTerminal),
|
|
2865
2903
|
});
|
|
2866
2904
|
}
|
|
2867
2905
|
return {
|
|
@@ -2910,6 +2948,7 @@ export class Orchestrator {
|
|
|
2910
2948
|
reviewScoped(input) {
|
|
2911
2949
|
return reviewCandidate({
|
|
2912
2950
|
...input,
|
|
2951
|
+
credentialObserverFactory: this.deps.credentialObserverFactory,
|
|
2913
2952
|
reviewerTimeoutMs: input.reviewerTimeoutMs ?? reviewerTimeoutMs(this.config(input.cwd)),
|
|
2914
2953
|
transientRetryPolicy: input.transientRetryPolicy ?? transientRetryPolicy(this.config(input.cwd)),
|
|
2915
2954
|
});
|
|
@@ -3222,11 +3261,7 @@ export class Orchestrator {
|
|
|
3222
3261
|
exhausted = true;
|
|
3223
3262
|
processingBudgetDenial ??= denial;
|
|
3224
3263
|
},
|
|
3225
|
-
}, convergenceAccess, (
|
|
3226
|
-
const safeEv = redactHarnessEvent(ev);
|
|
3227
|
-
safeInvoke(input.onHarnessEvent, safeEv);
|
|
3228
|
-
log.emit("harness.event", harnessEventPayload(adapter.id, attemptId, safeEv));
|
|
3229
|
-
}, input.signal, undefined, input.effort, "repair", log, effectiveWeb, this.interactionChannelFor(input, log, runId, taskId, attemptId, adapter.id, routed.supportsInteractive), (streamedUsd) => {
|
|
3264
|
+
}, convergenceAccess, input.onHarnessEvent, input.signal, undefined, input.effort, "repair", log, effectiveWeb, interactionChannelFor(input, log, runId, taskId, attemptId, adapter.id, routed.supportsInteractive), (streamedUsd) => {
|
|
3230
3265
|
ledger.updateHold(lease.lease?.lease_id ?? "", streamedUsd);
|
|
3231
3266
|
return ledger.tier() === "hard";
|
|
3232
3267
|
}, input, true, // convergence runs one candidate: live deltas on (W-C4)
|
|
@@ -3279,7 +3314,7 @@ export class Orchestrator {
|
|
|
3279
3314
|
observedPaths: [...observedPaths],
|
|
3280
3315
|
});
|
|
3281
3316
|
run.files = captured.files;
|
|
3282
|
-
run.
|
|
3317
|
+
run.captureRefusal = captured.refusal;
|
|
3283
3318
|
}
|
|
3284
3319
|
}
|
|
3285
3320
|
lastRun = run;
|
|
@@ -3287,6 +3322,18 @@ export class Orchestrator {
|
|
|
3287
3322
|
// per attempt, so the proof is spent per attempt.
|
|
3288
3323
|
assertDelegatedEvidence(input.delegated === true, convergenceAccess, [run]);
|
|
3289
3324
|
attemptTelemetries.push({ attemptId, harnessId: adapter.id, telemetry: run.telemetry });
|
|
3325
|
+
lastPostTurnSha = null;
|
|
3326
|
+
// Post-mutation fence for in-place: snapshot the live tree NOW (after the
|
|
3327
|
+
// harness mutated it, before this attempt's review). The last attempt's
|
|
3328
|
+
// value is the revert target persisted into work_product.yaml.
|
|
3329
|
+
if (input.inPlace === true && input.workspaceKind !== "directory" && !run.captureRefusal) {
|
|
3330
|
+
try {
|
|
3331
|
+
lastPostTurnSha = await snapshotTree(execRoot);
|
|
3332
|
+
}
|
|
3333
|
+
catch {
|
|
3334
|
+
lastPostTurnSha = null;
|
|
3335
|
+
}
|
|
3336
|
+
}
|
|
3290
3337
|
// Cancellation/deadline keeps priority over a belt failure finalized concurrently.
|
|
3291
3338
|
if (input.signal?.aborted || processingBudgetDenial)
|
|
3292
3339
|
break;
|
|
@@ -3319,7 +3366,7 @@ export class Orchestrator {
|
|
|
3319
3366
|
this.writeRunTelemetry(store, paths, contract, runId, taskId, mode, attemptTelemetries, null);
|
|
3320
3367
|
return failTerminally(log, store, paths, runId, taskId, mode, failure.phase, failure.error, ledger.spend(), failure.metadata);
|
|
3321
3368
|
}
|
|
3322
|
-
if (input.workspaceKind === "directory" && run.
|
|
3369
|
+
if (input.workspaceKind === "directory" && run.captureRefusal) {
|
|
3323
3370
|
return failedCandidatesResult({
|
|
3324
3371
|
ledger,
|
|
3325
3372
|
mode,
|
|
@@ -3338,17 +3385,6 @@ export class Orchestrator {
|
|
|
3338
3385
|
interrupted = true;
|
|
3339
3386
|
break;
|
|
3340
3387
|
}
|
|
3341
|
-
// Post-mutation fence for in-place: snapshot the live tree NOW (after the
|
|
3342
|
-
// harness mutated it, before this attempt's review). The last attempt's
|
|
3343
|
-
// value is the revert target persisted into work_product.yaml.
|
|
3344
|
-
if (input.inPlace === true && input.workspaceKind !== "directory") {
|
|
3345
|
-
try {
|
|
3346
|
-
lastPostTurnSha = await snapshotTree(execRoot);
|
|
3347
|
-
}
|
|
3348
|
-
catch {
|
|
3349
|
-
lastPostTurnSha = null;
|
|
3350
|
-
}
|
|
3351
|
-
}
|
|
3352
3388
|
// The review round is wrapped so a preflight/revalidation throw ends the
|
|
3353
3389
|
// run TERMINALLY with artifacts instead of orphaning the run dir.
|
|
3354
3390
|
let conv;
|
|
@@ -3399,7 +3435,7 @@ export class Orchestrator {
|
|
|
3399
3435
|
facts: makeOutcomeFacts("failed", { noChanges: run.files.noChanges }),
|
|
3400
3436
|
log,
|
|
3401
3437
|
});
|
|
3402
|
-
return
|
|
3438
|
+
return failAfterCandidates({ log, store, paths, runId, taskId, mode, ledger, runs: [run] }, "review", err);
|
|
3403
3439
|
}
|
|
3404
3440
|
if (conv.converged) {
|
|
3405
3441
|
converged = true;
|
|
@@ -3590,15 +3626,10 @@ export class Orchestrator {
|
|
|
3590
3626
|
facts,
|
|
3591
3627
|
log,
|
|
3592
3628
|
});
|
|
3593
|
-
if (lastRun.answerText) {
|
|
3594
|
-
store.writeText(join(paths.finalDir, "answer.md"), lastRun.answerText);
|
|
3595
|
-
log.emit("output.ready", { kind: "answer", path: "final/answer.md" });
|
|
3596
|
-
}
|
|
3597
3629
|
}
|
|
3598
3630
|
else {
|
|
3599
|
-
secretDiff.
|
|
3600
|
-
const patchSha256 =
|
|
3601
|
-
store.writeText(join(paths.finalDir, "patch.diff"), lastRun.diff);
|
|
3631
|
+
const savedPatch = secretDiff.persistFinalPatch(store, paths.finalDir, lastRun);
|
|
3632
|
+
const patchSha256 = savedPatch.patchSha256;
|
|
3602
3633
|
// Honest apply-state (parity with runRace single-candidate in-place): a
|
|
3603
3634
|
// convergence run with inPlace mutated the live tree directly across its
|
|
3604
3635
|
// attempts, so it is "applied" even when review blocked (Revert offered).
|
|
@@ -3625,16 +3656,22 @@ export class Orchestrator {
|
|
|
3625
3656
|
lifecycle: facts.lifecycle,
|
|
3626
3657
|
outcome_facts: facts,
|
|
3627
3658
|
review_verified: actualReviewVerified,
|
|
3628
|
-
patch_sha256: patchSha256,
|
|
3659
|
+
...{ patch_sha256: patchSha256, ...savedPatch.meta },
|
|
3629
3660
|
adopted: convAdopted,
|
|
3630
3661
|
apply_state: convApplyState,
|
|
3631
3662
|
pre_turn_sha: convAdopted === true ? preTurnSha : null,
|
|
3632
3663
|
post_turn_sha: convAdopted === true ? lastPostTurnSha : null,
|
|
3633
3664
|
revert_anchor_id: revertAnchorId,
|
|
3665
|
+
execution_root: convAdopted === true ? execRoot : null,
|
|
3634
3666
|
},
|
|
3635
3667
|
});
|
|
3636
3668
|
}
|
|
3637
|
-
|
|
3669
|
+
// INV-062: the answer is kept for every convergence result, patch or files.
|
|
3670
|
+
if (lastRun.answerText) {
|
|
3671
|
+
store.writeText(join(paths.finalDir, "answer.md"), lastRun.answerText);
|
|
3672
|
+
log.emit("output.ready", { kind: "answer", path: "final/answer.md" });
|
|
3673
|
+
}
|
|
3674
|
+
store.writeText(join(paths.finalDir, "summary.md"), `# 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.summaryDisclosure(lastRun.secretLike)}\n`);
|
|
3638
3675
|
// Lifecycle invariant (all modes): output.ready precedes the terminal
|
|
3639
3676
|
// event so a client that applied the terminal event has the output.
|
|
3640
3677
|
log.emit("output.ready", {
|
|
@@ -3646,6 +3683,7 @@ export class Orchestrator {
|
|
|
3646
3683
|
// A needs-decision terminal (review blocked / checks failed) writes a
|
|
3647
3684
|
// failure record + fires run.blocked even though the lifecycle succeeded.
|
|
3648
3685
|
const convIsFailureTerminal = facts.lifecycle !== "succeeded" || convNeedsDecision;
|
|
3686
|
+
const continuation = resumableTerminal(store, paths, lastRun ? [lastRun] : [], convIsFailureTerminal);
|
|
3649
3687
|
if (convIsFailureTerminal) {
|
|
3650
3688
|
// A5 last-result lift: the LAST attempt's typed mid-attempt refusal
|
|
3651
3689
|
// survives onto the terminal; budget/cancel/needs-decision outrank it.
|
|
@@ -3721,6 +3759,7 @@ export class Orchestrator {
|
|
|
3721
3759
|
}
|
|
3722
3760
|
if (!convIsFailureTerminal) {
|
|
3723
3761
|
log.emit("run.completed", {
|
|
3762
|
+
...continuation,
|
|
3724
3763
|
lifecycle: facts.lifecycle,
|
|
3725
3764
|
facts,
|
|
3726
3765
|
reason: facts.reason,
|
|
@@ -3729,6 +3768,7 @@ export class Orchestrator {
|
|
|
3729
3768
|
}
|
|
3730
3769
|
else if (facts.lifecycle === "succeeded") {
|
|
3731
3770
|
log.emit("run.blocked", {
|
|
3771
|
+
...continuation,
|
|
3732
3772
|
lifecycle: facts.lifecycle,
|
|
3733
3773
|
facts,
|
|
3734
3774
|
attempts: attempt,
|
|
@@ -3738,6 +3778,7 @@ export class Orchestrator {
|
|
|
3738
3778
|
}
|
|
3739
3779
|
else {
|
|
3740
3780
|
log.emit("run.failed", {
|
|
3781
|
+
...continuation,
|
|
3741
3782
|
lifecycle: facts.lifecycle,
|
|
3742
3783
|
facts,
|
|
3743
3784
|
reason: facts.reason,
|
|
@@ -3816,7 +3857,7 @@ export class Orchestrator {
|
|
|
3816
3857
|
spec.extra["abortSignal"] = input.signal
|
|
3817
3858
|
? AbortSignal.any([input.signal, plannerAbort.signal])
|
|
3818
3859
|
: plannerAbort.signal;
|
|
3819
|
-
const planInteraction =
|
|
3860
|
+
const planInteraction = interactionChannelFor(input, log, runId, taskId, attemptId, adapter.id, routed.supportsInteractive);
|
|
3820
3861
|
if (planInteraction)
|
|
3821
3862
|
spec.extra["interactionChannel"] = planInteraction;
|
|
3822
3863
|
const planWorkMode = applyWorkEnvelope(spec, this.workReportEnvelopeFor(routed, contract, Boolean(planInteraction)));
|
|
@@ -4409,18 +4450,14 @@ export class Orchestrator {
|
|
|
4409
4450
|
: null) ?? roHome.env,
|
|
4410
4451
|
});
|
|
4411
4452
|
this.credentials.stampProfileSelection(spec, input, adapter.id);
|
|
4412
|
-
const
|
|
4413
|
-
spec.extra["abortSignal"] = input.signal
|
|
4414
|
-
? AbortSignal.any([input.signal, reportAbort.signal])
|
|
4415
|
-
: reportAbort.signal;
|
|
4416
|
-
const reportInteraction = this.interactionChannelFor(input, log, runId, taskId, attemptId, adapter.id, routed.supportsInteractive);
|
|
4453
|
+
const reportInteraction = interactionChannelFor(input, log, runId, taskId, attemptId, adapter.id, routed.supportsInteractive);
|
|
4417
4454
|
if (reportInteraction)
|
|
4418
4455
|
spec.extra["interactionChannel"] = reportInteraction;
|
|
4419
4456
|
// D-16: compile the WorkReport envelope for the read-only lane.
|
|
4420
4457
|
const readonlyWorkEnvelope = this.workReportEnvelopeFor(routed, contract, Boolean(reportInteraction));
|
|
4421
4458
|
const readonlyWorkMode = applyWorkEnvelope(spec, readonlyWorkEnvelope);
|
|
4422
4459
|
const attemptEventsPath = join(paths.attemptsDir, attemptId, "events.jsonl");
|
|
4423
|
-
const answer = new
|
|
4460
|
+
const answer = new secretDiff.CountedAnswerAssembly();
|
|
4424
4461
|
const telemetry = createAttemptTelemetry(knobs.webPolicy, contract.external_context.web_required, effectiveWeb, [],
|
|
4425
4462
|
// Requested-model capture so ask/audit route receipts detect a silent
|
|
4426
4463
|
// model downgrade (typed model_mismatch), not just agent runs.
|
|
@@ -4428,7 +4465,6 @@ export class Orchestrator {
|
|
|
4428
4465
|
return {
|
|
4429
4466
|
knobs,
|
|
4430
4467
|
spec,
|
|
4431
|
-
reportAbort,
|
|
4432
4468
|
reportInteraction,
|
|
4433
4469
|
readonlyWorkMode,
|
|
4434
4470
|
attemptEventsPath,
|
|
@@ -4470,13 +4506,20 @@ export class Orchestrator {
|
|
|
4470
4506
|
}
|
|
4471
4507
|
return { status: "launched" };
|
|
4472
4508
|
}
|
|
4473
|
-
const { knobs, spec: preparedSpec,
|
|
4509
|
+
const { knobs, spec: preparedSpec, reportInteraction, readonlyWorkMode, attemptEventsPath, telemetry, } = preparation.value;
|
|
4474
4510
|
let { answer } = preparation.value;
|
|
4475
4511
|
let spec = preparedSpec;
|
|
4476
4512
|
bindProcessingAdmission(spec, ledger, lease.lease, adapter.id, attemptId, undefined, processingAdmission);
|
|
4477
4513
|
const retryPolicy = transientRetryPolicy(this.config(input.repoRoot));
|
|
4478
4514
|
let activeSessionId = spec.session_id;
|
|
4479
4515
|
const live = liveAttempt(input, routed, paths, contract, attemptId, () => activeSessionId);
|
|
4516
|
+
let continuity;
|
|
4517
|
+
({ continuity, spec } = await openContinuity(this.continuityDeps(adapter, routed, contract, attemptId, paths, input, log, {
|
|
4518
|
+
envelope: null,
|
|
4519
|
+
harnessHome: null,
|
|
4520
|
+
workOrder: input.prompt,
|
|
4521
|
+
firstPrompt: spec.prompt,
|
|
4522
|
+
}), { spec, continuation: input.continuation, store, telemetry }));
|
|
4480
4523
|
const onAbort = () => void adapter.cancel?.(activeSessionId)?.catch(() => { });
|
|
4481
4524
|
if (input.signal) {
|
|
4482
4525
|
if (input.signal.aborted)
|
|
@@ -4491,12 +4534,11 @@ export class Orchestrator {
|
|
|
4491
4534
|
let processingRefusal = null;
|
|
4492
4535
|
let poolExhausted = null; // A5: typed pool-exhausted refusal
|
|
4493
4536
|
try {
|
|
4494
|
-
const triedProfiles = new Set(); // W5.4 failover: each profile at most once
|
|
4495
4537
|
for (let nativeTry = 0; !input.signal?.aborted; nativeTry += 1) {
|
|
4496
4538
|
// A3 per-try isolation (candidate-lane parity): neither a failed try's
|
|
4497
4539
|
// output nor its progress markers leak into the next try's evidence.
|
|
4498
4540
|
if (nativeTry > 0) {
|
|
4499
|
-
answer = new
|
|
4541
|
+
answer = new secretDiff.CountedAnswerAssembly();
|
|
4500
4542
|
telemetry.outputMarkers = newAttemptOutputMarkers();
|
|
4501
4543
|
}
|
|
4502
4544
|
const runSpec = nativeTry === 0
|
|
@@ -4504,10 +4546,14 @@ export class Orchestrator {
|
|
|
4504
4546
|
: HarnessRunSpec.parse({
|
|
4505
4547
|
...spec,
|
|
4506
4548
|
session_id: newId("ses"),
|
|
4507
|
-
resume_session_id: null,
|
|
4508
4549
|
extra: { ...spec.extra },
|
|
4509
4550
|
});
|
|
4510
4551
|
activeSessionId = runSpec.session_id;
|
|
4552
|
+
const tryAbort = new AbortController();
|
|
4553
|
+
runSpec.extra["abortSignal"] = input.signal
|
|
4554
|
+
? AbortSignal.any([input.signal, tryAbort.signal])
|
|
4555
|
+
: tryAbort.signal;
|
|
4556
|
+
continuity.beginTry(runSpec, nativeTry, tryAbort);
|
|
4511
4557
|
// #363: bound before the spawn — a credential change mid-try voids it.
|
|
4512
4558
|
const refusal = this.credentials.bindPreProgressRefusal(adapter.id, runSpec);
|
|
4513
4559
|
const transientStart = telemetry.transientFailures.length;
|
|
@@ -4525,29 +4571,29 @@ export class Orchestrator {
|
|
|
4525
4571
|
timeoutMs: harnessInactivityTimeoutMs(this.config(input.repoRoot)),
|
|
4526
4572
|
countsAsProgress: countsAsAgentProgress,
|
|
4527
4573
|
onTimeout: () => {
|
|
4528
|
-
|
|
4574
|
+
tryAbort.abort();
|
|
4529
4575
|
void adapter.cancel?.(activeSessionId)?.catch(() => { });
|
|
4530
4576
|
},
|
|
4531
4577
|
isSuspended: () => (reportInteraction?.pendingCount?.() ?? 0) > 0,
|
|
4532
4578
|
suspensionVersion: () => reportInteraction?.suspensionVersion?.() ?? 0,
|
|
4533
4579
|
});
|
|
4534
4580
|
for await (const ev of watchedReport) {
|
|
4535
|
-
if (input.signal?.aborted)
|
|
4536
|
-
break;
|
|
4537
4581
|
const safeEv = redactHarnessEvent(ev);
|
|
4538
|
-
|
|
4539
|
-
|
|
4540
|
-
// session lives in the DURABLE per-lane home, so record it for the
|
|
4541
|
-
// next lane turn's resume. The read-only fallback chain is
|
|
4542
|
-
// sequential (never the parallel deep-scan swarm, which is
|
|
4543
|
-
// excluded from `laneRun`), so recordSession's upsert keeps the
|
|
4544
|
-
// latest lane session without a race.
|
|
4545
|
-
if (laneRun)
|
|
4546
|
-
observeNativeSessionEvent(input, adapter.id, safeEv);
|
|
4582
|
+
if (safeEv.payload?.["buffered"] !== true)
|
|
4583
|
+
safeInvoke(input.onHarnessEvent, safeEv);
|
|
4547
4584
|
observeAuthSwitch(log, adapter.id, attemptId, safeEv);
|
|
4548
|
-
log.emit("harness.event", harnessEventPayload(adapter.id, attemptId, safeEv));
|
|
4585
|
+
log.emit("harness.event", harnessEventPayload(adapter.id, attemptId, safeEv), safeEv.payload?.["buffered"] !== true);
|
|
4549
4586
|
appendLine(attemptEventsPath, JSON.stringify(safeEv));
|
|
4587
|
+
if (input.signal?.aborted)
|
|
4588
|
+
break;
|
|
4550
4589
|
observeAttemptTelemetry(telemetry, safeEv);
|
|
4590
|
+
const mismatch = continuity.observe(safeEv, runSpec, telemetry.outputMarkers);
|
|
4591
|
+
if (mismatch) {
|
|
4592
|
+
harnessError = mismatch;
|
|
4593
|
+
break;
|
|
4594
|
+
}
|
|
4595
|
+
if (laneRun && continuity.mayPublishSession)
|
|
4596
|
+
observeNativeSessionEvent(input, adapter.id, safeEv);
|
|
4551
4597
|
emitPlanProgress((t, p) => log.emit(t, p), adapter.id, attemptId, safeEv);
|
|
4552
4598
|
// read-only routes burn quota too (the orchestrate PLANNER is
|
|
4553
4599
|
// the loudest) — same single owner as the agent loop.
|
|
@@ -4561,12 +4607,12 @@ export class Orchestrator {
|
|
|
4561
4607
|
budgetStopped = streamBudgetDenied = true;
|
|
4562
4608
|
budgetDenial ??= streamDenial;
|
|
4563
4609
|
harnessError = streamDenial.reason;
|
|
4564
|
-
|
|
4610
|
+
tryAbort.abort();
|
|
4565
4611
|
void adapter.cancel?.(activeSessionId)?.catch(() => { });
|
|
4566
4612
|
break;
|
|
4567
4613
|
}
|
|
4568
4614
|
// A TYPED final message wins verbatim over joined narration.
|
|
4569
|
-
answer.
|
|
4615
|
+
answer.observeCounted(ev, safeEv);
|
|
4570
4616
|
if (safeEv.type === "error")
|
|
4571
4617
|
harnessError = safeEv.error
|
|
4572
4618
|
? redactSecrets(safeEv.error)
|
|
@@ -4594,62 +4640,34 @@ export class Orchestrator {
|
|
|
4594
4640
|
const reportSoFar = answer.text();
|
|
4595
4641
|
const delivered = !harnessError && reportSoFar.length > 0 && !input.signal?.aborted;
|
|
4596
4642
|
this.credentials.noteTryServed(refusal, telemetry.outputMarkers, delivered);
|
|
4597
|
-
|
|
4598
|
-
|
|
4599
|
-
|
|
4600
|
-
|
|
4601
|
-
|
|
4602
|
-
|
|
4603
|
-
|
|
4604
|
-
|
|
4605
|
-
|
|
4606
|
-
|
|
4607
|
-
|
|
4608
|
-
|
|
4609
|
-
|
|
4610
|
-
|
|
4611
|
-
|
|
4612
|
-
|
|
4613
|
-
|
|
4614
|
-
|
|
4615
|
-
|
|
4616
|
-
|
|
4617
|
-
|
|
4618
|
-
|
|
4619
|
-
emit: (type, payload) => log.emit(type, payload),
|
|
4620
|
-
newSessionId: () => newId("ses"),
|
|
4621
|
-
defaultRouteWasVendorNative: routed.authRouteEstimate === "local_session",
|
|
4622
|
-
// D-U6: an explicit pin never rotates; pool-selected rows do.
|
|
4623
|
-
pinned: input.credentialProfileId != null,
|
|
4624
|
-
});
|
|
4625
|
-
// A5 ordering: same as the candidate lane — terminalize typed
|
|
4626
|
-
// before the transient gate burns same-profile retries. The pool
|
|
4627
|
-
// refusal APPENDS to the original harness error (candidate-lane
|
|
4628
|
-
// parity: errors.push) instead of erasing the true failure.
|
|
4629
|
-
if (rotated && "poolExhausted" in rotated) {
|
|
4630
|
-
poolExhausted = rotated.poolExhausted;
|
|
4631
|
-
harnessError = harnessError
|
|
4632
|
-
? `${harnessError}; ${safeErrorMessage(poolExhausted)}`
|
|
4633
|
-
: safeErrorMessage(poolExhausted);
|
|
4634
|
-
break;
|
|
4635
|
-
}
|
|
4636
|
-
if (rotated) {
|
|
4637
|
-
// INV-137: the rotated row's session must land in ITS OWN lane
|
|
4638
|
-
// home — never the previous row's lane store.
|
|
4639
|
-
spec = rotatedSpecInLaneHome(spec, rotated, (id) => this.laneHomeEnvFor(input, adapter.id, id), input.credentialProfileId ?? null);
|
|
4640
|
-
harnessError = null;
|
|
4641
|
-
continue;
|
|
4642
|
-
}
|
|
4643
|
+
const verdict = await continuity.afterTry({
|
|
4644
|
+
runSpec,
|
|
4645
|
+
nativeTry,
|
|
4646
|
+
harnessErrored: harnessError !== null,
|
|
4647
|
+
aborted: input.signal?.aborted === true,
|
|
4648
|
+
requestRefused: Boolean(telemetry.requestRefusal),
|
|
4649
|
+
newTransients,
|
|
4650
|
+
sawTypedLimit,
|
|
4651
|
+
sawRetryable,
|
|
4652
|
+
answer,
|
|
4653
|
+
rawDeliverableEmpty: reportSoFar.length === 0,
|
|
4654
|
+
markers: telemetry.outputMarkers,
|
|
4655
|
+
lastLimit: telemetry.rateLimits.at(-1) ?? null,
|
|
4656
|
+
refusal,
|
|
4657
|
+
telemetry,
|
|
4658
|
+
});
|
|
4659
|
+
if (verdict.kind === "terminal") {
|
|
4660
|
+
poolExhausted = verdict.error;
|
|
4661
|
+
harnessError = harnessError
|
|
4662
|
+
? `${harnessError}; ${safeErrorMessage(poolExhausted)}`
|
|
4663
|
+
: safeErrorMessage(poolExhausted);
|
|
4664
|
+
break;
|
|
4643
4665
|
}
|
|
4644
|
-
if (
|
|
4645
|
-
!sawRetryable ||
|
|
4646
|
-
reportSoFar.length > 0 ||
|
|
4647
|
-
nativeTry >= retryPolicy.maxRetries ||
|
|
4648
|
-
input.signal?.aborted)
|
|
4666
|
+
if (verdict.kind === "break")
|
|
4649
4667
|
break;
|
|
4650
|
-
|
|
4668
|
+
spec = verdict.spec;
|
|
4651
4669
|
harnessError = null;
|
|
4652
|
-
await sleep(delayMs);
|
|
4670
|
+
await sleep(verdict.delayMs);
|
|
4653
4671
|
}
|
|
4654
4672
|
}
|
|
4655
4673
|
finally {
|
|
@@ -4691,6 +4709,7 @@ export class Orchestrator {
|
|
|
4691
4709
|
workReport: roUnwrapped.workReport,
|
|
4692
4710
|
workReportSource: roUnwrapped.source,
|
|
4693
4711
|
workReportViolation: roUnwrapped.contractViolation,
|
|
4712
|
+
workReportUnverified: roUnwrapped.unverified ?? null,
|
|
4694
4713
|
contextTerminalExhausted: telemetry.contextExhausted,
|
|
4695
4714
|
});
|
|
4696
4715
|
// A broken WorkReport contract is a hard failure ONLY when the finalizer
|
|
@@ -4713,6 +4732,13 @@ export class Orchestrator {
|
|
|
4713
4732
|
// interruptions keep the strict deliverable requirement.
|
|
4714
4733
|
emptyDeliverableAllowed: roFinalized.outcomeClass === "clean",
|
|
4715
4734
|
});
|
|
4735
|
+
const roResumable = await continuity.finish({
|
|
4736
|
+
runSpec: spec,
|
|
4737
|
+
errored: harnessError !== null,
|
|
4738
|
+
aborted: input.signal?.aborted === true,
|
|
4739
|
+
cancelReason: typeof input.signal?.reason === "string" ? input.signal.reason : null,
|
|
4740
|
+
workState: roFinalized.workState,
|
|
4741
|
+
});
|
|
4716
4742
|
if (harnessError) {
|
|
4717
4743
|
log.emit("harness.completed", {
|
|
4718
4744
|
harness_id: adapter.id,
|
|
@@ -4733,6 +4759,7 @@ export class Orchestrator {
|
|
|
4733
4759
|
: poolExhausted || processingRefusal
|
|
4734
4760
|
? { declaredFailure: declaredFailure(processingRefusal ?? poolExhausted) }
|
|
4735
4761
|
: {}),
|
|
4762
|
+
...continuity.terminalFacts(roResumable),
|
|
4736
4763
|
});
|
|
4737
4764
|
if (opts.deepScan) {
|
|
4738
4765
|
store.writeText(join(paths.findingsDir, `${attemptId}-error.md`), `# Explorer ${attemptId} failed\n\n${harnessError}\n`);
|
|
@@ -4753,9 +4780,11 @@ export class Orchestrator {
|
|
|
4753
4780
|
harnessId: adapter.id,
|
|
4754
4781
|
status: scoutInterrupted ? "failed" : "success",
|
|
4755
4782
|
report: report || "(no output)",
|
|
4783
|
+
answerMatches: answer.secretLikeMatches(),
|
|
4756
4784
|
error: scoutInterrupted ? "context capacity exhausted before the scout completed" : null,
|
|
4757
4785
|
telemetry,
|
|
4758
4786
|
...(scoutInterrupted ? { interrupted: true } : {}),
|
|
4787
|
+
...continuity.terminalFacts(roResumable),
|
|
4759
4788
|
});
|
|
4760
4789
|
if (opts.deepScan) {
|
|
4761
4790
|
const warningNote = toolWarnings(telemetry).length
|
|
@@ -4917,7 +4946,7 @@ export class Orchestrator {
|
|
|
4917
4946
|
harnessId: a.harnessId,
|
|
4918
4947
|
status: a.status,
|
|
4919
4948
|
}));
|
|
4920
|
-
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);
|
|
4949
|
+
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(attempts, "cancelled"));
|
|
4921
4950
|
if (input.signal?.aborted)
|
|
4922
4951
|
return cancelledTerminal();
|
|
4923
4952
|
const succeededReadonly = attempts.filter((a) => a.status === "success");
|
|
@@ -4943,14 +4972,6 @@ export class Orchestrator {
|
|
|
4943
4972
|
fallbackOpen = false;
|
|
4944
4973
|
}
|
|
4945
4974
|
const partialReport = [...attempts].reverse().find((a) => a.report)?.report ?? "";
|
|
4946
|
-
if (partialReport) {
|
|
4947
|
-
store.writeText(join(paths.finalDir, opts.artifactName), `# ${opts.title}\n\n> Unverified partial output. The run is ${webBlocked ? "blocked" : "failed"} because a required/attempted tool failed.\n\n${partialReport}\n`);
|
|
4948
|
-
log.emit("output.ready", {
|
|
4949
|
-
kind: opts.mode === "ask" ? "answer" : "report",
|
|
4950
|
-
path: `final/${opts.artifactName}`,
|
|
4951
|
-
state: "diagnostic",
|
|
4952
|
-
});
|
|
4953
|
-
}
|
|
4954
4975
|
this.writeRunTelemetry(store, paths, contract, runId, taskId, opts.mode, attemptTelemetries, null);
|
|
4955
4976
|
store.writeText(join(paths.contextDir, "context_error.md"), `# ${budgetMapping ? "Budget Denied" : "Harness Error"}\n\n${singleError}\n`);
|
|
4956
4977
|
const roEventRefs = attempts.map((a) => `attempts/${a.attemptId}/events.jsonl`);
|
|
@@ -5002,6 +5023,7 @@ export class Orchestrator {
|
|
|
5002
5023
|
harness_id: terminalHarnessId,
|
|
5003
5024
|
error: singleError,
|
|
5004
5025
|
failure_ref: "final/failure.yaml",
|
|
5026
|
+
...resumableTerminal(store, paths, attempts, true),
|
|
5005
5027
|
});
|
|
5006
5028
|
}
|
|
5007
5029
|
else {
|
|
@@ -5013,6 +5035,7 @@ export class Orchestrator {
|
|
|
5013
5035
|
harness_id: terminalHarnessId,
|
|
5014
5036
|
error: singleError,
|
|
5015
5037
|
failure_ref: "final/failure.yaml",
|
|
5038
|
+
...resumableTerminal(store, paths, attempts, true),
|
|
5016
5039
|
});
|
|
5017
5040
|
}
|
|
5018
5041
|
return {
|
|
@@ -5086,6 +5109,7 @@ export class Orchestrator {
|
|
|
5086
5109
|
phase: scanBudgetMapping ? scanBudgetMapping.phase : "harness",
|
|
5087
5110
|
error: message,
|
|
5088
5111
|
failure_ref: "final/failure.yaml",
|
|
5112
|
+
...resumableTerminal(store, paths, attempts, true),
|
|
5089
5113
|
});
|
|
5090
5114
|
return {
|
|
5091
5115
|
spendUsd: ledger.spend(),
|
|
@@ -5112,8 +5136,9 @@ export class Orchestrator {
|
|
|
5112
5136
|
// whole decision + reducer spawn lives in deepScanReducer.ts (its owner).
|
|
5113
5137
|
let deepScanSynthesis = null;
|
|
5114
5138
|
let reducedReport = null;
|
|
5139
|
+
let answerMatches = succeeded.reduce((sum, a) => sum + (a.answerMatches ?? 0), 0);
|
|
5115
5140
|
if (opts.deepScan) {
|
|
5116
|
-
({ deepScanSynthesis, reducedReport } = await resolveDeepScanSynthesis(this.deepScanReducerDeps(input, contract, log), {
|
|
5141
|
+
({ deepScanSynthesis, reducedReport, answerMatches } = await resolveDeepScanSynthesis(this.deepScanReducerDeps(input, contract, log), {
|
|
5117
5142
|
succeeded,
|
|
5118
5143
|
adapters,
|
|
5119
5144
|
budgetStopped,
|
|
@@ -5205,7 +5230,8 @@ export class Orchestrator {
|
|
|
5205
5230
|
const harnessLabel = attempts
|
|
5206
5231
|
.map((a) => `${a.attemptId}:${a.harnessId}:${a.status}`)
|
|
5207
5232
|
.join(", ");
|
|
5208
|
-
|
|
5233
|
+
const secretLike = secretDiff.answerSecretLikeFinding(answerMatches);
|
|
5234
|
+
store.writeText(join(paths.finalDir, "summary.md"), `# Run ${runId} (${opts.mode})\n\n- Harnesses: ${harnessLabel}\n- Lifecycle: ${terminalFacts.lifecycle}${terminalFacts.reason ? ` (${terminalFacts.reason})` : ""}${secretDiff.summaryDisclosure(secretLike)}\n\n${report}\n`);
|
|
5209
5235
|
const reportProducerAttemptId = opts.deepScan &&
|
|
5210
5236
|
deepScanSynthesis?.status === "succeeded" &&
|
|
5211
5237
|
deepScanSynthesis.reducer_attempt_id
|
|
@@ -5222,6 +5248,7 @@ export class Orchestrator {
|
|
|
5222
5248
|
mode: opts.mode,
|
|
5223
5249
|
intent: opts.intent,
|
|
5224
5250
|
read_only: true,
|
|
5251
|
+
...(secretLike ? { secret_like: secretLike } : {}),
|
|
5225
5252
|
},
|
|
5226
5253
|
});
|
|
5227
5254
|
log.emit("work_product.emitted", { kind: "report", winner: reportProducerAttemptId });
|
|
@@ -5243,6 +5270,7 @@ export class Orchestrator {
|
|
|
5243
5270
|
reason: terminalFacts.reason,
|
|
5244
5271
|
phase: "executor",
|
|
5245
5272
|
failure_ref: "final/failure.yaml",
|
|
5273
|
+
...resumableTerminal(store, paths, attempts, true),
|
|
5246
5274
|
});
|
|
5247
5275
|
}
|
|
5248
5276
|
else if (workVetoed) {
|
|
@@ -5250,6 +5278,7 @@ export class Orchestrator {
|
|
|
5250
5278
|
lifecycle: terminalFacts.lifecycle,
|
|
5251
5279
|
facts: terminalFacts,
|
|
5252
5280
|
reason: terminalFacts.reason,
|
|
5281
|
+
...resumableTerminal(store, paths, attempts, true),
|
|
5253
5282
|
});
|
|
5254
5283
|
}
|
|
5255
5284
|
else {
|
|
@@ -5257,6 +5286,7 @@ export class Orchestrator {
|
|
|
5257
5286
|
lifecycle: terminalFacts.lifecycle,
|
|
5258
5287
|
facts: terminalFacts,
|
|
5259
5288
|
reason: terminalFacts.reason,
|
|
5289
|
+
...resumableTerminal(store, paths, attempts),
|
|
5260
5290
|
});
|
|
5261
5291
|
}
|
|
5262
5292
|
return {
|