Skip to content

[codex] Add integrated browser preview, annotations, and agent automation#3053

Open
t3dotgg wants to merge 23 commits into
mainfrom
codex/browser-preview-port
Open

[codex] Add integrated browser preview, annotations, and agent automation#3053
t3dotgg wants to merge 23 commits into
mainfrom
codex/browser-preview-port

Conversation

@t3dotgg

@t3dotgg t3dotgg commented Jun 12, 2026

Copy link
Copy Markdown
Member

Summary

Adds a complete integrated browser workflow to T3 Code, spanning the web UI, Electron guest webview, environment server, provider sessions, and shared contracts.

  • adds a resizable, tabbed browser preview panel with compact navigation chrome, website favicons with generic fallback, zoom, keyboard shortcuts, loading/error states, and terminal-link handling
  • discovers local development servers and manages thread-scoped preview tabs and navigation state through server RPCs
  • adds screenshot-backed visual annotation tools for elements, multi-selection, rectangular regions, freehand drawings, comments, erasing, and live style adjustments
  • attaches structured DOM/component metadata and image context to composer drafts and conversation messages
  • adds one-click screenshots plus shift-click video recording, with active recording feedback and saved-artifact path toasts
  • exposes the visible preview to coding agents through an authenticated shared HTTP MCP server and ten preview_* automation tools

Agent automation

The environment server now hosts one reusable Streamable HTTP MCP endpoint at /mcp. Provider sessions receive short-lived, capability-scoped bearer credentials when they start or resume; only token hashes are retained, and credentials are revoked with the provider session.

The preview toolkit supports:

  • status and opening/showing the integrated preview
  • URL navigation with readiness controls
  • DOM/accessibility snapshots with screenshots
  • selector or coordinate clicks
  • text entry and key presses
  • scrolling
  • bounded JavaScript evaluation
  • waiting for selectors, text, or URL changes

Automation is routed through a preview broker to the focused desktop owner and then executed against the existing visible Electron webview via CDP. It does not launch a separate headless browser or per-thread MCP process, so the agent and user share the same page, cookies, navigation history, and visual state.

Provider integration covers Codex, Claude, Cursor, Grok, and OpenCode session startup/resume paths.

Preview and annotation architecture

  • apps/server owns local-server discovery, preview session state, WebSocket RPCs, MCP authentication, scoped provider credentials, and automation request routing.
  • apps/web owns the right-side preview experience, per-thread state, focused automation ownership, composer attachments, and preview lifecycle UX.
  • apps/desktop owns the sandboxed Electron webview, navigation/zoom state, screenshot and recording capture, element picking, annotation overlays, and CDP execution.
  • packages/contracts and packages/client-runtime define the shared preview, IPC, RPC, annotation, and automation protocols.

The picker preload intentionally uses contextIsolation=false so React component metadata is visible, while retaining sandbox=true and nodeIntegration=false; the main process also enforces the security-critical guest preferences before attachment.

Reliability

  • handles app/backend restarts and webview registration races without treating expected startup states as fatal IPC errors
  • normalizes MCP notification responses to 202 Accepted for Codex Streamable HTTP compatibility
  • bounds evaluation output, visible text, snapshot element counts, screenshots, and operation timeouts
  • scopes automation to the authenticated environment/thread/provider session and the currently focused preview owner
  • revokes credentials on provider teardown and invalidates all credentials on server restart

User impact

Users can discover and open a local app inside T3 Code, inspect and annotate the actual rendered page, capture screenshots or recordings, attach precise visual context to a prompt, and ask the coding agent to operate that same visible browser directly.

