dsh-opendesign-corouter 0.1.1 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.js CHANGED
@@ -52,13 +52,13 @@ import { randomUUID as randomUUID2, createHash } from "node:crypto";
52
52
  import { credentialKey, credentialRef } from "@deepseek-ai/dsh-credentials";
53
53
  import { launchEnvironmentOf } from "@deepseek-ai/dsh-launch-environment";
54
54
  import { Remote, RemoteError, TypertRemoteService } from "@deepseek-ai/dsh-typert-protocol";
55
+ import { LlmError as LlmError11 } from "@deepseek-ai/dsh-llm";
55
56
 
56
57
  // src/adapter.ts
57
58
  import {
58
59
  LlmAdapter,
59
- LlmError as LlmError3,
60
+ LlmError as LlmError10,
60
61
  ReasoningEffortId,
61
- attributionHeaders,
62
62
  assertUsableApiKey
63
63
  } from "@deepseek-ai/dsh-llm";
64
64
 
@@ -210,28 +210,102 @@ var MODELS = [
210
210
  { id: "qwen3.7-max", name: "Qwen3.7 Max", group: "paid" }
211
211
  ];
212
212
  var BY_ID = new Map(MODELS.map((model) => [model.id, model]));
213
- function modelsFor(group2) {
214
- return MODELS.filter((model) => model.group === group2);
215
- }
216
213
  function findModel(id) {
217
214
  return BY_ID.get(id);
218
215
  }
219
216
 
220
217
  // src/config.ts
221
218
  import Schema from "@deepseek-ai/schemastery";
219
+
220
+ // src/protocol-options.ts
221
+ var API_PROTOCOL_IDS = ["chat-completions", "responses", "messages"];
222
+ function openAiBaseURL(baseURL) {
223
+ const base = baseURL.replace(/\/+$/u, "");
224
+ return new URL(base).pathname === "/" ? `${base}/v1` : base;
225
+ }
226
+ function inferenceEndpoint(baseURL, protocol) {
227
+ const suffix = protocol === "responses" ? "/responses" : protocol === "messages" ? "/messages" : "/chat/completions";
228
+ return baseURL.trim() === "" ? suffix : `${openAiBaseURL(baseURL.trim())}${suffix}`;
229
+ }
230
+
231
+ // src/model-settings.ts
232
+ var RESERVED_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
233
+ function parseModelSettingsMap(value) {
234
+ if (value === null || typeof value !== "object" || Array.isArray(value)) throw new Error("\u6A21\u578B\u8BBE\u7F6E\u5FC5\u987B\u662F\u5BF9\u8C61");
235
+ const keys = Object.keys(value);
236
+ if (keys.length > 512) throw new Error("\u6A21\u578B\u8BBE\u7F6E\u6700\u591A\u5141\u8BB8 512 \u4E2A\u6A21\u578B");
237
+ const fields = /* @__PURE__ */ new Set(["enabled", "temperature", "topP", "maxTokens", "contextWindow", "reasoningEffort"]);
238
+ const efforts = ["none", "minimal", "low", "medium", "high", "xhigh", "ultra", "max"];
239
+ const out = {};
240
+ for (const id of keys) {
241
+ if (["__proto__", "constructor", "prototype"].includes(id)) throw new Error("\u6A21\u578B\u8BBE\u7F6E\u4E0D\u5141\u8BB8\u4FDD\u7559\u5B57\u6BB5\u540D");
242
+ if (!/^[A-Za-z0-9._:/-]{1,128}$/u.test(id)) throw new Error("\u6A21\u578B ID \u5FC5\u987B\u662F 1\u2013128 \u4F4D\u5B57\u6BCD\u3001\u6570\u5B57\u6216 . _ : / -");
243
+ const entry = value[id];
244
+ if (entry === null || typeof entry !== "object" || Array.isArray(entry)) throw new Error("\u6BCF\u4E2A\u6A21\u578B\u7684\u8BBE\u7F6E\u5FC5\u987B\u662F\u5BF9\u8C61");
245
+ const settings = entry;
246
+ for (const field of Object.keys(settings)) {
247
+ if (!fields.has(field)) throw new Error("\u6A21\u578B\u8BBE\u7F6E\u542B\u6709\u4E0D\u652F\u6301\u7684\u5B57\u6BB5");
248
+ }
249
+ const parsed = {};
250
+ if (settings.enabled !== void 0) {
251
+ if (typeof settings.enabled !== "boolean") throw new Error("\u6A21\u578B\u8BBE\u7F6E\u7684 enabled \u5FC5\u987B\u662F\u5E03\u5C14\u503C");
252
+ parsed.enabled = settings.enabled;
253
+ }
254
+ for (const [field, max] of [["temperature", 2], ["topP", 1]]) {
255
+ const item = settings[field];
256
+ if (item === void 0) continue;
257
+ if (typeof item !== "number" || !Number.isFinite(item) || item < 0 || item > max) throw new Error(`\u6A21\u578B\u8BBE\u7F6E\u7684 ${field} \u5FC5\u987B\u662F 0 \u5230 ${max} \u4E4B\u95F4\u7684\u6709\u9650\u6570\u5B57`);
258
+ parsed[field] = item;
259
+ }
260
+ for (const [field, max] of [["maxTokens", 1e7], ["contextWindow", 1e8]]) {
261
+ const item = settings[field];
262
+ if (item === void 0) continue;
263
+ if (typeof item !== "number" || !Number.isSafeInteger(item) || item < 1 || item > max) throw new Error(`\u6A21\u578B\u8BBE\u7F6E\u7684 ${field} \u5FC5\u987B\u662F 1 \u5230 ${max} \u4E4B\u95F4\u7684\u6574\u6570`);
264
+ parsed[field] = item;
265
+ }
266
+ if (settings.reasoningEffort !== void 0) {
267
+ if (typeof settings.reasoningEffort !== "string" || !efforts.includes(settings.reasoningEffort)) throw new Error("\u6A21\u578B\u8BBE\u7F6E\u7684 reasoningEffort \u4E0D\u5728\u5141\u8BB8\u7684\u53D6\u503C\u5185");
268
+ parsed.reasoningEffort = settings.reasoningEffort;
269
+ }
270
+ if (parsed.maxTokens !== void 0 && parsed.contextWindow !== void 0 && parsed.maxTokens >= parsed.contextWindow) throw new Error("\u6A21\u578B\u8BBE\u7F6E\u7684 maxTokens \u5FC5\u987B\u5C0F\u4E8E contextWindow");
271
+ out[id] = parsed;
272
+ }
273
+ return out;
274
+ }
275
+ function applyModelSettings(row, settings) {
276
+ const { contextWindow, maxTokens } = settings ?? {};
277
+ if (contextWindow === void 0 && maxTokens === void 0) return { ...row };
278
+ if (contextWindow !== void 0) parseModelSettingsMap({ model: { contextWindow } });
279
+ if (maxTokens !== void 0) parseModelSettingsMap({ model: { maxTokens } });
280
+ return {
281
+ ...row,
282
+ ...contextWindow === void 0 ? {} : { contextWindow },
283
+ ...maxTokens === void 0 ? {} : { maxOutputTokens: maxTokens }
284
+ };
285
+ }
286
+ function isModelEnabled(id, settings = {}) {
287
+ if (RESERVED_KEYS.has(id) || !Object.hasOwn(settings, id)) return true;
288
+ return settings[id]?.enabled !== false;
289
+ }
290
+
291
+ // src/config.ts
222
292
  var SETTINGS_NS = "llm-opendesign-corouter";
223
293
  var PLAN_PROVIDER = "opendesign-corouter-plan";
224
294
  var PAID_PROVIDER = "opendesign-corouter-paid";
225
295
  var DEFAULT_BASE_URL = "https://amr-link.open-design.ai/v1";
226
296
  var Config = Schema.object({
227
- apiKeyEnv: Schema.string().pattern(/^[A-Za-z_][A-Za-z0-9_]*$/u).default("COROUTER_API_KEY").description("CoRouter API Key \u7684\u73AF\u5883\u53D8\u91CF\u6216 DSH \u51ED\u636E\u5F15\u7528\u540D").volatile(),
228
- baseURL: Schema.transform(Schema.string(), (value) => assertBaseURL(value)).default(DEFAULT_BASE_URL).description("OpenAI \u517C\u5BB9\u63A8\u7406\u5730\u5740\uFF1B\u4E0D\u7528\u4E8E\u63A7\u5236\u53F0\u767B\u5F55\u6216\u989D\u5EA6\u67E5\u8BE2").volatile(),
297
+ apiKeyEnv: Schema.string().max(128).pattern(/^[A-Za-z_][A-Za-z0-9_]*$/u).default("COROUTER_API_KEY").description("CoRouter API Key \u7684\u73AF\u5883\u53D8\u91CF\u6216 DSH \u51ED\u636E\u5F15\u7528\u540D").volatile(),
298
+ baseURL: Schema.transform(Schema.string(), assertBaseURL).default(DEFAULT_BASE_URL).description("CoRouter \u7F51\u5173\u5730\u5740\uFF1B\u4E0D\u7528\u4E8E\u63A7\u5236\u53F0\u767B\u5F55\u6216\u989D\u5EA6\u67E5\u8BE2").volatile(),
299
+ apiProtocol: Schema.union(API_PROTOCOL_IDS).default("chat-completions").description("\u63A8\u7406 API \u534F\u8BAE\uFF1B\u6A21\u578B\u53D1\u73B0\u59CB\u7EC8\u4F7F\u7528 OpenAI models \u63A5\u53E3").volatile(),
300
+ modelSettings: Schema.transform(Schema.dict(Schema.any()), parseModelSettingsMap).default({}).description("\u9010\u6A21\u578B\u542F\u7528\u72B6\u6001\u548C\u8BF7\u6C42\u53C2\u6570").volatile(),
229
301
  useDesktopCredential: Schema.boolean().default(true).description("\u4F18\u5148\u8BFB\u53D6 OpenDesign \u684C\u9762\u7AEF\u767B\u5F55\u6001\u67E5\u8BE2\u989D\u5EA6").volatile(),
230
302
  emptyTurnGuard: Schema.boolean().default(true).description("\u76D1\u63A7\u5957\u9910\u8D60\u9001\u6A21\u578B\u7684\u7A7A\u8F6E/\u534F\u8BAE\u6B8B\u6E23\uFF1B\u7531\u5BBF\u4E3B\u91CD\u8BD5\u7B56\u7565\u5904\u7406\uFF0C\u53EF\u80FD\u989D\u5916\u6D88\u8017\u8D60\u9001\u989D\u5EA6").volatile(),
303
+ quotaRefreshSeconds: Schema.natural().min(5).max(3600).default(60).description("\u989D\u5EA6\u81EA\u52A8\u5237\u65B0\u95F4\u9694\uFF08\u79D2\uFF09").volatile(),
231
304
  requestTimeoutMs: Schema.natural().min(1e3).max(36e5).default(18e5).description("\u63A8\u7406\u8BF7\u6C42\u603B\u8D85\u65F6\uFF08\u6BEB\u79D2\uFF09").volatile(),
232
305
  streamIdleTimeoutMs: Schema.natural().min(1e3).max(18e5).default(3e5).description("\u6D41\u5F0F\u54CD\u5E94\u65E0\u6570\u636E\u8D85\u65F6\uFF08\u6BEB\u79D2\uFF09").volatile()
233
306
  });
234
307
  function assertBaseURL(raw) {
308
+ if (raw.length > 2048) throw new Error("\u63A8\u7406 Base URL \u6700\u591A\u5141\u8BB8 2048 \u5B57\u7B26");
235
309
  let url;
236
310
  try {
237
311
  url = new URL(raw);
@@ -240,12 +314,17 @@ function assertBaseURL(raw) {
240
314
  }
241
315
  if (!["https:", "http:"].includes(url.protocol) || url.username || url.password || url.search || url.hash) throw new Error("\u63A8\u7406 Base URL \u4E0D\u53EF\u5305\u542B\u51ED\u636E\u3001\u67E5\u8BE2\u4E32\u6216\u7247\u6BB5");
242
316
  if (url.protocol === "http:" && !["localhost", "127.0.0.1", "[::1]"].includes(url.hostname)) throw new Error("\u975E\u672C\u673A\u63A8\u7406\u5730\u5740\u5FC5\u987B\u4F7F\u7528 HTTPS");
243
- return url.href.replace(/\/+$/u, "");
317
+ const normalized = url.href.replace(/\/+$/u, "");
318
+ if (normalized.length > 2048) throw new Error("\u63A8\u7406 Base URL \u6700\u591A\u5141\u8BB8 2048 \u5B57\u7B26");
319
+ return normalized;
244
320
  }
245
321
  function readConfig(config) {
246
322
  return {
247
323
  apiKeyEnv: config.apiKeyEnv.get(),
248
324
  baseURL: assertBaseURL(config.baseURL.get()),
325
+ apiProtocol: config.apiProtocol.get(),
326
+ modelSettings: parseModelSettingsMap(config.modelSettings.get()),
327
+ quotaRefreshSeconds: config.quotaRefreshSeconds.get(),
249
328
  useDesktopCredential: config.useDesktopCredential.get(),
250
329
  emptyTurnGuard: config.emptyTurnGuard.get(),
251
330
  requestTimeoutMs: config.requestTimeoutMs.get(),
@@ -347,7 +426,7 @@ function estimateTokens(value) {
347
426
  }
348
427
 
349
428
  // src/stream.ts
350
- import { LlmError as LlmError2, ToolCallId } from "@deepseek-ai/dsh-llm";
429
+ import { LlmError as LlmError4, ToolCallId } from "@deepseek-ai/dsh-llm";
351
430
 
352
431
  // src/util.ts
353
432
  function record(value) {
@@ -378,18 +457,212 @@ function sleep(ms, signal) {
378
457
  });
379
458
  }
380
459
 
381
- // src/stream.ts
460
+ // src/protocols/errors.ts
461
+ import {
462
+ ACCOUNT_QUOTA_EXCEEDED_CODE,
463
+ CONTEXT_WINDOW_EXCEEDED_CODE,
464
+ INVALID_CREDENTIAL_CODE,
465
+ LlmError as LlmError2,
466
+ QUOTA_EXCEEDED_CODE,
467
+ isContextWindowExceededError,
468
+ isQuotaExceededError
469
+ } from "@deepseek-ai/dsh-llm";
470
+ var MESSAGES = {
471
+ [CONTEXT_WINDOW_EXCEEDED_CODE]: "OpenDesign\uFF1A\u8BF7\u6C42\u8D85\u51FA\u6A21\u578B\u4E0A\u4E0B\u6587\u957F\u5EA6\uFF0C\u8BF7\u7F29\u77ED\u8F93\u5165\u540E\u91CD\u8BD5",
472
+ [ACCOUNT_QUOTA_EXCEEDED_CODE]: "OpenDesign\uFF1A\u5957\u9910\u6216\u8D26\u6237\u989D\u5EA6\u4E0D\u8DB3\uFF0C\u8BF7\u68C0\u67E5\u5957\u9910\u989D\u5EA6\u540E\u91CD\u8BD5",
473
+ [QUOTA_EXCEEDED_CODE]: "OpenDesign\uFF1A\u94B1\u5305\u6216\u79EF\u5206\u4F59\u989D\u4E0D\u8DB3\uFF0C\u8BF7\u5145\u503C\u540E\u91CD\u8BD5",
474
+ [INVALID_CREDENTIAL_CODE]: "OpenDesign\uFF1AAPI Key \u65E0\u6548\u6216\u7F3A\u5931\uFF0C\u8BF7\u5728\u8BBE\u7F6E\u4E2D\u66F4\u65B0\u51ED\u636E",
475
+ AUTH: "OpenDesign\uFF1A\u8BA4\u8BC1\u672A\u901A\u8FC7\uFF0C\u8BF7\u91CD\u65B0\u767B\u5F55\u540E\u91CD\u8BD5",
476
+ PERMISSION: "OpenDesign\uFF1A\u5F53\u524D\u8D26\u6237\u65E0\u6743\u8BBF\u95EE\u8BE5\u6A21\u578B",
477
+ RATE_LIMIT: "OpenDesign\uFF1A\u8BF7\u6C42\u8FC7\u4E8E\u9891\u7E41\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5",
478
+ TIMEOUT: "OpenDesign\uFF1A\u8BF7\u6C42\u8D85\u65F6\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5",
479
+ SERVER: "OpenDesign\uFF1A\u4E0A\u6E38\u670D\u52A1\u6682\u4E0D\u53EF\u7528\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5",
480
+ INVALID_REQUEST: "OpenDesign\uFF1A\u8BF7\u6C42\u88AB\u4E0A\u6E38\u62D2\u7EDD",
481
+ INVALID_RESPONSE: "OpenDesign\uFF1A\u4E0A\u6E38\u54CD\u5E94\u683C\u5F0F\u65E0\u6548"
482
+ };
483
+ var UNKNOWN_MESSAGE = "OpenDesign\uFF1A\u8BF7\u6C42\u5931\u8D25";
484
+ var STRUCTURED_CREDENTIAL = /invalid[_-]?(?:x[_-]?)?api[_-]?key|missing[_-]?(?:x[_-]?)?api[_-]?key|incorrect[_-]?(?:x[_-]?)?api[_-]?key|api[_-]?key[_-]?(?:invalid|missing|incorrect)|invalid[_-]?credentials?|credentials?[_-]?(?:invalid|missing)/u;
485
+ var STRUCTURED_CONTEXT = /context[_-]?(?:length|window)[_-]?(?:exceeded|exceed|overflow)|context[_-]?overflow|(?:prompt|input)[_-]?too[_-]?long/u;
486
+ var STRUCTURED_WALLET = /(?:insufficient|low|exhausted|depleted|empty|zero)[_-]?(?:credits?|balance|funds)|(?:credits?|balance|wallet|funds)[_-]?(?:insufficient|exhausted|depleted|low|empty)|(?:credit|wallet)[_-]balance[_-](?:too[_-]low|insufficient|exhausted)/u;
487
+ var STRUCTURED_PLAN = /(?:insufficient|exceeded|exhausted|reached|over)[_-]?(?:quota|plan|usage[_-]?limit)|quota[_-]?(?:exceeded|exhausted|reached)|billing[_-]?hard[_-]?limit|usage[_-]?limit[_-]?(?:exceeded|reached)|(?:subscription|membership|plan)(?:[_-](?:quota|credits?|limit))?[_-](?:exceeded|exhausted|insufficient|reached)/u;
488
+ var STRUCTURED_RATE = /rate[_-]?limit|too[_-]?many[_-]?requests|resource[_-]?exhausted|(?:requests?|tokens?)[_-]?per[_-]?(?:minute|second)/u;
489
+ var STRUCTURED_TIMEOUT = /timeout|timed[_-]?out|deadline[_-]?exceeded/u;
490
+ var STRUCTURED_SERVER = /server[_-]?error|api[_-]?error|internal[_-]?(?:server[_-]?)?error|service[_-]?unavailable|unavailable|overloaded|bad[_-]?gateway|gateway[_-]?error/u;
491
+ var STRUCTURED_AUTH = /authentication|unauthorized|not[_-]?authenticated/u;
492
+ var STRUCTURED_PERMISSION = /permission|forbidden|access[_-]?denied|insufficient[_-]?scope/u;
493
+ var PROSE_CREDENTIAL = /invalid[ _-]?(?:x[ _-]?)?api[ _-]?key|(?:x[ _-]?)?api[ _-]?key[ _-]?(?:is[ _-]?)?(?:invalid|missing|incorrect|not[ _-]?valid)|missing[ _-]?(?:x[ _-]?)?api[ _-]?key|incorrect[ _-]?(?:x[ _-]?)?api[ _-]?key|invalid[ _-]?credentials?\b|api[ _-]?key\s*(?:无效|缺失|错误)|密钥(?:无效|缺失|错误)/u;
494
+ var PROSE_CONTEXT_EN = /\b(?:prompt|input|messages?)\s+(?:is\s+|are\s+)?too\s+(?:long|large)\b|\bmaximum context\b|\btokens? > \d+ maximum\b/u;
495
+ var PROSE_CONTEXT_ZH = /超出[^。]{0,8}上下文|上下文[^。]{0,16}(?:超出|超过|超限|过长|已满)|(?:输入|请求|提示词)[^。]{0,8}(?:过长|太长)|超过[^。]{0,8}上下文/u;
496
+ var PROSE_QUOTA_EN = /\b(?:credits?|balance|quota|funds|wallet)\b[^.;]{0,24}\b(?:insufficient|exhausted|depleted|used[ _-]?up|too[ _-]?low|empty|zero|exceeded)\b|\b(?:insufficient|exhausted|depleted|empty|zero)\b[^.;]{0,24}\b(?:credits?|balance|quota|funds|wallet)\b/u;
497
+ var PROSE_QUOTA_ZH = /(?:余额|积分|额度|配额|钱包)[^。]{0,12}(?:不足|用尽|耗尽|用完|超限|超出|已达上限|超过上限)|(?:不足|用尽|耗尽)[^。]{0,6}(?:余额|额度|配额|积分)/u;
498
+ var PROSE_QUOTA_PLAN = /套餐|订阅|会员|账户额度|账户配额|用量上限|使用上限|\bplan\b|\bsubscription\b|\bmembership\b|\bbilling\b|hard[ _-]?limit|usage[ _-]?limit/u;
499
+ var PROSE_QUOTA_WALLET = /钱包|充值|余额|积分|\bwallet\b|\brecharge\b|top[ _-]?up|\bbalance\b|\bcredits?\b|\bfunds?\b/u;
500
+ var PROSE_RATE = /rate[ _-]?limit|\btoo many requests\b|\bresource[ _-]?exhausted\b|请求过于频繁|频率超限|触发限流|接口限流/u;
501
+ var PROSE_TIMEOUT = /\btimeout\b|\btimed out\b|\bdeadline exceeded\b|请求超时|超时/u;
502
+ var PROSE_SERVER = /\bserver error\b|\bapi error\b|\binternal (?:server )?error\b|\bservice unavailable\b|\boverloaded\b|\bbad gateway\b|服务暂不可用|服务不可用|服务器错误|服务器繁忙|内部错误/u;
503
+ var PROSE_AUTH = /\bauthentication\b|\bunauthorized\b|\bnot authenticated\b|认证失败|登录已失效|未登录/u;
504
+ var PROSE_PERMISSION = /\bpermission\b|\bforbidden\b|\baccess denied\b|\bnot authorized\b|\binsufficient (?:permissions?|scope)\b|权限不足|无权限|无权访问/u;
505
+ function fail(code, status) {
506
+ const options = status === void 0 ? {} : { status };
507
+ return new LlmError2(MESSAGES[code] ?? UNKNOWN_MESSAGE, code, options);
508
+ }
509
+ function matches(fields, pattern) {
510
+ return fields.some((field) => pattern.test(field));
511
+ }
512
+ function structuredQuota(fields) {
513
+ if (matches(fields, STRUCTURED_WALLET)) return QUOTA_EXCEEDED_CODE;
514
+ if (matches(fields, STRUCTURED_PLAN)) return ACCOUNT_QUOTA_EXCEEDED_CODE;
515
+ return void 0;
516
+ }
517
+ function proseQuota(detail) {
518
+ if (PROSE_QUOTA_PLAN.test(detail)) return ACCOUNT_QUOTA_EXCEEDED_CODE;
519
+ if (PROSE_QUOTA_WALLET.test(detail)) return QUOTA_EXCEEDED_CODE;
520
+ return ACCOUNT_QUOTA_EXCEEDED_CODE;
521
+ }
522
+ function classifyStructured(fields) {
523
+ if (matches(fields, STRUCTURED_CREDENTIAL)) return INVALID_CREDENTIAL_CODE;
524
+ if (matches(fields, STRUCTURED_CONTEXT)) return CONTEXT_WINDOW_EXCEEDED_CODE;
525
+ const quota = structuredQuota(fields);
526
+ if (quota !== void 0) return quota;
527
+ if (matches(fields, STRUCTURED_RATE)) return "RATE_LIMIT";
528
+ if (matches(fields, STRUCTURED_TIMEOUT)) return "TIMEOUT";
529
+ if (matches(fields, STRUCTURED_SERVER)) return "SERVER";
530
+ if (matches(fields, STRUCTURED_AUTH)) return "AUTH";
531
+ if (matches(fields, STRUCTURED_PERMISSION)) return "PERMISSION";
532
+ return void 0;
533
+ }
534
+ function classifyProse(detail) {
535
+ if (detail === "") return void 0;
536
+ if (isContextWindowExceededError(detail) || PROSE_CONTEXT_EN.test(detail) || PROSE_CONTEXT_ZH.test(detail)) return CONTEXT_WINDOW_EXCEEDED_CODE;
537
+ if (isQuotaExceededError(detail) || PROSE_QUOTA_EN.test(detail) || PROSE_QUOTA_ZH.test(detail)) return proseQuota(detail);
538
+ if (PROSE_RATE.test(detail)) return "RATE_LIMIT";
539
+ if (PROSE_TIMEOUT.test(detail)) return "TIMEOUT";
540
+ if (PROSE_SERVER.test(detail)) return "SERVER";
541
+ if (PROSE_AUTH.test(detail)) return "AUTH";
542
+ if (PROSE_PERMISSION.test(detail)) return "PERMISSION";
543
+ return void 0;
544
+ }
545
+ function classifyStatus(status) {
546
+ if (status === void 0) return void 0;
547
+ if (status === 401) return "AUTH";
548
+ if (status === 403) return "PERMISSION";
549
+ if (status === 402) return QUOTA_EXCEEDED_CODE;
550
+ if (status === 429) return "RATE_LIMIT";
551
+ if (status === 408 || status === 504) return "TIMEOUT";
552
+ if (status >= 500) return "SERVER";
553
+ return void 0;
554
+ }
555
+ function fieldsOf(error) {
556
+ const structured = [];
557
+ for (const value of [error.code, error.type]) {
558
+ if (typeof value !== "string") continue;
559
+ const normalized = value.trim().toLowerCase();
560
+ if (normalized !== "") structured.push(normalized);
561
+ }
562
+ return { structured, prose: typeof error.message === "string" ? error.message.toLowerCase() : "" };
563
+ }
564
+ function classify(fields, status, fallback) {
565
+ const structured = classifyStructured(fields.structured);
566
+ if (structured !== void 0 && structured !== "AUTH" && structured !== "PERMISSION") return fail(structured, status);
567
+ if (structured === void 0 && status !== void 0 && status >= 500 && status !== 504) return fail("SERVER", status);
568
+ if (PROSE_CREDENTIAL.test(fields.prose)) return fail(INVALID_CREDENTIAL_CODE, status);
569
+ if (structured !== void 0) return fail(structured, status);
570
+ const prose = classifyProse(fields.prose);
571
+ if (prose !== void 0) return fail(prose, status);
572
+ const byStatus = classifyStatus(status);
573
+ return fail(byStatus ?? fallback, status);
574
+ }
382
575
  function gatewayError(status, body) {
383
576
  const error = record(body) && record(body.error) ? body.error : record(body) ? body : {};
384
- const detail = [error.code, error.type, error.message].filter((x) => typeof x === "string").join(" ").toLowerCase();
385
- const options = status === void 0 ? {} : { status };
386
- if (/context.{0,30}(exceed|length|window)|too many tokens|max.*context/u.test(detail)) return new LlmError2("OpenDesign\uFF1A\u8BF7\u6C42\u8D85\u8FC7\u4E0A\u4E0B\u6587\u9650\u5236", "CONTEXT_WINDOW_EXCEEDED", options);
387
- if (/quota|insufficient.{0,20}(credit|balance)|credit.{0,20}exhaust|balance.{0,20}insufficient/u.test(detail)) return new LlmError2("OpenDesign\uFF1A\u5F53\u524D\u8BA1\u8D39\u6C60\u989D\u5EA6\u4E0D\u8DB3", "ACCOUNT_QUOTA", options);
388
- if (status === 401 || status === 403 || /invalid_api_key|missing_api_key/u.test(detail)) return new LlmError2("OpenDesign\uFF1A\u8BA4\u8BC1\u5931\u8D25\uFF0C\u8BF7\u91CD\u65B0\u767B\u5F55\u6216\u68C0\u67E5\u51ED\u636E", "INVALID_CREDENTIAL", options);
389
- if (status === 429) return new LlmError2("OpenDesign\uFF1A\u8BF7\u6C42\u8FC7\u4E8E\u9891\u7E41\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5", "RATE_LIMIT", options);
390
- if (status === 408 || status === 504) return new LlmError2("OpenDesign\uFF1A\u8BF7\u6C42\u8D85\u65F6", "TIMEOUT", options);
391
- return new LlmError2(`OpenDesign\uFF1A${status !== void 0 && status >= 500 ? "\u670D\u52A1\u6682\u4E0D\u53EF\u7528" : "\u8BF7\u6C42\u88AB\u62D2\u7EDD"}${status === void 0 ? "" : `\uFF08HTTP ${status}\uFF09`}`, status !== void 0 && status >= 500 ? "SERVER" : "INVALID_REQUEST", options);
577
+ return classify(fieldsOf(error), status, "INVALID_REQUEST");
578
+ }
579
+ function anthropicError(status, body) {
580
+ const error = record(body) && record(body.error) ? body.error : {};
581
+ return classify(fieldsOf(error), status, "INVALID_REQUEST");
392
582
  }
583
+ function inBandError(error, fallback = "INVALID_RESPONSE") {
584
+ return classify(fieldsOf(record(error) ? error : {}), void 0, fallback);
585
+ }
586
+
587
+ // src/protocols/sse.ts
588
+ import { LlmError as LlmError3 } from "@deepseek-ai/dsh-llm";
589
+ var MAX_FRAME_CHARS = 4 * 1024 * 1024;
590
+ async function* sseFrames(body, signal, idleMs) {
591
+ const reader = body.getReader();
592
+ const decoder = new TextDecoder();
593
+ let buffer = "";
594
+ let data = [];
595
+ let dataSize = 0;
596
+ let name2;
597
+ function dispatch() {
598
+ const payload = data.join("\n");
599
+ const event = name2;
600
+ data = [];
601
+ dataSize = 0;
602
+ name2 = void 0;
603
+ return payload ? [{ ...event === void 0 ? {} : { event }, data: payload }] : [];
604
+ }
605
+ function line(value) {
606
+ if (!value) return dispatch();
607
+ if (value.startsWith(":")) return [];
608
+ if (value.startsWith("data:")) {
609
+ const part = value.slice(5).replace(/^ /u, "");
610
+ dataSize += part.length;
611
+ if (dataSize > MAX_FRAME_CHARS) throw new LlmError3("OpenDesign\uFF1A\u6D41\u5F0F\u4E8B\u4EF6\u8D85\u8FC7\u5927\u5C0F\u9650\u5236", "INVALID_RESPONSE");
612
+ data.push(part);
613
+ return [];
614
+ }
615
+ if (value.startsWith("event:")) {
616
+ const event = value.slice(6).trim();
617
+ name2 = event === "" ? void 0 : event;
618
+ }
619
+ return [];
620
+ }
621
+ async function read() {
622
+ signal.throwIfAborted();
623
+ let timer;
624
+ let abort = () => {
625
+ };
626
+ try {
627
+ return await Promise.race([
628
+ reader.read(),
629
+ new Promise((_, reject) => {
630
+ timer = setTimeout(() => reject(new LlmError3("OpenDesign\uFF1A\u6D41\u5F0F\u54CD\u5E94\u957F\u65F6\u95F4\u6CA1\u6709\u6570\u636E", "TIMEOUT")), idleMs);
631
+ abort = () => reject(signal.reason);
632
+ signal.addEventListener("abort", abort, { once: true });
633
+ if (signal.aborted) abort();
634
+ })
635
+ ]);
636
+ } finally {
637
+ clearTimeout(timer);
638
+ signal.removeEventListener("abort", abort);
639
+ }
640
+ }
641
+ try {
642
+ while (true) {
643
+ const result = await read();
644
+ buffer += result.done ? decoder.decode() : decoder.decode(result.value, { stream: true });
645
+ let match;
646
+ while (match = /\r\n|\n|\r(?!$)/u.exec(buffer)) {
647
+ const at = match.index;
648
+ const value = buffer.slice(0, at);
649
+ buffer = buffer.slice(at + match[0].length);
650
+ for (const frame of line(value)) yield frame;
651
+ }
652
+ if (buffer.length > MAX_FRAME_CHARS) throw new LlmError3("OpenDesign\uFF1A\u6D41\u5F0F\u884C\u8D85\u8FC7\u5927\u5C0F\u9650\u5236", "INVALID_RESPONSE");
653
+ if (result.done) {
654
+ if (buffer) for (const frame of line(buffer.replace(/\r$/u, ""))) yield frame;
655
+ for (const frame of dispatch()) yield frame;
656
+ break;
657
+ }
658
+ }
659
+ } finally {
660
+ await reader.cancel().catch(() => void 0);
661
+ reader.releaseLock();
662
+ }
663
+ }
664
+
665
+ // src/stream.ts
393
666
  function tokenUsage(value) {
394
667
  const prompt = finite(value.prompt_tokens), output = finite(value.completion_tokens);
395
668
  if (prompt === void 0 || output === void 0 || prompt < 0 || output < 0) return;
@@ -409,9 +682,8 @@ function tokenUsage(value) {
409
682
  };
410
683
  }
411
684
  async function* consumeSse(body, signal, idleMs) {
412
- const reader = body.getReader(), decoder = new TextDecoder();
413
- let buffer = "", data = [], dataSize = 0, done = false;
414
- let nextIndex = 0, count2 = 0, sawTool = false, text2, reasoning;
685
+ let nextIndex = 0, count2 = 0, sawTool = false, done = false;
686
+ let text2, reasoning;
415
687
  const tools = /* @__PURE__ */ new Map();
416
688
  let finish, usage;
417
689
  function close() {
@@ -419,17 +691,14 @@ async function* consumeSse(body, signal, idleMs) {
419
691
  if (text2) out.push({ type: "block-end", index: text2.index, block: { type: "text", text: text2.text } });
420
692
  if (reasoning) out.push({ type: "block-end", index: reasoning.index, block: { type: "reasoning", text: reasoning.text } });
421
693
  for (const call of tools.values()) {
422
- if (!call.id || !call.name) throw new LlmError2("OpenDesign\uFF1A\u5DE5\u5177\u8C03\u7528\u7F3A\u5C11\u6807\u8BC6\u6216\u540D\u79F0", "INVALID_RESPONSE");
694
+ if (!call.id || !call.name) throw new LlmError4("OpenDesign\uFF1A\u5DE5\u5177\u8C03\u7528\u7F3A\u5C11\u6807\u8BC6\u6216\u540D\u79F0", "INVALID_RESPONSE");
423
695
  out.push({ type: "block-end", index: call.index, block: { type: "tool-call", id: ToolCallId(call.id), name: call.name, arguments: call.arguments } });
424
696
  }
425
697
  text2 = reasoning = void 0;
426
698
  tools.clear();
427
699
  return out.sort((a, b) => ("index" in a ? a.index : 0) - ("index" in b ? b.index : 0));
428
700
  }
429
- function event() {
430
- const payload = data.join("\n");
431
- data = [];
432
- dataSize = 0;
701
+ function event(payload) {
433
702
  if (!payload) return [];
434
703
  if (payload.trim() === "[DONE]") {
435
704
  done = true;
@@ -439,18 +708,18 @@ async function* consumeSse(body, signal, idleMs) {
439
708
  try {
440
709
  value = JSON.parse(payload);
441
710
  } catch {
442
- throw new LlmError2("OpenDesign\uFF1A\u65E0\u6548\u7684\u6D41\u5F0F JSON", "INVALID_RESPONSE");
711
+ throw new LlmError4("OpenDesign\uFF1A\u65E0\u6548\u7684\u6D41\u5F0F JSON", "INVALID_RESPONSE");
443
712
  }
444
- if (!record(value)) throw new LlmError2("OpenDesign\uFF1A\u6D41\u5F0F\u4E8B\u4EF6\u4E0D\u662F\u5BF9\u8C61", "INVALID_RESPONSE");
713
+ if (!record(value)) throw new LlmError4("OpenDesign\uFF1A\u6D41\u5F0F\u4E8B\u4EF6\u4E0D\u662F\u5BF9\u8C61", "INVALID_RESPONSE");
445
714
  if (record(value.error)) throw gatewayError(void 0, value);
446
715
  if (record(value.usage)) usage = tokenUsage(value.usage) ?? usage;
447
716
  if (!Array.isArray(value.choices) || !value.choices.length) return [];
448
717
  const choice = value.choices[0];
449
- if (!record(choice)) throw new LlmError2("OpenDesign\uFF1A\u65E0\u6548\u7684\u6D41\u5F0F\u9009\u9879", "INVALID_RESPONSE");
718
+ if (!record(choice)) throw new LlmError4("OpenDesign\uFF1A\u65E0\u6548\u7684\u6D41\u5F0F\u9009\u9879", "INVALID_RESPONSE");
450
719
  const delta = record(choice.delta) ? choice.delta : {};
451
720
  const out = [];
452
721
  const hasDelta = typeof delta.content === "string" && delta.content !== "" || typeof delta.reasoning_content === "string" && delta.reasoning_content !== "" || Array.isArray(delta.tool_calls) && delta.tool_calls.length > 0;
453
- if (finish && hasDelta) throw new LlmError2("OpenDesign\uFF1A\u54CD\u5E94\u7ED3\u675F\u540E\u4ECD\u6536\u5230\u5185\u5BB9", "INVALID_RESPONSE");
722
+ if (finish && hasDelta) throw new LlmError4("OpenDesign\uFF1A\u54CD\u5E94\u7ED3\u675F\u540E\u4ECD\u6536\u5230\u5185\u5BB9", "INVALID_RESPONSE");
454
723
  const thinking = typeof delta.reasoning_content === "string" ? delta.reasoning_content : typeof delta.reasoning === "string" ? delta.reasoning : "";
455
724
  if (thinking) {
456
725
  if (!reasoning) {
@@ -471,7 +740,7 @@ async function* consumeSse(body, signal, idleMs) {
471
740
  out.push({ type: "text-delta", index: text2.index, text: delta.content });
472
741
  }
473
742
  if (Array.isArray(delta.tool_calls)) for (const item of delta.tool_calls) {
474
- if (!record(item) || typeof item.index !== "number" || !Number.isInteger(item.index) || item.index < 0) throw new LlmError2("OpenDesign\uFF1A\u65E0\u6548\u7684\u5DE5\u5177\u8C03\u7528\u7D22\u5F15", "INVALID_RESPONSE");
743
+ if (!record(item) || typeof item.index !== "number" || !Number.isInteger(item.index) || item.index < 0) throw new LlmError4("OpenDesign\uFF1A\u65E0\u6548\u7684\u5DE5\u5177\u8C03\u7528\u7D22\u5F15", "INVALID_RESPONSE");
475
744
  let call = tools.get(item.index);
476
745
  if (!call) {
477
746
  call = { index: nextIndex++, id: "", name: "", arguments: "", started: false };
@@ -481,7 +750,7 @@ async function* consumeSse(body, signal, idleMs) {
481
750
  }
482
751
  const fn = record(item.function) ? item.function : {};
483
752
  if (typeof item.id === "string" && item.id) {
484
- if (call.id && call.id !== item.id) throw new LlmError2("OpenDesign\uFF1A\u5DE5\u5177\u8C03\u7528\u6807\u8BC6\u53D1\u751F\u53D8\u5316", "INVALID_RESPONSE");
753
+ if (call.id && call.id !== item.id) throw new LlmError4("OpenDesign\uFF1A\u5DE5\u5177\u8C03\u7528\u6807\u8BC6\u53D1\u751F\u53D8\u5316", "INVALID_RESPONSE");
485
754
  call.id = item.id;
486
755
  }
487
756
  if (typeof fn.name === "string" && fn.name) call.name = fn.name;
@@ -496,7 +765,7 @@ async function* consumeSse(body, signal, idleMs) {
496
765
  }
497
766
  }
498
767
  if (typeof choice.finish_reason === "string" && choice.finish_reason) {
499
- if (finish) throw new LlmError2("OpenDesign\uFF1A\u91CD\u590D\u7684\u54CD\u5E94\u7EC8\u6B62\u4E8B\u4EF6", "INVALID_RESPONSE");
768
+ if (finish) throw new LlmError4("OpenDesign\uFF1A\u91CD\u590D\u7684\u54CD\u5E94\u7EC8\u6B62\u4E8B\u4EF6", "INVALID_RESPONSE");
500
769
  switch (choice.finish_reason) {
501
770
  case "length":
502
771
  finish = { kind: "max-tokens" };
@@ -509,70 +778,22 @@ async function* consumeSse(body, signal, idleMs) {
509
778
  finish = { kind: "stop" };
510
779
  break;
511
780
  default:
512
- throw new LlmError2("OpenDesign\uFF1A\u6A21\u578B\u54CD\u5E94\u88AB\u8FC7\u6EE4\u6216\u5F02\u5E38\u7EC8\u6B62", "INVALID_RESPONSE");
781
+ throw new LlmError4("OpenDesign\uFF1A\u6A21\u578B\u54CD\u5E94\u88AB\u8FC7\u6EE4\u6216\u5F02\u5E38\u7EC8\u6B62", "INVALID_RESPONSE");
513
782
  }
514
783
  out.push(...close());
515
784
  }
516
785
  return out;
517
786
  }
518
- function line(value) {
519
- if (!value) return event();
520
- if (value.startsWith("data:")) {
521
- const part = value.slice(5).replace(/^ /u, "");
522
- dataSize += part.length;
523
- if (dataSize > 4 * 1024 * 1024) throw new LlmError2("OpenDesign\uFF1A\u6D41\u5F0F\u4E8B\u4EF6\u8D85\u8FC7\u5927\u5C0F\u9650\u5236", "INVALID_RESPONSE");
524
- data.push(part);
525
- }
526
- return [];
527
- }
528
- async function read() {
529
- signal.throwIfAborted();
530
- let timer;
531
- let abort = () => {
532
- };
533
- try {
534
- return await Promise.race([
535
- reader.read(),
536
- new Promise((_, reject) => {
537
- timer = setTimeout(() => reject(new LlmError2("OpenDesign\uFF1A\u6D41\u5F0F\u54CD\u5E94\u957F\u65F6\u95F4\u6CA1\u6709\u6570\u636E", "TIMEOUT")), idleMs);
538
- abort = () => reject(signal.reason);
539
- signal.addEventListener("abort", abort, { once: true });
540
- if (signal.aborted) abort();
541
- })
542
- ]);
543
- } finally {
544
- clearTimeout(timer);
545
- signal.removeEventListener("abort", abort);
546
- }
547
- }
548
- try {
549
- while (!done) {
550
- const result = await read();
551
- buffer += result.done ? decoder.decode() : decoder.decode(result.value, { stream: true });
552
- let match;
553
- while (match = /\r\n|\n|\r(?!$)/u.exec(buffer)) {
554
- const at = match.index, value = buffer.slice(0, at);
555
- buffer = buffer.slice(at + match[0].length);
556
- for (const chunk of line(value)) yield chunk;
557
- if (done) break;
558
- }
559
- if (buffer.length > 4 * 1024 * 1024) throw new LlmError2("OpenDesign\uFF1A\u6D41\u5F0F\u884C\u8D85\u8FC7\u5927\u5C0F\u9650\u5236", "INVALID_RESPONSE");
560
- if (result.done) {
561
- if (buffer && !done) for (const chunk of line(buffer.replace(/\r$/u, ""))) yield chunk;
562
- if (!done) for (const chunk of event()) yield chunk;
563
- break;
564
- }
565
- }
566
- signal.throwIfAborted();
567
- if (!finish) throw new LlmError2("OpenDesign\uFF1A\u54CD\u5E94\u6D41\u63D0\u524D\u4E2D\u65AD\uFF0C\u672A\u6536\u5230\u7ED3\u675F\u539F\u56E0", "INVALID_RESPONSE");
568
- if (!count2) throw new LlmError2("OpenDesign\uFF1A\u6A21\u578B\u8FD4\u56DE\u7A7A\u54CD\u5E94", "EMPTY_RESPONSE");
569
- if (finish.kind === "tool-calls" && !sawTool) throw new LlmError2("OpenDesign\uFF1A\u54CD\u5E94\u58F0\u660E\u5DE5\u5177\u8C03\u7528\uFF0C\u4F46\u672A\u8FD4\u56DE\u4EFB\u4F55\u5DE5\u5177\u5757", "INVALID_RESPONSE");
570
- if (usage) yield { type: "usage", usage };
571
- yield { type: "finish", reason: finish };
572
- } finally {
573
- await reader.cancel().catch(() => void 0);
574
- reader.releaseLock();
787
+ for await (const frame of sseFrames(body, signal, idleMs)) {
788
+ for (const chunk of event(frame.data)) yield chunk;
789
+ if (done) break;
575
790
  }
791
+ signal.throwIfAborted();
792
+ if (!finish) throw new LlmError4("OpenDesign\uFF1A\u54CD\u5E94\u6D41\u63D0\u524D\u4E2D\u65AD\uFF0C\u672A\u6536\u5230\u7ED3\u675F\u539F\u56E0", "INVALID_RESPONSE");
793
+ if (!count2) throw new LlmError4("OpenDesign\uFF1A\u6A21\u578B\u8FD4\u56DE\u7A7A\u54CD\u5E94", "EMPTY_RESPONSE");
794
+ if (finish.kind === "tool-calls" && !sawTool) throw new LlmError4("OpenDesign\uFF1A\u54CD\u5E94\u58F0\u660E\u5DE5\u5177\u8C03\u7528\uFF0C\u4F46\u672A\u8FD4\u56DE\u4EFB\u4F55\u5DE5\u5177\u5757", "INVALID_RESPONSE");
795
+ if (usage) yield { type: "usage", usage };
796
+ yield { type: "finish", reason: finish };
576
797
  }
577
798
 
578
799
  // src/empty-turn.ts
@@ -698,22 +919,783 @@ var EmptyTurnTracker = class {
698
919
  }
699
920
  };
700
921
 
922
+ // src/protocols/index.ts
923
+ import { LlmError as LlmError9 } from "@deepseek-ai/dsh-llm";
924
+
925
+ // src/protocols/chat-completions.ts
926
+ import { LlmError as LlmError5 } from "@deepseek-ai/dsh-llm";
927
+
928
+ // src/protocols/headers.ts
929
+ import { attributionHeaders } from "@deepseek-ai/dsh-llm";
930
+ function streamHeaders(apiKey, extra = {}) {
931
+ return {
932
+ ...attributionHeaders(),
933
+ authorization: `Bearer ${apiKey}`,
934
+ "content-type": "application/json",
935
+ accept: "text/event-stream",
936
+ ...extra
937
+ };
938
+ }
939
+
940
+ // src/protocols/chat-completions.ts
941
+ function buildChatCompletionsRequest(ctx) {
942
+ const { options, model } = ctx;
943
+ const replayReasoning = model.id.startsWith("deepseek-") || model.id.startsWith("kimi-");
944
+ const messages2 = chatMessages(options, (ref) => ctx.images.get(ref.attachmentId), replayReasoning);
945
+ const tools = chatTools(options);
946
+ const body = { model: model.id, messages: messages2, stream: true, stream_options: { include_usage: true } };
947
+ if (tools) body.tools = tools;
948
+ if (options.temperature !== void 0) body.temperature = options.temperature;
949
+ if (ctx.topP !== void 0) body.top_p = ctx.topP;
950
+ if (options.maxTokens !== void 0) {
951
+ if (!Number.isSafeInteger(options.maxTokens) || options.maxTokens <= 0) throw new LlmError5("maxTokens \u5FC5\u987B\u662F\u6B63\u6574\u6570", "INVALID_REQUEST");
952
+ body.max_tokens = options.maxTokens;
953
+ }
954
+ if (options.reasoningEffort !== void 0) body.reasoning_effort = options.reasoningEffort;
955
+ if (options.stop?.length) body.stop = options.stop;
956
+ return { url: inferenceEndpoint(ctx.baseURL, "chat-completions"), headers: streamHeaders(ctx.apiKey), body };
957
+ }
958
+ var chatCompletions = {
959
+ id: "chat-completions",
960
+ build: buildChatCompletionsRequest,
961
+ stream(body, signal, idleMs) {
962
+ return consumeSse(body, signal, idleMs);
963
+ }
964
+ };
965
+
966
+ // src/protocols/messages.ts
967
+ import {
968
+ LlmError as LlmError7,
969
+ ToolCallId as ToolCallId2
970
+ } from "@deepseek-ai/dsh-llm";
971
+
972
+ // src/protocols/parts.ts
973
+ import { LlmError as LlmError6, textOnlyImageText as textOnlyImageText2 } from "@deepseek-ai/dsh-llm";
974
+ function projectContent(blocks, image) {
975
+ let text2 = "";
976
+ const images = [];
977
+ for (const block of blocks) {
978
+ switch (block.type) {
979
+ case "text":
980
+ text2 += block.text;
981
+ break;
982
+ case "image": {
983
+ const url = block.offloaded ? void 0 : image?.(block.attachment);
984
+ if (url) images.push(url);
985
+ else text2 += `${text2 ? "\n" : ""}${textOnlyImageText2(block.attachment)}`;
986
+ break;
987
+ }
988
+ case "file":
989
+ throw new LlmError6("\u539F\u59CB\u6587\u4EF6\u672A\u88AB\u5BBF\u4E3B\u6295\u5F71\u4E3A\u6587\u672C\u53E5\u67C4", "UNSUPPORTED_OPTION");
990
+ // 工具定义已由 Core 物化进 options.tools;工具更新块只对声明 in-history 的路由有意义。
991
+ default:
992
+ break;
993
+ }
994
+ }
995
+ return { text: text2, images };
996
+ }
997
+
998
+ // src/protocols/usage.ts
999
+ function valid(value) {
1000
+ return value !== void 0 && Number.isFinite(value) && value >= 0;
1001
+ }
1002
+ function openAiUsage(raw) {
1003
+ const input = raw.input, output = raw.output;
1004
+ if (!valid(input) || !valid(output)) return void 0;
1005
+ const read = valid(raw.cacheRead) ? raw.cacheRead : 0;
1006
+ const write = valid(raw.cacheWrite) ? raw.cacheWrite : 0;
1007
+ const cached = read + write <= input;
1008
+ const reasoning = valid(raw.reasoning) && raw.reasoning <= output ? raw.reasoning : void 0;
1009
+ return {
1010
+ inputTokens: cached ? input - read - write : input,
1011
+ outputTokens: output,
1012
+ totalTokens: input + output,
1013
+ ...cached && read > 0 ? { cacheReadTokens: read } : {},
1014
+ ...cached && write > 0 ? { cacheWriteTokens: write } : {},
1015
+ ...reasoning !== void 0 ? { reasoningTokens: reasoning } : {}
1016
+ };
1017
+ }
1018
+ function anthropicUsage(raw) {
1019
+ const input = raw.input, output = raw.output;
1020
+ if (!valid(input) || !valid(output)) return void 0;
1021
+ const read = valid(raw.cacheRead) ? raw.cacheRead : void 0;
1022
+ const write = valid(raw.cacheWrite) ? raw.cacheWrite : void 0;
1023
+ const reasoning = valid(raw.reasoning) && raw.reasoning <= output ? raw.reasoning : void 0;
1024
+ const total = valid(raw.total) ? raw.total : input + (read ?? 0) + (write ?? 0) + output;
1025
+ return {
1026
+ inputTokens: input,
1027
+ outputTokens: output,
1028
+ totalTokens: total,
1029
+ ...read !== void 0 && read > 0 ? { cacheReadTokens: read } : {},
1030
+ ...write !== void 0 && write > 0 ? { cacheWriteTokens: write } : {},
1031
+ ...reasoning !== void 0 ? { reasoningTokens: reasoning } : {}
1032
+ };
1033
+ }
1034
+
1035
+ // src/protocols/messages.ts
1036
+ var MESSAGES_DEFAULT_MAX_TOKENS = 4096;
1037
+ var ANTHROPIC_VERSION = "2023-06-01";
1038
+ var THINKING_EFFORTS = ["low", "medium", "high", "xhigh", "max"];
1039
+ var DATA_URL = /^data:([^;,]+);base64,(.*)$/su;
1040
+ function messagesURL(baseURL) {
1041
+ return inferenceEndpoint(baseURL, "messages");
1042
+ }
1043
+ function imagePart(url) {
1044
+ const match = DATA_URL.exec(url);
1045
+ return match?.[1] && match[2] ? { type: "image", source: { type: "base64", media_type: match[1], data: match[2] } } : { type: "image", source: { type: "url", url } };
1046
+ }
1047
+ function toolInput(argumentsText) {
1048
+ if (argumentsText.trim() === "") return {};
1049
+ let parsed;
1050
+ try {
1051
+ parsed = JSON.parse(argumentsText);
1052
+ } catch {
1053
+ throw new LlmError7("\u5386\u53F2\u5DE5\u5177\u8C03\u7528\u53C2\u6570\u4E0D\u662F\u5408\u6CD5 JSON\uFF0C\u65E0\u6CD5\u6295\u5F71\u4E3A Anthropic tool_use.input", "INVALID_REQUEST");
1054
+ }
1055
+ if (!record(parsed)) throw new LlmError7("\u5386\u53F2\u5DE5\u5177\u8C03\u7528\u53C2\u6570\u4E0D\u662F JSON \u5BF9\u8C61\uFF0C\u65E0\u6CD5\u6295\u5F71\u4E3A Anthropic tool_use.input", "INVALID_REQUEST");
1056
+ return parsed;
1057
+ }
1058
+ function buildMessagesRequest(ctx) {
1059
+ const { options, model } = ctx;
1060
+ const maxTokens = options.maxTokens ?? MESSAGES_DEFAULT_MAX_TOKENS;
1061
+ if (!Number.isSafeInteger(maxTokens) || maxTokens <= 0) throw new LlmError7("maxTokens \u5FC5\u987B\u662F\u6B63\u6574\u6570", "INVALID_REQUEST");
1062
+ const system = [];
1063
+ const messages2 = [];
1064
+ const lookup = ctx.images.size ? (ref) => ctx.images.get(ref.attachmentId) : void 0;
1065
+ let results = [];
1066
+ let lifted = [];
1067
+ const flushResults = () => {
1068
+ if (results.length) {
1069
+ messages2.push({ role: "user", content: results });
1070
+ results = [];
1071
+ }
1072
+ };
1073
+ const flushLifted = () => {
1074
+ flushResults();
1075
+ if (lifted.length) {
1076
+ messages2.push({
1077
+ role: "user",
1078
+ content: [{ type: "text", text: "(Image content from the preceding tool result.)" }, ...lifted.map(imagePart)]
1079
+ });
1080
+ lifted = [];
1081
+ }
1082
+ };
1083
+ for (const message of options.messages) {
1084
+ if (message.role !== "tool") flushLifted();
1085
+ const projected = projectContent(message.content, message.role === "user" || message.role === "tool" ? lookup : void 0);
1086
+ switch (message.role) {
1087
+ case "system":
1088
+ case "developer":
1089
+ if (projected.text) system.push(projected.text);
1090
+ break;
1091
+ case "user": {
1092
+ const content2 = [
1093
+ ...projected.text ? [{ type: "text", text: projected.text }] : [],
1094
+ ...projected.images.map(imagePart)
1095
+ ];
1096
+ if (content2.length) messages2.push({ role: "user", content: content2 });
1097
+ break;
1098
+ }
1099
+ case "assistant": {
1100
+ const content2 = [];
1101
+ if (projected.text) content2.push({ type: "text", text: projected.text });
1102
+ for (const block of message.content) {
1103
+ if (block.type === "tool-call") content2.push({ type: "tool_use", id: block.id, name: block.name, input: toolInput(block.arguments) });
1104
+ }
1105
+ if (content2.length) messages2.push({ role: "assistant", content: content2 });
1106
+ break;
1107
+ }
1108
+ case "tool": {
1109
+ results.push({
1110
+ type: "tool_result",
1111
+ tool_use_id: message.toolCallId,
1112
+ content: projected.text || (projected.images.length ? "(Image content follows in the next user message.)" : "")
1113
+ });
1114
+ lifted.push(...projected.images);
1115
+ break;
1116
+ }
1117
+ }
1118
+ }
1119
+ flushLifted();
1120
+ if (options.system?.trim()) system.unshift(options.system);
1121
+ const body = { model: model.id, max_tokens: maxTokens, messages: messages2, stream: true };
1122
+ if (system.length) body.system = system.join("\n\n");
1123
+ if (options.tools?.length) body.tools = options.tools.map((tool) => ({ name: tool.name, description: tool.description, input_schema: tool.parameters }));
1124
+ if (options.temperature !== void 0) body.temperature = options.temperature;
1125
+ if (ctx.topP !== void 0) body.top_p = ctx.topP;
1126
+ if (options.stop?.length) body.stop_sequences = options.stop;
1127
+ if (options.reasoningEffort !== void 0) {
1128
+ const effort = String(options.reasoningEffort);
1129
+ if (effort === "none") body.thinking = { type: "disabled" };
1130
+ else if (THINKING_EFFORTS.includes(effort)) {
1131
+ body.thinking = { type: "adaptive" };
1132
+ body.output_config = { effort };
1133
+ } else throw new LlmError7(`Anthropic Messages \u534F\u8BAE\u4E0D\u63A5\u53D7\u601D\u8003\u6DF1\u5EA6\u300C${effort}\u300D`, "UNSUPPORTED_OPTION");
1134
+ }
1135
+ return { url: messagesURL(ctx.baseURL), headers: streamHeaders(ctx.apiKey, { "anthropic-version": ANTHROPIC_VERSION }), body };
1136
+ }
1137
+ function num(value) {
1138
+ return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
1139
+ }
1140
+ async function* streamMessages(body, signal, idleMs) {
1141
+ let nextIndex = 0;
1142
+ let count2 = 0;
1143
+ let finish;
1144
+ let stopReason;
1145
+ let sawTool = false;
1146
+ const blocks = /* @__PURE__ */ new Map();
1147
+ const raw = {
1148
+ input: void 0,
1149
+ output: void 0,
1150
+ cacheRead: void 0,
1151
+ cacheWrite: void 0
1152
+ };
1153
+ let usage;
1154
+ function stateAt(index) {
1155
+ const state = typeof index === "number" ? blocks.get(index) : void 0;
1156
+ if (!state) throw new LlmError7("OpenDesign\uFF1A\u6D41\u5F0F\u5185\u5BB9\u5757\u7D22\u5F15\u65E0\u6548", "INVALID_RESPONSE");
1157
+ return state;
1158
+ }
1159
+ function assign(state) {
1160
+ if (state.index < 0) {
1161
+ state.index = nextIndex++;
1162
+ count2++;
1163
+ }
1164
+ }
1165
+ function close() {
1166
+ const out = [];
1167
+ for (const state of blocks.values()) {
1168
+ if (!state.started) continue;
1169
+ if (state.kind === "text") out.push({ type: "block-end", index: state.index, block: { type: "text", text: state.text } });
1170
+ else if (state.kind === "reasoning") out.push({ type: "block-end", index: state.index, block: { type: "reasoning", text: state.text } });
1171
+ else if (state.kind === "tool") {
1172
+ if (!state.id || !state.name) throw new LlmError7("OpenDesign\uFF1A\u5DE5\u5177\u8C03\u7528\u7F3A\u5C11\u6807\u8BC6\u6216\u540D\u79F0", "INVALID_RESPONSE");
1173
+ out.push({ type: "block-end", index: state.index, block: { type: "tool-call", id: ToolCallId2(state.id), name: state.name, arguments: state.args } });
1174
+ }
1175
+ }
1176
+ return out.sort((a, b) => ("index" in a ? a.index : 0) - ("index" in b ? b.index : 0));
1177
+ }
1178
+ function reasonFromStop(stop) {
1179
+ switch (stop) {
1180
+ case "end_turn":
1181
+ case "stop_sequence":
1182
+ return { kind: "stop" };
1183
+ case "max_tokens":
1184
+ case "model_context_window_exceeded":
1185
+ return { kind: "max-tokens" };
1186
+ case "tool_use":
1187
+ return { kind: "tool-calls" };
1188
+ case "refusal":
1189
+ throw new LlmError7("OpenDesign\uFF1A\u6A21\u578B\u62D2\u7EDD\u56DE\u7B54\uFF08refusal\uFF09", "INVALID_RESPONSE");
1190
+ case "pause_turn":
1191
+ throw new LlmError7("OpenDesign\uFF1A\u6A21\u578B\u6682\u505C\u8F6E\u6B21\uFF0C\u9700\u91CD\u65B0\u53D1\u8D77\u8BF7\u6C42", "INVALID_RESPONSE");
1192
+ default:
1193
+ throw new LlmError7("OpenDesign\uFF1A\u6A21\u578B\u54CD\u5E94\u88AB\u8FC7\u6EE4\u6216\u5F02\u5E38\u7EC8\u6B62", "INVALID_RESPONSE");
1194
+ }
1195
+ }
1196
+ for await (const frame of sseFrames(body, signal, idleMs)) {
1197
+ let value;
1198
+ try {
1199
+ value = JSON.parse(frame.data);
1200
+ } catch {
1201
+ throw new LlmError7("OpenDesign\uFF1A\u65E0\u6548\u7684\u6D41\u5F0F JSON", "INVALID_RESPONSE");
1202
+ }
1203
+ if (!record(value)) throw new LlmError7("OpenDesign\uFF1A\u6D41\u5F0F\u4E8B\u4EF6\u4E0D\u662F\u5BF9\u8C61", "INVALID_RESPONSE");
1204
+ const type = typeof value.type === "string" && value.type !== "" ? value.type : frame.event;
1205
+ if (!type) throw new LlmError7("OpenDesign\uFF1A\u6D41\u5F0F\u4E8B\u4EF6\u7F3A\u5C11\u7C7B\u578B", "INVALID_RESPONSE");
1206
+ const out = [];
1207
+ switch (type) {
1208
+ case "message_start": {
1209
+ const message = record(value.message) ? value.message : {};
1210
+ const counters = record(message.usage) ? message.usage : {};
1211
+ raw.input = num(counters.input_tokens) ?? raw.input;
1212
+ raw.output = num(counters.output_tokens) ?? raw.output;
1213
+ raw.cacheRead = num(counters.cache_read_input_tokens) ?? raw.cacheRead;
1214
+ raw.cacheWrite = num(counters.cache_creation_input_tokens) ?? raw.cacheWrite;
1215
+ break;
1216
+ }
1217
+ case "content_block_start": {
1218
+ const index = value.index;
1219
+ if (typeof index !== "number" || !Number.isSafeInteger(index) || index < 0) throw new LlmError7("OpenDesign\uFF1A\u6D41\u5F0F\u5185\u5BB9\u5757\u7D22\u5F15\u65E0\u6548", "INVALID_RESPONSE");
1220
+ const block = record(value.content_block) ? value.content_block : {};
1221
+ const kind = typeof block.type === "string" ? block.type : "";
1222
+ const state = { index: -1, kind: "skip", text: "", id: "", name: "", args: "", started: false };
1223
+ if (kind === "text") state.kind = "text";
1224
+ else if (kind === "thinking") state.kind = "reasoning";
1225
+ else if (kind === "tool_use") state.kind = "tool";
1226
+ if (state.kind === "tool") {
1227
+ state.id = typeof block.id === "string" ? block.id : "";
1228
+ state.name = typeof block.name === "string" ? block.name : "";
1229
+ sawTool = true;
1230
+ }
1231
+ if (state.kind === "text" && typeof block.text === "string" && block.text !== "") state.text = block.text;
1232
+ if (state.kind === "reasoning" && typeof block.thinking === "string" && block.thinking !== "") state.text = block.thinking;
1233
+ blocks.set(index, state);
1234
+ if (state.kind === "tool") {
1235
+ assign(state);
1236
+ state.started = true;
1237
+ out.push({ type: "block-start", index: state.index, blockType: "tool-call" });
1238
+ if (state.id) out.push({ type: "tool-call-delta", index: state.index, id: ToolCallId2(state.id), ...state.name ? { name: state.name } : {}, argumentsDelta: "" });
1239
+ } else if ((state.kind === "text" || state.kind === "reasoning") && state.text !== "") {
1240
+ assign(state);
1241
+ state.started = true;
1242
+ out.push({ type: "block-start", index: state.index, blockType: state.kind === "text" ? "text" : "reasoning" });
1243
+ out.push(state.kind === "text" ? { type: "text-delta", index: state.index, text: state.text } : { type: "reasoning-delta", index: state.index, text: state.text });
1244
+ }
1245
+ break;
1246
+ }
1247
+ case "content_block_delta": {
1248
+ const state = stateAt(value.index);
1249
+ const delta = record(value.delta) ? value.delta : {};
1250
+ const deltaType = typeof delta.type === "string" ? delta.type : "";
1251
+ if (deltaType === "text_delta") {
1252
+ const text2 = typeof delta.text === "string" ? delta.text : "";
1253
+ if (!text2) break;
1254
+ assign(state);
1255
+ if (!state.started) {
1256
+ state.started = true;
1257
+ out.push({ type: "block-start", index: state.index, blockType: "text" });
1258
+ }
1259
+ state.text += text2;
1260
+ out.push({ type: "text-delta", index: state.index, text: text2 });
1261
+ break;
1262
+ }
1263
+ if (deltaType === "thinking_delta") {
1264
+ const text2 = typeof delta.thinking === "string" ? delta.thinking : "";
1265
+ if (!text2) break;
1266
+ assign(state);
1267
+ if (!state.started) {
1268
+ state.started = true;
1269
+ out.push({ type: "block-start", index: state.index, blockType: "reasoning" });
1270
+ }
1271
+ state.text += text2;
1272
+ out.push({ type: "reasoning-delta", index: state.index, text: text2 });
1273
+ break;
1274
+ }
1275
+ if (deltaType === "input_json_delta") {
1276
+ if (state.kind !== "tool") throw new LlmError7("OpenDesign\uFF1A\u5DE5\u5177\u53C2\u6570\u5206\u7247\u843D\u5728\u975E\u5DE5\u5177\u5757\u4E0A", "INVALID_RESPONSE");
1277
+ const part = typeof delta.partial_json === "string" ? delta.partial_json : "";
1278
+ state.args += part;
1279
+ if (state.id) out.push({ type: "tool-call-delta", index: state.index, id: ToolCallId2(state.id), ...state.name ? { name: state.name } : {}, argumentsDelta: part });
1280
+ break;
1281
+ }
1282
+ break;
1283
+ }
1284
+ case "message_delta": {
1285
+ const delta = record(value.delta) ? value.delta : {};
1286
+ if (typeof delta.stop_reason === "string") stopReason = delta.stop_reason;
1287
+ const counters = record(value.usage) ? value.usage : {};
1288
+ raw.output = num(counters.output_tokens) ?? raw.output;
1289
+ raw.input = num(counters.input_tokens) ?? raw.input;
1290
+ raw.cacheRead = num(counters.cache_read_input_tokens) ?? raw.cacheRead;
1291
+ raw.cacheWrite = num(counters.cache_creation_input_tokens) ?? raw.cacheWrite;
1292
+ break;
1293
+ }
1294
+ case "message_stop": {
1295
+ const reason = reasonFromStop(stopReason);
1296
+ if (reason.kind === "tool-calls" && !sawTool) throw new LlmError7("OpenDesign\uFF1A\u54CD\u5E94\u58F0\u660E\u5DE5\u5177\u8C03\u7528\uFF0C\u4F46\u672A\u8FD4\u56DE\u4EFB\u4F55\u5DE5\u5177\u5757", "INVALID_RESPONSE");
1297
+ if (!count2) throw new LlmError7("OpenDesign\uFF1A\u6A21\u578B\u8FD4\u56DE\u7A7A\u54CD\u5E94", "EMPTY_RESPONSE");
1298
+ usage = anthropicUsage(raw) ?? usage;
1299
+ out.push(...close());
1300
+ if (usage) out.push({ type: "usage", usage });
1301
+ finish = reason;
1302
+ out.push({ type: "finish", reason });
1303
+ break;
1304
+ }
1305
+ case "error":
1306
+ throw anthropicError(void 0, value);
1307
+ case "ping":
1308
+ default:
1309
+ break;
1310
+ }
1311
+ for (const chunk of out) yield chunk;
1312
+ if (finish) break;
1313
+ }
1314
+ signal.throwIfAborted();
1315
+ if (!finish) throw new LlmError7("OpenDesign\uFF1A\u54CD\u5E94\u6D41\u63D0\u524D\u4E2D\u65AD\uFF0C\u672A\u6536\u5230\u7ED3\u675F\u4E8B\u4EF6", "INVALID_RESPONSE");
1316
+ }
1317
+ var messages = {
1318
+ id: "messages",
1319
+ build: buildMessagesRequest,
1320
+ stream: streamMessages
1321
+ };
1322
+
1323
+ // src/protocols/responses.ts
1324
+ import {
1325
+ LlmError as LlmError8,
1326
+ ToolCallId as ToolCallId3
1327
+ } from "@deepseek-ai/dsh-llm";
1328
+ var REASONING_EFFORTS = ["none", "minimal", "low", "medium", "high", "xhigh", "max"];
1329
+ function userContent(text2, images) {
1330
+ if (!images.length) return text2;
1331
+ return [
1332
+ ...text2 ? [{ type: "input_text", text: text2 }] : [],
1333
+ ...images.map((url) => ({ type: "input_image", image_url: url, detail: "auto" }))
1334
+ ];
1335
+ }
1336
+ function buildResponsesRequest(ctx) {
1337
+ const { options, model } = ctx;
1338
+ const instructions = [];
1339
+ const input = [];
1340
+ const lookup = ctx.images.size ? (ref) => ctx.images.get(ref.attachmentId) : void 0;
1341
+ let lifted = [];
1342
+ const flushLifted = () => {
1343
+ if (!lifted.length) return;
1344
+ input.push({ role: "user", content: userContent("(Image content from the preceding tool result.)", lifted) });
1345
+ lifted = [];
1346
+ };
1347
+ for (const message of options.messages) {
1348
+ if (message.role !== "tool") flushLifted();
1349
+ const projected = projectContent(message.content, message.role === "user" || message.role === "tool" ? lookup : void 0);
1350
+ switch (message.role) {
1351
+ case "system":
1352
+ case "developer":
1353
+ if (projected.text) instructions.push(projected.text);
1354
+ break;
1355
+ case "user":
1356
+ if (projected.text || projected.images.length) input.push({ role: "user", content: userContent(projected.text, projected.images) });
1357
+ break;
1358
+ case "assistant": {
1359
+ if (projected.text) input.push({ role: "assistant", content: projected.text });
1360
+ for (const block of message.content) {
1361
+ if (block.type === "tool-call") input.push({ type: "function_call", call_id: block.id, name: block.name, arguments: block.arguments });
1362
+ }
1363
+ break;
1364
+ }
1365
+ case "tool": {
1366
+ input.push({
1367
+ type: "function_call_output",
1368
+ call_id: message.toolCallId,
1369
+ output: projected.text || (projected.images.length ? "(Image content follows in the next user message.)" : "")
1370
+ });
1371
+ lifted.push(...projected.images);
1372
+ break;
1373
+ }
1374
+ }
1375
+ }
1376
+ flushLifted();
1377
+ if (options.system?.trim()) instructions.unshift(options.system);
1378
+ const body = { model: model.id, input, stream: true, store: false };
1379
+ if (instructions.length) body.instructions = instructions.join("\n\n");
1380
+ if (options.tools?.length) body.tools = options.tools.map((tool) => ({ type: "function", name: tool.name, description: tool.description, parameters: tool.parameters }));
1381
+ if (options.temperature !== void 0) body.temperature = options.temperature;
1382
+ if (ctx.topP !== void 0) body.top_p = ctx.topP;
1383
+ if (options.maxTokens !== void 0) {
1384
+ if (!Number.isSafeInteger(options.maxTokens) || options.maxTokens <= 0) throw new LlmError8("maxTokens \u5FC5\u987B\u662F\u6B63\u6574\u6570", "INVALID_REQUEST");
1385
+ body.max_output_tokens = options.maxTokens;
1386
+ }
1387
+ if (options.reasoningEffort !== void 0) {
1388
+ const effort = String(options.reasoningEffort);
1389
+ if (!REASONING_EFFORTS.includes(effort)) throw new LlmError8(`Responses \u534F\u8BAE\u4E0D\u63A5\u53D7\u601D\u8003\u6DF1\u5EA6\u300C${effort}\u300D`, "UNSUPPORTED_OPTION");
1390
+ body.reasoning = { effort, summary: "auto" };
1391
+ }
1392
+ if (options.stop?.length) throw new LlmError8("Responses \u534F\u8BAE\u4E0D\u652F\u6301 stop \u5E8F\u5217", "UNSUPPORTED_OPTION");
1393
+ return { url: inferenceEndpoint(ctx.baseURL, "responses"), headers: streamHeaders(ctx.apiKey), body };
1394
+ }
1395
+ function str(value) {
1396
+ return typeof value === "string" && value !== "" ? value : void 0;
1397
+ }
1398
+ function int(value) {
1399
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : void 0;
1400
+ }
1401
+ function responseUsage(response) {
1402
+ if (!record(response.usage)) return void 0;
1403
+ const usage = response.usage;
1404
+ const input = record(usage.input_tokens_details) ? usage.input_tokens_details : {};
1405
+ const output = record(usage.output_tokens_details) ? usage.output_tokens_details : {};
1406
+ return openAiUsage({
1407
+ input: typeof usage.input_tokens === "number" ? usage.input_tokens : void 0,
1408
+ output: typeof usage.output_tokens === "number" ? usage.output_tokens : void 0,
1409
+ cacheRead: typeof input.cached_tokens === "number" ? input.cached_tokens : void 0,
1410
+ cacheWrite: typeof input.cache_write_tokens === "number" ? input.cache_write_tokens : void 0,
1411
+ reasoning: typeof output.reasoning_tokens === "number" ? output.reasoning_tokens : void 0
1412
+ });
1413
+ }
1414
+ async function* streamResponses(body, signal, idleMs) {
1415
+ let nextIndex = 0;
1416
+ let count2 = 0;
1417
+ let usage;
1418
+ let finish;
1419
+ const texts = /* @__PURE__ */ new Map();
1420
+ const reasonings = /* @__PURE__ */ new Map();
1421
+ const calls = /* @__PURE__ */ new Map();
1422
+ const keyByOutput = /* @__PURE__ */ new Map();
1423
+ function textState(key) {
1424
+ let state = texts.get(key);
1425
+ if (!state) {
1426
+ state = { index: nextIndex++, text: "", started: false };
1427
+ texts.set(key, state);
1428
+ count2++;
1429
+ }
1430
+ return state;
1431
+ }
1432
+ function reasoningState(key) {
1433
+ let state = reasonings.get(key);
1434
+ if (!state) {
1435
+ state = { index: nextIndex++, text: "", started: false };
1436
+ reasonings.set(key, state);
1437
+ count2++;
1438
+ }
1439
+ return state;
1440
+ }
1441
+ function appendText(state, delta, out) {
1442
+ if (!state.started) {
1443
+ state.started = true;
1444
+ out.push({ type: "block-start", index: state.index, blockType: "text" });
1445
+ }
1446
+ state.text += delta;
1447
+ out.push({ type: "text-delta", index: state.index, text: delta });
1448
+ }
1449
+ function appendReasoning(state, delta, out) {
1450
+ if (!state.started) {
1451
+ state.started = true;
1452
+ out.push({ type: "block-start", index: state.index, blockType: "reasoning" });
1453
+ }
1454
+ state.text += delta;
1455
+ out.push({ type: "reasoning-delta", index: state.index, text: delta });
1456
+ }
1457
+ function callState(key, item, out) {
1458
+ let call = calls.get(key);
1459
+ if (!call) {
1460
+ call = { index: nextIndex++, id: "", name: "", args: "", started: false };
1461
+ calls.set(key, call);
1462
+ count2++;
1463
+ out?.push({ type: "block-start", index: call.index, blockType: "tool-call" });
1464
+ }
1465
+ if (item) {
1466
+ const id = str(item.call_id) ?? str(item.id);
1467
+ if (id && !call.id) call.id = id;
1468
+ const name2 = str(item.name);
1469
+ if (name2) call.name = name2;
1470
+ }
1471
+ return call;
1472
+ }
1473
+ function flushCall(call, delta, out) {
1474
+ if (!call.id) return;
1475
+ const name2 = call.name ? { name: call.name } : {};
1476
+ if (!call.started) {
1477
+ call.started = true;
1478
+ out.push({ type: "tool-call-delta", index: call.index, id: ToolCallId3(call.id), ...name2, argumentsDelta: call.args });
1479
+ return;
1480
+ }
1481
+ out.push({ type: "tool-call-delta", index: call.index, id: ToolCallId3(call.id), ...name2, argumentsDelta: delta });
1482
+ }
1483
+ function completeCall(call, item, out) {
1484
+ const args = item.arguments;
1485
+ let delta = "";
1486
+ if (args !== void 0) {
1487
+ if (typeof args !== "string" || !args.startsWith(call.args)) {
1488
+ throw new LlmError8("OpenDesign\uFF1A\u5DE5\u5177\u8C03\u7528\u5B8C\u6574\u53C2\u6570\u4E0E\u6D41\u5F0F\u589E\u91CF\u4E0D\u4E00\u81F4", "INVALID_RESPONSE");
1489
+ }
1490
+ delta = args.slice(call.args.length);
1491
+ call.args = args;
1492
+ }
1493
+ flushCall(call, delta, out);
1494
+ }
1495
+ function keyFor(value, kind, outputIndex) {
1496
+ const itemId = str(value.item_id) ?? (outputIndex === void 0 ? void 0 : keyByOutput.get(outputIndex));
1497
+ return `${kind}:${itemId ?? `idx:${outputIndex ?? -1}`}`;
1498
+ }
1499
+ function hydrate(output, out) {
1500
+ if (!Array.isArray(output)) return;
1501
+ for (const item of output) {
1502
+ if (!record(item)) continue;
1503
+ if (item.type === "function_call") {
1504
+ const itemId = str(item.id) ?? str(item.call_id);
1505
+ const callId = str(item.call_id) ?? itemId;
1506
+ if (!callId) continue;
1507
+ const existing = [...calls].find(([, call2]) => call2.id === callId);
1508
+ const call = callState(existing?.[0] ?? `call:${itemId ?? callId}`, item, out);
1509
+ completeCall(call, item, out);
1510
+ continue;
1511
+ }
1512
+ if (item.type === "message") {
1513
+ const key = `text:${str(item.id) ?? "unknown"}:0`;
1514
+ if (texts.has(key)) continue;
1515
+ const content2 = Array.isArray(item.content) ? item.content : [];
1516
+ const text2 = content2.map((part) => record(part) && (part.type === "output_text" || part.type === "refusal") ? str(part.text) ?? "" : "").join("");
1517
+ if (!text2) continue;
1518
+ appendText(textState(key), text2, out);
1519
+ continue;
1520
+ }
1521
+ if (item.type === "reasoning") {
1522
+ const key = `reasoning:${str(item.id) ?? "unknown"}:s0`;
1523
+ if (reasonings.has(key)) continue;
1524
+ const summary = Array.isArray(item.summary) ? item.summary : [];
1525
+ const text2 = summary.map((part) => record(part) ? str(part.text) ?? "" : "").join("");
1526
+ if (!text2) continue;
1527
+ appendReasoning(reasoningState(key), text2, out);
1528
+ }
1529
+ }
1530
+ }
1531
+ function close() {
1532
+ const out = [];
1533
+ for (const state of texts.values()) if (state.started) out.push({ type: "block-end", index: state.index, block: { type: "text", text: state.text } });
1534
+ for (const state of reasonings.values()) if (state.started) out.push({ type: "block-end", index: state.index, block: { type: "reasoning", text: state.text } });
1535
+ for (const call of calls.values()) {
1536
+ if (!call.id || !call.name) throw new LlmError8("OpenDesign\uFF1A\u5DE5\u5177\u8C03\u7528\u7F3A\u5C11\u6807\u8BC6\u6216\u540D\u79F0", "INVALID_RESPONSE");
1537
+ out.push({ type: "block-end", index: call.index, block: { type: "tool-call", id: ToolCallId3(call.id), name: call.name, arguments: call.args } });
1538
+ }
1539
+ return out.sort((a, b) => ("index" in a ? a.index : 0) - ("index" in b ? b.index : 0));
1540
+ }
1541
+ function reasonOf(type, response) {
1542
+ if (type === "response.incomplete") {
1543
+ const details = record(response.incomplete_details) ? response.incomplete_details : {};
1544
+ const why = str(details.reason) ?? "";
1545
+ if (why === "max_tokens" || why === "max_output_tokens") return { kind: "max-tokens" };
1546
+ if (why === "content_filter") throw new LlmError8("OpenDesign\uFF1A\u6A21\u578B\u54CD\u5E94\u88AB\u5185\u5BB9\u8FC7\u6EE4", "INVALID_RESPONSE");
1547
+ throw new LlmError8("OpenDesign\uFF1A\u6A21\u578B\u54CD\u5E94\u672A\u5B8C\u6210\uFF0C\u4E0A\u6E38\u4E2D\u65AD", "SERVER");
1548
+ }
1549
+ const output = Array.isArray(response.output) ? response.output : [];
1550
+ return output.some((item) => record(item) && item.type === "function_call") || calls.size > 0 ? { kind: "tool-calls" } : { kind: "stop" };
1551
+ }
1552
+ for await (const frame of sseFrames(body, signal, idleMs)) {
1553
+ if (frame.data.trim() === "[DONE]") break;
1554
+ let value;
1555
+ try {
1556
+ value = JSON.parse(frame.data);
1557
+ } catch {
1558
+ throw new LlmError8("OpenDesign\uFF1A\u65E0\u6548\u7684\u6D41\u5F0F JSON", "INVALID_RESPONSE");
1559
+ }
1560
+ if (!record(value)) throw new LlmError8("OpenDesign\uFF1A\u6D41\u5F0F\u4E8B\u4EF6\u4E0D\u662F\u5BF9\u8C61", "INVALID_RESPONSE");
1561
+ const type = str(value.type) ?? frame.event;
1562
+ if (!type) throw new LlmError8("OpenDesign\uFF1A\u6D41\u5F0F\u4E8B\u4EF6\u7F3A\u5C11\u7C7B\u578B", "INVALID_RESPONSE");
1563
+ const out = [];
1564
+ switch (type) {
1565
+ case "response.output_item.added": {
1566
+ const outputIndex = int(value.output_index);
1567
+ const item = record(value.item) ? value.item : void 0;
1568
+ if (item && outputIndex !== void 0) {
1569
+ const itemId = str(item.id) ?? str(item.call_id);
1570
+ if (itemId) keyByOutput.set(outputIndex, itemId);
1571
+ }
1572
+ if (item?.type === "function_call") callState(keyFor({ item: value.item }, "call", outputIndex), item, out);
1573
+ break;
1574
+ }
1575
+ case "response.output_text.delta":
1576
+ case "response.output_text.done": {
1577
+ if (type.endsWith(".done")) break;
1578
+ const delta = str(value.delta);
1579
+ if (!delta) break;
1580
+ const outputIndex = int(value.output_index);
1581
+ const contentIndex = int(value.content_index) ?? 0;
1582
+ const itemId = str(value.item_id) ?? (outputIndex === void 0 ? void 0 : keyByOutput.get(outputIndex));
1583
+ appendText(textState(`text:${itemId ?? `idx:${outputIndex ?? -1}`}:${contentIndex}`), delta, out);
1584
+ break;
1585
+ }
1586
+ case "response.refusal.delta": {
1587
+ const delta = str(value.delta);
1588
+ if (!delta) break;
1589
+ const outputIndex = int(value.output_index);
1590
+ const contentIndex = int(value.content_index) ?? 0;
1591
+ const itemId = str(value.item_id) ?? (outputIndex === void 0 ? void 0 : keyByOutput.get(outputIndex));
1592
+ appendText(textState(`text:${itemId ?? `idx:${outputIndex ?? -1}`}:${contentIndex}`), delta, out);
1593
+ break;
1594
+ }
1595
+ case "response.reasoning_summary_text.delta": {
1596
+ const delta = str(value.delta);
1597
+ if (!delta) break;
1598
+ const key = keyFor(value, "reasoning", int(value.output_index));
1599
+ appendReasoning(reasoningState(`${key}:s${int(value.summary_index) ?? 0}`), delta, out);
1600
+ break;
1601
+ }
1602
+ case "response.reasoning_text.delta": {
1603
+ const delta = str(value.delta);
1604
+ if (!delta) break;
1605
+ const key = keyFor(value, "reasoning", int(value.output_index));
1606
+ appendReasoning(reasoningState(`${key}:t`), delta, out);
1607
+ break;
1608
+ }
1609
+ case "response.function_call_arguments.delta": {
1610
+ const call = callState(keyFor(value, "call", int(value.output_index)), void 0, out);
1611
+ const delta = str(value.delta) ?? "";
1612
+ call.args += delta;
1613
+ flushCall(call, delta, out);
1614
+ break;
1615
+ }
1616
+ case "response.output_item.done": {
1617
+ const item = record(value.item) ? value.item : void 0;
1618
+ if (!item) break;
1619
+ const outputIndex = int(value.output_index);
1620
+ if (item.type === "function_call") {
1621
+ const call = callState(keyFor(value, "call", outputIndex), item, out);
1622
+ completeCall(call, item, out);
1623
+ } else if (item.type === "message") {
1624
+ const key = `text:${str(item.id) ?? `idx:${outputIndex ?? -1}`}:0`;
1625
+ if (!texts.has(key)) {
1626
+ const content2 = Array.isArray(item.content) ? item.content : [];
1627
+ const text2 = content2.map((part) => record(part) && (part.type === "output_text" || part.type === "refusal") ? str(part.text) ?? "" : "").join("");
1628
+ if (text2) appendText(textState(key), text2, out);
1629
+ }
1630
+ } else if (item.type === "reasoning") {
1631
+ const key = `reasoning:${str(item.id) ?? `idx:${outputIndex ?? -1}`}:s0`;
1632
+ if (!reasonings.has(key)) {
1633
+ const summary = Array.isArray(item.summary) ? item.summary : [];
1634
+ const text2 = summary.map((part) => record(part) ? str(part.text) ?? "" : "").join("");
1635
+ if (text2) appendReasoning(reasoningState(key), text2, out);
1636
+ }
1637
+ }
1638
+ break;
1639
+ }
1640
+ case "response.completed":
1641
+ case "response.incomplete":
1642
+ case "response.failed": {
1643
+ const response = record(value.response) ? value.response : {};
1644
+ if (type === "response.failed") throw inBandError(record(response.error) ? response.error : {}, "SERVER");
1645
+ usage = responseUsage(response) ?? usage;
1646
+ hydrate(response.output, out);
1647
+ const reason = reasonOf(type, response);
1648
+ if (reason.kind === "tool-calls" && calls.size === 0) throw new LlmError8("OpenDesign\uFF1A\u54CD\u5E94\u58F0\u660E\u5DE5\u5177\u8C03\u7528\uFF0C\u4F46\u672A\u8FD4\u56DE\u4EFB\u4F55\u5DE5\u5177\u5757", "INVALID_RESPONSE");
1649
+ if (!count2) throw new LlmError8("OpenDesign\uFF1A\u6A21\u578B\u8FD4\u56DE\u7A7A\u54CD\u5E94", "EMPTY_RESPONSE");
1650
+ out.push(...close());
1651
+ if (usage) out.push({ type: "usage", usage });
1652
+ finish = reason;
1653
+ out.push({ type: "finish", reason });
1654
+ break;
1655
+ }
1656
+ case "error":
1657
+ throw inBandError(value);
1658
+ default:
1659
+ break;
1660
+ }
1661
+ for (const chunk of out) yield chunk;
1662
+ if (finish) break;
1663
+ }
1664
+ signal.throwIfAborted();
1665
+ if (!finish) throw new LlmError8("OpenDesign\uFF1A\u54CD\u5E94\u6D41\u63D0\u524D\u4E2D\u65AD\uFF0C\u672A\u6536\u5230\u7ED3\u675F\u4E8B\u4EF6", "INVALID_RESPONSE");
1666
+ }
1667
+ var responses = {
1668
+ id: "responses",
1669
+ build: buildResponsesRequest,
1670
+ stream: streamResponses
1671
+ };
1672
+
1673
+ // src/protocols/index.ts
1674
+ var PROTOCOLS = {
1675
+ "chat-completions": chatCompletions,
1676
+ responses,
1677
+ messages
1678
+ };
1679
+ function protocolFor(id) {
1680
+ const protocol = PROTOCOLS[id];
1681
+ if (!protocol) throw new LlmError9(`\u672A\u77E5\u7684\u63A8\u7406\u534F\u8BAE\uFF1A${String(id)}`, "INVALID_REQUEST");
1682
+ return protocol;
1683
+ }
1684
+
701
1685
  // src/adapter.ts
702
1686
  function group(provider) {
703
1687
  if (provider === PLAN_PROVIDER) return "plan";
704
1688
  if (provider === PAID_PROVIDER) return "paid";
705
- throw new LlmError3("\u672A\u77E5\u7684 CoRouter Provider", "INVALID_REQUEST");
706
- }
707
- function modelOf(provider, id) {
708
- const model = findModel(id);
709
- if (!model || model.group !== group(provider)) throw new LlmError3("\u8BE5\u6A21\u578B\u4E0D\u5C5E\u4E8E\u5F53\u524D CoRouter Provider\uFF1B\u8BF7\u5728\u6A21\u578B\u9009\u62E9\u5668\u4E2D\u9009\u62E9\u5BF9\u5E94\u901A\u9053", "INVALID_REQUEST");
710
- return model;
1689
+ throw new LlmError10("\u672A\u77E5\u7684 CoRouter Provider", "INVALID_REQUEST");
711
1690
  }
712
- function outputReserve(model) {
713
- return model.maxOutputTokens === void 0 ? void 0 : model.contextWindow === void 0 ? model.maxOutputTokens : Math.min(model.maxOutputTokens, Math.floor(model.contextWindow / 4));
1691
+ function outputReserve(model, protocol) {
1692
+ const limit = model.maxOutputTokens ?? (protocol === "messages" ? 4096 : void 0);
1693
+ return limit === void 0 ? void 0 : model.contextWindow === void 0 ? limit : Math.min(limit, Math.floor(model.contextWindow / 4));
714
1694
  }
715
- function metadata(provider, row) {
716
- const reserve = outputReserve(row);
1695
+ function metadata(provider, row, config) {
1696
+ const setting = config.modelSettings?.[row.id];
1697
+ const reserve = setting?.maxTokens ?? outputReserve(row, config.apiProtocol ?? "chat-completions");
1698
+ const efforts = row.reasoningEfforts ?? (setting?.reasoningEffort ? [setting.reasoningEffort] : void 0);
717
1699
  return {
718
1700
  provider,
719
1701
  id: row.id,
@@ -721,7 +1703,10 @@ function metadata(provider, row) {
721
1703
  ...row.inputModalities ? { inputModalities: row.inputModalities } : {},
722
1704
  ...row.contextWindow === void 0 ? {} : { context: { contextWindow: row.contextWindow - (reserve ?? 0) } },
723
1705
  ...reserve === void 0 ? {} : { defaultMaxTokens: reserve },
724
- ...row.reasoningEfforts?.length ? { reasoning: { efforts: row.reasoningEfforts.map((id) => ({ id: ReasoningEffortId(id), name: { none: "\u5173\u95ED", minimal: "\u6700\u4F4E", low: "\u4F4E", medium: "\u4E2D", high: "\u9AD8", xhigh: "\u8F83\u9AD8", ultra: "\u6781\u9AD8", max: "\u6700\u9AD8" }[id] ?? id })) } } : {}
1706
+ ...efforts?.length ? { reasoning: {
1707
+ efforts: efforts.map((id) => ({ id: ReasoningEffortId(id), name: { none: "\u5173\u95ED", minimal: "\u6700\u4F4E", low: "\u4F4E", medium: "\u4E2D", high: "\u9AD8", xhigh: "\u8F83\u9AD8", ultra: "\u6781\u9AD8", max: "\u6700\u9AD8" }[id] ?? id })),
1708
+ ...setting?.reasoningEffort ? { defaultEffort: ReasoningEffortId(setting.reasoningEffort) } : {}
1709
+ } } : {}
725
1710
  };
726
1711
  }
727
1712
  var CoRouterAdapter = class extends LlmAdapter {
@@ -732,67 +1717,101 @@ var CoRouterAdapter = class extends LlmAdapter {
732
1717
  options;
733
1718
  #lifetime = new AbortController();
734
1719
  dispose() {
735
- this.#lifetime.abort(new LlmError3("CoRouter \u63D2\u4EF6\u5DF2\u5378\u8F7D", "ABORTED"));
1720
+ this.#lifetime.abort(new LlmError10("CoRouter \u63D2\u4EF6\u5DF2\u5378\u8F7D", "ABORTED"));
736
1721
  }
737
1722
  providerInfo(provider) {
738
1723
  return { id: provider, name: group(provider) === "plan" ? "OpenDesign CoRouter \xB7 \u5957\u9910\u8D60\u9001" : "OpenDesign CoRouter \xB7 \u4ED8\u8D39\u6A21\u578B" };
739
1724
  }
1725
+ modelOf(provider, id, config) {
1726
+ const row = (this.options.models?.() ?? MODELS).find((candidate) => candidate.id === id);
1727
+ if (!row || row.group !== group(provider)) throw new LlmError10("\u8BE5\u6A21\u578B\u4E0D\u5C5E\u4E8E\u5F53\u524D CoRouter Provider\uFF1B\u8BF7\u5237\u65B0\u6A21\u578B\u5217\u8868\u5E76\u9009\u62E9\u5BF9\u5E94\u901A\u9053", "INVALID_REQUEST");
1728
+ if (!isModelEnabled(id, config.modelSettings)) throw new LlmError10("\u8BE5\u6A21\u578B\u5DF2\u5728 CoRouter \u8BBE\u7F6E\u4E2D\u505C\u7528", "MODEL_DISABLED");
1729
+ const configured = applyModelSettings(row, config.modelSettings?.[id]);
1730
+ const output = config.modelSettings?.[id]?.maxTokens;
1731
+ if (output !== void 0 && configured.contextWindow !== void 0 && output >= configured.contextWindow) throw new LlmError10("\u6A21\u578B\u6700\u5927\u8F93\u51FA\u5FC5\u987B\u5C0F\u4E8E\u4E0A\u4E0B\u6587\u9884\u7B97\uFF0C\u8BF7\u8C03\u6574\u6A21\u578B\u53C2\u6570", "INVALID_REQUEST");
1732
+ return configured;
1733
+ }
740
1734
  async listModels(provider) {
741
- return modelsFor(group(provider)).map((row) => ({ provider, id: row.id, name: row.name, ...row.inputModalities ? { inputModalities: row.inputModalities } : {} }));
1735
+ const config = this.options.config(), selectedGroup = group(provider);
1736
+ return (this.options.models?.() ?? MODELS).filter((row) => row.group === selectedGroup && isModelEnabled(row.id, config.modelSettings)).map((row) => ({ provider, id: row.id, name: row.name, ...row.inputModalities ? { inputModalities: row.inputModalities } : {} }));
742
1737
  }
743
1738
  async resolveModel(provider, model, signal) {
744
1739
  signal?.throwIfAborted();
745
- return metadata(provider, modelOf(provider, model));
1740
+ const config = this.options.config();
1741
+ return metadata(provider, this.modelOf(provider, model, config), config);
746
1742
  }
747
1743
  async prepareCall(provider, model, signal) {
748
1744
  signal?.throwIfAborted();
749
- const config = this.options.config(), row = modelOf(provider, model);
750
- return { model: metadata(provider, row), stream: (options) => this.#stream(options, config, row) };
1745
+ const config = this.options.config(), row = this.modelOf(provider, model, config);
1746
+ return { model: metadata(provider, row, config), stream: (options) => this.#stream(options, config, row) };
751
1747
  }
752
1748
  stream(options) {
753
- return this.#stream(options, this.options.config(), modelOf(options.provider, options.model));
1749
+ const config = this.options.config();
1750
+ return this.#stream(options, config, this.modelOf(options.provider, options.model, config));
754
1751
  }
755
1752
  async *#stream(options, config, row) {
756
1753
  const signal = AbortSignal.any([combinedSignal(options.signal, config.requestTimeoutMs), this.#lifetime.signal]);
757
1754
  signal.throwIfAborted();
758
1755
  const raw = await this.options.apiKey(config.apiKeyEnv);
759
- if (!raw) throw new LlmError3("\u672A\u914D\u7F6E CoRouter API Key\uFF1B\u8BF7\u5728\u63D2\u4EF6\u8BBE\u7F6E\u4E2D\u4FDD\u5B58\u6216\u8BBE\u7F6E\u5BF9\u5E94\u73AF\u5883\u53D8\u91CF", "MISSING_CREDENTIAL");
1756
+ if (!raw) throw new LlmError10("\u672A\u914D\u7F6E CoRouter API Key\uFF1B\u8BF7\u5728\u63D2\u4EF6\u8BBE\u7F6E\u4E2D\u4FDD\u5B58\u6216\u8BBE\u7F6E\u5BF9\u5E94\u73AF\u5883\u53D8\u91CF", "MISSING_CREDENTIAL");
760
1757
  const key = assertUsableApiKey(raw, "dsh-opendesign-corouter", config.apiKeyEnv);
1758
+ const setting = config.modelSettings?.[row.id] ?? {};
1759
+ const protocolId = config.apiProtocol ?? "chat-completions";
761
1760
  const images = /* @__PURE__ */ new Map();
762
- if (row.inputModalities?.includes("image") && this.options.imageDataUrl) for (const message of options.messages.filter((row2) => row2.role === "user" || row2.role === "tool")) for (const block of message.content) {
763
- if (block.type !== "image" || block.offloaded || images.has(block.attachment.attachmentId)) continue;
764
- const url = await this.options.imageDataUrl(block.attachment, signal);
765
- if (url) images.set(block.attachment.attachmentId, url);
1761
+ const attemptedImages = /* @__PURE__ */ new Set();
1762
+ const unreadableImages = /* @__PURE__ */ new Set();
1763
+ if (row.inputModalities?.includes("image") && this.options.imageDataUrl) for (const message of options.messages.filter((item) => item.role === "user" || item.role === "tool")) for (const block of message.content) {
1764
+ if (block.type !== "image" || block.offloaded || attemptedImages.has(block.attachment.attachmentId)) continue;
1765
+ attemptedImages.add(block.attachment.attachmentId);
1766
+ try {
1767
+ const url = await this.options.imageDataUrl(block.attachment, signal);
1768
+ signal.throwIfAborted();
1769
+ if (url) images.set(block.attachment.attachmentId, url);
1770
+ else unreadableImages.add(block.attachment.attachmentId);
1771
+ } catch {
1772
+ signal.throwIfAborted();
1773
+ unreadableImages.add(block.attachment.attachmentId);
1774
+ }
766
1775
  }
767
- const messages = chatMessages(options, (ref) => images.get(ref.attachmentId), row.id.startsWith("deepseek-") || row.id.startsWith("kimi-"));
1776
+ const projectedOptions = unreadableImages.size === 0 ? options : {
1777
+ ...options,
1778
+ messages: options.messages.map((message) => message.role !== "user" && message.role !== "tool" ? message : {
1779
+ ...message,
1780
+ content: message.content.map((block) => block.type === "image" && !block.offloaded && unreadableImages.has(block.attachment.attachmentId) ? { type: "text", text: `[Image ${JSON.stringify(block.attachment.name ?? "attachment")} could not be read; attachment ${block.attachment.attachmentId}. The model's image capability has not changed.]` } : block)
1781
+ })
1782
+ };
1783
+ const messages2 = chatMessages(projectedOptions, (ref) => images.get(ref.attachmentId), row.id.startsWith("deepseek-") || row.id.startsWith("kimi-"));
768
1784
  const tools = chatTools(options);
769
- const body = { model: row.id, messages, stream: true, stream_options: { include_usage: true }, ...tools ? { tools } : {} };
770
- if (options.temperature !== void 0) body.temperature = options.temperature;
771
- const requested = options.maxTokens ?? outputReserve(row);
772
- if (requested !== void 0) {
773
- if (!Number.isInteger(requested) || requested <= 0) throw new LlmError3("maxTokens \u5FC5\u987B\u662F\u6B63\u6574\u6570", "INVALID_REQUEST");
774
- const capped = Math.min(requested, row.maxOutputTokens ?? requested);
775
- const room = row.contextWindow === void 0 ? capped : row.contextWindow - estimateTokens({ messages, tools });
776
- if (room < 1) throw new LlmError3("\u5F53\u524D\u8F93\u5165\u6CA1\u6709\u4E3A\u8F93\u51FA\u7559\u4E0B\u4E0A\u4E0B\u6587\u7A7A\u95F4", "CONTEXT_WINDOW_EXCEEDED");
777
- body.max_tokens = Math.min(capped, Math.floor(room));
778
- }
779
- if (options.reasoningEffort !== void 0) {
780
- if (row.reasoningEfforts && !row.reasoningEfforts.includes(options.reasoningEffort)) throw new LlmError3("\u8BE5\u6A21\u578B\u4E0D\u652F\u6301\u6240\u9009\u601D\u8003\u6DF1\u5EA6", "UNSUPPORTED_OPTION");
781
- body.reasoning_effort = options.reasoningEffort;
782
- }
783
- if (options.stop?.length) body.stop = options.stop;
1785
+ let maxTokens = options.maxTokens ?? setting.maxTokens ?? outputReserve(row, protocolId);
1786
+ if (maxTokens !== void 0) {
1787
+ if (!Number.isSafeInteger(maxTokens) || maxTokens <= 0) throw new LlmError10("maxTokens \u5FC5\u987B\u662F\u6B63\u6574\u6570", "INVALID_REQUEST");
1788
+ const capped = Math.min(maxTokens, row.maxOutputTokens ?? maxTokens);
1789
+ const room = row.contextWindow === void 0 ? capped : row.contextWindow - estimateTokens({ messages: messages2, tools });
1790
+ if (room < 1) throw new LlmError10("\u5F53\u524D\u8F93\u5165\u6CA1\u6709\u4E3A\u8F93\u51FA\u7559\u4E0B\u4E0A\u4E0B\u6587\u7A7A\u95F4", "CONTEXT_WINDOW_EXCEEDED");
1791
+ maxTokens = Math.min(capped, Math.floor(room));
1792
+ }
1793
+ const effort = options.reasoningEffort ?? (setting.reasoningEffort ? ReasoningEffortId(setting.reasoningEffort) : void 0);
1794
+ if (effort !== void 0 && row.reasoningEfforts && !row.reasoningEfforts.includes(effort)) throw new LlmError10("\u8BE5\u6A21\u578B\u4E0D\u652F\u6301\u6240\u9009\u601D\u8003\u6DF1\u5EA6", "UNSUPPORTED_OPTION");
1795
+ const effective = {
1796
+ ...projectedOptions,
1797
+ ...(options.temperature ?? setting.temperature) === void 0 ? {} : { temperature: options.temperature ?? setting.temperature },
1798
+ ...maxTokens === void 0 ? {} : { maxTokens },
1799
+ ...effort === void 0 ? {} : { reasoningEffort: effort }
1800
+ };
1801
+ const protocol = protocolFor(protocolId);
1802
+ const request = protocol.build({ baseURL: config.baseURL, apiKey: key, model: row, options: effective, images, ...setting.topP === void 0 ? {} : { topP: setting.topP } });
784
1803
  let response;
785
1804
  try {
786
- response = await (this.options.fetch ?? fetch)(`${config.baseURL}/chat/completions`, {
1805
+ response = await (this.options.fetch ?? fetch)(request.url, {
787
1806
  method: "POST",
788
- headers: { ...attributionHeaders(), authorization: `Bearer ${key}`, "content-type": "application/json", accept: "text/event-stream" },
789
- body: JSON.stringify(body),
1807
+ headers: request.headers,
1808
+ body: JSON.stringify(request.body),
790
1809
  signal,
791
1810
  redirect: "error"
792
1811
  });
793
1812
  } catch {
794
1813
  if (options.signal?.aborted || this.#lifetime.signal.aborted) signal.throwIfAborted();
795
- throw new LlmError3(signal.aborted ? "CoRouter \u8BF7\u6C42\u8D85\u8FC7\u603B\u8D85\u65F6" : "\u65E0\u6CD5\u8FDE\u63A5 CoRouter \u63A8\u7406\u670D\u52A1", signal.aborted ? "TIMEOUT" : "SERVER");
1814
+ throw new LlmError10(signal.aborted ? "CoRouter \u8BF7\u6C42\u8D85\u8FC7\u603B\u8D85\u65F6" : "\u65E0\u6CD5\u8FDE\u63A5 CoRouter \u63A8\u7406\u670D\u52A1", signal.aborted ? "TIMEOUT" : "SERVER");
796
1815
  }
797
1816
  if (!response.ok) {
798
1817
  let error = {};
@@ -820,25 +1839,152 @@ var CoRouterAdapter = class extends LlmAdapter {
820
1839
  }
821
1840
  throw gatewayError(response.status, error);
822
1841
  }
823
- if (!response.body) throw new LlmError3("CoRouter \u8FD4\u56DE\u7A7A\u54CD\u5E94", "EMPTY_RESPONSE");
1842
+ if (!response.body) throw new LlmError10("CoRouter \u8FD4\u56DE\u7A7A\u54CD\u5E94", "EMPTY_RESPONSE");
824
1843
  const tracker = new EmptyTurnTracker({ enabled: row.group === "plan" && config.emptyTurnGuard && Boolean(options.tools?.length), onEmptyTurn: () => this.options.onEmptyTurn?.() });
825
1844
  try {
826
- for await (const chunk of consumeSse(response.body, signal, config.streamIdleTimeoutMs)) {
1845
+ for await (const chunk of protocol.stream(response.body, signal, config.streamIdleTimeoutMs)) {
827
1846
  tracker.observe(chunk);
828
- if (chunk.type === "finish" && row.group === "plan" && config.emptyTurnGuard) {
829
- const reason = tracker.resolve(chunk.reason);
830
- yield { ...chunk, reason };
831
- } else yield chunk;
1847
+ if (chunk.type === "finish" && row.group === "plan" && config.emptyTurnGuard) yield { ...chunk, reason: tracker.resolve(chunk.reason) };
1848
+ else yield chunk;
832
1849
  }
833
1850
  } catch (error) {
834
1851
  if (options.signal?.aborted || this.#lifetime.signal.aborted) signal.throwIfAborted();
835
- if (signal.aborted) throw new LlmError3("CoRouter \u8BF7\u6C42\u8D85\u8FC7\u603B\u8D85\u65F6", "TIMEOUT");
836
- if (error instanceof LlmError3) throw error;
837
- throw new LlmError3("CoRouter \u54CD\u5E94\u6D41\u8FDE\u63A5\u4E2D\u65AD", "SERVER");
1852
+ if (signal.aborted) throw new LlmError10("CoRouter \u8BF7\u6C42\u8D85\u8FC7\u603B\u8D85\u65F6", "TIMEOUT");
1853
+ if (error instanceof LlmError10) throw error;
1854
+ throw new LlmError10("CoRouter \u54CD\u5E94\u6D41\u8FDE\u63A5\u4E2D\u65AD", "SERVER");
838
1855
  }
839
1856
  }
840
1857
  };
841
1858
 
1859
+ // src/model-discovery.ts
1860
+ import { attributionHeaders as attributionHeaders2 } from "@deepseek-ai/dsh-llm";
1861
+ var DISCOVERY_TIMEOUT_MS = 2e4;
1862
+ var MAX_DISCOVERY_BODY_BYTES = 2 * 1024 * 1024;
1863
+ var MAX_MODELS = 512;
1864
+ var MAX_MODEL_NAME_LENGTH = 128;
1865
+ var MODEL_ID = /^[A-Za-z0-9._:/-]{1,128}$/u;
1866
+ var MEDIA_MODEL_ID = /^(?:gpt-image-|nano-banana-|seedream-|imagen-|veo-|doubao-seedance-|sora-)/u;
1867
+ var MESSAGES2 = {
1868
+ url: "\u6A21\u578B\u53D1\u73B0\u5730\u5740\u65E0\u6548\uFF1A\u5FC5\u987B\u662F\u7EDD\u5BF9 HTTP(S) \u5730\u5740\uFF0C\u4E14\u4E0D\u542B\u51ED\u636E\u3001\u67E5\u8BE2\u4E32\u6216\u7247\u6BB5",
1869
+ credential: "\u6A21\u578B\u53D1\u73B0\u51ED\u636E\u7F3A\u5931\uFF0C\u6216\u542B\u6709\u4E0D\u53EF\u7528\u4E8E HTTP \u8BF7\u6C42\u5934\u7684\u5B57\u7B26",
1870
+ network: "\u65E0\u6CD5\u8FDE\u63A5\u6A21\u578B\u53D1\u73B0\u670D\u52A1",
1871
+ timeout: "\u6A21\u578B\u53D1\u73B0\u8BF7\u6C42\u8D85\u8FC7 20 \u79D2\u65F6\u9650",
1872
+ aborted: "\u6A21\u578B\u53D1\u73B0\u8BF7\u6C42\u5DF2\u53D6\u6D88",
1873
+ auth: "\u6A21\u578B\u53D1\u73B0\u8BA4\u8BC1\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5\u51ED\u636E",
1874
+ permission: "\u6A21\u578B\u53D1\u73B0\u51ED\u636E\u65E0\u6743\u8BFB\u53D6\u6A21\u578B\u5217\u8868",
1875
+ "rate-limit": "\u6A21\u578B\u53D1\u73B0\u8BF7\u6C42\u88AB\u9650\u6D41\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5",
1876
+ server: "\u6A21\u578B\u53D1\u73B0\u670D\u52A1\u6682\u65F6\u4E0D\u53EF\u7528",
1877
+ response: "\u6A21\u578B\u53D1\u73B0\u54CD\u5E94\u4E0D\u7B26\u5408 OpenAI \u6A21\u578B\u5217\u8868\u683C\u5F0F"
1878
+ };
1879
+ var ModelDiscoveryError = class extends Error {
1880
+ constructor(kind, status) {
1881
+ super(MESSAGES2[kind]);
1882
+ this.kind = kind;
1883
+ this.status = status;
1884
+ this.name = "ModelDiscoveryError";
1885
+ }
1886
+ kind;
1887
+ status;
1888
+ };
1889
+ function statusKind(status) {
1890
+ if (status === 401) return "auth";
1891
+ if (status === 403) return "permission";
1892
+ if (status === 429) return "rate-limit";
1893
+ if (status === 408 || status === 504) return "timeout";
1894
+ if (status >= 500) return "server";
1895
+ return "response";
1896
+ }
1897
+ async function boundedJson(response, signal, deadline) {
1898
+ const reader = response.body?.getReader();
1899
+ if (!reader) throw new ModelDiscoveryError("response");
1900
+ const declared = Number(response.headers.get("content-length"));
1901
+ if (Number.isFinite(declared) && declared > MAX_DISCOVERY_BODY_BYTES) {
1902
+ await reader.cancel().catch(() => void 0);
1903
+ reader.releaseLock();
1904
+ throw new ModelDiscoveryError("response");
1905
+ }
1906
+ let size = 0;
1907
+ let raw = "";
1908
+ const decoder = new TextDecoder();
1909
+ try {
1910
+ while (true) {
1911
+ const chunk = await reader.read();
1912
+ if (chunk.done) break;
1913
+ size += chunk.value.byteLength;
1914
+ if (size > MAX_DISCOVERY_BODY_BYTES) throw new ModelDiscoveryError("response");
1915
+ raw += decoder.decode(chunk.value, { stream: true });
1916
+ }
1917
+ raw += decoder.decode();
1918
+ } catch (error) {
1919
+ if (signal?.aborted) throw new ModelDiscoveryError("aborted");
1920
+ if (deadline.aborted) throw new ModelDiscoveryError("timeout");
1921
+ if (error instanceof ModelDiscoveryError) throw error;
1922
+ throw new ModelDiscoveryError("response");
1923
+ } finally {
1924
+ await reader.cancel().catch(() => void 0);
1925
+ reader.releaseLock();
1926
+ }
1927
+ try {
1928
+ return JSON.parse(raw);
1929
+ } catch {
1930
+ throw new ModelDiscoveryError("response");
1931
+ }
1932
+ }
1933
+ function normalize(body) {
1934
+ if (!record(body) || !Array.isArray(body.data)) throw new ModelDiscoveryError("response");
1935
+ if (body.data.length > MAX_MODELS) throw new ModelDiscoveryError("response");
1936
+ const seen = /* @__PURE__ */ new Set();
1937
+ const rows = [];
1938
+ for (const value of body.data) {
1939
+ if (!record(value) || typeof value.id !== "string" || !MODEL_ID.test(value.id) || ["__proto__", "constructor", "prototype"].includes(value.id)) continue;
1940
+ if (MEDIA_MODEL_ID.test(value.id) || seen.has(value.id)) continue;
1941
+ seen.add(value.id);
1942
+ const known = findModel(value.id);
1943
+ if (known) {
1944
+ rows.push({ ...known });
1945
+ continue;
1946
+ }
1947
+ const offered = typeof value.name === "string" ? value.name.trim() : "";
1948
+ const name2 = offered !== "" && offered.length <= MAX_MODEL_NAME_LENGTH ? offered : value.id;
1949
+ rows.push({ id: value.id, name: name2, group: "paid" });
1950
+ }
1951
+ return rows;
1952
+ }
1953
+ async function fetchModelCatalog(openAiBaseURL2, key, signal, fetcher) {
1954
+ let base;
1955
+ try {
1956
+ base = openAiBaseURL(assertBaseURL(openAiBaseURL2));
1957
+ } catch {
1958
+ throw new ModelDiscoveryError("url");
1959
+ }
1960
+ let credential;
1961
+ try {
1962
+ credential = safeSecret(key);
1963
+ } catch {
1964
+ throw new ModelDiscoveryError("credential");
1965
+ }
1966
+ const request = fetcher ?? fetch;
1967
+ const deadline = combinedSignal(signal, DISCOVERY_TIMEOUT_MS);
1968
+ let response;
1969
+ try {
1970
+ response = await request(`${base}/models`, {
1971
+ method: "GET",
1972
+ headers: { ...attributionHeaders2(), authorization: `Bearer ${credential}`, accept: "application/json" },
1973
+ signal: deadline,
1974
+ redirect: "error"
1975
+ });
1976
+ } catch {
1977
+ if (signal?.aborted) throw new ModelDiscoveryError("aborted");
1978
+ if (deadline.aborted) throw new ModelDiscoveryError("timeout");
1979
+ throw new ModelDiscoveryError("network");
1980
+ }
1981
+ if (!response.ok) {
1982
+ await response.body?.cancel().catch(() => void 0);
1983
+ throw new ModelDiscoveryError(statusKind(response.status), response.status);
1984
+ }
1985
+ return normalize(await boundedJson(response, signal, deadline));
1986
+ }
1987
+
842
1988
  // src/auth.ts
843
1989
  import { readFile as fsReadFile } from "node:fs/promises";
844
1990
  import { homedir as osHomedir } from "node:os";
@@ -1106,7 +2252,7 @@ var ConsoleLogin = class {
1106
2252
  job.status.jobId = job.id;
1107
2253
  job.status.startedAt = job.startedAt;
1108
2254
  this.job = job;
1109
- job.timer = setTimeout(() => this.finish(job, "cancelled", "\u767B\u5F55\u5DF2\u8D85\u8FC7\u4E94\u5206\u949F\uFF0C\u8BF7\u91CD\u65B0\u5F00\u59CB"), this.options.timeoutMs ?? DEFAULT_TIMEOUT_MS);
2255
+ job.timer = setTimeout(() => this.requestCancellation(job, "\u767B\u5F55\u5DF2\u8D85\u8FC7\u4E94\u5206\u949F\uFF0C\u8BF7\u91CD\u65B0\u5F00\u59CB"), this.options.timeoutMs ?? DEFAULT_TIMEOUT_MS);
1110
2256
  job.timer.unref?.();
1111
2257
  job.capturedAt = Promise.resolve().then(() => this.callbacks.capture(job.expectedRevision)).then((captured) => {
1112
2258
  job.captured = captured;
@@ -1126,25 +2272,31 @@ var ConsoleLogin = class {
1126
2272
  cancel(request) {
1127
2273
  const job = this.job;
1128
2274
  if (!job || job.status.jobId !== request.jobId) return this.status();
1129
- if (job.committing) {
1130
- job.cancelRequested = true;
1131
- job.controller.abort();
1132
- return this.status();
1133
- }
1134
- if (ACTIVE.has(job.status.state)) this.finish(job, "cancelled", "\u767B\u5F55\u5DF2\u53D6\u6D88\uFF0C\u672A\u4FDD\u5B58 Cookie");
2275
+ this.requestCancellation(job, "\u767B\u5F55\u5DF2\u53D6\u6D88\uFF0C\u672A\u4FDD\u5B58 Cookie");
1135
2276
  return this.status();
1136
2277
  }
1137
2278
  /** Plugin teardown: abort the attempt and close the browser. */
1138
2279
  dispose() {
1139
2280
  const job = this.job;
1140
- if (job && job.committing) {
1141
- job.cancelRequested = true;
1142
- job.controller.abort();
1143
- } else if (job && ACTIVE.has(job.status.state)) {
1144
- this.finish(job, "cancelled", "\u670D\u52A1\u5DF2\u5378\u8F7D\uFF0C\u767B\u5F55\u5DF2\u53D6\u6D88");
1145
- }
2281
+ if (job) this.requestCancellation(job, "\u670D\u52A1\u5DF2\u5378\u8F7D\uFF0C\u767B\u5F55\u5DF2\u53D6\u6D88");
1146
2282
  this.disposed = true;
1147
2283
  }
2284
+ /** Every cancellation source must let an in-flight atomic store decide the final outcome. */
2285
+ requestCancellation(job, message) {
2286
+ if (this.job !== job || !ACTIVE.has(job.status.state)) return;
2287
+ if (!job.committing) {
2288
+ this.finish(job, "cancelled", message);
2289
+ return;
2290
+ }
2291
+ if (!job.cancelRequested) {
2292
+ job.cancellationMessage = message;
2293
+ job.status.message = "\u6B63\u5728\u786E\u8BA4\u4F1A\u8BDD\u4FDD\u5B58\u7ED3\u679C";
2294
+ }
2295
+ job.cancelRequested = true;
2296
+ this.stopTimer(job);
2297
+ job.controller.abort();
2298
+ void this.close(job);
2299
+ }
1148
2300
  alive(job) {
1149
2301
  if (this.disposed || this.job !== job) return false;
1150
2302
  if (job.controller.signal.aborted && !job.committing) return false;
@@ -1182,7 +2334,7 @@ var ConsoleLogin = class {
1182
2334
  const browser = await launch(job.controller.signal);
1183
2335
  if (!browser) return null;
1184
2336
  job.browser = browser;
1185
- browser.on?.("disconnected", () => this.finish(job, "cancelled", "\u767B\u5F55\u7A97\u53E3\u5DF2\u5173\u95ED\uFF0C\u672A\u4FDD\u5B58 Cookie"));
2337
+ browser.on?.("disconnected", () => this.requestCancellation(job, "\u767B\u5F55\u7A97\u53E3\u5DF2\u5173\u95ED\uFF0C\u672A\u4FDD\u5B58 Cookie"));
1186
2338
  return browser;
1187
2339
  }
1188
2340
  async run(job) {
@@ -1199,7 +2351,7 @@ var ConsoleLogin = class {
1199
2351
  return;
1200
2352
  }
1201
2353
  const page = await context.newPage();
1202
- page.on?.("close", () => this.finish(job, "cancelled", "\u767B\u5F55\u7A97\u53E3\u5DF2\u5173\u95ED\uFF0C\u672A\u4FDD\u5B58 Cookie"));
2354
+ page.on?.("close", () => this.requestCancellation(job, "\u767B\u5F55\u7A97\u53E3\u5DF2\u5173\u95ED\uFF0C\u672A\u4FDD\u5B58 Cookie"));
1203
2355
  if (!this.alive(job)) {
1204
2356
  await this.close(job);
1205
2357
  return;
@@ -1273,7 +2425,7 @@ var ConsoleLogin = class {
1273
2425
  } catch (error) {
1274
2426
  job.committing = false;
1275
2427
  if (job.cancelRequested || job.controller.signal.aborted) {
1276
- this.finish(job, "cancelled", "\u767B\u5F55\u5DF2\u53D6\u6D88\uFF0C\u672A\u4FDD\u5B58 Cookie");
2428
+ this.finish(job, "cancelled", job.cancellationMessage ?? "\u767B\u5F55\u5DF2\u53D6\u6D88\uFF0C\u672A\u4FDD\u5B58 Cookie");
1277
2429
  return "cancelled";
1278
2430
  }
1279
2431
  if (FATAL_COMMIT_CODES.has(errorCode(error) ?? "")) {
@@ -1311,7 +2463,7 @@ async function defaultLaunch(signal) {
1311
2463
  }
1312
2464
 
1313
2465
  // src/quota.ts
1314
- import { attributionHeaders as attributionHeaders2 } from "@deepseek-ai/dsh-llm";
2466
+ import { attributionHeaders as attributionHeaders3 } from "@deepseek-ai/dsh-llm";
1315
2467
  var API_BASE = "https://amr-api.open-design.ai/api/v1";
1316
2468
  var CURRENT_URL = `${API_BASE}/workspaces/current`;
1317
2469
  var SUMMARY_URL = `${API_BASE}/billing/summary`;
@@ -1328,7 +2480,7 @@ var ConsoleError = class extends Error {
1328
2480
  async function consoleJson(url, session, signal, fetcher = fetch) {
1329
2481
  let response;
1330
2482
  try {
1331
- response = await fetcher(url, { headers: { ...attributionHeaders2(), accept: "application/json", ...session.headers }, signal: combinedSignal(signal, 2e4), redirect: "error" });
2483
+ response = await fetcher(url, { headers: { ...attributionHeaders3(), accept: "application/json", ...session.headers }, signal: combinedSignal(signal, 2e4), redirect: "error" });
1332
2484
  } catch {
1333
2485
  throw new ConsoleError("network");
1334
2486
  }
@@ -1357,16 +2509,23 @@ async function consoleJson(url, session, signal, fetcher = fetch) {
1357
2509
  await reader.cancel().catch(() => void 0);
1358
2510
  reader.releaseLock();
1359
2511
  }
2512
+ let body;
1360
2513
  try {
1361
- return JSON.parse(raw);
2514
+ body = JSON.parse(raw);
1362
2515
  } catch {
1363
2516
  throw new ConsoleError("response");
1364
2517
  }
2518
+ if (record(body) && (body.code === 401 || body.code === "401")) throw new ConsoleError("auth", 401);
2519
+ if (record(body) && (body.code === 403 || body.code === "403")) throw new ConsoleError("permission", 403);
2520
+ return body;
1365
2521
  }
1366
2522
  function number(value, negative = false) {
1367
2523
  const n = typeof value === "number" ? value : typeof value === "string" && /^-?\d+(\.\d+)?$/u.test(value.trim()) ? Number(value) : NaN;
1368
2524
  return Number.isFinite(n) && (negative || n >= 0) ? n : void 0;
1369
2525
  }
2526
+ function iso(value) {
2527
+ return typeof value === "string" && Number.isFinite(Date.parse(value)) ? new Date(value).toISOString() : void 0;
2528
+ }
1370
2529
  function normalizeQuota(body, summary) {
1371
2530
  if (!record(body) || !(typeof body.eligible === "boolean" || typeof body.tier === "string" || body.tier === null || Array.isArray(body.windows))) throw new ConsoleError("response");
1372
2531
  const windows = [];
@@ -1374,9 +2533,19 @@ function normalizeQuota(body, summary) {
1374
2533
  if (!record(row)) continue;
1375
2534
  const seconds = number(row.durationSeconds), used = number(row.usedCredits), limit = number(row.limitCredits), remaining = number(row.remainingCredits);
1376
2535
  if (seconds === void 0 || seconds <= 0 || used === void 0 || limit === void 0 || limit <= 0) continue;
1377
- windows.push({ durationSeconds: seconds, used, limit, percent: Math.min(100, used / limit * 100), ...remaining === void 0 ? {} : { remaining }, ...typeof row.resetsAt === "string" && Number.isFinite(Date.parse(row.resetsAt)) ? { resetsAt: row.resetsAt } : {} });
2536
+ const resetsAt = iso(row.resetsAt), windowStart = iso(row.windowStart);
2537
+ const resetMode = typeof row.resetMode === "string" && row.resetMode.trim() ? row.resetMode.trim() : void 0;
2538
+ windows.push({ durationSeconds: seconds, used, limit, percent: Math.min(100, used / limit * 100), ...remaining === void 0 ? {} : { remaining }, ...resetsAt === void 0 ? {} : { resetsAt }, ...windowStart === void 0 ? {} : { windowStart }, ...resetMode === void 0 ? {} : { resetMode } });
1378
2539
  }
1379
- return { windows, balances: normalizeBalances(summary), ...typeof body.tier === "string" && body.tier ? { tier: body.tier } : {}, ...typeof body.eligible === "boolean" ? { eligible: body.eligible } : {} };
2540
+ const membershipTier = record(summary) && typeof summary.membershipTier === "string" ? summary.membershipTier.trim() : "";
2541
+ const tier = typeof body.tier === "string" ? body.tier.trim() : body.tier === void 0 && body.eligible !== false ? membershipTier : "";
2542
+ const eligible = typeof body.eligible === "boolean" ? body.eligible : body.tier === null ? false : void 0;
2543
+ return { windows, balances: normalizeBalances(summary), ...normalizeSubscription(summary), ...tier ? { tier } : {}, ...eligible === void 0 ? {} : { eligible } };
2544
+ }
2545
+ function normalizeSubscription(summary) {
2546
+ if (!record(summary)) return {};
2547
+ const start = iso(summary.subscriptionCurrentPeriodStart), end = iso(summary.subscriptionCurrentPeriodEnd);
2548
+ return { ...start === void 0 ? {} : { subscriptionPeriodStart: start }, ...end === void 0 ? {} : { subscriptionPeriodEnd: end } };
1380
2549
  }
1381
2550
  function normalizeBalances(summary) {
1382
2551
  if (!record(summary)) return {};
@@ -1404,12 +2573,14 @@ async function readQuota(session, signal, fetcher = fetch) {
1404
2573
  } catch (error) {
1405
2574
  const balances = normalizeBalances(summary);
1406
2575
  if (!Object.keys(balances).length) throw error;
1407
- return { ...common, state: "partial", windows: [], balances, note: "\u94B1\u5305\u5DF2\u8BFB\u53D6\uFF1B\u5957\u9910\u8D60\u9001\u989D\u5EA6\u6682\u4E0D\u53EF\u786E\u8BA4" };
2576
+ return { ...common, state: "partial", windows: [], balances, ...normalizeSubscription(summary), note: "\u94B1\u5305\u5DF2\u8BFB\u53D6\uFF1B\u5957\u9910\u8D60\u9001\u989D\u5EA6\u6682\u4E0D\u53EF\u786E\u8BA4" };
1408
2577
  }
1409
- const missingWindows = normalized.eligible === true && !normalized.windows.length;
2578
+ const missingWindows = normalized.eligible !== false && !normalized.windows.length;
2579
+ const noSubscription = normalized.eligible === false && !normalized.windows.length;
1410
2580
  const missingBalances = personal && Object.keys(normalized.balances).length < 3;
1411
2581
  const partial = missingWindows || missingBalances || !personal;
1412
- return { ...common, ...normalized, state: partial ? "partial" : "ok", ...partial ? { note: !personal ? "\u56E2\u961F\u6216\u672A\u77E5\u5DE5\u4F5C\u533A\uFF1A\u8D26\u53F7\u94B1\u5305\u53E3\u5F84\u672A\u6838\u5B9E\uFF0C\u6682\u4E0D\u663E\u793A" : missingWindows ? "\u5957\u9910\u5B58\u5728\uFF0C\u4F46\u989D\u5EA6\u7A97\u53E3\u6682\u4E0D\u53EF\u786E\u8BA4" : "\u90E8\u5206\u94B1\u5305\u4F59\u989D\u6682\u4E0D\u53EF\u786E\u8BA4" } : {} };
2582
+ const note = !personal ? "\u56E2\u961F\u6216\u672A\u77E5\u5DE5\u4F5C\u533A\uFF1A\u8D26\u53F7\u94B1\u5305\u53E3\u5F84\u672A\u6838\u5B9E\uFF0C\u6682\u4E0D\u663E\u793A" : missingWindows ? "\u5957\u9910\u989D\u5EA6\u7A97\u53E3\u6682\u4E0D\u53EF\u786E\u8BA4" : noSubscription ? "\u8BE5\u8D26\u53F7\u6CA1\u6709 Design Plan \u8BA2\u9605\uFF1B\u94B1\u5305\u4F59\u989D\u7167\u5E38\u663E\u793A" : missingBalances ? "\u90E8\u5206\u94B1\u5305\u4F59\u989D\u6682\u4E0D\u53EF\u786E\u8BA4" : void 0;
2583
+ return { ...common, ...normalized, state: partial ? "partial" : "ok", ...note === void 0 ? {} : { note } };
1413
2584
  }
1414
2585
 
1415
2586
  // src/desktop-quota.ts
@@ -1442,31 +2613,36 @@ async function readDesktopQuota(session, signal, runner = runVela, fetcher = fet
1442
2613
  if (!record(workspace) || typeof workspace.workspaceId !== "string" || !/^[A-Za-z0-9_-]{1,64}$/u.test(workspace.workspaceId)) throw new ConsoleError("response");
1443
2614
  const funding2 = await runner(["billing", "funding-check", "--workspace-id", workspace.workspaceId, "--format", "json"], deadline, session.profile);
1444
2615
  if (!record(funding2) || !record(funding2.codingPlan)) throw new ConsoleError("response");
1445
- return { ...normalizeQuota(funding2.codingPlan), ...typeof funding2.balanceUsd === "string" ? { balance: funding2.balanceUsd } : {} };
2616
+ const generatedAt = typeof funding2.generatedAt === "string" && Number.isFinite(Date.parse(funding2.generatedAt)) ? new Date(funding2.generatedAt).toISOString() : void 0;
2617
+ return { ...normalizeQuota(funding2.codingPlan), ...typeof funding2.balanceUsd === "string" ? { balance: funding2.balanceUsd } : {}, ...generatedAt === void 0 ? {} : { generatedAt } };
1446
2618
  })();
1447
2619
  const [wallet, funding] = await Promise.allSettled([
1448
2620
  consoleJson(`${session.apiUrl}/api/v1/wallet/balance`, session, deadline, fetcher),
1449
2621
  cli
1450
2622
  ]);
2623
+ if (wallet.status === "rejected" && wallet.reason instanceof ConsoleError && wallet.reason.kind === "auth") throw wallet.reason;
1451
2624
  const walletBalances = wallet.status === "fulfilled" ? normalizeBalances(wallet.value) : {};
1452
2625
  const cliBalances = funding.status === "fulfilled" && "balance" in funding.value ? normalizeBalances({ balanceUsd: funding.value.balance }) : {};
1453
2626
  const windows = funding.status === "fulfilled" ? funding.value.windows : [];
1454
2627
  const balances = { ...cliBalances, ...walletBalances };
1455
- if (!windows.length && !Object.keys(balances).length) {
2628
+ const eligible = funding.status === "fulfilled" ? funding.value.eligible : void 0;
2629
+ const noSubscription = funding.status === "fulfilled" && eligible === false && windows.length === 0;
2630
+ if (!windows.length && !Object.keys(balances).length && !noSubscription) {
1456
2631
  if (wallet.status === "rejected" && wallet.reason instanceof ConsoleError && (wallet.reason.kind === "auth" || wallet.reason.kind === "permission")) throw wallet.reason;
1457
2632
  throw new ConsoleError("network");
1458
2633
  }
1459
2634
  const tier = (funding.status === "fulfilled" ? funding.value.tier : void 0) ?? session.plan;
1460
- const eligible = funding.status === "fulfilled" ? funding.value.eligible : void 0;
2635
+ const updatedAt = funding.status === "fulfilled" && funding.value.generatedAt || (/* @__PURE__ */ new Date()).toISOString();
2636
+ const note = noSubscription ? "\u684C\u9762\u767B\u5F55\u6001\u663E\u793A\u8BE5\u8D26\u53F7\u6CA1\u6709 Design Plan \u8BA2\u9605\uFF1B\u672A\u8BFB\u5230\u7684\u5206\u6C60\u4FDD\u6301\u672A\u77E5\u3002\u53EF\u5728\u8FDE\u63A5\u9875\u5B8C\u6210\u72EC\u7ACB\u767B\u5F55\uFF0C\u63D2\u4EF6\u4F1A\u4F18\u5148\u91C7\u7528\u53EF\u8BFB\u53D6\u7684\u5B8C\u6574\u989D\u5EA6\u3002" : !windows.length ? "\u684C\u9762\u767B\u5F55\u6001\u672A\u8FD4\u56DE\u5957\u9910\u7A97\u53E3\uFF1B\u5DF2\u8BFB\u5230\u7684\u94B1\u5305\u4F59\u989D\u7167\u5E38\u663E\u793A\uFF0C\u672A\u8BFB\u5230\u7684\u5206\u6C60\u4FDD\u6301\u672A\u77E5\u3002\u53EF\u5728\u8FDE\u63A5\u9875\u5B8C\u6210\u72EC\u7ACB\u767B\u5F55\uFF0C\u63D2\u4EF6\u4F1A\u4F18\u5148\u91C7\u7528\u53EF\u8BFB\u53D6\u7684\u5B8C\u6574\u989D\u5EA6\u3002" : "\u684C\u9762\u767B\u5F55\u6001\u90E8\u5206\u63A5\u53E3\u6743\u9650\u53D7\u9650\uFF1B\u672A\u8BFB\u5230\u7684\u5206\u6C60\u663E\u793A\u672A\u77E5\u3002\u53EF\u5728\u8FDE\u63A5\u9875\u5B8C\u6210\u72EC\u7ACB\u767B\u5F55\uFF0C\u63D2\u4EF6\u4F1A\u4F18\u5148\u91C7\u7528\u53EF\u8BFB\u53D6\u7684\u5B8C\u6574\u989D\u5EA6\u3002";
1461
2637
  return {
1462
2638
  state: "partial",
1463
2639
  source: "desktop",
1464
- updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
2640
+ updatedAt,
1465
2641
  windows,
1466
2642
  balances,
1467
2643
  ...tier ? { tier } : {},
1468
2644
  ...eligible === void 0 ? {} : { eligible },
1469
- note: "\u684C\u9762\u767B\u5F55\u6001\u90E8\u5206\u63A5\u53E3\u6743\u9650\u53D7\u9650\uFF1B\u672A\u8BFB\u5230\u7684\u5206\u6C60\u663E\u793A\u672A\u77E5\u3002\u5982\u9700\u5B8C\u6574\u5206\u6C60\uFF0C\u8BF7\u5728\u8FDE\u63A5\u9875\u767B\u5F55\u5E76\u5173\u95ED\u201C\u4F18\u5148\u684C\u9762\u767B\u5F55\u6001\u201D\u3002"
2645
+ note
1470
2646
  };
1471
2647
  }
1472
2648
 
@@ -1478,12 +2654,12 @@ var MAX_ENV_REF = 128;
1478
2654
  var MAX_TEXT = 4096;
1479
2655
  var MAX_MESSAGE = 1024;
1480
2656
  var MAX_TIER = 256;
1481
- var MAX_MODELS = 512;
2657
+ var MAX_MODELS2 = 512;
1482
2658
  var MAX_WINDOWS = 64;
1483
2659
  var MAX_API_KEY = 16384;
1484
2660
  var HTTP_ASCII = /^[\x21-\x7e]+$/u;
1485
2661
  var ENV_REF = /^[A-Za-z_][A-Za-z0-9_]*$/u;
1486
- var MODEL_ID = /^[A-Za-z0-9._:-]{1,128}$/u;
2662
+ var MODEL_ID2 = /^[A-Za-z0-9._:/-]{1,128}$/u;
1487
2663
  var LOGIN_PHASES = ["idle", "launching", "waiting", "verifying", "success", "cancelled", "error"];
1488
2664
  var QUOTA_STATES = ["ok", "partial", "need-login", "error"];
1489
2665
  var SESSION_SOURCES = ["desktop", "login", "environment"];
@@ -1509,6 +2685,15 @@ function amount(value, label, negative = false) {
1509
2685
  if (typeof value !== "number" || !Number.isFinite(value) || !negative && value < 0) throw new Error(`${label} \u5FC5\u987B\u662F${negative ? "" : "\u975E\u8D1F"}\u6709\u9650\u6570\u5B57`);
1510
2686
  return value;
1511
2687
  }
2688
+ function positiveInteger(value, label, max) {
2689
+ const result = count(value, label);
2690
+ if (result === 0 || result > max) throw new Error(`${label} \u8D85\u51FA\u8303\u56F4`);
2691
+ return result;
2692
+ }
2693
+ function parseEfforts(value) {
2694
+ if (!Array.isArray(value) || value.length > 16) throw new Error("\u6A21\u578B\u601D\u8003\u6863\u4F4D\u683C\u5F0F\u65E0\u6548");
2695
+ return value.map((item) => text(item, "\u601D\u8003\u6863\u4F4D", 32));
2696
+ }
1512
2697
  function member(value, allowed, label) {
1513
2698
  const hit = allowed.find((candidate) => candidate === value);
1514
2699
  if (hit === void 0) throw new Error(`${label} \u4E0D\u5728\u5141\u8BB8\u7684\u53D6\u503C\u5185`);
@@ -1526,7 +2711,9 @@ function parseQuotaWindow(value) {
1526
2711
  limit,
1527
2712
  ...value.remaining === void 0 ? {} : { remaining: amount(value.remaining, "\u5269\u4F59\u989D\u5EA6") },
1528
2713
  percent: Math.min(100, Math.max(0, amount(value.percent, "\u5DF2\u7528\u767E\u5206\u6BD4"))),
1529
- ...value.resetsAt === void 0 ? {} : { resetsAt: isoTime(value.resetsAt, "\u989D\u5EA6\u91CD\u7F6E\u65F6\u95F4") }
2714
+ ...value.resetsAt === void 0 ? {} : { resetsAt: isoTime(value.resetsAt, "\u989D\u5EA6\u91CD\u7F6E\u65F6\u95F4") },
2715
+ ...value.windowStart === void 0 ? {} : { windowStart: isoTime(value.windowStart, "\u989D\u5EA6\u7A97\u53E3\u5F00\u59CB\u65F6\u95F4") },
2716
+ ...value.resetMode === void 0 ? {} : { resetMode: text(value.resetMode, "\u989D\u5EA6\u5237\u65B0\u65B9\u5F0F", 128) }
1530
2717
  };
1531
2718
  }
1532
2719
  function parseQuotaSnapshot(value) {
@@ -1539,6 +2726,12 @@ function parseQuotaSnapshot(value) {
1539
2726
  state: member(value.state, QUOTA_STATES, "\u989D\u5EA6\u72B6\u6001"),
1540
2727
  ...value.source === void 0 ? {} : { source: member(value.source, SESSION_SOURCES, "\u989D\u5EA6\u6765\u6E90") },
1541
2728
  updatedAt: isoTime(value.updatedAt, "\u989D\u5EA6\u66F4\u65B0\u65F6\u95F4"),
2729
+ ...value.subscriptionPeriodStart === void 0 ? {} : { subscriptionPeriodStart: isoTime(value.subscriptionPeriodStart, "\u8BA2\u9605\u5468\u671F\u5F00\u59CB\u65F6\u95F4") },
2730
+ ...value.subscriptionPeriodEnd === void 0 ? {} : { subscriptionPeriodEnd: isoTime(value.subscriptionPeriodEnd, "\u8BA2\u9605\u5468\u671F\u7ED3\u675F\u65F6\u95F4") },
2731
+ ...value.nextRefreshAt === void 0 ? {} : { nextRefreshAt: isoTime(value.nextRefreshAt, "\u4E0B\u6B21\u81EA\u52A8\u5237\u65B0\u65F6\u95F4") },
2732
+ ...value.stale === void 0 ? {} : { stale: flag(value.stale, "\u5386\u53F2\u989D\u5EA6\u8BFB\u6570") },
2733
+ ...value.refreshFailures === void 0 ? {} : { refreshFailures: count(value.refreshFailures, "\u8FDE\u7EED\u5237\u65B0\u5931\u8D25\u6B21\u6570") },
2734
+ ...value.autoRefreshPaused === void 0 ? {} : { autoRefreshPaused: flag(value.autoRefreshPaused, "\u81EA\u52A8\u5237\u65B0\u6682\u505C\u72B6\u6001") },
1542
2735
  ...value.tier === void 0 ? {} : { tier: text(value.tier, "\u5957\u9910\u540D", MAX_TIER) },
1543
2736
  ...value.eligible === void 0 ? {} : { eligible: flag(value.eligible, "\u5957\u9910\u8D44\u683C") },
1544
2737
  windows: windows.map(parseQuotaWindow),
@@ -1565,11 +2758,15 @@ function parseLoginState(value) {
1565
2758
  function parseStatusModel(value) {
1566
2759
  if (!record(value)) throw new Error("\u6A21\u578B\u6761\u76EE\u5FC5\u987B\u662F\u5BF9\u8C61");
1567
2760
  const id = text(value.id, "\u6A21\u578B id", 128);
1568
- if (!MODEL_ID.test(id)) throw new Error("\u6A21\u578B id \u542B\u4E0D\u5141\u8BB8\u7684\u5B57\u7B26");
2761
+ if (!MODEL_ID2.test(id)) throw new Error("\u6A21\u578B id \u542B\u4E0D\u5141\u8BB8\u7684\u5B57\u7B26");
1569
2762
  return {
1570
2763
  id,
1571
2764
  name: text(value.name, "\u6A21\u578B\u540D", 128),
1572
- group: member(value.group, MODEL_GROUPS, "\u6A21\u578B\u5206\u7EC4")
2765
+ group: member(value.group, MODEL_GROUPS, "\u6A21\u578B\u5206\u7EC4"),
2766
+ ...value.enabled === void 0 ? {} : { enabled: flag(value.enabled, "\u6A21\u578B\u542F\u7528\u72B6\u6001") },
2767
+ ...value.contextWindow === void 0 ? {} : { contextWindow: positiveInteger(value.contextWindow, "\u4E0A\u4E0B\u6587\u9884\u7B97", 1e8) },
2768
+ ...value.maxOutputTokens === void 0 ? {} : { maxOutputTokens: positiveInteger(value.maxOutputTokens, "\u6700\u5927\u8F93\u51FA", 1e7) },
2769
+ ...value.reasoningEfforts === void 0 ? {} : { reasoningEfforts: parseEfforts(value.reasoningEfforts) }
1573
2770
  };
1574
2771
  }
1575
2772
  function parseStatusConfig(value) {
@@ -1580,13 +2777,18 @@ function parseStatusConfig(value) {
1580
2777
  emptyTurnGuard: flag(value.emptyTurnGuard, "\u7A7A\u8F6E\u4FDD\u62A4\u5F00\u5173"),
1581
2778
  useDesktopCredential: flag(value.useDesktopCredential, "\u684C\u9762\u767B\u5F55\u6001\u5F00\u5173"),
1582
2779
  baseURL: text(value.baseURL, "\u63A8\u7406\u5730\u5740", MAX_BASE_URL),
1583
- apiKeyEnv
2780
+ apiKeyEnv,
2781
+ ...value.apiProtocol === void 0 ? {} : { apiProtocol: member(value.apiProtocol, API_PROTOCOL_IDS, "\u63A8\u7406\u534F\u8BAE") },
2782
+ ...value.modelSettings === void 0 ? {} : { modelSettings: parseModelSettingsMap(value.modelSettings) },
2783
+ ...value.requestTimeoutMs === void 0 ? {} : { requestTimeoutMs: positiveInteger(value.requestTimeoutMs, "\u8BF7\u6C42\u8D85\u65F6", 36e5) },
2784
+ ...value.streamIdleTimeoutMs === void 0 ? {} : { streamIdleTimeoutMs: positiveInteger(value.streamIdleTimeoutMs, "\u6D41\u7A7A\u95F2\u8D85\u65F6", 18e5) },
2785
+ ...value.quotaRefreshSeconds === void 0 ? {} : { quotaRefreshSeconds: positiveInteger(value.quotaRefreshSeconds, "\u81EA\u52A8\u5237\u65B0\u95F4\u9694", 3600) }
1584
2786
  };
1585
2787
  }
1586
2788
  function parseStatus(value) {
1587
2789
  if (!record(value)) throw new Error("\u72B6\u6001\u5FC5\u987B\u662F\u5BF9\u8C61");
1588
2790
  const models = value.models;
1589
- if (!Array.isArray(models) || models.length > MAX_MODELS) throw new Error(`\u6A21\u578B\u5217\u8868\u5FC5\u987B\u662F\u6570\u7EC4\uFF08\u6700\u591A ${MAX_MODELS} \u9879\uFF09`);
2791
+ if (!Array.isArray(models) || models.length > MAX_MODELS2) throw new Error(`\u6A21\u578B\u5217\u8868\u5FC5\u987B\u662F\u6570\u7EC4\uFF08\u6700\u591A ${MAX_MODELS2} \u9879\uFF09`);
1590
2792
  const ids = /* @__PURE__ */ new Set();
1591
2793
  const parsed = models.map(parseStatusModel);
1592
2794
  for (const model of parsed) {
@@ -1601,7 +2803,10 @@ function parseStatus(value) {
1601
2803
  login: parseLoginState(value.login),
1602
2804
  config: parseStatusConfig(value.config),
1603
2805
  guardedTurns: count(value.guardedTurns, "\u5DF2\u62E6\u622A\u7A7A\u8F6E\u6570"),
1604
- models: parsed
2806
+ models: parsed,
2807
+ ...value.modelsSource === void 0 ? {} : { modelsSource: member(value.modelsSource, ["builtin", "openai"], "\u6A21\u578B\u76EE\u5F55\u6765\u6E90") },
2808
+ ...value.modelsUpdatedAt === void 0 ? {} : { modelsUpdatedAt: isoTime(value.modelsUpdatedAt, "\u6A21\u578B\u66F4\u65B0\u65F6\u95F4") },
2809
+ ...value.modelsError === void 0 ? {} : { modelsError: text(value.modelsError, "\u6A21\u578B\u66F4\u65B0\u63D0\u793A", MAX_MESSAGE) }
1605
2810
  };
1606
2811
  }
1607
2812
  function parseStartLoginRequest(value) {
@@ -1632,8 +2837,8 @@ function grantOf(value) {
1632
2837
  if (value?.kind !== "grant" || !record(value.payload) || typeof value.payload.cookie !== "string") return;
1633
2838
  return { cookie: value.payload.cookie, ...typeof value.payload.expiresAt === "number" ? { expiresAt: value.payload.expiresAt } : {} };
1634
2839
  }
1635
- var _saveApiKey_dec, _cancelLogin_dec, _loginStatus_dec, _startLogin_dec, _refreshQuota_dec, _status_dec, _a, _init;
1636
- var OpenDesignCoRouterService = class extends (_a = TypertRemoteService, _status_dec = [Remote("status")], _refreshQuota_dec = [Remote("refreshQuota")], _startLogin_dec = [Remote("startLogin")], _loginStatus_dec = [Remote("loginStatus")], _cancelLogin_dec = [Remote("cancelLogin")], _saveApiKey_dec = [Remote("saveApiKey")], _a) {
2840
+ var _saveApiKey_dec, _cancelLogin_dec, _loginStatus_dec, _startLogin_dec, _refreshQuotaNow_dec, _refreshQuota_dec, _refreshModels_dec, _status_dec, _a, _init;
2841
+ var OpenDesignCoRouterService = class extends (_a = TypertRemoteService, _status_dec = [Remote("status")], _refreshModels_dec = [Remote("refreshModels")], _refreshQuota_dec = [Remote("refreshQuota")], _refreshQuotaNow_dec = [Remote("refreshQuotaNow")], _startLogin_dec = [Remote("startLogin")], _loginStatus_dec = [Remote("loginStatus")], _cancelLogin_dec = [Remote("cancelLogin")], _saveApiKey_dec = [Remote("saveApiKey")], _a) {
1637
2842
  constructor(ctx, config, dependencies = { resolveSession, readQuota, readDesktopQuota }) {
1638
2843
  super(ctx, "openDesignCoRouter");
1639
2844
  this.dependencies = dependencies;
@@ -1646,10 +2851,18 @@ var OpenDesignCoRouterService = class extends (_a = TypertRemoteService, _status
1646
2851
  __publicField(this, "_quota", needLogin());
1647
2852
  __publicField(this, "_quotaAt", 0);
1648
2853
  __publicField(this, "_quotaConfig", "");
2854
+ __publicField(this, "_quotaIdentity");
1649
2855
  __publicField(this, "_quotaEpoch", 0);
1650
2856
  __publicField(this, "_quotaPending");
1651
2857
  __publicField(this, "_quotaController");
1652
2858
  __publicField(this, "guardedTurns", 0);
2859
+ __publicField(this, "_models", MODELS);
2860
+ __publicField(this, "_modelsUpdatedAt");
2861
+ __publicField(this, "_modelsError");
2862
+ __publicField(this, "_modelsFingerprint", "");
2863
+ __publicField(this, "_modelsEpoch", 0);
2864
+ __publicField(this, "_modelsPending");
2865
+ __publicField(this, "_modelsController");
1653
2866
  this._config = config;
1654
2867
  this._login = new ConsoleLogin({
1655
2868
  capture: async (expectedRevision) => {
@@ -1683,6 +2896,9 @@ var OpenDesignCoRouterService = class extends (_a = TypertRemoteService, _status
1683
2896
  });
1684
2897
  }
1685
2898
  dependencies;
2899
+ modelCatalog() {
2900
+ return this._models;
2901
+ }
1686
2902
  _credentials() {
1687
2903
  const service = this.ctx.get("credentials");
1688
2904
  if (!service) throw new RemoteError("opendesign/credentials-unavailable", "DSH \u51ED\u636E\u670D\u52A1\u4E0D\u53EF\u7528\uFF1B\u53EF\u5148\u4F7F\u7528\u542F\u52A8\u73AF\u5883\u53D8\u91CF", void 0);
@@ -1699,6 +2915,18 @@ var OpenDesignCoRouterService = class extends (_a = TypertRemoteService, _status
1699
2915
  const credentials = this.ctx.get("credentials"), config = this._config();
1700
2916
  const [apiRecord, sessionRecord, key] = await Promise.all([credentials?.readRecord(API_KEY), credentials?.readRecord(SESSION), this.resolveApiKey(config.apiKeyEnv)]);
1701
2917
  const state = { apiRecord, sessionRecord, key, config };
2918
+ const modelsFingerprint = hash({ key, baseURL: config.baseURL });
2919
+ if (modelsFingerprint !== this._modelsFingerprint) {
2920
+ const changed = this._modelsUpdatedAt !== void 0;
2921
+ this._modelsFingerprint = modelsFingerprint;
2922
+ this._modelsEpoch++;
2923
+ this._modelsController?.abort();
2924
+ this._modelsPending = void 0;
2925
+ this._models = MODELS;
2926
+ this._modelsUpdatedAt = void 0;
2927
+ this._modelsError = void 0;
2928
+ if (changed) this.dependencies.modelsChanged?.();
2929
+ }
1702
2930
  const fingerprint = hash(state);
1703
2931
  if (fingerprint !== this._fingerprint) {
1704
2932
  this._fingerprint = fingerprint;
@@ -1716,12 +2944,13 @@ var OpenDesignCoRouterService = class extends (_a = TypertRemoteService, _status
1716
2944
  this._quotaAt = 0;
1717
2945
  this._quotaEpoch++;
1718
2946
  this._quota = needLogin();
2947
+ this._quotaIdentity = void 0;
1719
2948
  this._quotaController?.abort();
1720
2949
  this._quotaPending = void 0;
1721
2950
  }
1722
2951
  async status() {
1723
2952
  const state = await this._state();
1724
- const quotaConfig = hash({ desktop: state.config.useDesktopCredential, session: state.sessionRecord });
2953
+ const quotaConfig = hash({ desktop: state.config.useDesktopCredential, session: state.sessionRecord, refreshSeconds: state.config.quotaRefreshSeconds });
1725
2954
  if (this._quotaConfig !== quotaConfig) {
1726
2955
  this._quotaConfig = quotaConfig;
1727
2956
  this._invalidateQuota();
@@ -1734,24 +2963,78 @@ var OpenDesignCoRouterService = class extends (_a = TypertRemoteService, _status
1734
2963
  login: this._login.status(),
1735
2964
  config: state.config,
1736
2965
  guardedTurns: this.guardedTurns,
1737
- models: MODELS.map(({ id, name: name2, group: group2 }) => ({ id, name: name2, group: group2 }))
2966
+ models: this._models.map(({ id, name: name2, group: group2, contextWindow, maxOutputTokens, reasoningEfforts }) => ({
2967
+ id,
2968
+ name: name2,
2969
+ group: group2,
2970
+ enabled: isModelEnabled(id, state.config.modelSettings),
2971
+ ...contextWindow === void 0 ? {} : { contextWindow },
2972
+ ...maxOutputTokens === void 0 ? {} : { maxOutputTokens },
2973
+ ...reasoningEfforts === void 0 ? {} : { reasoningEfforts }
2974
+ })),
2975
+ modelsSource: this._modelsUpdatedAt ? "openai" : "builtin",
2976
+ ...this._modelsUpdatedAt ? { modelsUpdatedAt: this._modelsUpdatedAt } : {},
2977
+ ...this._modelsError ? { modelsError: this._modelsError } : {}
1738
2978
  });
1739
2979
  }
2980
+ async refreshModels() {
2981
+ const state = await this._state();
2982
+ if (this._modelsPending) return this._modelsPending;
2983
+ const key = state.key;
2984
+ if (!key?.trim()) {
2985
+ this._modelsError = "\u8BF7\u5148\u914D\u7F6E API Key\uFF0C\u518D\u4ECE OpenAI \u7AEF\u53E3\u62C9\u53D6\u6A21\u578B\u3002";
2986
+ return this.status();
2987
+ }
2988
+ const epoch = this._modelsEpoch;
2989
+ const controller = new AbortController();
2990
+ this._modelsController = controller;
2991
+ const signal = AbortSignal.any([controller.signal, this._lifetime.signal]);
2992
+ const run = (async () => {
2993
+ try {
2994
+ const models = await (this.dependencies.fetchModels ?? fetchModelCatalog)(state.config.baseURL, key, signal);
2995
+ await this._state();
2996
+ if (signal.aborted || epoch !== this._modelsEpoch) return this.status();
2997
+ this._models = models;
2998
+ this._modelsUpdatedAt = (/* @__PURE__ */ new Date()).toISOString();
2999
+ this._modelsError = void 0;
3000
+ this.dependencies.modelsChanged?.();
3001
+ } catch (error) {
3002
+ if (!signal.aborted && epoch === this._modelsEpoch) this._modelsError = error instanceof ModelDiscoveryError ? error.message : "\u6A21\u578B\u5217\u8868\u66F4\u65B0\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002";
3003
+ }
3004
+ return this.status();
3005
+ })();
3006
+ this._modelsPending = run;
3007
+ try {
3008
+ return await run;
3009
+ } finally {
3010
+ if (this._modelsPending === run) this._modelsPending = void 0;
3011
+ }
3012
+ }
1740
3013
  async refreshQuota() {
3014
+ return this._refreshQuota(false);
3015
+ }
3016
+ async refreshQuotaNow() {
3017
+ return this._refreshQuota(true);
3018
+ }
3019
+ async _refreshQuota(force) {
1741
3020
  const state = await this._state();
1742
- const fingerprint = hash({ desktop: state.config.useDesktopCredential, session: state.sessionRecord });
3021
+ const fingerprint = hash({ desktop: state.config.useDesktopCredential, session: state.sessionRecord, refreshSeconds: state.config.quotaRefreshSeconds });
1743
3022
  if (fingerprint !== this._quotaConfig) {
1744
3023
  this._quotaConfig = fingerprint;
1745
3024
  this._invalidateQuota();
1746
3025
  }
1747
3026
  if (this._quotaPending) return this._quotaPending;
1748
- const cacheMs = this._quota.state === "error" || this._quota.state === "need-login" ? 5e3 : 3e4;
1749
- if (Date.now() - this._quotaAt < cacheMs) return this._quota;
3027
+ if (!force) {
3028
+ if (this._quota.autoRefreshPaused) return this._quota;
3029
+ const next = this._quota.nextRefreshAt ? Date.parse(this._quota.nextRefreshAt) : this._quotaAt + 5e3;
3030
+ if (Date.now() < next) return this._quota;
3031
+ }
1750
3032
  const epoch = this._quotaEpoch;
1751
3033
  const controller = new AbortController();
1752
3034
  this._quotaController = controller;
1753
3035
  const signal = AbortSignal.any([controller.signal, this._lifetime.signal]);
1754
- const run = this._readQuota(epoch, signal);
3036
+ const failures = force ? 0 : this._quota.refreshFailures ?? 0;
3037
+ const run = this._readQuota(epoch, signal, failures);
1755
3038
  this._quotaPending = run;
1756
3039
  void run.finally(() => {
1757
3040
  if (this._quotaPending === run) this._quotaPending = void 0;
@@ -1759,9 +3042,11 @@ var OpenDesignCoRouterService = class extends (_a = TypertRemoteService, _status
1759
3042
  });
1760
3043
  return run;
1761
3044
  }
1762
- async _readQuota(epoch, signal) {
3045
+ async _readQuota(epoch, signal, failures) {
1763
3046
  const config = this._config();
1764
3047
  let result;
3048
+ let identity;
3049
+ let refreshFailed = false;
1765
3050
  try {
1766
3051
  const authDeps = {
1767
3052
  useDesktop: config.useDesktopCredential,
@@ -1772,11 +3057,23 @@ var OpenDesignCoRouterService = class extends (_a = TypertRemoteService, _status
1772
3057
  }
1773
3058
  };
1774
3059
  const auth = await this.dependencies.resolveSession(authDeps);
3060
+ if (auth.state === "ready") identity = hash(auth.session);
1775
3061
  if (auth.state === "need-login") result = needLogin();
1776
3062
  else if (auth.session.source !== "desktop") result = await this.dependencies.readQuota(auth.session, signal);
1777
3063
  else {
1778
3064
  try {
1779
3065
  result = await this.dependencies.readDesktopQuota(auth.session, signal);
3066
+ if (result.state === "partial") {
3067
+ const fallback = await this.dependencies.resolveSession({ ...authDeps, useDesktop: false });
3068
+ if (fallback.state === "ready") {
3069
+ try {
3070
+ const complete = await this.dependencies.readQuota(fallback.session, signal);
3071
+ if (complete.state === "ok" && (complete.windows.length > 0 || complete.eligible === false)) result = { ...complete, note: `\u684C\u9762\u767B\u5F55\u6001\u4EC5\u63D0\u4F9B\u90E8\u5206\u989D\u5EA6\uFF1B\u5F53\u524D\u663E\u793A\u72EC\u7ACB\u767B\u5F55\u8D26\u53F7\u7684\u5B8C\u6574\u989D\u5EA6\u3002${complete.note ? ` ${complete.note}` : ""}` };
3072
+ } catch (error) {
3073
+ if (signal.aborted) throw error;
3074
+ }
3075
+ }
3076
+ }
1780
3077
  } catch (error) {
1781
3078
  if (!(error instanceof ConsoleError) || !["auth", "permission"].includes(error.kind)) throw error;
1782
3079
  const fallback = await this.dependencies.resolveSession({ ...authDeps, useDesktop: false });
@@ -1785,11 +3082,32 @@ var OpenDesignCoRouterService = class extends (_a = TypertRemoteService, _status
1785
3082
  }
1786
3083
  }
1787
3084
  } catch (error) {
1788
- result = { ...needLogin(), state: error instanceof ConsoleError && error.kind === "auth" ? "need-login" : "error", note: error instanceof ConsoleError ? error.message : "\u989D\u5EA6\u8BFB\u53D6\u6682\u65F6\u5931\u8D25\uFF1B\u8BF7\u68C0\u67E5\u8FDE\u63A5\u6216\u91CD\u65B0\u767B\u5F55" };
3085
+ const authFailure = error instanceof ConsoleError && error.kind === "auth";
3086
+ refreshFailed = !authFailure;
3087
+ const hasReadings = this._quota.windows.length > 0 || Object.keys(this._quota.balances).length > 0;
3088
+ if (!authFailure && identity !== void 0 && identity === this._quotaIdentity && hasReadings) {
3089
+ result = { ...this._quota, state: "partial", stale: true, note: "\u989D\u5EA6\u5237\u65B0\u5931\u8D25\uFF0C\u5F53\u524D\u663E\u793A\u4E0A\u6B21\u6210\u529F\u8BFB\u6570\u3002" };
3090
+ } else {
3091
+ result = { ...needLogin(), state: authFailure ? "need-login" : "error", note: error instanceof ConsoleError ? error.message : "\u989D\u5EA6\u8BFB\u53D6\u6682\u65F6\u5931\u8D25\uFF1B\u8BF7\u68C0\u67E5\u8FDE\u63A5\u6216\u91CD\u65B0\u767B\u5F55" };
3092
+ }
1789
3093
  }
1790
3094
  if (!signal.aborted && epoch === this._quotaEpoch) {
1791
- this._quota = result;
1792
3095
  this._quotaAt = Date.now();
3096
+ if (refreshFailed) {
3097
+ const count2 = failures + 1;
3098
+ const paused = count2 >= 6;
3099
+ const { nextRefreshAt: _previousPlan, ...previous } = result;
3100
+ result = {
3101
+ ...previous,
3102
+ refreshFailures: count2,
3103
+ autoRefreshPaused: paused,
3104
+ ...paused ? { note: `${result.note ?? "\u989D\u5EA6\u5237\u65B0\u5931\u8D25"} \u81EA\u52A8\u91CD\u8BD5\u5DF2\u6682\u505C\uFF0C\u8BF7\u70B9\u51FB\u5237\u65B0\u989D\u5EA6\u91CD\u8BD5\u3002` } : { nextRefreshAt: new Date(this._quotaAt + Math.min(6e4, 5e3 * 2 ** (count2 - 1))).toISOString() }
3105
+ };
3106
+ } else if (result.state !== "need-login") {
3107
+ result = { ...result, nextRefreshAt: new Date(this._quotaAt + (config.quotaRefreshSeconds ?? 60) * 1e3).toISOString() };
3108
+ }
3109
+ this._quota = result;
3110
+ this._quotaIdentity = identity;
1793
3111
  }
1794
3112
  return epoch === this._quotaEpoch ? result : this._quota;
1795
3113
  }
@@ -1819,7 +3137,9 @@ var OpenDesignCoRouterService = class extends (_a = TypertRemoteService, _status
1819
3137
  };
1820
3138
  _init = __decoratorStart(_a);
1821
3139
  __decorateElement(_init, 1, "status", _status_dec, OpenDesignCoRouterService);
3140
+ __decorateElement(_init, 1, "refreshModels", _refreshModels_dec, OpenDesignCoRouterService);
1822
3141
  __decorateElement(_init, 1, "refreshQuota", _refreshQuota_dec, OpenDesignCoRouterService);
3142
+ __decorateElement(_init, 1, "refreshQuotaNow", _refreshQuotaNow_dec, OpenDesignCoRouterService);
1823
3143
  __decorateElement(_init, 1, "startLogin", _startLogin_dec, OpenDesignCoRouterService);
1824
3144
  __decorateElement(_init, 1, "loginStatus", _loginStatus_dec, OpenDesignCoRouterService);
1825
3145
  __decorateElement(_init, 1, "cancelLogin", _cancelLogin_dec, OpenDesignCoRouterService);
@@ -1829,9 +3149,12 @@ function apply(ctx, config) {
1829
3149
  const current = () => readConfig(config);
1830
3150
  current();
1831
3151
  ctx.inject(["settings"], (scope) => scope.effect(() => scope.settings.configure({ auto: false }, ctx.fiber)));
1832
- const service = new OpenDesignCoRouterService(ctx, current);
3152
+ let modelsChanged = () => {
3153
+ };
3154
+ const service = new OpenDesignCoRouterService(ctx, current, { resolveSession, readQuota, readDesktopQuota, modelsChanged: () => modelsChanged() });
1833
3155
  const adapter = new CoRouterAdapter({
1834
3156
  config: current,
3157
+ models: () => service.modelCatalog(),
1835
3158
  apiKey: (ref) => service.resolveApiKey(ref),
1836
3159
  onEmptyTurn: () => {
1837
3160
  service.guardedTurns++;
@@ -1843,7 +3166,23 @@ function apply(ctx, config) {
1843
3166
  return `data:${image.ref.mediaType};base64,${Buffer.from(image.data).toString("base64")}`;
1844
3167
  }
1845
3168
  });
1846
- ctx.llm.registerAdapter([PLAN_PROVIDER, PAID_PROVIDER], adapter);
3169
+ const registration = ctx.llm.registerAdapter([PLAN_PROVIDER, PAID_PROVIDER], adapter);
3170
+ modelsChanged = () => registration.replace([PLAN_PROVIDER, PAID_PROVIDER]);
3171
+ let configFingerprint = hash(current());
3172
+ ctx.on("loader/volatile-update", () => {
3173
+ const next = hash(current());
3174
+ if (next === configFingerprint) return;
3175
+ configFingerprint = next;
3176
+ modelsChanged();
3177
+ });
3178
+ ctx.llm.registerModelDiscovery(SETTINGS_NS, async (request, signal) => {
3179
+ if (request.provider !== void 0 && ![PLAN_PROVIDER, PAID_PROVIDER].includes(request.provider)) throw new LlmError11("\u4E0D\u662F\u672C\u63D2\u4EF6\u7684\u6A21\u578B\u901A\u9053", "INVALID_REQUEST");
3180
+ const config2 = current();
3181
+ const key = request.apiKey ?? await service.resolveApiKey(config2.apiKeyEnv);
3182
+ if (!key) throw new LlmError11("\u8BF7\u5148\u914D\u7F6E CoRouter API Key", "MISSING_CREDENTIAL");
3183
+ const models = await fetchModelCatalog(config2.baseURL, key, signal);
3184
+ return models.map((row) => ({ id: row.id, name: row.name, ...row.contextWindow === void 0 ? {} : { contextWindow: row.contextWindow }, ...row.maxOutputTokens === void 0 ? {} : { maxTokens: row.maxOutputTokens }, ...row.inputModalities ? { inputModalities: row.inputModalities } : {} }));
3185
+ });
1847
3186
  ctx.llm.registerConfigurableProviders([
1848
3187
  { provider: PLAN_PROVIDER, displayName: "OpenDesign CoRouter \xB7 \u5957\u9910\u8D60\u9001", settingsNs: SETTINGS_NS, settingsPath: [], defaultBaseURL: current().baseURL },
1849
3188
  { provider: PAID_PROVIDER, displayName: "OpenDesign CoRouter \xB7 \u4ED8\u8D39\u6A21\u578B", settingsNs: SETTINGS_NS, settingsPath: [], defaultBaseURL: current().baseURL }