Happy 项目权限模式解析机制详解:App 端与 Claude CLI 的 State-Based 权限解析全流程
发布时间:2026/9/20 9:34:17 锦皓数字建站

Happy 项目权限模式解析机制详解App 端与 Claude CLI 的 State-Based 权限解析全流程【免费下载链接】happyMobile and Web client for Codex and Claude Code, with realtime voice, encryption and fully featured项目地址: https://gitcode.com/gh_mirrors/happy20/happy本篇技术指南围绕 happy 仓库中docs/permission-resolution.md的权限解析规范展开系统讲解permissionMode权限模式在移动/Web 客户端packages/happy-app与 Claude CLIpackages/happy-cli之间如何被解析、传递、映射与强制约束包括会话状态合并、出站消息元数据、CLI 启动解析、逐消息更新与沙箱策略。读完本文你将掌握 Happy 项目中从用户选择权限模式到最终传给 Claude SDK 的权限模式的完整状态机链路以及沙箱会话为何始终强制bypassPermissions的底层原理。一、范围与整体脉络权限模式的解析发生在三个层面原文档docs/permission-resolution.md将其明确划分为App 端状态解析会话默认值、持久化值、出站消息元数据的生成Claude CLI 解析启动模式、逐消息更新、沙箱策略最终发送给 Claude SDK 的模式由 CLI 在 SDK 边界完成映射。这三层共同保证了用户在 App 上选择的权限模式与Claude 进程实际执行的权限行为始终一致并且任何一端都无法通过消息元数据绕过沙箱约束。二、权限模式全集与 Claude 映射规则Happy 项目定义了一套共享的权限模式类型共享于 App 与 CLI 之间default | acceptEdits | bypassPermissions | plan | read-only | safe-yolo | yolo而从源码看实际完整的PermissionMode联合类型还包含auto共 8 种模式。这一全集定义在 permissionMode.ts 的VALID_PERMISSION_MODES常量中const VALID_PERMISSION_MODES: readonly PermissionMode[] [ auto, default, acceptEdits, bypassPermissions, plan, read-only, safe-yolo, yolo, ] as const;其中auto是 Agent SDK 自身PermissionMode联合类型中的一等公民模式因此直接透传而非映射到default。Claude SDK 仅支持 4 种模式Claude SDK 实际支持的权限模式为default | acceptEdits | bypassPermissions | plan。因此 Happy 需要在 SDK 边界把其他模式映射到 Claude 兼容模式。这一映射只存在于一个地方permissionMode.ts 的mapToClaudeModeconst codexToClaudeMap: Recordstring, ClaudeSdkPermissionMode { yolo: bypassPermissions, safe-yolo: default, read-only: default, }; return codexToClaudeMap[mode] ?? (mode as ClaudeSdkPermissionMode);映射规则Happy 模式Claude SDK 模式语义yolobypassPermissions两者都是跳过全部权限询问safe-yolodefaultClaude 无对应模式退化为询问权限read-onlydefaultClaude 不支持只读模式退化为询问权限auto/default/acceptEdits/bypassPermissions/plan原样透传Claude 原生支持的五个模式一个值得注意的实现细节mapToClaudeMode对undefined也做了特判并返回undefined——注释明确说明 Undefined is a meaningful value, not a missing one即undefined表示无覆盖SDK 会读取 Claude 自身的配置而不是被强制为询问模式。这避免了把所有未设置的会话都钉死在提示模式上见 runClaude.ts 中对currentEnhancedMode的注释。未知模式的防护normalizeRemotePermissionModepermissionMode.ts负责收窄从网络到达的模式消息 schema 允许任意字符串较新的 App 可能命名了当前 CLI 不认识的模式未知模式会被丢弃并打一条警告日志保证消息本身仍可投递、会话保持当前模式而不是让整条消息失败。三、App 端状态解析四层优先级1) 会话状态加载/合并时的解析在 storage.ts 的applySessions中App 端对session.permissionMode的解析遵循以下顺序内存中已存在的会话模式非default本地存储中持久化的每会话模式非default服务端会话 payload 中的模式非default沙箱兜底若session.metadata.sandbox.enabled truebypassPermissions否则default源码中的核心是resolveModePick辅助函数storage.ts它不仅处理 permissionMode还统一处理modelMode与effortLevelconst resolveModePick (field: permissionMode | modelMode | effortLevel): string | null { const existing state.sessions[session.id]?.[field] ?? null; if (isAgentModePushPending(session.id, field)) { return existing; // 乐观推送在途时保留本地新值 } return session.metadata session.metadata[field] ! undefined ? session.metadata[field] ?? null // 同步元数据优先含显式 null 重置 : existing; };这段注释对应 issue #1492说明了设计动机权限/模型/努力等级的选择通过会话元数据同步。元数据值包括显式null表示重置优先于本地镜像唯一例外是当该字段的乐观推送仍在途时——此时入站事件携带的仍是旧元数据若直接应用会把刚做的本地选择弹回去。2) 新会话草稿兜底在 persistence.ts 中NewSessionDraft结构包含permissionMode: PermissionModeKey | null。如果草稿缺少权限模式默认值为default。3) 新会话 UI 默认值新会话向导NewSessionWizard的默认选择为default。若当前所选模式对当前选中的 Agent 无效UI 会重置回上述 Agent 默认值。此外modelModeOptions.ts中的permissionModeSupportedByClimodelModeOptions.ts会根据会话 CLI 的版本过滤掉该 CLI 无法解析的模式例如auto模式从CLI_VERSION_WITH_AUTO版本才开始支持旧 CLI 的会话选择器会隐藏auto而保存的旧模式键如旧会话里的auto、或应用于旧 CLI 的全局默认auto绝不允许上线传输——因为旧 CLI 的 schema 会拒绝它并丢弃整条消息。测试 modelModeOptions.test.ts 验证了这一门控逻辑permissionModeSupportedByCli(auto, 1.2.1-beta.1) // false permissionModeSupportedByCli(auto, 1.2.1-beta.2) // true permissionModeSupportedByCli(plan, 1.2.0) // true4) 出站消息模式解析App 发送消息时调用链为sync.ts→resolveMessageModeMetamessageMeta.ts。关键逻辑若session.permissionMode非default原样发送否则若session.metadata.sandbox.enabled true发送bypassPermissions否则发送defaultresolveMessageModeMeta对三种 Agent 风味做了差异化处理Rigv1 元数据从session.permissionMode→metadata.currentOperatingModeCode→metadata.permissionMode→metadata.session.permissionMode依次取第一个可用值Codex / Agy总是以具体值发送——因为 Codex 会在 abort 时重置到启动模式而 Agy 在 provider 边界独立映射 model effort若省略兜底值执行结果可能与 UI 显示不一致Claude及其他会话有值用会话值否则回落到agentDefaultOverrides.permissionMode。这里还有一个重要的拒绝而非替换设计当会话或其保存的默认值携带了接收方 CLI 无法解析的模式时resolveMessageModeMeta会抛出UnsupportedPermissionModeErrormessageMeta.ts调用方 sync.ts 会弹出错误提示并拒绝发送。注释的说明很直白静默换成一个默认模式会悄悄改变 Agent 的权限边界——对 Claude 而言这可能把用户选择的reviewed Auto升级成 yolo。测试 messageMeta.test.ts 覆盖了旧 CLI 上保存auto的各种拒绝场景并断言错误消息中携带了模式名与 CLI 版本号。出站消息的传输载体最终解析出的模式随消息发送到两个位置见 sync.ts加密消息的meta.permissionModesocket envelope 的permissionModemeta: { sentFrom, ...(rigSendsMessageReceipts(session.metadata) ? { expectsAcceptance: true } : {}), appendSystemPrompt: systemPrompt, ...(modeMeta.permissionMode ! undefined ? { permissionMode: modeMeta.permissionMode } : {}), ...(modeMeta.model ! undefined ? { model: modeMeta.model } : {}), ... }注意出站时使用了条件展开只有permissionMode ! undefined才写入避免无意义的空值污染消息。四、Claude CLI 端解析启动、逐消息与本地进程1) 启动解析优先级CLI 启动时的初始模式解析在 runClaude.ts 与 permissionMode.ts 中实现优先级从高到低--dangerously-skip-permissions最高优先级→bypassPermissions--permission-mode VALUE或--permission-modeVALUE传入的options.permissionModeextractPermissionModeFromClaudeArgspermissionMode.ts同时支持两种命令行写法--permission-mode plan // 空格分隔 --permission-modeplan // 等号连接随后应用沙箱策略applySandboxPermissionPolicypermissionMode.ts沙箱启用强制bypassPermissions沙箱禁用保留解析出的模式runClaude中还会基于初始模式推导出dangerouslySkipPermissions标志并写入会话元数据runClaude.tsconst dangerouslySkipPermissions initialPermissionMode bypassPermissions || initialPermissionMode yolo || sandboxEnabled || Boolean(options.claudeArgs?.includes(--dangerously-skip-permissions));2) 远程流程中的逐消息更新当用户消息携带meta.permissionMode时CLI 调用resolveRemoteClaudePermissionModepermissionMode.ts沙箱启用强制bypassPermissions沙箱禁用使用入站模式该函数还有一个防止环境性降级的保护逻辑Happy App 的各个版本可能每条消息都发送permissionMode: default即使 CLI 进程是以 yolo/bypass 模式启动的。由于 Claude 在 SDK 边界把yolo和bypassPermissions都映射为 bypass不能让这种环境性的default把当前模式降级但仍允许plan等显式模式生效export function resolveRemoteClaudePermissionMode( currentMode: PermissionMode | undefined, incomingMode: PermissionMode | undefined, sandboxEnabled: boolean, ): PermissionMode | undefined { if (!incomingMode) { return currentMode; } const nextMode applySandboxPermissionPolicy(incomingMode, sandboxEnabled); if (isClaudeBypassEquivalent(currentMode) nextMode default) { return currentMode; // 当前是 yolo/bypass入站 default 不降级 } return nextMode; }isClaudeBypassEquivalent定义于 permissionMode.tsmode bypassPermissions || mode yolo。在 runClaude.ts 的消息循环中这一函数与normalizeRemotePermissionMode组合使用并记录了ignoredDefaultDowngrade以跟踪被忽略的降级。3) 本地 Claude 进程在 claudeLocal.ts 中若沙箱启用launcher 会在 spawn 前把--dangerously-skip-permissions追加进启动参数if (opts.sandboxConfig?.enabled) { ... if (!spawnArgs.includes(--dangerously-skip-permissions)) { spawnArgs [...spawnArgs, --dangerously-skip-permissions]; } const fullCommand [node, ...spawnArgs.map((arg) quoteShellArg(arg))].join( ); spawnCommand await wrapCommand(fullCommand); spawnWithShell true; logger.info([ClaudeLocal] Sandbox enabled: workspace..., network${opts.sandboxConfig.networkMode}); }同时注意沙箱初始化失败时的降级路径捕获异常后cleanupSandbox null; spawnCommand null; spawnWithShell false;并以不带--dangerously-skip-permissions的原始参数继续启动claudeLocal.ts。另外Windows 平台不支持沙箱会直接警告并继续claudeLocal.ts。五、有效结果矩阵综合 App 端与 CLI 端的两侧解析最终生效模式可用矩阵概括沙箱启用场景生效模式会话模式为default或缺失App 兜底为bypassPermissions用户消息携带任意模式CLI 沙箱策略强制为bypassPermissions本地 Claude 进程launcher 追加--dangerously-skip-permissions沙箱会话在全链路上都无法通过消息元数据重新启用权限询问——这正是权限模型稳定性的关键。沙箱禁用场景生效模式App/会话模式非default使用该模式如plan、acceptEditsApp/会话模式为default或缺失App 发送defaultCLI 走正常模式解析无沙箱强制六、稳定性设计为什么这个方案现在很稳定原文档在结尾点明了该设计的两个核心稳定性保证结合源码可以进一步展开客户端兜底只在沙箱会话强制跳过权限App 端的沙箱兜底逻辑metadata.sandbox.enabled true→bypassPermissions被收敛在 storage 合并与出站消息解析两处非沙箱会话不会受到任何强制用户选择的模式包括plan、read-only、acceptEdits原样透传。CLI 沙箱策略保证沙箱化 Claude 会话无法通过消息元数据重新启用权限询问三处实现形成闭环——启动解析时applySandboxPermissionPolicy强制 bypass、逐消息更新时resolveRemoteClaudePermissionMode再次强制、本地进程 spawn 时追加--dangerously-skip-permissions。即使 App 发送了default或恶意构造的元数据CLI 也会在三个入口统一拦截。版本兼容防护App 端的permissionModeSupportedByClimodelModeOptions.ts与 CLI 端的normalizeRemotePermissionModepermissionMode.ts分别在发送端和接收端做了双重校验发送端对旧 CLI 无法解析的模式拒绝发送而非静默替换UnsupportedPermissionModeError接收端对未知模式丢弃字段而非丢弃消息。两个方向都遵循宁可拒绝也不悄悄改变权限的原则。七、延伸阅读权限解析规范原文docs/permission-resolution.mdCLI 端模式映射与解析工具permissionMode.tsCLI 启动与远程消息循环runClaude.ts本地进程沙箱启动器claudeLocal.tsApp 端会话状态合并storage.tsApp 端出站消息模式解析messageMeta.ts 及测试 messageMeta.test.ts模式选择器与 CLI 版本兼容门控modelModeOptions.ts 及测试 modelModeOptions.test.ts消息发送调用链sync.ts新会话草稿持久化persistence.ts【免费下载链接】happyMobile and Web client for Codex and Claude Code, with realtime voice, encryption and fully featured项目地址: https://gitcode.com/gh_mirrors/happy20/happy创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。