@ycodium-ai/agent-kimi 0.2.2092 → 0.2.2488

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) {
@@ -24359,7 +24365,7 @@ function makeAcpContentDeltaEvent(input2) {
24359
24365
  ...input2.turnId !== void 0 ? { turnId: input2.turnId } : {},
24360
24366
  ...input2.itemId ? { itemId: input2.itemId } : {},
24361
24367
  payload: {
24362
- streamKind: "assistant_text",
24368
+ streamKind: input2.streamKind ?? "assistant_text",
24363
24369
  delta: input2.text
24364
24370
  },
24365
24371
  raw: {
@@ -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({
@@ -25043,21 +25251,24 @@ function parseSessionUpdateEvent(params) {
25043
25251
  }
25044
25252
  break;
25045
25253
  }
25046
- case "agent_message_chunk": {
25047
- const content = isRecord3(upd["content"]) ? upd["content"] : void 0;
25254
+ case "agent_message_chunk":
25255
+ case "agent_thought_chunk": {
25256
+ const content = isRecord5(upd["content"]) ? upd["content"] : void 0;
25048
25257
  const contentType = content?.["type"];
25258
+ const streamKind = kind === "agent_thought_chunk" ? "reasoning_text" : "assistant_text";
25049
25259
  if (contentType === "text") {
25050
25260
  const text = typeof content?.["text"] === "string" ? content["text"] : "";
25051
25261
  if (text.length > 0) {
25052
25262
  events.push({
25053
25263
  _tag: "ContentDelta",
25054
25264
  text,
25265
+ streamKind,
25055
25266
  rawPayload: params
25056
25267
  });
25057
25268
  }
25058
25269
  break;
25059
25270
  }
25060
- if (content && contentType !== "audio") {
25271
+ if (streamKind === "assistant_text" && content && contentType !== "audio") {
25061
25272
  events.push({
25062
25273
  _tag: "AssistantContentBlock",
25063
25274
  content,
@@ -25109,16 +25320,18 @@ function normalizeModelId(value) {
25109
25320
  return value?.trim() || void 0;
25110
25321
  }
25111
25322
  function modelSelectionStateFromSetup(setup) {
25112
- const sessionModels = isRecord3(setup.models) ? setup.models : void 0;
25323
+ const sessionModels = isRecord5(setup.models) ? setup.models : void 0;
25113
25324
  const modelConfigId = extractModelConfigId(setup);
25114
25325
  const strategy = sessionModels === void 0 && modelConfigId !== void 0 ? "config-option" : "session/set_model";
25115
- 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;
25116
25328
  const currentModelId = strategy === "config-option" ? typeof configModel === "string" ? normalizeModelId(configModel) : void 0 : typeof sessionModels?.["currentModelId"] === "string" ? normalizeModelId(sessionModels["currentModelId"]) : void 0;
25117
25329
  return {
25118
25330
  strategy,
25119
25331
  sessionModelsAdvertised: sessionModels !== void 0,
25120
25332
  modelConfigId,
25121
- currentModelId
25333
+ currentModelId,
25334
+ advertisedModelValues: strategy === "config-option" ? modelConfigOption === void 0 ? [] : collectSessionConfigOptionValues(modelConfigOption) : collectSessionModelIds(sessionModels)
25122
25335
  };
25123
25336
  }
25124
25337
  function createAcpExecutor(options) {
@@ -25137,8 +25350,10 @@ function createAcpExecutor(options) {
25137
25350
  strategy: "session/set_model",
25138
25351
  sessionModelsAdvertised: false,
25139
25352
  modelConfigId: void 0,
25140
- currentModelId: void 0
25353
+ currentModelId: void 0,
25354
+ advertisedModelValues: []
25141
25355
  };
25356
+ let lastUnmatchedModelId;
25142
25357
  let initializeResult;
25143
25358
  let startResult;
25144
25359
  let startInFlight;
@@ -25273,6 +25488,23 @@ function createAcpExecutor(options) {
25273
25488
  continue;
25274
25489
  }
25275
25490
  if (event._tag === "ContentDelta") {
25491
+ if (event.streamKind === "reasoning_text") {
25492
+ if (event.text.trim().length === 0) {
25493
+ continue;
25494
+ }
25495
+ emit(
25496
+ makeAcpContentDeltaEvent({
25497
+ stamp: stamp(),
25498
+ provider: options.provider,
25499
+ threadId: options.threadId,
25500
+ turnId: activeTurnId,
25501
+ text: event.text,
25502
+ streamKind: "reasoning_text",
25503
+ rawPayload: event.rawPayload
25504
+ })
25505
+ );
25506
+ continue;
25507
+ }
25276
25508
  if (event.text.trim().length === 0 && !assistantSegment.activeItemId) {
25277
25509
  continue;
25278
25510
  }
@@ -25285,6 +25517,7 @@ function createAcpExecutor(options) {
25285
25517
  turnId: activeTurnId,
25286
25518
  ...itemId ? { itemId } : {},
25287
25519
  text: event.text,
25520
+ streamKind: "assistant_text",
25288
25521
  rawPayload: event.rawPayload
25289
25522
  })
25290
25523
  );
@@ -25445,6 +25678,12 @@ function createAcpExecutor(options) {
25445
25678
  const userInputOutcome = await handlePermissionAsUserInput(params);
25446
25679
  if (userInputOutcome !== void 0) return userInputOutcome;
25447
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
+ }
25448
25687
  const requestId = randomId();
25449
25688
  const outcome = await new Promise((resolve2) => {
25450
25689
  const pending = {
@@ -25479,6 +25718,12 @@ function createAcpExecutor(options) {
25479
25718
  const connection = createAcpConnection({
25480
25719
  readable: options.readable,
25481
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
+ },
25482
25727
  onNotification: (notification) => {
25483
25728
  if (notification.method === "session/update") {
25484
25729
  handleSessionUpdate(notification.params);
@@ -25532,36 +25777,70 @@ function createAcpExecutor(options) {
25532
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) {
25533
25778
  return input2.state;
25534
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
+ }
25535
25795
  if (input2.state.strategy === "config-option") {
25536
25796
  await connection.request("session/set_config_option", {
25537
25797
  sessionId: input2.acpSessionId,
25538
25798
  configId: input2.state.modelConfigId,
25539
- value: requestedModelId
25799
+ value
25540
25800
  });
25541
25801
  } else {
25542
25802
  await connection.request("session/set_model", {
25543
25803
  sessionId: input2.acpSessionId,
25544
- modelId: requestedModelId
25804
+ modelId: value
25545
25805
  });
25546
25806
  }
25547
25807
  if (!isLive(input2.expectedGeneration)) {
25548
25808
  throw new AcpExecutorDisposedError();
25549
25809
  }
25550
- 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
+ }
25551
25841
  };
25552
25842
  const startOnce = async (expectedGeneration) => {
25553
- const initializePayload = {
25554
- protocolVersion: PROTOCOL_VERSION,
25555
- clientCapabilities: {
25556
- fs: {
25557
- readTextFile: false,
25558
- writeTextFile: false,
25559
- ...options.clientCapabilities?.fs
25560
- },
25561
- terminal: options.clientCapabilities?.terminal ?? false
25562
- },
25563
- clientInfo: options.clientInfo
25564
- };
25843
+ const initializePayload = acpInitializePayload(options.clientInfo, options.clientCapabilities);
25565
25844
  const initialized = await connection.request(
25566
25845
  "initialize",
25567
25846
  initializePayload
@@ -25605,12 +25884,27 @@ function createAcpExecutor(options) {
25605
25884
  nextSessionId = created.sessionId;
25606
25885
  setup = created;
25607
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
+ }
25608
25896
  const nextModelSelectionState = await applyRequestedModel({
25609
25897
  state: modelSelectionStateFromSetup(setup),
25610
25898
  acpSessionId: nextSessionId,
25611
25899
  requestedModelId: options.requestedModelId,
25612
25900
  expectedGeneration
25613
25901
  });
25902
+ await applyRequestedEffort({
25903
+ setup,
25904
+ acpSessionId: nextSessionId,
25905
+ requestedEffort: options.requestedEffort,
25906
+ expectedGeneration
25907
+ });
25614
25908
  if (options.beforeApplyResult) {
25615
25909
  await options.beforeApplyResult();
25616
25910
  }
@@ -25975,6 +26269,26 @@ function createAcpExecutor(options) {
25975
26269
  };
25976
26270
  apply();
25977
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
+ };
25978
26292
  const respondToUserInput = async (requestId, answers) => {
25979
26293
  failIfDisposed();
25980
26294
  const pending = pendingUserInputs.get(requestId);
@@ -25991,7 +26305,7 @@ function createAcpExecutor(options) {
25991
26305
  pendingUserInputs.delete(requestId);
25992
26306
  pending.resolve({
25993
26307
  kind: "answered",
25994
- answers: isRecord3(answers) ? answers : {}
26308
+ answers: isRecord5(answers) ? answers : {}
25995
26309
  });
25996
26310
  };
25997
26311
  const dispose = async () => {
@@ -26041,12 +26355,84 @@ function createAcpExecutor(options) {
26041
26355
  cancel,
26042
26356
  respondToRequest,
26043
26357
  respondToUserInput,
26358
+ setNativeMode,
26044
26359
  dispose
26045
26360
  };
26046
26361
  }
26047
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
+
26048
26434
  // src/config.ts
26049
- function isRecord4(value) {
26435
+ function isRecord6(value) {
26050
26436
  return typeof value === "object" && value !== null && !Array.isArray(value);
26051
26437
  }
26052
26438
  function readString(value) {
@@ -26059,7 +26445,7 @@ function readEnvironment(value) {
26059
26445
  if (!Array.isArray(value)) return {};
26060
26446
  const env = {};
26061
26447
  for (const entry of value) {
26062
- if (!isRecord4(entry)) continue;
26448
+ if (!isRecord6(entry)) continue;
26063
26449
  const name = readTrimmed(entry["name"]);
26064
26450
  if (name.length === 0) continue;
26065
26451
  env[name] = readString(entry["value"]);
@@ -26067,23 +26453,23 @@ function readEnvironment(value) {
26067
26453
  return env;
26068
26454
  }
26069
26455
  function parseKimiInstanceConfig(config2) {
26070
- if (!isRecord4(config2)) {
26456
+ if (!isRecord6(config2)) {
26071
26457
  return {
26072
26458
  binaryPath: "",
26073
- launchArgs: "",
26459
+ launchArgs: KIMI_CARD.launchArgs,
26074
26460
  homePath: "",
26075
- homeEnvVar: "",
26461
+ homeEnvVar: KIMI_CARD.homeEnvVar,
26076
26462
  environment: {},
26077
26463
  envelopeDriver: void 0
26078
26464
  };
26079
26465
  }
26080
- const nested = isRecord4(config2["config"]) ? config2["config"] : void 0;
26466
+ const nested = isRecord6(config2["config"]) ? config2["config"] : void 0;
26081
26467
  const settings = nested ?? config2;
26082
26468
  return {
26083
26469
  binaryPath: readTrimmed(settings["binaryPath"]),
26084
- launchArgs: readString(settings["launchArgs"]),
26470
+ launchArgs: readString(settings["launchArgs"]) || KIMI_CARD.launchArgs,
26085
26471
  homePath: readTrimmed(settings["homePath"]) || readTrimmed(config2["homePath"]),
26086
- homeEnvVar: readTrimmed(settings["homeEnvVar"]) || readTrimmed(config2["homeEnvVar"]),
26472
+ homeEnvVar: KIMI_CARD.homeEnvVar,
26087
26473
  environment: readEnvironment(config2["environment"]),
26088
26474
  envelopeDriver: readTrimmed(config2["driver"]) || void 0
26089
26475
  };
@@ -26140,13 +26526,51 @@ function tokenizeLaunchArgs(value) {
26140
26526
  return tokens;
26141
26527
  }
26142
26528
 
26143
- // src/contextWindow.ts
26144
- import * as NodeFSP from "node:fs/promises";
26529
+ // src/kimi.ts
26530
+ import * as NodeFS from "node:fs";
26145
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
+ }
26146
26571
  var WIRE_LOG_SEGMENTS = ["agents", "main", "wire.jsonl"];
26147
26572
  var SESSIONS_DIR = "sessions";
26148
26573
  var TAIL_SCAN_BYTES = 256 * 1024;
26149
- var isRecord5 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
26150
26574
  var positiveInt = (value) => typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.trunc(value) : void 0;
26151
26575
  function parseKimiContextWindow(contents) {
26152
26576
  let usedTokens;
@@ -26160,7 +26584,7 @@ function parseKimiContextWindow(contents) {
26160
26584
  } catch {
26161
26585
  continue;
26162
26586
  }
26163
- if (!isRecord5(record2)) continue;
26587
+ if (!isRecord7(record2)) continue;
26164
26588
  const type = record2["type"];
26165
26589
  if (type === "token_counting.turn_recorded" || type === "token_counting.measured") {
26166
26590
  usedTokens = positiveInt(record2["tokens"]) ?? usedTokens;
@@ -26170,31 +26594,35 @@ function parseKimiContextWindow(contents) {
26170
26594
  maxTokens = positiveInt(record2["maxTokens"]) ?? maxTokens;
26171
26595
  }
26172
26596
  }
26173
- return usedTokens === void 0 ? void 0 : { usedTokens, maxTokens };
26597
+ if (usedTokens === void 0) return void 0;
26598
+ return maxTokens === void 0 ? { usedTokens } : { usedTokens, maxTokens };
26174
26599
  }
26175
- async function readTail(filePath) {
26600
+ function readTail(filePath) {
26176
26601
  let handle;
26177
26602
  try {
26178
- handle = await NodeFSP.open(filePath, "r");
26179
- const info = await handle.stat();
26180
- const size = info.size;
26603
+ const size = NodeFS.statSync(filePath).size;
26181
26604
  if (!Number.isFinite(size) || size <= 0) return void 0;
26182
- const offset = size > TAIL_SCAN_BYTES ? size - TAIL_SCAN_BYTES : 0;
26183
- const length = size - offset;
26605
+ const length = size > TAIL_SCAN_BYTES ? TAIL_SCAN_BYTES : size;
26184
26606
  const buffer = Buffer.alloc(length);
26185
- await handle.read(buffer, 0, length, offset);
26607
+ handle = NodeFS.openSync(filePath, "r");
26608
+ NodeFS.readSync(handle, buffer, 0, length, size - length);
26186
26609
  return buffer.toString("utf8");
26187
26610
  } catch {
26188
26611
  return void 0;
26189
26612
  } finally {
26190
- await handle?.close().catch(() => void 0);
26613
+ if (handle !== void 0) {
26614
+ try {
26615
+ NodeFS.closeSync(handle);
26616
+ } catch {
26617
+ }
26618
+ }
26191
26619
  }
26192
26620
  }
26193
- async function findKimiSessionWireLog(input2) {
26621
+ function findKimiSessionWireLog(input2) {
26194
26622
  const sessionsRoot = NodePath.join(input2.homePath, SESSIONS_DIR);
26195
26623
  let workspaces;
26196
26624
  try {
26197
- workspaces = await NodeFSP.readdir(sessionsRoot);
26625
+ workspaces = NodeFS.readdirSync(sessionsRoot);
26198
26626
  } catch {
26199
26627
  return void 0;
26200
26628
  }
@@ -26205,126 +26633,175 @@ async function findKimiSessionWireLog(input2) {
26205
26633
  input2.acpSessionId,
26206
26634
  ...WIRE_LOG_SEGMENTS
26207
26635
  );
26208
- try {
26209
- await NodeFSP.access(candidate);
26210
- return candidate;
26211
- } catch {
26212
- }
26636
+ if (NodeFS.existsSync(candidate)) return candidate;
26213
26637
  }
26214
26638
  return void 0;
26215
26639
  }
26216
- async function readKimiContextWindow(input2) {
26640
+ function readKimiContextWindow(input2) {
26217
26641
  if (input2.homePath.trim().length === 0) return void 0;
26218
- const wireLog = await findKimiSessionWireLog(input2);
26642
+ const wireLog = findKimiSessionWireLog(input2);
26219
26643
  if (wireLog === void 0) return void 0;
26220
- const contents = await readTail(wireLog);
26644
+ const contents = readTail(wireLog);
26221
26645
  return contents === void 0 ? void 0 : parseKimiContextWindow(contents);
26222
26646
  }
26223
26647
 
26224
- // src/permission.ts
26225
- var isRecord6 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
26226
- var nonEmpty = (value) => {
26227
- if (typeof value !== "string") return void 0;
26228
- return value.length > 0 ? value : void 0;
26229
- };
26230
- var trimmedNonEmpty = (value) => {
26231
- if (typeof value !== "string") return void 0;
26232
- return value.trim().length > 0 ? value : void 0;
26233
- };
26234
- var questionText = (content) => {
26235
- if (!Array.isArray(content)) return "";
26236
- const texts = [];
26237
- for (const entry of content) {
26238
- if (!isRecord6(entry)) continue;
26239
- if (entry["type"] !== "content") continue;
26240
- const inner = entry["content"];
26241
- if (!isRecord6(inner)) continue;
26242
- if (inner["type"] !== "text") continue;
26243
- const text = inner["text"];
26244
- 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";
26245
26663
  }
26246
- return texts.join("\n\n").trim();
26247
- };
26248
- var KIMI_QUESTION_TITLE = "AskUserQuestion";
26249
- var KIMI_PLAN_TITLE = "ExitPlanMode";
26250
- var kimiPermissionUserInput = (request) => {
26251
- const title = request.toolCall.title;
26252
- if (title !== KIMI_QUESTION_TITLE && title !== KIMI_PLAN_TITLE) return void 0;
26253
- const question = questionText(request.toolCall.content);
26254
- const optionIds = new Set(request.options.map((option) => option.optionId));
26255
- if (request.options.length === 0 || optionIds.size !== request.options.length) {
26256
- 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";
26257
26669
  }
26258
- const id = trimmedNonEmpty(request.toolCall.toolCallId);
26259
- const options = [];
26260
- for (const option of request.options) {
26261
- const optionId = nonEmpty(option.optionId);
26262
- const label = trimmedNonEmpty(option.name);
26263
- if (optionId === void 0 || label === void 0) {
26264
- throw new Error("Kimi interaction is missing a question, plan, or response option identity.");
26265
- }
26266
- 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;
26267
26703
  }
26268
- if (id === void 0 || question.length === 0) {
26269
- 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
+ });
26270
26718
  }
26271
- return {
26272
- id,
26273
- header: title,
26274
- question,
26275
- kind: title === KIMI_PLAN_TITLE ? "plan" : "question",
26276
- allowFreeform: false,
26277
- multiSelect: false,
26278
- options
26279
- };
26280
- };
26281
-
26282
- // src/streams.ts
26283
- import * as NodeStream2 from "node:stream";
26284
- function nodeStreamsFromHandle(handle) {
26285
- const readable = new NodeStream2.PassThrough();
26286
- void (async () => {
26287
- try {
26288
- for await (const chunk of handle.stdout) {
26289
- if (!readable.writable) return;
26290
- if (!readable.write(chunk)) {
26291
- await new Promise((resolve2) => {
26292
- const onDone = () => {
26293
- readable.off("drain", onDone);
26294
- readable.off("close", onDone);
26295
- resolve2();
26296
- };
26297
- readable.once("drain", onDone);
26298
- readable.once("close", onDone);
26299
- });
26300
- }
26301
- }
26302
- } catch (error62) {
26303
- readable.destroy(error62 instanceof Error ? error62 : new Error(String(error62)));
26304
- return;
26305
- }
26306
- if (readable.writable) readable.end();
26307
- })();
26308
- void (async () => {
26309
- for await (const _chunk of handle.stderr) {
26310
- }
26311
- })().catch(() => void 0);
26312
- const writable = new NodeStream2.Writable({
26313
- write(chunk, _encoding, callback) {
26314
- const bytes = typeof chunk === "string" ? Buffer.from(chunk) : chunk;
26315
- handle.writeStdin(bytes).then(
26316
- () => callback(),
26317
- (error62) => callback(error62 instanceof Error ? error62 : new Error(String(error62)))
26318
- );
26319
- },
26320
- final(callback) {
26321
- handle.closeStdin().then(
26322
- () => callback(),
26323
- (error62) => callback(error62 instanceof Error ? error62 : new Error(String(error62)))
26324
- );
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
+ };
26325
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
26326
26771
  });
26327
- 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;
26328
26805
  }
26329
26806
 
26330
26807
  // src/kimiInstance.ts
@@ -26366,24 +26843,12 @@ var EventBus = class {
26366
26843
  }
26367
26844
  };
26368
26845
  function expandHomePath(value) {
26369
- if (value === "~") return NodeOS.homedir();
26846
+ if (value === "~") return NodeOS2.homedir();
26370
26847
  if (value.startsWith("~/") || value.startsWith("~\\")) {
26371
- return NodePath2.join(NodeOS.homedir(), value.slice(2));
26848
+ return NodePath3.join(NodeOS2.homedir(), value.slice(2));
26372
26849
  }
26373
26850
  return value;
26374
26851
  }
26375
- function resolveHomePath(value) {
26376
- const trimmed = value.trim();
26377
- if (trimmed.length === 0) return "";
26378
- return NodePath2.resolve(expandHomePath(trimmed));
26379
- }
26380
- function compactEnv(env) {
26381
- const out = {};
26382
- for (const [key, value] of Object.entries(env)) {
26383
- if (value !== void 0) out[key] = value;
26384
- }
26385
- return out;
26386
- }
26387
26852
  function modelIdFromSelection(selection) {
26388
26853
  if (typeof selection !== "object" || selection === null || Array.isArray(selection)) {
26389
26854
  return void 0;
@@ -26391,18 +26856,39 @@ function modelIdFromSelection(selection) {
26391
26856
  const model = selection["model"];
26392
26857
  return typeof model === "string" ? model.trim() || void 0 : void 0;
26393
26858
  }
26394
- function buildSpawnEnv(recipe, launch, resolvedHomePath) {
26859
+ function buildSpawnEnv(recipe, configuredHomePath) {
26860
+ const homePath = configuredHomePath.trim();
26395
26861
  const homeEnvVar = recipe.homeEnvVar.trim();
26396
- const homeInjection = resolvedHomePath.length > 0 && homeEnvVar.length > 0 ? { [homeEnvVar]: resolvedHomePath } : {};
26397
- return {
26398
- ...compactEnv(launch.processEnv),
26399
- ...homeInjection,
26400
- ...recipe.environment
26401
- };
26862
+ const homeInjection = homePath.length > 0 && homeEnvVar.length > 0 ? { [homeEnvVar]: expandHomePath(homePath) } : {};
26863
+ return mergeLaunchEnv({
26864
+ accountHome: homeInjection,
26865
+ instanceTable: recipe.environment
26866
+ });
26402
26867
  }
26403
26868
  function nowIso() {
26404
26869
  return (/* @__PURE__ */ new Date()).toISOString();
26405
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
+ }
26406
26892
  async function stopBooked(booked) {
26407
26893
  await booked.executor.dispose().catch(() => void 0);
26408
26894
  await booked.handle.kill("SIGTERM").catch(() => void 0);
@@ -26426,21 +26912,65 @@ var createKimiInstance = (config2, host) => {
26426
26912
  version: null,
26427
26913
  readiness: "error",
26428
26914
  auth: { status: "unknown" },
26429
- detail: "Kimi program path is not configured"
26915
+ detail: "Kimi Code program path is not configured"
26430
26916
  };
26431
26917
  }
26432
- return {
26433
- installed: true,
26434
- version: null,
26435
- readiness: "unchecked",
26436
- 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
+ }
26437
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
+ }
26438
26968
  },
26439
26969
  startSession: async (input2, signal) => {
26440
26970
  if (disposed) throw new Error("instance disposed");
26441
26971
  if (signal?.aborted) throw new Error("aborted");
26442
26972
  if (recipe.binaryPath.length === 0) {
26443
- throw new Error("Kimi program path is not configured");
26973
+ throw new Error("Kimi Code program path is not configured");
26444
26974
  }
26445
26975
  const cwd = input2.cwd ?? host.workspacePath(input2.threadId);
26446
26976
  if (cwd.trim() === "") throw new Error("workspace path missing");
@@ -26449,8 +26979,7 @@ var createKimiInstance = (config2, host) => {
26449
26979
  await stopBooked(existing);
26450
26980
  threads.delete(input2.threadId);
26451
26981
  }
26452
- const resolvedHomePath = resolveHomePath(input2.launch.homePath?.trim() || recipe.homePath);
26453
- const env = buildSpawnEnv(recipe, input2.launch, resolvedHomePath);
26982
+ const env = buildSpawnEnv(recipe, recipe.homePath);
26454
26983
  const handle = await host.process.spawn({
26455
26984
  program: recipe.binaryPath,
26456
26985
  args: tokenizeLaunchArgs(recipe.launchArgs),
@@ -26460,31 +26989,30 @@ var createKimiInstance = (config2, host) => {
26460
26989
  });
26461
26990
  const { readable, writable } = nodeStreamsFromHandle(handle);
26462
26991
  const resume = input2.discardResumeCursor === true ? void 0 : parseAcpResumeCursor(input2.resumeCursor, RESUME_SCHEMA_VERSION);
26463
- const requestedModelId = input2.launch.requestedModelId?.trim() || modelIdFromSelection(input2.modelSelection);
26992
+ const requestedModelId = modelIdFromSelection(input2.modelSelection);
26993
+ const requestedEffort = modelSelectionStringOption(
26994
+ input2.modelSelection,
26995
+ ACP_EFFORT_OPTION_ID
26996
+ );
26997
+ const hooks = kimiHooks(recipe);
26998
+ const autoApprove = input2.nativeModeId === void 0 ? (request) => acpRuntimeModeAutoApproves(
26999
+ threads.get(input2.threadId)?.session.runtimeMode ?? input2.runtimeMode,
27000
+ request.kind
27001
+ ) : void 0;
26464
27002
  const executor = createAcpExecutor({
26465
27003
  readable,
26466
27004
  writable,
27005
+ ...requestedEffort === void 0 ? {} : { requestedEffort },
26467
27006
  cwd,
26468
27007
  threadId: input2.threadId,
26469
27008
  provider: KIMI_DRIVER_KIND,
26470
27009
  providerInstanceId: host.instanceId,
26471
27010
  clientInfo: CLIENT_INFO,
26472
- hooks: {
26473
- permissionUserInput: kimiPermissionUserInput,
26474
- readContextWindow: async ({ sessionId }) => {
26475
- const reading = await readKimiContextWindow({
26476
- homePath: resolvedHomePath,
26477
- acpSessionId: sessionId
26478
- });
26479
- if (reading === void 0) return void 0;
26480
- return {
26481
- usedTokens: reading.usedTokens,
26482
- ...reading.maxTokens === void 0 ? {} : { maxTokens: reading.maxTokens }
26483
- };
26484
- }
26485
- },
27011
+ log: host.log,
26486
27012
  ...requestedModelId === void 0 ? {} : { requestedModelId },
26487
27013
  ...input2.bindingGeneration === void 0 ? {} : { originBindingGeneration: input2.bindingGeneration },
27014
+ hooks,
27015
+ ...autoApprove === void 0 ? {} : { autoApprove },
26488
27016
  ...resume === void 0 ? {} : { resumeSessionId: resume.sessionId }
26489
27017
  });
26490
27018
  const forwarding = (async () => {
@@ -26494,6 +27022,13 @@ var createKimiInstance = (config2, host) => {
26494
27022
  })();
26495
27023
  try {
26496
27024
  const started = await executor.start();
27025
+ const modeToApply = acpSessionModeToApply(
27026
+ input2.nativeModeId,
27027
+ acpCurrentModeId(started.sessionSetupResult)
27028
+ );
27029
+ if (modeToApply !== void 0) {
27030
+ await executor.setNativeMode(modeToApply);
27031
+ }
26497
27032
  await yieldTick();
26498
27033
  const createdAt = nowIso();
26499
27034
  const resumeCursor = {
@@ -26505,6 +27040,7 @@ var createKimiInstance = (config2, host) => {
26505
27040
  providerInstanceId: host.instanceId,
26506
27041
  status: "idle",
26507
27042
  runtimeMode: input2.runtimeMode,
27043
+ ...input2.nativeModeId === void 0 ? {} : { nativeModeId: input2.nativeModeId },
26508
27044
  cwd,
26509
27045
  threadId: input2.threadId,
26510
27046
  createdAt,
@@ -26588,6 +27124,22 @@ var createKimiInstance = (config2, host) => {
26588
27124
  const booked = requireThread(threadId);
26589
27125
  await booked.executor.respondToUserInput(requestId, answers);
26590
27126
  },
27127
+ /**
27128
+ * 切这只手自报的审批档。`nativeModeId` 是它自己报的 id(探测时原样上报的那些),
27129
+ * 一路不翻译成宿主四档。
27130
+ *
27131
+ * 这是 `setRuntimeMode` 做不到的那一半:那个只改插件自己的会话记录,CLI 收不到
27132
+ * (`runtime-mode-never-reaches-cli` 记的那族缺陷)。
27133
+ */
27134
+ setNativeMode: async (threadId, nativeModeId) => {
27135
+ const booked = requireThread(threadId);
27136
+ await booked.executor.setNativeMode(nativeModeId);
27137
+ booked.session = {
27138
+ ...booked.session,
27139
+ nativeModeId,
27140
+ updatedAt: nowIso()
27141
+ };
27142
+ },
26591
27143
  setRuntimeMode: async (threadId, runtimeMode) => {
26592
27144
  const booked = requireThread(threadId);
26593
27145
  booked.session = {
@@ -26607,6 +27159,7 @@ var createKimiInstance = (config2, host) => {
26607
27159
  return { threadId, turns: booked.turns };
26608
27160
  },
26609
27161
  events,
27162
+ continuationKey: kimiContinuationKey(recipe),
26610
27163
  dispose: async () => {
26611
27164
  if (disposed) return;
26612
27165
  disposed = true;
@@ -26634,7 +27187,7 @@ var index_default = definePlugin({
26634
27187
  apiVersion: 2,
26635
27188
  executors,
26636
27189
  activate(ctx) {
26637
- ctx.log.info("kimi executor ready");
27190
+ ctx.log.info("Kimi Code executor ready");
26638
27191
  }
26639
27192
  });
26640
27193
  export {