@eddyskywalker/dsh-chatgpt-subscription 0.40.1 → 0.40.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.js CHANGED
@@ -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.40.1";
144
+ const PLUGIN_VERSION = "0.40.2";
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$49(payload) ? payload : {};
3438
+ const record = isRecord$50(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$49(payload) ? payload : {};
3505
+ const record = isRecord$50(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$49(payload)) return false;
3852
+ if (!isRecord$50(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$49(error)) {
3856
+ if (isRecord$50(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$49(payload)) return void 0;
3870
+ if (!isRecord$50(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$49(error)) candidates.push(error.code, error.type);
3874
+ else if (isRecord$50(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$49(value) {
3881
+ function isRecord$50(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$48(value) {
4064
+ function isRecord$49(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$48(value) ? value : void 0;
4166
+ return isRecord$49(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$48(value)) return void 0;
4211
+ if (!isRecord$49(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$48(tokens)) return void 0;
4214
+ if (!isRecord$49(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$48(authClaims) ? nonEmptyString$9(authClaims.chatgpt_plan_type) : void 0;
4233
+ const planType = isRecord$49(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$47(value) {
5002
+ function isRecord$48(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$47(block) && typeof block.type === "string");
5008
+ return raw.filter((block) => isRecord$48(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$47(raw)) continue;
5023
+ if (!isRecord$48(raw)) continue;
5024
5024
  const content = readBlocks(raw.content);
5025
5025
  if (raw.role === "tool") {
5026
- const source = isRecord$47(raw.source) ? raw.source : void 0;
5026
+ const source = isRecord$48(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$47(raw.source) ? { source: raw.source } : {}
5048
+ ...isRecord$48(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$46(value) {
6752
+ function isRecord$47(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$46(left.properties) && isRecord$46(right.properties)) merged.properties = {
6760
+ if (isRecord$47(left.properties) && isRecord$47(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$46(branch.properties) ? "object" : isRecord$46(branch.items) ? "array" : "value";
6796
+ const type = typeof branch.type === "string" ? branch.type : isRecord$47(branch.properties) ? "object" : isRecord$47(branch.items) ? "array" : "value";
6797
6797
  const details = [];
6798
- if (isRecord$46(branch.properties)) {
6798
+ if (isRecord$47(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$46(branch.properties)) return 3;
6812
+ if (isRecord$47(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$46(branch.properties) && !isRecord$46(branch.items))) {
6841
+ if (new Set(branches.map((branch) => branch.type)).size === 1 && branches.every((branch) => !isRecord$47(branch.properties) && !isRecord$47(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$46(value)) {
6878
+ if (key === "properties" && isRecord$47(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$46(node)) return node;
6907
+ if (!isRecord$47(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$46(folded)) own = mergeSchemas(own, folded);
6911
+ if (isRecord$47(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$46);
6915
+ const branches = alternatives.map((branch) => foldSchemaUnions(branch)).filter(isRecord$47);
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$46(schema)) return schema;
6931
+ if (!isRecord$47(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$46(target) && Object.hasOwn(target, key) ? target[key] : void 0;
6939
+ target = isRecord$47(target) && Object.hasOwn(target, key) ? target[key] : void 0;
6940
6940
  }
6941
- if (!isRecord$46(target)) throw new LlmError(`Antigravity tool schema reference was not found: ${reference}`, "PROVIDER_ERROR");
6941
+ if (!isRecord$47(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$46(node)) return {};
6945
+ if (!isRecord$47(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$46(node.properties)) out.properties = Object.fromEntries(Object.entries(node.properties).map(([name, child]) => [name, convert(child, references)]));
6955
- if (isRecord$46(node.items)) out.items = convert(node.items, references);
6954
+ if (isRecord$47(node.properties)) out.properties = Object.fromEntries(Object.entries(node.properties).map(([name, child]) => [name, convert(child, references)]));
6955
+ if (isRecord$47(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$45(value) {
6991
+ function isRecord$46(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$45(raw)) return raw;
7011
+ if (isRecord$46(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$45(parsed) ? parsed : {};
7014
+ return isRecord$46(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$45(attachment)) return void 0;
7019
+ if (!isRecord$46(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$45(block.attachment) ? block.attachment : void 0;
7023
+ const attachment = isRecord$46(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$45(block)) continue;
7029
+ if (!isRecord$46(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$45(block)) continue;
7142
+ if (!isRecord$46(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$45(block)) return block;
7192
+ if (remaining.count === 0 || !isRecord$46(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$45(block.source) ? block.source : void 0;
7295
+ const source = isRecord$46(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$45(block) && block.type === "text" && typeof block.text === "string") parts.push({ text: sanitizeText$8(block.text) });
7321
- else if (isRecord$45(block) && block.type === "image") {
7320
+ for (const block of content) if (isRecord$46(block) && block.type === "text" && typeof block.text === "string") parts.push({ text: sanitizeText$8(block.text) });
7321
+ else if (isRecord$46(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$45(block)) continue;
7336
+ if (!isRecord$46(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$45(block)) return "";
7348
+ if (!isRecord$46(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$45(state)) return void 0;
7362
+ if (!isRecord$46(state)) return void 0;
7363
7363
  if (Array.isArray(state.blocks)) return state.blocks[index];
7364
- const resp = isRecord$45(state.response) ? state.response : void 0;
7364
+ const resp = isRecord$46(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$45(block)) continue;
7466
+ if (!isRecord$46(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$45) : [];
7469
+ const originalParts = Array.isArray(replay?.parts) ? replay.parts.filter(isRecord$46) : [];
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$45(part.functionCall));
7491
- const wireId = asString$24((isRecord$45(originalCall?.functionCall) ? originalCall.functionCall : void 0)?.id) || (toolCallIdNeeded(model.id, runtimeModel) ? sanitizeToolCallId(toolId, toolName) : originalCall ? void 0 : toolId || void 0);
7490
+ const originalCall = originalParts.find((part) => isRecord$46(part.functionCall));
7491
+ const wireId = asString$24((isRecord$46(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$45(b) || b.type !== "tool-result"), images);
7543
+ const userParts = contentToUserParts(content.filter((b) => !isRecord$46(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$45(b) && b.type === "tool-result") pushToolResult(contents, b, toolCalls, model, runtimeModel, images);
7555
+ for (const b of content) if (isRecord$46(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$45(value)) return;
7703
+ if (!isRecord$46(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$45(chunk)) return [];
7734
- const responseData = isRecord$45(chunk.response) ? chunk.response : chunk;
7735
- const error = isRecord$45(chunk.error) ? chunk.error : isRecord$45(responseData.error) ? responseData.error : void 0;
7733
+ if (!isRecord$46(chunk)) return [];
7734
+ const responseData = isRecord$46(chunk.response) ? chunk.response : chunk;
7735
+ const error = isRecord$46(chunk.error) ? chunk.error : isRecord$46(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$45(candidates[0]) ? candidates[0] : void 0;
7739
- const content = isRecord$45(candidate?.content) ? candidate.content : void 0;
7738
+ const candidate = isRecord$46(candidates[0]) ? candidates[0] : void 0;
7739
+ const content = isRecord$46(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$45(part)) continue;
7743
+ if (!isRecord$46(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$45(part.functionCall) && thoughtSignature(part)) {
7775
+ } else if (!isRecord$46(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$45(part.functionCall)) {
7799
+ if (isRecord$46(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$45(fc.args) ? fc.args : {});
7804
+ const argsText = JSON.stringify(isRecord$46(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$45(responseData.promptFeedback) ? responseData.promptFeedback : void 0)?.blockReason);
7843
+ const blockReason = asString$24((isRecord$46(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", "PROVIDER_ERROR");
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 });
@@ -13824,7 +13856,7 @@ async function compactionCandidates(ctx) {
13824
13856
  };
13825
13857
  }))).filter((provider) => provider.models.length > 0) };
13826
13858
  }
13827
- function isRecord$44(value) {
13859
+ function isRecord$45(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$44(value.contextWindowOverrides)) throw new PreferenceError("contextWindowOverrides must be an object.");
13900
+ if (!isRecord$45(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$43(value) {
18400
+ function isRecord$44(value) {
18369
18401
  return typeof value === "object" && value !== null && !Array.isArray(value);
18370
18402
  }
18371
18403
  function asRecord$15(value) {
18372
- return isRecord$43(value) ? value : void 0;
18404
+ return isRecord$44(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$1(record, keys) {
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$43(value)) return void 0;
18448
+ if (!isRecord$44(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$43(value)) return out;
18462
+ if (!isRecord$44(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$1(user, ["email", "primaryEmail"]) ?? (typeof user.email === "string" ? user.email : void 0) ?? fallback.email;
18496
+ const email = firstString$2(user, ["email", "primaryEmail"]) ?? (typeof user.email === "string" ? user.email : void 0) ?? fallback.email;
18465
18497
  return {
18466
- userId: firstString$1(user, [
18498
+ userId: firstString$2(user, [
18467
18499
  "id",
18468
18500
  "userId",
18469
18501
  "uid"
18470
- ]) ?? firstString$1(root, ["userId"]) ?? fallback.userId ?? null,
18471
- userName: firstString$1(user, [
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$1(root, ["userName"]) ?? fallback.userName ?? null,
18509
+ ]) ?? firstString$2(root, ["userName"]) ?? fallback.userName ?? null,
18478
18510
  email: email ?? null,
18479
- organizationName: firstString$1(org, [
18511
+ organizationName: firstString$2(org, [
18480
18512
  "name",
18481
18513
  "displayName",
18482
18514
  "slug"
18483
18515
  ]) ?? fallback.organizationName ?? null,
18484
- keyName: firstString$1(key, [
18516
+ keyName: firstString$2(key, [
18485
18517
  "name",
18486
18518
  "keyName",
18487
18519
  "label"
18488
18520
  ]) ?? fallback.keyName ?? null,
18489
- planLabel: firstString$1(subscription, [
18521
+ planLabel: firstString$2(subscription, [
18490
18522
  "name",
18491
18523
  "displayName",
18492
18524
  "label",
18493
18525
  "planName"
18494
- ]) ?? firstString$1(data, ["planName", "planLabel"]) ?? fallback.planLabel ?? null,
18495
- planId: firstString$1(subscription, [
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$1(record, [
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$1(record, [
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$1(record, [
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$1(record, [
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$1(record, [
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$1(record, [
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$1(record, ["displayName", "name"]) ?? id,
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$42(value) {
19272
+ function isRecord$43(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$42(attachment)) return void 0;
19312
+ if (!isRecord$43(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$42(block.attachment) ? block.attachment : void 0;
19316
+ const attachment = isRecord$43(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$42(block)) continue;
19322
+ if (!isRecord$43(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$42(block)) continue;
19343
+ if (!isRecord$43(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$42(block)) return block;
19379
+ if (remaining.count === 0 || !isRecord$43(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$42(block.source) ? block.source : void 0;
19451
+ const source = isRecord$43(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$42(block)) continue;
19477
+ if (!isRecord$43(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$42(block)) return "";
19486
+ if (!isRecord$43(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$42(block)) continue;
19504
+ if (!isRecord$43(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$42(block)) continue;
19553
+ if (!isRecord$43(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$42(schema)) return {
19597
+ if (!isRecord$43(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$42(block)) continue;
19614
+ if (!isRecord$43(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$42(block)) continue;
19642
+ if (!isRecord$43(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$42(block) && typeof block.toolCallId === "string" ? block.toolCallId : "";
19674
+ const callId = isRecord$43(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$42(block)) continue;
19735
+ if (!isRecord$43(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$42(block)) continue;
19772
+ if (!isRecord$43(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$42(parsed) ? parsed : {}
19785
+ input: isRecord$43(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$42(block)) continue;
19879
+ if (!isRecord$43(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$42(block)) continue;
19904
+ if (!isRecord$43(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$42(b) && b.type === "tool-result") : void 0;
19900
- const callId = (isRecord$42(toolResult) && typeof toolResult.toolCallId === "string" ? toolResult.toolCallId : "") || (typeof current.source?.callId === "string" ? current.source.callId : "");
19931
+ const toolResult = Array.isArray(current.content) ? current.content.find((b) => isRecord$43(b) && b.type === "tool-result") : void 0;
19932
+ const callId = (isRecord$43(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$42(call.function) ? call.function : void 0;
19955
+ const fn = isRecord$43(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$42(chunk)) return [];
20078
+ if (!isRecord$43(chunk)) return [];
20047
20079
  const out = [];
20048
- if (isRecord$42(chunk.error)) {
20080
+ if (isRecord$43(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$42(chunk.usage) ? chunk.usage : void 0;
20089
+ const usage = isRecord$43(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$42(usage.prompt_tokens_details) ? usage.prompt_tokens_details : void 0;
20093
+ const details = isRecord$43(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$42(usage.completion_tokens_details)) state.reasoningTokens = numberOr$8(usage.completion_tokens_details.reasoning_tokens, state.reasoningTokens);
20098
+ if (isRecord$43(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$42(choices[0]) ? choices[0] : void 0;
20070
- const delta = isRecord$42(choice?.delta) ? choice.delta : void 0;
20101
+ const choice = isRecord$43(choices[0]) ? choices[0] : void 0;
20102
+ const delta = isRecord$43(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$42(entry)) continue;
20130
+ if (!isRecord$43(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$42(entry.function) ? entry.function : {};
20144
+ const fn = isRecord$43(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$42(event)) return [];
20202
+ if (!isRecord$43(event)) return [];
20171
20203
  const type = asString$22(event.type);
20172
20204
  const out = [];
20173
20205
  if (type === "message_start") {
20174
- const message = isRecord$42(event.message) ? event.message : void 0;
20175
- const usage = message && isRecord$42(message.usage) ? message.usage : void 0;
20206
+ const message = isRecord$43(event.message) ? event.message : void 0;
20207
+ const usage = message && isRecord$43(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$42(event.content_block) ? event.content_block : {};
20221
+ const block = isRecord$43(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$42(event.delta) ? event.delta : {};
20271
+ const delta = isRecord$43(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$42(event.delta) ? event.delta : void 0;
20344
+ const delta = isRecord$43(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$42(event.usage) ? event.usage : void 0;
20347
+ const usage = isRecord$43(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$42(event.error) ? event.error : {};
20359
+ const error = isRecord$43(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$42(parsed)) return [];
20405
+ if (!isRecord$43(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$42(parsed.item) ? parsed.item : null;
20432
+ const item = isRecord$43(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$42(parsed.response) ? parsed.response : null;
20456
+ const response = isRecord$43(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$42(response.error) ? response.error : null;
20460
+ const errorObj = isRecord$43(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$42(response.incomplete_details) ? response.incomplete_details : null;
20473
+ const details = response && isRecord$43(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$42(parsed.error) ? parsed.error : isRecord$42(parsed.response) && isRecord$42(parsed.response.error) ? parsed.response.error : null;
20450
- const detail = (typeof parsed.message === "string" ? parsed.message : "") || (errorObj && typeof errorObj.message === "string" ? errorObj.message : "") || (isRecord$42(parsed.response) && typeof parsed.response.error === "string" ? parsed.response.error : typeof parsed.error === "string" ? parsed.error : "");
20481
+ const errorObj = isRecord$43(parsed.error) ? parsed.error : isRecord$43(parsed.response) && isRecord$43(parsed.response.error) ? parsed.response.error : null;
20482
+ const detail = (typeof parsed.message === "string" ? parsed.message : "") || (errorObj && typeof errorObj.message === "string" ? errorObj.message : "") || (isRecord$43(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$42(response.usage) ? response.usage : null;
20570
+ const usage = isRecord$43(response.usage) ? response.usage : null;
20539
20571
  if (usage === null) return;
20540
- const cached = isRecord$42(usage.input_tokens_details) ? numberOrZero(usage.input_tokens_details.cached_tokens) : 0;
20541
- const written = isRecord$42(usage.input_tokens_details) ? numberOrZero(usage.input_tokens_details.cache_write_tokens) : 0;
20572
+ const cached = isRecord$43(usage.input_tokens_details) ? numberOrZero(usage.input_tokens_details.cached_tokens) : 0;
20573
+ const written = isRecord$43(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$42(usage.output_tokens_details) ? usage.output_tokens_details : null;
20578
+ const outputDetails = isRecord$43(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$41(error) ? error : {};
20666
+ const fields = isRecord$42(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$41(error)) return null;
20725
+ if (!isRecord$42(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$41(value) {
20731
+ function isRecord$42(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$40(value) {
23926
+ function isRecord$41(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$40(block.attachment) ? block.attachment : void 0;
23933
+ const attachment = isRecord$41(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$40(part)) continue;
23969
+ if (!isRecord$41(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$40(inline)) {
23998
+ if (isRecord$41(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$40(message.source) ? message.source.callId : "") ?? "");
24039
+ const toolCallId = String(block.toolCallId ?? (isRecord$41(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$39(value) {
24650
+ function isRecord$40(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$39(attachment)) return void 0;
24709
+ if (!isRecord$40(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$39(block.attachment) ? block.attachment : void 0;
24713
+ const attachment = isRecord$40(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$39(block)) continue;
24719
+ if (!isRecord$40(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$39(block.source) ? block.source : void 0;
24749
+ const source = isRecord$40(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$39(block)) continue;
24775
+ if (!isRecord$40(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$39(block)) return block;
24829
+ if (remaining.count === 0 || !isRecord$40(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 {@link REQUEST_IMAGE_MAX_EDGE} is sent as the
24936
- * harness's downscaled request version (see {@link requestImageTarget}). The
24937
- * stored image and durable history do not change, and an image that already
24938
- * fits is sent byte-for-byte as stored.
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) > 1024 ? { kind: "unavailable" } : {
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$39(block.source) ? block.source : void 0;
25030
+ const source = isRecord$40(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:")) {
@@ -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$38(value) {
26609
+ function isRecord$39(value) {
26568
26610
  return typeof value === "object" && value !== null && !Array.isArray(value);
26569
26611
  }
26570
26612
  function asRecord$13(value) {
26571
- return isRecord$38(value) ? value : void 0;
26613
+ return isRecord$39(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;
@@ -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$37(value) {
27585
+ function isRecord$38(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$37(value)) {
27590
+ if (isRecord$38(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$37(current)) {
27605
+ if (isRecord$38(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$37(node)) {
27621
+ if (isRecord$38(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$37(inlined)) {
27630
+ if (isRecord$38(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$37(node)) {
27650
+ if (isRecord$38(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$37(node)) return;
27640
- if (isRecord$37(node.properties)) {
27641
- for (const propSchema of Object.values(node.properties)) if (isRecord$37(propSchema)) {
27681
+ if (!isRecord$38(node)) return;
27682
+ if (isRecord$38(node.properties)) {
27683
+ for (const propSchema of Object.values(node.properties)) if (isRecord$38(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$37(propSchema.properties)) propSchema.type = "object";
27687
+ else if (isRecord$38(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$37(node.items)) normalizeSchemaProperties(node.items);
27693
+ if (isRecord$38(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$37(schema)) return {
27701
+ if (!isRecord$38(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$36(value) {
27719
+ function isRecord$37(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$36(block)) return total;
27752
+ if (!isRecord$37(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$35(value) {
27853
+ function isRecord$36(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$35(attachment)) return void 0;
27861
+ if (!isRecord$36(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$35(block) || block.type !== "video") continue;
27868
+ if (!isRecord$36(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$35(block) || block.type !== "video") continue;
27883
+ if (!isRecord$36(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$35(block) && block.type === "video"));
27927
+ return options.messages.some((message) => Array.isArray(message.content) && message.content.some((block) => isRecord$36(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$35(block) || block.type !== "video") return block;
27962
+ if (remaining.count === 0 || !isRecord$36(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$34(value) {
28045
+ function isRecord$35(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$35(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
- /** Reasoning text on an inbound message or delta, remembering its key. */
28048
- observe(source) {
28049
- if (typeof source !== "object" || source === null) return void 0;
28050
- const record = source;
28051
- for (const key of KNOWN_REASONING_KEYS) {
28052
- const value = record[key];
28053
- if (typeof value !== "string") continue;
28054
- this.detected = key;
28055
- return value;
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$34(block)) continue;
28402
+ if (!isRecord$35(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$34(block)) return "";
28411
+ if (!isRecord$35(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$34(block)) continue;
28429
+ if (!isRecord$35(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$34(block)) continue;
28484
+ if (!isRecord$35(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$35(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
- /** Concatenated reasoning text one assistant message carries, when it has any. */
28362
- function reasoningText(message) {
28363
- if (!Array.isArray(message.content)) return "";
28364
- const parts = [];
28365
- for (const block of message.content) if (isRecord$34(block) && block.type === "reasoning" && typeof block.text === "string") parts.push(sanitizeText$6(block.text));
28366
- return parts.join("");
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$35(state) || !Array.isArray(state.blocks)) return void 0;
28603
+ const entry = state.blocks[blockIndex];
28604
+ if (!isRecord$35(entry) || !Array.isArray(entry.reasoning)) return void 0;
28605
+ const runs = [];
28606
+ for (const raw of entry.reasoning) {
28607
+ if (!isRecord$35(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$35(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.
@@ -28410,7 +28756,7 @@ function openAIUserContent$2(message, images, media = {}) {
28410
28756
  const parts = [];
28411
28757
  let hasRichPart = false;
28412
28758
  for (const block of message.content) {
28413
- if (!isRecord$34(block)) continue;
28759
+ if (!isRecord$35(block)) continue;
28414
28760
  if (block.type === "text" && typeof block.text === "string") {
28415
28761
  const text = sanitizeText$6(block.text);
28416
28762
  if (text !== "") parts.push({
@@ -28418,18 +28764,36 @@ function openAIUserContent$2(message, images, media = {}) {
28418
28764
  text
28419
28765
  });
28420
28766
  } else if (block.type === "image") {
28421
- const inline = imageBlockToInline$2(block, images);
28422
- if (inline && SUPPORTED_IMAGE_MEDIA_TYPES$3.has(inline.mediaType)) {
28767
+ const reference = hostedReference(media.imageReferences, block);
28768
+ if (reference !== void 0) {
28423
28769
  hasRichPart = true;
28424
28770
  parts.push({
28425
28771
  type: "image_url",
28426
- image_url: { url: `data:${inline.mediaType};base64,${inline.data}` }
28772
+ image_url: { url: reference }
28427
28773
  });
28428
- } else parts.push({
28429
- type: "text",
28430
- text: unavailableImageText$3(block)
28431
- });
28774
+ } else {
28775
+ const inline = imageBlockToInline$2(block, images);
28776
+ if (inline && SUPPORTED_IMAGE_MEDIA_TYPES$3.has(inline.mediaType)) {
28777
+ hasRichPart = true;
28778
+ parts.push({
28779
+ type: "image_url",
28780
+ image_url: { url: `data:${inline.mediaType};base64,${inline.data}` }
28781
+ });
28782
+ } else parts.push({
28783
+ type: "text",
28784
+ text: unavailableImageText$3(block)
28785
+ });
28786
+ }
28432
28787
  } else if (block.type === "video") {
28788
+ const reference = hostedReference(media.videoReferences, block);
28789
+ if (reference !== void 0) {
28790
+ hasRichPart = true;
28791
+ parts.push({
28792
+ type: "video_url",
28793
+ video_url: { url: reference }
28794
+ });
28795
+ continue;
28796
+ }
28433
28797
  const outcome = videoBlockToInline(block, media.videos ?? /* @__PURE__ */ new Map(), media.videoAccepted === true, videoOmissionText$1);
28434
28798
  if ("inline" in outcome) {
28435
28799
  hasRichPart = true;
@@ -28446,11 +28810,11 @@ function openAIUserContent$2(message, images, media = {}) {
28446
28810
  if (!hasRichPart) return parts.map((part) => typeof part.text === "string" ? part.text : "").join("");
28447
28811
  return parts;
28448
28812
  }
28449
- function openAIAssistantContent$2(message, toolCallIds) {
28813
+ function openAIAssistantContent$2(message, toolCallIds, reasoningKey) {
28450
28814
  const textParts = [];
28451
28815
  const toolCalls = [];
28452
28816
  for (const block of message.content) {
28453
- if (!isRecord$34(block)) continue;
28817
+ if (!isRecord$35(block)) continue;
28454
28818
  if (block.type === "text" && typeof block.text === "string") textParts.push(sanitizeText$6(block.text));
28455
28819
  else if (block.type === "tool-call" && typeof block.name === "string") toolCalls.push({
28456
28820
  id: toolCallIds.normalize(typeof block.id === "string" && block.id !== "" ? block.id : `call_${toolCalls.length}`),
@@ -28464,7 +28828,7 @@ function openAIAssistantContent$2(message, toolCallIds) {
28464
28828
  return {
28465
28829
  content: textParts.join(""),
28466
28830
  toolCalls,
28467
- reasoning: reasoningText(message)
28831
+ reasoning: replayedReasoning(message, reasoningKey)
28468
28832
  };
28469
28833
  }
28470
28834
  /** Why one declaration could not be put on the wire, as the model sees it. */
@@ -28526,6 +28890,58 @@ function declarationSlots(options, media) {
28526
28890
  }
28527
28891
  return slots;
28528
28892
  }
28893
+ /**
28894
+ * Write one assistant turn's reasoning under the field it arrived under.
28895
+ *
28896
+ * Two rules are in tension here and both matter:
28897
+ *
28898
+ * - Preserved Thinking requires a reasoning field on every assistant message, so
28899
+ * the learned dialect key is ALWAYS written, with an empty string when the
28900
+ * turn produced no reasoning. Omitting it is the documented cause of
28901
+ * "thinking is enabled but reasoning_content is missing in assistant tool call
28902
+ * message at index N".
28903
+ * - A replayed fragment must not be written under a key it did not come from, so
28904
+ * each surviving field is written on its own and nothing is concatenated onto
28905
+ * the dialect key. `reasoning_details` goes back in the shape it arrived in: a
28906
+ * bare string stays a string, elements stay an array.
28907
+ */
28908
+ function applyReplayedReasoning(entry, reasoning, dialectKey) {
28909
+ const fields = reasoning.fields.length === 0 ? [{
28910
+ key: dialectKey,
28911
+ text: ""
28912
+ }] : reasoning.fields;
28913
+ for (const field of fields) entry[field.key] = field.text;
28914
+ if (fields.every((field) => field.key !== dialectKey)) entry[dialectKey] = "";
28915
+ if (reasoning.details.length > 0) {
28916
+ const elements = reasoning.details.map(replayedDetailElement);
28917
+ if (reasoning.detailsString !== void 0 && reasoning.detailsString !== "") elements.unshift({ summary: reasoning.detailsString });
28918
+ entry.reasoning_details = elements;
28919
+ } else if (reasoning.detailsString !== void 0) entry.reasoning_details = reasoning.detailsString;
28920
+ }
28921
+ /**
28922
+ * One replayed element, in the shape the peer sent it in.
28923
+ *
28924
+ * `type` is echoed only when the wire carried one (a summary may have none), and
28925
+ * `index` is echoed only when the wire carried one - neither is invented.
28926
+ */
28927
+ function replayedDetailElement(detail) {
28928
+ const index = detail.index === void 0 ? {} : { index: detail.index };
28929
+ if (detail.form === "encrypted") return {
28930
+ ...detail.wireType === void 0 ? {} : { type: detail.wireType },
28931
+ encrypted: detail.encrypted,
28932
+ ...index
28933
+ };
28934
+ if (detail.form === "summary") return {
28935
+ ...detail.wireType === void 0 ? {} : { type: detail.wireType },
28936
+ summary: detail.summary,
28937
+ ...index
28938
+ };
28939
+ return {
28940
+ type: "reasoning.text",
28941
+ text: detail.text,
28942
+ ...index
28943
+ };
28944
+ }
28529
28945
  /** Build one `/chat/completions` body. */
28530
28946
  function buildOpenAIRequest(options, images = NO_RESOLVED_IMAGES$3, preserveThinking = preserveThinkingEnabled(), media = {}) {
28531
28947
  const effort = mapReasoningEffort(options.reasoningEffort === void 0 ? void 0 : String(options.reasoningEffort));
@@ -28561,13 +28977,13 @@ function buildOpenAIRequest(options, images = NO_RESOLVED_IMAGES$3, preserveThin
28561
28977
  nonSystemIndex += 1;
28562
28978
  }
28563
28979
  const block = current.content[0];
28564
- const callId = isRecord$34(block) && typeof block.toolCallId === "string" ? block.toolCallId : "";
28980
+ const callId = isRecord$35(block) && typeof block.toolCallId === "string" ? block.toolCallId : "";
28565
28981
  messages.push({
28566
28982
  role: "tool",
28567
28983
  tool_call_id: toolCallIds.normalize(callId),
28568
28984
  content: toolResultText$6(current.content)
28569
28985
  });
28570
- imageBlocks.push(...toolResultImageBlocks$3(current.content, images));
28986
+ imageBlocks.push(...toolResultImageBlocks$3(current.content, images, media));
28571
28987
  index += 1;
28572
28988
  }
28573
28989
  index -= 1;
@@ -28578,12 +28994,13 @@ function buildOpenAIRequest(options, images = NO_RESOLVED_IMAGES$3, preserveThin
28578
28994
  continue;
28579
28995
  }
28580
28996
  if (message.role === "assistant") {
28581
- const { content, toolCalls, reasoning } = openAIAssistantContent$2(message, toolCallIds);
28582
- if (content === "" && toolCalls.length === 0 && reasoning === "") continue;
28997
+ const reasoningKey = reasoningDialect.outboundKey();
28998
+ const { content, toolCalls, reasoning } = openAIAssistantContent$2(message, toolCallIds, reasoningKey);
28999
+ if (content === "" && toolCalls.length === 0 && reasoning.fields.length === 0 && reasoning.details.length === 0) continue;
28583
29000
  const entry = { role: "assistant" };
28584
29001
  if (content !== "" || toolCalls.length === 0) entry.content = content;
28585
29002
  if (toolCalls.length > 0) entry.tool_calls = toolCalls;
28586
- if (thinkingOn) entry[reasoningDialect.outboundKey()] = reasoning;
29003
+ if (thinkingOn) applyReplayedReasoning(entry, reasoning, reasoningKey);
28587
29004
  messages.push(entry);
28588
29005
  continue;
28589
29006
  }
@@ -28647,7 +29064,7 @@ function anthropicUserContent$2(message, images, toolCallIds) {
28647
29064
  if (!Array.isArray(message.content)) return [];
28648
29065
  const blocks = [];
28649
29066
  for (const block of message.content) {
28650
- if (!isRecord$34(block)) continue;
29067
+ if (!isRecord$35(block)) continue;
28651
29068
  if (block.type === "video") {
28652
29069
  blocks.push({
28653
29070
  type: "text",
@@ -28691,7 +29108,7 @@ function anthropicUserContent$2(message, images, toolCallIds) {
28691
29108
  function anthropicAssistantContent$2(message, toolCallIds) {
28692
29109
  const blocks = [];
28693
29110
  for (const block of message.content) {
28694
- if (!isRecord$34(block)) continue;
29111
+ if (!isRecord$35(block)) continue;
28695
29112
  if (block.type === "text" && typeof block.text === "string") {
28696
29113
  const text = sanitizeText$6(block.text);
28697
29114
  if (text !== "") blocks.push({
@@ -28704,7 +29121,7 @@ function anthropicAssistantContent$2(message, toolCallIds) {
28704
29121
  type: "tool_use",
28705
29122
  id: toolCallIds.normalize(typeof block.id === "string" && block.id !== "" ? block.id : `toolu_${blocks.length}`),
28706
29123
  name: block.name,
28707
- input: isRecord$34(parsed) ? parsed : {}
29124
+ input: isRecord$35(parsed) ? parsed : {}
28708
29125
  });
28709
29126
  }
28710
29127
  }
@@ -28826,7 +29243,7 @@ function walkJson(value, visit) {
28826
29243
  for (const item of value) walkJson(item, visit);
28827
29244
  return;
28828
29245
  }
28829
- if (!isRecord$34(value)) return;
29246
+ if (!isRecord$35(value)) return;
28830
29247
  visit(value);
28831
29248
  for (const nested of Object.values(value)) walkJson(nested, visit);
28832
29249
  }
@@ -28842,12 +29259,12 @@ function requestBodyBreakdown(body, carriesVideo = false) {
28842
29259
  const totalBytes = Buffer.byteLength(serialized, "utf8");
28843
29260
  let imageBytes = 0;
28844
29261
  walkJson(body, (node) => {
28845
- const url = isRecord$34(node.image_url) ? asString$16(node.image_url.url) : void 0;
29262
+ const url = isRecord$35(node.image_url) ? asString$16(node.image_url.url) : void 0;
28846
29263
  if (url?.startsWith("data:")) {
28847
29264
  imageBytes += Buffer.byteLength(url, "utf8");
28848
29265
  return;
28849
29266
  }
28850
- const source = isRecord$34(node.source) ? node.source : void 0;
29267
+ const source = isRecord$35(node.source) ? node.source : void 0;
28851
29268
  if (source && asString$16(source.data) !== void 0 && asString$16(source.media_type) !== void 0) imageBytes += Buffer.byteLength(asString$16(source.data) ?? "", "utf8");
28852
29269
  });
28853
29270
  const toolSchemaBytes = Buffer.byteLength(JSON.stringify(body.tools ?? []), "utf8");
@@ -28886,6 +29303,7 @@ function createStreamState$4(wire, sessionId) {
28886
29303
  sessionId: typeof sessionId === "string" && sessionId.trim() !== "" ? sessionId.trim() : void 0,
28887
29304
  blocks: [],
28888
29305
  current: null,
29306
+ reasoningRuns: /* @__PURE__ */ new Map(),
28889
29307
  toolCalls: /* @__PURE__ */ new Map(),
28890
29308
  contentIndexes: /* @__PURE__ */ new Map(),
28891
29309
  openContentIndex: null,
@@ -28972,9 +29390,9 @@ function processOpenAIStreamLine(line, state) {
28972
29390
  }
28973
29391
  if (payload === "") return [];
28974
29392
  const chunk = safeJsonParse$6(payload);
28975
- if (!isRecord$34(chunk)) return [];
29393
+ if (!isRecord$35(chunk)) return [];
28976
29394
  const out = [];
28977
- const errorPayload = isRecord$34(chunk.error) ? chunk.error : void 0;
29395
+ const errorPayload = isRecord$35(chunk.error) ? chunk.error : void 0;
28978
29396
  if (errorPayload !== void 0) {
28979
29397
  const message = asString$16(errorPayload.message) ?? "unknown error";
28980
29398
  state.streamError = {
@@ -28984,34 +29402,47 @@ function processOpenAIStreamLine(line, state) {
28984
29402
  };
28985
29403
  throw new LlmError$1(`Kimi Code stream error: ${message}`, isContextOverflow(errorPayload) ? CONTEXT_OVERFLOW_CODE : "PROVIDER_ERROR");
28986
29404
  }
28987
- const usage = isRecord$34(chunk.usage) ? chunk.usage : void 0;
29405
+ const usage = isRecord$35(chunk.usage) ? chunk.usage : void 0;
28988
29406
  if (usage) {
28989
29407
  state.sawUsage = true;
28990
29408
  const prompt = numberOr$7(usage.prompt_tokens, 0);
28991
- const details = isRecord$34(usage.prompt_tokens_details) ? usage.prompt_tokens_details : void 0;
29409
+ const details = isRecord$35(usage.prompt_tokens_details) ? usage.prompt_tokens_details : void 0;
28992
29410
  const cached = details ? numberOr$7(details.cached_tokens, 0) : 0;
28993
29411
  const written = details ? numberOr$7(details.cache_write_tokens, 0) : 0;
28994
29412
  state.inputTokens = Math.max(0, prompt - cached - written);
28995
29413
  state.cacheReadTokens = cached;
28996
29414
  state.cacheWriteTokens = written;
28997
29415
  state.outputTokens = numberOr$7(usage.completion_tokens, state.outputTokens);
28998
- if (isRecord$34(usage.completion_tokens_details)) state.reasoningTokens = numberOr$7(usage.completion_tokens_details.reasoning_tokens, state.reasoningTokens);
29416
+ if (isRecord$35(usage.completion_tokens_details)) state.reasoningTokens = numberOr$7(usage.completion_tokens_details.reasoning_tokens, state.reasoningTokens);
28999
29417
  }
29000
29418
  const choices = Array.isArray(chunk.choices) ? chunk.choices : [];
29001
- const choice = isRecord$34(choices[0]) ? choices[0] : void 0;
29002
- const delta = isRecord$34(choice?.delta) ? choice.delta : void 0;
29419
+ const choice = isRecord$35(choices[0]) ? choices[0] : void 0;
29420
+ const delta = isRecord$35(choice?.delta) ? choice.delta : void 0;
29003
29421
  if (delta) {
29004
- const reasoning = reasoningDialect.observe(delta);
29005
- if (reasoning !== void 0 && reasoning !== "") {
29422
+ for (const fragment of reasoningDialect.observeAll(delta)) {
29423
+ const text = sanitizeText$6(fragment.text);
29424
+ if (text === "" && fragment.form !== "encrypted") continue;
29006
29425
  out.push(...closeToolCalls$4(state));
29007
29426
  if (state.current === null || state.current.type !== "reasoning") out.push(...openTextBlock$4(state, "reasoning"));
29008
- state.current.text += sanitizeText$6(reasoning);
29009
- state.hasContent = true;
29010
- out.push({
29011
- type: "reasoning-delta",
29012
- index: state.current.index,
29013
- text: sanitizeText$6(reasoning)
29427
+ if (text !== "") {
29428
+ state.current.text += text;
29429
+ state.hasContent = true;
29430
+ out.push({
29431
+ type: "reasoning-delta",
29432
+ index: state.current.index,
29433
+ text
29434
+ });
29435
+ }
29436
+ const runs = state.reasoningRuns.get(state.current.index) ?? [];
29437
+ runs.push({
29438
+ key: fragment.key,
29439
+ ...fragment.form === void 0 ? {} : { form: fragment.form },
29440
+ ...fragment.wireType === void 0 ? {} : { wireType: fragment.wireType },
29441
+ ...fragment.index === void 0 ? {} : { index: fragment.index },
29442
+ ...fragment.encrypted === void 0 ? {} : { encrypted: fragment.encrypted },
29443
+ length: text.length
29014
29444
  });
29445
+ state.reasoningRuns.set(state.current.index, runs);
29015
29446
  }
29016
29447
  const content = asString$16(delta.content);
29017
29448
  if (content !== void 0 && content !== "") {
@@ -29027,7 +29458,7 @@ function processOpenAIStreamLine(line, state) {
29027
29458
  }
29028
29459
  const toolDeltas = Array.isArray(delta.tool_calls) ? delta.tool_calls : [];
29029
29460
  for (const entry of toolDeltas) {
29030
- if (!isRecord$34(entry)) continue;
29461
+ if (!isRecord$35(entry)) continue;
29031
29462
  out.push(...applyOpenAIToolDelta(entry, state));
29032
29463
  }
29033
29464
  }
@@ -29041,7 +29472,7 @@ function processOpenAIStreamLine(line, state) {
29041
29472
  }
29042
29473
  function applyOpenAIToolDelta(entry, state) {
29043
29474
  const wireIndex = typeof entry.index === "number" ? entry.index : 0;
29044
- const fn = isRecord$34(entry.function) ? entry.function : {};
29475
+ const fn = isRecord$35(entry.function) ? entry.function : {};
29045
29476
  const out = [];
29046
29477
  let call = state.toolCalls.get(wireIndex);
29047
29478
  if (call === void 0) {
@@ -29096,12 +29527,12 @@ function processAnthropicStreamLine(line, state) {
29096
29527
  const payload = trimmed.slice(5).trim();
29097
29528
  if (payload === "" || payload === "[DONE]") return [];
29098
29529
  const event = safeJsonParse$6(payload);
29099
- if (!isRecord$34(event)) return [];
29530
+ if (!isRecord$35(event)) return [];
29100
29531
  const type = asString$16(event.type);
29101
29532
  const out = [];
29102
29533
  if (type === "message_start") {
29103
- const message = isRecord$34(event.message) ? event.message : void 0;
29104
- const usage = message && isRecord$34(message.usage) ? message.usage : void 0;
29534
+ const message = isRecord$35(event.message) ? event.message : void 0;
29535
+ const usage = message && isRecord$35(message.usage) ? message.usage : void 0;
29105
29536
  if (usage) {
29106
29537
  state.sawUsage = true;
29107
29538
  state.inputTokens = numberOr$7(usage.input_tokens, 0);
@@ -29115,7 +29546,7 @@ function processAnthropicStreamLine(line, state) {
29115
29546
  }
29116
29547
  if (type === "content_block_start") {
29117
29548
  const contentIndex = numberOr$7(event.index, 0);
29118
- const block = isRecord$34(event.content_block) ? event.content_block : {};
29549
+ const block = isRecord$35(event.content_block) ? event.content_block : {};
29119
29550
  const blockType = asString$16(block.type);
29120
29551
  out.push(...closeCurrent$4(state));
29121
29552
  if (blockType === "tool_use") {
@@ -29165,7 +29596,7 @@ function processAnthropicStreamLine(line, state) {
29165
29596
  }
29166
29597
  if (type === "content_block_delta") {
29167
29598
  const contentIndex = numberOr$7(event.index, 0);
29168
- const delta = isRecord$34(event.delta) ? event.delta : {};
29599
+ const delta = isRecord$35(event.delta) ? event.delta : {};
29169
29600
  const deltaType = asString$16(delta.type);
29170
29601
  if (deltaType === "input_json_delta") {
29171
29602
  const pending = state.toolCalls.get(contentIndex);
@@ -29238,10 +29669,10 @@ function processAnthropicStreamLine(line, state) {
29238
29669
  return out;
29239
29670
  }
29240
29671
  if (type === "message_delta") {
29241
- const delta = isRecord$34(event.delta) ? event.delta : void 0;
29672
+ const delta = isRecord$35(event.delta) ? event.delta : void 0;
29242
29673
  const stop = delta ? asString$16(delta.stop_reason) : void 0;
29243
29674
  if (stop !== void 0 && stop !== "") state.finishReason = stop;
29244
- const usage = isRecord$34(event.usage) ? event.usage : void 0;
29675
+ const usage = isRecord$35(event.usage) ? event.usage : void 0;
29245
29676
  if (usage) {
29246
29677
  state.sawUsage = true;
29247
29678
  state.outputTokens = numberOr$7(usage.output_tokens, state.outputTokens);
@@ -29253,7 +29684,7 @@ function processAnthropicStreamLine(line, state) {
29253
29684
  return closeStream$3(state);
29254
29685
  }
29255
29686
  if (type === "error") {
29256
- const error = isRecord$34(event.error) ? event.error : {};
29687
+ const error = isRecord$35(event.error) ? event.error : {};
29257
29688
  const message = asString$16(error.message) ?? "unknown error";
29258
29689
  state.streamError = {
29259
29690
  vocabulary: "anthropic",
@@ -29512,6 +29943,46 @@ function finishReasonFor$6(state) {
29512
29943
  if (state.hasToolCall || reason === "tool_calls" || reason === "tool_use") return { kind: "tool-calls" };
29513
29944
  return { kind: "stop" };
29514
29945
  }
29946
+ /**
29947
+ * Replay metadata: which wire field every reasoning slice arrived under.
29948
+ *
29949
+ * The envelope is the only channel this route has for provenance. The harness's
29950
+ * `ReasoningBlock` is `{type, text}`, and its assembler rebuilds a streamed
29951
+ * reasoning block from the accumulated deltas, so a field added to the block
29952
+ * would be dropped before the message is stored. A terminal `finish` chunk's
29953
+ * `replayState` is persisted verbatim onto the assembled message's model
29954
+ * source, which is where the request builder reads it back - the Claude route in
29955
+ * this package relies on the same seam.
29956
+ *
29957
+ * Shape: one entry per emitted block, because assembly discards an envelope
29958
+ * whose block list does not match the emitted block count. A text or tool-call
29959
+ * block is therefore an explicit `null` rather than a skipped slot, and a
29960
+ * reasoning block is `{ reasoning: [{key, form?, wireType?, index?, encrypted?,
29961
+ * length}] }`. Only lengths are stored, never the text, and an encrypted run
29962
+ * carries `length: 0` plus the opaque payload — the one thing that must be
29963
+ * handed back to the provider unchanged. Everything here is plain JSON (strings,
29964
+ * numbers, arrays, nulls), which is what the harness's lossless-JSON check
29965
+ * requires.
29966
+ */
29967
+ function reasoningReplayState(state) {
29968
+ if (state.reasoningRuns.size === 0) return void 0;
29969
+ const blocks = state.blocks.map((block, index) => {
29970
+ const runs = state.reasoningRuns.get(index);
29971
+ if (runs === void 0 || runs.length === 0 || block.type !== "reasoning") return null;
29972
+ return { reasoning: runs.map((run) => ({
29973
+ key: run.key,
29974
+ ...run.form === void 0 ? {} : { form: run.form },
29975
+ ...run.wireType === void 0 ? {} : { wireType: run.wireType },
29976
+ ...run.index === void 0 ? {} : { index: run.index },
29977
+ ...run.encrypted === void 0 ? {} : { encrypted: run.encrypted },
29978
+ length: run.length
29979
+ })) };
29980
+ });
29981
+ return {
29982
+ response: { wire: state.wire },
29983
+ blocks
29984
+ };
29985
+ }
29515
29986
  /** Flush every open block, then emit usage and the terminal finish. */
29516
29987
  function closeStream$3(state, accountId) {
29517
29988
  if (state.finished) return [];
@@ -29524,9 +29995,11 @@ function closeStream$3(state, accountId) {
29524
29995
  usage: tokenUsage$4(state)
29525
29996
  });
29526
29997
  }
29998
+ const replayState = reasoningReplayState(state);
29527
29999
  out.push({
29528
30000
  type: "finish",
29529
- reason: finishReasonFor$6(state)
30001
+ reason: finishReasonFor$6(state),
30002
+ ...replayState === void 0 ? {} : { replayState }
29530
30003
  });
29531
30004
  return out;
29532
30005
  }
@@ -29535,6 +30008,532 @@ function assertStreamComplete$4(state) {
29535
30008
  if (!state.done && state.finishReason === null) throw new LlmError$1("Kimi Code stream ended before its terminal event", "TRANSPORT");
29536
30009
  }
29537
30010
  //#endregion
30011
+ //#region src/host/kimi-code/media-files.ts
30012
+ /**
30013
+ * Kimi Code hosted-file (media upload) client.
30014
+ *
30015
+ * WHY THIS FILE EXISTS. Everything this plugin has ever sent to the Kimi Code
30016
+ * gateway carries media INLINE, as a `data:<type>;base64,...` URL
30017
+ * (mapper.ts builds both the image and the video block that way). The gateway
30018
+ * also accepts a hosted reference instead: upload the bytes once, cite
30019
+ * `ms://<id>` afterwards. That is the only way to send a clip whose base64 would
30020
+ * overrun the request body ceiling, and it is what this module implements.
30021
+ *
30022
+ * EVERY WIRE FACT BELOW WAS MEASURED live against the subscription gateway by
30023
+ * `scripts/probe-kimi-files.probe.ts` on this line's own account and region,
30024
+ * not copied from the official client:
30025
+ *
30026
+ * - the upload is `POST {coding base}/v1/files`, `multipart/form-data` with a
30027
+ * `file` part and a `purpose` part whose value is `image` or `video`
30028
+ * (note: NOT the `*_understanding` vocabulary the MiniMax line uses);
30029
+ * - it authenticates with the bearer token plus the same `X-Msh-*` identity
30030
+ * headers every other request on this line sends;
30031
+ * - the body answers with the id at the top level or nested under
30032
+ * `data`/`file`/`result`, and the reference a later chat body cites is
30033
+ * `ms://<id>`;
30034
+ * - `content-type` is never set by hand: the runtime writes
30035
+ * `multipart/form-data; boundary=...` itself, and a hand-written JSON header
30036
+ * on a form body is a malformed request.
30037
+ *
30038
+ * DELIBERATELY NARROW. No adapter, mapper or route is touched here: this module
30039
+ * only uploads, caches and returns a reference. Integration is a later change.
30040
+ *
30041
+ * @module dsh-chatgpt-subscription/kimi-media-files
30042
+ */
30043
+ /** Upload route, relative to a base URL that may already carry `/v1`. */
30044
+ const FILES_UPLOAD_PATH$1 = "/v1/files";
30045
+ /** Scheme a stored id is cited by on the wire. */
30046
+ const FILE_REF_SCHEME$1 = "ms://";
30047
+ /** Time budget for one upload request. */
30048
+ const MEDIA_UPLOAD_TIMEOUT_MS = 12e4;
30049
+ /**
30050
+ * One failed upload.
30051
+ *
30052
+ * Carries the HTTP status and a REDACTED digest of the response body so a
30053
+ * caller can decide (401 -> sign in again, 5xx -> retry) without the raw body
30054
+ * reaching a log. The access token is never placed in `message` or `detail`;
30055
+ * see {@link redact}.
30056
+ */
30057
+ var KimiMediaUploadError = class extends Error {
30058
+ failure;
30059
+ status;
30060
+ /** Redacted one-line digest of the response body, or '' when there was none. */
30061
+ detail;
30062
+ constructor(failure, message, options = {}) {
30063
+ super(message);
30064
+ this.name = "KimiMediaUploadError";
30065
+ this.failure = failure;
30066
+ this.status = options.status ?? null;
30067
+ this.detail = options.detail ?? "";
30068
+ }
30069
+ };
30070
+ /**
30071
+ * The upload URL for one coding base.
30072
+ *
30073
+ * The base this line resolves normally carries neither a version nor a trailing
30074
+ * slash (`https://api.kimi.com/coding`), so the measured route is built by
30075
+ * appending `/v1/files` and the answer is `.../coding/v1/files`. A caller that
30076
+ * passes a base which ALREADY ends in `/v1` — the official client's own
30077
+ * KIMI_CODE_BASE_URL shape — gets the prefix removed once rather than doubled:
30078
+ * `.../coding/v1/v1/files` is a 404 that reads like a missing route.
30079
+ */
30080
+ function filesUploadUrl$1(baseUrl) {
30081
+ const trimmed = baseUrl.trim().replace(/\/+$/, "");
30082
+ if (trimmed === "") throw new KimiMediaUploadError("invalid-request", "no base URL was given for the media upload");
30083
+ const base = trimmed.replace(/\/v1$/i, "").replace(/\/+$/, "");
30084
+ if (base === "") throw new KimiMediaUploadError("invalid-request", "the media upload base URL has no host");
30085
+ return base + FILES_UPLOAD_PATH$1;
30086
+ }
30087
+ /** The upload purpose one media type maps to, or undefined when unsupported. */
30088
+ function mediaKindFor(mediaType) {
30089
+ const normalized = mediaType.trim().toLowerCase();
30090
+ if (normalized.startsWith("image/")) return "image";
30091
+ if (normalized.startsWith("video/")) return "video";
30092
+ }
30093
+ /** File extensions for the media types this line sends, for the file part's name. */
30094
+ const MEDIA_TYPE_FILENAMES = {
30095
+ "image/png": "image.png",
30096
+ "image/jpeg": "image.jpg",
30097
+ "image/gif": "image.gif",
30098
+ "image/webp": "image.webp",
30099
+ "image/heic": "image.heic",
30100
+ "image/bmp": "image.bmp",
30101
+ "video/mp4": "video.mp4",
30102
+ "video/quicktime": "video.mov",
30103
+ "video/webm": "video.webm",
30104
+ "video/x-msvideo": "video.avi",
30105
+ "video/mpeg": "video.mpeg",
30106
+ "video/mpg": "video.mpg",
30107
+ "video/x-flv": "video.flv",
30108
+ "video/3gpp": "video.3gp"
30109
+ };
30110
+ /**
30111
+ * The name the file part carries.
30112
+ *
30113
+ * A caller-supplied name is reduced to its leaf and stripped of control
30114
+ * characters: the part name is metadata, never a path, so a Windows-style
30115
+ * source name must not travel with its directories. Nothing depends on the
30116
+ * extension, but a reviewer looking at a captured request benefits from a real
30117
+ * one, and some gateways sniff it.
30118
+ */
30119
+ function filenameFor(kind, mediaType, provided) {
30120
+ const fallback = MEDIA_TYPE_FILENAMES[mediaType.trim().toLowerCase()] ?? (kind === "image" ? "image.bin" : "video.bin");
30121
+ if (provided === void 0) return fallback;
30122
+ const safe = (provided.split(/[\\/]/).pop() ?? "").replace(/[\u0000-\u001f\u007f"]/g, "").trim();
30123
+ return safe === "" ? fallback : safe;
30124
+ }
30125
+ /**
30126
+ * Remove anything token-shaped from a string that may reach a log or an error.
30127
+ *
30128
+ * The token this upload used is removed by exact match first — a body echoing
30129
+ * the request, a proxy page quoting the Authorization header, or a service that
30130
+ * reflects the credential all end up here — then the generic shapes (bearer
30131
+ * values, JWTs, e-mail addresses) are blanked. The result is whitespace-flattened
30132
+ * and bounded so one refusal cannot become a wall of text.
30133
+ */
30134
+ function redact(text, token, limit = 240) {
30135
+ let out = text;
30136
+ if (token !== "") out = out.split(token).join("<redacted>");
30137
+ 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);
30138
+ }
30139
+ /** Parse a body without throwing, reporting whether it was JSON at all. */
30140
+ function parseJson(raw) {
30141
+ const text = raw.trim();
30142
+ if (text === "") return {
30143
+ ok: false,
30144
+ value: void 0
30145
+ };
30146
+ try {
30147
+ return {
30148
+ ok: true,
30149
+ value: JSON.parse(text)
30150
+ };
30151
+ } catch {
30152
+ return {
30153
+ ok: false,
30154
+ value: void 0
30155
+ };
30156
+ }
30157
+ }
30158
+ /**
30159
+ * One-line digest of a refusal body.
30160
+ *
30161
+ * A JSON refusal names a code and a sentence; an HTML gateway page names
30162
+ * neither. Both are reduced to a short redacted line, because "the server said
30163
+ * no" and "the server said no because the model does not accept this type" lead
30164
+ * to different fixes.
30165
+ */
30166
+ function bodyDigest(raw, token) {
30167
+ const text = raw.trim();
30168
+ if (text === "") return "";
30169
+ const parsed = parseJson(text);
30170
+ if (parsed.ok && isRecord$34(parsed.value)) {
30171
+ const root = parsed.value;
30172
+ const nested = isRecord$34(root.error) ? root.error : void 0;
30173
+ const code = firstString(nested?.code) ?? firstString(root.code) ?? firstString(root.error_code) ?? "";
30174
+ const message = nested !== void 0 ? firstString(nested.message) ?? firstString(nested.code) ?? "" : typeof root.error === "string" ? root.error : firstString(root.message) ?? firstString(root.detail) ?? "";
30175
+ const composed = [code === "" ? "" : "code=" + code, message].filter((part) => part !== "").join(" ");
30176
+ if (composed !== "") return redact(composed, token);
30177
+ }
30178
+ return redact(text, token);
30179
+ }
30180
+ /** Keys the id was measured to hide behind, besides a top-level one. */
30181
+ const NESTED_ID_KEYS = [
30182
+ "data",
30183
+ "file",
30184
+ "result"
30185
+ ];
30186
+ /** Longest id accepted, so a body echoing a document cannot become a reference. */
30187
+ const MAX_FILE_ID_LENGTH = 200;
30188
+ /** A non-empty, single-token id; anything else is not a reference this line cites. */
30189
+ function normalizeFileId(value) {
30190
+ if (typeof value === "number") return Number.isSafeInteger(value) && value >= 0 ? String(value) : void 0;
30191
+ if (typeof value !== "string") return void 0;
30192
+ const trimmed = value.trim();
30193
+ const bare = trimmed.startsWith("ms://") ? trimmed.slice(5) : trimmed;
30194
+ if (bare === "" || bare.length > MAX_FILE_ID_LENGTH) return void 0;
30195
+ if (/[\s\u0000-\u001f\u007f]/.test(bare)) return void 0;
30196
+ return bare;
30197
+ }
30198
+ /**
30199
+ * The id from a successful upload body.
30200
+ *
30201
+ * The measured body carries it at the top level, and documented shapes nest it
30202
+ * under `data`, `file` or `result`; both are read so a service-side change of
30203
+ * envelope does not blank the whole chain. A body with no id is reported by the
30204
+ * caller as `no-id` rather than accepted as an empty reference.
30205
+ */
30206
+ function readFileId$1(value, depth = 0) {
30207
+ if (depth > 3) return void 0;
30208
+ if (!isRecord$34(value)) return void 0;
30209
+ const direct = normalizeFileId(value.id);
30210
+ if (direct !== void 0) return direct;
30211
+ for (const key of NESTED_ID_KEYS) {
30212
+ const nested = readFileId$1(value[key], depth + 1);
30213
+ if (nested !== void 0) return nested;
30214
+ }
30215
+ }
30216
+ /** The wire reference for a stored id. */
30217
+ function fileReference(id) {
30218
+ return FILE_REF_SCHEME$1 + id;
30219
+ }
30220
+ /** Hex SHA-256 of the bytes; the content half of the key. */
30221
+ function contentSha256(data) {
30222
+ return createHash("sha256").update(data).digest("hex");
30223
+ }
30224
+ /**
30225
+ * The cache key for one upload.
30226
+ *
30227
+ * Five things, and each of them is load-bearing:
30228
+ *
30229
+ * - ACCOUNT SCOPE, because an id minted for one account is never assumed to be
30230
+ * readable by another (see {@link KimiMediaUploadRequest.accountScope});
30231
+ * - BASE URL (normalized through {@link filesUploadUrl}, so a trailing slash
30232
+ * cannot split one endpoint into two slots), because two regions or two
30233
+ * deployments are two distinct file stores;
30234
+ * - MEDIA KIND and MEDIA TYPE, because the same bytes uploaded as an image and
30235
+ * as a video are two different resources as far as the purpose is concerned;
30236
+ * - CONTENT SHA-256, so identical bytes reuse one upload and any change at all
30237
+ * misses the cache.
30238
+ *
30239
+ * The access token is NOT part of it: a key is written to memory only, but a
30240
+ * token in a key is a token in the next dump, and the account is already
30241
+ * identified by its scope. `|` cannot appear in a scope because the scope is
30242
+ * sanitized here.
30243
+ */
30244
+ function mediaFileCacheKey(input) {
30245
+ const kind = mediaKindFor(input.mediaType) ?? "unsupported";
30246
+ return [
30247
+ "v1",
30248
+ input.accountScope.trim().replace(/[|\s]+/g, "_"),
30249
+ filesUploadUrl$1(input.baseUrl),
30250
+ kind,
30251
+ input.mediaType.trim().toLowerCase(),
30252
+ contentSha256(input.data)
30253
+ ].join("|");
30254
+ }
30255
+ function isRecord$34(value) {
30256
+ return typeof value === "object" && value !== null && !Array.isArray(value);
30257
+ }
30258
+ function firstString(value) {
30259
+ return typeof value === "string" && value.trim() !== "" ? value.trim() : void 0;
30260
+ }
30261
+ /** Whether one rejection is a cancellation rather than an upload failure. */
30262
+ function looksAborted(error) {
30263
+ if (typeof error !== "object" || error === null) return false;
30264
+ return error.name === "AbortError";
30265
+ }
30266
+ /** The abort to reject a caller with, keeping the reason it supplied. */
30267
+ function abortError(signal) {
30268
+ const reason = signal?.reason;
30269
+ if (reason instanceof Error) return reason;
30270
+ const error = /* @__PURE__ */ new Error("the media upload was aborted");
30271
+ error.name = "AbortError";
30272
+ return error;
30273
+ }
30274
+ /**
30275
+ * Uploads one media file per distinct key, caching and single-flighting them.
30276
+ *
30277
+ * Two behaviours are deliberate and easy to get wrong:
30278
+ *
30279
+ * 1. SINGLE FLIGHT. Concurrent callers with the same key share one request.
30280
+ * A caller that aborts stops waiting immediately and gets an AbortError, but
30281
+ * the shared request is only cancelled once NO caller is still waiting — so
30282
+ * one cancelled turn does not fail another turn that wants the same clip.
30283
+ * 2. NO TTL. A successful reference is cached until it is evicted by the LRU
30284
+ * ceiling. Inventing an expiry would either expire a live id (a failed turn)
30285
+ * or outlive it silently; nothing here measured a lifetime, so nothing here
30286
+ * pretends to know one. A reference the service later rejects is dropped with
30287
+ * {@link forget} by whoever sees that rejection.
30288
+ */
30289
+ var KimiMediaFilesUploader = class {
30290
+ fetchFn;
30291
+ maxCacheEntries;
30292
+ timeoutMs;
30293
+ /** key -> `ms://<id>`, in least-recently-used order. */
30294
+ cache = /* @__PURE__ */ new Map();
30295
+ flights = /* @__PURE__ */ new Map();
30296
+ constructor(options = {}) {
30297
+ this.fetchFn = options.fetchFn ?? fetch;
30298
+ const cap = options.maxCacheEntries;
30299
+ this.maxCacheEntries = cap !== void 0 && Number.isFinite(cap) && cap > 0 ? Math.floor(cap) : 256;
30300
+ const timeout = options.timeoutMs;
30301
+ this.timeoutMs = timeout !== void 0 && Number.isFinite(timeout) ? timeout : MEDIA_UPLOAD_TIMEOUT_MS;
30302
+ }
30303
+ /** Cached references currently held, for a settings surface or a test. */
30304
+ get size() {
30305
+ return this.cache.size;
30306
+ }
30307
+ /** Uploads one file and returns the `ms://<id>` reference a request cites. */
30308
+ async upload(request) {
30309
+ const resolved = this.resolve(request);
30310
+ if (resolved.signal?.aborted === true) throw abortError(resolved.signal);
30311
+ const cached = this.readCache(resolved.key);
30312
+ if (cached !== void 0) return cached;
30313
+ let flight = this.flights.get(resolved.key);
30314
+ if (flight === void 0) {
30315
+ flight = this.startFlight(resolved);
30316
+ this.flights.set(resolved.key, flight);
30317
+ }
30318
+ return this.join(flight, resolved);
30319
+ }
30320
+ /**
30321
+ * Drop the reference one upload would use.
30322
+ *
30323
+ * The caller that sees a stored id REJECTED on the wire calls this so the next
30324
+ * attempt uploads again instead of replaying the same dead reference.
30325
+ */
30326
+ forget(request) {
30327
+ let key;
30328
+ try {
30329
+ key = mediaFileCacheKey({
30330
+ accountScope: request.accountScope,
30331
+ baseUrl: request.baseUrl,
30332
+ mediaType: request.mediaType,
30333
+ data: request.data
30334
+ });
30335
+ } catch {
30336
+ return false;
30337
+ }
30338
+ return this.cache.delete(key);
30339
+ }
30340
+ /** Forget everything; a sign-out or a test starting over. */
30341
+ clear() {
30342
+ this.cache.clear();
30343
+ }
30344
+ /** Validate and normalize one request, or throw before anything is sent. */
30345
+ resolve(request) {
30346
+ const accessToken = request.accessToken.trim();
30347
+ if (accessToken === "") throw new KimiMediaUploadError("invalid-request", "the media upload needs an access token");
30348
+ const accountScope = request.accountScope.trim();
30349
+ if (accountScope === "") throw new KimiMediaUploadError("invalid-request", "the media upload needs an account scope for its cache key");
30350
+ if (accountScope === request.accessToken) throw new KimiMediaUploadError("invalid-request", "the media upload cache scope must not be the access token");
30351
+ const mediaType = request.mediaType.trim();
30352
+ const kind = mediaKindFor(mediaType);
30353
+ if (kind === void 0) throw new KimiMediaUploadError("unsupported-media", "the media upload accepts image/* or video/* only, not \"" + mediaType + "\"");
30354
+ if (request.data.byteLength === 0) throw new KimiMediaUploadError("invalid-request", "the media upload was given no bytes");
30355
+ filesUploadUrl$1(request.baseUrl);
30356
+ return {
30357
+ accessToken,
30358
+ baseUrl: request.baseUrl,
30359
+ accountScope,
30360
+ data: request.data,
30361
+ mediaType,
30362
+ kind,
30363
+ filename: filenameFor(kind, mediaType, request.filename),
30364
+ ...request.signal === void 0 ? {} : { signal: request.signal },
30365
+ fetchFn: this.fetchFn,
30366
+ timeoutMs: this.timeoutMs,
30367
+ key: mediaFileCacheKey({
30368
+ accountScope,
30369
+ baseUrl: request.baseUrl,
30370
+ mediaType,
30371
+ data: request.data
30372
+ })
30373
+ };
30374
+ }
30375
+ readCache(key) {
30376
+ const ref = this.cache.get(key);
30377
+ if (ref === void 0) return void 0;
30378
+ this.cache.delete(key);
30379
+ this.cache.set(key, ref);
30380
+ return ref;
30381
+ }
30382
+ writeCache(key, ref) {
30383
+ this.cache.delete(key);
30384
+ this.cache.set(key, ref);
30385
+ while (this.cache.size > this.maxCacheEntries) {
30386
+ const oldest = this.cache.keys().next();
30387
+ if (oldest.done === true) break;
30388
+ this.cache.delete(oldest.value);
30389
+ }
30390
+ }
30391
+ /** Open one flight and run it; the map entry is dropped as it settles. */
30392
+ startFlight(request) {
30393
+ const controller = new AbortController();
30394
+ const state = { timedOut: false };
30395
+ const promise = this.performUpload(controller, state, request).finally(() => {
30396
+ if (this.flights.get(request.key)?.promise === promise) this.flights.delete(request.key);
30397
+ });
30398
+ promise.catch(() => void 0);
30399
+ return {
30400
+ promise,
30401
+ controller,
30402
+ waiters: 0,
30403
+ state
30404
+ };
30405
+ }
30406
+ /**
30407
+ * Wait for one flight, respecting THIS caller's abort.
30408
+ *
30409
+ * The flight is not bound to the caller's signal: a caller that aborts
30410
+ * detaches, and the transport is cancelled only when the last waiter has gone.
30411
+ */
30412
+ async join(flight, request) {
30413
+ const signal = request.signal;
30414
+ if (signal?.aborted === true) throw abortError(signal);
30415
+ flight.waiters += 1;
30416
+ let released = false;
30417
+ const release = () => {
30418
+ if (released) return;
30419
+ released = true;
30420
+ flight.waiters -= 1;
30421
+ if (flight.waiters <= 0) {
30422
+ if (this.flights.get(request.key) === flight) this.flights.delete(request.key);
30423
+ flight.controller.abort();
30424
+ }
30425
+ };
30426
+ if (signal === void 0) try {
30427
+ return await flight.promise;
30428
+ } finally {
30429
+ release();
30430
+ }
30431
+ const callerSignal = signal;
30432
+ let detach;
30433
+ const cancelled = new Promise((_resolve, reject) => {
30434
+ const onAbort = () => reject(abortError(callerSignal));
30435
+ detach = () => callerSignal.removeEventListener("abort", onAbort);
30436
+ callerSignal.addEventListener("abort", onAbort, { once: true });
30437
+ });
30438
+ try {
30439
+ return await Promise.race([flight.promise, cancelled]);
30440
+ } finally {
30441
+ detach?.();
30442
+ release();
30443
+ }
30444
+ }
30445
+ /** The one request a flight performs. */
30446
+ async performUpload(controller, state, request) {
30447
+ const form = new FormData();
30448
+ form.append("file", new Blob([request.data], { type: request.mediaType }), request.filename);
30449
+ form.append("purpose", request.kind);
30450
+ const headers = await kimiIdentityHeaders({
30451
+ authorization: "Bearer " + request.accessToken,
30452
+ accept: "application/json"
30453
+ });
30454
+ const timer = request.timeoutMs > 0 ? setTimeout(() => {
30455
+ state.timedOut = true;
30456
+ controller.abort();
30457
+ }, request.timeoutMs) : void 0;
30458
+ timer?.unref();
30459
+ try {
30460
+ let response;
30461
+ try {
30462
+ response = await request.fetchFn(filesUploadUrl$1(request.baseUrl), {
30463
+ method: "POST",
30464
+ headers,
30465
+ body: form,
30466
+ signal: controller.signal
30467
+ });
30468
+ } catch (error) {
30469
+ throw this.transportFailure(error, state, request);
30470
+ }
30471
+ let raw = "";
30472
+ try {
30473
+ raw = await response.text();
30474
+ } catch (error) {
30475
+ throw this.transportFailure(error, state, request);
30476
+ }
30477
+ return this.settle(response, raw, request);
30478
+ } finally {
30479
+ if (timer !== void 0) clearTimeout(timer);
30480
+ }
30481
+ }
30482
+ /** Turn a transport rejection into the error the caller must see. */
30483
+ transportFailure(error, state, request) {
30484
+ if (looksAborted(error)) {
30485
+ if (state.timedOut) return new KimiMediaUploadError("timeout", "the media upload did not finish within " + request.timeoutMs + " ms");
30486
+ return abortError();
30487
+ }
30488
+ return new KimiMediaUploadError("network", "the media upload did not complete: " + redact(error instanceof Error ? error.message : String(error), request.accessToken));
30489
+ }
30490
+ /** Read one response into a reference, or into a typed failure. */
30491
+ settle(response, raw, request) {
30492
+ const status = response.status;
30493
+ if (response.ok !== true) {
30494
+ const detail = bodyDigest(raw, request.accessToken);
30495
+ if (status === 401 || status === 403) throw new KimiMediaUploadError("unauthorized", "the Kimi Code hosted-file endpoint rejected the credential (HTTP " + status + ")", {
30496
+ status,
30497
+ detail
30498
+ });
30499
+ 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 + ")", {
30500
+ status,
30501
+ detail
30502
+ });
30503
+ if (status === 413) throw new KimiMediaUploadError("too-large", "the Kimi Code hosted-file endpoint refused the upload as too large (HTTP 413)", {
30504
+ status,
30505
+ detail
30506
+ });
30507
+ if (status >= 500) throw new KimiMediaUploadError("server", "the Kimi Code hosted-file endpoint failed (HTTP " + status + ")", {
30508
+ status,
30509
+ detail
30510
+ });
30511
+ throw new KimiMediaUploadError("rejected", "the Kimi Code hosted-file endpoint refused the upload (HTTP " + status + ")", {
30512
+ status,
30513
+ detail
30514
+ });
30515
+ }
30516
+ const parsed = parseJson(raw);
30517
+ if (!parsed.ok) throw new KimiMediaUploadError("not-json", "the media upload answered HTTP " + status + " without a JSON body", {
30518
+ status,
30519
+ detail: redact(raw, request.accessToken)
30520
+ });
30521
+ const id = readFileId$1(parsed.value);
30522
+ if (id === void 0) throw new KimiMediaUploadError("no-id", "the media upload answered HTTP " + status + " without a usable file id", {
30523
+ status,
30524
+ detail: redact(raw, request.accessToken)
30525
+ });
30526
+ const ref = fileReference(id);
30527
+ this.writeCache(request.key, ref);
30528
+ return ref;
30529
+ }
30530
+ };
30531
+ /** One uploader with the default transport and cache size. */
30532
+ function createMediaFilesUploader(options = {}) {
30533
+ return new KimiMediaFilesUploader(options);
30534
+ }
30535
+ createMediaFilesUploader();
30536
+ //#endregion
29538
30537
  //#region src/host/kimi-code/adapter.ts
29539
30538
  /**
29540
30539
  * Rebuild attempts allowed while shrinking a request into its body ceiling.
@@ -29699,6 +30698,24 @@ const ACCOUNT_LIMIT_PATTERNS = [
29699
30698
  function matchesAny$1(text, patterns) {
29700
30699
  return patterns.some((pattern) => pattern.test(text));
29701
30700
  }
30701
+ /**
30702
+ * Cache scope for the single stored credential.
30703
+ *
30704
+ * The user id the sign-in reported is the only stable identity that file has, and
30705
+ * the constant covers a credential stored before any id was recorded — or one
30706
+ * whose sign-in never answered with one. It must stay constant for an account:
30707
+ * a scope that drifted would re-upload every clip on the next turn, while a scope
30708
+ * shared between two accounts would hand one account's file id to another.
30709
+ */
30710
+ const LEGACY_ACCOUNT_SCOPE = "legacy";
30711
+ function legacyAccountScope(credentials) {
30712
+ const userId = credentials.userId?.trim();
30713
+ return userId === void 0 || userId === "" ? LEGACY_ACCOUNT_SCOPE : userId;
30714
+ }
30715
+ /** Whether one rejection is a cancellation rather than a failure to report. */
30716
+ function isAbortError(error) {
30717
+ return typeof error === "object" && error !== null && error.name === "AbortError";
30718
+ }
29702
30719
  /** Short, single-line excerpt of one error body, safe to show a user. */
29703
30720
  function summarizeFailureBody$1(raw) {
29704
30721
  const text = raw.replace(/[\r\n\t]+/g, " ").trim();
@@ -29734,6 +30751,129 @@ function isKimiBodySizeRefusal(detail) {
29734
30751
  return /\btotal message size\b[^;]{0,64}?\bexceed(?:s|ed)?\b[^;]{0,64}?\blimit\b/i.test(detail);
29735
30752
  }
29736
30753
  /**
30754
+ * The sentence the service answers with when a cited hosted file is GONE.
30755
+ *
30756
+ * Measured on this line: a chat body that cites `ms://<id>` for a file the
30757
+ * store no longer holds is refused with HTTP 404 and exactly this text. It is
30758
+ * checked as a case-insensitive substring rather than as an exact body so a JSON
30759
+ * wrapper (`{"error":{"message":"The requested resource was not found"}}`) and a
30760
+ * trailing hash match too.
30761
+ *
30762
+ * DELIBERATELY NARROW, because 404 also means "no such model" and "no such
30763
+ * route" on this endpoint, and neither of those is fixed by resending the same
30764
+ * conversation with inline media. Only this sentence, on a request that really
30765
+ * cited a reference, opens the one inline retry.
30766
+ */
30767
+ const MISSING_RESOURCE_PATTERN = /\bthe requested resource was not found\b/i;
30768
+ /** Whether one 404 body is the service refusing a cited file rather than the model. */
30769
+ function isMissingResourceBody(bodyText) {
30770
+ return MISSING_RESOURCE_PATTERN.test(bodyText);
30771
+ }
30772
+ /**
30773
+ * Body length assumed for one reference while the request is measured.
30774
+ *
30775
+ * The body is measured BEFORE anything is uploaded, so the real id does not
30776
+ * exist yet. 68 characters is longer than an id the uploaded-file route is
30777
+ * documented to mint, and it is deliberately on the long side: a measurement
30778
+ * that over-estimates sends nothing it cannot fit, while one that
30779
+ * under-estimates would discover the overflow only after the upload was already
30780
+ * paid for.
30781
+ */
30782
+ const PLANNED_MEDIA_REFERENCE = "ms://" + "0".repeat(64);
30783
+ /**
30784
+ * Which occurrences of one request would be uploaded, if any.
30785
+ *
30786
+ * Nothing is uploaded here and no network is touched: this only decides WHICH
30787
+ * readable occurrences the reference path would cover, so the body-fit guard can
30788
+ * measure the body that will actually be sent instead of measuring an inline
30789
+ * body the reference path would never produce.
30790
+ *
30791
+ * An occurrence is eligible when all four hold, and each exclusion is load
30792
+ * bearing:
30793
+ *
30794
+ * - the wire is OpenAI-compatible — the Anthropic surface documents no hosted
30795
+ * media and must upload nothing at all;
30796
+ * - the selected model declares the matching modality — sending a purpose the
30797
+ * model cannot read uploads bytes nobody will look at;
30798
+ * - the occurrence resolved to `inline`, i.e. its bytes were actually read —
30799
+ * an unreadable media has nothing to upload and keeps its placeholder;
30800
+ * - the occurrence cites a durable attachment, because only that is what a
30801
+ * reference can be keyed by.
30802
+ */
30803
+ function planMediaReferences(input) {
30804
+ if (input.wire !== "openai") return [];
30805
+ const planned = [];
30806
+ const collect = (resolved, kind) => {
30807
+ for (const [attachmentId, occurrence] of resolved) {
30808
+ if (occurrence.kind !== "inline") continue;
30809
+ const data = Buffer.from(occurrence.data, "base64");
30810
+ if (data.byteLength === 0) continue;
30811
+ planned.push({
30812
+ attachmentId,
30813
+ kind,
30814
+ mediaType: occurrence.mediaType,
30815
+ data
30816
+ });
30817
+ }
30818
+ };
30819
+ if (input.imageAccepted) collect(input.images, "image");
30820
+ if (input.videoAccepted) collect(input.videos, "video");
30821
+ return planned;
30822
+ }
30823
+ /**
30824
+ * Attachment ids one built request STILL sends as media occurrences.
30825
+ *
30826
+ * Two stages can take an occurrence away, and both run before a reference could
30827
+ * be cited:
30828
+ *
30829
+ * - the image/video budget pass replaces the oldest occurrences with a text
30830
+ * placeholder, and
30831
+ * - the body-fit loop replaces more of them when the real serialized body is over
30832
+ * the ceiling.
30833
+ *
30834
+ * An occurrence either stage removed is no longer part of the request, so
30835
+ * uploading it would pay for bytes nobody reads and mint a reference nothing
30836
+ * cites. The upload set is therefore read off the FINAL build rather than off
30837
+ * what was merely readable when the request was planned.
30838
+ *
30839
+ * Tool results nest their own content, and a `read_image` result carries its
30840
+ * pixels there, so the traversal recurses exactly the way the budget does.
30841
+ */
30842
+ function mediaAttachmentIdsIn(options) {
30843
+ const ids = /* @__PURE__ */ new Set();
30844
+ const visit = (content) => {
30845
+ if (!Array.isArray(content)) return;
30846
+ for (const block of content) {
30847
+ if (typeof block !== "object" || block === null) continue;
30848
+ const record = block;
30849
+ if (record.type === "tool-result") {
30850
+ visit(record.content);
30851
+ continue;
30852
+ }
30853
+ if (record.type !== "image" && record.type !== "video") continue;
30854
+ const id = (typeof record.attachment === "object" && record.attachment !== null ? record.attachment : void 0)?.attachmentId;
30855
+ if (typeof id === "string" && id !== "") ids.add(id);
30856
+ }
30857
+ };
30858
+ for (const message of options.messages) visit(message.content);
30859
+ return ids;
30860
+ }
30861
+ /**
30862
+ * The placeholder reference maps one planned set measures the body with.
30863
+ *
30864
+ * Returned separately for images and videos because the mapper keys them by
30865
+ * field, not by kind.
30866
+ */
30867
+ function plannedReferenceMaps(planned) {
30868
+ const imageReferences = /* @__PURE__ */ new Map();
30869
+ const videoReferences = /* @__PURE__ */ new Map();
30870
+ for (const item of planned) (item.kind === "image" ? imageReferences : videoReferences).set(item.attachmentId, PLANNED_MEDIA_REFERENCE);
30871
+ return {
30872
+ imageReferences,
30873
+ videoReferences
30874
+ };
30875
+ }
30876
+ /**
29737
30877
  * Classify one non-2xx response for the retry policy.
29738
30878
  *
29739
30879
  * @param status - HTTP status the service answered with.
@@ -29885,6 +31025,19 @@ const ACCOUNT_LIMIT_COOLDOWN_MS = 300 * 6e4;
29885
31025
  const BILLING_CYCLE_COOLDOWN_MS = 720 * 60 * 6e4;
29886
31026
  /** Ceiling, so a malformed reset time cannot park an account for years. */
29887
31027
  const MAX_ACCOUNT_LIMIT_COOLDOWN_MS = BILLING_CYCLE_COOLDOWN_MS;
31028
+ /**
31029
+ * Longest edge of the request version this route asks the attachment store for.
31030
+ *
31031
+ * Kimi's own CLI downscales an oversized image to a 2000 px long edge by default,
31032
+ * so a screenshot its client would send at 2000 px must not be handed to this
31033
+ * route at the shared 1024 px ceiling: that kept a screenshot the model could
31034
+ * read at 2000 px legible-sized only by dropping and re-reading it. The
31035
+ * encoded-byte target is deliberately NOT raised with it — 256 KiB per request
31036
+ * version still fits four images inside the shared 1.5 MB budget — and an image
31037
+ * already inside this edge is still sent as stored, so small images, references
31038
+ * without dimensions and the body-fit ceiling all behave exactly as before.
31039
+ */
31040
+ const KIMI_CODE_REQUEST_IMAGE_MAX_EDGE = 2e3;
29888
31041
  var KimiCodeAdapter = class extends LlmAdapter {
29889
31042
  store;
29890
31043
  modelSettings;
@@ -29906,6 +31059,72 @@ var KimiCodeAdapter = class extends LlmAdapter {
29906
31059
  this.options = options;
29907
31060
  this.accountPool = accountPool ?? null;
29908
31061
  }
31062
+ /**
31063
+ * The uploader this adapter cites files through, built on first use.
31064
+ *
31065
+ * Built lazily for two reasons: a turn with no media pays nothing, and a test
31066
+ * that stubs the global transport before the first request still reaches the
31067
+ * default uploader, because the transport is read at construction rather than
31068
+ * at module load.
31069
+ *
31070
+ * Deliberately NOT {@link kimiMediaFiles}, the module-level singleton: that one
31071
+ * captured the global `fetch` when this module was imported, so an adapter
31072
+ * given its own transport would upload through a different one than it chats
31073
+ * through — the one thing this route must not do, since the reference it gets
31074
+ * back is only meaningful to the endpoint the chat request goes to.
31075
+ */
31076
+ mediaUploader;
31077
+ /** The shared uploader, created on first use with THIS adapter's transport. */
31078
+ mediaFiles() {
31079
+ if (this.mediaUploader === void 0) this.mediaUploader = this.options.mediaFiles ?? createMediaFilesUploader(this.options.fetchFn === void 0 ? {} : { fetchFn: this.options.fetchFn });
31080
+ return this.mediaUploader;
31081
+ }
31082
+ /**
31083
+ * Upload every planned occurrence under ONE account and collect its references.
31084
+ *
31085
+ * The credential, base URL and scope are the ones the chat request about to be
31086
+ * sent will use, which is what makes a reference citable at all: an id minted
31087
+ * for another account or another deployment is not assumed to be readable.
31088
+ *
31089
+ * A failure is contained per occurrence. Uploading is an optimisation — the
31090
+ * inline path always works — so a refused, overloaded or unreachable upload
31091
+ * endpoint leaves THAT media inline and the turn runs. What is never
31092
+ * contained is a cancellation: `signal` aborted, or an AbortError out of the
31093
+ * transport, is a turn the caller deliberately stopped, and continuing to send
31094
+ * the chat request would answer it anyway.
31095
+ *
31096
+ * This deliberately touches no account state: a 401/403/404/405/413/5xx or a
31097
+ * network failure of the UPLOAD is a fact about the hosted-file endpoint, not
31098
+ * a verdict on this account's quota, so it neither cools nor retires the
31099
+ * account. The chat request's own classification owns that decision.
31100
+ */
31101
+ async uploadPlannedReferences(input) {
31102
+ const uploader = this.mediaFiles();
31103
+ const imageReferences = /* @__PURE__ */ new Map();
31104
+ const videoReferences = /* @__PURE__ */ new Map();
31105
+ const cited = [];
31106
+ await Promise.all(input.planned.map(async (item) => {
31107
+ try {
31108
+ const reference = await uploader.upload({
31109
+ accessToken: input.accessToken,
31110
+ baseUrl: input.baseUrl,
31111
+ accountScope: input.accountScope,
31112
+ data: item.data,
31113
+ mediaType: item.mediaType,
31114
+ signal: input.signal
31115
+ });
31116
+ (item.kind === "image" ? imageReferences : videoReferences).set(item.attachmentId, reference);
31117
+ cited.push(item);
31118
+ } catch (error) {
31119
+ if (input.signal.aborted || isAbortError(error)) throw new LlmError("Kimi Code media upload aborted", "ABORTED", { cause: error });
31120
+ }
31121
+ }));
31122
+ return {
31123
+ imageReferences,
31124
+ videoReferences,
31125
+ cited
31126
+ };
31127
+ }
29909
31128
  providerInfo(provider) {
29910
31129
  return {
29911
31130
  id: provider,
@@ -29917,7 +31136,11 @@ var KimiCodeAdapter = class extends LlmAdapter {
29917
31136
  }
29918
31137
  imageRequestPricing() {}
29919
31138
  imageRequestTarget(_provider, _model, image) {
29920
- return requestImageTargetOrOriginal(image);
31139
+ return requestImageTarget(image, 2e3) ?? {
31140
+ width: image.width,
31141
+ height: image.height,
31142
+ maxBytes: image.bytes
31143
+ };
29921
31144
  }
29922
31145
  settings() {
29923
31146
  return this.preferences ? Promise.resolve(this.preferences.status()) : this.modelSettings.read();
@@ -30028,7 +31251,7 @@ var KimiCodeAdapter = class extends LlmAdapter {
30028
31251
  const wire = wireForCatalogEntry(options.model, catalog);
30029
31252
  const entry = catalog.find((model) => model.id === options.model);
30030
31253
  const normalized = normalizeGenerateOptions(options);
30031
- const images = await resolveRequestImages$1(normalized, this.options.attachments, signal);
31254
+ const images = await resolveRequestImages$1(normalized, this.options.attachments, signal, KIMI_CODE_REQUEST_IMAGE_MAX_EDGE);
30032
31255
  const imageTrimmed = offloadOldestRequestImages$1(normalized, maxRequestImageBytes$1(), images);
30033
31256
  const gatedEffort = coerceThinkingEffort(options.model, mapReasoningEffort(options.reasoningEffort === void 0 || options.reasoningEffort === null ? void 0 : String(options.reasoningEffort)));
30034
31257
  if (gatedEffort.coercedFrom !== null) recordThinkingCoercion({
@@ -30044,11 +31267,33 @@ var KimiCodeAdapter = class extends LlmAdapter {
30044
31267
  };
30045
31268
  const videos = await resolveRequestVideos(requestOptions, this.options.videos, signal);
30046
31269
  const settings = await this.settings();
30047
- const media = {
31270
+ const modalities = inputModalitiesForEntry(options.model, catalog);
31271
+ /**
31272
+ * Everything every attempt shares: the media reads and the capability gates.
31273
+ *
31274
+ * Account-specific references are merged on top of this per attempt, so the
31275
+ * gate is applied once and a reference can never be cited for a modality the
31276
+ * model does not declare.
31277
+ */
31278
+ const baseMedia = {
30048
31279
  videos,
30049
- videoAccepted: inputModalitiesForEntry(options.model, catalog).includes("video"),
31280
+ videoAccepted: modalities.includes("video"),
30050
31281
  messageTools: dynamicToolsForEntry(options.model, catalog)
30051
31282
  };
31283
+ /**
31284
+ * Which readable occurrences the reference path would cover.
31285
+ *
31286
+ * Decided here so the body-fit guard can measure the body that will actually
31287
+ * be sent; nothing is uploaded, and no network is touched, until that guard
31288
+ * has accepted the request (see the rotation loop).
31289
+ */
31290
+ const planned = planMediaReferences({
31291
+ wire,
31292
+ imageAccepted: modalities.includes("image"),
31293
+ videoAccepted: baseMedia.videoAccepted === true,
31294
+ images,
31295
+ videos
31296
+ });
30052
31297
  const contextWindow = this.contextWindowFor(options.model, entry, settings.contextWindowOverrides);
30053
31298
  const requestedMax = options.maxTokens ?? maxOutputTokensFor$4(options.model, contextWindow);
30054
31299
  const inputCap = maxInputTokensForEntry(options.model, catalog);
@@ -30059,29 +31304,57 @@ var KimiCodeAdapter = class extends LlmAdapter {
30059
31304
  };
30060
31305
  const carriesVideo = requestHasVideo(requestOptions);
30061
31306
  const bodyLimit = carriesVideo ? MAX_VIDEO_MESSAGE_BODY_BYTES : maxRequestBodyBytesFor(kimiCodeModelDef(entry?.id ?? options.model), process.env);
30062
- let buildOptions = boundedOptions;
30063
- let body = "";
30064
- for (let attempt = 0;; attempt++) {
30065
- const built = buildRequest(buildOptions, wire, images, void 0, media);
30066
- body = JSON.stringify(built);
30067
- const bytes = Buffer.byteLength(body, "utf8");
30068
- if (bytes <= bodyLimit) break;
30069
- const context = () => ({
30070
- droppedImages: countOmitted(buildOptions, requestOptions, images),
30071
- sendableImagesRemaining: requestImagePayloadLengths(buildOptions, images).length,
30072
- excessBytes: bytes - bodyLimit
30073
- });
30074
- if (attempt > MAX_BODY_FIT_ATTEMPTS || !requestHasSendableImage(buildOptions, images)) {
30075
- assertRequestBodyFits$1(built, carriesVideo, context(), bodyLimit);
30076
- throw new Error("unreachable: assertRequestBodyFits returns or throws");
30077
- }
30078
- const drop = dropOldestImagesUntilFits(buildOptions, images, bytes - bodyLimit, requestOptions);
30079
- if (drop.dropped === 0) {
30080
- assertRequestBodyFits$1(built, carriesVideo, context(), bodyLimit);
30081
- throw new Error("unreachable: assertRequestBodyFits returns or throws");
31307
+ /**
31308
+ * Run the greedy body-fit loop for ONE set of media references.
31309
+ *
31310
+ * A function rather than a loop in place because the same loop now serves
31311
+ * every build of one request: the pre-upload MEASUREMENT (placeholder
31312
+ * references), the real reference build after the upload, and the all-inline
31313
+ * rebuild the missing-resource fallback re-sends.
31314
+ *
31315
+ * Every call restarts from `boundedOptions`. That is load bearing rather than
31316
+ * tidy: the inline fallback has to re-fit the inline body from scratch and
31317
+ * stay inside the same ceiling instead of inheriting an omission the
31318
+ * reference path made to fit a different, much smaller body.
31319
+ */
31320
+ const buildBodyWithinLimit = (media) => {
31321
+ let buildOptions = boundedOptions;
31322
+ for (let attempt = 0;; attempt++) {
31323
+ const built = buildRequest(buildOptions, wire, images, void 0, media);
31324
+ const body = JSON.stringify(built);
31325
+ const bytes = Buffer.byteLength(body, "utf8");
31326
+ if (bytes <= bodyLimit) return {
31327
+ body,
31328
+ options: buildOptions
31329
+ };
31330
+ const context = () => ({
31331
+ droppedImages: countOmitted(buildOptions, requestOptions, images),
31332
+ sendableImagesRemaining: requestImagePayloadLengths(buildOptions, images).length,
31333
+ excessBytes: bytes - bodyLimit
31334
+ });
31335
+ if (attempt > MAX_BODY_FIT_ATTEMPTS || !requestHasSendableImage(buildOptions, images)) {
31336
+ assertRequestBodyFits$1(built, carriesVideo, context(), bodyLimit);
31337
+ throw new Error("unreachable: assertRequestBodyFits returns or throws");
31338
+ }
31339
+ const drop = dropOldestImagesUntilFits(buildOptions, images, bytes - bodyLimit, requestOptions);
31340
+ if (drop.dropped === 0) {
31341
+ assertRequestBodyFits$1(built, carriesVideo, context(), bodyLimit);
31342
+ throw new Error("unreachable: assertRequestBodyFits returns or throws");
31343
+ }
31344
+ buildOptions = drop.options;
30082
31345
  }
30083
- buildOptions = drop.options;
30084
- }
31346
+ };
31347
+ /**
31348
+ * The measured build for the reference path, done at most once.
31349
+ *
31350
+ * Held across rotation attempts because it is account-independent: it decides
31351
+ * whether this request can be sent at all, and that answer cannot change
31352
+ * because a different account answered.
31353
+ *
31354
+ * The OPTIONS travel with the body because they, not the plan, name the media
31355
+ * this request still carries: see {@link mediaAttachmentIdsIn}.
31356
+ */
31357
+ let measured;
30085
31358
  const pool = this.accountPool;
30086
31359
  const tried = /* @__PURE__ */ new Set();
30087
31360
  let servedByAccountId;
@@ -30115,19 +31388,69 @@ var KimiCodeAdapter = class extends LlmAdapter {
30115
31388
  const base = (apiCredentials.baseUrl ?? codingBaseUrl(region)).replace(/\/+$/, "");
30116
31389
  const endpoint = wire === "anthropic" ? `${base}/v1/messages?beta=true` : `${base}/v1/chat/completions`;
30117
31390
  const headers = await modelRequestHeaders$1(apiCredentials.accessToken, wire);
30118
- try {
30119
- response = await fetchWithContextBudgetRecovery(fetchFn)(endpoint, {
30120
- method: "POST",
30121
- headers,
30122
- body,
30123
- signal
31391
+ /**
31392
+ * Which account's file store this attempt writes to and cites from.
31393
+ *
31394
+ * A pooled account's own id, because that is the only identity a reference
31395
+ * may be cached under; a single stored credential uses the user id its
31396
+ * sign-in reported, or a constant when it reported none. The access token is
31397
+ * never used, here or anywhere a cache key is built.
31398
+ */
31399
+ const accountScope = accountId ?? legacyAccountScope(apiCredentials);
31400
+ if (measured === void 0) measured = buildBodyWithinLimit(planned.length === 0 ? baseMedia : {
31401
+ ...baseMedia,
31402
+ ...plannedReferenceMaps(planned)
31403
+ });
31404
+ const stillSent = mediaAttachmentIdsIn(measured.options);
31405
+ const uploadable = planned.filter((item) => stillSent.has(item.attachmentId));
31406
+ const uploads = uploadable.length === 0 ? void 0 : await this.uploadPlannedReferences({
31407
+ planned: uploadable,
31408
+ accessToken: apiCredentials.accessToken,
31409
+ baseUrl: base,
31410
+ accountScope,
31411
+ signal
31412
+ });
31413
+ let body = uploads === void 0 ? measured.body : buildBodyWithinLimit({
31414
+ ...baseMedia,
31415
+ imageReferences: uploads.imageReferences,
31416
+ videoReferences: uploads.videoReferences
31417
+ }).body;
31418
+ const post = async (payload) => {
31419
+ try {
31420
+ return await fetchWithContextBudgetRecovery(fetchFn)(endpoint, {
31421
+ method: "POST",
31422
+ headers,
31423
+ body: payload,
31424
+ signal
31425
+ });
31426
+ } catch (error) {
31427
+ if (signal.aborted) throw new LlmError("Kimi Code request aborted", "ABORTED", { cause: error });
31428
+ throw new LlmError(`Kimi Code request failed: ${error instanceof Error ? error.message : String(error)}`, "TRANSPORT", { cause: error });
31429
+ }
31430
+ };
31431
+ response = await post(body);
31432
+ let detail = response.ok ? "" : (await response.text().catch(() => "")).slice(0, 2e3);
31433
+ if (response.status === 404 && uploads !== void 0 && uploads.cited.length > 0 && isMissingResourceBody(detail)) {
31434
+ const uploader = this.mediaFiles();
31435
+ for (const item of uploads.cited) uploader.forget({
31436
+ baseUrl: base,
31437
+ accountScope,
31438
+ mediaType: item.mediaType,
31439
+ data: item.data
30124
31440
  });
30125
- } catch (error) {
30126
- if (signal.aborted) throw new LlmError("Kimi Code request aborted", "ABORTED", { cause: error });
30127
- throw new LlmError(`Kimi Code request failed: ${error instanceof Error ? error.message : String(error)}`, "TRANSPORT", { cause: error });
31441
+ let inline;
31442
+ try {
31443
+ inline = buildBodyWithinLimit(baseMedia).body;
31444
+ } catch {
31445
+ inline = void 0;
31446
+ }
31447
+ if (inline !== void 0) {
31448
+ body = inline;
31449
+ response = await post(body);
31450
+ detail = response.ok ? "" : (await response.text().catch(() => "")).slice(0, 2e3);
31451
+ }
30128
31452
  }
30129
31453
  if (response.ok) break;
30130
- const detail = (await response.text().catch(() => "")).slice(0, 2e3);
30131
31454
  const failure = classifyKimiFailure(response.status, detail);
30132
31455
  const after = response.status === 429 ? retryAfterMs$1(response.headers) : void 0;
30133
31456
  if (pool !== null && accountId !== void 0) {
@@ -62232,22 +63555,27 @@ var ClinePassAdapter = class extends LlmAdapter {
62232
63555
  const pinned = injectPrefs(body, meta, attempt);
62233
63556
  let response;
62234
63557
  let transportError;
62235
- for (let transportTry = 0; transportTry <= TRANSPORT_RETRIES$1; transportTry += 1) try {
62236
- response = await this.engine.openStream(account, pinned, withAttemptTimeout$1(upstream));
62237
- break;
62238
- } catch (error) {
62239
- if (signal.aborted) throw new LlmError("Cline Pass request aborted by caller", "ABORTED", { cause: error });
62240
- transportError = error;
62241
- if (transportTry < TRANSPORT_RETRIES$1 && isPreConnectionFailure$1(error)) {
62242
- try {
62243
- await setTimeout$1(TRANSPORT_RETRY_BASE_MS$1 * (transportTry + 1), void 0, { signal: upstream });
62244
- } catch (waitError) {
62245
- if (signal.aborted) throw new LlmError("Cline Pass request aborted by caller", "ABORTED", { cause: waitError });
62246
- throw waitError;
63558
+ for (let transportTry = 0; transportTry <= TRANSPORT_RETRIES$1; transportTry += 1) {
63559
+ const headerDeadline = withAttemptTimeout$1(upstream, this.options.headerTimeoutMs);
63560
+ try {
63561
+ response = await this.engine.openStream(account, pinned, headerDeadline.signal);
63562
+ headerDeadline.clear();
63563
+ break;
63564
+ } catch (error) {
63565
+ headerDeadline.clear();
63566
+ if (signal.aborted) throw new LlmError("Cline Pass request aborted by caller", "ABORTED", { cause: error });
63567
+ transportError = error;
63568
+ if (transportTry < TRANSPORT_RETRIES$1 && isPreConnectionFailure$1(error)) {
63569
+ try {
63570
+ await setTimeout$1(TRANSPORT_RETRY_BASE_MS$1 * (transportTry + 1), void 0, { signal: upstream });
63571
+ } catch (waitError) {
63572
+ if (signal.aborted) throw new LlmError("Cline Pass request aborted by caller", "ABORTED", { cause: waitError });
63573
+ throw waitError;
63574
+ }
63575
+ continue;
62247
63576
  }
62248
- continue;
63577
+ break;
62249
63578
  }
62250
- break;
62251
63579
  }
62252
63580
  if (response === void 0) {
62253
63581
  const note = errorChain$2(transportError);
@@ -62426,9 +63754,16 @@ var ClinePassAdapter = class extends LlmAdapter {
62426
63754
  await this.accountPool.markCooldown(accountId, POOL_COOLDOWN_MS, `${PROVIDER_NAME$4} quota exhausted`).catch(() => void 0);
62427
63755
  }
62428
63756
  };
62429
- /** A caller signal combined with the attempt's header deadline. */
62430
- function withAttemptTimeout$1(signal) {
62431
- return AbortSignal.any([signal, AbortSignal.timeout(ATTEMPT_TIMEOUT_MS$1)]);
63757
+ /** A caller signal combined with a clearable response-header deadline. */
63758
+ function withAttemptTimeout$1(signal, timeoutMs = ATTEMPT_TIMEOUT_MS$1) {
63759
+ const deadline = new AbortController();
63760
+ const timer = setTimeout(() => {
63761
+ deadline.abort(new DOMException("The operation was aborted due to timeout", "TimeoutError"));
63762
+ }, timeoutMs);
63763
+ return {
63764
+ signal: AbortSignal.any([signal, deadline.signal]),
63765
+ clear: () => clearTimeout(timer)
63766
+ };
62432
63767
  }
62433
63768
  /**
62434
63769
  * Whether a transport failure happened before any request was sent.