@ljwei-stak/model-router-galgame 0.11.0 → 0.12.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.
Files changed (33) hide show
  1. package/.dsh-plugin/client.js +1943 -4
  2. package/.dsh-plugin/official-tools-remote-service.mjs +1 -78
  3. package/.dsh-plugin/shared/official-tools-remote.mjs +0 -50
  4. package/INSTALLATION_GUIDE.zh.md +84 -69
  5. package/MIGRATION.md +36 -16
  6. package/README.i18n.yaml +3 -6
  7. package/README.md +68 -116
  8. package/README.zh.md +65 -99
  9. package/docs/assets/candidate-pruning.svg +80 -0
  10. package/docs/assets/desktop-official-tools-0.9.0.png +0 -0
  11. package/docs/assets/router-only-0.12.0.png +0 -0
  12. package/docs/assets/routing-workflow.svg +96 -0
  13. package/docs/assets/workbench-usage.svg +119 -0
  14. package/package.json +9 -23
  15. package/.dsh-plugin/shared/gal-spring-act1.mjs +0 -164
  16. package/.dsh-plugin/shared/gal-spring-act2.mjs +0 -482
  17. package/.dsh-plugin/shared/gal-spring-act3.mjs +0 -124
  18. package/.dsh-plugin/shared/gal-spring-sides.mjs +0 -392
  19. package/.dsh-plugin/shared/gal-story-echo.mjs +0 -415
  20. package/.dsh-plugin/shared/gal-story-echocity.mjs +0 -1283
  21. package/.dsh-plugin/shared/gal-story-spring.mjs +0 -333
  22. package/docs/ECHO_CITY_STORY.zh.md +0 -117
  23. package/docs/GAL_ART_PROMPTS.zh.md +0 -53
  24. package/docs/GAL_ART_SOURCES.zh.md +0 -84
  25. package/docs/GAL_SETTINGS.zh.md +0 -211
  26. package/docs/assets/gal-claude-0.10.1-preview.png +0 -0
  27. package/docs/assets/gal-fullbody-0.11.0-preview.jpg +0 -0
  28. package/docs/assets/gal-official-fullbody-0.11.0.jpg +0 -0
  29. package/docs/assets/gal-official-rc2-0.11.0.jpg +0 -0
  30. package/docs/assets/gal-portrait-sources.json +0 -355
  31. package/docs/assets/gal-settings-0.11.0-preview.jpg +0 -0
  32. package/docs/assets/gal-spring-0.10.1-preview.png +0 -0
  33. package/docs/assets/gal-title-0.11.0-preview.jpg +0 -0
