mirror of
https://github.com/stablyai/orca.git
synced 2026-10-31 00:57:45 +00:00
fix(native-chat): seed the chat permission default from Claude's and Codex's own arguments (#28100)
* fix(native-chat): seed the chat permission default from the Yolo/Manual toggle as it shows The one-time copy into Settings -> Chat -> Permissions gave Full access only when every agent's arguments were exactly the bypass flag, so a profile the toggle shows as Yolo (agents set differently, or an extra flag) got Ask. Follow the toggle: Manual seeds Ask, anything it shows as Yolo seeds Full access. * fix(native-chat): seed the chat permission default from Claude's and Codex's own arguments The Yolo/Manual toggle has no saved value: it summarises every agent's arguments and shows Yolo for any mix, including after Manual when another agent keeps custom arguments. Seeding from it would grant Full access to people who chose Manual. Read what Claude and Codex chats checked before they had modes instead: Full access when both launch with their bypass flag, otherwise Ask.
This commit is contained in:
@@ -137,7 +137,11 @@ export function normalizeLoadedGlobalSettings(
|
||||
// Why: the next settings save stores the seed; until then a reload derives the same one.
|
||||
nativeChatPermissionMode: loadedChatPermissionSetting(
|
||||
parsed.settings?.nativeChatPermissionMode,
|
||||
migratedAgentYoloDefaults
|
||||
{
|
||||
...migratedAgentYoloDefaults,
|
||||
terminalWindowsShell: parsed.settings?.terminalWindowsShell
|
||||
},
|
||||
process.platform
|
||||
),
|
||||
defaultTaskSource: taskProviderSettings.defaultTaskSource,
|
||||
visibleTaskProviders: taskProviderSettings.visibleTaskProviders,
|
||||
|
||||
@@ -79,40 +79,71 @@ describe('the mode a chat runs', () => {
|
||||
})
|
||||
|
||||
describe('the new-chat setting as loaded', () => {
|
||||
const seed = (terminal: Parameters<typeof loadedChatPermissionSetting>[1]) =>
|
||||
loadedChatPermissionSetting(undefined, terminal, 'darwin')
|
||||
const yoloWithArgs = (args: { claude?: string; codex?: string; grok?: string }) => {
|
||||
const terminal = applyAgentPermissionMode({ mode: 'yolo' })
|
||||
Object.assign(terminal.agentDefaultArgs, args)
|
||||
return terminal
|
||||
}
|
||||
|
||||
it.each([
|
||||
['yolo', 'full-access'],
|
||||
['manual', 'ask']
|
||||
] as const)('seeds %s Agent Permissions as %s', (mode, expected) => {
|
||||
expect(loadedChatPermissionSetting(undefined, applyAgentPermissionMode({ mode }))).toBe(
|
||||
expected
|
||||
)
|
||||
expect(seed(applyAgentPermissionMode({ mode }))).toBe(expected)
|
||||
})
|
||||
|
||||
it('seeds mixed Agent Permissions as Ask', () => {
|
||||
const terminal = applyAgentPermissionMode({ mode: 'yolo' })
|
||||
terminal.agentDefaultArgs.claude = ''
|
||||
expect(loadedChatPermissionSetting(undefined, terminal)).toBe('ask')
|
||||
it('seeds Full access when Claude and Codex carry their bypass flags among other arguments', () => {
|
||||
expect(
|
||||
seed(
|
||||
yoloWithArgs({
|
||||
claude: '--dangerously-skip-permissions --model opus',
|
||||
codex: '--model gpt-5 --dangerously-bypass-approvals-and-sandbox'
|
||||
})
|
||||
)
|
||||
).toBe('full-access')
|
||||
})
|
||||
|
||||
it('reads only Claude and Codex, not the other agents', () => {
|
||||
expect(seed(yoloWithArgs({ grok: '' }))).toBe('full-access')
|
||||
const othersManual = applyAgentPermissionMode({ mode: 'manual' })
|
||||
Object.assign(othersManual.agentDefaultArgs, {
|
||||
claude: '--dangerously-skip-permissions',
|
||||
codex: '--dangerously-bypass-approvals-and-sandbox'
|
||||
})
|
||||
expect(seed(othersManual)).toBe('full-access')
|
||||
})
|
||||
|
||||
it('seeds Ask when Claude or Codex launches without its bypass flag', () => {
|
||||
expect(seed(yoloWithArgs({ claude: '' }))).toBe('ask')
|
||||
expect(seed(yoloWithArgs({ codex: '--model gpt-5' }))).toBe('ask')
|
||||
})
|
||||
|
||||
// Manual leaves custom arguments alone, and the all-agent summary still shows Yolo for them.
|
||||
it('seeds Ask after Manual even when another agent keeps custom arguments', () => {
|
||||
const custom = yoloWithArgs({ codex: '--model gpt-5', grok: '--model grok-4' })
|
||||
expect(seed(applyAgentPermissionMode({ mode: 'manual', ...custom }))).toBe('ask')
|
||||
})
|
||||
|
||||
it('seeds a fresh profile from the shipped Yolo defaults', () => {
|
||||
expect(loadedChatPermissionSetting(undefined, getDefaultSettings('/tmp'))).toBe('full-access')
|
||||
expect(seed(getDefaultSettings('/tmp'))).toBe('full-access')
|
||||
expect(getDefaultSettings('/tmp').nativeChatPermissionMode).toBe('full-access')
|
||||
})
|
||||
|
||||
it.each(AGENT_CHAT_PERMISSION_MODES)(
|
||||
'keeps a saved %s whatever Agent Permissions say',
|
||||
(mode) => {
|
||||
expect(loadedChatPermissionSetting(mode, applyAgentPermissionMode({ mode: 'manual' }))).toBe(
|
||||
mode
|
||||
)
|
||||
expect(loadedChatPermissionSetting(mode, applyAgentPermissionMode({ mode: 'yolo' }))).toBe(
|
||||
mode
|
||||
)
|
||||
for (const terminal of ['manual', 'yolo'] as const) {
|
||||
expect(
|
||||
loadedChatPermissionSetting(mode, applyAgentPermissionMode({ mode: terminal }), 'darwin')
|
||||
).toBe(mode)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
it.each(['future', null, 42, 'bypass'])('reads an unknown saved %s as Ask', (value) => {
|
||||
expect(loadedChatPermissionSetting(value, getDefaultSettings('/tmp'))).toBe('ask')
|
||||
expect(loadedChatPermissionSetting(value, getDefaultSettings('/tmp'), 'darwin')).toBe('ask')
|
||||
})
|
||||
|
||||
// The catalog shape `claude` 2.1.280 lists: the default row is folded into the row it resolves to.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { GlobalSettings } from './global-settings-types'
|
||||
import { resolveAgentPermissionModeSummary } from './tui-agent-permissions'
|
||||
import { resolvedTuiAgentArgsBypassPermissions } from './tui-agent-launch-defaults'
|
||||
|
||||
/** A chat's own permission choice. Saved in chat records and settings: never rename a value. */
|
||||
export type AgentChatPermissionMode = 'ask' | 'accept-edits' | 'auto' | 'full-access'
|
||||
@@ -104,13 +104,18 @@ export function newChatPermissionMode(
|
||||
return support ? savedChatPermissionMode(support, setting) : null
|
||||
}
|
||||
|
||||
/** Seeded once from Agent Permissions when absent, then stored; an unknown value asks. */
|
||||
/** Seeded once when absent, then stored; an unknown value asks. */
|
||||
export function loadedChatPermissionSetting(
|
||||
saved: unknown,
|
||||
terminal: Partial<Pick<GlobalSettings, 'agentDefaultArgs' | 'agentDefaultEnv'>>
|
||||
terminal: Partial<Pick<GlobalSettings, 'agentDefaultArgs' | 'terminalWindowsShell'>>,
|
||||
platform: NodeJS.Platform
|
||||
): AgentChatPermissionMode {
|
||||
if (saved !== undefined) {
|
||||
return isAgentChatPermissionMode(saved) ? saved : 'ask'
|
||||
}
|
||||
return resolveAgentPermissionModeSummary(terminal) === 'yolo' ? 'full-access' : 'ask'
|
||||
// What chats ran before they had modes: Claude's and Codex's own arguments, not the all-agent
|
||||
// Yolo/Manual summary, which shows custom arguments on any agent as Yolo even after Manual.
|
||||
const bypasses = (agent: 'claude' | 'codex'): boolean =>
|
||||
resolvedTuiAgentArgsBypassPermissions(agent, terminal, platform)
|
||||
return bypasses('claude') && bypasses('codex') ? 'full-access' : 'ask'
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ import { DEFAULT_APP_ICON_ID } from './app-icon'
|
||||
import { DEFAULT_OPEN_IN_APPLICATIONS } from './open-in-applications'
|
||||
import { DEFAULT_DISABLED_TUI_AGENTS } from './tui-agent-selection'
|
||||
import { DEFAULT_TUI_AGENT_ARGS, DEFAULT_TUI_AGENT_ENV } from './tui-agent-launch-defaults'
|
||||
import { loadedChatPermissionSetting } from './agent-chat-permission-mode'
|
||||
import { UI_LANGUAGE_SYSTEM } from './ui-language'
|
||||
import {
|
||||
DEFAULT_LEFT_SIDEBAR_TINT_COLOR,
|
||||
@@ -140,10 +139,8 @@ export function buildDefaultSettings(args: {
|
||||
nativeChatInheritShellEnvironment: true,
|
||||
nativeChatShellEnvironmentVariables: [],
|
||||
nativeChatSessionOptions: {},
|
||||
nativeChatPermissionMode: loadedChatPermissionSetting(undefined, {
|
||||
agentDefaultArgs: DEFAULT_TUI_AGENT_ARGS,
|
||||
agentDefaultEnv: DEFAULT_TUI_AGENT_ENV
|
||||
}),
|
||||
// The shipped Claude and Codex arguments carry their bypass flags.
|
||||
nativeChatPermissionMode: 'full-access',
|
||||
openInApplications: [...DEFAULT_OPEN_IN_APPLICATIONS],
|
||||
rightSidebarOpenByDefault: true,
|
||||
showGitIgnoredFiles: true,
|
||||
|
||||
Reference in New Issue
Block a user