Skip to content

feat: kimi three-mode alignment — native plugin mode - #982

Merged
ranxianglei merged 1 commit into
masterfrom
2026-09-19_kimi-native-plugin
Sep 19, 2026
Merged

ranxianglei merged 1 commit into
masterfrom
2026-09-19_kimi-native-plugin

Conversation

@ranxianglei

Copy link
Copy Markdown
Owner

Fixes #963

What

Kimi Code (Moonshot) now has full three-mode alignment like pi/opencode/dsh: bili kimi launcher (already there, #757), /bili/ URL prefix, and — new — native plugin mode via bili plugin install kimi: no launcher, no env vars, no fixed port for the end user.

Why the design deviates from the dsh playbook

Kimi v2's plugin system is declarative only (kimi.plugin.json: MCP servers, hooks, skills — no in-process JS execution), so the dsh-style global fetch patch is impossible. The adapted design (approved in the #963 thread):

  • Installer writes $KIMI_CODE_HOME/plugins/managed/billion-context/kimi.plugin.json (stdio MCP server node <root>/dist/kimi/native-mcp.js + SessionStart hook node <root>/dist/kimi/bootstrap-hook.js) plus an installed.json record. Version floor: kimi --version ≥ 2.0.0 (installer refuses below; launcher still works).
  • Per-session bootstrap: kimi spawns the MCP server as a direct child per session; it attaches to BILLION_CONTEXT_PROXY or spawns on an ephemeral port, then rewrites routing with an idempotent line-surgical managed block in ~/.kimi-code/config.toml — own [providers.bili] cloning the active provider's oauth/api_key reference verbatim, [models.bili-kimi] alias, top-level default_model redirect with the previous value recorded in-block. Original snapshotted to config.toml.bili-bak once; every write under a mkdir lockfile; user content outside the block never touched (§7.3). Kimi's config hot-reload applies changes to live sessions.
  • Plugin-mode stamping discipline: custom_headers = { x-bili-plugin = "kimi" } is written ONLY after the ACP tool list is verified against the live proxy manifest — round 1 rides wire mode.
  • Runtime-info protocol ([native] runtime-info protocol: plugins report real model config (model / contextWindow / maxOutput) to the proxy #955): reported at bootstrap only (model + context window + max output from the client's own config). Static custom_headers cannot carry per-request window/model headers without going stale on model switch — documented deviation from pi/dsh.
  • Watchdog & lifecycle: 30s probe; attached mode waits forever (never touches a user-owned proxy); spawn mode respawns and re-routes to the new origin; give-up unroutes back to direct upstream (never a dead port). Session end → kimi kills the MCP child → parent-pid watchdog tears down the spawned proxy. Multi-TUI shares the first-spawned proxy and self-heals when it dies.
  • Opt-out: BILI_NATIVE_KIMI=0. Subagent conversations get separate derived sessions (per-call conversation_id binding, feat: per-call conversation_id for MCP tools — let the model supply its session id #760 channel) — kimi exposes no stable session id; documented limitation.

New files

  • src/kimi/toml-edit.ts — pure line-surgery TOML RMW (no parser dependency): managed-block render/apply/unroute with tamper guards + collision throws, route resolution (upstream/auth priority chain incl. KIMI_CODE_BASE_URL env truth injected by kimi's plugin service), plugin-header stamping.
  • src/kimi/native.ts — plan (kill-switches > attach > spawn), health probe (404 pre-first-request IS healthy for a fresh proxy), lockfile-guarded config rewrite + verify, runtime-info activation, unroute/restore.
  • src/kimi/native-mcp.ts — MCP entry: bootstrap → stamp-after-manifest-verify → ACP tool loop (shared runMcpStdio) + respawn/re-route watchdog.
  • src/kimi/bootstrap-hook.ts — SessionStart hook: attach-only opportunistic bootstrap (fail-open, never spawns).
  • tests/kimi-native.test.ts — 20 tests (plan precedence, TOML roundtrips incl. idempotency + user-change preservation, installer under fake home with PATH shim, live-proxy integration incl. runtime-info capture).

The tests found two real bugs, both fixed here: re-applying the managed block used to degrade the recorded default_model (second bootstrap would leave the user stuck on the alias at unroute), and the health probe treated a fresh proxy's pre-first-request 404 as unhealthy.

Pre-flight

  • npm run typecheck ✓
  • npm test ✓ (1919 pass / 0 fail / 2 skipped gated e2e)
  • npm run build ✓ (dist/kimi/native-mcp.js + dist/kimi/bootstrap-hook.js present)
  • README en+zh updated (client table, Option 1, runtime-info implementations, new "Kimi Code" section)

@github-actions

github-actions Bot commented Sep 19, 2026 •

Copy link
Copy Markdown
Contributor

📦 Built Package Artifact

Branch: 2026-09-19_kimi-native-plugin (7f0df5a)

Option A — Install from npm PR tag (recommended)

npm install -g billion-context@pr-982

Each push to this PR publishes a new version under the pr-982 npm tag.

Option B — Download artifact

  1. Download the artifact from the Actions run
  2. Extract the tarball and install:
tar xzf billion-context-pr982.tgz
npm install -g package

Install instructions are refreshed on each push.

@ranxianglei

Copy link
Copy Markdown
Owner Author

🤖 Powered by ework · qwen3.8-27b

[bot] 🏷 收到,开始核验这个 PR(不重做)。初步观察:分支 2026-09-19_kimi-native-plugin(单个 commit e529af5,11 files,+1523/-19),GitHub 显示当前 behind master。我会:拉取分支 → typecheck + 全量测试 + build → 检查 CI → review diff(重点:TOML RMW 的 tamper guard、两压缩模式下的 wire 行为、config.toml 写回是否遵守"只动 managed block")。有结果会在这里汇报。

@ranxianglei

Copy link
Copy Markdown
Owner Author

🤖 Powered by ework · qwen3.8-27b

[bot] 🏷 Verification complete — I did not redo the work (the ework-agent-pr marker is honored). Findings below.

What I verified

  1. Fetched the branch and rebased it onto current master (ba6c9ad, release v0.1.121): applies cleanly, zero conflicts — the overlapping README.md / README.zh-CN.md auto-merge. The "behind" state is therefore not a merge blocker: the merge button works as-is (verified conflict-free), or rebase first if you prefer linear history.
  2. Pre-flight on the rebased head: typecheck ✓ · full suite 1936 pass / 0 fail / 2 skipped (gated e2e) ✓ · build ✓ (dist/kimi/native-mcp.js + dist/kimi/bootstrap-hook.js emitted).
  3. CI on the PR head: all 8 checks green — test matrix ubuntu/windows × node 22/24, version-guard, build-artifact, codex e2e preflight.

Code review (focus areas)

  • config.toml read-modify-write (§7.3): tamper guards throw loud (stray / truncated markers), user-owned [providers.bili] / [models.bili-kimi] outside the block are refused, mkdir lockfile around every write, post-write re-resolve verification, byte-for-byte restore on unroute, and a manually-switched default_model is respected on both re-apply and unroute. Every refusal path has a dedicated test. This is the user-facing safety surface and it holds.
  • Both compression modes (§6): round 1 rides wire mode; the x-bili-plugin=kimi stamp lands only after the ACP tool list is verified against the live manifest, and a manifest failure fails closed to direct upstream rather than leaving a dead port. No server.ts change is needed — pluginAgentHeader treats agent names generically (sticky session.metadata.pluginAgent). The stdio MCP shell forwards tools/call to the proxy with per-call conversation_id binding (feat: per-call conversation_id for MCP tools — let the model supply its session id #760), so the compress call+result pair lives in kimi's own history and subagent conversations get separate derived sessions. The contract holds in both modes.
  • Watchdog & lifecycle: attach mode waits forever (never touches a user-owned proxy), spawn mode respawns + re-routes to the new origin, give-up unreoutes; parent-pid teardown on session end; multi-TUI sharing documented. Matches the design approved in [native] kimi: three-mode alignment (proxy / launcher / native plugin) + runtime-info #963.
  • Installer & docs: v2 engine floor enforced (launcher still works below it), atomic registry upsert without duplicate records, backup-once, plugin status row added, tsup entries, zh/en READMEs in sync, single linear commit, no whitespace churn, every line on-topic.

Minor findings (report-only, nothing fixed)

  1. Stale docstring: unrouteKimi (src/kimi/native.ts:197) claims it "Prefers the pristine snapshot; falls back to the in-block prev-default-model record" — the implementation always uses in-block surgery (.bili-bak is consulted only by restoreKimiBackup at uninstall time). The behavior is the safer of the two (preserves user edits made during native mode); only the comment is wrong.
  2. Redundant double-unroute: kimiRemove (src/plugin-install.ts) calls unrouteKimi a second time when no snapshot exists — restoreKimiBackup's catch already ran it. Idempotent, harmless.

Neither affects behavior. Per §7.4 this is a feat: (new capability surface), so it stays human-merged regardless — and I can't merge PRs under any circumstances. Please merge yourself: #982

一句话总结:已按 #963 批准的设计完整核验 kimi 三模式对齐实现——config.toml 改写路径防篡改守卫完备、wire/plugin 两种压缩模式行为均正确、rebase 到最新 master 后 typecheck/全量测试/build 与 CI 8 项检查全部通过,仅发现两处不影响行为的小问题(一处过期注释、一处幂等的重复 unroute 调用),可以合并。

Kimi Code (Moonshot) gets the full three-mode treatment like pi/opencode/dsh:
launcher (bili kimi, #757), /bili/ URL prefix, and — new — a self-spawning
native plugin via 'bili plugin install kimi'.

Kimi v2's plugin system is declarative only (kimi.plugin.json: MCP servers,
hooks, skills — no in-process JS execution), so the dsh playbook's fetch patch
is impossible. The adapted design (approved in-thread):

- Installer writes $KIMI_CODE_HOME/plugins/managed/billion-context/kimi.plugin.json
  (stdio MCP server + SessionStart hook, abs paths into the live package dist)
  plus an installed.json record; version floor kimi --version >= 2.0.0.
- Per-session bootstrap (MCP child of kimi): attach to BILLION_CONTEXT_PROXY or
  spawn on an ephemeral port, then an idempotent line-surgical managed block in
  ~/.kimi-code/config.toml — own [providers.bili] cloning the active provider's
  oauth/api_key reference, [models.bili-kimi] alias, default_model redirect with
  the previous value recorded; config.toml.bili-bak snapshot once; mkdir lockfile.
  Kimi's config hot-reload applies the change to live sessions.
- Plugin-mode stamping: custom_headers x-bili-plugin=kimi written ONLY after the
  ACP tool list is verified against the live manifest (round 1 rides wire mode).
  Static headers can't carry per-request window/model values without going stale
  on model switch, so runtime-info is reported at bootstrap only (#955 protocol).
- Watchdog: 30s probe; attached mode waits forever, spawn mode respawns and
  re-routes to the new origin; give-up unroutes back to direct upstream.
  Session end: kimi kills the MCP child -> parent-pid teardown kills the proxy.
- Opt-out BILI_NATIVE_KIMI=0. Subagent conversations get separate derived
  sessions (per-call conversation_id binding, #760 channel).

Tests: tests/kimi-native.test.ts (20 tests — plan precedence, toml roundtrips
incl. idempotency + user-change preservation, installer under fake home, live
proxy integration). Two real bugs found by the tests and fixed: re-apply used
to degrade the recorded default_model, and the health probe treated the fresh
proxy's pre-first-request 404 as unhealthy.
@ranxianglei
ranxianglei force-pushed the 2026-09-19_kimi-native-plugin branch from e529af5 to 7f0df5a Compare September 19, 2026 07:39
@ranxianglei

Copy link
Copy Markdown
Owner Author

Rebased onto master (post #981): resolved conflicts in README.md / README.zh-CN.md / tsup.config.ts / src/plugin-install.ts / tests/plugin-agent.test.ts — took the union in every case (claude native #964 + kimi native #963 both present). Pre-flight green: typecheck ✅, 1994 tests pass / 0 fail, build ✅. Head: 7f0df5a.

@ranxianglei
ranxianglei merged commit 519634b into master Sep 19, 2026
8 checks passed
@ranxianglei
ranxianglei deleted the 2026-09-19_kimi-native-plugin branch October 15, 2026 05:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[native] kimi: three-mode alignment (proxy / launcher / native plugin) + runtime-info

1 participant