@khorsheed/dsh-local-agent-kimi 0.1.0-rc.6

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 (50) hide show
  1. package/README.en.md +132 -0
  2. package/README.i18n.yaml +6 -0
  3. package/README.md +132 -0
  4. package/cordis.patch.yml +34 -0
  5. package/lib/client.js +1066 -0
  6. package/lib/index.js +4716 -0
  7. package/lib/invariant.js +31 -0
  8. package/lib/types/client/SettingsCard.d.ts +77 -0
  9. package/lib/types/client/SettingsCard.js +197 -0
  10. package/lib/types/client/icons.d.ts +18 -0
  11. package/lib/types/client/icons.js +2 -0
  12. package/lib/types/client/index.d.ts +42 -0
  13. package/lib/types/client/index.js +89 -0
  14. package/lib/types/client/locales.d.ts +64 -0
  15. package/lib/types/client/locales.js +55 -0
  16. package/lib/types/credential-guard.d.ts +29 -0
  17. package/lib/types/credential-guard.js +107 -0
  18. package/lib/types/index.d.ts +69 -0
  19. package/lib/types/index.js +270 -0
  20. package/lib/types/invariant.d.ts +16 -0
  21. package/lib/types/invariant.js +33 -0
  22. package/lib/types/kimi-cli-provider.d.ts +215 -0
  23. package/lib/types/kimi-cli-provider.js +855 -0
  24. package/lib/types/live-driver.d.ts +245 -0
  25. package/lib/types/live-driver.js +1312 -0
  26. package/lib/types/live-switch.d.ts +78 -0
  27. package/lib/types/live-switch.js +112 -0
  28. package/lib/types/member-bridge-config.d.ts +43 -0
  29. package/lib/types/member-bridge-config.js +80 -0
  30. package/lib/types/model-broker.d.ts +97 -0
  31. package/lib/types/model-broker.js +205 -0
  32. package/lib/types/model-catalog.d.ts +26 -0
  33. package/lib/types/model-catalog.js +86 -0
  34. package/lib/types/model-configuration.d.ts +11 -0
  35. package/lib/types/model-configuration.js +55 -0
  36. package/lib/types/preset-tools.d.ts +20 -0
  37. package/lib/types/preset-tools.js +36 -0
  38. package/lib/types/provision.d.ts +131 -0
  39. package/lib/types/provision.js +363 -0
  40. package/lib/types/records.d.ts +35 -0
  41. package/lib/types/records.js +85 -0
  42. package/lib/types/session-configuration.d.ts +5 -0
  43. package/lib/types/session-configuration.js +26 -0
  44. package/lib/types/session-mirror.d.ts +180 -0
  45. package/lib/types/session-mirror.js +446 -0
  46. package/lib/types/session-view.d.ts +121 -0
  47. package/lib/types/session-view.js +423 -0
  48. package/locale/en.json +6 -0
  49. package/locale/zh.json +6 -0
  50. package/package.json +107 -0
package/README.en.md ADDED
@@ -0,0 +1,132 @@
1
+ # dsh-local-agent-kimi
2
+
3
+ > Delegate to the Kimi Code CLI from any dsh session — your own `~/.kimi-code` stays untouched.
4
+
5
+ **Live output migration.** Live runs always consume incremental output. The old `liveMirrorGranularity: event | token` key is accepted for existing profiles but ignored; changing it never changes a running process. Exec remains available for evaluation. Final provider items remain authoritative, including tool history and usage.
6
+
7
+ English | [中文](README.md)
8
+
9
+ The Kimi Code harness of the local-agent family: every agent preset gains a `subagent_kimi` delegation tool and a `/kimi` command family, backed by the real `kimi` CLI running in its own scoped home.
10
+
11
+ ## Features
12
+
13
+ - **Delegate from any preset** — the `subagent_kimi` tool mounts at the profile root; no per-preset variants.
14
+ - **Scoped home** — config, credentials, and sessions live under `$DSH_HOME/local-agent/kimi`, never in your `~/.kimi-code`.
15
+ - **Device-code login** — `/kimi login` shows the auth URL in the session; `/kimi logout` clears scoped credentials for a fresh account.
16
+ - **Resumable delegations** — pass back the self-described `resume` handle to continue the same Kimi session, with real per-round usage and timing.
17
+ - **Session records + settings UI** — `/kimi sessions` lists your delegations; Settings → 本地 Agent shows auth status and the current workspace's sessions.
18
+ - **Model readback and per-cell working directory** — every settled round reads back the model from the wire.jsonl usage/request records into the delegation record; orchestrators pass a `cwd` per cell, and a resume in a different directory is rejected.
19
+ ## Install
20
+
21
+ Requires a running dsh profile and the Kimi Code CLI (`kimi`) on `PATH` — the plugin installs neither and never logs in on your behalf.
22
+
23
+ ```sh
24
+ # Name both: `dsh plugin add` reconciles only direct dependencies.
25
+ dsh plugin --profile web add @khorsheed/dsh-local-agent
26
+ dsh plugin --profile web add @khorsheed/dsh-local-agent-kimi
27
+ # Restart the profile, then run /kimi login once from a session.
28
+ ```
29
+
30
+ Uninstall:
31
+
32
+ ```sh
33
+ dsh plugin --profile web remove @khorsheed/dsh-local-agent-kimi
34
+ ```
35
+
36
+ The scoped home is kept on purpose so a reinstall needs no fresh login — delete `$DSH_HOME/local-agent/kimi` to remove every trace.
37
+
38
+ ## Config
39
+
40
+ Optional: route Kimi's LLM requests through your own endpoint via the scoped `config.toml` (`$DSH_HOME/local-agent/kimi/config.toml`):
41
+
42
+ ```toml
43
+ [providers."managed:kimi-code"]
44
+ base_url = "https://your-router.example/v1"
45
+ ```
46
+
47
+ Edit **only this key** — provisioning never overwrites an existing config, and the `[services.moonshot_*]` base URLs must stay (they route the built-in search/fetch tools).
48
+
49
+ ⚠️ **OAuth token exposure**: the scoped OAuth token is sent to whatever endpoint serves the request — point `base_url` only at endpoints you control or trust.
50
+
51
+ Plugin config of its own (optional, in the profile patch layer):
52
+
53
+ ```yaml
54
+ - id: local-agent-kimi
55
+ config:
56
+ model: kimi-code/k3 # optional: every delegation round starts the CLI with it; absent passes no model at all (below)
57
+ thinkingEffort: high # reasoning effort; written into a FRESH scoped config.toml ([thinking] effort and the model default_effort; low/high/max, default high). Provision-time only — an existing config is never overwritten
58
+ live: false # live driver: one resident kimi acp process per member, one session/prompt per round (runtime-level graceful session/cancel, push-triggered mirroring); off — or a channel that cannot come up — means the one-shot kimi -p path
59
+ liveIdleMs: 1800000 # idle lifetime of a resident runtime before reclaim (default 30 min)
60
+ ```
61
+
62
+ ### Default model (`model`)
63
+
64
+ ⚠️ **This key's meaning changed.** It used to apply exactly once, when a FRESH scoped home was provisioned: it became the `default_model` of the written `config.toml`, and an existing config was left alone. It now applies to **every delegation round**. The provisioning mirror is kept: a fresh scoped home with no user config to mirror still gets its minimal managed config built around this value.
65
+
66
+ **Absent = today's behavior.** Without this key the plugin adds no model flag to the argv at all: the scoped `config.toml`'s top-level `default_model` decides which model runs.
67
+
68
+ **Set = every delegation round starts the CLI with it.** The value must name a model the scoped `config.toml` defines (a `[models."…"]` key) — `-m` resolves against kimi's own model table.
69
+
70
+ | Drive | How it is passed |
71
+ |---|---|
72
+ | one-shot (default) | `kimi -m <model> -p <task>`; a resume round is `kimi -S <session> -m <model> -p <task>` — `-S` still leads and `-m` hugs `-p` (anything after `-p` is read as the prompt) |
73
+ | resident (`live: true`) | `kimi acp` has **no** model flag, so the scoped `config.toml`'s top-level `default_model` is rewritten to the value before each runtime spawns. The write is in place and idempotent: only that one top-level line moves, while comments, model tables, providers and `[thinking]` stay byte-identical; with no config to write into, none is created (provisioning owns creation), the round still runs, and the model read-back is what surfaces a mismatch |
74
+
75
+ So: the one-shot drive never touches your `config.toml`; the resident drive rewrites its `default_model` line.
76
+
77
+ The settings card’s "Default model" writes the provider setting for subsequent rounds. Its shared picker displays the scoped model directory, discovery source and completeness, plus an explicit model-ID input when needed. Clearing the selection follows the effective configuration/default chain. Saving does not interrupt an active round and requires no reload. If the shared picker is unavailable, the card retains its text-input fallback. Per-member model and effort changes use the durable controls described below.
78
+
79
+ The rich directory uses native ACP session model/configuration metadata when a live member exposes it, preserving labels and the current model's reasoning options. Before that it shows the member's scoped configuration as incomplete, configuration-sourced candidates. Reads create no disposable ACP sessions. Directory refresh and subscriptions use the shared core cache. The shared model picker and member effort controls are connected. Busy selections apply at the next complete turn boundary, including tool continuations; core owns current/pending state, cancellation and retry, while frozen evaluation members reject changes.
80
+
81
+ **A delegation's own model outranks this key.** An orchestrator may name the model for ONE delegation through the facade's `DelegationCallOptions.model`, which sits above this key (the layer order is in the family core README). The first round's request is recorded and every resume round re-requests it — `resume` takes no model of its own. In resident mode it becomes the member's **start model**: bound through the scoped-config rewrite before the runtime spawns; a resident runtime already bound to another model is retired first and respawns onto the new one, `session/load`-ing the same CLI session.
82
+
83
+ **Member-level switching (composer).** A member session's composer can switch that member's model: a **session-level override** ranking above every layer (the delegation record included). It lives in memory only and does not survive a host restart. A switch is refused while the member has a round in flight. In resident mode a switch retires the runtime whose bound model differs — the next round respawns onto the new model and `session/load`s the same CLI session, so the conversation carries over; on the one-shot drive the next round simply passes the new value to `-m`.
84
+
85
+ **This is not an evaluation gap.** A run freezes its condition at setup: change the key mid-run and the next round's model read-back sees declared ≠ observed and fails the run as misattributed (frozen decision 5).
86
+
87
+ **Evaluation snapshot (`effectiveSettings`).** The harness declares a live-read snapshot of its fairness-relevant settings for the evaluation condition hash: drive (exec/live, following the live preference), reasoning effort (read from the scoped config's `[thinking] effort`, falling back to the model's `default_effort`), whether tool use is auto-approved (the scoped config carries the `Bash(*)` allow rule), whether a custom endpoint is pinned (hostname only; the managed endpoint does not count as pinned), and the configured model (the plugin config's `model` key first — it overrules the file every round — then the scoped config's top-level `default_model`; absent when neither names one — never guessed), and the CLI version (`kimi --version`, cached against the executable's path + mtime; absent when the CLI cannot be asked). `/kimi status` and the `LocalAgentStatus` Remote attach the same snapshot — this is the read side of web-eval frozen decisions 2 through 4.
88
+
89
+ ## Compatibility
90
+
91
+ - npm release line (`@deepseek-ai/dsh@0.1.5-rc.1`): ✅ public API compatible. Live generation uses the local-agent transient Remote and public Conversation nodes; suffix checkpoints provide recovery, and native final messages retain transcript and usage semantics. Browser P95 acceptance is tracked separately in the room coordinator proposal. Older hosts stay on the previous release line.
92
+ - source line (deepseek-harness master): ✅ (verifiedHost: 0.1.5-rc.1)
93
+
94
+ ## Known Limitations
95
+
96
+ - **Login is interactive** — device-code only, one login at a time per harness, no API-key path.
97
+ - **Process bookkeeping touches the real home** — kimi writes content-free `~/.kimi-code/server/instances/*.json` regardless of `KIMI_CODE_HOME`; config, credentials, and sessions stay scoped.
98
+ - **No transcript replay yet** — `/kimi sessions` lists records but does not render a session's conversation.
99
+ - **`/kimi` commands need a Web session** — headless profiles can still delegate through a composition that mounts the tool row.
100
+
101
+ ## How it works
102
+
103
+ <details>
104
+ <summary>Internals (click to expand)</summary>
105
+
106
+ **Scoped home and login.** Every Kimi process the bundle starts runs with `KIMI_CODE_HOME=$DSH_HOME/local-agent/kimi`. Login is device-code only: `/kimi login` surfaces the authorization URL and code in the session, the CLI polls in the background, and credentials land in the scoped home. `/kimi logout` removes the scoped credentials and OAuth cache (the kimi CLI has no logout command). On first boot the scoped home is provisioned with a `config.toml` — the user's own config with every `api_key` blanked, or a minimal managed config when the user has none — because the CLI refuses to authenticate without provider and model definitions; an existing config is never overwritten.
107
+
108
+ **Mounting.** The bundle patch registers the `kimi` harness and mounts the `subagent_kimi` tool at the profile root; the `kimi-cli` one-shot provider spawns `kimi -p` under the scoped home. The family core (`local-agent` row, shared scoped-homes root) ships in `@khorsheed/dsh-local-agent`'s own patch, declared here as a dependency; the browser settings section ships with the family core's `./client` half.
109
+
110
+ **Session records.** `/kimi sessions` lists the scoped home's `session_index.jsonl` — this harness's delegations, never the user's personal sessions. The settings section renders the same list narrowed to records whose `workDir` matches the current session's cwd, and keeps re-probing auth status while a login is pending.
111
+
112
+ **Model readback and the cwd override.** After every settled round the provider reports the actual model from the wire.jsonl `usage.record` (falling back to `llm.request`) events' `model` field — last one seen, since a resumed session's later rounds append later records — through the `settled` run-progress event, and merges it into the delegation record's `observedModel` field; absence is recorded, never guessed. An orchestrator may also pass a per-round working directory through the facade's `DelegationCallOptions.cwd` (recorded as the record's `cwd`); when a resume round resolves to a different directory than the recorded first round, it fails loud before any process spawns — a CLI session continues in the directory its first round ran in.
113
+
114
+ **Model broker.** The harness registers a `modelBroker` with the registry: the settings card reads the memberless `modelInfo` (effective layer + choices) and the member composer reads and writes the per-member override through the gateway. The resolution order is fixed: override → the delegation's recorded model → the plugin `model` key → the scoped `default_model` → the CLI's built-in default (which names nothing). Choices are the deduped union of the key, the scoped default, the config's `[models."…"]` tables, and the recent saves. The override and the start-model ledger are in-memory (cleared on restart); `setMemberModel` throws while the member has a round in flight, and otherwise retires the resident runtime when its bound model differs from the new effective one (a same-model switch is a no-op) — the next round respawns lazily.
115
+
116
+ **Tool-call accounting.** Every settled round also reports how many tool calls it made, on the `settled` run-progress event (`toolCalls: { count, byName }`). What is counted is the transcript's `tool.call` lines carrying THIS ROUND'S TURN — deliberately not the mirror window: in resident mode a live poll may have drained the settle pass's delta, and a resume round must not inherit the earlier rounds' calls. `byName` keys are the tool names the wire gave, verbatim. One accounting per round, never cumulative; a round that called nothing carries no field at all (absent is not zero).
117
+
118
+ **Named scopes.** `/kimi login --scope <name>` opens a second `KIMI_CODE_HOME` at `<homesRoot>/kimi@<name>`; the directory is provisioned with the same two steps in the same order (provider/model config first, then the permission rules — the permission write reads the config file). From then on that scope has its own login, its own wire log and its own transcript mirror. A scoped delegation is exec-only (the resident `kimi acp` is bound per member to the default scoped home) and carries **no member channel**: the bridge declaration is written into a scoped home's `mcp.json` and the socket is a single homes-root one, so a named absence beats a half-wired presence.
119
+
120
+ **Container delegation.** An orchestrator may run the round inside an **already-acquired container** through the facade's `DelegationCallOptions.exec` (`{ container, workdir, env? }`): the argv becomes `docker exec -w <workdir> [-e NAME…] <container> kimi -p …` and everything else (the wire.jsonl mirror and readback, settle, recording) is byte-for-byte unchanged. `env` must name the in-container `KIMI_CODE_HOME`, and it should be the read-write bind mount of the host scoped home — the transcript mirror and the model readback read the host copy of the wire log. A container round always takes the exec one-shot (the resident `kimi acp` is a host process) and **does not write** the member-bridge entry into `mcp.json`: that declaration names a host node path the unit cannot start, and leaving a broken server in a shared config file is worse than running one round without the member channel.
121
+
122
+ **Resume.** A fresh delegation's result text self-describes the handle (`追问请带 resume="<childSessionId>"`); passing it back as the tool's optional `resume` parameter continues the same kimi session (`kimi -S session_<id> -p`) inside the same dsh child session. The handle never rides the prompt: it is resolved through the `localAgent` delegation registry only for the same parent session and provider that recorded the delegation — a forged handle is rejected before any CLI process starts.
123
+
124
+ **Isolation and accounting.** The child is a fresh session in the delegating session's workspace; the parent receives only the final answer or the exact error — child context, commentary, tool activity, and diffs never cross into the parent session. The provider opens `turn/start` at spawn and closes `turn/end` at settle, including on failure or abort (reason `error`/`aborted`), so durations equal real CLI runtime. Every mirrored step (assistant message, tool call/result) is wrapped in a `step/start`–`step/end` boundary pair at the same (turn, step) — the host's live conversation assembler registers a step only at its boundaries, so without them assistant messages do not render in the real-time view (only a full rebuild recovers them); the token granularity's streaming snapshots and completion folds also land before `turn/end`, boundaries included. Usage is the sum of the round's `usage.record` deltas (each one LLM request, not cumulative), carried on the round's final mirrored assistant message; the mirror filters kimi's auto-permission `<system-reminder>` messages, renders tool calls with arguments, pairs results to their calls, and advances incrementally so earlier messages never duplicate. Aborting settles the tool result immediately (SIGTERM→grace→SIGKILL) and keeps the partial work already mirrored.
125
+
126
+ **Live driver (`live: true`).** Replaces the per-round spawn: the member's first delegation brings up one resident `kimi acp` process (ACP over stdio; the handshake requires the `loadSession` capability, otherwise the breaker falls back), `session/new` creates the session (the server-assigned id becomes the delegation record's `cliSessionId`), and every later round is one `session/prompt`; `cancel` lands as `session/cancel` — the process survives and the session stays continuable. The member bridge rides the ACP `mcpServers` inline declaration (no mcp.json write). `session/request_permission` is auto-answered unattended (first allow option, cancelled when none — matching `kimi -p`'s auto-approve). **Mirroring of completed items deliberately stays on the file fold**: ACP pushes token-level chunks, which are not isomorphic to the wire.jsonl line fold, so pushes only trigger throttled `mirrorKimiDelta` passes and the settle pass stays authoritative — one fold, one offset, and the two driver paths cannot drift. Live output folds each completed item 1:1; generation-time text uses the shared transient channel with a maximum batching wait of 50ms. Incremental recovery checkpoints are independent of browser publication, and native final messages replace transient presentation at the reserved (turn, step). Aborted streams retain a final message marked interrupted. Runtimes are reclaimed after an idle timeout (stdin EOF → SIGTERM ladder); after a crash the next round re-spawns and `session/load`s the on-disk session.
127
+
128
+ </details>
129
+
130
+ ## Development
131
+
132
+ Part of the [dsh-plugins](https://github.com/Khorsheed/dsh-plugins) monorepo (`packages/local-agent-kimi`). Issues and contributions welcome there.
@@ -0,0 +1,6 @@
1
+ # Bilingual-pair consistency record: the git blob hash of each side as of the
2
+ # last confirmed-consistent state. Both languages carry equal authority; after
3
+ # editing either side, bring the other along and re-record with:
4
+ # pnpm run verify-translation-pairing --write packages/local-agent-kimi/README.en.md
5
+ packages/local-agent-kimi/README.en.md: a734981b2f97ea092bf387b10c4ce8076cfe5531
6
+ packages/local-agent-kimi/README.md: 359d304a76321d0be3a2c21ed19d499eb1cfdbf9
package/README.md ADDED
@@ -0,0 +1,132 @@
1
+ # dsh-local-agent-kimi
2
+
3
+ > 从任意 dsh 会话委派给 Kimi Code CLI——你自己的 `~/.kimi-code` 不受任何影响。
4
+
5
+ **实时输出迁移。** live 轮次统一消费增量输出。旧 `liveMirrorGranularity: event | token` 配置继续兼容读取,但不再影响行为,也不会改变运行中的进程。评测继续保留 exec。最终内容仍以 provider 完成项为准,包括工具记录和用量。
6
+
7
+ [English](README.en.md) | 中文
8
+
9
+ local-agent 家族的 Kimi Code harness:每个 agent preset 都获得 `subagent_kimi` 委派工具和 `/kimi` 命令族,由运行在自有作用域目录里的真实 `kimi` CLI 支撑。
10
+
11
+ ## 特性
12
+
13
+ - **任意 preset 都能委派**——`subagent_kimi` 工具挂在 profile 根,无需逐 preset 变体。
14
+ - **作用域目录**——config、凭据、会话全部留在 `$DSH_HOME/local-agent/kimi`,与你自己的 `~/.kimi-code` 互不干扰。
15
+ - **device-code 登录**——`/kimi login` 把授权 URL 呈现在会话中;`/kimi logout` 清除作用域凭据,换账号即重新登录。
16
+ - **可续聊的委派**——把结果自述的 `resume` 句柄传回,即继续同一个 Kimi 会话,并按轮记录真实用量与耗时。
17
+ - **会话记录 + 设置界面**——`/kimi sessions` 列出你的委派;设置 → 本地 Agent 显示认证状态与当前工作区的会话。
18
+ - **模型回读与独立工作目录**——每轮从 wire.jsonl 的 usage/request 记录回读实际模型,写进委派记录;编排器可用 `cwd` 选项给每格独立目录,resume 换目录即拒绝。
19
+ ## 安装
20
+
21
+ 需要一个可运行的 dsh profile,且 `PATH` 上有 Kimi Code CLI(`kimi`)——插件既不替你安装,也不替你登录。
22
+
23
+ ```sh
24
+ # 两个都得点名:`dsh plugin add` 只调和直接依赖。
25
+ dsh plugin --profile web add @khorsheed/dsh-local-agent
26
+ dsh plugin --profile web add @khorsheed/dsh-local-agent-kimi
27
+ # 重启 profile,然后在会话里执行一次 /kimi login。
28
+ ```
29
+
30
+ 卸载:
31
+
32
+ ```sh
33
+ dsh plugin --profile web remove @khorsheed/dsh-local-agent-kimi
34
+ ```
35
+
36
+ 作用域目录会被刻意保留,重装后无需重新登录——删除 `$DSH_HOME/local-agent/kimi` 即可清除全部痕迹。
37
+
38
+ ## 配置
39
+
40
+ 可选:经作用域 `config.toml`(`$DSH_HOME/local-agent/kimi/config.toml`)把 Kimi 的 LLM 请求路由到你自己的端点:
41
+
42
+ ```toml
43
+ [providers."managed:kimi-code"]
44
+ base_url = "https://your-router.example/v1"
45
+ ```
46
+
47
+ **只改写这一个键**——预置逻辑从不覆盖已存在的 config,且 `[services.moonshot_*]` 的 base_url 必须保留(它们路由内置的搜索/抓取工具)。
48
+
49
+ ⚠️ **OAuth token 暴露**:作用域登录的 OAuth token 会发送给处理请求的端点——`base_url` 只指向你控制或信任的端点。
50
+
51
+ 插件自身配置(可选,写在 profile patch 层):
52
+
53
+ ```yaml
54
+ - id: local-agent-kimi
55
+ config:
56
+ model: kimi-code/k3 # 可选:每轮委派以它起 CLI;不写就一个模型参数都不传(见下)
57
+ thinkingEffort: high # 推理强度;写入"全新"作用域 config.toml 的 [thinking] effort 与模型 default_effort(low/high/max,默认 high)。仅预置期生效——已存在的 config 永不覆盖
58
+ live: false # 长驻驱动:每成员常驻一个 kimi acp 进程,按轮发 session/prompt(runtime 级优雅取消 session/cancel、推送触发的镜像);关闭或通道不可用即回一次性 kimi -p
59
+ liveIdleMs: 1800000 # 长驻 runtime 空闲回收时限(默认 30 分钟)
60
+ ```
61
+
62
+ ### 默认模型(`model`)
63
+
64
+ ⚠️ **这个键的语义变了。**旧版里 `model` 只在**首次预置**全新作用域目录时用一次——写进 `config.toml` 的 `default_model`,已存在的 config 不动。现在它**每轮委派都生效**。首次预置的镜像行为保留:全新的、没有用户 config 可镜像的作用域目录,仍然按它写出最小 managed config。
65
+
66
+ **不写 = 今天的表现。**没有这个键时,本插件在 argv 上一个模型参数都不加,跑哪个模型由作用域 `config.toml` 的顶层 `default_model` 决定。
67
+
68
+ **写了 = 每轮委派以它起 CLI。**值必须是作用域 `config.toml` 里已定义的那个模型名(`[models."…"]` 的键)——`-m` 是按 kimi 自己的模型表解析的。
69
+
70
+ | 驱动 | 传法 |
71
+ |---|---|
72
+ | 一次性(默认) | `kimi -m <模型> -p <任务>`;resume 轮为 `kimi -S <会话> -m <模型> -p <任务>`——`-S` 仍在最前,`-m` 紧贴 `-p`(`-p` 之后的词会被当成提示词) |
73
+ | 常驻(`live: true`) | `kimi acp` **没有**模型旗标,所以每次起常驻进程前,把作用域 `config.toml` 的顶层 `default_model` 改写成该值。改写是就地、幂等的:只动顶层那一行,注释、模型表、provider、`[thinking]` 全部逐字节保留;没有 config 可写时不新建(预置逻辑才负责创建),该轮照跑,模型回读负责暴露不一致 |
74
+
75
+ 也就是说:一次性驱动不碰你的 `config.toml`,常驻驱动会改写其中的 `default_model` 一行。
76
+
77
+ 设置卡「默认模型」修改后续轮次使用的 provider 配置。共享选择器展示当前作用域的模型目录、发现来源和完整性,并保留按需填写模型 ID 的入口。清空选择后跟随有效配置及默认值链。保存不会打断当前轮次,也无需重载;共享选择器不可用时,卡片保留文本输入兜底。成员级模型和推理强度修改使用下文的持久控制面。
78
+
79
+ 丰富目录优先使用常驻成员提供的 ACP 原生模型和配置元数据,保留显示名及当前模型的推理选项。此前显示该成员 scoped 配置候选,并明确标为配置来源、不完整。查询不创建临时 ACP 会话。目录刷新与订阅共用 core 缓存,共用模型菜单和成员 effort 控制已接入。运行中选择排到下一完整轮次(含工具续跑);core 统一持有当前/待生效配置、撤销与重试,冻结评测成员禁止变更。
80
+
81
+ **委派级的模型优先。**编排器可以经门面 `DelegationCallOptions.model` 给**某一次委派**点名模型,它排在这个键之前(顺序见家族核心 README)。首轮请求的值记进委派记录,resume 轮照它重发——resume 不接受 model 参数。常驻模式下它成为该成员的**起始模型**:起进程前改写作用域 `default_model` 绑定;已有常驻 runtime 绑着别的模型时先退役,以新模型重起并 `session/load` 续上同一个 CLI 会话。
82
+
83
+ **成员级切换(composer)。**成员会话的 composer 可以为该成员切换模型:一个**会话级 override**,排在所有层(含委派记录)之前;只存内存,宿主重启即失效。成员有进行中的轮次时切换被拒绝。常驻模式下,切换让绑定模型不同的常驻 runtime 退役——下一轮以新模型重起、`session/load` 续上同一个 CLI 会话,对话本身不断;一次性模式下下一轮 `-m` 直接带新值。
84
+
85
+ **这不是评测的缺口。**评测 run 的条件在建立时冻结:run 跑到一半改这个键,下一轮的模型回读会发现声明模型 ≠ 实测模型,run 直接判为 misattributed 而失败(冻结决策 5)。
86
+
87
+ **评测快照(effectiveSettings)。** 本 harness 向注册表声明一份实时读取的公平性设置快照,供评测条件哈希使用:drive(exec/live,随 live 偏好)、推理强度(读作用域 config 的 `[thinking] effort`,缺则回模型 `default_effort`)、是否自动批准(作用域 config 是否带 `Bash(*)` 放行规则)、端点是否固定(只报主机名;managed 端点不算固定)、已配置模型(先看插件配置的 `model` 键——它每轮覆盖;没有才读作用域 config 的顶层 `default_model`;都没有就不给字段)、CLI 版本(`kimi --version`,按可执行文件路径+mtime 缓存;探测不到即字段缺位)。`/kimi status` 与 `LocalAgentStatus` Remote 附带同一份快照。web-eval 冻结决策 2 到 4 的显式化即由此读取。
88
+
89
+ ## Compatibility
90
+
91
+ - npm 发布线(`@deepseek-ai/dsh@0.1.5-rc.1`):✅ 公开 API 兼容。生成中的内容走 local-agent 瞬时 Remote 与公开 Conversation 节点;后缀检查点负责恢复,最终原生消息保留转写与用量语义。浏览器 P95 另在 room 协调者提案中验收。更旧宿主留在前一发布线。
92
+ - 源码线(deepseek-harness master):✅(verifiedHost: 0.1.5-rc.1)
93
+
94
+ ## 已知限制
95
+
96
+ - **登录需要一次交互**——仅 device-code,每个 harness 同一时间只允许一次登录,没有 API key 路径。
97
+ - **进程簿记会写真实 home**——无论 `KIMI_CODE_HOME` 如何,kimi 都会写入无实质内容的 `~/.kimi-code/server/instances/*.json`;config、凭据、会话仍在作用域内。
98
+ - **尚无会话回放**——`/kimi sessions` 只列记录,不渲染单个会话的对话内容。
99
+ - **`/kimi` 命令需要 Web 会话**——headless profile 仍可通过挂载工具行的组合进行委派。
100
+
101
+ ## 实现原理
102
+
103
+ <details>
104
+ <summary>内部结构(点击展开)</summary>
105
+
106
+ **模型回读与 cwd 覆盖。** 每轮 settle 后,provider 把 wire.jsonl 里 `usage.record`(次选 `llm.request`)事件 `model` 字段的实际模型(最后一个为准——resume 会话后续轮次追加在文件尾部)随 `settled` 进度事件上报,并合并进 `delegations.jsonl` 的 `observedModel` 字段;取不到即缺位,绝不猜测。编排器还可以经门面 `DelegationCallOptions.cwd` 给本轮指定工作目录(记录进 `cwd` 字段);resume 轮解析出的目录若与首轮记录不一致,进程启动前即 fail loud——CLI 会话延续的是首轮所在目录的上下文。
107
+
108
+ **Model broker。** 本 harness 向注册表声明 `modelBroker`:设置卡读无成员的 `modelInfo`(生效层 + 候选),成员 composer 经 gateway 读写成员级 override。解析顺序固定:override → 委派记录模型 → 插件 `model` 键 → 作用域 `default_model` → CLI 内置默认(不指名任何模型)。候选 = 本键 + 作用域默认 + config 的 `[models."…"]` 表 + 最近存值,去重。override 与起始模型账本都在内存(重启即清);`setMemberModel` 在成员有进行中轮次时抛错,否则在常驻 runtime 绑定模型 ≠ 新生效值时将其退役(同模型是 no-op),下一轮懒重起。
109
+
110
+ **工具调用计数。** 每轮 settle 时,provider 顺带数出本轮的工具调用,随 `settled` 进度事件上报(`toolCalls: { count, byName }`)。数的是 transcript 里**本轮 turn** 的 `tool.call` 行——刻意不按镜像窗口数:长驻模式下 settle 那一遍的 delta 可能已被 live 轮询清空,而 resume 轮也不该把前几轮的调用算进来。`byName` 的键是 wire 给出的工具名,原样保留。本轮一份,绝不累计;一次都没调用就整个字段缺位(缺席 ≠ 0)。
111
+
112
+ **命名 scope。** `/kimi login --scope <名>` 在 `<homesRoot>/kimi@<名>` 里另开一份 `KIMI_CODE_HOME`:目录建立时跑同一份 provision(provider/model 配置,然后是权限规则——顺序不能反,权限写入要读配置文件),之后该 scope 各自登录、各自 wire 日志、各自转写镜像。带 scope 的委派只走 exec(长驻 `kimi acp` 按成员绑缺省目录),且**不带成员通道**:成员桥声明写在作用域目录的 `mcp.json` 里、socket 又是 homes 根级单例,与其半接上不如这一轮明确不要。
113
+
114
+ **容器内委派。** 编排器可以经门面 `DelegationCallOptions.exec`(`{ container, workdir, env? }`)让本轮跑在一个**已取得的容器**里:argv 变成 `docker exec -w <workdir> [-e NAME…] <container> kimi -p …`,其余(wire.jsonl 镜像与回读、settle、记录)逐字节不变。`env` 必须给出容器内的 `KIMI_CODE_HOME`,且它应当是宿主作用域目录的 rw bind 挂载点——转写镜像与模型回读读的是宿主那份 wire 日志。容器轮固定走 exec 一次性驱动(长驻 `kimi acp` 是宿主进程),且**不写** `mcp.json` 的成员桥条目:那条声明带着宿主 node 路径,容器里起不来,与其在共享配置文件里留一个坏 server,不如这一轮不要成员通道。
115
+
116
+ **作用域目录与登录。** 本包启动的每个 Kimi 进程都以 `KIMI_CODE_HOME=$DSH_HOME/local-agent/kimi` 运行。登录只走 device-code:`/kimi login` 把授权 URL 和验证码呈现在会话中,CLI 在后台轮询,凭据写入作用域目录。`/kimi logout` 删除作用域内的凭据与 OAuth 缓存(kimi CLI 没有 logout 命令)。首次启动时作用域目录会被预置一份 `config.toml`——把用户自己的 config 中所有 `api_key` 抹空后复制,用户没有 config 时则写入最小 managed config——因为没有 provider 与 model 定义 CLI 就拒绝认证;已存在的 config 永不覆盖。
117
+
118
+ **挂载。** bundle patch 注册 `kimi` harness,并把 `subagent_kimi` 工具挂到 profile 根;`kimi-cli` 一次性 provider 在作用域目录下 spawn `kimi -p`。家族 core(`local-agent` 行,共享作用域目录根)随 `@khorsheed/dsh-local-agent` 自己的 patch 提供,本包把它声明为依赖;浏览器设置分区随家族 core 的 `./client` 半提供。
119
+
120
+ **会话记录。** `/kimi sessions` 列出作用域目录下的 `session_index.jsonl`——本 harness 的委派,绝不是用户的私人会话。设置分区呈现同一份列表,并收窄到 `workDir` 与当前会话 cwd 一致的记录;登录进行中时状态会持续重新探测,直到凭据落地。
121
+
122
+ **续聊(resume)。** 首次委派的结果文本自述句柄(`追问请带 resume="<childSessionId>"`);把它作为工具的可选 `resume` 参数传回,即在同一个 dsh 子会话里继续同一个 kimi 会话(`kimi -S session_<id> -p`)。句柄绝不进 prompt:它只经 `localAgent` 委派 registry 对记录该委派的同一 parent 会话与 provider 解析——伪造的句柄在任何 CLI 进程启动前就被拒绝。
123
+
124
+ **隔离与记账。** 子会话是委派会话工作区内一个全新会话;父级只收到最终回答或精确错误——子会话的上下文、评论、工具活动与 diff 永不跨入父级会话。provider 在 spawn 时开 `turn/start`、settle 时关 `turn/end`,失败或被中止也会关(reason `error`/`aborted`),因此耗时等于真实 CLI 运行时长。每个镜像 step(assistant 消息、工具调用/结果)都包在同 (turn, step) 的 `step/start`–`step/end` 边界对里——宿主会话的实时装配只在边界上登记 step,缺了边界 assistant 消息在实时视图里不渲染(整页刷新重建时才补回);token 粒度的流式快照与收尾折叠也在 `turn/end` 之前落地并带边界。用量是该轮 delta 内所有 `usage.record` 之和(每条是一次 LLM 请求的口径、非累计),挂在当轮最后一条镜像的 assistant 消息上;镜像过滤 kimi 自动权限模式的 `<system-reminder>` 消息、工具调用带参数渲染、结果按调用配对,并增量推进,早期消息绝不重复。中止会让工具结果立即 settle(SIGTERM→grace→SIGKILL),并保留已镜像的部分成果。
125
+
126
+ **长驻驱动(`live: true`)。** 替代每轮 spawn:成员首轮委派拉起一个常驻 `kimi acp` 进程(ACP over stdio;握手要求 `loadSession` 能力,否则熔断回退),`session/new` 建会话(server 分配 id,即委派记录的 `cliSessionId`),之后每轮 = `session/prompt`;`cancel` 落地为 `session/cancel`——进程不死、会话可续。成员桥经 ACP `mcpServers` 内联声明(不写 mcp.json)。`session/request_permission` 按无人值守策略自动应答(选第一个 allow,无则 cancelled,与 `kimi -p` 的自动批准一致)。**完成项的镜像刻意仍是文件折叠**:ACP 推送的是 token 级 chunk,与 wire.jsonl 行折叠不同构,所以推送只触发节流的 `mirrorKimiDelta` 过一遍,settle 对账仍是权威——单一折叠、单一 offset,两条驱动路径不可能漂移;实时输出把完成子项 1:1 全量折叠,生成中的文本经共用瞬时通道发布,默认最多合并 50ms;增量恢复检查点独立于浏览器发布,最终原生消息在预留的 (turn, step) 替换临时展示,中止的流保留带 interrupted 的最终消息。runtime 空闲超时回收(stdin EOF → SIGTERM 阶梯),崩溃后下一轮自动重连并 `session/load` 盘上的会话。
127
+
128
+ </details>
129
+
130
+ ## 开发
131
+
132
+ 隶属 [dsh-plugins](https://github.com/Khorsheed/dsh-plugins) monorepo(`packages/local-agent-kimi`)。问题与贡献请移步该仓库。
@@ -0,0 +1,34 @@
1
+ # The dsh-local-agent-kimi bundle patch: register the Kimi harness and mount
2
+ # its delegation tool. The family core row (`local-agent`, shared scoped-homes
3
+ # root) lives in the framework bundle @khorsheed/dsh-local-agent's own patch.
4
+ # This bundle declares that package as an npm dependency, which makes the
5
+ # core's module resolvable — it does NOT mount the core row: `dsh plugin add`
6
+ # reconciles only the profile's direct dependencies, so the core must be
7
+ # installed explicitly alongside this bundle, or this provider stays pending.
8
+ # No other patch inserts the core row id.
9
+ #
10
+ # Two rows are inserted:
11
+ # - local-agent-kimi: this bundle's plugin registers the `kimi` harness
12
+ # (KIMI_CODE_HOME, `kimi login`, session_index records).
13
+ # - tool-subagent-kimi: the family-owned delegation tool (schema
14
+ # description/prompt plus optional resume) over the `kimi-cli` provider.
15
+ # The toolName stays `subagent_kimi` — the same model-facing name the
16
+ # official tool-subagent row used — so presets and prompts keep working,
17
+ # while the family tool adds continuation: a later round may pass the dsh
18
+ # child session id returned by the first delegation to continue the same
19
+ # kimi conversation.
20
+ #
21
+ # The subagent_kimi tool row mounts at the profile root, so every agent
22
+ # preset (standard/code/minimal/cordis and future ones) can delegate without
23
+ # per-preset variants. The provider starts no Kimi process until a tool call
24
+ # or /kimi login.
25
+
26
+ - insert:
27
+ - id: local-agent-kimi
28
+ name: '@khorsheed/dsh-local-agent-kimi'
29
+
30
+ - id: tool-subagent-kimi
31
+ name: '@khorsheed/dsh-local-agent-tool-subagent'
32
+ config:
33
+ provider: kimi-cli
34
+ toolName: subagent_kimi