Validation

  • vp check
  • vp run typecheck
  • vp test run apps/desktop/src/preview-view-manager.test.ts apps/desktop/src/playwright-injected-runtime.test.ts
  • vp test run apps/server/src/mcp/Layers/McpHttpServer.test.ts apps/server/src/mcp/Layers/PreviewAutomationBroker.test.ts apps/server/src/mcp/toolkits/preview/tools.test.ts apps/server/src/provider/Layers/CodexAdapter.test.ts apps/server/src/provider/Layers/CodexSessionRuntime.test.ts
  • prior full suite: vp test (3,438 passed, 7 skipped)
  • live Codex verification: MCP server reached ready; preview_open, preview_status, and preview_snapshot executed against the integrated t3.chat webview

Note

High Risk
Large cross-cutting surface (auth tokens, MCP HTTP, CDP automation, asset/IPC paths) with security-sensitive browser control and broad provider wiring; regressions could affect session auth, remote preview routing, or desktop stability.

Overview
Adds an end-to-end integrated browser preview in the chat right panel (tabs, navigation, zoom, discovery) plus desktop Electron webview hosting with screenshots, recordings, element picking, and Tailwind-backed annotation overlays wired through new IPC/preload and browser-artifacts storage.

Exposes the visible preview to coding agents via a per-environment HTTP MCP server at /mcp: session-scoped bearer credentials (McpSessionRegistry), a preview automation broker that routes to the focused desktop client, and preview_* tools (open, navigate, snapshot, click/type/press/scroll, evaluate, wait) executed on the shared webview through CDP—not a headless browser. Provider adapters are wired to register the shared MCP endpoint; empty MCP notification responses are normalized to 202 for Codex compatibility.

Shared contracts and plans document preview automation schemas, WS bridge RPCs, and the revised shared-MCP architecture; desktop launch scripts gain Linux sandbox fallback and annotation CSS build tooling.

Reviewed by Cursor Bugbot for commit 524a92a. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Add integrated browser preview, annotations, and agent automation to the chat UI

  • Adds a unified right panel (RightPanelStore) to ChatView that hosts browser preview tabs, grouped terminals (horizontal/vertical splits), diff, and plan surfaces, replacing the previous diff-only sidebar.
  • Introduces a full preview browser (PreviewPanel, PreviewView, PreviewChromeRow, HostedBrowserWebview) that renders an Electron webview per session, tracks navigation state, handles zoom/devtools, and can auto-open based on project script config.
  • Adds a preview automation MCP server (McpHttpServer, PreviewAutomationBroker, PreviewToolkit) exposing preview_* tools (snapshot, click, type, navigate, etc.) that AI providers (Claude, Codex, Cursor, Grok, OpenCode) can call via per-thread bearer-token–authenticated sessions.
  • Adds element picking in the preview overlay (PickPreload) and wires picked elements as ElementContextDraft chips in the composer, appended to outgoing prompts.
  • Adds port discovery (PortScanner/PortDiscovery) that scans for localhost listeners, attributes them to terminals, and surfaces them in the sidebar and terminal UI as globe-icon shortcuts.
  • Replaces the /attachments/:id asset route with a signed, expiring /api/assets/ route (AssetAccess) covering workspace files, attachments, and project favicons; client code resolves URLs via a new useAssetUrl hook with auto-refresh.
  • Adds vertical terminal splits (splitTerminalVertical), new keybindings (rightPanel.toggle, terminal.splitVertical, preview.* zoom/refresh/focusUrl), and context-aware shortcut routing between the bottom drawer and right-panel terminals.
  • Risk: The asset route is now unauthenticated at the HTTP layer (authentication is token-based inside the signed URL); the previous scope-checked /attachments endpoint is removed entirely.

Macroscope summarized 524a92a.

aidenybai and others added 8 commits June 11, 2026 14:38
Adds a desktop-only browser preview that lives in the right panel slot
alongside plan/diff. Lets the user point an Electron <webview> at any
URL — typed into a chrome-style URL bar, clicked from the empty-state
list of detected localhost dev servers, or auto-opened by a project
script with `previewUrl` set. Single-tab per thread.

Server (Effect/Layers):
- PreviewManager: per-(thread, tab) session metadata via SynchronizedRef
  + PubSub<PreviewEvent>; survives WS reconnect via `list`/replay.
- PreviewPortScanner: lsof on macOS/Linux, TCP probe fallback on
  Windows; reference-counted polling so we only scan when subscribed.
- WS RPC + streams (`preview.open|navigate|refresh|close|list|reportStatus`,
  `subscribePreviewEvents`, `subscribeDiscoveredLocalServers`).

Desktop:
- PreviewViewManager owns Chromium WebContents per tab, mediates
  navigation/zoom/devtools/clear-storage. registerWebview gates by
  webContents.getType() === "webview" and host-window match.
- IPC channels for create/close/register/navigate/back/forward/refresh/
  zoom/hardReload/openDevTools/clearCookies/clearCache/getBrowserPartition.
- Forwards app-level shortcuts (mod+shift+J, mod+K, mod+,, mod+W) from
  the webview back to the main window.
- Persisted browser session partition (cookies, cache).

Web:
- PreviewPanel/PreviewView/PreviewWebview render the surface; chrome row
  with back/forward/refresh + URL input + Open-in-browser + 3-dot menu
  (Hard reload, DevTools, Zoom −/+/reset, Clear cookies/cache).
- usePreviewSession subscribes to server events; usePreviewBridge
  mirrors desktop state into the store and forwards Loading→Success/
  LoadFailed back to the server.
- previewStateStore: per-thread snapshot + desktopOverlay + recently-
  seen URLs (Zustand).
- rightPanelStore arbitrates plan vs. preview vs. diff; ChatView's
  toggles strip the `?diff=1` URL hint when switching to preview and
  vice versa so the panels are mutually exclusive.
- Top-nav Globe toggle in ChatHeader (desktop builds only) and a
  `mod+shift+J` keybinding routed via a typed previewActionBus.
- PreviewEmptyState lists detected localhost servers (scanner +
  configured project URLs + recently-seen) with live "listening" pulse.
- PreviewUnreachable: theme-aware port of Chromium's "site can't be
  reached" page.
- Resizable inline panel (RightPanelResizeHandle + useResizableWidth);
  width persists to localStorage on drag-end.
- Terminal link "Open in preview" context-menu integration for loopback
  URLs.

Contracts:
- preview.ts schemas (PreviewSessionSnapshot, PreviewNavStatus,
  PreviewEvent, RPC inputs/results, DiscoveredLocalServer).
- ProjectScript schema gains optional `previewUrl` + `autoOpenPreview`.
- New keybinding commands: preview.toggle/refresh/focusUrl/zoomIn/Out/
  resetZoom; new `when:` contexts `previewFocus` / `previewOpen`.

Shared:
- @t3tools/shared/preview: normalizePreviewUrl, isPreviewableUrl,
  isLoopbackHost, newPreviewTabId, LSOF_LOCAL_HOST_TOKENS.

Tests:
- contracts: schema decode tests for all preview events/snapshots/inputs.
- shared: URL normalization coverage.
- server: PreviewManager (open/navigate/reportStatus/refresh/close,
  multi-subscriber isolation, idempotency); PortScanner (lsof parsing
  including IPv6, TCP probe, reference-counted polling).
- web: previewStateStore (per-tab event application, dedupe,
  reconnect recovery); rightPanelStore arbitration.
Adds an in-page element picker to the preview browser. Clicking the
crosshair button in the chrome row activates a blue-highlight picker
inside the guest webview; clicking an element captures its component
name (via react-grab), source location, html/css preview, and selector,
then attaches it to the chat composer as a chip that serializes into an
`<element_context>` block in the outgoing message.

Architecture:
- Per-`<webview>` preload bundle (`preview-pick-preload.cjs`) renders
  the overlay, hosts the picker event loop, and bubbles the picked
  payload back to main via the per-WebContents `wc.ipc` channel (not
  `sendToHost`, which only fires on the host renderer's <webview>
  element and never reaches main).
