Skip to content

fix(desktop): make error cards dismissable + softer tone for recoverable ones#1487

Merged
esengine merged 1 commit into
mainfrom
fix/desktop-error-dismiss
May 21, 2026
Merged

fix(desktop): make error cards dismissable + softer tone for recoverable ones#1487
esengine merged 1 commit into
mainfrom
fix/desktop-error-dismiss

Conversation

@esengine

Copy link
Copy Markdown
Owner

Bug

Desktop transcript fills up with red "错误" / "Error" cards that have no close button — every storm-repair / repeat-loop guard notice the loop emits gets rendered as a permanent red error card. A long session ends up with a wall of them at the bottom of the chat, even though the loop already worked around each issue.

User report (screenshot): three stacked cards along the lines of

  • "已抑制1次重复工具调用 — …repeat-loop guard tripped"
  • "拦截到重复工具调用 — 让模型察觉问题并换种方式重试。"
  • "已停止卡死的重试循环 — …repeat-loop guard tripped"

…all rendered identically with red tone and zero affordance to dismiss.

Root cause

`KernelErrorEvent` already carries `recoverable: boolean` — `true` for storm-repair / repeat-loop warnings, `false` for hard failures. The desktop reducer ignored the flag and pushed every event as the same red card with no close button. Storm-repair text reads as a confusing string of "ERRORS" when the loop already handled them.

Fix

  • Error chat messages now carry an `id` (issued by a small `nextErrorId` counter) and an optional `recoverable` flag plumbed from the kernel event.
  • New `dismiss_error` action + reducer case removes a single error by id without touching adjacent messages.
  • Each error card renders a close (×) button that dispatches `dismiss_error` for its own id.
  • `recoverable: true` cards render with warn tone (warn border/icon, `app.warningLabel`); `recoverable: false` keeps the existing red error tone (`app.errorLabel`). RPC-layer `$error` events stay red (treated as hard failure).
  • New i18n keys `app.warningLabel` / `app.dismissError` in en + zh-CN.

Tests

4 new reducer tests in `tests/desktop-btw-status.test.ts`:

  • `dismiss_error` removes the targeted error by id, leaves others
  • `dismiss_error` is a no-op when id misses
  • kernel `type: "error"` with `recoverable: true` produces a recoverable=true chat message with an id
  • protocol `type: "$error"` produces a recoverable=false chat message

3539 tests pass locally.

Test plan

  • CI green (Ubuntu + Windows)
  • Manual desktop: trigger a repeat-loop guard (e.g. ask the model to retry an identical `run_command` 3+ times). Card appears in warn tone with a × button. Clicking × removes it.
  • Manual desktop: simulate a hard RPC error. Card appears in error tone with a × button. Clicking × removes it.

User report: storm-repair / repeat-loop warnings pile up as red error
cards in the desktop transcript with no way to close them. A handful
of self-repaired loops in a long session leaves a wall of "错误" cards
permanently sitting in the chat, even though the loop already worked
around the underlying problem.

The kernel-side already classifies these correctly — KernelErrorEvent
carries `recoverable: true` for storm-repair / repeat-loop notices and
`false` for hard failures. The desktop just wasn't using either signal:

  - Every error event got the same red tone with no dismiss UI.

This commit:

  - Adds an id to each error chat message so dismiss can target one
    without taking out unrelated messages.
  - New `dismiss_error` action + reducer case removes by id.
  - Each error card now renders a close (×) button. Click dispatches
    `dismiss_error` for that id.
  - When the kernel event marks the error `recoverable: true`, the
    card renders with warn tone (warningLabel + tone-warn border/text)
    instead of error tone — softer signal for things the loop already
    handled.
  - Protocol `$error` events (RPC-layer hard failures) keep the red
    error tone.

i18n: added `app.warningLabel` and `app.dismissError` for en + zh-CN.

3539 tests pass (+4: dismiss_error by id; no-op for missing id;
recoverable=true from kernel error; recoverable=false from \$error).
@esengine esengine merged commit 8429754 into main May 21, 2026
4 checks passed
@esengine esengine deleted the fix/desktop-error-dismiss branch May 21, 2026 12:27
esengine pushed a commit that referenced this pull request May 21, 2026
- App.tsx $session_empty branch was missing `id` on the error card so
  TS rejected the literal against ChatMessage's required-id variant.
- App.test.ts stub state still used the pre-#1487 UsageStats shape
  (`tokens.{input,output,...}`); update to the current flat shape so
  the test compiles.

Caught by the desktop-v* release workflow — root verify doesn't run
desktop tsc, so these regressed silently after #1487.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant