Yoda
参考Agent 设计指南Runtime 生命周期

版本管理

CC 双渠道(stable/latest)+ 后台自更新 + 服务端版本管控 vs Codex 单流发布 + 退出时按安装方式升级;harness 侧以 which + --version 探测为底座

版本管理

结论

Claude Code 与 Codex CLI 在版本管理上是两种哲学。CC 是「受管的持续交付」:双发布渠道(latest/stable)、原生安装器把每个版本落盘到 ~/.local/share/claude/versions/<version> 再用符号链接激活、进程内每 30 分钟后台检查更新、外加三层服务端管控(minVersion 强制下线、maxVersion 事故熔断、minimumVersion 用户侧防降级)。Codex 则是「检测 + 建议」:单一 GitHub Release 流、启动时异步刷新 $CODEX_HOME/version.json 缓存(20 小时一次)、不做后台自更新,只在 TUI 退出时或用户跑 codex update 时按 InstallContext 推断出的安装方式(npm/bun/brew/standalone)执行对应升级命令。对 harness(Yoda)而言,两者都能用 which + --version 统一探测,但「锁版本」只有 CC 原生安装真正支持多版本共存目录;Codex 的 standalone 布局(~/.codex/packages/standalone/releases/<version>)虽是多版本目录结构,却没有暴露 pin 接口。

研究问题

  • 两家 CLI 的版本号查询方式与输出格式差异?
  • 升级渠道(自更新 / 包管理器 / 安装脚本)各有哪些?发布渠道如何划分?
  • 版本如何锁定 / 防降级 / 服务端强控?
  • harness 如何检测已安装版本,能否管理多版本?

各 Agent 设计与实现

Claude Code

研究底座:v2.1.88 重构源码(src_2026-03-31/,泄露重构件,只做架构描述 + 短引,滞后线上约 2 个月)+ 官方 docs(2026-06)。

版本查询claude --version 是入口处的零依赖快速路径,输出 <version> (Claude Code),版本号由 MACRO.VERSION 构建期内联(entrypoints/cli.tsx:36-42)[一手源码]。版本号采用 SemVer + 构建元数据(X.X.X+SHA):兼容性比较忽略 +SHA,而 claude update 用精确字符串比较,以便仅 SHA 变化也触发更新(utils/autoUpdater.ts:54-69 注释)[一手源码]。

发布渠道type ReleaseChannel = 'stable' | 'latest'utils/config.ts:74)[一手源码]。渠道由 settings 的 autoUpdatesChannel(zod enum ['latest','stable']utils/settings/types.ts:804-807)控制,默认 latest;stable 通常滞后约一周并跳过有重大回归的版本(docs setup.md "Configure release channel")[一手文档]。版本源有两套:

  • npm dist-tags:npm view <pkg> dist-tags --json --prefer-online,从 homedir() 运行以防项目级 .npmrc 被恶意改写指向攻击者 registry(utils/autoUpdater.ts:324-326, 355-361)[一手源码];
  • GCS bucket:https://storage.googleapis.com/claude-code-dist-.../claude-code-releases/<channel> 返回版本号纯文本,供无 npm 的原生/包管理器安装使用(utils/autoUpdater.ts:30-31, 384-397)[一手源码]。

自更新:原生安装在进程内每 30 分钟检查一次(components/NativeAutoUpdater.tsx:162useInterval(checkForUpdates, 30 * 60 * 1000))[一手源码];Homebrew/WinGet 走 PackageManagerAutoUpdater.tsx:72(同样 1 800 000 ms),且需 CLAUDE_CODE_PACKAGE_MANAGER_AUTO_UPDATE=1 才会代跑升级命令 [一手源码+一手文档]。更新在后台下载安装、下次启动生效(docs setup.md "Auto-updates")[一手文档]。npm 全局安装的更新走 installGlobalPackage():抢 ~/.claude/.update.lock 文件锁(wx 原子创建 + 5 分钟 stale 超时 + 复查关闭 TOCTOU 竞态,utils/autoUpdater.ts:161-249),检查全局前缀可写性,再 npm/bun install -gutils/autoUpdater.ts:456-533)[一手源码]。

禁用与锁定(多层):

  • DISABLE_AUTOUPDATER 只停后台检查,claude update/claude install 仍可用;DISABLE_UPDATES 封死全部更新路径(getAutoUpdaterDisabledReason() 返回 development / env / config 三类原因,utils/config.ts:1735-1755;docs setup.md "Disable auto-updates")[一手源码+一手文档];
  • minimumVersion setting 是版本地板:切到 stable 渠道时防降级,shouldSkipVersion() 对低于地板的目标版本直接跳过(utils/autoUpdater.ts:145-159)[一手源码];
  • 服务端管控:assertMinVersion() 拉取动态配置 tengu_version_config,当前版本低于 minVersion 时打印升级提示并退出进程(utils/autoUpdater.ts:70-99);getMaxVersion()tengu_max_version_config,作为事故期间暂停推新的服务端 kill switch(utils/autoUpdater.ts:101-114 注释)[一手源码];
  • 企业管控:managed settings 的 requiredMinimumVersion/requiredMaximumVersion 让 CC 在版本区间外拒绝启动(docs setup.md "Pin a minimum version")[一手文档]。

