diff --git a/CHANGELOG.md b/CHANGELOG.md index 76fec4f27cc..efafe944df1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -66,6 +66,7 @@ Docs: https://docs.openclaw.ai ### Fixes +- Google/Gemini: normalize retired nested Gemini 3 Pro Preview ids when resolving exact configured proxy-provider refs, so `kilocode/google/gemini-3-pro-preview` resolves to `kilocode/google/gemini-3.1-pro-preview` for Gemini 3.1 testing. - CLI: strip generic OSC terminal escape payloads from sanitized output fields, preventing clipboard/title escape bodies from leaking into commitment tables and other terminal-safe text. Thanks @shakkernerd. - Codex app-server: match connector-backed plugin approval elicitations by stable connector id so enabled destructive actions no longer fall through to display-name-only rejection. - Build: replace selected build utility `tsx` preloads with Node native type stripping so Node 26 build paths no longer emit `DEP0205` module loader deprecation warnings. (#78584) Thanks @keshavbotagent. diff --git a/src/agents/model-selection-shared.ts b/src/agents/model-selection-shared.ts index 1cac68c11fd..85b8f00e47b 100644 --- a/src/agents/model-selection-shared.ts +++ b/src/agents/model-selection-shared.ts @@ -316,10 +316,13 @@ function resolveExactConfiguredProviderRef( const provider = normalizeLowercaseStringOrEmpty(configuredProvider); return { provider, - model: normalizeStaticProviderModelId(provider, modelRaw.trim(), { - allowManifestNormalization: params.allowManifestNormalization, - manifestPlugins: params.manifestPlugins, - }), + model: normalizeConfiguredProviderCatalogModelId( + provider, + normalizeStaticProviderModelId(provider, modelRaw.trim(), { + allowManifestNormalization: params.allowManifestNormalization, + manifestPlugins: params.manifestPlugins, + }), + ), }; } diff --git a/src/agents/model-selection.test.ts b/src/agents/model-selection.test.ts index 8303d733e74..d37ff357908 100644 --- a/src/agents/model-selection.test.ts +++ b/src/agents/model-selection.test.ts @@ -1768,6 +1768,34 @@ describe("model-selection", () => { ).toEqual({ provider: "modelstudio", model: "qwen3.6-plus" }); }); + it("normalizes retired nested Gemini ids in exact configured provider refs", () => { + const cfg = { + agents: { + defaults: { + model: { primary: "kilocode/google/gemini-3-pro-preview" }, + }, + }, + models: { + providers: { + kilocode: { + api: "openai-completions", + baseUrl: "https://kilocode.test/v1", + models: [{ id: "google/gemini-3-pro-preview", name: "Gemini 3 Pro" }], + }, + }, + }, + } as unknown as OpenClawConfig; + + expect( + resolveConfiguredModelRef({ + cfg, + defaultProvider: "anthropic", + defaultModel: "claude-opus-4-6", + allowPluginNormalization: false, + }), + ).toEqual({ provider: "kilocode", model: "google/gemini-3.1-pro-preview" }); + }); + it("keeps legacy modelstudio aliases when no exact foreign api owner is configured", () => { const cfg = { agents: { diff --git a/src/commands/models/list.configured.test.ts b/src/commands/models/list.configured.test.ts index 5c80b337d52..dd6dae08132 100644 --- a/src/commands/models/list.configured.test.ts +++ b/src/commands/models/list.configured.test.ts @@ -37,4 +37,40 @@ describe("resolveConfiguredEntries", () => { expect(entries[0]?.aliases).toEqual(["Codex"]); expect(entries[1]?.tags).toEqual(new Set(["fallback#1", "configured"])); }); + + it("normalizes retired nested Gemini ids in configured provider rows", () => { + const { entries } = resolveConfiguredEntries({ + agents: { + defaults: { + model: { primary: "kilocode/google/gemini-3-pro-preview" }, + models: { + "kilocode/google/gemini-3-pro-preview": { alias: "Kilo Gemini" }, + }, + }, + }, + models: { + providers: { + kilocode: { + api: "openai-completions", + baseUrl: "https://kilocode.test/v1", + models: [ + { + id: "google/gemini-3-pro-preview", + name: "Gemini 3 Pro", + reasoning: true, + input: ["text", "image"], + cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, + contextWindow: 1_048_576, + maxTokens: 65_536, + }, + ], + }, + }, + }, + }); + + expect(entries.map((entry) => entry.key)).toEqual(["kilocode/google/gemini-3.1-pro-preview"]); + expect(entries[0]?.aliases).toEqual(["Kilo Gemini"]); + expect(entries[0]?.tags).toEqual(new Set(["default", "configured"])); + }); }); diff --git a/src/commands/models/list.configured.ts b/src/commands/models/list.configured.ts index 7e07d110528..9ebc00fef0d 100644 --- a/src/commands/models/list.configured.ts +++ b/src/commands/models/list.configured.ts @@ -1,6 +1,5 @@ import { buildModelAliasIndex, - parseModelRef, resolveConfiguredModelRef, resolveModelRefFromString, } from "../../agents/model-selection.js"; @@ -74,11 +73,17 @@ export function resolveConfiguredEntries(cfg: OpenClawConfig) { }); for (const key of Object.keys(cfg.agents?.defaults?.models ?? {})) { - const parsed = parseModelRef(key, DEFAULT_PROVIDER, DISPLAY_MODEL_PARSE_OPTIONS); - if (!parsed) { + const resolved = resolveModelRefFromString({ + cfg, + raw: key, + defaultProvider: DEFAULT_PROVIDER, + aliasIndex, + ...DISPLAY_MODEL_PARSE_OPTIONS, + }); + if (!resolved) { continue; } - addEntry(parsed, "configured"); + addEntry(resolved.ref, "configured"); } const entries: ConfiguredEntry[] = order.map((key) => {