@ycodium-ai/agent-dsh 0.2.2106 → 0.2.2126

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +581 -120
  2. 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 * as NodeOS from "node:os";
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 isRecord3(value) {
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 = isRecord3(data?.["item"]) ? data["item"] : void 0;
24441
- const itemInput = item && isRecord3(item["input"]) ? item["input"] : void 0;
24442
- const itemResult = item && isRecord3(item["result"]) ? item["result"] : void 0;
24443
- const rawInput = isRecord3(data?.["rawInput"]) ? data["rawInput"] : void 0;
24444
- const dataInput = isRecord3(data?.["input"]) ? data["input"] : void 0;
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 (!isRecord3(value)) {
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 = isRecord3(input2.data) ? input2.data : void 0;
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 = isRecord3(data?.["rawInput"]) ? data["rawInput"] : void 0;
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 (!isRecord3(command)) {
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 = isRecord3(command["input"]) ? command["input"] : void 0;
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 (isRecord3(rawInput)) {
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 (!isRecord3(rawOutput)) {
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(isRecord3(entry) ? entry["status"] : void 0)
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 = isRecord3(upd["content"]) ? upd["content"] : void 0;
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 = isRecord3(setup.models) ? setup.models : void 0;
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 configModel = modelConfigId === void 0 ? void 0 : findSessionConfigOption(setup.configOptions, modelConfigId)?.currentValue;
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: requestedModelId
25779
+ value
25540
25780
  });
25541
25781
  } else {
25542
25782
  await connection.request("session/set_model", {
25543
25783
  sessionId: input2.acpSessionId,
25544
- modelId: requestedModelId
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: requestedModelId };
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: isRecord3(answers) ? 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 isRecord4(value) {
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 (!isRecord4(entry)) continue;
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 (!isRecord4(config2)) {
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 = isRecord4(config2["config"]) ? config2["config"] : void 0;
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,188 @@ function tokenizeLaunchArgs(value) {
26141
26478
  return tokens;
26142
26479
  }
26143
26480
 
26144
- // src/streams.ts
26145
- import * as NodeStream2 from "node:stream";
26146
- function nodeStreamsFromHandle(handle) {
26147
- const readable = new NodeStream2.PassThrough();
26148
- void (async () => {
26149
- try {
26150
- for await (const chunk of handle.stdout) {
26151
- if (!readable.writable) return;
26152
- if (!readable.write(chunk)) {
26153
- await new Promise((resolve2) => {
26154
- const onDone = () => {
26155
- readable.off("drain", onDone);
26156
- readable.off("close", onDone);
26157
- resolve2();
26158
- };
26159
- readable.once("drain", onDone);
26160
- readable.once("close", onDone);
26161
- });
26162
- }
26163
- }
26164
- } catch (error62) {
26165
- readable.destroy(error62 instanceof Error ? error62 : new Error(String(error62)));
26166
- return;
26167
- }
26168
- if (readable.writable) readable.end();
26169
- })();
26170
- void (async () => {
26171
- for await (const _chunk of handle.stderr) {
26172
- }
26173
- })().catch(() => void 0);
26174
- const writable = new NodeStream2.Writable({
26175
- write(chunk, _encoding, callback) {
26176
- const bytes = typeof chunk === "string" ? Buffer.from(chunk) : chunk;
26177
- handle.writeStdin(bytes).then(
26178
- () => callback(),
26179
- (error62) => callback(error62 instanceof Error ? error62 : new Error(String(error62)))
26180
- );
26181
- },
26182
- final(callback) {
26183
- handle.closeStdin().then(
26184
- () => callback(),
26185
- (error62) => callback(error62 instanceof Error ? error62 : new Error(String(error62)))
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 buildSpawnEnv(recipe, resolvedHomePath, launchEnv = {}, nativeModeId) {
26544
+ const homeEnvVar = recipe.homeEnvVar.trim() || DSH_CARD.homeEnvVar;
26545
+ const homeInjection = resolvedHomePath.length > 0 && homeEnvVar.length > 0 ? { [homeEnvVar]: resolvedHomePath } : {};
26546
+ return mergeLaunchEnv({
26547
+ brainSlots: brainSlotEnvKeys(DSH_CARD),
26548
+ accountHome: homeInjection,
26549
+ instanceTable: recipe.environment,
26550
+ // 用户挑的档压过实例表:实例表里那一栏是这台机器的默认,输入区那一下是这一轮的
26551
+ // 意思。铺在 `handOverrides` 这一层正是为了这个先后(见 `mergeLaunchEnv` 的层序)。
26552
+ handOverrides: dshPermissionEnv(nativeModeId),
26553
+ launch: launchEnv
26188
26554
  });
26189
- return { readable, writable };
26555
+ }
26556
+
26557
+ // src/probeModels.ts
26558
+ var PROBE_REQUEST_TIMEOUT_MS = 15e3;
26559
+ function dshProbeSessionCwd(instanceId) {
26560
+ const safe = instanceId.trim().replace(/[^A-Za-z0-9._-]/g, "-") || "instance";
26561
+ return NodePath2.join(NodeOS2.tmpdir(), `ycodium-dsh-probe-${safe}`);
26562
+ }
26563
+ function isRecord7(value) {
26564
+ return typeof value === "object" && value !== null && !Array.isArray(value);
26565
+ }
26566
+ function asString3(value) {
26567
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
26568
+ }
26569
+ function dshProbeModels(setup) {
26570
+ if (!isRecord7(setup)) return void 0;
26571
+ const rawOptions = setup["configOptions"];
26572
+ const choices = acpModelChoicesFromConfigOptions(rawOptions);
26573
+ if (choices === void 0) return void 0;
26574
+ const models = choices.map((choice) => ({
26575
+ slug: choice.value,
26576
+ ...choice.name === void 0 ? {} : { name: choice.name },
26577
+ ...choice.isDefault === true ? { isDefault: true } : {}
26578
+ }));
26579
+ const effort = findAcpEffortConfig(rawOptions);
26580
+ if (effort === void 0) return models;
26581
+ const capabilities = {
26582
+ optionDescriptors: [
26583
+ {
26584
+ id: DSH_EFFORT_OPTION_ID,
26585
+ label: effort.label || "Reasoning",
26586
+ type: "select",
26587
+ options: effort.choices.map((choice) => ({
26588
+ id: choice.value,
26589
+ label: choice.label,
26590
+ ...choice.description === void 0 ? {} : { description: choice.description },
26591
+ ...choice.isDefault === true ? { isDefault: true } : {}
26592
+ }))
26593
+ }
26594
+ ]
26595
+ };
26596
+ return models.map((model) => ({ ...model, capabilities }));
26597
+ }
26598
+ var DSH_EFFORT_OPTION_ID = "reasoning";
26599
+ function findProbeSessionId(listed, probeCwd) {
26600
+ if (!isRecord7(listed) || !Array.isArray(listed["sessions"])) return void 0;
26601
+ for (const entry of listed["sessions"]) {
26602
+ if (!isRecord7(entry)) continue;
26603
+ if (asString3(entry["cwd"]) !== probeCwd) continue;
26604
+ const sessionId = asString3(entry["sessionId"]);
26605
+ if (sessionId !== void 0) return sessionId;
26606
+ }
26607
+ return void 0;
26608
+ }
26609
+ async function probeSessionSetup(handshake, probeCwd) {
26610
+ const listed = await handshake.request("session/list", {}).catch(() => void 0);
26611
+ const reusable = findProbeSessionId(listed, probeCwd);
26612
+ if (reusable !== void 0) {
26613
+ const resumed = await handshake.request("session/resume", { sessionId: reusable, cwd: probeCwd, mcpServers: [] }).catch(() => void 0);
26614
+ if (resumed !== void 0) return resumed;
26615
+ }
26616
+ return handshake.request("session/new", { cwd: probeCwd, mcpServers: [] });
26617
+ }
26618
+ var failureDetail = (error62) => (error62 instanceof Error ? error62.message : String(error62)).trim() || void 0;
26619
+ async function probeDshModels(input2) {
26620
+ const { recipe, host } = input2;
26621
+ const probeCwd = dshProbeSessionCwd(host.instanceId);
26622
+ try {
26623
+ NodeFS.mkdirSync(probeCwd, { recursive: true });
26624
+ } catch (error62) {
26625
+ return { kind: "failed", detail: failureDetail(error62) };
26626
+ }
26627
+ const env = buildSpawnEnv(recipe, resolveHomePath(recipe.homePath));
26628
+ let handle;
26629
+ try {
26630
+ handle = await host.process.spawn({
26631
+ program: recipe.binaryPath,
26632
+ args: tokenizeLaunchArgs(recipe.launchArgs),
26633
+ cwd: probeCwd,
26634
+ owner: { bindingId: host.bindingId },
26635
+ ...Object.keys(env).length > 0 ? { env } : {}
26636
+ });
26637
+ } catch (error62) {
26638
+ return { kind: "not-started", detail: failureDetail(error62) };
26639
+ }
26640
+ let handshake;
26641
+ try {
26642
+ const { readable, writable } = nodeStreamsFromHandle(handle);
26643
+ handshake = await openAcpProbeHandshake({
26644
+ readable,
26645
+ writable,
26646
+ clientInfo: DSH_CLIENT_INFO,
26647
+ requestTimeoutMs: PROBE_REQUEST_TIMEOUT_MS
26648
+ });
26649
+ return {
26650
+ kind: "answered",
26651
+ models: dshProbeModels(await probeSessionSetup(handshake, probeCwd))
26652
+ };
26653
+ } catch (error62) {
26654
+ return { kind: "failed", detail: failureDetail(error62) };
26655
+ } finally {
26656
+ await handshake?.close().catch(() => void 0);
26657
+ await handle.kill("SIGTERM").catch(() => void 0);
26658
+ }
26190
26659
  }
26191
26660
 
26192
26661
  // src/dshInstance.ts
26193
26662
  var RESUME_SCHEMA_VERSION = 1;
26194
- var CLIENT_INFO = { name: "ycodium-agent-dsh", version: "0.1.0" };
26195
26663
  var yieldTick = () => new Promise((resolve2) => {
26196
26664
  setImmediate(resolve2);
26197
26665
  });
@@ -26227,25 +26695,6 @@ var EventBus = class {
26227
26695
  };
26228
26696
  }
26229
26697
  };
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
26698
  function modelIdFromSelection(selection) {
26250
26699
  if (typeof selection !== "object" || selection === null || Array.isArray(selection)) {
26251
26700
  return void 0;
@@ -26253,15 +26702,6 @@ function modelIdFromSelection(selection) {
26253
26702
  const model = selection["model"];
26254
26703
  return typeof model === "string" ? model.trim() || void 0 : void 0;
26255
26704
  }
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
26705
  function nowIso() {
26266
26706
  return (/* @__PURE__ */ new Date()).toISOString();
26267
26707
  }
@@ -26291,11 +26731,25 @@ var createDshInstance = (config2, host) => {
26291
26731
  detail: "DeepSeek Harness program path is not configured"
26292
26732
  };
26293
26733
  }
26734
+ const outcome = await probeDshModels({ recipe, host });
26735
+ if (outcome.kind !== "answered") {
26736
+ return {
26737
+ installed: outcome.kind === "failed",
26738
+ version: null,
26739
+ readiness: "error",
26740
+ auth: { status: "unknown" },
26741
+ ...outcome.detail === void 0 ? {} : { detail: outcome.detail },
26742
+ // 装着但握不上手时界面照样画权限格,不报档就退回宿主四档。
26743
+ ...outcome.kind === "failed" ? dshNativeModeReport(recipe.environment) : {}
26744
+ };
26745
+ }
26294
26746
  return {
26295
26747
  installed: true,
26296
26748
  version: null,
26297
- readiness: "unchecked",
26298
- auth: { status: "unknown" }
26749
+ readiness: "ready",
26750
+ auth: { status: "unknown" },
26751
+ ...outcome.models === void 0 ? {} : { models: outcome.models },
26752
+ ...dshNativeModeReport(recipe.environment)
26299
26753
  };
26300
26754
  },
26301
26755
  startSession: async (input2, signal) => {
@@ -26312,7 +26766,12 @@ var createDshInstance = (config2, host) => {
26312
26766
  threads.delete(input2.threadId);
26313
26767
  }
26314
26768
  const resolvedHomePath = resolveHomePath(input2.launch.homePath?.trim() || recipe.homePath);
26315
- const env = buildSpawnEnv(recipe, input2.launch, resolvedHomePath);
26769
+ const env = buildSpawnEnv(
26770
+ recipe,
26771
+ resolvedHomePath,
26772
+ input2.launch.processEnv,
26773
+ input2.nativeModeId
26774
+ );
26316
26775
  const handle = await host.process.spawn({
26317
26776
  program: recipe.binaryPath,
26318
26777
  args: tokenizeLaunchArgs(recipe.launchArgs),
@@ -26320,7 +26779,7 @@ var createDshInstance = (config2, host) => {
26320
26779
  owner: { bindingId: host.bindingId },
26321
26780
  ...Object.keys(env).length > 0 ? { env } : {}
26322
26781
  });
26323
- const { readable, writable } = nodeStreamsFromHandle(handle);
26782
+ const { readable, writable } = nodeStreamsFromHandle2(handle);
26324
26783
  const resume = input2.discardResumeCursor === true ? void 0 : parseAcpResumeCursor(input2.resumeCursor, RESUME_SCHEMA_VERSION);
26325
26784
  const requestedModelId = input2.launch.requestedModelId?.trim() || modelIdFromSelection(input2.modelSelection);
26326
26785
  const executor = createAcpExecutor({
@@ -26330,7 +26789,8 @@ var createDshInstance = (config2, host) => {
26330
26789
  threadId: input2.threadId,
26331
26790
  provider: DSH_DRIVER_KIND,
26332
26791
  providerInstanceId: host.instanceId,
26333
- clientInfo: CLIENT_INFO,
26792
+ clientInfo: DSH_CLIENT_INFO,
26793
+ log: host.log,
26334
26794
  // 该 agent 不实现 session/load,只实现 session/resume——续接重新激活
26335
26795
  // 持久会话而不重放记录,发 load 会卡在永远不结算的重放门上。
26336
26796
  resumeMethod: "session/resume",
@@ -26356,6 +26816,7 @@ var createDshInstance = (config2, host) => {
26356
26816
  providerInstanceId: host.instanceId,
26357
26817
  status: "idle",
26358
26818
  runtimeMode: input2.runtimeMode,
26819
+ ...input2.nativeModeId !== void 0 && isDshNativeModeId(input2.nativeModeId) ? { nativeModeId: input2.nativeModeId } : {},
26359
26820
  cwd,
26360
26821
  threadId: input2.threadId,
26361
26822
  createdAt,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ycodium-ai/agent-dsh",
3
- "version": "0.2.2106",
3
+ "version": "0.2.2126",
4
4
  "description": "DeepSeek Harness executor: dsh CLI over Agent Client Protocol stdio.",
5
5
  "keywords": [
6
6
  "ycodium",
@@ -20,8 +20,8 @@
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.2106",
24
23
  "ycodium-acp-executor": "0.0.0",
24
+ "@ycodium-ai/plugin-api": "0.2.2126",
25
25
  "ycodium-acp-protocol": "0.0.0"
26
26
  },
27
27
  "ycodium": {