v2.9.9
v2.9.9 聚焦 Codex Turn-State 治理与社区修复:新增 凭据级
X-Codex-Turn-State强制注入 + 用量日志注入 / 回带观测,以及 @sailstudio 贡献的 实验性 Turn-State 模板缓存(#690),两套机制可共存、运维手工值优先;合入 @Futureppo 贡献的 大号池下选号取消与 30 秒预算(#694)、@eshao731 贡献的 strict schemarequired裁剪(#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_state(null/ 空串 = 关闭)与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_mode(personal/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,@eshao731)。alignRequiredWithProperties只在节点自带properties时对齐required,字段名在allOf/anyOf/oneOf/then/else分支里或者根本没有来源时,多余项原样发往上游,gpt-5.5报「Extra required key ... supplied」。现在按证据强弱三种处置:组合分支里声明过的保留、其余裁掉(不补齐,组合语义下补齐会改变 schema 含义);完全无来源且自称object的节点整个删除required(additionalProperties: 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_state、upstream_turn_state、turn_state_overridden、turn_state_rewrite_note,SQLite 与 Postgres 启动时自动ADD COLUMN;system_settings新增codex_turn_state_template_cache_enabled、codex_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_state置null即停用强制注入。 - 验证与升级建议:main 分支 CI(Go 测试、race、前端构建、CodeQL、安全扫描)全部通过。凭据级注入已在本地 PG + Redis 部署对真实上游冒烟(注入 / 回带两列均记到);模板缓存以单元测试覆盖,尚未在真实上游长时间验证,建议先开
DRY_RUN观察日志;#694 已由贡献者在生产实测;#681 / #687 / #692 / #693 以单元测试覆盖。使用 Antigravity 账号跑 Codex、生产上有 strict schema 400 或大号池的部署建议升级。
致谢
- @sailstudio — 实验性 Turn-State 模板缓存(PR #690)
- @Futureppo — 选号取消传播与 30 秒预算(PR #694)
- @eshao731 — strict schema
required裁剪(PR #688,fix #687) - @frank8ai — Antigravity 自定义工具贯通(PR #681)
- @Jayson1998 — 图片任务精简结果接口、异步生图 102 心跳修复(PR #692 / #693)
- @bxb1337 — 异步生图 102 心跳修复(PR #691)
- @james-6-23 — 凭据级 Turn-State 强制注入与观测、重复声明清理、测试去抖
Full Changelog: v2.9.8...v2.9.9