@eddyskywalker/dsh-chatgpt-subscription 0.65.0 → 0.66.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 +24 -0
- package/lib/client.js +120 -9
- package/lib/client.js.map +1 -1
- package/lib/index.js +1836 -993
- package/lib/types/client/kimi-code/KimiCodeSection.d.ts.map +1 -1
- package/lib/types/client/kimi-code/locales.d.ts +8 -2
- package/lib/types/client/kimi-code/locales.d.ts.map +1 -1
- package/lib/types/client/locales-ru/kimi-code.d.ts.map +1 -1
- package/lib/types/client/locales-ru/progress-updates.d.ts.map +1 -1
- package/lib/types/client/locales-ru/step-code.d.ts.map +1 -1
- package/lib/types/client/progress-updates/locales.d.ts +23 -11
- package/lib/types/client/progress-updates/locales.d.ts.map +1 -1
- package/lib/types/client/progress-updates/settings-binding.d.ts +11 -1
- package/lib/types/client/progress-updates/settings-binding.d.ts.map +1 -1
- package/lib/types/client/progress-updates/settings-section.d.ts +2 -2
- package/lib/types/client/progress-updates/settings-section.d.ts.map +1 -1
- package/lib/types/client/step-code/StepCodeSection.d.ts.map +1 -1
- package/lib/types/client/step-code/locales.d.ts +5 -2
- package/lib/types/client/step-code/locales.d.ts.map +1 -1
- package/lib/types/compat.d.ts +1 -1
- package/lib/types/host/antigravity/adapter.d.ts +2 -0
- package/lib/types/host/antigravity/adapter.d.ts.map +1 -1
- package/lib/types/host/cache-warmup/budget.d.ts +2 -2
- package/lib/types/host/cache-warmup/budget.d.ts.map +1 -1
- package/lib/types/host/cache-warmup/contracts.d.ts +2 -0
- package/lib/types/host/cache-warmup/contracts.d.ts.map +1 -1
- package/lib/types/host/cache-warmup/install.d.ts +4 -4
- package/lib/types/host/cache-warmup/install.d.ts.map +1 -1
- package/lib/types/host/cache-warmup/result-tool.d.ts +3 -1
- package/lib/types/host/cache-warmup/result-tool.d.ts.map +1 -1
- package/lib/types/host/cache-warmup/settings.d.ts +5 -1
- package/lib/types/host/cache-warmup/settings.d.ts.map +1 -1
- package/lib/types/host/cache-warmup/usage.d.ts +1 -1
- package/lib/types/host/cache-warmup/usage.d.ts.map +1 -1
- package/lib/types/host/claude/adapter.d.ts +3 -0
- package/lib/types/host/claude/adapter.d.ts.map +1 -1
- package/lib/types/host/cline-pass/mapper.d.ts.map +1 -1
- package/lib/types/host/common/account-pool.d.ts +27 -1
- package/lib/types/host/common/account-pool.d.ts.map +1 -1
- package/lib/types/host/common/cache-activity.d.ts +14 -0
- package/lib/types/host/common/cache-activity.d.ts.map +1 -0
- package/lib/types/host/common/cache-affinity.d.ts +23 -0
- package/lib/types/host/common/cache-affinity.d.ts.map +1 -0
- package/lib/types/host/common/cache-hint.d.ts +18 -0
- package/lib/types/host/common/cache-hint.d.ts.map +1 -0
- package/lib/types/host/common/failure-classification.d.ts +21 -0
- package/lib/types/host/common/failure-classification.d.ts.map +1 -0
- package/lib/types/host/common/hosted-media.d.ts +36 -0
- package/lib/types/host/common/hosted-media.d.ts.map +1 -0
- package/lib/types/host/common/prefix-request-coordinator.d.ts +46 -0
- package/lib/types/host/common/prefix-request-coordinator.d.ts.map +1 -0
- package/lib/types/host/common/prefix-stability.d.ts +24 -0
- package/lib/types/host/common/prefix-stability.d.ts.map +1 -0
- package/lib/types/host/common/prompt-cache-policy.d.ts +43 -0
- package/lib/types/host/common/prompt-cache-policy.d.ts.map +1 -0
- package/lib/types/host/common/reasoning-dialect.d.ts +24 -0
- package/lib/types/host/common/reasoning-dialect.d.ts.map +1 -0
- package/lib/types/host/common/session-cache-state.d.ts +29 -0
- package/lib/types/host/common/session-cache-state.d.ts.map +1 -0
- package/lib/types/host/common/sticky-image-budget.d.ts +10 -0
- package/lib/types/host/common/sticky-image-budget.d.ts.map +1 -0
- package/lib/types/host/common/tool-runaway.d.ts +52 -0
- package/lib/types/host/common/tool-runaway.d.ts.map +1 -0
- package/lib/types/host/kimi-code/account-pool.d.ts +19 -2
- package/lib/types/host/kimi-code/account-pool.d.ts.map +1 -1
- package/lib/types/host/kimi-code/adapter.d.ts +44 -30
- package/lib/types/host/kimi-code/adapter.d.ts.map +1 -1
- package/lib/types/host/kimi-code/cache-affinity.d.ts +112 -0
- package/lib/types/host/kimi-code/cache-affinity.d.ts.map +1 -0
- package/lib/types/host/kimi-code/cache-hint.d.ts +3 -28
- package/lib/types/host/kimi-code/cache-hint.d.ts.map +1 -1
- package/lib/types/host/kimi-code/cache-wait.d.ts +6 -4
- package/lib/types/host/kimi-code/cache-wait.d.ts.map +1 -1
- package/lib/types/host/kimi-code/mapper.d.ts +2 -6
- package/lib/types/host/kimi-code/mapper.d.ts.map +1 -1
- package/lib/types/host/kimi-code/media-files.d.ts +2 -28
- package/lib/types/host/kimi-code/media-files.d.ts.map +1 -1
- package/lib/types/host/kimi-code/repeat-breaker.d.ts +13 -16
- package/lib/types/host/kimi-code/repeat-breaker.d.ts.map +1 -1
- package/lib/types/host/kimi-code/routes.d.ts.map +1 -1
- package/lib/types/host/kimi-code/session-cache-state.d.ts +16 -53
- package/lib/types/host/kimi-code/session-cache-state.d.ts.map +1 -1
- package/lib/types/host/minimax-code/adapter.d.ts +5 -1
- package/lib/types/host/minimax-code/adapter.d.ts.map +1 -1
- package/lib/types/host/minimax-code/files-api.d.ts.map +1 -1
- package/lib/types/host/minimax-code/runtime-enhancements.d.ts.map +1 -1
- package/lib/types/host/opendesign/mapper.d.ts.map +1 -1
- package/lib/types/host/qoder/mapper.d.ts.map +1 -1
- package/lib/types/host/responses-client.d.ts +4 -0
- package/lib/types/host/responses-client.d.ts.map +1 -1
- package/lib/types/host/step-code/account-pool.d.ts +2 -2
- package/lib/types/host/step-code/account-pool.d.ts.map +1 -1
- package/lib/types/host/step-code/adapter.d.ts +10 -0
- package/lib/types/host/step-code/adapter.d.ts.map +1 -1
- package/lib/types/host/step-code/routes.d.ts +3 -0
- package/lib/types/host/step-code/routes.d.ts.map +1 -1
- package/lib/types/index.d.ts +22 -3
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/shared/kimi-code-contracts.d.ts +16 -0
- package/lib/types/shared/kimi-code-contracts.d.ts.map +1 -1
- package/lib/types/shared/step-code-contracts.d.ts +5 -0
- package/lib/types/shared/step-code-contracts.d.ts.map +1 -1
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -809,7 +809,7 @@ const ROUTE_PREFIX$5 = "/api/dsh-chatgpt-subscription";
|
|
|
809
809
|
* `test/responses-client.test.ts` asserts the two agree, so a release that
|
|
810
810
|
* forgets this constant fails the suite instead of shipping quietly.
|
|
811
811
|
*/
|
|
812
|
-
const PLUGIN_VERSION = "0.
|
|
812
|
+
const PLUGIN_VERSION = "0.66.0";
|
|
813
813
|
const CODEX_CHATGPT_PROVIDER_ID = "codex-chatgpt";
|
|
814
814
|
const CODEX_API_BASE = "https://chatgpt.com/backend-api/codex";
|
|
815
815
|
const CODEX_RESPONSES_URL = `${CODEX_API_BASE}/responses`;
|
|
@@ -3656,10 +3656,18 @@ var AccountPoolCore = class {
|
|
|
3656
3656
|
return true;
|
|
3657
3657
|
}
|
|
3658
3658
|
/** The account a rotation strategy picks out of an already-eligible set. */
|
|
3659
|
-
selectAccount(eligible, data) {
|
|
3659
|
+
selectAccount(eligible, data, hint) {
|
|
3660
3660
|
const pinned = this.hooks.preferAccountId?.() ?? null;
|
|
3661
3661
|
const pinnedAccount = pinned === null ? void 0 : eligible.find((account) => account.id === pinned);
|
|
3662
3662
|
if (pinnedAccount !== void 0) return pinnedAccount;
|
|
3663
|
+
const preferred = hint?.preferAccountIds;
|
|
3664
|
+
if (preferred !== void 0 && preferred.length > 0) {
|
|
3665
|
+
const eligibleById = new Map(eligible.map((account) => [account.id, account]));
|
|
3666
|
+
for (const id of preferred) {
|
|
3667
|
+
const account = eligibleById.get(id);
|
|
3668
|
+
if (account !== void 0) return account;
|
|
3669
|
+
}
|
|
3670
|
+
}
|
|
3663
3671
|
if (data.rotationStrategy === "round-robin") return [...eligible].sort((a, b) => (a.lastUsedAt || 0) - (b.lastUsedAt || 0))[0];
|
|
3664
3672
|
if (data.rotationStrategy === "sticky") return eligible.find((account) => account.id === data.activeAccountId) || eligible.find((account) => account.isPrimary) || eligible[0];
|
|
3665
3673
|
return eligible.find((account) => account.isPrimary) || eligible[0];
|
|
@@ -3712,15 +3720,18 @@ var AccountPoolCore = class {
|
|
|
3712
3720
|
* @param forceRefresh - renew even when the stored token still looks usable.
|
|
3713
3721
|
* @param options - what the refresh may spend, passed down to the provider's
|
|
3714
3722
|
* refresh hook. Absent means the provider's own default budget.
|
|
3723
|
+
* @param selection - this request's own account preference, when the caller
|
|
3724
|
+
* has one (a warm prefix cache, for instance). Absent means the rotation
|
|
3725
|
+
* strategy decides alone, exactly as before.
|
|
3715
3726
|
* @throws LlmError with `RATE_LIMIT` when every account is cooling down.
|
|
3716
3727
|
*/
|
|
3717
|
-
async getEffectiveAccount(excludeIds, fetchFn = fetch, forceRefresh = false, options) {
|
|
3728
|
+
async getEffectiveAccount(excludeIds, fetchFn = fetch, forceRefresh = false, options, selection) {
|
|
3718
3729
|
const data = await this.read();
|
|
3719
3730
|
if (data.accounts.length === 0) throw new Error(this.hooks.emptyMessage ?? t("pool.notSignedIn", { provider: this.hooks.displayName }));
|
|
3720
3731
|
const now = Date.now();
|
|
3721
3732
|
const eligible = data.accounts.filter((account) => this.isEligible(account, now, excludeIds));
|
|
3722
3733
|
if (eligible.length === 0) throw this.allUnavailableError(data, now);
|
|
3723
|
-
const selected = this.selectAccount(eligible, data);
|
|
3734
|
+
const selected = this.selectAccount(eligible, data, selection);
|
|
3724
3735
|
let refreshed;
|
|
3725
3736
|
try {
|
|
3726
3737
|
refreshed = await this.freshAccount(selected, fetchFn, forceRefresh, options);
|
|
@@ -3728,7 +3739,7 @@ var AccountPoolCore = class {
|
|
|
3728
3739
|
if (this.hooks.refreshFailureStatus?.(error) === void 0 && !(error instanceof PoolAccountUnavailableError)) throw error;
|
|
3729
3740
|
const nextExclude = new Set(excludeIds ?? []);
|
|
3730
3741
|
nextExclude.add(selected.id);
|
|
3731
|
-
if (await this.hasAnotherAvailableAccount(nextExclude)) return this.getEffectiveAccount(nextExclude, fetchFn, forceRefresh, options);
|
|
3742
|
+
if (await this.hasAnotherAvailableAccount(nextExclude)) return this.getEffectiveAccount(nextExclude, fetchFn, forceRefresh, options, selection);
|
|
3732
3743
|
throw error;
|
|
3733
3744
|
}
|
|
3734
3745
|
const account = await this.updatePool((current) => {
|
|
@@ -4477,7 +4488,7 @@ var OAuthService = class {
|
|
|
4477
4488
|
throw new OAuthServiceError("oauth-token-exchange-failed", `The ChatGPT device code could not be issued (${response.status}${detail === null ? "" : `, ${detail}`}).`, response.status);
|
|
4478
4489
|
}
|
|
4479
4490
|
const payload = await response.json().catch(() => null);
|
|
4480
|
-
const record = isRecord$
|
|
4491
|
+
const record = isRecord$70(payload) ? payload : {};
|
|
4481
4492
|
const authId = nonEmptyString$11(record.device_auth_id);
|
|
4482
4493
|
const userCode = nonEmptyString$11(record.user_code) ?? nonEmptyString$11(record.usercode);
|
|
4483
4494
|
if (authId === void 0 || userCode === void 0) throw new OAuthServiceError("oauth-token-exchange-failed", "ChatGPT returned no device code.");
|
|
@@ -4544,7 +4555,7 @@ var OAuthService = class {
|
|
|
4544
4555
|
const detail = deviceErrorIdentifier(payload);
|
|
4545
4556
|
throw new OAuthServiceError("oauth-token-exchange-failed", `The ChatGPT device confirmation was refused (${response.status}${detail === void 0 ? "" : `, ${detail}`}).`, response.status);
|
|
4546
4557
|
}
|
|
4547
|
-
const record = isRecord$
|
|
4558
|
+
const record = isRecord$70(payload) ? payload : {};
|
|
4548
4559
|
const authorizationCode = nonEmptyString$11(record.authorization_code);
|
|
4549
4560
|
const codeVerifier = nonEmptyString$11(record.code_verifier);
|
|
4550
4561
|
if (authorizationCode === void 0 || codeVerifier === void 0) throw new OAuthServiceError("oauth-token-exchange-failed", "ChatGPT confirmed the device code but returned no authorization code.");
|
|
@@ -4877,11 +4888,11 @@ const DEVICE_PENDING_STATUSES = /* @__PURE__ */ new Set([
|
|
|
4877
4888
|
function isPendingDeviceAnswer(response, payload) {
|
|
4878
4889
|
if (response.status === 403 || response.status === 404) return true;
|
|
4879
4890
|
if (!response.ok && response.status !== 400) return false;
|
|
4880
|
-
if (!isRecord$
|
|
4891
|
+
if (!isRecord$70(payload)) return false;
|
|
4881
4892
|
const status = payload.status;
|
|
4882
4893
|
if (typeof status === "string" && DEVICE_PENDING_STATUSES.has(status.toLowerCase())) return true;
|
|
4883
4894
|
const error = payload.error;
|
|
4884
|
-
if (isRecord$
|
|
4895
|
+
if (isRecord$70(error)) {
|
|
4885
4896
|
const code = error.code;
|
|
4886
4897
|
if (typeof code === "string" && DEVICE_PENDING_STATUSES.has(code.toLowerCase())) return true;
|
|
4887
4898
|
}
|
|
@@ -4895,18 +4906,18 @@ function isPendingDeviceAnswer(response, payload) {
|
|
|
4895
4906
|
* wrong.
|
|
4896
4907
|
*/
|
|
4897
4908
|
function deviceErrorIdentifier(payload) {
|
|
4898
|
-
if (!isRecord$
|
|
4909
|
+
if (!isRecord$70(payload)) return void 0;
|
|
4899
4910
|
const candidates = [payload.code];
|
|
4900
4911
|
const error = payload.error;
|
|
4901
4912
|
if (typeof error === "string") candidates.push(error);
|
|
4902
|
-
else if (isRecord$
|
|
4913
|
+
else if (isRecord$70(error)) candidates.push(error.code, error.type);
|
|
4903
4914
|
candidates.push(payload.status);
|
|
4904
4915
|
for (const candidate of candidates) if (typeof candidate === "string" && /^[a-z0-9_.-]{1,64}$/i.test(candidate)) return candidate;
|
|
4905
4916
|
}
|
|
4906
4917
|
function nonEmptyString$11(value) {
|
|
4907
4918
|
return typeof value === "string" && value !== "" ? value : void 0;
|
|
4908
4919
|
}
|
|
4909
|
-
function isRecord$
|
|
4920
|
+
function isRecord$70(value) {
|
|
4910
4921
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4911
4922
|
}
|
|
4912
4923
|
async function oauthErrorIdentifier(response) {
|
|
@@ -5087,7 +5098,7 @@ const CODEX_CLI_CREDENTIAL_SOURCE = "codex";
|
|
|
5087
5098
|
* here, and the import has to be repeated afterwards.
|
|
5088
5099
|
*/
|
|
5089
5100
|
const ADOPTED_CODEX_CREDENTIAL_EXPIRED_HINT = "The sign-in imported from the Codex CLI has expired. Sign in again in the Codex CLI, then import it again from this settings card.";
|
|
5090
|
-
function isRecord$
|
|
5101
|
+
function isRecord$69(value) {
|
|
5091
5102
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
5092
5103
|
}
|
|
5093
5104
|
/** A trimmed, non-empty string, or nothing. */
|
|
@@ -5189,7 +5200,7 @@ function decodeJwtClaims$1(token) {
|
|
|
5189
5200
|
if (parts.length !== 3) return void 0;
|
|
5190
5201
|
try {
|
|
5191
5202
|
const value = JSON.parse(Buffer.from(parts[1], "base64url").toString("utf8"));
|
|
5192
|
-
return isRecord$
|
|
5203
|
+
return isRecord$69(value) ? value : void 0;
|
|
5193
5204
|
} catch {
|
|
5194
5205
|
return;
|
|
5195
5206
|
}
|
|
@@ -5234,10 +5245,10 @@ function expiryFromAccessToken(accessToken) {
|
|
|
5234
5245
|
* the file carries cannot reach the result.
|
|
5235
5246
|
*/
|
|
5236
5247
|
function parseAdoptedCodexCredential(value, sourcePath) {
|
|
5237
|
-
if (!isRecord$
|
|
5248
|
+
if (!isRecord$69(value)) return void 0;
|
|
5238
5249
|
if (value.auth_mode !== "chatgpt") return void 0;
|
|
5239
5250
|
const tokens = value[CODEX_CLI_TOKENS_KEY];
|
|
5240
|
-
if (!isRecord$
|
|
5251
|
+
if (!isRecord$69(tokens)) return void 0;
|
|
5241
5252
|
const accessToken = nonEmptyString$10(tokens.access_token);
|
|
5242
5253
|
if (accessToken === void 0) return void 0;
|
|
5243
5254
|
const refreshToken = nonEmptyString$10(tokens.refresh_token);
|
|
@@ -5256,7 +5267,7 @@ function parseAdoptedCodexCredential(value, sourcePath) {
|
|
|
5256
5267
|
const email = claims === void 0 ? void 0 : nonEmptyString$10(claims.email);
|
|
5257
5268
|
if (email !== void 0) credentials.email = email;
|
|
5258
5269
|
const authClaims = claims === void 0 ? void 0 : claims[CODEX_AUTH_CLAIMS_KEY];
|
|
5259
|
-
const planType = isRecord$
|
|
5270
|
+
const planType = isRecord$69(authClaims) ? nonEmptyString$10(authClaims.chatgpt_plan_type) : void 0;
|
|
5260
5271
|
if (planType !== void 0) credentials.planType = planType;
|
|
5261
5272
|
}
|
|
5262
5273
|
const accountId = nonEmptyString$10(tokens.account_id);
|
|
@@ -8149,7 +8160,7 @@ const schemaFields = /* @__PURE__ */ new Set([
|
|
|
8149
8160
|
"required",
|
|
8150
8161
|
"propertyOrdering"
|
|
8151
8162
|
]);
|
|
8152
|
-
function isRecord$
|
|
8163
|
+
function isRecord$68(value) {
|
|
8153
8164
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
8154
8165
|
}
|
|
8155
8166
|
function mergeSchemas(left, right) {
|
|
@@ -8157,7 +8168,7 @@ function mergeSchemas(left, right) {
|
|
|
8157
8168
|
...left,
|
|
8158
8169
|
...right
|
|
8159
8170
|
};
|
|
8160
|
-
if (isRecord$
|
|
8171
|
+
if (isRecord$68(left.properties) && isRecord$68(right.properties)) merged.properties = {
|
|
8161
8172
|
...left.properties,
|
|
8162
8173
|
...right.properties
|
|
8163
8174
|
};
|
|
@@ -8193,9 +8204,9 @@ const mergedTypeKeys = /* @__PURE__ */ new Set([
|
|
|
8193
8204
|
* model with strictly less than the schema it started from.
|
|
8194
8205
|
*/
|
|
8195
8206
|
function describeBranch(branch) {
|
|
8196
|
-
const type = typeof branch.type === "string" ? branch.type : isRecord$
|
|
8207
|
+
const type = typeof branch.type === "string" ? branch.type : isRecord$68(branch.properties) ? "object" : isRecord$68(branch.items) ? "array" : "value";
|
|
8197
8208
|
const details = [];
|
|
8198
|
-
if (isRecord$
|
|
8209
|
+
if (isRecord$68(branch.properties)) {
|
|
8199
8210
|
const names = Object.keys(branch.properties);
|
|
8200
8211
|
if (names.length > 0) details.push(`{${names.join(",")}}`);
|
|
8201
8212
|
}
|
|
@@ -8209,7 +8220,7 @@ function describeBranch(branch) {
|
|
|
8209
8220
|
}
|
|
8210
8221
|
/** How well one branch can stand in for a whole union; a named object shape wins. */
|
|
8211
8222
|
function branchRank(branch) {
|
|
8212
|
-
if (isRecord$
|
|
8223
|
+
if (isRecord$68(branch.properties)) return 3;
|
|
8213
8224
|
if (branch.type === "object") return 2;
|
|
8214
8225
|
if (branch.type === "array") return 1;
|
|
8215
8226
|
return 0;
|
|
@@ -8238,7 +8249,7 @@ function unionShape(branches) {
|
|
|
8238
8249
|
shape: branches[0],
|
|
8239
8250
|
folded: false
|
|
8240
8251
|
};
|
|
8241
|
-
if (new Set(branches.map((branch) => branch.type)).size === 1 && branches.every((branch) => !isRecord$
|
|
8252
|
+
if (new Set(branches.map((branch) => branch.type)).size === 1 && branches.every((branch) => !isRecord$68(branch.properties) && !isRecord$68(branch.items))) {
|
|
8242
8253
|
const shape = { type: branches[0].type };
|
|
8243
8254
|
if (branches.every((branch) => Array.isArray(branch.enum))) {
|
|
8244
8255
|
const values = [...new Set(branches.flatMap((branch) => branch.enum))];
|
|
@@ -8275,7 +8286,7 @@ function foldOwnFields(node) {
|
|
|
8275
8286
|
const out = {};
|
|
8276
8287
|
for (const [key, value] of Object.entries(node)) {
|
|
8277
8288
|
if (key === "anyOf" || key === "oneOf" || key === "allOf") continue;
|
|
8278
|
-
if (key === "properties" && isRecord$
|
|
8289
|
+
if (key === "properties" && isRecord$68(value)) {
|
|
8279
8290
|
out.properties = Object.fromEntries(Object.entries(value).map(([name, child]) => [name, foldSchemaUnions(child)]));
|
|
8280
8291
|
continue;
|
|
8281
8292
|
}
|
|
@@ -8304,15 +8315,15 @@ function foldOwnFields(node) {
|
|
|
8304
8315
|
* argument the model sends anyway fails there, naming the actual problem.
|
|
8305
8316
|
*/
|
|
8306
8317
|
function foldSchemaUnions(node) {
|
|
8307
|
-
if (!isRecord$
|
|
8318
|
+
if (!isRecord$68(node)) return node;
|
|
8308
8319
|
let own = foldOwnFields(node);
|
|
8309
8320
|
if (Array.isArray(node.allOf)) for (const branch of node.allOf) {
|
|
8310
8321
|
const folded = foldSchemaUnions(branch);
|
|
8311
|
-
if (isRecord$
|
|
8322
|
+
if (isRecord$68(folded)) own = mergeSchemas(own, folded);
|
|
8312
8323
|
}
|
|
8313
8324
|
const alternatives = Array.isArray(node.anyOf) ? node.anyOf : Array.isArray(node.oneOf) ? node.oneOf : [];
|
|
8314
8325
|
if (alternatives.length === 0) return own;
|
|
8315
|
-
const branches = alternatives.map((branch) => foldSchemaUnions(branch)).filter(isRecord$
|
|
8326
|
+
const branches = alternatives.map((branch) => foldSchemaUnions(branch)).filter(isRecord$68);
|
|
8316
8327
|
if (branches.length === 0) return own;
|
|
8317
8328
|
const shapes = branches.filter((branch) => branch.type !== "null");
|
|
8318
8329
|
const acceptsNull = branches.some((branch) => branch.type === "null");
|
|
@@ -8328,7 +8339,7 @@ function foldSchemaUnions(node) {
|
|
|
8328
8339
|
return merged;
|
|
8329
8340
|
}
|
|
8330
8341
|
function toAntigravityToolSchema(schema, options = {}) {
|
|
8331
|
-
if (!isRecord$
|
|
8342
|
+
if (!isRecord$68(schema)) return schema;
|
|
8332
8343
|
const root = schema;
|
|
8333
8344
|
function resolveReference(reference) {
|
|
8334
8345
|
let target = root;
|
|
@@ -8336,13 +8347,13 @@ function toAntigravityToolSchema(schema, options = {}) {
|
|
|
8336
8347
|
const path = reference === "#" ? [] : reference.slice(2).split("/");
|
|
8337
8348
|
for (const segment of path) {
|
|
8338
8349
|
const key = segment.replace(/~1/g, "/").replace(/~0/g, "~");
|
|
8339
|
-
target = isRecord$
|
|
8350
|
+
target = isRecord$68(target) && Object.hasOwn(target, key) ? target[key] : void 0;
|
|
8340
8351
|
}
|
|
8341
|
-
if (!isRecord$
|
|
8352
|
+
if (!isRecord$68(target)) throw new LlmError(`Antigravity tool schema reference was not found: ${reference}`, "PROVIDER_ERROR");
|
|
8342
8353
|
return target;
|
|
8343
8354
|
}
|
|
8344
8355
|
function convert(node, references) {
|
|
8345
|
-
if (!isRecord$
|
|
8356
|
+
if (!isRecord$68(node)) return {};
|
|
8346
8357
|
let inherited = {};
|
|
8347
8358
|
if (typeof node.$ref === "string") {
|
|
8348
8359
|
if (references.has(node.$ref)) throw new LlmError(`Antigravity tool schema contains a recursive reference: ${node.$ref}`, "PROVIDER_ERROR");
|
|
@@ -8351,8 +8362,8 @@ function toAntigravityToolSchema(schema, options = {}) {
|
|
|
8351
8362
|
if (Array.isArray(node.allOf)) for (const branch of node.allOf) inherited = mergeSchemas(inherited, convert(branch, references));
|
|
8352
8363
|
const out = {};
|
|
8353
8364
|
for (const [key, value] of Object.entries(node)) if (schemaFields.has(key)) out[key] = value;
|
|
8354
|
-
if (isRecord$
|
|
8355
|
-
if (isRecord$
|
|
8365
|
+
if (isRecord$68(node.properties)) out.properties = Object.fromEntries(Object.entries(node.properties).map(([name, child]) => [name, convert(child, references)]));
|
|
8366
|
+
if (isRecord$68(node.items)) out.items = convert(node.items, references);
|
|
8356
8367
|
const alternatives = Array.isArray(node.anyOf) ? node.anyOf : node.oneOf;
|
|
8357
8368
|
if (Array.isArray(alternatives)) out.anyOf = alternatives.map((child) => convert(child, references));
|
|
8358
8369
|
if (Array.isArray(node.type)) {
|
|
@@ -8388,7 +8399,7 @@ let toolCallCounter = 0;
|
|
|
8388
8399
|
function sanitizeText$9(text) {
|
|
8389
8400
|
return text.replace(/\0/g, "");
|
|
8390
8401
|
}
|
|
8391
|
-
function isRecord$
|
|
8402
|
+
function isRecord$67(value) {
|
|
8392
8403
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
8393
8404
|
}
|
|
8394
8405
|
function asString$30(value) {
|
|
@@ -8408,25 +8419,25 @@ function toolCallIdNeeded(modelId, runtimeModel) {
|
|
|
8408
8419
|
return modelId.startsWith("claude-") || modelId.startsWith("gpt-oss-") || runtimeModel.startsWith("claude-") || runtimeModel.startsWith("gpt-oss-");
|
|
8409
8420
|
}
|
|
8410
8421
|
function parseArguments$1(raw) {
|
|
8411
|
-
if (isRecord$
|
|
8422
|
+
if (isRecord$67(raw)) return raw;
|
|
8412
8423
|
if (raw === void 0 || raw === null || raw === "") return {};
|
|
8413
8424
|
const parsed = typeof raw === "string" ? safeJsonParse$9(raw) : raw;
|
|
8414
|
-
return isRecord$
|
|
8425
|
+
return isRecord$67(parsed) ? parsed : {};
|
|
8415
8426
|
}
|
|
8416
8427
|
const NO_RESOLVED_IMAGES$5 = /* @__PURE__ */ new Map();
|
|
8417
8428
|
function attachmentOf$5(block) {
|
|
8418
8429
|
const attachment = block.attachment;
|
|
8419
|
-
if (!isRecord$
|
|
8430
|
+
if (!isRecord$67(attachment)) return void 0;
|
|
8420
8431
|
return typeof attachment.attachmentId === "string" ? attachment : void 0;
|
|
8421
8432
|
}
|
|
8422
8433
|
function attachmentLabel$6(block) {
|
|
8423
|
-
const attachment = isRecord$
|
|
8434
|
+
const attachment = isRecord$67(block.attachment) ? block.attachment : void 0;
|
|
8424
8435
|
return asString$30(attachment?.name) || asString$30(attachment?.attachmentId);
|
|
8425
8436
|
}
|
|
8426
8437
|
function collectImageRefs$3(content, refs) {
|
|
8427
8438
|
if (!Array.isArray(content)) return;
|
|
8428
8439
|
for (const block of content) {
|
|
8429
|
-
if (!isRecord$
|
|
8440
|
+
if (!isRecord$67(block)) continue;
|
|
8430
8441
|
if (block.type === "image") {
|
|
8431
8442
|
const attachment = attachmentOf$5(block);
|
|
8432
8443
|
if (attachment) refs.set(attachment.attachmentId, attachment);
|
|
@@ -8559,7 +8570,7 @@ function unavailableImageText$6(block) {
|
|
|
8559
8570
|
}
|
|
8560
8571
|
function imageBlockToPart(block, images) {
|
|
8561
8572
|
let data = asString$30(block.data) || asString$30(block.base64);
|
|
8562
|
-
const source = isRecord$
|
|
8573
|
+
const source = isRecord$67(block.source) ? block.source : void 0;
|
|
8563
8574
|
if (!data && source) data = asString$30(source.data) || asString$30(source.base64);
|
|
8564
8575
|
let mimeType = asString$30(block.mimeType) || asString$30(block.mediaType) || (source ? asString$30(source.mimeType) || asString$30(source.mediaType) : void 0) || "image/png";
|
|
8565
8576
|
if (data?.startsWith("data:")) {
|
|
@@ -8584,8 +8595,8 @@ function contentToUserParts(content, images) {
|
|
|
8584
8595
|
if (typeof content === "string") return [{ text: sanitizeText$9(content) }];
|
|
8585
8596
|
if (!Array.isArray(content)) return [];
|
|
8586
8597
|
const parts = [];
|
|
8587
|
-
for (const block of content) if (isRecord$
|
|
8588
|
-
else if (isRecord$
|
|
8598
|
+
for (const block of content) if (isRecord$67(block) && block.type === "text" && typeof block.text === "string") parts.push({ text: sanitizeText$9(block.text) });
|
|
8599
|
+
else if (isRecord$67(block) && block.type === "image") {
|
|
8589
8600
|
const img = imageBlockToPart(block, images);
|
|
8590
8601
|
parts.push(img ?? { text: unavailableImageText$6(block) });
|
|
8591
8602
|
}
|
|
@@ -8600,7 +8611,7 @@ function toolResultImageParts(blocks, images) {
|
|
|
8600
8611
|
if (!Array.isArray(blocks)) return [];
|
|
8601
8612
|
const parts = [];
|
|
8602
8613
|
for (const block of blocks) {
|
|
8603
|
-
if (!isRecord$
|
|
8614
|
+
if (!isRecord$67(block)) continue;
|
|
8604
8615
|
if (block.type === "image") {
|
|
8605
8616
|
parts.push(imageBlockToPart(block, images) ?? { text: unavailableImageText$6(block) });
|
|
8606
8617
|
continue;
|
|
@@ -8612,7 +8623,7 @@ function toolResultImageParts(blocks, images) {
|
|
|
8612
8623
|
function toolResultText$9(blocks) {
|
|
8613
8624
|
if (!Array.isArray(blocks)) return "";
|
|
8614
8625
|
return blocks.map((block) => {
|
|
8615
|
-
if (!isRecord$
|
|
8626
|
+
if (!isRecord$67(block)) return "";
|
|
8616
8627
|
if (block.type === "text" && typeof block.text === "string") return sanitizeText$9(block.text);
|
|
8617
8628
|
if (block.type === "tool-result") return toolResultText$9(block.content);
|
|
8618
8629
|
if (block.type === "image") {
|
|
@@ -8626,9 +8637,9 @@ function replayBlockFor$1(message, index) {
|
|
|
8626
8637
|
const source = message.source;
|
|
8627
8638
|
if (!source || source.kind !== "model" || source.provider !== "antigravity") return void 0;
|
|
8628
8639
|
const state = source.replayState;
|
|
8629
|
-
if (!isRecord$
|
|
8640
|
+
if (!isRecord$67(state)) return void 0;
|
|
8630
8641
|
if (Array.isArray(state.blocks)) return state.blocks[index];
|
|
8631
|
-
const resp = isRecord$
|
|
8642
|
+
const resp = isRecord$67(state.response) ? state.response : void 0;
|
|
8632
8643
|
if (resp) {
|
|
8633
8644
|
if (Array.isArray(resp.outputItems)) return resp.outputItems[index];
|
|
8634
8645
|
if (Array.isArray(resp.blocks)) return resp.blocks[index];
|
|
@@ -8730,10 +8741,10 @@ function assistantParts(message, model, runtimeModel, toolCalls) {
|
|
|
8730
8741
|
if (!Array.isArray(message.content)) return parts;
|
|
8731
8742
|
for (let index = 0; index < message.content.length; index++) {
|
|
8732
8743
|
const block = message.content[index];
|
|
8733
|
-
if (!isRecord$
|
|
8744
|
+
if (!isRecord$67(block)) continue;
|
|
8734
8745
|
if (block.type === "reasoning" && runtimeModel.startsWith("claude-") && (message.source?.kind !== "model" || message.source.provider !== "antigravity" || !("model" in message.source) || message.source.model !== model.id)) continue;
|
|
8735
8746
|
const replay = replayBlockFor$1(message, index);
|
|
8736
|
-
const originalParts = Array.isArray(replay?.parts) ? replay.parts.filter(isRecord$
|
|
8747
|
+
const originalParts = Array.isArray(replay?.parts) ? replay.parts.filter(isRecord$67) : [];
|
|
8737
8748
|
if ((block.type === "text" || block.type === "reasoning") && originalParts.length > 0 && originalParts.every((part) => !part.functionCall) && originalParts.map((part) => asString$30(part.text) || "").join("") === sanitizeText$9(String(block.text || ""))) {
|
|
8738
8749
|
parts.push(...originalParts.map(replayPart));
|
|
8739
8750
|
continue;
|
|
@@ -8754,8 +8765,8 @@ function assistantParts(message, model, runtimeModel, toolCalls) {
|
|
|
8754
8765
|
} else if (block.type === "tool-call") {
|
|
8755
8766
|
const toolId = String(block.id || "");
|
|
8756
8767
|
const toolName = String(block.name || "");
|
|
8757
|
-
const originalCall = originalParts.find((part) => isRecord$
|
|
8758
|
-
const wireId = asString$30((isRecord$
|
|
8768
|
+
const originalCall = originalParts.find((part) => isRecord$67(part.functionCall));
|
|
8769
|
+
const wireId = asString$30((isRecord$67(originalCall?.functionCall) ? originalCall.functionCall : void 0)?.id) || (toolCallIdNeeded(model.id, runtimeModel) ? sanitizeToolCallId(toolId, toolName) : originalCall ? void 0 : toolId || void 0);
|
|
8759
8770
|
toolCalls.set(toolId, {
|
|
8760
8771
|
name: toolName,
|
|
8761
8772
|
id: wireId
|
|
@@ -8807,7 +8818,7 @@ function convertMessages(options, model, runtimeModel, images = NO_RESOLVED_IMAG
|
|
|
8807
8818
|
continue;
|
|
8808
8819
|
}
|
|
8809
8820
|
const content = Array.isArray(message.content) ? message.content : [];
|
|
8810
|
-
const userParts = contentToUserParts(content.filter((b) => !isRecord$
|
|
8821
|
+
const userParts = contentToUserParts(content.filter((b) => !isRecord$67(b) || b.type !== "tool-result"), images);
|
|
8811
8822
|
if (role === "system") {
|
|
8812
8823
|
if (userParts.length) contents.push({
|
|
8813
8824
|
role: GEMINI_ROLE.user,
|
|
@@ -8819,7 +8830,7 @@ function convertMessages(options, model, runtimeModel, images = NO_RESOLVED_IMAG
|
|
|
8819
8830
|
role: GEMINI_ROLE.user,
|
|
8820
8831
|
parts: userParts
|
|
8821
8832
|
});
|
|
8822
|
-
for (const b of content) if (isRecord$
|
|
8833
|
+
for (const b of content) if (isRecord$67(b) && b.type === "tool-result") pushToolResult(contents, b, toolCalls, model, runtimeModel, images);
|
|
8823
8834
|
}
|
|
8824
8835
|
return contents;
|
|
8825
8836
|
}
|
|
@@ -8962,7 +8973,7 @@ const USAGE_FIELDS = [
|
|
|
8962
8973
|
"totalTokenCount"
|
|
8963
8974
|
];
|
|
8964
8975
|
function collectUsage(value, state) {
|
|
8965
|
-
if (!isRecord$
|
|
8976
|
+
if (!isRecord$67(value)) return;
|
|
8966
8977
|
for (const key of USAGE_FIELDS) {
|
|
8967
8978
|
const count = value[key];
|
|
8968
8979
|
if (typeof count !== "number" || !Number.isSafeInteger(count) || count < 0) continue;
|
|
@@ -8992,17 +9003,17 @@ function processStreamLine$4(line, state) {
|
|
|
8992
9003
|
}
|
|
8993
9004
|
if (!json) return [];
|
|
8994
9005
|
const chunk = safeJsonParse$9(json);
|
|
8995
|
-
if (!isRecord$
|
|
8996
|
-
const responseData = isRecord$
|
|
8997
|
-
const error = isRecord$
|
|
9006
|
+
if (!isRecord$67(chunk)) return [];
|
|
9007
|
+
const responseData = isRecord$67(chunk.response) ? chunk.response : chunk;
|
|
9008
|
+
const error = isRecord$67(chunk.error) ? chunk.error : isRecord$67(responseData.error) ? responseData.error : void 0;
|
|
8998
9009
|
if (error !== void 0) throw new LlmError$1(`Antigravity stream error: ${asString$30(error.message) ?? "unknown error"}`, isContextOverflow(error) ? CONTEXT_OVERFLOW_CODE : "PROVIDER_ERROR");
|
|
8999
9010
|
const candidates = Array.isArray(responseData.candidates) ? responseData.candidates : [];
|
|
9000
|
-
const candidate = isRecord$
|
|
9001
|
-
const content = isRecord$
|
|
9011
|
+
const candidate = isRecord$67(candidates[0]) ? candidates[0] : void 0;
|
|
9012
|
+
const content = isRecord$67(candidate?.content) ? candidate.content : void 0;
|
|
9002
9013
|
const parts = Array.isArray(content?.parts) ? content.parts : [];
|
|
9003
9014
|
const out = [];
|
|
9004
9015
|
for (const part of parts) {
|
|
9005
|
-
if (!isRecord$
|
|
9016
|
+
if (!isRecord$67(part)) continue;
|
|
9006
9017
|
if (typeof part.text === "string" && part.text !== "") {
|
|
9007
9018
|
const isThinking = Boolean(part.thought);
|
|
9008
9019
|
const blockType = isThinking ? "reasoning" : "text";
|
|
@@ -9034,7 +9045,7 @@ function processStreamLine$4(line, state) {
|
|
|
9034
9045
|
index: state.currentBlock.index,
|
|
9035
9046
|
text: delta
|
|
9036
9047
|
});
|
|
9037
|
-
} else if (!isRecord$
|
|
9048
|
+
} else if (!isRecord$67(part.functionCall) && thoughtSignature(part)) {
|
|
9038
9049
|
if (state.replayBlocks.length === 0) {
|
|
9039
9050
|
const type = part.thought ? "reasoning" : "text";
|
|
9040
9051
|
state.blocks.push({
|
|
@@ -9058,12 +9069,12 @@ function processStreamLine$4(line, state) {
|
|
|
9058
9069
|
}
|
|
9059
9070
|
state.replayBlocks[state.replayBlocks.length - 1].parts.push(replayPart(part));
|
|
9060
9071
|
}
|
|
9061
|
-
if (isRecord$
|
|
9072
|
+
if (isRecord$67(part.functionCall)) {
|
|
9062
9073
|
out.push(...closeCurrentBlock(state));
|
|
9063
9074
|
const fc = part.functionCall;
|
|
9064
9075
|
const toolName = asString$30(fc.name) || "";
|
|
9065
9076
|
const toolId = asString$30(fc.id) || sanitizeToolCallId("", toolName);
|
|
9066
|
-
const argsText = JSON.stringify(isRecord$
|
|
9077
|
+
const argsText = JSON.stringify(isRecord$67(fc.args) ? fc.args : {});
|
|
9067
9078
|
const index = state.blocks.length;
|
|
9068
9079
|
const block = {
|
|
9069
9080
|
type: "tool-call",
|
|
@@ -9102,7 +9113,7 @@ function processStreamLine$4(line, state) {
|
|
|
9102
9113
|
if (responseData !== chunk) collectUsage(responseData.usageMetadata, state);
|
|
9103
9114
|
const finishMessage = asString$30(candidate?.finishMessage);
|
|
9104
9115
|
if (finishMessage) state.finishMessage = finishMessage;
|
|
9105
|
-
const blockReason = asString$30((isRecord$
|
|
9116
|
+
const blockReason = asString$30((isRecord$67(responseData.promptFeedback) ? responseData.promptFeedback : void 0)?.blockReason);
|
|
9106
9117
|
const finishReason = asString$30(candidate?.finishReason) || asString$30(responseData.finishReason) || (blockReason ? `PROMPT_BLOCKED:${blockReason}` : void 0);
|
|
9107
9118
|
if (finishReason) {
|
|
9108
9119
|
state.finishReason = finishReason;
|
|
@@ -9175,7 +9186,7 @@ function closeStream$7(state) {
|
|
|
9175
9186
|
*
|
|
9176
9187
|
* @module dsh-chatgpt-subscription/request-images
|
|
9177
9188
|
*/
|
|
9178
|
-
function isRecord$
|
|
9189
|
+
function isRecord$66(value) {
|
|
9179
9190
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
9180
9191
|
}
|
|
9181
9192
|
function asString$29(value) {
|
|
@@ -9234,17 +9245,17 @@ const SUPPORTED_IMAGE_MEDIA_TYPES$4 = /* @__PURE__ */ new Set([
|
|
|
9234
9245
|
]);
|
|
9235
9246
|
function attachmentOf$4(block) {
|
|
9236
9247
|
const attachment = block.attachment;
|
|
9237
|
-
if (!isRecord$
|
|
9248
|
+
if (!isRecord$66(attachment)) return void 0;
|
|
9238
9249
|
return typeof attachment.attachmentId === "string" ? attachment : void 0;
|
|
9239
9250
|
}
|
|
9240
9251
|
function attachmentLabel$5(block) {
|
|
9241
|
-
const attachment = isRecord$
|
|
9252
|
+
const attachment = isRecord$66(block.attachment) ? block.attachment : void 0;
|
|
9242
9253
|
return asString$29(attachment?.name) || asString$29(attachment?.attachmentId);
|
|
9243
9254
|
}
|
|
9244
9255
|
function collectImageRefs$2(content, refs) {
|
|
9245
9256
|
if (!Array.isArray(content)) return;
|
|
9246
9257
|
for (const block of content) {
|
|
9247
|
-
if (!isRecord$
|
|
9258
|
+
if (!isRecord$66(block)) continue;
|
|
9248
9259
|
if (block.type === "image") {
|
|
9249
9260
|
const attachment = attachmentOf$4(block);
|
|
9250
9261
|
if (attachment) refs.set(attachment.attachmentId, attachment);
|
|
@@ -9274,7 +9285,7 @@ function base64Length$2(bytes) {
|
|
|
9274
9285
|
*/
|
|
9275
9286
|
function inlinePayloadOf$1(block) {
|
|
9276
9287
|
let data = asString$29(block.data) || asString$29(block.base64);
|
|
9277
|
-
const source = isRecord$
|
|
9288
|
+
const source = isRecord$66(block.source) ? block.source : void 0;
|
|
9278
9289
|
if (!data && source) data = asString$29(source.data) || asString$29(source.base64);
|
|
9279
9290
|
if (data?.startsWith("data:")) {
|
|
9280
9291
|
const matched = data.match(/^data:([^;,]+);base64,(.*)$/s);
|
|
@@ -9304,7 +9315,7 @@ function requestImagePayloadLength(block, images) {
|
|
|
9304
9315
|
function collectRequestImagePayloads(content, images, lengths) {
|
|
9305
9316
|
if (!Array.isArray(content)) return;
|
|
9306
9317
|
for (const block of content) {
|
|
9307
|
-
if (!isRecord$
|
|
9318
|
+
if (!isRecord$66(block)) continue;
|
|
9308
9319
|
if (block.type === "image") {
|
|
9309
9320
|
const bytes = requestImagePayloadLength(block, images);
|
|
9310
9321
|
if (bytes !== void 0) lengths.push(bytes);
|
|
@@ -9358,7 +9369,7 @@ function replaceOldestRequestImages(options, count, images) {
|
|
|
9358
9369
|
let replaced = false;
|
|
9359
9370
|
return {
|
|
9360
9371
|
content: content.map((block) => {
|
|
9361
|
-
if (remaining.count === 0 || !isRecord$
|
|
9372
|
+
if (remaining.count === 0 || !isRecord$66(block)) return block;
|
|
9362
9373
|
if (block.type === "image") {
|
|
9363
9374
|
if (requestImagePayloadLength(block, images) === void 0) return block;
|
|
9364
9375
|
remaining.count -= 1;
|
|
@@ -9500,7 +9511,7 @@ function collectImageOccurrences(options, images) {
|
|
|
9500
9511
|
const walk = (content) => {
|
|
9501
9512
|
if (!Array.isArray(content)) return;
|
|
9502
9513
|
for (const block of content) {
|
|
9503
|
-
if (!isRecord$
|
|
9514
|
+
if (!isRecord$66(block)) continue;
|
|
9504
9515
|
if (block.type === "image") {
|
|
9505
9516
|
const bytes = requestImagePayloadLength(block, images);
|
|
9506
9517
|
const key = requestImageKey(block, images);
|
|
@@ -9554,7 +9565,7 @@ function replaceDroppedRequestImages(options, droppedKeys, images) {
|
|
|
9554
9565
|
let replaced = false;
|
|
9555
9566
|
return {
|
|
9556
9567
|
content: content.map((block) => {
|
|
9557
|
-
if (!isRecord$
|
|
9568
|
+
if (!isRecord$66(block)) return block;
|
|
9558
9569
|
if (block.type === "image") {
|
|
9559
9570
|
const key = requestImageKey(block, images);
|
|
9560
9571
|
if (key === void 0 || !droppedKeys.has(key)) return block;
|
|
@@ -9739,7 +9750,7 @@ function unavailableImageText$5(block) {
|
|
|
9739
9750
|
}
|
|
9740
9751
|
function imageBlockToInline$3(block, images) {
|
|
9741
9752
|
let data = asString$29(block.data) || asString$29(block.base64);
|
|
9742
|
-
const source = isRecord$
|
|
9753
|
+
const source = isRecord$66(block.source) ? block.source : void 0;
|
|
9743
9754
|
if (!data && source) data = asString$29(source.data) || asString$29(source.base64);
|
|
9744
9755
|
let mediaType = asString$29(block.mimeType) || asString$29(block.mediaType) || (source ? asString$29(source.mimeType) || asString$29(source.mediaType) : void 0) || "image/png";
|
|
9745
9756
|
if (data?.startsWith("data:")) {
|
|
@@ -9761,6 +9772,549 @@ function imageBlockToInline$3(block, images) {
|
|
|
9761
9772
|
} : void 0;
|
|
9762
9773
|
}
|
|
9763
9774
|
//#endregion
|
|
9775
|
+
//#region src/host/common/prompt-cache-policy.ts
|
|
9776
|
+
const MINUTE_MS = 6e4;
|
|
9777
|
+
const MAX_CACHE_TTL_MS = 720 * 60 * MINUTE_MS;
|
|
9778
|
+
const DISABLED = Object.freeze({
|
|
9779
|
+
mode: "none",
|
|
9780
|
+
scope: "unknown",
|
|
9781
|
+
staticPrefix: false,
|
|
9782
|
+
coldStart: "off",
|
|
9783
|
+
stickyImages: false,
|
|
9784
|
+
affinity: false,
|
|
9785
|
+
waitWarmup: false,
|
|
9786
|
+
hint: false
|
|
9787
|
+
});
|
|
9788
|
+
function policy(input) {
|
|
9789
|
+
return Object.freeze(input);
|
|
9790
|
+
}
|
|
9791
|
+
/** Built-in facts already represented by a line's mapper, adapter, or official client. */
|
|
9792
|
+
const PROMPT_CACHE_POLICIES = Object.freeze({
|
|
9793
|
+
"kimi-code": policy({
|
|
9794
|
+
mode: "automatic",
|
|
9795
|
+
scope: "account",
|
|
9796
|
+
ttlMs: 25 * MINUTE_MS,
|
|
9797
|
+
staticPrefix: true,
|
|
9798
|
+
coldStart: "warm",
|
|
9799
|
+
stickyImages: true,
|
|
9800
|
+
affinity: true,
|
|
9801
|
+
waitWarmup: true,
|
|
9802
|
+
hint: true
|
|
9803
|
+
}),
|
|
9804
|
+
"codex-chatgpt": policy({
|
|
9805
|
+
mode: "automatic",
|
|
9806
|
+
scope: "unknown",
|
|
9807
|
+
staticPrefix: true,
|
|
9808
|
+
coldStart: "in-flight",
|
|
9809
|
+
stickyImages: false,
|
|
9810
|
+
affinity: false,
|
|
9811
|
+
waitWarmup: false,
|
|
9812
|
+
hint: false
|
|
9813
|
+
}),
|
|
9814
|
+
antigravity: policy({
|
|
9815
|
+
mode: "automatic",
|
|
9816
|
+
scope: "unknown",
|
|
9817
|
+
staticPrefix: true,
|
|
9818
|
+
coldStart: "in-flight",
|
|
9819
|
+
stickyImages: false,
|
|
9820
|
+
affinity: false,
|
|
9821
|
+
waitWarmup: false,
|
|
9822
|
+
hint: false
|
|
9823
|
+
}),
|
|
9824
|
+
"step-code": policy({
|
|
9825
|
+
mode: "automatic",
|
|
9826
|
+
scope: "unknown",
|
|
9827
|
+
ttlMs: 5 * MINUTE_MS,
|
|
9828
|
+
staticPrefix: true,
|
|
9829
|
+
coldStart: "in-flight",
|
|
9830
|
+
stickyImages: false,
|
|
9831
|
+
affinity: false,
|
|
9832
|
+
waitWarmup: false,
|
|
9833
|
+
hint: true
|
|
9834
|
+
}),
|
|
9835
|
+
claude: policy({
|
|
9836
|
+
mode: "explicit",
|
|
9837
|
+
scope: "unknown",
|
|
9838
|
+
staticPrefix: true,
|
|
9839
|
+
coldStart: "in-flight",
|
|
9840
|
+
stickyImages: true,
|
|
9841
|
+
affinity: false,
|
|
9842
|
+
waitWarmup: false,
|
|
9843
|
+
hint: false
|
|
9844
|
+
}),
|
|
9845
|
+
"minimax-code": policy({
|
|
9846
|
+
mode: "explicit",
|
|
9847
|
+
scope: "unknown",
|
|
9848
|
+
staticPrefix: true,
|
|
9849
|
+
coldStart: "in-flight",
|
|
9850
|
+
stickyImages: true,
|
|
9851
|
+
affinity: false,
|
|
9852
|
+
waitWarmup: false,
|
|
9853
|
+
hint: false
|
|
9854
|
+
}),
|
|
9855
|
+
"glm-coding-plan": policy({
|
|
9856
|
+
...DISABLED,
|
|
9857
|
+
mode: "automatic"
|
|
9858
|
+
}),
|
|
9859
|
+
"command-code": policy({
|
|
9860
|
+
...DISABLED,
|
|
9861
|
+
mode: "automatic"
|
|
9862
|
+
}),
|
|
9863
|
+
"cline-pass": policy({
|
|
9864
|
+
...DISABLED,
|
|
9865
|
+
mode: "automatic"
|
|
9866
|
+
}),
|
|
9867
|
+
qoder: policy({
|
|
9868
|
+
...DISABLED,
|
|
9869
|
+
mode: "automatic"
|
|
9870
|
+
}),
|
|
9871
|
+
opendesign: policy({
|
|
9872
|
+
...DISABLED,
|
|
9873
|
+
mode: "automatic"
|
|
9874
|
+
}),
|
|
9875
|
+
"workbuddy-subscription": policy({
|
|
9876
|
+
...DISABLED,
|
|
9877
|
+
mode: "automatic"
|
|
9878
|
+
}),
|
|
9879
|
+
ollama: DISABLED
|
|
9880
|
+
});
|
|
9881
|
+
const OVERRIDE_KEYS = /* @__PURE__ */ new Set([
|
|
9882
|
+
"mode",
|
|
9883
|
+
"scope",
|
|
9884
|
+
"ttlMs",
|
|
9885
|
+
"staticPrefix",
|
|
9886
|
+
"coldStart",
|
|
9887
|
+
"stickyImages",
|
|
9888
|
+
"affinity",
|
|
9889
|
+
"waitWarmup",
|
|
9890
|
+
"hint"
|
|
9891
|
+
]);
|
|
9892
|
+
const BOOLEAN_KEYS = [
|
|
9893
|
+
"staticPrefix",
|
|
9894
|
+
"stickyImages",
|
|
9895
|
+
"affinity",
|
|
9896
|
+
"waitWarmup",
|
|
9897
|
+
"hint"
|
|
9898
|
+
];
|
|
9899
|
+
function isRecord$65(value) {
|
|
9900
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
9901
|
+
}
|
|
9902
|
+
/** Validate one provider override without silently dropping unknown keys. */
|
|
9903
|
+
function parsePromptCacheOverride(provider, value) {
|
|
9904
|
+
if (!isRecord$65(value)) throw new Error(`prompt cache policy for "${provider}" must be an object`);
|
|
9905
|
+
const out = {};
|
|
9906
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
9907
|
+
if (!OVERRIDE_KEYS.has(key)) throw new Error(`prompt cache policy for "${provider}" has unknown field "${key}"`);
|
|
9908
|
+
if (entry === void 0) continue;
|
|
9909
|
+
if (key === "mode") {
|
|
9910
|
+
if (entry !== "automatic" && entry !== "explicit" && entry !== "none") throw new Error(`invalid prompt cache mode for "${provider}"`);
|
|
9911
|
+
} else if (key === "scope") {
|
|
9912
|
+
if (entry !== "account" && entry !== "shared" && entry !== "unknown") throw new Error(`invalid prompt cache scope for "${provider}"`);
|
|
9913
|
+
} else if (key === "coldStart") {
|
|
9914
|
+
if (entry !== "off" && entry !== "in-flight" && entry !== "warm") throw new Error(`invalid cold-start mode for "${provider}"`);
|
|
9915
|
+
} else if (key === "ttlMs") {
|
|
9916
|
+
if (!Number.isSafeInteger(entry) || entry <= 0 || entry > MAX_CACHE_TTL_MS) throw new Error(`prompt cache ttlMs for "${provider}" must be a positive safe integer no greater than ${MAX_CACHE_TTL_MS}`);
|
|
9917
|
+
} else if (BOOLEAN_KEYS.includes(key) && typeof entry !== "boolean") throw new Error(`prompt cache ${key} for "${provider}" must be boolean`);
|
|
9918
|
+
out[key] = entry;
|
|
9919
|
+
}
|
|
9920
|
+
return out;
|
|
9921
|
+
}
|
|
9922
|
+
/** Validate a whole config-owned map. Undefined means no overrides. */
|
|
9923
|
+
function parsePromptCacheOverrides(value) {
|
|
9924
|
+
if (value === void 0) return {};
|
|
9925
|
+
if (!isRecord$65(value)) throw new Error("promptCacheOverrides must be an object keyed by provider id");
|
|
9926
|
+
const out = {};
|
|
9927
|
+
for (const [provider, override] of Object.entries(value)) {
|
|
9928
|
+
if (!/^[a-z0-9][a-z0-9-]*$/.test(provider)) throw new Error(`invalid prompt cache provider id "${provider}"`);
|
|
9929
|
+
out[provider] = parsePromptCacheOverride(provider, override);
|
|
9930
|
+
}
|
|
9931
|
+
return Object.freeze(out);
|
|
9932
|
+
}
|
|
9933
|
+
function validateResolved(provider, value) {
|
|
9934
|
+
if (value.mode === "none") return policy({ ...DISABLED });
|
|
9935
|
+
if ((value.coldStart === "warm" || value.waitWarmup || value.hint) && value.ttlMs === void 0) throw new Error(`prompt cache policy for "${provider}" needs a measured ttlMs for warm/waitWarmup/hint`);
|
|
9936
|
+
if (value.affinity && value.scope !== "account") throw new Error(`prompt cache affinity for "${provider}" needs scope:"account"`);
|
|
9937
|
+
return policy(value);
|
|
9938
|
+
}
|
|
9939
|
+
/** Resolve one line, applying an already-validated or config-shaped override. */
|
|
9940
|
+
function promptCachePolicyOf(provider, override) {
|
|
9941
|
+
const base = PROMPT_CACHE_POLICIES[provider] ?? DISABLED;
|
|
9942
|
+
const parsed = override === void 0 ? void 0 : parsePromptCacheOverride(provider, override);
|
|
9943
|
+
return validateResolved(provider, {
|
|
9944
|
+
...base,
|
|
9945
|
+
...parsed
|
|
9946
|
+
});
|
|
9947
|
+
}
|
|
9948
|
+
//#endregion
|
|
9949
|
+
//#region src/host/common/prefix-request-coordinator.ts
|
|
9950
|
+
const NOOP_LEASE = Object.freeze({
|
|
9951
|
+
leader: false,
|
|
9952
|
+
release: () => void 0,
|
|
9953
|
+
markWarm: () => void 0
|
|
9954
|
+
});
|
|
9955
|
+
var PrefixRequestCoordinator = class {
|
|
9956
|
+
leaders = /* @__PURE__ */ new Map();
|
|
9957
|
+
bursts = /* @__PURE__ */ new Map();
|
|
9958
|
+
mainHeaders = /* @__PURE__ */ new Map();
|
|
9959
|
+
warm = /* @__PURE__ */ new Map();
|
|
9960
|
+
waitMs;
|
|
9961
|
+
maxGroups;
|
|
9962
|
+
now;
|
|
9963
|
+
constructor(options = {}) {
|
|
9964
|
+
this.waitMs = options.waitMs ?? 2500;
|
|
9965
|
+
this.maxGroups = options.maxGroups ?? 256;
|
|
9966
|
+
this.now = options.now ?? Date.now;
|
|
9967
|
+
}
|
|
9968
|
+
/** Join or lead one cold prefix group. */
|
|
9969
|
+
async acquire(groupKey, signal, warmTtlMs) {
|
|
9970
|
+
if (groupKey === void 0 || groupKey === "") return NOOP_LEASE;
|
|
9971
|
+
if (warmTtlMs !== void 0 && this.isWarm(groupKey, warmTtlMs)) return NOOP_LEASE;
|
|
9972
|
+
const existing = this.leaders.get(groupKey);
|
|
9973
|
+
if (existing !== void 0 && this.now() - existing.startedAt < this.waitMs) {
|
|
9974
|
+
await this.race(existing.promise, signal);
|
|
9975
|
+
return NOOP_LEASE;
|
|
9976
|
+
}
|
|
9977
|
+
if (existing !== void 0) existing.release();
|
|
9978
|
+
return this.open(this.leaders, groupKey, warmTtlMs);
|
|
9979
|
+
}
|
|
9980
|
+
/** Serialize first-turn bursts on one account/model without a warm cache claim. */
|
|
9981
|
+
async acquireBurst(key, signal) {
|
|
9982
|
+
if (key === "") return NOOP_LEASE;
|
|
9983
|
+
while (true) {
|
|
9984
|
+
const existing = this.bursts.get(key);
|
|
9985
|
+
if (existing === void 0) break;
|
|
9986
|
+
await this.race(existing.promise, signal);
|
|
9987
|
+
if (signal.aborted) return NOOP_LEASE;
|
|
9988
|
+
if (this.bursts.get(key) === existing) existing.release();
|
|
9989
|
+
}
|
|
9990
|
+
return this.open(this.bursts, key);
|
|
9991
|
+
}
|
|
9992
|
+
/** A main request owns the prefill slot until its response headers arrive. */
|
|
9993
|
+
registerMainSession(sessionId) {
|
|
9994
|
+
if (sessionId.trim() === "") return NOOP_LEASE;
|
|
9995
|
+
const key = sessionId.trim();
|
|
9996
|
+
this.mainHeaders.get(key)?.release();
|
|
9997
|
+
return this.open(this.mainHeaders, key);
|
|
9998
|
+
}
|
|
9999
|
+
/** An auxiliary title request waits briefly for the main request's headers. */
|
|
10000
|
+
async waitForMainSession(sessionId, signal) {
|
|
10001
|
+
const existing = this.mainHeaders.get(sessionId.trim());
|
|
10002
|
+
if (existing !== void 0) await this.race(existing.promise, signal);
|
|
10003
|
+
}
|
|
10004
|
+
isWarm(groupKey, ttlMs, at = this.now()) {
|
|
10005
|
+
const warmedAt = this.warm.get(groupKey);
|
|
10006
|
+
return warmedAt !== void 0 && Number.isFinite(ttlMs) && ttlMs > 0 && at - warmedAt < ttlMs;
|
|
10007
|
+
}
|
|
10008
|
+
/** Release every waiter and clear all process-local evidence. */
|
|
10009
|
+
reset() {
|
|
10010
|
+
for (const store of [
|
|
10011
|
+
this.leaders,
|
|
10012
|
+
this.bursts,
|
|
10013
|
+
this.mainHeaders
|
|
10014
|
+
]) {
|
|
10015
|
+
for (const entry of [...store.values()]) entry.release();
|
|
10016
|
+
store.clear();
|
|
10017
|
+
}
|
|
10018
|
+
this.warm.clear();
|
|
10019
|
+
}
|
|
10020
|
+
open(store, key, warmTtlMs) {
|
|
10021
|
+
let resolve;
|
|
10022
|
+
const promise = new Promise((done) => {
|
|
10023
|
+
resolve = done;
|
|
10024
|
+
});
|
|
10025
|
+
let settled = false;
|
|
10026
|
+
const release = () => {
|
|
10027
|
+
if (settled) return;
|
|
10028
|
+
settled = true;
|
|
10029
|
+
resolve();
|
|
10030
|
+
if (store.get(key) === entry) store.delete(key);
|
|
10031
|
+
};
|
|
10032
|
+
const entry = {
|
|
10033
|
+
promise,
|
|
10034
|
+
startedAt: this.now(),
|
|
10035
|
+
release
|
|
10036
|
+
};
|
|
10037
|
+
store.set(key, entry);
|
|
10038
|
+
return {
|
|
10039
|
+
leader: true,
|
|
10040
|
+
release,
|
|
10041
|
+
markWarm: () => {
|
|
10042
|
+
if (warmTtlMs === void 0 || !Number.isFinite(warmTtlMs) || warmTtlMs <= 0) return;
|
|
10043
|
+
this.warm.delete(key);
|
|
10044
|
+
this.warm.set(key, this.now());
|
|
10045
|
+
while (this.warm.size > this.maxGroups) {
|
|
10046
|
+
const oldest = this.warm.keys().next();
|
|
10047
|
+
if (oldest.done !== true) this.warm.delete(oldest.value);
|
|
10048
|
+
}
|
|
10049
|
+
}
|
|
10050
|
+
};
|
|
10051
|
+
}
|
|
10052
|
+
/** Wait for a leader, the bounded budget, or caller cancellation; clean every listener. */
|
|
10053
|
+
async race(leader, signal) {
|
|
10054
|
+
const cleanups = [];
|
|
10055
|
+
const budget = new Promise((resolve) => {
|
|
10056
|
+
const timer = setTimeout(resolve, this.waitMs);
|
|
10057
|
+
timer.unref?.();
|
|
10058
|
+
cleanups.push(() => clearTimeout(timer));
|
|
10059
|
+
});
|
|
10060
|
+
const aborted = new Promise((resolve) => {
|
|
10061
|
+
if (signal.aborted) {
|
|
10062
|
+
resolve();
|
|
10063
|
+
return;
|
|
10064
|
+
}
|
|
10065
|
+
const onAbort = () => resolve();
|
|
10066
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
10067
|
+
cleanups.push(() => signal.removeEventListener("abort", onAbort));
|
|
10068
|
+
});
|
|
10069
|
+
await Promise.race([
|
|
10070
|
+
leader,
|
|
10071
|
+
budget,
|
|
10072
|
+
aborted
|
|
10073
|
+
]).finally(() => {
|
|
10074
|
+
for (const cleanup of cleanups) cleanup();
|
|
10075
|
+
});
|
|
10076
|
+
}
|
|
10077
|
+
};
|
|
10078
|
+
/** Cheap structural gate for auxiliary calls; provider-specific legacy probes may extend it. */
|
|
10079
|
+
function isAuxiliaryRequest(options) {
|
|
10080
|
+
return options.purpose !== void 0;
|
|
10081
|
+
}
|
|
10082
|
+
/** Coordinate one fetch through response headers, releasing on every outcome. */
|
|
10083
|
+
async function fetchWithPrefixCoordination(fetchFn, coordinator, groupKey, warmTtlMs, input, init) {
|
|
10084
|
+
const signal = init?.signal;
|
|
10085
|
+
if (signal == null) {
|
|
10086
|
+
const controller = new AbortController();
|
|
10087
|
+
const lease = await coordinator.acquire(groupKey, controller.signal, warmTtlMs);
|
|
10088
|
+
try {
|
|
10089
|
+
const response = await fetchFn(input, init);
|
|
10090
|
+
if (response.ok) lease.markWarm();
|
|
10091
|
+
return response;
|
|
10092
|
+
} finally {
|
|
10093
|
+
lease.release();
|
|
10094
|
+
}
|
|
10095
|
+
}
|
|
10096
|
+
const lease = await coordinator.acquire(groupKey, signal, warmTtlMs);
|
|
10097
|
+
try {
|
|
10098
|
+
const response = await fetchFn(input, init);
|
|
10099
|
+
if (response.ok) lease.markWarm();
|
|
10100
|
+
return response;
|
|
10101
|
+
} finally {
|
|
10102
|
+
lease.release();
|
|
10103
|
+
}
|
|
10104
|
+
}
|
|
10105
|
+
//#endregion
|
|
10106
|
+
//#region src/host/common/prefix-stability.ts
|
|
10107
|
+
function isRecord$64(value) {
|
|
10108
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
10109
|
+
}
|
|
10110
|
+
function firstTextOf$1(content) {
|
|
10111
|
+
if (typeof content === "string") return content.trim() === "" ? void 0 : content;
|
|
10112
|
+
if (!Array.isArray(content)) return void 0;
|
|
10113
|
+
for (const block of content) {
|
|
10114
|
+
if (!isRecord$64(block)) continue;
|
|
10115
|
+
if (block.type === "text" && typeof block.text === "string" && block.text !== "") return block.text;
|
|
10116
|
+
if (block.type === "tool-result") {
|
|
10117
|
+
const nested = firstTextOf$1(block.content);
|
|
10118
|
+
if (nested !== void 0) return nested;
|
|
10119
|
+
}
|
|
10120
|
+
}
|
|
10121
|
+
}
|
|
10122
|
+
function isToolResultMessage$9(message) {
|
|
10123
|
+
return message?.source?.kind === "tool";
|
|
10124
|
+
}
|
|
10125
|
+
/** Stability tier for an initial bootstrap reminder; null means dynamic/history-bound. */
|
|
10126
|
+
function classifyBootstrapUserMessage$1(message) {
|
|
10127
|
+
if (message.role !== "user" || isToolResultMessage$9(message)) return null;
|
|
10128
|
+
const source = isRecord$64(message.source) ? message.source : void 0;
|
|
10129
|
+
const kind = source !== void 0 && typeof source.kind === "string" ? source.kind : void 0;
|
|
10130
|
+
const text = firstTextOf$1(message.content)?.trimStart() ?? "";
|
|
10131
|
+
if (kind === "progress-updates-language" || text.startsWith("Progress update output language override:")) return 1;
|
|
10132
|
+
if (text.startsWith("<system-reminder>\nThe following workspace instructions") || kind === "agent-instructions" && !text.startsWith("<system-reminder>\nUpdated instructions") && !text.startsWith("<system-reminder>\nAdditional instructions") && !text.startsWith("<system-reminder>\nInstructions removed")) return 2;
|
|
10133
|
+
if (text.startsWith("<system-reminder>\nA skill is a reusable set of task-specific instructions.") || kind === "skill-catalog" && !text.startsWith("<system-reminder>\nThe available skill catalog changed.")) return 3;
|
|
10134
|
+
if (kind === "runtime-context" || text.startsWith("Current runtime context. This snapshot supersedes")) return 4;
|
|
10135
|
+
return null;
|
|
10136
|
+
}
|
|
10137
|
+
function isCompactCheckpointMessage(message) {
|
|
10138
|
+
if (message === void 0 || message.role !== "user" || isToolResultMessage$9(message)) return false;
|
|
10139
|
+
const source = isRecord$64(message.source) ? message.source : void 0;
|
|
10140
|
+
if (source !== void 0 && source.kind === "compact-checkpoint") return true;
|
|
10141
|
+
return firstTextOf$1(message.content)?.trimStart().startsWith("This is an automatically generated checkpoint condensing") === true;
|
|
10142
|
+
}
|
|
10143
|
+
/** Global instructions precede workspace instructions without reordering equals. */
|
|
10144
|
+
function normalizeAgentInstructionsMessage(message) {
|
|
10145
|
+
if (!Array.isArray(message.content) || message.content.length !== 1) return message;
|
|
10146
|
+
const first = message.content[0];
|
|
10147
|
+
if (!isRecord$64(first) || first.type !== "text" || typeof first.text !== "string") return message;
|
|
10148
|
+
const raw = first.text;
|
|
10149
|
+
const firstIdx = raw.indexOf("\n\nInstructions from: ");
|
|
10150
|
+
if (firstIdx < 0) return message;
|
|
10151
|
+
const closeIdx = raw.lastIndexOf("\n</system-reminder>");
|
|
10152
|
+
if (closeIdx <= firstIdx) return message;
|
|
10153
|
+
const header = raw.slice(0, firstIdx);
|
|
10154
|
+
const body = raw.slice(firstIdx + 2, closeIdx);
|
|
10155
|
+
const tail = raw.slice(closeIdx);
|
|
10156
|
+
const sections = body.split("\n\nInstructions from: ");
|
|
10157
|
+
if (sections.length < 2) return message;
|
|
10158
|
+
const tagged = sections.map((sec, idx) => {
|
|
10159
|
+
const firstLineEnd = sec.indexOf("\n");
|
|
10160
|
+
const sourcePath = (firstLineEnd < 0 ? sec : sec.slice(0, firstLineEnd)).trim();
|
|
10161
|
+
return {
|
|
10162
|
+
sec,
|
|
10163
|
+
idx,
|
|
10164
|
+
rank: sourcePath.startsWith("~/") || sourcePath.startsWith("~\\") || sourcePath.includes("/.dsh/") || sourcePath.includes("\\.dsh\\") ? 0 : 1
|
|
10165
|
+
};
|
|
10166
|
+
});
|
|
10167
|
+
if (!tagged.some((item, i) => i > 0 && item.rank < tagged[i - 1].rank)) return message;
|
|
10168
|
+
tagged.sort((a, b) => a.rank - b.rank || a.idx - b.idx);
|
|
10169
|
+
const rebuilt = `${header}\n\nInstructions from: ${tagged.map((item) => item.sec).join("\n\nInstructions from: ")}${tail}`;
|
|
10170
|
+
return {
|
|
10171
|
+
...message,
|
|
10172
|
+
content: [{
|
|
10173
|
+
...first,
|
|
10174
|
+
text: rebuilt
|
|
10175
|
+
}]
|
|
10176
|
+
};
|
|
10177
|
+
}
|
|
10178
|
+
/** Normalise the initial bootstrap run, including the post-compaction shape. */
|
|
10179
|
+
function orderStaticPrefixMessages$1(messages) {
|
|
10180
|
+
try {
|
|
10181
|
+
if (messages.length <= 1) return [...messages];
|
|
10182
|
+
let leadEnd = 0;
|
|
10183
|
+
while (leadEnd < messages.length && messages[leadEnd].role === "user" && !isToolResultMessage$9(messages[leadEnd])) leadEnd += 1;
|
|
10184
|
+
if (leadEnd > 0 && isCompactCheckpointMessage(messages[0])) {
|
|
10185
|
+
const seenInLead = /* @__PURE__ */ new Set();
|
|
10186
|
+
for (let i = 0; i < leadEnd; i++) {
|
|
10187
|
+
const tier = classifyBootstrapUserMessage$1(messages[i]);
|
|
10188
|
+
if (tier !== null) seenInLead.add(tier);
|
|
10189
|
+
}
|
|
10190
|
+
if (!seenInLead.has(1) && !seenInLead.has(2)) {
|
|
10191
|
+
const hoistedByTier = /* @__PURE__ */ new Map();
|
|
10192
|
+
for (let i = leadEnd; i < messages.length; i++) {
|
|
10193
|
+
const msg = messages[i];
|
|
10194
|
+
const tier = classifyBootstrapUserMessage$1(msg);
|
|
10195
|
+
if ((tier === 1 || tier === 2 || tier === 3) && !hoistedByTier.has(tier)) hoistedByTier.set(tier, {
|
|
10196
|
+
index: i,
|
|
10197
|
+
message: tier === 2 ? normalizeAgentInstructionsMessage(msg) : msg
|
|
10198
|
+
});
|
|
10199
|
+
}
|
|
10200
|
+
if (hoistedByTier.size > 0) {
|
|
10201
|
+
const indices = /* @__PURE__ */ new Set();
|
|
10202
|
+
const prefix = [];
|
|
10203
|
+
for (const tier of [
|
|
10204
|
+
1,
|
|
10205
|
+
2,
|
|
10206
|
+
3
|
|
10207
|
+
]) {
|
|
10208
|
+
const entry = hoistedByTier.get(tier);
|
|
10209
|
+
if (entry !== void 0) {
|
|
10210
|
+
indices.add(entry.index);
|
|
10211
|
+
prefix.push(entry.message);
|
|
10212
|
+
}
|
|
10213
|
+
}
|
|
10214
|
+
return [...prefix, ...messages.filter((_, index) => !indices.has(index))];
|
|
10215
|
+
}
|
|
10216
|
+
}
|
|
10217
|
+
}
|
|
10218
|
+
if (leadEnd <= 1) return [...messages];
|
|
10219
|
+
const seen = /* @__PURE__ */ new Set();
|
|
10220
|
+
const tagged = [];
|
|
10221
|
+
let hasBootstrap = false;
|
|
10222
|
+
for (let i = 0; i < leadEnd; i++) {
|
|
10223
|
+
const raw = messages[i];
|
|
10224
|
+
const tier = classifyBootstrapUserMessage$1(raw);
|
|
10225
|
+
if (tier !== null && !seen.has(tier)) {
|
|
10226
|
+
seen.add(tier);
|
|
10227
|
+
hasBootstrap = true;
|
|
10228
|
+
tagged.push({
|
|
10229
|
+
message: tier === 2 ? normalizeAgentInstructionsMessage(raw) : raw,
|
|
10230
|
+
rank: tier,
|
|
10231
|
+
index: i
|
|
10232
|
+
});
|
|
10233
|
+
} else tagged.push({
|
|
10234
|
+
message: raw,
|
|
10235
|
+
rank: 5,
|
|
10236
|
+
index: i
|
|
10237
|
+
});
|
|
10238
|
+
}
|
|
10239
|
+
if (!hasBootstrap) return [...messages];
|
|
10240
|
+
if (!tagged.some((item, i) => i > 0 && item.rank < tagged[i - 1].rank || item.message !== messages[i])) return [...messages];
|
|
10241
|
+
tagged.sort((a, b) => a.rank - b.rank || a.index - b.index);
|
|
10242
|
+
return [...tagged.map((item) => item.message), ...messages.slice(leadEnd)];
|
|
10243
|
+
} catch {
|
|
10244
|
+
return [...messages];
|
|
10245
|
+
}
|
|
10246
|
+
}
|
|
10247
|
+
/** Apply the policy at an adapter boundary; disabled providers keep object identity. */
|
|
10248
|
+
function withStaticPrefixOrdering(provider, options, override) {
|
|
10249
|
+
if (!promptCachePolicyOf(provider, override).staticPrefix) return options;
|
|
10250
|
+
const messages = orderStaticPrefixMessages$1(options.messages);
|
|
10251
|
+
return messages.some((message, index) => message !== options.messages[index]) ? {
|
|
10252
|
+
...options,
|
|
10253
|
+
messages
|
|
10254
|
+
} : options;
|
|
10255
|
+
}
|
|
10256
|
+
function allTextOf(content) {
|
|
10257
|
+
if (typeof content === "string") return content;
|
|
10258
|
+
if (!Array.isArray(content)) return "";
|
|
10259
|
+
return content.map((block) => {
|
|
10260
|
+
if (!isRecord$64(block)) return "";
|
|
10261
|
+
if (block.type === "text" && typeof block.text === "string") return block.text;
|
|
10262
|
+
if (block.type === "tool-result") return allTextOf(block.content);
|
|
10263
|
+
return "";
|
|
10264
|
+
}).join("");
|
|
10265
|
+
}
|
|
10266
|
+
function prefixFingerprint(text) {
|
|
10267
|
+
let hash = 2166136261;
|
|
10268
|
+
for (let index = 0; index < text.length; index++) {
|
|
10269
|
+
hash ^= text.charCodeAt(index);
|
|
10270
|
+
hash = Math.imul(hash, 16777619);
|
|
10271
|
+
}
|
|
10272
|
+
return (hash >>> 0).toString(16);
|
|
10273
|
+
}
|
|
10274
|
+
/** Provider-neutral identity for requests that may share one cold prefix. */
|
|
10275
|
+
function sharedPrefixGroupKey(provider, options, namespace, accountScope) {
|
|
10276
|
+
if (isAuxiliaryRequest(options)) return void 0;
|
|
10277
|
+
const systemParts = [];
|
|
10278
|
+
if (typeof options.system === "string" && options.system.trim() !== "") systemParts.push(options.system);
|
|
10279
|
+
for (const message of options.messages) if (message.role === "system") {
|
|
10280
|
+
const text = allTextOf(message.content);
|
|
10281
|
+
if (text !== "") systemParts.push(text);
|
|
10282
|
+
}
|
|
10283
|
+
if (systemParts.length === 0) return void 0;
|
|
10284
|
+
const systemHash = prefixFingerprint(systemParts.join("\n\n"));
|
|
10285
|
+
const tools = options.tools ?? [];
|
|
10286
|
+
const toolsHash = tools.length === 0 ? "none" : prefixFingerprint(JSON.stringify(tools.map((tool) => [
|
|
10287
|
+
tool.name,
|
|
10288
|
+
tool.description ?? "",
|
|
10289
|
+
tool.parameters ?? {}
|
|
10290
|
+
])));
|
|
10291
|
+
const staticParts = [];
|
|
10292
|
+
for (const message of orderStaticPrefixMessages$1(options.messages)) {
|
|
10293
|
+
const tier = classifyBootstrapUserMessage$1(message);
|
|
10294
|
+
if (tier !== 1 && tier !== 2 && tier !== 3) break;
|
|
10295
|
+
staticParts.push(allTextOf(message.content));
|
|
10296
|
+
}
|
|
10297
|
+
const staticHash = staticParts.length === 0 ? "none" : prefixFingerprint(staticParts.join("\n\n"));
|
|
10298
|
+
return [
|
|
10299
|
+
provider,
|
|
10300
|
+
namespace,
|
|
10301
|
+
options.model,
|
|
10302
|
+
systemHash,
|
|
10303
|
+
toolsHash,
|
|
10304
|
+
staticHash,
|
|
10305
|
+
accountScope
|
|
10306
|
+
].join(":");
|
|
10307
|
+
}
|
|
10308
|
+
/** Pure prefix-diff policy shared by provider-specific telemetry stores. */
|
|
10309
|
+
function classifyPrefixDrift(previous, current) {
|
|
10310
|
+
if (previous === void 0 || previous.systemPromptHash === null && previous.toolsHash === null && previous.cacheKey === null) return "first-request";
|
|
10311
|
+
if (current.cacheKey === null) return "cold-key";
|
|
10312
|
+
if (previous.cacheKey !== current.cacheKey) return "cache-key";
|
|
10313
|
+
if (previous.systemPromptHash !== current.systemPromptHash) return "system-prompt";
|
|
10314
|
+
if (previous.toolsHash !== current.toolsHash) return "tools";
|
|
10315
|
+
return "stable";
|
|
10316
|
+
}
|
|
10317
|
+
//#endregion
|
|
9764
10318
|
//#region src/host/antigravity/cache-stats.ts
|
|
9765
10319
|
const EMPTY_TOTALS = {
|
|
9766
10320
|
requests: 0,
|
|
@@ -10492,6 +11046,7 @@ function streamFailure$2(error, signal) {
|
|
|
10492
11046
|
const chain = errorChain(error).trim();
|
|
10493
11047
|
return new LlmError(`Antigravity stream failed: ${chain === "" ? "the response stream ended unexpectedly" : chain.slice(0, 500)}`, "TRANSPORT", { cause: error });
|
|
10494
11048
|
}
|
|
11049
|
+
const antigravityPrefixRequests = new PrefixRequestCoordinator();
|
|
10495
11050
|
var AntigravityAdapter = class extends LlmAdapter {
|
|
10496
11051
|
store;
|
|
10497
11052
|
modelSettings;
|
|
@@ -10614,7 +11169,7 @@ var AntigravityAdapter = class extends LlmAdapter {
|
|
|
10614
11169
|
const runtimeModel = routing?.routing[effort] || routing?.defaultRequestId || model.id;
|
|
10615
11170
|
const family = quotaFamilyForModel(model.id);
|
|
10616
11171
|
const runtimeMaxOutput = catalogMaxOutputFor(runtimeModel, catalog);
|
|
10617
|
-
const normalizedOptions = normalizeGenerateOptions(options);
|
|
11172
|
+
const normalizedOptions = withStaticPrefixOrdering("antigravity", normalizeGenerateOptions(options), this.options.promptCache);
|
|
10618
11173
|
const images = await resolveRequestImages$3(normalizedOptions, this.options.attachments, signal);
|
|
10619
11174
|
const requestOptions = offloadOldestRequestImages$2(normalizedOptions, MAX_REQUEST_IMAGE_BYTES$3, images);
|
|
10620
11175
|
let recoveredMissingThinkingSignature = false;
|
|
@@ -10656,7 +11211,9 @@ var AntigravityAdapter = class extends LlmAdapter {
|
|
|
10656
11211
|
let firstResponse;
|
|
10657
11212
|
for (const endpoint of endpointCandidates()) try {
|
|
10658
11213
|
let sentBody = body;
|
|
10659
|
-
|
|
11214
|
+
const cachePolicy = promptCachePolicyOf("antigravity", this.options.promptCache);
|
|
11215
|
+
const groupKey = cachePolicy.coldStart === "off" ? void 0 : sharedPrefixGroupKey("antigravity", requestOptions, endpoint, account.id);
|
|
11216
|
+
response = await fetchWithPrefixCoordination(fetchFn, antigravityPrefixRequests, groupKey, cachePolicy.coldStart === "warm" ? cachePolicy.ttlMs : void 0, `${endpoint}/v1internal:streamGenerateContent?alt=sse`, {
|
|
10660
11217
|
method: "POST",
|
|
10661
11218
|
headers,
|
|
10662
11219
|
body,
|
|
@@ -12173,6 +12730,135 @@ function createControlledModelFetch(baseFetch, options) {
|
|
|
12173
12730
|
};
|
|
12174
12731
|
}
|
|
12175
12732
|
//#endregion
|
|
12733
|
+
//#region src/host/common/tool-runaway.ts
|
|
12734
|
+
/**
|
|
12735
|
+
* Provider-neutral tool-call repetition primitives.
|
|
12736
|
+
*
|
|
12737
|
+
* The detector knows nothing about model wire formats. Providers supply reminder
|
|
12738
|
+
* text and may opt into a hard handoff threshold; coordination tools are exempt
|
|
12739
|
+
* because they are expected to recur while real work continues.
|
|
12740
|
+
*/
|
|
12741
|
+
function isRecord$63(value) {
|
|
12742
|
+
return typeof value === "object" && value !== null;
|
|
12743
|
+
}
|
|
12744
|
+
function isPlainRecord$2(value) {
|
|
12745
|
+
if (!isRecord$63(value) || Array.isArray(value)) return false;
|
|
12746
|
+
const prototype = Object.getPrototypeOf(value);
|
|
12747
|
+
return prototype === Object.prototype || prototype === null;
|
|
12748
|
+
}
|
|
12749
|
+
function sortJsonValue(value) {
|
|
12750
|
+
if (Array.isArray(value)) return value.map(sortJsonValue);
|
|
12751
|
+
if (!isPlainRecord$2(value)) return value;
|
|
12752
|
+
const out = {};
|
|
12753
|
+
for (const key of Object.keys(value).sort()) out[key] = sortJsonValue(value[key]);
|
|
12754
|
+
return out;
|
|
12755
|
+
}
|
|
12756
|
+
/** Stable identity for one tool call, independent of object key order. */
|
|
12757
|
+
function canonicalToolCallKey(toolName, args) {
|
|
12758
|
+
let json;
|
|
12759
|
+
try {
|
|
12760
|
+
json = JSON.stringify(sortJsonValue(args));
|
|
12761
|
+
} catch {
|
|
12762
|
+
json = void 0;
|
|
12763
|
+
}
|
|
12764
|
+
return toolName + " " + (json ?? String(args));
|
|
12765
|
+
}
|
|
12766
|
+
/** One immediately-repeated action streak. A different action starts at one. */
|
|
12767
|
+
function createExactRepeatTracker() {
|
|
12768
|
+
let current;
|
|
12769
|
+
return {
|
|
12770
|
+
observe(toolName, args) {
|
|
12771
|
+
const key = canonicalToolCallKey(toolName, args);
|
|
12772
|
+
current = {
|
|
12773
|
+
key,
|
|
12774
|
+
count: current?.key === key ? current.count + 1 : 1
|
|
12775
|
+
};
|
|
12776
|
+
return current;
|
|
12777
|
+
},
|
|
12778
|
+
current() {
|
|
12779
|
+
return current;
|
|
12780
|
+
},
|
|
12781
|
+
reset() {
|
|
12782
|
+
current = void 0;
|
|
12783
|
+
}
|
|
12784
|
+
};
|
|
12785
|
+
}
|
|
12786
|
+
/** Coordination calls that must not break or inflate an action streak. */
|
|
12787
|
+
const TOOL_RUNAWAY_EXEMPT_TOOLS = Object.freeze([
|
|
12788
|
+
"progress_update",
|
|
12789
|
+
"todo_write",
|
|
12790
|
+
"ask_user_question",
|
|
12791
|
+
"enter_plan_mode",
|
|
12792
|
+
"exit_plan_mode",
|
|
12793
|
+
"leave_plan_mode",
|
|
12794
|
+
"create_goal",
|
|
12795
|
+
"update_goal",
|
|
12796
|
+
"get_goal",
|
|
12797
|
+
"dispatch_ledger",
|
|
12798
|
+
"present"
|
|
12799
|
+
]);
|
|
12800
|
+
/** Read the live global switch; legacy Kimi-only installs without it stay on. */
|
|
12801
|
+
function resolveToolRunawayGuardEnabled(config) {
|
|
12802
|
+
const field = config?.toolRunawayGuard;
|
|
12803
|
+
if (field !== null && typeof field === "object") try {
|
|
12804
|
+
return field.get() !== false;
|
|
12805
|
+
} catch {
|
|
12806
|
+
return true;
|
|
12807
|
+
}
|
|
12808
|
+
return field !== false;
|
|
12809
|
+
}
|
|
12810
|
+
/**
|
|
12811
|
+
* Exact-repeat state machine shared by provider profiles. A missing forceStopAt
|
|
12812
|
+
* gives reminders only; no provider inherits Kimi's hard stop accidentally.
|
|
12813
|
+
*/
|
|
12814
|
+
function createExactRepeatBreaker(options) {
|
|
12815
|
+
const sessions = /* @__PURE__ */ new Map();
|
|
12816
|
+
const maxSessions = options.maxSessions ?? 256;
|
|
12817
|
+
const exempt = new Set(options.exemptTools ?? TOOL_RUNAWAY_EXEMPT_TOOLS);
|
|
12818
|
+
const stateOf = (sessionId) => {
|
|
12819
|
+
let state = sessions.get(sessionId);
|
|
12820
|
+
if (state === void 0) state = {
|
|
12821
|
+
tracker: createExactRepeatTracker(),
|
|
12822
|
+
count: 0,
|
|
12823
|
+
handoffActive: false
|
|
12824
|
+
};
|
|
12825
|
+
sessions.delete(sessionId);
|
|
12826
|
+
sessions.set(sessionId, state);
|
|
12827
|
+
if (sessions.size > maxSessions) {
|
|
12828
|
+
const oldest = sessions.keys().next();
|
|
12829
|
+
if (oldest.done !== true) sessions.delete(oldest.value);
|
|
12830
|
+
}
|
|
12831
|
+
return state;
|
|
12832
|
+
};
|
|
12833
|
+
return {
|
|
12834
|
+
checkBeforeExecute(sessionId, toolName, args) {
|
|
12835
|
+
if (options.forceStopAt === void 0) return void 0;
|
|
12836
|
+
const state = stateOf(sessionId);
|
|
12837
|
+
if (state.handoffActive) return options.handoffVeto ?? options.handoffReminder;
|
|
12838
|
+
const key = canonicalToolCallKey(toolName, args);
|
|
12839
|
+
if (state.tracker.current()?.key === key && state.count + 1 >= options.forceStopAt) {
|
|
12840
|
+
state.count += 1;
|
|
12841
|
+
state.handoffActive = true;
|
|
12842
|
+
return options.handoffVeto ?? options.handoffReminder;
|
|
12843
|
+
}
|
|
12844
|
+
},
|
|
12845
|
+
recordAfterExecute(sessionId, toolName, args) {
|
|
12846
|
+
if (exempt.has(toolName)) return void 0;
|
|
12847
|
+
const state = stateOf(sessionId);
|
|
12848
|
+
if (state.handoffActive) return options.handoffReminder ?? options.reminder(state.count);
|
|
12849
|
+
state.count = state.tracker.observe(toolName, args).count;
|
|
12850
|
+
if (options.forceStopAt !== void 0 && state.count >= options.forceStopAt) {
|
|
12851
|
+
state.handoffActive = true;
|
|
12852
|
+
return options.handoffReminder ?? options.reminder(state.count);
|
|
12853
|
+
}
|
|
12854
|
+
return options.reminder(state.count);
|
|
12855
|
+
},
|
|
12856
|
+
resetSession(sessionId) {
|
|
12857
|
+
sessions.delete(sessionId);
|
|
12858
|
+
}
|
|
12859
|
+
};
|
|
12860
|
+
}
|
|
12861
|
+
//#endregion
|
|
12176
12862
|
//#region src/host/common/provider-route.ts
|
|
12177
12863
|
/**
|
|
12178
12864
|
* Claim one provider route for this plugin and keep claiming it.
|
|
@@ -12728,7 +13414,7 @@ function preferencesPath() {
|
|
|
12728
13414
|
return path.join(dshHomeDir(), "storages", "dsh-chatgpt-subscription-preferences.json");
|
|
12729
13415
|
}
|
|
12730
13416
|
/** Distinguishes concurrent temporary files written by one process. */
|
|
12731
|
-
let writeSequence
|
|
13417
|
+
let writeSequence = 0;
|
|
12732
13418
|
/** One JSON document, validated and shaped by a schemastery schema. */
|
|
12733
13419
|
var FilePreferencesStore = class {
|
|
12734
13420
|
schema;
|
|
@@ -12830,8 +13516,8 @@ var FilePreferencesStore = class {
|
|
|
12830
13516
|
}
|
|
12831
13517
|
async write(value) {
|
|
12832
13518
|
await fs.mkdir(path.dirname(this.filePath), { recursive: true });
|
|
12833
|
-
writeSequence
|
|
12834
|
-
const tmp = `${this.filePath}.tmp.${process.pid}.${writeSequence
|
|
13519
|
+
writeSequence += 1;
|
|
13520
|
+
const tmp = `${this.filePath}.tmp.${process.pid}.${writeSequence}`;
|
|
12835
13521
|
try {
|
|
12836
13522
|
await fs.writeFile(tmp, JSON.stringify(value, null, 2), {
|
|
12837
13523
|
encoding: "utf8",
|
|
@@ -14049,9 +14735,17 @@ const REASONING_DELTA_FLUSH_CHARS = 768;
|
|
|
14049
14735
|
const REASONING_TRUNCATED_NOTICE = "\n\n[Reasoning summary truncated to keep the DSH web UI responsive.]";
|
|
14050
14736
|
/** Cap on live turns whose routing state is remembered at once. */
|
|
14051
14737
|
const MAX_TRACKED_TURN_STATES = 200;
|
|
14738
|
+
const codexPrefixRequests = new PrefixRequestCoordinator();
|
|
14739
|
+
function codexColdGroupKey(payload, credentials) {
|
|
14740
|
+
const instructions = typeof payload.instructions === "string" ? payload.instructions : "";
|
|
14741
|
+
if (instructions === "") return void 0;
|
|
14742
|
+
const digest = createHash("sha256").update(instructions).update("\0").update(JSON.stringify(Array.isArray(payload.tools) ? payload.tools : [])).digest("hex");
|
|
14743
|
+
return `${String(payload.model ?? "")}:${digest}:${authOwnerKey(credentials)}`;
|
|
14744
|
+
}
|
|
14052
14745
|
var ResponsesClient = class {
|
|
14053
14746
|
oauth;
|
|
14054
14747
|
attachments;
|
|
14748
|
+
options;
|
|
14055
14749
|
fetchFn;
|
|
14056
14750
|
onGenerationFinished;
|
|
14057
14751
|
outputVerbosity;
|
|
@@ -14080,6 +14774,7 @@ var ResponsesClient = class {
|
|
|
14080
14774
|
constructor(oauth, attachments, options = {}) {
|
|
14081
14775
|
this.oauth = oauth;
|
|
14082
14776
|
this.attachments = attachments;
|
|
14777
|
+
this.options = options;
|
|
14083
14778
|
this.fetchFn = options.fetchFn ?? fetch;
|
|
14084
14779
|
this.localRawImages = options.localRawImages ?? {};
|
|
14085
14780
|
this.onGenerationFinished = options.onGenerationFinished ?? (() => void 0);
|
|
@@ -14092,7 +14787,7 @@ var ResponsesClient = class {
|
|
|
14092
14787
|
}
|
|
14093
14788
|
localRawImages;
|
|
14094
14789
|
async *stream(rawOptions, capabilities) {
|
|
14095
|
-
const options = normalizeGenerateOptions(rawOptions);
|
|
14790
|
+
const options = withStaticPrefixOrdering("codex-chatgpt", normalizeGenerateOptions(rawOptions), this.options.promptCache);
|
|
14096
14791
|
const sessionId = stableSessionId(options.sessionId);
|
|
14097
14792
|
const resolveCapabilities = capabilities ?? this.capabilities ?? defaultWireCapabilities;
|
|
14098
14793
|
const turnKey = turnKeyFor(sessionId);
|
|
@@ -14275,7 +14970,9 @@ var ResponsesClient = class {
|
|
|
14275
14970
|
}
|
|
14276
14971
|
async request(payload, credentials, sessionId, turnKey, signal, useResponsesLite = false) {
|
|
14277
14972
|
for (let attempt = 0;; attempt += 1) try {
|
|
14278
|
-
|
|
14973
|
+
const cachePolicy = promptCachePolicyOf("codex-chatgpt", this.options.promptCache);
|
|
14974
|
+
const groupKey = cachePolicy.coldStart === "off" ? void 0 : codexColdGroupKey(payload, credentials);
|
|
14975
|
+
return await fetchWithPrefixCoordination(this.fetchFn, codexPrefixRequests, groupKey, cachePolicy.coldStart === "warm" ? cachePolicy.ttlMs : void 0, CODEX_RESPONSES_URL, {
|
|
14279
14976
|
method: "POST",
|
|
14280
14977
|
headers: {
|
|
14281
14978
|
...codexHeaders(credentials, sessionId, { turnState: this.turnStateFor(turnKey, credentials) }),
|
|
@@ -28497,6 +29194,10 @@ var FileResultReferenceStore = class {
|
|
|
28497
29194
|
*/
|
|
28498
29195
|
/** 有界登记表:Map 的插入顺序同时是 LRU 顺序(命中即重新插入到队尾)。 */
|
|
28499
29196
|
const registry = /* @__PURE__ */ new Map();
|
|
29197
|
+
const DEFAULT_USAGE_PROVIDER = "kimi-code";
|
|
29198
|
+
function usageScopeKey(sessionId, provider) {
|
|
29199
|
+
return (provider?.trim() || DEFAULT_USAGE_PROVIDER) + ":" + sessionId;
|
|
29200
|
+
}
|
|
28500
29201
|
/** 一个中性返回值,异常路径复用(不含任何容器操作)。 */
|
|
28501
29202
|
function neutralState(sessionId) {
|
|
28502
29203
|
return {
|
|
@@ -28601,6 +29302,7 @@ function normalizeUsage(input) {
|
|
|
28601
29302
|
const at = typeof input.at === "number" && Number.isFinite(input.at) ? input.at : Date.now();
|
|
28602
29303
|
const normalized = {
|
|
28603
29304
|
sessionId,
|
|
29305
|
+
provider: typeof input.provider === "string" && input.provider.trim() !== "" ? input.provider.trim() : DEFAULT_USAGE_PROVIDER,
|
|
28604
29306
|
inputTokens: finiteCount(input.inputTokens),
|
|
28605
29307
|
cacheReadTokens: finiteCount(input.cacheReadTokens),
|
|
28606
29308
|
outputTokens: finiteCount(input.outputTokens),
|
|
@@ -28631,16 +29333,19 @@ function stateOf(sessionId, reports) {
|
|
|
28631
29333
|
*/
|
|
28632
29334
|
function recordCacheWarmupUsage(input) {
|
|
28633
29335
|
let sessionId = "";
|
|
29336
|
+
let provider;
|
|
28634
29337
|
try {
|
|
28635
29338
|
const usage = normalizeUsage(input);
|
|
28636
29339
|
if (usage === void 0) return neutralState("");
|
|
28637
29340
|
sessionId = usage.sessionId;
|
|
28638
|
-
|
|
29341
|
+
provider = usage.provider;
|
|
29342
|
+
const scope = usageScopeKey(usage.sessionId, usage.provider);
|
|
29343
|
+
const previous = registry.get(scope);
|
|
28639
29344
|
const kept = previous === void 0 ? [] : [...previous];
|
|
28640
29345
|
kept.push(Object.freeze({ ...usage }));
|
|
28641
29346
|
while (kept.length > CACHE_WARMUP_DEFAULTS.maxUsageReportsPerSession) kept.shift();
|
|
28642
|
-
if (previous !== void 0) registry.delete(
|
|
28643
|
-
registry.set(
|
|
29347
|
+
if (previous !== void 0) registry.delete(scope);
|
|
29348
|
+
registry.set(scope, Object.freeze(kept));
|
|
28644
29349
|
while (registry.size > CACHE_WARMUP_DEFAULTS.maxUsageSessions) {
|
|
28645
29350
|
const oldest = registry.keys().next();
|
|
28646
29351
|
if (oldest.done === true) break;
|
|
@@ -28649,7 +29354,7 @@ function recordCacheWarmupUsage(input) {
|
|
|
28649
29354
|
return stateOf(usage.sessionId, kept);
|
|
28650
29355
|
} catch {
|
|
28651
29356
|
try {
|
|
28652
|
-
if (sessionId !== "") registry.delete(sessionId);
|
|
29357
|
+
if (sessionId !== "") registry.delete(usageScopeKey(sessionId, provider));
|
|
28653
29358
|
} catch {}
|
|
28654
29359
|
return neutralState(sessionId);
|
|
28655
29360
|
}
|
|
@@ -28659,10 +29364,10 @@ function recordCacheWarmupUsage(input) {
|
|
|
28659
29364
|
* @param sessionId - 会话 id。
|
|
28660
29365
|
* @returns 报告副本;没有或读不到时为空数组。
|
|
28661
29366
|
*/
|
|
28662
|
-
function cacheWarmupUsageReports(sessionId) {
|
|
29367
|
+
function cacheWarmupUsageReports(sessionId, provider) {
|
|
28663
29368
|
try {
|
|
28664
29369
|
if (typeof sessionId !== "string" || sessionId === "") return [];
|
|
28665
|
-
const reports = registry.get(sessionId);
|
|
29370
|
+
const reports = registry.get(usageScopeKey(sessionId, provider));
|
|
28666
29371
|
return reports === void 0 ? [] : [...reports];
|
|
28667
29372
|
} catch {
|
|
28668
29373
|
return [];
|
|
@@ -28675,11 +29380,12 @@ function cacheWarmupUsageReports(sessionId) {
|
|
|
28675
29380
|
function cacheWarmupUsageSessions() {
|
|
28676
29381
|
try {
|
|
28677
29382
|
const summaries = [];
|
|
28678
|
-
for (const
|
|
28679
|
-
|
|
29383
|
+
for (const reports of registry.values()) {
|
|
29384
|
+
const sample = reports[0];
|
|
29385
|
+
if (sample === void 0) continue;
|
|
28680
29386
|
const last = reports[reports.length - 1];
|
|
28681
29387
|
summaries.push({
|
|
28682
|
-
sessionId,
|
|
29388
|
+
sessionId: sample.sessionId,
|
|
28683
29389
|
reports: reports.length,
|
|
28684
29390
|
lastAt: last.at,
|
|
28685
29391
|
hitRatio: cacheWarmupHitRatio(last)
|
|
@@ -28726,10 +29432,10 @@ const WARMUP_BUDGET = Object.freeze({
|
|
|
28726
29432
|
* @param durable - 进程重启后尚无内存报告时的持久化回退摘要(可选)。
|
|
28727
29433
|
* @returns 本次投影可用的预算。
|
|
28728
29434
|
*/
|
|
28729
|
-
function cacheWarmupBudget(sessionId, now = Date.now(), durable) {
|
|
29435
|
+
function cacheWarmupBudget(sessionId, now = Date.now(), durable, provider) {
|
|
28730
29436
|
try {
|
|
28731
29437
|
if (typeof sessionId !== "string" || sessionId.trim() === "") return { ...WARMUP_BUDGET };
|
|
28732
|
-
const reports = cacheWarmupUsageReports(sessionId);
|
|
29438
|
+
const reports = cacheWarmupUsageReports(sessionId, provider);
|
|
28733
29439
|
if (reports.length === 0) {
|
|
28734
29440
|
if (durable !== void 0 && durable !== null && typeof durable === "object") {
|
|
28735
29441
|
const lastAt = durable.lastActiveAt;
|
|
@@ -28765,7 +29471,7 @@ function cacheWarmupBudget(sessionId, now = Date.now(), durable) {
|
|
|
28765
29471
|
* @param now - 当前时间戳(毫秒),默认 `Date.now()`。
|
|
28766
29472
|
* @returns 快照。
|
|
28767
29473
|
*/
|
|
28768
|
-
function cacheWarmupSnapshot(sessionId, now = Date.now()) {
|
|
29474
|
+
function cacheWarmupSnapshot(sessionId, now = Date.now(), provider) {
|
|
28769
29475
|
const fallback = {
|
|
28770
29476
|
budget: { ...WARMUP_BUDGET },
|
|
28771
29477
|
defaults: CACHE_WARMUP_DEFAULTS,
|
|
@@ -28783,7 +29489,7 @@ function cacheWarmupSnapshot(sessionId, now = Date.now()) {
|
|
|
28783
29489
|
sessions: 0
|
|
28784
29490
|
};
|
|
28785
29491
|
try {
|
|
28786
|
-
const budget = cacheWarmupBudget(sessionId, now);
|
|
29492
|
+
const budget = cacheWarmupBudget(sessionId, now, void 0, provider);
|
|
28787
29493
|
const reports = typeof sessionId === "string" && sessionId.trim() !== "" ? cacheWarmupUsageReports(sessionId) : [];
|
|
28788
29494
|
const totals = {
|
|
28789
29495
|
inputTokens: 0,
|
|
@@ -29378,7 +30084,7 @@ function planRetrievalOutline(text, toolName) {
|
|
|
29378
30084
|
* @returns 读到的元数据;两者都没有时 undefined。
|
|
29379
30085
|
*/
|
|
29380
30086
|
function shellMetadataOf(holder) {
|
|
29381
|
-
const job = isPlainRecord$
|
|
30087
|
+
const job = isPlainRecord$1(holder.job) ? holder.job : void 0;
|
|
29382
30088
|
let exitCode;
|
|
29383
30089
|
if (typeof holder.exitCode === "number" && Number.isFinite(holder.exitCode)) exitCode = holder.exitCode;
|
|
29384
30090
|
if (exitCode === void 0 && typeof job?.exitCode === "number" && Number.isFinite(job.exitCode)) exitCode = job.exitCode;
|
|
@@ -29509,7 +30215,7 @@ const JSON_ENVELOPE_LOG_KEYS = [
|
|
|
29509
30215
|
/** JSON envelope 其余字段允许占用的最大序列化字符数(防止把含批量源码的混合 envelope 压掉)。 */
|
|
29510
30216
|
const JSON_ENVELOPE_MAX_OTHER_CHARS = 800;
|
|
29511
30217
|
/** 判断一个值是不是普通 JSON 对象(不是数组,也不是 null)。 */
|
|
29512
|
-
function isPlainRecord$
|
|
30218
|
+
function isPlainRecord$1(value) {
|
|
29513
30219
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
29514
30220
|
}
|
|
29515
30221
|
/** 重建信封:只把预览写回被替换的键,其余字段原样保留。 */
|
|
@@ -29611,7 +30317,7 @@ function extractJsonEnvelopeLogField(text, toolName) {
|
|
|
29611
30317
|
});
|
|
29612
30318
|
}
|
|
29613
30319
|
for (const [outerKey, outerValue] of Object.entries(record)) {
|
|
29614
|
-
if (!isPlainRecord$
|
|
30320
|
+
if (!isPlainRecord$1(outerValue)) continue;
|
|
29615
30321
|
for (const key of JSON_ENVELOPE_LOG_KEYS) {
|
|
29616
30322
|
const value = outerValue[key];
|
|
29617
30323
|
if (typeof value === "string" && value.length > 0) candidates.push({
|
|
@@ -29701,7 +30407,7 @@ function canonicalGrepMatches(value) {
|
|
|
29701
30407
|
if (!Array.isArray(value) || value.length === 0) return void 0;
|
|
29702
30408
|
const matches = [];
|
|
29703
30409
|
for (const entry of value) {
|
|
29704
|
-
if (!isPlainRecord$
|
|
30410
|
+
if (!isPlainRecord$1(entry)) return void 0;
|
|
29705
30411
|
if (typeof entry.path !== "string") return void 0;
|
|
29706
30412
|
if (!Number.isSafeInteger(entry.lineNumber)) return void 0;
|
|
29707
30413
|
if (typeof entry.line !== "string") return void 0;
|
|
@@ -29715,12 +30421,12 @@ function canonicalGrepMatches(value) {
|
|
|
29715
30421
|
}
|
|
29716
30422
|
/** 规范读取结果。 */
|
|
29717
30423
|
function canonicalReadResult(value) {
|
|
29718
|
-
if (!isPlainRecord$
|
|
30424
|
+
if (!isPlainRecord$1(value)) return void 0;
|
|
29719
30425
|
if (typeof value.path !== "string" || value.path.trim() === "") return void 0;
|
|
29720
30426
|
if (!Array.isArray(value.lines) || value.lines.length === 0) return void 0;
|
|
29721
30427
|
const lines = [];
|
|
29722
30428
|
for (const entry of value.lines) {
|
|
29723
|
-
if (!isPlainRecord$
|
|
30429
|
+
if (!isPlainRecord$1(entry)) return void 0;
|
|
29724
30430
|
if (!Number.isSafeInteger(entry.number)) return void 0;
|
|
29725
30431
|
if (typeof entry.text !== "string") return void 0;
|
|
29726
30432
|
lines.push({
|
|
@@ -29745,7 +30451,7 @@ function findGrepBinding(record) {
|
|
|
29745
30451
|
matches: top
|
|
29746
30452
|
};
|
|
29747
30453
|
for (const [outerKey, outerValue] of Object.entries(record)) {
|
|
29748
|
-
if (!isPlainRecord$
|
|
30454
|
+
if (!isPlainRecord$1(outerValue)) continue;
|
|
29749
30455
|
const nested = canonicalGrepMatches(outerValue.matches);
|
|
29750
30456
|
if (nested !== void 0) return {
|
|
29751
30457
|
binding: {
|
|
@@ -29772,7 +30478,7 @@ function findGlobBinding(record) {
|
|
|
29772
30478
|
...typeof record.root === "string" ? { root: record.root } : {}
|
|
29773
30479
|
};
|
|
29774
30480
|
for (const [outerKey, outerValue] of Object.entries(record)) {
|
|
29775
|
-
if (!isPlainRecord$
|
|
30481
|
+
if (!isPlainRecord$1(outerValue)) continue;
|
|
29776
30482
|
const nested = canonicalGlobPaths(outerValue.paths);
|
|
29777
30483
|
if (nested !== void 0) return {
|
|
29778
30484
|
binding: {
|
|
@@ -29950,13 +30656,13 @@ function collectCompositeEntries(record) {
|
|
|
29950
30656
|
for (const [key, value] of Object.entries(record)) {
|
|
29951
30657
|
if (Array.isArray(value)) {
|
|
29952
30658
|
value.forEach((item, index) => {
|
|
29953
|
-
if (isPlainRecord$
|
|
30659
|
+
if (isPlainRecord$1(item)) push(`${key}[${index}]`, item);
|
|
29954
30660
|
});
|
|
29955
30661
|
continue;
|
|
29956
30662
|
}
|
|
29957
|
-
if (!isPlainRecord$
|
|
30663
|
+
if (!isPlainRecord$1(value)) continue;
|
|
29958
30664
|
if (push(key, value)) continue;
|
|
29959
|
-
for (const [subKey, subValue] of Object.entries(value)) if (isPlainRecord$
|
|
30665
|
+
for (const [subKey, subValue] of Object.entries(value)) if (isPlainRecord$1(subValue)) push(`${key}.${subKey}`, subValue);
|
|
29960
30666
|
}
|
|
29961
30667
|
return entries;
|
|
29962
30668
|
}
|
|
@@ -30380,6 +31086,17 @@ async function projectToolResult(input) {
|
|
|
30380
31086
|
};
|
|
30381
31087
|
}
|
|
30382
31088
|
//#endregion
|
|
31089
|
+
//#region src/host/common/failure-classification.ts
|
|
31090
|
+
/** Clamp a measured reset delay without parking an account forever or reopening at once. */
|
|
31091
|
+
function cooldownFromReset(input) {
|
|
31092
|
+
const now = input.now ?? Date.now();
|
|
31093
|
+
const minimum = input.minimumMs ?? 0;
|
|
31094
|
+
const maximum = input.maximumMs ?? Number.MAX_SAFE_INTEGER;
|
|
31095
|
+
if (input.resetAtMs === void 0 || input.resetAtMs === null || !Number.isFinite(input.resetAtMs)) return Math.min(maximum, Math.max(minimum, input.fallbackMs));
|
|
31096
|
+
if (input.resetAtMs <= now) return Math.min(maximum, Math.max(minimum, input.fallbackMs));
|
|
31097
|
+
return Math.min(maximum, Math.max(minimum, input.resetAtMs - now));
|
|
31098
|
+
}
|
|
31099
|
+
//#endregion
|
|
30383
31100
|
//#region src/host/kimi-code/model-catalog.ts
|
|
30384
31101
|
/**
|
|
30385
31102
|
* Request-body ceiling the 1M-context tier defaults to.
|
|
@@ -31363,6 +32080,67 @@ async function persistRegion(region) {
|
|
|
31363
32080
|
await fs.writeFile(file, region, "utf8");
|
|
31364
32081
|
} catch {}
|
|
31365
32082
|
}
|
|
32083
|
+
//#endregion
|
|
32084
|
+
//#region src/host/common/cache-hint.ts
|
|
32085
|
+
/** Missing data always skips; a provider must supply its own measured TTL. */
|
|
32086
|
+
function evaluateCacheHint$1(input) {
|
|
32087
|
+
const { now, lastActiveAt, totalTokens } = input;
|
|
32088
|
+
if (lastActiveAt === void 0 || totalTokens === void 0) return { kind: "skip" };
|
|
32089
|
+
if (!Number.isFinite(lastActiveAt) || !Number.isFinite(totalTokens)) return { kind: "skip" };
|
|
32090
|
+
const ttlMs = input.ttlMs;
|
|
32091
|
+
const minTokens = input.minTokensToHint ?? 1e5;
|
|
32092
|
+
if (ttlMs === void 0 || !Number.isFinite(ttlMs) || ttlMs <= 0) return { kind: "skip" };
|
|
32093
|
+
if (!Number.isFinite(minTokens) || minTokens < 0 || totalTokens < minTokens) return { kind: "skip" };
|
|
32094
|
+
const idleMs = now - lastActiveAt;
|
|
32095
|
+
if (idleMs <= ttlMs) return { kind: "skip" };
|
|
32096
|
+
return {
|
|
32097
|
+
kind: "hint",
|
|
32098
|
+
idleMs,
|
|
32099
|
+
totalTokens
|
|
32100
|
+
};
|
|
32101
|
+
}
|
|
32102
|
+
//#endregion
|
|
32103
|
+
//#region src/host/kimi-code/cache-hint.ts
|
|
32104
|
+
/**
|
|
32105
|
+
* When it is worth telling the user their prompt cache has gone cold.
|
|
32106
|
+
*
|
|
32107
|
+
* Kimi's cache is automatic and content-hash based, so a client gets no marker
|
|
32108
|
+
* that it is no longer being reused — the only evidence is the read ratio in
|
|
32109
|
+
* the usage stream. A conversation that sat idle past the cache lifetime pays
|
|
32110
|
+
* the full prompt again on its next turn, and it is large enough on a real
|
|
32111
|
+
* codebase to be worth a sentence.
|
|
32112
|
+
*
|
|
32113
|
+
* The trigger mirrors the official client's: idle beyond the cache lifetime AND
|
|
32114
|
+
* a context large enough that reprocessing it actually costs something. The
|
|
32115
|
+
* upstream rules arrive from a remote config that a private deployment cannot
|
|
32116
|
+
* reach, so the lifetime here is the measured one; the decision function stays
|
|
32117
|
+
* a pure function of its inputs so swapping the source later changes only where
|
|
32118
|
+
* the number comes from.
|
|
32119
|
+
*/
|
|
32120
|
+
/**
|
|
32121
|
+
* How long an entry survives, in milliseconds.
|
|
32122
|
+
*
|
|
32123
|
+
* MEASURED, not documented. The coding endpoint publishes no cache lifetime and
|
|
32124
|
+
* its `5m`/`1h` write tiers cannot be selected from any official client, so the
|
|
32125
|
+
* only figure available is the survival curve: 30 minutes, derived from the
|
|
32126
|
+
* endpoint's own measured behaviour.
|
|
32127
|
+
*
|
|
32128
|
+
* The measurement (2026-10-02/03, three replicas per age, one fresh prefix per
|
|
32129
|
+
* age so the probe does not renew what it is timing) put Kimi's coding plan at
|
|
32130
|
+
* 3/3 warm through 25 minutes, 1/3 at 30 minutes, and 0/3 at 35 minutes — a
|
|
32131
|
+
* 25–35 minute bracket around a fuzzy edge.
|
|
32132
|
+
*
|
|
32133
|
+
* 25 minutes is the last age at which every replica was still warm, and that is
|
|
32134
|
+
* what this notice needs: it must arrive BEFORE the entry is surely gone. The
|
|
32135
|
+
* 30-minute point where most replicas had gone cold is too late — by then the
|
|
32136
|
+
* user has already paid for the reprocessed prefix, so a notice there describes
|
|
32137
|
+
* the bill instead of preventing it.
|
|
32138
|
+
*
|
|
32139
|
+
* The old value was 5 minutes, taken from the `5m` write tier's NAME. On this
|
|
32140
|
+
* endpoint the name is not the lifetime: that estimate warned from minute 6
|
|
32141
|
+
* onward while the entry was still warm for another 19.
|
|
32142
|
+
*/
|
|
32143
|
+
const CACHE_TTL_MS = 1500 * 1e3;
|
|
31366
32144
|
/**
|
|
31367
32145
|
* Last time each session did something, epoch ms.
|
|
31368
32146
|
*
|
|
@@ -31394,26 +32172,13 @@ function sessionLastActiveAt(sessionId) {
|
|
|
31394
32172
|
if (sessionId === void 0 || sessionId.trim() === "") return void 0;
|
|
31395
32173
|
return lastActiveBySession.get(sessionId.trim());
|
|
31396
32174
|
}
|
|
31397
|
-
/**
|
|
31398
|
-
* Whether one conversation should be warned about a cold cache.
|
|
31399
|
-
*
|
|
31400
|
-
* Every missing-data branch skips. That asymmetry is deliberate and matches
|
|
31401
|
-
* the official client: a false negative costs one turn nobody noticed, while a
|
|
31402
|
-
* false positive teaches the user the notice is noise and stops them reading
|
|
31403
|
-
* the one that mattered.
|
|
31404
|
-
*/
|
|
32175
|
+
/** Kimi supplies its measured defaults; other providers call the common pure function directly. */
|
|
31405
32176
|
function evaluateCacheHint(input) {
|
|
31406
|
-
|
|
31407
|
-
|
|
31408
|
-
|
|
31409
|
-
|
|
31410
|
-
|
|
31411
|
-
if (idleMs <= 15e5) return { kind: "skip" };
|
|
31412
|
-
return {
|
|
31413
|
-
kind: "hint",
|
|
31414
|
-
idleMs,
|
|
31415
|
-
totalTokens
|
|
31416
|
-
};
|
|
32177
|
+
return evaluateCacheHint$1({
|
|
32178
|
+
...input,
|
|
32179
|
+
ttlMs: input.ttlMs ?? 15e5,
|
|
32180
|
+
minTokensToHint: input.minTokensToHint ?? 1e5
|
|
32181
|
+
});
|
|
31417
32182
|
}
|
|
31418
32183
|
//#endregion
|
|
31419
32184
|
//#region src/host/kimi-code/oauth.ts
|
|
@@ -33414,6 +34179,48 @@ function estimatedInputTokens(options) {
|
|
|
33414
34179
|
return Math.ceil(characters / 4);
|
|
33415
34180
|
}
|
|
33416
34181
|
//#endregion
|
|
34182
|
+
//#region src/host/common/reasoning-dialect.ts
|
|
34183
|
+
/** Learn the reasoning field a concrete OpenAI-compatible endpoint speaks. */
|
|
34184
|
+
const KNOWN_REASONING_KEYS$1 = [
|
|
34185
|
+
"reasoning_content",
|
|
34186
|
+
"reasoning_details",
|
|
34187
|
+
"reasoning"
|
|
34188
|
+
];
|
|
34189
|
+
var ReasoningKeyDialect$1 = class {
|
|
34190
|
+
defaultKey;
|
|
34191
|
+
keys;
|
|
34192
|
+
detected;
|
|
34193
|
+
constructor(defaultKey = KNOWN_REASONING_KEYS$1[0], keys = KNOWN_REASONING_KEYS$1) {
|
|
34194
|
+
this.defaultKey = defaultKey;
|
|
34195
|
+
this.keys = keys;
|
|
34196
|
+
}
|
|
34197
|
+
/** Record an observed field and return its text. Empty strings still teach the key. */
|
|
34198
|
+
observe(source) {
|
|
34199
|
+
if (typeof source !== "object" || source === null) return void 0;
|
|
34200
|
+
const record = source;
|
|
34201
|
+
for (const key of this.keys) {
|
|
34202
|
+
const value = record[key];
|
|
34203
|
+
if (typeof value === "string") {
|
|
34204
|
+
this.learn(key);
|
|
34205
|
+
return value;
|
|
34206
|
+
}
|
|
34207
|
+
}
|
|
34208
|
+
}
|
|
34209
|
+
/** Record a previously parsed fragment without re-reading the wire object. */
|
|
34210
|
+
observeKey(key) {
|
|
34211
|
+
if (this.keys.includes(key)) this.learn(key);
|
|
34212
|
+
}
|
|
34213
|
+
learn(key) {
|
|
34214
|
+
this.detected = key;
|
|
34215
|
+
}
|
|
34216
|
+
outboundKey() {
|
|
34217
|
+
return this.detected ?? this.defaultKey;
|
|
34218
|
+
}
|
|
34219
|
+
reset() {
|
|
34220
|
+
this.detected = void 0;
|
|
34221
|
+
}
|
|
34222
|
+
};
|
|
34223
|
+
//#endregion
|
|
33417
34224
|
//#region src/host/common/video.ts
|
|
33418
34225
|
/**
|
|
33419
34226
|
* Video input, shared by every line in this plugin that accepts it.
|
|
@@ -33717,13 +34524,8 @@ function sanitizeText$7(text) {
|
|
|
33717
34524
|
* `reasoning_details`. The first entry doubles as the default for a request
|
|
33718
34525
|
* made before any response has been seen.
|
|
33719
34526
|
*/
|
|
33720
|
-
const KNOWN_REASONING_KEYS =
|
|
33721
|
-
|
|
33722
|
-
"reasoning_details",
|
|
33723
|
-
"reasoning"
|
|
33724
|
-
];
|
|
33725
|
-
/** Default outbound reasoning key, used until a response proves otherwise. */
|
|
33726
|
-
const DEFAULT_REASONING_KEY = KNOWN_REASONING_KEYS[0];
|
|
34527
|
+
const KNOWN_REASONING_KEYS = KNOWN_REASONING_KEYS$1;
|
|
34528
|
+
KNOWN_REASONING_KEYS[0];
|
|
33727
34529
|
/**
|
|
33728
34530
|
* One `reasoning_details` element, in an official or a compatibility shape.
|
|
33729
34531
|
*
|
|
@@ -33846,8 +34648,7 @@ function reasoningFragmentsOf(source) {
|
|
|
33846
34648
|
* turn. Detection never clears: a response with no reasoning keeps the last
|
|
33847
34649
|
* known dialect, and an endpoint that switches is adapted on its next reply.
|
|
33848
34650
|
*/
|
|
33849
|
-
var ReasoningKeyDialect = class {
|
|
33850
|
-
detected;
|
|
34651
|
+
var ReasoningKeyDialect = class extends ReasoningKeyDialect$1 {
|
|
33851
34652
|
/**
|
|
33852
34653
|
* Every reasoning fragment one inbound delta carries, remembering the dialect.
|
|
33853
34654
|
*
|
|
@@ -33867,13 +34668,13 @@ var ReasoningKeyDialect = class {
|
|
|
33867
34668
|
const fragments = reasoningFragmentsOf(source);
|
|
33868
34669
|
const teaching = fragments.find((fragment) => fragment.form === void 0 || fragment.form === "string");
|
|
33869
34670
|
if (teaching !== void 0) {
|
|
33870
|
-
this.
|
|
34671
|
+
this.learn(teaching.key);
|
|
33871
34672
|
return fragments;
|
|
33872
34673
|
}
|
|
33873
34674
|
if (typeof source === "object" && source !== null) {
|
|
33874
34675
|
const record = source;
|
|
33875
34676
|
for (const key of KNOWN_REASONING_KEYS) if (typeof record[key] === "string") {
|
|
33876
|
-
this.
|
|
34677
|
+
this.learn(key);
|
|
33877
34678
|
break;
|
|
33878
34679
|
}
|
|
33879
34680
|
}
|
|
@@ -33883,14 +34684,6 @@ var ReasoningKeyDialect = class {
|
|
|
33883
34684
|
observe(source) {
|
|
33884
34685
|
return this.observeAll(source)[0]?.text;
|
|
33885
34686
|
}
|
|
33886
|
-
/** The key to serialize thinking into on an outbound assistant message. */
|
|
33887
|
-
outboundKey() {
|
|
33888
|
-
return this.detected ?? DEFAULT_REASONING_KEY;
|
|
33889
|
-
}
|
|
33890
|
-
/** Forget the detected key, returning to the default. Test seam only. */
|
|
33891
|
-
reset() {
|
|
33892
|
-
this.detected = void 0;
|
|
33893
|
-
}
|
|
33894
34687
|
};
|
|
33895
34688
|
/**
|
|
33896
34689
|
* Process-wide reasoning dialect for this route.
|
|
@@ -34236,59 +35029,7 @@ function nonSystemMessages$6(options) {
|
|
|
34236
35029
|
* arrived (append-only in-history).
|
|
34237
35030
|
*/
|
|
34238
35031
|
function classifyBootstrapUserMessage(message) {
|
|
34239
|
-
|
|
34240
|
-
const source = isRecord$53(message.source) ? message.source : void 0;
|
|
34241
|
-
const kind = source !== void 0 ? asString$21(source.kind) : void 0;
|
|
34242
|
-
const text = firstTextOf(message.content)?.trimStart() ?? "";
|
|
34243
|
-
if (kind === "progress-updates-language" || text.startsWith("Progress update output language override:")) return 1;
|
|
34244
|
-
if (text.startsWith("<system-reminder>\nThe following workspace instructions") || kind === "agent-instructions" && !text.startsWith("<system-reminder>\nUpdated instructions") && !text.startsWith("<system-reminder>\nAdditional instructions") && !text.startsWith("<system-reminder>\nInstructions removed")) return 2;
|
|
34245
|
-
if (text.startsWith("<system-reminder>\nA skill is a reusable set of task-specific instructions.") || kind === "skill-catalog" && !text.startsWith("<system-reminder>\nThe available skill catalog changed.")) return 3;
|
|
34246
|
-
if (kind === "runtime-context" || text.startsWith("Current runtime context. This snapshot supersedes")) return 4;
|
|
34247
|
-
return null;
|
|
34248
|
-
}
|
|
34249
|
-
function isCompactCheckpointMessage(message) {
|
|
34250
|
-
if (message === void 0 || message.role !== "user" || isToolResultMessage$7(message)) return false;
|
|
34251
|
-
const source = isRecord$53(message.source) ? message.source : void 0;
|
|
34252
|
-
if (source !== void 0 && asString$21(source.kind) === "compact-checkpoint") return true;
|
|
34253
|
-
return (firstTextOf(message.content)?.trimStart() ?? "").startsWith("This is an automatically generated checkpoint condensing");
|
|
34254
|
-
}
|
|
34255
|
-
/**
|
|
34256
|
-
* Normalize `agent-instructions` text so global `~/.dsh/` instructions precede
|
|
34257
|
-
* workspace-local `AGENTS.md` instructions when both appear in the same block.
|
|
34258
|
-
*/
|
|
34259
|
-
function normalizeAgentInstructionsMessage(message) {
|
|
34260
|
-
if (!Array.isArray(message.content) || message.content.length !== 1) return message;
|
|
34261
|
-
const first = message.content[0];
|
|
34262
|
-
if (!isRecord$53(first) || first.type !== "text" || typeof first.text !== "string") return message;
|
|
34263
|
-
const raw = first.text;
|
|
34264
|
-
const firstIdx = raw.indexOf("\n\nInstructions from: ");
|
|
34265
|
-
if (firstIdx < 0) return message;
|
|
34266
|
-
const closeIdx = raw.lastIndexOf("\n</system-reminder>");
|
|
34267
|
-
if (closeIdx <= firstIdx) return message;
|
|
34268
|
-
const header = raw.slice(0, firstIdx);
|
|
34269
|
-
const body = raw.slice(firstIdx + 2, closeIdx);
|
|
34270
|
-
const tail = raw.slice(closeIdx);
|
|
34271
|
-
const sections = body.split("\n\nInstructions from: ");
|
|
34272
|
-
if (sections.length < 2) return message;
|
|
34273
|
-
const tagged = sections.map((sec, idx) => {
|
|
34274
|
-
const firstLineEnd = sec.indexOf("\n");
|
|
34275
|
-
const sourcePath = (firstLineEnd < 0 ? sec : sec.slice(0, firstLineEnd)).trim();
|
|
34276
|
-
return {
|
|
34277
|
-
sec,
|
|
34278
|
-
idx,
|
|
34279
|
-
rank: sourcePath.startsWith("~/") || sourcePath.startsWith("~\\") || sourcePath.includes("/.dsh/") || sourcePath.includes("\\.dsh\\") ? 0 : 1
|
|
34280
|
-
};
|
|
34281
|
-
});
|
|
34282
|
-
if (!tagged.some((item, i) => i > 0 && item.rank < tagged[i - 1].rank)) return message;
|
|
34283
|
-
tagged.sort((a, b) => a.rank - b.rank || a.idx - b.idx);
|
|
34284
|
-
const rebuilt = `${header}\n\nInstructions from: ${tagged.map((t) => t.sec).join("\n\nInstructions from: ")}${tail}`;
|
|
34285
|
-
return {
|
|
34286
|
-
...message,
|
|
34287
|
-
content: [{
|
|
34288
|
-
...first,
|
|
34289
|
-
text: rebuilt
|
|
34290
|
-
}]
|
|
34291
|
-
};
|
|
35032
|
+
return classifyBootstrapUserMessage$1(message);
|
|
34292
35033
|
}
|
|
34293
35034
|
/**
|
|
34294
35035
|
* Deterministic entropy-sorted ordering for leading bootstrap reminders.
|
|
@@ -34304,73 +35045,7 @@ function normalizeAgentInstructionsMessage(message) {
|
|
|
34304
35045
|
* compaction boundaries and every step after compaction.
|
|
34305
35046
|
*/
|
|
34306
35047
|
function orderStaticPrefixMessages(messages) {
|
|
34307
|
-
|
|
34308
|
-
if (messages.length <= 1) return [...messages];
|
|
34309
|
-
let leadEnd = 0;
|
|
34310
|
-
while (leadEnd < messages.length && messages[leadEnd].role === "user" && !isToolResultMessage$7(messages[leadEnd])) leadEnd += 1;
|
|
34311
|
-
if (leadEnd > 0 && isCompactCheckpointMessage(messages[0])) {
|
|
34312
|
-
const seenInLead = /* @__PURE__ */ new Set();
|
|
34313
|
-
for (let i = 0; i < leadEnd; i++) {
|
|
34314
|
-
const tier = classifyBootstrapUserMessage(messages[i]);
|
|
34315
|
-
if (tier !== null) seenInLead.add(tier);
|
|
34316
|
-
}
|
|
34317
|
-
if (!seenInLead.has(1) && !seenInLead.has(2)) {
|
|
34318
|
-
const hoistedByTier = /* @__PURE__ */ new Map();
|
|
34319
|
-
for (let i = leadEnd; i < messages.length; i++) {
|
|
34320
|
-
const msg = messages[i];
|
|
34321
|
-
const tier = classifyBootstrapUserMessage(msg);
|
|
34322
|
-
if ((tier === 1 || tier === 2 || tier === 3) && !hoistedByTier.has(tier)) hoistedByTier.set(tier, {
|
|
34323
|
-
index: i,
|
|
34324
|
-
message: tier === 2 ? normalizeAgentInstructionsMessage(msg) : msg
|
|
34325
|
-
});
|
|
34326
|
-
}
|
|
34327
|
-
if (hoistedByTier.size > 0) {
|
|
34328
|
-
const hoistedIndices = /* @__PURE__ */ new Set();
|
|
34329
|
-
const prefix = [];
|
|
34330
|
-
for (const tier of [
|
|
34331
|
-
1,
|
|
34332
|
-
2,
|
|
34333
|
-
3
|
|
34334
|
-
]) {
|
|
34335
|
-
const entry = hoistedByTier.get(tier);
|
|
34336
|
-
if (entry !== void 0) {
|
|
34337
|
-
hoistedIndices.add(entry.index);
|
|
34338
|
-
prefix.push(entry.message);
|
|
34339
|
-
}
|
|
34340
|
-
}
|
|
34341
|
-
const rest = messages.filter((_, idx) => !hoistedIndices.has(idx));
|
|
34342
|
-
return [...prefix, ...rest];
|
|
34343
|
-
}
|
|
34344
|
-
}
|
|
34345
|
-
}
|
|
34346
|
-
if (leadEnd <= 1) return [...messages];
|
|
34347
|
-
const seenTiers = /* @__PURE__ */ new Set();
|
|
34348
|
-
const leadTagged = [];
|
|
34349
|
-
let hasBootstrap = false;
|
|
34350
|
-
for (let i = 0; i < leadEnd; i++) {
|
|
34351
|
-
const rawMsg = messages[i];
|
|
34352
|
-
const tier = classifyBootstrapUserMessage(rawMsg);
|
|
34353
|
-
if (tier !== null && !seenTiers.has(tier)) {
|
|
34354
|
-
seenTiers.add(tier);
|
|
34355
|
-
hasBootstrap = true;
|
|
34356
|
-
leadTagged.push({
|
|
34357
|
-
message: tier === 2 ? normalizeAgentInstructionsMessage(rawMsg) : rawMsg,
|
|
34358
|
-
rank: tier,
|
|
34359
|
-
index: i
|
|
34360
|
-
});
|
|
34361
|
-
} else leadTagged.push({
|
|
34362
|
-
message: rawMsg,
|
|
34363
|
-
rank: 5,
|
|
34364
|
-
index: i
|
|
34365
|
-
});
|
|
34366
|
-
}
|
|
34367
|
-
if (!hasBootstrap) return [...messages];
|
|
34368
|
-
if (!leadTagged.some((item, i) => i > 0 && item.rank < leadTagged[i - 1].rank || item.message !== messages[i])) return [...messages];
|
|
34369
|
-
leadTagged.sort((a, b) => a.rank - b.rank || a.index - b.index);
|
|
34370
|
-
return [...leadTagged.map((item) => item.message), ...messages.slice(leadEnd)];
|
|
34371
|
-
} catch {
|
|
34372
|
-
return [...messages];
|
|
34373
|
-
}
|
|
35048
|
+
return orderStaticPrefixMessages$1(messages);
|
|
34374
35049
|
}
|
|
34375
35050
|
const NO_REASONING = {
|
|
34376
35051
|
fields: [],
|
|
@@ -35992,12 +36667,11 @@ function trackPrefixStability(options) {
|
|
|
35992
36667
|
toolsHash,
|
|
35993
36668
|
cacheKey
|
|
35994
36669
|
});
|
|
35995
|
-
|
|
35996
|
-
|
|
35997
|
-
|
|
35998
|
-
|
|
35999
|
-
|
|
36000
|
-
return "stable";
|
|
36670
|
+
return classifyPrefixDrift(previous, {
|
|
36671
|
+
systemPromptHash,
|
|
36672
|
+
toolsHash,
|
|
36673
|
+
cacheKey
|
|
36674
|
+
});
|
|
36001
36675
|
}
|
|
36002
36676
|
/** Drift cause recorded for each session. */
|
|
36003
36677
|
const driftCauses = /* @__PURE__ */ new Map();
|
|
@@ -36315,61 +36989,26 @@ function closeStream$4(state, accountId) {
|
|
|
36315
36989
|
function assertStreamComplete$5(state) {
|
|
36316
36990
|
if (!state.done && state.finishReason === null) throw new LlmError$1("Kimi Code stream ended before its terminal event", "TRANSPORT");
|
|
36317
36991
|
}
|
|
36318
|
-
|
|
36319
|
-
|
|
36320
|
-
return path.join(dshHomeDir(), "storages", "dsh-chatgpt-subscription-kimi-cache.json");
|
|
36321
|
-
}
|
|
36322
|
-
/** Distinguishes concurrent temporary files written by one process. */
|
|
36323
|
-
let writeSequence = 0;
|
|
36324
|
-
/**
|
|
36325
|
-
* Whether a scheduled write should reach the disk.
|
|
36326
|
-
*
|
|
36327
|
-
* A unit test that exercises the request path must not overwrite the state file
|
|
36328
|
-
* of the machine running it; tests build their own store against a temporary
|
|
36329
|
-
* path (which always writes) and call {@link SessionCacheStateStore.flush}.
|
|
36330
|
-
*/
|
|
36331
|
-
function persistenceEnabled() {
|
|
36332
|
-
return process.env.VITEST !== "true" && process.env.NODE_ENV !== "test";
|
|
36333
|
-
}
|
|
36992
|
+
//#endregion
|
|
36993
|
+
//#region src/host/common/session-cache-state.ts
|
|
36334
36994
|
/**
|
|
36335
|
-
*
|
|
36336
|
-
*
|
|
36337
|
-
* Reads are synchronous because every caller is answering a status request or
|
|
36338
|
-
* closing a turn, both of which are allowed to block on a file this size. Writes
|
|
36339
|
-
* are debounced: a turn reports once, and several turns in flight must not
|
|
36340
|
-
* rewrite the document several times.
|
|
36995
|
+
* Generic bounded, durable session state for cache diagnostics and sticky media.
|
|
36996
|
+
* One provider owns one document; records remain keyed by session inside it.
|
|
36341
36997
|
*/
|
|
36342
|
-
var
|
|
36998
|
+
var CommonSessionCacheStateStore = class CommonSessionCacheStateStore {
|
|
36343
36999
|
document;
|
|
36344
37000
|
pending;
|
|
36345
|
-
/**
|
|
36346
|
-
* @param filePath - Document location; defaults to {@link sessionCacheStatePath}.
|
|
36347
|
-
* @param maxEntries - Conversations kept; the oldest are dropped first.
|
|
36348
|
-
*/
|
|
36349
37001
|
filePath;
|
|
36350
37002
|
maxEntries;
|
|
36351
|
-
/**
|
|
36352
|
-
* Whether this store may write at all.
|
|
36353
|
-
*
|
|
36354
|
-
* A store built on an explicit path is someone's own file - a test's temporary
|
|
36355
|
-
* document - and always writes. The default-path store belongs to the user's
|
|
36356
|
-
* harness home, and a test that never named a path must not overwrite it.
|
|
36357
|
-
*/
|
|
36358
37003
|
allowWrite;
|
|
36359
|
-
|
|
36360
|
-
|
|
36361
|
-
|
|
36362
|
-
this.
|
|
37004
|
+
normalizeRecord;
|
|
37005
|
+
static writeSequence = 0;
|
|
37006
|
+
constructor(options) {
|
|
37007
|
+
this.filePath = options.filePath;
|
|
37008
|
+
this.maxEntries = options.maxEntries ?? 64;
|
|
37009
|
+
this.allowWrite = options.allowWrite ?? true;
|
|
37010
|
+
this.normalizeRecord = options.normalizeRecord;
|
|
36363
37011
|
}
|
|
36364
|
-
/**
|
|
36365
|
-
* The record for one conversation, or the most recently active one.
|
|
36366
|
-
*
|
|
36367
|
-
* The fallback is what makes the status card useful without a session named:
|
|
36368
|
-
* the card describes the line, and the only conversation it can honestly
|
|
36369
|
-
* describe in that case is the one that ran last.
|
|
36370
|
-
* @param sessionId - Conversation to read; omit for the most recent one.
|
|
36371
|
-
* @returns the record, or undefined when nothing has ever been recorded.
|
|
36372
|
-
*/
|
|
36373
37012
|
read(sessionId) {
|
|
36374
37013
|
const sessions = this.load().sessions;
|
|
36375
37014
|
const named = sessionId?.trim();
|
|
@@ -36378,80 +37017,59 @@ var SessionCacheStateStore = class {
|
|
|
36378
37017
|
for (const record of Object.values(sessions)) if (newest === void 0 || record.updatedAt > newest.updatedAt) newest = record;
|
|
36379
37018
|
return newest;
|
|
36380
37019
|
}
|
|
36381
|
-
/**
|
|
36382
|
-
* File one conversation's state and schedule the document write.
|
|
36383
|
-
* @param sessionId - Conversation the record belongs to; ignored when blank.
|
|
36384
|
-
* @param record - Complete record for that conversation.
|
|
36385
|
-
*/
|
|
36386
37020
|
write(sessionId, record) {
|
|
36387
37021
|
const key = sessionId.trim();
|
|
36388
37022
|
if (key === "") return;
|
|
36389
37023
|
const document = this.load();
|
|
36390
37024
|
document.sessions[key] = record;
|
|
36391
|
-
|
|
37025
|
+
const entries = Object.entries(document.sessions);
|
|
37026
|
+
if (entries.length > this.maxEntries) {
|
|
37027
|
+
entries.sort((a, b) => b[1].updatedAt - a[1].updatedAt);
|
|
37028
|
+
document.sessions = Object.fromEntries(entries.slice(0, this.maxEntries));
|
|
37029
|
+
}
|
|
36392
37030
|
this.schedule();
|
|
36393
37031
|
}
|
|
36394
|
-
/**
|
|
36395
|
-
|
|
36396
|
-
* in place, without issuing a second `write()` call.
|
|
36397
|
-
*/
|
|
36398
|
-
noteWarmupStreak(sessionId, consecutiveHits) {
|
|
37032
|
+
/** Update an existing record; absence is not created implicitly. */
|
|
37033
|
+
update(sessionId, update) {
|
|
36399
37034
|
const key = sessionId.trim();
|
|
36400
|
-
if (key === "") return;
|
|
37035
|
+
if (key === "") return false;
|
|
36401
37036
|
const document = this.load();
|
|
36402
37037
|
const existing = document.sessions[key];
|
|
36403
|
-
if (existing === void 0) return;
|
|
36404
|
-
const
|
|
36405
|
-
if (
|
|
36406
|
-
|
|
36407
|
-
warmupConsecutiveHits: normalized
|
|
36408
|
-
};
|
|
36409
|
-
else if (existing.warmupConsecutiveHits !== void 0) {
|
|
36410
|
-
const { warmupConsecutiveHits: _dropped, ...rest } = existing;
|
|
36411
|
-
document.sessions[key] = rest;
|
|
36412
|
-
} else return;
|
|
37038
|
+
if (existing === void 0) return false;
|
|
37039
|
+
const next = update(existing);
|
|
37040
|
+
if (next === existing) return true;
|
|
37041
|
+
document.sessions[key] = next;
|
|
36413
37042
|
this.schedule();
|
|
37043
|
+
return true;
|
|
36414
37044
|
}
|
|
36415
|
-
/**
|
|
36416
|
-
* Write the document now, synchronously.
|
|
36417
|
-
*
|
|
36418
|
-
* Exposed for tests and for a caller that is about to lose the process; the
|
|
36419
|
-
* debounced path is what normal turns use.
|
|
36420
|
-
*/
|
|
36421
37045
|
flush() {
|
|
36422
37046
|
this.cancelPending();
|
|
36423
|
-
|
|
36424
|
-
if (document === void 0) return;
|
|
36425
|
-
this.persist(document);
|
|
37047
|
+
if (this.document !== void 0) this.persist(this.document);
|
|
36426
37048
|
}
|
|
36427
|
-
/** Drop everything older than the newest maxEntries conversations. */
|
|
36428
|
-
prune(document) {
|
|
36429
|
-
const entries = Object.entries(document.sessions);
|
|
36430
|
-
if (entries.length <= this.maxEntries) return;
|
|
36431
|
-
entries.sort((a, b) => b[1].updatedAt - a[1].updatedAt);
|
|
36432
|
-
document.sessions = Object.fromEntries(entries.slice(0, this.maxEntries));
|
|
36433
|
-
}
|
|
36434
|
-
/** Read the document once; a missing or corrupt file reads as empty. */
|
|
36435
37049
|
load() {
|
|
36436
37050
|
if (this.document !== void 0) return this.document;
|
|
37051
|
+
const sessions = {};
|
|
36437
37052
|
try {
|
|
36438
37053
|
const parsed = JSON.parse(fs$1.readFileSync(this.filePath, "utf8"));
|
|
36439
|
-
|
|
36440
|
-
|
|
36441
|
-
|
|
36442
|
-
|
|
36443
|
-
|
|
36444
|
-
|
|
36445
|
-
|
|
37054
|
+
if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
|
|
37055
|
+
const rawSessions = parsed.sessions;
|
|
37056
|
+
if (typeof rawSessions === "object" && rawSessions !== null && !Array.isArray(rawSessions)) for (const [key, entry] of Object.entries(rawSessions)) {
|
|
37057
|
+
const record = this.normalizeRecord(entry);
|
|
37058
|
+
if (record !== void 0) sessions[key] = record;
|
|
37059
|
+
}
|
|
37060
|
+
}
|
|
37061
|
+
} catch {}
|
|
37062
|
+
this.document = {
|
|
37063
|
+
version: 1,
|
|
37064
|
+
sessions
|
|
37065
|
+
};
|
|
36446
37066
|
return this.document;
|
|
36447
37067
|
}
|
|
36448
|
-
/** Coalesce the writes one turn's several fields would otherwise cause. */
|
|
36449
37068
|
schedule() {
|
|
36450
37069
|
if (!this.allowWrite || this.pending !== void 0) return;
|
|
36451
37070
|
this.pending = setTimeout(() => {
|
|
36452
37071
|
this.pending = void 0;
|
|
36453
|
-
|
|
36454
|
-
if (document !== void 0) this.persist(document);
|
|
37072
|
+
if (this.document !== void 0) this.persist(this.document);
|
|
36455
37073
|
}, 250);
|
|
36456
37074
|
this.pending.unref?.();
|
|
36457
37075
|
}
|
|
@@ -36460,35 +37078,69 @@ var SessionCacheStateStore = class {
|
|
|
36460
37078
|
clearTimeout(this.pending);
|
|
36461
37079
|
this.pending = void 0;
|
|
36462
37080
|
}
|
|
36463
|
-
/** Write through a temporary file so a crash cannot leave half a document. */
|
|
36464
37081
|
persist(document) {
|
|
36465
37082
|
if (!this.allowWrite) return;
|
|
36466
37083
|
try {
|
|
36467
37084
|
fs$1.mkdirSync(path.dirname(this.filePath), { recursive: true });
|
|
36468
|
-
const temporary = this.filePath + "." + process.pid + "." + ++writeSequence + ".tmp";
|
|
37085
|
+
const temporary = this.filePath + "." + process.pid + "." + ++CommonSessionCacheStateStore.writeSequence + ".tmp";
|
|
36469
37086
|
fs$1.writeFileSync(temporary, JSON.stringify(document), "utf8");
|
|
36470
37087
|
fs$1.renameSync(temporary, this.filePath);
|
|
36471
37088
|
} catch {}
|
|
36472
37089
|
}
|
|
36473
37090
|
};
|
|
36474
|
-
/**
|
|
36475
|
-
function
|
|
36476
|
-
|
|
36477
|
-
if (isRecord$52(value) && isRecord$52(value.sessions)) for (const [key, entry] of Object.entries(value.sessions)) {
|
|
36478
|
-
const record = normalizeRecord(entry);
|
|
36479
|
-
if (record !== void 0) sessions[key] = record;
|
|
36480
|
-
}
|
|
36481
|
-
return {
|
|
36482
|
-
version: 1,
|
|
36483
|
-
sessions
|
|
36484
|
-
};
|
|
37091
|
+
/** Where the document lives; beside the other plugin-owned JSON. */
|
|
37092
|
+
function sessionCacheStatePath() {
|
|
37093
|
+
return path.join(dshHomeDir(), "storages", "dsh-chatgpt-subscription-kimi-cache.json");
|
|
36485
37094
|
}
|
|
36486
|
-
|
|
37095
|
+
/**
|
|
37096
|
+
* Whether a scheduled write should reach the disk.
|
|
37097
|
+
*
|
|
37098
|
+
* A unit test that exercises the request path must not overwrite the state file
|
|
37099
|
+
* of the machine running it; tests build their own store against a temporary
|
|
37100
|
+
* path (which always writes) and call {@link SessionCacheStateStore.flush}.
|
|
37101
|
+
*/
|
|
37102
|
+
function persistenceEnabled$2() {
|
|
37103
|
+
return process.env.VITEST !== "true" && process.env.NODE_ENV !== "test";
|
|
37104
|
+
}
|
|
37105
|
+
/**
|
|
37106
|
+
* Per-session cache state held in one JSON document.
|
|
37107
|
+
*
|
|
37108
|
+
* Reads are synchronous because every caller is answering a status request or
|
|
37109
|
+
* closing a turn, both of which are allowed to block on a file this size. Writes
|
|
37110
|
+
* are debounced: a turn reports once, and several turns in flight must not
|
|
37111
|
+
* rewrite the document several times.
|
|
37112
|
+
*/
|
|
37113
|
+
var SessionCacheStateStore = class extends CommonSessionCacheStateStore {
|
|
37114
|
+
constructor(filePath = sessionCacheStatePath(), maxEntries = 64) {
|
|
37115
|
+
super({
|
|
37116
|
+
filePath,
|
|
37117
|
+
maxEntries,
|
|
37118
|
+
allowWrite: filePath !== sessionCacheStatePath() || persistenceEnabled$2(),
|
|
37119
|
+
normalizeRecord: normalizeRecord$2
|
|
37120
|
+
});
|
|
37121
|
+
}
|
|
37122
|
+
/** Update the warmup streak without creating a record for an unknown session. */
|
|
37123
|
+
noteWarmupStreak(sessionId, consecutiveHits) {
|
|
37124
|
+
this.update(sessionId, (existing) => {
|
|
37125
|
+
const normalized = count$1(consecutiveHits);
|
|
37126
|
+
if (normalized > 0) return {
|
|
37127
|
+
...existing,
|
|
37128
|
+
warmupConsecutiveHits: normalized
|
|
37129
|
+
};
|
|
37130
|
+
if (existing.warmupConsecutiveHits === void 0) return existing;
|
|
37131
|
+
const { warmupConsecutiveHits: _dropped, ...rest } = existing;
|
|
37132
|
+
return rest;
|
|
37133
|
+
});
|
|
37134
|
+
}
|
|
37135
|
+
};
|
|
37136
|
+
function normalizeRecord$2(value) {
|
|
36487
37137
|
if (!isRecord$52(value)) return void 0;
|
|
36488
37138
|
const lastActiveAt = finite(value.lastActiveAt);
|
|
36489
37139
|
const updatedAt = finite(value.updatedAt);
|
|
36490
37140
|
if (lastActiveAt === void 0 || updatedAt === void 0) return void 0;
|
|
36491
37141
|
const drift = typeof value.lastDrift === "string" && value.lastDrift !== "" ? value.lastDrift : void 0;
|
|
37142
|
+
const firstRequestCachedTokens = nonNegative(value.firstRequestCachedTokens);
|
|
37143
|
+
const firstRequestPromptTokens = nonNegative(value.firstRequestPromptTokens);
|
|
36492
37144
|
const warmupStreak = count$1(value.warmupConsecutiveHits);
|
|
36493
37145
|
const budgetDropped = droppedImageKeys(value.budgetDropped);
|
|
36494
37146
|
const lastRequestImageBytes = nonNegative(value.lastRequestImageBytes);
|
|
@@ -36505,6 +37157,8 @@ function normalizeRecord(value) {
|
|
|
36505
37157
|
cacheWrite5mTokens: count$1(value.cacheWrite5mTokens),
|
|
36506
37158
|
cacheWrite1hTokens: count$1(value.cacheWrite1hTokens),
|
|
36507
37159
|
...drift === void 0 ? {} : { lastDrift: drift },
|
|
37160
|
+
...firstRequestCachedTokens === void 0 ? {} : { firstRequestCachedTokens },
|
|
37161
|
+
...firstRequestPromptTokens === void 0 ? {} : { firstRequestPromptTokens },
|
|
36508
37162
|
...warmupStreak > 0 ? { warmupConsecutiveHits: warmupStreak } : {},
|
|
36509
37163
|
...budgetDropped.length > 0 ? { budgetDropped } : {},
|
|
36510
37164
|
...lastRequestImageBytes === void 0 ? {} : { lastRequestImageBytes },
|
|
@@ -36583,6 +37237,119 @@ function count$1(value) {
|
|
|
36583
37237
|
return number === void 0 || number < 0 ? 0 : Math.floor(number);
|
|
36584
37238
|
}
|
|
36585
37239
|
//#endregion
|
|
37240
|
+
//#region src/host/common/hosted-media.ts
|
|
37241
|
+
/** Default bounded LRU; providers with durable caches can supply their own adapter. */
|
|
37242
|
+
var MemoryHostedMediaCache = class {
|
|
37243
|
+
maxEntries;
|
|
37244
|
+
entries = /* @__PURE__ */ new Map();
|
|
37245
|
+
constructor(maxEntries = 256) {
|
|
37246
|
+
this.maxEntries = maxEntries;
|
|
37247
|
+
}
|
|
37248
|
+
get size() {
|
|
37249
|
+
return this.entries.size;
|
|
37250
|
+
}
|
|
37251
|
+
get(key) {
|
|
37252
|
+
const value = this.entries.get(key);
|
|
37253
|
+
if (value === void 0) return void 0;
|
|
37254
|
+
this.entries.delete(key);
|
|
37255
|
+
this.entries.set(key, value);
|
|
37256
|
+
return value;
|
|
37257
|
+
}
|
|
37258
|
+
set(key, value) {
|
|
37259
|
+
this.entries.delete(key);
|
|
37260
|
+
this.entries.set(key, value);
|
|
37261
|
+
while (this.entries.size > this.maxEntries) {
|
|
37262
|
+
const oldest = this.entries.keys().next();
|
|
37263
|
+
if (oldest.done !== true) this.entries.delete(oldest.value);
|
|
37264
|
+
}
|
|
37265
|
+
}
|
|
37266
|
+
delete(key) {
|
|
37267
|
+
return this.entries.delete(key);
|
|
37268
|
+
}
|
|
37269
|
+
clear() {
|
|
37270
|
+
this.entries.clear();
|
|
37271
|
+
}
|
|
37272
|
+
};
|
|
37273
|
+
var HostedMediaUploadCoordinator = class {
|
|
37274
|
+
cache;
|
|
37275
|
+
flights = /* @__PURE__ */ new Map();
|
|
37276
|
+
constructor(options = {}) {
|
|
37277
|
+
this.cache = options.cache ?? new MemoryHostedMediaCache(options.maxCacheEntries);
|
|
37278
|
+
}
|
|
37279
|
+
get size() {
|
|
37280
|
+
return this.cache.size ?? 0;
|
|
37281
|
+
}
|
|
37282
|
+
/** Upload once per key; one caller aborts only its own wait. */
|
|
37283
|
+
async upload(key, callerSignal, start) {
|
|
37284
|
+
if (callerSignal?.aborted === true) throw abortWithReason(callerSignal);
|
|
37285
|
+
const cached = await this.cache.get(key);
|
|
37286
|
+
if (cached !== void 0) return cached;
|
|
37287
|
+
let flight = this.flights.get(key);
|
|
37288
|
+
if (flight === void 0) {
|
|
37289
|
+
const controller = new AbortController();
|
|
37290
|
+
const promise = start(controller).then(async (value) => {
|
|
37291
|
+
await this.cache.set(key, value);
|
|
37292
|
+
return value;
|
|
37293
|
+
});
|
|
37294
|
+
flight = {
|
|
37295
|
+
promise,
|
|
37296
|
+
controller,
|
|
37297
|
+
waiters: 0
|
|
37298
|
+
};
|
|
37299
|
+
this.flights.set(key, flight);
|
|
37300
|
+
promise.catch(() => void 0);
|
|
37301
|
+
promise.finally(() => {
|
|
37302
|
+
if (this.flights.get(key) === flight) this.flights.delete(key);
|
|
37303
|
+
}).catch(() => void 0);
|
|
37304
|
+
}
|
|
37305
|
+
return this.join(flight, callerSignal);
|
|
37306
|
+
}
|
|
37307
|
+
forget(key) {
|
|
37308
|
+
return this.cache.delete(key);
|
|
37309
|
+
}
|
|
37310
|
+
clear() {
|
|
37311
|
+
return this.cache.clear();
|
|
37312
|
+
}
|
|
37313
|
+
async join(flight, signal) {
|
|
37314
|
+
if (signal?.aborted === true) throw abortWithReason(signal);
|
|
37315
|
+
flight.waiters += 1;
|
|
37316
|
+
let released = false;
|
|
37317
|
+
const release = () => {
|
|
37318
|
+
if (released) return;
|
|
37319
|
+
released = true;
|
|
37320
|
+
flight.waiters -= 1;
|
|
37321
|
+
if (flight.waiters <= 0) {
|
|
37322
|
+
for (const [key, candidate] of this.flights) if (candidate === flight) this.flights.delete(key);
|
|
37323
|
+
flight.controller.abort();
|
|
37324
|
+
}
|
|
37325
|
+
};
|
|
37326
|
+
if (signal === void 0) try {
|
|
37327
|
+
return await flight.promise;
|
|
37328
|
+
} finally {
|
|
37329
|
+
release();
|
|
37330
|
+
}
|
|
37331
|
+
let detach;
|
|
37332
|
+
const cancelled = new Promise((_resolve, reject) => {
|
|
37333
|
+
const onAbort = () => reject(abortWithReason(signal));
|
|
37334
|
+
detach = () => signal.removeEventListener("abort", onAbort);
|
|
37335
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
37336
|
+
});
|
|
37337
|
+
try {
|
|
37338
|
+
return await Promise.race([flight.promise, cancelled]);
|
|
37339
|
+
} finally {
|
|
37340
|
+
detach?.();
|
|
37341
|
+
release();
|
|
37342
|
+
}
|
|
37343
|
+
}
|
|
37344
|
+
};
|
|
37345
|
+
function abortWithReason(signal) {
|
|
37346
|
+
const reason = signal.reason;
|
|
37347
|
+
if (reason instanceof Error) return reason;
|
|
37348
|
+
const error = /* @__PURE__ */ new Error("the media upload was aborted");
|
|
37349
|
+
error.name = "AbortError";
|
|
37350
|
+
return error;
|
|
37351
|
+
}
|
|
37352
|
+
//#endregion
|
|
36586
37353
|
//#region src/host/kimi-code/media-files.ts
|
|
36587
37354
|
/**
|
|
36588
37355
|
* Kimi Code hosted-file (media upload) client.
|
|
@@ -36863,60 +37630,38 @@ function abortError(signal) {
|
|
|
36863
37630
|
*/
|
|
36864
37631
|
var KimiMediaFilesUploader = class {
|
|
36865
37632
|
fetchFn;
|
|
36866
|
-
maxCacheEntries;
|
|
36867
37633
|
timeoutMs;
|
|
36868
|
-
|
|
36869
|
-
cache = /* @__PURE__ */ new Map();
|
|
36870
|
-
flights = /* @__PURE__ */ new Map();
|
|
37634
|
+
coordinator;
|
|
36871
37635
|
constructor(options = {}) {
|
|
36872
37636
|
this.fetchFn = options.fetchFn ?? fetch;
|
|
36873
|
-
|
|
36874
|
-
this.maxCacheEntries = cap !== void 0 && Number.isFinite(cap) && cap > 0 ? Math.floor(cap) : 256;
|
|
37637
|
+
this.coordinator = new HostedMediaUploadCoordinator({ maxCacheEntries: options.maxCacheEntries });
|
|
36875
37638
|
const timeout = options.timeoutMs;
|
|
36876
37639
|
this.timeoutMs = timeout !== void 0 && Number.isFinite(timeout) ? timeout : MEDIA_UPLOAD_TIMEOUT_MS;
|
|
36877
37640
|
}
|
|
36878
|
-
/** Cached references currently held, for a settings surface or a test. */
|
|
36879
37641
|
get size() {
|
|
36880
|
-
return this.
|
|
37642
|
+
return this.coordinator.size;
|
|
36881
37643
|
}
|
|
36882
|
-
/** Uploads one file and returns the `ms://<id>` reference a request cites. */
|
|
36883
37644
|
async upload(request) {
|
|
36884
37645
|
const resolved = this.resolve(request);
|
|
36885
|
-
|
|
36886
|
-
const cached = this.readCache(resolved.key);
|
|
36887
|
-
if (cached !== void 0) return cached;
|
|
36888
|
-
let flight = this.flights.get(resolved.key);
|
|
36889
|
-
if (flight === void 0) {
|
|
36890
|
-
flight = this.startFlight(resolved);
|
|
36891
|
-
this.flights.set(resolved.key, flight);
|
|
36892
|
-
}
|
|
36893
|
-
return this.join(flight, resolved);
|
|
37646
|
+
return this.coordinator.upload(resolved.key, resolved.signal, (controller) => this.performUpload(controller, { timedOut: false }, resolved));
|
|
36894
37647
|
}
|
|
36895
|
-
/**
|
|
36896
|
-
* Drop the reference one upload would use.
|
|
36897
|
-
*
|
|
36898
|
-
* The caller that sees a stored id REJECTED on the wire calls this so the next
|
|
36899
|
-
* attempt uploads again instead of replaying the same dead reference.
|
|
36900
|
-
*/
|
|
37648
|
+
/** Drop a dead reference so the next attempt uploads again. */
|
|
36901
37649
|
forget(request) {
|
|
36902
|
-
let key;
|
|
36903
37650
|
try {
|
|
36904
|
-
key = mediaFileCacheKey({
|
|
37651
|
+
const key = mediaFileCacheKey({
|
|
36905
37652
|
accountScope: request.accountScope,
|
|
36906
37653
|
baseUrl: request.baseUrl,
|
|
36907
37654
|
mediaType: request.mediaType,
|
|
36908
37655
|
data: request.data
|
|
36909
37656
|
});
|
|
37657
|
+
return this.coordinator.forget(key);
|
|
36910
37658
|
} catch {
|
|
36911
37659
|
return false;
|
|
36912
37660
|
}
|
|
36913
|
-
return this.cache.delete(key);
|
|
36914
37661
|
}
|
|
36915
|
-
/** Forget everything; a sign-out or a test starting over. */
|
|
36916
37662
|
clear() {
|
|
36917
|
-
this.
|
|
37663
|
+
this.coordinator.clear();
|
|
36918
37664
|
}
|
|
36919
|
-
/** Validate and normalize one request, or throw before anything is sent. */
|
|
36920
37665
|
resolve(request) {
|
|
36921
37666
|
const accessToken = request.accessToken.trim();
|
|
36922
37667
|
if (accessToken === "") throw new KimiMediaUploadError("invalid-request", "the media upload needs an access token");
|
|
@@ -36947,77 +37692,6 @@ var KimiMediaFilesUploader = class {
|
|
|
36947
37692
|
})
|
|
36948
37693
|
};
|
|
36949
37694
|
}
|
|
36950
|
-
readCache(key) {
|
|
36951
|
-
const ref = this.cache.get(key);
|
|
36952
|
-
if (ref === void 0) return void 0;
|
|
36953
|
-
this.cache.delete(key);
|
|
36954
|
-
this.cache.set(key, ref);
|
|
36955
|
-
return ref;
|
|
36956
|
-
}
|
|
36957
|
-
writeCache(key, ref) {
|
|
36958
|
-
this.cache.delete(key);
|
|
36959
|
-
this.cache.set(key, ref);
|
|
36960
|
-
while (this.cache.size > this.maxCacheEntries) {
|
|
36961
|
-
const oldest = this.cache.keys().next();
|
|
36962
|
-
if (oldest.done === true) break;
|
|
36963
|
-
this.cache.delete(oldest.value);
|
|
36964
|
-
}
|
|
36965
|
-
}
|
|
36966
|
-
/** Open one flight and run it; the map entry is dropped as it settles. */
|
|
36967
|
-
startFlight(request) {
|
|
36968
|
-
const controller = new AbortController();
|
|
36969
|
-
const state = { timedOut: false };
|
|
36970
|
-
const promise = this.performUpload(controller, state, request).finally(() => {
|
|
36971
|
-
if (this.flights.get(request.key)?.promise === promise) this.flights.delete(request.key);
|
|
36972
|
-
});
|
|
36973
|
-
promise.catch(() => void 0);
|
|
36974
|
-
return {
|
|
36975
|
-
promise,
|
|
36976
|
-
controller,
|
|
36977
|
-
waiters: 0,
|
|
36978
|
-
state
|
|
36979
|
-
};
|
|
36980
|
-
}
|
|
36981
|
-
/**
|
|
36982
|
-
* Wait for one flight, respecting THIS caller's abort.
|
|
36983
|
-
*
|
|
36984
|
-
* The flight is not bound to the caller's signal: a caller that aborts
|
|
36985
|
-
* detaches, and the transport is cancelled only when the last waiter has gone.
|
|
36986
|
-
*/
|
|
36987
|
-
async join(flight, request) {
|
|
36988
|
-
const signal = request.signal;
|
|
36989
|
-
if (signal?.aborted === true) throw abortError(signal);
|
|
36990
|
-
flight.waiters += 1;
|
|
36991
|
-
let released = false;
|
|
36992
|
-
const release = () => {
|
|
36993
|
-
if (released) return;
|
|
36994
|
-
released = true;
|
|
36995
|
-
flight.waiters -= 1;
|
|
36996
|
-
if (flight.waiters <= 0) {
|
|
36997
|
-
if (this.flights.get(request.key) === flight) this.flights.delete(request.key);
|
|
36998
|
-
flight.controller.abort();
|
|
36999
|
-
}
|
|
37000
|
-
};
|
|
37001
|
-
if (signal === void 0) try {
|
|
37002
|
-
return await flight.promise;
|
|
37003
|
-
} finally {
|
|
37004
|
-
release();
|
|
37005
|
-
}
|
|
37006
|
-
const callerSignal = signal;
|
|
37007
|
-
let detach;
|
|
37008
|
-
const cancelled = new Promise((_resolve, reject) => {
|
|
37009
|
-
const onAbort = () => reject(abortError(callerSignal));
|
|
37010
|
-
detach = () => callerSignal.removeEventListener("abort", onAbort);
|
|
37011
|
-
callerSignal.addEventListener("abort", onAbort, { once: true });
|
|
37012
|
-
});
|
|
37013
|
-
try {
|
|
37014
|
-
return await Promise.race([flight.promise, cancelled]);
|
|
37015
|
-
} finally {
|
|
37016
|
-
detach?.();
|
|
37017
|
-
release();
|
|
37018
|
-
}
|
|
37019
|
-
}
|
|
37020
|
-
/** The one request a flight performs. */
|
|
37021
37695
|
async performUpload(controller, state, request) {
|
|
37022
37696
|
const form = new FormData();
|
|
37023
37697
|
form.append("file", new Blob([request.data], { type: request.mediaType }), request.filename);
|
|
@@ -37054,7 +37728,6 @@ var KimiMediaFilesUploader = class {
|
|
|
37054
37728
|
if (timer !== void 0) clearTimeout(timer);
|
|
37055
37729
|
}
|
|
37056
37730
|
}
|
|
37057
|
-
/** Turn a transport rejection into the error the caller must see. */
|
|
37058
37731
|
transportFailure(error, state, request) {
|
|
37059
37732
|
if (looksAborted(error)) {
|
|
37060
37733
|
if (state.timedOut) return new KimiMediaUploadError("timeout", "the media upload did not finish within " + request.timeoutMs + " ms");
|
|
@@ -37062,7 +37735,6 @@ var KimiMediaFilesUploader = class {
|
|
|
37062
37735
|
}
|
|
37063
37736
|
return new KimiMediaUploadError("network", "the media upload did not complete: " + redact(error instanceof Error ? error.message : String(error), request.accessToken));
|
|
37064
37737
|
}
|
|
37065
|
-
/** Read one response into a reference, or into a typed failure. */
|
|
37066
37738
|
settle(response, raw, request) {
|
|
37067
37739
|
const status = response.status;
|
|
37068
37740
|
if (response.ok !== true) {
|
|
@@ -37075,7 +37747,7 @@ var KimiMediaFilesUploader = class {
|
|
|
37075
37747
|
status,
|
|
37076
37748
|
detail
|
|
37077
37749
|
});
|
|
37078
|
-
if (status === 413) throw new KimiMediaUploadError("too-large", "the Kimi Code hosted-file endpoint refused the upload as too large (HTTP
|
|
37750
|
+
if (status === 413) throw new KimiMediaUploadError("too-large", "the Kimi Code hosted-file endpoint refused the upload as too large (HTTP " + status + ")", {
|
|
37079
37751
|
status,
|
|
37080
37752
|
detail
|
|
37081
37753
|
});
|
|
@@ -37083,7 +37755,7 @@ var KimiMediaFilesUploader = class {
|
|
|
37083
37755
|
status,
|
|
37084
37756
|
detail
|
|
37085
37757
|
});
|
|
37086
|
-
throw new KimiMediaUploadError("rejected", "the Kimi Code hosted-file endpoint
|
|
37758
|
+
throw new KimiMediaUploadError("rejected", "the Kimi Code hosted-file endpoint rejected the upload (HTTP " + status + ")", {
|
|
37087
37759
|
status,
|
|
37088
37760
|
detail
|
|
37089
37761
|
});
|
|
@@ -37094,13 +37766,11 @@ var KimiMediaFilesUploader = class {
|
|
|
37094
37766
|
detail: redact(raw, request.accessToken)
|
|
37095
37767
|
});
|
|
37096
37768
|
const id = readFileId$1(parsed.value);
|
|
37097
|
-
if (id === void 0) throw new KimiMediaUploadError("no-id", "the media upload answered HTTP " + status + " without a usable
|
|
37769
|
+
if (id === void 0) throw new KimiMediaUploadError("no-id", "the media upload answered HTTP " + status + " without a usable id", {
|
|
37098
37770
|
status,
|
|
37099
37771
|
detail: redact(raw, request.accessToken)
|
|
37100
37772
|
});
|
|
37101
|
-
|
|
37102
|
-
this.writeCache(request.key, ref);
|
|
37103
|
-
return ref;
|
|
37773
|
+
return fileReference(id);
|
|
37104
37774
|
}
|
|
37105
37775
|
};
|
|
37106
37776
|
/** One uploader with the default transport and cache size. */
|
|
@@ -37108,6 +37778,118 @@ function createMediaFilesUploader(options = {}) {
|
|
|
37108
37778
|
return new KimiMediaFilesUploader(options);
|
|
37109
37779
|
}
|
|
37110
37780
|
createMediaFilesUploader();
|
|
37781
|
+
function clampShare(value) {
|
|
37782
|
+
if (!Number.isFinite(value)) return 0;
|
|
37783
|
+
return Math.min(1, Math.max(0, value));
|
|
37784
|
+
}
|
|
37785
|
+
/** Tightest measured window; unknown remains null rather than pretending full/empty. */
|
|
37786
|
+
function remainingQuotaShare$1(quota) {
|
|
37787
|
+
if (quota === null || quota === void 0 || quota.windows.length === 0) return null;
|
|
37788
|
+
let tightest = null;
|
|
37789
|
+
for (const window of quota.windows) {
|
|
37790
|
+
const used = typeof window.usedFraction === "number" ? window.usedFraction : typeof window.usedPercent === "number" ? window.usedPercent / 100 : void 0;
|
|
37791
|
+
if (used === void 0 || !Number.isFinite(used)) continue;
|
|
37792
|
+
const remaining = clampShare(1 - used);
|
|
37793
|
+
tightest = tightest === null ? remaining : Math.min(tightest, remaining);
|
|
37794
|
+
}
|
|
37795
|
+
return tightest;
|
|
37796
|
+
}
|
|
37797
|
+
/** Warm and sufficiently-funded accounts first; no preference is an empty list. */
|
|
37798
|
+
function orderAffinityCandidates$1(input) {
|
|
37799
|
+
const floor = input.floorShare ?? .2;
|
|
37800
|
+
const measured = [];
|
|
37801
|
+
const unmeasured = [];
|
|
37802
|
+
for (const candidate of input.candidates) {
|
|
37803
|
+
if (!candidate.warm) continue;
|
|
37804
|
+
const share = input.remainingShareById?.[candidate.id];
|
|
37805
|
+
if (share === null || share === void 0 || !Number.isFinite(share)) {
|
|
37806
|
+
unmeasured.push(candidate.id);
|
|
37807
|
+
continue;
|
|
37808
|
+
}
|
|
37809
|
+
if (share < floor) continue;
|
|
37810
|
+
measured.push({
|
|
37811
|
+
id: candidate.id,
|
|
37812
|
+
share
|
|
37813
|
+
});
|
|
37814
|
+
}
|
|
37815
|
+
measured.sort((left, right) => right.share - left.share);
|
|
37816
|
+
return [...measured.map((entry) => entry.id), ...unmeasured];
|
|
37817
|
+
}
|
|
37818
|
+
//#endregion
|
|
37819
|
+
//#region src/host/kimi-code/cache-affinity.ts
|
|
37820
|
+
/**
|
|
37821
|
+
* The share of its tightest window an account has left, or null when unknown.
|
|
37822
|
+
*
|
|
37823
|
+
* The TIGHTEST window is the only honest answer: a pool row can carry several
|
|
37824
|
+
* at once, and an account with 90% of a weekly window but 2% of its five-hour
|
|
37825
|
+
* one is about to be refused. A window whose consumption was never measured
|
|
37826
|
+
* is skipped rather than read as empty or full, and a snapshot with no measured
|
|
37827
|
+
* window at all answers null — "unknown", which the caller treats as "not
|
|
37828
|
+
* provably short" instead of blocking the account on a number nobody measured.
|
|
37829
|
+
*
|
|
37830
|
+
* @param quota - The account's newest quota snapshot, when one was ever read.
|
|
37831
|
+
* @returns the smallest remaining fraction in `[0, 1]`, or null when unknown.
|
|
37832
|
+
*/
|
|
37833
|
+
function remainingQuotaShare(quota) {
|
|
37834
|
+
return remainingQuotaShare$1(quota);
|
|
37835
|
+
}
|
|
37836
|
+
/** Settings-card description for the cache-affinity switch. */
|
|
37837
|
+
const CACHE_AFFINITY_SETTING_DESCRIPTION = "多账号轮换时优先选「这条前缀的缓存还热着」且额度充足的账号;默认用于 Kimi。其他线路需在 promptCacheOverrides 中证明 scope:\"account\" 并显式启用;关闭后回到原轮换策略。";
|
|
37838
|
+
/**
|
|
37839
|
+
* Environment override for the affinity switch.
|
|
37840
|
+
*
|
|
37841
|
+
* The same contract as the wait-time warmup switch beside it in settings: an
|
|
37842
|
+
* explicit value wins over the stored setting, so a deployment can pin the
|
|
37843
|
+
* behavioural change without editing preferences on disk.
|
|
37844
|
+
*/
|
|
37845
|
+
const CACHE_AFFINITY_ENV = "DSH_KIMI_CODE_CACHE_AFFINITY";
|
|
37846
|
+
/**
|
|
37847
|
+
* Resolve whether cache-affinity routing is enabled.
|
|
37848
|
+
*
|
|
37849
|
+
* Enabled by default. `0`/`false`/`no`/`off` and `1`/`true`/`yes`/`on` override
|
|
37850
|
+
* the stored setting; otherwise the live `cacheAffinity` setting (default
|
|
37851
|
+
* `true`) governs. Anything unreadable falls back to enabled rather than
|
|
37852
|
+
* throwing inside a request path.
|
|
37853
|
+
*
|
|
37854
|
+
* @param config - Plugin config carrying the live setting.
|
|
37855
|
+
* @param env - Environment to consult; `process.env` in production.
|
|
37856
|
+
* @returns whether a request may state an account preference.
|
|
37857
|
+
*/
|
|
37858
|
+
function resolveCacheAffinityEnabled(config, env = process.env) {
|
|
37859
|
+
const raw = env[CACHE_AFFINITY_ENV]?.trim().toLowerCase();
|
|
37860
|
+
if (raw === "0" || raw === "false" || raw === "no" || raw === "off") return false;
|
|
37861
|
+
if (raw === "1" || raw === "true" || raw === "yes" || raw === "on") return true;
|
|
37862
|
+
const field = config?.cacheAffinity;
|
|
37863
|
+
if (field === void 0 || field === null) return true;
|
|
37864
|
+
if (typeof field === "object" && typeof field.get === "function") try {
|
|
37865
|
+
return field.get() !== false;
|
|
37866
|
+
} catch {
|
|
37867
|
+
return true;
|
|
37868
|
+
}
|
|
37869
|
+
return field !== false;
|
|
37870
|
+
}
|
|
37871
|
+
/**
|
|
37872
|
+
* Order the accounts this request would rather use, warmest and safest first.
|
|
37873
|
+
*
|
|
37874
|
+
* Only accounts whose prefix is warm AND whose remaining share is either
|
|
37875
|
+
* unknown or at least the floor are returned: a warm account that is nearly
|
|
37876
|
+
* spent is deliberately absent, because the pool then falls back to its own
|
|
37877
|
+
* rotation instead of spending the last of a window on a discount. An empty
|
|
37878
|
+
* result means "no preference" — the caller passes no hint at all and the
|
|
37879
|
+
* strategy behaves exactly as it would have without this module.
|
|
37880
|
+
*
|
|
37881
|
+
* Order: a warm account with a known-and-sufficient share first (largest
|
|
37882
|
+
* remaining first, so two warm accounts spread the load instead of always
|
|
37883
|
+
* spending one), then a warm account whose share nobody measured. Unknown last
|
|
37884
|
+
* because a measured account is a better bet than an unmeasured one, but still
|
|
37885
|
+
* listed: an unmeasured warm account is not evidence of anything bad.
|
|
37886
|
+
*
|
|
37887
|
+
* @param input - candidates plus the quota evidence behind them.
|
|
37888
|
+
* @returns account ids in preference order; empty when nothing is preferred.
|
|
37889
|
+
*/
|
|
37890
|
+
function orderAffinityCandidates(input) {
|
|
37891
|
+
return orderAffinityCandidates$1(input);
|
|
37892
|
+
}
|
|
37111
37893
|
//#endregion
|
|
37112
37894
|
//#region src/host/kimi-code/adapter.ts
|
|
37113
37895
|
/**
|
|
@@ -37592,9 +38374,13 @@ const BILLING_CYCLE_PATTERN = /usage limit for this billing cycle/i;
|
|
|
37592
38374
|
function accountLimitCooldownMs(bodyText, now = Date.now()) {
|
|
37593
38375
|
const field = bodyText.match(RESET_FIELD_PATTERN);
|
|
37594
38376
|
const instant = bodyText.match(RESET_INSTANT_PATTERN);
|
|
37595
|
-
|
|
37596
|
-
|
|
37597
|
-
|
|
38377
|
+
return cooldownFromReset({
|
|
38378
|
+
resetAtMs: field !== null ? parseTimestamp(Number(field[1])) : instant !== null ? Date.parse(instant[0].replace(" ", "T")) : null,
|
|
38379
|
+
now,
|
|
38380
|
+
fallbackMs: BILLING_CYCLE_PATTERN.test(bodyText) ? BILLING_CYCLE_COOLDOWN_MS : ACCOUNT_LIMIT_COOLDOWN_MS,
|
|
38381
|
+
minimumMs: POOL_COOLDOWN_MS$5,
|
|
38382
|
+
maximumMs: MAX_ACCOUNT_LIMIT_COOLDOWN_MS
|
|
38383
|
+
});
|
|
37598
38384
|
}
|
|
37599
38385
|
/**
|
|
37600
38386
|
* Cooldown one rate-limited account takes when the provider states no delay.
|
|
@@ -37673,122 +38459,30 @@ function toolUpdateMode$2(modelId, catalog) {
|
|
|
37673
38459
|
].includes(raw)) return void 0;
|
|
37674
38460
|
return "in-history";
|
|
37675
38461
|
}
|
|
37676
|
-
|
|
37677
|
-
|
|
37678
|
-
|
|
37679
|
-
* The leader only has to reach the response headers: past that the service is
|
|
37680
|
-
* already processing the prefix, and a follower that waited out the whole
|
|
37681
|
-
* budget would have lost more than the shared warm-up was worth.
|
|
37682
|
-
*/
|
|
37683
|
-
const COLD_START_WARMUP_WAIT_MS = 2500;
|
|
37684
|
-
/** How many prefix groups are remembered as warm. */
|
|
37685
|
-
const MAX_WARM_PREFIX_GROUPS = 256;
|
|
37686
|
-
/**
|
|
37687
|
-
* When each prefix group was last warmed.
|
|
37688
|
-
*
|
|
37689
|
-
* An entry older than the cache lifetime is treated as cold, because the
|
|
37690
|
-
* service's own entry is gone by then too. Bounded, so a long-lived process
|
|
37691
|
-
* cannot accumulate one entry per distinct prefix forever.
|
|
37692
|
-
*/
|
|
37693
|
-
const warmPrefixTimestamps = /* @__PURE__ */ new Map();
|
|
37694
|
-
/**
|
|
37695
|
-
* The request currently warming each prefix group.
|
|
37696
|
-
*
|
|
37697
|
-
* The promise is resolved when that leader reaches the response headers, which
|
|
37698
|
-
* is what releases the followers waiting behind it.
|
|
37699
|
-
*/
|
|
37700
|
-
const inFlightColdLeaders = /* @__PURE__ */ new Map();
|
|
37701
|
-
/** When each leader started, so a hung leader does not own its group forever. */
|
|
37702
|
-
const inFlightColdLeaderStartedAt = /* @__PURE__ */ new Map();
|
|
37703
|
-
/**
|
|
37704
|
-
* The resolver belonging to each in-flight leader.
|
|
37705
|
-
*
|
|
37706
|
-
* Kept beside the promise it settles, because a promise cannot be resolved
|
|
37707
|
-
* from the outside.
|
|
37708
|
-
*/
|
|
37709
|
-
const coldLeaderReleases = /* @__PURE__ */ new Map();
|
|
37710
|
-
/**
|
|
37711
|
-
* In-flight main-session requests waiting for their HTTP response headers,
|
|
37712
|
-
* keyed by sessionId. Used so a concurrent `session-title-first-prompt-llm`
|
|
37713
|
-
* side-channel request yields to the main turn's prefill instead of competing
|
|
37714
|
-
* on the same account during cold start.
|
|
37715
|
-
*/
|
|
37716
|
-
const inFlightMainSessionHeaders = /* @__PURE__ */ new Map();
|
|
37717
|
-
const mainSessionHeaderReleases = /* @__PURE__ */ new Map();
|
|
37718
|
-
/**
|
|
37719
|
-
* Per-account/model first-turn prefill pacing chain for concurrent subagent bursts.
|
|
37720
|
-
*/
|
|
37721
|
-
const inFlightSubagentBurstLeaders = /* @__PURE__ */ new Map();
|
|
37722
|
-
const subagentBurstReleases = /* @__PURE__ */ new Map();
|
|
37723
|
-
/** The gate of a request that neither waits for nor leads a cold start. */
|
|
37724
|
-
const NO_COLD_START_GATE = {
|
|
37725
|
-
isLeader: false,
|
|
37726
|
-
release: () => void 0,
|
|
37727
|
-
markWarm: () => void 0
|
|
37728
|
-
};
|
|
37729
|
-
/**
|
|
37730
|
-
* The group of requests that address the same cached prefix.
|
|
37731
|
-
*
|
|
37732
|
-
* The service derives its cache from the request content, so two requests with
|
|
37733
|
-
* the same model, the same system prompt, the same tool list and the same
|
|
37734
|
-
* leading static bootstrap reminders hit the same entry. The region is part of
|
|
37735
|
-
* the key because each region is a separate service with a cache of its own.
|
|
37736
|
-
*
|
|
37737
|
-
* A request with no system prompt has no prefix worth warming, and gets no
|
|
37738
|
-
* group at all.
|
|
37739
|
-
*
|
|
37740
|
-
* @param region - The region the request will be answered from.
|
|
37741
|
-
* @param options - The request about to be sent.
|
|
37742
|
-
* @returns The group key, or undefined when the request has no prefix.
|
|
37743
|
-
*/
|
|
37744
|
-
function coldStartGroupKey(region, options) {
|
|
38462
|
+
const prefixRequests = new PrefixRequestCoordinator({ waitMs: 2500 });
|
|
38463
|
+
/** The prefix identity shared by requests on one region/model. */
|
|
38464
|
+
function prefixGroupKey(region, options) {
|
|
37745
38465
|
const { systemPromptHash, toolsHash, staticPrefixHash } = prefixFingerprints(options);
|
|
37746
38466
|
if (systemPromptHash === null) return void 0;
|
|
37747
38467
|
return `${region}:${options.model}:${systemPromptHash}:${toolsHash ?? "none"}${staticPrefixHash ? ":" + staticPrefixHash : ""}`;
|
|
37748
38468
|
}
|
|
37749
|
-
|
|
37750
|
-
|
|
37751
|
-
|
|
37752
|
-
|
|
37753
|
-
|
|
37754
|
-
function rememberWarmPrefix(groupKey, now = Date.now()) {
|
|
37755
|
-
warmPrefixTimestamps.delete(groupKey);
|
|
37756
|
-
warmPrefixTimestamps.set(groupKey, now);
|
|
37757
|
-
while (warmPrefixTimestamps.size > MAX_WARM_PREFIX_GROUPS) {
|
|
37758
|
-
const oldest = warmPrefixTimestamps.keys().next();
|
|
37759
|
-
if (oldest.done === true) break;
|
|
37760
|
-
warmPrefixTimestamps.delete(oldest.value);
|
|
37761
|
-
}
|
|
38469
|
+
const GROUP_ACCOUNT_SEPARATOR = "|";
|
|
38470
|
+
/** The cache group on one account; cross-account warmth is never inferred. */
|
|
38471
|
+
function coldStartGroupKey(region, options, accountScope) {
|
|
38472
|
+
const prefix = prefixGroupKey(region, options);
|
|
38473
|
+
return prefix === void 0 ? void 0 : `${prefix}${GROUP_ACCOUNT_SEPARATOR}${accountScope}`;
|
|
37762
38474
|
}
|
|
37763
|
-
/**
|
|
37764
|
-
|
|
37765
|
-
|
|
38475
|
+
/** Whether this account's prefix is still warm under the measured Kimi TTL. */
|
|
38476
|
+
function isWarmPrefixGroup(region, options, accountScope, now = Date.now()) {
|
|
38477
|
+
const groupKey = coldStartGroupKey(region, options, accountScope);
|
|
38478
|
+
return groupKey !== void 0 && prefixRequests.isWarm(groupKey, 15e5, now);
|
|
38479
|
+
}
|
|
38480
|
+
/** Register a main request until response headers arrive. */
|
|
37766
38481
|
function registerInFlightMainSessionHeader(sessionId) {
|
|
37767
|
-
const
|
|
37768
|
-
|
|
37769
|
-
mainSessionHeaderReleases.get(key)?.();
|
|
37770
|
-
let resolveHeader;
|
|
37771
|
-
const promise = new Promise((resolve) => {
|
|
37772
|
-
resolveHeader = resolve;
|
|
37773
|
-
});
|
|
37774
|
-
inFlightMainSessionHeaders.set(key, promise);
|
|
37775
|
-
let released = false;
|
|
37776
|
-
const release = () => {
|
|
37777
|
-
if (released) return;
|
|
37778
|
-
released = true;
|
|
37779
|
-
resolveHeader();
|
|
37780
|
-
if (mainSessionHeaderReleases.get(key) === release) {
|
|
37781
|
-
mainSessionHeaderReleases.delete(key);
|
|
37782
|
-
inFlightMainSessionHeaders.delete(key);
|
|
37783
|
-
}
|
|
37784
|
-
};
|
|
37785
|
-
mainSessionHeaderReleases.set(key, release);
|
|
37786
|
-
return release;
|
|
38482
|
+
const lease = prefixRequests.registerMainSession(sessionId);
|
|
38483
|
+
return () => lease.release();
|
|
37787
38484
|
}
|
|
37788
|
-
/**
|
|
37789
|
-
* Whether a first-turn request carries a subagent mandate or large uncached
|
|
37790
|
-
* dynamic payload that should stagger behind an already in-flight sibling burst.
|
|
37791
|
-
*/
|
|
38485
|
+
/** Whether a first-turn request carries a mandate or a large dynamic payload. */
|
|
37792
38486
|
function isSubagentBurstCandidate(options) {
|
|
37793
38487
|
let dynamicChars = 0;
|
|
37794
38488
|
for (const message of options.messages) {
|
|
@@ -37804,113 +38498,14 @@ function isSubagentBurstCandidate(options) {
|
|
|
37804
38498
|
}
|
|
37805
38499
|
return dynamicChars >= 1200;
|
|
37806
38500
|
}
|
|
37807
|
-
/**
|
|
37808
|
-
* Stagger concurrent subagent first-turn requests on the same account/model
|
|
37809
|
-
* so they do not stampede prefill in the same millisecond.
|
|
37810
|
-
*/
|
|
38501
|
+
/** Serialize concurrent subagent first turns on one account/model. */
|
|
37811
38502
|
async function acquireSubagentBurstPacing(pacingKey, signal) {
|
|
37812
|
-
|
|
37813
|
-
|
|
37814
|
-
if (existing === void 0) break;
|
|
37815
|
-
await raceColdStartLeader(existing, signal);
|
|
37816
|
-
if (signal.aborted) return () => void 0;
|
|
37817
|
-
if (inFlightSubagentBurstLeaders.get(pacingKey) === existing) {
|
|
37818
|
-
subagentBurstReleases.get(pacingKey)?.();
|
|
37819
|
-
break;
|
|
37820
|
-
}
|
|
37821
|
-
}
|
|
37822
|
-
let resolvePacing;
|
|
37823
|
-
const promise = new Promise((resolve) => {
|
|
37824
|
-
resolvePacing = resolve;
|
|
37825
|
-
});
|
|
37826
|
-
inFlightSubagentBurstLeaders.set(pacingKey, promise);
|
|
37827
|
-
let settled = false;
|
|
37828
|
-
const release = () => {
|
|
37829
|
-
if (settled) return;
|
|
37830
|
-
settled = true;
|
|
37831
|
-
resolvePacing();
|
|
37832
|
-
if (subagentBurstReleases.get(pacingKey) === release) {
|
|
37833
|
-
subagentBurstReleases.delete(pacingKey);
|
|
37834
|
-
inFlightSubagentBurstLeaders.delete(pacingKey);
|
|
37835
|
-
}
|
|
37836
|
-
};
|
|
37837
|
-
subagentBurstReleases.set(pacingKey, release);
|
|
37838
|
-
return release;
|
|
37839
|
-
}
|
|
37840
|
-
/**
|
|
37841
|
-
* Wait for the leader of a prefix group, for the wait budget, or for an abort.
|
|
37842
|
-
*
|
|
37843
|
-
* The timer and the abort listener are removed whichever way the race ends, so
|
|
37844
|
-
* a follower that proceeds leaves neither behind.
|
|
37845
|
-
*
|
|
37846
|
-
* @param leader - The promise the in-flight leader settles.
|
|
37847
|
-
* @param signal - The caller's abort signal; an abort ends the wait.
|
|
37848
|
-
*/
|
|
37849
|
-
function raceColdStartLeader(leader, signal) {
|
|
37850
|
-
const cleanups = [];
|
|
37851
|
-
const budget = new Promise((resolve) => {
|
|
37852
|
-
const timer = setTimeout(() => resolve(), COLD_START_WARMUP_WAIT_MS);
|
|
37853
|
-
cleanups.push(() => clearTimeout(timer));
|
|
37854
|
-
});
|
|
37855
|
-
const aborted = new Promise((resolve) => {
|
|
37856
|
-
if (signal.aborted) {
|
|
37857
|
-
resolve();
|
|
37858
|
-
return;
|
|
37859
|
-
}
|
|
37860
|
-
const onAbort = () => resolve();
|
|
37861
|
-
signal.addEventListener("abort", onAbort, { once: true });
|
|
37862
|
-
cleanups.push(() => signal.removeEventListener("abort", onAbort));
|
|
37863
|
-
});
|
|
37864
|
-
return Promise.race([
|
|
37865
|
-
leader,
|
|
37866
|
-
budget,
|
|
37867
|
-
aborted
|
|
37868
|
-
]).finally(() => {
|
|
37869
|
-
for (const cleanup of cleanups) cleanup();
|
|
37870
|
-
});
|
|
38503
|
+
const lease = await prefixRequests.acquireBurst(pacingKey, signal);
|
|
38504
|
+
return () => lease.release();
|
|
37871
38505
|
}
|
|
37872
|
-
/**
|
|
37873
|
-
* Join the cold-start singleflight for one prefix group.
|
|
37874
|
-
*
|
|
37875
|
-
* A warm group returns at once. A cold group that already has a leader waits
|
|
37876
|
-
* for that leader — but never longer than {@link COLD_START_WARMUP_WAIT_MS}.
|
|
37877
|
-
* A cold group with no leader becomes the leader itself.
|
|
37878
|
-
*
|
|
37879
|
-
* @param groupKey - The prefix group this request belongs to.
|
|
37880
|
-
* @param signal - The caller's abort signal; an abort ends the wait.
|
|
37881
|
-
* @returns The gate this request holds.
|
|
37882
|
-
*/
|
|
38506
|
+
/** Join the shared cold-start coordinator with Kimi's measured warm TTL. */
|
|
37883
38507
|
async function acquireColdStartWarmup(groupKey, signal) {
|
|
37884
|
-
|
|
37885
|
-
if (warmedAt !== void 0 && Date.now() - warmedAt < 15e5) return NO_COLD_START_GATE;
|
|
37886
|
-
if (inFlightColdLeaders.get(groupKey) !== void 0) {
|
|
37887
|
-
const startedAt = inFlightColdLeaderStartedAt.get(groupKey);
|
|
37888
|
-
if (startedAt !== void 0 && Date.now() - startedAt >= 2500) coldLeaderReleases.get(groupKey)?.();
|
|
37889
|
-
}
|
|
37890
|
-
const leader = inFlightColdLeaders.get(groupKey);
|
|
37891
|
-
if (leader !== void 0) {
|
|
37892
|
-
await raceColdStartLeader(leader, signal);
|
|
37893
|
-
return NO_COLD_START_GATE;
|
|
37894
|
-
}
|
|
37895
|
-
let resolveLeader;
|
|
37896
|
-
const promise = new Promise((resolve) => {
|
|
37897
|
-
resolveLeader = resolve;
|
|
37898
|
-
});
|
|
37899
|
-
inFlightColdLeaders.set(groupKey, promise);
|
|
37900
|
-
inFlightColdLeaderStartedAt.set(groupKey, Date.now());
|
|
37901
|
-
const release = () => {
|
|
37902
|
-
resolveLeader();
|
|
37903
|
-
if (coldLeaderReleases.get(groupKey) !== release) return;
|
|
37904
|
-
coldLeaderReleases.delete(groupKey);
|
|
37905
|
-
inFlightColdLeaders.delete(groupKey);
|
|
37906
|
-
inFlightColdLeaderStartedAt.delete(groupKey);
|
|
37907
|
-
};
|
|
37908
|
-
coldLeaderReleases.set(groupKey, release);
|
|
37909
|
-
return {
|
|
37910
|
-
isLeader: true,
|
|
37911
|
-
release,
|
|
37912
|
-
markWarm: () => rememberWarmPrefix(groupKey)
|
|
37913
|
-
};
|
|
38508
|
+
return prefixRequests.acquire(groupKey, signal, CACHE_TTL_MS);
|
|
37914
38509
|
}
|
|
37915
38510
|
/** Sessions already told that their prefix broke, so the log stays readable. */
|
|
37916
38511
|
const driftWarned = /* @__PURE__ */ new Set();
|
|
@@ -37944,6 +38539,13 @@ function noteSessionCacheState(sessionId, state) {
|
|
|
37944
38539
|
const liveDropped = imageBudget?.droppedImages ?? 0;
|
|
37945
38540
|
const lastRequestImageBytes = liveBytes > 0 ? liveBytes : priorRecord?.lastRequestImageBytes;
|
|
37946
38541
|
const lastDroppedImages = liveDropped > 0 ? liveDropped : priorRecord?.lastDroppedImages;
|
|
38542
|
+
const firstRequest = stats.requests === 1 ? {
|
|
38543
|
+
firstRequestCachedTokens: state.cacheReadTokens,
|
|
38544
|
+
firstRequestPromptTokens: state.cacheReadTokens + state.inputTokens
|
|
38545
|
+
} : {
|
|
38546
|
+
...priorRecord?.firstRequestCachedTokens === void 0 ? {} : { firstRequestCachedTokens: priorRecord.firstRequestCachedTokens },
|
|
38547
|
+
...priorRecord?.firstRequestPromptTokens === void 0 ? {} : { firstRequestPromptTokens: priorRecord.firstRequestPromptTokens }
|
|
38548
|
+
};
|
|
37947
38549
|
sessionCacheState.write(sessionId, {
|
|
37948
38550
|
lastActiveAt: now,
|
|
37949
38551
|
totalTokens: state.cacheReadTokens + state.inputTokens,
|
|
@@ -37958,10 +38560,12 @@ function noteSessionCacheState(sessionId, state) {
|
|
|
37958
38560
|
...budgetDropped === void 0 ? {} : { budgetDropped },
|
|
37959
38561
|
...lastRequestImageBytes === void 0 ? {} : { lastRequestImageBytes },
|
|
37960
38562
|
...lastDroppedImages === void 0 ? {} : { lastDroppedImages },
|
|
38563
|
+
...firstRequest,
|
|
37961
38564
|
updatedAt: now
|
|
37962
38565
|
});
|
|
37963
38566
|
const warmupState = recordCacheWarmupUsage({
|
|
37964
38567
|
sessionId,
|
|
38568
|
+
provider: PROVIDER_ID$9,
|
|
37965
38569
|
inputTokens: state.inputTokens,
|
|
37966
38570
|
cacheReadTokens: state.cacheReadTokens,
|
|
37967
38571
|
outputTokens: state.outputTokens,
|
|
@@ -38078,6 +38682,70 @@ var KimiCodeAdapter = class extends LlmAdapter {
|
|
|
38078
38682
|
settings() {
|
|
38079
38683
|
return this.preferences ? Promise.resolve(this.preferences.status()) : this.modelSettings.read();
|
|
38080
38684
|
}
|
|
38685
|
+
/** Whether this deployment routes by warm prefix cache; on unless switched off. */
|
|
38686
|
+
cacheAffinityEnabled() {
|
|
38687
|
+
try {
|
|
38688
|
+
return this.options.cacheAffinity?.() !== false;
|
|
38689
|
+
} catch {
|
|
38690
|
+
return true;
|
|
38691
|
+
}
|
|
38692
|
+
}
|
|
38693
|
+
/**
|
|
38694
|
+
* Which accounts this request would rather be served by, when the prefix cache
|
|
38695
|
+
* can say something useful about it.
|
|
38696
|
+
*
|
|
38697
|
+
* The service keeps one prefix cache per account, so rotation — which knows
|
|
38698
|
+
* cooldowns, tokens and last use, never prompt content — can spend a request on
|
|
38699
|
+
* an account that has never seen this prompt while another one has it hot. Both
|
|
38700
|
+
* facts needed to do better are already in this process: the cold-start gate
|
|
38701
|
+
* records which (prefix, account) pairs are warm, and the quota cache says how
|
|
38702
|
+
* much each account has left.
|
|
38703
|
+
*
|
|
38704
|
+
* Returns undefined — no preference at all — wherever a preference would be a
|
|
38705
|
+
* guess: the switch is off, there is no pool or only one account, the pool
|
|
38706
|
+
* document cannot be read, a region cannot be resolved, or no account has this
|
|
38707
|
+
* prefix warm. The pool then applies its rotation strategy exactly as it would
|
|
38708
|
+
* have without this method.
|
|
38709
|
+
*
|
|
38710
|
+
* @param pool - The account pool serving this route, or null when none is installed.
|
|
38711
|
+
* @param options - The request about to be sent; its system prompt, tools and
|
|
38712
|
+
* static bootstrap are what decide the prefix identity.
|
|
38713
|
+
* @returns a preference for the pool, or undefined for "strategy decides".
|
|
38714
|
+
*/
|
|
38715
|
+
async cacheAffinityHint(pool, options) {
|
|
38716
|
+
if (pool === null || !this.cacheAffinityEnabled()) return void 0;
|
|
38717
|
+
let scopes;
|
|
38718
|
+
try {
|
|
38719
|
+
scopes = await pool.accountScopes();
|
|
38720
|
+
} catch {
|
|
38721
|
+
return;
|
|
38722
|
+
}
|
|
38723
|
+
if (scopes.length < 2) return void 0;
|
|
38724
|
+
let fallbackRegion = null;
|
|
38725
|
+
if (scopes.some((scope) => scope.region === null)) try {
|
|
38726
|
+
fallbackRegion = await resolveRegion();
|
|
38727
|
+
} catch {
|
|
38728
|
+
fallbackRegion = null;
|
|
38729
|
+
}
|
|
38730
|
+
const candidates = [];
|
|
38731
|
+
for (const scope of scopes) {
|
|
38732
|
+
const region = scope.region ?? fallbackRegion;
|
|
38733
|
+
if (region === null) continue;
|
|
38734
|
+
if (!isWarmPrefixGroup(region, options, scope.id)) continue;
|
|
38735
|
+
candidates.push({
|
|
38736
|
+
id: scope.id,
|
|
38737
|
+
warm: true
|
|
38738
|
+
});
|
|
38739
|
+
}
|
|
38740
|
+
if (candidates.length === 0) return void 0;
|
|
38741
|
+
const remainingShareById = {};
|
|
38742
|
+
for (const candidate of candidates) remainingShareById[candidate.id] = remainingQuotaShare(cachedQuotaFor$1(candidate.id));
|
|
38743
|
+
const preferAccountIds = orderAffinityCandidates({
|
|
38744
|
+
candidates,
|
|
38745
|
+
remainingShareById
|
|
38746
|
+
});
|
|
38747
|
+
return preferAccountIds.length === 0 ? void 0 : { preferAccountIds };
|
|
38748
|
+
}
|
|
38081
38749
|
/**
|
|
38082
38750
|
* Catalog for the picker: the live managed listing when reachable, the
|
|
38083
38751
|
* shipped fallback otherwise, narrowed by the user's enabled selection.
|
|
@@ -38182,7 +38850,7 @@ var KimiCodeAdapter = class extends LlmAdapter {
|
|
|
38182
38850
|
async *requestStream(options, signal, activity) {
|
|
38183
38851
|
const fetchFn = this.options.fetchFn ?? fetch;
|
|
38184
38852
|
const normalized = normalizeGenerateOptions(options);
|
|
38185
|
-
const sideChannel = isSideChannelTitleRequest(normalized);
|
|
38853
|
+
const sideChannel = isAuxiliaryRequest(normalized) || isSideChannelTitleRequest(normalized);
|
|
38186
38854
|
const sessionKey = typeof options.sessionId === "string" ? options.sessionId.trim() : "";
|
|
38187
38855
|
const mainHeaderRelease = !sideChannel && sessionKey !== "" ? registerInFlightMainSessionHeader(sessionKey) : () => void 0;
|
|
38188
38856
|
try {
|
|
@@ -38299,6 +38967,7 @@ var KimiCodeAdapter = class extends LlmAdapter {
|
|
|
38299
38967
|
let sentBuild;
|
|
38300
38968
|
const pool = this.accountPool;
|
|
38301
38969
|
const tried = /* @__PURE__ */ new Set();
|
|
38970
|
+
const affinity = sideChannel ? void 0 : await this.cacheAffinityHint(pool, requestOptions);
|
|
38302
38971
|
let servedByAccountId;
|
|
38303
38972
|
let response;
|
|
38304
38973
|
while (true) {
|
|
@@ -38316,7 +38985,7 @@ var KimiCodeAdapter = class extends LlmAdapter {
|
|
|
38316
38985
|
else {
|
|
38317
38986
|
let effective;
|
|
38318
38987
|
try {
|
|
38319
|
-
effective = await pool.getEffectiveCredential(tried, fetchFn, { maxAttempts: MODEL_REFRESH_MAX_ATTEMPTS });
|
|
38988
|
+
effective = await pool.getEffectiveCredential(tried, fetchFn, { maxAttempts: MODEL_REFRESH_MAX_ATTEMPTS }, affinity);
|
|
38320
38989
|
} catch (error) {
|
|
38321
38990
|
throw credentialFailure(error, signal);
|
|
38322
38991
|
}
|
|
@@ -38374,13 +39043,10 @@ var KimiCodeAdapter = class extends LlmAdapter {
|
|
|
38374
39043
|
}
|
|
38375
39044
|
};
|
|
38376
39045
|
if (sideChannel && sessionKey !== "") {
|
|
38377
|
-
|
|
38378
|
-
if (
|
|
38379
|
-
await raceColdStartLeader(pendingMain, signal);
|
|
38380
|
-
if (signal.aborted) throw new LlmError("Kimi Code request aborted", "ABORTED");
|
|
38381
|
-
}
|
|
39046
|
+
await prefixRequests.waitForMainSession(sessionKey, signal);
|
|
39047
|
+
if (signal.aborted) throw new LlmError("Kimi Code request aborted", "ABORTED");
|
|
38382
39048
|
}
|
|
38383
|
-
const coldGroupKey = sideChannel ? void 0 : coldStartGroupKey(region, requestOptions);
|
|
39049
|
+
const coldGroupKey = sideChannel ? void 0 : coldStartGroupKey(region, requestOptions, accountScope);
|
|
38384
39050
|
const coldGate = coldGroupKey === void 0 ? void 0 : await acquireColdStartWarmup(coldGroupKey, signal);
|
|
38385
39051
|
const burstPacingRelease = !sideChannel && sessionKey !== "" && getCacheStats(sessionKey).requests === 0 && isSubagentBurstCandidate(requestOptions) ? await acquireSubagentBurstPacing(`${accountScope}:${options.model}`, signal) : () => void 0;
|
|
38386
39052
|
let coldGateSettled = false;
|
|
@@ -38857,9 +39523,28 @@ var KimiCodeAccountPool = class extends AccountPoolCore {
|
|
|
38857
39523
|
*
|
|
38858
39524
|
* The returned credential carries its own region, OAuth host and base URL, so
|
|
38859
39525
|
* the caller must use those rather than the process-wide defaults.
|
|
39526
|
+
*
|
|
39527
|
+
* @param selection - this request's own preference, used by the cache-affinity
|
|
39528
|
+
* routing to put an account whose prefix cache is still warm first. Absent
|
|
39529
|
+
* means the rotation strategy decides alone.
|
|
39530
|
+
*/
|
|
39531
|
+
async getEffectiveCredential(excludeIds, fetchFn = fetch, options, selection) {
|
|
39532
|
+
return this.getEffectiveAccount(excludeIds, fetchFn, false, options, selection);
|
|
39533
|
+
}
|
|
39534
|
+
/**
|
|
39535
|
+
* Every pooled account's id with the region that decides its prefix group.
|
|
39536
|
+
*
|
|
39537
|
+
* The cache-affinity routing needs exactly two facts per account — which one
|
|
39538
|
+
* it is, and which regional service answers it, because each region keeps its
|
|
39539
|
+
* own prefix cache — and nothing else. Deliberately a narrow projection over
|
|
39540
|
+
* the pool document rather than a credential read: tokens never leave the
|
|
39541
|
+
* pool, and nothing here can be mistaken for a credential accessor.
|
|
38860
39542
|
*/
|
|
38861
|
-
async
|
|
38862
|
-
return this.
|
|
39543
|
+
async accountScopes() {
|
|
39544
|
+
return (await this.read()).accounts.map((account) => ({
|
|
39545
|
+
id: account.id,
|
|
39546
|
+
region: account.region ?? account.credentials.region ?? null
|
|
39547
|
+
}));
|
|
38863
39548
|
}
|
|
38864
39549
|
/** The pre-pool credential file this pool mirrors its primary account into. */
|
|
38865
39550
|
mirrorStore() {
|
|
@@ -39051,15 +39736,37 @@ function warmupStatusOf(sessionId) {
|
|
|
39051
39736
|
requiredHits: CACHE_WARMUP_DEFAULTS.consecutiveHitsRequired
|
|
39052
39737
|
};
|
|
39053
39738
|
}
|
|
39739
|
+
/**
|
|
39740
|
+
* How much of this conversation's first prompt was served from cache.
|
|
39741
|
+
*
|
|
39742
|
+
* Read from the durable record on BOTH paths, because the in-memory totals carry
|
|
39743
|
+
* rolling sums only: the first request's own numbers exist exactly once, at the
|
|
39744
|
+
* turn that closed it, and the record is where the adapter filed them. A record
|
|
39745
|
+
* written before this field existed simply has nothing to say, which is the
|
|
39746
|
+
* honest answer for a conversation that started under an older build.
|
|
39747
|
+
*/
|
|
39748
|
+
function openingInheritanceOf(sessionId) {
|
|
39749
|
+
const durable = sessionCacheState.read(sessionId);
|
|
39750
|
+
const cachedTokens = durable?.firstRequestCachedTokens;
|
|
39751
|
+
const promptTokens = durable?.firstRequestPromptTokens;
|
|
39752
|
+
if (cachedTokens === void 0 || promptTokens === void 0 || promptTokens <= 0) return void 0;
|
|
39753
|
+
return {
|
|
39754
|
+
cachedTokens,
|
|
39755
|
+
promptTokens,
|
|
39756
|
+
hitRatio: Math.min(1, Math.max(0, cachedTokens / promptTokens))
|
|
39757
|
+
};
|
|
39758
|
+
}
|
|
39054
39759
|
function cacheStatsOrNull(sessionId) {
|
|
39055
39760
|
const stats = getCacheStats(sessionId);
|
|
39056
39761
|
if (stats.requests === 0) return durableCacheStats(sessionId);
|
|
39057
39762
|
const lastDrift = getLastDriftCause(sessionId);
|
|
39058
39763
|
const warmup = warmupStatusOf(sessionId);
|
|
39764
|
+
const opening = openingInheritanceOf(sessionId);
|
|
39059
39765
|
return {
|
|
39060
39766
|
...stats,
|
|
39061
39767
|
...lastDrift === void 0 ? {} : { lastDrift },
|
|
39062
39768
|
...warmup === void 0 ? {} : { warmup },
|
|
39769
|
+
...opening === void 0 ? {} : { opening },
|
|
39063
39770
|
imageBudget: imageBudgetOf(sessionId),
|
|
39064
39771
|
cacheWriteTokens: stats.cacheWriteTokens,
|
|
39065
39772
|
cacheWrite5mTokens: stats.cacheWrite5mTokens,
|
|
@@ -39103,6 +39810,7 @@ function durableCacheStats(sessionId) {
|
|
|
39103
39810
|
if (durable === void 0 || durable.requests === 0) return null;
|
|
39104
39811
|
const lastDrift = driftCauseOf(durable.lastDrift);
|
|
39105
39812
|
const warmup = warmupStatusOf(sessionId);
|
|
39813
|
+
const opening = openingInheritanceOf(sessionId);
|
|
39106
39814
|
return {
|
|
39107
39815
|
requests: durable.requests,
|
|
39108
39816
|
cachedTokens: durable.cachedTokens,
|
|
@@ -39114,6 +39822,7 @@ function durableCacheStats(sessionId) {
|
|
|
39114
39822
|
cacheWrite1hTokens: durable.cacheWrite1hTokens,
|
|
39115
39823
|
...lastDrift === void 0 ? {} : { lastDrift },
|
|
39116
39824
|
...warmup === void 0 ? {} : { warmup },
|
|
39825
|
+
...opening === void 0 ? {} : { opening },
|
|
39117
39826
|
imageBudget: imageBudgetOf(sessionId)
|
|
39118
39827
|
};
|
|
39119
39828
|
}
|
|
@@ -40391,7 +41100,7 @@ const MINIMAX_CODE_PROVIDER$1 = "minimax-code";
|
|
|
40391
41100
|
* @param value - 任意值。
|
|
40392
41101
|
* @returns 是普通对象时为 true。
|
|
40393
41102
|
*/
|
|
40394
|
-
function isPlainRecord
|
|
41103
|
+
function isPlainRecord(value) {
|
|
40395
41104
|
if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
|
|
40396
41105
|
const prototype = Object.getPrototypeOf(value);
|
|
40397
41106
|
return prototype === Object.prototype || prototype === null;
|
|
@@ -40511,7 +41220,7 @@ function coerceBySchema(value, schema, fixes) {
|
|
|
40511
41220
|
const type = schema["type"];
|
|
40512
41221
|
if (type === "array") {
|
|
40513
41222
|
let current = value;
|
|
40514
|
-
if (isPlainRecord
|
|
41223
|
+
if (isPlainRecord(current) && Object.keys(current).length === 1 && "item" in current) {
|
|
40515
41224
|
const inner = current["item"];
|
|
40516
41225
|
current = Array.isArray(inner) ? [...inner] : [inner];
|
|
40517
41226
|
fixes.push("item_wrapper");
|
|
@@ -40543,7 +41252,7 @@ function coerceBySchema(value, schema, fixes) {
|
|
|
40543
41252
|
}
|
|
40544
41253
|
if (type === "object") {
|
|
40545
41254
|
const properties = schema["properties"];
|
|
40546
|
-
if (!isPlainRecord
|
|
41255
|
+
if (!isPlainRecord(value) || !isRecord$50(properties)) return value;
|
|
40547
41256
|
const next = { ...value };
|
|
40548
41257
|
for (const [key, subSchema] of Object.entries(properties)) {
|
|
40549
41258
|
if (!(key in next)) continue;
|
|
@@ -40552,7 +41261,7 @@ function coerceBySchema(value, schema, fixes) {
|
|
|
40552
41261
|
return next;
|
|
40553
41262
|
}
|
|
40554
41263
|
const properties = schema["properties"];
|
|
40555
|
-
if (isPlainRecord
|
|
41264
|
+
if (isPlainRecord(value) && isRecord$50(properties)) {
|
|
40556
41265
|
const next = { ...value };
|
|
40557
41266
|
for (const [key, subSchema] of Object.entries(properties)) {
|
|
40558
41267
|
if (!(key in next)) continue;
|
|
@@ -40612,7 +41321,7 @@ function coerceByKnownFields(args, fixes) {
|
|
|
40612
41321
|
* @returns 修复后的参数与修复项列表。
|
|
40613
41322
|
*/
|
|
40614
41323
|
function repairMinimaxToolArguments(toolName, args, schema) {
|
|
40615
|
-
if (!isPlainRecord
|
|
41324
|
+
if (!isPlainRecord(args)) return {
|
|
40616
41325
|
value: args,
|
|
40617
41326
|
fixes: []
|
|
40618
41327
|
};
|
|
@@ -40627,7 +41336,7 @@ function repairMinimaxToolArguments(toolName, args, schema) {
|
|
|
40627
41336
|
}
|
|
40628
41337
|
if (schema !== void 0) {
|
|
40629
41338
|
const coerced = coerceBySchema(working, schema, fixes);
|
|
40630
|
-
if (isPlainRecord
|
|
41339
|
+
if (isPlainRecord(coerced)) Object.assign(working, coerced);
|
|
40631
41340
|
} else coerceByKnownFields(working, fixes);
|
|
40632
41341
|
return {
|
|
40633
41342
|
value: working,
|
|
@@ -40773,28 +41482,6 @@ function errorFamilyOf(text) {
|
|
|
40773
41482
|
return (text.split(/\r?\n/, 1)[0] ?? "").replace(/\d+/g, "#").slice(0, 200);
|
|
40774
41483
|
}
|
|
40775
41484
|
/**
|
|
40776
|
-
* 把参数规范化成可比较的字符串(键排序)。
|
|
40777
|
-
* @param value - 参数值。
|
|
40778
|
-
* @returns 规范化后的 JSON 文本。
|
|
40779
|
-
*/
|
|
40780
|
-
function canonicalize(value) {
|
|
40781
|
-
return JSON.stringify(sortJsonValue$1(value));
|
|
40782
|
-
}
|
|
40783
|
-
/**
|
|
40784
|
-
* 深拷贝一份键排序后的 JSON 值。
|
|
40785
|
-
* @param value - 任意值。
|
|
40786
|
-
* @returns 排序后的副本。
|
|
40787
|
-
*/
|
|
40788
|
-
function sortJsonValue$1(value) {
|
|
40789
|
-
if (Array.isArray(value)) return value.map(sortJsonValue$1);
|
|
40790
|
-
if (isPlainRecord$1(value)) {
|
|
40791
|
-
const sorted = {};
|
|
40792
|
-
for (const key of Object.keys(value).sort()) sorted[key] = sortJsonValue$1(value[key]);
|
|
40793
|
-
return sorted;
|
|
40794
|
-
}
|
|
40795
|
-
return value;
|
|
40796
|
-
}
|
|
40797
|
-
/**
|
|
40798
41485
|
* 造一台工具级失控守卫。
|
|
40799
41486
|
*
|
|
40800
41487
|
* 三种可提醒信号(顺序即官方提醒优先级):
|
|
@@ -40813,8 +41500,8 @@ function sortJsonValue$1(value) {
|
|
|
40813
41500
|
*/
|
|
40814
41501
|
function createMinimaxRunawayGuard(options = {}) {
|
|
40815
41502
|
const threshold = options.remindAfterOccurrences ?? 3;
|
|
40816
|
-
/**
|
|
40817
|
-
|
|
41503
|
+
/** 同一次动作的连击,由所有线路共享的规范键与跟踪器计算。 */
|
|
41504
|
+
const exactActionRepeats = createExactRepeatTracker();
|
|
40818
41505
|
/** 同一族错误的连击(键是工具名 + 错误族)。 */
|
|
40819
41506
|
const errorStreaks = /* @__PURE__ */ new Map();
|
|
40820
41507
|
/** 轮询连击(键是工具名)。 */
|
|
@@ -40847,23 +41534,16 @@ function createMinimaxRunawayGuard(options = {}) {
|
|
|
40847
41534
|
*/
|
|
40848
41535
|
const isPolling = (observation) => {
|
|
40849
41536
|
if (!POLLING_TOOL_NAMES.includes(observation.toolName)) return false;
|
|
40850
|
-
return (isPlainRecord
|
|
41537
|
+
return (isPlainRecord(observation.arguments) ? observation.arguments : void 0)?.["wait"] !== true;
|
|
40851
41538
|
};
|
|
40852
41539
|
return {
|
|
40853
41540
|
observe(observation) {
|
|
40854
41541
|
const toolName = observation.toolName;
|
|
40855
|
-
const actionKey =
|
|
41542
|
+
const actionKey = canonicalToolCallKey(toolName, observation.arguments);
|
|
40856
41543
|
const countsAsAction = !POLLING_TOOL_NAMES.includes(toolName);
|
|
40857
41544
|
if (countsAsAction) {
|
|
40858
|
-
|
|
40859
|
-
|
|
40860
|
-
count: actionStreak.count + 1
|
|
40861
|
-
};
|
|
40862
|
-
else actionStreak = {
|
|
40863
|
-
key: actionKey,
|
|
40864
|
-
count: 1
|
|
40865
|
-
};
|
|
40866
|
-
record("exact_action_repeat", actionStreak.count, toolName);
|
|
41545
|
+
const action = exactActionRepeats.observe(toolName, observation.arguments);
|
|
41546
|
+
record("exact_action_repeat", action.count, toolName);
|
|
40867
41547
|
}
|
|
40868
41548
|
if (countsAsAction) {
|
|
40869
41549
|
recentActions.push(actionKey);
|
|
@@ -40890,7 +41570,7 @@ function createMinimaxRunawayGuard(options = {}) {
|
|
|
40890
41570
|
record("same_error_family", count, toolName);
|
|
40891
41571
|
} else for (const [key, entry] of [...errorStreaks]) if (entry.toolName === toolName) errorStreaks.delete(key);
|
|
40892
41572
|
if (isPolling(observation)) {
|
|
40893
|
-
const key = `${toolName
|
|
41573
|
+
const key = `${canonicalToolCallKey(toolName, observation.arguments)}\u0000${observation.resultText ?? ""}`;
|
|
40894
41574
|
const previous = pollingStreaks.get(toolName);
|
|
40895
41575
|
const count = previous !== void 0 && previous.key === key ? previous.count + 1 : 1;
|
|
40896
41576
|
pollingStreaks.set(toolName, {
|
|
@@ -40909,6 +41589,7 @@ function createMinimaxRunawayGuard(options = {}) {
|
|
|
40909
41589
|
text: renderSameErrorFamilyReminder(errorCandidate.count)
|
|
40910
41590
|
};
|
|
40911
41591
|
}
|
|
41592
|
+
const actionStreak = exactActionRepeats.current();
|
|
40912
41593
|
if (countsAsAction && actionStreak !== void 0 && actionStreak.count >= threshold) {
|
|
40913
41594
|
reminderAttempted = true;
|
|
40914
41595
|
return {
|
|
@@ -40928,7 +41609,7 @@ function createMinimaxRunawayGuard(options = {}) {
|
|
|
40928
41609
|
}
|
|
40929
41610
|
},
|
|
40930
41611
|
reset() {
|
|
40931
|
-
|
|
41612
|
+
exactActionRepeats.reset();
|
|
40932
41613
|
errorStreaks.clear();
|
|
40933
41614
|
pollingStreaks.clear();
|
|
40934
41615
|
recentActions.length = 0;
|
|
@@ -41198,14 +41879,14 @@ function schemaOf(tools, name, agent) {
|
|
|
41198
41879
|
function repairExecutionArguments(execution, tools) {
|
|
41199
41880
|
const record = isRecord$50(execution) ? execution : void 0;
|
|
41200
41881
|
const args = record?.["arguments"];
|
|
41201
|
-
if (!isPlainRecord
|
|
41882
|
+
if (!isPlainRecord(args)) return;
|
|
41202
41883
|
const name = stringOf$4(record?.["name"]);
|
|
41203
41884
|
if (name === void 0) return;
|
|
41204
41885
|
const agent = record?.["agent"];
|
|
41205
41886
|
const repaired = repairMinimaxToolArguments(name, args, schemaOf(tools, name, agent));
|
|
41206
41887
|
if (repaired.fixes.length === 0) return;
|
|
41207
41888
|
const target = repaired.value;
|
|
41208
|
-
if (!isPlainRecord
|
|
41889
|
+
if (!isPlainRecord(target)) return;
|
|
41209
41890
|
try {
|
|
41210
41891
|
for (const key of Object.keys(args)) if (!(key in target)) delete args[key];
|
|
41211
41892
|
Object.assign(args, target);
|
|
@@ -44699,6 +45380,62 @@ var FileIdStore = class FileIdStore {
|
|
|
44699
45380
|
/** Shared by every in-memory instance, so they see one cache. */
|
|
44700
45381
|
static memory;
|
|
44701
45382
|
};
|
|
45383
|
+
/** Durable MiniMax cache adapted to the shared upload singleflight core. */
|
|
45384
|
+
var MiniMaxHostedFileCache = class {
|
|
45385
|
+
store;
|
|
45386
|
+
now;
|
|
45387
|
+
constructor(options) {
|
|
45388
|
+
this.store = new FileIdStore(options.storePath);
|
|
45389
|
+
this.now = options.nowMs ?? Date.now;
|
|
45390
|
+
}
|
|
45391
|
+
async get(key) {
|
|
45392
|
+
const cached = (await this.store.read().catch(() => ({})))[key];
|
|
45393
|
+
return cached !== void 0 && cached.expiresAtMs > this.now() ? cached.fileId : void 0;
|
|
45394
|
+
}
|
|
45395
|
+
async set(key, fileId) {
|
|
45396
|
+
const cache = await this.store.read().catch(() => ({}));
|
|
45397
|
+
await this.store.write({
|
|
45398
|
+
...cache,
|
|
45399
|
+
[key]: {
|
|
45400
|
+
fileId,
|
|
45401
|
+
expiresAtMs: this.now() + DEFAULT_FILE_ID_TTL_SEC * 1e3
|
|
45402
|
+
}
|
|
45403
|
+
}).catch(() => void 0);
|
|
45404
|
+
}
|
|
45405
|
+
async delete(key) {
|
|
45406
|
+
const cache = await this.store.read().catch(() => ({}));
|
|
45407
|
+
const existed = cache[key] !== void 0;
|
|
45408
|
+
if (existed) {
|
|
45409
|
+
delete cache[key];
|
|
45410
|
+
await this.store.write(cache).catch(() => void 0);
|
|
45411
|
+
}
|
|
45412
|
+
return existed;
|
|
45413
|
+
}
|
|
45414
|
+
async clear() {
|
|
45415
|
+
await this.store.write({}).catch(() => void 0);
|
|
45416
|
+
}
|
|
45417
|
+
};
|
|
45418
|
+
const UPLOAD_COORDINATORS = /* @__PURE__ */ new Map();
|
|
45419
|
+
const CLOCK_IDS = /* @__PURE__ */ new WeakMap();
|
|
45420
|
+
let clockSequence = 0;
|
|
45421
|
+
function uploadCoordinator(options) {
|
|
45422
|
+
let clockKey = "";
|
|
45423
|
+
if (options.nowMs !== void 0) {
|
|
45424
|
+
let id = CLOCK_IDS.get(options.nowMs);
|
|
45425
|
+
if (id === void 0) {
|
|
45426
|
+
id = ++clockSequence;
|
|
45427
|
+
CLOCK_IDS.set(options.nowMs, id);
|
|
45428
|
+
}
|
|
45429
|
+
clockKey = ":clock:" + String(id);
|
|
45430
|
+
}
|
|
45431
|
+
const key = (options.storePath ?? "<memory>") + clockKey;
|
|
45432
|
+
let coordinator = UPLOAD_COORDINATORS.get(key);
|
|
45433
|
+
if (coordinator === void 0) {
|
|
45434
|
+
coordinator = new HostedMediaUploadCoordinator({ cache: new MiniMaxHostedFileCache(options) });
|
|
45435
|
+
UPLOAD_COORDINATORS.set(key, coordinator);
|
|
45436
|
+
}
|
|
45437
|
+
return coordinator;
|
|
45438
|
+
}
|
|
44702
45439
|
function isRecord$47(value) {
|
|
44703
45440
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
44704
45441
|
}
|
|
@@ -44759,25 +45496,19 @@ function filesPurposeFor(mediaType) {
|
|
|
44759
45496
|
* inline instead (too large, unconfigured, or the upload failed).
|
|
44760
45497
|
*/
|
|
44761
45498
|
async function uploadMediaFile(request, accountKey, options = {}) {
|
|
44762
|
-
|
|
44763
|
-
if (purpose === void 0) return void 0;
|
|
45499
|
+
if (filesPurposeFor(request.mediaType) === void 0) return void 0;
|
|
44764
45500
|
if (request.bytes > 52428800) throw new FilesApiError("too-large", "the file is " + formatBytes(request.bytes) + ", above the " + formatBytes(MAX_UPLOAD_BYTES) + " upload ceiling");
|
|
44765
45501
|
const key = cacheKey(request.base64, accountKey);
|
|
44766
|
-
|
|
44767
|
-
|
|
44768
|
-
|
|
44769
|
-
|
|
44770
|
-
|
|
45502
|
+
return uploadCoordinator(options).upload(key, request.signal, (controller) => performMediaUpload(request, options, controller));
|
|
45503
|
+
}
|
|
45504
|
+
/** One shared transport flight; cache reads/writes live in MiniMaxHostedFileCache. */
|
|
45505
|
+
async function performMediaUpload(request, options, controller) {
|
|
45506
|
+
const purpose = filesPurposeFor(request.mediaType);
|
|
44771
45507
|
const fetchFn = options.fetchFn ?? fetch;
|
|
44772
45508
|
const form = new FormData();
|
|
44773
45509
|
form.append("purpose", purpose);
|
|
44774
45510
|
form.append("file", new Blob([Buffer.from(request.base64, "base64")], { type: request.mediaType }), filenameForMediaType(request.mediaType));
|
|
44775
|
-
const controller = new AbortController();
|
|
44776
45511
|
const timer = setTimeout(() => controller.abort(), UPLOAD_TIMEOUT_MS);
|
|
44777
|
-
const abortForTurn = () => controller.abort(request.signal?.reason);
|
|
44778
|
-
if (request.signal?.aborted === true) abortForTurn();
|
|
44779
|
-
else request.signal?.addEventListener("abort", abortForTurn, { once: true });
|
|
44780
|
-
let fileId;
|
|
44781
45512
|
try {
|
|
44782
45513
|
const response = await fetchFn(filesUploadUrl(request.baseUrl), {
|
|
44783
45514
|
method: "POST",
|
|
@@ -44790,24 +45521,16 @@ async function uploadMediaFile(request, accountKey, options = {}) {
|
|
|
44790
45521
|
if (!response.ok) throw new FilesApiError("rejected", "the Files API upload failed with HTTP " + response.status);
|
|
44791
45522
|
const raw = await response.text().catch(() => "");
|
|
44792
45523
|
const payload = safeJson(raw);
|
|
44793
|
-
fileId = readFileId(payload);
|
|
45524
|
+
const fileId = readFileId(payload);
|
|
44794
45525
|
if (fileId === void 0) throw new FilesApiError("rejected", describeRejection(payload, raw));
|
|
45526
|
+
return fileId;
|
|
44795
45527
|
} catch (error) {
|
|
44796
45528
|
if (error instanceof FilesApiError) throw error;
|
|
44797
|
-
if (isAbort$1(error,
|
|
45529
|
+
if (isAbort$1(error, controller.signal)) throw error;
|
|
44798
45530
|
throw new FilesApiError("network", "the Files API upload did not complete: " + (error instanceof Error ? error.message : String(error)));
|
|
44799
45531
|
} finally {
|
|
44800
45532
|
clearTimeout(timer);
|
|
44801
|
-
request.signal?.removeEventListener("abort", abortForTurn);
|
|
44802
45533
|
}
|
|
44803
|
-
await store.write({
|
|
44804
|
-
...cache,
|
|
44805
|
-
[key]: {
|
|
44806
|
-
fileId,
|
|
44807
|
-
expiresAtMs: nowMs() + DEFAULT_FILE_ID_TTL_SEC * 1e3
|
|
44808
|
-
}
|
|
44809
|
-
}).catch(() => void 0);
|
|
44810
|
-
return fileId;
|
|
44811
45534
|
}
|
|
44812
45535
|
/** Whether one failure is a cancellation rather than a real upload failure. */
|
|
44813
45536
|
function isAbort$1(error, signal) {
|
|
@@ -44848,6 +45571,50 @@ function formatBytes(bytes) {
|
|
|
44848
45571
|
return (bytes / (1024 * 1024)).toFixed(1) + " MB";
|
|
44849
45572
|
}
|
|
44850
45573
|
//#endregion
|
|
45574
|
+
//#region src/host/common/sticky-image-budget.ts
|
|
45575
|
+
/** Durable dropped-image identities for a provider's sticky budget. */
|
|
45576
|
+
function normalizeRecord$1(value) {
|
|
45577
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
|
|
45578
|
+
const record = value;
|
|
45579
|
+
const updatedAt = record.updatedAt;
|
|
45580
|
+
if (typeof updatedAt !== "number" || !Number.isFinite(updatedAt)) return void 0;
|
|
45581
|
+
const keys = Array.isArray(record.budgetDropped) ? record.budgetDropped.filter((key) => typeof key === "string" && key !== "").slice(0, 512) : [];
|
|
45582
|
+
return {
|
|
45583
|
+
updatedAt,
|
|
45584
|
+
budgetDropped: [...new Set(keys)]
|
|
45585
|
+
};
|
|
45586
|
+
}
|
|
45587
|
+
function persistenceEnabled$1() {
|
|
45588
|
+
return process.env.VITEST !== "true" && process.env.NODE_ENV !== "test";
|
|
45589
|
+
}
|
|
45590
|
+
var StickyImageBudgetStore = class {
|
|
45591
|
+
store;
|
|
45592
|
+
constructor(provider, filePath) {
|
|
45593
|
+
const target = filePath ?? path.join(dshHomeDir(), "storages", `dsh-chatgpt-subscription-${provider}-cache.json`);
|
|
45594
|
+
this.store = new CommonSessionCacheStateStore({
|
|
45595
|
+
filePath: target,
|
|
45596
|
+
allowWrite: filePath !== void 0 || persistenceEnabled$1(),
|
|
45597
|
+
normalizeRecord: normalizeRecord$1
|
|
45598
|
+
});
|
|
45599
|
+
}
|
|
45600
|
+
/** Identities already omitted in this session, including before a restart. */
|
|
45601
|
+
priorDroppedKeys(sessionId) {
|
|
45602
|
+
if (sessionId === void 0 || sessionId.trim() === "") return /* @__PURE__ */ new Set();
|
|
45603
|
+
return new Set(this.store.read(sessionId)?.budgetDropped ?? []);
|
|
45604
|
+
}
|
|
45605
|
+
/** Persist the complete set, not just the newest omissions. */
|
|
45606
|
+
record(sessionId, keys) {
|
|
45607
|
+
if (sessionId === void 0 || sessionId.trim() === "") return;
|
|
45608
|
+
this.store.write(sessionId, {
|
|
45609
|
+
updatedAt: Date.now(),
|
|
45610
|
+
budgetDropped: [...keys].slice(0, 512)
|
|
45611
|
+
});
|
|
45612
|
+
}
|
|
45613
|
+
flush() {
|
|
45614
|
+
this.store.flush();
|
|
45615
|
+
}
|
|
45616
|
+
};
|
|
45617
|
+
//#endregion
|
|
44851
45618
|
//#region src/host/minimax-code/adapter.ts
|
|
44852
45619
|
/**
|
|
44853
45620
|
* The DSH adapter for the MiniMax Code subscription.
|
|
@@ -45150,6 +45917,8 @@ function classifyMinimaxFailure(status, bodyText) {
|
|
|
45150
45917
|
message: "MiniMax Code(编程订阅) API error (" + status + "): " + (detail === "" ? "No response" : detail)
|
|
45151
45918
|
};
|
|
45152
45919
|
}
|
|
45920
|
+
const minimaxPrefixRequests = new PrefixRequestCoordinator();
|
|
45921
|
+
const minimaxStickyImages = new StickyImageBudgetStore("minimax-code");
|
|
45153
45922
|
/** The reasoning levels one model advertises, from the hardcoded directory. */
|
|
45154
45923
|
function effortsForModel(modelId) {
|
|
45155
45924
|
return [...minimaxCodeModelDef(modelId)?.reasoningEfforts ?? []];
|
|
@@ -45517,7 +46286,11 @@ var MinimaxCodeAdapter = class extends LlmAdapter {
|
|
|
45517
46286
|
const settings = await this.settings();
|
|
45518
46287
|
minimaxCodeModelDef(options.model);
|
|
45519
46288
|
const contextWindow = contextWindowForModel(options.model, settings.contextWindowOverrides);
|
|
45520
|
-
const
|
|
46289
|
+
const normalized = withStaticPrefixOrdering("minimax-code", normalizeGenerateOptions(options), this.options.promptCache);
|
|
46290
|
+
const imageBudget = maxRequestImageBytes();
|
|
46291
|
+
const sticky = promptCachePolicyOf("minimax-code", this.options.promptCache).stickyImages ? offloadRequestImagesWithStickyBudget(normalized, imageBudget, Math.max(1, Math.floor(imageBudget / 2)), void 0, minimaxStickyImages.priorDroppedKeys(normalized.sessionId)) : void 0;
|
|
46292
|
+
const requestOptions = offloadOldestRequestVideos(sticky?.options ?? offloadOldestRequestImages$2(normalized, imageBudget), MAX_REQUEST_VIDEO_BYTES);
|
|
46293
|
+
if (sticky !== void 0 && sticky.newlyDroppedKeys.length > 0) minimaxStickyImages.record(normalized.sessionId, sticky.allDroppedKeys);
|
|
45521
46294
|
const [images, videos] = await Promise.all([resolveRequestImages$2(requestOptions, this.options.attachments, signal), resolveRequestVideos(requestOptions, this.options.videos, signal)]);
|
|
45522
46295
|
const effectiveMax = clampOutputToContext(floorForcedThinkingTokens(options.model, requestOptions.maxTokens) ?? maxOutputTokensFor$1(options.model, contextWindow), contextWindow, estimatedInputTokens(requestOptions));
|
|
45523
46296
|
const tried = /* @__PURE__ */ new Set();
|
|
@@ -45590,7 +46363,9 @@ var MinimaxCodeAdapter = class extends LlmAdapter {
|
|
|
45590
46363
|
let refusedBody;
|
|
45591
46364
|
for (let attempt = 0;; attempt += 1) {
|
|
45592
46365
|
try {
|
|
45593
|
-
|
|
46366
|
+
const cachePolicy = promptCachePolicyOf("minimax-code", this.options.promptCache);
|
|
46367
|
+
const groupKey = cachePolicy.coldStart === "off" ? void 0 : sharedPrefixGroupKey("minimax-code", requestOptions, credentials.region, accountId ?? "legacy");
|
|
46368
|
+
response = await fetchWithPrefixCoordination(fetchWithContextBudgetRecovery(fetchFn), minimaxPrefixRequests, groupKey, cachePolicy.coldStart === "warm" ? cachePolicy.ttlMs : void 0, messagesUrl(credentials.region), {
|
|
45594
46369
|
method: "POST",
|
|
45595
46370
|
headers: modelRequestHeaders(credentials.accessToken, requestContext),
|
|
45596
46371
|
body,
|
|
@@ -48681,34 +49456,6 @@ function makeReminderText2(repeatCount) {
|
|
|
48681
49456
|
const REMINDER_TEXT_3 = "<system-reminder>\nWrite your final response now, without any further tool calls. Cover: the current blocker, each approach you have tried and what it established, and the specific information or decision you need from the user to unblock progress. Text only.\n</system-reminder>";
|
|
48682
49457
|
/** 强停那一轮之后,守卫用来拒绝后续工具调用的文案。 */
|
|
48683
49458
|
const HANDOFF_VETO_TEXT = "This turn was ended by the repeat breaker after the same tool call was issued " + String(12) + " times in a row. This step accepts a text response only, so the tool call was not executed. Reply in text: the current blocker, what you tried, and what you need next.";
|
|
48684
|
-
/**
|
|
48685
|
-
* 不计入连击的协调类工具。
|
|
48686
|
-
*
|
|
48687
|
-
* 这些工具**本来就会**被反复调用:报一次进展、更新一次待办、问一次用户。把它们
|
|
48688
|
-
* 算进连击等于让刹车对着正常的心跳乱响。
|
|
48689
|
-
*/
|
|
48690
|
-
const REPEAT_BREAKER_EXEMPT_TOOLS = Object.freeze([
|
|
48691
|
-
"progress_update",
|
|
48692
|
-
"todo_write",
|
|
48693
|
-
"ask_user_question",
|
|
48694
|
-
"enter_plan_mode",
|
|
48695
|
-
"exit_plan_mode",
|
|
48696
|
-
"leave_plan_mode",
|
|
48697
|
-
"create_goal",
|
|
48698
|
-
"update_goal",
|
|
48699
|
-
"get_goal",
|
|
48700
|
-
"dispatch_ledger",
|
|
48701
|
-
"present"
|
|
48702
|
-
]);
|
|
48703
|
-
/**
|
|
48704
|
-
* 判断一个工具是不是不计入连击的协调类工具。
|
|
48705
|
-
*
|
|
48706
|
-
* @param toolName - 工具名。
|
|
48707
|
-
* @returns 豁免时为 true。
|
|
48708
|
-
*/
|
|
48709
|
-
function isRepeatBreakerExemptTool(toolName) {
|
|
48710
|
-
return REPEAT_BREAKER_EXEMPT_TOOLS.includes(toolName);
|
|
48711
|
-
}
|
|
48712
49459
|
/** 明确表示「关掉」的环境变量取值。 */
|
|
48713
49460
|
const DISABLED_ENV_VALUES = /* @__PURE__ */ new Set([
|
|
48714
49461
|
"0",
|
|
@@ -48731,17 +49478,6 @@ function isKimiRepeatBreakerEnabled(env = process.env) {
|
|
|
48731
49478
|
return !DISABLED_ENV_VALUES.has(raw.trim().toLowerCase());
|
|
48732
49479
|
}
|
|
48733
49480
|
/**
|
|
48734
|
-
* 判断一个值是不是普通对象(原型是 Object.prototype 或 null)。
|
|
48735
|
-
*
|
|
48736
|
-
* @param value - 任意值。
|
|
48737
|
-
* @returns 是普通对象时为 true。
|
|
48738
|
-
*/
|
|
48739
|
-
function isPlainRecord(value) {
|
|
48740
|
-
if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
|
|
48741
|
-
const prototype = Object.getPrototypeOf(value);
|
|
48742
|
-
return prototype === Object.prototype || prototype === null;
|
|
48743
|
-
}
|
|
48744
|
-
/**
|
|
48745
49481
|
* 判断一个值是不是对象(数组也算)。
|
|
48746
49482
|
*
|
|
48747
49483
|
* @param value - 任意值。
|
|
@@ -48796,42 +49532,6 @@ function kimiSessionRouteOf(agent) {
|
|
|
48796
49532
|
};
|
|
48797
49533
|
}
|
|
48798
49534
|
/**
|
|
48799
|
-
* 递归把一个值重排成「键名有序」的形状。
|
|
48800
|
-
*
|
|
48801
|
-
* 数组**保持原序**:数组元素的顺序本身就是语义([1, 2] 与 [2, 1] 不是同一件事),
|
|
48802
|
-
* 只有对象的键名顺序没有语义。
|
|
48803
|
-
*
|
|
48804
|
-
* @param value - 当前值。
|
|
48805
|
-
* @returns 重排后的值。
|
|
48806
|
-
*/
|
|
48807
|
-
function sortJsonValue(value) {
|
|
48808
|
-
if (Array.isArray(value)) return value.map(sortJsonValue);
|
|
48809
|
-
if (!isPlainRecord(value)) return value;
|
|
48810
|
-
const out = {};
|
|
48811
|
-
for (const key of Object.keys(value).sort()) out[key] = sortJsonValue(value[key]);
|
|
48812
|
-
return out;
|
|
48813
|
-
}
|
|
48814
|
-
/**
|
|
48815
|
-
* 造一次工具调用的**规范键**。
|
|
48816
|
-
*
|
|
48817
|
-
* 键 = 工具名 + 参数的规范 JSON。参数先递归重排键名再序列化,于是
|
|
48818
|
-
* { a: 1, b: 2 } 与 { b: 2, a: 1 } 得到**完全相同**的键 —— 否则模型只是把参数
|
|
48819
|
-
* 的书写顺序换了一下,刹车就看不见这是一个连击。
|
|
48820
|
-
*
|
|
48821
|
-
* @param toolName - 工具名。
|
|
48822
|
-
* @param args - 工具参数。
|
|
48823
|
-
* @returns 规范键。
|
|
48824
|
-
*/
|
|
48825
|
-
function canonicalKimiToolCallKey(toolName, args) {
|
|
48826
|
-
let json;
|
|
48827
|
-
try {
|
|
48828
|
-
json = JSON.stringify(sortJsonValue(args));
|
|
48829
|
-
} catch {
|
|
48830
|
-
json = void 0;
|
|
48831
|
-
}
|
|
48832
|
-
return toolName + " " + (json ?? String(args));
|
|
48833
|
-
}
|
|
48834
|
-
/**
|
|
48835
49535
|
* 造一台重复刹车。
|
|
48836
49536
|
*
|
|
48837
49537
|
* 状态**按会话**分开:连击是「这场对话里的同一件事重复了多少次」,两个会话
|
|
@@ -48839,53 +49539,22 @@ function canonicalKimiToolCallKey(toolName, args) {
|
|
|
48839
49539
|
*
|
|
48840
49540
|
* @returns 一台重复刹车。
|
|
48841
49541
|
*/
|
|
49542
|
+
function repeatReminder(count) {
|
|
49543
|
+
if (count >= 8) return REMINDER_TEXT_3;
|
|
49544
|
+
if (count >= 5) return makeReminderText2(count);
|
|
49545
|
+
if (count >= 3) return REMINDER_TEXT_1;
|
|
49546
|
+
}
|
|
48842
49547
|
function createKimiRepeatBreaker() {
|
|
48843
|
-
|
|
48844
|
-
|
|
48845
|
-
|
|
48846
|
-
|
|
48847
|
-
|
|
48848
|
-
|
|
48849
|
-
|
|
48850
|
-
|
|
48851
|
-
|
|
48852
|
-
|
|
48853
|
-
sessions.set(sessionId, created);
|
|
48854
|
-
return created;
|
|
48855
|
-
};
|
|
48856
|
-
return {
|
|
48857
|
-
checkBeforeExecute(sessionId, toolName, args) {
|
|
48858
|
-
const state = stateOf(sessionId);
|
|
48859
|
-
if (state.handoffActive) return HANDOFF_VETO_TEXT;
|
|
48860
|
-
const key = canonicalKimiToolCallKey(toolName, args);
|
|
48861
|
-
if (state.consecutiveKey === key && state.consecutiveCount + 1 >= 12) {
|
|
48862
|
-
state.consecutiveCount += 1;
|
|
48863
|
-
state.handoffActive = true;
|
|
48864
|
-
return HANDOFF_VETO_TEXT;
|
|
48865
|
-
}
|
|
48866
|
-
},
|
|
48867
|
-
recordAfterExecute(sessionId, toolName, args) {
|
|
48868
|
-
if (isRepeatBreakerExemptTool(toolName)) return void 0;
|
|
48869
|
-
const state = stateOf(sessionId);
|
|
48870
|
-
if (state.handoffActive) return REMINDER_TEXT_3;
|
|
48871
|
-
const key = canonicalKimiToolCallKey(toolName, args);
|
|
48872
|
-
if (state.consecutiveKey === key) state.consecutiveCount += 1;
|
|
48873
|
-
else {
|
|
48874
|
-
state.consecutiveKey = key;
|
|
48875
|
-
state.consecutiveCount = 1;
|
|
48876
|
-
}
|
|
48877
|
-
if (state.consecutiveCount >= 12) {
|
|
48878
|
-
state.handoffActive = true;
|
|
48879
|
-
return REMINDER_TEXT_3;
|
|
48880
|
-
}
|
|
48881
|
-
if (state.consecutiveCount >= 8) return REMINDER_TEXT_3;
|
|
48882
|
-
if (state.consecutiveCount >= 5) return makeReminderText2(state.consecutiveCount);
|
|
48883
|
-
if (state.consecutiveCount >= 3) return REMINDER_TEXT_1;
|
|
48884
|
-
},
|
|
48885
|
-
resetSession(sessionId) {
|
|
48886
|
-
sessions.delete(sessionId);
|
|
48887
|
-
}
|
|
48888
|
-
};
|
|
49548
|
+
return createExactRepeatBreaker({
|
|
49549
|
+
reminder: repeatReminder,
|
|
49550
|
+
forceStopAt: 12,
|
|
49551
|
+
handoffReminder: REMINDER_TEXT_3,
|
|
49552
|
+
handoffVeto: HANDOFF_VETO_TEXT
|
|
49553
|
+
});
|
|
49554
|
+
}
|
|
49555
|
+
/** Other providers receive the graduated reminders, never Kimi's hard stop. */
|
|
49556
|
+
function createReminderOnlyRepeatBreaker() {
|
|
49557
|
+
return createExactRepeatBreaker({ reminder: repeatReminder });
|
|
48889
49558
|
}
|
|
48890
49559
|
/**
|
|
48891
49560
|
* 读宿主上下文上的工具注册面。
|
|
@@ -49007,37 +49676,43 @@ function isRealUserTurn(message) {
|
|
|
49007
49676
|
function installKimiRepeatBreaker(ctx, options = {}) {
|
|
49008
49677
|
const releaseMount = claimMount$1(ctx);
|
|
49009
49678
|
if (releaseMount === void 0) return () => void 0;
|
|
49010
|
-
const
|
|
49679
|
+
const kimiBreaker = createKimiRepeatBreaker();
|
|
49680
|
+
const reminderBreaker = createReminderOnlyRepeatBreaker();
|
|
49011
49681
|
const enabled = options.enabled ?? (() => isKimiRepeatBreakerEnabled());
|
|
49012
|
-
|
|
49013
|
-
const
|
|
49682
|
+
const globalEnabled = options.globalEnabled ?? (() => true);
|
|
49683
|
+
const providers = new Set(options.providers ?? ["kimi-code"]);
|
|
49684
|
+
const readSwitch = (read) => {
|
|
49014
49685
|
try {
|
|
49015
|
-
return
|
|
49686
|
+
return read() !== false;
|
|
49016
49687
|
} catch {
|
|
49017
49688
|
return true;
|
|
49018
49689
|
}
|
|
49019
49690
|
};
|
|
49020
|
-
|
|
49021
|
-
* 取一次执行所属的会话;不是 Kimi Code 这条线路时是 undefined。
|
|
49022
|
-
*
|
|
49023
|
-
* @param agent - 调用方 agent。
|
|
49024
|
-
* @returns 会话标识;不该管时是 undefined。
|
|
49025
|
-
*/
|
|
49026
|
-
const kimiSessionIdOf = (agent) => {
|
|
49691
|
+
const targetOf = (agent) => {
|
|
49027
49692
|
const route = kimiSessionRouteOf(agent);
|
|
49028
|
-
|
|
49029
|
-
|
|
49693
|
+
const provider = route.provider;
|
|
49694
|
+
if (provider === void 0 || route.sessionId === void 0 || !providers.has(provider)) return void 0;
|
|
49695
|
+
return {
|
|
49696
|
+
provider,
|
|
49697
|
+
key: provider + ":" + route.sessionId,
|
|
49698
|
+
breaker: provider === "kimi-code" ? kimiBreaker : reminderBreaker
|
|
49699
|
+
};
|
|
49700
|
+
};
|
|
49701
|
+
const activeTargetOf = (agent) => {
|
|
49702
|
+
if (!readSwitch(globalEnabled)) return void 0;
|
|
49703
|
+
const target = targetOf(agent);
|
|
49704
|
+
if (target === void 0) return void 0;
|
|
49705
|
+
if (target.provider === "kimi-code" && !readSwitch(enabled)) return void 0;
|
|
49706
|
+
return target;
|
|
49030
49707
|
};
|
|
49031
49708
|
const disposeGuard = toolsServiceOf(ctx)?.guard?.((execution) => {
|
|
49032
49709
|
try {
|
|
49033
|
-
if (!isEnabled()) return void 0;
|
|
49034
49710
|
const record = isRecord$45(execution) ? execution : void 0;
|
|
49035
|
-
const
|
|
49036
|
-
|
|
49037
|
-
if (sessionId === void 0) return void 0;
|
|
49711
|
+
const target = activeTargetOf(record?.["agent"]);
|
|
49712
|
+
if (target === void 0) return void 0;
|
|
49038
49713
|
const toolName = stringOf$3(record?.["name"]);
|
|
49039
49714
|
if (toolName === void 0) return void 0;
|
|
49040
|
-
const veto = breaker.checkBeforeExecute(
|
|
49715
|
+
const veto = target.breaker.checkBeforeExecute(target.key, toolName, record?.["arguments"]);
|
|
49041
49716
|
if (veto === HANDOFF_VETO_TEXT) return HANDOFF_VETO_TEXT;
|
|
49042
49717
|
return veto;
|
|
49043
49718
|
} catch {
|
|
@@ -49047,13 +49722,11 @@ function installKimiRepeatBreaker(ctx, options = {}) {
|
|
|
49047
49722
|
const disposePostExecute = ctx.on?.("tools/post-execute", (async (exec, result, next) => {
|
|
49048
49723
|
const decision = await next();
|
|
49049
49724
|
try {
|
|
49050
|
-
|
|
49051
|
-
|
|
49052
|
-
const sessionId = kimiSessionIdOf(agent);
|
|
49053
|
-
if (sessionId === void 0) return decision;
|
|
49725
|
+
const target = activeTargetOf(exec.agent);
|
|
49726
|
+
if (target === void 0) return decision;
|
|
49054
49727
|
const toolName = stringOf$3(exec.name);
|
|
49055
49728
|
if (toolName === void 0) return decision;
|
|
49056
|
-
const reminder = breaker.recordAfterExecute(
|
|
49729
|
+
const reminder = target.breaker.recordAfterExecute(target.key, toolName, exec.arguments);
|
|
49057
49730
|
if (reminder === void 0) return decision;
|
|
49058
49731
|
return withAdditionalContexts(decision, [createRepeatBreakerReminder(reminder)]);
|
|
49059
49732
|
} catch {
|
|
@@ -49064,9 +49737,9 @@ function installKimiRepeatBreaker(ctx, options = {}) {
|
|
|
49064
49737
|
const decision = await next();
|
|
49065
49738
|
try {
|
|
49066
49739
|
if (!isRealUserTurn((Array.isArray(payload?.messages) ? payload.messages : []).at(-1))) return decision;
|
|
49067
|
-
const
|
|
49068
|
-
if (
|
|
49069
|
-
breaker.resetSession(
|
|
49740
|
+
const target = activeTargetOf(payload?.agent);
|
|
49741
|
+
if (target === void 0) return decision;
|
|
49742
|
+
target.breaker.resetSession(target.key);
|
|
49070
49743
|
} catch {
|
|
49071
49744
|
return decision;
|
|
49072
49745
|
}
|
|
@@ -49074,9 +49747,9 @@ function installKimiRepeatBreaker(ctx, options = {}) {
|
|
|
49074
49747
|
}));
|
|
49075
49748
|
const disposeDisposed = ctx.on?.("agent/disposed", ((payload) => {
|
|
49076
49749
|
try {
|
|
49077
|
-
const
|
|
49078
|
-
if (
|
|
49079
|
-
breaker.resetSession(
|
|
49750
|
+
const target = targetOf(payload?.agent);
|
|
49751
|
+
if (target === void 0) return;
|
|
49752
|
+
target.breaker.resetSession(target.key);
|
|
49080
49753
|
} catch {}
|
|
49081
49754
|
}));
|
|
49082
49755
|
return () => {
|
|
@@ -56502,6 +57175,8 @@ function assertClaudeCliVersionMeetsFloor(modelId, catalog = FALLBACK_MODELS) {
|
|
|
56502
57175
|
if (meetsDottedVersionFloor(effective, floor)) return;
|
|
56503
57176
|
throw new LlmError("Claude(订阅) will not serve " + modelId + " to a client reporting version " + effective + ": upstream requires " + floor + " or newer for this model, and a lower claim is refused with claude_code_version_too_old. This was caught locally, before the request was sent. Raise the reported version with the DSH_CLAUDE_CLI_VERSION environment variable, or with a setClaudeCliVersion pin, then retry. The stored sign-in is still valid — this is not a credential problem and signing in again will not change it.", "PROVIDER_ERROR");
|
|
56504
57177
|
}
|
|
57178
|
+
const claudePrefixRequests = new PrefixRequestCoordinator();
|
|
57179
|
+
const claudeStickyImages = new StickyImageBudgetStore("claude");
|
|
56505
57180
|
/**
|
|
56506
57181
|
* The immutable id of the account a token came from, presented as the two-method
|
|
56507
57182
|
* store oauth.ts wants. See module note 3.
|
|
@@ -56755,7 +57430,9 @@ var ClaudeAdapter = class ClaudeAdapter extends LlmAdapter {
|
|
|
56755
57430
|
cacheTtl
|
|
56756
57431
|
});
|
|
56757
57432
|
try {
|
|
56758
|
-
|
|
57433
|
+
const cachePolicy = promptCachePolicyOf("claude", this.options.promptCache);
|
|
57434
|
+
const groupKey = cachePolicy.coldStart === "off" ? void 0 : sharedPrefixGroupKey("claude", requestOptions, "subscription", credentials.account?.uuid ?? "legacy");
|
|
57435
|
+
return await fetchWithPrefixCoordination(fetchWithContextBudgetRecovery(fetchFn), claudePrefixRequests, groupKey, cachePolicy.coldStart === "warm" ? cachePolicy.ttlMs : void 0, "https://api.anthropic.com/v1/messages?beta=true", {
|
|
56759
57436
|
method: "POST",
|
|
56760
57437
|
headers: buildClaudeHeaders(credentials.accessToken, {
|
|
56761
57438
|
model: requestOptions.model,
|
|
@@ -56775,9 +57452,12 @@ var ClaudeAdapter = class ClaudeAdapter extends LlmAdapter {
|
|
|
56775
57452
|
}
|
|
56776
57453
|
async *requestStream(options, signal, activity) {
|
|
56777
57454
|
const fetchFn = this.options.fetchFn ?? fetch;
|
|
56778
|
-
const normalized = normalizeGenerateOptions(options);
|
|
57455
|
+
const normalized = withStaticPrefixOrdering("claude", normalizeGenerateOptions(options), this.options.promptCache);
|
|
56779
57456
|
const images = await resolveRequestImages(normalized, this.options.attachments, signal);
|
|
56780
|
-
const
|
|
57457
|
+
const stickyImages = new Map([...images].map(([id, image]) => [id, image.kind === "oversized" ? { kind: "unavailable" } : image]));
|
|
57458
|
+
const sticky = promptCachePolicyOf("claude", this.options.promptCache).stickyImages ? offloadRequestImagesWithStickyBudget(normalized, MAX_REQUEST_IMAGE_BYTES$2, Math.max(1, Math.floor(MAX_REQUEST_IMAGE_BYTES$2 / 2)), stickyImages, claudeStickyImages.priorDroppedKeys(normalized.sessionId)) : void 0;
|
|
57459
|
+
const requestOptions = sticky?.options ?? offloadOldestRequestImages(normalized, images);
|
|
57460
|
+
if (sticky !== void 0 && sticky.newlyDroppedKeys.length > 0) claudeStickyImages.record(normalized.sessionId, sticky.allDroppedKeys);
|
|
56781
57461
|
const toolNames = claudeToolNames(requestOptions.tools);
|
|
56782
57462
|
assertClaudeCliVersionMeetsFloor(requestOptions.model, await this.catalogForRequest());
|
|
56783
57463
|
const effort = requestOptions.reasoningEffort === void 0 || requestOptions.reasoningEffort === null ? void 0 : String(requestOptions.reasoningEffort);
|
|
@@ -66765,11 +67445,77 @@ function classifyStepCodeFailure(status, detail) {
|
|
|
66765
67445
|
}
|
|
66766
67446
|
//#endregion
|
|
66767
67447
|
//#region src/host/step-code/cache-stats.ts
|
|
67448
|
+
/**
|
|
67449
|
+
* Misses at or below this are cache-breakpoint granularity, not waste.
|
|
67450
|
+
*
|
|
67451
|
+
* A prefix cache breaks at block boundaries, so a few hundred tokens of drift
|
|
67452
|
+
* between two turns is normal re-shuffling. Reporting it teaches the user to
|
|
67453
|
+
* ignore the notice, which costs the one notice that mattered.
|
|
67454
|
+
*/
|
|
67455
|
+
const NOISE_FLOOR_TOKENS = 1024;
|
|
66768
67456
|
/** Aggregate prompt count of one turn: uncached input plus both cache buckets. */
|
|
66769
67457
|
function promptTokensOf(usage) {
|
|
66770
67458
|
return usage.inputTokens + (usage.cacheReadTokens ?? 0) + (usage.cacheWriteTokens ?? 0);
|
|
66771
67459
|
}
|
|
66772
67460
|
//#endregion
|
|
67461
|
+
//#region src/host/common/cache-activity.ts
|
|
67462
|
+
/** Last request activity used by cache-expiry hints and wait-time keepalive. */
|
|
67463
|
+
function normalizeRecord(value) {
|
|
67464
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
|
|
67465
|
+
const record = value;
|
|
67466
|
+
const lastActiveAt = record.lastActiveAt;
|
|
67467
|
+
const updatedAt = record.updatedAt;
|
|
67468
|
+
if (typeof lastActiveAt !== "number" || !Number.isFinite(lastActiveAt)) return void 0;
|
|
67469
|
+
if (typeof updatedAt !== "number" || !Number.isFinite(updatedAt)) return void 0;
|
|
67470
|
+
return {
|
|
67471
|
+
lastActiveAt,
|
|
67472
|
+
totalTokens: typeof record.totalTokens === "number" && Number.isFinite(record.totalTokens) ? Math.max(0, Math.floor(record.totalTokens)) : 0,
|
|
67473
|
+
updatedAt
|
|
67474
|
+
};
|
|
67475
|
+
}
|
|
67476
|
+
function persistenceEnabled() {
|
|
67477
|
+
return process.env.VITEST !== "true" && process.env.NODE_ENV !== "test";
|
|
67478
|
+
}
|
|
67479
|
+
var PromptCacheActivityStore = class {
|
|
67480
|
+
store;
|
|
67481
|
+
constructor(provider, filePath) {
|
|
67482
|
+
const target = filePath ?? path.join(dshHomeDir(), "storages", `dsh-chatgpt-subscription-${provider}-activity.json`);
|
|
67483
|
+
this.store = new CommonSessionCacheStateStore({
|
|
67484
|
+
filePath: target,
|
|
67485
|
+
allowWrite: filePath !== void 0 || persistenceEnabled(),
|
|
67486
|
+
normalizeRecord
|
|
67487
|
+
});
|
|
67488
|
+
}
|
|
67489
|
+
read(sessionId) {
|
|
67490
|
+
const record = this.store.read(sessionId);
|
|
67491
|
+
return record === void 0 ? void 0 : {
|
|
67492
|
+
lastActiveAt: record.lastActiveAt,
|
|
67493
|
+
totalTokens: record.totalTokens
|
|
67494
|
+
};
|
|
67495
|
+
}
|
|
67496
|
+
record(sessionId, totalTokens, at = Date.now()) {
|
|
67497
|
+
if (sessionId === void 0 || sessionId.trim() === "") return;
|
|
67498
|
+
this.store.write(sessionId, {
|
|
67499
|
+
lastActiveAt: at,
|
|
67500
|
+
totalTokens: Number.isFinite(totalTokens) ? Math.max(0, Math.floor(totalTokens)) : 0,
|
|
67501
|
+
updatedAt: at
|
|
67502
|
+
});
|
|
67503
|
+
}
|
|
67504
|
+
flush() {
|
|
67505
|
+
this.store.flush();
|
|
67506
|
+
}
|
|
67507
|
+
};
|
|
67508
|
+
const SHARED_ACTIVITY_STORES = /* @__PURE__ */ new Map();
|
|
67509
|
+
/** One live store per provider; tests can still construct isolated instances by path. */
|
|
67510
|
+
function promptCacheActivityStore(provider) {
|
|
67511
|
+
let store = SHARED_ACTIVITY_STORES.get(provider);
|
|
67512
|
+
if (store === void 0) {
|
|
67513
|
+
store = new PromptCacheActivityStore(provider);
|
|
67514
|
+
SHARED_ACTIVITY_STORES.set(provider, store);
|
|
67515
|
+
}
|
|
67516
|
+
return store;
|
|
67517
|
+
}
|
|
67518
|
+
//#endregion
|
|
66773
67519
|
//#region src/host/step-code/client.ts
|
|
66774
67520
|
/**
|
|
66775
67521
|
* Step Code 线路的上游 I/O:模型目录 + 开发者中心额度。
|
|
@@ -68446,6 +69192,8 @@ function streamHeadersFor(credentials) {
|
|
|
68446
69192
|
accept: "text/event-stream"
|
|
68447
69193
|
};
|
|
68448
69194
|
}
|
|
69195
|
+
const stepPrefixRequests = new PrefixRequestCoordinator();
|
|
69196
|
+
const stepCacheActivity = promptCacheActivityStore("step-code");
|
|
68449
69197
|
/**
|
|
68450
69198
|
* Step Code(Step Plan 订阅)线路的 LlmAdapter 实现。
|
|
68451
69199
|
*
|
|
@@ -68494,10 +69242,10 @@ var StepCodeAdapter = class extends LlmAdapter {
|
|
|
68494
69242
|
* 两条路径都**不刷新**:Step 的 api_key 永不过期,号池的 refresh 钩子恒抛
|
|
68495
69243
|
* `StepCodeCredentialRefreshError`,所以这里不存在「刷新失败后再试一次」的分支。
|
|
68496
69244
|
*/
|
|
68497
|
-
async credentialFor(excludeIds) {
|
|
69245
|
+
async credentialFor(excludeIds, selection) {
|
|
68498
69246
|
const fetchFn = this.options.fetchFn ?? fetch;
|
|
68499
69247
|
if (this.accountPool !== null) {
|
|
68500
|
-
const effective = await this.accountPool.getEffectiveCredential(excludeIds, fetchFn);
|
|
69248
|
+
const effective = await this.accountPool.getEffectiveCredential(excludeIds, fetchFn, selection);
|
|
68501
69249
|
const credentials = effective.credentials;
|
|
68502
69250
|
return {
|
|
68503
69251
|
accountId: effective.account.id,
|
|
@@ -68519,6 +69267,31 @@ var StepCodeAdapter = class extends LlmAdapter {
|
|
|
68519
69267
|
}
|
|
68520
69268
|
};
|
|
68521
69269
|
}
|
|
69270
|
+
/** Warm-prefix account preference, inert unless a deployment proves account scope. */
|
|
69271
|
+
async cacheAffinityHint(options) {
|
|
69272
|
+
const pool = this.accountPool;
|
|
69273
|
+
if (pool === null) return void 0;
|
|
69274
|
+
const policy = promptCachePolicyOf("step-code", this.options.promptCache);
|
|
69275
|
+
if (!policy.affinity || policy.ttlMs === void 0) return void 0;
|
|
69276
|
+
const data = await pool.read().catch(() => void 0);
|
|
69277
|
+
if (data === void 0 || data.accounts.length < 2) return void 0;
|
|
69278
|
+
const candidates = [];
|
|
69279
|
+
const remainingShareById = {};
|
|
69280
|
+
for (const account of data.accounts) {
|
|
69281
|
+
const key = sharedPrefixGroupKey("step-code", options, account.credentials.profile, account.id);
|
|
69282
|
+
if (key === void 0 || !stepPrefixRequests.isWarm(key, policy.ttlMs)) continue;
|
|
69283
|
+
candidates.push({
|
|
69284
|
+
id: account.id,
|
|
69285
|
+
warm: true
|
|
69286
|
+
});
|
|
69287
|
+
remainingShareById[account.id] = remainingQuotaShare$1(getCachedStepQuotaFor(account.id));
|
|
69288
|
+
}
|
|
69289
|
+
const preferAccountIds = orderAffinityCandidates$1({
|
|
69290
|
+
candidates,
|
|
69291
|
+
remainingShareById
|
|
69292
|
+
});
|
|
69293
|
+
return preferAccountIds.length === 0 ? void 0 : { preferAccountIds };
|
|
69294
|
+
}
|
|
68522
69295
|
async listModels(provider) {
|
|
68523
69296
|
const prov = provider || PROVIDER_ID$6;
|
|
68524
69297
|
const settings = await this.settings();
|
|
@@ -68581,13 +69354,14 @@ var StepCodeAdapter = class extends LlmAdapter {
|
|
|
68581
69354
|
async *requestStream(options, signal, activity) {
|
|
68582
69355
|
const fetchFn = this.options.fetchFn ?? fetch;
|
|
68583
69356
|
const settings = await this.settings();
|
|
68584
|
-
const normalized = normalizeGenerateOptions(options);
|
|
69357
|
+
const normalized = withStaticPrefixOrdering("step-code", normalizeGenerateOptions(options), this.options.promptCache);
|
|
68585
69358
|
const images = await resolveRequestImages$2(normalized, this.options.attachments, signal);
|
|
68586
69359
|
const requestOptions = offloadOldestStepCodeRequestImages(normalized, images);
|
|
68587
69360
|
const tried = /* @__PURE__ */ new Set();
|
|
68588
69361
|
let response;
|
|
68589
69362
|
let url = "";
|
|
68590
|
-
|
|
69363
|
+
const affinity = await this.cacheAffinityHint(requestOptions);
|
|
69364
|
+
let picked = await this.credentialFor(tried, affinity);
|
|
68591
69365
|
while (true) {
|
|
68592
69366
|
if (picked.accountId !== null) tried.add(picked.accountId);
|
|
68593
69367
|
const catalog = this.catalog(picked.credentials.profile);
|
|
@@ -68595,7 +69369,9 @@ var StepCodeAdapter = class extends LlmAdapter {
|
|
|
68595
69369
|
const body = JSON.stringify(buildChatRequest$1(requestOptions, images, stepCodeRequestOptionsFor(entry, { cacheRequestParams: settings.cacheRequestParams })));
|
|
68596
69370
|
url = stepChatCompletionsUrl(picked.credentials.profile);
|
|
68597
69371
|
try {
|
|
68598
|
-
|
|
69372
|
+
const cachePolicy = promptCachePolicyOf("step-code", this.options.promptCache);
|
|
69373
|
+
const groupKey = cachePolicy.coldStart === "off" ? void 0 : sharedPrefixGroupKey("step-code", requestOptions, picked.credentials.profile, picked.accountId ?? "legacy");
|
|
69374
|
+
response = await fetchWithPrefixCoordination(fetchWithContextBudgetRecovery(fetchFn), stepPrefixRequests, groupKey, cachePolicy.coldStart === "warm" ? cachePolicy.ttlMs : void 0, url, {
|
|
68599
69375
|
method: "POST",
|
|
68600
69376
|
headers: streamHeadersFor(picked.credentials),
|
|
68601
69377
|
body,
|
|
@@ -68617,13 +69393,13 @@ var StepCodeAdapter = class extends LlmAdapter {
|
|
|
68617
69393
|
const after = retryAfterMs$1(response.headers);
|
|
68618
69394
|
await this.accountPool.markCooldown(picked.accountId, after ?? 3e5, "Step Code(Step Plan 订阅) 429:上游限流或额度用尽").catch(() => void 0);
|
|
68619
69395
|
if (await this.accountPool.hasAnotherAvailableAccount(tried)) {
|
|
68620
|
-
picked = await this.credentialFor(tried);
|
|
69396
|
+
picked = await this.credentialFor(tried, affinity);
|
|
68621
69397
|
continue;
|
|
68622
69398
|
}
|
|
68623
69399
|
} else if (failure.code === "AUTH") {
|
|
68624
69400
|
await this.accountPool.markAuthFailed(picked.accountId, failure.message).catch(() => void 0);
|
|
68625
69401
|
if (await this.accountPool.hasAnotherAvailableAccount(tried)) {
|
|
68626
|
-
picked = await this.credentialFor(tried);
|
|
69402
|
+
picked = await this.credentialFor(tried, affinity);
|
|
68627
69403
|
continue;
|
|
68628
69404
|
}
|
|
68629
69405
|
}
|
|
@@ -68656,16 +69432,42 @@ var StepCodeAdapter = class extends LlmAdapter {
|
|
|
68656
69432
|
assertStreamComplete$2(state);
|
|
68657
69433
|
for (const chunk of closeStream$1(state)) yield chunk;
|
|
68658
69434
|
} finally {
|
|
68659
|
-
if (state.sawUsage)
|
|
68660
|
-
|
|
68661
|
-
|
|
68662
|
-
|
|
68663
|
-
|
|
68664
|
-
|
|
69435
|
+
if (state.sawUsage) {
|
|
69436
|
+
rememberStepCodeCacheUsage({
|
|
69437
|
+
inputTokens: state.inputTokens,
|
|
69438
|
+
outputTokens: state.outputTokens,
|
|
69439
|
+
cacheReadTokens: state.cacheReadTokens,
|
|
69440
|
+
cacheWriteTokens: state.cacheWriteTokens
|
|
69441
|
+
});
|
|
69442
|
+
stepCacheActivity.record(options.sessionId, promptTokensOf({
|
|
69443
|
+
inputTokens: state.inputTokens,
|
|
69444
|
+
outputTokens: state.outputTokens,
|
|
69445
|
+
cacheReadTokens: state.cacheReadTokens,
|
|
69446
|
+
cacheWriteTokens: state.cacheWriteTokens
|
|
69447
|
+
}));
|
|
69448
|
+
}
|
|
68665
69449
|
reader.cancel().catch(() => void 0);
|
|
68666
69450
|
}
|
|
68667
69451
|
}
|
|
68668
69452
|
};
|
|
69453
|
+
/** Cache-expiry hint for one session, or the latest recorded session when omitted. */
|
|
69454
|
+
function readStepCodeCacheHint(sessionId, override) {
|
|
69455
|
+
const policy = promptCachePolicyOf("step-code", override);
|
|
69456
|
+
if (!policy.hint) return null;
|
|
69457
|
+
const activity = stepCacheActivity.read(sessionId);
|
|
69458
|
+
if (activity === void 0) return null;
|
|
69459
|
+
const decision = evaluateCacheHint$1({
|
|
69460
|
+
now: Date.now(),
|
|
69461
|
+
lastActiveAt: activity.lastActiveAt,
|
|
69462
|
+
totalTokens: activity.totalTokens,
|
|
69463
|
+
ttlMs: policy.ttlMs,
|
|
69464
|
+
minTokensToHint: NOISE_FLOOR_TOKENS
|
|
69465
|
+
});
|
|
69466
|
+
return decision.kind === "hint" ? {
|
|
69467
|
+
idleMs: decision.idleMs,
|
|
69468
|
+
totalTokens: decision.totalTokens
|
|
69469
|
+
} : null;
|
|
69470
|
+
}
|
|
68669
69471
|
//#endregion
|
|
68670
69472
|
//#region src/host/step-code/oauth.ts
|
|
68671
69473
|
/**
|
|
@@ -69457,8 +70259,8 @@ var StepCodeAccountPool = class extends AccountPoolCore {
|
|
|
69457
70259
|
* 核心在这里做轮转、冷却、可用性判断与(被拒绝的)刷新;本方法只多给适配器一件事:
|
|
69458
70260
|
* 一次请求内换过账号时,把试过的账号排除掉。
|
|
69459
70261
|
*/
|
|
69460
|
-
async getEffectiveCredential(excludeIds, fetchFn = fetch) {
|
|
69461
|
-
return this.getEffectiveAccount(excludeIds, fetchFn);
|
|
70262
|
+
async getEffectiveCredential(excludeIds, fetchFn = fetch, selection) {
|
|
70263
|
+
return this.getEffectiveAccount(excludeIds, fetchFn, false, void 0, selection);
|
|
69462
70264
|
}
|
|
69463
70265
|
/**
|
|
69464
70266
|
* 一次额度读属于哪个账号,连同读它用的凭据。
|
|
@@ -69949,6 +70751,7 @@ async function getStepCodeStatus(sources) {
|
|
|
69949
70751
|
const settingsValue = await settings.status();
|
|
69950
70752
|
const quota = sources.quota;
|
|
69951
70753
|
const cacheStats = sources.cacheStats;
|
|
70754
|
+
const cacheHint = readStepCodeCacheHint(void 0, sources.options.promptCache);
|
|
69952
70755
|
const accountCount = sources.pool.accounts.length;
|
|
69953
70756
|
return {
|
|
69954
70757
|
...sources.pool,
|
|
@@ -69973,6 +70776,7 @@ async function getStepCodeStatus(sources) {
|
|
|
69973
70776
|
quotaCredential: sources.quotaCredential,
|
|
69974
70777
|
cacheRequestParams: settingsValue.cacheRequestParams === true,
|
|
69975
70778
|
...cacheStats === void 0 ? {} : { cacheStats },
|
|
70779
|
+
...cacheHint === null ? {} : { cacheHint },
|
|
69976
70780
|
serving: readOption$2(options.serving, true),
|
|
69977
70781
|
conflict: readOption$2(options.conflict, null)
|
|
69978
70782
|
};
|
|
@@ -72296,6 +73100,7 @@ function isRecord$26(value) {
|
|
|
72296
73100
|
*
|
|
72297
73101
|
* @module dsh-chatgpt-subscription/cline-pass-mapper
|
|
72298
73102
|
*/
|
|
73103
|
+
const clineReasoningDialect = new ReasoningKeyDialect$1();
|
|
72299
73104
|
/**
|
|
72300
73105
|
* The one wording the shared detector does not know.
|
|
72301
73106
|
*
|
|
@@ -72329,7 +73134,7 @@ function serializeAssistant$1(message) {
|
|
|
72329
73134
|
return {
|
|
72330
73135
|
role: "assistant",
|
|
72331
73136
|
content: text,
|
|
72332
|
-
...reasoning.length > 0 ? {
|
|
73137
|
+
...reasoning.length > 0 ? { [clineReasoningDialect.outboundKey()]: reasoning } : {},
|
|
72333
73138
|
...toolCalls.length > 0 ? { tool_calls: toolCalls } : {}
|
|
72334
73139
|
};
|
|
72335
73140
|
}
|
|
@@ -72624,6 +73429,7 @@ function acceptIdentity$1(current, incoming) {
|
|
|
72624
73429
|
* parts, which is where a gateway that omits the flat field puts the same text.
|
|
72625
73430
|
*/
|
|
72626
73431
|
function reasoningOf$1(delta) {
|
|
73432
|
+
clineReasoningDialect.observe(delta);
|
|
72627
73433
|
for (const field of ["reasoning", "reasoning_content"]) {
|
|
72628
73434
|
const value = delta[field];
|
|
72629
73435
|
if (typeof value === "string" && value.length > 0) return value;
|
|
@@ -79028,6 +79834,7 @@ async function loadQoderQuota(credentials, options = {}) {
|
|
|
79028
79834
|
*
|
|
79029
79835
|
* @module dsh-chatgpt-subscription/qoder-mapper
|
|
79030
79836
|
*/
|
|
79837
|
+
const qoderReasoningDialect = new ReasoningKeyDialect$1();
|
|
79031
79838
|
function isRecord$21(value) {
|
|
79032
79839
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
79033
79840
|
}
|
|
@@ -79374,7 +80181,7 @@ function buildQoderChatRequest(options, images, request, context) {
|
|
|
79374
80181
|
role: "assistant",
|
|
79375
80182
|
content: content !== "" ? content : toolCalls.length > 0 || reasoning !== "" ? " " : null
|
|
79376
80183
|
};
|
|
79377
|
-
if (reasoning !== "") entry.
|
|
80184
|
+
if (reasoning !== "") Object.assign(entry, { [qoderReasoningDialect.outboundKey()]: reasoning });
|
|
79378
80185
|
if (toolCalls.length > 0) entry.tool_calls = toolCalls;
|
|
79379
80186
|
if (entry.content !== null) messages.push(entry);
|
|
79380
80187
|
continue;
|
|
@@ -79723,6 +80530,7 @@ function processQoderStreamLine(line, state) {
|
|
|
79723
80530
|
const choice = isRecord$21(choices[0]) ? choices[0] : void 0;
|
|
79724
80531
|
const delta = choice !== void 0 && isRecord$21(choice.delta) ? choice.delta : void 0;
|
|
79725
80532
|
if (delta !== void 0) {
|
|
80533
|
+
qoderReasoningDialect.observe(delta);
|
|
79726
80534
|
const reasoning = asString$7(delta.reasoning_content) ?? asString$7(delta.reasoning);
|
|
79727
80535
|
if (reasoning !== void 0 && reasoning !== "") out.push(...appendTextDelta(state, "reasoning", sanitizeText$1(reasoning)));
|
|
79728
80536
|
const content = asString$7(delta.content);
|
|
@@ -84252,6 +85060,7 @@ async function fetchCodingPlanUsage({ fetchFn, controlKey, runtimeKey, workspace
|
|
|
84252
85060
|
*
|
|
84253
85061
|
* @module dsh-chatgpt-subscription/opendesign-mapper
|
|
84254
85062
|
*/
|
|
85063
|
+
const openDesignReasoningDialect = new ReasoningKeyDialect$1();
|
|
84255
85064
|
/** The sentence a no-content finish carries, shared with the sibling lines. */
|
|
84256
85065
|
const NO_ANSWER_PHRASE = "ended the turn without an answer or a tool call";
|
|
84257
85066
|
/**
|
|
@@ -84287,7 +85096,7 @@ function serializeAssistant(message) {
|
|
|
84287
85096
|
return {
|
|
84288
85097
|
role: "assistant",
|
|
84289
85098
|
content: text,
|
|
84290
|
-
...reasoning.length > 0 ? {
|
|
85099
|
+
...reasoning.length > 0 ? { [openDesignReasoningDialect.outboundKey()]: reasoning } : {},
|
|
84291
85100
|
...toolCalls.length > 0 ? { tool_calls: toolCalls } : {}
|
|
84292
85101
|
};
|
|
84293
85102
|
}
|
|
@@ -84630,6 +85439,7 @@ function acceptIdentity(current, incoming) {
|
|
|
84630
85439
|
* parts, which is where a gateway that omits the flat field puts the same text.
|
|
84631
85440
|
*/
|
|
84632
85441
|
function reasoningOf(delta) {
|
|
85442
|
+
openDesignReasoningDialect.observe(delta);
|
|
84633
85443
|
for (const field of ["reasoning", "reasoning_content"]) {
|
|
84634
85444
|
const value = delta[field];
|
|
84635
85445
|
if (typeof value === "string" && value.length > 0) return value;
|
|
@@ -96110,7 +96920,7 @@ function createCacheWarmupResultTool(deps) {
|
|
|
96110
96920
|
const route = deps.routeOf(exec);
|
|
96111
96921
|
const refId = args.refId;
|
|
96112
96922
|
const action = args.action;
|
|
96113
|
-
if (route.provider !== deps.provider) return failure(action, refId, route.sessionId ?? "", "WRONG_PROVIDER", "ref handles
|
|
96923
|
+
if (!(route.provider !== void 0 && (deps.allowProvider?.(route.provider) ?? route.provider === deps.provider))) return failure(action, refId, route.sessionId ?? "", "WRONG_PROVIDER", "ref handles are not available on this session route: \"" + (route.provider ?? "unknown") + "\"");
|
|
96114
96924
|
const sessionId = route.sessionId;
|
|
96115
96925
|
if (sessionId === void 0) return failure(action, refId, "", "NO_SESSION", "the current session could not be resolved, so the handle cannot be checked");
|
|
96116
96926
|
const store = deps.store();
|
|
@@ -96252,8 +97062,8 @@ function cacheWarmupNavigationIntentOf(agent) {
|
|
|
96252
97062
|
/**
|
|
96253
97063
|
* 本模块服务的线路。
|
|
96254
97064
|
*
|
|
96255
|
-
*
|
|
96256
|
-
*
|
|
97065
|
+
* Kimi 是缺少显式 provider 列表时的兼容回退;宿主正常接线会注入受控线路表。
|
|
97066
|
+
* 是否投影由当前设置决定,是否补读已有 ref 则由会话所有权与受支持路由决定。
|
|
96257
97067
|
*/
|
|
96258
97068
|
const CACHE_WARMUP_PROVIDER = "kimi-code";
|
|
96259
97069
|
/**
|
|
@@ -96385,9 +97195,9 @@ function claimMount(ctx) {
|
|
|
96385
97195
|
* @param sessionId - 会话标识。
|
|
96386
97196
|
* @returns 快照,或读不到时的 undefined。
|
|
96387
97197
|
*/
|
|
96388
|
-
function readCacheWarmupSnapshot(sessionId) {
|
|
97198
|
+
function readCacheWarmupSnapshot(sessionId, provider) {
|
|
96389
97199
|
try {
|
|
96390
|
-
const raw = cacheWarmupSnapshot(sessionId);
|
|
97200
|
+
const raw = cacheWarmupSnapshot(sessionId, void 0, provider);
|
|
96391
97201
|
return typeof raw === "object" && raw !== null ? raw : void 0;
|
|
96392
97202
|
} catch {
|
|
96393
97203
|
return;
|
|
@@ -96453,6 +97263,7 @@ function installCacheWarmup(ctx, options) {
|
|
|
96453
97263
|
if (tools === void 0) return () => void 0;
|
|
96454
97264
|
return tools.register(createCacheWarmupResultTool({
|
|
96455
97265
|
provider: CACHE_WARMUP_PROVIDER,
|
|
97266
|
+
allowProvider: (provider) => CONTROLLED_PROVIDERS.includes(provider),
|
|
96456
97267
|
routeOf: (exec) => cacheWarmupRouteOf(exec?.agent),
|
|
96457
97268
|
store: storeOf,
|
|
96458
97269
|
log
|
|
@@ -96474,12 +97285,13 @@ function installCacheWarmup(ctx, options) {
|
|
|
96474
97285
|
* @param sessionId - 会话标识。
|
|
96475
97286
|
* @param budget - 本轮读到的预算对象。
|
|
96476
97287
|
*/
|
|
96477
|
-
const trackWarmup = (sessionId, budget) => {
|
|
96478
|
-
const snapshot = readCacheWarmupSnapshot(sessionId);
|
|
97288
|
+
const trackWarmup = (sessionId, budget, provider) => {
|
|
97289
|
+
const snapshot = readCacheWarmupSnapshot(sessionId, provider);
|
|
96479
97290
|
const warmup = booleanOf(snapshot?.warmup) ?? booleanOf(budget.warmup);
|
|
96480
97291
|
const consecutiveHits = numberOf$1(snapshot?.consecutiveHits) ?? numberOf$1(budget.consecutiveHits);
|
|
96481
97292
|
const hitRatio = numberOf$1(snapshot?.hitRatio) ?? numberOf$1(budget.hitRatio);
|
|
96482
|
-
|
|
97293
|
+
const scope = (provider ?? "kimi-code") + ":" + sessionId;
|
|
97294
|
+
if (warmupState.get(scope) === true && warmup === false) {
|
|
96483
97295
|
const reason = exitReasonOf(consecutiveHits);
|
|
96484
97296
|
log({
|
|
96485
97297
|
event: "warmup-exit",
|
|
@@ -96489,7 +97301,7 @@ function installCacheWarmup(ctx, options) {
|
|
|
96489
97301
|
...hitRatio === void 0 ? {} : { hitRatio }
|
|
96490
97302
|
});
|
|
96491
97303
|
}
|
|
96492
|
-
if (warmup !== void 0) warmupState.set(
|
|
97304
|
+
if (warmup !== void 0) warmupState.set(scope, warmup);
|
|
96493
97305
|
return {
|
|
96494
97306
|
...warmup === void 0 ? {} : { warmup },
|
|
96495
97307
|
...consecutiveHits === void 0 ? {} : { consecutiveHits },
|
|
@@ -96533,7 +97345,8 @@ function installCacheWarmup(ctx, options) {
|
|
|
96533
97345
|
if (retrievalTool && navigation !== true) return decision;
|
|
96534
97346
|
if (sourceTool && !settings.enabled && navigation !== true) return decision;
|
|
96535
97347
|
const route = cacheWarmupRouteOf(exec.agent);
|
|
96536
|
-
|
|
97348
|
+
const admittedProviders = settings.providers ?? ["kimi-code"];
|
|
97349
|
+
if (route.provider === void 0 || !admittedProviders.includes(route.provider)) return decision;
|
|
96537
97350
|
const sessionId = route.sessionId;
|
|
96538
97351
|
if (sessionId === void 0) return decision;
|
|
96539
97352
|
const store = storeOf();
|
|
@@ -96541,11 +97354,11 @@ function installCacheWarmup(ctx, options) {
|
|
|
96541
97354
|
const content = blocks;
|
|
96542
97355
|
let durable;
|
|
96543
97356
|
try {
|
|
96544
|
-
durable = options.durableHint?.(sessionId);
|
|
97357
|
+
durable = options.durableHint?.(sessionId, route.provider);
|
|
96545
97358
|
} catch {
|
|
96546
97359
|
durable = void 0;
|
|
96547
97360
|
}
|
|
96548
|
-
const budget = durable === void 0 ? cacheWarmupBudget(sessionId) : cacheWarmupBudget(sessionId, Date.now(), durable);
|
|
97361
|
+
const budget = durable === void 0 ? cacheWarmupBudget(sessionId, Date.now(), void 0, route.provider) : cacheWarmupBudget(sessionId, Date.now(), durable, route.provider);
|
|
96549
97362
|
const projected = await projectToolResult({
|
|
96550
97363
|
toolName: exec.name,
|
|
96551
97364
|
provider: route.provider,
|
|
@@ -96560,7 +97373,7 @@ function installCacheWarmup(ctx, options) {
|
|
|
96560
97373
|
loggerOf()?.warn?.("[dsh-chatgpt-subscription] cache warmup projection skipped: " + cacheWarmupErrorLabel(error));
|
|
96561
97374
|
});
|
|
96562
97375
|
if (projected === void 0) return decision;
|
|
96563
|
-
const metrics = trackWarmup(sessionId, budget);
|
|
97376
|
+
const metrics = trackWarmup(sessionId, budget, route.provider);
|
|
96564
97377
|
log({
|
|
96565
97378
|
event: "projection",
|
|
96566
97379
|
session: sessionId,
|
|
@@ -96607,7 +97420,7 @@ function blocksOf(content) {
|
|
|
96607
97420
|
/** Environment override for wait-time cache keepalive warmup. */
|
|
96608
97421
|
const CACHE_WAIT_WARMUP_ENV = "DSH_KIMI_CODE_CACHE_WAIT_WARMUP";
|
|
96609
97422
|
/** Settings description for the progress-updates / Kimi cache-wait switch. */
|
|
96610
|
-
const CACHE_WAIT_WARMUP_SETTING_DESCRIPTION = "
|
|
97423
|
+
const CACHE_WAIT_WARMUP_SETTING_DESCRIPTION = "等待子代理完成期间按线路缓存 TTL 轻量续热;默认用于 Kimi(20/25 分钟),其他线路需明确 TTL 并在 promptCacheOverrides 中显式启用。";
|
|
96611
97424
|
const MAX_TRACKED_SESSIONS = 256;
|
|
96612
97425
|
/** Pure cache-wait policy; no child deadline or forced progress is involved. */
|
|
96613
97426
|
function evaluateCacheWait(input) {
|
|
@@ -96678,7 +97491,8 @@ function defaultTimer() {
|
|
|
96678
97491
|
}
|
|
96679
97492
|
};
|
|
96680
97493
|
}
|
|
96681
|
-
function defaultActivity(sessionId) {
|
|
97494
|
+
function defaultActivity(sessionId, provider = "kimi-code") {
|
|
97495
|
+
if (provider !== "kimi-code") return promptCacheActivityStore(provider).read(sessionId);
|
|
96682
97496
|
const durable = sessionCacheState.read(sessionId);
|
|
96683
97497
|
const lastActiveAt = sessionLastActiveAt(sessionId) ?? durable?.lastActiveAt;
|
|
96684
97498
|
if (lastActiveAt === void 0) return void 0;
|
|
@@ -96707,7 +97521,6 @@ var KimiCacheWaitController = class {
|
|
|
96707
97521
|
warmup;
|
|
96708
97522
|
logger;
|
|
96709
97523
|
decisionMs;
|
|
96710
|
-
ttlMs;
|
|
96711
97524
|
minWarmupTokens;
|
|
96712
97525
|
disposed = false;
|
|
96713
97526
|
constructor(options = {}) {
|
|
@@ -96721,7 +97534,6 @@ var KimiCacheWaitController = class {
|
|
|
96721
97534
|
this.warmup = typeof warmupOption === "function" ? warmupOption : () => warmupOption ?? true;
|
|
96722
97535
|
this.logger = options.logger;
|
|
96723
97536
|
this.decisionMs = options.decisionMs ?? 12e5;
|
|
96724
|
-
this.ttlMs = options.ttlMs ?? 15e5;
|
|
96725
97537
|
this.minWarmupTokens = options.minWarmupTokens ?? 0;
|
|
96726
97538
|
}
|
|
96727
97539
|
/** Observe one durable Session event. */
|
|
@@ -96854,15 +97666,15 @@ var KimiCacheWaitController = class {
|
|
|
96854
97666
|
this.timer.clear(state.timer);
|
|
96855
97667
|
state.timer = void 0;
|
|
96856
97668
|
}
|
|
96857
|
-
|
|
96858
|
-
if (this.kimiSession !== void 0) return this.kimiSession(session);
|
|
97669
|
+
providerForSession(session) {
|
|
97670
|
+
if (this.kimiSession !== void 0) return this.kimiSession(session) ? "kimi-code" : void 0;
|
|
96859
97671
|
try {
|
|
96860
97672
|
const current = text(session.requestHeader?.()?.config?.provider);
|
|
96861
|
-
if (current !== void 0) return current
|
|
97673
|
+
if (current !== void 0) return current;
|
|
96862
97674
|
} catch {}
|
|
96863
97675
|
const latest = this.providerBySession.get(session.id);
|
|
96864
|
-
if (latest !== void 0) return latest
|
|
96865
|
-
return this.activity(session.id)?.lastActiveAt !== void 0;
|
|
97676
|
+
if (latest !== void 0) return latest;
|
|
97677
|
+
return this.activity(session.id, "kimi-code")?.lastActiveAt !== void 0 ? "kimi-code" : void 0;
|
|
96866
97678
|
}
|
|
96867
97679
|
isWarmupEnabled() {
|
|
96868
97680
|
try {
|
|
@@ -96874,8 +97686,11 @@ var KimiCacheWaitController = class {
|
|
|
96874
97686
|
reconcile(session, state) {
|
|
96875
97687
|
this.clearTimer(state);
|
|
96876
97688
|
if (this.disposed || !state.waiting || state.handled || state.children.size === 0) return;
|
|
96877
|
-
|
|
96878
|
-
|
|
97689
|
+
const provider = this.providerForSession(session);
|
|
97690
|
+
if (provider === void 0) return;
|
|
97691
|
+
const cachePolicy = promptCachePolicyOf(provider, this.options.overrides?.[provider]);
|
|
97692
|
+
if (!cachePolicy.waitWarmup || cachePolicy.ttlMs === void 0) return;
|
|
97693
|
+
const activity = this.activity(session.id, provider);
|
|
96879
97694
|
if (activity === void 0 && state.lastWarmupAt === void 0) return;
|
|
96880
97695
|
const effectiveLastActiveAt = Math.max(activity?.lastActiveAt ?? -Infinity, state.lastWarmupAt ?? -Infinity);
|
|
96881
97696
|
if (!Number.isFinite(effectiveLastActiveAt)) return;
|
|
@@ -96888,8 +97703,8 @@ var KimiCacheWaitController = class {
|
|
|
96888
97703
|
totalTokens: activity?.totalTokens,
|
|
96889
97704
|
warmupEnabled: this.isWarmupEnabled(),
|
|
96890
97705
|
warmupFailed: state.warmupFailed,
|
|
96891
|
-
decisionMs: this.decisionMs,
|
|
96892
|
-
ttlMs:
|
|
97706
|
+
decisionMs: provider === "kimi-code" ? this.decisionMs : Math.max(1, Math.floor(cachePolicy.ttlMs * .8)),
|
|
97707
|
+
ttlMs: cachePolicy.ttlMs,
|
|
96893
97708
|
minWarmupTokens: this.minWarmupTokens
|
|
96894
97709
|
});
|
|
96895
97710
|
if (action.kind === "none") return;
|
|
@@ -96944,8 +97759,14 @@ function installKimiCacheWait(ctx, options = {}) {
|
|
|
96944
97759
|
* schema(`src/index.ts` 的 `Config`)与设置卡共用同一份文案:两处各写一份,
|
|
96945
97760
|
* 迟早变成「卡片说只影响新结果、schema 说立刻生效」这种自相矛盾。
|
|
96946
97761
|
*/
|
|
96947
|
-
const CACHE_WARMUP_SETTING_DESCRIPTION = "
|
|
96948
|
-
const CACHE_WARMUP_RETRIEVAL_SETTING_DESCRIPTION = "
|
|
97762
|
+
const CACHE_WARMUP_SETTING_DESCRIPTION = "按线路配置结果投影:可以可靠结构化提取的超大工具结果会被替换成带 ref:// 引用的预览,需要原文时由 cache_warmup_result 精确补读。关闭只影响之后产生的新结果,已进入历史的内容不变。";
|
|
97763
|
+
const CACHE_WARMUP_RETRIEVAL_SETTING_DESCRIPTION = "按线路配置检索型结果投影:明确导航任务中的大文件、网页和搜索结果可显示为大纲或分布预览,原文由 cache_warmup_result 补读。编辑、审查和意图不明的读取保持原文。默认开启,只影响之后的新结果。";
|
|
97764
|
+
/** 缺省设置由宿主注入 controlled provider 列表;独立使用时保持 Kimi 兼容。 */
|
|
97765
|
+
const DEFAULT_CACHE_WARMUP_SETTINGS = {
|
|
97766
|
+
enabled: true,
|
|
97767
|
+
retrievalEnabled: true,
|
|
97768
|
+
providers: ["kimi-code"]
|
|
97769
|
+
};
|
|
96949
97770
|
/**
|
|
96950
97771
|
* 读取一个配置字段,沿着 volatile 活引用取当前值。
|
|
96951
97772
|
* @param field - 字段本身。
|
|
@@ -96967,9 +97788,12 @@ function readCacheWarmupConfigField(field) {
|
|
|
96967
97788
|
function resolveCacheWarmupSettings(config) {
|
|
96968
97789
|
const enabled = readCacheWarmupConfigField(config?.cacheWarmup);
|
|
96969
97790
|
const retrievalEnabled = readCacheWarmupConfigField(config?.cacheWarmupRetrieval);
|
|
97791
|
+
const configuredProviders = readCacheWarmupConfigField(config?.cacheWarmupProviders);
|
|
97792
|
+
const providers = configuredProviders === void 0 ? DEFAULT_CACHE_WARMUP_SETTINGS.providers : [...new Set(configuredProviders.map((provider) => provider.trim()).filter((provider) => provider !== ""))];
|
|
96970
97793
|
return {
|
|
96971
97794
|
enabled: enabled !== false,
|
|
96972
|
-
retrievalEnabled: retrievalEnabled !== false
|
|
97795
|
+
retrievalEnabled: retrievalEnabled !== false,
|
|
97796
|
+
providers
|
|
96973
97797
|
};
|
|
96974
97798
|
}
|
|
96975
97799
|
//#endregion
|
|
@@ -105052,10 +105876,14 @@ const Config = z$1.object({
|
|
|
105052
105876
|
]).default("auto").volatile().description("进展更新的输出语言:auto 跟随对话语言,zh 中文,en 英文。"),
|
|
105053
105877
|
planModePipeline: z$1.boolean().default(true).volatile().description("启用计划模式管线:允许调度模式在澄清访谈收敛后用 enter_plan_mode 进入计划模式,放弃规划时用 leave_plan_mode 退出。"),
|
|
105054
105878
|
dispatchExecutionGuard: z$1.boolean().default(true).volatile().description("启用调度执行守卫:在执行期拦截子代理越权调用主控专用工具,并拦截等待子代理期间的纯 sleep 空等命令;不修改工具声明面,零损耗保护 Kimi 缓存。"),
|
|
105879
|
+
toolRunawayGuard: z$1.boolean().default(true).volatile().description("启用工具失控治理:重复调用同一工具与参数时分级提醒;Kimi 保留第 12 次强停,其他线路只提醒。"),
|
|
105055
105880
|
teamPolicyCacheGuard: z$1.boolean().default(true).volatile().description("把内置 Agent Teams 的 team:policy 段从 system prompt 摘掉,改为开关切换时以尾部用户消息投递一次,避免中途开团队协作时刷掉整段前缀缓存;默认开启。"),
|
|
105056
105881
|
cacheWarmup: z$1.boolean().default(true).volatile().description(CACHE_WARMUP_SETTING_DESCRIPTION),
|
|
105882
|
+
cacheWarmupProviders: z$1.array(z$1.string()).default(CONTROLLED_PROVIDERS).volatile().description("允许产生新结果预览的线路;默认覆盖全部受控线路,空数组表示关闭所有新投影。"),
|
|
105057
105883
|
cacheWarmupRetrieval: z$1.boolean().default(true).volatile().description(CACHE_WARMUP_RETRIEVAL_SETTING_DESCRIPTION),
|
|
105058
105884
|
cacheWaitWarmup: z$1.boolean().default(true).volatile().description(CACHE_WAIT_WARMUP_SETTING_DESCRIPTION),
|
|
105885
|
+
cacheAffinity: z$1.boolean().default(true).volatile().description(CACHE_AFFINITY_SETTING_DESCRIPTION),
|
|
105886
|
+
promptCacheOverrides: z$1.object({}).default({}).description("按线路覆盖缓存语义(ttlMs、scope、coldStart、affinity、waitWarmup 等);仅用于已有证据的部署,修改后重载插件。"),
|
|
105059
105887
|
codexStreamIdleTimeoutMs: z$1.number().min(1).max(MAX_TIMER_DELAY_MS).step(1).default(DEFAULT_STREAM_IDLE_TIMEOUT_MS$2),
|
|
105060
105888
|
poolStorage: z$1.union([z$1.const("keyring"), z$1.const("credentials")]).default("keyring").description("号池存储后端:keyring(默认,桌面平台的系统密钥环)或 credentials(DSH 凭据库,服务器推荐)。")
|
|
105061
105889
|
});
|
|
@@ -105068,6 +105896,7 @@ const inject = [
|
|
|
105068
105896
|
"loader"
|
|
105069
105897
|
];
|
|
105070
105898
|
function apply(ctx, pluginConfig = {}) {
|
|
105899
|
+
const promptCacheOverrides = parsePromptCacheOverrides(pluginConfig.promptCacheOverrides);
|
|
105071
105900
|
installHostLocale(new HostLocaleRuntime());
|
|
105072
105901
|
ctx.effect(() => () => installHostLocale(void 0), "dsh-chatgpt-subscription: host locale");
|
|
105073
105902
|
const fetchConfiguration = resolveFetchConfiguration(pluginConfig);
|
|
@@ -105134,7 +105963,10 @@ function apply(ctx, pluginConfig = {}) {
|
|
|
105134
105963
|
return () => guard.dispose();
|
|
105135
105964
|
}, "dsh-chatgpt-subscription: compaction policy guard");
|
|
105136
105965
|
ctx.effect(() => installMinimaxRuntimeEnhancements(ctx), "dsh-chatgpt-subscription: MiniMax runtime enhancements");
|
|
105137
|
-
ctx.effect(() => installKimiRepeatBreaker(ctx
|
|
105966
|
+
ctx.effect(() => installKimiRepeatBreaker(ctx, {
|
|
105967
|
+
providers: CONTROLLED_PROVIDERS.filter((provider) => provider !== "minimax-code"),
|
|
105968
|
+
globalEnabled: () => resolveToolRunawayGuardEnabled(pluginConfig)
|
|
105969
|
+
}), "dsh-chatgpt-subscription: shared tool runaway guard");
|
|
105138
105970
|
ctx.effect(() => installDispatchExecutionGuard(ctx, { enabled: () => resolveDispatchExecutionGuardEnabled(pluginConfig) }), "dsh-chatgpt-subscription: dispatch execution guard");
|
|
105139
105971
|
ctx.effect(() => installDispatchLedger(ctx, {
|
|
105140
105972
|
enabled: () => resolveDispatchExecutionGuardEnabled(pluginConfig),
|
|
@@ -105247,7 +106079,8 @@ function apply(ctx, pluginConfig = {}) {
|
|
|
105247
106079
|
}
|
|
105248
106080
|
ctx.effect(() => installCacheWarmup(ctx, {
|
|
105249
106081
|
settings: () => resolveCacheWarmupSettings(pluginConfig),
|
|
105250
|
-
durableHint: (sessionId) => {
|
|
106082
|
+
durableHint: (sessionId, provider) => {
|
|
106083
|
+
if (provider !== void 0 && provider !== "kimi-code") return void 0;
|
|
105251
106084
|
const record = sessionCacheState.read(sessionId);
|
|
105252
106085
|
if (record === void 0) return void 0;
|
|
105253
106086
|
const total = record.cachedTokens + record.freshTokens;
|
|
@@ -105259,7 +106092,10 @@ function apply(ctx, pluginConfig = {}) {
|
|
|
105259
106092
|
}
|
|
105260
106093
|
}), "dsh-chatgpt-subscription: cache warmup");
|
|
105261
106094
|
ctx.effect(() => {
|
|
105262
|
-
const dispose = installKimiCacheWait(ctx, {
|
|
106095
|
+
const dispose = installKimiCacheWait(ctx, {
|
|
106096
|
+
warmup: () => resolveCacheWaitWarmupEnabled(pluginConfig, process.env),
|
|
106097
|
+
overrides: promptCacheOverrides
|
|
106098
|
+
});
|
|
105263
106099
|
if (dispose === void 0) {
|
|
105264
106100
|
ctx.logger.warn("[dsh-chatgpt-subscription] Kimi cache wait skipped: this harness exposes no event bus");
|
|
105265
106101
|
return () => void 0;
|
|
@@ -105304,7 +106140,8 @@ function apply(ctx, pluginConfig = {}) {
|
|
|
105304
106140
|
label: PROVIDER_NAME$11,
|
|
105305
106141
|
adapter: new AntigravityAdapter(antigravityStore, antigravityModelSettings, antigravityPreferences, {
|
|
105306
106142
|
fetchFn: modelFetch.antigravity,
|
|
105307
|
-
attachments: ctx.attachments
|
|
106143
|
+
attachments: ctx.attachments,
|
|
106144
|
+
promptCache: promptCacheOverrides.antigravity
|
|
105308
106145
|
}, antigravityAccountPool)
|
|
105309
106146
|
});
|
|
105310
106147
|
const disposeAntigravityRoutes = registerAntigravityRoutes(ctx, antigravityStore, antigravityModelSettings, antigravityPreferences, antigravityProxyFetch, antigravityAccountPool);
|
|
@@ -105333,7 +106170,8 @@ function apply(ctx, pluginConfig = {}) {
|
|
|
105333
106170
|
videos: { readVideo: async (ref) => ({
|
|
105334
106171
|
data: await readVideoBytes(ref),
|
|
105335
106172
|
mediaType: ref.mediaType
|
|
105336
|
-
}) }
|
|
106173
|
+
}) },
|
|
106174
|
+
cacheAffinity: () => resolveCacheAffinityEnabled(pluginConfig, process.env)
|
|
105337
106175
|
}, kimiCodeAccountPool)
|
|
105338
106176
|
});
|
|
105339
106177
|
const ollamaClaim = claimProviderRoute(ctx, {
|
|
@@ -105378,7 +106216,8 @@ function apply(ctx, pluginConfig = {}) {
|
|
|
105378
106216
|
adapter: new MinimaxCodeAdapter(minimaxCodeStore, {
|
|
105379
106217
|
fetchFn: modelFetch["minimax-code"],
|
|
105380
106218
|
attachments: ctx.attachments,
|
|
105381
|
-
accountPool: minimaxCodeAccountPool
|
|
106219
|
+
accountPool: minimaxCodeAccountPool,
|
|
106220
|
+
promptCache: promptCacheOverrides["minimax-code"]
|
|
105382
106221
|
}, minimaxCodeModelSettings, minimaxCodePreferences)
|
|
105383
106222
|
});
|
|
105384
106223
|
let minimaxCodeAppVersion;
|
|
@@ -105415,7 +106254,8 @@ function apply(ctx, pluginConfig = {}) {
|
|
|
105415
106254
|
adapter: new ClaudeAdapter(claudeStore, claudeModelSettings, claudePreferences, {
|
|
105416
106255
|
fetchFn: modelFetch.claude,
|
|
105417
106256
|
attachments: ctx.attachments,
|
|
105418
|
-
accountPool: claudeAccountPool
|
|
106257
|
+
accountPool: claudeAccountPool,
|
|
106258
|
+
promptCache: promptCacheOverrides.claude
|
|
105419
106259
|
})
|
|
105420
106260
|
});
|
|
105421
106261
|
const disposeClaudeRoutes = registerClaudeRoutes(ctx, claudeStore, claudeModelSettings, claudePreferences, {
|
|
@@ -105430,14 +106270,16 @@ function apply(ctx, pluginConfig = {}) {
|
|
|
105430
106270
|
adapter: new StepCodeAdapter(stepCodeStore, stepCodeModelSettings, stepCodePreferences, {
|
|
105431
106271
|
fetchFn: modelFetch[PROVIDER_ID$6],
|
|
105432
106272
|
attachments: ctx.attachments,
|
|
105433
|
-
accountPool: stepCodeAccountPool
|
|
106273
|
+
accountPool: stepCodeAccountPool,
|
|
106274
|
+
promptCache: promptCacheOverrides["step-code"]
|
|
105434
106275
|
})
|
|
105435
106276
|
});
|
|
105436
106277
|
const disposeStepCodeRoutes = registerStepCodeRoutes(ctx, stepCodeSettingsSource(stepCodePreferences, stepCodeModelSettings), {
|
|
105437
106278
|
fetchFn: proxyFetch,
|
|
105438
106279
|
serving: () => stepCodeClaim.serving(),
|
|
105439
106280
|
conflict: () => stepCodeClaim.conflict(),
|
|
105440
|
-
accountPool: stepCodeAccountPool
|
|
106281
|
+
accountPool: stepCodeAccountPool,
|
|
106282
|
+
promptCache: promptCacheOverrides["step-code"]
|
|
105441
106283
|
});
|
|
105442
106284
|
const clinePassLine = createClinePassLine(ctx, {
|
|
105443
106285
|
fetchFn: proxyFetch,
|
|
@@ -105551,7 +106393,8 @@ function apply(ctx, pluginConfig = {}) {
|
|
|
105551
106393
|
outputVerbosity: () => preferences.status().outputVerbosity,
|
|
105552
106394
|
fastMode: () => preferences.status().fastMode,
|
|
105553
106395
|
reasoningSummary: () => preferences.status().reasoningSummary,
|
|
105554
|
-
codexStreamIdleTimeoutMs: pluginConfig.codexStreamIdleTimeoutMs
|
|
106396
|
+
codexStreamIdleTimeoutMs: pluginConfig.codexStreamIdleTimeoutMs,
|
|
106397
|
+
promptCache: promptCacheOverrides["codex-chatgpt"]
|
|
105555
106398
|
}), preferences, (options) => oauth.credentials(void 0, { purpose: "tool" }).then((credentials) => loadCodexCatalog(credentials, {
|
|
105556
106399
|
fetchFn: proxyFetch,
|
|
105557
106400
|
signal: options?.signal
|