feat(kimi): native Kimi Code CLI dialect support (Fixes #1561) (#1564)

* fix(kimi): gate --work-dir flag on probe, like --print (#1476)

Same pattern as the #1456 / PR #1461 --print fix. Newer Kimi Code CLI
builds no longer accept --work-dir, exiting with `error: unknown option
--work-dir` whenever the user's config sets a non-default workspace
directory.

- agent/kimi/probe.go: kimiFlagSupport gains a WorkDir bool; the probe
  fills it from `kimi --help` (parseKimiHelpFlags already scans the
  --work-dir token, no parser changes needed).
- agent/kimi/session.go buildArgs: --work-dir is now emitted only when
  flagSupport.WorkDir is true. The agent still runs in the correct
  directory via exec.Command.Dir (set separately), so omitting the flag
  on modern CLIs is functionally equivalent for users on default dirs
  and graceful for users on non-default dirs whose CLI just ignores it.
- agent/kimi/probe_test.go: extend the legacy test to assert
  --work-dir is detected, add modernKimiHelpWithoutWorkDir constant
  that mirrors the build the reporter hits, plus full
  TestKimiFlagSupport_LegacyHelpSetsWorkDir /
  TestKimiFlagSupport_ModernWithoutWorkDir coverage of the probe mapping.
- agent/kimi/session_test.go: TestBuildArgs_WorkDirFlagGated (no flag
  on modern CLI) + TestBuildArgs_WorkDirFlagEmitted (legacy CLI keeps
  it). Also asserts the work-dir value doesn't leak into args when
  the gate is closed, catching a partial-gate future bug.

go test ./agent/kimi/... — all pass.

* feat(kimi): native Kimi Code CLI dialect support

Fixes #1561

The kimi agent targeted the legacy Python kimi-cli dialect; the newer
Node.js Kimi Code CLI (kimi-code) speaks a different one. Extend the
#1461/#1483 probe-gating approach to cover the remaining differences:

- probe: also detect --quiet; add isModernFlavor() using --print absence
  as the family discriminator established in #1456
- buildArgs: resume with -r instead of --resume on the modern dialect
  (--resume is rejected; -r matches the CLI's own resume hint); gate
  --quiet and emulate quiet mode via local event suppression when the
  binary dropped it; never pass --yolo/--auto (bare --prompt already
  auto-approves, and Kimi Code rejects combining them)
- stream-json: accept plain-string content for assistant/tool messages
  alongside the legacy block-array shape (format-tolerant, no gating)
- session continuity: capture the session id from Kimi Code's stdout
  meta line {"role":"meta","type":"session.resume_hint"} instead of the
  legacy plain-text/stderr hint
- session listing: scan both ~/.kimi/sessions and ~/.kimi-code/sessions,
  understand the Kimi Code state.json schema, honor its workDir field

Tests: real v0.26.0 --help fixture, per-dialect arg tests, content-shape
and meta-hint regression tests, dual-flavor session listing test, and an
env-guarded live e2e (KIMI_LIVE_E2E=1) that verifies anchor recall across
a resumed turn against the production binary.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(kimi): don't report 0 messages for Kimi Code sessions in /list

Kimi Code CLI sessions have no context.jsonl; their transcript lives at
agents/main/wire.jsonl. Without a fallback, /list showed 0 messages for
every modern session. Count user turns from wire.jsonl (and use the first
user turn as the summary), matching legacy kimi-cli behavior.

Addresses review feedback on #1564.

* fix(kimi): adapt live e2e to main's Send(messageID, ...) signature

CI lint failed on the PR merge ref: main added a messageID string param to
AgentSession.Send (core/interfaces.go), but the PR still called the old 3-arg
form in live_e2e_test.go, so the merged code failed to compile
('not enough arguments in call to s.Send').

origin/main was merged into kimi-code-flavor (no conflicts); kimi's Send now
carries the 4-arg signature matching main. This updates the remaining 3-arg
call site to the new signature. agent/kimi and core compile, vet and tests green.

* fix(kimi): check f.Close error in transcript helpers (errcheck)

golangci-lint flagged the deferred file Close calls in the new
parseKimiTranscript/count* helpers as unchecked errors. Use an explicit
blank-assignment close to satisfy errcheck. agent/kimi still compiles,
vets and tests green.

* feat(kimi): native Kimi Code CLI dialect support

Fixes #1561

The kimi agent targeted the legacy Python kimi-cli dialect; the newer
Node.js Kimi Code CLI (kimi-code) speaks a different one. Extend the
#1461/#1483 probe-gating approach to cover the remaining differences:

- probe: also detect --quiet; add isModernFlavor() using --print absence
  as the family discriminator established in #1456
- buildArgs: resume with -r instead of --resume on the modern dialect
  (--resume is rejected; -r matches the CLI's own resume hint); gate
  --quiet and emulate quiet mode via local event suppression when the
  binary dropped it; never pass --yolo/--auto (bare --prompt already
  auto-approves, and Kimi Code rejects combining them)
- stream-json: accept plain-string content for assistant/tool messages
  alongside the legacy block-array shape (format-tolerant, no gating)
- session continuity: capture the session id from Kimi Code's stdout
  meta line {"role":"meta","type":"session.resume_hint"} instead of the
  legacy plain-text/stderr hint
- session listing: scan both ~/.kimi/sessions and ~/.kimi-code/sessions,
  understand the Kimi Code state.json schema, honor its workDir field

Tests: real v0.26.0 --help fixture, per-dialect arg tests, content-shape
and meta-hint regression tests, dual-flavor session listing test, and an
env-guarded live e2e (KIMI_LIVE_E2E=1) that verifies anchor recall across
a resumed turn against the production binary.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(kimi): don't report 0 messages for Kimi Code sessions in /list

Kimi Code CLI sessions have no context.jsonl; their transcript lives at
agents/main/wire.jsonl. Without a fallback, /list showed 0 messages for
every modern session. Count user turns from wire.jsonl (and use the first
user turn as the summary), matching legacy kimi-cli behavior.

Addresses review feedback on #1564.

* fix(kimi): adapt live e2e to main's Send(messageID, ...) signature

CI lint failed on the PR merge ref: main added a messageID string param to
AgentSession.Send (core/interfaces.go), but the PR still called the old 3-arg
form in live_e2e_test.go, so the merged code failed to compile
('not enough arguments in call to s.Send').

origin/main was merged into kimi-code-flavor (no conflicts); kimi's Send now
carries the 4-arg signature matching main. This updates the remaining 3-arg
call site to the new signature. agent/kimi and core compile, vet and tests green.

* fix(kimi): check f.Close error in transcript helpers (errcheck)

golangci-lint flagged the deferred file Close calls in the new
parseKimiTranscript/count* helpers as unchecked errors. Use an explicit
blank-assignment close to satisfy errcheck. agent/kimi still compiles,
vets and tests green.

* test(kimi): pin assistant string-content + tool_calls flush order (#1561)

Ported from #1586: an assistant event carrying plain-string content AND
tool_calls must surface the text as a thinking event (via
flushPendingAsThinking) before the tool-use event, not drop it silently.

* docs(changelog): add #1561 kimi-code dialect entry

---------

Co-authored-by: dev-claudecode <dev-claudecode@cc-connect.local>
Co-authored-by: whyihaveyou <whyihaveyou@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: QZP <qzp@QZPdeMacBook-Air.local>
Co-authored-by: octopus <2841776039@qq.com>
This commit is contained in:
authored and GitHub committed 2026-08-16 15:29:04 +08:00
1 parent 6c86079800
commit 1ddbac047c
8 files changed
+797 -122

No files matched your search

+2 -1
View File
@@ -1,4 +1,4 @@
# Changelog
# Changelog
## Unreleased
@@ -20,6 +20,7 @@
- **feishu**: coalesce consecutive image messages from the same session into a single multi-image dispatch to fix first-image drop on batch sends (#1395). When the Feishu mobile client sends N images in quick succession, each image arrives as a separate `image` event with very close `create_time` values. Dispatching each immediately caused core/engine's `create_time` watermark (PR #1168) to drop the oldest image, so the agent only saw N-1 images. A per-session image buffer with a 150ms quiet window now merges the burst into one `core.Message` carrying all images, in send order. Single-image sends and quoted-image replies are unaffected.
- **claudecode**: fix per-spawn system-prompt temp file EACCES under `run_as_user` (#1429). The per-spawn temp file written by `writeTempAppendPromptFile` (the 1% edge-case path used when the prompt has session-specific platform formatting or user `append_system_prompt`) inherited `os.CreateTemp`'s 0600 mode and was owned by the cc-connect process user (often root under systemd). When the agent was spawned under a different `run_as_user`, it could not read the file and exited before any prompt was loaded. The file is now `chmod 0o644` immediately after write, matching the shared `ensureSharedSystemPromptFile` path. Prompt content is non-secret (a superset of the already-shared base prompt), so 0644 is consistent with the shared file. Does not affect the shared-file path (already 0644 since #1376) or the daemon-mode path resolution (#1419).
- **kimi**: gate `--work-dir` flag on `kimiFlagSupport.WorkDir` so the Kimi Code CLI build that dropped the flag no longer exits with `error: unknown option --work-dir` (#1476). Same probe-based pattern as the `--print` fix in #1456 / PR #1461: the agent probes `kimi --help` once at construction, then `buildArgs` emits the flag only when the installed CLI advertises it. The agent still runs in the correct directory because `exec.Command.Dir` is set separately, so legacy kimi-cli users (who still have `--work-dir`) keep non-default workspace support while modern-CLI users get clean startup.
- **kimi**: native Kimi Code CLI (Node.js `kimi-code`) dialect support (#1561). The newer CLI removed `--quiet` and `--resume`, moved the session resume hint from a plain-text line to a stdout JSON meta event (`{"role":"meta","type":"session.resume_hint",...}`), and emits assistant/tool `content` as a plain string instead of typed blocks — causing `error: unknown option`, silently dropped replies, and broken multi-turn continuity. The probe now also gates `--quiet` (emulated locally by suppressing thinking/tool events when unsupported), resume uses `-r <id>` on the modern dialect (the command the CLI's own hint prints), the meta resume-hint event restores the session ID, and the stream parser accepts both content shapes regardless of probed flavor. Session listing additionally scans `~/.kimi-code/sessions` with its `state.json` schema (`title`/`workDir`, honoring the workDir filter) and counts messages from `agents/main/wire.jsonl` so `/list` and `/delete` work for both CLI flavors. Legacy kimi-cli behavior is unchanged.
- **core**: queue post-restart notification and dispatch on platform ready (#1383). Previously `/restart` sent the success notification immediately after engine startup, racing the platform's async connect window (Telegram: ~2.6s). On a not-yet-ready platform the send was silently dropped at debug log level. The notify is now queued on the engine and dispatched when the target platform reaches `OnPlatformReady`, with bounded retry (3 attempts, 0/500/1500 ms backoff) on transient send failure. Failed sends log at warn level. A 10s safety timeout drops the notify with a warning if the target platform never reaches ready, so startup is never blocked indefinitely. Also covers Discord / Weixin / Matrix (other AsyncRecoverablePlatform implementations) for free.
- **core**: `SaveFilesToDisk` / `AppendFileRefs` always emit absolute paths (#1459). When a user configured a relative `work_dir` (e.g. `~/project` or `.cc-connect`), `SaveFilesToDisk` joined relative paths into the attachments directory and the resulting paths were passed verbatim into the agent's prompt. The spawned agent process — typically run from a different cwd by the platform adapter — could not resolve them and silently dropped every attachment. `SaveFilesToDisk` now calls `filepath.Abs(workDir)` up front and falls back to the raw value on error, and `AppendFileRefs` defensively absolutizes each entry. Both behaviors are covered by new tests for relative, absolute, and empty workDir; the empty-workDir case falls back to the process cwd so misconfigured deploys still get a writable attachments directory.