fix(terminal): judge an agent's --shell against the runtime it runs in (#28234)

* fix(terminal): judge an agent's --shell against the runtime it runs in

`orca terminal create --shell` for an agent is now judged only against the runtime that agent
runs in, not also against the project's saved runtime. So --shell wsl.exe for an agent in a
\\wsl$ repo (saved as Windows host, or with no setting) runs instead of being refused.

* chore(ci): re-run checks against current main

* chore(ci): re-run checks against current main

* chore(ci): re-run checks against current main

* chore(ci): re-run checks against current main

* chore(ci): re-run checks against current main
This commit is contained in:
Brennan Benson
2026-10-14 11:22:35 -07:00
committed by GitHub
parent 98dc5495d5
commit ff1285a936
2 changed files with 17 additions and 25 deletions
@@ -79,8 +79,8 @@ describe('host agent planner', () => {
})
})
// TEMPORARY parity: main's project runtime still refuses this, though the agent runs in WSL.
it('refuses --shell wsl.exe for an agent in a WSL-share repo set to Windows host', async () => {
// A `\\wsl$` cwd runs in WSL whatever the project says, so a WSL shell is what this agent gets.
it('applies --shell wsl.exe for an agent in a WSL-share repo set to Windows host', async () => {
const repo = { id: 'r', path: '\\\\wsl$\\Ubuntu\\r' }
const preference = { kind: 'windows-host' }
const store = {
@@ -95,9 +95,7 @@ describe('host agent planner', () => {
const opts = { startupAgent: 'claude', shellOverride: 'wsl.exe' } as const
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: as above.
const planHost = { ...host, store: store as never }
const plan = resolveAgentTerminalCreateOptions(share, opts, planHost)
await expect(plan).rejects.toThrow(
"This workspace's project runs its terminals on the Windows host"
)
const launch = await resolveAgentTerminalCreateOptions(share, opts, planHost)
expect(launch.launchRuntime?.pty.shellOverride).toBe('wsl.exe')
})
})
@@ -1,5 +1,4 @@
import type { SessionOptionValue } from '../../shared/native-chat-session-options'
import type { ProjectExecutionRuntimeResolution } from '../../shared/project-execution-runtime'
import type { TuiAgent } from '../../shared/tui-agent'
import type { AgentLaunchPreferences } from '../../shared/agent-session-host-authority'
import {
@@ -76,24 +75,19 @@ export async function resolveAgentTerminalCreateOptions(
const launchAgent = plannedAgent ?? opts.launchAgent
// Before any early return: a host that cannot apply the requested shell must refuse rather than
// spawn another one. Judged against the runtime this PTY's shell will come from.
const refuseShell = (projectRuntime: ProjectExecutionRuntimeResolution | undefined) =>
terminalShellOverrideRefusal({
shellOverride: opts.shellOverride,
connectionId: workspace.connectionId,
platform: process.platform,
projectRuntime,
cwd: resolveTerminalStartupCwd(workspace.path, opts.cwd) ?? workspace.path,
workspacePath: workspace.path
})
const judgedStore = opts.shellOverride ? store : undefined
// TEMPORARY parity: main's project-only runtime still refuses an agent, then the agent's own.
const shellRefusal =
refuseShell(resolveLocalProjectRuntimeForWorktreeId(judgedStore, workspace.id)) ??
(judgedStore && (plannedAgent || opts.launchRuntime)
? refuseShell(
agentLaunchProjectRuntime(judgedStore, workspace, plannedAgent ?? opts.launchAgent)
)
: null)
const shellRefusal = terminalShellOverrideRefusal({
shellOverride: opts.shellOverride,
connectionId: workspace.connectionId,
platform: process.platform,
projectRuntime:
opts.shellOverride && store
? plannedAgent || opts.launchRuntime
? agentLaunchProjectRuntime(store, workspace, plannedAgent ?? opts.launchAgent)
: resolveLocalProjectRuntimeForWorktreeId(store, workspace.id)
: undefined,
cwd: resolveTerminalStartupCwd(workspace.path, opts.cwd) ?? workspace.path,
workspacePath: workspace.path
})
if (shellRefusal) {
throw shellRefusal
}