Skip to content

fix(desktop): map subagent timeout/error completion to terminal failed state - #85493

Closed
DavidMetcalfe wants to merge 3 commits into
NousResearch:mainfrom
DavidMetcalfe:fix/subagent-timeout-status-stuck
Closed

fix(desktop): map subagent timeout/error completion to terminal failed state#85493
DavidMetcalfe wants to merge 3 commits into
NousResearch:mainfrom
DavidMetcalfe:fix/subagent-timeout-status-stuck

Conversation

@DavidMetcalfe

@DavidMetcalfe DavidMetcalfe commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Summary

Timed-out (or exception-failed) subagents stay stuck as "running" in the desktop Agents panel and status-bar count forever — spinner glyph, counted in "Agents N running", never pruned — even though the parent conversation already received the failure. Fixes the immortal false-active rows reported in #73728 (also addresses #85492, filed as a duplicate; same root cause as #80018 and #81602, which this fix resolves too).

Root cause

Status-vocabulary drift between the Python backend and the desktop store. The backend completes subagents with statuses the UI never learned:

The desktop store's asStatus() (apps/desktop/src/store/subagents.ts) was an allowlist of completed | failed | interrupted | queued; anything else fell through to 'running'. A stuck running row is never cleaned up because pruneFinishedSessionSubagents only prunes terminal-status rows at the message.start boundary, and activeSubagentCount keeps counting it in the status bar.

Changes

  • apps/desktop/src/store/subagents.ts
    • Map backend "timeout" / "error" completion statuses to the existing terminal failed status; "cancelled" / "canceled" map to interrupted (compatibility aliases).
    • Fail closed on completion: subagent.complete is terminal by definition — an unrecognized status on it now renders failed instead of falling through to running, so a future backend status can't recreate the immortal false-active row. Live events keep the lenient running fallback. (This is the event-aware handling requested in review on fix(desktop): normalise timeout/error subagent statuses to terminal (#73728) #73859.)
    • Synthesize a Timed out after Xs summary (from the backend's duration_seconds) when the raw status is timeout and no summary arrived, so the row explains why it failed instead of rendering as a bare failure.
  • apps/desktop/src/store/subagents.test.ts — normalization regression test (timeout/error → failed, cancelled/canceled → interrupted, all prunable), timeout reason synthesis, missing-duration placeholder, event-aware fail-closed vs. lenient live fallback. 14 tests.

Relationship to #73859

This PR cherry-picks the commit from #73859 (by @RelaxJonh — same normalization mapping, authorship preserved in history) and adds the event-aware fail-closed handling that maintainer review requested on #73859 (@teknium1 keep_open, @GottZ triage 08-02/08-03). That PR has had no author response since 07-30; this PR supersedes it so the requested change doesn't stall the fix.

Testing

  • npx vitest run src/store/subagents.test.ts14 passed (10 pre-existing + 4 new)
  • npx eslint src/store/subagents.ts src/store/subagents.test.ts — clean

Notes

Open question: a distinct timeout status with its own glyph/label (instead of mapping to failed) would be richer UI, but is a much larger surface change — type union, glyph mapping, i18n strings, status-bar counts — for the same user-visible outcome. This PR keeps the narrow mapping; the synthesized summary line preserves the reason.

Closes #73728

@alt-glitch alt-glitch added type/bug Something isn't working comp/desktop Electron desktop app (apps/desktop/*) tool/delegate Subagent delegation P2 Medium — degraded but workaround exists duplicate This issue or pull request already exists sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state labels Aug 13, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

This was generated by AI during triage.

Duplicate of #73859: both map backend timeout/error completion statuses to a terminal Desktop state; #73859 is the earlier broader implementation with cancelled/canceled coverage.

RelaxJonh and others added 3 commits August 13, 2026 11:15
…ousResearch#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 NousResearch#73728
… surface timeout reason

Follow-up to the NousResearch#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 NousResearch#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.
@DavidMetcalfe

DavidMetcalfe commented Aug 15, 2026

Copy link
Copy Markdown
Contributor Author

Superseded by #86592@teknium1's salvage built directly on this branch: @RelaxJonh's normalization commit plus the event-aware fail-closed and timeout-summary commits are the first three commits there. Closing to keep the queue unambiguous; coverage and credits are tracked in #86592.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/desktop Electron desktop app (apps/desktop/*) duplicate This issue or pull request already exists 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 leaves timed-out subagents stuck as running

3 participants