Skip to content

fix(frontend): Keep the agent session streaming when you navigate away - #5862

Open
ashrafchowdury wants to merge 5 commits into
mainfrom
fix/sessions-continuesion-after-page-swtich
Open

fix(frontend): Keep the agent session streaming when you navigate away#5862
ashrafchowdury wants to merge 5 commits into
mainfrom
fix/sessions-continuesion-after-page-swtich

Conversation

@ashrafchowdury

Copy link
Copy Markdown
Contributor

Context

Start an agent run in the playground, switch to another page while it is still producing, and come back: the answer had stopped streaming. The turn was still alive on the runner, but the browser was no longer following it. The tab fell back to the 15s durable-log catch-up, so the rest of the answer arrived in jumps instead of live.

The cause was ownership. useChat created its Chat inside the conversation component, so the SSE read lived and died with the mount, and the D9 teardown effect called stop() on every unmount. A route change is an unmount, so it looked exactly like closing the tab. Fixes #5724.

Changes

The Chat instance now lives in a small module-scoped registry keyed by session id (state/chatRegistry.ts), and the component borrows it instead of owning it.

Before: unmount always aborted the stream.

useEffect(() => () => stop(), [sessionId, stop])

After: unmount asks whether the session itself is gone. A route change leaves the tab open, so the chat stays and the stream keeps running; re-entering the route re-binds to the same instance mid-turn. Closing, deleting, or archiving the session removes it from the open-tab set first, so that path still stops the stream and drops the instance.

const stillOpen = store.get(openSessionIdsAtomFamily(scopeKey)).has(sessionId)
releaseSessionChat(sessionId, {stillOpen})

Because the chat now outlives the mount, its callbacks (prepareRequest, sendAutomaticallyWhen, onFinish) are rebound on every acquire. That is what keeps a long-lived chat from running stale closures, and it is why a run still follows a revision switch or a self-commit rather than sticking to the revision the session first mounted on.

One subtlety is load-bearing and worth knowing while reading the diff: the registry must never hand useChat a fresh instance under a session id it already rendered. useChat swaps its internal ref on identity change but keys its message subscription on the chat id, which does not change, so it would keep listening to the dropped instance and the transcript would freeze. Keeping the entry alive for as long as the tab is open is what guarantees that. The trade-off is one idle Chat per open tab until that tab is closed or the page reloads.

Tests

  • chatRegistry.test.ts covers the acquire/release policy in 6 cases: re-bind on remount, preserve a streaming and a submitted chat across a navigation, keep an idle chat while its tab is open, tear down when the session is no longer open, and forward a settled turn to the current mount's onFinish.
  • Full slice suite green (vitest run src/components/AgentChatSlice, 18 files, 132 tests). tsc --noEmit and eslint clean on @agenta/oss.
  • Worth a reviewer's eye: the teardown reads "is this session still open?" from the open-tab set at cleanup time. That is correct because every close, delete, archive, and reset writer in state/sessions.ts removes the id from openIdsByAppAtom before React runs the cleanup. If a new teardown path is ever added, it has to follow the same order.
  • Known gap, not fixed here: a session archived from another device unmounts its pane while its id is still in the open list, so its chat lingers until reload. Closing that means letting an archive stop a running turn, which is a product call rather than a bug fix.

What to QA

  • Start a run in the agent playground. While it is streaming, go to Observability, then come back. The same turn is still streaming into the same bubble, no reload needed, no gap in the text.
  • Do the same but wait on the other page until the run finishes. Coming back shows the completed turn, and it survives a reload.
  • Start a run, then close that session tab while it is streaming. The run stops, as before.
  • Open a session and send a message as your very first action. The reply streams in progressively and your own message stays on screen. This is the regression the registry change is most likely to have broken, and it only shows up in dev.
  • Regression: reopen a closed session from the history picker. Its transcript rehydrates from the record log as before.
  • Regression: switch revisions (or let the agent commit a new revision of itself) in a session with history, then send another message. The turn runs on the new revision.

…c to maintain chat instances across navigation
@dosubot dosubot Bot added the size:L This PR changes 100-499 lines, ignoring generated files. label Aug 10, 2026
@vercel

vercel Bot commented Aug 10, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
agenta-documentation Error Error Aug 10, 2026 9:58am

Request Review

@dosubot dosubot Bot added bug report Something isn't working frontend labels Aug 10, 2026
@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: b14ad40b-58d2-4179-ab97-79475aa81c45

📥 Commits

Reviewing files that changed from the base of the PR and between 72b6837 and 7ef6e08.

📒 Files selected for processing (6)
  • web/oss/src/components/AgentChatSlice/AgentConversation.tsx
  • web/oss/src/components/AgentChatSlice/hooks/useAgentChatSession.ts
  • web/oss/src/components/AgentChatSlice/state/chatRegistry.test.ts
  • web/oss/src/components/AgentChatSlice/state/chatRegistry.ts
  • web/oss/src/components/AgentChatSlice/state/sessions.teardown.test.ts
  • web/oss/src/components/AgentChatSlice/state/sessions.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • web/oss/src/components/AgentChatSlice/hooks/useAgentChatSession.ts
  • web/oss/src/components/AgentChatSlice/state/sessions.ts
  • web/oss/src/components/AgentChatSlice/state/chatRegistry.test.ts

📝 Walkthrough

