@claudexor/orchestrator 5.0.1 → 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 (251) 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 +42 -8
  28. package/dist/candidateEvidence.d.ts.map +1 -1
  29. package/dist/candidateEvidence.js +75 -16
  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 +55 -11
  37. package/dist/candidateTerminals.js.map +1 -1
  38. package/dist/captureRefusal.d.ts +14 -4
  39. package/dist/captureRefusal.d.ts.map +1 -1
  40. package/dist/captureRefusal.js +64 -27
  41. package/dist/captureRefusal.js.map +1 -1
  42. package/dist/carrier-planner.d.ts +5 -2
  43. package/dist/carrier-planner.d.ts.map +1 -1
  44. package/dist/carrier-planner.js +6 -3
  45. package/dist/carrier-planner.js.map +1 -1
  46. package/dist/command-telemetry.d.ts +15 -0
  47. package/dist/command-telemetry.d.ts.map +1 -0
  48. package/dist/command-telemetry.js +27 -0
  49. package/dist/command-telemetry.js.map +1 -0
  50. package/dist/continuation-custody.d.ts.map +1 -1
  51. package/dist/continuation-custody.js +3 -1
  52. package/dist/continuation-custody.js.map +1 -1
  53. package/dist/continuation-evidence-io.d.ts +13 -3
  54. package/dist/continuation-evidence-io.d.ts.map +1 -1
  55. package/dist/continuation-evidence-io.js +36 -4
  56. package/dist/continuation-evidence-io.js.map +1 -1
  57. package/dist/continuation-evidence.d.ts +13 -2
  58. package/dist/continuation-evidence.d.ts.map +1 -1
  59. package/dist/continuation-evidence.js +23 -4
  60. package/dist/continuation-evidence.js.map +1 -1
  61. package/dist/continuation-later.d.ts.map +1 -1
  62. package/dist/continuation-later.js +6 -6
  63. package/dist/continuation-later.js.map +1 -1
  64. package/dist/continuity-facts.d.ts +25 -1
  65. package/dist/continuity-facts.d.ts.map +1 -1
  66. package/dist/continuity-facts.js +71 -4
  67. package/dist/continuity-facts.js.map +1 -1
  68. package/dist/continuity-notice.d.ts.map +1 -1
  69. package/dist/continuity-notice.js +2 -1
  70. package/dist/continuity-notice.js.map +1 -1
  71. package/dist/continuity-summary.d.ts +4 -1
  72. package/dist/continuity-summary.d.ts.map +1 -1
  73. package/dist/continuity-summary.js +30 -12
  74. package/dist/continuity-summary.js.map +1 -1
  75. package/dist/continuity.d.ts +36 -8
  76. package/dist/continuity.d.ts.map +1 -1
  77. package/dist/continuity.js +96 -21
  78. package/dist/continuity.js.map +1 -1
  79. package/dist/credential-cooldown.d.ts +7 -0
  80. package/dist/credential-cooldown.d.ts.map +1 -1
  81. package/dist/credential-cooldown.js +31 -5
  82. package/dist/credential-cooldown.js.map +1 -1
  83. package/dist/credential-differential.d.ts +15 -1
  84. package/dist/credential-differential.d.ts.map +1 -1
  85. package/dist/credential-differential.js +32 -17
  86. package/dist/credential-differential.js.map +1 -1
  87. package/dist/credential-execution.d.ts.map +1 -1
  88. package/dist/credential-execution.js +14 -15
  89. package/dist/credential-execution.js.map +1 -1
  90. package/dist/credential-preflight.js +1 -1
  91. package/dist/credential-preflight.js.map +1 -1
  92. package/dist/credential-profile-rotation.d.ts +1 -0
  93. package/dist/credential-profile-rotation.d.ts.map +1 -1
  94. package/dist/credential-profile-rotation.js +1 -1
  95. package/dist/credential-profile-rotation.js.map +1 -1
  96. package/dist/deepScanReducer.d.ts.map +1 -1
  97. package/dist/deepScanReducer.js +8 -18
  98. package/dist/deepScanReducer.js.map +1 -1
  99. package/dist/delegatedHome.d.ts +6 -4
  100. package/dist/delegatedHome.d.ts.map +1 -1
  101. package/dist/delegatedHome.js.map +1 -1
  102. package/dist/delegationFailure.d.ts +75 -14
  103. package/dist/delegationFailure.d.ts.map +1 -1
  104. package/dist/delegationFailure.js +38 -9
  105. package/dist/delegationFailure.js.map +1 -1
  106. package/dist/directoryCandidate.d.ts +10 -0
  107. package/dist/directoryCandidate.d.ts.map +1 -1
  108. package/dist/directoryCandidate.js +59 -9
  109. package/dist/directoryCandidate.js.map +1 -1
  110. package/dist/harnessFailure.d.ts +6 -5
  111. package/dist/harnessFailure.d.ts.map +1 -1
  112. package/dist/harnessFailure.js +7 -6
  113. package/dist/harnessFailure.js.map +1 -1
  114. package/dist/index.d.ts +2 -2
  115. package/dist/index.d.ts.map +1 -1
  116. package/dist/index.js +2 -1
  117. package/dist/index.js.map +1 -1
  118. package/dist/inrun-continuity-carrier.d.ts +4 -1
  119. package/dist/inrun-continuity-carrier.d.ts.map +1 -1
  120. package/dist/inrun-continuity-carrier.js +2 -0
  121. package/dist/inrun-continuity-carrier.js.map +1 -1
  122. package/dist/inrun-continuity.d.ts +2 -2
  123. package/dist/inrun-continuity.d.ts.map +1 -1
  124. package/dist/inrun-continuity.js +22 -19
  125. package/dist/inrun-continuity.js.map +1 -1
  126. package/dist/inrun-retained-output.d.ts +18 -4
  127. package/dist/inrun-retained-output.d.ts.map +1 -1
  128. package/dist/inrun-retained-output.js +39 -8
  129. package/dist/inrun-retained-output.js.map +1 -1
  130. package/dist/laneStreamEvents.d.ts +9 -10
  131. package/dist/laneStreamEvents.d.ts.map +1 -1
  132. package/dist/laneStreamEvents.js +21 -7
  133. package/dist/laneStreamEvents.js.map +1 -1
  134. package/dist/modelGovernance.d.ts +10 -4
  135. package/dist/modelGovernance.d.ts.map +1 -1
  136. package/dist/modelGovernance.js +21 -1
  137. package/dist/modelGovernance.js.map +1 -1
  138. package/dist/native-retry-telemetry.d.ts +4 -1
  139. package/dist/native-retry-telemetry.d.ts.map +1 -1
  140. package/dist/native-retry-telemetry.js +6 -1
  141. package/dist/native-retry-telemetry.js.map +1 -1
  142. package/dist/orchestrator.d.ts +10 -2
  143. package/dist/orchestrator.d.ts.map +1 -1
  144. package/dist/orchestrator.js +137 -156
  145. package/dist/orchestrator.js.map +1 -1
  146. package/dist/planRun.js +4 -4
  147. package/dist/planRun.js.map +1 -1
  148. package/dist/plannerAttempt.d.ts +1 -0
  149. package/dist/plannerAttempt.d.ts.map +1 -1
  150. package/dist/plannerAttempt.js +14 -19
  151. package/dist/plannerAttempt.js.map +1 -1
  152. package/dist/pool-attempt-evidence.d.ts +3 -1
  153. package/dist/pool-attempt-evidence.d.ts.map +1 -1
  154. package/dist/pool-attempt-evidence.js +16 -3
  155. package/dist/pool-attempt-evidence.js.map +1 -1
  156. package/dist/quota-refusal-scope.d.ts +1 -0
  157. package/dist/quota-refusal-scope.d.ts.map +1 -1
  158. package/dist/quota-refusal-scope.js +29 -1
  159. package/dist/quota-refusal-scope.js.map +1 -1
  160. package/dist/rawPatchLane.d.ts +42 -0
  161. package/dist/rawPatchLane.d.ts.map +1 -0
  162. package/dist/rawPatchLane.js +79 -0
  163. package/dist/rawPatchLane.js.map +1 -0
  164. package/dist/readonlyTerminals.d.ts +0 -3
  165. package/dist/readonlyTerminals.d.ts.map +1 -1
  166. package/dist/readonlyTerminals.js +30 -10
  167. package/dist/readonlyTerminals.js.map +1 -1
  168. package/dist/requestRequirements.d.ts +5 -4
  169. package/dist/requestRequirements.d.ts.map +1 -1
  170. package/dist/requestRequirements.js +6 -5
  171. package/dist/requestRequirements.js.map +1 -1
  172. package/dist/retainedOutput.d.ts +19 -2
  173. package/dist/retainedOutput.d.ts.map +1 -1
  174. package/dist/retainedOutput.js +83 -12
  175. package/dist/retainedOutput.js.map +1 -1
  176. package/dist/reviewerInventory.d.ts +2 -8
  177. package/dist/reviewerInventory.d.ts.map +1 -1
  178. package/dist/reviewerInventory.js +2 -3
  179. package/dist/reviewerInventory.js.map +1 -1
  180. package/dist/reviewerPanel.d.ts +1 -1
  181. package/dist/reviewerPanel.d.ts.map +1 -1
  182. package/dist/reviewerPanel.js +9 -4
  183. package/dist/reviewerPanel.js.map +1 -1
  184. package/dist/route-admission.d.ts.map +1 -1
  185. package/dist/route-admission.js +3 -1
  186. package/dist/route-admission.js.map +1 -1
  187. package/dist/routeControls.d.ts +32 -0
  188. package/dist/routeControls.d.ts.map +1 -0
  189. package/dist/routeControls.js +47 -0
  190. package/dist/routeControls.js.map +1 -0
  191. package/dist/run-input-resolution.d.ts +5 -0
  192. package/dist/run-input-resolution.d.ts.map +1 -1
  193. package/dist/run-input-resolution.js +12 -6
  194. package/dist/run-input-resolution.js.map +1 -1
  195. package/dist/run-runtime-root.d.ts +16 -0
  196. package/dist/run-runtime-root.d.ts.map +1 -0
  197. package/dist/run-runtime-root.js +16 -0
  198. package/dist/run-runtime-root.js.map +1 -0
  199. package/dist/runAdmission.d.ts.map +1 -1
  200. package/dist/runAdmission.js +2 -1
  201. package/dist/runAdmission.js.map +1 -1
  202. package/dist/runEventLog.d.ts +2 -1
  203. package/dist/runEventLog.d.ts.map +1 -1
  204. package/dist/runEventLog.js +3 -1
  205. package/dist/runEventLog.js.map +1 -1
  206. package/dist/runFacts.d.ts +5 -2
  207. package/dist/runFacts.d.ts.map +1 -1
  208. package/dist/runFacts.js +11 -11
  209. package/dist/runFacts.js.map +1 -1
  210. package/dist/runScope.d.ts +2 -0
  211. package/dist/runScope.d.ts.map +1 -1
  212. package/dist/runScope.js +3 -2
  213. package/dist/runScope.js.map +1 -1
  214. package/dist/runSupport.d.ts +3 -1
  215. package/dist/runSupport.d.ts.map +1 -1
  216. package/dist/runSupport.js +3 -1
  217. package/dist/runSupport.js.map +1 -1
  218. package/dist/runTelemetryWriter.d.ts +18 -5
  219. package/dist/runTelemetryWriter.d.ts.map +1 -1
  220. package/dist/runTelemetryWriter.js +7 -2
  221. package/dist/runTelemetryWriter.js.map +1 -1
  222. package/dist/runTerminalResults.d.ts.map +1 -1
  223. package/dist/runTerminalResults.js +22 -7
  224. package/dist/runTerminalResults.js.map +1 -1
  225. package/dist/runTerminals.d.ts.map +1 -1
  226. package/dist/runTerminals.js +19 -8
  227. package/dist/runTerminals.js.map +1 -1
  228. package/dist/secretDiff.d.ts.map +1 -1
  229. package/dist/secretDiff.js +2 -1
  230. package/dist/secretDiff.js.map +1 -1
  231. package/dist/transientClassify.d.ts +17 -9
  232. package/dist/transientClassify.d.ts.map +1 -1
  233. package/dist/transientClassify.js +33 -9
  234. package/dist/transientClassify.js.map +1 -1
  235. package/dist/webEvidence.d.ts +41 -0
  236. package/dist/webEvidence.d.ts.map +1 -0
  237. package/dist/webEvidence.js +99 -0
  238. package/dist/webEvidence.js.map +1 -0
  239. package/dist/workReportEnvelope.d.ts +12 -0
  240. package/dist/workReportEnvelope.d.ts.map +1 -1
  241. package/dist/workReportEnvelope.js +19 -0
  242. package/dist/workReportEnvelope.js.map +1 -1
  243. package/package.json +17 -17
  244. package/dist/continuation-chain.test-support.d.ts +0 -473
  245. package/dist/continuation-chain.test-support.d.ts.map +0 -1
  246. package/dist/continuation-chain.test-support.js +0 -206
  247. package/dist/continuation-chain.test-support.js.map +0 -1
  248. package/dist/inrun-continuity.test-support.d.ts +0 -504
  249. package/dist/inrun-continuity.test-support.d.ts.map +0 -1
  250. package/dist/inrun-continuity.test-support.js +0 -212
  251. 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
