@ycodium-ai/agent-claude 0.2.3488 → 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 +860 -670
  3. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -349,8 +349,8 @@ async function* historyBytes(path, start, end, signal) {
349
349
  async function* historyText(path, range, signal) {
350
350
  const decoder = new TextDecoder("utf-8", { fatal: true });
351
351
  for await (const bytes of historyBytes(path, range.start, range.end, signal)) {
352
- const text = decoder.decode(bytes, { stream: true });
353
- if (text) yield text;
352
+ const text2 = decoder.decode(bytes, { stream: true });
353
+ if (text2) yield text2;
354
354
  }
355
355
  const last = decoder.decode();
356
356
  if (last) yield last;
@@ -693,33 +693,33 @@ var OversizedRecordParser = class {
693
693
  this.#strCap = cap;
694
694
  }
695
695
  /** 字符串里的一个解码后片段;按保留规则累积。 */
696
- #append(text) {
697
- if (text.length === 0) return;
696
+ #append(text2) {
697
+ if (text2.length === 0) return;
698
698
  if (this.#stringIsKey) {
699
699
  if (this.#keyOverflow) return;
700
- this.#key += text;
700
+ this.#key += text2;
701
701
  if (this.#key.length > MAX_KEY_CHARS) {
702
702
  this.#keyOverflow = true;
703
703
  this.#key = "";
704
704
  }
705
705
  return;
706
706
  }
707
- this.#strLength += text.length;
708
- this.#strObserver?.onChunk?.(text);
707
+ this.#strLength += text2.length;
708
+ this.#strObserver?.onChunk?.(text2);
709
709
  if (!this.#strRetain) return;
710
710
  const cap = this.#strCap;
711
711
  if (cap === void 0) {
712
- this.#strHead += text;
713
- this.#charge(text.length);
712
+ this.#strHead += text2;
713
+ this.#charge(text2.length);
714
714
  return;
715
715
  }
716
716
  if (this.#strHead.length < cap) {
717
717
  const room = cap - this.#strHead.length;
718
- this.#strHead += text.slice(0, room);
719
- text = text.slice(room);
720
- if (text.length === 0) return;
718
+ this.#strHead += text2.slice(0, room);
719
+ text2 = text2.slice(room);
720
+ if (text2.length === 0) return;
721
721
  }
722
- this.#strTail += text;
722
+ this.#strTail += text2;
723
723
  if (this.#strTail.length > cap * 2) this.#strTail = this.#strTail.slice(-cap);
724
724
  }
725
725
  #consumeString(chunk, start) {
@@ -809,35 +809,35 @@ var OversizedRecordParser = class {
809
809
  return;
810
810
  }
811
811
  const cap = this.#strCap;
812
- let text = this.#strHead;
812
+ let text2 = this.#strHead;
813
813
  if (cap !== void 0) {
814
814
  let tail = this.#strTail;
815
815
  if (tail.length > cap) tail = tail.slice(-cap);
816
- if (this.#strLength > text.length + tail.length) {
817
- 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);
818
818
  if (isLowSurrogate(tail.charCodeAt(0))) tail = tail.slice(1);
819
- const omitted = this.#strLength - text.length - tail.length;
820
- text = `${text}
819
+ const omitted = this.#strLength - text2.length - tail.length;
820
+ text2 = `${text2}
821
821
  [\u2026 ${omitted} chars omitted \u2026]
822
822
  ${tail}`;
823
823
  } else {
824
- text = `${text}${tail}`;
824
+ text2 = `${text2}${tail}`;
825
825
  }
826
- this.#charge(text.length);
826
+ this.#charge(text2.length);
827
827
  }
828
828
  this.#strHead = "";
829
829
  this.#strTail = "";
830
- this.#store(text);
830
+ this.#store(text2);
831
831
  }
832
832
  #finishBare() {
833
- const text = this.#bare;
833
+ const text2 = this.#bare;
834
834
  this.#bare = "";
835
835
  const { mode, path } = this.#childMode(false);
836
836
  let value;
837
- if (text === "true") value = true;
838
- else if (text === "false") value = false;
839
- else if (text === "null") value = null;
840
- 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);
841
841
  else this.#fail();
842
842
  const observer = this.#fields.find(
843
843
  (field) => field.path.length === path.length && matchesPrefix(field.path, path, path.length)
@@ -893,14 +893,14 @@ async function* readJsonlEntries(fs2, path, options) {
893
893
  pending2 = 0;
894
894
  return next;
895
895
  };
896
- const append = (text) => {
897
- if (text.length === 0) return;
896
+ const append = (text2) => {
897
+ if (text2.length === 0) return;
898
898
  if (parser !== null) {
899
- parser.write(text);
899
+ parser.write(text2);
900
900
  return;
901
901
  }
902
- pieces.push(text);
903
- pending2 += text.length;
902
+ pieces.push(text2);
903
+ pending2 += text2.length;
904
904
  if (pending2 > options.maxLineChars) parser = startOversized();
905
905
  };
906
906
  const takeLine = () => {
@@ -916,15 +916,15 @@ async function* readJsonlEntries(fs2, path, options) {
916
916
  if (line.endsWith("\r")) line = line.slice(0, -1);
917
917
  return line.length === 0 ? null : { line };
918
918
  };
919
- const feed = function* (text) {
919
+ const feed = function* (text2) {
920
920
  let from = 0;
921
- while (from <= text.length) {
922
- const newline = text.indexOf("\n", from);
921
+ while (from <= text2.length) {
922
+ const newline = text2.indexOf("\n", from);
923
923
  if (newline < 0) {
924
- append(text.slice(from));
924
+ append(text2.slice(from));
925
925
  return;
926
926
  }
927
- append(text.slice(from, newline));
927
+ append(text2.slice(from, newline));
928
928
  const entry = takeLine();
929
929
  if (entry !== null) yield entry;
930
930
  from = newline + 1;
@@ -950,9 +950,9 @@ var textFields = (paths, accept) => {
950
950
  return paths.map((path) => ({
951
951
  path,
952
952
  scalarOnly: true,
953
- onChunk: (text) => {
953
+ onChunk: (text2) => {
954
954
  begin();
955
- accept(text);
955
+ accept(text2);
956
956
  },
957
957
  onEnd: () => {
958
958
  begin();
@@ -970,16 +970,16 @@ var createHistoryRecordAccumulator = (layout) => {
970
970
  const parser = new OversizedRecordParser(
971
971
  [
972
972
  ...selected.fields,
973
- ...textFields(selected.textPaths, (text) => {
974
- messageTotal += text.length;
973
+ ...textFields(selected.textPaths, (text2) => {
974
+ messageTotal += text2.length;
975
975
  })
976
976
  ],
977
977
  Number.MAX_SAFE_INTEGER,
978
978
  { pruneEmpty: true }
979
979
  );
980
- const accept = (text) => {
981
- rawTotal += text.length;
982
- parser.write(text);
980
+ const accept = (text2) => {
981
+ rawTotal += text2.length;
982
+ parser.write(text2);
983
983
  };
984
984
  return {
985
985
  write: (bytes) => {
@@ -1021,11 +1021,11 @@ async function* historyRecordWindows(path, info, mode, from, until, limit, layou
1021
1021
  let offset = from;
1022
1022
  let pending2 = "";
1023
1023
  const ready = [];
1024
- const accept = (text) => {
1024
+ const accept = (text2) => {
1025
1025
  const begin = Math.max(0, from - seen);
1026
- const end = Math.min(text.length, until - seen);
1027
- seen += text.length;
1028
- 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);
1029
1029
  while (pending2.length >= limit) {
1030
1030
  let size = limit;
1031
1031
  const code = pending2.charCodeAt(size - 1);
@@ -1039,9 +1039,9 @@ async function* historyRecordWindows(path, info, mode, from, until, limit, layou
1039
1039
  const parser = mode === "raw" ? void 0 : new OversizedRecordParser(textFields(selected.textPaths, accept), Number.MAX_SAFE_INTEGER, {
1040
1040
  pruneEmpty: true
1041
1041
  });
1042
- for await (const text of historyText(path, info.range, signal)) {
1043
- if (parser === void 0) accept(text);
1044
- 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);
1045
1045
  while (ready.length > 0) yield ready.shift();
1046
1046
  if (seen >= until) break;
1047
1047
  }
@@ -1053,7 +1053,7 @@ var verifyHistoryRecord = async (path, range, layout, signal) => {
1053
1053
  pruneEmpty: true
1054
1054
  });
1055
1055
  try {
1056
- 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);
1057
1057
  } catch (error) {
1058
1058
  if (!range.terminated && error instanceof TypeError) return false;
1059
1059
  throw error;
@@ -1092,7 +1092,7 @@ var width = (n) => String(n).padStart(20, "0");
1092
1092
  var high = (n) => n >= 55296 && n <= 56319;
1093
1093
  var low = (n) => n >= 56320 && n <= 57343;
1094
1094
  var totalOf = (info, index) => info.items[index].text === "raw" ? info.rawTotal : info.messageTotal;
1095
- var nativeItem = (identity, info, index, offset, text) => {
1095
+ var nativeItem = (identity, info, index, offset, text2) => {
1096
1096
  const { text: _mode, ...item } = info.items[index];
1097
1097
  const total = totalOf(info, index);
1098
1098
  const position = { e: identity, p: info.range.start, k: index, h: info.hash };
@@ -1101,13 +1101,13 @@ var nativeItem = (identity, info, index, offset, text) => {
1101
1101
  id: `${identity}:${info.range.start}:${index}`,
1102
1102
  orderKey: `${width(info.range.start)}:${width(index)}`,
1103
1103
  beforeCursor: encode({ ...position, o: offset }),
1104
- afterCursor: encode({ ...position, o: offset + text.length }),
1105
- text,
1104
+ afterCursor: encode({ ...position, o: offset + text2.length }),
1105
+ text: text2,
1106
1106
  textWindow: {
1107
1107
  offset,
1108
- returned: text.length,
1108
+ returned: text2.length,
1109
1109
  total,
1110
- nextOffset: offset + text.length < total ? offset + text.length : null
1110
+ nextOffset: offset + text2.length < total ? offset + text2.length : null
1111
1111
  }
1112
1112
  };
1113
1113
  };
@@ -1131,7 +1131,7 @@ var readWindow = async (path, info, index, from, until, layout, signal, cached)
1131
1131
  cached.text = parts.join("");
1132
1132
  cached.mode = mode;
1133
1133
  }
1134
- let text = cached.mode === mode ? cached.text?.slice(base, end) ?? "" : "";
1134
+ let text2 = cached.mode === mode ? cached.text?.slice(base, end) ?? "" : "";
1135
1135
  if (cached.mode !== mode) {
1136
1136
  for await (const window2 of historyRecordWindows(
1137
1137
  path,
@@ -1143,13 +1143,13 @@ var readWindow = async (path, info, index, from, until, layout, signal, cached)
1143
1143
  layout,
1144
1144
  signal
1145
1145
  ))
1146
- text += window2.text;
1146
+ text2 += window2.text;
1147
1147
  }
1148
1148
  let startIndex = from - base;
1149
1149
  let endIndex = until - base;
1150
- if (low(text.charCodeAt(startIndex)) && high(text.charCodeAt(startIndex - 1))) startIndex -= 1;
1151
- if (low(text.charCodeAt(endIndex)) && high(text.charCodeAt(endIndex - 1))) endIndex -= 1;
1152
- 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) };
1153
1153
  };
1154
1154
  async function openJsonlHistory(path, layout, signal) {
1155
1155
  const controller = new AbortController();
@@ -1546,10 +1546,10 @@ async function readClaudeTranscriptDigest(filePath) {
1546
1546
  continue;
1547
1547
  }
1548
1548
  if (typed.type !== "user") continue;
1549
- const text = textFromContent(typed.message?.content);
1550
- if (text.length === 0) continue;
1549
+ const text2 = textFromContent(typed.message?.content);
1550
+ if (text2.length === 0) continue;
1551
1551
  turnCount += 1;
1552
- if (preview === null) preview = text;
1552
+ if (preview === null) preview = text2;
1553
1553
  }
1554
1554
  } catch {
1555
1555
  return null;
@@ -1731,20 +1731,20 @@ async function readClaudeSessionRecord(input) {
1731
1731
  const content = message.content;
1732
1732
  const blocks = typeof content === "string" ? [{ type: "text", text: content }] : Array.isArray(content) ? content : [];
1733
1733
  const hasToolResult = blocks.some((block) => isRecord(block) && block.type === "tool_result");
1734
- const text = blocks.flatMap((block) => {
1734
+ const text2 = blocks.flatMap((block) => {
1735
1735
  const typed = isRecord(block) ? block : void 0;
1736
1736
  if (!typed) return [];
1737
1737
  if (typed.type === "text" && typeof typed.text === "string") return [typed.text];
1738
1738
  return [];
1739
1739
  }).join("\n\n").trim();
1740
- if (text.length === 0) continue;
1740
+ if (text2.length === 0) continue;
1741
1741
  if (value.type === "user" && hasToolResult) continue;
1742
1742
  const role = value.type === "assistant" ? "assistant" : "user";
1743
1743
  if (role === "assistant" && typeof message.model === "string") model = message.model;
1744
1744
  const timestamp = typeof value.timestamp === "string" ? Date.parse(value.timestamp) : typeof value.timestamp === "number" ? value.timestamp : void 0;
1745
1745
  messages.push({
1746
1746
  role,
1747
- text,
1747
+ text: text2,
1748
1748
  ...timestamp !== void 0 && Number.isFinite(timestamp) ? { createdAt: timestamp } : {}
1749
1749
  });
1750
1750
  }
@@ -1791,8 +1791,8 @@ var claudeHistoryLayout = (input) => () => {
1791
1791
  ].map((key) => ({ path: [key], scalarOnly: true, maxChars: 2048 })),
1792
1792
  {
1793
1793
  path: ["message", "content", "*", "type"],
1794
- onChunk: (text) => {
1795
- blockType = (blockType + text).slice(0, 128);
1794
+ onChunk: (text2) => {
1795
+ blockType = (blockType + text2).slice(0, 128);
1796
1796
  },
1797
1797
  onEnd: () => {
1798
1798
  hasTools ||= blockType === "tool_use" || blockType === "tool_result";
@@ -1802,20 +1802,20 @@ var claudeHistoryLayout = (input) => () => {
1802
1802
  },
1803
1803
  {
1804
1804
  path: ["message", "content", "*", "name"],
1805
- onChunk: (text) => {
1806
- name = (name + text).slice(0, 256);
1805
+ onChunk: (text2) => {
1806
+ name = (name + text2).slice(0, 256);
1807
1807
  }
1808
1808
  },
1809
1809
  {
1810
1810
  path: ["message", "content", "*", "id"],
1811
- onChunk: (text) => {
1812
- toolCallId = (toolCallId + text).slice(0, 256);
1811
+ onChunk: (text2) => {
1812
+ toolCallId = (toolCallId + text2).slice(0, 256);
1813
1813
  }
1814
1814
  },
1815
1815
  {
1816
1816
  path: ["message", "content", "*", "tool_use_id"],
1817
- onChunk: (text) => {
1818
- toolCallId = (toolCallId + text).slice(0, 256);
1817
+ onChunk: (text2) => {
1818
+ toolCallId = (toolCallId + text2).slice(0, 256);
1819
1819
  }
1820
1820
  }
1821
1821
  ],
@@ -1869,7 +1869,7 @@ var openClaudeHistory = (input) => {
1869
1869
  };
1870
1870
 
1871
1871
  // src/claudeInstance.ts
1872
- import * as NodeOS9 from "node:os";
1872
+ import * as NodeOS10 from "node:os";
1873
1873
  import * as NodePath19 from "node:path";
1874
1874
 
1875
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
@@ -21303,15 +21303,19 @@ function HU(e, t) {
21303
21303
  }
21304
21304
 
21305
21305
  // src/claudeInstance.ts
21306
- 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";
21307
21308
 
21308
21309
  // src/claudeProbe.ts
21309
21310
  import {
21310
21311
  describeExecutorSpawnFailure,
21311
21312
  ExecutorSpawnAbortedError,
21312
- ExecutorSpawnRefusedError,
21313
- raceAbort
21313
+ ExecutorSpawnRefusedError
21314
21314
  } from "@ycodium-ai/plugin-api/executor";
21315
+ import {
21316
+ runProcessOperation,
21317
+ isOperationCleanupError
21318
+ } from "@ycodium-ai/plugin-api/process-operation";
21315
21319
 
21316
21320
  // src/claudeAccount.ts
21317
21321
  import * as NodeFS4 from "node:fs";
@@ -21370,10 +21374,10 @@ function claudeCredentialCandidatePaths(input) {
21370
21374
  NodePath5.join(input.homePath, ".credentials.json")
21371
21375
  ] : [NodePath5.join(input.credentialsDirPath, ".credentials.json")];
21372
21376
  }
21373
- function readClaudeAccount(credentialsDirPath) {
21377
+ function readClaudeAccount(credentialsDirPath, credentialCandidates) {
21374
21378
  const accountDirectory = resolveClaudeAccountDirectory(credentialsDirPath);
21375
21379
  const exists = directoryExists(accountDirectory);
21376
- const candidates = claudeCredentialCandidatePaths({
21380
+ const candidates = credentialCandidates ?? claudeCredentialCandidatePaths({
21377
21381
  homePath: NodeOS2.homedir(),
21378
21382
  credentialsDirPath
21379
21383
  });
@@ -21400,6 +21404,14 @@ function readClaudeAccount(credentialsDirPath) {
21400
21404
  };
21401
21405
  }
21402
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
+
21403
21415
  // src/claudeHome.ts
21404
21416
  import * as NodeOS4 from "node:os";
21405
21417
  import * as NodePath7 from "node:path";
@@ -21549,9 +21561,171 @@ function claudeFailureHint(input) {
21549
21561
  return void 0;
21550
21562
  }
21551
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
+
21552
21726
  // src/claudeSkills.ts
21553
21727
  import * as NodeFSP3 from "node:fs/promises";
21554
- import * as NodePath8 from "node:path";
21728
+ import * as NodePath9 from "node:path";
21555
21729
  import * as NodeProcess3 from "node:process";
21556
21730
  var FRONT_MATTER_KEY = /^(description|disable-model-invocation|user-invocable|argument-hint):[ \t]*(.*)$/;
21557
21731
  var BLOCK_SCALAR = /^[|>][+-]?$/;
@@ -21617,7 +21791,7 @@ function claudeManagedSettingsPath(platform4, environment) {
21617
21791
  }
21618
21792
  if (platform4 === "win32") {
21619
21793
  const programData = environment["PROGRAMDATA"]?.trim();
21620
- return programData ? NodePath8.join(programData, "ClaudeCode", "managed-settings.json") : void 0;
21794
+ return programData ? NodePath9.join(programData, "ClaudeCode", "managed-settings.json") : void 0;
21621
21795
  }
21622
21796
  return "/etc/claude-code/managed-settings.json";
21623
21797
  }
@@ -21625,44 +21799,44 @@ function skillOverrideSettingsPaths(configDirPath, cwd, platform4, environment,
21625
21799
  const managedPath = claudeManagedSettingsPath(platform4, environment);
21626
21800
  const root = repositoryRoot !== void 0 && repositoryRoot !== cwd ? repositoryRoot : void 0;
21627
21801
  return [
21628
- NodePath8.join(configDirPath, "settings.json"),
21802
+ NodePath9.join(configDirPath, "settings.json"),
21629
21803
  ...cwd ? [
21630
- NodePath8.join(cwd, ".claude", "settings.json"),
21631
- NodePath8.join(cwd, ".claude", "settings.local.json")
21804
+ NodePath9.join(cwd, ".claude", "settings.json"),
21805
+ NodePath9.join(cwd, ".claude", "settings.local.json")
21632
21806
  ] : [],
21633
- ...root ? [NodePath8.join(root, ".claude", "settings.local.json")] : [],
21807
+ ...root ? [NodePath9.join(root, ".claude", "settings.local.json")] : [],
21634
21808
  ...managedPath ? [managedPath] : []
21635
21809
  ];
21636
21810
  }
21637
21811
  async function findRepositoryRoot(cwd) {
21638
- let current = NodePath8.resolve(cwd);
21812
+ let current = NodePath9.resolve(cwd);
21639
21813
  for (; ; ) {
21640
- const found = await NodeFSP3.stat(NodePath8.join(current, ".git")).then(
21814
+ const found = await NodeFSP3.stat(NodePath9.join(current, ".git")).then(
21641
21815
  () => true,
21642
21816
  () => false
21643
21817
  );
21644
21818
  if (found) return current;
21645
- const parent = NodePath8.dirname(current);
21819
+ const parent = NodePath9.dirname(current);
21646
21820
  if (parent === current) return void 0;
21647
21821
  current = parent;
21648
21822
  }
21649
21823
  }
21650
- function parseLenientJson(text) {
21824
+ function parseLenientJson(text2) {
21651
21825
  let output = "";
21652
21826
  let index = 0;
21653
- while (index < text.length) {
21654
- const char = text[index];
21655
- const next = text[index + 1];
21827
+ while (index < text2.length) {
21828
+ const char = text2[index];
21829
+ const next = text2[index + 1];
21656
21830
  if (char === '"') {
21657
21831
  let end = index + 1;
21658
- while (end < text.length && text[end] !== '"') end += text[end] === "\\" ? 2 : 1;
21659
- 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);
21660
21834
  index = end + 1;
21661
21835
  } else if (char === "/" && next === "/") {
21662
- while (index < text.length && text[index] !== "\n") index += 1;
21836
+ while (index < text2.length && text2[index] !== "\n") index += 1;
21663
21837
  } else if (char === "/" && next === "*") {
21664
- const end = text.indexOf("*/", index + 2);
21665
- index = end === -1 ? text.length : end + 2;
21838
+ const end = text2.indexOf("*/", index + 2);
21839
+ index = end === -1 ? text2.length : end + 2;
21666
21840
  } else {
21667
21841
  output += char;
21668
21842
  index += 1;
@@ -21677,10 +21851,10 @@ var SKILL_OVERRIDE_BY_VALUE = {
21677
21851
  on: { enabled: true, userInvocationOnly: false },
21678
21852
  "name-only": { enabled: true, userInvocationOnly: false }
21679
21853
  };
21680
- function readOverridesFromSettings(text) {
21854
+ function readOverridesFromSettings(text2) {
21681
21855
  let parsed;
21682
21856
  try {
21683
- parsed = parseLenientJson(text);
21857
+ parsed = parseLenientJson(text2);
21684
21858
  } catch {
21685
21859
  return void 0;
21686
21860
  }
@@ -21724,8 +21898,8 @@ async function discoverClaudeSkills(configDirPath, options = {}) {
21724
21898
  options.environment ?? NodeProcess3.env
21725
21899
  );
21726
21900
  const roots = [
21727
- { directory: NodePath8.join(configDirPath, "skills"), scope: "user" },
21728
- ...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" }] : []
21729
21903
  ];
21730
21904
  const skills = /* @__PURE__ */ new Map();
21731
21905
  for (const root of roots) {
@@ -21733,7 +21907,7 @@ async function discoverClaudeSkills(configDirPath, options = {}) {
21733
21907
  for (const entry of entries.toSorted()) {
21734
21908
  const name = entry.trim();
21735
21909
  if (name.length === 0 || skills.has(name)) continue;
21736
- const skillPath = NodePath8.join(root.directory, entry, "SKILL.md");
21910
+ const skillPath = NodePath9.join(root.directory, entry, "SKILL.md");
21737
21911
  const contents = await NodeFSP3.readFile(skillPath, "utf8").catch(() => void 0);
21738
21912
  if (contents === void 0) continue;
21739
21913
  const frontMatter = parseSkillFrontMatter(contents);
@@ -21796,15 +21970,15 @@ function knownClaudeModelCapabilities(model, catalog, customModels) {
21796
21970
  function claudeModelCapabilities(model, catalog, customModels) {
21797
21971
  return knownClaudeModelCapabilities(model, catalog, customModels) ?? DEFAULT_CLAUDE_MODEL_CAPABILITIES;
21798
21972
  }
21799
- function isRecord3(value) {
21973
+ function isRecord4(value) {
21800
21974
  return typeof value === "object" && value !== null && !Array.isArray(value);
21801
21975
  }
21802
21976
  function getModelSelectionStringOptionValue(modelSelection, optionId) {
21803
- if (!isRecord3(modelSelection)) return void 0;
21977
+ if (!isRecord4(modelSelection)) return void 0;
21804
21978
  const options = modelSelection["options"];
21805
21979
  if (!Array.isArray(options)) return void 0;
21806
21980
  for (const entry of options) {
21807
- if (!isRecord3(entry)) continue;
21981
+ if (!isRecord4(entry)) continue;
21808
21982
  if (entry["id"] === optionId && typeof entry["value"] === "string") {
21809
21983
  const trimmed = entry["value"].trim();
21810
21984
  if (trimmed.length > 0) return trimmed;
@@ -21813,11 +21987,11 @@ function getModelSelectionStringOptionValue(modelSelection, optionId) {
21813
21987
  return void 0;
21814
21988
  }
21815
21989
  function getModelSelectionBooleanOptionValue(modelSelection, optionId) {
21816
- if (!isRecord3(modelSelection)) return void 0;
21990
+ if (!isRecord4(modelSelection)) return void 0;
21817
21991
  const options = modelSelection["options"];
21818
21992
  if (!Array.isArray(options)) return void 0;
21819
21993
  for (const entry of options) {
21820
- if (!isRecord3(entry)) continue;
21994
+ if (!isRecord4(entry)) continue;
21821
21995
  if (entry["id"] === optionId && typeof entry["value"] === "boolean") {
21822
21996
  return entry["value"];
21823
21997
  }
@@ -21825,7 +21999,7 @@ function getModelSelectionBooleanOptionValue(modelSelection, optionId) {
21825
21999
  return void 0;
21826
22000
  }
21827
22001
  function modelIdFromSelection(selection) {
21828
- if (!isRecord3(selection)) return void 0;
22002
+ if (!isRecord4(selection)) return void 0;
21829
22003
  const model = selection["model"];
21830
22004
  return typeof model === "string" ? model.trim() || void 0 : void 0;
21831
22005
  }
@@ -22067,16 +22241,12 @@ function claudeToolSearchNotice(version, environment) {
22067
22241
  var CLAUDE_PROGRAM_NOT_CONFIGURED = "Claude program path is not configured";
22068
22242
  function createClaudeProbe(input) {
22069
22243
  const { recipe, host, modelCatalog } = input;
22070
- const collectProcessText = async (stream) => {
22071
- const chunks = [];
22072
- for await (const chunk of stream) {
22073
- chunks.push(Buffer.from(chunk));
22074
- }
22075
- return Buffer.concat(chunks).toString("utf8");
22076
- };
22077
22244
  const probeAuth = () => {
22078
- const hasEnvToken = (recipe.environment.ANTHROPIC_AUTH_TOKEN ?? "").trim().length > 0;
22079
- 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;
22080
22250
  if (hasEnvToken || hasEnvApiKey) {
22081
22251
  return {
22082
22252
  status: "authenticated",
@@ -22084,13 +22254,15 @@ function createClaudeProbe(input) {
22084
22254
  label: hasEnvToken ? "ANTHROPIC_AUTH_TOKEN" : "ANTHROPIC_API_KEY"
22085
22255
  };
22086
22256
  }
22087
- const account = readClaudeAccount(resolveClaudeCredentialsDirPath(recipe.credentialsPath));
22257
+ const account = readClaudeAccount(
22258
+ resolveClaudeCredentialsDirPath(recipe.credentialsPath),
22259
+ claudeCredentialFileCandidates(recipe)
22260
+ );
22088
22261
  switch (account.state) {
22089
22262
  case "signed-in":
22090
- case "expired":
22091
22263
  return { status: "authenticated" };
22264
+ case "expired":
22092
22265
  case "signed-out":
22093
- return { status: "unauthenticated" };
22094
22266
  case "unreadable":
22095
22267
  return { status: "unknown" };
22096
22268
  }
@@ -22109,16 +22281,31 @@ function createClaudeProbe(input) {
22109
22281
  if (signal?.aborted) {
22110
22282
  throw new ExecutorSpawnAbortedError();
22111
22283
  }
22112
- 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
+ };
22113
22296
  try {
22114
- handle = await host.process.spawn({
22297
+ command = await runProcessOperation(commandHost, {
22115
22298
  program: recipe.binaryPath,
22116
22299
  args: ["--version"],
22300
+ timeoutMs: CLAUDE_VERSION_TIMEOUT_MS,
22301
+ maxOutputBytes: 64 * 1024,
22117
22302
  owner: { bindingId: host.bindingId },
22118
22303
  ...signal === void 0 ? {} : { signal }
22119
22304
  });
22120
22305
  } catch (error) {
22121
- 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) {
22122
22309
  throw error;
22123
22310
  }
22124
22311
  const failure = describeExecutorSpawnFailure(error);
@@ -22130,14 +22317,7 @@ function createClaudeProbe(input) {
22130
22317
  detail: failure.detail
22131
22318
  };
22132
22319
  }
22133
- const [stdout, stderr, exitCode] = await raceAbort(
22134
- Promise.all([
22135
- collectProcessText(handle.stdout),
22136
- collectProcessText(handle.stderr),
22137
- handle.exited
22138
- ]),
22139
- signal
22140
- );
22320
+ const { stdout, stderr, exitCode } = command;
22141
22321
  if (signal?.aborted) throw new ExecutorSpawnAbortedError();
22142
22322
  const version = parseGenericCliVersion(`${stdout}
22143
22323
  ${stderr}`);
@@ -22197,49 +22377,29 @@ ${stderr}`);
22197
22377
  }
22198
22378
  var CLAUDE_VERSION_TIMEOUT_MS = 15e3;
22199
22379
  async function readClaudeCliVersion(input) {
22380
+ input.signal?.throwIfAborted();
22200
22381
  if (input.recipe.binaryPath.length === 0) return null;
22201
- let handle;
22202
22382
  try {
22203
- handle = await input.host.process.spawn({
22383
+ const { stdout, stderr } = await runProcessOperation(input.host, {
22204
22384
  program: input.recipe.binaryPath,
22205
22385
  args: ["--version"],
22206
- 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
22207
22390
  });
22208
- } catch {
22209
- return null;
22210
- }
22211
- const collect = async (stream) => {
22212
- const chunks = [];
22213
- for await (const chunk of stream) chunks.push(Buffer.from(chunk));
22214
- return Buffer.concat(chunks).toString("utf8");
22215
- };
22216
- const collected = Promise.all([collect(handle.stdout), collect(handle.stderr), handle.exited]);
22217
- let timer;
22218
- const timeout = new Promise((resolve5) => {
22219
- timer = setTimeout(() => resolve5("timeout"), CLAUDE_VERSION_TIMEOUT_MS);
22220
- timer.unref?.();
22221
- });
22222
- try {
22223
- const settled = await Promise.race([collected, timeout]);
22224
- if (settled === "timeout") {
22225
- collected.catch(() => void 0);
22226
- return null;
22227
- }
22228
- const [stdout, stderr] = settled;
22229
- return parseGenericCliVersion(`${stdout}
22230
- ${stderr}`);
22231
- } catch {
22391
+ return parseGenericCliVersion(stdout + "\n" + stderr);
22392
+ } catch (error) {
22393
+ if (isOperationCleanupError(error)) throw error;
22394
+ input.signal?.throwIfAborted();
22232
22395
  return null;
22233
- } finally {
22234
- if (timer !== void 0) clearTimeout(timer);
22235
- await handle.kill("SIGTERM").catch(() => void 0);
22236
22396
  }
22237
22397
  }
22238
22398
 
22239
22399
  // src/claudeExecutable.ts
22240
- import * as NodeFS5 from "node:fs";
22400
+ import * as NodeFS6 from "node:fs";
22241
22401
  import * as NodeOS5 from "node:os";
22242
- import * as NodePath9 from "node:path";
22402
+ import * as NodePath10 from "node:path";
22243
22403
  var WINDOWS_SHIM_EXTENSIONS = /* @__PURE__ */ new Set([".cmd", ".bat", ".ps1"]);
22244
22404
  var NPM_PACKAGE_ENTRY_CANDIDATES = [
22245
22405
  ["node_modules", "@anthropic-ai", "claude-code", "bin", "claude.exe"],
@@ -22247,7 +22407,7 @@ var NPM_PACKAGE_ENTRY_CANDIDATES = [
22247
22407
  ];
22248
22408
  function isExistingFile(filePath) {
22249
22409
  try {
22250
- return NodeFS5.statSync(filePath).isFile();
22410
+ return NodeFS6.statSync(filePath).isFile();
22251
22411
  } catch {
22252
22412
  return false;
22253
22413
  }
@@ -22257,13 +22417,13 @@ function resolveClaudeSdkExecutablePath(binaryPath, environment = process.env, i
22257
22417
  return binaryPath;
22258
22418
  }
22259
22419
  const resolved = resolveWindowsExecutable(binaryPath, environment) ?? binaryPath;
22260
- const extension = NodePath9.win32.extname(resolved).toLowerCase();
22420
+ const extension = NodePath10.win32.extname(resolved).toLowerCase();
22261
22421
  if (!WINDOWS_SHIM_EXTENSIONS.has(extension)) {
22262
22422
  return resolved;
22263
22423
  }
22264
- const shimDirectory = NodePath9.win32.dirname(resolved);
22424
+ const shimDirectory = NodePath10.win32.dirname(resolved);
22265
22425
  for (const entrySegments of NPM_PACKAGE_ENTRY_CANDIDATES) {
22266
- const candidate = NodePath9.win32.join(shimDirectory, ...entrySegments);
22426
+ const candidate = NodePath10.win32.join(shimDirectory, ...entrySegments);
22267
22427
  if (isFile(candidate)) {
22268
22428
  return candidate;
22269
22429
  }
@@ -22273,14 +22433,14 @@ function resolveClaudeSdkExecutablePath(binaryPath, environment = process.env, i
22273
22433
  function resolveWindowsExecutable(binaryPath, environment) {
22274
22434
  const trimmed = binaryPath.trim();
22275
22435
  if (trimmed.length === 0) return void 0;
22276
- if (NodePath9.win32.isAbsolute(trimmed) && isExistingFile(trimmed)) return trimmed;
22436
+ if (NodePath10.win32.isAbsolute(trimmed) && isExistingFile(trimmed)) return trimmed;
22277
22437
  const pathValue = environment.PATH ?? environment.Path ?? "";
22278
22438
  const pathext = environment.PATHEXT ?? ".EXE;.CMD;.BAT;.COM";
22279
22439
  const extensions = pathext.split(";").filter((ext) => ext.length > 0);
22280
- const hasExt = NodePath9.win32.extname(trimmed).length > 0;
22440
+ const hasExt = NodePath10.win32.extname(trimmed).length > 0;
22281
22441
  for (const dir of pathValue.split(";")) {
22282
22442
  if (dir.trim().length === 0) continue;
22283
- 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}`));
22284
22444
  for (const candidate of candidates) {
22285
22445
  if (isExistingFile(candidate)) return candidate;
22286
22446
  }
@@ -22330,7 +22490,7 @@ function asStringField(input, key) {
22330
22490
  const value = input[key];
22331
22491
  return typeof value === "string" ? value : void 0;
22332
22492
  }
22333
- function readString(value) {
22493
+ function readString2(value) {
22334
22494
  return typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
22335
22495
  }
22336
22496
  function readStringArray(value) {
@@ -22500,14 +22660,14 @@ function readRangeSuffix(input) {
22500
22660
  function loneStringValue(input) {
22501
22661
  let found;
22502
22662
  for (const value of Object.values(input)) {
22503
- const text = asTrimmedString(value);
22504
- if (text === void 0) {
22663
+ const text2 = asTrimmedString(value);
22664
+ if (text2 === void 0) {
22505
22665
  continue;
22506
22666
  }
22507
22667
  if (found !== void 0) {
22508
22668
  return void 0;
22509
22669
  }
22510
- found = text;
22670
+ found = text2;
22511
22671
  }
22512
22672
  return found;
22513
22673
  }
@@ -23322,11 +23482,11 @@ var makeAssistantText = (emitter) => {
23322
23482
  blockIndex: block.blockIndex,
23323
23483
  block
23324
23484
  }));
23325
- for (const [position, text] of snapshotTextBlocks.entries()) {
23485
+ for (const [position, text2] of snapshotTextBlocks.entries()) {
23326
23486
  const existingEntry = orderedBlocks[position];
23327
23487
  let entry = existingEntry;
23328
23488
  if (!entry) {
23329
- const created = await createSyntheticAssistantTextBlock(context, text);
23489
+ const created = await createSyntheticAssistantTextBlock(context, text2);
23330
23490
  if (created) {
23331
23491
  orderedBlocks.push(created);
23332
23492
  }
@@ -23336,7 +23496,7 @@ var makeAssistantText = (emitter) => {
23336
23496
  continue;
23337
23497
  }
23338
23498
  if (entry.block.fallbackText.length === 0) {
23339
- entry.block.fallbackText = text;
23499
+ entry.block.fallbackText = text2;
23340
23500
  }
23341
23501
  if (entry.block.streamClosed && !entry.block.completionEmitted) {
23342
23502
  await completeAssistantTextBlock(context, entry.block, {
@@ -23434,8 +23594,8 @@ function applyClaudeTaskToolResult(tasks, tool, result) {
23434
23594
  continue;
23435
23595
  }
23436
23596
  const task2 = entry;
23437
- const id2 = readString(task2.id);
23438
- const subject2 = readString(task2.subject);
23597
+ const id2 = readString2(task2.id);
23598
+ const subject2 = readString2(task2.subject);
23439
23599
  if (!id2 || !subject2) {
23440
23600
  continue;
23441
23601
  }
@@ -23450,8 +23610,8 @@ function applyClaudeTaskToolResult(tasks, tool, result) {
23450
23610
  }
23451
23611
  if (tool.toolName === "TaskCreate") {
23452
23612
  const resultTask = readClaudeTaskFromResult(result);
23453
- const id2 = readString(resultTask?.id);
23454
- 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);
23455
23615
  if (!id2 || !subject2) {
23456
23616
  return false;
23457
23617
  }
@@ -23463,7 +23623,7 @@ function applyClaudeTaskToolResult(tasks, tool, result) {
23463
23623
  });
23464
23624
  return true;
23465
23625
  }
23466
- const taskId = readString(tool.input.taskId) ?? readString(result?.taskId);
23626
+ const taskId = readString2(tool.input.taskId) ?? readString2(result?.taskId);
23467
23627
  if (!taskId) {
23468
23628
  return false;
23469
23629
  }
@@ -23471,7 +23631,7 @@ function applyClaudeTaskToolResult(tasks, tool, result) {
23471
23631
  if (!task) {
23472
23632
  return false;
23473
23633
  }
23474
- const subject = readString(tool.input.subject);
23634
+ const subject = readString2(tool.input.subject);
23475
23635
  if (subject && task.subject !== subject) {
23476
23636
  task.subject = subject;
23477
23637
  changed = true;
@@ -23882,23 +24042,23 @@ function tokenizeShellWords(value) {
23882
24042
 
23883
24043
  // src/config.ts
23884
24044
  var CLAUDE_AUTO_COMPACT_WINDOW_PATTERN = /^(?:|[1-9]\d{5}|1000000)$/;
23885
- function isRecord4(value) {
24045
+ function isRecord5(value) {
23886
24046
  return typeof value === "object" && value !== null && !Array.isArray(value);
23887
24047
  }
23888
- function readString2(value) {
24048
+ function readString3(value) {
23889
24049
  return typeof value === "string" ? value : "";
23890
24050
  }
23891
24051
  function readTrimmed(value) {
23892
- return readString2(value).trim();
24052
+ return readString3(value).trim();
23893
24053
  }
23894
24054
  function readEnvironment(value) {
23895
24055
  if (!Array.isArray(value)) return {};
23896
24056
  const env2 = {};
23897
24057
  for (const entry of value) {
23898
- if (!isRecord4(entry)) continue;
24058
+ if (!isRecord5(entry)) continue;
23899
24059
  const name = readTrimmed(entry["name"]);
23900
24060
  if (name.length === 0) continue;
23901
- env2[name] = readString2(entry["value"]);
24061
+ env2[name] = readString3(entry["value"]);
23902
24062
  }
23903
24063
  return env2;
23904
24064
  }
@@ -23911,12 +24071,12 @@ function readCustomModels(value) {
23911
24071
  if (slug2.length > 0) models.push({ slug: slug2 });
23912
24072
  continue;
23913
24073
  }
23914
- if (!isRecord4(entry)) continue;
24074
+ if (!isRecord5(entry)) continue;
23915
24075
  const slug = readTrimmed(entry["slug"]);
23916
24076
  if (slug.length === 0) continue;
23917
24077
  const thinkingRaw = entry["thinkingOptions"];
23918
24078
  const thinkingOptions = Array.isArray(thinkingRaw) ? thinkingRaw.flatMap((option) => {
23919
- if (!isRecord4(option)) return [];
24079
+ if (!isRecord5(option)) return [];
23920
24080
  const id2 = readTrimmed(option["id"]) || readTrimmed(option["value"]);
23921
24081
  const label = readTrimmed(option["label"]) || id2;
23922
24082
  if (id2.length === 0) return [];
@@ -23949,7 +24109,7 @@ function readAutoCompactWindow(value) {
23949
24109
  return CLAUDE_AUTO_COMPACT_WINDOW_PATTERN.test(trimmed) ? trimmed : "";
23950
24110
  }
23951
24111
  function parseClaudeInstanceConfig(config) {
23952
- if (!isRecord4(config)) {
24112
+ if (!isRecord5(config)) {
23953
24113
  return {
23954
24114
  binaryPath: "",
23955
24115
  homePath: "",
@@ -23962,13 +24122,13 @@ function parseClaudeInstanceConfig(config) {
23962
24122
  envelopeDriver: void 0
23963
24123
  };
23964
24124
  }
23965
- const nested = isRecord4(config["config"]) ? config["config"] : void 0;
24125
+ const nested = isRecord5(config["config"]) ? config["config"] : void 0;
23966
24126
  const settings = nested ?? config;
23967
24127
  return {
23968
24128
  binaryPath: readTrimmed(settings["binaryPath"]),
23969
24129
  homePath: readTrimmed(settings["homePath"]) || readTrimmed(config["homePath"]),
23970
24130
  credentialsPath: readTrimmed(settings["credentialsPath"]) || readTrimmed(config["credentialsPath"]),
23971
- launchArgs: readString2(settings["launchArgs"]),
24131
+ launchArgs: readString3(settings["launchArgs"]),
23972
24132
  autoCompactWindow: readAutoCompactWindow(settings["autoCompactWindow"]),
23973
24133
  customModels: readCustomModels(settings["customModels"]),
23974
24134
  enabled: settings["enabled"] !== false,
@@ -24017,91 +24177,295 @@ function parseCliArgs(args, options) {
24017
24177
  return { flags, positionals, variadicArgs };
24018
24178
  }
24019
24179
 
24180
+ // src/adapter/modelCatalog.ts
24181
+ import {
24182
+ raceAbort
24183
+ } from "@ycodium-ai/plugin-api/executor";
24184
+
24020
24185
  // src/sdkModels.ts
24021
24186
  import * as NodeOS6 from "node:os";
24022
24187
  import { REASONING_OPTION_LABEL as REASONING_OPTION_LABEL2, reasoningEffortShortLabel } from "@ycodium-ai/plugin-api/executor";
24023
- var CLAUDE_SDK_MODELS_TIMEOUT_MS = 2e4;
24024
- var CLAUDE_SDK_MODELS_TTL_MS = 30 * 6e4;
24025
- var CLAUDE_SDK_MODELS_RETRY_MS = 2 * 6e4;
24026
- var effortLabel = (level) => reasoningEffortShortLabel(level) ?? `${level.slice(0, 1).toUpperCase()}${level.slice(1)}`;
24027
- var claudeCatalogModelFromSdk = (info) => {
24028
- const descriptors = [];
24029
- const levels = info.supportsEffort === false ? [] : info.supportedEffortLevels ?? [];
24030
- if (levels.length > 0) {
24031
- descriptors.push({
24032
- type: "select",
24033
- id: "effort",
24034
- label: REASONING_OPTION_LABEL2,
24035
- options: levels.map((level) => ({ id: level, label: effortLabel(level) }))
24036
- });
24037
- }
24038
- if (info.supportsFastMode === true) {
24039
- 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
+ };
24040
24210
  }
24041
- return {
24042
- slug: info.value,
24043
- name: info.displayName.trim().length > 0 ? info.displayName : info.value,
24044
- isCustom: false,
24045
- capabilities: { optionDescriptors: descriptors }
24046
- };
24047
- };
24048
- var claudeCatalogFromSdk = (models) => {
24049
- const seen = /* @__PURE__ */ new Set();
24050
- const out = [];
24051
- for (const info of models) {
24052
- if (typeof info.value !== "string" || info.value.trim().length === 0) continue;
24053
- if (seen.has(info.value)) continue;
24054
- seen.add(info.value);
24055
- 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 } };
24056
24219
  }
24057
- return out;
24058
- };
24059
- var silentPrompt = (signal) => ({
24060
- [Symbol.asyncIterator]: () => ({
24061
- next: () => new Promise((resolve5) => {
24062
- if (signal.aborted) resolve5({ done: true, value: void 0 });
24063
- signal.addEventListener("abort", () => resolve5({ done: true, value: void 0 }), {
24064
- once: true
24065
- });
24066
- }),
24067
- return: async () => ({ done: true, value: void 0 })
24068
- })
24069
- });
24070
- var readClaudeSdkModels = async (input) => {
24071
- const abort = new AbortController();
24072
- const forwardAbort = () => abort.abort();
24073
- input.signal?.addEventListener("abort", forwardAbort, { once: true });
24074
- if (input.signal?.aborted) abort.abort();
24075
- let runtime;
24076
- let timer;
24077
- try {
24078
- runtime = input.createQuery({
24079
- prompt: silentPrompt(abort.signal),
24080
- options: { ...input.options, abortController: abort }
24081
- });
24082
- if (runtime.supportedModels === void 0) return void 0;
24083
- const timeout = new Promise((_3, reject) => {
24084
- timer = setTimeout(
24085
- () => reject(new Error("supportedModels timed out")),
24086
- input.timeoutMs ?? CLAUDE_SDK_MODELS_TIMEOUT_MS
24087
- );
24088
- });
24089
- const cancelled = new Promise((_3, reject) => {
24090
- abort.signal.addEventListener("abort", () => reject(new Error("aborted")), { once: true });
24091
- });
24092
- cancelled.catch(() => {
24093
- });
24094
- return await Promise.race([runtime.supportedModels(), timeout, cancelled]);
24095
- } catch {
24096
- return void 0;
24097
- } finally {
24098
- if (timer !== void 0) clearTimeout(timer);
24099
- input.signal?.removeEventListener("abort", forwardAbort);
24100
- abort.abort();
24101
- try {
24102
- runtime?.close();
24103
- } 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));
24104
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;
24105
24469
  }
24106
24470
  };
24107
24471
  function claudeModelProbeEnvironment(environment) {
@@ -24134,10 +24498,12 @@ var makeClaudeModelCatalog = (deps) => {
24134
24498
  let modelCatalogRead;
24135
24499
  let modelCatalogFailedAt;
24136
24500
  let catalogReadOk = false;
24501
+ let auth;
24137
24502
  let successfulReadAt;
24138
24503
  let skipPairedRefresh = false;
24139
24504
  const current = () => modelCatalog?.models ?? [];
24140
24505
  const refresh = (input) => {
24506
+ input?.signal?.throwIfAborted();
24141
24507
  if (input?.paired === true && skipPairedRefresh) {
24142
24508
  skipPairedRefresh = false;
24143
24509
  return Promise.resolve();
@@ -24151,10 +24517,11 @@ var makeClaudeModelCatalog = (deps) => {
24151
24517
  catalogReadOk = false;
24152
24518
  return Promise.resolve();
24153
24519
  }
24154
- if (modelCatalogRead !== void 0) return modelCatalogRead;
24520
+ if (modelCatalogRead !== void 0) return raceAbort(modelCatalogRead, input?.signal);
24155
24521
  const read = (async () => {
24156
24522
  const extraArgs = parseCliArgs(deps.recipe.launchArgs).flags;
24157
- const models = await readClaudeSdkModels({
24523
+ const probe = await readClaudeSdkProbe({
24524
+ host: deps.host,
24158
24525
  createQuery: deps.createQuery,
24159
24526
  signal: input?.signal,
24160
24527
  options: {
@@ -24165,7 +24532,9 @@ var makeClaudeModelCatalog = (deps) => {
24165
24532
  ...Object.keys(extraArgs).length > 0 ? { extraArgs } : {}
24166
24533
  }
24167
24534
  });
24168
- if (models === void 0) {
24535
+ input?.signal?.throwIfAborted();
24536
+ auth = probe?.auth;
24537
+ if (probe === void 0) {
24169
24538
  modelCatalogFailedAt = deps.now();
24170
24539
  catalogReadOk = false;
24171
24540
  successfulReadAt = void 0;
@@ -24174,7 +24543,7 @@ var makeClaudeModelCatalog = (deps) => {
24174
24543
  modelCatalogFailedAt = void 0;
24175
24544
  catalogReadOk = true;
24176
24545
  successfulReadAt = readAt;
24177
- modelCatalog = { models: claudeCatalogFromSdk(models), readAt };
24546
+ modelCatalog = { models: claudeCatalogFromSdk(probe.models), readAt };
24178
24547
  }
24179
24548
  })().finally(() => {
24180
24549
  modelCatalogRead = void 0;
@@ -24184,6 +24553,7 @@ var makeClaudeModelCatalog = (deps) => {
24184
24553
  };
24185
24554
  return {
24186
24555
  current,
24556
+ currentAuth: () => catalogReadOk ? auth : void 0,
24187
24557
  refresh,
24188
24558
  /** 此刻正在进行的那次读取;没有就是 undefined。起会话等它读完,不为此另起一次。 */
24189
24559
  pendingRead: () => modelCatalogRead,
@@ -24319,8 +24689,8 @@ var SKILL_TOKEN_REGEX = new RegExp(
24319
24689
  String.raw`(^|\s)([$/])(${SKILL_TOKEN_NAME_PATTERN})(?=\s|$)`,
24320
24690
  "g"
24321
24691
  );
24322
- function findSkillTokens(text) {
24323
- return [...text.matchAll(SKILL_TOKEN_REGEX)].map((match) => {
24692
+ function findSkillTokens(text2) {
24693
+ return [...text2.matchAll(SKILL_TOKEN_REGEX)].map((match) => {
24324
24694
  const start = (match.index ?? 0) + (match[1]?.length ?? 0);
24325
24695
  const name = match[3] ?? "";
24326
24696
  return { sigil: match[2], name, start, end: start + name.length + 1 };
@@ -24341,9 +24711,9 @@ function lookupSkill(name, index) {
24341
24711
  return session?.scope === SESSION_SKILL_NAMESPACE ? session : void 0;
24342
24712
  }
24343
24713
  var SLASH_SKILL_TOKEN = new RegExp(String.raw`^/(${SKILL_TOKEN_NAME_PATTERN})$`);
24344
- function matchSkillTokens(text, index) {
24714
+ function matchSkillTokens(text2, index) {
24345
24715
  if (index.size === 0) return [];
24346
- return findSkillTokens(text).flatMap((token) => {
24716
+ return findSkillTokens(text2).flatMap((token) => {
24347
24717
  const skill = lookupSkill(token.name, index);
24348
24718
  return skill === void 0 ? [] : [{ ...token, skill }];
24349
24719
  });
@@ -24357,7 +24727,7 @@ import { EXECUTOR_SEND_TURN_MAX_INPUT_CHARS as EXECUTOR_SEND_TURN_MAX_INPUT_CHAR
24357
24727
 
24358
24728
  // src/claudeInlineSkill.ts
24359
24729
  import * as NodeFSP4 from "node:fs/promises";
24360
- import * as NodePath10 from "node:path";
24730
+ import * as NodePath11 from "node:path";
24361
24731
  import { EXECUTOR_SEND_TURN_MAX_INPUT_CHARS } from "@ycodium-ai/plugin-api/executor";
24362
24732
 
24363
24733
  // src/claudeSkillArguments.ts
@@ -24468,7 +24838,7 @@ var notLoadedBlock = (name, reason) => skillBlock(name, [
24468
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.`
24469
24839
  ]);
24470
24840
  var renderLoaded = (invocation, source, variables) => {
24471
- const skillDir = NodePath10.dirname(invocation.path);
24841
+ const skillDir = NodePath11.dirname(invocation.path);
24472
24842
  const known = {
24473
24843
  CLAUDE_SKILL_DIR: skillDir,
24474
24844
  CLAUDE_PROJECT_DIR: variables.projectDir,
@@ -24619,14 +24989,14 @@ function assertSteeringTarget(expectedTurnId, turnState) {
24619
24989
  }
24620
24990
 
24621
24991
  // src/hostMcpAllowRules.ts
24622
- import { setsOwnModelConnection } from "@ycodium-ai/plugin-api/executor";
24992
+ import { setsOwnModelConnection as setsOwnModelConnection2 } from "@ycodium-ai/plugin-api/executor";
24623
24993
  function claudeHostMcpAllowRules(servers) {
24624
24994
  return (servers ?? []).filter((server) => server.hostOwned === true).map((server) => `mcp__${server.name}`);
24625
24995
  }
24626
24996
  function claudeUsesOfficialChannel(env2) {
24627
24997
  const connectivity = CLAUDE_CARD.modelConnectivity;
24628
24998
  const rows = Object.entries(env2).map(([name, value]) => ({ name, value }));
24629
- if (setsOwnModelConnection({ backendSwitchEnvKeys: connectivity?.backendSwitchEnvKeys }, rows))
24999
+ if (setsOwnModelConnection2({ backendSwitchEnvKeys: connectivity?.backendSwitchEnvKeys }, rows))
24630
25000
  return false;
24631
25001
  const key = connectivity?.baseUrlEnvKey;
24632
25002
  const baseUrl = key === void 0 ? void 0 : env2[key]?.trim();
@@ -25649,127 +26019,10 @@ var makeSessionLifecycle = (deps) => {
25649
26019
  };
25650
26020
  };
25651
26021
 
25652
- // src/claudeProcess.ts
25653
- import * as NodeEvents from "node:events";
25654
- import * as NodeStream from "node:stream";
25655
- import { adoptChildStdin, nodeWritableFromHandle } from "@ycodium-ai/plugin-api/process-stdin";
25656
- var asError = (cause) => cause instanceof Error ? cause : new Error(String(cause));
25657
- var ClaudeProcess = class extends NodeEvents.EventEmitter {
25658
- stdin;
25659
- stdout;
25660
- killed = false;
25661
- exitCode = null;
25662
- handle;
25663
- ended = false;
25664
- constructor(host, options, stderr, cleanup) {
25665
- super();
25666
- const env2 = {
25667
- ...Object.fromEntries(Object.keys(process.env).map((key) => [key, void 0])),
25668
- ...options.env
25669
- };
25670
- const ready = host.process.spawn({
25671
- program: options.command,
25672
- args: options.args,
25673
- ...options.cwd === void 0 ? {} : { cwd: options.cwd },
25674
- env: env2,
25675
- owner: { bindingId: host.bindingId }
25676
- });
25677
- this.stdin = nodeWritableFromHandle({
25678
- writeStdin: (chunk) => ready.then((handle) => handle.writeStdin(chunk)),
25679
- closeStdin: () => ready.then((handle) => handle.closeStdin())
25680
- });
25681
- this.stdout = NodeStream.Readable.from(
25682
- (async function* () {
25683
- const handle = await ready.catch(() => void 0);
25684
- if (handle) yield* handle.stdout;
25685
- })(),
25686
- { objectMode: false }
25687
- );
25688
- adoptChildStdin(this.stdin, {
25689
- onError: (error) => {
25690
- this.emit("error", error);
25691
- }
25692
- });
25693
- this.stdout.on("error", (error) => this.emit("error", error));
25694
- this.requestKill = (signal) => {
25695
- void ready.then((handle) => this.ended ? void 0 : handle.kill(signal)).catch((cause) => this.emit("error", asError(cause)));
25696
- };
25697
- const abort = () => {
25698
- this.kill("SIGTERM");
25699
- };
25700
- options.signal.addEventListener("abort", abort, { once: true });
25701
- if (options.signal.aborted) abort();
25702
- void ready.then(async (handle) => {
25703
- this.handle = handle;
25704
- const drainStderr = (async () => {
25705
- const decoder = new TextDecoder();
25706
- for await (const chunk of handle.stderr) stderr(decoder.decode(chunk, { stream: true }));
25707
- const tail = decoder.decode();
25708
- if (tail) stderr(tail);
25709
- })();
25710
- void drainStderr.catch((cause) => this.emit("error", asError(cause)));
25711
- try {
25712
- const code = await handle.exited;
25713
- this.ended = true;
25714
- this.handle = void 0;
25715
- this.exitCode = code;
25716
- await cleanup();
25717
- this.emit("exit", code, null);
25718
- } finally {
25719
- options.signal.removeEventListener("abort", abort);
25720
- }
25721
- }).catch(async (cause) => {
25722
- this.ended = true;
25723
- this.handle = void 0;
25724
- options.signal.removeEventListener("abort", abort);
25725
- await cleanup();
25726
- this.emit("error", asError(cause));
25727
- });
25728
- }
25729
- requestKill;
25730
- kill(signal) {
25731
- if (this.ended || signal !== "SIGTERM" && signal !== "SIGKILL") return false;
25732
- this.killed = true;
25733
- this.requestKill(signal);
25734
- return true;
25735
- }
25736
- };
25737
- function makeClaudeProcessSpawner(host, stderr, config = {}) {
25738
- const cleanup = config.cleanup ?? (async () => {
25739
- });
25740
- let process2;
25741
- let launch;
25742
- return {
25743
- spawn: (options) => {
25744
- const args = [...options.args, ...config.mcpConfigArgs ?? []];
25745
- if (config.mcpConfigPath !== void 0) {
25746
- const flag = args.findLastIndex(
25747
- (arg) => arg === "--mcp-config" || arg.startsWith("--mcp-config=")
25748
- );
25749
- if (flag === -1) args.push("--mcp-config", config.mcpConfigPath);
25750
- else {
25751
- let end = flag + 1;
25752
- while (end < args.length && !args[end].startsWith("-")) end++;
25753
- args.splice(end, 0, config.mcpConfigPath);
25754
- }
25755
- }
25756
- launch = { ...options, args };
25757
- process2 = new ClaudeProcess(host, launch, stderr, cleanup);
25758
- return process2;
25759
- },
25760
- current: () => process2?.handle && launch ? { handle: process2.handle, command: launch.command, args: launch.args } : void 0,
25761
- clear: () => {
25762
- process2 = void 0;
25763
- launch = void 0;
25764
- return cleanup();
25765
- }
25766
- };
25767
- }
25768
-
25769
26022
  // src/claudeMcpConfig.ts
25770
26023
  import * as NodeFSP5 from "node:fs/promises";
25771
26024
  import * as NodeOS7 from "node:os";
25772
- import * as NodePath11 from "node:path";
26025
+ import * as NodePath12 from "node:path";
25773
26026
  import * as NodeProcess4 from "node:process";
25774
26027
  async function createClaudeMcpConfig(mcpServers, log) {
25775
26028
  let directory;
@@ -25784,8 +26037,8 @@ async function createClaudeMcpConfig(mcpServers, log) {
25784
26037
  return { path: void 0, cleanup };
25785
26038
  }
25786
26039
  try {
25787
- directory = await NodeFSP5.mkdtemp(NodePath11.join(NodeOS7.tmpdir(), "ycodium-claude-mcp-"));
25788
- 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");
25789
26042
  await NodeFSP5.writeFile(file, JSON.stringify({ mcpServers }), { mode: 384 });
25790
26043
  await NodeFSP5.chmod(file, 384).catch((cause) => {
25791
26044
  if (NodeProcess4.platform !== "win32") throw cause;
@@ -26451,7 +26704,7 @@ var makeStreamEventHandler = (deps) => {
26451
26704
 
26452
26705
  // src/claudeWorkspaceInventory.ts
26453
26706
  import * as NodeFSP6 from "node:fs/promises";
26454
- import * as NodePath12 from "node:path";
26707
+ import * as NodePath13 from "node:path";
26455
26708
  var MAX_COMMAND_DEPTH = 5;
26456
26709
  var MAX_COMMAND_FILES = 500;
26457
26710
  var MAX_DESCRIPTION_LENGTH = 200;
@@ -26469,7 +26722,7 @@ var firstBodyLine = (contents) => {
26469
26722
  return void 0;
26470
26723
  };
26471
26724
  var scanCommands = async (claudeDir) => {
26472
- const root = NodePath12.join(claudeDir, "commands");
26725
+ const root = NodePath13.join(claudeDir, "commands");
26473
26726
  const commands = /* @__PURE__ */ new Map();
26474
26727
  const watched = [root];
26475
26728
  let level = [root];
@@ -26479,7 +26732,7 @@ var scanCommands = async (claudeDir) => {
26479
26732
  for (const directory of level) {
26480
26733
  const entries = await NodeFSP6.readdir(directory, { withFileTypes: true }).catch(() => []);
26481
26734
  for (const entry of entries.toSorted((left, right) => left.name.localeCompare(right.name))) {
26482
- const entryPath = NodePath12.join(directory, entry.name);
26735
+ const entryPath = NodePath13.join(directory, entry.name);
26483
26736
  if (entry.isDirectory()) {
26484
26737
  next.push(entryPath);
26485
26738
  watched.push(entryPath);
@@ -26507,13 +26760,13 @@ var scanCommands = async (claudeDir) => {
26507
26760
  return { commands: [...commands.values()], watched };
26508
26761
  };
26509
26762
  var watchSkillsRoot = async (claudeDir) => {
26510
- const root = NodePath12.join(claudeDir, "skills");
26763
+ const root = NodePath13.join(claudeDir, "skills");
26511
26764
  const entries = await NodeFSP6.readdir(root).catch(() => []);
26512
26765
  return [
26513
26766
  root,
26514
26767
  ...entries.flatMap((entry) => [
26515
- NodePath12.join(root, entry),
26516
- NodePath12.join(root, entry, "SKILL.md")
26768
+ NodePath13.join(root, entry),
26769
+ NodePath13.join(root, entry, "SKILL.md")
26517
26770
  ])
26518
26771
  ];
26519
26772
  };
@@ -26524,7 +26777,7 @@ var mergeByName = (base, preferred) => {
26524
26777
  return [...merged.values()].toSorted((left, right) => left.name.localeCompare(right.name));
26525
26778
  };
26526
26779
  var discoverClaudeWorkspaceInventory = async (input) => {
26527
- const projectClaudeDir = NodePath12.join(input.cwd, ".claude");
26780
+ const projectClaudeDir = NodePath13.join(input.cwd, ".claude");
26528
26781
  const [userCommands, projectCommands, skills, userSkillWatch, projectSkillWatch] = await Promise.all([
26529
26782
  scanCommands(input.configDirPath),
26530
26783
  scanCommands(projectClaudeDir),
@@ -26546,9 +26799,9 @@ var discoverClaudeWorkspaceInventory = async (input) => {
26546
26799
  ...userSkillWatch,
26547
26800
  ...projectSkillWatch,
26548
26801
  // `skillOverrides` 在这几个设置文件里:开关一个技能也要重查。
26549
- NodePath12.join(input.configDirPath, "settings.json"),
26550
- NodePath12.join(projectClaudeDir, "settings.json"),
26551
- 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")
26552
26805
  ]
26553
26806
  };
26554
26807
  };
@@ -26672,8 +26925,8 @@ var makeSystemMessageHandler = (deps) => {
26672
26925
  case "model_refusal_no_fallback": {
26673
26926
  const explanation = typeof record2.api_refusal_explanation === "string" ? record2.api_refusal_explanation.trim() : "";
26674
26927
  const content = typeof record2.content === "string" ? record2.content : "";
26675
- const text = explanation || content;
26676
- if (text.length > 0) await emitRuntimeWarning(context, text, message);
26928
+ const text2 = explanation || content;
26929
+ if (text2.length > 0) await emitRuntimeWarning(context, text2, message);
26677
26930
  return;
26678
26931
  }
26679
26932
  }
@@ -27315,7 +27568,7 @@ var makeTurnLifecycle = (deps) => {
27315
27568
  };
27316
27569
 
27317
27570
  // src/adapter/turnMessages.ts
27318
- import * as NodePath13 from "node:path";
27571
+ import * as NodePath14 from "node:path";
27319
27572
  var makeTurnMessageHandlers = (deps) => {
27320
27573
  const {
27321
27574
  emitter,
@@ -27581,7 +27834,7 @@ var makeTurnMessageHandlers = (deps) => {
27581
27834
  claudeFailureHint({
27582
27835
  authenticationFailed: context.turnState?.authenticationFailed === true,
27583
27836
  environment: buildLaunchEnvironment(),
27584
- cwd: NodePath13.resolve(context.session.cwd ?? "."),
27837
+ cwd: NodePath14.resolve(context.session.cwd ?? "."),
27585
27838
  ...context.turnState === void 0 ? {} : {
27586
27839
  usageLimit: {
27587
27840
  rejected: context.turnState.rejectedRateLimits,
@@ -27685,8 +27938,13 @@ function buildTurnSummaryPrompt(raw) {
27685
27938
  // src/textGeneration.ts
27686
27939
  import * as NodeFSP7 from "node:fs/promises";
27687
27940
  import * as NodeOS8 from "node:os";
27688
- import * as NodePath14 from "node:path";
27689
- 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";
27690
27948
 
27691
27949
  // src/textGenerationUtils.ts
27692
27950
  function limitSection(value, maxChars) {
@@ -27732,14 +27990,14 @@ function unwrapNestedTitleObject(raw) {
27732
27990
  return raw;
27733
27991
  }
27734
27992
  var MAX_THREAD_TITLE_CHARS = 120;
27735
- function limitTitleMessage(text, budget) {
27993
+ function limitTitleMessage(text2, budget) {
27736
27994
  const marker = "\n[Content truncated]\n";
27737
- if (text.length <= budget) return text;
27995
+ if (text2.length <= budget) return text2;
27738
27996
  if (budget <= marker.length) return "";
27739
27997
  const available = budget - marker.length;
27740
27998
  const head = Math.ceil(available / 2);
27741
27999
  const tail = available - head;
27742
- return `${text.slice(0, head)}${marker}${tail > 0 ? text.slice(-tail) : ""}`;
28000
+ return `${text2.slice(0, head)}${marker}${tail > 0 ? text2.slice(-tail) : ""}`;
27743
28001
  }
27744
28002
  function sanitizeThreadTitle(raw) {
27745
28003
  const normalized = unwrapNestedTitleObject(raw).trim().split(/\r?\n/g)[0]?.trim().replace(/^['"`]+|['"`]+$/g, "").trim().replace(/\s+/g, " ");
@@ -28050,19 +28308,12 @@ function buildHandoverSummaryPrompt(input) {
28050
28308
 
28051
28309
  // src/textGeneration.ts
28052
28310
  var CLAUDE_TIMEOUT_MS = 18e4;
28053
- function isRecord5(value) {
28311
+ function isRecord6(value) {
28054
28312
  return typeof value === "object" && value !== null && !Array.isArray(value);
28055
28313
  }
28056
- function readString3(value) {
28314
+ function readString4(value) {
28057
28315
  return typeof value === "string" ? value : "";
28058
28316
  }
28059
- async function collectText(stream) {
28060
- const chunks = [];
28061
- for await (const chunk of stream) {
28062
- chunks.push(Buffer.from(chunk));
28063
- }
28064
- return Buffer.concat(chunks).toString("utf8");
28065
- }
28066
28317
  function decodeStructuredOutput(stdout, operation) {
28067
28318
  let parsed;
28068
28319
  try {
@@ -28072,19 +28323,20 @@ function decodeStructuredOutput(stdout, operation) {
28072
28323
  cause: error
28073
28324
  });
28074
28325
  }
28075
- const envelope = Array.isArray(parsed) ? parsed.findLast((message) => isRecord5(message) && message["type"] === "result") : parsed;
28076
- 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)) {
28077
28328
  throw new Error(`${operation}: Claude CLI returned unexpected output format.`);
28078
28329
  }
28079
28330
  return envelope["structured_output"];
28080
28331
  }
28081
28332
  function createClaudeTextGeneration(recipe, host, modelCatalog) {
28082
28333
  const runClaudeJson = async (input) => {
28083
- const processEnv = mergeLaunchEnv({
28334
+ input.signal?.throwIfAborted();
28335
+ const processEnv = mergeLaunchEnv2({
28084
28336
  instanceTable: recipe.environment,
28085
28337
  handOverrides: claudeHomeVariables(resolveClaudeHomeLayout(recipe))
28086
28338
  });
28087
- const modelSelection = isRecord5(input.modelSelection) ? input.modelSelection : {};
28339
+ const modelSelection = isRecord6(input.modelSelection) ? input.modelSelection : {};
28088
28340
  const requestedModel = modelIdFromSelection(modelSelection) ?? "claude-haiku-4-5";
28089
28341
  const caps = claudeModelCapabilities(requestedModel, modelCatalog(), recipe.customModels);
28090
28342
  const rawEffort = getModelSelectionStringOptionValue(modelSelection, "effort");
@@ -28120,52 +28372,46 @@ function createClaudeTextGeneration(recipe, host, modelCatalog) {
28120
28372
  "--strict-mcp-config"
28121
28373
  ];
28122
28374
  const program = recipe.binaryPath.trim() || "claude";
28375
+ let safeToRemove = true;
28123
28376
  const runIn = async (cwd) => {
28124
- const spawned = await host.process.spawn({
28125
- program,
28126
- args,
28127
- cwd,
28128
- env: processEnv,
28129
- owner: { bindingId: host.bindingId }
28130
- });
28131
- const timeout = AbortSignal.timeout(CLAUDE_TIMEOUT_MS);
28132
- const signal = input.signal ? AbortSignal.any([input.signal, timeout]) : timeout;
28133
- const abort = () => {
28134
- void spawned.kill("SIGTERM");
28135
- };
28136
- if (signal.aborted) abort();
28137
- else signal.addEventListener("abort", abort, { once: true });
28138
- await spawned.writeStdin(input.prompt);
28139
- await spawned.closeStdin();
28140
- const [stdout, stderr, exitCode] = await Promise.all([
28141
- collectText(spawned.stdout),
28142
- collectText(spawned.stderr),
28143
- spawned.exited
28144
- ]);
28145
- if (signal.aborted && !input.signal?.aborted) {
28146
- throw new Error(`${input.operation}: Claude CLI request timed out.`);
28147
- }
28148
- if (exitCode !== 0) {
28149
- const detail = stderr.trim() || stdout.trim();
28150
- throw new Error(
28151
- detail.length > 0 ? `Claude CLI command failed: ${detail}` : `Claude CLI command failed with code ${exitCode}.`
28152
- );
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;
28153
28398
  }
28154
- return decodeStructuredOutput(stdout, input.operation);
28155
28399
  };
28156
28400
  if (input.operation !== "generateThreadTitle" && input.operation !== "summarizeTurn")
28157
28401
  return runIn(input.cwd);
28158
28402
  const scratchDirectory = await NodeFSP7.mkdtemp(
28159
- NodePath14.join(NodeOS8.tmpdir(), "ycodium-claude-title-")
28403
+ NodePath15.join(NodeOS8.tmpdir(), "ycodium-claude-title-")
28160
28404
  );
28161
28405
  try {
28162
28406
  return await runIn(scratchDirectory);
28163
28407
  } finally {
28164
- 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
+ }
28165
28411
  }
28166
28412
  };
28167
28413
  const asRecord2 = (value, operation) => {
28168
- if (!isRecord5(value))
28414
+ if (!isRecord6(value))
28169
28415
  throw new Error(`${operation}: Claude returned invalid structured output.`);
28170
28416
  return value;
28171
28417
  };
@@ -28176,15 +28422,15 @@ function createClaudeTextGeneration(recipe, host, modelCatalog) {
28176
28422
  const includeBranch = input["includeBranch"] === true;
28177
28423
  const { prompt, outputSchema } = buildCommitMessagePrompt({
28178
28424
  branch: typeof input["branch"] === "string" ? input["branch"] : null,
28179
- stagedSummary: readString3(input["stagedSummary"]),
28180
- stagedPatch: readString3(input["stagedPatch"]),
28425
+ stagedSummary: readString4(input["stagedSummary"]),
28426
+ stagedPatch: readString4(input["stagedPatch"]),
28181
28427
  includeBranch,
28182
- ...isRecord5(input["policy"]) ? { policy: input["policy"] } : {}
28428
+ ...isRecord6(input["policy"]) ? { policy: input["policy"] } : {}
28183
28429
  });
28184
28430
  const generated = asRecord2(
28185
28431
  await runClaudeJson({
28186
28432
  operation: "generateCommitMessage",
28187
- cwd: readString3(input["cwd"]) || ".",
28433
+ cwd: readString4(input["cwd"]) || ".",
28188
28434
  prompt,
28189
28435
  outputSchema,
28190
28436
  modelSelection: input["modelSelection"],
@@ -28193,11 +28439,12 @@ function createClaudeTextGeneration(recipe, host, modelCatalog) {
28193
28439
  "generateCommitMessage"
28194
28440
  );
28195
28441
  return {
28196
- subject: sanitizeCommitSubject(readString3(generated["subject"])),
28197
- body: readString3(generated["body"]).trim(),
28442
+ subject: sanitizeCommitSubject(readString4(generated["subject"])),
28443
+ body: readString4(generated["body"]).trim(),
28198
28444
  ...typeof generated["branch"] === "string" ? { branch: sanitizeFeatureBranchName(generated["branch"]) } : {}
28199
28445
  };
28200
28446
  } catch (error) {
28447
+ if (isOperationCleanupError2(error)) throw error;
28201
28448
  throw normalizeCliError("claude", error, "Failed to generate commit message");
28202
28449
  }
28203
28450
  },
@@ -28205,18 +28452,18 @@ function createClaudeTextGeneration(recipe, host, modelCatalog) {
28205
28452
  try {
28206
28453
  const input = asRecord2(raw, "generatePrContent");
28207
28454
  const { prompt, outputSchema } = buildPrContentPrompt({
28208
- baseBranch: readString3(input["baseBranch"]),
28209
- headBranch: readString3(input["headBranch"]),
28210
- commitSummary: readString3(input["commitSummary"]),
28211
- diffSummary: readString3(input["diffSummary"]),
28212
- 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"]),
28213
28460
  ...typeof input["changeRequestTemplate"] === "string" ? { changeRequestTemplate: input["changeRequestTemplate"] } : {},
28214
- ...isRecord5(input["policy"]) ? { policy: input["policy"] } : {}
28461
+ ...isRecord6(input["policy"]) ? { policy: input["policy"] } : {}
28215
28462
  });
28216
28463
  const generated = asRecord2(
28217
28464
  await runClaudeJson({
28218
28465
  operation: "generatePrContent",
28219
- cwd: readString3(input["cwd"]) || ".",
28466
+ cwd: readString4(input["cwd"]) || ".",
28220
28467
  prompt,
28221
28468
  outputSchema,
28222
28469
  modelSelection: input["modelSelection"],
@@ -28225,10 +28472,11 @@ function createClaudeTextGeneration(recipe, host, modelCatalog) {
28225
28472
  "generatePrContent"
28226
28473
  );
28227
28474
  return {
28228
- title: sanitizePrTitle(readString3(generated["title"])),
28229
- body: readString3(generated["body"]).trim()
28475
+ title: sanitizePrTitle(readString4(generated["title"])),
28476
+ body: readString4(generated["body"]).trim()
28230
28477
  };
28231
28478
  } catch (error) {
28479
+ if (isOperationCleanupError2(error)) throw error;
28232
28480
  throw normalizeCliError("claude", error, "Failed to generate change request content");
28233
28481
  }
28234
28482
  },
@@ -28236,14 +28484,14 @@ function createClaudeTextGeneration(recipe, host, modelCatalog) {
28236
28484
  try {
28237
28485
  const input = asRecord2(raw, "generateBranchName");
28238
28486
  const { prompt, outputSchema } = buildBranchNamePrompt({
28239
- message: readString3(input["message"]),
28487
+ message: readString4(input["message"]),
28240
28488
  ...Array.isArray(input["attachments"]) ? { attachments: input["attachments"] } : {},
28241
- ...isRecord5(input["policy"]) ? { policy: input["policy"] } : {}
28489
+ ...isRecord6(input["policy"]) ? { policy: input["policy"] } : {}
28242
28490
  });
28243
28491
  const generated = asRecord2(
28244
28492
  await runClaudeJson({
28245
28493
  operation: "generateBranchName",
28246
- cwd: readString3(input["cwd"]) || ".",
28494
+ cwd: readString4(input["cwd"]) || ".",
28247
28495
  prompt,
28248
28496
  outputSchema,
28249
28497
  modelSelection: input["modelSelection"],
@@ -28251,8 +28499,9 @@ function createClaudeTextGeneration(recipe, host, modelCatalog) {
28251
28499
  }),
28252
28500
  "generateBranchName"
28253
28501
  );
28254
- return { branch: sanitizeBranchFragment(readString3(generated["branch"])) };
28502
+ return { branch: sanitizeBranchFragment(readString4(generated["branch"])) };
28255
28503
  } catch (error) {
28504
+ if (isOperationCleanupError2(error)) throw error;
28256
28505
  throw normalizeCliError("claude", error, "Failed to generate branch name");
28257
28506
  }
28258
28507
  },
@@ -28260,16 +28509,16 @@ function createClaudeTextGeneration(recipe, host, modelCatalog) {
28260
28509
  try {
28261
28510
  const input = asRecord2(raw, "generateThreadTitle");
28262
28511
  const { prompt, outputSchema } = buildThreadTitlePrompt({
28263
- message: readString3(input["message"]),
28512
+ message: readString4(input["message"]),
28264
28513
  ...typeof input["linkedContext"] === "string" && input["linkedContext"].length > 0 ? { linkedContext: input["linkedContext"] } : {},
28265
28514
  ...typeof input["previousTitle"] === "string" ? { previousTitle: input["previousTitle"] } : {},
28266
28515
  ...Array.isArray(input["attachments"]) ? { attachments: input["attachments"] } : {},
28267
- ...isRecord5(input["policy"]) ? { policy: input["policy"] } : {}
28516
+ ...isRecord6(input["policy"]) ? { policy: input["policy"] } : {}
28268
28517
  });
28269
28518
  const generated = asRecord2(
28270
28519
  await runClaudeJson({
28271
28520
  operation: "generateThreadTitle",
28272
- cwd: readString3(input["cwd"]) || ".",
28521
+ cwd: readString4(input["cwd"]) || ".",
28273
28522
  prompt,
28274
28523
  outputSchema,
28275
28524
  modelSelection: input["modelSelection"],
@@ -28278,10 +28527,11 @@ function createClaudeTextGeneration(recipe, host, modelCatalog) {
28278
28527
  "generateThreadTitle"
28279
28528
  );
28280
28529
  return {
28281
- title: sanitizeThreadTitle(readString3(generated["title"])),
28530
+ title: sanitizeThreadTitle(readString4(generated["title"])),
28282
28531
  ...generated["needsRefinement"] === true ? { needsRefinement: true } : {}
28283
28532
  };
28284
28533
  } catch (error) {
28534
+ if (isOperationCleanupError2(error)) throw error;
28285
28535
  throw normalizeCliError("claude", error, "Failed to generate thread title");
28286
28536
  }
28287
28537
  },
@@ -28291,7 +28541,7 @@ function createClaudeTextGeneration(recipe, host, modelCatalog) {
28291
28541
  const generated = asRecord2(
28292
28542
  await runClaudeJson({
28293
28543
  operation: "summarizeTurn",
28294
- cwd: readString3(input["cwd"]) || ".",
28544
+ cwd: readString4(input["cwd"]) || ".",
28295
28545
  prompt: buildTurnSummaryPrompt(input),
28296
28546
  outputSchema: {
28297
28547
  type: "object",
@@ -28304,8 +28554,9 @@ function createClaudeTextGeneration(recipe, host, modelCatalog) {
28304
28554
  }),
28305
28555
  "summarizeTurn"
28306
28556
  );
28307
- return { summary: readString3(generated["summary"]).trim() };
28557
+ return { summary: readString4(generated["summary"]).trim() };
28308
28558
  } catch (error) {
28559
+ if (isOperationCleanupError2(error)) throw error;
28309
28560
  throw normalizeCliError("claude", error, "Failed to summarize turn");
28310
28561
  }
28311
28562
  },
@@ -28313,12 +28564,12 @@ function createClaudeTextGeneration(recipe, host, modelCatalog) {
28313
28564
  try {
28314
28565
  const input = asRecord2(raw, "summarizeHandover");
28315
28566
  const { prompt, outputSchema } = buildHandoverSummaryPrompt({
28316
- transcript: readString3(input["transcript"])
28567
+ transcript: readString4(input["transcript"])
28317
28568
  });
28318
28569
  const generated = asRecord2(
28319
28570
  await runClaudeJson({
28320
28571
  operation: "summarizeHandover",
28321
- cwd: readString3(input["cwd"]) || ".",
28572
+ cwd: readString4(input["cwd"]) || ".",
28322
28573
  prompt,
28323
28574
  outputSchema,
28324
28575
  modelSelection: input["modelSelection"],
@@ -28326,8 +28577,9 @@ function createClaudeTextGeneration(recipe, host, modelCatalog) {
28326
28577
  }),
28327
28578
  "summarizeHandover"
28328
28579
  );
28329
- return { summary: readString3(generated["summary"]).trim() };
28580
+ return { summary: readString4(generated["summary"]).trim() };
28330
28581
  } catch (error) {
28582
+ if (isOperationCleanupError2(error)) throw error;
28331
28583
  throw normalizeCliError("claude", error, "Failed to summarize handover");
28332
28584
  }
28333
28585
  }
@@ -28337,179 +28589,99 @@ function createClaudeTextGeneration(recipe, host, modelCatalog) {
28337
28589
  // src/usageRead/index.ts
28338
28590
  import * as NodePath18 from "node:path";
28339
28591
 
28340
- // src/usageRead/quota.ts
28341
- import * as NodeFS6 from "node:fs";
28342
- import * as NodePath15 from "node:path";
28343
- import { mergeLaunchEnv as mergeLaunchEnv2, setsOwnModelConnection as setsOwnModelConnection2 } from "@ycodium-ai/plugin-api/executor";
28344
- import {
28345
- parseRetryAfterMs
28346
- } from "@ycodium-ai/plugin-api/usage";
28347
- var CLAUDE_QUOTA_URL = "https://api.anthropic.com/api/oauth/usage";
28348
- var REQUEST_TIMEOUT_MS = 1e4;
28349
- var HOUR_MS = 60 * 60 * 1e3;
28350
- var CLAUDE_QUOTA_WINDOWS = [
28351
- { field: "five_hour", id: "session", labelKey: "usage.window.session", windowMs: 5 * HOUR_MS },
28352
- { field: "seven_day", id: "weekly", labelKey: "usage.window.weekly", windowMs: 7 * 24 * HOUR_MS }
28353
- ];
28354
- var isRecord6 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
28355
- var launchEnvironment = (recipe) => mergeLaunchEnv2({
28356
- inherited: process.env,
28357
- instanceTable: recipe.environment,
28358
- handOverrides: claudeHomeVariables(resolveClaudeHomeLayout(recipe))
28359
- });
28360
- var claudeCredentialFileCandidates = (recipe) => {
28361
- const environment = launchEnvironment(recipe);
28362
- const credentialsDir = resolveClaudeCredentialsDirPath(
28363
- environment[CLAUDE_CREDENTIALS_DIR_ENVIRONMENT_VARIABLE] ?? ""
28364
- );
28365
- if (credentialsDir !== null) return [NodePath15.join(credentialsDir, ".credentials.json")];
28366
- const configDir = resolveClaudeConfigDirPath(recipe.homePath, environment);
28367
- const home = resolveClaudeHomePath(recipe.homePath);
28368
- return [
28369
- .../* @__PURE__ */ new Set([
28370
- NodePath15.join(configDir, ".credentials.json"),
28371
- NodePath15.join(home, ".claude", ".credentials.json"),
28372
- NodePath15.join(home, ".credentials.json")
28373
- ])
28374
- ];
28375
- };
28376
- var readString4 = (value) => typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
28377
- var readLoginCredential = (candidates) => {
28378
- for (const path of candidates) {
28379
- let raw;
28380
- try {
28381
- raw = NodeFS6.readFileSync(path, "utf8");
28382
- } catch {
28383
- continue;
28384
- }
28385
- if (raw.trim().length === 0) continue;
28386
- let parsed;
28387
- try {
28388
- parsed = JSON.parse(raw);
28389
- } catch {
28390
- continue;
28391
- }
28392
- const oauth = isRecord6(parsed) ? parsed["claudeAiOauth"] : void 0;
28393
- if (!isRecord6(oauth)) continue;
28394
- const accessToken = readString4(oauth["accessToken"]);
28395
- if (accessToken === void 0) continue;
28396
- return {
28397
- accessToken,
28398
- subscriptionType: readString4(oauth["subscriptionType"]),
28399
- rateLimitTier: readString4(oauth["rateLimitTier"]),
28400
- path
28401
- };
28402
- }
28403
- return null;
28404
- };
28405
- var claudePlanName = (credential) => {
28406
- const type4 = credential.subscriptionType;
28407
- if (type4 === void 0) return void 0;
28408
- const name = `${type4.charAt(0).toUpperCase()}${type4.slice(1)}`;
28409
- const multiplier = /(?:^|_)(\d+x)$/.exec(credential.rateLimitTier ?? "")?.[1];
28410
- return multiplier === void 0 ? name : `${name} ${multiplier}`;
28411
- };
28412
- var toIso = (value) => {
28413
- if (typeof value !== "string") return null;
28414
- const parsed = Date.parse(value);
28415
- return Number.isNaN(parsed) ? null : new Date(parsed).toISOString();
28416
- };
28417
- var readWindows = (body) => CLAUDE_QUOTA_WINDOWS.flatMap((spec) => {
28418
- const row = body[spec.field];
28419
- if (!isRecord6(row)) return [];
28420
- const utilization = row["utilization"];
28421
- if (typeof utilization !== "number" || !Number.isFinite(utilization)) return [];
28422
- return [
28423
- {
28424
- id: spec.id,
28425
- label: { key: spec.labelKey },
28426
- usedPercent: Math.min(100, Math.max(0, utilization)),
28427
- resetsAt: toIso(row["resets_at"]),
28428
- 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");
28429
28611
  }
28430
- ];
28431
- });
28432
- var readClaudeQuota = async (request) => {
28433
- const { recipe } = request;
28434
- const connectivity = CLAUDE_CARD.modelConnectivity;
28435
- const environmentRows = Object.entries(recipe.environment).map(([name, value]) => ({
28436
- name,
28437
- value
28438
- }));
28439
- if (connectivity !== void 0 && setsOwnModelConnection2(connectivity, environmentRows)) {
28440
- const changesEndpoint = setsOwnModelConnection2(
28441
- {
28442
- baseUrlEnvKey: connectivity.baseUrlEnvKey,
28443
- backendSwitchEnvKeys: connectivity.backendSwitchEnvKeys
28444
- },
28445
- environmentRows
28446
- );
28447
- return {
28448
- status: "unavailable",
28449
- reason: changesEndpoint ? "customEndpoint" : "unsupportedLogin"
28450
- };
28451
- }
28452
- const credential = readLoginCredential(claudeCredentialFileCandidates(recipe));
28453
- if (credential === null) return { status: "unavailable", reason: "missingCredentials" };
28454
- const now = request.now ?? Date.now;
28455
- let response;
28456
- try {
28457
- response = await request.fetch(CLAUDE_QUOTA_URL, {
28458
- method: "GET",
28459
- headers: {
28460
- Accept: "application/json",
28461
- "anthropic-beta": "oauth-2025-04-20",
28462
- Authorization: `Bearer ${credential.accessToken}`
28463
- },
28464
- redirect: "error",
28465
- signal: request.signal === void 0 ? AbortSignal.timeout(REQUEST_TIMEOUT_MS) : AbortSignal.any([request.signal, AbortSignal.timeout(REQUEST_TIMEOUT_MS)])
28466
- });
28467
- } catch (cause) {
28468
- if (request.signal?.aborted === true) throw cause;
28469
- return { status: "unavailable", reason: "unreachable" };
28470
- }
28471
- const release = () => {
28472
- void response.body?.cancel().catch(() => void 0);
28473
28612
  };
28474
- if (response.status === 429) {
28475
- const retryAfterMs = parseRetryAfterMs(response.headers.get("Retry-After"), now());
28476
- release();
28477
- return {
28478
- status: "unavailable",
28479
- reason: "rateLimited",
28480
- ...retryAfterMs === null ? {} : { retryAfterMs }
28481
- };
28482
- }
28483
- if (response.status === 401 || response.status === 403) {
28484
- release();
28485
- return { status: "unavailable", reason: "authRejected" };
28486
- }
28487
- if (response.status < 200 || response.status >= 300) {
28488
- release();
28489
- 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" };
28490
28618
  }
28491
- let body;
28492
- try {
28493
- body = JSON.parse(await response.text());
28494
- } catch {
28619
+ if (value.rate_limits_available !== true || !isRecord7(value.rate_limits)) {
28495
28620
  return { status: "unavailable", reason: "unreachable" };
28496
28621
  }
28497
- if (!isRecord6(body)) return { status: "unavailable", reason: "unreachable" };
28498
- 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
+ });
28499
28626
  return {
28500
28627
  status: "available",
28501
28628
  ...plan === void 0 ? {} : { plan },
28502
- windows: readWindows(body),
28503
- balances: [],
28504
- accountKey: credential.path
28629
+ windows: readClaudeQuotaWindows(value.rate_limits),
28630
+ balances: []
28505
28631
  };
28506
- };
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
+ }
28507
28679
 
28508
28680
  // src/usageRead/rateLimitEvent.ts
28509
- var isRecord7 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
28681
+ var isRecord8 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
28510
28682
  var claudeQuotaFromRateLimitEvent = (message) => {
28511
- const info = isRecord7(message) ? message["rate_limit_info"] : void 0;
28512
- if (!isRecord7(info)) return null;
28683
+ const info = isRecord8(message) ? message["rate_limit_info"] : void 0;
28684
+ if (!isRecord8(info)) return null;
28513
28685
  const spec = CLAUDE_QUOTA_WINDOWS.find((window2) => window2.field === info["rateLimitType"]);
28514
28686
  const utilization = info["utilization"];
28515
28687
  if (spec === void 0 || typeof utilization !== "number" || !Number.isFinite(utilization)) {
@@ -28917,7 +29089,7 @@ var createClaudeTokenReader = (options) => {
28917
29089
  status: "missing",
28918
29090
  scannedFiles: 0,
28919
29091
  skippedFiles: 0,
28920
- message: "No Claude Code session records found on this machine."
29092
+ message: "No Claude Code session records found in the configured directory."
28921
29093
  }
28922
29094
  };
28923
29095
  }
@@ -29014,7 +29186,12 @@ var createClaudeUsage = (input) => {
29014
29186
  cache: input.cache
29015
29187
  });
29016
29188
  return {
29017
- 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
+ },
29018
29195
  quotaFromRateLimits: claudeQuotaFromRateLimitEvent,
29019
29196
  readTokenUsage: tokens.readTokenUsage
29020
29197
  };
@@ -29085,12 +29262,13 @@ function createClaudeAdapter(recipe, host, options) {
29085
29262
  if (recipe.binaryPath.length === 0) throw new Error(CLAUDE_PROGRAM_NOT_CONFIGURED);
29086
29263
  return sdkCreateQuery(input);
29087
29264
  };
29088
- const buildLaunchEnvironment = () => mergeLaunchEnv3({
29265
+ const buildLaunchEnvironment = () => mergeLaunchEnv4({
29089
29266
  inherited: { ...process.env, ...options?.environment },
29090
29267
  instanceTable: recipe.environment,
29091
29268
  handOverrides: claudeHomeVariables(resolveClaudeHomeLayout(recipe))
29092
29269
  });
29093
29270
  const modelCatalog = makeClaudeModelCatalog({
29271
+ host,
29094
29272
  recipe,
29095
29273
  now,
29096
29274
  createQuery,
@@ -29238,6 +29416,7 @@ function createClaudeAdapter(recipe, host, options) {
29238
29416
  modelCatalog: {
29239
29417
  refresh: modelCatalog.refresh,
29240
29418
  current: currentModelCatalog,
29419
+ currentAuth: modelCatalog.currentAuth,
29241
29420
  lastRefreshOk: modelCatalog.lastRefreshOk,
29242
29421
  notePairedRefresh: modelCatalog.notePairedRefresh
29243
29422
  },
@@ -29300,18 +29479,25 @@ function createClaudeInstance(config, host, attachments, overrides, usageCache)
29300
29479
  };
29301
29480
  return {
29302
29481
  probe,
29303
- readCliVersion: () => readClaudeCliVersion({ recipe, host }),
29482
+ readCliVersion: (signal) => readClaudeCliVersion({ recipe, host, ...signal ? { signal } : {} }),
29304
29483
  // `force: false` 立刻返回。每天那次探测自己按清单 TTL 决定要不要读;这里只服务「版本变了」和手动刷新。
29305
- refreshModelCatalog: async ({ force }) => {
29484
+ refreshModelCatalog: async ({ force, signal }) => {
29485
+ signal?.throwIfAborted();
29306
29486
  if (!force) return;
29487
+ let cleanupFailed = false;
29307
29488
  try {
29308
- await adapter.modelCatalog.refresh({ force: true });
29489
+ await adapter.modelCatalog.refresh({ force: true, signal });
29309
29490
  } catch (error) {
29491
+ if (isOperationCleanupError3(error)) {
29492
+ cleanupFailed = true;
29493
+ throw error;
29494
+ }
29495
+ signal?.throwIfAborted();
29310
29496
  host.log.warn("claude model catalog refresh failed", {
29311
29497
  detail: error instanceof Error ? error.message : String(error)
29312
29498
  });
29313
29499
  } finally {
29314
- adapter.modelCatalog.notePairedRefresh();
29500
+ if (!signal?.aborted && !cleanupFailed) adapter.modelCatalog.notePairedRefresh();
29315
29501
  }
29316
29502
  },
29317
29503
  startSession: adapter.startSession,
@@ -29367,7 +29553,7 @@ function createClaudeInstance(config, host, attachments, overrides, usageCache)
29367
29553
  start: async () => {
29368
29554
  const command = claudeSignInCommand(
29369
29555
  recipe.credentialsPath,
29370
- NodeOS9.type() === "Windows_NT" ? "win32" : "linux"
29556
+ NodeOS10.type() === "Windows_NT" ? "win32" : "linux"
29371
29557
  );
29372
29558
  return {
29373
29559
  operationId: `claude-sign-in-${crypto.randomUUID()}`,
@@ -29376,7 +29562,11 @@ function createClaudeInstance(config, host, attachments, overrides, usageCache)
29376
29562
  },
29377
29563
  cancel: async () => {
29378
29564
  },
29379
- 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
+ }
29380
29570
  }
29381
29571
  };
29382
29572
  }
@@ -29395,7 +29585,7 @@ var executors = [
29395
29585
  ];
29396
29586
  var index_default = definePlugin({
29397
29587
  id: "agent-claude",
29398
- apiVersion: 39,
29588
+ apiVersion: 41,
29399
29589
  executors,
29400
29590
  activate(ctx) {
29401
29591
  capturedAttachments = ctx.attachments;