@eddyskywalker/dsh-chatgpt-subscription 0.28.0 → 0.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/CHANGELOG.md +51 -0
  2. package/README.md +11 -2
  3. package/lib/client.js +291 -164
  4. package/lib/client.js.map +1 -1
  5. package/lib/index.js +694 -157
  6. package/lib/types/client/CodexSubscriptionSection.d.ts.map +1 -1
  7. package/lib/types/client/antigravity/locales.d.ts +11 -2
  8. package/lib/types/client/antigravity/locales.d.ts.map +1 -1
  9. package/lib/types/client/claude/locales.d.ts +11 -2
  10. package/lib/types/client/claude/locales.d.ts.map +1 -1
  11. package/lib/types/client/command-code/locales.d.ts +11 -2
  12. package/lib/types/client/command-code/locales.d.ts.map +1 -1
  13. package/lib/types/client/common/AccountPoolSection.d.ts +10 -2
  14. package/lib/types/client/common/AccountPoolSection.d.ts.map +1 -1
  15. package/lib/types/client/common/account-pool-labels.d.ts +17 -0
  16. package/lib/types/client/common/account-pool-labels.d.ts.map +1 -1
  17. package/lib/types/client/common/format.d.ts +14 -0
  18. package/lib/types/client/common/format.d.ts.map +1 -1
  19. package/lib/types/client/common/styles.d.ts.map +1 -1
  20. package/lib/types/client/glm-coding-plan/locales.d.ts +11 -2
  21. package/lib/types/client/glm-coding-plan/locales.d.ts.map +1 -1
  22. package/lib/types/client/glm-coding-plan/useGlmCodingPlanSection.d.ts.map +1 -1
  23. package/lib/types/client/kimi-code/locales.d.ts +11 -2
  24. package/lib/types/client/kimi-code/locales.d.ts.map +1 -1
  25. package/lib/types/client/locales.d.ts +7 -1
  26. package/lib/types/client/locales.d.ts.map +1 -1
  27. package/lib/types/client/minimax-code/MinimaxCodeSection.d.ts +8 -5
  28. package/lib/types/client/minimax-code/MinimaxCodeSection.d.ts.map +1 -1
  29. package/lib/types/client/minimax-code/locales.d.ts +17 -8
  30. package/lib/types/client/minimax-code/locales.d.ts.map +1 -1
  31. package/lib/types/client/minimax-code/useMinimaxCodeSection.d.ts.map +1 -1
  32. package/lib/types/client/ollama/locales.d.ts +18 -0
  33. package/lib/types/client/ollama/locales.d.ts.map +1 -1
  34. package/lib/types/client/progress-updates/index.d.ts.map +1 -1
  35. package/lib/types/client/step-code/locales.d.ts +11 -2
  36. package/lib/types/client/step-code/locales.d.ts.map +1 -1
  37. package/lib/types/client/step-code/useStepCodeSection.d.ts.map +1 -1
  38. package/lib/types/client/workbuddy/locales.d.ts +6 -0
  39. package/lib/types/client/workbuddy/locales.d.ts.map +1 -1
  40. package/lib/types/client/workbuddy/useWorkBuddySection.d.ts.map +1 -1
  41. package/lib/types/host/antigravity/account-pool.d.ts +17 -3
  42. package/lib/types/host/antigravity/account-pool.d.ts.map +1 -1
  43. package/lib/types/host/antigravity/adapter.d.ts +7 -2
  44. package/lib/types/host/antigravity/adapter.d.ts.map +1 -1
  45. package/lib/types/host/common/context-overflow.d.ts.map +1 -1
  46. package/lib/types/host/minimax-code/adapter.d.ts +14 -0
  47. package/lib/types/host/minimax-code/adapter.d.ts.map +1 -1
  48. package/lib/types/host/minimax-code/checkin-gateway.d.ts +36 -0
  49. package/lib/types/host/minimax-code/checkin-gateway.d.ts.map +1 -1
  50. package/lib/types/host/minimax-code/checkin.d.ts +60 -2
  51. package/lib/types/host/minimax-code/checkin.d.ts.map +1 -1
  52. package/lib/types/host/minimax-code/mapper.d.ts +51 -19
  53. package/lib/types/host/minimax-code/mapper.d.ts.map +1 -1
  54. package/lib/types/host/minimax-code/model-catalog.d.ts +40 -15
  55. package/lib/types/host/minimax-code/model-catalog.d.ts.map +1 -1
  56. package/lib/types/host/minimax-code/token-store.d.ts.map +1 -1
  57. package/lib/types/host/minimax-code/types.d.ts.map +1 -1
  58. package/lib/types/host/proxy-manager.d.ts.map +1 -1
  59. package/lib/types/host/workbuddy/account-pool.d.ts +24 -0
  60. package/lib/types/host/workbuddy/account-pool.d.ts.map +1 -1
  61. package/lib/types/index.d.ts +2 -2
  62. package/lib/types/index.d.ts.map +1 -1
  63. package/lib/types/shared/account-pool-contracts.d.ts +24 -0
  64. package/lib/types/shared/account-pool-contracts.d.ts.map +1 -1
  65. package/lib/types/shared/minimax-code-contracts.d.ts +17 -3
  66. package/lib/types/shared/minimax-code-contracts.d.ts.map +1 -1
  67. package/package.json +1 -1
package/lib/index.js CHANGED
@@ -50,10 +50,9 @@ const MINIMAX_CODE_PROVIDER_NAME = "MiniMax Code(编程订阅)";
50
50
  * 设置卡片只渲染这一份列表:档位是线路级的词汇表(`isMinimaxCodeReasoningEffort`
51
51
  * 就是按它校验的),而不是单个模型的能力表。某个模型实际接受哪些档位由
52
52
  * `MinimaxCodeModelOption.reasoningEfforts` 单独给出——把两者合成一份会让卡片
53
- * 在给一个只认 `default` 的模型显示 `max` 时无从判断是模型不支持还是文案没写。
53
+ * 在给一个只认 high 的模型显示 max 时无从判断是模型不支持还是文案没写。
54
54
  */
