@ycodium-ai/agent-claude 0.2.3484 → 0.2.3502

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 (3) hide show
  1. package/README.md +21 -0
  2. package/dist/index.js +929 -721
  3. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -60,6 +60,8 @@ var CLAUDE_CARD = {
60
60
  reportsSpawnedAgents: true,
61
61
  // 用户点名的技能:最后一个原生 `/name` 派发,更早的读 SKILL.md 内联展开(claudeSkillDispatch.ts)。
62
62
  handlesSkillInvocations: true,
63
+ // 会话的其它文件夹经 `--add-dir` 声明成工作目录,并让 CLI 加载它们的 CLAUDE.md 与规则(workingDirectories.ts)。
64
+ acceptsAdditionalDirectories: true,
63
65
  // 出处:Anthropic Messages API 的视觉文档——单张图片 5 MB、宽高各不超过 8000 像素,超了
64
66
  // API 直接拒掉整轮。5 MB 量的是 base64 之后的长度(SDK 把图片以 base64 块发出),所以
65
67
  // 原始字节上限是它的 3/4。宿主据此把更大的图退化成路径(Claude 自己的 Read 工具会读那个
@@ -347,8 +349,8 @@ async function* historyBytes(path, start, end, signal) {
347
349
  async function* historyText(path, range, signal) {
348
350
  const decoder = new TextDecoder("utf-8", { fatal: true });
349
351
  for await (const bytes of historyBytes(path, range.start, range.end, signal)) {
350
- const text = decoder.decode(bytes, { stream: true });
351
- if (text) yield text;
352
+ const text2 = decoder.decode(bytes, { stream: true });
353
+ if (text2) yield text2;
352
354
  }
353
355
  const last = decoder.decode();
354
356
  if (last) yield last;
@@ -691,33 +693,33 @@ var OversizedRecordParser = class {
691
693
  this.#strCap = cap;
692
694
  }
693
695
  /** 字符串里的一个解码后片段;按保留规则累积。 */
694
- #append(text) {
695
- if (text.length === 0) return;
696
+ #append(text2) {
697
+ if (text2.length === 0) return;
696
698
  if (this.#stringIsKey) {
697
699
  if (this.#keyOverflow) return;
698
- this.#key += text;
700
+ this.#key += text2;
699
701
  if (this.#key.length > MAX_KEY_CHARS) {
700
702
  this.#keyOverflow = true;
701
703
  this.#key = "";
702
704
  }
703
705
  return;
704
706
  }
705
- this.#strLength += text.length;
706
- this.#strObserver?.onChunk?.(text);
707
+ this.#strLength += text2.length;
708
+ this.#strObserver?.onChunk?.(text2);
707
709
  if (!this.#strRetain) return;
708
710
  const cap = this.#strCap;
709
711
  if (cap === void 0) {
710
- this.#strHead += text;
711
- this.#charge(text.length);
712
+ this.#strHead += text2;
713
+ this.#charge(text2.length);
712
714
  return;
713
715
  }
714
716
  if (this.#strHead.length < cap) {
715
717
  const room = cap - this.#strHead.length;
716
- this.#strHead += text.slice(0, room);
717
- text = text.slice(room);
718
- if (text.length === 0) return;
718
+ this.#strHead += text2.slice(0, room);
719
+ text2 = text2.slice(room);
720
+ if (text2.length === 0) return;
719
721
  }
720
- this.#strTail += text;
722
+ this.#strTail += text2;
721
723
  if (this.#strTail.length > cap * 2) this.#strTail = this.#strTail.slice(-cap);
722
724
  }
723
725
  #consumeString(chunk, start) {
@@ -807,35 +809,35 @@ var OversizedRecordParser = class {
807
809
  return;
808
810
  }
809
811
  const cap = this.#strCap;
810
- let text = this.#strHead;
812
+ let text2 = this.#strHead;
811
813
  if (cap !== void 0) {
812
814
  let tail = this.#strTail;
813
815
  if (tail.length > cap) tail = tail.slice(-cap);
814
- if (this.#strLength > text.length + tail.length) {
815
- if (isHighSurrogate(text.charCodeAt(text.length - 1))) text = text.slice(0, -1);
816
+ if (this.#strLength > text2.length + tail.length) {
817
+ if (isHighSurrogate(text2.charCodeAt(text2.length - 1))) text2 = text2.slice(0, -1);
816
818
  if (isLowSurrogate(tail.charCodeAt(0))) tail = tail.slice(1);
817
- const omitted = this.#strLength - text.length - tail.length;
818
- text = `${text}
819
+ const omitted = this.#strLength - text2.length - tail.length;
820
+ text2 = `${text2}
819
821
  [\u2026 ${omitted} chars omitted \u2026]
820
822
  ${tail}`;
821
823
  } else {
822
- text = `${text}${tail}`;
824
+ text2 = `${text2}${tail}`;
823
825
  }
824
- this.#charge(text.length);
826
+ this.#charge(text2.length);
825
827
  }
826
828
  this.#strHead = "";
827
829
  this.#strTail = "";
828
- this.#store(text);
830
+ this.#store(text2);
829
831
  }
830
832
  #finishBare() {
831
- const text = this.#bare;
833
+ const text2 = this.#bare;
832
834
  this.#bare = "";
833
835
  const { mode, path } = this.#childMode(false);
834
836
  let value;
835
- if (text === "true") value = true;
836
- else if (text === "false") value = false;
837
- else if (text === "null") value = null;
838
- else if (NUMBER_PATTERN.test(text)) value = Number(text);
837
+ if (text2 === "true") value = true;
838
+ else if (text2 === "false") value = false;
839
+ else if (text2 === "null") value = null;
840
+ else if (NUMBER_PATTERN.test(text2)) value = Number(text2);
839
841
  else this.#fail();
840
842
  const observer = this.#fields.find(
841
843
  (field) => field.path.length === path.length && matchesPrefix(field.path, path, path.length)
@@ -891,14 +893,14 @@ async function* readJsonlEntries(fs2, path, options) {
891
893
  pending2 = 0;
892
894
  return next;
893
895
  };
894
- const append = (text) => {
895
- if (text.length === 0) return;
896
+ const append = (text2) => {
897
+ if (text2.length === 0) return;
896
898
  if (parser !== null) {
897
- parser.write(text);
899
+ parser.write(text2);
898
900
  return;
899
901
  }
900
- pieces.push(text);
901
- pending2 += text.length;
902
+ pieces.push(text2);
903
+ pending2 += text2.length;
902
904
  if (pending2 > options.maxLineChars) parser = startOversized();
903
905
  };
904
906
  const takeLine = () => {
@@ -914,15 +916,15 @@ async function* readJsonlEntries(fs2, path, options) {
914
916
  if (line.endsWith("\r")) line = line.slice(0, -1);
915
917
  return line.length === 0 ? null : { line };
916
918
  };
917
- const feed = function* (text) {
919
+ const feed = function* (text2) {
918
920
  let from = 0;
919
- while (from <= text.length) {
920
- const newline = text.indexOf("\n", from);
921
+ while (from <= text2.length) {
922
+ const newline = text2.indexOf("\n", from);
921
923
  if (newline < 0) {
922
- append(text.slice(from));
924
+ append(text2.slice(from));
923
925
  return;
924
926
  }
925
- append(text.slice(from, newline));
927
+ append(text2.slice(from, newline));
926
928
  const entry = takeLine();
927
929
  if (entry !== null) yield entry;
928
930
  from = newline + 1;
@@ -948,9 +950,9 @@ var textFields = (paths, accept) => {
948
950
  return paths.map((path) => ({
949
951
  path,
950
952
  scalarOnly: true,
951
- onChunk: (text) => {
953
+ onChunk: (text2) => {
952
954
  begin();
953
- accept(text);
955
+ accept(text2);
954
956
  },
955
957
  onEnd: () => {
956
958
  begin();
@@ -968,16 +970,16 @@ var createHistoryRecordAccumulator = (layout) => {
968
970
  const parser = new OversizedRecordParser(
969
971
  [
970
972
  ...selected.fields,
971
- ...textFields(selected.textPaths, (text) => {
972
- messageTotal += text.length;
973
+ ...textFields(selected.textPaths, (text2) => {
974
+ messageTotal += text2.length;
973
975
  })
974
976
  ],
975
977
  Number.MAX_SAFE_INTEGER,
976
978
  { pruneEmpty: true }
977
979
  );
978
- const accept = (text) => {
979
- rawTotal += text.length;
980
- parser.write(text);
980
+ const accept = (text2) => {
981
+ rawTotal += text2.length;
982
+ parser.write(text2);
981
983
  };
982
984
  return {
983
985
  write: (bytes) => {
@@ -1019,11 +1021,11 @@ async function* historyRecordWindows(path, info, mode, from, until, limit, layou
1019
1021
  let offset = from;
1020
1022
  let pending2 = "";
1021
1023
  const ready = [];
1022
- const accept = (text) => {
1024
+ const accept = (text2) => {
1023
1025
  const begin = Math.max(0, from - seen);
1024
- const end = Math.min(text.length, until - seen);
1025
- seen += text.length;
1026
- if (end > begin) pending2 += text.slice(begin, end);
1026
+ const end = Math.min(text2.length, until - seen);
1027
+ seen += text2.length;
1028
+ if (end > begin) pending2 += text2.slice(begin, end);
1027
1029
  while (pending2.length >= limit) {
1028
1030
  let size = limit;
1029
1031
  const code = pending2.charCodeAt(size - 1);
@@ -1037,9 +1039,9 @@ async function* historyRecordWindows(path, info, mode, from, until, limit, layou
1037
1039
  const parser = mode === "raw" ? void 0 : new OversizedRecordParser(textFields(selected.textPaths, accept), Number.MAX_SAFE_INTEGER, {
1038
1040
  pruneEmpty: true
1039
1041
  });
1040
- for await (const text of historyText(path, info.range, signal)) {
1041
- if (parser === void 0) accept(text);
1042
- else parser.write(text);
1042
+ for await (const text2 of historyText(path, info.range, signal)) {
1043
+ if (parser === void 0) accept(text2);
1044
+ else parser.write(text2);
1043
1045
  while (ready.length > 0) yield ready.shift();
1044
1046
  if (seen >= until) break;
1045
1047
  }
@@ -1051,7 +1053,7 @@ var verifyHistoryRecord = async (path, range, layout, signal) => {
1051
1053
  pruneEmpty: true
1052
1054
  });
1053
1055
  try {
1054
- for await (const text of historyText(path, range, signal)) parser.write(text);
1056
+ for await (const text2 of historyText(path, range, signal)) parser.write(text2);
1055
1057
  } catch (error) {
1056
1058
  if (!range.terminated && error instanceof TypeError) return false;
1057
1059
  throw error;
@@ -1090,7 +1092,7 @@ var width = (n) => String(n).padStart(20, "0");
1090
1092
  var high = (n) => n >= 55296 && n <= 56319;
1091
1093
  var low = (n) => n >= 56320 && n <= 57343;
1092
1094
  var totalOf = (info, index) => info.items[index].text === "raw" ? info.rawTotal : info.messageTotal;
1093
- var nativeItem = (identity, info, index, offset, text) => {
1095
+ var nativeItem = (identity, info, index, offset, text2) => {
1094
1096
  const { text: _mode, ...item } = info.items[index];
1095
1097
  const total = totalOf(info, index);
1096
1098
  const position = { e: identity, p: info.range.start, k: index, h: info.hash };
@@ -1099,13 +1101,13 @@ var nativeItem = (identity, info, index, offset, text) => {
1099
1101
  id: `${identity}:${info.range.start}:${index}`,
1100
1102
  orderKey: `${width(info.range.start)}:${width(index)}`,
1101
1103
  beforeCursor: encode({ ...position, o: offset }),
1102
- afterCursor: encode({ ...position, o: offset + text.length }),
1103
- text,
1104
+ afterCursor: encode({ ...position, o: offset + text2.length }),
1105
+ text: text2,
1104
1106
  textWindow: {
1105
1107
  offset,
1106
- returned: text.length,
1108
+ returned: text2.length,
1107
1109
  total,
1108
- nextOffset: offset + text.length < total ? offset + text.length : null
1110
+ nextOffset: offset + text2.length < total ? offset + text2.length : null
1109
1111
  }
1110
1112
  };
1111
1113
  };
@@ -1129,7 +1131,7 @@ var readWindow = async (path, info, index, from, until, layout, signal, cached)
1129
1131
  cached.text = parts.join("");
1130
1132
  cached.mode = mode;
1131
1133
  }
1132
- let text = cached.mode === mode ? cached.text?.slice(base, end) ?? "" : "";
1134
+ let text2 = cached.mode === mode ? cached.text?.slice(base, end) ?? "" : "";
1133
1135
  if (cached.mode !== mode) {
1134
1136
  for await (const window2 of historyRecordWindows(
1135
1137
  path,
@@ -1141,13 +1143,13 @@ var readWindow = async (path, info, index, from, until, layout, signal, cached)
1141
1143
  layout,
1142
1144
  signal
1143
1145
  ))
1144
- text += window2.text;
1146
+ text2 += window2.text;
1145
1147
  }
1146
1148
  let startIndex = from - base;
1147
1149
  let endIndex = until - base;
1148
- if (low(text.charCodeAt(startIndex)) && high(text.charCodeAt(startIndex - 1))) startIndex -= 1;
1149
- if (low(text.charCodeAt(endIndex)) && high(text.charCodeAt(endIndex - 1))) endIndex -= 1;
1150
- return { offset: base + startIndex, text: text.slice(startIndex, endIndex) };
1150
+ if (low(text2.charCodeAt(startIndex)) && high(text2.charCodeAt(startIndex - 1))) startIndex -= 1;
1151
+ if (low(text2.charCodeAt(endIndex)) && high(text2.charCodeAt(endIndex - 1))) endIndex -= 1;
1152
+ return { offset: base + startIndex, text: text2.slice(startIndex, endIndex) };
1151
1153
  };
1152
1154
  async function openJsonlHistory(path, layout, signal) {
1153
1155
  const controller = new AbortController();
@@ -1544,10 +1546,10 @@ async function readClaudeTranscriptDigest(filePath) {
1544
1546
  continue;
1545
1547
  }
1546
1548
  if (typed.type !== "user") continue;
1547
- const text = textFromContent(typed.message?.content);
1548
- if (text.length === 0) continue;
1549
+ const text2 = textFromContent(typed.message?.content);
1550
+ if (text2.length === 0) continue;
1549
1551
  turnCount += 1;
1550
- if (preview === null) preview = text;
1552
+ if (preview === null) preview = text2;
1551
1553
  }
1552
1554
  } catch {
1553
1555
  return null;
@@ -1729,20 +1731,20 @@ async function readClaudeSessionRecord(input) {
1729
1731
  const content = message.content;
1730
1732
  const blocks = typeof content === "string" ? [{ type: "text", text: content }] : Array.isArray(content) ? content : [];
1731
1733
  const hasToolResult = blocks.some((block) => isRecord(block) && block.type === "tool_result");
1732
- const text = blocks.flatMap((block) => {
1734
+ const text2 = blocks.flatMap((block) => {
1733
1735
  const typed = isRecord(block) ? block : void 0;
1734
1736
  if (!typed) return [];
1735
1737
  if (typed.type === "text" && typeof typed.text === "string") return [typed.text];
1736
1738
  return [];
1737
1739
  }).join("\n\n").trim();
1738
- if (text.length === 0) continue;
1740
+ if (text2.length === 0) continue;
1739
1741
  if (value.type === "user" && hasToolResult) continue;
1740
1742
  const role = value.type === "assistant" ? "assistant" : "user";
1741
1743
  if (role === "assistant" && typeof message.model === "string") model = message.model;
1742
1744
  const timestamp = typeof value.timestamp === "string" ? Date.parse(value.timestamp) : typeof value.timestamp === "number" ? value.timestamp : void 0;
1743
1745
  messages.push({
1744
1746
  role,
1745
- text,
1747
+ text: text2,
1746
1748
  ...timestamp !== void 0 && Number.isFinite(timestamp) ? { createdAt: timestamp } : {}
1747
1749
  });
1748
1750
  }
@@ -1789,8 +1791,8 @@ var claudeHistoryLayout = (input) => () => {
1789
1791
  ].map((key) => ({ path: [key], scalarOnly: true, maxChars: 2048 })),
1790
1792
  {
1791
1793
  path: ["message", "content", "*", "type"],
1792
- onChunk: (text) => {
1793
- blockType = (blockType + text).slice(0, 128);
1794
+ onChunk: (text2) => {
1795
+ blockType = (blockType + text2).slice(0, 128);
1794
1796
  },
1795
1797
  onEnd: () => {
1796
1798
  hasTools ||= blockType === "tool_use" || blockType === "tool_result";
@@ -1800,20 +1802,20 @@ var claudeHistoryLayout = (input) => () => {
1800
1802
  },
1801
1803
  {
1802
1804
  path: ["message", "content", "*", "name"],
1803
- onChunk: (text) => {
1804
- name = (name + text).slice(0, 256);
1805
+ onChunk: (text2) => {
1806
+ name = (name + text2).slice(0, 256);
1805
1807
  }
1806
1808
  },
1807
1809
  {
1808
1810
  path: ["message", "content", "*", "id"],
1809
- onChunk: (text) => {
1810
- toolCallId = (toolCallId + text).slice(0, 256);
1811
+ onChunk: (text2) => {
1812
+ toolCallId = (toolCallId + text2).slice(0, 256);
1811
1813
  }
1812
1814
  },
1813
1815
  {
1814
1816
  path: ["message", "content", "*", "tool_use_id"],
1815
- onChunk: (text) => {
1816
- toolCallId = (toolCallId + text).slice(0, 256);
1817
+ onChunk: (text2) => {
1818
+ toolCallId = (toolCallId + text2).slice(0, 256);
1817
1819
  }
1818
1820
  }
1819
1821
  ],
@@ -1867,7 +1869,7 @@ var openClaudeHistory = (input) => {
1867
1869
  };
1868
1870
 
1869
1871
  // src/claudeInstance.ts
1870
- import * as NodeOS9 from "node:os";
1872
+ import * as NodeOS10 from "node:os";
1871
1873
  import * as NodePath19 from "node:path";
1872
1874
 
1873
1875
  // ../../node_modules/.pnpm/@anthropic-ai+claude-agent-sdk@0.3.170_@anthropic-ai+sdk@0.93.0_zod@4.6.5__@modelcontex_64204813d4dba2ebc464c3aa751485a4/node_modules/@anthropic-ai/claude-agent-sdk/sdk.mjs
@@ -21301,15 +21303,19 @@ function HU(e, t) {
21301
21303
  }
21302
21304
 
21303
21305
  // src/claudeInstance.ts
21304
- import { mergeLaunchEnv as mergeLaunchEnv3 } from "@ycodium-ai/plugin-api/executor";
21306
+ import { mergeLaunchEnv as mergeLaunchEnv4 } from "@ycodium-ai/plugin-api/executor";
21307
+ import { isOperationCleanupError as isOperationCleanupError3 } from "@ycodium-ai/plugin-api/process-operation";
21305
21308
 
21306
21309
  // src/claudeProbe.ts
21307
21310
  import {
21308
21311
  describeExecutorSpawnFailure,
21309
21312
  ExecutorSpawnAbortedError,
21310
- ExecutorSpawnRefusedError,
21311
- raceAbort
21313
+ ExecutorSpawnRefusedError
21312
21314
  } from "@ycodium-ai/plugin-api/executor";
21315
+ import {
21316
+ runProcessOperation,
21317
+ isOperationCleanupError
21318
+ } from "@ycodium-ai/plugin-api/process-operation";
21313
21319
 
21314
21320
  // src/claudeAccount.ts
21315
21321
  import * as NodeFS4 from "node:fs";
@@ -21368,10 +21374,10 @@ function claudeCredentialCandidatePaths(input) {
21368
21374
  NodePath5.join(input.homePath, ".credentials.json")
21369
21375
  ] : [NodePath5.join(input.credentialsDirPath, ".credentials.json")];
21370
21376
  }
21371
- function readClaudeAccount(credentialsDirPath) {
21377
+ function readClaudeAccount(credentialsDirPath, credentialCandidates) {
21372
21378
  const accountDirectory = resolveClaudeAccountDirectory(credentialsDirPath);
21373
21379
  const exists = directoryExists(accountDirectory);
21374
- const candidates = claudeCredentialCandidatePaths({
21380
+ const candidates = credentialCandidates ?? claudeCredentialCandidatePaths({
21375
21381
  homePath: NodeOS2.homedir(),
21376
21382
  credentialsDirPath
21377
21383
  });
@@ -21398,6 +21404,14 @@ function readClaudeAccount(credentialsDirPath) {
21398
21404
  };
21399
21405
  }
21400
21406
 
21407
+ // src/usageRead/quota.ts
21408
+ import * as NodeFS5 from "node:fs";
21409
+ import * as NodePath8 from "node:path";
21410
+ import { mergeLaunchEnv, setsOwnModelConnection } from "@ycodium-ai/plugin-api/executor";
21411
+ import {
21412
+ parseRetryAfterMs
21413
+ } from "@ycodium-ai/plugin-api/usage";
21414
+
21401
21415
  // src/claudeHome.ts
21402
21416
  import * as NodeOS4 from "node:os";
21403
21417
  import * as NodePath7 from "node:path";
@@ -21547,9 +21561,171 @@ function claudeFailureHint(input) {
21547
21561
  return void 0;
21548
21562
  }
21549
21563
 
21564
+ // src/usageRead/quota.ts
21565
+ var CLAUDE_QUOTA_URL = "https://api.anthropic.com/api/oauth/usage";
21566
+ var REQUEST_TIMEOUT_MS = 1e4;
21567
+ var HOUR_MS = 60 * 60 * 1e3;
21568
+ var CLAUDE_QUOTA_WINDOWS = [
21569
+ { field: "five_hour", id: "session", labelKey: "usage.window.session", windowMs: 5 * HOUR_MS },
21570
+ { field: "seven_day", id: "weekly", labelKey: "usage.window.weekly", windowMs: 7 * 24 * HOUR_MS }
21571
+ ];
21572
+ var isRecord3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
21573
+ var launchEnvironment = (recipe) => mergeLaunchEnv({
21574
+ inherited: process.env,
21575
+ instanceTable: recipe.environment,
21576
+ handOverrides: claudeHomeVariables(resolveClaudeHomeLayout(recipe))
21577
+ });
21578
+ var claudeCredentialFileCandidates = (recipe) => {
21579
+ const environment = launchEnvironment(recipe);
21580
+ const credentialsDir = resolveClaudeCredentialsDirPath(
21581
+ environment[CLAUDE_CREDENTIALS_DIR_ENVIRONMENT_VARIABLE] ?? ""
21582
+ );
21583
+ if (credentialsDir !== null) return [NodePath8.join(credentialsDir, ".credentials.json")];
21584
+ const configDir = resolveClaudeConfigDirPath(recipe.homePath, environment);
21585
+ const home = resolveClaudeHomePath(recipe.homePath);
21586
+ return [
21587
+ .../* @__PURE__ */ new Set([
21588
+ NodePath8.join(configDir, ".credentials.json"),
21589
+ NodePath8.join(home, ".claude", ".credentials.json"),
21590
+ NodePath8.join(home, ".credentials.json")
21591
+ ])
21592
+ ];
21593
+ };
21594
+ var readString = (value) => typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
21595
+ var readLoginCredential = (candidates) => {
21596
+ for (const path of candidates) {
21597
+ let raw;
21598
+ try {
21599
+ raw = NodeFS5.readFileSync(path, "utf8");
21600
+ } catch {
21601
+ continue;
21602
+ }
21603
+ if (raw.trim().length === 0) continue;
21604
+ let parsed;
21605
+ try {
21606
+ parsed = JSON.parse(raw);
21607
+ } catch {
21608
+ continue;
21609
+ }
21610
+ const oauth = isRecord3(parsed) ? parsed["claudeAiOauth"] : void 0;
21611
+ if (!isRecord3(oauth)) continue;
21612
+ const accessToken = readString(oauth["accessToken"]);
21613
+ if (accessToken === void 0) continue;
21614
+ return {
21615
+ accessToken,
21616
+ subscriptionType: readString(oauth["subscriptionType"]),
21617
+ rateLimitTier: readString(oauth["rateLimitTier"]),
21618
+ path
21619
+ };
21620
+ }
21621
+ return null;
21622
+ };
21623
+ var claudePlanName = (credential) => {
21624
+ const type4 = credential.subscriptionType;
21625
+ if (type4 === void 0) return void 0;
21626
+ const name = `${type4.charAt(0).toUpperCase()}${type4.slice(1)}`;
21627
+ const multiplier = /(?:^|_)(\d+x)$/.exec(credential.rateLimitTier ?? "")?.[1];
21628
+ return multiplier === void 0 ? name : `${name} ${multiplier}`;
21629
+ };
21630
+ var toIso = (value) => {
21631
+ if (typeof value !== "string") return null;
21632
+ const parsed = Date.parse(value);
21633
+ return Number.isNaN(parsed) ? null : new Date(parsed).toISOString();
21634
+ };
21635
+ var readClaudeQuotaWindows = (body) => CLAUDE_QUOTA_WINDOWS.flatMap((spec) => {
21636
+ const row = body[spec.field];
21637
+ if (!isRecord3(row)) return [];
21638
+ const utilization = row["utilization"];
21639
+ if (typeof utilization !== "number" || !Number.isFinite(utilization)) return [];
21640
+ return [
21641
+ {
21642
+ id: spec.id,
21643
+ label: { key: spec.labelKey },
21644
+ usedPercent: Math.min(100, Math.max(0, utilization)),
21645
+ resetsAt: toIso(row["resets_at"]),
21646
+ windowMs: spec.windowMs
21647
+ }
21648
+ ];
21649
+ });
21650
+ var readClaudeQuota = async (request) => {
21651
+ const { recipe } = request;
21652
+ const connectivity = CLAUDE_CARD.modelConnectivity;
21653
+ const environmentRows = Object.entries(recipe.environment).map(([name, value]) => ({
21654
+ name,
21655
+ value
21656
+ }));
21657
+ if (connectivity !== void 0 && setsOwnModelConnection(connectivity, environmentRows)) {
21658
+ const changesEndpoint = setsOwnModelConnection(
21659
+ {
21660
+ baseUrlEnvKey: connectivity.baseUrlEnvKey,
21661
+ backendSwitchEnvKeys: connectivity.backendSwitchEnvKeys
21662
+ },
21663
+ environmentRows
21664
+ );
21665
+ return {
21666
+ status: "unavailable",
21667
+ reason: changesEndpoint ? "customEndpoint" : "unsupportedLogin"
21668
+ };
21669
+ }
21670
+ const credential = readLoginCredential(claudeCredentialFileCandidates(recipe));
21671
+ if (credential === null) return { status: "unavailable", reason: "missingCredentials" };
21672
+ const now = request.now ?? Date.now;
21673
+ let response;
21674
+ try {
21675
+ response = await request.fetch(CLAUDE_QUOTA_URL, {
21676
+ method: "GET",
21677
+ headers: {
21678
+ Accept: "application/json",
21679
+ "anthropic-beta": "oauth-2025-04-20",
21680
+ Authorization: `Bearer ${credential.accessToken}`
21681
+ },
21682
+ redirect: "error",
21683
+ signal: request.signal === void 0 ? AbortSignal.timeout(REQUEST_TIMEOUT_MS) : AbortSignal.any([request.signal, AbortSignal.timeout(REQUEST_TIMEOUT_MS)])
21684
+ });
21685
+ } catch (cause) {
21686
+ if (request.signal?.aborted === true) throw cause;
21687
+ return { status: "unavailable", reason: "unreachable" };
21688
+ }
21689
+ const release = () => {
21690
+ void response.body?.cancel().catch(() => void 0);
21691
+ };
21692
+ if (response.status === 429) {
21693
+ const retryAfterMs = parseRetryAfterMs(response.headers.get("Retry-After"), now());
21694
+ release();
21695
+ return {
21696
+ status: "unavailable",
21697
+ reason: "rateLimited",
21698
+ ...retryAfterMs === null ? {} : { retryAfterMs }
21699
+ };
21700
+ }
21701
+ if (response.status === 401 || response.status === 403) {
21702
+ release();
21703
+ return { status: "unavailable", reason: "authRejected" };
21704
+ }
21705
+ if (response.status < 200 || response.status >= 300) {
21706
+ release();
21707
+ return { status: "unavailable", reason: "unreachable" };
21708
+ }
21709
+ let body;
21710
+ try {
21711
+ body = JSON.parse(await response.text());
21712
+ } catch {
21713
+ return { status: "unavailable", reason: "unreachable" };
21714
+ }
21715
+ if (!isRecord3(body)) return { status: "unavailable", reason: "unreachable" };
21716
+ const plan = claudePlanName(credential);
21717
+ return {
21718
+ status: "available",
21719
+ ...plan === void 0 ? {} : { plan },
21720
+ windows: readClaudeQuotaWindows(body),
21721
+ balances: [],
21722
+ accountKey: credential.path
21723
+ };
21724
+ };
21725
+
21550
21726
  // src/claudeSkills.ts
21551
21727
  import * as NodeFSP3 from "node:fs/promises";
21552
- import * as NodePath8 from "node:path";
21728
+ import * as NodePath9 from "node:path";
21553
21729
  import * as NodeProcess3 from "node:process";
21554
21730
  var FRONT_MATTER_KEY = /^(description|disable-model-invocation|user-invocable|argument-hint):[ \t]*(.*)$/;
21555
21731
  var BLOCK_SCALAR = /^[|>][+-]?$/;
@@ -21615,7 +21791,7 @@ function claudeManagedSettingsPath(platform4, environment) {
21615
21791
  }
21616
21792
  if (platform4 === "win32") {
21617
21793
  const programData = environment["PROGRAMDATA"]?.trim();
21618
- return programData ? NodePath8.join(programData, "ClaudeCode", "managed-settings.json") : void 0;
21794
+ return programData ? NodePath9.join(programData, "ClaudeCode", "managed-settings.json") : void 0;
21619
21795
  }
21620
21796
  return "/etc/claude-code/managed-settings.json";
21621
21797
  }
@@ -21623,44 +21799,44 @@ function skillOverrideSettingsPaths(configDirPath, cwd, platform4, environment,
21623
21799
  const managedPath = claudeManagedSettingsPath(platform4, environment);
21624
21800
  const root = repositoryRoot !== void 0 && repositoryRoot !== cwd ? repositoryRoot : void 0;
21625
21801
  return [
21626
- NodePath8.join(configDirPath, "settings.json"),
21802
+ NodePath9.join(configDirPath, "settings.json"),
21627
21803
  ...cwd ? [
21628
- NodePath8.join(cwd, ".claude", "settings.json"),
21629
- NodePath8.join(cwd, ".claude", "settings.local.json")
21804
+ NodePath9.join(cwd, ".claude", "settings.json"),
21805
+ NodePath9.join(cwd, ".claude", "settings.local.json")
21630
21806
  ] : [],
21631
- ...root ? [NodePath8.join(root, ".claude", "settings.local.json")] : [],
21807
+ ...root ? [NodePath9.join(root, ".claude", "settings.local.json")] : [],
21632
21808
  ...managedPath ? [managedPath] : []
21633
21809
  ];
21634
21810
  }
21635
21811
  async function findRepositoryRoot(cwd) {
21636
- let current = NodePath8.resolve(cwd);
21812
+ let current = NodePath9.resolve(cwd);
21637
21813
  for (; ; ) {
21638
- const found = await NodeFSP3.stat(NodePath8.join(current, ".git")).then(
21814
+ const found = await NodeFSP3.stat(NodePath9.join(current, ".git")).then(
21639
21815
  () => true,
21640
21816
  () => false
21641
21817
  );
21642
21818
  if (found) return current;
21643
- const parent = NodePath8.dirname(current);
21819
+ const parent = NodePath9.dirname(current);
21644
21820
  if (parent === current) return void 0;
21645
21821
  current = parent;
21646
21822
  }
21647
21823
  }
21648
- function parseLenientJson(text) {
21824
+ function parseLenientJson(text2) {
21649
21825
  let output = "";
21650
21826
  let index = 0;
21651
- while (index < text.length) {
21652
- const char = text[index];
21653
- const next = text[index + 1];
21827
+ while (index < text2.length) {
21828
+ const char = text2[index];
21829
+ const next = text2[index + 1];
21654
21830
  if (char === '"') {
21655
21831
  let end = index + 1;
21656
- while (end < text.length && text[end] !== '"') end += text[end] === "\\" ? 2 : 1;
21657
- output += text.slice(index, end + 1);
21832
+ while (end < text2.length && text2[end] !== '"') end += text2[end] === "\\" ? 2 : 1;
21833
+ output += text2.slice(index, end + 1);
21658
21834
  index = end + 1;
21659
21835
  } else if (char === "/" && next === "/") {
21660
- while (index < text.length && text[index] !== "\n") index += 1;
21836
+ while (index < text2.length && text2[index] !== "\n") index += 1;
21661
21837
  } else if (char === "/" && next === "*") {
21662
- const end = text.indexOf("*/", index + 2);
21663
- index = end === -1 ? text.length : end + 2;
21838
+ const end = text2.indexOf("*/", index + 2);
21839
+ index = end === -1 ? text2.length : end + 2;
21664
21840
  } else {
21665
21841
  output += char;
21666
21842
  index += 1;
@@ -21675,10 +21851,10 @@ var SKILL_OVERRIDE_BY_VALUE = {
21675
21851
  on: { enabled: true, userInvocationOnly: false },
21676
21852
  "name-only": { enabled: true, userInvocationOnly: false }
21677
21853
  };
21678
- function readOverridesFromSettings(text) {
21854
+ function readOverridesFromSettings(text2) {
21679
21855
  let parsed;
21680
21856
  try {
21681
- parsed = parseLenientJson(text);
21857
+ parsed = parseLenientJson(text2);
21682
21858
  } catch {
21683
21859
  return void 0;
21684
21860
  }
@@ -21722,8 +21898,8 @@ async function discoverClaudeSkills(configDirPath, options = {}) {
21722
21898
  options.environment ?? NodeProcess3.env
21723
21899
  );
21724
21900
  const roots = [
21725
- { directory: NodePath8.join(configDirPath, "skills"), scope: "user" },
21726
- ...cwd ? [{ directory: NodePath8.join(cwd, ".claude", "skills"), scope: "project" }] : []
21901
+ { directory: NodePath9.join(configDirPath, "skills"), scope: "user" },
21902
+ ...cwd ? [{ directory: NodePath9.join(cwd, ".claude", "skills"), scope: "project" }] : []
21727
21903
  ];
21728
21904
  const skills = /* @__PURE__ */ new Map();
21729
21905
  for (const root of roots) {
@@ -21731,7 +21907,7 @@ async function discoverClaudeSkills(configDirPath, options = {}) {
21731
21907
  for (const entry of entries.toSorted()) {
21732
21908
  const name = entry.trim();
21733
21909
  if (name.length === 0 || skills.has(name)) continue;
21734
- const skillPath = NodePath8.join(root.directory, entry, "SKILL.md");
21910
+ const skillPath = NodePath9.join(root.directory, entry, "SKILL.md");
21735
21911
  const contents = await NodeFSP3.readFile(skillPath, "utf8").catch(() => void 0);
21736
21912
  if (contents === void 0) continue;
21737
21913
  const frontMatter = parseSkillFrontMatter(contents);
@@ -21794,15 +21970,15 @@ function knownClaudeModelCapabilities(model, catalog, customModels) {
21794
21970
  function claudeModelCapabilities(model, catalog, customModels) {
21795
21971
  return knownClaudeModelCapabilities(model, catalog, customModels) ?? DEFAULT_CLAUDE_MODEL_CAPABILITIES;
21796
21972
  }
21797
- function isRecord3(value) {
21973
+ function isRecord4(value) {
21798
21974
  return typeof value === "object" && value !== null && !Array.isArray(value);
21799
21975
  }
21800
21976
  function getModelSelectionStringOptionValue(modelSelection, optionId) {
21801
- if (!isRecord3(modelSelection)) return void 0;
21977
+ if (!isRecord4(modelSelection)) return void 0;
21802
21978
  const options = modelSelection["options"];
21803
21979
  if (!Array.isArray(options)) return void 0;
21804
21980
  for (const entry of options) {
21805
- if (!isRecord3(entry)) continue;
21981
+ if (!isRecord4(entry)) continue;
21806
21982
  if (entry["id"] === optionId && typeof entry["value"] === "string") {
21807
21983
  const trimmed = entry["value"].trim();
21808
21984
  if (trimmed.length > 0) return trimmed;
@@ -21811,11 +21987,11 @@ function getModelSelectionStringOptionValue(modelSelection, optionId) {
21811
21987
  return void 0;
21812
21988
  }
21813
21989
  function getModelSelectionBooleanOptionValue(modelSelection, optionId) {
21814
- if (!isRecord3(modelSelection)) return void 0;
21990
+ if (!isRecord4(modelSelection)) return void 0;
21815
21991
  const options = modelSelection["options"];
21816
21992
  if (!Array.isArray(options)) return void 0;
21817
21993
  for (const entry of options) {
21818
- if (!isRecord3(entry)) continue;
21994
+ if (!isRecord4(entry)) continue;
21819
21995
  if (entry["id"] === optionId && typeof entry["value"] === "boolean") {
21820
21996
  return entry["value"];
21821
21997
  }
@@ -21823,7 +21999,7 @@ function getModelSelectionBooleanOptionValue(modelSelection, optionId) {
21823
21999
  return void 0;
21824
22000
  }
21825
22001
  function modelIdFromSelection(selection) {
21826
- if (!isRecord3(selection)) return void 0;
22002
+ if (!isRecord4(selection)) return void 0;
21827
22003
  const model = selection["model"];
21828
22004
  return typeof model === "string" ? model.trim() || void 0 : void 0;
21829
22005
  }
@@ -22065,16 +22241,12 @@ function claudeToolSearchNotice(version, environment) {
22065
22241
  var CLAUDE_PROGRAM_NOT_CONFIGURED = "Claude program path is not configured";
22066
22242
  function createClaudeProbe(input) {
22067
22243
  const { recipe, host, modelCatalog } = input;
22068
- const collectProcessText = async (stream) => {
22069
- const chunks = [];
22070
- for await (const chunk of stream) {
22071
- chunks.push(Buffer.from(chunk));
22072
- }
22073
- return Buffer.concat(chunks).toString("utf8");
22074
- };
22075
22244
  const probeAuth = () => {
22076
- const hasEnvToken = (recipe.environment.ANTHROPIC_AUTH_TOKEN ?? "").trim().length > 0;
22077
- const hasEnvApiKey = (recipe.environment.ANTHROPIC_API_KEY ?? "").trim().length > 0;
22245
+ const native = modelCatalog.currentAuth?.();
22246
+ if (native !== void 0) return native;
22247
+ const environment = input.launchEnvironment();
22248
+ const hasEnvToken = (environment.ANTHROPIC_AUTH_TOKEN ?? "").trim().length > 0;
22249
+ const hasEnvApiKey = (environment.ANTHROPIC_API_KEY ?? "").trim().length > 0;
22078
22250
  if (hasEnvToken || hasEnvApiKey) {
22079
22251
  return {
22080
22252
  status: "authenticated",
@@ -22082,13 +22254,15 @@ function createClaudeProbe(input) {
22082
22254
  label: hasEnvToken ? "ANTHROPIC_AUTH_TOKEN" : "ANTHROPIC_API_KEY"
22083
22255
  };
22084
22256
  }
22085
- const account = readClaudeAccount(resolveClaudeCredentialsDirPath(recipe.credentialsPath));
22257
+ const account = readClaudeAccount(
22258
+ resolveClaudeCredentialsDirPath(recipe.credentialsPath),
22259
+ claudeCredentialFileCandidates(recipe)
22260
+ );
22086
22261
  switch (account.state) {
22087
22262
  case "signed-in":
22088
- case "expired":
22089
22263
  return { status: "authenticated" };
22264
+ case "expired":
22090
22265
  case "signed-out":
22091
- return { status: "unauthenticated" };
22092
22266
  case "unreadable":
22093
22267
  return { status: "unknown" };
22094
22268
  }
@@ -22107,16 +22281,31 @@ function createClaudeProbe(input) {
22107
22281
  if (signal?.aborted) {
22108
22282
  throw new ExecutorSpawnAbortedError();
22109
22283
  }
22110
- let handle;
22284
+ let command;
22285
+ let commandStarted = false;
22286
+ const commandHost = {
22287
+ ...host,
22288
+ process: {
22289
+ spawn: async (request) => {
22290
+ const handle = await host.process.spawn(request);
22291
+ commandStarted = true;
22292
+ return handle;
22293
+ }
22294
+ }
22295
+ };
22111
22296
  try {
22112
- handle = await host.process.spawn({
22297
+ command = await runProcessOperation(commandHost, {
22113
22298
  program: recipe.binaryPath,
22114
22299
  args: ["--version"],
22300
+ timeoutMs: CLAUDE_VERSION_TIMEOUT_MS,
22301
+ maxOutputBytes: 64 * 1024,
22115
22302
  owner: { bindingId: host.bindingId },
22116
22303
  ...signal === void 0 ? {} : { signal }
22117
22304
  });
22118
22305
  } catch (error) {
22119
- if (error instanceof ExecutorSpawnRefusedError || error instanceof ExecutorSpawnAbortedError) {
22306
+ if (isOperationCleanupError(error)) throw error;
22307
+ if (signal?.aborted) throw new ExecutorSpawnAbortedError();
22308
+ if (commandStarted || error instanceof ExecutorSpawnRefusedError || error instanceof ExecutorSpawnAbortedError) {
22120
22309
  throw error;
22121
22310
  }
22122
22311
  const failure = describeExecutorSpawnFailure(error);
@@ -22128,14 +22317,7 @@ function createClaudeProbe(input) {
22128
22317
  detail: failure.detail
22129
22318
  };
22130
22319
  }
22131
- const [stdout, stderr, exitCode] = await raceAbort(
22132
- Promise.all([
22133
- collectProcessText(handle.stdout),
22134
- collectProcessText(handle.stderr),
22135
- handle.exited
22136
- ]),
22137
- signal
22138
- );
22320
+ const { stdout, stderr, exitCode } = command;
22139
22321
  if (signal?.aborted) throw new ExecutorSpawnAbortedError();
22140
22322
  const version = parseGenericCliVersion(`${stdout}
22141
22323
  ${stderr}`);
@@ -22195,49 +22377,29 @@ ${stderr}`);
22195
22377
  }
22196
22378
  var CLAUDE_VERSION_TIMEOUT_MS = 15e3;
22197
22379
  async function readClaudeCliVersion(input) {
22380
+ input.signal?.throwIfAborted();
22198
22381
  if (input.recipe.binaryPath.length === 0) return null;
22199
- let handle;
22200
22382
  try {
22201
- handle = await input.host.process.spawn({
22383
+ const { stdout, stderr } = await runProcessOperation(input.host, {
22202
22384
  program: input.recipe.binaryPath,
22203
22385
  args: ["--version"],
22204
- owner: { bindingId: input.host.bindingId }
22386
+ owner: { bindingId: input.host.bindingId },
22387
+ signal: input.signal,
22388
+ timeoutMs: CLAUDE_VERSION_TIMEOUT_MS,
22389
+ maxOutputBytes: 64 * 1024
22205
22390
  });
22206
- } catch {
22207
- return null;
22208
- }
22209
- const collect = async (stream) => {
22210
- const chunks = [];
22211
- for await (const chunk of stream) chunks.push(Buffer.from(chunk));
22212
- return Buffer.concat(chunks).toString("utf8");
22213
- };
22214
- const collected = Promise.all([collect(handle.stdout), collect(handle.stderr), handle.exited]);
22215
- let timer;
22216
- const timeout = new Promise((resolve5) => {
22217
- timer = setTimeout(() => resolve5("timeout"), CLAUDE_VERSION_TIMEOUT_MS);
22218
- timer.unref?.();
22219
- });
22220
- try {
22221
- const settled = await Promise.race([collected, timeout]);
22222
- if (settled === "timeout") {
22223
- collected.catch(() => void 0);
22224
- return null;
22225
- }
22226
- const [stdout, stderr] = settled;
22227
- return parseGenericCliVersion(`${stdout}
22228
- ${stderr}`);
22229
- } catch {
22391
+ return parseGenericCliVersion(stdout + "\n" + stderr);
22392
+ } catch (error) {
22393
+ if (isOperationCleanupError(error)) throw error;
22394
+ input.signal?.throwIfAborted();
22230
22395
  return null;
22231
- } finally {
22232
- if (timer !== void 0) clearTimeout(timer);
22233
- await handle.kill("SIGTERM").catch(() => void 0);
22234
22396
  }
22235
22397
  }
22236
22398
 
22237
22399
  // src/claudeExecutable.ts
22238
- import * as NodeFS5 from "node:fs";
22400
+ import * as NodeFS6 from "node:fs";
22239
22401
  import * as NodeOS5 from "node:os";
22240
- import * as NodePath9 from "node:path";
22402
+ import * as NodePath10 from "node:path";
22241
22403
  var WINDOWS_SHIM_EXTENSIONS = /* @__PURE__ */ new Set([".cmd", ".bat", ".ps1"]);
22242
22404
  var NPM_PACKAGE_ENTRY_CANDIDATES = [
22243
22405
  ["node_modules", "@anthropic-ai", "claude-code", "bin", "claude.exe"],
@@ -22245,7 +22407,7 @@ var NPM_PACKAGE_ENTRY_CANDIDATES = [
22245
22407
  ];
22246
22408
  function isExistingFile(filePath) {
22247
22409
  try {
22248
- return NodeFS5.statSync(filePath).isFile();
22410
+ return NodeFS6.statSync(filePath).isFile();
22249
22411
  } catch {
22250
22412
  return false;
22251
22413
  }
@@ -22255,13 +22417,13 @@ function resolveClaudeSdkExecutablePath(binaryPath, environment = process.env, i
22255
22417
  return binaryPath;
22256
22418
  }
22257
22419
  const resolved = resolveWindowsExecutable(binaryPath, environment) ?? binaryPath;
22258
- const extension = NodePath9.win32.extname(resolved).toLowerCase();
22420
+ const extension = NodePath10.win32.extname(resolved).toLowerCase();
22259
22421
  if (!WINDOWS_SHIM_EXTENSIONS.has(extension)) {
22260
22422
  return resolved;
22261
22423
  }
22262
- const shimDirectory = NodePath9.win32.dirname(resolved);
22424
+ const shimDirectory = NodePath10.win32.dirname(resolved);
22263
22425
  for (const entrySegments of NPM_PACKAGE_ENTRY_CANDIDATES) {
22264
- const candidate = NodePath9.win32.join(shimDirectory, ...entrySegments);
22426
+ const candidate = NodePath10.win32.join(shimDirectory, ...entrySegments);
22265
22427
  if (isFile(candidate)) {
22266
22428
  return candidate;
22267
22429
  }
@@ -22271,14 +22433,14 @@ function resolveClaudeSdkExecutablePath(binaryPath, environment = process.env, i
22271
22433
  function resolveWindowsExecutable(binaryPath, environment) {
22272
22434
  const trimmed = binaryPath.trim();
22273
22435
  if (trimmed.length === 0) return void 0;
22274
- if (NodePath9.win32.isAbsolute(trimmed) && isExistingFile(trimmed)) return trimmed;
22436
+ if (NodePath10.win32.isAbsolute(trimmed) && isExistingFile(trimmed)) return trimmed;
22275
22437
  const pathValue = environment.PATH ?? environment.Path ?? "";
22276
22438
  const pathext = environment.PATHEXT ?? ".EXE;.CMD;.BAT;.COM";
22277
22439
  const extensions = pathext.split(";").filter((ext) => ext.length > 0);
22278
- const hasExt = NodePath9.win32.extname(trimmed).length > 0;
22440
+ const hasExt = NodePath10.win32.extname(trimmed).length > 0;
22279
22441
  for (const dir of pathValue.split(";")) {
22280
22442
  if (dir.trim().length === 0) continue;
22281
- const candidates = hasExt ? [NodePath9.win32.join(dir, trimmed)] : extensions.map((ext) => NodePath9.win32.join(dir, `${trimmed}${ext}`));
22443
+ const candidates = hasExt ? [NodePath10.win32.join(dir, trimmed)] : extensions.map((ext) => NodePath10.win32.join(dir, `${trimmed}${ext}`));
22282
22444
  for (const candidate of candidates) {
22283
22445
  if (isExistingFile(candidate)) return candidate;
22284
22446
  }
@@ -22328,7 +22490,7 @@ function asStringField(input, key) {
22328
22490
  const value = input[key];
22329
22491
  return typeof value === "string" ? value : void 0;
22330
22492
  }
22331
- function readString(value) {
22493
+ function readString2(value) {
22332
22494
  return typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
22333
22495
  }
22334
22496
  function readStringArray(value) {
@@ -22498,14 +22660,14 @@ function readRangeSuffix(input) {
22498
22660
  function loneStringValue(input) {
22499
22661
  let found;
22500
22662
  for (const value of Object.values(input)) {
22501
- const text = asTrimmedString(value);
22502
- if (text === void 0) {
22663
+ const text2 = asTrimmedString(value);
22664
+ if (text2 === void 0) {
22503
22665
  continue;
22504
22666
  }
22505
22667
  if (found !== void 0) {
22506
22668
  return void 0;
22507
22669
  }
22508
- found = text;
22670
+ found = text2;
22509
22671
  }
22510
22672
  return found;
22511
22673
  }
@@ -23320,11 +23482,11 @@ var makeAssistantText = (emitter) => {
23320
23482
  blockIndex: block.blockIndex,
23321
23483
  block
23322
23484
  }));
23323
- for (const [position, text] of snapshotTextBlocks.entries()) {
23485
+ for (const [position, text2] of snapshotTextBlocks.entries()) {
23324
23486
  const existingEntry = orderedBlocks[position];
23325
23487
  let entry = existingEntry;
23326
23488
  if (!entry) {
23327
- const created = await createSyntheticAssistantTextBlock(context, text);
23489
+ const created = await createSyntheticAssistantTextBlock(context, text2);
23328
23490
  if (created) {
23329
23491
  orderedBlocks.push(created);
23330
23492
  }
@@ -23334,7 +23496,7 @@ var makeAssistantText = (emitter) => {
23334
23496
  continue;
23335
23497
  }
23336
23498
  if (entry.block.fallbackText.length === 0) {
23337
- entry.block.fallbackText = text;
23499
+ entry.block.fallbackText = text2;
23338
23500
  }
23339
23501
  if (entry.block.streamClosed && !entry.block.completionEmitted) {
23340
23502
  await completeAssistantTextBlock(context, entry.block, {
@@ -23432,8 +23594,8 @@ function applyClaudeTaskToolResult(tasks, tool, result) {
23432
23594
  continue;
23433
23595
  }
23434
23596
  const task2 = entry;
23435
- const id2 = readString(task2.id);
23436
- const subject2 = readString(task2.subject);
23597
+ const id2 = readString2(task2.id);
23598
+ const subject2 = readString2(task2.subject);
23437
23599
  if (!id2 || !subject2) {
23438
23600
  continue;
23439
23601
  }
@@ -23448,8 +23610,8 @@ function applyClaudeTaskToolResult(tasks, tool, result) {
23448
23610
  }
23449
23611
  if (tool.toolName === "TaskCreate") {
23450
23612
  const resultTask = readClaudeTaskFromResult(result);
23451
- const id2 = readString(resultTask?.id);
23452
- const subject2 = readString(resultTask?.subject) ?? readString(tool.input.subject);
23613
+ const id2 = readString2(resultTask?.id);
23614
+ const subject2 = readString2(resultTask?.subject) ?? readString2(tool.input.subject);
23453
23615
  if (!id2 || !subject2) {
23454
23616
  return false;
23455
23617
  }
@@ -23461,7 +23623,7 @@ function applyClaudeTaskToolResult(tasks, tool, result) {
23461
23623
  });
23462
23624
  return true;
23463
23625
  }
23464
- const taskId = readString(tool.input.taskId) ?? readString(result?.taskId);
23626
+ const taskId = readString2(tool.input.taskId) ?? readString2(result?.taskId);
23465
23627
  if (!taskId) {
23466
23628
  return false;
23467
23629
  }
@@ -23469,7 +23631,7 @@ function applyClaudeTaskToolResult(tasks, tool, result) {
23469
23631
  if (!task) {
23470
23632
  return false;
23471
23633
  }
23472
- const subject = readString(tool.input.subject);
23634
+ const subject = readString2(tool.input.subject);
23473
23635
  if (subject && task.subject !== subject) {
23474
23636
  task.subject = subject;
23475
23637
  changed = true;
@@ -23880,23 +24042,23 @@ function tokenizeShellWords(value) {
23880
24042
 
23881
24043
  // src/config.ts
23882
24044
  var CLAUDE_AUTO_COMPACT_WINDOW_PATTERN = /^(?:|[1-9]\d{5}|1000000)$/;
23883
- function isRecord4(value) {
24045
+ function isRecord5(value) {
23884
24046
  return typeof value === "object" && value !== null && !Array.isArray(value);
23885
24047
  }
23886
- function readString2(value) {
24048
+ function readString3(value) {
23887
24049
  return typeof value === "string" ? value : "";
23888
24050
  }
23889
24051
  function readTrimmed(value) {
23890
- return readString2(value).trim();
24052
+ return readString3(value).trim();
23891
24053
  }
23892
24054
  function readEnvironment(value) {
23893
24055
  if (!Array.isArray(value)) return {};
23894
24056
  const env2 = {};
23895
24057
  for (const entry of value) {
23896
- if (!isRecord4(entry)) continue;
24058
+ if (!isRecord5(entry)) continue;
23897
24059
  const name = readTrimmed(entry["name"]);
23898
24060
  if (name.length === 0) continue;
23899
- env2[name] = readString2(entry["value"]);
24061
+ env2[name] = readString3(entry["value"]);
23900
24062
  }
23901
24063
  return env2;
23902
24064
  }
@@ -23909,12 +24071,12 @@ function readCustomModels(value) {
23909
24071
  if (slug2.length > 0) models.push({ slug: slug2 });
23910
24072
  continue;
23911
24073
  }
23912
- if (!isRecord4(entry)) continue;
24074
+ if (!isRecord5(entry)) continue;
23913
24075
  const slug = readTrimmed(entry["slug"]);
23914
24076
  if (slug.length === 0) continue;
23915
24077
  const thinkingRaw = entry["thinkingOptions"];
23916
24078
  const thinkingOptions = Array.isArray(thinkingRaw) ? thinkingRaw.flatMap((option) => {
23917
- if (!isRecord4(option)) return [];
24079
+ if (!isRecord5(option)) return [];
23918
24080
  const id2 = readTrimmed(option["id"]) || readTrimmed(option["value"]);
23919
24081
  const label = readTrimmed(option["label"]) || id2;
23920
24082
  if (id2.length === 0) return [];
@@ -23947,7 +24109,7 @@ function readAutoCompactWindow(value) {
23947
24109
  return CLAUDE_AUTO_COMPACT_WINDOW_PATTERN.test(trimmed) ? trimmed : "";
23948
24110
  }
23949
24111
  function parseClaudeInstanceConfig(config) {
23950
- if (!isRecord4(config)) {
24112
+ if (!isRecord5(config)) {
23951
24113
  return {
23952
24114
  binaryPath: "",
23953
24115
  homePath: "",
@@ -23960,13 +24122,13 @@ function parseClaudeInstanceConfig(config) {
23960
24122
  envelopeDriver: void 0
23961
24123
  };
23962
24124
  }
23963
- const nested = isRecord4(config["config"]) ? config["config"] : void 0;
24125
+ const nested = isRecord5(config["config"]) ? config["config"] : void 0;
23964
24126
  const settings = nested ?? config;
23965
24127
  return {
23966
24128
  binaryPath: readTrimmed(settings["binaryPath"]),
23967
24129
  homePath: readTrimmed(settings["homePath"]) || readTrimmed(config["homePath"]),
23968
24130
  credentialsPath: readTrimmed(settings["credentialsPath"]) || readTrimmed(config["credentialsPath"]),
23969
- launchArgs: readString2(settings["launchArgs"]),
24131
+ launchArgs: readString3(settings["launchArgs"]),
23970
24132
  autoCompactWindow: readAutoCompactWindow(settings["autoCompactWindow"]),
23971
24133
  customModels: readCustomModels(settings["customModels"]),
23972
24134
  enabled: settings["enabled"] !== false,
@@ -24015,91 +24177,295 @@ function parseCliArgs(args, options) {
24015
24177
  return { flags, positionals, variadicArgs };
24016
24178
  }
24017
24179
 
24180
+ // src/adapter/modelCatalog.ts
24181
+ import {
24182
+ raceAbort
24183
+ } from "@ycodium-ai/plugin-api/executor";
24184
+
24018
24185
  // src/sdkModels.ts
24019
24186
  import * as NodeOS6 from "node:os";
24020
24187
  import { REASONING_OPTION_LABEL as REASONING_OPTION_LABEL2, reasoningEffortShortLabel } from "@ycodium-ai/plugin-api/executor";
24021
- var CLAUDE_SDK_MODELS_TIMEOUT_MS = 2e4;
24022
- var CLAUDE_SDK_MODELS_TTL_MS = 30 * 6e4;
24023
- var CLAUDE_SDK_MODELS_RETRY_MS = 2 * 6e4;
24024
- var effortLabel = (level) => reasoningEffortShortLabel(level) ?? `${level.slice(0, 1).toUpperCase()}${level.slice(1)}`;
24025
- var claudeCatalogModelFromSdk = (info) => {
24026
- const descriptors = [];
24027
- const levels = info.supportsEffort === false ? [] : info.supportedEffortLevels ?? [];
24028
- if (levels.length > 0) {
24029
- descriptors.push({
24030
- type: "select",
24031
- id: "effort",
24032
- label: REASONING_OPTION_LABEL2,
24033
- options: levels.map((level) => ({ id: level, label: effortLabel(level) }))
24034
- });
24035
- }
24036
- if (info.supportsFastMode === true) {
24037
- descriptors.push({ type: "boolean", id: "fastMode", label: "Fast Mode" });
24188
+ import { OperationCleanupError } from "@ycodium-ai/plugin-api/process-operation";
24189
+
24190
+ // src/sdkAccount.ts
24191
+ var text = (value) => typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
24192
+ function claudeAuthFromSdk(value) {
24193
+ if (value === void 0) return void 0;
24194
+ if (typeof value !== "object" || value === null || Array.isArray(value))
24195
+ return { status: "unknown" };
24196
+ const account = value;
24197
+ const token = text(account.tokenSource);
24198
+ const key = text(account.apiKeySource);
24199
+ const hasToken = token !== void 0 && token !== "none";
24200
+ const hasKey = key !== void 0 && key !== "none";
24201
+ if (hasToken || hasKey) {
24202
+ const email2 = text(account.email);
24203
+ const label2 = text(account.subscriptionType) ?? text(account.organization);
24204
+ return {
24205
+ status: "authenticated",
24206
+ type: hasToken ? "oauth" : "api-key",
24207
+ ...email2 === void 0 ? {} : { email: email2 },
24208
+ ...label2 === void 0 ? {} : { label: label2 }
24209
+ };
24038
24210
  }
24039
- return {
24040
- slug: info.value,
24041
- name: info.displayName.trim().length > 0 ? info.displayName : info.value,
24042
- isCustom: false,
24043
- capabilities: { optionDescriptors: descriptors }
24044
- };
24045
- };
24046
- var claudeCatalogFromSdk = (models) => {
24047
- const seen = /* @__PURE__ */ new Set();
24048
- const out = [];
24049
- for (const info of models) {
24050
- if (typeof info.value !== "string" || info.value.trim().length === 0) continue;
24051
- if (seen.has(info.value)) continue;
24052
- seen.add(info.value);
24053
- out.push(claudeCatalogModelFromSdk(info));
24211
+ const provider = text(account.apiProvider);
24212
+ if (provider !== void 0 && provider !== "firstParty")
24213
+ return { status: "unknown", type: provider };
24214
+ if (token === "none" && key === "none") return { status: "unauthenticated" };
24215
+ const email = text(account.email);
24216
+ const label = text(account.subscriptionType);
24217
+ if (provider === "firstParty" && email !== void 0) {
24218
+ return { status: "authenticated", email, ...label === void 0 ? {} : { label } };
24054
24219
  }
24055
- return out;
24056
- };
24057
- var silentPrompt = (signal) => ({
24058
- [Symbol.asyncIterator]: () => ({
24059
- next: () => new Promise((resolve5) => {
24060
- if (signal.aborted) resolve5({ done: true, value: void 0 });
24061
- signal.addEventListener("abort", () => resolve5({ done: true, value: void 0 }), {
24062
- once: true
24063
- });
24064
- }),
24065
- return: async () => ({ done: true, value: void 0 })
24066
- })
24067
- });
24068
- var readClaudeSdkModels = async (input) => {
24069
- const abort = new AbortController();
24070
- const forwardAbort = () => abort.abort();
24071
- input.signal?.addEventListener("abort", forwardAbort, { once: true });
24072
- if (input.signal?.aborted) abort.abort();
24073
- let runtime;
24074
- let timer;
24075
- try {
24076
- runtime = input.createQuery({
24077
- prompt: silentPrompt(abort.signal),
24078
- options: { ...input.options, abortController: abort }
24079
- });
24080
- if (runtime.supportedModels === void 0) return void 0;
24081
- const timeout = new Promise((_3, reject) => {
24082
- timer = setTimeout(
24083
- () => reject(new Error("supportedModels timed out")),
24084
- input.timeoutMs ?? CLAUDE_SDK_MODELS_TIMEOUT_MS
24085
- );
24086
- });
24087
- const cancelled = new Promise((_3, reject) => {
24088
- abort.signal.addEventListener("abort", () => reject(new Error("aborted")), { once: true });
24089
- });
24090
- cancelled.catch(() => {
24091
- });
24092
- return await Promise.race([runtime.supportedModels(), timeout, cancelled]);
24093
- } catch {
24094
- return void 0;
24095
- } finally {
24096
- if (timer !== void 0) clearTimeout(timer);
24097
- input.signal?.removeEventListener("abort", forwardAbort);
24098
- abort.abort();
24099
- try {
24100
- runtime?.close();
24101
- } catch {
24220
+ return { status: "unknown" };
24221
+ }
24222
+
24223
+ // src/sdkRead.ts
24224
+ import { withProcessOperation } from "@ycodium-ai/plugin-api/process-operation";
24225
+
24226
+ // src/claudeProcess.ts
24227
+ import * as NodeEvents from "node:events";
24228
+ import * as NodeStream from "node:stream";
24229
+ import { adoptChildStdin, nodeWritableFromHandle } from "@ycodium-ai/plugin-api/process-stdin";
24230
+ var asError = (cause) => cause instanceof Error ? cause : new Error(String(cause));
24231
+ var ClaudeProcess = class extends NodeEvents.EventEmitter {
24232
+ stdin;
24233
+ stdout;
24234
+ killed = false;
24235
+ exitCode = null;
24236
+ handle;
24237
+ ended = false;
24238
+ constructor(host, options, stderr, cleanup) {
24239
+ super();
24240
+ const env2 = {
24241
+ ...Object.fromEntries(Object.keys(process.env).map((key) => [key, void 0])),
24242
+ ...options.env
24243
+ };
24244
+ const ready = host.process.spawn({
24245
+ program: options.command,
24246
+ args: options.args,
24247
+ ...options.cwd === void 0 ? {} : { cwd: options.cwd },
24248
+ env: env2,
24249
+ owner: { bindingId: host.bindingId }
24250
+ });
24251
+ this.stdin = nodeWritableFromHandle({
24252
+ writeStdin: (chunk) => ready.then((handle) => handle.writeStdin(chunk)),
24253
+ closeStdin: () => ready.then((handle) => handle.closeStdin())
24254
+ });
24255
+ this.stdout = NodeStream.Readable.from(
24256
+ (async function* () {
24257
+ const handle = await ready.catch(() => void 0);
24258
+ if (handle) yield* handle.stdout;
24259
+ })(),
24260
+ { objectMode: false }
24261
+ );
24262
+ adoptChildStdin(this.stdin, {
24263
+ onError: (error) => {
24264
+ this.emit("error", error);
24265
+ }
24266
+ });
24267
+ this.stdout.on("error", (error) => this.emit("error", error));
24268
+ this.requestKill = (signal) => {
24269
+ void ready.then((handle) => this.ended ? void 0 : handle.kill(signal)).catch((cause) => this.emit("error", asError(cause)));
24270
+ };
24271
+ const abort = () => {
24272
+ this.kill("SIGTERM");
24273
+ };
24274
+ options.signal.addEventListener("abort", abort, { once: true });
24275
+ if (options.signal.aborted) abort();
24276
+ void ready.then(async (handle) => {
24277
+ this.handle = handle;
24278
+ const drainStderr = (async () => {
24279
+ const decoder = new TextDecoder();
24280
+ for await (const chunk of handle.stderr) stderr(decoder.decode(chunk, { stream: true }));
24281
+ const tail = decoder.decode();
24282
+ if (tail) stderr(tail);
24283
+ })();
24284
+ void drainStderr.catch((cause) => this.emit("error", asError(cause)));
24285
+ try {
24286
+ const code = await handle.exited;
24287
+ this.ended = true;
24288
+ this.handle = void 0;
24289
+ this.exitCode = code;
24290
+ await cleanup();
24291
+ this.emit("exit", code, null);
24292
+ } finally {
24293
+ options.signal.removeEventListener("abort", abort);
24294
+ }
24295
+ }).catch(async (cause) => {
24296
+ this.ended = true;
24297
+ this.handle = void 0;
24298
+ options.signal.removeEventListener("abort", abort);
24299
+ await cleanup();
24300
+ this.emit("error", asError(cause));
24301
+ });
24302
+ }
24303
+ requestKill;
24304
+ kill(signal) {
24305
+ if (this.ended || signal !== "SIGTERM" && signal !== "SIGKILL") return false;
24306
+ this.killed = true;
24307
+ this.requestKill(signal);
24308
+ return true;
24309
+ }
24310
+ };
24311
+ function makeClaudeProcessSpawner(host, stderr, config = {}) {
24312
+ const cleanup = config.cleanup ?? (async () => {
24313
+ });
24314
+ let process2;
24315
+ let launch;
24316
+ return {
24317
+ spawn: (options) => {
24318
+ const args = [...options.args, ...config.mcpConfigArgs ?? []];
24319
+ if (config.mcpConfigPath !== void 0) {
24320
+ const flag = args.findLastIndex(
24321
+ (arg) => arg === "--mcp-config" || arg.startsWith("--mcp-config=")
24322
+ );
24323
+ if (flag === -1) args.push("--mcp-config", config.mcpConfigPath);
24324
+ else {
24325
+ let end = flag + 1;
24326
+ while (end < args.length && !args[end].startsWith("-")) end++;
24327
+ args.splice(end, 0, config.mcpConfigPath);
24328
+ }
24329
+ }
24330
+ launch = { ...options, args };
24331
+ process2 = new ClaudeProcess(host, launch, stderr, cleanup);
24332
+ return process2;
24333
+ },
24334
+ current: () => process2?.handle && launch ? { handle: process2.handle, command: launch.command, args: launch.args } : void 0,
24335
+ clear: () => {
24336
+ process2 = void 0;
24337
+ launch = void 0;
24338
+ return cleanup();
24339
+ }
24340
+ };
24341
+ }
24342
+
24343
+ // src/sdkRead.ts
24344
+ var silentPrompt = (signal) => ({
24345
+ [Symbol.asyncIterator]: () => ({
24346
+ next: () => new Promise((resolve5) => {
24347
+ if (signal.aborted) {
24348
+ resolve5({ done: true, value: void 0 });
24349
+ return;
24350
+ }
24351
+ signal.addEventListener("abort", () => resolve5({ done: true, value: void 0 }), {
24352
+ once: true
24353
+ });
24354
+ }),
24355
+ return: async () => ({ done: true, value: void 0 })
24356
+ })
24357
+ });
24358
+ async function withClaudeSdkRead(input, read) {
24359
+ return withProcessOperation(
24360
+ input.host,
24361
+ {
24362
+ signal: input.signal,
24363
+ timeoutMs: input.timeoutMs ?? 2e4
24364
+ },
24365
+ async (operation) => {
24366
+ const processHost = {
24367
+ ...input.host,
24368
+ process: {
24369
+ spawn: (options) => operation.spawn(options).then(
24370
+ (handle) => input.stdin === void 0 ? handle : {
24371
+ ...handle,
24372
+ writeStdin: input.stdin(handle.writeStdin)
24373
+ }
24374
+ )
24375
+ }
24376
+ };
24377
+ const spawner = makeClaudeProcessSpawner(processHost, () => {
24378
+ });
24379
+ operation.defer(() => spawner.clear());
24380
+ const flags = input.options.extraArgs ?? {};
24381
+ if (["resume", "continue", "fork-session", "session-id", "prompt", "print"].some(
24382
+ (key) => key in flags
24383
+ )) {
24384
+ throw new Error("Read-only SDK queries cannot resume a session");
24385
+ }
24386
+ const runtime = input.createQuery({
24387
+ prompt: silentPrompt(operation.signal),
24388
+ options: {
24389
+ ...input.options,
24390
+ persistSession: false,
24391
+ tools: [],
24392
+ mcpServers: {},
24393
+ strictMcpConfig: true,
24394
+ extraArgs: {
24395
+ ...flags,
24396
+ "no-session-persistence": null,
24397
+ tools: "",
24398
+ "mcp-config": JSON.stringify({ mcpServers: {} }),
24399
+ "strict-mcp-config": null,
24400
+ "disable-slash-commands": null
24401
+ },
24402
+ abortController: operation.abortController,
24403
+ spawnClaudeCodeProcess: spawner.spawn
24404
+ }
24405
+ });
24406
+ operation.defer(() => runtime.close());
24407
+ return operation.track(read(runtime));
24102
24408
  }
24409
+ );
24410
+ }
24411
+
24412
+ // src/sdkModels.ts
24413
+ var CLAUDE_SDK_MODELS_TIMEOUT_MS = 2e4;
24414
+ var CLAUDE_SDK_MODELS_TTL_MS = 30 * 6e4;
24415
+ var CLAUDE_SDK_MODELS_RETRY_MS = 2 * 6e4;
24416
+ var effortLabel = (level) => reasoningEffortShortLabel(level) ?? `${level.slice(0, 1).toUpperCase()}${level.slice(1)}`;
24417
+ var claudeCatalogModelFromSdk = (info) => {
24418
+ const descriptors = [];
24419
+ const levels = info.supportsEffort === false ? [] : info.supportedEffortLevels ?? [];
24420
+ if (levels.length > 0) {
24421
+ descriptors.push({
24422
+ type: "select",
24423
+ id: "effort",
24424
+ label: REASONING_OPTION_LABEL2,
24425
+ options: levels.map((level) => ({ id: level, label: effortLabel(level) }))
24426
+ });
24427
+ }
24428
+ if (info.supportsFastMode === true) {
24429
+ descriptors.push({ type: "boolean", id: "fastMode", label: "Fast Mode" });
24430
+ }
24431
+ return {
24432
+ slug: info.value,
24433
+ name: info.displayName.trim().length > 0 ? info.displayName : info.value,
24434
+ isCustom: false,
24435
+ capabilities: { optionDescriptors: descriptors }
24436
+ };
24437
+ };
24438
+ var claudeCatalogFromSdk = (models) => {
24439
+ const seen = /* @__PURE__ */ new Set();
24440
+ const out = [];
24441
+ for (const info of models) {
24442
+ if (typeof info.value !== "string" || info.value.trim().length === 0) continue;
24443
+ if (seen.has(info.value)) continue;
24444
+ seen.add(info.value);
24445
+ out.push(claudeCatalogModelFromSdk(info));
24446
+ }
24447
+ return out;
24448
+ };
24449
+ var readClaudeSdkProbe = async (input) => {
24450
+ try {
24451
+ return await withClaudeSdkRead(
24452
+ {
24453
+ ...input,
24454
+ timeoutMs: input.timeoutMs ?? CLAUDE_SDK_MODELS_TIMEOUT_MS
24455
+ },
24456
+ async (runtime) => {
24457
+ if (runtime.supportedModels === void 0) throw new Error("SDK model probe unavailable");
24458
+ const [models, account] = await Promise.all([
24459
+ runtime.supportedModels(),
24460
+ runtime.accountInfo?.()
24461
+ ]);
24462
+ return { models, auth: claudeAuthFromSdk(account) };
24463
+ }
24464
+ );
24465
+ } catch (cause) {
24466
+ if (cause instanceof OperationCleanupError) throw cause;
24467
+ input.signal?.throwIfAborted();
24468
+ return void 0;
24103
24469
  }
24104
24470
  };
24105
24471
  function claudeModelProbeEnvironment(environment) {
@@ -24132,10 +24498,12 @@ var makeClaudeModelCatalog = (deps) => {
24132
24498
  let modelCatalogRead;
24133
24499
  let modelCatalogFailedAt;
24134
24500
  let catalogReadOk = false;
24501
+ let auth;
24135
24502
  let successfulReadAt;
24136
24503
  let skipPairedRefresh = false;
24137
24504
  const current = () => modelCatalog?.models ?? [];
24138
24505
  const refresh = (input) => {
24506
+ input?.signal?.throwIfAborted();
24139
24507
  if (input?.paired === true && skipPairedRefresh) {
24140
24508
  skipPairedRefresh = false;
24141
24509
  return Promise.resolve();
@@ -24149,10 +24517,11 @@ var makeClaudeModelCatalog = (deps) => {
24149
24517
  catalogReadOk = false;
24150
24518
  return Promise.resolve();
24151
24519
  }
24152
- if (modelCatalogRead !== void 0) return modelCatalogRead;
24520
+ if (modelCatalogRead !== void 0) return raceAbort(modelCatalogRead, input?.signal);
24153
24521
  const read = (async () => {
24154
24522
  const extraArgs = parseCliArgs(deps.recipe.launchArgs).flags;
24155
- const models = await readClaudeSdkModels({
24523
+ const probe = await readClaudeSdkProbe({
24524
+ host: deps.host,
24156
24525
  createQuery: deps.createQuery,
24157
24526
  signal: input?.signal,
24158
24527
  options: {
@@ -24163,7 +24532,9 @@ var makeClaudeModelCatalog = (deps) => {
24163
24532
  ...Object.keys(extraArgs).length > 0 ? { extraArgs } : {}
24164
24533
  }
24165
24534
  });
24166
- if (models === void 0) {
24535
+ input?.signal?.throwIfAborted();
24536
+ auth = probe?.auth;
24537
+ if (probe === void 0) {
24167
24538
  modelCatalogFailedAt = deps.now();
24168
24539
  catalogReadOk = false;
24169
24540
  successfulReadAt = void 0;
@@ -24172,7 +24543,7 @@ var makeClaudeModelCatalog = (deps) => {
24172
24543
  modelCatalogFailedAt = void 0;
24173
24544
  catalogReadOk = true;
24174
24545
  successfulReadAt = readAt;
24175
- modelCatalog = { models: claudeCatalogFromSdk(models), readAt };
24546
+ modelCatalog = { models: claudeCatalogFromSdk(probe.models), readAt };
24176
24547
  }
24177
24548
  })().finally(() => {
24178
24549
  modelCatalogRead = void 0;
@@ -24182,6 +24553,7 @@ var makeClaudeModelCatalog = (deps) => {
24182
24553
  };
24183
24554
  return {
24184
24555
  current,
24556
+ currentAuth: () => catalogReadOk ? auth : void 0,
24185
24557
  refresh,
24186
24558
  /** 此刻正在进行的那次读取;没有就是 undefined。起会话等它读完,不为此另起一次。 */
24187
24559
  pendingRead: () => modelCatalogRead,
@@ -24317,8 +24689,8 @@ var SKILL_TOKEN_REGEX = new RegExp(
24317
24689
  String.raw`(^|\s)([$/])(${SKILL_TOKEN_NAME_PATTERN})(?=\s|$)`,
24318
24690
  "g"
24319
24691
  );
24320
- function findSkillTokens(text) {
24321
- return [...text.matchAll(SKILL_TOKEN_REGEX)].map((match) => {
24692
+ function findSkillTokens(text2) {
24693
+ return [...text2.matchAll(SKILL_TOKEN_REGEX)].map((match) => {
24322
24694
  const start = (match.index ?? 0) + (match[1]?.length ?? 0);
24323
24695
  const name = match[3] ?? "";
24324
24696
  return { sigil: match[2], name, start, end: start + name.length + 1 };
@@ -24339,9 +24711,9 @@ function lookupSkill(name, index) {
24339
24711
  return session?.scope === SESSION_SKILL_NAMESPACE ? session : void 0;
24340
24712
  }
24341
24713
  var SLASH_SKILL_TOKEN = new RegExp(String.raw`^/(${SKILL_TOKEN_NAME_PATTERN})$`);
24342
- function matchSkillTokens(text, index) {
24714
+ function matchSkillTokens(text2, index) {
24343
24715
  if (index.size === 0) return [];
24344
- return findSkillTokens(text).flatMap((token) => {
24716
+ return findSkillTokens(text2).flatMap((token) => {
24345
24717
  const skill = lookupSkill(token.name, index);
24346
24718
  return skill === void 0 ? [] : [{ ...token, skill }];
24347
24719
  });
@@ -24355,7 +24727,7 @@ import { EXECUTOR_SEND_TURN_MAX_INPUT_CHARS as EXECUTOR_SEND_TURN_MAX_INPUT_CHAR
24355
24727
 
24356
24728
  // src/claudeInlineSkill.ts
24357
24729
  import * as NodeFSP4 from "node:fs/promises";
24358
- import * as NodePath10 from "node:path";
24730
+ import * as NodePath11 from "node:path";
24359
24731
  import { EXECUTOR_SEND_TURN_MAX_INPUT_CHARS } from "@ycodium-ai/plugin-api/executor";
24360
24732
 
24361
24733
  // src/claudeSkillArguments.ts
@@ -24466,7 +24838,7 @@ var notLoadedBlock = (name, reason) => skillBlock(name, [
24466
24838
  `The user invoked the skill "${name}" in this message, but it could not be included: ${reason}. It is not applied; don't assume its instructions.`
24467
24839
  ]);
24468
24840
  var renderLoaded = (invocation, source, variables) => {
24469
- const skillDir = NodePath10.dirname(invocation.path);
24841
+ const skillDir = NodePath11.dirname(invocation.path);
24470
24842
  const known = {
24471
24843
  CLAUDE_SKILL_DIR: skillDir,
24472
24844
  CLAUDE_PROJECT_DIR: variables.projectDir,
@@ -24617,14 +24989,14 @@ function assertSteeringTarget(expectedTurnId, turnState) {
24617
24989
  }
24618
24990
 
24619
24991
  // src/hostMcpAllowRules.ts
24620
- import { setsOwnModelConnection } from "@ycodium-ai/plugin-api/executor";
24992
+ import { setsOwnModelConnection as setsOwnModelConnection2 } from "@ycodium-ai/plugin-api/executor";
24621
24993
  function claudeHostMcpAllowRules(servers) {
24622
24994
  return (servers ?? []).filter((server) => server.hostOwned === true).map((server) => `mcp__${server.name}`);
24623
24995
  }
24624
24996
  function claudeUsesOfficialChannel(env2) {
24625
24997
  const connectivity = CLAUDE_CARD.modelConnectivity;
24626
24998
  const rows = Object.entries(env2).map(([name, value]) => ({ name, value }));
24627
- if (setsOwnModelConnection({ backendSwitchEnvKeys: connectivity?.backendSwitchEnvKeys }, rows))
24999
+ if (setsOwnModelConnection2({ backendSwitchEnvKeys: connectivity?.backendSwitchEnvKeys }, rows))
24628
25000
  return false;
24629
25001
  const key = connectivity?.baseUrlEnvKey;
24630
25002
  const baseUrl = key === void 0 ? void 0 : env2[key]?.trim();
@@ -25432,6 +25804,19 @@ var makeSessionControls = (deps) => {
25432
25804
  };
25433
25805
  };
25434
25806
 
25807
+ // src/workingDirectories.ts
25808
+ var ADDITIONAL_DIRECTORIES_CLAUDE_MD_ENV = "CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD";
25809
+ function claudeWorkingDirectories(cwd, others) {
25810
+ const hasOthers = (others?.length ?? 0) > 0;
25811
+ return {
25812
+ directories: [...cwd ? [cwd] : [], ...others ?? []],
25813
+ env: hasOthers ? { [ADDITIONAL_DIRECTORIES_CLAUDE_MD_ENV]: "1" } : {}
25814
+ };
25815
+ }
25816
+ function sameWorkingDirectories(previous, next) {
25817
+ return previous.length === next.length && previous.every((directory, i) => directory === next[i]);
25818
+ }
25819
+
25435
25820
  // src/adapter/sessionLifecycle.ts
25436
25821
  var RESULT_CLAIM_WAIT_MS = 1e3;
25437
25822
  var makeSessionLifecycle = (deps) => {
@@ -25575,7 +25960,7 @@ var makeSessionLifecycle = (deps) => {
25575
25960
  context.spawnEnvironment,
25576
25961
  desired.spawnEnvironment
25577
25962
  );
25578
- const reusable = !context.stopped && context.session.status !== "closed" && context.session.cwd === input.cwd && context.session.runtimeMode === input.runtimeMode && context.basePermissionMode === desired.permissionMode && context.launchEffort === desired.effectiveEffort && context.sessionSkills?.pluginDir === input.skills?.pluginDir && (resumeTarget === void 0 || resumeTarget === context.resumeSessionId) && changedEnvironmentKeys.length === 0;
25963
+ const reusable = !context.stopped && context.session.status !== "closed" && context.session.cwd === input.cwd && sameWorkingDirectories(context.additionalDirectories, desired.additionalDirectories) && context.session.runtimeMode === input.runtimeMode && context.basePermissionMode === desired.permissionMode && context.launchEffort === desired.effectiveEffort && context.sessionSkills?.pluginDir === input.skills?.pluginDir && (resumeTarget === void 0 || resumeTarget === context.resumeSessionId) && changedEnvironmentKeys.length === 0;
25579
25964
  if (!reusable) {
25580
25965
  if (changedEnvironmentKeys.length > 0) {
25581
25966
  log.info("claude.session.environment-changed", {
@@ -25584,177 +25969,60 @@ var makeSessionLifecycle = (deps) => {
25584
25969
  });
25585
25970
  }
25586
25971
  return void 0;
25587
- }
25588
- if (context.currentApiModelId !== desired.apiModelId) {
25589
- try {
25590
- await context.query.setModel(desired.apiModelId);
25591
- } catch (cause) {
25592
- throw toRequestError(context.session.threadId, "session/setModel", cause);
25593
- }
25594
- context.currentApiModelId = desired.apiModelId;
25595
- }
25596
- const settingsPatch = diffClaudeSessionSettings(context.currentSettings, desired.settings);
25597
- if (settingsPatch) {
25598
- try {
25599
- await context.query.applyFlagSettings(settingsPatch);
25600
- } catch (cause) {
25601
- throw toRequestError(context.session.threadId, "session/applyFlagSettings", cause);
25602
- }
25603
- context.currentSettings = desired.settings;
25604
- }
25605
- const updatedAt = nowIso();
25606
- context.session = {
25607
- ...context.session,
25608
- ...desired.model ? { model: desired.model } : {},
25609
- updatedAt
25610
- };
25611
- const configuredStamp = await makeEventStamp();
25612
- offerRuntimeEvent(context, {
25613
- type: "session.configured",
25614
- ...eventHeader(context, configuredStamp),
25615
- payload: {
25616
- config: {
25617
- ...desired.apiModelId ? { model: desired.apiModelId } : {},
25618
- ...input.cwd ? { cwd: input.cwd } : {},
25619
- ...desired.effectiveEffort ? { effort: desired.effectiveEffort } : {},
25620
- ...desired.permissionMode ? { permissionMode: desired.permissionMode } : {},
25621
- ...desired.settings.fastMode ? { fastMode: true } : {}
25622
- }
25623
- },
25624
- providerRefs: {}
25625
- });
25626
- return withProcessGeneration(context.session, context);
25627
- };
25628
- return {
25629
- sessions,
25630
- stopSessionInternal,
25631
- requireSession,
25632
- withProcessGeneration,
25633
- reconfigureSession
25634
- };
25635
- };
25636
-
25637
- // src/claudeProcess.ts
25638
- import * as NodeEvents from "node:events";
25639
- import * as NodeStream from "node:stream";
25640
- import { adoptChildStdin, nodeWritableFromHandle } from "@ycodium-ai/plugin-api/process-stdin";
25641
- var asError = (cause) => cause instanceof Error ? cause : new Error(String(cause));
25642
- var ClaudeProcess = class extends NodeEvents.EventEmitter {
25643
- stdin;
25644
- stdout;
25645
- killed = false;
25646
- exitCode = null;
25647
- handle;
25648
- ended = false;
25649
- constructor(host, options, stderr, cleanup) {
25650
- super();
25651
- const env2 = {
25652
- ...Object.fromEntries(Object.keys(process.env).map((key) => [key, void 0])),
25653
- ...options.env
25654
- };
25655
- const ready = host.process.spawn({
25656
- program: options.command,
25657
- args: options.args,
25658
- ...options.cwd === void 0 ? {} : { cwd: options.cwd },
25659
- env: env2,
25660
- owner: { bindingId: host.bindingId }
25661
- });
25662
- this.stdin = nodeWritableFromHandle({
25663
- writeStdin: (chunk) => ready.then((handle) => handle.writeStdin(chunk)),
25664
- closeStdin: () => ready.then((handle) => handle.closeStdin())
25665
- });
25666
- this.stdout = NodeStream.Readable.from(
25667
- (async function* () {
25668
- const handle = await ready.catch(() => void 0);
25669
- if (handle) yield* handle.stdout;
25670
- })(),
25671
- { objectMode: false }
25672
- );
25673
- adoptChildStdin(this.stdin, {
25674
- onError: (error) => {
25675
- this.emit("error", error);
25676
- }
25677
- });
25678
- this.stdout.on("error", (error) => this.emit("error", error));
25679
- this.requestKill = (signal) => {
25680
- void ready.then((handle) => this.ended ? void 0 : handle.kill(signal)).catch((cause) => this.emit("error", asError(cause)));
25681
- };
25682
- const abort = () => {
25683
- this.kill("SIGTERM");
25684
- };
25685
- options.signal.addEventListener("abort", abort, { once: true });
25686
- if (options.signal.aborted) abort();
25687
- void ready.then(async (handle) => {
25688
- this.handle = handle;
25689
- const drainStderr = (async () => {
25690
- const decoder = new TextDecoder();
25691
- for await (const chunk of handle.stderr) stderr(decoder.decode(chunk, { stream: true }));
25692
- const tail = decoder.decode();
25693
- if (tail) stderr(tail);
25694
- })();
25695
- void drainStderr.catch((cause) => this.emit("error", asError(cause)));
25696
- try {
25697
- const code = await handle.exited;
25698
- this.ended = true;
25699
- this.handle = void 0;
25700
- this.exitCode = code;
25701
- await cleanup();
25702
- this.emit("exit", code, null);
25703
- } finally {
25704
- options.signal.removeEventListener("abort", abort);
25705
- }
25706
- }).catch(async (cause) => {
25707
- this.ended = true;
25708
- this.handle = void 0;
25709
- options.signal.removeEventListener("abort", abort);
25710
- await cleanup();
25711
- this.emit("error", asError(cause));
25712
- });
25713
- }
25714
- requestKill;
25715
- kill(signal) {
25716
- if (this.ended || signal !== "SIGTERM" && signal !== "SIGKILL") return false;
25717
- this.killed = true;
25718
- this.requestKill(signal);
25719
- return true;
25720
- }
25721
- };
25722
- function makeClaudeProcessSpawner(host, stderr, config = {}) {
25723
- const cleanup = config.cleanup ?? (async () => {
25724
- });
25725
- let process2;
25726
- let launch;
25727
- return {
25728
- spawn: (options) => {
25729
- const args = [...options.args, ...config.mcpConfigArgs ?? []];
25730
- if (config.mcpConfigPath !== void 0) {
25731
- const flag = args.findLastIndex(
25732
- (arg) => arg === "--mcp-config" || arg.startsWith("--mcp-config=")
25733
- );
25734
- if (flag === -1) args.push("--mcp-config", config.mcpConfigPath);
25735
- else {
25736
- let end = flag + 1;
25737
- while (end < args.length && !args[end].startsWith("-")) end++;
25738
- args.splice(end, 0, config.mcpConfigPath);
25739
- }
25972
+ }
25973
+ if (context.currentApiModelId !== desired.apiModelId) {
25974
+ try {
25975
+ await context.query.setModel(desired.apiModelId);
25976
+ } catch (cause) {
25977
+ throw toRequestError(context.session.threadId, "session/setModel", cause);
25740
25978
  }
25741
- launch = { ...options, args };
25742
- process2 = new ClaudeProcess(host, launch, stderr, cleanup);
25743
- return process2;
25744
- },
25745
- current: () => process2?.handle && launch ? { handle: process2.handle, command: launch.command, args: launch.args } : void 0,
25746
- clear: () => {
25747
- process2 = void 0;
25748
- launch = void 0;
25749
- return cleanup();
25979
+ context.currentApiModelId = desired.apiModelId;
25750
25980
  }
25981
+ const settingsPatch = diffClaudeSessionSettings(context.currentSettings, desired.settings);
25982
+ if (settingsPatch) {
25983
+ try {
25984
+ await context.query.applyFlagSettings(settingsPatch);
25985
+ } catch (cause) {
25986
+ throw toRequestError(context.session.threadId, "session/applyFlagSettings", cause);
25987
+ }
25988
+ context.currentSettings = desired.settings;
25989
+ }
25990
+ const updatedAt = nowIso();
25991
+ context.session = {
25992
+ ...context.session,
25993
+ ...desired.model ? { model: desired.model } : {},
25994
+ updatedAt
25995
+ };
25996
+ const configuredStamp = await makeEventStamp();
25997
+ offerRuntimeEvent(context, {
25998
+ type: "session.configured",
25999
+ ...eventHeader(context, configuredStamp),
26000
+ payload: {
26001
+ config: {
26002
+ ...desired.apiModelId ? { model: desired.apiModelId } : {},
26003
+ ...input.cwd ? { cwd: input.cwd } : {},
26004
+ ...desired.effectiveEffort ? { effort: desired.effectiveEffort } : {},
26005
+ ...desired.permissionMode ? { permissionMode: desired.permissionMode } : {},
26006
+ ...desired.settings.fastMode ? { fastMode: true } : {}
26007
+ }
26008
+ },
26009
+ providerRefs: {}
26010
+ });
26011
+ return withProcessGeneration(context.session, context);
25751
26012
  };
25752
- }
26013
+ return {
26014
+ sessions,
26015
+ stopSessionInternal,
26016
+ requireSession,
26017
+ withProcessGeneration,
26018
+ reconfigureSession
26019
+ };
26020
+ };
25753
26021
 
25754
26022
  // src/claudeMcpConfig.ts
25755
26023
  import * as NodeFSP5 from "node:fs/promises";
25756
26024
  import * as NodeOS7 from "node:os";
25757
- import * as NodePath11 from "node:path";
26025
+ import * as NodePath12 from "node:path";
25758
26026
  import * as NodeProcess4 from "node:process";
25759
26027
  async function createClaudeMcpConfig(mcpServers, log) {
25760
26028
  let directory;
@@ -25769,8 +26037,8 @@ async function createClaudeMcpConfig(mcpServers, log) {
25769
26037
  return { path: void 0, cleanup };
25770
26038
  }
25771
26039
  try {
25772
- directory = await NodeFSP5.mkdtemp(NodePath11.join(NodeOS7.tmpdir(), "ycodium-claude-mcp-"));
25773
- const file = NodePath11.join(directory, "mcp.json");
26040
+ directory = await NodeFSP5.mkdtemp(NodePath12.join(NodeOS7.tmpdir(), "ycodium-claude-mcp-"));
26041
+ const file = NodePath12.join(directory, "mcp.json");
25774
26042
  await NodeFSP5.writeFile(file, JSON.stringify({ mcpServers }), { mode: 384 });
25775
26043
  await NodeFSP5.chmod(file, 384).catch((cause) => {
25776
26044
  if (NodeProcess4.platform !== "win32") throw cause;
@@ -25910,8 +26178,10 @@ var makeStartSession = (deps) => {
25910
26178
  ultracode,
25911
26179
  autoCompactWindow: recipe.autoCompactWindow
25912
26180
  });
26181
+ const workingDirectories = claudeWorkingDirectories(input.cwd, input.additionalDirectories);
26182
+ const additionalDirectories = workingDirectories.directories;
25913
26183
  const spawnEnvironment = {
25914
- processEnv: compactEnv(buildLaunchEnvironment())
26184
+ processEnv: { ...compactEnv(buildLaunchEnvironment()), ...workingDirectories.env }
25915
26185
  };
25916
26186
  const existingContext = sessions.get(input.threadId);
25917
26187
  if (existingContext) {
@@ -25921,7 +26191,8 @@ var makeStartSession = (deps) => {
25921
26191
  ...effectiveEffort ? { effectiveEffort } : {},
25922
26192
  ...modelSelection?.model ? { model: modelSelection.model } : {},
25923
26193
  settings,
25924
- spawnEnvironment
26194
+ spawnEnvironment,
26195
+ additionalDirectories
25925
26196
  });
25926
26197
  if (reconfigured) {
25927
26198
  return reconfigured;
@@ -25971,7 +26242,6 @@ var makeStartSession = (deps) => {
25971
26242
  contextAfterCompaction: input.contextAfterCompaction,
25972
26243
  log
25973
26244
  });
25974
- const additionalDirectories = input.cwd ? [input.cwd] : [];
25975
26245
  const mcpServers = toClaudeMcpServers(input.mcpServers);
25976
26246
  const querySettings = { ...settings };
25977
26247
  const permissionRules = {
@@ -26045,7 +26315,7 @@ var makeStartSession = (deps) => {
26045
26315
  },
26046
26316
  supportedDialogKinds: ["resume_return"],
26047
26317
  env: { ...spawnEnvironment.processEnv },
26048
- additionalDirectories,
26318
+ additionalDirectories: [...additionalDirectories],
26049
26319
  ...Object.keys(extraArgs).length > 0 ? { extraArgs } : {},
26050
26320
  ...claudeSessionPlugins(input.skills)
26051
26321
  };
@@ -26098,6 +26368,7 @@ var makeStartSession = (deps) => {
26098
26368
  launchEffort: effectiveEffort ?? void 0,
26099
26369
  sessionSkills: input.skills,
26100
26370
  spawnEnvironment,
26371
+ additionalDirectories,
26101
26372
  currentSettings: settings,
26102
26373
  permissionRules,
26103
26374
  currentPermissionAllow: permissionAllow,
@@ -26433,7 +26704,7 @@ var makeStreamEventHandler = (deps) => {
26433
26704
 
26434
26705
  // src/claudeWorkspaceInventory.ts
26435
26706
  import * as NodeFSP6 from "node:fs/promises";
26436
- import * as NodePath12 from "node:path";
26707
+ import * as NodePath13 from "node:path";
26437
26708
  var MAX_COMMAND_DEPTH = 5;
26438
26709
  var MAX_COMMAND_FILES = 500;
26439
26710
  var MAX_DESCRIPTION_LENGTH = 200;
@@ -26451,7 +26722,7 @@ var firstBodyLine = (contents) => {
26451
26722
  return void 0;
26452
26723
  };
26453
26724
  var scanCommands = async (claudeDir) => {
26454
- const root = NodePath12.join(claudeDir, "commands");
26725
+ const root = NodePath13.join(claudeDir, "commands");
26455
26726
  const commands = /* @__PURE__ */ new Map();
26456
26727
  const watched = [root];
26457
26728
  let level = [root];
@@ -26461,7 +26732,7 @@ var scanCommands = async (claudeDir) => {
26461
26732
  for (const directory of level) {
26462
26733
  const entries = await NodeFSP6.readdir(directory, { withFileTypes: true }).catch(() => []);
26463
26734
  for (const entry of entries.toSorted((left, right) => left.name.localeCompare(right.name))) {
26464
- const entryPath = NodePath12.join(directory, entry.name);
26735
+ const entryPath = NodePath13.join(directory, entry.name);
26465
26736
  if (entry.isDirectory()) {
26466
26737
  next.push(entryPath);
26467
26738
  watched.push(entryPath);
@@ -26489,13 +26760,13 @@ var scanCommands = async (claudeDir) => {
26489
26760
  return { commands: [...commands.values()], watched };
26490
26761
  };
26491
26762
  var watchSkillsRoot = async (claudeDir) => {
26492
- const root = NodePath12.join(claudeDir, "skills");
26763
+ const root = NodePath13.join(claudeDir, "skills");
26493
26764
  const entries = await NodeFSP6.readdir(root).catch(() => []);
26494
26765
  return [
26495
26766
  root,
26496
26767
  ...entries.flatMap((entry) => [
26497
- NodePath12.join(root, entry),
26498
- NodePath12.join(root, entry, "SKILL.md")
26768
+ NodePath13.join(root, entry),
26769
+ NodePath13.join(root, entry, "SKILL.md")
26499
26770
  ])
26500
26771
  ];
26501
26772
  };
@@ -26506,7 +26777,7 @@ var mergeByName = (base, preferred) => {
26506
26777
  return [...merged.values()].toSorted((left, right) => left.name.localeCompare(right.name));
26507
26778
  };
26508
26779
  var discoverClaudeWorkspaceInventory = async (input) => {
26509
- const projectClaudeDir = NodePath12.join(input.cwd, ".claude");
26780
+ const projectClaudeDir = NodePath13.join(input.cwd, ".claude");
26510
26781
  const [userCommands, projectCommands, skills, userSkillWatch, projectSkillWatch] = await Promise.all([
26511
26782
  scanCommands(input.configDirPath),
26512
26783
  scanCommands(projectClaudeDir),
@@ -26528,9 +26799,9 @@ var discoverClaudeWorkspaceInventory = async (input) => {
26528
26799
  ...userSkillWatch,
26529
26800
  ...projectSkillWatch,
26530
26801
  // `skillOverrides` 在这几个设置文件里:开关一个技能也要重查。
26531
- NodePath12.join(input.configDirPath, "settings.json"),
26532
- NodePath12.join(projectClaudeDir, "settings.json"),
26533
- NodePath12.join(projectClaudeDir, "settings.local.json")
26802
+ NodePath13.join(input.configDirPath, "settings.json"),
26803
+ NodePath13.join(projectClaudeDir, "settings.json"),
26804
+ NodePath13.join(projectClaudeDir, "settings.local.json")
26534
26805
  ]
26535
26806
  };
26536
26807
  };
@@ -26654,8 +26925,8 @@ var makeSystemMessageHandler = (deps) => {
26654
26925
  case "model_refusal_no_fallback": {
26655
26926
  const explanation = typeof record2.api_refusal_explanation === "string" ? record2.api_refusal_explanation.trim() : "";
26656
26927
  const content = typeof record2.content === "string" ? record2.content : "";
26657
- const text = explanation || content;
26658
- if (text.length > 0) await emitRuntimeWarning(context, text, message);
26928
+ const text2 = explanation || content;
26929
+ if (text2.length > 0) await emitRuntimeWarning(context, text2, message);
26659
26930
  return;
26660
26931
  }
26661
26932
  }
@@ -27297,7 +27568,7 @@ var makeTurnLifecycle = (deps) => {
27297
27568
  };
27298
27569
 
27299
27570
  // src/adapter/turnMessages.ts
27300
- import * as NodePath13 from "node:path";
27571
+ import * as NodePath14 from "node:path";
27301
27572
  var makeTurnMessageHandlers = (deps) => {
27302
27573
  const {
27303
27574
  emitter,
@@ -27563,7 +27834,7 @@ var makeTurnMessageHandlers = (deps) => {
27563
27834
  claudeFailureHint({
27564
27835
  authenticationFailed: context.turnState?.authenticationFailed === true,
27565
27836
  environment: buildLaunchEnvironment(),
27566
- cwd: NodePath13.resolve(context.session.cwd ?? "."),
27837
+ cwd: NodePath14.resolve(context.session.cwd ?? "."),
27567
27838
  ...context.turnState === void 0 ? {} : {
27568
27839
  usageLimit: {
27569
27840
  rejected: context.turnState.rejectedRateLimits,
@@ -27667,8 +27938,13 @@ function buildTurnSummaryPrompt(raw) {
27667
27938
  // src/textGeneration.ts
27668
27939
  import * as NodeFSP7 from "node:fs/promises";
27669
27940
  import * as NodeOS8 from "node:os";
27670
- import * as NodePath14 from "node:path";
27671
- import { mergeLaunchEnv } from "@ycodium-ai/plugin-api/executor";
27941
+ import * as NodePath15 from "node:path";
27942
+ import { mergeLaunchEnv as mergeLaunchEnv2 } from "@ycodium-ai/plugin-api/executor";
27943
+ import {
27944
+ OperationCleanupError as OperationCleanupError2,
27945
+ isOperationCleanupError as isOperationCleanupError2,
27946
+ runProcessOperation as runProcessOperation2
27947
+ } from "@ycodium-ai/plugin-api/process-operation";
27672
27948
 
27673
27949
  // src/textGenerationUtils.ts
27674
27950
  function limitSection(value, maxChars) {
@@ -27714,14 +27990,14 @@ function unwrapNestedTitleObject(raw) {
27714
27990
  return raw;
27715
27991
  }
27716
27992
  var MAX_THREAD_TITLE_CHARS = 120;
27717
- function limitTitleMessage(text, budget) {
27993
+ function limitTitleMessage(text2, budget) {
27718
27994
  const marker = "\n[Content truncated]\n";
27719
- if (text.length <= budget) return text;
27995
+ if (text2.length <= budget) return text2;
27720
27996
  if (budget <= marker.length) return "";
27721
27997
  const available = budget - marker.length;
27722
27998
  const head = Math.ceil(available / 2);
27723
27999
  const tail = available - head;
27724
- return `${text.slice(0, head)}${marker}${tail > 0 ? text.slice(-tail) : ""}`;
28000
+ return `${text2.slice(0, head)}${marker}${tail > 0 ? text2.slice(-tail) : ""}`;
27725
28001
  }
27726
28002
  function sanitizeThreadTitle(raw) {
27727
28003
  const normalized = unwrapNestedTitleObject(raw).trim().split(/\r?\n/g)[0]?.trim().replace(/^['"`]+|['"`]+$/g, "").trim().replace(/\s+/g, " ");
@@ -28032,19 +28308,12 @@ function buildHandoverSummaryPrompt(input) {
28032
28308
 
28033
28309
  // src/textGeneration.ts
28034
28310
  var CLAUDE_TIMEOUT_MS = 18e4;
28035
- function isRecord5(value) {
28311
+ function isRecord6(value) {
28036
28312
  return typeof value === "object" && value !== null && !Array.isArray(value);
28037
28313
  }
28038
- function readString3(value) {
28314
+ function readString4(value) {
28039
28315
  return typeof value === "string" ? value : "";
28040
28316
  }
28041
- async function collectText(stream) {
28042
- const chunks = [];
28043
- for await (const chunk of stream) {
28044
- chunks.push(Buffer.from(chunk));
28045
- }
28046
- return Buffer.concat(chunks).toString("utf8");
28047
- }
28048
28317
  function decodeStructuredOutput(stdout, operation) {
28049
28318
  let parsed;
28050
28319
  try {
@@ -28054,19 +28323,20 @@ function decodeStructuredOutput(stdout, operation) {
28054
28323
  cause: error
28055
28324
  });
28056
28325
  }
28057
- const envelope = Array.isArray(parsed) ? parsed.findLast((message) => isRecord5(message) && message["type"] === "result") : parsed;
28058
- if (!isRecord5(envelope) || !("structured_output" in envelope)) {
28326
+ const envelope = Array.isArray(parsed) ? parsed.findLast((message) => isRecord6(message) && message["type"] === "result") : parsed;
28327
+ if (!isRecord6(envelope) || !("structured_output" in envelope)) {
28059
28328
  throw new Error(`${operation}: Claude CLI returned unexpected output format.`);
28060
28329
  }
28061
28330
  return envelope["structured_output"];
28062
28331
  }
28063
28332
  function createClaudeTextGeneration(recipe, host, modelCatalog) {
28064
28333
  const runClaudeJson = async (input) => {
28065
- const processEnv = mergeLaunchEnv({
28334
+ input.signal?.throwIfAborted();
28335
+ const processEnv = mergeLaunchEnv2({
28066
28336
  instanceTable: recipe.environment,
28067
28337
  handOverrides: claudeHomeVariables(resolveClaudeHomeLayout(recipe))
28068
28338
  });
28069
- const modelSelection = isRecord5(input.modelSelection) ? input.modelSelection : {};
28339
+ const modelSelection = isRecord6(input.modelSelection) ? input.modelSelection : {};
28070
28340
  const requestedModel = modelIdFromSelection(modelSelection) ?? "claude-haiku-4-5";
28071
28341
  const caps = claudeModelCapabilities(requestedModel, modelCatalog(), recipe.customModels);
28072
28342
  const rawEffort = getModelSelectionStringOptionValue(modelSelection, "effort");
@@ -28102,52 +28372,46 @@ function createClaudeTextGeneration(recipe, host, modelCatalog) {
28102
28372
  "--strict-mcp-config"
28103
28373
  ];
28104
28374
  const program = recipe.binaryPath.trim() || "claude";
28375
+ let safeToRemove = true;
28105
28376
  const runIn = async (cwd) => {
28106
- const spawned = await host.process.spawn({
28107
- program,
28108
- args,
28109
- cwd,
28110
- env: processEnv,
28111
- owner: { bindingId: host.bindingId }
28112
- });
28113
- const timeout = AbortSignal.timeout(CLAUDE_TIMEOUT_MS);
28114
- const signal = input.signal ? AbortSignal.any([input.signal, timeout]) : timeout;
28115
- const abort = () => {
28116
- void spawned.kill("SIGTERM");
28117
- };
28118
- if (signal.aborted) abort();
28119
- else signal.addEventListener("abort", abort, { once: true });
28120
- await spawned.writeStdin(input.prompt);
28121
- await spawned.closeStdin();
28122
- const [stdout, stderr, exitCode] = await Promise.all([
28123
- collectText(spawned.stdout),
28124
- collectText(spawned.stderr),
28125
- spawned.exited
28126
- ]);
28127
- if (signal.aborted && !input.signal?.aborted) {
28128
- throw new Error(`${input.operation}: Claude CLI request timed out.`);
28129
- }
28130
- if (exitCode !== 0) {
28131
- const detail = stderr.trim() || stdout.trim();
28132
- throw new Error(
28133
- detail.length > 0 ? `Claude CLI command failed: ${detail}` : `Claude CLI command failed with code ${exitCode}.`
28134
- );
28377
+ try {
28378
+ const { stdout, stderr, exitCode } = await runProcessOperation2(host, {
28379
+ program,
28380
+ args,
28381
+ cwd,
28382
+ env: processEnv,
28383
+ owner: { bindingId: host.bindingId },
28384
+ signal: input.signal,
28385
+ timeoutMs: CLAUDE_TIMEOUT_MS,
28386
+ input: input.prompt
28387
+ });
28388
+ if (exitCode !== 0) {
28389
+ const detail = stderr.trim() || stdout.trim();
28390
+ throw new Error(
28391
+ detail.length > 0 ? `Claude CLI command failed: ${detail}` : `Claude CLI command failed with code ${exitCode}.`
28392
+ );
28393
+ }
28394
+ return decodeStructuredOutput(stdout, input.operation);
28395
+ } catch (cause) {
28396
+ if (cause instanceof OperationCleanupError2) safeToRemove = false;
28397
+ throw cause;
28135
28398
  }
28136
- return decodeStructuredOutput(stdout, input.operation);
28137
28399
  };
28138
28400
  if (input.operation !== "generateThreadTitle" && input.operation !== "summarizeTurn")
28139
28401
  return runIn(input.cwd);
28140
28402
  const scratchDirectory = await NodeFSP7.mkdtemp(
28141
- NodePath14.join(NodeOS8.tmpdir(), "ycodium-claude-title-")
28403
+ NodePath15.join(NodeOS8.tmpdir(), "ycodium-claude-title-")
28142
28404
  );
28143
28405
  try {
28144
28406
  return await runIn(scratchDirectory);
28145
28407
  } finally {
28146
- await NodeFSP7.rm(scratchDirectory, { recursive: true, force: true }).catch(() => void 0);
28408
+ if (safeToRemove) {
28409
+ await NodeFSP7.rm(scratchDirectory, { recursive: true, force: true }).catch(() => void 0);
28410
+ }
28147
28411
  }
28148
28412
  };
28149
28413
  const asRecord2 = (value, operation) => {
28150
- if (!isRecord5(value))
28414
+ if (!isRecord6(value))
28151
28415
  throw new Error(`${operation}: Claude returned invalid structured output.`);
28152
28416
  return value;
28153
28417
  };
@@ -28158,15 +28422,15 @@ function createClaudeTextGeneration(recipe, host, modelCatalog) {
28158
28422
  const includeBranch = input["includeBranch"] === true;
28159
28423
  const { prompt, outputSchema } = buildCommitMessagePrompt({
28160
28424
  branch: typeof input["branch"] === "string" ? input["branch"] : null,
28161
- stagedSummary: readString3(input["stagedSummary"]),
28162
- stagedPatch: readString3(input["stagedPatch"]),
28425
+ stagedSummary: readString4(input["stagedSummary"]),
28426
+ stagedPatch: readString4(input["stagedPatch"]),
28163
28427
  includeBranch,
28164
- ...isRecord5(input["policy"]) ? { policy: input["policy"] } : {}
28428
+ ...isRecord6(input["policy"]) ? { policy: input["policy"] } : {}
28165
28429
  });
28166
28430
  const generated = asRecord2(
28167
28431
  await runClaudeJson({
28168
28432
  operation: "generateCommitMessage",
28169
- cwd: readString3(input["cwd"]) || ".",
28433
+ cwd: readString4(input["cwd"]) || ".",
28170
28434
  prompt,
28171
28435
  outputSchema,
28172
28436
  modelSelection: input["modelSelection"],
@@ -28175,11 +28439,12 @@ function createClaudeTextGeneration(recipe, host, modelCatalog) {
28175
28439
  "generateCommitMessage"
28176
28440
  );
28177
28441
  return {
28178
- subject: sanitizeCommitSubject(readString3(generated["subject"])),
28179
- body: readString3(generated["body"]).trim(),
28442
+ subject: sanitizeCommitSubject(readString4(generated["subject"])),
28443
+ body: readString4(generated["body"]).trim(),
28180
28444
  ...typeof generated["branch"] === "string" ? { branch: sanitizeFeatureBranchName(generated["branch"]) } : {}
28181
28445
  };
28182
28446
  } catch (error) {
28447
+ if (isOperationCleanupError2(error)) throw error;
28183
28448
  throw normalizeCliError("claude", error, "Failed to generate commit message");
28184
28449
  }
28185
28450
  },
@@ -28187,18 +28452,18 @@ function createClaudeTextGeneration(recipe, host, modelCatalog) {
28187
28452
  try {
28188
28453
  const input = asRecord2(raw, "generatePrContent");
28189
28454
  const { prompt, outputSchema } = buildPrContentPrompt({
28190
- baseBranch: readString3(input["baseBranch"]),
28191
- headBranch: readString3(input["headBranch"]),
28192
- commitSummary: readString3(input["commitSummary"]),
28193
- diffSummary: readString3(input["diffSummary"]),
28194
- diffPatch: readString3(input["diffPatch"]),
28455
+ baseBranch: readString4(input["baseBranch"]),
28456
+ headBranch: readString4(input["headBranch"]),
28457
+ commitSummary: readString4(input["commitSummary"]),
28458
+ diffSummary: readString4(input["diffSummary"]),
28459
+ diffPatch: readString4(input["diffPatch"]),
28195
28460
  ...typeof input["changeRequestTemplate"] === "string" ? { changeRequestTemplate: input["changeRequestTemplate"] } : {},
28196
- ...isRecord5(input["policy"]) ? { policy: input["policy"] } : {}
28461
+ ...isRecord6(input["policy"]) ? { policy: input["policy"] } : {}
28197
28462
  });
28198
28463
  const generated = asRecord2(
28199
28464
  await runClaudeJson({
28200
28465
  operation: "generatePrContent",
28201
- cwd: readString3(input["cwd"]) || ".",
28466
+ cwd: readString4(input["cwd"]) || ".",
28202
28467
  prompt,
28203
28468
  outputSchema,
28204
28469
  modelSelection: input["modelSelection"],
@@ -28207,10 +28472,11 @@ function createClaudeTextGeneration(recipe, host, modelCatalog) {
28207
28472
  "generatePrContent"
28208
28473
  );
28209
28474
  return {
28210
- title: sanitizePrTitle(readString3(generated["title"])),
28211
- body: readString3(generated["body"]).trim()
28475
+ title: sanitizePrTitle(readString4(generated["title"])),
28476
+ body: readString4(generated["body"]).trim()
28212
28477
  };
28213
28478
  } catch (error) {
28479
+ if (isOperationCleanupError2(error)) throw error;
28214
28480
  throw normalizeCliError("claude", error, "Failed to generate change request content");
28215
28481
  }
28216
28482
  },
@@ -28218,14 +28484,14 @@ function createClaudeTextGeneration(recipe, host, modelCatalog) {
28218
28484
  try {
28219
28485
  const input = asRecord2(raw, "generateBranchName");
28220
28486
  const { prompt, outputSchema } = buildBranchNamePrompt({
28221
- message: readString3(input["message"]),
28487
+ message: readString4(input["message"]),
28222
28488
  ...Array.isArray(input["attachments"]) ? { attachments: input["attachments"] } : {},
28223
- ...isRecord5(input["policy"]) ? { policy: input["policy"] } : {}
28489
+ ...isRecord6(input["policy"]) ? { policy: input["policy"] } : {}
28224
28490
  });
28225
28491
  const generated = asRecord2(
28226
28492
  await runClaudeJson({
28227
28493
  operation: "generateBranchName",
28228
- cwd: readString3(input["cwd"]) || ".",
28494
+ cwd: readString4(input["cwd"]) || ".",
28229
28495
  prompt,
28230
28496
  outputSchema,
28231
28497
  modelSelection: input["modelSelection"],
@@ -28233,8 +28499,9 @@ function createClaudeTextGeneration(recipe, host, modelCatalog) {
28233
28499
  }),
28234
28500
  "generateBranchName"
28235
28501
  );
28236
- return { branch: sanitizeBranchFragment(readString3(generated["branch"])) };
28502
+ return { branch: sanitizeBranchFragment(readString4(generated["branch"])) };
28237
28503
  } catch (error) {
28504
+ if (isOperationCleanupError2(error)) throw error;
28238
28505
  throw normalizeCliError("claude", error, "Failed to generate branch name");
28239
28506
  }
28240
28507
  },
@@ -28242,16 +28509,16 @@ function createClaudeTextGeneration(recipe, host, modelCatalog) {
28242
28509
  try {
28243
28510
  const input = asRecord2(raw, "generateThreadTitle");
28244
28511
  const { prompt, outputSchema } = buildThreadTitlePrompt({
28245
- message: readString3(input["message"]),
28512
+ message: readString4(input["message"]),
28246
28513
  ...typeof input["linkedContext"] === "string" && input["linkedContext"].length > 0 ? { linkedContext: input["linkedContext"] } : {},
28247
28514
  ...typeof input["previousTitle"] === "string" ? { previousTitle: input["previousTitle"] } : {},
28248
28515
  ...Array.isArray(input["attachments"]) ? { attachments: input["attachments"] } : {},
28249
- ...isRecord5(input["policy"]) ? { policy: input["policy"] } : {}
28516
+ ...isRecord6(input["policy"]) ? { policy: input["policy"] } : {}
28250
28517
  });
28251
28518
  const generated = asRecord2(
28252
28519
  await runClaudeJson({
28253
28520
  operation: "generateThreadTitle",
28254
- cwd: readString3(input["cwd"]) || ".",
28521
+ cwd: readString4(input["cwd"]) || ".",
28255
28522
  prompt,
28256
28523
  outputSchema,
28257
28524
  modelSelection: input["modelSelection"],
@@ -28260,10 +28527,11 @@ function createClaudeTextGeneration(recipe, host, modelCatalog) {
28260
28527
  "generateThreadTitle"
28261
28528
  );
28262
28529
  return {
28263
- title: sanitizeThreadTitle(readString3(generated["title"])),
28530
+ title: sanitizeThreadTitle(readString4(generated["title"])),
28264
28531
  ...generated["needsRefinement"] === true ? { needsRefinement: true } : {}
28265
28532
  };
28266
28533
  } catch (error) {
28534
+ if (isOperationCleanupError2(error)) throw error;
28267
28535
  throw normalizeCliError("claude", error, "Failed to generate thread title");
28268
28536
  }
28269
28537
  },
@@ -28273,7 +28541,7 @@ function createClaudeTextGeneration(recipe, host, modelCatalog) {
28273
28541
  const generated = asRecord2(
28274
28542
  await runClaudeJson({
28275
28543
  operation: "summarizeTurn",
28276
- cwd: readString3(input["cwd"]) || ".",
28544
+ cwd: readString4(input["cwd"]) || ".",
28277
28545
  prompt: buildTurnSummaryPrompt(input),
28278
28546
  outputSchema: {
28279
28547
  type: "object",
@@ -28286,8 +28554,9 @@ function createClaudeTextGeneration(recipe, host, modelCatalog) {
28286
28554
  }),
28287
28555
  "summarizeTurn"
28288
28556
  );
28289
- return { summary: readString3(generated["summary"]).trim() };
28557
+ return { summary: readString4(generated["summary"]).trim() };
28290
28558
  } catch (error) {
28559
+ if (isOperationCleanupError2(error)) throw error;
28291
28560
  throw normalizeCliError("claude", error, "Failed to summarize turn");
28292
28561
  }
28293
28562
  },
@@ -28295,12 +28564,12 @@ function createClaudeTextGeneration(recipe, host, modelCatalog) {
28295
28564
  try {
28296
28565
  const input = asRecord2(raw, "summarizeHandover");
28297
28566
  const { prompt, outputSchema } = buildHandoverSummaryPrompt({
28298
- transcript: readString3(input["transcript"])
28567
+ transcript: readString4(input["transcript"])
28299
28568
  });
28300
28569
  const generated = asRecord2(
28301
28570
  await runClaudeJson({
28302
28571
  operation: "summarizeHandover",
28303
- cwd: readString3(input["cwd"]) || ".",
28572
+ cwd: readString4(input["cwd"]) || ".",
28304
28573
  prompt,
28305
28574
  outputSchema,
28306
28575
  modelSelection: input["modelSelection"],
@@ -28308,8 +28577,9 @@ function createClaudeTextGeneration(recipe, host, modelCatalog) {
28308
28577
  }),
28309
28578
  "summarizeHandover"
28310
28579
  );
28311
- return { summary: readString3(generated["summary"]).trim() };
28580
+ return { summary: readString4(generated["summary"]).trim() };
28312
28581
  } catch (error) {
28582
+ if (isOperationCleanupError2(error)) throw error;
28313
28583
  throw normalizeCliError("claude", error, "Failed to summarize handover");
28314
28584
  }
28315
28585
  }
@@ -28319,179 +28589,99 @@ function createClaudeTextGeneration(recipe, host, modelCatalog) {
28319
28589
  // src/usageRead/index.ts
28320
28590
  import * as NodePath18 from "node:path";
28321
28591
 
28322
- // src/usageRead/quota.ts
28323
- import * as NodeFS6 from "node:fs";
28324
- import * as NodePath15 from "node:path";
28325
- import { mergeLaunchEnv as mergeLaunchEnv2, setsOwnModelConnection as setsOwnModelConnection2 } from "@ycodium-ai/plugin-api/executor";
28326
- import {
28327
- parseRetryAfterMs
28328
- } from "@ycodium-ai/plugin-api/usage";
28329
- var CLAUDE_QUOTA_URL = "https://api.anthropic.com/api/oauth/usage";
28330
- var REQUEST_TIMEOUT_MS = 1e4;
28331
- var HOUR_MS = 60 * 60 * 1e3;
28332
- var CLAUDE_QUOTA_WINDOWS = [
28333
- { field: "five_hour", id: "session", labelKey: "usage.window.session", windowMs: 5 * HOUR_MS },
28334
- { field: "seven_day", id: "weekly", labelKey: "usage.window.weekly", windowMs: 7 * 24 * HOUR_MS }
28335
- ];
28336
- var isRecord6 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
28337
- var launchEnvironment = (recipe) => mergeLaunchEnv2({
28338
- inherited: process.env,
28339
- instanceTable: recipe.environment,
28340
- handOverrides: claudeHomeVariables(resolveClaudeHomeLayout(recipe))
28341
- });
28342
- var claudeCredentialFileCandidates = (recipe) => {
28343
- const environment = launchEnvironment(recipe);
28344
- const credentialsDir = resolveClaudeCredentialsDirPath(
28345
- environment[CLAUDE_CREDENTIALS_DIR_ENVIRONMENT_VARIABLE] ?? ""
28346
- );
28347
- if (credentialsDir !== null) return [NodePath15.join(credentialsDir, ".credentials.json")];
28348
- const configDir = resolveClaudeConfigDirPath(recipe.homePath, environment);
28349
- const home = resolveClaudeHomePath(recipe.homePath);
28350
- return [
28351
- .../* @__PURE__ */ new Set([
28352
- NodePath15.join(configDir, ".credentials.json"),
28353
- NodePath15.join(home, ".claude", ".credentials.json"),
28354
- NodePath15.join(home, ".credentials.json")
28355
- ])
28356
- ];
28357
- };
28358
- var readString4 = (value) => typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
28359
- var readLoginCredential = (candidates) => {
28360
- for (const path of candidates) {
28361
- let raw;
28362
- try {
28363
- raw = NodeFS6.readFileSync(path, "utf8");
28364
- } catch {
28365
- continue;
28366
- }
28367
- if (raw.trim().length === 0) continue;
28368
- let parsed;
28369
- try {
28370
- parsed = JSON.parse(raw);
28371
- } catch {
28372
- continue;
28373
- }
28374
- const oauth = isRecord6(parsed) ? parsed["claudeAiOauth"] : void 0;
28375
- if (!isRecord6(oauth)) continue;
28376
- const accessToken = readString4(oauth["accessToken"]);
28377
- if (accessToken === void 0) continue;
28378
- return {
28379
- accessToken,
28380
- subscriptionType: readString4(oauth["subscriptionType"]),
28381
- rateLimitTier: readString4(oauth["rateLimitTier"]),
28382
- path
28383
- };
28384
- }
28385
- return null;
28386
- };
28387
- var claudePlanName = (credential) => {
28388
- const type4 = credential.subscriptionType;
28389
- if (type4 === void 0) return void 0;
28390
- const name = `${type4.charAt(0).toUpperCase()}${type4.slice(1)}`;
28391
- const multiplier = /(?:^|_)(\d+x)$/.exec(credential.rateLimitTier ?? "")?.[1];
28392
- return multiplier === void 0 ? name : `${name} ${multiplier}`;
28393
- };
28394
- var toIso = (value) => {
28395
- if (typeof value !== "string") return null;
28396
- const parsed = Date.parse(value);
28397
- return Number.isNaN(parsed) ? null : new Date(parsed).toISOString();
28398
- };
28399
- var readWindows = (body) => CLAUDE_QUOTA_WINDOWS.flatMap((spec) => {
28400
- const row = body[spec.field];
28401
- if (!isRecord6(row)) return [];
28402
- const utilization = row["utilization"];
28403
- if (typeof utilization !== "number" || !Number.isFinite(utilization)) return [];
28404
- return [
28405
- {
28406
- id: spec.id,
28407
- label: { key: spec.labelKey },
28408
- usedPercent: Math.min(100, Math.max(0, utilization)),
28409
- resetsAt: toIso(row["resets_at"]),
28410
- windowMs: spec.windowMs
28592
+ // src/usageRead/nativeQuota.ts
28593
+ import * as NodeOS9 from "node:os";
28594
+ import { mergeLaunchEnv as mergeLaunchEnv3 } from "@ycodium-ai/plugin-api/executor";
28595
+ var isRecord7 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
28596
+ function quotaWriter(write) {
28597
+ const decoder = new TextDecoder();
28598
+ let pending2 = "";
28599
+ return async (chunk) => {
28600
+ pending2 += typeof chunk === "string" ? chunk : decoder.decode(chunk, { stream: true });
28601
+ let end;
28602
+ while ((end = pending2.indexOf("\n")) !== -1) {
28603
+ const line = pending2.slice(0, end);
28604
+ pending2 = pending2.slice(end + 1);
28605
+ if (line.trim().length === 0) continue;
28606
+ const frame = JSON.parse(line);
28607
+ if (isRecord7(frame) && frame.type === "control_request" && isRecord7(frame.request) && frame.request.subtype === "get_usage") {
28608
+ frame.request = { ...frame.request, skip_behaviors: true };
28609
+ }
28610
+ await write(JSON.stringify(frame) + "\n");
28411
28611
  }
28412
- ];
28413
- });
28414
- var readClaudeQuota = async (request) => {
28415
- const { recipe } = request;
28416
- const connectivity = CLAUDE_CARD.modelConnectivity;
28417
- const environmentRows = Object.entries(recipe.environment).map(([name, value]) => ({
28418
- name,
28419
- value
28420
- }));
28421
- if (connectivity !== void 0 && setsOwnModelConnection2(connectivity, environmentRows)) {
28422
- const changesEndpoint = setsOwnModelConnection2(
28423
- {
28424
- baseUrlEnvKey: connectivity.baseUrlEnvKey,
28425
- backendSwitchEnvKeys: connectivity.backendSwitchEnvKeys
28426
- },
28427
- environmentRows
28428
- );
28429
- return {
28430
- status: "unavailable",
28431
- reason: changesEndpoint ? "customEndpoint" : "unsupportedLogin"
28432
- };
28433
- }
28434
- const credential = readLoginCredential(claudeCredentialFileCandidates(recipe));
28435
- if (credential === null) return { status: "unavailable", reason: "missingCredentials" };
28436
- const now = request.now ?? Date.now;
28437
- let response;
28438
- try {
28439
- response = await request.fetch(CLAUDE_QUOTA_URL, {
28440
- method: "GET",
28441
- headers: {
28442
- Accept: "application/json",
28443
- "anthropic-beta": "oauth-2025-04-20",
28444
- Authorization: `Bearer ${credential.accessToken}`
28445
- },
28446
- redirect: "error",
28447
- signal: request.signal === void 0 ? AbortSignal.timeout(REQUEST_TIMEOUT_MS) : AbortSignal.any([request.signal, AbortSignal.timeout(REQUEST_TIMEOUT_MS)])
28448
- });
28449
- } catch (cause) {
28450
- if (request.signal?.aborted === true) throw cause;
28451
- return { status: "unavailable", reason: "unreachable" };
28452
- }
28453
- const release = () => {
28454
- void response.body?.cancel().catch(() => void 0);
28455
28612
  };
28456
- if (response.status === 429) {
28457
- const retryAfterMs = parseRetryAfterMs(response.headers.get("Retry-After"), now());
28458
- release();
28459
- return {
28460
- status: "unavailable",
28461
- reason: "rateLimited",
28462
- ...retryAfterMs === null ? {} : { retryAfterMs }
28463
- };
28464
- }
28465
- if (response.status === 401 || response.status === 403) {
28466
- release();
28467
- return { status: "unavailable", reason: "authRejected" };
28468
- }
28469
- if (response.status < 200 || response.status >= 300) {
28470
- release();
28471
- return { status: "unavailable", reason: "unreachable" };
28613
+ }
28614
+ function quotaReading(value) {
28615
+ if (!isRecord7(value)) return { status: "unavailable", reason: "unreachable" };
28616
+ if (value.rate_limits_available === false) {
28617
+ return { status: "unavailable", reason: "unsupportedLogin" };
28472
28618
  }
28473
- let body;
28474
- try {
28475
- body = JSON.parse(await response.text());
28476
- } catch {
28619
+ if (value.rate_limits_available !== true || !isRecord7(value.rate_limits)) {
28477
28620
  return { status: "unavailable", reason: "unreachable" };
28478
28621
  }
28479
- if (!isRecord6(body)) return { status: "unavailable", reason: "unreachable" };
28480
- const plan = claudePlanName(credential);
28622
+ const plan = claudePlanName({
28623
+ subscriptionType: typeof value.subscription_type === "string" ? value.subscription_type : void 0,
28624
+ rateLimitTier: void 0
28625
+ });
28481
28626
  return {
28482
28627
  status: "available",
28483
28628
  ...plan === void 0 ? {} : { plan },
28484
- windows: readWindows(body),
28485
- balances: [],
28486
- accountKey: credential.path
28629
+ windows: readClaudeQuotaWindows(value.rate_limits),
28630
+ balances: []
28487
28631
  };
28488
- };
28632
+ }
28633
+ async function readClaudeNativeQuota(input) {
28634
+ const { recipe, host, signal } = input;
28635
+ signal?.throwIfAborted();
28636
+ if (!recipe.binaryPath) return void 0;
28637
+ try {
28638
+ const environment = mergeLaunchEnv3({
28639
+ inherited: process.env,
28640
+ instanceTable: recipe.environment,
28641
+ handOverrides: claudeHomeVariables(resolveClaudeHomeLayout(recipe))
28642
+ });
28643
+ const { flags, positionals } = parseCliArgs(recipe.launchArgs);
28644
+ if (positionals.length > 0 || ["resume", "continue", "fork-session", "session-id", "prompt", "print"].some(
28645
+ (key) => key in flags
28646
+ )) {
28647
+ return { status: "unavailable", reason: "unreachable" };
28648
+ }
28649
+ const result = await withClaudeSdkRead(
28650
+ {
28651
+ host,
28652
+ signal,
28653
+ timeoutMs: input.timeoutMs,
28654
+ createQuery: XAe,
28655
+ stdin: quotaWriter,
28656
+ options: {
28657
+ cwd: NodeOS9.tmpdir(),
28658
+ pathToClaudeCodeExecutable: resolveClaudeSdkExecutablePath(
28659
+ recipe.binaryPath,
28660
+ environment
28661
+ ),
28662
+ env: compactEnv(claudeModelProbeEnvironment(environment)),
28663
+ settingSources: [...CLAUDE_SETTING_SOURCES],
28664
+ extraArgs: flags
28665
+ }
28666
+ },
28667
+ (runtime) => runtime.usage_EXPERIMENTAL_MAY_CHANGE_DO_NOT_RELY_ON_THIS_API_YET()
28668
+ );
28669
+ return quotaReading(result);
28670
+ } catch (error) {
28671
+ signal?.throwIfAborted();
28672
+ const message = error instanceof Error ? error.message : "";
28673
+ if (/get_usage/i.test(message) && /not supported|unknown|unrecognized|unsupported/i.test(message)) {
28674
+ return void 0;
28675
+ }
28676
+ return { status: "unavailable", reason: "unreachable" };
28677
+ }
28678
+ }
28489
28679
 
28490
28680
  // src/usageRead/rateLimitEvent.ts
28491
- var isRecord7 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
28681
+ var isRecord8 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
28492
28682
  var claudeQuotaFromRateLimitEvent = (message) => {
28493
- const info = isRecord7(message) ? message["rate_limit_info"] : void 0;
28494
- if (!isRecord7(info)) return null;
28683
+ const info = isRecord8(message) ? message["rate_limit_info"] : void 0;
28684
+ if (!isRecord8(info)) return null;
28495
28685
  const spec = CLAUDE_QUOTA_WINDOWS.find((window2) => window2.field === info["rateLimitType"]);
28496
28686
  const utilization = info["utilization"];
28497
28687
  if (spec === void 0 || typeof utilization !== "number" || !Number.isFinite(utilization)) {
@@ -28899,7 +29089,7 @@ var createClaudeTokenReader = (options) => {
28899
29089
  status: "missing",
28900
29090
  scannedFiles: 0,
28901
29091
  skippedFiles: 0,
28902
- message: "No Claude Code session records found on this machine."
29092
+ message: "No Claude Code session records found in the configured directory."
28903
29093
  }
28904
29094
  };
28905
29095
  }
@@ -28996,7 +29186,12 @@ var createClaudeUsage = (input) => {
28996
29186
  cache: input.cache
28997
29187
  });
28998
29188
  return {
28999
- readQuota: ({ signal }) => readClaudeQuota({ recipe, fetch: host.fetch, signal }),
29189
+ readQuota: async ({ signal }) => {
29190
+ const native = await readClaudeNativeQuota({ recipe, host, signal });
29191
+ if (native !== void 0) return native;
29192
+ const fallback = await readClaudeQuota({ recipe, fetch: host.fetch, signal });
29193
+ return recipe.binaryPath && fallback.status === "unavailable" && fallback.reason === "missingCredentials" ? { status: "unavailable", reason: "unreachable" } : fallback;
29194
+ },
29000
29195
  quotaFromRateLimits: claudeQuotaFromRateLimitEvent,
29001
29196
  readTokenUsage: tokens.readTokenUsage
29002
29197
  };
@@ -29067,12 +29262,13 @@ function createClaudeAdapter(recipe, host, options) {
29067
29262
  if (recipe.binaryPath.length === 0) throw new Error(CLAUDE_PROGRAM_NOT_CONFIGURED);
29068
29263
  return sdkCreateQuery(input);
29069
29264
  };
29070
- const buildLaunchEnvironment = () => mergeLaunchEnv3({
29265
+ const buildLaunchEnvironment = () => mergeLaunchEnv4({
29071
29266
  inherited: { ...process.env, ...options?.environment },
29072
29267
  instanceTable: recipe.environment,
29073
29268
  handOverrides: claudeHomeVariables(resolveClaudeHomeLayout(recipe))
29074
29269
  });
29075
29270
  const modelCatalog = makeClaudeModelCatalog({
29271
+ host,
29076
29272
  recipe,
29077
29273
  now,
29078
29274
  createQuery,
@@ -29220,6 +29416,7 @@ function createClaudeAdapter(recipe, host, options) {
29220
29416
  modelCatalog: {
29221
29417
  refresh: modelCatalog.refresh,
29222
29418
  current: currentModelCatalog,
29419
+ currentAuth: modelCatalog.currentAuth,
29223
29420
  lastRefreshOk: modelCatalog.lastRefreshOk,
29224
29421
  notePairedRefresh: modelCatalog.notePairedRefresh
29225
29422
  },
@@ -29282,18 +29479,25 @@ function createClaudeInstance(config, host, attachments, overrides, usageCache)
29282
29479
  };
29283
29480
  return {
29284
29481
  probe,
29285
- readCliVersion: () => readClaudeCliVersion({ recipe, host }),
29482
+ readCliVersion: (signal) => readClaudeCliVersion({ recipe, host, ...signal ? { signal } : {} }),
29286
29483
  // `force: false` 立刻返回。每天那次探测自己按清单 TTL 决定要不要读;这里只服务「版本变了」和手动刷新。
29287
- refreshModelCatalog: async ({ force }) => {
29484
+ refreshModelCatalog: async ({ force, signal }) => {
29485
+ signal?.throwIfAborted();
29288
29486
  if (!force) return;
29487
+ let cleanupFailed = false;
29289
29488
  try {
29290
- await adapter.modelCatalog.refresh({ force: true });
29489
+ await adapter.modelCatalog.refresh({ force: true, signal });
29291
29490
  } catch (error) {
29491
+ if (isOperationCleanupError3(error)) {
29492
+ cleanupFailed = true;
29493
+ throw error;
29494
+ }
29495
+ signal?.throwIfAborted();
29292
29496
  host.log.warn("claude model catalog refresh failed", {
29293
29497
  detail: error instanceof Error ? error.message : String(error)
29294
29498
  });
29295
29499
  } finally {
29296
- adapter.modelCatalog.notePairedRefresh();
29500
+ if (!signal?.aborted && !cleanupFailed) adapter.modelCatalog.notePairedRefresh();
29297
29501
  }
29298
29502
  },
29299
29503
  startSession: adapter.startSession,
@@ -29349,7 +29553,7 @@ function createClaudeInstance(config, host, attachments, overrides, usageCache)
29349
29553
  start: async () => {
29350
29554
  const command = claudeSignInCommand(
29351
29555
  recipe.credentialsPath,
29352
- NodeOS9.type() === "Windows_NT" ? "win32" : "linux"
29556
+ NodeOS10.type() === "Windows_NT" ? "win32" : "linux"
29353
29557
  );
29354
29558
  return {
29355
29559
  operationId: `claude-sign-in-${crypto.randomUUID()}`,
@@ -29358,7 +29562,11 @@ function createClaudeInstance(config, host, attachments, overrides, usageCache)
29358
29562
  },
29359
29563
  cancel: async () => {
29360
29564
  },
29361
- verify: async (signal) => probe(signal)
29565
+ verify: async (signal) => {
29566
+ await adapter.modelCatalog.refresh({ signal, force: true });
29567
+ adapter.modelCatalog.notePairedRefresh();
29568
+ return probe(signal);
29569
+ }
29362
29570
  }
29363
29571
  };
29364
29572
  }
@@ -29377,7 +29585,7 @@ var executors = [
29377
29585
  ];
29378
29586
  var index_default = definePlugin({
29379
29587
  id: "agent-claude",
29380
- apiVersion: 39,
29588
+ apiVersion: 41,
29381
29589
  executors,
29382
29590
  activate(ctx) {
29383
29591
  capturedAttachments = ctx.attachments;