fix: preserve failed responses when retrying model calls

Keep failed attempts in the saved transcript and omit only trailing failures from Retry's temporary agent history. Use the matching temporary prefix length when persisting genuinely new output.

Preserve the error notice across plan/compaction markers, refuse Retry after later compaction, and avoid synthesizing results for tool calls inside failed generations while retaining Pi's native filtering.

Verified: 173 focused Retry/session/compaction-projection/practice-record tests, typecheck, changed-file lint, and diff --check. No app launch or real provider calls.
This commit is contained in:
amosblomqvist
2026-10-27 16:49:29 +02:00
parent a572675900
commit 82c6999a63
7 changed files with 559 additions and 200 deletions
+6 -5
View File
@@ -19,6 +19,7 @@ import {
needsContinuation,
danglingQuestionOf,
answeredCallIds,
failedResponseAtEnd,
measuredContextTokens,
newAnswerResult,
newNextMessage,
@@ -286,12 +287,12 @@ export class SessionModel {
? { executions: this.executions, inFlight: true, streamingIds: this.streamingIds }
: undefined;
const messages = [...this.base, ...this.tail];
const latest = messages.at(-1);
// One rule for live and restored failures: only the final completed
// assistant can own the banner. While trying again, hide the old
// failure from the first Thinking frame, but show a new failure at end.
const latest = failedResponseAtEnd(messages);
// Presentation/compaction markers do not resolve a failure. While
// trying again, hide the old banner from the first Thinking frame,
// but show a new completed failure from this consume immediately.
const providerError =
latest?.role === "assistant" && latest.stopReason === "error" &&
latest !== undefined &&
!this.incompleteUsageIds.has(latest.id) && (!live || latest.id === this.liveFailureId)
? { message: latest.errorMessage ?? "An error occurred." }
: null;