Skip to content

fix(agents): keep session/conversation selection alive across sidebar nav - #2319

Merged
2witstudios merged 4 commits into
masterfrom
pu/bug-clicking-new-convo-then-navigating-back-via-teh-sidebar-both-before-and-after
Aug 3, 2026
Merged

2witstudios merged 4 commits into
masterfrom
pu/bug-clicking-new-convo-then-navigating-back-via-teh-sidebar-both-before-and-after

Conversation

@2witstudios

@2witstudios 2witstudios commented Aug 3, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • Sidebar's "Agents" link was a static href with no query string, while the Agents surface's whole selection lives in the URL query (useAgentSurfaceStore). Navigating away and back via that link reset the selection to nothing, even though the in-memory store still had it — fixed by deriving the link's href from the live store selection, scoped to the correct drive.
  • The in-pane "+" (start a new conversation in the current session) swapped the pane's content but never told useAgentSurfaceStore about it, unlike useSpawnSession's equivalent update when spawning a new session. On a later remount, the seeding effect tried to reopen the stale old conversation against a pane that no longer showed it, tripping the eviction guard's split fallback instead of recognizing the swap as done — producing a phantom second pane. Fixed by calling selectConversation right after the swap, scoped to chatContext === 'console' (review finding: AgentPanes is also embedded in a regular page's chat tab via AgentPageView, chatContext="page", whose selection isn't sourced from this store — an unconditional call there would silently navigate that user to /dashboard/agents).
  • Same fix extended to handleSwitchAgent's "focus-existing" branch (switching an agent already open elsewhere into the current pane) — it has the identical shape (treats the pane's outgoing conversation as replaced, just like the mint path) and was equally vulnerable to the same staleness bug, just reachable via a different everyday action. Consolidated both call sites behind one syncConsoleSelection helper instead of duplicating the chatContext guard.

Test plan

  • bun run typecheck, eslint, and knip:check pass on all changed files
  • Stood up a local instance (isolated Postgres container, seeded admin session, production build) and drove it with Playwright:
    • Spawn a new session, navigate away (Dashboard) and back via the sidebar "Agents" link — selection now persists (previously reset)
    • Use the in-pane "+" to start a new conversation, navigate away and back — exactly one pane shows, with the new conversation (previously showed a phantom second pane with the old conversation)
    • Confirmed the "Agents" nav row still highlights correctly when browsing other sections (regression check on the active-link logic)
  • Verified AgentPanes's only other rendering context (AgentPageView, page-embedded chat) is unaffected by the console-selection sync (gated on chatContext)
  • Ran a proactive self-review pass (reuse / simplification / efficiency / altitude) on the diff; applied the simplifications and the handleSwitchAgent coverage extension above. Deliberately did not extend to handleClosePane's rebind-pane branch — that branch only ever repoints a pane that held no conversation of its own, so there's nothing stale to sync there, and forcing it would risk hijacking the console's selection based on an unrelated pane close elsewhere in the grid.

🤖 Generated with Claude Code

https://claude.ai/code/session_01PxRVM6D5xXQz6urBaeSdjs

@coderabbitai

coderabbitai Bot commented Aug 3, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@2witstudios, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 23 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 95c9f4ee-8755-44ec-bf0e-cf364acc64db

📥 Commits

Reviewing files that changed from the base of the PR and between 3dfed54 and c46e718.

📒 Files selected for processing (2)
  • apps/web/src/components/agents/panes/AgentPanes.tsx
  • apps/web/src/components/layout/left-sidebar/PrimaryNavigation.tsx
📝 Walkthrough

Walkthrough

Console-created conversations now update global agent selection. The sidebar preserves matching session, conversation, and agent parameters for the current drive while using drive-aware active-path matching.

Changes

Agent selection navigation

Layer / File(s) Summary
Console conversation selection sync
apps/web/src/components/agents/panes/AgentPanes.tsx
Console-context panes select newly minted conversations through useAgentSurfaceStore. Page-context panes do not update the global selection.
Drive-scoped sidebar links
apps/web/src/components/layout/left-sidebar/PrimaryNavigation.tsx
The Agents link includes selection parameters only when they belong to the current drive. Active-state matching uses the query-free drive path.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: preserving session and conversation selection during Agents sidebar navigation.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch pu/bug-clicking-new-convo-then-navigating-back-via-teh-sidebar-both-before-and-after

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.

@2witstudios
2witstudios force-pushed the pu/bug-clicking-new-convo-then-navigating-back-via-teh-sidebar-both-before-and-after branch from 6cf45f3 to 0647092 Compare August 3, 2026 16:33

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6cf45f33d5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

// longer shows it — reverting the swap or, once the eviction guard
// treats this pane as protected, splitting a second pane open for it
// instead of recognizing the swap as already done (caught in review).
useAgentSurfaceStore.getState().selectConversation({ sessionId, conversationId, agentId: agentPageId });

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Restrict selection updates to the Agents console