多版本共存:原生安装器把每个版本放在 ~/.local/share/claude/versions/<version>(XDG data),~/.local/bin/claude 是指向当前版本二进制的符号链接,保留最近 VERSION_RETENTION_COUNT = 2 个版本(utils/nativeInstaller/installer.ts:73, 119-131, 468)[一手源码]。

Codex CLI

研究底座:codex-rs/ @ b89ce9a(2026-06-06),一手开源。

版本查询:clap 标准 --versioncli/src/main.rs:90-95#[clap(author, version, ...)]);运行期常量 CODEX_CLI_VERSIONtui/src/updates.rs:20)[一手源码]。

发布渠道:没有 stable/latest 双渠道,单一上游 = GitHub Release(https://api.github.com/repos/openai/codex/releases/latest,tag 形如 rust-vX.Y.Ztui/src/updates.rs:66, 132-143)。brew 安装例外地查 Homebrew cask API(formulae.brew.sh/api/cask/codex.json),因为 brew 收录滞后于 GitHub(tui/src/updates.rs:64-65 注释);npm 安装同时查 GitHub + npm registry,并用 ensure_version_ready 确认 npm 侧已可安装该版本(tui/src/updates.rs:99-110)[一手源码]。

更新检查get_upgrade_version() 受 config 项 check_for_update_on_startup 控制(config/src/config_toml.rs:459),源码构建(is_source_build_version)跳过;缓存写入 $CODEX_HOME/version.json(字段 latest_version/last_checked_at/dismissed_version),距上次检查超过 20 小时才在 tokio 后台任务刷新——本次启动读旧缓存、下次启动才显示更新横幅,确保 TUI 启动不被网络阻塞(tui/src/updates.rs:22-52)[一手源码]。用户可对某个版本点「忽略」,dismissed_version 持久化后该版本不再弹窗(tui/src/updates.rs:145-178)[一手源码]。

升级执行codex update 子命令(cli/src/main.rs:156-157, 749-764;debug 构建直接报错不可用)。升级命令由 UpdateAction 按安装方式映射(tui/src/update_action.rs:39-61)[一手源码]:

InstallMethod升级命令
Npmnpm install -g @openai/codex
Bunbun install -g @openai/codex
Brewbrew upgrade --cask codex
Standalone (Unix)curl -fsSL https://chatgpt.com/codex/install.sh | CODEX_NON_INTERACTIVE=1 sh
Standalone (Windows)irm https://chatgpt.com/codex/install.ps1 | iex(PowerShell -ExecutionPolicy Bypass

TUI 退出时若用户在更新弹窗确认,handle_app_exit 在主进程退出前同步执行该命令并要求重启(cli/src/main.rs:685-746)[一手源码]。

安装方式检测(harness 可直接借鉴的设计):独立 crate codex-rs/install-context。优先看 npm shim 注入的环境变量 CODEX_MANAGED_BY_NPM / CODEX_MANAGED_BY_BUNinstall-context/src/lib.rs:109-121;shim 在 codex-cli/bin/codex.js:140-148 设置,并附 CODEX_MANAGED_PACKAGE_ROOT),否则按可执行文件 canonical 路径推断:位于 $CODEX_HOME/packages/standalone/releases/ 下为 Standalone,macOS 上 /opt/homebrew/usr/local 前缀为 Brew,否则 Other(install-context/src/lib.rs:209-252)[一手源码]。检测不出时 codex update 放弃并指引手动更新(cli/src/main.rs:759-763)[一手源码]。

差异矩阵

维度Claude CodeCodex CLI
版本输出2.1.x (Claude Code),零依赖快速路径clap 标准 codex-cli x.y.z
发布渠道latest / stable 双渠道(npm dist-tag + GCS 指针 + 双 brew cask)单 GitHub Release 流
自更新进程内每 30 min 检查,后台静默安装,下次启动生效不自更新;启动异步刷新缓存(20h TTL),退出时/手动执行升级
更新命令claude updateclaude install [version|stable|latest]codex update(debug 构建禁用)
安装方式检测getCurrentInstallationType() 路径启发式(6 类)InstallContext env 变量 + 路径(5 类),独立 crate
版本锁定minimumVersion 地板 + managed requiredMin/MaxVersion 区间无 pin 机制,仅 check_for_update_on_startup=false 关检查
服务端管控tengu_version_config 强制最低版 + tengu_max_version_config 熔断
禁用开关DISABLE_AUTOUPDATER(仅后台)/ DISABLE_UPDATES(全部)check_for_update_on_startup(config.toml)
多版本落盘~/.local/share/claude/versions/<v> + symlink,保留 2 个~/.codex/packages/standalone/releases/<v-triple>(仅 standalone)
版本缓存无本地缓存文件(npm view / GCS 实时拉取,5s 超时)$CODEX_HOME/version.json(20h TTL + dismissed_version)
升级并发安全~/.claude/.update.lock 文件锁(O_EXCL + stale 复查)无锁(退出后单进程执行)

