@ycodium-ai/agent-acp 0.2.2092 → 0.2.2126
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/README.md +17 -3
- package/dist/index.js +954 -113
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -8,8 +8,14 @@ var __export = (target, all) => {
|
|
|
8
8
|
import { definePlugin } from "@ycodium-ai/plugin-api";
|
|
9
9
|
|
|
10
10
|
// src/acpInstance.ts
|
|
11
|
-
import * as
|
|
12
|
-
import * as
|
|
11
|
+
import * as NodeOS2 from "node:os";
|
|
12
|
+
import * as NodePath3 from "node:path";
|
|
13
|
+
import {
|
|
14
|
+
mergeLaunchEnv,
|
|
15
|
+
modelSelectionStringOption,
|
|
16
|
+
brainSlotEnvKeys
|
|
17
|
+
} from "@ycodium-ai/plugin-api/executor";
|
|
18
|
+
import { nodeStreamsFromHandle as nodeStreamsFromHandle2 } from "@ycodium-ai/plugin-api/process-stdin";
|
|
13
19
|
|
|
14
20
|
// ../../packages/ycodium-acp-executor/src/executor.ts
|
|
15
21
|
import * as NodeCrypto from "node:crypto";
|
|
@@ -23879,6 +23885,12 @@ function acpApprovalOptions(request) {
|
|
|
23879
23885
|
}
|
|
23880
23886
|
return options;
|
|
23881
23887
|
}
|
|
23888
|
+
function selectAutoApprovedAcpPermissionOption(request) {
|
|
23889
|
+
if (new Set(request.options.map((option) => option.kind)).size !== request.options.length) {
|
|
23890
|
+
return void 0;
|
|
23891
|
+
}
|
|
23892
|
+
return selectAcpPermissionOptionId(request, "acceptForSession") ?? selectAcpPermissionOptionId(request, "accept");
|
|
23893
|
+
}
|
|
23882
23894
|
function parseAcpResumeCursor(raw, schemaVersion) {
|
|
23883
23895
|
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) return void 0;
|
|
23884
23896
|
const record2 = raw;
|
|
@@ -24261,7 +24273,7 @@ function makeAcpContentDeltaEvent(input2) {
|
|
|
24261
24273
|
...input2.turnId !== void 0 ? { turnId: input2.turnId } : {},
|
|
24262
24274
|
...input2.itemId ? { itemId: input2.itemId } : {},
|
|
24263
24275
|
payload: {
|
|
24264
|
-
streamKind: "assistant_text",
|
|
24276
|
+
streamKind: input2.streamKind ?? "assistant_text",
|
|
24265
24277
|
delta: input2.text
|
|
24266
24278
|
},
|
|
24267
24279
|
raw: {
|
|
@@ -24272,6 +24284,233 @@ function makeAcpContentDeltaEvent(input2) {
|
|
|
24272
24284
|
};
|
|
24273
24285
|
}
|
|
24274
24286
|
|
|
24287
|
+
// ../../packages/ycodium-acp-executor/src/config-options.ts
|
|
24288
|
+
function isRecord3(value) {
|
|
24289
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
24290
|
+
}
|
|
24291
|
+
function asString(value) {
|
|
24292
|
+
return typeof value === "string" ? value : void 0;
|
|
24293
|
+
}
|
|
24294
|
+
function normalizedField(option, key) {
|
|
24295
|
+
return asString(option[key])?.trim().toLowerCase() ?? "";
|
|
24296
|
+
}
|
|
24297
|
+
function looksLikeEffortOption(option) {
|
|
24298
|
+
const id = normalizedField(option, "id");
|
|
24299
|
+
const name = normalizedField(option, "name");
|
|
24300
|
+
return id === "effort" || id === "reasoning" || name === "effort" || name === "reasoning" || name.includes("effort") || name.includes("reasoning") || normalizedField(option, "category") === "thought_level";
|
|
24301
|
+
}
|
|
24302
|
+
function flattenChoices(rawOptions, currentValue) {
|
|
24303
|
+
if (!Array.isArray(rawOptions)) return [];
|
|
24304
|
+
const out = [];
|
|
24305
|
+
const seen = /* @__PURE__ */ new Set();
|
|
24306
|
+
const push = (entry) => {
|
|
24307
|
+
if (!isRecord3(entry)) return;
|
|
24308
|
+
const value = asString(entry["value"]);
|
|
24309
|
+
if (value === void 0 || seen.has(value)) return;
|
|
24310
|
+
seen.add(value);
|
|
24311
|
+
const description = asString(entry["description"])?.trim();
|
|
24312
|
+
const name = asString(entry["name"])?.trim();
|
|
24313
|
+
out.push({
|
|
24314
|
+
value,
|
|
24315
|
+
...name ? { name } : {},
|
|
24316
|
+
label: name || value,
|
|
24317
|
+
...description ? { description } : {},
|
|
24318
|
+
...currentValue !== void 0 && currentValue === value ? { isDefault: true } : {}
|
|
24319
|
+
});
|
|
24320
|
+
};
|
|
24321
|
+
for (const entry of rawOptions) {
|
|
24322
|
+
if (isRecord3(entry) && Array.isArray(entry["options"])) {
|
|
24323
|
+
for (const nested of entry["options"]) push(nested);
|
|
24324
|
+
continue;
|
|
24325
|
+
}
|
|
24326
|
+
push(entry);
|
|
24327
|
+
}
|
|
24328
|
+
return out;
|
|
24329
|
+
}
|
|
24330
|
+
function findAcpEffortConfig(configOptions) {
|
|
24331
|
+
if (!Array.isArray(configOptions)) return void 0;
|
|
24332
|
+
const candidates = configOptions.filter(isRecord3).filter((option) => normalizedField(option, "type") === "select").filter(looksLikeEffortOption);
|
|
24333
|
+
if (candidates.length === 0) return void 0;
|
|
24334
|
+
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];
|
|
24335
|
+
if (picked === void 0) return void 0;
|
|
24336
|
+
const configId = asString(picked["id"])?.trim();
|
|
24337
|
+
if (!configId) return void 0;
|
|
24338
|
+
const choices = flattenChoices(picked["options"], asString(picked["currentValue"])?.trim());
|
|
24339
|
+
if (choices.length === 0) return void 0;
|
|
24340
|
+
return {
|
|
24341
|
+
configId,
|
|
24342
|
+
label: asString(picked["name"])?.trim() ?? "",
|
|
24343
|
+
choices
|
|
24344
|
+
};
|
|
24345
|
+
}
|
|
24346
|
+
function acpModelChoicesFromConfigOptions(configOptions) {
|
|
24347
|
+
if (!Array.isArray(configOptions)) return void 0;
|
|
24348
|
+
const records = configOptions.filter(isRecord3);
|
|
24349
|
+
const modelOption = records.find((option) => normalizedField(option, "category") === "model");
|
|
24350
|
+
if (modelOption === void 0) return void 0;
|
|
24351
|
+
if (!asString(modelOption["id"])?.trim()) return void 0;
|
|
24352
|
+
return flattenChoices(modelOption["options"], asString(modelOption["currentValue"])?.trim());
|
|
24353
|
+
}
|
|
24354
|
+
function acpModelChoicesFromSessionModels(models) {
|
|
24355
|
+
if (!isRecord3(models) || !Array.isArray(models["availableModels"])) return void 0;
|
|
24356
|
+
const currentModelId = asString(models["currentModelId"])?.trim();
|
|
24357
|
+
const out = [];
|
|
24358
|
+
const seen = /* @__PURE__ */ new Set();
|
|
24359
|
+
for (const entry of models["availableModels"]) {
|
|
24360
|
+
if (!isRecord3(entry)) continue;
|
|
24361
|
+
const value = asString(entry["modelId"])?.trim();
|
|
24362
|
+
if (!value || seen.has(value)) continue;
|
|
24363
|
+
seen.add(value);
|
|
24364
|
+
const name = asString(entry["name"])?.trim();
|
|
24365
|
+
const description = asString(entry["description"])?.trim();
|
|
24366
|
+
out.push({
|
|
24367
|
+
value,
|
|
24368
|
+
...name ? { name } : {},
|
|
24369
|
+
label: name || value,
|
|
24370
|
+
...description ? { description } : {},
|
|
24371
|
+
...currentModelId !== void 0 && currentModelId === value ? { isDefault: true } : {}
|
|
24372
|
+
});
|
|
24373
|
+
}
|
|
24374
|
+
return out;
|
|
24375
|
+
}
|
|
24376
|
+
|
|
24377
|
+
// ../../packages/ycodium-acp-executor/src/runtime-mode.ts
|
|
24378
|
+
function isRecord4(value) {
|
|
24379
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
24380
|
+
}
|
|
24381
|
+
function asString2(value) {
|
|
24382
|
+
return typeof value === "string" ? value : void 0;
|
|
24383
|
+
}
|
|
24384
|
+
function acpAvailableModes(setup) {
|
|
24385
|
+
if (!isRecord4(setup)) return [];
|
|
24386
|
+
const out = [];
|
|
24387
|
+
const seen = /* @__PURE__ */ new Set();
|
|
24388
|
+
const push = (entry) => {
|
|
24389
|
+
if (!isRecord4(entry)) return;
|
|
24390
|
+
const id = (asString2(entry["id"]) ?? asString2(entry["value"]))?.trim();
|
|
24391
|
+
if (!id || seen.has(id)) return;
|
|
24392
|
+
seen.add(id);
|
|
24393
|
+
const meta3 = entry["_meta"];
|
|
24394
|
+
const kind = isRecord4(meta3) ? asString2(meta3["kind"])?.trim().toLowerCase() : void 0;
|
|
24395
|
+
const name = asString2(entry["name"])?.trim();
|
|
24396
|
+
const description = asString2(entry["description"])?.trim();
|
|
24397
|
+
out.push({
|
|
24398
|
+
id,
|
|
24399
|
+
name: name || id,
|
|
24400
|
+
...description ? { description } : {},
|
|
24401
|
+
...kind ? { kind } : {}
|
|
24402
|
+
});
|
|
24403
|
+
};
|
|
24404
|
+
const modes = setup["modes"];
|
|
24405
|
+
if (isRecord4(modes) && Array.isArray(modes["availableModes"])) {
|
|
24406
|
+
for (const entry of modes["availableModes"]) push(entry);
|
|
24407
|
+
}
|
|
24408
|
+
const configOptions = setup["configOptions"];
|
|
24409
|
+
if (Array.isArray(configOptions)) {
|
|
24410
|
+
for (const option of configOptions) {
|
|
24411
|
+
if (!isRecord4(option)) continue;
|
|
24412
|
+
if (asString2(option["category"])?.trim().toLowerCase() !== "mode") continue;
|
|
24413
|
+
if (!Array.isArray(option["options"])) continue;
|
|
24414
|
+
for (const entry of option["options"]) push(entry);
|
|
24415
|
+
}
|
|
24416
|
+
}
|
|
24417
|
+
return out;
|
|
24418
|
+
}
|
|
24419
|
+
function acpModeConfigId(setup) {
|
|
24420
|
+
if (!isRecord4(setup)) return void 0;
|
|
24421
|
+
const configOptions = setup["configOptions"];
|
|
24422
|
+
if (!Array.isArray(configOptions)) return void 0;
|
|
24423
|
+
for (const option of configOptions) {
|
|
24424
|
+
if (!isRecord4(option)) continue;
|
|
24425
|
+
if (asString2(option["category"])?.trim().toLowerCase() !== "mode") continue;
|
|
24426
|
+
const id = asString2(option["id"])?.trim();
|
|
24427
|
+
if (id) return id;
|
|
24428
|
+
}
|
|
24429
|
+
return void 0;
|
|
24430
|
+
}
|
|
24431
|
+
function acpCurrentModeId(setup) {
|
|
24432
|
+
if (!isRecord4(setup)) return void 0;
|
|
24433
|
+
const modes = setup["modes"];
|
|
24434
|
+
if (isRecord4(modes)) {
|
|
24435
|
+
const current = asString2(modes["currentModeId"])?.trim();
|
|
24436
|
+
if (current) return current;
|
|
24437
|
+
}
|
|
24438
|
+
const configOptions = setup["configOptions"];
|
|
24439
|
+
if (Array.isArray(configOptions)) {
|
|
24440
|
+
for (const option of configOptions) {
|
|
24441
|
+
if (!isRecord4(option)) continue;
|
|
24442
|
+
if (asString2(option["category"])?.trim().toLowerCase() !== "mode") continue;
|
|
24443
|
+
const current = asString2(option["currentValue"])?.trim();
|
|
24444
|
+
if (current) return current;
|
|
24445
|
+
}
|
|
24446
|
+
}
|
|
24447
|
+
return void 0;
|
|
24448
|
+
}
|
|
24449
|
+
|
|
24450
|
+
// ../../packages/ycodium-acp-executor/src/handshake.ts
|
|
24451
|
+
function acpInitializePayload(clientInfo, overrides) {
|
|
24452
|
+
return {
|
|
24453
|
+
protocolVersion: PROTOCOL_VERSION,
|
|
24454
|
+
clientCapabilities: {
|
|
24455
|
+
fs: {
|
|
24456
|
+
readTextFile: false,
|
|
24457
|
+
writeTextFile: false,
|
|
24458
|
+
...overrides?.fs
|
|
24459
|
+
},
|
|
24460
|
+
terminal: overrides?.terminal ?? false
|
|
24461
|
+
},
|
|
24462
|
+
clientInfo
|
|
24463
|
+
};
|
|
24464
|
+
}
|
|
24465
|
+
|
|
24466
|
+
// ../../packages/ycodium-acp-executor/src/config-option-wiring.ts
|
|
24467
|
+
var VENDOR_CATEGORY_PREFIX = "_";
|
|
24468
|
+
var ACP_CONFIG_CATEGORY_HANDLING = {
|
|
24469
|
+
mode: {
|
|
24470
|
+
consumed: true,
|
|
24471
|
+
via: "runtime-mode.ts\uFF1AacpAvailableModes / acpCurrentModeId / acpModeConfigId"
|
|
24472
|
+
},
|
|
24473
|
+
model: {
|
|
24474
|
+
consumed: true,
|
|
24475
|
+
via: "runtime-model.ts:extractModelConfigId \u2192 executor.ts:applyRequestedModel"
|
|
24476
|
+
},
|
|
24477
|
+
thought_level: {
|
|
24478
|
+
consumed: true,
|
|
24479
|
+
via: "config-options.ts:findAcpEffortConfig \u2192 executor.ts:applyRequestedEffort"
|
|
24480
|
+
},
|
|
24481
|
+
model_option: {
|
|
24482
|
+
consumed: true,
|
|
24483
|
+
// 精确说法:它不是**识别器**,是**优先级**。`looksLikeEffortOption` 先筛出候选,
|
|
24484
|
+
// 标了 `model_option` 的在候选里排第一(厂商自己标的最准),排在 id 叫 `effort`
|
|
24485
|
+
// 的和标 `thought_level` 的前面。光有这个 category 而 id/name 不像力度档的,
|
|
24486
|
+
// 压根进不了候选。
|
|
24487
|
+
via: "config-options.ts:findAcpEffortConfig\uFF08\u5019\u9009\u91CC\u7684\u6700\u9AD8\u4F18\u5148\u7EA7\uFF0C\u538B\u8FC7 thought_level\uFF09"
|
|
24488
|
+
},
|
|
24489
|
+
model_config: {
|
|
24490
|
+
consumed: false,
|
|
24491
|
+
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"
|
|
24492
|
+
}
|
|
24493
|
+
};
|
|
24494
|
+
var KNOWN_CATEGORIES = new Set(Object.keys(ACP_CONFIG_CATEGORY_HANDLING));
|
|
24495
|
+
function unhandledConfigCategories(configOptions) {
|
|
24496
|
+
if (!configOptions) return [];
|
|
24497
|
+
const seen = /* @__PURE__ */ new Set();
|
|
24498
|
+
const out = [];
|
|
24499
|
+
for (const option of configOptions) {
|
|
24500
|
+
const category = option.category?.trim();
|
|
24501
|
+
if (!category) continue;
|
|
24502
|
+
if (KNOWN_CATEGORIES.has(category)) continue;
|
|
24503
|
+
if (seen.has(category)) continue;
|
|
24504
|
+
seen.add(category);
|
|
24505
|
+
out.push({
|
|
24506
|
+
category,
|
|
24507
|
+
optionId: option.id.trim(),
|
|
24508
|
+
vendorExtension: category.startsWith(VENDOR_CATEGORY_PREFIX)
|
|
24509
|
+
});
|
|
24510
|
+
}
|
|
24511
|
+
return out;
|
|
24512
|
+
}
|
|
24513
|
+
|
|
24275
24514
|
// ../../packages/ycodium-acp-executor/src/mutex.ts
|
|
24276
24515
|
var Mutex = class {
|
|
24277
24516
|
locked = false;
|
|
@@ -24314,7 +24553,7 @@ var Mutex = class {
|
|
|
24314
24553
|
};
|
|
24315
24554
|
|
|
24316
24555
|
// ../../packages/ycodium-acp-executor/src/util.ts
|
|
24317
|
-
function
|
|
24556
|
+
function isRecord5(value) {
|
|
24318
24557
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
24319
24558
|
}
|
|
24320
24559
|
function asTrimmedString(value) {
|
|
@@ -24360,11 +24599,11 @@ function extractCommandFromTitle(title) {
|
|
|
24360
24599
|
return backtickMatch?.[1]?.trim() || void 0;
|
|
24361
24600
|
}
|
|
24362
24601
|
function extractToolCommand(data, title) {
|
|
24363
|
-
const item =
|
|
24364
|
-
const itemInput = item &&
|
|
24365
|
-
const itemResult = item &&
|
|
24366
|
-
const rawInput =
|
|
24367
|
-
const dataInput =
|
|
24602
|
+
const item = isRecord5(data?.["item"]) ? data["item"] : void 0;
|
|
24603
|
+
const itemInput = item && isRecord5(item["input"]) ? item["input"] : void 0;
|
|
24604
|
+
const itemResult = item && isRecord5(item["result"]) ? item["result"] : void 0;
|
|
24605
|
+
const rawInput = isRecord5(data?.["rawInput"]) ? data["rawInput"] : void 0;
|
|
24606
|
+
const dataInput = isRecord5(data?.["input"]) ? data["input"] : void 0;
|
|
24368
24607
|
const candidates = [
|
|
24369
24608
|
normalizeCommandValue(item?.["command"]),
|
|
24370
24609
|
normalizeCommandValue(itemInput?.["command"]),
|
|
@@ -24409,7 +24648,7 @@ function collectPaths(value, paths, seen, depth) {
|
|
|
24409
24648
|
}
|
|
24410
24649
|
return;
|
|
24411
24650
|
}
|
|
24412
|
-
if (!
|
|
24651
|
+
if (!isRecord5(value)) {
|
|
24413
24652
|
return;
|
|
24414
24653
|
}
|
|
24415
24654
|
for (const key of ["path", "filePath", "relativePath", "filename", "newPath", "oldPath"]) {
|
|
@@ -24472,7 +24711,7 @@ function deriveToolActivityPresentation(input2) {
|
|
|
24472
24711
|
const title = asTrimmedString(input2.title);
|
|
24473
24712
|
const detail = stripTrailingExitCode(asTrimmedString(input2.detail));
|
|
24474
24713
|
const fallbackSummary = asTrimmedString(input2.fallbackSummary) ?? "Tool";
|
|
24475
|
-
const data =
|
|
24714
|
+
const data = isRecord5(input2.data) ? input2.data : void 0;
|
|
24476
24715
|
const command = extractToolCommand(data, title);
|
|
24477
24716
|
const primaryPath = extractPrimaryPath(data);
|
|
24478
24717
|
const action = classifyToolAction({
|
|
@@ -24504,7 +24743,7 @@ function deriveToolActivityPresentation(input2) {
|
|
|
24504
24743
|
};
|
|
24505
24744
|
}
|
|
24506
24745
|
if (action === "search") {
|
|
24507
|
-
const rawInput =
|
|
24746
|
+
const rawInput = isRecord5(data?.["rawInput"]) ? data["rawInput"] : void 0;
|
|
24508
24747
|
const query = asTrimmedString(rawInput?.["query"]) ?? asTrimmedString(rawInput?.["pattern"]) ?? asTrimmedString(rawInput?.["searchTerm"]);
|
|
24509
24748
|
return {
|
|
24510
24749
|
summary: "Searched files",
|
|
@@ -24543,6 +24782,46 @@ function findSessionConfigOption(configOptions, configId) {
|
|
|
24543
24782
|
}
|
|
24544
24783
|
return configOptions.find((option) => option.id.trim() === normalizedConfigId);
|
|
24545
24784
|
}
|
|
24785
|
+
function collectSessionConfigOptionValues(configOption) {
|
|
24786
|
+
if (configOption.type !== "select" || !configOption.options) {
|
|
24787
|
+
return [];
|
|
24788
|
+
}
|
|
24789
|
+
return configOption.options.flatMap(
|
|
24790
|
+
(entry) => "value" in entry ? [entry.value] : entry.options.map((option) => option.value)
|
|
24791
|
+
);
|
|
24792
|
+
}
|
|
24793
|
+
function collectSessionModelIds(models) {
|
|
24794
|
+
if (!isRecord5(models) || !Array.isArray(models["availableModels"])) {
|
|
24795
|
+
return [];
|
|
24796
|
+
}
|
|
24797
|
+
return models["availableModels"].flatMap(
|
|
24798
|
+
(model) => isRecord5(model) && typeof model["modelId"] === "string" ? [model["modelId"]] : []
|
|
24799
|
+
);
|
|
24800
|
+
}
|
|
24801
|
+
function catalogModelLastSegment(value) {
|
|
24802
|
+
if (value.startsWith("[")) {
|
|
24803
|
+
let parsed;
|
|
24804
|
+
try {
|
|
24805
|
+
parsed = JSON.parse(value);
|
|
24806
|
+
} catch {
|
|
24807
|
+
return void 0;
|
|
24808
|
+
}
|
|
24809
|
+
if (!Array.isArray(parsed) || parsed.length < 2) return void 0;
|
|
24810
|
+
const last = parsed[parsed.length - 1];
|
|
24811
|
+
return typeof last === "string" ? last : void 0;
|
|
24812
|
+
}
|
|
24813
|
+
const separator = value.indexOf("/");
|
|
24814
|
+
return separator > 0 && separator < value.length - 1 ? value.slice(separator + 1) : void 0;
|
|
24815
|
+
}
|
|
24816
|
+
function matchAdvertisedModelValue(requestedModelId, advertisedValues) {
|
|
24817
|
+
const wanted = requestedModelId.trim();
|
|
24818
|
+
if (!wanted) return void 0;
|
|
24819
|
+
if (advertisedValues.includes(wanted)) return wanted;
|
|
24820
|
+
const byLastSegment = advertisedValues.filter(
|
|
24821
|
+
(value) => catalogModelLastSegment(value) === wanted
|
|
24822
|
+
);
|
|
24823
|
+
return byLastSegment.length === 1 ? byLastSegment[0] : void 0;
|
|
24824
|
+
}
|
|
24546
24825
|
function parseSessionModeState(sessionResponse) {
|
|
24547
24826
|
const modes = sessionResponse.modes;
|
|
24548
24827
|
if (!modes) return void 0;
|
|
@@ -24581,7 +24860,7 @@ function parseAvailableCommandsUpdate(params) {
|
|
|
24581
24860
|
}
|
|
24582
24861
|
const commands = [];
|
|
24583
24862
|
for (const command of availableCommands) {
|
|
24584
|
-
if (!
|
|
24863
|
+
if (!isRecord5(command)) {
|
|
24585
24864
|
continue;
|
|
24586
24865
|
}
|
|
24587
24866
|
const name = typeof command["name"] === "string" ? command["name"].trim() : "";
|
|
@@ -24589,7 +24868,7 @@ function parseAvailableCommandsUpdate(params) {
|
|
|
24589
24868
|
continue;
|
|
24590
24869
|
}
|
|
24591
24870
|
const description = typeof command["description"] === "string" ? command["description"].trim() || void 0 : void 0;
|
|
24592
|
-
const input2 =
|
|
24871
|
+
const input2 = isRecord5(command["input"]) ? command["input"] : void 0;
|
|
24593
24872
|
const inputHint = typeof input2?.["hint"] === "string" ? input2["hint"].trim() || void 0 : void 0;
|
|
24594
24873
|
commands.push({
|
|
24595
24874
|
name,
|
|
@@ -24651,7 +24930,7 @@ function extractCommandFromTitle2(title) {
|
|
|
24651
24930
|
return match?.[1]?.trim() || void 0;
|
|
24652
24931
|
}
|
|
24653
24932
|
function extractToolCallCommand(rawInput, title) {
|
|
24654
|
-
if (
|
|
24933
|
+
if (isRecord5(rawInput)) {
|
|
24655
24934
|
const directCommand = normalizeCommandValue2(rawInput["command"]);
|
|
24656
24935
|
if (directCommand) {
|
|
24657
24936
|
return directCommand;
|
|
@@ -24678,7 +24957,7 @@ function boundToolCallOutputText(text) {
|
|
|
24678
24957
|
}
|
|
24679
24958
|
var RAW_OUTPUT_TEXT_FIELDS = ["content", "stdout", "stderr", "output"];
|
|
24680
24959
|
function boundToolCallRawOutput(rawOutput) {
|
|
24681
|
-
if (!
|
|
24960
|
+
if (!isRecord5(rawOutput)) {
|
|
24682
24961
|
return rawOutput;
|
|
24683
24962
|
}
|
|
24684
24963
|
let changed = false;
|
|
@@ -24908,7 +25187,7 @@ function parseSessionUpdateEvent(params) {
|
|
|
24908
25187
|
const entries = Array.isArray(upd["entries"]) ? upd["entries"] : [];
|
|
24909
25188
|
const plan = entries.map((entry, index) => ({
|
|
24910
25189
|
step: readAcpText(entry, "content") ?? `Step ${index + 1}`,
|
|
24911
|
-
status: normalizePlanStepStatus(
|
|
25190
|
+
status: normalizePlanStepStatus(isRecord5(entry) ? entry["status"] : void 0)
|
|
24912
25191
|
}));
|
|
24913
25192
|
if (plan.length > 0) {
|
|
24914
25193
|
events.push({
|
|
@@ -24945,21 +25224,24 @@ function parseSessionUpdateEvent(params) {
|
|
|
24945
25224
|
}
|
|
24946
25225
|
break;
|
|
24947
25226
|
}
|
|
24948
|
-
case "agent_message_chunk":
|
|
24949
|
-
|
|
25227
|
+
case "agent_message_chunk":
|
|
25228
|
+
case "agent_thought_chunk": {
|
|
25229
|
+
const content = isRecord5(upd["content"]) ? upd["content"] : void 0;
|
|
24950
25230
|
const contentType = content?.["type"];
|
|
25231
|
+
const streamKind = kind === "agent_thought_chunk" ? "reasoning_text" : "assistant_text";
|
|
24951
25232
|
if (contentType === "text") {
|
|
24952
25233
|
const text = typeof content?.["text"] === "string" ? content["text"] : "";
|
|
24953
25234
|
if (text.length > 0) {
|
|
24954
25235
|
events.push({
|
|
24955
25236
|
_tag: "ContentDelta",
|
|
24956
25237
|
text,
|
|
25238
|
+
streamKind,
|
|
24957
25239
|
rawPayload: params
|
|
24958
25240
|
});
|
|
24959
25241
|
}
|
|
24960
25242
|
break;
|
|
24961
25243
|
}
|
|
24962
|
-
if (content && contentType !== "audio") {
|
|
25244
|
+
if (streamKind === "assistant_text" && content && contentType !== "audio") {
|
|
24963
25245
|
events.push({
|
|
24964
25246
|
_tag: "AssistantContentBlock",
|
|
24965
25247
|
content,
|
|
@@ -25011,16 +25293,18 @@ function normalizeModelId(value) {
|
|
|
25011
25293
|
return value?.trim() || void 0;
|
|
25012
25294
|
}
|
|
25013
25295
|
function modelSelectionStateFromSetup(setup) {
|
|
25014
|
-
const sessionModels =
|
|
25296
|
+
const sessionModels = isRecord5(setup.models) ? setup.models : void 0;
|
|
25015
25297
|
const modelConfigId = extractModelConfigId(setup);
|
|
25016
25298
|
const strategy = sessionModels === void 0 && modelConfigId !== void 0 ? "config-option" : "session/set_model";
|
|
25017
|
-
const
|
|
25299
|
+
const modelConfigOption = modelConfigId === void 0 ? void 0 : findSessionConfigOption(setup.configOptions, modelConfigId);
|
|
25300
|
+
const configModel = modelConfigOption?.currentValue;
|
|
25018
25301
|
const currentModelId = strategy === "config-option" ? typeof configModel === "string" ? normalizeModelId(configModel) : void 0 : typeof sessionModels?.["currentModelId"] === "string" ? normalizeModelId(sessionModels["currentModelId"]) : void 0;
|
|
25019
25302
|
return {
|
|
25020
25303
|
strategy,
|
|
25021
25304
|
sessionModelsAdvertised: sessionModels !== void 0,
|
|
25022
25305
|
modelConfigId,
|
|
25023
|
-
currentModelId
|
|
25306
|
+
currentModelId,
|
|
25307
|
+
advertisedModelValues: strategy === "config-option" ? modelConfigOption === void 0 ? [] : collectSessionConfigOptionValues(modelConfigOption) : collectSessionModelIds(sessionModels)
|
|
25024
25308
|
};
|
|
25025
25309
|
}
|
|
25026
25310
|
function createAcpExecutor(options) {
|
|
@@ -25039,8 +25323,10 @@ function createAcpExecutor(options) {
|
|
|
25039
25323
|
strategy: "session/set_model",
|
|
25040
25324
|
sessionModelsAdvertised: false,
|
|
25041
25325
|
modelConfigId: void 0,
|
|
25042
|
-
currentModelId: void 0
|
|
25326
|
+
currentModelId: void 0,
|
|
25327
|
+
advertisedModelValues: []
|
|
25043
25328
|
};
|
|
25329
|
+
let lastUnmatchedModelId;
|
|
25044
25330
|
let initializeResult;
|
|
25045
25331
|
let startResult;
|
|
25046
25332
|
let startInFlight;
|
|
@@ -25175,6 +25461,23 @@ function createAcpExecutor(options) {
|
|
|
25175
25461
|
continue;
|
|
25176
25462
|
}
|
|
25177
25463
|
if (event._tag === "ContentDelta") {
|
|
25464
|
+
if (event.streamKind === "reasoning_text") {
|
|
25465
|
+
if (event.text.trim().length === 0) {
|
|
25466
|
+
continue;
|
|
25467
|
+
}
|
|
25468
|
+
emit(
|
|
25469
|
+
makeAcpContentDeltaEvent({
|
|
25470
|
+
stamp: stamp(),
|
|
25471
|
+
provider: options.provider,
|
|
25472
|
+
threadId: options.threadId,
|
|
25473
|
+
turnId: activeTurnId,
|
|
25474
|
+
text: event.text,
|
|
25475
|
+
streamKind: "reasoning_text",
|
|
25476
|
+
rawPayload: event.rawPayload
|
|
25477
|
+
})
|
|
25478
|
+
);
|
|
25479
|
+
continue;
|
|
25480
|
+
}
|
|
25178
25481
|
if (event.text.trim().length === 0 && !assistantSegment.activeItemId) {
|
|
25179
25482
|
continue;
|
|
25180
25483
|
}
|
|
@@ -25187,6 +25490,7 @@ function createAcpExecutor(options) {
|
|
|
25187
25490
|
turnId: activeTurnId,
|
|
25188
25491
|
...itemId ? { itemId } : {},
|
|
25189
25492
|
text: event.text,
|
|
25493
|
+
streamKind: "assistant_text",
|
|
25190
25494
|
rawPayload: event.rawPayload
|
|
25191
25495
|
})
|
|
25192
25496
|
);
|
|
@@ -25347,6 +25651,12 @@ function createAcpExecutor(options) {
|
|
|
25347
25651
|
const userInputOutcome = await handlePermissionAsUserInput(params);
|
|
25348
25652
|
if (userInputOutcome !== void 0) return userInputOutcome;
|
|
25349
25653
|
const permissionRequest = parsePermissionRequest(params);
|
|
25654
|
+
if (options.autoApprove?.(permissionRequest) === true) {
|
|
25655
|
+
const optionId = selectAutoApprovedAcpPermissionOption(params);
|
|
25656
|
+
if (optionId !== void 0) {
|
|
25657
|
+
return { outcome: { outcome: "selected", optionId } };
|
|
25658
|
+
}
|
|
25659
|
+
}
|
|
25350
25660
|
const requestId = randomId();
|
|
25351
25661
|
const outcome = await new Promise((resolve) => {
|
|
25352
25662
|
const pending = {
|
|
@@ -25434,36 +25744,70 @@ function createAcpExecutor(options) {
|
|
|
25434
25744
|
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) {
|
|
25435
25745
|
return input2.state;
|
|
25436
25746
|
}
|
|
25747
|
+
const value = matchAdvertisedModelValue(requestedModelId, input2.state.advertisedModelValues);
|
|
25748
|
+
if (value === void 0) {
|
|
25749
|
+
if (requestedModelId !== lastUnmatchedModelId) {
|
|
25750
|
+
lastUnmatchedModelId = requestedModelId;
|
|
25751
|
+
options.log?.info("requested model is not in the agent catalog; keeping its own model", {
|
|
25752
|
+
requestedModelId,
|
|
25753
|
+
currentModelId: input2.state.currentModelId,
|
|
25754
|
+
advertisedModelValues: input2.state.advertisedModelValues
|
|
25755
|
+
});
|
|
25756
|
+
}
|
|
25757
|
+
return input2.state;
|
|
25758
|
+
}
|
|
25759
|
+
if (value === input2.state.currentModelId) {
|
|
25760
|
+
return input2.state;
|
|
25761
|
+
}
|
|
25437
25762
|
if (input2.state.strategy === "config-option") {
|
|
25438
25763
|
await connection.request("session/set_config_option", {
|
|
25439
25764
|
sessionId: input2.acpSessionId,
|
|
25440
25765
|
configId: input2.state.modelConfigId,
|
|
25441
|
-
value
|
|
25766
|
+
value
|
|
25442
25767
|
});
|
|
25443
25768
|
} else {
|
|
25444
25769
|
await connection.request("session/set_model", {
|
|
25445
25770
|
sessionId: input2.acpSessionId,
|
|
25446
|
-
modelId:
|
|
25771
|
+
modelId: value
|
|
25447
25772
|
});
|
|
25448
25773
|
}
|
|
25449
25774
|
if (!isLive(input2.expectedGeneration)) {
|
|
25450
25775
|
throw new AcpExecutorDisposedError();
|
|
25451
25776
|
}
|
|
25452
|
-
return { ...input2.state, currentModelId:
|
|
25777
|
+
return { ...input2.state, currentModelId: value };
|
|
25778
|
+
};
|
|
25779
|
+
const applyRequestedEffort = async (input2) => {
|
|
25780
|
+
const requested = input2.requestedEffort?.trim();
|
|
25781
|
+
if (!requested) return;
|
|
25782
|
+
const configOptions = typeof input2.setup === "object" && input2.setup !== null ? input2.setup.configOptions : void 0;
|
|
25783
|
+
const effort = findAcpEffortConfig(configOptions);
|
|
25784
|
+
if (effort === void 0) return;
|
|
25785
|
+
const matched = effort.choices.find((choice) => choice.value === requested);
|
|
25786
|
+
if (matched === void 0) {
|
|
25787
|
+
options.log?.info("requested effort is not in the agent catalog; keeping its own", {
|
|
25788
|
+
requestedEffort: requested,
|
|
25789
|
+
advertised: effort.choices.map((choice) => choice.value)
|
|
25790
|
+
});
|
|
25791
|
+
return;
|
|
25792
|
+
}
|
|
25793
|
+
if (matched.isDefault === true) return;
|
|
25794
|
+
if (!isLive(input2.expectedGeneration)) return;
|
|
25795
|
+
try {
|
|
25796
|
+
await connection.request("session/set_config_option", {
|
|
25797
|
+
sessionId: input2.acpSessionId,
|
|
25798
|
+
configId: effort.configId,
|
|
25799
|
+
value: requested
|
|
25800
|
+
});
|
|
25801
|
+
} catch (error62) {
|
|
25802
|
+
options.log?.info("setting the effort level failed; the session keeps its own", {
|
|
25803
|
+
requestedEffort: requested,
|
|
25804
|
+
configId: effort.configId,
|
|
25805
|
+
error: error62 instanceof Error ? error62.message : String(error62)
|
|
25806
|
+
});
|
|
25807
|
+
}
|
|
25453
25808
|
};
|
|
25454
25809
|
const startOnce = async (expectedGeneration) => {
|
|
25455
|
-
const initializePayload =
|
|
25456
|
-
protocolVersion: PROTOCOL_VERSION,
|
|
25457
|
-
clientCapabilities: {
|
|
25458
|
-
fs: {
|
|
25459
|
-
readTextFile: false,
|
|
25460
|
-
writeTextFile: false,
|
|
25461
|
-
...options.clientCapabilities?.fs
|
|
25462
|
-
},
|
|
25463
|
-
terminal: options.clientCapabilities?.terminal ?? false
|
|
25464
|
-
},
|
|
25465
|
-
clientInfo: options.clientInfo
|
|
25466
|
-
};
|
|
25810
|
+
const initializePayload = acpInitializePayload(options.clientInfo, options.clientCapabilities);
|
|
25467
25811
|
const initialized = await connection.request(
|
|
25468
25812
|
"initialize",
|
|
25469
25813
|
initializePayload
|
|
@@ -25507,12 +25851,27 @@ function createAcpExecutor(options) {
|
|
|
25507
25851
|
nextSessionId = created.sessionId;
|
|
25508
25852
|
setup = created;
|
|
25509
25853
|
}
|
|
25854
|
+
const unhandledCategories = unhandledConfigCategories(
|
|
25855
|
+
setup.configOptions
|
|
25856
|
+
);
|
|
25857
|
+
if (unhandledCategories.length > 0) {
|
|
25858
|
+
options.log?.info("acp session reported config option categories we do not handle", {
|
|
25859
|
+
sessionId: nextSessionId,
|
|
25860
|
+
categories: unhandledCategories
|
|
25861
|
+
});
|
|
25862
|
+
}
|
|
25510
25863
|
const nextModelSelectionState = await applyRequestedModel({
|
|
25511
25864
|
state: modelSelectionStateFromSetup(setup),
|
|
25512
25865
|
acpSessionId: nextSessionId,
|
|
25513
25866
|
requestedModelId: options.requestedModelId,
|
|
25514
25867
|
expectedGeneration
|
|
25515
25868
|
});
|
|
25869
|
+
await applyRequestedEffort({
|
|
25870
|
+
setup,
|
|
25871
|
+
acpSessionId: nextSessionId,
|
|
25872
|
+
requestedEffort: options.requestedEffort,
|
|
25873
|
+
expectedGeneration
|
|
25874
|
+
});
|
|
25516
25875
|
if (options.beforeApplyResult) {
|
|
25517
25876
|
await options.beforeApplyResult();
|
|
25518
25877
|
}
|
|
@@ -25877,6 +26236,26 @@ function createAcpExecutor(options) {
|
|
|
25877
26236
|
};
|
|
25878
26237
|
apply();
|
|
25879
26238
|
};
|
|
26239
|
+
const setNativeMode = async (modeId) => {
|
|
26240
|
+
failIfDisposed();
|
|
26241
|
+
const started = startResult;
|
|
26242
|
+
if (started === void 0) {
|
|
26243
|
+
throw new AcpExecutorRequestError("session/set_mode", "session has not started yet");
|
|
26244
|
+
}
|
|
26245
|
+
const configId = acpModeConfigId(started.sessionSetupResult);
|
|
26246
|
+
if (configId !== void 0) {
|
|
26247
|
+
await connection.request("session/set_config_option", {
|
|
26248
|
+
sessionId: started.sessionId,
|
|
26249
|
+
configId,
|
|
26250
|
+
value: modeId
|
|
26251
|
+
});
|
|
26252
|
+
return;
|
|
26253
|
+
}
|
|
26254
|
+
await connection.request("session/set_mode", {
|
|
26255
|
+
sessionId: started.sessionId,
|
|
26256
|
+
modeId
|
|
26257
|
+
});
|
|
26258
|
+
};
|
|
25880
26259
|
const respondToUserInput = async (requestId, answers) => {
|
|
25881
26260
|
failIfDisposed();
|
|
25882
26261
|
const pending = pendingUserInputs.get(requestId);
|
|
@@ -25893,7 +26272,7 @@ function createAcpExecutor(options) {
|
|
|
25893
26272
|
pendingUserInputs.delete(requestId);
|
|
25894
26273
|
pending.resolve({
|
|
25895
26274
|
kind: "answered",
|
|
25896
|
-
answers:
|
|
26275
|
+
answers: isRecord5(answers) ? answers : {}
|
|
25897
26276
|
});
|
|
25898
26277
|
};
|
|
25899
26278
|
const dispose = async () => {
|
|
@@ -25943,10 +26322,53 @@ function createAcpExecutor(options) {
|
|
|
25943
26322
|
cancel,
|
|
25944
26323
|
respondToRequest,
|
|
25945
26324
|
respondToUserInput,
|
|
26325
|
+
setNativeMode,
|
|
25946
26326
|
dispose
|
|
25947
26327
|
};
|
|
25948
26328
|
}
|
|
25949
26329
|
|
|
26330
|
+
// ../../packages/ycodium-acp-executor/src/probe.ts
|
|
26331
|
+
function withProbeTimeout(promise2, timeoutMs, method) {
|
|
26332
|
+
return new Promise((resolve, reject) => {
|
|
26333
|
+
const timer = setTimeout(() => {
|
|
26334
|
+
reject(new Error(`ACP probe request '${method}' timed out after ${timeoutMs}ms`));
|
|
26335
|
+
}, timeoutMs);
|
|
26336
|
+
timer.unref?.();
|
|
26337
|
+
promise2.then(
|
|
26338
|
+
(value) => {
|
|
26339
|
+
clearTimeout(timer);
|
|
26340
|
+
resolve(value);
|
|
26341
|
+
},
|
|
26342
|
+
(error62) => {
|
|
26343
|
+
clearTimeout(timer);
|
|
26344
|
+
reject(error62 instanceof Error ? error62 : new Error(String(error62)));
|
|
26345
|
+
}
|
|
26346
|
+
);
|
|
26347
|
+
});
|
|
26348
|
+
}
|
|
26349
|
+
async function openAcpProbeHandshake(input2) {
|
|
26350
|
+
const connection = createAcpConnection({
|
|
26351
|
+
readable: input2.readable,
|
|
26352
|
+
writable: input2.writable,
|
|
26353
|
+
onNotification: () => void 0
|
|
26354
|
+
});
|
|
26355
|
+
try {
|
|
26356
|
+
const initializeResult = await withProbeTimeout(
|
|
26357
|
+
connection.request("initialize", acpInitializePayload(input2.clientInfo)),
|
|
26358
|
+
input2.requestTimeoutMs,
|
|
26359
|
+
"initialize"
|
|
26360
|
+
);
|
|
26361
|
+
return {
|
|
26362
|
+
initializeResult,
|
|
26363
|
+
request: (method, params) => withProbeTimeout(connection.request(method, params), input2.requestTimeoutMs, method),
|
|
26364
|
+
close: () => connection.close()
|
|
26365
|
+
};
|
|
26366
|
+
} catch (error62) {
|
|
26367
|
+
await connection.close().catch(() => void 0);
|
|
26368
|
+
throw error62;
|
|
26369
|
+
}
|
|
26370
|
+
}
|
|
26371
|
+
|
|
25950
26372
|
// src/card.ts
|
|
25951
26373
|
var ACP_DRIVER_KIND = "acp";
|
|
25952
26374
|
var ACP_CARD = {
|
|
@@ -25978,7 +26400,21 @@ var ACP_CARD = {
|
|
|
25978
26400
|
everydayHarness: false,
|
|
25979
26401
|
capabilities: {
|
|
25980
26402
|
sessionModelSwitch: "unsupported",
|
|
25981
|
-
|
|
26403
|
+
// **这一位仍是空头支票,切档走的是下面那位。** `setRuntimeMode` 在这里只改自己的
|
|
26404
|
+
// 会话记录,从不发 RPC——宿主四档发不到 CLI 上。它没删是因为编排还在读它(哪些手
|
|
26405
|
+
// 要重开会话),但用户真正切得动的是 `nativeModeSwitch` 那条:这只手自报几档、
|
|
26406
|
+
// 界面画几档、选哪档发哪档,不经四档翻译(各家语义本来就对不齐:grok 的
|
|
26407
|
+
// `acceptEdits` 实测等同 `default`,照样问)。
|
|
26408
|
+
runtimeModeSwitch: "in-session",
|
|
26409
|
+
// 能中途切自报的那几档。`setNativeMode` 按 agent 报的形状挑通道:报了
|
|
26410
|
+
// `configOptions` 里 `category: "mode"` 那一项就走 `session/set_config_option`
|
|
26411
|
+
// (claude-agent-acp),否则走 `session/set_mode`。
|
|
26412
|
+
//
|
|
26413
|
+
// 两条路都实测过:kimi `acp` 的 `session/set_mode plan` 回 `{}` 并推
|
|
26414
|
+
// `current_mode_update`,不存在的 modeId 被 `-32602 Unknown modeId` 拒掉——不是
|
|
26415
|
+
// Grok 那种「收下任何 modeId 都回 {}」的静默无效。dsh 两条都没有(`-32601`),
|
|
26416
|
+
// 所以它不声明这一位。
|
|
26417
|
+
nativeModeSwitch: true
|
|
25982
26418
|
},
|
|
25983
26419
|
credentialSources: [],
|
|
25984
26420
|
diagnostics: [],
|
|
@@ -26001,12 +26437,20 @@ var ACP_CARD = {
|
|
|
26001
26437
|
},
|
|
26002
26438
|
modelConnectivity: {
|
|
26003
26439
|
dialects: ["anthropic-messages", "openai-responses", "openai-chat-completions"],
|
|
26004
|
-
|
|
26440
|
+
// **通用 ACP 这张名片接不了外部端点,而且接不了是对的。** ACP 协议本身没有端点
|
|
26441
|
+
// 概念,地址与钥匙全靠各家 CLI 自己的环境变量,五家各不相同(kimi 的
|
|
26442
|
+
// `KIMI_MODEL_*`、qwen 的 `OPENAI_*`、gemini 的 `GOOGLE_GEMINI_*`……)。这张名片
|
|
26443
|
+
// 是认不出是哪一家时的兜底,它不知道该注什么。
|
|
26444
|
+
//
|
|
26445
|
+
// 原来这里写着 `true` 却一栏开关都不给——名片对外宣称能接,实际绑定时报一个含糊
|
|
26446
|
+
// 的 `driver-unknown`,而提案 0025 的进度表据此把 kimi 记成「通」。哪家要接,就为
|
|
26447
|
+
// 那一家单出一张名片(`resolveByLocator` 按启动命令认家),别回来改这一张。
|
|
26448
|
+
acceptsExternalEndpoints: false
|
|
26005
26449
|
}
|
|
26006
26450
|
};
|
|
26007
26451
|
|
|
26008
26452
|
// src/config.ts
|
|
26009
|
-
function
|
|
26453
|
+
function isRecord6(value) {
|
|
26010
26454
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
26011
26455
|
}
|
|
26012
26456
|
function readString(value) {
|
|
@@ -26019,7 +26463,7 @@ function readEnvironment(value) {
|
|
|
26019
26463
|
if (!Array.isArray(value)) return {};
|
|
26020
26464
|
const env = {};
|
|
26021
26465
|
for (const entry of value) {
|
|
26022
|
-
if (!
|
|
26466
|
+
if (!isRecord6(entry)) continue;
|
|
26023
26467
|
const name = readTrimmed(entry["name"]);
|
|
26024
26468
|
if (name.length === 0) continue;
|
|
26025
26469
|
env[name] = readString(entry["value"]);
|
|
@@ -26027,17 +26471,18 @@ function readEnvironment(value) {
|
|
|
26027
26471
|
return env;
|
|
26028
26472
|
}
|
|
26029
26473
|
function parseAcpInstanceConfig(config2) {
|
|
26030
|
-
if (!
|
|
26474
|
+
if (!isRecord6(config2)) {
|
|
26031
26475
|
return {
|
|
26032
26476
|
binaryPath: "",
|
|
26033
26477
|
launchArgs: "",
|
|
26034
26478
|
homePath: "",
|
|
26035
26479
|
homeEnvVar: "",
|
|
26036
26480
|
environment: {},
|
|
26037
|
-
envelopeDriver: void 0
|
|
26481
|
+
envelopeDriver: void 0,
|
|
26482
|
+
providerId: ""
|
|
26038
26483
|
};
|
|
26039
26484
|
}
|
|
26040
|
-
const nested =
|
|
26485
|
+
const nested = isRecord6(config2["config"]) ? config2["config"] : void 0;
|
|
26041
26486
|
const settings = nested ?? config2;
|
|
26042
26487
|
return {
|
|
26043
26488
|
binaryPath: readTrimmed(settings["binaryPath"]),
|
|
@@ -26045,7 +26490,8 @@ function parseAcpInstanceConfig(config2) {
|
|
|
26045
26490
|
homePath: readTrimmed(settings["homePath"]) || readTrimmed(config2["homePath"]),
|
|
26046
26491
|
homeEnvVar: readTrimmed(settings["homeEnvVar"]) || readTrimmed(config2["homeEnvVar"]),
|
|
26047
26492
|
environment: readEnvironment(config2["environment"]),
|
|
26048
|
-
envelopeDriver: readTrimmed(config2["driver"]) || void 0
|
|
26493
|
+
envelopeDriver: readTrimmed(config2["driver"]) || void 0,
|
|
26494
|
+
providerId: readTrimmed(settings["providerId"]) || readTrimmed(config2["providerId"])
|
|
26049
26495
|
};
|
|
26050
26496
|
}
|
|
26051
26497
|
function tokenizeLaunchArgs(value) {
|
|
@@ -26100,57 +26546,372 @@ function tokenizeLaunchArgs(value) {
|
|
|
26100
26546
|
return tokens;
|
|
26101
26547
|
}
|
|
26102
26548
|
|
|
26103
|
-
// src/
|
|
26104
|
-
import * as
|
|
26105
|
-
|
|
26106
|
-
|
|
26107
|
-
|
|
26549
|
+
// src/kimi.ts
|
|
26550
|
+
import * as NodeFS from "node:fs";
|
|
26551
|
+
import * as NodePath from "node:path";
|
|
26552
|
+
var KIMI_PROVIDER_ID = "kimiCode";
|
|
26553
|
+
var KIMI_HOME_ENV_VAR = "KIMI_CODE_HOME";
|
|
26554
|
+
var KIMI_USER_INPUT_TITLES = /* @__PURE__ */ new Set(["AskUserQuestion", "ExitPlanMode"]);
|
|
26555
|
+
var isRecord7 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
26556
|
+
function questionTextFromContent(content) {
|
|
26557
|
+
if (!Array.isArray(content)) return "";
|
|
26558
|
+
const parts = [];
|
|
26559
|
+
for (const entry of content) {
|
|
26560
|
+
if (!isRecord7(entry) || entry["type"] !== "content") continue;
|
|
26561
|
+
const inner = entry["content"];
|
|
26562
|
+
if (!isRecord7(inner) || inner["type"] !== "text") continue;
|
|
26563
|
+
const text = inner["text"];
|
|
26564
|
+
if (typeof text === "string") parts.push(text);
|
|
26565
|
+
}
|
|
26566
|
+
return parts.join("\n\n").trim();
|
|
26567
|
+
}
|
|
26568
|
+
function kimiPermissionUserInput(request) {
|
|
26569
|
+
const title = request.toolCall.title;
|
|
26570
|
+
if (typeof title !== "string" || !KIMI_USER_INPUT_TITLES.has(title)) return void 0;
|
|
26571
|
+
const ids = new Set(request.options.map((option) => option.optionId));
|
|
26572
|
+
if (request.options.length === 0 || ids.size !== request.options.length) {
|
|
26573
|
+
throw new Error("Kimi interaction must provide distinct response options.");
|
|
26574
|
+
}
|
|
26575
|
+
const question = questionTextFromContent(request.toolCall.content);
|
|
26576
|
+
if (question.length === 0) {
|
|
26577
|
+
throw new Error("Kimi interaction is missing a question or plan.");
|
|
26578
|
+
}
|
|
26579
|
+
return {
|
|
26580
|
+
id: request.toolCall.toolCallId,
|
|
26581
|
+
header: title,
|
|
26582
|
+
question,
|
|
26583
|
+
kind: title === "ExitPlanMode" ? "plan" : "question",
|
|
26584
|
+
allowFreeform: false,
|
|
26585
|
+
multiSelect: false,
|
|
26586
|
+
options: request.options.map((option) => ({
|
|
26587
|
+
id: option.optionId,
|
|
26588
|
+
label: option.name,
|
|
26589
|
+
description: ""
|
|
26590
|
+
}))
|
|
26591
|
+
};
|
|
26592
|
+
}
|
|
26593
|
+
var WIRE_LOG_SEGMENTS = ["agents", "main", "wire.jsonl"];
|
|
26594
|
+
var SESSIONS_DIR = "sessions";
|
|
26595
|
+
var TAIL_SCAN_BYTES = 256 * 1024;
|
|
26596
|
+
var positiveInt = (value) => typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.trunc(value) : void 0;
|
|
26597
|
+
function parseKimiContextWindow(contents) {
|
|
26598
|
+
let usedTokens;
|
|
26599
|
+
let maxTokens;
|
|
26600
|
+
for (const line of contents.split("\n")) {
|
|
26601
|
+
const trimmed = line.trim();
|
|
26602
|
+
if (trimmed.length === 0) continue;
|
|
26603
|
+
let record2;
|
|
26108
26604
|
try {
|
|
26109
|
-
|
|
26110
|
-
|
|
26111
|
-
|
|
26112
|
-
await new Promise((resolve) => {
|
|
26113
|
-
const onDone = () => {
|
|
26114
|
-
readable.off("drain", onDone);
|
|
26115
|
-
readable.off("close", onDone);
|
|
26116
|
-
resolve();
|
|
26117
|
-
};
|
|
26118
|
-
readable.once("drain", onDone);
|
|
26119
|
-
readable.once("close", onDone);
|
|
26120
|
-
});
|
|
26121
|
-
}
|
|
26122
|
-
}
|
|
26123
|
-
} catch (error62) {
|
|
26124
|
-
readable.destroy(error62 instanceof Error ? error62 : new Error(String(error62)));
|
|
26125
|
-
return;
|
|
26605
|
+
record2 = JSON.parse(trimmed);
|
|
26606
|
+
} catch {
|
|
26607
|
+
continue;
|
|
26126
26608
|
}
|
|
26127
|
-
if (
|
|
26128
|
-
|
|
26129
|
-
|
|
26130
|
-
|
|
26131
|
-
|
|
26132
|
-
})().catch(() => void 0);
|
|
26133
|
-
const writable = new NodeStream2.Writable({
|
|
26134
|
-
write(chunk, _encoding, callback) {
|
|
26135
|
-
const bytes = typeof chunk === "string" ? Buffer.from(chunk) : chunk;
|
|
26136
|
-
handle.writeStdin(bytes).then(
|
|
26137
|
-
() => callback(),
|
|
26138
|
-
(error62) => callback(error62 instanceof Error ? error62 : new Error(String(error62)))
|
|
26139
|
-
);
|
|
26140
|
-
},
|
|
26141
|
-
final(callback) {
|
|
26142
|
-
handle.closeStdin().then(
|
|
26143
|
-
() => callback(),
|
|
26144
|
-
(error62) => callback(error62 instanceof Error ? error62 : new Error(String(error62)))
|
|
26145
|
-
);
|
|
26609
|
+
if (!isRecord7(record2)) continue;
|
|
26610
|
+
const type = record2["type"];
|
|
26611
|
+
if (type === "token_counting.turn_recorded" || type === "token_counting.measured") {
|
|
26612
|
+
usedTokens = positiveInt(record2["tokens"]) ?? usedTokens;
|
|
26613
|
+
continue;
|
|
26146
26614
|
}
|
|
26615
|
+
if (type === "llm.request") {
|
|
26616
|
+
maxTokens = positiveInt(record2["maxTokens"]) ?? maxTokens;
|
|
26617
|
+
}
|
|
26618
|
+
}
|
|
26619
|
+
if (usedTokens === void 0) return void 0;
|
|
26620
|
+
return maxTokens === void 0 ? { usedTokens } : { usedTokens, maxTokens };
|
|
26621
|
+
}
|
|
26622
|
+
function readTail(filePath) {
|
|
26623
|
+
let handle;
|
|
26624
|
+
try {
|
|
26625
|
+
const size = NodeFS.statSync(filePath).size;
|
|
26626
|
+
if (!Number.isFinite(size) || size <= 0) return void 0;
|
|
26627
|
+
const length = size > TAIL_SCAN_BYTES ? TAIL_SCAN_BYTES : size;
|
|
26628
|
+
const buffer = Buffer.alloc(length);
|
|
26629
|
+
handle = NodeFS.openSync(filePath, "r");
|
|
26630
|
+
NodeFS.readSync(handle, buffer, 0, length, size - length);
|
|
26631
|
+
return buffer.toString("utf8");
|
|
26632
|
+
} catch {
|
|
26633
|
+
return void 0;
|
|
26634
|
+
} finally {
|
|
26635
|
+
if (handle !== void 0) {
|
|
26636
|
+
try {
|
|
26637
|
+
NodeFS.closeSync(handle);
|
|
26638
|
+
} catch {
|
|
26639
|
+
}
|
|
26640
|
+
}
|
|
26641
|
+
}
|
|
26642
|
+
}
|
|
26643
|
+
function findKimiSessionWireLog(input2) {
|
|
26644
|
+
const sessionsRoot = NodePath.join(input2.homePath, SESSIONS_DIR);
|
|
26645
|
+
let workspaces;
|
|
26646
|
+
try {
|
|
26647
|
+
workspaces = NodeFS.readdirSync(sessionsRoot);
|
|
26648
|
+
} catch {
|
|
26649
|
+
return void 0;
|
|
26650
|
+
}
|
|
26651
|
+
for (const workspace of workspaces) {
|
|
26652
|
+
const candidate = NodePath.join(
|
|
26653
|
+
sessionsRoot,
|
|
26654
|
+
workspace,
|
|
26655
|
+
input2.acpSessionId,
|
|
26656
|
+
...WIRE_LOG_SEGMENTS
|
|
26657
|
+
);
|
|
26658
|
+
if (NodeFS.existsSync(candidate)) return candidate;
|
|
26659
|
+
}
|
|
26660
|
+
return void 0;
|
|
26661
|
+
}
|
|
26662
|
+
function readKimiContextWindow(input2) {
|
|
26663
|
+
if (input2.homePath.trim().length === 0) return void 0;
|
|
26664
|
+
const wireLog = findKimiSessionWireLog(input2);
|
|
26665
|
+
if (wireLog === void 0) return void 0;
|
|
26666
|
+
const contents = readTail(wireLog);
|
|
26667
|
+
return contents === void 0 ? void 0 : parseKimiContextWindow(contents);
|
|
26668
|
+
}
|
|
26669
|
+
|
|
26670
|
+
// src/kimiModels.ts
|
|
26671
|
+
var PROBE_TIMEOUT_MS = 15e3;
|
|
26672
|
+
var MAX_OUTPUT_BYTES = 1024 * 1024;
|
|
26673
|
+
var KIMI_MODEL_LIST_ARGS = ["provider", "list", "--json"];
|
|
26674
|
+
function isRecord8(value) {
|
|
26675
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
26676
|
+
}
|
|
26677
|
+
function asString3(value) {
|
|
26678
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
|
|
26679
|
+
}
|
|
26680
|
+
function positiveInt2(value) {
|
|
26681
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.trunc(value) : void 0;
|
|
26682
|
+
}
|
|
26683
|
+
function kimiProbeModels(stdout) {
|
|
26684
|
+
let parsed;
|
|
26685
|
+
try {
|
|
26686
|
+
parsed = JSON.parse(stdout);
|
|
26687
|
+
} catch {
|
|
26688
|
+
return void 0;
|
|
26689
|
+
}
|
|
26690
|
+
if (!isRecord8(parsed)) return void 0;
|
|
26691
|
+
const models = parsed["models"];
|
|
26692
|
+
if (!isRecord8(models)) return void 0;
|
|
26693
|
+
const out = [];
|
|
26694
|
+
for (const [slug, entry] of Object.entries(models)) {
|
|
26695
|
+
const trimmed = slug.trim();
|
|
26696
|
+
if (trimmed.length === 0 || !isRecord8(entry)) continue;
|
|
26697
|
+
const name = asString3(entry["displayName"]);
|
|
26698
|
+
const contextWindow = positiveInt2(entry["maxContextSize"]);
|
|
26699
|
+
out.push({
|
|
26700
|
+
slug: trimmed,
|
|
26701
|
+
...name === void 0 ? {} : { name },
|
|
26702
|
+
...contextWindow === void 0 ? {} : { contextWindow }
|
|
26703
|
+
});
|
|
26704
|
+
}
|
|
26705
|
+
return out;
|
|
26706
|
+
}
|
|
26707
|
+
function withProbeTimeout2(promise2, what) {
|
|
26708
|
+
return new Promise((resolve, reject) => {
|
|
26709
|
+
const timer = setTimeout(
|
|
26710
|
+
() => reject(new Error(`${what} timed out after ${PROBE_TIMEOUT_MS}ms`)),
|
|
26711
|
+
PROBE_TIMEOUT_MS
|
|
26712
|
+
);
|
|
26713
|
+
timer.unref?.();
|
|
26714
|
+
promise2.then(
|
|
26715
|
+
(value) => {
|
|
26716
|
+
clearTimeout(timer);
|
|
26717
|
+
resolve(value);
|
|
26718
|
+
},
|
|
26719
|
+
(error62) => {
|
|
26720
|
+
clearTimeout(timer);
|
|
26721
|
+
reject(error62 instanceof Error ? error62 : new Error(String(error62)));
|
|
26722
|
+
}
|
|
26723
|
+
);
|
|
26147
26724
|
});
|
|
26148
|
-
|
|
26725
|
+
}
|
|
26726
|
+
function concat(chunks) {
|
|
26727
|
+
const total = chunks.reduce((sum, chunk) => sum + chunk.byteLength, 0);
|
|
26728
|
+
const out = new Uint8Array(total);
|
|
26729
|
+
let offset = 0;
|
|
26730
|
+
for (const chunk of chunks) {
|
|
26731
|
+
out.set(chunk, offset);
|
|
26732
|
+
offset += chunk.byteLength;
|
|
26733
|
+
}
|
|
26734
|
+
return out;
|
|
26735
|
+
}
|
|
26736
|
+
async function probeKimiModels(input2) {
|
|
26737
|
+
const { recipe, host, env } = input2;
|
|
26738
|
+
if (recipe.binaryPath.length === 0) return void 0;
|
|
26739
|
+
let handle;
|
|
26740
|
+
try {
|
|
26741
|
+
handle = await host.process.spawn({
|
|
26742
|
+
program: recipe.binaryPath,
|
|
26743
|
+
// 实例信封里的启动参数(Kimi 那行预填 `acp`)在这一栏用不上:读配置要换成
|
|
26744
|
+
// `provider list --json`,留着 `acp` 就等于让它起 ACP 服务然后什么都不问。
|
|
26745
|
+
args: KIMI_MODEL_LIST_ARGS,
|
|
26746
|
+
cwd: process.cwd(),
|
|
26747
|
+
owner: { bindingId: host.bindingId },
|
|
26748
|
+
...Object.keys(env).length > 0 ? { env } : {}
|
|
26749
|
+
});
|
|
26750
|
+
const spawned = handle;
|
|
26751
|
+
const collect = withProbeTimeout2(
|
|
26752
|
+
(async () => {
|
|
26753
|
+
const chunks = [];
|
|
26754
|
+
let total = 0;
|
|
26755
|
+
for await (const chunk of spawned.stdout) {
|
|
26756
|
+
total += chunk.byteLength;
|
|
26757
|
+
if (total > MAX_OUTPUT_BYTES) return void 0;
|
|
26758
|
+
chunks.push(chunk);
|
|
26759
|
+
}
|
|
26760
|
+
return new TextDecoder().decode(concat(chunks));
|
|
26761
|
+
})(),
|
|
26762
|
+
"kimi provider list"
|
|
26763
|
+
);
|
|
26764
|
+
const drain = (async () => {
|
|
26765
|
+
for await (const _chunk of spawned.stderr) {
|
|
26766
|
+
}
|
|
26767
|
+
})().catch(() => void 0);
|
|
26768
|
+
const [stdout, , exitCode] = await Promise.all([collect, drain, spawned.exited]);
|
|
26769
|
+
if (stdout === void 0) return void 0;
|
|
26770
|
+
if (exitCode !== 0) return void 0;
|
|
26771
|
+
return kimiProbeModels(stdout);
|
|
26772
|
+
} catch {
|
|
26773
|
+
return void 0;
|
|
26774
|
+
} finally {
|
|
26775
|
+
await handle?.kill("SIGTERM").catch(() => void 0);
|
|
26776
|
+
}
|
|
26777
|
+
}
|
|
26778
|
+
|
|
26779
|
+
// src/probeModels.ts
|
|
26780
|
+
import * as NodeFS2 from "node:fs";
|
|
26781
|
+
import * as NodeOS from "node:os";
|
|
26782
|
+
import * as NodePath2 from "node:path";
|
|
26783
|
+
import { nodeStreamsFromHandle } from "@ycodium-ai/plugin-api/process-stdin";
|
|
26784
|
+
var PROBE_REQUEST_TIMEOUT_MS = 15e3;
|
|
26785
|
+
var CLIENT_INFO = { name: "ycodium-agent-acp-probe", version: "0.2.0" };
|
|
26786
|
+
var ACP_EFFORT_OPTION_ID = "reasoning";
|
|
26787
|
+
function acpProbeSessionCwd(instanceId) {
|
|
26788
|
+
const safe = instanceId.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
26789
|
+
return NodePath2.join(NodeOS.tmpdir(), `ycodium-acp-probe-${safe}`);
|
|
26790
|
+
}
|
|
26791
|
+
function isRecord9(value) {
|
|
26792
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
26793
|
+
}
|
|
26794
|
+
function asString4(value) {
|
|
26795
|
+
return typeof value === "string" ? value : void 0;
|
|
26796
|
+
}
|
|
26797
|
+
function findAcpProbeSessionId(listed, probeCwd) {
|
|
26798
|
+
if (!isRecord9(listed) || !Array.isArray(listed["sessions"])) return void 0;
|
|
26799
|
+
for (const entry of listed["sessions"]) {
|
|
26800
|
+
if (!isRecord9(entry)) continue;
|
|
26801
|
+
if (asString4(entry["cwd"]) !== probeCwd) continue;
|
|
26802
|
+
const sessionId = asString4(entry["sessionId"]);
|
|
26803
|
+
if (sessionId !== void 0) return sessionId;
|
|
26804
|
+
}
|
|
26805
|
+
return void 0;
|
|
26806
|
+
}
|
|
26807
|
+
async function probeSessionSetup(handshake, probeCwd) {
|
|
26808
|
+
const listed = await handshake.request("session/list", {}).catch(() => void 0);
|
|
26809
|
+
const reusable = findAcpProbeSessionId(listed, probeCwd);
|
|
26810
|
+
if (reusable !== void 0) {
|
|
26811
|
+
const resumed = await handshake.request("session/resume", { sessionId: reusable, cwd: probeCwd, mcpServers: [] }).catch(() => void 0);
|
|
26812
|
+
if (resumed !== void 0) return resumed;
|
|
26813
|
+
}
|
|
26814
|
+
return handshake.request("session/new", { cwd: probeCwd, mcpServers: [] });
|
|
26815
|
+
}
|
|
26816
|
+
function acpProbeModels(setup) {
|
|
26817
|
+
if (!isRecord9(setup)) return void 0;
|
|
26818
|
+
const rawOptions = setup["configOptions"];
|
|
26819
|
+
const choices = acpModelChoicesFromConfigOptions(rawOptions) ?? acpModelChoicesFromSessionModels(setup["models"]);
|
|
26820
|
+
if (choices === void 0 || choices.length === 0) return void 0;
|
|
26821
|
+
const models = choices.map((choice) => ({
|
|
26822
|
+
slug: choice.value,
|
|
26823
|
+
...choice.name === void 0 ? {} : { name: choice.name },
|
|
26824
|
+
...choice.isDefault === true ? { isDefault: true } : {}
|
|
26825
|
+
}));
|
|
26826
|
+
const effort = findAcpEffortConfig(rawOptions);
|
|
26827
|
+
if (effort === void 0) return models;
|
|
26828
|
+
const capabilities = {
|
|
26829
|
+
optionDescriptors: [
|
|
26830
|
+
{
|
|
26831
|
+
id: ACP_EFFORT_OPTION_ID,
|
|
26832
|
+
label: effort.label || "Reasoning",
|
|
26833
|
+
type: "select",
|
|
26834
|
+
options: effort.choices.map((choice) => ({
|
|
26835
|
+
id: choice.value,
|
|
26836
|
+
label: choice.label,
|
|
26837
|
+
...choice.description === void 0 ? {} : { description: choice.description },
|
|
26838
|
+
...choice.isDefault === true ? { isDefault: true } : {}
|
|
26839
|
+
}))
|
|
26840
|
+
}
|
|
26841
|
+
]
|
|
26842
|
+
};
|
|
26843
|
+
return models.map((model) => ({ ...model, capabilities }));
|
|
26844
|
+
}
|
|
26845
|
+
var failureDetail = (error62) => (error62 instanceof Error ? error62.message : String(error62)).trim() || void 0;
|
|
26846
|
+
async function probeAcpModelsViaSession(input2) {
|
|
26847
|
+
const { recipe, host, env } = input2;
|
|
26848
|
+
const probeCwd = acpProbeSessionCwd(host.instanceId);
|
|
26849
|
+
try {
|
|
26850
|
+
NodeFS2.mkdirSync(probeCwd, { recursive: true });
|
|
26851
|
+
} catch (error62) {
|
|
26852
|
+
return { kind: "failed", detail: failureDetail(error62) };
|
|
26853
|
+
}
|
|
26854
|
+
let handle;
|
|
26855
|
+
try {
|
|
26856
|
+
handle = await host.process.spawn({
|
|
26857
|
+
program: recipe.binaryPath,
|
|
26858
|
+
args: tokenizeLaunchArgs(recipe.launchArgs),
|
|
26859
|
+
cwd: probeCwd,
|
|
26860
|
+
owner: { bindingId: host.bindingId },
|
|
26861
|
+
...Object.keys(env).length > 0 ? { env } : {}
|
|
26862
|
+
});
|
|
26863
|
+
} catch (error62) {
|
|
26864
|
+
return { kind: "not-started", detail: failureDetail(error62) };
|
|
26865
|
+
}
|
|
26866
|
+
let handshake;
|
|
26867
|
+
try {
|
|
26868
|
+
const { readable, writable } = nodeStreamsFromHandle(handle);
|
|
26869
|
+
handshake = await openAcpProbeHandshake({
|
|
26870
|
+
readable,
|
|
26871
|
+
writable,
|
|
26872
|
+
clientInfo: CLIENT_INFO,
|
|
26873
|
+
requestTimeoutMs: PROBE_REQUEST_TIMEOUT_MS
|
|
26874
|
+
});
|
|
26875
|
+
const setup = await probeSessionSetup(handshake, probeCwd);
|
|
26876
|
+
return {
|
|
26877
|
+
kind: "answered",
|
|
26878
|
+
models: acpProbeModels(setup),
|
|
26879
|
+
// 审批档原样上报:宿主落进 provider 快照的 `nativeModes`,composer 直接画这只手
|
|
26880
|
+
// 自己的那几档,不再退回宿主四档开关。
|
|
26881
|
+
nativeModes: acpAvailableModes(setup).map((mode) => ({
|
|
26882
|
+
id: mode.id,
|
|
26883
|
+
name: mode.name,
|
|
26884
|
+
...mode.description === void 0 ? {} : { description: mode.description }
|
|
26885
|
+
})),
|
|
26886
|
+
currentNativeModeId: acpCurrentModeId(setup)
|
|
26887
|
+
};
|
|
26888
|
+
} catch (error62) {
|
|
26889
|
+
return { kind: "failed", detail: failureDetail(error62) };
|
|
26890
|
+
} finally {
|
|
26891
|
+
await handshake?.close().catch(() => void 0);
|
|
26892
|
+
await handle.kill("SIGTERM").catch(() => void 0);
|
|
26893
|
+
}
|
|
26894
|
+
}
|
|
26895
|
+
|
|
26896
|
+
// src/runtimeModeApproval.ts
|
|
26897
|
+
function acpRuntimeModeAutoApproves(runtimeMode, toolKind) {
|
|
26898
|
+
switch (runtimeMode) {
|
|
26899
|
+
case "full-access":
|
|
26900
|
+
return true;
|
|
26901
|
+
case "auto-accept-edits":
|
|
26902
|
+
return toolKind === "edit";
|
|
26903
|
+
case "approval-required":
|
|
26904
|
+
case "auto":
|
|
26905
|
+
return false;
|
|
26906
|
+
}
|
|
26907
|
+
}
|
|
26908
|
+
function acpSessionModeToApply(requestedNativeModeId, startedModeId) {
|
|
26909
|
+
return requestedNativeModeId !== void 0 && requestedNativeModeId !== startedModeId ? requestedNativeModeId : void 0;
|
|
26149
26910
|
}
|
|
26150
26911
|
|
|
26151
26912
|
// src/acpInstance.ts
|
|
26152
26913
|
var RESUME_SCHEMA_VERSION = 1;
|
|
26153
|
-
var
|
|
26914
|
+
var CLIENT_INFO2 = { name: "ycodium-agent-acp", version: "0.2.0" };
|
|
26154
26915
|
var yieldTick = () => new Promise((resolve) => {
|
|
26155
26916
|
setImmediate(resolve);
|
|
26156
26917
|
});
|
|
@@ -26187,19 +26948,12 @@ var EventBus = class {
|
|
|
26187
26948
|
}
|
|
26188
26949
|
};
|
|
26189
26950
|
function expandHomePath(value) {
|
|
26190
|
-
if (value === "~") return
|
|
26951
|
+
if (value === "~") return NodeOS2.homedir();
|
|
26191
26952
|
if (value.startsWith("~/") || value.startsWith("~\\")) {
|
|
26192
|
-
return
|
|
26953
|
+
return NodePath3.join(NodeOS2.homedir(), value.slice(2));
|
|
26193
26954
|
}
|
|
26194
26955
|
return value;
|
|
26195
26956
|
}
|
|
26196
|
-
function compactEnv(env) {
|
|
26197
|
-
const out = {};
|
|
26198
|
-
for (const [key, value] of Object.entries(env)) {
|
|
26199
|
-
if (value !== void 0) out[key] = value;
|
|
26200
|
-
}
|
|
26201
|
-
return out;
|
|
26202
|
-
}
|
|
26203
26957
|
function modelIdFromSelection(selection) {
|
|
26204
26958
|
if (typeof selection !== "object" || selection === null || Array.isArray(selection)) {
|
|
26205
26959
|
return void 0;
|
|
@@ -26207,19 +26961,42 @@ function modelIdFromSelection(selection) {
|
|
|
26207
26961
|
const model = selection["model"];
|
|
26208
26962
|
return typeof model === "string" ? model.trim() || void 0 : void 0;
|
|
26209
26963
|
}
|
|
26210
|
-
function buildSpawnEnv(recipe,
|
|
26211
|
-
const homePath =
|
|
26964
|
+
function buildSpawnEnv(recipe, configuredHomePath, launchEnv = {}) {
|
|
26965
|
+
const homePath = configuredHomePath.trim();
|
|
26212
26966
|
const homeEnvVar = recipe.homeEnvVar.trim();
|
|
26213
26967
|
const homeInjection = homePath.length > 0 && homeEnvVar.length > 0 ? { [homeEnvVar]: expandHomePath(homePath) } : {};
|
|
26214
|
-
return {
|
|
26215
|
-
|
|
26216
|
-
|
|
26217
|
-
|
|
26218
|
-
|
|
26968
|
+
return mergeLaunchEnv({
|
|
26969
|
+
brainSlots: brainSlotEnvKeys(ACP_CARD),
|
|
26970
|
+
accountHome: homeInjection,
|
|
26971
|
+
instanceTable: recipe.environment,
|
|
26972
|
+
launch: launchEnv
|
|
26973
|
+
});
|
|
26219
26974
|
}
|
|
26220
26975
|
function nowIso() {
|
|
26221
26976
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
26222
26977
|
}
|
|
26978
|
+
function resolveHomePath(recipe, launchHomePath) {
|
|
26979
|
+
const envVar = recipe.homeEnvVar.trim();
|
|
26980
|
+
const fromEnvironment = envVar.length > 0 ? recipe.environment[envVar]?.trim() : void 0;
|
|
26981
|
+
if (fromEnvironment !== void 0 && fromEnvironment.length > 0) {
|
|
26982
|
+
return expandHomePath(fromEnvironment);
|
|
26983
|
+
}
|
|
26984
|
+
const configured = (launchHomePath?.trim() || recipe.homePath).trim();
|
|
26985
|
+
return configured.length > 0 ? expandHomePath(configured) : "";
|
|
26986
|
+
}
|
|
26987
|
+
function isKimiInstance(recipe) {
|
|
26988
|
+
return recipe.providerId === KIMI_PROVIDER_ID || recipe.homeEnvVar.trim() === KIMI_HOME_ENV_VAR;
|
|
26989
|
+
}
|
|
26990
|
+
function vendorHooksFor(recipe, launchHomePath) {
|
|
26991
|
+
if (!isKimiInstance(recipe)) return void 0;
|
|
26992
|
+
const homePath = resolveHomePath(recipe, launchHomePath);
|
|
26993
|
+
return {
|
|
26994
|
+
permissionUserInput: kimiPermissionUserInput,
|
|
26995
|
+
// ACP 的 `usage_update` 我们这边不消费,进度圈只能从各家自己写在磁盘上的
|
|
26996
|
+
// 记录里读。目前只有 Kimi 的格式核实过。
|
|
26997
|
+
readContextWindow: ({ sessionId }) => readKimiContextWindow({ homePath, acpSessionId: sessionId })
|
|
26998
|
+
};
|
|
26999
|
+
}
|
|
26223
27000
|
async function stopBooked(booked) {
|
|
26224
27001
|
await booked.executor.dispose().catch(() => void 0);
|
|
26225
27002
|
await booked.handle.kill("SIGTERM").catch(() => void 0);
|
|
@@ -26246,11 +27023,33 @@ var createAcpInstance = (config2, host) => {
|
|
|
26246
27023
|
detail: "ACP program path is not configured"
|
|
26247
27024
|
};
|
|
26248
27025
|
}
|
|
27026
|
+
const outcome = isKimiInstance(recipe) ? {
|
|
27027
|
+
kind: "answered",
|
|
27028
|
+
models: await probeKimiModels({
|
|
27029
|
+
recipe,
|
|
27030
|
+
host,
|
|
27031
|
+
env: buildSpawnEnv(recipe, recipe.homePath)
|
|
27032
|
+
}),
|
|
27033
|
+
nativeModes: void 0,
|
|
27034
|
+
currentNativeModeId: void 0
|
|
27035
|
+
} : await probeAcpModelsViaSession({
|
|
27036
|
+
recipe,
|
|
27037
|
+
host,
|
|
27038
|
+
env: buildSpawnEnv(recipe, recipe.homePath)
|
|
27039
|
+
});
|
|
27040
|
+
const models = outcome.kind === "answered" ? outcome.models : void 0;
|
|
27041
|
+
const nativeModes = outcome.kind === "answered" && outcome.nativeModes !== void 0 ? outcome.nativeModes : void 0;
|
|
27042
|
+
const currentNativeModeId = outcome.kind === "answered" ? outcome.currentNativeModeId : void 0;
|
|
26249
27043
|
return {
|
|
26250
27044
|
installed: true,
|
|
26251
27045
|
version: null,
|
|
26252
27046
|
readiness: "unchecked",
|
|
26253
|
-
auth: { status: "unknown" }
|
|
27047
|
+
auth: { status: "unknown" },
|
|
27048
|
+
...models === void 0 ? {} : { models },
|
|
27049
|
+
...nativeModes === void 0 || nativeModes.length === 0 ? {} : {
|
|
27050
|
+
nativeModes,
|
|
27051
|
+
...currentNativeModeId === void 0 ? {} : { currentNativeModeId }
|
|
27052
|
+
}
|
|
26254
27053
|
};
|
|
26255
27054
|
},
|
|
26256
27055
|
startSession: async (input2, signal) => {
|
|
@@ -26266,7 +27065,11 @@ var createAcpInstance = (config2, host) => {
|
|
|
26266
27065
|
await stopBooked(existing);
|
|
26267
27066
|
threads.delete(input2.threadId);
|
|
26268
27067
|
}
|
|
26269
|
-
const env = buildSpawnEnv(
|
|
27068
|
+
const env = buildSpawnEnv(
|
|
27069
|
+
recipe,
|
|
27070
|
+
input2.launch.homePath?.trim() || recipe.homePath,
|
|
27071
|
+
input2.launch.processEnv
|
|
27072
|
+
);
|
|
26270
27073
|
const handle = await host.process.spawn({
|
|
26271
27074
|
program: recipe.binaryPath,
|
|
26272
27075
|
args: tokenizeLaunchArgs(recipe.launchArgs),
|
|
@@ -26274,19 +27077,32 @@ var createAcpInstance = (config2, host) => {
|
|
|
26274
27077
|
owner: { bindingId: host.bindingId },
|
|
26275
27078
|
...Object.keys(env).length > 0 ? { env } : {}
|
|
26276
27079
|
});
|
|
26277
|
-
const { readable, writable } =
|
|
27080
|
+
const { readable, writable } = nodeStreamsFromHandle2(handle);
|
|
26278
27081
|
const resume = input2.discardResumeCursor === true ? void 0 : parseAcpResumeCursor(input2.resumeCursor, RESUME_SCHEMA_VERSION);
|
|
26279
27082
|
const requestedModelId = input2.launch.requestedModelId?.trim() || modelIdFromSelection(input2.modelSelection);
|
|
27083
|
+
const requestedEffort = modelSelectionStringOption(
|
|
27084
|
+
input2.modelSelection,
|
|
27085
|
+
ACP_EFFORT_OPTION_ID
|
|
27086
|
+
);
|
|
27087
|
+
const hooks = vendorHooksFor(recipe, input2.launch.homePath);
|
|
27088
|
+
const autoApprove = input2.nativeModeId === void 0 ? (request) => acpRuntimeModeAutoApproves(
|
|
27089
|
+
threads.get(input2.threadId)?.session.runtimeMode ?? input2.runtimeMode,
|
|
27090
|
+
request.kind
|
|
27091
|
+
) : void 0;
|
|
26280
27092
|
const executor = createAcpExecutor({
|
|
26281
27093
|
readable,
|
|
26282
27094
|
writable,
|
|
27095
|
+
...requestedEffort === void 0 ? {} : { requestedEffort },
|
|
26283
27096
|
cwd,
|
|
26284
27097
|
threadId: input2.threadId,
|
|
26285
27098
|
provider: ACP_DRIVER_KIND,
|
|
26286
27099
|
providerInstanceId: host.instanceId,
|
|
26287
|
-
clientInfo:
|
|
27100
|
+
clientInfo: CLIENT_INFO2,
|
|
27101
|
+
log: host.log,
|
|
26288
27102
|
...requestedModelId === void 0 ? {} : { requestedModelId },
|
|
26289
27103
|
...input2.bindingGeneration === void 0 ? {} : { originBindingGeneration: input2.bindingGeneration },
|
|
27104
|
+
...hooks === void 0 ? {} : { hooks },
|
|
27105
|
+
...autoApprove === void 0 ? {} : { autoApprove },
|
|
26290
27106
|
...resume === void 0 ? {} : { resumeSessionId: resume.sessionId }
|
|
26291
27107
|
});
|
|
26292
27108
|
const forwarding = (async () => {
|
|
@@ -26296,6 +27112,13 @@ var createAcpInstance = (config2, host) => {
|
|
|
26296
27112
|
})();
|
|
26297
27113
|
try {
|
|
26298
27114
|
const started = await executor.start();
|
|
27115
|
+
const modeToApply = acpSessionModeToApply(
|
|
27116
|
+
input2.nativeModeId,
|
|
27117
|
+
acpCurrentModeId(started.sessionSetupResult)
|
|
27118
|
+
);
|
|
27119
|
+
if (modeToApply !== void 0) {
|
|
27120
|
+
await executor.setNativeMode(modeToApply);
|
|
27121
|
+
}
|
|
26299
27122
|
await yieldTick();
|
|
26300
27123
|
const createdAt = nowIso();
|
|
26301
27124
|
const resumeCursor = {
|
|
@@ -26307,6 +27130,7 @@ var createAcpInstance = (config2, host) => {
|
|
|
26307
27130
|
providerInstanceId: host.instanceId,
|
|
26308
27131
|
status: "idle",
|
|
26309
27132
|
runtimeMode: input2.runtimeMode,
|
|
27133
|
+
...input2.nativeModeId === void 0 ? {} : { nativeModeId: input2.nativeModeId },
|
|
26310
27134
|
cwd,
|
|
26311
27135
|
threadId: input2.threadId,
|
|
26312
27136
|
createdAt,
|
|
@@ -26386,8 +27210,25 @@ var createAcpInstance = (config2, host) => {
|
|
|
26386
27210
|
reason
|
|
26387
27211
|
);
|
|
26388
27212
|
},
|
|
26389
|
-
respondToUserInput: async () => {
|
|
26390
|
-
|
|
27213
|
+
respondToUserInput: async (threadId, requestId, answers) => {
|
|
27214
|
+
const booked = requireThread(threadId);
|
|
27215
|
+
await booked.executor.respondToUserInput(requestId, answers);
|
|
27216
|
+
},
|
|
27217
|
+
/**
|
|
27218
|
+
* 切这只手自报的审批档。`nativeModeId` 是它自己报的 id(探测时原样上报的那些),
|
|
27219
|
+
* 一路不翻译成宿主四档。
|
|
27220
|
+
*
|
|
27221
|
+
* 这是 `setRuntimeMode` 做不到的那一半:那个只改插件自己的会话记录,CLI 收不到
|
|
27222
|
+
* (`runtime-mode-never-reaches-cli` 记的那族缺陷)。
|
|
27223
|
+
*/
|
|
27224
|
+
setNativeMode: async (threadId, nativeModeId) => {
|
|
27225
|
+
const booked = requireThread(threadId);
|
|
27226
|
+
await booked.executor.setNativeMode(nativeModeId);
|
|
27227
|
+
booked.session = {
|
|
27228
|
+
...booked.session,
|
|
27229
|
+
nativeModeId,
|
|
27230
|
+
updatedAt: nowIso()
|
|
27231
|
+
};
|
|
26391
27232
|
},
|
|
26392
27233
|
setRuntimeMode: async (threadId, runtimeMode) => {
|
|
26393
27234
|
const booked = requireThread(threadId);
|