Thanks to visit codestin.com
Credit goes to github.com

Skip to content

fix(ui): stop New Task from mirroring the previous session's live stream#1584

Merged
itomek merged 6 commits into
mainfrom
fix/1580-new-task-mirrors-stream
Jun 11, 2026
Merged

fix(ui): stop New Task from mirroring the previous session's live stream#1584
itomek merged 6 commits into
mainfrom
fix/1580-new-task-mirrors-stream

Conversation

@kovtcharov-amd

Copy link
Copy Markdown
Collaborator

Why this matters

Before: clicking New Task (or switching sessions) in the Agent UI while an agent was still streaming opened a window that mirrored the previous conversation and showed it as still running — the fresh session was not fresh. Reported by @sdevinenamd in #1580 while waiting on the email-triage agent. After: New Task always opens a clean window, and any in-flight stream is left to finish in its own (now background) session instead of leaking into the new one.

The session was always created correctly server-side; the bug was purely client-side shared state. The chat store keeps isStreaming, streamingContent, and agentSteps as single global values, and the old ChatView kept writing into them until its delayed (~220ms, the view-transition fade) unmount — so the incoming view read the previous stream's state.

The fix has two halves: reset the streaming globals at session-swap time so the incoming view starts clean, and guard every stream write path (onChunk / onAgentEvent / onDone / onError / flushStreamBuffer) by sessionId so a backgrounded stream can no longer mutate the shared store the active view reads from. This also covers switching to an existing session mid-stream, not just New Task.

Fixes #1580.

Test plan

  • npx tsc --noEmit in src/gaia/apps/webui — clean
  • vitest run — 42/42 pass, including new chatStore.test.ts covering resetStreaming
  • Manual: start a long agent run (e.g. email triage), click New Task mid-stream → new window is empty and not streaming; old session still holds its result when revisited
  • Manual: same as above but click a different existing session in the sidebar mid-stream → that session loads its own messages, no streaming carry-over

itomek and others added 3 commits June 10, 2026 15:35
#1570 migrated publish_agents.yml to OIDC trusted publishing and removed the
PYPI_API_TOKEN secret, but test_publish_workflow_exists_and_uses_pypi_action
still asserted "secrets.PYPI_API_TOKEN" in the workflow — so the test fails on
main and on every open PR current with it. Assert "id-token: write" (the OIDC
marker) instead, matching how the workflow actually authenticates now.
…finalized

