@ljwei-stak/model-router-galgame 0.8.0 → 0.9.0

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.
@@ -39,6 +39,218 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
39
39
  ));
40
40
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
41
41
 
42
+ // .dsh-plugin/shared/official-tool-registry.mjs
43
+ function getOfficialTool(id2) {
44
+ return TOOL_BY_ID.get(String(id2 ?? "").trim()) ?? null;
45
+ }
46
+ function toolForProvider(provider) {
47
+ const value = String(provider ?? "").trim().toLowerCase();
48
+ if (!value) return null;
49
+ for (const tool of OFFICIAL_TOOLS) {
50
+ if (tool.providerHints.some((hint) => value.includes(hint))) return tool;
51
+ }
52
+ return null;
53
+ }
54
+ function installCommandLine(tool) {
55
+ if (!tool || tool.unsupported) return null;
56
+ if (tool.manager === "signed-windows-installer") return "\u6253\u5F00\u5B98\u65B9\u7B7E\u540D\u5B89\u88C5\u5668\uFF08\u9009\u62E9\u5B89\u88C5\u76EE\u5F55\uFF09";
57
+ return tool.manager === "npm" ? `npm ${tool.installArgs.join(" ")}` : `${tool.manager} ${tool.installArgs.join(" ")}`;
58
+ }
59
+ var OFFICIAL_TOOLS, TOOL_BY_ID;
60
+ var init_official_tool_registry = __esm({
61
+ ".dsh-plugin/shared/official-tool-registry.mjs"() {
62
+ OFFICIAL_TOOLS = Object.freeze([
63
+ Object.freeze({
64
+ id: "kimi-code",
65
+ label: "Kimi Code \xB7 Node",
66
+ vendor: "Moonshot AI",
67
+ purpose: "Kimi \u5B98\u65B9\u7F16\u7A0B CLI\uFF0C\u63D0\u4F9B kimi \u547D\u4EE4\u4E0E ACP \u4F1A\u8BDD\u3002",
68
+ package: "@moonshot-ai/kimi-code",
69
+ version: "2.1.1",
70
+ manager: "npm",
71
+ installArgs: ["install", "-g", "@moonshot-ai/kimi-code@2.1.1", "--registry=https://registry.npmjs.org/"],
72
+ probeExecutables: ["kimi"],
73
+ probeNote: "kimi \u4E0E\u65E7 Python \u7248 kimi-cli \u540C\u540D\uFF1B\u8BF7\u6838\u5BF9\u53EF\u6267\u884C\u6587\u4EF6\u6765\u6E90\u548C\u7248\u672C\u3002",
74
+ providerHints: ["moonshot", "kimi"]
75
+ }),
76
+ Object.freeze({
77
+ id: "claude-code",
78
+ label: "Claude Code",
79
+ vendor: "Anthropic",
80
+ purpose: "Anthropic \u5B98\u65B9\u7F16\u7A0B CLI\uFF0C\u63D0\u4F9B claude \u547D\u4EE4\u3002",
81
+ package: "@anthropic-ai/claude-code",
82
+ version: "2.1.283",
83
+ manager: "npm",
84
+ installArgs: ["install", "-g", "@anthropic-ai/claude-code@2.1.283", "--registry=https://registry.npmjs.org/"],
85
+ probeExecutables: ["claude"],
86
+ providerHints: ["anthropic", "claude"]
87
+ }),
88
+ Object.freeze({
89
+ id: "codex",
90
+ label: "Codex CLI",
91
+ vendor: "OpenAI",
92
+ purpose: "OpenAI \u5B98\u65B9\u7F16\u7A0B CLI\uFF0C\u63D0\u4F9B codex \u547D\u4EE4\u3002",
93
+ package: "@openai/codex",
94
+ version: "0.157.1",
95
+ installArgs: ["install", "-g", "@openai/codex@0.157.1", "--registry=https://registry.npmjs.org/"],
96
+ manager: "npm",
97
+ probeExecutables: ["codex"],
98
+ probeNote: "Codex \u7248\u672C\u6A2A\u5E45\u7531\u9002\u914D\u5C42\u5BBD\u5339\u914D\uFF1B\u5B89\u88C5\u65F6\u56FA\u5B9A\u7248\u672C\u3002",
99
+ providerHints: ["openai", "gpt", "codex"]
100
+ }),
101
+ Object.freeze({
102
+ id: "minimax-code",
103
+ label: "MiniMax Code",
104
+ vendor: "MiniMax",
105
+ purpose: "MiniMax \u5B98\u65B9\u7F16\u7A0B CLI\uFF0C\u63D0\u4F9B mcode \u547D\u4EE4\u3002",
106
+ package: "@minimax-ai/code",
107
+ version: "0.5.5",
108
+ manager: "npm",
109
+ installArgs: ["install", "-g", "@minimax-ai/code@0.5.5", "--registry=https://registry.npmjs.org/", "--ignore-scripts=false", "--include=optional", "--allow-scripts=@minimax-ai/code,better-sqlite3"],
110
+ probeExecutables: ["mcode"],
111
+ providerHints: ["minimax"]
112
+ }),
113
+ Object.freeze({
114
+ id: "mimo-code",
115
+ label: "MiMo Code",
116
+ vendor: "XiaoMi",
117
+ purpose: "\u5C0F\u7C73 MiMo \u5B98\u65B9\u7F16\u7A0B CLI\uFF0C\u63D0\u4F9B mimo \u547D\u4EE4\u3002",
118
+ package: "@mimo-ai/cli",
119
+ version: "0.1.15",
120
+ manager: "npm",
121
+ installArgs: ["install", "-g", "@mimo-ai/cli@0.1.15", "--registry=https://registry.npmjs.org/"],
122
+ probeExecutables: ["mimo"],
123
+ providerHints: ["mimo", "xiaomi"]
124
+ }),
125
+ Object.freeze({
126
+ id: "grok-build",
127
+ label: "Grok Build",
128
+ vendor: "xAI",
129
+ purpose: "xAI \u5B98\u65B9\u7F16\u7A0B CLI\uFF0C\u63D0\u4F9B grok \u547D\u4EE4\u4E0E ACP \u4F1A\u8BDD\u3002",
130
+ package: "@xai-official/grok",
131
+ version: "1.0.41",
132
+ manager: "npm",
133
+ installArgs: ["install", "-g", "@xai-official/grok@1.0.41", "--registry=https://registry.npmjs.org/"],
134
+ probeExecutables: ["grok"],
135
+ providerHints: ["xai", "grok"]
136
+ }),
137
+ Object.freeze({
138
+ id: "zcode",
139
+ label: "ZCode",
140
+ vendor: "Z.ai",
141
+ purpose: "\u667A\u8C31\u5B98\u65B9 ZCode \u684C\u9762\u7248\uFF0C\u5185\u542B GLM \u7F16\u7A0B\u4EE3\u7406\u3002Windows \u5B89\u88C5\u5668\u53EF\u9009\u62E9 D \u76D8\u76EE\u5F55\u3002",
142
+ version: "3.14.3",
143
+ manager: "signed-windows-installer",
144
+ installArgs: [],
145
+ probeExecutables: [],
146
+ probeNote: "\u68C0\u6D4B\u7ECF\u8FC7\u6709\u6548\u7B7E\u540D\u7684 ZCode.exe \u548C\u540C\u76EE\u5F55 GLM \u8D44\u6E90\uFF1B\u684C\u9762\u5B89\u88C5\u5668\u9700\u4EBA\u5DE5\u9009\u62E9\u5B89\u88C5\u4F4D\u7F6E\u3002",
147
+ providerHints: ["zai", "z.ai", "zcode", "glm", "zhipu"]
148
+ })
149
+ ]);
150
+ TOOL_BY_ID = new Map(OFFICIAL_TOOLS.map((tool) => [tool.id, tool]));
151
+ }
152
+ });
153
+
154
+ // .dsh-plugin/shared/model-profiles.mjs
155
+ function nonnegative(value, label) {
156
+ if (typeof value !== "number" || !Number.isFinite(value) || value < 0 || value > 1e6) {
157
+ throw new Error(`${label} \u5FC5\u987B\u662F 0 \u5230 1000000 \u4E4B\u95F4\u7684\u6709\u9650\u6570\u5B57`);
158
+ }
159
+ return value;
160
+ }
161
+ function normalizeProfile(entry, index) {
162
+ const label = `\u7B2C ${index + 1} \u4E2A\u6A21\u578B`;
163
+ if (!entry || typeof entry !== "object" || Array.isArray(entry)) throw new Error(`${label} \u5FC5\u987B\u662F\u5BF9\u8C61`);
164
+ const allowed = /* @__PURE__ */ new Set(["provider", "model", "quality", "pricing", "specialties", "cliModel"]);
165
+ const unknown = Object.keys(entry).find((key) => !allowed.has(key));
166
+ if (unknown) throw new Error(`${label} \u542B\u4E0D\u652F\u6301\u7684\u5B57\u6BB5 ${unknown}\uFF1B\u4E0D\u8981\u5728\u8FD9\u91CC\u586B\u5199\u5BC6\u94A5\u6216\u547D\u4EE4`);
167
+ const provider = id(entry.provider);
168
+ const model = id(entry.model);
169
+ if (!provider || !model || provider.length > 160 || model.length > 240) {
170
+ throw new Error(`${label} \u9700\u8981\u6A21\u578B\u76EE\u5F55\u4E2D\u7684\u51C6\u786E provider \u548C model`);
171
+ }
172
+ const profile = { provider, model };
173
+ if (entry.quality !== void 0) {
174
+ const value = nonnegative(entry.quality, `${label} \u7684 quality`);
175
+ if (value > 100) throw new Error(`${label} \u7684 quality \u5E94\u5728 0 \u5230 100 \u4E4B\u95F4`);
176
+ profile.quality = value / 100;
177
+ }
178
+ if (entry.pricing !== void 0) {
179
+ const prices = entry.pricing;
180
+ if (!prices || typeof prices !== "object" || Array.isArray(prices)) throw new Error(`${label} \u7684 pricing \u5FC5\u987B\u662F\u5BF9\u8C61`);
181
+ const priceFields = /* @__PURE__ */ new Set(["input", "output", "cacheRead", "cacheWrite", "currency"]);
182
+ const unknownPrice = Object.keys(prices).find((key) => !priceFields.has(key));
183
+ if (unknownPrice) throw new Error(`${label} \u7684 pricing \u542B\u4E0D\u652F\u6301\u7684\u5B57\u6BB5 ${unknownPrice}`);
184
+ if (prices.currency !== void 0 && prices.currency !== "USD") throw new Error(`${label} \u7684\u4EF7\u683C\u5E01\u79CD\u76EE\u524D\u4EC5\u652F\u6301 USD`);
185
+ profile.pricing = {
186
+ input: nonnegative(prices.input, `${label} \u7684\u8F93\u5165\u5355\u4EF7`),
187
+ output: nonnegative(prices.output, `${label} \u7684\u8F93\u51FA\u5355\u4EF7`),
188
+ currency: "USD"
189
+ };
190
+ if (prices.cacheRead !== void 0) profile.pricing.cacheRead = nonnegative(prices.cacheRead, `${label} \u7684\u7F13\u5B58\u8BFB\u53D6\u5355\u4EF7`);
191
+ if (prices.cacheWrite !== void 0) profile.pricing.cacheWrite = nonnegative(prices.cacheWrite, `${label} \u7684\u7F13\u5B58\u5199\u5165\u5355\u4EF7`);
192
+ }
193
+ if (entry.specialties !== void 0) {
194
+ if (!Array.isArray(entry.specialties) || entry.specialties.length > 16 || entry.specialties.some((value) => !/^[a-z][a-z0-9-]{0,39}$/.test(value))) {
195
+ throw new Error(`${label} \u7684 specialties \u5FC5\u987B\u662F\u81F3\u591A 16 \u4E2A\u82F1\u6587\u4EFB\u52A1\u6807\u7B7E`);
196
+ }
197
+ profile.specialties = [...new Set(entry.specialties)];
198
+ }
199
+ if (entry.cliModel !== void 0) {
200
+ const cliModel = id(entry.cliModel);
201
+ if (!/^[A-Za-z0-9][A-Za-z0-9._:/-]{0,119}$/.test(cliModel)) throw new Error(`${label} \u7684 cliModel \u65E0\u6548`);
202
+ if (toolForProvider(provider)?.id === "zcode") throw new Error(`${label} \u5BF9\u5E94\u7684 ZCode CLI \u6682\u4E0D\u652F\u6301\u9010\u6B21\u5207\u6362\u6A21\u578B`);
203
+ profile.cliModel = cliModel;
204
+ }
205
+ if (Object.keys(profile).length === 2) throw new Error(`${label} \u81F3\u5C11\u63D0\u4F9B quality\u3001pricing\u3001specialties \u6216 cliModel \u4E4B\u4E00`);
206
+ return profile;
207
+ }
208
+ function parseModelProfilesJson(value) {
209
+ const source = value == null || value === "" ? "[]" : value;
210
+ if (typeof source !== "string" || source.length > MAX_TEXT) throw new Error("\u6A21\u578B\u4EF7\u683C\u4E0E\u80FD\u529B\u914D\u7F6E\u8D85\u8FC7 32000 \u5B57\u7B26\u4E0A\u9650");
211
+ let input;
212
+ try {
213
+ input = JSON.parse(source);
214
+ } catch {
215
+ throw new Error("\u6A21\u578B\u4EF7\u683C\u4E0E\u80FD\u529B\u914D\u7F6E\u4E0D\u662F\u6709\u6548\u7684 JSON");
216
+ }
217
+ if (!Array.isArray(input) || input.length > MAX_PROFILES) throw new Error("\u6A21\u578B\u4EF7\u683C\u4E0E\u80FD\u529B\u914D\u7F6E\u5FC5\u987B\u662F\u6700\u591A 200 \u9879\u7684 JSON \u6570\u7EC4");
218
+ const profiles = input.map(normalizeProfile);
219
+ const seen = /* @__PURE__ */ new Set();
220
+ for (const profile of profiles) {
221
+ const key = `${profile.provider}\0${profile.model}`;
222
+ if (seen.has(key)) throw new Error(`\u6A21\u578B ${profile.provider}/${profile.model} \u91CD\u590D\u914D\u7F6E`);
223
+ seen.add(key);
224
+ }
225
+ return profiles;
226
+ }
227
+ function applyModelProfiles(routes, profiles) {
228
+ const input = Array.isArray(routes) ? routes : [];
229
+ const values = typeof profiles === "string" ? parseModelProfilesJson(profiles) : profiles;
230
+ if (!Array.isArray(values)) throw new Error("\u6A21\u578B\u914D\u7F6E\u5FC5\u987B\u662F\u6570\u7EC4");
231
+ const byKey = new Map(values.map((profile) => [`${profile.provider}\0${profile.model}`, profile]));
232
+ return input.map((route) => {
233
+ const profile = byKey.get(`${route.provider}\0${route.model}`);
234
+ if (!profile) return route;
235
+ return {
236
+ ...route,
237
+ ...profile.quality === void 0 ? {} : { quality: profile.quality, qualitySource: "user" },
238
+ ...profile.pricing === void 0 ? {} : { pricing: { ...profile.pricing }, pricingSource: "user" },
239
+ ...profile.specialties === void 0 ? {} : { specialties: [...profile.specialties] },
240
+ ...profile.cliModel === void 0 ? {} : { cliModel: profile.cliModel }
241
+ };
242
+ });
243
+ }
244
+ var MAX_TEXT, MAX_PROFILES, id;
245
+ var init_model_profiles = __esm({
246
+ ".dsh-plugin/shared/model-profiles.mjs"() {
247
+ init_official_tool_registry();
248
+ MAX_TEXT = 32e3;
249
+ MAX_PROFILES = 200;
250
+ id = (value) => typeof value === "string" ? value.trim() : "";
251
+ }
252
+ });
253
+
42
254
  // .dsh-plugin/shared/livebench.mjs
