feat(tui): show live retry progress in the activity pane#1861
Conversation
The engine emits turn.step.retrying with full attempt/backoff data, but the interactive TUI dropped the event, so LLM retries (up to 10 attempts over ~3 minutes for sustained 429s) showed only the generic moon spinner and the session appeared frozen. Print mode and the v2 activity view already surface this event; the shell TUI was the only consumer that did not. Add a 'retrying' streaming phase driven by the event: the activity pane spinner shows a warning-colored live label such as "Rate limited (429) · attempt 3/10 · retrying in 12s", counting down on MoonLoader's existing 120ms tick via a new setLabelFn (no extra timer) and degrading to "retrying now…" once the backoff elapses. The failed attempt's partial live output is discarded before the next attempt re-streams (new StreamingUIController.discardStreamingBlock removes the already-rendered block; the existing reset methods only clear buffers). retryStatus is cleared centrally in setAppState on any phase transition out of 'retrying', with defensive clears on the paths that set no phase (empty thinking delta, tool-call start, step completed). Esc during backoff already aborts the sleep; no changes needed there. No transcript writes per retry; behavior is unchanged when no retry occurs.
🦋 Changeset detectedLatest commit: 7b1cd6c The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a57f1b6730
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| this.host.streamingUI.discardStreamingBlock(); | ||
| this.host.streamingUI.resetLiveText(); | ||
| this.host.streamingUI.resetToolUi(); |
There was a problem hiding this comment.
Discard all failed-attempt artifacts on retry
When a retry is triggered after the failed attempt has already rendered thinking or a tool-call preview, this path only removes the active assistant _streamingBlock; resetLiveText() just disposes the live ThinkingComponent, and resetToolUi() only disposes/clears pending tool components, leaving their mounted transcript children (and any assistant text finalized when the tool preview started) in place. Since the retry re-streams the step from the beginning, those stale thinking/tool/assistant fragments remain above the fresh attempt and duplicate or misrepresent the final transcript.
Useful? React with 👍 / 👎.
A retry re-streams the step from scratch, but the discard path only removed the active assistant streaming block. Thinking bubbles and tool-call previews are mounted into the transcript and merely disposed (spinner stopped) on reset, and assistant text already finalized when a tool preview started had dropped its ref entirely — so all of them stayed rendered above the fresh attempt, duplicating content. Track every transcript component the streaming UI mounts during the current step (assistant blocks with their transcript entries, thinking, tool previews and their group containers) and pull them all on turn.step.retrying. The scope clears at step boundaries so committed output from completed steps is never touched; group upgrades replace a solo card in place, and Container.removeChild is a no-op for non-children, so tracking both card and group stays safe.
|
Good catch — confirmed and fixed in 7b1cd6c. Verified against the code: The fix tracks every transcript component the streaming UI mounts during the current step (assistant blocks + their transcript entries, thinking bubbles, tool previews and their group containers) and pulls them all on |
Related Issue
Closes #1860 — together with #1857, which fixes the engine-side fail-fast half.
Problem
See #1860: the engine emits
turn.step.retrying(attempt, max, delay, error), but the interactive TUI drops it (case 'turn.step.retrying': break;), so during LLM retries the user sees only the generic moon spinner for up to ~3 minutes and reads it as a frozen app. Print mode and the v2 activity view already surface this event; the shell TUI was the only consumer that didn't.What changed
streamingPhase: 'retrying'plusAppState.retryStatus(mirrors agent-core-v2'sTurnPhase 'retrying'design language).Rate limited (429) · attempt 3/10 · retrying in 12s, degrading toretrying now…once the backoff elapses. The countdown piggybacks on MoonLoader's existing 120 ms tick via a newsetLabelFn— no extra timer. The reason text derives from statusCode/errorName (429 / 408 / 5xx / connection / timeout).handleStepRetryingdiscards the failed attempt's partial live output before the next attempt re-streams. This needed a newStreamingUIController.discardStreamingBlock(): the existing reset methods only clear internal buffers, while a partial assistant block flushed mid-stream is already rendered in the transcript and must also be removed — otherwise every mid-stream retry leaves duplicated partial text above the fresh stream. (Print mode gets this for free because it buffers and writes at step boundaries.)setAppStateclearsretryStatuson any phase transition out of'retrying', with defensive clears on the three paths that historically set no phase (empty thinking delta, tool-call start, step completed).editor-keyboardtreats any non-idle phase as cancellable) — no changes there.Tests: pure formatter unit tests (reason mapping, countdown ceil /
retrying now…degradation), a MoonLoader fake-timer test proving the label re-evaluates on tick, and 7 end-to-end event→render cases in the existing message-flow harness (label content, attempt counter climbing across consecutive retry events, clearing on assistant/thinking/tool-call/step-completed, Esc interrupt chain, partial-draft discard).Checklist
gen-changesetsskill, or this PR needs no changeset. (changeset added: patch for@moonshot-ai/kimi-code)gen-docsskill, or this PR needs no doc update. (self-explanatory UI status; no config surface)