@ycodium-ai/agent-acp 0.2.2106 → 0.2.2130
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 +963 -137
- package/package.json +3 -3
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";
|
|
@@ -5092,7 +5098,7 @@ var recursive = /* @__PURE__ */ new WeakMap();
|
|
|
5092
5098
|
var NONE = 0;
|
|
5093
5099
|
var ASSUMED = 1;
|
|
5094
5100
|
var PROVEN = 2;
|
|
5095
|
-
function isRecursive(inst, stack,
|
|
5101
|
+
function isRecursive(inst, stack, resolve2) {
|
|
5096
5102
|
const cached2 = recursive.get(inst);
|
|
5097
5103
|
if (cached2 !== void 0)
|
|
5098
5104
|
return cached2 ? PROVEN : NONE;
|
|
@@ -5102,7 +5108,7 @@ function isRecursive(inst, stack, resolve) {
|
|
|
5102
5108
|
let result = NONE;
|
|
5103
5109
|
const check2 = (child) => {
|
|
5104
5110
|
if (result !== PROVEN && child?._zod) {
|
|
5105
|
-
const answer = isRecursive(child, stack,
|
|
5111
|
+
const answer = isRecursive(child, stack, resolve2);
|
|
5106
5112
|
if (answer > result)
|
|
5107
5113
|
result = answer;
|
|
5108
5114
|
}
|
|
@@ -5113,7 +5119,7 @@ function isRecursive(inst, stack, resolve) {
|
|
|
5113
5119
|
const desc = Object.getOwnPropertyDescriptor(sh, key);
|
|
5114
5120
|
if (spread && !desc.enumerable)
|
|
5115
5121
|
continue;
|
|
5116
|
-
const child = desc.get ? ASSUMED : desc.value?._zod ? isRecursive(desc.value, stack,
|
|
5122
|
+
const child = desc.get ? ASSUMED : desc.value?._zod ? isRecursive(desc.value, stack, resolve2) : NONE;
|
|
5117
5123
|
if (child > answer)
|
|
5118
5124
|
answer = child;
|
|
5119
5125
|
}
|
|
@@ -5177,7 +5183,7 @@ function isRecursive(inst, stack, resolve) {
|
|
|
5177
5183
|
break;
|
|
5178
5184
|
// `$ZodLazy` caches its inner on the def, so a resolved edge is followed exactly
|
|
5179
5185
|
case "lazy": {
|
|
5180
|
-
const inner = def._cachedInner ?? (
|
|
5186
|
+
const inner = def._cachedInner ?? (resolve2 ? inst._zod.innerType : void 0);
|
|
5181
5187
|
merge2(inner ? isRecursive(inner, stack, false) : ASSUMED);
|
|
5182
5188
|
break;
|
|
5183
5189
|
}
|
|
@@ -21841,11 +21847,11 @@ var Connection = class {
|
|
|
21841
21847
|
const id = this.nextRequestId++;
|
|
21842
21848
|
let cancel = () => {
|
|
21843
21849
|
};
|
|
21844
|
-
const response = new Promise((
|
|
21850
|
+
const response = new Promise((resolve2, reject) => {
|
|
21845
21851
|
const pendingResponse = {
|
|
21846
21852
|
resolve: (value) => {
|
|
21847
21853
|
try {
|
|
21848
|
-
|
|
21854
|
+
resolve2(mapResponse ? mapResponse(value) : value);
|
|
21849
21855
|
} catch (error62) {
|
|
21850
21856
|
reject(error62);
|
|
21851
21857
|
}
|
|
@@ -21902,8 +21908,8 @@ var Connection = class {
|
|
|
21902
21908
|
this.stream = stream;
|
|
21903
21909
|
this.staticHandlers = handlers;
|
|
21904
21910
|
this.allowBatches = options?.allowBatches ?? true;
|
|
21905
|
-
this.closedPromise = new Promise((
|
|
21906
|
-
this.abortController.signal.addEventListener("abort", () =>
|
|
21911
|
+
this.closedPromise = new Promise((resolve2) => {
|
|
21912
|
+
this.abortController.signal.addEventListener("abort", () => resolve2());
|
|
21907
21913
|
});
|
|
21908
21914
|
void this.receive();
|
|
21909
21915
|
}
|
|
@@ -22797,8 +22803,8 @@ var AsyncQueue = class {
|
|
|
22797
22803
|
if (this.failed) {
|
|
22798
22804
|
return Promise.reject(this.failure);
|
|
22799
22805
|
}
|
|
22800
|
-
return new Promise((
|
|
22801
|
-
this.waiters.push({ resolve, reject });
|
|
22806
|
+
return new Promise((resolve2, reject) => {
|
|
22807
|
+
this.waiters.push({ resolve: resolve2, reject });
|
|
22802
22808
|
});
|
|
22803
22809
|
}
|
|
22804
22810
|
};
|
|
@@ -23823,8 +23829,8 @@ function createAcpConnection(options) {
|
|
|
23823
23829
|
failIfClosed();
|
|
23824
23830
|
const id = nextRequestId;
|
|
23825
23831
|
nextRequestId += 1;
|
|
23826
|
-
const { promise: promise2, resolve, reject } = Promise.withResolvers();
|
|
23827
|
-
pending.set(String(id), { method, resolve, reject });
|
|
23832
|
+
const { promise: promise2, resolve: resolve2, reject } = Promise.withResolvers();
|
|
23833
|
+
pending.set(String(id), { method, resolve: resolve2, reject });
|
|
23828
23834
|
const message = params === void 0 ? { jsonrpc: "2.0", id, method } : { jsonrpc: "2.0", id, method, params };
|
|
23829
23835
|
try {
|
|
23830
23836
|
await writeMessage(message);
|
|
@@ -23845,8 +23851,8 @@ function createAcpConnection(options) {
|
|
|
23845
23851
|
if (inboundBatches === 0) {
|
|
23846
23852
|
return;
|
|
23847
23853
|
}
|
|
23848
|
-
await new Promise((
|
|
23849
|
-
idleWaiters.push(
|
|
23854
|
+
await new Promise((resolve2) => {
|
|
23855
|
+
idleWaiters.push(resolve2);
|
|
23850
23856
|
if (inboundBatches === 0) {
|
|
23851
23857
|
releaseIdleWaiters();
|
|
23852
23858
|
}
|
|
@@ -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;
|
|
@@ -24019,8 +24031,8 @@ var UnboundedEventStream = class {
|
|
|
24019
24031
|
iterators.subscribers.delete(subscriber);
|
|
24020
24032
|
return { value: void 0, done: true };
|
|
24021
24033
|
}
|
|
24022
|
-
await new Promise((
|
|
24023
|
-
subscriber.wake =
|
|
24034
|
+
await new Promise((resolve2) => {
|
|
24035
|
+
subscriber.wake = resolve2;
|
|
24024
24036
|
});
|
|
24025
24037
|
}
|
|
24026
24038
|
},
|
|
@@ -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;
|
|
@@ -24284,8 +24523,8 @@ var Mutex = class {
|
|
|
24284
24523
|
this.locked = true;
|
|
24285
24524
|
return Promise.resolve();
|
|
24286
24525
|
}
|
|
24287
|
-
return new Promise((
|
|
24288
|
-
this.waiters.push(
|
|
24526
|
+
return new Promise((resolve2) => {
|
|
24527
|
+
this.waiters.push(resolve2);
|
|
24289
24528
|
});
|
|
24290
24529
|
}
|
|
24291
24530
|
tryAcquire() {
|
|
@@ -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({
|
|
@@ -24947,7 +25226,7 @@ function parseSessionUpdateEvent(params) {
|
|
|
24947
25226
|
}
|
|
24948
25227
|
case "agent_message_chunk":
|
|
24949
25228
|
case "agent_thought_chunk": {
|
|
24950
|
-
const content =
|
|
25229
|
+
const content = isRecord5(upd["content"]) ? upd["content"] : void 0;
|
|
24951
25230
|
const contentType = content?.["type"];
|
|
24952
25231
|
const streamKind = kind === "agent_thought_chunk" ? "reasoning_text" : "assistant_text";
|
|
24953
25232
|
if (contentType === "text") {
|
|
@@ -25014,16 +25293,18 @@ function normalizeModelId(value) {
|
|
|
25014
25293
|
return value?.trim() || void 0;
|
|
25015
25294
|
}
|
|
25016
25295
|
function modelSelectionStateFromSetup(setup) {
|
|
25017
|
-
const sessionModels =
|
|
25296
|
+
const sessionModels = isRecord5(setup.models) ? setup.models : void 0;
|
|
25018
25297
|
const modelConfigId = extractModelConfigId(setup);
|
|
25019
25298
|
const strategy = sessionModels === void 0 && modelConfigId !== void 0 ? "config-option" : "session/set_model";
|
|
25020
|
-
const
|
|
25299
|
+
const modelConfigOption = modelConfigId === void 0 ? void 0 : findSessionConfigOption(setup.configOptions, modelConfigId);
|
|
25300
|
+
const configModel = modelConfigOption?.currentValue;
|
|
25021
25301
|
const currentModelId = strategy === "config-option" ? typeof configModel === "string" ? normalizeModelId(configModel) : void 0 : typeof sessionModels?.["currentModelId"] === "string" ? normalizeModelId(sessionModels["currentModelId"]) : void 0;
|
|
25022
25302
|
return {
|
|
25023
25303
|
strategy,
|
|
25024
25304
|
sessionModelsAdvertised: sessionModels !== void 0,
|
|
25025
25305
|
modelConfigId,
|
|
25026
|
-
currentModelId
|
|
25306
|
+
currentModelId,
|
|
25307
|
+
advertisedModelValues: strategy === "config-option" ? modelConfigOption === void 0 ? [] : collectSessionConfigOptionValues(modelConfigOption) : collectSessionModelIds(sessionModels)
|
|
25027
25308
|
};
|
|
25028
25309
|
}
|
|
25029
25310
|
function createAcpExecutor(options) {
|
|
@@ -25042,8 +25323,10 @@ function createAcpExecutor(options) {
|
|
|
25042
25323
|
strategy: "session/set_model",
|
|
25043
25324
|
sessionModelsAdvertised: false,
|
|
25044
25325
|
modelConfigId: void 0,
|
|
25045
|
-
currentModelId: void 0
|
|
25326
|
+
currentModelId: void 0,
|
|
25327
|
+
advertisedModelValues: []
|
|
25046
25328
|
};
|
|
25329
|
+
let lastUnmatchedModelId;
|
|
25047
25330
|
let initializeResult;
|
|
25048
25331
|
let startResult;
|
|
25049
25332
|
let startInFlight;
|
|
@@ -25368,15 +25651,21 @@ function createAcpExecutor(options) {
|
|
|
25368
25651
|
const userInputOutcome = await handlePermissionAsUserInput(params);
|
|
25369
25652
|
if (userInputOutcome !== void 0) return userInputOutcome;
|
|
25370
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
|
+
}
|
|
25371
25660
|
const requestId = randomId();
|
|
25372
|
-
const outcome = await new Promise((
|
|
25661
|
+
const outcome = await new Promise((resolve2) => {
|
|
25373
25662
|
const pending = {
|
|
25374
25663
|
requestId,
|
|
25375
25664
|
request: params,
|
|
25376
25665
|
permissionRequest,
|
|
25377
25666
|
turnId: activeTurnId,
|
|
25378
25667
|
settled: false,
|
|
25379
|
-
resolve
|
|
25668
|
+
resolve: resolve2
|
|
25380
25669
|
};
|
|
25381
25670
|
approvals.add(pending);
|
|
25382
25671
|
emit(
|
|
@@ -25455,36 +25744,70 @@ function createAcpExecutor(options) {
|
|
|
25455
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) {
|
|
25456
25745
|
return input2.state;
|
|
25457
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
|
+
}
|
|
25458
25762
|
if (input2.state.strategy === "config-option") {
|
|
25459
25763
|
await connection.request("session/set_config_option", {
|
|
25460
25764
|
sessionId: input2.acpSessionId,
|
|
25461
25765
|
configId: input2.state.modelConfigId,
|
|
25462
|
-
value
|
|
25766
|
+
value
|
|
25463
25767
|
});
|
|
25464
25768
|
} else {
|
|
25465
25769
|
await connection.request("session/set_model", {
|
|
25466
25770
|
sessionId: input2.acpSessionId,
|
|
25467
|
-
modelId:
|
|
25771
|
+
modelId: value
|
|
25468
25772
|
});
|
|
25469
25773
|
}
|
|
25470
25774
|
if (!isLive(input2.expectedGeneration)) {
|
|
25471
25775
|
throw new AcpExecutorDisposedError();
|
|
25472
25776
|
}
|
|
25473
|
-
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
|
+
}
|
|
25474
25808
|
};
|
|
25475
25809
|
const startOnce = async (expectedGeneration) => {
|
|
25476
|
-
const initializePayload =
|
|
25477
|
-
protocolVersion: PROTOCOL_VERSION,
|
|
25478
|
-
clientCapabilities: {
|
|
25479
|
-
fs: {
|
|
25480
|
-
readTextFile: false,
|
|
25481
|
-
writeTextFile: false,
|
|
25482
|
-
...options.clientCapabilities?.fs
|
|
25483
|
-
},
|
|
25484
|
-
terminal: options.clientCapabilities?.terminal ?? false
|
|
25485
|
-
},
|
|
25486
|
-
clientInfo: options.clientInfo
|
|
25487
|
-
};
|
|
25810
|
+
const initializePayload = acpInitializePayload(options.clientInfo, options.clientCapabilities);
|
|
25488
25811
|
const initialized = await connection.request(
|
|
25489
25812
|
"initialize",
|
|
25490
25813
|
initializePayload
|
|
@@ -25528,12 +25851,27 @@ function createAcpExecutor(options) {
|
|
|
25528
25851
|
nextSessionId = created.sessionId;
|
|
25529
25852
|
setup = created;
|
|
25530
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
|
+
}
|
|
25531
25863
|
const nextModelSelectionState = await applyRequestedModel({
|
|
25532
25864
|
state: modelSelectionStateFromSetup(setup),
|
|
25533
25865
|
acpSessionId: nextSessionId,
|
|
25534
25866
|
requestedModelId: options.requestedModelId,
|
|
25535
25867
|
expectedGeneration
|
|
25536
25868
|
});
|
|
25869
|
+
await applyRequestedEffort({
|
|
25870
|
+
setup,
|
|
25871
|
+
acpSessionId: nextSessionId,
|
|
25872
|
+
requestedEffort: options.requestedEffort,
|
|
25873
|
+
expectedGeneration
|
|
25874
|
+
});
|
|
25537
25875
|
if (options.beforeApplyResult) {
|
|
25538
25876
|
await options.beforeApplyResult();
|
|
25539
25877
|
}
|
|
@@ -25610,14 +25948,14 @@ function createAcpExecutor(options) {
|
|
|
25610
25948
|
if (!promptMutex.isLocked) {
|
|
25611
25949
|
return true;
|
|
25612
25950
|
}
|
|
25613
|
-
return await new Promise((
|
|
25951
|
+
return await new Promise((resolve2) => {
|
|
25614
25952
|
const timer = setTimeout(() => {
|
|
25615
25953
|
promptWaiters = promptWaiters.filter((waiter) => waiter !== onSettle);
|
|
25616
|
-
|
|
25954
|
+
resolve2(false);
|
|
25617
25955
|
}, timeoutMs);
|
|
25618
25956
|
const onSettle = () => {
|
|
25619
25957
|
clearTimeout(timer);
|
|
25620
|
-
|
|
25958
|
+
resolve2(true);
|
|
25621
25959
|
};
|
|
25622
25960
|
promptWaiters.push(onSettle);
|
|
25623
25961
|
});
|
|
@@ -25898,6 +26236,26 @@ function createAcpExecutor(options) {
|
|
|
25898
26236
|
};
|
|
25899
26237
|
apply();
|
|
25900
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
|
+
};
|
|
25901
26259
|
const respondToUserInput = async (requestId, answers) => {
|
|
25902
26260
|
failIfDisposed();
|
|
25903
26261
|
const pending = pendingUserInputs.get(requestId);
|
|
@@ -25914,7 +26272,7 @@ function createAcpExecutor(options) {
|
|
|
25914
26272
|
pendingUserInputs.delete(requestId);
|
|
25915
26273
|
pending.resolve({
|
|
25916
26274
|
kind: "answered",
|
|
25917
|
-
answers:
|
|
26275
|
+
answers: isRecord5(answers) ? answers : {}
|
|
25918
26276
|
});
|
|
25919
26277
|
};
|
|
25920
26278
|
const dispose = async () => {
|
|
@@ -25964,10 +26322,53 @@ function createAcpExecutor(options) {
|
|
|
25964
26322
|
cancel,
|
|
25965
26323
|
respondToRequest,
|
|
25966
26324
|
respondToUserInput,
|
|
26325
|
+
setNativeMode,
|
|
25967
26326
|
dispose
|
|
25968
26327
|
};
|
|
25969
26328
|
}
|
|
25970
26329
|
|
|
26330
|
+
// ../../packages/ycodium-acp-executor/src/probe.ts
|
|
26331
|
+
function withProbeTimeout(promise2, timeoutMs, method) {
|
|
26332
|
+
return new Promise((resolve2, 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
|
+
resolve2(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
|
+
|
|
25971
26372
|
// src/card.ts
|
|
25972
26373
|
var ACP_DRIVER_KIND = "acp";
|
|
25973
26374
|
var ACP_CARD = {
|
|
@@ -25999,7 +26400,21 @@ var ACP_CARD = {
|
|
|
25999
26400
|
everydayHarness: false,
|
|
26000
26401
|
capabilities: {
|
|
26001
26402
|
sessionModelSwitch: "unsupported",
|
|
26002
|
-
|
|
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
|
|
26003
26418
|
},
|
|
26004
26419
|
credentialSources: [],
|
|
26005
26420
|
diagnostics: [],
|
|
@@ -26022,12 +26437,20 @@ var ACP_CARD = {
|
|
|
26022
26437
|
},
|
|
26023
26438
|
modelConnectivity: {
|
|
26024
26439
|
dialects: ["anthropic-messages", "openai-responses", "openai-chat-completions"],
|
|
26025
|
-
|
|
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
|
|
26026
26449
|
}
|
|
26027
26450
|
};
|
|
26028
26451
|
|
|
26029
26452
|
// src/config.ts
|
|
26030
|
-
function
|
|
26453
|
+
function isRecord6(value) {
|
|
26031
26454
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
26032
26455
|
}
|
|
26033
26456
|
function readString(value) {
|
|
@@ -26040,7 +26463,7 @@ function readEnvironment(value) {
|
|
|
26040
26463
|
if (!Array.isArray(value)) return {};
|
|
26041
26464
|
const env = {};
|
|
26042
26465
|
for (const entry of value) {
|
|
26043
|
-
if (!
|
|
26466
|
+
if (!isRecord6(entry)) continue;
|
|
26044
26467
|
const name = readTrimmed(entry["name"]);
|
|
26045
26468
|
if (name.length === 0) continue;
|
|
26046
26469
|
env[name] = readString(entry["value"]);
|
|
@@ -26048,17 +26471,18 @@ function readEnvironment(value) {
|
|
|
26048
26471
|
return env;
|
|
26049
26472
|
}
|
|
26050
26473
|
function parseAcpInstanceConfig(config2) {
|
|
26051
|
-
if (!
|
|
26474
|
+
if (!isRecord6(config2)) {
|
|
26052
26475
|
return {
|
|
26053
26476
|
binaryPath: "",
|
|
26054
26477
|
launchArgs: "",
|
|
26055
26478
|
homePath: "",
|
|
26056
26479
|
homeEnvVar: "",
|
|
26057
26480
|
environment: {},
|
|
26058
|
-
envelopeDriver: void 0
|
|
26481
|
+
envelopeDriver: void 0,
|
|
26482
|
+
providerId: ""
|
|
26059
26483
|
};
|
|
26060
26484
|
}
|
|
26061
|
-
const nested =
|
|
26485
|
+
const nested = isRecord6(config2["config"]) ? config2["config"] : void 0;
|
|
26062
26486
|
const settings = nested ?? config2;
|
|
26063
26487
|
return {
|
|
26064
26488
|
binaryPath: readTrimmed(settings["binaryPath"]),
|
|
@@ -26066,7 +26490,8 @@ function parseAcpInstanceConfig(config2) {
|
|
|
26066
26490
|
homePath: readTrimmed(settings["homePath"]) || readTrimmed(config2["homePath"]),
|
|
26067
26491
|
homeEnvVar: readTrimmed(settings["homeEnvVar"]) || readTrimmed(config2["homeEnvVar"]),
|
|
26068
26492
|
environment: readEnvironment(config2["environment"]),
|
|
26069
|
-
envelopeDriver: readTrimmed(config2["driver"]) || void 0
|
|
26493
|
+
envelopeDriver: readTrimmed(config2["driver"]) || void 0,
|
|
26494
|
+
providerId: readTrimmed(settings["providerId"]) || readTrimmed(config2["providerId"])
|
|
26070
26495
|
};
|
|
26071
26496
|
}
|
|
26072
26497
|
function tokenizeLaunchArgs(value) {
|
|
@@ -26121,59 +26546,374 @@ function tokenizeLaunchArgs(value) {
|
|
|
26121
26546
|
return tokens;
|
|
26122
26547
|
}
|
|
26123
26548
|
|
|
26124
|
-
// src/
|
|
26125
|
-
import * as
|
|
26126
|
-
|
|
26127
|
-
|
|
26128
|
-
|
|
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;
|
|
26129
26604
|
try {
|
|
26130
|
-
|
|
26131
|
-
|
|
26132
|
-
|
|
26133
|
-
await new Promise((resolve) => {
|
|
26134
|
-
const onDone = () => {
|
|
26135
|
-
readable.off("drain", onDone);
|
|
26136
|
-
readable.off("close", onDone);
|
|
26137
|
-
resolve();
|
|
26138
|
-
};
|
|
26139
|
-
readable.once("drain", onDone);
|
|
26140
|
-
readable.once("close", onDone);
|
|
26141
|
-
});
|
|
26142
|
-
}
|
|
26143
|
-
}
|
|
26144
|
-
} catch (error62) {
|
|
26145
|
-
readable.destroy(error62 instanceof Error ? error62 : new Error(String(error62)));
|
|
26146
|
-
return;
|
|
26605
|
+
record2 = JSON.parse(trimmed);
|
|
26606
|
+
} catch {
|
|
26607
|
+
continue;
|
|
26147
26608
|
}
|
|
26148
|
-
if (
|
|
26149
|
-
|
|
26150
|
-
|
|
26151
|
-
|
|
26152
|
-
|
|
26153
|
-
|
|
26154
|
-
|
|
26155
|
-
|
|
26156
|
-
const bytes = typeof chunk === "string" ? Buffer.from(chunk) : chunk;
|
|
26157
|
-
handle.writeStdin(bytes).then(
|
|
26158
|
-
() => callback(),
|
|
26159
|
-
(error62) => callback(error62 instanceof Error ? error62 : new Error(String(error62)))
|
|
26160
|
-
);
|
|
26161
|
-
},
|
|
26162
|
-
final(callback) {
|
|
26163
|
-
handle.closeStdin().then(
|
|
26164
|
-
() => callback(),
|
|
26165
|
-
(error62) => callback(error62 instanceof Error ? error62 : new Error(String(error62)))
|
|
26166
|
-
);
|
|
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;
|
|
26614
|
+
}
|
|
26615
|
+
if (type === "llm.request") {
|
|
26616
|
+
maxTokens = positiveInt(record2["maxTokens"]) ?? maxTokens;
|
|
26167
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((resolve2, 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
|
+
resolve2(value);
|
|
26718
|
+
},
|
|
26719
|
+
(error62) => {
|
|
26720
|
+
clearTimeout(timer);
|
|
26721
|
+
reject(error62 instanceof Error ? error62 : new Error(String(error62)));
|
|
26722
|
+
}
|
|
26723
|
+
);
|
|
26168
26724
|
});
|
|
26169
|
-
|
|
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;
|
|
26170
26910
|
}
|
|
26171
26911
|
|
|
26172
26912
|
// src/acpInstance.ts
|
|
26173
26913
|
var RESUME_SCHEMA_VERSION = 1;
|
|
26174
|
-
var
|
|
26175
|
-
var yieldTick = () => new Promise((
|
|
26176
|
-
setImmediate(
|
|
26914
|
+
var CLIENT_INFO2 = { name: "ycodium-agent-acp", version: "0.2.0" };
|
|
26915
|
+
var yieldTick = () => new Promise((resolve2) => {
|
|
26916
|
+
setImmediate(resolve2);
|
|
26177
26917
|
});
|
|
26178
26918
|
var EventBus = class {
|
|
26179
26919
|
#queued = [];
|
|
@@ -26200,27 +26940,20 @@ var EventBus = class {
|
|
|
26200
26940
|
const queued = this.#queued.shift();
|
|
26201
26941
|
if (queued !== void 0) return Promise.resolve({ done: false, value: queued });
|
|
26202
26942
|
if (this.#closed) return Promise.resolve({ done: true, value: void 0 });
|
|
26203
|
-
return new Promise((
|
|
26204
|
-
this.#waiters.push(
|
|
26943
|
+
return new Promise((resolve2) => {
|
|
26944
|
+
this.#waiters.push(resolve2);
|
|
26205
26945
|
});
|
|
26206
26946
|
}
|
|
26207
26947
|
};
|
|
26208
26948
|
}
|
|
26209
26949
|
};
|
|
26210
26950
|
function expandHomePath(value) {
|
|
26211
|
-
if (value === "~") return
|
|
26951
|
+
if (value === "~") return NodeOS2.homedir();
|
|
26212
26952
|
if (value.startsWith("~/") || value.startsWith("~\\")) {
|
|
26213
|
-
return
|
|
26953
|
+
return NodePath3.join(NodeOS2.homedir(), value.slice(2));
|
|
26214
26954
|
}
|
|
26215
26955
|
return value;
|
|
26216
26956
|
}
|
|
26217
|
-
function compactEnv(env) {
|
|
26218
|
-
const out = {};
|
|
26219
|
-
for (const [key, value] of Object.entries(env)) {
|
|
26220
|
-
if (value !== void 0) out[key] = value;
|
|
26221
|
-
}
|
|
26222
|
-
return out;
|
|
26223
|
-
}
|
|
26224
26957
|
function modelIdFromSelection(selection) {
|
|
26225
26958
|
if (typeof selection !== "object" || selection === null || Array.isArray(selection)) {
|
|
26226
26959
|
return void 0;
|
|
@@ -26228,19 +26961,47 @@ function modelIdFromSelection(selection) {
|
|
|
26228
26961
|
const model = selection["model"];
|
|
26229
26962
|
return typeof model === "string" ? model.trim() || void 0 : void 0;
|
|
26230
26963
|
}
|
|
26231
|
-
function buildSpawnEnv(recipe,
|
|
26232
|
-
const homePath =
|
|
26964
|
+
function buildSpawnEnv(recipe, configuredHomePath, launchEnv = {}) {
|
|
26965
|
+
const homePath = configuredHomePath.trim();
|
|
26233
26966
|
const homeEnvVar = recipe.homeEnvVar.trim();
|
|
26234
26967
|
const homeInjection = homePath.length > 0 && homeEnvVar.length > 0 ? { [homeEnvVar]: expandHomePath(homePath) } : {};
|
|
26235
|
-
return {
|
|
26236
|
-
|
|
26237
|
-
|
|
26238
|
-
|
|
26239
|
-
|
|
26968
|
+
return mergeLaunchEnv({
|
|
26969
|
+
brainSlots: brainSlotEnvKeys(ACP_CARD),
|
|
26970
|
+
accountHome: homeInjection,
|
|
26971
|
+
instanceTable: recipe.environment,
|
|
26972
|
+
launch: launchEnv
|
|
26973
|
+
});
|
|
26240
26974
|
}
|
|
26241
26975
|
function nowIso() {
|
|
26242
26976
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
26243
26977
|
}
|
|
26978
|
+
function acpContinuationKey(recipe) {
|
|
26979
|
+
const homePath = recipe.homePath.trim();
|
|
26980
|
+
const resolvedHomePath = homePath.length > 0 ? NodePath3.resolve(expandHomePath(homePath)) : "";
|
|
26981
|
+
return `acp:home:${recipe.binaryPath.trim()}\0${resolvedHomePath}`;
|
|
26982
|
+
}
|
|
26983
|
+
function resolveHomePath(recipe, launchHomePath) {
|
|
26984
|
+
const envVar = recipe.homeEnvVar.trim();
|
|
26985
|
+
const fromEnvironment = envVar.length > 0 ? recipe.environment[envVar]?.trim() : void 0;
|
|
26986
|
+
if (fromEnvironment !== void 0 && fromEnvironment.length > 0) {
|
|
26987
|
+
return expandHomePath(fromEnvironment);
|
|
26988
|
+
}
|
|
26989
|
+
const configured = (launchHomePath?.trim() || recipe.homePath).trim();
|
|
26990
|
+
return configured.length > 0 ? expandHomePath(configured) : "";
|
|
26991
|
+
}
|
|
26992
|
+
function isKimiInstance(recipe) {
|
|
26993
|
+
return recipe.providerId === KIMI_PROVIDER_ID || recipe.homeEnvVar.trim() === KIMI_HOME_ENV_VAR;
|
|
26994
|
+
}
|
|
26995
|
+
function vendorHooksFor(recipe, launchHomePath) {
|
|
26996
|
+
if (!isKimiInstance(recipe)) return void 0;
|
|
26997
|
+
const homePath = resolveHomePath(recipe, launchHomePath);
|
|
26998
|
+
return {
|
|
26999
|
+
permissionUserInput: kimiPermissionUserInput,
|
|
27000
|
+
// ACP 的 `usage_update` 我们这边不消费,进度圈只能从各家自己写在磁盘上的
|
|
27001
|
+
// 记录里读。目前只有 Kimi 的格式核实过。
|
|
27002
|
+
readContextWindow: ({ sessionId }) => readKimiContextWindow({ homePath, acpSessionId: sessionId })
|
|
27003
|
+
};
|
|
27004
|
+
}
|
|
26244
27005
|
async function stopBooked(booked) {
|
|
26245
27006
|
await booked.executor.dispose().catch(() => void 0);
|
|
26246
27007
|
await booked.handle.kill("SIGTERM").catch(() => void 0);
|
|
@@ -26267,11 +27028,33 @@ var createAcpInstance = (config2, host) => {
|
|
|
26267
27028
|
detail: "ACP program path is not configured"
|
|
26268
27029
|
};
|
|
26269
27030
|
}
|
|
27031
|
+
const outcome = isKimiInstance(recipe) ? {
|
|
27032
|
+
kind: "answered",
|
|
27033
|
+
models: await probeKimiModels({
|
|
27034
|
+
recipe,
|
|
27035
|
+
host,
|
|
27036
|
+
env: buildSpawnEnv(recipe, recipe.homePath)
|
|
27037
|
+
}),
|
|
27038
|
+
nativeModes: void 0,
|
|
27039
|
+
currentNativeModeId: void 0
|
|
27040
|
+
} : await probeAcpModelsViaSession({
|
|
27041
|
+
recipe,
|
|
27042
|
+
host,
|
|
27043
|
+
env: buildSpawnEnv(recipe, recipe.homePath)
|
|
27044
|
+
});
|
|
27045
|
+
const models = outcome.kind === "answered" ? outcome.models : void 0;
|
|
27046
|
+
const nativeModes = outcome.kind === "answered" && outcome.nativeModes !== void 0 ? outcome.nativeModes : void 0;
|
|
27047
|
+
const currentNativeModeId = outcome.kind === "answered" ? outcome.currentNativeModeId : void 0;
|
|
26270
27048
|
return {
|
|
26271
27049
|
installed: true,
|
|
26272
27050
|
version: null,
|
|
26273
27051
|
readiness: "unchecked",
|
|
26274
|
-
auth: { status: "unknown" }
|
|
27052
|
+
auth: { status: "unknown" },
|
|
27053
|
+
...models === void 0 ? {} : { models },
|
|
27054
|
+
...nativeModes === void 0 || nativeModes.length === 0 ? {} : {
|
|
27055
|
+
nativeModes,
|
|
27056
|
+
...currentNativeModeId === void 0 ? {} : { currentNativeModeId }
|
|
27057
|
+
}
|
|
26275
27058
|
};
|
|
26276
27059
|
},
|
|
26277
27060
|
startSession: async (input2, signal) => {
|
|
@@ -26287,7 +27070,11 @@ var createAcpInstance = (config2, host) => {
|
|
|
26287
27070
|
await stopBooked(existing);
|
|
26288
27071
|
threads.delete(input2.threadId);
|
|
26289
27072
|
}
|
|
26290
|
-
const env = buildSpawnEnv(
|
|
27073
|
+
const env = buildSpawnEnv(
|
|
27074
|
+
recipe,
|
|
27075
|
+
input2.launch.homePath?.trim() || recipe.homePath,
|
|
27076
|
+
input2.launch.processEnv
|
|
27077
|
+
);
|
|
26291
27078
|
const handle = await host.process.spawn({
|
|
26292
27079
|
program: recipe.binaryPath,
|
|
26293
27080
|
args: tokenizeLaunchArgs(recipe.launchArgs),
|
|
@@ -26295,19 +27082,32 @@ var createAcpInstance = (config2, host) => {
|
|
|
26295
27082
|
owner: { bindingId: host.bindingId },
|
|
26296
27083
|
...Object.keys(env).length > 0 ? { env } : {}
|
|
26297
27084
|
});
|
|
26298
|
-
const { readable, writable } =
|
|
27085
|
+
const { readable, writable } = nodeStreamsFromHandle2(handle);
|
|
26299
27086
|
const resume = input2.discardResumeCursor === true ? void 0 : parseAcpResumeCursor(input2.resumeCursor, RESUME_SCHEMA_VERSION);
|
|
26300
27087
|
const requestedModelId = input2.launch.requestedModelId?.trim() || modelIdFromSelection(input2.modelSelection);
|
|
27088
|
+
const requestedEffort = modelSelectionStringOption(
|
|
27089
|
+
input2.modelSelection,
|
|
27090
|
+
ACP_EFFORT_OPTION_ID
|
|
27091
|
+
);
|
|
27092
|
+
const hooks = vendorHooksFor(recipe, input2.launch.homePath);
|
|
27093
|
+
const autoApprove = input2.nativeModeId === void 0 ? (request) => acpRuntimeModeAutoApproves(
|
|
27094
|
+
threads.get(input2.threadId)?.session.runtimeMode ?? input2.runtimeMode,
|
|
27095
|
+
request.kind
|
|
27096
|
+
) : void 0;
|
|
26301
27097
|
const executor = createAcpExecutor({
|
|
26302
27098
|
readable,
|
|
26303
27099
|
writable,
|
|
27100
|
+
...requestedEffort === void 0 ? {} : { requestedEffort },
|
|
26304
27101
|
cwd,
|
|
26305
27102
|
threadId: input2.threadId,
|
|
26306
27103
|
provider: ACP_DRIVER_KIND,
|
|
26307
27104
|
providerInstanceId: host.instanceId,
|
|
26308
|
-
clientInfo:
|
|
27105
|
+
clientInfo: CLIENT_INFO2,
|
|
27106
|
+
log: host.log,
|
|
26309
27107
|
...requestedModelId === void 0 ? {} : { requestedModelId },
|
|
26310
27108
|
...input2.bindingGeneration === void 0 ? {} : { originBindingGeneration: input2.bindingGeneration },
|
|
27109
|
+
...hooks === void 0 ? {} : { hooks },
|
|
27110
|
+
...autoApprove === void 0 ? {} : { autoApprove },
|
|
26311
27111
|
...resume === void 0 ? {} : { resumeSessionId: resume.sessionId }
|
|
26312
27112
|
});
|
|
26313
27113
|
const forwarding = (async () => {
|
|
@@ -26317,6 +27117,13 @@ var createAcpInstance = (config2, host) => {
|
|
|
26317
27117
|
})();
|
|
26318
27118
|
try {
|
|
26319
27119
|
const started = await executor.start();
|
|
27120
|
+
const modeToApply = acpSessionModeToApply(
|
|
27121
|
+
input2.nativeModeId,
|
|
27122
|
+
acpCurrentModeId(started.sessionSetupResult)
|
|
27123
|
+
);
|
|
27124
|
+
if (modeToApply !== void 0) {
|
|
27125
|
+
await executor.setNativeMode(modeToApply);
|
|
27126
|
+
}
|
|
26320
27127
|
await yieldTick();
|
|
26321
27128
|
const createdAt = nowIso();
|
|
26322
27129
|
const resumeCursor = {
|
|
@@ -26328,6 +27135,7 @@ var createAcpInstance = (config2, host) => {
|
|
|
26328
27135
|
providerInstanceId: host.instanceId,
|
|
26329
27136
|
status: "idle",
|
|
26330
27137
|
runtimeMode: input2.runtimeMode,
|
|
27138
|
+
...input2.nativeModeId === void 0 ? {} : { nativeModeId: input2.nativeModeId },
|
|
26331
27139
|
cwd,
|
|
26332
27140
|
threadId: input2.threadId,
|
|
26333
27141
|
createdAt,
|
|
@@ -26407,8 +27215,25 @@ var createAcpInstance = (config2, host) => {
|
|
|
26407
27215
|
reason
|
|
26408
27216
|
);
|
|
26409
27217
|
},
|
|
26410
|
-
respondToUserInput: async () => {
|
|
26411
|
-
|
|
27218
|
+
respondToUserInput: async (threadId, requestId, answers) => {
|
|
27219
|
+
const booked = requireThread(threadId);
|
|
27220
|
+
await booked.executor.respondToUserInput(requestId, answers);
|
|
27221
|
+
},
|
|
27222
|
+
/**
|
|
27223
|
+
* 切这只手自报的审批档。`nativeModeId` 是它自己报的 id(探测时原样上报的那些),
|
|
27224
|
+
* 一路不翻译成宿主四档。
|
|
27225
|
+
*
|
|
27226
|
+
* 这是 `setRuntimeMode` 做不到的那一半:那个只改插件自己的会话记录,CLI 收不到
|
|
27227
|
+
* (`runtime-mode-never-reaches-cli` 记的那族缺陷)。
|
|
27228
|
+
*/
|
|
27229
|
+
setNativeMode: async (threadId, nativeModeId) => {
|
|
27230
|
+
const booked = requireThread(threadId);
|
|
27231
|
+
await booked.executor.setNativeMode(nativeModeId);
|
|
27232
|
+
booked.session = {
|
|
27233
|
+
...booked.session,
|
|
27234
|
+
nativeModeId,
|
|
27235
|
+
updatedAt: nowIso()
|
|
27236
|
+
};
|
|
26412
27237
|
},
|
|
26413
27238
|
setRuntimeMode: async (threadId, runtimeMode) => {
|
|
26414
27239
|
const booked = requireThread(threadId);
|
|
@@ -26429,6 +27254,7 @@ var createAcpInstance = (config2, host) => {
|
|
|
26429
27254
|
return { threadId, turns: booked.turns };
|
|
26430
27255
|
},
|
|
26431
27256
|
events,
|
|
27257
|
+
continuationKey: acpContinuationKey(recipe),
|
|
26432
27258
|
dispose: async () => {
|
|
26433
27259
|
if (disposed) return;
|
|
26434
27260
|
disposed = true;
|