55
55
  const MINIMAX_CODE_REASONING_EFFORTS = [
56
- "default",
57
56
  "low",
58
57
  "medium",
59
58
  "high",
@@ -802,7 +801,7 @@ async function rehydrateSnapshot(key) {
802
801
  * so a backend that flattens the envelope does not read as an empty catalog.
803
802
  */
804
803
  function parseListing(payload) {
805
- const record = asRecord$15(payload);
804
+ const record = asRecord$16(payload);
806
805
  const models = Array.isArray(payload) ? payload : record?.models;
807
806
  if (!Array.isArray(models)) return [];
808
807
  return models.flatMap((item) => parseListingEntry$1(item));
@@ -816,7 +815,7 @@ function parseListing(payload) {
816
815
  * back to a conservative answer rather than to a guess.
817
816
  */
818
817
  function parseListingEntry$1(value) {
819
- const record = asRecord$15(value);
818
+ const record = asRecord$16(value);
820
819
  if (record === void 0) return [];
821
820
  const id = asString$25(record.slug) ?? asString$25(record.id);
822
821
  if (id === void 0) return [];
@@ -852,7 +851,7 @@ function parseEfforts(value) {
852
851
  if (!Array.isArray(value)) return null;
853
852
  const out = [];
854
853
  for (const item of value) {
855
- const effort = asString$25(item) ?? asString$25(asRecord$15(item)?.effort);
854
+ const effort = asString$25(item) ?? asString$25(asRecord$16(item)?.effort);
856
855
  if (effort !== void 0 && !out.includes(effort)) out.push(effort);
857
856
  }
858
857
  return out.length > 0 ? out : null;
@@ -867,7 +866,7 @@ function parseEfforts(value) {
867
866
  function accountKeyFor$1(credentials) {
868
867
  return credentials.accountId || "default";
869
868
  }
870
- function asRecord$15(value) {
869
+ function asRecord$16(value) {
871
870
  return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
872
871
  }
873
872
  function asString$25(value) {
@@ -4423,7 +4422,7 @@ function isContextOverflow(error) {
4423
4422
  const parsed = JSON.parse(error);
4424
4423
  if (typeof parsed === "object" && parsed !== null) return isContextOverflow(parsed);
4425
4424
  } catch {}
4426
- return /\bcontext[_ -](?:length|window)[_ -](?:exceeded|overflow|limit[_ -]exceeded)\b/i.test(error) || /\b(?:maximum|max)(?: allowed| supported)? context (?:length|window)\b/i.test(error) && /\b(?:exceed(?:s|ed)?|requested|resulted in|reduce|too (?:long|large))\b/i.test(error) || /\b(?:prompt|input|request) (?:is )?too (?:long|large) for (?:the |this )?(?:model(?:'s)? )?context\b/i.test(error) || /\b(?:prompt|input) (?:is )?too long:\s*[\d,]+ tokens?\s*>\s*[\d,]+\s*(?:tokens?\s*)?maximum\b/i.test(error) || /\brequest exceeded model token limit\b/i.test(error) || /\b(?:input|prompt|request|messages?)\b.{0,40}\bexceed(?:s|ed)?\b.{0,40}\b(?:model(?:'s)? )?context (?:length|window)\b/i.test(error);
4425
+ return /\bcontext[_ -](?:length|window)[_ -](?:exceeded|overflow|limit[_ -]exceeded)\b/i.test(error) || /\bcontext window exceeds limit\b/i.test(error) || /\b(?:maximum|max)(?: allowed| supported)? context (?:length|window)\b/i.test(error) && /\b(?:exceed(?:s|ed)?|requested|resulted in|reduce|too (?:long|large))\b/i.test(error) || /\b(?:prompt|input|request) (?:is )?too (?:long|large) for (?:the |this )?(?:model(?:'s)? )?context\b/i.test(error) || /\b(?:prompt|input) (?:is )?too long:\s*[\d,]+ tokens?\s*>\s*[\d,]+\s*(?:tokens?\s*)?maximum\b/i.test(error) || /\bprompt is too long\b(?!\s+in\s+bytes)/i.test(error) || /\binput is too long\b(?!\s+in\s+bytes)/i.test(error) || /\brequest exceeded model token limit\b/i.test(error) || /\b(?:input|prompt|request|messages?)\b.{0,40}\bexceed(?:s|ed)?\b.{0,40}\b(?:model(?:'s)? )?context (?:length|window)\b/i.test(error);
4427
4426
  }
4428
4427
  if (typeof error !== "object" || error === null || Array.isArray(error)) return false;
4429
4428
  const fields = error;
@@ -4431,7 +4430,10 @@ function isContextOverflow(error) {
4431
4430
  fields.code,
4432
4431
  fields.type,
4433
4432
  fields.message,
4434
- fields.error
4433
+ fields.error,
4434
+ fields.errorReason,
4435
+ fields.status_msg,
4436
+ fields.base_resp
4435
4437
  ].some((value) => typeof value === "string" ? isContextOverflow(value) : value !== error && value !== void 0 && isContextOverflow(value));
4436
4438
  }
4437
4439
  /** Only request-rejection statuses can be reclassified as context overflow. */
@@ -7425,6 +7427,8 @@ function antigravityPoolQuota(quota) {
7425
7427
  }
7426
7428
  return poolQuota(quota.fetchedAt, windows);
7427
7429
  }
7430
+ /** How much of the upstream detail the family guard quotes in its message. */
7431
+ const MAX_DETAIL_CHARS = 500;
7428
7432
  /**
7429
7433
  * Antigravity's account pool.
7430
7434
  *
@@ -7572,11 +7576,19 @@ var AccountPoolStore = class {
7572
7576
  familyKey(accountId, family) {
7573
7577
  return `${family}\u0000${accountId}`;
7574
7578
  }
7575
- /** Cool one account down for one quota family after an upstream 429. */
7576
- markFamilyCooldown(accountId, family, durationMs, reason) {
7579
+ /**
7580
+ * Cool one account down for one quota family after an upstream 429.
7581
+ *
7582
+ * `detail` is what the upstream actually said, kept beside the cooldown it
7583
+ * caused: the pool sentence the guard raises is about the pool, and the
7584
+ * upstream statement is the only part that tells the user WHY the request
7585
+ * was refused.
7586
+ */
7587
+ markFamilyCooldown(accountId, family, durationMs, reason, detail) {
7577
7588
  this.familyCooldowns.set(this.familyKey(accountId, family), {
7578
7589
  until: Date.now() + Math.max(1e3, durationMs),
7579
- reason
7590
+ reason,
7591
+ ...detail === void 0 || detail === "" ? {} : { detail }
7580
7592
  });
7581
7593
  }
7582
7594
  /** Accounts whose allowance for this family is still cooling down. */
@@ -7590,11 +7602,26 @@ var AccountPoolStore = class {
7590
7602
  }
7591
7603
  return ids;
7592
7604
  }
7593
- shortestFamilyCooldownMs(family, now = Date.now()) {
7594
- let shortest = Infinity;
7605
+ /**
7606
+ * The family cooldown that expires soonest, with the account it belongs to.
7607
+ *
7608
+ * The wait the guard reports and the upstream detail it quotes come from
7609
+ * this ONE entry: two separate scans could pick different accounts, and the
7610
+ * message would then name a wait for one cooldown and a reason for another.
7611
+ */
7612
+ soonestFamilyCooldown(family, now = Date.now()) {
7613
+ let soonest;
7595
7614
  const prefix = `${family}\u0000`;
7596
- for (const [key, entry] of this.familyCooldowns) if (key.startsWith(prefix) && entry.until > now) shortest = Math.min(shortest, entry.until - now);
7597
- return Number.isFinite(shortest) ? shortest : null;
7615
+ for (const [key, entry] of this.familyCooldowns) {
7616
+ if (!key.startsWith(prefix) || entry.until <= now) continue;
7617
+ if (soonest === void 0 || entry.until < soonest.until) soonest = {
7618
+ accountId: key.slice(prefix.length),
7619
+ until: entry.until,
7620
+ reason: entry.reason,
7621
+ ...entry.detail === void 0 ? {} : { detail: entry.detail }
7622
+ };
7623
+ }
7624
+ return soonest ?? null;
7598
7625
  }
7599
7626
  /**
7600
7627
  * {@link getEffectiveAccount} for one quota family: accounts whose allowance
@@ -7611,8 +7638,11 @@ var AccountPoolStore = class {
7611
7638
  return await this.getEffectiveAccount(exclude, fetchFn);
7612
7639
  } catch (error) {
7613
7640
  if (cooling.size === 0 || !(error instanceof LlmError) || error.code !== "RATE_LIMIT") throw error;
7614
- const wait = this.shortestFamilyCooldownMs(family);
7615
- throw new LlmError(`全部可用 Antigravity 账号的 ${family === "gemini" ? "Gemini" : "Claude / GPT"} 配额均处于限制或冷却中 (429)。` + (wait === null ? "" : `最短预计在 ${Math.ceil(wait / 6e4)} 分钟后解除冷却。`) + "其他模型族的配额不受影响,可切换到其他模型族继续使用。", "RATE_LIMIT", {
7641
+ const soonest = this.soonestFamilyCooldown(family);
7642
+ const wait = soonest === null ? null : soonest.until - Date.now();
7643
+ const label = family === "gemini" ? "Gemini" : "Claude / GPT";
7644
+ const detail = soonest?.detail;
7645
+ throw new LlmError(`全部可用 Antigravity 账号的 ${label} 配额均处于限制或冷却中 (429)。` + (wait === null ? "" : `最短预计在 ${Math.ceil(wait / 6e4)} 分钟后解除冷却。`) + (detail === void 0 || detail === "" ? "" : `上游返回:${detail.length > MAX_DETAIL_CHARS ? `${detail.slice(0, MAX_DETAIL_CHARS)}…` : detail}`) + "其他模型族的配额不受影响,可切换到其他模型族继续使用。", "RATE_LIMIT", {
7616
7646
  status: 429,
7617
7647
  cause: error
7618
7648
  });
@@ -7635,6 +7665,18 @@ function resolveDefaultReasoningEffort$4(efforts, configuredEffort) {
7635
7665
  if (efforts.includes("low")) return ReasoningEffortId("low");
7636
7666
  if (efforts.length > 0) return ReasoningEffortId(efforts[0]);
7637
7667
  }
7668
+ /** How much of an upstream error body is quoted back to the user. */
7669
+ const MAX_UPSTREAM_BODY_CHARS = 500;
7670
+ /**
7671
+ * The upstream error body, short enough to read in a notice.
7672
+ *
7673
+ * A 429 body is small when the service states a reset time in it and can be an
7674
+ * arbitrary blob when it does not, so the excerpt is what a notice carries.
7675
+ */
7676
+ function upstreamBodyExcerpt(body) {
7677
+ const text = body.trim();
7678
+ return text.length > MAX_UPSTREAM_BODY_CHARS ? `${text.slice(0, MAX_UPSTREAM_BODY_CHARS)}…` : text;
7679
+ }
7638
7680
  /**
7639
7681
  * Milliseconds in one duration string: protobuf JSON (`"12.5s"`) or Go form
7640
7682
  * (`"1h2m3.5s"`, `"373.8ms"`). Undefined when the string is neither.
@@ -7894,6 +7936,7 @@ var AntigravityAdapter = class extends LlmAdapter {
7894
7936
  ...antigravityHeaders(token),
7895
7937
  ...model.id.startsWith("claude-") ? { "anthropic-beta": "interleaved-thinking-2025-05-14" } : {}
7896
7938
  };
7939
+ let firstResponse;
7897
7940
  for (const endpoint of endpointCandidates()) try {
7898
7941
  let sentBody = body;
7899
7942
  response = await fetchFn(`${endpoint}/v1internal:streamGenerateContent?alt=sse`, {
@@ -7959,23 +8002,34 @@ var AntigravityAdapter = class extends LlmAdapter {
7959
8002
  });
7960
8003
  }
7961
8004
  }
8005
+ if (firstResponse === void 0) firstResponse = response;
7962
8006
  if (response.ok || response.status === 400) break;
7963
8007
  if (response.status === 404) break;
8008
+ if (response.status === 429) {
8009
+ response = firstResponse;
8010
+ break;
8011
+ }
8012
+ response = firstResponse;
7964
8013
  } catch (err) {
7965
8014
  if (signal.aborted) throw new LlmError("Antigravity request aborted", "ABORTED", { cause: err });
7966
8015
  }
7967
8016
  if (response && response.status === 429) {
7968
8017
  const errorBody = await response.clone().text().catch(() => "");
7969
- const cooldownMs = retryDelayMs(response.headers, errorBody) ?? 9e5;
7970
- this.accountPool.markFamilyCooldown(account.id, family, cooldownMs, "429 Rate Limit");
7971
- if (await this.accountPool.hasAnotherAvailableAccountFor(family, triedAccountIds)) continue;
8018
+ const statedWaitMs = retryDelayMs(response.headers, errorBody);
8019
+ if (statedWaitMs !== void 0) {
8020
+ this.accountPool.markFamilyCooldown(account.id, family, statedWaitMs, "429 Rate Limit", upstreamBodyExcerpt(errorBody));
8021
+ if (await this.accountPool.hasAnotherAvailableAccountFor(family, triedAccountIds)) continue;
8022
+ }
7972
8023
  }
7973
8024
  break;
7974
8025
  }
7975
8026
  if (!response || !response.ok) {
7976
8027
  const status = response?.status ?? 500;
7977
- const errText = await response?.text().catch(() => "");
7978
- if (status === 429) throw new LlmError(`Antigravity 账号配额已耗尽或请求受限 (429 RESOURCE_EXHAUSTED)。请在插件设置页查看配额剩余百分比及重置倒计时。原始响应: ${errText || "No response"}`, "RATE_LIMIT", { status: 429 });
8028
+ const errText = await response?.text().catch(() => "") ?? "";
8029
+ if (response && response.status === 429) {
8030
+ if (retryDelayMs(response.headers, errText) !== void 0) throw new LlmError(`Antigravity 账号配额已耗尽或请求受限 (429 RESOURCE_EXHAUSTED)。请在插件设置页查看配额剩余百分比及重置倒计时。原始响应: ${errText || "No response"}`, "RATE_LIMIT", { status: 429 });
8031
+ throw new LlmError(`Antigravity 上游返回 429 (RESOURCE_EXHAUSTED) 但未给出重置时间,因此按瞬时限流处理,不视为账号配额耗尽,也不会冷却该账号。原始响应: ${upstreamBodyExcerpt(errText) || "No response"}`, "SERVER", { status: 429 });
8032
+ }
7979
8033
  throw new LlmError(`Antigravity API error (${status}): ${errText || "No response"}`, isHttpContextOverflow(status, errText) ? CONTEXT_OVERFLOW_CODE : "PROVIDER_ERROR", { status });
7980
8034
  }
7981
8035
  if (!response.body) throw new LlmError("Antigravity returned empty response body", "PROVIDER_ERROR");
@@ -8333,6 +8387,7 @@ async function requestImage(accountPool, store, fetchFn, prompt, aspectRatio, si
8333
8387
  tried.add(account.id);
8334
8388
  const body = JSON.stringify(buildImageRequest(prompt, projectId || "antigravity-default", aspectRatio));
8335
8389
  let response;
8390
+ let firstResponse;
8336
8391
  for (const endpoint of endpointCandidates()) try {
8337
8392
  response = await fetchFn(`${endpoint}/v1internal:streamGenerateContent?alt=sse`, {
8338
8393
  method: "POST",
@@ -8340,7 +8395,13 @@ async function requestImage(accountPool, store, fetchFn, prompt, aspectRatio, si
8340
8395
  body,
8341
8396
  signal
8342
8397
  });
8398
+ if (firstResponse === void 0) firstResponse = response;
8343
8399
  if (response.ok || response.status === 400 || response.status === 404) break;
8400
+ if (response.status === 429) {
8401
+ response = firstResponse;
8402
+ break;
8403
+ }
8404
+ response = firstResponse;
8344
8405
  } catch (error) {
8345
8406
  if (signal.aborted) throw error;
8346
8407
  }
@@ -8348,10 +8409,13 @@ async function requestImage(accountPool, store, fetchFn, prompt, aspectRatio, si
8348
8409
  const status = response?.status;
8349
8410
  const errorText = await response?.text().catch(() => "") ?? "";
8350
8411
  if (status === 429 && response) {
8351
- const cooldownMs = retryDelayMs(response.headers, errorText) ?? 9e5;
8352
- accountPool.markFamilyCooldown(account.id, "gemini", cooldownMs, "429 Rate Limit");
8353
- if (await accountPool.hasAnotherAvailableAccountFor("gemini", tried)) continue;
8354
- throw new HarnessError(`Antigravity image generation was rate limited. ${errorText}`.trim(), "ANTIGRAVITY_IMAGE_RATE_LIMITED");
8412
+ const statedWaitMs = retryDelayMs(response.headers, errorText);
8413
+ if (statedWaitMs !== void 0) {
8414
+ accountPool.markFamilyCooldown(account.id, "gemini", statedWaitMs, "429 Rate Limit", upstreamBodyExcerpt(errorText));
8415
+ if (await accountPool.hasAnotherAvailableAccountFor("gemini", tried)) continue;
8416
+ throw new HarnessError(`Antigravity image generation was rate limited. ${errorText}`.trim(), "ANTIGRAVITY_IMAGE_RATE_LIMITED");
8417
+ }
8418
+ throw new HarnessError(`Antigravity image generation was rate limited (429) without a stated reset time, so it is handled as a transient rate limit rather than a spent quota. ${upstreamBodyExcerpt(errorText)}`.trim(), "ANTIGRAVITY_IMAGE_RATE_LIMITED");
8355
8419
  }
8356
8420
  throw new HarnessError(`Antigravity image generation failed (${status ?? "no response"}).${errorText ? ` ${errorText}` : ""}`, "ANTIGRAVITY_IMAGE_FAILED");
8357
8421
  }
@@ -8475,10 +8539,24 @@ function readSource(value) {
8475
8539
  ...publishedAt !== null ? { publishedAt } : {}
8476
8540
  };
8477
8541
  }
8542
+ /**
8543
+ * The answer text, as the endpoint actually returns it.
8544
+ *
8545
+ * The live `/alpha/search` response carries three top-level fields:
8546
+ * `encrypted_output` (an opaque blob), `output` (the answer, with inline
8547
+ * citation markers) and `results` (the source list). The names above are the
8548
+ * older shapes the endpoint has used; `output` is the one it uses now, and
8549
+ * leaving it out meant every real search came back with NO answer text - the
8550
+ * harness tool then reported a bare source list, which reads as a working
8551
+ * search that simply found nothing to say.
8552
+ *
8553
+ * `encrypted_output` is deliberately not consulted: it is the encrypted
8554
+ * counterpart of `output`, not a fallback for it.
8555
+ */
8478
8556
  function readContent(data) {
8479
8557
  const root = record$4(data);
8480
8558
  if (root === null) return void 0;
8481
- return string$1(root.content) ?? string$1(root.output_text) ?? string$1(root.summary) ?? void 0;
8559
+ return string$1(root.content) ?? string$1(root.output_text) ?? string$1(root.summary) ?? string$1(root.output) ?? void 0;
8482
8560
  }
8483
8561
  function dedupeSources(sources) {
8484
8562
  const seen = /* @__PURE__ */ new Set();
@@ -8511,6 +8589,22 @@ function isSource(value) {
8511
8589
  //#endregion
8512
8590
  //#region src/host/proxy-manager.ts
8513
8591
  const SYSTEM_PROXY_CACHE_TTL_MS = 5e3;
8592
+ /**
8593
+ * How long a proxied connection may take to establish.
8594
+ *
8595
+ * undici's default is 10 seconds, and it covers the TLS handshake with the
8596
+ * ORIGIN, not merely the hop to the proxy: a proxy whose route to
8597
+ * chatgpt.com goes through a slow node measured 1.2s to 18s for that
8598
+ * handshake alone, and the request after it took 31s end to end. The default
8599
+ * therefore killed requests that would have succeeded seconds later, and it
8600
+ * killed them as an opaque `fetch failed` - the underlying
8601
+ * `UND_ERR_CONNECT_TIMEOUT` never named the proxy, so the failure read as
8602
+ * "the search is broken" rather than "the line was slow".
8603
+ *
8604
+ * 60 seconds covers the measured worst case with room to spare. It bounds the
8605
+ * CONNECT phase only; the request itself keeps undici's own timeouts.
8606
+ */
8607
+ const PROXY_CONNECT_TIMEOUT_MS = 6e4;
8514
8608
  function normalizeProxyUrl(rawUrl) {
8515
8609
  const trimmed = rawUrl.trim();
8516
8610
  if (!trimmed) return "";
@@ -8664,7 +8758,10 @@ var ProxyManager = class {
8664
8758
  getOrCreateAgent(proxyUrl) {
8665
8759
  let agent = this.agents.get(proxyUrl);
8666
8760
  if (!agent) {
8667
- agent = new ProxyAgent(proxyUrl);
8761
+ agent = new ProxyAgent({
8762
+ uri: proxyUrl,
8763
+ connectTimeout: PROXY_CONNECT_TIMEOUT_MS
8764
+ });
8668
8765
  this.agents.set(proxyUrl, agent);
8669
8766
  }
8670
8767
  return agent;
@@ -14227,7 +14324,7 @@ function timeoutSignal$4(signal, ms) {
14227
14324
  function isRecord$38(value) {
14228
14325
  return typeof value === "object" && value !== null && !Array.isArray(value);
14229
14326
  }
14230
- function asRecord$14(value) {
14327
+ function asRecord$15(value) {
14231
14328
  return isRecord$38(value) ? value : void 0;
14232
14329
  }
14233
14330
  function asString$23(value) {
@@ -14306,16 +14403,16 @@ const ORG_KEYS = [
14306
14403
  ];
14307
14404
  /** Map `/alpha/whoami` (or a stored key's own facts) onto the public account DTO. */
14308
14405
  function parseWhoami(payload, fallback = {}) {
14309
- const root = asRecord$14(payload) ?? {};
14310
- const data = asRecord$14(root.data) ?? root;
14311
- const user = asRecord$14(deepValue(data, USER_KEYS)) ?? {};
14312
- const org = asRecord$14(deepValue(data, ORG_KEYS)) ?? {};
14313
- const key = asRecord$14(deepValue(data, [
14406
+ const root = asRecord$15(payload) ?? {};
14407
+ const data = asRecord$15(root.data) ?? root;
14408
+ const user = asRecord$15(deepValue(data, USER_KEYS)) ?? {};
14409
+ const org = asRecord$15(deepValue(data, ORG_KEYS)) ?? {};
14410
+ const key = asRecord$15(deepValue(data, [
14314
14411
  "apiKey",
14315
14412
  "key",
14316
14413
  "credential"
14317
14414
  ])) ?? {};
14318
- const subscription = asRecord$14(deepValue(data, [
14415
+ const subscription = asRecord$15(deepValue(data, [
14319
14416
  "subscription",
14320
14417
  "plan",
14321
14418
  "tier"
@@ -14419,14 +14516,14 @@ function humanizeWindowKey$1(key) {
14419
14516
  * — so they are read here first, by name.
14420
14517
  */
14421
14518
  function parseWindowLimits(payload, consumed = []) {
14422
- const root = asRecord$14(payload) ?? {};
14423
- const limits = asRecord$14(deepValue(asRecord$14(root.data) ?? root, ["windowLimits", "window_limits"]));
14519
+ const root = asRecord$15(payload) ?? {};
14520
+ const limits = asRecord$15(deepValue(asRecord$15(root.data) ?? root, ["windowLimits", "window_limits"]));
14424
14521
  if (limits === void 0) return [];
14425
14522
  const keys = [...WINDOW_ORDER$1.filter((key) => limits[key] !== void 0), ...Object.keys(limits).filter((key) => !WINDOW_ORDER$1.includes(key))];
14426
14523
  const meters = [];
14427
14524
  for (const key of keys) {
14428
14525
  if (key === "limited" || key === "exceeded") continue;
14429
- const record = asRecord$14(limits[key]);
14526
+ const record = asRecord$15(limits[key]);
14430
14527
  if (record === void 0) continue;
14431
14528
  const cap = firstNumber$2(record, [
14432
14529
  "cap",
@@ -14460,8 +14557,8 @@ function parseWindowLimits(payload, consumed = []) {
14460
14557
  * source for a remaining balance.
14461
14558
  */
14462
14559
  function parseCreditBalances(payload, consumed = []) {
14463
- const root = asRecord$14(payload) ?? {};
14464
- const credits = asRecord$14(deepValue(asRecord$14(root.data) ?? root, ["credits"]));
14560
+ const root = asRecord$15(payload) ?? {};
14561
+ const credits = asRecord$15(deepValue(asRecord$15(root.data) ?? root, ["credits"]));
14465
14562
  if (credits === void 0) return [];
14466
14563
  const monthly = firstNumber$2(credits, ["monthlyCredits", "monthly_credits"]);
14467
14564
  const purchased = firstNumber$2(credits, ["purchasedCredits", "purchased_credits"]);
@@ -14508,13 +14605,13 @@ function parsePlanId(payloads) {
14508
14605
  * subscription's remaining credits as usable would be worse than showing none.
14509
14606
  */
14510
14607
  function parseSubscriptionStatus(payload) {
14511
- const root = asRecord$14(payload) ?? {};
14512
- return asString$23(deepValue(asRecord$14(root.data) ?? root, ["status"])) ?? null;
14608
+ const root = asRecord$15(payload) ?? {};
14609
+ return asString$23(deepValue(asRecord$15(root.data) ?? root, ["status"])) ?? null;
14513
14610
  }
14514
14611
  /** End of the current billing period, in Unix milliseconds. */
14515
14612
  function parseSubscriptionPeriodEnd(payload) {
14516
- const root = asRecord$14(payload) ?? {};
14517
- return parseTimestamp$1(deepValue(asRecord$14(root.data) ?? root, ["currentPeriodEnd", "current_period_end"]));
14613
+ const root = asRecord$15(payload) ?? {};
14614
+ return parseTimestamp$1(deepValue(asRecord$15(root.data) ?? root, ["currentPeriodEnd", "current_period_end"]));
14518
14615
  }
14519
14616
  /**
14520
14617
  * Turn one billing/usage payload into meters.
@@ -14731,9 +14828,9 @@ function parseUsageWindows$1(payload) {
14731
14828
  return windows;
14732
14829
  }
14733
14830
  function extractCreditsBalance(payload) {
14734
- const root = asRecord$14(payload) ?? {};
14735
- const data = asRecord$14(root.data) ?? root;
14736
- const credits = asRecord$14(data.credits);
14831
+ const root = asRecord$15(payload) ?? {};
14832
+ const data = asRecord$15(root.data) ?? root;
14833
+ const credits = asRecord$15(data.credits);
14737
14834
  if (credits !== void 0) {
14738
14835
  const pools = [
14739
14836
  "monthlyCredits",
@@ -14777,11 +14874,11 @@ let catalogInFlight$4 = null;
14777
14874
  const catalogSnapshotScopesLoaded$3 = /* @__PURE__ */ new Set();
14778
14875
  /** Parse the public `/provider/v1/models` payload. */
14779
14876
  function parseProviderModels(payload) {
14780
- const root = asRecord$14(payload) ?? {};
14877
+ const root = asRecord$15(payload) ?? {};
14781
14878
  const list = Array.isArray(root.data) ? root.data : Array.isArray(payload) ? payload : [];
14782
14879
  const models = [];
14783
14880
  for (const entry of list) {
14784
- const record = asRecord$14(entry);
14881
+ const record = asRecord$15(entry);
14785
14882
  if (!record) continue;
14786
14883
  const id = firstString$1(record, [
14787
14884
  "id",
@@ -21726,7 +21823,7 @@ function withTimeout$7(signal, ms) {
21726
21823
  function oauthEndpoint(host, endpoint) {
21727
21824
  return `${host.replace(/\/+$/, "")}${endpoint}`;
21728
21825
  }
21729
- function asRecord$13(value) {
21826
+ function asRecord$14(value) {
21730
21827
  return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
21731
21828
  }
21732
21829
  /**
@@ -21737,8 +21834,8 @@ function asRecord$13(value) {
21737
21834
  * caller decides whether the flow continues.
21738
21835
  */
21739
21836
  function oauthErrorFields$2(payload) {
21740
- const root = asRecord$13(payload) ?? {};
21741
- const nested = asRecord$13(root.error);
21837
+ const root = asRecord$14(payload) ?? {};
21838
+ const nested = asRecord$14(root.error);
21742
21839
  if (nested !== void 0) return {
21743
21840
  code: typeof nested.code === "string" ? nested.code : "",
21744
21841
  description: String(nested.message ?? nested.error_description ?? nested.detail ?? nested.type ?? "")
@@ -21770,7 +21867,7 @@ async function requestDeviceAuthorization(options = {}) {
21770
21867
  const { description } = oauthErrorFields$2(payload);
21771
21868
  throw new Error(`Device authorization failed (${response.status})${description ? `: ${description}` : ""}`);
21772
21869
  }
21773
- const record = asRecord$13(payload) ?? {};
21870
+ const record = asRecord$14(payload) ?? {};
21774
21871
  const userCode = typeof record.user_code === "string" ? record.user_code : "";
21775
21872
  const deviceCode = typeof record.device_code === "string" ? record.device_code : "";
21776
21873
  const complete = typeof record.verification_uri_complete === "string" ? record.verification_uri_complete : "";
@@ -21812,7 +21909,7 @@ async function pollDeviceToken$1(host, deviceCode, options) {
21812
21909
  signal: withTimeout$7(options.signal, OAUTH_TIMEOUT_MS$2)
21813
21910
  });
21814
21911
  const payload = await response.json().catch(() => void 0);
21815
- const record = asRecord$13(payload) ?? {};
21912
+ const record = asRecord$14(payload) ?? {};
21816
21913
  if (response.status === 200 && typeof record.access_token === "string" && record.access_token !== "") return {
21817
21914
  kind: "success",
21818
21915
  token: parseTokenResponse$2(record)
@@ -21938,7 +22035,7 @@ async function refreshAccessToken$1(refreshToken, options = {}) {
21938
22035
  const payload = await response.json().catch(() => void 0);
21939
22036
  const { code, description } = oauthErrorFields$2(payload);
21940
22037
  if (response.status === 401 || response.status === 403 || code === "invalid_grant") throw new KimiCodeUnauthorizedError(description || "Token refresh was rejected; sign in again.");
21941
- if (response.ok) return parseTokenResponse$2(asRecord$13(payload) ?? {});
22038
+ if (response.ok) return parseTokenResponse$2(asRecord$14(payload) ?? {});
21942
22039
  if (!RETRYABLE_REFRESH_STATUSES$1.has(response.status)) throw new Error(description || `Token refresh failed (HTTP ${response.status}).`);
21943
22040
  lastError = new KimiCodeRetryableError(description || `Token refresh failed (HTTP ${response.status}).`);
21944
22041
  } catch (error) {
@@ -22165,7 +22262,7 @@ const FIXED_POINT_CENTS = 1e6;
22165
22262
  function isRecord$33(value) {
22166
22263
  return typeof value === "object" && value !== null && !Array.isArray(value);
22167
22264
  }
22168
- function asRecord$12(value) {
22265
+ function asRecord$13(value) {
22169
22266
  return isRecord$33(value) ? value : void 0;
22170
22267
  }
22171
22268
  function asString$18(value) {
@@ -22308,7 +22405,7 @@ function parseThinkingType(record) {
22308
22405
  return raw === "only" || raw === "no" || raw === "both" ? raw : void 0;
22309
22406
  }
22310
22407
  function parseCatalogModel(value) {
22311
- const record = asRecord$12(value);
22408
+ const record = asRecord$13(value);
22312
22409
  if (record === void 0) return void 0;
22313
22410
  const id = asString$18(record.id);
22314
22411
  if (id === void 0) return void 0;
@@ -22316,10 +22413,10 @@ function parseCatalogModel(value) {
22316
22413
  if (status !== void 0 && RETIRED_MODEL_STATUSES.has(status)) return void 0;
22317
22414
  const contextWindow = firstNumber(record, ["context_length", "contextLength"]);
22318
22415
  if (contextWindow === void 0 || contextWindow <= 0) return void 0;
22319
- const declaredInput = firstNumber(asRecord$12(record.limit) ?? asRecord$12(record.limits) ?? {}, ["input"]) ?? firstNumber(record, ["max_input_tokens", "maxInputTokens"]);
22416
+ const declaredInput = firstNumber(asRecord$13(record.limit) ?? asRecord$13(record.limits) ?? {}, ["input"]) ?? firstNumber(record, ["max_input_tokens", "maxInputTokens"]);
22320
22417
  const maxInputTokens = declaredInput !== void 0 && declaredInput > 0 && declaredInput < contextWindow ? declaredInput : void 0;
22321
22418
  const thinkingType = parseThinkingType(record);
22322
- const efforts = asRecord$12(record.think_efforts) ?? asRecord$12(record.thinkEfforts);
22419
+ const efforts = asRecord$13(record.think_efforts) ?? asRecord$13(record.thinkEfforts);
22323
22420
  const validEfforts = Array.isArray(efforts?.valid_efforts) ? efforts.valid_efforts.filter((entry) => typeof entry === "string") : Array.isArray(efforts?.validEfforts) ? efforts.validEfforts.filter((entry) => typeof entry === "string") : void 0;
22324
22421
  const modalities = ["text"];
22325
22422
  if (record.supports_image_in === true || record.supportsImageIn === true) modalities.push("image");
@@ -22434,7 +22531,7 @@ async function performListing(options) {
22434
22531
  });
22435
22532
  if (!response.ok) throw new Error(`Kimi Code model listing failed (${response.status}).`);
22436
22533
  const payload = await response.json().catch(() => void 0);
22437
- const data = Array.isArray(payload) ? payload : asRecord$12(payload)?.data;
22534
+ const data = Array.isArray(payload) ? payload : asRecord$13(payload)?.data;
22438
22535
  if (!Array.isArray(data)) throw new Error("Kimi Code model listing was not in the documented shape.");
22439
22536
  const models = data.map(parseCatalogModel).filter((model) => model !== void 0);
22440
22537
  if (epoch !== catalogCacheEpoch$2) return models;
@@ -22769,7 +22866,7 @@ function usageWindow(id, record, descriptor) {
22769
22866
  * than a fabricated 0%.
22770
22867
  */
22771
22868
  function parseUsageWindows(payload) {
22772
- const root = asRecord$12(payload) ?? {};
22869
+ const root = asRecord$13(payload) ?? {};
22773
22870
  const windows = [];
22774
22871
  const seen = /* @__PURE__ */ new Set();
22775
22872
  const push = (id, record, descriptor) => {
@@ -22779,23 +22876,23 @@ function parseUsageWindows(payload) {
22779
22876
  seen.add(id);
22780
22877
  windows.push(parsed);
22781
22878
  };
22782
- const usages = asRecord$12(root.usages) ?? asRecord$12(root.usageWindows);
22879
+ const usages = asRecord$13(root.usages) ?? asRecord$13(root.usageWindows);
22783
22880
  if (usages !== void 0) {
22784
22881
  const keys = [...WINDOW_ORDER.filter((key) => usages[key] !== void 0), ...Object.keys(usages).filter((key) => !WINDOW_ORDER.includes(key))];
22785
22882
  for (const key of keys) {
22786
- const record = asRecord$12(usages[key]);
22883
+ const record = asRecord$13(usages[key]);
22787
22884
  if (record === void 0) continue;
22788
22885
  push(key, record, WINDOW_DESCRIPTORS[key]);
22789
22886
  }
22790
22887
  }
22791
- const topLevel = asRecord$12(root.usage);
22888
+ const topLevel = asRecord$13(root.usage);
22792
22889
  if (topLevel !== void 0 && seen.size === 0) push("limit_7d", topLevel, WINDOW_DESCRIPTORS.limit_7d);
22793
22890
  const limits = Array.isArray(root.limits) ? root.limits : [];
22794
22891
  for (const entry of limits) {
22795
- const record = asRecord$12(entry);
22892
+ const record = asRecord$13(entry);
22796
22893
  if (record === void 0) continue;
22797
- const detail = asRecord$12(record.detail) ?? record;
22798
- const window = asRecord$12(record.window);
22894
+ const detail = asRecord$13(record.detail) ?? record;
22895
+ const window = asRecord$13(record.window);
22799
22896
  const duration = firstNumber(window ?? {}, ["duration"]);
22800
22897
  const unit = asString$18(window?.timeUnit ?? window?.time_unit);
22801
22898
  const minutes = duration === void 0 ? null : unit === "TIME_UNIT_HOUR" ? duration * 60 : duration;
@@ -22828,13 +22925,13 @@ function fixedPointToCents(value) {
22828
22925
  * under-report a charge limit by six orders of magnitude.
22829
22926
  */
22830
22927
  function moneyCents(value) {
22831
- const record = asRecord$12(value);
22928
+ const record = asRecord$13(value);
22832
22929
  if (record === void 0) return null;
22833
22930
  const cents = asNumber$5(record.priceInCents ?? record.price_in_cents);
22834
22931
  return cents === void 0 || !Number.isFinite(cents) ? null : Math.round(cents);
22835
22932
  }
22836
22933
  function moneyCurrency(value) {
22837
- const record = asRecord$12(value);
22934
+ const record = asRecord$13(value);
22838
22935
  return record === void 0 ? void 0 : asString$18(record.currency);
22839
22936
  }
22840
22937
  /**
@@ -22845,10 +22942,10 @@ function moneyCurrency(value) {
22845
22942
  * cannot spend.
22846
22943
  */
22847
22944
  function parseExtraUsage(payload) {
22848
- const root = asRecord$12(payload) ?? {};
22849
- const wallet = asRecord$12(root.boosterWallet) ?? asRecord$12(root.booster_wallet) ?? asRecord$12(root.extraUsage);
22945
+ const root = asRecord$13(payload) ?? {};
22946
+ const wallet = asRecord$13(root.boosterWallet) ?? asRecord$13(root.booster_wallet) ?? asRecord$13(root.extraUsage);
22850
22947
  if (wallet === void 0) return null;
22851
- const balance = asRecord$12(wallet.balance);
22948
+ const balance = asRecord$13(wallet.balance);
22852
22949
  const balanceType = asString$18(balance?.type);
22853
22950
  const amount = asNumber$5(balance?.amount);
22854
22951
  if (balance === void 0 || balanceType !== "BOOSTER" || amount === void 0 || amount <= 0) return null;
@@ -22897,7 +22994,7 @@ function membershipLevelName(level) {
22897
22994
  * `user_level_name` still yields a readable tier instead of a raw enum.
22898
22995
  */
22899
22996
  function parsePlanName(payload) {
22900
- const root = asRecord$12(payload) ?? {};
22997
+ const root = asRecord$13(payload) ?? {};
22901
22998
  const direct = firstString(root, [
22902
22999
  "user_level_name",
22903
23000
  "userLevelName",
@@ -22905,17 +23002,17 @@ function parsePlanName(payload) {
22905
23002
  "plan_name"
22906
23003
  ]);
22907
23004
  if (direct !== void 0) return direct;
22908
- const membership = asRecord$12(asRecord$12(root.user)?.membership);
23005
+ const membership = asRecord$13(asRecord$13(root.user)?.membership);
22909
23006
  const named = asString$18(membership?.level_name ?? membership?.levelName);
22910
23007
  if (named !== void 0) return named;
22911
23008
  return membershipLevelName(asString$18(membership?.level) ?? null);
22912
23009
  }
22913
23010
  /** Machine tier level when the service reports one. */
22914
23011
  function parsePlanLevel(payload) {
22915
- const root = asRecord$12(payload) ?? {};
23012
+ const root = asRecord$13(payload) ?? {};
22916
23013
  const direct = firstNumber(root, ["user_level", "userLevel"]);
22917
23014
  if (direct !== void 0) return String(direct);
22918
- const membership = asRecord$12(asRecord$12(root.user)?.membership);
23015
+ const membership = asRecord$13(asRecord$13(root.user)?.membership);
22919
23016
  return asString$18(membership?.level ?? membership?.levelId ?? membership?.level_id) ?? null;
22920
23017
  }
22921
23018
  /**
@@ -23022,7 +23119,7 @@ function accountFromCredentials$2(credentials, payload, profile) {
23022
23119
  const planLevel = (profile === void 0 ? null : parsePlanLevel(profile)) ?? (payload === void 0 ? null : parsePlanLevel(payload));
23023
23120
  return {
23024
23121
  userId: credentials.userId ?? identity.userId ?? null,
23025
- nickname: (profile === void 0 ? null : firstString(asRecord$12(profile) ?? {}, [
23122
+ nickname: (profile === void 0 ? null : firstString(asRecord$13(profile) ?? {}, [
23026
23123
  "nickname",
23027
23124
  "username",
23028
23125
  "name"
@@ -27157,7 +27254,7 @@ function isRegion$3(value) {
27157
27254
  }
27158
27255
  /** Reasoning levels the whole route understands, for route-level validation. */
27159
27256
  function isMinimaxCodeReasoningEffort(value) {
27160
- return value === "default" || value === "low" || value === "medium" || value === "high" || value === "xhigh" || value === "max";
27257
+ return value === "low" || value === "medium" || value === "high" || value === "xhigh" || value === "max";
27161
27258
  }
27162
27259
  /**
27163
27260
  * Render a token for a log line or an error message without disclosing it.
@@ -27690,31 +27787,54 @@ function catalogEntry(modelId) {
27690
27787
  return minimaxCodeModelDef(modelId);
27691
27788
  }
27692
27789
  /**
27693
- * The thinking control for one request, as documented by MiniMax.
27790
+ * The `thinking` object one request carries, and the only thing that decides
27791
+ * WHICH CHANNEL the reasoning comes back in.
27792
+ *
27793
+ * The official "Model Invocation" page says thinking is on by default and needs
27794
+ * no configuration, and that the depth field is a top-level
27795
+ * `output_config.effort`. Both are true - and they were read as "the request
27796
+ * never carries a `thinking` key". Measured on this subscription endpoint on
27797
+ * 2026-10-20 (`scripts/probe-minimax-thinking-channels.probe.ts`):
27798
+ *
27799
+ * | request | response block | where the reasoning lands |
27800
+ * | --- | --- | --- |
27801
+ * | no `thinking` field | `content_block.type = "text"` | **in the answer text** |
27802
+ * | `thinking: { type: 'adaptive' }` | `content_block.type = "thinking"` | **in a thinking block** |
27694
27803
  *
27695
- * The official "Model Invocation" page settles the shape, and it is NOT a
27696
- * `thinking` object:
27804
+ * The same model, the same prompt, the same reasoning - but without the object
27805
+ * the service streams it as ordinary assistant text, so the model visibly
27806
+ * thinks out loud inside the answer. That also means the harness never sees a
27807
+ * reasoning block, and the interleaved-thinking replay has nothing to replay.
27697
27808
  *
27698
- * > Thinking is **on by default and needs no configuration**.
27809
+ * M3.1 needs no object: measured, a forced-on model answers in the thinking
27810
+ * channel either way, which is why the upstream managed path deletes the
27811
+ * `thinking` key for forced-on models and keeps it for switchable ones.
27699
27812
  *
27700
- * > Protocol | Thinking depth field | Where thinking content is returned
27701
- * > Anthropic-compatible | `output_config.effort` | `thinking` content block
27702
- * > OpenAI-compatible | `reasoning_effort` | `reasoning_content` field
27813
+ * The off state is the same object with `disabled`, which is what the upstream
27814
+ * managed path sends and what this endpoint measured as thinking off (10 output
27815
+ * tokens, no thinking block).
27703
27816
  *
27704
- * Three consequences, each replacing something this function used to do:
27817
+ * @returns the value for `thinking`, or undefined when the request should
27818
+ * carry no thinking object at all.
27819
+ */
27820
+ function thinkingFieldFor(modelId, requestedEffort) {
27821
+ const model = catalogEntry(modelId);
27822
+ if (model === void 0) return void 0;
27823
+ if (model.thinking !== "toggle") return void 0;
27824
+ return { type: isThinkingDisabledEffort(requestedEffort) ? "disabled" : "adaptive" };
27825
+ }
27826
+ /**
27827
+ * The depth level one request carries, as `output_config`.
27705
27828
  *
27706
- * 1. No `thinking` key is ever sent. The field was inferred, never documented,
27707
- * and a request that thinks is exactly a request that says nothing - which is
27708
- * why the field could sit here for months with nothing contradicting it.
27709
- * 2. The depth level belongs at the TOP level, in `output_config.effort`, not
27710
- * inside a thinking object. Anywhere else is a field the service does not read.
27711
- * 3. `effort` accepts low, medium, high, xhigh and max. There is no `default`
27712
- * member: the page says omitting the field means max, so `default` must not
27713
- * reach the wire, and neither must an explicit `max` for the same reason.
27829
+ * The level belongs at the TOP level - `output_config.effort` - and accepts
27830
+ * low, medium, high, xhigh and max. There is no `default` member: the page
27831
+ * says omitting the field means max, so the old local `default` spelling never
27832
+ * reached the wire. This line no longer leans on that: every model that has
27833
+ * levels states its default out loud, and a request that asks for nothing
27834
+ * resolves to that default instead of to an omission.
27714
27835
  *
27715
- * The off switch is a separate question the docs only answer for M3.1, where turning
27716
- * thinking off returns a 400. `toggle` models keep theirs because the
27717
- * subscription's own model table still names a none-thinking state for them.
27836
+ * The level is the DEPTH, not the channel: measured, low and max differ by
27837
+ * about 3x thinking and 4x latency, and it composes with the `thinking` object.
27718
27838
  *
27719
27839
  * @returns the value for `output_config`, or undefined when the request should
27720
27840
  * carry no thinking control at all.
@@ -27723,9 +27843,9 @@ function outputConfigFor(modelId, requestedEffort) {
27723
27843
  const model = catalogEntry(modelId);
27724
27844
  if (model === void 0) return void 0;
27725
27845
  if (model.thinking === "always-on") return void 0;
27726
- if (model.thinking === "toggle") return isThinkingDisabledEffort(requestedEffort) ? { effort: "none" } : void 0;
27727
- const effort = effortForModel(modelId, requestedEffort ?? null);
27728
- return effort === "default" ? void 0 : { effort };
27846
+ if (isThinkingDisabledEffort(requestedEffort)) return void 0;
27847
+ const effort = effortForModel(modelId, requestedEffort);
27848
+ return effort === null ? void 0 : { effort };
27729
27849
  }
27730
27850
  /**
27731
27851
  * Raise a cap that is too small for this line's forced thinking.
@@ -27856,7 +27976,10 @@ function buildMinimaxRequest(options, images = NO_RESOLVED_IMAGES$3, request = {
27856
27976
  }
27857
27977
  const system = leadingSystemText$5(options);
27858
27978
  const maxTokens = options.maxTokens ?? maxOutputTokensFor$1(options.model);
27859
- const outputConfig = outputConfigFor(options.model, options.reasoningEffort === void 0 ? null : String(options.reasoningEffort));
27979
+ const requestedEffort = options.reasoningEffort === void 0 ? null : String(options.reasoningEffort);
27980
+ const outputConfig = outputConfigFor(options.model, requestedEffort);
27981
+ const thinking = thinkingFieldFor(options.model, requestedEffort);
27982
+ const thinkingEngaged = outputConfig !== void 0 || thinking?.type === "adaptive";
27860
27983
  const caching = request.cacheControl !== false;
27861
27984
  const body = {
27862
27985
  model: options.model,
@@ -27867,13 +27990,14 @@ function buildMinimaxRequest(options, images = NO_RESOLVED_IMAGES$3, request = {
27867
27990
  type: "text",
27868
27991
  text: system
27869
27992
  }] : system },
27870
- ...options.temperature === void 0 || outputConfig !== void 0 ? {} : { temperature: options.temperature },
27993
+ ...options.temperature === void 0 || thinkingEngaged ? {} : { temperature: options.temperature },
27871
27994
  ...options.stop && options.stop.length > 0 ? { stop_sequences: options.stop } : {},
27872
27995
  ...options.tools && options.tools.length > 0 ? { tools: options.tools.map((tool) => ({
27873
27996
  name: tool.name,
27874
27997
  description: tool.description,
27875
27998
  input_schema: stripMetaSchema$2(tool.parameters)
27876
27999
  })) } : {},
28000
+ ...thinking === void 0 ? {} : { thinking },
27877
28001
  ...outputConfig === void 0 ? {} : { output_config: outputConfig }
27878
28002
  };
27879
28003
  if (caching) markMinimaxCacheBreakpoints(body);
@@ -28095,6 +28219,8 @@ function processMinimaxStreamLine(line, state) {
28095
28219
  }
28096
28220
  const stop = message === void 0 ? void 0 : asString$15(message.stop_reason);
28097
28221
  if (stop !== void 0) state.finishReason = stop;
28222
+ const answered = message === void 0 ? void 0 : asString$15(message.model);
28223
+ if (answered !== void 0) state.answeredModel = answered;
28098
28224
  return out;
28099
28225
  }
28100
28226
  if (type === "content_block_start") {
@@ -28329,8 +28455,8 @@ const MINIMAX_CODE_MODELS = [
28329
28455
  maxTokens: 128e3,
28330
28456
  inputModalities: ["text"],
28331
28457
  thinking: "always-on",
28332
- reasoningEfforts: ["default"],
28333
- defaultReasoningEffort: "default",
28458
+ reasoningEfforts: [],
28459
+ defaultReasoningEffort: null,
28334
28460
  maxAttachments: null,
28335
28461
  maxImageBytes: null,
28336
28462
  maxVideoBytes: null,
@@ -28345,8 +28471,8 @@ const MINIMAX_CODE_MODELS = [
28345
28471
  maxTokens: 128e3,
28346
28472
  inputModalities: ["text"],
28347
28473
  thinking: "always-on",
28348
- reasoningEfforts: ["default"],
28349
- defaultReasoningEffort: "default",
28474
+ reasoningEfforts: [],
28475
+ defaultReasoningEffort: null,
28350
28476
  maxAttachments: null,
28351
28477
  maxImageBytes: null,
28352
28478
  maxVideoBytes: null,
@@ -28365,8 +28491,14 @@ const MINIMAX_CODE_MODELS = [
28365
28491
  "video"
28366
28492
  ],
28367
28493
  thinking: "toggle",
28368
- reasoningEfforts: ["default"],
28369
- defaultReasoningEffort: "default",
28494
+ reasoningEfforts: [
28495
+ "low",
28496
+ "medium",
28497
+ "high",
28498
+ "xhigh",
28499
+ "max"
28500
+ ],
28501
+ defaultReasoningEffort: "max",
28370
28502
  maxAttachments: 9,
28371
28503
  maxImageBytes: 10 * 1024 * 1024,
28372
28504
  maxVideoBytes: 50 * 1024 * 1024,
@@ -28376,8 +28508,8 @@ const MINIMAX_CODE_MODELS = [
28376
28508
  {
28377
28509
  id: "MiniMax-M3.1-Flash",
28378
28510
  name: "MiniMax M3.1 Flash",
28379
- contextWindow: 1e6,
28380
- optionalContextWindow: null,
28511
+ contextWindow: 512e3,
28512
+ optionalContextWindow: 1e6,
28381
28513
  maxTokens: 128e3,
28382
28514
  inputModalities: [
28383
28515
  "text",
@@ -28386,14 +28518,13 @@ const MINIMAX_CODE_MODELS = [
28386
28518
  ],
28387
28519
  thinking: "forced-effort",
28388
28520
  reasoningEfforts: [
28389
- "default",
28390
28521
  "low",
28391
28522
  "medium",
28392
28523
  "high",
28393
28524
  "xhigh",
28394
28525
  "max"
28395
28526
  ],
28396
- defaultReasoningEffort: "default",
28527
+ defaultReasoningEffort: "max",
28397
28528
  maxAttachments: 4,
28398
28529
  maxImageBytes: 10 * 1024 * 1024,
28399
28530
  maxVideoBytes: 50 * 1024 * 1024,
@@ -28465,7 +28596,8 @@ function isThinkingDisabledEffort(value) {
28465
28596
  */
28466
28597
  function effortForModel(modelId, requested) {
28467
28598
  const model = minimaxCodeModelDef(modelId);
28468
- if (model === void 0) return "default";
28599
+ if (model === void 0) return null;
28600
+ if (model.reasoningEfforts.length === 0) return null;
28469
28601
  if (requested === void 0 || requested === null) return model.defaultReasoningEffort;
28470
28602
  const normalized = requested.trim().toLowerCase();
28471
28603
  return model.reasoningEfforts.find((effort) => effort === normalized) ?? model.defaultReasoningEffort;
@@ -28535,7 +28667,7 @@ function buildMinimaxCodeModelOptions(storedEnabledModelIds, contextWindowOverri
28535
28667
  contextWindow,
28536
28668
  defaultMaxTokens: maxOutputTokensFor$1(model.id, contextWindow),
28537
28669
  reasoningEfforts: [...model.reasoningEfforts],
28538
- defaultReasoningEffort: model.defaultReasoningEffort,
28670
+ defaultReasoningEffort: model.defaultReasoningEffort ?? void 0,
28539
28671
  thinking: model.thinking,
28540
28672
  description: model.description
28541
28673
  };
@@ -29283,7 +29415,7 @@ function registerMinimaxCodePreferenceStore(settings, fallbackStore = new Minima
29283
29415
  enabled: value.enabled !== false,
29284
29416
  enabledModelIds: value.enabledModelIds,
29285
29417
  contextWindowOverrides: value.contextWindowOverrides,
29286
- defaultReasoningEffort: value.defaultReasoningEffort,
29418
+ defaultReasoningEffort: isReasoningEffort$1(value.defaultReasoningEffort) ? value.defaultReasoningEffort : null,
29287
29419
  checkin: { enabled: value.checkin?.enabled !== false }
29288
29420
  };
29289
29421
  },
@@ -29356,7 +29488,7 @@ function withTimeout$6(signal, ms) {
29356
29488
  const timeout = AbortSignal.timeout(ms);
29357
29489
  return signal === void 0 ? timeout : AbortSignal.any([signal, timeout]);
29358
29490
  }
29359
- function asRecord$11(value) {
29491
+ function asRecord$12(value) {
29360
29492
  return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
29361
29493
  }
29362
29494
  function asString$13(value) {
@@ -29370,8 +29502,8 @@ function asString$13(value) {
29370
29502
  * both are unwrapped before the caller decides what the failure means.
29371
29503
  */
29372
29504
  function oauthErrorFields$1(payload) {
29373
- const root = asRecord$11(payload) ?? {};
29374
- const nested = asRecord$11(root.error);
29505
+ const root = asRecord$12(payload) ?? {};
29506
+ const nested = asRecord$12(root.error);
29375
29507
  if (nested !== void 0) return {
29376
29508
  code: asString$13(nested.code) ?? "",
29377
29509
  description: String(nested.message ?? nested.error_description ?? nested.detail ?? nested.type ?? "")
@@ -29427,7 +29559,7 @@ async function requestDeviceAuthorization$1(options = {}) {
29427
29559
  const { description } = oauthErrorFields$1(payload);
29428
29560
  throw new Error("MiniMax Code device authorization failed (" + response.status + ")" + (description === "" ? "" : ": " + description));
29429
29561
  }
29430
- const record = asRecord$11(payload) ?? {};
29562
+ const record = asRecord$12(payload) ?? {};
29431
29563
  const userCode = asString$13(record.user_code);
29432
29564
  const deviceCode = asString$13(record.device_code);
29433
29565
  if (userCode === void 0 || deviceCode === void 0) throw new Error("MiniMax Code device authorization response carried no device code or user code");
@@ -29481,7 +29613,7 @@ async function pollDeviceToken(authorization, options = {}) {
29481
29613
  signal: withTimeout$6(options.signal, OAUTH_TIMEOUT_MS$1)
29482
29614
  });
29483
29615
  const payload = await response.json().catch(() => void 0);
29484
- const record = asRecord$11(payload) ?? {};
29616
+ const record = asRecord$12(payload) ?? {};
29485
29617
  if (response.ok && asString$13(record.access_token) !== void 0) return {
29486
29618
  kind: "success",
29487
29619
  token: parseTokenResponse$1(record)
@@ -29545,7 +29677,7 @@ async function refreshAccessToken$2(refreshToken, options) {
29545
29677
  const payload = await response.json().catch(() => void 0);
29546
29678
  const { code, description } = oauthErrorFields$1(payload);
29547
29679
  if (response.status === 400 || response.status === 401 || response.status === 403 || code === "invalid_grant" || code === "invalid_client") throw new MinimaxCodeUnauthorizedError("MiniMax Code rejected the stored refresh token " + redactToken(refreshToken) + ". Sign in again in MiniMax Code, then reopen this card.");
29548
- if (response.ok) return parseTokenResponse$1(asRecord$11(payload) ?? {});
29680
+ if (response.ok) return parseTokenResponse$1(asRecord$12(payload) ?? {});
29549
29681
  if (!RETRYABLE_REFRESH_STATUSES.has(response.status)) throw new MinimaxCodeRetryableError(description || "MiniMax Code token refresh failed (HTTP " + response.status + ").");
29550
29682
  lastError = new MinimaxCodeRetryableError(description || "MiniMax Code token refresh failed (HTTP " + response.status + ").");
29551
29683
  } catch (error) {
@@ -29875,7 +30007,7 @@ function probeRequestHeaders(accessToken) {
29875
30007
  accept: "application/json"
29876
30008
  };
29877
30009
  }
29878
- function asRecord$10(value) {
30010
+ function asRecord$11(value) {
29879
30011
  return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
29880
30012
  }
29881
30013
  function numberOr$5(value, fallback) {
@@ -29883,7 +30015,7 @@ function numberOr$5(value, fallback) {
29883
30015
  }
29884
30016
  /** Read the four token counters the Messages response reports. */
29885
30017
  function parseAnthropicUsage(payload) {
29886
- const usage = asRecord$10(asRecord$10(payload)?.usage);
30018
+ const usage = asRecord$11(asRecord$11(payload)?.usage);
29887
30019
  if (usage === void 0) return null;
29888
30020
  return {
29889
30021
  inputTokens: numberOr$5(usage.input_tokens, 0),
@@ -29897,13 +30029,13 @@ function summarizeFailureBody(raw) {
29897
30029
  const text = raw.replace(/[\r\n\t]+/g, " ").trim();
29898
30030
  if (text === "") return "";
29899
30031
  try {
29900
- const record = asRecord$10(JSON.parse(text));
30032
+ const record = asRecord$11(JSON.parse(text));
29901
30033
  if (record !== void 0) {
29902
30034
  const message = record.message ?? record.error_description ?? record.errorReason ?? record.error;
29903
30035
  if (typeof message === "string") return message.slice(0, 400);
29904
- const nestedMessage = asRecord$10(record.error)?.message;
30036
+ const nestedMessage = asRecord$11(record.error)?.message;
29905
30037
  if (typeof nestedMessage === "string") return nestedMessage.slice(0, 400);
29906
- const baseResp = asRecord$10(record.base_resp)?.status_msg;
30038
+ const baseResp = asRecord$11(record.base_resp)?.status_msg;
29907
30039
  if (typeof baseResp === "string") return baseResp.slice(0, 400);
29908
30040
  }
29909
30041
  } catch {}
@@ -30222,11 +30354,11 @@ function quotaWindow(input) {
30222
30354
  * card's graceful degradation.
30223
30355
  */
30224
30356
  function parseTokenPlanQuota(payload, now = Date.now()) {
30225
- const rows = asRecord$10(payload)?.model_remains;
30357
+ const rows = asRecord$11(payload)?.model_remains;
30226
30358
  if (!Array.isArray(rows)) return null;
30227
30359
  const entries = [];
30228
30360
  for (const raw of rows) {
30229
- const row = asRecord$10(raw);
30361
+ const row = asRecord$11(raw);
30230
30362
  if (row === void 0) continue;
30231
30363
  const name = typeof row.model_name === "string" && row.model_name !== "" ? row.model_name : "quota";
30232
30364
  const intervalTotal = numberOr$5(row.current_interval_total_count, 0);
@@ -30302,7 +30434,7 @@ async function fetchTokenPlanQuota(credentials, options = {}) {
30302
30434
  }
30303
30435
  if (!response.ok) continue;
30304
30436
  const payload = await response.json().catch(() => void 0);
30305
- if (numberOr$5(asRecord$10(asRecord$10(payload)?.base_resp)?.status_code, 0) !== 0) {
30437
+ if (numberOr$5(asRecord$11(asRecord$11(payload)?.base_resp)?.status_code, 0) !== 0) {
30306
30438
  reason = "credential-not-accepted";
30307
30439
  break;
30308
30440
  }
@@ -30708,6 +30840,11 @@ function quotaCooldownMs(bodyText) {
30708
30840
  * the other — so a body whose chosen field says "rate limit" while its sibling says
30709
30841
  * 额度不足 must still be read as the quota verdict it is. Same reasoning, same raw-text
30710
30842
  * match, as the sibling line's plan-scope test (kimi-code/adapter.ts:838).
30843
+ *
30844
+ * This table is the FALLBACK, not the only verdict: the service states the same
30845
+ * condition as a number too, and {@link namesSpentWindow} asks that first. The entries
30846
+ * stay because a Chinese sentence arrives with no code at all, and because a body that
30847
+ * is not JSON has nothing but its wording to read.
30711
30848
  */
30712
30849
  const QUOTA_EXHAUSTED_PATTERNS = [
30713
30850
  /insufficient\s+(?:balance|quota|credit|funds)/i,
@@ -30727,13 +30864,111 @@ function matchesAny(text, patterns) {
30727
30864
  return patterns.some((pattern) => pattern.test(text));
30728
30865
  }
30729
30866
  /**
30867
+ * The upstream status codes that mean this account's usage window is spent.
30868
+ *
30869
+ * The official MiniMax Code client settles this verdict on a NUMBER and not on
30870
+ * wording — `LLM_USAGE_LIMIT_UPSTREAM_STATUS_CODES = [2056, 2067]` in
30871
+ * packages/shared/src/llm-error-classifier.ts — and until this table existed this
30872
+ * line had only the phrase regexes above. A phrase table is the brittle half of the
30873
+ * same rule: every phrasing the service invents needs a new entry, and every entry
30874
+ * added is another chance to file ordinary back-pressure as a terminal verdict (the
30875
+ * `/quota/i` → `rate_limit_exceeded` regression the table itself records). The code
30876
+ * is what the service already decided; reading it is reading the verdict rather
30877
+ * than guessing at it.
30878
+ *
30879
+ * Both codes are the same verdict as the wording path: the account's window is
30880
+ * gone until it resets, so the failure is terminal (no retry can refill a window)
30881
+ * and it belongs to ONE account (the pool cools it for the window the body names —
30882
+ * five hours or a week — and lets the next account answer).
30883
+ *
30884
+ * 2013 is deliberately absent. Upstream calls it a generic invalid-params code and
30885
+ * this repo already reads it that way on the reset-card and Files API paths, so a
30886
+ * 2013 must never become a quota verdict no matter which envelope carries it.
30887
+ */
30888
+ const USAGE_LIMIT_UPSTREAM_STATUS_CODES = /* @__PURE__ */ new Set([2056, 2067]);
30889
+ function isUsageLimitStatusCode(code) {
30890
+ return code !== void 0 && USAGE_LIMIT_UPSTREAM_STATUS_CODES.has(code);
30891
+ }
30892
+ /** A JSON object, or undefined for anything else (including a failed parse). */
30893
+ function asRecord$10(value) {
30894
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
30895
+ }
30896
+ /** The whole number one field holds, or undefined when it holds anything else. */
30897
+ function integerOrUndefined(value) {
30898
+ return typeof value === "number" && Number.isSafeInteger(value) ? value : void 0;
30899
+ }
30900
+ /**
30901
+ * The numeric status code one envelope carries, read from the field that envelope
30902
+ * documents.
30903
+ *
30904
+ * The two envelopes this line already parses put the code in different places, and
30905
+ * both are read here for the same reason `QUOTA_EXHAUSTED_PATTERNS` matches the raw
30906
+ * body: a body that carries only `base_resp.status_code` carries no sentence at all,
30907
+ * so an extractor keyed on one field would judge an envelope it never saw.
30908
+ *
30909
+ * · the platform envelope — `{"base_resp":{"status_code":…,"status_msg":…}}` — as
30910
+ * every platform route in this line already reads it (client.ts:131,
30911
+ * reset-card.ts:182-188, files-api.ts:373-375, checkin-gateway.ts:402-406);
30912
+ * · the client envelope — `{"errorCode":…,"errorReason":…}` — as types.ts:22 and
30913
+ * model-catalog.ts:6 document it;
30914
+ * · the Messages envelope — `{"code":…,"message":…}` — which is the ONLY numeric
30915
+ * field this route's own error bodies carry. Measured on 2026-10-20 (see
30916
+ * docs/minimax-code-probe-capabilities.md): a 400 on /messages carries no
30917
+ * base_resp and no errorCode at all, and its 2013 sits in the message TEXT
30918
+ * alone; the 401 that shares that text says `{"code":401,"message":"auth
30919
+ * failed"}`. Without this arm the numeric branch below could never fire on
30920
+ * this route, which is what left every /messages refusal to the phrase table.
30921
+ */
30922
+ function statusCodeIn(record) {
30923
+ if (record === void 0) return void 0;
30924
+ return integerOrUndefined(asRecord$10(record.base_resp)?.status_code) ?? integerOrUndefined(record.errorCode) ?? integerOrUndefined(record.code);
30925
+ }
30926
+ /**
30927
+ * Whether this body NAMES a spent usage window by its status code.
30928
+ *
30929
+ * Read from the raw body, in both envelopes, exactly as the phrase table is — with
30930
+ * one arm more: the in-band body this adapter itself builds (`{ error: <the stream's
30931
+ * own error object> }`, see the reclassification in `generate`) is one level down,
30932
+ * so the same two fields are read inside it as well. That is what lets an in-band
30933
+ * failure inherit this verdict instead of needing a second copy of the rule.
30934
+ *
30935
+ * A body that is not JSON answers false and falls through to the phrase table: a
30936
+ * truncated or plain-text body has no field to read, and the wording is the only
30937
+ * evidence it carries.
30938
+ */
30939
+ function namesUsageLimitStatusCode(bodyText) {
30940
+ const text = bodyText.trim();
30941
+ if (text === "") return false;
30942
+ let parsed;
30943
+ try {
30944
+ parsed = JSON.parse(text);
30945
+ } catch {
30946
+ return false;
30947
+ }
30948
+ const record = asRecord$10(parsed);
30949
+ if (record === void 0) return false;
30950
+ return isUsageLimitStatusCode(statusCodeIn(record)) || isUsageLimitStatusCode(statusCodeIn(asRecord$10(record.error)));
30951
+ }
30952
+ /**
30953
+ * Whether this body names a spent usage window at all — by number or by wording.
30954
+ *
30955
+ * The number is the service's own verdict and the wording is the fallback, which is
30956
+ * the order the two are asked in rather than two copies of the same question: a
30957
+ * Chinese sentence has no code, and a bare code has no sentence, so both arms exist
30958
+ * and both answer into the ONE verdict {@link quotaExhaustedFailure} builds.
30959
+ */
30960
+ function namesSpentWindow(bodyText) {
30961
+ return namesUsageLimitStatusCode(bodyText) || matchesAny(bodyText, QUOTA_EXHAUSTED_PATTERNS);
30962
+ }
30963
+ /**
30730
30964
  * Whether this refusal is a spent window rather than a refused credential.
30731
30965
  *
30732
- * The same raw-body match classifyMinimaxFailure performs, hoisted into its own name
30966
+ * The same raw-body test classifyMinimaxFailure performs, hoisted into its own name
30733
30967
  * because it now answers a second question too, and one the status alone cannot: is
30734
30968
  * renewing this credential worth attempting at all (see the recovery loop in this
30735
30969
  * adapter). A verdict the pool acts on and a verdict the user is shown have to be the
30736
- * SAME verdict, so both read this one predicate against the same table rather than
30970
+ * SAME verdict, so both read this one predicate — {@link namesSpentWindow}, which asks
30971
+ * the upstream usage-limit code first and the wording table second — rather than
30737
30972
  * keeping two copies of the condition that could drift apart.
30738
30973
  *
30739
30974
  * The status is inside on purpose. 403 is the only status a spent window arrives as,
@@ -30743,7 +30978,7 @@ function matchesAny(text, patterns) {
30743
30978
  * into it would send a working token off to be renewed.
30744
30979
  */
30745
30980
  function isSpentWindow403(status, bodyText) {
30746
- return status === 403 && matchesAny(bodyText, QUOTA_EXHAUSTED_PATTERNS);
30981
+ return status === 403 && namesSpentWindow(bodyText);
30747
30982
  }
30748
30983
  /**
30749
30984
  * The verdict a spent Token Plan window gets, whichever status carries it.
@@ -30787,7 +31022,7 @@ function classifyMinimaxFailure(status, bodyText) {
30787
31022
  message: "MiniMax Code(编程订阅) rejected the stored credential (" + status + "). Sign in again in the MiniMax Code app, then reopen this card." + (detail === "" ? "" : " " + detail)
30788
31023
  };
30789
31024
  if (status === 429) {
30790
- if (matchesAny(bodyText, QUOTA_EXHAUSTED_PATTERNS)) return quotaExhaustedFailure(detail);
31025
+ if (namesSpentWindow(bodyText)) return quotaExhaustedFailure(detail);
30791
31026
  return {
30792
31027
  code: "RATE_LIMIT",
30793
31028
  retryable: true,
@@ -30953,6 +31188,48 @@ async function uploadOversizedMedia(options, images, videos, credentials, accoun
30953
31188
  })
30954
31189
  };
30955
31190
  }
31191
+ /**
31192
+ * The error code for "the model this request named is not the model that answered".
31193
+ *
31194
+ * Deliberately NOT in `MINIMAX_CODE_RETRY_POLICY_CONFIG.retryableCodes` (which is
31195
+ * RATE_LIMIT / SERVER / TIMEOUT / TRANSPORT), so the harness never retries it:
31196
+ * re-issuing the SAME body against the SAME substitution answers the SAME wrong
31197
+ * model, and each attempt is billed at the substituted model's price. The failure
31198
+ * is terminal until a human changes the model, which is exactly what the retry
31199
+ * policy is keyed on.
31200
+ *
31201
+ * Exported so a test (and any later caller) can name it instead of copying the
31202
+ * literal, the same way NO_ANSWER_PHRASE is.
31203
+ */
31204
+ const MODEL_SUBSTITUTED_CODE = "MODEL_SUBSTITUTED";
31205
+ /**
31206
+ * Whether the id the service ANSWERED with is the id the request asked for.
31207
+ *
31208
+ * Case-insensitive equality is the whole rule: this line already accepts a case
31209
+ * variant of an id as the same model (the catalog and the stored settings compare
31210
+ * ids the same way), and nothing else is normalized. A REQUESTED id is whatever
31211
+ * the catalog spells; an ANSWERED id is whatever the service spells; the two
31212
+ * differing only in letter case is not a substitution, and refusing to answer over
31213
+ * one would fail a turn that is being served the model it asked for.
31214
+ *
31215
+ * Everything else is a mismatch: a different id, a retired spelling, an id the
31216
+ * service does not route at all. That is the substitution this check exists for.
31217
+ */
31218
+ function sameModelId(requested, answered) {
31219
+ return requested.trim().toLowerCase() === answered.trim().toLowerCase();
31220
+ }
31221
+ /**
31222
+ * The failure a substituted model gets, worded so the reader can act on it.
31223
+ *
31224
+ * Both ids are named because both are the facts: the catalogue-facing id the user
31225
+ * selected, and the one the service actually ran. Neither is a secret — they are
31226
+ * model names — and nothing else from the request (token, account, body) goes in.
31227
+ * The remedy is spelled out because the check cannot pick one for the user: the
31228
+ * substitution means this id no longer routes, so the selection has to change.
31229
+ */
31230
+ function modelSubstitutedFailure(requested, answered) {
31231
+ return new LlmError("MiniMax Code(编程订阅) answered with a different model than the one requested: the request asked for \"" + requested + "\", and the service answered as \"" + answered + "\". This endpoint does not reject a model id it cannot route — it silently runs another one — so the id \"" + requested + "\" is not being served and the request was billed at the answering model's rate instead. Nothing was retried, because the same request would be answered the same way. Pick a different model for this line, or check whether \"" + requested + "\" is still offered, then send the request again.", MODEL_SUBSTITUTED_CODE, { status: 200 });
31232
+ }
30956
31233
  var MinimaxCodeAdapter = class extends LlmAdapter {
30957
31234
  store;
30958
31235
  /**
@@ -31090,8 +31367,7 @@ var MinimaxCodeAdapter = class extends LlmAdapter {
31090
31367
  "medium",
31091
31368
  "high",
31092
31369
  "xhigh",
31093
- "max",
31094
- "default"
31370
+ "max"
31095
31371
  ]);
31096
31372
  const contextWindow = contextWindowForModel(modelId, settings.contextWindowOverrides);
31097
31373
  const defaultEffort = entry === void 0 ? void 0 : resolveDefaultEffort(entry, settings.defaultReasoningEffort);
@@ -31148,6 +31424,24 @@ var MinimaxCodeAdapter = class extends LlmAdapter {
31148
31424
  let authOwner;
31149
31425
  let accountId;
31150
31426
  let response;
31427
+ /**
31428
+ * The model id this request actually PUT ON THE WIRE — the comparison basis for
31429
+ * the served-model check further down.
31430
+ *
31431
+ * Read back out of the serialized body rather than taken from `options.model`
31432
+ * directly: `buildMinimaxRequest` writes `model: options.model` today, so the two
31433
+ * agree, but the thing that must be compared is by definition what left the
31434
+ * socket. The catalogue's renames (model-catalog.ts:269-282) never touch it —
31435
+ * "The WIRE still carries whatever id the caller asked for ... Only the LOOKUP is
31436
+ * renamed" — so a retired spelling is compared AS the retired spelling, which is
31437
+ * exactly the id the service substitutes a different model for.
31438
+ *
31439
+ * Hoisted here because the body is built (and rebuilt, by the 401 recovery)
31440
+ * inside the rotation loop while the stream is read after it. A body carrying no
31441
+ * model string leaves it undefined and turns the check off for the round — the
31442
+ * same "nothing to verify" outcome as a header with no model in it.
31443
+ */
31444
+ let wireModel;
31151
31445
  const pool = this.options.accountPool;
31152
31446
  while (true) {
31153
31447
  try {
@@ -31161,8 +31455,17 @@ var MinimaxCodeAdapter = class extends LlmAdapter {
31161
31455
  if (accountId !== void 0) tried.add(accountId);
31162
31456
  authOwner = computeMinimaxAuthOwner(credentials, accountId);
31163
31457
  const withUploads = await uploadOversizedMedia(requestOptions, images, videos, credentials, accountId, this.options.fetchFn ?? fetch, signal);
31458
+ /**
31459
+ * Build one body, and record the model id it carries as this round's
31460
+ * comparison basis.
31461
+ *
31462
+ * The two are set together because they cannot disagree: `wireModel` is read
31463
+ * back out of the body that is about to be sent, so the id the answered-model
31464
+ * check compares against is by construction the id on the wire — not the
31465
+ * catalogue name, not a renamed lookup.
31466
+ */
31164
31467
  const buildBodyForOwner = (owner) => {
31165
- return assertRequestBodyFits(buildMinimaxRequest({
31468
+ const built = buildMinimaxRequest({
31166
31469
  ...withUploads,
31167
31470
  maxTokens: effectiveMax
31168
31471
  }, images, {
@@ -31171,7 +31474,10 @@ var MinimaxCodeAdapter = class extends LlmAdapter {
31171
31474
  videoAccepted: minimaxCodeModelDef(options.model)?.inputModalities.includes("video") === true,
31172
31475
  authOwner: owner,
31173
31476
  route: PROVIDER_ID$4
31174
- }));
31477
+ });
31478
+ const serialized = assertRequestBodyFits(built);
31479
+ wireModel = typeof built.model === "string" ? built.model : void 0;
31480
+ return serialized;
31175
31481
  };
31176
31482
  let body = buildBodyForOwner(authOwner);
31177
31483
  let refusedBody;
@@ -31238,6 +31544,41 @@ var MinimaxCodeAdapter = class extends LlmAdapter {
31238
31544
  * in-band reclassification below pre-output only.
31239
31545
  */
31240
31546
  let outputStarted = false;
31547
+ /**
31548
+ * Whether the served-model check has already been decided for this stream.
31549
+ *
31550
+ * One decision per stream, made on the round `message_start` arrives: the field
31551
+ * cannot change afterwards, and re-deciding on every later frame would re-run two
31552
+ * string comparisons per SSE line for an answer that is already known.
31553
+ */
31554
+ let answeredVerified = false;
31555
+ /**
31556
+ * Compare the model the service ANSWERED with against the one this request put on
31557
+ * the wire, and refuse the turn when they differ.
31558
+ *
31559
+ * Measured fact this exists for (docs/minimax-code-probe-capabilities.md): the
31560
+ * /messages endpoint answers 200 with a normal stream for a model id it cannot
31561
+ * route, substituting a default model — `modelless-probe-id-zzz` and the empty
31562
+ * string both came back as MiniMax-M3, and an unlisted but real id came back as
31563
+ * itself. There is no "unknown model" error payload to react to, so this header is
31564
+ * the ONLY evidence that the id the user selected is not the id that ran, which on
31565
+ * this line is a wallet problem (M3 at max effort costs about 3x the thinking and
31566
+ * 4x the latency of the cheaper tiers).
31567
+ *
31568
+ * Two "no verdict" cases, both deliberate: a stream whose header carries no model
31569
+ * (undefined) and a request whose body carried none (also undefined). Neither is
31570
+ * evidence of substitution, and failing a turn over an unverifiable one would turn
31571
+ * a protocol variation into an outage.
31572
+ */
31573
+ const verifyAnsweredModel = () => {
31574
+ if (answeredVerified) return;
31575
+ const answered = state.answeredModel;
31576
+ if (answered === void 0) return;
31577
+ answeredVerified = true;
31578
+ if (wireModel === void 0) return;
31579
+ if (sameModelId(wireModel, answered)) return;
31580
+ throw modelSubstitutedFailure(wireModel, answered);
31581
+ };
31241
31582
  try {
31242
31583
  try {
31243
31584
  while (true) {
@@ -31249,7 +31590,9 @@ var MinimaxCodeAdapter = class extends LlmAdapter {
31249
31590
  buffer = lines.pop() ?? "";
31250
31591
  for (const line of lines) {
31251
31592
  activity?.pulse();
31252
- for (const chunk of processMinimaxStreamLine(line, state)) {
31593
+ const chunks = processMinimaxStreamLine(line, state);
31594
+ verifyAnsweredModel();
31595
+ for (const chunk of chunks) {
31253
31596
  outputStarted = true;
31254
31597
  yield chunk;
31255
31598
  }
@@ -31257,9 +31600,13 @@ var MinimaxCodeAdapter = class extends LlmAdapter {
31257
31600
  }
31258
31601
  }
31259
31602
  buffer += decoder.decode();
31260
- if (buffer.trim() !== "") for (const line of buffer.split("\n")) for (const chunk of processMinimaxStreamLine(line, state)) {
31261
- outputStarted = true;
31262
- yield chunk;
31603
+ if (buffer.trim() !== "") for (const line of buffer.split("\n")) {
31604
+ const tailChunks = processMinimaxStreamLine(line, state);
31605
+ verifyAnsweredModel();
31606
+ for (const chunk of tailChunks) {
31607
+ outputStarted = true;
31608
+ yield chunk;
31609
+ }
31263
31610
  }
31264
31611
  if (state.finished) return;
31265
31612
  assertStreamComplete(state);
@@ -38596,7 +38943,7 @@ var WorkBuddyAccountPool = class extends AccountPoolCore {
38596
38943
  return refreshed;
38597
38944
  },
38598
38945
  refreshFailureStatus: (error) => error instanceof LlmError && error.code === "INVALID_CREDENTIAL" ? "expired" : void 0,
38599
- extendSummary: (account, base) => ({
38946
+ extendSummary: (account, base, now) => ({
38600
38947
  ...base,
38601
38948
  region: account.region ?? account.credentials.region,
38602
38949
  source: account.source,
@@ -38611,6 +38958,10 @@ var WorkBuddyAccountPool = class extends AccountPoolCore {
38611
38958
  ...(() => {
38612
38959
  const quota = quotaFor(account.id);
38613
38960
  return quota === void 0 ? {} : { quota };
38961
+ })(),
38962
+ ...(() => {
38963
+ const modelCooldowns = this.liveModelCooldowns(account.id, now);
38964
+ return modelCooldowns.length === 0 ? {} : { modelCooldowns };
38614
38965
  })()
38615
38966
  }),
38616
38967
  preferAccountId: () => this.resolveSelectionId(options.selection?.().selectedAccountId ?? null),
@@ -38788,6 +39139,44 @@ var WorkBuddyAccountPool = class extends AccountPoolCore {
38788
39139
  const until = this.modelCooldowns.get(accountId)?.get(model);
38789
39140
  return until !== void 0 && until > now ? until : void 0;
38790
39141
  }
39142
+ /**
39143
+ * The models this account is parked on right now, with the instant each park
39144
+ * lifts.
39145
+ *
39146
+ * Only LIVE parks are reported: a window that has passed is dropped here, so
39147
+ * the card never shows a model that may already serve. The lift instant is
39148
+ * the one the upstream named, stored verbatim by
39149
+ * {@link markModelCooldown}; it is never recomputed from a duration that has
39150
+ * since drifted, because a re-derived instant would drift with it.
39151
+ */
39152
+ liveModelCooldowns(accountId, now) {
39153
+ const models = this.modelCooldowns.get(accountId);
39154
+ if (models === void 0) return [];
39155
+ const live = [];
39156
+ for (const [model, until] of models) if (until > now) live.push({
39157
+ model,
39158
+ cooldownUntil: until
39159
+ });
39160
+ else models.delete(model);
39161
+ if (models.size === 0) this.modelCooldowns.delete(accountId);
39162
+ return live;
39163
+ }
39164
+ /**
39165
+ * Clear one account's cooldown AND the models it is parked on.
39166
+ *
39167
+ * The card's "reset cooldown" button promises the account is usable again. A
39168
+ * model park is a cooldown too — the account stays eligible, but it still
39169
+ * refuses the model the request wants — so leaving the park behind would
39170
+ * report a reset that did not happen, and the park would outlive the only
39171
+ * control the user has over it.
39172
+ *
39173
+ * The base write comes first: it is the half that can fail, and a failed
39174
+ * write must leave the account's state as it was rather than half-cleared.
39175
+ */
39176
+ async clearCooldown(accountId) {
39177
+ await super.clearCooldown(accountId);
39178
+ this.modelCooldowns.delete(accountId);
39179
+ }
38791
39180
  /** Accounts currently parked on one model, with lifted entries pruned on the way. */
38792
39181
  parkedAccountIds(model, now = Date.now()) {
38793
39182
  const parked = /* @__PURE__ */ new Set();
@@ -58666,6 +59055,45 @@ function md5(value) {
58666
59055
  function checkinGatewayOrigin(region) {
58667
59056
  return region === "cn" ? "https://agent.minimaxi.com" : "https://agent.minimax.io";
58668
59057
  }
59058
+ /**
59059
+ * Non-secret identity of the SIGN-IN a check-in credential came from.
59060
+ *
59061
+ * Spelled the way `minimaxCodePoolIdentity` (account-pool.ts) and
59062
+ * `rotationKey` (token-store.ts) spell it — a native record slot, or the
59063
+ * sign-in that produced a plugin-owned credential — because it names the same
59064
+ * thing those do: one session, stable across every token rotation. It is
59065
+ * repeated here rather than imported so this wire half stays free of the pool's
59066
+ * imports.
59067
+ *
59068
+ * **Read this before trusting it as an account id.** It is a rotation key — a
59069
+ * guard rail that tells "the same session, rotated" from "a different session" —
59070
+ * and NOT the authoritative identity of the person behind it:
59071
+ *
59072
+ * - `native:<region>:<recordKey>` names a RECORD SLOT in the desktop app's
59073
+ * `auth.json`, not a user. `recordKey` is a digest of the app's auth-home
59074
+ * path, and the app keeps one record per slot, so signing a DIFFERENT account
59075
+ * in on the desktop replaces that slot's session while the key stays the same.
59076
+ * - `signin:<region>:<loginEpoch>` has the same shape of gap: it names the
59077
+ * sign-in event, not the account it produced.
59078
+ *
59079
+ * The authoritative account fact on this line is therefore the `realUserID`
59080
+ * resolved from the identity surface, and that is what the check-in state pins
59081
+ * and what an attempt is compared against. This key is supporting evidence: it
59082
+ * distinguishes a rotation from a swap, which `realUserID` cannot do without
59083
+ * a request.
59084
+ *
59085
+ * Both fields are deliberately non-secret: a record slot name and a sign-in
59086
+ * epoch, never a token and never a digest of one. That is what makes this the
59087
+ * only account identity allowed to appear in a check-in error message.
59088
+ */
59089
+ function checkinAccountIdentity(credentials) {
59090
+ const region = credentials.region;
59091
+ if (typeof region !== "string" || region === "") return void 0;
59092
+ const recordKey = credentials.recordKey;
59093
+ if (typeof recordKey === "string" && recordKey !== "") return `native:${region}:${recordKey}`;
59094
+ const loginEpoch = credentials.loginEpoch;
59095
+ if (typeof loginEpoch === "string" && loginEpoch !== "") return `signin:${region}:${loginEpoch}`;
59096
+ }
58669
59097
  const DAY_STATUSES = /* @__PURE__ */ new Set([
58670
59098
  1,
58671
59099
  2,
@@ -58972,6 +59400,34 @@ function localDateString(nowMs) {
58972
59400
  const day = `${date.getDate()}`.padStart(2, "0");
58973
59401
  return `${date.getFullYear()}-${month}-${day}`;
58974
59402
  }
59403
+ /**
59404
+ * The check-in refused to act because the account changed **halfway through one
59405
+ * attempt**.
59406
+ *
59407
+ * This is deliberately NOT the "the pool row was re-pointed at another account"
59408
+ * case: that one is a legitimate state change and the entry re-synchronises to
59409
+ * it (see {@link MinimaxCodeCheckinService['identityFor']}). What this class
59410
+ * reports is the one case that must never self-heal — a forced 401 renewal that
59411
+ * answers with a different account — because the panel was already read for one
59412
+ * account and the claim would post as another.
59413
+ *
59414
+ * Its own class so the caller can tell it apart from a transport failure: it is
59415
+ * never retried, and never recorded as "nothing to do today". The message
59416
+ * carries only non-secret facts — the account keys each side resolved to — and
59417
+ * never a token.
59418
+ */
59419
+ var MinimaxCodeCheckinAccountMismatchError = class extends Error {
59420
+ accountId;
59421
+ expectedIdentity;
59422
+ actualIdentity;
59423
+ constructor(accountId, expectedIdentity, actualIdentity) {
59424
+ super(`账号不一致:签到条目 pinned 到 ${expectedIdentity ?? "未知账号"},当前凭据属于 ${actualIdentity ?? "未知账号"};已中止本次签到(account ${accountId}),未领取。`);
59425
+ this.accountId = accountId;
59426
+ this.expectedIdentity = expectedIdentity;
59427
+ this.actualIdentity = actualIdentity;
59428
+ this.name = "MinimaxCodeCheckinAccountMismatchError";
59429
+ }
59430
+ };
58975
59431
  function isRecord(value) {
58976
59432
  return typeof value === "object" && value !== null && !Array.isArray(value);
58977
59433
  }
@@ -58999,6 +59455,33 @@ function parseStateEntry(value) {
58999
59455
  return entry;
59000
59456
  }
59001
59457
  /**
59458
+ * Refuse to act when the credential drifted away from the account this attempt
59459
+ * started on.
59460
+ *
59461
+ * This is the narrow, intra-attempt half of the account guard. The wide half —
59462
+ * "the pool row was re-pointed at another account" — is a legitimate state
59463
+ * change that `identityFor` re-synchronises to; this one is the case that must
59464
+ * never self-heal, because by the time it fires the panel and the claim would
59465
+ * belong to two different accounts.
59466
+ *
59467
+ * The comparison is on {@link checkinAccountIdentity}, which is deliberately
59468
+ * the ROTATION key rather than an account id: a rotated token of the same
59469
+ * sign-in passes by construction, and only a different sign-in fails. What it
59470
+ * cannot promise is that the sign-in behind a key is still the same person —
59471
+ * see the note on `native:<region>:<recordKey>` in {@link checkinAccountIdentity}
59472
+ * — which is exactly why the authoritative account fact in this file stays the
59473
+ * resolved `realUserID`, and why this check is a guard rail rather than the
59474
+ * identity system.
59475
+ *
59476
+ * With no key on either side there is nothing to compare, and comparing keys
59477
+ * that were absent when the attempt began would refuse a legitimate re-sync.
59478
+ */
59479
+ function assertSameAccount(accountId, credentials, pinned) {
59480
+ const actual = checkinAccountIdentity(credentials);
59481
+ if (pinned === void 0 || actual === void 0 || pinned === actual) return;
59482
+ throw new MinimaxCodeCheckinAccountMismatchError(accountId, pinned, actual);
59483
+ }
59484
+ /**
59002
59485
  * The check-in scheduler. One instance per plugin load; the host owns the
59003
59486
  * interval and clears it from the same `ctx.effect` that created it.
59004
59487
  */
@@ -59101,19 +59584,51 @@ var MinimaxCodeCheckinService = class {
59101
59584
  * it, so it is fetched from the identity surface once and kept in the state
59102
59585
  * document. The region is cached alongside because the identity call's
59103
59586
  * origin depends on it.
59587
+ *
59588
+ * The account identity of the resolving credential is pinned to the entry as
59589
+ * well: the pin records which sign-in this realUserID was learned FOR, and it
59590
+ * outlives the token that resolved it.
59591
+ *
59592
+ * **A re-pointed row is a state change, not a fault.** When the pin no longer
59593
+ * matches the credential in hand, the pool row now holds a different account
59594
+ * (the user signed a second account into the desktop app's sheet, or adopted
59595
+ * another session into this row). Following it is the correct outcome: a pool
59596
+ * row IS one account this plugin manages, and the check-in exists to sign in
59597
+ * whatever account that row now holds. Holding the stale pin instead would
59598
+ * mean asking the panel with A's realUserID while presenting B's token —
59599
+ * precisely "B's credential used to check in A" — and would have no exit,
59600
+ * because nothing clears a state document the card cannot reach.
59601
+ *
59602
+ * So a diverging pin re-resolves the identity WITH the credential in hand and
59603
+ * re-pins the entry to it. An entry written before the pin existed carries
59604
+ * `undefined` and re-resolves once too, which is how an old state document
59605
+ * heals itself into a pinned one.
59606
+ *
59607
+ * The one thing that must still never happen — an account changing halfway
59608
+ * through a single attempt — is caught elsewhere, because it cannot be caught
59609
+ * here: {@link attemptOnce}'s panel/claim pair and the forced-renewal guard in
59610
+ * {@link checkinOne}.
59104
59611
  */
59105
- async identityFor(account, accessToken, region) {
59612
+ async identityFor(account, credentials, region) {
59613
+ const credentialIdentity = checkinAccountIdentity(credentials);
59106
59614
  const cached = this.identities[account.id];
59107
- if (cached !== void 0 && cached.region === region) return cached;
59615
+ if (cached !== void 0 && cached.region === region && cached.accountIdentity === credentialIdentity) return {
59616
+ identity: cached,
59617
+ changedFrom: void 0
59618
+ };
59108
59619
  const next = {
59109
- realUserID: (await fetchAccountIdentity(accessToken, region, {
59620
+ realUserID: (await fetchAccountIdentity(credentials.accessToken, region, {
59110
59621
  fetchFn: this.fetchFn,
59111
59622
  ...this.appVersion === void 0 ? {} : { appVersion: this.appVersion }
59112
59623
  })).realUserID,
59113
- region
59624
+ region,
59625
+ ...credentialIdentity === void 0 ? {} : { accountIdentity: credentialIdentity }
59114
59626
  };
59115
59627
  this.identities[account.id] = next;
59116
- return next;
59628
+ return {
59629
+ identity: next,
59630
+ changedFrom: cached?.accountIdentity !== void 0 && cached.accountIdentity !== credentialIdentity ? cached.accountIdentity : void 0
59631
+ };
59117
59632
  }
59118
59633
  /** Check one account in, recording the outcome in its today's entry. */
59119
59634
  async checkinOne(account, today, nowMs) {
@@ -59133,11 +59648,16 @@ var MinimaxCodeCheckinService = class {
59133
59648
  if (!(error instanceof MinimaxCodeCheckinHttpError) || error.status !== 401 && error.status !== 403) throw error;
59134
59649
  delete this.identities[account.id];
59135
59650
  const renewed = await this.pool.renewCredential(account.id, this.fetchFn);
59651
+ const beforeRenewal = checkinAccountIdentity(credentials);
59652
+ const afterRenewal = checkinAccountIdentity(renewed);
59653
+ if (beforeRenewal !== void 0 && afterRenewal !== void 0 && beforeRenewal !== afterRenewal) throw new MinimaxCodeCheckinAccountMismatchError(account.id, beforeRenewal, afterRenewal);
59136
59654
  await this.attemptOnce(account, entry, renewed, region, nowMs);
59137
59655
  }
59138
59656
  } catch (error) {
59139
59657
  entry.attempts += 1;
59140
59658
  entry.lastError = error instanceof Error ? error.message : String(error);
59659
+ entry.inactive = false;
59660
+ delete entry.inactiveAt;
59141
59661
  this.logger?.warn(`[minimax-code-checkin] ${account.id} attempt ${entry.attempts}/3 failed: ${entry.lastError}`);
59142
59662
  }
59143
59663
  }
@@ -59147,23 +59667,39 @@ var MinimaxCodeCheckinService = class {
59147
59667
  * caller's renew-and-retry or attempt bookkeeping.
59148
59668
  */
59149
59669
  async attemptOnce(account, entry, credentials, region, nowMs) {
59150
- const identity = await this.identityFor(account, credentials.accessToken, region);
59151
- const panel = await this.panel(credentials, identity, region);
59670
+ const { identity, changedFrom } = await this.identityFor(account, credentials, region);
59671
+ if (changedFrom !== void 0) this.logger?.warn(`[minimax-code-checkin] ${account.id} account changed since the last pass (${changedFrom} -> ${checkinAccountIdentity(credentials) ?? "unknown"}); entry re-synced to the credential now held`);
59672
+ const pinned = checkinAccountIdentity(credentials);
59673
+ const panel = await this.panel(account, credentials, identity, region, pinned);
59152
59674
  if (this.recordPanelOutcome(entry, panel, account.id, nowMs)) return;
59153
- const claim = await this.claim(credentials, identity, region);
59675
+ const claim = await this.claim(account, credentials, identity, region, pinned);
59154
59676
  this.markSignedIn(entry, claim.panel);
59155
59677
  if (claim.claim_result === 1) entry.claimedPoints = claim.points;
59156
59678
  this.logger?.info(`[minimax-code-checkin] ${account.id} signed in; +${claim.points} credits${claim.claim_result === 2 ? " (already claimed upstream)" : ""}`);
59157
59679
  }
59158
- /** The status call, against the current credential. */
59159
- async panel(credentials, identity, region) {
59680
+ /**
59681
+ * The status call, against the current credential.
59682
+ *
59683
+ * The guard runs BEFORE the panel is read: the credential making this request
59684
+ * must still be the one the identity was resolved with a moment ago.
59685
+ */
59686
+ async panel(account, credentials, identity, region, pinned) {
59687
+ assertSameAccount(account.id, credentials, pinned);
59160
59688
  return fetchSigninPanel(credentials.accessToken, identity.realUserID, region, {
59161
59689
  fetchFn: this.fetchFn,
59162
59690
  ...this.appVersion === void 0 ? {} : { appVersion: this.appVersion }
59163
59691
  });
59164
59692
  }
59165
- /** The claim call, against the current credential. */
59166
- async claim(credentials, identity, region) {
59693
+ /**
59694
+ * The claim call, against the current credential.
59695
+ *
59696
+ * The guard runs again here — the pair the official client protects
59697
+ * (`getSigninPanel` then `claimSignin`) — so a panel read for one account can
59698
+ * never be followed by a claim posted as another, and both calls carry the
59699
+ * same identity value by construction.
59700
+ */
59701
+ async claim(account, credentials, identity, region, pinned) {
59702
+ assertSameAccount(account.id, credentials, pinned);
59167
59703
  return claimSignin(credentials.accessToken, identity.realUserID, region, {
59168
59704
  fetchFn: this.fetchFn,
59169
59705
  ...this.appVersion === void 0 ? {} : { appVersion: this.appVersion }
@@ -59253,7 +59789,8 @@ var MinimaxCodeCheckinService = class {
59253
59789
  if (isRecord(parsed.identities)) {
59254
59790
  for (const [id, value] of Object.entries(parsed.identities)) if (isRecord(value) && typeof value.realUserID === "string" && isRegion(value.region)) this.identities[id] = {
59255
59791
  realUserID: value.realUserID,
59256
- region: value.region
59792
+ region: value.region,
59793
+ ...typeof value.accountIdentity === "string" && value.accountIdentity !== "" ? { accountIdentity: value.accountIdentity } : {}
59257
59794
  };
59258
59795
  }
59259
59796
  }
@@ -62545,7 +63082,7 @@ function resolveProgressUpdatesSettings(config) {
62545
63082
  const enabled = readProgressConfigField(config?.progressUpdates);
62546
63083
  const threshold = readProgressConfigField(config?.progressUpdatesNudgeThreshold);
62547
63084
  return {
62548
- enabled: enabled === true,
63085
+ enabled: enabled !== false,
62549
63086
  nudgeThreshold: typeof threshold === "number" && Number.isSafeInteger(threshold) && threshold > 0 ? threshold : 8
62550
63087
  };
62551
63088
  }
@@ -62745,7 +63282,7 @@ const Config = z.object({
62745
63282
  reasoningCollapseGuard: z.union([z.object({}), z.const(false)]).default(false),
62746
63283
  noAnswerResume: z.union([z.object({}), z.const(false)]).default({}),
62747
63284
  codexStreamIdleTimeoutMs: z.number().min(1).max(MAX_TIMER_DELAY_MS).step(1).default(DEFAULT_STREAM_IDLE_TIMEOUT_MS),
62748
- progressUpdates: z.boolean().default(false).volatile().description("启用进展更新:允许 agent 用 progress_update 工具主动汇报多步任务的进展,并在侧栏展示。"),
63285
+ progressUpdates: z.boolean().default(true).volatile().description("启用进展更新:允许 agent 用 progress_update 工具主动汇报多步任务的进展,并在侧栏展示。"),
62749
63286
  progressUpdatesNudgeThreshold: z.number().step(1).min(1).max(50).default(8).volatile().description("催促阈值:连续这么多轮带工具调用却没有进展更新时,提醒 agent 发一条进展。")
62750
63287
  });
62751
63288
  const inject = [