最小复现

# harness 通用版本探测底座
$ which claude && claude --version
/Users/mark/.local/bin/claude
2.1.118 (Claude Code)

$ which codex && codex --version
/opt/homebrew/bin/codex
codex-cli 0.52.0

# Codex 版本缓存(20h TTL)
$ cat ~/.codex/version.json
{"latest_version":"0.52.0","last_checked_at":"2026-06-10T03:12:45Z"}

# CC 渠道指针(GCS,返回纯文本版本号;URL 见 utils/autoUpdater.ts:30-31)
$ curl -s https://storage.googleapis.com/claude-code-dist-86c565f3-f756-42ad-8dfa-d59b1c096819/claude-code-releases/stable

(前三段为本机示意输出;版本号随时间变化。)

Harness 接入建议(Yoda 实践)

Yoda 已上线的版本探测是两阶段异步流水线(yoda/src/main/core/dependencies/)[一手源码]:

  1. 路径解析(快,5s 超时):which(Windows 用 where)取首行,立即向 renderer 发事件(probe.ts:8, 15-26dependency-manager.ts:132-145);
  2. 版本探测(慢,10s 超时):执行 <bin> <versionArgs>,从 stdout/stderr 首行用正则 /(\d+\.\d+[\d.]*)/ 抽版本号(dependency-manager.ts:26, 42-48)。

关键经验:agent CLI 的 --version 行为千奇百怪——有的输出到 stderr、有的探测超时、有的非零退出。Yoda 的 agentResolveStatus 因此把「路径已解析 / 超时但有 stdout / 非零退出但有输出」都判为 available(registry.ts:90-100,注释明确说明了这一容错策略)。每个 runtime 的 versionArgs 可定制:Cline 用 help、Jules 用 version 子命令、Mistral Vibe 用 -hsrc/shared/runtime-registry.ts:516, 563, 579)[一手源码]。

改进方向:

  • 多版本管理缺位:Yoda 目前只认 PATH 上的那一个二进制。CC 原生布局天然多版本,harness 可用绝对路径 ~/.local/share/claude/versions/<v> 直接启动特定版本绕过 symlink,实现「锁定已验证版本」[推断,需实测 CC 二进制脱离 symlink 启动时自更新与 doctor 的行为];
  • 版本兼容矩阵:Yoda 的 resume / hook 注入依赖各 CLI 的 flag 语义,应在 probe 后把版本号与「已验证版本区间」比对并按需降级功能,而非仅做展示;
  • 借鉴 Codex 的 dismissed_version:Yoda 的依赖升级提示应支持按版本忽略,避免重复打扰。

失效条件

  • CC 改动 autoUpdatesChannel 取值或新增渠道(本章源为 v2.1.88 重构源 + 2026-06 docs,重构源滞后线上约 2 个月,结论以 docs 交叉验证为准)
  • CC 改动 --version 输出格式(Yoda 抽取正则与本章示例都依赖它)
  • CC 原生安装的 VERSION_RETENTION_COUNT(当前 2)或 versions 目录布局变更
  • Codex 引入 stable 渠道或后台自更新(tui/src/updates.rs 大改即触发回归)
  • Codex version.json 字段或 20h TTL 调整
  • Codex UpdateAction 命令映射变化(如 brew cask 改名、standalone 安装 URL 迁移)

参考资料

  • CC docs:claude-code-docs/docs/setup.md(Update Claude Code / Configure release channel / Pin a minimum version / Disable auto-updates)
  • CC 重构源:src_2026-03-31/utils/autoUpdater.tsutils/config.tsutils/settings/types.tsutils/nativeInstaller/installer.tscomponents/NativeAutoUpdater.tsxcomponents/PackageManagerAutoUpdater.tsx
  • Codex 源(b89ce9a):codex-rs/tui/src/updates.rstui/src/update_action.rsinstall-context/src/lib.rscli/src/main.rsconfig/src/config_toml.rscodex-cli/bin/codex.js
  • Yoda 源:src/main/core/dependencies/{probe,dependency-manager,registry}.tssrc/shared/runtime-registry.ts

On this page