kimi-plugin-cc 2.5.1 → 2.5.3

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/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +15 -1
  4. package/README.md +62 -4
  5. package/dist/cli-client.js +8 -0
  6. package/dist/commands/setup.js +78 -51
  7. package/dist/commands/swarm.js +2 -2
  8. package/dist/dsh/companion.js +9 -6
  9. package/dist/dsh/index.js +15 -2
  10. package/dist/hooks/config-safety.js +5 -2
  11. package/dist/hooks/managed-block.js +86 -20
  12. package/dist/kimi-command.js +1 -0
  13. package/dist/kimi-engine.js +3 -0
  14. package/dist/native-v2-preflight.js +138 -30
  15. package/dist/swarm-activation.js +276 -14
  16. package/dist/version.js +1 -1
  17. package/docs/migration.md +8 -6
  18. package/docs/registry-releases.md +66 -3
  19. package/docs/safety.md +14 -10
  20. package/docs/swarm-recovery.md +2 -2
  21. package/docs/upstream-2.5.0-certification.md +48 -0
  22. package/docs/upstream-2.5.1-certification.md +43 -0
  23. package/package.json +40 -5
  24. package/plugins/kimi-codex/.codex-plugin/plugin.json +1 -1
  25. package/plugins/kimi-codex/dist/cli-client.js +8 -0
  26. package/plugins/kimi-codex/dist/commands/setup.js +78 -51
  27. package/plugins/kimi-codex/dist/commands/swarm.js +2 -2
  28. package/plugins/kimi-codex/dist/dsh/companion.js +9 -6
  29. package/plugins/kimi-codex/dist/dsh/index.js +15 -2
  30. package/plugins/kimi-codex/dist/hooks/config-safety.js +5 -2
  31. package/plugins/kimi-codex/dist/hooks/managed-block.js +86 -20
  32. package/plugins/kimi-codex/dist/kimi-command.js +1 -0
  33. package/plugins/kimi-codex/dist/kimi-engine.js +3 -0
  34. package/plugins/kimi-codex/dist/native-v2-preflight.js +138 -30
  35. package/plugins/kimi-codex/dist/swarm-activation.js +276 -14
  36. package/plugins/kimi-codex/dist/version.js +1 -1
  37. package/plugins/kimi-dsh/README.md +4 -1
  38. package/plugins/kimi-dsh/dist/cli-client.js +8 -0
  39. package/plugins/kimi-dsh/dist/commands/setup.js +78 -51
  40. package/plugins/kimi-dsh/dist/commands/swarm.js +2 -2
  41. package/plugins/kimi-dsh/dist/dsh/companion.js +9 -6
  42. package/plugins/kimi-dsh/dist/dsh/index.js +15 -2
  43. package/plugins/kimi-dsh/dist/hooks/config-safety.js +5 -2
  44. package/plugins/kimi-dsh/dist/hooks/managed-block.js +86 -20
  45. package/plugins/kimi-dsh/dist/kimi-command.js +1 -0
  46. package/plugins/kimi-dsh/dist/kimi-engine.js +3 -0
  47. package/plugins/kimi-dsh/dist/native-v2-preflight.js +138 -30
  48. package/plugins/kimi-dsh/dist/swarm-activation.js +276 -14
  49. package/plugins/kimi-dsh/dist/version.js +1 -1
  50. package/plugins/kimi-dsh/package.json +33 -3
@@ -1,4 +1,5 @@
1
1
  import { spawn } from "node:child_process";
2
+ import { randomUUID } from "node:crypto";
2
3
  import { constants as fsConstants } from "node:fs";
3
4
  import { open } from "node:fs/promises";
4
5
  import path from "node:path";
@@ -9,7 +10,7 @@ import { assertExecutionPlanMatchesSpawn, assertNoUnsafeExperimentalSelector, }
9
10
  import { resolveKimiHome } from "./kimi-home.js";
10
11
  import { assertNativeV2Preflight } from "./native-v2-preflight.js";
