@claudexor/orchestrator 3.17.2 → 3.19.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.
Files changed (41) hide show
  1. package/dist/auth-route-classification.d.ts +22 -1
  2. package/dist/auth-route-classification.d.ts.map +1 -1
  3. package/dist/auth-route-classification.js +38 -0
  4. package/dist/auth-route-classification.js.map +1 -1
  5. package/dist/candidateEvidence.js +1 -1
  6. package/dist/candidateEvidence.js.map +1 -1
  7. package/dist/continuity-facts.d.ts +2 -0
  8. package/dist/continuity-facts.d.ts.map +1 -1
  9. package/dist/continuity-facts.js +2 -0
  10. package/dist/continuity-facts.js.map +1 -1
  11. package/dist/continuity-summary.d.ts +3 -0
  12. package/dist/continuity-summary.d.ts.map +1 -1
  13. package/dist/continuity-summary.js +4 -3
  14. package/dist/continuity-summary.js.map +1 -1
  15. package/dist/deepScanReducer.js +1 -1
  16. package/dist/deepScanReducer.js.map +1 -1
  17. package/dist/modelGovernance.d.ts +5 -31
  18. package/dist/modelGovernance.d.ts.map +1 -1
  19. package/dist/modelGovernance.js +49 -4
  20. package/dist/modelGovernance.js.map +1 -1
  21. package/dist/orchestrator-credentials.d.ts +19 -2
  22. package/dist/orchestrator-credentials.d.ts.map +1 -1
  23. package/dist/orchestrator-credentials.js +34 -2
  24. package/dist/orchestrator-credentials.js.map +1 -1
  25. package/dist/orchestrator.d.ts +7 -11
  26. package/dist/orchestrator.d.ts.map +1 -1
  27. package/dist/orchestrator.js +77 -96
  28. package/dist/orchestrator.js.map +1 -1
  29. package/dist/planRun.d.ts +2 -5
  30. package/dist/planRun.d.ts.map +1 -1
  31. package/dist/planRun.js +11 -13
  32. package/dist/planRun.js.map +1 -1
  33. package/dist/plannerAttempt.d.ts +15 -4
  34. package/dist/plannerAttempt.d.ts.map +1 -1
  35. package/dist/plannerAttempt.js +52 -26
  36. package/dist/plannerAttempt.js.map +1 -1
  37. package/dist/processing-dispatch.d.ts +7 -5
  38. package/dist/processing-dispatch.d.ts.map +1 -1
  39. package/dist/processing-dispatch.js +65 -31
  40. package/dist/processing-dispatch.js.map +1 -1
  41. package/package.json +17 -17
@@ -1,11 +1,11 @@
1
1
  import { applyWorkEnvelope, promptWithPointers as renderPromptWithPointers, threadContextPointer, } from "./prompt-framing.js";
2
2
  import { requestRefusalFailure } from "./runTerminalResults.js";
3
3
  import { delegationBeltFor } from "./delegationBelt.js";
4
- import { bindProcessingAdmission, processingAdmissionForLease, updateProcessingStreamHold, ProcessingBudgetAdmissionError, } from "./processing-dispatch.js";
4
+ import { bindProcessingAdmission, processingAttemptCostEvidence, processingAdmissionForLease, updateProcessingStreamHold, ProcessingBudgetAdmissionError, } from "./processing-dispatch.js";
5
5
  import { cancelledCandidatesResult, emptyCandidateResult, failedCandidatesResult, } from "./candidateTerminals.js";
6
6
  import { existsSync } from "node:fs";
7
7
  import { effectiveAuthPreference, observeNativeSessionEvent, resumeSessionForProfile, rotateSpecOnTypedLimit, } from "./credential-profiles.js";
8
- import { OrchestratorCredentials, reviewerProfileResolver, rotatedSpecInLaneHome, } from "./orchestrator-credentials.js";
8
+ import { AdmissionProfileProbes, OrchestratorCredentials, reviewerProfileResolver, rotatedSpecInLaneHome, } from "./orchestrator-credentials.js";
9
9
  import { accountPoolRows } from "./account-pool.js";
10
10
  import { writeRunTelemetryArtifact } from "./runTelemetryWriter.js";
11
11
  import { buildFileBackedSynthesisInput, materializeWinnerOutputs, stageFileBackedContext, writeCandidateAttemptArtifacts, } from "./candidateOutputs.js";
@@ -18,13 +18,13 @@ import { capabilityIntents } from "@claudexor/gateway";
18
18
  import { reviewCandidateRuns, resolveEngineReview, unreviewedCandidateEvidence, evaluateUnreviewedConvergence, } from "./candidateReview.js";
19
19
  import { join } from "node:path";
20
20
  import { finalizePlanRun, runCouncilPlan, writePlanHarnessFailure, } from "./planRun.js";
21
- import { runPlannerAttempt as executePlannerAttempt, } from "./plannerAttempt.js";
21
+ import { runPlannerAttempt as executePlannerAttempt, plannerAttemptSummary, } from "./plannerAttempt.js";
22
22
  import { HarnessRunSpec, MAX_COUNCIL_MEMBERS_DEFAULT, ModeKind as ModeKindSchema, QuotaSnapshot as QuotaSnapshotSchema, isBlocking, makeOutcomeFacts, reviewAllowsApply, workStateVetoes, strictifyOutputSchema, estimateEffectiveAuthRoute, } from "@claudexor/schema";
23
23
  import { globalConfigDir, loadConfig } from "@claudexor/config";
24
24
  import { prepareRoutedProcessing, processingCostEvidence, prepareReviewerProcessing, } from "./processing-routing.js";
25
25
  import { acceptedTryOutput, AccessProfileIncompatibleError, AnswerAssembly, CLAUDEXOR_BROWSER_ARTIFACT_SUBDIR, countsAsAgentProgress, HarnessUnavailableError, summarizeDiffPaths as diffStats, withInactivityWatchdog, } from "@claudexor/core";
26
26
  import { assertRouteModelsAllowed, runModelGovernedRoute } from "./modelGovernance.js";
27
- import { authModeForCredentialRoute, authModeForPreference } from "./auth-route-classification.js";
27
+ import { authModeForCredentialRoute, authModeForPreference, authRouteEvidenceFor, } from "./auth-route-classification.js";
28
28
  import { governRouteEffort } from "./effortGovernance.js";
29
29
  import { isFullAccess, RequestRequirementsResolver } from "./requestRequirements.js";
30
30
  import { activateDelegationParent } from "./delegation-parent-activation.js";
@@ -69,7 +69,7 @@ import { HarnessGateway } from "@claudexor/gateway";
69
69
  import { ReadinessLedger, evaluateConvergence, failureSignature, gatesPassed, reviewCandidate, runGates, } from "@claudexor/review";
70
70
  import { arbitrate } from "@claudexor/arbitration";
71
71
  import { buildSynthesisPlan, decideSynthesis } from "@claudexor/synthesis";
72
- import { attemptCostEvidence, billingKnowledgeForAuthRoute, attemptUsageCostSettlement, BudgetLedger, isBudgetTerminal, explainRanking, loadHarnessMetrics, promptFingerprint, rankHarnesses, } from "@claudexor/budget";
72
+ import { billingKnowledgeForAuthRoute, attemptUsageCostSettlement, BudgetLedger, isBudgetTerminal, RoutingPreflightError, explainRanking, loadHarnessMetrics, promptFingerprint, rankHarnesses, } from "@claudexor/budget";
73
73
  import { readTextSafe, appendLine, assertNoInlineSecretValues, hashJson, newId, noProjectRepoRoot, redactSecrets, safeInvoke, sha256, userConfigDir, } from "@claudexor/util";
74
74
  const LABELS = "ABCDEFGHIJ".split("");
75
75
  const NO_PROJECT_ROOT = noProjectRepoRoot();
@@ -489,9 +489,11 @@ export class Orchestrator {
489
489
  dropped.push(detail);
490
490
  droppedLanes.push({ harnessId, stage, detail });
491
491
  };
492
- // One observation set for the whole admission pass, so two lanes cannot be
493
- // judged against different vendor epochs.
492
+ // Initial lane readiness shares one quota observation. Selection and each
493
+ // physical dispatch still consume the latest exact-profile evidence.
494
494
  const vendorQuota = this.credentials.vendorQuotaObservations();
495
+ // Billing later reuses the probe that admitted each row (#260).
496
+ const admissionProbes = new AdmissionProfileProbes();
495
497
  const liveUnusable = this.deps.credentialUnusable?.() ?? [];
