@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.
- package/CHANGELOG.md +51 -0
- package/README.md +11 -2
- package/lib/client.js +291 -164
- package/lib/client.js.map +1 -1
- package/lib/index.js +694 -157
- package/lib/types/client/CodexSubscriptionSection.d.ts.map +1 -1
- package/lib/types/client/antigravity/locales.d.ts +11 -2
- package/lib/types/client/antigravity/locales.d.ts.map +1 -1
- package/lib/types/client/claude/locales.d.ts +11 -2
- package/lib/types/client/claude/locales.d.ts.map +1 -1
- package/lib/types/client/command-code/locales.d.ts +11 -2
- package/lib/types/client/command-code/locales.d.ts.map +1 -1
- package/lib/types/client/common/AccountPoolSection.d.ts +10 -2
- package/lib/types/client/common/AccountPoolSection.d.ts.map +1 -1
- package/lib/types/client/common/account-pool-labels.d.ts +17 -0
- package/lib/types/client/common/account-pool-labels.d.ts.map +1 -1
- package/lib/types/client/common/format.d.ts +14 -0
- package/lib/types/client/common/format.d.ts.map +1 -1
- package/lib/types/client/common/styles.d.ts.map +1 -1
- package/lib/types/client/glm-coding-plan/locales.d.ts +11 -2
- package/lib/types/client/glm-coding-plan/locales.d.ts.map +1 -1
- package/lib/types/client/glm-coding-plan/useGlmCodingPlanSection.d.ts.map +1 -1
- package/lib/types/client/kimi-code/locales.d.ts +11 -2
- package/lib/types/client/kimi-code/locales.d.ts.map +1 -1
- package/lib/types/client/locales.d.ts +7 -1
- package/lib/types/client/locales.d.ts.map +1 -1
- package/lib/types/client/minimax-code/MinimaxCodeSection.d.ts +8 -5
- package/lib/types/client/minimax-code/MinimaxCodeSection.d.ts.map +1 -1
- package/lib/types/client/minimax-code/locales.d.ts +17 -8
- package/lib/types/client/minimax-code/locales.d.ts.map +1 -1
- package/lib/types/client/minimax-code/useMinimaxCodeSection.d.ts.map +1 -1
- package/lib/types/client/ollama/locales.d.ts +18 -0
- package/lib/types/client/ollama/locales.d.ts.map +1 -1
- package/lib/types/client/progress-updates/index.d.ts.map +1 -1
- package/lib/types/client/step-code/locales.d.ts +11 -2
- package/lib/types/client/step-code/locales.d.ts.map +1 -1
- package/lib/types/client/step-code/useStepCodeSection.d.ts.map +1 -1
- package/lib/types/client/workbuddy/locales.d.ts +6 -0
- package/lib/types/client/workbuddy/locales.d.ts.map +1 -1
- package/lib/types/client/workbuddy/useWorkBuddySection.d.ts.map +1 -1
- package/lib/types/host/antigravity/account-pool.d.ts +17 -3
- package/lib/types/host/antigravity/account-pool.d.ts.map +1 -1
- package/lib/types/host/antigravity/adapter.d.ts +7 -2
- package/lib/types/host/antigravity/adapter.d.ts.map +1 -1
- package/lib/types/host/common/context-overflow.d.ts.map +1 -1
- package/lib/types/host/minimax-code/adapter.d.ts +14 -0
- package/lib/types/host/minimax-code/adapter.d.ts.map +1 -1
- package/lib/types/host/minimax-code/checkin-gateway.d.ts +36 -0
- package/lib/types/host/minimax-code/checkin-gateway.d.ts.map +1 -1
- package/lib/types/host/minimax-code/checkin.d.ts +60 -2
- package/lib/types/host/minimax-code/checkin.d.ts.map +1 -1
- package/lib/types/host/minimax-code/mapper.d.ts +51 -19
- package/lib/types/host/minimax-code/mapper.d.ts.map +1 -1
- package/lib/types/host/minimax-code/model-catalog.d.ts +40 -15
- package/lib/types/host/minimax-code/model-catalog.d.ts.map +1 -1
- package/lib/types/host/minimax-code/token-store.d.ts.map +1 -1
- package/lib/types/host/minimax-code/types.d.ts.map +1 -1
- package/lib/types/host/proxy-manager.d.ts.map +1 -1
- package/lib/types/host/workbuddy/account-pool.d.ts +24 -0
- package/lib/types/host/workbuddy/account-pool.d.ts.map +1 -1
- package/lib/types/index.d.ts +2 -2
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/shared/account-pool-contracts.d.ts +24 -0
- package/lib/types/shared/account-pool-contracts.d.ts.map +1 -1
- package/lib/types/shared/minimax-code-contracts.d.ts +17 -3
- package/lib/types/shared/minimax-code-contracts.d.ts.map +1 -1
- 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
|
-
* 在给一个只认
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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
|
-
/**
|
|
7576
|
-
|
|
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
|
-
|
|
7594
|
-
|
|
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)
|
|
7597
|
-
|
|
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
|
|
7615
|
-
|
|
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
|
|
7970
|
-
|
|
7971
|
-
|
|
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)
|
|
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
|
|
8352
|
-
|
|
8353
|
-
|
|
8354
|
-
|
|
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(
|
|
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$
|
|
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$
|
|
14310
|
-
const data = asRecord$
|
|
14311
|
-
const user = asRecord$
|
|
14312
|
-
const org = asRecord$
|
|
14313
|
-
const key = asRecord$
|
|
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$
|
|
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$
|
|
14423
|
-
const limits = asRecord$
|
|
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$
|
|
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$
|
|
14464
|
-
const credits = asRecord$
|
|
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$
|
|
14512
|
-
return asString$23(deepValue(asRecord$
|
|
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$
|
|
14517
|
-
return parseTimestamp$1(deepValue(asRecord$
|
|
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$
|
|
14735
|
-
const data = asRecord$
|
|
14736
|
-
const credits = asRecord$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
21741
|
-
const nested = asRecord$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
22892
|
+
const record = asRecord$13(entry);
|
|
22796
22893
|
if (record === void 0) continue;
|
|
22797
|
-
const detail = asRecord$
|
|
22798
|
-
const window = asRecord$
|
|
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$
|
|
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$
|
|
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$
|
|
22849
|
-
const wallet = asRecord$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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 === "
|
|
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
|
|
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
|
|
27696
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
27701
|
-
*
|
|
27702
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
27707
|
-
*
|
|
27708
|
-
*
|
|
27709
|
-
*
|
|
27710
|
-
*
|
|
27711
|
-
*
|
|
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
|
|
27716
|
-
* thinking
|
|
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 (
|
|
27727
|
-
const effort = effortForModel(modelId, requestedEffort
|
|
27728
|
-
return 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
|
|
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 ||
|
|
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: [
|
|
28333
|
-
defaultReasoningEffort:
|
|
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: [
|
|
28349
|
-
defaultReasoningEffort:
|
|
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: [
|
|
28369
|
-
|
|
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:
|
|
28380
|
-
optionalContextWindow:
|
|
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: "
|
|
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
|
|
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$
|
|
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$
|
|
29374
|
-
const nested = asRecord$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
30036
|
+
const nestedMessage = asRecord$11(record.error)?.message;
|
|
29905
30037
|
if (typeof nestedMessage === "string") return nestedMessage.slice(0, 400);
|
|
29906
|
-
const baseResp = asRecord$
|
|
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$
|
|
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$
|
|
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$
|
|
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
|
|
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
|
|
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 &&
|
|
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 (
|
|
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
|
-
|
|
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
|
-
|
|
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"))
|
|
31261
|
-
|
|
31262
|
-
|
|
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,
|
|
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
|
|
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
|
|
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
|
|
59151
|
-
|
|
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
|
-
/**
|
|
59159
|
-
|
|
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
|
-
/**
|
|
59166
|
-
|
|
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
|
|
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(
|
|
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 = [
|