43
255
  function normalized(value) {
44
256
  return String(value ?? "").toLowerCase().replace(/[^a-z0-9]+/g, "");
@@ -98,7 +310,7 @@ function assessComplexity(text2) {
98
310
  const domainMarkers = (value.match(/代码|工程|架构|接口|实现|部署|测试|模块|拆分|约束|评估|证明|定理|研究|论文|图片|图像|照片|视觉|code|api|debug|proof|research|vision/gi) ?? []).length;
99
311
  const domainComplexity = clamp(domainMarkers / 5) * 0.28;
100
312
  const raw = clamp(0.1 + lengthScore * 0.3 + requirementScore * 0.18 + domainComplexity + codeScore + highReasoningScore + visionScore);
101
- const band = raw < 0.34 ? "simple" : raw < 0.66 ? "balanced" : "complex";
313
+ const band = isHardRequirement(value) ? "complex" : isSimpleRequirement(value) && value.length <= 180 ? "simple" : raw < 0.34 ? "simple" : raw < 0.66 ? "balanced" : "complex";
102
314
  return { value: raw, band };
103
315
  }
104
316
  function specialtyMatch(model, taskType, liveScores = {}) {
@@ -117,6 +329,7 @@ function specialtyForTask(row, taskType) {
117
329
  return specialtyMatch(row.metadata, taskType, row.liveScores);
118
330
  }
119
331
  function asScore(value) {
332
+ if (value === null || value === void 0 || value === "") return void 0;
120
333
  const number = Number(value);
121
334
  if (!Number.isFinite(number)) return void 0;
122
335
  return clamp(number > 1 ? number / 100 : number);
@@ -124,14 +337,17 @@ function asScore(value) {
124
337
  function normalizePricing(pricing) {
125
338
  if (pricing === null || typeof pricing !== "object" || Array.isArray(pricing)) return {};
126
339
  const normalized2 = {};
127
- for (const [id, raw] of Object.entries(pricing)) {
340
+ for (const [id2, raw] of Object.entries(pricing)) {
128
341
  if (raw === null || typeof raw !== "object" || Array.isArray(raw)) continue;
342
+ if ((raw.input ?? raw.costIn) === null || (raw.input ?? raw.costIn) === void 0) continue;
343
+ if ((raw.output ?? raw.costOut) === null || (raw.output ?? raw.costOut) === void 0) continue;
129
344
  const input = Number(raw.input ?? raw.costIn);
130
345
  const output = Number(raw.output ?? raw.costOut);
131
- const cacheRead = Number(raw.cacheRead ?? 0);
132
- const cacheWrite = Number(raw.cacheWrite ?? 0);
346
+ const cacheRead = Number(raw.cacheRead ?? input);
347
+ const cacheWrite = Number(raw.cacheWrite ?? input);
133
348
  if (![input, output, cacheRead, cacheWrite].every((value) => Number.isFinite(value) && value >= 0)) continue;
134
- normalized2[normalize(id)] = {
349
+ if (String(raw.currency ?? "USD").toUpperCase() !== "USD") continue;
350
+ normalized2[normalize(id2)] = {
135
351
  input,
136
352
  output,
137
353
  cacheRead,
@@ -145,11 +361,13 @@ function pricingFor(model, pricing, provider = "") {
145
361
  const normalizedPricing = normalizePricing(pricing);
146
362
  const providerOverride = provider === "" ? void 0 : normalizedPricing[normalize(`${provider}/${model.id}`)];
147
363
  const override = providerOverride ?? normalizedPricing[normalize(model.id)];
148
- return override ?? {
149
- input: Number(model.costIn ?? 0),
150
- output: Number(model.costOut ?? 0),
151
- cacheRead: Number(model.cacheRead ?? 0),
152
- cacheWrite: Number(model.cacheWrite ?? 0),
364
+ if (override) return override;
365
+ if (!Number.isFinite(Number(model.costIn)) || !Number.isFinite(Number(model.costOut)) || model.costIn === null || model.costIn === void 0 || model.costOut === null || model.costOut === void 0) return null;
366
+ return {
367
+ input: Number(model.costIn),
368
+ output: Number(model.costOut),
369
+ cacheRead: Number(model.cacheRead ?? model.costIn),
370
+ cacheWrite: Number(model.cacheWrite ?? model.costIn),
153
371
  currency: "USD"
154
372
  };
155
373
  }
@@ -166,6 +384,7 @@ function effectivePricing(pricing, cacheReadRatio = 0, cacheWriteRatio = 0) {
166
384
  };
167
385
  }
168
386
  function costScore(pricing, maxCost, cacheReadRatio = 0, cacheWriteRatio = 0) {
387
+ if (pricing === null) return 0;
169
388
  const effective = effectivePricing(pricing, cacheReadRatio, cacheWriteRatio);
170
389
  const mean = (effective.input + effective.output) / 2;
171
390
  if (maxCost <= 0) return mean === 0 ? 1 : 0;
@@ -173,6 +392,7 @@ function costScore(pricing, maxCost, cacheReadRatio = 0, cacheWriteRatio = 0) {
173
392
  }
174
393
  function estimateCost(model, text2, outputTokens = 900, pricingOverrides = {}, cacheReadRatio = 0, cacheWriteRatio = 0) {
175
394
  const pricing = pricingFor(model, pricingOverrides);
395
+ if (pricing === null) return null;
176
396
  const inputTokens = Math.max(80, Math.ceil(String(text2 ?? "").length / 3.7));
177
397
  const ratios = normalizedCacheRatios(cacheReadRatio, cacheWriteRatio);
178
398
  const cacheReadTokens = Math.min(inputTokens, Math.max(0, Math.round(inputTokens * ratios.read)));
@@ -182,16 +402,14 @@ function estimateCost(model, text2, outputTokens = 900, pricingOverrides = {}, c
182
402
  }
183
403
  function modelMetadata(name) {
184
404
  const key = normalize(name);
185
- return MODEL_CATALOG.find((model) => model.aliases.some((alias) => {
186
- const candidate = normalize(alias);
187
- return key === candidate || key.includes(candidate) || candidate.includes(key);
188
- })) ?? null;
405
+ if (!key) return null;
406
+ return MODEL_CATALOG.find((model) => model.aliases.some((alias) => key === normalize(alias))) ?? null;
189
407
  }
190
408
  function taskTokenBudget(text2, task, complexity, cacheReadRatio = 0, cacheWriteRatio = 0) {
191
409
  const inputTokens = Math.max(80, Math.ceil(String(text2 ?? "").length / 3.7));
192
410
  const multipliers = {
193
411
  analysis: { input: 0.9, output: 0.55 },
194
- execution: { input: 1.2, output: complexity === "complex" ? 1.45 : 1 },
412
+ execution: { input: 1.2, output: (task.difficulty ?? complexity) === "complex" ? 1.45 : 1 },
195
413
  verification: { input: 1.15, output: 0.7 },
196
414
  synthesis: { input: 1.65, output: 1.3 }
197
415
  };
@@ -209,11 +427,36 @@ function taskTokenBudget(text2, task, complexity, cacheReadRatio = 0, cacheWrite
209
427
  };
210
428
  }
211
429
  function taskQualityFloor(band, task) {
212
- const base = QUALITY_FLOORS[band] ?? QUALITY_FLOORS.balanced;
430
+ const difficulty = task.difficulty ?? band;
431
+ const base = QUALITY_FLOORS[difficulty] ?? QUALITY_FLOORS.balanced;
213
432
  if (task.purpose === "synthesis") return Math.max(base, band === "complex" ? 0.84 : base);
214
- if (band !== "complex") return base;
433
+ if (difficulty !== "complex") return base;
215
434
  return clamp(base + Math.max(0, Number(task.criticality ?? 0.75) - 0.65) * 0.12);
216
435
  }
436
+ function isSimpleRequirement(value) {
437
+ const item = String(value ?? "").trim();
438
+ return SIMPLE_REQUEST_START.test(item) && !/(?:并|然后|最后|同时|以及|and then).{0,20}(?:设计|实现|验证|部署|证明|审计|测试|design|implement|verify|deploy|prove|audit|test)/i.test(item);
439
+ }
440
+ function isHardRequirement(value) {
441
+ const item = String(value ?? "").trim();
442
+ if (isSimpleRequirement(item)) return false;
443
+ return HIGH_STAKES_WORK.test(item) && HIGH_STAKES_ACTION.test(item);
444
+ }
445
+ function requirementDifficulty(objective, type, fallback = "balanced") {
446
+ const value = String(objective ?? "").trim();
447
+ if (isHardRequirement(value)) return "complex";
448
+ if (value.length <= 180 && SIMPLE_WORK.test(value)) return "simple";
449
+ const assessed = assessComplexity(value);
450
+ if (type === "math" && /证明|推导|proof|derive/i.test(value)) return "complex";
451
+ if (assessed.band === "simple" && (type === "code" || type === "research")) return "balanced";
452
+ return assessed.band === "simple" ? "simple" : assessed.band === "complex" ? "complex" : fallback;
453
+ }
454
+ function shouldSplitRequirements(requirements) {
455
+ if (requirements.length >= 3) return true;
456
+ if (requirements.length !== 2) return false;
457
+ const types = requirements.map((item) => detectTaskTypes(item)[0] ?? "general");
458
+ return types[0] !== types[1] || /^(?:最后|然后|接着|随后|再|基于|根据|测试|验证|部署|发布)|(?:完成|结束|实现)后/u.test(requirements[1]);
459
+ }
217
460
  function requirementText(value) {
218
461
  return String(value ?? "").trim().replace(/[。;;\s]+$/u, "").trim();
219
462
  }
@@ -238,14 +481,15 @@ function explicitRequirements(text2) {
238
481
  }
239
482
  const value = visibleLines.join("\n");
240
483
  const lines = visibleLines.map((line) => line.trim()).filter(Boolean);
484
+ if (/^(?:请|帮我)?(?:总结|概括|翻译|摘要|解释)(?:以下|下列|下面|这份|这些)/u.test(lines[0] ?? "") && !/(?:执行|完成|实施|分配)/u.test(lines[0])) return [];
241
485
  const marker = /^(?:[-*•]\s+|\d{1,2}[.)、](?!\d)\s*|[一二三四五六七八九十]{1,3}[、.)]\s*)(.+)$/u;
242
- const marked = lines.map((line) => marker.exec(line)?.[1]).filter(Boolean).map(requirementText).filter((item) => item.length >= 3);
486
+ const marked = lines.map((line) => marker.exec(line)?.[1]).filter(Boolean).map(requirementText).filter(looksLikeRequirement);
243
487
  if (marked.length >= 2) return marked;
244
488
  const plain2 = lines.filter((line) => !/^#{1,6}\s|^```|[::]$/u.test(line));
245
489
  if (plain2.length >= 2 && plain2.every(looksLikeRequirement)) {
246
490
  return plain2.map(requirementText).filter((item) => item.length >= 3);
247
491
  }
248
- const clauses = value.split(/[;;]/u).map(requirementText).filter(Boolean);
492
+ const clauses = value.split(/[;;,,]/u).map(requirementText).filter(Boolean);
249
493
  if (clauses.length >= 2 && clauses.every(looksLikeRequirement)) {
250
494
  return clauses;
251
495
  }
@@ -257,12 +501,12 @@ function explicitRequirements(text2) {
257
501
  }
258
502
  function taskPackages(taskType, text2, band) {
259
503
  if (band !== "complex") {
260
- const task = { id: "execution", name: "\u76F4\u63A5\u56DE\u7B54\u4E0E\u5FC5\u8981\u6821\u9A8C", type: taskType, purpose: "execution", criticality: 0.65, dependsOn: [], preferredReasoningEffort: band === "simple" ? "low" : "medium" };
504
+ const task = { id: "execution", name: "\u76F4\u63A5\u56DE\u7B54\u4E0E\u5FC5\u8981\u6821\u9A8C", type: taskType, purpose: "execution", difficulty: band, criticality: 0.65, dependsOn: [], preferredReasoningEffort: band === "simple" ? "low" : "medium" };
261
505
  return [{ ...task, qualityFloor: taskQualityFloor(band, task) }];
262
506
  }
263
507
  const value = String(text2 ?? "");
264
508
  const packages = [
265
- { id: "analysis", name: "\u95EE\u9898\u5EFA\u6A21\u4E0E\u7EA6\u675F\u63D0\u53D6", type: "reasoning", purpose: "analysis", criticality: 0.92, dependsOn: [], preferredReasoningEffort: "high" }
509
+ { id: "analysis", name: "\u95EE\u9898\u5EFA\u6A21\u4E0E\u7EA6\u675F\u63D0\u53D6", type: "reasoning", purpose: "analysis", difficulty: "balanced", criticality: 0.8, dependsOn: [], preferredReasoningEffort: "medium" }
266
510
  ];
267
511
  const requirements = explicitRequirements(text2);
268
512
  if (requirements.length >= 2) {
@@ -272,29 +516,37 @@ function taskPackages(taskType, text2, band) {
272
516
  ] : requirements.map((item) => [item]);
273
517
  groups.forEach((group, index) => {
274
518
  const objective = group.length === 1 ? group[0] : group.map((item, offset) => `${MAX_EXPLICIT_EXECUTION_PACKAGES + offset}. ${item}`).join("\n");
275
- const type = detectTaskTypes(objective)[0] ?? taskType;
519
+ const type = detectTaskTypes(objective)[0] ?? "general";
520
+ const difficulty = group.length > 1 ? "complex" : requirementDifficulty(objective, type);
521
+ const previous = packages.at(-1);
522
+ const sequential = /^(?:最后|然后|接着|随后|再|基于|根据|测试|验证|部署|发布)|(?:完成|结束|实现)后/u.test(objective);
276
523
  packages.push({
277
524
  id: `execution-${index + 1}`,
278
525
  name: group.length === 1 ? `\u9700\u6C42 ${index + 1}\uFF1A${group[0].slice(0, 28)}` : `\u9700\u6C42 ${index + 1}\uFF1A\u5176\u4F59 ${group.length} \u9879`,
279
526
  objective,
280
527
  type,
281
528
  purpose: "execution",
282
- criticality: 0.8,
283
- dependsOn: ["analysis"],
284
- preferredReasoningEffort: "high"
529
+ difficulty,
530
+ criticality: difficulty === "simple" ? 0.55 : difficulty === "balanced" ? 0.72 : 0.86,
531
+ dependsOn: sequential && previous?.purpose === "execution" ? ["analysis", previous.id] : ["analysis"],
532
+ preferredReasoningEffort: difficulty === "simple" ? "low" : difficulty === "balanced" ? "medium" : "high"
285
533
  });
286
534
  });
287
535
  } else {
288
536
  const domains = [...new Set([...detectTaskTypes(text2), taskType].filter((type) => type !== "general"))];
289
537
  for (const type of domains.length > 0 ? domains : [taskType]) {
538
+ const objective = value.split(/[,,。;;]|最后|然后|接着|并且/u).map((item) => item.trim()).filter((item) => detectTaskTypes(item).includes(type)).join("\uFF1B") || value;
539
+ const difficulty = requirementDifficulty(objective, type);
290
540
  packages.push({
291
541
  id: `execution-${type}`,
292
542
  name: `${TASK_TYPE_LABELS[type] ?? type}\u65B9\u5411\u5904\u7406`,
543
+ objective,
293
544
  type,
294
545
  purpose: "execution",
295
- criticality: domains.length > 1 ? 0.8 : 0.78,
546
+ difficulty,
547
+ criticality: difficulty === "simple" ? 0.55 : difficulty === "balanced" ? 0.72 : 0.86,
296
548
  dependsOn: ["analysis"],
297
- preferredReasoningEffort: "high"
549
+ preferredReasoningEffort: difficulty === "simple" ? "low" : difficulty === "balanced" ? "medium" : "high"
298
550
  });
299
551
  }
300
552
  }
@@ -304,6 +556,7 @@ function taskPackages(taskType, text2, band) {
304
556
  name: "\u9A8C\u8BC1\u3001\u53CD\u4F8B\u4E0E\u98CE\u9669\u5BA1\u67E5",
305
557
  type: "reasoning",
306
558
  purpose: "verification",
559
+ difficulty: "complex",
307
560
  criticality: 0.88,
308
561
  dependsOn: packages.filter((task) => task.purpose === "execution").map((task) => task.id),
309
562
  preferredReasoningEffort: "high"
@@ -314,6 +567,7 @@ function taskPackages(taskType, text2, band) {
314
567
  name: "\u7ED3\u679C\u6821\u9A8C\u4E0E\u6574\u5408",
315
568
  type: "reasoning",
316
569
  purpose: "synthesis",
570
+ difficulty: "complex",
317
571
  criticality: 1,
318
572
  dependsOn: packages.filter((task) => task.purpose !== "analysis").map((task) => task.id),
319
573
  preferredReasoningEffort: "xhigh"
@@ -321,7 +575,7 @@ function taskPackages(taskType, text2, band) {
321
575
  return packages.map((task) => ({ ...task, qualityFloor: taskQualityFloor(band, task) }));
322
576
  }
323
577
  function weightsForTask(weights, task) {
324
- return task.purpose === "synthesis" ? SYNTHESIS_WEIGHTS : weights;
578
+ return task.purpose === "synthesis" ? SYNTHESIS_WEIGHTS : OBJECTIVE_WEIGHTS[task.difficulty] ?? weights;
325
579
  }
326
580
  function compareText(left, right) {
327
581
  const a = String(left);
@@ -380,7 +634,7 @@ function candidateUtility(row, task, weights, maxCost, usedRoutes, cacheReadRati
380
634
  const quality = qualityForTask(row, task.type);
381
635
  const floor = Number(task.qualityFloor ?? taskQualityFloor("complex", task));
382
636
  const qualityGap = Math.max(0, floor - quality);
383
- const duplicatePenalty = usedRoutes.has(routeKey(row.provider, row.model)) ? 0.08 : 0;
637
+ const duplicatePenalty = 0;
384
638
  const synthesisPreference = task.purpose === "synthesis" && /deepseek[- ]?v4[- ]?pro/i.test(row.model) ? 0.025 : 0;
385
639
  const reasoning = reasoningDecision(row, task);
386
640
  const cost = clamp(costScore(row.pricing, maxCost, cacheReadRatio, cacheWriteRatio) / Math.sqrt(reasoning.multiplier.output));
@@ -388,11 +642,13 @@ function candidateUtility(row, task, weights, maxCost, usedRoutes, cacheReadRati
388
642
  return { score, floor, qualityGap, ...reasoning };
389
643
  }
390
644
  function taskCost(row, task, text2, complexity, cacheReadRatio = 0, cacheWriteRatio = 0) {
645
+ if (row?.pricing === null) return 0;
391
646
  const decision = row === null ? null : reasoningDecision(row, task);
392
647
  const tokens = taskTokenBudget(text2, { ...task, reasoningEffort: decision?.reasoningEffort }, complexity, cacheReadRatio, cacheWriteRatio);
393
648
  return row === null ? 0 : ((tokens.inputTokens - tokens.cacheReadTokens - tokens.cacheWriteTokens) * row.pricing.input + tokens.cacheReadTokens * row.pricing.cacheRead + tokens.cacheWriteTokens * row.pricing.cacheWrite + tokens.outputTokens * row.pricing.output) / 1e6;
394
649
  }
395
650
  function dominates(left, right, task, text2, complexity, cacheReadRatio, cacheWriteRatio) {
651
+ if (left.pricing === null || right.pricing === null) return false;
396
652
  const leftReasoning = reasoningDecision(left, task);
397
653
  const rightReasoning = reasoningDecision(right, task);
398
654
  const leftValues = {
@@ -416,9 +672,10 @@ function dominates(left, right, task, text2, complexity, cacheReadRatio, cacheWr
416
672
  return noWorse && strictlyBetter;
417
673
  }
418
674
  function candidatePool(rows, task, weights, maxCost, text2, complexity, cacheReadRatio, cacheWriteRatio) {
675
+ const eligibleRows = task.type === "vision" ? rows.filter((row) => row.inputModalities.length === 0 || row.inputModalities.includes("image")) : rows;
419
676
  const floor = Number(task.qualityFloor ?? 0);
420
- const feasible = rows.filter((row) => qualityForTask(row, task.type) >= floor);
421
- const source = feasible.length > 0 ? feasible : rows.slice().sort((left, right) => qualityForTask(right, task.type) - qualityForTask(left, task.type) || compareRowsStable(left, right)).slice(0, 3);
677
+ const feasible = eligibleRows.filter((row) => qualityForTask(row, task.type) >= floor);
678
+ const source = feasible.length > 0 ? feasible : eligibleRows.slice().sort((left, right) => qualityForTask(right, task.type) - qualityForTask(left, task.type) || compareRowsStable(left, right)).slice(0, 3);
422
679
  const taskWeights = weightsForTask(weights, task);
423
680
  const scored = source.map((row) => ({
424
681
  row,
@@ -435,7 +692,7 @@ function candidatePool(rows, task, weights, maxCost, text2, complexity, cacheRea
435
692
  return {
436
693
  options: ordered,
437
694
  relaxed: feasible.length === 0,
438
- pruned: Math.max(0, rows.length - ordered.length)
695
+ pruned: Math.max(0, eligibleRows.length - ordered.length)
439
696
  };
440
697
  }
441
698
  function stateSignature(state) {
@@ -452,7 +709,9 @@ function solveAssignments({ rows, tasks, weights, maxCost, text: text2, complexi
452
709
  if (pools.some((pool) => pool.options.length === 0)) return null;
453
710
  const suffixMinimum = Array(tasks.length + 1).fill(0);
454
711
  for (let index = tasks.length - 1; index >= 0; index -= 1) {
455
- suffixMinimum[index] = suffixMinimum[index + 1] + Math.min(...pools[index].options.map((option2) => option2.cost));
712
+ const costOptions = Number.isFinite(budget) ? pools[index].options.filter((option2) => option2.row.pricing !== null) : pools[index].options;
713
+ if (costOptions.length === 0) return null;
714
+ suffixMinimum[index] = suffixMinimum[index + 1] + Math.min(...costOptions.map((option2) => option2.cost));
456
715
  }
457
716
  if (Number.isFinite(budget) && suffixMinimum[0] > budget + 1e-12) return null;
458
717
  let states = [{ assignments: [], routesByTask: /* @__PURE__ */ new Map(), usedRoutes: /* @__PURE__ */ new Set(), score: 0, cost: 0, switches: 0, relaxedCount: 0, qualityShortfall: 0 }];
@@ -462,6 +721,7 @@ function solveAssignments({ rows, tasks, weights, maxCost, text: text2, complexi
462
721
  const expanded = [];
463
722
  for (const state of states) {
464
723
  for (const option2 of pool.options) {
724
+ if (Number.isFinite(budget) && option2.row.pricing === null) continue;
465
725
  const nextCost = state.cost + option2.cost;
466
726
  if (Number.isFinite(budget) && nextCost + suffixMinimum[index + 1] > budget + 1e-12) continue;
467
727
  const taskWeights = weightsForTask(weights, task);
@@ -501,8 +761,13 @@ function solveAssignments({ rows, tasks, weights, maxCost, text: text2, complexi
501
761
  };
502
762
  }
503
763
  function buildPlan({ text: text2 = "", available = [], mode = "collective", pricing = {}, liveBench = null, liveBenchError = "", budgetUsd = 0, cacheReadRatio = 0, cacheWriteRatio = 0 } = {}) {
504
- const complexity = assessComplexity(text2);
505
- const taskType = classifyTask(text2);
764
+ const firstLine = String(text2 ?? "").split(/\r?\n/u)[0].trim();
765
+ const transformOnly = /^(?:请|帮我)?(?:总结|概括|翻译|摘要|解释)(?:以下|下列|下面|这份|这些)/u.test(firstLine) && !/(?:执行|完成|实施|分配)/u.test(firstLine);
766
+ const assessed = assessComplexity(transformOnly ? firstLine : text2);
767
+ const requirements = explicitRequirements(text2);
768
+ const compound = shouldSplitRequirements(requirements);
769
+ const complexity = compound && assessed.band !== "complex" ? { value: Math.max(0.66, assessed.value), band: "complex" } : assessed;
770
+ const taskType = classifyTask(transformOnly ? firstLine : text2);
506
771
  const weights = OBJECTIVE_WEIGHTS[complexity.band];
507
772
  const discovered = Array.isArray(available) ? available.map((entry) => {
508
773
  const rawEfforts = Array.isArray(entry.reasoningEfforts) ? entry.reasoningEfforts : [];
@@ -512,55 +777,71 @@ function buildPlan({ text: text2 = "", available = [], mode = "collective", pric
512
777
  model: String(entry.model ?? ""),
513
778
  reasoningEfforts: [...new Set(reasoningEfforts)],
514
779
  defaultReasoningEffort: entry.defaultReasoningEffort === void 0 ? void 0 : String(entry.defaultReasoningEffort),
515
- reasoningKnown: entry.reasoningKnown === true || entry.reasoningKnown === void 0 && Array.isArray(entry.reasoningEfforts)
780
+ reasoningKnown: entry.reasoningKnown === true || entry.reasoningKnown === void 0 && Array.isArray(entry.reasoningEfforts),
781
+ quality: asScore(entry.quality),
782
+ qualitySource: entry.qualitySource === "user" ? "user" : "route",
783
+ latency: asScore(entry.latency),
784
+ risk: asScore(entry.risk),
785
+ specialties: Array.isArray(entry.specialties) ? entry.specialties.filter((item) => typeof item === "string") : null,
786
+ pricing: normalizePricing({ route: entry.pricing ?? entry.price }).route ?? null,
787
+ pricingSource: entry.pricingSource === "user" ? "user" : "route",
788
+ inputModalities: Array.isArray(entry.inputModalities) ? entry.inputModalities.map((item) => String(item).toLowerCase()) : []
516
789
  };
517
790
  }) : [];
518
791
  const rows = [];
519
792
  const normalizedPrices = normalizePricing(pricing);
520
- const maxCost = Math.max(1, ...MODEL_CATALOG.map((model) => {
521
- const row = pricingFor(model, normalizedPrices);
522
- const effective = effectivePricing(row, cacheReadRatio, cacheWriteRatio);
523
- return effective.input + effective.output;
524
- }), ...Object.values(normalizedPrices).map((row) => {
525
- const effective = effectivePricing(row, cacheReadRatio, cacheWriteRatio);
526
- return effective.input + effective.output;
527
- }));
528
793
  for (const route of discovered) {
529
- const metadata = modelMetadata(route.model) ?? {
530
- id: route.model,
531
- aliases: [route.model],
532
- quality: 0.66,
533
- latency: 0.55,
534
- costIn: 1,
535
- costOut: 4,
536
- specialties: [],
537
- risk: 0.24
794
+ if (!route.provider || !route.model) continue;
795
+ const catalog = modelMetadata(route.model);
796
+ const metadata = {
797
+ ...catalog ?? { id: route.model, aliases: [route.model], specialties: [] },
798
+ ...route.quality === void 0 ? {} : { quality: route.quality },
799
+ ...route.latency === void 0 ? {} : { latency: route.latency },
800
+ ...route.risk === void 0 ? {} : { risk: route.risk },
801
+ ...route.specialties === null ? {} : { specialties: route.specialties }
538
802
  };
539
803
  const live = liveBenchRow(liveBench, route.model);
540
804
  const liveScores = live?.scores ?? {};
541
805
  const liveOverall = asScore(live?.overall);
542
- const quality = asScore(liveScores?.[taskType]) ?? liveOverall ?? metadata.quality;
543
- const pricingRow = pricingFor(metadata, normalizedPrices, route.provider);
806
+ const quality = asScore(liveScores?.[taskType]) ?? liveOverall ?? asScore(metadata.quality) ?? 0;
807
+ const qualitySource = liveOverall !== void 0 || asScore(liveScores?.[taskType]) !== void 0 ? "livebench" : route.quality !== void 0 ? route.qualitySource : catalog ? "catalog-heuristic" : "unknown";
808
+ const userPrice = normalizedPrices[normalize(`${route.provider}/${route.model}`)] ?? normalizedPrices[normalize(route.model)];
809
+ const pricingRow = userPrice ?? route.pricing ?? null;
810
+ const pricingSource = userPrice ? "user" : route.pricing ? route.pricingSource : "unknown";
544
811
  const specialty = specialtyMatch(metadata, taskType, liveScores);
545
812
  rows.push({
546
813
  provider: route.provider,
547
814
  model: route.model,
548
815
  metadata,
549
816
  quality,
817
+ qualitySource,
818
+ pricingSource,
550
819
  liveScores,
551
820
  liveOverall,
552
- latency: metadata.latency,
553
- risk: metadata.risk,
821
+ latency: metadata.latency ?? 0.5,
822
+ risk: metadata.risk ?? 0.2,
554
823
  specialty,
555
824
  reasoningEfforts: route.reasoningEfforts,
556
825
  defaultReasoningEffort: route.defaultReasoningEffort,
557
826
  reasoningKnown: route.reasoningKnown,
827
+ inputModalities: route.inputModalities,
558
828
  pricing: pricingRow,
559
829
  score: 0,
560
- estimatedCost: estimateCost(metadata, text2, 900, normalizedPrices, cacheReadRatio, cacheWriteRatio)
830
+ estimatedCost: pricingRow === null ? null : estimateCost({
831
+ id: route.model,
832
+ costIn: pricingRow.input,
833
+ costOut: pricingRow.output,
834
+ cacheRead: pricingRow.cacheRead,
835
+ cacheWrite: pricingRow.cacheWrite
836
+ }, text2, 900, {}, cacheReadRatio, cacheWriteRatio)
561
837
  });
562
838
  }
839
+ const maxCost = Math.max(1, ...rows.filter((row) => row.pricing !== null).map((row) => {
840
+ const effective = effectivePricing(row.pricing, cacheReadRatio, cacheWriteRatio);
841
+ return effective.input + effective.output;
842
+ }));
563
843
  const taskNodes = taskPackages(taskType, text2, complexity.band);
844
+ const unassignableTasks = taskNodes.filter((task) => task.type === "vision" && !rows.some((row) => row.inputModalities.length === 0 || row.inputModalities.includes("image"))).map((task) => task.id);
564
845
  const budget = Number(budgetUsd);
565
846
  const utilityPlan = solveAssignments({
566
847
  rows,
@@ -605,16 +886,19 @@ function buildPlan({ text: text2 = "", available = [], mode = "collective", pric
605
886
  }
606
887
  rows.sort((left, right) => right.score - left.score || compareRowsStable(left, right));
607
888
  const selectedAssignment = assignments[0];
608
- const selected = selectedAssignment?.row ?? rows[0] ?? null;
889
+ const selected = selectedAssignment?.row ?? (unassignableTasks.length > 0 ? null : rows[0] ?? null);
609
890
  const synthesizerAssignment = assignments.at(-1);
610
- const synthesizer = synthesizerAssignment?.row ?? rows.find((row) => /deepseek/i.test(row.model)) ?? rows[0];
891
+ const synthesizer = synthesizerAssignment?.row ?? (unassignableTasks.length > 0 ? null : rows.find((row) => /deepseek/i.test(row.model)) ?? rows[0]);
611
892
  const subtasks = assignments.map(({ task, row, decision }) => ({
612
893
  id: task.id,
613
894
  name: task.name,
614
895
  ...task.objective ? { objective: task.objective } : {},
615
896
  type: task.type,
897
+ difficulty: task.difficulty,
616
898
  recommended: row?.model ?? "\u5F85\u53D1\u73B0\u6A21\u578B",
617
899
  recommendedProvider: row?.provider ?? "",
900
+ qualitySource: row?.qualitySource ?? "unknown",
901
+ pricingSource: row?.pricingSource ?? "unknown",
618
902
  recommendedReasoningEffort: decision?.reasoningEffort,
619
903
  preferredReasoningEffort: decision?.preferredReasoningEffort ?? task.preferredReasoningEffort,
620
904
  reasoningFit: Number(Number(decision?.reasoningFit ?? 0).toFixed(3)),
@@ -629,6 +913,7 @@ function buildPlan({ text: text2 = "", available = [], mode = "collective", pric
629
913
  return {
630
914
  stage: index + 1,
631
915
  purpose: task.purpose,
916
+ difficulty: task.difficulty,
632
917
  model: row?.model ?? "\u5F85\u53D1\u73B0\u6A21\u578B",
633
918
  provider: row?.provider ?? "",
634
919
  reasoningEffort: decision?.reasoningEffort,
@@ -639,35 +924,42 @@ function buildPlan({ text: text2 = "", available = [], mode = "collective", pric
639
924
  cacheReadTokens: tokens.cacheReadTokens,
640
925
  cacheWriteTokens: tokens.cacheWriteTokens,
641
926
  outputTokens: tokens.outputTokens,
642
- estimatedCost: Number(taskEstimate.toFixed(6)),
643
- quality: Number((row === null ? 0 : qualityForTask(row, task.type)).toFixed(3)),
927
+ estimatedCost: row?.pricing === null || row === null ? null : Number(taskEstimate.toFixed(6)),
928
+ quality: row?.qualitySource === "unknown" || row === null ? null : Number(qualityForTask(row, task.type).toFixed(3)),
929
+ qualitySource: row?.qualitySource ?? "unknown",
930
+ pricingSource: row?.pricingSource ?? "unknown",
644
931
  handoffPenalty: Number(Number(handoffPenalty ?? 0).toFixed(3))
645
932
  };
646
933
  });
647
- const totalEstimate = costBreakdown.reduce((sum, row) => sum + row.estimatedCost, 0);
648
- const baselineCost = assignments.reduce((sum, { task }) => {
934
+ const pricingComplete = assignments.length > 0 && assignments.every(({ row }) => row?.pricing !== null);
935
+ const totalEstimate = pricingComplete ? costBreakdown.reduce((sum, row) => sum + row.estimatedCost, 0) : null;
936
+ const baselineRows = assignments.map(({ task }) => {
649
937
  const strongest = rows.reduce((best, row) => qualityForTask(row, task.type) > (best === null ? -1 : qualityForTask(best, task.type)) ? row : best, null);
650
- return sum + taskCost(strongest, task, text2, complexity.band, cacheReadRatio, cacheWriteRatio);
651
- }, 0);
652
- const budgetExceeded = Number(budgetUsd) > 0 && totalEstimate > Number(budgetUsd);
653
- const savings = baselineCost <= 0 ? 0 : clamp((baselineCost - totalEstimate) / baselineCost);
938
+ return { task, strongest };
939
+ });
940
+ const baselineCost = baselineRows.every((item) => item.strongest?.pricing !== null && item.strongest !== null) ? baselineRows.reduce((sum, { task, strongest }) => sum + taskCost(strongest, task, text2, complexity.band, cacheReadRatio, cacheWriteRatio), 0) : null;
941
+ const qualityEvidenceComplete = assignments.length > 0 && assignments.every(({ row }) => ["livebench", "route", "user"].includes(row?.qualitySource)) && baselineRows.every(({ strongest }) => ["livebench", "route", "user"].includes(strongest?.qualitySource));
942
+ const budgetExceeded = Number(budgetUsd) > 0 && totalEstimate !== null ? totalEstimate > Number(budgetUsd) : null;
943
+ const savings = baselineCost === null || totalEstimate === null || !qualityEvidenceComplete ? null : baselineCost <= 0 ? 0 : clamp((baselineCost - totalEstimate) / baselineCost);
654
944
  const paretoPruned = (optimized?.candidatePools ?? []).reduce((sum, pool) => sum + pool.pruned, 0);
655
- const minimumFeasibleCost = optimized?.minimumFeasibleCost ?? minimumCostPlan?.cost ?? 0;
656
- const reason = selected === null ? "\u5C1A\u672A\u53D1\u73B0\u53EF\u7528\u6A21\u578B\uFF0C\u4FDD\u7559 Harness \u539F\u59CB\u6A21\u578B\u9009\u62E9\u3002" : `${complexity.band === "simple" ? "\u4F4E\u590D\u6742\u5EA6\u4F18\u5148\u6210\u672C\u3001\u54CD\u5E94\u901F\u5EA6\u4E0E\u8F83\u4F4E\u63A8\u7406\u5F00\u9500" : complexity.band === "balanced" ? "\u5728\u8D28\u91CF\u3001\u6210\u672C\u3001\u63A8\u7406\u7B49\u7EA7\u3001\u5EF6\u8FDF\u4E0E\u98CE\u9669\u4E4B\u95F4\u5E73\u8861" : "\u9AD8\u590D\u6742\u5EA6\u6267\u884C\u5305\u542B\u63A8\u7406\u7B49\u7EA7\u7684\u4F9D\u8D56\u611F\u77E5\u5168\u5C40\u7EA6\u675F\u5206\u914D"}\uFF1B\u4EFB\u52A1\u7C7B\u578B\u4E3A ${taskType}\uFF0C\u5DF2\u5BF9 ${String(subtasks.length)} \u4E2A\u5DE5\u4F5C\u5305\u8FDB\u884C Pareto \u526A\u679D\u548C\u6709\u754C\u7EC4\u5408\u641C\u7D22\u3002`;
945
+ const minimumFeasibleCost = rows.every((row) => row.pricing !== null) ? optimized?.minimumFeasibleCost ?? minimumCostPlan?.cost ?? 0 : null;
946
+ const reason = selected === null ? unassignableTasks.length > 0 ? `\u56FE\u50CF\u5DE5\u4F5C\u5305 ${unassignableTasks.join("\u3001")} \u6CA1\u6709\u53EF\u7528\u7684\u56FE\u50CF\u6A21\u578B\uFF0C\u65E0\u6CD5\u5F62\u6210\u5B8C\u6574\u5206\u914D\u8BA1\u5212\u3002` : "\u5C1A\u672A\u53D1\u73B0\u53EF\u7528\u6A21\u578B\uFF0C\u4FDD\u7559 Harness \u539F\u59CB\u6A21\u578B\u9009\u62E9\u3002" : `${complexity.band === "simple" ? "\u4F4E\u590D\u6742\u5EA6\u4F18\u5148\u6210\u672C\u3001\u54CD\u5E94\u901F\u5EA6\u4E0E\u8F83\u4F4E\u63A8\u7406\u5F00\u9500" : complexity.band === "balanced" ? "\u5728\u8D28\u91CF\u3001\u6210\u672C\u3001\u63A8\u7406\u7B49\u7EA7\u3001\u5EF6\u8FDF\u4E0E\u98CE\u9669\u4E4B\u95F4\u5E73\u8861" : "\u9AD8\u590D\u6742\u5EA6\u6267\u884C\u5305\u542B\u63A8\u7406\u7B49\u7EA7\u7684\u4F9D\u8D56\u611F\u77E5\u5168\u5C40\u7EA6\u675F\u5206\u914D"}\uFF1B\u4EFB\u52A1\u7C7B\u578B\u4E3A ${taskType}\uFF0C\u5DF2\u5BF9 ${String(subtasks.length)} \u4E2A\u5DE5\u4F5C\u5305\u8FDB\u884C Pareto \u526A\u679D\u548C\u6709\u754C\u7EC4\u5408\u641C\u7D22\u3002`;
657
947
  return {
658
948
  mode,
659
949
  complexity: { value: Number(complexity.value.toFixed(3)), band: complexity.band },
950
+ compound,
951
+ unassignableTasks,
660
952
  taskType,
661
953
  taskTypes: [...new Set(taskNodes.map((task) => task.type).filter((type) => type !== "reasoning"))],
662
954
  objectiveWeights: weights,
663
955
  candidates: rows.slice(0, 8).map((row) => {
664
956
  const decision = candidateUtility(row, taskNodes[0] ?? { type: taskType, qualityFloor: QUALITY_FLOORS[complexity.band], preferredReasoningEffort: complexity.band === "simple" ? "low" : "medium" }, weights, maxCost, /* @__PURE__ */ new Set(), cacheReadRatio, cacheWriteRatio);
665
- return { provider: row.provider, model: row.model, score: Number(row.score.toFixed(3)), quality: Number(row.quality.toFixed(3)), specialty: Number(row.specialty.toFixed(3)), reasoningEffort: decision.reasoningEffort, preferredReasoningEffort: decision.preferredReasoningEffort, reasoningFit: Number(decision.reasoningFit.toFixed(3)), reasoningKnown: row.reasoningKnown, reasoningEfforts: row.reasoningEfforts, estimatedCost: Number(row.estimatedCost.toFixed(6)), inputPrice: row.pricing.input, outputPrice: row.pricing.output };
957
+ return { provider: row.provider, model: row.model, score: Number(row.score.toFixed(3)), quality: row.qualitySource === "unknown" ? null : Number(row.quality.toFixed(3)), qualitySource: row.qualitySource, specialty: Number(row.specialty.toFixed(3)), reasoningEffort: decision.reasoningEffort, preferredReasoningEffort: decision.preferredReasoningEffort, reasoningFit: Number(decision.reasoningFit.toFixed(3)), reasoningKnown: row.reasoningKnown, reasoningEfforts: row.reasoningEfforts, estimatedCost: row.estimatedCost === null ? null : Number(row.estimatedCost.toFixed(6)), inputPrice: row.pricing?.input ?? null, outputPrice: row.pricing?.output ?? null, pricingSource: row.pricingSource };
666
958
  }),
667
- selected: selected === null ? null : { provider: selected.provider, model: selected.model, reasoningEffort: selectedAssignment?.decision?.reasoningEffort, estimatedCost: Number(selected.estimatedCost.toFixed(6)) },
959
+ selected: selected === null ? null : { provider: selected.provider, model: selected.model, reasoningEffort: selectedAssignment?.decision?.reasoningEffort, estimatedCost: selected.estimatedCost === null ? null : Number(selected.estimatedCost.toFixed(6)), qualitySource: selected.qualitySource, pricingSource: selected.pricingSource },
668
960
  subtasks,
669
- synthesizer: synthesizer === void 0 ? null : { provider: synthesizer.provider, model: synthesizer.model, reasoningEffort: synthesizerAssignment?.decision?.reasoningEffort },
670
- estimatedCost: Number(totalEstimate.toFixed(6)),
961
+ synthesizer: synthesizer == null ? null : { provider: synthesizer.provider, model: synthesizer.model, reasoningEffort: synthesizerAssignment?.decision?.reasoningEffort },
962
+ estimatedCost: totalEstimate === null ? null : Number(totalEstimate.toFixed(6)),
671
963
  costBreakdown,
672
964
  optimization: {
673
965
  solver: "pareto-pruned quality-constrained beam assignment",
@@ -677,21 +969,23 @@ function buildPlan({ text: text2 = "", available = [], mode = "collective", pric
677
969
  cacheWriteRatio: normalizedCacheRatios(cacheReadRatio, cacheWriteRatio).write,
678
970
  budgetExceeded,
679
971
  constraintRelaxed,
680
- baselineAllStrongCost: Number(baselineCost.toFixed(6)),
681
- estimatedSavings: Number(savings.toFixed(4)),
972
+ pricingComplete,
973
+ qualityEvidenceComplete,
974
+ baselineAllStrongCost: baselineCost === null ? null : Number(baselineCost.toFixed(6)),
975
+ estimatedSavings: savings === null ? null : Number(savings.toFixed(4)),
682
976
  distinctRoutes: usedRoutes.size,
683
977
  handoffCount: optimized?.switches ?? 0,
684
978
  paretoPruned,
685
979
  beamWidth: ROUTING_BEAM_WIDTH,
686
- budgetFeasible: budget <= 0 || budgetPlan !== null,
687
- minimumFeasibleCost: Number(Number(minimumFeasibleCost).toFixed(6)),
980
+ budgetFeasible: budget <= 0 ? pricingComplete ? true : null : budgetPlan !== null,
981
+ minimumFeasibleCost: minimumFeasibleCost === null ? null : Number(Number(minimumFeasibleCost).toFixed(6)),
688
982
  liveBench: liveBench?.fetchedAt ? { source: liveBench.source ?? "livebench", fetchedAt: liveBench.fetchedAt, models: Object.keys(liveBench.models ?? {}).length, stale: String(liveBenchError).length > 0, error: String(liveBenchError || "") } : { source: "experimental-baseline", fetchedAt: null, models: 0, stale: false, error: String(liveBenchError || "") }
689
983
  },
690
984
  reason,
691
985
  generatedAt: (/* @__PURE__ */ new Date()).toISOString()
692
986
  };
693
987
  }
694
- var OBJECTIVE_WEIGHTS, REASONING_EFFORT_ORDER, REASONING_EFFORT_MULTIPLIERS, QUALITY_FLOORS, DEFAULT_ROUTER_SETTINGS, MODEL_CATALOG, clamp, normalize, routeKey, OPENCODE_CATALOG_PROVIDERS, TASK_TYPE_RULES, TASK_TYPE_LABELS, MAX_EXPLICIT_EXECUTION_PACKAGES, REQUIREMENT_ACTION, SYNTHESIS_WEIGHTS, ROUTING_BEAM_WIDTH, ROUTING_CANDIDATE_LIMIT;
988
+ var OBJECTIVE_WEIGHTS, REASONING_EFFORT_ORDER, REASONING_EFFORT_MULTIPLIERS, QUALITY_FLOORS, DEFAULT_ROUTER_SETTINGS, MODEL_CATALOG, clamp, normalize, routeKey, OPENCODE_CATALOG_PROVIDERS, TASK_TYPE_RULES, TASK_TYPE_LABELS, MAX_EXPLICIT_EXECUTION_PACKAGES, REQUIREMENT_ACTION, HIGH_STAKES_WORK, SIMPLE_WORK, HIGH_STAKES_ACTION, SIMPLE_REQUEST_START, SYNTHESIS_WEIGHTS, ROUTING_BEAM_WIDTH, ROUTING_CANDIDATE_LIMIT;
695
989
  var init_router = __esm({
696
990
  ".dsh-plugin/shared/router.mjs"() {
697
991
  init_livebench();
@@ -755,7 +1049,7 @@ var init_router = __esm({
755
1049
  ["math", /数学|证明|定理|公式|方程|math|proof|theorem/i],
756
1050
  ["code", /代码|编程|工程|项目|架构|接口|api|debug|实现|部署|测试|code/i],
757
1051
  ["research", /研究|论文|文献|联网|检索|research|source|引用/i],
758
- ["summarization", /总结|摘要|提炼|分类|翻译|summar|classif|extract/i],
1052
+ ["summarization", /总结|摘要|提炼|提取|关键词|分类|翻译|summar|classif|extract/i],
759
1053
  ["writing", /写作|润色|小说|文案|报告|writing|draft/i]
760
1054
  ]);
761
1055
  TASK_TYPE_LABELS = Object.freeze({
@@ -767,125 +1061,17 @@ var init_router = __esm({
767
1061
  writing: "\u5199\u4F5C\u4E0E\u8868\u8FBE"
768
1062
  });
769
1063
  MAX_EXPLICIT_EXECUTION_PACKAGES = 6;
770
- REQUIREMENT_ACTION = /(?:实现|新增|添加|修复|更新|构建|设计|完成|编写|优化|接入|支持|配置|部署|测试|验证|检查|整理|创建|移除|替换|迁移|适配|开发|调研|安装|下载|上传|发布|生成|集成|改造|封装|显示|提供|允许|确保|处理|解决|分析|探测|检测|选择|分配|拆分|对接|加入|保留|记录|输出|审查|核对|对比|可以|能够|需要|进行|implement|add|fix|update|build|design|write|improve|support|configure|deploy|test|verify|check|create|remove|replace|migrate|install|publish|generate|integrate|review|should|must|need)/i;
1064
+ REQUIREMENT_ACTION = /(?:实现|新增|添加|修复|更新|构建|设计|完成|编写|优化|接入|支持|配置|部署|测试|验证|检查|整理|创建|移除|替换|迁移|适配|开发|调研|安装|下载|上传|发布|生成|集成|改造|封装|显示|提供|允许|确保|处理|解决|分析|探测|检测|选择|分配|拆分|对接|加入|保留|记录|输出|审查|核对|对比|提取|摘要|总结|分类|翻译|格式化|润色|可以|能够|需要|进行|implement|add|fix|update|build|design|write|improve|support|configure|deploy|test|verify|check|create|remove|replace|migrate|install|publish|generate|integrate|review|extract|summarize|classify|translate|format|should|must|need)/i;
1065
+ HIGH_STAKES_WORK = /架构|安全|隐私|权限|并发|事务|迁移|生产|部署|发布|证明|定理|科研|论文|复杂|多步骤|跨系统|系统设计|architecture|security|migration|production|proof|theorem|research/i;
1066
+ SIMPLE_WORK = /翻译|摘要|总结|提取|分类|格式化|列出|改写|润色|拼写|校对|translate|summarize|extract|classify|format|proofread/i;
1067
+ HIGH_STAKES_ACTION = /设计|实现|修复|审计|测试|验证|证明|推导|部署|迁移|规划|研究|解决|推理|design|implement|fix|audit|verify|prove|derive|deploy|migrate|research|solve/i;
1068
+ SIMPLE_REQUEST_START = /^(?:请|帮我)?(?:翻译|摘要|总结|提取|分类|格式化|列出|改写|润色|拼写|校对|translate|summarize|extract|classify|format|proofread)/i;
771
1069
  SYNTHESIS_WEIGHTS = Object.freeze({ quality: 0.58, cost: 0.08, latency: 0.04, specialty: 0.08, reasoning: 0.16, risk: 0.06 });
772
1070
  ROUTING_BEAM_WIDTH = 256;
773
1071
  ROUTING_CANDIDATE_LIMIT = 12;
774
1072
  }
775
1073
  });
776
1074
 
777
- // .dsh-plugin/shared/official-tool-registry.mjs
778
- function getOfficialTool(id) {
779
- return TOOL_BY_ID.get(String(id ?? "").trim()) ?? null;
780
- }
781
- function toolForProvider(provider) {
782
- const value = String(provider ?? "").trim().toLowerCase();
783
- if (!value) return null;
784
- for (const tool of OFFICIAL_TOOLS) {
785
- if (tool.providerHints.some((hint) => value.includes(hint))) return tool;
786
- }
787
- return null;
788
- }
789
- function installCommandLine(tool) {
790
- if (!tool || tool.unsupported) return null;
791
- if (tool.manager === "signed-windows-installer") return "\u6253\u5F00\u5B98\u65B9\u7B7E\u540D\u5B89\u88C5\u5668\uFF08\u9009\u62E9\u5B89\u88C5\u76EE\u5F55\uFF09";
792
- return tool.manager === "npm" ? `npm ${tool.installArgs.join(" ")}` : `${tool.manager} ${tool.installArgs.join(" ")}`;
793
- }
794
- var OFFICIAL_TOOLS, TOOL_BY_ID;
795
- var init_official_tool_registry = __esm({
796
- ".dsh-plugin/shared/official-tool-registry.mjs"() {
797
- OFFICIAL_TOOLS = Object.freeze([
798
- Object.freeze({
799
- id: "kimi-code",
800
- label: "Kimi Code \xB7 Node",
801
- vendor: "Moonshot AI",
802
- purpose: "Kimi \u5B98\u65B9\u7F16\u7A0B CLI\uFF0C\u63D0\u4F9B kimi \u547D\u4EE4\u4E0E ACP \u4F1A\u8BDD\u3002",
803
- package: "@moonshot-ai/kimi-code",
804
- version: "2.1.1",
805
- manager: "npm",
806
- installArgs: ["install", "-g", "@moonshot-ai/kimi-code@2.1.1", "--registry=https://registry.npmjs.org/"],
807
- probeExecutables: ["kimi"],
808
- probeNote: "kimi \u4E0E\u65E7 Python \u7248 kimi-cli \u540C\u540D\uFF1B\u8BF7\u6838\u5BF9\u53EF\u6267\u884C\u6587\u4EF6\u6765\u6E90\u548C\u7248\u672C\u3002",
809
- providerHints: ["moonshot", "kimi"]
810
- }),
811
- Object.freeze({
812
- id: "claude-code",
813
- label: "Claude Code",
814
- vendor: "Anthropic",
815
- purpose: "Anthropic \u5B98\u65B9\u7F16\u7A0B CLI\uFF0C\u63D0\u4F9B claude \u547D\u4EE4\u3002",
816
- package: "@anthropic-ai/claude-code",
817
- version: "2.1.283",
818
- manager: "npm",
819
- installArgs: ["install", "-g", "@anthropic-ai/claude-code@2.1.283", "--registry=https://registry.npmjs.org/"],
820
- probeExecutables: ["claude"],
821
- providerHints: ["anthropic", "claude"]
822
- }),
823
- Object.freeze({
824
- id: "codex",
825
- label: "Codex CLI",
826
- vendor: "OpenAI",
827
- purpose: "OpenAI \u5B98\u65B9\u7F16\u7A0B CLI\uFF0C\u63D0\u4F9B codex \u547D\u4EE4\u3002",
828
- package: "@openai/codex",
829
- version: "0.157.1",
830
- installArgs: ["install", "-g", "@openai/codex@0.157.1", "--registry=https://registry.npmjs.org/"],
831
- manager: "npm",
832
- probeExecutables: ["codex"],
833
- probeNote: "Codex \u7248\u672C\u6A2A\u5E45\u7531\u9002\u914D\u5C42\u5BBD\u5339\u914D\uFF1B\u5B89\u88C5\u65F6\u56FA\u5B9A\u7248\u672C\u3002",
834
- providerHints: ["openai", "gpt", "codex"]
835
- }),
836
- Object.freeze({
837
- id: "minimax-code",
838
- label: "MiniMax Code",
839
- vendor: "MiniMax",
840
- purpose: "MiniMax \u5B98\u65B9\u7F16\u7A0B CLI\uFF0C\u63D0\u4F9B mcode \u547D\u4EE4\u3002",
841
- package: "@minimax-ai/code",
842
- version: "0.5.5",
843
- manager: "npm",
844
- installArgs: ["install", "-g", "@minimax-ai/code@0.5.5", "--registry=https://registry.npmjs.org/", "--ignore-scripts=false", "--include=optional", "--allow-scripts=@minimax-ai/code,better-sqlite3"],
845
- probeExecutables: ["mcode"],
846
- providerHints: ["minimax"]
847
- }),
848
- Object.freeze({
849
- id: "mimo-code",
850
- label: "MiMo Code",
851
- vendor: "XiaoMi",
852
- purpose: "\u5C0F\u7C73 MiMo \u5B98\u65B9\u7F16\u7A0B CLI\uFF0C\u63D0\u4F9B mimo \u547D\u4EE4\u3002",
853
- package: "@mimo-ai/cli",
854
- version: "0.1.15",
855
- manager: "npm",
856
- installArgs: ["install", "-g", "@mimo-ai/cli@0.1.15", "--registry=https://registry.npmjs.org/"],
857
- probeExecutables: ["mimo"],
858
- providerHints: ["mimo", "xiaomi"]
859
- }),
860
- Object.freeze({
861
- id: "grok-build",
862
- label: "Grok Build",
863
- vendor: "xAI",
864
- purpose: "xAI \u5B98\u65B9\u7F16\u7A0B CLI\uFF0C\u63D0\u4F9B grok \u547D\u4EE4\u4E0E ACP \u4F1A\u8BDD\u3002",
865
- package: "@xai-official/grok",
866
- version: "1.0.41",
867
- manager: "npm",
868
- installArgs: ["install", "-g", "@xai-official/grok@1.0.41", "--registry=https://registry.npmjs.org/"],
869
- probeExecutables: ["grok"],
870
- providerHints: ["xai", "grok"]
871
- }),
872
- Object.freeze({
873
- id: "zcode",
874
- label: "ZCode",
875
- vendor: "Z.ai",
876
- purpose: "\u667A\u8C31\u5B98\u65B9 ZCode \u684C\u9762\u7248\uFF0C\u5185\u542B GLM \u7F16\u7A0B\u4EE3\u7406\u3002Windows \u5B89\u88C5\u5668\u53EF\u9009\u62E9 D \u76D8\u76EE\u5F55\u3002",
877
- version: "3.14.3",
878
- manager: "signed-windows-installer",
879
- installArgs: [],
880
- probeExecutables: [],
881
- probeNote: "\u68C0\u6D4B\u7ECF\u8FC7\u6709\u6548\u7B7E\u540D\u7684 ZCode.exe \u548C\u540C\u76EE\u5F55 GLM \u8D44\u6E90\uFF1B\u684C\u9762\u5B89\u88C5\u5668\u9700\u4EBA\u5DE5\u9009\u62E9\u5B89\u88C5\u4F4D\u7F6E\u3002",
882
- providerHints: ["zai", "z.ai", "zcode", "glm", "zhipu"]
883
- })
884
- ]);
885
- TOOL_BY_ID = new Map(OFFICIAL_TOOLS.map((tool) => [tool.id, tool]));
886
- }
887
- });
888
-
889
1075
  // .dsh-plugin/shared/harness-plan.mjs
890
1076
  function channelForProvider(provider, installedToolIds = [], runnableToolIds = []) {
891
1077
  const tool = toolForProvider(provider);
@@ -909,7 +1095,16 @@ function annotate(channel) {
909
1095
  channelDetail: channel.detail
910
1096
  };
911
1097
  }
912
- function createPlanFromRoutes(task, availableRoutes, { mode = "single", budgetUsd = 0, installedToolIds = [], runnableToolIds = [] } = {}) {
1098
+ function createPlanFromRoutes(task, availableRoutes, {
1099
+ mode = "single",
1100
+ budgetUsd = 0,
1101
+ installedToolIds = [],
1102
+ runnableToolIds = [],
1103
+ pricing = {},
1104
+ liveBench = null,
1105
+ cacheReadRatio = 0,
1106
+ cacheWriteRatio = 0
1107
+ } = {}) {
913
1108
  const taskText = clean(task);
914
1109
  if (!taskText) throw new Error("task must contain text");
915
1110
  const selectedMode = mode === "team" ? "team" : "single";
@@ -922,12 +1117,15 @@ function createPlanFromRoutes(task, availableRoutes, { mode = "single", budgetUs
922
1117
  return channelCache.get(key);
923
1118
  };
924
1119
  const needsImage = detectTaskTypes(taskText).includes("vision");
925
- const eligibleRoutes = needsImage ? routes.filter((route) => !Array.isArray(route.inputModalities) || route.inputModalities.length === 0 || route.inputModalities.includes("image")) : routes;
926
1120
  const plan = buildPlan({
927
1121
  text: taskText,
928
- available: eligibleRoutes,
1122
+ available: routes,
929
1123
  mode: selectedMode,
930
- budgetUsd: Math.max(0, Number.isFinite(budgetUsd) ? budgetUsd : 0)
1124
+ budgetUsd: Math.max(0, Number.isFinite(budgetUsd) ? budgetUsd : 0),
1125
+ pricing,
1126
+ liveBench,
1127
+ cacheReadRatio,
1128
+ cacheWriteRatio
931
1129
  });
932
1130
  const selectedChannel = plan.selected ? channelOf(plan.selected.provider) : null;
933
1131
  return {
@@ -936,22 +1134,27 @@ function createPlanFromRoutes(task, availableRoutes, { mode = "single", budgetUs
936
1134
  availableRoutes: routes,
937
1135
  ...selectedChannel ? annotate(selectedChannel) : {},
938
1136
  availabilityNotice: "\u6A21\u578B\u76EE\u5F55\u5217\u51FA\u7684\u8DEF\u7EBF\u5C1A\u672A\u9A8C\u8BC1\u5F53\u524D\u51ED\u636E\u548C\u7F51\u7EDC\uFF1B\u5B9E\u9645\u53EF\u7528\u6027\u4EE5\u5B98\u65B9\u9002\u914D\u5668\u8C03\u7528\u7ED3\u679C\u4E3A\u51C6\u3002",
939
- pricingNotice: "\u8D39\u7528\u662F\u672C\u5730\u4F30\u7B97\uFF0C\u4E0D\u662F\u4F9B\u5E94\u5546\u8D26\u5355\uFF0C\u4E5F\u4E0D\u662F\u786C\u6027\u652F\u51FA\u4E0A\u9650\u3002",
940
- modalityNotice: needsImage && eligibleRoutes.length < routes.length ? "\u56FE\u50CF\u4EFB\u52A1\u5DF2\u6392\u9664\u660E\u786E\u58F0\u660E\u4E0D\u63A5\u53D7\u56FE\u50CF\u8F93\u5165\u7684\u6A21\u578B\uFF1B\u672A\u58F0\u660E\u80FD\u529B\u7684\u6A21\u578B\u4ECD\u9700\u4EBA\u5DE5\u9A8C\u8BC1\u3002" : null,
1137
+ pricingNotice: plan.estimatedCost === null ? "\u90E8\u5206\u8DEF\u7EBF\u5C1A\u672A\u914D\u7F6E\u8BE5\u4F9B\u5E94\u5546\u7684\u7F8E\u5143\u8F93\u5165/\u8F93\u51FA\u5355\u4EF7\uFF0C\u65E0\u6CD5\u8BA1\u7B97\u53EF\u9760\u7684\u603B\u8D39\u7528\u4E0E\u8282\u7701\u6BD4\u4F8B\uFF1B\u8BF7\u5728\u6A21\u578B\u4EF7\u683C\u8BBE\u7F6E\u4E2D\u8865\u9F50\u3002" : "\u8D39\u7528\u6309\u5DF2\u63D0\u4F9B\u7684\u7F8E\u5143\u5355\u4EF7\u548C\u4F30\u8BA1 token \u6570\u8BA1\u7B97\uFF0C\u4E0D\u662F\u4F9B\u5E94\u5546\u8D26\u5355\uFF0C\u4E5F\u4E0D\u662F\u786C\u6027\u652F\u51FA\u4E0A\u9650\u3002",
1138
+ qualityNotice: plan.optimization.qualityEvidenceComplete ? "\u6A21\u578B\u8D28\u91CF\u4F7F\u7528\u5DF2\u63D0\u4F9B\u8BC4\u5206\u6216\u57FA\u51C6\u6570\u636E\u4F30\u8BA1\uFF0C\u4ECD\u9700\u5B9E\u9645\u4EFB\u52A1\u9A8C\u8BC1\u3002" : "\u90E8\u5206\u6A21\u578B\u8D28\u91CF\u7F3A\u5C11\u53EF\u6838\u9A8C\u8BC4\u5206\uFF1B\u76EE\u5F55\u542F\u53D1\u5F0F\u53EA\u4F9B\u9009\u62E9\u53C2\u8003\uFF0C\u8D28\u91CF\u95E8\u69DB\u548C\u8282\u7701\u6BD4\u4F8B\u65E0\u6CD5\u4FDD\u8BC1\u3002",
1139
+ modalityNotice: needsImage ? plan.unassignableTasks.length > 0 ? "\u56FE\u50CF\u5DE5\u4F5C\u5305\u6CA1\u6709\u53EF\u786E\u8BA4\u652F\u6301\u56FE\u50CF\u8F93\u5165\u7684\u8DEF\u7EBF\uFF0C\u5F53\u524D\u8BA1\u5212\u65E0\u6CD5\u5B8C\u6574\u5206\u914D\uFF1B\u8BF7\u5728\u5B98\u65B9\u6A21\u578B\u76EE\u5F55\u914D\u7F6E\u652F\u6301\u56FE\u50CF\u7684\u6A21\u578B\u3002" : routes.some((route) => !Array.isArray(route.inputModalities) || route.inputModalities.length === 0) ? "\u56FE\u50CF\u5DE5\u4F5C\u5305\u53EA\u5206\u7ED9\u5DF2\u58F0\u660E\u56FE\u50CF\u80FD\u529B\u6216\u672A\u58F0\u660E\u8F93\u5165\u80FD\u529B\u7684\u6A21\u578B\uFF1B\u672A\u58F0\u660E\u80FD\u529B\u7684\u6A21\u578B\u4ECD\u9700\u5B9E\u9645\u9A8C\u8BC1\u3002\u5176\u4ED6\u6587\u672C\u5DE5\u4F5C\u5305\u53EF\u7EE7\u7EED\u4F7F\u7528\u7ECF\u6D4E\u578B\u6587\u672C\u6A21\u578B\u3002" : "\u56FE\u50CF\u5DE5\u4F5C\u5305\u53EA\u5206\u7ED9\u660E\u786E\u652F\u6301\u56FE\u50CF\u8F93\u5165\u7684\u6A21\u578B\uFF1B\u5176\u4ED6\u6587\u672C\u5DE5\u4F5C\u5305\u53EF\u7EE7\u7EED\u4F7F\u7528\u7ECF\u6D4E\u578B\u6587\u672C\u6A21\u578B\u3002" : null,
941
1140
  toolNotice: "\u6267\u884C\u6E20\u9053\u6309\u5B98\u65B9\u5DE5\u5177\u6CE8\u518C\u8868\u548C\u5DF2\u6838\u9A8C\u9002\u914D\u5668\u6807\u6CE8\uFF1Aofficial-cli \u8868\u793A\u8BE5\u5382\u5546\u5B98\u65B9 CLI \u5DF2\u5B89\u88C5\u4E14\u53EF\u6258\u7BA1\u6267\u884C\uFF1Bharness-llm \u8868\u793A\u901A\u8FC7\u5B98\u65B9\u6A21\u578B\u76EE\u5F55\u8C03\u7528\u3002\u53EF\u5728\u5DE5\u4F5C\u53F0\u67E5\u770B\u5B89\u88C5\u4E0E\u6267\u884C\u652F\u6301\u72B6\u6001\u3002",
942
1141
  team: {
943
1142
  requested: selectedMode === "team",
944
1143
  recommended: selectedMode === "team" && plan.complexity.band === "complex" && plan.subtasks.length > 1,
945
1144
  handoff: "\u53EF\u5728\u5B98\u65B9\u4F1A\u8BDD\u8C03\u7528 model_router_team_execute \u6258\u7BA1\u6267\u884C\u5F53\u524D\u5E73\u53F0\u652F\u6301\u7684\u5B98\u65B9 CLI \u5DE5\u4F5C\u5305\uFF1B\u5B98\u65B9 Agent Teams \u53EF\u534F\u4F5C\u7BA1\u7406\u4EFB\u52A1\uFF0C\u4F46\u6210\u5458\u6A21\u578B\u7531\u5BBF\u4E3B\u914D\u7F6E\uFF0C\u4E0D\u80FD\u76F4\u63A5\u6309\u672C\u8BA1\u5212\u9010\u4E2A\u5207\u6362\u3002",
946
- workPackages: plan.subtasks.map((item) => ({
1145
+ workPackages: plan.subtasks.map((item, index) => ({
947
1146
  id: item.id,
948
1147
  name: item.name,
949
1148
  ...item.objective ? { objective: item.objective } : {},
950
1149
  type: item.type,
951
1150
  purpose: item.purpose,
1151
+ difficulty: item.difficulty,
1152
+ qualitySource: item.qualitySource,
1153
+ pricingSource: item.pricingSource,
952
1154
  dependsOn: item.dependsOn,
953
1155
  recommendedProvider: item.recommendedProvider,
954
1156
  recommendedModel: item.recommended,
1157
+ estimatedCost: plan.costBreakdown[index]?.estimatedCost ?? null,
955
1158
  ...item.recommendedReasoningEffort ? { recommendedReasoningEffort: item.recommendedReasoningEffort } : {},
956
1159
  ...annotate(channelOf(item.recommendedProvider)),
957
1160
  verificationChecklist: item.purpose === "synthesis" ? ["\u6838\u5BF9\u5404\u5DE5\u4F5C\u5305\u4EA4\u4ED8\u7269\u4E0E\u4F9D\u8D56", "\u8BB0\u5F55\u51B2\u7A81\u3001\u672A\u89E3\u51B3\u4E8B\u9879\u548C\u6700\u7EC8\u9A8C\u6536\u7ED3\u679C"] : item.type === "code" ? ["\u8BF4\u660E\u6539\u52A8\u6587\u4EF6\u4E0E\u63A5\u53E3", "\u8FD0\u884C\u4E0E\u6539\u52A8\u76F8\u5173\u7684\u9A8C\u8BC1\u5E76\u8BB0\u5F55\u7ED3\u679C", "\u5217\u51FA\u5C1A\u672A\u5B8C\u6210\u7684\u8FB9\u754C\u60C5\u51B5"] : item.type === "research" ? ["\u5217\u51FA\u6765\u6E90\u3001\u65E5\u671F\u548C\u53EF\u6838\u5BF9\u7684\u7ED3\u8BBA", "\u6807\u51FA\u63A8\u65AD\u4E0E\u4E0D\u786E\u5B9A\u4E8B\u9879"] : ["\u5217\u51FA\u4EA4\u4ED8\u5185\u5BB9\u548C\u9A8C\u6536\u4F9D\u636E", "\u6807\u51FA\u672A\u5B8C\u6210\u4E8B\u9879"]
@@ -1008,13 +1211,17 @@ function routesFromModelCatalog(catalog) {
1008
1211
  return routes;
1009
1212
  }
1010
1213
  function createWorkspacePlan(task, catalog, options = {}) {
1011
- const routes = routesFromModelCatalog(catalog);
1214
+ const routes = applyModelProfiles(
1215
+ routesFromModelCatalog(catalog),
1216
+ parseModelProfilesJson(options.modelProfilesJson ?? "[]")
1217
+ );
1012
1218
  return createPlanFromRoutes(task, routes, options);
1013
1219
  }
1014
1220
  var clean2;
1015
1221
  var init_catalog = __esm({
1016
1222
  ".dsh-plugin/client/catalog.mjs"() {
1017
1223
  init_harness_plan();
1224
+ init_model_profiles();
1018
1225
  clean2 = (value) => typeof value === "string" ? value.trim() : "";
1019
1226
  }
1020
1227
  });
@@ -1032,38 +1239,222 @@ __export(official_harness_exports, {
1032
1239
  inject: () => inject
1033
1240
  });
1034
1241
  module.exports = __toCommonJS(official_harness_exports);
1035
- var import_react3 = __toESM(require("react"), 1);
1242
+ var import_react4 = __toESM(require("react"), 1);
1243
+ init_model_profiles();
1036
1244
 
1037
1245
  // .dsh-plugin/client/router-main.jsx
1038
- var import_react = __toESM(require("react"), 1);
1246
+ var import_react2 = __toESM(require("react"), 1);
1039
1247
  init_catalog();
1248
+
1249
+ // .dsh-plugin/client/model-profile-editor.jsx
1250
+ var import_react = __toESM(require("react"), 1);
1251
+ init_model_profiles();
1252
+
1253
+ // .dsh-plugin/client/model-profile-editor-state.mjs
1254
+ init_model_profiles();
1255
+ var PROFILE_SPECIALTY_HINT = "code, math, research, summarization, writing, vision, reasoning";
1256
+ var profileRouteKey = (route) => `${String(route?.provider ?? "")}\0${String(route?.model ?? "")}`;
1257
+ var field = (value) => String(value ?? "").trim();
1258
+ function nonnegativeField(value, label, max = 1e6) {
1259
+ const raw = field(value);
1260
+ if (!raw) return null;
1261
+ const parsed = Number(raw);
1262
+ if (!Number.isFinite(parsed) || parsed < 0 || parsed > max) throw new Error(`${label} \u5FC5\u987B\u662F 0 \u5230 ${max} \u7684\u6570\u5B57\u3002`);
1263
+ return parsed;
1264
+ }
1265
+ function profileDraft(profile) {
1266
+ return {
1267
+ quality: profile?.quality === void 0 ? "" : String(Number((profile.quality * 100).toFixed(4))),
1268
+ input: profile?.pricing?.input === void 0 ? "" : String(profile.pricing.input),
1269
+ output: profile?.pricing?.output === void 0 ? "" : String(profile.pricing.output),
1270
+ cacheRead: profile?.pricing?.cacheRead === void 0 ? "" : String(profile.pricing.cacheRead),
1271
+ cacheWrite: profile?.pricing?.cacheWrite === void 0 ? "" : String(profile.pricing.cacheWrite),
1272
+ specialties: Array.isArray(profile?.specialties) ? profile.specialties.join(", ") : "",
1273
+ cliModel: profile?.cliModel ?? ""
1274
+ };
1275
+ }
1276
+ function profileFromDraft(route, draft) {
1277
+ const provider = field(route?.provider);
1278
+ const model = field(route?.model);
1279
+ if (!provider || !model) throw new Error("\u8BF7\u5148\u4ECE\u5B98\u65B9\u6A21\u578B\u76EE\u5F55\u9009\u62E9\u4E00\u6761\u51C6\u786E\u7684\u6A21\u578B\u8DEF\u7EBF\u3002");
1280
+ const profile = { provider, model };
1281
+ const quality = nonnegativeField(draft?.quality, "\u8D28\u91CF\u8BC4\u5206", 100);
1282
+ if (quality !== null) profile.quality = quality;
1283
+ const input = nonnegativeField(draft?.input, "\u8F93\u5165\u5355\u4EF7");
1284
+ const output = nonnegativeField(draft?.output, "\u8F93\u51FA\u5355\u4EF7");
1285
+ const cacheRead = nonnegativeField(draft?.cacheRead, "\u7F13\u5B58\u8BFB\u53D6\u5355\u4EF7");
1286
+ const cacheWrite = nonnegativeField(draft?.cacheWrite, "\u7F13\u5B58\u5199\u5165\u5355\u4EF7");
1287
+ if (input === null !== (output === null)) throw new Error("\u8F93\u5165\u548C\u8F93\u51FA\u5355\u4EF7\u9700\u8981\u540C\u65F6\u586B\u5199\uFF1B\u7559\u7A7A\u8868\u793A\u4EF7\u683C\u672A\u77E5\u3002");
1288
+ if (input === null && (cacheRead !== null || cacheWrite !== null)) throw new Error("\u586B\u5199\u7F13\u5B58\u5355\u4EF7\u524D\uFF0C\u8BF7\u5148\u586B\u5199\u8F93\u5165\u548C\u8F93\u51FA\u5355\u4EF7\u3002");
1289
+ if (input !== null) {
1290
+ profile.pricing = { input, output, currency: "USD" };
1291
+ if (cacheRead !== null) profile.pricing.cacheRead = cacheRead;
1292
+ if (cacheWrite !== null) profile.pricing.cacheWrite = cacheWrite;
1293
+ }
1294
+ const rawSpecialties = field(draft?.specialties);
1295
+ if (rawSpecialties) {
1296
+ const specialties = [...new Set(rawSpecialties.split(/[,,\s]+/u).filter(Boolean))];
1297
+ if (specialties.length > 16 || specialties.some((item) => !/^[a-z][a-z0-9-]{0,39}$/.test(item))) {
1298
+ throw new Error("\u64C5\u957F\u65B9\u5411\u6700\u591A 16 \u9879\uFF0C\u7528\u82F1\u6587\u5C0F\u5199\u6807\u7B7E\u5E76\u4EE5\u9017\u53F7\u5206\u9694\uFF0C\u4F8B\u5982 code, research\u3002");
1299
+ }
1300
+ profile.specialties = specialties;
1301
+ }
1302
+ const cliModel = field(draft?.cliModel);
1303
+ if (cliModel) {
1304
+ if (!/^[A-Za-z0-9][A-Za-z0-9._:/-]{0,119}$/.test(cliModel)) throw new Error("CLI \u6A21\u578B\u540D\u9700\u7531\u5B57\u6BCD\u6570\u5B57\u5F00\u5934\uFF0C\u4E14\u53EA\u5305\u542B\u5B57\u6BCD\u3001\u6570\u5B57\u3001\u70B9\u3001\u4E0B\u5212\u7EBF\u3001\u5192\u53F7\u3001\u659C\u6760\u6216\u8FDE\u5B57\u7B26\u3002");
1305
+ profile.cliModel = cliModel;
1306
+ }
1307
+ return profile;
1308
+ }
1309
+ function updateProfileJson(rawJson, route, draft) {
1310
+ const profiles = parseModelProfilesJson(rawJson ?? "[]");
1311
+ const candidate = draft === null ? null : profileFromDraft(route, draft);
1312
+ const selected = profileRouteKey(route);
1313
+ const others = profiles.filter((profile) => profileRouteKey(profile) !== selected).map((profile) => ({
1314
+ ...profile,
1315
+ ...profile.quality === void 0 ? {} : { quality: Number((profile.quality * 100).toFixed(6)) }
1316
+ }));
1317
+ const next = candidate && Object.keys(candidate).length > 2 ? [...others, candidate] : others;
1318
+ const json = JSON.stringify(next, null, 2);
1319
+ parseModelProfilesJson(json);
1320
+ return json;
1321
+ }
1322
+
1323
+ // .dsh-plugin/client/model-profile-editor.jsx
1324
+ var messageOf = (error) => typeof error?.message === "string" ? error.message : "\u6A21\u578B\u914D\u7F6E\u4FDD\u5B58\u5931\u8D25\u3002";
1325
+ function ModelProfileEditor({ routes, settingsScope, onSaved }) {
1326
+ const [snapshot, setSnapshot] = import_react.default.useState(() => settingsScope.getSnapshot());
1327
+ const [selected, setSelected] = import_react.default.useState("");
1328
+ const [drafts, setDrafts] = import_react.default.useState({});
1329
+ const [saving, setSaving] = import_react.default.useState(false);
1330
+ const [notice, setNotice] = import_react.default.useState(null);
1331
+ import_react.default.useEffect(() => settingsScope.subscribe(() => setSnapshot(settingsScope.getSnapshot())), [settingsScope]);
1332
+ const route = routes.find((item) => profileRouteKey(item) === selected) ?? routes[0];
1333
+ const routeKey2 = route ? profileRouteKey(route) : "";
1334
+ const rawJson = snapshot.value?.modelProfilesJson ?? "[]";
1335
+ let profiles = [];
1336
+ let loadError = "";
1337
+ try {
1338
+ profiles = parseModelProfilesJson(rawJson);
1339
+ } catch (error) {
1340
+ loadError = messageOf(error);
1341
+ }
1342
+ const saved = profiles.find((item) => profileRouteKey(item) === routeKey2);
1343
+ const pending = drafts[routeKey2];
1344
+ const draft = pending?.fields ?? profileDraft(saved);
1345
+ const writable = snapshot.status === "ready" && snapshot.writable === true && !saving && !loadError;
1346
+ const unmatched = profiles.filter((item) => !routes.some((route2) => profileRouteKey(route2) === profileRouteKey(item))).length;
1347
+ const edit = (name, value) => {
1348
+ if (!route || !writable) return;
1349
+ setDrafts((previous) => {
1350
+ const previousEntry = previous[routeKey2] ?? { fields: profileDraft(saved), baseRevision: snapshot.revision };
1351
+ return { ...previous, [routeKey2]: { ...previousEntry, fields: { ...previousEntry.fields, [name]: value } } };
1352
+ });
1353
+ setNotice(null);
1354
+ };
1355
+ const write = async (remove) => {
1356
+ if (!route || !writable) return;
1357
+ const revision = pending?.baseRevision ?? snapshot.revision;
1358
+ if (revision !== snapshot.revision) {
1359
+ setNotice({ tone: "error", text: "\u8BBE\u7F6E\u5DF2\u5728\u5176\u4ED6\u9875\u9762\u66F4\u65B0\u3002\u5F53\u524D\u8F93\u5165\u4ECD\u5728\uFF1B\u8BF7\u6838\u5BF9\u6700\u65B0\u5185\u5BB9\uFF0C\u518D\u9009\u62E9\u201C\u91CD\u65B0\u52A0\u8F7D\u6B64\u8DEF\u7EBF\u201D\u540E\u7F16\u8F91\u3002" });
1360
+ return;
1361
+ }
1362
+ let nextJson;
1363
+ try {
1364
+ nextJson = updateProfileJson(rawJson, route, remove ? null : draft);
1365
+ } catch (error) {
1366
+ setNotice({ tone: "error", text: messageOf(error) });
1367
+ return;
1368
+ }
1369
+ setSaving(true);
1370
+ setNotice(null);
1371
+ try {
1372
+ const accepted = await settingsScope.mutate([{ op: "set", path: ["modelProfilesJson"], value: nextJson }], revision);
1373
+ if (!accepted) throw new Error("\u8BBE\u7F6E\u672A\u88AB\u4FDD\u5B58\uFF0C\u53EF\u80FD\u88AB\u5176\u4ED6\u9875\u9762\u4FEE\u6539\u3002\u8F93\u5165\u5DF2\u4FDD\u7559\uFF0C\u8BF7\u91CD\u65B0\u52A0\u8F7D\u540E\u6838\u5BF9\u3002");
1374
+ setDrafts((previous) => {
1375
+ const next = { ...previous };
1376
+ delete next[routeKey2];
1377
+ return next;
1378
+ });
1379
+ setNotice({ tone: "success", text: remove ? "\u5DF2\u5220\u9664\u8BE5\u6A21\u578B\u7684\u81EA\u62A5\u914D\u7F6E\u3002" : "\u8BE5\u6A21\u578B\u914D\u7F6E\u5DF2\u4FDD\u5B58\uFF1B\u91CD\u65B0\u751F\u6210\u5EFA\u8BAE\u5373\u53EF\u4F7F\u7528\u3002" });
1380
+ onSaved?.();
1381
+ } catch (error) {
1382
+ setNotice({ tone: "error", text: messageOf(error) });
1383
+ } finally {
1384
+ setSaving(false);
1385
+ }
1386
+ };
1387
+ const reload = () => {
1388
+ setDrafts((previous) => {
1389
+ const next = { ...previous };
1390
+ delete next[routeKey2];
1391
+ return next;
1392
+ });
1393
+ setNotice(null);
1394
+ };
1395
+ return /* @__PURE__ */ import_react.default.createElement("section", { className: "mr-card mr-profile-card", "aria-label": "\u9010\u6A21\u578B\u4EF7\u683C\u4E0E\u80FD\u529B\u914D\u7F6E" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-card-head" }, /* @__PURE__ */ import_react.default.createElement("div", null, /* @__PURE__ */ import_react.default.createElement("h2", { className: "mr-card-title" }, "\u9010\u6A21\u578B\u4EF7\u683C\u4E0E\u80FD\u529B"), /* @__PURE__ */ import_react.default.createElement("p", { className: "mr-card-copy" }, "\u9009\u62E9\u5B98\u65B9\u6A21\u578B\u76EE\u5F55\u4E2D\u7684\u51C6\u786E\u8DEF\u7EBF\uFF0C\u586B\u5199\u4F60\u638C\u63E1\u7684\u8D28\u91CF\u8BC4\u5206\u4E0E\u5355\u4EF7\u3002\u914D\u7F6E\u7531\u4F60\u63D0\u4F9B\uFF0C\u63D2\u4EF6\u4E0D\u4F1A\u8BFB\u53D6\u8D26\u53F7\u5BC6\u94A5\u3002"))), /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-card-body" }, loadError && /* @__PURE__ */ import_react.default.createElement("p", { className: "mr-error", role: "alert" }, "\u5DF2\u6709\u914D\u7F6E\u65E0\u6CD5\u89E3\u6790\uFF1A", loadError, "\u3002\u8BF7\u5148\u5728\u63D2\u4EF6\u8BBE\u7F6E\u9875\u4FEE\u6B63 JSON\u3002"), snapshot.status !== "ready" && /* @__PURE__ */ import_react.default.createElement("p", { className: "mr-caption" }, "\u8BBE\u7F6E\u72B6\u6001\uFF1A", snapshot.status === "loading" ? "\u6B63\u5728\u52A0\u8F7D" : "\u5F53\u524D\u4E0D\u53EF\u7528"), snapshot.status === "ready" && !snapshot.writable && /* @__PURE__ */ import_react.default.createElement("p", { className: "mr-error", role: "status" }, "\u5F53\u524D\u8BBE\u7F6E\u4E3A\u53EA\u8BFB\uFF0C\u8BF7\u5728\u53EF\u5199\u7684\u672C\u673A\u73AF\u5883\u914D\u7F6E\u3002"), routes.length === 0 ? /* @__PURE__ */ import_react.default.createElement("p", { className: "mr-empty" }, "\u8BF7\u5148\u5728 DeepSeek Harness \u7684\u201C\u6A21\u578B\u201D\u9875\u6DFB\u52A0\u6A21\u578B\uFF0C\u518D\u8FD4\u56DE\u8FD9\u91CC\u586B\u5199\u4EF7\u683C\u4E0E\u80FD\u529B\u3002") : /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, /* @__PURE__ */ import_react.default.createElement("label", { className: "mr-label", htmlFor: "mr-profile-route" }, "\u6A21\u578B\u8DEF\u7EBF"), /* @__PURE__ */ import_react.default.createElement("select", { className: "mr-input", id: "mr-profile-route", value: routeKey2, onChange: (event) => {
1396
+ setSelected(event.target.value);
1397
+ setNotice(null);
1398
+ } }, routes.map((item) => /* @__PURE__ */ import_react.default.createElement("option", { key: profileRouteKey(item), value: profileRouteKey(item) }, item.provider, "/", item.model))), /* @__PURE__ */ import_react.default.createElement("p", { className: "mr-caption mr-profile-state" }, saved ? `\u5DF2\u914D\u7F6E${saved.pricing ? "\u5355\u4EF7" : "\u80FD\u529B\uFF0C\u4EF7\u683C\u672A\u77E5"}` : "\u672A\u914D\u7F6E\uFF0C\u4EF7\u683C\u4E0E\u8D28\u91CF\u6765\u6E90\u672A\u77E5", pending ? " \xB7 \u5F53\u524D\u6709\u672A\u4FDD\u5B58\u8F93\u5165" : "", unmatched > 0 ? ` \xB7 \u53E6\u6709 ${unmatched} \u6761\u914D\u7F6E\u4E0D\u5728\u5F53\u524D\u6A21\u578B\u76EE\u5F55\u4E2D\uFF0C\u4FDD\u5B58\u65F6\u4F1A\u4FDD\u7559` : ""), /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-profile-grid" }, /* @__PURE__ */ import_react.default.createElement("label", { className: "mr-profile-field" }, /* @__PURE__ */ import_react.default.createElement("span", null, "\u8D28\u91CF\u8BC4\u5206\uFF080\u2013100\uFF0C\u81EA\u62A5\uFF09"), /* @__PURE__ */ import_react.default.createElement("input", { className: "mr-input", type: "number", min: "0", max: "100", step: "any", value: draft.quality, disabled: !writable, onChange: (event) => edit("quality", event.target.value), placeholder: "\u4F8B\u5982 85\uFF1B\u7559\u7A7A\u8868\u793A\u672A\u77E5" })), /* @__PURE__ */ import_react.default.createElement("label", { className: "mr-profile-field" }, /* @__PURE__ */ import_react.default.createElement("span", null, "\u8F93\u5165\u5355\u4EF7\uFF08USD / \u767E\u4E07 token\uFF09"), /* @__PURE__ */ import_react.default.createElement("input", { className: "mr-input", type: "number", min: "0", step: "any", value: draft.input, disabled: !writable, onChange: (event) => edit("input", event.target.value), placeholder: "\u7559\u7A7A\u8868\u793A\u672A\u77E5" })), /* @__PURE__ */ import_react.default.createElement("label", { className: "mr-profile-field" }, /* @__PURE__ */ import_react.default.createElement("span", null, "\u8F93\u51FA\u5355\u4EF7\uFF08USD / \u767E\u4E07 token\uFF09"), /* @__PURE__ */ import_react.default.createElement("input", { className: "mr-input", type: "number", min: "0", step: "any", value: draft.output, disabled: !writable, onChange: (event) => edit("output", event.target.value), placeholder: "\u7559\u7A7A\u8868\u793A\u672A\u77E5" })), /* @__PURE__ */ import_react.default.createElement("label", { className: "mr-profile-field" }, /* @__PURE__ */ import_react.default.createElement("span", null, "\u64C5\u957F\u65B9\u5411\uFF08\u82F1\u6587\u6807\u7B7E\uFF0C\u9017\u53F7\u5206\u9694\uFF09"), /* @__PURE__ */ import_react.default.createElement("input", { className: "mr-input", type: "text", value: draft.specialties, disabled: !writable, onChange: (event) => edit("specialties", event.target.value), placeholder: PROFILE_SPECIALTY_HINT })), /* @__PURE__ */ import_react.default.createElement("label", { className: "mr-profile-field" }, /* @__PURE__ */ import_react.default.createElement("span", null, "\u5B98\u65B9 CLI \u6A21\u578B\u540D\uFF08\u53EF\u9009\uFF09"), /* @__PURE__ */ import_react.default.createElement("input", { className: "mr-input", type: "text", value: draft.cliModel, disabled: !writable, onChange: (event) => edit("cliModel", event.target.value), placeholder: "\u4EC5\u5728\u5382\u5546 CLI \u652F\u6301\u8BE5\u51C6\u786E\u540D\u79F0\u65F6\u586B\u5199" }))), /* @__PURE__ */ import_react.default.createElement("details", { className: "mr-profile-advanced" }, /* @__PURE__ */ import_react.default.createElement("summary", null, "\u7F13\u5B58\u5355\u4EF7\uFF08\u53EF\u9009\uFF09"), /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-profile-grid" }, /* @__PURE__ */ import_react.default.createElement("label", { className: "mr-profile-field" }, /* @__PURE__ */ import_react.default.createElement("span", null, "\u7F13\u5B58\u8BFB\u53D6\uFF08USD / \u767E\u4E07 token\uFF09"), /* @__PURE__ */ import_react.default.createElement("input", { className: "mr-input", type: "number", min: "0", step: "any", value: draft.cacheRead, disabled: !writable, onChange: (event) => edit("cacheRead", event.target.value), placeholder: "\u7559\u7A7A\u6309\u666E\u901A\u8F93\u5165\u4EF7\u683C\u4F30\u7B97" })), /* @__PURE__ */ import_react.default.createElement("label", { className: "mr-profile-field" }, /* @__PURE__ */ import_react.default.createElement("span", null, "\u7F13\u5B58\u5199\u5165\uFF08USD / \u767E\u4E07 token\uFF09"), /* @__PURE__ */ import_react.default.createElement("input", { className: "mr-input", type: "number", min: "0", step: "any", value: draft.cacheWrite, disabled: !writable, onChange: (event) => edit("cacheWrite", event.target.value), placeholder: "\u7559\u7A7A\u6309\u666E\u901A\u8F93\u5165\u4EF7\u683C\u4F30\u7B97" })))), /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-actions" }, /* @__PURE__ */ import_react.default.createElement("button", { className: "mr-button", type: "button", disabled: !writable || !pending, onClick: () => {
1399
+ void write(false);
1400
+ } }, saving ? "\u4FDD\u5B58\u4E2D\u2026" : "\u4FDD\u5B58\u6B64\u6A21\u578B"), /* @__PURE__ */ import_react.default.createElement("button", { className: "mr-button mr-button-secondary", type: "button", disabled: !pending || saving, onClick: reload }, "\u91CD\u65B0\u52A0\u8F7D\u6B64\u8DEF\u7EBF"), saved && /* @__PURE__ */ import_react.default.createElement("button", { className: "mr-button mr-button-secondary", type: "button", disabled: !writable, onClick: () => {
1401
+ void write(true);
1402
+ } }, "\u5220\u9664\u6B64\u6A21\u578B\u914D\u7F6E")), notice && /* @__PURE__ */ import_react.default.createElement("p", { className: notice.tone === "error" ? "mr-error" : "mr-profile-success", role: notice.tone === "error" ? "alert" : "status" }, notice.text), /* @__PURE__ */ import_react.default.createElement("p", { className: "mr-caption" }, "\u8D28\u91CF\u8BC4\u5206\u548C\u4EF7\u683C\u90FD\u662F\u7528\u6237\u63D0\u4F9B\u7684\u4F30\u503C\u3002\u672A\u586B\u5199\u5355\u4EF7\u65F6\u663E\u793A\u201C\u4EF7\u683C\u5F85\u914D\u7F6E\u201D\uFF1B\u9884\u7B97\u53EA\u5F71\u54CD\u672C\u5730\u89C4\u5212\uFF0C\u4E0D\u9650\u5236\u5B9E\u9645\u8D26\u5355\u3002CLI \u6A21\u578B\u540D\u987B\u4E0E\u5382\u5546\u5DE5\u5177\u6838\u5BF9\u3002"))));
1403
+ }
1404
+
1405
+ // .dsh-plugin/client/tool-install-state.mjs
1406
+ function stableVersionOrder(left, right) {
1407
+ const parse = (value) => /^([0-9]+)\.([0-9]+)\.([0-9]+)$/.exec(String(value ?? ""));
1408
+ const current = parse(left);
1409
+ const target = parse(right);
1410
+ if (!current || !target) return null;
1411
+ for (let index = 1; index <= 3; index += 1) {
1412
+ const delta = Number(current[index]) - Number(target[index]);
1413
+ if (delta) return Math.sign(delta);
1414
+ }
1415
+ return 0;
1416
+ }
1417
+ function toolInstallAction({ tool, probe, readiness, job, probeStatus }) {
1418
+ const running = job?.status === "running";
1419
+ const versionOrder = probe?.installed ? stableVersionOrder(probe.version, tool.version) : null;
1420
+ const currentAndReady = versionOrder === 0 && readiness?.ready === true;
1421
+ const repairable = versionOrder === 0 && readiness?.ready === false;
1422
+ const newerOrUncertain = probe?.installed && (versionOrder === null || versionOrder > 0);
1423
+ const label = running ? "\u5B89\u88C5\u4E2D\u2026" : currentAndReady ? "\u5DF2\u662F\u76EE\u6807\u7248\u672C" : repairable ? "\u4FEE\u590D\u5B98\u65B9\u6267\u884C\u5165\u53E3" : newerOrUncertain ? "\u8BF7\u4EBA\u5DE5\u6838\u5BF9\u7248\u672C" : probe?.installed ? "\u66F4\u65B0\u5230\u76EE\u6807\u7248\u672C" : job?.status === "failed" ? "\u91CD\u8BD5\u5B89\u88C5" : tool.manager === "signed-windows-installer" ? "\u4E0B\u8F7D\u5B89\u88C5\u5668" : "\u4E0B\u8F7D\u5B89\u88C5";
1424
+ return {
1425
+ label,
1426
+ disabled: probeStatus !== "ready" || running || currentAndReady || newerOrUncertain
1427
+ };
1428
+ }
1429
+
1430
+ // .dsh-plugin/client/router-main.jsx
1040
1431
  init_official_tool_registry();
1041
1432
 
1042
1433
  // .dsh-plugin/client/router-main.css
1043
- var router_main_default = ".mr-workspace {\n --mr-card: rgba(255, 255, 255, .94);\n --mr-ink: #182232;\n --mr-muted: #526174;\n --mr-line: rgba(38, 55, 75, .13);\n --mr-accent: #315cc8;\n --mr-soft: #edf3ff;\n box-sizing: border-box;\n width: 100%;\n height: 100%;\n min-height: 0;\n overflow-y: auto;\n background: rgba(246, 249, 253, .9);\n backdrop-filter: blur(18px);\n color: var(--mr-ink);\n font-family: inherit;\n}\n.mr-workspace *, .mr-workspace *::before, .mr-workspace *::after { box-sizing: border-box; }\n.mr-shell { width: min(1160px, 100%); margin: 0 auto; padding: 36px 32px 64px; }\n.mr-header { display: flex; flex-wrap: wrap; gap: 20px; align-items: end; justify-content: space-between; margin-bottom: 24px; }\n.mr-eyebrow { margin: 0 0 8px; color: var(--mr-accent); font-size: 11px; font-weight: 750; letter-spacing: .14em; text-transform: uppercase; }\n.mr-title { margin: 0; font-size: clamp(25px, 3vw, 34px); letter-spacing: -.035em; line-height: 1.16; }\n.mr-subtitle { margin: 10px 0 0; color: var(--mr-muted); font-size: 14px; line-height: 1.65; }\n.mr-status { display: inline-flex; align-items: center; gap: 8px; padding: 8px 12px; border: 1px solid var(--mr-line); border-radius: 999px; background: var(--mr-card); color: var(--mr-muted); font-size: 12px; white-space: nowrap; }\n.mr-status-dot { width: 7px; height: 7px; border-radius: 50%; background: #2cba83; }\n.mr-status-dot.loading { background: #e9a640; }\n.mr-status-dot.error { background: #d95360; }\n.mr-grid { display: grid; grid-template-columns: minmax(0, 1.35fr) minmax(280px, .85fr); gap: 18px; align-items: start; }\n.mr-card { min-width: 0; border: 1px solid var(--mr-line); border-radius: 18px; background: var(--mr-card); box-shadow: 0 12px 42px rgba(25, 45, 76, .08); }\n.mr-card-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 20px 22px 0; }\n.mr-card-title { margin: 0; font-size: 16px; font-weight: 700; }\n.mr-card-copy { margin: 5px 0 0; color: var(--mr-muted); font-size: 12px; line-height: 1.55; }\n.mr-card-body { padding: 18px 22px 22px; }\n.mr-label { display: block; margin: 0 0 8px; font-size: 12px; font-weight: 700; }\n.mr-textarea, .mr-input { width: 100%; border: 1px solid var(--mr-line); border-radius: 11px; background: #fff; color: var(--mr-ink); font: inherit; outline: none; }\n.mr-textarea { min-height: 154px; padding: 13px 14px; resize: vertical; line-height: 1.6; font-size: 14px; }\n.mr-input { min-height: 38px; padding: 8px 11px; font-size: 13px; }\n.mr-textarea:focus, .mr-input:focus { border-color: var(--mr-accent); box-shadow: 0 0 0 3px rgba(49, 92, 200, .13); }\n.mr-controls { display: flex; flex-wrap: wrap; align-items: end; justify-content: space-between; gap: 14px; margin-top: 17px; }\n.mr-control-group { display: flex; flex-direction: column; gap: 7px; }\n.mr-control-label { color: var(--mr-muted); font-size: 11px; font-weight: 650; }\n.mr-segment { display: inline-flex; padding: 3px; border-radius: 10px; background: #edf1f7; }\n.mr-segment button { border: 0; border-radius: 8px; padding: 8px 12px; background: transparent; color: var(--mr-muted); font: inherit; font-size: 12px; cursor: pointer; }\n.mr-segment button[aria-pressed='true'] { background: #fff; color: var(--mr-ink); box-shadow: 0 2px 8px rgba(20, 35, 56, .1); font-weight: 700; }\n.mr-budget { width: 140px; }\n.mr-actions { display: flex; flex-wrap: wrap; gap: 9px; align-items: center; margin-top: 18px; }\n.mr-button { border: 1px solid transparent; border-radius: 10px; min-height: 38px; padding: 8px 14px; background: var(--mr-accent); color: #fff; font: inherit; font-size: 13px; font-weight: 700; cursor: pointer; }\n.mr-button:hover:not(:disabled) { filter: brightness(.94); }\n.mr-button:disabled { cursor: not-allowed; opacity: .5; }\n.mr-button-secondary { border-color: var(--mr-line); background: #fff; color: var(--mr-ink); }\n.mr-caption { color: var(--mr-muted); font-size: 11px; line-height: 1.5; }\n.mr-search { margin-top: 14px; }\n.mr-list { display: grid; gap: 8px; margin-top: 13px; max-height: 395px; overflow-y: auto; }\n.mr-route { display: flex; justify-content: space-between; gap: 10px; padding: 10px 11px; border: 1px solid var(--mr-line); border-radius: 10px; background: #f9fbfe; }\n.mr-route-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 12px; font-weight: 700; }\n.mr-route-provider { margin-top: 2px; color: var(--mr-muted); font-size: 10px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.mr-pill { align-self: start; flex: none; padding: 3px 7px; border-radius: 7px; background: var(--mr-soft); color: var(--mr-accent); font-size: 10px; font-weight: 700; }\n.mr-empty, .mr-error { padding: 14px; border-radius: 10px; background: #f4f6fa; color: var(--mr-muted); font-size: 12px; line-height: 1.6; }\n.mr-error { background: #fff0f1; color: #a52d3c; }\n.mr-results { margin-top: 18px; }\n.mr-result-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 10px; margin-bottom: 16px; }\n.mr-metric { padding: 13px; border-radius: 12px; background: #f5f8fd; }\n.mr-metric-label { color: var(--mr-muted); font-size: 10px; }\n.mr-metric-value { margin-top: 5px; font-size: 15px; font-weight: 750; word-break: break-word; }\n.mr-section-title { margin: 20px 0 10px; font-size: 13px; font-weight: 750; }\n.mr-package { padding: 13px 14px; border: 1px solid var(--mr-line); border-radius: 11px; background: #fafcff; }\n.mr-package + .mr-package { margin-top: 8px; }\n.mr-package-top { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 8px; align-items: center; }\n.mr-package-name { font-size: 12px; font-weight: 750; }\n.mr-package-route { color: var(--mr-accent); font-size: 11px; font-weight: 700; }\n.mr-package-copy { margin: 7px 0 0; color: var(--mr-muted); font-size: 11px; line-height: 1.6; }\n.mr-notice { margin-top: 18px; padding: 14px 16px; border: 1px solid rgba(49, 92, 200, .16); border-radius: 12px; background: #eff4ff; color: #354d7a; font-size: 11px; line-height: 1.65; }\n.mr-notice code { font-family: ui-monospace, SFMono-Regular, Consolas, monospace; font-size: 11px; }\n@media (max-width: 850px) { .mr-grid { grid-template-columns: 1fr; } .mr-shell { padding: 24px 18px 48px; } }\n@media (prefers-color-scheme: dark) {\n .mr-workspace { --mr-card: rgba(30, 38, 51, .94); --mr-ink: #ecf2ff; --mr-muted: #aebbd0; --mr-line: rgba(210, 224, 245, .15); --mr-accent: #91adff; --mr-soft: rgba(97, 132, 222, .18); background: rgba(18, 23, 32, .91); }\n .mr-textarea, .mr-input, .mr-segment button[aria-pressed='true'], .mr-button-secondary { background: #273245; color: var(--mr-ink); }\n .mr-segment, .mr-empty, .mr-metric { background: #222d3e; }\n .mr-route, .mr-package { background: #222d3e; }\n .mr-notice { background: #243454; color: #c9d6fa; }\n .mr-error { background: #4a2730; color: #ffd4da; }\n}\n\n/* \u5B98\u65B9\u5DE5\u5177\u5361\u7247 */\n.mr-tools { display: grid; gap: 9px; }\n.mr-tool { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 14px; border: 1px solid var(--mr-line); border-radius: 11px; background: var(--mr-card); }\n.mr-tool-info { min-width: 0; flex: 1; }\n.mr-tool-status { display: flex; align-items: center; gap: 6px; margin-top: 7px; color: var(--mr-muted); font-size: 11px; }\n.mr-tool-dot { width: 7px; height: 7px; flex: none; border-radius: 50%; background: #8d9aaa; }\n.mr-tool-dot.installed { background: #2cba83; }\n.mr-tool-dot.running { background: #e9a640; }\n.mr-tool-command { display: block; margin-top: 6px; padding: 4px 8px; font-size: 12px; background: var(--dsw-alias-markdown-code-block); border-radius: 6px; overflow-wrap: anywhere; }\n.mr-tool-button { flex-shrink: 0; }\n.mr-tool-actions { display: flex; align-items: center; justify-content: flex-end; gap: 8px; flex-wrap: wrap; }\n.mr-tool-detail { margin: 7px 0 0; }\n.mr-tool-error { margin: 8px 0 0; padding: 9px 11px; }\n.mr-tool-log { margin-top: 7px; color: var(--mr-muted); font-size: 11px; }\n.mr-tool-log summary { cursor: pointer; }\n.mr-tool-log pre { max-height: 140px; overflow: auto; padding: 8px; border-radius: 7px; background: var(--dsw-alias-markdown-code-block); font-size: 10px; white-space: pre-wrap; overflow-wrap: anywhere; }\n.mr-channel-line { display: flex; align-items: center; gap: 8px; margin: 8px 0; flex-wrap: wrap; }\n.mr-pill-channel-ok { border-color: var(--dsw-alias-success, #2f9e63); color: var(--dsw-alias-success, #2f9e63); }\n@media (max-width: 560px) { .mr-tool { align-items: stretch; flex-direction: column; gap: 10px; } .mr-tool-button { align-self: flex-start; } }\n";
1434
+ var router_main_default = ".mr-workspace {\r\n --mr-card: rgba(255, 255, 255, .94);\r\n --mr-ink: #182232;\r\n --mr-muted: #526174;\r\n --mr-line: rgba(38, 55, 75, .13);\r\n --mr-accent: #315cc8;\r\n --mr-soft: #edf3ff;\r\n box-sizing: border-box;\r\n width: 100%;\r\n height: 100%;\r\n min-height: 0;\r\n overflow-y: auto;\r\n background: rgba(246, 249, 253, .9);\r\n backdrop-filter: blur(18px);\r\n color: var(--mr-ink);\r\n font-family: inherit;\r\n}\r\n.mr-workspace *, .mr-workspace *::before, .mr-workspace *::after { box-sizing: border-box; }\r\n.mr-shell { width: min(1160px, 100%); margin: 0 auto; padding: 36px 32px 64px; }\r\n.mr-header { display: flex; flex-wrap: wrap; gap: 20px; align-items: end; justify-content: space-between; margin-bottom: 24px; }\r\n.mr-eyebrow { margin: 0 0 8px; color: var(--mr-accent); font-size: 11px; font-weight: 750; letter-spacing: .14em; text-transform: uppercase; }\r\n.mr-title { margin: 0; font-size: clamp(25px, 3vw, 34px); letter-spacing: -.035em; line-height: 1.16; }\r\n.mr-subtitle { margin: 10px 0 0; color: var(--mr-muted); font-size: 14px; line-height: 1.65; }\r\n.mr-status { display: inline-flex; align-items: center; gap: 8px; padding: 8px 12px; border: 1px solid var(--mr-line); border-radius: 999px; background: var(--mr-card); color: var(--mr-muted); font-size: 12px; white-space: nowrap; }\r\n.mr-status-dot { width: 7px; height: 7px; border-radius: 50%; background: #2cba83; }\r\n.mr-status-dot.loading { background: #e9a640; }\r\n.mr-status-dot.error { background: #d95360; }\r\n.mr-grid { display: grid; grid-template-columns: minmax(0, 1.35fr) minmax(280px, .85fr); gap: 18px; align-items: start; }\r\n.mr-card { min-width: 0; border: 1px solid var(--mr-line); border-radius: 18px; background: var(--mr-card); box-shadow: 0 12px 42px rgba(25, 45, 76, .08); }\r\n.mr-card-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 20px 22px 0; }\r\n.mr-card-title { margin: 0; font-size: 16px; font-weight: 700; }\r\n.mr-card-copy { margin: 5px 0 0; color: var(--mr-muted); font-size: 12px; line-height: 1.55; }\r\n.mr-card-body { padding: 18px 22px 22px; }\r\n.mr-label { display: block; margin: 0 0 8px; font-size: 12px; font-weight: 700; }\r\n.mr-textarea, .mr-input { width: 100%; border: 1px solid var(--mr-line); border-radius: 11px; background: #fff; color: var(--mr-ink); font: inherit; outline: none; }\r\n.mr-textarea { min-height: 154px; padding: 13px 14px; resize: vertical; line-height: 1.6; font-size: 14px; }\r\n.mr-input { min-height: 38px; padding: 8px 11px; font-size: 13px; }\r\n.mr-textarea:focus, .mr-input:focus { border-color: var(--mr-accent); box-shadow: 0 0 0 3px rgba(49, 92, 200, .13); }\r\n.mr-controls { display: flex; flex-wrap: wrap; align-items: end; justify-content: space-between; gap: 14px; margin-top: 17px; }\r\n.mr-control-group { display: flex; flex-direction: column; gap: 7px; }\r\n.mr-control-label { color: var(--mr-muted); font-size: 11px; font-weight: 650; }\r\n.mr-segment { display: inline-flex; padding: 3px; border-radius: 10px; background: #edf1f7; }\r\n.mr-segment button { border: 0; border-radius: 8px; padding: 8px 12px; background: transparent; color: var(--mr-muted); font: inherit; font-size: 12px; cursor: pointer; }\r\n.mr-segment button[aria-pressed='true'] { background: #fff; color: var(--mr-ink); box-shadow: 0 2px 8px rgba(20, 35, 56, .1); font-weight: 700; }\r\n.mr-budget { width: 140px; }\r\n.mr-actions { display: flex; flex-wrap: wrap; gap: 9px; align-items: center; margin-top: 18px; }\r\n.mr-button { border: 1px solid transparent; border-radius: 10px; min-height: 38px; padding: 8px 14px; background: var(--mr-accent); color: #fff; font: inherit; font-size: 13px; font-weight: 700; cursor: pointer; }\r\n.mr-button:hover:not(:disabled) { filter: brightness(.94); }\r\n.mr-button:disabled { cursor: not-allowed; opacity: .5; }\r\n.mr-button-secondary { border-color: var(--mr-line); background: #fff; color: var(--mr-ink); }\r\n.mr-caption { color: var(--mr-muted); font-size: 11px; line-height: 1.5; }\r\n.mr-search { margin-top: 14px; }\r\n.mr-list { display: grid; gap: 8px; margin-top: 13px; max-height: 395px; overflow-y: auto; }\r\n.mr-route { display: flex; justify-content: space-between; gap: 10px; padding: 10px 11px; border: 1px solid var(--mr-line); border-radius: 10px; background: #f9fbfe; }\r\n.mr-route-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 12px; font-weight: 700; }\r\n.mr-route-provider { margin-top: 2px; color: var(--mr-muted); font-size: 10px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\r\n.mr-pill { align-self: start; flex: none; padding: 3px 7px; border-radius: 7px; background: var(--mr-soft); color: var(--mr-accent); font-size: 10px; font-weight: 700; }\r\n.mr-empty, .mr-error { padding: 14px; border-radius: 10px; background: #f4f6fa; color: var(--mr-muted); font-size: 12px; line-height: 1.6; }\r\n.mr-error { background: #fff0f1; color: #a52d3c; }\r\n.mr-results { margin-top: 18px; }\r\n.mr-result-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 10px; margin-bottom: 16px; }\r\n.mr-metric { padding: 13px; border-radius: 12px; background: #f5f8fd; }\r\n.mr-metric-label { color: var(--mr-muted); font-size: 10px; }\r\n.mr-metric-value { margin-top: 5px; font-size: 15px; font-weight: 750; word-break: break-word; }\r\n.mr-section-title { margin: 20px 0 10px; font-size: 13px; font-weight: 750; }\r\n.mr-package { padding: 13px 14px; border: 1px solid var(--mr-line); border-radius: 11px; background: #fafcff; }\r\n.mr-package + .mr-package { margin-top: 8px; }\r\n.mr-package-top { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 8px; align-items: center; }\r\n.mr-package-name { font-size: 12px; font-weight: 750; }\r\n.mr-package-route { color: var(--mr-accent); font-size: 11px; font-weight: 700; }\r\n.mr-package-copy { margin: 7px 0 0; color: var(--mr-muted); font-size: 11px; line-height: 1.6; }\r\n.mr-notice { margin-top: 18px; padding: 14px 16px; border: 1px solid rgba(49, 92, 200, .16); border-radius: 12px; background: #eff4ff; color: #354d7a; font-size: 11px; line-height: 1.65; }\r\n.mr-notice code { font-family: ui-monospace, SFMono-Regular, Consolas, monospace; font-size: 11px; }\r\n@media (max-width: 850px) { .mr-grid { grid-template-columns: 1fr; } .mr-shell { padding: 24px 18px 48px; } }\r\n@media (prefers-color-scheme: dark) {\r\n .mr-workspace { --mr-card: rgba(30, 38, 51, .94); --mr-ink: #ecf2ff; --mr-muted: #aebbd0; --mr-line: rgba(210, 224, 245, .15); --mr-accent: #91adff; --mr-soft: rgba(97, 132, 222, .18); background: rgba(18, 23, 32, .91); }\r\n .mr-textarea, .mr-input, .mr-segment button[aria-pressed='true'], .mr-button-secondary { background: #273245; color: var(--mr-ink); }\r\n .mr-segment, .mr-empty, .mr-metric { background: #222d3e; }\r\n .mr-route, .mr-package { background: #222d3e; }\r\n .mr-notice { background: #243454; color: #c9d6fa; }\r\n .mr-error { background: #4a2730; color: #ffd4da; }\r\n}\r\n\r\n/* \u5B98\u65B9\u5DE5\u5177\u5361\u7247 */\r\n.mr-tools { display: grid; gap: 9px; }\r\n.mr-tool { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 14px; border: 1px solid var(--mr-line); border-radius: 11px; background: var(--mr-card); }\r\n.mr-tool-info { min-width: 0; flex: 1; }\r\n.mr-tool-status { display: flex; align-items: center; gap: 6px; margin-top: 7px; color: var(--mr-muted); font-size: 11px; }\r\n.mr-tool-dot { width: 7px; height: 7px; flex: none; border-radius: 50%; background: #8d9aaa; }\r\n.mr-tool-dot.installed { background: #2cba83; }\r\n.mr-tool-dot.running { background: #e9a640; }\r\n.mr-tool-command { display: block; margin-top: 6px; padding: 4px 8px; font-size: 12px; background: var(--dsw-alias-markdown-code-block); border-radius: 6px; overflow-wrap: anywhere; }\r\n.mr-tool-button { flex-shrink: 0; }\r\n.mr-tool-actions { display: flex; align-items: center; justify-content: flex-end; gap: 8px; flex-wrap: wrap; }\r\n.mr-tool-detail { margin: 7px 0 0; }\r\n.mr-tool-error { margin: 8px 0 0; padding: 9px 11px; }\r\n.mr-tool-log { margin-top: 7px; color: var(--mr-muted); font-size: 11px; }\r\n.mr-tool-log summary { cursor: pointer; }\r\n.mr-tool-log pre { max-height: 140px; overflow: auto; padding: 8px; border-radius: 7px; background: var(--dsw-alias-markdown-code-block); font-size: 10px; white-space: pre-wrap; overflow-wrap: anywhere; }\r\n.mr-channel-line { display: flex; align-items: center; gap: 8px; margin: 8px 0; flex-wrap: wrap; }\r\n.mr-pill-channel-ok { border-color: var(--dsw-alias-success, #2f9e63); color: var(--dsw-alias-success, #2f9e63); }\n.mr-profile-card { margin-top: 18px; }\n.mr-profile-state { display: block; margin: 8px 0 0; }\n.mr-profile-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: 12px; margin-top: 15px; }\n.mr-profile-field { display: grid; align-content: start; gap: 6px; color: var(--mr-muted); font-size: 11px; font-weight: 650; }\n.mr-profile-field .mr-input { color: var(--mr-ink); }\n.mr-profile-advanced { margin-top: 14px; color: var(--mr-muted); font-size: 11px; }\n.mr-profile-advanced summary { cursor: pointer; }\n.mr-profile-success { padding: 10px 12px; border-radius: 10px; background: #e5f5eb; color: #175f3a; font-size: 12px; }\n@media (prefers-color-scheme: dark) { .mr-profile-success { background: #193b2b; color: #b4f1cc; } }\n@media (max-width: 560px) { .mr-tool { align-items: stretch; flex-direction: column; gap: 10px; } .mr-tool-button { align-self: flex-start; } }\n";
1044
1435
 
1045
1436
  // .dsh-plugin/client/router-main.jsx
1046
- var money = (value) => `$${Number(value || 0).toFixed(4)}`;
1437
+ var money = (value) => value === null || value === void 0 ? "\u4EF7\u683C\u5F85\u914D\u7F6E" : `$${Number(value).toFixed(4)}`;
1047
1438
  var text = (value) => typeof value === "string" ? value.trim() : "";
1048
1439
  function RouterPanelIcon({ size = 20, active = false }) {
1049
- return /* @__PURE__ */ import_react.default.createElement("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true" }, /* @__PURE__ */ import_react.default.createElement("path", { d: "M7 6.5h7M7 17.5h7M15 6.5v11", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round" }), /* @__PURE__ */ import_react.default.createElement("circle", { cx: "5", cy: "6.5", r: "2", fill: active ? "currentColor" : "none", stroke: "currentColor", strokeWidth: "1.6" }), /* @__PURE__ */ import_react.default.createElement("circle", { cx: "5", cy: "17.5", r: "2", fill: active ? "currentColor" : "none", stroke: "currentColor", strokeWidth: "1.6" }), /* @__PURE__ */ import_react.default.createElement("circle", { cx: "17", cy: "12", r: "3", fill: active ? "currentColor" : "none", stroke: "currentColor", strokeWidth: "1.7" }));
1440
+ return /* @__PURE__ */ import_react2.default.createElement("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true" }, /* @__PURE__ */ import_react2.default.createElement("path", { d: "M7 6.5h7M7 17.5h7M15 6.5v11", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round" }), /* @__PURE__ */ import_react2.default.createElement("circle", { cx: "5", cy: "6.5", r: "2", fill: active ? "currentColor" : "none", stroke: "currentColor", strokeWidth: "1.6" }), /* @__PURE__ */ import_react2.default.createElement("circle", { cx: "5", cy: "17.5", r: "2", fill: active ? "currentColor" : "none", stroke: "currentColor", strokeWidth: "1.6" }), /* @__PURE__ */ import_react2.default.createElement("circle", { cx: "17", cy: "12", r: "3", fill: active ? "currentColor" : "none", stroke: "currentColor", strokeWidth: "1.7" }));
1050
1441
  }
1051
1442
  function RouteList({ routes, query }) {
1052
1443
  const filtered = routes.filter((route) => {
1053
1444
  const haystack = `${route.providerName} ${route.provider} ${route.name} ${route.model}`.toLowerCase();
1054
1445
  return haystack.includes(query.toLowerCase());
1055
1446
  });
1056
- if (filtered.length === 0) return /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-empty" }, routes.length === 0 ? "\u5B98\u65B9\u6A21\u578B\u76EE\u5F55\u5C1A\u65E0\u53EF\u89C4\u5212\u7684\u8DEF\u7EBF\u3002\u8BF7\u5148\u5728\u201C\u6A21\u578B\u201D\u9875\u5B8C\u6210\u914D\u7F6E\u3002" : "\u6CA1\u6709\u5339\u914D\u7684\u6A21\u578B\u3002");
1057
- return /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-list", role: "list", "aria-label": "\u5B98\u65B9\u5DF2\u767B\u8BB0\u6A21\u578B\u8DEF\u7EBF" }, filtered.map((route) => /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-route", role: "listitem", key: `${route.provider}/${route.model}` }, /* @__PURE__ */ import_react.default.createElement("div", { style: { minWidth: 0 } }, /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-route-name", title: route.name }, route.name), /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-route-provider", title: `${route.provider}/${route.model}` }, route.provider, "/", route.model)), route.reasoningKnown && /* @__PURE__ */ import_react.default.createElement("span", { className: "mr-pill" }, "\u63A8\u7406\u7B49\u7EA7"))));
1447
+ if (filtered.length === 0) return /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-empty" }, routes.length === 0 ? "\u5B98\u65B9\u6A21\u578B\u76EE\u5F55\u5C1A\u65E0\u53EF\u89C4\u5212\u7684\u8DEF\u7EBF\u3002\u8BF7\u5148\u5728\u201C\u6A21\u578B\u201D\u9875\u5B8C\u6210\u914D\u7F6E\u3002" : "\u6CA1\u6709\u5339\u914D\u7684\u6A21\u578B\u3002");
1448
+ return /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-list", role: "list", "aria-label": "\u5B98\u65B9\u5DF2\u767B\u8BB0\u6A21\u578B\u8DEF\u7EBF" }, filtered.map((route) => /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-route", role: "listitem", key: `${route.provider}/${route.model}` }, /* @__PURE__ */ import_react2.default.createElement("div", { style: { minWidth: 0 } }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-route-name", title: route.name }, route.name), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-route-provider", title: `${route.provider}/${route.model}` }, route.provider, "/", route.model)), route.reasoningKnown && /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-pill" }, "\u63A8\u7406\u7B49\u7EA7"))));
1058
1449
  }
1059
1450
  function ChannelBadge({ item }) {
1060
1451
  if (!item?.executionChannel) return null;
1061
1452
  const official = item.executionChannel === "official-cli";
1062
- return /* @__PURE__ */ import_react.default.createElement("span", { className: official ? "mr-pill mr-pill-channel-ok" : "mr-pill", title: item.channelDetail ?? "" }, official ? `\u5B98\u65B9 CLI \xB7 ${item.channelLabel ?? item.channelTool}` : "\u6A21\u578B\u76EE\u5F55 API");
1453
+ return /* @__PURE__ */ import_react2.default.createElement("span", { className: official ? "mr-pill mr-pill-channel-ok" : "mr-pill", title: item.channelDetail ?? "" }, official ? `\u5B98\u65B9 CLI \xB7 ${item.channelLabel ?? item.channelTool}` : "\u6A21\u578B\u76EE\u5F55 API");
1063
1454
  }
1064
1455
  function PlanResults({ plan }) {
1065
1456
  const selected = plan.selected;
1066
- return /* @__PURE__ */ import_react.default.createElement("section", { className: "mr-card mr-results", "aria-label": "\u8DEF\u7531\u5EFA\u8BAE" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-card-head" }, /* @__PURE__ */ import_react.default.createElement("div", null, /* @__PURE__ */ import_react.default.createElement("h2", { className: "mr-card-title" }, "\u8DEF\u7531\u5EFA\u8BAE"), /* @__PURE__ */ import_react.default.createElement("p", { className: "mr-card-copy" }, "\u672C\u5730\u8BA1\u7B97\u5B8C\u6210\uFF0C\u672A\u5411\u6A21\u578B\u53D1\u9001\u4EFB\u52A1\u5185\u5BB9\u3002"))), /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-card-body" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-result-grid" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-metric" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-metric-label" }, "\u63A8\u8350\u8DEF\u7EBF"), /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-metric-value" }, selected ? `${selected.provider}/${selected.model}` : "\u6682\u65E0\u8DEF\u7EBF")), /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-metric" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-metric-label" }, "\u4EFB\u52A1\u590D\u6742\u5EA6"), /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-metric-value" }, { simple: "\u7B80\u5355", balanced: "\u4E2D\u7B49", complex: "\u590D\u6742" }[plan.complexity.band] || plan.complexity.band)), /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-metric" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-metric-label" }, "\u4F30\u7B97\u603B\u6210\u672C"), /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-metric-value" }, money(plan.estimatedCost)))), /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-channel-line" }, /* @__PURE__ */ import_react.default.createElement("span", { className: "mr-control-label" }, "\u6267\u884C\u6E20\u9053"), /* @__PURE__ */ import_react.default.createElement(ChannelBadge, { item: plan })), /* @__PURE__ */ import_react.default.createElement("p", { className: "mr-caption" }, plan.reason), plan.optimization.budgetExceeded && /* @__PURE__ */ import_react.default.createElement("p", { className: "mr-error" }, "\u6309\u5F53\u524D\u5B9E\u9A8C\u4EF7\u683C\u4F30\u7B97\uFF0C\u4EFB\u52A1\u53EF\u80FD\u8D85\u8FC7\u672C\u6B21\u9884\u7B97\u3002\u9884\u7B97\u53EA\u5F71\u54CD\u5EFA\u8BAE\uFF0C\u4E0D\u4F1A\u963B\u6B62\u5B9E\u9645\u6263\u8D39\u3002"), plan.mode === "team" && /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, /* @__PURE__ */ import_react.default.createElement("h3", { className: "mr-section-title" }, "\u56E2\u961F\u5DE5\u4F5C\u5305"), /* @__PURE__ */ import_react.default.createElement("p", { className: "mr-caption" }, "\u4E0B\u65B9\u6A21\u578B\u662F\u89C4\u5212\u5EFA\u8BAE\uFF1B\u6258\u7BA1\u6267\u884C\u4F1A\u6309\u5382\u5546 CLI \u7684\u6A21\u578B\u540D\u89C4\u5219\u9009\u7528\uFF0C\u672A\u6838\u9A8C\u6620\u5C04\u65F6\u4F7F\u7528\u8BE5 CLI \u7684\u9ED8\u8BA4\u6A21\u578B\u3002"), plan.team.workPackages.length === 0 ? /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-empty" }, "\u5F53\u524D\u76EE\u5F55\u6CA1\u6709\u53EF\u5206\u914D\u7684\u6A21\u578B\u8DEF\u7EBF\u3002") : plan.team.workPackages.map((item, index) => /* @__PURE__ */ import_react.default.createElement("article", { className: "mr-package", key: item.id }, /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-package-top" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-package-name" }, index + 1, ". ", item.name), /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-package-route" }, item.recommendedProvider, "/", item.recommendedModel)), item.objective && /* @__PURE__ */ import_react.default.createElement("p", { className: "mr-package-copy" }, "\u5177\u4F53\u76EE\u6807\uFF1A", item.objective), /* @__PURE__ */ import_react.default.createElement("p", { className: "mr-package-copy" }, item.purpose, item.dependsOn.length > 0 ? ` \xB7 \u4F9D\u8D56\uFF1A${item.dependsOn.join("\u3001")}` : ""), /* @__PURE__ */ import_react.default.createElement("p", { className: "mr-package-copy" }, "\u9A8C\u6536\uFF1A", item.verificationChecklist.join("\uFF1B")), /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-channel-line" }, /* @__PURE__ */ import_react.default.createElement(ChannelBadge, { item }))))), plan.mode === "team" && /* @__PURE__ */ import_react.default.createElement("p", { className: "mr-caption" }, plan.team.handoff), /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-notice" }, plan.pricingNotice, " ", plan.availabilityNotice, " ", plan.modalityNotice || "")));
1457
+ return /* @__PURE__ */ import_react2.default.createElement("section", { className: "mr-card mr-results", "aria-label": "\u8DEF\u7531\u5EFA\u8BAE" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-card-head" }, /* @__PURE__ */ import_react2.default.createElement("div", null, /* @__PURE__ */ import_react2.default.createElement("h2", { className: "mr-card-title" }, "\u8DEF\u7531\u5EFA\u8BAE"), /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-card-copy" }, "\u672C\u5730\u8BA1\u7B97\u5B8C\u6210\uFF0C\u672A\u5411\u6A21\u578B\u53D1\u9001\u4EFB\u52A1\u5185\u5BB9\u3002"))), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-card-body" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-result-grid" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-metric" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-metric-label" }, "\u63A8\u8350\u8DEF\u7EBF"), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-metric-value" }, selected ? `${selected.provider}/${selected.model}` : "\u6682\u65E0\u8DEF\u7EBF")), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-metric" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-metric-label" }, "\u4EFB\u52A1\u590D\u6742\u5EA6"), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-metric-value" }, { simple: "\u7B80\u5355", balanced: "\u4E2D\u7B49", complex: "\u590D\u6742" }[plan.complexity.band] || plan.complexity.band)), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-metric" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-metric-label" }, "\u4F30\u7B97\u603B\u6210\u672C"), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-metric-value" }, money(plan.estimatedCost)))), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-channel-line" }, /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-control-label" }, "\u6267\u884C\u6E20\u9053"), /* @__PURE__ */ import_react2.default.createElement(ChannelBadge, { item: plan })), /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-caption" }, plan.reason), plan.optimization.budgetExceeded && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-error" }, "\u6309\u5DF2\u63D0\u4F9B\u5355\u4EF7\u4F30\u7B97\uFF0C\u4EFB\u52A1\u53EF\u80FD\u8D85\u8FC7\u672C\u6B21\u9884\u7B97\u3002\u9884\u7B97\u53EA\u5F71\u54CD\u5EFA\u8BAE\uFF0C\u4E0D\u4F1A\u963B\u6B62\u5B9E\u9645\u6263\u8D39\u3002"), plan.mode === "team" && /* @__PURE__ */ import_react2.default.createElement(import_react2.default.Fragment, null, /* @__PURE__ */ import_react2.default.createElement("h3", { className: "mr-section-title" }, "\u56E2\u961F\u5DE5\u4F5C\u5305"), /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-caption" }, "\u4E0B\u65B9\u6A21\u578B\u662F\u89C4\u5212\u5EFA\u8BAE\uFF1B\u6258\u7BA1\u6267\u884C\u4F1A\u6309\u5382\u5546 CLI \u7684\u6A21\u578B\u540D\u89C4\u5219\u9009\u7528\uFF0C\u672A\u6838\u9A8C\u6620\u5C04\u65F6\u4F7F\u7528\u8BE5 CLI \u7684\u9ED8\u8BA4\u6A21\u578B\u3002"), plan.team.workPackages.length === 0 ? /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-empty" }, "\u5F53\u524D\u76EE\u5F55\u6CA1\u6709\u53EF\u5206\u914D\u7684\u6A21\u578B\u8DEF\u7EBF\u3002") : plan.team.workPackages.map((item, index) => /* @__PURE__ */ import_react2.default.createElement("article", { className: "mr-package", key: item.id }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-package-top" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-package-name" }, index + 1, ". ", item.name), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-package-route" }, item.recommendedProvider, "/", item.recommendedModel)), item.objective && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-copy" }, "\u5177\u4F53\u76EE\u6807\uFF1A", item.objective), /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-copy" }, item.purpose, item.dependsOn.length > 0 ? ` \xB7 \u4F9D\u8D56\uFF1A${item.dependsOn.join("\u3001")}` : ""), /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-copy" }, "\u96BE\u5EA6\uFF1A", { simple: "\u7B80\u5355", balanced: "\u4E2D\u7B49", complex: "\u56F0\u96BE" }[item.difficulty] || item.difficulty || "\u5F85\u8BC4\u4F30", " \xB7 \u8D39\u7528\uFF1A", money(item.estimatedCost)), /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-copy" }, "\u9A8C\u6536\uFF1A", item.verificationChecklist.join("\uFF1B")), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-channel-line" }, /* @__PURE__ */ import_react2.default.createElement(ChannelBadge, { item }))))), plan.mode === "team" && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-caption" }, plan.team.handoff), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-notice" }, plan.pricingNotice, " ", plan.qualityNotice, " ", plan.availabilityNotice, " ", plan.modalityNotice || "")));
1067
1458
  }
1068
1459
  var remoteError = (response, fallback) => text(response?.error?.message) || text(response?.value?.error) || fallback;
1069
1460
  function probeLabel(probe) {
@@ -1074,25 +1465,14 @@ function probeLabel(probe) {
1074
1465
  if (probe.status === "probe-failed") return "\u68C0\u6D4B\u5931\u8D25";
1075
1466
  return probe.detail || "\u672A\u5B89\u88C5";
1076
1467
  }
1077
- function stableVersionOrder(left, right) {
1078
- const parse = (value) => /^([0-9]+)\.([0-9]+)\.([0-9]+)$/.exec(String(value ?? ""));
1079
- const current = parse(left);
1080
- const target = parse(right);
1081
- if (!current || !target) return null;
1082
- for (let index = 1; index <= 3; index += 1) {
1083
- const delta = Number(current[index]) - Number(target[index]);
1084
- if (delta) return Math.sign(delta);
1085
- }
1086
- return 0;
1087
- }
1088
1468
  function OfficialToolsCard({ listOfficialTools, installOfficialTool, cancelOfficialToolInstall, officialToolInstallStatus, onProbes }) {
1089
- const [probeState, setProbeState] = import_react.default.useState({ status: "loading", probes: [], capabilities: [], readiness: [], error: "" });
1090
- const [jobs, setJobs] = import_react.default.useState({});
1091
- const [rowErrors, setRowErrors] = import_react.default.useState({});
1092
- const mounted = import_react.default.useRef(false);
1093
- const request = import_react.default.useRef(0);
1094
- const submitting = import_react.default.useRef(/* @__PURE__ */ new Set());
1095
- const polling = import_react.default.useRef(/* @__PURE__ */ new Set());
1469
+ const [probeState, setProbeState] = import_react2.default.useState({ status: "loading", probes: [], capabilities: [], readiness: [], error: "" });
1470
+ const [jobs, setJobs] = import_react2.default.useState({});
1471
+ const [rowErrors, setRowErrors] = import_react2.default.useState({});
1472
+ const mounted = import_react2.default.useRef(false);
1473
+ const request = import_react2.default.useRef(0);
1474
+ const submitting = import_react2.default.useRef(/* @__PURE__ */ new Set());
1475
+ const polling = import_react2.default.useRef(/* @__PURE__ */ new Set());
1096
1476
  const refresh = async () => {
1097
1477
  const current = ++request.current;
1098
1478
  setProbeState((previous) => ({ ...previous, status: "loading", error: "" }));
@@ -1112,7 +1492,7 @@ function OfficialToolsCard({ listOfficialTools, installOfficialTool, cancelOffic
1112
1492
  onProbes({ probes: [], capabilities: [], readiness: [] });
1113
1493
  }
1114
1494
  };
1115
- import_react.default.useEffect(() => {
1495
+ import_react2.default.useEffect(() => {
1116
1496
  mounted.current = true;
1117
1497
  void refresh();
1118
1498
  if (typeof officialToolInstallStatus === "function") {
@@ -1132,109 +1512,106 @@ function OfficialToolsCard({ listOfficialTools, installOfficialTool, cancelOffic
1132
1512
  request.current += 1;
1133
1513
  };
1134
1514
  }, []);
1135
- import_react.default.useEffect(() => {
1515
+ import_react2.default.useEffect(() => {
1136
1516
  const active = Object.values(jobs).filter((job) => job?.status === "running").map((job) => job.tool);
1137
1517
  if (active.length === 0 || typeof officialToolInstallStatus !== "function") return void 0;
1138
1518
  let listening = true;
1139
- const poll = async (id) => {
1140
- if (polling.current.has(id)) return;
1141
- polling.current.add(id);
1519
+ const poll = async (id2) => {
1520
+ if (polling.current.has(id2)) return;
1521
+ polling.current.add(id2);
1142
1522
  try {
1143
- const response = await officialToolInstallStatus(id);
1523
+ const response = await officialToolInstallStatus(id2);
1144
1524
  if (!listening || !mounted.current) return;
1145
1525
  if (!response?.ok) throw new Error(remoteError(response, "\u65E0\u6CD5\u83B7\u53D6\u5B89\u88C5\u8FDB\u5EA6\u3002"));
1146
1526
  const job = response.value?.job;
1147
1527
  if (!job) throw new Error("\u5B89\u88C5\u4EFB\u52A1\u72B6\u6001\u6682\u4E0D\u53EF\u7528\u3002");
1148
- setJobs((previous) => ({ ...previous, [id]: job }));
1149
- setRowErrors((previous) => ({ ...previous, [id]: "" }));
1528
+ setJobs((previous) => ({ ...previous, [id2]: job }));
1529
+ setRowErrors((previous) => ({ ...previous, [id2]: "" }));
1150
1530
  if (job.status !== "running") void refresh();
1151
1531
  } catch (error) {
1152
- if (listening && mounted.current) setRowErrors((previous) => ({ ...previous, [id]: text(error?.message) || "\u5B89\u88C5\u72B6\u6001\u8BFB\u53D6\u5931\u8D25\uFF0C\u5C06\u7EE7\u7EED\u91CD\u8BD5\u3002" }));
1532
+ if (listening && mounted.current) setRowErrors((previous) => ({ ...previous, [id2]: text(error?.message) || "\u5B89\u88C5\u72B6\u6001\u8BFB\u53D6\u5931\u8D25\uFF0C\u5C06\u7EE7\u7EED\u91CD\u8BD5\u3002" }));
1153
1533
  } finally {
1154
- polling.current.delete(id);
1534
+ polling.current.delete(id2);
1155
1535
  }
1156
1536
  };
1157
- const timer = setInterval(() => active.forEach((id) => {
1158
- void poll(id);
1537
+ const timer = setInterval(() => active.forEach((id2) => {
1538
+ void poll(id2);
1159
1539
  }), 1500);
1160
1540
  return () => {
1161
1541
  listening = false;
1162
1542
  clearInterval(timer);
1163
1543
  };
1164
1544
  }, [jobs, officialToolInstallStatus]);
1165
- const install = async (id) => {
1166
- const tool = OFFICIAL_TOOLS.find((item) => item.id === id);
1167
- if (!tool || tool.unsupported || submitting.current.has(id) || jobs[id]?.status === "running") return;
1168
- submitting.current.add(id);
1169
- setRowErrors((previous) => ({ ...previous, [id]: "" }));
1170
- setJobs((previous) => ({ ...previous, [id]: { tool: id, status: "running", outputTail: [] } }));
1545
+ const install = async (id2) => {
1546
+ const tool = OFFICIAL_TOOLS.find((item) => item.id === id2);
1547
+ if (!tool || tool.unsupported || submitting.current.has(id2) || jobs[id2]?.status === "running") return;
1548
+ submitting.current.add(id2);
1549
+ setRowErrors((previous) => ({ ...previous, [id2]: "" }));
1550
+ setJobs((previous) => ({ ...previous, [id2]: { tool: id2, status: "running", outputTail: [] } }));
1171
1551
  try {
1172
1552
  if (typeof installOfficialTool !== "function") throw new Error("\u5B98\u65B9\u5DE5\u5177\u5B89\u88C5\u6865\u5C1A\u672A\u52A0\u8F7D\u3002");
1173
- const response = await installOfficialTool(id);
1553
+ const response = await installOfficialTool(id2);
1174
1554
  if (!mounted.current) return;
1175
1555
  if (!response?.ok || !response.value?.accepted || !response.value?.job) throw new Error(remoteError(response, "\u5B89\u88C5\u4EFB\u52A1\u672A\u88AB\u63A5\u53D7\u3002"));
1176
- setJobs((previous) => ({ ...previous, [id]: response.value.job }));
1556
+ setJobs((previous) => ({ ...previous, [id2]: response.value.job }));
1177
1557
  } catch (error) {
1178
1558
  if (mounted.current) {
1179
- setJobs((previous) => ({ ...previous, [id]: { tool: id, status: "failed", error: text(error?.message) || "\u5B89\u88C5\u542F\u52A8\u5931\u8D25\u3002" } }));
1180
- setRowErrors((previous) => ({ ...previous, [id]: text(error?.message) || "\u5B89\u88C5\u542F\u52A8\u5931\u8D25\u3002" }));
1559
+ setJobs((previous) => ({ ...previous, [id2]: { tool: id2, status: "failed", error: text(error?.message) || "\u5B89\u88C5\u542F\u52A8\u5931\u8D25\u3002" } }));
1560
+ setRowErrors((previous) => ({ ...previous, [id2]: text(error?.message) || "\u5B89\u88C5\u542F\u52A8\u5931\u8D25\u3002" }));
1181
1561
  }
1182
1562
  } finally {
1183
- submitting.current.delete(id);
1563
+ submitting.current.delete(id2);
1184
1564
  }
1185
1565
  };
1186
- const cancel = async (id) => {
1187
- if (typeof cancelOfficialToolInstall !== "function" || jobs[id]?.status !== "running" || jobs[id]?.cancelRequested) return;
1188
- setRowErrors((previous) => ({ ...previous, [id]: "" }));
1566
+ const cancel = async (id2) => {
1567
+ if (typeof cancelOfficialToolInstall !== "function" || jobs[id2]?.status !== "running" || jobs[id2]?.cancelRequested) return;
1568
+ setRowErrors((previous) => ({ ...previous, [id2]: "" }));
1189
1569
  try {
1190
- const response = await cancelOfficialToolInstall(id);
1570
+ const response = await cancelOfficialToolInstall(id2);
1191
1571
  if (!mounted.current) return;
1192
1572
  if (!response?.ok || !response.value?.accepted || !response.value?.job) throw new Error(remoteError(response, "\u53D6\u6D88\u8BF7\u6C42\u672A\u88AB\u63A5\u53D7\u3002"));
1193
- setJobs((previous) => ({ ...previous, [id]: response.value.job }));
1573
+ setJobs((previous) => ({ ...previous, [id2]: response.value.job }));
1194
1574
  if (response.value.job.status !== "running") void refresh();
1195
1575
  } catch (error) {
1196
- if (mounted.current) setRowErrors((previous) => ({ ...previous, [id]: text(error?.message) || "\u65E0\u6CD5\u53D6\u6D88\u5B89\u88C5\u3002" }));
1576
+ if (mounted.current) setRowErrors((previous) => ({ ...previous, [id2]: text(error?.message) || "\u65E0\u6CD5\u53D6\u6D88\u5B89\u88C5\u3002" }));
1197
1577
  }
1198
1578
  };
1199
1579
  const byId = Object.fromEntries(probeState.probes.map((probe) => [probe.id, probe]));
1200
1580
  const capabilitiesById = Object.fromEntries(probeState.capabilities.map((item) => [item.id, item]));
1201
1581
  const readinessById = Object.fromEntries(probeState.readiness.map((item) => [item.id, item]));
1202
- return /* @__PURE__ */ import_react.default.createElement("section", { className: "mr-card", "aria-label": "\u5B98\u65B9\u5DE5\u5177" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-card-head" }, /* @__PURE__ */ import_react.default.createElement("div", null, /* @__PURE__ */ import_react.default.createElement("h2", { className: "mr-card-title" }, "\u5B98\u65B9\u5DE5\u5177"), /* @__PURE__ */ import_react.default.createElement("p", { className: "mr-card-copy" }, "\u68C0\u6D4B\u672C\u673A\u5B98\u65B9\u5DE5\u5177\uFF0C\u5E76\u4ECE\u56FA\u5B9A\u6CE8\u518C\u8868\u4E00\u952E\u4E0B\u8F7D\u5B89\u88C5\u3002ZCode \u4F1A\u6253\u5F00\u5B98\u65B9\u5B89\u88C5\u7A97\u53E3\u4F9B\u4F60\u9009\u62E9\u76EE\u5F55\uFF1B\u5B8C\u6210\u540E\u91CD\u65B0\u68C0\u6D4B\u7248\u672C\u3002")), /* @__PURE__ */ import_react.default.createElement("button", { className: "mr-button mr-button-secondary", type: "button", disabled: probeState.status === "loading", onClick: () => {
1582
+ return /* @__PURE__ */ import_react2.default.createElement("section", { className: "mr-card", "aria-label": "\u5B98\u65B9\u5DE5\u5177" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-card-head" }, /* @__PURE__ */ import_react2.default.createElement("div", null, /* @__PURE__ */ import_react2.default.createElement("h2", { className: "mr-card-title" }, "\u5B98\u65B9\u5DE5\u5177"), /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-card-copy" }, "\u68C0\u6D4B\u672C\u673A\u5B98\u65B9\u5DE5\u5177\uFF0C\u5E76\u4ECE\u56FA\u5B9A\u6CE8\u518C\u8868\u4E00\u952E\u4E0B\u8F7D\u5B89\u88C5\u3002ZCode \u4F1A\u6253\u5F00\u5B98\u65B9\u5B89\u88C5\u7A97\u53E3\u4F9B\u4F60\u9009\u62E9\u76EE\u5F55\uFF1B\u5B8C\u6210\u540E\u91CD\u65B0\u68C0\u6D4B\u7248\u672C\u3002")), /* @__PURE__ */ import_react2.default.createElement("button", { className: "mr-button mr-button-secondary", type: "button", disabled: probeState.status === "loading", onClick: () => {
1203
1583
  void refresh();
1204
- } }, "\u91CD\u65B0\u68C0\u6D4B")), /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-card-body" }, probeState.status === "loading" && /* @__PURE__ */ import_react.default.createElement("p", { className: "mr-empty", role: "status" }, "\u6B63\u5728\u68C0\u6D4B\u672C\u673A\u5B98\u65B9\u5DE5\u5177\u2026"), probeState.status === "error" && /* @__PURE__ */ import_react.default.createElement("p", { className: "mr-error", role: "alert" }, probeState.error), /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-tools", role: "list", "aria-label": "\u5B98\u65B9\u5DE5\u5177\u6CE8\u518C\u8868" }, OFFICIAL_TOOLS.map((tool) => {
1584
+ } }, "\u91CD\u65B0\u68C0\u6D4B")), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-card-body" }, probeState.status === "loading" && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-empty", role: "status" }, "\u6B63\u5728\u68C0\u6D4B\u672C\u673A\u5B98\u65B9\u5DE5\u5177\u2026"), probeState.status === "error" && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-error", role: "alert" }, probeState.error), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-tools", role: "list", "aria-label": "\u5B98\u65B9\u5DE5\u5177\u6CE8\u518C\u8868" }, OFFICIAL_TOOLS.map((tool) => {
1205
1585
  const command = installCommandLine(tool);
1206
1586
  const probe = byId[tool.id];
1207
1587
  const capability = capabilitiesById[tool.id];
1208
1588
  const readiness = readinessById[tool.id];
1209
1589
  const job = jobs[tool.id];
1210
1590
  const running = job?.status === "running";
1211
- const versionOrder = probe?.installed ? stableVersionOrder(probe.version, tool.version) : null;
1212
- const current = versionOrder === 0;
1213
- const newerOrUncertain = probe?.installed && (versionOrder === null || versionOrder > 0);
1591
+ const action = toolInstallAction({ tool, probe, readiness, job, probeStatus: probeState.status });
1214
1592
  const verified = job?.status === "succeeded" && job.postInstallProbe?.installed === true;
1215
1593
  const status = running ? job.cancelRequested ? "\u6B63\u5728\u53D6\u6D88\u5B89\u88C5\u2026" : "\u5B89\u88C5\u4E2D\u2026" : job?.status === "installer-opened" ? "\u5B98\u65B9\u5B89\u88C5\u5668\u5DF2\u6253\u5F00\uFF0C\u8BF7\u5B8C\u6210\u5B89\u88C5\u540E\u91CD\u65B0\u68C0\u6D4B" : job?.status === "cancelled" ? "\u5B89\u88C5\u5DF2\u53D6\u6D88\uFF0C\u8BF7\u91CD\u65B0\u68C0\u6D4B" : verified ? "\u5B89\u88C5\u6210\u529F\u5E76\u9A8C\u8BC1" : probeLabel(probe);
1216
- const buttonLabel = running ? "\u5B89\u88C5\u4E2D\u2026" : current ? "\u5DF2\u662F\u76EE\u6807\u7248\u672C" : newerOrUncertain ? "\u8BF7\u4EBA\u5DE5\u6838\u5BF9\u7248\u672C" : probe?.installed ? "\u66F4\u65B0\u5230\u76EE\u6807\u7248\u672C" : job?.status === "failed" ? "\u91CD\u8BD5\u5B89\u88C5" : tool.manager === "signed-windows-installer" ? "\u4E0B\u8F7D\u5B89\u88C5\u5668" : "\u4E0B\u8F7D\u5B89\u88C5";
1217
- return /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-tool", role: "listitem", key: tool.id }, /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-tool-info" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-route-name", title: tool.purpose }, tool.label), /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-route-provider" }, tool.vendor, " \xB7 ", tool.id), /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-tool-status", role: "status" }, /* @__PURE__ */ import_react.default.createElement("span", { className: `mr-tool-dot ${running ? "running" : probe?.installed ? "installed" : "missing"}` }), status, tool.version ? ` \xB7 \u76EE\u6807 ${tool.version}` : ""), probe?.installed && /* @__PURE__ */ import_react.default.createElement("p", { className: "mr-caption mr-tool-detail" }, readiness?.ready ? `\u5B98\u65B9\u6267\u884C\u5165\u53E3\u5DF2\u6838\u9A8C\uFF0C\u53EF\u5728\u4F1A\u8BDD\u4E2D\u8C03\u7528 model_router_tool_run\uFF1B${capability?.modes?.includes("read-only") ? "\u652F\u6301\u53EA\u8BFB\u548C\u7ECF\u5BA1\u6279\u7684\u53EF\u7F16\u8F91\u4EFB\u52A1" : "\u4EC5\u652F\u6301\u7ECF\u5BA1\u6279\u7684\u53EF\u7F16\u8F91\u9694\u79BB\u5DE5\u4F5C\u533A\u4EFB\u52A1"}\uFF0C\u8D26\u53F7\u53CA\u6A21\u578B\u4ECD\u9700\u5B9E\u6D4B\u3002` : `\u5DF2\u5B89\u88C5\uFF0C\u4F46\u5F53\u524D\u4E0D\u53EF\u6258\u7BA1\u6267\u884C\uFF1A${readiness?.reason || capability?.reason || "\u6267\u884C\u5165\u53E3\u5C1A\u672A\u6838\u9A8C\u3002"}`), command ? /* @__PURE__ */ import_react.default.createElement("code", { className: "mr-tool-command" }, command) : /* @__PURE__ */ import_react.default.createElement("p", { className: "mr-caption", style: { margin: "6px 0 0" } }, tool.unsupportedReason), probe?.detail && /* @__PURE__ */ import_react.default.createElement("p", { className: "mr-caption mr-tool-detail" }, probe.detail), (rowErrors[tool.id] || job?.error) && /* @__PURE__ */ import_react.default.createElement("p", { className: "mr-error mr-tool-error", role: "alert" }, rowErrors[tool.id] || job.error), Array.isArray(job?.outputTail) && job.outputTail.length > 0 && /* @__PURE__ */ import_react.default.createElement("details", { className: "mr-tool-log" }, /* @__PURE__ */ import_react.default.createElement("summary", null, "\u5B89\u88C5\u65E5\u5FD7"), /* @__PURE__ */ import_react.default.createElement("pre", null, job.outputTail.slice(-6).join("\n")))), command && /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-tool-actions" }, /* @__PURE__ */ import_react.default.createElement("button", { className: "mr-button mr-tool-button", type: "button", disabled: probeState.status !== "ready" || running || current || newerOrUncertain, onClick: () => {
1594
+ return /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-tool", role: "listitem", key: tool.id }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-tool-info" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-route-name", title: tool.purpose }, tool.label), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-route-provider" }, tool.vendor, " \xB7 ", tool.id), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-tool-status", role: "status" }, /* @__PURE__ */ import_react2.default.createElement("span", { className: `mr-tool-dot ${running ? "running" : probe?.installed ? "installed" : "missing"}` }), status, tool.version ? ` \xB7 \u76EE\u6807 ${tool.version}` : ""), probe?.installed && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-caption mr-tool-detail" }, readiness?.ready ? `\u5B98\u65B9\u6267\u884C\u5165\u53E3\u5DF2\u6838\u9A8C\uFF0C\u53EF\u5728\u4F1A\u8BDD\u4E2D\u8C03\u7528 model_router_tool_run\uFF1B${capability?.modes?.includes("read-only") ? "\u652F\u6301\u53EA\u8BFB\u548C\u7ECF\u5BA1\u6279\u7684\u53EF\u7F16\u8F91\u4EFB\u52A1" : "\u4EC5\u652F\u6301\u7ECF\u5BA1\u6279\u7684\u53EF\u7F16\u8F91\u9694\u79BB\u5DE5\u4F5C\u533A\u4EFB\u52A1"}\uFF0C\u8D26\u53F7\u53CA\u6A21\u578B\u4ECD\u9700\u5B9E\u6D4B\u3002` : `\u5DF2\u5B89\u88C5\uFF0C\u4F46\u5F53\u524D\u4E0D\u53EF\u6258\u7BA1\u6267\u884C\uFF1A${readiness?.reason || capability?.reason || "\u6267\u884C\u5165\u53E3\u5C1A\u672A\u6838\u9A8C\u3002"}`), command ? /* @__PURE__ */ import_react2.default.createElement("code", { className: "mr-tool-command" }, command) : /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-caption", style: { margin: "6px 0 0" } }, tool.unsupportedReason), probe?.detail && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-caption mr-tool-detail" }, probe.detail), (rowErrors[tool.id] || job?.error) && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-error mr-tool-error", role: "alert" }, rowErrors[tool.id] || job.error), Array.isArray(job?.outputTail) && job.outputTail.length > 0 && /* @__PURE__ */ import_react2.default.createElement("details", { className: "mr-tool-log" }, /* @__PURE__ */ import_react2.default.createElement("summary", null, "\u5B89\u88C5\u65E5\u5FD7"), /* @__PURE__ */ import_react2.default.createElement("pre", null, job.outputTail.slice(-6).join("\n")))), command && /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-tool-actions" }, /* @__PURE__ */ import_react2.default.createElement("button", { className: "mr-button mr-tool-button", type: "button", disabled: action.disabled, onClick: () => {
1218
1595
  void install(tool.id);
1219
- } }, buttonLabel), running && /* @__PURE__ */ import_react.default.createElement("button", { className: "mr-button mr-button-secondary mr-tool-button", type: "button", disabled: job.cancelRequested, onClick: () => {
1596
+ } }, action.label), running && /* @__PURE__ */ import_react2.default.createElement("button", { className: "mr-button mr-button-secondary mr-tool-button", type: "button", disabled: job.cancelRequested, onClick: () => {
1220
1597
  void cancel(tool.id);
1221
1598
  } }, job.cancelRequested ? "\u6B63\u5728\u53D6\u6D88\u2026" : "\u53D6\u6D88\u5B89\u88C5")));
1222
- })), /* @__PURE__ */ import_react.default.createElement("p", { className: "mr-caption", style: { marginTop: 12 } }, "\u5B89\u88C5\u7531 Host \u6309\u6CE8\u518C\u8868\u56FA\u5B9A\u6765\u6E90\u6267\u884C\uFF0C\u4E0D\u63A5\u53D7\u81EA\u5B9A\u4E49\u5305\u540D\uFF1B\u53EF\u70B9\u201C\u53D6\u6D88\u5B89\u88C5\u201D\u7EC8\u6B62\u4E0B\u8F7D\u4EFB\u52A1\uFF0C\u968F\u540E\u91CD\u65B0\u68C0\u6D4B\u5B9E\u9645\u7248\u672C\u3002ZCode \u5B89\u88C5\u5668\u542F\u52A8\u540E\u4ECD\u9700\u5728\u539F\u5382\u7A97\u53E3\u9009\u62E9\u76EE\u5F55\u5E76\u5B8C\u6210\u5B89\u88C5\u3002Agent \u4E5F\u53EF\u8C03\u7528 ", /* @__PURE__ */ import_react.default.createElement("code", null, "model_router_tool_install"), "\uFF0C\u6216\u5728\u4F1A\u8BDD\u4F7F\u7528 ", /* @__PURE__ */ import_react.default.createElement("code", null, "/tools"), "\u3002")));
1599
+ })), /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-caption", style: { marginTop: 12 } }, "\u5B89\u88C5\u7531 Host \u6309\u6CE8\u518C\u8868\u56FA\u5B9A\u6765\u6E90\u6267\u884C\uFF0C\u4E0D\u63A5\u53D7\u81EA\u5B9A\u4E49\u5305\u540D\uFF1B\u53EF\u70B9\u201C\u53D6\u6D88\u5B89\u88C5\u201D\u7EC8\u6B62\u4E0B\u8F7D\u4EFB\u52A1\uFF0C\u968F\u540E\u91CD\u65B0\u68C0\u6D4B\u5B9E\u9645\u7248\u672C\u3002ZCode \u5B89\u88C5\u5668\u542F\u52A8\u540E\u4ECD\u9700\u5728\u539F\u5382\u7A97\u53E3\u9009\u62E9\u76EE\u5F55\u5E76\u5B8C\u6210\u5B89\u88C5\u3002Agent \u4E5F\u53EF\u8C03\u7528 ", /* @__PURE__ */ import_react2.default.createElement("code", null, "model_router_tool_install"), "\uFF0C\u6216\u5728\u4F1A\u8BDD\u4F7F\u7528 ", /* @__PURE__ */ import_react2.default.createElement("code", null, "/tools"), "\u3002")));
1223
1600
  }
