diff --git a/src/main/ipc/pty-hidden-delivery-gate.test.ts b/src/main/ipc/pty-hidden-delivery-gate.test.ts index e4358812d5b..5efb9899732 100644 --- a/src/main/ipc/pty-hidden-delivery-gate.test.ts +++ b/src/main/ipc/pty-hidden-delivery-gate.test.ts @@ -50,9 +50,9 @@ describe('pty hidden delivery gate', () => { expect(shouldSuppressHiddenRendererPtyView(PTY_ID, {})).toBe(true) setRendererPtyDeliveryInterest(PTY_ID, true) expect(shouldSuppressHiddenRendererPtyView(PTY_ID, {})).toBe(true) - expect( - shouldSuppressHiddenRendererPtyView(PTY_ID, { terminalHiddenDeliveryGate: false }) - ).toBe(false) + expect(shouldSuppressHiddenRendererPtyView(PTY_ID, { terminalHiddenDeliveryGate: false })).toBe( + false + ) }) it('routes a suppressed PTY sidecar-only exactly while interest is registered', () => { diff --git a/src/main/ipc/pty-hidden-delivery-gate.ts b/src/main/ipc/pty-hidden-delivery-gate.ts index 25f53d915e5..3c318b280da 100644 --- a/src/main/ipc/pty-hidden-delivery-gate.ts +++ b/src/main/ipc/pty-hidden-delivery-gate.ts @@ -97,9 +97,7 @@ export function shouldDropHiddenRendererPtyData( id: string, settings: HiddenPtyDeliveryGateSettings | null | undefined ): boolean { - return ( - shouldSuppressHiddenRendererPtyView(id, settings) && !deliveryInterestRendererPtys.has(id) - ) + return shouldSuppressHiddenRendererPtyView(id, settings) && !deliveryInterestRendererPtys.has(id) } /** Bytes a sidecar still needs while the view is suppressed: sent, but flagged diff --git a/src/preload/api-types.ts b/src/preload/api-types.ts index 24555ca239e..19922310fba 100644 --- a/src/preload/api-types.ts +++ b/src/preload/api-types.ts @@ -1112,6 +1112,18 @@ export type PluginMarketplaceHostInstallPreview = { blockedByKillList?: { reason: string; advisoryUrl?: string } } +export type PtyDataPayload = { + id: string + data: string + seq?: number + rawLength?: number + transformed?: boolean + background?: boolean + droppedOutput?: boolean + /** Hidden-gated bytes delivered to raw-byte sidecars without the view. */ + sidecarOnly?: boolean +} + export type PreloadApi = { app: AppApi orcaProfiles: { @@ -1618,20 +1630,7 @@ export type PreloadApi = { rendererDispatcherReadyForcedCount: number }> resetRendererDeliveryDebug: () => Promise - onData: ( - callback: (data: { - id: string - data: string - seq?: number - rawLength?: number - transformed?: boolean - background?: boolean - droppedOutput?: boolean - /** Hidden-gated bytes for raw-byte sidecars only; the view must not - * render or reply to them (main answered their queries). */ - sidecarOnly?: boolean - }) => void - ) => () => void + onData: (callback: (data: PtyDataPayload) => void) => () => void onReplay: (callback: (data: { id: string; data: string }) => void) => () => void /** Out-of-band main→renderer signal that renderer-bound bytes were * dropped (hidden-delivery gate / pending cap); the pane restores from diff --git a/src/preload/index.ts b/src/preload/index.ts index 394dcfac7eb..bf349006354 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -222,7 +222,8 @@ import type { PluginHostInstallSource, PluginHostListEntry, PluginHostLogLine, - PreloadApi + PreloadApi, + PtyDataPayload } from './api-types' import type { AgentKind, LaunchSource, RequestKind } from '../shared/telemetry-events' import { createBrowserFindSubscriptions } from './browser-find-subscriptions' @@ -1154,30 +1155,8 @@ const api = { getSize: (id: string): Promise<{ cols: number; rows: number } | null> => ipcRenderer.invoke('pty:getSize', { id }), - onData: ( - callback: (data: { - id: string - data: string - seq?: number - rawLength?: number - transformed?: boolean - background?: boolean - droppedOutput?: boolean - }) => void - ): (() => void) => { - const listener = ( - _event: Electron.IpcRendererEvent, - data: { - id: string - data: string - seq?: number - rawLength?: number - transformed?: boolean - background?: boolean - droppedOutput?: boolean - sidecarOnly?: boolean - } - ) => callback(data) + onData: (callback: (data: PtyDataPayload) => void): (() => void) => { + const listener = (_event: Electron.IpcRendererEvent, data: PtyDataPayload) => callback(data) ipcRenderer.on('pty:data', listener) return () => ipcRenderer.removeListener('pty:data', listener) }, diff --git a/src/preload/pty-snapshot-capability-ipc.test.ts b/src/preload/pty-snapshot-capability-ipc.test.ts index 5c639edcfb7..6760a61335a 100644 --- a/src/preload/pty-snapshot-capability-ipc.test.ts +++ b/src/preload/pty-snapshot-capability-ipc.test.ts @@ -69,4 +69,23 @@ describe('PTY snapshot capability preload IPC', () => { expect.anything() ) }) + + it('forwards the sidecar-only PTY data bit through the public preload API', async () => { + await import('./index') + const api = exposeInMainWorld.mock.calls.find(([name]) => name === 'api')?.[1] as PreloadApi + const callback = vi.fn() + api.pty.onData(callback) + const listener = on.mock.calls.find(([channel]) => channel === 'pty:data')?.[1] as ( + event: unknown, + payload: { id: string; data: string; sidecarOnly?: boolean } + ) => void + + listener({}, { id: 'pty-contract', data: 'raw', sidecarOnly: true }) + + expect(callback).toHaveBeenCalledWith({ + id: 'pty-contract', + data: 'raw', + sidecarOnly: true + }) + }) })