fix(desktop): open chat tabs where chats are created, and fix tab order, popover and chat scroll

Merged
HA
1d ago
+1232-1269

Summary

This PR bundles four fixes and the removal of useAutoAdoptChatSessions:

  1. Chat tabs come from whoever creates the chat (fixes #9260). The desktop no longer finds chats with no pane and adopts them.
  2. Cloud chats bind by an exact terminal id instead of a hash of the first prompt.
  3. Activity popover keeps its open state per workspace (fixes #9311).
  4. 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: appendLaunchesToPaneLayout writes the chat (or terminal agent) tab first and selects it. The host returns an optional chatAgentId on agent results. For an older host, the desktop reads the agent from sessions.get by 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.newTab takes sessionId and focus. The host resolves the chat's agent and terminal, and sends them in a separate chat field 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 agentTerminalId with cloudWorkspace.create. It travels as SUPERSET_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 as terminalId.
  • 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 in superset-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: useCarriedReaderPosition saves 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 … --focus on 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 (scrollTop 900): 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 (createChatSessionInWorkspace with a terminal id, the env round trip, and the descriptor). The Sandbox workflow's real-sandbox run 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.terminalId becomes optional, and a call with neither or both ids is BAD_REQUEST, the same code a missing terminalId gave. New optional fields: sessionId, focus, agentTerminalId, chatAgentId, and the descriptor's terminalId. promptHash stays in the descriptor for released desktops.
  • New host + old desktop: a chat newTab op gets a clean 400, and callers ignore it.
  • New desktop + old remote host: chatAgentId falls back to sessions.get.

Deployment / Rollout

  1. The API deploys on merge (it adds agentTerminalId).
  2. Rebuild the sandbox image and publish the bundle before the desktop release (the bundle-touching merge runs production-release). The id reaches the box through sandbox.conf and 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.
  3. 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.newTab sessionId branch.

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.newTab now accepts sessionId and focus; 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 focused newTab in the sidebar shows the sidebar, and panes new-tab --sidebar --session checks 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_ID now reaches the box — both sandbox.conf and superset-boot previously 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.