@claudexor/orchestrator 3.17.0 → 3.17.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/account-pool.d.ts +11 -6
- package/dist/account-pool.d.ts.map +1 -1
- package/dist/account-pool.js +5 -8
- package/dist/account-pool.js.map +1 -1
- package/dist/account-resolution.d.ts +5 -1
- package/dist/account-resolution.d.ts.map +1 -1
- package/dist/account-resolution.js +5 -2
- package/dist/account-resolution.js.map +1 -1
- package/dist/attemptOutputMarkers.d.ts +5 -0
- package/dist/attemptOutputMarkers.d.ts.map +1 -1
- package/dist/attemptOutputMarkers.js +3 -1
- package/dist/attemptOutputMarkers.js.map +1 -1
- package/dist/attemptTelemetry.d.ts +6 -10
- package/dist/attemptTelemetry.d.ts.map +1 -1
- package/dist/attemptTelemetry.js +9 -5
- package/dist/attemptTelemetry.js.map +1 -1
- package/dist/candidateEvidence.d.ts +1 -1
- package/dist/candidateEvidence.d.ts.map +1 -1
- package/dist/candidateEvidence.js.map +1 -1
- package/dist/candidateTerminals.d.ts.map +1 -1
- package/dist/candidateTerminals.js +1 -0
- package/dist/candidateTerminals.js.map +1 -1
- package/dist/credential-differential.d.ts +1 -1
- package/dist/credential-differential.d.ts.map +1 -1
- package/dist/credential-differential.js +5 -2
- package/dist/credential-differential.js.map +1 -1
- package/dist/credential-profile-rotation.d.ts +4 -0
- package/dist/credential-profile-rotation.d.ts.map +1 -1
- package/dist/credential-profile-rotation.js +12 -13
- package/dist/credential-profile-rotation.js.map +1 -1
- package/dist/credential-profiles.d.ts +15 -2
- package/dist/credential-profiles.d.ts.map +1 -1
- package/dist/credential-profiles.js +19 -5
- package/dist/credential-profiles.js.map +1 -1
- package/dist/effortGovernance.d.ts +4 -40
- package/dist/effortGovernance.d.ts.map +1 -1
- package/dist/effortGovernance.js +4 -32
- package/dist/effortGovernance.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/laneStreamEvents.d.ts +4 -1
- package/dist/laneStreamEvents.d.ts.map +1 -1
- package/dist/laneStreamEvents.js +9 -0
- package/dist/laneStreamEvents.js.map +1 -1
- package/dist/modelGovernance.d.ts.map +1 -1
- package/dist/modelGovernance.js +4 -1
- package/dist/modelGovernance.js.map +1 -1
- package/dist/orchestrator-credentials.d.ts +42 -3
- package/dist/orchestrator-credentials.d.ts.map +1 -1
- package/dist/orchestrator-credentials.js +94 -5
- package/dist/orchestrator-credentials.js.map +1 -1
- package/dist/orchestrator.d.ts +3 -8
- package/dist/orchestrator.d.ts.map +1 -1
- package/dist/orchestrator.js +88 -97
- package/dist/orchestrator.js.map +1 -1
- package/dist/planRun.d.ts +7 -16
- package/dist/planRun.d.ts.map +1 -1
- package/dist/planRun.js +21 -23
- package/dist/planRun.js.map +1 -1
- package/dist/pre-progress-refusal.d.ts +61 -0
- package/dist/pre-progress-refusal.d.ts.map +1 -0
- package/dist/pre-progress-refusal.js +67 -0
- package/dist/pre-progress-refusal.js.map +1 -0
- package/dist/prompt-framing.d.ts +8 -0
- package/dist/prompt-framing.d.ts.map +1 -0
- package/dist/prompt-framing.js +33 -0
- package/dist/prompt-framing.js.map +1 -0
- package/dist/reviewerPanel.d.ts.map +1 -1
- package/dist/reviewerPanel.js +18 -17
- package/dist/reviewerPanel.js.map +1 -1
- package/dist/rotation-predicate.d.ts +3 -0
- package/dist/rotation-predicate.d.ts.map +1 -1
- package/dist/rotation-predicate.js +3 -0
- package/dist/rotation-predicate.js.map +1 -1
- package/dist/runSupport.d.ts.map +1 -1
- package/dist/runSupport.js +2 -4
- package/dist/runSupport.js.map +1 -1
- package/dist/runTerminalResults.d.ts +4 -1
- package/dist/runTerminalResults.d.ts.map +1 -1
- package/dist/runTerminalResults.js +14 -2
- package/dist/runTerminalResults.js.map +1 -1
- package/package.json +17 -17
package/dist/orchestrator.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import { applyWorkEnvelope, promptWithPointers as renderPromptWithPointers, threadContextPointer, } from "./prompt-framing.js";
|
|
2
|
+
import { requestRefusalFailure } from "./runTerminalResults.js";
|
|
1
3
|
import { delegationBeltFor } from "./delegationBelt.js";
|
|
2
4
|
import { bindProcessingAdmission, processingAdmissionForLease, updateProcessingStreamHold, ProcessingBudgetAdmissionError, } from "./processing-dispatch.js";
|
|
3
5
|
import { cancelledCandidatesResult, emptyCandidateResult, failedCandidatesResult, } from "./candidateTerminals.js";
|
|
4
6
|
import { existsSync } from "node:fs";
|
|
5
|
-
import { effectiveAuthPreference, observeNativeSessionEvent, resumeSessionForProfile, rotateSpecOnTypedLimit,
|
|
7
|
+
import { effectiveAuthPreference, observeNativeSessionEvent, resumeSessionForProfile, rotateSpecOnTypedLimit, } from "./credential-profiles.js";
|
|
6
8
|
import { OrchestratorCredentials, reviewerProfileResolver, rotatedSpecInLaneHome, } from "./orchestrator-credentials.js";
|
|
7
9
|
import { accountPoolRows } from "./account-pool.js";
|
|
8
10
|
import { writeRunTelemetryArtifact } from "./runTelemetryWriter.js";
|
|
@@ -11,7 +13,7 @@ import { processAttemptUsage } from "./attemptUsage.js";
|
|
|
11
13
|
import { captureDirectoryCandidate, directoryHasOutput, directoryCandidateStable, observeDirectoryPaths, publishDirectoryCandidate, } from "./directoryCandidate.js";
|
|
12
14
|
import { appliedAttemptFacts, assertDelegatedEvidence, isMutatingAccess, outerBoundaryNotice, scopedHarnessHome, } from "./delegatedHome.js";
|
|
13
15
|
import * as AC from "./attemptUsageCost.js";
|
|
14
|
-
import { candidateRoster, candidateStatuses, convergenceOutcomeFacts, isWorkingCandidate, partitionCandidates, toCandidateEvidence, } from "./candidateEvidence.js";
|
|
16
|
+
import { candidateRoster, candidateStatuses, convergenceOutcomeFacts, isWorkingCandidate, partitionCandidates, toCandidateEvidence, unanimousDeclaredFailure, } from "./candidateEvidence.js";
|
|
15
17
|
import { capabilityIntents } from "@claudexor/gateway";
|
|
16
18
|
import { reviewCandidateRuns, resolveEngineReview, unreviewedCandidateEvidence, evaluateUnreviewedConvergence, } from "./candidateReview.js";
|
|
17
19
|
import { join } from "node:path";
|
|
@@ -39,7 +41,7 @@ import { cancelledResult, declaredFailure, failTerminally, guardAnnouncedRun, wr
|
|
|
39
41
|
import { budgetFailureRecord, classifyBudgetFailure } from "./budgetFailure.js";
|
|
40
42
|
import { finalizeStructuredOutput } from "./structuredOutput.js";
|
|
41
43
|
import { admitRun } from "./runAdmission.js";
|
|
42
|
-
import { dropDeltaPastBudget, emitPlanProgress, emitTransientExhausted, emitTransientRetryPlan, observeReadonlySpend, } from "./laneStreamEvents.js";
|
|
44
|
+
import { dropDeltaPastBudget, emitPlanProgress, emitTransientExhausted, emitTransientRetryPlan, observeReadonlySpend, recordAdapterThrow, } from "./laneStreamEvents.js";
|
|
43
45
|
import { promptWithEngineConstraints, sleep, redactHarnessEvent, harnessEventPayload, safeErrorMessage, renderSummary, observeBudgetSignals, rotateOnStall, recordCleanAttemptMetrics, envInheritance, transientRetryPolicy, reviewerTimeoutMs, harnessInactivityTimeoutMs, observeAuthSwitch, emitPrimaryDivergence, emitPoolDegraded, deliveryRefusalFailure, winnerNeedsHuman, writeRaceDeliveryDecision, } from "./runSupport.js";
|
|
44
46
|
import { candidateStatusInRouteContext, resolveReadOnlyRouteContext, } from "./routeContext.js";
|
|
45
47
|
import { resolveAutoReviewerPanel, resolveExplicitReviewerPanel } from "./reviewerPanel.js";
|
|
@@ -48,7 +50,7 @@ import { buildContinuation, } from "./continuity.js";
|
|
|
48
50
|
import { activePlanPointer, resolveContinuitySummary, workspaceAnchor, } from "./continuity-facts.js";
|
|
49
51
|
import { runDiffReview } from "./diffReview.js";
|
|
50
52
|
import { rawScoutBundle, resolveDeepScanSynthesis, } from "./deepScanReducer.js";
|
|
51
|
-
import {
|
|
53
|
+
import { createAttemptTelemetry, observeAttemptTelemetry, setAttemptOutcome, telemetrySummary, toolWarnings, unrecoveredToolErrors, webUnsatisfied, } from "./attemptTelemetry.js";
|
|
52
54
|
import { newAttemptOutputMarkers } from "./attemptOutputMarkers.js";
|
|
53
55
|
import * as delegateFailure from "./delegationFailure.js";
|
|
54
56
|
import * as secretDiff from "./secretDiff.js";
|
|
@@ -366,6 +368,7 @@ export class Orchestrator {
|
|
|
366
368
|
quotaAbsences: () => this.deps.quotaAbsences?.() ?? [],
|
|
367
369
|
credentialUnusable: () => this.deps.credentialUnusable?.() ?? [],
|
|
368
370
|
recordCredentialUnusable: (obs) => this.deps.recordCredentialUnusable?.(obs),
|
|
371
|
+
preProgressRefusals: () => this.deps.preProgressRefusals,
|
|
369
372
|
authPreferenceForHarness: (repoRoot, harnessId, runPreference) => this.authPreferenceForHarness(repoRoot, harnessId, runPreference),
|
|
370
373
|
});
|
|
371
374
|
/**
|
|
@@ -554,43 +557,19 @@ export class Orchestrator {
|
|
|
554
557
|
// profile-admitted route joins even an AUTO pool (the run spawns with
|
|
555
558
|
// the profile's transport, so the default store's state is not the
|
|
556
559
|
// routing truth). Capability/manifest gating above still applies.
|
|
557
|
-
let profileAdmitted = false;
|
|
558
560
|
const profileAdapter = this.deps.registry.get(id);
|
|
559
|
-
const profileProbe = profileAdapter?.probeCredentialProfile?.bind(profileAdapter);
|
|
560
561
|
const explicitPin = this.credentials.effectiveProfileId(input, id);
|
|
561
|
-
const model = input.models?.[id] ?? cfgEntry?.default_model ?? null;
|
|
562
562
|
// Pins and bound/pool rows are checked against their own readiness.
|
|
563
|
-
const
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
for (const candidateId of rowCandidateIds) {
|
|
574
|
-
const verdict = await selectedProfileAvailability({
|
|
575
|
-
registry: this.config(input.repoRoot)?.global.credential_profiles ?? [],
|
|
576
|
-
profileId: candidateId,
|
|
577
|
-
harnessId: id,
|
|
578
|
-
probe: profileProbe,
|
|
579
|
-
// `verification: passed` from the local store only means a login file
|
|
580
|
-
// is present. The poller's authenticated vendor call is the only
|
|
581
|
-
// liveness evidence; admission must act on it or dispatch may use a revoked token.
|
|
582
|
-
quota: vendorQuota,
|
|
583
|
-
unusable: liveUnusable,
|
|
584
|
-
model,
|
|
585
|
-
// Only an explicit/bound route may consume bounded stale LKG evidence.
|
|
586
|
-
allowStale: explicitPin !== null || input.threadAccountBindings?.[id] === candidateId,
|
|
587
|
-
});
|
|
588
|
-
if (verdict === "available") {
|
|
589
|
-
profileAdmitted = true;
|
|
590
|
-
break;
|
|
591
|
-
}
|
|
592
|
-
lastRowVerdict = verdict;
|
|
593
|
-
}
|
|
563
|
+
const rows = await this.credentials.admitRouteRows({
|
|
564
|
+
input,
|
|
565
|
+
harnessId: id,
|
|
566
|
+
defaultReady: status.status === "ok",
|
|
567
|
+
model: input.models?.[id] ?? cfgEntry?.default_model ?? null,
|
|
568
|
+
quota: vendorQuota,
|
|
569
|
+
unusable: liveUnusable,
|
|
570
|
+
probe: profileAdapter?.probeCredentialProfile?.bind(profileAdapter),
|
|
571
|
+
});
|
|
572
|
+
const profileAdmitted = rows.admitted;
|
|
594
573
|
if (profileAdmitted) {
|
|
595
574
|
// A valid account row restores manifest intent truth when the default store failed.
|
|
596
575
|
if (status.status !== "ok") {
|
|
@@ -603,14 +582,19 @@ export class Orchestrator {
|
|
|
603
582
|
}
|
|
604
583
|
}
|
|
605
584
|
else if (explicitPin) {
|
|
606
|
-
// An explicit pin that is not ready refuses/drops the lane;
|
|
607
|
-
//
|
|
608
|
-
//
|
|
609
|
-
|
|
610
|
-
const why = `${id} credential profile is not ready: ${lastRowVerdict}`;
|
|
585
|
+
// An explicit pin that is not ready refuses/drops the lane; not-ready
|
|
586
|
+
// UNPINNED rows are named below, and the default doctor verdict speaks
|
|
587
|
+
// only when neither a row nor a default login exists.
|
|
588
|
+
const why = `${id} credential profile is not ready: ${rows.pinVerdict}`;
|
|
611
589
|
dropLane(id, "credential", why);
|
|
612
590
|
continue;
|
|
613
591
|
}
|
|
592
|
+
// #363: registered rows own this lane, so their observed states — not
|
|
593
|
+
// the default login's doctor advice — explain why none was admitted.
|
|
594
|
+
if (rows.unreadyRows && (status.status === "unavailable" || !explicitPool)) {
|
|
595
|
+
dropLane(id, "credential", rows.unreadyRows);
|
|
596
|
+
continue;
|
|
597
|
+
}
|
|
614
598
|
if (status.status === "unavailable" && !profileAdmitted) {
|
|
615
599
|
const why = `${id} is unavailable${status.reasons.length ? `: ${status.reasons.join("; ")}` : ""}`;
|
|
616
600
|
dropLane(id, "doctor", why);
|
|
@@ -1135,25 +1119,6 @@ export class Orchestrator {
|
|
|
1135
1119
|
callerSchema: contract.output_schema ?? null,
|
|
1136
1120
|
});
|
|
1137
1121
|
}
|
|
1138
|
-
/**
|
|
1139
|
-
* D-16: apply the resolved WorkReport transport to a built spec — set the
|
|
1140
|
-
* envelope output_schema (constrained/side_tool routes) and APPEND the fenced
|
|
1141
|
-
* metadata instruction (validated routes, e.g. cursor). Mutates the spec in
|
|
1142
|
-
* place and returns the mode the answer unwrap consumes. Called at every
|
|
1143
|
-
* task-producing spec-build site so the transport is never wired one-off.
|
|
1144
|
-
*/
|
|
1145
|
-
applyWorkEnvelope(spec, workEnvelope) {
|
|
1146
|
-
if (workEnvelope.outputSchema !== undefined)
|
|
1147
|
-
spec.output_schema = workEnvelope.outputSchema;
|
|
1148
|
-
const instruction = workEnvelope.mode.instruction;
|
|
1149
|
-
if (instruction) {
|
|
1150
|
-
spec.instructions =
|
|
1151
|
-
spec.instructions && spec.instructions.trim()
|
|
1152
|
-
? `${spec.instructions}\n\n${instruction}`
|
|
1153
|
-
: instruction;
|
|
1154
|
-
}
|
|
1155
|
-
return workEnvelope.mode;
|
|
1156
|
-
}
|
|
1157
1122
|
routeSpecKnobs(routed, contract, overrideModel, effortHint) {
|
|
1158
1123
|
const s = routed.settings;
|
|
1159
1124
|
const contractPolicy = contract.external_context.policy;
|
|
@@ -1286,7 +1251,7 @@ export class Orchestrator {
|
|
|
1286
1251
|
const briefPath = join(paths.contextDir, "THREAD.md");
|
|
1287
1252
|
store.writeText(briefPath, result.packetMarkdown);
|
|
1288
1253
|
return {
|
|
1289
|
-
pointerLine:
|
|
1254
|
+
pointerLine: threadContextPointer(briefPath),
|
|
1290
1255
|
};
|
|
1291
1256
|
}
|
|
1292
1257
|
catch (err) {
|
|
@@ -1405,6 +1370,8 @@ export class Orchestrator {
|
|
|
1405
1370
|
raw_context_packet: rawContextPacket,
|
|
1406
1371
|
stream_deltas: streamDeltas,
|
|
1407
1372
|
});
|
|
1373
|
+
if (runInput)
|
|
1374
|
+
this.credentials.stampProfileSelection(spec, runInput, adapter.id);
|
|
1408
1375
|
bindProcessingAdmission(spec, ledger, processingLease.id, adapter.id, attemptId, processingLease.onDenied, processingAdmission);
|
|
1409
1376
|
const billingInput = runInput ?? { repoRoot: contract.repo.root };
|
|
1410
1377
|
// Keep billing route-bound until admission observes the actual prepared
|
|
@@ -1418,7 +1385,7 @@ export class Orchestrator {
|
|
|
1418
1385
|
if (interaction)
|
|
1419
1386
|
spec.extra["interactionChannel"] = interaction;
|
|
1420
1387
|
const workEnvelope = this.workReportEnvelopeFor(routed, contract, Boolean(interaction));
|
|
1421
|
-
const workReportMode =
|
|
1388
|
+
const workReportMode = applyWorkEnvelope(spec, workEnvelope);
|
|
1422
1389
|
const inactivityMs = harnessInactivityTimeoutMs(this.config(contract.repo.root));
|
|
1423
1390
|
// Named once: the attempt record, the CandidateRun and the terminal gate all
|
|
1424
1391
|
// read the SAME applied facts rather than three re-derivations. Derived
|
|
@@ -1482,6 +1449,8 @@ export class Orchestrator {
|
|
|
1482
1449
|
? AbortSignal.any([signal, attemptAbort.signal])
|
|
1483
1450
|
: attemptAbort.signal;
|
|
1484
1451
|
activeSessionId = runSpec.session_id;
|
|
1452
|
+
// #363: bound before the spawn — a credential change mid-try voids it.
|
|
1453
|
+
const refusal = this.credentials.bindPreProgressRefusal(adapter.id, runSpec);
|
|
1485
1454
|
const transientStart = telemetry.transientFailures.length;
|
|
1486
1455
|
const rateLimitStart = telemetry.rateLimits.length;
|
|
1487
1456
|
let rawPatch = null;
|
|
@@ -1589,11 +1558,14 @@ export class Orchestrator {
|
|
|
1589
1558
|
processingRefusal = err;
|
|
1590
1559
|
break;
|
|
1591
1560
|
}
|
|
1592
|
-
|
|
1561
|
+
if (!recordAdapterThrow(telemetry, err))
|
|
1562
|
+
break;
|
|
1593
1563
|
}
|
|
1594
1564
|
finally {
|
|
1595
1565
|
clearFileBackedContext();
|
|
1596
1566
|
}
|
|
1567
|
+
if (telemetry.requestRefusal)
|
|
1568
|
+
break;
|
|
1597
1569
|
const newTransients = telemetry.transientFailures.slice(transientStart);
|
|
1598
1570
|
// #31: the centralized retry gate reads the classified `retryable` verdict.
|
|
1599
1571
|
const sawRetryable = newTransients.some((f) => f.retryable);
|
|
@@ -1605,6 +1577,8 @@ export class Orchestrator {
|
|
|
1605
1577
|
? directoryCapture.files?.noChanges === true
|
|
1606
1578
|
: currentDiff.trim().length === 0;
|
|
1607
1579
|
const deliverableEmpty = workspaceUnchanged && answer.text().length === 0;
|
|
1580
|
+
const delivered = !harnessErrored && !deliverableEmpty && !signal?.aborted;
|
|
1581
|
+
this.credentials.noteTryServed(refusal, telemetry.outputMarkers, delivered);
|
|
1608
1582
|
// W5.4 + A2 failover: a typed-limit hit OR a structural pre-progress
|
|
1609
1583
|
// death rebuilds the spec on a NEW session under the next profile.
|
|
1610
1584
|
if (harnessErrored && runInput && !signal?.aborted) {
|
|
@@ -1616,9 +1590,10 @@ export class Orchestrator {
|
|
|
1616
1590
|
registry: this.config(contract.repo.root)?.global.credential_profiles ?? [],
|
|
1617
1591
|
snapshots: this.deps.quotaSnapshots?.() ?? [],
|
|
1618
1592
|
probeReadyProfiles: () => this.credentials.readyProfileIdsForRotation(runInput, adapter.id, spec.credential_profile ?? null, triedProfiles, spec.model_hint ?? null),
|
|
1619
|
-
...this.credentials.rotationObservations(adapter, spec, newTransients),
|
|
1593
|
+
...this.credentials.rotationObservations(adapter, spec, newTransients, refusal),
|
|
1620
1594
|
triedProfiles,
|
|
1621
1595
|
markers: telemetry.outputMarkers,
|
|
1596
|
+
requestRefused: Boolean(telemetry.requestRefusal),
|
|
1622
1597
|
sawTypedLimit,
|
|
1623
1598
|
sawRetryable,
|
|
1624
1599
|
attemptErrored: harnessErrored,
|
|
@@ -1670,7 +1645,7 @@ export class Orchestrator {
|
|
|
1670
1645
|
}
|
|
1671
1646
|
// A pool-exhausted terminal is rotation's verdict, not the transient
|
|
1672
1647
|
// machinery's — no `route.transient.exhausted` rides along with it.
|
|
1673
|
-
if (harnessErrored && !poolExhausted && !processingRefusal) {
|
|
1648
|
+
if (harnessErrored && !poolExhausted && !processingRefusal && !telemetry.requestRefusal) {
|
|
1674
1649
|
emitTransientExhausted((t, p) => log?.emit(t, p), adapter.id, attemptId, telemetry, retryPolicy.maxRetries);
|
|
1675
1650
|
}
|
|
1676
1651
|
const attemptStreamEndedMs = Date.now();
|
|
@@ -1821,9 +1796,11 @@ export class Orchestrator {
|
|
|
1821
1796
|
telemetry,
|
|
1822
1797
|
...(secretDiffRefusal ? { secretDiffRefusal } : {}),
|
|
1823
1798
|
// A5: the typed refusal survives NORMAL attempt finalization (no throw).
|
|
1824
|
-
...(
|
|
1825
|
-
? { declaredFailure:
|
|
1826
|
-
:
|
|
1799
|
+
...(telemetry.requestRefusal
|
|
1800
|
+
? { declaredFailure: requestRefusalFailure(telemetry.requestRefusal) }
|
|
1801
|
+
: poolExhausted || processingRefusal
|
|
1802
|
+
? { declaredFailure: declaredFailure(processingRefusal ?? poolExhausted) }
|
|
1803
|
+
: {}),
|
|
1827
1804
|
outcomeClass: finalized.outcomeClass,
|
|
1828
1805
|
applied,
|
|
1829
1806
|
};
|
|
@@ -3326,6 +3303,9 @@ export class Orchestrator {
|
|
|
3326
3303
|
// Cancellation/deadline keeps priority over a belt failure finalized concurrently.
|
|
3327
3304
|
if (input.signal?.aborted || processingBudgetDenial)
|
|
3328
3305
|
break;
|
|
3306
|
+
if (run.telemetry.requestRefusal ||
|
|
3307
|
+
run.telemetry.effortResolution?.resolution === "rejected")
|
|
3308
|
+
break;
|
|
3329
3309
|
if (delegateFailure.candidateFailureKind(run)) {
|
|
3330
3310
|
const failure = delegateFailure.candidateFailureTerminal(run, "convergence");
|
|
3331
3311
|
if (run.files) {
|
|
@@ -3699,6 +3679,7 @@ export class Orchestrator {
|
|
|
3699
3679
|
: (convDeclared?.category ?? "internal"),
|
|
3700
3680
|
code: processingBudgetMapping?.code ?? convDeclared?.code ?? null,
|
|
3701
3681
|
resetsAt: convDeclared?.resetsAt ?? null,
|
|
3682
|
+
requestRefusal: convDeclared?.requestRefusal,
|
|
3702
3683
|
safeMessage: convNeedsDecision
|
|
3703
3684
|
? `review escalated to a human decision after ${attempt} attempt(s)`
|
|
3704
3685
|
: facts.reason === "stuck_no_progress"
|
|
@@ -3843,6 +3824,7 @@ export class Orchestrator {
|
|
|
3843
3824
|
? this.laneHomeEnvFor(input, adapter.id, sessionFields.credential_profile?.profile_id ?? input.credentialProfileId ?? null)
|
|
3844
3825
|
: null) ?? args.fallbackHome,
|
|
3845
3826
|
});
|
|
3827
|
+
this.credentials.stampProfileSelection(spec, input, adapter.id);
|
|
3846
3828
|
const plannerAbort = new AbortController();
|
|
3847
3829
|
spec.extra["abortSignal"] = input.signal
|
|
3848
3830
|
? AbortSignal.any([input.signal, plannerAbort.signal])
|
|
@@ -3850,7 +3832,7 @@ export class Orchestrator {
|
|
|
3850
3832
|
const planInteraction = this.interactionChannelFor(input, log, runId, taskId, attemptId, adapter.id, routed.supportsInteractive);
|
|
3851
3833
|
if (planInteraction)
|
|
3852
3834
|
spec.extra["interactionChannel"] = planInteraction;
|
|
3853
|
-
const planWorkMode =
|
|
3835
|
+
const planWorkMode = applyWorkEnvelope(spec, this.workReportEnvelopeFor(routed, contract, Boolean(planInteraction)));
|
|
3854
3836
|
return {
|
|
3855
3837
|
knobs,
|
|
3856
3838
|
effectiveWeb,
|
|
@@ -4036,6 +4018,8 @@ export class Orchestrator {
|
|
|
4036
4018
|
error: outcome.error,
|
|
4037
4019
|
});
|
|
4038
4020
|
if (outcome.status !== "success") {
|
|
4021
|
+
if (outcome.telemetry?.effortResolution?.resolution === "rejected")
|
|
4022
|
+
break;
|
|
4039
4023
|
const next = adapters[idx + 1];
|
|
4040
4024
|
if (next && !input.signal?.aborted) {
|
|
4041
4025
|
fallbackFrom = outcome.harnessId;
|
|
@@ -4211,10 +4195,11 @@ export class Orchestrator {
|
|
|
4211
4195
|
env_inheritance: envInheritance(this.config(input.repoRoot)),
|
|
4212
4196
|
env: homeEnv,
|
|
4213
4197
|
});
|
|
4198
|
+
this.credentials.stampProfileSelection(spec, input, routed.adapter.id);
|
|
4214
4199
|
// D-16: compile the WorkReport transport onto the reducer spec (the
|
|
4215
4200
|
// reducer is non-interactive) so its output is unwrapped + finalized
|
|
4216
4201
|
// through the shared attempt contract, not a fourth deliverable predicate.
|
|
4217
|
-
const workReportMode =
|
|
4202
|
+
const workReportMode = applyWorkEnvelope(spec, this.workReportEnvelopeFor(routed, contract, false));
|
|
4218
4203
|
return {
|
|
4219
4204
|
spec,
|
|
4220
4205
|
webPolicy: knobs.webPolicy,
|
|
@@ -4417,13 +4402,7 @@ export class Orchestrator {
|
|
|
4417
4402
|
: null;
|
|
4418
4403
|
// D-16d: the continuation packet pointer rides after the lane pointer so
|
|
4419
4404
|
// the fresh session is re-grounded in the exhausted attempt's work.
|
|
4420
|
-
const promptWithPointers =
|
|
4421
|
-
explorerPrompt,
|
|
4422
|
-
laneContinuity?.pointerLine,
|
|
4423
|
-
continuationPointer,
|
|
4424
|
-
]
|
|
4425
|
-
.filter((p) => Boolean(p))
|
|
4426
|
-
.join("\n\n");
|
|
4405
|
+
const promptWithPointers = renderPromptWithPointers(explorerPrompt, laneContinuity?.pointerLine, continuationPointer);
|
|
4427
4406
|
const spec = HarnessRunSpec.parse({
|
|
4428
4407
|
session_id: newId("ses"),
|
|
4429
4408
|
intent: opts.intent,
|
|
@@ -4446,6 +4425,7 @@ export class Orchestrator {
|
|
|
4446
4425
|
? this.laneHomeEnvFor(input, adapter.id, sessionFields.credential_profile?.profile_id ?? input.credentialProfileId ?? null)
|
|
4447
4426
|
: null) ?? roHome.env,
|
|
4448
4427
|
});
|
|
4428
|
+
this.credentials.stampProfileSelection(spec, input, adapter.id);
|
|
4449
4429
|
const reportAbort = new AbortController();
|
|
4450
4430
|
spec.extra["abortSignal"] = input.signal
|
|
4451
4431
|
? AbortSignal.any([input.signal, reportAbort.signal])
|
|
@@ -4455,7 +4435,7 @@ export class Orchestrator {
|
|
|
4455
4435
|
spec.extra["interactionChannel"] = reportInteraction;
|
|
4456
4436
|
// D-16: compile the WorkReport envelope for the read-only lane.
|
|
4457
4437
|
const readonlyWorkEnvelope = this.workReportEnvelopeFor(routed, contract, Boolean(reportInteraction));
|
|
4458
|
-
const readonlyWorkMode =
|
|
4438
|
+
const readonlyWorkMode = applyWorkEnvelope(spec, readonlyWorkEnvelope);
|
|
4459
4439
|
const attemptEventsPath = join(paths.attemptsDir, attemptId, "events.jsonl");
|
|
4460
4440
|
const answer = new AnswerAssembly();
|
|
4461
4441
|
const telemetry = createAttemptTelemetry(knobs.webPolicy, contract.external_context.web_required, effectiveWeb, [],
|
|
@@ -4548,6 +4528,8 @@ export class Orchestrator {
|
|
|
4548
4528
|
extra: { ...spec.extra },
|
|
4549
4529
|
});
|
|
4550
4530
|
activeSessionId = runSpec.session_id;
|
|
4531
|
+
// #363: bound before the spawn — a credential change mid-try voids it.
|
|
4532
|
+
const refusal = this.credentials.bindPreProgressRefusal(adapter.id, runSpec);
|
|
4551
4533
|
const transientStart = telemetry.transientFailures.length;
|
|
4552
4534
|
const rateLimitStart = telemetry.rateLimits.length;
|
|
4553
4535
|
log.emit("harness.started", {
|
|
@@ -4618,14 +4600,19 @@ export class Orchestrator {
|
|
|
4618
4600
|
break;
|
|
4619
4601
|
}
|
|
4620
4602
|
// #31: classify the throw (watchdog timeout vs process crash) as typed.
|
|
4621
|
-
|
|
4603
|
+
if (!recordAdapterThrow(telemetry, err))
|
|
4604
|
+
break;
|
|
4622
4605
|
}
|
|
4623
4606
|
if (streamBudgetDenied)
|
|
4624
4607
|
break;
|
|
4608
|
+
if (telemetry.requestRefusal)
|
|
4609
|
+
break;
|
|
4625
4610
|
const newTransients = telemetry.transientFailures.slice(transientStart);
|
|
4626
4611
|
const sawRetryable = newTransients.some((f) => f.retryable);
|
|
4627
4612
|
const sawTypedLimit = telemetry.rateLimits.length > rateLimitStart;
|
|
4628
4613
|
const reportSoFar = answer.text();
|
|
4614
|
+
const delivered = !harnessError && reportSoFar.length > 0 && !input.signal?.aborted;
|
|
4615
|
+
this.credentials.noteTryServed(refusal, telemetry.outputMarkers, delivered);
|
|
4629
4616
|
// W5.4 + A2 reactive failover, READ-ONLY lane (same contract as the
|
|
4630
4617
|
// candidate lane: typed limit or structural pre-progress death).
|
|
4631
4618
|
if (harnessError && !input.signal?.aborted) {
|
|
@@ -4637,9 +4624,10 @@ export class Orchestrator {
|
|
|
4637
4624
|
registry: this.config(input.repoRoot)?.global.credential_profiles ?? [],
|
|
4638
4625
|
snapshots: this.deps.quotaSnapshots?.() ?? [],
|
|
4639
4626
|
probeReadyProfiles: () => this.credentials.readyProfileIdsForRotation(input, adapter.id, spec.credential_profile ?? null, triedProfiles, spec.model_hint ?? null),
|
|
4640
|
-
...this.credentials.rotationObservations(adapter, spec, newTransients),
|
|
4627
|
+
...this.credentials.rotationObservations(adapter, spec, newTransients, refusal),
|
|
4641
4628
|
triedProfiles,
|
|
4642
4629
|
markers: telemetry.outputMarkers,
|
|
4630
|
+
requestRefused: Boolean(telemetry.requestRefusal),
|
|
4643
4631
|
sawTypedLimit,
|
|
4644
4632
|
sawRetryable,
|
|
4645
4633
|
attemptErrored: harnessError !== null,
|
|
@@ -4698,7 +4686,7 @@ export class Orchestrator {
|
|
|
4698
4686
|
preStreamFailureSource: "readonly-pre-stream",
|
|
4699
4687
|
});
|
|
4700
4688
|
}
|
|
4701
|
-
if (harnessError && !poolExhausted && !processingRefusal) {
|
|
4689
|
+
if (harnessError && !poolExhausted && !processingRefusal && !telemetry.requestRefusal) {
|
|
4702
4690
|
emitTransientExhausted((t, p) => log.emit(t, p), adapter.id, attemptId, telemetry, retryPolicy.maxRetries);
|
|
4703
4691
|
}
|
|
4704
4692
|
attemptTelemetries.push({ attemptId, harnessId: adapter.id, telemetry });
|
|
@@ -4759,9 +4747,11 @@ export class Orchestrator {
|
|
|
4759
4747
|
report,
|
|
4760
4748
|
error: harnessError,
|
|
4761
4749
|
telemetry,
|
|
4762
|
-
...(
|
|
4763
|
-
? { declaredFailure:
|
|
4764
|
-
:
|
|
4750
|
+
...(telemetry.requestRefusal
|
|
4751
|
+
? { declaredFailure: requestRefusalFailure(telemetry.requestRefusal) }
|
|
4752
|
+
: poolExhausted || processingRefusal
|
|
4753
|
+
? { declaredFailure: declaredFailure(processingRefusal ?? poolExhausted) }
|
|
4754
|
+
: {}),
|
|
4765
4755
|
});
|
|
4766
4756
|
if (opts.deepScan) {
|
|
4767
4757
|
store.writeText(join(paths.findingsDir, `${attemptId}-error.md`), `# Explorer ${attemptId} failed\n\n${harnessError}\n`);
|
|
@@ -4868,12 +4858,15 @@ export class Orchestrator {
|
|
|
4868
4858
|
}
|
|
4869
4859
|
}
|
|
4870
4860
|
}
|
|
4861
|
+
if (last?.telemetry.effortResolution?.resolution === "rejected")
|
|
4862
|
+
break;
|
|
4871
4863
|
// Per-harness fallback_model: one same-harness retry on FAILURE (not
|
|
4872
4864
|
// policy blocks) before falling through to the next harness.
|
|
4873
4865
|
const fallbackModel = routed.settings?.fallbackModel;
|
|
4874
4866
|
const firstModel = contract.routing_models[routed.adapter.id] ?? routed.settings?.defaultModel ?? null;
|
|
4875
4867
|
if (last &&
|
|
4876
4868
|
last.status === "failed" &&
|
|
4869
|
+
!last.telemetry.requestRefusal &&
|
|
4877
4870
|
fallbackModel &&
|
|
4878
4871
|
fallbackModel !== firstModel &&
|
|
4879
4872
|
!budgetStopped &&
|
|
@@ -5000,6 +4993,7 @@ export class Orchestrator {
|
|
|
5000
4993
|
eventRefs: roEventRefs,
|
|
5001
4994
|
runDir: paths.root,
|
|
5002
4995
|
resetsAt: roDeclared?.resetsAt ?? null,
|
|
4996
|
+
requestRefusal: roDeclared?.requestRefusal,
|
|
5003
4997
|
vendorFailure: attemptVendorFailure(attemptTelemetries, last?.attemptId),
|
|
5004
4998
|
nextActions: harnessFailureNextActions(roCategory),
|
|
5005
4999
|
});
|
|
@@ -5060,10 +5054,7 @@ export class Orchestrator {
|
|
|
5060
5054
|
const succeeded = succeededReadonly;
|
|
5061
5055
|
if (opts.deepScan && succeeded.length === 0) {
|
|
5062
5056
|
const blocked = attempts.some((a) => a.status === "blocked");
|
|
5063
|
-
// QA-050/QA-019:
|
|
5064
|
-
// before spawn) is a BUDGET failure, not harness_error — route it through
|
|
5065
|
-
// the shared classifier. Only a pure-denial scan (no scout actually errored
|
|
5066
|
-
// in the harness) qualifies, so a real explorer failure is never masked.
|
|
5057
|
+
// QA-050/QA-019: only a pure-denial scan may claim a budget failure.
|
|
5067
5058
|
const scanBudgetMapping = budgetStopped && !blocked && attempts.every((a) => a.budgetDenied === true)
|
|
5068
5059
|
? classifyBudgetFailure({ denial: budgetDenial, terminal: ledger.terminal() })
|
|
5069
5060
|
: null;
|
|
@@ -5085,12 +5076,15 @@ export class Orchestrator {
|
|
|
5085
5076
|
}));
|
|
5086
5077
|
}
|
|
5087
5078
|
else {
|
|
5088
|
-
//
|
|
5089
|
-
|
|
5079
|
+
// Mixed causes stay generic; only unanimous evidence may name the scan's cause.
|
|
5080
|
+
const declared = blocked ? null : unanimousDeclaredFailure(attempts);
|
|
5090
5081
|
const scanCategory = dominantHarnessFailureCategory(attemptTelemetries.flatMap((a) => a.telemetry.transientFailures));
|
|
5091
5082
|
writeFailure(store, paths, {
|
|
5092
5083
|
phase: "harness",
|
|
5093
|
-
category: blocked ? "policy" : "harness_error",
|
|
5084
|
+
category: blocked ? "policy" : (declared?.category ?? "harness_error"),
|
|
5085
|
+
code: declared?.code,
|
|
5086
|
+
resetsAt: declared?.resetsAt,
|
|
5087
|
+
requestRefusal: declared?.requestRefusal,
|
|
5094
5088
|
safeMessage: message || "all explorers failed",
|
|
5095
5089
|
eventRefs: attempts.map((a) => `attempts/${a.attemptId}/events.jsonl`),
|
|
5096
5090
|
runDir: paths.root,
|
|
@@ -5101,10 +5095,7 @@ export class Orchestrator {
|
|
|
5101
5095
|
],
|
|
5102
5096
|
});
|
|
5103
5097
|
}
|
|
5104
|
-
// QA-036:
|
|
5105
|
-
// deliverable, so a blocked scan can no longer read as a succeeded
|
|
5106
|
-
// "needs review" run (exit 0). An empty scan is a failure whether the
|
|
5107
|
-
// explorers were blocked or errored.
|
|
5098
|
+
// QA-036: no successful explorer means no synthesizable deliverable.
|
|
5108
5099
|
store.writeText(join(paths.finalDir, "summary.md"), `# Run ${runId} (${opts.mode})\n\n- Lifecycle: ${scanFailFacts.lifecycle}${scanFailFacts.reason ? ` (${scanFailFacts.reason})` : ""}\n\n${message}\n`);
|
|
5109
5100
|
log.emit("output.ready", { kind: "summary", path: "final/summary.md", state: "diagnostic" });
|
|
5110
5101
|
log.emit("run.failed", {
|