From 3e4d044b2db0ccf11277742faa036d30661de153 Mon Sep 17 00:00:00 2001 From: T3 Code PR Stack <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 1 Sep 2026 10:23:54 +0200 Subject: [PATCH 1/2] fix(codex): discover deferred preview tools Codex 5.6 Code Mode can defer MCP tools without exposing tool_search, leaving them reachable only through ALL_TOOLS. Teach T3's Codex instructions to use that fallback before declaring browser collaboration unavailable. Model: gpt-5.6-sol Harness: T3 Code Codex --- apps/server/src/provider/CodexDeveloperInstructions.ts | 2 ++ apps/server/src/provider/Layers/CodexSessionRuntime.test.ts | 3 +++ 2 files changed, 5 insertions(+) diff --git a/apps/server/src/provider/CodexDeveloperInstructions.ts b/apps/server/src/provider/CodexDeveloperInstructions.ts index 7ce2e553e9e7..a43cc581c71c 100644 --- a/apps/server/src/provider/CodexDeveloperInstructions.ts +++ b/apps/server/src/provider/CodexDeveloperInstructions.ts @@ -6,6 +6,8 @@ const T3_CODE_BROWSER_TOOL_INSTRUCTIONS = ` You are running inside T3 Code. The \`t3-code\` MCP server is the product-native collaborative browser shared with the user. When it exposes \`preview_*\` tools, prefer those tools for browser navigation, inspection, interaction, screenshots, and recordings. +Codex Code Mode may defer MCP tools instead of listing them as top-level tools. If \`preview_status\` is not listed directly, inspect the code-mode tool runner's \`ALL_TOOLS\` catalog for \`mcp__t3_code__preview_*\` and invoke the matching deferred tool there. Do this before deciding the T3 preview tools are absent; do not use MCP resource-listing tools to discover callable tools. + For browser work, first call \`preview_status\`. If no automation-capable preview is attached, call \`preview_open\` before concluding that the browser is unavailable. Then use \`preview_navigate\`, \`preview_snapshot\`, and the focused interaction tools. Prefer snapshot-provided locators over coordinates. Do not switch to global browser skills, Chrome, Node REPL browser automation, standalone Playwright, or agent-browser merely because the preview is initially closed or a first call fails. Use an alternative browser system only when the T3 preview tools are absent, the user explicitly requests another browser, or \`preview_open\` returns an explicit unsupported/unavailable error. A failed T3 preview tool call should be inspected and retried with corrected arguments when the error is actionable. diff --git a/apps/server/src/provider/Layers/CodexSessionRuntime.test.ts b/apps/server/src/provider/Layers/CodexSessionRuntime.test.ts index 612c08a64308..cb263f74d6f5 100644 --- a/apps/server/src/provider/Layers/CodexSessionRuntime.test.ts +++ b/apps/server/src/provider/Layers/CodexSessionRuntime.test.ts @@ -508,6 +508,9 @@ describe("T3 browser developer instructions", () => { NodeAssert.match(instructions, /t3-code/); NodeAssert.match(instructions, /preview_status/); NodeAssert.match(instructions, /preview_open/); + NodeAssert.match(instructions, /ALL_TOOLS/); + NodeAssert.match(instructions, /mcp__t3_code__preview_/); + NodeAssert.match(instructions, /do not use MCP resource-listing tools/); NodeAssert.match(instructions, /Do not switch to global browser skills/); } }); From e2153b3725764dd434a361dc6dac7df6176cb2f4 Mon Sep 17 00:00:00 2001 From: T3 Code PR Stack <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 1 Sep 2026 10:32:32 +0200 Subject: [PATCH 2/2] fix(web): require visible collaborative previews Do not report preview_open as ready when the requested floating preview has not actually attached to a visible browser surface. Model: GPT-5.6 Sol\nHarness: Codex via T3 Code --- .../components/preview/PreviewAutomationHosts.tsx | 13 ++++++++++--- .../preview/previewAutomationOpenReadiness.test.ts | 7 +++++++ .../preview/previewAutomationOpenReadiness.ts | 8 ++++++++ 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/apps/web/src/components/preview/PreviewAutomationHosts.tsx b/apps/web/src/components/preview/PreviewAutomationHosts.tsx index fc1ccfb57625..f6e1d294eff8 100644 --- a/apps/web/src/components/preview/PreviewAutomationHosts.tsx +++ b/apps/web/src/components/preview/PreviewAutomationHosts.tsx @@ -60,6 +60,7 @@ import { PreviewAutomationViewportTimeoutError, } from "./previewAutomationErrors"; import { + isPreviewAutomationPresentationReady, previewAutomationDefaultViewport, previewAutomationOpenNeedsOverlay, shouldOpenPreviewMiniPlayer, @@ -81,6 +82,7 @@ const waitForDesktopOverlay = async ( runtimeTabId: string, operation: PreviewAutomationRequest["operation"], timeoutMs: number, + requireVisible: boolean, ): Promise => { const deadline = Date.now() + timeoutMs; while (Date.now() <= deadline) { @@ -90,7 +92,11 @@ const waitForDesktopOverlay = async ( }); if (state.desktopByTabId[tabId] && previewBridge && isPreviewWebviewRendering(runtimeTabId)) { const status = await previewBridge.automation.status(runtimeTabId); - if (status.available) return; + const surfaceVisible = + useBrowserSurfaceStore.getState().byTabId[runtimeTabId]?.visible ?? false; + if (isPreviewAutomationPresentationReady(status.available, surfaceVisible, requireVisible)) { + return; + } } await new Promise((resolve) => window.setTimeout(resolve, 50)); } @@ -358,7 +364,7 @@ function PreviewAutomationHost(props: { readonly environmentId: EnvironmentId }) tabId, bridgeAvailable: Boolean(previewBridge), }; - const requireReadyTab = async () => { + const requireReadyTab = async (requireVisible = false) => { const bridge = previewBridge; const readyTabId = tabId; if (!bridge || !readyTabId) { @@ -374,6 +380,7 @@ function PreviewAutomationHost(props: { readonly environmentId: EnvironmentId }) runtimeTabId, request.operation, request.timeoutMs, + requireVisible, ); return { bridge, @@ -464,7 +471,7 @@ function PreviewAutomationHost(props: { readonly environmentId: EnvironmentId }) usePreviewMiniPlayerStore.getState().open(threadRef, activeTabId); } if (activeSnapshot && previewAutomationOpenNeedsOverlay(input, activeSnapshot)) { - await requireReadyTab(); + await requireReadyTab(shouldPresentPreview); } if (reusedExistingTab && resolvedInputUrl && previewBridge) { await previewBridge.navigate(activeTabId, resolvedInputUrl); diff --git a/apps/web/src/components/preview/previewAutomationOpenReadiness.test.ts b/apps/web/src/components/preview/previewAutomationOpenReadiness.test.ts index dc8836c14492..be0927b26acc 100644 --- a/apps/web/src/components/preview/previewAutomationOpenReadiness.test.ts +++ b/apps/web/src/components/preview/previewAutomationOpenReadiness.test.ts @@ -3,6 +3,7 @@ import { describe, expect, it } from "vite-plus/test"; import { DEFAULT_PREVIEW_AUTOMATION_VIEWPORT, + isPreviewAutomationPresentationReady, previewAutomationDefaultViewport, previewAutomationOpenNeedsOverlay, shouldOpenPreviewMiniPlayer, @@ -61,6 +62,12 @@ describe("preview automation open readiness", () => { ).toBe(true); }); + it("does not report a requested preview as ready until its surface is visible", () => { + expect(isPreviewAutomationPresentationReady(true, false, true)).toBe(false); + expect(isPreviewAutomationPresentationReady(true, true, true)).toBe(true); + expect(isPreviewAutomationPresentationReady(true, false, false)).toBe(true); + }); + it("gives newly-created automation tabs a stable desktop viewport", () => { expect(previewAutomationDefaultViewport(false, snapshot({ _tag: "Idle" }))).toEqual( DEFAULT_PREVIEW_AUTOMATION_VIEWPORT, diff --git a/apps/web/src/components/preview/previewAutomationOpenReadiness.ts b/apps/web/src/components/preview/previewAutomationOpenReadiness.ts index 7f525b425501..9bfb66ba3453 100644 --- a/apps/web/src/components/preview/previewAutomationOpenReadiness.ts +++ b/apps/web/src/components/preview/previewAutomationOpenReadiness.ts @@ -36,6 +36,14 @@ export function previewAutomationOpenNeedsOverlay( return input.url !== undefined || snapshot.navStatus._tag !== "Idle"; } +export function isPreviewAutomationPresentationReady( + automationAvailable: boolean, + surfaceVisible: boolean, + requireVisible: boolean, +): boolean { + return automationAvailable && (!requireVisible || surfaceVisible); +} + /** * Whether a freshly opened automation tab still needs a viewport applied. *