Compare commits

..
6 Commits
Author SHA1 Message Date
KimiSwitch Dev b9002281a3 chore(data): 同步 models-dev 2026-09-10(7615 模型 / 213 供应商)+ v0.7.16 release notes
Release / Version consistency (push) Canceled after 0s
Release / Build (macos-latest) (push) Canceled after 0s
Release / Build (ubuntu-latest) (push) Canceled after 0s
Release / Build (windows-latest) (push) Canceled after 0s
Release / Attach macOS install script (push) Canceled after 0s
2026-09-10 23:37:15 +08:00
KimiSwitch Dev 0ea9e5b3de chore(release): bump to v0.7.16 — 适配 kimi-code 0.42.0 实验 flag 9→6 + 模型用量统计滚动 2026-09-10 13:19:54 +08:00
KimiSwitch Dev 36e64a1f72 fix(dashboard): 模型用量统计图表固定最大高度 360px 并滚动,与右侧模型用量表一致 2026-09-10 11:09:06 +08:00
KimiSwitch Dev 9ce1ed65b0 feat(settings): 适配 kimi-code 0.42.0 —— 实验 flag 镜像 9→6
- 删除已转正/移除的 4 个 flag:secondary-model(模型池恒开)、
  persistence_minidb_readmodel(改由 [database] 配置节)、remote-control、
  search_worker;env 探测清单同步 11→8
- 新增 notify_user flag(KIMI_CODE_EXPERIMENTAL_NOTIFY_USER,实验性
  Updates 面板,默认关闭)
- 设置页移除 secondary-model 专属卡片与「默认开启」徽章,6 个 flag 统一渲染;
  [secondary_model] 模型池配置逻辑不变
- 残留旧键上游 schema 宽容忽略,无需迁移
2026-09-09 19:48:41 +08:00
KimiSwitch Dev 33a27ba31b fix(release): publish-gitea.sh 修复含空格文件名上传失败(暂存去空格副本 + 点号命名) 2026-09-09 14:32:34 +08:00
KimiSwitch Dev 7bc2dee8b2 chore(release): 新增国内 Gitea 发布脚本 publish-gitea.sh(先国内后 GitHub 流程) 2026-09-09 14:29:29 +08:00
16 changed files with 2592 additions and 1348 deletions

No files matched your search

