chore(cowork): Kimi CLI follow-ups (pip install / stream-json / CliAppType / button placement)#36
Open
stephenlzc wants to merge 14 commits into
Open
chore(cowork): Kimi CLI follow-ups (pip install / stream-json / CliAppType / button placement)#36stephenlzc wants to merge 14 commits into
stephenlzc wants to merge 14 commits into
Conversation
fix(cowork): use resolveUserShellPath in resolveCommand fallback
Add the Kimi CLI engine to the Cowork agent engine registry as a non-functional scaffold, so the next PRs can fill in the runtime, event normalization, and config-source behavior incrementally. Tracking issue: freestylefly#34 What this PR introduces: * CoworkAgentEngine.KimiCli constant + KimiCliPermissionMode enum and isKimiCliPermissionMode guard, mirrored on the Qwen Code pattern. * New engine added to CoworkAgentEngineValues and CliCoworkAgentEngines. * src/main/libs/kimiCliConfig.ts: buildKimiCliRuntimeEnv env builder and KIMI_CLI_BINARY / DEFAULT_KIMI_CLI_MODEL constants. Respects issue freestylefly#33 (WeSight does not write API keys into ~/.kimi/config.toml). * src/main/libs/kimiCliCliEvent.ts: stream-json event normalizer stub. Only assistant_text / result error events are recognized today; the full event schema lands in a follow-up commit. * src/main/libs/agentEngine/kimiCliRuntimeAdapter.ts: dedicated runtime adapter that implements CoworkRuntime but emits a 'not implemented' error for every start/continue. Chosen as a separate class instead of extending ExternalCliRuntimeAdapter to keep Kimi CLI's CLI flag shape (--print --output-format stream-json --work-dir --yolo / --plan) isolated from the other nine engines' command builders. * coworkEngineRouter + main.ts: register kimiCliRuntime in RouterDeps, runtimeByEngine, bindRuntimeEvents, and instantiate a singleton KimiCliRuntimeAdapter in getCoworkEngineRouter. * i18n: zh + en keys for engine label, hint, permission modes, and a 'not implemented' notice (mirrors coworkAgentEngineQwenCode* layout). Out of scope (follow-up commits): * UI integration in AgentEngineSelect / CoworkEngineSelector / model selector / AgentEnvironmentSetup. * Real spawn of 'kimi --print --output-format stream-json --work-dir <cwd> --yolo --model <model> --prompt <effectivePrompt>'. * Reading ~/.kimi/config.toml and syncing back to it (issue freestylefly#32/freestylefly#33). * 'kimi --login' status detection. * Vitest coverage equivalent to qwenCodeConfig.test.ts / qwenCodeCliEvent.test.ts. Design notes: * Reused the existing QwenCode permission mode shape (auto / conservative) rather than introducing yolo / plan literals yet — the mapping kimi --yolo <-> Auto and kimi --plan <-> Conservative is captured in the i18n hints, and the actual flag translation can land with the runtime work. * Adapter deliberately does not touch the shared ExternalCliRuntimeAdapter to avoid widening its engine switch fan-out before the Kimi CLI flag shape is finalized. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…gine> Adding CoworkAgentEngine.KimiCli broke the engineAvatarManifest Record<CoworkAgentEngine, CoworkStudioAvatar> type check. Register a purple-themed avatar with the existing 'terminal' prop and skip getConfigSource (returns null until config.kimiCliConfigSource lands). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Wire CoworkAgentEngine.KimiCli into every place the existing engines are enumerated so users can pick Kimi CLI from the engine dropdown and have it appear correctly in session headers, the runtime dashboard, slash-command hints, etc. Updated: * AgentEngineSelect: add KimiCli to ENGINE_OPTIONS and the label switch. * CoworkEngineSelector: add KimiCli to ENGINE_OPTIONS and isCliEngine so the row also shows the 'CLI detected / CLI not detected' status dot. * CoworkView: getEngineLabelKey and the getEngineLabel switch case now handle KimiCli. * CoworkSessionDetail: both engine-label switch cases handle KimiCli. * RuntimeDashboardView: getEngineLabel handles KimiCli. * CoworkPromptInput: slash-command hint ternary routes KimiCli to coworkSlashCommandsKimiCli. * i18n (zh + en): add coworkSlashCommandsKimiCli. Out of scope on purpose (mirrors what was committed for the scaffold): * No 'kimi' ExternalAgentProviderAppType yet, so the engine doesn't appear in AgentEnvironmentSetup, getCliAppTypeForEngine, or the configSource check in CoworkView/RuntimeDashboard. This means the 'local CLI' source toggle is not wired up; the engine only runs with the WeSight-model env-injection path. The install-detection surface in AgentEnvironmentSetup will be added together with the real spawn in a follow-up commit (issue freestylefly#34). * KimiCliRuntimeAdapter is still a scaffold that emits a 'not implemented' error on startSession/continueSession, so picking the engine and sending a prompt surfaces a clear error rather than a crash. Real 'kimi --print --output-format stream-json --work-dir ... --yolo --prompt ...' wiring lands next. Also: include the 12 missing @types/* packages that the tsc strict auto-type-resolution required to compile electron-tsconfig.json (they are referenced via @types/* symlinks but the package.json didn't list them as devDependencies). Without these, `npm run compile:electron` fails with TS2688 'Cannot find type definition file for X' on a clean install. This is a pre-existing repo issue; bundling the fix here so the Kimi CLI branch compiles end-to-end. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Wire Kimi CLI into the Settings → Agent Engine tab (not the runtime selector / model page) so users can: 1. Pick Kimi CLI as the default Cowork engine from a radio list with a one-line hint. 2. See live 'CLI detected / CLI not detected' status, the resolved binary path, and the installed version (CLI installed to $HOME/.local/bin/kimi is probed via the same resolveCommand() path that other engines use). 3. See a 'No extra config' panel under the radio when selected; the standard configSource widget is also rendered and gracefully no-ops because CoworkConfig has no kimiCliConfigSource field yet (TODO(issue freestylefly#34) — local CLI / WeSight-model source toggle lands together with the real spawn). Detection plumbing: * externalAgentEnvironment.ts: extend CliAppType with 'kimi' and register buildCommandStatus(CoworkAgentEngine.KimiCli, 'kimi', 'kimi', ...) in the engines array. The snapshot.engines consumer (getCliEngineStatus in Settings.tsx) auto-picks it up. Install stub: * externalAgentCliInstaller.ts: add the matching 'kimi' entry in INSTALL_TARGETS to keep Record<CliAppType, InstallTarget> exhaustive. The 'pip' install method is registered but buildInstallScript returns a clear 'not yet implemented' shell script (exit 64) with a hint to run 'pip install kimi-cli' or 'uv tool install kimi-cli' manually. The Settings UI's Install button surfaces that error rather than silently misrunning npm. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Mirror the Settings → Agent Engine radio list (bfe0512) into the 'More Agent Engines' card grid that renders in AgentEnvironmentSetup so users can see Kimi CLI alongside OpenCode / Grok Build / Qwen Code / DeepSeek-TUI, with the same Install / CLI-detected plumbing provided by externalAgentEnvironment.ts. The card uses `appType: 'kimi'`, so the three `CliAppType` union definitions in the repo had to be kept in lockstep: * src/renderer/types/cowork.ts L266 (renderer-side type alias) * src/renderer/types/electron.d.ts L129 (preload IPC contract) * src/main/libs/externalAgentEnvironment.ts L27 (main process, from bfe0512) Add 'kimi' to the renderer's Record<ExternalAgentProviderAppType, string> install-progress map so the install-progress state is exhaustive. These three `CliAppType` declarations are a known pre-existing duplication; consolidating them into a single shared type is a separate cleanup that should be tracked under issue freestylefly#32 rather than expanded in this branch. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…e wiring
Two changes that together make the 'I already configured the local
CLI, just use it' flow work for every CLI engine, with Kimi CLI as
the new addition.
1. LocalCli-by-default for unconfigured engines
Every CLI engine (ClaudeCode / Codex / Hermes / OpenCode / QwenCode
/ DeepSeekTui / KimiCli) used to default to WesightModel, which
meant a fresh install or a user who never touched Settings saw
'Please configure models in settings first' even when their
~/.{claude,kimi,...}/config already had a perfectly good model.
WeSight's local-provider sync (externalAgentProviderStore.
syncConfiguredProviders) already auto-loads the user's local CLI
config into SQLite on first listProviders call and auto-marks
the first/best provider as is_current=1 — the only thing missing
was the default source value:
* main/coworkStore.ts: new DEFAULT_EXTERNAL_AGENT_CONFIG_SOURCE_FOR_
NEW_INSTALL = LocalCli. getConfig() uses cfg.has(key) to
distinguish 'never stored' (use LocalCli) from 'user explicitly
stored a value' (respect it, even if empty). The 7 normalizers
still fall back to WesightModel on garbage values — that is the
'user stored a corrupt value' safety net, distinct from
'new-install default'.
* renderer/store/slices/coworkSlice.ts: initialState.config flips
7 *ConfigSource defaults to LocalCli. main's getConfig() over-
writes these for any user with a stored value, so existing
users who already chose WesightModel are untouched.
2. Complete Kimi CLI engine wiring (issue freestylefly#34)
The Kimi scaffold landed in 5095db0 / 973ee96, but the runtime
path was a stub. This commit fills in the six per-engine branches
that ExternalCliRuntimeAdapter has for every other CLI:
* getCommandName / getConfigSource / getSelectedProviderForLocalCli
all route Kimi to kimi
* buildCommandArgs: kimi --print --output-format stream-json
--work-dir <cwd> [--yolo|--plan] --model <model> --prompt <prompt>
* applyKimiCliRuntimeConfig: injects KIMI_API_KEY / KIMI_BASE_URL
/ KIMI_MODEL_NAME via the existing buildKimiCliRuntimeEnv
* handleKimiCliEvent: routes stream-json through
parseKimiCliJsonLine / normalizeKimiCliCliEvent
* runTurn: routes Kimi WesightModel path to applyKimiCliRuntimeConfig
Renderer plumbing:
* CoworkModelSelector.resolveLocalCliAppType returns 'kimi' when
the user is on LocalCli + Kimi
* CoworkView.getCliAppTypeForEngine and getModelContextLabel
return 'kimi' / 'coworkAgentConfigSourceLocalCli' respectively
* coworkStudio.getConfigSource returns config.kimiCliConfigSource
(the TODO(issue freestylefly#34) fallback is gone)
Main plumbing:
* applyExternalAgentConfigSourceForEngine routes Kimi
* isExternalAgentProviderAppType now includes 'kimi'
* externalAgentProviderStore.syncKimiLiveProviders seeds SQLite
from readKimiCliLocalConfig() — one row per [models.<name>]
entry, is_current=1 for the default_model row
Type plumbing:
* renderer/types/cowork.ts and types/electron.d.ts add
kimiCliConfigSource + kimiCliPermissionMode to CoworkConfig
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Complete the Settings → Agent Engine details panel plumbing for CoworkAgentEngine.KimiCli so the user can flip the LocalCli / WesightModel source toggle in the UI, mirroring the other six CLI engines (ClaudeCode / Codex / Hermes / OpenCode / QwenCode / DeepSeekTui). Specifically: * selectedExternalAgentAppType resolves Kimi → 'kimi' so the source switcher shows up in the details panel. * selectedAgentConfigSource reads coworkConfig.kimiCliConfigSource. * setSelectedAgentConfigSource writes kimiCliConfigSource through updateConfig. * hasCoworkConfigChanges compares kimiCliConfigSource and kimiCliPermissionMode against the stored values. * Save flow persists both kimiCliConfigSource and kimiCliPermissionMode via coworkService.updateConfig. * Local state defaults for the two new fields mirror the LocalCli default for fresh installs (issue freestylefly#34). * renderer/types/cowork.ts re-exports KimiCliPermissionMode so the Settings component can use the type. The 'sync global config to ~/.kimi/config.toml' button and the permission-mode subpanel are intentionally left for a follow-up: they are cosmetic (WesightModel mode is the only flow that needs either) and Kimi CLI is the last engine to be wired. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
When the engine switch / config update IPC returns success: false, the renderer only shows the localized 'Failed to switch engine' toast. The actual error from main's catch block was being swallowed. Add console.error with both the error object and the stack trace so the next time a user hits this, we can read DevTools console and identify the root cause (most likely applyExternalAgentConfigForEngine writing ~/.claude/settings.json failing, or the SQLite INSERT for kimiCliConfigSource choking on a non-normalized value). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Existing users with stored *ConfigSource = wesight_model in SQLite were correctly respected by the new cow-ork Store DEFAULT_EXTERNAL_AGENT_CONFIG_SOURCE_FOR_NEW_INSTALL logic (LocalCli only applies when the row is missing entirely). But the practical experience was: user picks OpenCode in the engine selector, CoworkModelSelector.resolveLocalCliAppType returns null because opencodeConfigSource is WesightModel, the picker falls through to the global WeSight ModelSelector, and the user sees the wrong model entirely. Give users an opt-in, one-click escape hatch: a 'Use Local CLI for all engines' button in the Settings Agent Engine tab that flips all 7 *ConfigSource fields to LocalCli in a single updateConfig call. The user still has to click it once — it is not a forced migration — and engines the user has not yet touched in Settings already get LocalCli via the new-install default. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace the previous 'Auto-install for Kimi CLI is not implemented' stub with a real installer that: * Prefers 'uv tool install kimi-cli' when uv is on PATH (fast, modern, uses uv-managed venvs). * Falls back to 'python3 -m pip install --user kimi-cli' with pip bootstrap (works on any macOS 13+ where Python ships with ensurepip). * Verifies the binary lands at ~/.local/bin/kimi (or elsewhere on PATH) and emits a __WESIGHT_BINARY_PATH__ line the install runner parses back into a structured result. * Errors with a clear message + exit 65 if python3 itself is missing. This unblocks the 'Install This Agent' button for Kimi CLI in the Settings → More Agent Engines card. The companion packageName field 'kimi-cli' replaces the empty placeholder; the install_target entry no longer needs the TODO comment. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Kimi CLI's --output-format stream-json is a superset of Claude Code's. The previous normalizer only recognized flat text/content/message/delta fields; tool_use and tool_result events landed in 'none' and never rendered in Cowork. The new normalizer handles: * 'assistant' with message.content blocks: tool_use blocks → tool_use; text blocks → assistant_text (concatenated). * 'user' with message.content blocks: tool_result blocks → tool_result (handles both string and array content). * 'stream_event' envelope: content_block_delta with text_delta → assistant_text. * 'result' with subtype 'success' / failure → assistant_text replace=true (existing behavior) or error. * Falls back to flat text/content/message/delta for non-standard Kimi events. handleKimiCliEvent in externalCliRuntimeAdapter just routes the normalized event through applyKimiNormalizedEvent (no event-shape handling change needed). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Three files had duplicate CliAppType union definitions: * src/main/libs/externalAgentEnvironment.ts:27 * src/renderer/types/cowork.ts:271 * src/renderer/types/electron.d.ts:132 Each had to be kept in lockstep when adding new app types (Kimi was the most recent trigger — see PR freestylefly#35 commit 973ee96). Consolidate to a single canonical definition in src/shared/cowork/constants.ts that all three re-export: * constants.ts: new CliAppType = { Claude, Codex, Hermes, OpenClaw, OpenCode, Grok, Qwen, DeepSeekTui, Kimi } as const * cowork.ts: import the shared type, alias as SharedCliAppType, keep local CliAppType name for back-compat * electron.d.ts: import the shared type, drop the duplicate union * externalAgentEnvironment.ts: same pattern New app types now require touching exactly one file (src/shared/cowork/constants.ts). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ted-engine conditional The button was nested inside renderAgentConfigSourceSettings(), which returns null when no engine is selected — meaning the button was only visible after the user had already picked an engine in Settings. That contradicts the intent of a 'one-click global migration' action that should fire even before the user picks anything. Move the button to the bottom of the Agent Engine tab content (after the engine list), so it always renders regardless of selectedEngine. No other change. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
背景
这是 PR #35(Kimi CLI 引擎) 推上之后的 follow-up 集。在 PR #35 落地的过程中,maintainer 可能希望拆成更小的 review 单元,所以本 PR 单独立项。
包含 4 个 commit,全部在 Kimi CLI 引擎框架内做补强:
10c2f3bc4f97950e8dd074d2d5e7改动细节
1.
10c2f3b真实 pip install kimi-cli之前
buildInstallScript的pip分支是 stub,返 exit 64 + "not implemented" 提示。Settings → More Agent Engines 卡片里的 "Install This Agent" 按钮点了会报错。现在实现真正的安装流程:
uv tool install kimi-cli(如果 PATH 上有 uv)python3 -m pip install --user kimi-cli(用 ensurepip bootstrap,macOS 13+ 自带)~/.local/bin/kimi落位 + 输出__WESIGHT_BINARY_PATH__让 runner 解析回结构化结果INSTALL_TARGETS.kimi条目不再有 TODO 注释,packageName 填kimi-cli。2.
c4f9795Kimi stream-json 完整事件 schemaKimi CLI
--output-format stream-json --include-partial-messages事件 schema 是 Claude Code 的超集。之前的归一化器只认平面text/content/message/delta,tool_use/tool_result/content_block_delta全部落none,导致 Kimi CLI 调用工具时 WeSight 不会渲染。新的
normalizeKimiCliCliEvent处理:assistant消息:扫content: [...]blocks —tool_use→ tool_use,text blocks 拼接 → assistant_textuser消息:content: [...]blocks —tool_result→ tool_result(content 兼容 string / array 两种 shape)stream_event信封:内层content_block_delta+text_delta→ 流式 assistant_textresultsuccess → assistant_text replace=truehandleKimiCliEvent在 adapter 里只负责路由,不再做事件 shape 解析。3.
0e8dd07CliAppType 合并去重仓库里
CliAppType有 3 份重复定义:src/main/libs/externalAgentEnvironment.tssrc/renderer/types/cowork.tssrc/renderer/types/electron.d.ts新增
'kimi'时三处必须同步,否则Record<CliAppType, ...>exhaustive check 报 TS2741——Kimi PR 当时是手工改的,容易漏。合并到
src/shared/cowork/constants.ts单点定义:三处 re-export 共享定义。新增 app type 现在只动 1 个文件。
4.
4d2d5e7修 "Use Local CLI for all engines" 按钮放置之前按钮埋在
renderAgentConfigSourceSettings()里,函数有if (!selectedExternalAgentAppType || !selectedAgentConfigSource) return null;——用户必须先点中一个引擎才能看到这个按钮。这违反"一键全局迁移"的设计意图。挪到 Agent Engine tab 内容最底部(跟引擎列表同级),无论选不选引擎都渲染。功能本身没变。
验证
tsc --noEmit(renderer)→ 0 错tsc --project electron-tsconfig.json --noEmit(main)→ 0 错vitest run→ 12 failed / 33 errors(预存基线,未引入新失败)python3≥ 3.10) + 按钮位置可见Out of scope
--mcp-config-file透传:需要coworkRunner把 MCP server list 透传给 adapter 写 temp JSON,跨层耦合大,放独立 issue。pip install kimi-cli的 Windows / Linux 路径:当前只测了 macOS 实现思路(~/.local/bin/kimi落位)。console.error诊断,没复现到根因。关联