@ycodium-ai/agent-dsh 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/dist/index.js +586 -120
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -9,6 +9,7 @@ import { definePlugin } from "@ycodium-ai/plugin-api";
|
|
|
9
9
|
|
|
10
10
|
// src/card.ts
|
|
11
11
|
var DSH_DRIVER_KIND = "dsh";
|
|
12
|
+
var DSH_CLIENT_INFO = { name: "ycodium-agent-dsh", version: "0.1.0" };
|
|
12
13
|
var DSH_CARD = {
|
|
13
14
|
driverKind: DSH_DRIVER_KIND,
|
|
14
15
|
displayName: "DeepSeek Harness",
|
|
@@ -30,6 +31,12 @@ var DSH_CARD = {
|
|
|
30
31
|
setupNote: "",
|
|
31
32
|
// 模型 id 是 agent 自家目录的 `provider/model` 对:只 trim,不做别名展开,
|
|
32
33
|
// 也没有默认值——空选择 = 保持 agent 起来时那个。
|
|
34
|
+
//
|
|
35
|
+
// **清单不靠这一栏兜底**:`probeDshModels` 每次探测都问 CLI 要那份目录
|
|
36
|
+
// (会话握手的 `configOptions`),报上来的是四个带名字的型号。这里留空是有意的:
|
|
37
|
+
// 编一个型号当兜底,等 CLI 换了目录就变成了发出去被拒的坏 id(grok 的名片上
|
|
38
|
+
// 有一条这样的兜底,代价写在它自己的注释里)。探测问不到时这双手就是没有清单,
|
|
39
|
+
// 界面按「机器没报模型」处理,比摆一个可能不对的型号诚实。
|
|
33
40
|
defaultModel: "",
|
|
34
41
|
defaultTextGenerationModel: "",
|
|
35
42
|
customModelIdExample: "",
|
|
@@ -47,6 +54,17 @@ var DSH_CARD = {
|
|
|
47
54
|
// DSH 换模型只有 `session/set_config_option` 一条路;库会按握手形状自动协商,
|
|
48
55
|
// 但插件侧的执行路径还没接到模型管理 UI,名片必须说实话。
|
|
49
56
|
sessionModelSwitch: "unsupported",
|
|
57
|
+
// **这一位今天是空头支票,别当它验过**(2026-09-19 拿真 `dsh --profile acp`
|
|
58
|
+
// 起一轮 ACP 会话实测)。两件事都不成立:
|
|
59
|
+
//
|
|
60
|
+
// 1. 本插件的 `setRuntimeMode` 只改自己的会话记录,一个字节都不发给 CLI——用户
|
|
61
|
+
// 中途切档,什么也没发生(与 Grok 那次 F8 同款缺陷)。
|
|
62
|
+
// 2. CLI 那头根本没有这条路:`session/new` 不报 `modes`,`session/set_mode` 回
|
|
63
|
+
// `-32601 "Method not found"`。所以照 kimi 那样接 `session/set_mode` 也没用,
|
|
64
|
+
// 得先查 dsh 自己有没有启动旗标(Grok 那条是 `--permission-mode`)。
|
|
65
|
+
//
|
|
66
|
+
// 改这一位要连着 `packages/contracts` 那张表一起改(`executorRegistry.test.ts`
|
|
67
|
+
// 的防漂移闸把两边钉成同值)。
|
|
50
68
|
runtimeModeSwitch: "in-session"
|
|
51
69
|
},
|
|
52
70
|
// 凭据由 CLI 自己保管在它 home 里,宿主不读 token。
|
|
@@ -85,8 +103,7 @@ var DSH_CARD = {
|
|
|
85
103
|
};
|
|
86
104
|
|
|
87
105
|
// src/dshInstance.ts
|
|
88
|
-
import
|
|
89
|
-
import * as NodePath from "node:path";
|
|
106
|
+
import { nodeStreamsFromHandle as nodeStreamsFromHandle2 } from "@ycodium-ai/plugin-api/process-stdin";
|
|
90
107
|
|
|
91
108
|
// ../../packages/ycodium-acp-executor/src/executor.ts
|
|
92
109
|
import * as NodeCrypto from "node:crypto";
|
|
@@ -23956,6 +23973,12 @@ function acpApprovalOptions(request) {
|
|
|
23956
23973
|
}
|
|
23957
23974
|
return options;
|
|
23958
23975
|
}
|
|
23976
|
+
function selectAutoApprovedAcpPermissionOption(request) {
|
|
23977
|
+
if (new Set(request.options.map((option) => option.kind)).size !== request.options.length) {
|
|
23978
|
+
return void 0;
|
|
23979
|
+
}
|
|
23980
|
+
return selectAcpPermissionOptionId(request, "acceptForSession") ?? selectAcpPermissionOptionId(request, "accept");
|
|
23981
|
+
}
|
|
23959
23982
|
function parseAcpResumeCursor(raw, schemaVersion) {
|
|
23960
23983
|
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) return void 0;
|
|
23961
23984
|
const record2 = raw;
|
|
@@ -24349,6 +24372,158 @@ function makeAcpContentDeltaEvent(input2) {
|
|
|
24349
24372
|
};
|
|
24350
24373
|
}
|
|
24351
24374
|
|
|
24375
|
+
// ../../packages/ycodium-acp-executor/src/config-options.ts
|
|
24376
|
+
function isRecord3(value) {
|
|
24377
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
24378
|
+
}
|
|
24379
|
+
function asString(value) {
|
|
24380
|
+
return typeof value === "string" ? value : void 0;
|
|
24381
|
+
}
|
|
24382
|
+
function normalizedField(option, key) {
|
|
24383
|
+
return asString(option[key])?.trim().toLowerCase() ?? "";
|
|
24384
|
+
}
|
|
24385
|
+
function looksLikeEffortOption(option) {
|
|
24386
|
+
const id = normalizedField(option, "id");
|
|
24387
|
+
const name = normalizedField(option, "name");
|
|
24388
|
+
return id === "effort" || id === "reasoning" || name === "effort" || name === "reasoning" || name.includes("effort") || name.includes("reasoning") || normalizedField(option, "category") === "thought_level";
|
|
24389
|
+
}
|
|
24390
|
+
function flattenChoices(rawOptions, currentValue) {
|
|
24391
|
+
if (!Array.isArray(rawOptions)) return [];
|
|
24392
|
+
const out = [];
|
|
24393
|
+
const seen = /* @__PURE__ */ new Set();
|
|
24394
|
+
const push = (entry) => {
|
|
24395
|
+
if (!isRecord3(entry)) return;
|
|
24396
|
+
const value = asString(entry["value"]);
|
|
24397
|
+
if (value === void 0 || seen.has(value)) return;
|
|
24398
|
+
seen.add(value);
|
|
24399
|
+
const description = asString(entry["description"])?.trim();
|
|
24400
|
+
const name = asString(entry["name"])?.trim();
|
|
24401
|
+
out.push({
|
|
24402
|
+
value,
|
|
24403
|
+
...name ? { name } : {},
|
|
24404
|
+
label: name || value,
|
|
24405
|
+
...description ? { description } : {},
|
|
24406
|
+
...currentValue !== void 0 && currentValue === value ? { isDefault: true } : {}
|
|
24407
|
+
});
|
|
24408
|
+
};
|
|
24409
|
+
for (const entry of rawOptions) {
|
|
24410
|
+
if (isRecord3(entry) && Array.isArray(entry["options"])) {
|
|
24411
|
+
for (const nested of entry["options"]) push(nested);
|
|
24412
|
+
continue;
|
|
24413
|
+
}
|
|
24414
|
+
push(entry);
|
|
24415
|
+
}
|
|
24416
|
+
return out;
|
|
24417
|
+
}
|
|
24418
|
+
function findAcpEffortConfig(configOptions) {
|
|
24419
|
+
if (!Array.isArray(configOptions)) return void 0;
|
|
24420
|
+
const candidates = configOptions.filter(isRecord3).filter((option) => normalizedField(option, "type") === "select").filter(looksLikeEffortOption);
|
|
24421
|
+
if (candidates.length === 0) return void 0;
|
|
24422
|
+
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];
|
|
24423
|
+
if (picked === void 0) return void 0;
|
|
24424
|
+
const configId = asString(picked["id"])?.trim();
|
|
24425
|
+
if (!configId) return void 0;
|
|
24426
|
+
const choices = flattenChoices(picked["options"], asString(picked["currentValue"])?.trim());
|
|
24427
|
+
if (choices.length === 0) return void 0;
|
|
24428
|
+
return {
|
|
24429
|
+
configId,
|
|
24430
|
+
label: asString(picked["name"])?.trim() ?? "",
|
|
24431
|
+
choices
|
|
24432
|
+
};
|
|
24433
|
+
}
|
|
24434
|
+
function acpModelChoicesFromConfigOptions(configOptions) {
|
|
24435
|
+
if (!Array.isArray(configOptions)) return void 0;
|
|
24436
|
+
const records = configOptions.filter(isRecord3);
|
|
24437
|
+
const modelOption = records.find((option) => normalizedField(option, "category") === "model");
|
|
24438
|
+
if (modelOption === void 0) return void 0;
|
|
24439
|
+
if (!asString(modelOption["id"])?.trim()) return void 0;
|
|
24440
|
+
return flattenChoices(modelOption["options"], asString(modelOption["currentValue"])?.trim());
|
|
24441
|
+
}
|
|
24442
|
+
|
|
24443
|
+
// ../../packages/ycodium-acp-executor/src/runtime-mode.ts
|
|
24444
|
+
function isRecord4(value) {
|
|
24445
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
24446
|
+
}
|
|
24447
|
+
function asString2(value) {
|
|
24448
|
+
return typeof value === "string" ? value : void 0;
|
|
24449
|
+
}
|
|
24450
|
+
function acpModeConfigId(setup) {
|
|
24451
|
+
if (!isRecord4(setup)) return void 0;
|
|
24452
|
+
const configOptions = setup["configOptions"];
|
|
24453
|
+
if (!Array.isArray(configOptions)) return void 0;
|
|
24454
|
+
for (const option of configOptions) {
|
|
24455
|
+
if (!isRecord4(option)) continue;
|
|
24456
|
+
if (asString2(option["category"])?.trim().toLowerCase() !== "mode") continue;
|
|
24457
|
+
const id = asString2(option["id"])?.trim();
|
|
24458
|
+
if (id) return id;
|
|
24459
|
+
}
|
|
24460
|
+
return void 0;
|
|
24461
|
+
}
|
|
24462
|
+
|
|
24463
|
+
// ../../packages/ycodium-acp-executor/src/handshake.ts
|
|
24464
|
+
function acpInitializePayload(clientInfo, overrides) {
|
|
24465
|
+
return {
|
|
24466
|
+
protocolVersion: PROTOCOL_VERSION,
|
|
24467
|
+
clientCapabilities: {
|
|
24468
|
+
fs: {
|
|
24469
|
+
readTextFile: false,
|
|
24470
|
+
writeTextFile: false,
|
|
24471
|
+
...overrides?.fs
|
|
24472
|
+
},
|
|
24473
|
+
terminal: overrides?.terminal ?? false
|
|
24474
|
+
},
|
|
24475
|
+
clientInfo
|
|
24476
|
+
};
|
|
24477
|
+
}
|
|
24478
|
+
|
|
24479
|
+
// ../../packages/ycodium-acp-executor/src/config-option-wiring.ts
|
|
24480
|
+
var VENDOR_CATEGORY_PREFIX = "_";
|
|
24481
|
+
var ACP_CONFIG_CATEGORY_HANDLING = {
|
|
24482
|
+
mode: {
|
|
24483
|
+
consumed: true,
|
|
24484
|
+
via: "runtime-mode.ts\uFF1AacpAvailableModes / acpCurrentModeId / acpModeConfigId"
|
|
24485
|
+
},
|
|
24486
|
+
model: {
|
|
24487
|
+
consumed: true,
|
|
24488
|
+
via: "runtime-model.ts:extractModelConfigId \u2192 executor.ts:applyRequestedModel"
|
|
24489
|
+
},
|
|
24490
|
+
thought_level: {
|
|
24491
|
+
consumed: true,
|
|
24492
|
+
via: "config-options.ts:findAcpEffortConfig \u2192 executor.ts:applyRequestedEffort"
|
|
24493
|
+
},
|
|
24494
|
+
model_option: {
|
|
24495
|
+
consumed: true,
|
|
24496
|
+
// 精确说法:它不是**识别器**,是**优先级**。`looksLikeEffortOption` 先筛出候选,
|
|
24497
|
+
// 标了 `model_option` 的在候选里排第一(厂商自己标的最准),排在 id 叫 `effort`
|
|
24498
|
+
// 的和标 `thought_level` 的前面。光有这个 category 而 id/name 不像力度档的,
|
|
24499
|
+
// 压根进不了候选。
|
|
24500
|
+
via: "config-options.ts:findAcpEffortConfig\uFF08\u5019\u9009\u91CC\u7684\u6700\u9AD8\u4F18\u5148\u7EA7\uFF0C\u538B\u8FC7 thought_level\uFF09"
|
|
24501
|
+
},
|
|
24502
|
+
model_config: {
|
|
24503
|
+
consumed: false,
|
|
24504
|
+
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"
|
|
24505
|
+
}
|
|
24506
|
+
};
|
|
24507
|
+
var KNOWN_CATEGORIES = new Set(Object.keys(ACP_CONFIG_CATEGORY_HANDLING));
|
|
24508
|
+
function unhandledConfigCategories(configOptions) {
|
|
24509
|
+
if (!configOptions) return [];
|
|
24510
|
+
const seen = /* @__PURE__ */ new Set();
|
|
24511
|
+
const out = [];
|
|
24512
|
+
for (const option of configOptions) {
|
|
24513
|
+
const category = option.category?.trim();
|
|
24514
|
+
if (!category) continue;
|
|
24515
|
+
if (KNOWN_CATEGORIES.has(category)) continue;
|
|
24516
|
+
if (seen.has(category)) continue;
|
|
24517
|
+
seen.add(category);
|
|
24518
|
+
out.push({
|
|
24519
|
+
category,
|
|
24520
|
+
optionId: option.id.trim(),
|
|
24521
|
+
vendorExtension: category.startsWith(VENDOR_CATEGORY_PREFIX)
|
|
24522
|
+
});
|
|
24523
|
+
}
|
|
24524
|
+
return out;
|
|
24525
|
+
}
|
|
24526
|
+
|
|
24352
24527
|
// ../../packages/ycodium-acp-executor/src/mutex.ts
|
|
24353
24528
|
var Mutex = class {
|
|
24354
24529
|
locked = false;
|
|
@@ -24391,7 +24566,7 @@ var Mutex = class {
|
|
|
24391
24566
|
};
|
|
24392
24567
|
|
|
24393
24568
|
// ../../packages/ycodium-acp-executor/src/util.ts
|
|
24394
|
-
function
|
|
24569
|
+
function isRecord5(value) {
|
|
24395
24570
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
24396
24571
|
}
|
|
24397
24572
|
function asTrimmedString(value) {
|
|
@@ -24437,11 +24612,11 @@ function extractCommandFromTitle(title) {
|
|
|
24437
24612
|
return backtickMatch?.[1]?.trim() || void 0;
|
|
24438
24613
|
}
|
|
24439
24614
|
function extractToolCommand(data, title) {
|
|
24440
|
-
const item =
|
|
24441
|
-
const itemInput = item &&
|
|
24442
|
-
const itemResult = item &&
|
|
24443
|
-
const rawInput =
|
|
24444
|
-
const dataInput =
|
|
24615
|
+
const item = isRecord5(data?.["item"]) ? data["item"] : void 0;
|
|
24616
|
+
const itemInput = item && isRecord5(item["input"]) ? item["input"] : void 0;
|
|
24617
|
+
const itemResult = item && isRecord5(item["result"]) ? item["result"] : void 0;
|
|
24618
|
+
const rawInput = isRecord5(data?.["rawInput"]) ? data["rawInput"] : void 0;
|
|
24619
|
+
const dataInput = isRecord5(data?.["input"]) ? data["input"] : void 0;
|
|
24445
24620
|
const candidates = [
|
|
24446
24621
|
normalizeCommandValue(item?.["command"]),
|
|
24447
24622
|
normalizeCommandValue(itemInput?.["command"]),
|
|
@@ -24486,7 +24661,7 @@ function collectPaths(value, paths, seen, depth) {
|
|
|
24486
24661
|
}
|
|
24487
24662
|
return;
|
|
24488
24663
|
}
|
|
24489
|
-
if (!
|
|
24664
|
+
if (!isRecord5(value)) {
|
|
24490
24665
|
return;
|
|
24491
24666
|
}
|
|
24492
24667
|
for (const key of ["path", "filePath", "relativePath", "filename", "newPath", "oldPath"]) {
|
|
@@ -24549,7 +24724,7 @@ function deriveToolActivityPresentation(input2) {
|
|
|
24549
24724
|
const title = asTrimmedString(input2.title);
|
|
24550
24725
|
const detail = stripTrailingExitCode(asTrimmedString(input2.detail));
|
|
24551
24726
|
const fallbackSummary = asTrimmedString(input2.fallbackSummary) ?? "Tool";
|
|
24552
|
-
const data =
|
|
24727
|
+
const data = isRecord5(input2.data) ? input2.data : void 0;
|
|
24553
24728
|
const command = extractToolCommand(data, title);
|
|
24554
24729
|
const primaryPath = extractPrimaryPath(data);
|
|
24555
24730
|
const action = classifyToolAction({
|
|
@@ -24581,7 +24756,7 @@ function deriveToolActivityPresentation(input2) {
|
|
|
24581
24756
|
};
|
|
24582
24757
|
}
|
|
24583
24758
|
if (action === "search") {
|
|
24584
|
-
const rawInput =
|
|
24759
|
+
const rawInput = isRecord5(data?.["rawInput"]) ? data["rawInput"] : void 0;
|
|
24585
24760
|
const query = asTrimmedString(rawInput?.["query"]) ?? asTrimmedString(rawInput?.["pattern"]) ?? asTrimmedString(rawInput?.["searchTerm"]);
|
|
24586
24761
|
return {
|
|
24587
24762
|
summary: "Searched files",
|
|
@@ -24620,6 +24795,46 @@ function findSessionConfigOption(configOptions, configId) {
|
|
|
24620
24795
|
}
|
|
24621
24796
|
return configOptions.find((option) => option.id.trim() === normalizedConfigId);
|
|
24622
24797
|
}
|
|
24798
|
+
function collectSessionConfigOptionValues(configOption) {
|
|
24799
|
+
if (configOption.type !== "select" || !configOption.options) {
|
|
24800
|
+
return [];
|
|
24801
|
+
}
|
|
24802
|
+
return configOption.options.flatMap(
|
|
24803
|
+
(entry) => "value" in entry ? [entry.value] : entry.options.map((option) => option.value)
|
|
24804
|
+
);
|
|
24805
|
+
}
|
|
24806
|
+
function collectSessionModelIds(models) {
|
|
24807
|
+
if (!isRecord5(models) || !Array.isArray(models["availableModels"])) {
|
|
24808
|
+
return [];
|
|
24809
|
+
}
|
|
24810
|
+
return models["availableModels"].flatMap(
|
|
24811
|
+
(model) => isRecord5(model) && typeof model["modelId"] === "string" ? [model["modelId"]] : []
|
|
24812
|
+
);
|
|
24813
|
+
}
|
|
24814
|
+
function catalogModelLastSegment(value) {
|
|
24815
|
+
if (value.startsWith("[")) {
|
|
24816
|
+
let parsed;
|
|
24817
|
+
try {
|
|
24818
|
+
parsed = JSON.parse(value);
|
|
24819
|
+
} catch {
|
|
24820
|
+
return void 0;
|
|
24821
|
+
}
|
|
24822
|
+
if (!Array.isArray(parsed) || parsed.length < 2) return void 0;
|
|
24823
|
+
const last = parsed[parsed.length - 1];
|
|
24824
|
+
return typeof last === "string" ? last : void 0;
|
|
24825
|
+
}
|
|
24826
|
+
const separator = value.indexOf("/");
|
|
24827
|
+
return separator > 0 && separator < value.length - 1 ? value.slice(separator + 1) : void 0;
|
|
24828
|
+
}
|
|
24829
|
+
function matchAdvertisedModelValue(requestedModelId, advertisedValues) {
|
|
24830
|
+
const wanted = requestedModelId.trim();
|
|
24831
|
+
if (!wanted) return void 0;
|
|
24832
|
+
if (advertisedValues.includes(wanted)) return wanted;
|
|
24833
|
+
const byLastSegment = advertisedValues.filter(
|
|
24834
|
+
(value) => catalogModelLastSegment(value) === wanted
|
|
24835
|
+
);
|
|
24836
|
+
return byLastSegment.length === 1 ? byLastSegment[0] : void 0;
|
|
24837
|
+
}
|
|
24623
24838
|
function parseSessionModeState(sessionResponse) {
|
|
24624
24839
|
const modes = sessionResponse.modes;
|
|
24625
24840
|
if (!modes) return void 0;
|
|
@@ -24658,7 +24873,7 @@ function parseAvailableCommandsUpdate(params) {
|
|
|
24658
24873
|
}
|
|
24659
24874
|
const commands = [];
|
|
24660
24875
|
for (const command of availableCommands) {
|
|
24661
|
-
if (!
|
|
24876
|
+
if (!isRecord5(command)) {
|
|
24662
24877
|
continue;
|
|
24663
24878
|
}
|
|
24664
24879
|
const name = typeof command["name"] === "string" ? command["name"].trim() : "";
|
|
@@ -24666,7 +24881,7 @@ function parseAvailableCommandsUpdate(params) {
|
|
|
24666
24881
|
continue;
|
|
24667
24882
|
}
|
|
24668
24883
|
const description = typeof command["description"] === "string" ? command["description"].trim() || void 0 : void 0;
|
|
24669
|
-
const input2 =
|
|
24884
|
+
const input2 = isRecord5(command["input"]) ? command["input"] : void 0;
|
|
24670
24885
|
const inputHint = typeof input2?.["hint"] === "string" ? input2["hint"].trim() || void 0 : void 0;
|
|
24671
24886
|
commands.push({
|
|
24672
24887
|
name,
|
|
@@ -24728,7 +24943,7 @@ function extractCommandFromTitle2(title) {
|
|
|
24728
24943
|
return match?.[1]?.trim() || void 0;
|
|
24729
24944
|
}
|
|
24730
24945
|
function extractToolCallCommand(rawInput, title) {
|
|
24731
|
-
if (
|
|
24946
|
+
if (isRecord5(rawInput)) {
|
|
24732
24947
|
const directCommand = normalizeCommandValue2(rawInput["command"]);
|
|
24733
24948
|
if (directCommand) {
|
|
24734
24949
|
return directCommand;
|
|
@@ -24755,7 +24970,7 @@ function boundToolCallOutputText(text) {
|
|
|
24755
24970
|
}
|
|
24756
24971
|
var RAW_OUTPUT_TEXT_FIELDS = ["content", "stdout", "stderr", "output"];
|
|
24757
24972
|
function boundToolCallRawOutput(rawOutput) {
|
|
24758
|
-
if (!
|
|
24973
|
+
if (!isRecord5(rawOutput)) {
|
|
24759
24974
|
return rawOutput;
|
|
24760
24975
|
}
|
|
24761
24976
|
let changed = false;
|
|
@@ -24985,7 +25200,7 @@ function parseSessionUpdateEvent(params) {
|
|
|
24985
25200
|
const entries = Array.isArray(upd["entries"]) ? upd["entries"] : [];
|
|
24986
25201
|
const plan = entries.map((entry, index) => ({
|
|
24987
25202
|
step: readAcpText(entry, "content") ?? `Step ${index + 1}`,
|
|
24988
|
-
status: normalizePlanStepStatus(
|
|
25203
|
+
status: normalizePlanStepStatus(isRecord5(entry) ? entry["status"] : void 0)
|
|
24989
25204
|
}));
|
|
24990
25205
|
if (plan.length > 0) {
|
|
24991
25206
|
events.push({
|
|
@@ -25024,7 +25239,7 @@ function parseSessionUpdateEvent(params) {
|
|
|
25024
25239
|
}
|
|
25025
25240
|
case "agent_message_chunk":
|
|
25026
25241
|
case "agent_thought_chunk": {
|
|
25027
|
-
const content =
|
|
25242
|
+
const content = isRecord5(upd["content"]) ? upd["content"] : void 0;
|
|
25028
25243
|
const contentType = content?.["type"];
|
|
25029
25244
|
const streamKind = kind === "agent_thought_chunk" ? "reasoning_text" : "assistant_text";
|
|
25030
25245
|
if (contentType === "text") {
|
|
@@ -25091,16 +25306,18 @@ function normalizeModelId(value) {
|
|
|
25091
25306
|
return value?.trim() || void 0;
|
|
25092
25307
|
}
|
|
25093
25308
|
function modelSelectionStateFromSetup(setup) {
|
|
25094
|
-
const sessionModels =
|
|
25309
|
+
const sessionModels = isRecord5(setup.models) ? setup.models : void 0;
|
|
25095
25310
|
const modelConfigId = extractModelConfigId(setup);
|
|
25096
25311
|
const strategy = sessionModels === void 0 && modelConfigId !== void 0 ? "config-option" : "session/set_model";
|
|
25097
|
-
const
|
|
25312
|
+
const modelConfigOption = modelConfigId === void 0 ? void 0 : findSessionConfigOption(setup.configOptions, modelConfigId);
|
|
25313
|
+
const configModel = modelConfigOption?.currentValue;
|
|
25098
25314
|
const currentModelId = strategy === "config-option" ? typeof configModel === "string" ? normalizeModelId(configModel) : void 0 : typeof sessionModels?.["currentModelId"] === "string" ? normalizeModelId(sessionModels["currentModelId"]) : void 0;
|
|
25099
25315
|
return {
|
|
25100
25316
|
strategy,
|
|
25101
25317
|
sessionModelsAdvertised: sessionModels !== void 0,
|
|
25102
25318
|
modelConfigId,
|
|
25103
|
-
currentModelId
|
|
25319
|
+
currentModelId,
|
|
25320
|
+
advertisedModelValues: strategy === "config-option" ? modelConfigOption === void 0 ? [] : collectSessionConfigOptionValues(modelConfigOption) : collectSessionModelIds(sessionModels)
|
|
25104
25321
|
};
|
|
25105
25322
|
}
|
|
25106
25323
|
function createAcpExecutor(options) {
|
|
@@ -25119,8 +25336,10 @@ function createAcpExecutor(options) {
|
|
|
25119
25336
|
strategy: "session/set_model",
|
|
25120
25337
|
sessionModelsAdvertised: false,
|
|
25121
25338
|
modelConfigId: void 0,
|
|
25122
|
-
currentModelId: void 0
|
|
25339
|
+
currentModelId: void 0,
|
|
25340
|
+
advertisedModelValues: []
|
|
25123
25341
|
};
|
|
25342
|
+
let lastUnmatchedModelId;
|
|
25124
25343
|
let initializeResult;
|
|
25125
25344
|
let startResult;
|
|
25126
25345
|
let startInFlight;
|
|
@@ -25445,6 +25664,12 @@ function createAcpExecutor(options) {
|
|
|
25445
25664
|
const userInputOutcome = await handlePermissionAsUserInput(params);
|
|
25446
25665
|
if (userInputOutcome !== void 0) return userInputOutcome;
|
|
25447
25666
|
const permissionRequest = parsePermissionRequest(params);
|
|
25667
|
+
if (options.autoApprove?.(permissionRequest) === true) {
|
|
25668
|
+
const optionId = selectAutoApprovedAcpPermissionOption(params);
|
|
25669
|
+
if (optionId !== void 0) {
|
|
25670
|
+
return { outcome: { outcome: "selected", optionId } };
|
|
25671
|
+
}
|
|
25672
|
+
}
|
|
25448
25673
|
const requestId = randomId();
|
|
25449
25674
|
const outcome = await new Promise((resolve2) => {
|
|
25450
25675
|
const pending = {
|
|
@@ -25532,36 +25757,70 @@ function createAcpExecutor(options) {
|
|
|
25532
25757
|
if (requestedModelId === void 0 || requestedModelId === input2.state.currentModelId || input2.state.strategy === "session/set_model" && !input2.state.sessionModelsAdvertised || input2.state.strategy === "config-option" && input2.state.modelConfigId === void 0) {
|
|
25533
25758
|
return input2.state;
|
|
25534
25759
|
}
|
|
25760
|
+
const value = matchAdvertisedModelValue(requestedModelId, input2.state.advertisedModelValues);
|
|
25761
|
+
if (value === void 0) {
|
|
25762
|
+
if (requestedModelId !== lastUnmatchedModelId) {
|
|
25763
|
+
lastUnmatchedModelId = requestedModelId;
|
|
25764
|
+
options.log?.info("requested model is not in the agent catalog; keeping its own model", {
|
|
25765
|
+
requestedModelId,
|
|
25766
|
+
currentModelId: input2.state.currentModelId,
|
|
25767
|
+
advertisedModelValues: input2.state.advertisedModelValues
|
|
25768
|
+
});
|
|
25769
|
+
}
|
|
25770
|
+
return input2.state;
|
|
25771
|
+
}
|
|
25772
|
+
if (value === input2.state.currentModelId) {
|
|
25773
|
+
return input2.state;
|
|
25774
|
+
}
|
|
25535
25775
|
if (input2.state.strategy === "config-option") {
|
|
25536
25776
|
await connection.request("session/set_config_option", {
|
|
25537
25777
|
sessionId: input2.acpSessionId,
|
|
25538
25778
|
configId: input2.state.modelConfigId,
|
|
25539
|
-
value
|
|
25779
|
+
value
|
|
25540
25780
|
});
|
|
25541
25781
|
} else {
|
|
25542
25782
|
await connection.request("session/set_model", {
|
|
25543
25783
|
sessionId: input2.acpSessionId,
|
|
25544
|
-
modelId:
|
|
25784
|
+
modelId: value
|
|
25545
25785
|
});
|
|
25546
25786
|
}
|
|
25547
25787
|
if (!isLive(input2.expectedGeneration)) {
|
|
25548
25788
|
throw new AcpExecutorDisposedError();
|
|
25549
25789
|
}
|
|
25550
|
-
return { ...input2.state, currentModelId:
|
|
25790
|
+
return { ...input2.state, currentModelId: value };
|
|
25791
|
+
};
|
|
25792
|
+
const applyRequestedEffort = async (input2) => {
|
|
25793
|
+
const requested = input2.requestedEffort?.trim();
|
|
25794
|
+
if (!requested) return;
|
|
25795
|
+
const configOptions = typeof input2.setup === "object" && input2.setup !== null ? input2.setup.configOptions : void 0;
|
|
25796
|
+
const effort = findAcpEffortConfig(configOptions);
|
|
25797
|
+
if (effort === void 0) return;
|
|
25798
|
+
const matched = effort.choices.find((choice) => choice.value === requested);
|
|
25799
|
+
if (matched === void 0) {
|
|
25800
|
+
options.log?.info("requested effort is not in the agent catalog; keeping its own", {
|
|
25801
|
+
requestedEffort: requested,
|
|
25802
|
+
advertised: effort.choices.map((choice) => choice.value)
|
|
25803
|
+
});
|
|
25804
|
+
return;
|
|
25805
|
+
}
|
|
25806
|
+
if (matched.isDefault === true) return;
|
|
25807
|
+
if (!isLive(input2.expectedGeneration)) return;
|
|
25808
|
+
try {
|
|
25809
|
+
await connection.request("session/set_config_option", {
|
|
25810
|
+
sessionId: input2.acpSessionId,
|
|
25811
|
+
configId: effort.configId,
|
|
25812
|
+
value: requested
|
|
25813
|
+
});
|
|
25814
|
+
} catch (error62) {
|
|
25815
|
+
options.log?.info("setting the effort level failed; the session keeps its own", {
|
|
25816
|
+
requestedEffort: requested,
|
|
25817
|
+
configId: effort.configId,
|
|
25818
|
+
error: error62 instanceof Error ? error62.message : String(error62)
|
|
25819
|
+
});
|
|
25820
|
+
}
|
|
25551
25821
|
};
|
|
25552
25822
|
const startOnce = async (expectedGeneration) => {
|
|
25553
|
-
const initializePayload =
|
|
25554
|
-
protocolVersion: PROTOCOL_VERSION,
|
|
25555
|
-
clientCapabilities: {
|
|
25556
|
-
fs: {
|
|
25557
|
-
readTextFile: false,
|
|
25558
|
-
writeTextFile: false,
|
|
25559
|
-
...options.clientCapabilities?.fs
|
|
25560
|
-
},
|
|
25561
|
-
terminal: options.clientCapabilities?.terminal ?? false
|
|
25562
|
-
},
|
|
25563
|
-
clientInfo: options.clientInfo
|
|
25564
|
-
};
|
|
25823
|
+
const initializePayload = acpInitializePayload(options.clientInfo, options.clientCapabilities);
|
|
25565
25824
|
const initialized = await connection.request(
|
|
25566
25825
|
"initialize",
|
|
25567
25826
|
initializePayload
|
|
@@ -25605,12 +25864,27 @@ function createAcpExecutor(options) {
|
|
|
25605
25864
|
nextSessionId = created.sessionId;
|
|
25606
25865
|
setup = created;
|
|
25607
25866
|
}
|
|
25867
|
+
const unhandledCategories = unhandledConfigCategories(
|
|
25868
|
+
setup.configOptions
|
|
25869
|
+
);
|
|
25870
|
+
if (unhandledCategories.length > 0) {
|
|
25871
|
+
options.log?.info("acp session reported config option categories we do not handle", {
|
|
25872
|
+
sessionId: nextSessionId,
|
|
25873
|
+
categories: unhandledCategories
|
|
25874
|
+
});
|
|
25875
|
+
}
|
|
25608
25876
|
const nextModelSelectionState = await applyRequestedModel({
|
|
25609
25877
|
state: modelSelectionStateFromSetup(setup),
|
|
25610
25878
|
acpSessionId: nextSessionId,
|
|
25611
25879
|
requestedModelId: options.requestedModelId,
|
|
25612
25880
|
expectedGeneration
|
|
25613
25881
|
});
|
|
25882
|
+
await applyRequestedEffort({
|
|
25883
|
+
setup,
|
|
25884
|
+
acpSessionId: nextSessionId,
|
|
25885
|
+
requestedEffort: options.requestedEffort,
|
|
25886
|
+
expectedGeneration
|
|
25887
|
+
});
|
|
25614
25888
|
if (options.beforeApplyResult) {
|
|
25615
25889
|
await options.beforeApplyResult();
|
|
25616
25890
|
}
|
|
@@ -25975,6 +26249,26 @@ function createAcpExecutor(options) {
|
|
|
25975
26249
|
};
|
|
25976
26250
|
apply();
|
|
25977
26251
|
};
|
|
26252
|
+
const setNativeMode = async (modeId) => {
|
|
26253
|
+
failIfDisposed();
|
|
26254
|
+
const started = startResult;
|
|
26255
|
+
if (started === void 0) {
|
|
26256
|
+
throw new AcpExecutorRequestError("session/set_mode", "session has not started yet");
|
|
26257
|
+
}
|
|
26258
|
+
const configId = acpModeConfigId(started.sessionSetupResult);
|
|
26259
|
+
if (configId !== void 0) {
|
|
26260
|
+
await connection.request("session/set_config_option", {
|
|
26261
|
+
sessionId: started.sessionId,
|
|
26262
|
+
configId,
|
|
26263
|
+
value: modeId
|
|
26264
|
+
});
|
|
26265
|
+
return;
|
|
26266
|
+
}
|
|
26267
|
+
await connection.request("session/set_mode", {
|
|
26268
|
+
sessionId: started.sessionId,
|
|
26269
|
+
modeId
|
|
26270
|
+
});
|
|
26271
|
+
};
|
|
25978
26272
|
const respondToUserInput = async (requestId, answers) => {
|
|
25979
26273
|
failIfDisposed();
|
|
25980
26274
|
const pending = pendingUserInputs.get(requestId);
|
|
@@ -25991,7 +26285,7 @@ function createAcpExecutor(options) {
|
|
|
25991
26285
|
pendingUserInputs.delete(requestId);
|
|
25992
26286
|
pending.resolve({
|
|
25993
26287
|
kind: "answered",
|
|
25994
|
-
answers:
|
|
26288
|
+
answers: isRecord5(answers) ? answers : {}
|
|
25995
26289
|
});
|
|
25996
26290
|
};
|
|
25997
26291
|
const dispose = async () => {
|
|
@@ -26041,13 +26335,56 @@ function createAcpExecutor(options) {
|
|
|
26041
26335
|
cancel,
|
|
26042
26336
|
respondToRequest,
|
|
26043
26337
|
respondToUserInput,
|
|
26338
|
+
setNativeMode,
|
|
26044
26339
|
dispose
|
|
26045
26340
|
};
|
|
26046
26341
|
}
|
|
26047
26342
|
|
|
26343
|
+
// ../../packages/ycodium-acp-executor/src/probe.ts
|
|
26344
|
+
function withProbeTimeout(promise2, timeoutMs, method) {
|
|
26345
|
+
return new Promise((resolve2, reject) => {
|
|
26346
|
+
const timer = setTimeout(() => {
|
|
26347
|
+
reject(new Error(`ACP probe request '${method}' timed out after ${timeoutMs}ms`));
|
|
26348
|
+
}, timeoutMs);
|
|
26349
|
+
timer.unref?.();
|
|
26350
|
+
promise2.then(
|
|
26351
|
+
(value) => {
|
|
26352
|
+
clearTimeout(timer);
|
|
26353
|
+
resolve2(value);
|
|
26354
|
+
},
|
|
26355
|
+
(error62) => {
|
|
26356
|
+
clearTimeout(timer);
|
|
26357
|
+
reject(error62 instanceof Error ? error62 : new Error(String(error62)));
|
|
26358
|
+
}
|
|
26359
|
+
);
|
|
26360
|
+
});
|
|
26361
|
+
}
|
|
26362
|
+
async function openAcpProbeHandshake(input2) {
|
|
26363
|
+
const connection = createAcpConnection({
|
|
26364
|
+
readable: input2.readable,
|
|
26365
|
+
writable: input2.writable,
|
|
26366
|
+
onNotification: () => void 0
|
|
26367
|
+
});
|
|
26368
|
+
try {
|
|
26369
|
+
const initializeResult = await withProbeTimeout(
|
|
26370
|
+
connection.request("initialize", acpInitializePayload(input2.clientInfo)),
|
|
26371
|
+
input2.requestTimeoutMs,
|
|
26372
|
+
"initialize"
|
|
26373
|
+
);
|
|
26374
|
+
return {
|
|
26375
|
+
initializeResult,
|
|
26376
|
+
request: (method, params) => withProbeTimeout(connection.request(method, params), input2.requestTimeoutMs, method),
|
|
26377
|
+
close: () => connection.close()
|
|
26378
|
+
};
|
|
26379
|
+
} catch (error62) {
|
|
26380
|
+
await connection.close().catch(() => void 0);
|
|
26381
|
+
throw error62;
|
|
26382
|
+
}
|
|
26383
|
+
}
|
|
26384
|
+
|
|
26048
26385
|
// src/config.ts
|
|
26049
26386
|
var DSH_DEFAULT_LAUNCH_ARGS = "--profile acp";
|
|
26050
|
-
function
|
|
26387
|
+
function isRecord6(value) {
|
|
26051
26388
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
26052
26389
|
}
|
|
26053
26390
|
function readString(value) {
|
|
@@ -26060,7 +26397,7 @@ function readEnvironment(value) {
|
|
|
26060
26397
|
if (!Array.isArray(value)) return {};
|
|
26061
26398
|
const env = {};
|
|
26062
26399
|
for (const entry of value) {
|
|
26063
|
-
if (!
|
|
26400
|
+
if (!isRecord6(entry)) continue;
|
|
26064
26401
|
const name = readTrimmed(entry["name"]);
|
|
26065
26402
|
if (name.length === 0) continue;
|
|
26066
26403
|
env[name] = readString(entry["value"]);
|
|
@@ -26068,7 +26405,7 @@ function readEnvironment(value) {
|
|
|
26068
26405
|
return env;
|
|
26069
26406
|
}
|
|
26070
26407
|
function parseDshInstanceConfig(config2) {
|
|
26071
|
-
if (!
|
|
26408
|
+
if (!isRecord6(config2)) {
|
|
26072
26409
|
return {
|
|
26073
26410
|
binaryPath: "",
|
|
26074
26411
|
launchArgs: DSH_DEFAULT_LAUNCH_ARGS,
|
|
@@ -26078,7 +26415,7 @@ function parseDshInstanceConfig(config2) {
|
|
|
26078
26415
|
envelopeDriver: void 0
|
|
26079
26416
|
};
|
|
26080
26417
|
}
|
|
26081
|
-
const nested =
|
|
26418
|
+
const nested = isRecord6(config2["config"]) ? config2["config"] : void 0;
|
|
26082
26419
|
const settings = nested ?? config2;
|
|
26083
26420
|
return {
|
|
26084
26421
|
binaryPath: readTrimmed(settings["binaryPath"]),
|
|
@@ -26141,57 +26478,192 @@ function tokenizeLaunchArgs(value) {
|
|
|
26141
26478
|
return tokens;
|
|
26142
26479
|
}
|
|
26143
26480
|
|
|
26144
|
-
// src/
|
|
26145
|
-
import * as
|
|
26146
|
-
|
|
26147
|
-
|
|
26148
|
-
|
|
26149
|
-
|
|
26150
|
-
|
|
26151
|
-
|
|
26152
|
-
|
|
26153
|
-
|
|
26154
|
-
|
|
26155
|
-
|
|
26156
|
-
|
|
26157
|
-
|
|
26158
|
-
|
|
26159
|
-
|
|
26160
|
-
|
|
26161
|
-
|
|
26162
|
-
|
|
26163
|
-
|
|
26164
|
-
|
|
26165
|
-
|
|
26166
|
-
|
|
26167
|
-
|
|
26168
|
-
|
|
26169
|
-
|
|
26170
|
-
|
|
26171
|
-
|
|
26172
|
-
|
|
26173
|
-
|
|
26174
|
-
|
|
26175
|
-
|
|
26176
|
-
|
|
26177
|
-
|
|
26178
|
-
|
|
26179
|
-
|
|
26180
|
-
|
|
26181
|
-
|
|
26182
|
-
|
|
26183
|
-
|
|
26184
|
-
|
|
26185
|
-
|
|
26186
|
-
|
|
26187
|
-
|
|
26481
|
+
// src/probeModels.ts
|
|
26482
|
+
import * as NodeFS from "node:fs";
|
|
26483
|
+
import * as NodeOS2 from "node:os";
|
|
26484
|
+
import * as NodePath2 from "node:path";
|
|
26485
|
+
import { nodeStreamsFromHandle } from "@ycodium-ai/plugin-api/process-stdin";
|
|
26486
|
+
|
|
26487
|
+
// src/spawnEnv.ts
|
|
26488
|
+
import * as NodeOS from "node:os";
|
|
26489
|
+
import * as NodePath from "node:path";
|
|
26490
|
+
import { mergeLaunchEnv, brainSlotEnvKeys } from "@ycodium-ai/plugin-api/executor";
|
|
26491
|
+
|
|
26492
|
+
// src/permission.ts
|
|
26493
|
+
var DSH_NATIVE_MODES = [
|
|
26494
|
+
{
|
|
26495
|
+
id: "read-only",
|
|
26496
|
+
name: "Read only",
|
|
26497
|
+
description: "Sandboxed to reading. Asks before anything that writes or runs."
|
|
26498
|
+
},
|
|
26499
|
+
{
|
|
26500
|
+
id: "workspace-write",
|
|
26501
|
+
name: "Workspace write",
|
|
26502
|
+
description: "Writes inside the workspace. Asks before stepping outside it."
|
|
26503
|
+
},
|
|
26504
|
+
{
|
|
26505
|
+
id: "danger-full-access",
|
|
26506
|
+
name: "Full access",
|
|
26507
|
+
description: "No sandbox, never asks. Every command runs immediately."
|
|
26508
|
+
}
|
|
26509
|
+
];
|
|
26510
|
+
var DSH_DEFAULT_NATIVE_MODE_ID = "workspace-write";
|
|
26511
|
+
function isDshNativeModeId(value) {
|
|
26512
|
+
return DSH_NATIVE_MODES.some((mode) => mode.id === value);
|
|
26513
|
+
}
|
|
26514
|
+
function dshNativeModeReport(instanceEnvironment) {
|
|
26515
|
+
const configured = instanceEnvironment["DSH_PERMISSION_MODE"]?.trim();
|
|
26516
|
+
return {
|
|
26517
|
+
nativeModes: DSH_NATIVE_MODES.map((mode) => ({
|
|
26518
|
+
id: mode.id,
|
|
26519
|
+
name: mode.name,
|
|
26520
|
+
description: mode.description
|
|
26521
|
+
})),
|
|
26522
|
+
currentNativeModeId: configured !== void 0 && isDshNativeModeId(configured) ? configured : DSH_DEFAULT_NATIVE_MODE_ID
|
|
26523
|
+
};
|
|
26524
|
+
}
|
|
26525
|
+
function dshPermissionEnv(nativeModeId) {
|
|
26526
|
+
if (nativeModeId === void 0 || !isDshNativeModeId(nativeModeId)) return {};
|
|
26527
|
+
return { DSH_PERMISSION_MODE: nativeModeId };
|
|
26528
|
+
}
|
|
26529
|
+
|
|
26530
|
+
// src/spawnEnv.ts
|
|
26531
|
+
function expandHomePath(value) {
|
|
26532
|
+
if (value === "~") return NodeOS.homedir();
|
|
26533
|
+
if (value.startsWith("~/") || value.startsWith("~\\")) {
|
|
26534
|
+
return NodePath.join(NodeOS.homedir(), value.slice(2));
|
|
26535
|
+
}
|
|
26536
|
+
return value;
|
|
26537
|
+
}
|
|
26538
|
+
function resolveHomePath(value) {
|
|
26539
|
+
const trimmed = value.trim();
|
|
26540
|
+
if (trimmed.length === 0) return "";
|
|
26541
|
+
return NodePath.resolve(expandHomePath(trimmed));
|
|
26542
|
+
}
|
|
26543
|
+
function dshContinuationKey(homePath) {
|
|
26544
|
+
const resolved = resolveHomePath(homePath);
|
|
26545
|
+
return `dsh:home:${resolved.length > 0 ? resolved : NodePath.join(NodeOS.homedir(), ".dsh")}`;
|
|
26546
|
+
}
|
|
26547
|
+
function buildSpawnEnv(recipe, resolvedHomePath, launchEnv = {}, nativeModeId) {
|
|
26548
|
+
const homeEnvVar = recipe.homeEnvVar.trim() || DSH_CARD.homeEnvVar;
|
|
26549
|
+
const homeInjection = resolvedHomePath.length > 0 && homeEnvVar.length > 0 ? { [homeEnvVar]: resolvedHomePath } : {};
|
|
26550
|
+
return mergeLaunchEnv({
|
|
26551
|
+
brainSlots: brainSlotEnvKeys(DSH_CARD),
|
|
26552
|
+
accountHome: homeInjection,
|
|
26553
|
+
instanceTable: recipe.environment,
|
|
26554
|
+
// 用户挑的档压过实例表:实例表里那一栏是这台机器的默认,输入区那一下是这一轮的
|
|
26555
|
+
// 意思。铺在 `handOverrides` 这一层正是为了这个先后(见 `mergeLaunchEnv` 的层序)。
|
|
26556
|
+
handOverrides: dshPermissionEnv(nativeModeId),
|
|
26557
|
+
launch: launchEnv
|
|
26188
26558
|
});
|
|
26189
|
-
|
|
26559
|
+
}
|
|
26560
|
+
|
|
26561
|
+
// src/probeModels.ts
|
|
26562
|
+
var PROBE_REQUEST_TIMEOUT_MS = 15e3;
|
|
26563
|
+
function dshProbeSessionCwd(instanceId) {
|
|
26564
|
+
const safe = instanceId.trim().replace(/[^A-Za-z0-9._-]/g, "-") || "instance";
|
|
26565
|
+
return NodePath2.join(NodeOS2.tmpdir(), `ycodium-dsh-probe-${safe}`);
|
|
26566
|
+
}
|
|
26567
|
+
function isRecord7(value) {
|
|
26568
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
26569
|
+
}
|
|
26570
|
+
function asString3(value) {
|
|
26571
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
|
|
26572
|
+
}
|
|
26573
|
+
function dshProbeModels(setup) {
|
|
26574
|
+
if (!isRecord7(setup)) return void 0;
|
|
26575
|
+
const rawOptions = setup["configOptions"];
|
|
26576
|
+
const choices = acpModelChoicesFromConfigOptions(rawOptions);
|
|
26577
|
+
if (choices === void 0) return void 0;
|
|
26578
|
+
const models = choices.map((choice) => ({
|
|
26579
|
+
slug: choice.value,
|
|
26580
|
+
...choice.name === void 0 ? {} : { name: choice.name },
|
|
26581
|
+
...choice.isDefault === true ? { isDefault: true } : {}
|
|
26582
|
+
}));
|
|
26583
|
+
const effort = findAcpEffortConfig(rawOptions);
|
|
26584
|
+
if (effort === void 0) return models;
|
|
26585
|
+
const capabilities = {
|
|
26586
|
+
optionDescriptors: [
|
|
26587
|
+
{
|
|
26588
|
+
id: DSH_EFFORT_OPTION_ID,
|
|
26589
|
+
label: effort.label || "Reasoning",
|
|
26590
|
+
type: "select",
|
|
26591
|
+
options: effort.choices.map((choice) => ({
|
|
26592
|
+
id: choice.value,
|
|
26593
|
+
label: choice.label,
|
|
26594
|
+
...choice.description === void 0 ? {} : { description: choice.description },
|
|
26595
|
+
...choice.isDefault === true ? { isDefault: true } : {}
|
|
26596
|
+
}))
|
|
26597
|
+
}
|
|
26598
|
+
]
|
|
26599
|
+
};
|
|
26600
|
+
return models.map((model) => ({ ...model, capabilities }));
|
|
26601
|
+
}
|
|
26602
|
+
var DSH_EFFORT_OPTION_ID = "reasoning";
|
|
26603
|
+
function findProbeSessionId(listed, probeCwd) {
|
|
26604
|
+
if (!isRecord7(listed) || !Array.isArray(listed["sessions"])) return void 0;
|
|
26605
|
+
for (const entry of listed["sessions"]) {
|
|
26606
|
+
if (!isRecord7(entry)) continue;
|
|
26607
|
+
if (asString3(entry["cwd"]) !== probeCwd) continue;
|
|
26608
|
+
const sessionId = asString3(entry["sessionId"]);
|
|
26609
|
+
if (sessionId !== void 0) return sessionId;
|
|
26610
|
+
}
|
|
26611
|
+
return void 0;
|
|
26612
|
+
}
|
|
26613
|
+
async function probeSessionSetup(handshake, probeCwd) {
|
|
26614
|
+
const listed = await handshake.request("session/list", {}).catch(() => void 0);
|
|
26615
|
+
const reusable = findProbeSessionId(listed, probeCwd);
|
|
26616
|
+
if (reusable !== void 0) {
|
|
26617
|
+
const resumed = await handshake.request("session/resume", { sessionId: reusable, cwd: probeCwd, mcpServers: [] }).catch(() => void 0);
|
|
26618
|
+
if (resumed !== void 0) return resumed;
|
|
26619
|
+
}
|
|
26620
|
+
return handshake.request("session/new", { cwd: probeCwd, mcpServers: [] });
|
|
26621
|
+
}
|
|
26622
|
+
var failureDetail = (error62) => (error62 instanceof Error ? error62.message : String(error62)).trim() || void 0;
|
|
26623
|
+
async function probeDshModels(input2) {
|
|
26624
|
+
const { recipe, host } = input2;
|
|
26625
|
+
const probeCwd = dshProbeSessionCwd(host.instanceId);
|
|
26626
|
+
try {
|
|
26627
|
+
NodeFS.mkdirSync(probeCwd, { recursive: true });
|
|
26628
|
+
} catch (error62) {
|
|
26629
|
+
return { kind: "failed", detail: failureDetail(error62) };
|
|
26630
|
+
}
|
|
26631
|
+
const env = buildSpawnEnv(recipe, resolveHomePath(recipe.homePath));
|
|
26632
|
+
let handle;
|
|
26633
|
+
try {
|
|
26634
|
+
handle = await host.process.spawn({
|
|
26635
|
+
program: recipe.binaryPath,
|
|
26636
|
+
args: tokenizeLaunchArgs(recipe.launchArgs),
|
|
26637
|
+
cwd: probeCwd,
|
|
26638
|
+
owner: { bindingId: host.bindingId },
|
|
26639
|
+
...Object.keys(env).length > 0 ? { env } : {}
|
|
26640
|
+
});
|
|
26641
|
+
} catch (error62) {
|
|
26642
|
+
return { kind: "not-started", detail: failureDetail(error62) };
|
|
26643
|
+
}
|
|
26644
|
+
let handshake;
|
|
26645
|
+
try {
|
|
26646
|
+
const { readable, writable } = nodeStreamsFromHandle(handle);
|
|
26647
|
+
handshake = await openAcpProbeHandshake({
|
|
26648
|
+
readable,
|
|
26649
|
+
writable,
|
|
26650
|
+
clientInfo: DSH_CLIENT_INFO,
|
|
26651
|
+
requestTimeoutMs: PROBE_REQUEST_TIMEOUT_MS
|
|
26652
|
+
});
|
|
26653
|
+
return {
|
|
26654
|
+
kind: "answered",
|
|
26655
|
+
models: dshProbeModels(await probeSessionSetup(handshake, probeCwd))
|
|
26656
|
+
};
|
|
26657
|
+
} catch (error62) {
|
|
26658
|
+
return { kind: "failed", detail: failureDetail(error62) };
|
|
26659
|
+
} finally {
|
|
26660
|
+
await handshake?.close().catch(() => void 0);
|
|
26661
|
+
await handle.kill("SIGTERM").catch(() => void 0);
|
|
26662
|
+
}
|
|
26190
26663
|
}
|
|
26191
26664
|
|
|
26192
26665
|
// src/dshInstance.ts
|
|
26193
26666
|
var RESUME_SCHEMA_VERSION = 1;
|
|
26194
|
-
var CLIENT_INFO = { name: "ycodium-agent-dsh", version: "0.1.0" };
|
|
26195
26667
|
var yieldTick = () => new Promise((resolve2) => {
|
|
26196
26668
|
setImmediate(resolve2);
|
|
26197
26669
|
});
|
|
@@ -26227,25 +26699,6 @@ var EventBus = class {
|
|
|
26227
26699
|
};
|
|
26228
26700
|
}
|
|
26229
26701
|
};
|
|
26230
|
-
function expandHomePath(value) {
|
|
26231
|
-
if (value === "~") return NodeOS.homedir();
|
|
26232
|
-
if (value.startsWith("~/") || value.startsWith("~\\")) {
|
|
26233
|
-
return NodePath.join(NodeOS.homedir(), value.slice(2));
|
|
26234
|
-
}
|
|
26235
|
-
return value;
|
|
26236
|
-
}
|
|
26237
|
-
function resolveHomePath(value) {
|
|
26238
|
-
const trimmed = value.trim();
|
|
26239
|
-
if (trimmed.length === 0) return "";
|
|
26240
|
-
return NodePath.resolve(expandHomePath(trimmed));
|
|
26241
|
-
}
|
|
26242
|
-
function compactEnv(env) {
|
|
26243
|
-
const out = {};
|
|
26244
|
-
for (const [key, value] of Object.entries(env)) {
|
|
26245
|
-
if (value !== void 0) out[key] = value;
|
|
26246
|
-
}
|
|
26247
|
-
return out;
|
|
26248
|
-
}
|
|
26249
26702
|
function modelIdFromSelection(selection) {
|
|
26250
26703
|
if (typeof selection !== "object" || selection === null || Array.isArray(selection)) {
|
|
26251
26704
|
return void 0;
|
|
@@ -26253,15 +26706,6 @@ function modelIdFromSelection(selection) {
|
|
|
26253
26706
|
const model = selection["model"];
|
|
26254
26707
|
return typeof model === "string" ? model.trim() || void 0 : void 0;
|
|
26255
26708
|
}
|
|
26256
|
-
function buildSpawnEnv(recipe, launch, resolvedHomePath) {
|
|
26257
|
-
const homeEnvVar = recipe.homeEnvVar.trim() || DSH_CARD.homeEnvVar;
|
|
26258
|
-
const homeInjection = resolvedHomePath.length > 0 && homeEnvVar.length > 0 ? { [homeEnvVar]: resolvedHomePath } : {};
|
|
26259
|
-
return {
|
|
26260
|
-
...compactEnv(launch.processEnv),
|
|
26261
|
-
...homeInjection,
|
|
26262
|
-
...recipe.environment
|
|
26263
|
-
};
|
|
26264
|
-
}
|
|
26265
26709
|
function nowIso() {
|
|
26266
26710
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
26267
26711
|
}
|
|
@@ -26291,11 +26735,25 @@ var createDshInstance = (config2, host) => {
|
|
|
26291
26735
|
detail: "DeepSeek Harness program path is not configured"
|
|
26292
26736
|
};
|
|
26293
26737
|
}
|
|
26738
|
+
const outcome = await probeDshModels({ recipe, host });
|
|
26739
|
+
if (outcome.kind !== "answered") {
|
|
26740
|
+
return {
|
|
26741
|
+
installed: outcome.kind === "failed",
|
|
26742
|
+
version: null,
|
|
26743
|
+
readiness: "error",
|
|
26744
|
+
auth: { status: "unknown" },
|
|
26745
|
+
...outcome.detail === void 0 ? {} : { detail: outcome.detail },
|
|
26746
|
+
// 装着但握不上手时界面照样画权限格,不报档就退回宿主四档。
|
|
26747
|
+
...outcome.kind === "failed" ? dshNativeModeReport(recipe.environment) : {}
|
|
26748
|
+
};
|
|
26749
|
+
}
|
|
26294
26750
|
return {
|
|
26295
26751
|
installed: true,
|
|
26296
26752
|
version: null,
|
|
26297
|
-
readiness: "
|
|
26298
|
-
auth: { status: "unknown" }
|
|
26753
|
+
readiness: "ready",
|
|
26754
|
+
auth: { status: "unknown" },
|
|
26755
|
+
...outcome.models === void 0 ? {} : { models: outcome.models },
|
|
26756
|
+
...dshNativeModeReport(recipe.environment)
|
|
26299
26757
|
};
|
|
26300
26758
|
},
|
|
26301
26759
|
startSession: async (input2, signal) => {
|
|
@@ -26312,7 +26770,12 @@ var createDshInstance = (config2, host) => {
|
|
|
26312
26770
|
threads.delete(input2.threadId);
|
|
26313
26771
|
}
|
|
26314
26772
|
const resolvedHomePath = resolveHomePath(input2.launch.homePath?.trim() || recipe.homePath);
|
|
26315
|
-
const env = buildSpawnEnv(
|
|
26773
|
+
const env = buildSpawnEnv(
|
|
26774
|
+
recipe,
|
|
26775
|
+
resolvedHomePath,
|
|
26776
|
+
input2.launch.processEnv,
|
|
26777
|
+
input2.nativeModeId
|
|
26778
|
+
);
|
|
26316
26779
|
const handle = await host.process.spawn({
|
|
26317
26780
|
program: recipe.binaryPath,
|
|
26318
26781
|
args: tokenizeLaunchArgs(recipe.launchArgs),
|
|
@@ -26320,7 +26783,7 @@ var createDshInstance = (config2, host) => {
|
|
|
26320
26783
|
owner: { bindingId: host.bindingId },
|
|
26321
26784
|
...Object.keys(env).length > 0 ? { env } : {}
|
|
26322
26785
|
});
|
|
26323
|
-
const { readable, writable } =
|
|
26786
|
+
const { readable, writable } = nodeStreamsFromHandle2(handle);
|
|
26324
26787
|
const resume = input2.discardResumeCursor === true ? void 0 : parseAcpResumeCursor(input2.resumeCursor, RESUME_SCHEMA_VERSION);
|
|
26325
26788
|
const requestedModelId = input2.launch.requestedModelId?.trim() || modelIdFromSelection(input2.modelSelection);
|
|
26326
26789
|
const executor = createAcpExecutor({
|
|
@@ -26330,7 +26793,8 @@ var createDshInstance = (config2, host) => {
|
|
|
26330
26793
|
threadId: input2.threadId,
|
|
26331
26794
|
provider: DSH_DRIVER_KIND,
|
|
26332
26795
|
providerInstanceId: host.instanceId,
|
|
26333
|
-
clientInfo:
|
|
26796
|
+
clientInfo: DSH_CLIENT_INFO,
|
|
26797
|
+
log: host.log,
|
|
26334
26798
|
// 该 agent 不实现 session/load,只实现 session/resume——续接重新激活
|
|
26335
26799
|
// 持久会话而不重放记录,发 load 会卡在永远不结算的重放门上。
|
|
26336
26800
|
resumeMethod: "session/resume",
|
|
@@ -26356,6 +26820,7 @@ var createDshInstance = (config2, host) => {
|
|
|
26356
26820
|
providerInstanceId: host.instanceId,
|
|
26357
26821
|
status: "idle",
|
|
26358
26822
|
runtimeMode: input2.runtimeMode,
|
|
26823
|
+
...input2.nativeModeId !== void 0 && isDshNativeModeId(input2.nativeModeId) ? { nativeModeId: input2.nativeModeId } : {},
|
|
26359
26824
|
cwd,
|
|
26360
26825
|
threadId: input2.threadId,
|
|
26361
26826
|
createdAt,
|
|
@@ -26458,6 +26923,7 @@ var createDshInstance = (config2, host) => {
|
|
|
26458
26923
|
return { threadId, turns: booked.turns };
|
|
26459
26924
|
},
|
|
26460
26925
|
events,
|
|
26926
|
+
continuationKey: dshContinuationKey(recipe.homePath),
|
|
26461
26927
|
dispose: async () => {
|
|
26462
26928
|
if (disposed) return;
|
|
26463
26929
|
disposed = true;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ycodium-ai/agent-dsh",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2130",
|
|
4
4
|
"description": "DeepSeek Harness executor: dsh CLI over Agent Client Protocol stdio.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ycodium",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"@types/node": "24.12.4",
|
|
21
21
|
"esbuild": "0.28.1",
|
|
22
22
|
"vite-plus": "0.2.2",
|
|
23
|
-
"@ycodium-ai/plugin-api": "0.2.
|
|
23
|
+
"@ycodium-ai/plugin-api": "0.2.2130",
|
|
24
24
|
"ycodium-acp-executor": "0.0.0",
|
|
25
25
|
"ycodium-acp-protocol": "0.0.0"
|
|
26
26
|
},
|