refactor(window): organize clipboard, lifecycle, security, and relay owners (#41634)

* refactor(window): group clipboard, lifecycle, security, and relay owners

* refactor(window): keep Electron paste handling with desktop clipboard
This commit is contained in:
Neil
2026-11-12 11:47:00 -08:00
committed by GitHub
parent b3f8a5ac8d
commit e1d4314a46
190 changed files with 469 additions and 3265 deletions
+2 -2
View File
@@ -12,7 +12,7 @@ on:
- 'config/scripts/macos-shipit-scheduling*'
- 'config/electron-builder.config.cjs'
- 'src/main/startup/quit/main-process-quit*'
- 'src/main/window/main-window-state-lifecycle*'
- 'src/main/window/main-window/main-window-state-lifecycle*'
- 'src/main/window/dashboard-popout-window*'
- 'src/packages/process-host/**'
- 'src/main/runtime/agent-session/lifecycle/structured-agent-cli-writers*'
@@ -56,7 +56,7 @@ jobs:
src/main/updater/updater.headless-serve-install.test.ts
src/main/updater/mac-install/updater-mac-quit-guard.test.ts
src/main/startup/quit/main-process-quit-update-veto.test.ts
src/main/window/main-window-state-lifecycle.test.ts
src/main/window/main-window/main-window-state-lifecycle.test.ts
- name: Prove login-backed terminal writer census on the disposable runner
env:
ORCA_MACOS_LOGIN_CENSUS_CI: '1'
+3 -3
View File
@@ -20,7 +20,7 @@
1 test-no-in-test-import-actual:src/main/speech/openai-api-key-store.test.ts
5 test-no-in-test-import-actual:src/main/ssh/config/ssh-config-loader-regression.test.ts
2 test-no-in-test-import-actual:src/main/ssh/connection/ssh-connection-auth-fallback.test.ts
1 test-no-in-test-import-actual:src/main/window/dropped-path-preparation.test.ts
1 test-no-in-test-import-actual:src/main/window/file-drop/dropped-path-preparation.test.ts
2 test-no-in-test-import-actual:src/main/workspace-hooks/hooks-runner.test.ts
1 test-no-in-test-import-actual:src/main/workspaces/removal/local-worktree-removal-recovery.test.ts
1 test-no-in-test-import-actual:src/renderer/src/components/terminal-pane/pty-connection/notifications/pty-connection-hook-idle-arbitration.test.ts
@@ -201,7 +201,7 @@
2 test-no-real-time-wait:src/main/startup/gpu-recovery/gpu-lifecycle-install-dir-acl-guard.test.ts
3 test-no-real-time-wait:config/scripts/development/launch/run-electron-vite-dev.test.ts
1 test-no-real-time-wait:src/main/updater/lifecycle/serve-supervisor-install-park.test.ts
1 test-no-real-time-wait:src/main/window/main-window-updater-deferred-setup.test.ts
1 test-no-real-time-wait:src/main/updater/lifecycle/main-window-updater-deferred-setup.test.ts
3 test-no-real-time-wait:src/main/windows/windows-host-job.win32.test.ts
1 test-no-real-time-wait:src/main/windows/windows-process-tree-command-line-patch.test.ts
2 test-no-real-time-wait:src/main/windows/windows-pty-job.win32.test.ts
@@ -630,7 +630,7 @@
1 test-timeout-needs-reason:src/main/sqlite/sync-database-concurrent-backup.test.ts
2 test-timeout-needs-reason:src/main/ssh/connection/ssh-connection-sftp-wire.test.ts
2 test-timeout-needs-reason:src/main/ssh/system-transport/forwarding/system-ssh-forward-process-real-openssh.test.ts
1 test-timeout-needs-reason:src/main/window/renderer-publication-throttle.electron.test.ts
1 test-timeout-needs-reason:src/main/window/visibility/renderer-publication-throttle.electron.test.ts
1 test-timeout-needs-reason:src/main/windows/windows-administrator-restart.win32.test.ts
1 test-timeout-needs-reason:src/main/windows/windows-host-job.win32.test.ts
1 test-timeout-needs-reason:src/main/windows/windows-msys-job.win32.test.ts
File diff suppressed because one or more lines are too long
@@ -113,13 +113,13 @@ const NODE_RUNTIME_INCLUDE = [
'src/shared/workspace-session/workspace-session-salvage.test.ts',
'src/main/git/repositories/discovery/repo-detection.test.ts',
'src/renderer/src/components/browser-pane/annotate/browser-grab-toast-owner-cleanup.test.tsx',
'src/main/window/native-chat-paste-files.test.ts',
'src/main/native-chat/paste-files/native-chat-paste-files.test.ts',
'src/main/computer/desktop-script/runtime-host/desktop-script-serve-channel.test.ts',
'src/main/providers/local-pty/local-pty-child-process-verdict.test.ts',
'src/main/providers/working-directory-validation.test.ts',
'src/main/pty/process-retirement/posix-pty-process-groups.integration.test.ts',
'src/shared/ephemeral-vm-recipe-output-capture.test.ts',
'src/main/window/main-window-updater-deferred-setup.test.ts',
'src/main/updater/lifecycle/main-window-updater-deferred-setup.test.ts',
'src/renderer/src/components/terminal-pane/host-state/terminal-pane-host-state-memo.test.ts',
'src/main/native-chat/agent-session-journal/**/*.test.ts',
'src/main/native-chat/agent-session-wire/host/testing/structured-agent-session-terminal-writer-acquire.test.ts',
@@ -151,7 +151,7 @@ export const PR_E2E_SOURCE_ROUTES = [
specs: ['tests/e2e/file-explorer-copy-file-windows.spec.ts'],
matches: (file) =>
isProductSource(file) &&
/^src\/(?:main\/window\/clipboard-(?:file-copy|ipc-handlers)|renderer\/src\/components\/right-sidebar\/file-explorer\/panel\/file-explorer-row-file-transfer)\.ts$/.test(
/^src\/(?:main\/window\/clipboard\/clipboard-(?:file-copy|ipc-handlers)|renderer\/src\/components\/right-sidebar\/file-explorer\/panel\/file-explorer-row-file-transfer)\.ts$/.test(
file
)
},
@@ -123,7 +123,7 @@ const SPECIFIC_PLUGIN_ROUTES = [
'src\\/main\\/plugins\\/panels\\/(?:frame-serving\\/|guest-isolation\\/|desktop-actions\\/)?plugin-frame-',
'src\\/shared\\/plugins\\/frames\\/plugin-(?:frame-guest|panel-shell)',
'src\\/preload\\/plugin-frame-guest',
'src\\/main\\/window\\/(?:main-window-webview-security|attach-main-window-services|host-frame-permission-policy)',
'src\\/main\\/window\\/(?:security/(?:main-window-webview-security|host-frame-permission-policy)|attach-main-window-services)',
'src\\/main\\/ipc\\/(?:plugins|plugin-frame-entry-ipc)\\.ts$',
'src\\/main\\/startup\\/plugin-startup\\/main-process-plugins',
'config\\/build-plugins\\/main-build-entry-points',
+3 -3
View File
@@ -92,9 +92,9 @@
"../src/main/startup/serve-launch/serve-mode-argv.ts",
"../src/main/startup/profile-startup/single-instance-lock.ts",
"../src/main/startup/diagnostics/startup-diagnostics.ts",
"../src/main/window/focus-existing-window.ts",
"../src/main/window/foreground-activation-policy.ts",
"../src/main/window/macos-app-activation.ts"
"../src/main/window/activation/focus-existing-window.ts",
"../src/main/window/activation/foreground-activation-policy.ts",
"../src/main/window/activation/macos-app-activation.ts"
],
"compilerOptions": {
"paths": {
@@ -7,7 +7,7 @@ import { loadKnownUsageWorktreesByRepo } from '../usage/worktree-attribution/usa
import { isTelemetryEnabled, track } from '../telemetry/client'
import type { UsageWorktreeSource } from '../usage/worktree-attribution/usage-worktree-metadata'
import { currentWorkerEntryLayout } from '../worker-threads/worker-thread-entry-path'
import { isRendererDocumentNavigation } from '../window/renderer-document-navigation'
import { isRendererDocumentNavigation } from '../window/security/renderer-document-navigation'
export function trustsBackendRenderer(contents: WebContents): boolean {
if (
@@ -37,7 +37,7 @@ vi.mock('../worker-threads/worker-thread-entry-path', () => ({
currentWorkerEntryLayout: () => ({ moduleDir: '/' }),
resolveWorkerThreadEntryPath: () => 'backend-entry.js'
}))
vi.mock('../window/renderer-document-navigation', () => ({
vi.mock('../window/security/renderer-document-navigation', () => ({
isRendererDocumentNavigation: () => true
}))
vi.mock('../usage/worktree-attribution/usage-worktree-metadata', () => ({
@@ -2,7 +2,7 @@ import { app, BaseWindow, BrowserWindow, protocol, ipcMain, session } from 'elec
import { renameSync, writeFileSync } from 'node:fs'
import '../../../../shared/constants'
import { ORCA_BROWSER_PARTITION } from '../../../../shared/browser/browser-session-partition'
import { applyBackgroundActivationPolicy } from '../../../window/foreground-activation-policy'
import { applyBackgroundActivationPolicy } from '../../../window/activation/foreground-activation-policy'
import { browserManager } from '../../guest-lifecycle/manager/browser-manager'
import { browserSessionRegistry } from '../../sessions/profiles/browser-session-registry'
import { initializeBrowserExtensionSupport } from '../browser-extension-startup'
@@ -3,7 +3,7 @@ import { renameSync, writeFileSync } from 'node:fs'
import { createServer } from 'node:http'
import '../../../../shared/constants'
import { ORCA_BROWSER_PARTITION } from '../../../../shared/browser/browser-session-partition'
import { applyBackgroundActivationPolicy } from '../../../window/foreground-activation-policy'
import { applyBackgroundActivationPolicy } from '../../../window/activation/foreground-activation-policy'
import { browserManager } from '../../guest-lifecycle/manager/browser-manager'
import { browserSessionRegistry } from '../../sessions/profiles/browser-session-registry'
import { initializeBrowserExtensionSupport } from '../browser-extension-startup'
@@ -27,7 +27,7 @@ vi.mock('electron', async () => {
nativeImage: {}
}
})
vi.mock('../../../window/foreground-activation-policy', () => ({
vi.mock('../../../window/activation/foreground-activation-policy', () => ({
isBackgroundLaunch: () => mocks.background
}))
vi.mock('../api-runtime/browser-extension-api-host', () => ({ emitBrowserExtensionEvent: vi.fn() }))
@@ -5,7 +5,7 @@ import {
getChromeExtensionOrigin,
parseChromeExtensionUrl
} from '../../../../shared/browser/extensions/browser-extension-url'
import { isBackgroundLaunch } from '../../../window/foreground-activation-policy'
import { isBackgroundLaunch } from '../../../window/activation/foreground-activation-policy'
import { emitBrowserExtensionEvent } from '../api-runtime/browser-extension-api-host'
import { resolveInsideExtensionDirectory } from '../installation/browser-extension-manifest'
import {
@@ -3,7 +3,7 @@ import { existsSync, mkdirSync, renameSync, writeFileSync } from 'node:fs'
import { join } from 'node:path'
import '../../../../shared/constants'
import { ORCA_BROWSER_PARTITION } from '../../../../shared/browser/browser-session-partition'
import { applyBackgroundActivationPolicy } from '../../../window/foreground-activation-policy'
import { applyBackgroundActivationPolicy } from '../../../window/activation/foreground-activation-policy'
import { browserSessionRegistry } from '../../sessions/profiles/browser-session-registry'
import { initializeBrowserExtensionSupport } from '../browser-extension-startup'
import { BROWSER_EXTENSION_API_CUSTOM_SCHEME } from '../api-runtime/browser-extension-api-scheme'
@@ -2,7 +2,7 @@ import { app, BaseWindow, protocol, session } from 'electron'
import { renameSync, writeFileSync } from 'node:fs'
import '../../../../shared/constants'
import { ORCA_BROWSER_PARTITION } from '../../../../shared/browser/browser-session-partition'
import { applyBackgroundActivationPolicy } from '../../../window/foreground-activation-policy'
import { applyBackgroundActivationPolicy } from '../../../window/activation/foreground-activation-policy'
import { browserSessionRegistry } from '../../sessions/profiles/browser-session-registry'
import { initializeBrowserExtensionSupport } from '../browser-extension-startup'
import { BROWSER_EXTENSION_API_CUSTOM_SCHEME } from '../api-runtime/browser-extension-api-scheme'
@@ -3,7 +3,7 @@ import { renameSync, writeFileSync } from 'node:fs'
import { createServer } from 'node:http'
import '../../../../shared/constants'
import { ORCA_BROWSER_PARTITION } from '../../../../shared/browser/browser-session-partition'
import { applyBackgroundActivationPolicy } from '../../../window/foreground-activation-policy'
import { applyBackgroundActivationPolicy } from '../../../window/activation/foreground-activation-policy'
import { browserManager } from '../../guest-lifecycle/manager/browser-manager'
import { browserSessionRegistry } from '../../sessions/profiles/browser-session-registry'
import { initializeBrowserExtensionSupport } from '../browser-extension-startup'
@@ -26,7 +26,7 @@ vi.mock('electron', async () => {
}
return { BrowserWindow: FakeBrowserWindow }
})
vi.mock('../../../window/foreground-activation-policy', () => ({
vi.mock('../../../window/activation/foreground-activation-policy', () => ({
isBackgroundLaunch: () => true,
showWindowWithoutStealingFocus: () => {}
}))
@@ -5,7 +5,7 @@ import { isSameExtensionNavigation } from '../../../../shared/browser/extensions
import {
isBackgroundLaunch,
showWindowWithoutStealingFocus
} from '../../../window/foreground-activation-policy'
} from '../../../window/activation/foreground-activation-policy'
import { emitBrowserExtensionEvent } from '../api-runtime/browser-extension-api-host'
import { watchBrowserMainWindowReadiness } from '../../guest-lifecycle/browser-main-window-readiness'
@@ -1,5 +1,5 @@
import { app, BrowserWindow } from 'electron'
import { applyBackgroundActivationPolicy } from '../../../window/foreground-activation-policy'
import { applyBackgroundActivationPolicy } from '../../../window/activation/foreground-activation-policy'
if (process.env.ORCA_BACKGROUND_LAUNCH !== '1') {
throw new Error('Find traversal fixture requires background launch')
@@ -5,7 +5,7 @@ import {
redactKagiSessionToken
} from '../../../../shared/browser-url'
import type { BrowserContextMenuRequestedEvent } from '../../../../shared/browser/guest-lifecycle/browser-guest-events'
import { readContextMenuSpelling } from '../../../window/editable-context-menu'
import { readContextMenuSpelling } from '../../../window/editing/editable-context-menu'
import { readGuestNavigationState } from '../navigation/browser-guest-navigation-state'
import type { ResolveRenderer } from '../browser-guest-renderer-target'
// oxlint-disable-next-line import/no-cycle -- browser subsystem singleton hub (browserManager); breaking it needs DI and test-mock updates, left for a human
@@ -40,7 +40,7 @@ vi.mock('../popups/presentation/popup-origin-bar-window', () => ({
}))
import { browserManager } from './browser-manager'
import { rendererPublicationThrottle } from '../../../window/renderer-publication-throttle'
import {
guestNavigationMethods,
rendererWebContentsId,
@@ -60,28 +60,6 @@ describe('browserManager', () => {
vi.useRealTimers()
})
it('shares guest leases with a concurrent stream even without a desktop renderer', () => {
vi.useFakeTimers()
const guest = {
id: 1708,
isDestroyed: vi.fn(() => false),
setBackgroundThrottling: vi.fn(),
capturePage: vi.fn(async () => null)
}
webContentsFromIdMock.mockReturnValue(guest)
const releaseStream = rendererPublicationThrottle.acquire(guest)
const releaseCapture = browserManager.holdPaintForCapture(guest.id)
releaseStream()
vi.runAllTimers()
expect(guest.setBackgroundThrottling.mock.calls).toEqual([[false]])
releaseCapture()
releaseCapture()
vi.runAllTimers()
expect(guest.setBackgroundThrottling.mock.calls).toEqual([[false], [true]])
})
it.each<string>(['acquire'])('cleans capture ownership after renderer %s fails', (failure) => {
vi.useFakeTimers()
const guest = {
@@ -139,70 +117,4 @@ describe('browserManager', () => {
vi.runAllTimers()
expect(guest.setBackgroundThrottling.mock.calls).toEqual([[false], [true], [false], [true]])
})
it('does not let a destroyed guest and renderer release a replacement page hold', () => {
vi.useFakeTimers()
const makeGuest = (id: number) => ({
...guestNavigationMethods(),
id,
isDestroyed: vi.fn(() => false),
isCurrentlyAudible: vi.fn(() => false),
getType: vi.fn(() => 'webview'),
setBackgroundThrottling: vi.fn(),
capturePage: vi.fn(async () => null),
setWindowOpenHandler: vi.fn(),
on: vi.fn(),
off: vi.fn(),
openDevTools: vi.fn()
})
const makeRenderer = (id: number) => ({
id,
isDestroyed: vi.fn(() => false),
send: vi.fn(),
setBackgroundThrottling: vi.fn(),
capturePage: vi.fn(async () => null)
})
const oldGuest = makeGuest(1709)
const newGuest = makeGuest(1710)
const oldRenderer = makeRenderer(rendererWebContentsId)
const newRenderer = makeRenderer(rendererWebContentsId + 1)
webContentsFromIdMock.mockImplementation((id: number) =>
[oldGuest, newGuest, oldRenderer, newRenderer].find((contents) => contents.id === id)
)
const register = (guest: ReturnType<typeof makeGuest>, rendererId: number) => {
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: this guest provides the policy and registration members exercised here.
browserManager.attachGuestPolicies(guest as never)
browserManager.registerGuest({
browserPageId: 'page-replaced',
webContentsId: guest.id,
rendererWebContentsId: rendererId
})
}
register(oldGuest, oldRenderer.id)
const releaseOld = browserManager.holdPaintForCapture(oldGuest.id)
oldGuest.isDestroyed.mockReturnValue(true)
oldRenderer.isDestroyed.mockReturnValue(true)
register(newGuest, newRenderer.id)
newRenderer.send.mockClear()
const releaseNew = browserManager.holdPaintForCapture(newGuest.id)
releaseOld()
vi.runAllTimers()
expect(newRenderer.send.mock.calls).toEqual([
['browser:capturePaintHold', { browserPageId: 'page-replaced', held: true }]
])
expect(oldGuest.setBackgroundThrottling.mock.calls).toEqual([[false]])
expect(oldRenderer.setBackgroundThrottling.mock.calls).toEqual([[false]])
expect(newGuest.setBackgroundThrottling.mock.calls).toEqual([[false]])
expect(newRenderer.setBackgroundThrottling.mock.calls).toEqual([[false]])
releaseNew()
vi.runAllTimers()
expect(newRenderer.send.mock.calls.at(-1)).toEqual([
'browser:capturePaintHold',
{ browserPageId: 'page-replaced', held: false }
])
expect(newGuest.setBackgroundThrottling.mock.calls).toEqual([[false], [true]])
expect(newRenderer.setBackgroundThrottling.mock.calls).toEqual([[false], [true]])
})
})
@@ -1,5 +1,5 @@
import { webContents } from 'electron'
import { rendererPublicationThrottle } from '../../../window/renderer-publication-throttle'
import { rendererPublicationThrottle } from '../../../window/visibility/renderer-publication-throttle'
import { BrowserManagerState } from './browser-manager-state'
import {
focusGuardUnavailableError,
@@ -1,6 +1,6 @@
import { app, BrowserWindow, ipcMain, session, webContents } from 'electron'
import { join } from 'node:path'
import { applyBackgroundActivationPolicy } from '../../../window/foreground-activation-policy'
import { applyBackgroundActivationPolicy } from '../../../window/activation/foreground-activation-policy'
if (process.env.ORCA_BACKGROUND_LAUNCH !== '1') {
throw new Error('Popup identity probes require a background launch')
@@ -1,5 +1,5 @@
import { BrowserWindow, ipcMain, type WebContents } from 'electron'
import { isResourceWindowVisible } from '../../../window/resource-view-admission'
import { isResourceWindowVisible } from '../../../window/visibility/resource-view-admission'
import { sendGuestCdpCommand } from '../../cdp/debugger/guest-cdp-command'
import { isAgentRpcCaller } from '../../../runtime/rpc/callers/rpc-caller-identity'
import {
@@ -9,7 +9,7 @@ import {
} from 'electron'
import { writeFileSync } from 'node:fs'
import { renameFileWithWindowsRetry } from '../../../../../shared/windows-retry-file-operations'
import { applyBackgroundActivationPolicy } from '../../../../window/foreground-activation-policy'
import { applyBackgroundActivationPolicy } from '../../../../window/activation/foreground-activation-policy'
import { BrowserPopupCompatibilityManager } from './browser-popup-compatibility-manager-fixture'
import { BrowserRouteWebContentsRegistry } from '../../../routing/guest-authority/browser-route-webcontents-registry'
import { initializeBrowserProcessUserAgent } from '../../../identity/browser-process-user-agent'
@@ -3,7 +3,7 @@ import type { BrowserWindow, WebContents } from 'electron'
import {
isBackgroundLaunch,
showWhenAppActive
} from '../../../../window/foreground-activation-policy'
} from '../../../../window/activation/foreground-activation-policy'
import type { PopupChildWindowOptions, PopupOriginBarWindow } from './browser-popup-window-contract'
import {
isNativeBrowserPopupWindow,
@@ -3,7 +3,7 @@ import {
isBackgroundLaunch,
revealWindowsAwaitingAppActive,
showWhenAppActive
} from '../../../../window/foreground-activation-policy'
} from '../../../../window/activation/foreground-activation-policy'
import {
discardBrowserPopupContents,
prepareNativeBrowserPopup
@@ -3,7 +3,7 @@ import { mkdirSync, writeFileSync } from 'node:fs'
import { join } from 'node:path'
import { openPopupWithOriginBar } from './popup-origin-bar-window'
import { prepareNativeBrowserPopup } from './browser-popup-native-window'
import { applyBackgroundActivationPolicy } from '../../../../window/foreground-activation-policy'
import { applyBackgroundActivationPolicy } from '../../../../window/activation/foreground-activation-policy'
if (process.env.ORCA_BACKGROUND_LAUNCH !== '1') {
throw new Error('Popup identity probes require a background launch')
@@ -1,6 +1,6 @@
import type { Dir } from 'node:fs'
import { isSafeOwnedDirectory } from '../../shared/owned-private-directory'
export { isSafeOwnedDirectory } from '../../shared/owned-private-directory'
import { lstat, mkdir, opendir } from 'node:fs/promises'
import { dirname, join, resolve } from 'node:path'
import { removeTree } from '../../shared/windows-transient-lock-removal'
@@ -1,4 +1,4 @@
import { isResourceWindowVisible } from '../window/resource-view-admission'
import { isResourceWindowVisible } from '../window/visibility/resource-view-admission'
import { BrowserWindow, ipcMain, Notification, powerMonitor } from 'electron'
import { readDesktopAwayState } from '../notifications/desktop-away-state'
import type { RuntimeDesktopSurface } from '../runtime/construction/runtime-desktop-surface'
+9 -3
View File
@@ -1,8 +1,14 @@
import { app, clipboard, dialog, type BrowserWindow } from 'electron'
import { isRoutedOrcaDeepLink } from './startup/open-routing/orca-deep-link-url'
import { createMacAppActivationHandler } from './window/macos-app-activation'
import { isBackgroundLaunch, isOrcadSidecarLaunch } from './window/foreground-activation-policy'
import { createDesktopHandoff, desktopHandoffArguments } from './window/macos-desktop-handoff'
import { createMacAppActivationHandler } from './window/activation/macos-app-activation'
import {
isBackgroundLaunch,
isOrcadSidecarLaunch
} from './window/activation/foreground-activation-policy'
import {
createDesktopHandoff,
desktopHandoffArguments
} from './window/activation/macos-desktop-handoff'
import {
focusExistingWindow as focusExistingWindowAction,
setMainWindowOpener
+1 -1
View File
@@ -9,7 +9,7 @@ import type { AppIdentity } from '../../shared/app-identity'
import type { MarkdownDocument } from '../../shared/file-listing/file-document'
import type { FloatingTerminalCwdRequest } from '../../shared/ui-chrome-types'
import { relaunchApp, runBeforeRelaunchCleanup } from '../startup/quit/app-relaunch'
import { setWindowMaterialSuppressed } from '../window/window-material-suppression'
import { setWindowMaterialSuppressed } from '../window/presentation/window-material-suppression'
import { isTrustedUIRenderer } from './ui'
import { prepareWindowsAdministratorRestart } from '../windows/windows-administrator-restart'
import { quitProcess } from '../startup/quit/process-quit-request'
+2 -2
View File
@@ -9,8 +9,8 @@ import {
isDashboardPopoutRenderer,
onDashboardPopoutOpenChanged
} from '../window/dashboard-popout-window'
import { safelyRevealWindow } from '../window/focus-existing-window'
import { isBackgroundLaunch } from '../window/foreground-activation-policy'
import { safelyRevealWindow } from '../window/activation/focus-existing-window'
import { isBackgroundLaunch } from '../window/activation/foreground-activation-policy'
import { getTrustedUIRendererWindow, isTrustedUIRenderer, sendToTrustedUIRenderer } from './ui'
import {
admitDashboardSnapshot,
+1 -1
View File
@@ -6,7 +6,7 @@ import { collectMemorySnapshot } from '../memory/collector'
import {
assertResourceViewVisible,
isResourceWindowVisible
} from '../window/resource-view-admission'
} from '../window/visibility/resource-view-admission'
import type { ResourceViewAdmission } from '../../shared/resource-view-admission'
export function registerMemoryHandlers(store: Store): void {
+2 -2
View File
@@ -4,8 +4,8 @@ import type {
NotificationDispatchResult,
NotificationSettings
} from '../../shared/notification-settings-types'
import { safelyRevealWindow } from '../window/focus-existing-window'
import { isBackgroundLaunch } from '../window/foreground-activation-policy'
import { safelyRevealWindow } from '../window/activation/focus-existing-window'
import { isBackgroundLaunch } from '../window/activation/foreground-activation-policy'
import { getRepoIdFromWorktreeId } from '../../shared/worktree/id'
import { parsePaneKey } from '../../shared/workspace-layout/stable-pane-id'
import type { buildNotificationOptions } from './notification-options'
+1 -1
View File
@@ -14,7 +14,7 @@ import { isNotificationAttentionAcknowledgement } from '../../shared/notificatio
import type { OrcaRuntimeService } from '../runtime/orca-runtime'
import { readNotificationAuthorizationStatus } from './notification-authorization-status'
import { setTrayAttention } from '../tray/system-tray'
import { isMainWindowVisible } from '../window/main-window-visibility'
import { isMainWindowVisible } from '../window/visibility/main-window-visibility'
import { activeNotificationsById } from './native-notification-lifecycle'
import { deliverNativeNotification } from './native-notification-delivery'
import { removeReadMacosNotifications } from './macos-notification-center'
+1 -1
View File
@@ -2,7 +2,7 @@ import { getPtyIpc } from '../pty-host-bindings'
import type { Store } from '../../../persistence/loading-store/store'
import type { OrcaRuntimeService } from '../../../runtime/orca-runtime'
import { rekeyMovedPaneOwners } from '../../../runtime/terminal/identity/moved-pane-owner-rekey'
import { commitRelayedPaneMove } from '../../../window/terminal-pane-move-commit-fence'
import { commitRelayedPaneMove } from '../../../window/renderer-relays/terminal-pane-move-commit-fence'
import {
isTerminalLeafMoveRequest,
type TerminalLeafMoveRequest,
+1 -1
View File
@@ -1,5 +1,5 @@
import type { ResourceViewAdmission } from '../../../../shared/resource-view-admission'
import { assertResourceViewVisible } from '../../../window/resource-view-admission'
import { assertResourceViewVisible } from '../../../window/visibility/resource-view-admission'
import { getPtyIpc } from '../pty-host-bindings'
import {
PtyProcessListAdmission,
@@ -78,11 +78,9 @@ import { registerMiniMaxCredentialsHandlers } from '../minimax-credentials'
import { registerZcodePlanCredentialsHandlers } from '../zcode-plan-credentials'
import { registerGrokAccountHandlers } from '../grok-accounts'
import { registerCursorAccountHandlers } from '../cursor-accounts'
import { registerUpdaterHandlers } from '../../window/attach-main-window-services'
import {
registerClipboardHandlers,
setTrustedClipboardRendererWebContentsId
} from '../../window/clipboard-ipc-handlers'
import { registerUpdaterHandlers } from '../../updater/lifecycle/main-window-updater'
import { registerClipboardHandlers } from '../../window/clipboard/clipboard-ipc-handlers'
import { setTrustedClipboardRendererWebContentsId } from '../../window/clipboard/clipboard-ipc-sender-trust'
import { isDashboardPopoutRenderer } from '../../window/dashboard-popout-window'
import type { RateLimitService } from '../../rate-limits/service/rate-limit-service'
import type { CodexAccountService } from '../../codex-accounts/service'
@@ -5,8 +5,8 @@ import {
getWorkspaceSpaceWindow,
isWorkspaceSpaceWindowRenderer
} from '../../window/workspace-space-window'
import { safelyRevealWindow } from '../../window/focus-existing-window'
import { isBackgroundLaunch } from '../../window/foreground-activation-policy'
import { safelyRevealWindow } from '../../window/activation/focus-existing-window'
import { isBackgroundLaunch } from '../../window/activation/foreground-activation-policy'
import { normalizeExecutionHostId } from '../../../shared/execution-host'
import { isWorkspaceSpaceChange } from '../../../shared/workspace-disk-space/workspace-space-change'
import type { Store } from '../../persistence/loading-store/store'
@@ -1,7 +1,7 @@
import {
isMainWindowVisible,
onMainWindowBecameVisible
} from '../../../window/main-window-visibility'
} from '../../../window/visibility/main-window-visibility'
import type {
WorktreeBaseRepoWatchConfig,
WorktreeBaseWatchTarget
+1 -1
View File
@@ -78,7 +78,7 @@ import { listRemoteOrcadRepositories } from '../../managed-server/migration/link
import { stageRemoteOrcadMigrationBlobChunk } from '../../managed-server/migration/transfers/orcad-migration-chunk-client'
import { listRemoteOrcadFolderCatalog } from '../../managed-server/migration/linking/orcad-server-folder-workspaces'
import { makeTerminalScrollbackSnapshotRef } from '../../pty/scrollback/terminal-scrollback-snapshots'
import { pauseWorkspacesForProjectMove } from '../../window/project-move-pause'
import { pauseWorkspacesForProjectMove } from '../../window/renderer-relays/project-move-pause'
import { getWslHostService } from '../../wsl/host-lifecycle/wsl-host-service-instance'
import { readWslGuestProcessTable } from '../../wsl/execution/wsl-guest-process-table'
import { isWslDistroRegistered } from '../../wsl/discovery/wsl-distro-registry-id'
@@ -7,7 +7,7 @@ import { renameFileWithWindowsRetryAsync } from '../../shared/windows-retry-file
import {
ensureOwnedTempStagingRoot,
getOwnedTempStagingRoot
} from '../window/owned-temp-staging-root'
} from '../filesystem/owned-temp-staging-root'
const claims = new Map<string, object>()
const publications = new Map<string, Promise<void>>()
@@ -2,11 +2,11 @@ import { mkdirSync, mkdtempSync, realpathSync, rmSync, symlinkSync, writeFileSyn
import { tmpdir } from 'node:os'
import path from 'node:path'
import { afterEach, beforeEach, describe, expect, it } from 'vitest'
import { installFakeAppEnvironment } from '../../../config/scripts/test-runtime/environment/vitest-host-ports-setup'
import { installFakeAppEnvironment } from '../../../../config/scripts/test-runtime/environment/vitest-host-ports-setup'
import type { Store } from '../persistence/loading-store/store'
import { resolveLocalFileRequestPath } from '../ipc/filesystem/access/local-file-access-resolution'
import { readLocalFileContent } from '../ipc/filesystem/filesystem-file-content-inspection'
import type { Store } from '../../persistence/loading-store/store'
import { resolveLocalFileRequestPath } from '../../ipc/filesystem/access/local-file-access-resolution'
import { readLocalFileContent } from '../../ipc/filesystem/filesystem-file-content-inspection'
import { restoreNativeChatPastes } from './native-chat-paste-files'
// A store with no projects: nothing but an access kind decides what a read may reach.
@@ -2,8 +2,8 @@
import { lstat, realpath, stat } from 'node:fs/promises'
import path from 'node:path'
import { getAppEnvironment } from '../../shared/app-environment'
import { NATIVE_CHAT_PASTE_FOLDER } from '../../shared/native-chat/input/native-chat-paste-folder'
import { getAppEnvironment } from '../../../shared/app-environment'
import { NATIVE_CHAT_PASTE_FOLDER } from '../../../shared/native-chat/input/native-chat-paste-folder'
const MAX_RESTORED_PASTES = 256
+1 -1
View File
@@ -6,7 +6,7 @@ import { z } from 'zod'
import { isSafeOwnedDirectory } from '../../shared/owned-private-directory'
import { isProcessAlive } from '../../shared/serve-bundle-coordination'
import { removeTree } from '../../shared/windows-transient-lock-removal'
import { isDirectChild } from '../window/owned-temp-staging-root'
import { isDirectChild } from '../filesystem/owned-temp-staging-root'
import { readRuntimeMetadataAsync } from '../runtime/rpc/server/runtime-metadata'
import { sendOrcadSidecarRequest } from './orcad-sidecar-runtime-client'
@@ -2,7 +2,7 @@ import { BrowserWindow, Menu, webContents as electronWebContents } from 'electro
import type { Session, WebContents } from 'electron'
import type { KeybindingOverrides } from '../../../../shared/keybindings'
import { setupGuestShortcutForwarding } from '../../../browser/guest-lifecycle/input/shortcuts/browser-guest-shortcut-forwarding'
import { buildEditableContextMenuTemplate } from '../../../window/editable-context-menu'
import { buildEditableContextMenuTemplate } from '../../../window/editing/editable-context-menu'
import { isAllowedPluginFrameNavigation } from './plugin-frame-request-policy'
/** `did-attach-webview`: per-guest fences the session cannot express, plus Orca's own input. */
@@ -1,6 +1,5 @@
/** An incomplete GC live set deletes owned history; regressions here caused the #14863 revert (#14975). */
import { describe, expect, it } from 'vitest'
import { folderWorkspaceKey } from '../../shared/workspace-scope'
import { getKnownWorktreeIdsForHistoryGc } from './history-gc-worktree-ids'
const noOtherProfiles = () => ({ ids: new Set<string>(), unreadableProfiles: 0 })
@@ -19,32 +18,8 @@ describe('getKnownWorktreeIdsForHistoryGc', () => {
})
// Folder PTYs mint history under folder:<id>; omitting their separate store collection falsely marks live history orphaned.
it('includes folder workspaces under their PTY workspace key', () => {
const live = getKnownWorktreeIdsForHistoryGc(
store(['repo-1::/path/wt'], ['fw-1', 'fw-2']),
noOtherProfiles
)
expect(live.has(folderWorkspaceKey('fw-1'))).toBe(true)
expect(live.has(folderWorkspaceKey('fw-2'))).toBe(true)
expect(live.size).toBe(3)
})
it('is empty only when there is genuinely nothing live', () => {
expect(getKnownWorktreeIdsForHistoryGc(store([], []), noOtherProfiles).size).toBe(0)
})
// History is not profile-scoped, so other profiles must remain in the live set.
it('includes worktrees owned by other profiles', () => {
const live = getKnownWorktreeIdsForHistoryGc(store(['repo-1::/b'], ['fw-b']), () => ({
ids: new Set(['repo-1::/a', folderWorkspaceKey('fw-a')]),
unreadableProfiles: 0
}))
expect(live).toEqual(
new Set(['repo-1::/b', folderWorkspaceKey('fw-b'), 'repo-1::/a', folderWorkspaceKey('fw-a')])
)
})
// Unreadable profiles make ownership unknown; empty means prune nothing, while partial IDs would delete live history.
it('reports nothing live when a profile could not be read', () => {
@@ -1,6 +1,6 @@
import type { Store } from '../persistence/loading-store/store'
import { folderWorkspaceKey } from '../../shared/workspace-scope'
import { getOtherProfileWorktreeIdsForHistoryGc } from '../orca-profiles/catalog/other-profile-workspace-catalog'
import type { Store } from '../../persistence/loading-store/store'
import { folderWorkspaceKey } from '../../../shared/workspace-scope'
import { getOtherProfileWorktreeIdsForHistoryGc } from '../../orca-profiles/catalog/other-profile-workspace-catalog'
/** Include git and folder:<id> PTY workspace keys; omitting folder workspaces makes their live shell history appear orphaned. */
export function getKnownWorktreeIdsForHistoryGc(
@@ -2,7 +2,7 @@ import { vi } from 'vitest'
import type { AgentBrowserBridge } from '../../../browser/agent-browser/agent-browser-bridge'
import type { RuntimeBrowserCommandHost } from '../commands/browser-command-contracts'
import { RuntimeBrowserPageRegistry } from './runtime-browser-page-registry'
import type { RendererPublicationThrottleTarget } from '../../../window/renderer-publication-throttle'
import type { RendererPublicationThrottleTarget } from '../../../window/visibility/renderer-publication-throttle'
// The part of the authoritative window a browser stream touches: its renderer's throttle lease target.
export type AuthoritativeWindowDouble = {
@@ -3,7 +3,7 @@ import { BrowserError } from '../../../browser/browser-error'
import { randomUUID } from 'node:crypto'
import { startBrowserScreencast } from '../../../browser/cdp/screencast/browser-screencast-stream'
import { browserManager } from '../../../browser/guest-lifecycle/manager/browser-manager'
import { rendererPublicationThrottle } from '../../../window/renderer-publication-throttle'
import { rendererPublicationThrottle } from '../../../window/visibility/renderer-publication-throttle'
import { mintBrowserScreencastSubscriptionId } from './browser-screencast-subscription-id'
import {
cancelScreencastSubscriberFrameRetry,
@@ -8,7 +8,7 @@ const { saveClipboardImageBufferAsTempFile } = vi.hoisted(() => ({
saveClipboardImageBufferAsTempFile: vi.fn()
}))
vi.mock('../../../../window/clipboard-image-temp-file', () => ({
vi.mock('../../../../window/clipboard/clipboard-image-temp-file', () => ({
saveClipboardImageBufferAsTempFile
}))
import '../../../../../shared/rpc-contract/clipboard-params'
@@ -1,5 +1,5 @@
import { defineMethod, type RpcContext } from '../../core'
import { saveClipboardImageBufferAsTempFile } from '../../../../window/clipboard-image-temp-file'
import { saveClipboardImageBufferAsTempFile } from '../../../../window/clipboard/clipboard-image-temp-file'
import { randomUUID } from 'node:crypto'
import { recordMobileClipboardImagePath } from '../../clipboard/mobile-clipboard-image-provenance'
import {
@@ -3,7 +3,7 @@ import { dialog, type BrowserWindow, type MessageBoxOptions } from 'electron'
import type { RuntimeRpcStartErrorClass } from '../../../../shared/telemetry-daemon-event-schemas'
import { translateMain } from '../../../i18n/main-i18n'
import { track } from '../../../telemetry/client'
import { waitForWindowToShow } from '../../../window/wait-for-window-to-show'
import { waitForWindowToShow } from '../../../window/visibility/wait-for-window-to-show'
const MAX_VISIBLE_CAUSE_LENGTH = 500
@@ -14,7 +14,7 @@ import {
} from './mobile-session-tab-close-outcome'
import { getRuntimeBrowserPageRegistry } from '../../browser/client-pages/runtime-browser-page-registry'
import type { RuntimeEdgeCommandSurface } from '../../construction/runtime-edge-command-controller'
import { rendererPublicationThrottle } from '../../../window/renderer-publication-throttle'
import { rendererPublicationThrottle } from '../../../window/visibility/renderer-publication-throttle'
import { retireHeadlessMobileSessionEditorTab } from '../../session-tabs/headless-editor-tabs'
import { LOCAL_EXECUTION_HOST_ID } from '../../../../shared/execution-host'
import { ORCA_LAYOUT_OWNER_IS_MAIN } from '../../../../shared/layout-owner-switch'
@@ -14,7 +14,7 @@ import { isWslUncPath } from '../../../../shared/wsl-paths'
import { isTuiAgent } from '../../../../shared/agent-launch/catalog/tui-agent-config'
import { applyRemoteViewerLaunchEnv } from './remote-viewer-launch-env'
import { deterministicAgentSessionUuid } from './runtime-agent-launch-resolution'
import { rendererPublicationThrottle } from '../../../window/renderer-publication-throttle'
import { rendererPublicationThrottle } from '../../../window/visibility/renderer-publication-throttle'
import { PROJECT_RUNTIME_REPAIR_REQUIRED_CODE } from '../../../../shared/project-runtime-repair-message'
import { OrchestrationError } from '../../orchestration/orchestration-error'
import { LOCAL_EXECUTION_HOST_ID } from '../../../../shared/execution-host'
@@ -12,7 +12,7 @@ import {
MOBILE_TERMINAL_SURFACE_TIMEOUT_MS
} from '../view/terminal-surface-deadlines'
import { isClientDisconnectedError } from '../../client/runtime-client-disconnect'
import { rendererPublicationThrottle } from '../../../window/renderer-publication-throttle'
import { rendererPublicationThrottle } from '../../../window/visibility/renderer-publication-throttle'
import { deterministicAgentSessionUuid } from './runtime-agent-launch-resolution'
import { assertMobileTerminalShellSupported } from './mobile-terminal-shell-refusal'
import { anchorTabOfCreate, mainPlacedAfter } from './mobile-terminal-create-anchor'
@@ -1,7 +1,7 @@
import { app, dialog } from 'electron'
import { prepareEarlyMainUiLanguage, translateMain } from '../i18n/main-i18n'
import { formatProfileStateStartupFailure } from '../persistence/profile-state/startup/profile-state-startup-failure'
import { isBackgroundLaunch } from '../window/foreground-activation-policy'
import { isBackgroundLaunch } from '../window/activation/foreground-activation-policy'
import { mainProcessState as state } from './main-process-state'
import { SINGLE_INSTANCE_ALREADY_RUNNING_EXIT_CODE } from '../../shared/single-instance-exit-code'
import { acquireDesktopProfileInstanceLock } from './profile-startup/desktop-profile-instance-lock'
@@ -1,5 +1,5 @@
import { app, session } from 'electron'
import { applyBackgroundActivationPolicy } from '../window/foreground-activation-policy'
import { applyBackgroundActivationPolicy } from '../window/activation/foreground-activation-policy'
import { applyElectronProxySettings } from '../network/proxy-settings'
import { installElectronProxyRequestGuard } from '../network/electron-proxy-request-guard'
import { handleElectronProxyLogin } from '../network/electron-proxy-credentials'
@@ -17,7 +17,7 @@ import {
sendOpenFeatureTour,
sendOpenSetupGuide
} from './main-window-actions'
import { ensureAutoUpdaterConfigured } from '../../window/main-window-updater'
import { ensureAutoUpdaterConfigured } from '../../updater/lifecycle/main-window-updater'
import { logStartupMilestone } from '../diagnostics/startup-diagnostics'
import { browserManager } from '../../browser/guest-lifecycle/manager/browser-manager'
@@ -40,10 +40,10 @@ vi.mock('../../tray/system-tray', () => ({
createSystemTray: vi.fn(),
setMacMenuBarIconVisible: vi.fn()
}))
vi.mock('../../window/main-window-updater', () => ({
vi.mock('../../updater/lifecycle/main-window-updater', () => ({
ensureAutoUpdaterConfigured: vi.fn()
}))
vi.mock('../../window/focus-existing-window', () => ({
vi.mock('../../window/activation/focus-existing-window', () => ({
focusExistingMainWindow: vi.fn(),
safelyRevealWindow: vi.fn()
}))
@@ -53,12 +53,14 @@ vi.mock('../windows-install-permissions/windows-install-dir-acl-recovery', () =>
describeInstallDirAclPoison: vi.fn(),
isBlockingInstallDirAclRepairInFlight: vi.fn()
}))
vi.mock('../../window/renderer-recovery-prompt', () => ({ presentRendererRecoveryPrompt: vi.fn() }))
vi.mock('../../window/renderer-launch-failure-probe', () => ({
vi.mock('../../window/renderer-recovery/renderer-recovery-prompt', () => ({
presentRendererRecoveryPrompt: vi.fn()
}))
vi.mock('../../window/renderer-recovery/renderer-launch-failure-probe', () => ({
probeRendererLaunchCapacity: vi.fn()
}))
import { safelyRevealWindow } from '../../window/focus-existing-window'
import { safelyRevealWindow } from '../../window/activation/focus-existing-window'
import {
consumeQuitAndStopAllSessionsRequest,
quitAfterRendererStoppedAllSessions,
@@ -20,13 +20,16 @@ import {
setMacMenuBarIconVisible,
type SystemTrayOptions
} from '../../tray/system-tray'
import { ensureAutoUpdaterConfigured } from '../../window/main-window-updater'
import { focusExistingMainWindow, safelyRevealWindow } from '../../window/focus-existing-window'
import { ensureAutoUpdaterConfigured } from '../../updater/lifecycle/main-window-updater'
import {
focusExistingMainWindow,
safelyRevealWindow
} from '../../window/activation/focus-existing-window'
import { mainProcessState as state } from '../main-process-state'
import { quitTeardownStartGate } from '../quit/quit-teardown-start-gate'
import { getDashboardPopoutWindow } from '../../window/dashboard-popout-window'
import { getWorkspaceSpaceWindow } from '../../window/workspace-space-window'
import { markUserQuitWindowClose } from '../../window/user-quit-window-close'
import { markUserQuitWindowClose } from '../../window/main-window/user-quit-window-close'
import { loadMainWindow } from '../../window/createMainWindow'
import {
describeInstallDirAclPoison,
@@ -35,8 +38,8 @@ import {
import {
presentRendererRecoveryPrompt,
type RendererRecoveryPromptFailure
} from '../../window/renderer-recovery-prompt'
import { probeRendererLaunchCapacity } from '../../window/renderer-launch-failure-probe'
} from '../../window/renderer-recovery/renderer-recovery-prompt'
import { probeRendererLaunchCapacity } from '../../window/renderer-recovery/renderer-launch-failure-probe'
import {
confirmQuitAndStopAllSessions,
explainStopAllSessionsUnavailable
@@ -15,7 +15,7 @@ import {
startWindowsInstallDirAclRepairIfPoisoned
} from '../windows-install-permissions/windows-install-dir-acl-recovery'
import { logStartupMilestone } from '../diagnostics/startup-diagnostics'
import { notifyMainWindowBecameVisible } from '../../window/main-window-visibility'
import { notifyMainWindowBecameVisible } from '../../window/visibility/main-window-visibility'
import { setTrayAttention } from '../../tray/system-tray'
import { createSystemTrayDeferred, showRendererRecoveryPrompt } from './main-window-actions'
import { attachMainWindowCoreServices } from './main-window-core-services'
@@ -37,14 +37,14 @@ import {
stopSyntheticTitleSpinnerTimer
} from './synthetic-title-runtime'
import { requireMainWindowServices } from './main-window-service-readiness'
import { recordRendererLaunchFailureProbe } from '../../window/renderer-launch-failure-probe'
import { recordRendererLaunchFailureProbe } from '../../window/renderer-recovery/renderer-launch-failure-probe'
import { trackBrowserExtensionMainWindow } from '../../browser/extensions/tabs-and-windows/browser-extension-window-model'
import { isResourceWindowVisible } from '../../window/resource-view-admission'
import { isResourceWindowVisible } from '../../window/visibility/resource-view-admission'
import {
applyBackgroundActivationPolicy,
applyServeDesktopWindowActivationPolicy
} from '../../window/foreground-activation-policy'
} from '../../window/activation/foreground-activation-policy'
const TRAY_CREATE_FALLBACK_MS = 12_000
const AGENT_STATE_CRASH_BREADCRUMB_MIN_INTERVAL_MS = 30_000
@@ -51,7 +51,7 @@ vi.mock('electron', () => ({
app: mocks,
dialog: { showMessageBox: mocks.showMessageBox }
}))
vi.mock('../../window/foreground-activation-policy', () => ({
vi.mock('../../window/activation/foreground-activation-policy', () => ({
applyBackgroundActivationPolicy: mocks.background
}))
vi.mock('node:fs', async (original) => {
@@ -14,7 +14,7 @@ import {
import { prepareEarlyMainUiLanguage, translateMain } from '../../i18n/main-i18n'
import { acquireProfileStateMaintenance } from '../../persistence/profile-state/access/profile-state-access'
import { rollbackProfileState } from '../../persistence/profile-state/recovery/profile-state-recovery-command'
import { applyBackgroundActivationPolicy } from '../../window/foreground-activation-policy'
import { applyBackgroundActivationPolicy } from '../../window/activation/foreground-activation-policy'
import { acquireSingleInstanceLock } from './single-instance-lock'
/** The process owning both locks performs recovery before Electron can initialize a runtime. */
@@ -2,8 +2,8 @@ import { app, dialog, type BrowserWindow, type MessageBoxOptions } from 'electro
import { translateMain } from '../../i18n/main-i18n'
import { profileStateWriterFailureOutcome } from '../../persistence/profile-state/writer/profile-state-writer-errors'
import { recordProfileStateWriteFailureReport } from '../../persistence/profile-state/writer/profile-state-writer-diagnostics'
import { isBackgroundLaunch } from '../../window/foreground-activation-policy'
import { waitForWindowToShow } from '../../window/wait-for-window-to-show'
import { isBackgroundLaunch } from '../../window/activation/foreground-activation-policy'
import { waitForWindowToShow } from '../../window/visibility/wait-for-window-to-show'
import { mainProcessState } from '../main-process-state'
let presenting = false
@@ -7,7 +7,7 @@ import {
} from '../../persistence/loading-store/user-data-path'
import { OrcaRuntimeRpcServer } from '../../runtime/rpc/server/runtime-rpc'
import { resolveServePinnedBindHost } from '../serve-launch/serve-pinned-bind-host'
import { isOrcadSidecarLaunch } from '../../window/foreground-activation-policy'
import { isOrcadSidecarLaunch } from '../../window/activation/foreground-activation-policy'
import { registerMobileHandlers } from '../../ipc/mobile'
import { createRuntimeEnvironmentMobileRelayHosts } from '../../ipc/runtime-environments/runtime-environment-mobile-relay-hosts'
import { getLocalPtyProvider } from '../../ipc/pty/provider/registry'
@@ -49,7 +49,7 @@ import {
} from '../cloud-startup/main-process-connector-startup'
import { mainProcessState as state } from '../main-process-state'
import { logStartupMilestone } from '../diagnostics/startup-diagnostics'
import { setupHeadlessServeAutoUpdater } from '../../window/main-window-updater'
import { setupHeadlessServeAutoUpdater } from '../../updater/lifecycle/main-window-updater'
import { preserveStateBeforeUpdateRestart } from '../../updater/update-restart-preservation'
import { resolveUpdateInstallMode } from '../../updater/updater-service'
import { scheduleAgentLaunchRecordWarmup } from '../agent-accounts/agent-launch-record-warmup'
@@ -1,8 +1,8 @@
import { EventEmitter } from 'node:events'
import type { BrowserWindow } from 'electron'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import type { Store } from '../persistence/loading-store/store'
import type { UpdaterSetupOptions } from '../updater/lifecycle/updater-setup'
import type { Store } from '../../persistence/loading-store/store'
import type { UpdaterSetupOptions } from './updater-setup'
const { setupMock, milestoneMock, getMainWindowMock } = vi.hoisted(() => ({
getMainWindowMock: vi.fn<() => BrowserWindow | null>(() => null),
@@ -11,15 +11,15 @@ const { setupMock, milestoneMock, getMainWindowMock } = vi.hoisted(() => ({
}))
vi.mock('electron', () => ({ app: {}, ipcMain: {} }))
vi.mock('../updater/updater-service', () => ({ setupAutoUpdater: setupMock }))
vi.mock('../updater/lifecycle/electron-updater-proxy-session', () => ({
vi.mock('../updater-service', () => ({ setupAutoUpdater: setupMock }))
vi.mock('./electron-updater-proxy-session', () => ({
applyProxyToElectronUpdaterSession: vi.fn()
}))
vi.mock('../ipc/ui', () => ({
vi.mock('../../ipc/ui', () => ({
isTrustedUIRenderer: () => true,
getTrustedUIRendererWindow: getMainWindowMock
}))
vi.mock('../startup/diagnostics/startup-diagnostics', () => ({
vi.mock('../../startup/diagnostics/startup-diagnostics', () => ({
logStartupMilestone: milestoneMock
}))
@@ -1,14 +1,17 @@
import { app, ipcMain } from 'electron'
import type { BrowserWindow, IpcMainInvokeEvent } from 'electron'
import type { ReleaseBuildListResult, UpdateCheckOptions } from '../../shared/update-status-types'
import { RELEASE_CHANNELS, type ReleaseChannel } from '../../shared/release-channel'
import { getTrustedUIRendererWindow, isTrustedUIRenderer } from '../ipc/ui'
import type { Store } from '../persistence/loading-store/store'
import { logStartupMilestone } from '../startup/diagnostics/startup-diagnostics'
import { applyProxyToElectronUpdaterSession } from '../updater/lifecycle/electron-updater-proxy-session'
import { updaterErrorMessage } from '../updater/lifecycle/updater-coded-error'
import { updaterCodedMessage } from '../../shared/updater-failure-copy'
import { normalizeUpdateDelayDays } from '../../shared/update-delay'
import type {
ReleaseBuildListResult,
UpdateCheckOptions
} from '../../../shared/update-status-types'
import { RELEASE_CHANNELS, type ReleaseChannel } from '../../../shared/release-channel'
import { getTrustedUIRendererWindow, isTrustedUIRenderer } from '../../ipc/ui'
import type { Store } from '../../persistence/loading-store/store'
import { logStartupMilestone } from '../../startup/diagnostics/startup-diagnostics'
import { applyProxyToElectronUpdaterSession } from './electron-updater-proxy-session'
import { updaterErrorMessage } from './updater-coded-error'
import { updaterCodedMessage } from '../../../shared/updater-failure-copy'
import { normalizeUpdateDelayDays } from '../../../shared/update-delay'
import {
checkForUpdatesFromMenu,
dismissAvailableUpdate,
@@ -20,12 +23,9 @@ import {
quitAndInstall,
setupAutoUpdater,
showLinuxPackage
} from '../updater/updater-service'
import type {
PreQuitCleanupFailureMode,
UpdateInstallMode
} from '../updater/lifecycle/updater-state'
import type { UpdaterSetupOptions } from '../updater/lifecycle/updater-setup'
} from '../updater-service'
import type { PreQuitCleanupFailureMode, UpdateInstallMode } from './updater-state'
import type { UpdaterSetupOptions } from './updater-setup'
const UPDATER_SETUP_FALLBACK_MS = 15_000
@@ -1,7 +1,7 @@
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'
import type { IpcMainInvokeEvent, WebContents } from 'electron'
import type { Store } from '../persistence/loading-store/store'
import type * as IpcUiModule from '../ipc/ui'
import type { Store } from '../../persistence/loading-store/store'
import type * as IpcUiModule from '../../ipc/ui'
const {
handleMock,
@@ -31,27 +31,27 @@ vi.mock('electron', () => ({
powerMonitor: { on: vi.fn(), off: vi.fn() }
}))
vi.mock('../ipc/repos', () => ({ registerRepoHandlers: vi.fn() }))
vi.mock('../ipc/worktrees', () => ({ registerWorktreeHandlers: vi.fn() }))
vi.mock('../ipc/worktrees/listing/worktree-change-invalidators', () => ({
vi.mock('../../ipc/repos', () => ({ registerRepoHandlers: vi.fn() }))
vi.mock('../../ipc/worktrees', () => ({ registerWorktreeHandlers: vi.fn() }))
vi.mock('../../ipc/worktrees/listing/worktree-change-invalidators', () => ({
runWorktreeChangeInvalidators: vi.fn()
}))
vi.mock('../ipc/pty/provider/registry', () => ({ getLocalPtyProvider: vi.fn() }))
vi.mock('../ipc/pty/register-handlers', () => ({ registerPtyHandlers: vi.fn() }))
vi.mock('../memory/hydrate-local-pty-registry', () => ({
vi.mock('../../ipc/pty/provider/registry', () => ({ getLocalPtyProvider: vi.fn() }))
vi.mock('../../ipc/pty/register-handlers', () => ({ registerPtyHandlers: vi.fn() }))
vi.mock('../../memory/hydrate-local-pty-registry', () => ({
hydrateLocalPtyRegistryAtBoot: vi.fn()
}))
vi.mock('../browser/guest-lifecycle/manager/browser-manager', () => ({
vi.mock('../../browser/guest-lifecycle/manager/browser-manager', () => ({
browserManager: { unregisterAll: vi.fn() }
}))
vi.mock('../macos-permissions/macos-tcc-prompt-notice', () => ({
vi.mock('../../macos-permissions/macos-tcc-prompt-notice', () => ({
acknowledgePendingTccPromptNotice: vi.fn(),
consumePendingTccPromptNotice: vi.fn(),
dismissTccPromptNotice: vi.fn(),
releasePendingTccPromptNotice: vi.fn()
}))
vi.mock('../updater/updater-service', () => ({
vi.mock('../updater-service', () => ({
checkForUpdates: vi.fn(),
checkForUpdatesFromMenu: vi.fn(),
downloadUpdate: vi.fn(),
@@ -65,7 +65,7 @@ vi.mock('../updater/updater-service', () => ({
}))
// Keep the seam mock for wiring proof but delegate to the real predicate for sender-authorization branches.
vi.mock('../ipc/ui', async (importOriginal) => ({
vi.mock('../../ipc/ui', async (importOriginal) => ({
...(await importOriginal<typeof IpcUiModule>()),
isTrustedUIRenderer: (sender: unknown) => isTrustedUIRendererMock(sender)
}))
@@ -120,7 +120,7 @@ function expectBothChannelsRejected(sender: Partial<WebContents>): void {
describe('updater linux package recovery IPC handlers', () => {
beforeAll(async () => {
// Read the real predicate using the same trusted-ID state these tests set.
actualUi = await vi.importActual<typeof IpcUiModule>('../ipc/ui')
actualUi = await vi.importActual<typeof IpcUiModule>('../../ipc/ui')
})
beforeEach(() => {
@@ -141,17 +141,6 @@ describe('updater linux package recovery IPC handlers', () => {
vi.unstubAllEnvs()
})
it('serves the current main UI renderer', async () => {
const event = senderEvent(webContents({}))
await expect(getHandler(RECOVERY_CHANNELS[0])(event)).resolves.toEqual({
ok: true,
command: "sudo apt install -- '<pkg>'",
packageFileName: 'p'
})
await expect(getHandler(RECOVERY_CHANNELS[1])(event)).resolves.toBeUndefined()
})
// Exercise distinct real authorization branches, not relabelled mock verdicts.
it.each([
['a guest webview', webContents({ getType: () => 'webview' })],
@@ -1,43 +1,9 @@
import type { BrowserWindow } from 'electron'
import { describe, expect, it, vi } from 'vitest'
vi.mock('electron', () => ({ app: { dock: { hide: vi.fn() }, setActivationPolicy: vi.fn() } }))
const {
applyBackgroundActivationPolicy,
applyServeDesktopWindowActivationPolicy,
showWindowWithoutStealingFocus
} = await import('./foreground-activation-policy')
function makeWindow(destroyed = false): BrowserWindow & {
show: ReturnType<typeof vi.fn>
showInactive: ReturnType<typeof vi.fn>
} {
return {
isDestroyed: () => destroyed,
show: vi.fn(() => {}),
showInactive: vi.fn(() => {})
} as unknown as BrowserWindow & {
show: ReturnType<typeof vi.fn>
showInactive: ReturnType<typeof vi.fn>
}
}
describe('showWindowWithoutStealingFocus', () => {
it('never reveals an explicitly background window', () => {
const window = makeWindow()
showWindowWithoutStealingFocus(window, { ORCA_BACKGROUND_LAUNCH: '1' })
expect(window.showInactive).not.toHaveBeenCalled()
expect(window.show).not.toHaveBeenCalled()
})
it('still reveals explicitly headful E2E without activation', () => {
const window = makeWindow()
showWindowWithoutStealingFocus(window, { ORCA_E2E_HEADFUL: '1' })
expect(window.showInactive).toHaveBeenCalledOnce()
expect(window.show).not.toHaveBeenCalled()
})
})
const { applyBackgroundActivationPolicy, applyServeDesktopWindowActivationPolicy } =
await import('./foreground-activation-policy')
describe('applyBackgroundActivationPolicy', () => {
function makeApp() {
@@ -36,23 +36,21 @@ import { openCodeHookService, openCode2HookService } from '../opencode/status-ho
import { listLiveDaemonPtyIds } from '../daemon/providers/lifecycle/daemon-provider-state'
import { hydrateLocalPtyRegistryAtBoot } from '../memory/hydrate-local-pty-registry'
import type { ClaudeRuntimeAuthPreparation } from '../claude-accounts/runtime-auth/runtime-auth-types'
import { getKnownWorktreeIdsForHistoryGc } from './history-gc-worktree-ids'
import { getKnownWorktreeIdsForHistoryGc } from '../pty/history/history-gc-worktree-ids'
import type { ClaudeAccountSelectionTarget } from '../claude-accounts/selection/runtime-selection'
import {
scheduleWorktreeBaseDirectoryWatcherSync,
setWorktreeBaseDirectoryWatcherSyncContext
} from '../ipc/worktrees/creation/worktree-base-directory-watcher'
import { startFolderRepoGitUpgradeWatch } from '../ipc/folder-repo-git-upgrade'
import { scheduleMainWindowAutoUpdaterSetup } from './main-window-updater'
import { scheduleMainWindowAutoUpdaterSetup } from '../updater/lifecycle/main-window-updater'
import { registerRuntimeWindowLifecycle } from './runtime-window-lifecycle'
import { ensureStructuredAgentSessionHostUnlessRefused } from '../runtime/agent-session/structured-agent-session-host-refusal'
import { getAgentSessionAttachmentStore } from '../native-chat/agent-session-attachments/agent-session-attachment-store-registry'
import { registerDroppedPathPreparation } from './dropped-path-preparation-ipc'
import { isPluginFramePermissionRequest } from './host-frame-permission-policy'
import { registerDroppedPathPreparation } from './file-drop/dropped-path-preparation-ipc'
import { isPluginFramePermissionRequest } from './security/host-frame-permission-policy'
import { installPluginDesktopFrameServices } from '../plugins/panels/desktop-actions/plugin-frame-desktop-services'
export { ensureAutoUpdaterConfigured, registerUpdaterHandlers } from './main-window-updater'
let appReloadHandlerTokenCounter = 0
let activeAppReloadHandlerToken: number | null = null
let tccPromptHandlerTokenCounter = 0
@@ -3,7 +3,7 @@ import { mkdtemp, readFile, rm } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { AgentSessionAttachmentStore } from '../native-chat/agent-session-attachments/agent-session-attachment-store'
import { AgentSessionAttachmentStore } from '../../native-chat/agent-session-attachments/agent-session-attachment-store'
const mocks = vi.hoisted(() => ({
handle: vi.fn(),
@@ -21,22 +21,20 @@ vi.mock('electron', () => ({
ipcMain: { removeHandler: vi.fn(), handle: mocks.handle },
nativeImage: { createFromBuffer: vi.fn() }
}))
vi.mock('../ipc/runtime-environments/runtime-environment-transport-routing', () => ({
vi.mock('../../ipc/runtime-environments/runtime-environment-transport-routing', () => ({
callRuntimeEnvironment: mocks.callRuntimeEnvironment
}))
vi.mock('../../shared/remote-runtime/host-store/runtime-environment-store', () => ({
vi.mock('../../../shared/remote-runtime/host-store/runtime-environment-store', () => ({
resolveEnvironment: (_userDataPath: string, selector: string) => ({ id: selector })
}))
vi.mock('./clipboard-remote-file-copy', () => ({
cleanupExpiredRemoteClipboardFiles: vi.fn(async () => {}),
scheduleLegacyRemoteClipboardFileCleanup: vi.fn()
}))
vi.mock('./dashboard-popout-window', () => ({ isDashboardPopoutRenderer: () => false }))
vi.mock('../dashboard-popout-window', () => ({ isDashboardPopoutRenderer: () => false }))
import {
registerClipboardHandlers,
setTrustedClipboardRendererWebContentsId
} from './clipboard-ipc-handlers'
import { registerClipboardHandlers } from './clipboard-ipc-handlers'
import { setTrustedClipboardRendererWebContentsId } from './clipboard-ipc-sender-trust'
function saveHandler(): (...args: unknown[]) => Promise<unknown> {
const entry = mocks.handle.mock.calls.find(
@@ -36,16 +36,14 @@ vi.mock('electron', () => ({
nativeImage: { createFromBuffer: vi.fn() }
}))
vi.mock('./dashboard-popout-window', () => ({ isDashboardPopoutRenderer }))
vi.mock('../dashboard-popout-window', () => ({ isDashboardPopoutRenderer }))
vi.mock('./clipboard-remote-file-copy', () => ({
cleanupExpiredRemoteClipboardFiles: vi.fn(async () => undefined),
scheduleLegacyRemoteClipboardFileCleanup: vi.fn()
}))
import {
registerClipboardHandlers,
setTrustedClipboardRendererWebContentsId
} from './clipboard-ipc-handlers'
import { registerClipboardHandlers } from './clipboard-ipc-handlers'
import { setTrustedClipboardRendererWebContentsId } from './clipboard-ipc-sender-trust'
const popoutEvent = {
sender: {
@@ -2,7 +2,7 @@ import { isAbsolute } from 'node:path'
import { pathToFileURL } from 'node:url'
import type { ProcessResult, ProcessSpec } from '@orca/process-host/process-spec'
import { windowsPowerShellPath } from '@orca/process-host/windows-system-binary'
import { withTimeout } from '../../shared/promise-timeout-fallback'
import { withTimeout } from '../../../shared/promise-timeout-fallback'
export type ClipboardFileResult = { ok: boolean; reason?: string }
@@ -1,5 +1,5 @@
import type { NativeImage } from 'electron'
import { encodeClipboardImageWithinLimit } from '../../shared/clipboard-image'
import { encodeClipboardImageWithinLimit } from '../../../shared/clipboard-image'
export function encodeClipboardNativeImagePng(
image: Pick<NativeImage, 'getSize' | 'resize' | 'toPNG'>
@@ -1,4 +1,4 @@
import { clipboardFormatsIncludeImage } from '../../shared/clipboard-image'
import { clipboardFormatsIncludeImage } from '../../../shared/clipboard-image'
import {
readWindowsCopiedImageFilePath,
type WindowsClipboardFileFormats
@@ -2,16 +2,16 @@ import fs from 'node:fs/promises'
import path from 'node:path'
import { randomUUID } from 'node:crypto'
import { SshHostNotServedError } from '../providers/ssh-host-not-served-error'
import { getAppEnvironment } from '../../shared/app-environment'
import { assertClipboardImageByteLengthWithinLimit } from '../../shared/clipboard-image'
import { SshHostNotServedError } from '../../providers/ssh-host-not-served-error'
import { getAppEnvironment } from '../../../shared/app-environment'
import { assertClipboardImageByteLengthWithinLimit } from '../../../shared/clipboard-image'
import {
agentSessionPastedImageName,
sanitizeAgentSessionAttachmentName,
type AgentSessionAttachmentClipboardTarget
} from '../../shared/agent-session/agent-session-attachments'
import { detectImageFileExtension } from '../../shared/image-file-signature'
import { nativeChatPasteFolder } from './native-chat-paste-files'
} from '../../../shared/agent-session/agent-session-attachments'
import { detectImageFileExtension } from '../../../shared/image-file-signature'
import { nativeChatPasteFolder } from '../../native-chat/paste-files/native-chat-paste-files'
export type SaveClipboardImageAsTempFileArgs = {
connectionId?: string | null
@@ -2,7 +2,7 @@ import {
clipboardImageThumbnailSize,
type ClipboardImageDimensions,
type ClipboardImageThumbnail
} from '../../shared/clipboard-image'
} from '../../../shared/clipboard-image'
/** Minimal NativeImage surface for tests without an Electron runtime. */
export type ClipboardImageLike = {
@@ -2,10 +2,10 @@ import { app, clipboard, ipcMain, nativeImage } from 'electron'
import { randomUUID } from 'node:crypto'
import { open, stat } from 'node:fs/promises'
import { runProcess } from '@orca/process-host'
import type { Store } from '../persistence/loading-store/store'
import { PATH_ACCESS_DENIED_MESSAGE } from '../ipc/filesystem/access/filesystem-auth'
import { resolveDesktopAuthorizedPath } from '../ipc/filesystem/access/local-file-access-resolution'
import { isENOENT } from '../ipc/filesystem/access/filesystem-path-containment'
import type { Store } from '../../persistence/loading-store/store'
import { PATH_ACCESS_DENIED_MESSAGE } from '../../ipc/filesystem/access/filesystem-auth'
import { resolveDesktopAuthorizedPath } from '../../ipc/filesystem/access/local-file-access-resolution'
import { isENOENT } from '../../ipc/filesystem/access/filesystem-path-containment'
import {
assertTrustedClipboardSender,
assertTrustedClipboardTextSender
@@ -14,8 +14,8 @@ import {
assertClipboardTextWriteWithinLimitWithYield,
assertClipboardTextWithinLimitWithYield,
type ReadClipboardTextOptions
} from '../../shared/clipboard-text'
import { clipboardHtmlMayHaveLinks } from '../../shared/clipboard-link-html'
} from '../../../shared/clipboard-text'
import { clipboardHtmlMayHaveLinks } from '../../../shared/clipboard-link-html'
import {
saveClipboardImageBufferAsTempFile,
type SaveClipboardImageAsTempFileArgs
@@ -25,19 +25,19 @@ import {
assertClipboardImageByteLengthWithinLimit,
assertClipboardImageDimensionsWithinLimit,
type ClipboardImageThumbnail
} from '../../shared/clipboard-image'
} from '../../../shared/clipboard-image'
import {
writeFileToClipboard,
type ClipboardFileDeps,
type ClipboardFileResult
} from './clipboard-file-copy'
import { SshHostNotServedError } from '../providers/ssh-host-not-served-error'
import { SshHostNotServedError } from '../../providers/ssh-host-not-served-error'
import {
cleanupExpiredRemoteClipboardFiles,
scheduleLegacyRemoteClipboardFileCleanup
} from './clipboard-remote-file-copy'
import { saveClipboardImageBufferInRuntime } from './clipboard-runtime-image-upload'
import { uploadPasteToAgentSessionAttachments } from '../ipc/agent-session-attachment-upload'
import { uploadPasteToAgentSessionAttachments } from '../../ipc/agent-session-attachment-upload'
import { readWindowsClipboardImageFileAsPng } from './clipboard-windows-image-file'
import { readClipboardImageSource } from './clipboard-image-source'
import { readClipboardCopiedFilePaths } from './clipboard-copied-file-paths'
@@ -50,12 +50,10 @@ import {
publishClipboardOperation,
publishClipboardText
} from './clipboard-text-publisher'
import { restoreNativeChatPastes } from './native-chat-paste-files'
import { restoreNativeChatPastes } from '../../native-chat/paste-files/native-chat-paste-files'
import { savePastedTextAsFile, type SavePastedTextAsFileArgs } from './native-chat-pasted-text-file'
import { agentSessionPastedImageName } from '../../shared/agent-session/agent-session-attachments'
import type { AgentSessionAttachmentStore } from '../native-chat/agent-session-attachments/agent-session-attachment-store'
export { setTrustedClipboardRendererWebContentsId } from './clipboard-ipc-sender-trust'
import { agentSessionPastedImageName } from '../../../shared/agent-session/agent-session-attachments'
import type { AgentSessionAttachmentStore } from '../../native-chat/agent-session-attachments/agent-session-attachment-store'
type ClipboardWriteFileRequest = {
filePath: string
@@ -1,6 +1,6 @@
import type { IpcMainInvokeEvent, WebContents } from 'electron'
import { isTrustedMainRendererSender } from '../ipc/main-renderer-sender-trust'
import { isDashboardPopoutRenderer } from './dashboard-popout-window'
import { isTrustedMainRendererSender } from '../../ipc/main-renderer-sender-trust'
import { isDashboardPopoutRenderer } from '../dashboard-popout-window'
let trustedClipboardRendererWebContentsId: number | null = null
@@ -1,7 +1,7 @@
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import type * as FsPromises from 'node:fs/promises'
import { installFakeAppEnvironment } from '../../../config/scripts/test-runtime/environment/vitest-host-ports-setup'
import '../../shared/clipboard-image'
import { installFakeAppEnvironment } from '../../../../config/scripts/test-runtime/environment/vitest-host-ports-setup'
import '../../../shared/clipboard-image'
const mocks = vi.hoisted(() => ({
handlers: new Map<string, (...args: unknown[]) => unknown>(),
@@ -32,22 +32,20 @@ vi.mock('node:fs/promises', async (importOriginal) => {
const fs = await importOriginal<typeof FsPromises>()
return { ...fs, default: { ...fs, writeFile: mocks.writeFile } }
})
vi.mock('../persistence/loading-store/store', () => ({ Store: vi.fn() }))
vi.mock('../../persistence/loading-store/store', () => ({ Store: vi.fn() }))
vi.mock('./clipboard-remote-file-copy', () => ({
cleanupExpiredRemoteClipboardFiles: vi.fn(),
scheduleLegacyRemoteClipboardFileCleanup: vi.fn()
}))
vi.mock('./native-chat-paste-files', () => ({
vi.mock('../../native-chat/paste-files/native-chat-paste-files', () => ({
restoreNativeChatPastes: vi.fn(),
nativeChatPasteFolder: () => '/tmp/native-chat-pastes'
}))
vi.mock('./dashboard-popout-window', () => ({ isDashboardPopoutRenderer: () => false }))
vi.mock('../dashboard-popout-window', () => ({ isDashboardPopoutRenderer: () => false }))
import { Store } from '../persistence/loading-store/store'
import {
registerClipboardHandlers,
setTrustedClipboardRendererWebContentsId
} from './clipboard-ipc-handlers'
import { Store } from '../../persistence/loading-store/store'
import { registerClipboardHandlers } from './clipboard-ipc-handlers'
import { setTrustedClipboardRendererWebContentsId } from './clipboard-ipc-sender-trust'
const event = {
sender: {
@@ -2,8 +2,8 @@ import { clipboard, nativeImage, type NativeImage } from 'electron'
import {
assertClipboardImageByteLengthWithinLimit,
assertClipboardImageDimensionsWithinLimit
} from '../../shared/clipboard-image'
import { readRasterImageDimensions } from '../../shared/file-preview/raster-image-dimensions'
} from '../../../shared/clipboard-image'
import { readRasterImageDimensions } from '../../../shared/file-preview/raster-image-dimensions'
export function readClipboardNativeImage(): NativeImage {
const image = clipboard.readImage()
@@ -5,10 +5,10 @@ import {
getOwnedTempStagingRoot,
isDirectChild,
isMissingPathError,
isSafeOwnedDirectory,
removeOwnedDirectory,
sweepExpiredOwnedDirectories
} from './owned-temp-staging-root'
} from '../../filesystem/owned-temp-staging-root'
import { isSafeOwnedDirectory } from '../../../shared/owned-private-directory'
const REMOTE_CLIPBOARD_STAGING_ROOT_NAME = 'orca-clipboard-files'
const REMOTE_CLIPBOARD_LEGACY_PREFIX = 'orca-clipboard-file-'
@@ -1,5 +1,5 @@
import { assertClipboardImageByteLengthWithinLimit } from '../../shared/clipboard-image'
import { callRuntimeEnvironment } from '../ipc/runtime-environments/runtime-environment-transport-routing'
import { assertClipboardImageByteLengthWithinLimit } from '../../../shared/clipboard-image'
import { callRuntimeEnvironment } from '../../ipc/runtime-environments/runtime-environment-transport-routing'
const CLIPBOARD_IMAGE_UPLOAD_CHUNK_BASE64_CHARS = 512 * 1024
const CLIPBOARD_IMAGE_SINGLE_FRAME_FALLBACK_BASE64_CHARS = 256 * 1024
@@ -2,10 +2,9 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'
import type { ProcessResult, ProcessSpec } from '@orca/process-host/process-spec'
import {
assertClipboardTextWriteWithinLimitWithYield,
CLIPBOARD_TEXT_MEASURE_YIELD_CODE_UNITS,
CLIPBOARD_TEXT_WRITE_MAX_BYTES
} from '../../shared/clipboard-text'
import { CLIPBOARD_IMAGE_MAX_BASE64_CHARS } from '../../shared/clipboard-image'
} from '../../../shared/clipboard-text'
import { CLIPBOARD_IMAGE_MAX_BASE64_CHARS } from '../../../shared/clipboard-image'
import { createClipboardPublisher } from './clipboard-text-publisher'
vi.mock('electron', () => ({ app: {}, clipboard: {} }))
@@ -34,38 +33,6 @@ describe('terminal clipboard writes', () => {
run.mockResolvedValue(ok)
})
it('publishes different and duplicate text without asking Electron to own the selection', async () => {
const copy = writer()
const text = '遥か\r\nline two\n'
await copy(text)
await copy(text)
expect(run).toHaveBeenCalledTimes(2)
expect(run).toHaveBeenCalledWith({
program: '/bundle/bin/orca-wayland-clipboard',
input: text,
timeoutMs: 2000,
stdio: ['pipe', 'ignore', 'ignore']
})
expect(write).not.toHaveBeenCalled()
expect(clearCache).toHaveBeenCalledTimes(2)
})
it('only discards a rejected Chromium source after the new owner is acknowledged', async () => {
let release: (value: typeof ok) => void = () => undefined
run.mockImplementationOnce(
() =>
new Promise((resolve) => {
release = resolve
})
)
const pending = writer()('copy')
await vi.waitFor(() => expect(run).toHaveBeenCalledTimes(1))
expect(clearCache).not.toHaveBeenCalled()
release(ok)
await pending
expect(clearCache).toHaveBeenCalledTimes(1)
})
it.each([
['timeout', { ...ok, code: null, timedOut: true }],
['helper failure', { ...ok, code: 70 }]
@@ -76,20 +43,6 @@ describe('terminal clipboard writes', () => {
expect(clearCache).not.toHaveBeenCalled()
})
it('preserves the existing clipboard when the bundled helper cannot start', async () => {
run.mockRejectedValueOnce(new Error('ENOENT'))
await expect(writer()('copy')).rejects.toThrow('ENOENT')
expect(write).not.toHaveBeenCalled()
expect(clearCache).not.toHaveBeenCalled()
})
it('retains verified Electron writes outside native Wayland', async () => {
await writer(false)('copy')
expect(write).toHaveBeenCalledWith('copy', 'terminal')
expect(run).not.toHaveBeenCalled()
expect(clearCache).not.toHaveBeenCalled()
})
it('rejects oversized text before either clipboard backend is invoked', async () => {
await expect(writer()('x'.repeat(CLIPBOARD_TEXT_WRITE_MAX_BYTES + 1))).rejects.toThrow(
'Clipboard text is too large'
@@ -98,60 +51,6 @@ describe('terminal clipboard writes', () => {
expect(write).not.toHaveBeenCalled()
})
it.each([false, true])(
'keeps a newer copy after yielding validation (Wayland=%s)',
async (wayland) => {
vi.useFakeTimers()
try {
const copy = writer(wayland)
const large = 'x'.repeat(CLIPBOARD_TEXT_MEASURE_YIELD_CODE_UNITS * 2)
const first = copy(large, 'terminal')
const second = copy('newer', 'ordinary')
await vi.runAllTimersAsync()
await Promise.all([first, second])
if (wayland) {
expect(run.mock.calls.map(([spec]) => spec.input === large)).toEqual([true, false])
expect(run.mock.calls[1]?.[0].input).toBe('newer')
expect(write).not.toHaveBeenCalled()
} else {
expect(write.mock.calls.map(([text, kind]) => [text === large, kind])).toEqual([
[true, 'terminal'],
[false, 'ordinary']
])
expect(write.mock.calls[1]?.[0]).toBe('newer')
expect(run).not.toHaveBeenCalled()
}
} finally {
vi.useRealTimers()
}
}
)
it('preserves request order across yielding validation and resumes after failure', async () => {
let release: (text: string) => void = () => undefined
const validate = vi.fn(async (text: string) => text)
validate.mockImplementationOnce(
() =>
new Promise((resolve) => {
release = resolve
})
)
run.mockRejectedValueOnce(new Error('helper failed'))
const copy = writer(true, validate)
const first = copy('first')
const firstRejected = expect(first).rejects.toThrow('helper failed')
const second = copy('second')
await vi.waitFor(() => expect(validate).toHaveBeenCalledTimes(1))
expect(run).not.toHaveBeenCalled()
release('first')
await firstRejected
await second
expect(run.mock.calls.map(([spec]) => spec.input)).toEqual(['first', 'second'])
expect(clearCache).toHaveBeenCalledTimes(1)
})
it.each([
{ label: 'count', count: 32, bytes: 1 },
{ label: 'retained bytes', count: 1, bytes: CLIPBOARD_IMAGE_MAX_BASE64_CHARS * 2 }
@@ -5,13 +5,13 @@ import {
assertClipboardTextWriteWithinLimitWithYield,
CLIPBOARD_TEXT_WRITE_MAX_BYTES,
CLIPBOARD_TEXT_WRITE_TOO_LARGE_ERROR
} from '../../shared/clipboard-text'
import { CLIPBOARD_IMAGE_MAX_BASE64_CHARS } from '../../shared/clipboard-image'
import { isLinuxWaylandSession } from '../../shared/linux-wayland-session'
} from '../../../shared/clipboard-text'
import { CLIPBOARD_IMAGE_MAX_BASE64_CHARS } from '../../../shared/clipboard-image'
import { isLinuxWaylandSession } from '../../../shared/linux-wayland-session'
import { writeClipboardTextAndVerify } from './clipboard-text-write-verify'
import { stageClipboardPublisher } from './clipboard-publisher-executable'
import { envWithoutAppImageRuntime } from '../pty/shell-startup/appimage-terminal-env'
import { isLegacyAppImageRuntimeIdentity } from '../updater/linux-package/linux-update-package-type'
import { envWithoutAppImageRuntime } from '../../pty/shell-startup/appimage-terminal-env'
import { isLegacyAppImageRuntimeIdentity } from '../../updater/linux-package/linux-update-package-type'
const WAYLAND_CLIPBOARD_WRITE_FAILED_ERROR = 'Wayland clipboard write failed'
const MAX_PENDING_CLIPBOARD_WRITES = 32

Some files were not shown because too many files have changed in this diff Show More