@claudexor/orchestrator 3.2.1 → 3.3.7
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/attemptUsageCost.d.ts +7 -1
- package/dist/attemptUsageCost.d.ts.map +1 -1
- package/dist/attemptUsageCost.js +7 -1
- package/dist/attemptUsageCost.js.map +1 -1
- package/dist/budgetFailure.d.ts +1 -1
- package/dist/budgetFailure.d.ts.map +1 -1
- package/dist/budgetFailure.js.map +1 -1
- package/dist/candidateEvidence.d.ts +24 -0
- package/dist/candidateEvidence.d.ts.map +1 -1
- package/dist/candidateEvidence.js +26 -0
- package/dist/candidateEvidence.js.map +1 -1
- package/dist/credential-profile-rotation.d.ts.map +1 -1
- package/dist/credential-profile-rotation.js +23 -3
- package/dist/credential-profile-rotation.js.map +1 -1
- package/dist/credential-profiles.d.ts +60 -1
- package/dist/credential-profiles.d.ts.map +1 -1
- package/dist/credential-profiles.js +92 -1
- package/dist/credential-profiles.js.map +1 -1
- package/dist/delegatedHome.d.ts +164 -0
- package/dist/delegatedHome.d.ts.map +1 -0
- package/dist/delegatedHome.js +180 -0
- package/dist/delegatedHome.js.map +1 -0
- package/dist/diffReview.d.ts +7 -0
- package/dist/diffReview.d.ts.map +1 -1
- package/dist/diffReview.js +1 -0
- package/dist/diffReview.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/orchestrator.d.ts +27 -2
- package/dist/orchestrator.d.ts.map +1 -1
- package/dist/orchestrator.js +135 -103
- package/dist/orchestrator.js.map +1 -1
- package/dist/requestRequirements.d.ts +15 -2
- package/dist/requestRequirements.d.ts.map +1 -1
- package/dist/requestRequirements.js +17 -3
- package/dist/requestRequirements.js.map +1 -1
- package/dist/routing-failure.d.ts +35 -1
- package/dist/routing-failure.d.ts.map +1 -1
- package/dist/routing-failure.js +70 -3
- package/dist/routing-failure.js.map +1 -1
- package/dist/runFacts.d.ts.map +1 -1
- package/dist/runFacts.js +4 -13
- package/dist/runFacts.js.map +1 -1
- package/dist/runTerminalResults.d.ts +26 -1
- package/dist/runTerminalResults.d.ts.map +1 -1
- package/dist/runTerminalResults.js +34 -4
- package/dist/runTerminalResults.js.map +1 -1
- package/dist/runTerminals.d.ts +2 -2
- package/dist/runTerminals.d.ts.map +1 -1
- package/dist/runTerminals.js +2 -2
- package/dist/runTerminals.js.map +1 -1
- package/package.json +17 -17
package/dist/orchestrator.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
|
-
import { effectiveAuthPreference, observeNativeSessionEvent, preflightCredentialProfile, preflightDefaultSubject, probeCredentialProfileStatus, profileHeadroomBreach, profileStatusAdmits, resolveCredentialProfile, resumeSessionForProfile, rotateSpecOnTypedLimit, selectedProfileAvailability, staticRotationCandidates, } from "./credential-profiles.js";
|
|
2
|
+
import { effectiveAuthPreference, observeNativeSessionEvent, preflightCredentialProfile, preflightDefaultSubject, probeCredentialProfileStatus, profileHeadroomBreach, profileStatusAdmits, resolveCredentialProfile, resumeSessionForProfile, rotateSpecOnTypedLimit, selectedProfileAvailability, staticRotationCandidates, vendorVerifiedProfileStatus, } from "./credential-profiles.js";
|
|
3
3
|
import { writeRunTelemetryArtifact } from "./runTelemetryWriter.js";
|
|
4
4
|
import { buildFileBackedSynthesisInput, materializeWinnerOutputs, stageFileBackedContext, writeCandidateAttemptArtifacts, } from "./candidateOutputs.js";
|
|
5
5
|
import { processAttemptUsage } from "./attemptUsage.js";
|
|
6
|
+
import { appliedAttemptFacts, assertDelegatedEvidence, confinementNotice, externallyConfinedLane, scopedHarnessHome, } from "./delegatedHome.js";
|
|
6
7
|
import * as AC from "./attemptUsageCost.js";
|
|
7
|
-
import { candidateRoster, convergenceOutcomeFacts, isWorkingCandidate, partitionCandidates, toCandidateEvidence, } from "./candidateEvidence.js";
|
|
8
|
+
import { candidateRoster, convergenceOutcomeFacts, isWorkingCandidate, partitionCandidates, toCandidateEvidence, unanimousDeclaredFailure, } from "./candidateEvidence.js";
|
|
8
9
|
import { capabilityIntents } from "@claudexor/gateway";
|
|
9
10
|
import { policyFindings } from "./policyFindings.js";
|
|
10
11
|
import { join } from "node:path";
|
|
@@ -17,7 +18,7 @@ import { assertRouteModelsAllowed } from "./modelGovernance.js";
|
|
|
17
18
|
import { governRouteEffort } from "./effortGovernance.js";
|
|
18
19
|
import { isFullAccess, RequestRequirementsResolver } from "./requestRequirements.js";
|
|
19
20
|
import { activateDelegationParent } from "./delegation-parent-activation.js";
|
|
20
|
-
import {
|
|
21
|
+
import { writeRoutingFailureTerminal } from "./routing-failure.js";
|
|
21
22
|
export { routingFailureClassification } from "./routing-failure.js";
|
|
22
23
|
import { runBounded } from "./run-bounded.js";
|
|
23
24
|
import { planPrompt } from "./plan-prompt.js";
|
|
@@ -26,7 +27,7 @@ import { resolveRunInputDefaults } from "./run-input-resolution.js";
|
|
|
26
27
|
import { beginAnnouncedRun } from "./runEventLog.js";
|
|
27
28
|
import { arbitrationBudgetOptions, decisionBudgetSummary } from "./decisionBudget.js";
|
|
28
29
|
import { buildRevisePrompt } from "./revisePrompt.js";
|
|
29
|
-
import { cancelledResult, failTerminally, guardAnnouncedRun, writeFailure, } from "./runTerminals.js";
|
|
30
|
+
import { cancelledResult, declaredFailure, failTerminally, guardAnnouncedRun, writeFailure, } from "./runTerminals.js";
|
|
30
31
|
import { budgetFailureRecord, classifyBudgetFailure } from "./budgetFailure.js";
|
|
31
32
|
import { assertOutputSchemaCompiles, finalizeStructuredOutput } from "./structuredOutput.js";
|
|
32
33
|
import { transientRetryDelayMs, promptWithEngineConstraints, sleep, redactHarnessEvent, harnessEventPayload, safeErrorMessage, renderSummary, observeBudgetSignals, rotateOnStall, recordCleanAttemptMetrics, envInheritance, transientRetryPolicy, reviewerTimeoutMs, harnessInactivityTimeoutMs, observeAuthSwitch, emitPrimaryDivergence, emitPoolDegraded, deliveryRefusalFailure, winnerNeedsHuman, writeRaceDeliveryDecision, } from "./runSupport.js";
|
|
@@ -409,8 +410,19 @@ export class Orchestrator {
|
|
|
409
410
|
const policy = this.config(repoRoot)?.global.harnesses?.[harnessId]?.profile_policy;
|
|
410
411
|
return policy ?? { limit_action: "fail", rotation_eligible: [], headroom_threshold: 0.9 };
|
|
411
412
|
}
|
|
413
|
+
/** The quota poller's authenticated vendor evidence for THIS decision epoch,
|
|
414
|
+
* read once so every profile in the epoch is judged against one observation
|
|
415
|
+
* set. This is what makes profile readiness at admission mean the same thing
|
|
416
|
+
* it means on the Accounts card (INV-135 honesty). */
|
|
417
|
+
vendorQuotaObservations() {
|
|
418
|
+
return {
|
|
419
|
+
snapshots: this.deps.quotaSnapshots?.() ?? [],
|
|
420
|
+
absences: this.deps.quotaAbsences?.() ?? [],
|
|
421
|
+
};
|
|
422
|
+
}
|
|
412
423
|
/** Fresh profile readiness for one rotation decision epoch. Accounts uses
|
|
413
|
-
* the same probe wrapper + admission predicate when
|
|
424
|
+
* the same probe wrapper + vendor overlay + admission predicate when
|
|
425
|
+
* projecting next_up. */
|
|
414
426
|
async readyProfileIdsForRotation(input, harnessId, current, excluded = new Set()) {
|
|
415
427
|
const profiles = staticRotationCandidates({
|
|
416
428
|
registry: this.config(input.repoRoot)?.global.credential_profiles ?? [],
|
|
@@ -420,9 +432,12 @@ export class Orchestrator {
|
|
|
420
432
|
excluded,
|
|
421
433
|
});
|
|
422
434
|
const adapter = this.deps.registry.get(harnessId);
|
|
435
|
+
const quota = this.vendorQuotaObservations();
|
|
423
436
|
const entries = await Promise.all(profiles.map(async (profile) => ({
|
|
424
437
|
profile,
|
|
425
|
-
|
|
438
|
+
// Rotating INTO a profile the vendor has already rejected would spend a
|
|
439
|
+
// whole attempt to rediscover the 401 the poller reported a minute ago.
|
|
440
|
+
status: vendorVerifiedProfileStatus(await probeCredentialProfileStatus(profile, adapter?.probeCredentialProfile?.bind(adapter)), quota),
|
|
426
441
|
})));
|
|
427
442
|
return new Set(entries
|
|
428
443
|
.filter(({ profile, status }) => profileStatusAdmits(profile, status))
|
|
@@ -580,6 +595,9 @@ export class Orchestrator {
|
|
|
580
595
|
dropped.push(detail);
|
|
581
596
|
droppedLanes.push({ harnessId, stage, detail });
|
|
582
597
|
};
|
|
598
|
+
// One observation set for the whole admission pass, so two lanes cannot be
|
|
599
|
+
// judged against different vendor epochs.
|
|
600
|
+
const vendorQuota = this.vendorQuotaObservations();
|
|
583
601
|
for (const id of ids) {
|
|
584
602
|
const adapter = this.deps.registry.get(id);
|
|
585
603
|
if (!adapter) {
|
|
@@ -643,6 +661,11 @@ export class Orchestrator {
|
|
|
643
661
|
profileId: this.effectiveProfileId(input, id),
|
|
644
662
|
harnessId: id,
|
|
645
663
|
probe: profileAdapter?.probeCredentialProfile?.bind(profileAdapter),
|
|
664
|
+
// `verification: passed` from the local store only means a login file
|
|
665
|
+
// is present. The poller's authenticated vendor call is the only
|
|
666
|
+
// liveness evidence we have, and admission is the surface that must
|
|
667
|
+
// act on it — otherwise the run dispatches into a revoked token.
|
|
668
|
+
quota: vendorQuota,
|
|
646
669
|
});
|
|
647
670
|
if (profileVerdict !== null) {
|
|
648
671
|
if (profileVerdict === "available") {
|
|
@@ -675,7 +698,7 @@ export class Orchestrator {
|
|
|
675
698
|
const readOnlyIntent = intent === "plan" || intent === "spec" || intent === "explain" || intent === "audit";
|
|
676
699
|
const requiredAccess = this.requestRequirements.adapterAccess(intent, manifest.capabilities.implementation_transport, readOnlyIntent
|
|
677
700
|
? "readonly"
|
|
678
|
-
: (input.access ?? this.config(input.repoRoot).trust.access_default));
|
|
701
|
+
: (input.access ?? this.config(input.repoRoot).trust.access_default), externallyConfinedLane(input.delegated === true));
|
|
679
702
|
const accessSupported = !requiredAccess || manifest.access_profiles_supported.includes(requiredAccess);
|
|
680
703
|
const webSupport = manifest.capabilities.web_policy;
|
|
681
704
|
// Match routeSpecKnobs: a per-harness web default upgrades run-level auto,
|
|
@@ -1381,11 +1404,28 @@ export class Orchestrator {
|
|
|
1381
1404
|
return { pointerLine: null };
|
|
1382
1405
|
}
|
|
1383
1406
|
}
|
|
1384
|
-
|
|
1407
|
+
/**
|
|
1408
|
+
* The ONE place an attempt's HOME and OS boundary are decided.
|
|
1409
|
+
*
|
|
1410
|
+
* Called by every caller of `runCandidateInEnvelope` INSIDE that caller's
|
|
1411
|
+
* try, so a delegated refusal is caught by the same catch that writes the
|
|
1412
|
+
* attempt record, and so the decided home is in scope there.
|
|
1413
|
+
*/
|
|
1414
|
+
harnessHomeFor(wsm, envelope, routed, runInput) {
|
|
1415
|
+
// Refuses (never silently degrades) when a delegated run cannot be confined.
|
|
1416
|
+
return scopedHarnessHome(wsm, envelope, envelope.worktree_path === envelope.repo_root, runInput?.delegated === true, routed.adapterAccess);
|
|
1417
|
+
}
|
|
1418
|
+
async runCandidateInEnvelope(routed, envelope, attemptId, label, contract, prompt, store, paths, wsm, ledger, access, onHarnessEvent, signal, modelHint, effortHint, intent, log, effectiveWebMode, interaction, budgetGuard, runInput, streamDeltas, fileBackedContext,
|
|
1385
1419
|
/** D-16d: when set, the mechanical continuation checkpoint pointer for a
|
|
1386
1420
|
* one-shot fresh-session continuation — appended to the prompt so the model
|
|
1387
1421
|
* (and the offline fake) re-grounds in the exhausted attempt's partial work. */
|
|
1388
|
-
continuationPointer
|
|
1422
|
+
continuationPointer,
|
|
1423
|
+
/** Decided by `harnessHomeFor` in the CALLER, so the per-attempt applied
|
|
1424
|
+
* facts exist in the caller's catch too: an attempt that ran and then threw
|
|
1425
|
+
* must record what it ran under, not just why it stopped. REQUIRED (no
|
|
1426
|
+
* default): a silent fallback here would spawn a delegated child on the
|
|
1427
|
+
* operator's real home while the record still claimed confinement. */
|
|
1428
|
+
harnessHome) {
|
|
1389
1429
|
const adapter = routed.adapter;
|
|
1390
1430
|
const knobs = this.routeSpecKnobs(routed, contract, modelHint, effortHint);
|
|
1391
1431
|
// Isolated scoped-home sessions are never retained after disposal.
|
|
@@ -1406,7 +1446,9 @@ export class Orchestrator {
|
|
|
1406
1446
|
intent,
|
|
1407
1447
|
// Engine-derived read-only prompt constraints: protected/auto-protected
|
|
1408
1448
|
// paths PLUS the exact typed gate argv the run will execute (QA-022 FIX B).
|
|
1409
|
-
prompt: promptWithEngineConstraints(
|
|
1449
|
+
prompt: promptWithEngineConstraints(
|
|
1450
|
+
// A child running without an OS boundary is TOLD so (disclosure 2 of 3).
|
|
1451
|
+
[prompt, confinementNotice(harnessHome), laneContinuity?.pointerLine, continuationPointer]
|
|
1410
1452
|
.filter((s) => typeof s === "string" && s.length > 0)
|
|
1411
1453
|
.join("\n\n"), contract.constraints.protected_paths, contract.constraints.auto_protected_paths, contract.constraints.protected_path_approvals, contract.tests.commands),
|
|
1412
1454
|
attachments: runInput?.attachments ?? [],
|
|
@@ -1428,9 +1470,14 @@ export class Orchestrator {
|
|
|
1428
1470
|
...(inPlaceEnvelope && sessionFields?.resume_session_id
|
|
1429
1471
|
? { resume_session_id: sessionFields.resume_session_id }
|
|
1430
1472
|
: {}),
|
|
1431
|
-
// Scoped harness home
|
|
1432
|
-
// native environment so the resumed
|
|
1433
|
-
|
|
1473
|
+
// Scoped harness home for isolated envelopes AND for every delegated run;
|
|
1474
|
+
// an ordinary in-place run keeps the native environment so the resumed
|
|
1475
|
+
// vendor session is reachable. See scopedHarnessHome for the cost a
|
|
1476
|
+
// delegated in-place attempt pays for that confinement.
|
|
1477
|
+
...(harnessHome.env ? { env: harnessHome.env } : {}),
|
|
1478
|
+
// The OS boundary itself. The shared CLI run loop wraps the argv with it;
|
|
1479
|
+
// no adapter opts in, so no adapter can forget.
|
|
1480
|
+
confinement: harnessHome.confinement,
|
|
1434
1481
|
raw_context_packet: rawContextPacket,
|
|
1435
1482
|
stream_deltas: streamDeltas,
|
|
1436
1483
|
});
|
|
@@ -1441,6 +1488,13 @@ export class Orchestrator {
|
|
|
1441
1488
|
const workEnvelope = this.workReportEnvelopeFor(routed, contract, Boolean(interaction));
|
|
1442
1489
|
const workReportMode = this.applyWorkEnvelope(spec, workEnvelope);
|
|
1443
1490
|
const inactivityMs = harnessInactivityTimeoutMs(this.config(contract.repo.root));
|
|
1491
|
+
// Named once: the attempt record, the CandidateRun and the terminal gate all
|
|
1492
|
+
// read the SAME applied facts rather than three re-derivations. Derived
|
|
1493
|
+
// from the LIVE spec, so a W5.4 failover that rotates the credential
|
|
1494
|
+
// mid-attempt is re-recorded at the point it becomes true — the receipt
|
|
1495
|
+
// must name the profile that ran, never the one the request asked for.
|
|
1496
|
+
const appliedNow = () => appliedAttemptFacts(harnessHome, spec.access, spec.credential_profile?.profile_id ?? runInput?.credentialProfileId ?? null);
|
|
1497
|
+
let applied = appliedNow();
|
|
1444
1498
|
const attemptStartedMs = Date.now();
|
|
1445
1499
|
const budgetSignalState = { quotaPressureDisclosed: false };
|
|
1446
1500
|
const triedProfiles = new Set(); // W5.4 failover: each profile at most once
|
|
@@ -1663,6 +1717,7 @@ export class Orchestrator {
|
|
|
1663
1717
|
});
|
|
1664
1718
|
if (rotated) {
|
|
1665
1719
|
spec = rotated;
|
|
1720
|
+
applied = appliedNow();
|
|
1666
1721
|
errors.length = 0;
|
|
1667
1722
|
harnessErrored = false;
|
|
1668
1723
|
continue;
|
|
@@ -1812,6 +1867,10 @@ export class Orchestrator {
|
|
|
1812
1867
|
...(secretDiffRefusal ? { secret_diff_refusal: secretDiffRefusal } : {}),
|
|
1813
1868
|
gates: gates.map((g) => ({ id: g.id, status: g.status })),
|
|
1814
1869
|
branch: envelope.branch_name,
|
|
1870
|
+
// Applied facts, not promises: the caller of a delegated run
|
|
1871
|
+
// verifies the confinement it asked for instead of trusting it.
|
|
1872
|
+
// Built by the SAME shape the failure path writes.
|
|
1873
|
+
...applied,
|
|
1815
1874
|
},
|
|
1816
1875
|
}), {
|
|
1817
1876
|
totalUsd: cost,
|
|
@@ -1835,6 +1894,7 @@ export class Orchestrator {
|
|
|
1835
1894
|
telemetry,
|
|
1836
1895
|
...(secretDiffRefusal ? { secretDiffRefusal } : {}),
|
|
1837
1896
|
outcomeClass: finalized.outcomeClass,
|
|
1897
|
+
applied,
|
|
1838
1898
|
};
|
|
1839
1899
|
}
|
|
1840
1900
|
interactionChannelFor(input, log, runId, taskId, attemptId, harnessId,
|
|
@@ -1963,26 +2023,11 @@ export class Orchestrator {
|
|
|
1963
2023
|
}
|
|
1964
2024
|
catch (err) {
|
|
1965
2025
|
const message = safeErrorMessage(err);
|
|
1966
|
-
store
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
phase: "routing",
|
|
1972
|
-
category: routingFailure.category,
|
|
1973
|
-
safeMessage: message,
|
|
1974
|
-
runDir: paths.root,
|
|
1975
|
-
...(routingFailure.nextActions ? { nextActions: routingFailure.nextActions } : {}),
|
|
1976
|
-
});
|
|
1977
|
-
store.writeText(join(paths.finalDir, "summary.md"), `# Run ${runId} (${mode})\n\n- Lifecycle: failed\n- Phase: routing\n\n${message}\n`);
|
|
1978
|
-
log.emit("output.ready", { kind: "summary", path: "final/summary.md", state: "diagnostic" });
|
|
1979
|
-
log.emit("run.failed", {
|
|
1980
|
-
lifecycle: "failed",
|
|
1981
|
-
facts: makeOutcomeFacts("failed", { reason: "harness_failed" }),
|
|
1982
|
-
reason: "harness_failed",
|
|
1983
|
-
phase: "routing",
|
|
1984
|
-
error: message,
|
|
1985
|
-
failure_ref: "final/failure.yaml",
|
|
2026
|
+
writeRoutingFailureTerminal(store, paths, log, {
|
|
2027
|
+
runId,
|
|
2028
|
+
modeLabel: mode,
|
|
2029
|
+
message,
|
|
2030
|
+
err,
|
|
1986
2031
|
});
|
|
1987
2032
|
return {
|
|
1988
2033
|
runId,
|
|
@@ -2104,6 +2149,9 @@ export class Orchestrator {
|
|
|
2104
2149
|
const knobs = this.routeSpecKnobs(slot.routed, contract, downgradeModel ?? undefined, input.effort);
|
|
2105
2150
|
const effectiveWeb = this.discloseWebUpgrade(log, slot.routed, knobs.webPolicy, slot.attemptId);
|
|
2106
2151
|
let envelope;
|
|
2152
|
+
// Declared OUTSIDE the try so the catch below can state what the attempt
|
|
2153
|
+
// actually ran under, not merely that it stopped.
|
|
2154
|
+
let harnessHome;
|
|
2107
2155
|
try {
|
|
2108
2156
|
log.emit("harness.started", {
|
|
2109
2157
|
harness_id: adapter.id,
|
|
@@ -2123,6 +2171,7 @@ export class Orchestrator {
|
|
|
2123
2171
|
requestedSingleCandidate &&
|
|
2124
2172
|
slot.routed.implementationTransport !== "git_patch_envelope",
|
|
2125
2173
|
});
|
|
2174
|
+
harnessHome = this.harnessHomeFor(wsm, envelope, slot.routed, input);
|
|
2126
2175
|
const run = await this.runCandidateInEnvelope(slot.routed, envelope, slot.attemptId, slot.label, contract, input.prompt, store, paths, wsm, ledger, candidateAccess, (ev) => {
|
|
2127
2176
|
const safeEv = redactHarnessEvent(ev);
|
|
2128
2177
|
safeInvoke(input.onHarnessEvent, safeEv);
|
|
@@ -2133,7 +2182,8 @@ export class Orchestrator {
|
|
|
2133
2182
|
return false;
|
|
2134
2183
|
budgetStopped = true;
|
|
2135
2184
|
return true;
|
|
2136
|
-
}, input, requestedSingleCandidate)
|
|
2185
|
+
}, input, requestedSingleCandidate, // W-C4 deltas: single-candidate chat lane only (racing = noise x N)
|
|
2186
|
+
undefined, undefined, harnessHome);
|
|
2137
2187
|
ledger.settle(slot.leaseId, attemptUsageCostSettlement(run.cost, run.costEstimated, run.attemptId, run.harnessId, run.telemetry.authMode, run.telemetry.usageCost));
|
|
2138
2188
|
log.emit("harness.completed", {
|
|
2139
2189
|
harness_id: adapter.id,
|
|
@@ -2209,7 +2259,7 @@ export class Orchestrator {
|
|
|
2209
2259
|
return false;
|
|
2210
2260
|
budgetStopped = true;
|
|
2211
2261
|
return true;
|
|
2212
|
-
}, input, requestedSingleCandidate, undefined, packet.pointerLine ?? undefined);
|
|
2262
|
+
}, input, requestedSingleCandidate, undefined, packet.pointerLine ?? undefined, harnessHome);
|
|
2213
2263
|
ledger.settle(contLeaseId, attemptUsageCostSettlement(contRun.cost, contRun.costEstimated, contRun.attemptId, contRun.harnessId, contRun.telemetry.authMode, contRun.telemetry.usageCost));
|
|
2214
2264
|
log.emit("harness.completed", {
|
|
2215
2265
|
harness_id: adapter.id,
|
|
@@ -2255,6 +2305,7 @@ export class Orchestrator {
|
|
|
2255
2305
|
const failureCost = AC.attemptFailureCost(err, "post-stream-error", 0);
|
|
2256
2306
|
ledger.settle(slot.leaseId, failureCost.settlement);
|
|
2257
2307
|
const message = safeErrorMessage(err);
|
|
2308
|
+
const declared = declaredFailure(err);
|
|
2258
2309
|
const infraPhase = envelope === undefined ? "workspace" : "harness";
|
|
2259
2310
|
log.emit("harness.completed", {
|
|
2260
2311
|
harness_id: adapter.id,
|
|
@@ -2263,7 +2314,7 @@ export class Orchestrator {
|
|
|
2263
2314
|
error: message,
|
|
2264
2315
|
phase: infraPhase,
|
|
2265
2316
|
});
|
|
2266
|
-
store.writeYaml(join(paths.attemptsDir, slot.attemptId, "attempt.yaml"), AC.attemptFailureRecord(slot.attemptId, adapter.id, failureCost, infraPhase, message));
|
|
2317
|
+
store.writeYaml(join(paths.attemptsDir, slot.attemptId, "attempt.yaml"), AC.attemptFailureRecord(slot.attemptId, adapter.id, failureCost, infraPhase, message, appliedAttemptFacts(harnessHome, slot.routed.adapterAccess, this.effectiveProfileId(input, adapter.id))));
|
|
2267
2318
|
runsBySlot[slotIdx] = {
|
|
2268
2319
|
attemptId: slot.attemptId,
|
|
2269
2320
|
harnessId: adapter.id,
|
|
@@ -2280,6 +2331,11 @@ export class Orchestrator {
|
|
|
2280
2331
|
slot.routed.delegationRequirement,
|
|
2281
2332
|
], knobs.model),
|
|
2282
2333
|
infraPhase,
|
|
2334
|
+
// Keep a TYPED pre-spawn refusal (spent quota window + its reset)
|
|
2335
|
+
// alive past this catch; `message` alone would force the terminal to
|
|
2336
|
+
// read prose back out.
|
|
2337
|
+
...(declared.code ? { declaredFailure: declared } : {}),
|
|
2338
|
+
applied: appliedAttemptFacts(harnessHome, slot.routed.adapterAccess, this.effectiveProfileId(input, adapter.id)),
|
|
2283
2339
|
};
|
|
2284
2340
|
}
|
|
2285
2341
|
finally {
|
|
@@ -2289,11 +2345,18 @@ export class Orchestrator {
|
|
|
2289
2345
|
};
|
|
2290
2346
|
await runBounded(slots, Math.min(slots.length, MAX_PARALLEL_CANDIDATES), runSlot);
|
|
2291
2347
|
const runs = runsBySlot.filter((r) => r !== undefined);
|
|
2348
|
+
// Fail-closed terminal: a delegated mutating run whose attempts cannot state
|
|
2349
|
+
// their applied confinement refuses instead of passing.
|
|
2350
|
+
assertDelegatedEvidence(input.delegated === true, candidateAccess, runs);
|
|
2292
2351
|
const cancelledCandidates = () => runs.map((r) => ({
|
|
2293
2352
|
attemptId: r.attemptId,
|
|
2294
2353
|
harnessId: r.harnessId,
|
|
2295
2354
|
status: gatesPassed(r.gates) && !r.errored ? "green" : "red",
|
|
2296
2355
|
}));
|
|
2356
|
+
/** The one cancellation terminal this race can reach, from any of its three
|
|
2357
|
+
* abort checks. Every argument is re-read at call time, exactly as it was
|
|
2358
|
+
* when each check spelled the whole call out for itself. */
|
|
2359
|
+
const cancelledRaceResult = () => cancelledResult(log, runId, taskId, mode, paths.root, cancelledCandidates(), () => this.writeRunTelemetry(store, paths, contract, runId, taskId, mode, candidateRoster(runs), null), ledger.spend(), input.signal, store);
|
|
2297
2360
|
// Revert divergence fence for the single-candidate in-place path: the
|
|
2298
2361
|
// candidate mutated the LIVE tree during execution above, so the post-turn
|
|
2299
2362
|
// snapshot must be taken NOW — before review/synthesis/arbitration, which can
|
|
@@ -2311,7 +2374,7 @@ export class Orchestrator {
|
|
|
2311
2374
|
}
|
|
2312
2375
|
if (input.signal?.aborted) {
|
|
2313
2376
|
await disposeReviewEnvelopes();
|
|
2314
|
-
return
|
|
2377
|
+
return cancelledRaceResult();
|
|
2315
2378
|
}
|
|
2316
2379
|
const failedDelegation = delegateFailure.dominantRaceCandidateFailure(runs);
|
|
2317
2380
|
if (failedDelegation) {
|
|
@@ -2412,15 +2475,21 @@ export class Orchestrator {
|
|
|
2412
2475
|
// harness cause (timeout, rate limit, crash, config) gets remediation that
|
|
2413
2476
|
// fits it, instead of a doomed "Check harness authentication".
|
|
2414
2477
|
const harnessCategory = dominantHarnessFailureCategory(first.telemetry.transientFailures);
|
|
2478
|
+
// A run speaks with a candidate's TYPED refusal only when EVERY candidate
|
|
2479
|
+
// died of the same one (candidateEvidence owns that rule); mixed causes
|
|
2480
|
+
// keep the honest harness terminal.
|
|
2481
|
+
const unanimous = unanimousDeclaredFailure(runs);
|
|
2415
2482
|
writeFailure(store, paths, {
|
|
2416
2483
|
phase,
|
|
2417
|
-
category: phase === "workspace" ? "project" : "harness_error",
|
|
2484
|
+
category: unanimous?.category ?? (phase === "workspace" ? "project" : "harness_error"),
|
|
2485
|
+
code: unanimous?.code ?? null,
|
|
2418
2486
|
harnessId: first.harnessId,
|
|
2419
2487
|
attemptId: first.attemptId,
|
|
2420
2488
|
safeMessage: rootCause,
|
|
2421
2489
|
rawDetailRef: `attempts/${first.attemptId}/attempt.yaml`,
|
|
2422
2490
|
eventRefs: existingEventRefs,
|
|
2423
2491
|
runDir: paths.root,
|
|
2492
|
+
resetsAt: unanimous?.resetsAt ?? null,
|
|
2424
2493
|
nextActions: first.secretDiffRefusal
|
|
2425
2494
|
? secretDiff.secretDiffNextActions(first.secretDiffRefusal)
|
|
2426
2495
|
: phase === "workspace"
|
|
@@ -2496,7 +2565,7 @@ export class Orchestrator {
|
|
|
2496
2565
|
await disposeReviewEnvelopes();
|
|
2497
2566
|
}
|
|
2498
2567
|
if (input.signal?.aborted) {
|
|
2499
|
-
return
|
|
2568
|
+
return cancelledRaceResult();
|
|
2500
2569
|
}
|
|
2501
2570
|
// Synthesis: if worthwhile, run a synthesizer as a NEW, re-checked candidate.
|
|
2502
2571
|
const synth = decideSynthesis(evidences, input.synthesis ?? "auto");
|
|
@@ -2532,11 +2601,12 @@ export class Orchestrator {
|
|
|
2532
2601
|
dirtyPolicy: "snapshot",
|
|
2533
2602
|
accessProfile: candidateAccess,
|
|
2534
2603
|
});
|
|
2604
|
+
const synthHome = this.harnessHomeFor(wsm, envelope, synthRouted, input);
|
|
2535
2605
|
const run = await this.runCandidateInEnvelope(synthRouted, envelope, "synth", "Synthesis", contract, synthesisInput.prompt, store, paths, wsm, ledger, candidateAccess, (ev) => {
|
|
2536
2606
|
const safeEv = redactHarnessEvent(ev);
|
|
2537
2607
|
safeInvoke(input.onHarnessEvent, safeEv);
|
|
2538
2608
|
log.emit("harness.event", harnessEventPayload(synthAdapter.id, "synth", safeEv));
|
|
2539
|
-
}, input.signal, undefined, input.effort, "synthesize", log, effectiveWeb, this.interactionChannelFor(input, log, runId, taskId, "synth", synthAdapter.id, synthRouted.supportsInteractive), undefined, input, false, synthesisInput.content);
|
|
2609
|
+
}, input.signal, undefined, input.effort, "synthesize", log, effectiveWeb, this.interactionChannelFor(input, log, runId, taskId, "synth", synthAdapter.id, synthRouted.supportsInteractive), undefined, input, false, synthesisInput.content, undefined, synthHome);
|
|
2540
2610
|
ledger.settle(lease.lease?.lease_id ?? "", attemptUsageCostSettlement(run.cost, run.costEstimated, run.attemptId, run.harnessId, run.telemetry.authMode, run.telemetry.usageCost));
|
|
2541
2611
|
reviewEnvelopes.push(envelope);
|
|
2542
2612
|
envelope = undefined;
|
|
@@ -2572,7 +2642,7 @@ export class Orchestrator {
|
|
|
2572
2642
|
}
|
|
2573
2643
|
}
|
|
2574
2644
|
if (input.signal?.aborted) {
|
|
2575
|
-
return
|
|
2645
|
+
return cancelledRaceResult();
|
|
2576
2646
|
}
|
|
2577
2647
|
let result;
|
|
2578
2648
|
try {
|
|
@@ -3209,26 +3279,11 @@ export class Orchestrator {
|
|
|
3209
3279
|
}
|
|
3210
3280
|
catch (err) {
|
|
3211
3281
|
const message = safeErrorMessage(err);
|
|
3212
|
-
store
|
|
3213
|
-
|
|
3214
|
-
|
|
3215
|
-
|
|
3216
|
-
|
|
3217
|
-
phase: "routing",
|
|
3218
|
-
category: routingFailure.category,
|
|
3219
|
-
safeMessage: message,
|
|
3220
|
-
runDir: paths.root,
|
|
3221
|
-
...(routingFailure.nextActions ? { nextActions: routingFailure.nextActions } : {}),
|
|
3222
|
-
});
|
|
3223
|
-
store.writeText(join(paths.finalDir, "summary.md"), `# Run ${runId} (${mode})\n\n- Lifecycle: failed\n- Phase: routing\n\n${message}\n`);
|
|
3224
|
-
log.emit("output.ready", { kind: "summary", path: "final/summary.md", state: "diagnostic" });
|
|
3225
|
-
log.emit("run.failed", {
|
|
3226
|
-
lifecycle: "failed",
|
|
3227
|
-
facts: makeOutcomeFacts("failed", { reason: "harness_failed" }),
|
|
3228
|
-
reason: "harness_failed",
|
|
3229
|
-
phase: "routing",
|
|
3230
|
-
error: message,
|
|
3231
|
-
failure_ref: "final/failure.yaml",
|
|
3282
|
+
writeRoutingFailureTerminal(store, paths, log, {
|
|
3283
|
+
runId,
|
|
3284
|
+
modeLabel: mode,
|
|
3285
|
+
message,
|
|
3286
|
+
err,
|
|
3232
3287
|
});
|
|
3233
3288
|
return {
|
|
3234
3289
|
spendUsd: ledger.spend(),
|
|
@@ -3379,7 +3434,9 @@ export class Orchestrator {
|
|
|
3379
3434
|
const knobs = this.routeSpecKnobs(routed, contract, undefined, input.effort);
|
|
3380
3435
|
const effectiveWeb = this.discloseWebUpgrade(log, routed, knobs.webPolicy, attemptId);
|
|
3381
3436
|
let run;
|
|
3437
|
+
let harnessHome;
|
|
3382
3438
|
try {
|
|
3439
|
+
harnessHome = this.harnessHomeFor(wsm, envelope, routed, input);
|
|
3383
3440
|
log.emit("harness.started", {
|
|
3384
3441
|
harness_id: adapter.id,
|
|
3385
3442
|
attempt_id: attemptId,
|
|
@@ -3393,7 +3450,8 @@ export class Orchestrator {
|
|
|
3393
3450
|
}, input.signal, undefined, input.effort, "repair", log, effectiveWeb, this.interactionChannelFor(input, log, runId, taskId, attemptId, adapter.id, routed.supportsInteractive), (streamedUsd) => {
|
|
3394
3451
|
ledger.updateHold(lease.lease?.lease_id ?? "", streamedUsd);
|
|
3395
3452
|
return ledger.tier() === "hard";
|
|
3396
|
-
}, input, true)
|
|
3453
|
+
}, input, true, // convergence runs one candidate: live deltas on (W-C4)
|
|
3454
|
+
undefined, undefined, harnessHome);
|
|
3397
3455
|
ledger.settle(lease.lease?.lease_id ?? "", attemptUsageCostSettlement(run.cost, run.costEstimated, run.attemptId, run.harnessId, run.telemetry.authMode, run.telemetry.usageCost));
|
|
3398
3456
|
log.emit("harness.completed", {
|
|
3399
3457
|
harness_id: adapter.id,
|
|
@@ -3416,8 +3474,9 @@ export class Orchestrator {
|
|
|
3416
3474
|
status: "failed",
|
|
3417
3475
|
error: message,
|
|
3418
3476
|
});
|
|
3419
|
-
store.writeYaml(join(paths.attemptsDir, attemptId, "attempt.yaml"), AC.attemptFailureRecord(attemptId, adapter.id, failureCost, "harness", message));
|
|
3477
|
+
store.writeYaml(join(paths.attemptsDir, attemptId, "attempt.yaml"), AC.attemptFailureRecord(attemptId, adapter.id, failureCost, "harness", message, appliedAttemptFacts(harnessHome, routed.adapterAccess, this.effectiveProfileId(input, adapter.id))));
|
|
3420
3478
|
run = {
|
|
3479
|
+
applied: appliedAttemptFacts(harnessHome, routed.adapterAccess, this.effectiveProfileId(input, adapter.id)),
|
|
3421
3480
|
attemptId,
|
|
3422
3481
|
harnessId: adapter.id,
|
|
3423
3482
|
label: `Attempt ${attempt}`,
|
|
@@ -3431,6 +3490,9 @@ export class Orchestrator {
|
|
|
3431
3490
|
};
|
|
3432
3491
|
}
|
|
3433
3492
|
lastRun = run;
|
|
3493
|
+
// Fail-closed twin of the candidate lane's gate: this loop terminalizes
|
|
3494
|
+
// per attempt, so the proof is spent per attempt.
|
|
3495
|
+
assertDelegatedEvidence(input.delegated === true, convergenceAccess, [run]);
|
|
3434
3496
|
attemptTelemetries.push({ attemptId, harnessId: adapter.id, telemetry: run.telemetry });
|
|
3435
3497
|
// Cancellation/deadline keeps priority over a belt failure finalized concurrently.
|
|
3436
3498
|
if (input.signal?.aborted)
|
|
@@ -4032,26 +4094,11 @@ export class Orchestrator {
|
|
|
4032
4094
|
catch (err) {
|
|
4033
4095
|
roHome.dispose();
|
|
4034
4096
|
const message = safeErrorMessage(err);
|
|
4035
|
-
store
|
|
4036
|
-
|
|
4037
|
-
|
|
4038
|
-
|
|
4039
|
-
|
|
4040
|
-
phase: "routing",
|
|
4041
|
-
category: routingFailure.category,
|
|
4042
|
-
safeMessage: message,
|
|
4043
|
-
runDir: paths.root,
|
|
4044
|
-
...(routingFailure.nextActions ? { nextActions: routingFailure.nextActions } : {}),
|
|
4045
|
-
});
|
|
4046
|
-
store.writeText(join(paths.finalDir, "summary.md"), `# Run ${runId} (plan)\n\n- Lifecycle: failed\n- Phase: routing\n\n${message}\n`);
|
|
4047
|
-
log.emit("output.ready", { kind: "summary", path: "final/summary.md", state: "diagnostic" });
|
|
4048
|
-
log.emit("run.failed", {
|
|
4049
|
-
lifecycle: "failed",
|
|
4050
|
-
facts: makeOutcomeFacts("failed", { reason: "harness_failed" }),
|
|
4051
|
-
reason: "harness_failed",
|
|
4052
|
-
phase: "routing",
|
|
4053
|
-
error: message,
|
|
4054
|
-
failure_ref: "final/failure.yaml",
|
|
4097
|
+
writeRoutingFailureTerminal(store, paths, log, {
|
|
4098
|
+
runId,
|
|
4099
|
+
modeLabel: "plan",
|
|
4100
|
+
message,
|
|
4101
|
+
err,
|
|
4055
4102
|
});
|
|
4056
4103
|
return {
|
|
4057
4104
|
spendUsd: ledger.spend(),
|
|
@@ -4428,26 +4475,11 @@ export class Orchestrator {
|
|
|
4428
4475
|
catch (err) {
|
|
4429
4476
|
roHome.dispose();
|
|
4430
4477
|
const message = safeErrorMessage(err);
|
|
4431
|
-
store
|
|
4432
|
-
|
|
4433
|
-
|
|
4434
|
-
|
|
4435
|
-
|
|
4436
|
-
phase: "routing",
|
|
4437
|
-
category: routingFailure.category,
|
|
4438
|
-
safeMessage: message,
|
|
4439
|
-
runDir: paths.root,
|
|
4440
|
-
...(routingFailure.nextActions ? { nextActions: routingFailure.nextActions } : {}),
|
|
4441
|
-
});
|
|
4442
|
-
store.writeText(join(paths.finalDir, "summary.md"), `# Run ${runId} (${opts.mode})\n\n- Lifecycle: failed\n- Phase: routing\n\n${message}\n`);
|
|
4443
|
-
log.emit("output.ready", { kind: "summary", path: "final/summary.md", state: "diagnostic" });
|
|
4444
|
-
log.emit("run.failed", {
|
|
4445
|
-
lifecycle: "failed",
|
|
4446
|
-
facts: makeOutcomeFacts("failed", { reason: "harness_failed" }),
|
|
4447
|
-
reason: "harness_failed",
|
|
4448
|
-
phase: "routing",
|
|
4449
|
-
error: message,
|
|
4450
|
-
failure_ref: "final/failure.yaml",
|
|
4478
|
+
writeRoutingFailureTerminal(store, paths, log, {
|
|
4479
|
+
runId,
|
|
4480
|
+
modeLabel: opts.mode,
|
|
4481
|
+
message,
|
|
4482
|
+
err,
|
|
4451
4483
|
});
|
|
4452
4484
|
return {
|
|
4453
4485
|
runId,
|