From 0c30d0d0b86a429418f7035bd79aec267904c636 Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Sun, 26 Apr 2026 19:10:21 -0700 Subject: [PATCH] fix(gateway): resolve configured thinking default in session rows (#72324) * fix(gateway): resolve configured thinking default in session rows * fix(gateway): preserve model thinking precedence --- CHANGELOG.md | 1 + src/gateway/session-utils.test.ts | 83 +++++++++++++++++++++++++++++++ src/gateway/session-utils.ts | 33 +++++++++--- 3 files changed, 111 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f874cbde582..f244644aee7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -51,6 +51,7 @@ Docs: https://docs.openclaw.ai - WebChat/Control UI: support non-video file attachments in chat uploads while preserving the existing image attachment path and MIME-sniff fallback for generic image uploads. (#70947) Thanks @IAMSamuelRodda. - Skills/memory: restore Chokidar v5 hot reloads by watching concrete skill and memory roots with filters, including SKILL.md removals and deleted skill folders without broad workspace recursion. Fixes #27404, #33585, and #41606. Thanks @shelvenzhou, @08820048, and @rocke2020. - Gateway/chat: keep duplicate attachment-backed `chat.send` retries with the same idempotency key on the documented in-flight path so aborts still target the real active run. Fixes #70139. Thanks @Feelw00. +- Gateway/session rows: report the same config-resolved thinking default that runtime sessions use, including global and per-agent defaults, so Control UI and TUI default labels stay aligned. (#71779, #70981, #71033, #70302) Thanks @chen-zhang-cs-code, @SymbolStar, and @cholaolu-boop. - Plugins: share package entrypoint resolution between install and discovery, reject mismatched `runtimeExtensions`, and cache bundled runtime-dependency manifest reads during scans. Thanks @codex. - WhatsApp/Web: keep quiet but healthy linked-device sessions connected by basing the watchdog on WhatsApp Web transport activity, while retaining a longer app-silence cap so frame activity cannot mask a stuck session forever. Fixes #70678; carries forward the focused #71466 approach and keeps #63939 as related configurable-timeout follow-up. Thanks @vincentkoc and @oromeis. - Discord/gateway: count failed health-monitor restart attempts toward cooldown and hourly caps, and evict stale account lifecycle state during channel reloads so repeated Discord gateway recovery cannot loop on old status. Fixes #38596. (#40413) Thanks @jellyAI-dev and @vashquez. diff --git a/src/gateway/session-utils.test.ts b/src/gateway/session-utils.test.ts index ccc8927ed2f..a5985d4f765 100644 --- a/src/gateway/session-utils.test.ts +++ b/src/gateway/session-utils.test.ts @@ -9,6 +9,7 @@ import { createEmptyPluginRegistry } from "../plugins/registry-empty.js"; import { resetPluginRuntimeStateForTest, setActivePluginRegistry } from "../plugins/runtime.js"; import { withStateDirEnv } from "../test-helpers/state-dir-env.js"; import { + buildGatewaySessionRow, capArrayByJsonBytes, classifySessionKey, deriveSessionTitle, @@ -139,6 +140,88 @@ describe("gateway session utils", () => { ); }); + test("session defaults use configured thinking default", () => { + const defaults = getSessionDefaults({ + agents: { + defaults: { + model: { primary: "openai-codex/gpt-5.5" }, + thinkingDefault: "high", + }, + }, + } as OpenClawConfig); + + expect(defaults).toMatchObject({ + modelProvider: "openai-codex", + model: "gpt-5.5", + thinkingDefault: "high", + }); + }); + + test("session rows use per-agent thinking default from config", () => { + const cfg = { + agents: { + defaults: { + model: { primary: "openai-codex/gpt-5.5" }, + thinkingDefault: "low", + models: { + "openai-codex/gpt-5.5": { + params: { thinking: "max" }, + }, + }, + }, + list: [ + { + id: "alpha", + default: true, + thinkingDefault: "high", + }, + ], + }, + } as OpenClawConfig; + + const row = buildGatewaySessionRow({ + cfg, + storePath: "", + store: {}, + key: "agent:alpha:main", + }); + + expect(row).toMatchObject({ + modelProvider: "openai-codex", + model: "gpt-5.5", + thinkingDefault: "high", + }); + }); + + test("session rows prefer per-model thinking over global default", () => { + const cfg = { + agents: { + defaults: { + model: { primary: "openai-codex/gpt-5.5" }, + thinkingDefault: "low", + models: { + "openai-codex/gpt-5.5": { + params: { thinking: "max" }, + }, + }, + }, + }, + } as OpenClawConfig; + + const row = buildGatewaySessionRow({ + cfg, + storePath: "", + store: {}, + key: "main", + }); + + expect(row).toMatchObject({ + modelProvider: "openai-codex", + model: "gpt-5.5", + thinkingDefault: "max", + }); + }); + test("classifySessionKey respects chat type + prefixes", () => { expect(classifySessionKey("global")).toBe("global"); expect(classifySessionKey("unknown")).toBe("unknown"); diff --git a/src/gateway/session-utils.ts b/src/gateway/session-utils.ts index 53c5ab1d6cc..6cc3839c730 100644 --- a/src/gateway/session-utils.ts +++ b/src/gateway/session-utils.ts @@ -2,6 +2,7 @@ import fs from "node:fs"; import path from "node:path"; import { listAgentIds, + resolveAgentConfig, resolveAgentEffectiveModelPrimary, resolveAgentModelFallbacksOverride, resolveAgentWorkspaceDir, @@ -17,6 +18,7 @@ import { resolveConfiguredModelRef, resolveDefaultModelForAgent, resolvePersistedSelectedModelRef, + resolveThinkingDefault, } from "../agents/model-selection.js"; import { countActiveDescendantRuns, @@ -31,10 +33,7 @@ import { RECENT_ENDED_SUBAGENT_CHILD_SESSION_MS, shouldKeepSubagentRunChildLink, } from "../agents/subagent-run-liveness.js"; -import { - listThinkingLevelOptions, - resolveThinkingDefaultForModel, -} from "../auto-reply/thinking.js"; +import { listThinkingLevelOptions } from "../auto-reply/thinking.js"; import { loadConfig } from "../config/config.js"; import { resolveAgentModelFallbackValues } from "../config/model-input.js"; import { resolveStateDir } from "../config/paths.js"; @@ -1038,6 +1037,25 @@ export function resolveGatewaySessionStoreTarget(params: { export { loadCombinedSessionStoreForGateway } from "../config/sessions/combined-store-gateway.js"; +function resolveGatewaySessionThinkingDefault(params: { + cfg: OpenClawConfig; + provider: string; + model: string; + agentId?: string; +}) { + const agentThinkingDefault = params.agentId + ? resolveAgentConfig(params.cfg, params.agentId)?.thinkingDefault + : undefined; + return ( + agentThinkingDefault ?? + resolveThinkingDefault({ + cfg: params.cfg, + provider: params.provider, + model: params.model, + }) + ); +} + export function getSessionDefaults(cfg: OpenClawConfig): GatewaySessionsDefaults { const resolved = resolveConfiguredModelRef({ cfg, @@ -1055,7 +1073,8 @@ export function getSessionDefaults(cfg: OpenClawConfig): GatewaySessionsDefaults contextTokens: contextTokens ?? null, thinkingLevels, thinkingOptions: thinkingLevels.map((level) => level.label), - thinkingDefault: resolveThinkingDefaultForModel({ + thinkingDefault: resolveGatewaySessionThinkingDefault({ + cfg, provider: resolved.provider, model: resolved.model, }), @@ -1429,9 +1448,11 @@ export function buildGatewaySessionRow(params: { thinkingLevel: entry?.thinkingLevel, thinkingLevels, thinkingOptions: thinkingLevels.map((level) => level.label), - thinkingDefault: resolveThinkingDefaultForModel({ + thinkingDefault: resolveGatewaySessionThinkingDefault({ + cfg, provider: thinkingProvider, model: thinkingModel, + agentId: sessionAgentId, }), fastMode: entry?.fastMode, verboseLevel: entry?.verboseLevel,