/** * AgentMail 的档位 → ZCode `--mode` 的映射。 * * # 为什么这个映射必须存在,而且不能想当然 * * ZCode 的权限判定里有一条: * * t.mode === "yolo" ? this.allow(t, i, "mode.yolo", "Yolo mode bypasses permission prompts") * * 也就是 **`yolo` 会绕过全部权限询问**,PermissionRequest 钩子根本不会触发 —— * 我们的授权桥会**静默消失**(不是报错,是没有询问,看起来一切正常)。 * * 而 `--prompt` 的**默认 mode 就是 `yolo`**(`--mode` 的 help 写着 * "default: yolo for --prompt")。所以驱动若图省事不传 `--mode`, * 人就会以为「授权系统在管事」,实际每一条命令都已经自动放行了。 * * 反过来也不能一律传 `build`:档位的意义就是三种不同的行为。 * * # 依据(从 CLI 产物里读出的规则表,不是猜) * * - `checkBuildMode`:只读放行;critical/high 风险 → **ask**; * 有副作用 / 需要审批 → **ask**。`Bash` 属 destructive(high)→ ask; * `Write`/`Edit` 有 workspace 副作用 → ask。 * - `checkEditMode`:`permissionName === "edit"` 的工作区文件编辑放行,其余退回 build。 * - plan:`mode.plan.nonReadOnly` → 非只读一律**拒**。 * - yolo:一律放行。 * * 于是映射为:plan → plan,workspace → build,full → yolo。 */ import { normalizeMode, DEFAULT_MODE, MODE_PLAN, MODE_FULL } from '../lib/permission-mode.js'; /** ZCode 认识的 headless mode(`normalizePromptMode` 只接受这四个)。 */ export const ZCODE_MODES = ['build', 'edit', 'plan', 'yolo']; const MODE_FOR_TIER = { [MODE_PLAN]: 'plan', workspace: 'build', [MODE_FULL]: 'yolo' }; /** * @param {string} tier AgentMail 的档位(plan / workspace / full) * @returns {'build'|'edit'|'plan'|'yolo'} */ export function zcodeModeForTier(tier) { const t = normalizeMode(tier) || DEFAULT_MODE; return MODE_FOR_TIER[t] || 'build'; } /** * 这个 mode 是否会让危险操作走到我们的授权钩子。 * * 用于启动自检与日志 —— 「驱动跑起来了但一次授权询问都没发生」有两种成因 * (真没人碰危险工具 / mode 把询问绕过了),它们必须以不同的方式被看见。 */ export function modeReachesPermissionHook(mode) { return mode === 'build' || mode === 'edit'; } /** 权限档位的人话解释,写进日志与回信里,方便复盘「当时是什么档」。 */ export function describeTier(tier, mode = zcodeModeForTier(tier)) { const t = normalizeMode(tier) || DEFAULT_MODE; if (t === MODE_PLAN) return `${t} 档 → --mode ${mode}(只读,ZCode 自己就会拒非只读工具)`; if (t === MODE_FULL) return `${t} 档 → --mode ${mode}(全权,刻意绕过权限询问)`; return `${t} 档 → --mode ${mode}(危险操作会走到 AgentMail 授权钩子)`; }