Skip to content

fix(dashboard): open remote sessions from every agent reveal path - #18403

Merged
nwparker merged 1 commit into
mainfrom
fix/16731-dashboard-remote-reveal
Sep 3, 2026
Merged

fix(dashboard): open remote sessions from every agent reveal path#18403
nwparker merged 1 commit into
mainfrom
fix/16731-dashboard-remote-reveal

Conversation

@nwparker

@nwparker nwparker commented Sep 3, 2026

Copy link
Copy Markdown
Contributor
Files Added Deleted Net
Test 6 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​228 $\color{#cf222e}{\Huge{\mathbf{−}}}$​50 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​178
Prod 8 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​75 $\color{#cf222e}{\Huge{\mathbf{−}}}$​37 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​38

Summary

Fixes #16731. Clicking an agent could not open a remote (SSH) session: the reveal landed on a workspace with no terminal, the Activity row-click was a silent no-op, and the preview claimed the pane had closed.

Three reveal paths called bare setActiveWorktree + activateTabAndFocusPane, skipping setActiveView('terminal'), ensureWorktreeHasInitialTerminal and resumeSleepingAgentSessionsForWorktree:

surface site
pop-out bridge useDashboardPopoutBridge.ts
in-window drawer AgentDashboardDrawer.tsx
Activity row-click activity-thread-actions.ts (added by #18222)

All three now route through the incumbent activateAndRevealWorkspace — the same dispatcher the sidebar (WorktreeCardAgents, active-worktree-focus-after-delete, use-worktree-context-menu-commands) and the row's own "Jump to workspace" affordance already use. No new activation helper; the two dashboard surfaces share one small revealDashboardAgent wrapper. The workspace dispatcher (not activateAndRevealWorktree directly) so a folder-workspace card keeps its path-status gate.

The Activity early-return is removed. It probed tabsByWorktree/unifiedTabsByWorktree before activating, so a cold-parked remote thread — the normal state of an SSH session that was never revived — produced a silent no-op click. Residency is now probed after activation, matching WorktreeCardAgents.handleActivateAgentTab: a revived tab gets focused, and a genuinely retained thread whose tab never returns still activates its workspace instead of doing nothing.

Message honesty. SshPtyProvider.canProvideAuthoritativeBufferSnapshot returns false and the relay's RecentPtyOutputBuffer has no snapshot RPC, so serializeTerminalBufferFromAvailableState has nothing to fall through to for SSH. A null snapshot for a remote pty is loss of contact, and per docs/reference/ssh-execution-boundary.md that is unverifiable, never exited. The preview and the no-pty dialog branch now render a host-aware string instead of asserting the pane closed. Adding the relay snapshot RPC is out of scope — a new stream opcode needs capability negotiation (docs/reference/remote-wire-compatibility.md).

Renderer-only. Nothing crosses the remote wire: hostKind is already on DashboardCard, and no snapshot field was added.

Not fixed here (deliberate)

activate-tab-and-focus-pane.ts dispatches FOCUS_TERMINAL_PANE_EVENT inside a single requestAnimationFrame, and its only consumer is a bare window.addEventListener, so a pane mounting after the dispatch never sees it. All four surfaces share this. It is a real gap, but it is a focus flash, not the reported symptom — with activation routed correctly the workspace opens and the terminal is there. Building a TTL'd pending-focus registry for it speculatively is a larger change than the bug being fixed, so it is left alone and noted.

Testing

  • pnpm tc — clean
  • pnpm test src (whole tree) — 69,614 passed. The only 2 failures are in src/main/native-chat/agent-session-wire/structured-tui-transcript-catchup.test.ts, which fails identically on a clean tree (verified by stashing) and is unrelated main-process transcript-watcher work.
  • pnpm run check:code-quality:changed — 0 new findings (code quality, type-aware, React Doctor)
  • oxlint over the three touched directories — clean, with max-lines proven to fire there via a 320-line positive control
  • Docker SSH e2e lane

Failed-first: 12 of the 66 assertions in the touched suites fail against the pre-fix source. The important one is activity-thread-actions.test.ts, which previously pinned the bug: it asserted the bare setActiveWorktree(thread.worktree.id, REMOTE_HOST) call for a remote host. That assertion is now inverted (setActiveWorktree must not be called; the dispatcher must be), and a cold-parked remote thread whose tab is not resident until activation revives it is covered directly — the reported symptom and the early-return case.

Notes

Supersedes #16732, which attempted this before the defect spread to Activity. That PR routes through activateAndRevealWorktree (missing the folder-workspace dispatcher), does not touch the Activity surface or its early-return, does not update the test that pins the bug, and copies the new English string into es/ja/ko/zh. Its one piece of unique work is the pending-focus registry described above. Recommend closing it in favour of this PR and tracking the focus registry separately if it proves load-bearing.

Three reveal paths called bare setActiveWorktree + activateTabAndFocusPane,
skipping setActiveView('terminal'), ensureWorktreeHasInitialTerminal and
resumeSleepingAgentSessionsForWorktree. A parked SSH workspace has no resident
tab until those run, so the reveal landed on a workspace with no terminal.

Route all three through the incumbent activateAndRevealWorkspace dispatcher
(which the sidebar and "Jump to workspace" already use, and which also handles
folder workspaces). The Activity row-click additionally early-returned when the
thread's tab was absent from tabsByWorktree/unifiedTabsByWorktree, which made a
cold-parked remote thread a silent no-op; residency is now probed after
activation, so a revived tab is focused and a genuinely retained thread still
activates its workspace instead of doing nothing.

Also stop asserting `exited` from an absence of local state: SshPtyProvider
reports no authoritative buffer snapshot and the relay has no snapshot RPC, so
a null preview snapshot for a remote pty is loss of contact. The preview and
the no-pty dialog branch now say the remote preview is unavailable rather than
claiming the pane closed. Adding the relay snapshot RPC stays out of scope --
it needs capability negotiation.

Fixes #16731
@coderabbitai

coderabbitai Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Workspace and dashboard agent reveal flows now activate the target workspace before focusing its tab or pane, including execution-host targeting and parked SSH workspace revival. Thread activation checks the post-activation tab state before focusing. Terminal preview fallbacks now distinguish unavailable remote SSH sessions from closed local panes through a shared message helper and new localization coverage. Tests cover activation ordering, missing workspaces, missing tabs, and terminal preview states.

Merge Risk: 🔵 Low · up to 75af6

Some remote terminal previews can still incorrectly say that a pane has closed when the SSH session is merely unavailable. This is a bounded user-facing status error and should be corrected by passing the remote host classification through the preview.

🚥 Pre-merge checks | ✅ 2 | ❌ 3

❌ Failed checks (3 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description explains the bug, implementation, scope, linked issue, and testing results. However, it omits the required Visual Proof section and checklist, and it does not provide the required befo… Add the required Visual Proof section with before/after screenshots or videos for the interaction change. Complete the template sections and checklist, including the manual testing and validation items.
Linked Issues check ⚠️ Warning The changes address the remote preview message and route all three reveal paths through workspace activation. However, linked issue #16731 also specifies a pending-focus registry for late-mounting rem… Implement the pending-focus registry described in issue #16731, or update the issue and PR scope so the remaining focus-race defect is tracked separately with explicit acceptance criteria.
Docstring Coverage ⚠️ Warning Docstring coverage is 55.56% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 9 functions across 13 files. (1 skipped: … Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the primary change: enabling remote session opening from all agent reveal paths.
Out of Scope Changes check ✅ Passed The production changes and tests remain focused on remote dashboard activation, Activity reveal behavior, and honest unavailable-preview messaging. No unrelated feature or remote-wire changes are incl…
Full details: Description check

Explanation

The description explains the bug, implementation, scope, linked issue, and testing results. However, it omits the required Visual Proof section and checklist, and it does not provide the required before/after attachments or an applicable N/A explanation.

Full details: Linked Issues check

Explanation

The changes address the remote preview message and route all three reveal paths through workspace activation. However, linked issue #16731 also specifies a pending-focus registry for late-mounting remote panes, which this PR explicitly does not implement. The focus race therefore remains unresolved.

Full details: Out of Scope Changes check

Explanation

The production changes and tests remain focused on remote dashboard activation, Activity reveal behavior, and honest unavailable-preview messaging. No unrelated feature or remote-wire changes are included.

Full details: Docstring Coverage

Explanation

Docstring coverage is 55.56% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 9 functions across 13 files. (1 skipped: 1 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch

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
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Team

Run ID: d4d01d3b-cba9-419e-8245-e38ff0fd655a

📥 Commits

Reviewing files that changed from the base of the PR and between f70f580 and 75af65f.

📒 Files selected for processing (14)
  • src/renderer/src/components/activity/activity-thread-actions.test.ts
  • src/renderer/src/components/activity/activity-thread-actions.ts
  • src/renderer/src/components/dashboard-popout/AgentTerminalDialog.test.tsx
  • src/renderer/src/components/dashboard-popout/AgentTerminalDialog.tsx
  • src/renderer/src/components/dashboard-popout/AgentTerminalPreview.test.tsx
  • src/renderer/src/components/dashboard-popout/AgentTerminalPreview.tsx
  • src/renderer/src/components/dashboard-popout/terminal-preview-unavailable-message.test.ts
  • src/renderer/src/components/dashboard-popout/terminal-preview-unavailable-message.ts
  • src/renderer/src/components/dashboard/AgentDashboardDrawer.test.tsx
  • src/renderer/src/components/dashboard/AgentDashboardDrawer.tsx
  • src/renderer/src/components/dashboard/reveal-dashboard-agent.ts
  • src/renderer/src/components/dashboard/useDashboardPopoutBridge.test.tsx
  • src/renderer/src/components/dashboard/useDashboardPopoutBridge.ts
  • src/renderer/src/i18n/locales/en.json

Included review availability: Your plan provides up to 10 included reviews per hour; 1 remains after this review.

'dashboardPopout.terminal.closed',
"No live terminal — this agent's pane has closed."
)}
{terminalPreviewUnavailableMessage({ ptyId })}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🔎 Supported by static analysis

🤖 get_repo_knowledge executed:

get_repo_knowledge stablyai/orca /tmp/coderabbit-repo-knowledge/stablyai-orca-89dc44e4/learnings

Length of output: 13825


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- candidate symbols ---'
rg -n -C 5 "terminalPreviewUnavailableMessage|parseAppSshPtyId|AgentTerminalPreview|AgentTerminalDialog|hostKind" src/renderer/src/components/dashboard-popout src/renderer/src 2>/dev/null | head -n 260
printf '%s\n' '--- file outline ---'
ast-grep outline src/renderer/src/components/dashboard-popout/AgentTerminalPreview.tsx --view compact
printf '%s\n' '--- related tests ---'
rg -n -C 4 "terminalPreviewUnavailableMessage|parseAppSshPtyId|AgentTerminalPreview|hostKind" --glob '*test*' --glob '*spec*' src 2>/dev/null | head -n 220

Repository: stablyai/orca

Length of output: 25236


🏁 Script executed:

#!/bin/bash
set -e
preview='src/renderer/src/components/dashboard-popout/AgentTerminalPreview.tsx'
dialog='src/renderer/src/components/dashboard-popout/AgentTerminalDialog.tsx'
printf '%s\n' '--- preview ---'
cat -n "$preview" | sed -n '1,120p;390,455p'
printf '%s\n' '--- dialog references ---'
rg -n -C 8 "AgentTerminalPreview|terminalPreviewUnavailableMessage|hostKind|parseAppSshPtyId" "$dialog"
printf '%s\n' '--- definitions and tests ---'
files=$(rg -l "terminalPreviewUnavailableMessage|parseAppSshPtyId" src)
printf '%s\n' "$files"
for file in $files; do
  printf '\n--- %s ---\n' "$file"
  rg -n -C 8 "terminalPreviewUnavailableMessage|parseAppSshPtyId" "$file"
done

Repository: stablyai/orca

Length of output: 50370


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- dialog ---'
cat -n src/renderer/src/components/dashboard-popout/AgentTerminalDialog.tsx | sed -n '1,105p'
printf '%s\n' '--- unavailable-message implementation ---'
cat -n src/renderer/src/components/dashboard-popout/terminal-preview-unavailable-message.ts
printf '%s\n' '--- unavailable-message tests ---'
cat -n src/renderer/src/components/dashboard-popout/terminal-preview-unavailable-message.test.ts
printf '%s\n' '--- PTY creation and snapshot path in preview ---'
rg -n -C 6 "ptyGone|snapshot|getMainBufferSnapshot|terminalPreview" src/renderer/src/components/dashboard-popout/AgentTerminalPreview.tsx

Repository: stablyai/orca

Length of output: 16519


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- DashboardCard contract ---'
rg -n -C 10 "export type DashboardCard|interface DashboardCard|ptyId:|hostKind:" src/shared/dashboard-snapshot.ts
printf '%s\n' '--- dashboard card construction ---'
rg -n -C 8 "DashboardCard|hostKind|ptyId" src/renderer/src/components/dashboard src/renderer/src/components/dashboard-popout src/shared/dashboard-snapshot.ts | head -n 260
printf '%s\n' '--- SSH PTY ID contract ---'
cat -n src/shared/ssh-pty-id.ts | sed -n '1,75p'

Repository: stablyai/orca

Length of output: 30088


Pass hostKind to AgentTerminalPreview.

When card.hostKind is 'ssh' and parseAppSshPtyId(card.ptyId) returns null, terminalPreviewUnavailableMessage renders the local dashboardPopout.terminal.closed message. Thread hostKind through AgentTerminalPreview and add a regression test.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

✅ No new issues found.

Reviewed changes

  • Activity row-clickactivateThreadTarget now routes through activateAndRevealWorkspace and probes tab residency after activation instead of before, so a cold-parked remote thread revives and focuses instead of silently no-op'ing. A gone workspace still returns early via the === false contract.
  • Dashboard reveal surfaces — the pop-out bridge and in-window drawer share one revealDashboardAgent wrapper that goes through the workspace dispatcher (not activateAndRevealWorktree directly), preserving the folder-workspace path-status gate.
  • Message honesty — new terminalPreviewUnavailableMessage distinguishes SSH (hostKind === 'ssh' or an SSH ptyId) from local, backed by SshPtyProvider.canProvideAuthoritativeBufferSnapshot === false.
  • Tests — the bug-pinning assertion is inverted (setActiveWorktree must not be called), a cold-parked revive is covered directly, and the gone-workspace no-op is pinned.

The change is renderer-only and adds no wire fields, matching the remote-wire compatibility constraint. Routing through the incumbent dispatcher is the right call: it reuses setActiveView('terminal'), resumeSleepingAgentSessionsForWorktree, and ensureWorktreeHasInitialTerminal, which is exactly what the bare setActiveWorktree calls were skipping. ensureWorktreeHasInitialTerminal only reseeds when renderableTabCount === 0, so structured-agent-session and live-tab workspaces aren't given spurious shells.

ℹ️ hostKind remote detection is SSH-specific

terminalPreviewUnavailableMessage keys "unobservable" off hostKind === 'ssh' / parseAppSshPtyId, while DashboardCardHostKind also enumerates remote (remote-runtime relay) and wsl. WSL is fine because the daemon snapshot is authoritative; whether the remote-runtime relay also lacks an authoritative preview snapshot is out of this PR's SSH scope and unchanged by it. Nothing to do here — just flagging the boundary so it isn't mistaken for a complete "remote host" fix.

Pullfrog  | View workflow run | Using DeepSeek Pro (free via Pullfrog for OSS) | 𝕏

@nwparker
nwparker merged commit 232d04f into main Sep 3, 2026
29 checks passed
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.

[Bug]: Agent dashboard cannot open remote (SSH) sessions — shows "terminal closed"

1 participant