@claudexor/orchestrator 5.0.2 → 5.1.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 (236) hide show
  1. package/dist/account-evidence.d.ts +7 -0
  2. package/dist/account-evidence.d.ts.map +1 -1
  3. package/dist/account-evidence.js +12 -0
  4. package/dist/account-evidence.js.map +1 -1
  5. package/dist/account-pool.d.ts +4 -0
  6. package/dist/account-pool.d.ts.map +1 -1
  7. package/dist/account-pool.js +8 -1
  8. package/dist/account-pool.js.map +1 -1
  9. package/dist/account-resolution.d.ts.map +1 -1
  10. package/dist/account-resolution.js +7 -5
  11. package/dist/account-resolution.js.map +1 -1
  12. package/dist/attemptFinalize.d.ts +22 -7
  13. package/dist/attemptFinalize.d.ts.map +1 -1
  14. package/dist/attemptFinalize.js +29 -7
  15. package/dist/attemptFinalize.js.map +1 -1
  16. package/dist/attemptTelemetry.d.ts +19 -18
  17. package/dist/attemptTelemetry.d.ts.map +1 -1
  18. package/dist/attemptTelemetry.js +32 -79
  19. package/dist/attemptTelemetry.js.map +1 -1
  20. package/dist/attemptUsage.d.ts.map +1 -1
  21. package/dist/attemptUsage.js +6 -14
  22. package/dist/attemptUsage.js.map +1 -1
  23. package/dist/attemptUsageCost.d.ts +11 -2
  24. package/dist/attemptUsageCost.d.ts.map +1 -1
  25. package/dist/attemptUsageCost.js +16 -3
  26. package/dist/attemptUsageCost.js.map +1 -1
  27. package/dist/candidateEvidence.d.ts +25 -8
  28. package/dist/candidateEvidence.d.ts.map +1 -1
  29. package/dist/candidateEvidence.js +45 -13
  30. package/dist/candidateEvidence.js.map +1 -1
  31. package/dist/candidateReview.d.ts.map +1 -1
  32. package/dist/candidateReview.js +4 -2
  33. package/dist/candidateReview.js.map +1 -1
  34. package/dist/candidateTerminals.d.ts +10 -0
  35. package/dist/candidateTerminals.d.ts.map +1 -1
  36. package/dist/candidateTerminals.js +28 -2
  37. package/dist/candidateTerminals.js.map +1 -1
  38. package/dist/carrier-planner.d.ts +5 -2
  39. package/dist/carrier-planner.d.ts.map +1 -1
  40. package/dist/carrier-planner.js +6 -3
  41. package/dist/carrier-planner.js.map +1 -1
  42. package/dist/command-telemetry.d.ts +15 -0
  43. package/dist/command-telemetry.d.ts.map +1 -0
  44. package/dist/command-telemetry.js +27 -0
  45. package/dist/command-telemetry.js.map +1 -0
  46. package/dist/continuation-evidence-io.d.ts +13 -3
  47. package/dist/continuation-evidence-io.d.ts.map +1 -1
  48. package/dist/continuation-evidence-io.js +36 -4
  49. package/dist/continuation-evidence-io.js.map +1 -1
  50. package/dist/continuation-evidence.d.ts +13 -2
  51. package/dist/continuation-evidence.d.ts.map +1 -1
  52. package/dist/continuation-evidence.js +23 -4
  53. package/dist/continuation-evidence.js.map +1 -1
  54. package/dist/continuation-later.d.ts.map +1 -1
  55. package/dist/continuation-later.js +6 -6
  56. package/dist/continuation-later.js.map +1 -1
  57. package/dist/continuity-facts.d.ts +25 -1
  58. package/dist/continuity-facts.d.ts.map +1 -1
  59. package/dist/continuity-facts.js +71 -4
  60. package/dist/continuity-facts.js.map +1 -1
  61. package/dist/continuity-notice.d.ts.map +1 -1
  62. package/dist/continuity-notice.js +2 -1
  63. package/dist/continuity-notice.js.map +1 -1
  64. package/dist/continuity-summary.d.ts +4 -1
  65. package/dist/continuity-summary.d.ts.map +1 -1
  66. package/dist/continuity-summary.js +30 -12
  67. package/dist/continuity-summary.js.map +1 -1
  68. package/dist/continuity.d.ts +36 -8
  69. package/dist/continuity.d.ts.map +1 -1
  70. package/dist/continuity.js +96 -21
  71. package/dist/continuity.js.map +1 -1
  72. package/dist/credential-cooldown.d.ts +7 -0
  73. package/dist/credential-cooldown.d.ts.map +1 -1
  74. package/dist/credential-cooldown.js +31 -5
  75. package/dist/credential-cooldown.js.map +1 -1
  76. package/dist/credential-differential.d.ts +15 -1
  77. package/dist/credential-differential.d.ts.map +1 -1
  78. package/dist/credential-differential.js +32 -17
  79. package/dist/credential-differential.js.map +1 -1
  80. package/dist/credential-execution.d.ts.map +1 -1
  81. package/dist/credential-execution.js +14 -15
  82. package/dist/credential-execution.js.map +1 -1
  83. package/dist/credential-preflight.js +1 -1
  84. package/dist/credential-preflight.js.map +1 -1
  85. package/dist/credential-profile-rotation.d.ts +1 -0
  86. package/dist/credential-profile-rotation.d.ts.map +1 -1
  87. package/dist/credential-profile-rotation.js +1 -1
  88. package/dist/credential-profile-rotation.js.map +1 -1
  89. package/dist/deepScanReducer.d.ts.map +1 -1
  90. package/dist/deepScanReducer.js +8 -18
  91. package/dist/deepScanReducer.js.map +1 -1
  92. package/dist/delegatedHome.d.ts +6 -4
  93. package/dist/delegatedHome.d.ts.map +1 -1
  94. package/dist/delegatedHome.js.map +1 -1
  95. package/dist/delegationFailure.d.ts +4 -4
  96. package/dist/directoryCandidate.d.ts.map +1 -1
  97. package/dist/directoryCandidate.js +35 -5
  98. package/dist/directoryCandidate.js.map +1 -1
  99. package/dist/harnessFailure.d.ts +6 -5
  100. package/dist/harnessFailure.d.ts.map +1 -1
  101. package/dist/harnessFailure.js +7 -6
  102. package/dist/harnessFailure.js.map +1 -1
  103. package/dist/index.d.ts +2 -2
  104. package/dist/index.d.ts.map +1 -1
  105. package/dist/index.js +2 -1
  106. package/dist/index.js.map +1 -1
  107. package/dist/inrun-continuity-carrier.d.ts +4 -1
  108. package/dist/inrun-continuity-carrier.d.ts.map +1 -1
  109. package/dist/inrun-continuity-carrier.js +2 -0
  110. package/dist/inrun-continuity-carrier.js.map +1 -1
  111. package/dist/inrun-continuity.d.ts +2 -2
  112. package/dist/inrun-continuity.d.ts.map +1 -1
  113. package/dist/inrun-continuity.js +22 -19
  114. package/dist/inrun-continuity.js.map +1 -1
  115. package/dist/inrun-retained-output.d.ts +18 -4
  116. package/dist/inrun-retained-output.d.ts.map +1 -1
  117. package/dist/inrun-retained-output.js +39 -8
  118. package/dist/inrun-retained-output.js.map +1 -1
  119. package/dist/laneStreamEvents.d.ts +9 -10
  120. package/dist/laneStreamEvents.d.ts.map +1 -1
  121. package/dist/laneStreamEvents.js +21 -7
  122. package/dist/laneStreamEvents.js.map +1 -1
  123. package/dist/modelGovernance.d.ts +10 -4
  124. package/dist/modelGovernance.d.ts.map +1 -1
  125. package/dist/modelGovernance.js +21 -1
  126. package/dist/modelGovernance.js.map +1 -1
  127. package/dist/native-retry-telemetry.d.ts +4 -1
  128. package/dist/native-retry-telemetry.d.ts.map +1 -1
  129. package/dist/native-retry-telemetry.js +6 -1
  130. package/dist/native-retry-telemetry.js.map +1 -1
  131. package/dist/orchestrator.d.ts +7 -1
  132. package/dist/orchestrator.d.ts.map +1 -1
  133. package/dist/orchestrator.js +86 -102
  134. package/dist/orchestrator.js.map +1 -1
  135. package/dist/planRun.js +4 -4
  136. package/dist/planRun.js.map +1 -1
  137. package/dist/plannerAttempt.d.ts +1 -0
  138. package/dist/plannerAttempt.d.ts.map +1 -1
  139. package/dist/plannerAttempt.js +14 -19
  140. package/dist/plannerAttempt.js.map +1 -1
  141. package/dist/pool-attempt-evidence.d.ts +3 -1
  142. package/dist/pool-attempt-evidence.d.ts.map +1 -1
  143. package/dist/pool-attempt-evidence.js +16 -3
  144. package/dist/pool-attempt-evidence.js.map +1 -1
  145. package/dist/quota-refusal-scope.d.ts +1 -0
  146. package/dist/quota-refusal-scope.d.ts.map +1 -1
  147. package/dist/quota-refusal-scope.js +29 -1
  148. package/dist/quota-refusal-scope.js.map +1 -1
  149. package/dist/rawPatchLane.d.ts +42 -0
  150. package/dist/rawPatchLane.d.ts.map +1 -0
  151. package/dist/rawPatchLane.js +79 -0
  152. package/dist/rawPatchLane.js.map +1 -0
  153. package/dist/readonlyTerminals.d.ts +0 -3
  154. package/dist/readonlyTerminals.d.ts.map +1 -1
  155. package/dist/readonlyTerminals.js +30 -10
  156. package/dist/readonlyTerminals.js.map +1 -1
  157. package/dist/requestRequirements.d.ts +5 -4
  158. package/dist/requestRequirements.d.ts.map +1 -1
  159. package/dist/requestRequirements.js +6 -5
  160. package/dist/requestRequirements.js.map +1 -1
  161. package/dist/retainedOutput.d.ts +19 -2
  162. package/dist/retainedOutput.d.ts.map +1 -1
  163. package/dist/retainedOutput.js +83 -12
  164. package/dist/retainedOutput.js.map +1 -1
  165. package/dist/reviewerInventory.d.ts +2 -8
  166. package/dist/reviewerInventory.d.ts.map +1 -1
  167. package/dist/reviewerInventory.js +2 -3
  168. package/dist/reviewerInventory.js.map +1 -1
  169. package/dist/reviewerPanel.d.ts +1 -1
  170. package/dist/reviewerPanel.d.ts.map +1 -1
  171. package/dist/reviewerPanel.js +9 -4
  172. package/dist/reviewerPanel.js.map +1 -1
  173. package/dist/route-admission.d.ts.map +1 -1
  174. package/dist/route-admission.js +3 -1
  175. package/dist/route-admission.js.map +1 -1
  176. package/dist/routeControls.d.ts +32 -0
  177. package/dist/routeControls.d.ts.map +1 -0
  178. package/dist/routeControls.js +47 -0
  179. package/dist/routeControls.js.map +1 -0
  180. package/dist/run-input-resolution.d.ts +5 -0
  181. package/dist/run-input-resolution.d.ts.map +1 -1
  182. package/dist/run-input-resolution.js +12 -6
  183. package/dist/run-input-resolution.js.map +1 -1
  184. package/dist/run-runtime-root.d.ts +16 -0
  185. package/dist/run-runtime-root.d.ts.map +1 -0
  186. package/dist/run-runtime-root.js +16 -0
  187. package/dist/run-runtime-root.js.map +1 -0
  188. package/dist/runAdmission.d.ts.map +1 -1
  189. package/dist/runAdmission.js +2 -1
  190. package/dist/runAdmission.js.map +1 -1
  191. package/dist/runFacts.d.ts +5 -2
  192. package/dist/runFacts.d.ts.map +1 -1
  193. package/dist/runFacts.js +11 -11
  194. package/dist/runFacts.js.map +1 -1
  195. package/dist/runScope.d.ts +2 -0
  196. package/dist/runScope.d.ts.map +1 -1
  197. package/dist/runScope.js +3 -2
  198. package/dist/runScope.js.map +1 -1
  199. package/dist/runSupport.d.ts +3 -1
  200. package/dist/runSupport.d.ts.map +1 -1
  201. package/dist/runSupport.js +3 -1
  202. package/dist/runSupport.js.map +1 -1
  203. package/dist/runTelemetryWriter.d.ts +18 -5
  204. package/dist/runTelemetryWriter.d.ts.map +1 -1
  205. package/dist/runTelemetryWriter.js +7 -2
  206. package/dist/runTelemetryWriter.js.map +1 -1
  207. package/dist/runTerminalResults.d.ts.map +1 -1
  208. package/dist/runTerminalResults.js +22 -7
  209. package/dist/runTerminalResults.js.map +1 -1
  210. package/dist/runTerminals.d.ts.map +1 -1
  211. package/dist/runTerminals.js +13 -5
  212. package/dist/runTerminals.js.map +1 -1
  213. package/dist/secretDiff.d.ts.map +1 -1
  214. package/dist/secretDiff.js +2 -1
  215. package/dist/secretDiff.js.map +1 -1
  216. package/dist/transientClassify.d.ts +17 -9
  217. package/dist/transientClassify.d.ts.map +1 -1
  218. package/dist/transientClassify.js +33 -9
  219. package/dist/transientClassify.js.map +1 -1
  220. package/dist/webEvidence.d.ts +41 -0
  221. package/dist/webEvidence.d.ts.map +1 -0
  222. package/dist/webEvidence.js +99 -0
  223. package/dist/webEvidence.js.map +1 -0
  224. package/dist/workReportEnvelope.d.ts +12 -0
  225. package/dist/workReportEnvelope.d.ts.map +1 -1
  226. package/dist/workReportEnvelope.js +19 -0
  227. package/dist/workReportEnvelope.js.map +1 -1
  228. package/package.json +17 -17
  229. package/dist/continuation-chain.test-support.d.ts +0 -473
  230. package/dist/continuation-chain.test-support.d.ts.map +0 -1
  231. package/dist/continuation-chain.test-support.js +0 -206
  232. package/dist/continuation-chain.test-support.js.map +0 -1
  233. package/dist/inrun-continuity.test-support.d.ts +0 -504
  234. package/dist/inrun-continuity.test-support.d.ts.map +0 -1
  235. package/dist/inrun-continuity.test-support.js +0 -212
  236. package/dist/inrun-continuity.test-support.js.map +0 -1