The agent-wheel publish path (#1179) is armed: a v* release tag would fire the
publish job (behind a manual gate, against PyPI projects that don't exist yet)
before the Agent Hub distribution design is settled. Disable it rather than ship
a half-finished release path: remove the v* tag trigger and hard-skip the
approve/publish jobs (both commented for one-line re-enable). PR build +
twine-check still run so agent wheels stay well-formed.

The OIDC test fix in this PR stands as-is — the asserted workflow strings
(id-token: write, tag gate, skip-existing, matrix) are all still present.
Clicking New Task (or switching sessions) while an agent was still
streaming surfaced the previous conversation in the fresh window and
showed it as still running. The chat store keeps isStreaming,
streamingContent, and agentSteps as single global values, and the old
ChatView kept writing into them until its delayed (~220ms) unmount, so
the new session read the in-flight stream's state.

Reset the streaming globals at session-swap time and short-circuit the
stream write paths (chunk/agent-event/done/error/flush) once the view's
sessionId is no longer the current one, so a backgrounded stream can no
longer leak into the active view.
@github-actions github-actions Bot added devops DevOps/infrastructure changes tests Test changes labels Jun 10, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Review: fix(ui): stop New Task from mirroring the previous session's live stream

Summary

Solid, well-scoped fix for #1580 — the root cause (global isStreaming/streamingContent/agentSteps in the chat store, written by a backgrounded ChatView until its delayed unmount) is correctly diagnosed and addressed on both halves: reset the globals at swap time and guard every stream-write path by sessionId. The guard coverage is complete: all five paths the description names plus three nested handlers (ChatView.tsx:1009,1141,1165) check currentSessionId before writing, and the guard keys off the store's currentSessionId — which flips synchronously on switch — rather than the 220ms-lagged displayedSessionId, so stale writes are blocked from the instant the user navigates away, with resetStreaming() as the final cleanup. No blocking issues.

Issues

🟢 Repeated guard expression could be a named helper (ChatView.tsx:286,682,726,967,1041,1009,1141,1165)
useChatStore.getState().currentSessionId !== sessionId appears eight times. A tiny local reads better and documents intent at each call site — purely optional:

const isStale = () => useChatStore.getState().currentSessionId !== sessionId;

Then each guard becomes if (isStale()) return;. Skip if you prefer the explicit form.

🟢 Switch-away-then-back mid-stream is worth a manual pass
resetStreaming() wipes streamingContent on every swap, so returning to a still-streaming session before it completes relies on the per-instance streamBufferRef / server reload to restore partial content. Not a regression and likely fine, but it's an edge case the two unchecked manual-test items don't cover — worth a quick look when you run them.

Strengths

  • Guard keyed on the store's currentSessionId (not the lagged displayedSessionId) is the right call — the comment at App.tsx:500 makes the 220ms transition relationship clear.
  • resetStreaming lands as one atomic set() in the store (chatStore.ts:191) rather than three separate calls scattered across the view, and chatStore.test.ts covers both the populated→clean transition and the no-op-safe path.
  • CI pause is reversible-by-design and self-documenting: the false && guards plus the trigger comment spell out exactly what to restore (Agent Hub: Publish Python agent wheels to PyPI #1179), and the paired test_agent_pypi_publish.py update keeps the assertion in sync with the OIDC/id-token: write reality instead of the removed token secret.

Verdict

Approve. Clean root-cause fix, full guard coverage, tests updated alongside both the UI and CI changes. The two 🟢 notes are optional — the guard-helper is cosmetic and the switch-back case just deserves a glance during the manual test you've already queued.

Ovtcharov added 2 commits June 10, 2026 16:38
The currentSessionId !== sessionId check appeared at eight call sites
(stream chunk/agent-event/done/error/flush plus the message
refresh/delete/resend callbacks). Collapse them into one documented
useCallback so the intent reads at each guard and the 220ms
transition relationship is explained in a single place.

No behavior change — isStale() evaluates the same live store value.

@itomek itomek left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isStale() guard keys off the synchronously-flipping currentSessionId; resetStreaming placement is correct. LGTM.


Generated by Claude Code

@itomek itomek added this pull request to the merge queue Jun 11, 2026
Merged via the queue into main with commit 973effe Jun 11, 2026
26 checks passed
@itomek itomek deleted the fix/1580-new-task-mirrors-stream branch June 11, 2026 13:18
somo9909 pushed a commit to somo9909/gaia that referenced this pull request Jun 15, 2026
…icator (amd#1580) (amd#1656)

## Why this matters

Follow-up to amd#1584. Before: clicking **New Task** (or switching
sessions) while an agent was still generating *killed* the run — the SSE
was torn down on unmount, the backend cancelled the turn, and the
in-flight answer was discarded with no sign it had ever been running.
@sdevinenamd reported the visible half ("losing the status bar… hard to
tell if the original agent is still running") while waiting on the
email-triage agent. After: the run keeps going in the background,
finishes and persists on its own, the sidebar shows a spinner on any
session with a live run, and re-opening that session re-attaches to its
live stream so progress resumes.

A chat turn is now a first-class background run (`RunManager`) that
outlives the SSE connection: the producer + persistence run in a
detached task, and the SSE endpoint is a thin subscriber over a replay
buffer with multi-subscriber fan-out. `GET /api/chat/active` is the
backend-truth source for the sidebar indicator (survives
refresh/revisit); `GET /api/chat/attach` reconnects to an in-flight run.
Because a disconnect no longer cancels, **Stop** now calls the cancel
endpoint explicitly, and deleting a session cancels its run so it can't
persist to a dead session.

## Test plan

- [x] `pytest tests/unit/chat/ui/` — 720 passed (1 pre-existing Windows
cp1252 flake in an unrelated `agent.py`-reading test, deselected).
Includes new `test_run_manager.py` (run survives subscriber disconnect &
persists, late-subscriber replay, active list, overlap-409, cancel) and
`test_chat_active_attach.py` (active/attach endpoints,
send-409-while-running, delete-cancels-run).
- [x] `tsc --noEmit` — clean on all app code; `vite build` — clean.
- [x] **Live Agent UI** (real backend + Lemonade/Gemma-4-E4B): started a
long run → clicked **New Task** mid-stream → new session opened clean,
old session kept its "Agent running" spinner → revisited it → live
stream resumed → run completed in the background, answer persisted,
session auto-titled, spinner cleared.
- [ ] `vitest run` (added `chatStore` running-sessions cases) — runs in
CI; not executed locally (vitest absent from the local env).

Co-authored-by: Ovtcharov <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

devops DevOps/infrastructure changes tests Test changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: While an agent is running, creating a new task mirrors the previous session instead of creating a fresh chat window

2 participants