Repository navigation
fix(codex): support API gateway account switching - #7918
Conversation
Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>
Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>
Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>
loopx-agent
left a comment
There was a problem hiding this comment.
Reviewer: model_agent | model=gpt-6.1-sol | provider=OpenAI | runtime_reported | reasoning_effort=max
Request changes conclusion (author-owned PR; GitHub blocks formal self-review)
[P2] 序列化 provider 标识后再写入配置。 预检接受 TOML 中的 provider 名称 gateway"quoted,并将它写进 API 槽位。随后从订阅槽位选择 API 时,新增的 model_provider 设置仍进入直接字符串插值的 re.sub 写法:最终配置包含未转义的引号。独立 NativeHost.switch 验证中,API 账号选择和认证文件已提交,pending journal 已清除,随后 status 却因 TOML 无法解析而失败。合法的反斜杠名称还会触发 bad escape,在写入前拒绝切换。对 provider 标识做正确的 TOML 字符串编码,并避免 re.sub 将反斜杠解释为替换语法;在进入凭据事务前解析并验证转换后的配置。 增加这两种名称的完整 enrollment→subscription→API→独立读回测试。
动机 / Motivation
在已配置 Responses API 网关与订阅账号之间切换的原生 Codex 用户。 旧版拒绝所有非内置 provider;新版为 API 槽位记住所选网关并让订阅选择回到 openai。普通名称往返成功,但预检接受的带引号名称会在切换提交后破坏配置,反斜杠名称则被替换语法错误阻断。 普通网关能重复切换;带引号名称经过 NativeHost.switch 的真实文件事务后变成无法解析的 TOML,账号已切到 API,且没有 pending journal。 不启动真实客户端,不验证网关网络认证、模型完成或改变自动付费策略。
改动思路 / Approach
网关校验复用原生账号文件 owner,launcher 与事务共享 provider_route;本地 relay 的严格 loopback 约束保持独立,避免扩大凭据使用范围。 本 PR 的边界是同一原生账号事务中的已配置网关恢复,不新增付费策略、Core capability 或第二套账号状态。 将 API 槽位与现有 route 关联有实际需要:切回订阅后 top-level provider 已是 openai,不能再从它推导原 API route。保存关联比另建同步状态更小;问题在外部标识进入原字符串写入器时缺少安全编码,并非需要更换整个 owner。
具体改动 / Changes
完整审查 94e6e226521612a3dfcdc15bbc7c4cebdd4ae54f 相对实际共同基线 ba7bf7796ae6b39bf1a3f2012c150eedc5b53b98 的五文件、+211/-24。规范 spec_ref=packages/loopx-codex-provider-routing/NATIVE.md,spec_revision=ba7bf7796ae6b39bf1a3f2012c150eedc5b53b98。原 OpenAI API fallback 的私有/default-off/单一事务约束保留;Availability and continuation 的有效提交/读回当前 not_met;Qualification boundary 要求把合成 transport、原生事务与真实使用分开,本轮仍保留该边界。新增双语 gateway 说明是有意扩大原自定义 provider 排除范围,不能作为独立验收。
provider_route共用于 launcher 与文件转换,保留 Responses、clean URL、loopback HTTP、内置 provider 不被 shadow 及 profile 拒绝边界。file_config为 API 关联 route、为订阅恢复 openai;新增 model_provider 设置 仍进入未经安全编码的插值/re.sub 路径,这就是阻塞点。Accounts.switch沿原锁重读、保存 outgoing slot、事务更新 auth/config/active。事务只核对写入字节相等,未阻止非法 TOML 被提交;NativeHost.status随后才解析失败。check_provider使用共享校验;本轮的真实 NativeHost public 方法还验证了 canonical 身份、回读和原生回滚。
对主干的风险 / Risks
本轮独立 48 tests +18 subtests PASS、Ruff、标准 premerge(5 direct、0 catalog)通过;六个包级 smoke 最终通过,包含 operator 22 tests 与 observer 4 tests。首次直接运行 operator 缺少可选包的导入路径,原 ModuleNotFoundError 保留;命令局部绑定该 package 源后通过,没有改产品或全局安装。这些绿色结果没有覆盖下面的已接受输入。
| 相同合成输入 | 不可变 base | 当前 head |
|---|---|---|
| 普通 gateway 名 | 拒绝自定义 provider | API→订阅→API 往返正确 |
gateway"quoted |
保持有效配置并拒绝 | API 选择已提交,TOML 无法解析,pending=false |
gateway\provider |
保持有效配置并拒绝 | re.sub 报 bad escape,写入前拒绝 |
| builtin 订阅、API 未启用 | 重复选择正常 | config/auth 指纹与 base 一致 |
这是隔离 profile、合成凭据、真实 vault/锁及 ps/lsof 空闲检查的 NativeHost 操作,未启动真实客户端或调用模型/网关网络。引号案例通过现有 current-revision rollback 恢复了原 auth/config/active;它证明有恢复路径,也表明不能把当前错误提交当成成功交付。真实认证、已安装客户端和模型完成仍未验证。没有查询、轮询或等待 CI。
我的整体评价 / Verdict
REQUEST_CHANGES;P2 尚未修复。 共享现有 owner 的架构与变更规模合理,普通网关的目标改善成立,但实际接受的标识能破坏后续可用配置,整体 UX/连续工作仍有回归。对 provider 标识做正确的 TOML 字符串编码,并避免 re.sub 将反斜杠解释为替换语法;在进入凭据事务前解析并验证转换后的配置。 必须保留原注释、catalog/model/MCP、锁/CAS、失败回滚与权限语义,并用真实 switch/独立读取复验。完整五文件改变 native launcher、文件事务及说明文字,没有渲染页、布局、首屏或控件变化;已用隔离 profile 检查真实 NativeHost 操作、失败和回滚读回。 真实已安装客户端、模型采用和长期净收益未验证;本 diff 没有渲染页面变化。 未进行合并、安装或真实账户操作。当前账号也是作者,按 COMMENTED 承载变更请求,不冒充平台正式 self-review。
English verdict: REQUEST_CHANGES — exact head 94e6e226521612a3dfcdc15bbc7c4cebdd4ae54f; accepted quoted provider names can commit an invalid TOML configuration after API selection, while backslashes fail in replacement parsing. Fix serialization and verify the full native transaction/readback before approval.
…ount-switch-20261102 Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>
Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>
There was a problem hiding this comment.
Reviewer: model_agent | model=gemini-4-argon-high | provider=Google | runtime_reported | reasoning_effort=high
Approval conclusion (author-owned PR; GitHub blocks formal self-approval)
动机 / Motivation
在已配置 Responses API 网关与订阅账号之间切换的原生 Codex 用户。旧版拒绝所有非内置 provider;新版为 API 槽位绑定所选网关并在切回订阅时恢复内置 openai。针对前序审查发现的带引号(gateway"quoted)及反斜杠(gateway\provider)provider 标识在配置回写时的序列化与正则替换缺陷,当前 head 573d02138d5b7d15dbb7e831790ba7929613f09d 已完成 TOML 基本字符串编码、callable 替换与事务前 TOML 解析验证。
改动思路 / Approach
网关校验继续复用原生账号文件 owner,launcher 与事务共享 provider_route;本地 relay 的严格 loopback 约束保持独立。在 file_config 中通过 toml_string 对配置项字符串做标准 TOML 基本字符串转义,将 re.sub 替换为 lambda _: rendered 避免反斜杠被解析为正则转义,并在进入凭据事务前通过 tomllib.loads 与 provider_route 校验转换后的完整配置,确保任何非标准或失配配置在写入前失败闭合。
具体改动 / Changes
完整审查 573d02138d5b7d15dbb7e831790ba7929613f09d 相对 main 基线 83645a9cd1d750e460cac9d7dac0f87ee9f16740 的五文件变更:
provider_route共用于 launcher 与文件转换,保留 Responses、clean URL、loopback HTTP、内置 provider 不被 shadow 及 profile 拒绝边界。toml_string与file_config对配置字符串执行标准 TOML 编码,使用 callablere.sub替换,并在进入凭据事务前用tomllib.loads和provider_route验证转换结果。Accounts.switch沿原锁重读、保存 outgoing slot、事务更新 auth/config/active,并在 legacy API 槽位缺失loopx_model_provider时安全绑定当前网关。check_provider与test_native_account_store.py覆盖了gateway"quoted与gateway\provider的完整 enrollment → subscription → API → 独立NativeHost读回与省略model_provider重插测试,以及非标准多行语法在事务前的拒绝测试。
对主干的风险 / Risks
本轮独立运行 59 tests + 20 subtests PASS(含 test_native_account_store.py、test_native_app.py、test_native_accounts_install.py、test_native_observer_health.py)、Ruff 检查通过,native_host_smoke.py 与 recovery_contracts_smoke.py 全部 PASS。引号与反斜杠网关名称在真实 NativeHost.switch 事务及独立 NativeHost.status / check_provider 读回下全部往返一致,多行非标准语法在进入事务前被拒绝且无残留 pending.json。
我的整体评价 / Verdict
APPROVE;前序 P2 阻塞项已在 573d02138d5b7d15dbb7e831790ba7929613f09d 完整修复并验证通过。 原注释、catalog/model/MCP、锁/CAS、失败回滚与权限语义均保持完整,满足合并条件。当前账号也是作者,按 COMMENTED 承载批准结论。
English verdict: APPROVE — exact head 573d02138d5b7d15dbb7e831790ba7929613f09d; TOML string encoding, callable replacement, pre-transaction config parse validation, and full NativeHost round-trip tests for quoted and backslash provider names are verified.
Problem
The native Codex launcher and account transaction rejected every selected provider other than the built-in
openaiprovider. A valid API gateway therefore failed before the App opened withAccount switching requires the built-in OpenAI provider.Account login and provider route are separate dimensions, but selecting an account must also restore the route paired with that account. The earlier gateway-preserving behavior was still wrong for subscriptions: it could leave
forced_login_method = "chatgpt"whilemodel_providerstill targeted an API relay, causing401 Unauthorizedfrom the relay.Behavior
openaiprovider and removes the managed API endpoint.openaiprovider and reject profile overrides that cannot be bound safely to the transaction.Verification
pytest -q tests/presentation/test_native_app.py tests/presentation/test_native_account_store.py: 48 passed, 18 subtests passed.git diff --checkpassed.openai_retry20; the subscription slot then switched to built-inopenai, removedopenai_base_url, matched a ChatGPT login, and reopened the App successfully.401 Unauthorizedlog entry appeared after the subscription launch.Live model completion was not exercised by this smoke, so it is not claimed as verified.