@@ -7,7 +7,7 @@ import { openRunScopeCustody, scopedRouteContext, scopedSpecEnv, } from "./runSc
7
7
  import { delegationBeltFor } from "./delegationBelt.js";
8
8
  import { routePromptCacheTtl, withReviewerPromptCacheTtl } from "./prompt-cache-routing.js";
9
9
  import { bindProcessingAdmission, processingAttemptCostEvidence, processingAdmissionForLease, updateProcessingStreamHold, } from "./processing-dispatch.js";
10
- import { cancelledCandidatesResult, emptyCandidateResult, failedCandidatesResult, failAfterCandidates, } from "./candidateTerminals.js";
10
+ import { cancelledCandidatesResult, emptyCandidateResult, erroredWinnerFailure, failedCandidatesResult, failAfterCandidates, } from "./candidateTerminals.js";
11
11
  import { deepScanFailureResult, readOnlyChainFailureResult, } from "./readonlyTerminals.js";
12
12
  import { effectiveAuthPreference, observeNativeSessionEvent, resumeSessionForProfile, } from "./credential-profiles.js";
13
13
  import { AdmissionProfileProbes, OrchestratorCredentials, reviewerProfileResolver, } from "./orchestrator-credentials.js";
@@ -15,14 +15,14 @@ import { continuityWorkspace, carryingAttempts, resumableOf, resumableTerminal,
15
15
  import { openContinuity } from "./continue-from.js";
16
16
  import { accountPoolRows } from "./account-pool.js";
17
17
  import { PoolAttemptEvidence, terminalPoolAttempts, withPoolAttempts, } from "./pool-attempt-evidence.js";
18
- import { writeRunTelemetryArtifact } from "./runTelemetryWriter.js";
18
+ import { pickTelemetry, writeRunTelemetryArtifact, } from "./runTelemetryWriter.js";
19
19
  import { buildFileBackedSynthesisInput, materializeWinnerOutputs, stageFileBackedContext, writeCandidateAttemptArtifacts, } from "./candidateOutputs.js";
20
20
  import { processAttemptUsage } from "./attemptUsage.js";
21
21
  import { captureDirectoryCandidate, directoryPreparationNotice, directoryHasOutput, directoryCandidateStable, observeDirectoryPaths, publishDirectoryCandidate, } from "./directoryCandidate.js";
22
22
  import { appliedAttemptFacts, assertDelegatedEvidence, isMutatingAccess, outerBoundaryNotice, scopedHarnessHome, selectLaneHome, followSpecHome, } from "./delegatedHome.js";
23
23
  import * as AC from "./attemptUsageCost.js";
24
24
  import * as custody from "./continuation-custody.js";
25
- import { candidateCaptureRefusals, candidateCompletion, candidateRoster, candidateStatuses, convergenceOutcomeFacts, isWorkingCandidate, partitionCandidates, toCandidateEvidence, } from "./candidateEvidence.js";
25
+ import { capturedNoGitChangeValue, candidateCaptureRefusals, candidateCompletion, candidateRoster, candidateStatuses, convergenceOutcomeFacts, declaredFailureSpeaks, interruptionOf, isWorkingCandidate, needsPostTurnFence, partitionCandidates, toCandidateEvidence, } from "./candidateEvidence.js";
26
26
  import { capabilityIntents } from "@claudexor/gateway";
27
27
  import { reviewCandidateRuns, unreviewedCandidateEvidence, evaluateUnreviewedConvergence, } from "./candidateReview.js";
28
28
  import { join } from "node:path";
@@ -33,7 +33,7 @@ import { globalConfigDir } from "@claudexor/config";
33
33
  import { executionRunConfig } from "./run-defaults.js";
34
34
  import { preflightOneShot } from "./one-shot-preflight.js";
35
35
  import { prepareRoutedProcessing, processingCostEvidence, prepareReviewerProcessing, } from "./processing-routing.js";
36
- import { acceptedTryOutput, AccessProfileIncompatibleError, AnswerAssembly, appendHarnessEvent, AuxiliaryWritableRootsUnsupportedError, CLAUDEXOR_BROWSER_ARTIFACT_SUBDIR, countsAsAgentProgress, HarnessUnavailableError, withInactivityWatchdog, } from "@claudexor/core";
36
+ import { acceptedTryOutput, AccessProfileIncompatibleError, AnswerAssembly, appendHarnessEvent, AuxiliaryWritableRootsUnsupportedError, CLAUDEXOR_BROWSER_ARTIFACT_SUBDIR, countsAsAgentProgress, HarnessUnavailableError, ModelSelectionRequiredError, missingRouteModel, withInactivityWatchdog, } from "@claudexor/core";
37
37
  import { assertRouteModelsAllowed, isDispatchRefusal, runModelGovernedRoute, } from "./modelGovernance.js";
38
38
  import { authModeForCredentialRoute, authModeForPreference, authRouteEvidenceFor, } from "./auth-route-classification.js";
39
39
  import { isFullAccess, RequestRequirementsResolver } from "./requestRequirements.js";
@@ -50,21 +50,21 @@ import { cancelledResult, declaredFailure, failTerminally, guardAnnouncedRun, wr
50
50
  import { budgetFailureRecord, classifyBudgetFailure } from "./budgetFailure.js";
51
51
  import { finalizeStructuredOutput } from "./structuredOutput.js";
52
52
  import { prepareRunAdmission } from "./runAdmission.js";
53
- import { dropDeltaPastBudget, emitPlanProgress, emitTransientExhausted, observeReadonlySpend, recordAdapterThrow, } from "./laneStreamEvents.js";
53
+ import { dropDeltaPastBudget, emitPlanProgress, emitTransientExhausted, observeUsageSpend, recordAdapterThrow, } from "./laneStreamEvents.js";
54
54
  import { promptWithEngineConstraints, sleep, harnessEventPayload, safeErrorMessage, renderSummary, observeBudgetSignals, rotateOnStall, recordCleanAttemptMetrics, envInheritance, transientRetryPolicy, reviewerTimeoutMs, harnessInactivityTimeoutMs, observeAuthSwitch, emitPoolDegraded, deliveryRefusalFailure, winnerNeedsHuman, writeRaceDeliveryDecision, } from "./runSupport.js";
55
55
  import { candidateStatusInRouteContext, resolveReadOnlyRouteContext, } from "./routeContext.js";
56
56
  import { resolveAutoReviewerPanel, resolveExplicitReviewerPanel } from "./reviewerPanel.js";
57
57
  import { ensureClaudeBridgeForRun, ensureWriteModeGitBoundary } from "./git-precondition.js";
58
- import { buildContinuation, } from "./continuity.js";
59
- import { activePlanPointer, resolveContinuitySummary, workspaceAnchor, } from "./continuity-facts.js";
58
+ import { buildContinuation } from "./continuity.js";
59
+ import { resolveContinuitySummary, threadContinuityRequest } from "./continuity-facts.js";
60
60
  import { runDiffReview } from "./diffReview.js";
61
61
  import { rawScoutBundle, resolveDeepScanSynthesis, } from "./deepScanReducer.js";
62
62
  import { createAttemptTelemetry, observeAttemptTelemetry, setAttemptOutcome, telemetrySummary, toolWarnings, unrecoveredToolErrors, webUnsatisfied, } from "./attemptTelemetry.js";
63
63
  import { beginNativeRetry } from "./native-retry-telemetry.js";
64
64
  import * as delegateFailure from "./delegationFailure.js";
65
65
  import * as secretDiff from "./secretDiff.js";
66
- import { finalizeAttempt, unrecoveredToolErrorFailure, webEvidenceFailure, } from "./attemptFinalize.js";
67
- import { resolveWorkReportEnvelope, unwrapWorkReportEnvelope, } from "./workReportEnvelope.js";
66
+ import { attemptInterruption, finalizeAttempt, interruptionDetail, unrecoveredToolErrorFailure, webEvidenceFailure, } from "./attemptFinalize.js";
67
+ import { resolveWorkReportEnvelope, unwrapAttemptAnswer, } from "./workReportEnvelope.js";
68
68
  import { buildContinuationPacket, decideContinuation, synthesizeContinuationRequest, } from "./continuation.js";
69
69
  import { interactionChannelFor } from "./interaction.js";
70
70
  import { liveAttempt } from "./live-input.js";
@@ -74,15 +74,17 @@ import { recordProcessingIntent } from "./retry-processing-intent.js";
74
74
  import { prepareReviewEvidenceDir, recordReviewEvidenceCleanup } from "./reviewEvidence.js";
75
75
  import { savedForSynthesis } from "./savedCandidate.js";
76
76
  import { ArtifactStore } from "@claudexor/artifact-store";
77
- import { buildContextPack, rawContextSizer, writeEvidencePacket } from "@claudexor/context";
78
- import { WorkspaceManager, captureRawPatchEnvelope, consumeRawPatchEnvelope, snapshotTree, } from "@claudexor/workspace";
77
+ import { buildContextPack, writeEvidencePacket } from "@claudexor/context";
78
+ import { RawPatchLane, correctableRefusal, endsConvergence } from "./rawPatchLane.js";
79
+ import { runRuntimeRoot } from "./run-runtime-root.js";
80
+ import { WorkspaceManager, snapshotTree, } from "@claudexor/workspace";
79
81
  import { blockedDecisionOverride, finalVerifyBlocks, verifyAndDeliverFiles, verifyAndDeliver, } from "@claudexor/delivery";
80
82
  import { HarnessGateway } from "@claudexor/gateway";
81
- import { ReadinessLedger, evaluateConvergence, failureSignature, gatesPassed, reviewCandidate, runGates, } from "@claudexor/review";
83
+ import { ReadinessLedger, evaluateConvergence, failureSignature, gatesPassed, requireReadingReviewers, reviewCandidate, runGates, } from "@claudexor/review";
82
84
  import { arbitrate } from "@claudexor/arbitration";
83
85
  import { buildSynthesisPlan, decideSynthesis } from "@claudexor/synthesis";
84
86
  import { billingKnowledgeForAuthRoute, attemptUsageCostSettlement, BudgetLedger, isBudgetTerminal, explainRanking, loadHarnessMetrics, promptFingerprint, rankHarnesses, } from "@claudexor/budget";
85
- import { readTextSafe, hashJson, newId, noProjectRepoRoot, redactSecrets, safeInvoke, userConfigDir, } from "@claudexor/util";
87
+ import { hashJson, newId, noProjectRepoRoot, redactSecrets, safeInvoke, userConfigDir, } from "@claudexor/util";
86
88
  const LABELS = "ABCDEFGHIJ".split("");
87
89
  const NO_PROJECT_ROOT = noProjectRepoRoot();
88
90
  export class Orchestrator {
@@ -191,7 +193,7 @@ export class Orchestrator {
191
193
  }
192
194
  async resolveReviewers(run, onIgnoredSetting) {
193
195
  if (this.deps.reviewers)
194
- return this.deps.reviewers;
196
+ return requireReadingReviewers(this.deps.reviewers);
195
197
  const { repoRoot: cwd, authPreference, signal } = run;
196
198
  const panelDeps = {
197
199
  cwd,
@@ -269,7 +271,7 @@ export class Orchestrator {
269
271
  if (input.mode === "ask" && input.contextMode === "off" && input.repoRoot === NO_PROJECT_ROOT) {
270
272
  return new ArtifactStore(input.repoRoot, { claudexorDir: userConfigDir() });
271
273
  }
272
- return new ArtifactStore(input.repoRoot);
274
+ return new ArtifactStore(input.repoRoot, { claudexorDir: runRuntimeRoot(input) });
273
275
  }
274
276
  /** The producing intent a candidate plays (create flag switches it; not hardcoded to implement). */
275
277
  candidateIntent(input) {
@@ -425,6 +427,8 @@ export class Orchestrator {
425
427
  throw new AuxiliaryWritableRootsUnsupportedError(detail);
426
428
  if (stage === "access")
427
429
  throw new AccessProfileIncompatibleError(detail);
430
+ if (stage === "model")
431
+ throw new ModelSelectionRequiredError(detail);
428
432
  throw new HarnessUnavailableError(detail);
429
433
  }
430
434
  dropped.push(detail);
@@ -587,6 +591,13 @@ export class Orchestrator {
587
591
  dropLane(id, "capability", readRefusal);
588
592
  continue;
589
593
  }
594
+ const modelRefusal = status.enabledIntents.includes(intent)
595
+ ? missingRouteModel(adapter, input.models?.[id] ?? cfgEntry?.default_model)
596
+ : null;
597
+ if (modelRefusal) {
598
+ dropLane(id, "model", modelRefusal);
599
+ continue;
600
+ }
590
601
  const reason = status.reasons.length > 0 ? `: ${status.reasons.join("; ")}` : "";
591
602
  if (status.enabledIntents.includes(intent) && accessSupported) {
592
603
  pool.push({
@@ -594,6 +605,7 @@ export class Orchestrator {
594
605
  adapterAccess: requiredAccess,
595
606
  auxiliaryWritableRoots,
596
607
  webSupport,
608
+ accessControl: manifest.capability_profile.access_control,
597
609
  providerFamily: manifest.provider_family,
598
610
  supportsMaxTurns: manifest.capabilities.max_turns,
599
611
  supportsToolLists: manifest.capabilities.tool_lists,
@@ -1136,33 +1148,7 @@ export class Orchestrator {
1136
1148
  return null;
1137
1149
  try {
1138
1150
  const profileId = resolvedProfileId ?? ctx.profileId ?? null;
1139
- const lane = { harness: harnessId, profileId };
1140
- const checkpoint = ctx.laneCheckpoints.find((c) => c.harness === harnessId && (c.profileId ?? null) === profileId);
1141
- const headTurnId = ctx.priorTurns.length
1142
- ? ctx.priorTurns[ctx.priorTurns.length - 1].id
1143
- : null;
1144
- const priorHeadOwner = headTurnId
1145
- ? ctx.laneCheckpoints.find((c) => c.turnId === headTurnId)
1146
- : undefined;
1147
- const priorHeadLane = priorHeadOwner
1148
- ? { harness: priorHeadOwner.harness, profileId: priorHeadOwner.profileId ?? null }
1149
- : null;
1150
- const priorTurns = ctx.priorTurns.map((t) => ({
1151
- id: t.id,
1152
- prompt: t.prompt,
1153
- outputText: t.runId
1154
- ? (readTextSafe(join(store.runPaths(t.runId).finalDir, "answer.md")) ?? "")
1155
- : "",
1156
- }));
1157
- const req = {
1158
- lane,
1159
- priorTurns,
1160
- laneCheckpointTurnId: checkpoint?.turnId ?? null,
1161
- nativeResumeAvailable,
1162
- priorHeadLane,
1163
- activePlan: activePlanPointer(ctx.priorTurns, store),
1164
- anchor: await workspaceAnchor(repoRoot),
1165
- };
1151
+ const req = await threadContinuityRequest(ctx, { harness: harnessId, profileId }, nativeResumeAvailable, store, repoRoot);
1166
1152
  // V9c: when the packet would collapse an older prefix, replace the
1167
1153
  // mechanical one-liners with a cached (or freshly summarized) prose
1168
1154
  // summary. Same credential route + scoped lane home a real read-only
@@ -1174,6 +1160,7 @@ export class Orchestrator {
1174
1160
  projectRoot: runInput.repoRoot,
1175
1161
  cwd: repoRoot,
1176
1162
  adapter: this.deps.registry.get(harnessId),
1163
+ model: this.deps.registry.get(harnessId)?.modelRequirement && runInput.models?.[harnessId],
1177
1164
  credentialProfile: sessionFields.credential_profile,
1178
1165
  authPreference: sessionFields.auth_preference ?? "auto",
1179
1166
  laneEnv: scopedSpecEnv(runInput.runScopeMarker, this.laneHomeEnvFor(runInput, harnessId, profileId) ?? {}) ?? {},
@@ -1342,13 +1329,8 @@ export class Orchestrator {
1342
1329
  // #147: bound to the project's context config; each try's dispatch sizes its own
1343
1330
  // packet for THIS request from the catalog read the gate makes for that try.
1344
1331
  const { context } = this.projectConfig(contract.repo.root);
1345
- const sizeRawContext = rawContextSizer(routed.implementationTransport, envelope, context);
1346
- let rawContextPacket = null;
1347
- const dispatch = sizeRawContext
1348
- ? {
1349
- rawContext: async (capacity) => (rawContextPacket = await sizeRawContext(capacity)),
1350
- }
1351
- : {};
1332
+ const rawLane = new RawPatchLane(routed.implementationTransport, envelope, context);
1333
+ const { dispatch } = rawLane;
1352
1334
  bindProcessingAdmission(spec, ledger, processingLease.lease, adapter.id, attemptId, processingLease.onDenied, processingAdmission);
1353
1335
  // Profile-less processing keeps its existing route estimate. Named rows
1354
1336
  // receive their own verification at the shared physical-dispatch gate.
@@ -1433,8 +1415,7 @@ export class Orchestrator {
1433
1415
  const refusal = continuity.beginTry(runSpec, nativeTry, attemptAbort);
1434
1416
  const transientStart = telemetry.transientFailures.length;
1435
1417
  const rateLimitStart = telemetry.rateLimits.length;
1436
- let rawPatch = null;
1437
- rawContextPacket = null; // a patch binds only the packet its own try sent
1418
+ rawLane.beginTry();
1438
1419
  try {
1439
1420
  const watched = withInactivityWatchdog(runModelGovernedRoute(routed, runSpec, dispatch), {
1440
1421
  timeoutMs: inactivityMs,
@@ -1454,7 +1435,7 @@ export class Orchestrator {
1454
1435
  // After Stop: stored evidence only, but file paths are still collected for the capture.
1455
1436
  const stopped = signal?.aborted === true;
1456
1437
  if (!stopped)
1457
- rawPatch = captureRawPatchEnvelope(sizeRawContext !== null, rawPatch, ev);
1438
+ rawLane.observe(ev);
1458
1439
  if (ev.type === "patch_produced")
1459
1440
  continue;
1460
1441
  if (directory)
@@ -1525,14 +1506,7 @@ export class Orchestrator {
1525
1506
  observeBudgetSignals(ledger, log, adapter.id, attemptId, ev, budgetSignalState);
1526
1507
  this.deps.quotaEventSink?.(adapter.id, ev);
1527
1508
  }
1528
- if (sizeRawContext && !harnessErrored)
1529
- await consumeRawPatchEnvelope({
1530
- repoRoot: envelope.repo_root,
1531
- worktreePath: envelope.worktree_path,
1532
- baseCommitSha: envelope.base_sha ?? "HEAD",
1533
- context: rawContextPacket,
1534
- envelope: rawPatch,
1535
- });
1509
+ await rawLane.settle(harnessErrored);
1536
1510
  }
1537
1511
  catch (err) {
1538
1512
  // A throwing adapter must not lose the cost already streamed: record the
@@ -1545,14 +1519,12 @@ export class Orchestrator {
1545
1519
  processingRefusal = err;
1546
1520
  break;
1547
1521
  }
1548
- if (!recordAdapterThrow(telemetry, err))
1522
+ if (!rawLane.refused(err) && !recordAdapterThrow(telemetry, err))
1549
1523
  break;
1550
1524
  }
1551
1525
  finally {
1552
1526
  clearFileBackedContext();
1553
1527
  }
1554
- if (telemetry.requestRefusal)
1555
- break;
1556
1528
  const newTransients = telemetry.transientFailures.slice(transientStart);
1557
1529
  // #31: the centralized retry gate reads the classified `retryable` verdict.
1558
1530
  const sawRetryable = newTransients.some((f) => f.retryable);
@@ -1582,6 +1554,8 @@ export class Orchestrator {
1582
1554
  this.credentials.noteTryServed(refusal, telemetry.outputMarkers, delivered);
1583
1555
  const verdict = await continuity.afterTry({
1584
1556
  ...tryFacts,
1557
+ // A refused patch is its account row above, but no retry or rotation repairs it.
1558
+ requestRefused: tryFacts.requestRefused || rawLane.refusal !== null,
1585
1559
  // A cancel or deadline that landed during capture still stops the
1586
1560
  // attempt; the try's own facts above stay its evidence.
1587
1561
  stopped: signal?.aborted === true,
@@ -1621,6 +1595,7 @@ export class Orchestrator {
1621
1595
  if (harnessErrored &&
1622
1596
  !poolExhausted &&
1623
1597
  !processingRefusal &&
1598
+ !rawLane.refusal &&
1624
1599
  !telemetry.requestRefusal &&
1625
1600
  !signal?.aborted) {
1626
1601
  emitTransientExhausted((t, p) => log?.emit(t, p), adapter.id, attemptId, telemetry, retryPolicy.maxRetries);
@@ -1631,7 +1606,7 @@ export class Orchestrator {
1631
1606
  }
1632
1607
  // D-16: answer.md persists only the deliverable. A3: an errored attempt
1633
1608
  // with no typed final contributes no answer material (acceptedTryOutput).
1634
- const unwrapped = unwrapWorkReportEnvelope(acceptedTryOutput(answer, harnessErrored), workReportMode, { sideToolReport: telemetry.sideToolWorkReport ?? undefined });
1609
+ const unwrapped = unwrapAttemptAnswer(acceptedTryOutput(answer, harnessErrored), harnessErrored && !answer.hasFinal(), workReportMode, { sideToolReport: telemetry.sideToolWorkReport ?? undefined });
1635
1610
  const answerText = unwrapped.deliverable.trim().length > 0 ? unwrapped.deliverable : undefined;
1636
1611
  if (directory)
1637
1612
  await captureDirectory(true);
@@ -1697,7 +1672,7 @@ export class Orchestrator {
1697
1672
  workReportSource: unwrapped.source,
1698
1673
  workReportViolation: unwrapped.contractViolation,
1699
1674
  workReportUnverified: unwrapped.unverified ?? null,
1700
- contextTerminalExhausted: telemetry.contextExhausted,
1675
+ interruption: attemptInterruption(telemetry),
1701
1676
  });
1702
1677
  harnessErrored = finalized.harnessErrored;
1703
1678
  if (finalized.outcomeClass === "contract_failure" && unwrapped.contractViolation)
@@ -1725,6 +1700,7 @@ export class Orchestrator {
1725
1700
  const attemptDir = join(paths.attemptsDir, attemptId);
1726
1701
  recordCleanAttemptMetrics(globalConfigDir(), adapter.id, {
1727
1702
  costUsd: cost,
1703
+ usageCost: telemetry.usageCost,
1728
1704
  streamMs: attemptStreamEndedMs - attemptStartedMs,
1729
1705
  errored,
1730
1706
  aborted: signal?.aborted === true,
@@ -1797,8 +1773,10 @@ export class Orchestrator {
1797
1773
  // A5: the typed refusal survives NORMAL attempt finalization (no throw).
1798
1774
  ...(telemetry.requestRefusal
1799
1775
  ? { declaredFailure: requestRefusalFailure(telemetry.requestRefusal) }
1800
- : poolExhausted || processingRefusal
1801
- ? { declaredFailure: declaredFailure(processingRefusal ?? poolExhausted) }
1776
+ : poolExhausted || processingRefusal || rawLane.refusal
1777
+ ? {
1778
+ declaredFailure: declaredFailure(processingRefusal ?? rawLane.refusal ?? poolExhausted),
1779
+ }
1802
1780
  : {}),
1803
1781
  outcomeClass: finalized.outcomeClass,
1804
1782
  applied,
@@ -1878,7 +1856,7 @@ export class Orchestrator {
1878
1856
  // this SINGLE root.
1879
1857
  const execRoot = this.execRootOf(input);
1880
1858
  const projectRoot = input.repoRoot;
1881
- const wsm = new WorkspaceManager(execRoot, { projectRoot });
1859
+ const wsm = new WorkspaceManager(execRoot, { projectRoot, runtimeRoot: runRuntimeRoot(input) });
1882
1860
  const anchors = secretDiff.revertAnchors(execRoot, projectRoot);
1883
1861
  store.writeYaml(join(paths.contextDir, "task.yaml"), contract);
1884
1862
  log.emit("task.contract.created", { task_contract_hash: hashJson(contract) });
@@ -2252,7 +2230,7 @@ export class Orchestrator {
2252
2230
  // Keep a TYPED pre-spawn refusal (spent quota window + its reset)
2253
2231
  // alive past this catch; `message` alone would force the terminal to
2254
2232
  // read prose back out.
2255
- ...(declared.code ? { declaredFailure: declared } : {}),
2233
+ ...(declared.code || declared.quotaRefusal ? { declaredFailure: declared } : {}),
2256
2234
  applied: appliedAttemptFacts(harnessHome, slot.routed.adapterAccess, this.credentials.effectiveProfileId(input, adapter.id)),
2257
2235
  };
2258
2236
  }
@@ -2300,18 +2278,17 @@ export class Orchestrator {
2300
2278
  : undefined,
2301
2279
  signal: input.signal,
2302
2280
  });
2303
- // Revert divergence fence for the single-candidate in-place path: the
2304
- // candidate mutated the LIVE tree during execution above, so the post-turn
2305
- // snapshot must be taken NOW — before review/synthesis/arbitration, which can
2306
- // run for a long time during which the user may edit files. Snapshotting after
2307
- // arbitration (as the race-adoption path does) would fold those user edits
2308
- // into the revert target and let a later revert clobber them.
2281
+ // Revert divergence fence for the single-candidate in-place path.
2282
+ // The candidate mutated the LIVE tree above. Snapshot NOW, before slow
2283
+ // review/synthesis/arbitration, or later user edits enter the revert target
2284
+ // and a revert can clobber them. The race-adoption path snapshots later.
2285
+ // No change, no fence.
2309
2286
  let earlyPostTurnSha = null;
2310
2287
  if (mutatingRun &&
2311
2288
  input.workspaceKind !== "directory" &&
2312
2289
  input.inPlace &&
2313
2290
  requestedSingleCandidate &&
2314
- runs.every((run) => !run.captureRefusal)) {
2291
+ needsPostTurnFence(runs)) {
2315
2292
  try {
2316
2293
  earlyPostTurnSha = await snapshotTree(execRoot);
2317
2294
  }
@@ -2757,6 +2734,7 @@ export class Orchestrator {
2757
2734
  if (inPlaceWinner && requestedSingleCandidate && adopted === true) {
2758
2735
  revertAnchorId = await anchors.turn(preTurnSha, postTurnSha);
2759
2736
  }
2737
+ const capturedNoGitChange = capturedNoGitChangeValue(winnerRun);
2760
2738
  store.writeYaml(join(paths.finalDir, "work_product.yaml"), {
2761
2739
  id: newId("wp"),
2762
2740
  kind: input.create === true ? "new_repo" : "patch",
@@ -2776,6 +2754,7 @@ export class Orchestrator {
2776
2754
  budget_stopped: budgetStopped,
2777
2755
  ...{ patch_sha256: patchSha256, ...savedPatch.meta },
2778
2756
  result_kind: resultKind,
2757
+ ...(capturedNoGitChange !== null ? { capturedNoGitChange } : {}),
2779
2758
  diffstat: {
2780
2759
  files: wstats.paths.length,
2781
2760
  additions: wstats.additions,
@@ -2899,6 +2878,7 @@ export class Orchestrator {
2899
2878
  ]),
2900
2879
  ...keptSources,
2901
2880
  ],
2881
+ ...(decisionBlock ? {} : erroredWinnerFailure(winnerRun, keptSources)),
2902
2882
  });
2903
2883
  if (!winnerRun) {
2904
2884
  store.writeText(join(paths.finalDir, "summary.md"), `# Run ${runId} (${mode})\n\n- Lifecycle: ${facts.lifecycle}${facts.reason ? ` (${facts.reason})` : ""}\n- Phase: arbitration\n\n${withPoolAttempts(result.decision.why_winner, pool)}\n`);
@@ -3054,7 +3034,10 @@ export class Orchestrator {
3054
3034
  // local `execRoot`; this path previously ensured the boundary on repoRoot,
3055
3035
  // which for an isolated thread is the project, not the mutated worktree.
3056
3036
  const execRoot = this.execRootOf(input);
3057
- const wsm = new WorkspaceManager(execRoot, { projectRoot: input.repoRoot });
3037
+ const wsm = new WorkspaceManager(execRoot, {
3038
+ projectRoot: input.repoRoot,
3039
+ runtimeRoot: runRuntimeRoot(input),
3040
+ });
3058
3041
  const anchors = secretDiff.revertAnchors(execRoot, input.repoRoot);
3059
3042
  const readiness = new ReadinessLedger();
3060
3043
  store.writeYaml(join(paths.contextDir, "task.yaml"), contract);
@@ -3170,7 +3153,7 @@ export class Orchestrator {
3170
3153
  let converged = false;
3171
3154
  let exhausted = false;
3172
3155
  let processingBudgetDenial = null;
3173
- let interrupted = false; // D-16 r8: terminalizes the run interrupted
3156
+ let interrupted = null; // D-16 r8, #299
3174
3157
  let lastFindings = [];
3175
3158
  let lastRun = null;
3176
3159
  const convergenceRuns = [];
@@ -3321,7 +3304,7 @@ export class Orchestrator {
3321
3304
  costEstimated: failureCost.estimated,
3322
3305
  errors: [message],
3323
3306
  poolAttempts: tried.snapshot(),
3324
- ...(declared.code ? { declaredFailure: declared } : {}),
3307
+ ...(declared.code || declared.quotaRefusal ? { declaredFailure: declared } : {}),
3325
3308
  telemetry: createAttemptTelemetry(knobs.webPolicy, contract.external_context.web_required, effectiveWeb, [routed.browserRequirement, routed.denyRequirement, routed.delegationRequirement], knobs.model),
3326
3309
  };
3327
3310
  if (envelope.workspace_kind === "directory") {
@@ -3339,12 +3322,12 @@ export class Orchestrator {
3339
3322
  // Fail-closed twin of the candidate lane's gate: this loop terminalizes
3340
3323
  // per attempt, so the proof is spent per attempt.
3341
3324
  assertDelegatedEvidence(input.delegated === true, convergenceAccess, [run]);
3342
- attemptTelemetries.push({ attemptId, harnessId: adapter.id, telemetry: run.telemetry });
3325
+ attemptTelemetries.push({ attemptId, harnessId: adapter.id, ...pickTelemetry(run) });
3343
3326
  lastPostTurnSha = null;
3344
3327
  // Post-mutation fence for in-place: snapshot the live tree NOW (after the
3345
3328
  // harness mutated it, before this attempt's review). The last attempt's
3346
- // value is the revert target persisted into work_product.yaml.
3347
- if (input.inPlace === true && input.workspaceKind !== "directory" && !run.captureRefusal) {
3329
+ // value is the revert target persisted into work_product.yaml (an unchanged one has none).
3330
+ if (input.inPlace && input.workspaceKind !== "directory" && needsPostTurnFence([run])) {
3348
3331
  try {
3349
3332
  lastPostTurnSha = await snapshotTree(execRoot);
3350
3333
  }
@@ -3364,8 +3347,7 @@ export class Orchestrator {
3364
3347
  exhausted = true;
3365
3348
  break;
3366
3349
  }
3367
- if (run.telemetry.requestRefusal ||
3368
- run.telemetry.effortResolution?.resolution === "rejected")
3350
+ if (endsConvergence(run))
3369
3351
  break;
3370
3352
  if (delegateFailure.candidateFailureKind(run)) {
3371
3353
  const failure = delegateFailure.candidateFailureTerminal(run, "convergence");
@@ -3396,7 +3378,7 @@ export class Orchestrator {
3396
3378
  // D-16 r8: interrupted (errored===false) would CONVERGE a partial diff
3397
3379
  // as clean — break BEFORE review; a harness error still gate-retries.
3398
3380
  if (run.outcomeClass === "interrupted") {
3399
- interrupted = true;
3381
+ interrupted = interruptionOf(run);
3400
3382
  break;
3401
3383
  }
3402
3384
  // The review round is wrapped so a preflight/revalidation throw ends the
@@ -3466,7 +3448,8 @@ export class Orchestrator {
3466
3448
  converged = true;
3467
3449
  break;
3468
3450
  }
3469
- const requiredGateFailing = run.gates.length > 0 && !gatesPassed(run.gates);
3451
+ const refusal = correctableRefusal(run); // INV-047: a refused patch has no result
3452
+ const requiredGateFailing = !refusal && run.gates.length > 0 && !gatesPassed(run.gates);
3470
3453
  const diffHash = run.files
3471
3454
  ? run.files.manifestSha256
3472
3455
  : input.workspaceKind === "directory"
@@ -3475,7 +3458,7 @@ export class Orchestrator {
3475
3458
  if (requiredGateFailing && diffHash !== null && diffHash === lastFailingGateDiffHash) {
3476
3459
  sameFailingGateDiffs += 1;
3477
3460
  }
3478
- else {
3461
+ else if (!refusal) {
3479
3462
  sameFailingGateDiffs = requiredGateFailing ? 1 : 0;
3480
3463
  lastFailingGateDiffHash = requiredGateFailing ? (diffHash ?? "") : "";
3481
3464
  }
@@ -3493,11 +3476,9 @@ export class Orchestrator {
3493
3476
  });
3494
3477
  break;
3495
3478
  }
3496
- const sig = failureSignature([
3497
- ...conv.reasons,
3498
- ...(run.files ? [`files_manifest:${run.files.manifestSha256}`] : []),
3499
- ]);
3500
- readiness.recordRound(sig, conv.reasons.join("; "));
3479
+ const manifest = run.files ? [`files_manifest:${run.files.manifestSha256}`] : [];
3480
+ const sig = refusal ?? failureSignature([...conv.reasons, ...manifest]);
3481
+ readiness.recordRound(sig, refusal ?? conv.reasons.join("; "));
3501
3482
  if (sig !== lastSig) {
3502
3483
  triedSinceProgress = new Set();
3503
3484
  lastSig = sig;
@@ -3678,6 +3659,7 @@ export class Orchestrator {
3678
3659
  : "applied_review_blocked"
3679
3660
  : "not_applied";
3680
3661
  const revertAnchorId = convAdopted === true ? await anchors.turn(preTurnSha, lastPostTurnSha) : null;
3662
+ const capturedNoGitChange = capturedNoGitChangeValue(lastRun);
3681
3663
  store.writeYaml(join(paths.finalDir, "work_product.yaml"), {
3682
3664
  id: newId("wp"),
3683
3665
  kind: "patch",
@@ -3686,6 +3668,7 @@ export class Orchestrator {
3686
3668
  meta: {
3687
3669
  harness_id: lastRun.harnessId,
3688
3670
  result_kind: "patch",
3671
+ ...(capturedNoGitChange !== null ? { capturedNoGitChange } : {}),
3689
3672
  mode,
3690
3673
  attempts: attempt,
3691
3674
  lifecycle: facts.lifecycle,
@@ -4084,6 +4067,8 @@ export class Orchestrator {
4084
4067
  if (outcome.status !== "success") {
4085
4068
  if (outcome.telemetry?.effortResolution?.resolution === "rejected")
4086
4069
  break;
4070
+ if (outcome.telemetry?.turnLimitReached)
4071
+ break; // #299: the cap ends the run
4087
4072
  const next = adapters[idx + 1];
4088
4073
  if (next && !input.signal?.aborted) {
4089
4074
  fallbackFrom = outcome.harnessId;
@@ -4553,7 +4538,8 @@ export class Orchestrator {
4553
4538
  report: "",
4554
4539
  error: message,
4555
4540
  telemetry,
4556
- ...(preDeclared.code ? { declaredFailure: preDeclared } : {}), // typed pre-spawn refusal
4541
+ // A typed pre-spawn refusal, a provider refusal with no code of its own included (#558).
4542
+ ...(declaredFailureSpeaks(preDeclared) ? { declaredFailure: preDeclared } : {}),
4557
4543
  });
4558
4544
  attemptTelemetries.push({ attemptId, harnessId: adapter.id, telemetry });
4559
4545
  if (opts.deepScan) {
@@ -4661,7 +4647,7 @@ export class Orchestrator {
4661
4647
  // the loudest) — same single owner as the agent loop.
4662
4648
  observeBudgetSignals(ledger, log, adapter.id, attemptId, ev, budgetSignalState);
4663
4649
  this.deps.quotaEventSink?.(adapter.id, ev);
4664
- const spend = observeReadonlySpend(ev, (t, p) => log.emit(t, p), adapter.id, attemptId);
4650
+ const spend = observeUsageSpend(ev, (t, p) => log.emit(t, p), adapter.id, attemptId, telemetry.usageCost);
4665
4651
  cost += spend.costUsd;
4666
4652
  costEstimated ||= spend.estimated;
4667
4653
  const streamDenial = updateProcessingStreamHold(runSpec, telemetry.usageCost, ledger, lease.lease.lease_id, adapter.id, attemptId);
@@ -4689,8 +4675,6 @@ export class Orchestrator {
4689
4675
  if (!recordAdapterThrow(telemetry, err))
4690
4676
  break;
4691
4677
  }
4692
- if (telemetry.requestRefusal)
4693
- break;
4694
4678
  const newTransients = telemetry.transientFailures.slice(transientStart);
4695
4679
  const sawRetryable = newTransients.some((f) => f.retryable);
4696
4680
  const sawTypedLimit = telemetry.rateLimits.length > rateLimitStart;
@@ -4752,7 +4736,7 @@ export class Orchestrator {
4752
4736
  attemptTelemetries.push({ attemptId, harnessId: adapter.id, telemetry });
4753
4737
  // D-16: the deliverable is the report. A3: an errored attempt with no
4754
4738
  // typed final contributes no report material (acceptedTryOutput).
4755
- const roUnwrapped = unwrapWorkReportEnvelope(acceptedTryOutput(answer, harnessError !== null), readonlyWorkMode, { sideToolReport: telemetry.sideToolWorkReport ?? undefined });
4739
+ const roUnwrapped = unwrapAttemptAnswer(acceptedTryOutput(answer, harnessError !== null), harnessError !== null && !answer.hasFinal(), readonlyWorkMode, { sideToolReport: telemetry.sideToolWorkReport ?? undefined });
4756
4740
  // Trim symmetrically with the plan path: a whitespace-only answer is not
4757
4741
  // a delivered report (the final-artifact wrapper heading would otherwise
4758
4742
  // make it read as present content by construction).
@@ -4771,7 +4755,7 @@ export class Orchestrator {
4771
4755
  workReportSource: roUnwrapped.source,
4772
4756
  workReportViolation: roUnwrapped.contractViolation,
4773
4757
  workReportUnverified: roUnwrapped.unverified ?? null,
4774
- contextTerminalExhausted: telemetry.contextExhausted,
4758
+ interruption: attemptInterruption(telemetry),
4775
4759
  });
4776
4760
  // A broken WorkReport contract is a hard failure ONLY when the finalizer
4777
4761
  // ranked it so — a concurrent terminal context exhaustion outranks it
@@ -4842,9 +4826,8 @@ export class Orchestrator {
4842
4826
  harnessId: adapter.id,
4843
4827
  status: scoutInterrupted ? "failed" : "success",
4844
4828
  report: report || "(no output)",
4845
- error: scoutInterrupted ? "context capacity exhausted before the scout completed" : null,
4829
+ error: scoutInterrupted ? interruptionDetail(roFinalized.reason) : null,
4846
4830
  telemetry,
4847
- ...(scoutInterrupted ? { interrupted: true } : {}),
4848
4831
  ...continuity.terminalFacts(roResumable),
4849
4832
  });
4850
4833
  launched.delete(tried);
@@ -4855,7 +4838,7 @@ export class Orchestrator {
4855
4838
  .join("; ")}\n`
4856
4839
  : "";
4857
4840
  const scoutTag = scoutInterrupted
4858
- ? " interrupted (context capacity exhausted)"
4841
+ ? ` interrupted (${interruptionDetail(roFinalized.reason)})`
4859
4842
  : ` (${adapter.id})`;
4860
4843
  store.writeText(join(paths.findingsDir, `${attemptId}${scoutInterrupted ? "-interrupted" : ""}.md`), `# Explorer ${attemptId}${scoutTag}\n\n${report || "(no output)"}${warningNote}\n`);
4861
4844
  }
@@ -4933,12 +4916,13 @@ export class Orchestrator {
4933
4916
  if (last?.telemetry.effortResolution?.resolution === "rejected")
4934
4917
  break;
4935
4918
  // Per-harness fallback_model: one same-harness retry on FAILURE (not
4936
- // policy blocks) before falling through to the next harness.
4919
+ // policy blocks or the caller's max-turns cap, #299) before the next harness.
4937
4920
  const fallbackModel = routed.settings?.fallbackModel;
4938
4921
  const firstModel = contract.routing_models[routed.adapter.id] ?? routed.settings?.defaultModel ?? null;
4939
4922
  if (last &&
4940
4923
  last.status === "failed" &&
4941
4924
  !last.telemetry.requestRefusal &&
4925
+ !last.telemetry.turnLimitReached &&
4942
4926
  fallbackModel &&
4943
4927
  fallbackModel !== firstModel &&
4944
4928
  !budgetStopped &&