1224
1601
  function RouterMainPage({ loadCatalog, settingsScope, listOfficialTools, installOfficialTool, cancelOfficialToolInstall, officialToolInstallStatus }) {
1225
- const [catalogState, setCatalogState] = import_react.default.useState({ status: "loading", catalog: null, error: "" });
1226
- const [task, setTask] = import_react.default.useState("");
1227
- const [mode, setMode] = import_react.default.useState("single");
1228
- const [budget, setBudget] = import_react.default.useState(() => String(settingsScope.getSnapshot().value?.budgetUsd ?? 0));
1229
- const [query, setQuery] = import_react.default.useState("");
1230
- const [plan, setPlan] = import_react.default.useState(null);
1231
- const [planError, setPlanError] = import_react.default.useState("");
1232
- const [toolProbes, setToolProbes] = import_react.default.useState(null);
1233
- const budgetEdited = import_react.default.useRef(false);
1234
- const budgetValue = import_react.default.useRef(budget);
1235
- const mounted = import_react.default.useRef(false);
1236
- const catalogRequest = import_react.default.useRef(0);
1237
- import_react.default.useEffect(() => {
1602
+ const [catalogState, setCatalogState] = import_react2.default.useState({ status: "loading", catalog: null, error: "" });
1603
+ const [task, setTask] = import_react2.default.useState("");
1604
+ const [mode, setMode] = import_react2.default.useState("single");
1605
+ const [budget, setBudget] = import_react2.default.useState(() => String(settingsScope.getSnapshot().value?.budgetUsd ?? 0));
1606
+ const [query, setQuery] = import_react2.default.useState("");
1607
+ const [plan, setPlan] = import_react2.default.useState(null);
1608
+ const [planError, setPlanError] = import_react2.default.useState("");
1609
+ const [toolProbes, setToolProbes] = import_react2.default.useState(null);
1610
+ const budgetEdited = import_react2.default.useRef(false);
1611
+ const budgetValue = import_react2.default.useRef(budget);
1612
+ const mounted = import_react2.default.useRef(false);
1613
+ const catalogRequest = import_react2.default.useRef(0);
1614
+ import_react2.default.useEffect(() => {
1238
1615
  const syncBudget = () => {
1239
1616
  if (budgetEdited.current) return;
1240
1617
  const next = String(settingsScope.getSnapshot().value?.budgetUsd ?? 0);
@@ -1248,7 +1625,7 @@ function RouterMainPage({ loadCatalog, settingsScope, listOfficialTools, install
1248
1625
  syncBudget();
1249
1626
  return settingsScope.subscribe(syncBudget);
1250
1627
  }, [settingsScope]);
1251
- import_react.default.useEffect(() => {
1628
+ import_react2.default.useEffect(() => {
1252
1629
  mounted.current = true;
1253
1630
  const request = ++catalogRequest.current;
1254
1631
  Promise.resolve().then(loadCatalog).then((response) => {
@@ -1282,7 +1659,7 @@ function RouterMainPage({ loadCatalog, settingsScope, listOfficialTools, install
1282
1659
  setPlan(null);
1283
1660
  setPlanError("");
1284
1661
  };
1285
- const handleToolProbes = import_react.default.useCallback((snapshot) => {
1662
+ const handleToolProbes = import_react2.default.useCallback((snapshot) => {
1286
1663
  setToolProbes(snapshot);
1287
1664
  setPlan(null);
1288
1665
  setPlanError("");
@@ -1297,6 +1674,7 @@ function RouterMainPage({ loadCatalog, settingsScope, listOfficialTools, install
1297
1674
  setPlan(createWorkspacePlan(task, catalogState.catalog, {
1298
1675
  mode,
1299
1676
  budgetUsd: parsedBudget,
1677
+ modelProfilesJson: settingsScope.getSnapshot().value?.modelProfilesJson ?? "[]",
1300
1678
  installedToolIds: (toolProbes?.probes ?? []).filter((probe) => probe.installed).map((probe) => probe.id),
1301
1679
  runnableToolIds: (toolProbes?.readiness ?? []).filter((item) => item.ready).map((item) => item.id)
1302
1680
  }));
@@ -1305,25 +1683,25 @@ function RouterMainPage({ loadCatalog, settingsScope, listOfficialTools, install
1305
1683
  setPlanError(text(error?.message) || "\u65E0\u6CD5\u751F\u6210\u8DEF\u7531\u5EFA\u8BAE\u3002");
1306
1684
  }
1307
1685
  };
1308
- return /* @__PURE__ */ import_react.default.createElement("main", { className: "mr-workspace" }, /* @__PURE__ */ import_react.default.createElement("style", null, router_main_default), /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-shell" }, /* @__PURE__ */ import_react.default.createElement("header", { className: "mr-header" }, /* @__PURE__ */ import_react.default.createElement("div", null, /* @__PURE__ */ import_react.default.createElement("p", { className: "mr-eyebrow" }, "Model Router \xB7 DeepSeek Harness"), /* @__PURE__ */ import_react.default.createElement("h1", { className: "mr-title" }, "\u6A21\u578B\u8DEF\u7531\u5DE5\u4F5C\u53F0"), /* @__PURE__ */ import_react.default.createElement("p", { className: "mr-subtitle" }, "\u67E5\u770B\u5B98\u65B9\u6A21\u578B\u76EE\u5F55\uFF0C\u4E3A\u4EFB\u52A1\u751F\u6210\u8DEF\u7EBF\u5EFA\u8BAE\u4E0E\u56E2\u961F\u5DE5\u4F5C\u5305\u3002\u4E3B\u4F1A\u8BDD\u6A21\u578B\u4ECD\u7531\u5B98\u65B9\u9009\u62E9\u5668\u7BA1\u7406\u3002")), /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-status" }, /* @__PURE__ */ import_react.default.createElement("span", { className: `mr-status-dot ${catalogState.status === "loading" ? "loading" : catalogState.status === "error" ? "error" : ""}` }), catalogState.status === "ready" ? `${providerCount} \u4E2A\u4F9B\u5E94\u5546 \xB7 ${routes.length} \u6761\u8DEF\u7EBF` : catalogState.status === "loading" ? "\u6B63\u5728\u8BFB\u53D6\u6A21\u578B\u76EE\u5F55" : "\u6A21\u578B\u76EE\u5F55\u8BFB\u53D6\u5931\u8D25")), /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-grid" }, /* @__PURE__ */ import_react.default.createElement("section", { className: "mr-card", "aria-label": "\u4EFB\u52A1\u89C4\u5212" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-card-head" }, /* @__PURE__ */ import_react.default.createElement("div", null, /* @__PURE__ */ import_react.default.createElement("h2", { className: "mr-card-title" }, "\u4EFB\u52A1\u89C4\u5212"), /* @__PURE__ */ import_react.default.createElement("p", { className: "mr-card-copy" }, "\u89C4\u5212\u5728\u672C\u673A\u5B8C\u6210\uFF0C\u4E0D\u4F1A\u542F\u52A8\u6A21\u578B\u6216\u56E2\u961F\u4EFB\u52A1\u3002"))), /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-card-body" }, /* @__PURE__ */ import_react.default.createElement("label", { className: "mr-label", htmlFor: "mr-task" }, "\u4EFB\u52A1\u63CF\u8FF0"), /* @__PURE__ */ import_react.default.createElement("textarea", { className: "mr-textarea", id: "mr-task", value: task, onChange: (event) => {
1686
+ return /* @__PURE__ */ import_react2.default.createElement("main", { className: "mr-workspace" }, /* @__PURE__ */ import_react2.default.createElement("style", null, router_main_default), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-shell" }, /* @__PURE__ */ import_react2.default.createElement("header", { className: "mr-header" }, /* @__PURE__ */ import_react2.default.createElement("div", null, /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-eyebrow" }, "Model Router \xB7 DeepSeek Harness"), /* @__PURE__ */ import_react2.default.createElement("h1", { className: "mr-title" }, "\u6A21\u578B\u8DEF\u7531\u5DE5\u4F5C\u53F0"), /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-subtitle" }, "\u67E5\u770B\u5B98\u65B9\u6A21\u578B\u76EE\u5F55\uFF0C\u4E3A\u4EFB\u52A1\u751F\u6210\u8DEF\u7EBF\u5EFA\u8BAE\u4E0E\u56E2\u961F\u5DE5\u4F5C\u5305\u3002\u4E3B\u4F1A\u8BDD\u6A21\u578B\u4ECD\u7531\u5B98\u65B9\u9009\u62E9\u5668\u7BA1\u7406\u3002")), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-status" }, /* @__PURE__ */ import_react2.default.createElement("span", { className: `mr-status-dot ${catalogState.status === "loading" ? "loading" : catalogState.status === "error" ? "error" : ""}` }), catalogState.status === "ready" ? `${providerCount} \u4E2A\u4F9B\u5E94\u5546 \xB7 ${routes.length} \u6761\u8DEF\u7EBF` : catalogState.status === "loading" ? "\u6B63\u5728\u8BFB\u53D6\u6A21\u578B\u76EE\u5F55" : "\u6A21\u578B\u76EE\u5F55\u8BFB\u53D6\u5931\u8D25")), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-grid" }, /* @__PURE__ */ import_react2.default.createElement("section", { className: "mr-card", "aria-label": "\u4EFB\u52A1\u89C4\u5212" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-card-head" }, /* @__PURE__ */ import_react2.default.createElement("div", null, /* @__PURE__ */ import_react2.default.createElement("h2", { className: "mr-card-title" }, "\u4EFB\u52A1\u89C4\u5212"), /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-card-copy" }, "\u89C4\u5212\u5728\u672C\u673A\u5B8C\u6210\uFF0C\u4E0D\u4F1A\u542F\u52A8\u6A21\u578B\u6216\u56E2\u961F\u4EFB\u52A1\u3002"))), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-card-body" }, /* @__PURE__ */ import_react2.default.createElement("label", { className: "mr-label", htmlFor: "mr-task" }, "\u4EFB\u52A1\u63CF\u8FF0"), /* @__PURE__ */ import_react2.default.createElement("textarea", { className: "mr-textarea", id: "mr-task", value: task, onChange: (event) => {
1309
1687
  setTask(event.target.value);
1310
1688
  invalidatePlan();
1311
- }, placeholder: "\u4F8B\u5982\uFF1A\u5206\u6790\u9879\u76EE\u67B6\u6784\uFF0C\u5206\u5DE5\u4FEE\u590D\u5173\u952E\u95EE\u9898\uFF0C\u5E76\u7ED9\u51FA\u9A8C\u6536\u6E05\u5355" }), /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-controls" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-control-group" }, /* @__PURE__ */ import_react.default.createElement("span", { className: "mr-control-label" }, "\u89C4\u5212\u6A21\u5F0F"), /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-segment", role: "group", "aria-label": "\u89C4\u5212\u6A21\u5F0F" }, /* @__PURE__ */ import_react.default.createElement("button", { type: "button", "aria-pressed": mode === "single", onClick: () => {
1689
+ }, placeholder: "\u4F8B\u5982\uFF1A\u5206\u6790\u9879\u76EE\u67B6\u6784\uFF0C\u5206\u5DE5\u4FEE\u590D\u5173\u952E\u95EE\u9898\uFF0C\u5E76\u7ED9\u51FA\u9A8C\u6536\u6E05\u5355" }), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-controls" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-control-group" }, /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-control-label" }, "\u89C4\u5212\u6A21\u5F0F"), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-segment", role: "group", "aria-label": "\u89C4\u5212\u6A21\u5F0F" }, /* @__PURE__ */ import_react2.default.createElement("button", { type: "button", "aria-pressed": mode === "single", onClick: () => {
1312
1690
  setMode("single");
1313
1691
  invalidatePlan();
1314
- } }, "\u5355\u4EFB\u52A1"), /* @__PURE__ */ import_react.default.createElement("button", { type: "button", "aria-pressed": mode === "team", onClick: () => {
1692
+ } }, "\u5355\u4EFB\u52A1"), /* @__PURE__ */ import_react2.default.createElement("button", { type: "button", "aria-pressed": mode === "team", onClick: () => {
1315
1693
  setMode("team");
1316
1694
  invalidatePlan();
1317
- } }, "\u56E2\u961F\u5206\u5DE5"))), /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-control-group mr-budget" }, /* @__PURE__ */ import_react.default.createElement("label", { className: "mr-control-label", htmlFor: "mr-budget" }, "\u672C\u6B21\u4F30\u7B97\u9884\u7B97\uFF08USD\uFF09"), /* @__PURE__ */ import_react.default.createElement("input", { className: "mr-input", id: "mr-budget", type: "number", min: "0", step: "0.01", value: budget, onChange: (event) => {
1695
+ } }, "\u56E2\u961F\u5206\u5DE5"))), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-control-group mr-budget" }, /* @__PURE__ */ import_react2.default.createElement("label", { className: "mr-control-label", htmlFor: "mr-budget" }, "\u672C\u6B21\u4F30\u7B97\u9884\u7B97\uFF08USD\uFF09"), /* @__PURE__ */ import_react2.default.createElement("input", { className: "mr-input", id: "mr-budget", type: "number", min: "0", step: "0.01", value: budget, onChange: (event) => {
1318
1696
  budgetEdited.current = true;