+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"name": "kimiswitch", "name": "kimiswitch",
"private": true, "private": true,
"version": "0.7.15", "version": "0.7.16",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
+771 -379
View File
File diff suppressed because it is too large. Load diff
+7
View File
@@ -0,0 +1,7 @@
# KimiSwitch v0.7.16
## 适配
- **适配 kimi-code 0.42.0 实验 flag 镜像(9→6)**:移除已转正/删除的 4 个开关(子代理次主力模型——模型池恒开、MiniDB 读模型——改由 `[database]` 配置节、远程控制、搜索 Worker),新增 `notify_user`(实验性 Updates 面板,默认关闭);环境变量探测清单同步;老配置残留旧键上游静默忽略,无需迁移
- 用量统计「模型用量统计」图表固定最大高度 360px 并支持滚动,与右侧模型用量表一致
- models-dev 快照同步至 2026-09-10(7615 模型 / 213 供应商),`public/` 前端静态目录同步
+65
View File
@@ -0,0 +1,65 @@
#!/usr/bin/env bash
# Publish a release (notes + assets) to the domestic Gitea mirror
# (git.codingplan.site/admin/KimiCodeSwitch).
#
# Usage: publish-gitea.sh <tag> <asset> [asset...]
# tag : e.g. v0.7.15; notes read from release-notes-<tag>.md
# assets : files to attach (MSI, install-macos.sh, ...)
#
# Auth: reuses the stored git credential for git.codingplan.site
# (`git credential fill`); the secret never appears in argv or output.
set -euo pipefail
GITEA="https://git.codingplan.site/api/v1"
REPO="admin/KimiCodeSwitch"
TAG="${1:?usage: publish-gitea.sh <tag> <asset> [asset...]}"
shift
NOTES_FILE="release-notes-${TAG}.md"
json_escape() {
python - "$1" <<'PYEOF'
import json, sys
print(json.dumps(open(sys.argv[1], encoding="utf-8").read()))
PYEOF
}
if [[ -f "$NOTES_FILE" ]]; then
BODY=$(json_escape "$NOTES_FILE")
else
BODY="\"${TAG}\""
fi
# Resolve stored git credential for this host (no secret echoed).
CRED=$(printf 'protocol=https\nhost=git.codingplan.site\n\n' | git credential fill)
USER=$(grep '^username=' <<<"$CRED" | cut -d= -f2-)
PASS=$(grep '^password=' <<<"$CRED" | cut -d= -f2-)
[[ -n "$USER" && -n "$PASS" ]] || { echo "no stored credential for git.codingplan.site" >&2; exit 1; }
AUTH=(-u "$USER:$PASS")
# Create (or reuse) the release for this tag.
EXISTING=$(curl -sf --max-time 20 "${AUTH[@]}" "$GITEA/repos/$REPO/releases/tags/$TAG" || true)
if [[ -n "$EXISTING" && "$EXISTING" != *"message"* ]]; then
RELEASE_ID=$(grep -o '"id":[0-9]*' <<<"$EXISTING" | head -1 | cut -d: -f2)
echo "release $TAG exists (id=$RELEASE_ID), attaching assets"
else
CREATED=$(curl -sf --max-time 20 "${AUTH[@]}" \
-H 'Content-Type: application/json' -X POST \
-d "{\"tag_name\":\"$TAG\",\"name\":\"$TAG\",\"body\":$BODY,\"draft\":false,\"prerelease\":false}" \
"$GITEA/repos/$REPO/releases")
RELEASE_ID=$(grep -o '"id":[0-9]*' <<<"$CREATED" | head -1 | cut -d: -f2)
echo "created release $TAG (id=$RELEASE_ID)"
fi
for ASSET in "$@"; do
[[ -f "$ASSET" ]] || { echo "skip missing asset: $ASSET" >&2; continue; }
NAME=$(basename "$ASSET" | tr ' ' '.')
STAGED=$(mktemp "${TMPDIR:-/tmp}/upload.XXXXXX.${NAME##*.}")
cp "$ASSET" "$STAGED"
CODE=$(curl -s -o /dev/null -w '%{http_code}' --max-time 600 "${AUTH[@]}" \
-F "attachment=@$STAGED" \
"$GITEA/repos/$REPO/releases/$RELEASE_ID/assets?name=$NAME")
rm -f "$STAGED"
echo "upload $NAME -> HTTP $CODE"
[[ "$CODE" == 2* ]] || { echo "upload failed: $NAME" >&2; exit 1; }
done
echo "done: https://git.codingplan.site/$REPO/releases/tag/$TAG"
+1 -1
View File
@@ -1978,7 +1978,7 @@ dependencies = [
[[package]] [[package]]
name = "kimiswitch" name = "kimiswitch"
version = "0.7.14" version = "0.7.16"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"chrono", "chrono",
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "kimiswitch" name = "kimiswitch"
version = "0.7.15" version = "0.7.16"
description = "Kimi Switch - model config manager" description = "Kimi Switch - model config manager"
authors = ["codingplan.site"] authors = ["codingplan.site"]
edition = "2021" edition = "2021"
+8 -9
View File
@@ -1335,7 +1335,7 @@ pub async fn kimi_oauth_poll(
} }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Experimental feature env-var probe (Kimi Code secondary model etc.) // Experimental feature env-var probe (Kimi Code experimental flags)
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/// Read the Kimi Code environment variables that are currently set (non-empty) /// Read the Kimi Code environment variables that are currently set (non-empty)
@@ -1348,6 +1348,11 @@ pub async fn kimi_oauth_poll(
/// are probed too — the latter lets AgentSettingsPanel decide whether to /// are probed too — the latter lets AgentSettingsPanel decide whether to
/// dual-write the v1 engine's loop_control keys. /// dual-write the v1 engine's loop_control keys.
/// ///
/// As of Kimi Code 0.42.0 the upstream registry holds 6 experimental flags;
/// `secondary-model`, `persistence_minidb_readmodel`, `remote-control` and
/// `search_worker` have been promoted or removed upstream and are no longer
/// probed here.
///
/// The flag id → env var mapping mirrors `EXPERIMENTAL_FLAGS` in /// The flag id → env var mapping mirrors `EXPERIMENTAL_FLAGS` in
/// src/lib/subagent-settings.ts; keep both lists in sync. /// src/lib/subagent-settings.ts; keep both lists in sync.
/// ///
@@ -1355,19 +1360,13 @@ pub async fn kimi_oauth_poll(
/// the raw values are returned and the truthy check happens on the frontend. /// the raw values are returned and the truthy check happens on the frontend.
#[tauri::command] #[tauri::command]
pub fn get_experimental_env_status() -> HashMap<String, String> { pub fn get_experimental_env_status() -> HashMap<String, String> {
const VARS: [(&str, &str); 11] = [ const VARS: [(&str, &str); 8] = [
("secondary-model", "KIMI_CODE_EXPERIMENTAL_SECONDARY_MODEL"),
("tool-select", "KIMI_CODE_EXPERIMENTAL_TOOL_SELECT"), ("tool-select", "KIMI_CODE_EXPERIMENTAL_TOOL_SELECT"),
(
"persistence_minidb_readmodel",
"KIMI_CODE_EXPERIMENTAL_PERSISTENCE_MINIDB_READMODEL",
),
("tower", "KIMI_CODE_EXPERIMENTAL_TOWER"), ("tower", "KIMI_CODE_EXPERIMENTAL_TOWER"),
("subagent_fork", "KIMI_CODE_EXPERIMENTAL_SUBAGENT_FORK"), ("subagent_fork", "KIMI_CODE_EXPERIMENTAL_SUBAGENT_FORK"),
("wait_for", "KIMI_CODE_EXPERIMENTAL_WAIT_FOR"), ("wait_for", "KIMI_CODE_EXPERIMENTAL_WAIT_FOR"),
("auto_session_title", "KIMI_CODE_EXPERIMENTAL_AUTO_SESSION_TITLE"), ("auto_session_title", "KIMI_CODE_EXPERIMENTAL_AUTO_SESSION_TITLE"),
("remote-control", "KIMI_CODE_EXPERIMENTAL_REMOTE_CONTROL"), ("notify_user", "KIMI_CODE_EXPERIMENTAL_NOTIFY_USER"),
("search_worker", "KIMI_CODE_EXPERIMENTAL_SEARCH_WORKER"),
// Non-flag probes consumed by the frontend: // Non-flag probes consumed by the frontend:
("master", "KIMI_CODE_EXPERIMENTAL_FLAG"), ("master", "KIMI_CODE_EXPERIMENTAL_FLAG"),
("legacy", "KIMI_CODE_LEGACY_FLAG"), ("legacy", "KIMI_CODE_LEGACY_FLAG"),
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"productName": "Kimi Switch", "productName": "Kimi Switch",
"version": "0.7.15", "version": "0.7.16",
"identifier": "com.kimiswitch.app", "identifier": "com.kimiswitch.app",
"build": { "build": {
"beforeDevCommand": "npm run dev", "beforeDevCommand": "npm run dev",
+5 -61
View File
@@ -43,21 +43,15 @@ const EFFORT_LABELS: Record<(typeof EFFORTS)[number], TranslationKey> = {
}; };
const FLAG_LABELS: Record<string, { name: TranslationKey; desc: TranslationKey }> = { const FLAG_LABELS: Record<string, { name: TranslationKey; desc: TranslationKey }> = {
"secondary-model": { name: "flagSecondaryModel", desc: "flagSecondaryModelDesc" }, wait_for: { name: "flagWaitFor", desc: "flagWaitForDesc" },
"tool-select": { name: "flagToolSelect", desc: "flagToolSelectDesc" }, "tool-select": { name: "flagToolSelect", desc: "flagToolSelectDesc" },
persistence_minidb_readmodel: { notify_user: { name: "flagNotifyUser", desc: "flagNotifyUserDesc" },
name: "flagMinidbReadmodel",
desc: "flagMinidbReadmodelDesc",
},
tower: { name: "flagTower", desc: "flagTowerDesc" }, tower: { name: "flagTower", desc: "flagTowerDesc" },
subagent_fork: { name: "flagSubagentFork", desc: "flagSubagentForkDesc" }, subagent_fork: { name: "flagSubagentFork", desc: "flagSubagentForkDesc" },
wait_for: { name: "flagWaitFor", desc: "flagWaitForDesc" },
auto_session_title: { auto_session_title: {
name: "flagAutoSessionTitle", name: "flagAutoSessionTitle",
desc: "flagAutoSessionTitleDesc", desc: "flagAutoSessionTitleDesc",
}, },
"remote-control": { name: "flagRemoteControl", desc: "flagRemoteControlDesc" },
search_worker: { name: "flagSearchWorker", desc: "flagSearchWorkerDesc" },
}; };
/** Validation-error i18n key per engine rule, for the pre-write self-check. */ /** Validation-error i18n key per engine rule, for the pre-write self-check. */
@@ -185,21 +179,6 @@ export function SubagentSettingsPage({
// off — upgrading with force would create an invalid force+pool-table combo. // off — upgrading with force would create an invalid force+pool-table combo.
const canUpgrade = !!pool && !isPoolView && pool.model !== undefined && !pool.force; const canUpgrade = !!pool && !isPoolView && pool.model !== undefined && !pool.force;
const masterOn = isMasterEnvOn(env); const masterOn = isMasterEnvOn(env);
const secondaryFlag = EXPERIMENTAL_FLAGS[0]; // "secondary-model"
const otherFlags = EXPERIMENTAL_FLAGS.slice(1);
/** Effective state of the secondary-model flag. Mirrors the kimi-code
* 0.40.1 priority: single-flag env > explicit [experimental] value >
* master env (force-on only) > upstream default. The master env no
* longer locks the toggle — only the flag's own env var does. */
const secondaryEnabled = isFlagLockedByEnv(env, secondaryFlag)
? forcedEnvValue(env, secondaryFlag)
: isExperimentalFlagSet(rawOther, secondaryFlag.id)
? flags["secondary-model"] === true
: masterOn
? true
: (secondaryFlag.defaultEnabled ?? false);
const secondaryLocked = isFlagLockedByEnv(env, secondaryFlag);
const aliasList = Object.keys(models).sort(); const aliasList = Object.keys(models).sort();
@@ -573,41 +552,6 @@ export function SubagentSettingsPage({
)} )}
<div className="border-t border-border" /> <div className="border-t border-border" />
<div className="flex items-center gap-3">
<div className="flex-1 min-w-0">
<div className="flex items-center gap-2 text-sm text-content-primary">
{t(FLAG_LABELS[secondaryFlag.id].name)}
{!secondaryLocked &&
!isExperimentalFlagSet(rawOther, secondaryFlag.id) &&
secondaryFlag.defaultEnabled && (
<span className="shrink-0 text-xs text-blue-600 dark:text-blue-400 border border-blue-500/30 rounded px-1">
{t("flagDefaultOn")}
</span>
)}
</div>
<div className="text-xs text-content-muted">
{t(FLAG_LABELS[secondaryFlag.id].desc)}
<code className="ml-2">{secondaryFlag.envVar}</code>
</div>
</div>
{secondaryLocked && (
<span className="text-xs text-content-muted shrink-0">
🔒 {t("lockedByEnv")}
</span>
)}
<Toggle
checked={secondaryEnabled}
disabled={secondaryLocked}
ariaLabel={t(FLAG_LABELS[secondaryFlag.id].name)}
onChange={(checked) =>
onChange(
setExperimentalFlag(rawOther, secondaryFlag.id, checked, {
explicitFalse: masterOn || secondaryFlag.defaultEnabled === true,
})
)
}
/>
</div>
{effectiveDefault !== undefined && ( {effectiveDefault !== undefined && (
<div className="rounded-lg border border-border bg-input/40 px-3 py-2.5 space-y-2"> <div className="rounded-lg border border-border bg-input/40 px-3 py-2.5 space-y-2">
@@ -636,9 +580,9 @@ export function SubagentSettingsPage({
<p className="text-xs text-content-muted">{t("secondaryModelPriorityHint")}</p> <p className="text-xs text-content-muted">{t("secondaryModelPriorityHint")}</p>
</Card> </Card>
{/* Other experimental features (not subagent-related) */} {/* Experimental feature flags — one unified list */}
<Card title={t("otherExperimentalFlags")}> <Card title={t("experimentalFlags")}>
{otherFlags.map(renderFlagRow)} {EXPERIMENTAL_FLAGS.map(renderFlagRow)}
<p className="text-xs text-content-muted">{t("experimentalFlagsHint")}</p> <p className="text-xs text-content-muted">{t("experimentalFlagsHint")}</p>
</Card> </Card>
</div> </div>
@@ -25,7 +25,7 @@ export function ModelTotalsChart({ rows }: ModelTotalsChartProps) {
const max = Math.max(...rows.map((m) => m.totalTokens || 0), 1); const max = Math.max(...rows.map((m) => m.totalTokens || 0), 1);
return ( return (
<div className="flex h-full flex-col gap-1 overflow-y-auto"> <div className="flex max-h-[360px] flex-col gap-1 overflow-y-auto">
{rows.map((m, i) => { {rows.map((m, i) => {
const color = i < 10 ? modelColor(i) : "#6b7280"; const color = i < 10 ? modelColor(i) : "#6b7280";
const name = m.isSecondary ? t("subagentModel") : m.modelDisplay || m.model; const name = m.isSecondary ? t("subagentModel") : m.modelDisplay || m.model;
+2 -11
View File
@@ -463,15 +463,10 @@ export const enTranslations: Record<TranslationKey, string> = {
// Advanced settings page (WebUI quick open + experimental flags + secondary model) // Advanced settings page (WebUI quick open + experimental flags + secondary model)
subagentSettings: "Advanced Settings", subagentSettings: "Advanced Settings",
experimentalFlags: "Experimental Features", experimentalFlags: "Experimental Features",
otherExperimentalFlags: "Other Experimental Features",
experimentalFlagsHint: experimentalFlagsHint:
"Toggles are written to the [experimental] section of config.toml; env vars take precedence and locked toggles cannot be changed here", "Toggles are written to the [experimental] section of config.toml; env vars take precedence and locked toggles cannot be changed here",
flagSecondaryModel: "Subagent Secondary Model",
flagSecondaryModelDesc: "Newly spawned subagents bind a second (usually cheaper) model config",
flagToolSelect: "Tool Select", flagToolSelect: "Tool Select",
flagToolSelectDesc: "Enable the experimental tool selection feature", flagToolSelectDesc: "Enable the experimental tool selection feature",
flagMinidbReadmodel: "MiniDB Read Model",
flagMinidbReadmodelDesc: "Enable the v2 engine MiniDB read-model backend",
flagTower: "Tower Mode", flagTower: "Tower Mode",
flagTowerDesc: "Enable tower mode: coordinate multiple agents on a shared objective (/tower command)", flagTowerDesc: "Enable tower mode: coordinate multiple agents on a shared objective (/tower command)",
flagSubagentFork: "Subagent Fork Context", flagSubagentFork: "Subagent Fork Context",
@@ -480,17 +475,13 @@ export const enTranslations: Record<TranslationKey, string> = {
flagWaitForDesc: "The agent can wait for background tasks to finish within the current turn", flagWaitForDesc: "The agent can wait for background tasks to finish within the current turn",
flagAutoSessionTitle: "Auto Session Title", flagAutoSessionTitle: "Auto Session Title",
flagAutoSessionTitleDesc: "Generate session titles automatically via the managed chat_title tool", flagAutoSessionTitleDesc: "Generate session titles automatically via the managed chat_title tool",
flagRemoteControl: "Remote Control", flagNotifyUser: "NotifyUser tool",
flagRemoteControlDesc: "Enable the experimental remote control feature", flagNotifyUserDesc: "Experimental Updates panel: paginated progress messages from the main agent and subagents (off by default)",
flagSearchWorker: "Search Worker",
flagSearchWorkerDesc: "Run the kap-server global search backend in a background worker host",
flagDefaultOn: "On by default", flagDefaultOn: "On by default",
lockedByEnv: "Locked by env var", lockedByEnv: "Locked by env var",
masterEnvOnHint: masterEnvOnHint:
"KIMI_CODE_EXPERIMENTAL_FLAG is set: flags without an explicit [experimental] entry are forced on; explicit entries in config.toml still win", "KIMI_CODE_EXPERIMENTAL_FLAG is set: flags without an explicit [experimental] entry are forced on; explicit entries in config.toml still win",
secondaryModelSection: "Subagent Model (Secondary Model)", secondaryModelSection: "Subagent Model (Secondary Model)",
secondaryModelDisabledHint:
"The experimental flag \"Subagent Secondary Model\" is off and this config will not take effect — enable the toggle above first",
secondaryModelLabel: "Secondary model", secondaryModelLabel: "Secondary model",
secondaryModelUnset: "Not configured", secondaryModelUnset: "Not configured",
secondaryModelInheritedContext: "Inherited context", secondaryModelInheritedContext: "Inherited context",
+2 -11
View File
@@ -458,15 +458,10 @@ export const zhTranslations = {
// Advanced settings page (WebUI quick open + experimental flags + secondary model) // Advanced settings page (WebUI quick open + experimental flags + secondary model)
subagentSettings: "高级设置", subagentSettings: "高级设置",
experimentalFlags: "实验功能开关", experimentalFlags: "实验功能开关",
otherExperimentalFlags: "其他实验功能",
experimentalFlagsHint: experimentalFlagsHint:
"开关写入 config.toml 的 [experimental] 节;环境变量优先级更高,被锁定的开关无法在此修改", "开关写入 config.toml 的 [experimental] 节;环境变量优先级更高,被锁定的开关无法在此修改",
flagSecondaryModel: "子代理次主力模型",
flagSecondaryModelDesc: "子代理派发时绑定第二份(通常更便宜的)模型配置",
flagToolSelect: "工具选择", flagToolSelect: "工具选择",
flagToolSelectDesc: "启用实验性工具选择功能", flagToolSelectDesc: "启用实验性工具选择功能",
flagMinidbReadmodel: "MiniDB 读模型",
flagMinidbReadmodelDesc: "启用 v2 引擎 MiniDB 读模型后端",
flagTower: "Tower 模式", flagTower: "Tower 模式",
flagTowerDesc: "启用 Tower 多智能体协同模式(/tower 命令切换)", flagTowerDesc: "启用 Tower 多智能体协同模式(/tower 命令切换)",
flagSubagentFork: "子代理 Fork 上下文", flagSubagentFork: "子代理 Fork 上下文",
@@ -475,17 +470,13 @@ export const zhTranslations = {
flagWaitForDesc: "代理可在当前回合内等待后台任务完成,而无需结束回合再被唤醒", flagWaitForDesc: "代理可在当前回合内等待后台任务完成,而无需结束回合再被唤醒",
flagAutoSessionTitle: "自动会话标题", flagAutoSessionTitle: "自动会话标题",
flagAutoSessionTitleDesc: "通过托管 chat_title 工具自动生成会话标题", flagAutoSessionTitleDesc: "通过托管 chat_title 工具自动生成会话标题",
flagRemoteControl: "远程控制", flagNotifyUser: "NotifyUser 工具",
flagRemoteControlDesc: "启用实验性远程控制功能", flagNotifyUserDesc: "实验性 Updates 面板:分页展示主代理与子代理的进度消息(默认关闭)",
flagSearchWorker: "搜索 Worker",
flagSearchWorkerDesc: "kap-server 全局搜索的后台 worker 宿主服务",
flagDefaultOn: "默认开启", flagDefaultOn: "默认开启",
lockedByEnv: "被环境变量锁定", lockedByEnv: "被环境变量锁定",
masterEnvOnHint: masterEnvOnHint:
"KIMI_CODE_EXPERIMENTAL_FLAG 已设置:未显式配置的实验项将被强制开启;config.toml 中的显式配置优先生效", "KIMI_CODE_EXPERIMENTAL_FLAG 已设置:未显式配置的实验项将被强制开启;config.toml 中的显式配置优先生效",
secondaryModelSection: "子代理模型(次主力模型)", secondaryModelSection: "子代理模型(次主力模型)",
secondaryModelDisabledHint:
"实验开关「子代理次主力模型」未启用,本配置不会生效——请先在上方打开对应开关",
secondaryModelLabel: "次主力模型", secondaryModelLabel: "次主力模型",
secondaryModelUnset: "未配置", secondaryModelUnset: "未配置",
secondaryModelInheritedContext: "继承上下文长度", secondaryModelInheritedContext: "继承上下文长度",
File diff suppressed because it is too large. Load diff
+771 -379
View File
File diff suppressed because it is too large. Load diff
+37 -17
View File
@@ -1,10 +1,12 @@
import { describe, expect, it } from "vitest"; import { describe, expect, it } from "vitest";
import { import {
EXPERIMENTAL_FLAGS, EXPERIMENTAL_FLAGS,
forcedEnvValue,
getExperimentalFlags, getExperimentalFlags,
getSecondaryModel, getSecondaryModel,
getSubagentModelPool, getSubagentModelPool,
isExperimentalFlagSet, isExperimentalFlagSet,
isFlagLockedByEnv,
removeSubagentPoolEntry, removeSubagentPoolEntry,
setExperimentalFlag, setExperimentalFlag,
setSecondaryModelOnly, setSecondaryModelOnly,
@@ -163,13 +165,13 @@ describe("setSecondaryModelOnly", () => {
it("preserves other top-level raw_other sections", () => { it("preserves other top-level raw_other sections", () => {
const raw = { const raw = {
experimental: { "secondary-model": true }, experimental: { wait_for: true },
secondary_model: { model: "kimi-k1" }, secondary_model: { model: "kimi-k1" },
}; };
const next = setSecondaryModelOnly(raw, "kimi-k2") as { const next = setSecondaryModelOnly(raw, "kimi-k2") as {
experimental?: unknown; experimental?: unknown;
}; };
expect(next.experimental).toEqual({ "secondary-model": true }); expect(next.experimental).toEqual({ wait_for: true });
}); });
}); });
@@ -455,31 +457,49 @@ describe("validateSubagentPool", () => {
}); });
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// [experimental] — flag registry (kimi-code 0.41.0) + write semantics // [experimental] — flag registry (kimi-code 0.42.0) + write semantics
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
describe("experimental flag registry", () => { describe("experimental flag registry", () => {
it("mirrors the 0.41.0 v2 registry (9 flags, file_history removed)", () => { it("mirrors the 0.42.0 v2 registry (6 flags)", () => {
expect(EXPERIMENTAL_FLAGS.map((f) => f.id)).toEqual([ expect(EXPERIMENTAL_FLAGS.map((f) => f.id)).toEqual([
"secondary-model", "wait_for",
"tool-select", "tool-select",
"persistence_minidb_readmodel", "notify_user",
"tower", "tower",
"subagent_fork", "subagent_fork",
"wait_for",
"auto_session_title", "auto_session_title",
]);
// Removed upstream: file_history (0.41.0, turn-level file history is
// always on) plus secondary-model / persistence_minidb_readmodel /
// remote-control / search_worker (0.42.0 — promoted or moved to
// dedicated config, the minidb read-model now lives under [database]),
// so the adapter must not mirror them either.
for (const id of [
"file_history",
"secondary-model",
"persistence_minidb_readmodel",
"remote-control", "remote-control",
"search_worker", "search_worker",
]); ]) {
// Upstream removed the flag in 0.41.0 (turn-level file history is expect(EXPERIMENTAL_FLAGS.some((f) => f.id === id)).toBe(false);
// always on), so the adapter must not mirror it either. }
expect(EXPERIMENTAL_FLAGS.some((f) => f.id === "file_history")).toBe(false);
}); });
it("secondary-model is on by default since 0.40.1", () => { it("notify_user is opt-in: no upstream default, own env var", () => {
expect( const flag = EXPERIMENTAL_FLAGS.find((f) => f.id === "notify_user");
EXPERIMENTAL_FLAGS.find((f) => f.id === "secondary-model")?.defaultEnabled expect(flag?.envVar).toBe("KIMI_CODE_EXPERIMENTAL_NOTIFY_USER");
).toBe(true); expect(flag?.defaultEnabled).toBeUndefined();
});
it("notify_user is locked by its env var and forced to the env value", () => {
const flag = EXPERIMENTAL_FLAGS.find((f) => f.id === "notify_user")!;
expect(isFlagLockedByEnv({}, flag)).toBe(false);
// A set env var locks the toggle even when it is falsy — it can force
// the flag on *or* off.
expect(isFlagLockedByEnv({ [flag.envVar]: "0" }, flag)).toBe(true);
expect(forcedEnvValue({ [flag.envVar]: "0" }, flag)).toBe(false);
expect(forcedEnvValue({ [flag.envVar]: "true" }, flag)).toBe(true);
}); });
}); });
@@ -514,13 +534,13 @@ describe("setExperimentalFlag", () => {
it("an explicit false coexists with other flags and keeps the section", () => { it("an explicit false coexists with other flags and keeps the section", () => {
const next = setExperimentalFlag( const next = setExperimentalFlag(
rawExp({ "secondary-model": false }), rawExp({ wait_for: false }),
"tower", "tower",
true, true,
{ explicitFalse: true } { explicitFalse: true }
) as { experimental: Record<string, unknown> }; ) as { experimental: Record<string, unknown> };
expect(next.experimental).toEqual({ expect(next.experimental).toEqual({
"secondary-model": false, wait_for: false,
tower: true, tower: true,
}); });
}); });
+15 -28
View File
@@ -24,7 +24,7 @@ function isTruthyEnv(value: string | undefined): boolean {
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
export interface ExperimentalFlagDef { export interface ExperimentalFlagDef {
/** Flag id as stored in config.toml `[experimental]` (e.g. "secondary-model"). */ /** Flag id as stored in config.toml `[experimental]` (e.g. "wait_for"). */
id: string; id: string;
/** Single-feature env var that can force the flag (locks the UI toggle). */ /** Single-feature env var that can force the flag (locks the UI toggle). */
envVar: string; envVar: string;
@@ -33,43 +33,30 @@ export interface ExperimentalFlagDef {
defaultEnabled?: boolean; defaultEnabled?: boolean;
} }
/** Known experimental flags — mirrors the kimi-code v2 flag registry /** Known experimental flags — mirrors the kimi-code 0.42.0 v2 flag registry
* (the per-feature flag.ts files under packages/agent-core-v2/src: * (the per-feature flag.ts files under packages/agent-core-v2/src):
* secondary-model, tool-select, persistence_minidb_readmodel, tower, * wait_for, tool-select, notify_user, tower, subagent_fork,
* subagent_fork, wait_for, auto_session_title, remote-control, * auto_session_title. Removed in 0.42.0: secondary-model, remote-control
* search_worker). `acp-v2` was removed upstream and is dropped here; * and search_worker (promoted or dropped upstream — no flag left), and
* `file_history` was removed in kimi-code 0.41.0 (turn-level file * persistence_minidb_readmodel (now controlled by the `[database]` config
* history is always on, no flag). Keep the env-var list in sync with * section). `acp-v2` was removed upstream earlier; `file_history` was
* `get_experimental_env_status` in src-tauri/src/commands.rs. */ * removed in kimi-code 0.41.0 (turn-level file history is always on, no
* flag). Keep the env-var list in sync with `get_experimental_env_status`
* in src-tauri/src/commands.rs. */
export const EXPERIMENTAL_FLAGS: ExperimentalFlagDef[] = [ export const EXPERIMENTAL_FLAGS: ExperimentalFlagDef[] = [
{
id: "secondary-model",
envVar: "KIMI_CODE_EXPERIMENTAL_SECONDARY_MODEL",
defaultEnabled: true, // on by default since kimi-code 0.40.1
},
{ id: "tool-select", envVar: "KIMI_CODE_EXPERIMENTAL_TOOL_SELECT" },
{
id: "persistence_minidb_readmodel",
envVar: "KIMI_CODE_EXPERIMENTAL_PERSISTENCE_MINIDB_READMODEL",
defaultEnabled: true,
},
{ id: "tower", envVar: "KIMI_CODE_EXPERIMENTAL_TOWER" },
{ id: "subagent_fork", envVar: "KIMI_CODE_EXPERIMENTAL_SUBAGENT_FORK" },
{ {
id: "wait_for", id: "wait_for",
envVar: "KIMI_CODE_EXPERIMENTAL_WAIT_FOR", envVar: "KIMI_CODE_EXPERIMENTAL_WAIT_FOR",
defaultEnabled: true, defaultEnabled: true,
}, },
{ id: "tool-select", envVar: "KIMI_CODE_EXPERIMENTAL_TOOL_SELECT" },
{ id: "notify_user", envVar: "KIMI_CODE_EXPERIMENTAL_NOTIFY_USER" },
{ id: "tower", envVar: "KIMI_CODE_EXPERIMENTAL_TOWER" },
{ id: "subagent_fork", envVar: "KIMI_CODE_EXPERIMENTAL_SUBAGENT_FORK" },
{ {
id: "auto_session_title", id: "auto_session_title",
envVar: "KIMI_CODE_EXPERIMENTAL_AUTO_SESSION_TITLE", envVar: "KIMI_CODE_EXPERIMENTAL_AUTO_SESSION_TITLE",
}, },
{ id: "remote-control", envVar: "KIMI_CODE_EXPERIMENTAL_REMOTE_CONTROL" },
{
id: "search_worker",
envVar: "KIMI_CODE_EXPERIMENTAL_SEARCH_WORKER",
defaultEnabled: true,
},
]; ];
export const EXPERIMENTAL_MASTER_ENV = "KIMI_CODE_EXPERIMENTAL_FLAG"; export const EXPERIMENTAL_MASTER_ENV = "KIMI_CODE_EXPERIMENTAL_FLAG";