From e87fd7aa304cfaf2e8817708e2a705760d0002fb Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Tue, 12 May 2026 03:36:50 +0100 Subject: [PATCH] fix: normalize google gemini preview config --- CHANGELOG.md | 1 + extensions/google/default-model.test.ts | 50 +++++++++++++++++++++++++ extensions/google/onboard.ts | 30 ++++++++++++++- src/plugin-sdk/provider-onboard.ts | 6 ++- 4 files changed, 85 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 098238883dc..540cf7c1b78 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -69,6 +69,7 @@ Docs: https://docs.openclaw.ai ### Fixes +- Google/Gemini: normalize retired Gemini 3 Pro Preview refs left in Google API-key onboarding model allowlists and fallbacks, so setup-emitted config keeps testing `google/gemini-3.1-pro-preview` instead of `google/gemini-3-pro-preview`. - 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. diff --git a/extensions/google/default-model.test.ts b/extensions/google/default-model.test.ts index 5af9bd51510..cfd97652186 100644 --- a/extensions/google/default-model.test.ts +++ b/extensions/google/default-model.test.ts @@ -18,6 +18,56 @@ describe("google default model", () => { expect(applied.next.agents?.defaults?.model).toEqual({ primary: GOOGLE_GEMINI_DEFAULT_MODEL }); }); + it("normalizes retired Gemini model map keys when applying the default", () => { + const applied = applyGoogleGeminiModelDefault({ + agents: { + defaults: { + model: { + primary: "google/gemini-3-pro-preview", + fallbacks: ["google/gemini-3-pro-preview"], + }, + models: { + "google/gemini-3-pro-preview": { alias: "gemini" }, + }, + }, + }, + } as OpenClawConfig); + + expect(applied.changed).toBe(true); + expect(applied.next.agents?.defaults?.model).toEqual({ + primary: "google/gemini-3.1-pro-preview", + fallbacks: ["google/gemini-3.1-pro-preview"], + }); + expect(applied.next.agents?.defaults?.models).toEqual({ + "google/gemini-3.1-pro-preview": { alias: "gemini" }, + }); + }); + + it("normalizes retired Gemini model maps even when the primary is already current", () => { + const applied = applyGoogleGeminiModelDefault({ + agents: { + defaults: { + model: { + primary: GOOGLE_GEMINI_DEFAULT_MODEL, + fallbacks: ["google/gemini-3-pro-preview"], + }, + models: { + "google/gemini-3-pro-preview": { alias: "gemini" }, + }, + }, + }, + } as OpenClawConfig); + + expect(applied.changed).toBe(true); + expect(applied.next.agents?.defaults?.model).toEqual({ + primary: GOOGLE_GEMINI_DEFAULT_MODEL, + fallbacks: [GOOGLE_GEMINI_DEFAULT_MODEL], + }); + expect(applied.next.agents?.defaults?.models).toEqual({ + [GOOGLE_GEMINI_DEFAULT_MODEL]: { alias: "gemini" }, + }); + }); + it("no-ops when already on the target default", () => { const cfg = { agents: { defaults: { model: { primary: GOOGLE_GEMINI_DEFAULT_MODEL } } }, diff --git a/extensions/google/onboard.ts b/extensions/google/onboard.ts index 5c6dd39d73f..cfbfc07aa30 100644 --- a/extensions/google/onboard.ts +++ b/extensions/google/onboard.ts @@ -4,6 +4,34 @@ import { } from "openclaw/plugin-sdk/provider-onboard"; export const GOOGLE_GEMINI_DEFAULT_MODEL = "google/gemini-3.1-pro-preview"; +const RETIRED_GOOGLE_GEMINI_MODEL_REFS = new Set([ + "google/gemini-3-pro", + "google/gemini-3-pro-preview", +]); + +function hasRetiredGeminiDefaultModelRefs(cfg: OpenClawConfig): boolean { + const defaults = cfg.agents?.defaults; + const model = defaults?.model as unknown; + if (model && typeof model === "object") { + const fallbacks = (model as { fallbacks?: unknown }).fallbacks; + if ( + Array.isArray(fallbacks) && + fallbacks.some( + (fallback) => + typeof fallback === "string" && RETIRED_GOOGLE_GEMINI_MODEL_REFS.has(fallback), + ) + ) { + return true; + } + } + + const models = defaults?.models; + return Boolean( + models && + typeof models === "object" && + Object.keys(models).some((modelRef) => RETIRED_GOOGLE_GEMINI_MODEL_REFS.has(modelRef)), + ); +} export function applyGoogleGeminiModelDefault(cfg: OpenClawConfig): { next: OpenClawConfig; @@ -18,7 +46,7 @@ export function applyGoogleGeminiModelDefault(cfg: OpenClawConfig): { typeof (current as { primary?: unknown }).primary === "string" ? ((current as { primary: string }).primary || "").trim() || undefined : undefined; - if (currentPrimary === GOOGLE_GEMINI_DEFAULT_MODEL) { + if (currentPrimary === GOOGLE_GEMINI_DEFAULT_MODEL && !hasRetiredGeminiDefaultModelRefs(cfg)) { return { next: cfg, changed: false }; } return { diff --git a/src/plugin-sdk/provider-onboard.ts b/src/plugin-sdk/provider-onboard.ts index 8d2274ab10d..00e7677a209 100644 --- a/src/plugin-sdk/provider-onboard.ts +++ b/src/plugin-sdk/provider-onboard.ts @@ -254,20 +254,24 @@ export function applyAgentDefaultModelPrimary( cfg: OpenClawConfig, primary: string, ): OpenClawConfig { + const defaults = cfg.agents?.defaults; const existingFallbacks = extractAgentDefaultModelFallbacks(cfg.agents?.defaults?.model); const normalizedFallbacks = existingFallbacks?.map((fallback) => normalizeAgentModelRefForConfig(fallback), ); + const normalizedModels = + defaults?.models === undefined ? undefined : normalizeAgentModelMapForConfig(defaults.models); return { ...cfg, agents: { ...cfg.agents, defaults: { - ...cfg.agents?.defaults, + ...defaults, model: { ...(normalizedFallbacks ? { fallbacks: normalizedFallbacks } : undefined), primary: normalizeAgentModelRefForConfig(primary), }, + ...(normalizedModels !== undefined ? { models: normalizedModels } : undefined), }, }, };