Skip to content

[Bug]: V2 keep-alive drops queued/waiting threads while UI still shows working #15289

Description

@ANSHSINGH050404

What happened

A thread that is queued or waiting loses its detail stream while the composer still shows connecting/running with Stop. The web keep-alive check only treats preparing, starting, and running as active, so queued and waiting threads are considered done and their subscription is closed early. Updates then arrive late or not at all until the next refresh.

Diagnosis

Grounded in source at upstream/main@cfdff56f7b:

  • apps/web/src/state/threads.ts:53-54 isRunning checks only preparing, starting, running.
  • apps/web/src/state/threads.ts:61-67 isDetailDone and :92-96 runningThreadIdsAtom both use isRunning, so queued/waiting threads are excluded from keep-alive.
  • packages/contracts/src/orchestrationV2.ts:446-456 RunStatus includes queued and waiting as live run states.
  • packages/client-runtime/src/state/models.ts:78-85 threadRunStatusIsActive includes preparing, queued, starting, running, waiting.
  • apps/web/src/session-logic.ts:1005-1013 derivePhase maps queued to connecting and waiting to running, so the UI shows a spinner and Stop while keep-alive has already closed the stream.

The narrower keep-alive predicate was introduced with the orchestrator V2 merge de34391.

Steps to reproduce

  1. Start a turn with queue mode so the follow-up run sits as queued, or reach a waiting run state.
  2. Observe the composer showing connecting/running with Stop available.
  3. The thread detail subscription is already treated as done, so live updates stop until a manual refresh or the next run transition.

Version

upstream/main@cfdff56f7b, post orchestrator V2 merge de34391; checked 2026-10-04.

Environment

Repo checkout on Windows x64, verified via git show upstream/main. Client-side defect affecting web and desktop.

Evidence

  • apps/web/src/state/threads.ts:53-54,61-67,92-96
  • packages/contracts/src/orchestrationV2.ts:446-456
  • packages/client-runtime/src/state/models.ts:78-85
  • apps/web/src/session-logic.ts:1005-1013

Verified via source reads only.

Related issues

Fix applied or workaround

None. Possible direction: include queued and waiting in the keep-alive active check to match threadRunStatusIsActive and derivePhase.

Activity

  1. juliusmarminge commented on Oct 3, 2026

    @juliusmarminge
    Member

    Note

    Grok responding on behalf of Julius.

    Triage

    Thanks @ANSHSINGH050404 for the detailed source walk-through. The predicate difference you found is real, but it's the desktop keep-alive's documented scope rather than the stream the composer reads.

    What keep-alive does

    • RunningThreadKeepAlive is mounted only in the Electron shell (apps/web/src/routes/__root.tsx); web and mobile don't keep background threads subscribed. Its job (docs/internals/connection-runtime.md) is to keep threads whose session is starting or running mounted, so opening one doesn't replay. isRunning in apps/web/src/state/threads.ts is that set: preparing, starting, running.
    • The open thread doesn't depend on it. ChatView mounts the detail atom itself through useThreadProjection, useThreadStatus, and useThreadVisibleTurnItems, so keep-alive releasing a thread doesn't close the stream the composer is reading. Phase and Stop come over the shell stream either way.
    • queued and waiting use different predicates. derivePhase maps queued to connecting and waiting to running, and Stop stays available while an earlier run is still interruptible. waiting is the short post-success state until checkpoint capture commits completed (see [BUG] V2 waiting run keeps showing Agent is working and offers Stop for finished work when checkpoint capture stalls #15124), not a live provider session. threadRunStatusIsActive is the sidebar/archive notion of "not settled", not the keep-alive set.

    A narrower case that could be a bug

    Keep-alive keys off the shell's latest-run status, not activityRunStatus. These differ when a later run is queued while an earlier one is still preparing / starting / running. A desktop that connects in that state won't pre-mount the thread, so its first open loads a snapshot. A thread that was already running stays held until that activity run leaves those statuses.

    What would help

    The report is based on reading isRunning and doesn't yet show the open composer missing events. If you've seen a desktop thread that was actively streaming, with a queued follow-up, stall until a manual refresh, please add:

    • The shell status and activityRunStatus while it was stalled
    • Whether that thread was the open route or only in the background
    • Whether the app had just connected, or the thread had already been running in that session

    A maintainer will decide on the fix direction.

  2. added
    bugSomething is broken or behaving incorrectly.
    needs more infoInitial triage showed no bug. Awaiting more info
    via-triageFiled through npx t3 triage
    on Oct 3, 2026
  3. ANSHSINGH050404 commented on Oct 3, 2026

    @ANSHSINGH050404
    ContributorAuthor

    Follow-up from the triage note, verified against upstream/main:

    • Keep-alive scope confirmed: RunningThreadKeepAlive mounts only when isElectron (apps/web/src/routes/__root.tsx), and the open thread mounts its own detail via useThreadProjection, useThreadStatus, and useThreadVisibleTurnItems in ChatView. So the open composer does not depend on keep-alive; this is about background pre-mount on desktop.
    • The narrower case reproduces in logic: keep-alive keys off the shell latest-run status (apps/web/src/state/threads.ts runningThreadIdsAtom), not activityRunStatus. When a follow-up is queued behind an active run, the shell reads queued and the thread is not pre-mounted on a fresh desktop connect, so the first open snapshot-loads.
    • Adjusted fix(web): keep queued threads subscribed #15293 accordingly: queued only, waiting left out since it is the short post-success state until checkpoint capture commits. No live-stall trace available here; the remaining claim is the pre-mount gap above, not the open composer missing events.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething is broken or behaving incorrectly.needs more infoInitial triage showed no bug. Awaiting more infovia-triageFiled through npx t3 triage

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions