kimi-plugin-cc 2.5.2 → 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 (36) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +6 -0
  4. package/dist/cli-client.js +8 -0
  5. package/dist/commands/swarm.js +2 -2
  6. package/dist/hooks/config-safety.js +5 -2
  7. package/dist/kimi-command.js +1 -0
  8. package/dist/kimi-engine.js +3 -0
  9. package/dist/native-v2-preflight.js +138 -30
  10. package/dist/swarm-activation.js +276 -14
  11. package/dist/version.js +1 -1
  12. package/docs/migration.md +4 -4
  13. package/docs/registry-releases.md +3 -1
  14. package/docs/safety.md +11 -9
  15. package/docs/swarm-recovery.md +2 -2
  16. package/docs/upstream-2.5.0-certification.md +48 -0
  17. package/docs/upstream-2.5.1-certification.md +43 -0
  18. package/package.json +3 -1
  19. package/plugins/kimi-codex/.codex-plugin/plugin.json +1 -1
  20. package/plugins/kimi-codex/dist/cli-client.js +8 -0
  21. package/plugins/kimi-codex/dist/commands/swarm.js +2 -2
  22. package/plugins/kimi-codex/dist/hooks/config-safety.js +5 -2
  23. package/plugins/kimi-codex/dist/kimi-command.js +1 -0
  24. package/plugins/kimi-codex/dist/kimi-engine.js +3 -0
  25. package/plugins/kimi-codex/dist/native-v2-preflight.js +138 -30
  26. package/plugins/kimi-codex/dist/swarm-activation.js +276 -14
  27. package/plugins/kimi-codex/dist/version.js +1 -1
  28. package/plugins/kimi-dsh/dist/cli-client.js +8 -0
  29. package/plugins/kimi-dsh/dist/commands/swarm.js +2 -2
  30. package/plugins/kimi-dsh/dist/hooks/config-safety.js +5 -2
  31. package/plugins/kimi-dsh/dist/kimi-command.js +1 -0
  32. package/plugins/kimi-dsh/dist/kimi-engine.js +3 -0
  33. package/plugins/kimi-dsh/dist/native-v2-preflight.js +138 -30
  34. package/plugins/kimi-dsh/dist/swarm-activation.js +276 -14
  35. package/plugins/kimi-dsh/dist/version.js +1 -1
  36. package/plugins/kimi-dsh/package.json +1 -1
@@ -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.2";
7
+ export const KIMI_PLUGIN_CC_VERSION = "2.5.3";
@@ -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
  }
@@ -28,8 +28,8 @@ import { buildKimiSessionTitle, syncKimiSessionTitle } from "../session-title.js
28
28
  // only the wall-clock hard bound. Read/write modes reuse review/rescue lineage,
29
29
  // and the mandatory PreToolUse hook enforces read-only policy or trusted-worktree
30
30
  // write confinement on the coordinator and every subagent. Write mode requires
31
- // kimi-code 0.18.0+. Exact 2.3.2, 2.3.3, 2.4.0 and 2.4.1 additionally activate a same-job
32
- // session through a bounded loopback web lifecycle, then resume it through the normal print path.
31
+ // kimi-code 0.18.0+. Exact 2.3.2, 2.3.3, 2.4.0, 2.4.1, 2.5.0 and 2.5.1 additionally
32
+ // activate a same-job session through a bounded loopback web lifecycle, then resume it through the normal print path.
33
33
  const SWARM_SUMMARY_MAX = 120;
34
34
  const SWARM_AGENT_PROFILE = "<swarm>";
35
35
  const SWARM_WRITE_AGENT_PROFILE = "<swarm-write>";
@@ -595,7 +595,8 @@ function validateVersionSensitiveEvents(events, version) {
595
595
  // Exact schemas reviewed at 2.0.0 (1b89e4b0), 2.0.1 (caf7d4e2),
596
596
  // 2.0.2 (9d07f634), 2.1.0 (52437299), 2.1.1 (f67e6398), 2.2.0 (472192c3),
597
597
  // 2.3.0 (b5e28fd6), 2.3.1 (684bae9e), 2.3.2 (b8dd3a13), 2.3.3 (ec495f0c),
598
- // 2.4.0 (f26e4d91), and 2.4.1 (2c73f3f5): the additive events and HookDefSchema are unchanged.
598
+ // 2.4.0 (f26e4d91), 2.4.1 (2c73f3f5), 2.5.0 (41c34143), and 2.5.1 (1b02030d):
599
+ // the additive events and HookDefSchema are unchanged.
599
600
  // Require the full version string AND matching components; no inferred 2.x compatibility.
600
601
  if (version.major === 2 && ((version.minor === 0 && ((version.version === "2.0.0" && version.patch === 0) ||
601
602
  (version.version === "2.0.1" && version.patch === 1) ||
@@ -608,7 +609,9 @@ function validateVersionSensitiveEvents(events, version) {
608
609
  (version.version === "2.3.2" && version.patch === 2) ||
609
610
  (version.version === "2.3.3" && version.patch === 3))) ||
610
611
  (version.minor === 4 && ((version.version === "2.4.0" && version.patch === 0) ||
611
- (version.version === "2.4.1" && version.patch === 1))))) {
612
+ (version.version === "2.4.1" && version.patch === 1))) ||
613
+ (version.minor === 5 && ((version.version === "2.5.0" && version.patch === 0) ||
614
+ (version.version === "2.5.1" && version.patch === 1))))) {
612
615
  return { valid: true };
613
616
  }
614
617
  if (version.major !== 0) {
@@ -47,6 +47,7 @@ const RESERVED_PREFIX_FLAGS = new Set([
47
47
  "--plan", "--auto", "-y", "--yolo", "--yes", "--auto-approve", "--manual",
48
48
  // Model / profile / roots.
49
49
  "-m", "--model", "--skills-dir", "--agent", "--agent-file", "--add-dir",
50
+ "--environment", "--environment-cwd",
50
51
  // Terminal informational flags (would replace the run with a banner).
51
52
  "-V", "--version", "-h", "--help",
52
53
  ]);
@@ -45,6 +45,8 @@ export const NATIVE_V2_CERTIFIED_VERSIONS = Object.freeze([
45
45
  "2.3.3",
46
46
  "2.4.0",
47
47
  "2.4.1",
48
+ "2.5.0",
49
+ "2.5.1",
48
50
  ]);
49
51
  /** Per-operation native-v2 certification. One green operation never certifies another. */
50
52
  export const NATIVE_V2_CERTIFIED = new Map([
@@ -127,6 +129,7 @@ export async function prepareKimiExecutionPlan(options) {
127
129
  kimiHome: resolveKimiHome(options.env, options.cwd),
128
130
  env: options.env,
129
131
  resumeSessionId: options.resumeSessionId,
132
+ expectedExecutionRoot: options.cwd,
130
133
  stage: "kimi-engine.plan",
131
134
  });
132
135
  };