Skip to content

fix(core): keep active runtime model in default getAllConfiguredModels listing#5729

Merged
yiliang114 merged 1 commit into
QwenLM:mainfrom
wenshao:fix/runtime-model-available-models
Jun 23, 2026
Merged

fix(core): keep active runtime model in default getAllConfiguredModels listing#5729
yiliang114 merged 1 commit into
QwenLM:mainfrom
wenshao:fix/runtime-model-available-models

Conversation

@wenshao

@wenshao wenshao commented Jun 23, 2026

Copy link
Copy Markdown
Collaborator

What this PR does

Fixes a core regression where an active runtime model is dropped from ModelsConfig.getAllConfiguredModels() when its authType has no registry models. In the default (no explicit authTypes filter) case the method now includes the active runtime model's authType in the iteration, so it is enumerated alongside registry models. Callers that pass an explicit authTypes filter are unchanged — they still get exactly the requested set (a matching runtime model was, and remains, included by the existing loop).

Why it's needed

#5089 changed the default branch of getAllConfiguredModels from Object.values(AuthType) to this.modelRegistry.getAuthTypes(), which only returns auth types that have registry models. A runtime model resolved purely from env/CLI overrides — e.g. OPENAI_API_KEY / OPENAI_BASE_URL / OPENAI_MODEL with no modelProviders entry — has authType: 'openai', which has no registry models, so 'openai' is absent from the iteration and the runtime model is never emitted. This happens even when the runtime model is the current/active model: currentModelId points at $runtime|openai|…(openai) while availableModels contains only coder-model(qwen-oauth).

User-facing impact: ACP clients (Zed / VSCode companion) cannot see or re-select the active openai model; the interactive /model picker (modelCommand.ts calls getAllConfiguredModels() with no filter) omits it too; and the ACP integration test supports session/set_config_option for mode and model fails expect(openaiModel).toBeDefined() on every main run since #5089. The failure is deterministic, not a parallel-settings race — which is why #5721 (assertion shape) and #5724 (QWEN_HOME isolation) did not fix it.

Reviewer Test Plan

How to verify

  1. New core unit regression test in packages/core/src/models/modelsConfig.test.ts captures a runtime openai model (no modelProviders) and asserts it appears in getAllConfiguredModels(); a second test asserts an explicit authTypes filter does not inject it. Run: npx vitest run packages/core/src/models/modelsConfig.test.ts.
  2. The previously failing integration test passes locally against the bundle with fake creds:
npm run bundle
QWEN_SANDBOX=false OPENAI_API_KEY=sk-dummy OPENAI_BASE_URL=https://example.invalid/v1 OPENAI_MODEL=my-openai-model \
  npx vitest run --root ./integration-tests cli/acp-integration -t "supports session/set_config_option for mode and model"

Expected: pass — availableModels now contains the …(openai) runtime model that the test looks for.

Evidence (Before & After)

Non-UI change (core model-listing logic). Observed via a script driving the real ACP protocol (initializeauthenticate openaisession/new) with OPENAI_* set:

Before — runtime model is the current model but missing from the list:

currentModelId: "$runtime|openai|my-openai-model(openai)"
availableModels:  [ "coder-model(qwen-oauth)" ]          # openai runtime model dropped

After — runtime model is enumerated:

availableModels:  [ "coder-model(qwen-oauth)", "$runtime|openai|my-openai-model(openai)" ]

The new unit test fails with expected undefined to be defined (the exact CI error) before the fix and passes after. Historically, the pre-#5089 E2E run had all 11 acp-integration tests green; every run since fails this one assertion.

Tested on

OS Status
🍏 macOS ⚠️
🪟 Windows ⚠️
🐧 Linux

Linux: ran the new unit tests, the target integration test (against the bundle, fake OPENAI_*), plus tsc / prettier / eslint — all green. macOS / Windows not run locally; the authoritative check is CI.

Environment (optional)

npm run bundle + vitest run --root ./integration-tests with fake OPENAI_* env and QWEN_SANDBOX=false. Core unit tests via vitest run at repo root. No real provider auth needed (the assertions exercise model enumeration, not inference).

