Skip to content

fix(desktop): map subagent timeout/error completions to terminal states so spinners stop - #86592

Merged
teknium1 merged 7 commits into
mainfrom
salvage/desktop-subagent-terminal-status
Aug 15, 2026
Merged

teknium1 merged 7 commits into
mainfrom
salvage/desktop-subagent-terminal-status

Conversation

@teknium1

Copy link
Copy Markdown
Collaborator

Desktop no longer leaves timed-out or errored subagents spinning as "running" forever — backend terminal statuses (timeout, error, cancelled/canceled) now map to terminal renderer states, subagent.complete fails closed on anything unrecognized, and delegate result rows stop rendering timed-out children behind a green check.

Salvages the duplicate PR cluster #80045 / #85493 / #85995 (all fixing the same status-vocabulary mismatch) into one PR, with contributor authorship preserved via cherry-pick.

Changes

  • apps/desktop/src/store/subagents.ts
    • asStatus now normalizes backend statuses: timeout/errorfailed, cancelled/canceledinterrupted.
    • Fail-closed on completion: a subagent.complete event with an unrecognized — or still-active (running/queued) — payload status settles as failed instead of leaving a dead row spinning. Live events keep the lenient fallback.
    • Synthesizes a "Timed out after Ns" summary from duration_seconds when the backend sends no summary on a hard child timeout, and prefers it over stale progress text.
  • apps/desktop/src/components/assistant-ui/tool/delegate-model.ts (sibling site widened)
    • Settled delegate result rows treated any status other than failed as completed, so rows the delegate tool settles with timeout/error (the statuses tools/delegate_tool.py actually emits) rendered as successes. Now only ok/completed map to completed; everything else renders failed.
  • Tests: 6 new cases in subagents.test.ts (timeout/error/cancelled normalization, synthesized timeout reason, fail-closed vs lenient fallback, active-payload completions, late-event no-revive) and 1 in delegate-model.test.ts (ok/timeout/error/failure row statuses).

Validation

Check Result
vitest run src/store/subagents.test.ts src/components/assistant-ui/tool/delegate-model.test.ts ✅ 26/26 passed
eslint on all four touched files ✅ clean
tsc --noEmit (touched files) ✅ no new errors
scripts/audit_pr_attribution.py --fix ✅ all contributor emails mapped

Issue coverage

Credits

Unifies three community PRs fixing the same bug (authorship preserved via cherry-pick / attributed follow-up commits):

Infographic

Desktop subagent terminal-status fix infographic

RelaxJonh and others added 7 commits August 14, 2026 18:43
…73728)

The backend emits terminal statuses including 'timeout' and 'error' in
subagent.complete payloads, but asStatus() only recognised 'completed',
'failed', 'interrupted', and 'queued'. Unrecognised values fell through
to 'running', making timed-out subagents immortal in the active status
stack.

Fix: map timeout/error to 'failed', cancelled/canceled to 'interrupted'.
Nonterminal unknown statuses still default to 'running' for forward
compatibility.

Fixes #73728
… surface timeout reason

Follow-up to the #73728 normalization fix (supersedes the event-agnostic
fallback the maintainers flagged as incomplete):

- subagent.complete is terminal by definition — an unrecognized status on
  it now renders as 'failed' instead of falling through to 'running',
  which would recreate the immortal false-active row for any future
  backend status (the keep_open request on #73859).
- Live events keep the lenient 'running' fallback.
- Synthesize a 'Timed out after Xs' summary from duration_seconds when
  the backend completes with status 'timeout' and no summary, so the
  failed row explains itself.
- Tests: timeout reason synthesis + pruning, event-aware fail-closed vs
  lenient live fallback (13 total).
…text

Review feedback: prev?.summary could shadow the 'Timed out after Xs'
reason when a live event had populated it. timeoutSummary() now wins for
raw timeout status; add coverage for the missing-duration placeholder.
…ettle as failed

Folded-in coverage from PR #85995 (smause): a subagent.complete event whose
payload still says 'running' or 'queued' must settle the row as failed —
the completion event itself is the source of truth that the child is done.
…ent row

Folded-in coverage from PR #80045 (gannotti, #80018): after a terminal
subagent.complete, a stray late 'running' progress event must not restart
the spinner — the upsert guard keeps the settled failed status.
Two sibling sites still treated only 'completed'/'failed' as terminal:

- delegate-model.ts settled result rows as 'completed' for ANY status other
  than 'failed', so a delegate result row with status 'timeout' or 'error'
  (the statuses tools/delegate_tool.py actually emits on child timeout or
  crash) rendered behind a green check. Settled rows now map ok/completed
  to completed and everything else to failed.
- subagents.ts asStatus accepted a literal 'queued' payload status even on
  a subagent.complete event, leaving the row active forever. The fail-closed
  branch now runs before the queued fallback, so completion events always
  settle.
@github-actions

github-actions Bot commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

૮ >ﻌ< ა ci review

ran on e83e1cb — chore: map contributor email for attribution audit

⚠️ Warnings

CI timings · View report · View job

Wall time 6m34s vs 3m27s (+90.3%). 22 job(s) slower, 10 faster, 5 unchanged.

  • JS & TS checks / apps/desktop / check:test:ui:shard-3of3: +40.0s
  • JS & TS checks / apps/desktop / check:test:ui:shard-2of3: +37.0s
  • Python tests / Run tests slice 3/12: -28.0s
  • Python tests / Run tests slice 7/12: +12.0s
  • Python tests / Run tests slice 9/12: +11.0s

OSV vulnerability scan · View job

5 known vulnerabilities found in pinned dependencies.

How to fix:

Review the findings in the Security tab. Update the affected dependencies if a patched version is available.

@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/desktop Electron desktop app (apps/desktop/*) tool/delegate Subagent delegation area/sessions Session lifecycle, resume, persistence, history sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state labels Aug 15, 2026
@teknium1
teknium1 merged commit b948248 into main Aug 15, 2026
57 checks passed
@teknium1
teknium1 deleted the salvage/desktop-subagent-terminal-status branch August 15, 2026 03:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/sessions Session lifecycle, resume, persistence, history comp/desktop Electron desktop app (apps/desktop/*) P2 Medium — degraded but workaround exists sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state tool/delegate Subagent delegation type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Desktop: subagent stays 'running' forever when it times out — status vocabulary mismatch ('timeout'/'error' are not terminal in the renderer)

5 participants