@@ -1,4 +1,1943 @@
1
- [diffend] Oversized file quarantined before diffing.
2
- name: package/.dsh-plugin/client.js
3
- size: 57234430 bytes
4
- sha256: 3408d5989501efa0d7c28c0845c8a3f2b553014fcd4a5e76d9fb0df9016d7924
1
+ window.__ModuleLoader__.load({
2
+ id: "@ljwei-stak/model-router-galgame",
3
+ factory: (require) => {
4
+ var module = { exports: {} };
5
+ var exports = module.exports;
6
+ var __create = Object.create;
7
+ var __defProp = Object.defineProperty;
8
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
9
+ var __getOwnPropNames = Object.getOwnPropertyNames;
10
+ var __getProtoOf = Object.getPrototypeOf;
11
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
12
+ var __export = (target, all) => {
13
+ for (var name in all)
14
+ __defProp(target, name, { get: all[name], enumerable: true });
15
+ };
16
+ var __copyProps = (to, from, except, desc) => {
17
+ if (from && typeof from === "object" || typeof from === "function") {
18
+ for (let key of __getOwnPropNames(from))
19
+ if (!__hasOwnProp.call(to, key) && key !== except)
20
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
21
+ }
22
+ return to;
23
+ };
24
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
25
+ // If the importer is in node compatibility mode or this is not an ESM
26
+ // file that has been converted to a CommonJS file using a Babel-
27
+ // compatible transform (i.e. "__esModule" has not been set), then set
28
+ // "default" to the CommonJS "module.exports" for node compatibility.
29
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
30
+ mod
31
+ ));
32
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
33
+
34
+ // .dsh-plugin/client/official-harness.jsx
35
+ var official_harness_exports = {};
36
+ __export(official_harness_exports, {
37
+ ROUTER_NAMESPACE: () => ROUTER_NAMESPACE,
38
+ ROUTER_PACKAGE: () => ROUTER_PACKAGE,
39
+ ROUTER_PANEL: () => ROUTER_PANEL,
40
+ RouterSettingsCard: () => RouterSettingsCard,
41
+ RouterSettingsCardController: () => RouterSettingsCardController,
42
+ apply: () => apply,
43
+ inject: () => inject
44
+ });
45
+ module.exports = __toCommonJS(official_harness_exports);
46
+ var import_react3 = __toESM(require("react"), 1);
47
+
48
+ // .dsh-plugin/shared/official-tool-registry.mjs
49
+ var OFFICIAL_TOOLS = Object.freeze([
50
+ Object.freeze({
51
+ id: "kimi-code",
52
+ label: "Kimi Code \xB7 Node",
53
+ vendor: "Moonshot AI",
54
+ purpose: "Kimi \u5B98\u65B9\u7F16\u7A0B CLI\uFF0C\u63D0\u4F9B kimi \u547D\u4EE4\u4E0E ACP \u4F1A\u8BDD\u3002",
55
+ package: "@moonshot-ai/kimi-code",
56
+ version: "2.1.1",
57
+ manager: "npm",
58
+ installArgs: ["install", "-g", "@moonshot-ai/kimi-code@2.1.1", "--registry=https://registry.npmjs.org/"],
59
+ probeExecutables: ["kimi"],
60
+ 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",
61
+ providerHints: ["moonshot", "kimi"]
62
+ }),
63
+ Object.freeze({
64
+ id: "claude-code",
65
+ label: "Claude Code",
66
+ vendor: "Anthropic",
67
+ purpose: "Anthropic \u5B98\u65B9\u7F16\u7A0B CLI\uFF0C\u63D0\u4F9B claude \u547D\u4EE4\u3002",
68
+ package: "@anthropic-ai/claude-code",
69
+ version: "2.1.283",
70
+ manager: "npm",
71
+ installArgs: ["install", "-g", "@anthropic-ai/claude-code@2.1.283", "--registry=https://registry.npmjs.org/"],
72
+ probeExecutables: ["claude"],
73
+ providerHints: ["anthropic", "claude"]
74
+ }),
75
+ Object.freeze({
76
+ id: "codex",
77
+ label: "Codex CLI",
78
+ vendor: "OpenAI",
79
+ purpose: "OpenAI \u5B98\u65B9\u7F16\u7A0B CLI\uFF0C\u63D0\u4F9B codex \u547D\u4EE4\u3002",
80
+ package: "@openai/codex",
81
+ version: "0.157.1",
82
+ installArgs: ["install", "-g", "@openai/codex@0.157.1", "--registry=https://registry.npmjs.org/"],
83
+ manager: "npm",
84
+ probeExecutables: ["codex"],
85
+ probeNote: "Codex \u7248\u672C\u6A2A\u5E45\u7531\u9002\u914D\u5C42\u5BBD\u5339\u914D\uFF1B\u5B89\u88C5\u65F6\u56FA\u5B9A\u7248\u672C\u3002",
86
+ providerHints: ["openai", "gpt", "codex"]
87
+ }),
88
+ Object.freeze({
89
+ id: "minimax-code",
90
+ label: "MiniMax Code",
91
+ vendor: "MiniMax",
92
+ purpose: "MiniMax \u5B98\u65B9\u7F16\u7A0B CLI\uFF0C\u63D0\u4F9B mcode \u547D\u4EE4\u3002",
93
+ package: "@minimax-ai/code",
94
+ version: "0.5.5",
95
+ manager: "npm",
96
+ 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"],
97
+ probeExecutables: ["mcode"],
98
+ providerHints: ["minimax"]
99
+ }),
100
+ Object.freeze({
101
+ id: "mimo-code",
102
+ label: "MiMo Code",
103
+ vendor: "XiaoMi",
104
+ purpose: "\u5C0F\u7C73 MiMo \u5B98\u65B9\u7F16\u7A0B CLI\uFF0C\u63D0\u4F9B mimo \u547D\u4EE4\u3002",
105
+ package: "@mimo-ai/cli",
106
+ version: "0.1.15",
107
+ manager: "npm",
108
+ installArgs: ["install", "-g", "@mimo-ai/cli@0.1.15", "--registry=https://registry.npmjs.org/"],
109
+ probeExecutables: ["mimo"],
110
+ providerHints: ["mimo", "xiaomi"]
111
+ }),
112
+ Object.freeze({
113
+ id: "grok-build",
114
+ label: "Grok Build",
115
+ vendor: "xAI",
116
+ purpose: "xAI \u5B98\u65B9\u7F16\u7A0B CLI\uFF0C\u63D0\u4F9B grok \u547D\u4EE4\u4E0E ACP \u4F1A\u8BDD\u3002",
117
+ package: "@xai-official/grok",
118
+ version: "1.0.41",
119
+ manager: "npm",
120
+ installArgs: ["install", "-g", "@xai-official/grok@1.0.41", "--registry=https://registry.npmjs.org/"],
121
+ probeExecutables: ["grok"],
122
+ providerHints: ["xai", "grok"]
123
+ }),
124
+ Object.freeze({
125
+ id: "zcode",
126
+ label: "ZCode",
127
+ vendor: "Z.ai",
128
+ 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",
129
+ version: "3.14.3",
130
+ manager: "signed-windows-installer",
131
+ installArgs: [],
132
+ probeExecutables: [],
133
+ 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",
134
+ providerHints: ["zai", "z.ai", "zcode", "glm", "zhipu"]
135
+ })
136
+ ]);
137
+ var TOOL_BY_ID = new Map(OFFICIAL_TOOLS.map((tool) => [tool.id, tool]));
138
+ function getOfficialTool(id2) {
139
+ return TOOL_BY_ID.get(String(id2 ?? "").trim()) ?? null;
140
+ }
141
+ function toolForProvider(provider) {
142
+ const value = String(provider ?? "").trim().toLowerCase();
143
+ if (!value) return null;
144
+ for (const tool of OFFICIAL_TOOLS) {
145
+ if (tool.providerHints.some((hint) => value.includes(hint))) return tool;
146
+ }
147
+ return null;
148
+ }
149
+ function installCommandLine(tool) {
150
+ if (!tool || tool.unsupported) return null;
151
+ if (tool.manager === "signed-windows-installer") return "\u6253\u5F00\u5B98\u65B9\u7B7E\u540D\u5B89\u88C5\u5668\uFF08\u9009\u62E9\u5B89\u88C5\u76EE\u5F55\uFF09";
152
+ return tool.manager === "npm" ? `npm ${tool.installArgs.join(" ")}` : `${tool.manager} ${tool.installArgs.join(" ")}`;
153
+ }
154
+
155
+ // .dsh-plugin/shared/model-profiles.mjs
156
+ var MAX_TEXT = 32e3;
157
+ var MAX_PROFILES = 200;
158
+ var id = (value) => typeof value === "string" ? value.trim() : "";
159
+ function nonnegative(value, label) {
160
+ if (typeof value !== "number" || !Number.isFinite(value) || value < 0 || value > 1e6) {
161
+ throw new Error(`${label} \u5FC5\u987B\u662F 0 \u5230 1000000 \u4E4B\u95F4\u7684\u6709\u9650\u6570\u5B57`);
162
+ }
163
+ return value;
164
+ }
165
+ function normalizeProfile(entry, index) {
166
+ const label = `\u7B2C ${index + 1} \u4E2A\u6A21\u578B`;
167
+ if (!entry || typeof entry !== "object" || Array.isArray(entry)) throw new Error(`${label} \u5FC5\u987B\u662F\u5BF9\u8C61`);
168
+ const allowed = /* @__PURE__ */ new Set(["provider", "model", "quality", "pricing", "specialties", "cliModel"]);
169
+ const unknown = Object.keys(entry).find((key) => !allowed.has(key));
170
+ 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`);
171
+ const provider = id(entry.provider);
172
+ const model = id(entry.model);
173
+ if (!provider || !model || provider.length > 160 || model.length > 240) {
174
+ throw new Error(`${label} \u9700\u8981\u6A21\u578B\u76EE\u5F55\u4E2D\u7684\u51C6\u786E provider \u548C model`);
175
+ }
176
+ const profile = { provider, model };
177
+ if (entry.quality !== void 0) {
178
+ const value = nonnegative(entry.quality, `${label} \u7684 quality`);
179
+ if (value > 100) throw new Error(`${label} \u7684 quality \u5E94\u5728 0 \u5230 100 \u4E4B\u95F4`);
180
+ profile.quality = value / 100;
181
+ }
182
+ if (entry.pricing !== void 0) {
183
+ const prices = entry.pricing;
184
+ if (!prices || typeof prices !== "object" || Array.isArray(prices)) throw new Error(`${label} \u7684 pricing \u5FC5\u987B\u662F\u5BF9\u8C61`);
185
+ const priceFields = /* @__PURE__ */ new Set(["input", "output", "cacheRead", "cacheWrite", "currency"]);
186
+ const unknownPrice = Object.keys(prices).find((key) => !priceFields.has(key));
187
+ if (unknownPrice) throw new Error(`${label} \u7684 pricing \u542B\u4E0D\u652F\u6301\u7684\u5B57\u6BB5 ${unknownPrice}`);
188
+ if (prices.currency !== void 0 && prices.currency !== "USD") throw new Error(`${label} \u7684\u4EF7\u683C\u5E01\u79CD\u76EE\u524D\u4EC5\u652F\u6301 USD`);
189
+ profile.pricing = {
190
+ input: nonnegative(prices.input, `${label} \u7684\u8F93\u5165\u5355\u4EF7`),
191
+ output: nonnegative(prices.output, `${label} \u7684\u8F93\u51FA\u5355\u4EF7`),
192
+ currency: "USD"
193
+ };
194
+ if (prices.cacheRead !== void 0) profile.pricing.cacheRead = nonnegative(prices.cacheRead, `${label} \u7684\u7F13\u5B58\u8BFB\u53D6\u5355\u4EF7`);
195
+ if (prices.cacheWrite !== void 0) profile.pricing.cacheWrite = nonnegative(prices.cacheWrite, `${label} \u7684\u7F13\u5B58\u5199\u5165\u5355\u4EF7`);
196
+ }
197
+ if (entry.specialties !== void 0) {
198
+ if (!Array.isArray(entry.specialties) || entry.specialties.length > 16 || entry.specialties.some((value) => !/^[a-z][a-z0-9-]{0,39}$/.test(value))) {
199
+ throw new Error(`${label} \u7684 specialties \u5FC5\u987B\u662F\u81F3\u591A 16 \u4E2A\u82F1\u6587\u4EFB\u52A1\u6807\u7B7E`);
200
+ }
201
+ profile.specialties = [...new Set(entry.specialties)];
202
+ }
203
+ if (entry.cliModel !== void 0) {
204
+ const cliModel = id(entry.cliModel);
205
+ if (!/^[A-Za-z0-9][A-Za-z0-9._:/-]{0,119}$/.test(cliModel)) throw new Error(`${label} \u7684 cliModel \u65E0\u6548`);
206
+ if (toolForProvider(provider)?.id === "zcode") throw new Error(`${label} \u5BF9\u5E94\u7684 ZCode CLI \u6682\u4E0D\u652F\u6301\u9010\u6B21\u5207\u6362\u6A21\u578B`);
207
+ profile.cliModel = cliModel;
208
+ }
209
+ if (Object.keys(profile).length === 2) throw new Error(`${label} \u81F3\u5C11\u63D0\u4F9B quality\u3001pricing\u3001specialties \u6216 cliModel \u4E4B\u4E00`);
210
+ return profile;
211
+ }
212
+ function parseModelProfilesJson(value) {
213
+ const source = value == null || value === "" ? "[]" : value;
214
+ 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");
215
+ let input;
216
+ try {
217
+ input = JSON.parse(source);
218
+ } catch {
219
+ throw new Error("\u6A21\u578B\u4EF7\u683C\u4E0E\u80FD\u529B\u914D\u7F6E\u4E0D\u662F\u6709\u6548\u7684 JSON");
220
+ }
221
+ 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");
222
+ const profiles = input.map(normalizeProfile);
223
+ const seen = /* @__PURE__ */ new Set();
224
+ for (const profile of profiles) {
225
+ const key = `${profile.provider}\0${profile.model}`;
226
+ if (seen.has(key)) throw new Error(`\u6A21\u578B ${profile.provider}/${profile.model} \u91CD\u590D\u914D\u7F6E`);
227
+ seen.add(key);
228
+ }
229
+ return profiles;
230
+ }
231
+ function applyModelProfiles(routes, profiles) {
232
+ const input = Array.isArray(routes) ? routes : [];
233
+ const values = typeof profiles === "string" ? parseModelProfilesJson(profiles) : profiles;
234
+ if (!Array.isArray(values)) throw new Error("\u6A21\u578B\u914D\u7F6E\u5FC5\u987B\u662F\u6570\u7EC4");
235
+ const byKey = new Map(values.map((profile) => [`${profile.provider}\0${profile.model}`, profile]));
236
+ return input.map((route) => {
237
+ const profile = byKey.get(`${route.provider}\0${route.model}`);
238
+ if (!profile) return route;
239
+ return {
240
+ ...route,
241
+ ...profile.quality === void 0 ? {} : { quality: profile.quality, qualitySource: "user" },
242
+ ...profile.pricing === void 0 ? {} : { pricing: { ...profile.pricing }, pricingSource: "user" },
243
+ ...profile.specialties === void 0 ? {} : { specialties: [...profile.specialties] },
244
+ ...profile.cliModel === void 0 ? {} : { cliModel: profile.cliModel }
245
+ };
246
+ });
247
+ }
248
+
249
+ // .dsh-plugin/client/router-main.jsx
250
+ var import_react2 = __toESM(require("react"), 1);
251
+
252
+ // .dsh-plugin/shared/livebench.mjs
253
+ var TASK_ALIASES = Object.freeze({
254
+ reasoning: ["reasoning", "reasoning_score", "hard_reasoning"],
255
+ code: ["code", "coding", "coding_score"],
256
+ math: ["math", "mathematics", "math_score"],
257
+ research: ["research", "retrieval", "knowledge", "data_analysis"],
258
+ writing: ["writing", "creative_writing", "language"],
259
+ vision: ["vision", "multimodal", "visual"],
260
+ summarization: ["summarization", "summary", "if"],
261
+ classification: ["classification", "instruction_following"]
262
+ });
263
+ var CATEGORY_TO_TASK = Object.freeze({
264
+ reasoning: "reasoning",
265
+ coding: "code",
266
+ "agentic coding": "code",
267
+ mathematics: "math",
268
+ "data analysis": "research",
269
+ language: "writing",
270
+ if: "summarization",
271
+ vision: "vision",
272
+ multimodal: "vision"
273
+ });
274
+ function normalized(value) {
275
+ return String(value ?? "").toLowerCase().replace(/[^a-z0-9]+/g, "");
276
+ }
277
+ function liveBenchRow(snapshot, model) {
278
+ return snapshot?.models?.[normalized(model)] ?? null;
279
+ }
280
+
281
+ // .dsh-plugin/shared/router.mjs
282
+ var OBJECTIVE_WEIGHTS = Object.freeze({
283
+ simple: Object.freeze({ quality: 0.28, cost: 0.45, latency: 0.14, specialty: 0.04, reasoning: 0.07, risk: 0.02 }),
284
+ balanced: Object.freeze({ quality: 0.4, cost: 0.26, latency: 0.1, specialty: 0.09, reasoning: 0.1, risk: 0.05 }),
285
+ complex: Object.freeze({ quality: 0.48, cost: 0.14, latency: 0.06, specialty: 0.14, reasoning: 0.11, risk: 0.07 })
286
+ });
287
+ var REASONING_EFFORT_ORDER = Object.freeze(["off", "minimal", "low", "medium", "high", "xhigh", "max"]);
288
+ var REASONING_EFFORT_MULTIPLIERS = Object.freeze({
289
+ off: Object.freeze({ output: 0.78, latency: 0.75 }),
290
+ minimal: Object.freeze({ output: 0.86, latency: 0.82 }),
291
+ low: Object.freeze({ output: 0.93, latency: 0.9 }),
292
+ medium: Object.freeze({ output: 1, latency: 1 }),
293
+ high: Object.freeze({ output: 1.16, latency: 1.15 }),
294
+ xhigh: Object.freeze({ output: 1.34, latency: 1.3 }),
295
+ max: Object.freeze({ output: 1.58, latency: 1.5 })
296
+ });
297
+ var QUALITY_FLOORS = Object.freeze({ simple: 0.75, balanced: 0.78, complex: 0.82 });
298
+ var DEFAULT_ROUTER_SETTINGS = Object.freeze({
299
+ pricing: Object.freeze({}),
300
+ // The official site publishes versioned table/categories assets and the
301
+ // adapter discovers the newest release from this root URL.
302
+ liveBenchEndpoint: "https://livebench.ai",
303
+ liveBenchTtlMs: 9e5,
304
+ budgetUsd: 0,
305
+ cacheReadRatio: 0,
306
+ cacheWriteRatio: 0
307
+ });
308
+ var MODEL_CATALOG = Object.freeze([
309
+ { id: "claude-fable-5", aliases: ["claude-fable-5", "claude fable 5"], quality: 0.99, latency: 0.34, costIn: 10, costOut: 50, specialties: ["reasoning", "writing", "research"], risk: 0.06 },
310
+ { id: "claude-opus-4-8", aliases: ["claude-opus-4-8", "claude opus 4.8"], quality: 0.97, latency: 0.39, costIn: 5, costOut: 25, specialties: ["reasoning", "writing", "code"], risk: 0.07 },
311
+ { id: "gpt-5.6-sol", aliases: ["gpt-5.6-sol", "gpt 5.6 sol"], quality: 0.98, latency: 0.4, costIn: 5, costOut: 30, specialties: ["reasoning", "code", "math", "vision"], risk: 0.06 },
312
+ { id: "gpt-5.5", aliases: ["gpt-5.5", "gpt 5.5"], quality: 0.95, latency: 0.44, costIn: 5, costOut: 30, specialties: ["reasoning", "code", "math"], risk: 0.08 },
313
+ { id: "deepseek-v4-pro", aliases: ["deepseek-v4-pro", "deepseek v4 pro"], quality: 0.93, latency: 0.52, costIn: 1.74, costOut: 3.48, specialties: ["code", "math", "reasoning"], risk: 0.1 },
314
+ { id: "deepseek-v4-flash", aliases: ["deepseek-v4-flash", "deepseek v4 flash"], quality: 0.82, latency: 0.82, costIn: 0.14, costOut: 0.28, specialties: ["code", "summarization", "classification"], risk: 0.14 },
315
+ { id: "kimi-k3", aliases: ["kimi-k3", "kimi k3"], quality: 0.91, latency: 0.56, costIn: 3, costOut: 15, specialties: ["reasoning", "long-context", "code"], risk: 0.1 },
316
+ { id: "qwen3.7-max", aliases: ["qwen3.7-max", "qwen 3.7 max"], quality: 0.94, latency: 0.5, costIn: 2.5, costOut: 7.5, specialties: ["reasoning", "math", "code"], risk: 0.08 },
317
+ { id: "qwen3.7-plus", aliases: ["qwen3.7-plus", "qwen 3.7 plus"], quality: 0.87, latency: 0.72, costIn: 0.4, costOut: 1.6, specialties: ["code", "math", "writing"], risk: 0.12 },
318
+ { id: "glm-5.2", aliases: ["glm-5.2", "glm 5.2"], quality: 0.89, latency: 0.64, costIn: 1.4, costOut: 4.4, specialties: ["reasoning", "writing", "math"], risk: 0.11 },
319
+ { id: "gpt-5.6-luna", aliases: ["gpt-5.6-luna", "gpt 5.6 luna"], quality: 0.84, latency: 0.86, costIn: 0.2, costOut: 1.2, specialties: ["classification", "summarization", "code"], risk: 0.14 },
320
+ { id: "gpt-5.6-terra", aliases: ["gpt-5.6-terra", "gpt 5.6 terra"], quality: 0.91, latency: 0.66, costIn: 2, costOut: 12, specialties: ["code", "writing", "reasoning"], risk: 0.1 },
321
+ { id: "minimax-m3", aliases: ["minimax-m3", "minimax m3"], quality: 0.86, latency: 0.69, costIn: 0.3, costOut: 1.2, specialties: ["writing", "code", "summarization"], risk: 0.13 },
322
+ { id: "gemini-3-flash", aliases: ["gemini 3 flash", "gemini-3-flash"], quality: 0.88, latency: 0.73, costIn: 0.5, costOut: 3, specialties: ["vision", "research", "summarization"], risk: 0.12 },
323
+ { id: "big-pickle", aliases: ["big pickle"], quality: 0.7, latency: 0.88, costIn: 0, costOut: 0, specialties: ["classification", "summarization"], risk: 0.24 }
324
+ ]);
325
+ var clamp = (value, min = 0, max = 1) => Math.max(min, Math.min(max, value));
326
+ var normalize = (value) => String(value ?? "").toLowerCase().replace(/[^a-z0-9]+/g, "");
327
+ var routeKey = (provider, model) => `${String(provider ?? "")}/${String(model ?? "")}`;
328
+ var OPENCODE_CATALOG_PROVIDERS = Object.freeze([
329
+ "opencode",
330
+ "opencode-go",
331
+ // Some OpenCode-compatible configuration examples use the product name as
332
+ // the route id. Treat those aliases as catalog routes too; the pi-ai catalog
333
+ // still owns the actual model endpoints.
334
+ "opencode-zen",
335
+ "opencode-go-zen"
336
+ ]);
337
+ function classifyTask(text2) {
338
+ const value = String(text2 ?? "");
339
+ if (value.length < 80 && /翻译|解释|translate|explain/i.test(value)) return "general";
340
+ return detectTaskTypes(value)[0] ?? "general";
341
+ }
342
+ var TASK_TYPE_RULES = Object.freeze([
343
+ ["vision", /图片|图像|照片|视觉|image|vision|截图|识图/i],
344
+ ["math", /数学|证明|定理|公式|方程|math|proof|theorem/i],
345
+ ["code", /代码|编程|工程|项目|架构|接口|api|debug|实现|部署|测试|code/i],
346
+ ["research", /研究|论文|文献|联网|检索|research|source|引用/i],
347
+ ["summarization", /总结|摘要|提炼|提取|关键词|分类|翻译|summar|classif|extract/i],
348
+ ["writing", /写作|润色|小说|文案|报告|writing|draft/i]
349
+ ]);
350
+ var TASK_TYPE_LABELS = Object.freeze({
351
+ vision: "\u89C6\u89C9\u5904\u7406",
352
+ math: "\u6570\u5B66\u63A8\u5BFC",
353
+ code: "\u5DE5\u7A0B\u4E0E\u4EE3\u7801",
354
+ research: "\u7814\u7A76\u4E0E\u68C0\u7D22",
355
+ summarization: "\u6458\u8981\u4E0E\u6574\u7406",
356
+ writing: "\u5199\u4F5C\u4E0E\u8868\u8FBE"
357
+ });
358
+ function detectTaskTypes(text2) {
359
+ const value = String(text2 ?? "");
360
+ const ranked = TASK_TYPE_RULES.map(([type, pattern]) => ({
361
+ type,
362
+ signals: value.match(new RegExp(pattern.source, pattern.flags.includes("g") ? pattern.flags : `${pattern.flags}g`))?.length ?? 0
363
+ })).filter((item) => item.signals > 0);
364
+ ranked.sort((left, right) => right.signals - left.signals || left.type.localeCompare(right.type));
365
+ return ranked.map((item) => item.type);
366
+ }
367
+ function assessComplexity(text2) {
368
+ const value = String(text2 ?? "");
369
+ const lengthScore = clamp(value.length / 2200);
370
+ const requirementScore = clamp((value.match(/(?:^|\n)\s*(?:[-*]|\d+[.)]|[一二三四五六七八九十]+[、.])/g) ?? []).length / 8);
371
+ const codeScore = /(代码|工程|架构|接口|实现|部署|测试|code|api|debug)/i.test(value) ? 0.22 : 0;
372
+ const highReasoningScore = /(数学|证明|定理|研究|论文|复杂|多步骤|约束|比较|评估|架构|模块|部署|math|proof|research)/i.test(value) ? 0.2 : 0;
373
+ const visionScore = /(图片|图像|照片|截图|视觉|image|vision)/i.test(value) ? 0.12 : 0;
374
+ const domainMarkers = (value.match(/代码|工程|架构|接口|实现|部署|测试|模块|拆分|约束|评估|证明|定理|研究|论文|图片|图像|照片|视觉|code|api|debug|proof|research|vision/gi) ?? []).length;
375
+ const domainComplexity = clamp(domainMarkers / 5) * 0.28;
376
+ const raw = clamp(0.1 + lengthScore * 0.3 + requirementScore * 0.18 + domainComplexity + codeScore + highReasoningScore + visionScore);
377
+ const band = isHardRequirement(value) ? "complex" : isSimpleRequirement(value) && value.length <= 180 ? "simple" : raw < 0.34 ? "simple" : raw < 0.66 ? "balanced" : "complex";
378
+ return { value: raw, band };
379
+ }
380
+ function specialtyMatch(model, taskType, liveScores = {}) {
381
+ const benchmark = asScore(liveScores?.[taskType]);
382
+ if (benchmark !== void 0) return benchmark;
383
+ if (model.specialties.includes(taskType)) return 1;
384
+ if (taskType === "general") return 0.58;
385
+ if (taskType === "research" && model.specialties.includes("writing")) return 0.68;
386
+ if (taskType === "writing" && model.specialties.includes("reasoning")) return 0.62;
387
+ return 0.38;
388
+ }
389
+ function qualityForTask(row, taskType) {
390
+ return asScore(row?.liveScores?.[taskType]) ?? asScore(row?.liveOverall) ?? row?.metadata?.quality ?? row?.quality ?? 0;
391
+ }
392
+ function specialtyForTask(row, taskType) {
393
+ return specialtyMatch(row.metadata, taskType, row.liveScores);
394
+ }
395
+ function asScore(value) {
396
+ if (value === null || value === void 0 || value === "") return void 0;
397
+ const number = Number(value);
398
+ if (!Number.isFinite(number)) return void 0;
399
+ return clamp(number > 1 ? number / 100 : number);
400
+ }
401
+ function normalizePricing(pricing) {
402
+ if (pricing === null || typeof pricing !== "object" || Array.isArray(pricing)) return {};
403
+ const normalized2 = {};
404
+ for (const [id2, raw] of Object.entries(pricing)) {
405
+ if (raw === null || typeof raw !== "object" || Array.isArray(raw)) continue;
406
+ if ((raw.input ?? raw.costIn) === null || (raw.input ?? raw.costIn) === void 0) continue;
407
+ if ((raw.output ?? raw.costOut) === null || (raw.output ?? raw.costOut) === void 0) continue;
408
+ const input = Number(raw.input ?? raw.costIn);
409
+ const output = Number(raw.output ?? raw.costOut);
410
+ const cacheRead = Number(raw.cacheRead ?? input);
411
+ const cacheWrite = Number(raw.cacheWrite ?? input);
412
+ if (![input, output, cacheRead, cacheWrite].every((value) => Number.isFinite(value) && value >= 0)) continue;
413
+ if (String(raw.currency ?? "USD").toUpperCase() !== "USD") continue;
414
+ normalized2[normalize(id2)] = {
415
+ input,
416
+ output,
417
+ cacheRead,
418
+ cacheWrite,
419
+ currency: String(raw.currency ?? "USD").toUpperCase()
420
+ };
421
+ }
422
+ return normalized2;
423
+ }
424
+ function pricingFor(model, pricing, provider = "") {
425
+ const normalizedPricing = normalizePricing(pricing);
426
+ const providerOverride = provider === "" ? void 0 : normalizedPricing[normalize(`${provider}/${model.id}`)];
427
+ const override = providerOverride ?? normalizedPricing[normalize(model.id)];
428
+ if (override) return override;
429
+ 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;
430
+ return {
431
+ input: Number(model.costIn),
432
+ output: Number(model.costOut),
433
+ cacheRead: Number(model.cacheRead ?? model.costIn),
434
+ cacheWrite: Number(model.cacheWrite ?? model.costIn),
435
+ currency: "USD"
436
+ };
437
+ }
438
+ function normalizedCacheRatios(cacheReadRatio = 0, cacheWriteRatio = 0) {
439
+ const read = Number.isFinite(Number(cacheReadRatio)) ? clamp(Number(cacheReadRatio)) : 0;
440
+ const write = Number.isFinite(Number(cacheWriteRatio)) ? Math.min(clamp(Number(cacheWriteRatio)), 1 - read) : 0;
441
+ return { read, write };
442
+ }
443
+ function effectivePricing(pricing, cacheReadRatio = 0, cacheWriteRatio = 0) {
444
+ const { read, write } = normalizedCacheRatios(cacheReadRatio, cacheWriteRatio);
445
+ return {
446
+ input: (1 - read - write) * Number(pricing.input) + read * Number(pricing.cacheRead) + write * Number(pricing.cacheWrite),
447
+ output: Number(pricing.output)
448
+ };
449
+ }
450
+ function costScore(pricing, maxCost, cacheReadRatio = 0, cacheWriteRatio = 0) {
451
+ if (pricing === null) return 0;
452
+ const effective = effectivePricing(pricing, cacheReadRatio, cacheWriteRatio);
453
+ const mean = (effective.input + effective.output) / 2;
454
+ if (maxCost <= 0) return mean === 0 ? 1 : 0;
455
+ return clamp(1 - mean / maxCost);
456
+ }
457
+ function estimateCost(model, text2, outputTokens = 900, pricingOverrides = {}, cacheReadRatio = 0, cacheWriteRatio = 0) {
458
+ const pricing = pricingFor(model, pricingOverrides);
459
+ if (pricing === null) return null;
460
+ const inputTokens = Math.max(80, Math.ceil(String(text2 ?? "").length / 3.7));
461
+ const ratios = normalizedCacheRatios(cacheReadRatio, cacheWriteRatio);
462
+ const cacheReadTokens = Math.min(inputTokens, Math.max(0, Math.round(inputTokens * ratios.read)));
463
+ const cacheWriteTokens = Math.min(inputTokens - cacheReadTokens, Math.max(0, Math.round(inputTokens * ratios.write)));
464
+ const billableInputTokens = inputTokens - cacheReadTokens - cacheWriteTokens;
465
+ return (billableInputTokens * pricing.input + cacheReadTokens * pricing.cacheRead + cacheWriteTokens * pricing.cacheWrite + outputTokens * pricing.output) / 1e6;
466
+ }
467
+ function modelMetadata(name) {
468
+ const key = normalize(name);
469
+ if (!key) return null;
470
+ return MODEL_CATALOG.find((model) => model.aliases.some((alias) => key === normalize(alias))) ?? null;
471
+ }
472
+ function taskTokenBudget(text2, task, complexity, cacheReadRatio = 0, cacheWriteRatio = 0) {
473
+ const inputTokens = Math.max(80, Math.ceil(String(text2 ?? "").length / 3.7));
474
+ const multipliers = {
475
+ analysis: { input: 0.9, output: 0.55 },
476
+ execution: { input: 1.2, output: (task.difficulty ?? complexity) === "complex" ? 1.45 : 1 },
477
+ verification: { input: 1.15, output: 0.7 },
478
+ synthesis: { input: 1.65, output: 1.3 }
479
+ };
480
+ const multiplier = multipliers[task.purpose] ?? { input: 1, output: 1 };
481
+ const effortMultiplier = reasoningEffortMultiplier(task.reasoningEffort);
482
+ const totalInputTokens = Math.max(80, Math.round(inputTokens * multiplier.input));
483
+ const ratios = normalizedCacheRatios(cacheReadRatio, cacheWriteRatio);
484
+ const cacheReadTokens = Math.min(totalInputTokens, Math.max(0, Math.round(totalInputTokens * ratios.read)));
485
+ const cacheWriteTokens = Math.min(totalInputTokens - cacheReadTokens, Math.max(0, Math.round(totalInputTokens * ratios.write)));
486
+ return {
487
+ inputTokens: totalInputTokens,
488
+ cacheReadTokens,
489
+ cacheWriteTokens,
490
+ outputTokens: Math.max(220, Math.round(900 * multiplier.output * effortMultiplier.output))
491
+ };
492
+ }
493
+ function taskQualityFloor(band, task) {
494
+ const difficulty = task.difficulty ?? band;
495
+ const base = QUALITY_FLOORS[difficulty] ?? QUALITY_FLOORS.balanced;
496
+ if (task.purpose === "synthesis") return Math.max(base, band === "complex" ? 0.84 : base);
497
+ if (difficulty !== "complex") return base;
498
+ return clamp(base + Math.max(0, Number(task.criticality ?? 0.75) - 0.65) * 0.12);
499
+ }
500
+ var MAX_EXPLICIT_EXECUTION_PACKAGES = 6;
501
+ var 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;
502
+ var HIGH_STAKES_WORK = /架构|安全|隐私|权限|并发|事务|迁移|生产|部署|发布|证明|定理|科研|论文|复杂|多步骤|跨系统|系统设计|architecture|security|migration|production|proof|theorem|research/i;
503
+ var SIMPLE_WORK = /翻译|摘要|总结|提取|分类|格式化|列出|改写|润色|拼写|校对|translate|summarize|extract|classify|format|proofread/i;
504
+ var HIGH_STAKES_ACTION = /设计|实现|修复|审计|测试|验证|证明|推导|部署|迁移|规划|研究|解决|推理|design|implement|fix|audit|verify|prove|derive|deploy|migrate|research|solve/i;
505
+ var SIMPLE_REQUEST_START = /^(?:请|帮我)?(?:翻译|摘要|总结|提取|分类|格式化|列出|改写|润色|拼写|校对|translate|summarize|extract|classify|format|proofread)/i;
506
+ function isSimpleRequirement(value) {
507
+ const item = String(value ?? "").trim();
508
+ return SIMPLE_REQUEST_START.test(item) && !/(?:并|然后|最后|同时|以及|and then).{0,20}(?:设计|实现|验证|部署|证明|审计|测试|design|implement|verify|deploy|prove|audit|test)/i.test(item);
509
+ }
510
+ function isHardRequirement(value) {
511
+ const item = String(value ?? "").trim();
512
+ if (isSimpleRequirement(item)) return false;
513
+ return HIGH_STAKES_WORK.test(item) && HIGH_STAKES_ACTION.test(item);
514
+ }
515
+ function requirementDifficulty(objective, type, fallback = "balanced") {
516
+ const value = String(objective ?? "").trim();
517
+ if (isHardRequirement(value)) return "complex";
518
+ if (value.length <= 180 && SIMPLE_WORK.test(value)) return "simple";
519
+ const assessed = assessComplexity(value);
520
+ if (type === "math" && /证明|推导|proof|derive/i.test(value)) return "complex";
521
+ if (assessed.band === "simple" && (type === "code" || type === "research")) return "balanced";
522
+ return assessed.band === "simple" ? "simple" : assessed.band === "complex" ? "complex" : fallback;
523
+ }
524
+ function shouldSplitRequirements(requirements) {
525
+ if (requirements.length >= 3) return true;
526
+ if (requirements.length !== 2) return false;
527
+ const types = requirements.map((item) => detectTaskTypes(item)[0] ?? "general");
528
+ return types[0] !== types[1] || /^(?:最后|然后|接着|随后|再|基于|根据|测试|验证|部署|发布)|(?:完成|结束|实现)后/u.test(requirements[1]);
529
+ }
530
+ function requirementText(value) {
531
+ return String(value ?? "").trim().replace(/[。;;\s]+$/u, "").trim();
532
+ }
533
+ function looksLikeRequirement(value) {
534
+ const item = requirementText(value);
535
+ return item.length >= 3 && !/[::]$/u.test(item) && !/(?:以下|下列|如下)(?:的)?(?:任务|需求|工作|事项|要求)/u.test(item) && REQUIREMENT_ACTION.test(item.slice(0, 32));
536
+ }
537
+ function explicitRequirements(text2) {
538
+ const visibleLines = [];
539
+ let fence = null;
540
+ for (const line of String(text2 ?? "").split(/\r?\n/u)) {
541
+ const marker2 = /^\s*(`{3,}|~{3,})/u.exec(line);
542
+ if (fence) {
543
+ if (marker2 && marker2[1][0] === fence.char && marker2[1].length >= fence.length && !line.slice(marker2[0].length).trim()) fence = null;
544
+ continue;
545
+ }
546
+ if (marker2) {
547
+ fence = { char: marker2[1][0], length: marker2[1].length };
548
+ continue;
549
+ }
550
+ visibleLines.push(line);
551
+ }
552
+ const value = visibleLines.join("\n");
553
+ const lines = visibleLines.map((line) => line.trim()).filter(Boolean);
554
+ if (/^(?:请|帮我)?(?:总结|概括|翻译|摘要|解释)(?:以下|下列|下面|这份|这些)/u.test(lines[0] ?? "") && !/(?:执行|完成|实施|分配)/u.test(lines[0])) return [];
555
+ const marker = /^(?:[-*•]\s+|\d{1,2}[.)、](?!\d)\s*|[一二三四五六七八九十]{1,3}[、.)]\s*)(.+)$/u;
556
+ const marked = lines.map((line) => marker.exec(line)?.[1]).filter(Boolean).map(requirementText).filter(looksLikeRequirement);
557
+ if (marked.length >= 2) return marked;
558
+ const plain = lines.filter((line) => !/^#{1,6}\s|^```|[::]$/u.test(line));
559
+ if (plain.length >= 2 && plain.every(looksLikeRequirement)) {
560
+ return plain.map(requirementText).filter((item) => item.length >= 3);
561
+ }
562
+ const clauses = value.split(/[;;,,]/u).map(requirementText).filter(Boolean);
563
+ if (clauses.length >= 2 && clauses.every(looksLikeRequirement)) {
564
+ return clauses;
565
+ }
566
+ const sentences = value.split(/[。!?]/u).map(requirementText).filter(Boolean);
567
+ if (sentences.length >= 2 && sentences.every(looksLikeRequirement)) {
568
+ return sentences;
569
+ }
570
+ return [];
571
+ }
572
+ function taskPackages(taskType, text2, band) {
573
+ if (band !== "complex") {
574
+ 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" };
575
+ return [{ ...task, qualityFloor: taskQualityFloor(band, task) }];
576
+ }
577
+ const value = String(text2 ?? "");
578
+ const packages = [
579
+ { id: "analysis", name: "\u95EE\u9898\u5EFA\u6A21\u4E0E\u7EA6\u675F\u63D0\u53D6", type: "reasoning", purpose: "analysis", difficulty: "balanced", criticality: 0.8, dependsOn: [], preferredReasoningEffort: "medium" }
580
+ ];
581
+ const requirements = explicitRequirements(text2);
582
+ if (requirements.length >= 2) {
583
+ const groups = requirements.length > MAX_EXPLICIT_EXECUTION_PACKAGES ? [
584
+ ...requirements.slice(0, MAX_EXPLICIT_EXECUTION_PACKAGES - 1).map((item) => [item]),
585
+ requirements.slice(MAX_EXPLICIT_EXECUTION_PACKAGES - 1)
586
+ ] : requirements.map((item) => [item]);
587
+ groups.forEach((group, index) => {
588
+ const objective = group.length === 1 ? group[0] : group.map((item, offset) => `${MAX_EXPLICIT_EXECUTION_PACKAGES + offset}. ${item}`).join("\n");
589
+ const type = detectTaskTypes(objective)[0] ?? "general";
590
+ const difficulty = group.length > 1 ? "complex" : requirementDifficulty(objective, type);
591
+ const previous = packages.at(-1);
592
+ const sequential = /^(?:最后|然后|接着|随后|再|基于|根据|测试|验证|部署|发布)|(?:完成|结束|实现)后/u.test(objective);
593
+ packages.push({
594
+ id: `execution-${index + 1}`,
595
+ name: group.length === 1 ? `\u9700\u6C42 ${index + 1}\uFF1A${group[0].slice(0, 28)}` : `\u9700\u6C42 ${index + 1}\uFF1A\u5176\u4F59 ${group.length} \u9879`,
596
+ objective,
597
+ type,
598
+ purpose: "execution",
599
+ difficulty,
600
+ criticality: difficulty === "simple" ? 0.55 : difficulty === "balanced" ? 0.72 : 0.86,
601
+ dependsOn: sequential && previous?.purpose === "execution" ? ["analysis", previous.id] : ["analysis"],
602
+ preferredReasoningEffort: difficulty === "simple" ? "low" : difficulty === "balanced" ? "medium" : "high"
603
+ });
604
+ });
605
+ } else {
606
+ const domains = [...new Set([...detectTaskTypes(text2), taskType].filter((type) => type !== "general"))];
607
+ for (const type of domains.length > 0 ? domains : [taskType]) {
608
+ const objective = value.split(/[,,。;;]|最后|然后|接着|并且/u).map((item) => item.trim()).filter((item) => detectTaskTypes(item).includes(type)).join("\uFF1B") || value;
609
+ const difficulty = requirementDifficulty(objective, type);
610
+ packages.push({
611
+ id: `execution-${type}`,
612
+ name: `${TASK_TYPE_LABELS[type] ?? type}\u65B9\u5411\u5904\u7406`,
613
+ objective,
614
+ type,
615
+ purpose: "execution",
616
+ difficulty,
617
+ criticality: difficulty === "simple" ? 0.55 : difficulty === "balanced" ? 0.72 : 0.86,
618
+ dependsOn: ["analysis"],
619
+ preferredReasoningEffort: difficulty === "simple" ? "low" : difficulty === "balanced" ? "medium" : "high"
620
+ });
621
+ }
622
+ }
623
+ if (/(测试|验证|评估|对比|benchmark|test|verify|audit)/i.test(value)) {
624
+ packages.push({
625
+ id: "verification",
626
+ name: "\u9A8C\u8BC1\u3001\u53CD\u4F8B\u4E0E\u98CE\u9669\u5BA1\u67E5",
627
+ type: "reasoning",
628
+ purpose: "verification",
629
+ difficulty: "complex",
630
+ criticality: 0.88,
631
+ dependsOn: packages.filter((task) => task.purpose === "execution").map((task) => task.id),
632
+ preferredReasoningEffort: "high"
633
+ });
634
+ }
635
+ packages.push({
636
+ id: "synthesis",
637
+ name: "\u7ED3\u679C\u6821\u9A8C\u4E0E\u6574\u5408",
638
+ type: "reasoning",
639
+ purpose: "synthesis",
640
+ difficulty: "complex",
641
+ criticality: 1,
642
+ dependsOn: packages.filter((task) => task.purpose !== "analysis").map((task) => task.id),
643
+ preferredReasoningEffort: "xhigh"
644
+ });
645
+ return packages.map((task) => ({ ...task, qualityFloor: taskQualityFloor(band, task) }));
646
+ }
647
+ var SYNTHESIS_WEIGHTS = Object.freeze({ quality: 0.58, cost: 0.08, latency: 0.04, specialty: 0.08, reasoning: 0.16, risk: 0.06 });
648
+ var ROUTING_BEAM_WIDTH = 256;
649
+ var ROUTING_CANDIDATE_LIMIT = 12;
650
+ function weightsForTask(weights, task) {
651
+ return task.purpose === "synthesis" ? SYNTHESIS_WEIGHTS : OBJECTIVE_WEIGHTS[task.difficulty] ?? weights;
652
+ }
653
+ function compareText(left, right) {
654
+ const a = String(left);
655
+ const b = String(right);
656
+ return a < b ? -1 : a > b ? 1 : 0;
657
+ }
658
+ function compareRowsStable(left, right) {
659
+ return compareText(routeKey(left.provider, left.model), routeKey(right.provider, right.model));
660
+ }
661
+ function reasoningEffortRank(effort) {
662
+ const normalized2 = String(effort ?? "").trim().toLowerCase().replace(/[\s_-]+/g, "");
663
+ const aliases = { none: "off", disabled: "off", extra: "xhigh", extrahigh: "xhigh", maximum: "max" };
664
+ const canonical = aliases[normalized2] ?? normalized2;
665
+ const index = REASONING_EFFORT_ORDER.indexOf(canonical);
666
+ return index < 0 ? REASONING_EFFORT_ORDER.indexOf("medium") : index;
667
+ }
668
+ function reasoningEffortMultiplier(effort) {
669
+ const normalized2 = String(effort ?? "").trim().toLowerCase().replace(/[\s_-]+/g, "");
670
+ const aliases = { none: "off", disabled: "off", extra: "xhigh", extrahigh: "xhigh", maximum: "max" };
671
+ return REASONING_EFFORT_MULTIPLIERS[aliases[normalized2] ?? normalized2] ?? REASONING_EFFORT_MULTIPLIERS.medium;
672
+ }
673
+ function selectReasoningEffort(efforts, preferred = "medium") {
674
+ const exact = Array.isArray(efforts) ? [...new Set(efforts.map((effort) => String(effort?.id ?? effort ?? "")).filter(Boolean))] : [];
675
+ if (exact.length === 0) return void 0;
676
+ const preferredRank = reasoningEffortRank(preferred);
677
+ return exact.slice().sort((left, right) => {
678
+ const distance = Math.abs(reasoningEffortRank(left) - preferredRank) - Math.abs(reasoningEffortRank(right) - preferredRank);
679
+ if (distance !== 0) return distance;
680
+ return reasoningEffortRank(left) - reasoningEffortRank(right) || compareText(left, right);
681
+ })[0];
682
+ }
683
+ function reasoningDecision(row, task) {
684
+ const preferred = String(task.preferredReasoningEffort ?? "medium");
685
+ const efforts = Array.isArray(row.reasoningEfforts) ? row.reasoningEfforts : [];
686
+ if (efforts.length === 0) {
687
+ const knownUnsupported = row.reasoningKnown === true;
688
+ const preferredRank2 = reasoningEffortRank(preferred);
689
+ return {
690
+ reasoningEffort: void 0,
691
+ reasoningFit: knownUnsupported ? clamp(0.78 - preferredRank2 * 0.08) : 0.55,
692
+ preferredReasoningEffort: preferred,
693
+ multiplier: REASONING_EFFORT_MULTIPLIERS.medium
694
+ };
695
+ }
696
+ const preferredRank = reasoningEffortRank(preferred);
697
+ const chosen = selectReasoningEffort(efforts, preferred);
698
+ const distance = Math.abs(reasoningEffortRank(chosen) - preferredRank);
699
+ return {
700
+ reasoningEffort: chosen,
701
+ reasoningFit: clamp(1 - distance / (REASONING_EFFORT_ORDER.length - 1)),
702
+ preferredReasoningEffort: preferred,
703
+ multiplier: reasoningEffortMultiplier(chosen)
704
+ };
705
+ }
706
+ function candidateUtility(row, task, weights, maxCost, usedRoutes, cacheReadRatio = 0, cacheWriteRatio = 0) {
707
+ const quality = qualityForTask(row, task.type);
708
+ const floor = Number(task.qualityFloor ?? taskQualityFloor("complex", task));
709
+ const qualityGap = Math.max(0, floor - quality);
710
+ const duplicatePenalty = 0;
711
+ const synthesisPreference = task.purpose === "synthesis" && /deepseek[- ]?v4[- ]?pro/i.test(row.model) ? 0.025 : 0;
712
+ const reasoning = reasoningDecision(row, task);
713
+ const cost = clamp(costScore(row.pricing, maxCost, cacheReadRatio, cacheWriteRatio) / Math.sqrt(reasoning.multiplier.output));
714
+ const score = weights.quality * quality + weights.cost * cost + weights.latency * (1 - clamp(row.latency * reasoning.multiplier.latency)) + weights.specialty * specialtyForTask(row, task.type) + (weights.reasoning ?? 0) * reasoning.reasoningFit - weights.risk * row.risk - duplicatePenalty - qualityGap * (task.criticality ?? 0.75) + synthesisPreference;
715
+ return { score, floor, qualityGap, ...reasoning };
716
+ }
717
+ function taskCost(row, task, text2, complexity, cacheReadRatio = 0, cacheWriteRatio = 0) {
718
+ if (row?.pricing === null) return 0;
719
+ const decision = row === null ? null : reasoningDecision(row, task);
720
+ const tokens = taskTokenBudget(text2, { ...task, reasoningEffort: decision?.reasoningEffort }, complexity, cacheReadRatio, cacheWriteRatio);
721
+ 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;
722
+ }
723
+ function dominates(left, right, task, text2, complexity, cacheReadRatio, cacheWriteRatio) {
724
+ if (left.pricing === null || right.pricing === null) return false;
725
+ const leftReasoning = reasoningDecision(left, task);
726
+ const rightReasoning = reasoningDecision(right, task);
727
+ const leftValues = {
728
+ quality: qualityForTask(left, task.type),
729
+ cost: taskCost(left, task, text2, complexity, cacheReadRatio, cacheWriteRatio),
730
+ latency: clamp(left.latency * leftReasoning.multiplier.latency),
731
+ specialty: specialtyForTask(left, task.type),
732
+ reasoning: leftReasoning.reasoningFit,
733
+ risk: clamp(left.risk)
734
+ };
735
+ const rightValues = {
736
+ quality: qualityForTask(right, task.type),
737
+ cost: taskCost(right, task, text2, complexity, cacheReadRatio, cacheWriteRatio),
738
+ latency: clamp(right.latency * rightReasoning.multiplier.latency),
739
+ specialty: specialtyForTask(right, task.type),
740
+ reasoning: rightReasoning.reasoningFit,
741
+ risk: clamp(right.risk)
742
+ };
743
+ const noWorse = leftValues.quality >= rightValues.quality && leftValues.cost <= rightValues.cost && leftValues.latency <= rightValues.latency && leftValues.specialty >= rightValues.specialty && leftValues.reasoning >= rightValues.reasoning && leftValues.risk <= rightValues.risk;
744
+ const strictlyBetter = leftValues.quality > rightValues.quality || leftValues.cost < rightValues.cost || leftValues.latency < rightValues.latency || leftValues.specialty > rightValues.specialty || leftValues.reasoning > rightValues.reasoning || leftValues.risk < rightValues.risk;
745
+ return noWorse && strictlyBetter;
746
+ }
747
+ function candidatePool(rows, task, weights, maxCost, text2, complexity, cacheReadRatio, cacheWriteRatio) {
748
+ const eligibleRows = task.type === "vision" ? rows.filter((row) => row.inputModalities.length === 0 || row.inputModalities.includes("image")) : rows;
749
+ const floor = Number(task.qualityFloor ?? 0);
750
+ const feasible = eligibleRows.filter((row) => qualityForTask(row, task.type) >= floor);
751
+ 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);
752
+ const taskWeights = weightsForTask(weights, task);
753
+ const scored = source.map((row) => ({
754
+ row,
755
+ decision: candidateUtility(row, task, taskWeights, maxCost, /* @__PURE__ */ new Set(), cacheReadRatio, cacheWriteRatio),
756
+ cost: taskCost(row, task, text2, complexity, cacheReadRatio, cacheWriteRatio)
757
+ }));
758
+ const frontier = scored.filter((item) => !source.some((other) => other !== item.row && dominates(other, item.row, task, text2, complexity, cacheReadRatio, cacheWriteRatio)));
759
+ const essential = [
760
+ scored.slice().sort((left, right) => left.cost - right.cost || compareRowsStable(left.row, right.row))[0],
761
+ scored.slice().sort((left, right) => right.decision.score - left.decision.score || compareRowsStable(left.row, right.row))[0],
762
+ scored.slice().sort((left, right) => qualityForTask(right.row, task.type) - qualityForTask(left.row, task.type) || compareRowsStable(left.row, right.row))[0]
763
+ ].filter(Boolean);
764
+ const ordered = [...frontier, ...essential].filter((item, index, all) => all.findIndex((candidate) => candidate.row === item.row) === index).sort((left, right) => right.decision.score - left.decision.score || left.cost - right.cost || compareRowsStable(left.row, right.row)).slice(0, ROUTING_CANDIDATE_LIMIT);
765
+ return {
766
+ options: ordered,
767
+ relaxed: feasible.length === 0,
768
+ pruned: Math.max(0, eligibleRows.length - ordered.length)
769
+ };
770
+ }
771
+ function stateSignature(state) {
772
+ return state.assignments.map((assignment) => `${routeKey(assignment.row?.provider, assignment.row?.model)}@${assignment.decision?.reasoningEffort ?? "provider-default"}`).join("|");
773
+ }
774
+ function compareUtilityStates(left, right) {
775
+ return left.relaxedCount - right.relaxedCount || left.qualityShortfall - right.qualityShortfall || right.score - left.score || left.cost - right.cost || left.switches - right.switches || compareText(stateSignature(left), stateSignature(right));
776
+ }
777
+ function compareCostStates(left, right) {
778
+ return left.relaxedCount - right.relaxedCount || left.qualityShortfall - right.qualityShortfall || left.cost - right.cost || right.score - left.score || left.switches - right.switches || compareText(stateSignature(left), stateSignature(right));
779
+ }
780
+ function solveAssignments({ rows, tasks, weights, maxCost, text: text2, complexity, budget, cacheReadRatio, cacheWriteRatio, minimizeCost = false }) {
781
+ const pools = tasks.map((task) => candidatePool(rows, task, weights, maxCost, text2, complexity, cacheReadRatio, cacheWriteRatio));
782
+ if (pools.some((pool) => pool.options.length === 0)) return null;
783
+ const suffixMinimum = Array(tasks.length + 1).fill(0);
784
+ for (let index = tasks.length - 1; index >= 0; index -= 1) {
785
+ const costOptions = Number.isFinite(budget) ? pools[index].options.filter((option) => option.row.pricing !== null) : pools[index].options;
786
+ if (costOptions.length === 0) return null;
787
+ suffixMinimum[index] = suffixMinimum[index + 1] + Math.min(...costOptions.map((option) => option.cost));
788
+ }
789
+ if (Number.isFinite(budget) && suffixMinimum[0] > budget + 1e-12) return null;
790
+ let states = [{ assignments: [], routesByTask: /* @__PURE__ */ new Map(), usedRoutes: /* @__PURE__ */ new Set(), score: 0, cost: 0, switches: 0, relaxedCount: 0, qualityShortfall: 0 }];
791
+ for (let index = 0; index < tasks.length; index += 1) {
792
+ const task = tasks[index];
793
+ const pool = pools[index];
794
+ const expanded = [];
795
+ for (const state of states) {
796
+ for (const option of pool.options) {
797
+ if (Number.isFinite(budget) && option.row.pricing === null) continue;
798
+ const nextCost = state.cost + option.cost;
799
+ if (Number.isFinite(budget) && nextCost + suffixMinimum[index + 1] > budget + 1e-12) continue;
800
+ const taskWeights = weightsForTask(weights, task);
801
+ const decision = candidateUtility(option.row, task, taskWeights, maxCost, state.usedRoutes, cacheReadRatio, cacheWriteRatio);
802
+ const route = routeKey(option.row.provider, option.row.model);
803
+ const dependencySwitches = (task.dependsOn ?? []).reduce((count, dependency) => {
804
+ const dependencyRoute = state.routesByTask.get(dependency);
805
+ return count + (dependencyRoute !== void 0 && dependencyRoute !== route ? 1 : 0);
806
+ }, 0);
807
+ const handoffPenalty = dependencySwitches * 0.015;
808
+ const qualityShortfall = Math.max(0, decision.floor - qualityForTask(option.row, task.type));
809
+ const usedRoutes = new Set(state.usedRoutes);
810
+ usedRoutes.add(route);
811
+ const routesByTask = new Map(state.routesByTask);
812
+ routesByTask.set(task.id, route);
813
+ expanded.push({
814
+ assignments: [...state.assignments, { task, row: option.row, decision: { ...decision, relaxed: qualityShortfall > 0 }, estimatedCost: option.cost, handoffPenalty }],
815
+ routesByTask,
816
+ usedRoutes,
817
+ score: state.score + decision.score - handoffPenalty,
818
+ cost: nextCost,
819
+ switches: state.switches + dependencySwitches,
820
+ relaxedCount: state.relaxedCount + (qualityShortfall > 0 ? 1 : 0),
821
+ qualityShortfall: state.qualityShortfall + qualityShortfall
822
+ });
823
+ }
824
+ }
825
+ if (expanded.length === 0) return null;
826
+ expanded.sort(minimizeCost ? compareCostStates : compareUtilityStates);
827
+ states = expanded.slice(0, ROUTING_BEAM_WIDTH);
828
+ }
829
+ states.sort(minimizeCost ? compareCostStates : compareUtilityStates);
830
+ return {
831
+ ...states[0],
832
+ candidatePools: pools,
833
+ minimumFeasibleCost: suffixMinimum[0]
834
+ };
835
+ }
836
+ function buildPlan({ text: text2 = "", available = [], mode = "collective", pricing = {}, liveBench = null, liveBenchError = "", budgetUsd = 0, cacheReadRatio = 0, cacheWriteRatio = 0 } = {}) {
837
+ const firstLine = String(text2 ?? "").split(/\r?\n/u)[0].trim();
838
+ const transformOnly = /^(?:请|帮我)?(?:总结|概括|翻译|摘要|解释)(?:以下|下列|下面|这份|这些)/u.test(firstLine) && !/(?:执行|完成|实施|分配)/u.test(firstLine);
839
+ const assessed = assessComplexity(transformOnly ? firstLine : text2);
840
+ const requirements = explicitRequirements(text2);
841
+ const compound = shouldSplitRequirements(requirements);
842
+ const complexity = compound && assessed.band !== "complex" ? { value: Math.max(0.66, assessed.value), band: "complex" } : assessed;
843
+ const taskType = classifyTask(transformOnly ? firstLine : text2);
844
+ const weights = OBJECTIVE_WEIGHTS[complexity.band];
845
+ const discovered = Array.isArray(available) ? available.map((entry) => {
846
+ const rawEfforts = Array.isArray(entry.reasoningEfforts) ? entry.reasoningEfforts : [];
847
+ const reasoningEfforts = rawEfforts.map((effort) => String(effort?.id ?? effort ?? "")).filter(Boolean);
848
+ return {
849
+ provider: String(entry.provider ?? ""),
850
+ model: String(entry.model ?? ""),
851
+ reasoningEfforts: [...new Set(reasoningEfforts)],
852
+ defaultReasoningEffort: entry.defaultReasoningEffort === void 0 ? void 0 : String(entry.defaultReasoningEffort),
853
+ reasoningKnown: entry.reasoningKnown === true || entry.reasoningKnown === void 0 && Array.isArray(entry.reasoningEfforts),
854
+ quality: asScore(entry.quality),
855
+ qualitySource: entry.qualitySource === "user" ? "user" : "route",
856
+ latency: asScore(entry.latency),
857
+ risk: asScore(entry.risk),
858
+ specialties: Array.isArray(entry.specialties) ? entry.specialties.filter((item) => typeof item === "string") : null,
859
+ pricing: normalizePricing({ route: entry.pricing ?? entry.price }).route ?? null,
860
+ pricingSource: entry.pricingSource === "user" ? "user" : "route",
861
+ inputModalities: Array.isArray(entry.inputModalities) ? entry.inputModalities.map((item) => String(item).toLowerCase()) : []
862
+ };
863
+ }) : [];
864
+ const rows = [];
865
+ const normalizedPrices = normalizePricing(pricing);
866
+ for (const route of discovered) {
867
+ if (!route.provider || !route.model) continue;
868
+ const catalog = modelMetadata(route.model);
869
+ const metadata = {
870
+ ...catalog ?? { id: route.model, aliases: [route.model], specialties: [] },
871
+ ...route.quality === void 0 ? {} : { quality: route.quality },
872
+ ...route.latency === void 0 ? {} : { latency: route.latency },
873
+ ...route.risk === void 0 ? {} : { risk: route.risk },
874
+ ...route.specialties === null ? {} : { specialties: route.specialties }
875
+ };
876
+ const live = liveBenchRow(liveBench, route.model);
877
+ const liveScores = live?.scores ?? {};
878
+ const liveOverall = asScore(live?.overall);
879
+ const quality = asScore(liveScores?.[taskType]) ?? liveOverall ?? asScore(metadata.quality) ?? 0;
880
+ const qualitySource = liveOverall !== void 0 || asScore(liveScores?.[taskType]) !== void 0 ? "livebench" : route.quality !== void 0 ? route.qualitySource : catalog ? "catalog-heuristic" : "unknown";
881
+ const userPrice = normalizedPrices[normalize(`${route.provider}/${route.model}`)] ?? normalizedPrices[normalize(route.model)];
882
+ const pricingRow = userPrice ?? route.pricing ?? null;
883
+ const pricingSource = userPrice ? "user" : route.pricing ? route.pricingSource : "unknown";
884
+ const specialty = specialtyMatch(metadata, taskType, liveScores);
885
+ rows.push({
886
+ provider: route.provider,
887
+ model: route.model,
888
+ metadata,
889
+ quality,
890
+ qualitySource,
891
+ pricingSource,
892
+ liveScores,
893
+ liveOverall,
894
+ latency: metadata.latency ?? 0.5,
895
+ risk: metadata.risk ?? 0.2,
896
+ specialty,
897
+ reasoningEfforts: route.reasoningEfforts,
898
+ defaultReasoningEffort: route.defaultReasoningEffort,
899
+ reasoningKnown: route.reasoningKnown,
900
+ inputModalities: route.inputModalities,
901
+ pricing: pricingRow,
902
+ score: 0,
903
+ estimatedCost: pricingRow === null ? null : estimateCost({
904
+ id: route.model,
905
+ costIn: pricingRow.input,
906
+ costOut: pricingRow.output,
907
+ cacheRead: pricingRow.cacheRead,
908
+ cacheWrite: pricingRow.cacheWrite
909
+ }, text2, 900, {}, cacheReadRatio, cacheWriteRatio)
910
+ });
911
+ }
912
+ const maxCost = Math.max(1, ...rows.filter((row) => row.pricing !== null).map((row) => {
913
+ const effective = effectivePricing(row.pricing, cacheReadRatio, cacheWriteRatio);
914
+ return effective.input + effective.output;
915
+ }));
916
+ const taskNodes = taskPackages(taskType, text2, complexity.band);
917
+ const unassignableTasks = taskNodes.filter((task) => task.type === "vision" && !rows.some((row) => row.inputModalities.length === 0 || row.inputModalities.includes("image"))).map((task) => task.id);
918
+ const budget = Number(budgetUsd);
919
+ const utilityPlan = solveAssignments({
920
+ rows,
921
+ tasks: taskNodes,
922
+ weights,
923
+ maxCost,
924
+ text: text2,
925
+ complexity: complexity.band,
926
+ budget: Number.POSITIVE_INFINITY,
927
+ cacheReadRatio,
928
+ cacheWriteRatio
929
+ });
930
+ const budgetPlan = budget > 0 ? solveAssignments({
931
+ rows,
932
+ tasks: taskNodes,
933
+ weights,
934
+ maxCost,
935
+ text: text2,
936
+ complexity: complexity.band,
937
+ budget,
938
+ cacheReadRatio,
939
+ cacheWriteRatio
940
+ }) : null;
941
+ const minimumCostPlan = budget > 0 && budgetPlan === null ? solveAssignments({
942
+ rows,
943
+ tasks: taskNodes,
944
+ weights,
945
+ maxCost,
946
+ text: text2,
947
+ complexity: complexity.band,
948
+ budget: Number.POSITIVE_INFINITY,
949
+ cacheReadRatio,
950
+ cacheWriteRatio,
951
+ minimizeCost: true
952
+ }) : null;
953
+ const optimized = budget > 0 ? budgetPlan ?? minimumCostPlan ?? utilityPlan : utilityPlan;
954
+ const assignments = optimized?.assignments ?? [];
955
+ const usedRoutes = optimized?.usedRoutes ?? /* @__PURE__ */ new Set();
956
+ const constraintRelaxed = (optimized?.relaxedCount ?? 0) > 0;
957
+ for (const row of rows) {
958
+ row.score = candidateUtility(row, taskNodes[0] ?? { type: taskType, qualityFloor: QUALITY_FLOORS[complexity.band] }, weights, maxCost, /* @__PURE__ */ new Set(), cacheReadRatio, cacheWriteRatio).score;
959
+ }
960
+ rows.sort((left, right) => right.score - left.score || compareRowsStable(left, right));
961
+ const selectedAssignment = assignments[0];
962
+ const selected = selectedAssignment?.row ?? (unassignableTasks.length > 0 ? null : rows[0] ?? null);
963
+ const synthesizerAssignment = assignments.at(-1);
964
+ const synthesizer = synthesizerAssignment?.row ?? (unassignableTasks.length > 0 ? null : rows.find((row) => /deepseek/i.test(row.model)) ?? rows[0]);
965
+ const subtasks = assignments.map(({ task, row, decision }) => ({
966
+ id: task.id,
967
+ name: task.name,
968
+ ...task.objective ? { objective: task.objective } : {},
969
+ type: task.type,
970
+ difficulty: task.difficulty,
971
+ recommended: row?.model ?? "\u5F85\u53D1\u73B0\u6A21\u578B",
972
+ recommendedProvider: row?.provider ?? "",
973
+ qualitySource: row?.qualitySource ?? "unknown",
974
+ pricingSource: row?.pricingSource ?? "unknown",
975
+ recommendedReasoningEffort: decision?.reasoningEffort,
976
+ preferredReasoningEffort: decision?.preferredReasoningEffort ?? task.preferredReasoningEffort,
977
+ reasoningFit: Number(Number(decision?.reasoningFit ?? 0).toFixed(3)),
978
+ purpose: task.purpose,
979
+ criticality: task.criticality,
980
+ qualityFloor: Number(task.qualityFloor.toFixed(3)),
981
+ dependsOn: [...task.dependsOn ?? []]
982
+ }));
983
+ const costBreakdown = assignments.map(({ task, row, decision, estimatedCost, handoffPenalty }, index) => {
984
+ const tokens = taskTokenBudget(text2, { ...task, reasoningEffort: decision?.reasoningEffort }, complexity.band, cacheReadRatio, cacheWriteRatio);
985
+ const taskEstimate = estimatedCost ?? taskCost(row, task, text2, complexity.band, cacheReadRatio, cacheWriteRatio);
986
+ return {
987
+ stage: index + 1,
988
+ purpose: task.purpose,
989
+ difficulty: task.difficulty,
990
+ model: row?.model ?? "\u5F85\u53D1\u73B0\u6A21\u578B",
991
+ provider: row?.provider ?? "",
992
+ reasoningEffort: decision?.reasoningEffort,
993
+ preferredReasoningEffort: decision?.preferredReasoningEffort,
994
+ reasoningFit: Number(Number(decision?.reasoningFit ?? 0).toFixed(3)),
995
+ reasoningOutputMultiplier: Number(Number(decision?.multiplier?.output ?? 1).toFixed(2)),
996
+ inputTokens: tokens.inputTokens,
997
+ cacheReadTokens: tokens.cacheReadTokens,
998
+ cacheWriteTokens: tokens.cacheWriteTokens,
999
+ outputTokens: tokens.outputTokens,
1000
+ estimatedCost: row?.pricing === null || row === null ? null : Number(taskEstimate.toFixed(6)),
1001
+ quality: row?.qualitySource === "unknown" || row === null ? null : Number(qualityForTask(row, task.type).toFixed(3)),
1002
+ qualitySource: row?.qualitySource ?? "unknown",
1003
+ pricingSource: row?.pricingSource ?? "unknown",
1004
+ handoffPenalty: Number(Number(handoffPenalty ?? 0).toFixed(3))
1005
+ };
1006
+ });
1007
+ const pricingComplete = assignments.length > 0 && assignments.every(({ row }) => row?.pricing !== null);
1008
+ const totalEstimate = pricingComplete ? costBreakdown.reduce((sum, row) => sum + row.estimatedCost, 0) : null;
1009
+ const baselineRows = assignments.map(({ task }) => {
1010
+ const strongest = rows.reduce((best, row) => qualityForTask(row, task.type) > (best === null ? -1 : qualityForTask(best, task.type)) ? row : best, null);
1011
+ return { task, strongest };
1012
+ });
1013
+ 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;
1014
+ const qualityEvidenceComplete = assignments.length > 0 && assignments.every(({ row }) => ["livebench", "route", "user"].includes(row?.qualitySource)) && baselineRows.every(({ strongest }) => ["livebench", "route", "user"].includes(strongest?.qualitySource));
1015
+ const budgetExceeded = Number(budgetUsd) > 0 && totalEstimate !== null ? totalEstimate > Number(budgetUsd) : null;
1016
+ const savings = baselineCost === null || totalEstimate === null || !qualityEvidenceComplete ? null : baselineCost <= 0 ? 0 : clamp((baselineCost - totalEstimate) / baselineCost);
1017
+ const paretoPruned = (optimized?.candidatePools ?? []).reduce((sum, pool) => sum + pool.pruned, 0);
1018
+ const minimumFeasibleCost = rows.every((row) => row.pricing !== null) ? optimized?.minimumFeasibleCost ?? minimumCostPlan?.cost ?? 0 : null;
1019
+ 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`;
1020
+ return {
1021
+ mode,
1022
+ complexity: { value: Number(complexity.value.toFixed(3)), band: complexity.band },
1023
+ compound,
1024
+ unassignableTasks,
1025
+ taskType,
1026
+ taskTypes: [...new Set(taskNodes.map((task) => task.type).filter((type) => type !== "reasoning"))],
1027
+ objectiveWeights: weights,
1028
+ candidates: rows.slice(0, 8).map((row) => {
1029
+ 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);
1030
+ 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 };
1031
+ }),
1032
+ 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 },
1033
+ subtasks,
1034
+ synthesizer: synthesizer == null ? null : { provider: synthesizer.provider, model: synthesizer.model, reasoningEffort: synthesizerAssignment?.decision?.reasoningEffort },
1035
+ estimatedCost: totalEstimate === null ? null : Number(totalEstimate.toFixed(6)),
1036
+ costBreakdown,
1037
+ optimization: {
1038
+ solver: "pareto-pruned quality-constrained beam assignment",
1039
+ qualityFloor: QUALITY_FLOORS[complexity.band],
1040
+ budgetUsd: Number(Number(budgetUsd) > 0 ? Number(budgetUsd) : 0),
1041
+ cacheReadRatio: normalizedCacheRatios(cacheReadRatio, cacheWriteRatio).read,
1042
+ cacheWriteRatio: normalizedCacheRatios(cacheReadRatio, cacheWriteRatio).write,
1043
+ budgetExceeded,
1044
+ constraintRelaxed,
1045
+ pricingComplete,
1046
+ qualityEvidenceComplete,
1047
+ baselineAllStrongCost: baselineCost === null ? null : Number(baselineCost.toFixed(6)),
1048
+ estimatedSavings: savings === null ? null : Number(savings.toFixed(4)),
1049
+ distinctRoutes: usedRoutes.size,
1050
+ handoffCount: optimized?.switches ?? 0,
1051
+ paretoPruned,
1052
+ beamWidth: ROUTING_BEAM_WIDTH,
1053
+ budgetFeasible: budget <= 0 ? pricingComplete ? true : null : budgetPlan !== null,
1054
+ minimumFeasibleCost: minimumFeasibleCost === null ? null : Number(Number(minimumFeasibleCost).toFixed(6)),
1055
+ 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 || "") }
1056
+ },
1057
+ reason,
1058
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString()
1059
+ };
1060
+ }
1061
+
1062
+ // .dsh-plugin/shared/harness-plan.mjs
1063
+ var clean = (value) => typeof value === "string" ? value.trim() : "";
1064
+ function channelForProvider(provider, installedToolIds = [], runnableToolIds = []) {
1065
+ const tool = toolForProvider(provider);
1066
+ if (!tool || tool.unsupported) {
1067
+ return { kind: "harness-llm", detail: "\u901A\u8FC7\u5B98\u65B9\u6A21\u578B\u76EE\u5F55 API \u8C03\u7528\u3002" };
1068
+ }
1069
+ const installed = Array.isArray(installedToolIds) && installedToolIds.includes(tool.id);
1070
+ const runnable = installed && Array.isArray(runnableToolIds) && runnableToolIds.includes(tool.id);
1071
+ return runnable ? { kind: "official-cli", tool: tool.id, label: tool.label, detail: tool.id === "zcode" ? "ZCode \u5DF2\u5B89\u88C5\uFF0C\u63D2\u4EF6\u53EF\u8C03\u7528\u5176\u5B98\u65B9\u7F16\u7A0B\u4EE3\u7406\uFF1B3.14.3 \u7684 CLI \u4F7F\u7528\u81EA\u8EAB\u914D\u7F6E\u7684\u9ED8\u8BA4\u6A21\u578B\uFF0C\u4E0D\u80FD\u4FDD\u8BC1\u4E0E Harness \u5EFA\u8BAE\u6A21\u578B\u4E00\u81F4\u3002" : `${tool.label} \u5DF2\u5B89\u88C5\uFF0C\u63D2\u4EF6\u53EF\u6258\u7BA1\u8C03\u7528\u5176\u5B98\u65B9 CLI\uFF1BHarness \u6A21\u578B\u76EE\u5F55\u4E0E\u5382\u5546 CLI \u540D\u79F0\u53EF\u80FD\u4E0D\u540C\uFF0C\u56E2\u961F\u65E0\u6CD5\u786E\u8BA4\u6620\u5C04\u65F6\u4F7F\u7528 CLI \u9ED8\u8BA4\u6A21\u578B\uFF0C\u5B9E\u9645\u6A21\u578B\u4ECD\u987B\u6838\u5BF9\u8FD0\u884C\u8BB0\u5F55\u3002` } : {
1072
+ kind: "harness-llm",
1073
+ tool: tool.id,
1074
+ label: tool.label,
1075
+ detail: installed ? `${tool.label} \u5DF2\u5B89\u88C5\uFF0C\u4F46\u5F53\u524D\u5E73\u53F0\u7F3A\u5C11\u7ECF\u6838\u9A8C\u7684\u6258\u7BA1\u6267\u884C\u9002\u914D\u5668\uFF1B\u5B9E\u9645\u8C03\u7528\u4F7F\u7528\u5B98\u65B9\u6A21\u578B\u76EE\u5F55 API\u3002` : `${tool.label} \u672A\u5B89\u88C5\uFF1B\u5B9E\u9645\u8C03\u7528\u4F7F\u7528\u5B98\u65B9\u6A21\u578B\u76EE\u5F55 API\u3002\u53EF\u5728\u5DE5\u4F5C\u53F0\u4E00\u952E\u5B89\u88C5\uFF0C\u6216\u8FD0\u884C /tools install ${tool.id}\u3002`
1076
+ };
1077
+ }
1078
+ function annotate(channel) {
1079
+ return {
1080
+ executionChannel: channel.kind,
1081
+ ...channel.tool ? { channelTool: channel.tool } : {},
1082
+ ...channel.label ? { channelLabel: channel.label } : {},
1083
+ channelDetail: channel.detail
1084
+ };
1085
+ }
1086
+ function createPlanFromRoutes(task, availableRoutes, {
1087
+ mode = "single",
1088
+ budgetUsd = 0,
1089
+ installedToolIds = [],
1090
+ runnableToolIds = [],
1091
+ pricing = {},
1092
+ liveBench = null,
1093
+ cacheReadRatio = 0,
1094
+ cacheWriteRatio = 0
1095
+ } = {}) {
1096
+ const taskText = clean(task);
1097
+ if (!taskText) throw new Error("task must contain text");
1098
+ const selectedMode = mode === "team" ? "team" : "single";
1099
+ const routes = Array.isArray(availableRoutes) ? availableRoutes : [];
1100
+ const installedIds = Array.isArray(installedToolIds) ? installedToolIds.filter(Boolean) : [];
1101
+ const channelCache = /* @__PURE__ */ new Map();
1102
+ const channelOf = (provider) => {
1103
+ const key = String(provider ?? "");
1104
+ if (!channelCache.has(key)) channelCache.set(key, channelForProvider(key, installedIds, runnableToolIds));
1105
+ return channelCache.get(key);
1106
+ };
1107
+ const needsImage = detectTaskTypes(taskText).includes("vision");
1108
+ const plan = buildPlan({
1109
+ text: taskText,
1110
+ available: routes,
1111
+ mode: selectedMode,
1112
+ budgetUsd: Math.max(0, Number.isFinite(budgetUsd) ? budgetUsd : 0),
1113
+ pricing,
1114
+ liveBench,
1115
+ cacheReadRatio,
1116
+ cacheWriteRatio
1117
+ });
1118
+ const selectedChannel = plan.selected ? channelOf(plan.selected.provider) : null;
1119
+ return {
1120
+ ...plan,
1121
+ contractVersion: 2,
1122
+ availableRoutes: routes,
1123
+ ...selectedChannel ? annotate(selectedChannel) : {},
1124
+ 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",
1125
+ 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",
1126
+ 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",
1127
+ 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,
1128
+ 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",
1129
+ team: {
1130
+ requested: selectedMode === "team",
1131
+ recommended: selectedMode === "team" && plan.complexity.band === "complex" && plan.subtasks.length > 1,
1132
+ 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",
1133
+ workPackages: plan.subtasks.map((item, index) => ({
1134
+ id: item.id,
1135
+ name: item.name,
1136
+ ...item.objective ? { objective: item.objective } : {},
1137
+ type: item.type,
1138
+ purpose: item.purpose,
1139
+ difficulty: item.difficulty,
1140
+ qualitySource: item.qualitySource,
1141
+ pricingSource: item.pricingSource,
1142
+ dependsOn: item.dependsOn,
1143
+ recommendedProvider: item.recommendedProvider,
1144
+ recommendedModel: item.recommended,
1145
+ estimatedCost: plan.costBreakdown[index]?.estimatedCost ?? null,
1146
+ ...item.recommendedReasoningEffort ? { recommendedReasoningEffort: item.recommendedReasoningEffort } : {},
1147
+ ...annotate(channelOf(item.recommendedProvider)),
1148
+ 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"]
1149
+ }))
1150
+ }
1151
+ };
1152
+ }
1153
+
1154
+ // .dsh-plugin/client/catalog.mjs
1155
+ var clean2 = (value) => typeof value === "string" ? value.trim() : "";
1156
+ function routesFromModelCatalog(catalog) {
1157
+ const groups = Array.isArray(catalog?.groups) ? catalog.groups : [];
1158
+ const routable = new Set(Array.isArray(catalog?.routableProviders) ? catalog.routableProviders : []);
1159
+ const routes = [];
1160
+ const seen = /* @__PURE__ */ new Set();
1161
+ for (const group of groups) {
1162
+ const provider = clean2(group?.id);
1163
+ if (!provider || !routable.has(provider)) continue;
1164
+ for (const entry of Array.isArray(group.models) ? group.models : []) {
1165
+ const model = clean2(entry?.id);
1166
+ if (!model) continue;
1167
+ const key = `${provider}\0${model}`;
1168
+ if (seen.has(key)) continue;
1169
+ seen.add(key);
1170
+ const reasoning = entry?.reasoning;
1171
+ const efforts = Array.isArray(reasoning?.efforts) ? [...new Set(reasoning.efforts.map((item) => clean2(item?.id ?? item)).filter(Boolean))] : [];
1172
+ routes.push({
1173
+ provider,
1174
+ providerName: clean2(group.name) || provider,
1175
+ model,
1176
+ name: clean2(entry.name) || model,
1177
+ reasoningKnown: reasoning !== void 0 && reasoning !== null,
1178
+ reasoningEfforts: efforts,
1179
+ ...clean2(reasoning?.defaultEffort) ? { defaultReasoningEffort: clean2(reasoning.defaultEffort) } : {},
1180
+ // modelCatalog deliberately omits modalities; unknown means the Host
1181
+ // must still validate image capability before a real request.
1182
+ inputModalities: []
1183
+ });
1184
+ }
1185
+ }
1186
+ routes.sort((left, right) => `${left.provider}/${left.model}`.localeCompare(`${right.provider}/${right.model}`));
1187
+ return routes;
1188
+ }
1189
+ function createWorkspacePlan(task, catalog, options = {}) {
1190
+ const routes = applyModelProfiles(
1191
+ routesFromModelCatalog(catalog),
1192
+ parseModelProfilesJson(options.modelProfilesJson ?? "[]")
1193
+ );
1194
+ return createPlanFromRoutes(task, routes, options);
1195
+ }
1196
+
1197
+ // .dsh-plugin/client/model-profile-editor.jsx
1198
+ var import_react = __toESM(require("react"), 1);
1199
+
1200
+ // .dsh-plugin/client/model-profile-editor-state.mjs
1201
+ var PROFILE_SPECIALTY_HINT = "code, math, research, summarization, writing, vision, reasoning";
1202
+ var profileRouteKey = (route) => `${String(route?.provider ?? "")}\0${String(route?.model ?? "")}`;
1203
+ var field = (value) => String(value ?? "").trim();
1204
+ function nonnegativeField(value, label, max = 1e6) {
1205
+ const raw = field(value);
1206
+ if (!raw) return null;
1207
+ const parsed = Number(raw);
1208
+ if (!Number.isFinite(parsed) || parsed < 0 || parsed > max) throw new Error(`${label} \u5FC5\u987B\u662F 0 \u5230 ${max} \u7684\u6570\u5B57\u3002`);
1209
+ return parsed;
1210
+ }
1211
+ function profileDraft(profile) {
1212
+ return {
1213
+ quality: profile?.quality === void 0 ? "" : String(Number((profile.quality * 100).toFixed(4))),
1214
+ input: profile?.pricing?.input === void 0 ? "" : String(profile.pricing.input),
1215
+ output: profile?.pricing?.output === void 0 ? "" : String(profile.pricing.output),
1216
+ cacheRead: profile?.pricing?.cacheRead === void 0 ? "" : String(profile.pricing.cacheRead),
1217
+ cacheWrite: profile?.pricing?.cacheWrite === void 0 ? "" : String(profile.pricing.cacheWrite),
1218
+ specialties: Array.isArray(profile?.specialties) ? profile.specialties.join(", ") : "",
1219
+ cliModel: profile?.cliModel ?? ""
1220
+ };
1221
+ }
1222
+ function profileFromDraft(route, draft) {
1223
+ const provider = field(route?.provider);
1224
+ const model = field(route?.model);
1225
+ 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");
1226
+ const profile = { provider, model };
1227
+ const quality = nonnegativeField(draft?.quality, "\u8D28\u91CF\u8BC4\u5206", 100);
1228
+ if (quality !== null) profile.quality = quality;
1229
+ const input = nonnegativeField(draft?.input, "\u8F93\u5165\u5355\u4EF7");
1230
+ const output = nonnegativeField(draft?.output, "\u8F93\u51FA\u5355\u4EF7");
1231
+ const cacheRead = nonnegativeField(draft?.cacheRead, "\u7F13\u5B58\u8BFB\u53D6\u5355\u4EF7");
1232
+ const cacheWrite = nonnegativeField(draft?.cacheWrite, "\u7F13\u5B58\u5199\u5165\u5355\u4EF7");
1233
+ 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");
1234
+ 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");
1235
+ if (input !== null) {
1236
+ profile.pricing = { input, output, currency: "USD" };
1237
+ if (cacheRead !== null) profile.pricing.cacheRead = cacheRead;
1238
+ if (cacheWrite !== null) profile.pricing.cacheWrite = cacheWrite;
1239
+ }
1240
+ const rawSpecialties = field(draft?.specialties);
1241
+ if (rawSpecialties) {
1242
+ const specialties = [...new Set(rawSpecialties.split(/[,,\s]+/u).filter(Boolean))];
1243
+ if (specialties.length > 16 || specialties.some((item) => !/^[a-z][a-z0-9-]{0,39}$/.test(item))) {
1244
+ 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");
1245
+ }
1246
+ profile.specialties = specialties;
1247
+ }
1248
+ const cliModel = field(draft?.cliModel);
1249
+ if (cliModel) {
1250
+ 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");
1251
+ profile.cliModel = cliModel;
1252
+ }
1253
+ return profile;
1254
+ }
1255
+ function updateProfileJson(rawJson, route, draft) {
1256
+ const profiles = parseModelProfilesJson(rawJson ?? "[]");
1257
+ const candidate = draft === null ? null : profileFromDraft(route, draft);
1258
+ const selected = profileRouteKey(route);
1259
+ const others = profiles.filter((profile) => profileRouteKey(profile) !== selected).map((profile) => ({
1260
+ ...profile,
1261
+ ...profile.quality === void 0 ? {} : { quality: Number((profile.quality * 100).toFixed(6)) }
1262
+ }));
1263
+ const next = candidate && Object.keys(candidate).length > 2 ? [...others, candidate] : others;
1264
+ const json = JSON.stringify(next, null, 2);
1265
+ parseModelProfilesJson(json);
1266
+ return json;
1267
+ }
1268
+
1269
+ // .dsh-plugin/client/model-profile-editor.jsx
1270
+ var messageOf = (error) => typeof error?.message === "string" ? error.message : "\u6A21\u578B\u914D\u7F6E\u4FDD\u5B58\u5931\u8D25\u3002";
1271
+ function ModelProfileEditor({ routes, settingsScope, onSaved }) {
1272
+ const [snapshot, setSnapshot] = import_react.default.useState(() => settingsScope.getSnapshot());
1273
+ const [selected, setSelected] = import_react.default.useState("");
1274
+ const [drafts, setDrafts] = import_react.default.useState({});
1275
+ const [saving, setSaving] = import_react.default.useState(false);
1276
+ const [notice, setNotice] = import_react.default.useState(null);
1277
+ import_react.default.useEffect(() => settingsScope.subscribe(() => setSnapshot(settingsScope.getSnapshot())), [settingsScope]);
1278
+ const route = routes.find((item) => profileRouteKey(item) === selected) ?? routes[0];
1279
+ const routeKey2 = route ? profileRouteKey(route) : "";
1280
+ const rawJson = snapshot.value?.modelProfilesJson ?? "[]";
1281
+ let profiles = [];
1282
+ let loadError = "";
1283
+ try {
1284
+ profiles = parseModelProfilesJson(rawJson);
1285
+ } catch (error) {
1286
+ loadError = messageOf(error);
1287
+ }
1288
+ const saved = profiles.find((item) => profileRouteKey(item) === routeKey2);
1289
+ const pending = drafts[routeKey2];
1290
+ const draft = pending?.fields ?? profileDraft(saved);
1291
+ const writable = snapshot.status === "ready" && snapshot.writable === true && !saving && !loadError;
1292
+ const unmatched = profiles.filter((item) => !routes.some((route2) => profileRouteKey(route2) === profileRouteKey(item))).length;
1293
+ const edit = (name, value) => {
1294
+ if (!route || !writable) return;
1295
+ setDrafts((previous) => {
1296
+ const previousEntry = previous[routeKey2] ?? { fields: profileDraft(saved), baseRevision: snapshot.revision };
1297
+ return { ...previous, [routeKey2]: { ...previousEntry, fields: { ...previousEntry.fields, [name]: value } } };
1298
+ });
1299
+ setNotice(null);
1300
+ };
1301
+ const write = async (remove) => {
1302
+ if (!route || !writable) return;
1303
+ const revision = pending?.baseRevision ?? snapshot.revision;
1304
+ if (revision !== snapshot.revision) {
1305
+ 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" });
1306
+ return;
1307
+ }
1308
+ let nextJson;
1309
+ try {
1310
+ nextJson = updateProfileJson(rawJson, route, remove ? null : draft);
1311
+ } catch (error) {
1312
+ setNotice({ tone: "error", text: messageOf(error) });
1313
+ return;
1314
+ }
1315
+ setSaving(true);
1316
+ setNotice(null);
1317
+ try {
1318
+ const accepted = await settingsScope.mutate([{ op: "set", path: ["modelProfilesJson"], value: nextJson }], revision);
1319
+ 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");
1320
+ setDrafts((previous) => {
1321
+ const next = { ...previous };
1322
+ delete next[routeKey2];
1323
+ return next;
1324
+ });
1325
+ 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" });
1326
+ onSaved?.();
1327
+ } catch (error) {
1328
+ setNotice({ tone: "error", text: messageOf(error) });
1329
+ } finally {
1330
+ setSaving(false);
1331
+ }
1332
+ };
1333
+ const reload = () => {
1334
+ setDrafts((previous) => {
1335
+ const next = { ...previous };
1336
+ delete next[routeKey2];
1337
+ return next;
1338
+ });
1339
+ setNotice(null);
1340
+ };
1341
+ 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) => {
1342
+ setSelected(event.target.value);
1343
+ setNotice(null);
1344
+ } }, 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: () => {
1345
+ void write(false);
1346
+ } }, 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: () => {
1347
+ void write(true);
1348
+ } }, "\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"))));
1349
+ }
1350
+
1351
+ // .dsh-plugin/client/tool-install-state.mjs
1352
+ function stableVersionOrder(left, right) {
1353
+ const parse = (value) => /^([0-9]+)\.([0-9]+)\.([0-9]+)$/.exec(String(value ?? ""));
1354
+ const current = parse(left);
1355
+ const target = parse(right);
1356
+ if (!current || !target) return null;
1357
+ for (let index = 1; index <= 3; index += 1) {
1358
+ const delta = Number(current[index]) - Number(target[index]);
1359
+ if (delta) return Math.sign(delta);
1360
+ }
1361
+ return 0;
1362
+ }
1363
+ function toolInstallAction({ tool, probe, readiness, job, probeStatus }) {
1364
+ const running = job?.status === "running";
1365
+ const versionOrder = probe?.installed ? stableVersionOrder(probe.version, tool.version) : null;
1366
+ const currentAndReady = versionOrder === 0 && readiness?.ready === true;
1367
+ const repairable = versionOrder === 0 && readiness?.ready === false;
1368
+ const newerOrUncertain = probe?.installed && (versionOrder === null || versionOrder > 0);
1369
+ 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";
1370
+ return {
1371
+ label,
1372
+ disabled: probeStatus !== "ready" || running || currentAndReady || newerOrUncertain
1373
+ };
1374
+ }
1375
+
1376
+ // .dsh-plugin/client/router-main.css
1377
+ 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";
1378
+
1379
+ // .dsh-plugin/client/router-main.jsx
1380
+ var money = (value) => value === null || value === void 0 ? "\u4EF7\u683C\u5F85\u914D\u7F6E" : `$${Number(value).toFixed(4)}`;
1381
+ var text = (value) => typeof value === "string" ? value.trim() : "";
1382
+ function RouterPanelIcon({ size = 20, active = false }) {
1383
+ 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" }));
1384
+ }
1385
+ function RouteList({ routes, query }) {
1386
+ const filtered = routes.filter((route) => {
1387
+ const haystack = `${route.providerName} ${route.provider} ${route.name} ${route.model}`.toLowerCase();
1388
+ return haystack.includes(query.toLowerCase());
1389
+ });
1390
+ 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");
1391
+ 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"))));
1392
+ }
1393
+ function ChannelBadge({ item }) {
1394
+ if (!item?.executionChannel) return null;
1395
+ const official = item.executionChannel === "official-cli";
1396
+ 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");
1397
+ }
1398
+ function PlanResults({ plan }) {
1399
+ const selected = plan.selected;
1400
+ 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 || "")));
1401
+ }
1402
+ var remoteError = (response, fallback) => text(response?.error?.message) || text(response?.value?.error) || fallback;
1403
+ function probeLabel(probe) {
1404
+ if (!probe) return "\u5C1A\u672A\u68C0\u6D4B";
1405
+ if (probe.installed) return `\u5DF2\u5B89\u88C5${probe.version ? ` \xB7 ${probe.version}` : ""}`;
1406
+ if (probe.status === "not-installed") return "\u672A\u5B89\u88C5";
1407
+ if (probe.status === "probe-timeout") return "\u68C0\u6D4B\u8D85\u65F6";
1408
+ if (probe.status === "probe-failed") return "\u68C0\u6D4B\u5931\u8D25";
1409
+ return probe.detail || "\u672A\u5B89\u88C5";
1410
+ }
1411
+ function OfficialToolsCard({ listOfficialTools, installOfficialTool, cancelOfficialToolInstall, officialToolInstallStatus, onProbes }) {
1412
+ const [probeState, setProbeState] = import_react2.default.useState({ status: "loading", probes: [], capabilities: [], readiness: [], error: "" });
1413
+ const [jobs, setJobs] = import_react2.default.useState({});
1414
+ const [rowErrors, setRowErrors] = import_react2.default.useState({});
1415
+ const mounted = import_react2.default.useRef(false);
1416
+ const request = import_react2.default.useRef(0);
1417
+ const submitting = import_react2.default.useRef(/* @__PURE__ */ new Set());
1418
+ const polling = import_react2.default.useRef(/* @__PURE__ */ new Set());
1419
+ const refresh = async () => {
1420
+ const current = ++request.current;
1421
+ setProbeState((previous) => ({ ...previous, status: "loading", error: "" }));
1422
+ try {
1423
+ if (typeof listOfficialTools !== "function") throw new Error("\u5B98\u65B9\u5DE5\u5177\u5B89\u88C5\u6865\u5C1A\u672A\u52A0\u8F7D\u3002");
1424
+ const response = await listOfficialTools();
1425
+ if (!mounted.current || current !== request.current) return;
1426
+ if (!response?.ok) throw new Error(remoteError(response, "\u65E0\u6CD5\u68C0\u6D4B\u5B98\u65B9\u5DE5\u5177\u3002"));
1427
+ const probes = Array.isArray(response.value?.tools) ? response.value.tools : [];
1428
+ const capabilities = Array.isArray(response.value?.executionCapabilities) ? response.value.executionCapabilities : [];
1429
+ const readiness = Array.isArray(response.value?.executionReadiness) ? response.value.executionReadiness : [];
1430
+ setProbeState({ status: "ready", probes, capabilities, readiness, error: "" });
1431
+ onProbes({ probes, capabilities, readiness });
1432
+ } catch (error) {
1433
+ if (!mounted.current || current !== request.current) return;
1434
+ setProbeState({ status: "error", probes: [], capabilities: [], readiness: [], error: text(error?.message) || "\u65E0\u6CD5\u68C0\u6D4B\u5B98\u65B9\u5DE5\u5177\u3002" });
1435
+ onProbes({ probes: [], capabilities: [], readiness: [] });
1436
+ }
1437
+ };
1438
+ import_react2.default.useEffect(() => {
1439
+ mounted.current = true;
1440
+ void refresh();
1441
+ if (typeof officialToolInstallStatus === "function") {
1442
+ void Promise.all(OFFICIAL_TOOLS.map(async (tool) => {
1443
+ try {
1444
+ const response = await officialToolInstallStatus(tool.id);
1445
+ return response?.ok && response.value?.job ? [tool.id, response.value.job] : null;
1446
+ } catch {
1447
+ return null;
1448
+ }
1449
+ })).then((entries) => {
1450
+ if (mounted.current) setJobs((previous) => ({ ...Object.fromEntries(entries.filter(Boolean)), ...previous }));
1451
+ });
1452
+ }
1453
+ return () => {
1454
+ mounted.current = false;
1455
+ request.current += 1;
1456
+ };
1457
+ }, []);
1458
+ import_react2.default.useEffect(() => {
1459
+ const active = Object.values(jobs).filter((job) => job?.status === "running").map((job) => job.tool);
1460
+ if (active.length === 0 || typeof officialToolInstallStatus !== "function") return void 0;
1461
+ let listening = true;
1462
+ const poll = async (id2) => {
1463
+ if (polling.current.has(id2)) return;
1464
+ polling.current.add(id2);
1465
+ try {
1466
+ const response = await officialToolInstallStatus(id2);
1467
+ if (!listening || !mounted.current) return;
1468
+ if (!response?.ok) throw new Error(remoteError(response, "\u65E0\u6CD5\u83B7\u53D6\u5B89\u88C5\u8FDB\u5EA6\u3002"));
1469
+ const job = response.value?.job;
1470
+ if (!job) throw new Error("\u5B89\u88C5\u4EFB\u52A1\u72B6\u6001\u6682\u4E0D\u53EF\u7528\u3002");
1471
+ setJobs((previous) => ({ ...previous, [id2]: job }));
1472
+ setRowErrors((previous) => ({ ...previous, [id2]: "" }));
1473
+ if (job.status !== "running") void refresh();
1474
+ } catch (error) {
1475
+ 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" }));
1476
+ } finally {
1477
+ polling.current.delete(id2);
1478
+ }
1479
+ };
1480
+ const timer = setInterval(() => active.forEach((id2) => {
1481
+ void poll(id2);
1482
+ }), 1500);
1483
+ return () => {
1484
+ listening = false;
1485
+ clearInterval(timer);
1486
+ };
1487
+ }, [jobs, officialToolInstallStatus]);
1488
+ const install = async (id2) => {
1489
+ const tool = OFFICIAL_TOOLS.find((item) => item.id === id2);
1490
+ if (!tool || tool.unsupported || submitting.current.has(id2) || jobs[id2]?.status === "running") return;
1491
+ submitting.current.add(id2);
1492
+ setRowErrors((previous) => ({ ...previous, [id2]: "" }));
1493
+ setJobs((previous) => ({ ...previous, [id2]: { tool: id2, status: "running", outputTail: [] } }));
1494
+ try {
1495
+ if (typeof installOfficialTool !== "function") throw new Error("\u5B98\u65B9\u5DE5\u5177\u5B89\u88C5\u6865\u5C1A\u672A\u52A0\u8F7D\u3002");
1496
+ const response = await installOfficialTool(id2);
1497
+ if (!mounted.current) return;
1498
+ if (!response?.ok || !response.value?.accepted || !response.value?.job) throw new Error(remoteError(response, "\u5B89\u88C5\u4EFB\u52A1\u672A\u88AB\u63A5\u53D7\u3002"));
1499
+ setJobs((previous) => ({ ...previous, [id2]: response.value.job }));
1500
+ } catch (error) {
1501
+ if (mounted.current) {
1502
+ setJobs((previous) => ({ ...previous, [id2]: { tool: id2, status: "failed", error: text(error?.message) || "\u5B89\u88C5\u542F\u52A8\u5931\u8D25\u3002" } }));
1503
+ setRowErrors((previous) => ({ ...previous, [id2]: text(error?.message) || "\u5B89\u88C5\u542F\u52A8\u5931\u8D25\u3002" }));
1504
+ }
1505
+ } finally {
1506
+ submitting.current.delete(id2);
1507
+ }
1508
+ };
1509
+ const cancel = async (id2) => {
1510
+ if (typeof cancelOfficialToolInstall !== "function" || jobs[id2]?.status !== "running" || jobs[id2]?.cancelRequested) return;
1511
+ setRowErrors((previous) => ({ ...previous, [id2]: "" }));
1512
+ try {
1513
+ const response = await cancelOfficialToolInstall(id2);
1514
+ if (!mounted.current) return;
1515
+ if (!response?.ok || !response.value?.accepted || !response.value?.job) throw new Error(remoteError(response, "\u53D6\u6D88\u8BF7\u6C42\u672A\u88AB\u63A5\u53D7\u3002"));
1516
+ setJobs((previous) => ({ ...previous, [id2]: response.value.job }));
1517
+ if (response.value.job.status !== "running") void refresh();
1518
+ } catch (error) {
1519
+ if (mounted.current) setRowErrors((previous) => ({ ...previous, [id2]: text(error?.message) || "\u65E0\u6CD5\u53D6\u6D88\u5B89\u88C5\u3002" }));
1520
+ }
1521
+ };
1522
+ const byId = Object.fromEntries(probeState.probes.map((probe) => [probe.id, probe]));
1523
+ const capabilitiesById = Object.fromEntries(probeState.capabilities.map((item) => [item.id, item]));
1524
+ const readinessById = Object.fromEntries(probeState.readiness.map((item) => [item.id, item]));
1525
+ 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: () => {
1526
+ void refresh();
1527
+ } }, "\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) => {
1528
+ const command = installCommandLine(tool);
1529
+ const probe = byId[tool.id];
1530
+ const capability = capabilitiesById[tool.id];
1531
+ const readiness = readinessById[tool.id];
1532
+ const job = jobs[tool.id];
1533
+ const running = job?.status === "running";
1534
+ const action = toolInstallAction({ tool, probe, readiness, job, probeStatus: probeState.status });
1535
+ const verified = job?.status === "succeeded" && job.postInstallProbe?.installed === true;
1536
+ 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);
1537
+ 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: () => {
1538
+ void install(tool.id);
1539
+ } }, action.label), running && /* @__PURE__ */ import_react2.default.createElement("button", { className: "mr-button mr-button-secondary mr-tool-button", type: "button", disabled: job.cancelRequested, onClick: () => {
1540
+ void cancel(tool.id);
1541
+ } }, job.cancelRequested ? "\u6B63\u5728\u53D6\u6D88\u2026" : "\u53D6\u6D88\u5B89\u88C5")));
1542
+ })), /* @__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")));
1543
+ }
1544
+ function RouterMainPage({ loadCatalog, settingsScope, listOfficialTools, installOfficialTool, cancelOfficialToolInstall, officialToolInstallStatus }) {
1545
+ const [catalogState, setCatalogState] = import_react2.default.useState({ status: "loading", catalog: null, error: "" });
1546
+ const [task, setTask] = import_react2.default.useState("");
1547
+ const [mode, setMode] = import_react2.default.useState("single");
1548
+ const [budget, setBudget] = import_react2.default.useState(() => String(settingsScope.getSnapshot().value?.budgetUsd ?? 0));
1549
+ const [query, setQuery] = import_react2.default.useState("");
1550
+ const [plan, setPlan] = import_react2.default.useState(null);
1551
+ const [planError, setPlanError] = import_react2.default.useState("");
1552
+ const [toolProbes, setToolProbes] = import_react2.default.useState(null);
1553
+ const budgetEdited = import_react2.default.useRef(false);
1554
+ const budgetValue = import_react2.default.useRef(budget);
1555
+ const mounted = import_react2.default.useRef(false);
1556
+ const catalogRequest = import_react2.default.useRef(0);
1557
+ import_react2.default.useEffect(() => {
1558
+ const syncBudget = () => {
1559
+ if (budgetEdited.current) return;
1560
+ const next = String(settingsScope.getSnapshot().value?.budgetUsd ?? 0);
1561
+ if (next !== budgetValue.current) {
1562
+ budgetValue.current = next;
1563
+ setBudget(next);
1564
+ setPlan(null);
1565
+ setPlanError("");
1566
+ }
1567
+ };
1568
+ syncBudget();
1569
+ return settingsScope.subscribe(syncBudget);
1570
+ }, [settingsScope]);
1571
+ import_react2.default.useEffect(() => {
1572
+ mounted.current = true;
1573
+ const request = ++catalogRequest.current;
1574
+ Promise.resolve().then(loadCatalog).then((response) => {
1575
+ if (!mounted.current || request !== catalogRequest.current) return;
1576
+ if (response?.ok) setCatalogState({ status: "ready", catalog: response.value, error: "" });
1577
+ else setCatalogState({ status: "error", catalog: null, error: text(response?.error?.message) || "\u6A21\u578B\u76EE\u5F55\u8BFB\u53D6\u5931\u8D25" });
1578
+ }).catch((error) => {
1579
+ if (mounted.current && request === catalogRequest.current) setCatalogState({ status: "error", catalog: null, error: text(error?.message) || "\u6A21\u578B\u76EE\u5F55\u8BFB\u53D6\u5931\u8D25" });
1580
+ });
1581
+ return () => {
1582
+ mounted.current = false;
1583
+ catalogRequest.current += 1;
1584
+ };
1585
+ }, []);
1586
+ const routes = routesFromModelCatalog(catalogState.catalog);
1587
+ const providerCount = new Set(routes.map((route) => route.provider)).size;
1588
+ const refresh = async () => {
1589
+ const request = ++catalogRequest.current;
1590
+ setCatalogState({ status: "loading", catalog: null, error: "" });
1591
+ setPlan(null);
1592
+ try {
1593
+ const response = await loadCatalog();
1594
+ if (!mounted.current || request !== catalogRequest.current) return;
1595
+ if (response?.ok) setCatalogState({ status: "ready", catalog: response.value, error: "" });
1596
+ else setCatalogState({ status: "error", catalog: null, error: text(response?.error?.message) || "\u6A21\u578B\u76EE\u5F55\u8BFB\u53D6\u5931\u8D25" });
1597
+ } catch (error) {
1598
+ if (mounted.current && request === catalogRequest.current) setCatalogState({ status: "error", catalog: null, error: text(error?.message) || "\u6A21\u578B\u76EE\u5F55\u8BFB\u53D6\u5931\u8D25" });
1599
+ }
1600
+ };
1601
+ const invalidatePlan = () => {
1602
+ setPlan(null);
1603
+ setPlanError("");
1604
+ };
1605
+ const handleToolProbes = import_react2.default.useCallback((snapshot) => {
1606
+ setToolProbes(snapshot);
1607
+ setPlan(null);
1608
+ setPlanError("");
1609
+ }, []);
1610
+ const generate = () => {
1611
+ setPlanError("");
1612
+ try {
1613
+ if (!text(task)) throw new Error("\u8BF7\u5148\u63CF\u8FF0\u4EFB\u52A1\u3002");
1614
+ if (routes.length === 0) throw new Error("\u8BF7\u5148\u5728\u5B98\u65B9\u201C\u6A21\u578B\u201D\u9875\u914D\u7F6E\u81F3\u5C11\u4E00\u6761\u6A21\u578B\u8DEF\u7EBF\u3002");
1615
+ const parsedBudget = Number(budget);
1616
+ if (!Number.isFinite(parsedBudget) || parsedBudget < 0) throw new Error("\u9884\u7B97\u5FC5\u987B\u662F\u4E0D\u5C0F\u4E8E 0 \u7684\u6570\u5B57\u3002");
1617
+ setPlan(createWorkspacePlan(task, catalogState.catalog, {
1618
+ mode,
1619
+ budgetUsd: parsedBudget,
1620
+ modelProfilesJson: settingsScope.getSnapshot().value?.modelProfilesJson ?? "[]",
1621
+ installedToolIds: (toolProbes?.probes ?? []).filter((probe) => probe.installed).map((probe) => probe.id),
1622
+ runnableToolIds: (toolProbes?.readiness ?? []).filter((item) => item.ready).map((item) => item.id)
1623
+ }));
1624
+ } catch (error) {
1625
+ setPlan(null);
1626
+ setPlanError(text(error?.message) || "\u65E0\u6CD5\u751F\u6210\u8DEF\u7531\u5EFA\u8BAE\u3002");
1627
+ }
1628
+ };
1629
+ 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) => {
1630
+ setTask(event.target.value);
1631
+ invalidatePlan();
1632
+ }, 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: () => {
1633
+ setMode("single");
1634
+ invalidatePlan();
1635
+ } }, "\u5355\u4EFB\u52A1"), /* @__PURE__ */ import_react2.default.createElement("button", { type: "button", "aria-pressed": mode === "team", onClick: () => {
1636
+ setMode("team");
1637
+ invalidatePlan();
1638
+ } }, "\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) => {
1639
+ budgetEdited.current = true;
1640
+ budgetValue.current = event.target.value;
1641
+ setBudget(event.target.value);
1642
+ invalidatePlan();
1643
+ } }))), /* @__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")));
1644
+ }
1645
+
1646
+ // .dsh-plugin/shared/official-tools-remote.mjs
1647
+ var OFFICIAL_TOOLS_REMOTE_PACKAGE = "@ljwei-stak/model-router-galgame";
1648
+ var OFFICIAL_TOOLS_REMOTE_NAMESPACE = "modelRouterOfficialTools";
1649
+ function strictCodec(typeSymbol, parse) {
1650
+ return Object.freeze({ mode: "strict", typeSymbol, create: () => ({ parse }) });
1651
+ }
1652
+ function plainObject(value, subject) {
1653
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
1654
+ throw new TypeError(`${subject} must be an object`);
1655
+ }
1656
+ return value;
1657
+ }
1658
+ var toolIdCodec = strictCodec(`${OFFICIAL_TOOLS_REMOTE_PACKAGE}#OfficialToolId`, (value) => {
1659
+ if (typeof value !== "string" || !getOfficialTool(value)) {
1660
+ throw new TypeError("toolId must name a fixed official tool");
1661
+ }
1662
+ return value;
1663
+ });
1664
+ var listResultCodec = strictCodec(`${OFFICIAL_TOOLS_REMOTE_PACKAGE}#OfficialToolList`, (value) => {
1665
+ const result = plainObject(value, "tool list result");
1666
+ if (!Array.isArray(result.tools)) throw new TypeError("tool list result needs a tools array");
1667
+ return result;
1668
+ });
1669
+ var installResultCodec = strictCodec(`${OFFICIAL_TOOLS_REMOTE_PACKAGE}#OfficialToolInstall`, (value) => {
1670
+ const result = plainObject(value, "install result");
1671
+ if (typeof result.accepted !== "boolean") throw new TypeError("install result needs accepted");
1672
+ if (result.accepted && !result.job) throw new TypeError("accepted install needs a job");
1673
+ if (!result.accepted && typeof result.error !== "string") throw new TypeError("refused install needs an error");
1674
+ return result;
1675
+ });
1676
+ var statusResultCodec = strictCodec(`${OFFICIAL_TOOLS_REMOTE_PACKAGE}#OfficialToolStatus`, (value) => {
1677
+ const result = plainObject(value, "install status result");
1678
+ if (result.job !== null && (typeof result.job !== "object" || Array.isArray(result.job))) {
1679
+ throw new TypeError("install status result needs a job or null");
1680
+ }
1681
+ return result;
1682
+ });
1683
+ function descriptor(method, parameters, result) {
1684
+ return Object.freeze({
1685
+ id: `${OFFICIAL_TOOLS_REMOTE_PACKAGE}#${OFFICIAL_TOOLS_REMOTE_NAMESPACE}/${method}`,
1686
+ service: OFFICIAL_TOOLS_REMOTE_NAMESPACE,
1687
+ namespace: OFFICIAL_TOOLS_REMOTE_NAMESPACE,
1688
+ method,
1689
+ invocation: { kind: "direct" },
1690
+ parameters,
1691
+ result
1692
+ });
1693
+ }
1694
+ var toolIdParameter = Object.freeze({
1695
+ name: "toolId",
1696
+ wire: "toolId",
1697
+ source: "json",
1698
+ codec: toolIdCodec
1699
+ });
1700
+ var OFFICIAL_TOOLS_REMOTE_DESCRIPTORS = Object.freeze([
1701
+ descriptor("list", [], listResultCodec),
1702
+ descriptor("installTool", [toolIdParameter], installResultCodec),
1703
+ descriptor("cancel", [toolIdParameter], installResultCodec),
1704
+ descriptor("status", [toolIdParameter], statusResultCodec)
1705
+ ]);
1706
+ var OFFICIAL_TOOLS_CLIENT_REMOTE = Object.freeze({
1707
+ package: OFFICIAL_TOOLS_REMOTE_PACKAGE,
1708
+ descriptors: OFFICIAL_TOOLS_REMOTE_DESCRIPTORS
1709
+ });
1710
+ var OFFICIAL_TOOLS_HOST_TYPERT = Object.freeze({
1711
+ package: OFFICIAL_TOOLS_REMOTE_PACKAGE,
1712
+ face: "host",
1713
+ schemas: [],
1714
+ invocations: OFFICIAL_TOOLS_REMOTE_DESCRIPTORS,
1715
+ model: { services: [], events: [], objects: [] }
1716
+ });
1717
+
1718
+ // .dsh-plugin/client/official-harness.jsx
1719
+ var import_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
1720
+ var ROUTER_NAMESPACE = "model-router-galgame";
1721
+ var ROUTER_PACKAGE = "@ljwei-stak/model-router-galgame";
1722
+ var ROUTER_PANEL = "model-router-galgame";
1723
+ var inject = ["remote"];
1724
+ var UI_INJECT = [
1725
+ "slots",
1726
+ "configForms",
1727
+ "remote",
1728
+ "remote.session",
1729
+ `remote.${OFFICIAL_TOOLS_REMOTE_NAMESPACE}`,
1730
+ "layout"
1731
+ ];
1732
+ var FORM_LABELS = Object.freeze({
1733
+ unavailable: "\u8BE5\u63D2\u4EF6\u5F53\u524D\u672A\u52A0\u8F7D\uFF0C\u6682\u65F6\u65E0\u6CD5\u914D\u7F6E\u3002",
1734
+ readOnly: "\u5F53\u524D\u914D\u7F6E\u4E3A\u53EA\u8BFB\u3002",
1735
+ saveFailed: "\u8BBE\u7F6E\u6CA1\u6709\u88AB\u4FDD\u5B58\uFF0C\u8BF7\u68C0\u67E5\u8F93\u5165\u540E\u91CD\u8BD5\u3002",
1736
+ save: "\u4FDD\u5B58\u8BBE\u7F6E",
1737
+ saving: "\u4FDD\u5B58\u4E2D\u2026"
1738
+ });
1739
+ var FIELD_COPY = Object.freeze({
1740
+ overridden: "\u5DF2\u8986\u76D6",
1741
+ reset: "\u6062\u590D\u9ED8\u8BA4",
1742
+ invalidNumber: "\u8BF7\u8F93\u5165\u5141\u8BB8\u8303\u56F4\u5185\u7684\u6570\u5B57\uFF0C\u6216\u7559\u7A7A\u6062\u590D\u9ED8\u8BA4\u3002"
1743
+ });
1744
+ function boundedNumberField(field2, { minimum = 0, maximum = Number.MAX_SAFE_INTEGER, integer = false } = {}) {
1745
+ const numeric = (0, import_dsh_client_ui_primitives.settingsNumberField)(field2);
1746
+ return {
1747
+ ...numeric,
1748
+ parse: (text2) => {
1749
+ const write = numeric.parse(text2);
1750
+ if (write?.kind !== "set") return write;
1751
+ const value = write.value;
1752
+ if (typeof value !== "number" || value < minimum || value > maximum) return void 0;
1753
+ if (integer && (!Number.isSafeInteger(value) || Object.is(value, -0))) return void 0;
1754
+ return write;
1755
+ }
1756
+ };
1757
+ }
1758
+ function modelProfilesField() {
1759
+ const field2 = (0, import_dsh_client_ui_primitives.settingsTextField)("modelProfilesJson");
1760
+ return {
1761
+ ...field2,
1762
+ parse: (text2) => {
1763
+ try {
1764
+ parseModelProfilesJson(text2);
1765
+ return field2.parse(text2);
1766
+ } catch {
1767
+ return void 0;
1768
+ }
1769
+ }
1770
+ };
1771
+ }
1772
+ var RouterSettingsCardController = class {
1773
+ constructor(scope) {
1774
+ this.form = new import_dsh_client_ui_primitives.SettingsFormModel(scope, [
1775
+ boundedNumberField("budgetUsd", { minimum: 0 }),
1776
+ boundedNumberField("maxConsultOutputChars", { minimum: 500, maximum: 5e4, integer: true }),
1777
+ modelProfilesField()
1778
+ ]);
1779
+ this.store = this.form.bind(() => ({
1780
+ ...this.form.shell(),
1781
+ budgetUsd: this.form.field("budgetUsd"),
1782
+ maxConsultOutputChars: this.form.field("maxConsultOutputChars"),
1783
+ modelProfilesJson: this.form.field("modelProfilesJson")
1784
+ }));
1785
+ }
1786
+ /** Supply the snapshot hook and staged form actions to the Plugins slot. */
1787
+ inject() {
1788
+ return { hooks: { modelRouterSettings: this.store }, ...this.form.actions() };
1789
+ }
1790
+ /** Release the live Host-form subscription when the client plugin unloads. */
1791
+ dispose() {
1792
+ this.form.dispose();
1793
+ }
1794
+ };
1795
+ function RouterSettingsCard(props) {
1796
+ const state = props.useModelRouterSettings((snapshot) => snapshot);
1797
+ if (props.view === "summary") {
1798
+ 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";
1799
+ }
1800
+ const disabled = !state.writable || state.saving;
1801
+ 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(
1802
+ import_dsh_client_ui_primitives.SettingsValueField,
1803
+ {
1804
+ id: "model-router-budget-usd",
1805
+ label: "\u5355\u6B21\u8BA1\u5212\u9884\u7B97\u4E0A\u9650\uFF08USD\uFF09",
1806
+ 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",
1807
+ help: {
1808
+ label: "\u9884\u7B97\u8BF4\u660E",
1809
+ 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"
1810
+ },
1811
+ disabled,
1812
+ ...state.budgetUsd,
1813
+ overriddenLabel: FIELD_COPY.overridden,
1814
+ resetLabel: FIELD_COPY.reset,
1815
+ invalidLabel: FIELD_COPY.invalidNumber,
1816
+ onEdit: (text2) => {
1817
+ props.edit("budgetUsd", text2);
1818
+ },
1819
+ onReset: () => {
1820
+ props.resetField("budgetUsd");
1821
+ }
1822
+ }
1823
+ ), /* @__PURE__ */ import_react3.default.createElement(
1824
+ import_dsh_client_ui_primitives.SettingsValueField,
1825
+ {
1826
+ id: "model-router-consult-output",
1827
+ label: "\u8DE8\u6A21\u578B\u54A8\u8BE2\u6700\u5927\u8F93\u51FA\u5B57\u7B26\u6570",
1828
+ hint: "\u9650\u5236 model_router_consult \u8FD4\u56DE\u7684\u5B57\u7B26\u6570\uFF0C\u9632\u6B62\u5355\u6B21\u54A8\u8BE2\u5360\u7528\u8FC7\u591A\u4E0A\u4E0B\u6587\u3002",
1829
+ numeric: true,
1830
+ disabled,
1831
+ ...state.maxConsultOutputChars,
1832
+ overriddenLabel: FIELD_COPY.overridden,
1833
+ resetLabel: FIELD_COPY.reset,
1834
+ invalidLabel: FIELD_COPY.invalidNumber,
1835
+ onEdit: (text2) => {
1836
+ props.edit("maxConsultOutputChars", text2);
1837
+ },
1838
+ onReset: () => {
1839
+ props.resetField("maxConsultOutputChars");
1840
+ }
1841
+ }
1842
+ ), /* @__PURE__ */ import_react3.default.createElement("div", { style: styles.profileEditor }, /* @__PURE__ */ import_react3.default.createElement("label", { htmlFor: "model-router-profiles-json", style: styles.profileLabel }, "\u6A21\u578B\u4EF7\u683C\u4E0E\u80FD\u529B\u914D\u7F6E\uFF08JSON\uFF09"), /* @__PURE__ */ import_react3.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_react3.default.createElement(
1843
+ "textarea",
1844
+ {
1845
+ id: "model-router-profiles-json",
1846
+ value: state.modelProfilesJson.text,
1847
+ disabled,
1848
+ "aria-invalid": state.modelProfilesJson.invalid,
1849
+ onChange: (event) => props.edit("modelProfilesJson", event.target.value),
1850
+ spellCheck: false,
1851
+ style: styles.profileTextarea
1852
+ }
1853
+ ), state.modelProfilesJson.invalid && /* @__PURE__ */ import_react3.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_react3.default.createElement("details", { style: styles.profileExample }, /* @__PURE__ */ import_react3.default.createElement("summary", null, "\u67E5\u770B\u914D\u7F6E\u683C\u5F0F"), /* @__PURE__ */ import_react3.default.createElement("pre", null, `[
1854
+ {
1855
+ "provider": "\u6A21\u578B\u76EE\u5F55\u4E2D\u7684\u4F9B\u5E94\u5546 ID",
1856
+ "model": "\u6A21\u578B\u76EE\u5F55\u4E2D\u7684\u6A21\u578B ID",
1857
+ "quality": 80,
1858
+ "pricing": { "input": 0.2, "output": 0.8 },
1859
+ "specialties": ["code"],
1860
+ "cliModel": "\u5382\u5546 CLI \u4F7F\u7528\u7684\u6A21\u578B\u540D\uFF08\u53EF\u9009\uFF09"
1861
+ }
1862
+ ]`)), /* @__PURE__ */ import_react3.default.createElement("button", { type: "button", disabled, onClick: () => props.resetField("modelProfilesJson") }, "\u6062\u590D\u9ED8\u8BA4\u914D\u7F6E")), /* @__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")));
1863
+ }
1864
+ function OpenRouterWorkspace({ subject, openPanel }) {
1865
+ if (subject?.kind !== "bundle" || subject.pkg?.name !== ROUTER_PACKAGE) return null;
1866
+ 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");
1867
+ }
1868
+ function registerUi(ctx) {
1869
+ const officialToolsRemote = ctx.remote[OFFICIAL_TOOLS_REMOTE_NAMESPACE];
1870
+ const settingsScope = ctx.configForms.get(ROUTER_NAMESPACE);
1871
+ const card = new RouterSettingsCardController(settingsScope);
1872
+ ctx.effect(() => () => {
1873
+ card.dispose();
1874
+ }, "model-router-galgame: settings form subscription");
1875
+ ctx.effect(() => ctx.configForms.whileServed([ROUTER_NAMESPACE], () => ctx.slots.inject("plugins.bundle.config", () => ctx.slots.register({
1876
+ name: "plugins.bundle.config",
1877
+ key: ROUTER_PACKAGE,
1878
+ inject: () => card.inject()
1879
+ }, RouterSettingsCard))), "model-router-galgame: installed bundle settings");
1880
+ ctx.effect(() => ctx.configForms.whileServed([ROUTER_NAMESPACE], () => ctx.slots.inject("main", () => ctx.slots.register({
1881
+ name: "main",
1882
+ key: ROUTER_PANEL,
1883
+ inject: () => ({
1884
+ loadCatalog: () => ctx.remote.session.modelCatalog(),
1885
+ settingsScope,
1886
+ listOfficialTools: () => officialToolsRemote.list(),
1887
+ installOfficialTool: (toolId) => officialToolsRemote.installTool(toolId),
1888
+ cancelOfficialToolInstall: (toolId) => officialToolsRemote.cancel(toolId),
1889
+ officialToolInstallStatus: (toolId) => officialToolsRemote.status(toolId)
1890
+ })
1891
+ }, RouterMainPage))), "model-router-galgame: main workspace");
1892
+ ctx.effect(() => ctx.configForms.whileServed([ROUTER_NAMESPACE], () => ctx.slots.inject("sidebar.panellist", () => ctx.slots.register({
1893
+ name: "sidebar.panellist",
1894
+ id: ROUTER_PANEL,
1895
+ order: 30,
1896
+ label: "\u6A21\u578B\u8DEF\u7531"
1897
+ }, RouterPanelIcon))), "model-router-galgame: sidebar entry");
1898
+ ctx.effect(() => ctx.configForms.whileServed([ROUTER_NAMESPACE], () => ctx.slots.inject("plugins.detail.actions", () => ctx.slots.register({
1899
+ name: "plugins.detail.actions",
1900
+ id: "model-router-open-workspace",
1901
+ order: 30,
1902
+ inject: () => ({ openPanel: () => ctx.layout.selectPanel(ROUTER_PANEL) })
1903
+ }, OpenRouterWorkspace))), "model-router-galgame: bundle open action");
1904
+ }
1905
+ async function apply(ctx) {
1906
+ const disposeRemote = await ctx.remote.$mount(OFFICIAL_TOOLS_CLIENT_REMOTE);
1907
+ const ui = ctx.inject(UI_INJECT, registerUi);
1908
+ try {
1909
+ await ui;
1910
+ } catch (error) {
1911
+ await ui.dispose();
1912
+ await disposeRemote();
1913
+ throw error;
1914
+ }
1915
+ return async () => {
1916
+ await ui.dispose();
1917
+ await disposeRemote();
1918
+ };
1919
+ }
1920
+ var styles = Object.freeze({
1921
+ titleLine: { display: "flex", alignItems: "center", flexWrap: "wrap", gap: "8px" },
1922
+ notice: {
1923
+ marginTop: "4px",
1924
+ padding: "12px",
1925
+ border: "1px solid var(--dsw-alias-border-l1)",
1926
+ borderRadius: "8px",
1927
+ background: "var(--dsw-alias-markdown-code-block)"
1928
+ },
1929
+ noticeText: {
1930
+ margin: "7px 0 0",
1931
+ color: "var(--dsw-alias-label-secondary)",
1932
+ fontSize: "13px",
1933
+ lineHeight: "20px"
1934
+ },
1935
+ profileEditor: { display: "grid", gap: "8px", marginTop: "14px" },
1936
+ profileLabel: { fontSize: "13px", fontWeight: 600 },
1937
+ profileTextarea: { width: "100%", minHeight: "150px", padding: "10px", fontFamily: "monospace", fontSize: "12px", borderRadius: "7px", border: "1px solid var(--dsw-alias-border-l1)", boxSizing: "border-box" },
1938
+ profileError: { color: "var(--dsw-alias-label-danger)", fontSize: "12px", margin: 0 },
1939
+ profileExample: { fontSize: "12px", whiteSpace: "pre-wrap" }
1940
+ });
1941
+ return module.exports;
1942
+ },
1943
+ });