@claudexor/orchestrator 5.1.0 → 6.1.1

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 (74) hide show
  1. package/dist/attemptTelemetry.d.ts.map +1 -1
  2. package/dist/attemptTelemetry.js +1 -0
  3. package/dist/attemptTelemetry.js.map +1 -1
  4. package/dist/candidateEvidence.d.ts +8 -0
  5. package/dist/candidateEvidence.d.ts.map +1 -1
  6. package/dist/candidateEvidence.js +14 -12
  7. package/dist/candidateEvidence.js.map +1 -1
  8. package/dist/candidateTerminals.d.ts +17 -1
  9. package/dist/candidateTerminals.d.ts.map +1 -1
  10. package/dist/candidateTerminals.js +24 -1
  11. package/dist/candidateTerminals.js.map +1 -1
  12. package/dist/continuation-evidence-io.d.ts.map +1 -1
  13. package/dist/continuation-evidence-io.js +1 -1
  14. package/dist/continuation-evidence-io.js.map +1 -1
  15. package/dist/continuation-evidence.d.ts +0 -6
  16. package/dist/continuation-evidence.d.ts.map +1 -1
  17. package/dist/continuation-evidence.js +1 -8
  18. package/dist/continuation-evidence.js.map +1 -1
  19. package/dist/continuity-facts.d.ts +2 -0
  20. package/dist/continuity-facts.d.ts.map +1 -1
  21. package/dist/continuity-facts.js +1 -0
  22. package/dist/continuity-facts.js.map +1 -1
  23. package/dist/continuity-summary.d.ts +3 -0
  24. package/dist/continuity-summary.d.ts.map +1 -1
  25. package/dist/continuity-summary.js +3 -1
  26. package/dist/continuity-summary.js.map +1 -1
  27. package/dist/delegatedHome.d.ts +6 -3
  28. package/dist/delegatedHome.d.ts.map +1 -1
  29. package/dist/delegatedHome.js +16 -4
  30. package/dist/delegatedHome.js.map +1 -1
  31. package/dist/delegationFailure.d.ts +5 -4
  32. package/dist/delegationFailure.d.ts.map +1 -1
  33. package/dist/delegationFailure.js +7 -5
  34. package/dist/delegationFailure.js.map +1 -1
  35. package/dist/git-precondition.d.ts +0 -2
  36. package/dist/git-precondition.d.ts.map +1 -1
  37. package/dist/git-precondition.js +1 -20
  38. package/dist/git-precondition.js.map +1 -1
  39. package/dist/harnessFailure.d.ts +20 -0
  40. package/dist/harnessFailure.d.ts.map +1 -1
  41. package/dist/harnessFailure.js +34 -0
  42. package/dist/harnessFailure.js.map +1 -1
  43. package/dist/interaction.d.ts +14 -1
  44. package/dist/interaction.d.ts.map +1 -1
  45. package/dist/interaction.js +91 -22
  46. package/dist/interaction.js.map +1 -1
  47. package/dist/orchestrator.d.ts.map +1 -1
  48. package/dist/orchestrator.js +47 -51
  49. package/dist/orchestrator.js.map +1 -1
  50. package/dist/planRun.d.ts.map +1 -1
  51. package/dist/planRun.js +3 -4
  52. package/dist/planRun.js.map +1 -1
  53. package/dist/pool-attempt-evidence.d.ts +2 -0
  54. package/dist/pool-attempt-evidence.d.ts.map +1 -1
  55. package/dist/pool-attempt-evidence.js +22 -3
  56. package/dist/pool-attempt-evidence.js.map +1 -1
  57. package/dist/processing-telemetry.d.ts +3 -1
  58. package/dist/processing-telemetry.d.ts.map +1 -1
  59. package/dist/processing-telemetry.js +11 -0
  60. package/dist/processing-telemetry.js.map +1 -1
  61. package/dist/rawPatchLane.d.ts +26 -0
  62. package/dist/rawPatchLane.d.ts.map +1 -1
  63. package/dist/rawPatchLane.js +38 -1
  64. package/dist/rawPatchLane.js.map +1 -1
  65. package/dist/readonlyTerminals.d.ts.map +1 -1
  66. package/dist/readonlyTerminals.js +6 -4
  67. package/dist/readonlyTerminals.js.map +1 -1
  68. package/dist/runSupport.d.ts.map +1 -1
  69. package/dist/runSupport.js +3 -2
  70. package/dist/runSupport.js.map +1 -1
  71. package/dist/runTerminalResults.d.ts.map +1 -1
  72. package/dist/runTerminalResults.js +14 -2
  73. package/dist/runTerminalResults.js.map +1 -1
  74. package/package.json +17 -17
