Skip to content

fix(app): keep root tab active for subagents - #43616

Open
opencode-agent[bot] wants to merge 1 commit into
v2from
subagent-tab-active
Open

fix(app): keep root tab active for subagents#43616
opencode-agent[bot] wants to merge 1 commit into
v2from
subagent-tab-active

Conversation

@opencode-agent

Copy link
Copy Markdown
Contributor

Summary

  • keep the root session tab active while viewing direct and nested subagents
  • resolve tab identity synchronously from the shared session cache after card navigation
  • fall back to full parent-lineage resolution for cold or incomplete session data
  • add a nested-subagent regression covering active state and duplicate-tab prevention

Version comparison

The reported build is 0.0.0-beta-17727 (2a7d0729d0). The next/latest successful beta is 0.0.0-beta-17728 (f929856cdf). The only commit between them updates Nix node_modules hashes; there are no changes under packages/app, packages/client, or packages/session-ui. The different reproduction result is therefore state/timing-dependent, not a behavior difference between these two beta builds.

Root cause

Titlebar tabs represent root sessions, but the routed page identifies the currently viewed leaf session.

packages/app/src/components/titlebar.tsx previously matched a routed session tab in two steps only:

  1. exact routed session ID
  2. the fetched session's immediate parentID

That has two failure modes:

  • Direct child navigation updates shared session data and then changes the route, but the titlebar starts a separate async fetch. Until that fetch resolves, currentTab() is undefined and the root tab renders inactive.
  • Nested subagents have leaf.parentID === intermediateChildID, while the stored tab is keyed by the root ID. The root tab never matches, and reconciliation can add the intermediate child as a second tab.

Call stack and data flow

Subagent tool card
  packages/session-ui/src/tools/tool-renderer.tsx
  input: tool metadata.sessionID
  output: child session href + navigateToSession(childID)

SessionUIProvider.navigateToSession
  packages/app/src/pages/directory-layout.tsx
  data.session.sync(childID) -> shared session store
  navigate(child href)       -> LayoutRoute { type: "session", sessionId: childID }

Titlebar
  packages/app/src/components/titlebar.tsx
  input: LayoutRoute + shared session store + API fallback
  transform: leaf ID -> complete parent lineage -> root ID
  output: root-keyed currentTab / addSessionTab

TitlebarTabStrip -> TitlebarTabNav
  packages/app/src/components/titlebar-tab-strip.tsx
  packages/app/src/components/titlebar-tab-nav.tsx
  input: currentTab object identity
  output: data-active="true" on the root tab

The complete-lineage primitive already exists in packages/app/src/utils/session-route.ts, and the loaded-data resolver exists at packages/client/src/solid/data.ts as data.session.root(sessionID).

Fix

packages/app/src/components/titlebar.tsx now:

  • reads the routed leaf and root synchronously from shared data when available
  • validates async resource results against the current route
  • walks the complete lineage with rootSession() when cached ancestry is incomplete
  • uses the resolved root ID for both route matching and missing-tab reconciliation

Validation

Passed:

  • bun typecheck (packages/app)
  • bun run typecheck:e2e (packages/app)
  • bun test --conditions=solid --preload ./happydom.ts ./src/utils/session-route.test.ts (4 passed)
  • git diff --check

The focused Playwright test was attempted locally, but this shared host could not initialize Rolldown's Rayon thread pool (ThreadPoolBuildError: Resource temporarily unavailable) before the page fixture started. CI should execute the added regression.

Requested by: @Hona (Hona via Slack)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant