Skip to content

fix(preview): Browser pane navigates when the desktop backend has no browser channel - #35424

Merged
juliusmarminge merged 2 commits into
mainfrom
factory/issue-17009-a28601
Nov 12, 2026
Merged

juliusmarminge merged 2 commits into
mainfrom
factory/issue-17009-a28601

Conversation

@juliusmarminge

@juliusmarminge juliusmarminge commented Nov 12, 2026 •

Copy link
Copy Markdown
Member

Since #22877, ServerBrowser.setUpTab marked a tab headless only when the desktop browser channel was available (!desktop && desktopChannel.available). In WSL-only mode the desktop starts its only backend without the browser fds, so the channel is unavailable and primary-environment server tabs stayed non-headless. rendersServerTabNatively then had Electron draw its own local page, which typed URLs never reached, while the server ran a separate headless page for agents. The fix drops the desktopChannel.available condition, so every tab the desktop does not draw (no channel, attach timeout, or adopted popup) is marked headless and the pane streams the server's page. When the channel is present and the desktop attaches, nothing changes. On servers with no desktop at all (plain t3, remote), tabs now carry headless: true. Nothing reads the flag outside the native-render check, which already needs an Electron primary environment, so there is no visible change there. I chose this over PR #31172, which reads the fd config in PreviewManager.open, because it keeps the decision in the one place that already makes it. The repro test 'a server started without the desktop browser channel marks its tabs headless' fails on the base ('expected undefined to be true') and passes with the fix. No new regex or parser. I did not re-run the desktop app on this branch; the repro step already reproduced the bug end to end there.

Fixes #17009

Reproduction on main

Commit origin/main f03db4f. WSL itself is not available on Linux, so I simulated WSL-only mode exactly at its root cause: I commented out desktopBrowserFd/desktopBrowserControlFd in the Linux primary backend config (apps/desktop/src/backend/DesktopBackendConfiguration.ts L518-519, local only, not committed) and ran the real desktop app (T3CODE_DEV_SEED=0 headless-desktop vp run dev:desktop), driving it over CDP. Steps: created a project, opened a thread, Open a surface -> Browser, typed example.com and pressed Enter. The address field cleared back to 'Search or enter URL', example.com appeared under 'Recently used', and no page loaded; clicking the listed local server localhost:7568 also did nothing. Control: I restored the two lines (Electron and the backend restarted with the fds) and ran the same steps: the tab loaded 'Example Domain' at https://example.com/. Failing test: apps/server/src/preview/ServerBrowser.test.ts, 'a server started without the desktop browser channel marks its tabs headless', runs ServerBrowser with the real DesktopBrowserChannel layer and no fds; on main the opened tab has headless undefined instead of true.

Evidence

Real Linux Electron desktop under Xvfb, with both desktop browser channel fds omitted to simulate the WSL-only backend condition. Fresh synthetic data, light theme, identical 484×780 Browser-pane crops. Steps: open Browser from a new-thread view, type example.com, press Enter; then click the listed localhost:7568 server from a new-tab pane.

Before After
f03db4fbef b51b3613d2
Before: URL entry leaves the new-tab list After: example.com renders
Before URL recording After URL recording
Before: local-server click leaves the new-tab list After: local-server page renders
Before local-server recording After local-server recording

Capture driver · Seed script · Synthetic local page · Observations

AI-assisted; verified with the tests and tools listed above.

Not shown:

  • Permanent blocker: native Windows with a WSL-only backend is unavailable on this Linux VM; evidence uses the explicitly supplied no-browser-channel simulation.
  • Direct agent preview-tool operation and synchronization with a saved thread were not exercised; captures demonstrate desktop Browser navigation from the new-thread draft view. A direct MCP request using the desktop client credential was rejected with HTTP 401.

Tests

cd apps/server && vp test run src/preview/ServerBrowser.test.ts src/preview/Manager.test.ts: 79/79 passed. With the fix stashed, the repro test fails with 'expected undefined to be true'. vp run check:changed: format, lint, knip, typecheck (3 packages) and tests (2 files) all passed.

Not in this change

  • Open PR fix(preview): typed URLs navigate browser tabs when WSL is the primary environment #31172 fixes the same bug a different way, by marking tabs headless in PreviewManager.open from the fd config; it could be closed in favour of this fix.
  • When the desktop browser fds are missing, the WSL-only desktop backend still starts without a browser channel at all, so it streams the server's page instead of rendering natively. Giving WSL backends a real channel is a separate feature.

AI-assisted; verified with the tests and tools listed above.

juliusmarminge and others added 2 commits November 12, 2026 17:24
…tabs headless

Fails on main: in WSL-only mode the desktop starts its backend without
browser fds, the server never marks its tabs headless, and the desktop
draws a local page the address bar cannot navigate (#17009).

AI-assisted; verified with the tests and tools listed above.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…browser channel

In WSL-only mode the desktop starts its backend without the desktop
browser fds, so the server never marked its tabs headless. The desktop
then drew its own local page that typed URLs never reached, while agents
drove a separate headless page. Mark every tab the desktop does not draw
headless, so the pane streams the server's page.

Fixes #17009

AI-assisted; verified with the tests and tools listed above.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot added vouch:trusted Author is trusted by repo permissions or the VOUCHED list. size:XS 0-9 changed lines (additions + deletions). labels Nov 12, 2026
@macroscopeapp

macroscopeapp Bot commented Nov 12, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Approved at b51b361

Macroscope's review found this PR approvable — Server-backed browser tabs are now marked headless even when no desktop browser channel exists, allowing the preview pane to render the page actually being controlled. Desktop-connected tabs remain unchanged, and the change is limited to rendering selection.

You can add or adjust custom eligibility rules. Learn more.

@github-actions

Copy link
Copy Markdown
Contributor

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

Provider Metric Main baseline This PR Impact PR ceiling
Codex Total thread wire 5.0 KiB 5.0 KiB 0 B (0.0%) 6.8 KiB ✅
Codex Thread snapshot wire 3.8 KiB 3.8 KiB 0 B (0.0%) 4.9 KiB ✅
Codex Live turn WebSocket wire 1.2 KiB 1.2 KiB 0 B (0.0%) 2.0 KiB ✅
Codex Live turn WebSocket decoded 21.5 KiB 21.5 KiB 0 B (0.0%) 24.4 KiB ✅
Codex Live turn messages 1 1 0 (0.0%) 8 ✅
Claude Total thread wire 5.0 KiB 5.0 KiB 0 B (0.0%) 6.8 KiB ✅
Claude Thread snapshot wire 3.8 KiB 3.8 KiB 0 B (0.0%) 4.9 KiB ✅
Claude Live turn WebSocket wire 1.2 KiB 1.2 KiB 0 B (0.0%) 2.0 KiB ✅
Claude Live turn WebSocket decoded 21.9 KiB 21.9 KiB 0 B (0.0%) 24.4 KiB ✅
Claude Live turn messages 1 1 0 (0.0%) 8 ✅

Other scenarios

All 64 scenario measurements match main.

All 64 scenario measurements
Provider Scenario Metric Main baseline This PR Impact PR ceiling
Codex short turn resume WebSocket decoded 10.5 KiB 10.5 KiB 0 B (0.0%) 12.7 KiB ✅
Codex short turn resume WebSocket messages 2 2 0 (0.0%) 2 ✅
Codex long turn running, cold open HTTP decoded 281.7 KiB 281.7 KiB 0 B (0.0%) 302.7 KiB ✅
Codex long turn running, cold open rows 75 75 0 (0.0%) 75 ✅
Codex long turn running, history page HTTP decoded 92.4 KiB 92.4 KiB 0 B (0.0%) 107.4 KiB ✅
Codex long turn running, history page rows 75 75 0 (0.0%) 75 ✅
Codex long turn finished, cold open HTTP decoded 381.5 KiB 381.5 KiB 0 B (0.0%) 410.2 KiB ✅
Codex long turn finished, cold open rows 151 151 0 (0.0%) 151 ✅
Codex long turn finished, socket cold open WebSocket decoded 381.6 KiB 381.6 KiB 0 B (0.0%) 410.2 KiB ✅
Codex long turn finished, socket cold open WebSocket messages 2 2 0 (0.0%) 2 ✅
Codex long turn finished, socket cold open rows 151 151 0 (0.0%) 151 ✅
Codex long turn finished, work page HTTP decoded 186.2 KiB 186.2 KiB 0 B (0.0%) 200.2 KiB ✅
Codex long turn finished, work page rows 150 150 0 (0.0%) 150 ✅
Codex idle thread resume WebSocket decoded 65 B 65 B 0 B (0.0%) 200 B ✅
Codex idle thread resume WebSocket messages 1 1 0 (0.0%) 1 ✅
Codex fleet shell snapshot HTTP decoded 41.6 KiB 41.6 KiB 0 B (0.0%) 53.7 KiB ✅
Codex fleet shell snapshot rows 27 27 0 (0.0%) 27 ✅
Codex fleet shell subscribe WebSocket decoded 65 B 65 B 0 B (0.0%) 200 B ✅
Codex fleet shell subscribe WebSocket messages 1 1 0 (0.0%) 1 ✅
Codex fleet shell resume check HTTP decoded 18 B 18 B 0 B (0.0%) 100 B ✅
Codex fleet shell resume check rows 0 0 0 0 ✅
Codex stale shell reload HTTP decoded 41.6 KiB 41.6 KiB 0 B (0.0%) 53.7 KiB ✅
Codex stale shell reload rows 27 27 0 (0.0%) 27 ✅
Codex stale shell resume WebSocket decoded 140 B 140 B 0 B (0.0%) 400 B ✅
Codex stale shell resume WebSocket messages 2 2 0 (0.0%) 2 ✅
Codex stale todo list cold open HTTP decoded 72.4 KiB 72.4 KiB 0 B (0.0%) 83.0 KiB ✅
Codex stale todo list cold open rows 75 75 0 (0.0%) 75 ✅
Codex stale todo list cold open nodes 2 2 0 (0.0%) 4 ✅
Codex stale todo list cold open subagents 2 2 0 (0.0%) 4 ✅
Codex stale todo list cold open plans 1 1 0 (0.0%) 2 ✅
Codex stale todo list cold open providerTurns 1 1 0 (0.0%) 2 ✅
Codex stale todo list cold open checkpoints 1 1 0 (0.0%) 2 ✅
Claude short turn resume WebSocket decoded 10.6 KiB 10.6 KiB 0 B (0.0%) 12.7 KiB ✅
Claude short turn resume WebSocket messages 2 2 0 (0.0%) 2 ✅
Claude long turn running, cold open HTTP decoded 283.3 KiB 283.3 KiB 0 B (0.0%) 302.7 KiB ✅
Claude long turn running, cold open rows 75 75 0 (0.0%) 75 ✅
Claude long turn running, history page HTTP decoded 93.4 KiB 93.4 KiB 0 B (0.0%) 107.4 KiB ✅
Claude long turn running, history page rows 75 75 0 (0.0%) 75 ✅
Claude long turn finished, cold open HTTP decoded 384.2 KiB 384.2 KiB 0 B (0.0%) 410.2 KiB ✅
Claude long turn finished, cold open rows 151 151 0 (0.0%) 151 ✅
Claude long turn finished, socket cold open WebSocket decoded 384.3 KiB 384.3 KiB 0 B (0.0%) 410.2 KiB ✅
Claude long turn finished, socket cold open WebSocket messages 2 2 0 (0.0%) 2 ✅
Claude long turn finished, socket cold open rows 151 151 0 (0.0%) 151 ✅
Claude long turn finished, work page HTTP decoded 188.1 KiB 188.1 KiB 0 B (0.0%) 200.2 KiB ✅
Claude long turn finished, work page rows 150 150 0 (0.0%) 150 ✅
Claude idle thread resume WebSocket decoded 66 B 66 B 0 B (0.0%) 200 B ✅
Claude idle thread resume WebSocket messages 1 1 0 (0.0%) 1 ✅
Claude fleet shell snapshot HTTP decoded 42.2 KiB 42.2 KiB 0 B (0.0%) 53.7 KiB ✅
Claude fleet shell snapshot rows 27 27 0 (0.0%) 27 ✅
Claude fleet shell subscribe WebSocket decoded 66 B 66 B 0 B (0.0%) 200 B ✅
Claude fleet shell subscribe WebSocket messages 1 1 0 (0.0%) 1 ✅
Claude fleet shell resume check HTTP decoded 18 B 18 B 0 B (0.0%) 100 B ✅
Claude fleet shell resume check rows 0 0 0 0 ✅
Claude stale shell reload HTTP decoded 42.2 KiB 42.2 KiB 0 B (0.0%) 53.7 KiB ✅
Claude stale shell reload rows 27 27 0 (0.0%) 27 ✅
Claude stale shell resume WebSocket decoded 140 B 140 B 0 B (0.0%) 400 B ✅
Claude stale shell resume WebSocket messages 2 2 0 (0.0%) 2 ✅
Claude stale todo list cold open HTTP decoded 73.4 KiB 73.4 KiB 0 B (0.0%) 83.0 KiB ✅
Claude stale todo list cold open rows 75 75 0 (0.0%) 75 ✅
Claude stale todo list cold open nodes 2 2 0 (0.0%) 4 ✅
Claude stale todo list cold open subagents 2 2 0 (0.0%) 4 ✅
Claude stale todo list cold open plans 1 1 0 (0.0%) 2 ✅
Claude stale todo list cold open providerTurns 1 1 0 (0.0%) 2 ✅
Claude stale todo list cold open checkpoints 1 1 0 (0.0%) 2 ✅

Baseline: a2685e6 · PR result: b51b361 · Source CI: success

Scenario and decoded snapshot size

10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.

  • Codex decoded thread snapshot: 108.5 KiB
  • Claude decoded thread snapshot: 108.8 KiB

Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed.

@coderabbitai

coderabbitai Bot commented Nov 12, 2026 •

Copy link
Copy Markdown
Contributor

Explain Change →

No actionable comments were generated in the recent review.

Walkthrough

ServerBrowser now marks a tab headless whenever no desktop page is connected, regardless of desktop-channel availability. A regression test covers the unavailable-channel case.

Changes: Server tab headless fallback

Layer / File(s) Summary
Headless fallback and regression test
apps/server/src/preview/ServerBrowser.ts, apps/server/src/preview/ServerBrowser.test.ts
The headless fallback now applies whenever no desktop page is connected. A test checks that a tab is marked headless when the desktop channel is unavailable.

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

Change: Bug fix · Severity of issue fixed: Medium

Possibly related issues

  • Issue #32940 reports that the panel remains on the new-tab page in WSL-only mode. This change covers headless marking when no desktop page is connected, but the issue’s exact cause is not verified.

Possibly related PRs

  • pingdotgg/t3code#27043: Changes the same headless condition in ServerBrowser.ts and tests the no-desktop-channel case.
  • pingdotgg/t3code#31172: Addresses headless marking for server tabs when the desktop browser channel is absent, through PreviewManager.open.
  • pingdotgg/t3code#28488: Applies the same headless condition in ServerBrowser.createTab and tests the no-desktop-channel case.

Suggested labels: bugfix

Suggested reviewers: t3dotgg, macroscopeapp, maria-rcks

Merge Risk: Minimal · up to b51b3

Tabs that the desktop does not draw now fall back to the server's page, which fixes the reported navigation failure when no desktop browser channel exists. No concrete merge-blocking risk remains.

Pre-merge checks: Passed 3 · Inconclusive 1

Failed Failed checks: 1 inconclusive

Check name Status Explanation Resolution
Linked Issues check Inconclusive Issue #17009 reports two symptoms: (1) the built-in browser pane accepts a URL but does not navigate, and (2) agent desktop browser tools fail with [browser.disconnected]. The diff changes one conditi… Provide evidence that agent desktop browser tools (browser.tabs.list, browser.tabs.open) no longer fail with [browser.disconnected] when the desktop has no browser channel. This can be a test or a run in the desktop app on this head. Altern…
Passed Passed checks: 3 passed

Check name Status Explanation
Title check Passed The title names the main change: the browser pane navigates when the desktop backend has no browser channel. The PR removes the desktopChannel.available condition so that tabs the desktop does not dra…
Out of Scope Changes check Passed The source change is a one-condition change in ServerBrowser.ts setUpTab. It directly implements the requirement that unrendered tabs stream the server page. The test change in ServerBrowser.test.ts i…
Description check Passed The description follows the repository template closely. It has a clear Problem section, a Change section that explains why dropping the desktopChannel.available condition fixes the bug, and a Scope s…
Full details: Title check

Explanation

The title names the main change: the browser pane navigates when the desktop backend has no browser channel. The PR removes the desktopChannel.available condition so that tabs the desktop does not draw are marked headless and stream the server's page. The title is specific and follows a conventional commit format.

Full details: Out of Scope Changes check

Explanation

The source change is a one-condition change in ServerBrowser.ts setUpTab. It directly implements the requirement that unrendered tabs stream the server page. The test change in ServerBrowser.test.ts is a regression test for the same condition. The PR description says the flag is read only by the native-render check, and this was not contradicted in the reviewed diff. No unrelated changes appear in the whole-PR diff from f03db4f to b51b361. The PR does not add WSL browser-channel support, which the PR description excludes as a separate feature.

Full details: Description check

Explanation

The description follows the repository template closely. It has a clear Problem section, a Change section that explains why dropping the desktopChannel.available condition fixes the bug, and a Scope section that links issue #17009 and explains the bug fix. Its Verification section lists a failing-then-passing repro test, a 79/79 test run, check:changed results, and before/after evidence with a reproduction on main. The author also states items not checked, including native Windows with a WSL-only backend and the agent preview tool path. The template's optional agent-model line is not present, but that is minor. The description is mostly complete, so the check passes.

Full details: Linked Issues check

Explanation

Issue #17009 reports two symptoms: (1) the built-in browser pane accepts a URL but does not navigate, and (2) agent desktop browser tools fail with [browser.disconnected]. The diff changes one condition in apps/server/src/preview/ServerBrowser.ts (setUpTab, around L887-891). The condition was !desktop &amp;&amp; desktopChannel.available. It is now !desktop. Tabs that the desktop does not draw are marked headless, so the pane streams the server page. This addresses symptom (1) on the code path. The added test 'a server started without the desktop browser channel marks its tabs headless' covers the same path. The diff does not touch the code that emits the [browser.disconnected] error, and no change in this PR is shown to connect agent desktop browser tools to the session. The PR author reports a failing test on the base and a passing test with the fix. The author did not re-run the desktop app on this branch, so end-to-end navigation on this head is reported, not verified here.

Resolution

Provide evidence that agent desktop browser tools (browser.tabs.list, browser.tabs.open) no longer fail with [browser.disconnected] when the desktop has no browser channel. This can be a test or a run in the desktop app on this head. Alternatively, state that the tool path is out of scope for this PR and the issue should stay open for it. Also provide the end-to-end run on this head that the PR's Evidence section plans to capture.

Finishing Touches

Generate unit tests (beta)

  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
Recent review info

Run configuration

Commits

Reviewing files that changed from the base of the PR and between a2685e6 and b51b361.

Files selected for processing: 2

  • apps/server/src/preview/ServerBrowser.test.ts
  • apps/server/src/preview/ServerBrowser.ts

Limit details: You’ve used all 10 included reviews currently available.

@juliusmarminge
juliusmarminge merged commit 4c255bc into main Nov 12, 2026
36 checks passed
@juliusmarminge
juliusmarminge deleted the factory/issue-17009-a28601 branch November 12, 2026 18:59
Pipyakas pushed a commit to Pipyakas/t3code that referenced this pull request Nov 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XS 0-9 changed lines (additions + deletions). vouch:trusted Author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Built-in browser does not render or navigate on v0.0.46 nightlies; desktop browser tools report "No desktop browser is connected to this session"

1 participant