@ycodium-ai/agent-claude 0.2.2169 → 0.2.2488

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,12 +1,53 @@
1
1
  // src/index.ts
2
2
  import { definePlugin } from "@ycodium-ai/plugin-api";
3
3
 
4
+ // src/accountUsage.ts
5
+ var METRICS = [
6
+ {
7
+ id: "five_hour",
8
+ optional: true,
9
+ kind: "quota-window",
10
+ scope: "account",
11
+ unit: { kind: "percent", direction: "used" },
12
+ usedAt: "/five_hour/utilization",
13
+ resetsAt: { path: "/five_hour/resets_at", format: "iso" },
14
+ label: "session",
15
+ windowSeconds: 5 * 60 * 60
16
+ },
17
+ {
18
+ id: "seven_day",
19
+ optional: true,
20
+ kind: "quota-window",
21
+ scope: "account",
22
+ unit: { kind: "percent", direction: "used" },
23
+ usedAt: "/seven_day/utilization",
24
+ resetsAt: { path: "/seven_day/resets_at", format: "iso" },
25
+ label: "weekly",
26
+ windowSeconds: 7 * 24 * 60 * 60
27
+ }
28
+ ];
29
+ var CLAUDE_ACCOUNT_USAGE = {
30
+ id: "agent-claude:account-usage",
31
+ credentialKind: "claude-oauth",
32
+ request: {
33
+ url: "https://api.anthropic.com/api/oauth/usage",
34
+ headers: {
35
+ Accept: "application/json",
36
+ "anthropic-beta": "oauth-2025-04-20",
37
+ Authorization: { credential: "accessToken", prefix: "Bearer " }
38
+ }
39
+ },
40
+ ttlMs: 6e4,
41
+ cooldownMs: 5e3,
42
+ metrics: METRICS
43
+ };
44
+
4
45
  // src/card.ts
5
46
  var CLAUDE_DRIVER_KIND = "claudeAgent";
6
47
  var CLAUDE_CARD = {
7
48
  driverKind: CLAUDE_DRIVER_KIND,
8
- // 手叫 Claude Code,不叫 Claude——「Claude」会和脑(Anthropic · Claude Opus 5)混。
9
- // 已有实例存的显示名不动,这里只影响新建实例与手脑拆分后的回填名。
49
+ // 手叫 Claude Code,不叫 Claude——「Claude」会和模型(Anthropic · Claude Opus 5)混。
50
+ // 已有实例存的显示名不动,这里只影响新建实例的默认名。
10
51
  displayName: "Claude Code",
11
52
  iconUrl: "./assets/icon.svg",
12
53
  commandName: "claude",
@@ -20,6 +61,7 @@ var CLAUDE_CARD = {
20
61
  windowsCommand: "irm https://claude.ai/install.ps1 | iex",
21
62
  installedPathSegment: "/.local/"
22
63
  },
64
+ // `claude auth login` 没有无头选项,只能在有浏览器的机器上跑。
23
65
  signIn: [{ kind: "command", command: "claude auth login" }],
24
66
  deviceCodeSignInArgs: [],
25
67
  npmPackage: "@anthropic-ai/claude-code",
@@ -30,33 +72,8 @@ var CLAUDE_CARD = {
30
72
  defaultModel: "claude-sonnet-5",
31
73
  defaultTextGenerationModel: "claude-haiku-4-5",
32
74
  customModelIdExample: "claude-sonnet-5",
33
- modelSlugAliases: {
34
- opus: "claude-opus-5",
35
- "opus-5": "claude-opus-5",
36
- "claude-opus-5.0": "claude-opus-5",
37
- "claude-opus-5-0": "claude-opus-5",
38
- "opus-4.8": "claude-opus-4-8",
39
- "claude-opus-4.8": "claude-opus-4-8",
40
- "opus-4.7": "claude-opus-4-7",
41
- "claude-opus-4.7": "claude-opus-4-7",
42
- "opus-4.6": "claude-opus-4-6",
43
- "claude-opus-4.6": "claude-opus-4-6",
44
- "claude-opus-4-6-20251117": "claude-opus-4-6",
45
- sonnet: "claude-sonnet-5",
46
- "sonnet-5": "claude-sonnet-5",
47
- "claude-sonnet-5.0": "claude-sonnet-5",
48
- "claude-sonnet-5-0": "claude-sonnet-5",
49
- "sonnet-4.6": "claude-sonnet-4-6",
50
- "claude-sonnet-4.6": "claude-sonnet-4-6",
51
- "claude-sonnet-4-6-20251117": "claude-sonnet-4-6",
52
- haiku: "claude-haiku-4-5",
53
- "haiku-4.5": "claude-haiku-4-5",
54
- "claude-haiku-4.5": "claude-haiku-4-5",
55
- "claude-haiku-4-5-20251001": "claude-haiku-4-5"
56
- },
57
- modelsDevProviderIds: ["anthropic"],
58
- modelsDevSlugPrefixed: false,
59
- modelSlugPrefix: "",
75
+ // 不改写用户的选择:`opus` / `sonnet` 这类简称的含义随 CLI 版本变,由 Claude 自己解析。
76
+ modelSlugAliases: null,
60
77
  mcpTransports: ["stdio", "http", "sse"],
61
78
  pluginManaged: true,
62
79
  verifiedSkills: true,
@@ -90,14 +107,62 @@ var CLAUDE_CARD = {
90
107
  }
91
108
  ],
92
109
  diagnostics: [],
110
+ accountUsage: CLAUDE_ACCOUNT_USAGE,
111
+ // 设置表单照这份画:带 x-ycodium-form 的属性才进表单(enabled、customModels 等另有入口,不在此列),
112
+ // 顺序看 x-ycodium-form-order;文案键在本插件 locales/<locale>.json 里解析,宿主不兜底。
93
113
  configSchema: {
94
114
  type: "object",
115
+ "x-ycodium-form-order": [
116
+ "binaryPath",
117
+ "homePath",
118
+ "credentialsPath",
119
+ "autoCompactWindow",
120
+ "launchArgs"
121
+ ],
95
122
  properties: {
96
- binaryPath: { type: "string" },
97
- homePath: { type: "string" },
98
- credentialsPath: { type: "string" },
99
- launchArgs: { type: "string" },
100
- autoCompactWindow: { type: "string" },
123
+ binaryPath: {
124
+ type: "string",
125
+ "x-ycodium-form": {
126
+ titleKey: "form.binaryPath.title",
127
+ descriptionKey: "form.binaryPath.description",
128
+ placeholderKey: "form.binaryPath.placeholder",
129
+ control: "executable-path"
130
+ }
131
+ },
132
+ homePath: {
133
+ type: "string",
134
+ "x-ycodium-form": {
135
+ titleKey: "form.homePath.title",
136
+ descriptionKey: "form.homePath.description",
137
+ placeholderKey: "form.homePath.placeholder"
138
+ }
139
+ },
140
+ credentialsPath: {
141
+ type: "string",
142
+ "x-ycodium-form": {
143
+ titleKey: "form.credentialsPath.title",
144
+ descriptionKey: "form.credentialsPath.description",
145
+ placeholderKey: "form.credentialsPath.placeholder"
146
+ }
147
+ },
148
+ launchArgs: {
149
+ type: "string",
150
+ "x-ycodium-form": {
151
+ titleKey: "form.launchArgs.title",
152
+ descriptionKey: "form.launchArgs.description",
153
+ placeholderKey: "form.launchArgs.placeholder"
154
+ }
155
+ },
156
+ autoCompactWindow: {
157
+ type: "string",
158
+ // 空,或 100000 到 1000000 的整数(与服务端设置校验同一条规则)。
159
+ pattern: "^(?:|[1-9]\\d{5}|1000000)$",
160
+ "x-ycodium-form": {
161
+ titleKey: "form.autoCompactWindow.title",
162
+ descriptionKey: "form.autoCompactWindow.description",
163
+ placeholderKey: "form.autoCompactWindow.placeholder"
164
+ }
165
+ },
101
166
  customModels: { type: "array" },
102
167
  enabled: { type: "boolean" },
103
168
  driver: { type: "string" },
@@ -109,19 +174,29 @@ var CLAUDE_CARD = {
109
174
  modelConnectivity: {
110
175
  dialects: ["anthropic-messages"],
111
176
  acceptsExternalEndpoints: true,
112
- // claude 认 origin:CLI 自己在注入的 base 后追加 `/v1/messages`,所以后缀整条
113
- // 就是路由,注进去的 base 是纯 origin(compileBinding 按这一栏算出该注什么)。
177
+ // claude 认 origin:CLI 自己在 `ANTHROPIC_BASE_URL` 后追加 `/v1/messages`,所以实例上
178
+ // 填的是纯 origin。
114
179
  nativePathSuffix: "/v1/messages",
115
- // 与宿主内置名片逐字相同。credentialEnvKey 是宿主给绑定注入的那个变量;
116
- // alternateCredentialEnvKeys 是 CLI 也认的备选凭据变量(手脑拆分线加的,用来识别捆绑)。
180
+ // 用户在实例上填这几个变量接模型服务(ADR-0032)。CLI 两个凭据变量都认:
181
+ // `ANTHROPIC_AUTH_TOKEN` 走 bearer,`ANTHROPIC_API_KEY` 走 x-api-key。
117
182
  baseUrlEnvKey: "ANTHROPIC_BASE_URL",
118
183
  credentialEnvKey: "ANTHROPIC_AUTH_TOKEN",
119
184
  alternateCredentialEnvKeys: ["ANTHROPIC_API_KEY"],
185
+ // 设成真值就改连云厂商托管的 Claude,不再用订阅登录。名单取自 claude-agent-sdk
186
+ // 0.3.170 的 `sdk.mjs`:它按 1/true/yes/on 判这五个开关。设置页据此给这样的实例
187
+ // 单独的凭据目录,不让它顶着这台机器上登录的订阅。
188
+ backendSwitchEnvKeys: [
189
+ "CLAUDE_CODE_USE_BEDROCK",
190
+ "CLAUDE_CODE_USE_VERTEX",
191
+ "CLAUDE_CODE_USE_FOUNDRY",
192
+ "CLAUDE_CODE_USE_ANTHROPIC_AWS",
193
+ "CLAUDE_CODE_USE_MANTLE"
194
+ ],
120
195
  supportsNativeSubscription: true,
121
- // 七个档位变量一起钉成这次的模型:claude 的 opus / sonnet / haiku / fable 档与
122
- // 子代理、小快模型各读一个变量,只钉 `ANTHROPIC_MODEL` 的话子代理会跑回默认档。
123
- // 名单与旧 `CLAUDE_SETTINGS_MODEL_ENV_KEYS` 逐字相同(含 2026-09-09 从 2.1.265
124
- // 二进制核实出来的 fable 那一条)。`$model` 由 compileBinding 替换。
196
+ // 七个档位变量:claude 的 opus / sonnet / haiku / fable 档与子代理、小快模型各读一个,
197
+ // 只设 `ANTHROPIC_MODEL` 的话子代理会跑回默认档。名单与旧 `CLAUDE_SETTINGS_MODEL_ENV_KEYS`
198
+ // 逐字相同(含 2026-09-09 从 2.1.265 二进制核实出来的 fable 那一条)。宿主不再替换
199
+ // `$model`,这一栏只是记录。
125
200
  extraEnv: [
126
201
  { name: "ANTHROPIC_MODEL", value: "$model" },
127
202
  { name: "ANTHROPIC_DEFAULT_OPUS_MODEL", value: "$model" },
@@ -135,8 +210,8 @@ var CLAUDE_CARD = {
135
210
  };
136
211
 
137
212
  // src/claudeInstance.ts
138
- import * as NodeOS6 from "node:os";
139
- import * as NodePath7 from "node:path";
213
+ import * as NodeOS8 from "node:os";
214
+ import * as NodePath8 from "node:path";
140
215
 
141
216
  // ../../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
142
217
  import { createRequire as xz } from "node:module";
@@ -19569,11 +19644,7 @@ function HU(e, t) {
19569
19644
  }
19570
19645
 
19571
19646
  // src/claudeInstance.ts
19572
- import {
19573
- mergeLaunchEnv as mergeLaunchEnv2,
19574
- withoutBrainSlots,
19575
- brainSlotEnvKeys as brainSlotEnvKeys3
19576
- } from "@ycodium-ai/plugin-api/executor";
19647
+ import { mergeLaunchEnv as mergeLaunchEnv2 } from "@ycodium-ai/plugin-api/executor";
19577
19648
 
19578
19649
  // src/claudeAccount.ts
19579
19650
  import * as NodeFS from "node:fs";
@@ -19731,10 +19802,6 @@ function expandHomePath(value) {
19731
19802
 
19732
19803
  // src/claudeHome.ts
19733
19804
  var CLAUDE_CREDENTIALS_DIR_ENVIRONMENT_VARIABLE = "CLAUDE_SECURESTORAGE_CONFIG_DIR";
19734
- var CLAUDE_ROUTED_HOME_DIR_NAME = "claude-home";
19735
- function claudeRoutedHomePath(pluginDataDirPath) {
19736
- return NodePath4.join(pluginDataDirPath, CLAUDE_ROUTED_HOME_DIR_NAME);
19737
- }
19738
19805
  function resolveClaudeHomePath(homePath) {
19739
19806
  const trimmed = homePath.trim();
19740
19807
  return NodePath4.resolve(trimmed.length > 0 ? expandHomePath(trimmed) : NodeOS4.homedir());
@@ -19759,26 +19826,9 @@ function resolveClaudeCredentialsDirPath(credentialsPath) {
19759
19826
  return NodePath4.resolve(expandHomePath(trimmed));
19760
19827
  }
19761
19828
  function resolveClaudeHomeLayout(input) {
19762
- const userConfigDirPath = resolveClaudeConfigDirPath(input.homePath, input.environment ?? {});
19763
- const instanceConfigDirPath = input.homePath.trim().length > 0 ? resolveClaudeHomePath(input.homePath) : void 0;
19764
- const instanceCredentialsDirPath = resolveClaudeCredentialsDirPath(input.credentialsPath);
19765
- const routedHomePath = input.routedHomePath?.trim() ?? "";
19766
- if (routedHomePath.length === 0 || instanceConfigDirPath !== void 0 || instanceCredentialsDirPath !== null) {
19767
- return {
19768
- mode: "instance",
19769
- userConfigDirPath,
19770
- configDirPath: instanceConfigDirPath,
19771
- credentialsDirPath: instanceCredentialsDirPath ?? void 0
19772
- };
19773
- }
19774
- const resolvedRoutedHomePath = NodePath4.resolve(expandHomePath(routedHomePath));
19775
19829
  return {
19776
- mode: "routed",
19777
- userConfigDirPath,
19778
- configDirPath: resolvedRoutedHomePath,
19779
- // 登录目录显式钉成影子目录本身。不设这个变量时 CLI 本来就把凭据放配置目录,但
19780
- // 「登录去哪」正是这次要修的东西,不靠默认值。
19781
- credentialsDirPath: resolvedRoutedHomePath
19830
+ configDirPath: input.homePath.trim().length > 0 ? resolveClaudeHomePath(input.homePath) : void 0,
19831
+ credentialsDirPath: resolveClaudeCredentialsDirPath(input.credentialsPath) ?? void 0
19782
19832
  };
19783
19833
  }
19784
19834
  function claudeHomeVariables(layout) {
@@ -19792,8 +19842,7 @@ function makeClaudeEnvironment(config, baseEnv) {
19792
19842
  const variables = claudeHomeVariables(
19793
19843
  resolveClaudeHomeLayout({
19794
19844
  homePath: config.homePath,
19795
- credentialsPath: config.credentialsPath,
19796
- environment: resolvedBaseEnv
19845
+ credentialsPath: config.credentialsPath
19797
19846
  })
19798
19847
  );
19799
19848
  if (Object.keys(variables).length === 0) return resolvedBaseEnv;
@@ -19820,6 +19869,60 @@ function claudeSignInCommand(credentialsPath, platform) {
19820
19869
  return withClaudeCredentialsDir({ command, credentialsDirPath, platform });
19821
19870
  }
19822
19871
 
19872
+ // src/claudeSkills.ts
19873
+ import * as NodeFSP from "node:fs/promises";
19874
+ import * as NodePath5 from "node:path";
19875
+ var FRONT_MATTER_KEY = /^(name|description):[ \t]*(.*)$/;
19876
+ var BLOCK_SCALAR = /^[|>][+-]?$/;
19877
+ function parseSkillFrontMatter(contents) {
19878
+ const lines = contents.split(/\r?\n/);
19879
+ if (lines[0]?.trim() !== "---") return {};
19880
+ const values = {};
19881
+ for (let index = 1; index < lines.length; index += 1) {
19882
+ const line = lines[index] ?? "";
19883
+ if (line.trim() === "---") break;
19884
+ const match = FRONT_MATTER_KEY.exec(line);
19885
+ if (match === null) continue;
19886
+ const key = match[1];
19887
+ let value = (match[2] ?? "").trim();
19888
+ if (BLOCK_SCALAR.test(value)) {
19889
+ const block = [];
19890
+ while (index + 1 < lines.length) {
19891
+ const next = lines[index + 1] ?? "";
19892
+ if (next.trim() === "---" || next.trim().length > 0 && !/^[ \t]/.test(next)) break;
19893
+ index += 1;
19894
+ if (next.trim().length > 0) block.push(next.trim());
19895
+ }
19896
+ value = block.join(value.startsWith("|") ? "\n" : " ");
19897
+ } else {
19898
+ value = value.replace(/^(["'])(.*)\1$/, "$2").trim();
19899
+ }
19900
+ if (value.length > 0 && values[key] === void 0) values[key] = value;
19901
+ }
19902
+ return values;
19903
+ }
19904
+ async function discoverClaudeSkills(configDirPath) {
19905
+ const root = NodePath5.join(configDirPath, "skills");
19906
+ const entries = await NodeFSP.readdir(root).catch(() => []);
19907
+ const skills = /* @__PURE__ */ new Map();
19908
+ for (const entry of entries.toSorted()) {
19909
+ const skillPath = NodePath5.join(root, entry, "SKILL.md");
19910
+ const contents = await NodeFSP.readFile(skillPath, "utf8").catch(() => void 0);
19911
+ if (contents === void 0) continue;
19912
+ const frontMatter = parseSkillFrontMatter(contents);
19913
+ const name = frontMatter.name ?? entry.trim();
19914
+ if (name.length === 0) continue;
19915
+ skills.set(name, {
19916
+ name,
19917
+ path: skillPath,
19918
+ enabled: true,
19919
+ scope: "user",
19920
+ ...frontMatter.description === void 0 ? {} : { description: frontMatter.description }
19921
+ });
19922
+ }
19923
+ return [...skills.values()].toSorted((left, right) => left.name.localeCompare(right.name));
19924
+ }
19925
+
19823
19926
  // src/config.ts
19824
19927
  var CLAUDE_AUTO_COMPACT_WINDOW_PATTERN = /^(?:|[1-9]\d{5}|1000000)$/;
19825
19928
  function isRecord2(value) {
@@ -20026,8 +20129,8 @@ var EventBus = class {
20026
20129
  const queued = this.#queued.shift();
20027
20130
  if (queued !== void 0) return Promise.resolve({ done: false, value: queued });
20028
20131
  if (this.#closed) return Promise.resolve({ done: true, value: void 0 });
20029
- return new Promise((resolve3) => {
20030
- this.#waiters.push(resolve3);
20132
+ return new Promise((resolve2) => {
20133
+ this.#waiters.push(resolve2);
20031
20134
  });
20032
20135
  }
20033
20136
  };
@@ -20043,290 +20146,17 @@ var CLAUDE_CUSTOM_MODEL_THINKING_SELECT = {
20043
20146
  id: "effort",
20044
20147
  label: "Reasoning"
20045
20148
  };
20046
- var MINIMUM_CLAUDE_OPUS_5_VERSION = "2.1.219";
20047
- var MINIMUM_CLAUDE_FABLE_5_VERSION = "2.1.169";
20048
- var MINIMUM_CLAUDE_OPUS_4_8_VERSION = "2.1.154";
20049
- var MINIMUM_CLAUDE_OPUS_4_7_VERSION = "2.1.111";
20050
- var CURRENT_CLAUDE_MODELS = /* @__PURE__ */ new Set(["claude-fable-5", "claude-opus-5", "claude-sonnet-5"]);
20051
- function isLegacyClaudeModel(model) {
20052
- return !CURRENT_CLAUDE_MODELS.has(model);
20053
- }
20054
- function select(id2, label, options, promptInjectedValues) {
20055
- return {
20056
- type: "select",
20057
- id: id2,
20058
- label,
20059
- options: options.map((option) => ({
20060
- id: option.value,
20061
- label: option.label,
20062
- ...option.isDefault ? { isDefault: true } : {},
20063
- ...option.description ? { description: option.description } : {}
20064
- })),
20065
- ...promptInjectedValues ? { promptInjectedValues } : {}
20066
- };
20067
- }
20068
- function booleanOption(id2, label) {
20069
- return { type: "boolean", id: id2, label };
20070
- }
20071
- var ULTRACODE = {
20072
- value: "ultracode",
20073
- label: "Ultracode",
20074
- description: "xhigh effort plus multi-agent workflow orchestration"
20075
- };
20076
- var CLAUDE_MODEL_CATALOG = [
20077
- {
20078
- slug: "claude-fable-5",
20079
- name: "Claude Fable 5",
20080
- isCustom: false,
20081
- capabilities: {
20082
- optionDescriptors: [
20083
- select(
20084
- "effort",
20085
- "Reasoning",
20086
- [
20087
- { value: "low", label: "Low" },
20088
- { value: "medium", label: "Medium" },
20089
- { value: "high", label: "High", isDefault: true },
20090
- { value: "xhigh", label: "Extra High" },
20091
- { value: "max", label: "Max" },
20092
- ULTRACODE,
20093
- { value: "ultrathink", label: "Ultrathink" }
20094
- ],
20095
- ["ultrathink"]
20096
- ),
20097
- select("contextWindow", "Context Window", [
20098
- { value: "200k", label: "200k" },
20099
- { value: "1m", label: "1M", isDefault: true }
20100
- ])
20101
- ]
20102
- }
20103
- },
20104
- {
20105
- slug: "claude-opus-5",
20106
- name: "Claude Opus 5",
20107
- isCustom: false,
20108
- capabilities: {
20109
- optionDescriptors: [
20110
- select(
20111
- "effort",
20112
- "Reasoning",
20113
- [
20114
- { value: "low", label: "Low" },
20115
- { value: "medium", label: "Medium" },
20116
- { value: "high", label: "High", isDefault: true },
20117
- { value: "xhigh", label: "Extra High" },
20118
- { value: "max", label: "Max" },
20119
- ULTRACODE,
20120
- { value: "ultrathink", label: "Ultrathink" }
20121
- ],
20122
- ["ultrathink"]
20123
- ),
20124
- booleanOption("fastMode", "Fast Mode"),
20125
- select("contextWindow", "Context Window", [
20126
- { value: "200k", label: "200k" },
20127
- { value: "1m", label: "1M", isDefault: true }
20128
- ])
20129
- ]
20130
- }
20131
- },
20132
- {
20133
- slug: "claude-opus-4-8",
20134
- name: "Claude Opus 4.8",
20135
- isCustom: false,
20136
- capabilities: {
20137
- optionDescriptors: [
20138
- select(
20139
- "effort",
20140
- "Reasoning",
20141
- [
20142
- { value: "low", label: "Low" },
20143
- { value: "medium", label: "Medium" },
20144
- { value: "high", label: "High", isDefault: true },
20145
- { value: "xhigh", label: "Extra High" },
20146
- { value: "max", label: "Max" },
20147
- ULTRACODE,
20148
- { value: "ultrathink", label: "Ultrathink" }
20149
- ],
20150
- ["ultrathink"]
20151
- ),
20152
- booleanOption("fastMode", "Fast Mode")
20153
- ]
20154
- }
20155
- },
20156
- {
20157
- slug: "claude-opus-4-7",
20158
- name: "Claude Opus 4.7",
20159
- isCustom: false,
20160
- capabilities: {
20161
- optionDescriptors: [
20162
- select(
20163
- "effort",
20164
- "Reasoning",
20165
- [
20166
- { value: "low", label: "Low" },
20167
- { value: "medium", label: "Medium" },
20168
- { value: "high", label: "High" },
20169
- { value: "xhigh", label: "Extra High", isDefault: true },
20170
- { value: "max", label: "Max" },
20171
- { value: "ultrathink", label: "Ultrathink" }
20172
- ],
20173
- ["ultrathink"]
20174
- ),
20175
- booleanOption("fastMode", "Fast Mode")
20176
- ]
20177
- }
20178
- },
20179
- {
20180
- slug: "claude-opus-4-6",
20181
- name: "Claude Opus 4.6",
20182
- isCustom: false,
20183
- capabilities: {
20184
- optionDescriptors: [
20185
- select(
20186
- "effort",
20187
- "Reasoning",
20188
- [
20189
- { value: "low", label: "Low" },
20190
- { value: "medium", label: "Medium" },
20191
- { value: "high", label: "High", isDefault: true },
20192
- { value: "max", label: "Max" },
20193
- { value: "ultrathink", label: "Ultrathink" }
20194
- ],
20195
- ["ultrathink"]
20196
- ),
20197
- booleanOption("fastMode", "Fast Mode"),
20198
- select("contextWindow", "Context Window", [
20199
- { value: "200k", label: "200k" },
20200
- { value: "1m", label: "1M", isDefault: true }
20201
- ])
20202
- ]
20203
- }
20204
- },
20205
- {
20206
- slug: "claude-opus-4-5",
20207
- name: "Claude Opus 4.5",
20208
- isCustom: false,
20209
- capabilities: {
20210
- optionDescriptors: [
20211
- select("effort", "Reasoning", [
20212
- { value: "low", label: "Low" },
20213
- { value: "medium", label: "Medium" },
20214
- { value: "high", label: "High", isDefault: true },
20215
- { value: "max", label: "Max" }
20216
- ]),
20217
- booleanOption("fastMode", "Fast Mode")
20218
- ]
20219
- }
20220
- },
20221
- {
20222
- slug: "claude-sonnet-5",
20223
- name: "Claude Sonnet 5",
20224
- isCustom: false,
20225
- capabilities: {
20226
- optionDescriptors: [
20227
- select(
20228
- "effort",
20229
- "Reasoning",
20230
- [
20231
- { value: "low", label: "Low" },
20232
- { value: "medium", label: "Medium" },
20233
- { value: "high", label: "High", isDefault: true },
20234
- { value: "xhigh", label: "Extra High" },
20235
- { value: "max", label: "Max" },
20236
- { value: "ultrathink", label: "Ultrathink" }
20237
- ],
20238
- ["ultrathink"]
20239
- ),
20240
- select("contextWindow", "Context Window", [
20241
- { value: "200k", label: "200k", isDefault: true },
20242
- { value: "1m", label: "1M" }
20243
- ])
20244
- ]
20245
- }
20246
- },
20247
- {
20248
- slug: "claude-sonnet-4-6",
20249
- name: "Claude Sonnet 4.6",
20250
- isCustom: false,
20251
- capabilities: {
20252
- optionDescriptors: [
20253
- select(
20254
- "effort",
20255
- "Reasoning",
20256
- [
20257
- { value: "low", label: "Low" },
20258
- { value: "medium", label: "Medium" },
20259
- { value: "high", label: "High", isDefault: true },
20260
- { value: "max", label: "Max" },
20261
- { value: "ultrathink", label: "Ultrathink" }
20262
- ],
20263
- ["ultrathink"]
20264
- ),
20265
- select("contextWindow", "Context Window", [
20266
- { value: "200k", label: "200k", isDefault: true },
20267
- { value: "1m", label: "1M" }
20268
- ])
20269
- ]
20270
- }
20271
- },
20272
- {
20273
- slug: "claude-haiku-4-5",
20274
- name: "Claude Haiku 4.5",
20275
- isCustom: false,
20276
- capabilities: {
20277
- optionDescriptors: [booleanOption("thinking", "Thinking")]
20278
- }
20279
- }
20280
- ];
20281
- var BUILT_IN_MODELS = CLAUDE_MODEL_CATALOG.map(
20282
- (model) => isLegacyClaudeModel(model.slug) ? { ...model, isLegacy: true } : model
20283
- );
20284
- var CLAUDE_XHIGH_SLUGS = /* @__PURE__ */ new Set([
20285
- "claude-fable-5",
20286
- "claude-opus-5",
20287
- "claude-opus-4-8",
20288
- "claude-sonnet-5"
20289
- ]);
20290
- function compareSemverVersions(left, right) {
20291
- const parse = (value) => {
20292
- const match = value.trim().replace(/^v/, "").match(/^(\d+)\.(\d+)\.(\d+)/);
20293
- if (!match) return null;
20294
- return [Number(match[1]), Number(match[2]), Number(match[3])];
20295
- };
20296
- const a = parse(left);
20297
- const b3 = parse(right);
20298
- if (!a || !b3) return left.localeCompare(right);
20299
- if (a[0] !== b3[0]) return a[0] - b3[0];
20300
- if (a[1] !== b3[1]) return a[1] - b3[1];
20301
- return a[2] - b3[2];
20302
- }
20303
- function supportsVersion(version, minimum) {
20304
- return version ? compareSemverVersions(version, minimum) >= 0 : false;
20305
- }
20306
- function getBuiltInClaudeModelsForVersion(version) {
20307
- return BUILT_IN_MODELS.filter((model) => {
20308
- if (model.slug === "claude-opus-5")
20309
- return supportsVersion(version, MINIMUM_CLAUDE_OPUS_5_VERSION);
20310
- if (model.slug === "claude-fable-5")
20311
- return supportsVersion(version, MINIMUM_CLAUDE_FABLE_5_VERSION);
20312
- if (model.slug === "claude-opus-4-8")
20313
- return supportsVersion(version, MINIMUM_CLAUDE_OPUS_4_8_VERSION);
20314
- if (model.slug === "claude-opus-4-7")
20315
- return supportsVersion(version, MINIMUM_CLAUDE_OPUS_4_7_VERSION);
20316
- return true;
20317
- });
20318
- }
20319
20149
  function parseGenericCliVersion(output) {
20320
- const match = output.match(/\b(\d+\.\d+\.\d+)\b/);
20150
+ const match = output.match(/\bv?(\d+\.\d+\.\d+)\b/);
20321
20151
  return match?.[1] ?? null;
20322
20152
  }
20323
20153
  function normalizeCustomModelSlug(slug) {
20324
20154
  return slug.trim();
20325
20155
  }
20326
- function knownClaudeModelCapabilities(model, customModels) {
20156
+ function knownClaudeModelCapabilities(model, catalog, customModels) {
20327
20157
  const slug = model?.trim();
20328
- const builtIn = BUILT_IN_MODELS.find((candidate) => candidate.slug === slug)?.capabilities;
20329
- if (builtIn) return builtIn;
20158
+ const fromCatalog = catalog.find((candidate) => candidate.slug === slug)?.capabilities;
20159
+ if (fromCatalog) return fromCatalog;
20330
20160
  const custom = customModels?.find(
20331
20161
  (candidate) => normalizeCustomModelSlug(candidate.slug) === slug
20332
20162
  );
@@ -20346,8 +20176,8 @@ function knownClaudeModelCapabilities(model, customModels) {
20346
20176
  }
20347
20177
  return void 0;
20348
20178
  }
20349
- function claudeModelCapabilities(model, customModels) {
20350
- return knownClaudeModelCapabilities(model, customModels) ?? DEFAULT_CLAUDE_MODEL_CAPABILITIES;
20179
+ function claudeModelCapabilities(model, catalog, customModels) {
20180
+ return knownClaudeModelCapabilities(model, catalog, customModels) ?? DEFAULT_CLAUDE_MODEL_CAPABILITIES;
20351
20181
  }
20352
20182
  function isRecord3(value) {
20353
20183
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -20384,59 +20214,34 @@ function modelIdFromSelection(selection) {
20384
20214
  }
20385
20215
  function currentSelectValue(descriptor, raw) {
20386
20216
  if (raw && descriptor.options.some((option) => option.id === raw)) return raw;
20387
- return descriptor.options.find((option) => option.isDefault)?.id ?? descriptor.options[0]?.id;
20217
+ return descriptor.options.find((option) => option.isDefault)?.id;
20388
20218
  }
20389
20219
  function resolveClaudeEffort(caps, raw) {
20390
20220
  const descriptor = caps.optionDescriptors.find((entry) => entry.id === "effort");
20391
20221
  if (!descriptor || descriptor.type !== "select") return void 0;
20392
20222
  return currentSelectValue(descriptor, raw);
20393
20223
  }
20394
- function acceptsClaudeXhigh(model, caps) {
20395
- const slug = model?.trim();
20396
- if (slug !== void 0 && CLAUDE_XHIGH_SLUGS.has(slug)) return true;
20397
- if (knownClaudeModelCapabilities(slug)) return false;
20398
- if (!caps) return false;
20399
- const descriptor = caps.optionDescriptors.find((candidate) => candidate.id === "effort");
20400
- return descriptor?.type === "select" ? descriptor.options.some((option) => option.id === "xhigh") : false;
20401
- }
20402
- function normalizeClaudeCliEffort(effort, model, caps) {
20224
+ function normalizeClaudeCliEffort(effort) {
20403
20225
  if (!effort || effort === "ultrathink") {
20404
20226
  return void 0;
20405
20227
  }
20406
20228
  if (effort === "ultracode") {
20407
20229
  return "xhigh";
20408
20230
  }
20409
- if (effort === "xhigh" && !acceptsClaudeXhigh(model, caps)) {
20410
- return "max";
20411
- }
20412
- if (effort === "max" && model === "claude-sonnet-4-6") {
20413
- return "high";
20414
- }
20415
20231
  return effort;
20416
20232
  }
20417
20233
  function isClaudeUltracodeEffort(effort) {
20418
20234
  return effort === "ultracode";
20419
20235
  }
20420
- function resolveClaudeContextWindow(caps, modelSelection) {
20421
- const raw = getModelSelectionStringOptionValue(modelSelection, "contextWindow");
20422
- const descriptor = caps.optionDescriptors.find((entry) => entry.id === "contextWindow");
20423
- if (!descriptor || descriptor.type !== "select") return void 0;
20424
- return currentSelectValue(descriptor, raw);
20425
- }
20426
20236
  function claudeCliModelId(input) {
20427
- const catalog = [
20428
- ...BUILT_IN_MODELS.map((model) => model.slug),
20237
+ const requested = input.requestedModelId.trim();
20238
+ if (requested.length > 0 && (input.catalog ?? []).length === 0) return requested;
20239
+ const known = [
20240
+ ...(input.catalog ?? []).map((model) => model.slug),
20241
+ ...input.declared ?? [],
20429
20242
  ...(input.customModels ?? []).map((model) => normalizeCustomModelSlug(model.slug))
20430
20243
  ];
20431
- return matchCatalogModel(input.boundModelId, catalog) ?? input.placeholder;
20432
- }
20433
- function resolveClaudeApiModelId(caps, modelSelection) {
20434
- switch (resolveClaudeContextWindow(caps, modelSelection)) {
20435
- case "1m":
20436
- return `${modelSelection.model}[1m]`;
20437
- default:
20438
- return modelSelection.model;
20439
- }
20244
+ return matchCatalogModel(input.requestedModelId, known) ?? input.placeholder;
20440
20245
  }
20441
20246
  function resolvePromptInjectedEffort(caps, rawEffort) {
20442
20247
  const trimmed = rawEffort?.trim();
@@ -20460,26 +20265,106 @@ function applyClaudePromptEffortPrefix(text, effort) {
20460
20265
  return `Ultrathink:
20461
20266
  ${trimmed}`;
20462
20267
  }
20463
- function selectedClaudeContextWindow(caps, modelSelection) {
20464
- switch (modelSelection?.model) {
20465
- case "claude-opus-4-8":
20466
- case "claude-opus-4-7":
20467
- return 1e6;
20468
- }
20469
- switch (resolveClaudeContextWindow(caps, modelSelection)) {
20470
- case "1m":
20471
- return 1e6;
20472
- case "200k":
20473
- return 2e5;
20474
- default:
20475
- return void 0;
20476
- }
20477
- }
20478
- function getEffectiveClaudeAgentEffort(effort, model, caps) {
20479
- const normalized = normalizeClaudeCliEffort(effort, model, caps);
20268
+ function getEffectiveClaudeAgentEffort(effort) {
20269
+ const normalized = normalizeClaudeCliEffort(effort);
20480
20270
  return normalized ? normalized : null;
20481
20271
  }
20482
20272
 
20273
+ // src/sdkModels.ts
20274
+ import * as NodeOS5 from "node:os";
20275
+ var CLAUDE_SDK_MODELS_TIMEOUT_MS = 2e4;
20276
+ var CLAUDE_SDK_MODELS_TTL_MS = 30 * 6e4;
20277
+ var CLAUDE_SDK_MODELS_RETRY_MS = 2 * 6e4;
20278
+ var EFFORT_LABELS = {
20279
+ low: "Low",
20280
+ medium: "Medium",
20281
+ high: "High",
20282
+ xhigh: "Extra High",
20283
+ max: "Max"
20284
+ };
20285
+ var effortLabel = (level) => EFFORT_LABELS[level] ?? `${level.slice(0, 1).toUpperCase()}${level.slice(1)}`;
20286
+ var claudeCatalogModelFromSdk = (info) => {
20287
+ const descriptors = [];
20288
+ const levels = info.supportsEffort === false ? [] : info.supportedEffortLevels ?? [];
20289
+ if (levels.length > 0) {
20290
+ descriptors.push({
20291
+ type: "select",
20292
+ id: "effort",
20293
+ label: "Reasoning",
20294
+ options: levels.map((level) => ({ id: level, label: effortLabel(level) }))
20295
+ });
20296
+ }
20297
+ if (info.supportsFastMode === true) {
20298
+ descriptors.push({ type: "boolean", id: "fastMode", label: "Fast Mode" });
20299
+ }
20300
+ return {
20301
+ slug: info.value,
20302
+ name: info.displayName.trim().length > 0 ? info.displayName : info.value,
20303
+ isCustom: false,
20304
+ capabilities: { optionDescriptors: descriptors }
20305
+ };
20306
+ };
20307
+ var claudeCatalogFromSdk = (models) => {
20308
+ const seen = /* @__PURE__ */ new Set();
20309
+ const out = [];
20310
+ for (const info of models) {
20311
+ if (typeof info.value !== "string" || info.value.trim().length === 0) continue;
20312
+ if (seen.has(info.value)) continue;
20313
+ seen.add(info.value);
20314
+ out.push(claudeCatalogModelFromSdk(info));
20315
+ }
20316
+ return out;
20317
+ };
20318
+ var silentPrompt = (signal) => ({
20319
+ [Symbol.asyncIterator]: () => ({
20320
+ next: () => new Promise((resolve2) => {
20321
+ if (signal.aborted) resolve2({ done: true, value: void 0 });
20322
+ signal.addEventListener("abort", () => resolve2({ done: true, value: void 0 }), {
20323
+ once: true
20324
+ });
20325
+ }),
20326
+ return: async () => ({ done: true, value: void 0 })
20327
+ })
20328
+ });
20329
+ var readClaudeSdkModels = async (input) => {
20330
+ const abort = new AbortController();
20331
+ const forwardAbort = () => abort.abort();
20332
+ input.signal?.addEventListener("abort", forwardAbort, { once: true });
20333
+ if (input.signal?.aborted) abort.abort();
20334
+ let runtime;
20335
+ let timer;
20336
+ try {
20337
+ runtime = input.createQuery({
20338
+ prompt: silentPrompt(abort.signal),
20339
+ options: { ...input.options, abortController: abort }
20340
+ });
20341
+ if (runtime.supportedModels === void 0) return void 0;
20342
+ const timeout = new Promise((_3, reject) => {
20343
+ timer = setTimeout(
20344
+ () => reject(new Error("supportedModels timed out")),
20345
+ input.timeoutMs ?? CLAUDE_SDK_MODELS_TIMEOUT_MS
20346
+ );
20347
+ });
20348
+ const cancelled = new Promise((_3, reject) => {
20349
+ abort.signal.addEventListener("abort", () => reject(new Error("aborted")), { once: true });
20350
+ });
20351
+ cancelled.catch(() => {
20352
+ });
20353
+ return await Promise.race([runtime.supportedModels(), timeout, cancelled]);
20354
+ } catch {
20355
+ return void 0;
20356
+ } finally {
20357
+ if (timer !== void 0) clearTimeout(timer);
20358
+ input.signal?.removeEventListener("abort", forwardAbort);
20359
+ abort.abort();
20360
+ try {
20361
+ runtime?.close();
20362
+ } catch {
20363
+ }
20364
+ }
20365
+ };
20366
+ var claudeSdkModelsCwd = () => NodeOS5.tmpdir();
20367
+
20483
20368
  // src/util.ts
20484
20369
  function encodeJsonStringForDiagnostics(input) {
20485
20370
  try {
@@ -20656,10 +20541,10 @@ function inputDiffStat(input) {
20656
20541
  let deletions = 0;
20657
20542
  for (const edit of input.edits) {
20658
20543
  if (edit === null || typeof edit !== "object" || Array.isArray(edit)) return void 0;
20659
- const stat2 = replacementDiffStat(edit, budget);
20660
- if (!stat2) return void 0;
20661
- additions += stat2.additions;
20662
- deletions += stat2.deletions;
20544
+ const stat = replacementDiffStat(edit, budget);
20545
+ if (!stat) return void 0;
20546
+ additions += stat.additions;
20547
+ deletions += stat.deletions;
20663
20548
  }
20664
20549
  return { additions, deletions };
20665
20550
  }
@@ -21236,8 +21121,8 @@ var CLAUDE_SETTING_SOURCES = [
21236
21121
 
21237
21122
  // src/sessions.ts
21238
21123
  import * as NodeFS3 from "node:fs";
21239
- import * as NodeOS5 from "node:os";
21240
- import * as NodePath5 from "node:path";
21124
+ import * as NodeOS6 from "node:os";
21125
+ import * as NodePath6 from "node:path";
21241
21126
  import * as NodeProcess from "node:process";
21242
21127
 
21243
21128
  // src/sessionScope.ts
@@ -21357,7 +21242,7 @@ function listTranscriptFiles(transcriptDir, directory) {
21357
21242
  const scanned = matched.length > 0 ? matched : dirs;
21358
21243
  const found = [];
21359
21244
  for (const dir of scanned) {
21360
- const projectDir = NodePath5.join(transcriptDir, dir);
21245
+ const projectDir = NodePath6.join(transcriptDir, dir);
21361
21246
  let names;
21362
21247
  try {
21363
21248
  names = NodeFS3.readdirSync(projectDir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(TRANSCRIPT_SUFFIX)).map((entry) => entry.name);
@@ -21365,17 +21250,17 @@ function listTranscriptFiles(transcriptDir, directory) {
21365
21250
  continue;
21366
21251
  }
21367
21252
  for (const name of names) {
21368
- let stat2;
21253
+ let stat;
21369
21254
  try {
21370
- stat2 = NodeFS3.statSync(NodePath5.join(projectDir, name));
21255
+ stat = NodeFS3.statSync(NodePath6.join(projectDir, name));
21371
21256
  } catch {
21372
21257
  continue;
21373
21258
  }
21374
21259
  found.push({
21375
- path: NodePath5.join(projectDir, name),
21260
+ path: NodePath6.join(projectDir, name),
21376
21261
  sessionId: name.slice(0, -TRANSCRIPT_SUFFIX.length),
21377
- size: stat2.size,
21378
- mtimeMs: Number.isFinite(stat2.mtimeMs) ? stat2.mtimeMs : 0
21262
+ size: stat.size,
21263
+ mtimeMs: Number.isFinite(stat.mtimeMs) ? stat.mtimeMs : 0
21379
21264
  });
21380
21265
  }
21381
21266
  }
@@ -21383,7 +21268,7 @@ function listTranscriptFiles(transcriptDir, directory) {
21383
21268
  }
21384
21269
  function readClaudeLiveSessionIds(configDir) {
21385
21270
  const live = /* @__PURE__ */ new Set();
21386
- const markerDir = NodePath5.join(configDir, "sessions");
21271
+ const markerDir = NodePath6.join(configDir, "sessions");
21387
21272
  let entries;
21388
21273
  try {
21389
21274
  entries = NodeFS3.readdirSync(markerDir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) => entry.name);
@@ -21393,7 +21278,7 @@ function readClaudeLiveSessionIds(configDir) {
21393
21278
  for (const entry of entries) {
21394
21279
  let marker;
21395
21280
  try {
21396
- marker = JSON.parse(NodeFS3.readFileSync(NodePath5.join(markerDir, entry), "utf8"));
21281
+ marker = JSON.parse(NodeFS3.readFileSync(NodePath6.join(markerDir, entry), "utf8"));
21397
21282
  } catch {
21398
21283
  continue;
21399
21284
  }
@@ -21423,7 +21308,7 @@ function scanClaudeSessions(input) {
21423
21308
  }
21424
21309
  const files = [...listTranscriptFiles(input.transcriptDir, input.directory)].sort((left, right) => right.mtimeMs - left.mtimeMs).slice(0, MAX_SCANNED_FILES);
21425
21310
  const live = readClaudeLiveSessionIds(input.configDir);
21426
- const caseInsensitive = input.caseInsensitive ?? NodeOS5.type() === "Windows_NT";
21311
+ const caseInsensitive = input.caseInsensitive ?? NodeOS6.type() === "Windows_NT";
21427
21312
  const rows = [];
21428
21313
  for (const file of files) {
21429
21314
  if (rows.length >= MAX_ROWS) break;
@@ -21447,7 +21332,7 @@ function scanClaudeSessions(input) {
21447
21332
  function resolveTranscriptPath(input) {
21448
21333
  const fileName = `${input.sessionId}${TRANSCRIPT_SUFFIX}`;
21449
21334
  if (input.cwd !== void 0 && input.cwd.length > 0) {
21450
- const direct = NodePath5.join(
21335
+ const direct = NodePath6.join(
21451
21336
  input.transcriptDir,
21452
21337
  encodeClaudeProjectDirName(input.cwd),
21453
21338
  fileName
@@ -21464,7 +21349,7 @@ function resolveTranscriptPath(input) {
21464
21349
  return null;
21465
21350
  }
21466
21351
  for (const dir of dirs) {
21467
- const candidate = NodePath5.join(input.transcriptDir, dir, fileName);
21352
+ const candidate = NodePath6.join(input.transcriptDir, dir, fileName);
21468
21353
  try {
21469
21354
  if (NodeFS3.statSync(candidate).isFile()) return candidate;
21470
21355
  } catch {
@@ -21541,18 +21426,8 @@ function readClaudeSessionRecord(input) {
21541
21426
 
21542
21427
  // src/spawnEnvironment.ts
21543
21428
  function changedSpawnEnvironmentKeys(previous, next) {
21544
- const settingsPresenceChanged = previous.settingsEnv === void 0 !== (next.settingsEnv === void 0);
21545
- return [
21546
- ...changedKeys(previous.processEnv, next.processEnv),
21547
- ...settingsPresenceChanged ? ["settings.env"] : [],
21548
- ...changedKeys(previous.settingsEnv ?? {}, next.settingsEnv ?? {}).map(
21549
- (key) => `settings.env.${key}`
21550
- )
21551
- ];
21552
- }
21553
- function changedKeys(previous, next) {
21554
- const keys = /* @__PURE__ */ new Set([...Object.keys(previous), ...Object.keys(next)]);
21555
- return [...keys].filter((key) => previous[key] !== next[key]).sort();
21429
+ const keys = /* @__PURE__ */ new Set([...Object.keys(previous.processEnv), ...Object.keys(next.processEnv)]);
21430
+ return [...keys].filter((key) => previous.processEnv[key] !== next.processEnv[key]).sort();
21556
21431
  }
21557
21432
 
21558
21433
  // src/usage.ts
@@ -21962,18 +21837,6 @@ function normalizeClaudeTaskProgressTokenUsage(value, memory) {
21962
21837
  }
21963
21838
 
21964
21839
  // src/probeModels.ts
21965
- var CONTEXT_WINDOW_TOKENS = {
21966
- "200k": 2e5,
21967
- "1m": 1e6
21968
- };
21969
- var defaultContextWindowOf = (caps) => {
21970
- const descriptor = caps.optionDescriptors.find(
21971
- (entry) => entry.type === "select" && entry.id === "contextWindow"
21972
- );
21973
- if (descriptor === void 0) return void 0;
21974
- const chosen = descriptor.options.find((option) => option.isDefault === true);
21975
- return chosen === void 0 ? void 0 : CONTEXT_WINDOW_TOKENS[chosen.id];
21976
- };
21977
21840
  var toProbeCapabilities = (caps) => ({
21978
21841
  optionDescriptors: caps.optionDescriptors.map(
21979
21842
  (descriptor) => descriptor.type === "select" ? {
@@ -21996,17 +21859,14 @@ var toProbeCapabilities = (caps) => ({
21996
21859
  var claudeProbeModels = (input) => {
21997
21860
  const seen = /* @__PURE__ */ new Set();
21998
21861
  const out = [];
21999
- for (const model of input.builtIn) {
21862
+ for (const model of input.catalog) {
22000
21863
  if (seen.has(model.slug)) continue;
22001
21864
  seen.add(model.slug);
22002
21865
  const userLabel = input.customModels.find((entry) => entry.slug === model.slug)?.label;
22003
- const contextWindow = defaultContextWindowOf(model.capabilities);
22004
21866
  out.push({
22005
21867
  slug: model.slug,
22006
21868
  name: userLabel?.trim() || model.name,
22007
- ...model.isLegacy === void 0 ? {} : { isLegacy: model.isLegacy },
22008
- capabilities: toProbeCapabilities(model.capabilities),
22009
- ...contextWindow === void 0 ? {} : { contextWindow }
21869
+ capabilities: toProbeCapabilities(model.capabilities)
22010
21870
  });
22011
21871
  }
22012
21872
  for (const model of input.customModels) {
@@ -22014,251 +21874,14 @@ var claudeProbeModels = (input) => {
22014
21874
  seen.add(model.slug);
22015
21875
  out.push({ slug: model.slug, name: model.label?.trim() || model.slug });
22016
21876
  }
22017
- for (const model of input.discovered ?? []) {
22018
- if (seen.has(model.slug)) continue;
22019
- seen.add(model.slug);
22020
- out.push(model);
22021
- }
22022
21877
  return out;
22023
21878
  };
22024
21879
 
22025
- // src/routedHome.ts
22026
- import * as NodeFSP from "node:fs/promises";
22027
- import * as NodePath6 from "node:path";
22028
-
22029
- // src/routedSettings.ts
22030
- import { brainSlotEnvKeys } from "@ycodium-ai/plugin-api";
22031
- var PROXY_ENV_KEYS = [
22032
- "HTTP_PROXY",
22033
- "HTTPS_PROXY",
22034
- "NO_PROXY",
22035
- "ALL_PROXY",
22036
- "http_proxy",
22037
- "https_proxy",
22038
- "no_proxy",
22039
- "all_proxy"
22040
- ];
22041
- function isRoutedLaunch(launchProcessEnv) {
22042
- const baseUrlKey = CLAUDE_CARD.modelConnectivity.baseUrlEnvKey;
22043
- if (baseUrlKey === void 0) return false;
22044
- return (launchProcessEnv?.[baseUrlKey] ?? "").trim().length > 0;
22045
- }
22046
- function routedEnvOverride(input) {
22047
- if (!isRoutedLaunch(input.launchProcessEnv)) return void 0;
22048
- const valueOf = (key) => input.mergedEnv[key] ?? "";
22049
- const override = {};
22050
- for (const key of brainSlotEnvKeys(CLAUDE_CARD)) override[key] = valueOf(key);
22051
- for (const { name } of CLAUDE_CARD.modelConnectivity.extraEnv ?? []) {
22052
- const bound = valueOf(name);
22053
- override[name] = bound.length === 0 ? "" : claudeCliModelId({
22054
- boundModelId: bound,
22055
- placeholder: CLAUDE_CARD.defaultModel,
22056
- ...input.customModels === void 0 ? {} : { customModels: input.customModels }
22057
- });
22058
- }
22059
- for (const key of PROXY_ENV_KEYS) override[key] = valueOf(key);
22060
- return override;
22061
- }
22062
-
22063
- // src/routedHome.ts
22064
- var BORROWED_ENTRY_NAMES = [
22065
- "projects",
22066
- "plugins",
22067
- "skills",
22068
- "agents",
22069
- "commands",
22070
- "CLAUDE.md"
22071
- ];
22072
- var GLOBAL_CONFIG_FILE_NAME = ".claude.json";
22073
- var USER_SETTINGS_FILE_NAME = "settings.json";
22074
- var ROUTING_AND_IDENTITY_SETTING_KEYS = [
22075
- "env",
22076
- "apiKeyHelper",
22077
- "awsAuthRefresh",
22078
- "awsCredentialExport"
22079
- ];
22080
- var FIRST_RUN_FLAG_KEYS = ["hasCompletedOnboarding", "bypassPermissionsModeAccepted"];
22081
- function isRecord5(value) {
22082
- return typeof value === "object" && value !== null && !Array.isArray(value);
22083
- }
22084
- async function readTextFile(filePath) {
22085
- try {
22086
- return await NodeFSP.readFile(filePath, "utf8");
22087
- } catch {
22088
- return null;
22089
- }
22090
- }
22091
- function parseJsonObject(raw) {
22092
- try {
22093
- const parsed = JSON.parse(raw);
22094
- return isRecord5(parsed) ? parsed : null;
22095
- } catch {
22096
- return null;
22097
- }
22098
- }
22099
- async function writeFileAtomic(filePath, contents) {
22100
- const tempPath = `${filePath}.${crypto.randomUUID()}.tmp`;
22101
- await NodeFSP.writeFile(tempPath, contents, { encoding: "utf8", mode: 384 });
22102
- await NodeFSP.rename(tempPath, filePath);
22103
- }
22104
- async function readLinkState(linkPath) {
22105
- try {
22106
- return { _tag: "Symlink", target: await NodeFSP.readlink(linkPath) };
22107
- } catch (error) {
22108
- const code = typeof error === "object" && error !== null && "code" in error ? String(error.code) : "";
22109
- if (code === "ENOENT") return { _tag: "Missing" };
22110
- return { _tag: "NotSymlink" };
22111
- }
22112
- }
22113
- async function isEmptyDirectory(path) {
22114
- try {
22115
- return (await NodeFSP.readdir(path)).length === 0;
22116
- } catch {
22117
- return false;
22118
- }
22119
- }
22120
- async function borrowEntry(input) {
22121
- const target = NodePath6.join(input.userConfigDirPath, input.entryName);
22122
- const targetInfo = await NodeFSP.stat(target).then(
22123
- (info) => info,
22124
- () => null
22125
- );
22126
- if (targetInfo === null) return;
22127
- const link = NodePath6.join(input.configDirPath, input.entryName);
22128
- const state = await readLinkState(link);
22129
- if (state._tag === "NotSymlink") {
22130
- if (!await isEmptyDirectory(link)) {
22131
- input.log?.warn("Claude routed home entry left unlinked: a real entry is already there.", {
22132
- entryName: input.entryName,
22133
- link
22134
- });
22135
- return;
22136
- }
22137
- await NodeFSP.rm(link, { recursive: true, force: true });
22138
- await NodeFSP.symlink(target, link, targetInfo.isDirectory() ? "junction" : "file");
22139
- return;
22140
- }
22141
- if (state._tag === "Symlink") {
22142
- if (NodePath6.resolve(NodePath6.dirname(link), state.target) === target) return;
22143
- await NodeFSP.rm(link, { force: true });
22144
- }
22145
- await NodeFSP.symlink(target, link, targetInfo.isDirectory() ? "junction" : "file");
22146
- }
22147
- async function mirrorUserSettings(input) {
22148
- const sourcePath = NodePath6.join(input.userConfigDirPath, USER_SETTINGS_FILE_NAME);
22149
- const raw = await readTextFile(sourcePath);
22150
- if (raw === null) return;
22151
- const parsed = parseJsonObject(raw);
22152
- if (parsed === null) {
22153
- input.log?.warn(
22154
- "Claude routed home settings mirror skipped: the source is not a JSON object.",
22155
- {
22156
- sourcePath
22157
- }
22158
- );
22159
- return;
22160
- }
22161
- const mirrored = { ...parsed };
22162
- for (const key of ROUTING_AND_IDENTITY_SETTING_KEYS) delete mirrored[key];
22163
- const targetPath = NodePath6.join(input.configDirPath, USER_SETTINGS_FILE_NAME);
22164
- const desired = `${JSON.stringify(mirrored, null, 2)}
22165
- `;
22166
- if (await readTextFile(targetPath) === desired) return;
22167
- await writeFileAtomic(targetPath, desired);
22168
- }
22169
- async function seedFirstRun(input) {
22170
- const filePath = NodePath6.join(input.configDirPath, GLOBAL_CONFIG_FILE_NAME);
22171
- const raw = await readTextFile(filePath);
22172
- const parsed = raw === null ? {} : parseJsonObject(raw);
22173
- if (parsed === null) {
22174
- input.log?.warn("Claude routed home first-run seed skipped: the config file is not readable.", {
22175
- filePath
22176
- });
22177
- return;
22178
- }
22179
- const next = { ...parsed };
22180
- let changed = false;
22181
- if (raw === null) {
22182
- for (const key of FIRST_RUN_FLAG_KEYS) next[key] = true;
22183
- changed = true;
22184
- }
22185
- const existingProjects = next["projects"];
22186
- const projects = isRecord5(existingProjects) ? { ...existingProjects } : {};
22187
- for (const trustedPath of input.trustedPaths) {
22188
- const resolved = NodePath6.resolve(trustedPath);
22189
- const entry = projects[resolved];
22190
- const project = isRecord5(entry) ? { ...entry } : {};
22191
- if (project["hasTrustDialogAccepted"] === true) continue;
22192
- project["hasTrustDialogAccepted"] = true;
22193
- projects[resolved] = project;
22194
- changed = true;
22195
- }
22196
- if (Object.keys(projects).length > 0) next["projects"] = projects;
22197
- if (!changed) return;
22198
- await writeFileAtomic(filePath, `${JSON.stringify(next, null, 2)}
22199
- `);
22200
- }
22201
- function resolveClaudeLaunchHome(input) {
22202
- const routed = isRoutedLaunch(input.launchProcessEnv);
22203
- const dataDirPath = input.pluginDataDirPath?.trim() ?? "";
22204
- if (routed && dataDirPath.length === 0) {
22205
- input.log?.warn(
22206
- "Ycodium is routing this turn but has no directory of its own to hand the CLI; it will read the user's."
22207
- );
22208
- }
22209
- return resolveClaudeHomeLayout({
22210
- homePath: input.homePath,
22211
- credentialsPath: input.credentialsPath,
22212
- ...routed && dataDirPath.length > 0 ? { routedHomePath: claudeRoutedHomePath(dataDirPath) } : {},
22213
- ...input.environment === void 0 ? {} : { environment: input.environment }
22214
- });
22215
- }
22216
- async function materializeClaudeRoutedHome(layout, options) {
22217
- if (layout.mode !== "routed") return;
22218
- const configDirPath = layout.configDirPath;
22219
- if (configDirPath === void 0) return;
22220
- if (configDirPath === layout.userConfigDirPath) {
22221
- throw new Error(
22222
- `Ycodium's Claude configuration directory '${configDirPath}' must differ from the user's.`
22223
- );
22224
- }
22225
- await NodeFSP.mkdir(configDirPath, { recursive: true });
22226
- await NodeFSP.chmod(configDirPath, 448);
22227
- const credentialsDirPath = layout.credentialsDirPath;
22228
- if (credentialsDirPath !== void 0 && credentialsDirPath !== configDirPath) {
22229
- await NodeFSP.mkdir(credentialsDirPath, { recursive: true });
22230
- await NodeFSP.chmod(credentialsDirPath, 448);
22231
- }
22232
- const log = options?.log;
22233
- for (const entryName of BORROWED_ENTRY_NAMES) {
22234
- try {
22235
- await borrowEntry({
22236
- userConfigDirPath: layout.userConfigDirPath,
22237
- configDirPath,
22238
- entryName,
22239
- log
22240
- });
22241
- } catch (error) {
22242
- log?.warn("Claude routed home entry left unlinked.", {
22243
- entryName,
22244
- reason: error instanceof Error ? error.message : String(error)
22245
- });
22246
- }
22247
- }
22248
- await mirrorUserSettings({
22249
- userConfigDirPath: layout.userConfigDirPath,
22250
- configDirPath,
22251
- log
22252
- });
22253
- await seedFirstRun({
22254
- configDirPath,
22255
- trustedPaths: options?.trustedPaths ?? [],
22256
- log
22257
- });
22258
- }
22259
-
22260
21880
  // src/textGeneration.ts
22261
- import { mergeLaunchEnv, brainSlotEnvKeys as brainSlotEnvKeys2 } from "@ycodium-ai/plugin-api/executor";
21881
+ import * as NodeFSP2 from "node:fs/promises";
21882
+ import * as NodeOS7 from "node:os";
21883
+ import * as NodePath7 from "node:path";
21884
+ import { mergeLaunchEnv } from "@ycodium-ai/plugin-api/executor";
22262
21885
 
22263
21886
  // src/textGenerationUtils.ts
22264
21887
  function limitSection(value, maxChars) {
@@ -22607,7 +22230,7 @@ function buildHandoverSummaryPrompt(input) {
22607
22230
 
22608
22231
  // src/textGeneration.ts
22609
22232
  var CLAUDE_TIMEOUT_MS = 18e4;
22610
- function isRecord6(value) {
22233
+ function isRecord5(value) {
22611
22234
  return typeof value === "object" && value !== null && !Array.isArray(value);
22612
22235
  }
22613
22236
  function readString3(value) {
@@ -22629,46 +22252,36 @@ function decodeStructuredOutput(stdout, operation) {
22629
22252
  cause: error
22630
22253
  });
22631
22254
  }
22632
- if (!isRecord6(parsed) || !("structured_output" in parsed)) {
22255
+ const envelope = Array.isArray(parsed) ? parsed.findLast((message) => isRecord5(message) && message["type"] === "result") : parsed;
22256
+ if (!isRecord5(envelope) || !("structured_output" in envelope)) {
22633
22257
  throw new Error(`${operation}: Claude CLI returned unexpected output format.`);
22634
22258
  }
22635
- return parsed.structured_output;
22259
+ return envelope["structured_output"];
22636
22260
  }
22637
- function createClaudeTextGeneration(recipe, host, pluginDataDirPath) {
22261
+ function createClaudeTextGeneration(recipe, host, modelCatalog) {
22638
22262
  const runClaudeJson = async (input) => {
22639
- const homeLayout = resolveClaudeLaunchHome({
22640
- homePath: recipe.homePath,
22641
- credentialsPath: recipe.credentialsPath,
22642
- launchProcessEnv: input.launch?.processEnv,
22643
- pluginDataDirPath,
22644
- log: host.log
22645
- });
22646
- await materializeClaudeRoutedHome(homeLayout, {
22647
- trustedPaths: [input.cwd],
22648
- log: host.log
22649
- });
22650
22263
  const processEnv = mergeLaunchEnv({
22651
- brainSlots: brainSlotEnvKeys2(CLAUDE_CARD),
22652
22264
  instanceTable: recipe.environment,
22653
- handOverrides: claudeHomeVariables(homeLayout),
22654
- launch: input.launch?.processEnv
22265
+ handOverrides: claudeHomeVariables(resolveClaudeHomeLayout(recipe))
22655
22266
  });
22656
- const modelSelection = isRecord6(input.modelSelection) ? input.modelSelection : {};
22657
- const requestedModel = input.launch?.requestedModelId ?? modelIdFromSelection(modelSelection) ?? "claude-haiku-4-5";
22658
- const caps = claudeModelCapabilities(requestedModel, recipe.customModels);
22267
+ const modelSelection = isRecord5(input.modelSelection) ? input.modelSelection : {};
22268
+ const requestedModel = modelIdFromSelection(modelSelection) ?? "claude-haiku-4-5";
22269
+ const caps = claudeModelCapabilities(requestedModel, modelCatalog(), recipe.customModels);
22659
22270
  const rawEffort = getModelSelectionStringOptionValue(modelSelection, "effort");
22660
22271
  const resolvedEffort = resolveClaudeEffort(caps, rawEffort);
22661
- const cliEffort = normalizeClaudeCliEffort(resolvedEffort, requestedModel, caps);
22272
+ const cliEffort = normalizeClaudeCliEffort(resolvedEffort);
22662
22273
  const ultracode = isClaudeUltracodeEffort(resolvedEffort);
22663
22274
  const thinking = getModelSelectionBooleanOptionValue(modelSelection, "thinking");
22664
22275
  const fastMode = getModelSelectionBooleanOptionValue(modelSelection, "fastMode");
22665
22276
  const settings = {
22277
+ // 元数据生成只需要传进去的 prompt:不跑用户配的 hook。
22278
+ disableAllHooks: true,
22666
22279
  ...typeof thinking === "boolean" ? { alwaysThinkingEnabled: thinking } : {},
22667
22280
  ...fastMode ? { fastMode: true } : {},
22668
22281
  ...ultracode ? { ultracode: true } : {}
22669
22282
  };
22670
- const settingsJson = Object.keys(settings).length > 0 ? JSON.stringify(settings) : void 0;
22671
- const apiModelId = input.launch?.requestedModelId ?? (typeof modelSelection["model"] === "string" ? resolveClaudeApiModelId(caps, { model: modelSelection["model"] }) : requestedModel);
22283
+ const settingsJson = JSON.stringify(settings);
22284
+ const apiModelId = typeof modelSelection["model"] === "string" ? modelSelection["model"] : requestedModel;
22672
22285
  const args = [
22673
22286
  "-p",
22674
22287
  "--output-format",
@@ -22678,44 +22291,60 @@ function createClaudeTextGeneration(recipe, host, pluginDataDirPath) {
22678
22291
  "--model",
22679
22292
  apiModelId,
22680
22293
  ...cliEffort ? ["--effort", cliEffort] : [],
22681
- ...settingsJson ? ["--settings", settingsJson] : [],
22682
- "--dangerously-skip-permissions"
22294
+ "--settings",
22295
+ settingsJson,
22296
+ // 标题、提交信息这类只要文本:即使 prompt 里出现技能名,也不给任何可执行能力。
22297
+ "--tools",
22298
+ "",
22299
+ "--disable-slash-commands",
22300
+ "--strict-mcp-config"
22683
22301
  ];
22684
22302
  const program = recipe.binaryPath.trim() || "claude";
22685
- const spawned = await host.process.spawn({
22686
- program,
22687
- args,
22688
- cwd: input.cwd,
22689
- env: processEnv,
22690
- owner: { bindingId: host.bindingId }
22691
- });
22692
- const timeout = AbortSignal.timeout(CLAUDE_TIMEOUT_MS);
22693
- const signal = input.signal ? AbortSignal.any([input.signal, timeout]) : timeout;
22694
- const abort = () => {
22695
- void spawned.kill("SIGTERM");
22303
+ const runIn = async (cwd) => {
22304
+ const spawned = await host.process.spawn({
22305
+ program,
22306
+ args,
22307
+ cwd,
22308
+ env: processEnv,
22309
+ owner: { bindingId: host.bindingId }
22310
+ });
22311
+ const timeout = AbortSignal.timeout(CLAUDE_TIMEOUT_MS);
22312
+ const signal = input.signal ? AbortSignal.any([input.signal, timeout]) : timeout;
22313
+ const abort = () => {
22314
+ void spawned.kill("SIGTERM");
22315
+ };
22316
+ if (signal.aborted) abort();
22317
+ else signal.addEventListener("abort", abort, { once: true });
22318
+ await spawned.writeStdin(input.prompt);
22319
+ await spawned.closeStdin();
22320
+ const [stdout, stderr, exitCode] = await Promise.all([
22321
+ collectText(spawned.stdout),
22322
+ collectText(spawned.stderr),
22323
+ spawned.exited
22324
+ ]);
22325
+ if (signal.aborted && !input.signal?.aborted) {
22326
+ throw new Error(`${input.operation}: Claude CLI request timed out.`);
22327
+ }
22328
+ if (exitCode !== 0) {
22329
+ const detail = stderr.trim() || stdout.trim();
22330
+ throw new Error(
22331
+ detail.length > 0 ? `Claude CLI command failed: ${detail}` : `Claude CLI command failed with code ${exitCode}.`
22332
+ );
22333
+ }
22334
+ return decodeStructuredOutput(stdout, input.operation);
22696
22335
  };
22697
- if (signal.aborted) abort();
22698
- else signal.addEventListener("abort", abort, { once: true });
22699
- await spawned.writeStdin(input.prompt);
22700
- await spawned.closeStdin();
22701
- const [stdout, stderr, exitCode] = await Promise.all([
22702
- collectText(spawned.stdout),
22703
- collectText(spawned.stderr),
22704
- spawned.exited
22705
- ]);
22706
- if (signal.aborted && !input.signal?.aborted) {
22707
- throw new Error(`${input.operation}: Claude CLI request timed out.`);
22708
- }
22709
- if (exitCode !== 0) {
22710
- const detail = stderr.trim() || stdout.trim();
22711
- throw new Error(
22712
- detail.length > 0 ? `Claude CLI command failed: ${detail}` : `Claude CLI command failed with code ${exitCode}.`
22713
- );
22336
+ if (input.operation !== "generateThreadTitle") return runIn(input.cwd);
22337
+ const scratchDirectory = await NodeFSP2.mkdtemp(
22338
+ NodePath7.join(NodeOS7.tmpdir(), "ycodium-claude-title-")
22339
+ );
22340
+ try {
22341
+ return await runIn(scratchDirectory);
22342
+ } finally {
22343
+ await NodeFSP2.rm(scratchDirectory, { recursive: true, force: true }).catch(() => void 0);
22714
22344
  }
22715
- return decodeStructuredOutput(stdout, input.operation);
22716
22345
  };
22717
22346
  const asRecord = (value, operation) => {
22718
- if (!isRecord6(value))
22347
+ if (!isRecord5(value))
22719
22348
  throw new Error(`${operation}: Claude returned invalid structured output.`);
22720
22349
  return value;
22721
22350
  };
@@ -22729,7 +22358,7 @@ function createClaudeTextGeneration(recipe, host, pluginDataDirPath) {
22729
22358
  stagedSummary: readString3(input["stagedSummary"]),
22730
22359
  stagedPatch: readString3(input["stagedPatch"]),
22731
22360
  includeBranch,
22732
- ...isRecord6(input["policy"]) ? { policy: input["policy"] } : {}
22361
+ ...isRecord5(input["policy"]) ? { policy: input["policy"] } : {}
22733
22362
  });
22734
22363
  const generated = asRecord(
22735
22364
  await runClaudeJson({
@@ -22738,7 +22367,6 @@ function createClaudeTextGeneration(recipe, host, pluginDataDirPath) {
22738
22367
  prompt,
22739
22368
  outputSchema,
22740
22369
  modelSelection: input["modelSelection"],
22741
- ...isRecord6(input["launch"]) ? { launch: input["launch"] } : {},
22742
22370
  ...signal === void 0 ? {} : { signal }
22743
22371
  }),
22744
22372
  "generateCommitMessage"
@@ -22762,7 +22390,7 @@ function createClaudeTextGeneration(recipe, host, pluginDataDirPath) {
22762
22390
  diffSummary: readString3(input["diffSummary"]),
22763
22391
  diffPatch: readString3(input["diffPatch"]),
22764
22392
  ...typeof input["changeRequestTemplate"] === "string" ? { changeRequestTemplate: input["changeRequestTemplate"] } : {},
22765
- ...isRecord6(input["policy"]) ? { policy: input["policy"] } : {}
22393
+ ...isRecord5(input["policy"]) ? { policy: input["policy"] } : {}
22766
22394
  });
22767
22395
  const generated = asRecord(
22768
22396
  await runClaudeJson({
@@ -22771,7 +22399,6 @@ function createClaudeTextGeneration(recipe, host, pluginDataDirPath) {
22771
22399
  prompt,
22772
22400
  outputSchema,
22773
22401
  modelSelection: input["modelSelection"],
22774
- ...isRecord6(input["launch"]) ? { launch: input["launch"] } : {},
22775
22402
  ...signal === void 0 ? {} : { signal }
22776
22403
  }),
22777
22404
  "generatePrContent"
@@ -22790,7 +22417,7 @@ function createClaudeTextGeneration(recipe, host, pluginDataDirPath) {
22790
22417
  const { prompt, outputSchema } = buildBranchNamePrompt({
22791
22418
  message: readString3(input["message"]),
22792
22419
  ...Array.isArray(input["attachments"]) ? { attachments: input["attachments"] } : {},
22793
- ...isRecord6(input["policy"]) ? { policy: input["policy"] } : {}
22420
+ ...isRecord5(input["policy"]) ? { policy: input["policy"] } : {}
22794
22421
  });
22795
22422
  const generated = asRecord(
22796
22423
  await runClaudeJson({
@@ -22799,7 +22426,6 @@ function createClaudeTextGeneration(recipe, host, pluginDataDirPath) {
22799
22426
  prompt,
22800
22427
  outputSchema,
22801
22428
  modelSelection: input["modelSelection"],
22802
- ...isRecord6(input["launch"]) ? { launch: input["launch"] } : {},
22803
22429
  ...signal === void 0 ? {} : { signal }
22804
22430
  }),
22805
22431
  "generateBranchName"
@@ -22816,7 +22442,7 @@ function createClaudeTextGeneration(recipe, host, pluginDataDirPath) {
22816
22442
  message: readString3(input["message"]),
22817
22443
  ...typeof input["previousTitle"] === "string" ? { previousTitle: input["previousTitle"] } : {},
22818
22444
  ...Array.isArray(input["attachments"]) ? { attachments: input["attachments"] } : {},
22819
- ...isRecord6(input["policy"]) ? { policy: input["policy"] } : {}
22445
+ ...isRecord5(input["policy"]) ? { policy: input["policy"] } : {}
22820
22446
  });
22821
22447
  const generated = asRecord(
22822
22448
  await runClaudeJson({
@@ -22825,7 +22451,6 @@ function createClaudeTextGeneration(recipe, host, pluginDataDirPath) {
22825
22451
  prompt,
22826
22452
  outputSchema,
22827
22453
  modelSelection: input["modelSelection"],
22828
- ...isRecord6(input["launch"]) ? { launch: input["launch"] } : {},
22829
22454
  ...signal === void 0 ? {} : { signal }
22830
22455
  }),
22831
22456
  "generateThreadTitle"
@@ -22848,7 +22473,6 @@ function createClaudeTextGeneration(recipe, host, pluginDataDirPath) {
22848
22473
  prompt,
22849
22474
  outputSchema,
22850
22475
  modelSelection: input["modelSelection"],
22851
- ...isRecord6(input["launch"]) ? { launch: input["launch"] } : {},
22852
22476
  ...signal === void 0 ? {} : { signal }
22853
22477
  }),
22854
22478
  "summarizeHandover"
@@ -22941,9 +22565,9 @@ var PromptQueue = class {
22941
22565
  const queued = this.#queued.shift();
22942
22566
  if (queued !== void 0) return Promise.resolve(queued);
22943
22567
  if (this.#closed) return Promise.resolve(void 0);
22944
- return new Promise((resolve3) => {
22568
+ return new Promise((resolve2) => {
22945
22569
  this.#waiters.push((result) => {
22946
- resolve3(result.done ? void 0 : result.value);
22570
+ resolve2(result.done ? void 0 : result.value);
22947
22571
  });
22948
22572
  });
22949
22573
  }
@@ -23098,6 +22722,48 @@ function createClaudeAdapter(recipe, host, options) {
23098
22722
  prompt: input.prompt,
23099
22723
  options: input.options
23100
22724
  }));
22725
+ const buildLaunchEnvironment = () => mergeLaunchEnv2({
22726
+ inherited: { ...process.env, ...options?.environment },
22727
+ instanceTable: recipe.environment,
22728
+ handOverrides: claudeHomeVariables(resolveClaudeHomeLayout(recipe))
22729
+ });
22730
+ let modelCatalog;
22731
+ let modelCatalogRead;
22732
+ let modelCatalogFailedAt;
22733
+ const currentModelCatalog = () => modelCatalog?.models ?? [];
22734
+ const refreshModelCatalog = (input) => {
22735
+ const fresh = modelCatalog !== void 0 && Date.now() - modelCatalog.readAt < CLAUDE_SDK_MODELS_TTL_MS;
22736
+ if (fresh && input?.force !== true) return Promise.resolve();
22737
+ if (input?.force !== true && modelCatalogFailedAt !== void 0 && Date.now() - modelCatalogFailedAt < CLAUDE_SDK_MODELS_RETRY_MS) {
22738
+ return Promise.resolve();
22739
+ }
22740
+ if (modelCatalogRead !== void 0) return modelCatalogRead;
22741
+ const read = (async () => {
22742
+ const extraArgs = parseCliArgs(recipe.launchArgs).flags;
22743
+ const models = await readClaudeSdkModels({
22744
+ createQuery,
22745
+ signal: input?.signal,
22746
+ options: {
22747
+ cwd: claudeSdkModelsCwd(),
22748
+ pathToClaudeCodeExecutable: claudeSdkExecutablePath,
22749
+ settingSources: [...CLAUDE_SETTING_SOURCES],
22750
+ env: compactEnv(buildLaunchEnvironment()),
22751
+ ...Object.keys(extraArgs).length > 0 ? { extraArgs } : {}
22752
+ }
22753
+ });
22754
+ if (models === void 0) {
22755
+ modelCatalogFailedAt = Date.now();
22756
+ } else {
22757
+ modelCatalogFailedAt = void 0;
22758
+ modelCatalog = { models: claudeCatalogFromSdk(models), readAt: Date.now() };
22759
+ }
22760
+ })().finally(() => {
22761
+ modelCatalogRead = void 0;
22762
+ });
22763
+ modelCatalogRead = read;
22764
+ return read;
22765
+ };
22766
+ const declaredModels = [CLAUDE_CARD.defaultModel, CLAUDE_CARD.defaultTextGenerationModel];
23101
22767
  const sessions = /* @__PURE__ */ new Map();
23102
22768
  const runtimeEventQueue = new EventBus();
23103
22769
  const nowIso = () => (/* @__PURE__ */ new Date()).toISOString();
@@ -23381,8 +23047,8 @@ function createClaudeAdapter(recipe, host, options) {
23381
23047
  try {
23382
23048
  usage = await Promise.race([
23383
23049
  context.query.getContextUsage(),
23384
- new Promise((resolve3) => {
23385
- setTimeout(() => resolve3(void 0), 1e3);
23050
+ new Promise((resolve2) => {
23051
+ setTimeout(() => resolve2(void 0), 1e3);
23386
23052
  })
23387
23053
  ]);
23388
23054
  } catch {
@@ -24762,7 +24428,7 @@ function createClaudeAdapter(recipe, host, options) {
24762
24428
  if (streamFiber) {
24763
24429
  await Promise.race([
24764
24430
  streamFiber.catch(() => void 0),
24765
- new Promise((resolve3) => setTimeout(resolve3, 1e3))
24431
+ new Promise((resolve2) => setTimeout(resolve2, 1e3))
24766
24432
  ]);
24767
24433
  }
24768
24434
  const updatedAt = nowIso();
@@ -24845,9 +24511,6 @@ function createClaudeAdapter(recipe, host, options) {
24845
24511
  }
24846
24512
  context.currentSettings = desired.settings;
24847
24513
  }
24848
- if (desired.contextWindow !== void 0) {
24849
- context.lastKnownContextWindow = desired.contextWindow;
24850
- }
24851
24514
  const updatedAt = nowIso();
24852
24515
  context.session = {
24853
24516
  ...context.session,
@@ -24877,16 +24540,20 @@ function createClaudeAdapter(recipe, host, options) {
24877
24540
  const startSession = async (input) => {
24878
24541
  const extraArgs = parseCliArgs(recipe.launchArgs).flags;
24879
24542
  const modelSelection = boundModelSelection(input.modelSelection, boundInstanceId);
24880
- const caps = claudeModelCapabilities(modelSelection?.model, recipe.customModels);
24543
+ await modelCatalogRead;
24544
+ const caps = claudeModelCapabilities(
24545
+ modelSelection?.model,
24546
+ currentModelCatalog(),
24547
+ recipe.customModels
24548
+ );
24881
24549
  const descriptors = caps.optionDescriptors;
24882
- const apiModelId = modelSelection?.model ? resolveClaudeApiModelId(caps, {
24883
- model: claudeCliModelId({
24884
- boundModelId: modelSelection.model,
24885
- placeholder: CLAUDE_CARD.defaultModel,
24886
- customModels: recipe.customModels
24887
- })
24550
+ const apiModelId = modelSelection?.model ? claudeCliModelId({
24551
+ requestedModelId: modelSelection.model,
24552
+ placeholder: CLAUDE_CARD.defaultModel,
24553
+ catalog: currentModelCatalog(),
24554
+ declared: declaredModels,
24555
+ customModels: recipe.customModels
24888
24556
  }) : void 0;
24889
- const initialContextWindow = selectedClaudeContextWindow(caps, modelSelection);
24890
24557
  const rawEffort = getModelSelectionStringOptionValue(modelSelection, "effort");
24891
24558
  const effort = resolveClaudeEffort(caps, rawEffort) ?? null;
24892
24559
  const fastModeSupported = descriptors.some(
@@ -24898,7 +24565,7 @@ function createClaudeAdapter(recipe, host, options) {
24898
24565
  const fastMode = getModelSelectionBooleanOptionValue(modelSelection, "fastMode") === true && fastModeSupported;
24899
24566
  const thinking = thinkingSupported ? getModelSelectionBooleanOptionValue(modelSelection, "thinking") : void 0;
24900
24567
  const ultracode = isClaudeUltracodeEffort(effort);
24901
- const effectiveEffort = getEffectiveClaudeAgentEffort(effort, modelSelection?.model, caps);
24568
+ const effectiveEffort = getEffectiveClaudeAgentEffort(effort);
24902
24569
  const permissionMode = claudePermissionModeFor(input.runtimeMode, input.nativeModeId);
24903
24570
  const settings = buildClaudeSessionSettings({
24904
24571
  thinking,
@@ -24906,28 +24573,8 @@ function createClaudeAdapter(recipe, host, options) {
24906
24573
  ultracode,
24907
24574
  autoCompactWindow: recipe.autoCompactWindow
24908
24575
  });
24909
- const homeLayout = resolveClaudeLaunchHome({
24910
- homePath: recipe.homePath,
24911
- credentialsPath: recipe.credentialsPath,
24912
- launchProcessEnv: input.launch.processEnv,
24913
- pluginDataDirPath: options?.pluginDataDirPath,
24914
- environment: claudeHomeEnvironment,
24915
- ...options?.log === void 0 ? {} : { log: options.log }
24916
- });
24917
- const launchEnvironment = mergeLaunchEnv2({
24918
- brainSlots: brainSlotEnvKeys3(CLAUDE_CARD),
24919
- inherited: { ...process.env, ...options?.environment },
24920
- instanceTable: recipe.environment,
24921
- handOverrides: claudeHomeVariables(homeLayout),
24922
- launch: input.launch.processEnv
24923
- });
24924
24576
  const spawnEnvironment = {
24925
- processEnv: compactEnv(launchEnvironment),
24926
- settingsEnv: routedEnvOverride({
24927
- launchProcessEnv: input.launch.processEnv,
24928
- mergedEnv: launchEnvironment,
24929
- ...recipe.customModels === void 0 ? {} : { customModels: recipe.customModels }
24930
- })
24577
+ processEnv: compactEnv(buildLaunchEnvironment())
24931
24578
  };
24932
24579
  const existingContext = sessions.get(input.threadId);
24933
24580
  if (existingContext) {
@@ -24935,7 +24582,6 @@ function createClaudeAdapter(recipe, host, options) {
24935
24582
  permissionMode,
24936
24583
  ...apiModelId ? { apiModelId } : {},
24937
24584
  ...effectiveEffort ? { effectiveEffort } : {},
24938
- ...initialContextWindow !== void 0 ? { contextWindow: initialContextWindow } : {},
24939
24585
  ...modelSelection?.model ? { model: modelSelection.model } : {},
24940
24586
  settings,
24941
24587
  spawnEnvironment
@@ -24971,7 +24617,7 @@ function createClaudeAdapter(recipe, host, options) {
24971
24617
  return await Promise.race([
24972
24618
  contextDeferred.promise,
24973
24619
  new Promise(
24974
- (resolve3) => setTimeout(() => resolve3(void 0), CLAUDE_SESSION_CONTEXT_WAIT_MS)
24620
+ (resolve2) => setTimeout(() => resolve2(void 0), CLAUDE_SESSION_CONTEXT_WAIT_MS)
24975
24621
  )
24976
24622
  ]);
24977
24623
  } catch {
@@ -25255,11 +24901,7 @@ function createClaudeAdapter(recipe, host, options) {
25255
24901
  const onUserDialog = (request, callbackOptions) => handleResumeDialog(request, callbackOptions);
25256
24902
  const additionalDirectories = input.cwd ? [input.cwd] : [];
25257
24903
  const mcpServers = toClaudeMcpServers(input.mcpServers);
25258
- await materializeClaudeRoutedHome(homeLayout, {
25259
- ...input.cwd ? { trustedPaths: [input.cwd] } : {},
25260
- ...options?.log === void 0 ? {} : { log: options.log }
25261
- });
25262
- const querySettings = spawnEnvironment.settingsEnv === void 0 ? { ...settings } : { ...settings, env: { ...spawnEnvironment.settingsEnv } };
24904
+ const querySettings = { ...settings };
25263
24905
  const stderrTail = createOutputTail(CLAUDE_STDERR_TAIL_CHARS);
25264
24906
  const queryOptions = {
25265
24907
  ...input.cwd ? { cwd: input.cwd } : {},
@@ -25340,6 +24982,8 @@ function createClaudeAdapter(recipe, host, options) {
25340
24982
  basePermissionMode: permissionMode,
25341
24983
  currentPermissionMode: permissionMode,
25342
24984
  currentApiModelId: apiModelId,
24985
+ // 上下文长度只信 SDK 结果里报的那个数,起步时不猜。
24986
+ lastKnownContextWindow: void 0,
25343
24987
  currentEffort: effectiveEffort ?? void 0,
25344
24988
  launchEffort: effectiveEffort ?? void 0,
25345
24989
  spawnEnvironment,
@@ -25360,7 +25004,6 @@ function createClaudeAdapter(recipe, host, options) {
25360
25004
  compaction: void 0,
25361
25005
  compactionInProgress: false,
25362
25006
  pendingManualCompaction: false,
25363
- lastKnownContextWindow: initialContextWindow,
25364
25007
  lastKnownTokenUsage: void 0,
25365
25008
  lastKnownTotalProcessedTokens: void 0,
25366
25009
  lastAssistantUuid: resumeState?.resumeSessionAt,
@@ -25443,13 +25086,17 @@ function createClaudeAdapter(recipe, host, options) {
25443
25086
  await completeTurn(context, "completed");
25444
25087
  }
25445
25088
  if (modelSelection?.model) {
25446
- const turnCaps = claudeModelCapabilities(modelSelection.model, recipe.customModels);
25447
- const apiModelId = resolveClaudeApiModelId(turnCaps, {
25448
- model: claudeCliModelId({
25449
- boundModelId: modelSelection.model,
25450
- placeholder: CLAUDE_CARD.defaultModel,
25451
- customModels: recipe.customModels
25452
- })
25089
+ const turnCaps = claudeModelCapabilities(
25090
+ modelSelection.model,
25091
+ currentModelCatalog(),
25092
+ recipe.customModels
25093
+ );
25094
+ const apiModelId = claudeCliModelId({
25095
+ requestedModelId: modelSelection.model,
25096
+ placeholder: CLAUDE_CARD.defaultModel,
25097
+ catalog: currentModelCatalog(),
25098
+ declared: declaredModels,
25099
+ customModels: recipe.customModels
25453
25100
  });
25454
25101
  if (context.currentApiModelId !== apiModelId) {
25455
25102
  try {
@@ -25467,7 +25114,7 @@ function createClaudeAdapter(recipe, host, options) {
25467
25114
  turnCaps,
25468
25115
  getModelSelectionStringOptionValue(modelSelection, "effort")
25469
25116
  );
25470
- context.currentEffort = getEffectiveClaudeAgentEffort(turnEffort ?? null, modelSelection.model, turnCaps) ?? void 0;
25117
+ context.currentEffort = getEffectiveClaudeAgentEffort(turnEffort ?? null) ?? void 0;
25471
25118
  }
25472
25119
  if (input.interactionMode === "plan") {
25473
25120
  try {
@@ -25484,7 +25131,11 @@ function createClaudeAdapter(recipe, host, options) {
25484
25131
  }
25485
25132
  context.currentPermissionMode = context.basePermissionMode;
25486
25133
  }
25487
- const turnCapsForPrompt = claudeModelCapabilities(modelSelection?.model, recipe.customModels);
25134
+ const turnCapsForPrompt = claudeModelCapabilities(
25135
+ modelSelection?.model,
25136
+ currentModelCatalog(),
25137
+ recipe.customModels
25138
+ );
25488
25139
  const turnId = steeringTurnState?.turnId ?? randomUUIDv4();
25489
25140
  if (steeringTurnState === null) {
25490
25141
  const turnState = {
@@ -25548,7 +25199,7 @@ function createClaudeAdapter(recipe, host, options) {
25548
25199
  context.query.interrupt().then(() => settled.promise).then(() => {
25549
25200
  acknowledged = true;
25550
25201
  }),
25551
- new Promise((resolve3) => setTimeout(resolve3, CLAUDE_INTERRUPT_GRACE_MS))
25202
+ new Promise((resolve2) => setTimeout(resolve2, CLAUDE_INTERRUPT_GRACE_MS))
25552
25203
  ]);
25553
25204
  } catch (cause) {
25554
25205
  throw toRequestError(threadId, "turn/interrupt", cause);
@@ -25648,6 +25299,8 @@ function createClaudeAdapter(recipe, host, options) {
25648
25299
  await stopSessionInternal(context, { emitExitEvent: true });
25649
25300
  };
25650
25301
  return {
25302
+ /** 型号目录:探测用它读并拿当前这份;会话内部自己走同一份。 */
25303
+ modelCatalog: { refresh: refreshModelCatalog, current: currentModelCatalog },
25651
25304
  startSession,
25652
25305
  sendTurn,
25653
25306
  interruptTurn,
@@ -25674,17 +25327,14 @@ function createClaudeAdapter(recipe, host, options) {
25674
25327
  };
25675
25328
  }
25676
25329
  function claudeAdapterBaseEnvironment(recipe) {
25677
- return {
25678
- ...process.env,
25679
- ...withoutBrainSlots(recipe.environment, brainSlotEnvKeys3(CLAUDE_CARD))
25680
- };
25330
+ return { ...process.env, ...recipe.environment };
25681
25331
  }
25682
- function createClaudeInstance(config, host, attachments, pluginDataDirPath) {
25332
+ function createClaudeInstance(config, host, attachments, overrides) {
25683
25333
  const recipe = parseClaudeInstanceConfig(config);
25684
25334
  const adapter = createClaudeAdapter(recipe, host, {
25685
25335
  instanceId: host.instanceId,
25686
25336
  ...attachments === void 0 ? {} : { attachments },
25687
- ...pluginDataDirPath === void 0 ? {} : { pluginDataDirPath },
25337
+ ...overrides?.createQuery === void 0 ? {} : { createQuery: overrides.createQuery },
25688
25338
  environment: claudeAdapterBaseEnvironment(recipe),
25689
25339
  log: host.log
25690
25340
  });
@@ -25716,6 +25366,7 @@ function createClaudeInstance(config, host, attachments, pluginDataDirPath) {
25716
25366
  return { status: "unknown" };
25717
25367
  }
25718
25368
  };
25369
+ let lastProbedVersion;
25719
25370
  const probe = async (signal) => {
25720
25371
  if (recipe.binaryPath.length === 0) {
25721
25372
  return {
@@ -25741,11 +25392,18 @@ function createClaudeInstance(config, host, attachments, pluginDataDirPath) {
25741
25392
  ]);
25742
25393
  const version = parseGenericCliVersion(`${stdout}
25743
25394
  ${stderr}`);
25395
+ if (version !== null || exitCode === 0) {
25396
+ await adapter.modelCatalog.refresh({ signal, force: version !== lastProbedVersion });
25397
+ lastProbedVersion = version;
25398
+ }
25744
25399
  const models = claudeProbeModels({
25745
- builtIn: getBuiltInClaudeModelsForVersion(version),
25400
+ catalog: adapter.modelCatalog.current(),
25746
25401
  customModels: recipe.customModels
25747
25402
  });
25748
25403
  const auth = probeAuth();
25404
+ const skills = await discoverClaudeSkills(
25405
+ resolveClaudeConfigDirPath(recipe.homePath, { ...process.env, ...recipe.environment })
25406
+ );
25749
25407
  if (exitCode !== 0 && version === null) {
25750
25408
  const detail = (stderr || stdout || "claude --version failed").trim();
25751
25409
  return {
@@ -25763,6 +25421,7 @@ ${stderr}`);
25763
25421
  readiness: "error",
25764
25422
  auth,
25765
25423
  models,
25424
+ skills,
25766
25425
  detail: "Claude is signed out",
25767
25426
  // 退出登录了档位照样是那五档。不报就退回宿主四档,而那份映射少了 `plan`。
25768
25427
  ...CLAUDE_NATIVE_MODE_REPORT
@@ -25774,6 +25433,7 @@ ${stderr}`);
25774
25433
  readiness: "ready",
25775
25434
  auth,
25776
25435
  models,
25436
+ skills,
25777
25437
  ...CLAUDE_NATIVE_MODE_REPORT
25778
25438
  };
25779
25439
  };
@@ -25785,7 +25445,7 @@ ${stderr}`);
25785
25445
  );
25786
25446
  return {
25787
25447
  configDir,
25788
- transcriptDir: NodePath7.join(configDir, "projects")
25448
+ transcriptDir: NodePath8.join(configDir, "projects")
25789
25449
  };
25790
25450
  };
25791
25451
  return {
@@ -25802,7 +25462,7 @@ ${stderr}`);
25802
25462
  readThread: adapter.readThread,
25803
25463
  events: adapter.events,
25804
25464
  dispose: adapter.dispose,
25805
- textGeneration: createClaudeTextGeneration(recipe, host, pluginDataDirPath),
25465
+ textGeneration: createClaudeTextGeneration(recipe, host, adapter.modelCatalog.current),
25806
25466
  continuationKey: claudeContinuationKey(recipe.homePath),
25807
25467
  sessions: {
25808
25468
  scan: async (input) => {
@@ -25828,7 +25488,7 @@ ${stderr}`);
25828
25488
  start: async () => {
25829
25489
  const command = claudeSignInCommand(
25830
25490
  recipe.credentialsPath,
25831
- NodeOS6.type() === "Windows_NT" ? "win32" : "linux"
25491
+ NodeOS8.type() === "Windows_NT" ? "win32" : "linux"
25832
25492
  );
25833
25493
  return {
25834
25494
  operationId: `claude-sign-in-${crypto.randomUUID()}`,
@@ -25844,13 +25504,12 @@ ${stderr}`);
25844
25504
 
25845
25505
  // src/index.ts
25846
25506
  var capturedAttachments;
25847
- var capturedDataDirPath;
25848
25507
  var executors = [
25849
25508
  {
25850
25509
  card: CLAUDE_CARD,
25851
25510
  createInstance: async (config, host) => {
25852
25511
  host.log.info("createInstance");
25853
- return createClaudeInstance(config, host, capturedAttachments, capturedDataDirPath);
25512
+ return createClaudeInstance(config, host, capturedAttachments);
25854
25513
  }
25855
25514
  }
25856
25515
  ];
@@ -25860,7 +25519,6 @@ var index_default = definePlugin({
25860
25519
  executors,
25861
25520
  activate(ctx) {
25862
25521
  capturedAttachments = ctx.attachments;
25863
- capturedDataDirPath = ctx.paths.dataDir;
25864
25522
  ctx.log.info("claude executor ready");
25865
25523
  }
25866
25524
  });