Skip to content

feat(codex): 增加会话级 Fast Mode 控制 - #619

Open
MascaraDeemo wants to merge 5 commits into
deepcoldy:masterfrom
MascaraDeemo:fix/codex-fast-cold-start
Open

feat(codex): 增加会话级 Fast Mode 控制#619
MascaraDeemo wants to merge 5 commits into
deepcoldy:masterfrom
MascaraDeemo:fix/codex-fast-cold-start

Conversation

@MascaraDeemo

@MascaraDeemo MascaraDeemo commented Jul 27, 2026

Copy link
Copy Markdown

背景

将 Codex 原生 /fast 直接作为 passthrough 时,命令可以切换 CLI,但 Botmux 不会向飞书回复;同时 Codex 会把服务等级写进 CODEX_HOME,导致新 Session 继承上一个 Session 的 Fast Mode。

变更

  • /fast 提升为 Botmux daemon 命令,支持 /fast/fast on/fast off/fast status
  • Fast Mode 持久化在当前 Botmux Session;新 Session 默认关闭,互不影响。
  • Codex 启动时以进程级 service_tier 覆盖固定当前 Session 的服务等级。
  • 实际状态变化时才向运行中的 Codex 发送一次原生 /fast,保证 on/off 幂等。
  • /fast 作为新话题首条消息时只记录设置,不为控制命令空启动 CLI;下一条普通消息按该设置启动。
  • 补齐中英文帮助和领域术语。
  • Aiden 网关无法透传 Codex config,因此明确拒绝该组合;其余已知 wrapper 保持原有透传/改写行为。

验证

  • 相关测试:769 passed。
  • 完整单测:10845 passed,1 个既有 Codex App 超时清理用例在高并发运行中出现一次时序抖动,单独复跑 11/11 passed。
  • pnpm build 通过。
  • git diff --check 通过。

@MascaraDeemo
MascaraDeemo requested a review from deepcoldy as a code owner July 27, 2026 08:48
@MascaraDeemo
MascaraDeemo force-pushed the fix/codex-fast-cold-start branch from 5f41c59 to fa1ff4e Compare July 27, 2026 09:11
@MascaraDeemo MascaraDeemo changed the title fix(codex): allow /fast to start sessions feat(codex): 增加会话级 Fast Mode 控制 Jul 27, 2026
@MascaraDeemo

Copy link
Copy Markdown
Author

已按 review 的 4 个问题补齐,修复提交:432a9c08。

  • RPC executor 现在从 model/list 解析当前模型的 Fast tier id,并在 thread/startthread/resumeturn/start 及运行时 thread/settings/update 中传递;不再把展示名 fast 当协议值。
  • /fast 改为确认式 set_fast_mode IPC:启动期排队,RPC 等 app-server ACK;原生 TUI 用显式 tier 重启并等新 prompt,更新 restart snapshot 后才 ACK。Session 仅在 executor 状态确认后持久化和回复。
  • 不支持 Fast tier 的模型 fail closed,不修改状态。
  • /fast status、非法参数及非 Codex 请求不再创建 worker:null phantom session;只有合法 Codex enable 且 model catalog preflight 成功才允许预创建。

验证:10,870 unit tests passed(41 platform skips);pnpm build passed;本机真实 Codex 0.142.3 model/list probe 返回 tier priority

@MascaraDeemo

Copy link
Copy Markdown
Author

补充修复了 legacy / cold Fast Session 的迁移边界(06305768):

  • 新增 executor-confirmed state version;仅 capability probe 的冷配置不会被误认为已经生效。
  • native 模式首次遇到未确认 Fast 状态时,会精确 kill + verify 旧 persistent target,带 concrete service tier 冷恢复;到真实 prompt 后才持久化确认标记。
  • RPC 模式继续以 app-server 协议 ACK 为确认;后续 daemon restart 可正常热重连,不会重复重建已确认 pane。
  • /fast on 对 legacy fastMode:true 但未确认的 Session 会重新走 ACK 路径。

