@ycodium-ai/agent-kimi 0.2.2106 → 0.2.2510

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.
package/dist/index.js CHANGED
@@ -8,7 +8,7 @@ var __export = (target, all) => {
8
8
  import { definePlugin } from "@ycodium-ai/plugin-api";
9
9
 
10
10
  // src/card.ts
11
- var KIMI_DRIVER_KIND = "kimi";
11
+ var KIMI_DRIVER_KIND = "kimiCode";
12
12
  var KIMI_CARD = {
13
13
  driverKind: KIMI_DRIVER_KIND,
14
14
  displayName: "Kimi Code",
@@ -17,20 +17,20 @@ var KIMI_CARD = {
17
17
  launchArgs: "acp",
18
18
  homeEnvVar: "KIMI_CODE_HOME",
19
19
  environment: [],
20
+ // 官方安装器装进 ~/.kimi-code(安装器的 KIMI_INSTALL_DIR 默认值)。
20
21
  install: {
21
22
  kind: "vendorScript",
22
23
  posixCommand: "curl -fsSL https://code.kimi.com/kimi-code/install.sh | bash",
23
24
  windowsCommand: "irm https://code.kimi.com/kimi-code/install.ps1 | iex",
24
- // 安装器的 KIMI_INSTALL_DIR 默认就是 $HOME/.kimi-code。
25
25
  installedPathSegment: "/.kimi-code/"
26
26
  },
27
- // `kimi login` 就是设备码流程(CLI 自述 "via the device-code flow"),无头机器登得上;
28
- // ACP 子进程复用缓存凭据,不需要环境变量。
27
+ // `kimi login` 就是设备码流程(CLI 自述 "via the device-code flow"),无头机器也登得上;
28
+ // ACP 子进程复用缓存下来的凭据,不需要环境变量。
29
29
  signIn: [{ kind: "command", command: "kimi login" }],
30
30
  deviceCodeSignInArgs: ["login"],
31
31
  npmPackage: "@moonshot-ai/kimi-code",
32
32
  homebrewFormula: "",
33
- // 是 `upgrade` 不是 `update`(`update` 只是别名)。
33
+ // 是 `upgrade` 不是 `update`(`update` 只是它的别名)。
34
34
  selfUpdateArgs: ["upgrade"],
35
35
  docsUrl: "https://github.com/MoonshotAI/kimi-code",
36
36
  setupNote: "",
@@ -38,33 +38,43 @@ var KIMI_CARD = {
38
38
  defaultTextGenerationModel: "",
39
39
  customModelIdExample: "",
40
40
  modelSlugAliases: null,
41
- // 两份都是 Moonshot 自家目录,id 空间不相交,按序精确匹配不撞车。
42
- modelsDevProviderIds: ["kimi-for-coding", "moonshotai"],
43
- modelsDevSlugPrefixed: false,
44
- // ACP 上报的 id 一律带这个命名空间(`kimi-code/k3`),models.dev 那边是裸的。
45
- modelSlugPrefix: "kimi-code/",
46
- // 与宿主 acp driver 今天的注入面一致。
47
41
  mcpTransports: ["stdio", "http", "sse"],
48
42
  pluginManaged: true,
49
43
  verifiedSkills: false,
50
44
  everydayHarness: false,
51
45
  capabilities: {
52
- // ycodium-acp-executor 尚不支持会话内换模型,名片必须说实话。
53
46
  sessionModelSwitch: "unsupported",
54
- runtimeModeSwitch: "in-session"
47
+ // 与通用 ACP 同:`setRuntimeMode` 只改自己的会话记录,用户真正切得动的是
48
+ // `nativeModeSwitch`。kimi `acp` 的 `session/set_mode plan` 实测回 `{}` 并推
49
+ // `current_mode_update`,不存在的 modeId 被 `-32602 Unknown modeId` 拒掉。
50
+ runtimeModeSwitch: "in-session",
51
+ nativeModeSwitch: true
55
52
  },
56
- // 凭据由 CLI 自己保管在它 home 里,宿主不读 token。
57
53
  credentialSources: [],
58
54
  diagnostics: [],
55
+ // 设置表单照这份画:带 x-ycodium-form 的属性才进表单(enabled、customModels 等另有入口,不在此列),
56
+ // 顺序看 x-ycodium-form-order;文案键在本插件 locales/<locale>.json 里解析,宿主不兜底。
59
57
  configSchema: {
60
58
  type: "object",
59
+ "x-ycodium-form-order": ["binaryPath", "homePath"],
61
60
  properties: {
62
- binaryPath: { type: "string" },
63
- launchArgs: { type: "string" },
64
- homePath: { type: "string" },
65
- homeEnvVar: { type: "string" },
66
- providerId: { type: "string" },
67
- packageName: { type: "string" },
61
+ binaryPath: {
62
+ type: "string",
63
+ "x-ycodium-form": {
64
+ titleKey: "form.binaryPath.title",
65
+ descriptionKey: "form.binaryPath.description",
66
+ placeholderKey: "form.binaryPath.placeholder",
67
+ control: "executable-path"
68
+ }
69
+ },
70
+ homePath: {
71
+ type: "string",
72
+ "x-ycodium-form": {
73
+ titleKey: "form.homePath.title",
74
+ descriptionKey: "form.homePath.description",
75
+ placeholderKey: "form.homePath.placeholder"
76
+ }
77
+ },
68
78
  enabled: { type: "boolean" },
69
79
  customModels: { type: "array" },
70
80
  driver: { type: "string" },
@@ -74,40 +84,16 @@ var KIMI_CARD = {
74
84
  additionalProperties: true
75
85
  },
76
86
  modelConnectivity: {
77
- dialects: ["anthropic-messages", "openai-responses", "openai-chat-completions"],
78
- acceptsExternalEndpoints: true,
79
- // 四个变量,少一个整套都不生效(从本机 `@moonshot-ai/kimi-code` 的
80
- // `applyEnvModelConfig` 逐行读出来的):
81
- //
82
- // KIMI_MODEL_NAME 门闩。没设它,后面三个一律不读。
83
- // KIMI_MODEL_API_KEY 设了 NAME 却缺它会硬报错。
84
- // KIMI_MODEL_BASE_URL 地址。
85
- // KIMI_MODEL_PROVIDER_TYPE kimi(默认) / anthropic / openai,决定它说哪种话。
86
- //
87
- // 别用 ANTHROPIC_BASE_URL——「Kimi 是 Claude Code fork」在这个 CLI 上不成立:
88
- // ANTHROPIC_* 只存在于它内嵌的 SDK 里,而 Kimi 显式传 baseUrl 进去,显式值赢过
89
- // SDK 的环境变量兜底。KIMI_BASE_URL 也不行,那个读的是 config 文件里的
90
- // provider.env 映射,不是进程环境。
91
- //
92
- // 这里钉死 `openai` 类型 = chat-completions。代理按手实际打的路径认方言,所以
93
- // 就算这一栏将来改成别的,代理那边不用跟着动。
94
- // 它 `openai` 类型的默认 base 是 `https://api.openai.com/v1`——base 含 /v1,
95
- // 后面自己加 /chat/completions,跟 dsh 一样。
96
- nativePathSuffix: "/chat/completions",
97
- baseUrlEnvKey: "KIMI_MODEL_BASE_URL",
98
- credentialEnvKey: "KIMI_MODEL_API_KEY",
99
- extraEnv: [
100
- { name: "KIMI_MODEL_NAME", value: "$model" },
101
- { name: "KIMI_MODEL_PROVIDER_TYPE", value: "openai" }
102
- ]
103
- },
104
- // 存量 `driver: "acp"` 信封里命令是 kimi 或账号目录变量是 KIMI_CODE_HOME 的归这双手。
105
- legacyAcpClaim: { commandName: "kimi", homeEnvVar: "KIMI_CODE_HOME" }
87
+ dialects: [],
88
+ acceptsExternalEndpoints: false
89
+ }
106
90
  };
107
91
 
108
92
  // src/kimiInstance.ts
109
- import * as NodeOS from "node:os";
110
- import * as NodePath2 from "node:path";
93
+ import * as NodeOS2 from "node:os";
94
+ import * as NodePath3 from "node:path";
95
+ import { mergeLaunchEnv, modelSelectionStringOption } from "@ycodium-ai/plugin-api/executor";
96
+ import { nodeStreamsFromHandle } from "@ycodium-ai/plugin-api/process-stdin";
111
97
 
112
98
  // ../../packages/ycodium-acp-executor/src/executor.ts
113
99
  import * as NodeCrypto from "node:crypto";
@@ -23497,10 +23483,44 @@ var legacyClientNotificationMethods = /* @__PURE__ */ new Set([
23497
23483
  CLIENT_METHODS.elicitation_complete
23498
23484
  ]);
23499
23485
 
23500
- // ../../packages/ycodium-acp-protocol/src/connection.ts
23486
+ // ../../packages/ycodium-acp-executor/src/connection.ts
23501
23487
  import * as NodeStream from "node:stream";
23502
23488
 
23503
- // ../../packages/ycodium-acp-protocol/src/errors.ts
23489
+ // ../../packages/ycodium-acp-executor/src/errors.ts
23490
+ var AcpExecutorError = class extends Error {
23491
+ code;
23492
+ constructor(code, message) {
23493
+ super(message);
23494
+ this.name = "AcpExecutorError";
23495
+ this.code = code;
23496
+ }
23497
+ };
23498
+ var AcpExecutorDisposedError = class extends AcpExecutorError {
23499
+ constructor(message = "ACP executor has been disposed") {
23500
+ super("disposed", message);
23501
+ this.name = "AcpExecutorDisposedError";
23502
+ }
23503
+ };
23504
+ var AcpExecutorNotStartedError = class extends AcpExecutorError {
23505
+ constructor(message = "ACP session has not been started") {
23506
+ super("not-started", message);
23507
+ this.name = "AcpExecutorNotStartedError";
23508
+ }
23509
+ };
23510
+ var AcpExecutorCancelTimeoutError = class extends AcpExecutorError {
23511
+ constructor(message = "Timed out waiting for session/prompt to finish after cancellation") {
23512
+ super("cancel-timeout", message);
23513
+ this.name = "AcpExecutorCancelTimeoutError";
23514
+ }
23515
+ };
23516
+ var AcpExecutorRequestError = class extends AcpExecutorError {
23517
+ method;
23518
+ constructor(method, message) {
23519
+ super("request-failed", message);
23520
+ this.name = "AcpExecutorRequestError";
23521
+ this.method = method;
23522
+ }
23523
+ };
23504
23524
  var AcpConnectionClosedError = class extends Error {
23505
23525
  constructor(message = "ACP connection closed") {
23506
23526
  super(message);
@@ -23508,7 +23528,7 @@ var AcpConnectionClosedError = class extends Error {
23508
23528
  }
23509
23529
  };
23510
23530
 
23511
- // ../../packages/ycodium-acp-protocol/src/jsonrpc.ts
23531
+ // ../../packages/ycodium-acp-executor/src/jsonrpc.ts
23512
23532
  function isRecord2(value) {
23513
23533
  return typeof value === "object" && value !== null;
23514
23534
  }
@@ -23546,7 +23566,7 @@ function readJsonRpcError(value) {
23546
23566
  };
23547
23567
  }
23548
23568
 
23549
- // ../../packages/ycodium-acp-protocol/src/line-buffer.ts
23569
+ // ../../packages/ycodium-acp-executor/src/line-buffer.ts
23550
23570
  var newline2 = 10;
23551
23571
  var LineBuffer2 = class {
23552
23572
  #pending = [];
@@ -23590,7 +23610,7 @@ var LineBuffer2 = class {
23590
23610
  }
23591
23611
  };
23592
23612
 
23593
- // ../../packages/ycodium-acp-protocol/src/session-update.ts
23613
+ // ../../packages/ycodium-acp-executor/src/session-update.ts
23594
23614
  var KNOWN_SESSION_UPDATES = /* @__PURE__ */ new Set([
23595
23615
  "user_message_chunk",
23596
23616
  "agent_message_chunk",
@@ -23635,7 +23655,7 @@ function decodeSessionUpdate(payload) {
23635
23655
  return { status: "raw", params: payload };
23636
23656
  }
23637
23657
 
23638
- // ../../packages/ycodium-acp-protocol/src/connection.ts
23658
+ // ../../packages/ycodium-acp-executor/src/connection.ts
23639
23659
  var textDecoder = new TextDecoder();
23640
23660
  var textEncoder = new TextEncoder();
23641
23661
  function asBytes(chunk) {
@@ -23725,6 +23745,7 @@ function createAcpConnection(options) {
23725
23745
  options.readable.off("data", onData);
23726
23746
  options.readable.off("end", onEnd);
23727
23747
  options.readable.off("error", onError);
23748
+ options.writable.off("error", onWritableError);
23728
23749
  try {
23729
23750
  await byteWriter.close();
23730
23751
  } catch {
@@ -23863,8 +23884,17 @@ function createAcpConnection(options) {
23863
23884
  endInboundBatch();
23864
23885
  }
23865
23886
  };
23887
+ const failInbound = (error62) => {
23888
+ if (closedError !== void 0) return;
23889
+ options.onInboundFailure?.(error62);
23890
+ void shutdown(
23891
+ new AcpConnectionClosedError(
23892
+ `ACP inbound handling failed: ${error62 instanceof Error ? error62.message : String(error62)}`
23893
+ )
23894
+ );
23895
+ };
23866
23896
  const enqueueInbound = (work) => {
23867
- inboundTail = inboundTail.then(work, work);
23897
+ inboundTail = inboundTail.then(work).catch(failInbound);
23868
23898
  };
23869
23899
  const onData = (chunk) => {
23870
23900
  if (closedError !== void 0) {
@@ -23914,9 +23944,15 @@ function createAcpConnection(options) {
23914
23944
  );
23915
23945
  });
23916
23946
  };
23947
+ const onWritableError = (error62) => {
23948
+ void shutdown(
23949
+ error62 instanceof Error ? error62 : new AcpConnectionClosedError("ACP output stream error")
23950
+ );
23951
+ };
23917
23952
  options.readable.on("data", onData);
23918
23953
  options.readable.on("end", onEnd);
23919
23954
  options.readable.on("error", onError);
23955
+ options.writable.once("error", onWritableError);
23920
23956
  const request = (async (method, params) => {
23921
23957
  failIfClosed();
23922
23958
  const id = nextRequestId;
@@ -23925,7 +23961,7 @@ function createAcpConnection(options) {
23925
23961
  pending.set(String(id), { method, resolve: resolve2, reject });
23926
23962
  const message = params === void 0 ? { jsonrpc: "2.0", id, method } : { jsonrpc: "2.0", id, method, params };
23927
23963
  try {
23928
- await writeMessage(message);
23964
+ await Promise.race([writeMessage(message), promise2]);
23929
23965
  } catch (error62) {
23930
23966
  if (pending.delete(String(id))) {
23931
23967
  reject(error62);
@@ -23977,6 +24013,12 @@ function acpApprovalOptions(request) {
23977
24013
  }
23978
24014
  return options;
23979
24015
  }
24016
+ function selectAutoApprovedAcpPermissionOption(request) {
24017
+ if (new Set(request.options.map((option) => option.kind)).size !== request.options.length) {
24018
+ return void 0;
24019
+ }
24020
+ return selectAcpPermissionOptionId(request, "acceptForSession") ?? selectAcpPermissionOptionId(request, "accept");
24021
+ }
23980
24022
  function parseAcpResumeCursor(raw, schemaVersion) {
23981
24023
  if (typeof raw !== "object" || raw === null || Array.isArray(raw)) return void 0;
23982
24024
  const record2 = raw;
@@ -24138,42 +24180,6 @@ var UnboundedEventStream = class {
24138
24180
  }
24139
24181
  };
24140
24182
 
24141
- // ../../packages/ycodium-acp-executor/src/errors.ts
24142
- var AcpExecutorError = class extends Error {
24143
- code;
24144
- constructor(code, message) {
24145
- super(message);
24146
- this.name = "AcpExecutorError";
24147
- this.code = code;
24148
- }
24149
- };
24150
- var AcpExecutorDisposedError = class extends AcpExecutorError {
24151
- constructor(message = "ACP executor has been disposed") {
24152
- super("disposed", message);
24153
- this.name = "AcpExecutorDisposedError";
24154
- }
24155
- };
24156
- var AcpExecutorNotStartedError = class extends AcpExecutorError {
24157
- constructor(message = "ACP session has not been started") {
24158
- super("not-started", message);
24159
- this.name = "AcpExecutorNotStartedError";
24160
- }
24161
- };
24162
- var AcpExecutorCancelTimeoutError = class extends AcpExecutorError {
24163
- constructor(message = "Timed out waiting for session/prompt to finish after cancellation") {
24164
- super("cancel-timeout", message);
24165
- this.name = "AcpExecutorCancelTimeoutError";
24166
- }
24167
- };
24168
- var AcpExecutorRequestError = class extends AcpExecutorError {
24169
- method;
24170
- constructor(method, message) {
24171
- super("request-failed", message);
24172
- this.name = "AcpExecutorRequestError";
24173
- this.method = method;
24174
- }
24175
- };
24176
-
24177
24183
  // ../../packages/ycodium-acp-executor/src/events.ts
24178
24184
  function canonicalRequestTypeFromAcpKind(kind) {
24179
24185
  switch (kind) {
@@ -24370,6 +24376,168 @@ function makeAcpContentDeltaEvent(input2) {
24370
24376
  };
24371
24377
  }
24372
24378
 
24379
+ // ../../packages/ycodium-acp-executor/src/config-options.ts
24380
+ function isRecord3(value) {
24381
+ return typeof value === "object" && value !== null && !Array.isArray(value);
24382
+ }
24383
+ function asString(value) {
24384
+ return typeof value === "string" ? value : void 0;
24385
+ }
24386
+ function normalizedField(option, key) {
24387
+ return asString(option[key])?.trim().toLowerCase() ?? "";
24388
+ }
24389
+ function looksLikeEffortOption(option) {
24390
+ const id = normalizedField(option, "id");
24391
+ const name = normalizedField(option, "name");
24392
+ return id === "effort" || id === "reasoning" || name === "effort" || name === "reasoning" || name.includes("effort") || name.includes("reasoning") || normalizedField(option, "category") === "thought_level";
24393
+ }
24394
+ function flattenChoices(rawOptions, currentValue) {
24395
+ if (!Array.isArray(rawOptions)) return [];
24396
+ const out = [];
24397
+ const seen = /* @__PURE__ */ new Set();
24398
+ const push = (entry) => {
24399
+ if (!isRecord3(entry)) return;
24400
+ const value = asString(entry["value"]);
24401
+ if (value === void 0 || seen.has(value)) return;
24402
+ seen.add(value);
24403
+ const description = asString(entry["description"])?.trim();
24404
+ const name = asString(entry["name"])?.trim();
24405
+ out.push({
24406
+ value,
24407
+ ...name ? { name } : {},
24408
+ label: name || value,
24409
+ ...description ? { description } : {},
24410
+ ...currentValue !== void 0 && currentValue === value ? { isDefault: true } : {}
24411
+ });
24412
+ };
24413
+ for (const entry of rawOptions) {
24414
+ if (isRecord3(entry) && Array.isArray(entry["options"])) {
24415
+ for (const nested of entry["options"]) push(nested);
24416
+ continue;
24417
+ }
24418
+ push(entry);
24419
+ }
24420
+ return out;
24421
+ }
24422
+ function findAcpEffortConfig(configOptions) {
24423
+ if (!Array.isArray(configOptions)) return void 0;
24424
+ const candidates = configOptions.filter(isRecord3).filter((option) => normalizedField(option, "type") === "select").filter(looksLikeEffortOption);
24425
+ if (candidates.length === 0) return void 0;
24426
+ const picked = candidates.find((option) => normalizedField(option, "category") === "model_option") ?? candidates.find((option) => normalizedField(option, "id") === "effort") ?? candidates.find((option) => normalizedField(option, "category") === "thought_level") ?? candidates[0];
24427
+ if (picked === void 0) return void 0;
24428
+ const configId = asString(picked["id"])?.trim();
24429
+ if (!configId) return void 0;
24430
+ const choices = flattenChoices(picked["options"], asString(picked["currentValue"])?.trim());
24431
+ if (choices.length === 0) return void 0;
24432
+ return {
24433
+ configId,
24434
+ label: asString(picked["name"])?.trim() ?? "",
24435
+ choices
24436
+ };
24437
+ }
24438
+
24439
+ // ../../packages/ycodium-acp-executor/src/runtime-mode.ts
24440
+ function isRecord4(value) {
24441
+ return typeof value === "object" && value !== null && !Array.isArray(value);
24442
+ }
24443
+ function asString2(value) {
24444
+ return typeof value === "string" ? value : void 0;
24445
+ }
24446
+ function acpModeConfigId(setup) {
24447
+ if (!isRecord4(setup)) return void 0;
24448
+ const configOptions = setup["configOptions"];
24449
+ if (!Array.isArray(configOptions)) return void 0;
24450
+ for (const option of configOptions) {
24451
+ if (!isRecord4(option)) continue;
24452
+ if (asString2(option["category"])?.trim().toLowerCase() !== "mode") continue;
24453
+ const id = asString2(option["id"])?.trim();
24454
+ if (id) return id;
24455
+ }
24456
+ return void 0;
24457
+ }
24458
+ function acpCurrentModeId(setup) {
24459
+ if (!isRecord4(setup)) return void 0;
24460
+ const modes = setup["modes"];
24461
+ if (isRecord4(modes)) {
24462
+ const current = asString2(modes["currentModeId"])?.trim();
24463
+ if (current) return current;
24464
+ }
24465
+ const configOptions = setup["configOptions"];
24466
+ if (Array.isArray(configOptions)) {
24467
+ for (const option of configOptions) {
24468
+ if (!isRecord4(option)) continue;
24469
+ if (asString2(option["category"])?.trim().toLowerCase() !== "mode") continue;
24470
+ const current = asString2(option["currentValue"])?.trim();
24471
+ if (current) return current;
24472
+ }
24473
+ }
24474
+ return void 0;
24475
+ }
24476
+
24477
+ // ../../packages/ycodium-acp-executor/src/handshake.ts
24478
+ function acpInitializePayload(clientInfo, overrides) {
24479
+ return {
24480
+ protocolVersion: PROTOCOL_VERSION,
24481
+ clientCapabilities: {
24482
+ fs: {
24483
+ readTextFile: false,
24484
+ writeTextFile: false,
24485
+ ...overrides?.fs
24486
+ },
24487
+ terminal: overrides?.terminal ?? false
24488
+ },
24489
+ clientInfo
24490
+ };
24491
+ }
24492
+
24493
+ // ../../packages/ycodium-acp-executor/src/config-option-wiring.ts
24494
+ var VENDOR_CATEGORY_PREFIX = "_";
24495
+ var ACP_CONFIG_CATEGORY_HANDLING = {
24496
+ mode: {
24497
+ consumed: true,
24498
+ via: "runtime-mode.ts\uFF1AacpAvailableModes / acpCurrentModeId / acpModeConfigId"
24499
+ },
24500
+ model: {
24501
+ consumed: true,
24502
+ via: "runtime-model.ts:extractModelConfigId \u2192 executor.ts:applyRequestedModel"
24503
+ },
24504
+ thought_level: {
24505
+ consumed: true,
24506
+ via: "config-options.ts:findAcpEffortConfig \u2192 executor.ts:applyRequestedEffort"
24507
+ },
24508
+ model_option: {
24509
+ consumed: true,
24510
+ // 精确说法:它不是**识别器**,是**优先级**。`looksLikeEffortOption` 先筛出候选,
24511
+ // 标了 `model_option` 的在候选里排第一(厂商自己标的最准),排在 id 叫 `effort`
24512
+ // 的和标 `thought_level` 的前面。光有这个 category 而 id/name 不像力度档的,
24513
+ // 压根进不了候选。
24514
+ via: "config-options.ts:findAcpEffortConfig\uFF08\u5019\u9009\u91CC\u7684\u6700\u9AD8\u4F18\u5148\u7EA7\uFF0C\u538B\u8FC7 thought_level\uFF09"
24515
+ },
24516
+ model_config: {
24517
+ consumed: false,
24518
+ note: "\u53EA\u6709\u5BBF\u4E3B\u7684 CursorProvider \u6D88\u8D39\u8FC7\uFF08context / fast / thinking \u4E09\u4E2A\u5F00\u5173\uFF09\u3002Cursor 2026-09-20 \u5E9F\u5F03\u5220\u9664\uFF0C\u8FD9\u4E00\u7C7B\u73B0\u5728\u6CA1\u6709\u6D88\u8D39\u70B9\uFF1B\u6CA1\u89C1\u8FC7\u522B\u5BB6\u7684\u771F\u5B9E\u8F7D\u8377\uFF0C\u5148\u8BB0\u8D26\u4E0D\u731C\u5F62\u72B6\u3002"
24519
+ }
24520
+ };
24521
+ var KNOWN_CATEGORIES = new Set(Object.keys(ACP_CONFIG_CATEGORY_HANDLING));
24522
+ function unhandledConfigCategories(configOptions) {
24523
+ if (!configOptions) return [];
24524
+ const seen = /* @__PURE__ */ new Set();
24525
+ const out = [];
24526
+ for (const option of configOptions) {
24527
+ const category = option.category?.trim();
24528
+ if (!category) continue;
24529
+ if (KNOWN_CATEGORIES.has(category)) continue;
24530
+ if (seen.has(category)) continue;
24531
+ seen.add(category);
24532
+ out.push({
24533
+ category,
24534
+ optionId: option.id.trim(),
24535
+ vendorExtension: category.startsWith(VENDOR_CATEGORY_PREFIX)
24536
+ });
24537
+ }
24538
+ return out;
24539
+ }
24540
+
24373
24541
  // ../../packages/ycodium-acp-executor/src/mutex.ts
24374
24542
  var Mutex = class {
24375
24543
  locked = false;
@@ -24412,7 +24580,7 @@ var Mutex = class {
24412
24580
  };
24413
24581
 
24414
24582
  // ../../packages/ycodium-acp-executor/src/util.ts
24415
- function isRecord3(value) {
24583
+ function isRecord5(value) {
24416
24584
  return typeof value === "object" && value !== null && !Array.isArray(value);
24417
24585
  }
24418
24586
  function asTrimmedString(value) {
@@ -24458,11 +24626,11 @@ function extractCommandFromTitle(title) {
24458
24626
  return backtickMatch?.[1]?.trim() || void 0;
24459
24627
  }
24460
24628
  function extractToolCommand(data, title) {
24461
- const item = isRecord3(data?.["item"]) ? data["item"] : void 0;
24462
- const itemInput = item && isRecord3(item["input"]) ? item["input"] : void 0;
24463
- const itemResult = item && isRecord3(item["result"]) ? item["result"] : void 0;
24464
- const rawInput = isRecord3(data?.["rawInput"]) ? data["rawInput"] : void 0;
24465
- const dataInput = isRecord3(data?.["input"]) ? data["input"] : void 0;
24629
+ const item = isRecord5(data?.["item"]) ? data["item"] : void 0;
24630
+ const itemInput = item && isRecord5(item["input"]) ? item["input"] : void 0;
24631
+ const itemResult = item && isRecord5(item["result"]) ? item["result"] : void 0;
24632
+ const rawInput = isRecord5(data?.["rawInput"]) ? data["rawInput"] : void 0;
24633
+ const dataInput = isRecord5(data?.["input"]) ? data["input"] : void 0;
24466
24634
  const candidates = [
24467
24635
  normalizeCommandValue(item?.["command"]),
24468
24636
  normalizeCommandValue(itemInput?.["command"]),
@@ -24507,7 +24675,7 @@ function collectPaths(value, paths, seen, depth) {
24507
24675
  }
24508
24676
  return;
24509
24677
  }
24510
- if (!isRecord3(value)) {
24678
+ if (!isRecord5(value)) {
24511
24679
  return;
24512
24680
  }
24513
24681
  for (const key of ["path", "filePath", "relativePath", "filename", "newPath", "oldPath"]) {
@@ -24570,7 +24738,7 @@ function deriveToolActivityPresentation(input2) {
24570
24738
  const title = asTrimmedString(input2.title);
24571
24739
  const detail = stripTrailingExitCode(asTrimmedString(input2.detail));
24572
24740
  const fallbackSummary = asTrimmedString(input2.fallbackSummary) ?? "Tool";
24573
- const data = isRecord3(input2.data) ? input2.data : void 0;
24741
+ const data = isRecord5(input2.data) ? input2.data : void 0;
24574
24742
  const command = extractToolCommand(data, title);
24575
24743
  const primaryPath = extractPrimaryPath(data);
24576
24744
  const action = classifyToolAction({
@@ -24602,7 +24770,7 @@ function deriveToolActivityPresentation(input2) {
24602
24770
  };
24603
24771
  }
24604
24772
  if (action === "search") {
24605
- const rawInput = isRecord3(data?.["rawInput"]) ? data["rawInput"] : void 0;
24773
+ const rawInput = isRecord5(data?.["rawInput"]) ? data["rawInput"] : void 0;
24606
24774
  const query = asTrimmedString(rawInput?.["query"]) ?? asTrimmedString(rawInput?.["pattern"]) ?? asTrimmedString(rawInput?.["searchTerm"]);
24607
24775
  return {
24608
24776
  summary: "Searched files",
@@ -24641,6 +24809,46 @@ function findSessionConfigOption(configOptions, configId) {
24641
24809
  }
24642
24810
  return configOptions.find((option) => option.id.trim() === normalizedConfigId);
24643
24811
  }
24812
+ function collectSessionConfigOptionValues(configOption) {
24813
+ if (configOption.type !== "select" || !configOption.options) {
24814
+ return [];
24815
+ }
24816
+ return configOption.options.flatMap(
24817
+ (entry) => "value" in entry ? [entry.value] : entry.options.map((option) => option.value)
24818
+ );
24819
+ }
24820
+ function collectSessionModelIds(models) {
24821
+ if (!isRecord5(models) || !Array.isArray(models["availableModels"])) {
24822
+ return [];
24823
+ }
24824
+ return models["availableModels"].flatMap(
24825
+ (model) => isRecord5(model) && typeof model["modelId"] === "string" ? [model["modelId"]] : []
24826
+ );
24827
+ }
24828
+ function catalogModelLastSegment(value) {
24829
+ if (value.startsWith("[")) {
24830
+ let parsed;
24831
+ try {
24832
+ parsed = JSON.parse(value);
24833
+ } catch {
24834
+ return void 0;
24835
+ }
24836
+ if (!Array.isArray(parsed) || parsed.length < 2) return void 0;
24837
+ const last = parsed[parsed.length - 1];
24838
+ return typeof last === "string" ? last : void 0;
24839
+ }
24840
+ const separator = value.indexOf("/");
24841
+ return separator > 0 && separator < value.length - 1 ? value.slice(separator + 1) : void 0;
24842
+ }
24843
+ function matchAdvertisedModelValue(requestedModelId, advertisedValues) {
24844
+ const wanted = requestedModelId.trim();
24845
+ if (!wanted) return void 0;
24846
+ if (advertisedValues.includes(wanted)) return wanted;
24847
+ const byLastSegment = advertisedValues.filter(
24848
+ (value) => catalogModelLastSegment(value) === wanted
24849
+ );
24850
+ return byLastSegment.length === 1 ? byLastSegment[0] : void 0;
24851
+ }
24644
24852
  function parseSessionModeState(sessionResponse) {
24645
24853
  const modes = sessionResponse.modes;
24646
24854
  if (!modes) return void 0;
@@ -24679,7 +24887,7 @@ function parseAvailableCommandsUpdate(params) {
24679
24887
  }
24680
24888
  const commands = [];
24681
24889
  for (const command of availableCommands) {
24682
- if (!isRecord3(command)) {
24890
+ if (!isRecord5(command)) {
24683
24891
  continue;
24684
24892
  }
24685
24893
  const name = typeof command["name"] === "string" ? command["name"].trim() : "";
@@ -24687,7 +24895,7 @@ function parseAvailableCommandsUpdate(params) {
24687
24895
  continue;
24688
24896
  }
24689
24897
  const description = typeof command["description"] === "string" ? command["description"].trim() || void 0 : void 0;
24690
- const input2 = isRecord3(command["input"]) ? command["input"] : void 0;
24898
+ const input2 = isRecord5(command["input"]) ? command["input"] : void 0;
24691
24899
  const inputHint = typeof input2?.["hint"] === "string" ? input2["hint"].trim() || void 0 : void 0;
24692
24900
  commands.push({
24693
24901
  name,
@@ -24749,7 +24957,7 @@ function extractCommandFromTitle2(title) {
24749
24957
  return match?.[1]?.trim() || void 0;
24750
24958
  }
24751
24959
  function extractToolCallCommand(rawInput, title) {
24752
- if (isRecord3(rawInput)) {
24960
+ if (isRecord5(rawInput)) {
24753
24961
  const directCommand = normalizeCommandValue2(rawInput["command"]);
24754
24962
  if (directCommand) {
24755
24963
  return directCommand;
@@ -24776,7 +24984,7 @@ function boundToolCallOutputText(text) {
24776
24984
  }
24777
24985
  var RAW_OUTPUT_TEXT_FIELDS = ["content", "stdout", "stderr", "output"];
24778
24986
  function boundToolCallRawOutput(rawOutput) {
24779
- if (!isRecord3(rawOutput)) {
24987
+ if (!isRecord5(rawOutput)) {
24780
24988
  return rawOutput;
24781
24989
  }
24782
24990
  let changed = false;
@@ -25006,7 +25214,7 @@ function parseSessionUpdateEvent(params) {
25006
25214
  const entries = Array.isArray(upd["entries"]) ? upd["entries"] : [];
25007
25215
  const plan = entries.map((entry, index) => ({
25008
25216
  step: readAcpText(entry, "content") ?? `Step ${index + 1}`,
25009
- status: normalizePlanStepStatus(isRecord3(entry) ? entry["status"] : void 0)
25217
+ status: normalizePlanStepStatus(isRecord5(entry) ? entry["status"] : void 0)
25010
25218
  }));
25011
25219
  if (plan.length > 0) {
25012
25220
  events.push({
@@ -25045,7 +25253,7 @@ function parseSessionUpdateEvent(params) {
25045
25253
  }
25046
25254
  case "agent_message_chunk":
25047
25255
  case "agent_thought_chunk": {
25048
- const content = isRecord3(upd["content"]) ? upd["content"] : void 0;
25256
+ const content = isRecord5(upd["content"]) ? upd["content"] : void 0;
25049
25257
  const contentType = content?.["type"];
25050
25258
  const streamKind = kind === "agent_thought_chunk" ? "reasoning_text" : "assistant_text";
25051
25259
  if (contentType === "text") {
@@ -25112,16 +25320,18 @@ function normalizeModelId(value) {
25112
25320
  return value?.trim() || void 0;
25113
25321
  }
25114
25322
  function modelSelectionStateFromSetup(setup) {
25115
- const sessionModels = isRecord3(setup.models) ? setup.models : void 0;
25323
+ const sessionModels = isRecord5(setup.models) ? setup.models : void 0;
25116
25324
  const modelConfigId = extractModelConfigId(setup);
25117
25325
  const strategy = sessionModels === void 0 && modelConfigId !== void 0 ? "config-option" : "session/set_model";
25118
- const configModel = modelConfigId === void 0 ? void 0 : findSessionConfigOption(setup.configOptions, modelConfigId)?.currentValue;
25326
+ const modelConfigOption = modelConfigId === void 0 ? void 0 : findSessionConfigOption(setup.configOptions, modelConfigId);
25327
+ const configModel = modelConfigOption?.currentValue;
25119
25328
  const currentModelId = strategy === "config-option" ? typeof configModel === "string" ? normalizeModelId(configModel) : void 0 : typeof sessionModels?.["currentModelId"] === "string" ? normalizeModelId(sessionModels["currentModelId"]) : void 0;
25120
25329
  return {
25121
25330
  strategy,
25122
25331
  sessionModelsAdvertised: sessionModels !== void 0,
25123
25332
  modelConfigId,
25124
- currentModelId
25333
+ currentModelId,
25334
+ advertisedModelValues: strategy === "config-option" ? modelConfigOption === void 0 ? [] : collectSessionConfigOptionValues(modelConfigOption) : collectSessionModelIds(sessionModels)
25125
25335
  };
25126
25336
  }
25127
25337
  function createAcpExecutor(options) {
@@ -25140,8 +25350,10 @@ function createAcpExecutor(options) {
25140
25350
  strategy: "session/set_model",
25141
25351
  sessionModelsAdvertised: false,
25142
25352
  modelConfigId: void 0,
25143
- currentModelId: void 0
25353
+ currentModelId: void 0,
25354
+ advertisedModelValues: []
25144
25355
  };
25356
+ let lastUnmatchedModelId;
25145
25357
  let initializeResult;
25146
25358
  let startResult;
25147
25359
  let startInFlight;
@@ -25466,6 +25678,12 @@ function createAcpExecutor(options) {
25466
25678
  const userInputOutcome = await handlePermissionAsUserInput(params);
25467
25679
  if (userInputOutcome !== void 0) return userInputOutcome;
25468
25680
  const permissionRequest = parsePermissionRequest(params);
25681
+ if (options.autoApprove?.(permissionRequest) === true) {
25682
+ const optionId = selectAutoApprovedAcpPermissionOption(params);
25683
+ if (optionId !== void 0) {
25684
+ return { outcome: { outcome: "selected", optionId } };
25685
+ }
25686
+ }
25469
25687
  const requestId = randomId();
25470
25688
  const outcome = await new Promise((resolve2) => {
25471
25689
  const pending = {
@@ -25500,6 +25718,12 @@ function createAcpExecutor(options) {
25500
25718
  const connection = createAcpConnection({
25501
25719
  readable: options.readable,
25502
25720
  writable: options.writable,
25721
+ onInboundFailure: (error62) => {
25722
+ options.log?.warn?.("acp connection closed: an inbound request could not be answered", {
25723
+ threadId: options.threadId,
25724
+ error: error62 instanceof Error ? error62.message : String(error62)
25725
+ });
25726
+ },
25503
25727
  onNotification: (notification) => {
25504
25728
  if (notification.method === "session/update") {
25505
25729
  handleSessionUpdate(notification.params);
@@ -25553,36 +25777,70 @@ function createAcpExecutor(options) {
25553
25777
  if (requestedModelId === void 0 || requestedModelId === input2.state.currentModelId || input2.state.strategy === "session/set_model" && !input2.state.sessionModelsAdvertised || input2.state.strategy === "config-option" && input2.state.modelConfigId === void 0) {
25554
25778
  return input2.state;
25555
25779
  }
25780
+ const value = matchAdvertisedModelValue(requestedModelId, input2.state.advertisedModelValues);
25781
+ if (value === void 0) {
25782
+ if (requestedModelId !== lastUnmatchedModelId) {
25783
+ lastUnmatchedModelId = requestedModelId;
25784
+ options.log?.info("requested model is not in the agent catalog; keeping its own model", {
25785
+ requestedModelId,
25786
+ currentModelId: input2.state.currentModelId,
25787
+ advertisedModelValues: input2.state.advertisedModelValues
25788
+ });
25789
+ }
25790
+ return input2.state;
25791
+ }
25792
+ if (value === input2.state.currentModelId) {
25793
+ return input2.state;
25794
+ }
25556
25795
  if (input2.state.strategy === "config-option") {
25557
25796
  await connection.request("session/set_config_option", {
25558
25797
  sessionId: input2.acpSessionId,
25559
25798
  configId: input2.state.modelConfigId,
25560
- value: requestedModelId
25799
+ value
25561
25800
  });
25562
25801
  } else {
25563
25802
  await connection.request("session/set_model", {
25564
25803
  sessionId: input2.acpSessionId,
25565
- modelId: requestedModelId
25804
+ modelId: value
25566
25805
  });
25567
25806
  }
25568
25807
  if (!isLive(input2.expectedGeneration)) {
25569
25808
  throw new AcpExecutorDisposedError();
25570
25809
  }
25571
- return { ...input2.state, currentModelId: requestedModelId };
25810
+ return { ...input2.state, currentModelId: value };
25811
+ };
25812
+ const applyRequestedEffort = async (input2) => {
25813
+ const requested = input2.requestedEffort?.trim();
25814
+ if (!requested) return;
25815
+ const configOptions = typeof input2.setup === "object" && input2.setup !== null ? input2.setup.configOptions : void 0;
25816
+ const effort = findAcpEffortConfig(configOptions);
25817
+ if (effort === void 0) return;
25818
+ const matched = effort.choices.find((choice) => choice.value === requested);
25819
+ if (matched === void 0) {
25820
+ options.log?.info("requested effort is not in the agent catalog; keeping its own", {
25821
+ requestedEffort: requested,
25822
+ advertised: effort.choices.map((choice) => choice.value)
25823
+ });
25824
+ return;
25825
+ }
25826
+ if (matched.isDefault === true) return;
25827
+ if (!isLive(input2.expectedGeneration)) return;
25828
+ try {
25829
+ await connection.request("session/set_config_option", {
25830
+ sessionId: input2.acpSessionId,
25831
+ configId: effort.configId,
25832
+ value: requested
25833
+ });
25834
+ } catch (error62) {
25835
+ options.log?.info("setting the effort level failed; the session keeps its own", {
25836
+ requestedEffort: requested,
25837
+ configId: effort.configId,
25838
+ error: error62 instanceof Error ? error62.message : String(error62)
25839
+ });
25840
+ }
25572
25841
  };
25573
25842
  const startOnce = async (expectedGeneration) => {
25574
- const initializePayload = {
25575
- protocolVersion: PROTOCOL_VERSION,
25576
- clientCapabilities: {
25577
- fs: {
25578
- readTextFile: false,
25579
- writeTextFile: false,
25580
- ...options.clientCapabilities?.fs
25581
- },
25582
- terminal: options.clientCapabilities?.terminal ?? false
25583
- },
25584
- clientInfo: options.clientInfo
25585
- };
25843
+ const initializePayload = acpInitializePayload(options.clientInfo, options.clientCapabilities);
25586
25844
  const initialized = await connection.request(
25587
25845
  "initialize",
25588
25846
  initializePayload
@@ -25626,12 +25884,27 @@ function createAcpExecutor(options) {
25626
25884
  nextSessionId = created.sessionId;
25627
25885
  setup = created;
25628
25886
  }
25887
+ const unhandledCategories = unhandledConfigCategories(
25888
+ setup.configOptions
25889
+ );
25890
+ if (unhandledCategories.length > 0) {
25891
+ options.log?.info("acp session reported config option categories we do not handle", {
25892
+ sessionId: nextSessionId,
25893
+ categories: unhandledCategories
25894
+ });
25895
+ }
25629
25896
  const nextModelSelectionState = await applyRequestedModel({
25630
25897
  state: modelSelectionStateFromSetup(setup),
25631
25898
  acpSessionId: nextSessionId,
25632
25899
  requestedModelId: options.requestedModelId,
25633
25900
  expectedGeneration
25634
25901
  });
25902
+ await applyRequestedEffort({
25903
+ setup,
25904
+ acpSessionId: nextSessionId,
25905
+ requestedEffort: options.requestedEffort,
25906
+ expectedGeneration
25907
+ });
25635
25908
  if (options.beforeApplyResult) {
25636
25909
  await options.beforeApplyResult();
25637
25910
  }
@@ -25996,6 +26269,26 @@ function createAcpExecutor(options) {
25996
26269
  };
25997
26270
  apply();
25998
26271
  };
26272
+ const setNativeMode = async (modeId) => {
26273
+ failIfDisposed();
26274
+ const started = startResult;
26275
+ if (started === void 0) {
26276
+ throw new AcpExecutorRequestError("session/set_mode", "session has not started yet");
26277
+ }
26278
+ const configId = acpModeConfigId(started.sessionSetupResult);
26279
+ if (configId !== void 0) {
26280
+ await connection.request("session/set_config_option", {
26281
+ sessionId: started.sessionId,
26282
+ configId,
26283
+ value: modeId
26284
+ });
26285
+ return;
26286
+ }
26287
+ await connection.request("session/set_mode", {
26288
+ sessionId: started.sessionId,
26289
+ modeId
26290
+ });
26291
+ };
25999
26292
  const respondToUserInput = async (requestId, answers) => {
26000
26293
  failIfDisposed();
26001
26294
  const pending = pendingUserInputs.get(requestId);
@@ -26012,7 +26305,7 @@ function createAcpExecutor(options) {
26012
26305
  pendingUserInputs.delete(requestId);
26013
26306
  pending.resolve({
26014
26307
  kind: "answered",
26015
- answers: isRecord3(answers) ? answers : {}
26308
+ answers: isRecord5(answers) ? answers : {}
26016
26309
  });
26017
26310
  };
26018
26311
  const dispose = async () => {
@@ -26062,12 +26355,84 @@ function createAcpExecutor(options) {
26062
26355
  cancel,
26063
26356
  respondToRequest,
26064
26357
  respondToUserInput,
26358
+ setNativeMode,
26065
26359
  dispose
26066
26360
  };
26067
26361
  }
26068
26362
 
26363
+ // ../../packages/ycodium-acp-executor/src/probe.ts
26364
+ function withProbeTimeout(promise2, timeoutMs, what) {
26365
+ return new Promise((resolve2, reject) => {
26366
+ const timer = setTimeout(() => {
26367
+ reject(new Error(`${what} timed out after ${timeoutMs}ms`));
26368
+ }, timeoutMs);
26369
+ timer.unref?.();
26370
+ promise2.then(
26371
+ (value) => {
26372
+ clearTimeout(timer);
26373
+ resolve2(value);
26374
+ },
26375
+ (error62) => {
26376
+ clearTimeout(timer);
26377
+ reject(error62 instanceof Error ? error62 : new Error(String(error62)));
26378
+ }
26379
+ );
26380
+ });
26381
+ }
26382
+ var CLI_VERSION_PATTERN = /(?<![\d.])(\d+\.\d+\.\d+(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?)/;
26383
+ function parseCliVersion(output2) {
26384
+ return CLI_VERSION_PATTERN.exec(output2)?.[1] ?? null;
26385
+ }
26386
+ async function collectCappedText(stream, maxBytes) {
26387
+ const decoder = new TextDecoder();
26388
+ let text = "";
26389
+ let total = 0;
26390
+ for await (const chunk of stream) {
26391
+ total += chunk.byteLength;
26392
+ if (total <= maxBytes) text += decoder.decode(chunk, { stream: true });
26393
+ }
26394
+ return { text: text + decoder.decode(), overflowed: total > maxBytes };
26395
+ }
26396
+ async function collectProbeProcess(child, input2) {
26397
+ try {
26398
+ await child.closeStdin().catch(() => void 0);
26399
+ const [stdout, stderr, exitCode] = await withProbeTimeout(
26400
+ Promise.all([
26401
+ collectCappedText(child.stdout, input2.maxOutputBytes),
26402
+ collectCappedText(child.stderr, input2.maxOutputBytes),
26403
+ child.exited
26404
+ ]),
26405
+ input2.timeoutMs,
26406
+ input2.what
26407
+ );
26408
+ return { stdout: stdout.overflowed ? void 0 : stdout.text, stderr: stderr.text, exitCode };
26409
+ } finally {
26410
+ await child.kill("SIGTERM").catch(() => void 0);
26411
+ }
26412
+ }
26413
+ var MAX_VERSION_OUTPUT_BYTES = 64 * 1024;
26414
+ async function probeCliVersion(input2) {
26415
+ let child;
26416
+ try {
26417
+ child = await input2.spawn();
26418
+ } catch {
26419
+ return null;
26420
+ }
26421
+ try {
26422
+ const run = await collectProbeProcess(child, {
26423
+ timeoutMs: input2.timeoutMs,
26424
+ maxOutputBytes: MAX_VERSION_OUTPUT_BYTES,
26425
+ what: "--version"
26426
+ });
26427
+ if (run.exitCode !== 0) return null;
26428
+ return parseCliVersion(run.stdout ?? "") ?? parseCliVersion(run.stderr);
26429
+ } catch {
26430
+ return null;
26431
+ }
26432
+ }
26433
+
26069
26434
  // src/config.ts
26070
- function isRecord4(value) {
26435
+ function isRecord6(value) {
26071
26436
  return typeof value === "object" && value !== null && !Array.isArray(value);
26072
26437
  }
26073
26438
  function readString(value) {
@@ -26080,7 +26445,7 @@ function readEnvironment(value) {
26080
26445
  if (!Array.isArray(value)) return {};
26081
26446
  const env = {};
26082
26447
  for (const entry of value) {
26083
- if (!isRecord4(entry)) continue;
26448
+ if (!isRecord6(entry)) continue;
26084
26449
  const name = readTrimmed(entry["name"]);
26085
26450
  if (name.length === 0) continue;
26086
26451
  env[name] = readString(entry["value"]);
@@ -26088,23 +26453,23 @@ function readEnvironment(value) {
26088
26453
  return env;
26089
26454
  }
26090
26455
  function parseKimiInstanceConfig(config2) {
26091
- if (!isRecord4(config2)) {
26456
+ if (!isRecord6(config2)) {
26092
26457
  return {
26093
26458
  binaryPath: "",
26094
- launchArgs: "",
26459
+ launchArgs: KIMI_CARD.launchArgs,
26095
26460
  homePath: "",
26096
- homeEnvVar: "",
26461
+ homeEnvVar: KIMI_CARD.homeEnvVar,
26097
26462
  environment: {},
26098
26463
  envelopeDriver: void 0
26099
26464
  };
26100
26465
  }
26101
- const nested = isRecord4(config2["config"]) ? config2["config"] : void 0;
26466
+ const nested = isRecord6(config2["config"]) ? config2["config"] : void 0;
26102
26467
  const settings = nested ?? config2;
26103
26468
  return {
26104
26469
  binaryPath: readTrimmed(settings["binaryPath"]),
26105
- launchArgs: readString(settings["launchArgs"]),
26470
+ launchArgs: readString(settings["launchArgs"]) || KIMI_CARD.launchArgs,
26106
26471
  homePath: readTrimmed(settings["homePath"]) || readTrimmed(config2["homePath"]),
26107
- homeEnvVar: readTrimmed(settings["homeEnvVar"]) || readTrimmed(config2["homeEnvVar"]),
26472
+ homeEnvVar: KIMI_CARD.homeEnvVar,
26108
26473
  environment: readEnvironment(config2["environment"]),
26109
26474
  envelopeDriver: readTrimmed(config2["driver"]) || void 0
26110
26475
  };
@@ -26161,13 +26526,51 @@ function tokenizeLaunchArgs(value) {
26161
26526
  return tokens;
26162
26527
  }
26163
26528
 
26164
- // src/contextWindow.ts
26165
- import * as NodeFSP from "node:fs/promises";
26529
+ // src/kimi.ts
26530
+ import * as NodeFS from "node:fs";
26166
26531
  import * as NodePath from "node:path";
26532
+ var KIMI_USER_INPUT_TITLES = /* @__PURE__ */ new Set(["AskUserQuestion", "ExitPlanMode"]);
26533
+ var isRecord7 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
26534
+ function questionTextFromContent(content) {
26535
+ if (!Array.isArray(content)) return "";
26536
+ const parts = [];
26537
+ for (const entry of content) {
26538
+ if (!isRecord7(entry) || entry["type"] !== "content") continue;
26539
+ const inner = entry["content"];
26540
+ if (!isRecord7(inner) || inner["type"] !== "text") continue;
26541
+ const text = inner["text"];
26542
+ if (typeof text === "string") parts.push(text);
26543
+ }
26544
+ return parts.join("\n\n").trim();
26545
+ }
26546
+ function kimiPermissionUserInput(request) {
26547
+ const title = request.toolCall.title;
26548
+ if (typeof title !== "string" || !KIMI_USER_INPUT_TITLES.has(title)) return void 0;
26549
+ const ids = new Set(request.options.map((option) => option.optionId));
26550
+ if (request.options.length === 0 || ids.size !== request.options.length) {
26551
+ throw new Error("Kimi interaction must provide distinct response options.");
26552
+ }
26553
+ const question = questionTextFromContent(request.toolCall.content);
26554
+ if (question.length === 0) {
26555
+ throw new Error("Kimi interaction is missing a question or plan.");
26556
+ }
26557
+ return {
26558
+ id: request.toolCall.toolCallId,
26559
+ header: title,
26560
+ question,
26561
+ kind: title === "ExitPlanMode" ? "plan" : "question",
26562
+ allowFreeform: false,
26563
+ multiSelect: false,
26564
+ options: request.options.map((option) => ({
26565
+ id: option.optionId,
26566
+ label: option.name,
26567
+ description: ""
26568
+ }))
26569
+ };
26570
+ }
26167
26571
  var WIRE_LOG_SEGMENTS = ["agents", "main", "wire.jsonl"];
26168
26572
  var SESSIONS_DIR = "sessions";
26169
26573
  var TAIL_SCAN_BYTES = 256 * 1024;
26170
- var isRecord5 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
26171
26574
  var positiveInt = (value) => typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.trunc(value) : void 0;
26172
26575
  function parseKimiContextWindow(contents) {
26173
26576
  let usedTokens;
@@ -26181,7 +26584,7 @@ function parseKimiContextWindow(contents) {
26181
26584
  } catch {
26182
26585
  continue;
26183
26586
  }
26184
- if (!isRecord5(record2)) continue;
26587
+ if (!isRecord7(record2)) continue;
26185
26588
  const type = record2["type"];
26186
26589
  if (type === "token_counting.turn_recorded" || type === "token_counting.measured") {
26187
26590
  usedTokens = positiveInt(record2["tokens"]) ?? usedTokens;
@@ -26191,31 +26594,35 @@ function parseKimiContextWindow(contents) {
26191
26594
  maxTokens = positiveInt(record2["maxTokens"]) ?? maxTokens;
26192
26595
  }
26193
26596
  }
26194
- return usedTokens === void 0 ? void 0 : { usedTokens, maxTokens };
26597
+ if (usedTokens === void 0) return void 0;
26598
+ return maxTokens === void 0 ? { usedTokens } : { usedTokens, maxTokens };
26195
26599
  }
26196
- async function readTail(filePath) {
26600
+ function readTail(filePath) {
26197
26601
  let handle;
26198
26602
  try {
26199
- handle = await NodeFSP.open(filePath, "r");
26200
- const info = await handle.stat();
26201
- const size = info.size;
26603
+ const size = NodeFS.statSync(filePath).size;
26202
26604
  if (!Number.isFinite(size) || size <= 0) return void 0;
26203
- const offset = size > TAIL_SCAN_BYTES ? size - TAIL_SCAN_BYTES : 0;
26204
- const length = size - offset;
26605
+ const length = size > TAIL_SCAN_BYTES ? TAIL_SCAN_BYTES : size;
26205
26606
  const buffer = Buffer.alloc(length);
26206
- await handle.read(buffer, 0, length, offset);
26607
+ handle = NodeFS.openSync(filePath, "r");
26608
+ NodeFS.readSync(handle, buffer, 0, length, size - length);
26207
26609
  return buffer.toString("utf8");
26208
26610
  } catch {
26209
26611
  return void 0;
26210
26612
  } finally {
26211
- await handle?.close().catch(() => void 0);
26613
+ if (handle !== void 0) {
26614
+ try {
26615
+ NodeFS.closeSync(handle);
26616
+ } catch {
26617
+ }
26618
+ }
26212
26619
  }
26213
26620
  }
26214
- async function findKimiSessionWireLog(input2) {
26621
+ function findKimiSessionWireLog(input2) {
26215
26622
  const sessionsRoot = NodePath.join(input2.homePath, SESSIONS_DIR);
26216
26623
  let workspaces;
26217
26624
  try {
26218
- workspaces = await NodeFSP.readdir(sessionsRoot);
26625
+ workspaces = NodeFS.readdirSync(sessionsRoot);
26219
26626
  } catch {
26220
26627
  return void 0;
26221
26628
  }
@@ -26226,126 +26633,175 @@ async function findKimiSessionWireLog(input2) {
26226
26633
  input2.acpSessionId,
26227
26634
  ...WIRE_LOG_SEGMENTS
26228
26635
  );
26229
- try {
26230
- await NodeFSP.access(candidate);
26231
- return candidate;
26232
- } catch {
26233
- }
26636
+ if (NodeFS.existsSync(candidate)) return candidate;
26234
26637
  }
26235
26638
  return void 0;
26236
26639
  }
26237
- async function readKimiContextWindow(input2) {
26640
+ function readKimiContextWindow(input2) {
26238
26641
  if (input2.homePath.trim().length === 0) return void 0;
26239
- const wireLog = await findKimiSessionWireLog(input2);
26642
+ const wireLog = findKimiSessionWireLog(input2);
26240
26643
  if (wireLog === void 0) return void 0;
26241
- const contents = await readTail(wireLog);
26644
+ const contents = readTail(wireLog);
26242
26645
  return contents === void 0 ? void 0 : parseKimiContextWindow(contents);
26243
26646
  }
26244
26647
 
26245
- // src/permission.ts
26246
- var isRecord6 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
26247
- var nonEmpty = (value) => {
26248
- if (typeof value !== "string") return void 0;
26249
- return value.length > 0 ? value : void 0;
26250
- };
26251
- var trimmedNonEmpty = (value) => {
26252
- if (typeof value !== "string") return void 0;
26253
- return value.trim().length > 0 ? value : void 0;
26254
- };
26255
- var questionText = (content) => {
26256
- if (!Array.isArray(content)) return "";
26257
- const texts = [];
26258
- for (const entry of content) {
26259
- if (!isRecord6(entry)) continue;
26260
- if (entry["type"] !== "content") continue;
26261
- const inner = entry["content"];
26262
- if (!isRecord6(inner)) continue;
26263
- if (inner["type"] !== "text") continue;
26264
- const text = inner["text"];
26265
- if (typeof text === "string") texts.push(text);
26648
+ // src/kimiAccount.ts
26649
+ import * as NodeFS2 from "node:fs";
26650
+ import * as NodeOS from "node:os";
26651
+ import * as NodePath2 from "node:path";
26652
+ function kimiCredentialsPath(resolvedHomePath) {
26653
+ const home = resolvedHomePath.trim().length > 0 ? resolvedHomePath : NodePath2.join(NodeOS.homedir(), ".kimi-code");
26654
+ return NodePath2.join(home, "credentials", "kimi-code.json");
26655
+ }
26656
+ var nonEmptyString = (value) => typeof value === "string" && value.length > 0;
26657
+ function readKimiAuthState(resolvedHomePath) {
26658
+ let raw;
26659
+ try {
26660
+ raw = NodeFS2.readFileSync(kimiCredentialsPath(resolvedHomePath), "utf8");
26661
+ } catch (error62) {
26662
+ return error62.code === "ENOENT" ? "signed-out" : "unreadable";
26266
26663
  }
26267
- return texts.join("\n\n").trim();
26268
- };
26269
- var KIMI_QUESTION_TITLE = "AskUserQuestion";
26270
- var KIMI_PLAN_TITLE = "ExitPlanMode";
26271
- var kimiPermissionUserInput = (request) => {
26272
- const title = request.toolCall.title;
26273
- if (title !== KIMI_QUESTION_TITLE && title !== KIMI_PLAN_TITLE) return void 0;
26274
- const question = questionText(request.toolCall.content);
26275
- const optionIds = new Set(request.options.map((option) => option.optionId));
26276
- if (request.options.length === 0 || optionIds.size !== request.options.length) {
26277
- throw new Error("Kimi interaction must provide distinct response options.");
26664
+ let parsed;
26665
+ try {
26666
+ parsed = JSON.parse(raw);
26667
+ } catch {
26668
+ return "unreadable";
26278
26669
  }
26279
- const id = trimmedNonEmpty(request.toolCall.toolCallId);
26280
- const options = [];
26281
- for (const option of request.options) {
26282
- const optionId = nonEmpty(option.optionId);
26283
- const label = trimmedNonEmpty(option.name);
26284
- if (optionId === void 0 || label === void 0) {
26285
- throw new Error("Kimi interaction is missing a question, plan, or response option identity.");
26286
- }
26287
- options.push({ id: optionId, label, description: "" });
26670
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return "unreadable";
26671
+ const record2 = parsed;
26672
+ return nonEmptyString(record2["refresh_token"]) || nonEmptyString(record2["access_token"]) ? "signed-in" : "signed-out";
26673
+ }
26674
+
26675
+ // src/kimiModels.ts
26676
+ import {
26677
+ ExecutorSpawnRefusedError
26678
+ } from "@ycodium-ai/plugin-api/executor";
26679
+
26680
+ // src/probeModels.ts
26681
+ var ACP_EFFORT_OPTION_ID = "reasoning";
26682
+ var probeFailureDetail = (error62) => (error62 instanceof Error ? error62.message : String(error62)).trim() || void 0;
26683
+
26684
+ // src/kimiModels.ts
26685
+ var PROBE_TIMEOUT_MS = 15e3;
26686
+ var MAX_OUTPUT_BYTES = 1024 * 1024;
26687
+ var KIMI_MODEL_LIST_ARGS = ["provider", "list", "--json"];
26688
+ function isRecord8(value) {
26689
+ return typeof value === "object" && value !== null && !Array.isArray(value);
26690
+ }
26691
+ function asString3(value) {
26692
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
26693
+ }
26694
+ function positiveInt2(value) {
26695
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.trunc(value) : void 0;
26696
+ }
26697
+ function kimiProbeModels(stdout) {
26698
+ let parsed;
26699
+ try {
26700
+ parsed = JSON.parse(stdout);
26701
+ } catch {
26702
+ return void 0;
26288
26703
  }
26289
- if (id === void 0 || question.length === 0) {
26290
- throw new Error("Kimi interaction is missing a question, plan, or response option identity.");
26704
+ if (!isRecord8(parsed)) return void 0;
26705
+ const models = parsed["models"];
26706
+ if (!isRecord8(models)) return void 0;
26707
+ const out = [];
26708
+ for (const [slug, entry] of Object.entries(models)) {
26709
+ const trimmed = slug.trim();
26710
+ if (trimmed.length === 0 || !isRecord8(entry)) continue;
26711
+ const name = asString3(entry["displayName"]);
26712
+ const contextWindow = positiveInt2(entry["maxContextSize"]);
26713
+ out.push({
26714
+ slug: trimmed,
26715
+ ...name === void 0 ? {} : { name },
26716
+ ...contextWindow === void 0 ? {} : { contextWindow }
26717
+ });
26291
26718
  }
26292
- return {
26293
- id,
26294
- header: title,
26295
- question,
26296
- kind: title === KIMI_PLAN_TITLE ? "plan" : "question",
26297
- allowFreeform: false,
26298
- multiSelect: false,
26299
- options
26300
- };
26301
- };
26302
-
26303
- // src/streams.ts
26304
- import * as NodeStream2 from "node:stream";
26305
- function nodeStreamsFromHandle(handle) {
26306
- const readable = new NodeStream2.PassThrough();
26307
- void (async () => {
26308
- try {
26309
- for await (const chunk of handle.stdout) {
26310
- if (!readable.writable) return;
26311
- if (!readable.write(chunk)) {
26312
- await new Promise((resolve2) => {
26313
- const onDone = () => {
26314
- readable.off("drain", onDone);
26315
- readable.off("close", onDone);
26316
- resolve2();
26317
- };
26318
- readable.once("drain", onDone);
26319
- readable.once("close", onDone);
26320
- });
26321
- }
26322
- }
26323
- } catch (error62) {
26324
- readable.destroy(error62 instanceof Error ? error62 : new Error(String(error62)));
26325
- return;
26326
- }
26327
- if (readable.writable) readable.end();
26328
- })();
26329
- void (async () => {
26330
- for await (const _chunk of handle.stderr) {
26331
- }
26332
- })().catch(() => void 0);
26333
- const writable = new NodeStream2.Writable({
26334
- write(chunk, _encoding, callback) {
26335
- const bytes = typeof chunk === "string" ? Buffer.from(chunk) : chunk;
26336
- handle.writeStdin(bytes).then(
26337
- () => callback(),
26338
- (error62) => callback(error62 instanceof Error ? error62 : new Error(String(error62)))
26339
- );
26340
- },
26341
- final(callback) {
26342
- handle.closeStdin().then(
26343
- () => callback(),
26344
- (error62) => callback(error62 instanceof Error ? error62 : new Error(String(error62)))
26345
- );
26719
+ return out;
26720
+ }
26721
+ async function probeKimiModels(input2) {
26722
+ const { recipe, host, env } = input2;
26723
+ let handle;
26724
+ try {
26725
+ handle = await host.process.spawn({
26726
+ program: recipe.binaryPath,
26727
+ // 实例信封里的启动参数(Kimi 那行预填 `acp`)在这一栏用不上:读配置要换成
26728
+ // `provider list --json`,留着 `acp` 就等于让它起 ACP 服务然后什么都不问。
26729
+ args: KIMI_MODEL_LIST_ARGS,
26730
+ cwd: process.cwd(),
26731
+ owner: { bindingId: host.bindingId },
26732
+ ...Object.keys(env).length > 0 ? { env } : {}
26733
+ });
26734
+ } catch (error62) {
26735
+ if (error62 instanceof ExecutorSpawnRefusedError) throw error62;
26736
+ return { kind: "not-started", detail: probeFailureDetail(error62) };
26737
+ }
26738
+ try {
26739
+ const run = await collectProbeProcess(handle, {
26740
+ timeoutMs: input2.timeoutMs ?? PROBE_TIMEOUT_MS,
26741
+ // 这份 JSON 实测 1.7KB;封顶是防 CLI 抽风刷日志把内存吃掉。
26742
+ maxOutputBytes: MAX_OUTPUT_BYTES,
26743
+ what: "kimi provider list"
26744
+ });
26745
+ if (run.exitCode !== 0) {
26746
+ const stderr = run.stderr.trim();
26747
+ return {
26748
+ kind: "failed",
26749
+ detail: stderr.length > 0 ? stderr : run.exitCode === null ? "kimi provider list was killed by a signal" : `kimi provider list exited with code ${run.exitCode}`
26750
+ };
26346
26751
  }
26752
+ return {
26753
+ kind: "answered",
26754
+ models: run.stdout === void 0 ? void 0 : kimiProbeModels(run.stdout)
26755
+ };
26756
+ } catch (error62) {
26757
+ return { kind: "failed", detail: probeFailureDetail(error62) };
26758
+ }
26759
+ }
26760
+ async function probeKimiVersion(input2) {
26761
+ const { recipe, host, env } = input2;
26762
+ return probeCliVersion({
26763
+ spawn: () => host.process.spawn({
26764
+ program: recipe.binaryPath,
26765
+ args: ["--version"],
26766
+ cwd: process.cwd(),
26767
+ owner: { bindingId: host.bindingId },
26768
+ ...Object.keys(env).length > 0 ? { env } : {}
26769
+ }),
26770
+ timeoutMs: PROBE_TIMEOUT_MS
26347
26771
  });
26348
- return { readable, writable };
26772
+ }
26773
+ async function probeKimi(input2) {
26774
+ const [listed, version2] = await Promise.all([probeKimiModels(input2), probeKimiVersion(input2)]);
26775
+ switch (listed.kind) {
26776
+ case "not-started":
26777
+ return { kind: "not-started", detail: listed.detail };
26778
+ case "failed":
26779
+ return { kind: "failed", detail: listed.detail, version: version2 };
26780
+ case "answered":
26781
+ return {
26782
+ kind: "answered",
26783
+ version: version2,
26784
+ models: listed.models,
26785
+ nativeModes: [],
26786
+ currentNativeModeId: void 0
26787
+ };
26788
+ }
26789
+ }
26790
+
26791
+ // src/runtimeModeApproval.ts
26792
+ function acpRuntimeModeAutoApproves(runtimeMode, toolKind) {
26793
+ switch (runtimeMode) {
26794
+ case "full-access":
26795
+ return true;
26796
+ case "auto-accept-edits":
26797
+ return toolKind === "edit";
26798
+ case "approval-required":
26799
+ case "auto":
26800
+ return false;
26801
+ }
26802
+ }
26803
+ function acpSessionModeToApply(requestedNativeModeId, startedModeId) {
26804
+ return requestedNativeModeId !== void 0 && requestedNativeModeId !== startedModeId ? requestedNativeModeId : void 0;
26349
26805
  }
26350
26806
 
26351
26807
  // src/kimiInstance.ts
@@ -26387,24 +26843,12 @@ var EventBus = class {
26387
26843
  }
26388
26844
  };
26389
26845
  function expandHomePath(value) {
26390
- if (value === "~") return NodeOS.homedir();
26846
+ if (value === "~") return NodeOS2.homedir();
26391
26847
  if (value.startsWith("~/") || value.startsWith("~\\")) {
26392
- return NodePath2.join(NodeOS.homedir(), value.slice(2));
26848
+ return NodePath3.join(NodeOS2.homedir(), value.slice(2));
26393
26849
  }
26394
26850
  return value;
26395
26851
  }
26396
- function resolveHomePath(value) {
26397
- const trimmed = value.trim();
26398
- if (trimmed.length === 0) return "";
26399
- return NodePath2.resolve(expandHomePath(trimmed));
26400
- }
26401
- function compactEnv(env) {
26402
- const out = {};
26403
- for (const [key, value] of Object.entries(env)) {
26404
- if (value !== void 0) out[key] = value;
26405
- }
26406
- return out;
26407
- }
26408
26852
  function modelIdFromSelection(selection) {
26409
26853
  if (typeof selection !== "object" || selection === null || Array.isArray(selection)) {
26410
26854
  return void 0;
@@ -26412,18 +26856,39 @@ function modelIdFromSelection(selection) {
26412
26856
  const model = selection["model"];
26413
26857
  return typeof model === "string" ? model.trim() || void 0 : void 0;
26414
26858
  }
26415
- function buildSpawnEnv(recipe, launch, resolvedHomePath) {
26859
+ function buildSpawnEnv(recipe, configuredHomePath) {
26860
+ const homePath = configuredHomePath.trim();
26416
26861
  const homeEnvVar = recipe.homeEnvVar.trim();
26417
- const homeInjection = resolvedHomePath.length > 0 && homeEnvVar.length > 0 ? { [homeEnvVar]: resolvedHomePath } : {};
26418
- return {
26419
- ...compactEnv(launch.processEnv),
26420
- ...homeInjection,
26421
- ...recipe.environment
26422
- };
26862
+ const homeInjection = homePath.length > 0 && homeEnvVar.length > 0 ? { [homeEnvVar]: expandHomePath(homePath) } : {};
26863
+ return mergeLaunchEnv({
26864
+ accountHome: homeInjection,
26865
+ instanceTable: recipe.environment
26866
+ });
26423
26867
  }
26424
26868
  function nowIso() {
26425
26869
  return (/* @__PURE__ */ new Date()).toISOString();
26426
26870
  }
26871
+ function kimiContinuationKey(recipe) {
26872
+ const homePath = recipe.homePath.trim();
26873
+ const resolvedHomePath = homePath.length > 0 ? NodePath3.resolve(expandHomePath(homePath)) : "";
26874
+ return `kimiCode:home:${recipe.binaryPath.trim()}\0${resolvedHomePath}`;
26875
+ }
26876
+ function resolveHomePath(recipe) {
26877
+ const envVar = recipe.homeEnvVar.trim();
26878
+ const fromEnvironment = envVar.length > 0 ? recipe.environment[envVar]?.trim() : void 0;
26879
+ if (fromEnvironment !== void 0 && fromEnvironment.length > 0) {
26880
+ return expandHomePath(fromEnvironment);
26881
+ }
26882
+ const configured = recipe.homePath.trim();
26883
+ return configured.length > 0 ? expandHomePath(configured) : "";
26884
+ }
26885
+ function kimiHooks(recipe) {
26886
+ const homePath = resolveHomePath(recipe);
26887
+ return {
26888
+ permissionUserInput: kimiPermissionUserInput,
26889
+ readContextWindow: ({ sessionId }) => readKimiContextWindow({ homePath, acpSessionId: sessionId })
26890
+ };
26891
+ }
26427
26892
  async function stopBooked(booked) {
26428
26893
  await booked.executor.dispose().catch(() => void 0);
26429
26894
  await booked.handle.kill("SIGTERM").catch(() => void 0);
@@ -26447,21 +26912,67 @@ var createKimiInstance = (config2, host) => {
26447
26912
  version: null,
26448
26913
  readiness: "error",
26449
26914
  auth: { status: "unknown" },
26450
- detail: "Kimi program path is not configured"
26915
+ detail: "Kimi Code program path is not configured"
26451
26916
  };
26452
26917
  }
26453
- return {
26454
- installed: true,
26455
- version: null,
26456
- readiness: "unchecked",
26457
- auth: { status: "unknown" }
26918
+ const env = buildSpawnEnv(recipe, recipe.homePath);
26919
+ const outcome = await probeKimi({ recipe, host, env });
26920
+ const auth = () => {
26921
+ switch (readKimiAuthState(resolveHomePath(recipe))) {
26922
+ case "signed-in":
26923
+ return { status: "authenticated" };
26924
+ case "signed-out":
26925
+ return { status: "unauthenticated" };
26926
+ case "unreadable":
26927
+ return { status: "unknown" };
26928
+ }
26458
26929
  };
26930
+ switch (outcome.kind) {
26931
+ case "not-started":
26932
+ return {
26933
+ installed: false,
26934
+ version: null,
26935
+ readiness: "error",
26936
+ auth: { status: "unknown" },
26937
+ ...outcome.detail === void 0 ? {} : { detail: outcome.detail }
26938
+ };
26939
+ case "failed":
26940
+ return {
26941
+ installed: true,
26942
+ version: outcome.version,
26943
+ readiness: "error",
26944
+ auth: auth(),
26945
+ ...outcome.detail === void 0 ? {} : { detail: outcome.detail }
26946
+ };
26947
+ case "auth-required":
26948
+ return {
26949
+ installed: true,
26950
+ version: outcome.version,
26951
+ readiness: "ready",
26952
+ auth: { status: "unauthenticated" }
26953
+ };
26954
+ case "answered":
26955
+ return {
26956
+ installed: true,
26957
+ version: outcome.version,
26958
+ readiness: "ready",
26959
+ auth: auth(),
26960
+ ...outcome.models === void 0 ? {} : { models: outcome.models },
26961
+ // 清单与当前档必须同进同出:只报清单,界面在清单里找不到当前档会整个不画那一格。
26962
+ ...outcome.nativeModes.length === 0 ? {} : {
26963
+ nativeModes: outcome.nativeModes,
26964
+ ...outcome.currentNativeModeId === void 0 ? {} : { currentNativeModeId: outcome.currentNativeModeId }
26965
+ }
26966
+ };
26967
+ }
26459
26968
  },
26969
+ // 暂不处理 input.readOnlyDirectories(有意为之):kimi 经共享的 ycodium-acp-executor 起会话,
26970
+ // 还没用真机确认这家 CLI 对 cwd 之外的读怎么处理、要不要放行。确认之后再接这一栏。
26460
26971
  startSession: async (input2, signal) => {
26461
26972
  if (disposed) throw new Error("instance disposed");
26462
26973
  if (signal?.aborted) throw new Error("aborted");
26463
26974
  if (recipe.binaryPath.length === 0) {
26464
- throw new Error("Kimi program path is not configured");
26975
+ throw new Error("Kimi Code program path is not configured");
26465
26976
  }
26466
26977
  const cwd = input2.cwd ?? host.workspacePath(input2.threadId);
26467
26978
  if (cwd.trim() === "") throw new Error("workspace path missing");
@@ -26470,8 +26981,7 @@ var createKimiInstance = (config2, host) => {
26470
26981
  await stopBooked(existing);
26471
26982
  threads.delete(input2.threadId);
26472
26983
  }
26473
- const resolvedHomePath = resolveHomePath(input2.launch.homePath?.trim() || recipe.homePath);
26474
- const env = buildSpawnEnv(recipe, input2.launch, resolvedHomePath);
26984
+ const env = buildSpawnEnv(recipe, recipe.homePath);
26475
26985
  const handle = await host.process.spawn({
26476
26986
  program: recipe.binaryPath,
26477
26987
  args: tokenizeLaunchArgs(recipe.launchArgs),
@@ -26481,31 +26991,30 @@ var createKimiInstance = (config2, host) => {
26481
26991
  });
26482
26992
  const { readable, writable } = nodeStreamsFromHandle(handle);
26483
26993
  const resume = input2.discardResumeCursor === true ? void 0 : parseAcpResumeCursor(input2.resumeCursor, RESUME_SCHEMA_VERSION);
26484
- const requestedModelId = input2.launch.requestedModelId?.trim() || modelIdFromSelection(input2.modelSelection);
26994
+ const requestedModelId = modelIdFromSelection(input2.modelSelection);
26995
+ const requestedEffort = modelSelectionStringOption(
26996
+ input2.modelSelection,
26997
+ ACP_EFFORT_OPTION_ID
26998
+ );
26999
+ const hooks = kimiHooks(recipe);
27000
+ const autoApprove = input2.nativeModeId === void 0 ? (request) => acpRuntimeModeAutoApproves(
27001
+ threads.get(input2.threadId)?.session.runtimeMode ?? input2.runtimeMode,
27002
+ request.kind
27003
+ ) : void 0;
26485
27004
  const executor = createAcpExecutor({
26486
27005
  readable,
26487
27006
  writable,
27007
+ ...requestedEffort === void 0 ? {} : { requestedEffort },
26488
27008
  cwd,
26489
27009
  threadId: input2.threadId,
26490
27010
  provider: KIMI_DRIVER_KIND,
26491
27011
  providerInstanceId: host.instanceId,
26492
27012
  clientInfo: CLIENT_INFO,
26493
- hooks: {
26494
- permissionUserInput: kimiPermissionUserInput,
26495
- readContextWindow: async ({ sessionId }) => {
26496
- const reading = await readKimiContextWindow({
26497
- homePath: resolvedHomePath,
26498
- acpSessionId: sessionId
26499
- });
26500
- if (reading === void 0) return void 0;
26501
- return {
26502
- usedTokens: reading.usedTokens,
26503
- ...reading.maxTokens === void 0 ? {} : { maxTokens: reading.maxTokens }
26504
- };
26505
- }
26506
- },
27013
+ log: host.log,
26507
27014
  ...requestedModelId === void 0 ? {} : { requestedModelId },
26508
27015
  ...input2.bindingGeneration === void 0 ? {} : { originBindingGeneration: input2.bindingGeneration },
27016
+ hooks,
27017
+ ...autoApprove === void 0 ? {} : { autoApprove },
26509
27018
  ...resume === void 0 ? {} : { resumeSessionId: resume.sessionId }
26510
27019
  });
26511
27020
  const forwarding = (async () => {
@@ -26515,6 +27024,13 @@ var createKimiInstance = (config2, host) => {
26515
27024
  })();
26516
27025
  try {
26517
27026
  const started = await executor.start();
27027
+ const modeToApply = acpSessionModeToApply(
27028
+ input2.nativeModeId,
27029
+ acpCurrentModeId(started.sessionSetupResult)
27030
+ );
27031
+ if (modeToApply !== void 0) {
27032
+ await executor.setNativeMode(modeToApply);
27033
+ }
26518
27034
  await yieldTick();
26519
27035
  const createdAt = nowIso();
26520
27036
  const resumeCursor = {
@@ -26526,6 +27042,7 @@ var createKimiInstance = (config2, host) => {
26526
27042
  providerInstanceId: host.instanceId,
26527
27043
  status: "idle",
26528
27044
  runtimeMode: input2.runtimeMode,
27045
+ ...input2.nativeModeId === void 0 ? {} : { nativeModeId: input2.nativeModeId },
26529
27046
  cwd,
26530
27047
  threadId: input2.threadId,
26531
27048
  createdAt,
@@ -26609,6 +27126,22 @@ var createKimiInstance = (config2, host) => {
26609
27126
  const booked = requireThread(threadId);
26610
27127
  await booked.executor.respondToUserInput(requestId, answers);
26611
27128
  },
27129
+ /**
27130
+ * 切这只手自报的审批档。`nativeModeId` 是它自己报的 id(探测时原样上报的那些),
27131
+ * 一路不翻译成宿主四档。
27132
+ *
27133
+ * 这是 `setRuntimeMode` 做不到的那一半:那个只改插件自己的会话记录,CLI 收不到
27134
+ * (`runtime-mode-never-reaches-cli` 记的那族缺陷)。
27135
+ */
27136
+ setNativeMode: async (threadId, nativeModeId) => {
27137
+ const booked = requireThread(threadId);
27138
+ await booked.executor.setNativeMode(nativeModeId);
27139
+ booked.session = {
27140
+ ...booked.session,
27141
+ nativeModeId,
27142
+ updatedAt: nowIso()
27143
+ };
27144
+ },
26612
27145
  setRuntimeMode: async (threadId, runtimeMode) => {
26613
27146
  const booked = requireThread(threadId);
26614
27147
  booked.session = {
@@ -26628,6 +27161,7 @@ var createKimiInstance = (config2, host) => {
26628
27161
  return { threadId, turns: booked.turns };
26629
27162
  },
26630
27163
  events,
27164
+ continuationKey: kimiContinuationKey(recipe),
26631
27165
  dispose: async () => {
26632
27166
  if (disposed) return;
26633
27167
  disposed = true;
@@ -26655,7 +27189,7 @@ var index_default = definePlugin({
26655
27189
  apiVersion: 2,
26656
27190
  executors,
26657
27191
  activate(ctx) {
26658
- ctx.log.info("kimi executor ready");
27192
+ ctx.log.info("Kimi Code executor ready");
26659
27193
  }
26660
27194
  });
26661
27195
  export {