Skip to content

v2.9.9

Choose a tag to compare

@github-actions github-actions released this 19 Sep 16:40
· 47 commits to main since this release

v2.9.9 聚焦 Codex Turn-State 治理与社区修复:新增 凭据级 X-Codex-Turn-State 强制注入 + 用量日志注入 / 回带观测,以及 @sailstudio 贡献的 实验性 Turn-State 模板缓存(#690,两套机制可共存、运维手工值优先;合入 @Futureppo 贡献的 大号池下选号取消与 30 秒预算(#694@eshao731 贡献的 strict schema required 裁剪(#687@frank8ai 贡献的 Antigravity 自定义工具贯通(#681@Jayson1998 贡献的 图片任务精简结果接口(#692,以及 @bxb1337 / @Jayson1998 各自修复的 异步生图 102 心跳误判(#691 / #693

Features

  • 凭据级 X-Codex-Turn-State 强制注入 + 用量日志观测(@james-6-23。Codex 官方账号可在凭据里配置运维手工粘入的 turn-state 令牌:PATCH /api/admin/accounts/:id/scheduler 新增 codex_turn_statenull / 空串 = 关闭)与 codex_turn_state_models,账号编辑弹窗新增「Turn State 强制注入」卡片,带值时效倒计时(上游令牌实测约 1 小时失效;只有值真正换掉才重置起点,原样重提不重置)。客户端模型或映射后的上游模型任一命中名单(逗号分隔、大小写不敏感、结尾 * 前缀匹配,空名单 = 全部模型)即强制覆盖每个出站 Codex 请求:HTTP 在账号自定义头之后写头;WebSocket 同时写握手头与帧体 client_metadata.x-codex-turn-state,客户端回带值一律被覆盖。upstream_trace 每次尝试记录实际注入值与上游回带值(HTTP 读响应头,WS 逐帧扫 codex.response.metadata),落到 usage_logs 新列 injected_turn_state / upstream_turn_state,用量页 trace 悬浮与错误详情可查看复制。这套不自动学习、不回写;注入后回带列为空是预期(上游对回带值静默接受、不再铸新令牌),不代表没生效。

  • 实验性 Turn-State 模板缓存,自动替换降级令牌(#690,@sailstudio。系统设置新增 codex_turn_state_template_cache_enabled(默认关,设置页「Turn-State 模板缓存(实验性)」):开启后网关把上游铸发的健康长度令牌(个人号约 292、Team 约 332)按账号 + 模型抓进进程内存,出站遇到降级长度(个人约 312、Team 约 356)就替换,客户端没带则注入。抓取按严格 Fernet 信封解析,过期或签发时间在未来的直接拒收;接受时留 30 秒 TTL 余量;条目只在解析 / TTL 失败时清除;注入后连续两次正常响应清零 strike;codex_turn_state_account_modepersonal / team / auto 按套餐,默认 auto)决定长度档。出站顺序固定为 跨账号回带守卫 → 模板缓存 → 凭据级强制注入,运维手工值永远最后生效;HTTP Responses / Chat / Compact 与 WS 握手同序;可复用的 WS 池槽按模型隔离(握手会冻结令牌);每个 WS 回合重置审计上下文。用量日志新增 turn_state_overridden / turn_state_rewrite_note(如 312→292 / inject,不记原始令牌),显示在 UA 旁;设置项在写库成功后才发布。CODEX_TURN_STATE_TEMPLATE_LENGTH / REPLACE_LENGTH / INJECT_MODE / TTL / MAX_ENTRIES / LOG_DECISIONS / DRY_RUN 保留为可选调参,不再是主开关。审查阶段处理了 CodeRabbit 的五项发现。

  • 图片任务精简结果接口(#692,@Jayson1998。新增 GET /v1/images/jobs/:id/result,复用任务详情的 API Key 鉴权与归属校验,只返回状态、错误 / 警告、时间与生成图片的签名下载地址和尺寸,走独立查询不加载 prompt / params_json。背景是原详情接口把编辑任务的输入图展开成 data URL,轮询一次可能拉回 10 MB 以上。queued / running / succeeded / failed 均可查,无产物时 assets: [],include_cache=1 不增加内容,不存在或他人任务返回 404,原有任务接口不变,无迁移。

  • Antigravity Responses 适配器支持 freeform 自定义工具(#681,@frank8ai。Codex 以 wire_api = "responses" 走 Antigravity OAuth 账号时,一碰 apply_patch 就报「does not safely support input item type custom_tool_call」。根因是三段链路全缺:type: "custom" 的工具声明被静默丢弃、custom_tool_call / custom_tool_call_output 输入项落进不支持分支、响应侧只会产出 function_call。现在 custom 声明降级为单个 input 字符串参数的 function declaration 给 Gemini,调用与回执复用 function call / response 路径,并且只对请求里显式声明为 custom 的工具名把响应还原为 custom_tool_call 项(ctc_ag_ 前缀)+ response.custom_tool_call_input.delta / .done 事件、解包回原文,与 #286 建立的契约一致;未声明的工具保持 function_call 形态。

  • glass 主题 + 电影感主题切换动画(@james-6-23。新增半透明玻璃主题,主题设置页接入 CinematicThemeSwitcher 切换过渡。该提交实际已随 v2.9.8 二进制发布,本次补记。

Fixes

  • 请求取消或 30 秒预算耗尽时立即停止选号(#694,@Futureppo。大号池下请求已取消,选号仍会继续遍历候选并逐个查 Redis 模型冷却,因为冷却读取用的是独立后台 context,而 30 秒等待窗口要到首次同步扫描之后才生效。回归测试里 32 个候选、扫描前取消,legacy 路径仍调用过滤器 32 次,indexed 路径 64 次。现在取消传播到冷却读取与失效缓存清理,context 结束即停止昂贵过滤;首次扫描、队列等待、排除列表重试共用一个 30 秒预算,到期以 503「Account selection timed out. Retry after 1 second.」或对应 SSE / WebSocket 错误结束,不再继续额度扫描。预算只约束选号,不限制选中账号后的流式生成;已经开始的同步过滤器不会被强行抢占。贡献者已在生产环境实测。

  • strict schema 在无 properties 的节点上裁剪多余 required#687,@eshao731alignRequiredWithProperties 只在节点自带 properties 时对齐 required,字段名在 allOf / anyOf / oneOf / then / else 分支里或者根本没有来源时,多余项原样发往上游,gpt-5.5 报「Extra required key ... supplied」。现在按证据强弱三种处置:组合分支里声明过的保留、其余裁掉(不补齐,组合语义下补齐会改变 schema 含义);完全无来源且自称 object 的节点整个删除 requiredadditionalProperties: false 下永远无法满足);带 $ref / $dynamicRef 的节点保持原样,引用目标在自己的位置被独立清洗。只影响此前必被上游 400 的形态。

  • 异步生图不再把 HTTP 102 心跳当成最终失败(#691 @bxb1337,#693 @Jayson1998。进程内文生图 / 图生图复用 Images handler 跑在 httptest.ResponseRecorder 上,它会把先到的 102 Processing 心跳提交为最终状态,之后即使返回正常图片 JSON,任务仍被判失败,图片数据还被截断塞进 2000 字符的 error_message 无法恢复。现在内部记录器忽略临时 1xx 响应(保留 101 协议切换),完整保留最终状态与正文。两个 PR 用同名类型在同一文件不同位置各修了一遍,git 无冲突合并后 Go 编译报重复声明,main 短暂失败,已删除重复副本。

  • 两个 race 偶发失败的测试已稳定(@james-6-23。持续重试亲和清理测试在 ctx.Done() 后立刻断言,但 deadline 定时器是先取消 context 再跑 timeout cleanup,偶尔和解绑竞态;改为等待绑定之后注册的哨兵 cleanup。Codex 刷新持久化重试测试用无 busy_timeout 的裸 SQLite 连接删故障注入触发器,store 持有 WAL 写锁时立刻 SQLITE_BUSY,触发器留过整个重试预算;注入连接改用 busy_timeout(5000)。本地 -race -cpu 4 分别 30 / 60 次全过。

Upgrade Notes

  • 数据库迁移:usage_logs 新增四列 injected_turn_stateupstream_turn_stateturn_state_overriddenturn_state_rewrite_note,SQLite 与 Postgres 启动时自动 ADD COLUMN;system_settings 新增 codex_turn_state_template_cache_enabledcodex_turn_state_account_mode。账号凭据新增 codex_turn_state / codex_turn_state_models / codex_turn_state_set_at 三键,无需迁移。升级前按现有部署流程备份数据库。
  • 默认行为变化:模板缓存默认关闭,凭据级注入默认为空,两者都不配置时出站请求与 v2.9.8 完全一致。大号池部署下选号有了 30 秒硬预算,超时返回 503,依赖长时间排队拿号的下游请注意。strict schema 裁剪只作用于此前必被上游拒绝的形态。
  • 逃生阀:codex_turn_state_template_cache_enabled 关闭即停用模板缓存,CODEX_TURN_STATE_DRY_RUN=true 可只记日志不改头;账号 codex_turn_statenull 即停用强制注入。
  • 验证与升级建议:main 分支 CI(Go 测试、race、前端构建、CodeQL、安全扫描)全部通过。凭据级注入已在本地 PG + Redis 部署对真实上游冒烟(注入 / 回带两列均记到);模板缓存以单元测试覆盖,尚未在真实上游长时间验证,建议先开 DRY_RUN 观察日志;#694 已由贡献者在生产实测;#681 / #687 / #692 / #693 以单元测试覆盖。使用 Antigravity 账号跑 Codex、生产上有 strict schema 400 或大号池的部署建议升级。

致谢

Full Changelog: v2.9.8...v2.9.9