@claudexor/orchestrator 3.1.1 → 3.1.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/attemptTelemetry.d.ts +9 -20
- package/dist/attemptTelemetry.d.ts.map +1 -1
- package/dist/attemptTelemetry.js +36 -69
- package/dist/attemptTelemetry.js.map +1 -1
- package/dist/attemptUsageCost.d.ts +31 -0
- package/dist/attemptUsageCost.d.ts.map +1 -0
- package/dist/attemptUsageCost.js +181 -0
- package/dist/attemptUsageCost.js.map +1 -0
- package/dist/budgetFailure.d.ts +3 -1
- package/dist/budgetFailure.d.ts.map +1 -1
- package/dist/budgetFailure.js.map +1 -1
- package/dist/candidateEvidence.d.ts +5 -0
- package/dist/candidateEvidence.d.ts.map +1 -1
- package/dist/candidateEvidence.js +3 -1
- package/dist/candidateEvidence.js.map +1 -1
- package/dist/candidateOutputs.d.ts +19 -0
- package/dist/candidateOutputs.d.ts.map +1 -1
- package/dist/candidateOutputs.js +201 -67
- package/dist/candidateOutputs.js.map +1 -1
- package/dist/decisionBudget.d.ts +43 -0
- package/dist/decisionBudget.d.ts.map +1 -0
- package/dist/decisionBudget.js +84 -0
- package/dist/decisionBudget.js.map +1 -0
- package/dist/decisionTerminalReconciliation.d.ts +18 -0
- package/dist/decisionTerminalReconciliation.d.ts.map +1 -0
- package/dist/decisionTerminalReconciliation.js +26 -0
- package/dist/decisionTerminalReconciliation.js.map +1 -0
- package/dist/delegation-parent-activation.d.ts +6 -0
- package/dist/delegation-parent-activation.d.ts.map +1 -0
- package/dist/delegation-parent-activation.js +13 -0
- package/dist/delegation-parent-activation.js.map +1 -0
- package/dist/delegationBudgetAuthority.d.ts +30 -0
- package/dist/delegationBudgetAuthority.d.ts.map +1 -0
- package/dist/delegationBudgetAuthority.js +157 -0
- package/dist/delegationBudgetAuthority.js.map +1 -0
- package/dist/delegationFailure.d.ts +58 -0
- package/dist/delegationFailure.d.ts.map +1 -0
- package/dist/delegationFailure.js +184 -0
- package/dist/delegationFailure.js.map +1 -0
- package/dist/delegationToolEvidence.d.ts +20 -0
- package/dist/delegationToolEvidence.d.ts.map +1 -0
- package/dist/delegationToolEvidence.js +46 -0
- package/dist/delegationToolEvidence.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/orchestrator.d.ts +23 -32
- package/dist/orchestrator.d.ts.map +1 -1
- package/dist/orchestrator.js +248 -346
- package/dist/orchestrator.js.map +1 -1
- package/dist/plan-prompt.d.ts +3 -0
- package/dist/plan-prompt.d.ts.map +1 -0
- package/dist/plan-prompt.js +28 -0
- package/dist/plan-prompt.js.map +1 -0
- package/dist/requestRequirements.d.ts +17 -0
- package/dist/requestRequirements.d.ts.map +1 -1
- package/dist/requestRequirements.js +69 -1
- package/dist/requestRequirements.js.map +1 -1
- package/dist/root-ledger.d.ts +13 -0
- package/dist/root-ledger.d.ts.map +1 -0
- package/dist/root-ledger.js +19 -0
- package/dist/root-ledger.js.map +1 -0
- package/dist/routing-failure.d.ts +6 -0
- package/dist/routing-failure.d.ts.map +1 -0
- package/dist/routing-failure.js +12 -0
- package/dist/routing-failure.js.map +1 -0
- package/dist/run-bounded.d.ts +3 -0
- package/dist/run-bounded.d.ts.map +1 -0
- package/dist/run-bounded.js +17 -0
- package/dist/run-bounded.js.map +1 -0
- package/dist/run-input-resolution.d.ts +10 -0
- package/dist/run-input-resolution.d.ts.map +1 -0
- package/dist/run-input-resolution.js +70 -0
- package/dist/run-input-resolution.js.map +1 -0
- package/dist/runTelemetryWriter.d.ts +5 -2
- package/dist/runTelemetryWriter.d.ts.map +1 -1
- package/dist/runTelemetryWriter.js +72 -1
- package/dist/runTelemetryWriter.js.map +1 -1
- package/dist/runTerminals.d.ts +34 -7
- package/dist/runTerminals.d.ts.map +1 -1
- package/dist/runTerminals.js +215 -24
- package/dist/runTerminals.js.map +1 -1
- package/dist/secretDiff.d.ts +29 -0
- package/dist/secretDiff.d.ts.map +1 -0
- package/dist/secretDiff.js +133 -0
- package/dist/secretDiff.js.map +1 -0
- package/dist/terminalOutcome.d.ts +22 -0
- package/dist/terminalOutcome.d.ts.map +1 -0
- package/dist/terminalOutcome.js +39 -0
- package/dist/terminalOutcome.js.map +1 -0
- package/package.json +17 -17
package/dist/orchestrator.js
CHANGED
|
@@ -3,6 +3,7 @@ import { observeNativeSessionEvent, preflightCredentialProfile, preflightDefault
|
|
|
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 * as AC from "./attemptUsageCost.js";
|
|
6
7
|
import { candidateRoster, convergenceOutcomeFacts, isWorkingCandidate, partitionCandidates, toCandidateEvidence, } from "./candidateEvidence.js";
|
|
7
8
|
import { capabilityIntents } from "@claudexor/gateway";
|
|
8
9
|
import { policyFindings } from "./policyFindings.js";
|
|
@@ -13,9 +14,17 @@ import { globalConfigDir, loadConfig, trustConfigPath } from "@claudexor/config"
|
|
|
13
14
|
import { AnswerAssembly, CLAUDEXOR_ARTIFACT_DIR, CLAUDEXOR_BROWSER_ARTIFACT_SUBDIR, HarnessUnavailableError, summarizeDiffPaths as diffStats, withInactivityWatchdog, } from "@claudexor/core";
|
|
14
15
|
import { assertRouteModelsAllowed } from "./modelGovernance.js";
|
|
15
16
|
import { governRouteEffort } from "./effortGovernance.js";
|
|
16
|
-
import { RequestRequirementsResolver } from "./requestRequirements.js";
|
|
17
|
+
import { isFullAccess, RequestRequirementsResolver } from "./requestRequirements.js";
|
|
18
|
+
import { activateDelegationParent } from "./delegation-parent-activation.js";
|
|
19
|
+
import { routingFailureClassification } from "./routing-failure.js";
|
|
20
|
+
export { routingFailureClassification } from "./routing-failure.js";
|
|
21
|
+
import { runBounded } from "./run-bounded.js";
|
|
22
|
+
import { planPrompt } from "./plan-prompt.js";
|
|
23
|
+
import { resolveRunInputDefaults } from "./run-input-resolution.js";
|
|
24
|
+
import { createRootLedger } from "./root-ledger.js";
|
|
25
|
+
import { arbitrationBudgetOptions, decisionBudgetSummary } from "./decisionBudget.js";
|
|
17
26
|
import { buildRevisePrompt } from "./revisePrompt.js";
|
|
18
|
-
import { cancelledResult, failTerminally, guardAnnouncedRun, writeFailure, } from "./runTerminals.js";
|
|
27
|
+
import { announcedRunContext, cancelledResult, failTerminally, guardAnnouncedRun, writeFailure, } from "./runTerminals.js";
|
|
19
28
|
import { budgetFailureRecord, classifyBudgetFailure } from "./budgetFailure.js";
|
|
20
29
|
import { assertOutputSchemaCompiles, finalizeStructuredOutput } from "./structuredOutput.js";
|
|
21
30
|
import { transientRetryDelayMs, promptWithEngineConstraints, sleep, redactHarnessEvent, harnessEventPayload, safeErrorMessage, renderSummary, observeBudgetSignals, rotateOnStall, recordCleanAttemptMetrics, envInheritance, transientRetryPolicy, reviewerTimeoutMs, harnessInactivityTimeoutMs, observeAuthSwitch, emitPrimaryDivergence, emitPoolDegraded, deliveryRefusalFailure, writeRaceDeliveryDecision, } from "./runSupport.js";
|
|
@@ -26,6 +35,8 @@ import { activePlanPointer, resolveContinuitySummary, workspaceAnchor, } from ".
|
|
|
26
35
|
import { runDiffReview } from "./diffReview.js";
|
|
27
36
|
import { rawScoutBundle, resolveDeepScanSynthesis, } from "./deepScanReducer.js";
|
|
28
37
|
import { classifyAdapterThrow, createAttemptTelemetry, observeAttemptTelemetry, setAttemptOutcome, telemetrySummary, toolWarnings, unrecoveredToolErrors, webUnsatisfied, } from "./attemptTelemetry.js";
|
|
38
|
+
import * as delegateFailure from "./delegationFailure.js";
|
|
39
|
+
import * as secretDiff from "./secretDiff.js";
|
|
29
40
|
import { dominantHarnessFailureCategory, harnessFailureNextActions } from "./harnessFailure.js";
|
|
30
41
|
import { finalizeAttempt, readOnlyNoSuccessTerminal, resolveWorkReportEnvelope, unrecoveredToolErrorFailure, unwrapWorkReportEnvelope, webEvidenceFailure, } from "./attemptFinalize.js";
|
|
31
42
|
import { buildContinuationPacket, decideContinuation, synthesizeContinuationRequest, } from "./continuation.js";
|
|
@@ -40,57 +51,15 @@ import { HarnessGateway } from "@claudexor/gateway";
|
|
|
40
51
|
import { ReadinessLedger, evaluateConvergence, failureSignature, gatesPassed, reviewCandidate, revalidateFindings, runGates, } from "@claudexor/review";
|
|
41
52
|
import { arbitrate } from "@claudexor/arbitration";
|
|
42
53
|
import { buildSynthesisPlan, decideSynthesis } from "@claudexor/synthesis";
|
|
43
|
-
import { attemptCostEvidence, attemptUsageCostSettlement, BudgetLedger, isBudgetTerminal, explainRanking, loadHarnessMetrics, promptFingerprint,
|
|
54
|
+
import { attemptCostEvidence, attemptUsageCostSettlement, BudgetLedger, isBudgetTerminal, explainRanking, loadHarnessMetrics, promptFingerprint, rankHarnesses, reviewUsageCostSettlement, } from "@claudexor/budget";
|
|
44
55
|
import { readTextSafe, appendLine, assertNoInlineSecretValues, containsSecretLikeToken, DELEGATION_ENV, hashJson, newId, noProjectRepoRoot, nowIso, redactSecrets, safeInvoke, sha256, userConfigDir, writeText, } from "@claudexor/util";
|
|
45
56
|
import { assertWriteIsolation } from "./write-isolation.js";
|
|
46
|
-
/** A routed candidate adapter plus its manifest capabilities and user settings. */
|
|
47
|
-
/** The two access profiles that map to codex `danger-full-access` / an
|
|
48
|
-
* unsandboxed lane — the only ones under which a full-access-requiring MCP
|
|
49
|
-
* injection (the belt on codex) can reach the daemon. */
|
|
50
|
-
export function isFullAccess(access) {
|
|
51
|
-
return access === "full" || access === "external_sandbox_full";
|
|
52
|
-
}
|
|
53
|
-
/**
|
|
54
|
-
* A routing preflight refusal (`RoutingPreflightError`: quality routing with no
|
|
55
|
-
* comparable user-declared tier for the intent) is a CONFIGURATION error, not a
|
|
56
|
-
* harness-availability problem (A-1/D-9/#22). Classifying it as
|
|
57
|
-
* `harness_unavailable` sent the operator to re-auth or wait for a harness; the
|
|
58
|
-
* real fix is to configure a tier or change the routing goal. Detected by the
|
|
59
|
-
* typed `code` (robust across duplicate `@claudexor/budget` package copies) so
|
|
60
|
-
* EVERY strategy's routing catch (ask/agent/plan/deep-scan/council) classifies
|
|
61
|
-
* it identically. Returns the failure category + matching remediation.
|
|
62
|
-
*/
|
|
63
|
-
export function routingFailureClassification(err) {
|
|
64
|
-
const isPreflightRefusal = !!err &&
|
|
65
|
-
typeof err === "object" &&
|
|
66
|
-
err.code === "routing_preflight_refused";
|
|
67
|
-
if (isPreflightRefusal) {
|
|
68
|
-
return { category: "config_error", nextActions: harnessFailureNextActions("config_error") };
|
|
69
|
-
}
|
|
70
|
-
return { category: "harness_unavailable" };
|
|
71
|
-
}
|
|
72
57
|
const LABELS = "ABCDEFGHIJ".split("");
|
|
73
58
|
const NO_PROJECT_ROOT = noProjectRepoRoot();
|
|
74
59
|
/** Concurrency cap for parallel candidates/explorers (locked decision: min(n, 4)). */
|
|
75
60
|
const MAX_PARALLEL_CANDIDATES = 4;
|
|
76
61
|
/** Default wait for one interactive answer before a benign decline. */
|
|
77
62
|
const DEFAULT_INTERACTION_TIMEOUT_MS = 900_000;
|
|
78
|
-
/** Run `work` over `items` with bounded concurrency, preserving item order via index. */
|
|
79
|
-
async function runBounded(items, limit, work) {
|
|
80
|
-
if (items.length === 0)
|
|
81
|
-
return;
|
|
82
|
-
const concurrency = Math.max(1, Math.min(limit, items.length));
|
|
83
|
-
let next = 0;
|
|
84
|
-
const workers = Array.from({ length: concurrency }, async () => {
|
|
85
|
-
for (;;) {
|
|
86
|
-
const idx = next++;
|
|
87
|
-
if (idx >= items.length)
|
|
88
|
-
return;
|
|
89
|
-
await work(items[idx], idx);
|
|
90
|
-
}
|
|
91
|
-
});
|
|
92
|
-
await Promise.all(workers);
|
|
93
|
-
}
|
|
94
63
|
export class Orchestrator {
|
|
95
64
|
deps;
|
|
96
65
|
gateway;
|
|
@@ -118,7 +87,7 @@ export class Orchestrator {
|
|
|
118
87
|
});
|
|
119
88
|
}
|
|
120
89
|
async run(input) {
|
|
121
|
-
|
|
90
|
+
let resolved = this.resolveRunInput(input);
|
|
122
91
|
// INV-062 at the ENGINE boundary: every surface fences prompts already,
|
|
123
92
|
// but a direct embedder (or the daemon-less local REPL fallback) reaches
|
|
124
93
|
// this entry without one. Prompts, per-run instructions, AND outputSchema
|
|
@@ -137,6 +106,21 @@ export class Orchestrator {
|
|
|
137
106
|
throw new Error(`unknown mode: ${String(resolved.mode)}`);
|
|
138
107
|
}
|
|
139
108
|
const mode = parsedMode.data;
|
|
109
|
+
if (resolved.delegate === true && mode !== "agent") {
|
|
110
|
+
throw new Error(`Delegate is an agent-only strategy (got mode=${mode})`);
|
|
111
|
+
}
|
|
112
|
+
const runId = resolved.runId ?? newId("run");
|
|
113
|
+
resolved = {
|
|
114
|
+
...resolved,
|
|
115
|
+
runId,
|
|
116
|
+
taskId: resolved.taskId ?? newId("task"),
|
|
117
|
+
};
|
|
118
|
+
if (resolved.delegate === true) {
|
|
119
|
+
resolved = {
|
|
120
|
+
...resolved,
|
|
121
|
+
delegationParentRunId: runId,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
140
124
|
const projectProtectedPaths = mode === "agent" ? this.projectConfig(resolved.repoRoot).constraints.protected_paths : [];
|
|
141
125
|
assertWriteIsolation({
|
|
142
126
|
mode,
|
|
@@ -203,11 +187,20 @@ export class Orchestrator {
|
|
|
203
187
|
case "plan":
|
|
204
188
|
return this.runPlan(resolved, announce);
|
|
205
189
|
}
|
|
190
|
+
}, async ({ runId }) => {
|
|
191
|
+
const authority = this.deps.delegationBudgetAuthority;
|
|
192
|
+
if (!authority?.hasParent(runId))
|
|
193
|
+
return;
|
|
194
|
+
authority.beginParentClose(runId);
|
|
195
|
+
await authority.waitForChildren(runId);
|
|
206
196
|
},
|
|
207
197
|
// Single per-run terminalization hook: release the routing-rationale map
|
|
208
198
|
// entry on EVERY terminal (incl. a run that died before its telemetry
|
|
209
199
|
// writer ran, which is the leak this closes).
|
|
210
|
-
(runId) =>
|
|
200
|
+
(runId) => {
|
|
201
|
+
this.routingRationaleByRun.delete(runId);
|
|
202
|
+
this.deps.delegationBudgetAuthority?.releaseRun(runId);
|
|
203
|
+
});
|
|
211
204
|
}
|
|
212
205
|
async resolveReviewers(cwd, runAuthPreference, onIgnoredSetting) {
|
|
213
206
|
if (this.deps.reviewers)
|
|
@@ -416,89 +409,11 @@ export class Orchestrator {
|
|
|
416
409
|
* expand to n. Fails loudly if nothing can perform the intent.
|
|
417
410
|
*/
|
|
418
411
|
resolveRunInput(input) {
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
const configuredPool = cfg?.global.routing.eligible_harnesses;
|
|
425
|
-
const harnesses = input.harnesses ?? (configuredPool && configuredPool.length > 0 ? configuredPool : undefined);
|
|
426
|
-
// GH #25 precedence: an explicit --primary-harness wins and is validated
|
|
427
|
-
// against the pool; else a single-item explicit pool infers itself as
|
|
428
|
-
// primary (shipped in #34); else the configured default primary applies.
|
|
429
|
-
const explicitPrimary = input.primaryHarness;
|
|
430
|
-
const configPrimary = cfg?.global.routing.primary_harness;
|
|
431
|
-
const primaryHarness = explicitPrimary ??
|
|
432
|
-
(input.harnesses?.length === 1 ? input.harnesses[0] : undefined) ??
|
|
433
|
-
configPrimary ??
|
|
434
|
-
undefined;
|
|
435
|
-
if (primaryHarness &&
|
|
436
|
-
harnesses &&
|
|
437
|
-
harnesses.length > 0 &&
|
|
438
|
-
!harnesses.includes(primaryHarness)) {
|
|
439
|
-
if (explicitPrimary) {
|
|
440
|
-
// An explicit primary must be a member of the eligible pool (authoritative).
|
|
441
|
-
throw new Error(`primary harness '${explicitPrimary}' is not in the eligible harness pool (${harnesses.join(", ")}); ` +
|
|
442
|
-
`pass --primary-harness as one of [${harnesses.join(", ")}], or add '${explicitPrimary}' to --harness`);
|
|
443
|
-
}
|
|
444
|
-
// GH #25 remainder: a MULTI-harness pool whose CONFIGURED default primary
|
|
445
|
-
// is absent, with no --primary-harness pinned, is ambiguous — the engine
|
|
446
|
-
// must not silently reroute. Refuse with a structured, copy-pasteable fix
|
|
447
|
-
// naming the pool, the missing primary, and the exact flag to add.
|
|
448
|
-
throw new HarnessUnavailableError(`ambiguous primary harness: the configured default primary '${primaryHarness}' is not in the selected pool [${harnesses.join(", ")}], ` +
|
|
449
|
-
`and no --primary-harness was given. Pin one explicitly, e.g. \`--primary-harness ${harnesses[0]}\` ` +
|
|
450
|
-
`(or another of [${harnesses.join(", ")}]).`);
|
|
451
|
-
}
|
|
452
|
-
if (input.web && input.externalContextPolicy && input.web !== input.externalContextPolicy) {
|
|
453
|
-
throw new Error(`contradictory web policy: web='${input.web}' vs externalContextPolicy='${input.externalContextPolicy}' (pass one, or equal values)`);
|
|
454
|
-
}
|
|
455
|
-
const web = input.web ?? input.externalContextPolicy ?? "auto";
|
|
456
|
-
// INV-103: scalar `model` expands only to the resolved primary, never the pool;
|
|
457
|
-
// an explicit per-harness map wins. Unknown map keys fail loudly (INV-021).
|
|
458
|
-
const knownHarnessIds = new Set(this.deps.registry.keys());
|
|
459
|
-
for (const key of Object.keys(input.models ?? {})) {
|
|
460
|
-
if (!knownHarnessIds.has(key)) {
|
|
461
|
-
throw new Error(`models map names unknown harness '${key}' (registered: ${[...knownHarnessIds].sort().join(", ")}); ` +
|
|
462
|
-
`run \`claudexor harness list --all\``);
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
const models = { ...input.models };
|
|
466
|
-
if (input.model) {
|
|
467
|
-
const scalarTarget = primaryHarness ?? (harnesses && harnesses.length === 1 ? harnesses[0] : undefined);
|
|
468
|
-
if (!scalarTarget) {
|
|
469
|
-
throw new Error(`a scalar model ('${input.model}') is ambiguous without a primary harness: ` +
|
|
470
|
-
`the pool is ${harnesses && harnesses.length > 0 ? `[${harnesses.join(", ")}]` : "auto-resolved"} — ` +
|
|
471
|
-
`set a primary harness, pass exactly one --harness, or use a harness-scoped model map`);
|
|
472
|
-
}
|
|
473
|
-
models[scalarTarget] ??= input.model;
|
|
474
|
-
}
|
|
475
|
-
// QA-035: FREEZE the config-derived per-harness default_model into the
|
|
476
|
-
// resolved model map at initial normalization, exactly like an explicit
|
|
477
|
-
// input. Without this the TaskContract records `routing_models: {}` and an
|
|
478
|
-
// Exact Retry re-resolves the model against CURRENT settings — silently
|
|
479
|
-
// changing the route after a settings edit. A per-turn/scalar value already
|
|
480
|
-
// set wins (??=). Only a known resolved pool can be frozen here; a pure
|
|
481
|
-
// auto pool's lanes are not yet known (documented seam).
|
|
482
|
-
const harnessCfg = cfg?.global.harnesses ?? {};
|
|
483
|
-
for (const hid of harnesses ?? []) {
|
|
484
|
-
const def = harnessCfg[hid]?.default_model;
|
|
485
|
-
if (def)
|
|
486
|
-
models[hid] ??= def;
|
|
487
|
-
}
|
|
488
|
-
return {
|
|
489
|
-
...input,
|
|
490
|
-
harnesses,
|
|
491
|
-
primaryHarness,
|
|
492
|
-
model: undefined,
|
|
493
|
-
models,
|
|
494
|
-
routingGoal: input.routingGoal ??
|
|
495
|
-
this.deps.routingGoal ??
|
|
496
|
-
cfg?.project.budget?.routing_goal ??
|
|
497
|
-
cfg?.global.routing.goal ??
|
|
498
|
-
"auto",
|
|
499
|
-
web,
|
|
500
|
-
externalContextPolicy: web,
|
|
501
|
-
};
|
|
412
|
+
return resolveRunInputDefaults(input, {
|
|
413
|
+
config: this.config(input.repoRoot),
|
|
414
|
+
registryIds: this.deps.registry.keys(),
|
|
415
|
+
routingGoal: this.deps.routingGoal,
|
|
416
|
+
});
|
|
502
417
|
}
|
|
503
418
|
async resolveCandidateAdapters(input, intent, ledger, log, routeContext,
|
|
504
419
|
/** QA-034: when provided, the pool-ordering rationale is recorded under this
|
|
@@ -717,6 +632,14 @@ export class Orchestrator {
|
|
|
717
632
|
access: requiredAccess,
|
|
718
633
|
}),
|
|
719
634
|
denyRequirement: this.requestRequirements.resolveDenyPaths(id, (input.denyPaths?.length ?? 0) > 0),
|
|
635
|
+
delegationRequirement: this.requestRequirements.resolveDelegation({
|
|
636
|
+
harnessId: id,
|
|
637
|
+
requested: input.delegate === true,
|
|
638
|
+
runtimeAvailable: input.delegationBelt != null,
|
|
639
|
+
manifestCapable: manifest.capability_profile.mcp_injection,
|
|
640
|
+
requiresFullAccess: manifest.capability_profile.mcp_injection_requires_full_access,
|
|
641
|
+
fullAccess: isFullAccess(requiredAccess),
|
|
642
|
+
}),
|
|
720
643
|
effortLevels: manifest.capabilities.effort_levels,
|
|
721
644
|
knownModels: manifest.capabilities.known_models,
|
|
722
645
|
// A selected profile's credential_kind IS the route (round-18 #2);
|
|
@@ -763,6 +686,7 @@ export class Orchestrator {
|
|
|
763
686
|
}
|
|
764
687
|
emitPrimaryDivergence(log, input.primaryHarness, ordered, pool, dropped);
|
|
765
688
|
const n = input.n ?? ordered.length;
|
|
689
|
+
const selectionOrder = ordered;
|
|
766
690
|
const out = [];
|
|
767
691
|
if (droppedLanes.length > 0 && !allowDuplicateFill) {
|
|
768
692
|
// QA-043: lanes were dropped from an AUTO best-of pool (an explicit pool
|
|
@@ -771,15 +695,15 @@ export class Orchestrator {
|
|
|
771
695
|
// masks the omission. Clamp to distinct survivors and disclose below.
|
|
772
696
|
// (Deep-scan sets allowDuplicateFill: its width is scout coverage, not
|
|
773
697
|
// harness diversity, so a dropped lane must not cut the scout count.)
|
|
774
|
-
for (let i = 0; i < Math.min(n,
|
|
775
|
-
out.push(
|
|
698
|
+
for (let i = 0; i < Math.min(n, selectionOrder.length); i++)
|
|
699
|
+
out.push(selectionOrder[i]);
|
|
776
700
|
}
|
|
777
701
|
else {
|
|
778
702
|
// No lane was dropped: a pool smaller than `n` is an intentional
|
|
779
703
|
// best-of-N on the available harness(es) (e.g. explicit `--harness codex
|
|
780
704
|
// -n 3`), so the historical width fill is preserved.
|
|
781
705
|
for (let i = 0; i < n; i++)
|
|
782
|
-
out.push(
|
|
706
|
+
out.push(selectionOrder[i % selectionOrder.length]);
|
|
783
707
|
}
|
|
784
708
|
// Disclose an auto-pool omission / width clamp once, with the
|
|
785
709
|
// requested-vs-effective route receipt (never silent — QA-043).
|
|
@@ -791,25 +715,24 @@ export class Orchestrator {
|
|
|
791
715
|
droppedLanes,
|
|
792
716
|
});
|
|
793
717
|
this.requestRequirements.requireEffectiveBrowser(input.browser === true, out.map((lane) => lane.browserRequirement));
|
|
794
|
-
//
|
|
795
|
-
//
|
|
796
|
-
//
|
|
797
|
-
|
|
798
|
-
const names = [...new Set(out.map((lane) => lane.adapter.id))].join(", ");
|
|
799
|
-
throw new HarnessUnavailableError(`--delegate requires a harness that can host the Claudexor delegation belt (capability_profile.mcp_injection); the routed harness(es) [${names}] cannot inject MCP servers — choose claude or codex, or drop --delegate`);
|
|
800
|
-
}
|
|
801
|
-
// A belt-injecting lane may still be UNABLE to reach the daemon at its
|
|
802
|
-
// access: codex's workspace-write seatbelt cancels the belt's daemon-crossing
|
|
803
|
-
// MCP call, so codex only hosts the belt at FULL access (same as its browser
|
|
804
|
-
// MCP). If EVERY injecting lane requires full access but runs below it, the
|
|
805
|
-
// belt would be injected only to be silently cancelled by the sandbox — the
|
|
806
|
-
// exact non-delegation this guard prevents. Refuse with the real remedy.
|
|
718
|
+
// Owner decision (2026-07-26): known PRE-START belt unavailability does
|
|
719
|
+
// not discard the requested Agent work. Continue without Delegate and emit
|
|
720
|
+
// a durable typed warning. Once a descriptor is injected, typed startup
|
|
721
|
+
// failure stays terminal in attemptTelemetry (no mid-attempt downgrade).
|
|
807
722
|
if (input.delegate === true) {
|
|
808
|
-
const
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
723
|
+
const unavailable = out
|
|
724
|
+
.map((lane) => lane.delegationRequirement)
|
|
725
|
+
.filter((resolution) => !resolution.effective);
|
|
726
|
+
if (unavailable.length > 0) {
|
|
727
|
+
log?.emit("delegation.belt.degraded", {
|
|
728
|
+
requested: true,
|
|
729
|
+
effective: out.some((lane) => lane.delegationRequirement.effective),
|
|
730
|
+
reason: unavailable[0]?.reason ?? "runtime_unavailable",
|
|
731
|
+
lanes: unavailable.map((resolution) => ({
|
|
732
|
+
harness_id: resolution.harness_id,
|
|
733
|
+
reason: resolution.reason,
|
|
734
|
+
})),
|
|
735
|
+
});
|
|
813
736
|
}
|
|
814
737
|
}
|
|
815
738
|
// outputSchema is MANDATORY (Quiz-6a): a selected lane that cannot
|
|
@@ -846,6 +769,8 @@ export class Orchestrator {
|
|
|
846
769
|
*/
|
|
847
770
|
orderPool(pool, input, intent, statusById, ledger, runId) {
|
|
848
771
|
let ordered = pool;
|
|
772
|
+
let rationale = null;
|
|
773
|
+
let selectionReason = null;
|
|
849
774
|
if (pool.length > 0) {
|
|
850
775
|
const routeLedger = ledger ?? new BudgetLedger();
|
|
851
776
|
const config = this.config(input.repoRoot).global;
|
|
@@ -927,16 +852,35 @@ export class Orchestrator {
|
|
|
927
852
|
const ranked = rankHarnesses(remaining, routeCtx)
|
|
928
853
|
.map((candidate) => byId.get(candidate.harnessId))
|
|
929
854
|
.filter((candidate) => Boolean(candidate));
|
|
930
|
-
|
|
931
|
-
// pinned to routeCtx.now so it cannot disagree with the order just taken.
|
|
932
|
-
if (runId)
|
|
933
|
-
this.routingRationaleByRun.set(runId, explainRanking(remaining, routeCtx));
|
|
855
|
+
rationale = explainRanking(remaining, routeCtx);
|
|
934
856
|
ordered = ranked;
|
|
935
857
|
}
|
|
858
|
+
if (input.delegate === true && input.primaryHarnessExplicit !== true) {
|
|
859
|
+
const delegateFirst = [
|
|
860
|
+
...ordered.filter((lane) => lane.delegationRequirement.effective),
|
|
861
|
+
...ordered.filter((lane) => !lane.delegationRequirement.effective),
|
|
862
|
+
];
|
|
863
|
+
if (delegateFirst.some((lane, index) => lane !== ordered[index])) {
|
|
864
|
+
ordered = delegateFirst;
|
|
865
|
+
selectionReason = "delegate_effective_first";
|
|
866
|
+
}
|
|
867
|
+
}
|
|
936
868
|
if (input.primaryHarness) {
|
|
937
869
|
const primary = ordered.find((r) => r.adapter.id === input.primaryHarness);
|
|
938
|
-
if (primary)
|
|
870
|
+
if (primary && primary !== ordered[0]) {
|
|
939
871
|
ordered = [primary, ...ordered.filter((r) => r !== primary)];
|
|
872
|
+
selectionReason = "explicit_primary";
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
// QA-034: persist the FINAL selected order, including request constraints
|
|
876
|
+
// that intentionally override the underlying cost/quota ranking. This is
|
|
877
|
+
// what keeps route evidence aligned with the lane actually executed.
|
|
878
|
+
if (runId && rationale) {
|
|
879
|
+
this.routingRationaleByRun.set(runId, {
|
|
880
|
+
...rationale,
|
|
881
|
+
order: ordered.map((lane) => lane.adapter.id),
|
|
882
|
+
reason: selectionReason ?? rationale.reason,
|
|
883
|
+
});
|
|
940
884
|
}
|
|
941
885
|
return ordered;
|
|
942
886
|
}
|
|
@@ -1083,6 +1027,11 @@ export class Orchestrator {
|
|
|
1083
1027
|
created_at: nowIso(),
|
|
1084
1028
|
repo: { root: input.repoRoot, base_ref: input.baseRef ?? "HEAD", dirty_policy: "snapshot" },
|
|
1085
1029
|
mode: { kind: mode },
|
|
1030
|
+
delegation_requested: input.delegate === true,
|
|
1031
|
+
run_lineage: {
|
|
1032
|
+
parent_run_id: input.parentRunId ?? null,
|
|
1033
|
+
delegated_from_run_id: input.delegatedFromRunId ?? null,
|
|
1034
|
+
},
|
|
1086
1035
|
user_intent: { raw: redactSecrets(input.prompt) },
|
|
1087
1036
|
// Redacted for symmetry with user_intent.raw — a no-op on fenced input
|
|
1088
1037
|
// (the inline-secret fence already blocked any secret-like value at every
|
|
@@ -1168,13 +1117,15 @@ export class Orchestrator {
|
|
|
1168
1117
|
* a WRITING agent intent (the delegator integrates results in its workspace;
|
|
1169
1118
|
* read lanes and reviewers have nothing to delegate). */
|
|
1170
1119
|
delegationBeltFor(input, intent, routed, resolvedBudget) {
|
|
1171
|
-
if (!input?.delegate ||
|
|
1120
|
+
if (!input?.delegate ||
|
|
1121
|
+
!input.delegationBelt ||
|
|
1122
|
+
!input.delegationParentRunId ||
|
|
1123
|
+
!routed.delegationRequirement.effective)
|
|
1172
1124
|
return [];
|
|
1173
1125
|
// A lane that sandbox-cancels the belt below full access (codex) must NOT
|
|
1174
|
-
// receive a belt it cannot use
|
|
1175
|
-
//
|
|
1176
|
-
//
|
|
1177
|
-
// that cannot host it, so a mixed pool keeps the belt on the lanes that can.
|
|
1126
|
+
// receive a belt it cannot use. Per-lane requirement resolution records the
|
|
1127
|
+
// typed degradation, while a mixed pool keeps the belt on lanes that can
|
|
1128
|
+
// host it.
|
|
1178
1129
|
if (routed.mcpInjectionRequiresFullAccess && !isFullAccess(routed.adapterAccess))
|
|
1179
1130
|
return [];
|
|
1180
1131
|
const writingIntents = ["implement", "create_from_scratch", "repair"];
|
|
@@ -1190,6 +1141,8 @@ export class Orchestrator {
|
|
|
1190
1141
|
...input.delegationBelt,
|
|
1191
1142
|
env: {
|
|
1192
1143
|
...input.delegationBelt.env,
|
|
1144
|
+
[DELEGATION_ENV.parentRunId]: input.delegationParentRunId,
|
|
1145
|
+
[DELEGATION_ENV.repoRoot]: input.repoRoot,
|
|
1193
1146
|
[DELEGATION_ENV.budget]: JSON.stringify(resolvedBudget),
|
|
1194
1147
|
},
|
|
1195
1148
|
},
|
|
@@ -1493,7 +1446,7 @@ export class Orchestrator {
|
|
|
1493
1446
|
const browserServerName = spec.browser ? "browser" : null;
|
|
1494
1447
|
const telemetry = createAttemptTelemetry(knobs.webPolicy, contract.external_context.web_required ||
|
|
1495
1448
|
knobs.webPolicy === "cached" ||
|
|
1496
|
-
knobs.webPolicy === "live", effectiveWebMode ?? knobs.webPolicy, [routed.browserRequirement, routed.denyRequirement], knobs.model, beltServerName, browserServerName);
|
|
1449
|
+
knobs.webPolicy === "live", effectiveWebMode ?? knobs.webPolicy, [routed.browserRequirement, routed.denyRequirement, routed.delegationRequirement], knobs.model, beltServerName, browserServerName);
|
|
1497
1450
|
let activeSessionId = spec.session_id;
|
|
1498
1451
|
const onAbort = () => {
|
|
1499
1452
|
void adapter.cancel?.(activeSessionId)?.catch(() => { });
|
|
@@ -1573,10 +1526,9 @@ export class Orchestrator {
|
|
|
1573
1526
|
observeNativeSessionEvent(runInput, adapter.id, safeEv);
|
|
1574
1527
|
observeAuthSwitch(log, adapter.id, attemptId, safeEv);
|
|
1575
1528
|
observeAttemptTelemetry(telemetry, safeEv);
|
|
1576
|
-
// QA-024: the injected delegation belt's MCP server reported
|
|
1577
|
-
//
|
|
1578
|
-
//
|
|
1579
|
-
// `mcp__<belt>__*` tools and may degrade to its own native subagent.
|
|
1529
|
+
// QA-024: the injected delegation belt's MCP server reported a
|
|
1530
|
+
// terminal startup failure. Disclose it ONCE while live; recoverable
|
|
1531
|
+
// exact tool-result failures are evaluated at attempt finalization.
|
|
1580
1532
|
// The terminal outcome axis (delegationBeltUnavailable) reflects it
|
|
1581
1533
|
// too; this event makes the failure visible while the run is live.
|
|
1582
1534
|
if (telemetry.delegationBelt.requested &&
|
|
@@ -1727,20 +1679,20 @@ export class Orchestrator {
|
|
|
1727
1679
|
if (webUnsatisfied(telemetry)) {
|
|
1728
1680
|
errors.push(webEvidenceFailure(telemetry.web));
|
|
1729
1681
|
}
|
|
1730
|
-
const diff = await wsm.diff(envelope);
|
|
1731
1682
|
// D-16: un-nest {work_report, output} so answer.md persists the OUTPUT, not the envelope.
|
|
1732
1683
|
const unwrapped = unwrapWorkReportEnvelope(answer.machineText() ?? "", workReportMode, {
|
|
1733
1684
|
sideToolReport: telemetry.sideToolWorkReport ?? undefined,
|
|
1734
1685
|
});
|
|
1735
|
-
// X119: persist the VERBATIM redacted bytes; trim ONLY for the emptiness check.
|
|
1736
1686
|
const redacted = redactSecrets(unwrapped.deliverable);
|
|
1737
|
-
const
|
|
1687
|
+
const candidateAnswer = redacted.trim().length > 0 ? redacted : undefined;
|
|
1688
|
+
const { diff, refusal: secretDiffRefusal } = await secretDiff.quarantineCandidateWorkspace(wsm, envelope, inPlaceEnvelope, candidateAnswer);
|
|
1689
|
+
harnessErrored = secretDiff.recordSecretDiffRefusal(secretDiffRefusal, errors, harnessErrored);
|
|
1690
|
+
const answerText = secretDiffRefusal ? undefined : candidateAnswer;
|
|
1738
1691
|
const deliverableEvidence = diff.trim().length > 0 || Boolean(answerText);
|
|
1739
|
-
// Cancelled attempts skip gates
|
|
1740
|
-
// stop NOW; running a 600s-per-gate suite after the abort delays the ack
|
|
1692
|
+
// Cancelled attempts skip gates: running a 600s-per-gate suite delays the ack
|
|
1741
1693
|
// and burns compute on a result nobody will adopt. Diff/attempt.yaml
|
|
1742
1694
|
// still land, so partial work stays inspectable.
|
|
1743
|
-
const gateSignalAborted = signal?.aborted === true;
|
|
1695
|
+
const gateSignalAborted = signal?.aborted === true || secretDiffRefusal !== undefined;
|
|
1744
1696
|
if (!gateSignalAborted) {
|
|
1745
1697
|
log?.emit("gate.started", {
|
|
1746
1698
|
attempt_id: attemptId,
|
|
@@ -1770,6 +1722,14 @@ export class Orchestrator {
|
|
|
1770
1722
|
});
|
|
1771
1723
|
}
|
|
1772
1724
|
const webBlocked = webUnsatisfied(telemetry);
|
|
1725
|
+
// A descriptor that was injected and then reported failed is past the
|
|
1726
|
+
// pre-start degradation boundary. Hard-fail this attempt; never continue as
|
|
1727
|
+
// ordinary Agent or let a native vendor subagent masquerade as belt work.
|
|
1728
|
+
const delegationError = delegateFailure.delegationFailureError(telemetry);
|
|
1729
|
+
if (delegationError) {
|
|
1730
|
+
harnessErrored = true;
|
|
1731
|
+
errors.push(delegationError);
|
|
1732
|
+
}
|
|
1773
1733
|
// D-16 unified finalizer: fold the WorkReport / context signals into the
|
|
1774
1734
|
// deliverable + work_state. A broken contract on a constrained route
|
|
1775
1735
|
// elevates harnessErrored (never a prose success).
|
|
@@ -1794,14 +1754,6 @@ export class Orchestrator {
|
|
|
1794
1754
|
workState: finalized.workState,
|
|
1795
1755
|
});
|
|
1796
1756
|
const attemptDir = join(paths.attemptsDir, attemptId);
|
|
1797
|
-
try {
|
|
1798
|
-
assertNoSecretLikeTokens("candidate patch diff", diff);
|
|
1799
|
-
}
|
|
1800
|
-
catch (err) {
|
|
1801
|
-
// The stream already settled real spend; a post-stream assertion throw
|
|
1802
|
-
// must carry it so the slot catch settles the TRUE cost, not 0.
|
|
1803
|
-
throw Object.assign(err instanceof Error ? err : new Error(String(err)), { costUsd: cost });
|
|
1804
|
-
}
|
|
1805
1757
|
recordCleanAttemptMetrics(globalConfigDir(), adapter.id, {
|
|
1806
1758
|
costUsd: cost,
|
|
1807
1759
|
streamMs: attemptStreamEndedMs - attemptStartedMs,
|
|
@@ -1809,11 +1761,13 @@ export class Orchestrator {
|
|
|
1809
1761
|
aborted: signal?.aborted === true,
|
|
1810
1762
|
authMode: telemetry.authMode,
|
|
1811
1763
|
});
|
|
1812
|
-
const producedFiles = writeCandidateAttemptArtifacts({
|
|
1764
|
+
const producedFiles = AC.withAttemptFailureCost(() => writeCandidateAttemptArtifacts({
|
|
1813
1765
|
store,
|
|
1814
1766
|
attemptDir,
|
|
1815
1767
|
worktreePath: envelope.worktree_path,
|
|
1816
1768
|
diff,
|
|
1769
|
+
persistPatch: secretDiffRefusal === undefined,
|
|
1770
|
+
persistProducedMedia: secretDiffRefusal === undefined,
|
|
1817
1771
|
answerText,
|
|
1818
1772
|
record: {
|
|
1819
1773
|
attempt_id: attemptId,
|
|
@@ -1825,9 +1779,14 @@ export class Orchestrator {
|
|
|
1825
1779
|
errors: errors.slice(0, 5),
|
|
1826
1780
|
...telemetrySummary(telemetry),
|
|
1827
1781
|
outcome: telemetry.outcome,
|
|
1782
|
+
...(secretDiffRefusal ? { secret_diff_refusal: secretDiffRefusal } : {}),
|
|
1828
1783
|
gates: gates.map((g) => ({ id: g.id, status: g.status })),
|
|
1829
1784
|
branch: envelope.branch_name,
|
|
1830
1785
|
},
|
|
1786
|
+
}), {
|
|
1787
|
+
totalUsd: cost,
|
|
1788
|
+
estimated: costEstimated,
|
|
1789
|
+
settlement: attemptUsageCostSettlement(cost, costEstimated, attemptId, adapter.id, telemetry.authMode, telemetry.usageCost),
|
|
1831
1790
|
});
|
|
1832
1791
|
return {
|
|
1833
1792
|
attemptId,
|
|
@@ -1844,6 +1803,7 @@ export class Orchestrator {
|
|
|
1844
1803
|
costEstimated,
|
|
1845
1804
|
errors: errors.slice(0, 8),
|
|
1846
1805
|
telemetry,
|
|
1806
|
+
...(secretDiffRefusal ? { secretDiffRefusal } : {}),
|
|
1847
1807
|
outcomeClass: finalized.outcomeClass,
|
|
1848
1808
|
};
|
|
1849
1809
|
}
|
|
@@ -1989,16 +1949,7 @@ export class Orchestrator {
|
|
|
1989
1949
|
safeInvoke(input.onRunStart, { runId, taskId, runDir: paths.root });
|
|
1990
1950
|
log.emit("run.created", { mode, prompt: redactSecrets(input.prompt) });
|
|
1991
1951
|
const ledger = this.rootLedger(input, contract, log);
|
|
1992
|
-
announce?.({
|
|
1993
|
-
log,
|
|
1994
|
-
store,
|
|
1995
|
-
paths,
|
|
1996
|
-
runId,
|
|
1997
|
-
taskId,
|
|
1998
|
-
mode,
|
|
1999
|
-
phase: "race",
|
|
2000
|
-
spend: () => ledger.spend(),
|
|
2001
|
-
});
|
|
1952
|
+
announce?.(announcedRunContext({ log, store, paths, runId, taskId, mode, phase: "race" }, ledger, () => this.deps.delegationBudgetAuthority?.hasParent(runId) === true));
|
|
2002
1953
|
store.writeYaml(join(paths.contextDir, "task.yaml"), contract);
|
|
2003
1954
|
log.emit("task.contract.created", { task_contract_hash: hashJson(contract) });
|
|
2004
1955
|
// Write modes need a git boundary for worktree isolation and honest diffs.
|
|
@@ -2087,6 +2038,7 @@ export class Orchestrator {
|
|
|
2087
2038
|
candidates: [],
|
|
2088
2039
|
};
|
|
2089
2040
|
}
|
|
2041
|
+
activateDelegationParent(this.deps.delegationBudgetAuthority, input, runId, ledger, adapters, log);
|
|
2090
2042
|
const reviewersOutcome = await this.resolveReviewersWithArtifacts(input, log, store, paths, runId, taskId, mode);
|
|
2091
2043
|
if ("failed" in reviewersOutcome)
|
|
2092
2044
|
return reviewersOutcome.failed;
|
|
@@ -2311,7 +2263,7 @@ export class Orchestrator {
|
|
|
2311
2263
|
effectiveRun = contRun;
|
|
2312
2264
|
}
|
|
2313
2265
|
catch (err) {
|
|
2314
|
-
ledger.settle(contLeaseId,
|
|
2266
|
+
ledger.settle(contLeaseId, AC.attemptFailureCost(err, "continuation-error", 0).settlement);
|
|
2315
2267
|
log.emit("harness.completed", {
|
|
2316
2268
|
harness_id: adapter.id,
|
|
2317
2269
|
attempt_id: contAttemptId,
|
|
@@ -2335,18 +2287,9 @@ export class Orchestrator {
|
|
|
2335
2287
|
envelope = undefined;
|
|
2336
2288
|
}
|
|
2337
2289
|
catch (err) {
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
// post-stream throw (e.g. the secret-token assertion) carries its
|
|
2341
|
-
// streamed spend on the error — settle the TRUE cost, never launder
|
|
2342
|
-
// real spend down to 0.
|
|
2343
|
-
const carriedCost = typeof err?.costUsd === "number"
|
|
2344
|
-
? err.costUsd
|
|
2345
|
-
: 0;
|
|
2346
|
-
ledger.settle(slot.leaseId, unknownCostSettlement("post-stream-error", carriedCost));
|
|
2290
|
+
const failureCost = AC.attemptFailureCost(err, "post-stream-error", 0);
|
|
2291
|
+
ledger.settle(slot.leaseId, failureCost.settlement);
|
|
2347
2292
|
const message = safeErrorMessage(err);
|
|
2348
|
-
// envelope is still undefined when wsm.create() itself threw — that is
|
|
2349
|
-
// a workspace-phase infrastructure failure, not a harness error.
|
|
2350
2293
|
const infraPhase = envelope === undefined ? "workspace" : "harness";
|
|
2351
2294
|
log.emit("harness.completed", {
|
|
2352
2295
|
harness_id: adapter.id,
|
|
@@ -2355,26 +2298,22 @@ export class Orchestrator {
|
|
|
2355
2298
|
error: message,
|
|
2356
2299
|
phase: infraPhase,
|
|
2357
2300
|
});
|
|
2358
|
-
|
|
2359
|
-
store.writeYaml(join(paths.attemptsDir, slot.attemptId, "attempt.yaml"), {
|
|
2360
|
-
attempt_id: slot.attemptId,
|
|
2361
|
-
harness_id: adapter.id,
|
|
2362
|
-
cost_usd: carriedCost,
|
|
2363
|
-
errored: true,
|
|
2364
|
-
phase: infraPhase,
|
|
2365
|
-
errors: [message],
|
|
2366
|
-
});
|
|
2301
|
+
store.writeYaml(join(paths.attemptsDir, slot.attemptId, "attempt.yaml"), AC.attemptFailureRecord(slot.attemptId, adapter.id, failureCost, infraPhase, message));
|
|
2367
2302
|
runsBySlot[slotIdx] = {
|
|
2368
2303
|
attemptId: slot.attemptId,
|
|
2369
2304
|
harnessId: adapter.id,
|
|
2370
2305
|
label: slot.label,
|
|
2371
2306
|
diff: "",
|
|
2372
2307
|
gates: [],
|
|
2373
|
-
cost:
|
|
2308
|
+
cost: failureCost.totalUsd,
|
|
2374
2309
|
errored: true,
|
|
2375
|
-
costEstimated:
|
|
2310
|
+
costEstimated: failureCost.estimated,
|
|
2376
2311
|
errors: [message],
|
|
2377
|
-
telemetry: createAttemptTelemetry(knobs.webPolicy, contract.external_context.web_required, effectiveWeb, [
|
|
2312
|
+
telemetry: createAttemptTelemetry(knobs.webPolicy, contract.external_context.web_required, effectiveWeb, [
|
|
2313
|
+
slot.routed.browserRequirement,
|
|
2314
|
+
slot.routed.denyRequirement,
|
|
2315
|
+
slot.routed.delegationRequirement,
|
|
2316
|
+
], knobs.model),
|
|
2378
2317
|
infraPhase,
|
|
2379
2318
|
};
|
|
2380
2319
|
}
|
|
@@ -2397,7 +2336,7 @@ export class Orchestrator {
|
|
|
2397
2336
|
// arbitration (as the race-adoption path does) would fold those user edits
|
|
2398
2337
|
// into the revert target and let a later revert clobber them.
|
|
2399
2338
|
let earlyPostTurnSha = null;
|
|
2400
|
-
if (input.inPlace
|
|
2339
|
+
if (input.inPlace && requestedSingleCandidate && runs.every((run) => !run.secretDiffRefusal)) {
|
|
2401
2340
|
try {
|
|
2402
2341
|
earlyPostTurnSha = await snapshotTree(execRoot);
|
|
2403
2342
|
}
|
|
@@ -2409,6 +2348,20 @@ export class Orchestrator {
|
|
|
2409
2348
|
await disposeReviewEnvelopes();
|
|
2410
2349
|
return cancelledResult(log, runId, taskId, mode, paths.root, cancelledCandidates(), () => this.writeRunTelemetry(store, paths, contract, runId, taskId, mode, candidateRoster(runs), null), ledger.spend(), input.signal, store);
|
|
2411
2350
|
}
|
|
2351
|
+
const failedDelegation = delegateFailure.dominantRaceCandidateFailure(runs);
|
|
2352
|
+
if (failedDelegation) {
|
|
2353
|
+
const failure = delegateFailure.candidateFailureTerminal(failedDelegation, "race");
|
|
2354
|
+
await disposeReviewEnvelopes();
|
|
2355
|
+
await delegateFailure.persistFailedInPlaceWorkProduct({
|
|
2356
|
+
...{ store, log, paths, execRoot, preTurnSha, taskId, mode },
|
|
2357
|
+
live: input.inPlace === true && failedDelegation.reviewCwd === execRoot,
|
|
2358
|
+
run: failedDelegation,
|
|
2359
|
+
postTurnSha: earlyPostTurnSha,
|
|
2360
|
+
kind: input.create === true ? "new_repo" : "patch",
|
|
2361
|
+
});
|
|
2362
|
+
this.writeRunTelemetry(store, paths, contract, runId, taskId, mode, candidateRoster(runs), null);
|
|
2363
|
+
return failTerminally(log, store, paths, runId, taskId, mode, failure.phase, failure.error, ledger.spend(), failure.metadata);
|
|
2364
|
+
}
|
|
2412
2365
|
if (runs.length === 0) {
|
|
2413
2366
|
const budgetReason = ledger.terminal();
|
|
2414
2367
|
// QA-050: when the zero-candidate cause is a budget refusal, the shared
|
|
@@ -2428,12 +2381,7 @@ export class Orchestrator {
|
|
|
2428
2381
|
why_winner: why,
|
|
2429
2382
|
evidence_facts: ["no candidates were produced"],
|
|
2430
2383
|
apply_recommendation: "continue",
|
|
2431
|
-
budget_summary:
|
|
2432
|
-
spend_usd: ledger.spend(),
|
|
2433
|
-
estimated: false,
|
|
2434
|
-
cash_usd: ledger.spend(),
|
|
2435
|
-
valuation_usd: ledger.valuation(),
|
|
2436
|
-
},
|
|
2384
|
+
budget_summary: decisionBudgetSummary(ledger),
|
|
2437
2385
|
});
|
|
2438
2386
|
store.writeText(join(paths.finalDir, "summary.md"), `# Run ${runId} (${mode})\n\n- Lifecycle: ${facts.lifecycle}${facts.reason ? ` (${facts.reason})` : ""}\n- Phase: ${agentBudgetMapping ? "budget" : "executor"}\n\n${why}\n`);
|
|
2439
2387
|
if (agentBudgetMapping) {
|
|
@@ -2480,7 +2428,7 @@ export class Orchestrator {
|
|
|
2480
2428
|
if (workingRuns.length === 0) {
|
|
2481
2429
|
await disposeReviewEnvelopes();
|
|
2482
2430
|
const first = runs[0];
|
|
2483
|
-
const phase = first.infraPhase ?? "harness";
|
|
2431
|
+
const phase = first.secretDiffRefusal ? "artifact_security" : (first.infraPhase ?? "harness");
|
|
2484
2432
|
const { facts, why: rootCause } = partitionCandidates(runs);
|
|
2485
2433
|
store.writeYaml(join(paths.arbitrationDir, "decision.yaml"), {
|
|
2486
2434
|
winner: null,
|
|
@@ -2488,12 +2436,7 @@ export class Orchestrator {
|
|
|
2488
2436
|
why_winner: rootCause,
|
|
2489
2437
|
evidence_facts: runs.map((r) => `${r.attemptId} produced no work: ${r.errors[0] ?? "unknown"}`),
|
|
2490
2438
|
apply_recommendation: "continue",
|
|
2491
|
-
budget_summary:
|
|
2492
|
-
spend_usd: ledger.spend(),
|
|
2493
|
-
estimated: false,
|
|
2494
|
-
cash_usd: ledger.spend(),
|
|
2495
|
-
valuation_usd: ledger.valuation(),
|
|
2496
|
-
},
|
|
2439
|
+
budget_summary: decisionBudgetSummary(ledger),
|
|
2497
2440
|
});
|
|
2498
2441
|
this.writeRunTelemetry(store, paths, contract, runId, taskId, mode, candidateRoster(runs), null);
|
|
2499
2442
|
store.writeText(join(paths.finalDir, "summary.md"), `# Run ${runId} (${mode})\n\n- Lifecycle: ${facts.lifecycle}\n- Phase: ${phase}\n\n${rootCause}\n`);
|
|
@@ -2513,9 +2456,11 @@ export class Orchestrator {
|
|
|
2513
2456
|
rawDetailRef: `attempts/${first.attemptId}/attempt.yaml`,
|
|
2514
2457
|
eventRefs: existingEventRefs,
|
|
2515
2458
|
runDir: paths.root,
|
|
2516
|
-
nextActions:
|
|
2517
|
-
?
|
|
2518
|
-
:
|
|
2459
|
+
nextActions: first.secretDiffRefusal
|
|
2460
|
+
? secretDiff.secretDiffNextActions(first.secretDiffRefusal)
|
|
2461
|
+
: phase === "workspace"
|
|
2462
|
+
? ["Check the project folder", "Open diagnostics", "Retry the run"]
|
|
2463
|
+
: harnessFailureNextActions(harnessCategory),
|
|
2519
2464
|
});
|
|
2520
2465
|
log.emit("output.ready", { kind: "summary", path: "final/summary.md", state: "diagnostic" });
|
|
2521
2466
|
log.emit("run.failed", {
|
|
@@ -2648,7 +2593,7 @@ export class Orchestrator {
|
|
|
2648
2593
|
}
|
|
2649
2594
|
}
|
|
2650
2595
|
catch (err) {
|
|
2651
|
-
ledger.settle(lease.lease?.lease_id ?? "",
|
|
2596
|
+
ledger.settle(lease.lease?.lease_id ?? "", AC.attemptFailureCost(err, "synthesis-error").settlement);
|
|
2652
2597
|
log.emit("harness.completed", {
|
|
2653
2598
|
attempt_id: "synth",
|
|
2654
2599
|
status: "failed",
|
|
@@ -2666,14 +2611,7 @@ export class Orchestrator {
|
|
|
2666
2611
|
}
|
|
2667
2612
|
let result;
|
|
2668
2613
|
try {
|
|
2669
|
-
result = arbitrate(evidences,
|
|
2670
|
-
spendUsd: ledger.spend(),
|
|
2671
|
-
estimatedSpend: runs.some((r) => r.costEstimated),
|
|
2672
|
-
// QA-010b: carry the settled cash + subscription-valuation totals
|
|
2673
|
-
// (reviewer panel included) onto the decision record.
|
|
2674
|
-
cashUsd: ledger.spend(),
|
|
2675
|
-
valuationUsd: ledger.valuation(),
|
|
2676
|
-
});
|
|
2614
|
+
result = arbitrate(evidences, arbitrationBudgetOptions(ledger));
|
|
2677
2615
|
}
|
|
2678
2616
|
catch (err) {
|
|
2679
2617
|
// Arbitration throws end terminally with artifacts, never as an orphan.
|
|
@@ -3086,7 +3024,7 @@ export class Orchestrator {
|
|
|
3086
3024
|
// (so a failing test gate or no_op outcome is unchanged), just unreviewed.
|
|
3087
3025
|
const hasDiff = run.diff.trim().length > 0;
|
|
3088
3026
|
// Reviewer panels spend real money: reserve before, settle the observed cost.
|
|
3089
|
-
const reviewLease = hasDiff
|
|
3027
|
+
const reviewLease = hasDiff && reviewers.length > 0
|
|
3090
3028
|
? ledger?.reserve({
|
|
3091
3029
|
taskId: taskId ?? "task",
|
|
3092
3030
|
attemptId: run.attemptId,
|
|
@@ -3119,11 +3057,16 @@ export class Orchestrator {
|
|
|
3119
3057
|
reviewSpendUsd: 0,
|
|
3120
3058
|
reviewSpendEstimated: false,
|
|
3121
3059
|
reviewCashUsd: 0,
|
|
3060
|
+
reviewCashKnowledge: "unknown",
|
|
3122
3061
|
reviewValuationUsd: 0,
|
|
3062
|
+
reviewValuationKnowledge: "unknown",
|
|
3123
3063
|
reviewUnknownUsd: 0,
|
|
3124
3064
|
};
|
|
3125
3065
|
if (reviewLease?.granted) {
|
|
3126
|
-
ledger?.settle(reviewLease.lease?.lease_id ?? "", reviewUsageCostSettlement(result.reviewCashUsd, result.reviewValuationUsd,
|
|
3066
|
+
ledger?.settle(reviewLease.lease?.lease_id ?? "", reviewUsageCostSettlement(result.reviewCashUsd, result.reviewValuationUsd, {
|
|
3067
|
+
cash: result.reviewCashKnowledge,
|
|
3068
|
+
valuation: result.reviewValuationKnowledge,
|
|
3069
|
+
}, [`attempt:${run.attemptId}`, "review:panel"], result.reviewUnknownUsd));
|
|
3127
3070
|
if ((result.reviewSpendUsd ?? 0) > 0) {
|
|
3128
3071
|
log.emit("budget.observation", {
|
|
3129
3072
|
harness_id: "review-panel",
|
|
@@ -3243,20 +3186,23 @@ export class Orchestrator {
|
|
|
3243
3186
|
const execRoot = this.execRootOf(input);
|
|
3244
3187
|
const wsm = new WorkspaceManager(execRoot);
|
|
3245
3188
|
const readiness = new ReadinessLedger();
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
|
|
3250
|
-
|
|
3251
|
-
log,
|
|
3252
|
-
store,
|
|
3253
|
-
|
|
3254
|
-
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3189
|
+
let ledger;
|
|
3190
|
+
try {
|
|
3191
|
+
ledger = this.rootLedger(input, contract, log);
|
|
3192
|
+
store.writeYaml(join(paths.contextDir, "task.yaml"), contract);
|
|
3193
|
+
safeInvoke(input.onRunStart, { runId, taskId, runDir: paths.root });
|
|
3194
|
+
log.emit("run.created", { mode, prompt: redactSecrets(input.prompt) });
|
|
3195
|
+
announce?.(announcedRunContext({ log, store, paths, runId, taskId, mode, phase: "convergence" }, ledger, () => this.deps.delegationBudgetAuthority?.hasParent(runId) === true));
|
|
3196
|
+
}
|
|
3197
|
+
catch (error) {
|
|
3198
|
+
// A delegated child attaches its scoped financial view before the run is
|
|
3199
|
+
// announced. If any fallible artifact/start callback in that narrow gap
|
|
3200
|
+
// throws, the terminal net has no run context, so detach here explicitly.
|
|
3201
|
+
if (input.delegatedFromRunId) {
|
|
3202
|
+
this.deps.delegationBudgetAuthority?.releaseRun(runId);
|
|
3203
|
+
}
|
|
3204
|
+
throw error;
|
|
3205
|
+
}
|
|
3260
3206
|
// Live (in-place) isolation deliberately tolerates non-git stateful
|
|
3261
3207
|
// environments; only envelope isolation needs the git boundary.
|
|
3262
3208
|
if (!input.inPlace) {
|
|
@@ -3334,6 +3280,7 @@ export class Orchestrator {
|
|
|
3334
3280
|
candidates: [],
|
|
3335
3281
|
};
|
|
3336
3282
|
}
|
|
3283
|
+
activateDelegationParent(this.deps.delegationBudgetAuthority, input, runId, ledger, adapterPool, log);
|
|
3337
3284
|
// Fail fast on a provably unwinnable predicate instead of burning paid
|
|
3338
3285
|
// rounds: the default convergence predicate requires a clean cross-family
|
|
3339
3286
|
// review, which needs >=2 healthy reviewer provider families.
|
|
@@ -3405,7 +3352,6 @@ export class Orchestrator {
|
|
|
3405
3352
|
const allCooledDown = () => adapterPool.every((a) => ledger.cooldownActive(a.adapter.id));
|
|
3406
3353
|
const attemptTelemetries = [];
|
|
3407
3354
|
let lastDiffStable = true;
|
|
3408
|
-
let reviewSpendEstimated = false;
|
|
3409
3355
|
try {
|
|
3410
3356
|
// The contract's ENGINE-COMPUTED effective profile drives the envelope and
|
|
3411
3357
|
// every attempt spec (parity with runRace); telemetry must never claim an
|
|
@@ -3495,30 +3441,48 @@ export class Orchestrator {
|
|
|
3495
3441
|
});
|
|
3496
3442
|
}
|
|
3497
3443
|
catch (err) {
|
|
3498
|
-
//
|
|
3499
|
-
//
|
|
3500
|
-
|
|
3444
|
+
// Setup failures remain unknown; post-stream persistence failures
|
|
3445
|
+
// carry their route-specific settlement from runCandidateInEnvelope.
|
|
3446
|
+
const failureCost = AC.attemptFailureCost(err, "attempt-error");
|
|
3447
|
+
const message = safeErrorMessage(err);
|
|
3448
|
+
ledger.settle(lease.lease?.lease_id ?? "", failureCost.settlement);
|
|
3501
3449
|
log.emit("harness.completed", {
|
|
3502
3450
|
harness_id: adapter.id,
|
|
3503
3451
|
attempt_id: attemptId,
|
|
3504
3452
|
status: "failed",
|
|
3505
|
-
error:
|
|
3453
|
+
error: message,
|
|
3506
3454
|
});
|
|
3455
|
+
store.writeYaml(join(paths.attemptsDir, attemptId, "attempt.yaml"), AC.attemptFailureRecord(attemptId, adapter.id, failureCost, "harness", message));
|
|
3507
3456
|
run = {
|
|
3508
3457
|
attemptId,
|
|
3509
3458
|
harnessId: adapter.id,
|
|
3510
3459
|
label: `Attempt ${attempt}`,
|
|
3511
3460
|
diff: "",
|
|
3512
3461
|
gates: [],
|
|
3513
|
-
cost:
|
|
3462
|
+
cost: failureCost.totalUsd,
|
|
3514
3463
|
errored: true,
|
|
3515
|
-
costEstimated:
|
|
3516
|
-
errors: [
|
|
3517
|
-
telemetry: createAttemptTelemetry(knobs.webPolicy, contract.external_context.web_required, effectiveWeb, [routed.browserRequirement, routed.denyRequirement], knobs.model),
|
|
3464
|
+
costEstimated: failureCost.estimated,
|
|
3465
|
+
errors: [message],
|
|
3466
|
+
telemetry: createAttemptTelemetry(knobs.webPolicy, contract.external_context.web_required, effectiveWeb, [routed.browserRequirement, routed.denyRequirement, routed.delegationRequirement], knobs.model),
|
|
3518
3467
|
};
|
|
3519
3468
|
}
|
|
3520
3469
|
lastRun = run;
|
|
3521
3470
|
attemptTelemetries.push({ attemptId, harnessId: adapter.id, telemetry: run.telemetry });
|
|
3471
|
+
// Cancellation/deadline keeps priority over a belt failure finalized concurrently.
|
|
3472
|
+
if (input.signal?.aborted)
|
|
3473
|
+
break;
|
|
3474
|
+
if (delegateFailure.candidateFailureKind(run)) {
|
|
3475
|
+
const failure = delegateFailure.candidateFailureTerminal(run, "convergence");
|
|
3476
|
+
await delegateFailure.persistFailedInPlaceWorkProduct({
|
|
3477
|
+
...{ store, log, paths, execRoot, preTurnSha, taskId, mode },
|
|
3478
|
+
live: input.inPlace === true,
|
|
3479
|
+
run,
|
|
3480
|
+
kind: input.create === true ? "new_repo" : "patch",
|
|
3481
|
+
attempts: attempt,
|
|
3482
|
+
});
|
|
3483
|
+
this.writeRunTelemetry(store, paths, contract, runId, taskId, mode, attemptTelemetries, null);
|
|
3484
|
+
return failTerminally(log, store, paths, runId, taskId, mode, failure.phase, failure.error, ledger.spend(), failure.metadata);
|
|
3485
|
+
}
|
|
3522
3486
|
// D-16 r8: interrupted (errored===false) would CONVERGE a partial diff
|
|
3523
3487
|
// as clean — break BEFORE review; a harness error still gate-retries.
|
|
3524
3488
|
if (run.outcomeClass === "interrupted") {
|
|
@@ -3580,11 +3544,16 @@ export class Orchestrator {
|
|
|
3580
3544
|
reviewSpendUsd: 0,
|
|
3581
3545
|
reviewSpendEstimated: false,
|
|
3582
3546
|
reviewCashUsd: 0,
|
|
3547
|
+
reviewCashKnowledge: "unknown",
|
|
3583
3548
|
reviewValuationUsd: 0,
|
|
3549
|
+
reviewValuationKnowledge: "unknown",
|
|
3584
3550
|
reviewUnknownUsd: 0,
|
|
3585
3551
|
};
|
|
3586
3552
|
if (reviewLease?.granted) {
|
|
3587
|
-
ledger.settle(reviewLease.lease?.lease_id ?? "", reviewUsageCostSettlement(reviewResult.reviewCashUsd, reviewResult.reviewValuationUsd,
|
|
3553
|
+
ledger.settle(reviewLease.lease?.lease_id ?? "", reviewUsageCostSettlement(reviewResult.reviewCashUsd, reviewResult.reviewValuationUsd, {
|
|
3554
|
+
cash: reviewResult.reviewCashKnowledge,
|
|
3555
|
+
valuation: reviewResult.reviewValuationKnowledge,
|
|
3556
|
+
}, [`attempt:${attemptId}`, "review:panel"], reviewResult.reviewUnknownUsd));
|
|
3588
3557
|
if ((reviewResult.reviewSpendUsd ?? 0) > 0) {
|
|
3589
3558
|
log.emit("budget.observation", {
|
|
3590
3559
|
harness_id: "review-panel",
|
|
@@ -3596,8 +3565,6 @@ export class Orchestrator {
|
|
|
3596
3565
|
unknown_usd: reviewResult.reviewUnknownUsd,
|
|
3597
3566
|
estimated: reviewResult.reviewSpendEstimated === true,
|
|
3598
3567
|
});
|
|
3599
|
-
if (reviewResult.reviewSpendEstimated === true)
|
|
3600
|
-
reviewSpendEstimated = true;
|
|
3601
3568
|
}
|
|
3602
3569
|
}
|
|
3603
3570
|
else if (reviewLease && !reviewLease.granted) {
|
|
@@ -3779,13 +3746,7 @@ export class Orchestrator {
|
|
|
3779
3746
|
if (lastRun && !interrupted) {
|
|
3780
3747
|
const arb = arbitrate([
|
|
3781
3748
|
toCandidateEvidence(lastRun, contract, lastFindings, lastFinalReviewClean, actualReviewVerified),
|
|
3782
|
-
],
|
|
3783
|
-
spendUsd: ledger.spend(),
|
|
3784
|
-
estimatedSpend: lastRun.costEstimated || reviewSpendEstimated,
|
|
3785
|
-
// QA-010b: settled cash + valuation (reviewer panel included).
|
|
3786
|
-
cashUsd: ledger.spend(),
|
|
3787
|
-
valuationUsd: ledger.valuation(),
|
|
3788
|
-
});
|
|
3749
|
+
], arbitrationBudgetOptions(ledger));
|
|
3789
3750
|
decision = arb.decision;
|
|
3790
3751
|
store.writeYaml(join(paths.arbitrationDir, "decision.yaml"), decision);
|
|
3791
3752
|
// A converged run adopts the arbitration axes (checks/review); an
|
|
@@ -4017,40 +3978,6 @@ export class Orchestrator {
|
|
|
4017
3978
|
reviewVerified: actualReviewVerified,
|
|
4018
3979
|
};
|
|
4019
3980
|
}
|
|
4020
|
-
/** plan mode: multi-harness planning -> aggregate -> (optional) plan review -> plan. Read-only. */
|
|
4021
|
-
/**
|
|
4022
|
-
* Wrap the user's goal in an explicit "plan, do not implement" instruction.
|
|
4023
|
-
* Without this the raw prompt ("make a racing game") reaches the harness with
|
|
4024
|
-
* only a read-only sandbox, so the model tries to BUILD it and dumps code into
|
|
4025
|
-
* the plan when writes are blocked — the v0.9 "HTML in the plan" bug. The
|
|
4026
|
-
* read-only access still enforces it; this gives the model the right job.
|
|
4027
|
-
*/
|
|
4028
|
-
planPrompt(goal) {
|
|
4029
|
-
return [
|
|
4030
|
-
`You are planning, NOT implementing. Explore the repository read-only and produce a plan another agent will execute later. Do not write files or output full implementations.`,
|
|
4031
|
-
``,
|
|
4032
|
-
`## Goal`,
|
|
4033
|
-
goal,
|
|
4034
|
-
``,
|
|
4035
|
-
`## Required output (markdown)`,
|
|
4036
|
-
`1. Approach — 2-3 sentences on how you'd solve this.`,
|
|
4037
|
-
`2. Steps — a numbered list; each step names the file(s) it touches and what changes.`,
|
|
4038
|
-
`3. Risks & edge cases.`,
|
|
4039
|
-
`4. End your response with a section titled exactly:`,
|
|
4040
|
-
``,
|
|
4041
|
-
`## Open Questions`,
|
|
4042
|
-
``,
|
|
4043
|
-
`List every decision the user must make before implementation, one per bullet, in EXACTLY this format:`,
|
|
4044
|
-
``,
|
|
4045
|
-
`- [single] <question> :: <option A> :: <option B>`,
|
|
4046
|
-
`- [multi] <question> :: <option A> :: <option B>`,
|
|
4047
|
-
`- [text] <question that has no good fixed options>`,
|
|
4048
|
-
``,
|
|
4049
|
-
`Rules: [single] = pick exactly one; [multi] = pick one or more; [text] = free-form (no "::" options). Ground every option in THIS repository. If nothing is ambiguous, write a single bullet: - (none)`,
|
|
4050
|
-
``,
|
|
4051
|
-
`Keep it concise. Reference real paths you found. Do NOT paste large code blocks; describe the change instead.`,
|
|
4052
|
-
].join("\n");
|
|
4053
|
-
}
|
|
4054
3981
|
/** One read-only planner spawn shared by solo fallback, Council drafts, and merge. */
|
|
4055
3982
|
async runPlannerAttempt(args) {
|
|
4056
3983
|
const { input, contract, taskId, runId, log, store, paths, ledger, routed, attemptId } = args;
|
|
@@ -4309,16 +4236,7 @@ export class Orchestrator {
|
|
|
4309
4236
|
safeInvoke(input.onRunStart, { runId, taskId, runDir: paths.root });
|
|
4310
4237
|
log.emit("run.created", { mode: "plan", prompt: redactSecrets(input.prompt) });
|
|
4311
4238
|
const ledger = this.rootLedger(input, contract, log);
|
|
4312
|
-
announce?.({
|
|
4313
|
-
log,
|
|
4314
|
-
store,
|
|
4315
|
-
paths,
|
|
4316
|
-
runId,
|
|
4317
|
-
taskId,
|
|
4318
|
-
mode: "plan",
|
|
4319
|
-
phase: "plan",
|
|
4320
|
-
spend: () => ledger.spend(),
|
|
4321
|
-
});
|
|
4239
|
+
announce?.(announcedRunContext({ log, store, paths, runId, taskId, mode: "plan", phase: "plan" }, ledger, () => this.deps.delegationBudgetAuthority?.hasParent(runId) === true));
|
|
4322
4240
|
store.writeYaml(join(paths.contextDir, "task.yaml"), contract);
|
|
4323
4241
|
log.emit("task.contract.created", { task_contract_hash: hashJson(contract) });
|
|
4324
4242
|
// W3.3: ONE resolved read-only context — the routing point-probe and every
|
|
@@ -4453,7 +4371,7 @@ export class Orchestrator {
|
|
|
4453
4371
|
attemptId,
|
|
4454
4372
|
laneRun,
|
|
4455
4373
|
fallbackHome: roHome.env,
|
|
4456
|
-
promptBody:
|
|
4374
|
+
promptBody: planPrompt(input.prompt) + contextSection,
|
|
4457
4375
|
intent: "plan",
|
|
4458
4376
|
});
|
|
4459
4377
|
if (outcome.budgetDenied) {
|
|
@@ -4576,7 +4494,7 @@ export class Orchestrator {
|
|
|
4576
4494
|
runPlannerAttempt: (a) => this.runPlannerAttempt(a),
|
|
4577
4495
|
writeRunTelemetry: (store, paths, contract, runId, taskId, mode, attempts, finalAttemptId) => this.writeRunTelemetry(store, paths, contract, runId, taskId, mode, attempts, finalAttemptId),
|
|
4578
4496
|
execRootOf: (input) => this.execRootOf(input),
|
|
4579
|
-
planPrompt
|
|
4497
|
+
planPrompt,
|
|
4580
4498
|
};
|
|
4581
4499
|
}
|
|
4582
4500
|
/** ask: one selected harness answers read-only questions; no patch/apply controls. */
|
|
@@ -4605,21 +4523,14 @@ export class Orchestrator {
|
|
|
4605
4523
|
resolvePaidBudget(inputBudget, cfg) {
|
|
4606
4524
|
return inputBudget ?? this.deps.paidBudget ?? cfg.global.budget.paid_budget_per_run;
|
|
4607
4525
|
}
|
|
4608
|
-
rootLedger(
|
|
4609
|
-
|
|
4610
|
-
|
|
4611
|
-
|
|
4612
|
-
|
|
4613
|
-
|
|
4614
|
-
|
|
4615
|
-
cash_spend_usd: cashSpendUsd,
|
|
4616
|
-
valuation_usd: valuationUsd,
|
|
4617
|
-
}),
|
|
4526
|
+
rootLedger(input, contract, log) {
|
|
4527
|
+
return createRootLedger({
|
|
4528
|
+
input,
|
|
4529
|
+
contract,
|
|
4530
|
+
log,
|
|
4531
|
+
authority: this.deps.delegationBudgetAuthority,
|
|
4532
|
+
quotaSnapshots: this.deps.quotaSnapshots?.() ?? [],
|
|
4618
4533
|
});
|
|
4619
|
-
for (const snapshot of this.deps.quotaSnapshots?.() ?? []) {
|
|
4620
|
-
ledger.observeQuotaSnapshot(snapshot);
|
|
4621
|
-
}
|
|
4622
|
-
return ledger;
|
|
4623
4534
|
}
|
|
4624
4535
|
routeBillingKnowledge(input, harnessId) {
|
|
4625
4536
|
// A selected profile's credential_kind decides billing (round-18 #2).
|
|
@@ -4699,16 +4610,7 @@ export class Orchestrator {
|
|
|
4699
4610
|
safeInvoke(input.onRunStart, { runId, taskId, runDir: paths.root });
|
|
4700
4611
|
log.emit("run.created", { mode: opts.mode, prompt: redactSecrets(prompt) });
|
|
4701
4612
|
const ledger = this.rootLedger(input, contract, log);
|
|
4702
|
-
announce?.({
|
|
4703
|
-
log,
|
|
4704
|
-
store,
|
|
4705
|
-
paths,
|
|
4706
|
-
runId,
|
|
4707
|
-
taskId,
|
|
4708
|
-
mode: opts.mode,
|
|
4709
|
-
phase: "report",
|
|
4710
|
-
spend: () => ledger.spend(),
|
|
4711
|
-
});
|
|
4613
|
+
announce?.(announcedRunContext({ log, store, paths, runId, taskId, mode: opts.mode, phase: "report" }, ledger, () => this.deps.delegationBudgetAuthority?.hasParent(runId) === true));
|
|
4712
4614
|
store.writeYaml(join(paths.contextDir, "task.yaml"), contract);
|
|
4713
4615
|
log.emit("task.contract.created", { task_contract_hash: hashJson(contract) });
|
|
4714
4616
|
// The ask/deep-scan report stays bare (its scouts read the tree themselves);
|