- Main coordinates via `PreviewViewManager.pickElement(tabId)`, which
  cancels any in-flight session, force-focuses the guest (so the first
  click on a remote page actually reaches the preload), then awaits the
  payload. User-initiated cancels (Escape, beforeunload) echo `null`
  back to main; main-initiated cancels and supersession tear down
  silently to avoid the new-pick-resolves-with-stale-null race.
- Renderer fetches partition + webPreferences + preload URL in a single
  `getPreviewConfig()` IPC call, snapshots the previously-focused host
  element before triggering a pick, and restores focus when the pick
  resolves so the user's textarea cursor isn't lost.

Security posture for the guest webview:
- `webpreferences="contextIsolation=false,sandbox=true,nodeIntegration=false"`
  centralized in `preview-webview-preferences.ts`. contextIsolation off
  is required so react-grab's `getElementContext` can reach the page's
  React DevTools hook on `globalThis`. sandbox stays on so the page
  cannot reach Node APIs even with shared globals (without it, the
  preload's `require` would land on the page's `globalThis` and any
  third-party site could send arbitrary IPC to main).
- Defense in depth: a `will-attach-webview` handler in main, gated on
  the preview partition, force-pins `sandbox: true`, all
  `nodeIntegration*: false`, and the absolute preload PATH (not URL —
  that field rejects file:// URLs with "preload script must have
  absolute path" and silently disables the picker).

Composer + transcript integration:
- New `elementContexts` slice in `composerDraftStore` (mirrors the
  terminal-context slice: dedup by selector+tag+component+url, persist
  via partializer, restore on send-failure retry).
- `ComposerPendingElementContexts` chip row above the editor.
- `deriveDisplayedUserMessageState` now strips both `<element_context>`
  AND `<terminal_context>` blocks (element first, since it's appended
  last) and exposes element entries to `MessagesTimeline`, which renders
  them as compact chips beneath the message body.
- Pick button is disabled with explanatory tooltip when the page failed
  to load (the React `<PreviewUnreachable>` overlay covers the webview,
  so picks would silently dangle otherwise).

Tests added:
- `preview-webview-preferences.test.ts` locks down the security flags
  (contextIsolation=false, sandbox=true, nodeIntegration=false, no
  whitespace, only true/false literal values).
- `preview-pick-label-position.test.ts` covers the floating-label
  clamp/flip math (no off-screen overflow, flip-below when no room
  above, etc.).
- `picked-element-payload.test.ts` validator coverage.
- `elementContext.test.ts` for the serialization round-trip,
  normalization, dedup, and label formatting.
- `composerDraftStore.test.ts` element-contexts slice (add, dedup,
  remove, set, clear, persistence round-trip).
- `ChatView.logic.test.ts` sendable-content-with-element-only.

Build: new `tsdown` entry inlines react-grab + bippy into the picker
preload bundle (~59KB / 19KB gzipped).
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
- Add structured annotation payload validation and tests
- Update preview preload to capture selected elements, regions, and strokes
- Wire new preview annotation UI into the web app

Co-authored-by: codex <codex@users.noreply.github.com>
@coderabbitai

coderabbitai Bot commented Jun 12, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 5ff7fe7e-66cd-4d3c-aeb1-be504ec97dff

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/browser-preview-port

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:XXL 1,000+ changed lines (additions + deletions). labels Jun 12, 2026
@github-actions

github-actions Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

🚀 Expo continuous deployment is ready!

  • Project → t3-code
  • Platforms → android, ios
  • Scheme → t3code-preview
  🤖 Android 🍎 iOS
Fingerprint ae17d94b35f91f9c608a63dadbc3ddd9f4ba056e 313e506a7f15a9c3f15b01d787d68a12382432bf
Build Details Build Permalink
DetailsDistribution: INTERNAL
Build profile: preview:dev
Runtime version: ae17d94b35f91f9c608a63dadbc3ddd9f4ba056e
App version: 0.1.0
Git commit: 3f5c22bd9c4c4db6a5c56e9a13afff7696e3b4a3
Build Permalink
DetailsDistribution: INTERNAL
Build profile: preview:dev
Runtime version: 313e506a7f15a9c3f15b01d787d68a12382432bf
App version: 0.1.0
Git commit: bea35c26cdef0f9f5edc7b88968f52e1b6b4d362
Update Details Update Permalink
DetailsBranch: pr-3053
Runtime version: ae17d94b35f91f9c608a63dadbc3ddd9f4ba056e
Git commit: bea35c26cdef0f9f5edc7b88968f52e1b6b4d362
Update Permalink
DetailsBranch: pr-3053
Runtime version: 313e506a7f15a9c3f15b01d787d68a12382432bf
Git commit: bea35c26cdef0f9f5edc7b88968f52e1b6b4d362
Update QR

Comment thread apps/web/src/components/chat/MessagesTimeline.tsx
Comment thread apps/desktop/src/preview/Manager.ts
Comment thread apps/desktop/src/preview/Manager.ts
@juliusmarminge juliusmarminge changed the title [codex] Add in-app browser preview and annotation tools [codex] Add integrated browser preview, annotations, and agent automation Jun 12, 2026
Comment thread apps/web/src/components/ChatView.tsx Outdated
Comment on lines +4243 to +4249
{shouldUsePlanSidebarSheet && previewPanelOpen && activeThreadRef ? (
<RightPanelSheet open onClose={closePreviewPanel}>
<Suspense fallback={null}>
<PreviewPanel mode="sheet" threadRef={activeThreadRef} visible />
</Suspense>
</RightPanelSheet>
) : null}

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.

🟢 Low components/ChatView.tsx:4243

The mobile preview sheet at line 4243 conditionally renders based on previewPanelOpen, so it unmounts instantly when closed. The plan sidebar sheet at line 4250 stays mounted with open={planSidebarOpen}, allowing the @base-ui/react Sheet closing animation to play. This causes the preview panel to disappear jarringly on mobile instead of animating smoothly like the plan sidebar.

-      {shouldUsePlanSidebarSheet && previewPanelOpen && activeThreadRef ? (
+      {shouldUsePlanSidebarSheet && activeThreadRef ? (
         <RightPanelSheet open onClose={closePreviewPanel}>
           <Suspense fallback={null}>
-            <PreviewPanel mode="sheet" threadRef={activeThreadRef} visible />
+            <PreviewPanel mode="sheet" threadRef={activeThreadRef} visible={previewPanelOpen} />
           </Suspense>
         </RightPanelSheet>
       ) : null}
🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @apps/web/src/components/ChatView.tsx around lines 4243-4249:

The mobile preview sheet at line 4243 conditionally renders based on `previewPanelOpen`, so it unmounts instantly when closed. The plan sidebar sheet at line 4250 stays mounted with `open={planSidebarOpen}`, allowing the `@base-ui/react` `Sheet` closing animation to play. This causes the preview panel to disappear jarringly on mobile instead of animating smoothly like the plan sidebar.

Evidence trail:
apps/web/src/components/ChatView.tsx lines 4243-4264 (REVIEWED_COMMIT) — preview panel conditional mount vs. plan sidebar staying mounted.
apps/web/src/components/RightPanelSheet.tsx lines 6-29 (REVIEWED_COMMIT) — `keepMounted` on SheetPopup, `open` prop passed through to `Sheet`.
apps/web/src/components/ui/sheet.tsx line 3 — imports `@base-ui/react/dialog` as the Sheet primitive.

Comment thread apps/web/src/components/ChatView.tsx Outdated
Comment thread apps/desktop/src/preview/Manager.ts Outdated
Co-authored-by: codex <codex@users.noreply.github.com>
Comment thread apps/desktop/src/preview/Manager.ts Outdated
Comment thread apps/web/src/components/ChatView.tsx Outdated
workspaceRoot={activeWorkspaceRoot}
timestampFormat={timestampFormat}

{!shouldUsePlanSidebarSheet &&

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.

🟠 High components/ChatView.tsx:4243

On viewports wider than 980px, the inline RightPanelTabs (lines 4243–4275) only renders children for "preview" and "diff" surface kinds. When activeRightPanelSurface.kind is "terminal" or "plan", the children expression falls through to null, leaving the panel tabs visible with an empty content area. The sheet (mobile) version at 4277–4334 handles these cases correctly with PersistentThreadTerminalDrawer and PlanSidebar. Consider adding the missing surface kind branches to the inline rendering block so terminal and plan panels render on desktop.

🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @apps/web/src/components/ChatView.tsx around line 4243:

On viewports wider than 980px, the inline `RightPanelTabs` (lines 4243–4275) only renders children for `"preview"` and `"diff"` surface kinds. When `activeRightPanelSurface.kind` is `"terminal"` or `"plan"`, the children expression falls through to `null`, leaving the panel tabs visible with an empty content area. The sheet (mobile) version at 4277–4334 handles these cases correctly with `PersistentThreadTerminalDrawer` and `PlanSidebar`. Consider adding the missing surface kind branches to the inline rendering block so terminal and plan panels render on desktop.

Evidence trail:
apps/web/src/components/ChatView.tsx lines 4243-4275 (inline block handles only 'preview' and 'diff', falls through to null); lines 4277-4334 (sheet block handles 'preview', 'terminal', 'diff', and 'plan'); apps/web/src/rightPanelStore.ts lines 19-24 (RightPanelSurface type includes 'terminal' and 'plan' kinds); apps/web/src/components/ChatView.tsx line 1095 (terminal surface opened unconditionally regardless of viewport); apps/web/src/rightPanelLayout.ts line 1 (media query is max-width: 980px)

Comment thread apps/server/src/mcp/Layers/PreviewAutomationBroker.ts Outdated
- Add IPC and runtime plumbing for preview annotation theming
- Generate and ship annotation CSS for the desktop overlay
- Add pointer and artifact handling for browser preview interactions
Comment thread apps/desktop/src/preview/Manager.ts Outdated
Comment thread apps/desktop/src/preview-pick-preload.ts Outdated
- Move MCP session registry and preview broker out of `Layers/` and `Services/`
- Update imports, tests, and server wiring to use the new module layout
- Move preview session and IPC wiring into the new preview module
- Tighten IPC validation with schema-based handlers
- Update preview asset paths and tests for the browser preview port

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.

🟡 Medium

In recoverSessionForThread, when the "adopt-existing" branch is taken (lines 371-386), the function returns early at line 386 without calling prepareMcpSession. Since McpProviderSession.sessionsByThread is an in-memory map that is empty after server restart, recovering a pre-existing session via this path leaves the MCP session configuration unset, causing subsequent MCP tool calls to fail. Consider calling prepareMcpSession before returning in the adopt-existing branch, or document if this omission is intentional.

🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @apps/server/src/provider/Layers/ProviderService.ts around line 376:

In `recoverSessionForThread`, when the "adopt-existing" branch is taken (lines 371-386), the function returns early at line 386 without calling `prepareMcpSession`. Since `McpProviderSession.sessionsByThread` is an in-memory map that is empty after server restart, recovering a pre-existing session via this path leaves the MCP session configuration unset, causing subsequent MCP tool calls to fail. Consider calling `prepareMcpSession` before returning in the adopt-existing branch, or document if this omission is intentional.

Evidence trail:
apps/server/src/provider/Layers/ProviderService.ts lines 355-438 (recoverSessionForThread function), specifically lines 371-386 (adopt-existing branch returns without calling prepareMcpSession) vs line 400 (resume-thread branch calls prepareMcpSession). apps/server/src/provider/Layers/ProviderService.ts lines 217-224 (prepareMcpSession definition). apps/server/src/mcp/McpProviderSession.ts lines 12-19 (in-memory Map and setMcpProviderSession/readMcpProviderSession). apps/server/src/provider/Layers/ClaudeAdapter.ts lines 3449, 3475-3487 (readMcpProviderSession consumed conditionally for mcpServers config).

Comment on lines +48 to +59
export const PreviewOpenTool = browserTool(
Tool.make("preview_open", {
description:
"Show and initialize the browser preview for the scoped thread, optionally reusing its current tab and navigating to a URL.",
parameters: PreviewAutomationOpenInput,
success: PreviewAutomationStatus,
failure: PreviewAutomationError,
dependencies,
})
.annotate(Tool.Title, "Open browser preview")
.annotate(Tool.Destructive, false),
);

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.

🟢 Low preview/tools.ts:48

The .annotate(Tool.Destructive, false) on line 58 is overwritten by browserTool(), which calls .annotate(Tool.Destructive, true) last. The final tool has Destructive: true instead of the intended false. Consider using safeBrowserTool() instead, which preserves Destructive: false as the final annotation.

-export const PreviewOpenTool = browserTool(
-  Tool.make("preview_open", {
-    description:
-      "Show and initialize the browser preview for the scoped thread, optionally reusing its current tab and navigating to a URL.",
-    parameters: PreviewAutomationOpenInput,
-    success: PreviewAutomationStatus,
-    failure: PreviewAutomationError,
-    dependencies,
-  })
-    .annotate(Tool.Title, "Open browser preview")
-    .annotate(Tool.Destructive, false),
-);
🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @apps/server/src/mcp/toolkits/preview/tools.ts around lines 48-59:

The `.annotate(Tool.Destructive, false)` on line 58 is overwritten by `browserTool()`, which calls `.annotate(Tool.Destructive, true)` last. The final tool has `Destructive: true` instead of the intended `false`. Consider using `safeBrowserTool()` instead, which preserves `Destructive: false` as the final annotation.

Evidence trail:
apps/server/src/mcp/toolkits/preview/tools.ts lines 27-31 (browserTool and safeBrowserTool definitions), lines 48-59 (PreviewOpenTool using browserTool with .annotate(Tool.Destructive, false) on line 58 that gets overwritten), lines 61-70 (PreviewNavigateTool correctly using safeBrowserTool for comparison).

- derive preview partitions through `BrowserSession`
- serialize session state and async preview control flow
- update tests for screenshot, automation, and partition behavior
- Tie preview and debugger listeners to Effect scopes
- Factor shared automation helpers for snapshot and input handling
- Improve cleanup for browser preview sessions and port scanning
- Fetch preview sessions through atom-backed SWR state
- Recover browser preview sessions after reconnects
- Ignore older streamed snapshots when SWR revalidates
- Track preview store revisions per thread
- Ignore stale SWR results while revalidating
- Avoid restoring closed sessions from outdated data
@juliusmarminge juliusmarminge marked this pull request as ready for review June 13, 2026 01:35
Comment thread apps/web/src/components/preview/PreviewAutomationOwner.tsx
@macroscopeapp

macroscopeapp Bot commented Jun 13, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

2 blocking correctness issues found. Diff is too large for automated approval analysis. A human reviewer should evaluate this PR.

You can customize Macroscope's approvability policy. Learn more.

…-port

# Conflicts:
#	apps/web/src/components/chat/MessagesTimeline.tsx
- Replace attachment and favicon routes with signed asset URLs
- Harden workspace and attachment asset resolution
- Update browser preview components and shared contracts

@cursor cursor 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.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

There are 2 total unresolved issues (including 1 from previous review).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 98669c8. Configure here.

Comment thread apps/server/src/assets/AssetAccess.ts
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL 1,000+ changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants