dsh-loop-engine 1.0.0-rc6 → 1.0.0-rc8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/README.md +43 -3
  2. package/README.zh.md +4 -3
  3. package/lib/client.js +82 -19
  4. package/lib/index.js +2249 -460
  5. package/lib/invariant.js +4 -3
  6. package/lib/types/client/LoopEngineBadge.d.ts +1 -1
  7. package/lib/types/client/LoopEngineComposerSelect.d.ts +1 -1
  8. package/lib/types/client/LoopEngineSection.d.ts +1 -1
  9. package/lib/types/client/index.d.ts +1 -1
  10. package/lib/types/client/locales.d.ts +4 -0
  11. package/lib/types/client/store.d.ts +2 -1
  12. package/lib/types/engine-claude/agent.d.ts +2 -0
  13. package/lib/types/engine-claude/loop.d.ts +24 -2
  14. package/lib/types/engine-claude/mapping.d.ts +3 -3
  15. package/lib/types/engine-codex/agent.d.ts +2 -0
  16. package/lib/types/engine-codex/appserver/mapping.d.ts +4 -4
  17. package/lib/types/engine-codex/loop.d.ts +24 -2
  18. package/lib/types/engine-kimi/acp/client.d.ts +76 -0
  19. package/lib/types/engine-kimi/acp/mapping.d.ts +44 -0
  20. package/lib/types/engine-kimi/acp/types.d.ts +95 -0
  21. package/lib/types/engine-kimi/agent.d.ts +123 -0
  22. package/lib/types/engine-kimi/commands.d.ts +40 -0
  23. package/lib/types/engine-kimi/loop.d.ts +108 -0
  24. package/lib/types/engine-kimi/mapping.d.ts +71 -0
  25. package/lib/types/engine-kimi/permission.d.ts +28 -0
  26. package/lib/types/engine-kimi/process.d.ts +61 -0
  27. package/lib/types/engine-kimi/skills.d.ts +57 -0
  28. package/lib/types/engine-kimi/types.d.ts +23 -0
  29. package/lib/types/engine-pi/agent.d.ts +2 -0
  30. package/lib/types/engine-pi/loop.d.ts +24 -2
  31. package/lib/types/index.d.ts +20 -2
  32. package/lib/types/patch-manager.d.ts +9 -0
  33. package/lib/types/preset.d.ts +73 -0
  34. package/lib/types/provider-route.d.ts +35 -0
  35. package/lib/types/settings.d.ts +6 -6
  36. package/package.json +24 -24
package/README.md CHANGED
@@ -4,8 +4,8 @@
4
4
 
5
5
  Switch the agent loop engine of **dsh web** the same way you switch a model: a
6
6
  "Loop engine" dropdown in Settings chooses which driver runs your agents — the
7
- built-in in-process loop, the Claude Code CLI, the Codex CLI, or the Pi CLI —
8
- without changing anything in the main repository.
7
+ built-in in-process loop, the Claude Code CLI, the Codex CLI, the Pi CLI, or the
8
+ Kimi Code CLI — without changing anything in the main repository.
9
9
 
10
10
  ## Install
11
11
 
@@ -19,6 +19,13 @@ Restart `dsh web`, then open **Settings → Loop engine**.
19
19
  > Everything else you wrote in that file is preserved; only the plugin's own
20
20
  > span changes.
21
21
 
22
+ > **pnpm users:** pnpm 10+ blocks dependency build scripts by default, so the
23
+ > install may report `@google/genai`, `node-pty`, and `protobufjs` as blocked.
24
+ > This is expected — click **"Allow build scripts and retry"** (or run
25
+ > `pnpm approve-builds`, or list them under `pnpm.onlyBuiltDependencies` in
26
+ > your project root) and retry. Only the installing project can grant this;
27
+ > the plugin cannot pre-approve its own dependencies.
28
+
22
29
  ### Requirements
23
30
 
24
31
  - For the Claude Code engine: the Claude Code CLI installed and logged in on
@@ -28,15 +35,30 @@ Restart `dsh web`, then open **Settings → Loop engine**.
28
35
  - For the Pi engine: authenticated the way `pi` expects (its own
29
36
  `~/.pi/agent/auth.json` or the provider's API-key environment variable such as
30
37
  `ANTHROPIC_API_KEY`).
38
+ - For the Kimi Code engine: the `kimi` CLI installed and logged in on the host
39
+ (e.g. `kimi login`), and reachable on `PATH` (or pinned to an absolute path
40
+ via `kimiBin` in the composition entry).
31
41
 
32
42
  ## Usage
33
43
 
34
44
  1. Pick an engine in **Settings → Loop engine** — `in-process` (default),
35
- `claude-code`, `codex`, or `pi` — then restart `dsh web`.
45
+ `claude-code`, `codex`, `pi`, or `kimi` — then restart `dsh web`.
36
46
  2. To return to the default, pick **In-process** and restart again.
37
47
  3. To remove the plugin: `dsh plugin --profile web remove dsh-loop-engine`, then
38
48
  restart `dsh web`.
39
49
 
50
+ ### What a hosted engine takes over
51
+
52
+ While a hosted engine is selected, it owns the session's command and skill
53
+ surface: the plugin disables dsh's own `/goal` and points new sessions at a
54
+ managed `loop-engine` agent preset — a copy of `standard` minus the dsh-native
55
+ `/compact`, `/plan`, goal-tool, and skill rows that an external engine cannot
56
+ honor — so the slash menu shows the engine's bridged commands and its own
57
+ skill catalog. Engine-agnostic dsh commands (`/export`, `/feedback`,
58
+ `/permission`) keep working and stay. Switching back to `in-process` restores
59
+ the previous preset default; already-running sessions always keep the preset
60
+ they were created with.
61
+
40
62
  ### Engine notes
41
63
 
42
64
  - The Claude Code driver runs one SDK query per step; its slash commands are
@@ -54,6 +76,24 @@ Restart `dsh web`, then open **Settings → Loop engine**.
54
76
  preferred, plus the user-level file under the pi config dir) and its
55
77
  `skills/` catalogs (`~/.pi/agent/skills/` and `.pi/skills/`) are surfaced
56
78
  through the dsh skill-injection seam.
79
+ - The Kimi Code driver runs a persistent `kimi acp` child (Agent Client
80
+ Protocol over stdio) and speaks one stateless `session/new` + `session/prompt`
81
+ per dsh step; the durable dsh session log is the sole model context. It streams
82
+ assistant text (`agent_message_chunk`) and thinking (`agent_thought_chunk`)
83
+ incrementally into the log, and maps tool calls/streams (`tool_call` /
84
+ `tool_call_update`) into `tool/call` + `tool/result`. ACP surfaces tool
85
+ approvals as `session/request_permission`, which the driver answers from the
86
+ session's dsh approval knobs (an `ask` policy denies, fail-closed). The child
87
+ is spawned through the dsh subprocess seam — the only privilege boundary
88
+ (default read-only sandbox). Its project `AGENTS.md` chain (cwd→git root) and
89
+ `.kimi-code/skills/` catalogs (user and project) are surfaced through the dsh
90
+ skill-injection seam, and its slash commands are bridged (built-ins forward the
91
+ raw `/name` line back to the engine, which expands it). The prompt is an ACP
92
+ request body — not an argv positional — so there is no command-line length
93
+ ceiling. Note Kimi's remaining slash-command surface is TUI-only
94
+ (`/login`, `/provider`, `/settings`, `/sessions`, …); those are not bridged
95
+ because the ACP prompt surface does not expand them, but `skill:` commands are
96
+ carried by the skill seam and Kimi's own shorthand.
57
97
 
58
98
  ## License
59
99
 
package/README.zh.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [![npm version](https://img.shields.io/npm/v/dsh-loop-engine?color=cb3837)](https://www.npmjs.com/package/dsh-loop-engine)
4
4
 
5
- 像切换模型一样切换 **dsh web** 的 agent 循环引擎:设置页的「Loop engine」下拉选择运行 agent 的驱动——内置 in-process 循环、Claude Code CLI、Codex CLI,或 Pi CLI——**无需改动主仓库**。
5
+ 像切换模型一样切换 **dsh web** 的 agent 循环引擎:设置页的「Loop engine」下拉选择运行 agent 的驱动——内置 in-process 循环、Claude Code CLI、Codex CLI、Pi CLI,或 Kimi Code CLI——**无需改动主仓库**。
6
6
 
7
7
  ## 安装
8
8
 
@@ -19,10 +19,11 @@ dsh plugin --profile web add dsh-loop-engine
19
19
  - 使用 Claude Code 引擎时需要本机已安装并登录 Claude Code CLI。
20
20
  - 使用 Codex 引擎时需要完成认证:本机执行过 `codex login`,或配置 `CODEX_API_KEY` 环境变量。
21
21
  - 使用 Pi 引擎时需要以 `pi` 要求的方式完成认证(其自身的 `~/.pi/agent/auth.json`,或提供方的 API-key 环境变量,如 `ANTHROPIC_API_KEY`)。
22
+ - 使用 Kimi Code 引擎时需要本机已安装并登录 `kimi` CLI(例如 `kimi login`),且在 `PATH` 上(或在组合条目里用 `kimiBin` 固定为绝对路径)。
22
23
 
23
24
  ## 使用方法
24
25
 
25
- 1. 在 **Settings → Loop engine** 选择引擎——`in-process`(默认)、`claude-code`、`codex` 或 `pi`——然后重启 `dsh web`。
26
+ 1. 在 **Settings → Loop engine** 选择引擎——`in-process`(默认)、`claude-code`、`codex`、`pi` 或 `kimi`——然后重启 `dsh web`。
26
27
  2. 要切回默认,选 **In-process** 再重启即可。
27
28
  3. 卸载插件:`dsh plugin --profile web remove dsh-loop-engine`,然后重启 `dsh web`。
28
29
 
@@ -30,7 +31,7 @@ dsh plugin --profile web add dsh-loop-engine
30
31
 
31
32
  - Codex 驱动运行 `codex app-server`,没有交互式工具审批——权限来自会话的 `sandboxMode` + `approvalPolicy`。
32
33
  - Pi 驱动运行 `pi --mode rpc`;Pi 没有权限系统,所以整个子进程经 dsh subprocess 服务做沙箱化(默认 `read-only`)。
33
- - 两者都往会话日志流式输出,并通过 dsh 技能注入接口暴露 `AGENTS.md`。
34
+ - Kimi Code 驱动运行一个常驻的 `kimi acp` 子进程(Agent Client Protocol over stdio),每步一次无状态的 `session/new` + `session/prompt`;durable 会话日志是唯一模型上下文。它把助手文本(`agent_message_chunk`)与**思考**(`agent_thought_chunk`)**增量**写入日志,并把工具调用/流(`tool_call` / `tool_call_update`)映射为 `tool/call` + `tool/result`。ACP 通过 `session/request_permission` 暴露工具审批,驱动根据会话的 dsh 审批旋钮应答(ask 策略拒绝,失败关闭)。子进程经 dsh subprocess seam 拉起——唯一权限边界(默认只读沙箱)。其项目 `AGENTS.md` 链(cwd→git 根)与 `.kimi-code/skills/` 目录(用户与项目)通过 dsh 技能注入接口暴露,其斜杠命令也已桥接(内置命令把原始 `/name` 行转发回引擎展开)。prompt 是 ACP 请求体而非 argv 位置参数,因此**不存在命令行长度上限**。注意 Kimi 剩余的斜杠命令面是纯 TUI(`/login`、`/provider`、`/settings`、`/sessions`…),这些不桥接(ACP prompt 面不扩展它们);`skill:` 命令由技能接口与 kimi 自身的 shorthand 承载。
34
35
 
35
36
  ## License
36
37
 
package/lib/client.js CHANGED
@@ -34,7 +34,8 @@ var ENGINE_OPTIONS = [
34
34
  { value: "in-process", key: "engineInProcess" },
35
35
  { value: "claude-code", key: "engineClaudeCode" },
36
36
  { value: "codex", key: "engineCodex" },
37
- { value: "pi", key: "enginePi" }
37
+ { value: "pi", key: "enginePi" },
38
+ { value: "kimi", key: "engineKimi" }
38
39
  ];
39
40
  function engineLabelKey(engine) {
40
41
  switch (engine) {
@@ -44,6 +45,8 @@ function engineLabelKey(engine) {
44
45
  return "engineCodex";
45
46
  case "pi":
46
47
  return "enginePi";
48
+ case "kimi":
49
+ return "engineKimi";
47
50
  default:
48
51
  return "engineInProcess";
49
52
  }
@@ -206,6 +209,7 @@ function LoopEngineSection(props) {
206
209
  open: pending !== null,
207
210
  onClose: cancelSwitch,
208
211
  title: t("confirmTitle"),
212
+ closeLabel: t("closeLabel"),
209
213
  footer: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
210
214
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "outline", onClick: cancelSwitch, children: t("cancelAction") }),
211
215
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "primary", onClick: confirmSwitch, children: t("confirmAction") })
@@ -236,7 +240,7 @@ function LoopEngineBadge(props) {
236
240
  const { status, engine } = useSnapshot((state) => state);
237
241
  if (status !== "ready") return null;
238
242
  const label = t(
239
- engine === "claude-code" ? "engineClaudeCode" : engine === "codex" ? "engineCodex" : engine === "pi" ? "enginePi" : "engineInProcess"
243
+ engine === "claude-code" ? "engineClaudeCode" : engine === "codex" ? "engineCodex" : engine === "pi" ? "enginePi" : engine === "kimi" ? "engineKimi" : "engineInProcess"
240
244
  );
241
245
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { style: pill, title: t("description"), children: [
242
246
  t("nav"),
@@ -253,7 +257,8 @@ var ENGINE_OPTIONS2 = [
253
257
  { value: "in-process", key: "engineInProcess" },
254
258
  { value: "claude-code", key: "engineClaudeCode" },
255
259
  { value: "codex", key: "engineCodex" },
256
- { value: "pi", key: "enginePi" }
260
+ { value: "pi", key: "enginePi" },
261
+ { value: "kimi", key: "engineKimi" }
257
262
  ];
258
263
  function engineLabelKey2(engine) {
259
264
  switch (engine) {
@@ -263,28 +268,69 @@ function engineLabelKey2(engine) {
263
268
  return "engineCodex";
264
269
  case "pi":
265
270
  return "enginePi";
271
+ case "kimi":
272
+ return "engineKimi";
266
273
  default:
267
274
  return "engineInProcess";
268
275
  }
269
276
  }
277
+ function engineGlyph(engine, size = 16) {
278
+ switch (engine) {
279
+ case "claude-code":
280
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", "aria-hidden": true, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { fill: "currentColor", d: "M21 10.5h3v3h-3v3h-1.5v3H18v-3h-1.5v3H15v-3H9v3H7.5v-3H6v3H4.5v-3H3v-3H0v-3h3v-6h18Zm-15 0h1.5v-3H6Zm10.5 0H18v-3h-1.5z" }) });
281
+ case "codex":
282
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("svg", { width: size, height: size, viewBox: "0 0 256 260", fill: "none", "aria-hidden": true, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { fill: "currentColor", d: "M239.183914,106.202783 C245.054304,88.5242096 243.02228,69.1733805 233.607599,53.0998864 C219.451678,28.4588021 190.999703,15.7836129 163.213007,21.739505 C147.554077,4.32145883 123.794909,-3.42398554 100.87901,1.41873898 C77.9631105,6.26146349 59.3690093,22.9572536 52.0959621,45.2214219 C33.8436494,48.9644867 18.0901721,60.392749 8.86672513,76.5818033 C-5.443491,101.182962 -2.19544431,132.215255 16.8986662,153.320094 C11.0060865,170.990656 13.0197283,190.343991 22.4238231,206.422991 C36.5975553,231.072344 65.0680342,243.746566 92.8695738,237.783372 C105.235639,251.708249 123.001113,259.630942 141.623968,259.52692 C170.105359,259.552169 195.337611,241.165718 204.037777,214.045661 C222.28734,210.296356 238.038489,198.869783 247.267014,182.68528 C261.404453,158.127515 258.142494,127.262775 239.183914,106.202783 L239.183914,106.202783 Z M141.623968,242.541207 C130.255682,242.559177 119.243876,238.574642 110.519381,231.286197 L112.054146,230.416496 L163.724595,200.590881 C166.340648,199.056444 167.954321,196.256818 167.970781,193.224005 L167.970781,120.373788 L189.815614,133.010026 C190.034132,133.121423 190.186235,133.330564 190.224885,133.572774 L190.224885,193.940229 C190.168603,220.758427 168.442166,242.484864 141.623968,242.541207 Z M37.1575749,197.93062 C31.456498,188.086359 29.4094818,176.546984 31.3766237,165.342426 L32.9113895,166.263285 L84.6329973,196.088901 C87.2389349,197.618207 90.4682717,197.618207 93.0742093,196.088901 L156.255402,159.663793 L156.255402,184.885111 C156.243557,185.149771 156.111725,185.394602 155.89729,185.550176 L103.561776,215.733903 C80.3054953,229.131632 50.5924954,221.165435 37.1575749,197.93062 Z M23.5493181,85.3811273 C29.2899861,75.4733097 38.3511911,67.9162648 49.1287482,64.0478825 L49.1287482,125.438515 C49.0891492,128.459425 50.6965386,131.262556 53.3237748,132.754232 L116.198014,169.025864 L94.3531808,181.662102 C94.1132325,181.789434 93.8257461,181.789434 93.5857979,181.662102 L41.3526015,151.529534 C18.1419426,138.076098 10.1817681,108.385562 23.5493181,85.125333 L23.5493181,85.3811273 Z M203.0146,127.075598 L139.935725,90.4458545 L161.7294,77.8607748 C161.969348,77.7334434 162.256834,77.7334434 162.496783,77.8607748 L214.729979,108.044502 C231.032329,117.451747 240.437294,135.426109 238.871504,154.182739 C237.305714,172.939368 225.050719,189.105572 207.414262,195.67963 L207.414262,134.288998 C207.322521,131.276867 205.650697,128.535853 203.0146,127.075598 Z M224.757116,94.3850867 L223.22235,93.4642272 L171.60306,63.3828173 C168.981293,61.8443751 165.732456,61.8443751 163.110689,63.3828173 L99.9806554,99.8079259 L99.9806554,74.5866077 C99.9533004,74.3254088 100.071095,74.0701869 100.287609,73.9215426 L152.520805,43.7889738 C168.863098,34.3743518 189.174256,35.2529043 204.642579,46.0434841 C220.110903,56.8340638 227.949269,75.5923959 224.757116,94.1804513 L224.757116,94.3850867 Z M88.0606409,139.097931 L66.2158076,126.512851 C65.9950399,126.379091 65.8450965,126.154176 65.8065367,125.898945 L65.8065367,65.684966 C65.8314495,46.8285367 76.7500605,29.6846032 93.8270852,21.6883055 C110.90411,13.6920079 131.063833,16.2835462 145.5632,28.338998 L144.028434,29.2086986 L92.3579852,59.0343142 C89.7419327,60.5687513 88.1282597,63.3683767 88.1117998,66.4011901 L88.0606409,139.097931 Z M99.9294965,113.5185 L128.06687,97.3011417 L156.255402,113.5185 L156.255402,145.953218 L128.169187,162.170577 L99.9806554,145.953218 L99.9294965,113.5185 Z" }) });
283
+ case "pi":
284
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("svg", { width: size, height: size, viewBox: "0 0 800 800", fill: "none", "aria-hidden": true, children: [
285
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { fill: "currentColor", fillRule: "evenodd", d: "M165.29 165.29H517.36V400H400V517.36H282.65V634.72H165.29ZM282.65 282.65V400H400V282.65Z" }),
286
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { fill: "currentColor", d: "M517.36 400H634.72V634.72H517.36Z" })
287
+ ] });
288
+ case "kimi":
289
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", "aria-hidden": true, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { fill: "currentColor", d: "M6 3h2.6v6.5L14.4 3h3.4l-5.6 7.4L17.8 21h-3.4l-5.9-8v8H6z" }) });
290
+ default:
291
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_dsh_client_ui_primitives2.FishLogo, { size });
292
+ }
293
+ }
270
294
  var trigger2 = {
271
295
  appearance: "none",
272
296
  boxSizing: "border-box",
273
297
  display: "inline-flex",
274
298
  alignItems: "center",
275
- gap: 6,
276
- padding: "4px 8px",
277
- border: "1px solid var(--dsw-alias-border-l2)",
278
- borderRadius: 10,
279
- background: "var(--dsw-alias-bg-layer-1)",
280
- color: "var(--dsw-alias-label-primary)",
299
+ gap: 4,
300
+ minWidth: 0,
301
+ maxWidth: 220,
302
+ height: 28,
303
+ padding: "0 4px 0 8px",
304
+ border: "none",
305
+ borderRadius: 24,
306
+ outline: "none",
307
+ background: "transparent",
308
+ color: "var(--dsw-alias-label-secondary)",
281
309
  font: "inherit",
282
- fontSize: 12,
310
+ fontSize: 13,
283
311
  lineHeight: "20px",
284
- whiteSpace: "nowrap",
312
+ fontWeight: 500,
285
313
  cursor: "pointer"
286
314
  };
287
- var triggerDisabled2 = { ...trigger2, opacity: 0.5, cursor: "default" };
315
+ var triggerHover = { ...trigger2, background: "var(--dsw-alias-interactive-bg-hover)" };
316
+ var triggerDisabled2 = { ...trigger2, color: "var(--dsw-alias-label-dimmed)", cursor: "default" };
317
+ var triggerIcon = {
318
+ display: "inline-flex",
319
+ flex: "0 0 auto"
320
+ };
321
+ var triggerLabel = {
322
+ minWidth: 0,
323
+ overflow: "hidden",
324
+ textOverflow: "ellipsis",
325
+ whiteSpace: "nowrap"
326
+ };
327
+ var chevron = {
328
+ display: "inline-flex",
329
+ flex: "0 0 auto",
330
+ color: "var(--dsw-alias-label-caption)",
331
+ transition: "transform 120ms ease"
332
+ };
333
+ var chevronOpen = { ...chevron, transform: "rotate(180deg)" };
288
334
  var confirmBody2 = {
289
335
  margin: 0,
290
336
  fontSize: 13,
@@ -295,6 +341,7 @@ function LoopEngineComposerSelect(props) {
295
341
  const { controller, useSnapshot, t } = props;
296
342
  const { status, engine, showInComposer, writable } = useSnapshot((snapshot) => snapshot);
297
343
  const [open, setOpen] = (0, import_react2.useState)(false);
344
+ const [hovered, setHovered] = (0, import_react2.useState)(false);
298
345
  const [pending, setPending] = (0, import_react2.useState)(null);
299
346
  const triggerRef = (0, import_react2.useRef)(null);
300
347
  if (status !== "ready" || !showInComposer) return null;
@@ -327,7 +374,11 @@ function LoopEngineComposerSelect(props) {
327
374
  onClose: () => {
328
375
  setOpen(false);
329
376
  },
330
- items: ENGINE_OPTIONS2.map((option) => ({ id: option.value, label: t(option.key) })),
377
+ items: ENGINE_OPTIONS2.map((option) => ({
378
+ id: option.value,
379
+ label: t(option.key),
380
+ icon: engineGlyph(option.value)
381
+ })),
331
382
  selectedId: engine,
332
383
  onSelect,
333
384
  align: "start",
@@ -341,14 +392,21 @@ function LoopEngineComposerSelect(props) {
341
392
  "aria-haspopup": "menu",
342
393
  "aria-expanded": open,
343
394
  disabled,
344
- style: disabled ? triggerDisabled2 : trigger2,
395
+ style: disabled ? triggerDisabled2 : hovered ? triggerHover : trigger2,
345
396
  title,
397
+ onMouseEnter: () => {
398
+ setHovered(true);
399
+ },
400
+ onMouseLeave: () => {
401
+ setHovered(false);
402
+ },
346
403
  onClick: () => {
347
404
  setOpen(!open);
348
405
  },
349
406
  children: [
350
- label,
351
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_dsh_client_ui_primitives2.IconChevronDownOutline14, { size: 14 })
407
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: triggerIcon, "aria-hidden": true, children: engineGlyph(engine, 14) }),
408
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: triggerLabel, children: label }),
409
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: open ? chevronOpen : chevron, "aria-hidden": true, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_dsh_client_ui_primitives2.IconChevronDownOutline14, { size: 14 }) })
352
410
  ]