Summary by CodeRabbit

  • Bug Fixes
    • Preserved active chat sessions when navigating away and returning, including in-progress responses.
    • Prevented unnecessary stream interruptions when a session remains open.
    • Improved cleanup of chats for closed, deleted, archived, or reset sessions.
    • Ensured completed responses continue to trigger the appropriate completion handling after remounts.
    • Improved chat status handling so sessions remain accurately marked while activity continues.

Walkthrough

The PR adds a session-scoped chat registry. useAgentChatSession reuses shared Chat instances across remounts and releases them based on session state. Session cleanup paths remove closed chats. Tests cover reuse, preservation, disposal, callback binding, and teardown.

Changes

Session chat persistence

Layer / File(s) Summary
Session chat registry lifecycle
web/oss/src/components/AgentChatSlice/state/chatRegistry.ts
Adds registry-owned Chat instances, hook rebinding, busy-state detection, error logging, and release behavior for closed sessions.
Session hook integration
web/oss/src/components/AgentChatSlice/hooks/useAgentChatSession.ts, web/oss/src/components/AgentChatSlice/AgentConversation.tsx
Uses the shared chat for request preparation, approval resumption, completion callbacks, busy-state checks, status handling, and unmount cleanup.
Session cleanup and registry validation
web/oss/src/components/AgentChatSlice/state/sessions.ts, web/oss/src/components/AgentChatSlice/state/chatRegistry.test.ts, web/oss/src/components/AgentChatSlice/state/sessions.teardown.test.ts
Drops chat state when sessions close, delete, archive, reconcile as archived, disappear remotely, or reset. Tests verify reuse, open-session preservation, callback binding, disposal, and teardown behavior.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant AgentChatSession
  participant SessionChatRegistry
  participant SessionState
  participant Chat
  AgentChatSession->>SessionChatRegistry: acquireSessionChat(sessionId, hooks)
  SessionChatRegistry->>Chat: create or reuse session chat
  Chat-->>AgentChatSession: provide shared chat instance
  AgentChatSession->>SessionChatRegistry: releaseSessionChat(sessionId, stillOpen)
  SessionState->>SessionChatRegistry: dropSessionChat(sessionId) when session closes
  SessionChatRegistry->>Chat: stop and remove closed chat
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main fix: preserving agent session streaming during navigation away from the playground.
Description check ✅ Passed The description explains the streaming failure, registry-based fix, lifecycle behavior, tests, and relevant QA scenarios.
Linked Issues check ✅ Passed The registry and teardown changes directly address issue #5724 by preserving active agent runs across navigation and stopping them only when sessions close.
Out of Scope Changes check ✅ Passed The implementation and tests remain focused on session chat persistence, lifecycle cleanup, callback rebinding, and streaming behavior.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/sessions-continuesion-after-page-swtich

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: dda9c5a8-2ea1-43be-ba15-ba583abd5e42

📥 Commits

Reviewing files that changed from the base of the PR and between adec2aa and cd5e882.

📒 Files selected for processing (3)
  • web/oss/src/components/AgentChatSlice/hooks/useAgentChatSession.ts
  • web/oss/src/components/AgentChatSlice/state/chatRegistry.test.ts
  • web/oss/src/components/AgentChatSlice/state/chatRegistry.ts

Comment thread web/oss/src/components/AgentChatSlice/hooks/useAgentChatSession.ts Outdated
Comment thread web/oss/src/components/AgentChatSlice/hooks/useAgentChatSession.ts
@github-actions

github-actions Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Railway Preview Environment

Preview URL https://gateway-pr-5862.up.railway.app/w
Project agenta-oss-clone-spike
Image tag pr-5862-26e9279
Status Deployed
Railway logs Open logs
Workflow logs View workflow run
Updated at 2026-08-10T10:08:22.829Z

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
web/oss/src/components/AgentChatSlice/state/chatRegistry.ts (1)

47-65: 🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

Do not publish a new registry entry during render.

useAgentChatSession calls acquireSessionChat during render (the supplied web/oss/src/components/AgentChatSlice/hooks/useAgentChatSession.ts snippet, Lines 65-224). Lines 49-65 create and register a Chat before any commit. If React abandons that first render, no effect cleanup runs. A later committed mount reuses the entry at Line 47, ignores its own initialMessages, and can retain the abandoned chat indefinitely.

Make registry ownership commit-aware. Keep a new entry provisional until a committed mount claims it, or discard uncommitted entries with a tokenized protocol. Add a regression test for an abandoned first acquisition with different initialMessages.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 23cb2404-9619-4daa-89e6-4d3d055919e5

📥 Commits

Reviewing files that changed from the base of the PR and between cd5e882 and 72b6837.

📒 Files selected for processing (4)
  • web/oss/src/components/AgentChatSlice/hooks/useAgentChatSession.ts
  • web/oss/src/components/AgentChatSlice/state/chatRegistry.test.ts
  • web/oss/src/components/AgentChatSlice/state/chatRegistry.ts
  • web/oss/src/components/AgentChatSlice/state/sessions.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • web/oss/src/components/AgentChatSlice/hooks/useAgentChatSession.ts

Comment thread web/oss/src/components/AgentChatSlice/state/chatRegistry.ts
Comment thread web/oss/src/components/AgentChatSlice/state/sessions.ts Outdated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug report Something isn't working frontend size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[bug] Agent run stops when leaving playground page

1 participant