import { useEffect, useState } from "react"; import { invoke } from "@tauri-apps/api/core"; import { useTranslation } from "../i18n"; import type { TranslationKey } from "../i18n/zh"; import { getAgentSettings, setAgentSettings } from "../lib/agent-settings"; import type { AgentSettings, ExperimentalEnvStatus, Hook, PermissionRule, } from "../types"; import { Card, Checkbox, NumberField, Segmented } from "./ui/controls"; interface AgentSettingsPanelProps { rawOther: unknown; onChange: (nextRawOther: unknown) => void; } // Upstream removed the "max" effort tier (auto-migrates to "high"). const THINKING_LEVELS = ["low", "medium", "high"] as const; const THINKING_LABELS: Record<(typeof THINKING_LEVELS)[number], TranslationKey> = { low: "thinkingLow", medium: "thinkingMedium", high: "thinkingHigh", }; const PERMISSION_DECISIONS = ["allow", "deny", "ask"] as const; const PERMISSION_LABELS: Record<(typeof PERMISSION_DECISIONS)[number], TranslationKey> = { allow: "permissionAllow", deny: "permissionDeny", ask: "permissionAsk", }; // kimi-code 0.33+ added TurnStarted / UserPromptQueued / TaskStarted / // SessionHeartbeat hook events (plus SessionStart / SessionEnd). const COMMON_EVENTS = [ "PreToolUse", "PostToolUse", "TurnStarted", "UserPromptQueued", "TaskStarted", "SessionHeartbeat", "SessionStart", "SessionEnd", ] as const; export function AgentSettingsPanel({ rawOther, onChange }: AgentSettingsPanelProps) { const { t } = useTranslation(); const settings = getAgentSettings(rawOther); /** * KIMI_CODE_LEGACY_FLAG=1 keeps the v1 loop_control keys dual-written for * v1-engine users; the default (v2) writes only max_attempts_per_step. */ const [legacyV1, setLegacyV1] = useState(false); useEffect(() => { invoke("get_experimental_env_status") .then((env) => { const value = env?.KIMI_CODE_LEGACY_FLAG ?? ""; setLegacyV1(["1", "true", "yes", "on"].includes(value.trim().toLowerCase())); }) .catch(() => setLegacyV1(false)); }, []); const update = (patch: Partial) => { onChange(setAgentSettings(rawOther, patch, { legacyV1 })); }; const updateThinking = (patch: Partial) => { update({ thinking: { ...settings.thinking, ...patch } }); }; const updateLoopControl = (patch: Partial) => { update({ loop_control: { ...settings.loop_control, ...patch } }); }; const updateBackground = (patch: Partial) => { update({ background: { ...settings.background, ...patch } }); }; const updateWatch = (patch: Partial) => { update({ watch: { ...settings.watch, ...patch } }); }; const setRules = (rules: PermissionRule[]) => { update({ permission: { rules } }); }; const setHooks = (hooks: Hook[]) => { update({ hooks }); }; const thinkingEnabled = settings.thinking?.enabled ?? true; return (

{t("agentSettings")}

updateThinking({ enabled: checked })} />
{t("thinkingLevel")} ({ key: lvl, label: t(THINKING_LABELS[lvl]), }))} value={settings.thinking?.effort ?? "medium"} onChange={(effort) => updateThinking({ effort: effort as NonNullable["effort"] }) } disabled={!thinkingEnabled} />
updateThinking({ keep: checked ? "all" : "off" })} />

{t("thinkingContextHint")}

updateLoopControl({ max_attempts_per_step: v, max_retries_per_step: v, }) } /> updateLoopControl({ reserved_context_size: v })} /> {/* kimi-code 0.43.0+ `loop_control.compaction_max_attempts`. The upstream default is 5; an empty field or 0 removes the key instead of writing a literal 0 (see setAgentSettings). */} updateLoopControl({ compaction_max_attempts: v })} />

{t("compactionMaxAttemptsHint")}

updateBackground({ max_running_tasks: v })} /> updateBackground({ keep_alive_on_exit: checked })} /> {/* Top-level `[watch] enabled` (kimi-code 2.0.1+; off by default since 2.0.2). KIMI_CODE_WATCH outranks this config at runtime — the env var is probed by get_experimental_env_status as a non-flag entry but deliberately locks nothing here, the toggle just documents the config value that applies when the env var is unset. */} updateWatch({ enabled: checked })} />

{t("watchEnabledDesc")}

{/* kimi-code `[permission] dangerous_command_guard`. The env var KIMI_CODE_DANGEROUS_COMMAND_GUARD (literal "true"/"false") outranks this config at runtime; no env-lock UI here (deliberate minimal scope). Absent key = upstream default on. Since kimi-code 0.41.0 the guard only gates Manual/YOLO modes — Auto (never-ask) mode no longer intercepts dangerous commands. */} update({ permission: { ...settings.permission, dangerous_command_guard: checked, }, }) } />

{t("permissionDangerousGuardDesc")}

{(settings.permission?.rules ?? []).map((rule, idx) => (
{ const rules = [...(settings.permission?.rules ?? [])]; rules[idx] = { ...rule, pattern: e.target.value }; setRules(rules); }} />
))}
{(settings.hooks ?? []).map((hook, idx) => (
{ const hooks = [...(settings.hooks ?? [])]; hooks[idx] = { ...hook, event: e.target.value }; setHooks(hooks); }} /> {COMMON_EVENTS.map((e) => ( { const hooks = [...(settings.hooks ?? [])]; hooks[idx] = { ...hook, matcher: e.target.value }; setHooks(hooks); }} /> { const hooks = [...(settings.hooks ?? [])]; hooks[idx] = { ...hook, command: e.target.value }; setHooks(hooks); }} /> { const hooks = [...(settings.hooks ?? [])]; hooks[idx] = { ...hook, timeout: e.target.value === "" ? undefined : Number(e.target.value), }; setHooks(hooks); }} />
))}
); }