153680→		thread: params.threadSpec,
153681→		tableMode: params.tableMode,
153682→		chunkMode: params.chunkMode,
153683→		linkPreview: telegramCfg.linkPreview
153684→	});
153685→	if (commandsToRegister.length > 0 || pluginCatalog.commands.length > 0) if (typeof bot.command !== "function") logVerbose("telegram: bot.command unavailable; skipping native handlers");
153686→	else {
153687→		for (const command of nativeCommands) {
153688→			const normalizedCommandName = normalizeTelegramCommandName(command.name);
153689→			bot.command(normalizedCommandName, async (ctx) => {
153690→				const msg = ctx.message;
153691→				if (!msg) return;
153692→				if (shouldSkipUpdate(ctx)) return;
153693→				const auth = await resolveTelegramCommandAuth({
153694→					msg,
153695→					bot,
153696→					cfg,
153697→					accountId,
153698→					telegramCfg,
153699→					allowFrom,
153700→					groupAllowFrom,
153701→					useAccessGroups,
153702→					resolveGroupPolicy,
153703→					resolveTelegramGroupConfig,
153704→					requireAuth: true
153705→				});
153706→				if (!auth) return;
153707→				const { chatId, isGroup, isForum, resolvedThreadId, senderId, senderUsername, groupConfig, topicConfig, commandAuthorized } = auth;
153708→				const runtimeContext = await resolveCommandRuntimeContext({
153709→					msg,
153710→					isGroup,
153711→					isForum,
153712→					resolvedThreadId,
153713→					senderId,
153714→					topicAgentId: topicConfig?.agentId
153715→				});
153716→				if (!runtimeContext) return;
153717→				const { threadSpec, route, mediaLocalRoots, tableMode, chunkMode } = runtimeContext;
153718→				const threadParams = buildTelegramThreadParams(threadSpec) ?? {};
153719→				const commandDefinition = findCommandByNativeName(command.name, "telegram");
153720→				const rawText = ctx.match?.trim() ?? "";
153721→				const commandArgs = commandDefinition ? parseCommandArgs(commandDefinition, rawText) : rawText ? { raw: rawText } : void 0;
153722→				const prompt = commandDefinition ? buildCommandTextFromArgs(commandDefinition, commandArgs) : rawText ? `/${command.name} ${rawText}` : `/${command.name}`;
153723→				const menu = commandDefinition ? resolveCommandArgMenu({
153724→					command: commandDefinition,
153725→					args: commandArgs,
153726→					cfg
153727→				}) : null;
153728→				if (menu && commandDefinition) {
153729→					const title = menu.title ?? `Choose ${menu.arg.description || menu.arg.name} for /${commandDefinition.nativeName ?? commandDefinition.key}.`;
153730→					const rows = [];
153731→					for (let i = 0; i < menu.choices.length; i += 2) {
153732→						const slice = menu.choices.slice(i, i + 2);
153733→						rows.push(slice.map((choice) => {
153734→							const args = { values: { [menu.arg.name]: choice.value } };
153735→							return {
153736→								text: choice.label,
153737→								callback_data: buildCommandTextFromArgs(commandDefinition, args)
153738→							};
153739→						}));
153740→					}
153741→					const replyMarkup = buildInlineKeyboard(rows);
153742→					await withTelegramApiErrorLogging({
153743→						operation: "sendMessage",
153744→						runtime,
153745→						fn: () => bot.api.sendMessage(chatId, title, {
153746→							...replyMarkup ? { reply_markup: replyMarkup } : {},
153747→							...threadParams
153748→						})
153749→					});
153750→					return;
153751→				}
153752→				const baseSessionKey = route.sessionKey;
153753→				const dmThreadId = threadSpec.scope === "dm" ? threadSpec.id : void 0;
153754→				const sessionKey = (dmThreadId != null ? resolveThreadSessionKeys({
153755→					baseSessionKey,
153756→					threadId: `${chatId}:${dmThreadId}`
153757→				}) : null)?.sessionKey ?? baseSessionKey;
153758→				const { skillFilter, groupSystemPrompt } = resolveTelegramGroupPromptSettings({
153759→					groupConfig,
153760→					topicConfig
153761→				});
153762→				const { sessionKey: commandSessionKey, commandTargetSessionKey } = resolveNativeCommandSessionTargets({
153763→					agentId: route.agentId,
153764→					sessionPrefix: "telegram:slash",
153765→					userId: String(senderId || chatId),
153766→					targetSessionKey: sessionKey
153767→				});
153768→				const deliveryBaseOptions = buildCommandDeliveryBaseOptions({
153769→					chatId,
153770→					accountId: route.accountId,
153771→					sessionKeyForInternalHooks: commandSessionKey,
153772→					mirrorIsGroup: isGroup,
153773→					mirrorGroupId: isGroup ? String(chatId) : void 0,
153774→					mediaLocalRoots,
153775→					threadSpec,
153776→					tableMode,
153777→					chunkMode
153778→				});
153779→				const conversationLabel = isGroup ? msg.chat.title ? `${msg.chat.title} id:${chatId}` : `group:${chatId}` : buildSenderName(msg) ?? String(senderId || chatId);
153780→				const ctxPayload = finalizeInboundContext({
153781→					Body: prompt,
153782→					BodyForAgent: prompt,
153783→					RawBody: prompt,
153784→					CommandBody: prompt,
153785→					CommandArgs: commandArgs,
153786→					From: isGroup ? buildTelegramGroupFrom(chatId, resolvedThreadId) : `telegram:${chatId}`,
153787→					To: `slash:${senderId || chatId}`,
153788→					ChatType: isGroup ? "group" : "direct",
153789→					ConversationLabel: conversationLabel,
153790→					GroupSubject: isGroup ? msg.chat.title ?? void 0 : void 0,
153791→					GroupSystemPrompt: isGroup || !isGroup && groupConfig ? groupSystemPrompt : void 0,
153792→					SenderName: buildSenderName(msg),
153793→					SenderId: senderId || void 0,
153794→					SenderUsername: senderUsername || void 0,
153795→					Surface: "telegram",
153796→					Provider: "telegram",
153797→					MessageSid: String(msg.message_id),
153798→					Timestamp: msg.date ? msg.date * 1e3 : void 0,
153799→					WasMentioned: true,
153800→					CommandAuthorized: commandAuthorized,
153801→					CommandSource: "native",
153802→					SessionKey: commandSessionKey,
153803→					AccountId: route.accountId,
153804→					CommandTargetSessionKey: commandTargetSessionKey,
153805→					MessageThreadId: threadSpec.id,
153806→					IsForum: isForum,
153807→					OriginatingChannel: "telegram",
153808→					OriginatingTo: `telegram:${chatId}`
153809→				});
153810→				await recordInboundSessionMetaSafe({
153811→					cfg,
153812→					agentId: route.agentId,
153813→					sessionKey: ctxPayload.SessionKey ?? route.sessionKey,
153814→					ctx: ctxPayload,
153815→					onError: (err) => runtime.error?.(danger(`telegram slash: failed updating session meta: ${String(err)}`))
153816→				});
153817→				const disableBlockStreaming = typeof telegramCfg.blockStreaming === "boolean" ? !telegramCfg.blockStreaming : void 0;
153818→				const deliveryState = {
153819→					delivered: false,
153820→					skippedNonSilent: 0
153821→				};
153822→				const { onModelSelected, ...prefixOptions } = createReplyPrefixOptions({
153823→					cfg,
153824→					agentId: route.agentId,
153825→					channel: "telegram",
153826→					accountId: route.accountId
153827→				});
153828→				await dispatchReplyWithBufferedBlockDispatcher({
153829→					ctx: ctxPayload,
153830→					cfg,
153831→					dispatcherOptions: {
153832→						...prefixOptions,
153833→						deliver: async (payload, _info) => {
153834→							if (shouldSuppressLocalTelegramExecApprovalPrompt({
153835→								cfg,
153836→								accountId: route.accountId,
153837→								payload
153838→							})) {
153839→								deliveryState.delivered = true;
153840→								return;
153841→							}
153842→							if ((await deliverReplies({
153843→								replies: [payload],
153844→								...deliveryBaseOptions
153845→							})).delivered) deliveryState.delivered = true;
153846→						},
153847→						onSkip: (_payload, info) => {
153848→							if (info.reason !== "silent") deliveryState.skippedNonSilent += 1;
153849→						},
153850→						onError: (err, info) => {
153851→							runtime.error?.(danger(`telegram slash ${info.kind} reply failed: ${String(err)}`));
153852→						}
153853→					},
153854→					replyOptions: {
153855→						skillFilter,
153856→						disableBlockStreaming,
153857→						onModelSelected
153858→					}
153859→				});
153860→				if (!deliveryState.delivered && deliveryState.skippedNonSilent > 0) await deliverReplies({
153861→					replies: [{ text: EMPTY_RESPONSE_FALLBACK }],
153862→					...deliveryBaseOptions
153863→				});
153864→			});
153865→		}
153866→		for (const pluginCommand of pluginCatalog.commands) bot.command(pluginCommand.command, async (ctx) => {
153867→			const msg = ctx.message;
153868→			if (!msg) return;
153869→			if (shouldSkipUpdate(ctx)) return;
153870→			const chatId = msg.chat.id;
153871→			const rawText = ctx.match?.trim() ?? "";
153872→			const commandBody = `/${pluginCommand.command}${rawText ? ` ${rawText}` : ""}`;
153873→			const match = matchPluginCommand(commandBody);
153874→			if (!match) {
153875→				await withTelegramApiErrorLogging({
153876→					operation: "sendMessage",
153877→					runtime,
153878→					fn: () => bot.api.sendMessage(chatId, "Command not found.")
153879→				});
153880→				return;
153881→			}
153882→			const auth = await resolveTelegramCommandAuth({
153883→				msg,
153884→				bot,
153885→				cfg,
153886→				accountId,
153887→				telegramCfg,
153888→				allowFrom,
153889→				groupAllowFrom,
153890→				useAccessGroups,
153891→				resolveGroupPolicy,
153892→				resolveTelegramGroupConfig,
153893→				requireAuth: match.command.requireAuth !== false
153894→			});
153895→			if (!auth) return;
153896→			const { senderId, commandAuthorized, isGroup, isForum, resolvedThreadId } = auth;
153897→			const runtimeContext = await resolveCommandRuntimeContext({
153898→				msg,
153899→				isGroup,
153900→				isForum,
153901→				resolvedThreadId,
153902→				senderId,
153903→				topicAgentId: auth.topicConfig?.agentId
153904→			});
153905→			if (!runtimeContext) return;
153906→			const { threadSpec, route, mediaLocalRoots, tableMode, chunkMode } = runtimeContext;
153907→			const deliveryBaseOptions = buildCommandDeliveryBaseOptions({
153908→				chatId,
153909→				accountId: route.accountId,
153910→				sessionKeyForInternalHooks: route.sessionKey,
153911→				mirrorIsGroup: isGroup,
153912→				mirrorGroupId: isGroup ? String(chatId) : void 0,
153913→				mediaLocalRoots,
153914→				threadSpec,
153915→				tableMode,
153916→				chunkMode
153917→			});
153918→			const from = isGroup ? buildTelegramGroupFrom(chatId, threadSpec.id) : `telegram:${chatId}`;
153919→			const to = `telegram:${chatId}`;
153920→			const result = await executePluginCommand({
153921→				command: match.command,
153922→				args: match.args,
153923→				senderId,
153924→				channel: "telegram",
153925→				isAuthorizedSender: commandAuthorized,
153926→				commandBody,
153927→				config: cfg,
153928→				from,
153929→				to,

<system-reminder>
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
</system-reminder>


Bash

grep -n "nativeCommands\|/models\|models.*command" /usr/lib/node_modules/openclaw/dist/discord-CcCLMjHw.js | grep -B 5 -A 5 "models\|/models" | head -60


21863:			nativeCommands: true,
21949:			nativeCommands: true,
22085:			nativeCommands: true,
25772:	if (/models\/[^\s]+ is not found/i.test(raw)) return true;
27684:/** Defaults for models discovered from GET /v1/models. */
27778:* Discover chat-completion models from Hugging Face Inference Providers (GET /v1/models).
27786:		const response = await fetch(`${HUGGINGFACE_BASE_URL}/models`, {
27794:			log$37.warn(`GET /v1/models failed: HTTP ${response.status}, using static catalog`);
27891:* The /api/gateway/models endpoint is public and doesn't require authentication.
28500:* The /models endpoint is public and doesn't require authentication.
28506:			const currentResponse = await fetch(`${VENICE_BASE_URL}/models`, {
28683:		const response = await fetch(`${VERCEL_AI_GATEWAY_BASE_URL}/v1/models`, { signal: AbortSignal.timeout(5e3) });
29209://#region src/agents/models-config.providers.static.ts
30437://#region src/agents/models-config.merge.ts
30576://#region src/agents/models-config.plan.ts
30645://#region src/agents/models-config.ts
34152:	const url = `${baseUrl}/models/${model}:generateContent`;
42253:			textAlias: "/models",
42314:		...listChannelDocks().filter((dock) => dock.capabilities.nativeCommands).map((dock) => defineDockCommand(dock))
42337:	cachedNativeCommandSurfaces = new Set(listChannelDocks().filter((dock) => dock.capabilities.nativeCommands).map((dock) => dock.id));
76973:	const url = `${(params.baseUrl ?? "https://generativelanguage.googleapis.com").replace(/\/+$/, "").replace(/\/v1beta$/, "")}/v1beta/models/${encodeURIComponent(params.modelId)}:generateContent?key=${encodeURIComponent(apiKey)}`;
88920:		url: `${GEMINI_API_BASE}/models/${params.model}:generateContent`,
112034:	if (!body.startsWith("/models")) return null;
112035:	const { provider, page, pageSize, all } = parseModelsArgs(body.replace(/^\/models\b/i, "").trim());
112053:			"Use: /models <provider>",
112063:		"Use: /models <provider>"
112076:		"Browse: /models",
112108:		`Try: /models ${provider} ${safePage}`,
112109:		`All: /models ${provider} all`
112117:	if (!all && safePage < pageCount) lines.push(`More: /models ${provider} ${safePage + 1}`);
112118:	if (!all) lines.push(`All: /models ${provider} all`);
112124:	if (!commandBodyNormalized.startsWith("/models")) return null;
112125:	const unauthorized = rejectUnauthorizedCommand(params, "/models");
116233:		return { error: `Unrecognized model "${rawTrimmed}". Use /models to list providers, or /models <provider> to list models.` };
116252:	return { error: `Model "${resolved.ref.provider}/${resolved.ref.model}" is not allowed. Use /models to list providers, or /models <provider> to list models.` };
116378:		commandBodyNormalized: "/models"
116408:			"Browse: /models (providers) or /models <provider> (models)",
116412:	const modelsPath = `${params.agentDir}/models.json`;
116476:		"Browse: /models or /models <provider>",
148170:	let nativeCommands = [];
148175:		nativeCommands = slashCommandsRuntime.listNativeCommandSpecsForConfig(cfg, {
148180:	if (nativeCommands.length > 0) {
148182:		for (const command of nativeCommands) ctx.app.command(`/${command.name}`, async ({ command: cmd, ack, respond, body }) => {
148206:	if (nativeCommands.length === 0 || !supportsInteractiveArgMenus) return;
153581:	const nativeCommands = nativeEnabled ? listNativeCommandSpecsForConfig(cfg, {
153595:		existingCommands: new Set([...nativeCommands.map((command) => normalizeTelegramCommandName(command.name)), ...customCommands.map((command) => command.command)].map((command) => command.toLowerCase()))
153599:		...nativeCommands.map((command) => {
153687:		for (const command of nativeCommands) {
157486://#region src/agents/models-config.providers.ts


116640→		allowedModelCatalog,
116641→		provider
116642→	});
116643→	if (modelResolution.errorText) return { text: modelResolution.errorText };
116644→	const modelSelection = modelResolution.modelSelection;
116645→	const profileOverride = modelResolution.profileOverride;
116646→	const resolvedProvider = modelSelection?.provider ?? provider;
116647→	const resolvedModel = modelSelection?.model ?? model;
116648→	const fastModeState = resolveFastModeState({
116649→		cfg: params.cfg,
116650→		provider: resolvedProvider,
116651→		model: resolvedModel,
116652→		sessionEntry
116653→	});
116654→	const effectiveFastMode = directives.fastMode ?? currentFastMode ?? fastModeState.enabled;
116655→	const effectiveFastModeSource = directives.fastMode !== void 0 ? "session" : fastModeState.source;
116656→	if (directives.hasThinkDirective && !directives.thinkLevel) {
116657→		if (!directives.rawThinkLevel) return { text: withOptions(`Current thinking level: ${currentThinkLevel ?? "off"}.`, formatThinkingLevels(resolvedProvider, resolvedModel)) };
116658→		return { text: `Unrecognized thinking level "${directives.rawThinkLevel}". Valid levels: ${formatThinkingLevels(resolvedProvider, resolvedModel)}.` };
116659→	}
116660→	if (directives.hasVerboseDirective && !directives.verboseLevel) {
116661→		if (!directives.rawVerboseLevel) return { text: withOptions(`Current verbose level: ${currentVerboseLevel ?? "off"}.`, "on, full, off") };
116662→		return { text: `Unrecognized verbose level "${directives.rawVerboseLevel}". Valid levels: off, on, full.` };
116663→	}
116664→	if (directives.hasFastDirective && directives.fastMode === void 0) {
116665→		if (!directives.rawFastMode) return { text: withOptions(`Current fast mode: ${effectiveFastMode ? "on" : "off"}${effectiveFastModeSource === "config" ? " (config)" : effectiveFastModeSource === "default" ? " (default)" : ""}.`, "on, off") };
116666→		return { text: `Unrecognized fast mode "${directives.rawFastMode}". Valid levels: on, off.` };
116667→	}
116668→	if (directives.hasReasoningDirective && !directives.reasoningLevel) {
116669→		if (!directives.rawReasoningLevel) return { text: withOptions(`Current reasoning level: ${currentReasoningLevel ?? "off"}.`, "on, off, stream") };
116670→		return { text: `Unrecognized reasoning level "${directives.rawReasoningLevel}". Valid levels: on, off, stream.` };
116671→	}
116672→	if (directives.hasElevatedDirective && !directives.elevatedLevel) {
116673→		if (!directives.rawElevatedLevel) {
116674→			if (!elevatedEnabled || !elevatedAllowed) return { text: formatElevatedUnavailableText({
116675→				runtimeSandboxed: runtimeIsSandboxed,
116676→				failures: params.elevatedFailures,
116677→				sessionKey: params.sessionKey
116678→			}) };
116679→			return { text: [withOptions(`Current elevated level: ${currentElevatedLevel ?? "off"}.`, "on, off, ask, full"), shouldHintDirectRuntime ? formatElevatedRuntimeHint() : null].filter(Boolean).join("\n") };
116680→		}
116681→		return { text: `Unrecognized elevated level "${directives.rawElevatedLevel}". Valid levels: off, on, ask, full.` };
116682→	}
116683→	if (directives.hasElevatedDirective && (!elevatedEnabled || !elevatedAllowed)) return { text: formatElevatedUnavailableText({
116684→		runtimeSandboxed: runtimeIsSandboxed,
116685→		failures: params.elevatedFailures,
116686→		sessionKey: params.sessionKey
116687→	}) };
116688→	if (directives.hasExecDirective) {
116689→		if (directives.invalidExecHost) return { text: `Unrecognized exec host "${directives.rawExecHost ?? ""}". Valid hosts: sandbox, gateway, node.` };
116690→		if (directives.invalidExecSecurity) return { text: `Unrecognized exec security "${directives.rawExecSecurity ?? ""}". Valid: deny, allowlist, full.` };
116691→		if (directives.invalidExecAsk) return { text: `Unrecognized exec ask "${directives.rawExecAsk ?? ""}". Valid: off, on-miss, always.` };
116692→		if (directives.invalidExecNode) return { text: "Exec node requires a value." };
116693→		if (!directives.hasExecOptions) {
116694→			const execDefaults = resolveExecDefaults({
116695→				cfg: params.cfg,
116696→				sessionEntry,
116697→				agentId: activeAgentId
116698→			});
116699→			const nodeLabel = execDefaults.node ? `node=${execDefaults.node}` : "node=(unset)";
116700→			return { text: withOptions(`Current exec defaults: host=${execDefaults.host}, security=${execDefaults.security}, ask=${execDefaults.ask}, ${nodeLabel}.`, "host=sandbox|gateway|node, security=deny|allowlist|full, ask=off|on-miss|always, node=<id>") };
116701→		}
116702→	}
116703→	const queueAck = maybeHandleQueueDirective({
116704→		directives,
116705→		cfg: params.cfg,
116706→		channel: provider,
116707→		sessionEntry
116708→	});
116709→	if (queueAck) return queueAck;
116710→	if (directives.hasThinkDirective && directives.thinkLevel === "xhigh" && !supportsXHighThinking(resolvedProvider, resolvedModel)) return { text: `Thinking level "xhigh" is only supported for ${formatXHighModelHint()}.` };
116711→	const nextThinkLevel = directives.hasThinkDirective ? directives.thinkLevel : sessionEntry?.thinkingLevel ?? currentThinkLevel;
116712→	const shouldDowngradeXHigh = !directives.hasThinkDirective && nextThinkLevel === "xhigh" && !supportsXHighThinking(resolvedProvider, resolvedModel);
116713→	const prevElevatedLevel = currentElevatedLevel ?? sessionEntry.elevatedLevel ?? (elevatedAllowed ? "on" : "off");
116714→	const prevReasoningLevel = currentReasoningLevel ?? sessionEntry.reasoningLevel ?? "off";
116715→	let elevatedChanged = directives.hasElevatedDirective && directives.elevatedLevel !== void 0 && elevatedEnabled && elevatedAllowed;
116716→	const fastModeChanged = directives.hasFastDirective && directives.fastMode !== void 0 && directives.fastMode !== currentFastMode;
116717→	let reasoningChanged = directives.hasReasoningDirective && directives.reasoningLevel !== void 0;
116718→	if (directives.hasThinkDirective && directives.thinkLevel) sessionEntry.thinkingLevel = directives.thinkLevel;
116719→	if (directives.hasFastDirective && directives.fastMode !== void 0) sessionEntry.fastMode = directives.fastMode;
116720→	if (shouldDowngradeXHigh) sessionEntry.thinkingLevel = "high";
116721→	if (directives.hasVerboseDirective && directives.verboseLevel) applyVerboseOverride(sessionEntry, directives.verboseLevel);
116722→	if (directives.hasReasoningDirective && directives.reasoningLevel) {
116723→		if (directives.reasoningLevel === "off") sessionEntry.reasoningLevel = "off";
116724→		else sessionEntry.reasoningLevel = directives.reasoningLevel;
116725→		reasoningChanged = directives.reasoningLevel !== prevReasoningLevel && directives.reasoningLevel !== void 0;
116726→	}
116727→	if (directives.hasElevatedDirective && directives.elevatedLevel) {
116728→		sessionEntry.elevatedLevel = directives.elevatedLevel;
116729→		elevatedChanged = elevatedChanged || directives.elevatedLevel !== prevElevatedLevel && directives.elevatedLevel !== void 0;
116730→	}
116731→	if (directives.hasExecDirective && directives.hasExecOptions) {
116732→		if (directives.execHost) sessionEntry.execHost = directives.execHost;
116733→		if (directives.execSecurity) sessionEntry.execSecurity = directives.execSecurity;
116734→		if (directives.execAsk) sessionEntry.execAsk = directives.execAsk;
116735→		if (directives.execNode) sessionEntry.execNode = directives.execNode;
116736→	}
116737→	if (modelSelection) applyModelOverrideToSessionEntry({
116738→		entry: sessionEntry,
116739→		selection: modelSelection,
116740→		profileOverride
116741→	});
116742→	if (directives.hasQueueDirective && directives.queueReset) {
116743→		delete sessionEntry.queueMode;
116744→		delete sessionEntry.queueDebounceMs;
116745→		delete sessionEntry.queueCap;
116746→		delete sessionEntry.queueDrop;
116747→	} else if (directives.hasQueueDirective) {
116748→		if (directives.queueMode) sessionEntry.queueMode = directives.queueMode;
116749→		if (typeof directives.debounceMs === "number") sessionEntry.queueDebounceMs = directives.debounceMs;
116750→		if (typeof directives.cap === "number") sessionEntry.queueCap = directives.cap;
116751→		if (directives.dropPolicy) sessionEntry.queueDrop = directives.dropPolicy;
116752→	}
116753→	sessionEntry.updatedAt = Date.now();
116754→	sessionStore[sessionKey] = sessionEntry;
116755→	if (storePath) await updateSessionStore(storePath, (store) => {
116756→		store[sessionKey] = sessionEntry;
116757→	});
116758→	if (modelSelection) {
116759→		const nextLabel = `${modelSelection.provider}/${modelSelection.model}`;
116760→		if (nextLabel !== initialModelLabel) enqueueSystemEvent(formatModelSwitchEvent(nextLabel, modelSelection.alias), {
116761→			sessionKey,
116762→			contextKey: `model:${nextLabel}`
116763→		});
116764→	}
116765→	enqueueModeSwitchEvents({
116766→		enqueueSystemEvent,
116767→		sessionEntry,
116768→		sessionKey,
116769→		elevatedChanged,
116770→		reasoningChanged
116771→	});
116772→	const parts = [];
116773→	if (directives.hasThinkDirective && directives.thinkLevel) parts.push(directives.thinkLevel === "off" ? "Thinking disabled." : `Thinking level set to ${directives.thinkLevel}.`);
116774→	if (directives.hasFastDirective && directives.fastMode !== void 0) parts.push(directives.fastMode ? formatDirectiveAck("Fast mode enabled.") : formatDirectiveAck("Fast mode disabled."));
116775→	if (directives.hasVerboseDirective && directives.verboseLevel) parts.push(directives.verboseLevel === "off" ? formatDirectiveAck("Verbose logging disabled.") : directives.verboseLevel === "full" ? formatDirectiveAck("Verbose logging set to full.") : formatDirectiveAck("Verbose logging enabled."));
116776→	if (directives.hasReasoningDirective && directives.reasoningLevel) parts.push(directives.reasoningLevel === "off" ? formatDirectiveAck("Reasoning visibility disabled.") : directives.reasoningLevel === "stream" ? formatDirectiveAck("Reasoning stream enabled (Telegram only).") : formatDirectiveAck("Reasoning visibility enabled."));
116777→	if (directives.hasElevatedDirective && directives.elevatedLevel) {
116778→		parts.push(directives.elevatedLevel === "off" ? formatDirectiveAck("Elevated mode disabled.") : directives.elevatedLevel === "full" ? formatDirectiveAck("Elevated mode set to full (auto-approve).") : formatDirectiveAck("Elevated mode set to ask (approvals may still apply)."));
116779→		if (shouldHintDirectRuntime) parts.push(formatElevatedRuntimeHint());
116780→	}
116781→	if (directives.hasExecDirective && directives.hasExecOptions) {
116782→		const execParts = [];
116783→		if (directives.execHost) execParts.push(`host=${directives.execHost}`);
116784→		if (directives.execSecurity) execParts.push(`security=${directives.execSecurity}`);
116785→		if (directives.execAsk) execParts.push(`ask=${directives.execAsk}`);
116786→		if (directives.execNode) execParts.push(`node=${directives.execNode}`);
116787→		if (execParts.length > 0) parts.push(formatDirectiveAck(`Exec defaults set (${execParts.join(", ")}).`));
116788→	}
116789→	if (shouldDowngradeXHigh) parts.push(`Thinking level set to high (xhigh not supported for ${resolvedProvider}/${resolvedModel}).`);
116790→	if (modelSelection) {
116791→		const label = `${modelSelection.provider}/${modelSelection.model}`;
116792→		const labelWithAlias = modelSelection.alias ? `${modelSelection.alias} (${label})` : label;
116793→		parts.push(modelSelection.isDefault ? `Model reset to default (${labelWithAlias}).` : `Model set to ${labelWithAlias}.`);
116794→		if (profileOverride) parts.push(`Auth profile set to ${profileOverride}.`);
116795→	}
116796→	if (directives.hasQueueDirective && directives.queueMode) parts.push(formatDirectiveAck(`Queue mode set to ${directives.queueMode}.`));
116797→	else if (directives.hasQueueDirective && directives.queueReset) parts.push(formatDirectiveAck("Queue mode reset to default."));
116798→	if (directives.hasQueueDirective && typeof directives.debounceMs === "number") parts.push(formatDirectiveAck(`Queue debounce set to ${directives.debounceMs}ms.`));
116799→	if (directives.hasQueueDirective && typeof directives.cap === "number") parts.push(formatDirectiveAck(`Queue cap set to ${directives.cap}.`));
116800→	if (directives.hasQueueDirective && directives.dropPolicy) parts.push(formatDirectiveAck(`Queue drop set to ${directives.dropPolicy}.`));
116801→	if (fastModeChanged) enqueueSystemEvent(`Fast mode ${sessionEntry.fastMode ? "enabled" : "disabled"}.`, {
116802→		sessionKey,
116803→		contextKey: `fast:${sessionEntry.fastMode ? "on" : "off"}`
116804→	});
116805→	const ack = parts.join(" ").trim();
116806→	if (!ack && directives.hasStatusDirective) return;
116807→	return { text: ack || "OK." };
116808→}
116809→//#endregion
116810→//#region src/auto-reply/reply/directive-handling.levels.ts
116811→async function resolveCurrentDirectiveLevels(params) {
116812→	return {
116813→		currentThinkLevel: params.sessionEntry?.thinkingLevel ?? await params.resolveDefaultThinkingLevel() ?? params.agentCfg?.thinkingDefault,
116814→		currentFastMode: typeof params.sessionEntry?.fastMode === "boolean" ? params.sessionEntry.fastMode : void 0,
116815→		currentVerboseLevel: params.sessionEntry?.verboseLevel ?? params.agentCfg?.verboseDefault,
116816→		currentReasoningLevel: params.sessionEntry?.reasoningLevel ?? "off",
116817→		currentElevatedLevel: params.sessionEntry?.elevatedLevel ?? params.agentCfg?.elevatedDefault
116818→	};
116819→}
116820→//#endregion
116821→//#region src/auto-reply/model.ts
116822→function extractModelDirective(body, options) {
116823→	if (!body) return {
116824→		cleaned: "",
116825→		hasDirective: false
116826→	};
116827→	const modelMatch = body.match(/(?:^|\s)\/model(?=$|\s|:)\s*:?\s*([A-Za-z0-9_.:@-]+(?:\/[A-Za-z0-9_.:@-]+)*)?/i);
116828→	const aliases = (options?.aliases ?? []).map((alias) => alias.trim()).filter(Boolean);
116829→	const aliasMatch = modelMatch || aliases.length === 0 ? null : body.match(new RegExp(`(?:^|\\s)\\/(${aliases.map(escapeRegExp).join("|")})(?=$|\\s|:)(?:\\s*:\\s*)?`, "i"));
116830→	const match = modelMatch ?? aliasMatch;
116831→	const raw = modelMatch ? modelMatch?.[1]?.trim() : aliasMatch?.[1]?.trim();
116832→	let rawModel = raw;
116833→	let rawProfile;
116834→	if (raw) {
116835→		const split = splitTrailingAuthProfile(raw);
116836→		rawModel = split.model;
116837→		rawProfile = split.profile;
116838→	}
116839→	return {

<system-reminder>
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
</system-reminder>


112030→	})}) — ${params.total} available`;
112031→}
112032→async function resolveModelsCommandReply(params) {
112033→	const body = params.commandBodyNormalized.trim();
112034→	if (!body.startsWith("/models")) return null;
112035→	const { provider, page, pageSize, all } = parseModelsArgs(body.replace(/^\/models\b/i, "").trim());
112036→	const { byProvider, providers } = await buildModelsProviderData(params.cfg, params.agentId);
112037→	const isTelegram = params.surface === "telegram";
112038→	if (!provider) {
112039→		if (isTelegram && providers.length > 0) return {
112040→			text: "Select a provider:",
112041→			channelData: { telegram: { buttons: buildProviderKeyboard(providers.map((p) => ({
112042→				id: p,
112043→				count: byProvider.get(p)?.size ?? 0
112044→			}))) } }
112045→		};
112046→		return { text: [
112047→			"Providers:",
112048→			...providers.map((p) => formatProviderLine({
112049→				provider: p,
112050→				count: byProvider.get(p)?.size ?? 0
112051→			})),
112052→			"",
112053→			"Use: /models <provider>",
112054→			"Switch: /model <provider/model>"
112055→		].join("\n") };
112056→	}
112057→	if (!byProvider.has(provider)) return { text: [
112058→		`Unknown provider: ${provider}`,
112059→		"",
112060→		"Available providers:",
112061→		...providers.map((p) => `- ${p}`),
112062→		"",
112063→		"Use: /models <provider>"
112064→	].join("\n") };
112065→	const models = [...byProvider.get(provider) ?? /* @__PURE__ */ new Set()].toSorted();
112066→	const total = models.length;
112067→	const providerLabel = resolveProviderLabel$1({
112068→		provider,
112069→		cfg: params.cfg,
112070→		agentDir: params.agentDir,
112071→		sessionEntry: params.sessionEntry
112072→	});
112073→	if (total === 0) return { text: [
112074→		`Models (${providerLabel}) — none`,
112075→		"",
112076→		"Browse: /models",
112077→		"Switch: /model <provider/model>"
112078→	].join("\n") };
112079→	if (isTelegram) {
112080→		const telegramPageSize = getModelsPageSize();
112081→		const totalPages = calculateTotalPages(total, telegramPageSize);
112082→		const safePage = Math.max(1, Math.min(page, totalPages));
112083→		const buttons = buildModelsKeyboard({
112084→			provider,
112085→			models,
112086→			currentModel: params.currentModel,
112087→			currentPage: safePage,
112088→			totalPages,
112089→			pageSize: telegramPageSize
112090→		});
112091→		return {
112092→			text: formatModelsAvailableHeader({
112093→				provider,
112094→				total,
112095→				cfg: params.cfg,
112096→				agentDir: params.agentDir,
112097→				sessionEntry: params.sessionEntry
112098→			}),
112099→			channelData: { telegram: { buttons } }
112100→		};
112101→	}
112102→	const effectivePageSize = all ? total : pageSize;
112103→	const pageCount = effectivePageSize > 0 ? Math.ceil(total / effectivePageSize) : 1;
112104→	const safePage = all ? 1 : Math.max(1, Math.min(page, pageCount));
112105→	if (!all && page !== safePage) return { text: [
112106→		`Page out of range: ${page} (valid: 1-${pageCount})`,
112107→		"",
112108→		`Try: /models ${provider} ${safePage}`,
112109→		`All: /models ${provider} all`
112110→	].join("\n") };
112111→	const startIndex = (safePage - 1) * effectivePageSize;
112112→	const endIndexExclusive = Math.min(total, startIndex + effectivePageSize);
112113→	const pageModels = models.slice(startIndex, endIndexExclusive);
112114→	const lines = [`Models (${providerLabel}) — showing ${startIndex + 1}-${endIndexExclusive} of ${total} (page ${safePage}/${pageCount})`];
112115→	for (const id of pageModels) lines.push(`- ${provider}/${id}`);
112116→	lines.push("", "Switch: /model <provider/model>");
112117→	if (!all && safePage < pageCount) lines.push(`More: /models ${provider} ${safePage + 1}`);
112118→	if (!all) lines.push(`All: /models ${provider} all`);
112119→	return { text: lines.join("\n") };
112120→}
112121→const handleModelsCommand = async (params, allowTextCommands) => {
112122→	if (!allowTextCommands) return null;
112123→	const commandBodyNormalized = params.command.commandBodyNormalized.trim();
112124→	if (!commandBodyNormalized.startsWith("/models")) return null;
112125→	const unauthorized = rejectUnauthorizedCommand(params, "/models");
112126→	if (unauthorized) return unauthorized;
112127→	const modelsAgentId = params.agentId ?? resolveSessionAgentId({
112128→		sessionKey: params.sessionKey,
112129→		config: params.cfg
112130→	});
112131→	const modelsAgentDir = resolveAgentDir(params.cfg, modelsAgentId);
112132→	const reply = await resolveModelsCommandReply({
112133→		cfg: params.cfg,
112134→		commandBodyNormalized,
112135→		surface: params.ctx.Surface,
112136→		currentModel: params.model ? `${params.provider}/${params.model}` : void 0,
112137→		agentId: modelsAgentId,
112138→		agentDir: modelsAgentDir,
112139→		sessionEntry: params.sessionEntry
112140→	});
112141→	if (!reply) return null;
112142→	return {
112143→		reply,
112144→		shouldContinue: false
112145→	};
112146→};
112147→//#endregion
112148→//#region src/auto-reply/reply/commands-plugin.ts
112149→/**
112150→* Plugin Command Handler
112151→*
112152→* Handles commands registered by plugins, bypassing the LLM agent.
112153→* This handler is called before built-in command handlers.
112154→*/
112155→/**
112156→* Handle plugin-registered commands.
112157→* Returns a result if a plugin command was matched and executed,
112158→* or null to continue to the next handler.
112159→*/
112160→const handlePluginCommand = async (params, allowTextCommands) => {
112161→	const { command, cfg } = params;
112162→	if (!allowTextCommands) return null;
112163→	const match = matchPluginCommand(command.commandBodyNormalized);
112164→	if (!match) return null;
112165→	return {
112166→		shouldContinue: false,
112167→		reply: await executePluginCommand({
112168→			command: match.command,
112169→			args: match.args,
112170→			senderId: command.senderId,
112171→			channel: command.channel,
112172→			channelId: command.channelId,
112173→			isAuthorizedSender: command.isAuthorizedSender,
112174→			commandBody: command.commandBodyNormalized,
112175→			config: cfg,
112176→			from: command.from,
112177→			to: command.to,
112178→			accountId: params.ctx.AccountId ?? void 0,
112179→			messageThreadId: typeof params.ctx.MessageThreadId === "number" ? params.ctx.MessageThreadId : void 0
112180→		})
112181→	};
112182→};
112183→//#endregion
112184→//#region src/discord/monitor/thread-bindings.persona.ts
112185→const THREAD_BINDING_PERSONA_MAX_CHARS = 80;
112186→function normalizePersonaLabel(value) {
112187→	if (!value) return;
112188→	return value.replace(/\s+/g, " ").trim() || void 0;
112189→}
112190→function resolveThreadBindingPersona(params) {
112191→	return `${SYSTEM_MARK} ${normalizePersonaLabel(params.label) || normalizePersonaLabel(params.agentId) || "agent"}`.slice(0, THREAD_BINDING_PERSONA_MAX_CHARS);
112192→}
112193→function resolveThreadBindingPersonaFromRecord(record) {
112194→	return resolveThreadBindingPersona({
112195→		label: record.label,
112196→		agentId: record.agentId
112197→	});
112198→}
112199→//#endregion
112200→//#region src/discord/monitor/thread-bindings.types.ts
112201→const THREAD_BINDINGS_SWEEP_INTERVAL_MS$1 = 12e4;
112202→const DEFAULT_THREAD_BINDING_IDLE_TIMEOUT_MS$1 = 1440 * 60 * 1e3;
112203→const RECENT_UNBOUND_WEBHOOK_ECHO_WINDOW_MS = 3e4;
112204→//#endregion
112205→//#region src/discord/monitor/thread-bindings.state.ts
112206→const THREAD_BINDINGS_STATE_KEY = "__openclawDiscordThreadBindingsState";
112207→function createThreadBindingsGlobalState() {
112208→	return {
112209→		managersByAccountId: /* @__PURE__ */ new Map(),
112210→		bindingsByThreadId: /* @__PURE__ */ new Map(),
112211→		bindingsBySessionKey: /* @__PURE__ */ new Map(),
112212→		tokensByAccountId: /* @__PURE__ */ new Map(),
112213→		recentUnboundWebhookEchoesByBindingKey: /* @__PURE__ */ new Map(),
112214→		reusableWebhooksByAccountChannel: /* @__PURE__ */ new Map(),
112215→		persistByAccountId: /* @__PURE__ */ new Map(),
112216→		loadedBindings: false,
112217→		lastPersistedAtMs: 0
112218→	};
112219→}
112220→function resolveThreadBindingsGlobalState() {
112221→	const runtimeGlobal = globalThis;
112222→	if (!runtimeGlobal[THREAD_BINDINGS_STATE_KEY]) runtimeGlobal[THREAD_BINDINGS_STATE_KEY] = createThreadBindingsGlobalState();
112223→	return runtimeGlobal[THREAD_BINDINGS_STATE_KEY];
112224→}
112225→const THREAD_BINDINGS_STATE = resolveThreadBindingsGlobalState();
112226→const MANAGERS_BY_ACCOUNT_ID$1 = THREAD_BINDINGS_STATE.managersByAccountId;
112227→const BINDINGS_BY_THREAD_ID = THREAD_BINDINGS_STATE.bindingsByThreadId;
112228→const BINDINGS_BY_SESSION_KEY = THREAD_BINDINGS_STATE.bindingsBySessionKey;
112229→const TOKENS_BY_ACCOUNT_ID = THREAD_BINDINGS_STATE.tokensByAccountId;

<system-reminder>
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
</system-reminder>


grep -n "\/model[^s]\|handleModelCommand\|selectModel\|applyModelOverride" /usr/lib/node_modules/openclaw/dist/discord-CcCLMjHw.js | head -40


<tool_use_error>InputValidationError: Bash failed due to the following issues:
An unexpected parameter `file_path` was provided
An unexpected parameter `offset` was provided
An unexpected parameter `limit` was provided</tool_use_error>

InputValidationError: [
  {
    "code": "unrecognized_keys",
    "keys": [
      "file_path",
      "offset",
      "limit"
    ],
    "path": [],
    "message": "Unrecognized keys: \"file_path\", \"offset\", \"limit\""
  }
]


grep -n "\/model[^s]\|handleModelCommand\|selectModel\|applyModelOverride" /usr/lib/node_modules/openclaw/dist/discord-CcCLMjHw.js | head -40


1823://#region src/agents/model-ref-profile.ts
27178://#region src/agents/model-auth-env-vars.ts
27228://#region src/agents/model-auth-markers.ts
28695:createSubsystemLogger("agents/model-providers");
29781:	* Allows plugins to override provider/model before model resolution.
30106://#region src/agents/model-auth.ts
30382://#region src/agents/model-suppression.ts
30744://#region src/agents/model-catalog.ts
35690:* Reads provider/model/apiKey from `tools.media.audio` in the openclaw config,
36800://#region src/agents/model-alias-lines.ts
36817://#region src/agents/model-compat.ts
36878://#region src/agents/model-forward-compat.ts
37241://#region src/agents/pi-embedded-runner/model.provider-normalization.ts
37280://#region src/agents/pi-embedded-runner/model.ts
42241:			textAlias: "/model",
42245:				description: "Model id (provider/model or id)",
50015://#region src/agents/model-tool-support.ts
67080://#region src/agents/model-fallback-observation.ts
67136://#region src/agents/model-fallback.ts
67737://#region src/agents/tools/model-config.helpers.ts
78990://#region src/channels/model-overrides.ts
79275://#region src/auto-reply/model-runtime.ts
79786:		"/model <id>",
80877://#region src/sessions/model-overrides.ts
80878:function applyModelOverrideToSessionEntry(params) {
80974://#region src/agents/model-auth-label.ts
81199:				if (applyModelOverrideToSessionEntry({
89557:				message: provider === "perplexity" ? "country filtering is only supported by the native Perplexity Search API path. Remove Perplexity baseUrl/model overrides or use a direct PERPLEXITY_API_KEY to enable it." : `country filtering is not supported by the ${provider} provider. Only Brave and Perplexity support country filtering.`,
89563:				message: provider === "perplexity" ? "language filtering is only supported by the native Perplexity Search API path. Remove Perplexity baseUrl/model overrides or use a direct PERPLEXITY_API_KEY to enable it." : `language filtering is not supported by the ${provider} provider. Only Brave and Perplexity support language filtering.`,
89623:				message: provider === "perplexity" ? "date_after/date_before are only supported by the native Perplexity Search API path. Remove Perplexity baseUrl/model overrides or use a direct PERPLEXITY_API_KEY to enable them." : `date_after/date_before filtering is not supported by the ${provider} provider. Only Brave and Perplexity support date filtering.`,
89651:				message: provider === "perplexity" ? "domain_filter is only supported by the native Perplexity Search API path. Remove Perplexity baseUrl/model overrides or use a direct PERPLEXITY_API_KEY to enable it." : `domain_filter is not supported by the ${provider} provider. Only Perplexity supports domain filtering.`,
89672:				message: "max_tokens and max_tokens_per_page are only supported by the native Perplexity Search API path. Remove Perplexity baseUrl/model overrides or use a direct PERPLEXITY_API_KEY to enable them.",
91488:	const detail = params.hasGatedCoreEntries && params.hasOtherEntries ? "Some entries are shipped core tools but unavailable in the current runtime/provider/model/config; other entries won't match any tool unless the plugin is enabled." : params.hasGatedCoreEntries ? "These entries are shipped core tools but unavailable in the current runtime/provider/model/config." : "These entries won't match any tool unless the plugin is enabled.";
95136:			"- session_status: show usage/time/model state and answer \"what model are we using?\""
95182:		params.modelAliasLines && params.modelAliasLines.length > 0 && !isMinimal ? "Prefer aliases when specifying model overrides; full provider/model is also accepted." : "",
111727://#region src/telegram/model-buttons.ts
111874:* Build "Browse providers" button for /model summary.
111907:* Build provider/model data from config and catalog.
112054:			"Switch: /model <provider/model>"
112077:		"Switch: /model <provider/model>"


116200→		if (candidates.length === 0) return {};
116201→		const bestScored = candidates.map((candidate) => {
116202→			const details = scoreFuzzyMatch({
116203→				provider: candidate.provider,
116204→				model: candidate.model,
116205→				fragment,
116206→				aliasIndex,
116207→				defaultProvider,
116208→				defaultModel
116209→			});
116210→			return Object.assign({ candidate }, details);
116211→		}).toSorted((a, b) => {
116212→			if (b.score !== a.score) return b.score - a.score;
116213→			if (a.isDefault !== b.isDefault) return a.isDefault ? -1 : 1;
116214→			if (a.variantMatchCount !== b.variantMatchCount) return b.variantMatchCount - a.variantMatchCount;
116215→			if (a.variantCount !== b.variantCount) return a.variantCount - b.variantCount;
116216→			if (a.modelLength !== b.modelLength) return a.modelLength - b.modelLength;
116217→			return a.key.localeCompare(b.key);
116218→		})[0];
116219→		const best = bestScored?.candidate;
116220→		if (!best || !bestScored) return {};
116221→		const minScore = providerFilter ? 90 : 120;
116222→		if (bestScored.score < minScore) return {};
116223→		return { selection: buildSelection(best.provider, best.model) };
116224→	};
116225→	const resolved = resolveModelRefFromString({
116226→		raw: rawTrimmed,
116227→		defaultProvider,
116228→		aliasIndex
116229→	});
116230→	if (!resolved) {
116231→		const fuzzy = resolveFuzzy({ fragment: rawTrimmed });
116232→		if (fuzzy.selection || fuzzy.error) return fuzzy;
116233→		return { error: `Unrecognized model "${rawTrimmed}". Use /models to list providers, or /models <provider> to list models.` };
116234→	}
116235→	const resolvedKey = modelKey(resolved.ref.provider, resolved.ref.model);
116236→	if (allowedModelKeys.size === 0 || allowedModelKeys.has(resolvedKey)) return { selection: {
116237→		provider: resolved.ref.provider,
116238→		model: resolved.ref.model,
116239→		isDefault: resolved.ref.provider === defaultProvider && resolved.ref.model === defaultModel,
116240→		alias: resolved.alias
116241→	} };
116242→	if (rawLower.includes("/")) {
116243→		const slash = rawTrimmed.indexOf("/");
116244→		const fuzzy = resolveFuzzy({
116245→			provider: normalizeProviderId(rawTrimmed.slice(0, slash).trim()),
116246→			fragment: rawTrimmed.slice(slash + 1).trim()
116247→		});
116248→		if (fuzzy.selection || fuzzy.error) return fuzzy;
116249→	}
116250→	const fuzzy = resolveFuzzy({ fragment: rawTrimmed });
116251→	if (fuzzy.selection || fuzzy.error) return fuzzy;
116252→	return { error: `Model "${resolved.ref.provider}/${resolved.ref.model}" is not allowed. Use /models to list providers, or /models <provider> to list models.` };
116253→}
116254→function resolveContextTokens(params) {
116255→	return params.agentCfg?.contextTokens ?? lookupContextTokens(params.model) ?? 2e5;
116256→}
116257→//#endregion
116258→//#region src/auto-reply/reply/directive-handling.model.ts
116259→function pushUniqueCatalogEntry(params) {
116260→	const provider = normalizeProviderId(params.provider);
116261→	const id = String(params.id ?? "").trim();
116262→	if (!provider || !id) return;
116263→	const key = modelKey(provider, id);
116264→	if (params.keys.has(key)) return;
116265→	params.keys.add(key);
116266→	params.out.push({
116267→		provider,
116268→		id,
116269→		name: params.fallbackNameToId ? params.name ?? id : params.name
116270→	});
116271→}
116272→function buildModelPickerCatalog(params) {
116273→	const resolvedDefault = resolveConfiguredModelRef({
116274→		cfg: params.cfg,
116275→		defaultProvider: params.defaultProvider,
116276→		defaultModel: params.defaultModel
116277→	});
116278→	const buildConfiguredCatalog = () => {
116279→		const out = [];
116280→		const keys = /* @__PURE__ */ new Set();
116281→		const pushRef = (ref, name) => {
116282→			pushUniqueCatalogEntry({
116283→				keys,
116284→				out,
116285→				provider: ref.provider,
116286→				id: ref.model,
116287→				name,
116288→				fallbackNameToId: true
116289→			});
116290→		};
116291→		const pushRaw = (raw) => {
116292→			const value = String(raw ?? "").trim();
116293→			if (!value) return;
116294→			const resolved = resolveModelRefFromString({
116295→				raw: value,
116296→				defaultProvider: params.defaultProvider,
116297→				aliasIndex: params.aliasIndex
116298→			});
116299→			if (!resolved) return;
116300→			pushRef(resolved.ref);
116301→		};
116302→		pushRef(resolvedDefault);
116303→		const modelConfig = params.cfg.agents?.defaults?.model;
116304→		const modelFallbacks = modelConfig && typeof modelConfig === "object" ? modelConfig.fallbacks ?? [] : [];
116305→		for (const fallback of modelFallbacks) pushRaw(String(fallback ?? ""));
116306→		const imageConfig = params.cfg.agents?.defaults?.imageModel;
116307→		if (imageConfig && typeof imageConfig === "object") {
116308→			pushRaw(imageConfig.primary);
116309→			for (const fallback of imageConfig.fallbacks ?? []) pushRaw(String(fallback ?? ""));
116310→		}
116311→		for (const raw of Object.keys(params.cfg.agents?.defaults?.models ?? {})) pushRaw(raw);
116312→		return out;
116313→	};
116314→	const keys = /* @__PURE__ */ new Set();
116315→	const out = [];
116316→	const push = (entry) => {
116317→		pushUniqueCatalogEntry({
116318→			keys,
116319→			out,
116320→			provider: entry.provider,
116321→			id: String(entry.id ?? ""),
116322→			name: entry.name,
116323→			fallbackNameToId: false
116324→		});
116325→	};
116326→	if (!(Object.keys(params.cfg.agents?.defaults?.models ?? {}).length > 0)) {
116327→		for (const entry of params.allowedModelCatalog) push({
116328→			provider: entry.provider,
116329→			id: entry.id ?? "",
116330→			name: entry.name
116331→		});
116332→		for (const entry of buildConfiguredCatalog()) push(entry);
116333→		return out;
116334→	}
116335→	for (const entry of params.allowedModelCatalog) push({
116336→		provider: entry.provider,
116337→		id: entry.id ?? "",
116338→		name: entry.name
116339→	});
116340→	for (const raw of Object.keys(params.cfg.agents?.defaults?.models ?? {})) {
116341→		const resolved = resolveModelRefFromString({
116342→			raw: String(raw),
116343→			defaultProvider: params.defaultProvider,
116344→			aliasIndex: params.aliasIndex
116345→		});
116346→		if (!resolved) continue;
116347→		push({
116348→			provider: resolved.ref.provider,
116349→			id: resolved.ref.model,
116350→			name: resolved.ref.model
116351→		});
116352→	}
116353→	if (resolvedDefault.model) push({
116354→		provider: resolvedDefault.provider,
116355→		id: resolvedDefault.model,
116356→		name: resolvedDefault.model
116357→	});
116358→	return out;
116359→}
116360→async function maybeHandleModelDirectiveInfo(params) {
116361→	if (!params.directives.hasModelDirective) return;
116362→	const rawDirective = params.directives.rawModelDirective?.trim();
116363→	const directive = rawDirective?.toLowerCase();
116364→	const wantsStatus = directive === "status";
116365→	const wantsSummary = !rawDirective;
116366→	const wantsLegacyList = directive === "list";
116367→	if (!wantsSummary && !wantsStatus && !wantsLegacyList) return;
116368→	if (params.directives.rawModelProfile) return { text: "Auth profile override requires a model selection." };
116369→	const pickerCatalog = buildModelPickerCatalog({
116370→		cfg: params.cfg,
116371→		defaultProvider: params.defaultProvider,
116372→		defaultModel: params.defaultModel,
116373→		aliasIndex: params.aliasIndex,
116374→		allowedModelCatalog: params.allowedModelCatalog
116375→	});
116376→	if (wantsLegacyList) return await resolveModelsCommandReply({
116377→		cfg: params.cfg,
116378→		commandBodyNormalized: "/models"
116379→	}) ?? { text: "No models available." };
116380→	if (wantsSummary) {
116381→		const modelRefs = resolveSelectedAndActiveModel({
116382→			selectedProvider: params.provider,
116383→			selectedModel: params.model,
116384→			sessionEntry: params.sessionEntry
116385→		});
116386→		const current = modelRefs.selected.label;
116387→		const isTelegram = params.surface === "telegram";
116388→		const activeRuntimeLine = modelRefs.activeDiffers ? `Active: ${modelRefs.active.label} (runtime)` : null;
116389→		if (isTelegram) {
116390→			const buttons = buildBrowseProvidersButton();
116391→			return {
116392→				text: [
116393→					`Current: ${current}${modelRefs.activeDiffers ? " (selected)" : ""}`,
116394→					activeRuntimeLine,
116395→					"",
116396→					"Tap below to browse models, or use:",
116397→					"/model <provider/model> to switch",
116398→					"/model status for details"
116399→				].filter(Boolean).join("\n"),
116400→				channelData: { telegram: { buttons } }
116401→			};
116402→		}
116403→		return { text: [
116404→			`Current: ${current}${modelRefs.activeDiffers ? " (selected)" : ""}`,
116405→			activeRuntimeLine,
116406→			"",
116407→			"Switch: /model <provider/model>",
116408→			"Browse: /models (providers) or /models <provider> (models)",
116409→			"More: /model status"
116410→		].filter(Boolean).join("\n") };
116411→	}
116412→	const modelsPath = `${params.agentDir}/models.json`;
116413→	const formatPath = (value) => shortenHomePath(value);
116414→	const authMode = "verbose";
116415→	if (pickerCatalog.length === 0) return { text: "No models available." };
116416→	const authByProvider = /* @__PURE__ */ new Map();
116417→	for (const entry of pickerCatalog) {
116418→		const provider = normalizeProviderId(entry.provider);
116419→		if (authByProvider.has(provider)) continue;
116420→		const auth = await resolveAuthLabel(provider, params.cfg, modelsPath, params.agentDir, authMode);
116421→		authByProvider.set(provider, formatAuthLabel(auth));
116422→	}
116423→	const modelRefs = resolveSelectedAndActiveModel({
116424→		selectedProvider: params.provider,
116425→		selectedModel: params.model,
116426→		sessionEntry: params.sessionEntry
116427→	});
116428→	const current = modelRefs.selected.label;
116429→	const defaultLabel = `${params.defaultProvider}/${params.defaultModel}`;
116430→	const lines = [
116431→		`Current: ${current}${modelRefs.activeDiffers ? " (selected)" : ""}`,
116432→		modelRefs.activeDiffers ? `Active: ${modelRefs.active.label} (runtime)` : null,
116433→		`Default: ${defaultLabel}`,
116434→		`Agent: ${params.activeAgentId}`,
116435→		`Auth file: ${formatPath(resolveAuthStorePathForDisplay(params.agentDir))}`
116436→	].filter((line) => Boolean(line));
116437→	if (params.resetModelOverride) lines.push(`(previous selection reset to default)`);
116438→	const byProvider = /* @__PURE__ */ new Map();
116439→	for (const entry of pickerCatalog) {
116440→		const provider = normalizeProviderId(entry.provider);
116441→		const models = byProvider.get(provider);
116442→		if (models) {
116443→			models.push(entry);
116444→			continue;
116445→		}
116446→		byProvider.set(provider, [entry]);
116447→	}
116448→	for (const provider of byProvider.keys()) {
116449→		const models = byProvider.get(provider);

<system-reminder>
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
</system-reminder>


116820→//#endregion
116821→//#region src/auto-reply/model.ts
116822→function extractModelDirective(body, options) {
116823→	if (!body) return {
116824→		cleaned: "",
116825→		hasDirective: false
116826→	};
116827→	const modelMatch = body.match(/(?:^|\s)\/model(?=$|\s|:)\s*:?\s*([A-Za-z0-9_.:@-]+(?:\/[A-Za-z0-9_.:@-]+)*)?/i);
116828→	const aliases = (options?.aliases ?? []).map((alias) => alias.trim()).filter(Boolean);
116829→	const aliasMatch = modelMatch || aliases.length === 0 ? null : body.match(new RegExp(`(?:^|\\s)\\/(${aliases.map(escapeRegExp).join("|")})(?=$|\\s|:)(?:\\s*:\\s*)?`, "i"));
116830→	const match = modelMatch ?? aliasMatch;
116831→	const raw = modelMatch ? modelMatch?.[1]?.trim() : aliasMatch?.[1]?.trim();
116832→	let rawModel = raw;
116833→	let rawProfile;
116834→	if (raw) {
116835→		const split = splitTrailingAuthProfile(raw);
116836→		rawModel = split.model;
116837→		rawProfile = split.profile;
116838→	}
116839→	return {
116840→		cleaned: match ? body.replace(match[0], " ").replace(/\s+/g, " ").trim() : body.trim(),
116841→		rawModel,
116842→		rawProfile,
116843→		hasDirective: !!match
116844→	};
116845→}
116846→//#endregion
116847→//#region src/auto-reply/reply/directive-handling.parse.ts
116848→function parseInlineDirectives(body, options) {
116849→	const { cleaned: thinkCleaned, thinkLevel, rawLevel: rawThinkLevel, hasDirective: hasThinkDirective } = extractThinkDirective(body);
116850→	const { cleaned: verboseCleaned, verboseLevel, rawLevel: rawVerboseLevel, hasDirective: hasVerboseDirective } = extractVerboseDirective(thinkCleaned);
116851→	const { cleaned: fastCleaned, fastMode, rawLevel: rawFastMode, hasDirective: hasFastDirective } = extractFastDirective(verboseCleaned);
116852→	const { cleaned: reasoningCleaned, reasoningLevel, rawLevel: rawReasoningLevel, hasDirective: hasReasoningDirective } = extractReasoningDirective(fastCleaned);
116853→	const { cleaned: elevatedCleaned, elevatedLevel, rawLevel: rawElevatedLevel, hasDirective: hasElevatedDirective } = options?.disableElevated ? {
116854→		cleaned: reasoningCleaned,
116855→		elevatedLevel: void 0,
116856→		rawLevel: void 0,
116857→		hasDirective: false
116858→	} : extractElevatedDirective(reasoningCleaned);
116859→	const { cleaned: execCleaned, execHost, execSecurity, execAsk, execNode, rawExecHost, rawExecSecurity, rawExecAsk, rawExecNode, hasExecOptions, invalidHost: invalidExecHost, invalidSecurity: invalidExecSecurity, invalidAsk: invalidExecAsk, invalidNode: invalidExecNode, hasDirective: hasExecDirective } = extractExecDirective(elevatedCleaned);
116860→	const { cleaned: statusCleaned, hasDirective: hasStatusDirective } = options?.allowStatusDirective !== false ? extractStatusDirective(execCleaned) : {
116861→		cleaned: execCleaned,
116862→		hasDirective: false
116863→	};
116864→	const { cleaned: modelCleaned, rawModel, rawProfile, hasDirective: hasModelDirective } = extractModelDirective(statusCleaned, { aliases: options?.modelAliases });
116865→	const { cleaned: queueCleaned, queueMode, queueReset, rawMode, debounceMs, cap, dropPolicy, rawDebounce, rawCap, rawDrop, hasDirective: hasQueueDirective, hasOptions: hasQueueOptions } = extractQueueDirective(modelCleaned);
116866→	return {
116867→		cleaned: queueCleaned,
116868→		hasThinkDirective,
116869→		thinkLevel,
116870→		rawThinkLevel,
116871→		hasVerboseDirective,
116872→		verboseLevel,
116873→		rawVerboseLevel,
116874→		hasFastDirective,
116875→		fastMode,
116876→		rawFastMode,
116877→		hasReasoningDirective,
116878→		reasoningLevel,
116879→		rawReasoningLevel,
116880→		hasElevatedDirective,
116881→		elevatedLevel,
116882→		rawElevatedLevel,
116883→		hasExecDirective,
116884→		execHost,
116885→		execSecurity,
116886→		execAsk,
116887→		execNode,
116888→		rawExecHost,
116889→		rawExecSecurity,
116890→		rawExecAsk,
116891→		rawExecNode,
116892→		hasExecOptions,
116893→		invalidExecHost,
116894→		invalidExecSecurity,
116895→		invalidExecAsk,
116896→		invalidExecNode,
116897→		hasStatusDirective,
116898→		hasModelDirective,
116899→		rawModelDirective: rawModel,
116900→		rawModelProfile: rawProfile,
116901→		hasQueueDirective,
116902→		queueMode,
116903→		queueReset,
116904→		rawQueueMode: rawMode,
116905→		debounceMs,
116906→		cap,
116907→		dropPolicy,
116908→		rawDebounce,
116909→		rawCap,
116910→		rawDrop,
116911→		hasQueueOptions
116912→	};
116913→}
116914→function isDirectiveOnly(params) {
116915→	const { directives, cleanedBody, ctx, cfg, agentId, isGroup } = params;
116916→	if (!directives.hasThinkDirective && !directives.hasVerboseDirective && !directives.hasFastDirective && !directives.hasReasoningDirective && !directives.hasElevatedDirective && !directives.hasExecDirective && !directives.hasModelDirective && !directives.hasQueueDirective) return false;
116917→	const stripped = stripStructuralPrefixes(cleanedBody ?? "");
116918→	return (isGroup ? stripMentions(stripped, ctx, cfg, agentId) : stripped).length === 0;
116919→}
116920→//#endregion
116921→//#region src/auto-reply/reply/directive-handling.fast-lane.ts
116922→async function applyInlineDirectivesFastLane(params) {
116923→	const { directives, commandAuthorized, ctx, cfg, agentId, isGroup, sessionEntry, sessionStore, sessionKey, storePath, elevatedEnabled, elevatedAllowed, elevatedFailures, messageProviderKey, defaultProvider, defaultModel, aliasIndex, allowedModelKeys, allowedModelCatalog, resetModelOverride, formatModelSwitchEvent, modelState } = params;
116924→	let { provider, model } = params;
116925→	if (!commandAuthorized || isDirectiveOnly({
116926→		directives,
116927→		cleanedBody: directives.cleaned,
116928→		ctx,
116929→		cfg,
116930→		agentId,
116931→		isGroup
116932→	})) return {
116933→		directiveAck: void 0,
116934→		provider,
116935→		model
116936→	};
116937→	const agentCfg = params.agentCfg;
116938→	const { currentThinkLevel, currentFastMode, currentVerboseLevel, currentReasoningLevel, currentElevatedLevel } = await resolveCurrentDirectiveLevels({
116939→		sessionEntry,
116940→		agentCfg,
116941→		resolveDefaultThinkingLevel: () => modelState.resolveDefaultThinkingLevel()
116942→	});
116943→	const directiveAck = await handleDirectiveOnly({
116944→		cfg,
116945→		directives,
116946→		sessionEntry,
116947→		sessionStore,
116948→		sessionKey,
116949→		storePath,
116950→		elevatedEnabled,
116951→		elevatedAllowed,
116952→		elevatedFailures,
116953→		messageProviderKey,
116954→		defaultProvider,
116955→		defaultModel,
116956→		aliasIndex,
116957→		allowedModelKeys,
116958→		allowedModelCatalog,
116959→		resetModelOverride,
116960→		provider,
116961→		model,
116962→		initialModelLabel: params.initialModelLabel,
116963→		formatModelSwitchEvent,
116964→		currentThinkLevel,
116965→		currentFastMode,
116966→		currentVerboseLevel,
116967→		currentReasoningLevel,
116968→		currentElevatedLevel
116969→	});
116970→	if (sessionEntry?.providerOverride) provider = sessionEntry.providerOverride;
116971→	if (sessionEntry?.modelOverride) model = sessionEntry.modelOverride;
116972→	return {
116973→		directiveAck,
116974→		provider,
116975→		model
116976→	};
116977→}
116978→//#endregion
116979→//#region src/auto-reply/reply/directive-handling.persist.ts
116980→async function persistInlineDirectives(params) {
116981→	const { directives, cfg, sessionEntry, sessionStore, sessionKey, storePath, elevatedEnabled, elevatedAllowed, defaultProvider, defaultModel, aliasIndex, allowedModelKeys, initialModelLabel, formatModelSwitchEvent, agentCfg } = params;
116982→	let { provider, model } = params;
116983→	const agentDir = resolveAgentDir(cfg, sessionKey ? resolveSessionAgentId({
116984→		sessionKey,
116985→		config: cfg
116986→	}) : resolveDefaultAgentId(cfg));
116987→	if (sessionEntry && sessionStore && sessionKey) {
116988→		const prevElevatedLevel = sessionEntry.elevatedLevel ?? agentCfg?.elevatedDefault ?? (elevatedAllowed ? "on" : "off");
116989→		const prevReasoningLevel = sessionEntry.reasoningLevel ?? "off";
116990→		let elevatedChanged = directives.hasElevatedDirective && directives.elevatedLevel !== void 0 && elevatedEnabled && elevatedAllowed;
116991→		let reasoningChanged = directives.hasReasoningDirective && directives.reasoningLevel !== void 0;
116992→		let updated = false;
116993→		if (directives.hasThinkDirective && directives.thinkLevel) {
116994→			sessionEntry.thinkingLevel = directives.thinkLevel;
116995→			updated = true;
116996→		}
116997→		if (directives.hasVerboseDirective && directives.verboseLevel) {
116998→			applyVerboseOverride(sessionEntry, directives.verboseLevel);
116999→			updated = true;
117000→		}
117001→		if (directives.hasReasoningDirective && directives.reasoningLevel) {
117002→			if (directives.reasoningLevel === "off") sessionEntry.reasoningLevel = "off";
117003→			else sessionEntry.reasoningLevel = directives.reasoningLevel;
117004→			reasoningChanged = reasoningChanged || directives.reasoningLevel !== prevReasoningLevel && directives.reasoningLevel !== void 0;
117005→			updated = true;
117006→		}
117007→		if (directives.hasElevatedDirective && directives.elevatedLevel && elevatedEnabled && elevatedAllowed) {
117008→			sessionEntry.elevatedLevel = directives.elevatedLevel;
117009→			elevatedChanged = elevatedChanged || directives.elevatedLevel !== prevElevatedLevel && directives.elevatedLevel !== void 0;
117010→			updated = true;
117011→		}
117012→		if (directives.hasExecDirective && directives.hasExecOptions) {
117013→			if (directives.execHost) {
117014→				sessionEntry.execHost = directives.execHost;
117015→				updated = true;
117016→			}
117017→			if (directives.execSecurity) {
117018→				sessionEntry.execSecurity = directives.execSecurity;
117019→				updated = true;

<system-reminder>
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
</system-reminder>


 80875→}
 80876→//#endregion
 80877→//#region src/sessions/model-overrides.ts
 80878→function applyModelOverrideToSessionEntry(params) {
 80879→	const { entry, selection, profileOverride } = params;
 80880→	const profileOverrideSource = params.profileOverrideSource ?? "user";
 80881→	let updated = false;
 80882→	let selectionUpdated = false;
 80883→	if (selection.isDefault) {
 80884→		if (entry.providerOverride) {
 80885→			delete entry.providerOverride;
 80886→			updated = true;
 80887→			selectionUpdated = true;
 80888→		}
 80889→		if (entry.modelOverride) {
 80890→			delete entry.modelOverride;
 80891→			updated = true;
 80892→			selectionUpdated = true;
 80893→		}
 80894→	} else {
 80895→		if (entry.providerOverride !== selection.provider) {
 80896→			entry.providerOverride = selection.provider;
 80897→			updated = true;
 80898→			selectionUpdated = true;
 80899→		}
 80900→		if (entry.modelOverride !== selection.model) {
 80901→			entry.modelOverride = selection.model;
 80902→			updated = true;
 80903→			selectionUpdated = true;
 80904→		}
 80905→	}
 80906→	const runtimeModel = typeof entry.model === "string" ? entry.model.trim() : "";
 80907→	const runtimeProvider = typeof entry.modelProvider === "string" ? entry.modelProvider.trim() : "";
 80908→	const runtimePresent = runtimeModel.length > 0 || runtimeProvider.length > 0;
 80909→	const runtimeAligned = runtimeModel === selection.model && (runtimeProvider.length === 0 || runtimeProvider === selection.provider);
 80910→	if (runtimePresent && (selectionUpdated || !runtimeAligned)) {
 80911→		if (entry.model !== void 0) {
 80912→			delete entry.model;
 80913→			updated = true;
 80914→		}
 80915→		if (entry.modelProvider !== void 0) {
 80916→			delete entry.modelProvider;
 80917→			updated = true;
 80918→		}
 80919→	}
 80920→	if (entry.contextTokens !== void 0 && (selectionUpdated || runtimePresent && !runtimeAligned)) {
 80921→		delete entry.contextTokens;
 80922→		updated = true;
 80923→	}
 80924→	if (profileOverride) {
 80925→		if (entry.authProfileOverride !== profileOverride) {
 80926→			entry.authProfileOverride = profileOverride;
 80927→			updated = true;
 80928→		}
 80929→		if (entry.authProfileOverrideSource !== profileOverrideSource) {
 80930→			entry.authProfileOverrideSource = profileOverrideSource;
 80931→			updated = true;
 80932→		}
 80933→		if (entry.authProfileOverrideCompactionCount !== void 0) {
 80934→			delete entry.authProfileOverrideCompactionCount;
 80935→			updated = true;
 80936→		}
 80937→	} else {
 80938→		if (entry.authProfileOverride) {
 80939→			delete entry.authProfileOverride;
 80940→			updated = true;
 80941→		}
 80942→		if (entry.authProfileOverrideSource) {
 80943→			delete entry.authProfileOverrideSource;
 80944→			updated = true;
 80945→		}
 80946→		if (entry.authProfileOverrideCompactionCount !== void 0) {
 80947→			delete entry.authProfileOverrideCompactionCount;
 80948→			updated = true;
 80949→		}
 80950→	}
 80951→	if (updated) {
 80952→		delete entry.fallbackNoticeSelectedModel;
 80953→		delete entry.fallbackNoticeActiveModel;
 80954→		delete entry.fallbackNoticeReason;
 80955→		entry.updatedAt = Date.now();
 80956→	}
 80957→	return { updated };
 80958→}
 80959→//#endregion
 80960→//#region src/sessions/session-id-resolution.ts
 80961→function resolvePreferredSessionKeyForSessionIdMatches(matches, sessionId) {
 80962→	if (matches.length === 0) return;
 80963→	if (matches.length === 1) return matches[0][0];
 80964→	const loweredSessionId = sessionId.trim().toLowerCase();
 80965→	const structuralMatches = matches.filter(([storeKey]) => {
 80966→		const requestKey = toAgentRequestSessionKey(storeKey)?.toLowerCase();
 80967→		return storeKey.toLowerCase().endsWith(`:${loweredSessionId}`) || requestKey === loweredSessionId || requestKey?.endsWith(`:${loweredSessionId}`) === true;
 80968→	});
 80969→	if (structuralMatches.length === 1) return structuralMatches[0][0];
 80970→	const [freshest, secondFreshest] = [...matches].toSorted((a, b) => (b[1]?.updatedAt ?? 0) - (a[1]?.updatedAt ?? 0));
 80971→	if ((freshest?.[1]?.updatedAt ?? 0) > (secondFreshest?.[1]?.updatedAt ?? 0)) return freshest?.[0];
 80972→}
 80973→//#endregion
 80974→//#region src/agents/model-auth-label.ts
 80975→function resolveModelAuthLabel(params) {
 80976→	const resolvedProvider = params.provider?.trim();
 80977→	if (!resolvedProvider) return;
 80978→	const providerKey = normalizeProviderId(resolvedProvider);
 80979→	const store = ensureAuthProfileStore(params.agentDir, { allowKeychainPrompt: false });
 80980→	const profileOverride = params.sessionEntry?.authProfileOverride?.trim();
 80981→	const candidates = [profileOverride, ...resolveAuthProfileOrder({
 80982→		cfg: params.cfg,
 80983→		store,
 80984→		provider: providerKey,
 80985→		preferredProfile: profileOverride
 80986→	})].filter(Boolean);
 80987→	for (const profileId of candidates) {
 80988→		const profile = store.profiles[profileId];
 80989→		if (!profile || normalizeProviderId(profile.provider) !== providerKey) continue;
 80990→		const label = resolveAuthProfileDisplayLabel({
 80991→			cfg: params.cfg,
 80992→			store,
 80993→			profileId
 80994→		});
 80995→		if (profile.type === "oauth") return `oauth${label ? ` (${label})` : ""}`;
 80996→		if (profile.type === "token") return `token${label ? ` (${label})` : ""}`;
 80997→		return `api-key${label ? ` (${label})` : ""}`;
 80998→	}
 80999→	const envKey = resolveEnvApiKey(providerKey);
 81000→	if (envKey?.apiKey) {
 81001→		if (envKey.source.includes("OAUTH_TOKEN")) return `oauth (${envKey.source})`;
 81002→		return `api-key (${envKey.source})`;
 81003→	}
 81004→	if (resolveUsableCustomProviderApiKey({
 81005→		cfg: params.cfg,
 81006→		provider: providerKey
 81007→	})) return `api-key (models.json)`;
 81008→	return "unknown";
 81009→}
 81010→//#endregion
 81011→//#region src/agents/tools/session-status-tool.ts
 81012→const SessionStatusToolSchema = Type.Object({
 81013→	sessionKey: Type.Optional(Type.String()),
 81014→	model: Type.Optional(Type.String())
 81015→});
 81016→function resolveSessionEntry(params) {
 81017→	const keyRaw = params.keyRaw.trim();
 81018→	if (!keyRaw) return null;
 81019→	const internal = resolveInternalSessionKey({
 81020→		key: keyRaw,
 81021→		alias: params.alias,
 81022→		mainKey: params.mainKey
 81023→	});
 81024→	const candidates = new Set([keyRaw, internal]);

<system-reminder>
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
</system-reminder>


find /home/ubuntu/.openclaw -maxdepth 3 -type f -name "*telegram*" -o -name "*channel*" 2>/dev/null | head -20


total 568
drwx------ 27 ubuntu ubuntu  4096 Mar 16 17:20 .
drwxr-x--- 22 ubuntu ubuntu  4096 Mar 16 17:28 ..
drwx------ 12 ubuntu ubuntu  4096 Mar  5 15:02 agents
drwxrwxr-x  3 ubuntu ubuntu  4096 Mar  4 14:48 blockrun
drwxrwxrwx  4 ubuntu ubuntu  4096 Mar  4 23:09 browser
drwxrwxr-x  2 ubuntu ubuntu  4096 Feb 26 23:26 canvas
drwxrwxr-x  2 ubuntu ubuntu  4096 Feb 26 23:27 completions
drwx------  2 ubuntu ubuntu  4096 Feb 26 23:29 credentials
drwx------  3 ubuntu ubuntu  4096 Mar 16 17:21 cron
drwx------  3 ubuntu ubuntu  4096 Mar 16 17:16 delivery-queue
drwxrwxr-x  2 ubuntu ubuntu  4096 Mar 16 16:50 devices
-rw-------  1 ubuntu ubuntu  5387 Mar 16 01:06 exec-approvals.json
drwxrwxr-x  4 ubuntu ubuntu  4096 Mar 15 22:46 extensions
drwxrwxr-x  2 ubuntu ubuntu  4096 Feb 26 23:49 identity
drwx------  2 ubuntu ubuntu  4096 Feb 28 17:59 logs
drwx------  4 ubuntu ubuntu  4096 Feb 27 05:09 media
drwxrwxr-x  2 ubuntu ubuntu  4096 Mar 16 17:25 memory
-rw-------  1 ubuntu ubuntu 56338 Mar 16 17:20 openclaw.json
-rw-------  1 ubuntu ubuntu 56478 Mar 16 17:20 openclaw.json.bak
-rw-------  1 ubuntu ubuntu 53582 Mar  5 00:54 openclaw.json.bak-20260305-0056
-rw-------  1 ubuntu ubuntu 53582 Mar  5 02:38 openclaw.json.bak-20260305-023851
-rw-------  1 ubuntu ubuntu 56346 Mar 16 03:20 openclaw.json.bak.1
-rw-------  1 ubuntu ubuntu 56347 Mar 16 03:15 openclaw.json.bak.2
-rw-------  1 ubuntu ubuntu 56346 Mar 16 03:15 openclaw.json.bak.3
-rw-------  1 ubuntu ubuntu 56345 Mar 16 03:08 openclaw.json.bak.4
drwxrwxr-x  2 ubuntu ubuntu  4096 Feb 28 16:38 skills
drwx------  2 ubuntu ubuntu  4096 Feb 27 05:14 subagents
drwx------  2 ubuntu ubuntu  4096 Mar 16 17:26 telegram
-rw-------  1 ubuntu ubuntu   187 Mar 15 13:36 update-check.json
drwxrwxr-x 37 ubuntu ubuntu  4096 Mar  8 23:07 workspace
drwxrwxr-x  3 ubuntu ubuntu  4096 Mar  4 03:26 workspace-claude
drwxrwxr-x  3 ubuntu ubuntu  4096 Mar  4 03:24 workspace-claude-opus
drwxrwxr-x  4 ubuntu ubuntu  4096 Mar  3 21:07 workspace-codex
drwxrwxr-x  3 ubuntu ubuntu  4096 Mar  3 21:07 workspace-deepseek-code
drwxrwxr-x  3 ubuntu ubuntu  4096 Mar  3 21:07 workspace-gemini
drwxrwxr-x  4 ubuntu ubuntu  4096 Mar  5 03:15 workspace-kimi
drwxrwxr-x  3 ubuntu ubuntu  4096 Mar  5 15:17 workspace-openrouter


grep -n "telegram\|kimi\|/models\|models.*command" /home/ubuntu/.openclaw/openclaw.json | head -50


17:      "telegram.http"
54:      "kimi",
642:            "id": "moonshot/kimi-k2.5",
831:            "id": "nvidia/kimi-k2.5",
1417:            "id": "kimi",
1418:            "name": "kimi → Kimi K2.5",
1453:            "id": "kimi-k2.5",
1454:            "name": "kimi-k2.5 → Kimi K2.5",
1686:            "id": "kimi-k2.5",
1775:        "blockrun/moonshot/kimi-k2.5": {},
1779:        "custom-api-moonshot-ai/kimi-k2.5": {
1780:          "alias": "kimi"
1819:        "kimi-cli": {
1820:          "command": "/home/ubuntu/.local/bin/kimi-openclaw",
1851:            "kimi",
1893:        "id": "kimi",
1894:        "name": "kimi",
1895:        "workspace": "/home/ubuntu/.openclaw/workspace-kimi",
1896:        "agentDir": "/home/ubuntu/.openclaw/agents/kimi/agent",
1897:        "model": "kimi-cli/default"
1937:        "kimi",
1944:        "telegram": [
1984:    "telegram": {
2062:      "telegram",


Perfect! Now I have all the information I need. Let me create a comprehensive summary document:

## Summary: Telegram /models Command Implementation in OpenClaw

Based on my thorough analysis of the OpenClaw platform source code, here's how the Telegram `/models` command is implemented and how model selection (including "kimi") maps to actual API providers:

### 1. **Where /models Command is Implemented**

**Main Files:**
- `/usr/lib/node_modules/openclaw/dist/discord-CcCLMjHw.js` (line 112032: `resolveModelsCommandReply`)
- `/usr/lib/node_modules/openclaw/dist/models-60KfC8dh.js` (general models CLI commands)

**Telegram Bot Handler:**
- `/usr/lib/node_modules/openclaw/dist/discord-CcCLMjHw.js` (line 153689)
- Implementation uses Grammy bot framework (`bot.command()`)

### 2. **How /models Command Lists Available Models**

The `/models` command workflow:

1. **Entry Point** (line 112032 in discord-CcCLMjHw.js):
   ```
   resolveModelsCommandReply(params)
   - Takes command body like "/models" or "/models provider"
   - Parses arguments with parseModelsArgs()
   - Returns provider list or paginated model list
   ```

2. **Provider Data Building** (line 112036):
   ```javascript
   buildModelsProviderData(cfg, agentId)
   - Returns: { byProvider: Map, providers: array }
   - Iterates model catalog
   ```

3. **Three Display Modes**:
   - **No provider specified** (line 112038-112055): Shows list of available providers with model counts
   - **Telegram UI** (line 112079-112100): Generates inline keyboard buttons for provider selection
   - **Text mode** (line 112102-112119): Shows paginated model list with pagination hints

### 3. **Model Selection & Routing (/model command)**

**Telegram Native Command Registration** (line 153687-153865):
```javascript
bot.command(normalizedCommandName, async (ctx) => {
  // Resolves command auth
  // Parses model arguments
  // Builds command menu if needed
  // Dispatches to agent with model override
})
```

**Model Directive Parsing** (line 116822-116845):
```javascript
extractModelDirective(body, options)
- Matches: /model <provider/model> or /modelAlias
- Extracts: rawModel, rawProfile (auth profile override)
- Returns: cleaned body + directive data
```

**Model Resolution** (line 116101-116252):
```javascript
resolveModelSelection(params)
- Takes: rawModel input, allowedModelCatalog, defaultProvider/model
- Supports: full refs (provider/model), aliases, fuzzy matching
- Returns: { provider, model, isDefault, alias }
```

### 4. **How "kimi" Model Selection Routes to API Providers**

**Kimi Model Configuration** (from openclaw.json):

1. **Alias Configuration** (line 1780 in openclaw.json):
   ```json
   "custom-api-moonshot-ai/kimi-k2.5": {
     "alias": "kimi"
   }
   ```

2. **Multiple Provider Mappings**:
   - `moonshot/kimi-k2.5` - Direct Moonshot API
   - `nvidia/kimi-k2.5` - NVIDIA endpoint
   - `kimi-cli/default` - Local CLI wrapper (`/home/ubuntu/.local/bin/kimi-openclaw`)
   - `blockrun/moonshot/kimi-k2.5` - BlockRun provider

3. **Agent-Level Default**:
   ```json
   {
     "id": "kimi",
     "model": "kimi-cli/default",  // Default model for kimi agent
     "workspace": "/home/ubuntu/.openclaw/workspace-kimi"
   }
   ```

### 5. **Session Model Override Flow**

When user selects a model via `/model kimi`:

1. **applyModelOverrideToSessionEntry** (line 80878-80958):
   ```javascript
   function applyModelOverrideToSessionEntry({entry, selection, profileOverride})
   - Sets: entry.providerOverride = selection.provider
   - Sets: entry.modelOverride = selection.model
   - Clears: runtime model/provider (forces use of override)
   ```

2. **Session Store Update**:
   - Stores override in session entry (persisted if storePath provided)
   - Affects all subsequent messages in that session
   - Can be reset with `/model :reset` or default

3. **Runtime Model Resolution**:
   - Overrides take precedence over config defaults
   - Auth profile can also be overridden with `/model kimi:profile-name`

### 6. **Telegram-Specific UI Elements**

**Model Keyboard Buttons** (line 111727+ - model-buttons.ts):
- `buildProviderKeyboard()`: Creates button grid for provider selection
- `buildModelsKeyboard()`: Creates paginated model buttons per provider
- `buildBrowseProvidersButton()`: Summary view with browse button

**Button Interaction** (line 153730-153751):
- Buttons send `callback_data` with command text
- Example: button "kimi-k2.5" sends `/model custom-api-moonshot-ai/kimi-k2.5`

### 7. **Key Architecture Points**

**Model Registry**:
- Loads from `~/.openclaw/agents/{agentId}/models.json`
- Can include custom provider configs
- Built-in catalog merged with custom entries

**Auth Profile Override**:
- Format: `/model <provider/model>:profile-id`
- Stored in session as `authProfileOverride`
- Allows switching auth credentials per session

**Native vs Plugin Commands**:
- Native: `/models`, `/model` - built-in, fast-tracked
- Plugin: custom commands from plugin catalog
- Telegram prefers native commands when available

### 8. **Command Authorization**

**Permission Check** (line 116125):
```javascript
unauthorized = rejectUnauthorizedCommand(params, "/models")
```
- Checks: allowFrom, ownerAllowFrom, group policies
- Returns error if sender unauthorized

### Files Summary

**Primary Implementation Files:**
1. `/usr/lib/node_modules/openclaw/dist/discord-CcCLMjHw.js` - Main implementation (168k lines)
2. `/usr/lib/node_modules/openclaw/dist/models-60KfC8dh.js` - Models CLI and helper functions
3. `/home/ubuntu/.openclaw/openclaw.json` - Configuration with model/provider mappings

**Type Definitions:**
- `/usr/lib/node_modules/openclaw/dist/plugin-sdk/telegram/bot-native-commands.d.ts`
- `/usr/lib/node_modules/openclaw/dist/plugin-sdk/config/telegram-custom-commands.d.ts`
- `/usr/lib/node_modules/openclaw/dist/plugin-sdk/agents/model-selection.d.ts`

This architecture allows flexible model selection across multiple providers while maintaining Telegram-specific UI optimizations and per-session configuration overrides.

agentId: a5ad70f505032907e (for resuming to continue this agent's work if needed)
<usage>total_tokens: 89853
tool_uses: 49
duration_ms: 78970</usage>

Prompt:
Find how the Telegram /models command is implemented in the OpenClaw platform. Look in:
- /usr/lib/node_modules/openclaw/ (main platform)
- /home/ubuntu/.openclaw/

I want to understand:
1. How /models command lists available models
2. How model selection maps to actual API providers
3. Specifically how "kimi" model selection routes to the kimi agent or kimi-cli

Search for files related to telegram bot, models command, channel handlers. Look for patterns like "models", "/models", "setModel", "selectModel", telegram bot handlers, channel config.

Be thorough - check source files in the openclaw package.


Now let me check the specific alias and what button gets sent when you select kimi from the UI:


python3 -c "
import json
with open('/home/ubuntu/.openclaw/openclaw.json') as f:
    cfg = json.load(f)

# Check model overrides/aliases
overrides = cfg.get('models', {}).get('overrides', {})
print('=== Model overrides/aliases ===')
for k, v in overrides.items():
    if 'kimi' in k.lower() or 'moonshot' in k.lower():
        print(f'  {k}: {json.dumps(v)}')

# Check what models are in each provider
providers = cfg.get('models', {}).get('providers', {})
print()
print('=== Providers with kimi models ===')
for pname, pdata in providers.items():
    models = pdata.get('models', [])
    for m in models:
        if 'kimi' in m.get('id','').lower() or 'kimi' in m.get('name','').lower():
            print(f'  {pname}/{m[\"id\"]} → {m.get(\"name\")}')

# Check telegram channel config
channels = cfg.get('channels', {})
tg = channels.get('telegram', {})
print()
print('=== Telegram channel config ===')
print(json.dumps(tg, indent=2)[:2000])
"

Parse openclaw.json for kimi model aliases and telegram config


=== Model overrides/aliases ===

=== Providers with kimi models ===
  blockrun/moonshot/kimi-k2.5 → Kimi K2.5
  blockrun/nvidia/kimi-k2.5 → NVIDIA Kimi K2.5
  blockrun/kimi → kimi → Kimi K2.5
  blockrun/moonshot → moonshot → Kimi K2.5
  blockrun/kimi-k2.5 → kimi-k2.5 → Kimi K2.5
  custom-api-moonshot-ai/kimi-k2.5 → Kimi K2.5 (Moonshot)

=== Telegram channel config ===
{
  "enabled": true,
  "dmPolicy": "allowlist",
  "botToken": "8658426327:AAHFrqQzIfxZhYJHhGmkKC9yXvF6B1aeRCc",
  "replyToMode": "all",
  "allowFrom": [
    842987156
  ],
  "groupPolicy": "allowlist",
  "streaming": "off",
  "mediaMaxMb": 20,
  "timeoutSeconds": 60,
  "retry": {
    "attempts": 6,
    "minDelayMs": 1500,
    "maxDelayMs": 90000,
    "jitter": 0.1
  },
  "network": {
    "autoSelectFamily": false,
    "dnsResultOrder": "ipv4first"
  }
}


python3 -c "
import json
with open('/home/ubuntu/.openclaw/openclaw.json') as f:
    cfg = json.load(f)

# Find kimi agent definition fully
agents = cfg.get('acp', {}).get('list', [])
for a in agents:
    if a.get('id') == 'kimi':
        print('=== Kimi agent ===')
        print(json.dumps(a, indent=2))

# Check what model the main pipo agent uses when model=kimi is selected
# Find the alias mapping
models_cfg = cfg.get('models', {})
overrides = models_cfg.get('overrides', {})
print()
print('=== All overrides with alias ===')
for k, v in overrides.items():
    if 'alias' in v:
        print(f'  {k}: alias={v[\"alias\"]}')
"

Get kimi agent definition and all model aliases


=== All overrides with alias ===


python3 -c "
import json
with open('/home/ubuntu/.openclaw/openclaw.json') as f:
    cfg = json.load(f)

# Find kimi agent
agents = cfg.get('acp', {}).get('list', [])
for a in agents:
    if a.get('id') == 'kimi':
        print(json.dumps(a, indent=2))

# Find alias in overrides (search deeper)
models_cfg = cfg.get('models', {})
print()
print('=== models keys ===', list(models_cfg.keys()))

# search for alias anywhere
def find_alias(obj, path=''):
    if isinstance(obj, dict):
        if 'alias' in obj:
            print(f'  {path}: alias={obj[\"alias\"]}')
        for k, v in obj.items():
            find_alias(v, f'{path}.{k}')
    elif isinstance(obj, list):
        for i, v in enumerate(obj):
            find_alias(v, f'{path}[{i}]')

find_alias(models_cfg, 'models')
"

Deep search for alias mappings in models config


=== models keys === ['mode', 'providers']


python3 -c "
import json
with open('/home/ubuntu/.openclaw/openclaw.json') as f:
    cfg = json.load(f)

# kimi agent
agents = cfg.get('acp', {}).get('list', [])
for a in agents:
    if a.get('id') == 'kimi':
        print('KIMI AGENT:')
        print(json.dumps(a, indent=2))
        print()

# check kimi-cli provider
providers = cfg.get('models', {}).get('providers', {})
kimi_cli = providers.get('kimi-cli', {})
print('kimi-cli provider:')
print(json.dumps(kimi_cli, indent=2))
"

Get kimi agent and kimi-cli provider config


kimi-cli provider:
{}


grep -n "kimi-cli\|kimi_cli\|kimi\.cli" /home/ubuntu/.openclaw/openclaw.json | head -20

Find kimi-cli references in config


1819:        "kimi-cli": {
1897:        "model": "kimi-cli/default"


  1810→        },
  1811→        "claude-sonnet-cli": {
  1812→          "command": "/home/ubuntu/.local/bin/claude-sonnet-openclaw",
  1813→          "output": "text"
  1814→        },
  1815→        "claude-opus-cli": {
  1816→          "command": "/home/ubuntu/.local/bin/claude-opus-openclaw",
  1817→          "output": "text"
  1818→        },
  1819→        "kimi-cli": {
  1820→          "command": "/home/ubuntu/.local/bin/kimi-openclaw",
  1821→          "output": "text"
  1822→        }
  1823→      },
  1824→      "compaction": {
  1825→        "mode": "safeguard"
  1826→      },
  1827→      "elevatedDefault": "off",
  1828→      "maxConcurrent": 1,
  1829→      "subagents": {
  1830→        "maxConcurrent": 4
  1831→      }
  1832→    },
  1833→    "list": [
  1834→      {
  1835→        "id": "pipo",
  1836→        "name": "pipo",
  1837→        "workspace": "/home/ubuntu/.openclaw/workspace",
  1838→        "model": {
  1839→          "primary": "openai-codex/gpt-5.4",
  1840→          "fallbacks": [
  1841→            "blockrun/auto",
  1842→            "openrouter/openrouter/free"
  1843→          ]
  1844→        },
  1845→        "subagents": {
  1846→          "allowAgents": [
  1847→            "codex",
  1848→            "gemini",
  1849→            "claude",
  1850→            "deepseek-code",
  1851→            "kimi",
  1852→            "clawrouter",
  1853→            "openrouter"
  1854→          ]
  1855→        }
  1856→      },
  1857→      {
  1858→        "id": "codex",
  1859→        "name": "codex",
  1860→        "workspace": "/home/ubuntu/.openclaw/workspace-codex",
  1861→        "agentDir": "/home/ubuntu/.openclaw/agents/codex/agent",
  1862→        "model": "openai-codex/gpt-5.3-codex"
  1863→      },
  1864→      {
  1865→        "id": "gemini",
  1866→        "name": "gemini",
  1867→        "workspace": "/home/ubuntu/.openclaw/workspace-gemini",
  1868→        "agentDir": "/home/ubuntu/.openclaw/agents/gemini/agent",
  1869→        "model": "gemini-cli/default"
  1870→      },
  1871→      {
  1872→        "id": "deepseek-code",
  1873→        "name": "deepseek-code",
  1874→        "workspace": "/home/ubuntu/.openclaw/workspace-deepseek-code",
  1875→        "agentDir": "/home/ubuntu/.openclaw/agents/deepseek-code/agent",
  1876→        "model": "deepseek-code/default"
  1877→      },
  1878→      {
  1879→        "id": "claude",
  1880→        "name": "claude",
  1881→        "workspace": "/home/ubuntu/.openclaw/workspace-claude",
  1882→        "agentDir": "/home/ubuntu/.openclaw/agents/claude/agent",
  1883→        "model": "claude-sonnet-cli/default"
  1884→      },
  1885→      {
  1886→        "id": "claude-opus",
  1887→        "name": "claude-opus",
  1888→        "workspace": "/home/ubuntu/.openclaw/workspace-claude-opus",
  1889→        "agentDir": "/home/ubuntu/.openclaw/agents/claude-opus/agent",
  1890→        "model": "claude-opus-cli/default"
  1891→      },
  1892→      {
  1893→        "id": "kimi",
  1894→        "name": "kimi",
  1895→        "workspace": "/home/ubuntu/.openclaw/workspace-kimi",
  1896→        "agentDir": "/home/ubuntu/.openclaw/agents/kimi/agent",
  1897→        "model": "kimi-cli/default"
  1898→      },
  1899→      {
  1900→        "id": "clawrouter",
  1901→        "name": "ClawRouter",
  1902→        "workspace": "/home/ubuntu/.openclaw/workspace",
  1903→        "model": {
  1904→          "primary": "blockrun/auto",
  1905→          "fallbacks": [
  1906→            "openrouter/openrouter/free"
  1907→          ]
  1908→        }
  1909→      },

<system-reminder>
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
</system-reminder>

        },
        "claude-sonnet-cli": {
          "command": "/home/ubuntu/.local/bin/claude-sonnet-openclaw",
          "output": "text"
        },
        "claude-opus-cli": {
          "command": "/home/ubuntu/.local/bin/claude-opus-openclaw",
          "output": "text"
        },
        "kimi-cli": {
          "command": "/home/ubuntu/.local/bin/kimi-openclaw",
          "output": "text"
        }
      },
      "compaction": {
        "mode": "safeguard"
      },
      "elevatedDefault": "off",
      "maxConcurrent": 1,
      "subagents": {
        "maxConcurrent": 4
      }
    },
    "list": [
      {
        "id": "pipo",
        "name": "pipo",
        "workspace": "/home/ubuntu/.openclaw/workspace",
        "model": {
          "primary": "openai-codex/gpt-5.4",
          "fallbacks": [
            "blockrun/auto",
            "openrouter/openrouter/free"
          ]
        },
        "subagents": {
          "allowAgents": [
            "codex",
            "gemini",
            "claude",
            "deepseek-code",
            "kimi",
            "clawrouter",
            "openrouter"
          ]
        }
      },
      {
        "id": "codex",
        "name": "codex",
        "workspace": "/home/ubuntu/.openclaw/workspace-codex",
        "agentDir": "/home/ubuntu/.openclaw/agents/codex/agent",
        "model": "openai-codex/gpt-5.3-codex"
      },
      {
        "id": "gemini",
        "name": "gemini",
        "workspace": "/home/ubuntu/.openclaw/workspace-gemini",
        "agentDir": "/home/ubuntu/.openclaw/agents/gemini/agent",
        "model": "gemini-cli/default"
      },
      {
        "id": "deepseek-code",
        "name": "deepseek-code",
        "workspace": "/home/ubuntu/.openclaw/workspace-deepseek-code",
        "agentDir": "/home/ubuntu/.openclaw/agents/deepseek-code/agent",
        "model": "deepseek-code/default"
      },
      {
        "id": "claude",
        "name": "claude",
        "workspace": "/home/ubuntu/.openclaw/workspace-claude",
        "agentDir": "/home/ubuntu/.openclaw/agents/claude/agent",
        "model": "claude-sonnet-cli/default"
      },
      {
        "id": "claude-opus",
        "name": "claude-opus",
        "workspace": "/home/ubuntu/.openclaw/workspace-claude-opus",
        "agentDir": "/home/ubuntu/.openclaw/agents/claude-opus/agent",
        "model": "claude-opus-cli/default"
      },
      {
        "id": "kimi",
        "name": "kimi",
        "workspace": "/home/ubuntu/.openclaw/workspace-kimi",
        "agentDir": "/home/ubuntu/.openclaw/agents/kimi/agent",
        "model": "kimi-cli/default"
      },
      {
        "id": "clawrouter",
        "name": "ClawRouter",
        "workspace": "/home/ubuntu/.openclaw/workspace",
        "model": {
          "primary": "blockrun/auto",
          "fallbacks": [
            "openrouter/openrouter/free"
          ]
        }
      },


Now let me check if the blockrun proxy (port 8402) is running, since the kimi models in `/models` go through it:


curl -s http://127.0.0.1:8402/health 2>&1 | head -5; echo "---"; systemctl status blockrun 2>/dev/null | head -10 || echo "no systemd unit"

Check if blockrun proxy is running


curl -s http://127.0.0.1:8402/v1/models 2>&1 | python3 -c "import json,sys; d=json.load(sys.stdin); [print(m['id']) for m in d.get('data',[])]" 2>&1 | grep -i kimi | head -20

List blockrun available models filtered for kimi


moonshot/kimi-k2.5
nvidia/kimi-k2.5
kimi
kimi-k2.5


Good. Here's the full picture:

---

## How `/models` → Kimi actually works

**Two completely separate paths exist for "kimi":**

### Path 1: `/models` UI (model override on pipo agent)
When you browse `/models` and select a kimi option, you're setting a **model override on the pipo agent**. The options that show up are:

| What you see | What it calls |
|---|---|
| `kimi → Kimi K2.5` | `blockrun/kimi` → port 8402 proxy |
| `moonshot → Kimi K2.5` | `blockrun/moonshot` → port 8402 proxy |
| `Kimi K2.5` (moonshot provider) | `blockrun/moonshot/kimi-k2.5` → port 8402 proxy |
| `Kimi K2.5 (Moonshot)` | `custom-api-moonshot-ai/kimi-k2.5` → **direct Moonshot API** |

The blockrun ones go through a local proxy at port 8402 (which itself routes to Moonshot/NVIDIA). The `custom-api-moonshot-ai/kimi-k2.5` one goes **directly** to `api.moonshot.ai`.

### Path 2: Kimi as a separate agent
The `kimi` **agent** (`"id": "kimi"`) has its own workspace and uses `kimi-cli/default` → runs `/home/ubuntu/.local/bin/kimi-openclaw` → direct Moonshot API. This agent is only invoked when explicitly dispatched (e.g. via `/agent kimi`).

---

**So which one were you using yesterday?** The `/models` selection — if you picked anything labeled "kimi" — was going through **blockrun** (not directly to Moonshot). That's why you saw zero usage in the Moonshot dashboard: blockrun proxies the request and bills differently.

To use the **direct Moonshot API** (and see usage in your Moonshot dashboard), you'd need to select specifically `custom-api-moonshot-ai/kimi-k2.5`. Does that option appear in your `/models` UI? You can check by typing `/models custom-api-moonshot-ai` in Telegram.


can you check how the telegram /models is set up so that we ensure 100% that when I select it, its actually working as expected?


<local-command-caveat>Caveat: The messages below were generated by the user while running local commands. DO NOT respond to these messages or otherwise consider them in your response unless the user explicitly asks you to.</local-command-caveat>


<command-name>/exit</command-name>
            <command-message>exit</command-message>
            <command-args></command-args>


<local-command-stdout>Bye!</local-command-stdout>