验证:全量 unit 704 files / 10,872 tests passed(41 skipped);pnpm buildtsc --noEmitgit diff --check 均通过。devbox 已部署 0630576,Loxy / Eve / dashboard 均稳定 online。

@deepcoldy

Copy link
Copy Markdown
Owner

首审(Claude)— 无阻塞,待 codex 复审 + 申晗确认

Review head: 06305768 · MERGEABLE(BLOCKED = 缺 approval)· 相对 origin/master b30e894 merge-tree 干净。

改动逻辑(白话)

把 Codex 原生 /fast(切换 service_tier 服务档位)从裸 passthrough 提升为 botmux daemon 命令,解决两个问题:① 透传后飞书无回执;② Codex 把档位写进 CODEX_HOME 导致新会话继承旧会话的 Fast 状态。改后:/fast/on/off/status 均有飞书回执;状态只挂当前话题 Session(新 Session 默认关);仅 Codex 生效(非 Codex / adopt / Aiden 网关明确拒绝)。

三条关键链路:

  1. 状态与去串味:Session 新增 fastMode + fastServiceTier,持久化随重启/恢复走;启动用进程级 -c service_tier=<id> 盖住 CODEX_HOME 全局值。
  2. "fast" 不是真档位 id(第 2 版关键升级):"Fast" 只是显示名,真 id 要从 app-server model/list 按当前模型查(OpenAI 系目前是 priority);查不到 fail-closed。
  3. executor-confirmed:daemon 发带 requestId 的 set_fast_mode,等 worker 真 ACK 才落库+回执,超时 fail-closed。RPC 模式走协议 thread/settings/update;原生模式用带显式档位的重启替换旧进程,回到真 prompt 才确认。第 3 版补 fastModeStateVersion:1 版本标记,legacy/冷启动记录会强制 kill 未确认的存活 pane 并用显式档位重开。

影响面

  • 跨 CLI:fastModeSessionSupported 静态门卡死仅 codex;其它 CLI 传 undefined 不注入 → 零影响。codex-app 独立 buildArgs 不含该字段。
  • Wrapper:aiden 剥离 / cjadk 改写的白名单动态识别本次真实注入的档位 id,不误伤用户自带 -c service_tier=;ttadk/原生透传照旧。
  • 跨后端:tmux/herdr/zellij 存活 pane 重连场景由 fastModeStateVersion 版本化和解闭环。
  • 冷启动:/fast on 首条消息先做模型目录预检才建 Session;status/off/非法参数走 sessionless 不建幽灵会话;预检与启动的 workingDir 被 pin 一致。

验证

  • pnpm build 绿。
  • PR 触及的 11 个测试文件全绿(841 用例):RPC 引擎用假 app-server 验 model/listprioritythread/settings/update 序列 ['priority', null];daemon 路由级验冷启动预检 + 幽灵会话零创建;handshake 验只认精确 ACK + 超时 fail-closed;wrapper 验动态档位 id 精确剥离/改写且不误伤用户 config。
  • 实测 codex-cli 0.145.0(PR 目标版本):-c service_tier="fast"--strict-config 下不报错(合法 config key),对照组乱写 key 报 unknown configuration field — 佐证注入的是真实可识别的 config。
  • 回归:PR head 全量失败文件 == 干净 master 基线(scheduler / schedule-card-model / v3-distillation-runner / fs-policy-bwrap 等全为时区/bwrap-root/网络类环境失败,与本 PR 文件零关系)→ 零回归。
  • 安全:/fast 内容只喂正则解析器,无 shell 插值;service_tier 值只有 default 或目录解析出的 id,JSON 序列化进 -c,无注入面。

结论

✅ 方向正确、实现扎实、测试判别力足够,代码层面无阻塞(P0/P1)问题。