Risk & Scope

  • Main risk or tradeoff: None expected. The behavior change is limited to the default listing — it adds the active runtime model when it would otherwise be missing. Registry models and ordering (qwen-oauth first) are unchanged, and explicit-filter callers (getFastModel, etc.) are unaffected.
  • Not validated / out of scope: macOS/Windows not run locally (covered by CI). Inference-driven ACP tests not re-run (need real provider auth).
  • Breaking changes / migration notes: None. No production dependency changes.

Linked Issues

Fixes the deterministic ACP set_config_option failure introduced by #5089. Follow-up to the prior attempts #5721 and #5724 (which addressed the symptom, not the cause). Complementary to #5728: that PR hardens the test by injecting an openai modelProviders entry (giving openai a registry model, which sidesteps the symptom); this PR fixes the underlying product bug, so the original assertion and real env-only-auth users work without any injection. The two do not conflict.

中文说明

这个 PR 做了什么

修复一个核心回归:当当前激活的 runtime 模型所属的 authType 没有 registry 模型时,它会被 ModelsConfig.getAllConfiguredModels() 丢弃。现在在默认分支(未显式传 authTypes 过滤)里,会把当前激活 runtime 模型的 authType 也纳入遍历,使其与 registry 模型一起被枚举。显式传 authTypes 的调用方行为不变——仍然只得到所请求的集合(匹配的 runtime 模型本来就由原有循环包含)。

为什么需要

#5089getAllConfiguredModels 的默认分支从 Object.values(AuthType) 改成了 this.modelRegistry.getAuthTypes(),而后者只返回有 registry 模型的 authType。一个仅由环境变量 / CLI 覆盖解析出来的 runtime 模型(例如只设了 OPENAI_API_KEY / OPENAI_BASE_URL / OPENAI_MODEL、没有 modelProviders 配置)的 authType'openai',而 openai 没有 registry 模型,于是 'openai' 不在遍历集合里,这个 runtime 模型就永远不会被输出。即使它就是当前激活模型也一样:currentModelId 指向 $runtime|openai|…(openai),而 availableModels 里只有 coder-model(qwen-oauth)

用户可见影响:ACP 客户端(Zed / VSCode companion)看不到、也无法重新选中当前激活的 openai 模型;交互式 /model 选择器(modelCommand.ts 无参调用 getAllConfiguredModels())同样漏掉它;ACP 集成测试 supports session/set_config_option for mode and model#5089 起在每次 main 跑都会在 expect(openaiModel).toBeDefined() 失败。这是确定性失败,不是并发 settings 竞争——所以 #5721(改断言形态)和 #5724(隔离 QWEN_HOME)都没修好。

评审验证

如何验证

  1. packages/core/src/models/modelsConfig.test.ts 新增核心单测回归:捕获一个 runtime openai 模型(无 modelProviders),断言它出现在 getAllConfiguredModels() 里;另一个用例断言显式 authTypes 过滤不会注入它。运行:npx vitest run packages/core/src/models/modelsConfig.test.ts
  2. 之前失败的集成测试现在用 fake 凭证对着 bundle 本地通过:
npm run bundle
QWEN_SANDBOX=false OPENAI_API_KEY=sk-dummy OPENAI_BASE_URL=https://example.invalid/v1 OPENAI_MODEL=my-openai-model \
  npx vitest run --root ./integration-tests cli/acp-integration -t "supports session/set_config_option for mode and model"

预期通过——availableModels 现在包含测试要找的 …(openai) runtime 模型。

证据(Before & After)

非 UI 改动(核心模型列表逻辑)。通过一个驱动真实 ACP 协议的脚本观察(initializeauthenticate openaisession/new),并设置了 OPENAI_*:

Before——runtime 模型是当前模型,却不在列表里:

currentModelId: "$runtime|openai|my-openai-model(openai)"
availableModels:  [ "coder-model(qwen-oauth)" ]          # openai runtime 模型被丢弃

After——runtime 模型被枚举出来:

availableModels:  [ "coder-model(qwen-oauth)", "$runtime|openai|my-openai-model(openai)" ]

新单测在修复前以 expected undefined to be defined(即 CI 的报错)失败,修复后通过。历史上 #5089 之前的 E2E 跑里 11 个 acp-integration 用例全绿;自 #5089 起每次都挂在这一条断言上。

测试平台

OS 状态
🍏 macOS ⚠️
🪟 Windows ⚠️
🐧 Linux