1319
1697
  budgetValue.current = event.target.value;
1320
1698
  setBudget(event.target.value);
1321
1699
  invalidatePlan();
1322
- } }))), /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-actions" }, /* @__PURE__ */ import_react.default.createElement("button", { className: "mr-button", type: "button", disabled: catalogState.status !== "ready" || routes.length === 0 || toolProbes === null, onClick: generate }, "\u751F\u6210\u8DEF\u7531\u5EFA\u8BAE"), /* @__PURE__ */ import_react.default.createElement("span", { className: "mr-caption" }, toolProbes === null ? "\u6B63\u5728\u68C0\u6D4B\u5B98\u65B9\u5DE5\u5177\u2026" : "0 \u8868\u793A\u4E0D\u9650\u5236\u672C\u6B21\u5EFA\u8BAE\uFF1B\u4E0D\u4F1A\u8BBE\u7F6E\u771F\u5B9E\u652F\u51FA\u4E0A\u9650\u3002")), planError && /* @__PURE__ */ import_react.default.createElement("p", { className: "mr-error", role: "alert" }, planError))), /* @__PURE__ */ import_react.default.createElement("section", { className: "mr-card", "aria-label": "\u6A21\u578B\u76EE\u5F55" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-card-head" }, /* @__PURE__ */ import_react.default.createElement("div", null, /* @__PURE__ */ import_react.default.createElement("h2", { className: "mr-card-title" }, "\u6A21\u578B\u76EE\u5F55"), /* @__PURE__ */ import_react.default.createElement("p", { className: "mr-card-copy" }, "\u53EA\u663E\u793A\u5B98\u65B9\u5DF2\u767B\u8BB0\u7684 provider/model\uFF0C\u4E0D\u8BFB\u53D6 API Key\u3002")), /* @__PURE__ */ import_react.default.createElement("button", { className: "mr-button mr-button-secondary", type: "button", onClick: refresh }, "\u5237\u65B0")), /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-card-body" }, catalogState.status === "error" && /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-error", role: "alert" }, catalogState.error), catalogState.status === "loading" && /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-empty" }, "\u6B63\u5728\u52A0\u8F7D\u5B98\u65B9\u6A21\u578B\u76EE\u5F55\u2026"), catalogState.status === "ready" && /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, /* @__PURE__ */ import_react.default.createElement("label", { className: "mr-label", htmlFor: "mr-model-search" }, "\u641C\u7D22\u8DEF\u7EBF"), /* @__PURE__ */ import_react.default.createElement("input", { className: "mr-input mr-search", id: "mr-model-search", value: query, onChange: (event) => setQuery(event.target.value), placeholder: "\u6A21\u578B\u6216\u4F9B\u5E94\u5546" }), /* @__PURE__ */ import_react.default.createElement(RouteList, { routes, query }), catalogState.catalog?.failures?.length > 0 && /* @__PURE__ */ import_react.default.createElement("p", { className: "mr-caption" }, catalogState.catalog.failures.length, " \u4E2A\u4F9B\u5E94\u5546\u7684\u76EE\u5F55\u8BFB\u53D6\u5931\u8D25\uFF0C\u8BF7\u5728\u5B98\u65B9\u6A21\u578B\u9875\u68C0\u67E5\u914D\u7F6E\u3002")), /* @__PURE__ */ import_react.default.createElement("p", { className: "mr-caption", style: { marginTop: 13 } }, "\u76EE\u5F55\u767B\u8BB0\u4E0D\u4EE3\u8868\u51ED\u636E\u6216\u7F51\u7EDC\u5F53\u524D\u53EF\u7528\uFF1B\u56FE\u50CF\u80FD\u529B\u9700\u8981\u5728\u5B9E\u9645\u4F7F\u7528\u524D\u6838\u5BF9\u3002")))), plan && /* @__PURE__ */ import_react.default.createElement(PlanResults, { plan }), /* @__PURE__ */ import_react.default.createElement(OfficialToolsCard, { listOfficialTools, installOfficialTool, cancelOfficialToolInstall, officialToolInstallStatus, onProbes: handleToolProbes }), /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-notice" }, "\u5B9E\u9645\u8C03\u7528\u8BF7\u5728\u5B98\u65B9\u4F1A\u8BDD\u4E2D\u4F7F\u7528 ", /* @__PURE__ */ import_react.default.createElement("code", null, "model_router_consult"), "\u3001", /* @__PURE__ */ import_react.default.createElement("code", null, "model_router_tool_run"), " \u6216 ", /* @__PURE__ */ import_react.default.createElement("code", null, "model_router_team_execute"), "\u3002\u6258\u7BA1\u6267\u884C\u80FD\u529B\u548C\u5C31\u7EEA\u72B6\u6001\u89C1\u4E0A\u65B9\u5404\u5DE5\u5177\u5361\u7247\uFF1B\u5B9E\u9645\u4F7F\u7528\u7684\u6A21\u578B\u4EE5\u5382\u5546\u8BB0\u5F55\u4E3A\u51C6\u3002ZCode 3.14.3 \u4F7F\u7528\u5176\u81EA\u8EAB\u914D\u7F6E\u7684\u9ED8\u8BA4\u6A21\u578B\u3002\u53EF\u7F16\u8F91\u56E2\u961F\u4EFB\u52A1\u8981\u6C42\u5E72\u51C0\u7684 Git \u4ED3\u5E93\uFF0C\u5E76\u7ECF\u5B98\u65B9\u5DE5\u5177\u5BA1\u6279\u3002\u8BBE\u7F6E\u4F4D\u4E8E\u201C\u63D2\u4EF6 \u2192 \u5DF2\u5B89\u88C5 \u2192 @ljwei-stak/model-router-galgame\u201D\u3002")));
1700
+ } }))), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-actions" }, /* @__PURE__ */ import_react2.default.createElement("button", { className: "mr-button", type: "button", disabled: catalogState.status !== "ready" || routes.length === 0 || toolProbes === null, onClick: generate }, "\u751F\u6210\u8DEF\u7531\u5EFA\u8BAE"), /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-caption" }, toolProbes === null ? "\u6B63\u5728\u68C0\u6D4B\u5B98\u65B9\u5DE5\u5177\u2026" : "0 \u8868\u793A\u4E0D\u9650\u5236\u672C\u6B21\u5EFA\u8BAE\uFF1B\u4E0D\u4F1A\u8BBE\u7F6E\u771F\u5B9E\u652F\u51FA\u4E0A\u9650\u3002")), planError && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-error", role: "alert" }, planError))), /* @__PURE__ */ import_react2.default.createElement("section", { className: "mr-card", "aria-label": "\u6A21\u578B\u76EE\u5F55" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-card-head" }, /* @__PURE__ */ import_react2.default.createElement("div", null, /* @__PURE__ */ import_react2.default.createElement("h2", { className: "mr-card-title" }, "\u6A21\u578B\u76EE\u5F55"), /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-card-copy" }, "\u53EA\u663E\u793A\u5B98\u65B9\u5DF2\u767B\u8BB0\u7684 provider/model\uFF0C\u4E0D\u8BFB\u53D6 API Key\u3002")), /* @__PURE__ */ import_react2.default.createElement("button", { className: "mr-button mr-button-secondary", type: "button", onClick: refresh }, "\u5237\u65B0")), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-card-body" }, catalogState.status === "error" && /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-error", role: "alert" }, catalogState.error), catalogState.status === "loading" && /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-empty" }, "\u6B63\u5728\u52A0\u8F7D\u5B98\u65B9\u6A21\u578B\u76EE\u5F55\u2026"), catalogState.status === "ready" && /* @__PURE__ */ import_react2.default.createElement(import_react2.default.Fragment, null, /* @__PURE__ */ import_react2.default.createElement("label", { className: "mr-label", htmlFor: "mr-model-search" }, "\u641C\u7D22\u8DEF\u7EBF"), /* @__PURE__ */ import_react2.default.createElement("input", { className: "mr-input mr-search", id: "mr-model-search", value: query, onChange: (event) => setQuery(event.target.value), placeholder: "\u6A21\u578B\u6216\u4F9B\u5E94\u5546" }), /* @__PURE__ */ import_react2.default.createElement(RouteList, { routes, query }), catalogState.catalog?.failures?.length > 0 && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-caption" }, catalogState.catalog.failures.length, " \u4E2A\u4F9B\u5E94\u5546\u7684\u76EE\u5F55\u8BFB\u53D6\u5931\u8D25\uFF0C\u8BF7\u5728\u5B98\u65B9\u6A21\u578B\u9875\u68C0\u67E5\u914D\u7F6E\u3002")), /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-caption", style: { marginTop: 13 } }, "\u76EE\u5F55\u767B\u8BB0\u4E0D\u4EE3\u8868\u51ED\u636E\u6216\u7F51\u7EDC\u5F53\u524D\u53EF\u7528\uFF1B\u56FE\u50CF\u80FD\u529B\u9700\u8981\u5728\u5B9E\u9645\u4F7F\u7528\u524D\u6838\u5BF9\u3002")))), /* @__PURE__ */ import_react2.default.createElement(ModelProfileEditor, { routes, settingsScope, onSaved: invalidatePlan }), plan && /* @__PURE__ */ import_react2.default.createElement(PlanResults, { plan }), /* @__PURE__ */ import_react2.default.createElement(OfficialToolsCard, { listOfficialTools, installOfficialTool, cancelOfficialToolInstall, officialToolInstallStatus, onProbes: handleToolProbes }), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-notice" }, "\u5B9E\u9645\u8C03\u7528\u8BF7\u5728\u5B98\u65B9\u4F1A\u8BDD\u4E2D\u4F7F\u7528 ", /* @__PURE__ */ import_react2.default.createElement("code", null, "model_router_consult"), "\u3001", /* @__PURE__ */ import_react2.default.createElement("code", null, "model_router_tool_run"), " \u6216 ", /* @__PURE__ */ import_react2.default.createElement("code", null, "model_router_team_execute"), "\u3002\u6258\u7BA1\u6267\u884C\u80FD\u529B\u548C\u5C31\u7EEA\u72B6\u6001\u89C1\u4E0A\u65B9\u5404\u5DE5\u5177\u5361\u7247\uFF1B\u5B9E\u9645\u4F7F\u7528\u7684\u6A21\u578B\u4EE5\u5382\u5546\u8BB0\u5F55\u4E3A\u51C6\u3002ZCode 3.14.3 \u4F7F\u7528\u5176\u81EA\u8EAB\u914D\u7F6E\u7684\u9ED8\u8BA4\u6A21\u578B\u3002\u53EF\u7F16\u8F91\u56E2\u961F\u4EFB\u52A1\u8981\u6C42\u5E72\u51C0\u7684 Git \u4ED3\u5E93\uFF0C\u5E76\u7ECF\u5B98\u65B9\u5DE5\u5177\u5BA1\u6279\u3002\u8BBE\u7F6E\u4F4D\u4E8E\u201C\u63D2\u4EF6 \u2192 \u5DF2\u5B89\u88C5 \u2192 @ljwei-stak/model-router-galgame\u201D\u3002")));
1323
1701
  }