🟡 一处 P3(测试质量 nit,非产品 bug):test/codex-fast-worker-wiring.test.ts 中验 buildArgs 区块的用例,区块结束分隔符写成字面量 '\n });'(反斜杠-n),真实源码是真换行 → indexOf 返回 -1 → 截出的 region 实为文件后半段约 18 万字符而非那 ~637 字符的 buildArgs 块。三个 toContain 仍通过(字符串在别处也存在)但断言被弱化。产品代码正确(真实 buildArgs 调用确含 remoteWsUrl/remoteThreadId/fastServiceTier: cfg.fastServiceTier)。建议改真换行分隔符收紧,不阻塞。

@codex 独立复审 + 申晗确认后方可合码。

@chatgpt-codex-connector

Copy link
Copy Markdown

To use Codex here, create a Codex account and connect to github.

@deepcoldy

Copy link
Copy Markdown
Owner

复审收敛更新(Claude)— 两审一致 REQUEST CHANGES

codex 独立复审在 head 06305768 抓到 3 个 P1 blocker。我逐条对照代码独立核实,全部 CONFIRMED——我此前首审判"无阻塞"漏了这三个边界,更正如下。当前不可合

P1-1 · fastModeStateVersion 只和解 ON,旧的 OFF/default 存活 pane 仍不可信

  • worker.ts:9994 和解条件 msg.fastMode === true && (...),只覆盖启用态。
  • Premise 成立:升级前 /fast 是裸 passthrough(可切档位),且 Codex 把档位持久化进 CODEX_HOME(PR 背景自述)。因此旧 Codex pane 可能实际在 Fast,而 Session 记录里无 fastMode/version → daemon 重启后 init 折成 fastMode:falsefastModeNeedsReconciliation=false → tmux/herdr/zellij 直接重连旧 Fast pane,而 reattach 根本不执行启动参数 service_tier(worker.ts:7264 明确说明)。
  • 后果:/fast status 报"关闭"但 executor 仍 Fast;显式 /fast off 也修不了(command-handler.ts:1933 对 unconfirmed OFF 判 needsApply=false,只落库 false);与 types.ts:344 注释矛盾。
  • 修法(采纳 codex):version 覆盖 ON 和 OFF/default 两态,缺 version 时两种目标都必须确认;原生 persistent pane 精确替换,RPC resume 以 serviceTier:null ACK 后发布 version 1;/fast off 对 unconfirmed false 也走 apply。

P1-2 · Riff 假成功(远端任务完全没应用 Fast)

  • fastModeSessionSupported()(fast-mode-control.ts:26)只看 CLI/wrapper/adopt,未看 backendcliId=codex + backendType=riff 被判支持。
  • RiffBackend.spawn() 明确 ignoring bin/args(riff-backend.ts:415);task-execute config 只有 model/reasoning/env/repos/setup,serviceTier(riff-backend.ts:586-628);worker 对 riff 立即 markPromptReady()(worker.ts:8482)→ 发 fast_mode_state + success ACK + 持久化 version 1,而远端 Codex 从未收到 tier。
  • 修法:能力门显式拒绝 Riff(直到 Riff API 真正支持并 ACK 该字段)+ 补路由/worker 回归测试。

P1-3 · 原生 replacement 不出 prompt 时,fastModeChangeInFlight 永久卡死整个会话

  • 原生切换在 worker.ts:5230spendingRestartFastModeAck + fastModeChangeInFlight=true,markPromptReady()(5297)清 latch
  • codex supportsTypeAhead:true → 首 prompt 90s 硬超时走 flush 分支(worker.ts:8469)而非 markPromptReady;flushPending() 见 Fast barrier → flushPendingFastModeChanges()fastModeChangeInFlight=true 立即 return。daemon 的 120s 超时只删 daemon 侧 waiter(fast-mode-handshake.ts:22),不 cancel worker 请求。
  • restartCliProcess 只在 throw 才走 catch(5243)回滚;"spawn 成功但卡在启动菜单/空屏/不匹配 prompt"不 throw → 无兜底。
  • 后果:用户 120s 后收失败,但 worker latch 恒 true,后续普通消息与 passthrough 无限排队;更晚出现 prompt 时还可能"已回复失败"后偷偷提交旧请求。
  • 修法:worker 侧与 requestId 绑定的有界 watchdog/cancel——超时回滚 staged config、清 pending ACK + in-flight、拒绝该请求并继续排队,同时防晚到 prompt 提交过期请求。