11
12
  import { collectDescendantIdentities, collectProcessGroupsIdentities, probeProcessIdentity, revalidateProcessIdentities, waitForProcessTreeExit, } from "./process-tree.js";
12
- const PREACTIVATION_VERSIONS = ["2.3.2", "2.3.3", "2.4.0", "2.4.1"];
13
+ const PREACTIVATION_VERSIONS = ["2.3.2", "2.3.3", "2.4.0", "2.4.1", "2.5.0", "2.5.1"];
13
14
  const READY_TIMEOUT_MS = 10_000;
14
15
  const REST_TIMEOUT_MS = 5_000;
15
16
  // Exact 2.4.0 can take longer than 5s to drain session/workspace lifecycle work.
@@ -18,6 +19,8 @@ const JOURNAL_TIMEOUT_MS = 2_000;
18
19
  const JOURNAL_POLL_MS = 50;
19
20
  const READY_LINE_MAX_CHARS = 65_536;
20
21
  const REST_RESPONSE_MAX_BYTES = 1024 * 1024;
22
+ const WEBSOCKET_MAX_FRAMES = 64;
23
+ const WEBSOCKET_RESPONSE_MAX_CHARS = 1024 * 1024;
21
24
  const JOURNAL_MAX_BYTES = 64 * 1024 * 1024;
22
25
  const DESCENDANT_SNAPSHOT_TIMEOUT_MS = 2_000;
23
26
  const DESCENDANT_TRACK_INTERVAL_MS = 50;