- import { captureDirectoryCandidate, directoryHasOutput, directoryCandidateStable, observeDirectoryPaths, publishDirectoryCandidate, } from "./directoryCandidate.js";
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 { 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) ?? {}) ?? {},
@@ -1262,11 +1249,12 @@ export class Orchestrator {
1262
1249
  const inPlaceEnvelope = envelope.worktree_path === envelope.repo_root;
1263
1250
  const directory = envelope.workspace_kind === "directory";
1264
1251
  let directoryCapture = {};
1265
- const captureDirectory = async () => (directoryCapture = await captureDirectoryCandidate({
1252
+ const captureDirectory = async (final = false) => (directoryCapture = await captureDirectoryCandidate({
1266
1253
  manager: wsm,
1267
1254
  envelope,
1268
1255
  artifactRoot: join(paths.attemptsDir, attemptId),
1269
1256
  observedPaths: [...observedPaths],
1257
+ ...(final ? { holder: { runId: paths.runId, runDir: paths.root } } : {}),
1270
1258
  }));
1271
1259
  const sessionFields = runInput
1272
1260
  ? await this.sessionSpecFields(runInput, adapter.id, knobs.model, log, routed.authRouteEstimate, routed.quotaAdmission)
@@ -1304,6 +1292,7 @@ export class Orchestrator {
1304
1292
  [
1305
1293
  prompt,
1306
1294
  outerBoundaryNotice(harnessHome, routed.auxiliaryWritableRoots),
1295
+ await directoryPreparationNotice(envelope, attemptId, log),
1307
1296
  laneContinuity?.pointerLine,
1308
1297
  continuationPointer,
1309
1298
  ]
@@ -1340,13 +1329,8 @@ export class Orchestrator {
1340
1329
  // #147: bound to the project's context config; each try's dispatch sizes its own
1341
1330
  // packet for THIS request from the catalog read the gate makes for that try.
1342
1331
  const { context } = this.projectConfig(contract.repo.root);
1343
- const sizeRawContext = rawContextSizer(routed.implementationTransport, envelope, context);
1344
- let rawContextPacket = null;
1345
- const dispatch = sizeRawContext
1346
- ? {
1347
- rawContext: async (capacity) => (rawContextPacket = await sizeRawContext(capacity)),
1348
- }
1349
- : {};
1332
+ const rawLane = new RawPatchLane(routed.implementationTransport, envelope, context);
1333
+ const { dispatch } = rawLane;
1350
1334
  bindProcessingAdmission(spec, ledger, processingLease.lease, adapter.id, attemptId, processingLease.onDenied, processingAdmission);
1351
1335
  // Profile-less processing keeps its existing route estimate. Named rows
1352
1336
  // receive their own verification at the shared physical-dispatch gate.
@@ -1431,8 +1415,7 @@ export class Orchestrator {
1431
1415
  const refusal = continuity.beginTry(runSpec, nativeTry, attemptAbort);
1432
1416
  const transientStart = telemetry.transientFailures.length;
1433
1417
  const rateLimitStart = telemetry.rateLimits.length;
1434
- let rawPatch = null;
1435
- rawContextPacket = null; // a patch binds only the packet its own try sent
1418
+ rawLane.beginTry();
1436
1419
  try {
1437
1420
  const watched = withInactivityWatchdog(runModelGovernedRoute(routed, runSpec, dispatch), {
1438
1421
  timeoutMs: inactivityMs,
@@ -1452,7 +1435,7 @@ export class Orchestrator {
1452
1435
  // After Stop: stored evidence only, but file paths are still collected for the capture.
1453
1436
  const stopped = signal?.aborted === true;
1454
1437
  if (!stopped)
1455
- rawPatch = captureRawPatchEnvelope(sizeRawContext !== null, rawPatch, ev);
1438
+ rawLane.observe(ev);
1456
1439
  if (ev.type === "patch_produced")
1457
1440
  continue;
1458
1441
  if (directory)
@@ -1523,14 +1506,7 @@ export class Orchestrator {
1523
1506
  observeBudgetSignals(ledger, log, adapter.id, attemptId, ev, budgetSignalState);
1524
1507
  this.deps.quotaEventSink?.(adapter.id, ev);
1525
1508
  }
1526
- if (sizeRawContext && !harnessErrored)
1527
- await consumeRawPatchEnvelope({
1528
- repoRoot: envelope.repo_root,
1529
- worktreePath: envelope.worktree_path,
1530
- baseCommitSha: envelope.base_sha ?? "HEAD",
1531
- context: rawContextPacket,
1532
- envelope: rawPatch,
1533
- });
1509
+ await rawLane.settle(harnessErrored);
1534
1510
  }
1535
1511
  catch (err) {
1536
1512
  // A throwing adapter must not lose the cost already streamed: record the
@@ -1543,14 +1519,12 @@ export class Orchestrator {
1543
1519
  processingRefusal = err;
1544
1520
  break;
1545
1521
  }
1546
- if (!recordAdapterThrow(telemetry, err))
1522
+ if (!rawLane.refused(err) && !recordAdapterThrow(telemetry, err))
1547
1523
  break;
1548
1524
  }
1549
1525
  finally {
1550
1526
  clearFileBackedContext();
1551
1527
  }
1552
- if (telemetry.requestRefusal)
1553
- break;
1554
1528
  const newTransients = telemetry.transientFailures.slice(transientStart);
1555
1529
  // #31: the centralized retry gate reads the classified `retryable` verdict.
1556
1530
  const sawRetryable = newTransients.some((f) => f.retryable);
@@ -1580,6 +1554,8 @@ export class Orchestrator {
1580
1554
  this.credentials.noteTryServed(refusal, telemetry.outputMarkers, delivered);
1581
1555
  const verdict = await continuity.afterTry({
1582
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,
1583
1559
  // A cancel or deadline that landed during capture still stops the
1584
1560
  // attempt; the try's own facts above stay its evidence.
1585
1561
  stopped: signal?.aborted === true,
@@ -1619,6 +1595,7 @@ export class Orchestrator {
1619
1595
  if (harnessErrored &&
1620
1596
  !poolExhausted &&
1621
1597
  !processingRefusal &&
1598
+ !rawLane.refusal &&
1622
1599
  !telemetry.requestRefusal &&
1623
1600
  !signal?.aborted) {
1624
1601
  emitTransientExhausted((t, p) => log?.emit(t, p), adapter.id, attemptId, telemetry, retryPolicy.maxRetries);
@@ -1629,10 +1606,10 @@ export class Orchestrator {
1629
1606
  }
1630
1607
  // D-16: answer.md persists only the deliverable. A3: an errored attempt
1631
1608
  // with no typed final contributes no answer material (acceptedTryOutput).
1632
- 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 });
1633
1610
  const answerText = unwrapped.deliverable.trim().length > 0 ? unwrapped.deliverable : undefined;
1634
1611
  if (directory)
1635
- await captureDirectory();
1612
+ await captureDirectory(true);
1636
1613
  const captured = directory
1637
1614
  ? { diff: "", captureRefusal: directoryCapture.refusal }
1638
1615
  : await secretDiff.captureCandidateWorkspace({
@@ -1695,7 +1672,7 @@ export class Orchestrator {
1695
1672
  workReportSource: unwrapped.source,
1696
1673
  workReportViolation: unwrapped.contractViolation,
1697
1674
  workReportUnverified: unwrapped.unverified ?? null,
1698
- contextTerminalExhausted: telemetry.contextExhausted,
1675
+ interruption: attemptInterruption(telemetry),
1699
1676
  });
1700
1677
  harnessErrored = finalized.harnessErrored;
1701
1678
  if (finalized.outcomeClass === "contract_failure" && unwrapped.contractViolation)
@@ -1723,6 +1700,7 @@ export class Orchestrator {
1723
1700
  const attemptDir = join(paths.attemptsDir, attemptId);
1724
1701
  recordCleanAttemptMetrics(globalConfigDir(), adapter.id, {
1725
1702
  costUsd: cost,
1703
+ usageCost: telemetry.usageCost,
1726
1704
  streamMs: attemptStreamEndedMs - attemptStartedMs,
1727
1705
  errored,
1728
1706
  aborted: signal?.aborted === true,
@@ -1795,8 +1773,10 @@ export class Orchestrator {
1795
1773
  // A5: the typed refusal survives NORMAL attempt finalization (no throw).
1796
1774
  ...(telemetry.requestRefusal
1797
1775
  ? { declaredFailure: requestRefusalFailure(telemetry.requestRefusal) }
1798
- : poolExhausted || processingRefusal
1799
- ? { declaredFailure: declaredFailure(processingRefusal ?? poolExhausted) }
1776
+ : poolExhausted || processingRefusal || rawLane.refusal
1777
+ ? {
1778
+ declaredFailure: declaredFailure(processingRefusal ?? rawLane.refusal ?? poolExhausted),
1779
+ }
1800
1780
  : {}),
1801
1781
  outcomeClass: finalized.outcomeClass,
1802
1782
  applied,
@@ -1876,7 +1856,7 @@ export class Orchestrator {
1876
1856
  // this SINGLE root.
1877
1857
  const execRoot = this.execRootOf(input);
1878
1858
  const projectRoot = input.repoRoot;
1879
- const wsm = new WorkspaceManager(execRoot, { projectRoot });
1859
+ const wsm = new WorkspaceManager(execRoot, { projectRoot, runtimeRoot: runRuntimeRoot(input) });
1880
1860
  const anchors = secretDiff.revertAnchors(execRoot, projectRoot);
1881
1861
  store.writeYaml(join(paths.contextDir, "task.yaml"), contract);
1882
1862
  log.emit("task.contract.created", { task_contract_hash: hashJson(contract) });
@@ -2108,17 +2088,7 @@ export class Orchestrator {
2108
2088
  }, input, requestedSingleCandidate, // W-C4 deltas: single-candidate chat lane only (racing = noise x N)
2109
2089
  undefined, undefined, harnessHome, undefined, tried);
2110
2090
  ledger.settle(slot.lease.lease_id, attemptUsageCostSettlement(run.cost, run.costEstimated, run.attemptId, run.harnessId, run.telemetry.authMode, run.telemetry.usageCost));
2111
- log.emit("harness.completed", {
2112
- harness_id: adapter.id,
2113
- attempt_id: slot.attemptId,
2114
- // QA-027: never claim `success` over an attempt the operator/deadline
2115
- // cut short. An abort makes the run non-successful; the top-level
2116
- // status axis must say `cancelled` (the nested outcome axis already
2117
- // rides in telemetrySummary), not launder a torn-off stream as clean.
2118
- status: input.signal?.aborted ? "cancelled" : run.errored ? "failed" : "success",
2119
- cost_usd: run.cost,
2120
- ...telemetrySummary(run.telemetry),
2121
- });
2091
+ log.emit("harness.completed", candidateCompletion(run, input.signal));
2122
2092
  // D-16d one-shot continuation for an ENVELOPED candidate (parity with the
2123
2093
  // read-only loop, which had the ONLY continuation wiring). An eligible
2124
2094
  // terminal context exhaustion (repeated_refill, no completed report) gets
@@ -2187,21 +2157,18 @@ export class Orchestrator {
2187
2157
  return true;
2188
2158
  }, input, requestedSingleCandidate, undefined, packet.pointerLine ?? undefined, harnessHome, undefined, contTried);
2189
2159
  ledger.settle(contLeaseId, attemptUsageCostSettlement(contRun.cost, contRun.costEstimated, contRun.attemptId, contRun.harnessId, contRun.telemetry.authMode, contRun.telemetry.usageCost));
2190
- log.emit("harness.completed", {
2191
- harness_id: adapter.id,
2192
- attempt_id: contAttemptId,
2193
- status: input.signal?.aborted
2194
- ? "cancelled"
2195
- : contRun.errored
2196
- ? "failed"
2197
- : "success",
2198
- cost_usd: contRun.cost,
2199
- ...telemetrySummary(contRun.telemetry),
2200
- });
2160
+ log.emit("harness.completed", candidateCompletion(contRun, input.signal));
2201
2161
  // Supersede the exhausted candidate ONLY after the continuation
2202
2162
  // actually completes cleanly (never over a torn-off/aborted stream).
2203
2163
  if (!contRun.errored && !input.signal?.aborted)
2204
2164
  effectiveRun = contRun;
2165
+ // A refused capture is errored: the original keeps leading, and
2166
+ // the copy they share stays kept under the continuation's receipt.
2167
+ if (contRun.captureRefusal)
2168
+ effectiveRun.continuationRefusal = {
2169
+ attemptId: contRun.attemptId,
2170
+ refusal: contRun.captureRefusal,
2171
+ };
2205
2172
  }
2206
2173
  catch (err) {
2207
2174
  ledger.settle(contLeaseId, AC.attemptFailureCost(err, "continuation-error", 0).settlement);
@@ -2263,7 +2230,7 @@ export class Orchestrator {
2263
2230
  // Keep a TYPED pre-spawn refusal (spent quota window + its reset)
2264
2231
  // alive past this catch; `message` alone would force the terminal to
2265
2232
  // read prose back out.
2266
- ...(declared.code ? { declaredFailure: declared } : {}),
2233
+ ...(declared.code || declared.quotaRefusal ? { declaredFailure: declared } : {}),
2267
2234
  applied: appliedAttemptFacts(harnessHome, slot.routed.adapterAccess, this.credentials.effectiveProfileId(input, adapter.id)),
2268
2235
  };
2269
2236
  }
@@ -2311,18 +2278,17 @@ export class Orchestrator {
2311
2278
  : undefined,
2312
2279
  signal: input.signal,
2313
2280
  });
2314
- // Revert divergence fence for the single-candidate in-place path: the
2315
- // candidate mutated the LIVE tree during execution above, so the post-turn
2316
- // snapshot must be taken NOW — before review/synthesis/arbitration, which can
2317
- // run for a long time during which the user may edit files. Snapshotting after
2318
- // arbitration (as the race-adoption path does) would fold those user edits
2319
- // 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.
2320
2286
  let earlyPostTurnSha = null;
2321
2287
  if (mutatingRun &&
2322
2288
  input.workspaceKind !== "directory" &&
2323
2289
  input.inPlace &&
2324
2290
  requestedSingleCandidate &&
2325
- runs.every((run) => !run.captureRefusal)) {
2291
+ needsPostTurnFence(runs)) {
2326
2292
  try {
2327
2293
  earlyPostTurnSha = await snapshotTree(execRoot);
2328
2294
  }
@@ -2334,7 +2300,7 @@ export class Orchestrator {
2334
2300
  await disposeReviewEnvelopes();
2335
2301
  return cancelledRaceResult();
2336
2302
  }
2337
- const failedDelegation = delegateFailure.dominantRaceCandidateFailure(runs);
2303
+ const failedDelegation = delegateFailure.dominantRaceCandidateFailure(runs, input);
2338
2304
  if (failedDelegation) {
2339
2305
  await disposeReviewEnvelopes();
2340
2306
  const failedOutput = {
@@ -2360,7 +2326,7 @@ export class Orchestrator {
2360
2326
  budgetDenial,
2361
2327
  ...(mutatingRun ? { liveCapture: failedOutput } : {}),
2362
2328
  });
2363
- const failure = delegateFailure.candidateFailureTerminal(failedDelegation, "race");
2329
+ const failure = delegateFailure.candidateFailureTerminal(failedDelegation, "race", runs);
2364
2330
  if (mutatingRun)
2365
2331
  await delegateFailure.persistFailedCandidateOutput(failedOutput);
2366
2332
  raceTerminal.writeTelemetry();
@@ -2768,6 +2734,7 @@ export class Orchestrator {
2768
2734
  if (inPlaceWinner && requestedSingleCandidate && adopted === true) {
2769
2735
  revertAnchorId = await anchors.turn(preTurnSha, postTurnSha);
2770
2736
  }
2737
+ const capturedNoGitChange = capturedNoGitChangeValue(winnerRun);
2771
2738
  store.writeYaml(join(paths.finalDir, "work_product.yaml"), {
2772
2739
  id: newId("wp"),
2773
2740
  kind: input.create === true ? "new_repo" : "patch",
@@ -2787,6 +2754,7 @@ export class Orchestrator {
2787
2754
  budget_stopped: budgetStopped,
2788
2755
  ...{ patch_sha256: patchSha256, ...savedPatch.meta },
2789
2756
  result_kind: resultKind,
2757
+ ...(capturedNoGitChange !== null ? { capturedNoGitChange } : {}),
2790
2758
  diffstat: {
2791
2759
  files: wstats.paths.length,
2792
2760
  additions: wstats.additions,
@@ -2802,7 +2770,7 @@ export class Orchestrator {
2802
2770
  },
2803
2771
  });
2804
2772
  }
2805
- store.writeText(join(paths.finalDir, "summary.md"), withPoolAttempts(renderSummary(runId, mode, { ...result.decision, facts }, evidences, synth.reason, actualReviewVerified, secretDiff.resultSummaryLines(winnerRun)), racePool()));
2773
+ store.writeText(join(paths.finalDir, "summary.md"), withPoolAttempts(renderSummary(runId, mode, { ...result.decision, facts }, evidences, synth.reason, actualReviewVerified, delegateFailure.raceSummaryDisclosure(winnerRun, runs)), racePool()));
2806
2774
  // summary.md is a DIAGNOSTIC artifact only (V8/PLAN addendum 2): it no
2807
2775
  // longer carries primary-output authority. A clean applyable success or a
2808
2776
  // winner answer still marks it ready for legacy INV-116 ordering; any
@@ -2829,12 +2797,15 @@ export class Orchestrator {
2829
2797
  const needsDecisionTerminal = facts.review === "blocked" || facts.checks === "failed";
2830
2798
  const isFailureTerminal = facts.lifecycle !== "succeeded" || needsDecisionTerminal;
2831
2799
  const pool = racePool();
2800
+ // Beside whichever failure record leads, a source another attempt kept stays named.
2801
+ const keptSources = candidateCaptureRefusals(runs).flatMap(secretDiff.captureInspectionActions);
2832
2802
  if (deliveryFailureReason && !finalVerifyFailed) {
2833
2803
  const refused = deliveryRefusalFailure(deliveryFailureReason, paths.root);
2834
2804
  writeFailure(store, paths, {
2835
2805
  ...refused,
2836
2806
  safeMessage: withPoolAttempts(refused.safeMessage, pool),
2837
2807
  poolAttempts: pool,
2808
+ nextActions: [...refused.nextActions, ...keptSources],
2838
2809
  });
2839
2810
  }
2840
2811
  else if (finalVerifyFailed) {
@@ -2848,15 +2819,18 @@ export class Orchestrator {
2848
2819
  : `final verify failed: ${finalVerify?.reason ?? (finalVerify?.gates_passed === false ? "deterministic gates failed on the fresh verify tree" : "unknown")}`, pool),
2849
2820
  poolAttempts: pool,
2850
2821
  runDir: paths.root,
2851
- nextActions: finalVerify?.applied_cleanly === null
2852
- ? [
2853
- "Inspect arbitration/decision.yaml (final_verify) for the verifier error",
2854
- "Fix the verify infrastructure (git worktree/tmp) and re-run, or accept_risk to override",
2855
- ]
2856
- : [
2857
- "Inspect arbitration/decision.yaml (final_verify)",
2858
- "Re-run after fixing the base conflict or the failing gates",
2859
- ],
2822
+ nextActions: [
2823
+ ...(finalVerify?.applied_cleanly === null
2824
+ ? [
2825
+ "Inspect arbitration/decision.yaml (final_verify) for the verifier error",
2826
+ "Fix the verify infrastructure (git worktree/tmp) and re-run, or accept_risk to override",
2827
+ ]
2828
+ : [
2829
+ "Inspect arbitration/decision.yaml (final_verify)",
2830
+ "Re-run after fixing the base conflict or the failing gates",
2831
+ ]),
2832
+ ...keptSources,
2833
+ ],
2860
2834
  });
2861
2835
  }
2862
2836
  else if (isFailureTerminal) {
@@ -2888,19 +2862,23 @@ export class Orchestrator {
2888
2862
  ? `attempts/${winnerRun.attemptId}/attempt.yaml`
2889
2863
  : undefined,
2890
2864
  runDir: paths.root,
2891
- nextActions: decisionBlock
2892
- ? [
2893
- "Review the blocking findings on the run's turn",
2894
- "Accept the risk to apply this exact patch, or discard the change",
2895
- ...(facts.checks === "failed"
2896
- ? ["Configure/approve the deterministic test command, then re-run"]
2897
- : []),
2898
- ]
2899
- : [
2900
- "Open diagnostics",
2901
- "Inspect candidate artifacts",
2902
- "Retry with a narrower prompt or different harness pool",
2903
- ],
2865
+ nextActions: [
2866
+ ...(decisionBlock
2867
+ ? [
2868
+ "Review the blocking findings on the run's turn",
2869
+ "Accept the risk to apply this exact patch, or discard the change",
2870
+ ...(facts.checks === "failed"
2871
+ ? ["Configure/approve the deterministic test command, then re-run"]
2872
+ : []),
2873
+ ]
2874
+ : [
2875
+ "Open diagnostics",
2876
+ "Inspect candidate artifacts",
2877
+ "Retry with a narrower prompt or different harness pool",
2878
+ ]),
2879
+ ...keptSources,
2880
+ ],
2881
+ ...(decisionBlock ? {} : erroredWinnerFailure(winnerRun, keptSources)),
2904
2882
  });
2905
2883
  if (!winnerRun) {
2906
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`);
@@ -3056,7 +3034,10 @@ export class Orchestrator {
3056
3034
  // local `execRoot`; this path previously ensured the boundary on repoRoot,
3057
3035
  // which for an isolated thread is the project, not the mutated worktree.
3058
3036
  const execRoot = this.execRootOf(input);
3059
- const wsm = new WorkspaceManager(execRoot, { projectRoot: input.repoRoot });
3037
+ const wsm = new WorkspaceManager(execRoot, {
3038
+ projectRoot: input.repoRoot,
3039
+ runtimeRoot: runRuntimeRoot(input),
3040
+ });
3060
3041
  const anchors = secretDiff.revertAnchors(execRoot, input.repoRoot);
3061
3042
  const readiness = new ReadinessLedger();
3062
3043
  store.writeYaml(join(paths.contextDir, "task.yaml"), contract);
@@ -3172,7 +3153,7 @@ export class Orchestrator {
3172
3153
  let converged = false;
3173
3154
  let exhausted = false;
3174
3155
  let processingBudgetDenial = null;
3175
- let interrupted = false; // D-16 r8: terminalizes the run interrupted
3156
+ let interrupted = null; // D-16 r8, #299
3176
3157
  let lastFindings = [];
3177
3158
  let lastRun = null;
3178
3159
  const convergenceRuns = [];
@@ -3323,18 +3304,17 @@ export class Orchestrator {
3323
3304
  costEstimated: failureCost.estimated,
3324
3305
  errors: [message],
3325
3306
  poolAttempts: tried.snapshot(),
3326
- ...(declared.code ? { declaredFailure: declared } : {}),
3307
+ ...(declared.code || declared.quotaRefusal ? { declaredFailure: declared } : {}),
3327
3308
  telemetry: createAttemptTelemetry(knobs.webPolicy, contract.external_context.web_required, effectiveWeb, [routed.browserRequirement, routed.denyRequirement, routed.delegationRequirement], knobs.model),
3328
3309
  };
3329
3310
  if (envelope.workspace_kind === "directory") {
3330
- const captured = await captureDirectoryCandidate({
3311
+ ({ files: run.files, refusal: run.captureRefusal } = await captureDirectoryCandidate({
3331
3312
  manager: wsm,
3332
3313
  envelope,
3333
3314
  artifactRoot: join(paths.attemptsDir, attemptId),
3334
3315
  observedPaths: [...observedPaths],
3335
- });
3336
- run.files = captured.files;
3337
- run.captureRefusal = captured.refusal;
3316
+ holder: { runId: paths.runId, runDir: paths.root },
3317
+ }));
3338
3318
  }
3339
3319
  }
3340
3320
  lastRun = run;
@@ -3342,12 +3322,12 @@ export class Orchestrator {
3342
3322
  // Fail-closed twin of the candidate lane's gate: this loop terminalizes
3343
3323
  // per attempt, so the proof is spent per attempt.
3344
3324
  assertDelegatedEvidence(input.delegated === true, convergenceAccess, [run]);
3345
- attemptTelemetries.push({ attemptId, harnessId: adapter.id, telemetry: run.telemetry });
3325
+ attemptTelemetries.push({ attemptId, harnessId: adapter.id, ...pickTelemetry(run) });
3346
3326
  lastPostTurnSha = null;
3347
3327
  // Post-mutation fence for in-place: snapshot the live tree NOW (after the
3348
3328
  // harness mutated it, before this attempt's review). The last attempt's
3349
- // value is the revert target persisted into work_product.yaml.
3350
- 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])) {
3351
3331
  try {
3352
3332
  lastPostTurnSha = await snapshotTree(execRoot);
3353
3333
  }
@@ -3367,8 +3347,7 @@ export class Orchestrator {
3367
3347
  exhausted = true;
3368
3348
  break;
3369
3349
  }
3370
- if (run.telemetry.requestRefusal ||
3371
- run.telemetry.effortResolution?.resolution === "rejected")
3350
+ if (endsConvergence(run))
3372
3351
  break;
3373
3352
  if (delegateFailure.candidateFailureKind(run)) {
3374
3353
  const failure = delegateFailure.candidateFailureTerminal(run, "convergence");
@@ -3399,7 +3378,7 @@ export class Orchestrator {
3399
3378
  // D-16 r8: interrupted (errored===false) would CONVERGE a partial diff
3400
3379
  // as clean — break BEFORE review; a harness error still gate-retries.
3401
3380
  if (run.outcomeClass === "interrupted") {
3402
- interrupted = true;
3381
+ interrupted = interruptionOf(run);
3403
3382
  break;
3404
3383
  }
3405
3384
  // The review round is wrapped so a preflight/revalidation throw ends the
@@ -3469,7 +3448,8 @@ export class Orchestrator {
3469
3448
  converged = true;
3470
3449
  break;
3471
3450
  }
3472
- 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);
3473
3453
  const diffHash = run.files
3474
3454
  ? run.files.manifestSha256
3475
3455
  : input.workspaceKind === "directory"
@@ -3478,7 +3458,7 @@ export class Orchestrator {
3478
3458
  if (requiredGateFailing && diffHash !== null && diffHash === lastFailingGateDiffHash) {
3479
3459
  sameFailingGateDiffs += 1;
3480
3460
  }
3481
- else {
3461
+ else if (!refusal) {
3482
3462
  sameFailingGateDiffs = requiredGateFailing ? 1 : 0;
3483
3463
  lastFailingGateDiffHash = requiredGateFailing ? (diffHash ?? "") : "";
3484
3464
  }
@@ -3496,11 +3476,9 @@ export class Orchestrator {
3496
3476
  });
3497
3477
  break;
3498
3478
  }
3499
- const sig = failureSignature([
3500
- ...conv.reasons,
3501
- ...(run.files ? [`files_manifest:${run.files.manifestSha256}`] : []),
3502
- ]);
3503
- 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("; "));
3504
3482
  if (sig !== lastSig) {
3505
3483
  triedSinceProgress = new Set();
3506
3484
  lastSig = sig;
@@ -3681,6 +3659,7 @@ export class Orchestrator {
3681
3659
  : "applied_review_blocked"
3682
3660
  : "not_applied";
3683
3661
  const revertAnchorId = convAdopted === true ? await anchors.turn(preTurnSha, lastPostTurnSha) : null;
3662
+ const capturedNoGitChange = capturedNoGitChangeValue(lastRun);
3684
3663
  store.writeYaml(join(paths.finalDir, "work_product.yaml"), {
3685
3664
  id: newId("wp"),
3686
3665
  kind: "patch",
@@ -3689,6 +3668,7 @@ export class Orchestrator {
3689
3668
  meta: {
3690
3669
  harness_id: lastRun.harnessId,
3691
3670
  result_kind: "patch",
3671
+ ...(capturedNoGitChange !== null ? { capturedNoGitChange } : {}),
3692
3672
  mode,
3693
3673
  attempts: attempt,
3694
3674
  lifecycle: facts.lifecycle,
@@ -4087,6 +4067,8 @@ export class Orchestrator {
4087
4067
  if (outcome.status !== "success") {
4088
4068
  if (outcome.telemetry?.effortResolution?.resolution === "rejected")
4089
4069
  break;
4070
+ if (outcome.telemetry?.turnLimitReached)
4071
+ break; // #299: the cap ends the run
4090
4072
  const next = adapters[idx + 1];
4091
4073
  if (next && !input.signal?.aborted) {
4092
4074
  fallbackFrom = outcome.harnessId;
@@ -4556,7 +4538,8 @@ export class Orchestrator {
4556
4538
  report: "",
4557
4539
  error: message,
4558
4540
  telemetry,
4559
- ...(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 } : {}),
4560
4543
  });
4561
4544
  attemptTelemetries.push({ attemptId, harnessId: adapter.id, telemetry });
4562
4545
  if (opts.deepScan) {
@@ -4664,7 +4647,7 @@ export class Orchestrator {
4664
4647
  // the loudest) — same single owner as the agent loop.
4665
4648
  observeBudgetSignals(ledger, log, adapter.id, attemptId, ev, budgetSignalState);
4666
4649
  this.deps.quotaEventSink?.(adapter.id, ev);
4667
- 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);
4668
4651
  cost += spend.costUsd;
4669
4652
  costEstimated ||= spend.estimated;
4670
4653
  const streamDenial = updateProcessingStreamHold(runSpec, telemetry.usageCost, ledger, lease.lease.lease_id, adapter.id, attemptId);
@@ -4692,8 +4675,6 @@ export class Orchestrator {
4692
4675
  if (!recordAdapterThrow(telemetry, err))
4693
4676
  break;
4694
4677
  }
4695
- if (telemetry.requestRefusal)
4696
- break;
4697
4678
  const newTransients = telemetry.transientFailures.slice(transientStart);
4698
4679
  const sawRetryable = newTransients.some((f) => f.retryable);
4699
4680
  const sawTypedLimit = telemetry.rateLimits.length > rateLimitStart;
@@ -4755,7 +4736,7 @@ export class Orchestrator {
4755
4736
  attemptTelemetries.push({ attemptId, harnessId: adapter.id, telemetry });
4756
4737
  // D-16: the deliverable is the report. A3: an errored attempt with no
4757
4738
  // typed final contributes no report material (acceptedTryOutput).
4758
- 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 });
4759
4740
  // Trim symmetrically with the plan path: a whitespace-only answer is not
4760
4741
  // a delivered report (the final-artifact wrapper heading would otherwise
4761
4742
  // make it read as present content by construction).
@@ -4774,7 +4755,7 @@ export class Orchestrator {
4774
4755
  workReportSource: roUnwrapped.source,
4775
4756
  workReportViolation: roUnwrapped.contractViolation,
4776
4757
  workReportUnverified: roUnwrapped.unverified ?? null,
4777
- contextTerminalExhausted: telemetry.contextExhausted,
4758
+ interruption: attemptInterruption(telemetry),
4778
4759
  });
4779
4760
  // A broken WorkReport contract is a hard failure ONLY when the finalizer
4780
4761
  // ranked it so — a concurrent terminal context exhaustion outranks it
@@ -4845,9 +4826,8 @@ export class Orchestrator {
4845
4826
  harnessId: adapter.id,
4846
4827
  status: scoutInterrupted ? "failed" : "success",
4847
4828
  report: report || "(no output)",
4848
- error: scoutInterrupted ? "context capacity exhausted before the scout completed" : null,
4829
+ error: scoutInterrupted ? interruptionDetail(roFinalized.reason) : null,
4849
4830
  telemetry,
4850
- ...(scoutInterrupted ? { interrupted: true } : {}),
4851
4831
  ...continuity.terminalFacts(roResumable),
4852
4832
  });
4853
4833
  launched.delete(tried);
@@ -4858,7 +4838,7 @@ export class Orchestrator {
4858
4838
  .join("; ")}\n`
4859
4839
  : "";
4860
4840
  const scoutTag = scoutInterrupted
4861
- ? " interrupted (context capacity exhausted)"
4841
+ ? ` interrupted (${interruptionDetail(roFinalized.reason)})`
4862
4842
  : ` (${adapter.id})`;
4863
4843
  store.writeText(join(paths.findingsDir, `${attemptId}${scoutInterrupted ? "-interrupted" : ""}.md`), `# Explorer ${attemptId}${scoutTag}\n\n${report || "(no output)"}${warningNote}\n`);
4864
4844
  }
@@ -4936,12 +4916,13 @@ export class Orchestrator {
4936
4916
  if (last?.telemetry.effortResolution?.resolution === "rejected")
4937
4917
  break;
4938
4918
  // Per-harness fallback_model: one same-harness retry on FAILURE (not
4939
- // policy blocks) before falling through to the next harness.
4919
+ // policy blocks or the caller's max-turns cap, #299) before the next harness.
4940
4920
  const fallbackModel = routed.settings?.fallbackModel;
4941
4921
  const firstModel = contract.routing_models[routed.adapter.id] ?? routed.settings?.defaultModel ?? null;
4942
4922
  if (last &&
4943
4923
  last.status === "failed" &&
4944
4924
  !last.telemetry.requestRefusal &&
4925
+ !last.telemetry.turnLimitReached &&
4945
4926
  fallbackModel &&
4946
4927
  fallbackModel !== firstModel &&
4947
4928
  !budgetStopped &&