Linux:跑了新单测、目标集成测试(对着 bundle、fake OPENAI_*),以及 tsc / prettier / eslint,全部通过。macOS / Windows 未本地运行;以 CI 为权威校验。

运行环境(可选)

npm run bundle + vitest run --root ./integration-tests,fake OPENAI_* 环境变量、QWEN_SANDBOX=false。核心单测在仓库根用 vitest run 跑。无需真实 provider 鉴权(断言验证的是模型枚举,不是推理)。

风险与范围

  • 主要风险 / 取舍:预计无。行为变化仅限于默认列表——在本应缺失时补上当前激活的 runtime 模型。registry 模型和排序(qwen-oauth 在前)均不变,显式过滤的调用方(getFastModel 等)不受影响。
  • 未验证 / 范围外:macOS/Windows 未本地运行(由 CI 覆盖);依赖推理的 ACP 用例未重跑(需真实 provider 鉴权)。
  • 破坏性变更 / 迁移说明:无。不涉及生产依赖变更。

关联 Issue

修复 #5089 引入的、确定性的 ACP set_config_option 失败。是此前两次尝试 #5721#5724 的后续(那两次处理的是症状,不是根因)。与 #5728 互补:#5728 通过注入一个 openai modelProviders 条目来加固测试(给 openai 造了个 registry 模型,从而绕开症状);本 PR 修的是底层产品 bug,因此原断言以及真实的纯 env 认证用户无需任何注入即可正常工作。两者不冲突。

…s listing

QwenLM#5089 switched getAllConfiguredModels from Object.values(AuthType) to
modelRegistry.getAuthTypes(), which only returns authTypes that have registry
models. A runtime model resolved from env/CLI overrides (e.g. OPENAI_API_KEY /
OPENAI_BASE_URL / OPENAI_MODEL with no modelProviders entry) uses an authType
that has no registry models, so it was dropped from the default listing — even
when it is the active/current model.

This hid the active model from the ACP `availableModels` list and the
interactive `/model` picker, and deterministically failed the ACP
`set_config_option` integration test (`expect(openaiModel).toBeDefined()`) on
every main run since QwenLM#5089.

Include the active runtime model's authType in the default (no-filter) case so
it is enumerated alongside registry models. Explicit-authType callers are
unchanged. Add a core unit regression test.

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Hi @wenshao — thanks for the detailed write-up! The technical content here is excellent and the root-cause analysis is thorough.

Unfortunately, the PR body doesn't follow our PR template. Several required headings are missing:

  • Why it's needed — your "Root cause" section covers this, but needs the template heading
  • Reviewer Test Plan — with sub-sections: "How to verify", "Evidence (Before & After)", "Tested on" table
  • Linked Issues — e.g. Fixes #N or references to #5721, #5724, #5089, #5728
  • 中文说明 — should be wrapped in a <details> tag per the template

Could you reformat the PR body to match the template? The content is all there — it just needs to be organized under the right headings so reviewers can find things quickly.

中文说明

@wenshao 你好——PR 的技术内容写得很好,根因分析也很到位。

不过 PR 正文没有按照我们的 PR 模板 来写,缺少几个必填的标题:

  • Why it's needed — 你的"根因"部分已经覆盖了,但需要用模板里的标题
  • Reviewer Test Plan — 需要包含"How to verify"、"Evidence (Before & After)"、"Tested on"表格等子标题
  • Linked Issues — 例如 Fixes #N 或引用 #5721#5724#5089#5728
  • 中文说明 — 需要按模板用 <details> 标签包裹

麻烦按照模板重新整理一下正文,内容都有了,只是需要放到对应的标题下面。

Qwen Code · qwen3.7-max

@wenshao

wenshao commented Jun 23, 2026

Copy link
Copy Markdown
Collaborator Author

@qwen-code /triage

