diff --git a/src/main/claude/hook-service.test.ts b/src/main/claude/hook-service.test.ts index b40f6884525..3514e666267 100644 --- a/src/main/claude/hook-service.test.ts +++ b/src/main/claude/hook-service.test.ts @@ -17,8 +17,6 @@ vi.mock('electron', () => ({ import type { SFTPWrapper } from 'ssh2' import { ClaudeHookService } from './hook-service' -const CLAUDE_SETTINGS_FILE = 'claude-agent-status-settings.json' - type FakeFs = { files: Map dirs: Set @@ -104,7 +102,7 @@ function createFakeSftp(): { sftp: SFTPWrapper; fs: FakeFs } { } describe('ClaudeHookService.install', () => { - it('keeps the scoped settings hook-only and preserves user Bedrock settings', () => { + it('installs managed hooks into Claude settings and preserves user Bedrock settings', () => { const tmpHome = mkdtempSync(join(tmpdir(), 'orca-claude-hooks-')) vi.stubEnv('HOME', tmpHome) try { @@ -141,11 +139,6 @@ describe('ClaudeHookService.install', () => { const status = new ClaudeHookService().install() expect(status.state).toBe('installed') - const scoped = JSON.parse( - readFileSync(join(tmpHome, '.orca', 'agent-hooks', CLAUDE_SETTINGS_FILE), 'utf-8') - ) - expect(Object.keys(scoped)).toEqual(['hooks']) - const legacy = JSON.parse(readFileSync(legacyPath, 'utf-8')) expect(legacy).toMatchObject({ apiKeyHelper: '/opt/company/claude-key-helper', @@ -160,7 +153,15 @@ describe('ClaudeHookService.install', () => { (definition: { hooks: { command: string }[] }) => definition.hooks.map((hook) => hook.command) ) - expect(legacyCommands).toEqual(['/usr/local/bin/user-hook']) + expect(legacyCommands).toContain('/usr/local/bin/user-hook') + expect(legacyCommands.some((command: string) => command.includes('claude-hook.sh'))).toBe( + true + ) + expect( + legacyCommands.some((command: string) => + command.includes('/Users/old/.orca/agent-hooks/claude-hook.sh') + ) + ).toBe(false) } finally { vi.unstubAllEnvs() rmSync(tmpHome, { recursive: true, force: true }) @@ -169,13 +170,13 @@ describe('ClaudeHookService.install', () => { }) describe('ClaudeHookService.installRemote', () => { - it('writes scoped settings + managed script under the remote $HOME', async () => { + it('writes Claude settings + managed script under the remote $HOME', async () => { const svc = new ClaudeHookService() const { sftp, fs } = createFakeSftp() const status = await svc.installRemote(sftp, '/home/dev') expect(status.state).toBe('installed') - expect(status.configPath).toBe('/home/dev/.orca/agent-hooks/claude-agent-status-settings.json') - const settings = fs.files.get('/home/dev/.orca/agent-hooks/claude-agent-status-settings.json') + expect(status.configPath).toBe('/home/dev/.claude/settings.json') + const settings = fs.files.get('/home/dev/.claude/settings.json') expect(settings).toBeTruthy() const parsed = JSON.parse(settings!) // Why: every load-bearing event must be present and point at the @@ -199,20 +200,19 @@ describe('ClaudeHookService.installRemote', () => { // Managed script body expect(fs.files.get('/home/dev/.orca/agent-hooks/claude-hook.sh')).toContain('#!/bin/sh') expect(fs.modes.get('/home/dev/.orca/agent-hooks/claude-hook.sh')).toBe(0o755) - expect(fs.files.has('/home/dev/.claude/settings.json')).toBe(false) }) - it('reports parse error when legacy remote settings.json cannot be cleaned', async () => { + it('reports parse error when remote settings.json cannot be parsed', async () => { const svc = new ClaudeHookService() const { sftp, fs } = createFakeSftp() fs.files.set('/home/dev/.claude/settings.json', 'not json') const status = await svc.installRemote(sftp, '/home/dev') expect(status.state).toBe('error') - expect(status.managedHooksPresent).toBe(true) - expect(status.detail).toContain('Scoped Claude hooks installed') + expect(status.managedHooksPresent).toBe(false) + expect(status.detail).toContain('Could not parse remote Claude settings.json') }) - it('preserves user-authored legacy hook entries while sweeping old managed entries', async () => { + it('preserves user-authored hook entries while sweeping old managed entries', async () => { const svc = new ClaudeHookService() const { sftp, fs } = createFakeSftp() fs.files.set( @@ -238,14 +238,11 @@ describe('ClaudeHookService.installRemote', () => { ) await svc.installRemote(sftp, '/home/dev') const parsed = JSON.parse(fs.files.get('/home/dev/.claude/settings.json')!) - // Original user-authored entry survives, while legacy global Orca entries - // are removed because scoped --settings carries the managed hook now. + // Original user-authored entry survives, while stale Orca entries are + // replaced with the current managed hook command. const stopDefs = parsed.hooks.Stop as { hooks: { command: string }[] }[] const userCmds = stopDefs.flatMap((d) => d.hooks.map((h) => h.command)) expect(userCmds).toContain('/usr/local/bin/my-user-hook') - expect(userCmds.some((c) => c.includes('claude-hook.sh'))).toBe(false) - expect(fs.files.get('/home/dev/.orca/agent-hooks/claude-agent-status-settings.json')).toContain( - 'claude-hook.sh' - ) + expect(userCmds.filter((c) => c.includes('claude-hook.sh'))).toHaveLength(1) }) }) diff --git a/src/main/claude/hook-service.ts b/src/main/claude/hook-service.ts index 5a8bac41c2c..4fa638b6968 100644 --- a/src/main/claude/hook-service.ts +++ b/src/main/claude/hook-service.ts @@ -1,7 +1,5 @@ -import { existsSync, unlinkSync } from 'fs' import type { SFTPWrapper } from 'ssh2' import type { AgentHookInstallState, AgentHookInstallStatus } from '../../shared/agent-hook-types' -import { ORCA_CLAUDE_AGENT_STATUS_SETTINGS_ENV } from '../../shared/claude-settings' import { buildWindowsAgentHookPostCommand, readHooksJson, @@ -16,13 +14,11 @@ import { import { applyManagedHooks, CLAUDE_EVENTS, - getLegacyConfigPath, + getConfigPath, getManagedCommand, getManagedScriptPath, - getRemoteLegacyConfigPath, + getRemoteConfigPath, getRemoteManagedCommand, - getRemoteScopedSettingsPath, - getScopedSettingsPath, removeManagedHooks } from './hook-settings' @@ -92,7 +88,7 @@ function getManagedScript(target: 'local' | 'posix' = 'local'): string { export class ClaudeHookService { getStatus(): AgentHookInstallStatus { - const configPath = getScopedSettingsPath() + const configPath = getConfigPath() const scriptPath = getManagedScriptPath() const config = readHooksJson(configPath) if (!config) { @@ -101,7 +97,7 @@ export class ClaudeHookService { state: 'error', configPath, managedHooksPresent: false, - detail: 'Could not parse Orca Claude settings file' + detail: 'Could not parse Claude settings.json' } } @@ -142,41 +138,18 @@ export class ClaudeHookService { } install(): AgentHookInstallStatus { - const scopedStatus = this.installScopedSettings() - if (scopedStatus.state === 'error') { - return scopedStatus - } - const legacyStatus = this.removeLegacyGlobalHooks() - if (legacyStatus.state === 'error') { - return { - ...legacyStatus, - managedHooksPresent: scopedStatus.managedHooksPresent, - detail: scopedStatus.managedHooksPresent - ? `Scoped Claude hooks installed, but ${legacyStatus.detail}` - : legacyStatus.detail - } - } - return scopedStatus - } - - buildPtyEnv(): Record { - try { - const status = this.installScopedSettings() - if (status.state === 'error') { - console.warn(`[agent-hooks] Failed to prepare Claude scoped settings: ${status.detail}`) - } - } catch (error) { - console.warn('[agent-hooks] Failed to prepare Claude scoped settings:', error) - } - return { - [ORCA_CLAUDE_AGENT_STATUS_SETTINGS_ENV]: getScopedSettingsPath() - } - } - - private installScopedSettings(): AgentHookInstallStatus { - const configPath = getScopedSettingsPath() + const configPath = getConfigPath() const scriptPath = getManagedScriptPath() - const config = readHooksJson(configPath) ?? {} + const config = readHooksJson(configPath) + if (!config) { + return { + agent: 'claude', + state: 'error', + configPath, + managedHooksPresent: false, + detail: 'Could not parse Claude settings.json' + } + } const command = getManagedCommand(scriptPath) const nextConfig = applyManagedHooks(config, command) @@ -185,37 +158,15 @@ export class ClaudeHookService { return this.getStatus() } - private removeLegacyGlobalHooks(): AgentHookInstallStatus { - const configPath = getLegacyConfigPath() - const config = readHooksJson(configPath) - if (!config) { - return { - agent: 'claude', - state: 'error', - configPath, - managedHooksPresent: false, - detail: 'Could not parse Claude settings.json to remove legacy global hooks' - } - } - const { config: nextConfig, changed } = removeManagedHooks(config) - if (changed) { - writeHooksJson(configPath, nextConfig) - } - return this.getStatus() - } - - // Why: install Orca's scoped Claude hook settings on the remote box rather - // than the local Mac/Linux machine. Caller passes the user's SFTP handle - // from the SshConnection plus the resolved remote `$HOME` used to compute - // the Orca-owned settings path. POSIX-only by design — see - // docs/design/agent-status-over-ssh.md §3 / §6 (Windows-remote deferred). + // Why: install Orca's Claude hook settings on the remote box rather than the + // local machine. Caller passes the user's SFTP handle plus the resolved + // remote `$HOME`; POSIX-only by design (Windows-remote deferred). async installRemote(sftp: SFTPWrapper, remoteHome: string): Promise { // Why: remote-Windows is out of scope for v1 — we ship POSIX-shaped paths // and a `.sh` managed script body. The remote platform is gated by the // relay's capability RPC at a higher layer; we cannot detect it from // `process.platform` here (that's the local box). - const remoteConfigPath = getRemoteScopedSettingsPath(remoteHome) - const remoteLegacyConfigPath = getRemoteLegacyConfigPath(remoteHome) + const remoteConfigPath = getRemoteConfigPath(remoteHome) const remoteScriptPath = `${remoteHome.replace(/\/$/, '')}/.orca/agent-hooks/claude-hook.sh` // Why: SFTP reads/writes fail far more often than local fs (network drops, // EACCES on remote dirs, disk full, channel closed). Wrap the entire @@ -225,7 +176,16 @@ export class ClaudeHookService { // specifically means "file present but unparseable" — keep that branch // distinct so the user sees an actionable message. try { - const config = (await readHooksJsonRemote(sftp, remoteConfigPath)) ?? {} + const config = await readHooksJsonRemote(sftp, remoteConfigPath) + if (!config) { + return { + agent: 'claude', + state: 'error', + configPath: remoteConfigPath, + managedHooksPresent: false, + detail: 'Could not parse remote Claude settings.json' + } + } // Why: the POSIX wrapper is identical regardless of where the script // lands; only the path differs. Reuse the same wrapper helper. @@ -243,25 +203,6 @@ export class ClaudeHookService { await writeManagedScriptRemote(sftp, remoteScriptPath, getManagedScript('posix')) await writeHooksJsonRemote(sftp, remoteConfigPath, nextConfig) - const legacyConfig = await readHooksJsonRemote(sftp, remoteLegacyConfigPath) - if (!legacyConfig) { - return { - agent: 'claude', - state: 'error', - configPath: remoteLegacyConfigPath, - managedHooksPresent: true, - detail: - 'Scoped Claude hooks installed, but could not parse remote Claude settings.json to remove legacy global hooks' - } - } - const { config: nextLegacyConfig, changed } = removeManagedHooks( - legacyConfig, - 'claude-hook.sh' - ) - if (changed) { - await writeHooksJsonRemote(sftp, remoteLegacyConfigPath, nextLegacyConfig) - } - return { agent: 'claude', state: 'installed', @@ -281,13 +222,20 @@ export class ClaudeHookService { } remove(): AgentHookInstallStatus { - const scopedSettingsPath = getScopedSettingsPath() - if (existsSync(scopedSettingsPath)) { - unlinkSync(scopedSettingsPath) + const configPath = getConfigPath() + const config = readHooksJson(configPath) + if (!config) { + return { + agent: 'claude', + state: 'error', + configPath, + managedHooksPresent: false, + detail: 'Could not parse Claude settings.json' + } } - const legacyStatus = this.removeLegacyGlobalHooks() - if (legacyStatus.state === 'error') { - return legacyStatus + const { config: nextConfig, changed } = removeManagedHooks(config) + if (changed) { + writeHooksJson(configPath, nextConfig) } return this.getStatus() } diff --git a/src/main/claude/hook-settings.ts b/src/main/claude/hook-settings.ts index 9a54642bfe9..e4e3bc07662 100644 --- a/src/main/claude/hook-settings.ts +++ b/src/main/claude/hook-settings.ts @@ -1,6 +1,5 @@ import { homedir } from 'os' import { join } from 'path' -import { ORCA_CLAUDE_AGENT_STATUS_SETTINGS_FILE } from '../../shared/claude-settings' import { createManagedCommandMatcher, getSharedManagedScriptPath, @@ -33,14 +32,10 @@ export const CLAUDE_EVENTS = [ } ] as const -export function getLegacyConfigPath(): string { +export function getConfigPath(): string { return join(homedir(), '.claude', 'settings.json') } -export function getScopedSettingsPath(): string { - return join(homedir(), '.orca', 'agent-hooks', ORCA_CLAUDE_AGENT_STATUS_SETTINGS_FILE) -} - export function getManagedScriptFileName(): string { return process.platform === 'win32' ? 'claude-hook.cmd' : 'claude-hook.sh' } @@ -49,11 +44,7 @@ export function getManagedScriptPath(): string { return getSharedManagedScriptPath(getManagedScriptFileName()) } -export function getRemoteScopedSettingsPath(remoteHome: string): string { - return `${remoteHome.replace(/\/$/, '')}/.orca/agent-hooks/${ORCA_CLAUDE_AGENT_STATUS_SETTINGS_FILE}` -} - -export function getRemoteLegacyConfigPath(remoteHome: string): string { +export function getRemoteConfigPath(remoteHome: string): string { return `${remoteHome.replace(/\/$/, '')}/.claude/settings.json` } diff --git a/src/main/ipc/pty.test.ts b/src/main/ipc/pty.test.ts index d286975625d..b7ca9f2164d 100644 --- a/src/main/ipc/pty.test.ts +++ b/src/main/ipc/pty.test.ts @@ -20,7 +20,6 @@ const { spawnMock, openCodeBuildPtyEnvMock, openCodeClearPtyMock, - claudeBuildPtyEnvMock, buildAgentHookEnvMock, clearAgentHookPaneStateMock, registerPaneKeyAliasMock, @@ -50,7 +49,6 @@ const { getPathMock: vi.fn(), spawnMock: vi.fn(), openCodeBuildPtyEnvMock: vi.fn(), - claudeBuildPtyEnvMock: vi.fn(), isPwshAvailableMock: vi.fn(), openCodeClearPtyMock: vi.fn(), buildAgentHookEnvMock: vi.fn(), @@ -105,12 +103,6 @@ vi.mock('../opencode/hook-service', () => ({ } })) -vi.mock('../claude/hook-service', () => ({ - claudeHookService: { - buildPtyEnv: claudeBuildPtyEnvMock - } -})) - vi.mock('../agent-hooks/server', () => ({ agentHookServer: { buildPtyEnv: buildAgentHookEnvMock, @@ -198,7 +190,7 @@ describe('registerPtyHandlers', () => { const savedOrcaCodexHome = process.env.ORCA_CODEX_HOME const savedOrcaOmpAgentDir = process.env.ORCA_OMP_CODING_AGENT_DIR const savedOrcaOmpSourceAgentDir = process.env.ORCA_OMP_SOURCE_AGENT_DIR - const savedOrcaClaudeSettings = process.env.ORCA_CLAUDE_AGENT_STATUS_SETTINGS + const savedOrcaClaudeAgentStatusSettings = process.env.ORCA_CLAUDE_AGENT_STATUS_SETTINGS beforeEach(() => { delete process.env.OPENCODE_CONFIG_DIR @@ -228,7 +220,6 @@ describe('registerPtyHandlers', () => { spawnMock.mockReset() openCodeBuildPtyEnvMock.mockReset() openCodeClearPtyMock.mockReset() - claudeBuildPtyEnvMock.mockReset() buildAgentHookEnvMock.mockReset() clearAgentHookPaneStateMock.mockReset() registerPaneKeyAliasMock.mockReset() @@ -265,9 +256,6 @@ describe('registerPtyHandlers', () => { ORCA_AGENT_HOOK_PORT: '5678', ORCA_AGENT_HOOK_TOKEN: 'agent-token' }) - claudeBuildPtyEnvMock.mockReturnValue({ - ORCA_CLAUDE_AGENT_STATUS_SETTINGS: '/tmp/orca-claude-settings.json' - }) piBuildPtyEnvMock.mockImplementation( (_ptyId: string, existingAgentDir?: string, _kind?: string) => ({ PI_CODING_AGENT_DIR: existingAgentDir @@ -335,10 +323,10 @@ describe('registerPtyHandlers', () => { } else { delete process.env.ORCA_OMP_SOURCE_AGENT_DIR } - if (savedOrcaClaudeSettings === undefined) { + if (savedOrcaClaudeAgentStatusSettings === undefined) { delete process.env.ORCA_CLAUDE_AGENT_STATUS_SETTINGS } else { - process.env.ORCA_CLAUDE_AGENT_STATUS_SETTINGS = savedOrcaClaudeSettings + process.env.ORCA_CLAUDE_AGENT_STATUS_SETTINGS = savedOrcaClaudeAgentStatusSettings } }) @@ -731,7 +719,7 @@ describe('registerPtyHandlers', () => { expect(env.ORCA_PI_SOURCE_AGENT_DIR).toBe('/home/tester/.config/pi-agent') }) - it('injects the Claude/Codex hook receiver env into Orca terminal PTYs', async () => { + it('injects the agent hook receiver env into Orca terminal PTYs', async () => { const env = await spawnAndGetEnv() // Why: after the daemon-parity refactor, buildAgentHookEnv runs exactly // once for a local spawn — inside the shared buildPtyHostEnv helper, @@ -739,10 +727,8 @@ describe('registerPtyHandlers', () => { // both route through. The handler's separate ad-hoc injection (which // used to cause a double-call for local spawns) is gone. expect(buildAgentHookEnvMock).toHaveBeenCalledTimes(1) - expect(claudeBuildPtyEnvMock).toHaveBeenCalledTimes(1) expect(env.ORCA_AGENT_HOOK_PORT).toBe('5678') expect(env.ORCA_AGENT_HOOK_TOKEN).toBe('agent-token') - expect(env.ORCA_CLAUDE_AGENT_STATUS_SETTINGS).toBe('/tmp/orca-claude-settings.json') }) it('strips stale inherited hook receiver env before injecting this runtime', async () => { @@ -752,7 +738,7 @@ describe('registerPtyHandlers', () => { ORCA_AGENT_HOOK_ENV: 'production', ORCA_AGENT_HOOK_VERSION: 'stale-version', ORCA_AGENT_HOOK_ENDPOINT: '/tmp/stale-endpoint.env', - ORCA_CLAUDE_AGENT_STATUS_SETTINGS: '/tmp/stale-claude-settings.json' + ORCA_CLAUDE_AGENT_STATUS_SETTINGS: '/tmp/orca/agent-hooks/claude-agent-status-settings.json' }) expect(env.ORCA_AGENT_HOOK_PORT).toBe('5678') @@ -760,7 +746,7 @@ describe('registerPtyHandlers', () => { expect(env.ORCA_AGENT_HOOK_ENV).toBeUndefined() expect(env.ORCA_AGENT_HOOK_VERSION).toBeUndefined() expect(env.ORCA_AGENT_HOOK_ENDPOINT).toBeUndefined() - expect(env.ORCA_CLAUDE_AGENT_STATUS_SETTINGS).toBe('/tmp/orca-claude-settings.json') + expect(env.ORCA_CLAUDE_AGENT_STATUS_SETTINGS).toBeUndefined() }) it('does not leak inherited hook receiver env if the hook server is unavailable', async () => { @@ -772,7 +758,7 @@ describe('registerPtyHandlers', () => { ORCA_AGENT_HOOK_ENV: 'production', ORCA_AGENT_HOOK_VERSION: 'stale-version', ORCA_AGENT_HOOK_ENDPOINT: '/tmp/stale-endpoint.env', - ORCA_CLAUDE_AGENT_STATUS_SETTINGS: '/tmp/stale-claude-settings.json' + ORCA_CLAUDE_AGENT_STATUS_SETTINGS: '/tmp/orca/agent-hooks/claude-agent-status-settings.json' }) expect(env.ORCA_AGENT_HOOK_PORT).toBeUndefined() @@ -780,7 +766,7 @@ describe('registerPtyHandlers', () => { expect(env.ORCA_AGENT_HOOK_ENV).toBeUndefined() expect(env.ORCA_AGENT_HOOK_VERSION).toBeUndefined() expect(env.ORCA_AGENT_HOOK_ENDPOINT).toBeUndefined() - expect(env.ORCA_CLAUDE_AGENT_STATUS_SETTINGS).toBe('/tmp/orca-claude-settings.json') + expect(env.ORCA_CLAUDE_AGENT_STATUS_SETTINGS).toBeUndefined() }) it('prepends local git/gh attribution shims when attribution is enabled', async () => { @@ -1086,7 +1072,53 @@ describe('registerPtyHandlers', () => { const env = await daemonSpawnAndGetEnv({}) expect(env.ORCA_AGENT_HOOK_PORT).toBe('5678') expect(env.ORCA_AGENT_HOOK_TOKEN).toBe('agent-token') - expect(env.ORCA_CLAUDE_AGENT_STATUS_SETTINGS).toBe('/tmp/orca-claude-settings.json') + }) + + it('deletes stale Claude scoped settings env from daemon-hosted PTYs', async () => { + const spawnOptions = await daemonSpawnAndGetOptions({}, undefined, undefined, { + ORCA_CLAUDE_AGENT_STATUS_SETTINGS: + '/tmp/orca/agent-hooks/claude-agent-status-settings.json' + }) + expect(spawnOptions.env.ORCA_CLAUDE_AGENT_STATUS_SETTINGS).toBeUndefined() + expect(spawnOptions.envToDelete).toEqual( + expect.arrayContaining(['ORCA_CLAUDE_AGENT_STATUS_SETTINGS']) + ) + expect(spawnOptions.env.ORCA_AGENT_HOOK_PORT).toBe('5678') + expect(spawnOptions.env.ORCA_AGENT_HOOK_TOKEN).toBe('agent-token') + }) + + it('deletes stale Claude scoped settings env from runtime-created daemon PTYs', async () => { + type RuntimeSpawnController = { + spawn(args: { + cols: number + rows: number + worktreeId?: string + env?: Record + }): Promise<{ id: string }> + } + const daemonSpawn = setupDaemonAdapter() + const runtime = { + setPtyController: vi.fn(), + registerPty: vi.fn(), + onPtySpawned: vi.fn(), + onPtyExit: vi.fn(), + onPtyData: vi.fn() + } + process.env.ORCA_CLAUDE_AGENT_STATUS_SETTINGS = + '/tmp/orca/agent-hooks/claude-agent-status-settings.json' + handlers.clear() + registerPtyHandlers(mainWindow as never, runtime as never) + const controller = runtime.setPtyController.mock.calls[0]?.[0] as RuntimeSpawnController + + await controller.spawn({ cols: 80, rows: 24, worktreeId: 'wt-runtime', env: {} }) + + const spawnOptions = daemonSpawn.mock.calls.at(-1)?.[0] as DaemonSpawnCall + expect(spawnOptions.env.ORCA_CLAUDE_AGENT_STATUS_SETTINGS).toBeUndefined() + expect(spawnOptions.envToDelete).toEqual( + expect.arrayContaining(['ORCA_CLAUDE_AGENT_STATUS_SETTINGS']) + ) + expect(spawnOptions.env.ORCA_AGENT_HOOK_PORT).toBe('5678') + expect(spawnOptions.env.ORCA_AGENT_HOOK_TOKEN).toBe('agent-token') }) it('strips inherited agent-hook endpoint env from development daemon PTYs', async () => { @@ -1381,7 +1413,6 @@ describe('registerPtyHandlers', () => { // worst a credential leak. expect(env.ORCA_AGENT_HOOK_PORT).toBeUndefined() expect(env.ORCA_AGENT_HOOK_TOKEN).toBeUndefined() - expect(env.ORCA_CLAUDE_AGENT_STATUS_SETTINGS).toBeUndefined() expect(env.ORCA_ENABLE_GIT_ATTRIBUTION).toBeUndefined() expect(env.OPENCODE_CONFIG_DIR).toBeUndefined() expect(env.ORCA_OPENCODE_CONFIG_DIR).toBeUndefined() diff --git a/src/main/ipc/pty.ts b/src/main/ipc/pty.ts index 23bd9dc97ab..8b9d888ce78 100644 --- a/src/main/ipc/pty.ts +++ b/src/main/ipc/pty.ts @@ -10,8 +10,6 @@ export { getBashShellReadyRcfileContent } from '../providers/local-pty-shell-rea import type { OrcaRuntimeService } from '../runtime/orca-runtime' import type { Store } from '../persistence' import type { GlobalSettings } from '../../shared/types' -import { ORCA_CLAUDE_AGENT_STATUS_SETTINGS_ENV } from '../../shared/claude-settings' -import { claudeHookService } from '../claude/hook-service' import { openCodeHookService } from '../opencode/hook-service' import { agentHookServer } from '../agent-hooks/server' import { isAgentStatusHooksEnabled } from '../agent-hooks/managed-agent-hook-controls' @@ -93,7 +91,9 @@ const AGENT_HOOK_RUNTIME_ENV_KEYS = [ 'ORCA_AGENT_HOOK_ENV', 'ORCA_AGENT_HOOK_VERSION', 'ORCA_AGENT_HOOK_ENDPOINT', - ORCA_CLAUDE_AGENT_STATUS_SETTINGS_ENV + // Why: PR 2778 briefly exported this scoped Claude settings path. Keep + // deleting stale inherited values so older PTYs cannot leak the reverted path. + 'ORCA_CLAUDE_AGENT_STATUS_SETTINGS' ] as const export function getPtyIdForPaneKey(paneKey: string): string | undefined { @@ -332,6 +332,16 @@ function mergePtyEnvDeletions( return Array.from(new Set([...(existingKeys ?? []), ...additionalKeys])) } +function getInheritedAgentHookEnvKeysToDelete( + spawnEnv: Record | undefined +): string[] { + const env = spawnEnv ?? {} + // Why: daemon/local providers merge process.env after main-process cleanup. + // Delete reverted or unavailable hook env keys there without dropping fresh + // receiver coordinates that buildPtyHostEnv intentionally set. + return AGENT_HOOK_RUNTIME_ENV_KEYS.filter((key) => env[key] === undefined) +} + // Why: when agent status is disabled, a nested Orca terminal can still pass // through a prior PTY's OpenCode/Pi overlay env. Restore the user's original // source dir when Orca recorded one, otherwise strip only values known to be ours. @@ -431,7 +441,6 @@ export function buildPtyHostEnv( } if (opts.agentStatusHooksEnabled) { Object.assign(baseEnv, agentHookServer.buildPtyEnv()) - Object.assign(baseEnv, claudeHookService.buildPtyEnv()) } // Why: PI_CODING_AGENT_DIR owns Pi's / OMP's full config/session root (OMP @@ -1103,9 +1112,12 @@ export function registerPtyHandlers( cwd: args.cwd, env } - if (claudeAuth?.stripAuthEnv) { - spawnOptions.envToDelete = [...CLAUDE_AUTH_ENV_VARS, 'ANTHROPIC_CUSTOM_HEADERS'] - } + spawnOptions.envToDelete = mergePtyEnvDeletions( + claudeAuth?.stripAuthEnv + ? [...CLAUDE_AUTH_ENV_VARS, 'ANTHROPIC_CUSTOM_HEADERS'] + : undefined, + args.connectionId ? [] : getInheritedAgentHookEnvKeysToDelete(env) + ) if (skipCodexHomeEnv) { spawnOptions.envToDelete = mergePtyEnvDeletions( spawnOptions.envToDelete, @@ -1507,7 +1519,10 @@ export function registerPtyHandlers( ? [...CLAUDE_AUTH_ENV_VARS, 'ANTHROPIC_CUSTOM_HEADERS'] : undefined const combinedEnvToDelete = mergePtyEnvDeletions( - envToDelete, + mergePtyEnvDeletions( + envToDelete, + args.connectionId ? [] : getInheritedAgentHookEnvKeysToDelete(spawnEnv) + ), skipCodexHomeEnv ? CODEX_HOME_ENV_KEYS : [] ) const spawnOptions: PtySpawnOptions = { diff --git a/src/main/runtime/orca-runtime.test.ts b/src/main/runtime/orca-runtime.test.ts index b8da62e9d79..34bd5fe8ad5 100644 --- a/src/main/runtime/orca-runtime.test.ts +++ b/src/main/runtime/orca-runtime.test.ts @@ -7189,7 +7189,7 @@ describe('OrcaRuntimeService', () => { expect(spawn).toHaveBeenCalledWith( expect.objectContaining({ cwd: '/remote/mobile-startup-draft', - command: `claude --settings "$HOME/.orca/agent-hooks/claude-agent-status-settings.json" --prefill '${draftUrl}'`, + command: `claude --prefill '${draftUrl}'`, connectionId: 'ssh-1', worktreeId: result.worktree.id }) diff --git a/src/main/runtime/orca-runtime.ts b/src/main/runtime/orca-runtime.ts index 7246fd0f925..46e243b5c45 100644 --- a/src/main/runtime/orca-runtime.ts +++ b/src/main/runtime/orca-runtime.ts @@ -6730,8 +6730,7 @@ export class OrcaRuntimeService { agent, draft: content, cmdOverrides: settings.agentCmdOverrides ?? {}, - platform: agentLaunchPlatform, - useOrcaClaudeAgentStatusSettings: settings.agentStatusHooksEnabled !== false + platform: agentLaunchPlatform }) if (draftLaunchPlan) { return { @@ -6748,8 +6747,7 @@ export class OrcaRuntimeService { prompt: '', cmdOverrides: settings.agentCmdOverrides ?? {}, platform: agentLaunchPlatform, - allowEmptyPromptLaunch: true, - useOrcaClaudeAgentStatusSettings: settings.agentStatusHooksEnabled !== false + allowEmptyPromptLaunch: true }) if (!startupPlan) { return null diff --git a/src/renderer/src/components/floating-terminal/FloatingTerminalWindowControls.tsx b/src/renderer/src/components/floating-terminal/FloatingTerminalWindowControls.tsx index 35fd859149e..89c33154f9e 100644 --- a/src/renderer/src/components/floating-terminal/FloatingTerminalWindowControls.tsx +++ b/src/renderer/src/components/floating-terminal/FloatingTerminalWindowControls.tsx @@ -48,8 +48,7 @@ export function FloatingTerminalWindowControls({ prompt: '', cmdOverrides: state.settings?.agentCmdOverrides ?? {}, platform: CLIENT_PLATFORM, - allowEmptyPromptLaunch: true, - useOrcaClaudeAgentStatusSettings: state.settings?.agentStatusHooksEnabled !== false + allowEmptyPromptLaunch: true }) if (!startupPlan) { toast.error(`Could not build launch command for ${defaultAgentLabel ?? defaultAgent}.`) diff --git a/src/renderer/src/components/settings/AgentsPane.test.tsx b/src/renderer/src/components/settings/AgentsPane.test.tsx index c1eabf5d787..92f690c8a8e 100644 --- a/src/renderer/src/components/settings/AgentsPane.test.tsx +++ b/src/renderer/src/components/settings/AgentsPane.test.tsx @@ -147,7 +147,6 @@ describe('AgentsPane', () => { it('includes hook search metadata for the status setting', () => { expect(matchesSettingsSearch('hooks', AGENTS_PANE_SEARCH_ENTRIES)).toBe(true) expect(matchesSettingsSearch('waiting', AGENTS_PANE_SEARCH_ENTRIES)).toBe(true) - expect(matchesSettingsSearch('scoped', AGENTS_PANE_SEARCH_ENTRIES)).toBe(true) expect(matchesSettingsSearch('codex', AGENTS_PANE_SEARCH_ENTRIES)).toBe(true) }) }) diff --git a/src/renderer/src/components/settings/agent-status-hooks-copy.ts b/src/renderer/src/components/settings/agent-status-hooks-copy.ts index 99c9cfb689b..45b103086d7 100644 --- a/src/renderer/src/components/settings/agent-status-hooks-copy.ts +++ b/src/renderer/src/components/settings/agent-status-hooks-copy.ts @@ -1,7 +1,7 @@ export const AGENT_STATUS_HOOKS_TITLE = 'Agent status hooks' export const AGENT_STATUS_HOOKS_DESCRIPTION = - 'Shows working, waiting, and done states in Orca. For Claude and Codex, Orca uses scoped settings and profiles so terminal sessions outside Orca keep your existing config. Turn off to remove Orca-managed hooks and stop reinstalling them.' + 'Shows working, waiting, and done states in Orca. Turn off to remove Orca-managed hooks and stop reinstalling them.' export const AGENT_STATUS_HOOKS_SEARCH_KEYWORDS = [ 'hooks', @@ -11,8 +11,6 @@ export const AGENT_STATUS_HOOKS_SEARCH_KEYWORDS = [ 'done', 'remove', 'restore', - 'scoped', - 'profile', 'settings', 'config', 'claude', diff --git a/src/renderer/src/hooks/useComposerState.ts b/src/renderer/src/hooks/useComposerState.ts index ad349d738e9..41ba953abab 100644 --- a/src/renderer/src/hooks/useComposerState.ts +++ b/src/renderer/src/hooks/useComposerState.ts @@ -1761,8 +1761,7 @@ export function useComposerState(options: UseComposerStateOptions): UseComposerS agent: tuiAgent, prompt: startupPrompt, cmdOverrides: settings?.agentCmdOverrides ?? {}, - platform: CLIENT_PLATFORM, - useOrcaClaudeAgentStatusSettings: settings?.agentStatusHooksEnabled !== false + platform: CLIENT_PLATFORM }) // Why: thread agent_started telemetry through the queued startup so @@ -1836,7 +1835,6 @@ export function useComposerState(options: UseComposerStateOptions): UseComposerS selectedRepoIsGit, selectedRepoRequiresConnection, settings?.agentCmdOverrides, - settings?.agentStatusHooksEnabled, setSidebarOpen, setupDecision, sparseEnabled, @@ -1980,8 +1978,7 @@ export function useComposerState(options: UseComposerStateOptions): UseComposerS agent, draft: quickDraftPrompt, cmdOverrides: settings?.agentCmdOverrides ?? {}, - platform: CLIENT_PLATFORM, - useOrcaClaudeAgentStatusSettings: settings?.agentStatusHooksEnabled !== false + platform: CLIENT_PLATFORM }) let startupPlan: ReturnType = null @@ -1999,8 +1996,7 @@ export function useComposerState(options: UseComposerStateOptions): UseComposerS prompt: quickPrompt, cmdOverrides: settings?.agentCmdOverrides ?? {}, platform: CLIENT_PLATFORM, - allowEmptyPromptLaunch: true, - useOrcaClaudeAgentStatusSettings: settings?.agentStatusHooksEnabled !== false + allowEmptyPromptLaunch: true }) if (startupPlan && quickDraftPrompt) { startupPlan.draftPrompt = quickDraftPrompt @@ -2077,7 +2073,6 @@ export function useComposerState(options: UseComposerStateOptions): UseComposerS selectedRepoIsGit, selectedRepoRequiresConnection, settings?.agentCmdOverrides, - settings?.agentStatusHooksEnabled, setSidebarOpen, setupDecision, sparseEnabled, diff --git a/src/renderer/src/lib/launch-agent-background-session.test.ts b/src/renderer/src/lib/launch-agent-background-session.test.ts index 4e196775862..27afc646dc8 100644 --- a/src/renderer/src/lib/launch-agent-background-session.test.ts +++ b/src/renderer/src/lib/launch-agent-background-session.test.ts @@ -18,8 +18,6 @@ const mockSubscribeToPtyExit = vi.fn() const mockPasteDraftWhenAgentReady = vi.fn() const mockMarkTrusted = vi.fn() const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/ -const CLAUDE_SCOPED_SETTINGS = - 'claude --settings "$HOME/.orca/agent-hooks/claude-agent-status-settings.json"' function expectStablePaneSpawn(): string { const spawnArgs = mockSpawn.mock.calls[0]?.[0] @@ -124,7 +122,7 @@ describe('launchAgentBackgroundSession', () => { expect(mockSpawn).toHaveBeenCalledWith( expect.objectContaining({ cwd: '/repo/worktree', - command: `${CLAUDE_SCOPED_SETTINGS} 'run the automation'`, + command: "claude 'run the automation'", env: expect.objectContaining({ ORCA_TAB_ID: 'tab-1', ORCA_WORKTREE_ID: 'wt-1' @@ -269,10 +267,7 @@ describe('launchAgentBackgroundSession', () => { dataSidecar('user@remote repo % ') vi.advanceTimersByTime(50) - expect(mockWrite).toHaveBeenCalledWith( - 'pty-1', - `${CLAUDE_SCOPED_SETTINGS} 'run the automation'\r` - ) + expect(mockWrite).toHaveBeenCalledWith('pty-1', "claude 'run the automation'\r") } finally { vi.useRealTimers() } @@ -306,7 +301,7 @@ describe('launchAgentBackgroundSession', () => { method: 'terminal.create', params: expect.objectContaining({ worktree: 'wt-1', - command: `${CLAUDE_SCOPED_SETTINGS} 'run the automation'`, + command: "claude 'run the automation'", env: expect.objectContaining({ ORCA_PANE_KEY: `tab-1:${leafId}`, ORCA_TAB_ID: 'tab-1', diff --git a/src/renderer/src/lib/launch-agent-background-session.ts b/src/renderer/src/lib/launch-agent-background-session.ts index ba60358638f..03b0659d73b 100644 --- a/src/renderer/src/lib/launch-agent-background-session.ts +++ b/src/renderer/src/lib/launch-agent-background-session.ts @@ -63,7 +63,6 @@ export async function launchAgentBackgroundSession( } } const cmdOverrides = store.settings?.agentCmdOverrides ?? {} - const useOrcaAgentStatusHooks = store.settings?.agentStatusHooksEnabled !== false const trimmedPrompt = prompt?.trim() ?? '' const hasPrompt = trimmedPrompt.length > 0 const isFollowupPath = TUI_AGENT_CONFIG[agent].promptInjectionMode === 'stdin-after-start' @@ -76,8 +75,7 @@ export async function launchAgentBackgroundSession( prompt: '', cmdOverrides, platform: CLIENT_PLATFORM, - allowEmptyPromptLaunch: true, - useOrcaClaudeAgentStatusSettings: useOrcaAgentStatusHooks + allowEmptyPromptLaunch: true }) pasteDraftAfterLaunch = trimmedPrompt } else { @@ -86,8 +84,7 @@ export async function launchAgentBackgroundSession( prompt: hasPrompt ? trimmedPrompt : '', cmdOverrides, platform: CLIENT_PLATFORM, - allowEmptyPromptLaunch: !hasPrompt, - useOrcaClaudeAgentStatusSettings: useOrcaAgentStatusHooks + allowEmptyPromptLaunch: !hasPrompt }) } if (!startupPlan) { diff --git a/src/renderer/src/lib/launch-agent-in-new-tab.ts b/src/renderer/src/lib/launch-agent-in-new-tab.ts index c80aa775800..79170f70dc6 100644 --- a/src/renderer/src/lib/launch-agent-in-new-tab.ts +++ b/src/renderer/src/lib/launch-agent-in-new-tab.ts @@ -72,7 +72,6 @@ export function launchAgentInNewTab(args: LaunchAgentInNewTabArgs): LaunchAgentI } = args const store = useAppStore.getState() const cmdOverrides = store.settings?.agentCmdOverrides ?? {} - const useOrcaAgentStatusHooks = store.settings?.agentStatusHooksEnabled !== false const trimmedPrompt = prompt?.trim() ?? '' const hasPrompt = trimmedPrompt.length > 0 const isFollowupPath = TUI_AGENT_CONFIG[agent].promptInjectionMode === 'stdin-after-start' @@ -96,8 +95,7 @@ export function launchAgentInNewTab(args: LaunchAgentInNewTabArgs): LaunchAgentI prompt: '', cmdOverrides, platform: CLIENT_PLATFORM, - allowEmptyPromptLaunch: true, - useOrcaClaudeAgentStatusSettings: useOrcaAgentStatusHooks + allowEmptyPromptLaunch: true }) pasteDraftAfterLaunch = trimmedPrompt submitPastedPrompt = true @@ -107,8 +105,7 @@ export function launchAgentInNewTab(args: LaunchAgentInNewTabArgs): LaunchAgentI agent, draft: trimmedPrompt, cmdOverrides, - platform: CLIENT_PLATFORM, - useOrcaClaudeAgentStatusSettings: useOrcaAgentStatusHooks + platform: CLIENT_PLATFORM }) if (draftLaunchPlan) { startupPlan = { @@ -124,8 +121,7 @@ export function launchAgentInNewTab(args: LaunchAgentInNewTabArgs): LaunchAgentI prompt: '', cmdOverrides, platform: CLIENT_PLATFORM, - allowEmptyPromptLaunch: true, - useOrcaClaudeAgentStatusSettings: useOrcaAgentStatusHooks + allowEmptyPromptLaunch: true }) pasteDraftAfterLaunch = trimmedPrompt } @@ -135,8 +131,7 @@ export function launchAgentInNewTab(args: LaunchAgentInNewTabArgs): LaunchAgentI prompt: '', cmdOverrides, platform: CLIENT_PLATFORM, - allowEmptyPromptLaunch: true, - useOrcaClaudeAgentStatusSettings: useOrcaAgentStatusHooks + allowEmptyPromptLaunch: true }) pasteDraftAfterLaunch = trimmedPrompt } else { @@ -145,8 +140,7 @@ export function launchAgentInNewTab(args: LaunchAgentInNewTabArgs): LaunchAgentI prompt: hasPrompt ? trimmedPrompt : '', cmdOverrides, platform: CLIENT_PLATFORM, - allowEmptyPromptLaunch: !hasPrompt, - useOrcaClaudeAgentStatusSettings: useOrcaAgentStatusHooks + allowEmptyPromptLaunch: !hasPrompt }) } diff --git a/src/renderer/src/lib/launch-work-item-direct.ts b/src/renderer/src/lib/launch-work-item-direct.ts index f4e8d412827..5e3cf5d2fd1 100644 --- a/src/renderer/src/lib/launch-work-item-direct.ts +++ b/src/renderer/src/lib/launch-work-item-direct.ts @@ -298,8 +298,7 @@ export async function launchWorkItemDirect(args: LaunchWorkItemDirectArgs): Prom agent: effectiveAgent, draft: draftContent, cmdOverrides: settings?.agentCmdOverrides ?? {}, - platform: CLIENT_PLATFORM, - useOrcaClaudeAgentStatusSettings: settings?.agentStatusHooksEnabled !== false + platform: CLIENT_PLATFORM }) if (draftLaunchPlan) { startupPlan = { @@ -316,8 +315,7 @@ export async function launchWorkItemDirect(args: LaunchWorkItemDirectArgs): Prom prompt: '', cmdOverrides: settings?.agentCmdOverrides ?? {}, platform: CLIENT_PLATFORM, - allowEmptyPromptLaunch: true, - useOrcaClaudeAgentStatusSettings: settings?.agentStatusHooksEnabled !== false + allowEmptyPromptLaunch: true }) } diff --git a/src/renderer/src/lib/onboarding-folder-agent-startup.ts b/src/renderer/src/lib/onboarding-folder-agent-startup.ts index 60b25e7dae4..1d8d68d637a 100644 --- a/src/renderer/src/lib/onboarding-folder-agent-startup.ts +++ b/src/renderer/src/lib/onboarding-folder-agent-startup.ts @@ -29,8 +29,7 @@ export function buildOnboardingFolderAgentStartup( prompt: '', cmdOverrides: settings.agentCmdOverrides ?? {}, platform: getClientPlatform(), - allowEmptyPromptLaunch: true, - useOrcaClaudeAgentStatusSettings: settings.agentStatusHooksEnabled !== false + allowEmptyPromptLaunch: true }) if (!startupPlan) { return undefined diff --git a/src/renderer/src/lib/worktree-activation.ts b/src/renderer/src/lib/worktree-activation.ts index 6384cc301e8..3f688cec468 100644 --- a/src/renderer/src/lib/worktree-activation.ts +++ b/src/renderer/src/lib/worktree-activation.ts @@ -97,9 +97,7 @@ function buildCreatedAgentReopenStartup(worktree: Worktree): prompt: '', cmdOverrides: useAppStore.getState().settings?.agentCmdOverrides ?? {}, platform: CLIENT_PLATFORM, - allowEmptyPromptLaunch: true, - useOrcaClaudeAgentStatusSettings: - useAppStore.getState().settings?.agentStatusHooksEnabled !== false + allowEmptyPromptLaunch: true }) if (!startupPlan) { return undefined diff --git a/src/shared/claude-settings.ts b/src/shared/claude-settings.ts deleted file mode 100644 index fe82752beb5..00000000000 --- a/src/shared/claude-settings.ts +++ /dev/null @@ -1,19 +0,0 @@ -export const ORCA_CLAUDE_AGENT_STATUS_SETTINGS_ENV = 'ORCA_CLAUDE_AGENT_STATUS_SETTINGS' -export const ORCA_CLAUDE_AGENT_STATUS_SETTINGS_FILE = 'claude-agent-status-settings.json' - -type ClaudeSettingsShell = 'posix' | 'powershell' | 'cmd' - -export function appendOrcaClaudeAgentStatusSettings( - command: string, - shell: ClaudeSettingsShell -): string { - // Why: Claude's --settings is a per-process overlay. CLAUDE_CONFIG_DIR - // would fork auth/session state and normal external Claude launches. - if (shell === 'powershell') { - return `${command} --settings $Env:${ORCA_CLAUDE_AGENT_STATUS_SETTINGS_ENV}` - } - if (shell === 'cmd') { - return `${command} --settings "%${ORCA_CLAUDE_AGENT_STATUS_SETTINGS_ENV}%"` - } - return `${command} --settings "$HOME/.orca/agent-hooks/${ORCA_CLAUDE_AGENT_STATUS_SETTINGS_FILE}"` -} diff --git a/src/shared/tui-agent-startup.test.ts b/src/shared/tui-agent-startup.test.ts index d019c2b23df..21ac0d6ffc0 100644 --- a/src/shared/tui-agent-startup.test.ts +++ b/src/shared/tui-agent-startup.test.ts @@ -47,55 +47,16 @@ describe('tui agent startup plans', () => { expect(plan?.launchCommand).toBe("codex 'fix it'") }) - it('does not inject Codex profile flags through the shared agent-status option', () => { - const plan = buildAgentStartupPlan({ - agent: 'codex', - prompt: 'fix it', - cmdOverrides: {}, - platform: 'linux', - useOrcaClaudeAgentStatusSettings: true - }) - - expect(plan?.launchCommand).toBe("codex 'fix it'") - expect(plan?.launchCommand).not.toContain('--profile') - expect(plan?.launchCommand).not.toContain('orca-agent-status') - }) - - it('launches Claude with the Orca settings file when agent status hooks are enabled', () => { + it('launches Claude without Orca settings injection', () => { const plan = buildAgentStartupPlan({ agent: 'claude', prompt: 'fix it', cmdOverrides: {}, - platform: 'linux', - useOrcaClaudeAgentStatusSettings: true + platform: 'linux' }) - expect(plan?.launchCommand).toBe( - 'claude --settings "$HOME/.orca/agent-hooks/claude-agent-status-settings.json" \'fix it\'' - ) - }) - - it('uses the target shell syntax for Claude settings injection', () => { - expect( - buildAgentStartupPlan({ - agent: 'claude', - prompt: 'fix it', - cmdOverrides: {}, - platform: 'win32', - useOrcaClaudeAgentStatusSettings: true - })?.launchCommand - ).toBe("claude --settings $Env:ORCA_CLAUDE_AGENT_STATUS_SETTINGS 'fix it'") - - expect( - buildAgentStartupPlan({ - agent: 'claude', - prompt: 'fix it', - cmdOverrides: {}, - platform: 'win32', - shell: 'cmd', - useOrcaClaudeAgentStatusSettings: true - })?.launchCommand - ).toBe('claude --settings "%ORCA_CLAUDE_AGENT_STATUS_SETTINGS%" "fix it"') + expect(plan?.launchCommand).toBe("claude 'fix it'") + expect(plan?.launchCommand).not.toContain('--settings') }) it('leaves Claude command overrides untouched', () => { @@ -103,8 +64,7 @@ describe('tui agent startup plans', () => { agent: 'claude', prompt: 'fix it', cmdOverrides: { claude: 'claude --dangerously-skip-permissions' }, - platform: 'linux', - useOrcaClaudeAgentStatusSettings: true + platform: 'linux' }) expect(plan?.launchCommand).toBe("claude --dangerously-skip-permissions 'fix it'") diff --git a/src/shared/tui-agent-startup.ts b/src/shared/tui-agent-startup.ts index 4c724ea0706..56ce9b71b94 100644 --- a/src/shared/tui-agent-startup.ts +++ b/src/shared/tui-agent-startup.ts @@ -1,5 +1,4 @@ import { isShellProcess } from './agent-detection' -import { appendOrcaClaudeAgentStatusSettings } from './claude-settings' import { TUI_AGENT_CONFIG } from './tui-agent-config' import type { TuiAgent } from './types' @@ -49,16 +48,12 @@ function resolveBaseCommand(args: { agent: TuiAgent cmdOverrides: Partial> shell: AgentStartupShell - useOrcaClaudeAgentStatusSettings?: boolean }): string { const override = args.cmdOverrides[args.agent] if (override) { return override } const command = TUI_AGENT_CONFIG[args.agent].launchCmd - if (args.agent === 'claude' && args.useOrcaClaudeAgentStatusSettings) { - return appendOrcaClaudeAgentStatusSettings(command, args.shell) - } // Why: Codex status hooks live in Orca's runtime CODEX_HOME; adding // --profile-v2 makes Codex load a second hook representation and warn. return command @@ -71,7 +66,6 @@ export function buildAgentStartupPlan(args: { platform: NodeJS.Platform shell?: AgentStartupShell allowEmptyPromptLaunch?: boolean - useOrcaClaudeAgentStatusSettings?: boolean }): AgentStartupPlan | null { const { agent, prompt, cmdOverrides, platform, allowEmptyPromptLaunch = false } = args const shell = resolveStartupShell(platform, args.shell) @@ -80,8 +74,7 @@ export function buildAgentStartupPlan(args: { const baseCommand = resolveBaseCommand({ agent, cmdOverrides, - shell, - useOrcaClaudeAgentStatusSettings: args.useOrcaClaudeAgentStatusSettings + shell }) if (!trimmedPrompt) { @@ -155,7 +148,6 @@ export function buildAgentDraftLaunchPlan(args: { cmdOverrides: Partial> platform: NodeJS.Platform shell?: AgentStartupShell - useOrcaClaudeAgentStatusSettings?: boolean }): AgentDraftLaunchPlan | null { const { agent, draft, cmdOverrides, platform } = args const shell = resolveStartupShell(platform, args.shell) @@ -167,8 +159,7 @@ export function buildAgentDraftLaunchPlan(args: { const baseCommand = resolveBaseCommand({ agent, cmdOverrides, - shell, - useOrcaClaudeAgentStatusSettings: args.useOrcaClaudeAgentStatusSettings + shell }) if (config.draftPromptFlag) { const quoted = quoteStartupArg(trimmed, shell)