@eddyskywalker/dsh-chatgpt-subscription 0.40.1 → 0.41.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 +41 -0
- package/lib/client.js +420 -15
- package/lib/client.js.map +1 -1
- package/lib/index.js +2101 -335
- package/lib/types/client/common/AccountPoolSection.d.ts +19 -0
- package/lib/types/client/common/AccountPoolSection.d.ts.map +1 -1
- package/lib/types/client/kimi-code/KimiCodeSection.d.ts.map +1 -1
- package/lib/types/client/kimi-code/KimiQuotaCard.d.ts +31 -0
- package/lib/types/client/kimi-code/KimiQuotaCard.d.ts.map +1 -0
- package/lib/types/client/kimi-code/locales.d.ts +74 -2
- package/lib/types/client/kimi-code/locales.d.ts.map +1 -1
- package/lib/types/client/kimi-code/staff-art.d.ts +19 -0
- package/lib/types/client/kimi-code/staff-art.d.ts.map +1 -0
- package/lib/types/client/kimi-code/styles.d.ts.map +1 -1
- package/lib/types/client/locales-ru/kimi-code.d.ts.map +1 -1
- package/lib/types/compat.d.ts +1 -1
- package/lib/types/host/antigravity/adapter.d.ts.map +1 -1
- package/lib/types/host/antigravity/mapper.d.ts.map +1 -1
- package/lib/types/host/cline-pass/adapter.d.ts +2 -0
- package/lib/types/host/cline-pass/adapter.d.ts.map +1 -1
- package/lib/types/host/common/request-images.d.ts +15 -5
- package/lib/types/host/common/request-images.d.ts.map +1 -1
- package/lib/types/host/kimi-code/adapter.d.ts +172 -0
- package/lib/types/host/kimi-code/adapter.d.ts.map +1 -1
- package/lib/types/host/kimi-code/cache-hint.d.ts +5 -5
- package/lib/types/host/kimi-code/client.d.ts.map +1 -1
- package/lib/types/host/kimi-code/mapper.d.ts +166 -1
- package/lib/types/host/kimi-code/mapper.d.ts.map +1 -1
- package/lib/types/host/kimi-code/media-files.d.ts +232 -0
- package/lib/types/host/kimi-code/media-files.d.ts.map +1 -0
- package/lib/types/host/kimi-code/routes.d.ts.map +1 -1
- package/lib/types/host/kimi-code/session-cache-state.d.ts +140 -0
- package/lib/types/host/kimi-code/session-cache-state.d.ts.map +1 -0
- package/lib/types/host/progress-updates/index.d.ts.map +1 -1
- package/lib/types/host/progress-updates/prompt.d.ts +6 -3
- package/lib/types/host/progress-updates/prompt.d.ts.map +1 -1
- package/lib/types/index.d.ts.map +1 -1
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -141,7 +141,7 @@ const ROUTE_PREFIX$4 = "/api/dsh-chatgpt-subscription";
|
|
|
141
141
|
* `test/responses-client.test.ts` asserts the two agree, so a release that
|
|
142
142
|
* forgets this constant fails the suite instead of shipping quietly.
|
|
143
143
|
*/
|
|
144
|
-
const PLUGIN_VERSION = "0.
|
|
144
|
+
const PLUGIN_VERSION = "0.41.0";
|
|
145
145
|
const CODEX_CHATGPT_PROVIDER_ID = "codex-chatgpt";
|
|
146
146
|
const CODEX_API_BASE = "https://chatgpt.com/backend-api/codex";
|
|
147
147
|
const CODEX_RESPONSES_URL = `${CODEX_API_BASE}/responses`;
|
|
@@ -3435,7 +3435,7 @@ var OAuthService = class {
|
|
|
3435
3435
|
throw new OAuthServiceError("oauth-token-exchange-failed", `The ChatGPT device code could not be issued (${response.status}${detail === null ? "" : `, ${detail}`}).`, response.status);
|
|
3436
3436
|
}
|
|
3437
3437
|
const payload = await response.json().catch(() => null);
|
|
3438
|
-
const record = isRecord$
|
|
3438
|
+
const record = isRecord$51(payload) ? payload : {};
|
|
3439
3439
|
const authId = nonEmptyString$10(record.device_auth_id);
|
|
3440
3440
|
const userCode = nonEmptyString$10(record.user_code) ?? nonEmptyString$10(record.usercode);
|
|
3441
3441
|
if (authId === void 0 || userCode === void 0) throw new OAuthServiceError("oauth-token-exchange-failed", "ChatGPT returned no device code.");
|
|
@@ -3502,7 +3502,7 @@ var OAuthService = class {
|
|
|
3502
3502
|
const detail = deviceErrorIdentifier(payload);
|
|
3503
3503
|
throw new OAuthServiceError("oauth-token-exchange-failed", `The ChatGPT device confirmation was refused (${response.status}${detail === void 0 ? "" : `, ${detail}`}).`, response.status);
|
|
3504
3504
|
}
|
|
3505
|
-
const record = isRecord$
|
|
3505
|
+
const record = isRecord$51(payload) ? payload : {};
|
|
3506
3506
|
const authorizationCode = nonEmptyString$10(record.authorization_code);
|
|
3507
3507
|
const codeVerifier = nonEmptyString$10(record.code_verifier);
|
|
3508
3508
|
if (authorizationCode === void 0 || codeVerifier === void 0) throw new OAuthServiceError("oauth-token-exchange-failed", "ChatGPT confirmed the device code but returned no authorization code.");
|
|
@@ -3849,11 +3849,11 @@ const DEVICE_PENDING_STATUSES = /* @__PURE__ */ new Set([
|
|
|
3849
3849
|
function isPendingDeviceAnswer(response, payload) {
|
|
3850
3850
|
if (response.status === 403 || response.status === 404) return true;
|
|
3851
3851
|
if (!response.ok && response.status !== 400) return false;
|
|
3852
|
-
if (!isRecord$
|
|
3852
|
+
if (!isRecord$51(payload)) return false;
|
|
3853
3853
|
const status = payload.status;
|
|
3854
3854
|
if (typeof status === "string" && DEVICE_PENDING_STATUSES.has(status.toLowerCase())) return true;
|
|
3855
3855
|
const error = payload.error;
|
|
3856
|
-
if (isRecord$
|
|
3856
|
+
if (isRecord$51(error)) {
|
|
3857
3857
|
const code = error.code;
|
|
3858
3858
|
if (typeof code === "string" && DEVICE_PENDING_STATUSES.has(code.toLowerCase())) return true;
|
|
3859
3859
|
}
|
|
@@ -3867,18 +3867,18 @@ function isPendingDeviceAnswer(response, payload) {
|
|
|
3867
3867
|
* wrong.
|
|
3868
3868
|
*/
|
|
3869
3869
|
function deviceErrorIdentifier(payload) {
|
|
3870
|
-
if (!isRecord$
|
|
3870
|
+
if (!isRecord$51(payload)) return void 0;
|
|
3871
3871
|
const candidates = [payload.code];
|
|
3872
3872
|
const error = payload.error;
|
|
3873
3873
|
if (typeof error === "string") candidates.push(error);
|
|
3874
|
-
else if (isRecord$
|
|
3874
|
+
else if (isRecord$51(error)) candidates.push(error.code, error.type);
|
|
3875
3875
|
candidates.push(payload.status);
|
|
3876
3876
|
for (const candidate of candidates) if (typeof candidate === "string" && /^[a-z0-9_.-]{1,64}$/i.test(candidate)) return candidate;
|
|
3877
3877
|
}
|
|
3878
3878
|
function nonEmptyString$10(value) {
|
|
3879
3879
|
return typeof value === "string" && value !== "" ? value : void 0;
|
|
3880
3880
|
}
|
|
3881
|
-
function isRecord$
|
|
3881
|
+
function isRecord$51(value) {
|
|
3882
3882
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3883
3883
|
}
|
|
3884
3884
|
async function oauthErrorIdentifier(response) {
|
|
@@ -4061,7 +4061,7 @@ const CODEX_AUTH_CLAIMS_KEY = "https://api.openai.com/auth";
|
|
|
4061
4061
|
* here, and the import has to be repeated afterwards.
|
|
4062
4062
|
*/
|
|
4063
4063
|
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.";
|
|
4064
|
-
function isRecord$
|
|
4064
|
+
function isRecord$50(value) {
|
|
4065
4065
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4066
4066
|
}
|
|
4067
4067
|
/** A trimmed, non-empty string, or nothing. */
|
|
@@ -4163,7 +4163,7 @@ function decodeJwtClaims$1(token) {
|
|
|
4163
4163
|
if (parts.length !== 3) return void 0;
|
|
4164
4164
|
try {
|
|
4165
4165
|
const value = JSON.parse(Buffer.from(parts[1], "base64url").toString("utf8"));
|
|
4166
|
-
return isRecord$
|
|
4166
|
+
return isRecord$50(value) ? value : void 0;
|
|
4167
4167
|
} catch {
|
|
4168
4168
|
return;
|
|
4169
4169
|
}
|
|
@@ -4208,10 +4208,10 @@ function expiryFromAccessToken(accessToken) {
|
|
|
4208
4208
|
* the file carries cannot reach the result.
|
|
4209
4209
|
*/
|
|
4210
4210
|
function parseAdoptedCodexCredential(value, sourcePath) {
|
|
4211
|
-
if (!isRecord$
|
|
4211
|
+
if (!isRecord$50(value)) return void 0;
|
|
4212
4212
|
if (value.auth_mode !== "chatgpt") return void 0;
|
|
4213
4213
|
const tokens = value[CODEX_CLI_TOKENS_KEY];
|
|
4214
|
-
if (!isRecord$
|
|
4214
|
+
if (!isRecord$50(tokens)) return void 0;
|
|
4215
4215
|
const accessToken = nonEmptyString$9(tokens.access_token);
|
|
4216
4216
|
if (accessToken === void 0) return void 0;
|
|
4217
4217
|
const refreshToken = nonEmptyString$9(tokens.refresh_token);
|
|
@@ -4230,7 +4230,7 @@ function parseAdoptedCodexCredential(value, sourcePath) {
|
|
|
4230
4230
|
const email = claims === void 0 ? void 0 : nonEmptyString$9(claims.email);
|
|
4231
4231
|
if (email !== void 0) credentials.email = email;
|
|
4232
4232
|
const authClaims = claims === void 0 ? void 0 : claims[CODEX_AUTH_CLAIMS_KEY];
|
|
4233
|
-
const planType = isRecord$
|
|
4233
|
+
const planType = isRecord$50(authClaims) ? nonEmptyString$9(authClaims.chatgpt_plan_type) : void 0;
|
|
4234
4234
|
if (planType !== void 0) credentials.planType = planType;
|
|
4235
4235
|
}
|
|
4236
4236
|
const accountId = nonEmptyString$9(tokens.account_id);
|
|
@@ -4999,13 +4999,13 @@ function webServerOrigin(server) {
|
|
|
4999
4999
|
//#region src/host/common/llm-compat.ts
|
|
5000
5000
|
/** This package's kind in the harness message-source map. */
|
|
5001
5001
|
const PLUGIN_MESSAGE_SOURCE_KIND = "dsh-chatgpt-subscription";
|
|
5002
|
-
function isRecord$
|
|
5002
|
+
function isRecord$49(value) {
|
|
5003
5003
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
5004
5004
|
}
|
|
5005
5005
|
/** Read a message body's blocks, keeping every entry that carries a block tag. */
|
|
5006
5006
|
function readBlocks(raw) {
|
|
5007
5007
|
if (!Array.isArray(raw)) return [];
|
|
5008
|
-
return raw.filter((block) => isRecord$
|
|
5008
|
+
return raw.filter((block) => isRecord$49(block) && typeof block.type === "string");
|
|
5009
5009
|
}
|
|
5010
5010
|
/**
|
|
5011
5011
|
* Normalize one harness message list into {@link Message}.
|
|
@@ -5020,10 +5020,10 @@ function readBlocks(raw) {
|
|
|
5020
5020
|
function normalizeMessages(messages) {
|
|
5021
5021
|
const normalized = [];
|
|
5022
5022
|
for (const raw of messages ?? []) {
|
|
5023
|
-
if (!isRecord$
|
|
5023
|
+
if (!isRecord$49(raw)) continue;
|
|
5024
5024
|
const content = readBlocks(raw.content);
|
|
5025
5025
|
if (raw.role === "tool") {
|
|
5026
|
-
const source = isRecord$
|
|
5026
|
+
const source = isRecord$49(raw.source) ? raw.source : void 0;
|
|
5027
5027
|
const callId = String(raw.toolCallId ?? source?.callId ?? "");
|
|
5028
5028
|
normalized.push({
|
|
5029
5029
|
id: raw.id,
|
|
@@ -5045,7 +5045,7 @@ function normalizeMessages(messages) {
|
|
|
5045
5045
|
id: raw.id,
|
|
5046
5046
|
role: typeof raw.role === "string" ? raw.role : "user",
|
|
5047
5047
|
content,
|
|
5048
|
-
...isRecord$
|
|
5048
|
+
...isRecord$49(raw.source) ? { source: raw.source } : {}
|
|
5049
5049
|
});
|
|
5050
5050
|
}
|
|
5051
5051
|
return normalized;
|
|
@@ -6749,7 +6749,7 @@ const schemaFields = /* @__PURE__ */ new Set([
|
|
|
6749
6749
|
"required",
|
|
6750
6750
|
"propertyOrdering"
|
|
6751
6751
|
]);
|
|
6752
|
-
function isRecord$
|
|
6752
|
+
function isRecord$48(value) {
|
|
6753
6753
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6754
6754
|
}
|
|
6755
6755
|
function mergeSchemas(left, right) {
|
|
@@ -6757,7 +6757,7 @@ function mergeSchemas(left, right) {
|
|
|
6757
6757
|
...left,
|
|
6758
6758
|
...right
|
|
6759
6759
|
};
|
|
6760
|
-
if (isRecord$
|
|
6760
|
+
if (isRecord$48(left.properties) && isRecord$48(right.properties)) merged.properties = {
|
|
6761
6761
|
...left.properties,
|
|
6762
6762
|
...right.properties
|
|
6763
6763
|
};
|
|
@@ -6793,9 +6793,9 @@ const mergedTypeKeys = /* @__PURE__ */ new Set([
|
|
|
6793
6793
|
* model with strictly less than the schema it started from.
|
|
6794
6794
|
*/
|
|
6795
6795
|
function describeBranch(branch) {
|
|
6796
|
-
const type = typeof branch.type === "string" ? branch.type : isRecord$
|
|
6796
|
+
const type = typeof branch.type === "string" ? branch.type : isRecord$48(branch.properties) ? "object" : isRecord$48(branch.items) ? "array" : "value";
|
|
6797
6797
|
const details = [];
|
|
6798
|
-
if (isRecord$
|
|
6798
|
+
if (isRecord$48(branch.properties)) {
|
|
6799
6799
|
const names = Object.keys(branch.properties);
|
|
6800
6800
|
if (names.length > 0) details.push(`{${names.join(",")}}`);
|
|
6801
6801
|
}
|
|
@@ -6809,7 +6809,7 @@ function describeBranch(branch) {
|
|
|
6809
6809
|
}
|
|
6810
6810
|
/** How well one branch can stand in for a whole union; a named object shape wins. */
|
|
6811
6811
|
function branchRank(branch) {
|
|
6812
|
-
if (isRecord$
|
|
6812
|
+
if (isRecord$48(branch.properties)) return 3;
|
|
6813
6813
|
if (branch.type === "object") return 2;
|
|
6814
6814
|
if (branch.type === "array") return 1;
|
|
6815
6815
|
return 0;
|
|
@@ -6838,7 +6838,7 @@ function unionShape(branches) {
|
|
|
6838
6838
|
shape: branches[0],
|
|
6839
6839
|
folded: false
|
|
6840
6840
|
};
|
|
6841
|
-
if (new Set(branches.map((branch) => branch.type)).size === 1 && branches.every((branch) => !isRecord$
|
|
6841
|
+
if (new Set(branches.map((branch) => branch.type)).size === 1 && branches.every((branch) => !isRecord$48(branch.properties) && !isRecord$48(branch.items))) {
|
|
6842
6842
|
const shape = { type: branches[0].type };
|
|
6843
6843
|
if (branches.every((branch) => Array.isArray(branch.enum))) {
|
|
6844
6844
|
const values = [...new Set(branches.flatMap((branch) => branch.enum))];
|
|
@@ -6875,7 +6875,7 @@ function foldOwnFields(node) {
|
|
|
6875
6875
|
const out = {};
|
|
6876
6876
|
for (const [key, value] of Object.entries(node)) {
|
|
6877
6877
|
if (key === "anyOf" || key === "oneOf" || key === "allOf") continue;
|
|
6878
|
-
if (key === "properties" && isRecord$
|
|
6878
|
+
if (key === "properties" && isRecord$48(value)) {
|
|
6879
6879
|
out.properties = Object.fromEntries(Object.entries(value).map(([name, child]) => [name, foldSchemaUnions(child)]));
|
|
6880
6880
|
continue;
|
|
6881
6881
|
}
|
|
@@ -6904,15 +6904,15 @@ function foldOwnFields(node) {
|
|
|
6904
6904
|
* argument the model sends anyway fails there, naming the actual problem.
|
|
6905
6905
|
*/
|
|
6906
6906
|
function foldSchemaUnions(node) {
|
|
6907
|
-
if (!isRecord$
|
|
6907
|
+
if (!isRecord$48(node)) return node;
|
|
6908
6908
|
let own = foldOwnFields(node);
|
|
6909
6909
|
if (Array.isArray(node.allOf)) for (const branch of node.allOf) {
|
|
6910
6910
|
const folded = foldSchemaUnions(branch);
|
|
6911
|
-
if (isRecord$
|
|
6911
|
+
if (isRecord$48(folded)) own = mergeSchemas(own, folded);
|
|
6912
6912
|
}
|
|
6913
6913
|
const alternatives = Array.isArray(node.anyOf) ? node.anyOf : Array.isArray(node.oneOf) ? node.oneOf : [];
|
|
6914
6914
|
if (alternatives.length === 0) return own;
|
|
6915
|
-
const branches = alternatives.map((branch) => foldSchemaUnions(branch)).filter(isRecord$
|
|
6915
|
+
const branches = alternatives.map((branch) => foldSchemaUnions(branch)).filter(isRecord$48);
|
|
6916
6916
|
if (branches.length === 0) return own;
|
|
6917
6917
|
const shapes = branches.filter((branch) => branch.type !== "null");
|
|
6918
6918
|
const acceptsNull = branches.some((branch) => branch.type === "null");
|
|
@@ -6928,7 +6928,7 @@ function foldSchemaUnions(node) {
|
|
|
6928
6928
|
return merged;
|
|
6929
6929
|
}
|
|
6930
6930
|
function toAntigravityToolSchema(schema, options = {}) {
|
|
6931
|
-
if (!isRecord$
|
|
6931
|
+
if (!isRecord$48(schema)) return schema;
|
|
6932
6932
|
const root = schema;
|
|
6933
6933
|
function resolveReference(reference) {
|
|
6934
6934
|
let target = root;
|
|
@@ -6936,13 +6936,13 @@ function toAntigravityToolSchema(schema, options = {}) {
|
|
|
6936
6936
|
const path = reference === "#" ? [] : reference.slice(2).split("/");
|
|
6937
6937
|
for (const segment of path) {
|
|
6938
6938
|
const key = segment.replace(/~1/g, "/").replace(/~0/g, "~");
|
|
6939
|
-
target = isRecord$
|
|
6939
|
+
target = isRecord$48(target) && Object.hasOwn(target, key) ? target[key] : void 0;
|
|
6940
6940
|
}
|
|
6941
|
-
if (!isRecord$
|
|
6941
|
+
if (!isRecord$48(target)) throw new LlmError(`Antigravity tool schema reference was not found: ${reference}`, "PROVIDER_ERROR");
|
|
6942
6942
|
return target;
|
|
6943
6943
|
}
|
|
6944
6944
|
function convert(node, references) {
|
|
6945
|
-
if (!isRecord$
|
|
6945
|
+
if (!isRecord$48(node)) return {};
|
|
6946
6946
|
let inherited = {};
|
|
6947
6947
|
if (typeof node.$ref === "string") {
|
|
6948
6948
|
if (references.has(node.$ref)) throw new LlmError(`Antigravity tool schema contains a recursive reference: ${node.$ref}`, "PROVIDER_ERROR");
|
|
@@ -6951,8 +6951,8 @@ function toAntigravityToolSchema(schema, options = {}) {
|
|
|
6951
6951
|
if (Array.isArray(node.allOf)) for (const branch of node.allOf) inherited = mergeSchemas(inherited, convert(branch, references));
|
|
6952
6952
|
const out = {};
|
|
6953
6953
|
for (const [key, value] of Object.entries(node)) if (schemaFields.has(key)) out[key] = value;
|
|
6954
|
-
if (isRecord$
|
|
6955
|
-
if (isRecord$
|
|
6954
|
+
if (isRecord$48(node.properties)) out.properties = Object.fromEntries(Object.entries(node.properties).map(([name, child]) => [name, convert(child, references)]));
|
|
6955
|
+
if (isRecord$48(node.items)) out.items = convert(node.items, references);
|
|
6956
6956
|
const alternatives = Array.isArray(node.anyOf) ? node.anyOf : node.oneOf;
|
|
6957
6957
|
if (Array.isArray(alternatives)) out.anyOf = alternatives.map((child) => convert(child, references));
|
|
6958
6958
|
if (Array.isArray(node.type)) {
|
|
@@ -6988,7 +6988,7 @@ let toolCallCounter = 0;
|
|
|
6988
6988
|
function sanitizeText$8(text) {
|
|
6989
6989
|
return text.replace(/\0/g, "");
|
|
6990
6990
|
}
|
|
6991
|
-
function isRecord$
|
|
6991
|
+
function isRecord$47(value) {
|
|
6992
6992
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6993
6993
|
}
|
|
6994
6994
|
function asString$24(value) {
|
|
@@ -7008,25 +7008,25 @@ function toolCallIdNeeded(modelId, runtimeModel) {
|
|
|
7008
7008
|
return modelId.startsWith("claude-") || modelId.startsWith("gpt-oss-") || runtimeModel.startsWith("claude-") || runtimeModel.startsWith("gpt-oss-");
|
|
7009
7009
|
}
|
|
7010
7010
|
function parseArguments$2(raw) {
|
|
7011
|
-
if (isRecord$
|
|
7011
|
+
if (isRecord$47(raw)) return raw;
|
|
7012
7012
|
if (raw === void 0 || raw === null || raw === "") return {};
|
|
7013
7013
|
const parsed = typeof raw === "string" ? safeJsonParse$8(raw) : raw;
|
|
7014
|
-
return isRecord$
|
|
7014
|
+
return isRecord$47(parsed) ? parsed : {};
|
|
7015
7015
|
}
|
|
7016
7016
|
const NO_RESOLVED_IMAGES$5 = /* @__PURE__ */ new Map();
|
|
7017
7017
|
function attachmentOf$5(block) {
|
|
7018
7018
|
const attachment = block.attachment;
|
|
7019
|
-
if (!isRecord$
|
|
7019
|
+
if (!isRecord$47(attachment)) return void 0;
|
|
7020
7020
|
return typeof attachment.attachmentId === "string" ? attachment : void 0;
|
|
7021
7021
|
}
|
|
7022
7022
|
function attachmentLabel$6(block) {
|
|
7023
|
-
const attachment = isRecord$
|
|
7023
|
+
const attachment = isRecord$47(block.attachment) ? block.attachment : void 0;
|
|
7024
7024
|
return asString$24(attachment?.name) || asString$24(attachment?.attachmentId);
|
|
7025
7025
|
}
|
|
7026
7026
|
function collectImageRefs$3(content, refs) {
|
|
7027
7027
|
if (!Array.isArray(content)) return;
|
|
7028
7028
|
for (const block of content) {
|
|
7029
|
-
if (!isRecord$
|
|
7029
|
+
if (!isRecord$47(block)) continue;
|
|
7030
7030
|
if (block.type === "image") {
|
|
7031
7031
|
const attachment = attachmentOf$5(block);
|
|
7032
7032
|
if (attachment) refs.set(attachment.attachmentId, attachment);
|
|
@@ -7139,7 +7139,7 @@ function requestImageBytes$2(block) {
|
|
|
7139
7139
|
function collectRequestImageBytes$2(content, lengths) {
|
|
7140
7140
|
if (!Array.isArray(content)) return;
|
|
7141
7141
|
for (const block of content) {
|
|
7142
|
-
if (!isRecord$
|
|
7142
|
+
if (!isRecord$47(block)) continue;
|
|
7143
7143
|
if (block.type === "tool-result") {
|
|
7144
7144
|
collectRequestImageBytes$2(block.content, lengths);
|
|
7145
7145
|
continue;
|
|
@@ -7189,7 +7189,7 @@ function offloadOldestRequestImages$3(options) {
|
|
|
7189
7189
|
let replaced = false;
|
|
7190
7190
|
return {
|
|
7191
7191
|
content: content.map((block) => {
|
|
7192
|
-
if (remaining.count === 0 || !isRecord$
|
|
7192
|
+
if (remaining.count === 0 || !isRecord$47(block)) return block;
|
|
7193
7193
|
if (block.type === "image") {
|
|
7194
7194
|
if (requestImageBytes$2(block) === void 0) return block;
|
|
7195
7195
|
remaining.count -= 1;
|
|
@@ -7292,7 +7292,7 @@ function unavailableImageText$6(block) {
|
|
|
7292
7292
|
}
|
|
7293
7293
|
function imageBlockToPart(block, images) {
|
|
7294
7294
|
let data = asString$24(block.data) || asString$24(block.base64);
|
|
7295
|
-
const source = isRecord$
|
|
7295
|
+
const source = isRecord$47(block.source) ? block.source : void 0;
|
|
7296
7296
|
if (!data && source) data = asString$24(source.data) || asString$24(source.base64);
|
|
7297
7297
|
let mimeType = asString$24(block.mimeType) || asString$24(block.mediaType) || (source ? asString$24(source.mimeType) || asString$24(source.mediaType) : void 0) || "image/png";
|
|
7298
7298
|
if (data?.startsWith("data:")) {
|
|
@@ -7317,8 +7317,8 @@ function contentToUserParts(content, images) {
|
|
|
7317
7317
|
if (typeof content === "string") return [{ text: sanitizeText$8(content) }];
|
|
7318
7318
|
if (!Array.isArray(content)) return [];
|
|
7319
7319
|
const parts = [];
|
|
7320
|
-
for (const block of content) if (isRecord$
|
|
7321
|
-
else if (isRecord$
|
|
7320
|
+
for (const block of content) if (isRecord$47(block) && block.type === "text" && typeof block.text === "string") parts.push({ text: sanitizeText$8(block.text) });
|
|
7321
|
+
else if (isRecord$47(block) && block.type === "image") {
|
|
7322
7322
|
const img = imageBlockToPart(block, images);
|
|
7323
7323
|
parts.push(img ?? { text: unavailableImageText$6(block) });
|
|
7324
7324
|
}
|
|
@@ -7333,7 +7333,7 @@ function toolResultImageParts(blocks, images) {
|
|
|
7333
7333
|
if (!Array.isArray(blocks)) return [];
|
|
7334
7334
|
const parts = [];
|
|
7335
7335
|
for (const block of blocks) {
|
|
7336
|
-
if (!isRecord$
|
|
7336
|
+
if (!isRecord$47(block)) continue;
|
|
7337
7337
|
if (block.type === "image") {
|
|
7338
7338
|
parts.push(imageBlockToPart(block, images) ?? { text: unavailableImageText$6(block) });
|
|
7339
7339
|
continue;
|
|
@@ -7345,7 +7345,7 @@ function toolResultImageParts(blocks, images) {
|
|
|
7345
7345
|
function toolResultText$8(blocks) {
|
|
7346
7346
|
if (!Array.isArray(blocks)) return "";
|
|
7347
7347
|
return blocks.map((block) => {
|
|
7348
|
-
if (!isRecord$
|
|
7348
|
+
if (!isRecord$47(block)) return "";
|
|
7349
7349
|
if (block.type === "text" && typeof block.text === "string") return sanitizeText$8(block.text);
|
|
7350
7350
|
if (block.type === "tool-result") return toolResultText$8(block.content);
|
|
7351
7351
|
if (block.type === "image") {
|
|
@@ -7359,9 +7359,9 @@ function replayBlockFor$1(message, index) {
|
|
|
7359
7359
|
const source = message.source;
|
|
7360
7360
|
if (!source || source.kind !== "model" || source.provider !== "antigravity") return void 0;
|
|
7361
7361
|
const state = source.replayState;
|
|
7362
|
-
if (!isRecord$
|
|
7362
|
+
if (!isRecord$47(state)) return void 0;
|
|
7363
7363
|
if (Array.isArray(state.blocks)) return state.blocks[index];
|
|
7364
|
-
const resp = isRecord$
|
|
7364
|
+
const resp = isRecord$47(state.response) ? state.response : void 0;
|
|
7365
7365
|
if (resp) {
|
|
7366
7366
|
if (Array.isArray(resp.outputItems)) return resp.outputItems[index];
|
|
7367
7367
|
if (Array.isArray(resp.blocks)) return resp.blocks[index];
|
|
@@ -7463,10 +7463,10 @@ function assistantParts(message, model, runtimeModel, toolCalls) {
|
|
|
7463
7463
|
if (!Array.isArray(message.content)) return parts;
|
|
7464
7464
|
for (let index = 0; index < message.content.length; index++) {
|
|
7465
7465
|
const block = message.content[index];
|
|
7466
|
-
if (!isRecord$
|
|
7466
|
+
if (!isRecord$47(block)) continue;
|
|
7467
7467
|
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;
|
|
7468
7468
|
const replay = replayBlockFor$1(message, index);
|
|
7469
|
-
const originalParts = Array.isArray(replay?.parts) ? replay.parts.filter(isRecord$
|
|
7469
|
+
const originalParts = Array.isArray(replay?.parts) ? replay.parts.filter(isRecord$47) : [];
|
|
7470
7470
|
if ((block.type === "text" || block.type === "reasoning") && originalParts.length > 0 && originalParts.every((part) => !part.functionCall) && originalParts.map((part) => asString$24(part.text) || "").join("") === sanitizeText$8(String(block.text || ""))) {
|
|
7471
7471
|
parts.push(...originalParts.map(replayPart));
|
|
7472
7472
|
continue;
|
|
@@ -7487,8 +7487,8 @@ function assistantParts(message, model, runtimeModel, toolCalls) {
|
|
|
7487
7487
|
} else if (block.type === "tool-call") {
|
|
7488
7488
|
const toolId = String(block.id || "");
|
|
7489
7489
|
const toolName = String(block.name || "");
|
|
7490
|
-
const originalCall = originalParts.find((part) => isRecord$
|
|
7491
|
-
const wireId = asString$24((isRecord$
|
|
7490
|
+
const originalCall = originalParts.find((part) => isRecord$47(part.functionCall));
|
|
7491
|
+
const wireId = asString$24((isRecord$47(originalCall?.functionCall) ? originalCall.functionCall : void 0)?.id) || (toolCallIdNeeded(model.id, runtimeModel) ? sanitizeToolCallId(toolId, toolName) : originalCall ? void 0 : toolId || void 0);
|
|
7492
7492
|
toolCalls.set(toolId, {
|
|
7493
7493
|
name: toolName,
|
|
7494
7494
|
id: wireId
|
|
@@ -7540,7 +7540,7 @@ function convertMessages(options, model, runtimeModel, images = NO_RESOLVED_IMAG
|
|
|
7540
7540
|
continue;
|
|
7541
7541
|
}
|
|
7542
7542
|
const content = Array.isArray(message.content) ? message.content : [];
|
|
7543
|
-
const userParts = contentToUserParts(content.filter((b) => !isRecord$
|
|
7543
|
+
const userParts = contentToUserParts(content.filter((b) => !isRecord$47(b) || b.type !== "tool-result"), images);
|
|
7544
7544
|
if (role === "system") {
|
|
7545
7545
|
if (userParts.length) contents.push({
|
|
7546
7546
|
role: GEMINI_ROLE.user,
|
|
@@ -7552,7 +7552,7 @@ function convertMessages(options, model, runtimeModel, images = NO_RESOLVED_IMAG
|
|
|
7552
7552
|
role: GEMINI_ROLE.user,
|
|
7553
7553
|
parts: userParts
|
|
7554
7554
|
});
|
|
7555
|
-
for (const b of content) if (isRecord$
|
|
7555
|
+
for (const b of content) if (isRecord$47(b) && b.type === "tool-result") pushToolResult(contents, b, toolCalls, model, runtimeModel, images);
|
|
7556
7556
|
}
|
|
7557
7557
|
return contents;
|
|
7558
7558
|
}
|
|
@@ -7700,7 +7700,7 @@ const USAGE_FIELDS = [
|
|
|
7700
7700
|
"totalTokenCount"
|
|
7701
7701
|
];
|
|
7702
7702
|
function collectUsage(value, state) {
|
|
7703
|
-
if (!isRecord$
|
|
7703
|
+
if (!isRecord$47(value)) return;
|
|
7704
7704
|
for (const key of USAGE_FIELDS) {
|
|
7705
7705
|
const count = value[key];
|
|
7706
7706
|
if (typeof count !== "number" || !Number.isSafeInteger(count) || count < 0) continue;
|
|
@@ -7730,17 +7730,17 @@ function processStreamLine$3(line, state) {
|
|
|
7730
7730
|
}
|
|
7731
7731
|
if (!json) return [];
|
|
7732
7732
|
const chunk = safeJsonParse$8(json);
|
|
7733
|
-
if (!isRecord$
|
|
7734
|
-
const responseData = isRecord$
|
|
7735
|
-
const error = isRecord$
|
|
7733
|
+
if (!isRecord$47(chunk)) return [];
|
|
7734
|
+
const responseData = isRecord$47(chunk.response) ? chunk.response : chunk;
|
|
7735
|
+
const error = isRecord$47(chunk.error) ? chunk.error : isRecord$47(responseData.error) ? responseData.error : void 0;
|
|
7736
7736
|
if (error !== void 0) throw new LlmError$1(`Antigravity stream error: ${asString$24(error.message) ?? "unknown error"}`, isContextOverflow(error) ? CONTEXT_OVERFLOW_CODE : "PROVIDER_ERROR");
|
|
7737
7737
|
const candidates = Array.isArray(responseData.candidates) ? responseData.candidates : [];
|
|
7738
|
-
const candidate = isRecord$
|
|
7739
|
-
const content = isRecord$
|
|
7738
|
+
const candidate = isRecord$47(candidates[0]) ? candidates[0] : void 0;
|
|
7739
|
+
const content = isRecord$47(candidate?.content) ? candidate.content : void 0;
|
|
7740
7740
|
const parts = Array.isArray(content?.parts) ? content.parts : [];
|
|
7741
7741
|
const out = [];
|
|
7742
7742
|
for (const part of parts) {
|
|
7743
|
-
if (!isRecord$
|
|
7743
|
+
if (!isRecord$47(part)) continue;
|
|
7744
7744
|
if (typeof part.text === "string" && part.text !== "") {
|
|
7745
7745
|
const isThinking = Boolean(part.thought);
|
|
7746
7746
|
const blockType = isThinking ? "reasoning" : "text";
|
|
@@ -7772,7 +7772,7 @@ function processStreamLine$3(line, state) {
|
|
|
7772
7772
|
index: state.currentBlock.index,
|
|
7773
7773
|
text: delta
|
|
7774
7774
|
});
|
|
7775
|
-
} else if (!isRecord$
|
|
7775
|
+
} else if (!isRecord$47(part.functionCall) && thoughtSignature(part)) {
|
|
7776
7776
|
if (state.replayBlocks.length === 0) {
|
|
7777
7777
|
const type = part.thought ? "reasoning" : "text";
|
|
7778
7778
|
state.blocks.push({
|
|
@@ -7796,12 +7796,12 @@ function processStreamLine$3(line, state) {
|
|
|
7796
7796
|
}
|
|
7797
7797
|
state.replayBlocks[state.replayBlocks.length - 1].parts.push(replayPart(part));
|
|
7798
7798
|
}
|
|
7799
|
-
if (isRecord$
|
|
7799
|
+
if (isRecord$47(part.functionCall)) {
|
|
7800
7800
|
out.push(...closeCurrentBlock(state));
|
|
7801
7801
|
const fc = part.functionCall;
|
|
7802
7802
|
const toolName = asString$24(fc.name) || "";
|
|
7803
7803
|
const toolId = asString$24(fc.id) || sanitizeToolCallId("", toolName);
|
|
7804
|
-
const argsText = JSON.stringify(isRecord$
|
|
7804
|
+
const argsText = JSON.stringify(isRecord$47(fc.args) ? fc.args : {});
|
|
7805
7805
|
const index = state.blocks.length;
|
|
7806
7806
|
const block = {
|
|
7807
7807
|
type: "tool-call",
|
|
@@ -7840,7 +7840,7 @@ function processStreamLine$3(line, state) {
|
|
|
7840
7840
|
if (responseData !== chunk) collectUsage(responseData.usageMetadata, state);
|
|
7841
7841
|
const finishMessage = asString$24(candidate?.finishMessage);
|
|
7842
7842
|
if (finishMessage) state.finishMessage = finishMessage;
|
|
7843
|
-
const blockReason = asString$24((isRecord$
|
|
7843
|
+
const blockReason = asString$24((isRecord$47(responseData.promptFeedback) ? responseData.promptFeedback : void 0)?.blockReason);
|
|
7844
7844
|
const finishReason = asString$24(candidate?.finishReason) || asString$24(responseData.finishReason) || (blockReason ? `PROMPT_BLOCKED:${blockReason}` : void 0);
|
|
7845
7845
|
if (finishReason) {
|
|
7846
7846
|
state.finishReason = finishReason;
|
|
@@ -7883,7 +7883,7 @@ function finishReasonFor$8(state) {
|
|
|
7883
7883
|
}
|
|
7884
7884
|
function closeStream$6(state) {
|
|
7885
7885
|
if (state.finished) return [];
|
|
7886
|
-
if (!state.finishReason && !state.done) throw new LlmError$1("Antigravity stream ended before its terminal response", "
|
|
7886
|
+
if (!state.finishReason && !state.done) throw new LlmError$1("Antigravity stream ended before its terminal response", "TRANSPORT");
|
|
7887
7887
|
state.finished = true;
|
|
7888
7888
|
const out = closeCurrentBlock(state);
|
|
7889
7889
|
if (state.usageMetadata) out.push({
|
|
@@ -8602,6 +8602,36 @@ function withoutValidatedToolConfig(body) {
|
|
|
8602
8602
|
delete parsed.request.toolConfig;
|
|
8603
8603
|
return JSON.stringify(parsed);
|
|
8604
8604
|
}
|
|
8605
|
+
/**
|
|
8606
|
+
* The failure a stream that died mid-body is reported as.
|
|
8607
|
+
*
|
|
8608
|
+
* Everything above the response body is already typed: a connection that never
|
|
8609
|
+
* produced a response raises TRANSPORT, a non-2xx raises its status verdict, and
|
|
8610
|
+
* the mapper raises its own protocol or provider one. The body itself was the
|
|
8611
|
+
* gap: a connection that ends before the response does arrives as undici's bare
|
|
8612
|
+
* `TypeError: terminated`, which carries no code of its own, so the harness
|
|
8613
|
+
* normalizes it to UNKNOWN — and UNKNOWN is not a code DSH retries, so one
|
|
8614
|
+
* severed connection ended the long answer the user was reading with no second
|
|
8615
|
+
* attempt at all. The real reason (ECONNRESET, a socket closed by the far side,
|
|
8616
|
+
* a body timeout) sat on `.cause`, which the harness drops before the failure is
|
|
8617
|
+
* persisted, so the report could not even say what had happened.
|
|
8618
|
+
*
|
|
8619
|
+
* So the transport is named here and its cause chain is kept in the message, and
|
|
8620
|
+
* only what the route already typed is left alone: an `LlmError` is this line's
|
|
8621
|
+
* own verdict (an in-band provider error, a stream truncated before its terminal
|
|
8622
|
+
* frame), and a caller abort is a cancellation rather than a fault.
|
|
8623
|
+
*
|
|
8624
|
+
* The code is TRANSPORT even once chunks have been yielded. DSH settles a failed
|
|
8625
|
+
* attempt as a discarded `assistant/attempt` and starts a fresh one on retry, and
|
|
8626
|
+
* a tool call only runs after its stream finishes, so retrying can neither
|
|
8627
|
+
* repeat committed output nor re-run a tool.
|
|
8628
|
+
*/
|
|
8629
|
+
function streamFailure$2(error, signal) {
|
|
8630
|
+
if (error instanceof LlmError) return error;
|
|
8631
|
+
if (signal.aborted) return new LlmError("Antigravity stream aborted", "ABORTED", { cause: error });
|
|
8632
|
+
const chain = errorChain(error).trim();
|
|
8633
|
+
return new LlmError(`Antigravity stream failed: ${chain === "" ? "the response stream ended unexpectedly" : chain.slice(0, 500)}`, "TRANSPORT", { cause: error });
|
|
8634
|
+
}
|
|
8605
8635
|
var AntigravityAdapter = class extends LlmAdapter {
|
|
8606
8636
|
store;
|
|
8607
8637
|
modelSettings;
|
|
@@ -8868,7 +8898,9 @@ var AntigravityAdapter = class extends LlmAdapter {
|
|
|
8868
8898
|
let buffer = "";
|
|
8869
8899
|
try {
|
|
8870
8900
|
while (true) {
|
|
8871
|
-
const { done, value } = await reader.read()
|
|
8901
|
+
const { done, value } = await reader.read().catch((error) => {
|
|
8902
|
+
throw streamFailure$2(error, signal);
|
|
8903
|
+
});
|
|
8872
8904
|
if (done) break;
|
|
8873
8905
|
if (value !== void 0 && value.byteLength > 0) activity?.pulse();
|
|
8874
8906
|
buffer += decoder.decode(value, { stream: true });
|
|
@@ -10522,7 +10554,7 @@ function preferencesPath() {
|
|
|
10522
10554
|
return path.join(dshHomeDir(), "storages", "dsh-chatgpt-subscription-preferences.json");
|
|
10523
10555
|
}
|
|
10524
10556
|
/** Distinguishes concurrent temporary files written by one process. */
|
|
10525
|
-
let writeSequence = 0;
|
|
10557
|
+
let writeSequence$1 = 0;
|
|
10526
10558
|
/** One JSON document, validated and shaped by a schemastery schema. */
|
|
10527
10559
|
var FilePreferencesStore = class {
|
|
10528
10560
|
schema;
|
|
@@ -10624,8 +10656,8 @@ var FilePreferencesStore = class {
|
|
|
10624
10656
|
}
|
|
10625
10657
|
async write(value) {
|
|
10626
10658
|
await fs.mkdir(path.dirname(this.filePath), { recursive: true });
|
|
10627
|
-
writeSequence += 1;
|
|
10628
|
-
const tmp = `${this.filePath}.tmp.${process.pid}.${writeSequence}`;
|
|
10659
|
+
writeSequence$1 += 1;
|
|
10660
|
+
const tmp = `${this.filePath}.tmp.${process.pid}.${writeSequence$1}`;
|
|
10629
10661
|
try {
|
|
10630
10662
|
await fs.writeFile(tmp, JSON.stringify(value, null, 2), {
|
|
10631
10663
|
encoding: "utf8",
|
|
@@ -13824,7 +13856,7 @@ async function compactionCandidates(ctx) {
|
|
|
13824
13856
|
};
|
|
13825
13857
|
}))).filter((provider) => provider.models.length > 0) };
|
|
13826
13858
|
}
|
|
13827
|
-
function isRecord$
|
|
13859
|
+
function isRecord$46(value) {
|
|
13828
13860
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
13829
13861
|
}
|
|
13830
13862
|
function readPreferencesUpdate(value, current) {
|
|
@@ -13865,7 +13897,7 @@ function readPreferencesUpdate(value, current) {
|
|
|
13865
13897
|
};
|
|
13866
13898
|
}
|
|
13867
13899
|
if ("contextWindowOverrides" in value) {
|
|
13868
|
-
if (!isRecord$
|
|
13900
|
+
if (!isRecord$46(value.contextWindowOverrides)) throw new PreferenceError("contextWindowOverrides must be an object.");
|
|
13869
13901
|
const overrides = {};
|
|
13870
13902
|
for (const [model, contextWindow] of Object.entries(value.contextWindowOverrides)) {
|
|
13871
13903
|
if (!isCodexModelId(model)) throw new PreferenceError("Unknown Codex model for a context window override.");
|
|
@@ -18365,11 +18397,11 @@ function commandCodeHeaders(apiKey, extra = {}) {
|
|
|
18365
18397
|
function timeoutSignal$4(signal, ms) {
|
|
18366
18398
|
return signal ? AbortSignal.any([signal, AbortSignal.timeout(ms)]) : AbortSignal.timeout(ms);
|
|
18367
18399
|
}
|
|
18368
|
-
function isRecord$
|
|
18400
|
+
function isRecord$45(value) {
|
|
18369
18401
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
18370
18402
|
}
|
|
18371
18403
|
function asRecord$15(value) {
|
|
18372
|
-
return isRecord$
|
|
18404
|
+
return isRecord$45(value) ? value : void 0;
|
|
18373
18405
|
}
|
|
18374
18406
|
function asString$23(value) {
|
|
18375
18407
|
if (typeof value === "string" && value.trim() !== "") return value;
|
|
@@ -18382,7 +18414,7 @@ function asNumber$5(value) {
|
|
|
18382
18414
|
if (Number.isFinite(parsed)) return parsed;
|
|
18383
18415
|
}
|
|
18384
18416
|
}
|
|
18385
|
-
function firstString$
|
|
18417
|
+
function firstString$2(record, keys) {
|
|
18386
18418
|
for (const key of keys) {
|
|
18387
18419
|
const value = asString$23(record[key]);
|
|
18388
18420
|
if (value !== void 0) return value;
|
|
@@ -18413,7 +18445,7 @@ function deepValue(value, keys, depth = 0) {
|
|
|
18413
18445
|
}
|
|
18414
18446
|
return;
|
|
18415
18447
|
}
|
|
18416
|
-
if (!isRecord$
|
|
18448
|
+
if (!isRecord$45(value)) return void 0;
|
|
18417
18449
|
for (const key of keys) if (value[key] !== void 0 && value[key] !== null) return value[key];
|
|
18418
18450
|
for (const nested of Object.values(value)) {
|
|
18419
18451
|
const hit = deepValue(nested, keys, depth + 1);
|
|
@@ -18427,7 +18459,7 @@ function collectRecords(value, keys, depth = 0, out = []) {
|
|
|
18427
18459
|
for (const entry of value) collectRecords(entry, keys, depth + 1, out);
|
|
18428
18460
|
return out;
|
|
18429
18461
|
}
|
|
18430
|
-
if (!isRecord$
|
|
18462
|
+
if (!isRecord$45(value)) return out;
|
|
18431
18463
|
if (keys.some((key) => value[key] !== void 0)) out.push(value);
|
|
18432
18464
|
for (const nested of Object.values(value)) collectRecords(nested, keys, depth + 1, out);
|
|
18433
18465
|
return out;
|
|
@@ -18461,38 +18493,38 @@ function parseWhoami(payload, fallback = {}) {
|
|
|
18461
18493
|
"plan",
|
|
18462
18494
|
"tier"
|
|
18463
18495
|
])) ?? {};
|
|
18464
|
-
const email = firstString$
|
|
18496
|
+
const email = firstString$2(user, ["email", "primaryEmail"]) ?? (typeof user.email === "string" ? user.email : void 0) ?? fallback.email;
|
|
18465
18497
|
return {
|
|
18466
|
-
userId: firstString$
|
|
18498
|
+
userId: firstString$2(user, [
|
|
18467
18499
|
"id",
|
|
18468
18500
|
"userId",
|
|
18469
18501
|
"uid"
|
|
18470
|
-
]) ?? firstString$
|
|
18471
|
-
userName: firstString$
|
|
18502
|
+
]) ?? firstString$2(root, ["userId"]) ?? fallback.userId ?? null,
|
|
18503
|
+
userName: firstString$2(user, [
|
|
18472
18504
|
"userName",
|
|
18473
18505
|
"username",
|
|
18474
18506
|
"name",
|
|
18475
18507
|
"displayName",
|
|
18476
18508
|
"fullName"
|
|
18477
|
-
]) ?? firstString$
|
|
18509
|
+
]) ?? firstString$2(root, ["userName"]) ?? fallback.userName ?? null,
|
|
18478
18510
|
email: email ?? null,
|
|
18479
|
-
organizationName: firstString$
|
|
18511
|
+
organizationName: firstString$2(org, [
|
|
18480
18512
|
"name",
|
|
18481
18513
|
"displayName",
|
|
18482
18514
|
"slug"
|
|
18483
18515
|
]) ?? fallback.organizationName ?? null,
|
|
18484
|
-
keyName: firstString$
|
|
18516
|
+
keyName: firstString$2(key, [
|
|
18485
18517
|
"name",
|
|
18486
18518
|
"keyName",
|
|
18487
18519
|
"label"
|
|
18488
18520
|
]) ?? fallback.keyName ?? null,
|
|
18489
|
-
planLabel: firstString$
|
|
18521
|
+
planLabel: firstString$2(subscription, [
|
|
18490
18522
|
"name",
|
|
18491
18523
|
"displayName",
|
|
18492
18524
|
"label",
|
|
18493
18525
|
"planName"
|
|
18494
|
-
]) ?? firstString$
|
|
18495
|
-
planId: firstString$
|
|
18526
|
+
]) ?? firstString$2(data, ["planName", "planLabel"]) ?? fallback.planLabel ?? null,
|
|
18527
|
+
planId: firstString$2(subscription, [
|
|
18496
18528
|
"id",
|
|
18497
18529
|
"planId",
|
|
18498
18530
|
"slug",
|
|
@@ -18726,7 +18758,7 @@ function sweepMeters(payload, skipIds, consumed) {
|
|
|
18726
18758
|
usedFraction = Math.max(0, 1 - remainingFraction);
|
|
18727
18759
|
}
|
|
18728
18760
|
if (usedFraction === null && remainingFraction === null && limit === void 0 && balance === void 0) continue;
|
|
18729
|
-
const rawId = firstString$
|
|
18761
|
+
const rawId = firstString$2(record, [
|
|
18730
18762
|
"id",
|
|
18731
18763
|
"bucketId",
|
|
18732
18764
|
"bucket_id",
|
|
@@ -18735,7 +18767,7 @@ function sweepMeters(payload, skipIds, consumed) {
|
|
|
18735
18767
|
"type",
|
|
18736
18768
|
"key"
|
|
18737
18769
|
]);
|
|
18738
|
-
const label = firstString$
|
|
18770
|
+
const label = firstString$2(record, [
|
|
18739
18771
|
"displayName",
|
|
18740
18772
|
"display_name",
|
|
18741
18773
|
"label",
|
|
@@ -18765,7 +18797,7 @@ function sweepMeters(payload, skipIds, consumed) {
|
|
|
18765
18797
|
"periodEnd",
|
|
18766
18798
|
"renewalDate"
|
|
18767
18799
|
])),
|
|
18768
|
-
description: firstString$
|
|
18800
|
+
description: firstString$2(record, [
|
|
18769
18801
|
"description",
|
|
18770
18802
|
"detail",
|
|
18771
18803
|
"subtitle"
|
|
@@ -18836,7 +18868,7 @@ function parseUsageWindows$1(payload) {
|
|
|
18836
18868
|
"percentUsed"
|
|
18837
18869
|
]);
|
|
18838
18870
|
if (percent === void 0) continue;
|
|
18839
|
-
const id = firstString$
|
|
18871
|
+
const id = firstString$2(record, [
|
|
18840
18872
|
"id",
|
|
18841
18873
|
"bucketId",
|
|
18842
18874
|
"name",
|
|
@@ -18848,7 +18880,7 @@ function parseUsageWindows$1(payload) {
|
|
|
18848
18880
|
seen.add(id);
|
|
18849
18881
|
windows.push({
|
|
18850
18882
|
id,
|
|
18851
|
-
label: firstString$
|
|
18883
|
+
label: firstString$2(record, [
|
|
18852
18884
|
"displayName",
|
|
18853
18885
|
"label",
|
|
18854
18886
|
"name",
|
|
@@ -18924,7 +18956,7 @@ function parseProviderModels(payload) {
|
|
|
18924
18956
|
for (const entry of list) {
|
|
18925
18957
|
const record = asRecord$15(entry);
|
|
18926
18958
|
if (!record) continue;
|
|
18927
|
-
const id = firstString$
|
|
18959
|
+
const id = firstString$2(record, [
|
|
18928
18960
|
"id",
|
|
18929
18961
|
"model",
|
|
18930
18962
|
"name"
|
|
@@ -18932,7 +18964,7 @@ function parseProviderModels(payload) {
|
|
|
18932
18964
|
if (id === void 0) continue;
|
|
18933
18965
|
models.push({
|
|
18934
18966
|
id,
|
|
18935
|
-
name: firstString$
|
|
18967
|
+
name: firstString$2(record, ["displayName", "name"]) ?? id,
|
|
18936
18968
|
contextWindow: firstNumber$2(record, [
|
|
18937
18969
|
"context_length",
|
|
18938
18970
|
"contextLength",
|
|
@@ -19237,7 +19269,7 @@ function buildModelOptions$1(catalog, enabledModelIds, overrides) {
|
|
|
19237
19269
|
*
|
|
19238
19270
|
* https://commandcode.ai/blog/command-code-provider-api
|
|
19239
19271
|
*/
|
|
19240
|
-
function isRecord$
|
|
19272
|
+
function isRecord$44(value) {
|
|
19241
19273
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
19242
19274
|
}
|
|
19243
19275
|
function asString$22(value) {
|
|
@@ -19277,17 +19309,17 @@ const SUPPORTED_IMAGE_MEDIA_TYPES$4 = /* @__PURE__ */ new Set([
|
|
|
19277
19309
|
]);
|
|
19278
19310
|
function attachmentOf$4(block) {
|
|
19279
19311
|
const attachment = block.attachment;
|
|
19280
|
-
if (!isRecord$
|
|
19312
|
+
if (!isRecord$44(attachment)) return void 0;
|
|
19281
19313
|
return typeof attachment.attachmentId === "string" ? attachment : void 0;
|
|
19282
19314
|
}
|
|
19283
19315
|
function attachmentLabel$5(block) {
|
|
19284
|
-
const attachment = isRecord$
|
|
19316
|
+
const attachment = isRecord$44(block.attachment) ? block.attachment : void 0;
|
|
19285
19317
|
return asString$22(attachment?.name) || asString$22(attachment?.attachmentId);
|
|
19286
19318
|
}
|
|
19287
19319
|
function collectImageRefs$2(content, refs) {
|
|
19288
19320
|
if (!Array.isArray(content)) return;
|
|
19289
19321
|
for (const block of content) {
|
|
19290
|
-
if (!isRecord$
|
|
19322
|
+
if (!isRecord$44(block)) continue;
|
|
19291
19323
|
if (block.type === "image") {
|
|
19292
19324
|
const attachment = attachmentOf$4(block);
|
|
19293
19325
|
if (attachment) refs.set(attachment.attachmentId, attachment);
|
|
@@ -19308,7 +19340,7 @@ function requestImageBytes$1(block) {
|
|
|
19308
19340
|
function collectRequestImageBytes$1(content, lengths) {
|
|
19309
19341
|
if (!Array.isArray(content)) return;
|
|
19310
19342
|
for (const block of content) {
|
|
19311
|
-
if (!isRecord$
|
|
19343
|
+
if (!isRecord$44(block)) continue;
|
|
19312
19344
|
if (block.type === "tool-result") {
|
|
19313
19345
|
collectRequestImageBytes$1(block.content, lengths);
|
|
19314
19346
|
continue;
|
|
@@ -19344,7 +19376,7 @@ function offloadOldestRequestImages$2(options) {
|
|
|
19344
19376
|
let replaced = false;
|
|
19345
19377
|
return {
|
|
19346
19378
|
content: content.map((block) => {
|
|
19347
|
-
if (remaining.count === 0 || !isRecord$
|
|
19379
|
+
if (remaining.count === 0 || !isRecord$44(block)) return block;
|
|
19348
19380
|
if (block.type === "image") {
|
|
19349
19381
|
if (requestImageBytes$1(block) === void 0) return block;
|
|
19350
19382
|
remaining.count -= 1;
|
|
@@ -19416,7 +19448,7 @@ function unavailableImageText$5(block) {
|
|
|
19416
19448
|
}
|
|
19417
19449
|
function imageBlockToInline$3(block, images) {
|
|
19418
19450
|
let data = asString$22(block.data) || asString$22(block.base64);
|
|
19419
|
-
const source = isRecord$
|
|
19451
|
+
const source = isRecord$44(block.source) ? block.source : void 0;
|
|
19420
19452
|
if (!data && source) data = asString$22(source.data) || asString$22(source.base64);
|
|
19421
19453
|
let mediaType = asString$22(block.mimeType) || asString$22(block.mediaType) || (source ? asString$22(source.mimeType) || asString$22(source.mediaType) : void 0) || "image/png";
|
|
19422
19454
|
if (data?.startsWith("data:")) {
|
|
@@ -19442,7 +19474,7 @@ function textOf$8(content) {
|
|
|
19442
19474
|
if (!Array.isArray(content)) return "";
|
|
19443
19475
|
const parts = [];
|
|
19444
19476
|
for (const block of content) {
|
|
19445
|
-
if (!isRecord$
|
|
19477
|
+
if (!isRecord$44(block)) continue;
|
|
19446
19478
|
if (block.type === "text" && typeof block.text === "string") parts.push(sanitizeText$7(block.text));
|
|
19447
19479
|
else if (block.type === "tool-result") parts.push(textOf$8(block.content));
|
|
19448
19480
|
}
|
|
@@ -19451,7 +19483,7 @@ function textOf$8(content) {
|
|
|
19451
19483
|
function toolResultText$7(blocks) {
|
|
19452
19484
|
if (!Array.isArray(blocks)) return "";
|
|
19453
19485
|
return blocks.map((block) => {
|
|
19454
|
-
if (!isRecord$
|
|
19486
|
+
if (!isRecord$44(block)) return "";
|
|
19455
19487
|
if (block.type === "text" && typeof block.text === "string") return sanitizeText$7(block.text);
|
|
19456
19488
|
if (block.type === "tool-result") return toolResultText$7(block.content);
|
|
19457
19489
|
if (block.type === "image") return `[image: ${attachmentLabel$5(block) ?? "attached image"}]`;
|
|
@@ -19469,7 +19501,7 @@ function toolResultBlocks$2(blocks, images) {
|
|
|
19469
19501
|
const out = [];
|
|
19470
19502
|
let hasImage = false;
|
|
19471
19503
|
for (const block of blocks) {
|
|
19472
|
-
if (!isRecord$
|
|
19504
|
+
if (!isRecord$44(block)) continue;
|
|
19473
19505
|
if (block.type === "text" && typeof block.text === "string") {
|
|
19474
19506
|
out.push({
|
|
19475
19507
|
type: "text",
|
|
@@ -19518,7 +19550,7 @@ function toolResultImageBlocks$4(blocks, images) {
|
|
|
19518
19550
|
if (!Array.isArray(blocks)) return [];
|
|
19519
19551
|
const out = [];
|
|
19520
19552
|
for (const block of blocks) {
|
|
19521
|
-
if (!isRecord$
|
|
19553
|
+
if (!isRecord$44(block)) continue;
|
|
19522
19554
|
if (block.type === "image") {
|
|
19523
19555
|
const inline = imageBlockToInline$3(block, images);
|
|
19524
19556
|
if (inline && SUPPORTED_IMAGE_MEDIA_TYPES$4.has(inline.mediaType)) out.push({
|
|
@@ -19562,7 +19594,7 @@ function nonSystemMessages$7(options) {
|
|
|
19562
19594
|
}
|
|
19563
19595
|
/** Drop the JSON-Schema keywords provider gateways reject or ignore. */
|
|
19564
19596
|
function stripMetaSchema$2(schema) {
|
|
19565
|
-
if (!isRecord$
|
|
19597
|
+
if (!isRecord$44(schema)) return {
|
|
19566
19598
|
type: "object",
|
|
19567
19599
|
properties: {}
|
|
19568
19600
|
};
|
|
@@ -19579,7 +19611,7 @@ function openAIUserContent$3(message, images) {
|
|
|
19579
19611
|
const parts = [];
|
|
19580
19612
|
let hasImage = false;
|
|
19581
19613
|
for (const block of message.content) {
|
|
19582
|
-
if (!isRecord$
|
|
19614
|
+
if (!isRecord$44(block)) continue;
|
|
19583
19615
|
if (block.type === "text" && typeof block.text === "string") {
|
|
19584
19616
|
const text = sanitizeText$7(block.text);
|
|
19585
19617
|
if (text !== "") parts.push({
|
|
@@ -19607,7 +19639,7 @@ function openAIAssistantContent$3(message) {
|
|
|
19607
19639
|
const textParts = [];
|
|
19608
19640
|
const toolCalls = [];
|
|
19609
19641
|
for (const block of message.content) {
|
|
19610
|
-
if (!isRecord$
|
|
19642
|
+
if (!isRecord$44(block)) continue;
|
|
19611
19643
|
if (block.type === "text" && typeof block.text === "string") textParts.push(sanitizeText$7(block.text));
|
|
19612
19644
|
else if (block.type === "tool-call" && typeof block.name === "string") toolCalls.push({
|
|
19613
19645
|
id: typeof block.id === "string" && block.id !== "" ? block.id : `call_${toolCalls.length}`,
|
|
@@ -19639,7 +19671,7 @@ function buildOpenAIRequest$1(options, images = NO_RESOLVED_IMAGES$4) {
|
|
|
19639
19671
|
while (index < conversation.length && isToolResultMessage$7(conversation[index])) {
|
|
19640
19672
|
const current = conversation[index];
|
|
19641
19673
|
const block = current.content[0];
|
|
19642
|
-
const callId = isRecord$
|
|
19674
|
+
const callId = isRecord$44(block) && typeof block.toolCallId === "string" ? block.toolCallId : "";
|
|
19643
19675
|
messages.push({
|
|
19644
19676
|
role: "tool",
|
|
19645
19677
|
tool_call_id: callId,
|
|
@@ -19700,7 +19732,7 @@ function anthropicUserContent$3(message, images) {
|
|
|
19700
19732
|
if (!Array.isArray(message.content)) return [];
|
|
19701
19733
|
const blocks = [];
|
|
19702
19734
|
for (const block of message.content) {
|
|
19703
|
-
if (!isRecord$
|
|
19735
|
+
if (!isRecord$44(block)) continue;
|
|
19704
19736
|
if (block.type === "text" && typeof block.text === "string") {
|
|
19705
19737
|
const text = sanitizeText$7(block.text);
|
|
19706
19738
|
if (text !== "") blocks.push({
|
|
@@ -19737,7 +19769,7 @@ function anthropicUserContent$3(message, images) {
|
|
|
19737
19769
|
function anthropicAssistantContent$3(message) {
|
|
19738
19770
|
const blocks = [];
|
|
19739
19771
|
for (const block of message.content) {
|
|
19740
|
-
if (!isRecord$
|
|
19772
|
+
if (!isRecord$44(block)) continue;
|
|
19741
19773
|
if (block.type === "text" && typeof block.text === "string") {
|
|
19742
19774
|
const text = sanitizeText$7(block.text);
|
|
19743
19775
|
if (text !== "") blocks.push({
|
|
@@ -19750,7 +19782,7 @@ function anthropicAssistantContent$3(message) {
|
|
|
19750
19782
|
type: "tool_use",
|
|
19751
19783
|
id: typeof block.id === "string" && block.id !== "" ? block.id : `toolu_${blocks.length}`,
|
|
19752
19784
|
name: block.name,
|
|
19753
|
-
input: isRecord$
|
|
19785
|
+
input: isRecord$44(parsed) ? parsed : {}
|
|
19754
19786
|
});
|
|
19755
19787
|
}
|
|
19756
19788
|
}
|
|
@@ -19844,7 +19876,7 @@ function responsesUserContent(message, images) {
|
|
|
19844
19876
|
if (!Array.isArray(message.content)) return [];
|
|
19845
19877
|
const parts = [];
|
|
19846
19878
|
for (const block of message.content) {
|
|
19847
|
-
if (!isRecord$
|
|
19879
|
+
if (!isRecord$44(block)) continue;
|
|
19848
19880
|
if (block.type === "text" && typeof block.text === "string") {
|
|
19849
19881
|
const text = sanitizeText$7(block.text);
|
|
19850
19882
|
if (text !== "") parts.push({
|
|
@@ -19869,7 +19901,7 @@ function responsesToolResultImageBlocks(blocks, images) {
|
|
|
19869
19901
|
if (!Array.isArray(blocks)) return [];
|
|
19870
19902
|
const out = [];
|
|
19871
19903
|
for (const block of blocks) {
|
|
19872
|
-
if (!isRecord$
|
|
19904
|
+
if (!isRecord$44(block)) continue;
|
|
19873
19905
|
if (block.type === "image") {
|
|
19874
19906
|
const inline = imageBlockToInline$3(block, images);
|
|
19875
19907
|
if (inline && SUPPORTED_IMAGE_MEDIA_TYPES$4.has(inline.mediaType)) out.push({
|
|
@@ -19896,8 +19928,8 @@ function buildResponsesRequest(options, images = NO_RESOLVED_IMAGES$4) {
|
|
|
19896
19928
|
const imageBlocks = [];
|
|
19897
19929
|
while (index < conversation.length && isToolResultMessage$7(conversation[index])) {
|
|
19898
19930
|
const current = conversation[index];
|
|
19899
|
-
const toolResult = Array.isArray(current.content) ? current.content.find((b) => isRecord$
|
|
19900
|
-
const callId = (isRecord$
|
|
19931
|
+
const toolResult = Array.isArray(current.content) ? current.content.find((b) => isRecord$44(b) && b.type === "tool-result") : void 0;
|
|
19932
|
+
const callId = (isRecord$44(toolResult) && typeof toolResult.toolCallId === "string" ? toolResult.toolCallId : "") || (typeof current.source?.callId === "string" ? current.source.callId : "");
|
|
19901
19933
|
input.push({
|
|
19902
19934
|
type: "function_call_output",
|
|
19903
19935
|
call_id: callId,
|
|
@@ -19920,7 +19952,7 @@ function buildResponsesRequest(options, images = NO_RESOLVED_IMAGES$4) {
|
|
|
19920
19952
|
content
|
|
19921
19953
|
});
|
|
19922
19954
|
for (const call of toolCalls) {
|
|
19923
|
-
const fn = isRecord$
|
|
19955
|
+
const fn = isRecord$44(call.function) ? call.function : void 0;
|
|
19924
19956
|
input.push({
|
|
19925
19957
|
type: "function_call",
|
|
19926
19958
|
call_id: call.id,
|
|
@@ -20043,9 +20075,9 @@ function processOpenAIStreamLine$1(line, state) {
|
|
|
20043
20075
|
}
|
|
20044
20076
|
if (payload === "") return [];
|
|
20045
20077
|
const chunk = safeJsonParse$7(payload);
|
|
20046
|
-
if (!isRecord$
|
|
20078
|
+
if (!isRecord$44(chunk)) return [];
|
|
20047
20079
|
const out = [];
|
|
20048
|
-
if (isRecord$
|
|
20080
|
+
if (isRecord$44(chunk.error)) {
|
|
20049
20081
|
const message = asString$22(chunk.error.message) ?? "unknown error";
|
|
20050
20082
|
state.streamError = {
|
|
20051
20083
|
vocabulary: "openai",
|
|
@@ -20054,20 +20086,20 @@ function processOpenAIStreamLine$1(line, state) {
|
|
|
20054
20086
|
};
|
|
20055
20087
|
throw new LlmError$1(`Command Code stream error: ${message}`, isContextOverflow(chunk.error) ? CONTEXT_OVERFLOW_CODE : "PROVIDER_ERROR");
|
|
20056
20088
|
}
|
|
20057
|
-
const usage = isRecord$
|
|
20089
|
+
const usage = isRecord$44(chunk.usage) ? chunk.usage : void 0;
|
|
20058
20090
|
if (usage) {
|
|
20059
20091
|
state.sawUsage = true;
|
|
20060
20092
|
const prompt = numberOr$8(usage.prompt_tokens, 0);
|
|
20061
|
-
const details = isRecord$
|
|
20093
|
+
const details = isRecord$44(usage.prompt_tokens_details) ? usage.prompt_tokens_details : void 0;
|
|
20062
20094
|
const cached = details ? numberOr$8(details.cached_tokens, 0) : 0;
|
|
20063
20095
|
state.inputTokens = Math.max(0, prompt - cached);
|
|
20064
20096
|
state.cacheReadTokens = cached;
|
|
20065
20097
|
state.outputTokens = numberOr$8(usage.completion_tokens, state.outputTokens);
|
|
20066
|
-
if (isRecord$
|
|
20098
|
+
if (isRecord$44(usage.completion_tokens_details)) state.reasoningTokens = numberOr$8(usage.completion_tokens_details.reasoning_tokens, state.reasoningTokens);
|
|
20067
20099
|
}
|
|
20068
20100
|
const choices = Array.isArray(chunk.choices) ? chunk.choices : [];
|
|
20069
|
-
const choice = isRecord$
|
|
20070
|
-
const delta = isRecord$
|
|
20101
|
+
const choice = isRecord$44(choices[0]) ? choices[0] : void 0;
|
|
20102
|
+
const delta = isRecord$44(choice?.delta) ? choice.delta : void 0;
|
|
20071
20103
|
if (delta) {
|
|
20072
20104
|
const reasoning = asString$22(delta.reasoning_content) ?? asString$22(delta.reasoning);
|
|
20073
20105
|
if (reasoning !== void 0 && reasoning !== "") {
|
|
@@ -20095,7 +20127,7 @@ function processOpenAIStreamLine$1(line, state) {
|
|
|
20095
20127
|
}
|
|
20096
20128
|
const toolDeltas = Array.isArray(delta.tool_calls) ? delta.tool_calls : [];
|
|
20097
20129
|
for (const entry of toolDeltas) {
|
|
20098
|
-
if (!isRecord$
|
|
20130
|
+
if (!isRecord$44(entry)) continue;
|
|
20099
20131
|
out.push(...applyOpenAIToolDelta$1(entry, state));
|
|
20100
20132
|
}
|
|
20101
20133
|
}
|
|
@@ -20109,7 +20141,7 @@ function processOpenAIStreamLine$1(line, state) {
|
|
|
20109
20141
|
}
|
|
20110
20142
|
function applyOpenAIToolDelta$1(entry, state) {
|
|
20111
20143
|
const wireIndex = typeof entry.index === "number" ? entry.index : 0;
|
|
20112
|
-
const fn = isRecord$
|
|
20144
|
+
const fn = isRecord$44(entry.function) ? entry.function : {};
|
|
20113
20145
|
const out = [];
|
|
20114
20146
|
let call = state.toolCalls.get(wireIndex);
|
|
20115
20147
|
if (call === void 0) {
|
|
@@ -20167,12 +20199,12 @@ function processAnthropicStreamLine$1(line, state) {
|
|
|
20167
20199
|
const payload = trimmed.slice(5).trim();
|
|
20168
20200
|
if (payload === "" || payload === "[DONE]") return [];
|
|
20169
20201
|
const event = safeJsonParse$7(payload);
|
|
20170
|
-
if (!isRecord$
|
|
20202
|
+
if (!isRecord$44(event)) return [];
|
|
20171
20203
|
const type = asString$22(event.type);
|
|
20172
20204
|
const out = [];
|
|
20173
20205
|
if (type === "message_start") {
|
|
20174
|
-
const message = isRecord$
|
|
20175
|
-
const usage = message && isRecord$
|
|
20206
|
+
const message = isRecord$44(event.message) ? event.message : void 0;
|
|
20207
|
+
const usage = message && isRecord$44(message.usage) ? message.usage : void 0;
|
|
20176
20208
|
if (usage) {
|
|
20177
20209
|
state.sawUsage = true;
|
|
20178
20210
|
state.inputTokens = numberOr$8(usage.input_tokens, 0);
|
|
@@ -20186,7 +20218,7 @@ function processAnthropicStreamLine$1(line, state) {
|
|
|
20186
20218
|
}
|
|
20187
20219
|
if (type === "content_block_start") {
|
|
20188
20220
|
const contentIndex = numberOr$8(event.index, 0);
|
|
20189
|
-
const block = isRecord$
|
|
20221
|
+
const block = isRecord$44(event.content_block) ? event.content_block : {};
|
|
20190
20222
|
const blockType = asString$22(block.type);
|
|
20191
20223
|
out.push(...closeCurrent$5(state));
|
|
20192
20224
|
if (blockType === "tool_use") {
|
|
@@ -20236,7 +20268,7 @@ function processAnthropicStreamLine$1(line, state) {
|
|
|
20236
20268
|
}
|
|
20237
20269
|
if (type === "content_block_delta") {
|
|
20238
20270
|
const contentIndex = numberOr$8(event.index, 0);
|
|
20239
|
-
const delta = isRecord$
|
|
20271
|
+
const delta = isRecord$44(event.delta) ? event.delta : {};
|
|
20240
20272
|
const deltaType = asString$22(delta.type);
|
|
20241
20273
|
if (deltaType === "input_json_delta") {
|
|
20242
20274
|
const pending = state.toolCalls.get(contentIndex);
|
|
@@ -20309,10 +20341,10 @@ function processAnthropicStreamLine$1(line, state) {
|
|
|
20309
20341
|
return out;
|
|
20310
20342
|
}
|
|
20311
20343
|
if (type === "message_delta") {
|
|
20312
|
-
const delta = isRecord$
|
|
20344
|
+
const delta = isRecord$44(event.delta) ? event.delta : void 0;
|
|
20313
20345
|
const stop = delta ? asString$22(delta.stop_reason) : void 0;
|
|
20314
20346
|
if (stop !== void 0 && stop !== "") state.finishReason = stop;
|
|
20315
|
-
const usage = isRecord$
|
|
20347
|
+
const usage = isRecord$44(event.usage) ? event.usage : void 0;
|
|
20316
20348
|
if (usage) {
|
|
20317
20349
|
state.sawUsage = true;
|
|
20318
20350
|
state.outputTokens = numberOr$8(usage.output_tokens, state.outputTokens);
|
|
@@ -20324,7 +20356,7 @@ function processAnthropicStreamLine$1(line, state) {
|
|
|
20324
20356
|
return closeStream$5(state);
|
|
20325
20357
|
}
|
|
20326
20358
|
if (type === "error") {
|
|
20327
|
-
const error = isRecord$
|
|
20359
|
+
const error = isRecord$44(event.error) ? event.error : {};
|
|
20328
20360
|
const message = asString$22(error.message) ?? "unknown error";
|
|
20329
20361
|
state.streamError = {
|
|
20330
20362
|
vocabulary: "anthropic",
|
|
@@ -20370,7 +20402,7 @@ function processResponsesStreamLine(line, state) {
|
|
|
20370
20402
|
} catch {
|
|
20371
20403
|
return [];
|
|
20372
20404
|
}
|
|
20373
|
-
if (!isRecord$
|
|
20405
|
+
if (!isRecord$44(parsed)) return [];
|
|
20374
20406
|
const type = typeof parsed.type === "string" ? parsed.type : "";
|
|
20375
20407
|
if (type === "response.output_text.delta") {
|
|
20376
20408
|
const delta = typeof parsed.delta === "string" ? parsed.delta : "";
|
|
@@ -20397,7 +20429,7 @@ function processResponsesStreamLine(line, state) {
|
|
|
20397
20429
|
}];
|
|
20398
20430
|
}
|
|
20399
20431
|
if (type === "response.output_item.added") {
|
|
20400
|
-
const item = isRecord$
|
|
20432
|
+
const item = isRecord$44(parsed.item) ? parsed.item : null;
|
|
20401
20433
|
if (item === null || item.type !== "function_call") return [];
|
|
20402
20434
|
return startResponsesToolCall(state, parsed, item);
|
|
20403
20435
|
}
|
|
@@ -20421,11 +20453,11 @@ function processResponsesStreamLine(line, state) {
|
|
|
20421
20453
|
return [];
|
|
20422
20454
|
}
|
|
20423
20455
|
if (type === "response.completed" || type === "response.incomplete") {
|
|
20424
|
-
const response = isRecord$
|
|
20456
|
+
const response = isRecord$44(parsed.response) ? parsed.response : null;
|
|
20425
20457
|
if (response !== null) {
|
|
20426
20458
|
if (response.status === "failed") {
|
|
20427
20459
|
state.done = true;
|
|
20428
|
-
const errorObj = isRecord$
|
|
20460
|
+
const errorObj = isRecord$44(response.error) ? response.error : null;
|
|
20429
20461
|
const detail = errorObj && typeof errorObj.message === "string" ? errorObj.message : typeof response.error === "string" ? response.error : "";
|
|
20430
20462
|
state.streamError = {
|
|
20431
20463
|
vocabulary: "responses",
|
|
@@ -20438,7 +20470,7 @@ function processResponsesStreamLine(line, state) {
|
|
|
20438
20470
|
}
|
|
20439
20471
|
state.done = true;
|
|
20440
20472
|
if (type === "response.incomplete") {
|
|
20441
|
-
const details = response && isRecord$
|
|
20473
|
+
const details = response && isRecord$44(response.incomplete_details) ? response.incomplete_details : null;
|
|
20442
20474
|
const reason = details && typeof details.reason === "string" ? details.reason : "";
|
|
20443
20475
|
state.finishReason = reason === "max_output_tokens" || reason === "length" || reason === "max_tokens" ? "length" : reason || "length";
|
|
20444
20476
|
} else state.finishReason = "stop";
|
|
@@ -20446,8 +20478,8 @@ function processResponsesStreamLine(line, state) {
|
|
|
20446
20478
|
}
|
|
20447
20479
|
if (type === "response.failed" || type === "error") {
|
|
20448
20480
|
state.done = true;
|
|
20449
|
-
const errorObj = isRecord$
|
|
20450
|
-
const detail = (typeof parsed.message === "string" ? parsed.message : "") || (errorObj && typeof errorObj.message === "string" ? errorObj.message : "") || (isRecord$
|
|
20481
|
+
const errorObj = isRecord$44(parsed.error) ? parsed.error : isRecord$44(parsed.response) && isRecord$44(parsed.response.error) ? parsed.response.error : null;
|
|
20482
|
+
const detail = (typeof parsed.message === "string" ? parsed.message : "") || (errorObj && typeof errorObj.message === "string" ? errorObj.message : "") || (isRecord$44(parsed.response) && typeof parsed.response.error === "string" ? parsed.response.error : typeof parsed.error === "string" ? parsed.error : "");
|
|
20451
20483
|
state.streamError = {
|
|
20452
20484
|
vocabulary: "responses",
|
|
20453
20485
|
error: errorObj,
|
|
@@ -20535,15 +20567,15 @@ function responsesToolCall(state, key) {
|
|
|
20535
20567
|
for (const call of state.toolCalls.values()) if (call.id === toToolCallId(key)) return call;
|
|
20536
20568
|
}
|
|
20537
20569
|
function readResponsesUsage(response, state) {
|
|
20538
|
-
const usage = isRecord$
|
|
20570
|
+
const usage = isRecord$44(response.usage) ? response.usage : null;
|
|
20539
20571
|
if (usage === null) return;
|
|
20540
|
-
const cached = isRecord$
|
|
20541
|
-
const written = isRecord$
|
|
20572
|
+
const cached = isRecord$44(usage.input_tokens_details) ? numberOrZero(usage.input_tokens_details.cached_tokens) : 0;
|
|
20573
|
+
const written = isRecord$44(usage.input_tokens_details) ? numberOrZero(usage.input_tokens_details.cache_write_tokens) : 0;
|
|
20542
20574
|
state.inputTokens = Math.max(0, numberOrZero(usage.input_tokens) - cached - written);
|
|
20543
20575
|
state.cacheReadTokens = cached;
|
|
20544
20576
|
state.cacheWriteTokens = written;
|
|
20545
20577
|
state.outputTokens = numberOrZero(usage.output_tokens);
|
|
20546
|
-
const outputDetails = isRecord$
|
|
20578
|
+
const outputDetails = isRecord$44(usage.output_tokens_details) ? usage.output_tokens_details : null;
|
|
20547
20579
|
if (outputDetails !== null) state.reasoningTokens = numberOrZero(outputDetails.reasoning_tokens);
|
|
20548
20580
|
state.sawUsage = true;
|
|
20549
20581
|
}
|
|
@@ -20631,7 +20663,7 @@ const ANTHROPIC_IN_BAND_STATUS = {
|
|
|
20631
20663
|
* Anything not recognized keeps the mapper's verdict.
|
|
20632
20664
|
*/
|
|
20633
20665
|
function inBandResponsesCode(error, message) {
|
|
20634
|
-
const fields = isRecord$
|
|
20666
|
+
const fields = isRecord$43(error) ? error : {};
|
|
20635
20667
|
const rawCode = (asString$21(fields.code) ?? asString$21(fields.type) ?? "").toLowerCase();
|
|
20636
20668
|
const text = message.toLowerCase();
|
|
20637
20669
|
if (text.includes("rate limit") || rawCode === "rate_limit" || rawCode === "rate_limit_exceeded") return "RATE_LIMIT";
|
|
@@ -20690,13 +20722,13 @@ function reclassifyInBandResponsesError(thrown, rawError, message, classify) {
|
|
|
20690
20722
|
}
|
|
20691
20723
|
/** The wire `error` object of an in-band event, read defensively. */
|
|
20692
20724
|
function readErrorType(error) {
|
|
20693
|
-
if (!isRecord$
|
|
20725
|
+
if (!isRecord$43(error)) return null;
|
|
20694
20726
|
return typeof error.type === "string" ? error.type : null;
|
|
20695
20727
|
}
|
|
20696
20728
|
function asString$21(value) {
|
|
20697
20729
|
return typeof value === "string" ? value : void 0;
|
|
20698
20730
|
}
|
|
20699
|
-
function isRecord$
|
|
20731
|
+
function isRecord$43(value) {
|
|
20700
20732
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
20701
20733
|
}
|
|
20702
20734
|
//#endregion
|
|
@@ -23891,14 +23923,14 @@ const IN_BAND_SERVER = [
|
|
|
23891
23923
|
function isAbort$3(error, signal) {
|
|
23892
23924
|
return signal?.aborted === true || error instanceof Error && error.name === "AbortError";
|
|
23893
23925
|
}
|
|
23894
|
-
function isRecord$
|
|
23926
|
+
function isRecord$42(value) {
|
|
23895
23927
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
23896
23928
|
}
|
|
23897
23929
|
function asString$20(value) {
|
|
23898
23930
|
return typeof value === "string" && value !== "" ? value : void 0;
|
|
23899
23931
|
}
|
|
23900
23932
|
function attachmentLabel$4(block) {
|
|
23901
|
-
const attachment = isRecord$
|
|
23933
|
+
const attachment = isRecord$42(block.attachment) ? block.attachment : void 0;
|
|
23902
23934
|
return asString$20(attachment?.name) || asString$20(attachment?.attachmentId);
|
|
23903
23935
|
}
|
|
23904
23936
|
function unavailableImageText$4(block) {
|
|
@@ -23934,7 +23966,7 @@ async function processMessageContent(content, model, attachments, signal) {
|
|
|
23934
23966
|
textParts.push(part);
|
|
23935
23967
|
continue;
|
|
23936
23968
|
}
|
|
23937
|
-
if (!isRecord$
|
|
23969
|
+
if (!isRecord$42(part)) continue;
|
|
23938
23970
|
if (part.type === "text") {
|
|
23939
23971
|
if (typeof part.text === "string" && part.text !== "") textParts.push(part.text);
|
|
23940
23972
|
continue;
|
|
@@ -23963,7 +23995,7 @@ async function processMessageContent(content, model, attachments, signal) {
|
|
|
23963
23995
|
continue;
|
|
23964
23996
|
}
|
|
23965
23997
|
const inline = part.source;
|
|
23966
|
-
if (isRecord$
|
|
23998
|
+
if (isRecord$42(inline)) {
|
|
23967
23999
|
const data = inline.data;
|
|
23968
24000
|
if (typeof data === "string" && data !== "") {
|
|
23969
24001
|
images.push(encodeImage(data, asMediaType(inline.mediaType)));
|
|
@@ -24004,7 +24036,7 @@ async function toOllamaRequest(options, attachments, signal, declared) {
|
|
|
24004
24036
|
const toolResultBlocks = message.content.filter((b) => b.type === "tool-result");
|
|
24005
24037
|
if (toolResultBlocks.length > 0) {
|
|
24006
24038
|
for (const block of toolResultBlocks) {
|
|
24007
|
-
const toolCallId = String(block.toolCallId ?? (isRecord$
|
|
24039
|
+
const toolCallId = String(block.toolCallId ?? (isRecord$42(message.source) ? message.source.callId : "") ?? "");
|
|
24008
24040
|
const processed = await processMessageContent(block.content, options.model, attachments, signal);
|
|
24009
24041
|
messages.push({
|
|
24010
24042
|
role: "tool",
|
|
@@ -24615,7 +24647,7 @@ var OllamaAccountPool = class extends AccountPoolCore {
|
|
|
24615
24647
|
};
|
|
24616
24648
|
//#endregion
|
|
24617
24649
|
//#region src/host/common/request-images.ts
|
|
24618
|
-
function isRecord$
|
|
24650
|
+
function isRecord$41(value) {
|
|
24619
24651
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
24620
24652
|
}
|
|
24621
24653
|
function asString$19(value) {
|
|
@@ -24674,17 +24706,17 @@ const SUPPORTED_IMAGE_MEDIA_TYPES$3 = /* @__PURE__ */ new Set([
|
|
|
24674
24706
|
]);
|
|
24675
24707
|
function attachmentOf$3(block) {
|
|
24676
24708
|
const attachment = block.attachment;
|
|
24677
|
-
if (!isRecord$
|
|
24709
|
+
if (!isRecord$41(attachment)) return void 0;
|
|
24678
24710
|
return typeof attachment.attachmentId === "string" ? attachment : void 0;
|
|
24679
24711
|
}
|
|
24680
24712
|
function attachmentLabel$3(block) {
|
|
24681
|
-
const attachment = isRecord$
|
|
24713
|
+
const attachment = isRecord$41(block.attachment) ? block.attachment : void 0;
|
|
24682
24714
|
return asString$19(attachment?.name) || asString$19(attachment?.attachmentId);
|
|
24683
24715
|
}
|
|
24684
24716
|
function collectImageRefs$1(content, refs) {
|
|
24685
24717
|
if (!Array.isArray(content)) return;
|
|
24686
24718
|
for (const block of content) {
|
|
24687
|
-
if (!isRecord$
|
|
24719
|
+
if (!isRecord$41(block)) continue;
|
|
24688
24720
|
if (block.type === "image") {
|
|
24689
24721
|
const attachment = attachmentOf$3(block);
|
|
24690
24722
|
if (attachment) refs.set(attachment.attachmentId, attachment);
|
|
@@ -24714,7 +24746,7 @@ function base64Length$1(bytes) {
|
|
|
24714
24746
|
*/
|
|
24715
24747
|
function requestImagePayloadLength(block, images) {
|
|
24716
24748
|
let data = asString$19(block.data) || asString$19(block.base64);
|
|
24717
|
-
const source = isRecord$
|
|
24749
|
+
const source = isRecord$41(block.source) ? block.source : void 0;
|
|
24718
24750
|
if (!data && source) data = asString$19(source.data) || asString$19(source.base64);
|
|
24719
24751
|
if (data?.startsWith("data:")) {
|
|
24720
24752
|
const matched = data.match(/^data:([^;,]+);base64,(.*)$/s);
|
|
@@ -24740,7 +24772,7 @@ function requestImagePayloadLength(block, images) {
|
|
|
24740
24772
|
function collectRequestImagePayloads(content, images, lengths) {
|
|
24741
24773
|
if (!Array.isArray(content)) return;
|
|
24742
24774
|
for (const block of content) {
|
|
24743
|
-
if (!isRecord$
|
|
24775
|
+
if (!isRecord$41(block)) continue;
|
|
24744
24776
|
if (block.type === "image") {
|
|
24745
24777
|
const bytes = requestImagePayloadLength(block, images);
|
|
24746
24778
|
if (bytes !== void 0) lengths.push(bytes);
|
|
@@ -24794,7 +24826,7 @@ function replaceOldestRequestImages(options, count, images) {
|
|
|
24794
24826
|
let replaced = false;
|
|
24795
24827
|
return {
|
|
24796
24828
|
content: content.map((block) => {
|
|
24797
|
-
if (remaining.count === 0 || !isRecord$
|
|
24829
|
+
if (remaining.count === 0 || !isRecord$41(block)) return block;
|
|
24798
24830
|
if (block.type === "image") {
|
|
24799
24831
|
if (requestImagePayloadLength(block, images) === void 0) return block;
|
|
24800
24832
|
remaining.count -= 1;
|
|
@@ -24932,10 +24964,20 @@ function requestHasSendableImage(options, images) {
|
|
|
24932
24964
|
* An unreadable image resolves to `unavailable` rather than disappearing, so
|
|
24933
24965
|
* the model is told the picture is missing instead of answering about a blank.
|
|
24934
24966
|
*
|
|
24935
|
-
* An image whose long edge is over
|
|
24936
|
-
*
|
|
24937
|
-
*
|
|
24938
|
-
*
|
|
24967
|
+
* An image whose long edge is over the ceiling is sent as the harness's
|
|
24968
|
+
* downscaled request version (see {@link requestImageTarget}). The stored image
|
|
24969
|
+
* and durable history do not change, and an image that already fits is sent
|
|
24970
|
+
* byte-for-byte as stored.
|
|
24971
|
+
*
|
|
24972
|
+
* The ceiling is a PARAMETER because it belongs to the upstream route, exactly
|
|
24973
|
+
* like the image budget: `edge` defaults to the shared
|
|
24974
|
+
* {@link REQUEST_IMAGE_MAX_EDGE}, so every caller that does not name one keeps
|
|
24975
|
+
* the ceiling it always had. A route whose own CLI downscales to a different
|
|
24976
|
+
* long edge passes its value here and gets the same number in both places that
|
|
24977
|
+
* matter — the target it asks the attachment store for AND the check that the
|
|
24978
|
+
* version handed back really is that target. Deriving the target from one
|
|
24979
|
+
* constant and validating it against another is how a 2000 px request gets
|
|
24980
|
+
* discarded as if the store had ignored it.
|
|
24939
24981
|
*
|
|
24940
24982
|
* WHY scale instead of drop. `offloadOldestRequestImages` has to drop an image
|
|
24941
24983
|
* once the request exceeds `MAX_REQUEST_IMAGE_BYTES`, and the number it drops
|
|
@@ -24945,7 +24987,7 @@ function requestHasSendableImage(options, images) {
|
|
|
24945
24987
|
* outgrew the budget once stays inside it instead of breaking the cache every
|
|
24946
24988
|
* time an image is added.
|
|
24947
24989
|
*/
|
|
24948
|
-
async function resolveRequestImages$1(options, attachments, signal) {
|
|
24990
|
+
async function resolveRequestImages$1(options, attachments, signal, edge = REQUEST_IMAGE_MAX_EDGE) {
|
|
24949
24991
|
const refs = /* @__PURE__ */ new Map();
|
|
24950
24992
|
for (const message of options.messages) collectImageRefs$1(message.content, refs);
|
|
24951
24993
|
if (refs.size === 0) return NO_RESOLVED_IMAGES$3;
|
|
@@ -24956,10 +24998,10 @@ async function resolveRequestImages$1(options, attachments, signal) {
|
|
|
24956
24998
|
return;
|
|
24957
24999
|
}
|
|
24958
25000
|
try {
|
|
24959
|
-
const target = requestImageTarget(ref);
|
|
25001
|
+
const target = requestImageTarget(ref, edge);
|
|
24960
25002
|
if (target !== void 0) {
|
|
24961
25003
|
const version = await attachments.readImageRequest(ref, target, signal);
|
|
24962
|
-
resolved.set(attachmentId, Math.max(version.width, version.height) >
|
|
25004
|
+
resolved.set(attachmentId, Math.max(version.width, version.height) > edge ? { kind: "unavailable" } : {
|
|
24963
25005
|
kind: "inline",
|
|
24964
25006
|
mediaType: version.mediaType,
|
|
24965
25007
|
data: Buffer.from(version.data).toString("base64")
|
|
@@ -24985,7 +25027,7 @@ function unavailableImageText$3(block) {
|
|
|
24985
25027
|
}
|
|
24986
25028
|
function imageBlockToInline$2(block, images) {
|
|
24987
25029
|
let data = asString$19(block.data) || asString$19(block.base64);
|
|
24988
|
-
const source = isRecord$
|
|
25030
|
+
const source = isRecord$41(block.source) ? block.source : void 0;
|
|
24989
25031
|
if (!data && source) data = asString$19(source.data) || asString$19(source.base64);
|
|
24990
25032
|
let mediaType = asString$19(block.mimeType) || asString$19(block.mediaType) || (source ? asString$19(source.mimeType) || asString$19(source.mediaType) : void 0) || "image/png";
|
|
24991
25033
|
if (data?.startsWith("data:")) {
|
|
@@ -25973,7 +26015,7 @@ function evaluateCacheHint(input) {
|
|
|
25973
26015
|
if (!Number.isFinite(lastActiveAt) || !Number.isFinite(totalTokens)) return { kind: "skip" };
|
|
25974
26016
|
if (totalTokens < 1e5) return { kind: "skip" };
|
|
25975
26017
|
const idleMs = now - lastActiveAt;
|
|
25976
|
-
if (idleMs <=
|
|
26018
|
+
if (idleMs <= 15e5) return { kind: "skip" };
|
|
25977
26019
|
return {
|
|
25978
26020
|
kind: "hint",
|
|
25979
26021
|
idleMs,
|
|
@@ -26564,11 +26606,11 @@ const MODELS_PATH$2 = "/models";
|
|
|
26564
26606
|
const USAGES_PATH = "/usages";
|
|
26565
26607
|
/** The service reports money in fixed-point units of 1e-6 cents. */
|
|
26566
26608
|
const FIXED_POINT_CENTS = 1e6;
|
|
26567
|
-
function isRecord$
|
|
26609
|
+
function isRecord$40(value) {
|
|
26568
26610
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
26569
26611
|
}
|
|
26570
26612
|
function asRecord$13(value) {
|
|
26571
|
-
return isRecord$
|
|
26613
|
+
return isRecord$40(value) ? value : void 0;
|
|
26572
26614
|
}
|
|
26573
26615
|
function asString$18(value) {
|
|
26574
26616
|
if (typeof value === "string" && value.trim() !== "") return value;
|
|
@@ -26582,7 +26624,7 @@ function asNumber$4(value) {
|
|
|
26582
26624
|
if (Number.isFinite(parsed)) return parsed;
|
|
26583
26625
|
}
|
|
26584
26626
|
}
|
|
26585
|
-
function firstString(record, keys) {
|
|
26627
|
+
function firstString$1(record, keys) {
|
|
26586
26628
|
for (const key of keys) {
|
|
26587
26629
|
const value = asString$18(record[key]);
|
|
26588
26630
|
if (value !== void 0) return value;
|
|
@@ -27155,7 +27197,7 @@ function usageWindow(id, record, descriptor) {
|
|
|
27155
27197
|
id,
|
|
27156
27198
|
label: descriptor?.label ?? humanizeWindowKey(id),
|
|
27157
27199
|
usedFraction,
|
|
27158
|
-
usedPercent: Math.round(usedFraction * 100
|
|
27200
|
+
usedPercent: Math.round(usedFraction * 1e4) / 100,
|
|
27159
27201
|
windowDurationMins: descriptor?.minutes ?? null,
|
|
27160
27202
|
resetsAt: parseTimestamp(record.reset_time ?? record.resetTime ?? record.resetsAt),
|
|
27161
27203
|
limit: formatAmount(limit),
|
|
@@ -27203,7 +27245,7 @@ function parseUsageWindows(payload) {
|
|
|
27203
27245
|
const duration = firstNumber(window ?? {}, ["duration"]);
|
|
27204
27246
|
const unit = asString$18(window?.timeUnit ?? window?.time_unit);
|
|
27205
27247
|
const minutes = duration === void 0 ? null : unit === "TIME_UNIT_HOUR" ? duration * 60 : duration;
|
|
27206
|
-
push(minutes === 300 ? "limit_5h" : minutes === 10080 ? "limit_7d" : firstString(record, ["id", "name"]) ?? `limit-${windows.length + 1}`, detail, minutes === null ? void 0 : {
|
|
27248
|
+
push(minutes === 300 ? "limit_5h" : minutes === 10080 ? "limit_7d" : firstString$1(record, ["id", "name"]) ?? `limit-${windows.length + 1}`, detail, minutes === null ? void 0 : {
|
|
27207
27249
|
label: minutes === 300 ? "5-hour" : `${minutes}-minute`,
|
|
27208
27250
|
minutes
|
|
27209
27251
|
});
|
|
@@ -27302,7 +27344,7 @@ function membershipLevelName(level) {
|
|
|
27302
27344
|
*/
|
|
27303
27345
|
function parsePlanName(payload) {
|
|
27304
27346
|
const root = asRecord$13(payload) ?? {};
|
|
27305
|
-
const direct = firstString(root, [
|
|
27347
|
+
const direct = firstString$1(root, [
|
|
27306
27348
|
"user_level_name",
|
|
27307
27349
|
"userLevelName",
|
|
27308
27350
|
"planName",
|
|
@@ -27426,7 +27468,7 @@ function accountFromCredentials$1(credentials, payload, profile) {
|
|
|
27426
27468
|
const planLevel = (profile === void 0 ? null : parsePlanLevel(profile)) ?? (payload === void 0 ? null : parsePlanLevel(payload));
|
|
27427
27469
|
return {
|
|
27428
27470
|
userId: credentials.userId ?? identity.userId ?? null,
|
|
27429
|
-
nickname: (profile === void 0 ? null : firstString(asRecord$13(profile) ?? {}, [
|
|
27471
|
+
nickname: (profile === void 0 ? null : firstString$1(asRecord$13(profile) ?? {}, [
|
|
27430
27472
|
"nickname",
|
|
27431
27473
|
"username",
|
|
27432
27474
|
"name"
|
|
@@ -27540,12 +27582,12 @@ async function testConnection$1(store, options = {}) {
|
|
|
27540
27582
|
*
|
|
27541
27583
|
* @module dsh-chatgpt-subscription/tool-schema
|
|
27542
27584
|
*/
|
|
27543
|
-
function isRecord$
|
|
27585
|
+
function isRecord$39(value) {
|
|
27544
27586
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
27545
27587
|
}
|
|
27546
27588
|
function cloneJsonValue(value) {
|
|
27547
27589
|
if (Array.isArray(value)) return value.map(cloneJsonValue);
|
|
27548
|
-
if (isRecord$
|
|
27590
|
+
if (isRecord$39(value)) {
|
|
27549
27591
|
const res = {};
|
|
27550
27592
|
for (const [k, v] of Object.entries(value)) res[k] = cloneJsonValue(v);
|
|
27551
27593
|
return res;
|
|
@@ -27560,7 +27602,7 @@ function resolveLocalJsonPointer(root, ref) {
|
|
|
27560
27602
|
let current = root;
|
|
27561
27603
|
for (const rawPart of ref.slice(2).split("/")) {
|
|
27562
27604
|
const part = rawPart.replaceAll("~1", "/").replaceAll("~0", "~");
|
|
27563
|
-
if (isRecord$
|
|
27605
|
+
if (isRecord$39(current)) {
|
|
27564
27606
|
if (!Object.prototype.hasOwnProperty.call(current, part)) return { found: false };
|
|
27565
27607
|
current = current[part];
|
|
27566
27608
|
} else if (Array.isArray(current)) {
|
|
@@ -27576,7 +27618,7 @@ function resolveLocalJsonPointer(root, ref) {
|
|
|
27576
27618
|
}
|
|
27577
27619
|
function derefNode(node, root, visited) {
|
|
27578
27620
|
if (Array.isArray(node)) return node.map((item) => derefNode(item, root, visited));
|
|
27579
|
-
if (isRecord$
|
|
27621
|
+
if (isRecord$39(node)) {
|
|
27580
27622
|
if (typeof node["$ref"] === "string" && (node["$ref"] === "#" || node["$ref"].startsWith("#/"))) {
|
|
27581
27623
|
const ref = node["$ref"];
|
|
27582
27624
|
if (visited.has(ref)) return node;
|
|
@@ -27585,7 +27627,7 @@ function derefNode(node, root, visited) {
|
|
|
27585
27627
|
visited.add(ref);
|
|
27586
27628
|
const inlined = derefNode(resolved.value, root, visited);
|
|
27587
27629
|
visited.delete(ref);
|
|
27588
|
-
if (isRecord$
|
|
27630
|
+
if (isRecord$39(inlined)) {
|
|
27589
27631
|
const merged = { ...inlined };
|
|
27590
27632
|
for (const [k, v] of Object.entries(node)) {
|
|
27591
27633
|
if (k === "$ref") continue;
|
|
@@ -27605,7 +27647,7 @@ function derefNode(node, root, visited) {
|
|
|
27605
27647
|
}
|
|
27606
27648
|
function hasUnresolvedDefinitionRef(node, bucketKey) {
|
|
27607
27649
|
if (Array.isArray(node)) return node.some((child) => hasUnresolvedDefinitionRef(child, bucketKey));
|
|
27608
|
-
if (isRecord$
|
|
27650
|
+
if (isRecord$39(node)) {
|
|
27609
27651
|
const ref = node["$ref"];
|
|
27610
27652
|
if (typeof ref === "string" && ref.startsWith(`#/${bucketKey}/`)) return true;
|
|
27611
27653
|
for (const [k, v] of Object.entries(node)) {
|
|
@@ -27636,19 +27678,19 @@ function inferTypeFromValues(values) {
|
|
|
27636
27678
|
if (types.size === 1) return types.values().next().value;
|
|
27637
27679
|
}
|
|
27638
27680
|
function normalizeSchemaProperties(node) {
|
|
27639
|
-
if (!isRecord$
|
|
27640
|
-
if (isRecord$
|
|
27641
|
-
for (const propSchema of Object.values(node.properties)) if (isRecord$
|
|
27681
|
+
if (!isRecord$39(node)) return;
|
|
27682
|
+
if (isRecord$39(node.properties)) {
|
|
27683
|
+
for (const propSchema of Object.values(node.properties)) if (isRecord$39(propSchema)) {
|
|
27642
27684
|
if (!propSchema.type && !propSchema.$ref) {
|
|
27643
27685
|
if (Array.isArray(propSchema.enum) && propSchema.enum.length > 0) propSchema.type = inferTypeFromValues(propSchema.enum) ?? "string";
|
|
27644
27686
|
else if (propSchema.const !== void 0) propSchema.type = inferValueType(propSchema.const) ?? "string";
|
|
27645
|
-
else if (isRecord$
|
|
27687
|
+
else if (isRecord$39(propSchema.properties)) propSchema.type = "object";
|
|
27646
27688
|
else if (propSchema.items) propSchema.type = "array";
|
|
27647
27689
|
}
|
|
27648
27690
|
normalizeSchemaProperties(propSchema);
|
|
27649
27691
|
}
|
|
27650
27692
|
}
|
|
27651
|
-
if (isRecord$
|
|
27693
|
+
if (isRecord$39(node.items)) normalizeSchemaProperties(node.items);
|
|
27652
27694
|
else if (Array.isArray(node.items)) for (const item of node.items) normalizeSchemaProperties(item);
|
|
27653
27695
|
}
|
|
27654
27696
|
/**
|
|
@@ -27656,7 +27698,7 @@ function normalizeSchemaProperties(node) {
|
|
|
27656
27698
|
* inlining definitions ($defs/definitions/$ref), and ensuring property types for enums/consts.
|
|
27657
27699
|
*/
|
|
27658
27700
|
function normalizeKimiToolSchema(schema) {
|
|
27659
|
-
if (!isRecord$
|
|
27701
|
+
if (!isRecord$39(schema)) return {
|
|
27660
27702
|
type: "object",
|
|
27661
27703
|
properties: {}
|
|
27662
27704
|
};
|
|
@@ -27674,7 +27716,7 @@ function stripMetaSchema$1(schema) {
|
|
|
27674
27716
|
}
|
|
27675
27717
|
//#endregion
|
|
27676
27718
|
//#region src/host/common/prompt-estimate.ts
|
|
27677
|
-
function isRecord$
|
|
27719
|
+
function isRecord$38(value) {
|
|
27678
27720
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
27679
27721
|
}
|
|
27680
27722
|
/** Tool-call arguments as the JSON string a wire carries, whatever shape they arrived in. */
|
|
@@ -27707,7 +27749,7 @@ function estimatedInputTokens(options) {
|
|
|
27707
27749
|
const contentCharacters = (content) => {
|
|
27708
27750
|
if (!Array.isArray(content)) return typeof content === "string" ? content.length : 0;
|
|
27709
27751
|
return content.reduce((total, block) => {
|
|
27710
|
-
if (!isRecord$
|
|
27752
|
+
if (!isRecord$38(block)) return total;
|
|
27711
27753
|
if ((block.type === "text" || block.type === "reasoning") && typeof block.text === "string") return total + block.text.length;
|
|
27712
27754
|
if (block.type === "tool-result") return total + contentCharacters(block.content);
|
|
27713
27755
|
if (block.type === "tool-call") {
|
|
@@ -27808,7 +27850,7 @@ function videoOmissionText$1(reason, label) {
|
|
|
27808
27850
|
}
|
|
27809
27851
|
//#endregion
|
|
27810
27852
|
//#region src/host/common/video-request.ts
|
|
27811
|
-
function isRecord$
|
|
27853
|
+
function isRecord$37(value) {
|
|
27812
27854
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
27813
27855
|
}
|
|
27814
27856
|
function asString$17(value) {
|
|
@@ -27816,14 +27858,14 @@ function asString$17(value) {
|
|
|
27816
27858
|
}
|
|
27817
27859
|
function videoAttachmentOf(block) {
|
|
27818
27860
|
const attachment = block.attachment;
|
|
27819
|
-
if (!isRecord$
|
|
27861
|
+
if (!isRecord$37(attachment)) return void 0;
|
|
27820
27862
|
if (typeof attachment.attachmentId !== "string") return void 0;
|
|
27821
27863
|
return attachment;
|
|
27822
27864
|
}
|
|
27823
27865
|
function collectVideoRefs(content, refs) {
|
|
27824
27866
|
if (!Array.isArray(content)) return;
|
|
27825
27867
|
for (const block of content) {
|
|
27826
|
-
if (!isRecord$
|
|
27868
|
+
if (!isRecord$37(block) || block.type !== "video") continue;
|
|
27827
27869
|
const attachment = videoAttachmentOf(block);
|
|
27828
27870
|
if (attachment) refs.set(attachment.attachmentId, attachment);
|
|
27829
27871
|
}
|
|
@@ -27838,7 +27880,7 @@ function requestVideoBytes(block) {
|
|
|
27838
27880
|
function collectRequestVideoBytes(content, lengths) {
|
|
27839
27881
|
if (!Array.isArray(content)) return;
|
|
27840
27882
|
for (const block of content) {
|
|
27841
|
-
if (!isRecord$
|
|
27883
|
+
if (!isRecord$37(block) || block.type !== "video") continue;
|
|
27842
27884
|
const bytes = requestVideoBytes(block);
|
|
27843
27885
|
if (bytes !== void 0) lengths.push(bytes);
|
|
27844
27886
|
}
|
|
@@ -27882,7 +27924,7 @@ function isAbortLike(error, signal) {
|
|
|
27882
27924
|
}
|
|
27883
27925
|
/** True when the request carries any video occurrence at all. */
|
|
27884
27926
|
function requestHasVideo(options) {
|
|
27885
|
-
return options.messages.some((message) => Array.isArray(message.content) && message.content.some((block) => isRecord$
|
|
27927
|
+
return options.messages.some((message) => Array.isArray(message.content) && message.content.some((block) => isRecord$37(block) && block.type === "video"));
|
|
27886
27928
|
}
|
|
27887
27929
|
function omittedVideoText(omit, block) {
|
|
27888
27930
|
return omit("unreadable", videoBlockLabel(block));
|
|
@@ -27917,7 +27959,7 @@ function offloadOldestRequestVideos(options, maxBytes) {
|
|
|
27917
27959
|
if (remaining.count === 0 || !Array.isArray(message.content)) return message;
|
|
27918
27960
|
let replaced = false;
|
|
27919
27961
|
const content = message.content.map((block) => {
|
|
27920
|
-
if (remaining.count === 0 || !isRecord$
|
|
27962
|
+
if (remaining.count === 0 || !isRecord$37(block) || block.type !== "video") return block;
|
|
27921
27963
|
if (requestVideoBytes(block) === void 0) return block;
|
|
27922
27964
|
remaining.count -= 1;
|
|
27923
27965
|
replaced = true;
|
|
@@ -28000,7 +28042,7 @@ function videoBlockToInline(block, videos, videoAccepted, omit) {
|
|
|
28000
28042
|
*
|
|
28001
28043
|
* See https://www.kimi.com/code/docs/en/kimi-code/error-reference.html
|
|
28002
28044
|
*/
|
|
28003
|
-
function isRecord$
|
|
28045
|
+
function isRecord$36(value) {
|
|
28004
28046
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
28005
28047
|
}
|
|
28006
28048
|
function asString$16(value) {
|
|
@@ -28033,6 +28075,118 @@ const KNOWN_REASONING_KEYS = [
|
|
|
28033
28075
|
/** Default outbound reasoning key, used until a response proves otherwise. */
|
|
28034
28076
|
const DEFAULT_REASONING_KEY = KNOWN_REASONING_KEYS[0];
|
|
28035
28077
|
/**
|
|
28078
|
+
* One `reasoning_details` element, in an official or a compatibility shape.
|
|
28079
|
+
*
|
|
28080
|
+
* The official shapes are the ones agent-core-v2's
|
|
28081
|
+
* `bases/openai/reasoning-key.ts` reads: `type` is absent or `'summary'` for a
|
|
28082
|
+
* visible summary (payload in `summary`), or `'encrypted'` for opaque provider
|
|
28083
|
+
* state (payload in `encrypted`). Its `toReasoningDetailsElement` keeps
|
|
28084
|
+
* `record.index` when that is a number and otherwise the element's position, so
|
|
28085
|
+
* every element has an index; this function does the same, and replays it.
|
|
28086
|
+
*
|
|
28087
|
+
* Its `convertReasoningDetails` then walks each element twice, in this order:
|
|
28088
|
+
* a non-encrypted element with a `summary` produces visible thinking, and a
|
|
28089
|
+
* non-summary element with `encrypted` produces opaque thinking. A type-less
|
|
28090
|
+
* element carrying BOTH therefore yields two fragments — the visible summary
|
|
28091
|
+
* first, then the opaque state. An encrypted payload is never text: it is
|
|
28092
|
+
* provider state, and printing it would show a blob as if the model had thought
|
|
28093
|
+
* it.
|
|
28094
|
+
*
|
|
28095
|
+
* The `reasoning.text` / `reasoning.summary` gateway spellings this route
|
|
28096
|
+
* already accepted stay readable, and are replayed under the type they arrived
|
|
28097
|
+
* with. Anything else — an unknown `type`, a missing payload, a non-object — is
|
|
28098
|
+
* ignored rather than mistaken for body text.
|
|
28099
|
+
*/
|
|
28100
|
+
function reasoningDetailElement(entry, position) {
|
|
28101
|
+
const type = asString$16(entry.type);
|
|
28102
|
+
const rawIndex = entry.index;
|
|
28103
|
+
const index = typeof rawIndex === "number" && Number.isInteger(rawIndex) && rawIndex >= 0 ? rawIndex : position;
|
|
28104
|
+
const shape = {
|
|
28105
|
+
key: "reasoning_details",
|
|
28106
|
+
...type === void 0 ? {} : { wireType: type },
|
|
28107
|
+
index
|
|
28108
|
+
};
|
|
28109
|
+
if (type === "reasoning.text") {
|
|
28110
|
+
const text = asString$16(entry.text);
|
|
28111
|
+
return text === void 0 || text === "" ? [] : [{
|
|
28112
|
+
...shape,
|
|
28113
|
+
form: "text",
|
|
28114
|
+
text
|
|
28115
|
+
}];
|
|
28116
|
+
}
|
|
28117
|
+
const summary = asString$16(entry.summary);
|
|
28118
|
+
const encrypted = asString$16(entry.encrypted);
|
|
28119
|
+
const fragments = [];
|
|
28120
|
+
if (type === void 0 || type === "summary" || type === "reasoning.summary") {
|
|
28121
|
+
if (summary !== void 0 && summary !== "") fragments.push({
|
|
28122
|
+
...shape,
|
|
28123
|
+
form: "summary",
|
|
28124
|
+
text: summary
|
|
28125
|
+
});
|
|
28126
|
+
}
|
|
28127
|
+
if (type === void 0 || type === "encrypted") {
|
|
28128
|
+
if (encrypted !== void 0 && encrypted !== "") fragments.push({
|
|
28129
|
+
...shape,
|
|
28130
|
+
form: "encrypted",
|
|
28131
|
+
encrypted,
|
|
28132
|
+
text: ""
|
|
28133
|
+
});
|
|
28134
|
+
}
|
|
28135
|
+
return fragments;
|
|
28136
|
+
}
|
|
28137
|
+
/**
|
|
28138
|
+
* Fragments carried by one `reasoning_details` value.
|
|
28139
|
+
*
|
|
28140
|
+
* A bare string — the older gateway spelling, and already a learned dialect here
|
|
28141
|
+
* — is one `string` fragment. An array keeps its arrival order, exactly as the
|
|
28142
|
+
* official `extractReasoningDetails` does: elements are pushed in the order they
|
|
28143
|
+
* appear and nothing is re-sorted. `index` is metadata (defaulted to the
|
|
28144
|
+
* position), never an ordering key here.
|
|
28145
|
+
*/
|
|
28146
|
+
function reasoningDetailFragments(value) {
|
|
28147
|
+
if (typeof value === "string") return value === "" ? [] : [{
|
|
28148
|
+
key: "reasoning_details",
|
|
28149
|
+
form: "string",
|
|
28150
|
+
text: value
|
|
28151
|
+
}];
|
|
28152
|
+
if (!Array.isArray(value)) return [];
|
|
28153
|
+
const fragments = [];
|
|
28154
|
+
value.forEach((entry, position) => {
|
|
28155
|
+
if (!isRecord$36(entry)) return;
|
|
28156
|
+
fragments.push(...reasoningDetailElement(entry, position));
|
|
28157
|
+
});
|
|
28158
|
+
return fragments;
|
|
28159
|
+
}
|
|
28160
|
+
/**
|
|
28161
|
+
* Every reasoning fragment one inbound object (a stream delta or a message)
|
|
28162
|
+
* carries, in a fixed, documented order.
|
|
28163
|
+
*
|
|
28164
|
+
* `reasoning_content` comes first and `reasoning` last because that is the
|
|
28165
|
+
* order of {@link KNOWN_REASONING_KEYS}: the Kimi field is the one this route is
|
|
28166
|
+
* documented against and `reasoning` is the newer vLLM rename, so a delta
|
|
28167
|
+
* carrying both keeps its text in the order a single-key endpoint would have
|
|
28168
|
+
* produced it in. A `reasoning_details` array keeps its arrival order; `index` is
|
|
28169
|
+
* carried as metadata and never reorders it (see reasoningDetailFragments).
|
|
28170
|
+
*/
|
|
28171
|
+
function reasoningFragmentsOf(source) {
|
|
28172
|
+
if (typeof source !== "object" || source === null) return [];
|
|
28173
|
+
const record = source;
|
|
28174
|
+
const fragments = [];
|
|
28175
|
+
for (const key of KNOWN_REASONING_KEYS) {
|
|
28176
|
+
const value = record[key];
|
|
28177
|
+
if (key === "reasoning_details") {
|
|
28178
|
+
fragments.push(...reasoningDetailFragments(value));
|
|
28179
|
+
continue;
|
|
28180
|
+
}
|
|
28181
|
+
if (typeof value !== "string" || value === "") continue;
|
|
28182
|
+
fragments.push({
|
|
28183
|
+
key,
|
|
28184
|
+
text: value
|
|
28185
|
+
});
|
|
28186
|
+
}
|
|
28187
|
+
return fragments;
|
|
28188
|
+
}
|
|
28189
|
+
/**
|
|
28036
28190
|
* Per-endpoint reasoning-field dialect: observes what the endpoint actually
|
|
28037
28191
|
* sends, and echoes thinking back under the same key.
|
|
28038
28192
|
*
|
|
@@ -28044,16 +28198,40 @@ const DEFAULT_REASONING_KEY = KNOWN_REASONING_KEYS[0];
|
|
|
28044
28198
|
*/
|
|
28045
28199
|
var ReasoningKeyDialect = class {
|
|
28046
28200
|
detected;
|
|
28047
|
-
/**
|
|
28048
|
-
|
|
28049
|
-
|
|
28050
|
-
|
|
28051
|
-
|
|
28052
|
-
|
|
28053
|
-
|
|
28054
|
-
|
|
28055
|
-
|
|
28201
|
+
/**
|
|
28202
|
+
* Every reasoning fragment one inbound delta carries, remembering the dialect.
|
|
28203
|
+
*
|
|
28204
|
+
* The FIRST string-shaped fragment names the key this endpoint speaks, which is
|
|
28205
|
+
* exactly the field the earlier single-match read picked: an endpoint answering
|
|
28206
|
+
* under `reasoning_content` keeps that outbound key even when the same delta
|
|
28207
|
+
* also carries a sibling field, so learning does not change just because a peer
|
|
28208
|
+
* became chattier.
|
|
28209
|
+
*
|
|
28210
|
+
* An element of a `reasoning_details` ARRAY never teaches the dialect: the
|
|
28211
|
+
* outbound key must be a field that takes a plain string, and array elements
|
|
28212
|
+
* could only teach `reasoning_details`, which on that wire is an array. A bare
|
|
28213
|
+
* string `reasoning_details` still teaches it, because that spelling really is
|
|
28214
|
+
* a string.
|
|
28215
|
+
*/
|
|
28216
|
+
observeAll(source) {
|
|
28217
|
+
const fragments = reasoningFragmentsOf(source);
|
|
28218
|
+
const teaching = fragments.find((fragment) => fragment.form === void 0 || fragment.form === "string");
|
|
28219
|
+
if (teaching !== void 0) {
|
|
28220
|
+
this.detected = teaching.key;
|
|
28221
|
+
return fragments;
|
|
28222
|
+
}
|
|
28223
|
+
if (typeof source === "object" && source !== null) {
|
|
28224
|
+
const record = source;
|
|
28225
|
+
for (const key of KNOWN_REASONING_KEYS) if (typeof record[key] === "string") {
|
|
28226
|
+
this.detected = key;
|
|
28227
|
+
break;
|
|
28228
|
+
}
|
|
28056
28229
|
}
|
|
28230
|
+
return fragments;
|
|
28231
|
+
}
|
|
28232
|
+
/** First reasoning fragment on an inbound message or delta, remembering its key. */
|
|
28233
|
+
observe(source) {
|
|
28234
|
+
return this.observeAll(source)[0]?.text;
|
|
28057
28235
|
}
|
|
28058
28236
|
/** The key to serialize thinking into on an outbound assistant message. */
|
|
28059
28237
|
outboundKey() {
|
|
@@ -28221,7 +28399,7 @@ function textOf$6(content) {
|
|
|
28221
28399
|
if (!Array.isArray(content)) return "";
|
|
28222
28400
|
const parts = [];
|
|
28223
28401
|
for (const block of content) {
|
|
28224
|
-
if (!isRecord$
|
|
28402
|
+
if (!isRecord$36(block)) continue;
|
|
28225
28403
|
if (block.type === "text" && typeof block.text === "string") parts.push(sanitizeText$6(block.text));
|
|
28226
28404
|
else if (block.type === "tool-result") parts.push(textOf$6(block.content));
|
|
28227
28405
|
}
|
|
@@ -28230,7 +28408,7 @@ function textOf$6(content) {
|
|
|
28230
28408
|
function toolResultText$6(blocks) {
|
|
28231
28409
|
if (!Array.isArray(blocks)) return "";
|
|
28232
28410
|
return blocks.map((block) => {
|
|
28233
|
-
if (!isRecord$
|
|
28411
|
+
if (!isRecord$36(block)) return "";
|
|
28234
28412
|
if (block.type === "text" && typeof block.text === "string") return sanitizeText$6(block.text);
|
|
28235
28413
|
if (block.type === "tool-result") return toolResultText$6(block.content);
|
|
28236
28414
|
if (block.type === "image") return `[image: ${attachmentLabel$3(block) ?? "attached image"}]`;
|
|
@@ -28248,7 +28426,7 @@ function toolResultBlocks$1(blocks, images) {
|
|
|
28248
28426
|
const out = [];
|
|
28249
28427
|
let hasImage = false;
|
|
28250
28428
|
for (const block of blocks) {
|
|
28251
|
-
if (!isRecord$
|
|
28429
|
+
if (!isRecord$36(block)) continue;
|
|
28252
28430
|
if (block.type === "text" && typeof block.text === "string") {
|
|
28253
28431
|
out.push({
|
|
28254
28432
|
type: "text",
|
|
@@ -28292,13 +28470,27 @@ function toolResultBlocks$1(blocks, images) {
|
|
|
28292
28470
|
* OpenAI `image_url` blocks for one tool result. A `role: "tool"` message can
|
|
28293
28471
|
* only carry text, so these ride on a `user` message appended after the run of
|
|
28294
28472
|
* tool messages rather than inside it.
|
|
28473
|
+
*
|
|
28474
|
+
* A hosted reference is honoured here too, because a `read_image` result or a
|
|
28475
|
+
* screenshot is exactly the kind of image that would otherwise inline a
|
|
28476
|
+
* megabyte into a request the conversation already made large. Sending base64
|
|
28477
|
+
* for an image this request uploaded would also contradict the reference it
|
|
28478
|
+
* cites elsewhere.
|
|
28295
28479
|
*/
|
|
28296
|
-
function toolResultImageBlocks$3(blocks, images) {
|
|
28480
|
+
function toolResultImageBlocks$3(blocks, images, media = {}) {
|
|
28297
28481
|
if (!Array.isArray(blocks)) return [];
|
|
28298
28482
|
const out = [];
|
|
28299
28483
|
for (const block of blocks) {
|
|
28300
|
-
if (!isRecord$
|
|
28484
|
+
if (!isRecord$36(block)) continue;
|
|
28301
28485
|
if (block.type === "image") {
|
|
28486
|
+
const reference = hostedReference(media.imageReferences, block);
|
|
28487
|
+
if (reference !== void 0) {
|
|
28488
|
+
out.push({
|
|
28489
|
+
type: "image_url",
|
|
28490
|
+
image_url: { url: reference }
|
|
28491
|
+
});
|
|
28492
|
+
continue;
|
|
28493
|
+
}
|
|
28302
28494
|
const inline = imageBlockToInline$2(block, images);
|
|
28303
28495
|
if (inline && SUPPORTED_IMAGE_MEDIA_TYPES$3.has(inline.mediaType)) out.push({
|
|
28304
28496
|
type: "image_url",
|
|
@@ -28310,10 +28502,41 @@ function toolResultImageBlocks$3(blocks, images) {
|
|
|
28310
28502
|
});
|
|
28311
28503
|
continue;
|
|
28312
28504
|
}
|
|
28313
|
-
if (block.type === "tool-result") out.push(...toolResultImageBlocks$3(block.content, images));
|
|
28505
|
+
if (block.type === "tool-result") out.push(...toolResultImageBlocks$3(block.content, images, media));
|
|
28314
28506
|
}
|
|
28315
28507
|
return out;
|
|
28316
28508
|
}
|
|
28509
|
+
/** Scheme under which this route cites a file it uploaded. */
|
|
28510
|
+
const HOSTED_FILE_SCHEME = "ms://";
|
|
28511
|
+
/**
|
|
28512
|
+
* The attachment id one block cites, when it cites a durable attachment.
|
|
28513
|
+
*
|
|
28514
|
+
* Only a durable occurrence can have been uploaded: a block carrying its own
|
|
28515
|
+
* `data:` URL has no id, so it is never looked up here and always keeps its
|
|
28516
|
+
* bytes.
|
|
28517
|
+
*/
|
|
28518
|
+
function attachmentIdOf(block) {
|
|
28519
|
+
const attachment = isRecord$36(block.attachment) ? block.attachment : void 0;
|
|
28520
|
+
const id = attachment === void 0 ? void 0 : asString$16(attachment.attachmentId);
|
|
28521
|
+
return id === void 0 || id === "" ? void 0 : id;
|
|
28522
|
+
}
|
|
28523
|
+
/**
|
|
28524
|
+
* The hosted reference for one block, when this request uploaded it.
|
|
28525
|
+
*
|
|
28526
|
+
* A value that is not an `ms://` reference is IGNORED rather than sent. This is
|
|
28527
|
+
* the one place a per-account cache key could reach the wire, so a bare id, an
|
|
28528
|
+
* empty string or an http URL is dropped instead of being trusted; the
|
|
28529
|
+
* occurrence then falls back to its inline bytes, which always work.
|
|
28530
|
+
*/
|
|
28531
|
+
function hostedReference(references, block) {
|
|
28532
|
+
if (references === void 0) return void 0;
|
|
28533
|
+
const id = attachmentIdOf(block);
|
|
28534
|
+
if (id === void 0) return void 0;
|
|
28535
|
+
const reference = references.get(id);
|
|
28536
|
+
if (reference === void 0) return void 0;
|
|
28537
|
+
const trimmed = reference.trim();
|
|
28538
|
+
return trimmed.startsWith(HOSTED_FILE_SCHEME) && trimmed.length > 5 ? trimmed : void 0;
|
|
28539
|
+
}
|
|
28317
28540
|
function toolCallArguments$5(raw) {
|
|
28318
28541
|
if (typeof raw === "string") return raw;
|
|
28319
28542
|
if (raw === void 0 || raw === null) return "{}";
|
|
@@ -28358,12 +28581,135 @@ function leadingSystemText$6(options) {
|
|
|
28358
28581
|
function nonSystemMessages$6(options) {
|
|
28359
28582
|
return options.messages.filter((message) => message.role !== "system");
|
|
28360
28583
|
}
|
|
28361
|
-
|
|
28362
|
-
|
|
28363
|
-
|
|
28364
|
-
|
|
28365
|
-
|
|
28366
|
-
|
|
28584
|
+
const NO_REASONING = {
|
|
28585
|
+
fields: [],
|
|
28586
|
+
details: [],
|
|
28587
|
+
joined: ""
|
|
28588
|
+
};
|
|
28589
|
+
/** Form tag read back from persisted provenance; undefined for an unknown one. */
|
|
28590
|
+
function readReasoningForm(value) {
|
|
28591
|
+
return value === "string" || value === "text" || value === "summary" || value === "encrypted" ? value : void 0;
|
|
28592
|
+
}
|
|
28593
|
+
/**
|
|
28594
|
+
* Provenance recorded for one reasoning block by the stream.
|
|
28595
|
+
*
|
|
28596
|
+
* Every shape that cannot be verified returns undefined, so a block written by
|
|
28597
|
+
* an older generation of this file - or one whose metadata was lost in transit -
|
|
28598
|
+
* falls back to the single dialect key instead of being split on a guess.
|
|
28599
|
+
*/
|
|
28600
|
+
function reasoningRunsAt(message, blockIndex) {
|
|
28601
|
+
const state = message.source?.replayState;
|
|
28602
|
+
if (!isRecord$36(state) || !Array.isArray(state.blocks)) return void 0;
|
|
28603
|
+
const entry = state.blocks[blockIndex];
|
|
28604
|
+
if (!isRecord$36(entry) || !Array.isArray(entry.reasoning)) return void 0;
|
|
28605
|
+
const runs = [];
|
|
28606
|
+
for (const raw of entry.reasoning) {
|
|
28607
|
+
if (!isRecord$36(raw)) return void 0;
|
|
28608
|
+
const key = asString$16(raw.key);
|
|
28609
|
+
const length = raw.length;
|
|
28610
|
+
if (key === void 0 || typeof length !== "number" || !Number.isInteger(length) || length < 0) return void 0;
|
|
28611
|
+
const form = readReasoningForm(raw.form);
|
|
28612
|
+
const wireType = asString$16(raw.wireType);
|
|
28613
|
+
const index = typeof raw.index === "number" && Number.isInteger(raw.index) && raw.index >= 0 ? raw.index : void 0;
|
|
28614
|
+
const encrypted = asString$16(raw.encrypted);
|
|
28615
|
+
runs.push({
|
|
28616
|
+
key,
|
|
28617
|
+
...form === void 0 ? {} : { form },
|
|
28618
|
+
...wireType === void 0 ? {} : { wireType },
|
|
28619
|
+
...index === void 0 ? {} : { index },
|
|
28620
|
+
...encrypted === void 0 ? {} : { encrypted },
|
|
28621
|
+
length
|
|
28622
|
+
});
|
|
28623
|
+
}
|
|
28624
|
+
return runs.length === 0 ? void 0 : runs;
|
|
28625
|
+
}
|
|
28626
|
+
/**
|
|
28627
|
+
* Split one message's reasoning back onto the shapes it arrived in.
|
|
28628
|
+
*
|
|
28629
|
+
* The block text stays authoritative: the recorded runs only say where its
|
|
28630
|
+
* slices belong. Their total must equal the block's length exactly, and one
|
|
28631
|
+
* mismatch abandons the split for that block rather than writing a truncated
|
|
28632
|
+
* prefix under the wrong field. An encrypted run has length 0, so it changes
|
|
28633
|
+
* neither the total nor the offsets while still producing its element.
|
|
28634
|
+
*
|
|
28635
|
+
* `fallbackKey` is the learned dialect, used for reasoning with no surviving
|
|
28636
|
+
* provenance - every message written before this provenance existed, and any
|
|
28637
|
+
* message whose metadata was dropped in transit.
|
|
28638
|
+
*/
|
|
28639
|
+
function replayedReasoning(message, fallbackKey) {
|
|
28640
|
+
if (!Array.isArray(message.content)) return NO_REASONING;
|
|
28641
|
+
const order = [];
|
|
28642
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
28643
|
+
const details = [];
|
|
28644
|
+
let detailsString;
|
|
28645
|
+
let joined = "";
|
|
28646
|
+
const put = (key, text) => {
|
|
28647
|
+
let parts = byKey.get(key);
|
|
28648
|
+
if (parts === void 0) {
|
|
28649
|
+
parts = [];
|
|
28650
|
+
order.push(key);
|
|
28651
|
+
byKey.set(key, parts);
|
|
28652
|
+
}
|
|
28653
|
+
parts.push(text);
|
|
28654
|
+
};
|
|
28655
|
+
for (let blockIndex = 0; blockIndex < message.content.length; blockIndex++) {
|
|
28656
|
+
const block = message.content[blockIndex];
|
|
28657
|
+
if (!isRecord$36(block) || block.type !== "reasoning" || typeof block.text !== "string") continue;
|
|
28658
|
+
const text = sanitizeText$6(block.text);
|
|
28659
|
+
joined += text;
|
|
28660
|
+
const runs = reasoningRunsAt(message, blockIndex);
|
|
28661
|
+
const total = runs === void 0 ? -1 : runs.reduce((sum, run) => sum + run.length, 0);
|
|
28662
|
+
if (runs === void 0 || total !== text.length) {
|
|
28663
|
+
if (text !== "") put(fallbackKey, text);
|
|
28664
|
+
continue;
|
|
28665
|
+
}
|
|
28666
|
+
let offset = 0;
|
|
28667
|
+
for (const run of runs) {
|
|
28668
|
+
const slice = text.slice(offset, offset + run.length);
|
|
28669
|
+
offset += run.length;
|
|
28670
|
+
if (run.key !== "reasoning_details") {
|
|
28671
|
+
if (slice !== "") put(run.key, slice);
|
|
28672
|
+
continue;
|
|
28673
|
+
}
|
|
28674
|
+
if (run.form === "encrypted") {
|
|
28675
|
+
if (run.encrypted !== void 0 && run.encrypted !== "") details.push({
|
|
28676
|
+
form: "encrypted",
|
|
28677
|
+
encrypted: run.encrypted,
|
|
28678
|
+
...run.wireType === void 0 ? {} : { wireType: run.wireType },
|
|
28679
|
+
...run.index === void 0 ? {} : { index: run.index }
|
|
28680
|
+
});
|
|
28681
|
+
continue;
|
|
28682
|
+
}
|
|
28683
|
+
if (slice === "") continue;
|
|
28684
|
+
if (run.form === "text") {
|
|
28685
|
+
details.push({
|
|
28686
|
+
form: "text",
|
|
28687
|
+
text: slice,
|
|
28688
|
+
...run.index === void 0 ? {} : { index: run.index }
|
|
28689
|
+
});
|
|
28690
|
+
continue;
|
|
28691
|
+
}
|
|
28692
|
+
if (run.form === "summary") {
|
|
28693
|
+
details.push({
|
|
28694
|
+
form: "summary",
|
|
28695
|
+
summary: slice,
|
|
28696
|
+
...run.wireType === void 0 ? {} : { wireType: run.wireType },
|
|
28697
|
+
...run.index === void 0 ? {} : { index: run.index }
|
|
28698
|
+
});
|
|
28699
|
+
continue;
|
|
28700
|
+
}
|
|
28701
|
+
detailsString = (detailsString ?? "") + slice;
|
|
28702
|
+
}
|
|
28703
|
+
}
|
|
28704
|
+
return {
|
|
28705
|
+
fields: order.map((key) => ({
|
|
28706
|
+
key,
|
|
28707
|
+
text: byKey.get(key).join("")
|
|
28708
|
+
})),
|
|
28709
|
+
...detailsString === void 0 ? {} : { detailsString },
|
|
28710
|
+
details,
|
|
28711
|
+
joined
|
|
28712
|
+
};
|
|
28367
28713
|
}
|
|
28368
28714
|
/**
|
|
28369
28715
|
* A tool declaration that belongs to a message rather than the request.
|
|
@@ -28394,6 +28740,73 @@ function messageToolsOf(message) {
|
|
|
28394
28740
|
if (Array.isArray(value) && value.length > 0) return value;
|
|
28395
28741
|
}
|
|
28396
28742
|
}
|
|
28743
|
+
/**
|
|
28744
|
+
* Names of the tools one developer message adds at its own history position.
|
|
28745
|
+
*
|
|
28746
|
+
* The harness states an addition as a name only; the definition it activates
|
|
28747
|
+
* lives in the request's declaration list, which is why the two are resolved
|
|
28748
|
+
* together rather than the block carrying a schema of its own.
|
|
28749
|
+
*/
|
|
28750
|
+
function toolAdditionNames(message) {
|
|
28751
|
+
const names = [];
|
|
28752
|
+
for (const block of message.content) {
|
|
28753
|
+
if (block.type !== "tool-addition") continue;
|
|
28754
|
+
const name = block.toolName;
|
|
28755
|
+
if (typeof name !== "string" || name.trim() === "") continue;
|
|
28756
|
+
names.push(name.trim());
|
|
28757
|
+
}
|
|
28758
|
+
return names;
|
|
28759
|
+
}
|
|
28760
|
+
/**
|
|
28761
|
+
* Names of the tools one developer message revokes at its own history position.
|
|
28762
|
+
*
|
|
28763
|
+
* `in-history` is the only mode that emits these; a route declaring
|
|
28764
|
+
* `addition-only` never sees one, because the harness removes the declaration
|
|
28765
|
+
* from the list instead.
|
|
28766
|
+
*/
|
|
28767
|
+
function toolRemovalNames(message) {
|
|
28768
|
+
const names = [];
|
|
28769
|
+
for (const block of message.content) {
|
|
28770
|
+
if (block.type !== "tool-removal") continue;
|
|
28771
|
+
const name = block.toolName;
|
|
28772
|
+
if (typeof name !== "string" || name.trim() === "") continue;
|
|
28773
|
+
names.push(name.trim());
|
|
28774
|
+
}
|
|
28775
|
+
return names;
|
|
28776
|
+
}
|
|
28777
|
+
/**
|
|
28778
|
+
* The model-facing note that revokes one or more tools.
|
|
28779
|
+
*
|
|
28780
|
+
* Imperative and unambiguous on purpose: the tool is still in the declaration
|
|
28781
|
+
* list (that is what keeps the prefix intact), so the only thing standing
|
|
28782
|
+
* between the model and a call that would fail is this sentence.
|
|
28783
|
+
*/
|
|
28784
|
+
function removalNotice(names) {
|
|
28785
|
+
const list = names.map((name) => "\"" + name + "\"").join(", ");
|
|
28786
|
+
return names.length === 1 ? "[" + list + " is no longer available. Do not call it again: any call will fail.]" : "[" + list + " are no longer available. Do not call them again: any call will fail.]";
|
|
28787
|
+
}
|
|
28788
|
+
/**
|
|
28789
|
+
* Resolve additions against the declaration list the harness sent.
|
|
28790
|
+
*
|
|
28791
|
+
* A deferred definition keeps `deferLoading` for the top-level filter and loses
|
|
28792
|
+
* it here: a message-level declaration has no such field, and passing it on
|
|
28793
|
+
* would be a property the service documented a stricter shape for.
|
|
28794
|
+
*/
|
|
28795
|
+
function declarationsFor(names, tools) {
|
|
28796
|
+
if (tools === void 0 || names.length === 0) return [];
|
|
28797
|
+
const byName = new Map(tools.map((tool) => [tool.name, tool]));
|
|
28798
|
+
const declarations = [];
|
|
28799
|
+
for (const name of names) {
|
|
28800
|
+
const tool = byName.get(name);
|
|
28801
|
+
if (tool === void 0) continue;
|
|
28802
|
+
declarations.push({
|
|
28803
|
+
name: tool.name,
|
|
28804
|
+
description: tool.description ?? "",
|
|
28805
|
+
parameters: stripMetaSchema$1(tool.parameters)
|
|
28806
|
+
});
|
|
28807
|
+
}
|
|
28808
|
+
return declarations;
|
|
28809
|
+
}
|
|
28397
28810
|
/** One declaration in the wire shape Kimi documents for `messages[].tools`. */
|
|
28398
28811
|
function openAIDynamicTool(tool) {
|
|
28399
28812
|
return {
|
|
@@ -28410,7 +28823,7 @@ function openAIUserContent$2(message, images, media = {}) {
|
|
|
28410
28823
|
const parts = [];
|
|
28411
28824
|
let hasRichPart = false;
|
|
28412
28825
|
for (const block of message.content) {
|
|
28413
|
-
if (!isRecord$
|
|
28826
|
+
if (!isRecord$36(block)) continue;
|
|
28414
28827
|
if (block.type === "text" && typeof block.text === "string") {
|
|
28415
28828
|
const text = sanitizeText$6(block.text);
|
|
28416
28829
|
if (text !== "") parts.push({
|
|
@@ -28418,18 +28831,36 @@ function openAIUserContent$2(message, images, media = {}) {
|
|
|
28418
28831
|
text
|
|
28419
28832
|
});
|
|
28420
28833
|
} else if (block.type === "image") {
|
|
28421
|
-
const
|
|
28422
|
-
if (
|
|
28834
|
+
const reference = hostedReference(media.imageReferences, block);
|
|
28835
|
+
if (reference !== void 0) {
|
|
28423
28836
|
hasRichPart = true;
|
|
28424
28837
|
parts.push({
|
|
28425
28838
|
type: "image_url",
|
|
28426
|
-
image_url: { url:
|
|
28839
|
+
image_url: { url: reference }
|
|
28427
28840
|
});
|
|
28428
|
-
} else
|
|
28429
|
-
|
|
28430
|
-
|
|
28431
|
-
|
|
28841
|
+
} else {
|
|
28842
|
+
const inline = imageBlockToInline$2(block, images);
|
|
28843
|
+
if (inline && SUPPORTED_IMAGE_MEDIA_TYPES$3.has(inline.mediaType)) {
|
|
28844
|
+
hasRichPart = true;
|
|
28845
|
+
parts.push({
|
|
28846
|
+
type: "image_url",
|
|
28847
|
+
image_url: { url: `data:${inline.mediaType};base64,${inline.data}` }
|
|
28848
|
+
});
|
|
28849
|
+
} else parts.push({
|
|
28850
|
+
type: "text",
|
|
28851
|
+
text: unavailableImageText$3(block)
|
|
28852
|
+
});
|
|
28853
|
+
}
|
|
28432
28854
|
} else if (block.type === "video") {
|
|
28855
|
+
const reference = hostedReference(media.videoReferences, block);
|
|
28856
|
+
if (reference !== void 0) {
|
|
28857
|
+
hasRichPart = true;
|
|
28858
|
+
parts.push({
|
|
28859
|
+
type: "video_url",
|
|
28860
|
+
video_url: { url: reference }
|
|
28861
|
+
});
|
|
28862
|
+
continue;
|
|
28863
|
+
}
|
|
28433
28864
|
const outcome = videoBlockToInline(block, media.videos ?? /* @__PURE__ */ new Map(), media.videoAccepted === true, videoOmissionText$1);
|
|
28434
28865
|
if ("inline" in outcome) {
|
|
28435
28866
|
hasRichPart = true;
|
|
@@ -28446,11 +28877,11 @@ function openAIUserContent$2(message, images, media = {}) {
|
|
|
28446
28877
|
if (!hasRichPart) return parts.map((part) => typeof part.text === "string" ? part.text : "").join("");
|
|
28447
28878
|
return parts;
|
|
28448
28879
|
}
|
|
28449
|
-
function openAIAssistantContent$2(message, toolCallIds) {
|
|
28880
|
+
function openAIAssistantContent$2(message, toolCallIds, reasoningKey) {
|
|
28450
28881
|
const textParts = [];
|
|
28451
28882
|
const toolCalls = [];
|
|
28452
28883
|
for (const block of message.content) {
|
|
28453
|
-
if (!isRecord$
|
|
28884
|
+
if (!isRecord$36(block)) continue;
|
|
28454
28885
|
if (block.type === "text" && typeof block.text === "string") textParts.push(sanitizeText$6(block.text));
|
|
28455
28886
|
else if (block.type === "tool-call" && typeof block.name === "string") toolCalls.push({
|
|
28456
28887
|
id: toolCallIds.normalize(typeof block.id === "string" && block.id !== "" ? block.id : `call_${toolCalls.length}`),
|
|
@@ -28464,7 +28895,7 @@ function openAIAssistantContent$2(message, toolCallIds) {
|
|
|
28464
28895
|
return {
|
|
28465
28896
|
content: textParts.join(""),
|
|
28466
28897
|
toolCalls,
|
|
28467
|
-
reasoning:
|
|
28898
|
+
reasoning: replayedReasoning(message, reasoningKey)
|
|
28468
28899
|
};
|
|
28469
28900
|
}
|
|
28470
28901
|
/** Why one declaration could not be put on the wire, as the model sees it. */
|
|
@@ -28493,6 +28924,28 @@ function declarationSlots(options, media) {
|
|
|
28493
28924
|
const slots = [];
|
|
28494
28925
|
let beforeIndex = 0;
|
|
28495
28926
|
for (const message of options.messages) {
|
|
28927
|
+
if (message.role === "developer") {
|
|
28928
|
+
const additions = declarationsFor(toolAdditionNames(message), options.tools);
|
|
28929
|
+
const entries = [];
|
|
28930
|
+
if (additions.length > 0) entries.push(media.messageTools === true ? {
|
|
28931
|
+
role: "system",
|
|
28932
|
+
tools: additions.map(openAIDynamicTool)
|
|
28933
|
+
} : {
|
|
28934
|
+
role: "system",
|
|
28935
|
+
content: declarationNotice(options.model, additions.length, "capability")
|
|
28936
|
+
});
|
|
28937
|
+
const removals = toolRemovalNames(message);
|
|
28938
|
+
if (removals.length > 0) entries.push({
|
|
28939
|
+
role: "system",
|
|
28940
|
+
content: removalNotice(removals)
|
|
28941
|
+
});
|
|
28942
|
+
if (entries.length > 0) slots.push({
|
|
28943
|
+
beforeIndex,
|
|
28944
|
+
entries
|
|
28945
|
+
});
|
|
28946
|
+
beforeIndex += 1;
|
|
28947
|
+
continue;
|
|
28948
|
+
}
|
|
28496
28949
|
if (message.role !== "system") {
|
|
28497
28950
|
beforeIndex += 1;
|
|
28498
28951
|
continue;
|
|
@@ -28526,6 +28979,58 @@ function declarationSlots(options, media) {
|
|
|
28526
28979
|
}
|
|
28527
28980
|
return slots;
|
|
28528
28981
|
}
|
|
28982
|
+
/**
|
|
28983
|
+
* Write one assistant turn's reasoning under the field it arrived under.
|
|
28984
|
+
*
|
|
28985
|
+
* Two rules are in tension here and both matter:
|
|
28986
|
+
*
|
|
28987
|
+
* - Preserved Thinking requires a reasoning field on every assistant message, so
|
|
28988
|
+
* the learned dialect key is ALWAYS written, with an empty string when the
|
|
28989
|
+
* turn produced no reasoning. Omitting it is the documented cause of
|
|
28990
|
+
* "thinking is enabled but reasoning_content is missing in assistant tool call
|
|
28991
|
+
* message at index N".
|
|
28992
|
+
* - A replayed fragment must not be written under a key it did not come from, so
|
|
28993
|
+
* each surviving field is written on its own and nothing is concatenated onto
|
|
28994
|
+
* the dialect key. `reasoning_details` goes back in the shape it arrived in: a
|
|
28995
|
+
* bare string stays a string, elements stay an array.
|
|
28996
|
+
*/
|
|
28997
|
+
function applyReplayedReasoning(entry, reasoning, dialectKey) {
|
|
28998
|
+
const fields = reasoning.fields.length === 0 ? [{
|
|
28999
|
+
key: dialectKey,
|
|
29000
|
+
text: ""
|
|
29001
|
+
}] : reasoning.fields;
|
|
29002
|
+
for (const field of fields) entry[field.key] = field.text;
|
|
29003
|
+
if (fields.every((field) => field.key !== dialectKey)) entry[dialectKey] = "";
|
|
29004
|
+
if (reasoning.details.length > 0) {
|
|
29005
|
+
const elements = reasoning.details.map(replayedDetailElement);
|
|
29006
|
+
if (reasoning.detailsString !== void 0 && reasoning.detailsString !== "") elements.unshift({ summary: reasoning.detailsString });
|
|
29007
|
+
entry.reasoning_details = elements;
|
|
29008
|
+
} else if (reasoning.detailsString !== void 0) entry.reasoning_details = reasoning.detailsString;
|
|
29009
|
+
}
|
|
29010
|
+
/**
|
|
29011
|
+
* One replayed element, in the shape the peer sent it in.
|
|
29012
|
+
*
|
|
29013
|
+
* `type` is echoed only when the wire carried one (a summary may have none), and
|
|
29014
|
+
* `index` is echoed only when the wire carried one - neither is invented.
|
|
29015
|
+
*/
|
|
29016
|
+
function replayedDetailElement(detail) {
|
|
29017
|
+
const index = detail.index === void 0 ? {} : { index: detail.index };
|
|
29018
|
+
if (detail.form === "encrypted") return {
|
|
29019
|
+
...detail.wireType === void 0 ? {} : { type: detail.wireType },
|
|
29020
|
+
encrypted: detail.encrypted,
|
|
29021
|
+
...index
|
|
29022
|
+
};
|
|
29023
|
+
if (detail.form === "summary") return {
|
|
29024
|
+
...detail.wireType === void 0 ? {} : { type: detail.wireType },
|
|
29025
|
+
summary: detail.summary,
|
|
29026
|
+
...index
|
|
29027
|
+
};
|
|
29028
|
+
return {
|
|
29029
|
+
type: "reasoning.text",
|
|
29030
|
+
text: detail.text,
|
|
29031
|
+
...index
|
|
29032
|
+
};
|
|
29033
|
+
}
|
|
28529
29034
|
/** Build one `/chat/completions` body. */
|
|
28530
29035
|
function buildOpenAIRequest(options, images = NO_RESOLVED_IMAGES$3, preserveThinking = preserveThinkingEnabled(), media = {}) {
|
|
28531
29036
|
const effort = mapReasoningEffort(options.reasoningEffort === void 0 ? void 0 : String(options.reasoningEffort));
|
|
@@ -28561,13 +29066,13 @@ function buildOpenAIRequest(options, images = NO_RESOLVED_IMAGES$3, preserveThin
|
|
|
28561
29066
|
nonSystemIndex += 1;
|
|
28562
29067
|
}
|
|
28563
29068
|
const block = current.content[0];
|
|
28564
|
-
const callId = isRecord$
|
|
29069
|
+
const callId = isRecord$36(block) && typeof block.toolCallId === "string" ? block.toolCallId : "";
|
|
28565
29070
|
messages.push({
|
|
28566
29071
|
role: "tool",
|
|
28567
29072
|
tool_call_id: toolCallIds.normalize(callId),
|
|
28568
29073
|
content: toolResultText$6(current.content)
|
|
28569
29074
|
});
|
|
28570
|
-
imageBlocks.push(...toolResultImageBlocks$3(current.content, images));
|
|
29075
|
+
imageBlocks.push(...toolResultImageBlocks$3(current.content, images, media));
|
|
28571
29076
|
index += 1;
|
|
28572
29077
|
}
|
|
28573
29078
|
index -= 1;
|
|
@@ -28578,12 +29083,13 @@ function buildOpenAIRequest(options, images = NO_RESOLVED_IMAGES$3, preserveThin
|
|
|
28578
29083
|
continue;
|
|
28579
29084
|
}
|
|
28580
29085
|
if (message.role === "assistant") {
|
|
28581
|
-
const
|
|
28582
|
-
|
|
29086
|
+
const reasoningKey = reasoningDialect.outboundKey();
|
|
29087
|
+
const { content, toolCalls, reasoning } = openAIAssistantContent$2(message, toolCallIds, reasoningKey);
|
|
29088
|
+
if (content === "" && toolCalls.length === 0 && reasoning.fields.length === 0 && reasoning.details.length === 0) continue;
|
|
28583
29089
|
const entry = { role: "assistant" };
|
|
28584
29090
|
if (content !== "" || toolCalls.length === 0) entry.content = content;
|
|
28585
29091
|
if (toolCalls.length > 0) entry.tool_calls = toolCalls;
|
|
28586
|
-
if (thinkingOn) entry
|
|
29092
|
+
if (thinkingOn) applyReplayedReasoning(entry, reasoning, reasoningKey);
|
|
28587
29093
|
messages.push(entry);
|
|
28588
29094
|
continue;
|
|
28589
29095
|
}
|
|
@@ -28595,6 +29101,7 @@ function buildOpenAIRequest(options, images = NO_RESOLVED_IMAGES$3, preserveThin
|
|
|
28595
29101
|
});
|
|
28596
29102
|
}
|
|
28597
29103
|
flushSlots(nonSystemIndex);
|
|
29104
|
+
const topLevelTools = (options.tools ?? []).filter((tool) => tool.deferLoading !== true);
|
|
28598
29105
|
const maxTokens = options.maxTokens ?? maxOutputTokensFor$4(options.model);
|
|
28599
29106
|
const body = {
|
|
28600
29107
|
model: options.model,
|
|
@@ -28603,8 +29110,8 @@ function buildOpenAIRequest(options, images = NO_RESOLVED_IMAGES$3, preserveThin
|
|
|
28603
29110
|
stream_options: { include_usage: true },
|
|
28604
29111
|
max_completion_tokens: maxTokens,
|
|
28605
29112
|
...stopSequences(options.stop).length > 0 ? { stop: stopSequences(options.stop) } : {},
|
|
28606
|
-
...
|
|
28607
|
-
tools:
|
|
29113
|
+
...topLevelTools.length > 0 ? {
|
|
29114
|
+
tools: topLevelTools.map((tool) => ({
|
|
28608
29115
|
type: "function",
|
|
28609
29116
|
function: {
|
|
28610
29117
|
name: tool.name,
|
|
@@ -28647,7 +29154,7 @@ function anthropicUserContent$2(message, images, toolCallIds) {
|
|
|
28647
29154
|
if (!Array.isArray(message.content)) return [];
|
|
28648
29155
|
const blocks = [];
|
|
28649
29156
|
for (const block of message.content) {
|
|
28650
|
-
if (!isRecord$
|
|
29157
|
+
if (!isRecord$36(block)) continue;
|
|
28651
29158
|
if (block.type === "video") {
|
|
28652
29159
|
blocks.push({
|
|
28653
29160
|
type: "text",
|
|
@@ -28691,7 +29198,7 @@ function anthropicUserContent$2(message, images, toolCallIds) {
|
|
|
28691
29198
|
function anthropicAssistantContent$2(message, toolCallIds) {
|
|
28692
29199
|
const blocks = [];
|
|
28693
29200
|
for (const block of message.content) {
|
|
28694
|
-
if (!isRecord$
|
|
29201
|
+
if (!isRecord$36(block)) continue;
|
|
28695
29202
|
if (block.type === "text" && typeof block.text === "string") {
|
|
28696
29203
|
const text = sanitizeText$6(block.text);
|
|
28697
29204
|
if (text !== "") blocks.push({
|
|
@@ -28704,7 +29211,7 @@ function anthropicAssistantContent$2(message, toolCallIds) {
|
|
|
28704
29211
|
type: "tool_use",
|
|
28705
29212
|
id: toolCallIds.normalize(typeof block.id === "string" && block.id !== "" ? block.id : `toolu_${blocks.length}`),
|
|
28706
29213
|
name: block.name,
|
|
28707
|
-
input: isRecord$
|
|
29214
|
+
input: isRecord$36(parsed) ? parsed : {}
|
|
28708
29215
|
});
|
|
28709
29216
|
}
|
|
28710
29217
|
}
|
|
@@ -28826,7 +29333,7 @@ function walkJson(value, visit) {
|
|
|
28826
29333
|
for (const item of value) walkJson(item, visit);
|
|
28827
29334
|
return;
|
|
28828
29335
|
}
|
|
28829
|
-
if (!isRecord$
|
|
29336
|
+
if (!isRecord$36(value)) return;
|
|
28830
29337
|
visit(value);
|
|
28831
29338
|
for (const nested of Object.values(value)) walkJson(nested, visit);
|
|
28832
29339
|
}
|
|
@@ -28842,12 +29349,12 @@ function requestBodyBreakdown(body, carriesVideo = false) {
|
|
|
28842
29349
|
const totalBytes = Buffer.byteLength(serialized, "utf8");
|
|
28843
29350
|
let imageBytes = 0;
|
|
28844
29351
|
walkJson(body, (node) => {
|
|
28845
|
-
const url = isRecord$
|
|
29352
|
+
const url = isRecord$36(node.image_url) ? asString$16(node.image_url.url) : void 0;
|
|
28846
29353
|
if (url?.startsWith("data:")) {
|
|
28847
29354
|
imageBytes += Buffer.byteLength(url, "utf8");
|
|
28848
29355
|
return;
|
|
28849
29356
|
}
|
|
28850
|
-
const source = isRecord$
|
|
29357
|
+
const source = isRecord$36(node.source) ? node.source : void 0;
|
|
28851
29358
|
if (source && asString$16(source.data) !== void 0 && asString$16(source.media_type) !== void 0) imageBytes += Buffer.byteLength(asString$16(source.data) ?? "", "utf8");
|
|
28852
29359
|
});
|
|
28853
29360
|
const toolSchemaBytes = Buffer.byteLength(JSON.stringify(body.tools ?? []), "utf8");
|
|
@@ -28886,6 +29393,7 @@ function createStreamState$4(wire, sessionId) {
|
|
|
28886
29393
|
sessionId: typeof sessionId === "string" && sessionId.trim() !== "" ? sessionId.trim() : void 0,
|
|
28887
29394
|
blocks: [],
|
|
28888
29395
|
current: null,
|
|
29396
|
+
reasoningRuns: /* @__PURE__ */ new Map(),
|
|
28889
29397
|
toolCalls: /* @__PURE__ */ new Map(),
|
|
28890
29398
|
contentIndexes: /* @__PURE__ */ new Map(),
|
|
28891
29399
|
openContentIndex: null,
|
|
@@ -28972,9 +29480,9 @@ function processOpenAIStreamLine(line, state) {
|
|
|
28972
29480
|
}
|
|
28973
29481
|
if (payload === "") return [];
|
|
28974
29482
|
const chunk = safeJsonParse$6(payload);
|
|
28975
|
-
if (!isRecord$
|
|
29483
|
+
if (!isRecord$36(chunk)) return [];
|
|
28976
29484
|
const out = [];
|
|
28977
|
-
const errorPayload = isRecord$
|
|
29485
|
+
const errorPayload = isRecord$36(chunk.error) ? chunk.error : void 0;
|
|
28978
29486
|
if (errorPayload !== void 0) {
|
|
28979
29487
|
const message = asString$16(errorPayload.message) ?? "unknown error";
|
|
28980
29488
|
state.streamError = {
|
|
@@ -28984,34 +29492,47 @@ function processOpenAIStreamLine(line, state) {
|
|
|
28984
29492
|
};
|
|
28985
29493
|
throw new LlmError$1(`Kimi Code stream error: ${message}`, isContextOverflow(errorPayload) ? CONTEXT_OVERFLOW_CODE : "PROVIDER_ERROR");
|
|
28986
29494
|
}
|
|
28987
|
-
const usage = isRecord$
|
|
29495
|
+
const usage = isRecord$36(chunk.usage) ? chunk.usage : void 0;
|
|
28988
29496
|
if (usage) {
|
|
28989
29497
|
state.sawUsage = true;
|
|
28990
29498
|
const prompt = numberOr$7(usage.prompt_tokens, 0);
|
|
28991
|
-
const details = isRecord$
|
|
29499
|
+
const details = isRecord$36(usage.prompt_tokens_details) ? usage.prompt_tokens_details : void 0;
|
|
28992
29500
|
const cached = details ? numberOr$7(details.cached_tokens, 0) : 0;
|
|
28993
29501
|
const written = details ? numberOr$7(details.cache_write_tokens, 0) : 0;
|
|
28994
29502
|
state.inputTokens = Math.max(0, prompt - cached - written);
|
|
28995
29503
|
state.cacheReadTokens = cached;
|
|
28996
29504
|
state.cacheWriteTokens = written;
|
|
28997
29505
|
state.outputTokens = numberOr$7(usage.completion_tokens, state.outputTokens);
|
|
28998
|
-
if (isRecord$
|
|
29506
|
+
if (isRecord$36(usage.completion_tokens_details)) state.reasoningTokens = numberOr$7(usage.completion_tokens_details.reasoning_tokens, state.reasoningTokens);
|
|
28999
29507
|
}
|
|
29000
29508
|
const choices = Array.isArray(chunk.choices) ? chunk.choices : [];
|
|
29001
|
-
const choice = isRecord$
|
|
29002
|
-
const delta = isRecord$
|
|
29509
|
+
const choice = isRecord$36(choices[0]) ? choices[0] : void 0;
|
|
29510
|
+
const delta = isRecord$36(choice?.delta) ? choice.delta : void 0;
|
|
29003
29511
|
if (delta) {
|
|
29004
|
-
const
|
|
29005
|
-
|
|
29512
|
+
for (const fragment of reasoningDialect.observeAll(delta)) {
|
|
29513
|
+
const text = sanitizeText$6(fragment.text);
|
|
29514
|
+
if (text === "" && fragment.form !== "encrypted") continue;
|
|
29006
29515
|
out.push(...closeToolCalls$4(state));
|
|
29007
29516
|
if (state.current === null || state.current.type !== "reasoning") out.push(...openTextBlock$4(state, "reasoning"));
|
|
29008
|
-
|
|
29009
|
-
|
|
29010
|
-
|
|
29011
|
-
|
|
29012
|
-
|
|
29013
|
-
|
|
29517
|
+
if (text !== "") {
|
|
29518
|
+
state.current.text += text;
|
|
29519
|
+
state.hasContent = true;
|
|
29520
|
+
out.push({
|
|
29521
|
+
type: "reasoning-delta",
|
|
29522
|
+
index: state.current.index,
|
|
29523
|
+
text
|
|
29524
|
+
});
|
|
29525
|
+
}
|
|
29526
|
+
const runs = state.reasoningRuns.get(state.current.index) ?? [];
|
|
29527
|
+
runs.push({
|
|
29528
|
+
key: fragment.key,
|
|
29529
|
+
...fragment.form === void 0 ? {} : { form: fragment.form },
|
|
29530
|
+
...fragment.wireType === void 0 ? {} : { wireType: fragment.wireType },
|
|
29531
|
+
...fragment.index === void 0 ? {} : { index: fragment.index },
|
|
29532
|
+
...fragment.encrypted === void 0 ? {} : { encrypted: fragment.encrypted },
|
|
29533
|
+
length: text.length
|
|
29014
29534
|
});
|
|
29535
|
+
state.reasoningRuns.set(state.current.index, runs);
|
|
29015
29536
|
}
|
|
29016
29537
|
const content = asString$16(delta.content);
|
|
29017
29538
|
if (content !== void 0 && content !== "") {
|
|
@@ -29027,7 +29548,7 @@ function processOpenAIStreamLine(line, state) {
|
|
|
29027
29548
|
}
|
|
29028
29549
|
const toolDeltas = Array.isArray(delta.tool_calls) ? delta.tool_calls : [];
|
|
29029
29550
|
for (const entry of toolDeltas) {
|
|
29030
|
-
if (!isRecord$
|
|
29551
|
+
if (!isRecord$36(entry)) continue;
|
|
29031
29552
|
out.push(...applyOpenAIToolDelta(entry, state));
|
|
29032
29553
|
}
|
|
29033
29554
|
}
|
|
@@ -29041,7 +29562,7 @@ function processOpenAIStreamLine(line, state) {
|
|
|
29041
29562
|
}
|
|
29042
29563
|
function applyOpenAIToolDelta(entry, state) {
|
|
29043
29564
|
const wireIndex = typeof entry.index === "number" ? entry.index : 0;
|
|
29044
|
-
const fn = isRecord$
|
|
29565
|
+
const fn = isRecord$36(entry.function) ? entry.function : {};
|
|
29045
29566
|
const out = [];
|
|
29046
29567
|
let call = state.toolCalls.get(wireIndex);
|
|
29047
29568
|
if (call === void 0) {
|
|
@@ -29096,12 +29617,12 @@ function processAnthropicStreamLine(line, state) {
|
|
|
29096
29617
|
const payload = trimmed.slice(5).trim();
|
|
29097
29618
|
if (payload === "" || payload === "[DONE]") return [];
|
|
29098
29619
|
const event = safeJsonParse$6(payload);
|
|
29099
|
-
if (!isRecord$
|
|
29620
|
+
if (!isRecord$36(event)) return [];
|
|
29100
29621
|
const type = asString$16(event.type);
|
|
29101
29622
|
const out = [];
|
|
29102
29623
|
if (type === "message_start") {
|
|
29103
|
-
const message = isRecord$
|
|
29104
|
-
const usage = message && isRecord$
|
|
29624
|
+
const message = isRecord$36(event.message) ? event.message : void 0;
|
|
29625
|
+
const usage = message && isRecord$36(message.usage) ? message.usage : void 0;
|
|
29105
29626
|
if (usage) {
|
|
29106
29627
|
state.sawUsage = true;
|
|
29107
29628
|
state.inputTokens = numberOr$7(usage.input_tokens, 0);
|
|
@@ -29115,7 +29636,7 @@ function processAnthropicStreamLine(line, state) {
|
|
|
29115
29636
|
}
|
|
29116
29637
|
if (type === "content_block_start") {
|
|
29117
29638
|
const contentIndex = numberOr$7(event.index, 0);
|
|
29118
|
-
const block = isRecord$
|
|
29639
|
+
const block = isRecord$36(event.content_block) ? event.content_block : {};
|
|
29119
29640
|
const blockType = asString$16(block.type);
|
|
29120
29641
|
out.push(...closeCurrent$4(state));
|
|
29121
29642
|
if (blockType === "tool_use") {
|
|
@@ -29165,7 +29686,7 @@ function processAnthropicStreamLine(line, state) {
|
|
|
29165
29686
|
}
|
|
29166
29687
|
if (type === "content_block_delta") {
|
|
29167
29688
|
const contentIndex = numberOr$7(event.index, 0);
|
|
29168
|
-
const delta = isRecord$
|
|
29689
|
+
const delta = isRecord$36(event.delta) ? event.delta : {};
|
|
29169
29690
|
const deltaType = asString$16(delta.type);
|
|
29170
29691
|
if (deltaType === "input_json_delta") {
|
|
29171
29692
|
const pending = state.toolCalls.get(contentIndex);
|
|
@@ -29238,10 +29759,10 @@ function processAnthropicStreamLine(line, state) {
|
|
|
29238
29759
|
return out;
|
|
29239
29760
|
}
|
|
29240
29761
|
if (type === "message_delta") {
|
|
29241
|
-
const delta = isRecord$
|
|
29762
|
+
const delta = isRecord$36(event.delta) ? event.delta : void 0;
|
|
29242
29763
|
const stop = delta ? asString$16(delta.stop_reason) : void 0;
|
|
29243
29764
|
if (stop !== void 0 && stop !== "") state.finishReason = stop;
|
|
29244
|
-
const usage = isRecord$
|
|
29765
|
+
const usage = isRecord$36(event.usage) ? event.usage : void 0;
|
|
29245
29766
|
if (usage) {
|
|
29246
29767
|
state.sawUsage = true;
|
|
29247
29768
|
state.outputTokens = numberOr$7(usage.output_tokens, state.outputTokens);
|
|
@@ -29253,7 +29774,7 @@ function processAnthropicStreamLine(line, state) {
|
|
|
29253
29774
|
return closeStream$3(state);
|
|
29254
29775
|
}
|
|
29255
29776
|
if (type === "error") {
|
|
29256
|
-
const error = isRecord$
|
|
29777
|
+
const error = isRecord$36(event.error) ? event.error : {};
|
|
29257
29778
|
const message = asString$16(error.message) ?? "unknown error";
|
|
29258
29779
|
state.streamError = {
|
|
29259
29780
|
vocabulary: "anthropic",
|
|
@@ -29512,6 +30033,46 @@ function finishReasonFor$6(state) {
|
|
|
29512
30033
|
if (state.hasToolCall || reason === "tool_calls" || reason === "tool_use") return { kind: "tool-calls" };
|
|
29513
30034
|
return { kind: "stop" };
|
|
29514
30035
|
}
|
|
30036
|
+
/**
|
|
30037
|
+
* Replay metadata: which wire field every reasoning slice arrived under.
|
|
30038
|
+
*
|
|
30039
|
+
* The envelope is the only channel this route has for provenance. The harness's
|
|
30040
|
+
* `ReasoningBlock` is `{type, text}`, and its assembler rebuilds a streamed
|
|
30041
|
+
* reasoning block from the accumulated deltas, so a field added to the block
|
|
30042
|
+
* would be dropped before the message is stored. A terminal `finish` chunk's
|
|
30043
|
+
* `replayState` is persisted verbatim onto the assembled message's model
|
|
30044
|
+
* source, which is where the request builder reads it back - the Claude route in
|
|
30045
|
+
* this package relies on the same seam.
|
|
30046
|
+
*
|
|
30047
|
+
* Shape: one entry per emitted block, because assembly discards an envelope
|
|
30048
|
+
* whose block list does not match the emitted block count. A text or tool-call
|
|
30049
|
+
* block is therefore an explicit `null` rather than a skipped slot, and a
|
|
30050
|
+
* reasoning block is `{ reasoning: [{key, form?, wireType?, index?, encrypted?,
|
|
30051
|
+
* length}] }`. Only lengths are stored, never the text, and an encrypted run
|
|
30052
|
+
* carries `length: 0` plus the opaque payload — the one thing that must be
|
|
30053
|
+
* handed back to the provider unchanged. Everything here is plain JSON (strings,
|
|
30054
|
+
* numbers, arrays, nulls), which is what the harness's lossless-JSON check
|
|
30055
|
+
* requires.
|
|
30056
|
+
*/
|
|
30057
|
+
function reasoningReplayState(state) {
|
|
30058
|
+
if (state.reasoningRuns.size === 0) return void 0;
|
|
30059
|
+
const blocks = state.blocks.map((block, index) => {
|
|
30060
|
+
const runs = state.reasoningRuns.get(index);
|
|
30061
|
+
if (runs === void 0 || runs.length === 0 || block.type !== "reasoning") return null;
|
|
30062
|
+
return { reasoning: runs.map((run) => ({
|
|
30063
|
+
key: run.key,
|
|
30064
|
+
...run.form === void 0 ? {} : { form: run.form },
|
|
30065
|
+
...run.wireType === void 0 ? {} : { wireType: run.wireType },
|
|
30066
|
+
...run.index === void 0 ? {} : { index: run.index },
|
|
30067
|
+
...run.encrypted === void 0 ? {} : { encrypted: run.encrypted },
|
|
30068
|
+
length: run.length
|
|
30069
|
+
})) };
|
|
30070
|
+
});
|
|
30071
|
+
return {
|
|
30072
|
+
response: { wire: state.wire },
|
|
30073
|
+
blocks
|
|
30074
|
+
};
|
|
30075
|
+
}
|
|
29515
30076
|
/** Flush every open block, then emit usage and the terminal finish. */
|
|
29516
30077
|
function closeStream$3(state, accountId) {
|
|
29517
30078
|
if (state.finished) return [];
|
|
@@ -29524,9 +30085,11 @@ function closeStream$3(state, accountId) {
|
|
|
29524
30085
|
usage: tokenUsage$4(state)
|
|
29525
30086
|
});
|
|
29526
30087
|
}
|
|
30088
|
+
const replayState = reasoningReplayState(state);
|
|
29527
30089
|
out.push({
|
|
29528
30090
|
type: "finish",
|
|
29529
|
-
reason: finishReasonFor$6(state)
|
|
30091
|
+
reason: finishReasonFor$6(state),
|
|
30092
|
+
...replayState === void 0 ? {} : { replayState }
|
|
29530
30093
|
});
|
|
29531
30094
|
return out;
|
|
29532
30095
|
}
|
|
@@ -29534,6 +30097,737 @@ function closeStream$3(state, accountId) {
|
|
|
29534
30097
|
function assertStreamComplete$4(state) {
|
|
29535
30098
|
if (!state.done && state.finishReason === null) throw new LlmError$1("Kimi Code stream ended before its terminal event", "TRANSPORT");
|
|
29536
30099
|
}
|
|
30100
|
+
/** Where the document lives; beside the other plugin-owned JSON. */
|
|
30101
|
+
function sessionCacheStatePath() {
|
|
30102
|
+
return path.join(dshHomeDir(), "storages", "dsh-chatgpt-subscription-kimi-cache.json");
|
|
30103
|
+
}
|
|
30104
|
+
/** Distinguishes concurrent temporary files written by one process. */
|
|
30105
|
+
let writeSequence = 0;
|
|
30106
|
+
/**
|
|
30107
|
+
* Whether a scheduled write should reach the disk.
|
|
30108
|
+
*
|
|
30109
|
+
* A unit test that exercises the request path must not overwrite the state file
|
|
30110
|
+
* of the machine running it; tests build their own store against a temporary
|
|
30111
|
+
* path (which always writes) and call {@link SessionCacheStateStore.flush}.
|
|
30112
|
+
*/
|
|
30113
|
+
function persistenceEnabled() {
|
|
30114
|
+
return process.env.VITEST !== "true" && process.env.NODE_ENV !== "test";
|
|
30115
|
+
}
|
|
30116
|
+
/**
|
|
30117
|
+
* Per-session cache state held in one JSON document.
|
|
30118
|
+
*
|
|
30119
|
+
* Reads are synchronous because every caller is answering a status request or
|
|
30120
|
+
* closing a turn, both of which are allowed to block on a file this size. Writes
|
|
30121
|
+
* are debounced: a turn reports once, and several turns in flight must not
|
|
30122
|
+
* rewrite the document several times.
|
|
30123
|
+
*/
|
|
30124
|
+
var SessionCacheStateStore = class {
|
|
30125
|
+
document;
|
|
30126
|
+
pending;
|
|
30127
|
+
/**
|
|
30128
|
+
* @param filePath - Document location; defaults to {@link sessionCacheStatePath}.
|
|
30129
|
+
* @param maxEntries - Conversations kept; the oldest are dropped first.
|
|
30130
|
+
*/
|
|
30131
|
+
filePath;
|
|
30132
|
+
maxEntries;
|
|
30133
|
+
/**
|
|
30134
|
+
* Whether this store may write at all.
|
|
30135
|
+
*
|
|
30136
|
+
* A store built on an explicit path is someone's own file - a test's temporary
|
|
30137
|
+
* document - and always writes. The default-path store belongs to the user's
|
|
30138
|
+
* harness home, and a test that never named a path must not overwrite it.
|
|
30139
|
+
*/
|
|
30140
|
+
allowWrite;
|
|
30141
|
+
constructor(filePath = sessionCacheStatePath(), maxEntries = 64) {
|
|
30142
|
+
this.filePath = filePath;
|
|
30143
|
+
this.maxEntries = maxEntries;
|
|
30144
|
+
this.allowWrite = filePath !== sessionCacheStatePath() || persistenceEnabled();
|
|
30145
|
+
}
|
|
30146
|
+
/**
|
|
30147
|
+
* The record for one conversation, or the most recently active one.
|
|
30148
|
+
*
|
|
30149
|
+
* The fallback is what makes the status card useful without a session named:
|
|
30150
|
+
* the card describes the line, and the only conversation it can honestly
|
|
30151
|
+
* describe in that case is the one that ran last.
|
|
30152
|
+
* @param sessionId - Conversation to read; omit for the most recent one.
|
|
30153
|
+
* @returns the record, or undefined when nothing has ever been recorded.
|
|
30154
|
+
*/
|
|
30155
|
+
read(sessionId) {
|
|
30156
|
+
const sessions = this.load().sessions;
|
|
30157
|
+
const named = sessionId?.trim();
|
|
30158
|
+
if (named !== void 0 && named !== "") return sessions[named];
|
|
30159
|
+
let newest;
|
|
30160
|
+
for (const record of Object.values(sessions)) if (newest === void 0 || record.updatedAt > newest.updatedAt) newest = record;
|
|
30161
|
+
return newest;
|
|
30162
|
+
}
|
|
30163
|
+
/**
|
|
30164
|
+
* File one conversation's state and schedule the document write.
|
|
30165
|
+
* @param sessionId - Conversation the record belongs to; ignored when blank.
|
|
30166
|
+
* @param record - Complete record for that conversation.
|
|
30167
|
+
*/
|
|
30168
|
+
write(sessionId, record) {
|
|
30169
|
+
const key = sessionId.trim();
|
|
30170
|
+
if (key === "") return;
|
|
30171
|
+
const document = this.load();
|
|
30172
|
+
document.sessions[key] = record;
|
|
30173
|
+
this.prune(document);
|
|
30174
|
+
this.schedule();
|
|
30175
|
+
}
|
|
30176
|
+
/**
|
|
30177
|
+
* Write the document now, synchronously.
|
|
30178
|
+
*
|
|
30179
|
+
* Exposed for tests and for a caller that is about to lose the process; the
|
|
30180
|
+
* debounced path is what normal turns use.
|
|
30181
|
+
*/
|
|
30182
|
+
flush() {
|
|
30183
|
+
this.cancelPending();
|
|
30184
|
+
const document = this.document;
|
|
30185
|
+
if (document === void 0) return;
|
|
30186
|
+
this.persist(document);
|
|
30187
|
+
}
|
|
30188
|
+
/** Drop everything older than the newest maxEntries conversations. */
|
|
30189
|
+
prune(document) {
|
|
30190
|
+
const entries = Object.entries(document.sessions);
|
|
30191
|
+
if (entries.length <= this.maxEntries) return;
|
|
30192
|
+
entries.sort((a, b) => b[1].updatedAt - a[1].updatedAt);
|
|
30193
|
+
document.sessions = Object.fromEntries(entries.slice(0, this.maxEntries));
|
|
30194
|
+
}
|
|
30195
|
+
/** Read the document once; a missing or corrupt file reads as empty. */
|
|
30196
|
+
load() {
|
|
30197
|
+
if (this.document !== void 0) return this.document;
|
|
30198
|
+
try {
|
|
30199
|
+
const parsed = JSON.parse(fs$1.readFileSync(this.filePath, "utf8"));
|
|
30200
|
+
this.document = normalizeDocument(parsed);
|
|
30201
|
+
} catch {
|
|
30202
|
+
this.document = {
|
|
30203
|
+
version: 1,
|
|
30204
|
+
sessions: {}
|
|
30205
|
+
};
|
|
30206
|
+
}
|
|
30207
|
+
return this.document;
|
|
30208
|
+
}
|
|
30209
|
+
/** Coalesce the writes one turn's several fields would otherwise cause. */
|
|
30210
|
+
schedule() {
|
|
30211
|
+
if (!this.allowWrite || this.pending !== void 0) return;
|
|
30212
|
+
this.pending = setTimeout(() => {
|
|
30213
|
+
this.pending = void 0;
|
|
30214
|
+
const document = this.document;
|
|
30215
|
+
if (document !== void 0) this.persist(document);
|
|
30216
|
+
}, 250);
|
|
30217
|
+
this.pending.unref?.();
|
|
30218
|
+
}
|
|
30219
|
+
cancelPending() {
|
|
30220
|
+
if (this.pending === void 0) return;
|
|
30221
|
+
clearTimeout(this.pending);
|
|
30222
|
+
this.pending = void 0;
|
|
30223
|
+
}
|
|
30224
|
+
/** Write through a temporary file so a crash cannot leave half a document. */
|
|
30225
|
+
persist(document) {
|
|
30226
|
+
if (!this.allowWrite) return;
|
|
30227
|
+
try {
|
|
30228
|
+
fs$1.mkdirSync(path.dirname(this.filePath), { recursive: true });
|
|
30229
|
+
const temporary = this.filePath + "." + process.pid + "." + ++writeSequence + ".tmp";
|
|
30230
|
+
fs$1.writeFileSync(temporary, JSON.stringify(document), "utf8");
|
|
30231
|
+
fs$1.renameSync(temporary, this.filePath);
|
|
30232
|
+
} catch {}
|
|
30233
|
+
}
|
|
30234
|
+
};
|
|
30235
|
+
/** Validate one parsed document, dropping entries that do not describe a record. */
|
|
30236
|
+
function normalizeDocument(value) {
|
|
30237
|
+
const sessions = {};
|
|
30238
|
+
if (isRecord$35(value) && isRecord$35(value.sessions)) for (const [key, entry] of Object.entries(value.sessions)) {
|
|
30239
|
+
const record = normalizeRecord(entry);
|
|
30240
|
+
if (record !== void 0) sessions[key] = record;
|
|
30241
|
+
}
|
|
30242
|
+
return {
|
|
30243
|
+
version: 1,
|
|
30244
|
+
sessions
|
|
30245
|
+
};
|
|
30246
|
+
}
|
|
30247
|
+
function normalizeRecord(value) {
|
|
30248
|
+
if (!isRecord$35(value)) return void 0;
|
|
30249
|
+
const lastActiveAt = finite(value.lastActiveAt);
|
|
30250
|
+
const updatedAt = finite(value.updatedAt);
|
|
30251
|
+
if (lastActiveAt === void 0 || updatedAt === void 0) return void 0;
|
|
30252
|
+
const drift = typeof value.lastDrift === "string" && value.lastDrift !== "" ? value.lastDrift : void 0;
|
|
30253
|
+
return {
|
|
30254
|
+
lastActiveAt,
|
|
30255
|
+
updatedAt,
|
|
30256
|
+
totalTokens: count$1(value.totalTokens),
|
|
30257
|
+
requests: count$1(value.requests),
|
|
30258
|
+
cachedTokens: count$1(value.cachedTokens),
|
|
30259
|
+
freshTokens: count$1(value.freshTokens),
|
|
30260
|
+
outputTokens: count$1(value.outputTokens),
|
|
30261
|
+
cacheWriteTokens: count$1(value.cacheWriteTokens),
|
|
30262
|
+
cacheWrite5mTokens: count$1(value.cacheWrite5mTokens),
|
|
30263
|
+
cacheWrite1hTokens: count$1(value.cacheWrite1hTokens),
|
|
30264
|
+
...drift === void 0 ? {} : { lastDrift: drift }
|
|
30265
|
+
};
|
|
30266
|
+
}
|
|
30267
|
+
/**
|
|
30268
|
+
* Combine process memory with the durable record into the hint's two inputs.
|
|
30269
|
+
*
|
|
30270
|
+
* The process supplies the idle baseline whenever it has served the conversation,
|
|
30271
|
+
* because only it knows about the turn that just ended. The prompt size comes
|
|
30272
|
+
* from the file when it has one: the record is rewritten at every turn close, so
|
|
30273
|
+
* it holds the LAST request's size, while the process only offers a sum across
|
|
30274
|
+
* every turn of the conversation - which can overstate the next turn's cost by
|
|
30275
|
+
* an order of magnitude. With no conversation named the file is the only source,
|
|
30276
|
+
* because a process aggregate spans several conversations and would make "this
|
|
30277
|
+
* conversation's prompt" a number no single conversation has.
|
|
30278
|
+
* @param sessionId - Conversation being described; omit for "the most recent".
|
|
30279
|
+
* @param memory - What this process knows about it.
|
|
30280
|
+
* @param durable - What the last run on this machine left behind.
|
|
30281
|
+
* @returns the idle baseline and prompt size the expiry rule needs.
|
|
30282
|
+
*/
|
|
30283
|
+
function resolveSessionCacheView(sessionId, memory, durable) {
|
|
30284
|
+
if (sessionId === void 0 || sessionId.trim() === "") return {
|
|
30285
|
+
...durable === void 0 ? {} : { lastActiveAt: durable.lastActiveAt },
|
|
30286
|
+
totalTokens: durable?.totalTokens ?? 0
|
|
30287
|
+
};
|
|
30288
|
+
return {
|
|
30289
|
+
lastActiveAt: memory.memoryLastActiveAt ?? durable?.lastActiveAt,
|
|
30290
|
+
totalTokens: durable?.totalTokens ?? memory.memoryTotalTokens
|
|
30291
|
+
};
|
|
30292
|
+
}
|
|
30293
|
+
/** The process-wide store the adapter writes and the routes read. */
|
|
30294
|
+
const sessionCacheState = new SessionCacheStateStore();
|
|
30295
|
+
function isRecord$35(value) {
|
|
30296
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
30297
|
+
}
|
|
30298
|
+
function finite(value) {
|
|
30299
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
30300
|
+
}
|
|
30301
|
+
function count$1(value) {
|
|
30302
|
+
const number = finite(value);
|
|
30303
|
+
return number === void 0 || number < 0 ? 0 : Math.floor(number);
|
|
30304
|
+
}
|
|
30305
|
+
//#endregion
|
|
30306
|
+
//#region src/host/kimi-code/media-files.ts
|
|
30307
|
+
/**
|
|
30308
|
+
* Kimi Code hosted-file (media upload) client.
|
|
30309
|
+
*
|
|
30310
|
+
* WHY THIS FILE EXISTS. Everything this plugin has ever sent to the Kimi Code
|
|
30311
|
+
* gateway carries media INLINE, as a `data:<type>;base64,...` URL
|
|
30312
|
+
* (mapper.ts builds both the image and the video block that way). The gateway
|
|
30313
|
+
* also accepts a hosted reference instead: upload the bytes once, cite
|
|
30314
|
+
* `ms://<id>` afterwards. That is the only way to send a clip whose base64 would
|
|
30315
|
+
* overrun the request body ceiling, and it is what this module implements.
|
|
30316
|
+
*
|
|
30317
|
+
* EVERY WIRE FACT BELOW WAS MEASURED live against the subscription gateway by
|
|
30318
|
+
* `scripts/probe-kimi-files.probe.ts` on this line's own account and region,
|
|
30319
|
+
* not copied from the official client:
|
|
30320
|
+
*
|
|
30321
|
+
* - the upload is `POST {coding base}/v1/files`, `multipart/form-data` with a
|
|
30322
|
+
* `file` part and a `purpose` part whose value is `image` or `video`
|
|
30323
|
+
* (note: NOT the `*_understanding` vocabulary the MiniMax line uses);
|
|
30324
|
+
* - it authenticates with the bearer token plus the same `X-Msh-*` identity
|
|
30325
|
+
* headers every other request on this line sends;
|
|
30326
|
+
* - the body answers with the id at the top level or nested under
|
|
30327
|
+
* `data`/`file`/`result`, and the reference a later chat body cites is
|
|
30328
|
+
* `ms://<id>`;
|
|
30329
|
+
* - `content-type` is never set by hand: the runtime writes
|
|
30330
|
+
* `multipart/form-data; boundary=...` itself, and a hand-written JSON header
|
|
30331
|
+
* on a form body is a malformed request.
|
|
30332
|
+
*
|
|
30333
|
+
* DELIBERATELY NARROW. No adapter, mapper or route is touched here: this module
|
|
30334
|
+
* only uploads, caches and returns a reference. Integration is a later change.
|
|
30335
|
+
*
|
|
30336
|
+
* @module dsh-chatgpt-subscription/kimi-media-files
|
|
30337
|
+
*/
|
|
30338
|
+
/** Upload route, relative to a base URL that may already carry `/v1`. */
|
|
30339
|
+
const FILES_UPLOAD_PATH$1 = "/v1/files";
|
|
30340
|
+
/** Scheme a stored id is cited by on the wire. */
|
|
30341
|
+
const FILE_REF_SCHEME$1 = "ms://";
|
|
30342
|
+
/** Time budget for one upload request. */
|
|
30343
|
+
const MEDIA_UPLOAD_TIMEOUT_MS = 12e4;
|
|
30344
|
+
/**
|
|
30345
|
+
* One failed upload.
|
|
30346
|
+
*
|
|
30347
|
+
* Carries the HTTP status and a REDACTED digest of the response body so a
|
|
30348
|
+
* caller can decide (401 -> sign in again, 5xx -> retry) without the raw body
|
|
30349
|
+
* reaching a log. The access token is never placed in `message` or `detail`;
|
|
30350
|
+
* see {@link redact}.
|
|
30351
|
+
*/
|
|
30352
|
+
var KimiMediaUploadError = class extends Error {
|
|
30353
|
+
failure;
|
|
30354
|
+
status;
|
|
30355
|
+
/** Redacted one-line digest of the response body, or '' when there was none. */
|
|
30356
|
+
detail;
|
|
30357
|
+
constructor(failure, message, options = {}) {
|
|
30358
|
+
super(message);
|
|
30359
|
+
this.name = "KimiMediaUploadError";
|
|
30360
|
+
this.failure = failure;
|
|
30361
|
+
this.status = options.status ?? null;
|
|
30362
|
+
this.detail = options.detail ?? "";
|
|
30363
|
+
}
|
|
30364
|
+
};
|
|
30365
|
+
/**
|
|
30366
|
+
* The upload URL for one coding base.
|
|
30367
|
+
*
|
|
30368
|
+
* The base this line resolves normally carries neither a version nor a trailing
|
|
30369
|
+
* slash (`https://api.kimi.com/coding`), so the measured route is built by
|
|
30370
|
+
* appending `/v1/files` and the answer is `.../coding/v1/files`. A caller that
|
|
30371
|
+
* passes a base which ALREADY ends in `/v1` — the official client's own
|
|
30372
|
+
* KIMI_CODE_BASE_URL shape — gets the prefix removed once rather than doubled:
|
|
30373
|
+
* `.../coding/v1/v1/files` is a 404 that reads like a missing route.
|
|
30374
|
+
*/
|
|
30375
|
+
function filesUploadUrl$1(baseUrl) {
|
|
30376
|
+
const trimmed = baseUrl.trim().replace(/\/+$/, "");
|
|
30377
|
+
if (trimmed === "") throw new KimiMediaUploadError("invalid-request", "no base URL was given for the media upload");
|
|
30378
|
+
const base = trimmed.replace(/\/v1$/i, "").replace(/\/+$/, "");
|
|
30379
|
+
if (base === "") throw new KimiMediaUploadError("invalid-request", "the media upload base URL has no host");
|
|
30380
|
+
return base + FILES_UPLOAD_PATH$1;
|
|
30381
|
+
}
|
|
30382
|
+
/** The upload purpose one media type maps to, or undefined when unsupported. */
|
|
30383
|
+
function mediaKindFor(mediaType) {
|
|
30384
|
+
const normalized = mediaType.trim().toLowerCase();
|
|
30385
|
+
if (normalized.startsWith("image/")) return "image";
|
|
30386
|
+
if (normalized.startsWith("video/")) return "video";
|
|
30387
|
+
}
|
|
30388
|
+
/** File extensions for the media types this line sends, for the file part's name. */
|
|
30389
|
+
const MEDIA_TYPE_FILENAMES = {
|
|
30390
|
+
"image/png": "image.png",
|
|
30391
|
+
"image/jpeg": "image.jpg",
|
|
30392
|
+
"image/gif": "image.gif",
|
|
30393
|
+
"image/webp": "image.webp",
|
|
30394
|
+
"image/heic": "image.heic",
|
|
30395
|
+
"image/bmp": "image.bmp",
|
|
30396
|
+
"video/mp4": "video.mp4",
|
|
30397
|
+
"video/quicktime": "video.mov",
|
|
30398
|
+
"video/webm": "video.webm",
|
|
30399
|
+
"video/x-msvideo": "video.avi",
|
|
30400
|
+
"video/mpeg": "video.mpeg",
|
|
30401
|
+
"video/mpg": "video.mpg",
|
|
30402
|
+
"video/x-flv": "video.flv",
|
|
30403
|
+
"video/3gpp": "video.3gp"
|
|
30404
|
+
};
|
|
30405
|
+
/**
|
|
30406
|
+
* The name the file part carries.
|
|
30407
|
+
*
|
|
30408
|
+
* A caller-supplied name is reduced to its leaf and stripped of control
|
|
30409
|
+
* characters: the part name is metadata, never a path, so a Windows-style
|
|
30410
|
+
* source name must not travel with its directories. Nothing depends on the
|
|
30411
|
+
* extension, but a reviewer looking at a captured request benefits from a real
|
|
30412
|
+
* one, and some gateways sniff it.
|
|
30413
|
+
*/
|
|
30414
|
+
function filenameFor(kind, mediaType, provided) {
|
|
30415
|
+
const fallback = MEDIA_TYPE_FILENAMES[mediaType.trim().toLowerCase()] ?? (kind === "image" ? "image.bin" : "video.bin");
|
|
30416
|
+
if (provided === void 0) return fallback;
|
|
30417
|
+
const safe = (provided.split(/[\\/]/).pop() ?? "").replace(/[\u0000-\u001f\u007f"]/g, "").trim();
|
|
30418
|
+
return safe === "" ? fallback : safe;
|
|
30419
|
+
}
|
|
30420
|
+
/**
|
|
30421
|
+
* Remove anything token-shaped from a string that may reach a log or an error.
|
|
30422
|
+
*
|
|
30423
|
+
* The token this upload used is removed by exact match first — a body echoing
|
|
30424
|
+
* the request, a proxy page quoting the Authorization header, or a service that
|
|
30425
|
+
* reflects the credential all end up here — then the generic shapes (bearer
|
|
30426
|
+
* values, JWTs, e-mail addresses) are blanked. The result is whitespace-flattened
|
|
30427
|
+
* and bounded so one refusal cannot become a wall of text.
|
|
30428
|
+
*/
|
|
30429
|
+
function redact(text, token, limit = 240) {
|
|
30430
|
+
let out = text;
|
|
30431
|
+
if (token !== "") out = out.split(token).join("<redacted>");
|
|
30432
|
+
return out.replace(/Bearer\s+[A-Za-z0-9._~+/=-]{6,}/gi, "Bearer <redacted>").replace(/eyJ[A-Za-z0-9._-]{8,}/g, "<jwt>").replace(/[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}/g, "<email>").replace(/\s+/g, " ").trim().slice(0, limit);
|
|
30433
|
+
}
|
|
30434
|
+
/** Parse a body without throwing, reporting whether it was JSON at all. */
|
|
30435
|
+
function parseJson(raw) {
|
|
30436
|
+
const text = raw.trim();
|
|
30437
|
+
if (text === "") return {
|
|
30438
|
+
ok: false,
|
|
30439
|
+
value: void 0
|
|
30440
|
+
};
|
|
30441
|
+
try {
|
|
30442
|
+
return {
|
|
30443
|
+
ok: true,
|
|
30444
|
+
value: JSON.parse(text)
|
|
30445
|
+
};
|
|
30446
|
+
} catch {
|
|
30447
|
+
return {
|
|
30448
|
+
ok: false,
|
|
30449
|
+
value: void 0
|
|
30450
|
+
};
|
|
30451
|
+
}
|
|
30452
|
+
}
|
|
30453
|
+
/**
|
|
30454
|
+
* One-line digest of a refusal body.
|
|
30455
|
+
*
|
|
30456
|
+
* A JSON refusal names a code and a sentence; an HTML gateway page names
|
|
30457
|
+
* neither. Both are reduced to a short redacted line, because "the server said
|
|
30458
|
+
* no" and "the server said no because the model does not accept this type" lead
|
|
30459
|
+
* to different fixes.
|
|
30460
|
+
*/
|
|
30461
|
+
function bodyDigest(raw, token) {
|
|
30462
|
+
const text = raw.trim();
|
|
30463
|
+
if (text === "") return "";
|
|
30464
|
+
const parsed = parseJson(text);
|
|
30465
|
+
if (parsed.ok && isRecord$34(parsed.value)) {
|
|
30466
|
+
const root = parsed.value;
|
|
30467
|
+
const nested = isRecord$34(root.error) ? root.error : void 0;
|
|
30468
|
+
const code = firstString(nested?.code) ?? firstString(root.code) ?? firstString(root.error_code) ?? "";
|
|
30469
|
+
const message = nested !== void 0 ? firstString(nested.message) ?? firstString(nested.code) ?? "" : typeof root.error === "string" ? root.error : firstString(root.message) ?? firstString(root.detail) ?? "";
|
|
30470
|
+
const composed = [code === "" ? "" : "code=" + code, message].filter((part) => part !== "").join(" ");
|
|
30471
|
+
if (composed !== "") return redact(composed, token);
|
|
30472
|
+
}
|
|
30473
|
+
return redact(text, token);
|
|
30474
|
+
}
|
|
30475
|
+
/** Keys the id was measured to hide behind, besides a top-level one. */
|
|
30476
|
+
const NESTED_ID_KEYS = [
|
|
30477
|
+
"data",
|
|
30478
|
+
"file",
|
|
30479
|
+
"result"
|
|
30480
|
+
];
|
|
30481
|
+
/** Longest id accepted, so a body echoing a document cannot become a reference. */
|
|
30482
|
+
const MAX_FILE_ID_LENGTH = 200;
|
|
30483
|
+
/** A non-empty, single-token id; anything else is not a reference this line cites. */
|
|
30484
|
+
function normalizeFileId(value) {
|
|
30485
|
+
if (typeof value === "number") return Number.isSafeInteger(value) && value >= 0 ? String(value) : void 0;
|
|
30486
|
+
if (typeof value !== "string") return void 0;
|
|
30487
|
+
const trimmed = value.trim();
|
|
30488
|
+
const bare = trimmed.startsWith("ms://") ? trimmed.slice(5) : trimmed;
|
|
30489
|
+
if (bare === "" || bare.length > MAX_FILE_ID_LENGTH) return void 0;
|
|
30490
|
+
if (/[\s\u0000-\u001f\u007f]/.test(bare)) return void 0;
|
|
30491
|
+
return bare;
|
|
30492
|
+
}
|
|
30493
|
+
/**
|
|
30494
|
+
* The id from a successful upload body.
|
|
30495
|
+
*
|
|
30496
|
+
* The measured body carries it at the top level, and documented shapes nest it
|
|
30497
|
+
* under `data`, `file` or `result`; both are read so a service-side change of
|
|
30498
|
+
* envelope does not blank the whole chain. A body with no id is reported by the
|
|
30499
|
+
* caller as `no-id` rather than accepted as an empty reference.
|
|
30500
|
+
*/
|
|
30501
|
+
function readFileId$1(value, depth = 0) {
|
|
30502
|
+
if (depth > 3) return void 0;
|
|
30503
|
+
if (!isRecord$34(value)) return void 0;
|
|
30504
|
+
const direct = normalizeFileId(value.id);
|
|
30505
|
+
if (direct !== void 0) return direct;
|
|
30506
|
+
for (const key of NESTED_ID_KEYS) {
|
|
30507
|
+
const nested = readFileId$1(value[key], depth + 1);
|
|
30508
|
+
if (nested !== void 0) return nested;
|
|
30509
|
+
}
|
|
30510
|
+
}
|
|
30511
|
+
/** The wire reference for a stored id. */
|
|
30512
|
+
function fileReference(id) {
|
|
30513
|
+
return FILE_REF_SCHEME$1 + id;
|
|
30514
|
+
}
|
|
30515
|
+
/** Hex SHA-256 of the bytes; the content half of the key. */
|
|
30516
|
+
function contentSha256(data) {
|
|
30517
|
+
return createHash("sha256").update(data).digest("hex");
|
|
30518
|
+
}
|
|
30519
|
+
/**
|
|
30520
|
+
* The cache key for one upload.
|
|
30521
|
+
*
|
|
30522
|
+
* Five things, and each of them is load-bearing:
|
|
30523
|
+
*
|
|
30524
|
+
* - ACCOUNT SCOPE, because an id minted for one account is never assumed to be
|
|
30525
|
+
* readable by another (see {@link KimiMediaUploadRequest.accountScope});
|
|
30526
|
+
* - BASE URL (normalized through {@link filesUploadUrl}, so a trailing slash
|
|
30527
|
+
* cannot split one endpoint into two slots), because two regions or two
|
|
30528
|
+
* deployments are two distinct file stores;
|
|
30529
|
+
* - MEDIA KIND and MEDIA TYPE, because the same bytes uploaded as an image and
|
|
30530
|
+
* as a video are two different resources as far as the purpose is concerned;
|
|
30531
|
+
* - CONTENT SHA-256, so identical bytes reuse one upload and any change at all
|
|
30532
|
+
* misses the cache.
|
|
30533
|
+
*
|
|
30534
|
+
* The access token is NOT part of it: a key is written to memory only, but a
|
|
30535
|
+
* token in a key is a token in the next dump, and the account is already
|
|
30536
|
+
* identified by its scope. `|` cannot appear in a scope because the scope is
|
|
30537
|
+
* sanitized here.
|
|
30538
|
+
*/
|
|
30539
|
+
function mediaFileCacheKey(input) {
|
|
30540
|
+
const kind = mediaKindFor(input.mediaType) ?? "unsupported";
|
|
30541
|
+
return [
|
|
30542
|
+
"v1",
|
|
30543
|
+
input.accountScope.trim().replace(/[|\s]+/g, "_"),
|
|
30544
|
+
filesUploadUrl$1(input.baseUrl),
|
|
30545
|
+
kind,
|
|
30546
|
+
input.mediaType.trim().toLowerCase(),
|
|
30547
|
+
contentSha256(input.data)
|
|
30548
|
+
].join("|");
|
|
30549
|
+
}
|
|
30550
|
+
function isRecord$34(value) {
|
|
30551
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
30552
|
+
}
|
|
30553
|
+
function firstString(value) {
|
|
30554
|
+
return typeof value === "string" && value.trim() !== "" ? value.trim() : void 0;
|
|
30555
|
+
}
|
|
30556
|
+
/** Whether one rejection is a cancellation rather than an upload failure. */
|
|
30557
|
+
function looksAborted(error) {
|
|
30558
|
+
if (typeof error !== "object" || error === null) return false;
|
|
30559
|
+
return error.name === "AbortError";
|
|
30560
|
+
}
|
|
30561
|
+
/** The abort to reject a caller with, keeping the reason it supplied. */
|
|
30562
|
+
function abortError(signal) {
|
|
30563
|
+
const reason = signal?.reason;
|
|
30564
|
+
if (reason instanceof Error) return reason;
|
|
30565
|
+
const error = /* @__PURE__ */ new Error("the media upload was aborted");
|
|
30566
|
+
error.name = "AbortError";
|
|
30567
|
+
return error;
|
|
30568
|
+
}
|
|
30569
|
+
/**
|
|
30570
|
+
* Uploads one media file per distinct key, caching and single-flighting them.
|
|
30571
|
+
*
|
|
30572
|
+
* Two behaviours are deliberate and easy to get wrong:
|
|
30573
|
+
*
|
|
30574
|
+
* 1. SINGLE FLIGHT. Concurrent callers with the same key share one request.
|
|
30575
|
+
* A caller that aborts stops waiting immediately and gets an AbortError, but
|
|
30576
|
+
* the shared request is only cancelled once NO caller is still waiting — so
|
|
30577
|
+
* one cancelled turn does not fail another turn that wants the same clip.
|
|
30578
|
+
* 2. NO TTL. A successful reference is cached until it is evicted by the LRU
|
|
30579
|
+
* ceiling. Inventing an expiry would either expire a live id (a failed turn)
|
|
30580
|
+
* or outlive it silently; nothing here measured a lifetime, so nothing here
|
|
30581
|
+
* pretends to know one. A reference the service later rejects is dropped with
|
|
30582
|
+
* {@link forget} by whoever sees that rejection.
|
|
30583
|
+
*/
|
|
30584
|
+
var KimiMediaFilesUploader = class {
|
|
30585
|
+
fetchFn;
|
|
30586
|
+
maxCacheEntries;
|
|
30587
|
+
timeoutMs;
|
|
30588
|
+
/** key -> `ms://<id>`, in least-recently-used order. */
|
|
30589
|
+
cache = /* @__PURE__ */ new Map();
|
|
30590
|
+
flights = /* @__PURE__ */ new Map();
|
|
30591
|
+
constructor(options = {}) {
|
|
30592
|
+
this.fetchFn = options.fetchFn ?? fetch;
|
|
30593
|
+
const cap = options.maxCacheEntries;
|
|
30594
|
+
this.maxCacheEntries = cap !== void 0 && Number.isFinite(cap) && cap > 0 ? Math.floor(cap) : 256;
|
|
30595
|
+
const timeout = options.timeoutMs;
|
|
30596
|
+
this.timeoutMs = timeout !== void 0 && Number.isFinite(timeout) ? timeout : MEDIA_UPLOAD_TIMEOUT_MS;
|
|
30597
|
+
}
|
|
30598
|
+
/** Cached references currently held, for a settings surface or a test. */
|
|
30599
|
+
get size() {
|
|
30600
|
+
return this.cache.size;
|
|
30601
|
+
}
|
|
30602
|
+
/** Uploads one file and returns the `ms://<id>` reference a request cites. */
|
|
30603
|
+
async upload(request) {
|
|
30604
|
+
const resolved = this.resolve(request);
|
|
30605
|
+
if (resolved.signal?.aborted === true) throw abortError(resolved.signal);
|
|
30606
|
+
const cached = this.readCache(resolved.key);
|
|
30607
|
+
if (cached !== void 0) return cached;
|
|
30608
|
+
let flight = this.flights.get(resolved.key);
|
|
30609
|
+
if (flight === void 0) {
|
|
30610
|
+
flight = this.startFlight(resolved);
|
|
30611
|
+
this.flights.set(resolved.key, flight);
|
|
30612
|
+
}
|
|
30613
|
+
return this.join(flight, resolved);
|
|
30614
|
+
}
|
|
30615
|
+
/**
|
|
30616
|
+
* Drop the reference one upload would use.
|
|
30617
|
+
*
|
|
30618
|
+
* The caller that sees a stored id REJECTED on the wire calls this so the next
|
|
30619
|
+
* attempt uploads again instead of replaying the same dead reference.
|
|
30620
|
+
*/
|
|
30621
|
+
forget(request) {
|
|
30622
|
+
let key;
|
|
30623
|
+
try {
|
|
30624
|
+
key = mediaFileCacheKey({
|
|
30625
|
+
accountScope: request.accountScope,
|
|
30626
|
+
baseUrl: request.baseUrl,
|
|
30627
|
+
mediaType: request.mediaType,
|
|
30628
|
+
data: request.data
|
|
30629
|
+
});
|
|
30630
|
+
} catch {
|
|
30631
|
+
return false;
|
|
30632
|
+
}
|
|
30633
|
+
return this.cache.delete(key);
|
|
30634
|
+
}
|
|
30635
|
+
/** Forget everything; a sign-out or a test starting over. */
|
|
30636
|
+
clear() {
|
|
30637
|
+
this.cache.clear();
|
|
30638
|
+
}
|
|
30639
|
+
/** Validate and normalize one request, or throw before anything is sent. */
|
|
30640
|
+
resolve(request) {
|
|
30641
|
+
const accessToken = request.accessToken.trim();
|
|
30642
|
+
if (accessToken === "") throw new KimiMediaUploadError("invalid-request", "the media upload needs an access token");
|
|
30643
|
+
const accountScope = request.accountScope.trim();
|
|
30644
|
+
if (accountScope === "") throw new KimiMediaUploadError("invalid-request", "the media upload needs an account scope for its cache key");
|
|
30645
|
+
if (accountScope === request.accessToken) throw new KimiMediaUploadError("invalid-request", "the media upload cache scope must not be the access token");
|
|
30646
|
+
const mediaType = request.mediaType.trim();
|
|
30647
|
+
const kind = mediaKindFor(mediaType);
|
|
30648
|
+
if (kind === void 0) throw new KimiMediaUploadError("unsupported-media", "the media upload accepts image/* or video/* only, not \"" + mediaType + "\"");
|
|
30649
|
+
if (request.data.byteLength === 0) throw new KimiMediaUploadError("invalid-request", "the media upload was given no bytes");
|
|
30650
|
+
filesUploadUrl$1(request.baseUrl);
|
|
30651
|
+
return {
|
|
30652
|
+
accessToken,
|
|
30653
|
+
baseUrl: request.baseUrl,
|
|
30654
|
+
accountScope,
|
|
30655
|
+
data: request.data,
|
|
30656
|
+
mediaType,
|
|
30657
|
+
kind,
|
|
30658
|
+
filename: filenameFor(kind, mediaType, request.filename),
|
|
30659
|
+
...request.signal === void 0 ? {} : { signal: request.signal },
|
|
30660
|
+
fetchFn: this.fetchFn,
|
|
30661
|
+
timeoutMs: this.timeoutMs,
|
|
30662
|
+
key: mediaFileCacheKey({
|
|
30663
|
+
accountScope,
|
|
30664
|
+
baseUrl: request.baseUrl,
|
|
30665
|
+
mediaType,
|
|
30666
|
+
data: request.data
|
|
30667
|
+
})
|
|
30668
|
+
};
|
|
30669
|
+
}
|
|
30670
|
+
readCache(key) {
|
|
30671
|
+
const ref = this.cache.get(key);
|
|
30672
|
+
if (ref === void 0) return void 0;
|
|
30673
|
+
this.cache.delete(key);
|
|
30674
|
+
this.cache.set(key, ref);
|
|
30675
|
+
return ref;
|
|
30676
|
+
}
|
|
30677
|
+
writeCache(key, ref) {
|
|
30678
|
+
this.cache.delete(key);
|
|
30679
|
+
this.cache.set(key, ref);
|
|
30680
|
+
while (this.cache.size > this.maxCacheEntries) {
|
|
30681
|
+
const oldest = this.cache.keys().next();
|
|
30682
|
+
if (oldest.done === true) break;
|
|
30683
|
+
this.cache.delete(oldest.value);
|
|
30684
|
+
}
|
|
30685
|
+
}
|
|
30686
|
+
/** Open one flight and run it; the map entry is dropped as it settles. */
|
|
30687
|
+
startFlight(request) {
|
|
30688
|
+
const controller = new AbortController();
|
|
30689
|
+
const state = { timedOut: false };
|
|
30690
|
+
const promise = this.performUpload(controller, state, request).finally(() => {
|
|
30691
|
+
if (this.flights.get(request.key)?.promise === promise) this.flights.delete(request.key);
|
|
30692
|
+
});
|
|
30693
|
+
promise.catch(() => void 0);
|
|
30694
|
+
return {
|
|
30695
|
+
promise,
|
|
30696
|
+
controller,
|
|
30697
|
+
waiters: 0,
|
|
30698
|
+
state
|
|
30699
|
+
};
|
|
30700
|
+
}
|
|
30701
|
+
/**
|
|
30702
|
+
* Wait for one flight, respecting THIS caller's abort.
|
|
30703
|
+
*
|
|
30704
|
+
* The flight is not bound to the caller's signal: a caller that aborts
|
|
30705
|
+
* detaches, and the transport is cancelled only when the last waiter has gone.
|
|
30706
|
+
*/
|
|
30707
|
+
async join(flight, request) {
|
|
30708
|
+
const signal = request.signal;
|
|
30709
|
+
if (signal?.aborted === true) throw abortError(signal);
|
|
30710
|
+
flight.waiters += 1;
|
|
30711
|
+
let released = false;
|
|
30712
|
+
const release = () => {
|
|
30713
|
+
if (released) return;
|
|
30714
|
+
released = true;
|
|
30715
|
+
flight.waiters -= 1;
|
|
30716
|
+
if (flight.waiters <= 0) {
|
|
30717
|
+
if (this.flights.get(request.key) === flight) this.flights.delete(request.key);
|
|
30718
|
+
flight.controller.abort();
|
|
30719
|
+
}
|
|
30720
|
+
};
|
|
30721
|
+
if (signal === void 0) try {
|
|
30722
|
+
return await flight.promise;
|
|
30723
|
+
} finally {
|
|
30724
|
+
release();
|
|
30725
|
+
}
|
|
30726
|
+
const callerSignal = signal;
|
|
30727
|
+
let detach;
|
|
30728
|
+
const cancelled = new Promise((_resolve, reject) => {
|
|
30729
|
+
const onAbort = () => reject(abortError(callerSignal));
|
|
30730
|
+
detach = () => callerSignal.removeEventListener("abort", onAbort);
|
|
30731
|
+
callerSignal.addEventListener("abort", onAbort, { once: true });
|
|
30732
|
+
});
|
|
30733
|
+
try {
|
|
30734
|
+
return await Promise.race([flight.promise, cancelled]);
|
|
30735
|
+
} finally {
|
|
30736
|
+
detach?.();
|
|
30737
|
+
release();
|
|
30738
|
+
}
|
|
30739
|
+
}
|
|
30740
|
+
/** The one request a flight performs. */
|
|
30741
|
+
async performUpload(controller, state, request) {
|
|
30742
|
+
const form = new FormData();
|
|
30743
|
+
form.append("file", new Blob([request.data], { type: request.mediaType }), request.filename);
|
|
30744
|
+
form.append("purpose", request.kind);
|
|
30745
|
+
const headers = await kimiIdentityHeaders({
|
|
30746
|
+
authorization: "Bearer " + request.accessToken,
|
|
30747
|
+
accept: "application/json"
|
|
30748
|
+
});
|
|
30749
|
+
const timer = request.timeoutMs > 0 ? setTimeout(() => {
|
|
30750
|
+
state.timedOut = true;
|
|
30751
|
+
controller.abort();
|
|
30752
|
+
}, request.timeoutMs) : void 0;
|
|
30753
|
+
timer?.unref();
|
|
30754
|
+
try {
|
|
30755
|
+
let response;
|
|
30756
|
+
try {
|
|
30757
|
+
response = await request.fetchFn(filesUploadUrl$1(request.baseUrl), {
|
|
30758
|
+
method: "POST",
|
|
30759
|
+
headers,
|
|
30760
|
+
body: form,
|
|
30761
|
+
signal: controller.signal
|
|
30762
|
+
});
|
|
30763
|
+
} catch (error) {
|
|
30764
|
+
throw this.transportFailure(error, state, request);
|
|
30765
|
+
}
|
|
30766
|
+
let raw = "";
|
|
30767
|
+
try {
|
|
30768
|
+
raw = await response.text();
|
|
30769
|
+
} catch (error) {
|
|
30770
|
+
throw this.transportFailure(error, state, request);
|
|
30771
|
+
}
|
|
30772
|
+
return this.settle(response, raw, request);
|
|
30773
|
+
} finally {
|
|
30774
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
30775
|
+
}
|
|
30776
|
+
}
|
|
30777
|
+
/** Turn a transport rejection into the error the caller must see. */
|
|
30778
|
+
transportFailure(error, state, request) {
|
|
30779
|
+
if (looksAborted(error)) {
|
|
30780
|
+
if (state.timedOut) return new KimiMediaUploadError("timeout", "the media upload did not finish within " + request.timeoutMs + " ms");
|
|
30781
|
+
return abortError();
|
|
30782
|
+
}
|
|
30783
|
+
return new KimiMediaUploadError("network", "the media upload did not complete: " + redact(error instanceof Error ? error.message : String(error), request.accessToken));
|
|
30784
|
+
}
|
|
30785
|
+
/** Read one response into a reference, or into a typed failure. */
|
|
30786
|
+
settle(response, raw, request) {
|
|
30787
|
+
const status = response.status;
|
|
30788
|
+
if (response.ok !== true) {
|
|
30789
|
+
const detail = bodyDigest(raw, request.accessToken);
|
|
30790
|
+
if (status === 401 || status === 403) throw new KimiMediaUploadError("unauthorized", "the Kimi Code hosted-file endpoint rejected the credential (HTTP " + status + ")", {
|
|
30791
|
+
status,
|
|
30792
|
+
detail
|
|
30793
|
+
});
|
|
30794
|
+
if (status === 404 || status === 405) throw new KimiMediaUploadError("not-found", "the Kimi Code hosted-file endpoint is not available on this base URL (HTTP " + status + ")", {
|
|
30795
|
+
status,
|
|
30796
|
+
detail
|
|
30797
|
+
});
|
|
30798
|
+
if (status === 413) throw new KimiMediaUploadError("too-large", "the Kimi Code hosted-file endpoint refused the upload as too large (HTTP 413)", {
|
|
30799
|
+
status,
|
|
30800
|
+
detail
|
|
30801
|
+
});
|
|
30802
|
+
if (status >= 500) throw new KimiMediaUploadError("server", "the Kimi Code hosted-file endpoint failed (HTTP " + status + ")", {
|
|
30803
|
+
status,
|
|
30804
|
+
detail
|
|
30805
|
+
});
|
|
30806
|
+
throw new KimiMediaUploadError("rejected", "the Kimi Code hosted-file endpoint refused the upload (HTTP " + status + ")", {
|
|
30807
|
+
status,
|
|
30808
|
+
detail
|
|
30809
|
+
});
|
|
30810
|
+
}
|
|
30811
|
+
const parsed = parseJson(raw);
|
|
30812
|
+
if (!parsed.ok) throw new KimiMediaUploadError("not-json", "the media upload answered HTTP " + status + " without a JSON body", {
|
|
30813
|
+
status,
|
|
30814
|
+
detail: redact(raw, request.accessToken)
|
|
30815
|
+
});
|
|
30816
|
+
const id = readFileId$1(parsed.value);
|
|
30817
|
+
if (id === void 0) throw new KimiMediaUploadError("no-id", "the media upload answered HTTP " + status + " without a usable file id", {
|
|
30818
|
+
status,
|
|
30819
|
+
detail: redact(raw, request.accessToken)
|
|
30820
|
+
});
|
|
30821
|
+
const ref = fileReference(id);
|
|
30822
|
+
this.writeCache(request.key, ref);
|
|
30823
|
+
return ref;
|
|
30824
|
+
}
|
|
30825
|
+
};
|
|
30826
|
+
/** One uploader with the default transport and cache size. */
|
|
30827
|
+
function createMediaFilesUploader(options = {}) {
|
|
30828
|
+
return new KimiMediaFilesUploader(options);
|
|
30829
|
+
}
|
|
30830
|
+
createMediaFilesUploader();
|
|
29537
30831
|
//#endregion
|
|
29538
30832
|
//#region src/host/kimi-code/adapter.ts
|
|
29539
30833
|
/**
|
|
@@ -29699,6 +30993,24 @@ const ACCOUNT_LIMIT_PATTERNS = [
|
|
|
29699
30993
|
function matchesAny$1(text, patterns) {
|
|
29700
30994
|
return patterns.some((pattern) => pattern.test(text));
|
|
29701
30995
|
}
|
|
30996
|
+
/**
|
|
30997
|
+
* Cache scope for the single stored credential.
|
|
30998
|
+
*
|
|
30999
|
+
* The user id the sign-in reported is the only stable identity that file has, and
|
|
31000
|
+
* the constant covers a credential stored before any id was recorded — or one
|
|
31001
|
+
* whose sign-in never answered with one. It must stay constant for an account:
|
|
31002
|
+
* a scope that drifted would re-upload every clip on the next turn, while a scope
|
|
31003
|
+
* shared between two accounts would hand one account's file id to another.
|
|
31004
|
+
*/
|
|
31005
|
+
const LEGACY_ACCOUNT_SCOPE = "legacy";
|
|
31006
|
+
function legacyAccountScope(credentials) {
|
|
31007
|
+
const userId = credentials.userId?.trim();
|
|
31008
|
+
return userId === void 0 || userId === "" ? LEGACY_ACCOUNT_SCOPE : userId;
|
|
31009
|
+
}
|
|
31010
|
+
/** Whether one rejection is a cancellation rather than a failure to report. */
|
|
31011
|
+
function isAbortError(error) {
|
|
31012
|
+
return typeof error === "object" && error !== null && error.name === "AbortError";
|
|
31013
|
+
}
|
|
29702
31014
|
/** Short, single-line excerpt of one error body, safe to show a user. */
|
|
29703
31015
|
function summarizeFailureBody$1(raw) {
|
|
29704
31016
|
const text = raw.replace(/[\r\n\t]+/g, " ").trim();
|
|
@@ -29734,6 +31046,129 @@ function isKimiBodySizeRefusal(detail) {
|
|
|
29734
31046
|
return /\btotal message size\b[^;]{0,64}?\bexceed(?:s|ed)?\b[^;]{0,64}?\blimit\b/i.test(detail);
|
|
29735
31047
|
}
|
|
29736
31048
|
/**
|
|
31049
|
+
* The sentence the service answers with when a cited hosted file is GONE.
|
|
31050
|
+
*
|
|
31051
|
+
* Measured on this line: a chat body that cites `ms://<id>` for a file the
|
|
31052
|
+
* store no longer holds is refused with HTTP 404 and exactly this text. It is
|
|
31053
|
+
* checked as a case-insensitive substring rather than as an exact body so a JSON
|
|
31054
|
+
* wrapper (`{"error":{"message":"The requested resource was not found"}}`) and a
|
|
31055
|
+
* trailing hash match too.
|
|
31056
|
+
*
|
|
31057
|
+
* DELIBERATELY NARROW, because 404 also means "no such model" and "no such
|
|
31058
|
+
* route" on this endpoint, and neither of those is fixed by resending the same
|
|
31059
|
+
* conversation with inline media. Only this sentence, on a request that really
|
|
31060
|
+
* cited a reference, opens the one inline retry.
|
|
31061
|
+
*/
|
|
31062
|
+
const MISSING_RESOURCE_PATTERN = /\bthe requested resource was not found\b/i;
|
|
31063
|
+
/** Whether one 404 body is the service refusing a cited file rather than the model. */
|
|
31064
|
+
function isMissingResourceBody(bodyText) {
|
|
31065
|
+
return MISSING_RESOURCE_PATTERN.test(bodyText);
|
|
31066
|
+
}
|
|
31067
|
+
/**
|
|
31068
|
+
* Body length assumed for one reference while the request is measured.
|
|
31069
|
+
*
|
|
31070
|
+
* The body is measured BEFORE anything is uploaded, so the real id does not
|
|
31071
|
+
* exist yet. 68 characters is longer than an id the uploaded-file route is
|
|
31072
|
+
* documented to mint, and it is deliberately on the long side: a measurement
|
|
31073
|
+
* that over-estimates sends nothing it cannot fit, while one that
|
|
31074
|
+
* under-estimates would discover the overflow only after the upload was already
|
|
31075
|
+
* paid for.
|
|
31076
|
+
*/
|
|
31077
|
+
const PLANNED_MEDIA_REFERENCE = "ms://" + "0".repeat(64);
|
|
31078
|
+
/**
|
|
31079
|
+
* Which occurrences of one request would be uploaded, if any.
|
|
31080
|
+
*
|
|
31081
|
+
* Nothing is uploaded here and no network is touched: this only decides WHICH
|
|
31082
|
+
* readable occurrences the reference path would cover, so the body-fit guard can
|
|
31083
|
+
* measure the body that will actually be sent instead of measuring an inline
|
|
31084
|
+
* body the reference path would never produce.
|
|
31085
|
+
*
|
|
31086
|
+
* An occurrence is eligible when all four hold, and each exclusion is load
|
|
31087
|
+
* bearing:
|
|
31088
|
+
*
|
|
31089
|
+
* - the wire is OpenAI-compatible — the Anthropic surface documents no hosted
|
|
31090
|
+
* media and must upload nothing at all;
|
|
31091
|
+
* - the selected model declares the matching modality — sending a purpose the
|
|
31092
|
+
* model cannot read uploads bytes nobody will look at;
|
|
31093
|
+
* - the occurrence resolved to `inline`, i.e. its bytes were actually read —
|
|
31094
|
+
* an unreadable media has nothing to upload and keeps its placeholder;
|
|
31095
|
+
* - the occurrence cites a durable attachment, because only that is what a
|
|
31096
|
+
* reference can be keyed by.
|
|
31097
|
+
*/
|
|
31098
|
+
function planMediaReferences(input) {
|
|
31099
|
+
if (input.wire !== "openai") return [];
|
|
31100
|
+
const planned = [];
|
|
31101
|
+
const collect = (resolved, kind) => {
|
|
31102
|
+
for (const [attachmentId, occurrence] of resolved) {
|
|
31103
|
+
if (occurrence.kind !== "inline") continue;
|
|
31104
|
+
const data = Buffer.from(occurrence.data, "base64");
|
|
31105
|
+
if (data.byteLength === 0) continue;
|
|
31106
|
+
planned.push({
|
|
31107
|
+
attachmentId,
|
|
31108
|
+
kind,
|
|
31109
|
+
mediaType: occurrence.mediaType,
|
|
31110
|
+
data
|
|
31111
|
+
});
|
|
31112
|
+
}
|
|
31113
|
+
};
|
|
31114
|
+
if (input.imageAccepted) collect(input.images, "image");
|
|
31115
|
+
if (input.videoAccepted) collect(input.videos, "video");
|
|
31116
|
+
return planned;
|
|
31117
|
+
}
|
|
31118
|
+
/**
|
|
31119
|
+
* Attachment ids one built request STILL sends as media occurrences.
|
|
31120
|
+
*
|
|
31121
|
+
* Two stages can take an occurrence away, and both run before a reference could
|
|
31122
|
+
* be cited:
|
|
31123
|
+
*
|
|
31124
|
+
* - the image/video budget pass replaces the oldest occurrences with a text
|
|
31125
|
+
* placeholder, and
|
|
31126
|
+
* - the body-fit loop replaces more of them when the real serialized body is over
|
|
31127
|
+
* the ceiling.
|
|
31128
|
+
*
|
|
31129
|
+
* An occurrence either stage removed is no longer part of the request, so
|
|
31130
|
+
* uploading it would pay for bytes nobody reads and mint a reference nothing
|
|
31131
|
+
* cites. The upload set is therefore read off the FINAL build rather than off
|
|
31132
|
+
* what was merely readable when the request was planned.
|
|
31133
|
+
*
|
|
31134
|
+
* Tool results nest their own content, and a `read_image` result carries its
|
|
31135
|
+
* pixels there, so the traversal recurses exactly the way the budget does.
|
|
31136
|
+
*/
|
|
31137
|
+
function mediaAttachmentIdsIn(options) {
|
|
31138
|
+
const ids = /* @__PURE__ */ new Set();
|
|
31139
|
+
const visit = (content) => {
|
|
31140
|
+
if (!Array.isArray(content)) return;
|
|
31141
|
+
for (const block of content) {
|
|
31142
|
+
if (typeof block !== "object" || block === null) continue;
|
|
31143
|
+
const record = block;
|
|
31144
|
+
if (record.type === "tool-result") {
|
|
31145
|
+
visit(record.content);
|
|
31146
|
+
continue;
|
|
31147
|
+
}
|
|
31148
|
+
if (record.type !== "image" && record.type !== "video") continue;
|
|
31149
|
+
const id = (typeof record.attachment === "object" && record.attachment !== null ? record.attachment : void 0)?.attachmentId;
|
|
31150
|
+
if (typeof id === "string" && id !== "") ids.add(id);
|
|
31151
|
+
}
|
|
31152
|
+
};
|
|
31153
|
+
for (const message of options.messages) visit(message.content);
|
|
31154
|
+
return ids;
|
|
31155
|
+
}
|
|
31156
|
+
/**
|
|
31157
|
+
* The placeholder reference maps one planned set measures the body with.
|
|
31158
|
+
*
|
|
31159
|
+
* Returned separately for images and videos because the mapper keys them by
|
|
31160
|
+
* field, not by kind.
|
|
31161
|
+
*/
|
|
31162
|
+
function plannedReferenceMaps(planned) {
|
|
31163
|
+
const imageReferences = /* @__PURE__ */ new Map();
|
|
31164
|
+
const videoReferences = /* @__PURE__ */ new Map();
|
|
31165
|
+
for (const item of planned) (item.kind === "image" ? imageReferences : videoReferences).set(item.attachmentId, PLANNED_MEDIA_REFERENCE);
|
|
31166
|
+
return {
|
|
31167
|
+
imageReferences,
|
|
31168
|
+
videoReferences
|
|
31169
|
+
};
|
|
31170
|
+
}
|
|
31171
|
+
/**
|
|
29737
31172
|
* Classify one non-2xx response for the retry policy.
|
|
29738
31173
|
*
|
|
29739
31174
|
* @param status - HTTP status the service answered with.
|
|
@@ -29885,6 +31320,99 @@ const ACCOUNT_LIMIT_COOLDOWN_MS = 300 * 6e4;
|
|
|
29885
31320
|
const BILLING_CYCLE_COOLDOWN_MS = 720 * 60 * 6e4;
|
|
29886
31321
|
/** Ceiling, so a malformed reset time cannot park an account for years. */
|
|
29887
31322
|
const MAX_ACCOUNT_LIMIT_COOLDOWN_MS = BILLING_CYCLE_COOLDOWN_MS;
|
|
31323
|
+
/**
|
|
31324
|
+
* Longest edge of the request version this route asks the attachment store for.
|
|
31325
|
+
*
|
|
31326
|
+
* Kimi's own CLI downscales an oversized image to a 2000 px long edge by default,
|
|
31327
|
+
* so a screenshot its client would send at 2000 px must not be handed to this
|
|
31328
|
+
* route at the shared 1024 px ceiling: that kept a screenshot the model could
|
|
31329
|
+
* read at 2000 px legible-sized only by dropping and re-reading it. The
|
|
31330
|
+
* encoded-byte target is deliberately NOT raised with it — 256 KiB per request
|
|
31331
|
+
* version still fits four images inside the shared 1.5 MB budget — and an image
|
|
31332
|
+
* already inside this edge is still sent as stored, so small images, references
|
|
31333
|
+
* without dimensions and the body-fit ceiling all behave exactly as before.
|
|
31334
|
+
*/
|
|
31335
|
+
const KIMI_CODE_REQUEST_IMAGE_MAX_EDGE = 2e3;
|
|
31336
|
+
/**
|
|
31337
|
+
* Persist the cache facts a status card needs to survive a restart.
|
|
31338
|
+
*
|
|
31339
|
+
* Written at the end of every request, from the same numbers the in-memory
|
|
31340
|
+
* totals just recorded. The process forgets them at exit, and the expiry hint
|
|
31341
|
+
* depends on knowing how long a conversation sat idle before it was resumed —
|
|
31342
|
+
* the moment the hint matters is the moment memory no longer has the answer.
|
|
31343
|
+
*
|
|
31344
|
+
* The prompt size is this request's own (cache read + fresh input), not the
|
|
31345
|
+
* rolling sum: the hint describes what the NEXT turn would have to reprocess.
|
|
31346
|
+
*/
|
|
31347
|
+
/**
|
|
31348
|
+
* How this route carries a mid-conversation tool change, or undefined for none.
|
|
31349
|
+
*
|
|
31350
|
+
* Three gates decide whether the route gets one at all: the model accepts
|
|
31351
|
+
* message-level declarations, the request travels the OpenAI-compatible surface
|
|
31352
|
+
* (the Anthropic one documents no such field), and no operator opted out. A
|
|
31353
|
+
* model that fails any gate keeps the old shape, in which every request carries
|
|
31354
|
+
* the complete tool list.
|
|
31355
|
+
*
|
|
31356
|
+
* The mode is `in-history`, not `addition-only`: both append an addition, but
|
|
31357
|
+
* only `in-history` also expresses a removal by appending a note. Under
|
|
31358
|
+
* `addition-only` a revoked tool is deleted from the declaration list, the
|
|
31359
|
+
* top-level `tools` array shrinks, and the prefix cache is thrown away.
|
|
31360
|
+
* @param modelId - exact model id.
|
|
31361
|
+
* @param catalog - live catalog, possibly empty when offline.
|
|
31362
|
+
* @returns the tool-update mode, or undefined when this route must resend everything.
|
|
31363
|
+
*/
|
|
31364
|
+
function toolUpdateMode(modelId, catalog) {
|
|
31365
|
+
if (!dynamicToolsForEntry(modelId, catalog)) return void 0;
|
|
31366
|
+
if (wireForCatalogEntry(modelId, catalog) !== "openai") return void 0;
|
|
31367
|
+
const raw = (process.env.DSH_KIMI_CODE_TOOL_UPDATE ?? "").trim().toLowerCase();
|
|
31368
|
+
if (raw !== "" && [
|
|
31369
|
+
"0",
|
|
31370
|
+
"false",
|
|
31371
|
+
"no",
|
|
31372
|
+
"off",
|
|
31373
|
+
"none"
|
|
31374
|
+
].includes(raw)) return void 0;
|
|
31375
|
+
return "in-history";
|
|
31376
|
+
}
|
|
31377
|
+
/** Sessions already told that their prefix broke, so the log stays readable. */
|
|
31378
|
+
const driftWarned = /* @__PURE__ */ new Set();
|
|
31379
|
+
/**
|
|
31380
|
+
* Say once per session when the prefix cache was rebuilt by a change we made.
|
|
31381
|
+
*
|
|
31382
|
+
* The cost of a rebuild is invisible in the transcript — it shows up as a
|
|
31383
|
+
* slower turn and a larger bill — so the one place it can be seen is here.
|
|
31384
|
+
* Deliberately not a per-request notice: a repeated warning is a warning people
|
|
31385
|
+
* stop reading.
|
|
31386
|
+
*/
|
|
31387
|
+
function reportDriftCause(sessionId, cause) {
|
|
31388
|
+
if (cause !== "tools" && cause !== "system-prompt") return;
|
|
31389
|
+
const key = sessionId + ":" + cause;
|
|
31390
|
+
if (driftWarned.has(key)) return;
|
|
31391
|
+
driftWarned.add(key);
|
|
31392
|
+
if (driftWarned.size > 256) driftWarned.clear();
|
|
31393
|
+
console.warn("[kimi-code] prompt cache rebuilt for session " + sessionId + ": the " + cause + " changed mid-conversation, so the whole prompt was reprocessed once.");
|
|
31394
|
+
}
|
|
31395
|
+
function noteSessionCacheState(sessionId, state) {
|
|
31396
|
+
if (sessionId === void 0 || sessionId.trim() === "") return;
|
|
31397
|
+
const stats = getCacheStats(sessionId);
|
|
31398
|
+
if (stats.requests === 0) return;
|
|
31399
|
+
const lastDrift = getLastDriftCause(sessionId);
|
|
31400
|
+
reportDriftCause(sessionId, lastDrift);
|
|
31401
|
+
const now = Date.now();
|
|
31402
|
+
sessionCacheState.write(sessionId, {
|
|
31403
|
+
lastActiveAt: now,
|
|
31404
|
+
totalTokens: state.cacheReadTokens + state.inputTokens,
|
|
31405
|
+
requests: stats.requests,
|
|
31406
|
+
cachedTokens: stats.cachedTokens,
|
|
31407
|
+
freshTokens: stats.freshTokens,
|
|
31408
|
+
outputTokens: stats.outputTokens,
|
|
31409
|
+
cacheWriteTokens: stats.cacheWriteTokens,
|
|
31410
|
+
cacheWrite5mTokens: stats.cacheWrite5mTokens,
|
|
31411
|
+
cacheWrite1hTokens: stats.cacheWrite1hTokens,
|
|
31412
|
+
...lastDrift === void 0 ? {} : { lastDrift },
|
|
31413
|
+
updatedAt: now
|
|
31414
|
+
});
|
|
31415
|
+
}
|
|
29888
31416
|
var KimiCodeAdapter = class extends LlmAdapter {
|
|
29889
31417
|
store;
|
|
29890
31418
|
modelSettings;
|
|
@@ -29906,6 +31434,72 @@ var KimiCodeAdapter = class extends LlmAdapter {
|
|
|
29906
31434
|
this.options = options;
|
|
29907
31435
|
this.accountPool = accountPool ?? null;
|
|
29908
31436
|
}
|
|
31437
|
+
/**
|
|
31438
|
+
* The uploader this adapter cites files through, built on first use.
|
|
31439
|
+
*
|
|
31440
|
+
* Built lazily for two reasons: a turn with no media pays nothing, and a test
|
|
31441
|
+
* that stubs the global transport before the first request still reaches the
|
|
31442
|
+
* default uploader, because the transport is read at construction rather than
|
|
31443
|
+
* at module load.
|
|
31444
|
+
*
|
|
31445
|
+
* Deliberately NOT {@link kimiMediaFiles}, the module-level singleton: that one
|
|
31446
|
+
* captured the global `fetch` when this module was imported, so an adapter
|
|
31447
|
+
* given its own transport would upload through a different one than it chats
|
|
31448
|
+
* through — the one thing this route must not do, since the reference it gets
|
|
31449
|
+
* back is only meaningful to the endpoint the chat request goes to.
|
|
31450
|
+
*/
|
|
31451
|
+
mediaUploader;
|
|
31452
|
+
/** The shared uploader, created on first use with THIS adapter's transport. */
|
|
31453
|
+
mediaFiles() {
|
|
31454
|
+
if (this.mediaUploader === void 0) this.mediaUploader = this.options.mediaFiles ?? createMediaFilesUploader(this.options.fetchFn === void 0 ? {} : { fetchFn: this.options.fetchFn });
|
|
31455
|
+
return this.mediaUploader;
|
|
31456
|
+
}
|
|
31457
|
+
/**
|
|
31458
|
+
* Upload every planned occurrence under ONE account and collect its references.
|
|
31459
|
+
*
|
|
31460
|
+
* The credential, base URL and scope are the ones the chat request about to be
|
|
31461
|
+
* sent will use, which is what makes a reference citable at all: an id minted
|
|
31462
|
+
* for another account or another deployment is not assumed to be readable.
|
|
31463
|
+
*
|
|
31464
|
+
* A failure is contained per occurrence. Uploading is an optimisation — the
|
|
31465
|
+
* inline path always works — so a refused, overloaded or unreachable upload
|
|
31466
|
+
* endpoint leaves THAT media inline and the turn runs. What is never
|
|
31467
|
+
* contained is a cancellation: `signal` aborted, or an AbortError out of the
|
|
31468
|
+
* transport, is a turn the caller deliberately stopped, and continuing to send
|
|
31469
|
+
* the chat request would answer it anyway.
|
|
31470
|
+
*
|
|
31471
|
+
* This deliberately touches no account state: a 401/403/404/405/413/5xx or a
|
|
31472
|
+
* network failure of the UPLOAD is a fact about the hosted-file endpoint, not
|
|
31473
|
+
* a verdict on this account's quota, so it neither cools nor retires the
|
|
31474
|
+
* account. The chat request's own classification owns that decision.
|
|
31475
|
+
*/
|
|
31476
|
+
async uploadPlannedReferences(input) {
|
|
31477
|
+
const uploader = this.mediaFiles();
|
|
31478
|
+
const imageReferences = /* @__PURE__ */ new Map();
|
|
31479
|
+
const videoReferences = /* @__PURE__ */ new Map();
|
|
31480
|
+
const cited = [];
|
|
31481
|
+
await Promise.all(input.planned.map(async (item) => {
|
|
31482
|
+
try {
|
|
31483
|
+
const reference = await uploader.upload({
|
|
31484
|
+
accessToken: input.accessToken,
|
|
31485
|
+
baseUrl: input.baseUrl,
|
|
31486
|
+
accountScope: input.accountScope,
|
|
31487
|
+
data: item.data,
|
|
31488
|
+
mediaType: item.mediaType,
|
|
31489
|
+
signal: input.signal
|
|
31490
|
+
});
|
|
31491
|
+
(item.kind === "image" ? imageReferences : videoReferences).set(item.attachmentId, reference);
|
|
31492
|
+
cited.push(item);
|
|
31493
|
+
} catch (error) {
|
|
31494
|
+
if (input.signal.aborted || isAbortError(error)) throw new LlmError("Kimi Code media upload aborted", "ABORTED", { cause: error });
|
|
31495
|
+
}
|
|
31496
|
+
}));
|
|
31497
|
+
return {
|
|
31498
|
+
imageReferences,
|
|
31499
|
+
videoReferences,
|
|
31500
|
+
cited
|
|
31501
|
+
};
|
|
31502
|
+
}
|
|
29909
31503
|
providerInfo(provider) {
|
|
29910
31504
|
return {
|
|
29911
31505
|
id: provider,
|
|
@@ -29917,7 +31511,11 @@ var KimiCodeAdapter = class extends LlmAdapter {
|
|
|
29917
31511
|
}
|
|
29918
31512
|
imageRequestPricing() {}
|
|
29919
31513
|
imageRequestTarget(_provider, _model, image) {
|
|
29920
|
-
return
|
|
31514
|
+
return requestImageTarget(image, 2e3) ?? {
|
|
31515
|
+
width: image.width,
|
|
31516
|
+
height: image.height,
|
|
31517
|
+
maxBytes: image.bytes
|
|
31518
|
+
};
|
|
29921
31519
|
}
|
|
29922
31520
|
settings() {
|
|
29923
31521
|
return this.preferences ? Promise.resolve(this.preferences.status()) : this.modelSettings.read();
|
|
@@ -29997,6 +31595,7 @@ var KimiCodeAdapter = class extends LlmAdapter {
|
|
|
29997
31595
|
id: modelId,
|
|
29998
31596
|
name: entry?.name ?? modelId,
|
|
29999
31597
|
inputModalities: inputModalitiesForEntry(modelId, catalog),
|
|
31598
|
+
...toolUpdateMode(modelId, catalog) === void 0 ? {} : { toolUpdate: toolUpdateMode(modelId, catalog) },
|
|
30000
31599
|
context: { contextWindow: this.contextWindowFor(modelId, entry, settings.contextWindowOverrides) },
|
|
30001
31600
|
...efforts.length === 0 ? {} : { reasoning: {
|
|
30002
31601
|
efforts: efforts.map((effort) => ({
|
|
@@ -30028,7 +31627,7 @@ var KimiCodeAdapter = class extends LlmAdapter {
|
|
|
30028
31627
|
const wire = wireForCatalogEntry(options.model, catalog);
|
|
30029
31628
|
const entry = catalog.find((model) => model.id === options.model);
|
|
30030
31629
|
const normalized = normalizeGenerateOptions(options);
|
|
30031
|
-
const images = await resolveRequestImages$1(normalized, this.options.attachments, signal);
|
|
31630
|
+
const images = await resolveRequestImages$1(normalized, this.options.attachments, signal, KIMI_CODE_REQUEST_IMAGE_MAX_EDGE);
|
|
30032
31631
|
const imageTrimmed = offloadOldestRequestImages$1(normalized, maxRequestImageBytes$1(), images);
|
|
30033
31632
|
const gatedEffort = coerceThinkingEffort(options.model, mapReasoningEffort(options.reasoningEffort === void 0 || options.reasoningEffort === null ? void 0 : String(options.reasoningEffort)));
|
|
30034
31633
|
if (gatedEffort.coercedFrom !== null) recordThinkingCoercion({
|
|
@@ -30044,11 +31643,33 @@ var KimiCodeAdapter = class extends LlmAdapter {
|
|
|
30044
31643
|
};
|
|
30045
31644
|
const videos = await resolveRequestVideos(requestOptions, this.options.videos, signal);
|
|
30046
31645
|
const settings = await this.settings();
|
|
30047
|
-
const
|
|
31646
|
+
const modalities = inputModalitiesForEntry(options.model, catalog);
|
|
31647
|
+
/**
|
|
31648
|
+
* Everything every attempt shares: the media reads and the capability gates.
|
|
31649
|
+
*
|
|
31650
|
+
* Account-specific references are merged on top of this per attempt, so the
|
|
31651
|
+
* gate is applied once and a reference can never be cited for a modality the
|
|
31652
|
+
* model does not declare.
|
|
31653
|
+
*/
|
|
31654
|
+
const baseMedia = {
|
|
30048
31655
|
videos,
|
|
30049
|
-
videoAccepted:
|
|
31656
|
+
videoAccepted: modalities.includes("video"),
|
|
30050
31657
|
messageTools: dynamicToolsForEntry(options.model, catalog)
|
|
30051
31658
|
};
|
|
31659
|
+
/**
|
|
31660
|
+
* Which readable occurrences the reference path would cover.
|
|
31661
|
+
*
|
|
31662
|
+
* Decided here so the body-fit guard can measure the body that will actually
|
|
31663
|
+
* be sent; nothing is uploaded, and no network is touched, until that guard
|
|
31664
|
+
* has accepted the request (see the rotation loop).
|
|
31665
|
+
*/
|
|
31666
|
+
const planned = planMediaReferences({
|
|
31667
|
+
wire,
|
|
31668
|
+
imageAccepted: modalities.includes("image"),
|
|
31669
|
+
videoAccepted: baseMedia.videoAccepted === true,
|
|
31670
|
+
images,
|
|
31671
|
+
videos
|
|
31672
|
+
});
|
|
30052
31673
|
const contextWindow = this.contextWindowFor(options.model, entry, settings.contextWindowOverrides);
|
|
30053
31674
|
const requestedMax = options.maxTokens ?? maxOutputTokensFor$4(options.model, contextWindow);
|
|
30054
31675
|
const inputCap = maxInputTokensForEntry(options.model, catalog);
|
|
@@ -30059,29 +31680,57 @@ var KimiCodeAdapter = class extends LlmAdapter {
|
|
|
30059
31680
|
};
|
|
30060
31681
|
const carriesVideo = requestHasVideo(requestOptions);
|
|
30061
31682
|
const bodyLimit = carriesVideo ? MAX_VIDEO_MESSAGE_BODY_BYTES : maxRequestBodyBytesFor(kimiCodeModelDef(entry?.id ?? options.model), process.env);
|
|
30062
|
-
|
|
30063
|
-
|
|
30064
|
-
|
|
30065
|
-
|
|
30066
|
-
|
|
30067
|
-
|
|
30068
|
-
|
|
30069
|
-
|
|
30070
|
-
|
|
30071
|
-
|
|
30072
|
-
|
|
30073
|
-
|
|
30074
|
-
|
|
30075
|
-
|
|
30076
|
-
|
|
30077
|
-
|
|
30078
|
-
|
|
30079
|
-
|
|
30080
|
-
|
|
30081
|
-
|
|
31683
|
+
/**
|
|
31684
|
+
* Run the greedy body-fit loop for ONE set of media references.
|
|
31685
|
+
*
|
|
31686
|
+
* A function rather than a loop in place because the same loop now serves
|
|
31687
|
+
* every build of one request: the pre-upload MEASUREMENT (placeholder
|
|
31688
|
+
* references), the real reference build after the upload, and the all-inline
|
|
31689
|
+
* rebuild the missing-resource fallback re-sends.
|
|
31690
|
+
*
|
|
31691
|
+
* Every call restarts from `boundedOptions`. That is load bearing rather than
|
|
31692
|
+
* tidy: the inline fallback has to re-fit the inline body from scratch and
|
|
31693
|
+
* stay inside the same ceiling instead of inheriting an omission the
|
|
31694
|
+
* reference path made to fit a different, much smaller body.
|
|
31695
|
+
*/
|
|
31696
|
+
const buildBodyWithinLimit = (media) => {
|
|
31697
|
+
let buildOptions = boundedOptions;
|
|
31698
|
+
for (let attempt = 0;; attempt++) {
|
|
31699
|
+
const built = buildRequest(buildOptions, wire, images, void 0, media);
|
|
31700
|
+
const body = JSON.stringify(built);
|
|
31701
|
+
const bytes = Buffer.byteLength(body, "utf8");
|
|
31702
|
+
if (bytes <= bodyLimit) return {
|
|
31703
|
+
body,
|
|
31704
|
+
options: buildOptions
|
|
31705
|
+
};
|
|
31706
|
+
const context = () => ({
|
|
31707
|
+
droppedImages: countOmitted(buildOptions, requestOptions, images),
|
|
31708
|
+
sendableImagesRemaining: requestImagePayloadLengths(buildOptions, images).length,
|
|
31709
|
+
excessBytes: bytes - bodyLimit
|
|
31710
|
+
});
|
|
31711
|
+
if (attempt > MAX_BODY_FIT_ATTEMPTS || !requestHasSendableImage(buildOptions, images)) {
|
|
31712
|
+
assertRequestBodyFits$1(built, carriesVideo, context(), bodyLimit);
|
|
31713
|
+
throw new Error("unreachable: assertRequestBodyFits returns or throws");
|
|
31714
|
+
}
|
|
31715
|
+
const drop = dropOldestImagesUntilFits(buildOptions, images, bytes - bodyLimit, requestOptions);
|
|
31716
|
+
if (drop.dropped === 0) {
|
|
31717
|
+
assertRequestBodyFits$1(built, carriesVideo, context(), bodyLimit);
|
|
31718
|
+
throw new Error("unreachable: assertRequestBodyFits returns or throws");
|
|
31719
|
+
}
|
|
31720
|
+
buildOptions = drop.options;
|
|
30082
31721
|
}
|
|
30083
|
-
|
|
30084
|
-
|
|
31722
|
+
};
|
|
31723
|
+
/**
|
|
31724
|
+
* The measured build for the reference path, done at most once.
|
|
31725
|
+
*
|
|
31726
|
+
* Held across rotation attempts because it is account-independent: it decides
|
|
31727
|
+
* whether this request can be sent at all, and that answer cannot change
|
|
31728
|
+
* because a different account answered.
|
|
31729
|
+
*
|
|
31730
|
+
* The OPTIONS travel with the body because they, not the plan, name the media
|
|
31731
|
+
* this request still carries: see {@link mediaAttachmentIdsIn}.
|
|
31732
|
+
*/
|
|
31733
|
+
let measured;
|
|
30085
31734
|
const pool = this.accountPool;
|
|
30086
31735
|
const tried = /* @__PURE__ */ new Set();
|
|
30087
31736
|
let servedByAccountId;
|
|
@@ -30115,19 +31764,69 @@ var KimiCodeAdapter = class extends LlmAdapter {
|
|
|
30115
31764
|
const base = (apiCredentials.baseUrl ?? codingBaseUrl(region)).replace(/\/+$/, "");
|
|
30116
31765
|
const endpoint = wire === "anthropic" ? `${base}/v1/messages?beta=true` : `${base}/v1/chat/completions`;
|
|
30117
31766
|
const headers = await modelRequestHeaders$1(apiCredentials.accessToken, wire);
|
|
30118
|
-
|
|
30119
|
-
|
|
30120
|
-
|
|
30121
|
-
|
|
30122
|
-
|
|
30123
|
-
|
|
31767
|
+
/**
|
|
31768
|
+
* Which account's file store this attempt writes to and cites from.
|
|
31769
|
+
*
|
|
31770
|
+
* A pooled account's own id, because that is the only identity a reference
|
|
31771
|
+
* may be cached under; a single stored credential uses the user id its
|
|
31772
|
+
* sign-in reported, or a constant when it reported none. The access token is
|
|
31773
|
+
* never used, here or anywhere a cache key is built.
|
|
31774
|
+
*/
|
|
31775
|
+
const accountScope = accountId ?? legacyAccountScope(apiCredentials);
|
|
31776
|
+
if (measured === void 0) measured = buildBodyWithinLimit(planned.length === 0 ? baseMedia : {
|
|
31777
|
+
...baseMedia,
|
|
31778
|
+
...plannedReferenceMaps(planned)
|
|
31779
|
+
});
|
|
31780
|
+
const stillSent = mediaAttachmentIdsIn(measured.options);
|
|
31781
|
+
const uploadable = planned.filter((item) => stillSent.has(item.attachmentId));
|
|
31782
|
+
const uploads = uploadable.length === 0 ? void 0 : await this.uploadPlannedReferences({
|
|
31783
|
+
planned: uploadable,
|
|
31784
|
+
accessToken: apiCredentials.accessToken,
|
|
31785
|
+
baseUrl: base,
|
|
31786
|
+
accountScope,
|
|
31787
|
+
signal
|
|
31788
|
+
});
|
|
31789
|
+
let body = uploads === void 0 ? measured.body : buildBodyWithinLimit({
|
|
31790
|
+
...baseMedia,
|
|
31791
|
+
imageReferences: uploads.imageReferences,
|
|
31792
|
+
videoReferences: uploads.videoReferences
|
|
31793
|
+
}).body;
|
|
31794
|
+
const post = async (payload) => {
|
|
31795
|
+
try {
|
|
31796
|
+
return await fetchWithContextBudgetRecovery(fetchFn)(endpoint, {
|
|
31797
|
+
method: "POST",
|
|
31798
|
+
headers,
|
|
31799
|
+
body: payload,
|
|
31800
|
+
signal
|
|
31801
|
+
});
|
|
31802
|
+
} catch (error) {
|
|
31803
|
+
if (signal.aborted) throw new LlmError("Kimi Code request aborted", "ABORTED", { cause: error });
|
|
31804
|
+
throw new LlmError(`Kimi Code request failed: ${error instanceof Error ? error.message : String(error)}`, "TRANSPORT", { cause: error });
|
|
31805
|
+
}
|
|
31806
|
+
};
|
|
31807
|
+
response = await post(body);
|
|
31808
|
+
let detail = response.ok ? "" : (await response.text().catch(() => "")).slice(0, 2e3);
|
|
31809
|
+
if (response.status === 404 && uploads !== void 0 && uploads.cited.length > 0 && isMissingResourceBody(detail)) {
|
|
31810
|
+
const uploader = this.mediaFiles();
|
|
31811
|
+
for (const item of uploads.cited) uploader.forget({
|
|
31812
|
+
baseUrl: base,
|
|
31813
|
+
accountScope,
|
|
31814
|
+
mediaType: item.mediaType,
|
|
31815
|
+
data: item.data
|
|
30124
31816
|
});
|
|
30125
|
-
|
|
30126
|
-
|
|
30127
|
-
|
|
31817
|
+
let inline;
|
|
31818
|
+
try {
|
|
31819
|
+
inline = buildBodyWithinLimit(baseMedia).body;
|
|
31820
|
+
} catch {
|
|
31821
|
+
inline = void 0;
|
|
31822
|
+
}
|
|
31823
|
+
if (inline !== void 0) {
|
|
31824
|
+
body = inline;
|
|
31825
|
+
response = await post(body);
|
|
31826
|
+
detail = response.ok ? "" : (await response.text().catch(() => "")).slice(0, 2e3);
|
|
31827
|
+
}
|
|
30128
31828
|
}
|
|
30129
31829
|
if (response.ok) break;
|
|
30130
|
-
const detail = (await response.text().catch(() => "")).slice(0, 2e3);
|
|
30131
31830
|
const failure = classifyKimiFailure(response.status, detail);
|
|
30132
31831
|
const after = response.status === 429 ? retryAfterMs$1(response.headers) : void 0;
|
|
30133
31832
|
if (pool !== null && accountId !== void 0) {
|
|
@@ -30200,6 +31899,7 @@ var KimiCodeAdapter = class extends LlmAdapter {
|
|
|
30200
31899
|
throw streamFailure(error, signal);
|
|
30201
31900
|
}
|
|
30202
31901
|
} finally {
|
|
31902
|
+
if (state.sawUsage) noteSessionCacheState(requestOptions.sessionId ?? state.sessionId, state);
|
|
30203
31903
|
reader.cancel().catch(() => void 0);
|
|
30204
31904
|
}
|
|
30205
31905
|
}
|
|
@@ -30613,7 +32313,7 @@ async function getKimiCodeWebStatus(store, modelSettings, preferences, options =
|
|
|
30613
32313
|
*/
|
|
30614
32314
|
function cacheStatsOrNull(sessionId) {
|
|
30615
32315
|
const stats = getCacheStats(sessionId);
|
|
30616
|
-
if (stats.requests === 0) return
|
|
32316
|
+
if (stats.requests === 0) return durableCacheStats(sessionId);
|
|
30617
32317
|
const lastDrift = getLastDriftCause(sessionId);
|
|
30618
32318
|
return {
|
|
30619
32319
|
...stats,
|
|
@@ -30632,11 +32332,15 @@ function cacheStatsOrNull(sessionId) {
|
|
|
30632
32332
|
* some global figure.
|
|
30633
32333
|
*/
|
|
30634
32334
|
function cacheHintOrNull(sessionId) {
|
|
30635
|
-
const
|
|
32335
|
+
const stats = getCacheStats(sessionId);
|
|
32336
|
+
const view = resolveSessionCacheView(sessionId, {
|
|
32337
|
+
memoryLastActiveAt: sessionLastActiveAt(sessionId),
|
|
32338
|
+
memoryTotalTokens: stats.cachedTokens + stats.freshTokens
|
|
32339
|
+
}, sessionCacheState.read(sessionId));
|
|
30636
32340
|
const decision = evaluateCacheHint({
|
|
30637
32341
|
now: Date.now(),
|
|
30638
|
-
lastActiveAt,
|
|
30639
|
-
totalTokens:
|
|
32342
|
+
lastActiveAt: view.lastActiveAt,
|
|
32343
|
+
totalTokens: view.totalTokens
|
|
30640
32344
|
});
|
|
30641
32345
|
if (decision.kind !== "hint") return null;
|
|
30642
32346
|
return {
|
|
@@ -30644,6 +32348,52 @@ function cacheHintOrNull(sessionId) {
|
|
|
30644
32348
|
totalTokens: decision.totalTokens
|
|
30645
32349
|
};
|
|
30646
32350
|
}
|
|
32351
|
+
/**
|
|
32352
|
+
* The card built from the durable record, for a process that has served nothing.
|
|
32353
|
+
*
|
|
32354
|
+
* Only reached when the in-memory totals are empty, which after a restart is the
|
|
32355
|
+
* normal case rather than an error: the previous run wrote this record at every
|
|
32356
|
+
* turn close, so it still describes the conversation the card was showing.
|
|
32357
|
+
*/
|
|
32358
|
+
function durableCacheStats(sessionId) {
|
|
32359
|
+
const durable = sessionCacheState.read(sessionId);
|
|
32360
|
+
if (durable === void 0 || durable.requests === 0) return null;
|
|
32361
|
+
const lastDrift = driftCauseOf(durable.lastDrift);
|
|
32362
|
+
return {
|
|
32363
|
+
requests: durable.requests,
|
|
32364
|
+
cachedTokens: durable.cachedTokens,
|
|
32365
|
+
freshTokens: durable.freshTokens,
|
|
32366
|
+
outputTokens: durable.outputTokens,
|
|
32367
|
+
hitRatio: hitRatioOf(durable.cachedTokens, durable.freshTokens),
|
|
32368
|
+
cacheWriteTokens: durable.cacheWriteTokens,
|
|
32369
|
+
cacheWrite5mTokens: durable.cacheWrite5mTokens,
|
|
32370
|
+
cacheWrite1hTokens: durable.cacheWrite1hTokens,
|
|
32371
|
+
...lastDrift === void 0 ? {} : { lastDrift }
|
|
32372
|
+
};
|
|
32373
|
+
}
|
|
32374
|
+
/**
|
|
32375
|
+
* The drift cause a durable record names, when it is one the DTO can carry.
|
|
32376
|
+
*
|
|
32377
|
+
* The record stores the cause as a string because it outlives the module that
|
|
32378
|
+
* produced it; an unrecognized value is dropped rather than forwarded, so a
|
|
32379
|
+
* downgraded plugin cannot render a cause this build has no label for.
|
|
32380
|
+
*/
|
|
32381
|
+
function driftCauseOf(value) {
|
|
32382
|
+
switch (value) {
|
|
32383
|
+
case "first-request":
|
|
32384
|
+
case "stable":
|
|
32385
|
+
case "system-prompt":
|
|
32386
|
+
case "tools":
|
|
32387
|
+
case "cache-key":
|
|
32388
|
+
case "cold-key": return value;
|
|
32389
|
+
default: return;
|
|
32390
|
+
}
|
|
32391
|
+
}
|
|
32392
|
+
/** Cached / (cached + fresh), or null before any prompt token was counted. */
|
|
32393
|
+
function hitRatioOf(cached, fresh) {
|
|
32394
|
+
const total = cached + fresh;
|
|
32395
|
+
return total > 0 ? cached / total : null;
|
|
32396
|
+
}
|
|
30647
32397
|
/** Register the Kimi Code settings routes under `/kimi-code/api`. */
|
|
30648
32398
|
function registerKimiCodeRoutes(ctx, store, modelSettings, preferences, options = {}, accountPool = options.accountPool) {
|
|
30649
32399
|
const fetchFn = options.fetchFn ?? fetch;
|
|
@@ -62232,22 +63982,27 @@ var ClinePassAdapter = class extends LlmAdapter {
|
|
|
62232
63982
|
const pinned = injectPrefs(body, meta, attempt);
|
|
62233
63983
|
let response;
|
|
62234
63984
|
let transportError;
|
|
62235
|
-
for (let transportTry = 0; transportTry <= TRANSPORT_RETRIES$1; transportTry += 1)
|
|
62236
|
-
|
|
62237
|
-
|
|
62238
|
-
|
|
62239
|
-
|
|
62240
|
-
|
|
62241
|
-
|
|
62242
|
-
|
|
62243
|
-
|
|
62244
|
-
|
|
62245
|
-
|
|
62246
|
-
|
|
63985
|
+
for (let transportTry = 0; transportTry <= TRANSPORT_RETRIES$1; transportTry += 1) {
|
|
63986
|
+
const headerDeadline = withAttemptTimeout$1(upstream, this.options.headerTimeoutMs);
|
|
63987
|
+
try {
|
|
63988
|
+
response = await this.engine.openStream(account, pinned, headerDeadline.signal);
|
|
63989
|
+
headerDeadline.clear();
|
|
63990
|
+
break;
|
|
63991
|
+
} catch (error) {
|
|
63992
|
+
headerDeadline.clear();
|
|
63993
|
+
if (signal.aborted) throw new LlmError("Cline Pass request aborted by caller", "ABORTED", { cause: error });
|
|
63994
|
+
transportError = error;
|
|
63995
|
+
if (transportTry < TRANSPORT_RETRIES$1 && isPreConnectionFailure$1(error)) {
|
|
63996
|
+
try {
|
|
63997
|
+
await setTimeout$1(TRANSPORT_RETRY_BASE_MS$1 * (transportTry + 1), void 0, { signal: upstream });
|
|
63998
|
+
} catch (waitError) {
|
|
63999
|
+
if (signal.aborted) throw new LlmError("Cline Pass request aborted by caller", "ABORTED", { cause: waitError });
|
|
64000
|
+
throw waitError;
|
|
64001
|
+
}
|
|
64002
|
+
continue;
|
|
62247
64003
|
}
|
|
62248
|
-
|
|
64004
|
+
break;
|
|
62249
64005
|
}
|
|
62250
|
-
break;
|
|
62251
64006
|
}
|
|
62252
64007
|
if (response === void 0) {
|
|
62253
64008
|
const note = errorChain$2(transportError);
|
|
@@ -62426,9 +64181,16 @@ var ClinePassAdapter = class extends LlmAdapter {
|
|
|
62426
64181
|
await this.accountPool.markCooldown(accountId, POOL_COOLDOWN_MS, `${PROVIDER_NAME$4} quota exhausted`).catch(() => void 0);
|
|
62427
64182
|
}
|
|
62428
64183
|
};
|
|
62429
|
-
/** A caller signal combined with
|
|
62430
|
-
function withAttemptTimeout$1(signal) {
|
|
62431
|
-
|
|
64184
|
+
/** A caller signal combined with a clearable response-header deadline. */
|
|
64185
|
+
function withAttemptTimeout$1(signal, timeoutMs = ATTEMPT_TIMEOUT_MS$1) {
|
|
64186
|
+
const deadline = new AbortController();
|
|
64187
|
+
const timer = setTimeout(() => {
|
|
64188
|
+
deadline.abort(new DOMException("The operation was aborted due to timeout", "TimeoutError"));
|
|
64189
|
+
}, timeoutMs);
|
|
64190
|
+
return {
|
|
64191
|
+
signal: AbortSignal.any([signal, deadline.signal]),
|
|
64192
|
+
clear: () => clearTimeout(timer)
|
|
64193
|
+
};
|
|
62432
64194
|
}
|
|
62433
64195
|
/**
|
|
62434
64196
|
* Whether a transport failure happened before any request was sent.
|
|
@@ -78892,12 +80654,15 @@ function progressLanguageDirective(language) {
|
|
|
78892
80654
|
}
|
|
78893
80655
|
/**
|
|
78894
80656
|
* 生成提示词分节的文本提供者。
|
|
78895
|
-
*
|
|
80657
|
+
*
|
|
80658
|
+
* 文本与开关无关,这是刻意的:系统提示是缓存前缀的第一段,会话中途改一个字
|
|
80659
|
+
* 就会让整段前缀失效、整段重新计费(Kimi 的缓存是内容前缀匹配)。开关的语义
|
|
80660
|
+
* 因此只留给工具自己——关闭时 `execute` 直接返回 `displayed:false`。
|
|
78896
80661
|
* @param language - 读取当前输出语言的函数;缺省按 `auto` 处理。
|
|
78897
|
-
* @returns
|
|
80662
|
+
* @returns 引导词加语言指令。
|
|
78898
80663
|
*/
|
|
78899
|
-
function progressSectionText(
|
|
78900
|
-
return () =>
|
|
80664
|
+
function progressSectionText(language = () => "auto") {
|
|
80665
|
+
return () => PROGRESS_GUIDANCE + "\n\n" + progressLanguageDirective(language());
|
|
78901
80666
|
}
|
|
78902
80667
|
//#endregion
|
|
78903
80668
|
//#region src/host/progress-updates/projection.ts
|
|
@@ -79486,7 +81251,7 @@ function installProgressUpdates(ctx, options) {
|
|
|
79486
81251
|
return systemPrompt.section({
|
|
79487
81252
|
name: PROGRESS_SECTION_NAME,
|
|
79488
81253
|
order: 220,
|
|
79489
|
-
text: progressSectionText(() => options.settings().
|
|
81254
|
+
text: progressSectionText(() => options.settings().language)
|
|
79490
81255
|
});
|
|
79491
81256
|
}, "dsh-chatgpt-subscription: progress update prompt section");
|
|
79492
81257
|
track(() => {
|
|
@@ -80142,14 +81907,15 @@ function apply(ctx, pluginConfig = {}) {
|
|
|
80142
81907
|
isEnabled: () => antigravityPreferences.status().enabled !== false
|
|
80143
81908
|
}));
|
|
80144
81909
|
const disposeVideoTool = ctx.tools.register(createKimiVideoTool(ctx, { fetchFn: proxyFetch }));
|
|
80145
|
-
const
|
|
81910
|
+
const minimaxMediaToolsEnabled = minimaxCodePreferences.status().enabled;
|
|
81911
|
+
const disposeMinimaxUnderstandVideoTool = minimaxMediaToolsEnabled ? ctx.tools.register(createMinimaxUnderstandVideoTool(ctx, {
|
|
80146
81912
|
fetchFn: proxyFetch,
|
|
80147
81913
|
pool: minimaxCodeAccountPool
|
|
80148
|
-
}));
|
|
80149
|
-
const disposeMinimaxGenerateVideoTool = ctx.tools.register(createMinimaxGenerateVideoTool(ctx, {
|
|
81914
|
+
})) : () => void 0;
|
|
81915
|
+
const disposeMinimaxGenerateVideoTool = minimaxMediaToolsEnabled ? ctx.tools.register(createMinimaxGenerateVideoTool(ctx, {
|
|
80150
81916
|
fetchFn: proxyFetch,
|
|
80151
81917
|
pool: minimaxCodeAccountPool
|
|
80152
|
-
}));
|
|
81918
|
+
})) : () => void 0;
|
|
80153
81919
|
const disposeProgressUpdates = installProgressUpdates(ctx, { settings: () => resolveProgressUpdatesSettings(pluginConfig) });
|
|
80154
81920
|
ctx.inject(["web"], (ctx) => {
|
|
80155
81921
|
ctx.web.registerSearchProvider(createCodexSearchProvider(oauth, { fetchFn: proxyFetch }));
|