@ycodium-ai/agent-grok 0.2.3614 → 0.2.3671

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 +532 -298
  2. package/package.json +4 -4
package/dist/index.js CHANGED
@@ -54,6 +54,8 @@ var GROK_CARD = {
54
54
  defaultModel: "grok-4.7",
55
55
  customModelIdExample: "",
56
56
  modelSlugAliases: null,
57
+ // 目录外的型号被 `session/set_model` 拒(`-32602 unknown model id`)。
58
+ modelCatalogAuthoritative: true,
57
59
  mcpTransports: ["stdio", "http", "sse"],
58
60
  pluginManaged: true,
59
61
  // `--plugin-dir` 加载会话技能目录:grok 1.0.46 真实登录态下 session/new 之后的命令清单多出
@@ -61,10 +63,10 @@ var GROK_CARD = {
61
63
  verifiedSkills: true,
62
64
  everydayHarness: false,
63
65
  capabilities: {
64
- // 插件不承诺原生会话内活切型号。
65
- sessionModelSwitch: "unsupported",
66
- // 思考档位只在 spawn 时定(`--reasoning-effort`):中途换档,宿主带 resume cursor 重开会话。
67
- sessionOptionSwitch: "unsupported",
66
+ // 型号与思考档都在会话里切,不重开(重开会把进程里的子 agent 一起关掉):执行器发送前经
67
+ // `model` 与 `reasoning_effort` 两个配置项写进去(1.0.70 实测,同一进程,下一轮生效)。
68
+ sessionModelSwitch: "in-session",
69
+ sessionOptionSwitch: "in-session",
68
70
  // 运行模式只在 spawn 时定得了(`--permission-mode` 是启动旗标,ACP 那一侧
69
71
  // `modes: null`,`session/set_mode` 对它无效),所以不许声称能会话中途换。
70
72
  // 报 next-turn 之后宿主 `ProviderCommandReactor` 不再下发,而是把新档记在
@@ -247,6 +249,40 @@ var AcpExecutorTurnEndedError = class extends AcpExecutorError {
247
249
  this.name = "AcpExecutorTurnEndedError";
248
250
  }
249
251
  };
252
+ var AcpExecutorOptionRejectedError = class extends AcpExecutorError {
253
+ constructor(option, value, cause) {
254
+ const detail = acpErrorDetail(cause);
255
+ super(
256
+ "provider-rejected",
257
+ `The agent rejected the ${option}${value === void 0 ? "" : ` "${value}"`}${detail === void 0 ? "" : ` (${detail})`}. This turn was not sent.`
258
+ );
259
+ this.name = "AcpExecutorOptionRejectedError";
260
+ }
261
+ };
262
+ var AcpExecutorOptionTimeoutError = class extends AcpExecutorError {
263
+ constructor(option, value) {
264
+ super(
265
+ "provider-rejected",
266
+ `The agent did not answer the ${option} "${value}" change in time. This turn was not sent.`
267
+ );
268
+ this.name = "AcpExecutorOptionTimeoutError";
269
+ }
270
+ };
271
+ var AcpOptionWriteStoppedError = class extends Error {
272
+ constructor() {
273
+ super("option write stopped");
274
+ this.name = "AcpOptionWriteStoppedError";
275
+ }
276
+ };
277
+ function acpErrorDetail(cause) {
278
+ if (typeof cause !== "object" || cause === null) return void 0;
279
+ const message = cause.message;
280
+ const data = cause.data;
281
+ const parts = [message, data].filter(
282
+ (part) => typeof part === "string" && part.trim().length > 0
283
+ );
284
+ return parts.length === 0 ? void 0 : parts.join(": ");
285
+ }
250
286
  var AcpExecutorNotStartedError = class extends AcpExecutorError {
251
287
  constructor(message = "ACP session has not been started") {
252
288
  super("not-started", message);
@@ -360,10 +396,88 @@ var UnboundedEventStream = class {
360
396
  }
361
397
  };
362
398
 
363
- // ../../packages/ycodium-acp-executor/src/util.ts
399
+ // ../../packages/ycodium-acp-executor/src/config-options.ts
364
400
  function isRecord(value) {
365
401
  return typeof value === "object" && value !== null && !Array.isArray(value);
366
402
  }
403
+ function asString(value) {
404
+ return typeof value === "string" ? value : void 0;
405
+ }
406
+ function normalizedField(option, key) {
407
+ return asString(option[key])?.trim().toLowerCase() ?? "";
408
+ }
409
+ function looksLikeEffortOption(option) {
410
+ const id = normalizedField(option, "id");
411
+ const name = normalizedField(option, "name");
412
+ return id === "effort" || id === "reasoning" || name === "effort" || name === "reasoning" || name.includes("effort") || name.includes("reasoning") || normalizedField(option, "category") === "thought_level";
413
+ }
414
+ function flattenChoices(rawOptions, currentValue) {
415
+ if (!Array.isArray(rawOptions)) return [];
416
+ const out = [];
417
+ const seen = /* @__PURE__ */ new Set();
418
+ const push = (entry) => {
419
+ if (!isRecord(entry)) return;
420
+ const value = asString(entry["value"]);
421
+ if (value === void 0 || seen.has(value)) return;
422
+ seen.add(value);
423
+ const description = asString(entry["description"])?.trim();
424
+ const name = asString(entry["name"])?.trim();
425
+ out.push({
426
+ value,
427
+ ...name ? { name } : {},
428
+ label: name || value,
429
+ ...description ? { description } : {},
430
+ ...currentValue !== void 0 && currentValue === value ? { isDefault: true } : {}
431
+ });
432
+ };
433
+ for (const entry of rawOptions) {
434
+ if (isRecord(entry) && Array.isArray(entry["options"])) {
435
+ for (const nested of entry["options"]) push(nested);
436
+ continue;
437
+ }
438
+ push(entry);
439
+ }
440
+ return out;
441
+ }
442
+ function isConfigOption(entry, configId) {
443
+ return isRecord(entry) && asString(entry["id"])?.trim() === configId.trim();
444
+ }
445
+ function findConfigOptionEntry(configOptions, configId) {
446
+ return Array.isArray(configOptions) && configId.trim() !== "" ? configOptions.find(
447
+ (entry) => isConfigOption(entry, configId)
448
+ ) : void 0;
449
+ }
450
+ function withConfigOptionValue(configOptions, configId, value) {
451
+ if (!Array.isArray(configOptions)) return configOptions;
452
+ return configOptions.map(
453
+ (option) => isConfigOption(option, configId) ? { ...option, currentValue: value } : option
454
+ );
455
+ }
456
+ function pickEffortOption(configOptions) {
457
+ if (!Array.isArray(configOptions)) return void 0;
458
+ const candidates = configOptions.filter(isRecord).filter((option) => normalizedField(option, "type") === "select").filter(looksLikeEffortOption);
459
+ 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];
460
+ }
461
+ function findAcpEffortConfig(configOptions) {
462
+ const picked = pickEffortOption(configOptions);
463
+ if (picked === void 0) return void 0;
464
+ const configId = asString(picked["id"])?.trim();
465
+ if (!configId) return void 0;
466
+ const currentValue = asString(picked["currentValue"])?.trim() || void 0;
467
+ const choices = flattenChoices(picked["options"], currentValue);
468
+ if (choices.length === 0) return void 0;
469
+ return {
470
+ configId,
471
+ label: asString(picked["name"])?.trim() ?? "",
472
+ choices,
473
+ ...currentValue === void 0 ? {} : { currentValue }
474
+ };
475
+ }
476
+
477
+ // ../../packages/ycodium-acp-executor/src/util.ts
478
+ function isRecord2(value) {
479
+ return typeof value === "object" && value !== null && !Array.isArray(value);
480
+ }
367
481
  function asTrimmedString(value) {
368
482
  if (typeof value !== "string") {
369
483
  return void 0;
@@ -416,11 +530,11 @@ function extractCommandFromTitle(title) {
416
530
  return backtickMatch?.[1]?.trim() || void 0;
417
531
  }
418
532
  function extractToolCommand(data, title) {
419
- const item = isRecord(data?.["item"]) ? data["item"] : void 0;
420
- const itemInput = item && isRecord(item["input"]) ? item["input"] : void 0;
421
- const itemResult = item && isRecord(item["result"]) ? item["result"] : void 0;
422
- const rawInput = isRecord(data?.["rawInput"]) ? data["rawInput"] : void 0;
423
- const dataInput = isRecord(data?.["input"]) ? data["input"] : void 0;
533
+ const item = isRecord2(data?.["item"]) ? data["item"] : void 0;
534
+ const itemInput = item && isRecord2(item["input"]) ? item["input"] : void 0;
535
+ const itemResult = item && isRecord2(item["result"]) ? item["result"] : void 0;
536
+ const rawInput = isRecord2(data?.["rawInput"]) ? data["rawInput"] : void 0;
537
+ const dataInput = isRecord2(data?.["input"]) ? data["input"] : void 0;
424
538
  const candidates = [
425
539
  normalizeCommandValue(item?.["command"]),
426
540
  normalizeCommandValue(itemInput?.["command"]),
@@ -465,7 +579,7 @@ function collectPaths(value, paths, seen, depth) {
465
579
  }
466
580
  return;
467
581
  }
468
- if (!isRecord(value)) {
582
+ if (!isRecord2(value)) {
469
583
  return;
470
584
  }
471
585
  for (const key of ["path", "filePath", "relativePath", "filename", "newPath", "oldPath"]) {
@@ -543,7 +657,7 @@ function deriveToolActivityPresentation(input2) {
543
657
  const title = asTrimmedString(input2.title);
544
658
  const detail = stripTrailingExitCode(asTrimmedString(input2.detail));
545
659
  const fallbackSummary = asTrimmedString(input2.fallbackSummary) ?? "Tool";
546
- const data = isRecord(input2.data) ? input2.data : void 0;
660
+ const data = isRecord2(input2.data) ? input2.data : void 0;
547
661
  const command = extractToolCommand(data, title);
548
662
  const primaryPath = extractPrimaryPath(data);
549
663
  const action = classifyToolAction({
@@ -575,7 +689,7 @@ function deriveToolActivityPresentation(input2) {
575
689
  };
576
690
  }
577
691
  if (action === "search") {
578
- const rawInput = isRecord(data?.["rawInput"]) ? data["rawInput"] : void 0;
692
+ const rawInput = isRecord2(data?.["rawInput"]) ? data["rawInput"] : void 0;
579
693
  const query = asTrimmedString(rawInput?.["query"]) ?? asTrimmedString(rawInput?.["pattern"]) ?? asTrimmedString(rawInput?.["searchTerm"]);
580
694
  return {
581
695
  summary: "Searched files",
@@ -635,7 +749,7 @@ function extractCommandFromTitle2(title) {
635
749
  return match?.[1]?.trim() || void 0;
636
750
  }
637
751
  function extractToolCallCommand(rawInput, title) {
638
- if (isRecord(rawInput)) {
752
+ if (isRecord2(rawInput)) {
639
753
  const directCommand = normalizeCommandValue2(rawInput["command"]);
640
754
  if (directCommand) {
641
755
  return directCommand;
@@ -663,7 +777,7 @@ function boundTailText(text4, limit = TOOL_CALL_CONTENT_MAX_CHARS) {
663
777
  var boundToolCallOutputText = (text4) => boundTailText(text4);
664
778
  var RAW_OUTPUT_TEXT_FIELDS = ["content", "stdout", "stderr", "output"];
665
779
  function boundToolCallRawOutput(rawOutput) {
666
- if (!isRecord(rawOutput)) {
780
+ if (!isRecord2(rawOutput)) {
667
781
  return rawOutput;
668
782
  }
669
783
  let changed = false;
@@ -844,14 +958,7 @@ function extractModelConfigId(sessionResponse) {
844
958
  return configOptions.find((opt) => opt.id.trim() === "model" && opt.type === "select") ? "model" : void 0;
845
959
  }
846
960
  function findSessionConfigOption(configOptions, configId) {
847
- if (!configOptions) {
848
- return void 0;
849
- }
850
- const normalizedConfigId = configId.trim();
851
- if (!normalizedConfigId) {
852
- return void 0;
853
- }
854
- return configOptions.find((option) => option.id.trim() === normalizedConfigId);
961
+ return findConfigOptionEntry(configOptions, configId);
855
962
  }
856
963
  function collectSessionConfigOptionValues(configOption) {
857
964
  if (configOption.type !== "select" || !configOption.options) {
@@ -862,11 +969,11 @@ function collectSessionConfigOptionValues(configOption) {
862
969
  );
863
970
  }
864
971
  function collectSessionModelIds(models) {
865
- if (!isRecord(models) || !Array.isArray(models["availableModels"])) {
972
+ if (!isRecord2(models) || !Array.isArray(models["availableModels"])) {
866
973
  return [];
867
974
  }
868
975
  return models["availableModels"].flatMap(
869
- (model) => isRecord(model) && typeof model["modelId"] === "string" ? [model["modelId"]] : []
976
+ (model) => isRecord2(model) && typeof model["modelId"] === "string" ? [model["modelId"]] : []
870
977
  );
871
978
  }
872
979
  function catalogModelLastSegment(value) {
@@ -938,7 +1045,7 @@ function parseAvailableCommandsUpdate(params) {
938
1045
  }
939
1046
  const commands = [];
940
1047
  for (const command of availableCommands) {
941
- if (!isRecord(command)) {
1048
+ if (!isRecord2(command)) {
942
1049
  continue;
943
1050
  }
944
1051
  const name = typeof command["name"] === "string" ? command["name"].trim() : "";
@@ -946,7 +1053,7 @@ function parseAvailableCommandsUpdate(params) {
946
1053
  continue;
947
1054
  }
948
1055
  const description = typeof command["description"] === "string" ? command["description"].trim() || void 0 : void 0;
949
- const input2 = isRecord(command["input"]) ? command["input"] : void 0;
1056
+ const input2 = isRecord2(command["input"]) ? command["input"] : void 0;
950
1057
  const inputHint = typeof input2?.["hint"] === "string" ? input2["hint"].trim() || void 0 : void 0;
951
1058
  commands.push({
952
1059
  name,
@@ -1000,7 +1107,7 @@ function boundToolCallRawPayload(params, update, toolCall) {
1000
1107
  }
1001
1108
  function textFromContentBlocks(blocks) {
1002
1109
  const parts = blocks.flatMap(
1003
- (block) => isRecord(block) && block["type"] === "text" && typeof block["text"] === "string" ? [block["text"]] : []
1110
+ (block) => isRecord2(block) && block["type"] === "text" && typeof block["text"] === "string" ? [block["text"]] : []
1004
1111
  );
1005
1112
  const joined = parts.join("");
1006
1113
  return joined.trim().length > 0 ? joined : void 0;
@@ -1025,7 +1132,7 @@ function parseSessionUpdateEvent(params) {
1025
1132
  const entries = Array.isArray(upd["entries"]) ? upd["entries"] : [];
1026
1133
  const plan = entries.map((entry, index) => ({
1027
1134
  step: readAcpText(entry, "content") ?? `Step ${index + 1}`,
1028
- status: normalizePlanStepStatus(isRecord(entry) ? entry["status"] : void 0)
1135
+ status: normalizePlanStepStatus(isRecord2(entry) ? entry["status"] : void 0)
1029
1136
  }));
1030
1137
  if (plan.length > 0) {
1031
1138
  events.push({
@@ -1065,7 +1172,7 @@ function parseSessionUpdateEvent(params) {
1065
1172
  }
1066
1173
  case "agent_message_chunk":
1067
1174
  case "agent_thought_chunk": {
1068
- const content = isRecord(upd["content"]) ? upd["content"] : void 0;
1175
+ const content = isRecord2(upd["content"]) ? upd["content"] : void 0;
1069
1176
  const contentType = content?.["type"];
1070
1177
  const streamKind = kind === "agent_thought_chunk" ? "reasoning_text" : "assistant_text";
1071
1178
  if (contentType === "text") {
@@ -1105,7 +1212,7 @@ function parseSessionUpdateEvent(params) {
1105
1212
  }
1106
1213
  case "compaction_summary_chunk": {
1107
1214
  const compactionId = readAcpText(upd, "compactionId");
1108
- const content = isRecord(upd["content"]) ? upd["content"] : void 0;
1215
+ const content = isRecord2(upd["content"]) ? upd["content"] : void 0;
1109
1216
  const text4 = content?.["type"] === "text" ? content["text"] : void 0;
1110
1217
  if (compactionId === void 0 || typeof text4 !== "string" || text4.length === 0) break;
1111
1218
  events.push({ _tag: "CompactionSummaryChunk", compactionId, text: text4 });
@@ -22799,17 +22906,17 @@ function isResponseMessage(value) {
22799
22906
  function isNotificationMessage(value) {
22800
22907
  return isJsonRpcEnvelope(value) && !("id" in value) && typeof value["method"] === "string";
22801
22908
  }
22802
- function isRecord2(value) {
22909
+ function isRecord3(value) {
22803
22910
  return typeof value === "object" && value !== null;
22804
22911
  }
22805
22912
  function isJsonRpcEnvelope(value) {
22806
- return isRecord2(value) && value["jsonrpc"] === "2.0";
22913
+ return isRecord3(value) && value["jsonrpc"] === "2.0";
22807
22914
  }
22808
22915
  function isJsonRpcId(value) {
22809
22916
  return value === null || typeof value === "string" || typeof value === "number" && Number.isFinite(value);
22810
22917
  }
22811
22918
  function isResponseShapedMessage(value) {
22812
- return isRecord2(value) && !("method" in value) && ("id" in value || "result" in value || "error" in value);
22919
+ return isRecord3(value) && !("method" in value) && ("id" in value || "result" in value || "error" in value);
22813
22920
  }
22814
22921
  function isResponseBatch(batch) {
22815
22922
  let hasValidCall = false;
@@ -22819,7 +22926,7 @@ function isResponseBatch(batch) {
22819
22926
  for (const entry of batch) {
22820
22927
  hasValidCall ||= isRequestMessage(entry) || isNotificationMessage(entry);
22821
22928
  hasValidResponse ||= isResponseMessage(entry);
22822
- if (!isRecord2(entry)) {
22929
+ if (!isRecord3(entry)) {
22823
22930
  continue;
22824
22931
  }
22825
22932
  hasCallShape ||= "method" in entry;
@@ -22834,13 +22941,13 @@ function isResponseBatch(batch) {
22834
22941
  return hasResponseShape && !hasCallShape;
22835
22942
  }
22836
22943
  function cancelRequestId(params) {
22837
- if (!isRecord2(params) || !isJsonRpcId(params["requestId"])) {
22944
+ if (!isRecord3(params) || !isJsonRpcId(params["requestId"])) {
22838
22945
  return void 0;
22839
22946
  }
22840
22947
  return params["requestId"];
22841
22948
  }
22842
22949
  function isErrorResponse(value) {
22843
- return isRecord2(value) && typeof value["code"] === "number" && Number.isInteger(value["code"]) && typeof value["message"] === "string";
22950
+ return isRecord3(value) && typeof value["code"] === "number" && Number.isInteger(value["code"]) && typeof value["message"] === "string";
22844
22951
  }
22845
22952
  var Handled = {
22846
22953
  /**
@@ -23385,7 +23492,7 @@ var Connection = class {
23385
23492
  if (this.abortController.signal.aborted) {
23386
23493
  return Promise.resolve();
23387
23494
  }
23388
- if (!isRecord2(message)) {
23495
+ if (!isRecord3(message)) {
23389
23496
  console.error("Invalid message", { message });
23390
23497
  return Promise.resolve();
23391
23498
  }
@@ -23788,7 +23895,7 @@ function ndJsonStream(output2, input2) {
23788
23895
  await writeJson(protocolErrorResponse(RequestError.parseError()));
23789
23896
  return;
23790
23897
  }
23791
- if (isRecord2(message) || Array.isArray(message)) {
23898
+ if (isRecord3(message) || Array.isArray(message)) {
23792
23899
  controller.enqueue(message);
23793
23900
  } else {
23794
23901
  await writeJson(protocolErrorResponse(RequestError.invalidRequest(message)));
@@ -24775,14 +24882,14 @@ var legacyClientNotificationMethods = /* @__PURE__ */ new Set([
24775
24882
  import * as NodeStream from "node:stream";
24776
24883
 
24777
24884
  // ../../packages/ycodium-acp-executor/src/jsonrpc.ts
24778
- function isRecord3(value) {
24885
+ function isRecord4(value) {
24779
24886
  return typeof value === "object" && value !== null;
24780
24887
  }
24781
24888
  function isJsonRpcId2(value) {
24782
24889
  return typeof value === "string" || typeof value === "number" && Number.isFinite(value);
24783
24890
  }
24784
24891
  function isJsonRpcEnvelope2(value) {
24785
- return isRecord3(value) && value["jsonrpc"] === "2.0";
24892
+ return isRecord4(value) && value["jsonrpc"] === "2.0";
24786
24893
  }
24787
24894
  function isRequestMessage2(value) {
24788
24895
  return isJsonRpcEnvelope2(value) && "id" in value && typeof value["method"] === "string" && isJsonRpcId2(value["id"]);
@@ -24802,7 +24909,7 @@ function isNotificationMessage2(value) {
24802
24909
  return isJsonRpcEnvelope2(value) && !("id" in value) && typeof value["method"] === "string";
24803
24910
  }
24804
24911
  function readJsonRpcError(value) {
24805
- if (!isRecord3(value) || typeof value["code"] !== "number" || typeof value["message"] !== "string") {
24912
+ if (!isRecord4(value) || typeof value["code"] !== "number" || typeof value["message"] !== "string") {
24806
24913
  return void 0;
24807
24914
  }
24808
24915
  return {
@@ -24875,11 +24982,11 @@ var KNOWN_SESSION_UPDATES = /* @__PURE__ */ new Set([
24875
24982
  "compaction_summary_chunk"
24876
24983
  ]);
24877
24984
  function isSessionUpdateEnvelope(payload) {
24878
- if (!isRecord3(payload) || typeof payload["sessionId"] !== "string") {
24985
+ if (!isRecord4(payload) || typeof payload["sessionId"] !== "string") {
24879
24986
  return false;
24880
24987
  }
24881
24988
  const update = payload["update"];
24882
- return isRecord3(update) && typeof update["sessionUpdate"] === "string";
24989
+ return isRecord4(update) && typeof update["sessionUpdate"] === "string";
24883
24990
  }
24884
24991
  function hasKnownVariantShape(update) {
24885
24992
  const kind = update["sessionUpdate"];
@@ -24887,7 +24994,7 @@ function hasKnownVariantShape(update) {
24887
24994
  return false;
24888
24995
  }
24889
24996
  if (kind === "user_message_chunk" || kind === "agent_message_chunk" || kind === "agent_thought_chunk") {
24890
- return isRecord3(update["content"]);
24997
+ return isRecord4(update["content"]);
24891
24998
  }
24892
24999
  return true;
24893
25000
  }
@@ -25076,6 +25183,10 @@ function createAcpConnection(options) {
25076
25183
  );
25077
25184
  } catch {
25078
25185
  }
25186
+ try {
25187
+ waiter.onArrival?.();
25188
+ } catch {
25189
+ }
25079
25190
  if (Object.hasOwn(message, "error")) {
25080
25191
  const rpcError = readJsonRpcError(message["error"]);
25081
25192
  waiter.reject(
@@ -25206,7 +25317,7 @@ function createAcpConnection(options) {
25206
25317
  options.readable.on("end", onEnd);
25207
25318
  options.readable.on("error", onError);
25208
25319
  options.writable.once("error", onWritableError);
25209
- const begin = (method, params, promptId) => {
25320
+ const begin = (method, params, promptId, onArrival) => {
25210
25321
  failIfClosed();
25211
25322
  const id = nextRequestId;
25212
25323
  nextRequestId += 1;
@@ -25214,6 +25325,7 @@ function createAcpConnection(options) {
25214
25325
  pending.set(String(id), {
25215
25326
  method,
25216
25327
  ...promptId === void 0 ? {} : { promptId },
25328
+ ...onArrival === void 0 ? {} : { onArrival },
25217
25329
  resolve: resolve2,
25218
25330
  reject
25219
25331
  });
@@ -25269,10 +25381,31 @@ function createAcpConnection(options) {
25269
25381
  function normalizeModelId(value) {
25270
25382
  return value?.trim() || void 0;
25271
25383
  }
25384
+ function adoptReportedConfigOptions(state, configOptions) {
25385
+ const option = state.modelConfigId === void 0 ? void 0 : findConfigOptionEntry(configOptions, state.modelConfigId);
25386
+ const value = option?.["currentValue"];
25387
+ const reported = typeof value === "string" ? normalizeModelId(value) : void 0;
25388
+ if (reported === void 0) return { ...state, configOptions };
25389
+ const { modelUnconfirmed: _confirmed, ...known } = state;
25390
+ return { ...known, configOptions, currentModelId: reported };
25391
+ }
25392
+ function forgetConfigOptionValue(state, configId) {
25393
+ const forgetsModel = configId === void 0 || configId === state.modelConfigId;
25394
+ const configOptions = configId !== void 0 && Array.isArray(state.configOptions) ? state.configOptions.map((option) => {
25395
+ if (!isRecord2(option) || option["id"] !== configId) return option;
25396
+ const { currentValue: _forgotten, ...rest } = option;
25397
+ return rest;
25398
+ }) : state.configOptions;
25399
+ return {
25400
+ ...state,
25401
+ configOptions,
25402
+ ...forgetsModel ? { currentModelId: void 0, modelUnconfirmed: true } : {}
25403
+ };
25404
+ }
25272
25405
  function modelSelectionStateFromSetup(setup) {
25273
- const sessionModels = isRecord(setup.models) ? setup.models : void 0;
25406
+ const sessionModels = isRecord2(setup.models) ? setup.models : void 0;
25274
25407
  const modelConfigId = extractModelConfigId(setup);
25275
- const strategy = sessionModels === void 0 && modelConfigId !== void 0 ? "config-option" : "session/set_model";
25408
+ const strategy = modelConfigId !== void 0 ? "config-option" : "session/set_model";
25276
25409
  const modelConfigOption = modelConfigId === void 0 ? void 0 : findSessionConfigOption(setup.configOptions, modelConfigId);
25277
25410
  const configModel = modelConfigOption?.currentValue;
25278
25411
  const currentModelId = strategy === "config-option" ? typeof configModel === "string" ? normalizeModelId(configModel) : void 0 : typeof sessionModels?.["currentModelId"] === "string" ? normalizeModelId(sessionModels["currentModelId"]) : void 0;
@@ -25282,7 +25415,8 @@ function modelSelectionStateFromSetup(setup) {
25282
25415
  modelConfigId,
25283
25416
  currentModelId,
25284
25417
  advertisedModelValues: strategy === "config-option" ? modelConfigOption === void 0 ? [] : collectSessionConfigOptionValues(modelConfigOption) : collectSessionModelIds(sessionModels),
25285
- configOptions: setup.configOptions
25418
+ configOptions: setup.configOptions,
25419
+ catalogSetup: setup
25286
25420
  };
25287
25421
  }
25288
25422
 
@@ -25380,6 +25514,10 @@ function createExecutorLifecycle({
25380
25514
  if (targetTurnId !== void 0) {
25381
25515
  interruptedTurnIds.add(targetTurnId);
25382
25516
  }
25517
+ const optionWrites = shared.optionWriteStop;
25518
+ if (optionWrites !== void 0 && (targetTurnId === void 0 || optionWrites.turnId === targetTurnId)) {
25519
+ optionWrites.stop();
25520
+ }
25383
25521
  settleAllApprovals("cancel");
25384
25522
  settleAllUserInputs();
25385
25523
  abortPendingSettlement("turn cancelled");
@@ -25592,6 +25730,41 @@ function isAcpRequestPermissionParams(value) {
25592
25730
  return Array.isArray(record4["options"]);
25593
25731
  }
25594
25732
 
25733
+ // ../../packages/ycodium-acp-executor/src/apply-effort.ts
25734
+ var alignRequestedEffort = async (input2) => {
25735
+ const unchanged = { configOptions: input2.configOptions };
25736
+ const effort = findAcpEffortConfig(input2.configOptions);
25737
+ if (effort === void 0) return unchanged;
25738
+ const values = new Set(effort.choices.map((choice) => choice.value));
25739
+ const current = effort.currentValue;
25740
+ const requested = input2.requestedEffort?.trim() || void 0;
25741
+ const unsupportedEffort = requested !== void 0 && !values.has(requested) ? requested : void 0;
25742
+ const staleCurrent = current !== void 0 && !values.has(current);
25743
+ const fallback = input2.modelDefaultEffort !== void 0 && values.has(input2.modelDefaultEffort) ? input2.modelDefaultEffort : void 0;
25744
+ const target = requested !== void 0 && unsupportedEffort === void 0 ? requested : unsupportedEffort !== void 0 || staleCurrent ? fallback : void 0;
25745
+ const written = target === void 0 || target === current ? void 0 : await input2.write(effort.configId, target);
25746
+ const configOptions = written === void 0 ? input2.configOptions : written.configOptions;
25747
+ if (unsupportedEffort === void 0) return { configOptions };
25748
+ const confirmed = fallback !== void 0 && written?.reported === true && findAcpEffortConfig(written.configOptions)?.currentValue === fallback;
25749
+ return { configOptions, unsupportedEffort, ...confirmed ? { appliedEffort: fallback } : {} };
25750
+ };
25751
+
25752
+ // ../../packages/ycodium-acp-executor/src/reasoning-level-fallback.ts
25753
+ var REASONING_LEVEL_FALLBACK_CODE = "reasoning-level-fallback";
25754
+ function reasoningLevelFallbackWarning(input2) {
25755
+ const model = input2.model ?? "This model";
25756
+ const outcome = input2.appliedEffort === void 0 ? "so the agent picks this turn's level" : `so this turn uses its default, "${input2.appliedEffort}"`;
25757
+ return {
25758
+ message: `${model} does not offer the "${input2.requestedEffort}" reasoning effort, ${outcome}. Your saved choice is unchanged.`,
25759
+ detail: {
25760
+ code: REASONING_LEVEL_FALLBACK_CODE,
25761
+ ...input2.model === void 0 ? {} : { model: input2.model },
25762
+ requestedEffort: input2.requestedEffort,
25763
+ ...input2.appliedEffort === void 0 ? {} : { appliedEffort: input2.appliedEffort }
25764
+ }
25765
+ };
25766
+ }
25767
+
25595
25768
  // ../../packages/ycodium-acp-executor/src/failureCategory.ts
25596
25769
  var ACP_FAILURE_CATEGORIES = [
25597
25770
  "quota",
@@ -25932,6 +26105,8 @@ function createExecutorPrompt({
25932
26105
  threadMutex,
25933
26106
  promptMutex,
25934
26107
  applyRequestedModel,
26108
+ modelDefaultEffort,
26109
+ writeConfigOptionReported,
25935
26110
  notifyPromptSettled,
25936
26111
  shared,
25937
26112
  promptResults,
@@ -26025,6 +26200,57 @@ function createExecutorPrompt({
26025
26200
  }
26026
26201
  }
26027
26202
  };
26203
+ const alignTurnOptions = async (input2, sessionId, generation, isStale, signal) => {
26204
+ const applied = await applyRequestedModel({
26205
+ state: shared.modelSelectionState,
26206
+ acpSessionId: sessionId,
26207
+ requestedModelId: input2.requestedModelId,
26208
+ expectedGeneration: generation,
26209
+ signal
26210
+ });
26211
+ shared.modelSelectionState = applied;
26212
+ if (isStale()) return;
26213
+ const switched = applied.configOptions;
26214
+ let aligned;
26215
+ try {
26216
+ aligned = await alignRequestedEffort({
26217
+ configOptions: switched,
26218
+ write: (configId, value) => writeConfigOptionReported(sessionId, switched, configId, value, {
26219
+ option: "reasoning level",
26220
+ signal
26221
+ }),
26222
+ requestedEffort: input2.requestedEffort,
26223
+ modelDefaultEffort: modelDefaultEffort(applied)
26224
+ });
26225
+ } catch (error62) {
26226
+ throw error62 instanceof RequestError ? new AcpExecutorOptionRejectedError(
26227
+ "reasoning level",
26228
+ input2.requestedEffort?.trim(),
26229
+ error62
26230
+ ) : error62;
26231
+ }
26232
+ if (!isLive(generation)) throw new AcpExecutorDisposedError();
26233
+ if (aligned.configOptions !== switched) {
26234
+ shared.modelSelectionState = adoptReportedConfigOptions(
26235
+ shared.modelSelectionState,
26236
+ aligned.configOptions
26237
+ );
26238
+ }
26239
+ if (aligned.unsupportedEffort !== void 0) {
26240
+ emit({
26241
+ type: "runtime.warning",
26242
+ ...stamp(),
26243
+ provider: options.provider,
26244
+ threadId: options.threadId,
26245
+ ...shared.activeTurnId === void 0 ? {} : { turnId: shared.activeTurnId },
26246
+ payload: reasoningLevelFallbackWarning({
26247
+ model: applied.currentModelId,
26248
+ requestedEffort: aligned.unsupportedEffort,
26249
+ appliedEffort: aligned.appliedEffort
26250
+ })
26251
+ });
26252
+ }
26253
+ };
26028
26254
  const failIfPromptUnavailable = () => {
26029
26255
  failIfDisposed();
26030
26256
  if (shared.connectionClosed) throw new AcpExecutorDisposedError();
@@ -26032,7 +26258,7 @@ function createExecutorPrompt({
26032
26258
  const prompt = async (input2) => {
26033
26259
  failIfPromptUnavailable();
26034
26260
  const started = requireStarted();
26035
- let sessionId = started.sessionId;
26261
+ const sessionId = started.sessionId;
26036
26262
  let record4;
26037
26263
  const settle2 = (rest) => settleTurn({
26038
26264
  expectedGeneration: started.generation,
@@ -26119,32 +26345,34 @@ function createExecutorPrompt({
26119
26345
  failIfDisposed();
26120
26346
  const queuedTurnId = turnId;
26121
26347
  const isStale = () => interruptedTurnIds.has(queuedTurnId) || input2.expectedActiveTurnId !== void 0 && shared.activeTurnId !== queuedTurnId;
26122
- if (!isStale()) {
26348
+ if (!isStale() && input2.expectedActiveTurnId === void 0) {
26349
+ const optionWrites = new AbortController();
26350
+ shared.optionWriteStop = { turnId, stop: () => optionWrites.abort() };
26123
26351
  try {
26124
- const applied = await applyRequestedModel({
26125
- state: shared.modelSelectionState,
26126
- acpSessionId: sessionId,
26127
- setup: shared.startResult?.sessionSetupResult ?? {},
26128
- requestedModelId: input2.requestedModelId,
26129
- expectedGeneration: started.generation
26130
- });
26131
- shared.modelSelectionState = applied.state;
26132
- sessionId = applied.sessionId;
26133
- record4.sessionId = applied.sessionId;
26352
+ await alignTurnOptions(
26353
+ input2,
26354
+ sessionId,
26355
+ started.generation,
26356
+ isStale,
26357
+ optionWrites.signal
26358
+ );
26134
26359
  } catch (error62) {
26135
- const failureCategory = readAcpFailureCategory(error62);
26136
- if (failureCategory === "model-unavailable") {
26137
- 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.";
26138
- settle2({
26139
- turnId,
26140
- stopReason: "error",
26141
- state: "failed",
26142
- errorMessage,
26143
- failureCategory
26144
- });
26145
- throw error62 instanceof Error ? withFailureCategory(error62, failureCategory) : error62;
26360
+ if (error62 instanceof AcpExecutorDisposedError) throw error62;
26361
+ if (isStale()) {
26362
+ settle2({ turnId, stopReason: "cancelled", state: "cancelled" });
26363
+ return { threadId: options.threadId, turnId, stopReason: "cancelled" };
26146
26364
  }
26147
- throw error62;
26365
+ const failureCategory = readAcpFailureCategory(error62);
26366
+ settle2({
26367
+ turnId,
26368
+ stopReason: "error",
26369
+ state: "failed",
26370
+ errorMessage: error62 instanceof Error && error62.message.trim() || "Switching the model or reasoning level failed. This turn was not sent.",
26371
+ ...failureCategory === void 0 ? {} : { failureCategory }
26372
+ });
26373
+ throw error62 instanceof Error ? withFailureCategory(error62, failureCategory) : error62;
26374
+ } finally {
26375
+ shared.optionWriteStop = void 0;
26148
26376
  }
26149
26377
  }
26150
26378
  failIfDisposed();
@@ -26527,7 +26755,7 @@ function createExecutorRequests({
26527
26755
  pendingUserInputs.delete(requestId);
26528
26756
  pending.resolve({
26529
26757
  kind: "answered",
26530
- answers: isRecord(answers) ? answers : {}
26758
+ answers: isRecord2(answers) ? answers : {}
26531
26759
  });
26532
26760
  };
26533
26761
  return {
@@ -26702,97 +26930,6 @@ function createExecutorRuntime({
26702
26930
  };
26703
26931
  }
26704
26932
 
26705
- // ../../packages/ycodium-acp-executor/src/config-options.ts
26706
- function isRecord4(value) {
26707
- return typeof value === "object" && value !== null && !Array.isArray(value);
26708
- }
26709
- function asString(value) {
26710
- return typeof value === "string" ? value : void 0;
26711
- }
26712
- function normalizedField(option, key) {
26713
- return asString(option[key])?.trim().toLowerCase() ?? "";
26714
- }
26715
- function looksLikeEffortOption(option) {
26716
- const id = normalizedField(option, "id");
26717
- const name = normalizedField(option, "name");
26718
- return id === "effort" || id === "reasoning" || name === "effort" || name === "reasoning" || name.includes("effort") || name.includes("reasoning") || normalizedField(option, "category") === "thought_level";
26719
- }
26720
- function flattenChoices(rawOptions, currentValue) {
26721
- if (!Array.isArray(rawOptions)) return [];
26722
- const out = [];
26723
- const seen = /* @__PURE__ */ new Set();
26724
- const push = (entry) => {
26725
- if (!isRecord4(entry)) return;
26726
- const value = asString(entry["value"]);
26727
- if (value === void 0 || seen.has(value)) return;
26728
- seen.add(value);
26729
- const description = asString(entry["description"])?.trim();
26730
- const name = asString(entry["name"])?.trim();
26731
- out.push({
26732
- value,
26733
- ...name ? { name } : {},
26734
- label: name || value,
26735
- ...description ? { description } : {},
26736
- ...currentValue !== void 0 && currentValue === value ? { isDefault: true } : {}
26737
- });
26738
- };
26739
- for (const entry of rawOptions) {
26740
- if (isRecord4(entry) && Array.isArray(entry["options"])) {
26741
- for (const nested of entry["options"]) push(nested);
26742
- continue;
26743
- }
26744
- push(entry);
26745
- }
26746
- return out;
26747
- }
26748
- function findAcpEffortConfig(configOptions) {
26749
- if (!Array.isArray(configOptions)) return void 0;
26750
- const candidates = configOptions.filter(isRecord4).filter((option) => normalizedField(option, "type") === "select").filter(looksLikeEffortOption);
26751
- if (candidates.length === 0) return void 0;
26752
- 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];
26753
- if (picked === void 0) return void 0;
26754
- const configId = asString(picked["id"])?.trim();
26755
- if (!configId) return void 0;
26756
- const choices = flattenChoices(picked["options"], asString(picked["currentValue"])?.trim());
26757
- if (choices.length === 0) return void 0;
26758
- return {
26759
- configId,
26760
- label: asString(picked["name"])?.trim() ?? "",
26761
- choices
26762
- };
26763
- }
26764
-
26765
- // ../../packages/ycodium-acp-executor/src/apply-effort.ts
26766
- var applyRequestedEffort = async (input2) => {
26767
- const requested = input2.requestedEffort?.trim();
26768
- if (!requested) return;
26769
- const effort = findAcpEffortConfig(input2.configOptions);
26770
- if (effort === void 0) return;
26771
- const matched = effort.choices.find((choice) => choice.value === requested);
26772
- if (matched === void 0) {
26773
- input2.log?.info("requested effort is not in the agent catalog; keeping its own", {
26774
- requestedEffort: requested,
26775
- advertised: effort.choices.map((choice) => choice.value)
26776
- });
26777
- return;
26778
- }
26779
- if (matched.isDefault === true) return;
26780
- if (!input2.isLive(input2.expectedGeneration)) return;
26781
- try {
26782
- await input2.connection.request("session/set_config_option", {
26783
- sessionId: input2.acpSessionId,
26784
- configId: effort.configId,
26785
- value: requested
26786
- });
26787
- } catch (error62) {
26788
- input2.log?.info("setting the effort level failed; the session keeps its own", {
26789
- requestedEffort: requested,
26790
- configId: effort.configId,
26791
- error: error62 instanceof Error ? error62.message : String(error62)
26792
- });
26793
- }
26794
- };
26795
-
26796
26933
  // ../../packages/ycodium-acp-executor/src/config-option-wiring.ts
26797
26934
  var VENDOR_CATEGORY_PREFIX = "_";
26798
26935
  var ACP_CONFIG_CATEGORY_HANDLING = {
@@ -26806,7 +26943,7 @@ var ACP_CONFIG_CATEGORY_HANDLING = {
26806
26943
  },
26807
26944
  thought_level: {
26808
26945
  consumed: true,
26809
- via: "config-options.ts:findAcpEffortConfig \u2192 apply-effort.ts:applyRequestedEffort"
26946
+ via: "config-options.ts:findAcpEffortConfig \u2192 apply-effort.ts:alignRequestedEffort"
26810
26947
  },
26811
26948
  model_option: {
26812
26949
  consumed: true,
@@ -26875,13 +27012,14 @@ function judgeRequestedModel(input2) {
26875
27012
  if (requested === void 0 || requested === current) return { kind: "keep" };
26876
27013
  const hasCatalog = input2.state.advertisedModelValues.length > 0;
26877
27014
  const hasCurrent = current !== void 0;
26878
- if (!hasCatalog && !hasCurrent) return { kind: "keep" };
26879
- const canSwitch = input2.state.strategy === "session/set_model" && input2.state.sessionModelsAdvertised || input2.state.strategy === "config-option" && input2.state.modelConfigId !== void 0;
27015
+ if (!hasCatalog && !hasCurrent && input2.state.modelUnconfirmed !== true) return { kind: "keep" };
27016
+ const canSwitch = canSwitchModel(input2.state);
26880
27017
  const value = hasCatalog ? matchAdvertisedModelValue(requested, input2.state.advertisedModelValues) : void 0;
26881
27018
  if (value !== void 0 && value === current) return { kind: "keep" };
26882
27019
  if (value !== void 0 && canSwitch) return { kind: "switch", value };
26883
- return input2.refreshed ? { kind: "unavailable" } : { kind: "refresh" };
27020
+ return { kind: "refresh" };
26884
27021
  }
27022
+ var canSwitchModel = (state) => state.strategy === "session/set_model" && state.sessionModelsAdvertised || state.strategy === "config-option" && state.modelConfigId !== void 0;
26885
27023
  function modelUnavailableError(requestedModelId, currentModelId) {
26886
27024
  const current = currentModelId ?? "its own model";
26887
27025
  return withFailureCategory(
@@ -26938,6 +27076,7 @@ function acpModeConfigId(setup) {
26938
27076
  // ../../packages/ycodium-acp-executor/src/executor/session.ts
26939
27077
  function createExecutorSession({
26940
27078
  options,
27079
+ hooks,
26941
27080
  connection,
26942
27081
  isLive,
26943
27082
  emit,
@@ -26959,131 +27098,140 @@ function createExecutorSession({
26959
27098
  shared.sessionOpensInFlight -= 1;
26960
27099
  }
26961
27100
  };
26962
- const openFreshSession = async (expectedGeneration) => {
26963
- const initialized = shared.initializeResult;
26964
- if (initialized === void 0) {
26965
- throw new Error("no handshake");
27101
+ const optionDeadlineMs = () => options.modelCatalogRefreshTimeoutMs ?? MODEL_CATALOG_REFRESH_TIMEOUT_MS;
27102
+ const within = async (work, signal, onTimeout) => {
27103
+ void work.catch(() => void 0);
27104
+ let timer;
27105
+ let onStop;
27106
+ const bound = new Promise((_resolve, reject) => {
27107
+ timer = setTimeout(() => reject(onTimeout()), optionDeadlineMs());
27108
+ onStop = () => reject(new AcpOptionWriteStoppedError());
27109
+ if (signal?.aborted === true) onStop();
27110
+ else signal?.addEventListener("abort", onStop, { once: true });
27111
+ });
27112
+ try {
27113
+ return await Promise.race([work, bound]);
27114
+ } finally {
27115
+ clearTimeout(timer);
27116
+ if (onStop !== void 0) signal?.removeEventListener("abort", onStop);
26966
27117
  }
26967
- const mcpServers = filterMcpServersByAgentCapabilities(
26968
- options.mcpServers ?? [],
26969
- initialized.agentCapabilities
27118
+ };
27119
+ const awaitOptionReply = async (pending, configId, write) => {
27120
+ void pending.response.catch(() => void 0);
27121
+ try {
27122
+ return await within(
27123
+ pending.written.then(() => pending.response),
27124
+ write.signal,
27125
+ () => new AcpExecutorOptionTimeoutError(write.option, write.value)
27126
+ );
27127
+ } catch (error62) {
27128
+ if (error62 instanceof AcpExecutorOptionTimeoutError || error62 instanceof AcpOptionWriteStoppedError) {
27129
+ shared.modelSelectionState = forgetConfigOptionValue(shared.modelSelectionState, configId);
27130
+ }
27131
+ throw error62;
27132
+ }
27133
+ };
27134
+ const writeConfigOptionReported = async (sessionId, configOptions, configId, value, write) => {
27135
+ const issuedAt = shared.configUpdateSeq;
27136
+ let arrivedAt = Number.POSITIVE_INFINITY;
27137
+ const pending = connection.begin(
27138
+ "session/set_config_option",
27139
+ { sessionId, configId, value },
27140
+ void 0,
27141
+ () => {
27142
+ arrivedAt = shared.configUpdateSeq;
27143
+ }
26970
27144
  );
26971
- const additionalDirectories = options.additionalDirectories !== void 0 && options.additionalDirectories.length > 0 ? { additionalDirectories: options.additionalDirectories } : {};
26972
- const created = await requestSession("session/new", {
26973
- cwd: options.cwd,
26974
- mcpServers,
26975
- ...additionalDirectories
26976
- });
26977
- if (!isLive(expectedGeneration)) {
26978
- throw new AcpExecutorDisposedError();
27145
+ const reply = await awaitOptionReply(pending, configId, { ...write, value });
27146
+ await connection.awaitInboundIdle();
27147
+ const ours = shared.configOptionUpdates.get(sessionId);
27148
+ if (ours !== void 0 && ours.seq > arrivedAt) {
27149
+ return { configOptions: ours.configOptions, reported: true };
26979
27150
  }
26980
- if (created.sessionId === void 0 || created.sessionId.length === 0) {
26981
- throw new Error("session/new did not return a session id");
27151
+ if (reply?.configOptions !== void 0) {
27152
+ return { configOptions: reply.configOptions, reported: true };
26982
27153
  }
26983
- return { sessionId: created.sessionId, setup: created };
27154
+ const base = ours !== void 0 && ours.seq > issuedAt ? ours.configOptions : configOptions;
27155
+ return { configOptions: withConfigOptionValue(base, configId, value), reported: false };
26984
27156
  };
27157
+ const writeConfigOption = async (...args) => (await writeConfigOptionReported(...args)).configOptions;
26985
27158
  const switchModel = async (input2) => {
26986
27159
  let configOptions = input2.state.configOptions;
26987
- if (input2.state.strategy === "config-option") {
26988
- const switched = await connection.request(
26989
- "session/set_config_option",
26990
- { sessionId: input2.sessionId, configId: input2.state.modelConfigId, value: input2.value }
27160
+ const write = { option: "model", signal: input2.signal };
27161
+ if (input2.state.strategy === "config-option" && input2.state.modelConfigId !== void 0) {
27162
+ configOptions = await writeConfigOption(
27163
+ input2.sessionId,
27164
+ configOptions,
27165
+ input2.state.modelConfigId,
27166
+ input2.value,
27167
+ write
26991
27168
  );
26992
- configOptions = switched?.configOptions ?? configOptions;
26993
27169
  } else {
26994
- await connection.request("session/set_model", {
27170
+ const before = shared.configOptionUpdates.get(input2.sessionId);
27171
+ const pending = connection.begin("session/set_model", {
26995
27172
  sessionId: input2.sessionId,
26996
27173
  modelId: input2.value
26997
27174
  });
27175
+ await awaitOptionReply(pending, void 0, { ...write, value: input2.value });
27176
+ await connection.awaitInboundIdle();
27177
+ const reported = shared.configOptionUpdates.get(input2.sessionId);
27178
+ if (reported !== void 0 && reported !== before) {
27179
+ configOptions = reported.configOptions;
27180
+ }
26998
27181
  }
26999
27182
  if (!isLive(input2.expectedGeneration)) {
27000
27183
  throw new AcpExecutorDisposedError();
27001
27184
  }
27002
- return { ...input2.state, currentModelId: input2.value, configOptions };
27003
- };
27004
- const adoptReopenedSession = (applied) => {
27005
- if (shared.sessionId === void 0 && shared.startResult === void 0) return;
27006
- const setup = applied.setup;
27007
- shared.sessionId = applied.sessionId;
27008
- shared.modelSelectionState = applied.state;
27009
- shared.modeState = parseSessionModeState(setup);
27010
- if (shared.startResult !== void 0) {
27011
- shared.startResult = {
27012
- ...shared.startResult,
27013
- sessionId: applied.sessionId,
27014
- sessionSetupResult: setup,
27015
- modelConfigId: extractModelConfigId(setup),
27016
- modelId: applied.state.currentModelId
27017
- };
27018
- }
27185
+ const { modelUnconfirmed: _confirmed, ...known } = input2.state;
27186
+ return adoptReportedConfigOptions({ ...known, currentModelId: input2.value }, configOptions);
27019
27187
  };
27020
27188
  const applyRequestedModel = async (input2) => {
27021
- async function applyOnce(state, sessionId, setup, refreshed) {
27022
- const requestedModelId = normalizeModelId(input2.requestedModelId) ?? "";
27189
+ const requestedModelId = normalizeModelId(input2.requestedModelId) ?? "";
27190
+ const switchTo = (value) => switchModel({
27191
+ state: input2.state,
27192
+ sessionId: input2.acpSessionId,
27193
+ value,
27194
+ expectedGeneration: input2.expectedGeneration,
27195
+ signal: input2.signal
27196
+ });
27197
+ try {
27023
27198
  const judgment = judgeRequestedModel({
27024
- state,
27025
- requestedModelId: input2.requestedModelId,
27026
- refreshed
27199
+ state: input2.state,
27200
+ requestedModelId: input2.requestedModelId
27027
27201
  });
27028
- if (judgment.kind === "keep") return { state, sessionId, setup };
27029
- if (judgment.kind === "unavailable") {
27030
- throw modelUnavailableError(requestedModelId, state.currentModelId);
27031
- }
27032
- if (judgment.kind === "switch") {
27033
- return {
27034
- state: await switchModel({
27035
- state,
27036
- sessionId,
27037
- value: judgment.value,
27038
- expectedGeneration: input2.expectedGeneration
27039
- }),
27040
- sessionId,
27041
- setup
27042
- };
27043
- }
27044
- return refreshAndRejudge(requestedModelId);
27045
- }
27046
- async function refreshAndRejudge(requestedModelId) {
27047
- const timeoutMs = options.modelCatalogRefreshTimeoutMs ?? MODEL_CATALOG_REFRESH_TIMEOUT_MS;
27048
- let timedOut = false;
27049
- const timeoutError = new ModelCatalogRefreshTimeoutError();
27050
- let timer;
27051
- const work = (async () => {
27052
- await options.refreshCatalogForSend?.();
27053
- if (timedOut) throw timeoutError;
27054
- const reopened = await openFreshSession(input2.expectedGeneration);
27055
- if (timedOut) throw timeoutError;
27056
- const next = modelSelectionStateFromSetup(reopened.setup);
27057
- const applied = await applyOnce(next, reopened.sessionId, reopened.setup, true);
27058
- if (timedOut) throw timeoutError;
27059
- adoptReopenedSession(applied);
27060
- return applied;
27061
- })();
27062
- void work.then(
27063
- () => void 0,
27064
- () => void 0
27202
+ if (judgment.kind === "keep") return input2.state;
27203
+ if (judgment.kind === "switch") return await switchTo(judgment.value);
27204
+ await refreshCatalog(requestedModelId, input2.signal);
27205
+ if (!isLive(input2.expectedGeneration)) throw new AcpExecutorDisposedError();
27206
+ if (!canSwitchModel(input2.state)) {
27207
+ throw modelUnavailableError(requestedModelId, input2.state.currentModelId);
27208
+ }
27209
+ const switched = await switchTo(requestedModelId);
27210
+ return switched.advertisedModelValues.includes(requestedModelId) ? switched : {
27211
+ ...switched,
27212
+ advertisedModelValues: [...switched.advertisedModelValues, requestedModelId]
27213
+ };
27214
+ } catch (error62) {
27215
+ throw error62 instanceof RequestError ? withFailureCategory(
27216
+ new AcpExecutorOptionRejectedError("model", requestedModelId, error62),
27217
+ "model-unavailable"
27218
+ ) : error62;
27219
+ }
27220
+ };
27221
+ const refreshCatalog = async (requestedModelId, signal) => {
27222
+ try {
27223
+ await within(
27224
+ Promise.resolve(options.refreshCatalogForSend?.()),
27225
+ signal,
27226
+ () => new ModelCatalogRefreshTimeoutError()
27065
27227
  );
27066
- const deadline = new Promise((_resolve, reject) => {
27067
- timer = setTimeout(() => {
27068
- timedOut = true;
27069
- connection.abandonPending(timeoutError);
27070
- reject(timeoutError);
27071
- }, timeoutMs);
27072
- });
27073
- try {
27074
- return await Promise.race([work, deadline]);
27075
- } catch (error62) {
27076
- if (error62 instanceof AcpExecutorDisposedError) throw error62;
27077
- if (timedOut || error62 instanceof ModelCatalogRefreshTimeoutError) {
27078
- throw modelRefreshTimedOutError(requestedModelId);
27079
- }
27080
- if (readAcpFailureCategory(error62) === "model-unavailable") throw error62;
27081
- throw modelRefreshFailedError(requestedModelId, error62);
27082
- } finally {
27083
- if (timer !== void 0) clearTimeout(timer);
27228
+ } catch (error62) {
27229
+ if (error62 instanceof AcpOptionWriteStoppedError) throw error62;
27230
+ if (error62 instanceof ModelCatalogRefreshTimeoutError) {
27231
+ throw modelRefreshTimedOutError(requestedModelId);
27084
27232
  }
27233
+ throw modelRefreshFailedError(requestedModelId, error62);
27085
27234
  }
27086
- return applyOnce(input2.state, input2.acpSessionId, input2.setup, false);
27087
27235
  };
27088
27236
  const startOnce = async (expectedGeneration) => {
27089
27237
  const initializePayload = acpInitializePayload(
@@ -27158,26 +27306,45 @@ function createExecutorSession({
27158
27306
  categories: unhandledCategories
27159
27307
  });
27160
27308
  }
27161
- const openedSetup = setup;
27162
- const appliedModel = await applyRequestedModel({
27163
- state: modelSelectionStateFromSetup(openedSetup),
27309
+ let nextModelSelectionState = await applyRequestedModel({
27310
+ state: modelSelectionStateFromSetup(setup),
27164
27311
  acpSessionId: nextSessionId,
27165
- setup: openedSetup,
27166
27312
  requestedModelId: options.requestedModelId,
27167
27313
  expectedGeneration
27168
27314
  });
27169
- nextSessionId = appliedModel.sessionId;
27170
- setup = appliedModel.setup;
27171
- const nextModelSelectionState = appliedModel.state;
27172
- await applyRequestedEffort({
27173
- connection,
27174
- log: options.log,
27175
- isLive,
27176
- configOptions: nextModelSelectionState.configOptions,
27177
- acpSessionId: nextSessionId,
27178
- requestedEffort: options.requestedEffort,
27179
- expectedGeneration
27180
- });
27315
+ if (!isLive(expectedGeneration)) {
27316
+ throw new AcpExecutorDisposedError();
27317
+ }
27318
+ try {
27319
+ const opened = nextModelSelectionState.configOptions;
27320
+ const aligned = await alignRequestedEffort({
27321
+ configOptions: opened,
27322
+ write: (configId, value) => writeConfigOptionReported(nextSessionId, opened, configId, value, {
27323
+ option: "reasoning level"
27324
+ }),
27325
+ requestedEffort: options.requestedEffort,
27326
+ modelDefaultEffort: modelDefaultEffort(nextModelSelectionState)
27327
+ });
27328
+ nextModelSelectionState = adoptReportedConfigOptions(
27329
+ nextModelSelectionState,
27330
+ aligned.configOptions
27331
+ );
27332
+ if (aligned.unsupportedEffort !== void 0) {
27333
+ options.log?.info("requested effort is not offered by this model; using its default", {
27334
+ requestedEffort: aligned.unsupportedEffort,
27335
+ modelId: nextModelSelectionState.currentModelId
27336
+ });
27337
+ }
27338
+ } catch (error62) {
27339
+ const effortConfigId = findAcpEffortConfig(nextModelSelectionState.configOptions)?.configId;
27340
+ if (error62 instanceof AcpExecutorOptionTimeoutError && effortConfigId !== void 0) {
27341
+ nextModelSelectionState = forgetConfigOptionValue(nextModelSelectionState, effortConfigId);
27342
+ }
27343
+ options.log?.info("setting the effort level failed; the session keeps its own", {
27344
+ requestedEffort: options.requestedEffort,
27345
+ error: error62 instanceof Error ? error62.message : String(error62)
27346
+ });
27347
+ }
27181
27348
  if (options.beforeApplyResult) {
27182
27349
  await options.beforeApplyResult();
27183
27350
  }
@@ -27237,6 +27404,7 @@ function createExecutorSession({
27237
27404
  );
27238
27405
  return shared.startInFlight;
27239
27406
  };
27407
+ const modelDefaultEffort = (state) => state.currentModelId === void 0 ? void 0 : hooks.modelDefaultEffort?.({ setup: state.catalogSetup, modelId: state.currentModelId });
27240
27408
  const requireStarted = () => {
27241
27409
  failIfDisposed();
27242
27410
  if (!shared.sessionId || !shared.startResult) {
@@ -27264,7 +27432,14 @@ function createExecutorSession({
27264
27432
  modeId
27265
27433
  });
27266
27434
  };
27267
- return { applyRequestedModel, start, requireStarted, setNativeMode };
27435
+ return {
27436
+ applyRequestedModel,
27437
+ modelDefaultEffort,
27438
+ writeConfigOptionReported,
27439
+ start,
27440
+ requireStarted,
27441
+ setNativeMode
27442
+ };
27268
27443
  }
27269
27444
 
27270
27445
  // ../../packages/ycodium-acp-executor/src/executor/settlement.ts
@@ -27328,14 +27503,14 @@ function createExecutorSettlement({
27328
27503
 
27329
27504
  // ../../packages/ycodium-acp-executor/src/executor/subagent-sessions.ts
27330
27505
  function parseState(raw) {
27331
- if (!isRecord(raw)) return { status: void 0, error: void 0 };
27506
+ if (!isRecord2(raw)) return { status: void 0, error: void 0 };
27332
27507
  const state = raw["state"];
27333
27508
  if (state === "running") return { status: "running", error: void 0 };
27334
27509
  if (state === "requires_action") return { status: "waiting", error: void 0 };
27335
27510
  if (state !== "idle") return { status: void 0, error: void 0 };
27336
27511
  const stopReason = raw["stopReason"];
27337
- const error62 = isRecord(raw["error"]) ? asTrimmedString(raw["error"]["message"]) : void 0;
27338
- if (stopReason === "error" || isRecord(raw["error"])) return { status: "failed", error: error62 };
27512
+ const error62 = isRecord2(raw["error"]) ? asTrimmedString(raw["error"]["message"]) : void 0;
27513
+ if (stopReason === "error" || isRecord2(raw["error"])) return { status: "failed", error: error62 };
27339
27514
  if (stopReason === "cancelled") return { status: "cancelled", error: void 0 };
27340
27515
  return { status: "idle", error: void 0 };
27341
27516
  }
@@ -27351,7 +27526,7 @@ function childLinkage(childId, record4) {
27351
27526
  function createSubagentSessionTracker(registry2) {
27352
27527
  const observe = (notification, ctx) => {
27353
27528
  const update = notification.update;
27354
- if (!isRecord(update) || update["sessionUpdate"] !== "subagent_update") return [];
27529
+ if (!isRecord2(update) || update["sessionUpdate"] !== "subagent_update") return [];
27355
27530
  const childId = asTrimmedString(update["sessionId"]);
27356
27531
  if (childId === void 0 || ctx.ownSessionIds.has(childId)) return [];
27357
27532
  const outerId = notification.sessionId;
@@ -27577,6 +27752,21 @@ function createExecutorUpdates({
27577
27752
  markChildSession
27578
27753
  });
27579
27754
  const handleSessionUpdate = (params) => {
27755
+ const update = params.update;
27756
+ const reportedOptions = update["configOptions"];
27757
+ if (update["sessionUpdate"] === "config_option_update" && Array.isArray(reportedOptions)) {
27758
+ shared.configUpdateSeq += 1;
27759
+ shared.configOptionUpdates.set(params.sessionId, {
27760
+ configOptions: reportedOptions,
27761
+ seq: shared.configUpdateSeq
27762
+ });
27763
+ if (params.sessionId === shared.sessionId) {
27764
+ shared.modelSelectionState = adoptReportedConfigOptions(
27765
+ shared.modelSelectionState,
27766
+ reportedOptions
27767
+ );
27768
+ }
27769
+ }
27580
27770
  const parsed = parseSessionUpdateEvent(params);
27581
27771
  if (parsed.modeId && shared.modeState) {
27582
27772
  shared.modeState = {
@@ -27892,7 +28082,10 @@ function createAcpExecutor(options) {
27892
28082
  droppedReplayUpdates: 0,
27893
28083
  ownSessionIds: /* @__PURE__ */ new Set(),
27894
28084
  childSessions: createChildSessionRegistry(),
27895
- sessionOpensInFlight: 0
28085
+ sessionOpensInFlight: 0,
28086
+ configOptionUpdates: /* @__PURE__ */ new Map(),
28087
+ optionWriteStop: void 0,
28088
+ configUpdateSeq: 0
27896
28089
  };
27897
28090
  const interruptedTurnIds = /* @__PURE__ */ new Set();
27898
28091
  const approvals = createApprovalGate({
@@ -28041,8 +28234,16 @@ function createAcpExecutor(options) {
28041
28234
  };
28042
28235
  options.readable.once("end", onInputDead);
28043
28236
  options.readable.once("error", onInputDead);
28044
- const { applyRequestedModel, start, requireStarted, setNativeMode } = createExecutorSession({
28237
+ const {
28238
+ applyRequestedModel,
28239
+ modelDefaultEffort,
28240
+ writeConfigOptionReported,
28241
+ start,
28242
+ requireStarted,
28243
+ setNativeMode
28244
+ } = createExecutorSession({
28045
28245
  options,
28246
+ hooks,
28046
28247
  connection,
28047
28248
  isLive,
28048
28249
  emit,
@@ -28068,6 +28269,8 @@ function createAcpExecutor(options) {
28068
28269
  threadMutex,
28069
28270
  promptMutex,
28070
28271
  applyRequestedModel,
28272
+ modelDefaultEffort,
28273
+ writeConfigOptionReported,
28071
28274
  notifyPromptSettled,
28072
28275
  shared
28073
28276
  });
@@ -29149,6 +29352,26 @@ function grokReasoningEffortDescriptor(modelMeta) {
29149
29352
  ...currentValue === void 0 ? {} : { currentValue }
29150
29353
  };
29151
29354
  }
29355
+ function grokReasoningEffortKnown(modelMeta) {
29356
+ if (!isRecord10(modelMeta)) return "unknown";
29357
+ if (modelMeta["supportsReasoningEffort"] === false) return "none";
29358
+ if (modelMeta["supportsReasoningEffort"] !== true) return "unknown";
29359
+ const table = modelMeta["reasoningEfforts"];
29360
+ if (!Array.isArray(table)) return "unknown";
29361
+ const readable = table.every(
29362
+ (entry) => isRecord10(entry) && (trimmed2(entry["value"]) ?? trimmed2(entry["id"])) !== void 0
29363
+ );
29364
+ if (!readable) return "unknown";
29365
+ return table.length === 0 ? "none" : "listed";
29366
+ }
29367
+ function grokModelDefaultEffort(input2) {
29368
+ const models = isRecord10(input2.setup) ? input2.setup["models"] : void 0;
29369
+ const available = isRecord10(models) ? models["availableModels"] : void 0;
29370
+ if (!Array.isArray(available)) return void 0;
29371
+ const model = available.find((entry) => isRecord10(entry) && entry["modelId"] === input2.modelId);
29372
+ const current = isRecord10(model) ? grokReasoningEffortDescriptor(model["_meta"])?.currentValue : void 0;
29373
+ return typeof current === "string" ? current : void 0;
29374
+ }
29152
29375
  function grokReasoningEffortArgs(effort) {
29153
29376
  const value = effort?.trim();
29154
29377
  return value === void 0 || value.length === 0 ? [] : ["--reasoning-effort", value];
@@ -29711,7 +29934,8 @@ function grokAdvertisedModels(initializeResult) {
29711
29934
  name: trimmed2(entry["name"]),
29712
29935
  isDefault: state.currentModelId !== void 0 && modelId === state.currentModelId,
29713
29936
  contextWindow,
29714
- reasoningEffort: grokReasoningEffortDescriptor(modelMeta)
29937
+ reasoningEffort: grokReasoningEffortDescriptor(modelMeta),
29938
+ reasoningEffortKnown: grokReasoningEffortKnown(modelMeta)
29715
29939
  });
29716
29940
  }
29717
29941
  return models;
@@ -30020,7 +30244,11 @@ function grokProbeModels(initializeResult) {
30020
30244
  ...model.name === void 0 ? {} : { name: model.name },
30021
30245
  ...model.isDefault ? { isDefault: true } : {},
30022
30246
  ...model.contextWindow === void 0 ? {} : { contextWindow: model.contextWindow },
30023
- ...model.reasoningEffort === void 0 ? {} : { capabilities: { optionDescriptors: [model.reasoningEffort] } }
30247
+ ...model.reasoningEffortKnown === "unknown" || model.reasoningEffortKnown === "listed" && model.reasoningEffort === void 0 ? {} : {
30248
+ capabilities: {
30249
+ optionDescriptors: model.reasoningEffort === void 0 ? [] : [model.reasoningEffort]
30250
+ }
30251
+ }
30024
30252
  }));
30025
30253
  }
30026
30254
  var failureDetail = (error62) => (error62 instanceof Error ? error62.message : String(error62)).trim() || void 0;
@@ -30466,6 +30694,7 @@ var createGrokInstance = (config2, host) => {
30466
30694
  authMethodId: resolveGrokAuthMethodId,
30467
30695
  hooks: {
30468
30696
  promptSettlement: turnUsage.settlement,
30697
+ modelDefaultEffort: grokModelDefaultEffort,
30469
30698
  // 派发子 agent 的调用由子 agent 名单接管,不再画成普通工具。
30470
30699
  onToolCallUpdated: (update) => ({
30471
30700
  events: buildGrokBackgroundTaskEvents({
@@ -30609,10 +30838,15 @@ var createGrokInstance = (config2, host) => {
30609
30838
  signal?.addEventListener("abort", onAbort);
30610
30839
  try {
30611
30840
  const requestedModelId = modelIdFromSelection(input2.modelSelection);
30841
+ const requestedEffort = modelSelectionStringOption(
30842
+ input2.modelSelection,
30843
+ GROK_REASONING_OPTION_ID
30844
+ );
30612
30845
  const turn = await dispatchAcpPrompt(booked.executor, {
30613
30846
  text: input2.input ?? "",
30614
30847
  expectedActiveTurnId: input2.expectedTurnId,
30615
- ...requestedModelId === void 0 ? {} : { requestedModelId }
30848
+ ...requestedModelId === void 0 ? {} : { requestedModelId },
30849
+ ...requestedEffort === void 0 ? {} : { requestedEffort }
30616
30850
  });
30617
30851
  booked.turns.push({ id: turn.turnId, items: [] });
30618
30852
  return {
@@ -30696,7 +30930,7 @@ var executors = [
30696
30930
  ];
30697
30931
  var index_default = definePlugin({
30698
30932
  id: "agent-grok",
30699
- apiVersion: 49,
30933
+ apiVersion: 51,
30700
30934
  executors,
30701
30935
  activate(ctx) {
30702
30936
  ctx.log.info("grok executor ready");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ycodium-ai/agent-grok",
3
- "version": "0.2.3614",
3
+ "version": "0.2.3671",
4
4
  "description": "Grok executor: grok CLI 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-ai/plugin-api": "0.2.3614",
25
- "ycodium-acp-executor": "0.2.3614",
26
- "ycodium-executor-testing": "0.0.0"
24
+ "@ycodium-ai/plugin-api": "0.2.3671",
25
+ "ycodium-executor-testing": "0.0.0",
26
+ "ycodium-acp-executor": "0.2.3671"
27
27
  },
28
28
  "ycodium": {
29
29
  "plugin": "./dist/index.js",