Gemini CLI 0.63 修复了非交互任务进入计划模式后因等待批准而提前退出的问题。作者通过本地模拟接口捕获请求,发现同一版本会依据是否存在 TTY 使用不同的计划模式系统指令。
gemini-cli 有个老 bug,乍看像是模型的问题,其实不是。Issue #28913:在脚本里运行 gemini -p "..." -y,Agent 一旦进入 Plan Mode,运行就会自行结束。没有报错,也没有崩溃——模型只输出一段文字,不调用任何工具,进程就退出了。在非交互模式下,纯文本响应就是终止信号,因此,Agent 礼貌地等待批准,在程序看来和“我做完了”完全一样。
这个问题在 0.63 中通过 #29539 修复,变更标题是 fix(core): enable autonomous plan execution in non-interactive mode。我想看看这个修复究竟改了什么,于是把 0.63.0 指向本地 mock endpoint,抓取了两次实际发送的 systemInstruction:一次不带 TTY,一次在 TTY 中运行。
同一个二进制文件,同样的 --approval-mode plan,得到的 prompt 却不同。

不带 TTY(gemini -p "..."):29,139 个字符。
在 pty 中运行(script -qec "gemini -i ..."):30,370 个字符。
Agent 启动时收到的指令相差 1,231 个字符,而决定这一差异的唯一因素,就是 stdin 是否为终端。
不需要 API key,也不需要模型。gemini-cli 从 GOOGLE_GEMINI_BASE_URL 读取 endpoint,所以我写了一个小型 HTTP server:接收请求,保存请求体,再返回一段预设响应。然后运行:
npm i -g @google/gemini-cli@0.63.0
gemini --version # 0.63.0
# ~/.gemini/settings.json
# { "security": { "auth": { "selectedType": "gemini-api-key" } } }
export GEMINI_API_KEY=fake
export GOOGLE_GEMINI_BASE_URL=http://127.0.0.1:8799
# run 1: no TTY
gemini -p "Plan a small refactor of this repo" --approval-mode plan --skip-trust
# run 2: same thing, but give it a terminal
script -qec "gemini -i 'Plan a small refactor of this repo' --approval-mode plan --skip-trust" /dev/null
两次运行请求的 model id 都相同(gemini-3.1-pro-preview-customtools:streamGenerateContent),两个请求体也都记进了 server 日志。我对比的是其中的 systemInstruction 字符串。关键技巧是 script -qec:它会分配一个 pty,让 CLI 认为自己处于交互模式,从而改变它构建的 prompt。
规划工作流由模板渲染生成,会根据是否处于交互模式选择不同分支。
交互模式的 prompt 中包含那句导致问题的指令:“**关键要求:**在首次提出策略的同一轮中,你绝不能继续执行第 3 步(起草)或第 4 步(审查与批准)。你必须等待用户反馈。”这句话,再加上“纯文本回复会结束运行”的模式,就足以解释 #28913。非交互模式的 prompt 删除了这句话,并将目标替换为“自主继续执行”。
交互模式的工具表额外列出了三个工具:activate_skill、ask_user 和 web_fetch。非交互模式的 prompt 还删除了 shell 命令规则中对 ask_user 的提及。这与 issue 讨论中的情况一致:无头会话本来就禁止使用 ask_user,所以旧版的阻塞行为从来不是通过 ask_user 实现的——它靠的是不调用任何工具、只返回纯文本,而这种响应不会被任何机制拦截。
这个 PR 修改了 packages/core/src/prompts/snippets.ts 中的 renderPlanningWorkflow(),让它根据 options.interactive 选择分支:第 2 步分别变成“确定策略”和“征询意见”,第 4 步的结尾从“正式请求批准。”改为“开始实现。”,而“对齐检查”那条指令只会在交互模式分支中渲染。
我没有仅凭合并状态下结论,而是直接从已安装的 @google/gemini-cli@0.63.0 包中检索这些字符串。发布 bundle 中确实包含两个版本,形式也与代码 diff 所显示的一致。修复已经进入我实际运行的二进制内容,而不只是存在于默认分支上。
你拿到什么 prompt,取决于 stdin 是否为 TTY。这对 CI 来说是个容易踩中的坑。
gemini -p "..." 和 echo "..." | gemini 会拿到自主执行的 prompt。很好。
任何给进程提供 pty 的方式,都会拿到交互模式的 prompt——也就是要求停下来等待的那个版本。使用 script、pty.fork()、expect、某些 docker run -it 调用,或终端复用型 runner 的 wrapper,都会多收到 1,231 个字符的指令,要求 Agent 等待一个根本不会出现的人;而在这种运行模式下,等待会让运行悄无声息地结束。
如果你基于 gemini-cli 构建了无头 runner,它在规划任务中卡住或提前退出,那么在排查其他问题之前,先检查有没有分配 TTY。这会是我首先检查的地方。
需要如实说明验证边界:我抓取的是 prompt 的构建结果,不是模型行为。我的 endpoint 返回的是预设响应,因此,我能证明 CLI 发送了哪些指令,也能证明发布 bundle 中包含这个分支。但我无法证明真实的 Gemini 模型现在会自行调用 exit_plan_mode——这需要真实的 key 和实际运行,我在这里没有做。Google 的 Plan Mode 文档说明,非交互会话会自动批准 enter_plan_mode / exit_plan_mode,并在退出 Plan Mode 时切换到 YOLO;这里我只是引用文档,没有实测。
我也没有抓取 0.62 的 prompt,所以“0.63 修复了这个问题”的判断,依据是 changelog 条目和代码 diff,而不是两个版本的前后对照运行。
仅有 GEMINI_API_KEY 还不够。必须让 security.auth.selectedType 指定一个有效的认证方式,否则 CLI 会拒绝启动。填入 "gateway" 会得到 Invalid auth method selected.(退出码 41);接受的值是 oauth-personal、compute-default-credentials、gemini-api-key 和 vertex-ai。
抓取时不要使用 -y。YOLO 模式会在主请求之前,先发起一次 gemini-3.5-flash-lite:generateContent 的“Task Routing AI”复杂度评分调用。我的预设响应 server 无法生成评分,CLI 重试了五次,随后就一直停在那里。如果你只想获取 prompt,就使用 --approval-mode plan,不要加 -y。交互模式还会为会话标题或摘要额外发起一次 flash-lite 调用。
两份抓取结果都保存为纯文本:分别为 29,139 和 30,370 个字符,总计 59,509 个字符。我调用 CLI 的方式只有一处差异:是否连接了 pty。
如果需要进一步采取行动,你可以考虑屏蔽此人和/或举报滥用行为。