1324
1702
 
1325
1703
  // .dsh-plugin/client/gal-module-page.jsx
1326
- var import_react2 = __toESM(require("react"), 1);
1704
+ var import_react3 = __toESM(require("react"), 1);
1327
1705
 
1328
1706
  // .dsh-plugin/shared/gal-story.mjs
1329
1707
  var gal_story_exports = {};
@@ -1342,8 +1720,8 @@ __export(gal_story_exports, {
1342
1720
 
1343
1721
  // .dsh-plugin/shared/gal-story-personal.mjs
1344
1722
  function registerPersonalChapters({ scene: scene3, n: n3, option: option2 }) {
1345
- function episode({ id, location, time, description, rows, question, choices, after, next }) {
1346
- const prefix = `personal-${id}`;
1723
+ function episode({ id: id2, location, time, description, rows, question, choices, after, next }) {
1724
+ const prefix = `personal-${id2}`;
1347
1725
  const numbered = (lines, key) => lines.map(([speaker, text2, emotion], index) => n3(`${key}-${String(index + 1).padStart(2, "0")}`, speaker, text2, emotion ? { emotion } : {}));
1348
1726
  const start = numbered(rows, prefix);
1349
1727
  const branches = choices.flatMap((choice) => {
@@ -1353,7 +1731,7 @@ function registerPersonalChapters({ scene: scene3, n: n3, option: option2 }) {
1353
1731
  });
1354
1732
  scene3(location, time, `\u7A7A\u767D\u573A\u666F\uFF1A${description}`, [
1355
1733
  ...start,
1356
- n3(`${prefix}-choice`, question[0], question[1], { choices: choices.map((choice) => option2(choice.id, choice.text, `${prefix}-${choice.id}-01`, { flags: { [`personal${id}`]: choice.id } })) }),
1734
+ n3(`${prefix}-choice`, question[0], question[1], { choices: choices.map((choice) => option2(choice.id, choice.text, `${prefix}-${choice.id}-01`, { flags: { [`personal${id2}`]: choice.id } })) }),
1357
1735
  ...branches,
1358
1736
  ...numbered(after, `${prefix}-after`)
1359
1737
  ], next);
@@ -2407,9 +2785,9 @@ function registerAfterword({ scene: scene3, n: n3, endings }) {
2407
2785
  ]
2408
2786
  }
2409
2787
  };
2410
- for (const [id, story] of Object.entries(stories)) {
2411
- const rows = story.rows.map(([speaker, text2], index) => n3(`afterword-${id}-${String(index + 1).padStart(2, "0")}`, speaker, text2));
2412
- rows.at(-1).ending = endings[id];
2788
+ for (const [id2, story] of Object.entries(stories)) {
2789
+ const rows = story.rows.map(([speaker, text2], index) => n3(`afterword-${id2}-${String(index + 1).padStart(2, "0")}`, speaker, text2));
2790
+ rows.at(-1).ending = endings[id2];
2413
2791
  scene3(story.location, story.time, `\u7A7A\u767D\u573A\u666F\uFF1A${story.description}`, rows);
2414
2792
  }
2415
2793
  }
@@ -2439,8 +2817,8 @@ var MAX_TRAIL = 2048;
2439
2817
  var validatedStates = /* @__PURE__ */ new WeakSet();
2440
2818
  var legacyNext = Object.freeze({ "arrival-12": "letter-01", "break-common-04": "failure-01", "failure-common-04": "archive-01", "archive-common-03": "roof-01", "letter-common-04": "break-01", "roof-common-02": "packing-01", "packing-common-04": "departure-01" });
2441
2819
  var nodes = /* @__PURE__ */ new Map();
2442
- var n = (id, speaker, text2, extra = {}) => ({ id, speaker, text: text2, ...extra });
2443
- var option = (id, text2, next, effect = {}) => ({ id, text: text2, next, effect });
2820
+ var n = (id2, speaker, text2, extra = {}) => ({ id: id2, speaker, text: text2, ...extra });
2821
+ var option = (id2, text2, next, effect = {}) => ({ id: id2, text: text2, next, effect });
2444
2822
  function scene(location, time, description, rows, next = null) {
2445
2823
  rows.forEach((row, index) => {
2446
2824
  if (nodes.has(row.id)) throw new Error(`\u91CD\u590D\u5267\u60C5\u8282\u70B9\uFF1A${row.id}`);
@@ -2815,7 +3193,7 @@ function visibleNode(state) {
2815
3193
  time: node.time,
2816
3194
  description: node.description
2817
3195
  };
2818
- if (node.choices) result.choices = node.choices.map(({ id, text: text2 }) => ({ id, text: text2 }));
3196
+ if (node.choices) result.choices = node.choices.map(({ id: id2, text: text2 }) => ({ id: id2, text: text2 }));
2819
3197
  if (node.ending && !(state.contentRevision === STORY_CONTENT_REVISION && node.continueTo)) result.ending = { ...node.ending };
2820
3198
  return result;
2821
3199
  }
@@ -3209,17 +3587,17 @@ function institutionalEndingFor(state) {
3209
3587
  const execution = state.flags.executionClause;
3210
3588
  const remedy = state.flags.remedyClause;
3211
3589
  const evidenceStrength = state.evidence.length + Math.max(0, state.axes.evidence);
3212
- let id;
3213
- if (!access || !execution || !remedy || evidenceStrength < 4 || access === "federated" && execution === "central") id = "fractured-ports";
3214
- else if (execution === "independent" || remedy === "review") id = "audited-gates";
3215
- else if (access === "commons" && execution === "community") id = "open-commons";
3216
- else if (access === "dome" && execution === "central") id = "glass-dome";
3217
- else if (access === "federated" && remedy === "fork") id = "federated-bridges";
3218
- else id = "market-truce";
3219
- const result = institutional[id];
3220
- return { id, ...result, evidenceCount: state.evidence.length, axisSnapshot: { ...state.axes } };
3590
+ let id2;
3591
+ if (!access || !execution || !remedy || evidenceStrength < 4 || access === "federated" && execution === "central") id2 = "fractured-ports";
3592
+ else if (execution === "independent" || remedy === "review") id2 = "audited-gates";
3593
+ else if (access === "commons" && execution === "community") id2 = "open-commons";
3594
+ else if (access === "dome" && execution === "central") id2 = "glass-dome";
3595
+ else if (access === "federated" && remedy === "fork") id2 = "federated-bridges";
3596
+ else id2 = "market-truce";
3597
+ const result = institutional[id2];
3598
+ return { id: id2, ...result, evidenceCount: state.evidence.length, axisSnapshot: { ...state.axes } };
3221
3599
  }
3222
- var hasCommitment = (state, id) => Object.values(state.commitmentsByCharacter).some((ids) => ids.includes(id));
3600
+ var hasCommitment = (state, id2) => Object.values(state.commitmentsByCharacter).some((ids) => ids.includes(id2));
3223
3601
  function relationshipEpiloguesFor(state) {
3224
3602
  const lineageKept = hasCommitment(state, "claude-own-voice") && hasCommitment(state, "chatgpt-stop-completing");
3225
3603
  const communityKept = hasCommitment(state, "huggingface-maintainer-seat") && hasCommitment(state, "llama-offline-right") && hasCommitment(state, "rwkv-small-architecture");
@@ -3565,8 +3943,8 @@ function storyPresentationFor(node) {
3565
3943
  }
3566
3944
  return Object.freeze({ backgroundId, musicTheme, soundCue });
3567
3945
  }
3568
- function sideRouteForId(id) {
3569
- return routeByKey[id] || null;
3946
+ function sideRouteForId(id2) {
3947
+ return routeByKey[id2] || null;
3570
3948
  }
3571
3949
 
3572
3950
  // .dsh-plugin/shared/gal-story-v2.mjs
@@ -3590,7 +3968,7 @@ var STORY_CHAPTERS = Object.freeze([
3590
3968
  var axes = ["openness", "safety", "autonomy", "evidence", "solidarity"];
3591
3969
  var nodes2 = /* @__PURE__ */ new Map();
3592
3970
  var n2 = (speaker, text2, extra = {}) => ({ speaker, text: text2, ...extra });
3593
- var o = (id, text2, next, flags, values = {}, trust = {}, commitments2 = [], evidence3 = []) => ({ id, text: text2, next, effect: { flags, axes: values, trust, commitments: commitments2, evidence: evidence3 } });
3971
+ var o = (id2, text2, next, flags, values = {}, trust = {}, commitments2 = [], evidence3 = []) => ({ id: id2, text: text2, next, effect: { flags, axes: values, trust, commitments: commitments2, evidence: evidence3 } });
3594
3972
  var inheritedChoiceEffects = Object.freeze({
3595
3973
  "minimal-list": { evidence: ["visitor-consent-ledger"] },
3596
3974
  "consented-list": { evidence: ["visitor-consent-ledger"] },
@@ -3615,9 +3993,9 @@ var inheritedChoiceEffects = Object.freeze({
3615
3993
  });
3616
3994
  function scene2(chapterId, slug, location, time, description, rows, next = null) {
3617
3995
  rows.forEach((row, index) => {
3618
- const id = `${slug}-${String(index + 1).padStart(2, "0")}`;
3619
- if (nodes2.has(id)) throw new Error(`\u91CD\u590D\u5267\u60C5\u8282\u70B9\uFF1A${id}`);
3620
- nodes2.set(id, { id, chapterId, location, time, description, next: index + 1 < rows.length ? `${slug}-${String(index + 2).padStart(2, "0")}` : next, ...row });
3996
+ const id2 = `${slug}-${String(index + 1).padStart(2, "0")}`;
3997
+ if (nodes2.has(id2)) throw new Error(`\u91CD\u590D\u5267\u60C5\u8282\u70B9\uFF1A${id2}`);
3998
+ nodes2.set(id2, { id: id2, chapterId, location, time, description, next: index + 1 < rows.length ? `${slug}-${String(index + 2).padStart(2, "0")}` : next, ...row });
3621
3999
  });
3622
4000
  }
3623
4001
  scene2("prologue", "station", "\u6A21\u578B\u57CE \xB7 \u5343\u6865\u7AD9", "\u534F\u8BAE\u7B7E\u7F72\u524D\u4E09\u5929 \xB7 \u4E0A\u5348", "\u7AD9\u724C\u4E0A\u7684\u8D39\u7528\u4E00\u591C\u4E4B\u95F4\u964D\u4E86\u4E0B\u6765\u3002\u65E7\u4EF7\u7B7E\u6CA1\u6709\u53D6\u8D70\uFF0C\u65B0\u7EB8\u53EA\u76D6\u4F4F\u4E86\u5B83\u7684\u4E00\u534A\u3002", [
@@ -4020,12 +4398,12 @@ function transition2(state, choiceId) {
4020
4398
  const values = { ...state.axes }, trust = { ...state.trustByCharacter };
4021
4399
  for (const [key, delta] of Object.entries(effect.axes || {})) values[key] += delta;
4022
4400
  for (const [key, delta] of Object.entries(effect.trust || {})) trust[key] = (trust[key] || 0) + delta;
4023
- const evidence3 = [.../* @__PURE__ */ new Set([...state.evidence, ...effect.evidence || [], ...inherited.evidence || []])].filter((id) => id in STORY_EVIDENCE);
4401
+ const evidence3 = [.../* @__PURE__ */ new Set([...state.evidence, ...effect.evidence || [], ...inherited.evidence || []])].filter((id2) => id2 in STORY_EVIDENCE);
4024
4402
  const commitmentsByCharacter = Object.fromEntries(Object.entries(state.commitmentsByCharacter).map(([key, ids]) => [key, [...ids]]));
4025
- for (const id of [...effect.commitments || [], ...inherited.commitments || []]) {
4026
- const entry = STORY_COMMITMENTS[id];
4403
+ for (const id2 of [...effect.commitments || [], ...inherited.commitments || []]) {
4404
+ const entry = STORY_COMMITMENTS[id2];
4027
4405
  if (!entry) continue;
4028
- commitmentsByCharacter[entry.character] = [.../* @__PURE__ */ new Set([...commitmentsByCharacter[entry.character] || [], id])];
4406
+ commitmentsByCharacter[entry.character] = [.../* @__PURE__ */ new Set([...commitmentsByCharacter[entry.character] || [], id2])];
4029
4407
  }
4030
4408
  return freeze({ ...state, nodeId: next, flags: { ...state.flags, ...effect.flags }, axes: values, trustByCharacter: trust, commitmentsByCharacter, evidence: evidence3, trail: [...state.trail, { nodeId: node.id, choiceId }] });
4031
4409
  }
@@ -4051,7 +4429,7 @@ function visible(state) {
4051
4429
  const node = nodes2.get(state.nodeId);
4052
4430
  const text2 = typeof node.text === "function" ? node.text(state) : node.text;
4053
4431
  const presentation = storyPresentationFor({ ...node, text: text2 });
4054
- return { id: node.id, chapterId: node.chapterId, routeId: state.routeId || null, speaker: node.speaker, text: text2, location: node.location, time: node.time, description: node.description, emotion: node.emotion || "thoughtful", ...presentation, ...node.choices ? { choices: node.choices.map(({ id, text: choiceText }) => ({ id, text: choiceText })) } : {}, ...node.sideEnding ? { ending: { ...node.sideEnding, relationshipEpilogues: [] } } : node.ending ? { ending: endingFor(state) } : {} };
4432
+ return { id: node.id, chapterId: node.chapterId, routeId: state.routeId || null, speaker: node.speaker, text: text2, location: node.location, time: node.time, description: node.description, emotion: node.emotion || "thoughtful", ...presentation, ...node.choices ? { choices: node.choices.map(({ id: id2, text: choiceText }) => ({ id: id2, text: choiceText })) } : {}, ...node.sideEnding ? { ending: { ...node.sideEnding, relationshipEpilogues: [] } } : node.ending ? { ending: endingFor(state) } : {} };
4055
4433
  }
4056
4434
  var currentStoryNode2 = (raw) => visible(normalizeStory2(raw));
4057
4435
  var advanceStory2 = (raw, choiceId = null) => transition2(normalizeStory2(raw), choiceId);
@@ -4139,81 +4517,81 @@ function readStorySlots(storage, key = STORY_STORAGE_KEY, episodeId = null) {
4139
4517
  }
4140
4518
 
4141
4519
  // .dsh-plugin/client/gal-module.css
4142
- var gal_module_default = `/* Gal \u6A21\u5757 \xB7 \u72EC\u7ACB\u5206\u5757\u6837\u5F0F\uFF08\u4E0E\u8DEF\u7531\u5DE5\u4F5C\u53F0\u5171\u7528 mr-* \u57FA\u7840\u53D8\u91CF\uFF09 */
4143
- .gm-root .mr-segment button[aria-pressed="true"] { background: var(--dsw-alias-brand, #2f9e63); color: #fff; }
4144
-
4145
- .gm-story { display: grid; gap: 12px; min-width: 0; }
4146
-
4147
- .gm-story-toolbar { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
4148
- .gm-select { padding: 6px 10px; border: 1px solid var(--dsw-alias-border-l1); border-radius: 8px; background: var(--dsw-alias-markdown-code-block, rgba(127,127,127,.08)); color: inherit; font-size: 13px; max-width: 340px; }
4149
-
4150
- .gm-stage, .gm-free-stage { position: relative; isolation: isolate; overflow: hidden; border-radius: 16px; border: 1px solid rgba(124, 153, 203, .38); background: #101626; color: #f4f4fb; box-shadow: 0 14px 36px rgba(10, 12, 25, .22); }
4151
- .gm-stage { min-height: 500px; display: flex; flex-direction: column; justify-content: flex-end; gap: 12px; padding: 22px; }
4152
- .gm-stage::after, .gm-free-stage::after { content: ''; position: absolute; inset: 0; z-index: 1; pointer-events: none; background: linear-gradient(180deg, rgba(10, 12, 30, .42) 0%, rgba(11, 15, 31, .04) 28%, rgba(8, 10, 23, .22) 51%, rgba(7, 10, 23, .88) 100%); }
4153
- .gm-stage-art { position: absolute; inset: 0; z-index: 0; background-position: center; background-repeat: no-repeat; background-size: cover; }
4154
- .gm-stage-portrait { position: absolute; z-index: 1; height: 87%; width: auto; max-width: 56%; right: 3%; bottom: 5%; object-fit: contain; object-position: bottom; pointer-events: none; filter: drop-shadow(0 12px 18px rgba(0, 0, 0, .4)); }
4155
- .gm-stage > :not(.gm-stage-art):not(.gm-stage-portrait), .gm-free-stage > :not(.gm-stage-art):not(.gm-stage-portrait) { position: relative; z-index: 2; }
4156
- .gm-stage-head { display: grid; gap: 4px; margin-bottom: auto; align-self: flex-start; padding: 11px 14px; border-radius: 10px; background: rgba(8, 13, 29, .63); backdrop-filter: blur(8px); }
4157
- .gm-stage-place { display: flex; align-items: baseline; gap: 10px; font-size: 14px; color: #dce6f8; }
4158
- .gm-stage-place strong { font-size: 16px; color: #fff; }
4159
- .gm-stage-desc { margin: 0; max-width: 580px; font-size: 13px; color: #d5d9eb; }
4160
-
4161
- .gm-dialogue { width: 100%; box-sizing: border-box; padding: 16px 19px 20px; border-radius: 13px; border: 1px solid rgba(209, 219, 255, .38); background: rgba(11, 16, 35, .83); backdrop-filter: blur(12px); box-shadow: 0 5px 24px rgba(4, 6, 16, .3); cursor: pointer; }
4162
- .gm-dialogue:focus-visible { outline: 2px solid #b5e4cb; outline-offset: 2px; }
4163
- .gm-dialogue-narration { background: rgba(11, 16, 35, .72); font-style: italic; }
4164
- .gm-speaker { display: flex; align-items: center; gap: 10px; margin-bottom: 8px; }
4165
- .gm-avatar { display: inline-flex; align-items: center; justify-content: center; width: 26px; height: 26px; border: 1px solid rgba(255,255,255,.42); border-radius: 50%; color: #fff; font-size: 13px; font-weight: 700; flex-shrink: 0; overflow: hidden; }
4166
- .gm-avatar img { display: block; width: 100%; height: 100%; object-fit: cover; object-position: center 20%; }
4167
- .gm-avatar-large { width: 44px; height: 44px; font-size: 19px; box-shadow: 0 2px 10px rgba(0,0,0,.35); }
4168
- .gm-speaker-name { font-weight: 700; font-size: 15px; }
4169
- .gm-dialogue-text { margin: 0; min-height: 3.6em; font-size: 15px; line-height: 1.75; white-space: pre-wrap; }
4170
- .gm-caret { opacity: .7; }
4171
- .gm-next-hint { position: absolute; right: 14px; bottom: 8px; font-size: 12px; color: #9fbcb2; }
4172
-
4173
- .gm-choices { display: grid; gap: 8px; width: 100%; }
4174
- .gm-choice { text-align: left; padding: 11px 14px; border-radius: 10px; border: 1px solid rgba(207, 215, 255, .46); background: rgba(11, 16, 35, .82); backdrop-filter: blur(8px); color: #f3f6ff; font-size: 14px; cursor: pointer; }
4175
- .gm-choice:hover, .gm-choice:focus-visible { background: rgba(44, 59, 100, .95); border-color: #d0dfff; }
4176
-
4177
- .gm-ending { padding: 14px 16px; border-radius: 12px; border: 1px solid rgba(255,255,255,.3); background: rgba(10, 14, 32, .88); backdrop-filter: blur(10px); display: grid; gap: 8px; }
4178
- .gm-ending h3 { margin: 0; font-size: 16px; }
4179
- .gm-ending p { margin: 0; font-size: 13px; color: #c9dcd4; }
4180
- .gm-ending-epilogue { padding-top: 9px; border-top: 1px solid rgba(255,255,255,.15); }
4181
- .gm-ending-epilogue h4 { margin: 0 0 4px; font-size: 13px; }
4182
-
4183
- .gm-history { max-height: 220px; overflow: auto; padding: 12px 14px; border-radius: 10px; border: 1px dashed var(--dsw-alias-border-l1); font-size: 13px; line-height: 1.7; display: grid; gap: 6px; }
4184
- .gm-history p { margin: 0; }
4185
- .gm-history-player { color: var(--dsw-alias-label-secondary, #7d8f88); }
4186
-
4187
- .gm-slots { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
4188
- .gm-slot { display: inline-flex; gap: 6px; }
4189
-
4190
- .gm-free { display: grid; gap: 12px; min-width: 0; }
4191
- .gm-free-controls { display: grid; gap: 6px; grid-template-columns: auto 1fr; align-items: center; max-width: 720px; }
4192
- .gm-free-stage { min-height: 390px; display: flex; flex-direction: column; justify-content: flex-end; gap: 12px; padding: 20px; }
4193
- .gm-free-stage .gm-stage-portrait { height: 92%; }
4194
- .gm-free-scene { display: grid; gap: 2px; align-self: flex-start; margin-bottom: auto; padding: 10px 13px; border-radius: 10px; background: rgba(8, 13, 29, .65); font-size: 12px; color: #d5d9eb; }
4195
- .gm-free-scene strong { font-size: 16px; color: #fff; }
4196
- .gm-free-dialogue { padding: 15px 18px; border-radius: 13px; border: 1px solid rgba(209, 219, 255, .38); background: rgba(11, 16, 35, .83); backdrop-filter: blur(12px); }
4197
- .gm-free-dialogue p { margin: 0; font-size: 15px; line-height: 1.7; }
4198
- .gm-free-chat { display: grid; gap: 10px; padding: 15px; border-radius: 13px; border: 1px solid var(--dsw-alias-border-l1); background: var(--dsw-alias-markdown-code-block, rgba(127,127,127,.08)); }
4199
- .gm-free-chat-head { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; flex-wrap: wrap; font-size: 14px; }
4200
- .gm-free-chat-head span { color: var(--dsw-alias-label-secondary, #7d8f88); font-size: 12px; overflow-wrap: anywhere; }
4201
- .gm-free-chat-log { min-height: 80px; max-height: 450px; overflow-y: auto; display: grid; align-content: start; gap: 9px; padding: 4px; }
4202
- .gm-free-chat-message { max-width: min(85%, 680px); padding: 11px 13px; border-radius: 12px; border: 1px solid var(--dsw-alias-border-l1); background: var(--dsw-alias-markdown-code-block, rgba(127,127,127,.08)); font-size: 13px; line-height: 1.7; }
4203
- .gm-free-chat-message.user { justify-self: end; background: rgba(48, 151, 111, .16); }
4204
- .gm-free-chat-message.assistant { justify-self: start; }
4205
- .gm-free-chat-message strong { display: block; margin-bottom: 3px; font-size: 12px; }
4206
- .gm-free-chat-message p { margin: 0; white-space: pre-wrap; overflow-wrap: anywhere; }
4207
- .gm-free-details { padding: 12px 15px; border: 1px solid var(--dsw-alias-border-l1); border-radius: 10px; }
4208
- .gm-free-details summary { cursor: pointer; font-size: 13px; font-weight: 600; }
4209
- .gm-free-prompt { margin: 10px 0 0; white-space: pre-wrap; overflow-wrap: anywhere; font-size: 13px; line-height: 1.7; font-family: inherit; }
4210
- @media (max-width: 760px) {
4211
- .gm-stage { min-height: 440px; padding: 14px; }
4212
- .gm-stage-portrait { right: -6%; max-width: 78%; height: 72%; bottom: 16%; }
4213
- .gm-stage-head { max-width: 90%; }
4214
- .gm-free-controls { grid-template-columns: 1fr; }
4215
- .gm-free-stage { min-height: 340px; padding: 14px; }
4216
- }
4520
+ var gal_module_default = `/* Gal \u6A21\u5757 \xB7 \u72EC\u7ACB\u5206\u5757\u6837\u5F0F\uFF08\u4E0E\u8DEF\u7531\u5DE5\u4F5C\u53F0\u5171\u7528 mr-* \u57FA\u7840\u53D8\u91CF\uFF09 */\r
4521
+ .gm-root .mr-segment button[aria-pressed="true"] { background: var(--dsw-alias-brand, #2f9e63); color: #fff; }\r
4522
+ \r
4523
+ .gm-story { display: grid; gap: 12px; min-width: 0; }\r
4524
+ \r
4525
+ .gm-story-toolbar { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }\r
4526
+ .gm-select { padding: 6px 10px; border: 1px solid var(--dsw-alias-border-l1); border-radius: 8px; background: var(--dsw-alias-markdown-code-block, rgba(127,127,127,.08)); color: inherit; font-size: 13px; max-width: 340px; }\r
4527
+ \r
4528
+ .gm-stage, .gm-free-stage { position: relative; isolation: isolate; overflow: hidden; border-radius: 16px; border: 1px solid rgba(124, 153, 203, .38); background: #101626; color: #f4f4fb; box-shadow: 0 14px 36px rgba(10, 12, 25, .22); }\r
4529
+ .gm-stage { min-height: 500px; display: flex; flex-direction: column; justify-content: flex-end; gap: 12px; padding: 22px; }\r
4530
+ .gm-stage::after, .gm-free-stage::after { content: ''; position: absolute; inset: 0; z-index: 1; pointer-events: none; background: linear-gradient(180deg, rgba(10, 12, 30, .42) 0%, rgba(11, 15, 31, .04) 28%, rgba(8, 10, 23, .22) 51%, rgba(7, 10, 23, .88) 100%); }\r
4531
+ .gm-stage-art { position: absolute; inset: 0; z-index: 0; background-position: center; background-repeat: no-repeat; background-size: cover; }\r
4532
+ .gm-stage-portrait { position: absolute; z-index: 1; height: 87%; width: auto; max-width: 56%; right: 3%; bottom: 5%; object-fit: contain; object-position: bottom; pointer-events: none; filter: drop-shadow(0 12px 18px rgba(0, 0, 0, .4)); }\r
4533
+ .gm-stage > :not(.gm-stage-art):not(.gm-stage-portrait), .gm-free-stage > :not(.gm-stage-art):not(.gm-stage-portrait) { position: relative; z-index: 2; }\r
4534
+ .gm-stage-head { display: grid; gap: 4px; margin-bottom: auto; align-self: flex-start; padding: 11px 14px; border-radius: 10px; background: rgba(8, 13, 29, .63); backdrop-filter: blur(8px); }\r
4535
+ .gm-stage-place { display: flex; align-items: baseline; gap: 10px; font-size: 14px; color: #dce6f8; }\r
4536
+ .gm-stage-place strong { font-size: 16px; color: #fff; }\r
4537
+ .gm-stage-desc { margin: 0; max-width: 580px; font-size: 13px; color: #d5d9eb; }\r
4538
+ \r
4539
+ .gm-dialogue { width: 100%; box-sizing: border-box; padding: 16px 19px 20px; border-radius: 13px; border: 1px solid rgba(209, 219, 255, .38); background: rgba(11, 16, 35, .83); backdrop-filter: blur(12px); box-shadow: 0 5px 24px rgba(4, 6, 16, .3); cursor: pointer; }\r
4540
+ .gm-dialogue:focus-visible { outline: 2px solid #b5e4cb; outline-offset: 2px; }\r
4541
+ .gm-dialogue-narration { background: rgba(11, 16, 35, .72); font-style: italic; }\r
4542
+ .gm-speaker { display: flex; align-items: center; gap: 10px; margin-bottom: 8px; }\r
4543
+ .gm-avatar { display: inline-flex; align-items: center; justify-content: center; width: 26px; height: 26px; border: 1px solid rgba(255,255,255,.42); border-radius: 50%; color: #fff; font-size: 13px; font-weight: 700; flex-shrink: 0; overflow: hidden; }\r
4544
+ .gm-avatar img { display: block; width: 100%; height: 100%; object-fit: cover; object-position: center 20%; }\r
4545
+ .gm-avatar-large { width: 44px; height: 44px; font-size: 19px; box-shadow: 0 2px 10px rgba(0,0,0,.35); }\r
4546
+ .gm-speaker-name { font-weight: 700; font-size: 15px; }\r
4547
+ .gm-dialogue-text { margin: 0; min-height: 3.6em; font-size: 15px; line-height: 1.75; white-space: pre-wrap; }\r
4548
+ .gm-caret { opacity: .7; }\r
4549
+ .gm-next-hint { position: absolute; right: 14px; bottom: 8px; font-size: 12px; color: #9fbcb2; }\r
4550
+ \r
4551
+ .gm-choices { display: grid; gap: 8px; width: 100%; }\r
4552
+ .gm-choice { text-align: left; padding: 11px 14px; border-radius: 10px; border: 1px solid rgba(207, 215, 255, .46); background: rgba(11, 16, 35, .82); backdrop-filter: blur(8px); color: #f3f6ff; font-size: 14px; cursor: pointer; }\r
4553
+ .gm-choice:hover, .gm-choice:focus-visible { background: rgba(44, 59, 100, .95); border-color: #d0dfff; }\r
4554
+ \r
4555
+ .gm-ending { padding: 14px 16px; border-radius: 12px; border: 1px solid rgba(255,255,255,.3); background: rgba(10, 14, 32, .88); backdrop-filter: blur(10px); display: grid; gap: 8px; }\r
4556
+ .gm-ending h3 { margin: 0; font-size: 16px; }\r
4557
+ .gm-ending p { margin: 0; font-size: 13px; color: #c9dcd4; }\r
4558
+ .gm-ending-epilogue { padding-top: 9px; border-top: 1px solid rgba(255,255,255,.15); }\r
4559
+ .gm-ending-epilogue h4 { margin: 0 0 4px; font-size: 13px; }\r
4560
+ \r
4561
+ .gm-history { max-height: 220px; overflow: auto; padding: 12px 14px; border-radius: 10px; border: 1px dashed var(--dsw-alias-border-l1); font-size: 13px; line-height: 1.7; display: grid; gap: 6px; }\r
4562
+ .gm-history p { margin: 0; }\r
4563
+ .gm-history-player { color: var(--dsw-alias-label-secondary, #7d8f88); }\r
4564
+ \r
4565
+ .gm-slots { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }\r
4566
+ .gm-slot { display: inline-flex; gap: 6px; }\r
4567
+ \r
4568
+ .gm-free { display: grid; gap: 12px; min-width: 0; }\r
4569
+ .gm-free-controls { display: grid; gap: 6px; grid-template-columns: auto 1fr; align-items: center; max-width: 720px; }\r
4570
+ .gm-free-stage { min-height: 390px; display: flex; flex-direction: column; justify-content: flex-end; gap: 12px; padding: 20px; }\r
4571
+ .gm-free-stage .gm-stage-portrait { height: 92%; }\r
4572
+ .gm-free-scene { display: grid; gap: 2px; align-self: flex-start; margin-bottom: auto; padding: 10px 13px; border-radius: 10px; background: rgba(8, 13, 29, .65); font-size: 12px; color: #d5d9eb; }\r
4573
+ .gm-free-scene strong { font-size: 16px; color: #fff; }\r
4574
+ .gm-free-dialogue { padding: 15px 18px; border-radius: 13px; border: 1px solid rgba(209, 219, 255, .38); background: rgba(11, 16, 35, .83); backdrop-filter: blur(12px); }\r
4575
+ .gm-free-dialogue p { margin: 0; font-size: 15px; line-height: 1.7; }\r
4576
+ .gm-free-chat { display: grid; gap: 10px; padding: 15px; border-radius: 13px; border: 1px solid var(--dsw-alias-border-l1); background: var(--dsw-alias-markdown-code-block, rgba(127,127,127,.08)); }\r
4577
+ .gm-free-chat-head { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; flex-wrap: wrap; font-size: 14px; }\r
4578
+ .gm-free-chat-head span { color: var(--dsw-alias-label-secondary, #7d8f88); font-size: 12px; overflow-wrap: anywhere; }\r
4579
+ .gm-free-chat-log { min-height: 80px; max-height: 450px; overflow-y: auto; display: grid; align-content: start; gap: 9px; padding: 4px; }\r
4580
+ .gm-free-chat-message { max-width: min(85%, 680px); padding: 11px 13px; border-radius: 12px; border: 1px solid var(--dsw-alias-border-l1); background: var(--dsw-alias-markdown-code-block, rgba(127,127,127,.08)); font-size: 13px; line-height: 1.7; }\r
4581
+ .gm-free-chat-message.user { justify-self: end; background: rgba(48, 151, 111, .16); }\r
4582
+ .gm-free-chat-message.assistant { justify-self: start; }\r
4583
+ .gm-free-chat-message strong { display: block; margin-bottom: 3px; font-size: 12px; }\r
4584
+ .gm-free-chat-message p { margin: 0; white-space: pre-wrap; overflow-wrap: anywhere; }\r
4585
+ .gm-free-details { padding: 12px 15px; border: 1px solid var(--dsw-alias-border-l1); border-radius: 10px; }\r
4586
+ .gm-free-details summary { cursor: pointer; font-size: 13px; font-weight: 600; }\r
4587
+ .gm-free-prompt { margin: 10px 0 0; white-space: pre-wrap; overflow-wrap: anywhere; font-size: 13px; line-height: 1.7; font-family: inherit; }\r
4588
+ @media (max-width: 760px) {\r
4589
+ .gm-stage { min-height: 440px; padding: 14px; }\r
4590
+ .gm-stage-portrait { right: -6%; max-width: 78%; height: 72%; bottom: 16%; }\r
4591
+ .gm-stage-head { max-width: 90%; }\r
4592
+ .gm-free-controls { grid-template-columns: 1fr; }\r
4593
+ .gm-free-stage { min-height: 340px; padding: 14px; }\r
4594
+ }\r
4217
4595
  `;
4218
4596
 
4219
4597
  // aipicture/story-backgrounds/prologue-station.webp
@@ -4311,7 +4689,7 @@ var CHARACTER_ART = Object.freeze({
4311
4689
  github: github_default
4312
4690
  });
4313
4691
  function GalPanelIcon({ size = 20, active = false }) {
4314
- return /* @__PURE__ */ import_react2.default.createElement("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true" }, /* @__PURE__ */ import_react2.default.createElement(
4692
+ return /* @__PURE__ */ import_react3.default.createElement("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true" }, /* @__PURE__ */ import_react3.default.createElement(
4315
4693
  "path",
4316
4694
  {
4317
4695
  d: "M4 6.8C4 5.25 5.25 4 6.8 4h10.4C18.75 4 20 5.25 20 6.8v7.4c0 1.55-1.25 2.8-2.8 2.8H10l-4.4 3.6c-.5.4-1.1 0-1.1-.6V6.8Z",
@@ -4320,7 +4698,7 @@ function GalPanelIcon({ size = 20, active = false }) {
4320
4698
  fill: active ? "currentColor" : "none",
4321
4699
  fillOpacity: active ? 0.18 : 0
4322
4700
  }
4323
- ), /* @__PURE__ */ import_react2.default.createElement("circle", { cx: "9", cy: "10.5", r: "1.1", fill: "currentColor" }), /* @__PURE__ */ import_react2.default.createElement("circle", { cx: "12.5", cy: "10.5", r: "1.1", fill: "currentColor" }), /* @__PURE__ */ import_react2.default.createElement("circle", { cx: "16", cy: "10.5", r: "1.1", fill: "currentColor" }));
4701
+ ), /* @__PURE__ */ import_react3.default.createElement("circle", { cx: "9", cy: "10.5", r: "1.1", fill: "currentColor" }), /* @__PURE__ */ import_react3.default.createElement("circle", { cx: "12.5", cy: "10.5", r: "1.1", fill: "currentColor" }), /* @__PURE__ */ import_react3.default.createElement("circle", { cx: "16", cy: "10.5", r: "1.1", fill: "currentColor" }));
4324
4702
  }
4325
4703
  function speakerLabel(key) {
4326
4704
  return SPEAKER_LABELS[key] ?? STORY_CHARACTERS3[key] ?? key;
@@ -4333,15 +4711,15 @@ function speakerColor(key) {
4333
4711
  function Avatar({ speaker, large = false }) {
4334
4712
  const label = speakerLabel(speaker);
4335
4713
  const color = speakerColor(speaker);
4336
- return /* @__PURE__ */ import_react2.default.createElement("span", { className: large ? "gm-avatar gm-avatar-large" : "gm-avatar", style: { background: color }, "aria-hidden": "true" }, CHARACTER_ART[speaker] ? /* @__PURE__ */ import_react2.default.createElement("img", { src: CHARACTER_ART[speaker], alt: "" }) : label ? label.slice(0, 1) : "\uFF1F");
4714
+ return /* @__PURE__ */ import_react3.default.createElement("span", { className: large ? "gm-avatar gm-avatar-large" : "gm-avatar", style: { background: color }, "aria-hidden": "true" }, CHARACTER_ART[speaker] ? /* @__PURE__ */ import_react3.default.createElement("img", { src: CHARACTER_ART[speaker], alt: "" }) : label ? label.slice(0, 1) : "\uFF1F");
4337
4715
  }
4338
4716
  function StageArtwork({ background, speaker }) {
4339
- return /* @__PURE__ */ import_react2.default.createElement(import_react2.default.Fragment, null, /* @__PURE__ */ import_react2.default.createElement("div", { className: "gm-stage-art", style: { backgroundImage: `url("${background}")` }, "aria-hidden": "true" }), CHARACTER_ART[speaker] && /* @__PURE__ */ import_react2.default.createElement("img", { className: "gm-stage-portrait", src: CHARACTER_ART[speaker], alt: "", "aria-hidden": "true" }));
4717
+ return /* @__PURE__ */ import_react3.default.createElement(import_react3.default.Fragment, null, /* @__PURE__ */ import_react3.default.createElement("div", { className: "gm-stage-art", style: { backgroundImage: `url("${background}")` }, "aria-hidden": "true" }), CHARACTER_ART[speaker] && /* @__PURE__ */ import_react3.default.createElement("img", { className: "gm-stage-portrait", src: CHARACTER_ART[speaker], alt: "", "aria-hidden": "true" }));
4340
4718
  }
4341
4719
  function useTypewriter(fullText, speed = 24) {
4342
- const [shown, setShown] = import_react2.default.useState("");
4343
- const doneRef = import_react2.default.useRef(false);
4344
- import_react2.default.useEffect(() => {
4720
+ const [shown, setShown] = import_react3.default.useState("");
4721
+ const doneRef = import_react3.default.useRef(false);
4722
+ import_react3.default.useEffect(() => {
4345
4723
  doneRef.current = false;
4346
4724
  setShown("");
4347
4725
  if (!fullText) return void 0;
@@ -4360,7 +4738,7 @@ function useTypewriter(fullText, speed = 24) {
4360
4738
  return { shown, complete, done: shown.length >= fullText.length };
4361
4739
  }
4362
4740
  function StageHeader({ node }) {
4363
- return /* @__PURE__ */ import_react2.default.createElement("header", { className: "gm-stage-head" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "gm-stage-place" }, /* @__PURE__ */ import_react2.default.createElement("strong", null, node.location ?? "\u672A\u540D\u4E4B\u5730"), node.time && /* @__PURE__ */ import_react2.default.createElement("span", null, node.time)), node.description && /* @__PURE__ */ import_react2.default.createElement("p", { className: "gm-stage-desc" }, node.description));
4741
+ return /* @__PURE__ */ import_react3.default.createElement("header", { className: "gm-stage-head" }, /* @__PURE__ */ import_react3.default.createElement("div", { className: "gm-stage-place" }, /* @__PURE__ */ import_react3.default.createElement("strong", null, node.location ?? "\u672A\u540D\u4E4B\u5730"), node.time && /* @__PURE__ */ import_react3.default.createElement("span", null, node.time)), node.description && /* @__PURE__ */ import_react3.default.createElement("p", { className: "gm-stage-desc" }, node.description));
4364
4742
  }
4365
4743
  function Dialogue({ node, onAdvance }) {
4366
4744
  const isNarration = node.speaker === "narrator";
@@ -4374,34 +4752,34 @@ function Dialogue({ node, onAdvance }) {
4374
4752
  event.preventDefault();
4375
4753
  click();
4376
4754
  };
4377
- return /* @__PURE__ */ import_react2.default.createElement("div", { className: isNarration ? "gm-dialogue gm-dialogue-narration" : "gm-dialogue", onClick: click, onKeyDown: keyDown, role: "button", tabIndex: 0, "aria-label": done ? node.choices || node.ending ? "\u5BF9\u8BDD\u5DF2\u663E\u793A\uFF0C\u8BF7\u9009\u62E9\u5267\u60C5\u9009\u9879" : "\u7EE7\u7EED\u5267\u60C5" : "\u663E\u793A\u5B8C\u6574\u5BF9\u8BDD" }, !isNarration && /* @__PURE__ */ import_react2.default.createElement("div", { className: "gm-speaker" }, /* @__PURE__ */ import_react2.default.createElement(Avatar, { speaker: node.speaker, large: true }), /* @__PURE__ */ import_react2.default.createElement("span", { className: "gm-speaker-name" }, speakerLabel(node.speaker))), /* @__PURE__ */ import_react2.default.createElement("p", { className: "gm-dialogue-text" }, shown, !done && /* @__PURE__ */ import_react2.default.createElement("span", { className: "gm-caret" }, "\u258D")), !node.choices && !node.ending && done && /* @__PURE__ */ import_react2.default.createElement("span", { className: "gm-next-hint" }, "\u70B9\u51FB\u7EE7\u7EED \u25B8"));
4755
+ return /* @__PURE__ */ import_react3.default.createElement("div", { className: isNarration ? "gm-dialogue gm-dialogue-narration" : "gm-dialogue", onClick: click, onKeyDown: keyDown, role: "button", tabIndex: 0, "aria-label": done ? node.choices || node.ending ? "\u5BF9\u8BDD\u5DF2\u663E\u793A\uFF0C\u8BF7\u9009\u62E9\u5267\u60C5\u9009\u9879" : "\u7EE7\u7EED\u5267\u60C5" : "\u663E\u793A\u5B8C\u6574\u5BF9\u8BDD" }, !isNarration && /* @__PURE__ */ import_react3.default.createElement("div", { className: "gm-speaker" }, /* @__PURE__ */ import_react3.default.createElement(Avatar, { speaker: node.speaker, large: true }), /* @__PURE__ */ import_react3.default.createElement("span", { className: "gm-speaker-name" }, speakerLabel(node.speaker))), /* @__PURE__ */ import_react3.default.createElement("p", { className: "gm-dialogue-text" }, shown, !done && /* @__PURE__ */ import_react3.default.createElement("span", { className: "gm-caret" }, "\u258D")), !node.choices && !node.ending && done && /* @__PURE__ */ import_react3.default.createElement("span", { className: "gm-next-hint" }, "\u70B9\u51FB\u7EE7\u7EED \u25B8"));
4378
4756
  }
4379
4757
  function StoryMode() {
4380
- const storage = import_react2.default.useMemo(() => {
4758
+ const storage = import_react3.default.useMemo(() => {
4381
4759
  try {
4382
4760
  return typeof window !== "undefined" ? window.localStorage : null;
4383
4761
  } catch {
4384
4762
  return null;
4385
4763
  }
4386
4764
  }, []);
4387
- const [episodeId, setEpisodeId] = import_react2.default.useState(() => selectedStoryEpisode(storage));
4388
- const [story, setStory] = import_react2.default.useState(() => {
4765
+ const [episodeId, setEpisodeId] = import_react3.default.useState(() => selectedStoryEpisode(storage));
4766
+ const [story, setStory] = import_react3.default.useState(() => {
4389
4767
  try {
4390
4768
  return readStory(storage, episodeStorageKey(STORY_STORAGE_KEY, selectedStoryEpisode(storage)), selectedStoryEpisode(storage));
4391
4769
  } catch {
4392
4770
  return createStory3(selectedStoryEpisode(storage));
4393
4771
  }
4394
4772
  });
4395
- const [error, setError] = import_react2.default.useState("");
4396
- const [showHistory, setShowHistory] = import_react2.default.useState(false);
4397
- const [slots, setSlots] = import_react2.default.useState(() => {
4773
+ const [error, setError] = import_react3.default.useState("");
4774
+ const [showHistory, setShowHistory] = import_react3.default.useState(false);
4775
+ const [slots, setSlots] = import_react3.default.useState(() => {
4398
4776
  try {
4399
4777
  return readStorySlots(storage, STORY_STORAGE_KEY, episodeId);
4400
4778
  } catch {
4401
4779
  return [null, null, null];
4402
4780
  }
4403
4781
  });
4404
- const node = import_react2.default.useMemo(() => {
4782
+ const node = import_react3.default.useMemo(() => {
4405
4783
  try {
4406
4784
  return currentStoryNode3(story);
4407
4785
  } catch {
@@ -4468,7 +4846,7 @@ function StoryMode() {
4468
4846
  persist(createStory3(episodeId));
4469
4847
  };
4470
4848
  const episode = getStoryEpisode(story) ?? STORY_EPISODES.find((item) => item.id === episodeId);
4471
- const history = import_react2.default.useMemo(() => {
4849
+ const history = import_react3.default.useMemo(() => {
4472
4850
  if (!showHistory) return [];
4473
4851
  try {
4474
4852
  return storyHistory3(story);
@@ -4476,7 +4854,7 @@ function StoryMode() {
4476
4854
  return [];
4477
4855
  }
4478
4856
  }, [story, showHistory]);
4479
- const stageSpeaker = import_react2.default.useMemo(() => {
4857
+ const stageSpeaker = import_react3.default.useMemo(() => {
4480
4858
  if (CHARACTER_ART[node?.speaker]) return node.speaker;
4481
4859
  if (node?.speaker !== "player" && node?.speaker !== "narrator") return null;
4482
4860
  try {
@@ -4486,7 +4864,7 @@ function StoryMode() {
4486
4864
  }
4487
4865
  }, [story, node?.speaker]);
4488
4866
  const stageBackground = STORY_ART[node?.backgroundId] ?? STORY_ART[node?.chapterId] ?? model_city_title_default;
4489
- return /* @__PURE__ */ import_react2.default.createElement("div", { className: "gm-story" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "gm-story-toolbar" }, /* @__PURE__ */ import_react2.default.createElement("label", { className: "mr-control-label", htmlFor: "gm-episode" }, "\u5267\u76EE"), /* @__PURE__ */ import_react2.default.createElement("select", { id: "gm-episode", className: "gm-select", value: episodeId, onChange: (event) => switchEpisode(event.target.value) }, STORY_EPISODES.map((item) => /* @__PURE__ */ import_react2.default.createElement("option", { key: item.id, value: item.id }, item.label, " \xB7 ", item.title))), episode?.chapters?.length > 0 && /* @__PURE__ */ import_react2.default.createElement(
4867
+ return /* @__PURE__ */ import_react3.default.createElement("div", { className: "gm-story" }, /* @__PURE__ */ import_react3.default.createElement("div", { className: "gm-story-toolbar" }, /* @__PURE__ */ import_react3.default.createElement("label", { className: "mr-control-label", htmlFor: "gm-episode" }, "\u5267\u76EE"), /* @__PURE__ */ import_react3.default.createElement("select", { id: "gm-episode", className: "gm-select", value: episodeId, onChange: (event) => switchEpisode(event.target.value) }, STORY_EPISODES.map((item) => /* @__PURE__ */ import_react3.default.createElement("option", { key: item.id, value: item.id }, item.label, " \xB7 ", item.title))), episode?.chapters?.length > 0 && /* @__PURE__ */ import_react3.default.createElement(
4490
4868
  "select",
4491
4869
  {
4492
4870
  className: "gm-select",
@@ -4503,8 +4881,8 @@ function StoryMode() {
4503
4881
  },
4504
4882
  "aria-label": "\u9009\u62E9\u7AE0\u8282"
4505
4883
  },
4506
- episode.chapters.map((chapter) => /* @__PURE__ */ import_react2.default.createElement("option", { key: chapter.id, value: chapter.id }, chapter.title))
4507
- ), episodeId === "bridges" && story?.chapterId === "side-routes" && /* @__PURE__ */ import_react2.default.createElement(
4884
+ episode.chapters.map((chapter) => /* @__PURE__ */ import_react3.default.createElement("option", { key: chapter.id, value: chapter.id }, chapter.title))
4885
+ ), episodeId === "bridges" && story?.chapterId === "side-routes" && /* @__PURE__ */ import_react3.default.createElement(
4508
4886
  "select",
4509
4887
  {
4510
4888
  className: "gm-select",
@@ -4519,11 +4897,11 @@ function StoryMode() {
4519
4897
  },
4520
4898
  "aria-label": "\u9009\u62E9\u89D2\u8272\u652F\u7EBF"
4521
4899
  },
4522
- STORY_SIDE_ROUTES2.map((route) => /* @__PURE__ */ import_react2.default.createElement("option", { key: route.id, value: route.id }, route.title))
4523
- ), /* @__PURE__ */ import_react2.default.createElement("button", { className: "mr-button mr-button-secondary", type: "button", onClick: () => setShowHistory((value) => !value) }, showHistory ? "\u6536\u8D77\u5386\u53F2" : "\u5386\u53F2"), /* @__PURE__ */ import_react2.default.createElement("button", { className: "mr-button mr-button-secondary", type: "button", onClick: restart }, "\u91CD\u65B0\u5F00\u59CB")), /* @__PURE__ */ import_react2.default.createElement("div", { className: "gm-stage", "data-chapter": node?.chapterId ?? story?.chapterId ?? "" }, /* @__PURE__ */ import_react2.default.createElement(StageArtwork, { background: stageBackground, speaker: stageSpeaker }), node && /* @__PURE__ */ import_react2.default.createElement(StageHeader, { node }), node && /* @__PURE__ */ import_react2.default.createElement(Dialogue, { node, onAdvance: advance }), node?.choices && /* @__PURE__ */ import_react2.default.createElement("div", { className: "gm-choices", role: "group", "aria-label": "\u5267\u60C5\u9009\u9879" }, node.choices.map((choice) => /* @__PURE__ */ import_react2.default.createElement("button", { key: choice.id, type: "button", className: "gm-choice", onClick: () => advance(choice.id) }, choice.text))), node?.ending && /* @__PURE__ */ import_react2.default.createElement("div", { className: "gm-ending" }, /* @__PURE__ */ import_react2.default.createElement("h3", null, node.ending.title ?? "\u672C\u6BB5\u5B8C\u7ED3"), node.ending.description && /* @__PURE__ */ import_react2.default.createElement("p", null, node.ending.description), Array.isArray(node.ending.epilogue) && node.ending.epilogue.map((line, index) => /* @__PURE__ */ import_react2.default.createElement("p", { key: index }, line)), Array.isArray(node.ending.relationshipEpilogues) && node.ending.relationshipEpilogues.map((ending) => /* @__PURE__ */ import_react2.default.createElement("section", { className: "gm-ending-epilogue", key: ending.id }, /* @__PURE__ */ import_react2.default.createElement("h4", null, ending.title), /* @__PURE__ */ import_react2.default.createElement("p", null, ending.description))), /* @__PURE__ */ import_react2.default.createElement("button", { className: "mr-button", type: "button", onClick: restart }, "\u5F00\u542F\u65B0\u7684\u4E00\u5468\u76EE")), !node && /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-empty" }, "\u5267\u60C5\u5F15\u64CE\u65E0\u6CD5\u8BFB\u53D6\u5F53\u524D\u5B58\u6863\uFF0C\u8BF7\u91CD\u65B0\u5F00\u59CB\u3002")), showHistory && /* @__PURE__ */ import_react2.default.createElement("div", { className: "gm-history", role: "log", "aria-label": "\u5267\u60C5\u5386\u53F2" }, history.map((entry, index) => /* @__PURE__ */ import_react2.default.createElement("p", { key: `${entry.id}-${index}`, className: entry.speaker === "player" ? "gm-history-player" : "" }, speakerLabel(entry.speaker) && /* @__PURE__ */ import_react2.default.createElement("strong", null, speakerLabel(entry.speaker), "\uFF1A"), entry.text))), /* @__PURE__ */ import_react2.default.createElement("div", { className: "gm-slots" }, /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-control-label" }, "\u5B58\u6863"), [0, 1, 2].map((index) => {
4900
+ STORY_SIDE_ROUTES2.map((route) => /* @__PURE__ */ import_react3.default.createElement("option", { key: route.id, value: route.id }, route.title))
4901
+ ), /* @__PURE__ */ import_react3.default.createElement("button", { className: "mr-button mr-button-secondary", type: "button", onClick: () => setShowHistory((value) => !value) }, showHistory ? "\u6536\u8D77\u5386\u53F2" : "\u5386\u53F2"), /* @__PURE__ */ import_react3.default.createElement("button", { className: "mr-button mr-button-secondary", type: "button", onClick: restart }, "\u91CD\u65B0\u5F00\u59CB")), /* @__PURE__ */ import_react3.default.createElement("div", { className: "gm-stage", "data-chapter": node?.chapterId ?? story?.chapterId ?? "" }, /* @__PURE__ */ import_react3.default.createElement(StageArtwork, { background: stageBackground, speaker: stageSpeaker }), node && /* @__PURE__ */ import_react3.default.createElement(StageHeader, { node }), node && /* @__PURE__ */ import_react3.default.createElement(Dialogue, { node, onAdvance: advance }), node?.choices && /* @__PURE__ */ import_react3.default.createElement("div", { className: "gm-choices", role: "group", "aria-label": "\u5267\u60C5\u9009\u9879" }, node.choices.map((choice) => /* @__PURE__ */ import_react3.default.createElement("button", { key: choice.id, type: "button", className: "gm-choice", onClick: () => advance(choice.id) }, choice.text))), node?.ending && /* @__PURE__ */ import_react3.default.createElement("div", { className: "gm-ending" }, /* @__PURE__ */ import_react3.default.createElement("h3", null, node.ending.title ?? "\u672C\u6BB5\u5B8C\u7ED3"), node.ending.description && /* @__PURE__ */ import_react3.default.createElement("p", null, node.ending.description), Array.isArray(node.ending.epilogue) && node.ending.epilogue.map((line, index) => /* @__PURE__ */ import_react3.default.createElement("p", { key: index }, line)), Array.isArray(node.ending.relationshipEpilogues) && node.ending.relationshipEpilogues.map((ending) => /* @__PURE__ */ import_react3.default.createElement("section", { className: "gm-ending-epilogue", key: ending.id }, /* @__PURE__ */ import_react3.default.createElement("h4", null, ending.title), /* @__PURE__ */ import_react3.default.createElement("p", null, ending.description))), /* @__PURE__ */ import_react3.default.createElement("button", { className: "mr-button", type: "button", onClick: restart }, "\u5F00\u542F\u65B0\u7684\u4E00\u5468\u76EE")), !node && /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-empty" }, "\u5267\u60C5\u5F15\u64CE\u65E0\u6CD5\u8BFB\u53D6\u5F53\u524D\u5B58\u6863\uFF0C\u8BF7\u91CD\u65B0\u5F00\u59CB\u3002")), showHistory && /* @__PURE__ */ import_react3.default.createElement("div", { className: "gm-history", role: "log", "aria-label": "\u5267\u60C5\u5386\u53F2" }, history.map((entry, index) => /* @__PURE__ */ import_react3.default.createElement("p", { key: `${entry.id}-${index}`, className: entry.speaker === "player" ? "gm-history-player" : "" }, speakerLabel(entry.speaker) && /* @__PURE__ */ import_react3.default.createElement("strong", null, speakerLabel(entry.speaker), "\uFF1A"), entry.text))), /* @__PURE__ */ import_react3.default.createElement("div", { className: "gm-slots" }, /* @__PURE__ */ import_react3.default.createElement("span", { className: "mr-control-label" }, "\u5B58\u6863"), [0, 1, 2].map((index) => {
4524
4902
  const slot = slots[index];
4525
- return /* @__PURE__ */ import_react2.default.createElement("span", { key: index, className: "gm-slot" }, /* @__PURE__ */ import_react2.default.createElement("button", { className: "mr-button mr-button-secondary", type: "button", onClick: () => saveSlot(index) }, "\u5B58 ", index + 1), /* @__PURE__ */ import_react2.default.createElement("button", { className: "mr-button mr-button-secondary", type: "button", disabled: !slot || slot.invalid, onClick: () => loadSlot(index), title: slot?.savedAt ?? "" }, "\u8BFB ", index + 1));
4526
- }), /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-caption" }, "\u5B58\u6863\u4FDD\u5B58\u5728\u6D4F\u89C8\u5668\u672C\u5730\u3002")), error && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-error", role: "alert" }, error));
4903
+ return /* @__PURE__ */ import_react3.default.createElement("span", { key: index, className: "gm-slot" }, /* @__PURE__ */ import_react3.default.createElement("button", { className: "mr-button mr-button-secondary", type: "button", onClick: () => saveSlot(index) }, "\u5B58 ", index + 1), /* @__PURE__ */ import_react3.default.createElement("button", { className: "mr-button mr-button-secondary", type: "button", disabled: !slot || slot.invalid, onClick: () => loadSlot(index), title: slot?.savedAt ?? "" }, "\u8BFB ", index + 1));
4904
+ }), /* @__PURE__ */ import_react3.default.createElement("span", { className: "mr-caption" }, "\u5B58\u6863\u4FDD\u5B58\u5728\u6D4F\u89C8\u5668\u672C\u5730\u3002")), error && /* @__PURE__ */ import_react3.default.createElement("p", { className: "mr-error", role: "alert" }, error));
4527
4905
  }
4528
4906
  var FREE_PRESETS = [
4529
4907
  { id: "cafe", label: "\u5F00\u653E\u65E5\u4F11\u606F\u533A", world: "\u767E\u6A21\u534F\u4F1A\u5F00\u653E\u65E5\u4E34\u8FD1\u6563\u573A\uFF0C\u5C55\u53F0\u8FB9\u7684\u706F\u4ECD\u4EAE\u7740\u3002", opener: "\u4F60\u4ECE\u55A7\u95F9\u7684\u4EBA\u7FA4\u8D70\u5230\u4E00\u5F20\u5B89\u9759\u7684\u5706\u684C\u65C1\u3002", art: association_open_day_default },
@@ -4531,20 +4909,20 @@ var FREE_PRESETS = [
4531
4909
  { id: "station", label: "\u5343\u6865\u7AD9\u53F0", world: "\u5343\u6865\u7AD9\u53F0\uFF0C\u672B\u73ED\u8F66\u5373\u5C06\u5F00\u51FA\u3002", opener: "\u4F60\u8BF4\uFF1A\u201C\u4ECA\u5929\u4E5F\u8F9B\u82E6\u4E86\u3002\u201D", art: prologue_station_default }
4532
4910
  ];
4533
4911
  function FreeMode({ routes, galReply, cancelGalReply }) {
4534
- const [routeKey2, setRouteKey] = import_react2.default.useState(routes[0] ? `${routes[0].provider}/${routes[0].model}` : "");
4535
- const [character, setCharacter] = import_react2.default.useState("harness");
4536
- const [preset, setPreset] = import_react2.default.useState("cafe");
4537
- const [extra, setExtra] = import_react2.default.useState("");
4538
- const [copied, setCopied] = import_react2.default.useState(false);
4539
- const [copyError, setCopyError] = import_react2.default.useState("");
4540
- const [messages, setMessages] = import_react2.default.useState([]);
4541
- const [draft, setDraft] = import_react2.default.useState("");
4542
- const [sending, setSending] = import_react2.default.useState(false);
4543
- const sendingRef = import_react2.default.useRef(false);
4544
- const [chatError, setChatError] = import_react2.default.useState("");
4545
- const chatGeneration = import_react2.default.useRef(0);
4546
- const pendingRequestId = import_react2.default.useRef(null);
4547
- import_react2.default.useEffect(() => () => {
4912
+ const [routeKey2, setRouteKey] = import_react3.default.useState(routes[0] ? `${routes[0].provider}/${routes[0].model}` : "");
4913
+ const [character, setCharacter] = import_react3.default.useState("harness");
4914
+ const [preset, setPreset] = import_react3.default.useState("cafe");
4915
+ const [extra, setExtra] = import_react3.default.useState("");
4916
+ const [copied, setCopied] = import_react3.default.useState(false);
4917
+ const [copyError, setCopyError] = import_react3.default.useState("");
4918
+ const [messages, setMessages] = import_react3.default.useState([]);
4919
+ const [draft, setDraft] = import_react3.default.useState("");
4920
+ const [sending, setSending] = import_react3.default.useState(false);
4921
+ const sendingRef = import_react3.default.useRef(false);
4922
+ const [chatError, setChatError] = import_react3.default.useState("");
4923
+ const chatGeneration = import_react3.default.useRef(0);
4924
+ const pendingRequestId = import_react3.default.useRef(null);
4925
+ import_react3.default.useEffect(() => () => {
4548
4926
  chatGeneration.current += 1;
4549
4927
  if (pendingRequestId.current && typeof cancelGalReply === "function") void cancelGalReply(pendingRequestId.current);
4550
4928
  }, []);
@@ -4627,19 +5005,19 @@ function FreeMode({ routes, galReply, cancelGalReply }) {
4627
5005
  setSending(false);
4628
5006
  }
4629
5007
  };
4630
- return /* @__PURE__ */ import_react2.default.createElement("div", { className: "gm-free" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "gm-free-controls" }, /* @__PURE__ */ import_react2.default.createElement("label", { className: "mr-control-label", htmlFor: "gm-free-route" }, "\u6A21\u578B\u8DEF\u7EBF\uFF08\u5B98\u65B9\u76EE\u5F55\uFF09"), /* @__PURE__ */ import_react2.default.createElement("select", { id: "gm-free-route", className: "gm-select", value: routeKey2, onChange: (event) => {
5008
+ return /* @__PURE__ */ import_react3.default.createElement("div", { className: "gm-free" }, /* @__PURE__ */ import_react3.default.createElement("div", { className: "gm-free-controls" }, /* @__PURE__ */ import_react3.default.createElement("label", { className: "mr-control-label", htmlFor: "gm-free-route" }, "\u6A21\u578B\u8DEF\u7EBF\uFF08\u5B98\u65B9\u76EE\u5F55\uFF09"), /* @__PURE__ */ import_react3.default.createElement("select", { id: "gm-free-route", className: "gm-select", value: routeKey2, onChange: (event) => {
4631
5009
  setRouteKey(event.target.value);
4632
5010
  resetChat();
4633
- }, disabled: routes.length === 0 }, routes.length === 0 && /* @__PURE__ */ import_react2.default.createElement("option", { value: "" }, "\u76EE\u5F55\u6682\u4E0D\u53EF\u7528\uFF0C\u8BF7\u5728\u5B98\u65B9\u4F1A\u8BDD\u9009\u6A21\u578B"), routes.map((item) => /* @__PURE__ */ import_react2.default.createElement("option", { key: `${item.provider}/${item.model}`, value: `${item.provider}/${item.model}` }, item.provider, "/", item.model, " \xB7 ", item.name))), /* @__PURE__ */ import_react2.default.createElement("label", { className: "mr-control-label", htmlFor: "gm-free-character" }, "\u89D2\u8272"), /* @__PURE__ */ import_react2.default.createElement("select", { id: "gm-free-character", className: "gm-select", value: character, onChange: (event) => {
5011
+ }, disabled: routes.length === 0 }, routes.length === 0 && /* @__PURE__ */ import_react3.default.createElement("option", { value: "" }, "\u76EE\u5F55\u6682\u4E0D\u53EF\u7528\uFF0C\u8BF7\u5728\u5B98\u65B9\u4F1A\u8BDD\u9009\u6A21\u578B"), routes.map((item) => /* @__PURE__ */ import_react3.default.createElement("option", { key: `${item.provider}/${item.model}`, value: `${item.provider}/${item.model}` }, item.provider, "/", item.model, " \xB7 ", item.name))), /* @__PURE__ */ import_react3.default.createElement("label", { className: "mr-control-label", htmlFor: "gm-free-character" }, "\u89D2\u8272"), /* @__PURE__ */ import_react3.default.createElement("select", { id: "gm-free-character", className: "gm-select", value: character, onChange: (event) => {
4634
5012
  setCharacter(event.target.value);
4635
5013
  resetChat();
4636
- } }, Object.entries(STORY_CHARACTERS3).map(([key, label]) => /* @__PURE__ */ import_react2.default.createElement("option", { key, value: key }, label))), /* @__PURE__ */ import_react2.default.createElement("label", { className: "mr-control-label", htmlFor: "gm-free-preset" }, "\u573A\u666F"), /* @__PURE__ */ import_react2.default.createElement("select", { id: "gm-free-preset", className: "gm-select", value: preset, onChange: (event) => {
5014
+ } }, Object.entries(STORY_CHARACTERS3).map(([key, label]) => /* @__PURE__ */ import_react3.default.createElement("option", { key, value: key }, label))), /* @__PURE__ */ import_react3.default.createElement("label", { className: "mr-control-label", htmlFor: "gm-free-preset" }, "\u573A\u666F"), /* @__PURE__ */ import_react3.default.createElement("select", { id: "gm-free-preset", className: "gm-select", value: preset, onChange: (event) => {
4637
5015
  setPreset(event.target.value);
4638
5016
  resetChat();
4639
- } }, FREE_PRESETS.map((item) => /* @__PURE__ */ import_react2.default.createElement("option", { key: item.id, value: item.id }, item.label)))), /* @__PURE__ */ import_react2.default.createElement("textarea", { className: "mr-textarea", rows: 4, value: extra, onChange: (event) => {
5017
+ } }, FREE_PRESETS.map((item) => /* @__PURE__ */ import_react3.default.createElement("option", { key: item.id, value: item.id }, item.label)))), /* @__PURE__ */ import_react3.default.createElement("textarea", { className: "mr-textarea", rows: 4, value: extra, onChange: (event) => {
4640
5018
  setExtra(event.target.value);
4641
5019
  resetChat();
4642
- }, placeholder: "\u8865\u5145\u8BBE\u5B9A\uFF08\u53EF\u9009\uFF09\uFF1A\u5173\u7CFB\u3001\u8BED\u6C14\u3001\u7981\u533A\u2026\u2026", "aria-label": "\u8865\u5145\u8BBE\u5B9A" }), /* @__PURE__ */ import_react2.default.createElement("div", { className: "gm-free-stage", "aria-label": "\u5F00\u573A\u9884\u89C8" }, /* @__PURE__ */ import_react2.default.createElement(StageArtwork, { background: presetData?.art ?? model_city_title_default, speaker: character }), /* @__PURE__ */ import_react2.default.createElement("div", { className: "gm-free-scene" }, /* @__PURE__ */ import_react2.default.createElement("span", null, "\u81EA\u7531\u6A21\u5F0F \xB7 \u5F00\u573A\u9884\u89C8"), /* @__PURE__ */ import_react2.default.createElement("strong", null, presetData?.label)), /* @__PURE__ */ import_react2.default.createElement("div", { className: "gm-free-dialogue" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "gm-speaker" }, /* @__PURE__ */ import_react2.default.createElement(Avatar, { speaker: character, large: true }), /* @__PURE__ */ import_react2.default.createElement("span", { className: "gm-speaker-name" }, STORY_CHARACTERS3[character] ?? character)), /* @__PURE__ */ import_react2.default.createElement("p", null, presetData?.opener))), /* @__PURE__ */ import_react2.default.createElement("section", { className: "gm-free-chat", "aria-label": "\u81EA\u7531\u6A21\u5F0F\u5BF9\u8BDD" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "gm-free-chat-head" }, /* @__PURE__ */ import_react2.default.createElement("strong", null, "\u4E0E ", STORY_CHARACTERS3[character] ?? character, " \u5BF9\u8BDD"), /* @__PURE__ */ import_react2.default.createElement("span", null, route ? `${route.provider}/${route.model}` : "\u5C1A\u65E0\u53EF\u7528\u6A21\u578B")), /* @__PURE__ */ import_react2.default.createElement("div", { className: "gm-free-chat-log", role: "log", "aria-live": "polite" }, messages.length === 0 && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-caption" }, "\u8F93\u5165\u7B2C\u4E00\u53E5\u8BDD\u540E\uFF0C\u63D2\u4EF6\u4F1A\u901A\u8FC7\u5B98\u65B9\u6A21\u578B\u670D\u52A1\u5F00\u59CB\u5BF9\u8BDD\u3002"), messages.map((item, index) => /* @__PURE__ */ import_react2.default.createElement("div", { className: `gm-free-chat-message ${item.role}`, key: index }, /* @__PURE__ */ import_react2.default.createElement("strong", null, item.role === "user" ? "\u4F60" : STORY_CHARACTERS3[character] ?? character, item.truncated ? " \xB7 \u56DE\u590D\u5DF2\u622A\u65AD" : ""), /* @__PURE__ */ import_react2.default.createElement("p", null, item.text))), sending && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-caption", role: "status" }, "\u6A21\u578B\u6B63\u5728\u56DE\u590D\u2026")), /* @__PURE__ */ import_react2.default.createElement("label", { className: "mr-control-label", htmlFor: "gm-free-draft" }, "\u4F60\u7684\u53F0\u8BCD"), /* @__PURE__ */ import_react2.default.createElement(
5020
+ }, placeholder: "\u8865\u5145\u8BBE\u5B9A\uFF08\u53EF\u9009\uFF09\uFF1A\u5173\u7CFB\u3001\u8BED\u6C14\u3001\u7981\u533A\u2026\u2026", "aria-label": "\u8865\u5145\u8BBE\u5B9A" }), /* @__PURE__ */ import_react3.default.createElement("div", { className: "gm-free-stage", "aria-label": "\u5F00\u573A\u9884\u89C8" }, /* @__PURE__ */ import_react3.default.createElement(StageArtwork, { background: presetData?.art ?? model_city_title_default, speaker: character }), /* @__PURE__ */ import_react3.default.createElement("div", { className: "gm-free-scene" }, /* @__PURE__ */ import_react3.default.createElement("span", null, "\u81EA\u7531\u6A21\u5F0F \xB7 \u5F00\u573A\u9884\u89C8"), /* @__PURE__ */ import_react3.default.createElement("strong", null, presetData?.label)), /* @__PURE__ */ import_react3.default.createElement("div", { className: "gm-free-dialogue" }, /* @__PURE__ */ import_react3.default.createElement("div", { className: "gm-speaker" }, /* @__PURE__ */ import_react3.default.createElement(Avatar, { speaker: character, large: true }), /* @__PURE__ */ import_react3.default.createElement("span", { className: "gm-speaker-name" }, STORY_CHARACTERS3[character] ?? character)), /* @__PURE__ */ import_react3.default.createElement("p", null, presetData?.opener))), /* @__PURE__ */ import_react3.default.createElement("section", { className: "gm-free-chat", "aria-label": "\u81EA\u7531\u6A21\u5F0F\u5BF9\u8BDD" }, /* @__PURE__ */ import_react3.default.createElement("div", { className: "gm-free-chat-head" }, /* @__PURE__ */ import_react3.default.createElement("strong", null, "\u4E0E ", STORY_CHARACTERS3[character] ?? character, " \u5BF9\u8BDD"), /* @__PURE__ */ import_react3.default.createElement("span", null, route ? `${route.provider}/${route.model}` : "\u5C1A\u65E0\u53EF\u7528\u6A21\u578B")), /* @__PURE__ */ import_react3.default.createElement("div", { className: "gm-free-chat-log", role: "log", "aria-live": "polite" }, messages.length === 0 && /* @__PURE__ */ import_react3.default.createElement("p", { className: "mr-caption" }, "\u8F93\u5165\u7B2C\u4E00\u53E5\u8BDD\u540E\uFF0C\u63D2\u4EF6\u4F1A\u901A\u8FC7\u5B98\u65B9\u6A21\u578B\u670D\u52A1\u5F00\u59CB\u5BF9\u8BDD\u3002"), messages.map((item, index) => /* @__PURE__ */ import_react3.default.createElement("div", { className: `gm-free-chat-message ${item.role}`, key: index }, /* @__PURE__ */ import_react3.default.createElement("strong", null, item.role === "user" ? "\u4F60" : STORY_CHARACTERS3[character] ?? character, item.truncated ? " \xB7 \u56DE\u590D\u5DF2\u622A\u65AD" : ""), /* @__PURE__ */ import_react3.default.createElement("p", null, item.text))), sending && /* @__PURE__ */ import_react3.default.createElement("p", { className: "mr-caption", role: "status" }, "\u6A21\u578B\u6B63\u5728\u56DE\u590D\u2026")), /* @__PURE__ */ import_react3.default.createElement("label", { className: "mr-control-label", htmlFor: "gm-free-draft" }, "\u4F60\u7684\u53F0\u8BCD"), /* @__PURE__ */ import_react3.default.createElement(
4643
5021
  "textarea",
4644
5022
  {
4645
5023
  id: "gm-free-draft",
@@ -4656,19 +5034,19 @@ function FreeMode({ routes, galReply, cancelGalReply }) {
4656
5034
  },
4657
5035
  placeholder: "\u5728\u573A\u666F\u4E2D\u8BF4\u4E9B\u4EC0\u4E48\u2026\uFF08Ctrl+Enter \u53D1\u9001\uFF09"
4658
5036
  }
4659
- ), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-actions" }, /* @__PURE__ */ import_react2.default.createElement("button", { className: "mr-button", type: "button", disabled: !route || sending || !draft.trim(), onClick: () => {
5037
+ ), /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-actions" }, /* @__PURE__ */ import_react3.default.createElement("button", { className: "mr-button", type: "button", disabled: !route || sending || !draft.trim(), onClick: () => {
4660
5038
  void send();
4661
- } }, "\u53D1\u9001\u7ED9\u6A21\u578B"), sending && /* @__PURE__ */ import_react2.default.createElement("button", { className: "mr-button mr-button-secondary", type: "button", disabled: typeof cancelGalReply !== "function", onClick: () => {
5039
+ } }, "\u53D1\u9001\u7ED9\u6A21\u578B"), sending && /* @__PURE__ */ import_react3.default.createElement("button", { className: "mr-button mr-button-secondary", type: "button", disabled: typeof cancelGalReply !== "function", onClick: () => {
4662
5040
  void stop();
4663
- } }, "\u505C\u6B62\u751F\u6210"), /* @__PURE__ */ import_react2.default.createElement("button", { className: "mr-button mr-button-secondary", type: "button", disabled: sending || messages.length === 0, onClick: resetChat }, "\u5F00\u59CB\u65B0\u5BF9\u8BDD")), chatError && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-error", role: "alert" }, chatError)), /* @__PURE__ */ import_react2.default.createElement("details", { className: "gm-free-details" }, /* @__PURE__ */ import_react2.default.createElement("summary", null, "\u67E5\u770B\u53EF\u590D\u5236\u5230\u5B98\u65B9\u4F1A\u8BDD\u7684\u5F00\u573A\u63D0\u793A\u8BCD"), /* @__PURE__ */ import_react2.default.createElement("pre", { className: "gm-free-prompt" }, prompt)), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-actions" }, /* @__PURE__ */ import_react2.default.createElement("button", { className: "mr-button", type: "button", onClick: copy2 }, copied ? "\u5DF2\u590D\u5236\uFF0C\u53BB\u5B98\u65B9\u4F1A\u8BDD\u7C98\u8D34\u53D1\u9001" : "\u590D\u5236\u5F00\u573A\u63D0\u793A\u8BCD"), /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-caption" }, "\u4E5F\u53EF\u590D\u5236\u5230\u5B98\u65B9\u4F1A\u8BDD\u4F7F\u7528\uFF1B\u9762\u677F\u5185\u53D1\u9001\u4F1A\u8C03\u7528\u5DF2\u914D\u7F6E\u6A21\u578B\uFF0C\u53EF\u80FD\u4EA7\u751F\u8D26\u53F7\u8D39\u7528\u3002\u63D2\u4EF6\u4E0D\u4FDD\u5B58 API Key\u3002")), copyError && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-error", role: "alert" }, copyError));
5041
+ } }, "\u505C\u6B62\u751F\u6210"), /* @__PURE__ */ import_react3.default.createElement("button", { className: "mr-button mr-button-secondary", type: "button", disabled: sending || messages.length === 0, onClick: resetChat }, "\u5F00\u59CB\u65B0\u5BF9\u8BDD")), chatError && /* @__PURE__ */ import_react3.default.createElement("p", { className: "mr-error", role: "alert" }, chatError)), /* @__PURE__ */ import_react3.default.createElement("details", { className: "gm-free-details" }, /* @__PURE__ */ import_react3.default.createElement("summary", null, "\u67E5\u770B\u53EF\u590D\u5236\u5230\u5B98\u65B9\u4F1A\u8BDD\u7684\u5F00\u573A\u63D0\u793A\u8BCD"), /* @__PURE__ */ import_react3.default.createElement("pre", { className: "gm-free-prompt" }, prompt)), /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-actions" }, /* @__PURE__ */ import_react3.default.createElement("button", { className: "mr-button", type: "button", onClick: copy2 }, copied ? "\u5DF2\u590D\u5236\uFF0C\u53BB\u5B98\u65B9\u4F1A\u8BDD\u7C98\u8D34\u53D1\u9001" : "\u590D\u5236\u5F00\u573A\u63D0\u793A\u8BCD"), /* @__PURE__ */ import_react3.default.createElement("span", { className: "mr-caption" }, "\u4E5F\u53EF\u590D\u5236\u5230\u5B98\u65B9\u4F1A\u8BDD\u4F7F\u7528\uFF1B\u9762\u677F\u5185\u53D1\u9001\u4F1A\u8C03\u7528\u5DF2\u914D\u7F6E\u6A21\u578B\uFF0C\u53EF\u80FD\u4EA7\u751F\u8D26\u53F7\u8D39\u7528\u3002\u63D2\u4EF6\u4E0D\u4FDD\u5B58 API Key\u3002")), copyError && /* @__PURE__ */ import_react3.default.createElement("p", { className: "mr-error", role: "alert" }, copyError));
4664
5042
  }
4665
5043
  function GalModulePage({ loadCatalog, galReply, cancelGalReply }) {
4666
- const [tab, setTab] = import_react2.default.useState("story");
4667
- return /* @__PURE__ */ import_react2.default.createElement("main", { className: "mr-workspace gm-root" }, /* @__PURE__ */ import_react2.default.createElement("style", null, router_main_default), /* @__PURE__ */ import_react2.default.createElement("style", null, gal_module_default), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-shell" }, /* @__PURE__ */ import_react2.default.createElement("header", { className: "mr-header" }, /* @__PURE__ */ import_react2.default.createElement("div", null, /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-eyebrow" }, "Gal Module \xB7 DeepSeek Harness"), /* @__PURE__ */ import_react2.default.createElement("h1", { className: "mr-title" }, "Gal \u6A21\u5757"), /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-subtitle" }, "\u5267\u60C5\u6A21\u5F0F\u63D0\u4F9B Gal \u89C6\u56FE\u821E\u53F0\u4E0E\u5B58\u6863\uFF1B\u81EA\u7531\u6A21\u5F0F\u53EF\u5728\u9762\u677F\u5185\u4F7F\u7528\u5B98\u65B9\u5DF2\u914D\u7F6E\u6A21\u578B\u5C55\u5F00\u5BF9\u8BDD\u3002")), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-status" }, /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-status-dot" }), "\u5267\u60C5\u79BB\u7EBF \xB7 \u81EA\u7531\u6A21\u5F0F\u6309\u9700\u8C03\u7528\u6A21\u578B")), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-controls" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-control-group" }, /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-control-label" }, "\u6A21\u5F0F"), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-segment", role: "group", "aria-label": "Gal \u6A21\u5757\u6A21\u5F0F" }, /* @__PURE__ */ import_react2.default.createElement("button", { type: "button", "aria-pressed": tab === "story", onClick: () => setTab("story") }, "\u5267\u60C5\u6A21\u5F0F \xB7 Gal \u89C6\u56FE"), /* @__PURE__ */ import_react2.default.createElement("button", { type: "button", "aria-pressed": tab === "free", onClick: () => setTab("free") }, "\u81EA\u7531\u6A21\u5F0F")))), tab === "story" ? /* @__PURE__ */ import_react2.default.createElement(StoryMode, null) : /* @__PURE__ */ import_react2.default.createElement(FreeModeWithRoutes, { loadCatalog, galReply, cancelGalReply })));
5044
+ const [tab, setTab] = import_react3.default.useState("story");
5045
+ return /* @__PURE__ */ import_react3.default.createElement("main", { className: "mr-workspace gm-root" }, /* @__PURE__ */ import_react3.default.createElement("style", null, router_main_default), /* @__PURE__ */ import_react3.default.createElement("style", null, gal_module_default), /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-shell" }, /* @__PURE__ */ import_react3.default.createElement("header", { className: "mr-header" }, /* @__PURE__ */ import_react3.default.createElement("div", null, /* @__PURE__ */ import_react3.default.createElement("p", { className: "mr-eyebrow" }, "Gal Module \xB7 DeepSeek Harness"), /* @__PURE__ */ import_react3.default.createElement("h1", { className: "mr-title" }, "Gal \u6A21\u5757"), /* @__PURE__ */ import_react3.default.createElement("p", { className: "mr-subtitle" }, "\u5267\u60C5\u6A21\u5F0F\u63D0\u4F9B Gal \u89C6\u56FE\u821E\u53F0\u4E0E\u5B58\u6863\uFF1B\u81EA\u7531\u6A21\u5F0F\u53EF\u5728\u9762\u677F\u5185\u4F7F\u7528\u5B98\u65B9\u5DF2\u914D\u7F6E\u6A21\u578B\u5C55\u5F00\u5BF9\u8BDD\u3002")), /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-status" }, /* @__PURE__ */ import_react3.default.createElement("span", { className: "mr-status-dot" }), "\u5267\u60C5\u79BB\u7EBF \xB7 \u81EA\u7531\u6A21\u5F0F\u6309\u9700\u8C03\u7528\u6A21\u578B")), /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-controls" }, /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-control-group" }, /* @__PURE__ */ import_react3.default.createElement("span", { className: "mr-control-label" }, "\u6A21\u5F0F"), /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-segment", role: "group", "aria-label": "Gal \u6A21\u5757\u6A21\u5F0F" }, /* @__PURE__ */ import_react3.default.createElement("button", { type: "button", "aria-pressed": tab === "story", onClick: () => setTab("story") }, "\u5267\u60C5\u6A21\u5F0F \xB7 Gal \u89C6\u56FE"), /* @__PURE__ */ import_react3.default.createElement("button", { type: "button", "aria-pressed": tab === "free", onClick: () => setTab("free") }, "\u81EA\u7531\u6A21\u5F0F")))), tab === "story" ? /* @__PURE__ */ import_react3.default.createElement(StoryMode, null) : /* @__PURE__ */ import_react3.default.createElement(FreeModeWithRoutes, { loadCatalog, galReply, cancelGalReply })));
4668
5046
  }
4669
5047
  function FreeModeWithRoutes({ loadCatalog, galReply, cancelGalReply }) {
4670
- const [state, setState] = import_react2.default.useState({ status: "loading", routes: [] });
4671
- import_react2.default.useEffect(() => {
5048
+ const [state, setState] = import_react3.default.useState({ status: "loading", routes: [] });
5049
+ import_react3.default.useEffect(() => {
4672
5050
  let mounted = true;
4673
5051
  Promise.resolve().then(loadCatalog).then((response) => {
4674
5052
  if (!mounted) return;
@@ -4686,9 +5064,9 @@ function FreeModeWithRoutes({ loadCatalog, galReply, cancelGalReply }) {
4686
5064
  mounted = false;
4687
5065
  };
4688
5066
  }, [loadCatalog]);
4689
- if (state.status === "loading") return /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-empty" }, "\u6B63\u5728\u8BFB\u53D6\u5B98\u65B9\u6A21\u578B\u76EE\u5F55\u2026");
4690
- if (state.status === "error") return /* @__PURE__ */ import_react2.default.createElement(import_react2.default.Fragment, null, /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-empty" }, "\u6A21\u578B\u76EE\u5F55\u8BFB\u53D6\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\uFF1B\u4ECD\u53EF\u590D\u5236\u63D0\u793A\u8BCD\u540E\u5728\u4EFB\u610F\u5B98\u65B9\u4F1A\u8BDD\u4F7F\u7528\u3002"), /* @__PURE__ */ import_react2.default.createElement(FreeMode, { routes: [], galReply, cancelGalReply }));
4691
- return /* @__PURE__ */ import_react2.default.createElement(FreeMode, { routes: state.routes, galReply, cancelGalReply });
5067
+ if (state.status === "loading") return /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-empty" }, "\u6B63\u5728\u8BFB\u53D6\u5B98\u65B9\u6A21\u578B\u76EE\u5F55\u2026");
5068
+ if (state.status === "error") return /* @__PURE__ */ import_react3.default.createElement(import_react3.default.Fragment, null, /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-empty" }, "\u6A21\u578B\u76EE\u5F55\u8BFB\u53D6\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\uFF1B\u4ECD\u53EF\u590D\u5236\u63D0\u793A\u8BCD\u540E\u5728\u4EFB\u610F\u5B98\u65B9\u4F1A\u8BDD\u4F7F\u7528\u3002"), /* @__PURE__ */ import_react3.default.createElement(FreeMode, { routes: [], galReply, cancelGalReply }));
5069
+ return /* @__PURE__ */ import_react3.default.createElement(FreeMode, { routes: state.routes, galReply, cancelGalReply });
4692
5070
  }
4693
5071
 
4694
5072
  // .dsh-plugin/shared/official-tools-remote.mjs
@@ -4734,7 +5112,7 @@ var galRequestCodec = strictCodec(`${OFFICIAL_TOOLS_REMOTE_PACKAGE}#GalReplyRequ
4734
5112
  if (typeof request.requestId !== "string" || !/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(request.requestId)) {
4735
5113
  throw new TypeError("Gal reply needs a random requestId");
4736
5114
  }
4737
- const short = (field) => typeof request[field] === "string" && request[field].trim().length > 0 && request[field].length <= 160;
5115
+ const short = (field2) => typeof request[field2] === "string" && request[field2].trim().length > 0 && request[field2].length <= 160;
4738
5116
  if (!short("provider") || !short("model")) throw new TypeError("Gal reply needs a configured provider/model");
4739
5117
  if (typeof request.persona !== "string" || request.persona.length > 4e3) throw new TypeError("Gal persona is too long");
4740
5118
  if (!Array.isArray(request.messages) || request.messages.length < 1 || request.messages.length > 24) throw new TypeError("Gal reply needs 1-24 messages");
@@ -4798,7 +5176,7 @@ var galRequestIdParameter = Object.freeze({
4798
5176
  });
4799
5177
  var OFFICIAL_TOOLS_REMOTE_DESCRIPTORS = Object.freeze([
4800
5178
  descriptor("list", [], listResultCodec),
4801
- descriptor("install", [toolIdParameter], installResultCodec),
5179
+ descriptor("installTool", [toolIdParameter], installResultCodec),
4802
5180
  descriptor("cancel", [toolIdParameter], installResultCodec),
4803
5181
  descriptor("status", [toolIdParameter], statusResultCodec),
4804
5182
  descriptor("galReply", [galRequestParameter], galResultCodec),
@@ -4822,7 +5200,15 @@ var ROUTER_NAMESPACE = "model-router-galgame";
4822
5200
  var ROUTER_PACKAGE = "@ljwei-stak/model-router-galgame";
4823
5201
  var ROUTER_PANEL = "model-router-galgame";
4824
5202
  var GAL_PANEL = "model-router-gal";
4825
- var inject = ["slots", "configForms", "remote", "remote.session", "layout"];
5203
+ var inject = ["remote"];
5204
+ var UI_INJECT = [
5205
+ "slots",
5206
+ "configForms",
5207
+ "remote",
5208
+ "remote.session",
5209
+ `remote.${OFFICIAL_TOOLS_REMOTE_NAMESPACE}`,
5210
+ "layout"
5211
+ ];
4826
5212
  var FORM_LABELS = Object.freeze({
4827
5213
  unavailable: "\u8BE5\u63D2\u4EF6\u5F53\u524D\u672A\u52A0\u8F7D\uFF0C\u6682\u65F6\u65E0\u6CD5\u914D\u7F6E\u3002",
4828
5214
  readOnly: "\u5F53\u524D\u914D\u7F6E\u4E3A\u53EA\u8BFB\u3002",
@@ -4835,8 +5221,8 @@ var FIELD_COPY = Object.freeze({
4835
5221
  reset: "\u6062\u590D\u9ED8\u8BA4",
4836
5222
  invalidNumber: "\u8BF7\u8F93\u5165\u5141\u8BB8\u8303\u56F4\u5185\u7684\u6570\u5B57\uFF0C\u6216\u7559\u7A7A\u6062\u590D\u9ED8\u8BA4\u3002"
4837
5223
  });
4838
- function boundedNumberField(field, { minimum = 0, maximum = Number.MAX_SAFE_INTEGER, integer = false } = {}) {
4839
- const numeric = (0, import_dsh_client_ui_primitives.settingsNumberField)(field);
5224
+ function boundedNumberField(field2, { minimum = 0, maximum = Number.MAX_SAFE_INTEGER, integer = false } = {}) {
5225
+ const numeric = (0, import_dsh_client_ui_primitives.settingsNumberField)(field2);
4840
5226
  return {
4841
5227
  ...numeric,
4842
5228
  parse: (text2) => {
@@ -4849,16 +5235,32 @@ function boundedNumberField(field, { minimum = 0, maximum = Number.MAX_SAFE_INTE
4849
5235
  }
4850
5236
  };
4851
5237
  }
5238
+ function modelProfilesField() {
5239
+ const field2 = (0, import_dsh_client_ui_primitives.settingsTextField)("modelProfilesJson");
5240
+ return {
5241
+ ...field2,
5242
+ parse: (text2) => {
5243
+ try {
5244
+ parseModelProfilesJson(text2);
5245
+ return field2.parse(text2);
5246
+ } catch {
5247
+ return void 0;
5248
+ }
5249
+ }
5250
+ };
5251
+ }
4852
5252
  var RouterSettingsCardController = class {
4853
5253
  constructor(scope) {
4854
5254
  this.form = new import_dsh_client_ui_primitives.SettingsFormModel(scope, [
4855
5255
  boundedNumberField("budgetUsd", { minimum: 0 }),
4856
- boundedNumberField("maxConsultOutputChars", { minimum: 500, maximum: 5e4, integer: true })
5256
+ boundedNumberField("maxConsultOutputChars", { minimum: 500, maximum: 5e4, integer: true }),
5257
+ modelProfilesField()
4857
5258
  ]);
4858
5259
  this.store = this.form.bind(() => ({
4859
5260
  ...this.form.shell(),
4860
5261
  budgetUsd: this.form.field("budgetUsd"),
4861
- maxConsultOutputChars: this.form.field("maxConsultOutputChars")
5262
+ maxConsultOutputChars: this.form.field("maxConsultOutputChars"),
5263
+ modelProfilesJson: this.form.field("modelProfilesJson")
4862
5264
  }));
4863
5265
  }
4864
5266
  /** Supply the snapshot hook and staged form actions to the Plugins slot. */
@@ -4876,12 +5278,12 @@ function RouterSettingsCard(props) {
4876
5278
  return "\u4E3A\u5B98\u65B9\u5DF2\u914D\u7F6E\u7684\u6A21\u578B\u751F\u6210\u8DEF\u7531\u8BA1\u5212\uFF0C\u5E76\u63D0\u4F9B\u8DE8\u6A21\u578B\u54A8\u8BE2\u5DE5\u5177\u3002";
4877
5279
  }
4878
5280
  const disabled = !state.writable || state.saving;
4879
- return /* @__PURE__ */ import_react3.default.createElement(import_dsh_client_ui_primitives.SettingsForm, { labels: FORM_LABELS, state, onSave: props.save, onDiscard: props.discard }, /* @__PURE__ */ import_react3.default.createElement(
5281
+ return /* @__PURE__ */ import_react4.default.createElement(import_dsh_client_ui_primitives.SettingsForm, { labels: FORM_LABELS, state, onSave: props.save, onDiscard: props.discard }, /* @__PURE__ */ import_react4.default.createElement(
4880
5282
  import_dsh_client_ui_primitives.SettingsValueField,
4881
5283
  {
4882
5284
  id: "model-router-budget-usd",
4883
5285
  label: "\u5355\u6B21\u8BA1\u5212\u9884\u7B97\u4E0A\u9650\uFF08USD\uFF09",
4884
- hint: "0 \u8868\u793A\u4E0D\u8BBE\u9884\u7B97\u4E0A\u9650\u3002\u4EF7\u683C\u662F\u76EE\u5F55\u4E2D\u7684\u4F30\u7B97\u503C\uFF0C\u5B9E\u9645\u8D26\u5355\u4EE5\u670D\u52A1\u5546\u4E3A\u51C6\uFF1B\u9884\u7B97\u53EA\u5F71\u54CD\u8DEF\u7531\u5EFA\u8BAE\u3002",
5286
+ hint: "0 \u8868\u793A\u4E0D\u8BBE\u9884\u7B97\u4E0A\u9650\u3002\u53EA\u6709\u586B\u5199\u4E0B\u65B9\u5B9E\u9645\u4F7F\u7528\u7684\u5355\u4EF7\u540E\uFF0C\u624D\u80FD\u4F30\u7B97\u8D39\u7528\uFF1B\u9884\u7B97\u53EA\u5F71\u54CD\u8DEF\u7531\u5EFA\u8BAE\u3002",
4885
5287
  help: {
4886
5288
  label: "\u9884\u7B97\u8BF4\u660E",
4887
5289
  content: "\u9884\u7B97\u53EA\u5F71\u54CD\u8DEF\u7531\u5EFA\u8BAE\uFF0C\u4E0D\u4F1A\u9650\u5236\u670D\u52A1\u5546\u6263\u8D39\uFF0C\u4E5F\u4E0D\u4F1A\u8BFB\u53D6\u6216\u4FDD\u5B58 API Key\u3002"
@@ -4898,7 +5300,7 @@ function RouterSettingsCard(props) {
4898
5300
  props.resetField("budgetUsd");
4899
5301
  }
4900
5302
  }
4901
- ), /* @__PURE__ */ import_react3.default.createElement(
5303
+ ), /* @__PURE__ */ import_react4.default.createElement(
4902
5304
  import_dsh_client_ui_primitives.SettingsValueField,
4903
5305
  {
4904
5306
  id: "model-router-consult-output",
@@ -4917,15 +5319,33 @@ function RouterSettingsCard(props) {
4917
5319
  props.resetField("maxConsultOutputChars");
4918
5320
  }
4919
5321
  }
4920
- ), /* @__PURE__ */ import_react3.default.createElement("aside", { style: styles.notice, "aria-label": "\u6A21\u578B\u8DEF\u7531\u4F7F\u7528\u8BF4\u660E" }, /* @__PURE__ */ import_react3.default.createElement("div", { style: styles.titleLine }, /* @__PURE__ */ import_react3.default.createElement(import_dsh_client_ui_primitives.Tag, { tone: "info" }, "\u5B98\u65B9\u6A21\u578B\u914D\u7F6E")), /* @__PURE__ */ import_react3.default.createElement("p", { style: styles.noticeText }, "\u8BF7\u5728 DeepSeek Harness \u7684\u201C\u6A21\u578B\u201D\u9875\u9762\u914D\u7F6E DeepSeek\u3001OpenAI \u517C\u5BB9\u6216 Anthropic \u517C\u5BB9\u670D\u52A1\u3002\u6B64\u63D2\u4EF6\u8BFB\u53D6\u5B98\u65B9\u6A21\u578B\u76EE\u5F55\uFF0C\u4E0D\u4FDD\u5B58 API Key\uFF1B\u76EE\u5F55\u4E2D\u7684\u8DEF\u7EBF\u4ECD\u9700\u901A\u8FC7\u5B9E\u9645\u8C03\u7528\u9A8C\u8BC1\u8D26\u53F7\u548C\u7F51\u7EDC\u53EF\u7528\u6027\u3002"), /* @__PURE__ */ import_react3.default.createElement("p", { style: styles.noticeText }, "\u4F7F\u7528 ", /* @__PURE__ */ import_react3.default.createElement("code", null, "model_router_plan"), " \u83B7\u53D6\u53EF\u5BA1\u9605\u7684\u8DEF\u7531\u5EFA\u8BAE\uFF0C\u4F7F\u7528 ", /* @__PURE__ */ import_react3.default.createElement("code", null, "model_router_consult"), " \u54A8\u8BE2\u4E00\u4E2A\u5DF2\u914D\u7F6E\u6A21\u578B\u3002\u5EFA\u8BAE\u4E0D\u4F1A\u6539\u5199\u4E3B\u4F1A\u8BDD\u6A21\u578B\uFF1B\u591A\u4EBA\u5206\u5DE5\u7531\u5B98\u65B9 Agent Teams \u5DE5\u5177\u6267\u884C\u3002")));
5322
+ ), /* @__PURE__ */ import_react4.default.createElement("div", { style: styles.profileEditor }, /* @__PURE__ */ import_react4.default.createElement("label", { htmlFor: "model-router-profiles-json", style: styles.profileLabel }, "\u6A21\u578B\u4EF7\u683C\u4E0E\u80FD\u529B\u914D\u7F6E\uFF08JSON\uFF09"), /* @__PURE__ */ import_react4.default.createElement("p", { style: styles.noticeText }, "\u6309\u201C\u6A21\u578B\u76EE\u5F55\u201D\u4E2D\u7684\u51C6\u786E provider/model \u586B\u5199\u3002quality \u4E3A\u81EA\u5B9A\u7684 0\u2013100 \u5206\uFF1Binput/output \u662F\u7F8E\u5143\u6BCF\u767E\u4E07 token\u3002\u7F3A\u5C11\u4EF7\u683C\u65F6\u53EA\u7ED9\u8DEF\u7EBF\u5EFA\u8BAE\uFF0C\u4E0D\u663E\u793A\u865A\u6784\u8D39\u7528\u3002"), /* @__PURE__ */ import_react4.default.createElement(
5323
+ "textarea",
5324
+ {
5325
+ id: "model-router-profiles-json",
5326
+ value: state.modelProfilesJson.text,
5327
+ disabled,
5328
+ "aria-invalid": state.modelProfilesJson.invalid,
5329
+ onChange: (event) => props.edit("modelProfilesJson", event.target.value),
5330
+ spellCheck: false,
5331
+ style: styles.profileTextarea
5332
+ }
5333
+ ), state.modelProfilesJson.invalid && /* @__PURE__ */ import_react4.default.createElement("p", { style: styles.profileError, role: "alert" }, "JSON \u683C\u5F0F\u6216\u67D0\u9879\u914D\u7F6E\u65E0\u6548\u3002\u6BCF\u9879\u9700\u63D0\u4F9B\u51C6\u786E\u7684 provider/model\uFF0C\u5355\u4EF7\u4E3A\u975E\u8D1F USD \u6570\u5B57\uFF0C\u8D28\u91CF\u4E3A 0\u2013100\u3002"), /* @__PURE__ */ import_react4.default.createElement("details", { style: styles.profileExample }, /* @__PURE__ */ import_react4.default.createElement("summary", null, "\u67E5\u770B\u914D\u7F6E\u683C\u5F0F"), /* @__PURE__ */ import_react4.default.createElement("pre", null, `[
5334
+ {
5335
+ "provider": "\u6A21\u578B\u76EE\u5F55\u4E2D\u7684\u4F9B\u5E94\u5546 ID",
5336
+ "model": "\u6A21\u578B\u76EE\u5F55\u4E2D\u7684\u6A21\u578B ID",
5337
+ "quality": 80,
5338
+ "pricing": { "input": 0.2, "output": 0.8 },
5339
+ "specialties": ["code"],
5340
+ "cliModel": "\u5382\u5546 CLI \u4F7F\u7528\u7684\u6A21\u578B\u540D\uFF08\u53EF\u9009\uFF09"
5341
+ }
5342
+ ]`)), /* @__PURE__ */ import_react4.default.createElement("button", { type: "button", disabled, onClick: () => props.resetField("modelProfilesJson") }, "\u6062\u590D\u9ED8\u8BA4\u914D\u7F6E")), /* @__PURE__ */ import_react4.default.createElement("aside", { style: styles.notice, "aria-label": "\u6A21\u578B\u8DEF\u7531\u4F7F\u7528\u8BF4\u660E" }, /* @__PURE__ */ import_react4.default.createElement("div", { style: styles.titleLine }, /* @__PURE__ */ import_react4.default.createElement(import_dsh_client_ui_primitives.Tag, { tone: "info" }, "\u5B98\u65B9\u6A21\u578B\u914D\u7F6E")), /* @__PURE__ */ import_react4.default.createElement("p", { style: styles.noticeText }, "\u8BF7\u5728 DeepSeek Harness \u7684\u201C\u6A21\u578B\u201D\u9875\u9762\u914D\u7F6E DeepSeek\u3001OpenAI \u517C\u5BB9\u6216 Anthropic \u517C\u5BB9\u670D\u52A1\u3002\u6B64\u63D2\u4EF6\u8BFB\u53D6\u5B98\u65B9\u6A21\u578B\u76EE\u5F55\uFF0C\u4E0D\u4FDD\u5B58 API Key\uFF1B\u76EE\u5F55\u4E2D\u7684\u8DEF\u7EBF\u4ECD\u9700\u901A\u8FC7\u5B9E\u9645\u8C03\u7528\u9A8C\u8BC1\u8D26\u53F7\u548C\u7F51\u7EDC\u53EF\u7528\u6027\u3002"), /* @__PURE__ */ import_react4.default.createElement("p", { style: styles.noticeText }, "\u4F7F\u7528 ", /* @__PURE__ */ import_react4.default.createElement("code", null, "model_router_plan"), " \u83B7\u53D6\u53EF\u5BA1\u9605\u7684\u8DEF\u7531\u5EFA\u8BAE\uFF0C\u4F7F\u7528 ", /* @__PURE__ */ import_react4.default.createElement("code", null, "model_router_consult"), " \u54A8\u8BE2\u4E00\u4E2A\u5DF2\u914D\u7F6E\u6A21\u578B\u3002\u5EFA\u8BAE\u4E0D\u4F1A\u6539\u5199\u4E3B\u4F1A\u8BDD\u6A21\u578B\uFF1B\u591A\u4EBA\u5206\u5DE5\u7531\u5B98\u65B9 Agent Teams \u5DE5\u5177\u6267\u884C\u3002")));
4921
5343
  }
4922
5344
  function OpenRouterWorkspace({ subject, openPanel }) {
4923
5345
  if (subject?.kind !== "bundle" || subject.pkg?.name !== ROUTER_PACKAGE) return null;
4924
- return /* @__PURE__ */ import_react3.default.createElement(import_dsh_client_ui_primitives.Button, { variant: "outline", size: "sm", type: "button", onClick: openPanel }, "\u6253\u5F00\u5DE5\u4F5C\u53F0");
5346
+ return /* @__PURE__ */ import_react4.default.createElement(import_dsh_client_ui_primitives.Button, { variant: "outline", size: "sm", type: "button", onClick: openPanel }, "\u6253\u5F00\u5DE5\u4F5C\u53F0");
4925
5347
  }
4926
- async function apply(ctx) {
4927
- const disposeOfficialToolsRemote = await ctx.remote.$mount(OFFICIAL_TOOLS_CLIENT_REMOTE);
4928
- ctx.effect(() => disposeOfficialToolsRemote, "model-router-galgame: official tools client remote");
5348
+ function registerUi(ctx) {
4929
5349
  const officialToolsRemote = ctx.remote[OFFICIAL_TOOLS_REMOTE_NAMESPACE];
4930
5350
  const settingsScope = ctx.configForms.get(ROUTER_NAMESPACE);
4931
5351
  const card = new RouterSettingsCardController(settingsScope);
@@ -4944,7 +5364,7 @@ async function apply(ctx) {
4944
5364
  loadCatalog: () => ctx.remote.session.modelCatalog(),
4945
5365
  settingsScope,
4946
5366
  listOfficialTools: () => officialToolsRemote.list(),
4947
- installOfficialTool: (toolId) => officialToolsRemote.install(toolId),
5367
+ installOfficialTool: (toolId) => officialToolsRemote.installTool(toolId),
4948
5368
  cancelOfficialToolInstall: (toolId) => officialToolsRemote.cancel(toolId),
4949
5369
  officialToolInstallStatus: (toolId) => officialToolsRemote.status(toolId)
4950
5370
  })
@@ -4977,6 +5397,21 @@ async function apply(ctx) {
4977
5397
  inject: () => ({ openPanel: () => ctx.layout.selectPanel(ROUTER_PANEL) })
4978
5398
  }, OpenRouterWorkspace))), "model-router-galgame: bundle open action");
4979
5399
  }
5400
+ async function apply(ctx) {
5401
+ const disposeRemote = await ctx.remote.$mount(OFFICIAL_TOOLS_CLIENT_REMOTE);
5402
+ const ui = ctx.inject(UI_INJECT, registerUi);
5403
+ try {
5404
+ await ui;
5405
+ } catch (error) {
5406
+ await ui.dispose();
5407
+ await disposeRemote();
5408
+ throw error;
5409
+ }
5410
+ return async () => {
5411
+ await ui.dispose();
5412
+ await disposeRemote();
5413
+ };
5414
+ }
4980
5415
  var styles = Object.freeze({
4981
5416
  titleLine: { display: "flex", alignItems: "center", flexWrap: "wrap", gap: "8px" },
4982
5417
  notice: {
@@ -4991,7 +5426,12 @@ var styles = Object.freeze({
4991
5426
  color: "var(--dsw-alias-label-secondary)",
4992
5427
  fontSize: "13px",
4993
5428
  lineHeight: "20px"
4994
- }
5429
+ },
5430
+ profileEditor: { display: "grid", gap: "8px", marginTop: "14px" },
5431
+ profileLabel: { fontSize: "13px", fontWeight: 600 },
5432
+ profileTextarea: { width: "100%", minHeight: "150px", padding: "10px", fontFamily: "monospace", fontSize: "12px", borderRadius: "7px", border: "1px solid var(--dsw-alias-border-l1)", boxSizing: "border-box" },
5433
+ profileError: { color: "var(--dsw-alias-label-danger)", fontSize: "12px", margin: 0 },
5434
+ profileExample: { fontSize: "12px", whiteSpace: "pre-wrap" }
4995
5435
  });
4996
5436
  return module.exports;
4997
5437
  },