@@ -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, erroredWinnerFailure, failedCandidatesResult, failAfterCandidates, } from "./candidateTerminals.js";
10
+ import { cancelledCandidatesResult, emptyCandidateResult, erroredWinnerFailure, failedCandidatesResult, failAfterCandidates, convergenceCause, raceStatement, } 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";
@@ -54,7 +54,7 @@ import { dropDeltaPastBudget, emitPlanProgress, emitTransientExhausted, observeU
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
- import { ensureClaudeBridgeForRun, ensureWriteModeGitBoundary } from "./git-precondition.js";
57
+ import { ensureWriteModeGitBoundary } from "./git-precondition.js";
58
58
  import { buildContinuation } from "./continuity.js";
59
59
  import { resolveContinuitySummary, threadContinuityRequest } from "./continuity-facts.js";
60
60
  import { runDiffReview } from "./diffReview.js";
@@ -75,9 +75,9 @@ import { prepareReviewEvidenceDir, recordReviewEvidenceCleanup } from "./reviewE
75
75
  import { savedForSynthesis } from "./savedCandidate.js";
76
76
  import { ArtifactStore } from "@claudexor/artifact-store";
77
77
  import { buildContextPack, writeEvidencePacket } from "@claudexor/context";
78
- import { RawPatchLane, correctableRefusal, endsConvergence } from "./rawPatchLane.js";
78
+ import { RawAdoption, RawPatchLane, correctableRefusal, endsConvergence } from "./rawPatchLane.js";
79
79
  import { runRuntimeRoot } from "./run-runtime-root.js";
80
- import { WorkspaceManager, snapshotTree, } from "@claudexor/workspace";
80
+ import { WorkspaceManager, rawAwareSnapshot, } from "@claudexor/workspace";
81
81
  import { blockedDecisionOverride, finalVerifyBlocks, verifyAndDeliverFiles, verifyAndDeliver, } from "@claudexor/delivery";
82
82
  import { HarnessGateway } from "@claudexor/gateway";
83
83
  import { ReadinessLedger, evaluateConvergence, failureSignature, gatesPassed, requireReadingReviewers, reviewCandidate, runGates, } from "@claudexor/review";
@@ -1159,6 +1159,7 @@ export class Orchestrator {
1159
1159
  threadId: runInput.threadId,
1160
1160
  projectRoot: runInput.repoRoot,
1161
1161
  cwd: repoRoot,
1162
+ inputFileDir: paths.contextDir,
1162
1163
  adapter: this.deps.registry.get(harnessId),
1163
1164
  model: this.deps.registry.get(harnessId)?.modelRequirement && runInput.models?.[harnessId],
1164
1165
  credentialProfile: sessionFields.credential_profile,
@@ -1307,6 +1308,7 @@ export class Orchestrator {
1307
1308
  : join(envelope.worktree_path, artifactRelativeDir, CLAUDEXOR_BROWSER_ARTIFACT_SUBDIR)),
1308
1309
  extra_mcp_servers: delegationBeltFor(runInput, intent, routed, contract.budget.paid_budget),
1309
1310
  cwd: envelope.worktree_path,
1311
+ input_file_dir: paths.contextDir,
1310
1312
  access: routed.adapterAccess,
1311
1313
  // Writable lanes only: routing admitted this list for this exact lane.
1312
1314
  auxiliary_writable_roots: routed.auxiliaryWritableRoots,
@@ -1882,22 +1884,12 @@ export class Orchestrator {
1882
1884
  };
1883
1885
  }
1884
1886
  }
1885
- // Same run-prep stage as the git boundary: if the PROJECT root uses AGENTS.md
1886
- // with no CLAUDE.md, bridge it so a Claude Code candidate reads it (INV-113).
1887
- if (mutatingRun && input.workspaceKind !== "directory") {
1888
- ensureClaudeBridgeForRun(input.repoRoot, input.inPlace === true, log);
1889
- }
1890
1887
  // Pre-turn snapshot of the live tree for in-place runs: the revert restore
1891
1888
  // target (server-owned revertInPlace). A snapshot failure must never fail the
1892
1889
  // run — revert is simply unavailable then.
1893
1890
  let preTurnSha = null;
1894
1891
  if (mutatingRun && input.inPlace === true && input.workspaceKind !== "directory") {
1895
- try {
1896
- preTurnSha = await snapshotTree(execRoot);
1897
- }
1898
- catch {
1899
- preTurnSha = null;
1900
- }
1892
+ preTurnSha = await rawAwareSnapshot(execRoot).catch(() => null);
1901
1893
  }
1902
1894
  // ContextPack is LAZY: agent/race candidates explore the live tree
1903
1895
  // themselves inside their envelopes; only the read-only report modes
@@ -1948,6 +1940,7 @@ export class Orchestrator {
1948
1940
  for (const env of envelopes)
1949
1941
  await kept.settle(wsm, env, runsBySlot);
1950
1942
  };
1943
+ const rawAdoption = new RawAdoption(execRoot, input);
1951
1944
  const candidateAccess = contract.access.effective_profile;
1952
1945
  let budgetStopped = false;
1953
1946
  // QA-050: keep the ledger's typed denial so the zero-candidate terminal
@@ -2072,6 +2065,9 @@ export class Orchestrator {
2072
2065
  requestedSingleCandidate &&
2073
2066
  slot.routed.implementationTransport !== "git_patch_envelope"),
2074
2067
  });
2068
+ // A continueFrom envelope already owns its record: no empty source overwrites it.
2069
+ if (!input.continuation?.adopt)
2070
+ await rawAdoption.seed(envelope.worktree_path);
2075
2071
  harnessHome = this.harnessHomeFor(wsm, envelope, slot.routed, input);
2076
2072
  const run = await this.runCandidateInEnvelope(slot.routed, envelope, slot.attemptId, slot.label, contract, input.prompt, store, paths, wsm, ledger, {
2077
2073
  lease: slot.lease,
@@ -2192,6 +2188,8 @@ export class Orchestrator {
2192
2188
  }
2193
2189
  }
2194
2190
  }
2191
+ if (!effectiveRun.errored && secretDiff.hasCandidateChanges(effectiveRun))
2192
+ await rawAdoption.keep(effectiveRun.attemptId, envelope.worktree_path);
2195
2193
  runsBySlot[slotIdx] = effectiveRun;
2196
2194
  reviewEnvelopes.push(envelope);
2197
2195
  envelope = undefined;
