@claudexor/orchestrator 3.20.1 → 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/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 +4 -0
- package/dist/candidateTerminals.d.ts.map +1 -1
- package/dist/candidateTerminals.js +18 -7
- package/dist/candidateTerminals.js.map +1 -1
- package/dist/candidateWorkProduct.d.ts.map +1 -1
- package/dist/candidateWorkProduct.js +6 -6
- package/dist/candidateWorkProduct.js.map +1 -1
- 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-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-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.map +1 -1
- package/dist/credential-cooldown.js +8 -3
- package/dist/credential-cooldown.js.map +1 -1
- package/dist/credential-differential.d.ts +1 -0
- package/dist/credential-differential.d.ts.map +1 -1
- package/dist/credential-differential.js +32 -4
- package/dist/credential-differential.js.map +1 -1
- package/dist/deepScanReducer.d.ts +4 -1
- package/dist/deepScanReducer.d.ts.map +1 -1
- package/dist/deepScanReducer.js +10 -5
- 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 +25 -24
- 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/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/modelGovernance.d.ts +4 -0
- package/dist/modelGovernance.d.ts.map +1 -1
- package/dist/modelGovernance.js +27 -1
- package/dist/modelGovernance.js.map +1 -1
- package/dist/orchestrator-credentials.d.ts +1 -1
- package/dist/orchestrator-credentials.d.ts.map +1 -1
- package/dist/orchestrator-credentials.js +4 -3
- package/dist/orchestrator-credentials.js.map +1 -1
- package/dist/orchestrator.d.ts +4 -4
- package/dist/orchestrator.d.ts.map +1 -1
- package/dist/orchestrator.js +252 -226
- 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 +2 -28
- package/dist/planRun.d.ts.map +1 -1
- package/dist/planRun.js +5 -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 +9 -4
- 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/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/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 +8 -1
- package/dist/runTerminalResults.js.map +1 -1
- package/dist/runTerminals.d.ts.map +1 -1
- package/dist/runTerminals.js +6 -5
- 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) {
|
|
@@ -659,7 +661,6 @@ export class Orchestrator {
|
|
|
659
661
|
requiresFullAccess: manifest.capability_profile.mcp_injection_requires_full_access,
|
|
660
662
|
fullAccess: isFullAccess(requiredAccess),
|
|
661
663
|
}),
|
|
662
|
-
effortLevels: manifest.capabilities.effort_levels,
|
|
663
664
|
knownModels: manifest.capabilities.known_models,
|
|
664
665
|
modelInventory: manifest.capabilities,
|
|
665
666
|
// A selected profile's credential_kind IS the route (round-18 #2);
|
|
@@ -819,7 +820,7 @@ export class Orchestrator {
|
|
|
819
820
|
// settles clean), so a schema run keeps its interaction channel — since
|
|
820
821
|
// the daemon always arms one, the refusal was denying claude every
|
|
821
822
|
// daemon/CLI structured-output run. The WorkReport side_tool envelope
|
|
822
|
-
// stays interactive-gated separately (
|
|
823
|
+
// stays interactive-gated separately (workReportEnvelope) as a deliberate
|
|
823
824
|
// scope choice; the caller schema itself rides regardless.
|
|
824
825
|
if (input.outputSchema !== undefined && input.outputSchema !== null) {
|
|
825
826
|
const incapable = out.filter((lane) => !lane.supportsJsonSchemaOutput);
|
|
@@ -1141,16 +1142,14 @@ export class Orchestrator {
|
|
|
1141
1142
|
// per-harness map, which beats the per-harness settings default. There is
|
|
1142
1143
|
// no run-global model.
|
|
1143
1144
|
const model = overrideModel ?? contract.routing_models[routed.adapter.id] ?? s?.defaultModel ?? null;
|
|
1144
|
-
//
|
|
1145
|
-
//
|
|
1146
|
-
//
|
|
1147
|
-
//
|
|
1148
|
-
//
|
|
1149
|
-
//
|
|
1150
|
-
|
|
1151
|
-
const effort =
|
|
1152
|
-
if (governed.ignored)
|
|
1153
|
-
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;
|
|
1154
1153
|
return {
|
|
1155
1154
|
model,
|
|
1156
1155
|
effort,
|
|
@@ -1385,17 +1384,17 @@ export class Orchestrator {
|
|
|
1385
1384
|
let applied = appliedNow();
|
|
1386
1385
|
const attemptStartedMs = Date.now();
|
|
1387
1386
|
const budgetSignalState = { quotaPressureDisclosed: false };
|
|
1388
|
-
const triedProfiles = new Set(); // W5.4 failover: each profile at most once
|
|
1389
1387
|
let cost = 0;
|
|
1390
1388
|
let costEstimated = false;
|
|
1391
1389
|
let harnessErrored = false;
|
|
1390
|
+
let streamBudgetDenied = false;
|
|
1392
1391
|
let processingRefusal = null;
|
|
1393
1392
|
let poolExhausted = null; // A5: typed pool-exhausted refusal
|
|
1394
1393
|
const deltaFlood = { count: 0, disclosed: false }; // W-C4 per-attempt delta budget
|
|
1395
1394
|
// QA-024: emit the belt-failure disclosure event at most once per attempt.
|
|
1396
1395
|
let beltFailureDisclosed = false;
|
|
1397
1396
|
const errors = [];
|
|
1398
|
-
let answer = new
|
|
1397
|
+
let answer = new secretDiff.CountedAnswerAssembly();
|
|
1399
1398
|
const retryPolicy = transientRetryPolicy(this.config(contract.repo.root));
|
|
1400
1399
|
// QA-024: the delegation belt is the ONLY engine-owned extra MCP server
|
|
1401
1400
|
// injected into an agent lane (the browser MCP rides its own field), so its
|
|
@@ -1416,28 +1415,31 @@ export class Orchestrator {
|
|
|
1416
1415
|
onAbort();
|
|
1417
1416
|
else
|
|
1418
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 }));
|
|
1419
1425
|
try {
|
|
1420
1426
|
for (let nativeTry = 0; !signal?.aborted; nativeTry += 1) {
|
|
1421
1427
|
// A3 per-try isolation: neither output nor progress markers leak across tries.
|
|
1422
1428
|
if (nativeTry > 0) {
|
|
1423
|
-
answer = new
|
|
1429
|
+
answer = new secretDiff.CountedAnswerAssembly();
|
|
1424
1430
|
telemetry.outputMarkers = newAttemptOutputMarkers();
|
|
1425
1431
|
}
|
|
1426
1432
|
const clearFileBackedContext = stageFileBackedContext(envelope.worktree_path, fileBackedContext);
|
|
1427
1433
|
const runSpec = nativeTry === 0
|
|
1428
1434
|
? spec
|
|
1429
1435
|
: HarnessRunSpec.parse({ ...spec, session_id: newId("ses"), extra: { ...spec.extra } });
|
|
1430
|
-
//
|
|
1431
|
-
// stream (killing the process group through the existing abort
|
|
1432
|
-
// plumbing) without touching the run-level cancel signal, so a timeout
|
|
1433
|
-
// and a user cancel stay distinguishable. Recreated per
|
|
1434
|
-
// nativeTry — a transient retry must get a LIVE signal, not the
|
|
1435
|
-
// previous try's already-aborted composite.
|
|
1436
|
+
// The watchdog aborts only this try; run cancellation also prevents successors.
|
|
1436
1437
|
const attemptAbort = new AbortController();
|
|
1437
1438
|
runSpec.extra["abortSignal"] = signal
|
|
1438
1439
|
? AbortSignal.any([signal, attemptAbort.signal])
|
|
1439
1440
|
: attemptAbort.signal;
|
|
1440
1441
|
activeSessionId = runSpec.session_id;
|
|
1442
|
+
continuity.beginTry(runSpec, nativeTry, attemptAbort);
|
|
1441
1443
|
// #363: bound before the spawn — a credential change mid-try voids it.
|
|
1442
1444
|
const refusal = this.credentials.bindPreProgressRefusal(adapter.id, runSpec);
|
|
1443
1445
|
const transientStart = telemetry.transientFailures.length;
|
|
@@ -1471,15 +1473,16 @@ export class Orchestrator {
|
|
|
1471
1473
|
break;
|
|
1472
1474
|
if (display)
|
|
1473
1475
|
safeInvoke(onHarnessEvent, safeEv);
|
|
1474
|
-
// In-place turns run in the live tree under the native environment, so
|
|
1475
|
-
// the session they emit IS reachable for the next turn: record it. An
|
|
1476
|
-
// ISOLATED envelope-born session lives in the scoped home that dispose()
|
|
1477
|
-
// deletes, so observing it would poison the thread resume map with
|
|
1478
|
-
// unreachable ids — skip it there.
|
|
1479
|
-
if (inPlaceEnvelope)
|
|
1480
|
-
observeNativeSessionEvent(runInput, adapter.id, safeEv);
|
|
1481
1476
|
observeAuthSwitch(log, adapter.id, attemptId, safeEv);
|
|
1482
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);
|
|
1483
1486
|
// QA-024: the injected delegation belt's MCP server reported a
|
|
1484
1487
|
// terminal startup failure. Disclose it ONCE while live; recoverable
|
|
1485
1488
|
// exact tool-result failures are evaluated at attempt finalization.
|
|
@@ -1512,7 +1515,7 @@ export class Orchestrator {
|
|
|
1512
1515
|
cost = usage.cost;
|
|
1513
1516
|
costEstimated = usage.costEstimated;
|
|
1514
1517
|
if (usage.hardCapReached) {
|
|
1515
|
-
harnessErrored = true;
|
|
1518
|
+
harnessErrored = streamBudgetDenied = true;
|
|
1516
1519
|
errors.push("budget hard cap reached mid-attempt; stream aborted");
|
|
1517
1520
|
break;
|
|
1518
1521
|
}
|
|
@@ -1523,7 +1526,7 @@ export class Orchestrator {
|
|
|
1523
1526
|
}
|
|
1524
1527
|
// Capture assistant prose so an answer-only turn (no file changes) still
|
|
1525
1528
|
// has an honest output artifact; a TYPED final message wins verbatim.
|
|
1526
|
-
answer.
|
|
1529
|
+
answer.observeCounted(ev, safeEv);
|
|
1527
1530
|
// Observe ALL budget/quota signals (one codex usage event carries
|
|
1528
1531
|
// BOTH spend and quota); pressure disclosed once per attempt.
|
|
1529
1532
|
observeBudgetSignals(ledger, log, adapter.id, attemptId, safeEv, budgetSignalState);
|
|
@@ -1571,64 +1574,37 @@ export class Orchestrator {
|
|
|
1571
1574
|
const deliverableEmpty = workspaceUnchanged && answer.text().length === 0;
|
|
1572
1575
|
const delivered = !harnessErrored && !deliverableEmpty && !signal?.aborted;
|
|
1573
1576
|
this.credentials.noteTryServed(refusal, telemetry.outputMarkers, delivered);
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
lastLimit: telemetry.rateLimits.at(-1) ?? null,
|
|
1598
|
-
emit: (type, payload) => log?.emit(type, payload),
|
|
1599
|
-
newSessionId: () => newId("ses"),
|
|
1600
|
-
defaultRouteWasVendorNative: routed.authRouteEstimate === "local_session",
|
|
1601
|
-
// D-U6: an explicit pin never rotates; pool-selected rows do.
|
|
1602
|
-
pinned: runInput.credentialProfileId != null,
|
|
1603
|
-
});
|
|
1604
|
-
// A5 ordering: an exhausted pool terminalizes TYPED here, BEFORE the
|
|
1605
|
-
// transient gate below burns same-profile retries (limits classify
|
|
1606
|
-
// as retryable) on the already-refused subject.
|
|
1607
|
-
if (rotated && "poolExhausted" in rotated) {
|
|
1608
|
-
poolExhausted = rotated.poolExhausted;
|
|
1609
|
-
errors.push(safeErrorMessage(poolExhausted));
|
|
1610
|
-
break;
|
|
1611
|
-
}
|
|
1612
|
-
if (rotated) {
|
|
1613
|
-
// INV-137: the rotated row runs in ITS OWN lane home (no-op for
|
|
1614
|
-
// isolated envelopes and native-env in-place turns).
|
|
1615
|
-
spec = rotatedSpecInLaneHome(spec, rotated, (id) => this.laneHomeEnvFor(runInput, adapter.id, id), runInput.credentialProfileId ?? null);
|
|
1616
|
-
applied = appliedNow();
|
|
1617
|
-
errors.length = 0;
|
|
1618
|
-
harnessErrored = false;
|
|
1619
|
-
continue;
|
|
1620
|
-
}
|
|
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;
|
|
1621
1600
|
}
|
|
1622
|
-
if (
|
|
1623
|
-
!sawRetryable ||
|
|
1624
|
-
!deliverableEmpty ||
|
|
1625
|
-
nativeTry >= retryPolicy.maxRetries ||
|
|
1626
|
-
signal?.aborted)
|
|
1601
|
+
if (verdict.kind === "break")
|
|
1627
1602
|
break;
|
|
1628
|
-
|
|
1603
|
+
spec = verdict.spec;
|
|
1604
|
+
applied = appliedNow();
|
|
1629
1605
|
errors.length = 0;
|
|
1630
1606
|
harnessErrored = false;
|
|
1631
|
-
await sleep(delayMs);
|
|
1607
|
+
await sleep(verdict.delayMs);
|
|
1632
1608
|
}
|
|
1633
1609
|
}
|
|
1634
1610
|
finally {
|
|
@@ -1648,19 +1624,23 @@ export class Orchestrator {
|
|
|
1648
1624
|
// with no typed final contributes no answer material (acceptedTryOutput).
|
|
1649
1625
|
const unwrapped = unwrapWorkReportEnvelope(acceptedTryOutput(answer, harnessErrored), workReportMode, { sideToolReport: telemetry.sideToolWorkReport ?? undefined });
|
|
1650
1626
|
const redacted = redactSecrets(unwrapped.deliverable);
|
|
1651
|
-
|
|
1627
|
+
// INV-062: the (already redacted) answer is kept whatever the patch holds.
|
|
1628
|
+
const answerText = redacted.trim().length > 0 ? redacted : undefined;
|
|
1652
1629
|
if (directory)
|
|
1653
1630
|
await captureDirectory();
|
|
1654
|
-
const
|
|
1655
|
-
? { diff: "",
|
|
1656
|
-
: await secretDiff.
|
|
1657
|
-
|
|
1658
|
-
|
|
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);
|
|
1659
1639
|
const deliverableEvidence = diff.trim().length > 0 || Boolean(answerText) || directoryHasOutput(directoryCapture.files);
|
|
1660
1640
|
// Cancelled attempts skip gates: running a 600s-per-gate suite delays the ack
|
|
1661
1641
|
// and burns compute on a result nobody will adopt. Diff/attempt.yaml
|
|
1662
1642
|
// still land, so partial work stays inspectable.
|
|
1663
|
-
const gateSignalAborted = signal?.aborted === true ||
|
|
1643
|
+
const gateSignalAborted = signal?.aborted === true || captureRefusal !== undefined;
|
|
1664
1644
|
if (!gateSignalAborted) {
|
|
1665
1645
|
log?.emit("gate.started", {
|
|
1666
1646
|
attempt_id: attemptId,
|
|
@@ -1707,6 +1687,7 @@ export class Orchestrator {
|
|
|
1707
1687
|
workReport: unwrapped.workReport,
|
|
1708
1688
|
workReportSource: unwrapped.source,
|
|
1709
1689
|
workReportViolation: unwrapped.contractViolation,
|
|
1690
|
+
workReportUnverified: unwrapped.unverified ?? null,
|
|
1710
1691
|
contextTerminalExhausted: telemetry.contextExhausted,
|
|
1711
1692
|
});
|
|
1712
1693
|
harnessErrored = finalized.harnessErrored;
|
|
@@ -1714,6 +1695,14 @@ export class Orchestrator {
|
|
|
1714
1695
|
errors.push(`work_report contract: ${unwrapped.contractViolation}`);
|
|
1715
1696
|
const deliverablePresent = finalized.deliverablePresent;
|
|
1716
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
|
+
});
|
|
1717
1706
|
setAttemptOutcome(telemetry, {
|
|
1718
1707
|
deliverablePresent,
|
|
1719
1708
|
gatesPassed: gates.length > 0 ? gatesPassed(gates) : null,
|
|
@@ -1734,9 +1723,9 @@ export class Orchestrator {
|
|
|
1734
1723
|
attemptDir,
|
|
1735
1724
|
worktreePath: envelope.worktree_path,
|
|
1736
1725
|
artifactRelativeDir,
|
|
1737
|
-
diff,
|
|
1738
|
-
persistPatch: !directory &&
|
|
1739
|
-
persistProducedMedia: !directory &&
|
|
1726
|
+
...{ diff, persistedDiff: captured.persistedDiff },
|
|
1727
|
+
persistPatch: !directory && captureRefusal === undefined && isMutatingAccess(access),
|
|
1728
|
+
persistProducedMedia: !directory && captureRefusal === undefined,
|
|
1740
1729
|
answerText,
|
|
1741
1730
|
record: {
|
|
1742
1731
|
attempt_id: attemptId,
|
|
@@ -1748,7 +1737,7 @@ export class Orchestrator {
|
|
|
1748
1737
|
errors: errors.slice(0, 5),
|
|
1749
1738
|
...telemetrySummary(telemetry),
|
|
1750
1739
|
outcome: telemetry.outcome,
|
|
1751
|
-
...(
|
|
1740
|
+
...secretDiff.attemptDisclosure(captured),
|
|
1752
1741
|
gates: gates.map((g) => ({ id: g.id, status: g.status })),
|
|
1753
1742
|
branch: envelope.branch_name,
|
|
1754
1743
|
...(directoryCapture.files
|
|
@@ -1780,7 +1769,7 @@ export class Orchestrator {
|
|
|
1780
1769
|
attemptId,
|
|
1781
1770
|
harnessId: adapter.id,
|
|
1782
1771
|
label,
|
|
1783
|
-
|
|
1772
|
+
...captured,
|
|
1784
1773
|
...(directoryCapture.files ? { files: directoryCapture.files } : {}),
|
|
1785
1774
|
answerText,
|
|
1786
1775
|
reviewCwd: envelope.worktree_path,
|
|
@@ -1792,7 +1781,6 @@ export class Orchestrator {
|
|
|
1792
1781
|
costEstimated,
|
|
1793
1782
|
errors: errors.slice(0, 8),
|
|
1794
1783
|
telemetry,
|
|
1795
|
-
...(secretDiffRefusal ? { secretDiffRefusal } : {}),
|
|
1796
1784
|
// A5: the typed refusal survives NORMAL attempt finalization (no throw).
|
|
1797
1785
|
...(telemetry.requestRefusal
|
|
1798
1786
|
? { declaredFailure: requestRefusalFailure(telemetry.requestRefusal) }
|
|
@@ -1801,14 +1789,47 @@ export class Orchestrator {
|
|
|
1801
1789
|
: {}),
|
|
1802
1790
|
outcomeClass: finalized.outcomeClass,
|
|
1803
1791
|
applied,
|
|
1792
|
+
...continuity.terminalFacts(resumable),
|
|
1804
1793
|
};
|
|
1805
1794
|
}
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
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
|
+
};
|
|
1812
1833
|
}
|
|
1813
1834
|
async runRace(input, mode, announce) {
|
|
1814
1835
|
const taskId = input.taskId ?? newId("task");
|
|
@@ -1928,7 +1949,7 @@ export class Orchestrator {
|
|
|
1928
1949
|
const disposeReviewEnvelopes = async () => {
|
|
1929
1950
|
const envelopes = reviewEnvelopes.splice(0);
|
|
1930
1951
|
for (const env of envelopes)
|
|
1931
|
-
await
|
|
1952
|
+
await kept.settle(wsm, env, runsBySlot);
|
|
1932
1953
|
};
|
|
1933
1954
|
const candidateAccess = contract.access.effective_profile;
|
|
1934
1955
|
let budgetStopped = false;
|
|
@@ -1937,6 +1958,7 @@ export class Orchestrator {
|
|
|
1937
1958
|
let budgetDenial = null;
|
|
1938
1959
|
let softWarned = false;
|
|
1939
1960
|
const requestedSingleCandidate = adapters.length === 1;
|
|
1961
|
+
const kept = custody.forRun(input, requestedSingleCandidate && mutatingRun, runId, paths, log);
|
|
1940
1962
|
const slots = [];
|
|
1941
1963
|
for (let i = 0; i < adapters.length; i++) {
|
|
1942
1964
|
const routed = adapters[i];
|
|
@@ -2036,7 +2058,7 @@ export class Orchestrator {
|
|
|
2036
2058
|
external_context_policy: knobs.webPolicy,
|
|
2037
2059
|
...(knobs.ignored.length > 0 ? { ignored_settings: knobs.ignored } : {}),
|
|
2038
2060
|
});
|
|
2039
|
-
envelope = await
|
|
2061
|
+
envelope = await kept.envelope(wsm, {
|
|
2040
2062
|
taskId,
|
|
2041
2063
|
attemptId: slot.attemptId,
|
|
2042
2064
|
baseRef: contract.repo.base_ref,
|
|
@@ -2058,7 +2080,7 @@ export class Orchestrator {
|
|
|
2058
2080
|
budgetStopped = true;
|
|
2059
2081
|
budgetDenial ??= denial;
|
|
2060
2082
|
},
|
|
2061
|
-
}, candidateAccess, input.onHarnessEvent, input.signal, downgradeModel ?? undefined, input.effort, this.candidateIntent(input), log, effectiveWeb,
|
|
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) => {
|
|
2062
2084
|
ledger.updateHold(slot.lease.lease_id, streamedUsd);
|
|
2063
2085
|
if (ledger.tier() !== "hard")
|
|
2064
2086
|
return false;
|
|
@@ -2101,7 +2123,7 @@ export class Orchestrator {
|
|
|
2101
2123
|
harness: adapter.id,
|
|
2102
2124
|
profileId: input.credentialProfileId ?? null,
|
|
2103
2125
|
priorPrompt: input.prompt,
|
|
2104
|
-
priorOutput: run.answerText ?? run.diff ?? "",
|
|
2126
|
+
priorOutput: run.answerText ?? run.persistedDiff ?? run.diff ?? "",
|
|
2105
2127
|
}));
|
|
2106
2128
|
// Reserve the continuation lease BEFORE any disclosure: a denied lease
|
|
2107
2129
|
// must never emit run.continuation (which claims a continuation
|
|
@@ -2137,7 +2159,7 @@ export class Orchestrator {
|
|
|
2137
2159
|
budgetStopped = true;
|
|
2138
2160
|
budgetDenial ??= denial;
|
|
2139
2161
|
},
|
|
2140
|
-
}, candidateAccess, input.onHarnessEvent, input.signal, downgradeModel ?? undefined, input.effort, this.candidateIntent(input), log, effectiveWeb,
|
|
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) => {
|
|
2141
2163
|
ledger.updateHold(contLeaseId, streamedUsd);
|
|
2142
2164
|
if (ledger.tier() !== "hard")
|
|
2143
2165
|
return false;
|
|
@@ -2224,7 +2246,7 @@ export class Orchestrator {
|
|
|
2224
2246
|
}
|
|
2225
2247
|
finally {
|
|
2226
2248
|
if (envelope)
|
|
2227
|
-
await
|
|
2249
|
+
await kept.settle(wsm, envelope, runsBySlot); // no leak on create/run error
|
|
2228
2250
|
}
|
|
2229
2251
|
};
|
|
2230
2252
|
await runParallelCandidates(slots, this.deps.runtimeConcurrencyCaps, runSlot);
|
|
@@ -2265,7 +2287,7 @@ export class Orchestrator {
|
|
|
2265
2287
|
input.workspaceKind !== "directory" &&
|
|
2266
2288
|
input.inPlace &&
|
|
2267
2289
|
requestedSingleCandidate &&
|
|
2268
|
-
runs.every((run) => !run.
|
|
2290
|
+
runs.every((run) => !run.captureRefusal)) {
|
|
2269
2291
|
try {
|
|
2270
2292
|
earlyPostTurnSha = await snapshotTree(execRoot);
|
|
2271
2293
|
}
|
|
@@ -2378,7 +2400,7 @@ export class Orchestrator {
|
|
|
2378
2400
|
catch (err) {
|
|
2379
2401
|
// Review preflight/evidence failures end TERMINALLY with artifacts —
|
|
2380
2402
|
// never as an escaped throw that orphans the run dir.
|
|
2381
|
-
return
|
|
2403
|
+
return failAfterCandidates({ log, store, paths, runId, taskId, mode, ledger, runs }, "review", err);
|
|
2382
2404
|
}
|
|
2383
2405
|
finally {
|
|
2384
2406
|
// Review preflight failures must not leak candidate worktrees.
|
|
@@ -2436,7 +2458,7 @@ export class Orchestrator {
|
|
|
2436
2458
|
budgetStopped = true;
|
|
2437
2459
|
budgetDenial ??= denial;
|
|
2438
2460
|
},
|
|
2439
|
-
}, candidateAccess, input.onHarnessEvent, input.signal, undefined, input.effort, "synthesize", log, effectiveWeb,
|
|
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);
|
|
2440
2462
|
ledger.settle(lease.lease?.lease_id ?? "", attemptUsageCostSettlement(run.cost, run.costEstimated, run.attemptId, run.harnessId, run.telemetry.authMode, run.telemetry.usageCost));
|
|
2441
2463
|
reviewEnvelopes.push(envelope);
|
|
2442
2464
|
envelope = undefined;
|
|
@@ -2483,7 +2505,7 @@ export class Orchestrator {
|
|
|
2483
2505
|
}
|
|
2484
2506
|
catch (err) {
|
|
2485
2507
|
// Arbitration throws end terminally with artifacts, never as an orphan.
|
|
2486
|
-
return
|
|
2508
|
+
return failAfterCandidates({ log, store, paths, runId, taskId, mode, ledger, runs }, "arbitration", err);
|
|
2487
2509
|
}
|
|
2488
2510
|
log.emit("arbitration.completed", {
|
|
2489
2511
|
winner: result.decision.winner,
|
|
@@ -2629,9 +2651,8 @@ export class Orchestrator {
|
|
|
2629
2651
|
});
|
|
2630
2652
|
}
|
|
2631
2653
|
else if (mutatingRun) {
|
|
2632
|
-
secretDiff.
|
|
2633
|
-
const patchSha256 =
|
|
2634
|
-
store.writeText(join(paths.finalDir, "patch.diff"), winnerRun.diff);
|
|
2654
|
+
const savedPatch = secretDiff.persistFinalPatch(store, paths.finalDir, winnerRun);
|
|
2655
|
+
const patchSha256 = savedPatch.patchSha256;
|
|
2635
2656
|
const wstats = diffStats(winnerRun.diff);
|
|
2636
2657
|
const hasDiff = winnerRun.diff.trim().length > 0;
|
|
2637
2658
|
const blockers = winnerEvidence
|
|
@@ -2727,7 +2748,7 @@ export class Orchestrator {
|
|
|
2727
2748
|
outcome_facts: facts,
|
|
2728
2749
|
review_verified: actualReviewVerified,
|
|
2729
2750
|
budget_stopped: budgetStopped,
|
|
2730
|
-
patch_sha256: patchSha256,
|
|
2751
|
+
...{ patch_sha256: patchSha256, ...savedPatch.meta },
|
|
2731
2752
|
result_kind: resultKind,
|
|
2732
2753
|
diffstat: {
|
|
2733
2754
|
files: wstats.paths.length,
|
|
@@ -2744,7 +2765,7 @@ export class Orchestrator {
|
|
|
2744
2765
|
},
|
|
2745
2766
|
});
|
|
2746
2767
|
}
|
|
2747
|
-
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)));
|
|
2748
2769
|
// summary.md is a DIAGNOSTIC artifact only (V8/PLAN addendum 2): it no
|
|
2749
2770
|
// longer carries primary-output authority. A clean applyable success or a
|
|
2750
2771
|
// winner answer still marks it ready for legacy INV-116 ordering; any
|
|
@@ -2849,7 +2870,12 @@ export class Orchestrator {
|
|
|
2849
2870
|
log.emit("work_product.emitted", { winner: result.decision.winner });
|
|
2850
2871
|
}
|
|
2851
2872
|
if (!isFailureTerminal) {
|
|
2852
|
-
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
|
+
});
|
|
2853
2879
|
}
|
|
2854
2880
|
else if (facts.lifecycle === "succeeded") {
|
|
2855
2881
|
// needsDecision at terminal — the event's phase must agree with
|
|
@@ -2857,6 +2883,7 @@ export class Orchestrator {
|
|
|
2857
2883
|
log.emit("run.blocked", {
|
|
2858
2884
|
lifecycle: facts.lifecycle,
|
|
2859
2885
|
facts,
|
|
2886
|
+
...resumableTerminal(store, paths, runs, isFailureTerminal),
|
|
2860
2887
|
phase: deliveryFailureReason && !finalVerifyFailed
|
|
2861
2888
|
? "delivery"
|
|
2862
2889
|
: finalVerifyFailed
|
|
@@ -2872,6 +2899,7 @@ export class Orchestrator {
|
|
|
2872
2899
|
reason: facts.reason,
|
|
2873
2900
|
phase: "arbitration",
|
|
2874
2901
|
failure_ref: "final/failure.yaml",
|
|
2902
|
+
...resumableTerminal(store, paths, runs, isFailureTerminal),
|
|
2875
2903
|
});
|
|
2876
2904
|
}
|
|
2877
2905
|
return {
|
|
@@ -3233,7 +3261,7 @@ export class Orchestrator {
|
|
|
3233
3261
|
exhausted = true;
|
|
3234
3262
|
processingBudgetDenial ??= denial;
|
|
3235
3263
|
},
|
|
3236
|
-
}, convergenceAccess, input.onHarnessEvent, input.signal, undefined, input.effort, "repair", log, effectiveWeb,
|
|
3264
|
+
}, convergenceAccess, input.onHarnessEvent, input.signal, undefined, input.effort, "repair", log, effectiveWeb, interactionChannelFor(input, log, runId, taskId, attemptId, adapter.id, routed.supportsInteractive), (streamedUsd) => {
|
|
3237
3265
|
ledger.updateHold(lease.lease?.lease_id ?? "", streamedUsd);
|
|
3238
3266
|
return ledger.tier() === "hard";
|
|
3239
3267
|
}, input, true, // convergence runs one candidate: live deltas on (W-C4)
|
|
@@ -3286,7 +3314,7 @@ export class Orchestrator {
|
|
|
3286
3314
|
observedPaths: [...observedPaths],
|
|
3287
3315
|
});
|
|
3288
3316
|
run.files = captured.files;
|
|
3289
|
-
run.
|
|
3317
|
+
run.captureRefusal = captured.refusal;
|
|
3290
3318
|
}
|
|
3291
3319
|
}
|
|
3292
3320
|
lastRun = run;
|
|
@@ -3298,9 +3326,7 @@ export class Orchestrator {
|
|
|
3298
3326
|
// Post-mutation fence for in-place: snapshot the live tree NOW (after the
|
|
3299
3327
|
// harness mutated it, before this attempt's review). The last attempt's
|
|
3300
3328
|
// value is the revert target persisted into work_product.yaml.
|
|
3301
|
-
if (input.inPlace === true &&
|
|
3302
|
-
input.workspaceKind !== "directory" &&
|
|
3303
|
-
!run.secretDiffRefusal) {
|
|
3329
|
+
if (input.inPlace === true && input.workspaceKind !== "directory" && !run.captureRefusal) {
|
|
3304
3330
|
try {
|
|
3305
3331
|
lastPostTurnSha = await snapshotTree(execRoot);
|
|
3306
3332
|
}
|
|
@@ -3340,7 +3366,7 @@ export class Orchestrator {
|
|
|
3340
3366
|
this.writeRunTelemetry(store, paths, contract, runId, taskId, mode, attemptTelemetries, null);
|
|
3341
3367
|
return failTerminally(log, store, paths, runId, taskId, mode, failure.phase, failure.error, ledger.spend(), failure.metadata);
|
|
3342
3368
|
}
|
|
3343
|
-
if (input.workspaceKind === "directory" && run.
|
|
3369
|
+
if (input.workspaceKind === "directory" && run.captureRefusal) {
|
|
3344
3370
|
return failedCandidatesResult({
|
|
3345
3371
|
ledger,
|
|
3346
3372
|
mode,
|
|
@@ -3409,7 +3435,7 @@ export class Orchestrator {
|
|
|
3409
3435
|
facts: makeOutcomeFacts("failed", { noChanges: run.files.noChanges }),
|
|
3410
3436
|
log,
|
|
3411
3437
|
});
|
|
3412
|
-
return
|
|
3438
|
+
return failAfterCandidates({ log, store, paths, runId, taskId, mode, ledger, runs: [run] }, "review", err);
|
|
3413
3439
|
}
|
|
3414
3440
|
if (conv.converged) {
|
|
3415
3441
|
converged = true;
|
|
@@ -3600,15 +3626,10 @@ export class Orchestrator {
|
|
|
3600
3626
|
facts,
|
|
3601
3627
|
log,
|
|
3602
3628
|
});
|
|
3603
|
-
if (lastRun.answerText) {
|
|
3604
|
-
store.writeText(join(paths.finalDir, "answer.md"), lastRun.answerText);
|
|
3605
|
-
log.emit("output.ready", { kind: "answer", path: "final/answer.md" });
|
|
3606
|
-
}
|
|
3607
3629
|
}
|
|
3608
3630
|
else {
|
|
3609
|
-
secretDiff.
|
|
3610
|
-
const patchSha256 =
|
|
3611
|
-
store.writeText(join(paths.finalDir, "patch.diff"), lastRun.diff);
|
|
3631
|
+
const savedPatch = secretDiff.persistFinalPatch(store, paths.finalDir, lastRun);
|
|
3632
|
+
const patchSha256 = savedPatch.patchSha256;
|
|
3612
3633
|
// Honest apply-state (parity with runRace single-candidate in-place): a
|
|
3613
3634
|
// convergence run with inPlace mutated the live tree directly across its
|
|
3614
3635
|
// attempts, so it is "applied" even when review blocked (Revert offered).
|
|
@@ -3635,7 +3656,7 @@ export class Orchestrator {
|
|
|
3635
3656
|
lifecycle: facts.lifecycle,
|
|
3636
3657
|
outcome_facts: facts,
|
|
3637
3658
|
review_verified: actualReviewVerified,
|
|
3638
|
-
patch_sha256: patchSha256,
|
|
3659
|
+
...{ patch_sha256: patchSha256, ...savedPatch.meta },
|
|
3639
3660
|
adopted: convAdopted,
|
|
3640
3661
|
apply_state: convApplyState,
|
|
3641
3662
|
pre_turn_sha: convAdopted === true ? preTurnSha : null,
|
|
@@ -3645,7 +3666,12 @@ export class Orchestrator {
|
|
|
3645
3666
|
},
|
|
3646
3667
|
});
|
|
3647
3668
|
}
|
|
3648
|
-
|
|
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`);
|
|
3649
3675
|
// Lifecycle invariant (all modes): output.ready precedes the terminal
|
|
3650
3676
|
// event so a client that applied the terminal event has the output.
|
|
3651
3677
|
log.emit("output.ready", {
|
|
@@ -3657,6 +3683,7 @@ export class Orchestrator {
|
|
|
3657
3683
|
// A needs-decision terminal (review blocked / checks failed) writes a
|
|
3658
3684
|
// failure record + fires run.blocked even though the lifecycle succeeded.
|
|
3659
3685
|
const convIsFailureTerminal = facts.lifecycle !== "succeeded" || convNeedsDecision;
|
|
3686
|
+
const continuation = resumableTerminal(store, paths, lastRun ? [lastRun] : [], convIsFailureTerminal);
|
|
3660
3687
|
if (convIsFailureTerminal) {
|
|
3661
3688
|
// A5 last-result lift: the LAST attempt's typed mid-attempt refusal
|
|
3662
3689
|
// survives onto the terminal; budget/cancel/needs-decision outrank it.
|
|
@@ -3732,6 +3759,7 @@ export class Orchestrator {
|
|
|
3732
3759
|
}
|
|
3733
3760
|
if (!convIsFailureTerminal) {
|
|
3734
3761
|
log.emit("run.completed", {
|
|
3762
|
+
...continuation,
|
|
3735
3763
|
lifecycle: facts.lifecycle,
|
|
3736
3764
|
facts,
|
|
3737
3765
|
reason: facts.reason,
|
|
@@ -3740,6 +3768,7 @@ export class Orchestrator {
|
|
|
3740
3768
|
}
|
|
3741
3769
|
else if (facts.lifecycle === "succeeded") {
|
|
3742
3770
|
log.emit("run.blocked", {
|
|
3771
|
+
...continuation,
|
|
3743
3772
|
lifecycle: facts.lifecycle,
|
|
3744
3773
|
facts,
|
|
3745
3774
|
attempts: attempt,
|
|
@@ -3749,6 +3778,7 @@ export class Orchestrator {
|
|
|
3749
3778
|
}
|
|
3750
3779
|
else {
|
|
3751
3780
|
log.emit("run.failed", {
|
|
3781
|
+
...continuation,
|
|
3752
3782
|
lifecycle: facts.lifecycle,
|
|
3753
3783
|
facts,
|
|
3754
3784
|
reason: facts.reason,
|
|
@@ -3827,7 +3857,7 @@ export class Orchestrator {
|
|
|
3827
3857
|
spec.extra["abortSignal"] = input.signal
|
|
3828
3858
|
? AbortSignal.any([input.signal, plannerAbort.signal])
|
|
3829
3859
|
: plannerAbort.signal;
|
|
3830
|
-
const planInteraction =
|
|
3860
|
+
const planInteraction = interactionChannelFor(input, log, runId, taskId, attemptId, adapter.id, routed.supportsInteractive);
|
|
3831
3861
|
if (planInteraction)
|
|
3832
3862
|
spec.extra["interactionChannel"] = planInteraction;
|
|
3833
3863
|
const planWorkMode = applyWorkEnvelope(spec, this.workReportEnvelopeFor(routed, contract, Boolean(planInteraction)));
|
|
@@ -4420,18 +4450,14 @@ export class Orchestrator {
|
|
|
4420
4450
|
: null) ?? roHome.env,
|
|
4421
4451
|
});
|
|
4422
4452
|
this.credentials.stampProfileSelection(spec, input, adapter.id);
|
|
4423
|
-
const
|
|
4424
|
-
spec.extra["abortSignal"] = input.signal
|
|
4425
|
-
? AbortSignal.any([input.signal, reportAbort.signal])
|
|
4426
|
-
: reportAbort.signal;
|
|
4427
|
-
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);
|
|
4428
4454
|
if (reportInteraction)
|
|
4429
4455
|
spec.extra["interactionChannel"] = reportInteraction;
|
|
4430
4456
|
// D-16: compile the WorkReport envelope for the read-only lane.
|
|
4431
4457
|
const readonlyWorkEnvelope = this.workReportEnvelopeFor(routed, contract, Boolean(reportInteraction));
|
|
4432
4458
|
const readonlyWorkMode = applyWorkEnvelope(spec, readonlyWorkEnvelope);
|
|
4433
4459
|
const attemptEventsPath = join(paths.attemptsDir, attemptId, "events.jsonl");
|
|
4434
|
-
const answer = new
|
|
4460
|
+
const answer = new secretDiff.CountedAnswerAssembly();
|
|
4435
4461
|
const telemetry = createAttemptTelemetry(knobs.webPolicy, contract.external_context.web_required, effectiveWeb, [],
|
|
4436
4462
|
// Requested-model capture so ask/audit route receipts detect a silent
|
|
4437
4463
|
// model downgrade (typed model_mismatch), not just agent runs.
|
|
@@ -4439,7 +4465,6 @@ export class Orchestrator {
|
|
|
4439
4465
|
return {
|
|
4440
4466
|
knobs,
|
|
4441
4467
|
spec,
|
|
4442
|
-
reportAbort,
|
|
4443
4468
|
reportInteraction,
|
|
4444
4469
|
readonlyWorkMode,
|
|
4445
4470
|
attemptEventsPath,
|
|
@@ -4481,13 +4506,20 @@ export class Orchestrator {
|
|
|
4481
4506
|
}
|
|
4482
4507
|
return { status: "launched" };
|
|
4483
4508
|
}
|
|
4484
|
-
const { knobs, spec: preparedSpec,
|
|
4509
|
+
const { knobs, spec: preparedSpec, reportInteraction, readonlyWorkMode, attemptEventsPath, telemetry, } = preparation.value;
|
|
4485
4510
|
let { answer } = preparation.value;
|
|
4486
4511
|
let spec = preparedSpec;
|
|
4487
4512
|
bindProcessingAdmission(spec, ledger, lease.lease, adapter.id, attemptId, undefined, processingAdmission);
|
|
4488
4513
|
const retryPolicy = transientRetryPolicy(this.config(input.repoRoot));
|
|
4489
4514
|
let activeSessionId = spec.session_id;
|
|
4490
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 }));
|
|
4491
4523
|
const onAbort = () => void adapter.cancel?.(activeSessionId)?.catch(() => { });
|
|
4492
4524
|
if (input.signal) {
|
|
4493
4525
|
if (input.signal.aborted)
|
|
@@ -4502,12 +4534,11 @@ export class Orchestrator {
|
|
|
4502
4534
|
let processingRefusal = null;
|
|
4503
4535
|
let poolExhausted = null; // A5: typed pool-exhausted refusal
|
|
4504
4536
|
try {
|
|
4505
|
-
const triedProfiles = new Set(); // W5.4 failover: each profile at most once
|
|
4506
4537
|
for (let nativeTry = 0; !input.signal?.aborted; nativeTry += 1) {
|
|
4507
4538
|
// A3 per-try isolation (candidate-lane parity): neither a failed try's
|
|
4508
4539
|
// output nor its progress markers leak into the next try's evidence.
|
|
4509
4540
|
if (nativeTry > 0) {
|
|
4510
|
-
answer = new
|
|
4541
|
+
answer = new secretDiff.CountedAnswerAssembly();
|
|
4511
4542
|
telemetry.outputMarkers = newAttemptOutputMarkers();
|
|
4512
4543
|
}
|
|
4513
4544
|
const runSpec = nativeTry === 0
|
|
@@ -4515,10 +4546,14 @@ export class Orchestrator {
|
|
|
4515
4546
|
: HarnessRunSpec.parse({
|
|
4516
4547
|
...spec,
|
|
4517
4548
|
session_id: newId("ses"),
|
|
4518
|
-
resume_session_id: null,
|
|
4519
4549
|
extra: { ...spec.extra },
|
|
4520
4550
|
});
|
|
4521
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);
|
|
4522
4557
|
// #363: bound before the spawn — a credential change mid-try voids it.
|
|
4523
4558
|
const refusal = this.credentials.bindPreProgressRefusal(adapter.id, runSpec);
|
|
4524
4559
|
const transientStart = telemetry.transientFailures.length;
|
|
@@ -4536,7 +4571,7 @@ export class Orchestrator {
|
|
|
4536
4571
|
timeoutMs: harnessInactivityTimeoutMs(this.config(input.repoRoot)),
|
|
4537
4572
|
countsAsProgress: countsAsAgentProgress,
|
|
4538
4573
|
onTimeout: () => {
|
|
4539
|
-
|
|
4574
|
+
tryAbort.abort();
|
|
4540
4575
|
void adapter.cancel?.(activeSessionId)?.catch(() => { });
|
|
4541
4576
|
},
|
|
4542
4577
|
isSuspended: () => (reportInteraction?.pendingCount?.() ?? 0) > 0,
|
|
@@ -4546,20 +4581,19 @@ export class Orchestrator {
|
|
|
4546
4581
|
const safeEv = redactHarnessEvent(ev);
|
|
4547
4582
|
if (safeEv.payload?.["buffered"] !== true)
|
|
4548
4583
|
safeInvoke(input.onHarnessEvent, safeEv);
|
|
4549
|
-
// A thread ASK turn IS a chat turn now (INV-034): its native
|
|
4550
|
-
// session lives in the DURABLE per-lane home, so record it for the
|
|
4551
|
-
// next lane turn's resume. The read-only fallback chain is
|
|
4552
|
-
// sequential (never the parallel deep-scan swarm, which is
|
|
4553
|
-
// excluded from `laneRun`), so recordSession's upsert keeps the
|
|
4554
|
-
// latest lane session without a race.
|
|
4555
|
-
if (laneRun)
|
|
4556
|
-
observeNativeSessionEvent(input, adapter.id, safeEv);
|
|
4557
4584
|
observeAuthSwitch(log, adapter.id, attemptId, safeEv);
|
|
4558
4585
|
log.emit("harness.event", harnessEventPayload(adapter.id, attemptId, safeEv), safeEv.payload?.["buffered"] !== true);
|
|
4559
4586
|
appendLine(attemptEventsPath, JSON.stringify(safeEv));
|
|
4560
4587
|
if (input.signal?.aborted)
|
|
4561
4588
|
break;
|
|
4562
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);
|
|
4563
4597
|
emitPlanProgress((t, p) => log.emit(t, p), adapter.id, attemptId, safeEv);
|
|
4564
4598
|
// read-only routes burn quota too (the orchestrate PLANNER is
|
|
4565
4599
|
// the loudest) — same single owner as the agent loop.
|
|
@@ -4573,12 +4607,12 @@ export class Orchestrator {
|
|
|
4573
4607
|
budgetStopped = streamBudgetDenied = true;
|
|
4574
4608
|
budgetDenial ??= streamDenial;
|
|
4575
4609
|
harnessError = streamDenial.reason;
|
|
4576
|
-
|
|
4610
|
+
tryAbort.abort();
|
|
4577
4611
|
void adapter.cancel?.(activeSessionId)?.catch(() => { });
|
|
4578
4612
|
break;
|
|
4579
4613
|
}
|
|
4580
4614
|
// A TYPED final message wins verbatim over joined narration.
|
|
4581
|
-
answer.
|
|
4615
|
+
answer.observeCounted(ev, safeEv);
|
|
4582
4616
|
if (safeEv.type === "error")
|
|
4583
4617
|
harnessError = safeEv.error
|
|
4584
4618
|
? redactSecrets(safeEv.error)
|
|
@@ -4606,62 +4640,34 @@ export class Orchestrator {
|
|
|
4606
4640
|
const reportSoFar = answer.text();
|
|
4607
4641
|
const delivered = !harnessError && reportSoFar.length > 0 && !input.signal?.aborted;
|
|
4608
4642
|
this.credentials.noteTryServed(refusal, telemetry.outputMarkers, delivered);
|
|
4609
|
-
|
|
4610
|
-
|
|
4611
|
-
|
|
4612
|
-
|
|
4613
|
-
|
|
4614
|
-
|
|
4615
|
-
|
|
4616
|
-
|
|
4617
|
-
|
|
4618
|
-
|
|
4619
|
-
|
|
4620
|
-
|
|
4621
|
-
|
|
4622
|
-
|
|
4623
|
-
|
|
4624
|
-
|
|
4625
|
-
|
|
4626
|
-
|
|
4627
|
-
|
|
4628
|
-
|
|
4629
|
-
|
|
4630
|
-
|
|
4631
|
-
emit: (type, payload) => log.emit(type, payload),
|
|
4632
|
-
newSessionId: () => newId("ses"),
|
|
4633
|
-
defaultRouteWasVendorNative: routed.authRouteEstimate === "local_session",
|
|
4634
|
-
// D-U6: an explicit pin never rotates; pool-selected rows do.
|
|
4635
|
-
pinned: input.credentialProfileId != null,
|
|
4636
|
-
});
|
|
4637
|
-
// A5 ordering: same as the candidate lane — terminalize typed
|
|
4638
|
-
// before the transient gate burns same-profile retries. The pool
|
|
4639
|
-
// refusal APPENDS to the original harness error (candidate-lane
|
|
4640
|
-
// parity: errors.push) instead of erasing the true failure.
|
|
4641
|
-
if (rotated && "poolExhausted" in rotated) {
|
|
4642
|
-
poolExhausted = rotated.poolExhausted;
|
|
4643
|
-
harnessError = harnessError
|
|
4644
|
-
? `${harnessError}; ${safeErrorMessage(poolExhausted)}`
|
|
4645
|
-
: safeErrorMessage(poolExhausted);
|
|
4646
|
-
break;
|
|
4647
|
-
}
|
|
4648
|
-
if (rotated) {
|
|
4649
|
-
// INV-137: the rotated row's session must land in ITS OWN lane
|
|
4650
|
-
// home — never the previous row's lane store.
|
|
4651
|
-
spec = rotatedSpecInLaneHome(spec, rotated, (id) => this.laneHomeEnvFor(input, adapter.id, id), input.credentialProfileId ?? null);
|
|
4652
|
-
harnessError = null;
|
|
4653
|
-
continue;
|
|
4654
|
-
}
|
|
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;
|
|
4655
4665
|
}
|
|
4656
|
-
if (
|
|
4657
|
-
!sawRetryable ||
|
|
4658
|
-
reportSoFar.length > 0 ||
|
|
4659
|
-
nativeTry >= retryPolicy.maxRetries ||
|
|
4660
|
-
input.signal?.aborted)
|
|
4666
|
+
if (verdict.kind === "break")
|
|
4661
4667
|
break;
|
|
4662
|
-
|
|
4668
|
+
spec = verdict.spec;
|
|
4663
4669
|
harnessError = null;
|
|
4664
|
-
await sleep(delayMs);
|
|
4670
|
+
await sleep(verdict.delayMs);
|
|
4665
4671
|
}
|
|
4666
4672
|
}
|
|
4667
4673
|
finally {
|
|
@@ -4703,6 +4709,7 @@ export class Orchestrator {
|
|
|
4703
4709
|
workReport: roUnwrapped.workReport,
|
|
4704
4710
|
workReportSource: roUnwrapped.source,
|
|
4705
4711
|
workReportViolation: roUnwrapped.contractViolation,
|
|
4712
|
+
workReportUnverified: roUnwrapped.unverified ?? null,
|
|
4706
4713
|
contextTerminalExhausted: telemetry.contextExhausted,
|
|
4707
4714
|
});
|
|
4708
4715
|
// A broken WorkReport contract is a hard failure ONLY when the finalizer
|
|
@@ -4725,6 +4732,13 @@ export class Orchestrator {
|
|
|
4725
4732
|
// interruptions keep the strict deliverable requirement.
|
|
4726
4733
|
emptyDeliverableAllowed: roFinalized.outcomeClass === "clean",
|
|
4727
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
|
+
});
|
|
4728
4742
|
if (harnessError) {
|
|
4729
4743
|
log.emit("harness.completed", {
|
|
4730
4744
|
harness_id: adapter.id,
|
|
@@ -4745,6 +4759,7 @@ export class Orchestrator {
|
|
|
4745
4759
|
: poolExhausted || processingRefusal
|
|
4746
4760
|
? { declaredFailure: declaredFailure(processingRefusal ?? poolExhausted) }
|
|
4747
4761
|
: {}),
|
|
4762
|
+
...continuity.terminalFacts(roResumable),
|
|
4748
4763
|
});
|
|
4749
4764
|
if (opts.deepScan) {
|
|
4750
4765
|
store.writeText(join(paths.findingsDir, `${attemptId}-error.md`), `# Explorer ${attemptId} failed\n\n${harnessError}\n`);
|
|
@@ -4765,9 +4780,11 @@ export class Orchestrator {
|
|
|
4765
4780
|
harnessId: adapter.id,
|
|
4766
4781
|
status: scoutInterrupted ? "failed" : "success",
|
|
4767
4782
|
report: report || "(no output)",
|
|
4783
|
+
answerMatches: answer.secretLikeMatches(),
|
|
4768
4784
|
error: scoutInterrupted ? "context capacity exhausted before the scout completed" : null,
|
|
4769
4785
|
telemetry,
|
|
4770
4786
|
...(scoutInterrupted ? { interrupted: true } : {}),
|
|
4787
|
+
...continuity.terminalFacts(roResumable),
|
|
4771
4788
|
});
|
|
4772
4789
|
if (opts.deepScan) {
|
|
4773
4790
|
const warningNote = toolWarnings(telemetry).length
|
|
@@ -4929,7 +4946,7 @@ export class Orchestrator {
|
|
|
4929
4946
|
harnessId: a.harnessId,
|
|
4930
4947
|
status: a.status,
|
|
4931
4948
|
}));
|
|
4932
|
-
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"));
|
|
4933
4950
|
if (input.signal?.aborted)
|
|
4934
4951
|
return cancelledTerminal();
|
|
4935
4952
|
const succeededReadonly = attempts.filter((a) => a.status === "success");
|
|
@@ -5006,6 +5023,7 @@ export class Orchestrator {
|
|
|
5006
5023
|
harness_id: terminalHarnessId,
|
|
5007
5024
|
error: singleError,
|
|
5008
5025
|
failure_ref: "final/failure.yaml",
|
|
5026
|
+
...resumableTerminal(store, paths, attempts, true),
|
|
5009
5027
|
});
|
|
5010
5028
|
}
|
|
5011
5029
|
else {
|
|
@@ -5017,6 +5035,7 @@ export class Orchestrator {
|
|
|
5017
5035
|
harness_id: terminalHarnessId,
|
|
5018
5036
|
error: singleError,
|
|
5019
5037
|
failure_ref: "final/failure.yaml",
|
|
5038
|
+
...resumableTerminal(store, paths, attempts, true),
|
|
5020
5039
|
});
|
|
5021
5040
|
}
|
|
5022
5041
|
return {
|
|
@@ -5090,6 +5109,7 @@ export class Orchestrator {
|
|
|
5090
5109
|
phase: scanBudgetMapping ? scanBudgetMapping.phase : "harness",
|
|
5091
5110
|
error: message,
|
|
5092
5111
|
failure_ref: "final/failure.yaml",
|
|
5112
|
+
...resumableTerminal(store, paths, attempts, true),
|
|
5093
5113
|
});
|
|
5094
5114
|
return {
|
|
5095
5115
|
spendUsd: ledger.spend(),
|
|
@@ -5116,8 +5136,9 @@ export class Orchestrator {
|
|
|
5116
5136
|
// whole decision + reducer spawn lives in deepScanReducer.ts (its owner).
|
|
5117
5137
|
let deepScanSynthesis = null;
|
|
5118
5138
|
let reducedReport = null;
|
|
5139
|
+
let answerMatches = succeeded.reduce((sum, a) => sum + (a.answerMatches ?? 0), 0);
|
|
5119
5140
|
if (opts.deepScan) {
|
|
5120
|
-
({ deepScanSynthesis, reducedReport } = await resolveDeepScanSynthesis(this.deepScanReducerDeps(input, contract, log), {
|
|
5141
|
+
({ deepScanSynthesis, reducedReport, answerMatches } = await resolveDeepScanSynthesis(this.deepScanReducerDeps(input, contract, log), {
|
|
5121
5142
|
succeeded,
|
|
5122
5143
|
adapters,
|
|
5123
5144
|
budgetStopped,
|
|
@@ -5209,7 +5230,8 @@ export class Orchestrator {
|
|
|
5209
5230
|
const harnessLabel = attempts
|
|
5210
5231
|
.map((a) => `${a.attemptId}:${a.harnessId}:${a.status}`)
|
|
5211
5232
|
.join(", ");
|
|
5212
|
-
|
|
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`);
|
|
5213
5235
|
const reportProducerAttemptId = opts.deepScan &&
|
|
5214
5236
|
deepScanSynthesis?.status === "succeeded" &&
|
|
5215
5237
|
deepScanSynthesis.reducer_attempt_id
|
|
@@ -5226,6 +5248,7 @@ export class Orchestrator {
|
|
|
5226
5248
|
mode: opts.mode,
|
|
5227
5249
|
intent: opts.intent,
|
|
5228
5250
|
read_only: true,
|
|
5251
|
+
...(secretLike ? { secret_like: secretLike } : {}),
|
|
5229
5252
|
},
|
|
5230
5253
|
});
|
|
5231
5254
|
log.emit("work_product.emitted", { kind: "report", winner: reportProducerAttemptId });
|
|
@@ -5247,6 +5270,7 @@ export class Orchestrator {
|
|
|
5247
5270
|
reason: terminalFacts.reason,
|
|
5248
5271
|
phase: "executor",
|
|
5249
5272
|
failure_ref: "final/failure.yaml",
|
|
5273
|
+
...resumableTerminal(store, paths, attempts, true),
|
|
5250
5274
|
});
|
|
5251
5275
|
}
|
|
5252
5276
|
else if (workVetoed) {
|
|
@@ -5254,6 +5278,7 @@ export class Orchestrator {
|
|
|
5254
5278
|
lifecycle: terminalFacts.lifecycle,
|
|
5255
5279
|
facts: terminalFacts,
|
|
5256
5280
|
reason: terminalFacts.reason,
|
|
5281
|
+
...resumableTerminal(store, paths, attempts, true),
|
|
5257
5282
|
});
|
|
5258
5283
|
}
|
|
5259
5284
|
else {
|
|
@@ -5261,6 +5286,7 @@ export class Orchestrator {
|
|
|
5261
5286
|
lifecycle: terminalFacts.lifecycle,
|
|
5262
5287
|
facts: terminalFacts,
|
|
5263
5288
|
reason: terminalFacts.reason,
|
|
5289
|
+
...resumableTerminal(store, paths, attempts),
|
|
5264
5290
|
});
|
|
5265
5291
|
}
|
|
5266
5292
|
return {
|