Skip to content

Commit c3fd0af

Browse files
ruomuxydtclaude
andcommitted
fix(cron): deliver recovered answer for no-preference channels (#94846)
When a recovered tool-warning run has finalAssistantVisibleText, the output fields (summary, outputText, deliveryPayloads) now use the recovered answer, not the stale warning payload, even for channels that do not prefer final assistant visible text. This addresses ClawSweeper rank-up move: 'Fix the no-preference finalAssistantVisibleText-only recovery case so the delivered payload is the recovered answer.' Co-Authored-By: Claude <[email protected]>
1 parent f54ba90 commit c3fd0af

2 files changed

Lines changed: 22 additions & 6 deletions

File tree

src/cron/isolated-agent/helpers.test.ts

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -243,10 +243,10 @@ describe("resolveCronPayloadOutcome", () => {
243243
});
244244
// The recovered run should not be fatal
245245
expect(result.hasFatalErrorPayload).toBe(false);
246-
// Output fields should reflect the successful recovery, not the formatted run-level error
247-
expect(result.summary).toBe("Patch applied successfully.");
248-
expect(result.outputText).toBe("Patch applied successfully.");
249-
expect(result.deliveryPayloads).toEqual([{ text: "Patch applied successfully." }]);
246+
// Output fields should reflect the recovered answer (final assistant text), not the formatted run-level error
247+
expect(result.summary).toContain("REPRO_FINAL_OUTPUT_PRESENT");
248+
expect(result.outputText).toContain("REPRO_FINAL_OUTPUT_PRESENT");
249+
expect(result.deliveryPayloads).toEqual([{ text: "REPRO_FINAL_OUTPUT_PRESENT" }]);
250250
});
251251

252252
it("still treats a genuine fatal error as fatal when no recovery occurs", () => {
@@ -295,6 +295,21 @@ describe("resolveCronPayloadOutcome", () => {
295295
expect(result.summary).toContain("Task completed despite missing file.");
296296
});
297297

298+
it("delivers the recovered answer for no-preference channels when finalAssistantVisibleText exists", () => {
299+
// No preferFinalAssistantVisibleText (default false) — recovered tool-warning should still use the final answer
300+
const result = resolveCronPayloadOutcome({
301+
payloads: [
302+
{ text: "⚠️ 🛠️ sed: file missing", isError: true },
303+
],
304+
runLevelError: new Error("sed: file missing"),
305+
finalAssistantVisibleText: "Task completed despite missing file.",
306+
});
307+
expect(result.hasFatalErrorPayload).toBe(false);
308+
expect(result.summary).toContain("Task completed despite missing file.");
309+
expect(result.outputText).toContain("Task completed despite missing file.");
310+
expect(result.deliveryPayloads).toEqual([{ text: "Task completed despite missing file." }]);
311+
});
312+
298313
it("preserves original error payload text for generic fatal run-level errors with later success", () => {
299314
// Generic fatal (not tool warning) with later success-looking payload
300315
const result = resolveCronPayloadOutcome({

src/cron/isolated-agent/helpers.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -309,10 +309,11 @@ export function resolveCronPayloadOutcome(params: {
309309
// A final assistant answer can replace textual warning payloads, but never
310310
// structured/media payloads that carry the actual delivery content.
311311
const shouldUseFinalAssistantVisibleText =
312-
params.preferFinalAssistantVisibleText === true &&
313312
normalizedFinalAssistantVisibleText !== undefined &&
314313
!hasFatalStructuredErrorPayload &&
315-
!hasStructuredDeliveryPayloads;
314+
!hasStructuredDeliveryPayloads &&
315+
(params.preferFinalAssistantVisibleText === true ||
316+
(hasRecoveringTerminalOutput && isLastErrorAToolWarning));
316317
const summary = shouldUseFinalAssistantVisibleText
317318
? (pickSummaryFromOutput(normalizedFinalAssistantVisibleText) ?? fallbackSummary)
318319
: fallbackSummary;

0 commit comments

Comments
 (0)