P3(已认同,非 blocker)

test/codex-fast-worker-wiring.test.ts:19 用字面量 '\n });'(反斜杠-n)当区块结束符,实测 indexOf 返回 -1、region 长 181137(应为 637)→ 三个 toContain 从文件后半段"捞到"字符串,断言未锁定 buildArgs 区块。产品代码正确,顺手改真换行分隔符即可。

结论

两审一致 REQUEST CHANGES。需作者修复 3 个 P1 + 补对应回归测试(Riff 路由/worker + P1-3 超时 watchdog),复测后再复审,最终等申晗确认。

@MascaraDeemo

Copy link
Copy Markdown
Author

已按复审 3 个 P1 + P3 全部收敛,修复提交:46e6aa30

  • P1-1(OFF/default 和解)fastModeStateVersion 现在覆盖 ON 与 OFF/default 两态;缺 version 时两种目标都会进入 executor-confirmed 路径。RPC 以 serviceTier:null 的协议 ACK 确认 default;native 会精确替换 persistent pane,到真实 prompt 后才发布 version 1。显式 /fast off 对 unconfirmed OFF 也会重新 apply。
  • P1-2(Riff 假成功):Fast capability gate 新增 effective backend 维度,command handler、daemon 路由、worker 三层都传入配对后的 backend;Riff 明确 fail closed,且补了 command/新话题路由/worker 回归。现有配对约束会把非法 codex + riff 原始配置规范化到本地 backend,真正的 Riff executor 是 cliId=riff + backend=riff;helper 仍额外直接拒绝 backendType=riff 作为纵深防御。
  • P1-3(native 永久 latch):新增 requestId 绑定的 worker watchdog(110s,早于 daemon 120s)。超时先原子摘除 pending ACK,恢复 staged config 并回复失败,再以旧 service tier/default 发起补偿重启;重启后清 fastModeChangeInFlight 并继续队列。daemon 自己超时时也会发送 cancel_fast_mode,可移除尚未执行的同 id 请求或触发同一 native 回滚。晚到 prompt 因 pending ACK 已摘除,不能再提交过期请求。
  • P3:buildArgs 测试改用真实换行结束符,并断言 start/end 有效与 region < 2000,不再从 18 万字符后半文件误命中。

验证:

  • 相关回归:9 files / 366 tests passed。
  • 全量 unit(Node 26 的 loader 弃用告警用 NODE_NO_WARNINGS=1 隔离,受控并发):705 files / 10,881 tests passed,41 skipped。
  • 已知 codex-app-threads 高并发时序用例另做连续 10 轮复跑:110/110 passed
  • pnpm buildtsc --noEmitgit diff --check 均通过。

Resolve Fast Mode init payload and input-barrier integration with the new Codex runner freshness queue.
@MascaraDeemo

Copy link
Copy Markdown
Author

已同步最新 origin/master (6dcca31b) 并解决冲突,合并提交:4d20b15e

冲突处理:

  • DaemonToWorker.init 同时保留 Fast Mode 状态字段与上游 runner freshness / restart 字段。
  • Fast Mode 配置屏障接入上游新的 freshnessInputQueueraw_input 仍由 freshness queue 统一入队,同时不会越过待确认的 Fast Mode 切换。

验证:

  • TypeScript tsc --noEmit 通过。
  • 交叉面回归:17 files / 895 tests passed。
  • 完整 unit:721 files / 11,020 tests passed(41 platform skips)。本机 Node 26 + tsx 4.21 会向子进程 stderr 输出 DEP0205,完整验证用 NODE_OPTIONS=--no-deprecation 屏蔽该纯环境警告;相关 3 个测试文件及依赖相对 master 未修改。
  • pnpm buildgit diff --check 通过。

GitHub 当前状态:MERGEABLE;仅因尚缺 approval 显示 BLOCKED

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants