Fix typecheck for host-backed agent launches and address review feedback.

Guard null tab ids from web host routing, narrow runtime environment ids for tab close, and respect mobile resize locks in web refit retries.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Jinwoo-H
2026-06-07 12:49:16 -07:00
co-authored by Cursor
parent 2ad4be457b
commit 5cfce7510f
11 changed files with 53 additions and 16 deletions
@@ -3502,7 +3502,9 @@ function ChecksTab({
toast.error('Could not build the agent launch command.')
return
}
focusTerminalTabSurface(result.tabId)
if (result.tabId) {
focusTerminalTabSurface(result.tabId)
}
toast.success('Started an AI agent for the broken checks.')
} finally {
setFixingChecks(false)
@@ -3756,7 +3756,9 @@ function ChecksTab({
toast.error('Could not build the agent launch command.')
return
}
focusTerminalTabSurface(result.tabId)
if (result.tabId) {
focusTerminalTabSurface(result.tabId)
}
toast.success('Started an AI agent for the broken checks.')
} finally {
setFixingChecks(false)
@@ -1837,7 +1837,9 @@ export default function ChecksPanel(): React.JSX.Element {
toast.error('Could not build the agent launch command.')
return
}
focusTerminalTabSurface(result.tabId)
if (result.tabId) {
focusTerminalTabSurface(result.tabId)
}
toast.success('Started an AI agent for the conflicts.')
} finally {
setIsResolvingConflictsWithAI(false)
@@ -1939,7 +1941,9 @@ export default function ChecksPanel(): React.JSX.Element {
toast.error('Could not build the agent launch command.')
return
}
focusTerminalTabSurface(result.tabId)
if (result.tabId) {
focusTerminalTabSurface(result.tabId)
}
} finally {
setIsFixingChecksWithAI(false)
}
@@ -1770,7 +1770,9 @@ function SourceControlInner(): React.JSX.Element {
return
}
focusTerminalTabSurface(result.tabId)
if (result.tabId) {
focusTerminalTabSurface(result.tabId)
}
toast.success('Started an AI agent for the conflicts.')
} finally {
setIsLaunchingConflictAgent(false)
@@ -1843,7 +1845,9 @@ function SourceControlInner(): React.JSX.Element {
return false
}
focusTerminalTabSurface(result.tabId)
if (result.tabId) {
focusTerminalTabSurface(result.tabId)
}
toast.success('Started an AI agent for the commit failure.')
return true
} finally {
@@ -138,16 +138,22 @@ function QuickLaunchAgentMenuItemsInner({
toast.error(`Could not build launch command for ${label}.`)
return
}
if (!result.tabId) {
// Why: paired web clients create the tab on the host; focus follows the
// next session-tabs snapshot instead of a local tab id.
return
}
onFocusTerminal(result.tabId)
// Why: launch success means the terminal session exists. Agent readiness
// can lag behind on slow machines, and prompt paste flows already own
// their own readiness timeout once a PTY exists.
void waitForTerminalPty(result.tabId, 5000).then((hasPty) => {
const launchedTabId = result.tabId
void waitForTerminalPty(launchedTabId, 5000).then((hasPty) => {
if (hasPty) {
return
}
const launchState = getTerminalLaunchState(result.tabId)
const launchState = getTerminalLaunchState(launchedTabId)
if (!launchState.stillOpen) {
return
}
@@ -47,7 +47,11 @@ import { useNotificationDispatch } from './use-notification-dispatch'
import { connectPanePty } from './pty-connection'
import { shouldPreserveTerminalScrollbackBuffers } from '../../../../shared/workspace-session-terminal-buffers'
import { getFitOverrideForPty, onOverrideChange } from '@/lib/pane-manager/mobile-fit-overrides'
import { getDriverForPty, onDriverChange } from '@/lib/pane-manager/mobile-driver-state'
import {
getDriverForPty,
isPtyLocked,
onDriverChange
} from '@/lib/pane-manager/mobile-driver-state'
import { resolvePaneKeyForManager } from '@/lib/pane-manager/pane-key-resolution'
import { safeFit } from '@/lib/pane-manager/pane-tree-ops'
import { captureTerminalShutdownLayout } from './terminal-shutdown-layout-capture'
@@ -1132,9 +1136,19 @@ export default function TerminalPane({
for (const pane of manager.getPanes()) {
safeFit(pane)
const transport = paneTransportsRef.current.get(pane.id)
if (transport?.isConnected()) {
transport.resize(pane.terminal.cols, pane.terminal.rows)
if (!transport?.isConnected()) {
continue
}
const ptyId = transport.getPtyId()
if (!ptyId) {
continue
}
// Why: match pty-connection resize guards so web refit retries do not
// forward SIGWINCH while mobile-lock or phone-fit overrides are active.
if (getFitOverrideForPty(ptyId) || isPtyLocked(ptyId)) {
continue
}
transport.resize(pane.terminal.cols, pane.terminal.rows)
}
}
const scheduleFrame = (): void => {
@@ -15,7 +15,7 @@ const {
getStateMock: vi.fn(),
isWebRuntimeSessionActiveMock: vi.fn(),
isWebTerminalSurfaceTabIdMock: vi.fn(() => false),
resolveHostSessionTabIdForWebSessionTabMock: vi.fn(() => null)
resolveHostSessionTabIdForWebSessionTabMock: vi.fn<() => string | null>(() => null)
}))
vi.mock('@/store', () => ({
@@ -84,7 +84,7 @@ export function closeTerminalTab(tabId: string): void {
}
const runtimeEnvironmentId = state.settings?.activeRuntimeEnvironmentId?.trim()
if (isWebRuntimeSessionActive(runtimeEnvironmentId)) {
if (runtimeEnvironmentId && isWebRuntimeSessionActive(runtimeEnvironmentId)) {
const hostBackedTabId =
resolveHostSessionTabIdForWebSessionTab(state, {
environmentId: runtimeEnvironmentId,
@@ -378,14 +378,18 @@ export function useAutomationDispatchEvents(): void {
if (!result) {
throw new Error('Unable to build an agent launch plan.')
}
observeAgentStatus(result.tabId, dispatchStartedAt)
if (!result.tabId) {
throw new Error('Host-backed agent tabs are not yet trackable for automation dispatch.')
}
const launchedTabId = result.tabId
observeAgentStatus(launchedTabId, dispatchStartedAt)
try {
await markDispatchResult({
runId: run.id,
status: 'dispatched',
workspaceId: worktree.id,
workspaceDisplayName: worktree.displayName,
terminalSessionId: result.tabId,
terminalSessionId: launchedTabId,
precheckResult,
error: null
})
@@ -11,7 +11,7 @@ const mockTrack = vi.fn()
const LEAF_ID = '11111111-1111-4111-8111-111111111111'
const store = {
settings: { agentCmdOverrides: {} },
settings: { agentCmdOverrides: {}, activeRuntimeEnvironmentId: null as string | null },
tabsByWorktree: {
'wt-1': [{ id: 'tab-1' }]
},
@@ -17,6 +17,7 @@ export function cancelPendingSidebarWorktreeActivation(): void {
}
function shouldDeferSidebarWorktreeActivation(worktreeId: string): boolean {
// Why: web clients should activate immediately to avoid host/session churn and wake lag.
if ((globalThis as { __ORCA_WEB_CLIENT__?: boolean }).__ORCA_WEB_CLIENT__ === true) {
return false
}