diff --git a/CHANGELOG.md b/CHANGELOG.md index 591f2c1f5b4..4aa75b2a4fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,9 +44,18 @@ Docs: https://docs.openclaw.ai ### Fixes -- Security: require operator.approvals for gateway /approve commands. (#1) Thanks @mitsuhiko, @yueyueL. -- Updates: honor update.channel for update.run (Control UI) and channel-based npm tags for global installs. -- Security: Matrix allowlists now require full MXIDs; ambiguous name resolution no longer grants access. Thanks @MegaManSec. +- Docs: finish renaming the QMD memory docs to reference the OpenClaw state dir. +- Onboarding: keep TUI flow exclusive (skip completion prompt + background Web UI seed). +- Onboarding: drop completion prompt now handled by install/update. +- TUI: block onboarding output while TUI is active and restore terminal state on exit. +- CLI: cache shell completion scripts in state dir and source cached files in profiles. +- Zsh completion: escape option descriptions to avoid invalid option errors. +- Agents: repair malformed tool calls and session transcripts. (#7473) Thanks @justinhuangcode. +- fix(agents): validate AbortSignal instances before calling AbortSignal.any() (#7277) (thanks @Elarwei001) +- fix(webchat): respect user scroll position during streaming and refresh (#7226) (thanks @marcomarandiz) +- Telegram: recover from grammY long-poll timed out errors. (#7466) Thanks @macmimi23. +- Telegram: honor session model overrides in inline model selection. (#8193) Thanks @gildo. +- Media understanding: skip binary media from file text extraction. (#7475) Thanks @AlexZhangji. - Security: enforce access-group gating for Slack slash commands when channel type lookup fails. - Security: require validated shared-secret auth before skipping device identity on gateway connect. - Security: guard skill installer downloads with SSRF checks (block private/localhost URLs). diff --git a/src/auto-reply/reply/model-selection.ts b/src/auto-reply/reply/model-selection.ts index 5a4329790df..fa5fa36abb7 100644 --- a/src/auto-reply/reply/model-selection.ts +++ b/src/auto-reply/reply/model-selection.ts @@ -93,7 +93,7 @@ function boundedLevenshteinDistance(a: string, b: string, maxDistance: number): return dist; } -type StoredModelOverride = { +export type StoredModelOverride = { provider?: string; model: string; source: "session" | "parent"; @@ -126,7 +126,7 @@ function resolveParentSessionKeyCandidate(params: { return null; } -function resolveStoredModelOverride(params: { +export function resolveStoredModelOverride(params: { sessionEntry?: SessionEntry; sessionStore?: Record; sessionKey?: string; diff --git a/src/telegram/bot-handlers.ts b/src/telegram/bot-handlers.ts index 6c0ccea55cd..33e6b18c00d 100644 --- a/src/telegram/bot-handlers.ts +++ b/src/telegram/bot-handlers.ts @@ -8,19 +8,23 @@ import { } from "../auto-reply/inbound-debounce.js"; import { buildCommandsPaginationKeyboard } from "../auto-reply/reply/commands-info.js"; import { buildModelsProviderData } from "../auto-reply/reply/commands-models.js"; +import { resolveStoredModelOverride } from "../auto-reply/reply/model-selection.js"; import { listSkillCommandsForAgents } from "../auto-reply/skill-commands.js"; import { buildCommandsMessagePaginated } from "../auto-reply/status.js"; import { resolveChannelConfigWrites } from "../channels/plugins/config-writes.js"; import { loadConfig } from "../config/config.js"; import { writeConfigFile } from "../config/io.js"; +import { loadSessionStore, resolveStorePath } from "../config/sessions.js"; import { danger, logVerbose, warn } from "../globals.js"; import { readChannelAllowFromStore } from "../pairing/pairing-store.js"; +import { resolveAgentRoute } from "../routing/resolve-route.js"; +import { resolveThreadSessionKeys } from "../routing/session-key.js"; import { withTelegramApiErrorLogging } from "./api-logging.js"; import { firstDefined, isSenderAllowed, normalizeAllowFromWithStore } from "./bot-access.js"; import { RegisterTelegramHandlerParams } from "./bot-native-commands.js"; import { MEDIA_GROUP_TIMEOUT_MS, type MediaGroupEntry } from "./bot-updates.js"; import { resolveMedia } from "./bot/delivery.js"; -import { resolveTelegramForumThreadId } from "./bot/helpers.js"; +import { buildTelegramGroupPeerId, resolveTelegramForumThreadId } from "./bot/helpers.js"; import { migrateTelegramGroupConfig } from "./group-migration.js"; import { resolveTelegramInlineButtonsScope } from "./inline-buttons.js"; import { @@ -128,6 +132,60 @@ export const registerTelegramHandlers = ({ }, }); + const resolveTelegramSessionModel = (params: { + chatId: number | string; + isGroup: boolean; + isForum: boolean; + messageThreadId?: number; + resolvedThreadId?: number; + }): string | undefined => { + const resolvedThreadId = + params.resolvedThreadId ?? + resolveTelegramForumThreadId({ + isForum: params.isForum, + messageThreadId: params.messageThreadId, + }); + const peerId = params.isGroup + ? buildTelegramGroupPeerId(params.chatId, resolvedThreadId) + : String(params.chatId); + const route = resolveAgentRoute({ + cfg, + channel: "telegram", + accountId, + peer: { + kind: params.isGroup ? "group" : "dm", + id: peerId, + }, + }); + const baseSessionKey = route.sessionKey; + const dmThreadId = !params.isGroup ? params.messageThreadId : undefined; + const threadKeys = + dmThreadId != null + ? resolveThreadSessionKeys({ baseSessionKey, threadId: String(dmThreadId) }) + : null; + const sessionKey = threadKeys?.sessionKey ?? baseSessionKey; + const storePath = resolveStorePath(cfg.session?.store, { agentId: route.agentId }); + const store = loadSessionStore(storePath); + const entry = store[sessionKey]; + const storedOverride = resolveStoredModelOverride({ + sessionEntry: entry, + sessionStore: store, + sessionKey, + }); + if (storedOverride) { + return storedOverride.provider + ? `${storedOverride.provider}/${storedOverride.model}` + : storedOverride.model; + } + const provider = entry?.modelProvider?.trim(); + const model = entry?.model?.trim(); + if (provider && model) { + return `${provider}/${model}`; + } + const modelCfg = cfg.agents?.defaults?.model; + return typeof modelCfg === "string" ? modelCfg : modelCfg?.primary; + }; + const processMediaGroup = async (entry: MediaGroupEntry) => { try { entry.messages.sort((a, b) => a.msg.message_id - b.msg.message_id); @@ -474,9 +532,14 @@ export const registerTelegramHandlers = ({ const totalPages = calculateTotalPages(models.length, pageSize); const safePage = Math.max(1, Math.min(page, totalPages)); - // Get current model from config for checkmark display - const modelCfg = cfg.agents?.defaults?.model; - const currentModel = typeof modelCfg === "string" ? modelCfg : modelCfg?.primary; + // Resolve current model from session (prefer overrides) + const currentModel = resolveTelegramSessionModel({ + chatId, + isGroup, + isForum, + messageThreadId, + resolvedThreadId, + }); const buttons = buildModelsKeyboard({ provider,