@@ -39,11 +42,12 @@ export async function preactivateSwarmSession(options) {
39
42
  web = await startWebChild(options, prepared.env, failureCode);
40
43
  await verifyMeta(web, prepared.expectedVersion, options.signal, failureCode);
41
44
  const model = await resolveProfileModel(web, options, failureCode);
42
- identity = await createSession(web, options, failureCode);
45
+ identity = await createSession(web, options, prepared.expectedVersion, failureCode);
46
+ await verifyLocalEnvironment(web, identity, options.cwd, prepared.expectedVersion, options.signal, failureCode);
43
47
  options.onRecoveryEvent?.({ kind: "session", ...identity });
44
48
  profileMutationAttempted = true;
45
49
  await postSwarmProfile(web, identity, true, model, options.signal, failureCode);
46
- await verifySwarmStatus(web, identity, true, model, options.signal, failureCode);
50
+ await verifySwarmStatus(web, identity, true, model, prepared.expectedVersion, options.signal, failureCode);
47
51
  await shutdownWeb(web, options.signal, failureCode);
48
52
  await requireSwarmJournal(prepared.kimiHome, identity, "enter", options.signal, failureCode);
49
53
  throwIfAborted(options.signal, failureCode, "swarm-activation.complete");
@@ -86,8 +90,9 @@ export async function deactivateSwarmSession(options) {
86
90
  throwIfAborted(options.signal, failureCode, "swarm-deactivation.pre-spawn");
87
91
  web = await startWebChild(options, prepared.env, failureCode);
88
92
  await verifyMeta(web, prepared.expectedVersion, options.signal, failureCode);
93
+ await verifyLocalEnvironment(web, options, options.cwd, prepared.expectedVersion, options.signal, failureCode);
89
94
  await postSwarmProfile(web, options, false, undefined, options.signal, failureCode);
90
- await verifySwarmStatus(web, options, false, undefined, options.signal, failureCode);
95
+ await verifySwarmStatus(web, options, false, undefined, prepared.expectedVersion, options.signal, failureCode);
91
96
  await shutdownWeb(web, options.signal, failureCode);
92
97
  await requireSwarmJournal(prepared.kimiHome, options, "exit", options.signal, failureCode);
93
98
  options.onRecoveryEvent?.({ kind: "disabled" });
@@ -124,6 +129,7 @@ async function prepareWebSpawn(options, failureCode) {
124
129
  await assertNativeV2Preflight({
125
130
  kimiHome,
126
131
  env: options.env,
132
+ expectedExecutionRoot: options.cwd,
127
133
  stage: "swarm-activation.pre-spawn",
128
134
  });
129
135
  }
@@ -329,13 +335,29 @@ async function resolveProfileModel(web, options, failureCode) {
329
335
  }
330
336
  return data.default_model;
331
337
  }
332
- async function createSession(web, options, failureCode) {
333
- const data = await restData(web, "POST", "/api/v1/sessions", { metadata: { cwd: options.cwd } }, options.signal, failureCode);
338
+ async function createSession(web, options, expectedVersion, failureCode) {
339
+ const request = { metadata: { cwd: options.cwd } };
340
+ if (["2.5.0", "2.5.1"].includes(expectedVersion)) {
341
+ request.environment_id = "local";
342
+ request.environment_cwd = options.cwd;
343
+ }
344
+ const data = await restData(web, "POST", "/api/v1/sessions", request, options.signal, failureCode);
334
345
  if (!isRecord(data) || !validPathId(data.id) || !validPathId(data.workspace_id)) {
335
346
  throw failure(failureCode, "swarm-activation.create-session", "Kimi web returned an invalid session identity.");
336
347
  }
337
348
  return { sessionId: data.id, workspaceId: data.workspace_id };
338
349
  }
350
+ async function verifyLocalEnvironment(web, identity, expectedCwd, expectedVersion, signal, failureCode) {
351
+ if (!["2.5.0", "2.5.1"].includes(expectedVersion))
352
+ return;
353
+ const data = await restData(web, "GET", `/api/v1/sessions/${encodeURIComponent(identity.sessionId)}/environment`, undefined, signal, failureCode);
354
+ if (!isRecord(data)) {
355
+ throw failure(failureCode, "swarm-activation.environment", "Kimi web /environment returned an invalid payload.");
356
+ }
357
+ if (data.workspace_id !== identity.workspaceId || data.environment_id !== "local" || data.cwd !== expectedCwd) {
358
+ throw failure(failureCode, "swarm-activation.environment", "Kimi web /environment did not confirm the expected local workspace binding.");
359
+ }
360
+ }
339
361
  async function postSwarmProfile(web, identity, active, model, signal, failureCode) {
340
362
  const agentConfig = {
341
363
  swarm_mode: active,
@@ -345,24 +367,264 @@ async function postSwarmProfile(web, identity, active, model, signal, failureCod
345
367
  agentConfig.model = model;
346
368
  await restData(web, "POST", `/api/v1/sessions/${encodeURIComponent(identity.sessionId)}/profile`, { agent_config: agentConfig }, signal, failureCode);
347
369
  }
348
- async function verifySwarmStatus(web, identity, active, model, signal, failureCode) {
349
- const data = await restData(web, "GET", `/api/v1/sessions/${encodeURIComponent(identity.sessionId)}/status`, undefined, signal, failureCode);
370
+ async function verifySwarmStatus(web, identity, active, model, expectedVersion, signal, failureCode) {
371
+ const data = ["2.5.0", "2.5.1"].includes(expectedVersion)
372
+ ? await readWebSocketAgentState(web, identity.sessionId, signal, failureCode)
373
+ : await restData(web, "GET", `/api/v1/sessions/${encodeURIComponent(identity.sessionId)}/status`, undefined, signal, failureCode);
374
+ const source = ["2.5.0", "2.5.1"].includes(expectedVersion) ? "agent state" : "/status";
350
375
  if (!isRecord(data)) {
351
- throw failure(failureCode, "swarm-activation.status", "Kimi web /status returned an invalid payload.");
376
+ throw failure(failureCode, "swarm-activation.status", `Kimi web ${source} returned an invalid payload.`);
352
377
  }
353
378
  if (data.swarm_mode !== active) {
354
- throw failure(failureCode, "swarm-activation.status", `Kimi web /status did not confirm swarm_mode=${active}.`, { expected_swarm_mode: active });
379
+ throw failure(failureCode, "swarm-activation.status", `Kimi web ${source} did not confirm swarm_mode=${active}.`, { expected_swarm_mode: active });
355
380
  }
356
381
  if (data.plan_mode !== false) {
357
- throw failure(failureCode, "swarm-activation.status", "Kimi web /status did not confirm plan mode is off.");
382
+ throw failure(failureCode, "swarm-activation.status", `Kimi web ${source} did not confirm plan mode is off.`);
358
383
  }
359
- if (data.permission !== "auto") {
360
- throw failure(failureCode, "swarm-activation.status", "Kimi web /status did not confirm auto permission mode.");
384
+ if (["2.5.0", "2.5.1"].includes(expectedVersion) && data.tower_mode !== false) {
385
+ throw failure(failureCode, "swarm-activation.status", "Kimi web agent state did not confirm tower mode is off.");
386
+ }
387
+ const permission = ["2.5.0", "2.5.1"].includes(expectedVersion) ? data.permission_mode : data.permission;
388
+ if (permission !== "auto") {
389
+ throw failure(failureCode, "swarm-activation.status", `Kimi web ${source} did not confirm auto permission mode.`);
361
390
  }
362
391
  if (active && data.model !== model) {
363
- throw failure(failureCode, "swarm-activation.status", "Kimi web /status did not confirm the selected model alias.");
392
+ throw failure(failureCode, "swarm-activation.status", `Kimi web ${source} did not confirm the selected model alias.`);
364
393
  }
365
394
  }
395
+ async function readWebSocketAgentState(web, sessionId, signal, failureCode) {
396
+ throwIfAborted(signal, failureCode, "swarm-activation.status");
397
+ await refreshOwnedDescendants(web);
398
+ throwIfAborted(signal, failureCode, "swarm-activation.status");
399
+ try {
400
+ return await new Promise((resolve, reject) => {
401
+ let socket;
402
+ const bearerProtocol = `kimi-code.bearer.${web.token}`;
403
+ const helloId = `swarm-activation-hello-${randomUUID()}`;
404
+ const subscribeId = `swarm-activation-subscribe-${randomUUID()}`;
405
+ try {
406
+ const url = new URL(web.origin);
407
+ url.protocol = "ws:";
408
+ url.pathname = "/api/v1/ws";
409
+ socket = new WebSocket(url, bearerProtocol);
410
+ }
411
+ catch {
412
+ reject(failure(failureCode, "swarm-activation.status", "Could not open the authenticated Kimi web agent-state channel."));
413
+ return;
414
+ }
415
+ let settled = false;
416
+ let serverHello = false;
417
+ let clientHelloAcknowledged = false;
418
+ let subscribeSent = false;
419
+ let subscriptionAcknowledged = false;
420
+ let closeRequested = false;
421
+ let latestState;
422
+ let frames = 0;
423
+ let receivedChars = 0;
424
+ const timer = setTimeout(() => {
425
+ settle(failure(failureCode, "swarm-activation.status", "Kimi web agent-state verification timed out.", { timeout_ms: REST_TIMEOUT_MS }));
426
+ }, REST_TIMEOUT_MS);
427
+ const cleanup = () => {
428
+ clearTimeout(timer);
429
+ signal?.removeEventListener("abort", onAbort);
430
+ socket.removeEventListener("open", onOpen);
431
+ socket.removeEventListener("message", onMessage);
432
+ socket.removeEventListener("error", onError);
433
+ socket.removeEventListener("close", onClose);
434
+ try {
435
+ socket.close();
436
+ }
437
+ catch {
438
+ }
439
+ };
440
+ const settle = (result) => {
441
+ if (settled)
442
+ return;
443
+ settled = true;
444
+ cleanup();
445
+ if (result instanceof RuntimeError)
446
+ reject(result);
447
+ else
448
+ resolve(result);
449
+ };
450
+ const rejectState = (message) => {
451
+ settle(failure(failureCode, "swarm-activation.status", message));
452
+ };
453
+ const send = (frame) => {
454
+ try {
455
+ socket.send(JSON.stringify(frame));
456
+ return true;
457
+ }
458
+ catch {
459
+ rejectState("Could not send a Kimi web agent-state control frame.");
460
+ return false;
461
+ }
462
+ };
463
+ const completeIfReady = () => {
464
+ if (serverHello &&
465
+ clientHelloAcknowledged &&
466
+ subscriptionAcknowledged &&
467
+ latestState !== undefined &&
468
+ !closeRequested) {
469
+ closeRequested = true;
470
+ try {
471
+ socket.close();
472
+ }
473
+ catch {
474
+ rejectState("Could not close the Kimi web agent-state channel for final validation.");
475
+ }
476
+ }
477
+ };
478
+ const onOpen = () => {
479
+ if (socket.protocol !== bearerProtocol) {
480
+ rejectState("Kimi web did not authenticate the agent-state channel with the expected subprotocol.");
481
+ return;
482
+ }
483
+ send({
484
+ type: "client_hello",
485
+ id: helloId,
486
+ payload: { client_id: "kimi-plugin-cc-swarm-activation" },
487
+ });
488
+ };
489
+ const onMessage = (event) => {
490
+ if (settled)
491
+ return;
492
+ if (typeof event.data !== "string") {
493
+ rejectState("Kimi web agent-state channel returned a non-text frame.");
494
+ return;
495
+ }
496
+ frames += 1;
497
+ receivedChars += event.data.length;
498
+ if (frames > WEBSOCKET_MAX_FRAMES || receivedChars > WEBSOCKET_RESPONSE_MAX_CHARS) {
499
+ rejectState("Kimi web agent-state channel exceeded the bounded response limit.");
500
+ return;
501
+ }
502
+ let frame;
503
+ try {
504
+ frame = JSON.parse(event.data);
505
+ }
506
+ catch {
507
+ rejectState("Kimi web agent-state channel returned malformed JSON.");
508
+ return;
509
+ }
510
+ if (!isRecord(frame) || typeof frame.type !== "string") {
511
+ rejectState("Kimi web agent-state channel returned an invalid frame.");
512
+ return;
513
+ }
514
+ if (frame.type === "server_hello") {
515
+ if (serverHello || !isRecord(frame.payload) ||
516
+ frame.payload.protocol_version !== 2 ||
517
+ typeof frame.payload.ws_connection_id !== "string" ||
518
+ !isRecord(frame.payload.capabilities) ||
519
+ frame.payload.capabilities.event_batching !== false ||
520
+ frame.payload.capabilities.compression !== false) {
521
+ rejectState("Kimi web agent-state channel did not provide the expected protocol-v2 hello.");
522
+ return;
523
+ }
524
+ serverHello = true;
525
+ return;
526
+ }
527
+ if (!serverHello) {
528
+ rejectState("Kimi web agent-state channel sent data before its protocol hello.");
529
+ return;
530
+ }
531
+ if (frame.type === "ack") {
532
+ if (typeof frame.id !== "string" || typeof frame.code !== "number" || !isRecord(frame.payload)) {
533
+ rejectState("Kimi web agent-state channel returned an invalid acknowledgement.");
534
+ return;
535
+ }
536
+ if (frame.id === helloId) {
537
+ if (clientHelloAcknowledged || frame.code !== 0) {
538
+ rejectState("Kimi web agent-state channel rejected the client hello.");
539
+ return;
540
+ }
541
+ clientHelloAcknowledged = true;
542
+ subscribeSent = send({
543
+ type: "subscribe_v2",
544
+ id: subscribeId,
545
+ payload: { session_id: sessionId, transcript: { main: "block" } },
546
+ });
547
+ return;
548
+ }
549
+ if (frame.id === subscribeId) {
550
+ if (subscriptionAcknowledged || !subscribeSent || frame.code !== 0 ||
551
+ !Array.isArray(frame.payload.accepted) ||
552
+ frame.payload.accepted.length !== 1 ||
553
+ frame.payload.accepted[0] !== sessionId ||
554
+ !Array.isArray(frame.payload.not_found) ||
555
+ frame.payload.not_found.length !== 0) {
556
+ rejectState("Kimi web agent-state channel did not accept the exact main-session subscription.");
557
+ return;
558
+ }
559
+ subscriptionAcknowledged = true;
560
+ completeIfReady();
561
+ return;
562
+ }
563
+ rejectState("Kimi web agent-state channel returned an unexpected acknowledgement.");
564
+ return;
565
+ }
566
+ if (frame.type === "agent") {
567
+ if (!subscribeSent || !isAgentStateFrame(frame, sessionId)) {
568
+ rejectState("Kimi web agent-state channel returned invalid or misbound main-agent state.");
569
+ return;
570
+ }
571
+ latestState = frame;
572
+ completeIfReady();
573
+ return;
574
+ }
575
+ if (frame.type === "ping") {
576
+ if (isRecord(frame.payload) && typeof frame.payload.nonce === "string") {
577
+ send({ type: "pong", payload: { nonce: frame.payload.nonce } });
578
+ }
579
+ }
580
+ };
581
+ const onAbort = () => {
582
+ settle(cancelledFailure(failureCode, "swarm-activation.status"));
583
+ };
584
+ const onError = () => {
585
+ rejectState("Kimi web agent-state channel failed before valid state was received.");
586
+ };
587
+ const onClose = (event) => {
588
+ if (event.wasClean &&
589
+ closeRequested &&
590
+ serverHello &&
591
+ clientHelloAcknowledged &&
592
+ subscriptionAcknowledged &&
593
+ latestState !== undefined) {
594
+ settle(latestState);
595
+ return;
596
+ }
597
+ rejectState("Kimi web agent-state channel closed before valid state was received.");
598
+ };
599
+ signal?.addEventListener("abort", onAbort, { once: true });
600
+ socket.addEventListener("open", onOpen);
601
+ socket.addEventListener("message", onMessage);
602
+ socket.addEventListener("error", onError);
603
+ socket.addEventListener("close", onClose);
604
+ if (signal?.aborted === true)
605
+ onAbort();
606
+ });
607
+ }
608
+ finally {
609
+ await refreshOwnedDescendants(web);
610
+ }
611
+ }
612
+ function isAgentStateFrame(frame, sessionId) {
613
+ return frame.session_id === sessionId &&
614
+ frame.agent_id === "main" &&
615
+ typeof frame.event_created_at === "string" &&
616
+ Number.isFinite(Date.parse(frame.event_created_at)) &&
617
+ (frame.status === "idle" || frame.status === "running") &&
618
+ (frame.model === undefined || typeof frame.model === "string") &&
619
+ (frame.thinking === undefined || typeof frame.thinking === "string") &&
620
+ (frame.permission_mode === undefined ||
621
+ frame.permission_mode === "manual" ||
622
+ frame.permission_mode === "yolo" ||
623
+ frame.permission_mode === "auto") &&
624
+ typeof frame.plan_mode === "boolean" &&
625
+ typeof frame.swarm_mode === "boolean" &&
626
+ typeof frame.tower_mode === "boolean";
627
+ }
366
628
  async function shutdownWeb(web, signal, failureCode) {
367
629
  await restData(web, "POST", "/api/v1/shutdown", undefined, signal, failureCode);
368
630
  const settlement = await waitForChildClose(web, signal, failureCode);
@@ -4,4 +4,4 @@
4
4
  // and AGENTS.md on every release. A future improvement would read this from
5
5
  // package.json at build time, but that adds a build-step dependency we
6
6
  // don't want yet.
7
- export const KIMI_PLUGIN_CC_VERSION = "2.5.1";
7
+ export const KIMI_PLUGIN_CC_VERSION = "2.5.3";
@@ -23,6 +23,7 @@ Job controls are `/kimi-status [job-id]`, `/kimi-result [job-id]`, and `/kimi-ca
23
23
  - Node.js 24.9.0 or later.
24
24
  - DeepSeek Harness >=0.2.1-rc.5 <0.3.0; this package is developed and composed against 0.2.1-rc.5.
25
25
  - Kimi Code CLI on `PATH`, authenticated and configured through Kimi. The companion's exact-version certification table remains authoritative.
26
+ - A patched DSH host and profiles with zod >=4.6.6 and undici >=8.12.2; see [host installation and audit instructions](../../docs/registry-releases.md#patched-dsh-host-and-profiles). The plugin does not patch your host automatically.
26
27
 
27
28
  ## Install
28
29
 
@@ -34,6 +35,8 @@ dsh plugin --profile web add ./plugins/kimi-dsh
34
35
 
35
36
  For another DSH profile, replace `web` with its name. To install the published root package instead, run `dsh plugin --profile web add kimi-plugin-cc`.
36
37
 
38
+ For DSH Discover, enable **Custom plugin indexes** and add `https://raw.githubusercontent.com/linxule/kimi-plugin-cc/main/dsh/index.json` as an index source.
39
+
37
40
  Restart when requested, then run `/kimi-setup`. Setup is explicit: installing or enabling the bundle does not modify Kimi configuration. Delegation refuses to start when the DSH managed hook is missing or invalid.
38
41
 
39
42
  Useful setup commands:
@@ -61,7 +64,7 @@ The bundle's `kimi-plugin-cc` row accepts:
61
64
  |---|---|---|
62
65
  | `dataRoot` | `$DSH_HOME/plugin-data/kimi-plugin-cc` | Absolute companion data directory; `KIMI_PLUGIN_CC_DATA` overrides the derived default |
63
66
  | `env` | `{}` | Explicit child environment layered over DSH's credential-scrubbed environment |
64
- | `disposeGraceMs` | `6000` | Managed subprocess termination grace |
67
+ | `disposeGraceMs` | `6000` | Managed subprocess termination grace in milliseconds; minimum 6000 |
65
68
  | `maxOutputBytes` | `16777216` | Maximum captured stdout or stderr bytes per command |
66
69
  | `model` | Native Kimi default | Optional configured model alias applied to every delegation |
67
70
  | `thinkingEffort` | Native Kimi default | Optional per-run thinking effort request |
@@ -65,6 +65,7 @@ export async function runCliPrompt(opts) {
65
65
  kimiHome: resolveKimiHome(opts.env, opts.cwd),
66
66
  env: opts.env,
67
67
  resumeSessionId: opts.resumeSessionId,
68
+ expectedExecutionRoot: opts.trustedWorkspaceRoot ?? opts.cwd,
68
69
  stage: "cli-client.spawn",
69
70
  });
70
71
  }
@@ -781,6 +782,13 @@ const KIMI_LEGACY_FORCED_VALUE = "1";
781
782
  function buildArgs(opts) {
782
783
  const args = [...(opts.prefixArgs ?? [])];
783
784
  args.push("--output-format", "stream-json");
785
+ const smokeCandidate = opts.executionPlan.certification === "test-bypass" && opts.env.KIMI_PLUGIN_CC_SMOKE === "1"
786
+ ? opts.env.KIMI_PLUGIN_CC_SMOKE_V2_CANDIDATE
787
+ : undefined;
788
+ if (opts.resumeSessionId === undefined &&
789
+ ["2.5.0", "2.5.1"].includes(opts.executionPlan.kimiVersion ?? smokeCandidate ?? "")) {
790
+ args.push("--environment", "local");
791
+ }
784
792
  if (opts.resumeSessionId !== undefined) {
785
793
  args.push("-r", opts.resumeSessionId);
786
794
  }