Thanks for the catch — I've reformatted the body to follow the PR template: added Why it's needed, Reviewer Test Plan (How to verify / Evidence Before & After / Tested on table / Environment), Risk & Scope, and Linked Issues (#5089, #5721, #5724, #5728), with the full 中文说明 wrapped in a <details> tag.

已按模板重排正文:补齐了 Why it's needed、Reviewer Test Plan(How to verify / Evidence / Tested on / Environment)、Risk & Scope、Linked Issues 等标题,中文说明也折叠进 <details> 了。

@wenshao

wenshao commented Jun 23, 2026

Copy link
Copy Markdown
Collaborator Author

@qwen-code /triage

@qwen-code-ci-bot

qwen-code-ci-bot commented Jun 23, 2026

Copy link
Copy Markdown
Collaborator

Thanks for the PR, @wenshao!

Template looks good ✓ — all required sections present and well-filled.

On direction: this is a clear regression fix. #5089 narrowed the default auth type iteration in getAllConfiguredModels() to only registry-backed types, which silently drops the active runtime model when its authType (e.g. openai) has no registry entry. The active current model disappearing from the listing — and therefore from ACP clients and the /model picker — is a real user-facing bug. Strongly aligned with the project's core model management.

On approach: the fix is minimal and surgical — 2 files, +87/−7. It moves getRuntimeModelOption() earlier, then injects the runtime model's authType into the dedup set only when (a) no explicit filter was passed and (b) that authType isn't already present. The two new unit tests cover both the positive case and the explicit-filter edge case. No scope creep, no drive-by refactors.

One observation (not a blocker): the !hasExplicitAuthTypes guard is well-scoped — callers who pass an explicit authTypes filter still get exactly the requested set, and the existing loop already handles runtime models whose authType happens to be in the filter.

CI is all green (Test × 3 platforms, Lint, CodeQL, Integration Tests). Moving on to code review. 🔍

中文说明

感谢贡献,@wenshao

模板完整 ✓ — 所有必要章节都有且填写充分。

方向:这是一个明确的回归修复。#5089getAllConfiguredModels() 的默认 auth type 遍历范围收窄到仅有 registry 模型的类型,导致当前激活的 runtime 模型在其 authType(如 openai)没有 registry 条目时被静默丢弃。当前激活模型从列表里消失——进而从 ACP 客户端和 /model 选择器里消失——是真实的用户可见 bug。与项目核心模型管理高度对齐。

方案:修复极简且精准——2 个文件,+87/−7。把 getRuntimeModelOption() 提前,然后在仅当 (a) 未传显式过滤条件且 (b) 该 authType 尚未存在时,把 runtime 模型的 authType 注入去重集合。两个新单测分别覆盖了正向场景和显式过滤的边界场景。无范围蔓延,无顺手重构。

一个小观察(不阻塞):!hasExplicitAuthTypes 守卫范围合理——传显式 authTypes 的调用方仍然只得到所请求的集合,现有循环已能处理 runtime 模型的 authType 恰好在过滤条件里的情况。

CI 全绿(三平台测试、Lint、CodeQL、集成测试)。进入代码审查 🔍

Qwen Code · qwen3.7-max

@doudouOUC doudouOUC left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Reviewed the diff and relevant model-listing paths. Verified with build during clean install, targeted modelsConfig unit tests, diff check, and typecheck. No blocking issues found.

@yiliang114

Copy link
Copy Markdown
Collaborator

@qwen-code /triage

@wenshao wenshao 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.

No review findings. Downgraded from Approve to Comment: self-PR; CI still running.

Reviewed the runtime-model listing change and the explicit authType filter behavior. Local verification passed: npx vitest run src/models/modelsConfig.test.ts, qwen review deterministic, npm run build, and npm run typecheck after build.

— GPT-5 Codex via Qwen Code /review

@yiliang114 yiliang114 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

No review findings. LGTM!

@yiliang114
yiliang114 merged commit 2eb3b80 into QwenLM:main Jun 23, 2026
47 of 48 checks passed
@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Code Review

Independent proposal: I would fix this by, in the default branch of getAllConfiguredModels(), adding the current runtime model's authType to the iteration set when it's not already covered by modelRegistry.getAuthTypes(). This requires fetching the runtime option before the dedup loop, then appending its authType conditionally (only when no explicit filter, only when not already present).

Comparison with the diff: The PR's approach matches my proposal exactly. It introduces hasExplicitAuthTypes, moves getRuntimeModelOption() before the dedup loop, and appends the runtime authType to uniqueAuthTypes with the correct guard. Clean, correct, and minimal.

No critical blockers. No AGENTS.md violations. The change is 13 net production lines doing exactly one thing — no over-abstraction, no duplication, no code in the wrong package.

Testing

Ran the PR's new regression test against both main (without the fix) and the PR branch (with the fix) in tmux.

Before (main branch — bug present)

 ❯ src/models/modelsConfig.test.ts (75 tests | 1 failed | 74 skipped) 20ms
   ↓ ModelsConfig > getAllConfiguredModels > should return all models across all authTypes and put qwen-oauth first
   ↓ ModelsConfig > getAllConfiguredModels > should return empty array when no models are registered
   ↓ ModelsConfig > getAllConfiguredModels > should return models with correct structure
   ↓ ModelsConfig > getAllConfiguredModels > should support filtering by authTypes and still put qwen-oauth first when included
   × ModelsConfig > getAllConfiguredModels > should include an active runtime model whose authType has no registry models 18ms
     → expected undefined to be defined
   ↓ ModelsConfig > getAllConfiguredModels > should not inject the runtime model when an explicit authType filter excludes it

⎯⎯⎯⎯⎯⎯⎯ Failed Tests 1 ⎯⎯⎯⎯⎯⎯⎯

 FAIL  src/models/modelsConfig.test.ts > ModelsConfig > getAllConfiguredModels > should include an active runtime model whose authType has no registry models
AssertionError: expected undefined to be defined
 ❯ src/models/modelsConfig.test.ts:1922:27
    1920|         (m) => m.authType === AuthType.USE_OPENAI,
    1921|       );
    1922|       expect(openaiModel).toBeDefined();
       |                           ^
    1923|       expect(openaiModel?.id).toBe('my-openai-model');
    1924|       expect(openaiModel?.isRuntimeModel).toBe(true);

 Test Files  1 failed (1)
      Tests  1 failed | 74 skipped (75)

