From 1db95e308a3460347f9bee39103ef762ffc844fc Mon Sep 17 00:00:00 2001 From: Jinwoo Hong <73622457+Jinwoo-H@users.noreply.github.com> Date: Wed, 21 Oct 2026 15:53:36 -0400 Subject: [PATCH] fix(terminal): stamp ORCA_WORKSPACE_ID on every terminal main creates, not only folder ones (#31608) The window stamps it (equal to ORCA_WORKTREE_ID) for every workspace kind and agent wrappers forward it; terminals started from the phone, the CLI or a server's own create in a git worktree lacked it. --- .../ipc/pty-launch-parity-host.test-cases.ts | 7 +++- src/main/ipc/pty-launch-parity.test.ts | 1 + ...nal-creation-and-readiness-part-03.spec.ts | 3 +- .../terminal-workspace-id-env.spec.ts | 41 +++++++++++++++++++ src/main/runtime/orca-runtime.test.ts | 1 + .../launch/terminal-workspace-scope.ts | 6 ++- 6 files changed, 54 insertions(+), 5 deletions(-) create mode 100644 src/main/runtime/orca-runtime-tests/terminal-workspace-id-env.spec.ts diff --git a/src/main/ipc/pty-launch-parity-host.test-cases.ts b/src/main/ipc/pty-launch-parity-host.test-cases.ts index 65545d6df75..2b20115f26e 100644 --- a/src/main/ipc/pty-launch-parity-host.test-cases.ts +++ b/src/main/ipc/pty-launch-parity-host.test-cases.ts @@ -193,7 +193,10 @@ export function expectedHostProvider(c: HostLaunchCase): Record env: {}, ...provider, ...(c.os === 'win32' ? { terminalWindowsWslDistro } : {}), - // main today: the host lane stamps ORCA_WORKSPACE_ID on folders only. - orcaEnv: c.workspace.kind === 'folder' ? [...FOLDER_ENV_KEYS, ...HOST_ENV_KEYS] : HOST_ENV_KEYS + // Like the window lane, the host lane stamps ORCA_WORKSPACE_ID for every workspace kind. + orcaEnv: + c.workspace.kind === 'folder' + ? [...FOLDER_ENV_KEYS, ...HOST_ENV_KEYS] + : ['ORCA_WORKSPACE_ID', ...HOST_ENV_KEYS] } } diff --git a/src/main/ipc/pty-launch-parity.test.ts b/src/main/ipc/pty-launch-parity.test.ts index f65f9cd4d60..3700c763696 100644 --- a/src/main/ipc/pty-launch-parity.test.ts +++ b/src/main/ipc/pty-launch-parity.test.ts @@ -278,6 +278,7 @@ describe('host lane: producer call -> runtime controller -> provider', () => { // The host stamps its own pane identity (the window lane's stale-key case is below). const spawned = recordOf(lanes.provider.mock.calls.at(-1)?.[0]) expect(recordOf(spawned.env).ORCA_PANE_KEY).toBe(spawned.paneKey) + expect(recordOf(spawned.env).ORCA_WORKSPACE_ID).toBe(launchWorkspaceId(c.workspace)) expect(trackMock.mock.calls).toEqual(c.telemetry ? [['agent_started', c.telemetry]] : []) expect(lanes.persistPtyBinding.mock.calls[0]?.[0]).toMatchObject({ placement: { kind: 'new-tab' } diff --git a/src/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-03.spec.ts b/src/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-03.spec.ts index 0139305406e..e4420cdf00d 100644 --- a/src/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-03.spec.ts +++ b/src/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-03.spec.ts @@ -328,7 +328,8 @@ describe('OrcaRuntimeService', () => { worktreeId: FLOATING_TERMINAL_WORKTREE_ID }) expect(spawnedPtyEnv(spawnCall).ORCA_WORKTREE_ID).toBe(FLOATING_TERMINAL_WORKTREE_ID) - expect(spawnCall?.env?.ORCA_WORKSPACE_ID).toBeUndefined() + // The window's floating panes carry the sentinel as their workspace id too. + expect(spawnCall?.env?.ORCA_WORKSPACE_ID).toBe(FLOATING_TERMINAL_WORKTREE_ID) expect(spawnCall?.env?.ORCA_PROJECT_GROUP_ID).toBeUndefined() expect(spawnCall?.env?.ORCA_WORKSPACE_ROOT).toBeUndefined() }) diff --git a/src/main/runtime/orca-runtime-tests/terminal-workspace-id-env.spec.ts b/src/main/runtime/orca-runtime-tests/terminal-workspace-id-env.spec.ts new file mode 100644 index 00000000000..e6768f75959 --- /dev/null +++ b/src/main/runtime/orca-runtime-tests/terminal-workspace-id-env.spec.ts @@ -0,0 +1,41 @@ +import { describe, expect, it, vi } from 'vitest' +import { OrcaRuntimeService } from '../orca-runtime-test-mocks.spec' +import { TEST_WORKTREE_ID, spawnedPtyEnv, store } from '../orca-runtime-test-fixtures.spec' + +function makeRuntime(): { runtime: OrcaRuntimeService; spawn: ReturnType } { + const spawn = vi.fn().mockResolvedValue({ id: 'pty-workspace-id' }) + const runtime = new OrcaRuntimeService(store) + runtime.setPtyController({ + spawn, + write: () => true, + kill: () => true, + getForegroundProcess: async () => null + }) + return { runtime, spawn } +} + +// Why: the window stamps ORCA_WORKSPACE_ID on every pane and agent wrappers forward it, so a +// terminal main creates (phone, CLI, or a remote server's own create) must carry the same value. +describe('OrcaRuntimeService terminal ORCA_WORKSPACE_ID', () => { + it('stamps the worktree id on a git worktree terminal, as the window does', async () => { + const { runtime, spawn } = makeRuntime() + + await runtime.createTerminal(`id:${TEST_WORKTREE_ID}`, { title: 'phone shell' }) + + const env = spawnedPtyEnv(spawn.mock.calls[0]?.[0]) + expect(env.ORCA_WORKSPACE_ID).toBe(TEST_WORKTREE_ID) + expect(env.ORCA_WORKSPACE_ID).toBe(env.ORCA_WORKTREE_ID) + }) + + // A window attached to a remote server sends its pane env with the create; the host's own + // workspace id wins, as it already does for folders. + it('replaces a client-sent value with the host workspace id', async () => { + const { runtime, spawn } = makeRuntime() + + await runtime.createTerminal(`id:${TEST_WORKTREE_ID}`, { + env: { ORCA_WORKSPACE_ID: 'stale-client-id' } + }) + + expect(spawnedPtyEnv(spawn.mock.calls[0]?.[0]).ORCA_WORKSPACE_ID).toBe(TEST_WORKTREE_ID) + }) +}) diff --git a/src/main/runtime/orca-runtime.test.ts b/src/main/runtime/orca-runtime.test.ts index e4ee41928dd..dfe144bed4e 100644 --- a/src/main/runtime/orca-runtime.test.ts +++ b/src/main/runtime/orca-runtime.test.ts @@ -46,6 +46,7 @@ await import('./orca-runtime-tests/agent-status-and-waits-part-03.spec') await import('./orca-runtime-tests/terminal-creation-and-readiness.spec') await import('./orca-runtime-tests/terminal-creation-and-readiness-part-02.spec') await import('./orca-runtime-tests/terminal-creation-and-readiness-part-03.spec') +await import('./orca-runtime-tests/terminal-workspace-id-env.spec') await import('./orca-runtime-tests/terminal-creation-and-readiness-part-04.spec') await import('./orca-runtime-tests/terminal-creation-and-readiness-part-05.spec') await import('./orca-runtime-tests/terminal-creation-and-readiness-part-06.spec') diff --git a/src/main/runtime/terminal/launch/terminal-workspace-scope.ts b/src/main/runtime/terminal/launch/terminal-workspace-scope.ts index 1dcd139f15e..0599b678803 100644 --- a/src/main/runtime/terminal/launch/terminal-workspace-scope.ts +++ b/src/main/runtime/terminal/launch/terminal-workspace-scope.ts @@ -198,17 +198,19 @@ export abstract class OrcaRuntimeWithResolveBrowserNetworkExecutionHostForWorktr delete cleanBaseEnv[key] } // Pane identity (ORCA_PANE_KEY/TAB_ID/WORKTREE_ID, jcode dir) is stamped by the spawn step from the spawn's ids. + // Why: the window stamps ORCA_WORKSPACE_ID (its ORCA_WORKTREE_ID) for every workspace kind, + // and agent wrappers forward it. const env = { ...cleanBaseEnv, ...agentTeamsEnv, - ...this.buildAgentHookPtyEnv?.() + ...this.buildAgentHookPtyEnv?.(), + ORCA_WORKSPACE_ID: scope.id } if (!scope.folderWorkspace) { return env } return { ...env, - ORCA_WORKSPACE_ID: scope.id, ORCA_PROJECT_GROUP_ID: scope.folderWorkspace.projectGroupId, ORCA_WORKSPACE_ROOT: scope.folderWorkspace.folderPath }