@claudexor/orchestrator 3.2.0 → 3.3.7-rc.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/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 +89 -1
- package/dist/credential-profiles.js.map +1 -1
- package/dist/delegatedHome.d.ts +156 -0
- package/dist/delegatedHome.d.ts.map +1 -0
- package/dist/delegatedHome.js +179 -0
- package/dist/delegatedHome.js.map +1 -0
- 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 +102 -19
- package/dist/orchestrator.js.map +1 -1
- package/dist/planQuestions.js +4 -1
- package/dist/planQuestions.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/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";
|
|
@@ -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";
|
|
@@ -407,8 +408,19 @@ export class Orchestrator {
|
|
|
407
408
|
const policy = this.config(repoRoot)?.global.harnesses?.[harnessId]?.profile_policy;
|
|
408
409
|
return policy ?? { limit_action: "fail", rotation_eligible: [], headroom_threshold: 0.9 };
|
|
409
410
|
}
|
|
411
|
+
/** The quota poller's authenticated vendor evidence for THIS decision epoch,
|
|
412
|
+
* read once so every profile in the epoch is judged against one observation
|
|
413
|
+
* set. This is what makes profile readiness at admission mean the same thing
|
|
414
|
+
* it means on the Accounts card (INV-135 honesty). */
|
|
415
|
+
vendorQuotaObservations() {
|
|
416
|
+
return {
|
|
417
|
+
snapshots: this.deps.quotaSnapshots?.() ?? [],
|
|
418
|
+
absences: this.deps.quotaAbsences?.() ?? [],
|
|
419
|
+
};
|
|
420
|
+
}
|
|
410
421
|
/** Fresh profile readiness for one rotation decision epoch. Accounts uses
|
|
411
|
-
* the same probe wrapper + admission predicate when
|
|
422
|
+
* the same probe wrapper + vendor overlay + admission predicate when
|
|
423
|
+
* projecting next_up. */
|
|
412
424
|
async readyProfileIdsForRotation(input, harnessId, current, excluded = new Set()) {
|
|
413
425
|
const profiles = staticRotationCandidates({
|
|
414
426
|
registry: this.config(input.repoRoot)?.global.credential_profiles ?? [],
|
|
@@ -418,9 +430,12 @@ export class Orchestrator {
|
|
|
418
430
|
excluded,
|
|
419
431
|
});
|
|
420
432
|
const adapter = this.deps.registry.get(harnessId);
|
|
433
|
+
const quota = this.vendorQuotaObservations();
|
|
421
434
|
const entries = await Promise.all(profiles.map(async (profile) => ({
|
|
422
435
|
profile,
|
|
423
|
-
|
|
436
|
+
// Rotating INTO a profile the vendor has already rejected would spend a
|
|
437
|
+
// whole attempt to rediscover the 401 the poller reported a minute ago.
|
|
438
|
+
status: vendorVerifiedProfileStatus(await probeCredentialProfileStatus(profile, adapter?.probeCredentialProfile?.bind(adapter)), quota),
|
|
424
439
|
})));
|
|
425
440
|
return new Set(entries
|
|
426
441
|
.filter(({ profile, status }) => profileStatusAdmits(profile, status))
|
|
@@ -576,6 +591,9 @@ export class Orchestrator {
|
|
|
576
591
|
dropped.push(detail);
|
|
577
592
|
droppedLanes.push({ harnessId, stage, detail });
|
|
578
593
|
};
|
|
594
|
+
// One observation set for the whole admission pass, so two lanes cannot be
|
|
595
|
+
// judged against different vendor epochs.
|
|
596
|
+
const vendorQuota = this.vendorQuotaObservations();
|
|
579
597
|
for (const id of ids) {
|
|
580
598
|
const adapter = this.deps.registry.get(id);
|
|
581
599
|
if (!adapter) {
|
|
@@ -639,6 +657,11 @@ export class Orchestrator {
|
|
|
639
657
|
profileId: this.effectiveProfileId(input, id),
|
|
640
658
|
harnessId: id,
|
|
641
659
|
probe: profileAdapter?.probeCredentialProfile?.bind(profileAdapter),
|
|
660
|
+
// `verification: passed` from the local store only means a login file
|
|
661
|
+
// is present. The poller's authenticated vendor call is the only
|
|
662
|
+
// liveness evidence we have, and admission is the surface that must
|
|
663
|
+
// act on it — otherwise the run dispatches into a revoked token.
|
|
664
|
+
quota: vendorQuota,
|
|
642
665
|
});
|
|
643
666
|
if (profileVerdict !== null) {
|
|
644
667
|
if (profileVerdict === "available") {
|
|
@@ -671,7 +694,7 @@ export class Orchestrator {
|
|
|
671
694
|
const readOnlyIntent = intent === "plan" || intent === "spec" || intent === "explain" || intent === "audit";
|
|
672
695
|
const requiredAccess = this.requestRequirements.adapterAccess(intent, manifest.capabilities.implementation_transport, readOnlyIntent
|
|
673
696
|
? "readonly"
|
|
674
|
-
: (input.access ?? this.config(input.repoRoot).trust.access_default));
|
|
697
|
+
: (input.access ?? this.config(input.repoRoot).trust.access_default), externallyConfinedLane(input.delegated === true));
|
|
675
698
|
const accessSupported = !requiredAccess || manifest.access_profiles_supported.includes(requiredAccess);
|
|
676
699
|
const webSupport = manifest.capabilities.web_policy;
|
|
677
700
|
// Match routeSpecKnobs: a per-harness web default upgrades run-level auto,
|
|
@@ -1332,11 +1355,28 @@ export class Orchestrator {
|
|
|
1332
1355
|
return { pointerLine: null };
|
|
1333
1356
|
}
|
|
1334
1357
|
}
|
|
1335
|
-
|
|
1358
|
+
/**
|
|
1359
|
+
* The ONE place an attempt's HOME and OS boundary are decided.
|
|
1360
|
+
*
|
|
1361
|
+
* Called by every caller of `runCandidateInEnvelope` INSIDE that caller's
|
|
1362
|
+
* try, so a delegated refusal is caught by the same catch that writes the
|
|
1363
|
+
* attempt record, and so the decided home is in scope there.
|
|
1364
|
+
*/
|
|
1365
|
+
harnessHomeFor(wsm, envelope, routed, runInput) {
|
|
1366
|
+
// Refuses (never silently degrades) when a delegated run cannot be confined.
|
|
1367
|
+
return scopedHarnessHome(wsm, envelope, envelope.worktree_path === envelope.repo_root, runInput?.delegated === true, routed.adapterAccess);
|
|
1368
|
+
}
|
|
1369
|
+
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,
|
|
1336
1370
|
/** D-16d: when set, the mechanical continuation checkpoint pointer for a
|
|
1337
1371
|
* one-shot fresh-session continuation — appended to the prompt so the model
|
|
1338
1372
|
* (and the offline fake) re-grounds in the exhausted attempt's partial work. */
|
|
1339
|
-
continuationPointer
|
|
1373
|
+
continuationPointer,
|
|
1374
|
+
/** Decided by `harnessHomeFor` in the CALLER, so the per-attempt applied
|
|
1375
|
+
* facts exist in the caller's catch too: an attempt that ran and then threw
|
|
1376
|
+
* must record what it ran under, not just why it stopped. REQUIRED (no
|
|
1377
|
+
* default): a silent fallback here would spawn a delegated child on the
|
|
1378
|
+
* operator's real home while the record still claimed confinement. */
|
|
1379
|
+
harnessHome) {
|
|
1340
1380
|
const adapter = routed.adapter;
|
|
1341
1381
|
const knobs = this.routeSpecKnobs(routed, contract, modelHint, effortHint);
|
|
1342
1382
|
// Isolated scoped-home sessions are never retained after disposal.
|
|
@@ -1357,7 +1397,9 @@ export class Orchestrator {
|
|
|
1357
1397
|
intent,
|
|
1358
1398
|
// Engine-derived read-only prompt constraints: protected/auto-protected
|
|
1359
1399
|
// paths PLUS the exact typed gate argv the run will execute (QA-022 FIX B).
|
|
1360
|
-
prompt: promptWithEngineConstraints(
|
|
1400
|
+
prompt: promptWithEngineConstraints(
|
|
1401
|
+
// A child running without an OS boundary is TOLD so (disclosure 2 of 3).
|
|
1402
|
+
[prompt, confinementNotice(harnessHome), laneContinuity?.pointerLine, continuationPointer]
|
|
1361
1403
|
.filter((s) => typeof s === "string" && s.length > 0)
|
|
1362
1404
|
.join("\n\n"), contract.constraints.protected_paths, contract.constraints.auto_protected_paths, contract.constraints.protected_path_approvals, contract.tests.commands),
|
|
1363
1405
|
attachments: runInput?.attachments ?? [],
|
|
@@ -1379,9 +1421,14 @@ export class Orchestrator {
|
|
|
1379
1421
|
...(inPlaceEnvelope && sessionFields?.resume_session_id
|
|
1380
1422
|
? { resume_session_id: sessionFields.resume_session_id }
|
|
1381
1423
|
: {}),
|
|
1382
|
-
// Scoped harness home
|
|
1383
|
-
// native environment so the resumed
|
|
1384
|
-
|
|
1424
|
+
// Scoped harness home for isolated envelopes AND for every delegated run;
|
|
1425
|
+
// an ordinary in-place run keeps the native environment so the resumed
|
|
1426
|
+
// vendor session is reachable. See scopedHarnessHome for the cost a
|
|
1427
|
+
// delegated in-place attempt pays for that confinement.
|
|
1428
|
+
...(harnessHome.env ? { env: harnessHome.env } : {}),
|
|
1429
|
+
// The OS boundary itself. The shared CLI run loop wraps the argv with it;
|
|
1430
|
+
// no adapter opts in, so no adapter can forget.
|
|
1431
|
+
confinement: harnessHome.confinement,
|
|
1385
1432
|
raw_context_packet: rawContextPacket,
|
|
1386
1433
|
stream_deltas: streamDeltas,
|
|
1387
1434
|
});
|
|
@@ -1392,6 +1439,9 @@ export class Orchestrator {
|
|
|
1392
1439
|
const workEnvelope = this.workReportEnvelopeFor(routed, contract, Boolean(interaction));
|
|
1393
1440
|
const workReportMode = this.applyWorkEnvelope(spec, workEnvelope);
|
|
1394
1441
|
const inactivityMs = harnessInactivityTimeoutMs(this.config(contract.repo.root));
|
|
1442
|
+
// Named once: the attempt record, the CandidateRun and the terminal gate all
|
|
1443
|
+
// read the SAME applied facts rather than three re-derivations.
|
|
1444
|
+
const applied = appliedAttemptFacts(harnessHome, spec.access, sessionFields?.credential_profile?.profile_id ?? runInput?.credentialProfileId ?? null);
|
|
1395
1445
|
const attemptStartedMs = Date.now();
|
|
1396
1446
|
const budgetSignalState = { quotaPressureDisclosed: false };
|
|
1397
1447
|
const triedProfiles = new Set(); // W5.4 failover: each profile at most once
|
|
@@ -1763,6 +1813,10 @@ export class Orchestrator {
|
|
|
1763
1813
|
...(secretDiffRefusal ? { secret_diff_refusal: secretDiffRefusal } : {}),
|
|
1764
1814
|
gates: gates.map((g) => ({ id: g.id, status: g.status })),
|
|
1765
1815
|
branch: envelope.branch_name,
|
|
1816
|
+
// Applied facts, not promises: the caller of a delegated run
|
|
1817
|
+
// verifies the confinement it asked for instead of trusting it.
|
|
1818
|
+
// Built by the SAME shape the failure path writes.
|
|
1819
|
+
...applied,
|
|
1766
1820
|
},
|
|
1767
1821
|
}), {
|
|
1768
1822
|
totalUsd: cost,
|
|
@@ -1786,6 +1840,7 @@ export class Orchestrator {
|
|
|
1786
1840
|
telemetry,
|
|
1787
1841
|
...(secretDiffRefusal ? { secretDiffRefusal } : {}),
|
|
1788
1842
|
outcomeClass: finalized.outcomeClass,
|
|
1843
|
+
applied,
|
|
1789
1844
|
};
|
|
1790
1845
|
}
|
|
1791
1846
|
interactionChannelFor(input, log, runId, taskId, attemptId, harnessId,
|
|
@@ -2055,6 +2110,9 @@ export class Orchestrator {
|
|
|
2055
2110
|
const knobs = this.routeSpecKnobs(slot.routed, contract, downgradeModel ?? undefined, input.effort);
|
|
2056
2111
|
const effectiveWeb = this.discloseWebUpgrade(log, slot.routed, knobs.webPolicy, slot.attemptId);
|
|
2057
2112
|
let envelope;
|
|
2113
|
+
// Declared OUTSIDE the try so the catch below can state what the attempt
|
|
2114
|
+
// actually ran under, not merely that it stopped.
|
|
2115
|
+
let harnessHome;
|
|
2058
2116
|
try {
|
|
2059
2117
|
log.emit("harness.started", {
|
|
2060
2118
|
harness_id: adapter.id,
|
|
@@ -2074,6 +2132,7 @@ export class Orchestrator {
|
|
|
2074
2132
|
requestedSingleCandidate &&
|
|
2075
2133
|
slot.routed.implementationTransport !== "git_patch_envelope",
|
|
2076
2134
|
});
|
|
2135
|
+
harnessHome = this.harnessHomeFor(wsm, envelope, slot.routed, input);
|
|
2077
2136
|
const run = await this.runCandidateInEnvelope(slot.routed, envelope, slot.attemptId, slot.label, contract, input.prompt, store, paths, wsm, ledger, candidateAccess, (ev) => {
|
|
2078
2137
|
const safeEv = redactHarnessEvent(ev);
|
|
2079
2138
|
safeInvoke(input.onHarnessEvent, safeEv);
|
|
@@ -2084,7 +2143,8 @@ export class Orchestrator {
|
|
|
2084
2143
|
return false;
|
|
2085
2144
|
budgetStopped = true;
|
|
2086
2145
|
return true;
|
|
2087
|
-
}, input, requestedSingleCandidate)
|
|
2146
|
+
}, input, requestedSingleCandidate, // W-C4 deltas: single-candidate chat lane only (racing = noise x N)
|
|
2147
|
+
undefined, undefined, harnessHome);
|
|
2088
2148
|
ledger.settle(slot.leaseId, attemptUsageCostSettlement(run.cost, run.costEstimated, run.attemptId, run.harnessId, run.telemetry.authMode, run.telemetry.usageCost));
|
|
2089
2149
|
log.emit("harness.completed", {
|
|
2090
2150
|
harness_id: adapter.id,
|
|
@@ -2160,7 +2220,7 @@ export class Orchestrator {
|
|
|
2160
2220
|
return false;
|
|
2161
2221
|
budgetStopped = true;
|
|
2162
2222
|
return true;
|
|
2163
|
-
}, input, requestedSingleCandidate, undefined, packet.pointerLine ?? undefined);
|
|
2223
|
+
}, input, requestedSingleCandidate, undefined, packet.pointerLine ?? undefined, harnessHome);
|
|
2164
2224
|
ledger.settle(contLeaseId, attemptUsageCostSettlement(contRun.cost, contRun.costEstimated, contRun.attemptId, contRun.harnessId, contRun.telemetry.authMode, contRun.telemetry.usageCost));
|
|
2165
2225
|
log.emit("harness.completed", {
|
|
2166
2226
|
harness_id: adapter.id,
|
|
@@ -2206,6 +2266,7 @@ export class Orchestrator {
|
|
|
2206
2266
|
const failureCost = AC.attemptFailureCost(err, "post-stream-error", 0);
|
|
2207
2267
|
ledger.settle(slot.leaseId, failureCost.settlement);
|
|
2208
2268
|
const message = safeErrorMessage(err);
|
|
2269
|
+
const declared = declaredFailure(err);
|
|
2209
2270
|
const infraPhase = envelope === undefined ? "workspace" : "harness";
|
|
2210
2271
|
log.emit("harness.completed", {
|
|
2211
2272
|
harness_id: adapter.id,
|
|
@@ -2214,7 +2275,7 @@ export class Orchestrator {
|
|
|
2214
2275
|
error: message,
|
|
2215
2276
|
phase: infraPhase,
|
|
2216
2277
|
});
|
|
2217
|
-
store.writeYaml(join(paths.attemptsDir, slot.attemptId, "attempt.yaml"), AC.attemptFailureRecord(slot.attemptId, adapter.id, failureCost, infraPhase, message));
|
|
2278
|
+
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))));
|
|
2218
2279
|
runsBySlot[slotIdx] = {
|
|
2219
2280
|
attemptId: slot.attemptId,
|
|
2220
2281
|
harnessId: adapter.id,
|
|
@@ -2231,6 +2292,11 @@ export class Orchestrator {
|
|
|
2231
2292
|
slot.routed.delegationRequirement,
|
|
2232
2293
|
], knobs.model),
|
|
2233
2294
|
infraPhase,
|
|
2295
|
+
// Keep a TYPED pre-spawn refusal (spent quota window + its reset)
|
|
2296
|
+
// alive past this catch; `message` alone would force the terminal to
|
|
2297
|
+
// read prose back out.
|
|
2298
|
+
...(declared.code ? { declaredFailure: declared } : {}),
|
|
2299
|
+
applied: appliedAttemptFacts(harnessHome, slot.routed.adapterAccess, this.effectiveProfileId(input, adapter.id)),
|
|
2234
2300
|
};
|
|
2235
2301
|
}
|
|
2236
2302
|
finally {
|
|
@@ -2240,6 +2306,9 @@ export class Orchestrator {
|
|
|
2240
2306
|
};
|
|
2241
2307
|
await runBounded(slots, Math.min(slots.length, MAX_PARALLEL_CANDIDATES), runSlot);
|
|
2242
2308
|
const runs = runsBySlot.filter((r) => r !== undefined);
|
|
2309
|
+
// Fail-closed terminal: a delegated mutating run whose attempts cannot state
|
|
2310
|
+
// their applied confinement refuses instead of passing.
|
|
2311
|
+
assertDelegatedEvidence(input.delegated === true, candidateAccess, runs);
|
|
2243
2312
|
const cancelledCandidates = () => runs.map((r) => ({
|
|
2244
2313
|
attemptId: r.attemptId,
|
|
2245
2314
|
harnessId: r.harnessId,
|
|
@@ -2363,15 +2432,21 @@ export class Orchestrator {
|
|
|
2363
2432
|
// harness cause (timeout, rate limit, crash, config) gets remediation that
|
|
2364
2433
|
// fits it, instead of a doomed "Check harness authentication".
|
|
2365
2434
|
const harnessCategory = dominantHarnessFailureCategory(first.telemetry.transientFailures);
|
|
2435
|
+
// A run speaks with a candidate's TYPED refusal only when EVERY candidate
|
|
2436
|
+
// died of the same one (candidateEvidence owns that rule); mixed causes
|
|
2437
|
+
// keep the honest harness terminal.
|
|
2438
|
+
const unanimous = unanimousDeclaredFailure(runs);
|
|
2366
2439
|
writeFailure(store, paths, {
|
|
2367
2440
|
phase,
|
|
2368
|
-
category: phase === "workspace" ? "project" : "harness_error",
|
|
2441
|
+
category: unanimous?.category ?? (phase === "workspace" ? "project" : "harness_error"),
|
|
2442
|
+
code: unanimous?.code ?? null,
|
|
2369
2443
|
harnessId: first.harnessId,
|
|
2370
2444
|
attemptId: first.attemptId,
|
|
2371
2445
|
safeMessage: rootCause,
|
|
2372
2446
|
rawDetailRef: `attempts/${first.attemptId}/attempt.yaml`,
|
|
2373
2447
|
eventRefs: existingEventRefs,
|
|
2374
2448
|
runDir: paths.root,
|
|
2449
|
+
resetsAt: unanimous?.resetsAt ?? null,
|
|
2375
2450
|
nextActions: first.secretDiffRefusal
|
|
2376
2451
|
? secretDiff.secretDiffNextActions(first.secretDiffRefusal)
|
|
2377
2452
|
: phase === "workspace"
|
|
@@ -2483,11 +2558,12 @@ export class Orchestrator {
|
|
|
2483
2558
|
dirtyPolicy: "snapshot",
|
|
2484
2559
|
accessProfile: candidateAccess,
|
|
2485
2560
|
});
|
|
2561
|
+
const synthHome = this.harnessHomeFor(wsm, envelope, synthRouted, input);
|
|
2486
2562
|
const run = await this.runCandidateInEnvelope(synthRouted, envelope, "synth", "Synthesis", contract, synthesisInput.prompt, store, paths, wsm, ledger, candidateAccess, (ev) => {
|
|
2487
2563
|
const safeEv = redactHarnessEvent(ev);
|
|
2488
2564
|
safeInvoke(input.onHarnessEvent, safeEv);
|
|
2489
2565
|
log.emit("harness.event", harnessEventPayload(synthAdapter.id, "synth", safeEv));
|
|
2490
|
-
}, input.signal, undefined, input.effort, "synthesize", log, effectiveWeb, this.interactionChannelFor(input, log, runId, taskId, "synth", synthAdapter.id, synthRouted.supportsInteractive), undefined, input, false, synthesisInput.content);
|
|
2566
|
+
}, 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);
|
|
2491
2567
|
ledger.settle(lease.lease?.lease_id ?? "", attemptUsageCostSettlement(run.cost, run.costEstimated, run.attemptId, run.harnessId, run.telemetry.authMode, run.telemetry.usageCost));
|
|
2492
2568
|
reviewEnvelopes.push(envelope);
|
|
2493
2569
|
envelope = undefined;
|
|
@@ -3329,7 +3405,9 @@ export class Orchestrator {
|
|
|
3329
3405
|
const knobs = this.routeSpecKnobs(routed, contract, undefined, input.effort);
|
|
3330
3406
|
const effectiveWeb = this.discloseWebUpgrade(log, routed, knobs.webPolicy, attemptId);
|
|
3331
3407
|
let run;
|
|
3408
|
+
let harnessHome;
|
|
3332
3409
|
try {
|
|
3410
|
+
harnessHome = this.harnessHomeFor(wsm, envelope, routed, input);
|
|
3333
3411
|
log.emit("harness.started", {
|
|
3334
3412
|
harness_id: adapter.id,
|
|
3335
3413
|
attempt_id: attemptId,
|
|
@@ -3343,7 +3421,8 @@ export class Orchestrator {
|
|
|
3343
3421
|
}, input.signal, undefined, input.effort, "repair", log, effectiveWeb, this.interactionChannelFor(input, log, runId, taskId, attemptId, adapter.id, routed.supportsInteractive), (streamedUsd) => {
|
|
3344
3422
|
ledger.updateHold(lease.lease?.lease_id ?? "", streamedUsd);
|
|
3345
3423
|
return ledger.tier() === "hard";
|
|
3346
|
-
}, input, true)
|
|
3424
|
+
}, input, true, // convergence runs one candidate: live deltas on (W-C4)
|
|
3425
|
+
undefined, undefined, harnessHome);
|
|
3347
3426
|
ledger.settle(lease.lease?.lease_id ?? "", attemptUsageCostSettlement(run.cost, run.costEstimated, run.attemptId, run.harnessId, run.telemetry.authMode, run.telemetry.usageCost));
|
|
3348
3427
|
log.emit("harness.completed", {
|
|
3349
3428
|
harness_id: adapter.id,
|
|
@@ -3366,8 +3445,9 @@ export class Orchestrator {
|
|
|
3366
3445
|
status: "failed",
|
|
3367
3446
|
error: message,
|
|
3368
3447
|
});
|
|
3369
|
-
store.writeYaml(join(paths.attemptsDir, attemptId, "attempt.yaml"), AC.attemptFailureRecord(attemptId, adapter.id, failureCost, "harness", message));
|
|
3448
|
+
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))));
|
|
3370
3449
|
run = {
|
|
3450
|
+
applied: appliedAttemptFacts(harnessHome, routed.adapterAccess, this.effectiveProfileId(input, adapter.id)),
|
|
3371
3451
|
attemptId,
|
|
3372
3452
|
harnessId: adapter.id,
|
|
3373
3453
|
label: `Attempt ${attempt}`,
|
|
@@ -3381,6 +3461,9 @@ export class Orchestrator {
|
|
|
3381
3461
|
};
|
|
3382
3462
|
}
|
|
3383
3463
|
lastRun = run;
|
|
3464
|
+
// Fail-closed twin of the candidate lane's gate: this loop terminalizes
|
|
3465
|
+
// per attempt, so the proof is spent per attempt.
|
|
3466
|
+
assertDelegatedEvidence(input.delegated === true, convergenceAccess, [run]);
|
|
3384
3467
|
attemptTelemetries.push({ attemptId, harnessId: adapter.id, telemetry: run.telemetry });
|
|
3385
3468
|
// Cancellation/deadline keeps priority over a belt failure finalized concurrently.
|
|
3386
3469
|
if (input.signal?.aborted)
|