After (this PR — bug fixed)

 ✓ src/models/modelsConfig.test.ts (75 tests | 74 skipped) 5ms

 Test Files  1 passed (1)
      Tests  1 passed | 74 skipped (75)

Full test suite (worktree, PR branch)

All 75 tests in modelsConfig.test.ts pass, including both new test cases.

CI

All green: Test (macOS, Ubuntu, Windows), Lint, CodeQL, Integration Tests (No-AK Smoke).

中文说明

代码审查

独立方案: 我会在 getAllConfiguredModels() 的默认分支里,当 modelRegistry.getAuthTypes() 未涵盖当前 runtime 模型的 authType 时,将其加入遍历集合。需要把 getRuntimeModelOption() 移到去重循环之前,然后有条件地(仅在未显式过滤、且尚未存在时)追加该 authType。

与 diff 对比: PR 的方案与我的独立方案完全一致。引入 hasExplicitAuthTypes,把 getRuntimeModelOption() 移到去重循环前,用正确的守卫条件把 runtime authType 追加到 uniqueAuthTypes。干净、正确、极简。

无关键阻塞问题,无 AGENTS.md 违规。生产代码净增 13 行,只做一件事——无过度抽象、无重复、无错位代码。

测试

在 tmux 里把 PR 的新回归测试分别对着 main(无修复)和 PR 分支(有修复)跑了一遍。

修复前:expected undefined to be defined——完全复现了 PR 描述的 bug。
修复后:75 个测试全部通过,包括两个新用例。

CI 全绿:三平台测试、Lint、CodeQL、集成测试。

Qwen Code · qwen3.7-max

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

This is a clean regression fix that does exactly what it says and nothing more.

The bug is real and deterministic: #5089 narrowed getAllConfiguredModels() to only iterate registry-backed authTypes, which silently drops the active runtime model when its authType has no registry entry. The current active model disappearing from ACP clients and the /model picker is a genuine user-facing problem.

The fix is the simplest possible approach — 13 production lines that add the runtime model's authType to the iteration set in the default path only. Explicit-filter callers are untouched. The getRuntimeModelOption() call was already there; the PR just moves it three lines earlier. No new abstractions, no new dependencies, no drive-by refactors.

The test design is solid: one test captures the bug (and fails with the exact CI error on main), the other guards against over-injection when an explicit filter is passed. Both pass with the fix. Full CI green across all three platforms.

LGTM. Approving. ✅

中文说明

这是一个干净且极简的回归修复。

Bug 真实且确定性复现:#5089getAllConfiguredModels() 收窄为仅遍历有 registry 模型的 authType,导致当前激活的 runtime 模型在其 authType 无 registry 条目时被静默丢弃。当前激活模型从 ACP 客户端和 /model 选择器消失是真实的用户可见问题。

修复方案是最简路径——13 行生产代码,仅在默认路径里把 runtime 模型的 authType 加入遍历集合。显式过滤的调用方不受影响。getRuntimeModelOption() 调用本就在那里,PR 只是把它提前了三行。无新抽象、无新依赖、无顺手重构。

测试设计合理:一个用例复现 bug(在 main 上以完全一致的 CI 报错失败),另一个用例防止显式过滤时被过度注入。修复后两个都通过。CI 三平台全绿。

可以合入。✅

Qwen Code · qwen3.7-max

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM, looks ready to ship. ✅

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

⚠️ Downgraded from Approve to Comment: CI still running.

Fix is correct and minimal — the runtime model's authType is properly injected into uniqueAuthTypes before the qwen-oauth reorder, seen is kept in sync, and the !hasExplicitAuthTypes guard preserves the explicit-filter contract. Two new tests cover the primary regression and exclusion guard well. One minor code quality note inline.

: this.modelRegistry.getAuthTypes();
const hasExplicitAuthTypes = !!authTypes && authTypes.length > 0;
const inputAuthTypes = hasExplicitAuthTypes
? authTypes!

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] The authTypes! non-null assertion loses the type narrowing that the original ternary provided. The existing pattern authTypes && authTypes.length > 0 ? authTypes : ... lets TypeScript narrow authTypes from AuthType[] | undefined to AuthType[] without an escape hatch. Splitting into a separate hasExplicitAuthTypes boolean loses that narrowing, forcing ! to compensate.

Suggested change
? authTypes!
const inputAuthTypes = hasExplicitAuthTypes
? authTypes
: this.modelRegistry.getAuthTypes();

If the hasExplicitAuthTypes name is desired for the guard below, it can still be extracted without sacrificing the ternary's narrowing — just don't split the assignment into a separate statement.

— qwen3.7-max via Qwen Code /review

wenshao added a commit to wenshao/qwen-code that referenced this pull request Jun 23, 2026
…M#5089)

Reverts the structural changes from QwenLM#5089 back to the pre-QwenLM#5089 shape:
AuthType stays a fixed enum (not `string`), the Protocol enum is removed,
modelProviders is `Record<authType, ModelConfig[]>` again (not
`{ protocol, models }`), and createContentGenerator dispatches on authType.
The v4->v5 settings migration is removed and SETTINGS_VERSION reverts to 4.

Features merged on top of QwenLM#5089 are kept and re-adapted to the old
enum+array structure (not reverted):
- QwenLM#5632 fastOnly/voiceOnly model flags (test fixtures reshaped to arrays)
- QwenLM#5638 workspace provider defaults (readProviderModels already tolerates
  both shapes; test fixtures reshaped to arrays)
- QwenLM#5729 active-runtime-model listing (pre-QwenLM#5089 getAllConfiguredModels
  already enumerates Object.values(AuthType), so the runtime model is
  included natively)
- QwenLM#5728 ACP set_config_option deterministic provider fixture (reshaped to
  array; the flake fix is preserved)

KNOWN DOWNGRADE CAVEAT: settings already migrated to $version:5 (shipped in
v0.19.0) retain the v5 `{ protocol, models }` modelProviders shape, which
the reverted ModelRegistry consumes as an array. Such settings will throw
on load until re-configured. A v5->v4 downgrade guard/migration is a
separate follow-up if backward compatibility for migrated users is needed.
wenshao added a commit that referenced this pull request Jun 23, 2026
#5745)

* revert(core): revert Protocol enum & model-identity decoupling (#5089)

Reverts the structural changes from #5089 back to the pre-#5089 shape:
AuthType stays a fixed enum (not `string`), the Protocol enum is removed,
modelProviders is `Record<authType, ModelConfig[]>` again (not
`{ protocol, models }`), and createContentGenerator dispatches on authType.
The v4->v5 settings migration is removed and SETTINGS_VERSION reverts to 4.

Features merged on top of #5089 are kept and re-adapted to the old
enum+array structure (not reverted):
- #5632 fastOnly/voiceOnly model flags (test fixtures reshaped to arrays)
- #5638 workspace provider defaults (readProviderModels already tolerates
  both shapes; test fixtures reshaped to arrays)
- #5729 active-runtime-model listing (pre-#5089 getAllConfiguredModels
  already enumerates Object.values(AuthType), so the runtime model is
  included natively)
- #5728 ACP set_config_option deterministic provider fixture (reshaped to
  array; the flake fix is preserved)

KNOWN DOWNGRADE CAVEAT: settings already migrated to $version:5 (shipped in
v0.19.0) retain the v5 `{ protocol, models }` modelProviders shape, which
the reverted ModelRegistry consumes as an array. Such settings will throw
on load until re-configured. A v5->v4 downgrade guard/migration is a
separate follow-up if backward compatibility for migrated users is needed.

* feat(cli): add v5->v4 settings downgrade migration for #5089 revert

After reverting #5089, settings already migrated to $version:5 (shipped in
v0.19.0) carry a modelProviders `{ protocol, models }` shape that the
reverted v4 readers consume as arrays, throwing "models is not iterable"
on load. This adds the inverse migration so those configs auto-converge to
v4 on load (the user-facing "automatically migrate $version:5 to 4").

- V5ToV4Migration: unwraps each modelProviders `{ protocol, models }` back
  to its `models` array, drops the now-implicit protocol (warning only when
  the explicit protocol differs from the key-derived one), and resets
  $version to 4.
- DOWNGRADE_MIGRATIONS keeps the downgrade out of the ascending forward
  ALL_MIGRATIONS chain (preserving its invariants); runMigrations and
  needsMigration consider both via a combined convergence set.
- needsMigration now gates on `=== SETTINGS_VERSION` instead of `>=`, so a
  newer-but-handled version (v5) is reported as needing migration while a
  genuinely unknown newer version (v6+) is still left untouched.

Covered by unit tests for the migration, the framework wiring, and an
end-to-end loadSettings downgrade-on-load test.

* fix(test): align integration settings-version constant with reverted v4

The integration suites hard-coded CURRENT_SETTINGS_VERSION = 5 (introduced
by #5676), which mismatched the reverted SETTINGS_VERSION = 4 and failed the
migration assertions ($version now writes 4, not 5). Revert the constant to
4 in both settings-migration and qwen-config-dir integration tests.

Verified: QWEN_SANDBOX=false vitest run --root ./integration-tests
cli/settings-migration.test.ts cli/qwen-config-dir.test.ts → 21 passed.

* fix: harden v5-era settings handling on the #5089 revert path

Addresses /qreview feedback on the revert:

- vscode findOpenaiModels: restore read-side tolerance for the V5
  { protocol, models } shape. The extension reads/writes settings.json
  without running the CLI v5->v4 migration, so a not-yet-downgraded
  $version:5 file would otherwise return [] and silently drop existing
  OpenAI models on the next write. (Critical)
- modelRegistry.registerAuthTypeModels: guard against a non-array provider
  value (skip + warn) instead of throwing an opaque "models is not
  iterable" — covers hand-edited or unmigrated files the downgrade misses.
- needsMigration JSDoc: update the stale ">= SETTINGS_VERSION" wording to
  match the "=== SETTINGS_VERSION, else fall through" logic the downgrade
  path depends on.
- settings.test.ts: also assert the v5->v4 downgrade is persisted to disk
  (.tmp write-back), not just the in-memory merged result.

Adds tests for the registry guard and the vscode V5 read tolerance.

* fix(core): break contentGenerator import cycle + cover reverted error paths

Addresses /review suggestions on the revert:

- contentGenerator: import PROVIDER_SOURCED_FIELDS from constants.js (where
  it is actually defined) instead of modelsConfig.js, breaking the runtime
  import cycle contentGenerator -> modelsConfig -> contentGenerator.
  constants.js only references contentGenerator at the type level, which is
  erased at runtime, so no cycle remains.
- contentGenerator.test: add coverage for the two authType error paths the
  revert restored (missing authType -> "must have an authType"; unknown
  authType -> "Unsupported authType"), which #5089's protocol-based tests
  had replaced. Neither was covered before.

The acpAgent z.nativeEnum(AuthType).parse(methodId) suggestion is left as-is:
that line is byte-identical to pre-#5089, so it is pre-existing behavior the
revert faithfully restores rather than a regression of this PR.
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.

4 participants