When AgentPanes is embedded in an AI_CHAT page through AgentPageView, clicking “Start a new conversation” also reaches this call. selectConversation always constructs an Agents-console URL and invokes history.pushState, which Next folds into router state, so successfully creating a conversation unexpectedly navigates the user away from the page to /dashboard/agents (or the store's possibly stale drive-scoped Agents route). The console selection should be updated only when this grid is hosted by AgentsSurface, rather than from this shared component unconditionally.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Confirmed and fixed in 3dfed54 — good catch. AgentPanes is indeed also embedded via AgentPageView with chatContext="page", whose initialConversation comes from its own local current state, not useAgentSurfaceStore. The unconditional call would have both done nothing useful there and silently pushed a /dashboard/agents URL, navigating a page-embedded chat's user away.

Gated the selectConversation(...) call on chatContext === 'console' (the one context — AgentsSurface's default — where this store is actually this component's source of truth for what should be showing). Left this thread open for verification rather than resolving it myself.

2witstudios and others added 4 commits August 3, 2026 12:12
… nav

Two bugs on the Agents console, both surfacing as "the thing I just
created isn't where I left it after navigating away and back via the
sidebar":

- The left sidebar's "Agents" link was a static href with no query
  string, while the surface's whole selection lives in the URL query
  (useAgentSurfaceStore). Navigating away and clicking "Agents" again
  wiped the selection even though the in-memory store still had it.
  Fixed by deriving the link's href from the live store selection,
  scoped to the correct drive.

- The in-pane "+" (start a new conversation in the current session)
  swapped the pane's content but never told useAgentSurfaceStore about
  it, unlike useSpawnSession which does the equivalent update when
  spawning a new session. On a later remount, the seeding effect tried
  to reopen the stale old conversation against a pane that no longer
  showed it, tripping the eviction guard's split fallback instead of
  recognizing the swap as already done — producing a phantom second
  pane. Fixed by calling selectConversation right after the swap.

Verified live: stood up a local instance (isolated Postgres, seeded
admin session, production build) and drove it with Playwright to
reproduce both bugs and confirm the fixes.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PxRVM6D5xXQz6urBaeSdjs
Self-review cleanup: every other useAgentWorkspaceStore action in
AgentPanes.tsx is obtained via the reactive useAgentWorkspaceStore((s) =>
s.xxx) selector hook, not an imperative .getState() call. Match that
convention for useAgentSurfaceStore's selectConversation too — functionally
identical (a single-action selector is a stable reference either way) but
consistent with the file's existing style.

Also normalize PrimaryNavigation.tsx's agentsBasePathForDrive to derive
from the already-computed agentsTargetDriveId rather than the raw driveId
prop directly — same behavior (agentsBasePath treats undefined/null
identically), just one fewer way to spell "this nav's drive scope."

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PxRVM6D5xXQz6urBaeSdjs
Review finding (chatgpt-codex-connector, P1): AgentPanes is also embedded
in a regular page's chat tab via AgentPageView (chatContext="page"), whose
initialConversation is driven by its own local `current` state, not
useAgentSurfaceStore. The unconditional selectConversation() call added in
0647092 would, for that embedding, both do nothing useful (its seeding
effect never reads this store) and push a /dashboard/agents URL via
history.pushState — silently navigating a page-embedded chat's user away
to the Agents console the instant they started a new conversation there.

Gate the call on chatContext === 'console' (AgentsSurface's default,
matching the one context where useAgentSurfaceStore's selection is
actually this component's source of truth for what should be showing).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PxRVM6D5xXQz6urBaeSdjs
Proactive self-review pass (/simplify: reuse, simplification, efficiency,
altitude angles run in parallel):

- AgentPanes.tsx: extracted the chatContext-gated selectConversation call
  into a single syncConsoleSelection(conversationId, agentPageId) helper
  instead of inlining the same guard at every call site that reassigns a
  pane's targetId to a different conversation. Applied it to the one
  additional call site with the identical bug: handleSwitchAgent's
  focus-existing branch already treats the pane's outgoing conversation as
  "replaced" (it calls closeReplacedConversation, same as the mint path)
  but was never telling the console's selection about the incoming one —
  same staleness risk (revert-or-split on next remount), reachable simply
  by switching an agent in an existing pane, not just minting a new
  conversation.

  Deliberately NOT extended to handleClosePane's rebind-pane branch: that
  branch only ever repoints a pane that held NO conversation of its own
  (terminal/picker/still-minting), so selectedConversationId was never
  referring to what's being displaced there. Force-syncing it would risk a
  DIFFERENT bug — silently reassigning the console's "selected conversation"
  to whatever gets rebound into an unrelated empty pane, based on a close
  the user made elsewhere in the grid.

- PrimaryNavigation.tsx: dropped the separate `matchPath` field (and the
  `agentsBasePathForDrive` value it was hand-computed from) in favor of
  deriving the active-match path from `item.href.split("?")[0]` at render
  time — the Agents entry's href is the only one that ever carries a query
  string, so this removes a whole "did these two stay in sync" axis rather
  than just documenting it. Also replaced the three-way
  `agentsMatchesDrive ? ... : null` field-by-field ternary with a single
  selection swap against the already-exported `EMPTY_AGENT_SELECTION`
  constant (lib/agents/agent-selection.ts), and split the drive-id
  comparison into its own primitive selector so the remaining shallow-compared
  object exactly matches `AgentSelection`'s shape.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PxRVM6D5xXQz6urBaeSdjs
@2witstudios
2witstudios force-pushed the pu/bug-clicking-new-convo-then-navigating-back-via-teh-sidebar-both-before-and-after branch from 0abe91e to c46e718 Compare August 3, 2026 17:13
@2witstudios

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@2witstudios

Copy link
Copy Markdown
Owner Author

@codex review

@coderabbitai

coderabbitai Bot commented Aug 3, 2026 •

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@2witstudios
2witstudios merged commit 6262d75 into master Aug 3, 2026
3 checks passed
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Nice work!

Reviewed commit: c46e718418

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@2witstudios
2witstudios deleted the pu/bug-clicking-new-convo-then-navigating-back-via-teh-sidebar-both-before-and-after branch August 3, 2026 18:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant