From cd3927ad67ae9328eac067930e068c2b8bd06dec Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Tue, 24 Feb 2026 04:15:18 +0000 Subject: [PATCH] fix(sessions): preserve allow-any subagent model overrides (#21088) (thanks @Slats24) --- CHANGELOG.md | 1 + src/commands/agent.test.ts | 42 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 358264587..008661d60 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,7 @@ Docs: https://docs.openclaw.ai - Infra/Windows TOCTOU: handle Windows `dev=0` edge cases in same-file identity checks. (#24939) - Exec/Bash tools: clamp poll sleep duration to non-negative values in process polling loops. (#24889) - Subagents/Announce queue: add exponential backoff when queue-drain delivery fails to reduce retry storms. (#24783) +- Sessions/Model overrides: keep stored sub-agent model overrides when `agents.defaults.models` is empty (allow-any mode) instead of resetting to defaults. (#21088) Thanks @Slats24. - Config/Kilo Gateway: Kilo provider flow now surfaces an updated list of models. (#24921) thanks @gumadeiras. - Agents/Tool warnings: suppress `sessions_send` relay errors from chat-facing warning payloads to avoid leaking transient inter-session transport failures. (#24740) Thanks @Glucksberg. - WhatsApp/Logging: redact outbound recipient identifiers in WhatsApp outbound + heartbeat logs and remove message/poll preview text from those log lines. (#24980) Thanks @coygeek. diff --git a/src/commands/agent.test.ts b/src/commands/agent.test.ts index 3e26ec3ec..0118e0763 100644 --- a/src/commands/agent.test.ts +++ b/src/commands/agent.test.ts @@ -367,6 +367,48 @@ describe("agentCommand", () => { }); }); + it("keeps stored session model override when models allowlist is empty", async () => { + await withTempHome(async (home) => { + const store = path.join(home, "sessions.json"); + writeSessionStoreSeed(store, { + "agent:main:subagent:allow-any": { + sessionId: "session-allow-any", + updatedAt: Date.now(), + providerOverride: "openai", + modelOverride: "gpt-custom-foo", + }, + }); + + mockConfig(home, store, { + model: { primary: "anthropic/claude-opus-4-5" }, + models: {}, + }); + + vi.mocked(loadModelCatalog).mockResolvedValueOnce([ + { id: "claude-opus-4-5", name: "Opus", provider: "anthropic" }, + ]); + + await agentCommand( + { + message: "hi", + sessionKey: "agent:main:subagent:allow-any", + }, + runtime, + ); + + const callArgs = vi.mocked(runEmbeddedPiAgent).mock.calls.at(-1)?.[0]; + expect(callArgs?.provider).toBe("openai"); + expect(callArgs?.model).toBe("gpt-custom-foo"); + + const saved = JSON.parse(fs.readFileSync(store, "utf-8")) as Record< + string, + { providerOverride?: string; modelOverride?: string } + >; + expect(saved["agent:main:subagent:allow-any"]?.providerOverride).toBe("openai"); + expect(saved["agent:main:subagent:allow-any"]?.modelOverride).toBe("gpt-custom-foo"); + }); + }); + it("keeps explicit sessionKey even when sessionId exists elsewhere", async () => { await withTempHome(async (home) => { const store = path.join(home, "sessions.json");