mirror of
https://github.com/claude-code-best/claude-code.git
synced 2026-06-17 13:55:50 +00:00
feat: 正式启用 auto mode (#307)
* fix: 修复settings.json内存状态溢出的问题 * fix: 修复auto mode gate check未处理的promise rejection 在 bypassPermissionsKillswitch.ts 的 useKickOffCheckAndDisableAutoModeIfNeeded 中,void fire-and-forget 调用缺少 .catch() 处理,导致 verifyAutoModeGateAccess 失败时产生 unhandled promise rejection。同时移除 permissionSetup.ts 中冗余的 null check。 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: 开放 auto mode 和 bypass mode 给所有用户 通过 Shift+Tab 统一循环:default → acceptEdits → plan → auto → bypassPermissions → default - 移除 USER_TYPE 分支判断,所有用户使用同一循环路径 - isBypassPermissionsModeAvailable 始终为 true - isAutoModeAvailable 初始化直接为 true - 移除 AutoModeOptInDialog 确认流程 - 简化 isAutoModeGateEnabled 仅保留快模式熔断器 - 简化 verifyAutoModeGateAccess 仅检查快模式 - 移除 GrowthBook/Statsig 远程门控 - bypass permissions killswitch 改为 no-op - 新增 24 个测试覆盖循环逻辑和门控不变量 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: 为sideQuery添加Langfuse追踪 sideQuery 绕过了 claude.ts 的主 API 路径,导致所有走 sideQuery 的调用 (auto mode classifier、permission explainer、session search 等)都没有 Langfuse 记录。现在为每次 sideQuery 调用创建独立 trace 并记录 LLM observation, 未配置 Langfuse 时全部 no-op。 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: ACP availableModes 补齐 bypassPermissions 并修正测试 import 路径 - ACP agent availableModes 按条件包含 bypassPermissions(非 root/sandbox) - 顺序对齐 REPL 循环:default → acceptEdits → plan → auto → bypassPermissions - 新增 2 个测试验证 availableModes 包含 bypassPermissions 及模式切换 - 修正 getNextPermissionMode.test.ts 和 permissionSetup.test.ts 的 import 路径 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -42,7 +42,7 @@ const mockGetDefaultAppState = mock(() => ({
|
||||
alwaysAllowRules: { user: [], project: [], local: [] },
|
||||
alwaysDenyRules: { user: [], project: [], local: [] },
|
||||
alwaysAskRules: { user: [], project: [], local: [] },
|
||||
isBypassPermissionsModeAvailable: false,
|
||||
isBypassPermissionsModeAvailable: true,
|
||||
},
|
||||
fastMode: false,
|
||||
settings: {},
|
||||
@@ -627,6 +627,23 @@ describe('AcpAgent', () => {
|
||||
agent.setSessionMode({ sessionId: 'ghost', modeId: 'auto' } as any),
|
||||
).rejects.toThrow('Session not found')
|
||||
})
|
||||
|
||||
test('availableModes includes bypassPermissions when not root', async () => {
|
||||
const agent = new AcpAgent(makeConn())
|
||||
const { sessionId } = await agent.newSession({ cwd: '/tmp' } as any)
|
||||
const session = agent.sessions.get(sessionId)
|
||||
const modeIds = session?.modes.availableModes.map((m: any) => m.id)
|
||||
expect(modeIds).toContain('bypassPermissions')
|
||||
})
|
||||
|
||||
test('can switch to bypassPermissions mode', async () => {
|
||||
const agent = new AcpAgent(makeConn())
|
||||
const { sessionId } = await agent.newSession({ cwd: '/tmp' } as any)
|
||||
await agent.setSessionMode({ sessionId, modeId: 'bypassPermissions' } as any)
|
||||
const session = agent.sessions.get(sessionId)
|
||||
expect(session?.modes.currentModeId).toBe('bypassPermissions')
|
||||
expect(session?.appState.toolPermissionContext.mode).toBe('bypassPermissions')
|
||||
})
|
||||
})
|
||||
|
||||
describe('setSessionConfigOption', () => {
|
||||
|
||||
@@ -519,12 +519,15 @@ export class AcpAgent implements Agent {
|
||||
|
||||
const queryEngine = new QueryEngine(engineConfig)
|
||||
|
||||
// Build modes
|
||||
// Build modes — bypassPermissions only available when not running as root (or in sandbox)
|
||||
const availableModes = [
|
||||
{ id: 'auto', name: 'Auto', description: 'Use a model classifier to approve/deny permission prompts.' },
|
||||
{ id: 'default', name: 'Default', description: 'Standard behavior, prompts for dangerous operations' },
|
||||
{ id: 'acceptEdits', name: 'Accept Edits', description: 'Auto-accept file edit operations' },
|
||||
{ id: 'plan', name: 'Plan Mode', description: 'Planning mode, no actual tool execution' },
|
||||
{ id: 'auto', name: 'Auto', description: 'Use a model classifier to approve/deny permission prompts.' },
|
||||
...(isBypassAvailable
|
||||
? [{ id: 'bypassPermissions' as const, name: 'Bypass Permissions', description: 'Skip all permission checks' }]
|
||||
: []),
|
||||
{ id: 'dontAsk', name: "Don't Ask", description: "Don't prompt for permissions, deny if not pre-approved" },
|
||||
]
|
||||
|
||||
|
||||
@@ -109,7 +109,6 @@ const externalTips: Tip[] = [
|
||||
`Use Plan Mode to prepare for a complex request before making changes. Press ${getShortcutDisplay('chat:cycleMode', 'Chat', 'shift+tab')} twice to enable.`,
|
||||
cooldownSessions: 5,
|
||||
isRelevant: async () => {
|
||||
if (process.env.USER_TYPE === 'ant') return false
|
||||
const config = getGlobalConfig()
|
||||
// Show to users who haven't used plan mode recently (7+ days)
|
||||
const daysSinceLastUse = config.lastPlanModeUse
|
||||
@@ -401,9 +400,7 @@ const externalTips: Tip[] = [
|
||||
{
|
||||
id: 'shift-tab',
|
||||
content: async () =>
|
||||
process.env.USER_TYPE === 'ant'
|
||||
? `Hit ${getShortcutDisplay('chat:cycleMode', 'Chat', 'shift+tab')} to cycle between default mode and auto mode`
|
||||
: `Hit ${getShortcutDisplay('chat:cycleMode', 'Chat', 'shift+tab')} to cycle between default mode, auto-accept edit mode, and plan mode`,
|
||||
`Hit ${getShortcutDisplay('chat:cycleMode', 'Chat', 'shift+tab')} to cycle between default, accept edits, plan, auto, and bypass modes`,
|
||||
cooldownSessions: 10,
|
||||
isRelevant: async () => true,
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user