@@ -2202,6 +2200,7 @@ export class Orchestrator {
2202
2200
  const message = safeErrorMessage(err);
2203
2201
  const declared = declaredFailure(err);
2204
2202
  const infraPhase = envelope === undefined ? "workspace" : "harness";
2203
+ const applied = appliedAttemptFacts(harnessHome, slot.routed.adapterAccess, this.credentials.effectiveProfileId(input, adapter.id));
2205
2204
  log.emit("harness.completed", {
2206
2205
  harness_id: adapter.id,
2207
2206
  attempt_id: slot.attemptId,
@@ -2209,7 +2208,7 @@ export class Orchestrator {
2209
2208
  error: message,
2210
2209
  phase: infraPhase,
2211
2210
  });
2212
- store.writeYaml(join(paths.attemptsDir, slot.attemptId, "attempt.yaml"), AC.attemptFailureRecord(slot.attemptId, adapter.id, failureCost, infraPhase, message, appliedAttemptFacts(harnessHome, slot.routed.adapterAccess, this.credentials.effectiveProfileId(input, adapter.id))));
2211
+ store.writeYaml(join(paths.attemptsDir, slot.attemptId, "attempt.yaml"), AC.attemptFailureRecord(slot.attemptId, adapter.id, failureCost, infraPhase, message, applied));
2213
2212
  runsBySlot[slotIdx] = {
2214
2213
  attemptId: slot.attemptId,
2215
2214
  harnessId: adapter.id,
@@ -2231,7 +2230,9 @@ export class Orchestrator {
2231
2230
  // alive past this catch; `message` alone would force the terminal to
2232
2231
  // read prose back out.
2233
2232
  ...(declared.code || declared.quotaRefusal ? { declaredFailure: declared } : {}),
2234
- applied: appliedAttemptFacts(harnessHome, slot.routed.adapterAccess, this.credentials.effectiveProfileId(input, adapter.id)),
2233
+ applied,
2234
+ // No HOME was decided, so no child started (#648); never read from a record.
2235
+ ...(harnessHome === undefined ? { homeUndecided: true } : {}),
2235
2236
  };
2236
2237
  }
2237
2238
  finally {
@@ -2289,12 +2290,7 @@ export class Orchestrator {
2289
2290
  input.inPlace &&
2290
2291
  requestedSingleCandidate &&
2291
2292
  needsPostTurnFence(runs)) {
2292
- try {
2293
- earlyPostTurnSha = await snapshotTree(execRoot);
2294
- }
2295
- catch {
2296
- earlyPostTurnSha = null;
2297
- }
2293
+ earlyPostTurnSha = await rawAwareSnapshot(execRoot).catch(() => null);
2298
2294
  }
2299
2295
  if (input.signal?.aborted) {
2300
2296
  await disposeReviewEnvelopes();
@@ -2447,6 +2443,7 @@ export class Orchestrator {
2447
2443
  workspaceKind: input.workspaceKind,
2448
2444
  scopePaths: input.scopePaths,
2449
2445
  });
2446
+ await rawAdoption.seed(envelope.worktree_path);
2450
2447
  const synthHome = this.harnessHomeFor(wsm, envelope, synthRouted, input);
2451
2448
  const run = await this.runCandidateInEnvelope(synthRouted, envelope, "synth", "Synthesis", contract, synthesisInput.prompt, store, paths, wsm, ledger, {
2452
2449
  lease: lease.lease,
@@ -2456,6 +2453,8 @@ export class Orchestrator {
2456
2453
  },
2457
2454
  }, candidateAccess, input.onHarnessEvent, input.signal, undefined, input.effort, "synthesize", log, effectiveWeb, interactionChannelFor(input, log, runId, taskId, "synth", synthAdapter.id, synthRouted.supportsInteractive), undefined, input, false, synthesisInput.content, undefined, synthHome, undefined, synthTried);
2458
2455
  ledger.settle(lease.lease?.lease_id ?? "", attemptUsageCostSettlement(run.cost, run.costEstimated, run.attemptId, run.harnessId, run.telemetry.authMode, run.telemetry.usageCost));
2456
+ if (!run.errored && secretDiff.hasCandidateChanges(run))
2457
+ await rawAdoption.keep(run.attemptId, envelope.worktree_path);
2459
2458
  reviewEnvelopes.push(envelope);
2460
2459
  envelope = undefined;
2461
2460
  // D-16 r8: only a WORKING synth is reviewed/adopted (same veto owner
@@ -2700,8 +2699,9 @@ export class Orchestrator {
2700
2699
  applied: true,
2701
2700
  patch_sha256: patchSha256,
2702
2701
  winner: winnerRun.attemptId,
2702
+ ...(await rawAdoption.publish(winnerRun.attemptId)),
2703
2703
  });
2704
- postTurnSha = await snapshotTree(execRoot).catch(() => null);
2704
+ postTurnSha = await rawAwareSnapshot(execRoot).catch(() => null);
2705
2705
  revertAnchorId = await anchors.after(winnerRun, applied);
2706
2706
  }
2707
2707
  else {
@@ -2719,6 +2719,7 @@ export class Orchestrator {
2719
2719
  patch_sha256: patchSha256,
2720
2720
  detail: redactSecrets(applied.detail ?? "apply failed"),
2721
2721
  tree_mutated: applied.treeMutated,
2722
+ ...(adopted ? await rawAdoption.publish(winnerRun.attemptId) : {}),
2722
2723
  });
2723
2724
  }
2724
2725
  }
