@ycodium-ai/agent-kimi 0.2.3608 → 0.2.3635

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -42,9 +42,10 @@ var KIMI_CARD = {
42
42
  verifiedSkills: false,
43
43
  everydayHarness: false,
44
44
  capabilities: {
45
- sessionModelSwitch: "unsupported",
46
- // 档位只在建会话时经 `session/set_config_option` 发一次:中途换档,宿主带 resume cursor 重开。
47
- sessionOptionSwitch: "unsupported",
45
+ // 型号与档位都经 `session/set_config_option` 在会话里切,执行器发送前写进去,不重开会话
46
+ // (2.3.3 实测,同一进程,下一轮发给模型的就是新型号、新档)。
47
+ sessionModelSwitch: "in-session",
48
+ sessionOptionSwitch: "in-session",
48
49
  // 与通用 ACP 同:`setRuntimeMode` 只改自己的会话记录,用户真正切得动的是
49
50
  // `nativeModeSwitch`。kimi `acp` 的 `session/set_mode plan` 实测回 `{}` 并推
50
51
  // `current_mode_update`,不存在的 modeId 被 `-32602 Unknown modeId` 拒掉。
@@ -183,6 +184,40 @@ var AcpExecutorTurnEndedError = class extends AcpExecutorError {
183
184
  this.name = "AcpExecutorTurnEndedError";
184
185
  }
185
186
  };
187
+ var AcpExecutorOptionRejectedError = class extends AcpExecutorError {
188
+ constructor(option, value, cause) {
189
+ const detail = acpErrorDetail(cause);
190
+ super(
191
+ "provider-rejected",
192
+ `The agent rejected the ${option}${value === void 0 ? "" : ` "${value}"`}${detail === void 0 ? "" : ` (${detail})`}. This turn was not sent.`
193
+ );
194
+ this.name = "AcpExecutorOptionRejectedError";
195
+ }
196
+ };
197
+ var AcpExecutorOptionTimeoutError = class extends AcpExecutorError {
198
+ constructor(option, value) {
199
+ super(
200
+ "provider-rejected",
201
+ `The agent did not answer the ${option} "${value}" change in time. This turn was not sent.`
202
+ );
203
+ this.name = "AcpExecutorOptionTimeoutError";
204
+ }
205
+ };
206
+ var AcpOptionWriteStoppedError = class extends Error {
207
+ constructor() {
208
+ super("option write stopped");
209
+ this.name = "AcpOptionWriteStoppedError";
210
+ }
211
+ };
212
+ function acpErrorDetail(cause) {
213
+ if (typeof cause !== "object" || cause === null) return void 0;
214
+ const message = cause.message;
215
+ const data = cause.data;
216
+ const parts = [message, data].filter(
217
+ (part) => typeof part === "string" && part.trim().length > 0
218
+ );
219
+ return parts.length === 0 ? void 0 : parts.join(": ");
220
+ }
186
221
  var AcpExecutorNotStartedError = class extends AcpExecutorError {
187
222
  constructor(message = "ACP session has not been started") {
188
223
  super("not-started", message);
@@ -296,10 +331,88 @@ var UnboundedEventStream = class {
296
331
  }
297
332
  };
298
333
 
299
- // ../../packages/ycodium-acp-executor/src/util.ts
334
+ // ../../packages/ycodium-acp-executor/src/config-options.ts
300
335
  function isRecord(value) {
301
336
  return typeof value === "object" && value !== null && !Array.isArray(value);
302
337
  }
338
+ function asString(value) {
339
+ return typeof value === "string" ? value : void 0;
340
+ }
341
+ function normalizedField(option, key) {
342
+ return asString(option[key])?.trim().toLowerCase() ?? "";
343
+ }
344
+ function looksLikeEffortOption(option) {
345
+ const id = normalizedField(option, "id");
346
+ const name = normalizedField(option, "name");
347
+ return id === "effort" || id === "reasoning" || name === "effort" || name === "reasoning" || name.includes("effort") || name.includes("reasoning") || normalizedField(option, "category") === "thought_level";
348
+ }
349
+ function flattenChoices(rawOptions, currentValue) {
350
+ if (!Array.isArray(rawOptions)) return [];
351
+ const out = [];
352
+ const seen = /* @__PURE__ */ new Set();
353
+ const push = (entry) => {
354
+ if (!isRecord(entry)) return;
355
+ const value = asString(entry["value"]);
356
+ if (value === void 0 || seen.has(value)) return;
357
+ seen.add(value);
358
+ const description = asString(entry["description"])?.trim();
359
+ const name = asString(entry["name"])?.trim();
360
+ out.push({
361
+ value,
362
+ ...name ? { name } : {},
363
+ label: name || value,
364
+ ...description ? { description } : {},
365
+ ...currentValue !== void 0 && currentValue === value ? { isDefault: true } : {}
366
+ });
367
+ };
368
+ for (const entry of rawOptions) {
369
+ if (isRecord(entry) && Array.isArray(entry["options"])) {
370
+ for (const nested of entry["options"]) push(nested);
371
+ continue;
372
+ }
373
+ push(entry);
374
+ }
375
+ return out;
376
+ }
377
+ function isConfigOption(entry, configId) {
378
+ return isRecord(entry) && asString(entry["id"])?.trim() === configId.trim();
379
+ }
380
+ function findConfigOptionEntry(configOptions, configId) {
381
+ return Array.isArray(configOptions) && configId.trim() !== "" ? configOptions.find(
382
+ (entry) => isConfigOption(entry, configId)
383
+ ) : void 0;
384
+ }
385
+ function withConfigOptionValue(configOptions, configId, value) {
386
+ if (!Array.isArray(configOptions)) return configOptions;
387
+ return configOptions.map(
388
+ (option) => isConfigOption(option, configId) ? { ...option, currentValue: value } : option
389
+ );
390
+ }
391
+ function pickEffortOption(configOptions) {
392
+ if (!Array.isArray(configOptions)) return void 0;
393
+ const candidates = configOptions.filter(isRecord).filter((option) => normalizedField(option, "type") === "select").filter(looksLikeEffortOption);
394
+ return 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];
395
+ }
396
+ function findAcpEffortConfig(configOptions) {
397
+ const picked = pickEffortOption(configOptions);
398
+ if (picked === void 0) return void 0;
399
+ const configId = asString(picked["id"])?.trim();
400
+ if (!configId) return void 0;
401
+ const currentValue = asString(picked["currentValue"])?.trim() || void 0;
402
+ const choices = flattenChoices(picked["options"], currentValue);
403
+ if (choices.length === 0) return void 0;
404
+ return {
405
+ configId,
406
+ label: asString(picked["name"])?.trim() ?? "",
407
+ choices,
408
+ ...currentValue === void 0 ? {} : { currentValue }
409
+ };
410
+ }
411
+
412
+ // ../../packages/ycodium-acp-executor/src/util.ts
413
+ function isRecord2(value) {
414
+ return typeof value === "object" && value !== null && !Array.isArray(value);
415
+ }
303
416
  function asTrimmedString(value) {
304
417
  if (typeof value !== "string") {
305
418
  return void 0;
@@ -352,11 +465,11 @@ function extractCommandFromTitle(title) {
352
465
  return backtickMatch?.[1]?.trim() || void 0;
353
466
  }
354
467
  function extractToolCommand(data, title) {
355
- const item = isRecord(data?.["item"]) ? data["item"] : void 0;
356
- const itemInput = item && isRecord(item["input"]) ? item["input"] : void 0;
357
- const itemResult = item && isRecord(item["result"]) ? item["result"] : void 0;
358
- const rawInput = isRecord(data?.["rawInput"]) ? data["rawInput"] : void 0;
359
- const dataInput = isRecord(data?.["input"]) ? data["input"] : void 0;
468
+ const item = isRecord2(data?.["item"]) ? data["item"] : void 0;
469
+ const itemInput = item && isRecord2(item["input"]) ? item["input"] : void 0;
470
+ const itemResult = item && isRecord2(item["result"]) ? item["result"] : void 0;
471
+ const rawInput = isRecord2(data?.["rawInput"]) ? data["rawInput"] : void 0;
472
+ const dataInput = isRecord2(data?.["input"]) ? data["input"] : void 0;
360
473
  const candidates = [
361
474
  normalizeCommandValue(item?.["command"]),
362
475
  normalizeCommandValue(itemInput?.["command"]),
@@ -401,7 +514,7 @@ function collectPaths(value, paths, seen, depth) {
401
514
  }
402
515
  return;
403
516
  }
404
- if (!isRecord(value)) {
517
+ if (!isRecord2(value)) {
405
518
  return;
406
519
  }
407
520
  for (const key of ["path", "filePath", "relativePath", "filename", "newPath", "oldPath"]) {
@@ -479,7 +592,7 @@ function deriveToolActivityPresentation(input2) {
479
592
  const title = asTrimmedString(input2.title);
480
593
  const detail = stripTrailingExitCode(asTrimmedString(input2.detail));
481
594
  const fallbackSummary = asTrimmedString(input2.fallbackSummary) ?? "Tool";
482
- const data = isRecord(input2.data) ? input2.data : void 0;
595
+ const data = isRecord2(input2.data) ? input2.data : void 0;
483
596
  const command = extractToolCommand(data, title);
484
597
  const primaryPath = extractPrimaryPath(data);
485
598
  const action = classifyToolAction({
@@ -511,7 +624,7 @@ function deriveToolActivityPresentation(input2) {
511
624
  };
512
625
  }
513
626
  if (action === "search") {
514
- const rawInput = isRecord(data?.["rawInput"]) ? data["rawInput"] : void 0;
627
+ const rawInput = isRecord2(data?.["rawInput"]) ? data["rawInput"] : void 0;
515
628
  const query = asTrimmedString(rawInput?.["query"]) ?? asTrimmedString(rawInput?.["pattern"]) ?? asTrimmedString(rawInput?.["searchTerm"]);
516
629
  return {
517
630
  summary: "Searched files",
@@ -571,7 +684,7 @@ function extractCommandFromTitle2(title) {
571
684
  return match?.[1]?.trim() || void 0;
572
685
  }
573
686
  function extractToolCallCommand(rawInput, title) {
574
- if (isRecord(rawInput)) {
687
+ if (isRecord2(rawInput)) {
575
688
  const directCommand = normalizeCommandValue2(rawInput["command"]);
576
689
  if (directCommand) {
577
690
  return directCommand;
@@ -599,7 +712,7 @@ function boundTailText(text, limit = TOOL_CALL_CONTENT_MAX_CHARS) {
599
712
  var boundToolCallOutputText = (text) => boundTailText(text);
600
713
  var RAW_OUTPUT_TEXT_FIELDS = ["content", "stdout", "stderr", "output"];
601
714
  function boundToolCallRawOutput(rawOutput) {
602
- if (!isRecord(rawOutput)) {
715
+ if (!isRecord2(rawOutput)) {
603
716
  return rawOutput;
604
717
  }
605
718
  let changed = false;
@@ -780,14 +893,7 @@ function extractModelConfigId(sessionResponse) {
780
893
  return configOptions.find((opt) => opt.id.trim() === "model" && opt.type === "select") ? "model" : void 0;
781
894
  }
782
895
  function findSessionConfigOption(configOptions, configId) {
783
- if (!configOptions) {
784
- return void 0;
785
- }
786
- const normalizedConfigId = configId.trim();
787
- if (!normalizedConfigId) {
788
- return void 0;
789
- }
790
- return configOptions.find((option) => option.id.trim() === normalizedConfigId);
896
+ return findConfigOptionEntry(configOptions, configId);
791
897
  }
792
898
  function collectSessionConfigOptionValues(configOption) {
793
899
  if (configOption.type !== "select" || !configOption.options) {
@@ -798,11 +904,11 @@ function collectSessionConfigOptionValues(configOption) {
798
904
  );
799
905
  }
800
906
  function collectSessionModelIds(models) {
801
- if (!isRecord(models) || !Array.isArray(models["availableModels"])) {
907
+ if (!isRecord2(models) || !Array.isArray(models["availableModels"])) {
802
908
  return [];
803
909
  }
804
910
  return models["availableModels"].flatMap(
805
- (model) => isRecord(model) && typeof model["modelId"] === "string" ? [model["modelId"]] : []
911
+ (model) => isRecord2(model) && typeof model["modelId"] === "string" ? [model["modelId"]] : []
806
912
  );
807
913
  }
808
914
  function catalogModelLastSegment(value) {
@@ -874,7 +980,7 @@ function parseAvailableCommandsUpdate(params) {
874
980
  }
875
981
  const commands = [];
876
982
  for (const command of availableCommands) {
877
- if (!isRecord(command)) {
983
+ if (!isRecord2(command)) {
878
984
  continue;
879
985
  }
880
986
  const name = typeof command["name"] === "string" ? command["name"].trim() : "";
@@ -882,7 +988,7 @@ function parseAvailableCommandsUpdate(params) {
882
988
  continue;
883
989
  }
884
990
  const description = typeof command["description"] === "string" ? command["description"].trim() || void 0 : void 0;
885
- const input2 = isRecord(command["input"]) ? command["input"] : void 0;
991
+ const input2 = isRecord2(command["input"]) ? command["input"] : void 0;
886
992
  const inputHint = typeof input2?.["hint"] === "string" ? input2["hint"].trim() || void 0 : void 0;
887
993
  commands.push({
888
994
  name,
@@ -936,7 +1042,7 @@ function boundToolCallRawPayload(params, update, toolCall) {
936
1042
  }
937
1043
  function textFromContentBlocks(blocks) {
938
1044
  const parts = blocks.flatMap(
939
- (block) => isRecord(block) && block["type"] === "text" && typeof block["text"] === "string" ? [block["text"]] : []
1045
+ (block) => isRecord2(block) && block["type"] === "text" && typeof block["text"] === "string" ? [block["text"]] : []
940
1046
  );
941
1047
  const joined = parts.join("");
942
1048
  return joined.trim().length > 0 ? joined : void 0;
@@ -961,7 +1067,7 @@ function parseSessionUpdateEvent(params) {
961
1067
  const entries = Array.isArray(upd["entries"]) ? upd["entries"] : [];
962
1068
  const plan = entries.map((entry, index) => ({
963
1069
  step: readAcpText(entry, "content") ?? `Step ${index + 1}`,
964
- status: normalizePlanStepStatus(isRecord(entry) ? entry["status"] : void 0)
1070
+ status: normalizePlanStepStatus(isRecord2(entry) ? entry["status"] : void 0)
965
1071
  }));
966
1072
  if (plan.length > 0) {
967
1073
  events.push({
@@ -1001,7 +1107,7 @@ function parseSessionUpdateEvent(params) {
1001
1107
  }
1002
1108
  case "agent_message_chunk":
1003
1109
  case "agent_thought_chunk": {
1004
- const content = isRecord(upd["content"]) ? upd["content"] : void 0;
1110
+ const content = isRecord2(upd["content"]) ? upd["content"] : void 0;
1005
1111
  const contentType = content?.["type"];
1006
1112
  const streamKind = kind === "agent_thought_chunk" ? "reasoning_text" : "assistant_text";
1007
1113
  if (contentType === "text") {
@@ -1041,7 +1147,7 @@ function parseSessionUpdateEvent(params) {
1041
1147
  }
1042
1148
  case "compaction_summary_chunk": {
1043
1149
  const compactionId = readAcpText(upd, "compactionId");
1044
- const content = isRecord(upd["content"]) ? upd["content"] : void 0;
1150
+ const content = isRecord2(upd["content"]) ? upd["content"] : void 0;
1045
1151
  const text = content?.["type"] === "text" ? content["text"] : void 0;
1046
1152
  if (compactionId === void 0 || typeof text !== "string" || text.length === 0) break;
1047
1153
  events.push({ _tag: "CompactionSummaryChunk", compactionId, text });
@@ -22735,17 +22841,17 @@ function isResponseMessage(value) {
22735
22841
  function isNotificationMessage(value) {
22736
22842
  return isJsonRpcEnvelope(value) && !("id" in value) && typeof value["method"] === "string";
22737
22843
  }
22738
- function isRecord2(value) {
22844
+ function isRecord3(value) {
22739
22845
  return typeof value === "object" && value !== null;
22740
22846
  }
22741
22847
  function isJsonRpcEnvelope(value) {
22742
- return isRecord2(value) && value["jsonrpc"] === "2.0";
22848
+ return isRecord3(value) && value["jsonrpc"] === "2.0";
22743
22849
  }
22744
22850
  function isJsonRpcId(value) {
22745
22851
  return value === null || typeof value === "string" || typeof value === "number" && Number.isFinite(value);
22746
22852
  }
22747
22853
  function isResponseShapedMessage(value) {
22748
- return isRecord2(value) && !("method" in value) && ("id" in value || "result" in value || "error" in value);
22854
+ return isRecord3(value) && !("method" in value) && ("id" in value || "result" in value || "error" in value);
22749
22855
  }
22750
22856
  function isResponseBatch(batch) {
22751
22857
  let hasValidCall = false;
@@ -22755,7 +22861,7 @@ function isResponseBatch(batch) {
22755
22861
  for (const entry of batch) {
22756
22862
  hasValidCall ||= isRequestMessage(entry) || isNotificationMessage(entry);
22757
22863
  hasValidResponse ||= isResponseMessage(entry);
22758
- if (!isRecord2(entry)) {
22864
+ if (!isRecord3(entry)) {
22759
22865
  continue;
22760
22866
  }
22761
22867
  hasCallShape ||= "method" in entry;
@@ -22770,13 +22876,13 @@ function isResponseBatch(batch) {
22770
22876
  return hasResponseShape && !hasCallShape;
22771
22877
  }
22772
22878
  function cancelRequestId(params) {
22773
- if (!isRecord2(params) || !isJsonRpcId(params["requestId"])) {
22879
+ if (!isRecord3(params) || !isJsonRpcId(params["requestId"])) {
22774
22880
  return void 0;
22775
22881
  }
22776
22882
  return params["requestId"];
22777
22883
  }
22778
22884
  function isErrorResponse(value) {
22779
- return isRecord2(value) && typeof value["code"] === "number" && Number.isInteger(value["code"]) && typeof value["message"] === "string";
22885
+ return isRecord3(value) && typeof value["code"] === "number" && Number.isInteger(value["code"]) && typeof value["message"] === "string";
22780
22886
  }
22781
22887
  var Handled = {
22782
22888
  /**
@@ -23321,7 +23427,7 @@ var Connection = class {
23321
23427
  if (this.abortController.signal.aborted) {
23322
23428
  return Promise.resolve();
23323
23429
  }
23324
- if (!isRecord2(message)) {
23430
+ if (!isRecord3(message)) {
23325
23431
  console.error("Invalid message", { message });
23326
23432
  return Promise.resolve();
23327
23433
  }
@@ -23724,7 +23830,7 @@ function ndJsonStream(output2, input2) {
23724
23830
  await writeJson(protocolErrorResponse(RequestError.parseError()));
23725
23831
  return;
23726
23832
  }
23727
- if (isRecord2(message) || Array.isArray(message)) {
23833
+ if (isRecord3(message) || Array.isArray(message)) {
23728
23834
  controller.enqueue(message);
23729
23835
  } else {
23730
23836
  await writeJson(protocolErrorResponse(RequestError.invalidRequest(message)));
@@ -24711,14 +24817,14 @@ var legacyClientNotificationMethods = /* @__PURE__ */ new Set([
24711
24817
  import * as NodeStream from "node:stream";
24712
24818
 
24713
24819
  // ../../packages/ycodium-acp-executor/src/jsonrpc.ts
24714
- function isRecord3(value) {
24820
+ function isRecord4(value) {
24715
24821
  return typeof value === "object" && value !== null;
24716
24822
  }
24717
24823
  function isJsonRpcId2(value) {
24718
24824
  return typeof value === "string" || typeof value === "number" && Number.isFinite(value);
24719
24825
  }
24720
24826
  function isJsonRpcEnvelope2(value) {
24721
- return isRecord3(value) && value["jsonrpc"] === "2.0";
24827
+ return isRecord4(value) && value["jsonrpc"] === "2.0";
24722
24828
  }
24723
24829
  function isRequestMessage2(value) {
24724
24830
  return isJsonRpcEnvelope2(value) && "id" in value && typeof value["method"] === "string" && isJsonRpcId2(value["id"]);
@@ -24738,7 +24844,7 @@ function isNotificationMessage2(value) {
24738
24844
  return isJsonRpcEnvelope2(value) && !("id" in value) && typeof value["method"] === "string";
24739
24845
  }
24740
24846
  function readJsonRpcError(value) {
24741
- if (!isRecord3(value) || typeof value["code"] !== "number" || typeof value["message"] !== "string") {
24847
+ if (!isRecord4(value) || typeof value["code"] !== "number" || typeof value["message"] !== "string") {
24742
24848
  return void 0;
24743
24849
  }
24744
24850
  return {
@@ -24811,11 +24917,11 @@ var KNOWN_SESSION_UPDATES = /* @__PURE__ */ new Set([
24811
24917
  "compaction_summary_chunk"
24812
24918
  ]);
24813
24919
  function isSessionUpdateEnvelope(payload) {
24814
- if (!isRecord3(payload) || typeof payload["sessionId"] !== "string") {
24920
+ if (!isRecord4(payload) || typeof payload["sessionId"] !== "string") {
24815
24921
  return false;
24816
24922
  }
24817
24923
  const update = payload["update"];
24818
- return isRecord3(update) && typeof update["sessionUpdate"] === "string";
24924
+ return isRecord4(update) && typeof update["sessionUpdate"] === "string";
24819
24925
  }
24820
24926
  function hasKnownVariantShape(update) {
24821
24927
  const kind = update["sessionUpdate"];
@@ -24823,7 +24929,7 @@ function hasKnownVariantShape(update) {
24823
24929
  return false;
24824
24930
  }
24825
24931
  if (kind === "user_message_chunk" || kind === "agent_message_chunk" || kind === "agent_thought_chunk") {
24826
- return isRecord3(update["content"]);
24932
+ return isRecord4(update["content"]);
24827
24933
  }
24828
24934
  return true;
24829
24935
  }
@@ -25012,6 +25118,10 @@ function createAcpConnection(options) {
25012
25118
  );
25013
25119
  } catch {
25014
25120
  }
25121
+ try {
25122
+ waiter.onArrival?.();
25123
+ } catch {
25124
+ }
25015
25125
  if (Object.hasOwn(message, "error")) {
25016
25126
  const rpcError = readJsonRpcError(message["error"]);
25017
25127
  waiter.reject(
@@ -25142,7 +25252,7 @@ function createAcpConnection(options) {
25142
25252
  options.readable.on("end", onEnd);
25143
25253
  options.readable.on("error", onError);
25144
25254
  options.writable.once("error", onWritableError);
25145
- const begin = (method, params, promptId) => {
25255
+ const begin = (method, params, promptId, onArrival) => {
25146
25256
  failIfClosed();
25147
25257
  const id = nextRequestId;
25148
25258
  nextRequestId += 1;
@@ -25150,6 +25260,7 @@ function createAcpConnection(options) {
25150
25260
  pending.set(String(id), {
25151
25261
  method,
25152
25262
  ...promptId === void 0 ? {} : { promptId },
25263
+ ...onArrival === void 0 ? {} : { onArrival },
25153
25264
  resolve: resolve3,
25154
25265
  reject
25155
25266
  });
@@ -25205,10 +25316,31 @@ function createAcpConnection(options) {
25205
25316
  function normalizeModelId(value) {
25206
25317
  return value?.trim() || void 0;
25207
25318
  }
25319
+ function adoptReportedConfigOptions(state, configOptions) {
25320
+ const option = state.modelConfigId === void 0 ? void 0 : findConfigOptionEntry(configOptions, state.modelConfigId);
25321
+ const value = option?.["currentValue"];
25322
+ const reported = typeof value === "string" ? normalizeModelId(value) : void 0;
25323
+ if (reported === void 0) return { ...state, configOptions };
25324
+ const { modelUnconfirmed: _confirmed, ...known } = state;
25325
+ return { ...known, configOptions, currentModelId: reported };
25326
+ }
25327
+ function forgetConfigOptionValue(state, configId) {
25328
+ const forgetsModel = configId === void 0 || configId === state.modelConfigId;
25329
+ const configOptions = configId !== void 0 && Array.isArray(state.configOptions) ? state.configOptions.map((option) => {
25330
+ if (!isRecord2(option) || option["id"] !== configId) return option;
25331
+ const { currentValue: _forgotten, ...rest } = option;
25332
+ return rest;
25333
+ }) : state.configOptions;
25334
+ return {
25335
+ ...state,
25336
+ configOptions,
25337
+ ...forgetsModel ? { currentModelId: void 0, modelUnconfirmed: true } : {}
25338
+ };
25339
+ }
25208
25340
  function modelSelectionStateFromSetup(setup) {
25209
- const sessionModels = isRecord(setup.models) ? setup.models : void 0;
25341
+ const sessionModels = isRecord2(setup.models) ? setup.models : void 0;
25210
25342
  const modelConfigId = extractModelConfigId(setup);
25211
- const strategy = sessionModels === void 0 && modelConfigId !== void 0 ? "config-option" : "session/set_model";
25343
+ const strategy = modelConfigId !== void 0 ? "config-option" : "session/set_model";
25212
25344
  const modelConfigOption = modelConfigId === void 0 ? void 0 : findSessionConfigOption(setup.configOptions, modelConfigId);
25213
25345
  const configModel = modelConfigOption?.currentValue;
25214
25346
  const currentModelId = strategy === "config-option" ? typeof configModel === "string" ? normalizeModelId(configModel) : void 0 : typeof sessionModels?.["currentModelId"] === "string" ? normalizeModelId(sessionModels["currentModelId"]) : void 0;
@@ -25218,7 +25350,8 @@ function modelSelectionStateFromSetup(setup) {
25218
25350
  modelConfigId,
25219
25351
  currentModelId,
25220
25352
  advertisedModelValues: strategy === "config-option" ? modelConfigOption === void 0 ? [] : collectSessionConfigOptionValues(modelConfigOption) : collectSessionModelIds(sessionModels),
25221
- configOptions: setup.configOptions
25353
+ configOptions: setup.configOptions,
25354
+ catalogSetup: setup
25222
25355
  };
25223
25356
  }
25224
25357
 
@@ -25316,6 +25449,10 @@ function createExecutorLifecycle({
25316
25449
  if (targetTurnId !== void 0) {
25317
25450
  interruptedTurnIds.add(targetTurnId);
25318
25451
  }
25452
+ const optionWrites = shared.optionWriteStop;
25453
+ if (optionWrites !== void 0 && (targetTurnId === void 0 || optionWrites.turnId === targetTurnId)) {
25454
+ optionWrites.stop();
25455
+ }
25319
25456
  settleAllApprovals("cancel");
25320
25457
  settleAllUserInputs();
25321
25458
  abortPendingSettlement("turn cancelled");
@@ -25528,6 +25665,41 @@ function isAcpRequestPermissionParams(value) {
25528
25665
  return Array.isArray(record2["options"]);
25529
25666
  }
25530
25667
 
25668
+ // ../../packages/ycodium-acp-executor/src/apply-effort.ts
25669
+ var alignRequestedEffort = async (input2) => {
25670
+ const unchanged = { configOptions: input2.configOptions };
25671
+ const effort = findAcpEffortConfig(input2.configOptions);
25672
+ if (effort === void 0) return unchanged;
25673
+ const values = new Set(effort.choices.map((choice) => choice.value));
25674
+ const current = effort.currentValue;
25675
+ const requested = input2.requestedEffort?.trim() || void 0;
25676
+ const unsupportedEffort = requested !== void 0 && !values.has(requested) ? requested : void 0;
25677
+ const staleCurrent = current !== void 0 && !values.has(current);
25678
+ const fallback = input2.modelDefaultEffort !== void 0 && values.has(input2.modelDefaultEffort) ? input2.modelDefaultEffort : void 0;
25679
+ const target = requested !== void 0 && unsupportedEffort === void 0 ? requested : unsupportedEffort !== void 0 || staleCurrent ? fallback : void 0;
25680
+ const written = target === void 0 || target === current ? void 0 : await input2.write(effort.configId, target);
25681
+ const configOptions = written === void 0 ? input2.configOptions : written.configOptions;
25682
+ if (unsupportedEffort === void 0) return { configOptions };
25683
+ const confirmed = fallback !== void 0 && written?.reported === true && findAcpEffortConfig(written.configOptions)?.currentValue === fallback;
25684
+ return { configOptions, unsupportedEffort, ...confirmed ? { appliedEffort: fallback } : {} };
25685
+ };
25686
+
25687
+ // ../../packages/ycodium-acp-executor/src/reasoning-level-fallback.ts
25688
+ var REASONING_LEVEL_FALLBACK_CODE = "reasoning-level-fallback";
25689
+ function reasoningLevelFallbackWarning(input2) {
25690
+ const model = input2.model ?? "This model";
25691
+ const outcome = input2.appliedEffort === void 0 ? "so the agent picks this turn's level" : `so this turn uses its default, "${input2.appliedEffort}"`;
25692
+ return {
25693
+ message: `${model} does not offer the "${input2.requestedEffort}" reasoning effort, ${outcome}. Your saved choice is unchanged.`,
25694
+ detail: {
25695
+ code: REASONING_LEVEL_FALLBACK_CODE,
25696
+ ...input2.model === void 0 ? {} : { model: input2.model },
25697
+ requestedEffort: input2.requestedEffort,
25698
+ ...input2.appliedEffort === void 0 ? {} : { appliedEffort: input2.appliedEffort }
25699
+ }
25700
+ };
25701
+ }
25702
+
25531
25703
  // ../../packages/ycodium-acp-executor/src/failureCategory.ts
25532
25704
  var ACP_FAILURE_CATEGORIES = [
25533
25705
  "quota",
@@ -25868,6 +26040,8 @@ function createExecutorPrompt({
25868
26040
  threadMutex,
25869
26041
  promptMutex,
25870
26042
  applyRequestedModel,
26043
+ modelDefaultEffort,
26044
+ writeConfigOptionReported,
25871
26045
  notifyPromptSettled,
25872
26046
  shared,
25873
26047
  promptResults,
@@ -25961,6 +26135,57 @@ function createExecutorPrompt({
25961
26135
  }
25962
26136
  }
25963
26137
  };
26138
+ const alignTurnOptions = async (input2, sessionId, generation, isStale, signal) => {
26139
+ const applied = await applyRequestedModel({
26140
+ state: shared.modelSelectionState,
26141
+ acpSessionId: sessionId,
26142
+ requestedModelId: input2.requestedModelId,
26143
+ expectedGeneration: generation,
26144
+ signal
26145
+ });
26146
+ shared.modelSelectionState = applied;
26147
+ if (isStale()) return;
26148
+ const switched = applied.configOptions;
26149
+ let aligned;
26150
+ try {
26151
+ aligned = await alignRequestedEffort({
26152
+ configOptions: switched,
26153
+ write: (configId, value) => writeConfigOptionReported(sessionId, switched, configId, value, {
26154
+ option: "reasoning level",
26155
+ signal
26156
+ }),
26157
+ requestedEffort: input2.requestedEffort,
26158
+ modelDefaultEffort: modelDefaultEffort(applied)
26159
+ });
26160
+ } catch (error62) {
26161
+ throw error62 instanceof RequestError ? new AcpExecutorOptionRejectedError(
26162
+ "reasoning level",
26163
+ input2.requestedEffort?.trim(),
26164
+ error62
26165
+ ) : error62;
26166
+ }
26167
+ if (!isLive(generation)) throw new AcpExecutorDisposedError();
26168
+ if (aligned.configOptions !== switched) {
26169
+ shared.modelSelectionState = adoptReportedConfigOptions(
26170
+ shared.modelSelectionState,
26171
+ aligned.configOptions
26172
+ );
26173
+ }
26174
+ if (aligned.unsupportedEffort !== void 0) {
26175
+ emit({
26176
+ type: "runtime.warning",
26177
+ ...stamp(),
26178
+ provider: options.provider,
26179
+ threadId: options.threadId,
26180
+ ...shared.activeTurnId === void 0 ? {} : { turnId: shared.activeTurnId },
26181
+ payload: reasoningLevelFallbackWarning({
26182
+ model: applied.currentModelId,
26183
+ requestedEffort: aligned.unsupportedEffort,
26184
+ appliedEffort: aligned.appliedEffort
26185
+ })
26186
+ });
26187
+ }
26188
+ };
25964
26189
  const failIfPromptUnavailable = () => {
25965
26190
  failIfDisposed();
25966
26191
  if (shared.connectionClosed) throw new AcpExecutorDisposedError();
@@ -25968,7 +26193,7 @@ function createExecutorPrompt({
25968
26193
  const prompt = async (input2) => {
25969
26194
  failIfPromptUnavailable();
25970
26195
  const started = requireStarted();
25971
- let sessionId = started.sessionId;
26196
+ const sessionId = started.sessionId;
25972
26197
  let record2;
25973
26198
  const settle2 = (rest) => settleTurn({
25974
26199
  expectedGeneration: started.generation,
@@ -26055,32 +26280,34 @@ function createExecutorPrompt({
26055
26280
  failIfDisposed();
26056
26281
  const queuedTurnId = turnId;
26057
26282
  const isStale = () => interruptedTurnIds.has(queuedTurnId) || input2.expectedActiveTurnId !== void 0 && shared.activeTurnId !== queuedTurnId;
26058
- if (!isStale()) {
26283
+ if (!isStale() && input2.expectedActiveTurnId === void 0) {
26284
+ const optionWrites = new AbortController();
26285
+ shared.optionWriteStop = { turnId, stop: () => optionWrites.abort() };
26059
26286
  try {
26060
- const applied = await applyRequestedModel({
26061
- state: shared.modelSelectionState,
26062
- acpSessionId: sessionId,
26063
- setup: shared.startResult?.sessionSetupResult ?? {},
26064
- requestedModelId: input2.requestedModelId,
26065
- expectedGeneration: started.generation
26066
- });
26067
- shared.modelSelectionState = applied.state;
26068
- sessionId = applied.sessionId;
26069
- record2.sessionId = applied.sessionId;
26287
+ await alignTurnOptions(
26288
+ input2,
26289
+ sessionId,
26290
+ started.generation,
26291
+ isStale,
26292
+ optionWrites.signal
26293
+ );
26070
26294
  } catch (error62) {
26071
- const failureCategory = readAcpFailureCategory(error62);
26072
- if (failureCategory === "model-unavailable") {
26073
- const errorMessage = error62 instanceof Error && error62.message.trim().length > 0 ? error62.message : "Requested model is not in this agent's catalog. This turn was not sent.";
26074
- settle2({
26075
- turnId,
26076
- stopReason: "error",
26077
- state: "failed",
26078
- errorMessage,
26079
- failureCategory
26080
- });
26081
- throw error62 instanceof Error ? withFailureCategory(error62, failureCategory) : error62;
26295
+ if (error62 instanceof AcpExecutorDisposedError) throw error62;
26296
+ if (isStale()) {
26297
+ settle2({ turnId, stopReason: "cancelled", state: "cancelled" });
26298
+ return { threadId: options.threadId, turnId, stopReason: "cancelled" };
26082
26299
  }
26083
- throw error62;
26300
+ const failureCategory = readAcpFailureCategory(error62);
26301
+ settle2({
26302
+ turnId,
26303
+ stopReason: "error",
26304
+ state: "failed",
26305
+ errorMessage: error62 instanceof Error && error62.message.trim() || "Switching the model or reasoning level failed. This turn was not sent.",
26306
+ ...failureCategory === void 0 ? {} : { failureCategory }
26307
+ });
26308
+ throw error62 instanceof Error ? withFailureCategory(error62, failureCategory) : error62;
26309
+ } finally {
26310
+ shared.optionWriteStop = void 0;
26084
26311
  }
26085
26312
  }
26086
26313
  failIfDisposed();
@@ -26463,7 +26690,7 @@ function createExecutorRequests({
26463
26690
  pendingUserInputs.delete(requestId);
26464
26691
  pending.resolve({
26465
26692
  kind: "answered",
26466
- answers: isRecord(answers) ? answers : {}
26693
+ answers: isRecord2(answers) ? answers : {}
26467
26694
  });
26468
26695
  };
26469
26696
  return {
@@ -26638,97 +26865,6 @@ function createExecutorRuntime({
26638
26865
  };
26639
26866
  }
26640
26867
 
26641
- // ../../packages/ycodium-acp-executor/src/config-options.ts
26642
- function isRecord4(value) {
26643
- return typeof value === "object" && value !== null && !Array.isArray(value);
26644
- }
26645
- function asString(value) {
26646
- return typeof value === "string" ? value : void 0;
26647
- }
26648
- function normalizedField(option, key) {
26649
- return asString(option[key])?.trim().toLowerCase() ?? "";
26650
- }
26651
- function looksLikeEffortOption(option) {
26652
- const id = normalizedField(option, "id");
26653
- const name = normalizedField(option, "name");
26654
- return id === "effort" || id === "reasoning" || name === "effort" || name === "reasoning" || name.includes("effort") || name.includes("reasoning") || normalizedField(option, "category") === "thought_level";
26655
- }
26656
- function flattenChoices(rawOptions, currentValue) {
26657
- if (!Array.isArray(rawOptions)) return [];
26658
- const out = [];
26659
- const seen = /* @__PURE__ */ new Set();
26660
- const push = (entry) => {
26661
- if (!isRecord4(entry)) return;
26662
- const value = asString(entry["value"]);
26663
- if (value === void 0 || seen.has(value)) return;
26664
- seen.add(value);
26665
- const description = asString(entry["description"])?.trim();
26666
- const name = asString(entry["name"])?.trim();
26667
- out.push({
26668
- value,
26669
- ...name ? { name } : {},
26670
- label: name || value,
26671
- ...description ? { description } : {},
26672
- ...currentValue !== void 0 && currentValue === value ? { isDefault: true } : {}
26673
- });
26674
- };
26675
- for (const entry of rawOptions) {
26676
- if (isRecord4(entry) && Array.isArray(entry["options"])) {
26677
- for (const nested of entry["options"]) push(nested);
26678
- continue;
26679
- }
26680
- push(entry);
26681
- }
26682
- return out;
26683
- }
26684
- function findAcpEffortConfig(configOptions) {
26685
- if (!Array.isArray(configOptions)) return void 0;
26686
- const candidates = configOptions.filter(isRecord4).filter((option) => normalizedField(option, "type") === "select").filter(looksLikeEffortOption);
26687
- if (candidates.length === 0) return void 0;
26688
- 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];
26689
- if (picked === void 0) return void 0;
26690
- const configId = asString(picked["id"])?.trim();
26691
- if (!configId) return void 0;
26692
- const choices = flattenChoices(picked["options"], asString(picked["currentValue"])?.trim());
26693
- if (choices.length === 0) return void 0;
26694
- return {
26695
- configId,
26696
- label: asString(picked["name"])?.trim() ?? "",
26697
- choices
26698
- };
26699
- }
26700
-
26701
- // ../../packages/ycodium-acp-executor/src/apply-effort.ts
26702
- var applyRequestedEffort = async (input2) => {
26703
- const requested = input2.requestedEffort?.trim();
26704
- if (!requested) return;
26705
- const effort = findAcpEffortConfig(input2.configOptions);
26706
- if (effort === void 0) return;
26707
- const matched = effort.choices.find((choice) => choice.value === requested);
26708
- if (matched === void 0) {
26709
- input2.log?.info("requested effort is not in the agent catalog; keeping its own", {
26710
- requestedEffort: requested,
26711
- advertised: effort.choices.map((choice) => choice.value)
26712
- });
26713
- return;
26714
- }
26715
- if (matched.isDefault === true) return;
26716
- if (!input2.isLive(input2.expectedGeneration)) return;
26717
- try {
26718
- await input2.connection.request("session/set_config_option", {
26719
- sessionId: input2.acpSessionId,
26720
- configId: effort.configId,
26721
- value: requested
26722
- });
26723
- } catch (error62) {
26724
- input2.log?.info("setting the effort level failed; the session keeps its own", {
26725
- requestedEffort: requested,
26726
- configId: effort.configId,
26727
- error: error62 instanceof Error ? error62.message : String(error62)
26728
- });
26729
- }
26730
- };
26731
-
26732
26868
  // ../../packages/ycodium-acp-executor/src/config-option-wiring.ts
26733
26869
  var VENDOR_CATEGORY_PREFIX = "_";
26734
26870
  var ACP_CONFIG_CATEGORY_HANDLING = {
@@ -26742,7 +26878,7 @@ var ACP_CONFIG_CATEGORY_HANDLING = {
26742
26878
  },
26743
26879
  thought_level: {
26744
26880
  consumed: true,
26745
- via: "config-options.ts:findAcpEffortConfig \u2192 apply-effort.ts:applyRequestedEffort"
26881
+ via: "config-options.ts:findAcpEffortConfig \u2192 apply-effort.ts:alignRequestedEffort"
26746
26882
  },
26747
26883
  model_option: {
26748
26884
  consumed: true,
@@ -26811,13 +26947,14 @@ function judgeRequestedModel(input2) {
26811
26947
  if (requested === void 0 || requested === current) return { kind: "keep" };
26812
26948
  const hasCatalog = input2.state.advertisedModelValues.length > 0;
26813
26949
  const hasCurrent = current !== void 0;
26814
- if (!hasCatalog && !hasCurrent) return { kind: "keep" };
26815
- const canSwitch = input2.state.strategy === "session/set_model" && input2.state.sessionModelsAdvertised || input2.state.strategy === "config-option" && input2.state.modelConfigId !== void 0;
26950
+ if (!hasCatalog && !hasCurrent && input2.state.modelUnconfirmed !== true) return { kind: "keep" };
26951
+ const canSwitch = canSwitchModel(input2.state);
26816
26952
  const value = hasCatalog ? matchAdvertisedModelValue(requested, input2.state.advertisedModelValues) : void 0;
26817
26953
  if (value !== void 0 && value === current) return { kind: "keep" };
26818
26954
  if (value !== void 0 && canSwitch) return { kind: "switch", value };
26819
- return input2.refreshed ? { kind: "unavailable" } : { kind: "refresh" };
26955
+ return { kind: "refresh" };
26820
26956
  }
26957
+ var canSwitchModel = (state) => state.strategy === "session/set_model" && state.sessionModelsAdvertised || state.strategy === "config-option" && state.modelConfigId !== void 0;
26821
26958
  function modelUnavailableError(requestedModelId, currentModelId) {
26822
26959
  const current = currentModelId ?? "its own model";
26823
26960
  return withFailureCategory(
@@ -26892,6 +27029,7 @@ function acpCurrentModeId(setup) {
26892
27029
  // ../../packages/ycodium-acp-executor/src/executor/session.ts
26893
27030
  function createExecutorSession({
26894
27031
  options,
27032
+ hooks,
26895
27033
  connection,
26896
27034
  isLive,
26897
27035
  emit,
@@ -26913,131 +27051,140 @@ function createExecutorSession({
26913
27051
  shared.sessionOpensInFlight -= 1;
26914
27052
  }
26915
27053
  };
26916
- const openFreshSession = async (expectedGeneration) => {
26917
- const initialized = shared.initializeResult;
26918
- if (initialized === void 0) {
26919
- throw new Error("no handshake");
27054
+ const optionDeadlineMs = () => options.modelCatalogRefreshTimeoutMs ?? MODEL_CATALOG_REFRESH_TIMEOUT_MS;
27055
+ const within = async (work, signal, onTimeout) => {
27056
+ void work.catch(() => void 0);
27057
+ let timer;
27058
+ let onStop;
27059
+ const bound = new Promise((_resolve, reject) => {
27060
+ timer = setTimeout(() => reject(onTimeout()), optionDeadlineMs());
27061
+ onStop = () => reject(new AcpOptionWriteStoppedError());
27062
+ if (signal?.aborted === true) onStop();
27063
+ else signal?.addEventListener("abort", onStop, { once: true });
27064
+ });
27065
+ try {
27066
+ return await Promise.race([work, bound]);
27067
+ } finally {
27068
+ clearTimeout(timer);
27069
+ if (onStop !== void 0) signal?.removeEventListener("abort", onStop);
26920
27070
  }
26921
- const mcpServers = filterMcpServersByAgentCapabilities(
26922
- options.mcpServers ?? [],
26923
- initialized.agentCapabilities
27071
+ };
27072
+ const awaitOptionReply = async (pending, configId, write) => {
27073
+ void pending.response.catch(() => void 0);
27074
+ try {
27075
+ return await within(
27076
+ pending.written.then(() => pending.response),
27077
+ write.signal,
27078
+ () => new AcpExecutorOptionTimeoutError(write.option, write.value)
27079
+ );
27080
+ } catch (error62) {
27081
+ if (error62 instanceof AcpExecutorOptionTimeoutError || error62 instanceof AcpOptionWriteStoppedError) {
27082
+ shared.modelSelectionState = forgetConfigOptionValue(shared.modelSelectionState, configId);
27083
+ }
27084
+ throw error62;
27085
+ }
27086
+ };
27087
+ const writeConfigOptionReported = async (sessionId, configOptions, configId, value, write) => {
27088
+ const issuedAt = shared.configUpdateSeq;
27089
+ let arrivedAt = Number.POSITIVE_INFINITY;
27090
+ const pending = connection.begin(
27091
+ "session/set_config_option",
27092
+ { sessionId, configId, value },
27093
+ void 0,
27094
+ () => {
27095
+ arrivedAt = shared.configUpdateSeq;
27096
+ }
26924
27097
  );
26925
- const additionalDirectories = options.additionalDirectories !== void 0 && options.additionalDirectories.length > 0 ? { additionalDirectories: options.additionalDirectories } : {};
26926
- const created = await requestSession("session/new", {
26927
- cwd: options.cwd,
26928
- mcpServers,
26929
- ...additionalDirectories
26930
- });
26931
- if (!isLive(expectedGeneration)) {
26932
- throw new AcpExecutorDisposedError();
27098
+ const reply = await awaitOptionReply(pending, configId, { ...write, value });
27099
+ await connection.awaitInboundIdle();
27100
+ const ours = shared.configOptionUpdates.get(sessionId);
27101
+ if (ours !== void 0 && ours.seq > arrivedAt) {
27102
+ return { configOptions: ours.configOptions, reported: true };
26933
27103
  }
26934
- if (created.sessionId === void 0 || created.sessionId.length === 0) {
26935
- throw new Error("session/new did not return a session id");
27104
+ if (reply?.configOptions !== void 0) {
27105
+ return { configOptions: reply.configOptions, reported: true };
26936
27106
  }
26937
- return { sessionId: created.sessionId, setup: created };
27107
+ const base = ours !== void 0 && ours.seq > issuedAt ? ours.configOptions : configOptions;
27108
+ return { configOptions: withConfigOptionValue(base, configId, value), reported: false };
26938
27109
  };
27110
+ const writeConfigOption = async (...args) => (await writeConfigOptionReported(...args)).configOptions;
26939
27111
  const switchModel = async (input2) => {
26940
27112
  let configOptions = input2.state.configOptions;
26941
- if (input2.state.strategy === "config-option") {
26942
- const switched = await connection.request(
26943
- "session/set_config_option",
26944
- { sessionId: input2.sessionId, configId: input2.state.modelConfigId, value: input2.value }
27113
+ const write = { option: "model", signal: input2.signal };
27114
+ if (input2.state.strategy === "config-option" && input2.state.modelConfigId !== void 0) {
27115
+ configOptions = await writeConfigOption(
27116
+ input2.sessionId,
27117
+ configOptions,
27118
+ input2.state.modelConfigId,
27119
+ input2.value,
27120
+ write
26945
27121
  );
26946
- configOptions = switched?.configOptions ?? configOptions;
26947
27122
  } else {
26948
- await connection.request("session/set_model", {
27123
+ const before = shared.configOptionUpdates.get(input2.sessionId);
27124
+ const pending = connection.begin("session/set_model", {
26949
27125
  sessionId: input2.sessionId,
26950
27126
  modelId: input2.value
26951
27127
  });
27128
+ await awaitOptionReply(pending, void 0, { ...write, value: input2.value });
27129
+ await connection.awaitInboundIdle();
27130
+ const reported = shared.configOptionUpdates.get(input2.sessionId);
27131
+ if (reported !== void 0 && reported !== before) {
27132
+ configOptions = reported.configOptions;
27133
+ }
26952
27134
  }
26953
27135
  if (!isLive(input2.expectedGeneration)) {
26954
27136
  throw new AcpExecutorDisposedError();
26955
27137
  }
26956
- return { ...input2.state, currentModelId: input2.value, configOptions };
26957
- };
26958
- const adoptReopenedSession = (applied) => {
26959
- if (shared.sessionId === void 0 && shared.startResult === void 0) return;
26960
- const setup = applied.setup;
26961
- shared.sessionId = applied.sessionId;
26962
- shared.modelSelectionState = applied.state;
26963
- shared.modeState = parseSessionModeState(setup);
26964
- if (shared.startResult !== void 0) {
26965
- shared.startResult = {
26966
- ...shared.startResult,
26967
- sessionId: applied.sessionId,
26968
- sessionSetupResult: setup,
26969
- modelConfigId: extractModelConfigId(setup),
26970
- modelId: applied.state.currentModelId
26971
- };
26972
- }
27138
+ const { modelUnconfirmed: _confirmed, ...known } = input2.state;
27139
+ return adoptReportedConfigOptions({ ...known, currentModelId: input2.value }, configOptions);
26973
27140
  };
26974
27141
  const applyRequestedModel = async (input2) => {
26975
- async function applyOnce(state, sessionId, setup, refreshed) {
26976
- const requestedModelId = normalizeModelId(input2.requestedModelId) ?? "";
27142
+ const requestedModelId = normalizeModelId(input2.requestedModelId) ?? "";
27143
+ const switchTo = (value) => switchModel({
27144
+ state: input2.state,
27145
+ sessionId: input2.acpSessionId,
27146
+ value,
27147
+ expectedGeneration: input2.expectedGeneration,
27148
+ signal: input2.signal
27149
+ });
27150
+ try {
26977
27151
  const judgment = judgeRequestedModel({
26978
- state,
26979
- requestedModelId: input2.requestedModelId,
26980
- refreshed
27152
+ state: input2.state,
27153
+ requestedModelId: input2.requestedModelId
26981
27154
  });
26982
- if (judgment.kind === "keep") return { state, sessionId, setup };
26983
- if (judgment.kind === "unavailable") {
26984
- throw modelUnavailableError(requestedModelId, state.currentModelId);
26985
- }
26986
- if (judgment.kind === "switch") {
26987
- return {
26988
- state: await switchModel({
26989
- state,
26990
- sessionId,
26991
- value: judgment.value,
26992
- expectedGeneration: input2.expectedGeneration
26993
- }),
26994
- sessionId,
26995
- setup
26996
- };
26997
- }
26998
- return refreshAndRejudge(requestedModelId);
26999
- }
27000
- async function refreshAndRejudge(requestedModelId) {
27001
- const timeoutMs = options.modelCatalogRefreshTimeoutMs ?? MODEL_CATALOG_REFRESH_TIMEOUT_MS;
27002
- let timedOut = false;
27003
- const timeoutError = new ModelCatalogRefreshTimeoutError();
27004
- let timer;
27005
- const work = (async () => {
27006
- await options.refreshCatalogForSend?.();
27007
- if (timedOut) throw timeoutError;
27008
- const reopened = await openFreshSession(input2.expectedGeneration);
27009
- if (timedOut) throw timeoutError;
27010
- const next = modelSelectionStateFromSetup(reopened.setup);
27011
- const applied = await applyOnce(next, reopened.sessionId, reopened.setup, true);
27012
- if (timedOut) throw timeoutError;
27013
- adoptReopenedSession(applied);
27014
- return applied;
27015
- })();
27016
- void work.then(
27017
- () => void 0,
27018
- () => void 0
27155
+ if (judgment.kind === "keep") return input2.state;
27156
+ if (judgment.kind === "switch") return await switchTo(judgment.value);
27157
+ await refreshCatalog(requestedModelId, input2.signal);
27158
+ if (!isLive(input2.expectedGeneration)) throw new AcpExecutorDisposedError();
27159
+ if (!canSwitchModel(input2.state)) {
27160
+ throw modelUnavailableError(requestedModelId, input2.state.currentModelId);
27161
+ }
27162
+ const switched = await switchTo(requestedModelId);
27163
+ return switched.advertisedModelValues.includes(requestedModelId) ? switched : {
27164
+ ...switched,
27165
+ advertisedModelValues: [...switched.advertisedModelValues, requestedModelId]
27166
+ };
27167
+ } catch (error62) {
27168
+ throw error62 instanceof RequestError ? withFailureCategory(
27169
+ new AcpExecutorOptionRejectedError("model", requestedModelId, error62),
27170
+ "model-unavailable"
27171
+ ) : error62;
27172
+ }
27173
+ };
27174
+ const refreshCatalog = async (requestedModelId, signal) => {
27175
+ try {
27176
+ await within(
27177
+ Promise.resolve(options.refreshCatalogForSend?.()),
27178
+ signal,
27179
+ () => new ModelCatalogRefreshTimeoutError()
27019
27180
  );
27020
- const deadline = new Promise((_resolve, reject) => {
27021
- timer = setTimeout(() => {
27022
- timedOut = true;
27023
- connection.abandonPending(timeoutError);
27024
- reject(timeoutError);
27025
- }, timeoutMs);
27026
- });
27027
- try {
27028
- return await Promise.race([work, deadline]);
27029
- } catch (error62) {
27030
- if (error62 instanceof AcpExecutorDisposedError) throw error62;
27031
- if (timedOut || error62 instanceof ModelCatalogRefreshTimeoutError) {
27032
- throw modelRefreshTimedOutError(requestedModelId);
27033
- }
27034
- if (readAcpFailureCategory(error62) === "model-unavailable") throw error62;
27035
- throw modelRefreshFailedError(requestedModelId, error62);
27036
- } finally {
27037
- if (timer !== void 0) clearTimeout(timer);
27181
+ } catch (error62) {
27182
+ if (error62 instanceof AcpOptionWriteStoppedError) throw error62;
27183
+ if (error62 instanceof ModelCatalogRefreshTimeoutError) {
27184
+ throw modelRefreshTimedOutError(requestedModelId);
27038
27185
  }
27186
+ throw modelRefreshFailedError(requestedModelId, error62);
27039
27187
  }
27040
- return applyOnce(input2.state, input2.acpSessionId, input2.setup, false);
27041
27188
  };
27042
27189
  const startOnce = async (expectedGeneration) => {
27043
27190
  const initializePayload = acpInitializePayload(
@@ -27112,26 +27259,45 @@ function createExecutorSession({
27112
27259
  categories: unhandledCategories
27113
27260
  });
27114
27261
  }
27115
- const openedSetup = setup;
27116
- const appliedModel = await applyRequestedModel({
27117
- state: modelSelectionStateFromSetup(openedSetup),
27262
+ let nextModelSelectionState = await applyRequestedModel({
27263
+ state: modelSelectionStateFromSetup(setup),
27118
27264
  acpSessionId: nextSessionId,
27119
- setup: openedSetup,
27120
27265
  requestedModelId: options.requestedModelId,
27121
27266
  expectedGeneration
27122
27267
  });
27123
- nextSessionId = appliedModel.sessionId;
27124
- setup = appliedModel.setup;
27125
- const nextModelSelectionState = appliedModel.state;
27126
- await applyRequestedEffort({
27127
- connection,
27128
- log: options.log,
27129
- isLive,
27130
- configOptions: nextModelSelectionState.configOptions,
27131
- acpSessionId: nextSessionId,
27132
- requestedEffort: options.requestedEffort,
27133
- expectedGeneration
27134
- });
27268
+ if (!isLive(expectedGeneration)) {
27269
+ throw new AcpExecutorDisposedError();
27270
+ }
27271
+ try {
27272
+ const opened = nextModelSelectionState.configOptions;
27273
+ const aligned = await alignRequestedEffort({
27274
+ configOptions: opened,
27275
+ write: (configId, value) => writeConfigOptionReported(nextSessionId, opened, configId, value, {
27276
+ option: "reasoning level"
27277
+ }),
27278
+ requestedEffort: options.requestedEffort,
27279
+ modelDefaultEffort: modelDefaultEffort(nextModelSelectionState)
27280
+ });
27281
+ nextModelSelectionState = adoptReportedConfigOptions(
27282
+ nextModelSelectionState,
27283
+ aligned.configOptions
27284
+ );
27285
+ if (aligned.unsupportedEffort !== void 0) {
27286
+ options.log?.info("requested effort is not offered by this model; using its default", {
27287
+ requestedEffort: aligned.unsupportedEffort,
27288
+ modelId: nextModelSelectionState.currentModelId
27289
+ });
27290
+ }
27291
+ } catch (error62) {
27292
+ const effortConfigId = findAcpEffortConfig(nextModelSelectionState.configOptions)?.configId;
27293
+ if (error62 instanceof AcpExecutorOptionTimeoutError && effortConfigId !== void 0) {
27294
+ nextModelSelectionState = forgetConfigOptionValue(nextModelSelectionState, effortConfigId);
27295
+ }
27296
+ options.log?.info("setting the effort level failed; the session keeps its own", {
27297
+ requestedEffort: options.requestedEffort,
27298
+ error: error62 instanceof Error ? error62.message : String(error62)
27299
+ });
27300
+ }
27135
27301
  if (options.beforeApplyResult) {
27136
27302
  await options.beforeApplyResult();
27137
27303
  }
@@ -27191,6 +27357,7 @@ function createExecutorSession({
27191
27357
  );
27192
27358
  return shared.startInFlight;
27193
27359
  };
27360
+ const modelDefaultEffort = (state) => state.currentModelId === void 0 ? void 0 : hooks.modelDefaultEffort?.({ setup: state.catalogSetup, modelId: state.currentModelId });
27194
27361
  const requireStarted = () => {
27195
27362
  failIfDisposed();
27196
27363
  if (!shared.sessionId || !shared.startResult) {
@@ -27218,7 +27385,14 @@ function createExecutorSession({
27218
27385
  modeId
27219
27386
  });
27220
27387
  };
27221
- return { applyRequestedModel, start, requireStarted, setNativeMode };
27388
+ return {
27389
+ applyRequestedModel,
27390
+ modelDefaultEffort,
27391
+ writeConfigOptionReported,
27392
+ start,
27393
+ requireStarted,
27394
+ setNativeMode
27395
+ };
27222
27396
  }
27223
27397
 
27224
27398
  // ../../packages/ycodium-acp-executor/src/executor/settlement.ts
@@ -27282,14 +27456,14 @@ function createExecutorSettlement({
27282
27456
 
27283
27457
  // ../../packages/ycodium-acp-executor/src/executor/subagent-sessions.ts
27284
27458
  function parseState(raw) {
27285
- if (!isRecord(raw)) return { status: void 0, error: void 0 };
27459
+ if (!isRecord2(raw)) return { status: void 0, error: void 0 };
27286
27460
  const state = raw["state"];
27287
27461
  if (state === "running") return { status: "running", error: void 0 };
27288
27462
  if (state === "requires_action") return { status: "waiting", error: void 0 };
27289
27463
  if (state !== "idle") return { status: void 0, error: void 0 };
27290
27464
  const stopReason = raw["stopReason"];
27291
- const error62 = isRecord(raw["error"]) ? asTrimmedString(raw["error"]["message"]) : void 0;
27292
- if (stopReason === "error" || isRecord(raw["error"])) return { status: "failed", error: error62 };
27465
+ const error62 = isRecord2(raw["error"]) ? asTrimmedString(raw["error"]["message"]) : void 0;
27466
+ if (stopReason === "error" || isRecord2(raw["error"])) return { status: "failed", error: error62 };
27293
27467
  if (stopReason === "cancelled") return { status: "cancelled", error: void 0 };
27294
27468
  return { status: "idle", error: void 0 };
27295
27469
  }
@@ -27305,7 +27479,7 @@ function childLinkage(childId, record2) {
27305
27479
  function createSubagentSessionTracker(registry2) {
27306
27480
  const observe = (notification, ctx) => {
27307
27481
  const update = notification.update;
27308
- if (!isRecord(update) || update["sessionUpdate"] !== "subagent_update") return [];
27482
+ if (!isRecord2(update) || update["sessionUpdate"] !== "subagent_update") return [];
27309
27483
  const childId = asTrimmedString(update["sessionId"]);
27310
27484
  if (childId === void 0 || ctx.ownSessionIds.has(childId)) return [];
27311
27485
  const outerId = notification.sessionId;
@@ -27531,6 +27705,21 @@ function createExecutorUpdates({
27531
27705
  markChildSession
27532
27706
  });
27533
27707
  const handleSessionUpdate = (params) => {
27708
+ const update = params.update;
27709
+ const reportedOptions = update["configOptions"];
27710
+ if (update["sessionUpdate"] === "config_option_update" && Array.isArray(reportedOptions)) {
27711
+ shared.configUpdateSeq += 1;
27712
+ shared.configOptionUpdates.set(params.sessionId, {
27713
+ configOptions: reportedOptions,
27714
+ seq: shared.configUpdateSeq
27715
+ });
27716
+ if (params.sessionId === shared.sessionId) {
27717
+ shared.modelSelectionState = adoptReportedConfigOptions(
27718
+ shared.modelSelectionState,
27719
+ reportedOptions
27720
+ );
27721
+ }
27722
+ }
27534
27723
  const parsed = parseSessionUpdateEvent(params);
27535
27724
  if (parsed.modeId && shared.modeState) {
27536
27725
  shared.modeState = {
@@ -27846,7 +28035,10 @@ function createAcpExecutor(options) {
27846
28035
  droppedReplayUpdates: 0,
27847
28036
  ownSessionIds: /* @__PURE__ */ new Set(),
27848
28037
  childSessions: createChildSessionRegistry(),
27849
- sessionOpensInFlight: 0
28038
+ sessionOpensInFlight: 0,
28039
+ configOptionUpdates: /* @__PURE__ */ new Map(),
28040
+ optionWriteStop: void 0,
28041
+ configUpdateSeq: 0
27850
28042
  };
27851
28043
  const interruptedTurnIds = /* @__PURE__ */ new Set();
27852
28044
  const approvals = createApprovalGate({
@@ -27995,8 +28187,16 @@ function createAcpExecutor(options) {
27995
28187
  };
27996
28188
  options.readable.once("end", onInputDead);
27997
28189
  options.readable.once("error", onInputDead);
27998
- const { applyRequestedModel, start, requireStarted, setNativeMode } = createExecutorSession({
28190
+ const {
28191
+ applyRequestedModel,
28192
+ modelDefaultEffort,
28193
+ writeConfigOptionReported,
28194
+ start,
28195
+ requireStarted,
28196
+ setNativeMode
28197
+ } = createExecutorSession({
27999
28198
  options,
28199
+ hooks,
28000
28200
  connection,
28001
28201
  isLive,
28002
28202
  emit,
@@ -28022,6 +28222,8 @@ function createAcpExecutor(options) {
28022
28222
  threadMutex,
28023
28223
  promptMutex,
28024
28224
  applyRequestedModel,
28225
+ modelDefaultEffort,
28226
+ writeConfigOptionReported,
28025
28227
  notifyPromptSettled,
28026
28228
  shared
28027
28229
  });
@@ -28943,12 +29145,13 @@ function positiveInt2(value) {
28943
29145
  }
28944
29146
  function kimiEffortDescriptor(entry) {
28945
29147
  const supported = entry["supportEfforts"];
28946
- if (!Array.isArray(supported)) return void 0;
29148
+ if (!Array.isArray(supported)) return "unknown";
28947
29149
  const values = supported.flatMap((value) => {
28948
29150
  const effort = asString4(value);
28949
29151
  return effort === void 0 ? [] : [effort];
28950
29152
  });
28951
- if (values.length === 0) return void 0;
29153
+ if (values.length !== supported.length) return "unknown";
29154
+ if (values.length === 0) return "none";
28952
29155
  const defaultEffort = asString4(entry["defaultEffort"]);
28953
29156
  return {
28954
29157
  id: ACP_EFFORT_OPTION_ID,
@@ -28983,7 +29186,7 @@ function kimiProbeModels(stdout) {
28983
29186
  slug: trimmed2,
28984
29187
  ...name === void 0 ? {} : { name },
28985
29188
  ...contextWindow === void 0 ? {} : { contextWindow },
28986
- ...effort === void 0 ? {} : { capabilities: { optionDescriptors: [effort] } }
29189
+ ...effort === "unknown" ? {} : { capabilities: { optionDescriptors: effort === "none" ? [] : [effort] } }
28987
29190
  });
28988
29191
  }
28989
29192
  return out;
@@ -29569,10 +29772,15 @@ var createKimiInstance = (config2, host) => {
29569
29772
  signal?.addEventListener("abort", onAbort);
29570
29773
  try {
29571
29774
  const requestedModelId = modelIdFromSelection(input2.modelSelection);
29775
+ const requestedEffort = modelSelectionStringOption(
29776
+ input2.modelSelection,
29777
+ ACP_EFFORT_OPTION_ID
29778
+ );
29572
29779
  const turn = await dispatchAcpPrompt(booked.executor, {
29573
29780
  text: input2.input ?? "",
29574
29781
  expectedActiveTurnId: input2.expectedTurnId,
29575
- ...requestedModelId === void 0 ? {} : { requestedModelId }
29782
+ ...requestedModelId === void 0 ? {} : { requestedModelId },
29783
+ ...requestedEffort === void 0 ? {} : { requestedEffort }
29576
29784
  });
29577
29785
  booked.turns.push({ id: turn.turnId, items: [] });
29578
29786
  return {
@@ -29672,7 +29880,7 @@ var executors = [
29672
29880
  ];
29673
29881
  var index_default = definePlugin({
29674
29882
  id: "agent-kimi",
29675
- apiVersion: 49,
29883
+ apiVersion: 50,
29676
29884
  executors,
29677
29885
  activate(ctx) {
29678
29886
  ctx.log.info("Kimi Code executor ready");
package/locales/en.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "form": {
3
3
  "binaryPath": {
4
4
  "title": "Program path",
5
- "description": "Path to the Kimi Code program this provider runs.",
5
+ "description": "Must be an absolute path.",
6
6
  "placeholder": "kimi"
7
7
  },
8
8
  "homePath": {
@@ -2,7 +2,7 @@
2
2
  "form": {
3
3
  "binaryPath": {
4
4
  "title": "程序路径",
5
- "description": "此 provider 运行的 Kimi Code 程序的路径。",
5
+ "description": "必须是绝对路径。",
6
6
  "placeholder": "kimi"
7
7
  },
8
8
  "homePath": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ycodium-ai/agent-kimi",
3
- "version": "0.2.3608",
3
+ "version": "0.2.3635",
4
4
  "description": "Kimi Code executor: runs `kimi acp` over Agent Client Protocol stdio.",
5
5
  "keywords": [
6
6
  "ycodium",
@@ -21,9 +21,9 @@
21
21
  "@types/node": "24.12.4",
22
22
  "esbuild": "0.28.1",
23
23
  "vite-plus": "0.2.2",
24
- "ycodium-acp-executor": "0.2.3608",
25
- "@ycodium-ai/plugin-api": "0.2.3608",
26
- "ycodium-executor-testing": "0.0.0"
24
+ "@ycodium-ai/plugin-api": "0.2.3635",
25
+ "ycodium-executor-testing": "0.0.0",
26
+ "ycodium-acp-executor": "0.2.3635"
27
27
  },
28
28
  "ycodium": {
29
29
  "plugin": "./dist/index.js",