diff --git a/mobile/src/files/host-file-explorer-operations.ts b/mobile/src/files/host-file-explorer-operations.ts index 8144d4ca469..bad5cec19b0 100644 --- a/mobile/src/files/host-file-explorer-operations.ts +++ b/mobile/src/files/host-file-explorer-operations.ts @@ -5,7 +5,6 @@ export type HostFileExplorerDirectoryResult = | { kind: 'directory' entries: MobileDirEntry[] - truncated: boolean } | { kind: 'legacy-list' diff --git a/mobile/src/session/MobileSessionSheets.tsx b/mobile/src/session/MobileSessionSheets.tsx index e24c7e5bd4e..5e02d9b9d8a 100644 --- a/mobile/src/session/MobileSessionSheets.tsx +++ b/mobile/src/session/MobileSessionSheets.tsx @@ -105,7 +105,6 @@ export function MobileSessionSheets({ controller }: { controller: MobileSessionC visible={showQuickCommands && quickCommandsSupported === true} onClose={() => setShowQuickCommands(false)} operations={sessionOperations?.quickCommand ?? null} - workspaceId={worktreeId} repoId={ isFolderWorkspaceRoute || isFloatingWorkspaceRoute ? null diff --git a/mobile/src/session/QuickCommandsSheet.tsx b/mobile/src/session/QuickCommandsSheet.tsx index fac2b212f40..1b68d1cb3aa 100644 --- a/mobile/src/session/QuickCommandsSheet.tsx +++ b/mobile/src/session/QuickCommandsSheet.tsx @@ -24,7 +24,6 @@ type Props = { visible: boolean onClose: () => void operations: HostSessionQuickCommandOperations | null - workspaceId: string repoId: string | null repoName: string | null onLaunch: (command: TerminalQuickCommand) => boolean @@ -36,14 +35,12 @@ export function QuickCommandsSheet({ visible, onClose, operations, - workspaceId, repoId, repoName, onLaunch }: Props) { const { commands, loading, ready, error, persist } = useQuickCommands({ operations, - workspaceId, enabled: visible }) const [view, setView] = useState('list') diff --git a/mobile/src/session/host-session-native-chat-operations.ts b/mobile/src/session/host-session-native-chat-operations.ts index cad0d769f85..4a06f861bb0 100644 --- a/mobile/src/session/host-session-native-chat-operations.ts +++ b/mobile/src/session/host-session-native-chat-operations.ts @@ -44,13 +44,6 @@ export type HostSessionNativeChatOperations = { beforeOffset?: number ): Promise stop(target: HostSessionNativeChatTarget, deadline?: number): Promise - pasteImages?( - target: HostSessionNativeChatTarget, - references: readonly string[], - deadline?: number, - followedByText?: boolean - ): Promise - releaseImages?(target: HostSessionNativeChatTarget, references: readonly string[]): Promise /** Drops the legacy full-inventory fallback's cached listing for a workspace. Without it a * second visit inside one connection serves the first read's inventory, so a file created in * between is missing from `@` autocomplete. */ diff --git a/mobile/src/session/host-session-quick-command-operations.ts b/mobile/src/session/host-session-quick-command-operations.ts index 9e0022f934d..99672a5cb72 100644 --- a/mobile/src/session/host-session-quick-command-operations.ts +++ b/mobile/src/session/host-session-quick-command-operations.ts @@ -1,25 +1,11 @@ -import type { - TerminalQuickCommand, - TerminalQuickCommandScope -} from '../../../src/shared/terminal-quick-command-types' +import type { TerminalQuickCommand } from '../../../src/shared/terminal-quick-command-types' import type { TerminalQuickCommandMutation } from '../terminal/quick-commands' export type HostSessionQuickCommandSnapshot = { commands: TerminalQuickCommand[] - totalCount: number - repoId: string | null } export type HostSessionQuickCommandOperations = { - snapshot(workspaceId: string, signal?: AbortSignal): Promise - mutate( - workspaceId: string, - mutation: TerminalQuickCommandMutation - ): Promise -} - -export function explicitQuickCommandScope( - scope: TerminalQuickCommandScope | undefined -): TerminalQuickCommandScope { - return scope?.type === 'repo' ? { type: 'repo', repoId: scope.repoId } : { type: 'global' } + snapshot(signal?: AbortSignal): Promise + mutate(mutation: TerminalQuickCommandMutation): Promise } diff --git a/mobile/src/session/host-session-terminal-file-operations.ts b/mobile/src/session/host-session-terminal-file-operations.ts index 9aa71937fc6..192546d36f1 100644 --- a/mobile/src/session/host-session-terminal-file-operations.ts +++ b/mobile/src/session/host-session-terminal-file-operations.ts @@ -13,14 +13,6 @@ export type HostSessionTerminalFileTarget = grantId: string workspaceId?: string } - | { - kind: 'web-artifact' - /** The terminal text the desktop re-resolves for every chunk read. */ - pathText: string - displayName: string - previewKind: 'text' | 'raster' - workspaceId?: string - } export type HostSessionTerminalFileResolveRequest = { workspaceId: string @@ -28,8 +20,6 @@ export type HostSessionTerminalFileResolveRequest = { pathText: string cwd: string | null nativeChatContext: RuntimeNativeChatFileContext | null - line: number | null - column: number | null } export type HostSessionTerminalFileOperations = { diff --git a/mobile/src/session/host-session-terminal-operations.ts b/mobile/src/session/host-session-terminal-operations.ts index 5bffe947238..594a84b5293 100644 --- a/mobile/src/session/host-session-terminal-operations.ts +++ b/mobile/src/session/host-session-terminal-operations.ts @@ -52,11 +52,6 @@ export type HostSessionTerminalSubscribeArgs = { } export type HostSessionTerminalOperations = { - subscribe( - args: HostSessionTerminalSubscribeArgs, - onEvent: (event: HostSessionTerminalStreamEvent) => void, - onError: () => void - ): () => void sendInput( terminalId: string, text: string, @@ -70,5 +65,5 @@ export type HostSessionTerminalOperations = { clientId: string | null ): Promise clear(terminalId: string): Promise - rename(terminalId: string, title: string, workspaceId: string): Promise + rename(terminalId: string, title: string): Promise } diff --git a/mobile/src/session/mobile-file-tap-open.ts b/mobile/src/session/mobile-file-tap-open.ts index 7a9315cfa52..4bdec99f980 100644 --- a/mobile/src/session/mobile-file-tap-open.ts +++ b/mobile/src/session/mobile-file-tap-open.ts @@ -77,9 +77,7 @@ async function openMobileFileTapAsync( terminalHandle, pathText: options.pathText, cwd, - nativeChatContext: options.nativeChatContext ?? null, - line: options.line, - column: options.column + nativeChatContext: options.nativeChatContext ?? null }) if (!resolved) { reportOpenFailure(options) diff --git a/mobile/src/session/mobile-session-route-parity.test.ts b/mobile/src/session/mobile-session-route-parity.test.ts index 4acb598f7eb..eb583426d63 100644 --- a/mobile/src/session/mobile-session-route-parity.test.ts +++ b/mobile/src/session/mobile-session-route-parity.test.ts @@ -70,7 +70,7 @@ const HEAD_CALLBACK_BODY_SHA256 = 'e66e6436cdb9a66e870c06fdfc140106502fbeddd8db4 const HEAD_EFFECT_SHA256 = 'd9ebfaabc1e79773cdada7ab370b20459ed972f1f8edce1652199f4d0391cd13' const HEAD_CONTENT_HOOK_SHA256 = '9c3b612fef3f370d66873aefdbe1d701f20cb64ded31fef5cc45fde6f8189581' const HEAD_NESTED_FUNCTION_SHA256 = - '3b75d854a92b086907571f4c877961bd429f76e3826979cee5b0e94ed2007c8e' + 'ad78ecb8bd61cd0cc3a7037bbd7dcc06b0fbe0de62b23090b8350c2947d65c3d' const HEAD_NATIVE_REGISTRATION_SHA256 = 'cab85e4e4a3f43289ba93ddea9ccce57aea83e0bf14fd1620a965aad0c1cb49e' const HEAD_NATIVE_REMOVAL_SHA256 = @@ -79,13 +79,13 @@ const HEAD_TIMER_CREATION_SHA256 = '1a31b625e2174c3db77272249843196d2b6b06ab1e654a96d8f7858e3082e66b' const HEAD_TIMER_CLEANUP_SHA256 = 'c73f1d1c2cc89642f3d727d6f3b6b81860a9d6f34234541a2065ec3d1a8cd116' const HEAD_RUNTIME_STRING_SHA256 = - '2f78f3e8f835b3a786c8940dabc127d0d131e909c0e736a4a341c5d50ce35059' + '58ee753e4fc849c693aa6ebb71b2ff7c38dbbba50977cacc737b03c4bb2a7224' const HEAD_HOST_JSX_SHA256 = '390405926b1695fa3a33686f0bc192b432f5468d8576499d7cafbb4922defbb5' -const HEAD_LEAF_JSX_SHA256 = 'd5f1ef0db57c63eb3e4ee7c98e8483bc21882a151ce0ca24e42c7d1234e1dace' +const HEAD_LEAF_JSX_SHA256 = '15ea1af8ae1a28a4a5f2ea31c2aac908fc24a0fe68923733889f1d40d0c8e3a3' const HEAD_STYLE_REFERENCE_SHA256 = '295a3501c2c6d7bea7c8bbf38b3f3534f01344cd7e1b91bb8e07c040821d596a' const HEAD_IDENTITY_FIELD_SHA256 = - '2084e23285fc128e02ffbf751092ffc834b2d257c5c4bd782769f0bf97d2f161' + '89bdd544369e41829581e80d320112019f3127fd3529c810d9651e5ece37338b' const HEAD_NAVIGATION_SHA256 = '9d96f5dad7de555d6553eac39c0fab00efad507470fd562cb9beaa32db16f512' const HEAD_CAPABILITY_SHA256 = 'ca219f7909a091717110b823d5b94a20770ad3ae51894e0fa765e8628309392d' @@ -529,9 +529,7 @@ describe('mobile session route extraction parity', () => { ) expect(hash(native.cleanups)).toBe(HEAD_TIMER_CLEANUP_SHA256) const compatibility = readCompatibilityFacts(definitions) - // 15, not main's 14: the seam moved two hook-side `deviceToken` fields into three - // adapter-side `client: { id }` builders. Every binding main pinned is still pinned. - expect(compatibility.identityFields).toHaveLength(15) + expect(compatibility.identityFields).toHaveLength(14) expect(hash(compatibility.identityFields)).toBe(HEAD_IDENTITY_FIELD_SHA256) expect(compatibility.navigation).toHaveLength(6) expect(hash(compatibility.navigation)).toBe(HEAD_NAVIGATION_SHA256) @@ -541,7 +539,7 @@ describe('mobile session route extraction parity', () => { it('preserves runtime strings, styles, and the expanded JSX tree', () => { const strings = readRuntimeStrings() - expect(strings).toHaveLength(593) + expect(strings).toHaveLength(591) expect(hash(strings)).toBe(HEAD_RUNTIME_STRING_SHA256) const jsx = readJsxFacts(readDefinitions()) expect(jsx.host).toHaveLength(124) diff --git a/mobile/src/session/native-host-session-quick-command-operations.ts b/mobile/src/session/native-host-session-quick-command-operations.ts index 0d9e51dc06d..3ea3bba970d 100644 --- a/mobile/src/session/native-host-session-quick-command-operations.ts +++ b/mobile/src/session/native-host-session-quick-command-operations.ts @@ -5,8 +5,6 @@ import type { HostSessionQuickCommandOperations, HostSessionQuickCommandSnapshot } from './host-session-quick-command-operations' -import { isFloatingWorkspaceWorktreeId } from './floating-workspace' -import { getRepoIdFromMobileWorktreeId } from './mobile-session-route-helpers' const LOAD_CUTOVER_MAX_RETRIES = 5 @@ -14,16 +12,16 @@ export function nativeHostSessionQuickCommandOperations( client: RpcClient ): HostSessionQuickCommandOperations { return { - async snapshot(workspaceId, signal) { + async snapshot(signal) { // The ok check sits outside the retry, as it did before this seam existed: a refusal // envelope is an answer, and replaying it would depend on the host's error text. const response = await loadWithCutoverRetry(client, signal) if (!response.ok) { throw new Error(response.error.message || 'Failed to load quick commands') } - return quickCommandSnapshot(response.result, workspaceId, 'Failed to load quick commands') + return quickCommandSnapshot(response.result, 'Failed to load quick commands') }, - async mutate(workspaceId, mutation) { + async mutate(mutation) { // Why no cutover retry here: a quick-command mutation is not idempotent, so a replay // after a logical cutover could apply the same edit twice. const response = await client.sendRequest('settings.updateTerminalQuickCommands', { @@ -32,7 +30,7 @@ export function nativeHostSessionQuickCommandOperations( if (!response.ok) { throw new Error(response.error.message || 'Failed to save quick command') } - return quickCommandSnapshot(response.result, workspaceId, 'Failed to save quick command') + return quickCommandSnapshot(response.result, 'Failed to save quick command') } } } @@ -55,7 +53,6 @@ async function loadWithCutoverRetry(client: RpcClient, signal?: AbortSignal) { function quickCommandSnapshot( result: unknown, - workspaceId: string, invalidResultMessage: string ): HostSessionQuickCommandSnapshot { const commands = parseNormalizedTerminalQuickCommands( @@ -64,12 +61,5 @@ function quickCommandSnapshot( if (!commands) { throw new Error(invalidResultMessage) } - return { - commands, - totalCount: commands.length, - repoId: - workspaceId.startsWith('folder:') || isFloatingWorkspaceWorktreeId(workspaceId) - ? null - : getRepoIdFromMobileWorktreeId(workspaceId) - } + return { commands } } diff --git a/mobile/src/session/native-host-session-terminal-operations.test.ts b/mobile/src/session/native-host-session-terminal-operations.test.ts index fd12d35cf77..f7e2dddeddb 100644 --- a/mobile/src/session/native-host-session-terminal-operations.test.ts +++ b/mobile/src/session/native-host-session-terminal-operations.test.ts @@ -3,47 +3,14 @@ import type { RpcClient } from '../transport/rpc-client' import { nativeHostSessionTerminalOperations } from './native-host-session-terminal-operations' describe('native host session terminal operations', () => { - it('preserves the existing mobile subscription and input RPC semantics', async () => { - const unsubscribe = vi.fn() - let onData: ((event: unknown) => void) | null = null - const subscribe = vi.fn((_method, _params, listener) => { - onData = listener - return unsubscribe - }) + it('preserves the existing mobile terminal input RPC semantics', async () => { const sendRequest = vi .fn() .mockResolvedValue({ ok: true, result: { send: { accepted: true } } }) const operations = nativeHostSessionTerminalOperations({ - subscribe, sendRequest } as unknown as RpcClient) - const onEvent = vi.fn() - const cleanup = operations.subscribe( - { - workspaceId: 'workspace-1', - terminalId: 'terminal-native-1', - clientId: 'device-1', - viewport: { cols: 90, rows: 30 }, - visible: true, - capabilities: { terminalBinaryStream: 1 } - }, - onEvent, - vi.fn() - ) - onData?.({ type: 'data', chunk: 'hello' }) - - expect(subscribe).toHaveBeenCalledWith( - 'terminal.subscribe', - { - terminal: 'terminal-native-1', - client: { id: 'device-1', type: 'mobile' }, - viewport: { cols: 90, rows: 30 }, - capabilities: { terminalBinaryStream: 1 } - }, - expect.any(Function) - ) - expect(onEvent).toHaveBeenCalledWith({ type: 'data', chunk: 'hello' }) await expect(operations.sendInput('terminal-native-1', 'ls', true, 'device-1')).resolves.toBe( true ) @@ -60,9 +27,7 @@ describe('native host session terminal operations', () => { await expect( operations.setDisplayMode('terminal-native-1', 'auto', { cols: 90, rows: 30 }, 'device-1') ).resolves.toBe(true) - await expect( - operations.rename('terminal-native-1', 'Build', 'workspace-native-1') - ).resolves.toBe(true) + await expect(operations.rename('terminal-native-1', 'Build')).resolves.toBe(true) await expect(operations.clear('terminal-native-1')).resolves.toBe(true) expect(sendRequest).toHaveBeenCalledWith('terminal.setDisplayMode', { terminal: 'terminal-native-1', @@ -77,8 +42,5 @@ describe('native host session terminal operations', () => { expect(sendRequest).toHaveBeenCalledWith('terminal.clearBuffer', { terminal: 'terminal-native-1' }) - - cleanup() - expect(unsubscribe).toHaveBeenCalledOnce() }) }) diff --git a/mobile/src/session/native-host-session-terminal-operations.ts b/mobile/src/session/native-host-session-terminal-operations.ts index 677a3861eaf..310c9a9141b 100644 --- a/mobile/src/session/native-host-session-terminal-operations.ts +++ b/mobile/src/session/native-host-session-terminal-operations.ts @@ -2,26 +2,11 @@ import { isTerminalSendRpcAccepted } from '../terminal/terminal-send-rpc-respons import { TERMINAL_INPUT_SEND_OPTIONS } from '../terminal/terminal-send-request' import type { RpcClient } from '../transport/rpc-client' import type { HostSessionTerminalOperations } from './host-session-terminal-operations' -import { subscribeMobileTerminalSafely } from './mobile-terminal-stream-subscribe' export function nativeHostSessionTerminalOperations( client: RpcClient ): HostSessionTerminalOperations { return { - subscribe(args, onEvent, onError) { - const unsubscribe = subscribeMobileTerminalSafely( - client, - { - terminal: args.terminalId, - ...(args.clientId ? { client: { id: args.clientId, type: 'mobile' as const } } : {}), - viewport: args.viewport, - capabilities: args.capabilities - }, - (event) => onEvent(event as Parameters[0]), - onError - ) - return unsubscribe - }, async sendInput(terminalId, text, enter, clientId) { return client .sendRequest( diff --git a/mobile/src/session/use-mobile-session-close-actions.ts b/mobile/src/session/use-mobile-session-close-actions.ts index 0af994b05ea..e3f50be3f5e 100644 --- a/mobile/src/session/use-mobile-session-close-actions.ts +++ b/mobile/src/session/use-mobile-session-close-actions.ts @@ -40,7 +40,7 @@ export function useMobileSessionCloseActions(scope: MobileSessionContentCreateAc try { const title = value.trim() - if (await sessionOperations.terminal.rename(target.handle, title, worktreeId)) { + if (await sessionOperations.terminal.rename(target.handle, title)) { setTerminals((prev) => { const next = prev.map((terminal) => terminal.handle === target.handle diff --git a/mobile/src/session/use-quick-commands.ts b/mobile/src/session/use-quick-commands.ts index fe422ab9da2..17f68a08c42 100644 --- a/mobile/src/session/use-quick-commands.ts +++ b/mobile/src/session/use-quick-commands.ts @@ -8,7 +8,6 @@ import type { HostSessionQuickCommandOperations } from './host-session-quick-com type Args = { operations: HostSessionQuickCommandOperations | null - workspaceId: string // Fetch only while the sheet is open — quick commands are settings data we // don't need to keep hydrated for every session screen. enabled: boolean @@ -31,14 +30,13 @@ type PendingMutation = { type MutationContext = { operations: HostSessionQuickCommandOperations - workspaceId: string confirmed: TerminalQuickCommand[] pending: PendingMutation[] queue: Promise nextMutationId: number } -export function useQuickCommands({ operations, workspaceId, enabled }: Args): QuickCommandsState { +export function useQuickCommands({ operations, enabled }: Args): QuickCommandsState { const [commands, setCommands] = useState([]) const [loading, setLoading] = useState(false) const [ready, setReady] = useState(false) @@ -53,11 +51,10 @@ export function useQuickCommands({ operations, workspaceId, enabled }: Args): Qu return } let mutationContext = mutationContextRef.current - if (mutationContext?.operations !== operations || mutationContext.workspaceId !== workspaceId) { + if (mutationContext?.operations !== operations) { // A request for an old host must not delay or update mutations on a new one. mutationContext = { operations, - workspaceId, confirmed: [], pending: [], queue: Promise.resolve(), @@ -89,7 +86,7 @@ export function useQuickCommands({ operations, workspaceId, enabled }: Args): Qu ) { return } - const snapshot = await operations.snapshot(workspaceId, abortController.signal) + const snapshot = await operations.snapshot(abortController.signal) if ( stale || operationId !== operationIdRef.current || @@ -124,20 +121,14 @@ export function useQuickCommands({ operations, workspaceId, enabled }: Args): Qu stale = true abortController.abort() } - }, [enabled, operations, workspaceId]) + }, [enabled, operations]) const persist = useCallback( async (commandMutation: TerminalQuickCommandMutation) => { // Why: the loaded list is the optimistic/rollback baseline; mutating // before it arrives would make failure recovery show invented state. const mutationContext = mutationContextRef.current - if ( - !operations || - loading || - !ready || - mutationContext?.operations !== operations || - mutationContext.workspaceId !== workspaceId - ) { + if (!operations || loading || !ready || mutationContext?.operations !== operations) { return false } const mutation: PendingMutation = { @@ -157,7 +148,7 @@ export function useQuickCommands({ operations, workspaceId, enabled }: Args): Qu try { // Why: an invalid success payload throws inside the adapter rather than // confirming [] — a later full-list mutation would erase live commands. - const snapshot = await operations.mutate(workspaceId, commandMutation) + const snapshot = await operations.mutate(commandMutation) mutationContext.confirmed = snapshot.commands succeeded = true return true @@ -191,7 +182,7 @@ export function useQuickCommands({ operations, workspaceId, enabled }: Args): Qu ) return await request }, - [loading, operations, ready, workspaceId] + [loading, operations, ready] ) return { commands, loading, ready, error, persist }