353
411
  }
354
412
  )
@@ -360,6 +418,7 @@ function LoopEngineComposerSelect(props) {
360
418
  open: pending !== null,
361
419
  onClose: cancelSwitch,
362
420
  title: t("confirmTitle"),
421
+ closeLabel: t("closeLabel"),
363
422
  footer: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
364
423
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_dsh_client_ui_primitives2.Button, { variant: "outline", onClick: cancelSwitch, children: t("cancelAction") }),
365
424
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_dsh_client_ui_primitives2.Button, { variant: "primary", onClick: confirmSwitch, children: t("confirmAction") })
@@ -371,11 +430,11 @@ function LoopEngineComposerSelect(props) {
371
430
  }
372
431
 
373
432
  // src/client/store.ts
374
- var import_client = require("@deepseek-ai/dsh-client-runtime/client");
433
+ var import_dsh_client_store = require("@deepseek-ai/dsh-client-store");
375
434
  function decodeLoopEngine(section) {
376
435
  if (typeof section !== "object" || section === null || Array.isArray(section)) return void 0;
377
436
  const { engine, showInComposer } = section;
378
- if (engine !== "in-process" && engine !== "claude-code" && engine !== "codex" && engine !== "pi") {
437
+ if (engine !== "in-process" && engine !== "claude-code" && engine !== "codex" && engine !== "pi" && engine !== "kimi") {
379
438
  return void 0;
380
439
  }
381
440
  return { engine, showInComposer: showInComposer !== false };
@@ -389,7 +448,7 @@ var LoopEngineStore = class {
389
448
  }
390
449
  scope;
391
450
  /** uSES-safe state source shared by the registered settings section. */
392
- store = (0, import_client.createSnapshotStore)({
451
+ store = (0, import_dsh_client_store.createSnapshotStore)({
393
452
  status: "loading",
394
453
  engine: "in-process",
395
454
  showInComposer: true,
@@ -514,6 +573,7 @@ var zh = {
514
573
  engineClaudeCode: "Claude Code CLI",
515
574
  engineCodex: "Codex CLI",
516
575
  enginePi: "Pi CLI",
576
+ engineKimi: "Kimi Code CLI",
517
577
  showInComposerLabel: "\u5728\u5BF9\u8BDD\u9875\u663E\u793A\u5F15\u64CE\u9009\u62E9\u5668",
518
578
  unavailable: "\u5FAA\u73AF\u5F15\u64CE\u8BBE\u7F6E\u4E0D\u53EF\u7528",
519
579
  switchNotice: "\u5207\u6362\u5F15\u64CE\u4F1A\u4E2D\u65AD\u5F53\u524D\u4F7F\u7528\u65E7\u5F15\u64CE\u8FD0\u884C\u4E2D\u7684\u4F1A\u8BDD\u3002",
@@ -522,6 +582,7 @@ var zh = {
522
582
  confirmBody: "\u5207\u6362\u4F1A\u4E2D\u65AD\u5F53\u524D\u4F7F\u7528\u65E7\u5F15\u64CE\u8FD0\u884C\u4E2D\u7684\u4F1A\u8BDD\uFF0C\u786E\u8BA4\u540E\u9875\u9762\u5C06\u81EA\u52A8\u5237\u65B0\uFF0C\u65B0\u5BF9\u8BDD\u4F7F\u7528\u65B0\u5F15\u64CE\u3002\u786E\u8BA4\u5207\u6362\u5417\uFF1F",
523
583
  confirmAction: "\u5207\u6362",
524
584
  cancelAction: "\u53D6\u6D88",
585
+ closeLabel: "\u5173\u95ED",
525
586
  claudeModelNotice: "\u5F53\u524D\u4F7F\u7528 Claude Code \u5F15\u64CE\uFF1A\u5B9E\u9645\u6A21\u578B\u7531 Claude Code \u539F\u751F\u51B3\u5B9A\uFF0C\u9875\u9762\u4E0A\u7684\u6A21\u578B\u9009\u62E9\u4E0D\u751F\u6548\u3002"
526
587
  };
527
588
  var en = {
@@ -531,6 +592,7 @@ var en = {
531
592
  engineClaudeCode: "Claude Code CLI",
532
593
  engineCodex: "Codex CLI",
533
594
  enginePi: "Pi CLI",
595
+ engineKimi: "Kimi Code CLI",
534
596
  showInComposerLabel: "Show the engine selector in the chat page",
535
597
  unavailable: "Loop engine settings are unavailable",
536
598
  switchNotice: "Switching engines interrupts sessions currently running on the previous engine.",
@@ -539,6 +601,7 @@ var en = {
539
601
  confirmBody: "Switching interrupts sessions currently running on the previous engine; the page reloads after the switch and new turns use the new engine. Switch now?",
540
602
  confirmAction: "Switch",
541
603
  cancelAction: "Cancel",
604
+ closeLabel: "Close",
542
605
  claudeModelNotice: "Claude Code engine active: the actual model is decided natively by Claude Code; the model selector in this session has no effect."
543
606
  };
544
607