@@ -2796,6 +2797,8 @@ export class Orchestrator {
2796
2797
  // (applyable, no_changes, or not-verified) is an honest completion.
2797
2798
  const needsDecisionTerminal = facts.review === "blocked" || facts.checks === "failed";
2798
2799
  const isFailureTerminal = facts.lifecycle !== "succeeded" || needsDecisionTerminal;
2800
+ const decisionBlock = needsHuman || needsDecisionTerminal;
2801
+ const statement = raceStatement(winnerRun, decisionBlock, result.decision.why_winner);
2799
2802
  const pool = racePool();
2800
2803
  // Beside whichever failure record leads, a source another attempt kept stays named.
2801
2804
  const keptSources = candidateCaptureRefusals(runs).flatMap(secretDiff.captureInspectionActions);
@@ -2840,7 +2843,6 @@ export class Orchestrator {
2840
2843
  // stays `internal`. A review/checks block is `policy` (the acceptance
2841
2844
  // path), phase `review`, and its remedy names the decision, not "retry
2842
2845
  // with a different harness".
2843
- const decisionBlock = needsHuman || needsDecisionTerminal;
2844
2846
  writeFailure(store, paths, {
2845
2847
  phase: decisionBlock ? "review" : "arbitration",
2846
2848
  category: decisionBlock
@@ -2856,7 +2858,7 @@ export class Orchestrator {
2856
2858
  ? `review escalated to a human decision: ${result.decision.why_winner}`
2857
2859
  : needsDecisionTerminal
2858
2860
  ? `review blocked before apply: ${result.decision.why_winner}`
2859
- : result.decision.why_winner, pool),
2861
+ : statement, pool),
2860
2862
  poolAttempts: pool,
2861
2863
  rawDetailRef: winnerRun?.errored
2862
2864
  ? `attempts/${winnerRun.attemptId}/attempt.yaml`
@@ -2937,7 +2939,7 @@ export class Orchestrator {
2937
2939
  facts,
2938
2940
  winner: result.decision.winner,
2939
2941
  runDir: paths.root,
2940
- summary: withPoolAttempts(result.decision.why_winner, pool),
2942
+ summary: withPoolAttempts(statement, pool),
2941
2943
  candidates: candidateStatuses(runs),
2942
2944
  decisionPath,
2943
2945
  reviewVerified: actualReviewVerified,
@@ -3059,11 +3061,6 @@ export class Orchestrator {
3059
3061
  candidates: [],
3060
3062
  };
3061
3063
  }
3062
- // Same run-prep stage as the git boundary (and the same `!inPlace`
3063
- // exclusion — we are inside that branch, so inPlace is false here): bridge
3064
- // an AGENTS.md-only PROJECT root so a Claude Code convergence attempt reads
3065
- // it (INV-113).
3066
- ensureClaudeBridgeForRun(input.repoRoot, false, log);
3067
3064
  }
3068
3065
  const reviewDir = join(paths.root, "review-evidence");
3069
3066
  if (contract.review_requested !== false)
@@ -3187,12 +3184,7 @@ export class Orchestrator {
3187
3184
  // access level the envelope did not actually run with.
3188
3185
  const convergenceAccess = contract.access.effective_profile;
3189
3186
  if (input.inPlace === true && input.workspaceKind !== "directory") {
3190
- try {
3191
- preTurnSha = await snapshotTree(execRoot);
3192
- }
3193
- catch {
3194
- preTurnSha = null;
3195
- }
3187
+ preTurnSha = await rawAwareSnapshot(execRoot).catch(() => null);
3196
3188
  }
3197
3189
  envelope = await wsm.create({
3198
3190
  taskId,
@@ -3285,15 +3277,16 @@ export class Orchestrator {
3285
3277
  // A5: keep a TYPED refusal alive past this catch (race-lane parity).
3286
3278
  const declared = declaredFailure(err);
3287
3279
  ledger.settle(lease.lease?.lease_id ?? "", failureCost.settlement);
3280
+ const applied = appliedAttemptFacts(harnessHome, routed.adapterAccess, this.credentials.effectiveProfileId(input, adapter.id));
3288
3281
  log.emit("harness.completed", {
3289
3282
  harness_id: adapter.id,
3290
3283
  attempt_id: attemptId,
3291
3284
  status: "failed",
3292
3285
  error: message,
3293
3286
  });
3294
- store.writeYaml(join(paths.attemptsDir, attemptId, "attempt.yaml"), AC.attemptFailureRecord(attemptId, adapter.id, failureCost, "harness", message, appliedAttemptFacts(harnessHome, routed.adapterAccess, this.credentials.effectiveProfileId(input, adapter.id))));
3287
+ store.writeYaml(join(paths.attemptsDir, attemptId, "attempt.yaml"), AC.attemptFailureRecord(attemptId, adapter.id, failureCost, "harness", message, applied));
3295
3288
  run = {
3296
- applied: appliedAttemptFacts(harnessHome, routed.adapterAccess, this.credentials.effectiveProfileId(input, adapter.id)),
3289
+ applied,
3297
3290
  attemptId,
3298
3291
  harnessId: adapter.id,
3299
3292
  label: `Attempt ${attempt}`,
@@ -3316,6 +3309,9 @@ export class Orchestrator {
3316
3309
  holder: { runId: paths.runId, runDir: paths.root },
3317
3310
  }));
3318
3311
  }
3312
+ // Refused before its HOME was decided, no child ran: that refusal ends the run (#648).
3313
+ if (input.delegated === true && harnessHome === undefined)
3314
+ throw err;
3319
3315
  }
3320
3316
  lastRun = run;
3321
3317
  convergenceRuns.push(run);
@@ -3328,12 +3324,7 @@ export class Orchestrator {
3328
3324
  // harness mutated it, before this attempt's review). The last attempt's
3329
3325
  // value is the revert target persisted into work_product.yaml (an unchanged one has none).
3330
3326
  if (input.inPlace && input.workspaceKind !== "directory" && needsPostTurnFence([run])) {
3331
- try {
3332
- lastPostTurnSha = await snapshotTree(execRoot);
3333
- }
3334
- catch {
3335
- lastPostTurnSha = null;
3336
- }
3327
+ lastPostTurnSha = await rawAwareSnapshot(execRoot).catch(() => null);
3337
3328
  }
3338
3329
  // Cancellation/deadline keeps priority over a belt failure finalized concurrently.
3339
3330
  if (input.signal?.aborted || processingBudgetDenial)
@@ -3614,6 +3605,7 @@ export class Orchestrator {
3614
3605
  const convNeedsDecision = facts.review === "blocked" || facts.checks === "failed";
3615
3606
  // Every attempt's failed tries by the actual outcome; a block is not an account failure.
3616
3607
  const convPool = terminalPoolAttempts(facts, convergenceRuns);
3608
+ const convCause = convergenceCause(lastRun, facts, Boolean(convBudgetMapping));
3617
3609
  if (decision) {
3618
3610
  // Shared honesty owner (same as the race path): a needs-decision terminal
3619
3611
  // overrides the persisted decision; final_verify is recorded either way.
@@ -3689,7 +3681,7 @@ export class Orchestrator {
3689
3681
  store.writeText(join(paths.finalDir, "answer.md"), lastRun.answerText);
3690
3682
  log.emit("output.ready", { kind: "answer", path: "final/answer.md" });
3691
3683
  }
3692
- store.writeText(join(paths.finalDir, "summary.md"), `${withPoolAttempts(`# Run ${runId} (${mode})\n\n- Lifecycle: ${facts.lifecycle}${facts.reason ? ` (${facts.reason})` : ""}\n- Attempts: ${attempt}\n- Winner: ${lastRun.attemptId}\n- Review verified (cross-family): ${actualReviewVerified}\n- Apply recommendation: ${decision?.apply_recommendation ?? "inspect"}${stuckNoProgressReason ? `\n- No-progress reason: ${stuckNoProgressReason}` : ""}${secretDiff.resultSummaryDisclosure(lastRun)}`, convPool)}\n`);
3684
+ store.writeText(join(paths.finalDir, "summary.md"), `${withPoolAttempts(`# Run ${runId} (${mode})\n\n- Lifecycle: ${facts.lifecycle}${facts.reason ? ` (${facts.reason})` : ""}\n- Attempts: ${attempt}\n- Winner: ${lastRun.attemptId}\n- Review verified (cross-family): ${actualReviewVerified}\n- Apply recommendation: ${decision?.apply_recommendation ?? "inspect"}${stuckNoProgressReason ? `\n- No-progress reason: ${stuckNoProgressReason}` : ""}${convCause ? `\n- Failure: ${convCause}` : ""}${secretDiff.resultSummaryDisclosure(lastRun)}`, convPool)}\n`);
3693
3685
  // Lifecycle invariant (all modes): output.ready precedes the terminal
3694
3686
  // event so a client that applied the terminal event has the output.
3695
3687
  log.emit("output.ready", {
@@ -3725,6 +3717,7 @@ export class Orchestrator {
3725
3717
  rawContextShortfall: convDeclared?.rawContextShortfall,
3726
3718
  harnessId: lastRun?.harnessId,
3727
3719
  attemptId: lastRun?.attemptId,
3720
+ vendorFailure: convCause ? lastRun?.telemetry.vendorFailure : null,
3728
3721
  runDir: paths.root,
3729
3722
  captureFailure: lastRun?.captureRefusal?.failure,
3730
3723
  nextActions: [
@@ -3763,7 +3756,7 @@ export class Orchestrator {
3763
3756
  ? `review escalated to a human decision after ${attempt} attempt(s)`
3764
3757
  : facts.reason === "stuck_no_progress"
3765
3758
  ? (stuckNoProgressReason ?? `stuck_no_progress after ${attempt} attempt(s)`)
3766
- : `${facts.lifecycle}${facts.reason ? ` (${facts.reason})` : ""} after ${attempt} attempt(s)${lastDiffStable ? "" : " (diff changed after review; review is stale)"}`), convPool),
3759
+ : `${convCause ? `${convCause}\n` : ""}${facts.lifecycle}${facts.reason ? ` (${facts.reason})` : ""} after ${attempt} attempt(s)${lastDiffStable ? "" : " (diff changed after review; review is stale)"}`), convPool),
3767
3760
  poolAttempts: convPool,
3768
3761
  });
3769
3762
  // D-16 r8/r9: an INTERRUPTED envelope run still gets its diagnostic
@@ -3839,7 +3832,7 @@ export class Orchestrator {
3839
3832
  : {}),
3840
3833
  summary: withPoolAttempts(converged
3841
3834
  ? `converged in ${attempt} attempt(s)`
3842
- : `${facts.lifecycle} after ${attempt} attempt(s)`, convPool),
3835
+ : `${convCause ? `${convCause}\n` : ""}${facts.lifecycle} after ${attempt} attempt(s)`, convPool),
3843
3836
  candidates: lastRun
3844
3837
  ? [
3845
3838
  {
@@ -3874,6 +3867,7 @@ export class Orchestrator {
3874
3867
  ? `${args.promptBody}\n\n${laneContinuity.pointerLine}`
3875
3868
  : args.promptBody,
3876
3869
  cwd: this.execRootOf(input),
3870
+ input_file_dir: paths.contextDir,
3877
3871
  access: "readonly",
3878
3872
  attachments: input.attachments ?? [],
3879
3873
  ...sessionFields,
@@ -4222,7 +4216,7 @@ export class Orchestrator {
4222
4216
  * route/session/knob machinery HERE and hands the module only finished public
4223
4217
  * types (a `HarnessRunSpec`, cost evidence, a disposable home).
4224
4218
  */
4225
- deepScanReducerDeps(input, contract, log) {
4219
+ deepScanReducerDeps(input, contract, log, paths) {
4226
4220
  return {
4227
4221
  newReadOnlyHome: () => scopedRouteContext(resolveReadOnlyRouteContext(this.execRootOf(input)), input.runScopeMarker),
4228
4222
  costEvidence: (harnessId, attemptId, routed) =>
@@ -4239,6 +4233,7 @@ export class Orchestrator {
4239
4233
  intent: "synthesize",
4240
4234
  prompt,
4241
4235
  cwd: this.execRootOf(input),
4236
+ input_file_dir: paths.contextDir,
4242
4237
  access: "readonly",
4243
4238
  attachments: [],
4244
4239
  auth_preference: sessionFields.auth_preference,
@@ -4472,6 +4467,7 @@ export class Orchestrator {
4472
4467
  intent: opts.intent,
4473
4468
  prompt: promptWithPointers,
4474
4469
  cwd: this.execRootOf(input),
4470
+ input_file_dir: paths.contextDir,
4475
4471
  access: "readonly",
4476
4472
  // ASK/EXPLORE/AUDIT read-only runs must forward the user's attachments —
4477
4473
  // a live "describe this image" turn sent an image that was being dropped here, so
@@ -5016,7 +5012,7 @@ export class Orchestrator {
5016
5012
  let deepScanSynthesis = null;
5017
5013
  let reducedReport = null;
5018
5014
  if (opts.deepScan) {
5019
- ({ deepScanSynthesis, reducedReport } = await resolveDeepScanSynthesis(this.deepScanReducerDeps(input, contract, log), {
5015
+ ({ deepScanSynthesis, reducedReport } = await resolveDeepScanSynthesis(this.deepScanReducerDeps(input, contract, log, paths), {
5020
5016
  succeeded,
5021
5017
  adapters,
5022
5018
  budgetStopped,