Skip to content

fix(codex): support API gateway account switching - #7918

Merged
loopx-agent merged 5 commits into
mainfrom
codex/api-gateway-account-switch-20261102
Nov 3, 2026
Merged

loopx-agent merged 5 commits into
mainfrom
codex/api-gateway-account-switch-20261102

Conversation

@loopx-agent

@loopx-agent loopx-agent commented Nov 2, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

The native Codex launcher and account transaction rejected every selected provider other than the built-in openai provider. A valid API gateway therefore failed before the App opened with Account 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" while model_provider still targeted an API relay, causing 401 Unauthorized from the relay.

Behavior

  • An API slot records the selected top-level Responses gateway provider.
  • Selecting an API slot automatically restores that gateway, its model catalog, and its URL.
  • Selecting a subscription slot automatically selects the built-in openai provider and removes the managed API endpoint.
  • Legacy API slots without route metadata are backfilled from the currently selected gateway before switching.
  • Allow clean HTTP(S) gateway URLs; unencrypted HTTP remains limited to loopback.
  • Reject definitions that shadow the built-in openai provider 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.
  • Package smokes: native host, host clients, provider routing, recovery contracts, operator smoke, and native observer all passed.
  • Ruff, compileall, and git diff --check passed.
  • Real local round trip verified: API slot restored openai_retry20; the subscription slot then switched to built-in openai, removed openai_base_url, matched a ChatGPT login, and reopened the App successfully.
  • No API fallback or 401 Unauthorized log entry appeared after the subscription launch.

Live model completion was not exercised by this smoke, so it is not claimed as verified.

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 loopx-agent left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>

@loopx-agent loopx-agent left a comment •

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 编码,使用 callable re.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.

@loopx-agent
loopx-agent merged commit f736bd2 into main Nov 3, 2026
12 of 34 checks passed
@loopx-agent
loopx-agent deleted the codex/api-gateway-account-switch-20261102 branch November 3, 2026 02:06
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.

1 participant