@ycodium-ai/agent-claude 0.2.2169 → 0.2.2510

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
  }
@@ -21234,10 +21119,32 @@ var CLAUDE_SETTING_SOURCES = [
21234
21119
  "local"
21235
21120
  ];
21236
21121
 
21122
+ // src/readOnlyDirectories.ts
21123
+ var GLOB_METACHARACTERS = /[*?[\]{}]/;
21124
+ function toClaudeAbsolutePath(directory) {
21125
+ const trimmed = directory.trim();
21126
+ if (GLOB_METACHARACTERS.test(trimmed)) return void 0;
21127
+ const drive = /^([A-Za-z]):[\\/]+(.*)$/.exec(trimmed);
21128
+ if (drive) {
21129
+ const rest = (drive[2] ?? "").split("\\").join("/").replace(/\/+$/, "");
21130
+ return rest === "" ? void 0 : `//${(drive[1] ?? "").toLowerCase()}/${rest}`;
21131
+ }
21132
+ if (/^\/[^/]/.test(trimmed)) return `/${trimmed.replace(/\/+$/, "")}`;
21133
+ return void 0;
21134
+ }
21135
+ function claudeReadAllowRules(directories) {
21136
+ const rules = [];
21137
+ for (const directory of directories ?? []) {
21138
+ const path = toClaudeAbsolutePath(directory);
21139
+ if (path !== void 0) rules.push(`Read(${path}/**)`);
21140
+ }
21141
+ return rules;
21142
+ }
21143
+
21237
21144
  // src/sessions.ts
21238
21145
  import * as NodeFS3 from "node:fs";
21239
- import * as NodeOS5 from "node:os";
21240
- import * as NodePath5 from "node:path";
21146
+ import * as NodeOS6 from "node:os";
21147
+ import * as NodePath6 from "node:path";
21241
21148
  import * as NodeProcess from "node:process";
21242
21149
 
21243
21150
  // src/sessionScope.ts
@@ -21357,7 +21264,7 @@ function listTranscriptFiles(transcriptDir, directory) {
21357
21264
  const scanned = matched.length > 0 ? matched : dirs;
21358
21265
  const found = [];
21359
21266
  for (const dir of scanned) {
21360
- const projectDir = NodePath5.join(transcriptDir, dir);
21267
+ const projectDir = NodePath6.join(transcriptDir, dir);
21361
21268
  let names;
21362
21269
  try {
21363
21270
  names = NodeFS3.readdirSync(projectDir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(TRANSCRIPT_SUFFIX)).map((entry) => entry.name);
@@ -21365,17 +21272,17 @@ function listTranscriptFiles(transcriptDir, directory) {
21365
21272
  continue;
21366
21273
  }
21367
21274
  for (const name of names) {
21368
- let stat2;
21275
+ let stat;
21369
21276
  try {
21370
- stat2 = NodeFS3.statSync(NodePath5.join(projectDir, name));
21277
+ stat = NodeFS3.statSync(NodePath6.join(projectDir, name));
21371
21278
  } catch {
21372
21279
  continue;
21373
21280
  }
21374
21281
  found.push({
21375
- path: NodePath5.join(projectDir, name),
21282
+ path: NodePath6.join(projectDir, name),
21376
21283
  sessionId: name.slice(0, -TRANSCRIPT_SUFFIX.length),
21377
- size: stat2.size,
21378
- mtimeMs: Number.isFinite(stat2.mtimeMs) ? stat2.mtimeMs : 0
21284
+ size: stat.size,
21285
+ mtimeMs: Number.isFinite(stat.mtimeMs) ? stat.mtimeMs : 0
21379
21286
  });
21380
21287
  }
21381
21288
  }
@@ -21383,7 +21290,7 @@ function listTranscriptFiles(transcriptDir, directory) {
21383
21290
  }
21384
21291
  function readClaudeLiveSessionIds(configDir) {
21385
21292
  const live = /* @__PURE__ */ new Set();
21386
- const markerDir = NodePath5.join(configDir, "sessions");
21293
+ const markerDir = NodePath6.join(configDir, "sessions");
21387
21294
  let entries;
21388
21295
  try {
21389
21296
  entries = NodeFS3.readdirSync(markerDir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) => entry.name);
@@ -21393,7 +21300,7 @@ function readClaudeLiveSessionIds(configDir) {
21393
21300
  for (const entry of entries) {
21394
21301
  let marker;
21395
21302
  try {
21396
- marker = JSON.parse(NodeFS3.readFileSync(NodePath5.join(markerDir, entry), "utf8"));
21303
+ marker = JSON.parse(NodeFS3.readFileSync(NodePath6.join(markerDir, entry), "utf8"));
21397
21304
  } catch {
21398
21305
  continue;
21399
21306
  }
@@ -21423,7 +21330,7 @@ function scanClaudeSessions(input) {
21423
21330
  }
21424
21331
  const files = [...listTranscriptFiles(input.transcriptDir, input.directory)].sort((left, right) => right.mtimeMs - left.mtimeMs).slice(0, MAX_SCANNED_FILES);
21425
21332
  const live = readClaudeLiveSessionIds(input.configDir);
21426
- const caseInsensitive = input.caseInsensitive ?? NodeOS5.type() === "Windows_NT";
21333
+ const caseInsensitive = input.caseInsensitive ?? NodeOS6.type() === "Windows_NT";
21427
21334
  const rows = [];
21428
21335
  for (const file of files) {
21429
21336
  if (rows.length >= MAX_ROWS) break;
@@ -21447,7 +21354,7 @@ function scanClaudeSessions(input) {
21447
21354
  function resolveTranscriptPath(input) {
21448
21355
  const fileName = `${input.sessionId}${TRANSCRIPT_SUFFIX}`;
21449
21356
  if (input.cwd !== void 0 && input.cwd.length > 0) {
21450
- const direct = NodePath5.join(
21357
+ const direct = NodePath6.join(
21451
21358
  input.transcriptDir,
21452
21359
  encodeClaudeProjectDirName(input.cwd),
21453
21360
  fileName
@@ -21464,7 +21371,7 @@ function resolveTranscriptPath(input) {
21464
21371
  return null;
21465
21372
  }
21466
21373
  for (const dir of dirs) {
21467
- const candidate = NodePath5.join(input.transcriptDir, dir, fileName);
21374
+ const candidate = NodePath6.join(input.transcriptDir, dir, fileName);
21468
21375
  try {
21469
21376
  if (NodeFS3.statSync(candidate).isFile()) return candidate;
21470
21377
  } catch {
@@ -21541,18 +21448,8 @@ function readClaudeSessionRecord(input) {
21541
21448
 
21542
21449
  // src/spawnEnvironment.ts
21543
21450
  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();
21451
+ const keys = /* @__PURE__ */ new Set([...Object.keys(previous.processEnv), ...Object.keys(next.processEnv)]);
21452
+ return [...keys].filter((key) => previous.processEnv[key] !== next.processEnv[key]).sort();
21556
21453
  }
21557
21454
 
21558
21455
  // src/usage.ts
@@ -21662,16 +21559,6 @@ function normalizeClaudeActiveTokenUsage(value, contextWindow, totalProcessedTok
21662
21559
  ...totalProcessedTokens !== void 0 ? { totalProcessedTokens } : {}
21663
21560
  });
21664
21561
  }
21665
- function normalizeClaudeContextUsageApiSnapshot(value, totalProcessedTokens) {
21666
- const autoCompactThreshold = finitePositiveInteger(value.autoCompactThreshold);
21667
- return makeClaudeTokenUsageSnapshot({
21668
- activeTokens: value.totalTokens,
21669
- contextWindow: value.maxTokens,
21670
- ...totalProcessedTokens !== void 0 ? { totalProcessedTokens } : {},
21671
- compactsAutomatically: value.isAutoCompactEnabled,
21672
- ...autoCompactThreshold !== void 0 ? { autoCompactThreshold } : {}
21673
- });
21674
- }
21675
21562
  function compactBoundaryTokenUsageSnapshot(message, contextWindow, totalProcessedTokens) {
21676
21563
  const metadata = message.compact_metadata;
21677
21564
  if (!metadata || typeof metadata !== "object" || Array.isArray(metadata)) {
@@ -21962,18 +21849,6 @@ function normalizeClaudeTaskProgressTokenUsage(value, memory) {
21962
21849
  }
21963
21850
 
21964
21851
  // 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
21852
  var toProbeCapabilities = (caps) => ({
21978
21853
  optionDescriptors: caps.optionDescriptors.map(
21979
21854
  (descriptor) => descriptor.type === "select" ? {
@@ -21996,17 +21871,14 @@ var toProbeCapabilities = (caps) => ({
21996
21871
  var claudeProbeModels = (input) => {
21997
21872
  const seen = /* @__PURE__ */ new Set();
21998
21873
  const out = [];
21999
- for (const model of input.builtIn) {
21874
+ for (const model of input.catalog) {
22000
21875
  if (seen.has(model.slug)) continue;
22001
21876
  seen.add(model.slug);
22002
21877
  const userLabel = input.customModels.find((entry) => entry.slug === model.slug)?.label;
22003
- const contextWindow = defaultContextWindowOf(model.capabilities);
22004
21878
  out.push({
22005
21879
  slug: model.slug,
22006
21880
  name: userLabel?.trim() || model.name,
22007
- ...model.isLegacy === void 0 ? {} : { isLegacy: model.isLegacy },
22008
- capabilities: toProbeCapabilities(model.capabilities),
22009
- ...contextWindow === void 0 ? {} : { contextWindow }
21881
+ capabilities: toProbeCapabilities(model.capabilities)
22010
21882
  });
22011
21883
  }
22012
21884
  for (const model of input.customModels) {
@@ -22014,251 +21886,14 @@ var claudeProbeModels = (input) => {
22014
21886
  seen.add(model.slug);
22015
21887
  out.push({ slug: model.slug, name: model.label?.trim() || model.slug });
22016
21888
  }
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
21889
  return out;
22023
21890
  };
22024
21891
 
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
21892
  // src/textGeneration.ts
22261
- import { mergeLaunchEnv, brainSlotEnvKeys as brainSlotEnvKeys2 } from "@ycodium-ai/plugin-api/executor";
21893
+ import * as NodeFSP2 from "node:fs/promises";
21894
+ import * as NodeOS7 from "node:os";
21895
+ import * as NodePath7 from "node:path";
21896
+ import { mergeLaunchEnv } from "@ycodium-ai/plugin-api/executor";
22262
21897
 
22263
21898
  // src/textGenerationUtils.ts
22264
21899
  function limitSection(value, maxChars) {
@@ -22607,7 +22242,7 @@ function buildHandoverSummaryPrompt(input) {
22607
22242
 
22608
22243
  // src/textGeneration.ts
22609
22244
  var CLAUDE_TIMEOUT_MS = 18e4;
22610
- function isRecord6(value) {
22245
+ function isRecord5(value) {
22611
22246
  return typeof value === "object" && value !== null && !Array.isArray(value);
22612
22247
  }
22613
22248
  function readString3(value) {
@@ -22629,46 +22264,36 @@ function decodeStructuredOutput(stdout, operation) {
22629
22264
  cause: error
22630
22265
  });
22631
22266
  }
22632
- if (!isRecord6(parsed) || !("structured_output" in parsed)) {
22267
+ const envelope = Array.isArray(parsed) ? parsed.findLast((message) => isRecord5(message) && message["type"] === "result") : parsed;
22268
+ if (!isRecord5(envelope) || !("structured_output" in envelope)) {
22633
22269
  throw new Error(`${operation}: Claude CLI returned unexpected output format.`);
22634
22270
  }
22635
- return parsed.structured_output;
22271
+ return envelope["structured_output"];
22636
22272
  }
22637
- function createClaudeTextGeneration(recipe, host, pluginDataDirPath) {
22273
+ function createClaudeTextGeneration(recipe, host, modelCatalog) {
22638
22274
  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
22275
  const processEnv = mergeLaunchEnv({
22651
- brainSlots: brainSlotEnvKeys2(CLAUDE_CARD),
22652
22276
  instanceTable: recipe.environment,
22653
- handOverrides: claudeHomeVariables(homeLayout),
22654
- launch: input.launch?.processEnv
22277
+ handOverrides: claudeHomeVariables(resolveClaudeHomeLayout(recipe))
22655
22278
  });
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);
22279
+ const modelSelection = isRecord5(input.modelSelection) ? input.modelSelection : {};
22280
+ const requestedModel = modelIdFromSelection(modelSelection) ?? "claude-haiku-4-5";
22281
+ const caps = claudeModelCapabilities(requestedModel, modelCatalog(), recipe.customModels);
22659
22282
  const rawEffort = getModelSelectionStringOptionValue(modelSelection, "effort");
22660
22283
  const resolvedEffort = resolveClaudeEffort(caps, rawEffort);
22661
- const cliEffort = normalizeClaudeCliEffort(resolvedEffort, requestedModel, caps);
22284
+ const cliEffort = normalizeClaudeCliEffort(resolvedEffort);
22662
22285
  const ultracode = isClaudeUltracodeEffort(resolvedEffort);
22663
22286
  const thinking = getModelSelectionBooleanOptionValue(modelSelection, "thinking");
22664
22287
  const fastMode = getModelSelectionBooleanOptionValue(modelSelection, "fastMode");
22665
22288
  const settings = {
22289
+ // 元数据生成只需要传进去的 prompt:不跑用户配的 hook。
22290
+ disableAllHooks: true,
22666
22291
  ...typeof thinking === "boolean" ? { alwaysThinkingEnabled: thinking } : {},
22667
22292
  ...fastMode ? { fastMode: true } : {},
22668
22293
  ...ultracode ? { ultracode: true } : {}
22669
22294
  };
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);
22295
+ const settingsJson = JSON.stringify(settings);
22296
+ const apiModelId = typeof modelSelection["model"] === "string" ? modelSelection["model"] : requestedModel;
22672
22297
  const args = [
22673
22298
  "-p",
22674
22299
  "--output-format",
@@ -22678,44 +22303,60 @@ function createClaudeTextGeneration(recipe, host, pluginDataDirPath) {
22678
22303
  "--model",
22679
22304
  apiModelId,
22680
22305
  ...cliEffort ? ["--effort", cliEffort] : [],
22681
- ...settingsJson ? ["--settings", settingsJson] : [],
22682
- "--dangerously-skip-permissions"
22306
+ "--settings",
22307
+ settingsJson,
22308
+ // 标题、提交信息这类只要文本:即使 prompt 里出现技能名,也不给任何可执行能力。
22309
+ "--tools",
22310
+ "",
22311
+ "--disable-slash-commands",
22312
+ "--strict-mcp-config"
22683
22313
  ];
22684
22314
  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");
22315
+ const runIn = async (cwd) => {
22316
+ const spawned = await host.process.spawn({
22317
+ program,
22318
+ args,
22319
+ cwd,
22320
+ env: processEnv,
22321
+ owner: { bindingId: host.bindingId }
22322
+ });
22323
+ const timeout = AbortSignal.timeout(CLAUDE_TIMEOUT_MS);
22324
+ const signal = input.signal ? AbortSignal.any([input.signal, timeout]) : timeout;
22325
+ const abort = () => {
22326
+ void spawned.kill("SIGTERM");
22327
+ };
22328
+ if (signal.aborted) abort();
22329
+ else signal.addEventListener("abort", abort, { once: true });
22330
+ await spawned.writeStdin(input.prompt);
22331
+ await spawned.closeStdin();
22332
+ const [stdout, stderr, exitCode] = await Promise.all([
22333
+ collectText(spawned.stdout),
22334
+ collectText(spawned.stderr),
22335
+ spawned.exited
22336
+ ]);
22337
+ if (signal.aborted && !input.signal?.aborted) {
22338
+ throw new Error(`${input.operation}: Claude CLI request timed out.`);
22339
+ }
22340
+ if (exitCode !== 0) {
22341
+ const detail = stderr.trim() || stdout.trim();
22342
+ throw new Error(
22343
+ detail.length > 0 ? `Claude CLI command failed: ${detail}` : `Claude CLI command failed with code ${exitCode}.`
22344
+ );
22345
+ }
22346
+ return decodeStructuredOutput(stdout, input.operation);
22696
22347
  };
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
- );
22348
+ if (input.operation !== "generateThreadTitle") return runIn(input.cwd);
22349
+ const scratchDirectory = await NodeFSP2.mkdtemp(
22350
+ NodePath7.join(NodeOS7.tmpdir(), "ycodium-claude-title-")
22351
+ );
22352
+ try {
22353
+ return await runIn(scratchDirectory);
22354
+ } finally {
22355
+ await NodeFSP2.rm(scratchDirectory, { recursive: true, force: true }).catch(() => void 0);
22714
22356
  }
22715
- return decodeStructuredOutput(stdout, input.operation);
22716
22357
  };
22717
22358
  const asRecord = (value, operation) => {
22718
- if (!isRecord6(value))
22359
+ if (!isRecord5(value))
22719
22360
  throw new Error(`${operation}: Claude returned invalid structured output.`);
22720
22361
  return value;
22721
22362
  };
@@ -22729,7 +22370,7 @@ function createClaudeTextGeneration(recipe, host, pluginDataDirPath) {
22729
22370
  stagedSummary: readString3(input["stagedSummary"]),
22730
22371
  stagedPatch: readString3(input["stagedPatch"]),
22731
22372
  includeBranch,
22732
- ...isRecord6(input["policy"]) ? { policy: input["policy"] } : {}
22373
+ ...isRecord5(input["policy"]) ? { policy: input["policy"] } : {}
22733
22374
  });
22734
22375
  const generated = asRecord(
22735
22376
  await runClaudeJson({
@@ -22738,7 +22379,6 @@ function createClaudeTextGeneration(recipe, host, pluginDataDirPath) {
22738
22379
  prompt,
22739
22380
  outputSchema,
22740
22381
  modelSelection: input["modelSelection"],
22741
- ...isRecord6(input["launch"]) ? { launch: input["launch"] } : {},
22742
22382
  ...signal === void 0 ? {} : { signal }
22743
22383
  }),
22744
22384
  "generateCommitMessage"
@@ -22762,7 +22402,7 @@ function createClaudeTextGeneration(recipe, host, pluginDataDirPath) {
22762
22402
  diffSummary: readString3(input["diffSummary"]),
22763
22403
  diffPatch: readString3(input["diffPatch"]),
22764
22404
  ...typeof input["changeRequestTemplate"] === "string" ? { changeRequestTemplate: input["changeRequestTemplate"] } : {},
22765
- ...isRecord6(input["policy"]) ? { policy: input["policy"] } : {}
22405
+ ...isRecord5(input["policy"]) ? { policy: input["policy"] } : {}
22766
22406
  });
22767
22407
  const generated = asRecord(
22768
22408
  await runClaudeJson({
@@ -22771,7 +22411,6 @@ function createClaudeTextGeneration(recipe, host, pluginDataDirPath) {
22771
22411
  prompt,
22772
22412
  outputSchema,
22773
22413
  modelSelection: input["modelSelection"],
22774
- ...isRecord6(input["launch"]) ? { launch: input["launch"] } : {},
22775
22414
  ...signal === void 0 ? {} : { signal }
22776
22415
  }),
22777
22416
  "generatePrContent"
@@ -22790,7 +22429,7 @@ function createClaudeTextGeneration(recipe, host, pluginDataDirPath) {
22790
22429
  const { prompt, outputSchema } = buildBranchNamePrompt({
22791
22430
  message: readString3(input["message"]),
22792
22431
  ...Array.isArray(input["attachments"]) ? { attachments: input["attachments"] } : {},
22793
- ...isRecord6(input["policy"]) ? { policy: input["policy"] } : {}
22432
+ ...isRecord5(input["policy"]) ? { policy: input["policy"] } : {}
22794
22433
  });
22795
22434
  const generated = asRecord(
22796
22435
  await runClaudeJson({
@@ -22799,7 +22438,6 @@ function createClaudeTextGeneration(recipe, host, pluginDataDirPath) {
22799
22438
  prompt,
22800
22439
  outputSchema,
22801
22440
  modelSelection: input["modelSelection"],
22802
- ...isRecord6(input["launch"]) ? { launch: input["launch"] } : {},
22803
22441
  ...signal === void 0 ? {} : { signal }
22804
22442
  }),
22805
22443
  "generateBranchName"
@@ -22816,7 +22454,7 @@ function createClaudeTextGeneration(recipe, host, pluginDataDirPath) {
22816
22454
  message: readString3(input["message"]),
22817
22455
  ...typeof input["previousTitle"] === "string" ? { previousTitle: input["previousTitle"] } : {},
22818
22456
  ...Array.isArray(input["attachments"]) ? { attachments: input["attachments"] } : {},
22819
- ...isRecord6(input["policy"]) ? { policy: input["policy"] } : {}
22457
+ ...isRecord5(input["policy"]) ? { policy: input["policy"] } : {}
22820
22458
  });
22821
22459
  const generated = asRecord(
22822
22460
  await runClaudeJson({
@@ -22825,7 +22463,6 @@ function createClaudeTextGeneration(recipe, host, pluginDataDirPath) {
22825
22463
  prompt,
22826
22464
  outputSchema,
22827
22465
  modelSelection: input["modelSelection"],
22828
- ...isRecord6(input["launch"]) ? { launch: input["launch"] } : {},
22829
22466
  ...signal === void 0 ? {} : { signal }
22830
22467
  }),
22831
22468
  "generateThreadTitle"
@@ -22848,7 +22485,6 @@ function createClaudeTextGeneration(recipe, host, pluginDataDirPath) {
22848
22485
  prompt,
22849
22486
  outputSchema,
22850
22487
  modelSelection: input["modelSelection"],
22851
- ...isRecord6(input["launch"]) ? { launch: input["launch"] } : {},
22852
22488
  ...signal === void 0 ? {} : { signal }
22853
22489
  }),
22854
22490
  "summarizeHandover"
@@ -22941,9 +22577,9 @@ var PromptQueue = class {
22941
22577
  const queued = this.#queued.shift();
22942
22578
  if (queued !== void 0) return Promise.resolve(queued);
22943
22579
  if (this.#closed) return Promise.resolve(void 0);
22944
- return new Promise((resolve3) => {
22580
+ return new Promise((resolve2) => {
22945
22581
  this.#waiters.push((result) => {
22946
- resolve3(result.done ? void 0 : result.value);
22582
+ resolve2(result.done ? void 0 : result.value);
22947
22583
  });
22948
22584
  });
22949
22585
  }
@@ -23024,9 +22660,6 @@ function nativeProviderRefs(_context, options) {
23024
22660
  }
23025
22661
  async function buildUserMessageFromTurn(input) {
23026
22662
  const sdkContent = [];
23027
- if (input.text.length > 0) {
23028
- sdkContent.push({ type: "text", text: input.text });
23029
- }
23030
22663
  for (const attachment of input.attachments ?? []) {
23031
22664
  if (!attachment || typeof attachment !== "object" || Array.isArray(attachment)) continue;
23032
22665
  const rec = attachment;
@@ -23045,6 +22678,9 @@ async function buildUserMessageFromTurn(input) {
23045
22678
  const bytes = await input.attachmentsApi.read(id2);
23046
22679
  sdkContent.push(buildClaudeImageContentBlock({ mimeType, bytes }));
23047
22680
  }
22681
+ if (input.text.length > 0) {
22682
+ sdkContent.push({ type: "text", text: input.text });
22683
+ }
23048
22684
  return buildUserMessage({ sdkContent });
23049
22685
  }
23050
22686
  async function persistProducedImage(input) {
@@ -23098,6 +22734,48 @@ function createClaudeAdapter(recipe, host, options) {
23098
22734
  prompt: input.prompt,
23099
22735
  options: input.options
23100
22736
  }));
22737
+ const buildLaunchEnvironment = () => mergeLaunchEnv2({
22738
+ inherited: { ...process.env, ...options?.environment },
22739
+ instanceTable: recipe.environment,
22740
+ handOverrides: claudeHomeVariables(resolveClaudeHomeLayout(recipe))
22741
+ });
22742
+ let modelCatalog;
22743
+ let modelCatalogRead;
22744
+ let modelCatalogFailedAt;
22745
+ const currentModelCatalog = () => modelCatalog?.models ?? [];
22746
+ const refreshModelCatalog = (input) => {
22747
+ const fresh = modelCatalog !== void 0 && Date.now() - modelCatalog.readAt < CLAUDE_SDK_MODELS_TTL_MS;
22748
+ if (fresh && input?.force !== true) return Promise.resolve();
22749
+ if (input?.force !== true && modelCatalogFailedAt !== void 0 && Date.now() - modelCatalogFailedAt < CLAUDE_SDK_MODELS_RETRY_MS) {
22750
+ return Promise.resolve();
22751
+ }
22752
+ if (modelCatalogRead !== void 0) return modelCatalogRead;
22753
+ const read = (async () => {
22754
+ const extraArgs = parseCliArgs(recipe.launchArgs).flags;
22755
+ const models = await readClaudeSdkModels({
22756
+ createQuery,
22757
+ signal: input?.signal,
22758
+ options: {
22759
+ cwd: claudeSdkModelsCwd(),
22760
+ pathToClaudeCodeExecutable: claudeSdkExecutablePath,
22761
+ settingSources: [...CLAUDE_SETTING_SOURCES],
22762
+ env: compactEnv(buildLaunchEnvironment()),
22763
+ ...Object.keys(extraArgs).length > 0 ? { extraArgs } : {}
22764
+ }
22765
+ });
22766
+ if (models === void 0) {
22767
+ modelCatalogFailedAt = Date.now();
22768
+ } else {
22769
+ modelCatalogFailedAt = void 0;
22770
+ modelCatalog = { models: claudeCatalogFromSdk(models), readAt: Date.now() };
22771
+ }
22772
+ })().finally(() => {
22773
+ modelCatalogRead = void 0;
22774
+ });
22775
+ modelCatalogRead = read;
22776
+ return read;
22777
+ };
22778
+ const declaredModels = [CLAUDE_CARD.defaultModel, CLAUDE_CARD.defaultTextGenerationModel];
23101
22779
  const sessions = /* @__PURE__ */ new Map();
23102
22780
  const runtimeEventQueue = new EventBus();
23103
22781
  const nowIso = () => (/* @__PURE__ */ new Date()).toISOString();
@@ -23116,10 +22794,7 @@ function createClaudeAdapter(recipe, host, options) {
23116
22794
  }
23117
22795
  return {
23118
22796
  threadId,
23119
- turns: context.turns.map((turn) => ({
23120
- id: turn.id,
23121
- items: [...turn.items]
23122
- }))
22797
+ turns: context.turns.map((turn) => ({ id: turn.id, items: [] }))
23123
22798
  };
23124
22799
  };
23125
22800
  const updateResumeCursor = async (context) => {
@@ -23373,27 +23048,6 @@ function createClaudeAdapter(recipe, host, options) {
23373
23048
  } : {}
23374
23049
  });
23375
23050
  };
23376
- const queryCurrentContextUsage = async (context, totalProcessedTokens) => {
23377
- if (!context.query.getContextUsage) {
23378
- return void 0;
23379
- }
23380
- let usage;
23381
- try {
23382
- usage = await Promise.race([
23383
- context.query.getContextUsage(),
23384
- new Promise((resolve3) => {
23385
- setTimeout(() => resolve3(void 0), 1e3);
23386
- })
23387
- ]);
23388
- } catch {
23389
- usage = void 0;
23390
- }
23391
- if (!usage) {
23392
- return void 0;
23393
- }
23394
- context.lastKnownContextWindow = usage.maxTokens;
23395
- return normalizeClaudeContextUsageApiSnapshot(usage, totalProcessedTokens);
23396
- };
23397
23051
  const emitProposedPlanCompleted = async (context, input) => {
23398
23052
  const turnState = context.turnState;
23399
23053
  const planMarkdown = input.planMarkdown.trim();
@@ -23466,10 +23120,11 @@ function createClaudeAdapter(recipe, host, options) {
23466
23120
  turnId,
23467
23121
  startedAt,
23468
23122
  synthetic: true,
23469
- items: [],
23470
23123
  assistantTextBlocks: /* @__PURE__ */ new Map(),
23471
23124
  assistantTextBlockOrder: [],
23472
23125
  capturedProposedPlanKeys: /* @__PURE__ */ new Set(),
23126
+ latestAssistantUsage: void 0,
23127
+ compactedSinceLatestAssistantUsage: false,
23473
23128
  retrying: false,
23474
23129
  nextSyntheticAssistantBlockIndex: -1
23475
23130
  };
@@ -23509,10 +23164,6 @@ function createClaudeAdapter(recipe, host, options) {
23509
23164
  if (accumulatedTotalProcessedTokens !== void 0) {
23510
23165
  context.lastKnownTotalProcessedTokens = accumulatedTotalProcessedTokens;
23511
23166
  }
23512
- const contextUsageSnapshot = await queryCurrentContextUsage(
23513
- context,
23514
- accumulatedTotalProcessedTokens ?? context.lastKnownTotalProcessedTokens
23515
- );
23516
23167
  const resultUsageRecord = result?.usage && typeof result.usage === "object" && !Array.isArray(result.usage) ? result.usage : void 0;
23517
23168
  const hasResultUsageIteration = resultUsageRecord !== void 0 && lastClaudeUsageIteration(resultUsageRecord) !== void 0;
23518
23169
  const resultHasActiveUsage = resultUsageRecord !== void 0 && (hasResultUsageIteration || claudeUsageInputTokens(resultUsageRecord) + claudeUsageOutputTokens(resultUsageRecord) > 0);
@@ -23522,8 +23173,14 @@ function createClaudeAdapter(recipe, host, options) {
23522
23173
  maxTokens,
23523
23174
  accumulatedTotalProcessedTokens ?? context.lastKnownTotalProcessedTokens
23524
23175
  ) : void 0;
23176
+ const latestAssistantSnapshot = normalizeClaudeActiveTokenUsage(
23177
+ context.turnState?.latestAssistantUsage,
23178
+ maxTokens,
23179
+ accumulatedTotalProcessedTokens ?? context.lastKnownTotalProcessedTokens
23180
+ );
23525
23181
  const lastGoodUsage = context.lastKnownTokenUsage;
23526
- const usageSnapshot = contextUsageSnapshot ?? (resultTotalOnly && lastGoodUsage ? {
23182
+ const usageSnapshot = latestAssistantSnapshot ?? // 压缩之后还没有新的 assistant 用量时,result 里的累计数反映的是压缩前的上下文,不能用。
23183
+ (context.turnState?.compactedSinceLatestAssistantUsage ? void 0 : resultTotalOnly && lastGoodUsage ? {
23527
23184
  ...lastGoodUsage,
23528
23185
  ...typeof maxTokens === "number" && Number.isFinite(maxTokens) && maxTokens > 0 ? { maxTokens } : {},
23529
23186
  ...typeof accumulatedTotalProcessedTokens === "number" && Number.isFinite(accumulatedTotalProcessedTokens) && accumulatedTotalProcessedTokens > lastGoodUsage.usedTokens ? {
@@ -23590,10 +23247,7 @@ function createClaudeAdapter(recipe, host, options) {
23590
23247
  rawPayload: result ?? { status }
23591
23248
  });
23592
23249
  }
23593
- context.turns.push({
23594
- id: turnState.turnId,
23595
- items: [...turnState.items]
23596
- });
23250
+ context.turns.push({ id: turnState.turnId });
23597
23251
  await emitThreadTokenUsage(context, usageSnapshot, {
23598
23252
  rawMethod: "claude/result",
23599
23253
  rawPayload: result ?? { status }
@@ -23891,9 +23545,6 @@ function createClaudeAdapter(recipe, host, options) {
23891
23545
  if (message.type !== "user") {
23892
23546
  return;
23893
23547
  }
23894
- if (context.turnState) {
23895
- context.turnState.items.push(message.message);
23896
- }
23897
23548
  for (const toolResult of toolResultBlocksFromUserMessage(message)) {
23898
23549
  const toolEntry = Array.from(context.inFlightTools.entries()).find(
23899
23550
  ([, tool2]) => tool2.itemId === toolResult.toolUseId
@@ -24112,7 +23763,14 @@ function createClaudeAdapter(recipe, host, options) {
24112
23763
  }
24113
23764
  }
24114
23765
  if (context.turnState) {
24115
- context.turnState.items.push(message.message);
23766
+ if (normalizeClaudeActiveTokenUsage(
23767
+ message.message.usage,
23768
+ context.lastKnownContextWindow,
23769
+ context.lastKnownTotalProcessedTokens
23770
+ )) {
23771
+ context.turnState.latestAssistantUsage = message.message.usage;
23772
+ context.turnState.compactedSinceLatestAssistantUsage = false;
23773
+ }
24116
23774
  await backfillAssistantTextBlocksFromSnapshot(context, message);
24117
23775
  }
24118
23776
  context.lastAssistantUuid = message.uuid;
@@ -24258,6 +23916,10 @@ function createClaudeAdapter(recipe, host, options) {
24258
23916
  return;
24259
23917
  }
24260
23918
  case "compact_boundary": {
23919
+ if (context.turnState) {
23920
+ context.turnState.latestAssistantUsage = void 0;
23921
+ context.turnState.compactedSinceLatestAssistantUsage = true;
23922
+ }
24261
23923
  await emitThreadTokenUsage(
24262
23924
  context,
24263
23925
  compactBoundaryTokenUsageSnapshot(
@@ -24523,13 +24185,19 @@ function createClaudeAdapter(recipe, host, options) {
24523
24185
  return;
24524
24186
  // 目前无 Ycodium 表面的内部协议/UX 细节 — 主动消费
24525
24187
  // deliberately so they don't masquerade as unknown-subtype warnings.
24526
- case "model_refusal_fallback":
24527
24188
  case "local_command_output":
24528
24189
  case "plugin_install":
24529
24190
  case "commands_changed":
24530
24191
  case "memory_recall":
24531
24192
  case "elicitation_complete":
24532
24193
  return;
24194
+ case "model_refusal_fallback":
24195
+ await emitRuntimeWarning(
24196
+ context,
24197
+ message.content.length > 0 ? message.content : `Claude switched from ${message.original_model} to ${message.fallback_model} after a safety refusal.`,
24198
+ message
24199
+ );
24200
+ return;
24533
24201
  case "permission_denied":
24534
24202
  offerRuntimeEvent(context, {
24535
24203
  ...base,
@@ -24762,7 +24430,7 @@ function createClaudeAdapter(recipe, host, options) {
24762
24430
  if (streamFiber) {
24763
24431
  await Promise.race([
24764
24432
  streamFiber.catch(() => void 0),
24765
- new Promise((resolve3) => setTimeout(resolve3, 1e3))
24433
+ new Promise((resolve2) => setTimeout(resolve2, 1e3))
24766
24434
  ]);
24767
24435
  }
24768
24436
  const updatedAt = nowIso();
@@ -24845,9 +24513,6 @@ function createClaudeAdapter(recipe, host, options) {
24845
24513
  }
24846
24514
  context.currentSettings = desired.settings;
24847
24515
  }
24848
- if (desired.contextWindow !== void 0) {
24849
- context.lastKnownContextWindow = desired.contextWindow;
24850
- }
24851
24516
  const updatedAt = nowIso();
24852
24517
  context.session = {
24853
24518
  ...context.session,
@@ -24877,16 +24542,20 @@ function createClaudeAdapter(recipe, host, options) {
24877
24542
  const startSession = async (input) => {
24878
24543
  const extraArgs = parseCliArgs(recipe.launchArgs).flags;
24879
24544
  const modelSelection = boundModelSelection(input.modelSelection, boundInstanceId);
24880
- const caps = claudeModelCapabilities(modelSelection?.model, recipe.customModels);
24545
+ await modelCatalogRead;
24546
+ const caps = claudeModelCapabilities(
24547
+ modelSelection?.model,
24548
+ currentModelCatalog(),
24549
+ recipe.customModels
24550
+ );
24881
24551
  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
- })
24552
+ const apiModelId = modelSelection?.model ? claudeCliModelId({
24553
+ requestedModelId: modelSelection.model,
24554
+ placeholder: CLAUDE_CARD.defaultModel,
24555
+ catalog: currentModelCatalog(),
24556
+ declared: declaredModels,
24557
+ customModels: recipe.customModels
24888
24558
  }) : void 0;
24889
- const initialContextWindow = selectedClaudeContextWindow(caps, modelSelection);
24890
24559
  const rawEffort = getModelSelectionStringOptionValue(modelSelection, "effort");
24891
24560
  const effort = resolveClaudeEffort(caps, rawEffort) ?? null;
24892
24561
  const fastModeSupported = descriptors.some(
@@ -24898,7 +24567,7 @@ function createClaudeAdapter(recipe, host, options) {
24898
24567
  const fastMode = getModelSelectionBooleanOptionValue(modelSelection, "fastMode") === true && fastModeSupported;
24899
24568
  const thinking = thinkingSupported ? getModelSelectionBooleanOptionValue(modelSelection, "thinking") : void 0;
24900
24569
  const ultracode = isClaudeUltracodeEffort(effort);
24901
- const effectiveEffort = getEffectiveClaudeAgentEffort(effort, modelSelection?.model, caps);
24570
+ const effectiveEffort = getEffectiveClaudeAgentEffort(effort);
24902
24571
  const permissionMode = claudePermissionModeFor(input.runtimeMode, input.nativeModeId);
24903
24572
  const settings = buildClaudeSessionSettings({
24904
24573
  thinking,
@@ -24906,28 +24575,8 @@ function createClaudeAdapter(recipe, host, options) {
24906
24575
  ultracode,
24907
24576
  autoCompactWindow: recipe.autoCompactWindow
24908
24577
  });
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
24578
  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
- })
24579
+ processEnv: compactEnv(buildLaunchEnvironment())
24931
24580
  };
24932
24581
  const existingContext = sessions.get(input.threadId);
24933
24582
  if (existingContext) {
@@ -24935,7 +24584,6 @@ function createClaudeAdapter(recipe, host, options) {
24935
24584
  permissionMode,
24936
24585
  ...apiModelId ? { apiModelId } : {},
24937
24586
  ...effectiveEffort ? { effectiveEffort } : {},
24938
- ...initialContextWindow !== void 0 ? { contextWindow: initialContextWindow } : {},
24939
24587
  ...modelSelection?.model ? { model: modelSelection.model } : {},
24940
24588
  settings,
24941
24589
  spawnEnvironment
@@ -24971,7 +24619,7 @@ function createClaudeAdapter(recipe, host, options) {
24971
24619
  return await Promise.race([
24972
24620
  contextDeferred.promise,
24973
24621
  new Promise(
24974
- (resolve3) => setTimeout(() => resolve3(void 0), CLAUDE_SESSION_CONTEXT_WAIT_MS)
24622
+ (resolve2) => setTimeout(() => resolve2(void 0), CLAUDE_SESSION_CONTEXT_WAIT_MS)
24975
24623
  )
24976
24624
  ]);
24977
24625
  } catch {
@@ -25255,11 +24903,11 @@ function createClaudeAdapter(recipe, host, options) {
25255
24903
  const onUserDialog = (request, callbackOptions) => handleResumeDialog(request, callbackOptions);
25256
24904
  const additionalDirectories = input.cwd ? [input.cwd] : [];
25257
24905
  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 } };
24906
+ const querySettings = { ...settings };
24907
+ const readAllowRules = claudeReadAllowRules(input.readOnlyDirectories);
24908
+ if (readAllowRules.length > 0) {
24909
+ querySettings.permissions = { allow: [...readAllowRules] };
24910
+ }
25263
24911
  const stderrTail = createOutputTail(CLAUDE_STDERR_TAIL_CHARS);
25264
24912
  const queryOptions = {
25265
24913
  ...input.cwd ? { cwd: input.cwd } : {},
@@ -25340,6 +24988,8 @@ function createClaudeAdapter(recipe, host, options) {
25340
24988
  basePermissionMode: permissionMode,
25341
24989
  currentPermissionMode: permissionMode,
25342
24990
  currentApiModelId: apiModelId,
24991
+ // 上下文长度只信 SDK 结果里报的那个数,起步时不猜。
24992
+ lastKnownContextWindow: void 0,
25343
24993
  currentEffort: effectiveEffort ?? void 0,
25344
24994
  launchEffort: effectiveEffort ?? void 0,
25345
24995
  spawnEnvironment,
@@ -25360,7 +25010,6 @@ function createClaudeAdapter(recipe, host, options) {
25360
25010
  compaction: void 0,
25361
25011
  compactionInProgress: false,
25362
25012
  pendingManualCompaction: false,
25363
- lastKnownContextWindow: initialContextWindow,
25364
25013
  lastKnownTokenUsage: void 0,
25365
25014
  lastKnownTotalProcessedTokens: void 0,
25366
25015
  lastAssistantUuid: resumeState?.resumeSessionAt,
@@ -25443,13 +25092,17 @@ function createClaudeAdapter(recipe, host, options) {
25443
25092
  await completeTurn(context, "completed");
25444
25093
  }
25445
25094
  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
- })
25095
+ const turnCaps = claudeModelCapabilities(
25096
+ modelSelection.model,
25097
+ currentModelCatalog(),
25098
+ recipe.customModels
25099
+ );
25100
+ const apiModelId = claudeCliModelId({
25101
+ requestedModelId: modelSelection.model,
25102
+ placeholder: CLAUDE_CARD.defaultModel,
25103
+ catalog: currentModelCatalog(),
25104
+ declared: declaredModels,
25105
+ customModels: recipe.customModels
25453
25106
  });
25454
25107
  if (context.currentApiModelId !== apiModelId) {
25455
25108
  try {
@@ -25467,7 +25120,7 @@ function createClaudeAdapter(recipe, host, options) {
25467
25120
  turnCaps,
25468
25121
  getModelSelectionStringOptionValue(modelSelection, "effort")
25469
25122
  );
25470
- context.currentEffort = getEffectiveClaudeAgentEffort(turnEffort ?? null, modelSelection.model, turnCaps) ?? void 0;
25123
+ context.currentEffort = getEffectiveClaudeAgentEffort(turnEffort ?? null) ?? void 0;
25471
25124
  }
25472
25125
  if (input.interactionMode === "plan") {
25473
25126
  try {
@@ -25484,16 +25137,21 @@ function createClaudeAdapter(recipe, host, options) {
25484
25137
  }
25485
25138
  context.currentPermissionMode = context.basePermissionMode;
25486
25139
  }
25487
- const turnCapsForPrompt = claudeModelCapabilities(modelSelection?.model, recipe.customModels);
25140
+ const turnCapsForPrompt = claudeModelCapabilities(
25141
+ modelSelection?.model,
25142
+ currentModelCatalog(),
25143
+ recipe.customModels
25144
+ );
25488
25145
  const turnId = steeringTurnState?.turnId ?? randomUUIDv4();
25489
25146
  if (steeringTurnState === null) {
25490
25147
  const turnState = {
25491
25148
  turnId,
25492
25149
  startedAt: nowIso(),
25493
- items: [],
25494
25150
  assistantTextBlocks: /* @__PURE__ */ new Map(),
25495
25151
  assistantTextBlockOrder: [],
25496
25152
  capturedProposedPlanKeys: /* @__PURE__ */ new Set(),
25153
+ latestAssistantUsage: void 0,
25154
+ compactedSinceLatestAssistantUsage: false,
25497
25155
  retrying: false,
25498
25156
  nextSyntheticAssistantBlockIndex: -1
25499
25157
  };
@@ -25548,7 +25206,7 @@ function createClaudeAdapter(recipe, host, options) {
25548
25206
  context.query.interrupt().then(() => settled.promise).then(() => {
25549
25207
  acknowledged = true;
25550
25208
  }),
25551
- new Promise((resolve3) => setTimeout(resolve3, CLAUDE_INTERRUPT_GRACE_MS))
25209
+ new Promise((resolve2) => setTimeout(resolve2, CLAUDE_INTERRUPT_GRACE_MS))
25552
25210
  ]);
25553
25211
  } catch (cause) {
25554
25212
  throw toRequestError(threadId, "turn/interrupt", cause);
@@ -25648,6 +25306,8 @@ function createClaudeAdapter(recipe, host, options) {
25648
25306
  await stopSessionInternal(context, { emitExitEvent: true });
25649
25307
  };
25650
25308
  return {
25309
+ /** 型号目录:探测用它读并拿当前这份;会话内部自己走同一份。 */
25310
+ modelCatalog: { refresh: refreshModelCatalog, current: currentModelCatalog },
25651
25311
  startSession,
25652
25312
  sendTurn,
25653
25313
  interruptTurn,
@@ -25674,17 +25334,14 @@ function createClaudeAdapter(recipe, host, options) {
25674
25334
  };
25675
25335
  }
25676
25336
  function claudeAdapterBaseEnvironment(recipe) {
25677
- return {
25678
- ...process.env,
25679
- ...withoutBrainSlots(recipe.environment, brainSlotEnvKeys3(CLAUDE_CARD))
25680
- };
25337
+ return { ...process.env, ...recipe.environment };
25681
25338
  }
25682
- function createClaudeInstance(config, host, attachments, pluginDataDirPath) {
25339
+ function createClaudeInstance(config, host, attachments, overrides) {
25683
25340
  const recipe = parseClaudeInstanceConfig(config);
25684
25341
  const adapter = createClaudeAdapter(recipe, host, {
25685
25342
  instanceId: host.instanceId,
25686
25343
  ...attachments === void 0 ? {} : { attachments },
25687
- ...pluginDataDirPath === void 0 ? {} : { pluginDataDirPath },
25344
+ ...overrides?.createQuery === void 0 ? {} : { createQuery: overrides.createQuery },
25688
25345
  environment: claudeAdapterBaseEnvironment(recipe),
25689
25346
  log: host.log
25690
25347
  });
@@ -25716,6 +25373,7 @@ function createClaudeInstance(config, host, attachments, pluginDataDirPath) {
25716
25373
  return { status: "unknown" };
25717
25374
  }
25718
25375
  };
25376
+ let lastProbedVersion;
25719
25377
  const probe = async (signal) => {
25720
25378
  if (recipe.binaryPath.length === 0) {
25721
25379
  return {
@@ -25741,11 +25399,18 @@ function createClaudeInstance(config, host, attachments, pluginDataDirPath) {
25741
25399
  ]);
25742
25400
  const version = parseGenericCliVersion(`${stdout}
25743
25401
  ${stderr}`);
25402
+ if (version !== null || exitCode === 0) {
25403
+ await adapter.modelCatalog.refresh({ signal, force: version !== lastProbedVersion });
25404
+ lastProbedVersion = version;
25405
+ }
25744
25406
  const models = claudeProbeModels({
25745
- builtIn: getBuiltInClaudeModelsForVersion(version),
25407
+ catalog: adapter.modelCatalog.current(),
25746
25408
  customModels: recipe.customModels
25747
25409
  });
25748
25410
  const auth = probeAuth();
25411
+ const skills = await discoverClaudeSkills(
25412
+ resolveClaudeConfigDirPath(recipe.homePath, { ...process.env, ...recipe.environment })
25413
+ );
25749
25414
  if (exitCode !== 0 && version === null) {
25750
25415
  const detail = (stderr || stdout || "claude --version failed").trim();
25751
25416
  return {
@@ -25763,6 +25428,7 @@ ${stderr}`);
25763
25428
  readiness: "error",
25764
25429
  auth,
25765
25430
  models,
25431
+ skills,
25766
25432
  detail: "Claude is signed out",
25767
25433
  // 退出登录了档位照样是那五档。不报就退回宿主四档,而那份映射少了 `plan`。
25768
25434
  ...CLAUDE_NATIVE_MODE_REPORT
@@ -25774,6 +25440,7 @@ ${stderr}`);
25774
25440
  readiness: "ready",
25775
25441
  auth,
25776
25442
  models,
25443
+ skills,
25777
25444
  ...CLAUDE_NATIVE_MODE_REPORT
25778
25445
  };
25779
25446
  };
@@ -25785,7 +25452,7 @@ ${stderr}`);
25785
25452
  );
25786
25453
  return {
25787
25454
  configDir,
25788
- transcriptDir: NodePath7.join(configDir, "projects")
25455
+ transcriptDir: NodePath8.join(configDir, "projects")
25789
25456
  };
25790
25457
  };
25791
25458
  return {
@@ -25802,7 +25469,7 @@ ${stderr}`);
25802
25469
  readThread: adapter.readThread,
25803
25470
  events: adapter.events,
25804
25471
  dispose: adapter.dispose,
25805
- textGeneration: createClaudeTextGeneration(recipe, host, pluginDataDirPath),
25472
+ textGeneration: createClaudeTextGeneration(recipe, host, adapter.modelCatalog.current),
25806
25473
  continuationKey: claudeContinuationKey(recipe.homePath),
25807
25474
  sessions: {
25808
25475
  scan: async (input) => {
@@ -25828,7 +25495,7 @@ ${stderr}`);
25828
25495
  start: async () => {
25829
25496
  const command = claudeSignInCommand(
25830
25497
  recipe.credentialsPath,
25831
- NodeOS6.type() === "Windows_NT" ? "win32" : "linux"
25498
+ NodeOS8.type() === "Windows_NT" ? "win32" : "linux"
25832
25499
  );
25833
25500
  return {
25834
25501
  operationId: `claude-sign-in-${crypto.randomUUID()}`,
@@ -25844,13 +25511,12 @@ ${stderr}`);
25844
25511
 
25845
25512
  // src/index.ts
25846
25513
  var capturedAttachments;
25847
- var capturedDataDirPath;
25848
25514
  var executors = [
25849
25515
  {
25850
25516
  card: CLAUDE_CARD,
25851
25517
  createInstance: async (config, host) => {
25852
25518
  host.log.info("createInstance");
25853
- return createClaudeInstance(config, host, capturedAttachments, capturedDataDirPath);
25519
+ return createClaudeInstance(config, host, capturedAttachments);
25854
25520
  }
25855
25521
  }
25856
25522
  ];
@@ -25860,7 +25526,6 @@ var index_default = definePlugin({
25860
25526
  executors,
25861
25527
  activate(ctx) {
25862
25528
  capturedAttachments = ctx.attachments;
25863
- capturedDataDirPath = ctx.paths.dataDir;
25864
25529
  ctx.log.info("claude executor ready");
25865
25530
  }
25866
25531
  });