fix(desktop): open chat tabs where chats are created, and fix tab order, popover and chat scroll
Summary
This PR bundles four fixes and the removal of useAutoAdoptChatSessions:
- Chat tabs come from whoever creates the chat (fixes #9260). The desktop no longer finds chats with no pane and adopts them.
- Cloud chats bind by an exact terminal id instead of a hash of the first prompt.
- Activity popover keeps its open state per workspace (fixes #9311).
- Chat keeps its scroll position when you leave and come back (fixes #9312).
Closes #9260, closes #9311, closes #9312.
Part 1: Chat tabs from the creator
Why
In Chat mode, a new workspace opened with "Workspace Setup" first and selected, and the agent's chat second. The create flow dropped chat launches. The chat only appeared later, when useAutoAdoptChatSessions found it as an orphan and added a background tab. That hook polled sessions and guessed which panes to open.
What / How
- Local create:
appendLaunchesToPaneLayoutwrites the chat (or terminal agent) tab first and selects it. The host returns an optionalchatAgentIdon agent results. For an older host, the desktop reads the agent fromsessions.getby the exact session id. If a create times out but the row exists, the launched agents are read back from the host's agent bindings. panes.newTabtakessessionIdandfocus. The host resolves the chat's agent and terminal, and sends them in a separatechatfield of the op. A desktop that predates it then rejects the op, instead of opening a terminal pane on the chat's id. A chat that already has a pane is reused. CLI:superset panes new-tab --session ID [--focus].- Other creators ask for a tab after they start a chat: CLI
sessions create/import/resume, relay automation dispatch, and the mobile new-session sheet. Each call is best-effort and opens the tab in the background. - Removed: orphan adoption,
latestOpenChatSessions, and the closed-terminal bookkeeping that only kept adoption from reopening chats.
Part 2: Cloud chats bind by terminal id
- The composer sends
agentTerminalIdwithcloudWorkspace.create. It travels asSUPERSET_SANDBOX_AGENT_TERMINAL_ID, and the sandbox host starts the chat (or the terminal it falls back to) under that id. The launch descriptor echoes it asterminalId. - The pending pane binds itself to the session or terminal with that exact id, and still releases to a plain chat at the 3-minute deadline. Background terminal adoption now treats the promised terminal as attached, so a terminal fallback does not open twice.
- The id is carried in
sandboxIdentitySchema(sandbox.conf) and insuperset-boot's host-service env list. CI's boot test caught both, because they would have dropped it silently. - Deleted
useAutoAdoptChatSessions: prompt-hash matching, first-prompt reads, backoff polling, and the closed-launch tracking.
Part 3: #9311 and #9312
- #9311: The floating popover's open state lives in
activityPanelStore, per workspace. Clicks on the workspace sidebar no longer close it. - #9312:
useCarriedReaderPositionsaves the reader position by session id in a layout cleanup, before React detaches the DOM. The existing restore path uses it. It is kept in memory and capped at 50 sessions.
Manual QA (dev desktop, driven over CDP with real input)
Each fix was reproduced on main first, then checked on this branch with the same journey.
Chat tabs
- New workspace with a prompt in Chat mode: the chat tab is first and selected, and Setup is second. (
main: Setup first and selected.) - CLI
sessions create: a background tab is added and the selected tab does not change. -
panes new-tab --session … --focuson the same session reuses its tab and selects it. - A session created with no tab request gets no tab, so adoption is gone.
Cloud binding (against the real local host; the pending pane was seeded as setup)
- A chat under a different terminal id does not bind (control case).
- A chat started under the promised id binds in about 1 s, in the same tab.
- A terminal started under the promised id becomes the pane. Before the background-adoption fix, this showed two tabs.
#9311 / #9312
- Narrow window: open the floating popover in A, switch to B from the sidebar, then back to A. It is open again. Outside click and Escape still close it.
- Chat scrolled to the middle (
scrollTop900): the same position after a tab switch, and after a workspace switch. (main: back at the bottom.)
Testing
- Typecheck: desktop, host-service, trpc, cli, and mobile (through turbo).
bun test: trpc, shared, and cli all pass. Desktop has 9222 passing and 2 failing in browser-extension bundle tests that this PR does not touch. host-service has 22 naming tests that fail only in a full-suite run (they pass alone), plus a health test that reads this cloud box's real boot stamps.- Biome is clean.
Known Limitations
- Not tested end to end: the sandbox boot launching under the promised id, and composer → API → pending pane against a real cloud sandbox. Both are covered by unit tests (
createChatSessionInWorkspacewith a terminal id, the env round trip, and the descriptor). The Sandbox workflow'sreal-sandboxrun on this branch is the way to check them. - A chat started by a client this PR does not update (for example another window's own flow) appears only in chat history now.
- The deadline release is covered only by unit tests.
Compatibility
- Every tRPC change is additive.
panes.newTab.terminalIdbecomes optional, and a call with neither or both ids isBAD_REQUEST, the same code a missingterminalIdgave. New optional fields:sessionId,focus,agentTerminalId,chatAgentId, and the descriptor'sterminalId.promptHashstays in the descriptor for released desktops. - New host + old desktop: a chat
newTabop gets a clean 400, and callers ignore it. - New desktop + old remote host:
chatAgentIdfalls back tosessions.get.
Deployment / Rollout
- The API deploys on merge (it adds
agentTerminalId). - Rebuild the sandbox image and publish the bundle before the desktop release (the bundle-touching merge runs
production-release). The id reaches the box throughsandbox.confand the boot runner (superset-boot), and a fresh box runs the runner from the image. On an old image or host, the pending pane waits until the deadline, and the launched chat is only reachable from history. - Desktop release.
Rollback
Revert the PR. No migrations. The API fields are optional, so released clients are unaffected either way.
Follow-ups
- Terminals have the same adoption pattern (
useAutoAdoptBackgroundSessions). - Add a router test for the host's
panes.newTabsessionIdbranch.
Summary by cubic
Fixes chat tabs so they come from whoever creates the chat, and fixes tab order, the activity popover, and chat scroll restoration.
Behavior changes
- Chat tabs are opened by the creator (desktop create flow, CLI sessions commands, mobile new-session sheet); the orphan-adoption hook is removed. Cloud chats bind by an exact terminal id instead of a hash of the first prompt, so the pending pane attaches to the launched chat or terminal without matching prompts. The floating activity popover keeps its open state per workspace and no longer closes when switching workspaces via the sidebar. Chat scroll position is saved on unmount and restored on remount, capped at 50 sessions.
Notes for review
panes.newTabnow acceptssessionIdandfocus; the host resolves the chat's terminal and agent, and a desktop that predates the op rejects it instead of opening a terminal pane. A focusednewTabin the sidebar shows the sidebar, andpanes new-tab --sidebar --sessionchecks for sidebar support like the terminal path. The pending cloud chat pane fetches the full session page so a busy workspace doesn't hide the promised session. Tab requests from the CLI, mobile, and automations, plus agent launch recovery, log unexpected failures while staying quiet when no desktop is attached or the host is too old. The chat agent lookup on workspace create retries before giving up. All tRPC changes are additive.SUPERSET_SANDBOX_AGENT_TERMINAL_IDnow reaches the box — bothsandbox.confandsuperset-bootpreviously dropped it — so publish a sandbox bundle with this host-service before the desktop release. Not tested end to end: the sandbox boot launch under the promised id and the composer-to-API-to-pane path, which are covered by unit tests.
Summary by CodeRabbit
- New Features
- Chat sessions created, imported, resumed, or launched by automation can open in a desktop workspace tab.
- The CLI can open chat sessions in tabs and optionally focus them.
- Workspace creation can recover agent launches after timeouts, and pending launches can resolve to a chat or terminal tab.
- Agent launches can appear as chat or terminal tabs in the workspace.
- Bug Fixes
- Activity popovers stay open when interacting with the workspace sidebar.
- Chat reader position is retained across pane unmounts.
- Opening an existing chat session reuses its current tab.