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

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.

Co-authored-by: dev-claudecode <dev-claudecode@cc-connect.local>
This commit is contained in:
cg33anddev-claudecode authored and GitHub committed 2026-08-15 09:14:58 +08:00
1 parent 9fe3ea261e
commit 6c86079800
5 files changed
+124 -6

No files matched your search

+1
View File
@@ -19,6 +19,7 @@
- **Feishu: tighter `@` mention detection in `SendWithStatusFooter` / `buildReplyContent`** — a bare `@` inside an email address, URL, or escaped character no longer false-positives as a mention. Mention detection now checks for the resolved `<at user_id="...">` tag instead of a substring match, so card rendering (and the notation-style status footer) is preserved for content that merely contains `@`. Real `@mentions` still force `MsgTypeText` so Feishu fires the mention event (#1322).
- **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.
- **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.
+10 -4
View File
@@ -18,10 +18,15 @@ import (
//
// The newer Kimi Code CLI removed the standalone `--print` flag — passing it
// now produces: `error: unknown option '--print' (Did you mean --prompt?)`.
// The older kimi-cli still requires `--print` for `--output-format` to take
// effect. We probe the help text once and adapt accordingly. See #1456.
// The same CLI release also dropped `--work-dir` (the working directory is
// now taken from the process cwd only) and rejects the flag with `error:
// unknown option --work-dir`. The older kimi-cli still requires `--print`
// for `--output-format` to take effect and exposes `--work-dir` for
// non-default workspace locations. We probe the help text once and adapt
// accordingly. See #1456, #1476.
type kimiFlagSupport struct {
Print bool
Print bool
WorkDir bool
}
// probeKimiFlags runs `<cmd> --help` with a short timeout and returns the
@@ -49,7 +54,8 @@ func probeKimiFlags(parent context.Context, cmd string, timeout time.Duration) k
flags := parseKimiHelpFlags(out.String())
support := kimiFlagSupport{
Print: flags["--print"],
Print: flags["--print"],
WorkDir: flags["--work-dir"],
}
slog.Debug("kimi: flag probe complete", "cmd", cmd, "support", support)
return support
+70 -1
View File
@@ -33,7 +33,11 @@ const legacyKimiHelp = `
`
// modernKimiHelp emulates the newer Kimi Code CLI, where --print has been
// removed entirely. This is the surface that triggers #1456 today.
// removed entirely. This is the surface that triggers #1456 today. As of the
// build tested in #1476, the CLI also still advertises --work-dir here (we
// keep this constant realistic so #1456 tests stay anchored to a known CLI
// surface); see modernKimiHelpWithoutWorkDir below for the deeper-no-work-dir
// surface that triggers #1476.
const modernKimiHelp = `
Usage: kimi [OPTIONS] COMMAND [ARGS]...
@@ -51,9 +55,30 @@ const modernKimiHelp = `
--plan Start a new session in Plan mode.
`
// modernKimiHelpWithoutWorkDir models the Kimi Code CLI build the #1476
// reporter hits: --print is gone, --work-dir is gone too. The CLI now takes
// the working directory from the process cwd, so passing --work-dir must be
// avoided entirely (it triggers `error: unknown option --work-dir`).
const modernKimiHelpWithoutWorkDir = `
Usage: kimi [OPTIONS] COMMAND [ARGS]...
Kimi, your next CLI agent.
Options:
-V, --version Show version and exit.
-S, --session [ID] Resume a session.
-c, --continue Continue the most recent session.
-m, --model TEXT LLM model to use.
-p, --prompt TEXT Run a single prompt non-interactively.
--output-format FORMAT Non-interactive output format.
-y, --yolo Auto-approve regular tool calls.
--plan Start a new session in Plan mode.
`
func TestParseKimiHelpFlags_LegacyAdvertisesPrint(t *testing.T) {
flags := parseKimiHelpFlags(legacyKimiHelp)
assert.True(t, flags["--print"], "legacy help text advertises --print")
assert.True(t, flags["--work-dir"], "legacy help text advertises --work-dir (#1476)")
assert.True(t, flags["--prompt"], "legacy help text advertises --prompt")
assert.True(t, flags["--output-format"])
assert.True(t, flags["--plan"])
@@ -66,10 +91,26 @@ func TestParseKimiHelpFlags_ModernHidesPrint(t *testing.T) {
// Regression for #1456: the new Kimi Code CLI must not be detected
// as supporting --print.
assert.False(t, flags["--print"], "modern help text must not advertise --print")
assert.True(t, flags["--work-dir"], "this modern CLI build still advertises --work-dir")
assert.True(t, flags["--prompt"], "modern CLI still advertises --prompt")
assert.True(t, flags["--output-format"])
}
// TestParseKimiHelpFlags_ModernWithoutWorkDir is the regression test for
// #1476: when the installed Kimi Code CLI build no longer advertises
// --work-dir, parseKimiHelpFlags must NOT detect it, so the probe can drive
// buildArgs to omit --work-dir entirely (the CLI then takes the cwd from
// exec.Command.Dir, which the Go process already sets correctly).
func TestParseKimiHelpFlags_ModernWithoutWorkDir(t *testing.T) {
flags := parseKimiHelpFlags(modernKimiHelpWithoutWorkDir)
assert.False(t, flags["--work-dir"],
"modern CLI without --work-dir must NOT advertise it (#1476)")
assert.False(t, flags["--print"],
"modern CLI still without --print (#1456 compatibility)")
assert.True(t, flags["--prompt"], "still advertises --prompt")
assert.True(t, flags["--output-format"])
}
func TestParseKimiHelpFlags_IgnoresPositionalAndShortOnly(t *testing.T) {
help := `
Arguments:
@@ -94,3 +135,31 @@ func TestProbeKimiFlags_FallbackOnMissingBinary(t *testing.T) {
200*time.Millisecond)
assert.Equal(t, kimiFlagSupport{}, got)
}
// TestKimiFlagSupport_LegacyHelpSetsWorkDir verifies the full probe-mapping
// for the legacy help surface: both Print and WorkDir must come back true so
// existing kimi-cli users keep receiving --work-dir.
func TestKimiFlagSupport_LegacyHelpSetsWorkDir(t *testing.T) {
flags := parseKimiHelpFlags(legacyKimiHelp)
assert.True(t, flags["--work-dir"], "legacy help advertises --work-dir")
support := kimiFlagSupport{
Print: flags["--print"],
WorkDir: flags["--work-dir"],
}
assert.True(t, support.Print)
assert.True(t, support.WorkDir, "modern probe must surface work-dir support")
}
// TestKimiFlagSupport_ModernWithoutWorkDir verifies the full probe-mapping
// for the #1476 surface: WorkDir must come back false so buildArgs can drop
// the flag.
func TestKimiFlagSupport_ModernWithoutWorkDir(t *testing.T) {
flags := parseKimiHelpFlags(modernKimiHelpWithoutWorkDir)
support := kimiFlagSupport{
Print: flags["--print"],
WorkDir: flags["--work-dir"],
}
assert.False(t, support.Print)
assert.False(t, support.WorkDir,
"probe must NOT surface work-dir support on CLI builds that dropped it (#1476)")
}
+1 -1
View File
@@ -94,7 +94,7 @@ func (ks *kimiSession) buildArgs(prompt string) []string {
if ks.model != "" {
args = append(args, "--model", ks.model)
}
if ks.workDir != "" {
if ks.workDir != "" && ks.flagSupport.WorkDir {
args = append(args, "--work-dir", ks.workDir)
}
+42
View File
@@ -199,6 +199,48 @@ func TestBuildArgs_PrintSupportIncludesPrintFlag(t *testing.T) {
assert.Contains(t, args, "stream-json")
}
// TestBuildArgs_WorkDirFlagGated is the regression test for #1476. When the
// locally installed Kimi Code CLI does not advertise --work-dir in its help
// output, cc-connect must omit that flag — otherwise the CLI exits with
// `error: unknown option --work-dir`. The agent still runs in the correct
// directory because exec.Command.Dir is set separately (see session.go).
func TestBuildArgs_WorkDirFlagGated(t *testing.T) {
ctx := context.Background()
ks, err := newKimiSession(ctx, "kimi", nil, "/", "", "default", "", nil, 0, kimiFlagSupport{WorkDir: false})
require.NoError(t, err)
defer func() { _ = ks.Close() }()
args := ks.buildArgs("hello")
for _, a := range args {
if a == "--work-dir" {
t.Fatalf("buildArgs unexpectedly emitted --work-dir when flagSupport.WorkDir=false; args=%v", args)
}
}
// The work-dir value must also NOT leak into args even though we asked
// for one. (Confirms the gate is at the flag level, not just dropping
// the flag-name while keeping the value.)
for _, a := range args {
if a == "/" {
t.Fatalf("buildArgs leaked workDir value %q into args; args=%v", a, args)
}
}
}
// TestBuildArgs_WorkDirFlagEmitted covers the legacy kimi-cli branch — the
// binary advertises --work-dir, so we must keep emitting it for non-default
// workspace locations.
func TestBuildArgs_WorkDirFlagEmitted(t *testing.T) {
ctx := context.Background()
ks, err := newKimiSession(ctx, "kimi", nil, "/", "", "default", "", nil, 0, kimiFlagSupport{WorkDir: true})
require.NoError(t, err)
defer func() { _ = ks.Close() }()
args := ks.buildArgs("hello")
assert.Contains(t, args, "--work-dir")
assert.Contains(t, args, "/")
}
// TestBuildArgs_PlanMode confirms plan mode still passes --plan independent
// of --print support.
func TestBuildArgs_PlanMode(t *testing.T) {