496
498
  for (const id of ids) {
497
499
  const adapter = this.deps.registry.get(id);
@@ -558,6 +560,7 @@ export class Orchestrator {
558
560
  // the profile's transport, so the default store's state is not the
559
561
  // routing truth). Capability/manifest gating above still applies.
560
562
  const profileAdapter = this.deps.registry.get(id);
563
+ const profileProbe = admissionProbes.record(profileAdapter?.probeCredentialProfile?.bind(profileAdapter));
561
564
  const explicitPin = this.credentials.effectiveProfileId(input, id);
562
565
  // Pins and bound/pool rows are checked against their own readiness.
563
566
  const rows = await this.credentials.admitRouteRows({
@@ -567,7 +570,7 @@ export class Orchestrator {
567
570
  model: input.models?.[id] ?? cfgEntry?.default_model ?? null,
568
571
  quota: vendorQuota,
569
572
  unusable: liveUnusable,
570
- probe: profileAdapter?.probeCredentialProfile?.bind(profileAdapter),
573
+ probe: profileProbe,
571
574
  });
572
575
  const profileAdmitted = rows.admitted;
573
576
  if (profileAdmitted) {
@@ -661,7 +664,7 @@ export class Orchestrator {
661
664
  // the default store's sources apply only to profile-less runs.
662
665
  authRouteEstimate: this.credentials.profileAuthRoute(input, id) ??
663
666
  estimateEffectiveAuthRoute(this.authPreferenceForHarness(input.repoRoot, id, input.authPreference), status.authSources),
664
- quotaAdmission: { model: null, profile: null, route: null },
667
+ quotaAdmission: { model: null, profile: null, route: null, profileVerification: null },
665
668
  supportsSynthesize: manifest.capabilities.synthesize,
666
669
  supportsInteractive: manifest.capabilities.interactive,
667
670
  liveInput: manifest.capability_profile.live_input,
@@ -706,7 +709,7 @@ export class Orchestrator {
706
709
  const model = input.models?.[routed.adapter.id] ?? routed.settings?.defaultModel ?? null;
707
710
  let profile;
708
711
  try {
709
- profile = await this.credentials.preflightProfile(input, routed.adapter.id, model, log, routed.authRouteEstimate);
712
+ profile = await this.credentials.preflightProfile(input, routed.adapter.id, model, log, routed.authRouteEstimate, admissionProbes);
710
713
  }
711
714
  catch (err) {
712
715
  // AUTO drops typed lane-local unavailability only when a sibling
@@ -730,8 +733,16 @@ export class Orchestrator {
730
733
  : routed.authRouteEstimate === "local_session"
731
734
  ? "vendor_native"
732
735
  : null;
736
+ const profileVerification = profile
737
+ ? await this.credentials.selectedProfileBillingVerification(profile, admissionProbes)
738
+ : null;
733
739
  return {
734
- prepared: { ...routed, quotaAdmission: { model, profile, route } },
740
+ prepared: {
741
+ ...routed,
742
+ quotaAdmission: { model, profile, route, profileVerification },
743
+ paidFallback: this.config(input.repoRoot).global.routing.paid_fallback,
744
+ billingVerificationForProfile: (actual) => this.credentials.selectedProfileBillingVerification(actual),
745
+ },
735
746
  refused: null,
736
747
  };
737
748
  }));
@@ -862,13 +873,14 @@ export class Orchestrator {
862
873
  // engine default. Profile A's cooldown never excludes profile B or the
863
874
  // default on the same harness and route.
864
875
  const credentialSubjectId = r.quotaAdmission.profile?.profile_id ?? null;
865
- // QA-034: the typed auth-route evidence (doctor source verification x the
866
- // resolved route) is AUTHORITATIVE for billing knowledge in the router —
867
- // a VERIFIED native route proves subscription_entitlement, so it survives
876
+ // QA-034: the typed auth-route evidence (the admitted profile's own
877
+ // verification, else doctor source verification, x the resolved route)
878
+ // is AUTHORITATIVE for billing knowledge in the router — a VERIFIED
879
+ // native route proves subscription_entitlement, so it survives
868
880
  // paid_fallback:never and ranks with a real economy tuple instead of
869
881
  // reading as unknown/paid. Absent (unknown route) falls back to the
870
882
  // metric-derived billingKnowledge below.
871
- const authRoute = this.authRouteEvidenceFor(authMode, status?.authSources ?? []);
883
+ const authRoute = authRouteEvidenceFor(authMode, status?.authSources ?? [], r.quotaAdmission.profileVerification);
872
884
  const processingCost = processingCostEvidence(r.processing, authRoute ? billingKnowledgeForAuthRoute(authRoute) : "unknown", [`harness:${r.adapter.id}`, `profile:${credentialSubjectId ?? "default"}`]);
873
885
  return {
874
886
  harnessId: r.adapter.id,
@@ -968,30 +980,6 @@ export class Orchestrator {
968
980
  },
969
981
  };
970
982
  }
971
- /**
972
- * Typed auth-route evidence for one candidate (QA-034): the concrete
973
- * credential route the resolved auth mode maps to, plus the doctor's
974
- * verification for the source that route runs under. `local_session` →
975
- * vendor_native + the native/OAuth source verification; `api_key` →
976
- * managed_api_key + the key source verification. Unknown route → no evidence
977
- * (the router keeps its conservative metric-derived billing). Verification is
978
- * the source's typed verdict — never inferred from mere availability.
979
- */
980
- authRouteEvidenceFor(authMode, sources) {
981
- const usable = (s) => s.availability === "available" && s.verification !== "failed";
982
- if (authMode === "local_session") {
983
- const native = sources.find((s) => usable(s) && (s.source === "native_session" || s.source === "oauth_token_env"));
984
- return { route: "vendor_native", verification: native?.verification ?? "not_run" };
985
- }
986
- if (authMode === "api_key") {
987
- const key = sources.find((s) => usable(s) &&
988
- (s.source === "api_key_env" ||
989
- s.source === "api_key_flag" ||
990
- s.source === "provider_auth_file"));
991
- return { route: "managed_api_key", verification: key?.verification ?? "not_run" };
992
- }
993
- return undefined;
994
- }
995
983
  /**
996
984
  * Lazy ContextPack: built ONLY for the read-only report modes
997
985
  * (explore/plan/readonly_audit) that consume it. Persisted to
@@ -1234,6 +1222,8 @@ export class Orchestrator {
1234
1222
  processing,
1235
1223
  processingAdmission,
1236
1224
  physicalDispatchStarted,
1225
+ billingVerificationForProfile: (profile) => this.credentials.selectedProfileBillingVerification(profile),
1226
+ paidFallback: this.config(runInput.repoRoot).global.routing.paid_fallback,
1237
1227
  });
1238
1228
  const result = buildContinuation(req);
1239
1229
  // Disclose on every lane and stamp the turn (INV-137: never silent).
@@ -1319,7 +1309,7 @@ export class Orchestrator {
1319
1309
  const sessionFields = runInput
1320
1310
  ? await this.sessionSpecFields(runInput, adapter.id, knobs.model, log, routed.authRouteEstimate, routed.quotaAdmission)
1321
1311
  : undefined;
1322
- const processingAdmission = processingAdmissionForLease(ledger, processingLease.id, adapter.id, attemptId, processingLease.onDenied);
1312
+ const processingAdmission = processingAdmissionForLease(ledger, processingLease.lease, adapter.id, attemptId, processingLease.onDenied);
1323
1313
  const capturedProcessing = {
1324
1314
  processing_preference: contract.processing_preference,
1325
1315
  processing: knobs.processing?.receipt,
@@ -1327,7 +1317,7 @@ export class Orchestrator {
1327
1317
  processing_allow_paid: knobs.processingAllowPaid,
1328
1318
  };
1329
1319
  const laneContinuity = runInput
1330
- ? await this.resolveContinuity(runInput, adapter.id, sessionFields?.credential_profile?.profile_id ?? runInput.credentialProfileId ?? null, inPlaceEnvelope && !!sessionFields?.resume_session_id, sessionFields, store, paths, envelope.repo_root, log, capturedProcessing, processingAdmission, () => ledger.markPhysicalDispatchStarted(processingLease.id))
1320
+ ? await this.resolveContinuity(runInput, adapter.id, sessionFields?.credential_profile?.profile_id ?? runInput.credentialProfileId ?? null, inPlaceEnvelope && !!sessionFields?.resume_session_id, sessionFields, store, paths, envelope.repo_root, log, capturedProcessing, processingAdmission, () => ledger.markPhysicalDispatchStarted(processingLease.lease.lease_id))
1331
1321
  : null;
1332
1322
  const artifactRelativeDir = routed.browserRequirement.effective
1333
1323
  ? (wsm.ensureArtifactDirectory(envelope), wsm.ownedArtifactRelativeDirectory(envelope))
@@ -1372,16 +1362,12 @@ export class Orchestrator {
1372
1362
  });
1373
1363
  if (runInput)
1374
1364
  this.credentials.stampProfileSelection(spec, runInput, adapter.id);
1375
- bindProcessingAdmission(spec, ledger, processingLease.id, adapter.id, attemptId, processingLease.onDenied, processingAdmission);
1376
- const billingInput = runInput ?? { repoRoot: contract.repo.root };
1377
- // Keep billing route-bound until admission observes the actual prepared
1378
- // profile. The resolver is evaluated per physical dispatch; preparedCost
1379
- // gives an actual profile precedence over this pool fallback.
1380
- spec.extra["routeBillingKnowledge"] = (actual) => actual.credential_profile?.credential_kind === "api_key"
1381
- ? "metered"
1382
- : actual.credential_profile
1383
- ? "subscription_entitlement"
1384
- : this.routeBillingKnowledge(billingInput, adapter.id);
1365
+ bindProcessingAdmission(spec, ledger, processingLease.lease, adapter.id, attemptId, processingLease.onDenied, processingAdmission);
1366
+ // Profile-less processing keeps its existing route estimate. Named rows
1367
+ // receive their own verification at the shared physical-dispatch gate.
1368
+ spec.extra["routeBillingKnowledge"] = (actual) => actual.credential_profile
1369
+ ? "unknown"
1370
+ : this.routeBillingKnowledge(runInput ?? { repoRoot: contract.repo.root }, adapter.id);
1385
1371
  if (interaction)
1386
1372
  spec.extra["interactionChannel"] = interaction;
1387
1373
  const workEnvelope = this.workReportEnvelopeFor(routed, contract, Boolean(interaction));
@@ -1554,7 +1540,8 @@ export class Orchestrator {
1554
1540
  // gate and required-actions read a typed category, not a bare boolean.
1555
1541
  harnessErrored = true;
1556
1542
  errors.push(safeErrorMessage(err));
1557
- if (err instanceof ProcessingBudgetAdmissionError) {
1543
+ if (err instanceof ProcessingBudgetAdmissionError ||
1544
+ err instanceof RoutingPreflightError) {
1558
1545
  processingRefusal = err;
1559
1546
  break;
1560
1547
  }
@@ -1948,7 +1935,7 @@ export class Orchestrator {
1948
1935
  attemptId,
1949
1936
  intent: this.candidateIntent(input),
1950
1937
  harnessId: routed.adapter.id,
1951
- cost: attemptCostEvidence(routed.adapter.id, attemptId, this.reservationEstimateUsd(input, i > 0), this.routeBillingKnowledge(input, routed.adapter.id), processingCostEvidence(routed.processing, this.routeBillingKnowledge(input, routed.adapter.id), [`harness:${routed.adapter.id}`])),
1938
+ cost: processingAttemptCostEvidence(routed.adapter.id, attemptId, this.reservationEstimateUsd(input, i > 0), this.routeBillingKnowledge(input, routed.adapter.id, routed), routed.processing),
1952
1939
  });
1953
1940
  log.emit("budget.lease.created", {
1954
1941
  granted: lease.granted,
@@ -1973,7 +1960,7 @@ export class Orchestrator {
1973
1960
  routed,
1974
1961
  attemptId,
1975
1962
  label: `Candidate ${LABELS[i] ?? i + 1}`,
1976
- leaseId: lease.lease?.lease_id ?? "",
1963
+ lease: lease.lease,
1977
1964
  });
1978
1965
  }
1979
1966
  const runsBySlot = new Array(slots.length);
@@ -1984,14 +1971,14 @@ export class Orchestrator {
1984
1971
  let candidateContinuationCount = 0;
1985
1972
  const runSlot = async (slot, slotIdx) => {
1986
1973
  if (input.signal?.aborted) {
1987
- ledger.cancel(slot.leaseId);
1974
+ ledger.cancel(slot.lease.lease_id);
1988
1975
  return;
1989
1976
  }
1990
1977
  // Leases are granted upfront (before spend exists); a worker still
1991
1978
  // re-checks the circuit breaker so queued slots beyond the parallel wave
1992
1979
  // do not start after earlier candidates already blew the hard cap.
1993
1980
  if (budgetStopped || ledger.tier() === "hard") {
1994
- ledger.cancel(slot.leaseId);
1981
+ ledger.cancel(slot.lease.lease_id);
1995
1982
  log.emit("budget.lease.created", {
1996
1983
  granted: false,
1997
1984
  reason: "budget exhausted (hard cap reached)",
@@ -2055,7 +2042,7 @@ export class Orchestrator {
2055
2042
  });
2056
2043
  harnessHome = this.harnessHomeFor(wsm, envelope, slot.routed, input);
2057
2044
  const run = await this.runCandidateInEnvelope(slot.routed, envelope, slot.attemptId, slot.label, contract, input.prompt, store, paths, wsm, ledger, {
2058
- id: slot.leaseId,
2045
+ lease: slot.lease,
2059
2046
  onDenied: (denial) => {
2060
2047
  budgetStopped = true;
2061
2048
  budgetDenial ??= denial;
@@ -2065,14 +2052,14 @@ export class Orchestrator {
2065
2052
  safeInvoke(input.onHarnessEvent, safeEv);
2066
2053
  log.emit("harness.event", harnessEventPayload(adapter.id, slot.attemptId, safeEv));
2067
2054
  }, input.signal, downgradeModel ?? undefined, input.effort, this.candidateIntent(input), log, effectiveWeb, this.interactionChannelFor(input, log, runId, taskId, slot.attemptId, adapter.id, slot.routed.supportsInteractive), (streamedUsd) => {
2068
- ledger.updateHold(slot.leaseId, streamedUsd);
2055
+ ledger.updateHold(slot.lease.lease_id, streamedUsd);
2069
2056
  if (ledger.tier() !== "hard")
2070
2057
  return false;
2071
2058
  budgetStopped = true;
2072
2059
  return true;
2073
2060
  }, input, requestedSingleCandidate, // W-C4 deltas: single-candidate chat lane only (racing = noise x N)
2074
2061
  undefined, undefined, harnessHome);
2075
- ledger.settle(slot.leaseId, attemptUsageCostSettlement(run.cost, run.costEstimated, run.attemptId, run.harnessId, run.telemetry.authMode, run.telemetry.usageCost));
2062
+ ledger.settle(slot.lease.lease_id, attemptUsageCostSettlement(run.cost, run.costEstimated, run.attemptId, run.harnessId, run.telemetry.authMode, run.telemetry.usageCost));
2076
2063
  log.emit("harness.completed", {
2077
2064
  harness_id: adapter.id,
2078
2065
  attempt_id: slot.attemptId,
@@ -2118,7 +2105,7 @@ export class Orchestrator {
2118
2105
  attemptId: contAttemptId,
2119
2106
  intent: this.candidateIntent(input),
2120
2107
  harnessId: adapter.id,
2121
- cost: attemptCostEvidence(adapter.id, contAttemptId, this.estimateUsdFloor(input.repoRoot), this.routeBillingKnowledge(input, adapter.id), processingCostEvidence(slot.routed.processing, this.routeBillingKnowledge(input, slot.routed.adapter.id), [`harness:${adapter.id}`])),
2108
+ cost: processingAttemptCostEvidence(adapter.id, contAttemptId, this.estimateUsdFloor(input.repoRoot), this.routeBillingKnowledge(input, adapter.id, slot.routed), slot.routed.processing),
2122
2109
  });
2123
2110
  if (contLease.granted) {
2124
2111
  candidateContinuationCount += 1; // claim the one-shot only once it launches
@@ -2138,7 +2125,7 @@ export class Orchestrator {
2138
2125
  const contLeaseId = contLease.lease?.lease_id ?? "";
2139
2126
  try {
2140
2127
  const contRun = await this.runCandidateInEnvelope(slot.routed, envelope, contAttemptId, slot.label, contract, input.prompt, store, paths, wsm, ledger, {
2141
- id: contLeaseId,
2128
+ lease: contLease.lease,
2142
2129
  onDenied: (denial) => {
2143
2130
  budgetStopped = true;
2144
2131
  budgetDenial ??= denial;
@@ -2197,7 +2184,7 @@ export class Orchestrator {
2197
2184
  }
2198
2185
  catch (err) {
2199
2186
  const failureCost = AC.attemptFailureCost(err, "post-stream-error", 0);
2200
- ledger.settle(slot.leaseId, failureCost.settlement);
2187
+ ledger.settle(slot.lease.lease_id, failureCost.settlement);
2201
2188
  const message = safeErrorMessage(err);
2202
2189
  const declared = declaredFailure(err);
2203
2190
  const infraPhase = envelope === undefined ? "workspace" : "harness";
@@ -2404,7 +2391,7 @@ export class Orchestrator {
2404
2391
  attemptId: "synth",
2405
2392
  intent: "synthesize",
2406
2393
  harnessId: synthRouted.adapter.id,
2407
- cost: attemptCostEvidence(synthRouted.adapter.id, "synth", this.reservationEstimateUsd(input), this.routeBillingKnowledge(input, synthRouted.adapter.id), processingCostEvidence(synthRouted.processing, this.routeBillingKnowledge(input, synthRouted.adapter.id), [`harness:${synthRouted.adapter.id}`])),
2394
+ cost: processingAttemptCostEvidence(synthRouted.adapter.id, "synth", this.reservationEstimateUsd(input), this.routeBillingKnowledge(input, synthRouted.adapter.id, synthRouted), synthRouted.processing),
2408
2395
  });
2409
2396
  if (lease.granted) {
2410
2397
  let envelope;
@@ -2431,7 +2418,7 @@ export class Orchestrator {
2431
2418
  });
2432
2419
  const synthHome = this.harnessHomeFor(wsm, envelope, synthRouted, input);
2433
2420
  const run = await this.runCandidateInEnvelope(synthRouted, envelope, "synth", "Synthesis", contract, synthesisInput.prompt, store, paths, wsm, ledger, {
2434
- id: lease.lease.lease_id,
2421
+ lease: lease.lease,
2435
2422
  onDenied: (denial) => {
2436
2423
  budgetStopped = true;
2437
2424
  budgetDenial ??= denial;
@@ -3211,7 +3198,7 @@ export class Orchestrator {
3211
3198
  attemptId,
3212
3199
  intent: "repair",
3213
3200
  harnessId: adapter.id,
3214
- cost: attemptCostEvidence(adapter.id, attemptId, this.reservationEstimateUsd(input), this.routeBillingKnowledge(input, adapter.id), processingCostEvidence(routed.processing, this.routeBillingKnowledge(input, routed.adapter.id), [`harness:${adapter.id}`])),
3201
+ cost: processingAttemptCostEvidence(adapter.id, attemptId, this.reservationEstimateUsd(input), this.routeBillingKnowledge(input, adapter.id, routed), routed.processing),
3215
3202
  });
3216
3203
  if (!lease.granted) {
3217
3204
  exhausted = true;
@@ -3230,7 +3217,7 @@ export class Orchestrator {
3230
3217
  ...(knobs.ignored.length > 0 ? { ignored_settings: knobs.ignored } : {}),
3231
3218
  });
3232
3219
  run = await this.runCandidateInEnvelope(routed, envelope, attemptId, `Attempt ${attempt}`, contract, prompt, store, paths, wsm, ledger, {
3233
- id: lease.lease.lease_id,
3220
+ lease: lease.lease,
3234
3221
  onDenied: (denial) => {
3235
3222
  exhausted = true;
3236
3223
  processingBudgetDenial ??= denial;
@@ -3796,17 +3783,17 @@ export class Orchestrator {
3796
3783
  /** Bind private route/session preparation to the planner-attempt owner. */
3797
3784
  plannerAttemptDeps() {
3798
3785
  return {
3799
- billingKnowledge: (input, harnessId) => this.routeBillingKnowledge(input, harnessId),
3786
+ billingKnowledge: (input, harnessId, routed) => this.routeBillingKnowledge(input, harnessId, routed),
3800
3787
  inactivityTimeoutMs: (repoRoot) => harnessInactivityTimeoutMs(this.config(repoRoot)),
3801
3788
  quotaEventSink: this.deps.quotaEventSink,
3802
- prepare: async (args) => {
3789
+ prepare: async (args, processingAdmission, physicalDispatchStarted) => {
3803
3790
  const { input, contract, taskId, runId, log, store, paths, routed, attemptId } = args;
3804
3791
  const adapter = routed.adapter;
3805
3792
  const knobs = this.routeSpecKnobs(routed, contract, undefined, input.effort);
3806
3793
  const effectiveWeb = this.discloseWebUpgrade(log, routed, knobs.webPolicy, attemptId);
3807
3794
  const sessionFields = await this.sessionSpecFields(input, adapter.id, knobs.model, log, routed.authRouteEstimate, routed.quotaAdmission);
3808
3795
  const laneContinuity = args.laneRun
3809
- ? await this.resolveContinuity(input, adapter.id, sessionFields.credential_profile?.profile_id ?? input.credentialProfileId ?? null, sessionFields.resume_session_id !== null, sessionFields, store, paths, this.execRootOf(input), log)
3796
+ ? await this.resolveContinuity(input, adapter.id, sessionFields.credential_profile?.profile_id ?? input.credentialProfileId ?? null, sessionFields.resume_session_id !== null, sessionFields, store, paths, this.execRootOf(input), log, this.harnessSpecKnobs(contract, knobs, args.intent), processingAdmission, physicalDispatchStarted)
3810
3797
  : null;
3811
3798
  const spec = HarnessRunSpec.parse({
3812
3799
  session_id: newId("ses"),
@@ -3995,13 +3982,7 @@ export class Orchestrator {
3995
3982
  // terminal names the refused route and does not read "all planners
3996
3983
  // failed"; capture the typed denial for the budget classifier.
3997
3984
  planBudgetDenial ??= outcome.budgetDenial ?? null;
3998
- planAttempts.push({
3999
- attemptId,
4000
- harnessId: outcome.harnessId,
4001
- status: outcome.status,
4002
- outcomeClass: outcome.outcomeClass,
4003
- error: outcome.error,
4004
- });
3985
+ planAttempts.push(plannerAttemptSummary(outcome));
4005
3986
  break;
4006
3987
  }
4007
3988
  if (outcome.telemetry)
@@ -4010,13 +3991,7 @@ export class Orchestrator {
4010
3991
  harnessId: outcome.harnessId,
4011
3992
  telemetry: outcome.telemetry,
4012
3993
  });
4013
- planAttempts.push({
4014
- attemptId,
4015
- harnessId: outcome.harnessId,
4016
- status: outcome.status,
4017
- outcomeClass: outcome.outcomeClass,
4018
- error: outcome.error,
4019
- });
3994
+ planAttempts.push(plannerAttemptSummary(outcome));
4020
3995
  if (outcome.status !== "success") {
4021
3996
  if (outcome.telemetry?.effortResolution?.resolution === "rejected")
4022
3997
  break;
@@ -4141,7 +4116,13 @@ export class Orchestrator {
4141
4116
  quotaSnapshotPreflight() {
4142
4117
  return [...(this.deps.quotaSnapshots?.() ?? [])].map((snapshot) => QuotaSnapshotSchema.parse(snapshot));
4143
4118
  }
4144
- routeBillingKnowledge(input, harnessId) {
4119
+ routeBillingKnowledge(input, harnessId, routed) {
4120
+ if (routed?.quotaAdmission.profile) {
4121
+ return billingKnowledgeForAuthRoute({
4122
+ route: routed.quotaAdmission.route,
4123
+ verification: routed.quotaAdmission.profileVerification ?? "not_run",
4124
+ });
4125
+ }
4145
4126
  // A selected profile's credential_kind decides billing (round-18 #2).
4146
4127
  const profileRoute = this.credentials.profileAuthRoute(input, harnessId);
4147
4128
  if (profileRoute)
@@ -4173,9 +4154,7 @@ export class Orchestrator {
4173
4154
  // The reducer admits under a finite estimate floor (mirror of the n>1
4174
4155
  // scout reserve) so a subscription route is not refused for lacking a
4175
4156
  // cash quote.
4176
- attemptCostEvidence(harnessId, attemptId, this.estimateUsdFloor(input.repoRoot), this.routeBillingKnowledge(input, harnessId), processingCostEvidence(routed?.processing, this.routeBillingKnowledge(input, harnessId), [
4177
- `harness:${harnessId}`,
4178
- ])),
4157
+ processingAttemptCostEvidence(harnessId, attemptId, this.estimateUsdFloor(input.repoRoot), this.routeBillingKnowledge(input, harnessId, routed), routed?.processing),
4179
4158
  buildSpec: async (routed, homeEnv, prompt, attemptId) => {
4180
4159
  const knobs = this.routeSpecKnobs(routed, contract, undefined, input.effort);
4181
4160
  const effectiveWeb = this.discloseWebUpgrade(log, routed, knobs.webPolicy, attemptId);
@@ -4349,7 +4328,7 @@ export class Orchestrator {
4349
4328
  // (mirror of the candidate loop): the first top-level scout reserves
4350
4329
  // without a floor; later scouts and every real Delegate child pass the
4351
4330
  // repo floor because they overlap an existing family unit.
4352
- cost: attemptCostEvidence(adapter.id, attemptId, this.reservationEstimateUsd(input, opts.deepScan && idx > 0), this.routeBillingKnowledge(input, adapter.id), processingCostEvidence(routed.processing, this.routeBillingKnowledge(input, routed.adapter.id), [`harness:${adapter.id}`])),
4331
+ cost: processingAttemptCostEvidence(adapter.id, attemptId, this.reservationEstimateUsd(input, opts.deepScan && idx > 0), this.routeBillingKnowledge(input, adapter.id, routed), routed.processing),
4353
4332
  });
4354
4333
  if (!lease.granted) {
4355
4334
  log.emit("budget.lease.created", {
@@ -4378,6 +4357,10 @@ export class Orchestrator {
4378
4357
  }
4379
4358
  return { status: "budget_denied", reason: lease.reason ?? "budget lease denied" };
4380
4359
  }
4360
+ const processingAdmission = processingAdmissionForLease(ledger, lease.lease, adapter.id, attemptId, (denial) => {
4361
+ budgetStopped = true;
4362
+ budgetDenial ??= denial;
4363
+ });
4381
4364
  // As with planners, the granted lease owns profile/continuity/spec
4382
4365
  // preparation. Contain a pre-stream rejection as this attempt's failure;
4383
4366
  // parallel siblings can then finish before the shared HOME is disposed.
@@ -4398,7 +4381,7 @@ export class Orchestrator {
4398
4381
  // scouts are excluded from laneRun); native resume is available only when
4399
4382
  // this slot was granted the lane's recorded session.
4400
4383
  const laneContinuity = laneRun
4401
- ? await this.resolveContinuity(input, adapter.id, sessionFields.credential_profile?.profile_id ?? input.credentialProfileId ?? null, grantResume, sessionFields, store, paths, this.execRootOf(input), log)
4384
+ ? await this.resolveContinuity(input, adapter.id, sessionFields.credential_profile?.profile_id ?? input.credentialProfileId ?? null, grantResume, sessionFields, store, paths, this.execRootOf(input), log, this.harnessSpecKnobs(contract, knobs, opts.intent), processingAdmission, () => ledger.markPhysicalDispatchStarted(lease.lease.lease_id))
4402
4385
  : null;
4403
4386
  // D-16d: the continuation packet pointer rides after the lane pointer so
4404
4387
  // the fresh session is re-grounded in the exhausted attempt's work.
@@ -4457,7 +4440,7 @@ export class Orchestrator {
4457
4440
  const message = `read-only attempt setup failed: ${safeErrorMessage(preparation.error)}`;
4458
4441
  AC.settleGrantedAttemptLease({
4459
4442
  ledger,
4460
- leaseId: lease.lease?.lease_id ?? "",
4443
+ leaseId: lease.lease.lease_id,
4461
4444
  attemptId,
4462
4445
  harnessId: adapter.id,
4463
4446
  costUsd: 0,
@@ -4490,10 +4473,7 @@ export class Orchestrator {
4490
4473
  const { knobs, spec: preparedSpec, reportAbort, reportInteraction, readonlyWorkMode, attemptEventsPath, telemetry, } = preparation.value;
4491
4474
  let { answer } = preparation.value;
4492
4475
  let spec = preparedSpec;
4493
- bindProcessingAdmission(spec, ledger, lease.lease.lease_id, adapter.id, attemptId, (denial) => {
4494
- budgetStopped = true;
4495
- budgetDenial ??= denial;
4496
- });
4476
+ bindProcessingAdmission(spec, ledger, lease.lease, adapter.id, attemptId, undefined, processingAdmission);
4497
4477
  const retryPolicy = transientRetryPolicy(this.config(input.repoRoot));
4498
4478
  let activeSessionId = spec.session_id;
4499
4479
  const live = liveAttempt(input, routed, paths, contract, attemptId, () => activeSessionId);
@@ -4595,7 +4575,8 @@ export class Orchestrator {
4595
4575
  }
4596
4576
  catch (err) {
4597
4577
  harnessError = safeErrorMessage(err);
4598
- if (err instanceof ProcessingBudgetAdmissionError) {
4578
+ if (err instanceof ProcessingBudgetAdmissionError ||
4579
+ err instanceof RoutingPreflightError) {
4599
4580
  processingRefusal = err;
4600
4581
  break;
4601
4582
  }
@@ -4676,7 +4657,7 @@ export class Orchestrator {
4676
4657
  input.signal?.removeEventListener("abort", onAbort);
4677
4658
  AC.settleGrantedAttemptLease({
4678
4659
  ledger,
4679
- leaseId: lease.lease?.lease_id ?? "",
4660
+ leaseId: lease.lease.lease_id,
4680
4661
  attemptId,
4681
4662
  harnessId: adapter.id,
4682
4663
  costUsd: cost,