Download apps/vscode/src/handlers/config.handler.ts from SaylorTwift/kimi-code: direct link, hf CLI and curl.
- Browser
- Download file 8.32 kB
-
https://huggingface.co/SaylorTwift/kimi-code/resolve/main/apps/vscode/src/handlers/config.handler.ts
- Command line
-
hf download hf://SaylorTwift/kimi-code/apps/vscode/src/handlers/config.handler.ts
-
curl -L -o config.handler.ts https://huggingface.co/SaylorTwift/kimi-code/resolve/main/apps/vscode/src/handlers/config.handler.ts
8.32 kB
| import * as vscode from "vscode"; | |
| import { | |
| effectiveModelAlias, | |
| type KimiConfig as SdkKimiConfig, | |
| type ModelAlias, | |
| type ProviderType, | |
| type ThinkingEffort, | |
| } from "@moonshot-ai/kimi-code-sdk"; | |
| import { Methods } from "../../shared/bridge"; | |
| import type { | |
| KimiConfig as WebviewKimiConfig, | |
| ModelConfig, | |
| SlashCommandInfo, | |
| } from "../../shared/legacy-sdk"; | |
| import type { ExtensionConfig, SessionConfig } from "../../shared/types"; | |
| import { VSCodeSettings } from "../config/vscode-settings"; | |
| import type { Handler } from "./types"; | |
| const SLASH_COMMANDS: SlashCommandInfo[] = [ | |
| { name: "init", aliases: [], description: "Analyze the codebase and generate AGENTS.md" }, | |
| { name: "compact", aliases: [], description: "Compact the conversation context" }, | |
| { name: "clear", aliases: ["reset"], description: "Clear the context" }, | |
| { name: "yolo", aliases: [], description: "Toggle YOLO mode (auto-approve tool actions; may still ask questions)" }, | |
| { | |
| name: "auto", | |
| aliases: ["afk"], | |
| description: "Toggle Auto mode (fully autonomous; the agent will not ask questions)", | |
| }, | |
| { name: "plan", aliases: [], description: "Toggle plan mode. Usage: /plan [on|off|view|clear]" }, | |
| { | |
| name: "add-dir", | |
| aliases: [], | |
| description: "Add a directory to the workspace. Usage: /add-dir <path>", | |
| }, | |
| { name: "export", aliases: [], description: "Export current session context to a markdown file" }, | |
| { name: "import", aliases: [], description: "Import context from a file or session ID" }, | |
| ]; | |
| const saveConfig: Handler<SessionConfig, { ok: boolean }> = async (params, ctx) => { | |
| const effort = sessionConfigEffort(params); | |
| const effortChanged = params.effortChanged !== false; | |
| const config = await ctx.harness.getConfig({ reload: true }); | |
| const model = config.models?.[params.model]; | |
| // Resolve with the provider type the way the TUI's effectiveModelForHost | |
| // does: without it the Anthropic fallback profile (e.g. `claude-latest`) | |
| // never matches, so the inferred default that gates persistence is missed. | |
| const providerType = | |
| model === undefined ? undefined : (config.providers?.[model.provider]?.type ?? model.protocol); | |
| const full = thinkingConfig( | |
| effort, | |
| model === undefined ? undefined : effectiveModelAlias(model, providerType), | |
| ); | |
| // Re-confirming the effort already shown is not an explicit choice — | |
| // persist the model but leave the stored effort preference alone (the TUI's | |
| // persistModelSelection rule). | |
| const patch = effortChanged ? full : { enabled: full.enabled }; | |
| if ( | |
| config.defaultModel !== params.model | |
| || config.thinking?.enabled !== patch.enabled | |
| || (effortChanged && config.thinking?.effort !== patch.effort) | |
| ) { | |
| await ctx.harness.setConfig({ | |
| defaultModel: params.model, | |
| thinking: patch, | |
| }); | |
| } | |
| const runtime = ctx.getSession(); | |
| if (runtime !== undefined) { | |
| const status = await runtime.session.getStatus(); | |
| if (status.model !== params.model) await runtime.session.setModel(params.model); | |
| if (status.thinkingEffort !== effort) await runtime.session.setThinking(effort); | |
| } | |
| return { ok: true }; | |
| }; | |
| const getExtensionConfig: Handler<void, ExtensionConfig> = async () => { | |
| return VSCodeSettings.getExtensionConfig(); | |
| }; | |
| const openSettings: Handler<void, { ok: boolean }> = async () => { | |
| await vscode.commands.executeCommand("workbench.action.openSettings", "kimi"); | |
| return { ok: true }; | |
| }; | |
| const getModels: Handler<void, WebviewKimiConfig> = async (_, ctx) => { | |
| const config = await ctx.harness.getConfig({ reload: true }); | |
| return toWebviewConfig(config); | |
| }; | |
| const getSlashCommands: Handler<void, SlashCommandInfo[]> = async (_, ctx) => { | |
| if (!ctx.workDir) return SLASH_COMMANDS; | |
| try { | |
| const skills = await ctx.harness.listWorkspaceSkills(ctx.workDir); | |
| const skillCommands = skills | |
| .filter((skill) => isUserActivatableSkill(skill.type)) | |
| .toSorted((left, right) => left.name.localeCompare(right.name)) | |
| .map((skill) => ({ | |
| name: `skill:${skill.name}`, | |
| aliases: [], | |
| description: skill.description ?? "", | |
| })); | |
| return [...SLASH_COMMANDS, ...skillCommands]; | |
| } catch (error) { | |
| ctx.logError("Unable to list workspace skills", error); | |
| return SLASH_COMMANDS; | |
| } | |
| }; | |
| const showLogs: Handler<void, { ok: boolean }> = async (_, ctx) => { | |
| ctx.showLogs(); | |
| return { ok: true }; | |
| }; | |
| const reloadWebview: Handler<void, { ok: boolean }> = async (_, ctx) => { | |
| await ctx.closeSession(); | |
| ctx.fileManager.clearTracked(ctx.webviewId); | |
| ctx.reloadWebview(); | |
| return { ok: true }; | |
| }; | |
| export const configHandlers = { | |
| [Methods.SaveConfig]: saveConfig, | |
| [Methods.GetExtensionConfig]: getExtensionConfig, | |
| [Methods.OpenSettings]: openSettings, | |
| [Methods.GetModels]: getModels, | |
| [Methods.GetSlashCommands]: getSlashCommands, | |
| [Methods.ShowLogs]: showLogs, | |
| [Methods.ReloadWebview]: reloadWebview, | |
| } as Record<string, Handler<any, any>>; | |
| export function toWebviewConfig(config: SdkKimiConfig): WebviewKimiConfig { | |
| const models: ModelConfig[] = Object.entries(config.models ?? {}) | |
| // Resolve with the provider type the way saveConfig does: without it the | |
| // Anthropic fallback profile never matches, and the webview's effort | |
| // persistence seed would gate on a different effective model. | |
| .map(([id, model]) => | |
| toWebviewModel(id, model, config.providers?.[model.provider]?.type ?? model.protocol), | |
| ) | |
| .toSorted((left, right) => left.name.localeCompare(right.name)); | |
| return { | |
| defaultModel: config.defaultModel ?? models[0]?.id ?? null, | |
| defaultThinking: config.thinking?.enabled !== false, | |
| defaultThinkingEffort: config.thinking?.effort, | |
| models, | |
| }; | |
| } | |
| function toWebviewModel(id: string, model: ModelAlias, providerType?: ProviderType): ModelConfig { | |
| const effective = effectiveModelAlias(model, providerType); | |
| return { | |
| id, | |
| name: effective.displayName ?? effective.model ?? id, | |
| provider: effective.provider, | |
| capabilities: [...(effective.capabilities ?? [])], | |
| adaptive_thinking: effective.adaptiveThinking, | |
| support_efforts: | |
| effective.supportEfforts === undefined ? undefined : [...effective.supportEfforts], | |
| default_effort: effective.defaultEffort, | |
| }; | |
| } | |
| function sessionConfigEffort(config: SessionConfig): ThinkingEffort { | |
| if (config.effort !== undefined) return config.effort as ThinkingEffort; | |
| return config.thinking === true ? "on" : "off"; | |
| } | |
| /** | |
| * Project a thinking effort to the `[thinking]` config patch persisted to | |
| * config.toml — mirrors the TUI's thinkingEffortToConfig. "off" disables | |
| * thinking; "on" is the boolean-model on-signal, so it only persists | |
| * `enabled`. A concrete effort persists as the global default, EXCEPT when it | |
| * ranks above the model's effective default effort: `support_efforts` is | |
| * ordered by strength, and a pick more expensive than the default stays | |
| * session-only and records just `enabled`, so it never becomes the global | |
| * default for every new session. The default here is the effective model's, | |
| * however it arose — declared via the catalog or overrides, or synthesized | |
| * by the protocol-profile inference (`withAnthropicProfile` resolves Claude | |
| * models to "high", so an "xhigh" pick stays session-only there). When the | |
| * effective model carries no default effort at all, its highest declared | |
| * level stays session-only (the historical rule). When the model's levels | |
| * are unknown the concrete effort is persisted as-is. | |
| */ | |
| function thinkingConfig( | |
| effort: ThinkingEffort, | |
| model?: Pick<ModelAlias, "supportEfforts" | "defaultEffort">, | |
| ): { enabled: boolean; effort?: string } { | |
| if (effort === "off") return { enabled: false }; | |
| if (effort === "on") return { enabled: true }; | |
| const efforts = model?.supportEfforts; | |
| if (efforts !== undefined && efforts.includes(effort)) { | |
| const declared = model?.defaultEffort; | |
| const ceiling = | |
| declared !== undefined && efforts.includes(declared) | |
| ? efforts.indexOf(declared) | |
| : efforts.length - 2; | |
| if (efforts.indexOf(effort) > ceiling) return { enabled: true }; | |
| } | |
| return { enabled: true, effort }; | |
| } | |
| function isUserActivatableSkill(type: string | undefined): boolean { | |
| return type === undefined || type === "prompt" || type === "inline" || type === "flow"; | |
| } | |