@eddyskywalker/dsh-chatgpt-subscription 0.40.0 → 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
@@ -133,7 +133,15 @@ const OAUTH_DEVICE_GRANT_LIFETIME_MS = 15 * 6e4;
133
133
  const OAUTH_DEVICE_MIN_POLL_INTERVAL_MS = 1e3;
134
134
  const TOKEN_REFRESH_MARGIN_MS = 6e4;
135
135
  const ROUTE_PREFIX$4 = "/api/dsh-chatgpt-subscription";
136
- const PLUGIN_VERSION = "0.1.0-alpha.0";
136
+ /**
137
+ * Version this line presents to the Codex backend in its `user-agent`.
138
+ *
139
+ * It must equal `package.json`'s version: a stale value here reports a plugin
140
+ * version that no release ever shipped, and nothing else in the build notices.
141
+ * `test/responses-client.test.ts` asserts the two agree, so a release that
142
+ * forgets this constant fails the suite instead of shipping quietly.
143
+ */
144
+ const PLUGIN_VERSION = "0.40.2";
137
145
  const CODEX_CHATGPT_PROVIDER_ID = "codex-chatgpt";
138
146
  const CODEX_API_BASE = "https://chatgpt.com/backend-api/codex";
139
147
  const CODEX_RESPONSES_URL = `${CODEX_API_BASE}/responses`;
@@ -3427,7 +3435,7 @@ var OAuthService = class {
3427
3435
  throw new OAuthServiceError("oauth-token-exchange-failed", `The ChatGPT device code could not be issued (${response.status}${detail === null ? "" : `, ${detail}`}).`, response.status);
3428
3436
  }
3429
3437
  const payload = await response.json().catch(() => null);
3430
- const record = isRecord$49(payload) ? payload : {};
3438
+ const record = isRecord$50(payload) ? payload : {};
3431
3439
  const authId = nonEmptyString$10(record.device_auth_id);
3432
3440
  const userCode = nonEmptyString$10(record.user_code) ?? nonEmptyString$10(record.usercode);
3433
3441
  if (authId === void 0 || userCode === void 0) throw new OAuthServiceError("oauth-token-exchange-failed", "ChatGPT returned no device code.");
@@ -3494,7 +3502,7 @@ var OAuthService = class {
3494
3502
  const detail = deviceErrorIdentifier(payload);
3495
3503
  throw new OAuthServiceError("oauth-token-exchange-failed", `The ChatGPT device confirmation was refused (${response.status}${detail === void 0 ? "" : `, ${detail}`}).`, response.status);
3496
3504
  }
3497
- const record = isRecord$49(payload) ? payload : {};
3505
+ const record = isRecord$50(payload) ? payload : {};
3498
3506
  const authorizationCode = nonEmptyString$10(record.authorization_code);
3499
3507
  const codeVerifier = nonEmptyString$10(record.code_verifier);
3500
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.");
@@ -3841,11 +3849,11 @@ const DEVICE_PENDING_STATUSES = /* @__PURE__ */ new Set([
3841
3849
  function isPendingDeviceAnswer(response, payload) {
3842
3850
  if (response.status === 403 || response.status === 404) return true;
3843
3851
  if (!response.ok && response.status !== 400) return false;
3844
- if (!isRecord$49(payload)) return false;
3852
+ if (!isRecord$50(payload)) return false;
3845
3853
  const status = payload.status;
3846
3854
  if (typeof status === "string" && DEVICE_PENDING_STATUSES.has(status.toLowerCase())) return true;
3847
3855
  const error = payload.error;
3848
- if (isRecord$49(error)) {
3856
+ if (isRecord$50(error)) {
3849
3857
  const code = error.code;
3850
3858
  if (typeof code === "string" && DEVICE_PENDING_STATUSES.has(code.toLowerCase())) return true;
3851
3859
  }
@@ -3859,18 +3867,18 @@ function isPendingDeviceAnswer(response, payload) {
3859
3867
  * wrong.
3860
3868
  */
3861
3869
  function deviceErrorIdentifier(payload) {
3862
- if (!isRecord$49(payload)) return void 0;
3870
+ if (!isRecord$50(payload)) return void 0;
3863
3871
  const candidates = [payload.code];
3864
3872
  const error = payload.error;
3865
3873
  if (typeof error === "string") candidates.push(error);
3866
- else if (isRecord$49(error)) candidates.push(error.code, error.type);
3874
+ else if (isRecord$50(error)) candidates.push(error.code, error.type);
3867
3875
  candidates.push(payload.status);
3868
3876
  for (const candidate of candidates) if (typeof candidate === "string" && /^[a-z0-9_.-]{1,64}$/i.test(candidate)) return candidate;
3869
3877
  }
3870
3878
  function nonEmptyString$10(value) {
3871
3879
  return typeof value === "string" && value !== "" ? value : void 0;
3872
3880
  }
3873
- function isRecord$49(value) {
3881
+ function isRecord$50(value) {
3874
3882
  return typeof value === "object" && value !== null && !Array.isArray(value);
3875
3883
  }
3876
3884
  async function oauthErrorIdentifier(response) {
@@ -4053,7 +4061,7 @@ const CODEX_AUTH_CLAIMS_KEY = "https://api.openai.com/auth";
4053
4061
  * here, and the import has to be repeated afterwards.
4054
4062
  */
4055
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.";
4056
- function isRecord$48(value) {
4064
+ function isRecord$49(value) {
4057
4065
  return typeof value === "object" && value !== null && !Array.isArray(value);
4058
4066
  }
4059
4067
  /** A trimmed, non-empty string, or nothing. */
@@ -4155,7 +4163,7 @@ function decodeJwtClaims$1(token) {
4155
4163
  if (parts.length !== 3) return void 0;
4156
4164
  try {
4157
4165
  const value = JSON.parse(Buffer.from(parts[1], "base64url").toString("utf8"));
4158
- return isRecord$48(value) ? value : void 0;
4166
+ return isRecord$49(value) ? value : void 0;
4159
4167
  } catch {
4160
4168
  return;
4161
4169
  }
@@ -4200,10 +4208,10 @@ function expiryFromAccessToken(accessToken) {
4200
4208
  * the file carries cannot reach the result.
4201
4209
  */
4202
4210
  function parseAdoptedCodexCredential(value, sourcePath) {
4203
- if (!isRecord$48(value)) return void 0;
4211
+ if (!isRecord$49(value)) return void 0;
4204
4212
  if (value.auth_mode !== "chatgpt") return void 0;
4205
4213
  const tokens = value[CODEX_CLI_TOKENS_KEY];
4206
- if (!isRecord$48(tokens)) return void 0;
4214
+ if (!isRecord$49(tokens)) return void 0;
4207
4215
  const accessToken = nonEmptyString$9(tokens.access_token);
4208
4216
  if (accessToken === void 0) return void 0;
4209
4217
  const refreshToken = nonEmptyString$9(tokens.refresh_token);
@@ -4222,7 +4230,7 @@ function parseAdoptedCodexCredential(value, sourcePath) {
4222
4230
  const email = claims === void 0 ? void 0 : nonEmptyString$9(claims.email);
4223
4231
  if (email !== void 0) credentials.email = email;
4224
4232
  const authClaims = claims === void 0 ? void 0 : claims[CODEX_AUTH_CLAIMS_KEY];
4225
- 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;
4226
4234
  if (planType !== void 0) credentials.planType = planType;
4227
4235
  }
4228
4236
  const accountId = nonEmptyString$9(tokens.account_id);
@@ -4991,13 +4999,13 @@ function webServerOrigin(server) {
4991
4999
  //#region src/host/common/llm-compat.ts
4992
5000
  /** This package's kind in the harness message-source map. */
4993
5001
  const PLUGIN_MESSAGE_SOURCE_KIND = "dsh-chatgpt-subscription";
4994
- function isRecord$47(value) {
5002
+ function isRecord$48(value) {
4995
5003
  return typeof value === "object" && value !== null && !Array.isArray(value);
4996
5004
  }
4997
5005
  /** Read a message body's blocks, keeping every entry that carries a block tag. */
4998
5006
  function readBlocks(raw) {
4999
5007
  if (!Array.isArray(raw)) return [];
5000
- return raw.filter((block) => isRecord$47(block) && typeof block.type === "string");
5008
+ return raw.filter((block) => isRecord$48(block) && typeof block.type === "string");
5001
5009
  }
5002
5010
  /**
5003
5011
  * Normalize one harness message list into {@link Message}.
@@ -5012,10 +5020,10 @@ function readBlocks(raw) {
5012
5020
  function normalizeMessages(messages) {
5013
5021
  const normalized = [];
5014
5022
  for (const raw of messages ?? []) {
5015
- if (!isRecord$47(raw)) continue;
5023
+ if (!isRecord$48(raw)) continue;
5016
5024
  const content = readBlocks(raw.content);
5017
5025
  if (raw.role === "tool") {
5018
- const source = isRecord$47(raw.source) ? raw.source : void 0;
5026
+ const source = isRecord$48(raw.source) ? raw.source : void 0;
5019
5027
  const callId = String(raw.toolCallId ?? source?.callId ?? "");
5020
5028
  normalized.push({
5021
5029
  id: raw.id,
@@ -5037,7 +5045,7 @@ function normalizeMessages(messages) {
5037
5045
  id: raw.id,
5038
5046
  role: typeof raw.role === "string" ? raw.role : "user",
5039
5047
  content,
5040
- ...isRecord$47(raw.source) ? { source: raw.source } : {}
5048
+ ...isRecord$48(raw.source) ? { source: raw.source } : {}
5041
5049
  });
5042
5050
  }
5043
5051
  return normalized;
@@ -6741,7 +6749,7 @@ const schemaFields = /* @__PURE__ */ new Set([
6741
6749
  "required",
6742
6750
  "propertyOrdering"
6743
6751
  ]);
6744
- function isRecord$46(value) {
6752
+ function isRecord$47(value) {
6745
6753
  return typeof value === "object" && value !== null && !Array.isArray(value);
6746
6754
  }
6747
6755
  function mergeSchemas(left, right) {
@@ -6749,7 +6757,7 @@ function mergeSchemas(left, right) {
6749
6757
  ...left,
6750
6758
  ...right
6751
6759
  };
6752
- if (isRecord$46(left.properties) && isRecord$46(right.properties)) merged.properties = {
6760
+ if (isRecord$47(left.properties) && isRecord$47(right.properties)) merged.properties = {
6753
6761
  ...left.properties,
6754
6762
  ...right.properties
6755
6763
  };
@@ -6785,9 +6793,9 @@ const mergedTypeKeys = /* @__PURE__ */ new Set([
6785
6793
  * model with strictly less than the schema it started from.
6786
6794
  */
6787
6795
  function describeBranch(branch) {
6788
- 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";
6789
6797
  const details = [];
6790
- if (isRecord$46(branch.properties)) {
6798
+ if (isRecord$47(branch.properties)) {
6791
6799
  const names = Object.keys(branch.properties);
6792
6800
  if (names.length > 0) details.push(`{${names.join(",")}}`);
6793
6801
  }
@@ -6801,7 +6809,7 @@ function describeBranch(branch) {
6801
6809
  }
6802
6810
  /** How well one branch can stand in for a whole union; a named object shape wins. */
6803
6811
  function branchRank(branch) {
6804
- if (isRecord$46(branch.properties)) return 3;
6812
+ if (isRecord$47(branch.properties)) return 3;
6805
6813
  if (branch.type === "object") return 2;
6806
6814
  if (branch.type === "array") return 1;
6807
6815
  return 0;
@@ -6830,7 +6838,7 @@ function unionShape(branches) {
6830
6838
  shape: branches[0],
6831
6839
  folded: false
6832
6840
  };
6833
- 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))) {
6834
6842
  const shape = { type: branches[0].type };
6835
6843
  if (branches.every((branch) => Array.isArray(branch.enum))) {
6836
6844
  const values = [...new Set(branches.flatMap((branch) => branch.enum))];
@@ -6867,7 +6875,7 @@ function foldOwnFields(node) {
6867
6875
  const out = {};
6868
6876
  for (const [key, value] of Object.entries(node)) {
6869
6877
  if (key === "anyOf" || key === "oneOf" || key === "allOf") continue;
6870
- if (key === "properties" && isRecord$46(value)) {
6878
+ if (key === "properties" && isRecord$47(value)) {
6871
6879
  out.properties = Object.fromEntries(Object.entries(value).map(([name, child]) => [name, foldSchemaUnions(child)]));
6872
6880
  continue;
6873
6881
  }
@@ -6896,15 +6904,15 @@ function foldOwnFields(node) {
6896
6904
  * argument the model sends anyway fails there, naming the actual problem.
6897
6905
  */
6898
6906
  function foldSchemaUnions(node) {
6899
- if (!isRecord$46(node)) return node;
6907
+ if (!isRecord$47(node)) return node;
6900
6908
  let own = foldOwnFields(node);
6901
6909
  if (Array.isArray(node.allOf)) for (const branch of node.allOf) {
6902
6910
  const folded = foldSchemaUnions(branch);
6903
- if (isRecord$46(folded)) own = mergeSchemas(own, folded);
6911
+ if (isRecord$47(folded)) own = mergeSchemas(own, folded);
6904
6912
  }
6905
6913
  const alternatives = Array.isArray(node.anyOf) ? node.anyOf : Array.isArray(node.oneOf) ? node.oneOf : [];
6906
6914
  if (alternatives.length === 0) return own;
6907
- const branches = alternatives.map((branch) => foldSchemaUnions(branch)).filter(isRecord$46);
6915
+ const branches = alternatives.map((branch) => foldSchemaUnions(branch)).filter(isRecord$47);
6908
6916
  if (branches.length === 0) return own;
6909
6917
  const shapes = branches.filter((branch) => branch.type !== "null");
6910
6918
  const acceptsNull = branches.some((branch) => branch.type === "null");
@@ -6920,7 +6928,7 @@ function foldSchemaUnions(node) {
6920
6928
  return merged;
6921
6929
  }
6922
6930
  function toAntigravityToolSchema(schema, options = {}) {
6923
- if (!isRecord$46(schema)) return schema;
6931
+ if (!isRecord$47(schema)) return schema;
6924
6932
  const root = schema;
6925
6933
  function resolveReference(reference) {
6926
6934
  let target = root;
@@ -6928,13 +6936,13 @@ function toAntigravityToolSchema(schema, options = {}) {
6928
6936
  const path = reference === "#" ? [] : reference.slice(2).split("/");
6929
6937
  for (const segment of path) {
6930
6938
  const key = segment.replace(/~1/g, "/").replace(/~0/g, "~");
6931
- 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;
6932
6940
  }
6933
- 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");
6934
6942
  return target;
6935
6943
  }
6936
6944
  function convert(node, references) {
6937
- if (!isRecord$46(node)) return {};
6945
+ if (!isRecord$47(node)) return {};
6938
6946
  let inherited = {};
6939
6947
  if (typeof node.$ref === "string") {
6940
6948
  if (references.has(node.$ref)) throw new LlmError(`Antigravity tool schema contains a recursive reference: ${node.$ref}`, "PROVIDER_ERROR");
@@ -6943,8 +6951,8 @@ function toAntigravityToolSchema(schema, options = {}) {
6943
6951
  if (Array.isArray(node.allOf)) for (const branch of node.allOf) inherited = mergeSchemas(inherited, convert(branch, references));
6944
6952
  const out = {};
6945
6953
  for (const [key, value] of Object.entries(node)) if (schemaFields.has(key)) out[key] = value;
6946
- if (isRecord$46(node.properties)) out.properties = Object.fromEntries(Object.entries(node.properties).map(([name, child]) => [name, convert(child, references)]));
6947
- 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);
6948
6956
  const alternatives = Array.isArray(node.anyOf) ? node.anyOf : node.oneOf;
6949
6957
  if (Array.isArray(alternatives)) out.anyOf = alternatives.map((child) => convert(child, references));
6950
6958
  if (Array.isArray(node.type)) {
@@ -6980,7 +6988,7 @@ let toolCallCounter = 0;
6980
6988
  function sanitizeText$8(text) {
6981
6989
  return text.replace(/\0/g, "");
6982
6990
  }
6983
- function isRecord$45(value) {
6991
+ function isRecord$46(value) {
6984
6992
  return typeof value === "object" && value !== null && !Array.isArray(value);
6985
6993
  }
6986
6994
  function asString$24(value) {
@@ -7000,25 +7008,25 @@ function toolCallIdNeeded(modelId, runtimeModel) {
7000
7008
  return modelId.startsWith("claude-") || modelId.startsWith("gpt-oss-") || runtimeModel.startsWith("claude-") || runtimeModel.startsWith("gpt-oss-");
7001
7009
  }
7002
7010
  function parseArguments$2(raw) {
7003
- if (isRecord$45(raw)) return raw;
7011
+ if (isRecord$46(raw)) return raw;
7004
7012
  if (raw === void 0 || raw === null || raw === "") return {};
7005
7013
  const parsed = typeof raw === "string" ? safeJsonParse$8(raw) : raw;
7006
- return isRecord$45(parsed) ? parsed : {};
7014
+ return isRecord$46(parsed) ? parsed : {};
7007
7015
  }
7008
7016
  const NO_RESOLVED_IMAGES$5 = /* @__PURE__ */ new Map();
7009
7017
  function attachmentOf$5(block) {
7010
7018
  const attachment = block.attachment;
7011
- if (!isRecord$45(attachment)) return void 0;
7019
+ if (!isRecord$46(attachment)) return void 0;
7012
7020
  return typeof attachment.attachmentId === "string" ? attachment : void 0;
7013
7021
  }
7014
7022
  function attachmentLabel$6(block) {
7015
- const attachment = isRecord$45(block.attachment) ? block.attachment : void 0;
7023
+ const attachment = isRecord$46(block.attachment) ? block.attachment : void 0;
7016
7024
  return asString$24(attachment?.name) || asString$24(attachment?.attachmentId);
7017
7025
  }
7018
7026
  function collectImageRefs$3(content, refs) {
7019
7027
  if (!Array.isArray(content)) return;
7020
7028
  for (const block of content) {
7021
- if (!isRecord$45(block)) continue;
7029
+ if (!isRecord$46(block)) continue;
7022
7030
  if (block.type === "image") {
7023
7031
  const attachment = attachmentOf$5(block);
7024
7032
  if (attachment) refs.set(attachment.attachmentId, attachment);
@@ -7131,7 +7139,7 @@ function requestImageBytes$2(block) {
7131
7139
  function collectRequestImageBytes$2(content, lengths) {
7132
7140
  if (!Array.isArray(content)) return;
7133
7141
  for (const block of content) {
7134
- if (!isRecord$45(block)) continue;
7142
+ if (!isRecord$46(block)) continue;
7135
7143
  if (block.type === "tool-result") {
7136
7144
  collectRequestImageBytes$2(block.content, lengths);
7137
7145
  continue;
@@ -7181,7 +7189,7 @@ function offloadOldestRequestImages$3(options) {
7181
7189
  let replaced = false;
7182
7190
  return {
7183
7191
  content: content.map((block) => {
7184
- if (remaining.count === 0 || !isRecord$45(block)) return block;
7192
+ if (remaining.count === 0 || !isRecord$46(block)) return block;
7185
7193
  if (block.type === "image") {
7186
7194
  if (requestImageBytes$2(block) === void 0) return block;
7187
7195
  remaining.count -= 1;
@@ -7284,7 +7292,7 @@ function unavailableImageText$6(block) {
7284
7292
  }
7285
7293
  function imageBlockToPart(block, images) {
7286
7294
  let data = asString$24(block.data) || asString$24(block.base64);
7287
- const source = isRecord$45(block.source) ? block.source : void 0;
7295
+ const source = isRecord$46(block.source) ? block.source : void 0;
7288
7296
  if (!data && source) data = asString$24(source.data) || asString$24(source.base64);
7289
7297
  let mimeType = asString$24(block.mimeType) || asString$24(block.mediaType) || (source ? asString$24(source.mimeType) || asString$24(source.mediaType) : void 0) || "image/png";
7290
7298
  if (data?.startsWith("data:")) {
@@ -7309,8 +7317,8 @@ function contentToUserParts(content, images) {
7309
7317
  if (typeof content === "string") return [{ text: sanitizeText$8(content) }];
7310
7318
  if (!Array.isArray(content)) return [];
7311
7319
  const parts = [];
7312
- for (const block of content) if (isRecord$45(block) && block.type === "text" && typeof block.text === "string") parts.push({ text: sanitizeText$8(block.text) });
7313
- 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") {
7314
7322
  const img = imageBlockToPart(block, images);
7315
7323
  parts.push(img ?? { text: unavailableImageText$6(block) });
7316
7324
  }
@@ -7325,7 +7333,7 @@ function toolResultImageParts(blocks, images) {
7325
7333
  if (!Array.isArray(blocks)) return [];
7326
7334
  const parts = [];
7327
7335
  for (const block of blocks) {
7328
- if (!isRecord$45(block)) continue;
7336
+ if (!isRecord$46(block)) continue;
7329
7337
  if (block.type === "image") {
7330
7338
  parts.push(imageBlockToPart(block, images) ?? { text: unavailableImageText$6(block) });
7331
7339
  continue;
@@ -7337,7 +7345,7 @@ function toolResultImageParts(blocks, images) {
7337
7345
  function toolResultText$8(blocks) {
7338
7346
  if (!Array.isArray(blocks)) return "";
7339
7347
  return blocks.map((block) => {
7340
- if (!isRecord$45(block)) return "";
7348
+ if (!isRecord$46(block)) return "";
7341
7349
  if (block.type === "text" && typeof block.text === "string") return sanitizeText$8(block.text);
7342
7350
  if (block.type === "tool-result") return toolResultText$8(block.content);
7343
7351
  if (block.type === "image") {
@@ -7351,9 +7359,9 @@ function replayBlockFor$1(message, index) {
7351
7359
  const source = message.source;
7352
7360
  if (!source || source.kind !== "model" || source.provider !== "antigravity") return void 0;
7353
7361
  const state = source.replayState;
7354
- if (!isRecord$45(state)) return void 0;
7362
+ if (!isRecord$46(state)) return void 0;
7355
7363
  if (Array.isArray(state.blocks)) return state.blocks[index];
7356
- const resp = isRecord$45(state.response) ? state.response : void 0;
7364
+ const resp = isRecord$46(state.response) ? state.response : void 0;
7357
7365
  if (resp) {
7358
7366
  if (Array.isArray(resp.outputItems)) return resp.outputItems[index];
7359
7367
  if (Array.isArray(resp.blocks)) return resp.blocks[index];
@@ -7455,10 +7463,10 @@ function assistantParts(message, model, runtimeModel, toolCalls) {
7455
7463
  if (!Array.isArray(message.content)) return parts;
7456
7464
  for (let index = 0; index < message.content.length; index++) {
7457
7465
  const block = message.content[index];
7458
- if (!isRecord$45(block)) continue;
7466
+ if (!isRecord$46(block)) continue;
7459
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;
7460
7468
  const replay = replayBlockFor$1(message, index);
7461
- const originalParts = Array.isArray(replay?.parts) ? replay.parts.filter(isRecord$45) : [];
7469
+ const originalParts = Array.isArray(replay?.parts) ? replay.parts.filter(isRecord$46) : [];
7462
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 || ""))) {
7463
7471
  parts.push(...originalParts.map(replayPart));
7464
7472
  continue;
@@ -7479,8 +7487,8 @@ function assistantParts(message, model, runtimeModel, toolCalls) {
7479
7487
  } else if (block.type === "tool-call") {
7480
7488
  const toolId = String(block.id || "");
7481
7489
  const toolName = String(block.name || "");
7482
- const originalCall = originalParts.find((part) => isRecord$45(part.functionCall));
7483
- 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);
7484
7492
  toolCalls.set(toolId, {
7485
7493
  name: toolName,
7486
7494
  id: wireId
@@ -7532,7 +7540,7 @@ function convertMessages(options, model, runtimeModel, images = NO_RESOLVED_IMAG
7532
7540
  continue;
7533
7541
  }
7534
7542
  const content = Array.isArray(message.content) ? message.content : [];
7535
- 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);
7536
7544
  if (role === "system") {
7537
7545
  if (userParts.length) contents.push({
7538
7546
  role: GEMINI_ROLE.user,
@@ -7544,7 +7552,7 @@ function convertMessages(options, model, runtimeModel, images = NO_RESOLVED_IMAG
7544
7552
  role: GEMINI_ROLE.user,
7545
7553
  parts: userParts
7546
7554
  });
7547
- 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);
7548
7556
  }
7549
7557
  return contents;
7550
7558
  }
@@ -7692,7 +7700,7 @@ const USAGE_FIELDS = [
7692
7700
  "totalTokenCount"
7693
7701
  ];
7694
7702
  function collectUsage(value, state) {
7695
- if (!isRecord$45(value)) return;
7703
+ if (!isRecord$46(value)) return;
7696
7704
  for (const key of USAGE_FIELDS) {
7697
7705
  const count = value[key];
7698
7706
  if (typeof count !== "number" || !Number.isSafeInteger(count) || count < 0) continue;
@@ -7722,17 +7730,17 @@ function processStreamLine$3(line, state) {
7722
7730
  }
7723
7731
  if (!json) return [];
7724
7732
  const chunk = safeJsonParse$8(json);
7725
- if (!isRecord$45(chunk)) return [];
7726
- const responseData = isRecord$45(chunk.response) ? chunk.response : chunk;
7727
- 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;
7728
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");
7729
7737
  const candidates = Array.isArray(responseData.candidates) ? responseData.candidates : [];
7730
- const candidate = isRecord$45(candidates[0]) ? candidates[0] : void 0;
7731
- 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;
7732
7740
  const parts = Array.isArray(content?.parts) ? content.parts : [];
7733
7741
  const out = [];
7734
7742
  for (const part of parts) {
7735
- if (!isRecord$45(part)) continue;
7743
+ if (!isRecord$46(part)) continue;
7736
7744
  if (typeof part.text === "string" && part.text !== "") {
7737
7745
  const isThinking = Boolean(part.thought);
7738
7746
  const blockType = isThinking ? "reasoning" : "text";
@@ -7764,7 +7772,7 @@ function processStreamLine$3(line, state) {
7764
7772
  index: state.currentBlock.index,
7765
7773
  text: delta
7766
7774
  });
7767
- } else if (!isRecord$45(part.functionCall) && thoughtSignature(part)) {
7775
+ } else if (!isRecord$46(part.functionCall) && thoughtSignature(part)) {
7768
7776
  if (state.replayBlocks.length === 0) {
7769
7777
  const type = part.thought ? "reasoning" : "text";
7770
7778
  state.blocks.push({
@@ -7788,12 +7796,12 @@ function processStreamLine$3(line, state) {
7788
7796
  }
7789
7797
  state.replayBlocks[state.replayBlocks.length - 1].parts.push(replayPart(part));
7790
7798
  }
7791
- if (isRecord$45(part.functionCall)) {
7799
+ if (isRecord$46(part.functionCall)) {
7792
7800
  out.push(...closeCurrentBlock(state));
7793
7801
  const fc = part.functionCall;
7794
7802
  const toolName = asString$24(fc.name) || "";
7795
7803
  const toolId = asString$24(fc.id) || sanitizeToolCallId("", toolName);
7796
- const argsText = JSON.stringify(isRecord$45(fc.args) ? fc.args : {});
7804
+ const argsText = JSON.stringify(isRecord$46(fc.args) ? fc.args : {});
7797
7805
  const index = state.blocks.length;
7798
7806
  const block = {
7799
7807
  type: "tool-call",
@@ -7832,7 +7840,7 @@ function processStreamLine$3(line, state) {
7832
7840
  if (responseData !== chunk) collectUsage(responseData.usageMetadata, state);
7833
7841
  const finishMessage = asString$24(candidate?.finishMessage);
7834
7842
  if (finishMessage) state.finishMessage = finishMessage;
7835
- 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);
7836
7844
  const finishReason = asString$24(candidate?.finishReason) || asString$24(responseData.finishReason) || (blockReason ? `PROMPT_BLOCKED:${blockReason}` : void 0);
7837
7845
  if (finishReason) {
7838
7846
  state.finishReason = finishReason;
@@ -7875,7 +7883,7 @@ function finishReasonFor$8(state) {
7875
7883
  }
7876
7884
  function closeStream$6(state) {
7877
7885
  if (state.finished) return [];
7878
- 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");
7879
7887
  state.finished = true;
7880
7888
  const out = closeCurrentBlock(state);
7881
7889
  if (state.usageMetadata) out.push({
@@ -8594,6 +8602,36 @@ function withoutValidatedToolConfig(body) {
8594
8602
  delete parsed.request.toolConfig;
8595
8603
  return JSON.stringify(parsed);
8596
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
+ }
8597
8635
  var AntigravityAdapter = class extends LlmAdapter {
8598
8636
  store;
8599
8637
  modelSettings;
@@ -8860,7 +8898,9 @@ var AntigravityAdapter = class extends LlmAdapter {
8860
8898
  let buffer = "";
8861
8899
  try {
8862
8900
  while (true) {
8863
- const { done, value } = await reader.read();
8901
+ const { done, value } = await reader.read().catch((error) => {
8902
+ throw streamFailure$2(error, signal);
8903
+ });
8864
8904
  if (done) break;
8865
8905
  if (value !== void 0 && value.byteLength > 0) activity?.pulse();
8866
8906
  buffer += decoder.decode(value, { stream: true });
@@ -13816,7 +13856,7 @@ async function compactionCandidates(ctx) {
13816
13856
  };
13817
13857
  }))).filter((provider) => provider.models.length > 0) };
13818
13858
  }
13819
- function isRecord$44(value) {
13859
+ function isRecord$45(value) {
13820
13860
  return typeof value === "object" && value !== null && !Array.isArray(value);
13821
13861
  }
13822
13862
  function readPreferencesUpdate(value, current) {
@@ -13857,7 +13897,7 @@ function readPreferencesUpdate(value, current) {
13857
13897
  };
13858
13898
  }
13859
13899
  if ("contextWindowOverrides" in value) {
13860
- 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.");
13861
13901
  const overrides = {};
13862
13902
  for (const [model, contextWindow] of Object.entries(value.contextWindowOverrides)) {
13863
13903
  if (!isCodexModelId(model)) throw new PreferenceError("Unknown Codex model for a context window override.");
@@ -18357,11 +18397,11 @@ function commandCodeHeaders(apiKey, extra = {}) {
18357
18397
  function timeoutSignal$4(signal, ms) {
18358
18398
  return signal ? AbortSignal.any([signal, AbortSignal.timeout(ms)]) : AbortSignal.timeout(ms);
18359
18399
  }
18360
- function isRecord$43(value) {
18400
+ function isRecord$44(value) {
18361
18401
  return typeof value === "object" && value !== null && !Array.isArray(value);
18362
18402
  }
18363
18403
  function asRecord$15(value) {
18364
- return isRecord$43(value) ? value : void 0;
18404
+ return isRecord$44(value) ? value : void 0;
18365
18405
  }
18366
18406
  function asString$23(value) {
18367
18407
  if (typeof value === "string" && value.trim() !== "") return value;
@@ -18374,7 +18414,7 @@ function asNumber$5(value) {
18374
18414
  if (Number.isFinite(parsed)) return parsed;
18375
18415
  }
18376
18416
  }
18377
- function firstString$1(record, keys) {
18417
+ function firstString$2(record, keys) {
18378
18418
  for (const key of keys) {
18379
18419
  const value = asString$23(record[key]);
18380
18420
  if (value !== void 0) return value;
@@ -18405,7 +18445,7 @@ function deepValue(value, keys, depth = 0) {
18405
18445
  }
18406
18446
  return;
18407
18447
  }
18408
- if (!isRecord$43(value)) return void 0;
18448
+ if (!isRecord$44(value)) return void 0;
18409
18449
  for (const key of keys) if (value[key] !== void 0 && value[key] !== null) return value[key];
18410
18450
  for (const nested of Object.values(value)) {
18411
18451
  const hit = deepValue(nested, keys, depth + 1);
@@ -18419,7 +18459,7 @@ function collectRecords(value, keys, depth = 0, out = []) {
18419
18459
  for (const entry of value) collectRecords(entry, keys, depth + 1, out);
18420
18460
  return out;
18421
18461
  }
18422
- if (!isRecord$43(value)) return out;
18462
+ if (!isRecord$44(value)) return out;
18423
18463
  if (keys.some((key) => value[key] !== void 0)) out.push(value);
18424
18464
  for (const nested of Object.values(value)) collectRecords(nested, keys, depth + 1, out);
18425
18465
  return out;
@@ -18453,38 +18493,38 @@ function parseWhoami(payload, fallback = {}) {
18453
18493
  "plan",
18454
18494
  "tier"
18455
18495
  ])) ?? {};
18456
- 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;
18457
18497
  return {
18458
- userId: firstString$1(user, [
18498
+ userId: firstString$2(user, [
18459
18499
  "id",
18460
18500
  "userId",
18461
18501
  "uid"
18462
- ]) ?? firstString$1(root, ["userId"]) ?? fallback.userId ?? null,
18463
- userName: firstString$1(user, [
18502
+ ]) ?? firstString$2(root, ["userId"]) ?? fallback.userId ?? null,
18503
+ userName: firstString$2(user, [
18464
18504
  "userName",
18465
18505
  "username",
18466
18506
  "name",
18467
18507
  "displayName",
18468
18508
  "fullName"
18469
- ]) ?? firstString$1(root, ["userName"]) ?? fallback.userName ?? null,
18509
+ ]) ?? firstString$2(root, ["userName"]) ?? fallback.userName ?? null,
18470
18510
  email: email ?? null,
18471
- organizationName: firstString$1(org, [
18511
+ organizationName: firstString$2(org, [
18472
18512
  "name",
18473
18513
  "displayName",
18474
18514
  "slug"
18475
18515
  ]) ?? fallback.organizationName ?? null,
18476
- keyName: firstString$1(key, [
18516
+ keyName: firstString$2(key, [
18477
18517
  "name",
18478
18518
  "keyName",
18479
18519
  "label"
18480
18520
  ]) ?? fallback.keyName ?? null,
18481
- planLabel: firstString$1(subscription, [
18521
+ planLabel: firstString$2(subscription, [
18482
18522
  "name",
18483
18523
  "displayName",
18484
18524
  "label",
18485
18525
  "planName"
18486
- ]) ?? firstString$1(data, ["planName", "planLabel"]) ?? fallback.planLabel ?? null,
18487
- planId: firstString$1(subscription, [
18526
+ ]) ?? firstString$2(data, ["planName", "planLabel"]) ?? fallback.planLabel ?? null,
18527
+ planId: firstString$2(subscription, [
18488
18528
  "id",
18489
18529
  "planId",
18490
18530
  "slug",
@@ -18718,7 +18758,7 @@ function sweepMeters(payload, skipIds, consumed) {
18718
18758
  usedFraction = Math.max(0, 1 - remainingFraction);
18719
18759
  }
18720
18760
  if (usedFraction === null && remainingFraction === null && limit === void 0 && balance === void 0) continue;
18721
- const rawId = firstString$1(record, [
18761
+ const rawId = firstString$2(record, [
18722
18762
  "id",
18723
18763
  "bucketId",
18724
18764
  "bucket_id",
@@ -18727,7 +18767,7 @@ function sweepMeters(payload, skipIds, consumed) {
18727
18767
  "type",
18728
18768
  "key"
18729
18769
  ]);
18730
- const label = firstString$1(record, [
18770
+ const label = firstString$2(record, [
18731
18771
  "displayName",
18732
18772
  "display_name",
18733
18773
  "label",
@@ -18757,7 +18797,7 @@ function sweepMeters(payload, skipIds, consumed) {
18757
18797
  "periodEnd",
18758
18798
  "renewalDate"
18759
18799
  ])),
18760
- description: firstString$1(record, [
18800
+ description: firstString$2(record, [
18761
18801
  "description",
18762
18802
  "detail",
18763
18803
  "subtitle"
@@ -18828,7 +18868,7 @@ function parseUsageWindows$1(payload) {
18828
18868
  "percentUsed"
18829
18869
  ]);
18830
18870
  if (percent === void 0) continue;
18831
- const id = firstString$1(record, [
18871
+ const id = firstString$2(record, [
18832
18872
  "id",
18833
18873
  "bucketId",
18834
18874
  "name",
@@ -18840,7 +18880,7 @@ function parseUsageWindows$1(payload) {
18840
18880
  seen.add(id);
18841
18881
  windows.push({
18842
18882
  id,
18843
- label: firstString$1(record, [
18883
+ label: firstString$2(record, [
18844
18884
  "displayName",
18845
18885
  "label",
18846
18886
  "name",
@@ -18916,7 +18956,7 @@ function parseProviderModels(payload) {
18916
18956
  for (const entry of list) {
18917
18957
  const record = asRecord$15(entry);
18918
18958
  if (!record) continue;
18919
- const id = firstString$1(record, [
18959
+ const id = firstString$2(record, [
18920
18960
  "id",
18921
18961
  "model",
18922
18962
  "name"
@@ -18924,7 +18964,7 @@ function parseProviderModels(payload) {
18924
18964
  if (id === void 0) continue;
18925
18965
  models.push({
18926
18966
  id,
18927
- name: firstString$1(record, ["displayName", "name"]) ?? id,
18967
+ name: firstString$2(record, ["displayName", "name"]) ?? id,
18928
18968
  contextWindow: firstNumber$2(record, [
18929
18969
  "context_length",
18930
18970
  "contextLength",
@@ -19229,7 +19269,7 @@ function buildModelOptions$1(catalog, enabledModelIds, overrides) {
19229
19269
  *
19230
19270
  * https://commandcode.ai/blog/command-code-provider-api
19231
19271
  */
19232
- function isRecord$42(value) {
19272
+ function isRecord$43(value) {
19233
19273
  return typeof value === "object" && value !== null && !Array.isArray(value);
19234
19274
  }
19235
19275
  function asString$22(value) {
@@ -19269,17 +19309,17 @@ const SUPPORTED_IMAGE_MEDIA_TYPES$4 = /* @__PURE__ */ new Set([
19269
19309
  ]);
19270
19310
  function attachmentOf$4(block) {
19271
19311
  const attachment = block.attachment;
19272
- if (!isRecord$42(attachment)) return void 0;
19312
+ if (!isRecord$43(attachment)) return void 0;
19273
19313
  return typeof attachment.attachmentId === "string" ? attachment : void 0;
19274
19314
  }
19275
19315
  function attachmentLabel$5(block) {
19276
- const attachment = isRecord$42(block.attachment) ? block.attachment : void 0;
19316
+ const attachment = isRecord$43(block.attachment) ? block.attachment : void 0;
19277
19317
  return asString$22(attachment?.name) || asString$22(attachment?.attachmentId);
19278
19318
  }
19279
19319
  function collectImageRefs$2(content, refs) {
19280
19320
  if (!Array.isArray(content)) return;
19281
19321
  for (const block of content) {
19282
- if (!isRecord$42(block)) continue;
19322
+ if (!isRecord$43(block)) continue;
19283
19323
  if (block.type === "image") {
19284
19324
  const attachment = attachmentOf$4(block);
19285
19325
  if (attachment) refs.set(attachment.attachmentId, attachment);
@@ -19300,7 +19340,7 @@ function requestImageBytes$1(block) {
19300
19340
  function collectRequestImageBytes$1(content, lengths) {
19301
19341
  if (!Array.isArray(content)) return;
19302
19342
  for (const block of content) {
19303
- if (!isRecord$42(block)) continue;
19343
+ if (!isRecord$43(block)) continue;
19304
19344
  if (block.type === "tool-result") {
19305
19345
  collectRequestImageBytes$1(block.content, lengths);
19306
19346
  continue;
@@ -19336,7 +19376,7 @@ function offloadOldestRequestImages$2(options) {
19336
19376
  let replaced = false;
19337
19377
  return {
19338
19378
  content: content.map((block) => {
19339
- if (remaining.count === 0 || !isRecord$42(block)) return block;
19379
+ if (remaining.count === 0 || !isRecord$43(block)) return block;
19340
19380
  if (block.type === "image") {
19341
19381
  if (requestImageBytes$1(block) === void 0) return block;
19342
19382
  remaining.count -= 1;
@@ -19408,7 +19448,7 @@ function unavailableImageText$5(block) {
19408
19448
  }
19409
19449
  function imageBlockToInline$3(block, images) {
19410
19450
  let data = asString$22(block.data) || asString$22(block.base64);
19411
- const source = isRecord$42(block.source) ? block.source : void 0;
19451
+ const source = isRecord$43(block.source) ? block.source : void 0;
19412
19452
  if (!data && source) data = asString$22(source.data) || asString$22(source.base64);
19413
19453
  let mediaType = asString$22(block.mimeType) || asString$22(block.mediaType) || (source ? asString$22(source.mimeType) || asString$22(source.mediaType) : void 0) || "image/png";
19414
19454
  if (data?.startsWith("data:")) {
@@ -19434,7 +19474,7 @@ function textOf$8(content) {
19434
19474
  if (!Array.isArray(content)) return "";
19435
19475
  const parts = [];
19436
19476
  for (const block of content) {
19437
- if (!isRecord$42(block)) continue;
19477
+ if (!isRecord$43(block)) continue;
19438
19478
  if (block.type === "text" && typeof block.text === "string") parts.push(sanitizeText$7(block.text));
19439
19479
  else if (block.type === "tool-result") parts.push(textOf$8(block.content));
19440
19480
  }
@@ -19443,7 +19483,7 @@ function textOf$8(content) {
19443
19483
  function toolResultText$7(blocks) {
19444
19484
  if (!Array.isArray(blocks)) return "";
19445
19485
  return blocks.map((block) => {
19446
- if (!isRecord$42(block)) return "";
19486
+ if (!isRecord$43(block)) return "";
19447
19487
  if (block.type === "text" && typeof block.text === "string") return sanitizeText$7(block.text);
19448
19488
  if (block.type === "tool-result") return toolResultText$7(block.content);
19449
19489
  if (block.type === "image") return `[image: ${attachmentLabel$5(block) ?? "attached image"}]`;
@@ -19461,7 +19501,7 @@ function toolResultBlocks$2(blocks, images) {
19461
19501
  const out = [];
19462
19502
  let hasImage = false;
19463
19503
  for (const block of blocks) {
19464
- if (!isRecord$42(block)) continue;
19504
+ if (!isRecord$43(block)) continue;
19465
19505
  if (block.type === "text" && typeof block.text === "string") {
19466
19506
  out.push({
19467
19507
  type: "text",
@@ -19510,7 +19550,7 @@ function toolResultImageBlocks$4(blocks, images) {
19510
19550
  if (!Array.isArray(blocks)) return [];
19511
19551
  const out = [];
19512
19552
  for (const block of blocks) {
19513
- if (!isRecord$42(block)) continue;
19553
+ if (!isRecord$43(block)) continue;
19514
19554
  if (block.type === "image") {
19515
19555
  const inline = imageBlockToInline$3(block, images);
19516
19556
  if (inline && SUPPORTED_IMAGE_MEDIA_TYPES$4.has(inline.mediaType)) out.push({
@@ -19554,7 +19594,7 @@ function nonSystemMessages$7(options) {
19554
19594
  }
19555
19595
  /** Drop the JSON-Schema keywords provider gateways reject or ignore. */
19556
19596
  function stripMetaSchema$2(schema) {
19557
- if (!isRecord$42(schema)) return {
19597
+ if (!isRecord$43(schema)) return {
19558
19598
  type: "object",
19559
19599
  properties: {}
19560
19600
  };
@@ -19571,7 +19611,7 @@ function openAIUserContent$3(message, images) {
19571
19611
  const parts = [];
19572
19612
  let hasImage = false;
19573
19613
  for (const block of message.content) {
19574
- if (!isRecord$42(block)) continue;
19614
+ if (!isRecord$43(block)) continue;
19575
19615
  if (block.type === "text" && typeof block.text === "string") {
19576
19616
  const text = sanitizeText$7(block.text);
19577
19617
  if (text !== "") parts.push({
@@ -19599,7 +19639,7 @@ function openAIAssistantContent$3(message) {
19599
19639
  const textParts = [];
19600
19640
  const toolCalls = [];
19601
19641
  for (const block of message.content) {
19602
- if (!isRecord$42(block)) continue;
19642
+ if (!isRecord$43(block)) continue;
19603
19643
  if (block.type === "text" && typeof block.text === "string") textParts.push(sanitizeText$7(block.text));
19604
19644
  else if (block.type === "tool-call" && typeof block.name === "string") toolCalls.push({
19605
19645
  id: typeof block.id === "string" && block.id !== "" ? block.id : `call_${toolCalls.length}`,
@@ -19631,7 +19671,7 @@ function buildOpenAIRequest$1(options, images = NO_RESOLVED_IMAGES$4) {
19631
19671
  while (index < conversation.length && isToolResultMessage$7(conversation[index])) {
19632
19672
  const current = conversation[index];
19633
19673
  const block = current.content[0];
19634
- const callId = isRecord$42(block) && typeof block.toolCallId === "string" ? block.toolCallId : "";
19674
+ const callId = isRecord$43(block) && typeof block.toolCallId === "string" ? block.toolCallId : "";
19635
19675
  messages.push({
19636
19676
  role: "tool",
19637
19677
  tool_call_id: callId,
@@ -19692,7 +19732,7 @@ function anthropicUserContent$3(message, images) {
19692
19732
  if (!Array.isArray(message.content)) return [];
19693
19733
  const blocks = [];
19694
19734
  for (const block of message.content) {
19695
- if (!isRecord$42(block)) continue;
19735
+ if (!isRecord$43(block)) continue;
19696
19736
  if (block.type === "text" && typeof block.text === "string") {
19697
19737
  const text = sanitizeText$7(block.text);
19698
19738
  if (text !== "") blocks.push({
@@ -19729,7 +19769,7 @@ function anthropicUserContent$3(message, images) {
19729
19769
  function anthropicAssistantContent$3(message) {
19730
19770
  const blocks = [];
19731
19771
  for (const block of message.content) {
19732
- if (!isRecord$42(block)) continue;
19772
+ if (!isRecord$43(block)) continue;
19733
19773
  if (block.type === "text" && typeof block.text === "string") {
19734
19774
  const text = sanitizeText$7(block.text);
19735
19775
  if (text !== "") blocks.push({
@@ -19742,7 +19782,7 @@ function anthropicAssistantContent$3(message) {
19742
19782
  type: "tool_use",
19743
19783
  id: typeof block.id === "string" && block.id !== "" ? block.id : `toolu_${blocks.length}`,
19744
19784
  name: block.name,
19745
- input: isRecord$42(parsed) ? parsed : {}
19785
+ input: isRecord$43(parsed) ? parsed : {}
19746
19786
  });
19747
19787
  }
19748
19788
  }
@@ -19836,7 +19876,7 @@ function responsesUserContent(message, images) {
19836
19876
  if (!Array.isArray(message.content)) return [];
19837
19877
  const parts = [];
19838
19878
  for (const block of message.content) {
19839
- if (!isRecord$42(block)) continue;
19879
+ if (!isRecord$43(block)) continue;
19840
19880
  if (block.type === "text" && typeof block.text === "string") {
19841
19881
  const text = sanitizeText$7(block.text);
19842
19882
  if (text !== "") parts.push({
@@ -19861,7 +19901,7 @@ function responsesToolResultImageBlocks(blocks, images) {
19861
19901
  if (!Array.isArray(blocks)) return [];
19862
19902
  const out = [];
19863
19903
  for (const block of blocks) {
19864
- if (!isRecord$42(block)) continue;
19904
+ if (!isRecord$43(block)) continue;
19865
19905
  if (block.type === "image") {
19866
19906
  const inline = imageBlockToInline$3(block, images);
19867
19907
  if (inline && SUPPORTED_IMAGE_MEDIA_TYPES$4.has(inline.mediaType)) out.push({
@@ -19888,8 +19928,8 @@ function buildResponsesRequest(options, images = NO_RESOLVED_IMAGES$4) {
19888
19928
  const imageBlocks = [];
19889
19929
  while (index < conversation.length && isToolResultMessage$7(conversation[index])) {
19890
19930
  const current = conversation[index];
19891
- const toolResult = Array.isArray(current.content) ? current.content.find((b) => isRecord$42(b) && b.type === "tool-result") : void 0;
19892
- 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 : "");
19893
19933
  input.push({
19894
19934
  type: "function_call_output",
19895
19935
  call_id: callId,
@@ -19912,7 +19952,7 @@ function buildResponsesRequest(options, images = NO_RESOLVED_IMAGES$4) {
19912
19952
  content
19913
19953
  });
19914
19954
  for (const call of toolCalls) {
19915
- const fn = isRecord$42(call.function) ? call.function : void 0;
19955
+ const fn = isRecord$43(call.function) ? call.function : void 0;
19916
19956
  input.push({
19917
19957
  type: "function_call",
19918
19958
  call_id: call.id,
@@ -20035,9 +20075,9 @@ function processOpenAIStreamLine$1(line, state) {
20035
20075
  }
20036
20076
  if (payload === "") return [];
20037
20077
  const chunk = safeJsonParse$7(payload);
20038
- if (!isRecord$42(chunk)) return [];
20078
+ if (!isRecord$43(chunk)) return [];
20039
20079
  const out = [];
20040
- if (isRecord$42(chunk.error)) {
20080
+ if (isRecord$43(chunk.error)) {
20041
20081
  const message = asString$22(chunk.error.message) ?? "unknown error";
20042
20082
  state.streamError = {
20043
20083
  vocabulary: "openai",
@@ -20046,20 +20086,20 @@ function processOpenAIStreamLine$1(line, state) {
20046
20086
  };
20047
20087
  throw new LlmError$1(`Command Code stream error: ${message}`, isContextOverflow(chunk.error) ? CONTEXT_OVERFLOW_CODE : "PROVIDER_ERROR");
20048
20088
  }
20049
- const usage = isRecord$42(chunk.usage) ? chunk.usage : void 0;
20089
+ const usage = isRecord$43(chunk.usage) ? chunk.usage : void 0;
20050
20090
  if (usage) {
20051
20091
  state.sawUsage = true;
20052
20092
  const prompt = numberOr$8(usage.prompt_tokens, 0);
20053
- 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;
20054
20094
  const cached = details ? numberOr$8(details.cached_tokens, 0) : 0;
20055
20095
  state.inputTokens = Math.max(0, prompt - cached);
20056
20096
  state.cacheReadTokens = cached;
20057
20097
  state.outputTokens = numberOr$8(usage.completion_tokens, state.outputTokens);
20058
- 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);
20059
20099
  }
20060
20100
  const choices = Array.isArray(chunk.choices) ? chunk.choices : [];
20061
- const choice = isRecord$42(choices[0]) ? choices[0] : void 0;
20062
- 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;
20063
20103
  if (delta) {
20064
20104
  const reasoning = asString$22(delta.reasoning_content) ?? asString$22(delta.reasoning);
20065
20105
  if (reasoning !== void 0 && reasoning !== "") {
@@ -20087,7 +20127,7 @@ function processOpenAIStreamLine$1(line, state) {
20087
20127
  }
20088
20128
  const toolDeltas = Array.isArray(delta.tool_calls) ? delta.tool_calls : [];
20089
20129
  for (const entry of toolDeltas) {
20090
- if (!isRecord$42(entry)) continue;
20130
+ if (!isRecord$43(entry)) continue;
20091
20131
  out.push(...applyOpenAIToolDelta$1(entry, state));
20092
20132
  }
20093
20133
  }
@@ -20101,7 +20141,7 @@ function processOpenAIStreamLine$1(line, state) {
20101
20141
  }
20102
20142
  function applyOpenAIToolDelta$1(entry, state) {
20103
20143
  const wireIndex = typeof entry.index === "number" ? entry.index : 0;
20104
- const fn = isRecord$42(entry.function) ? entry.function : {};
20144
+ const fn = isRecord$43(entry.function) ? entry.function : {};
20105
20145
  const out = [];
20106
20146
  let call = state.toolCalls.get(wireIndex);
20107
20147
  if (call === void 0) {
@@ -20159,12 +20199,12 @@ function processAnthropicStreamLine$1(line, state) {
20159
20199
  const payload = trimmed.slice(5).trim();
20160
20200
  if (payload === "" || payload === "[DONE]") return [];
20161
20201
  const event = safeJsonParse$7(payload);
20162
- if (!isRecord$42(event)) return [];
20202
+ if (!isRecord$43(event)) return [];
20163
20203
  const type = asString$22(event.type);
20164
20204
  const out = [];
20165
20205
  if (type === "message_start") {
20166
- const message = isRecord$42(event.message) ? event.message : void 0;
20167
- 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;
20168
20208
  if (usage) {
20169
20209
  state.sawUsage = true;
20170
20210
  state.inputTokens = numberOr$8(usage.input_tokens, 0);
@@ -20178,7 +20218,7 @@ function processAnthropicStreamLine$1(line, state) {
20178
20218
  }
20179
20219
  if (type === "content_block_start") {
20180
20220
  const contentIndex = numberOr$8(event.index, 0);
20181
- const block = isRecord$42(event.content_block) ? event.content_block : {};
20221
+ const block = isRecord$43(event.content_block) ? event.content_block : {};
20182
20222
  const blockType = asString$22(block.type);
20183
20223
  out.push(...closeCurrent$5(state));
20184
20224
  if (blockType === "tool_use") {
@@ -20228,7 +20268,7 @@ function processAnthropicStreamLine$1(line, state) {
20228
20268
  }
20229
20269
  if (type === "content_block_delta") {
20230
20270
  const contentIndex = numberOr$8(event.index, 0);
20231
- const delta = isRecord$42(event.delta) ? event.delta : {};
20271
+ const delta = isRecord$43(event.delta) ? event.delta : {};
20232
20272
  const deltaType = asString$22(delta.type);
20233
20273
  if (deltaType === "input_json_delta") {
20234
20274
  const pending = state.toolCalls.get(contentIndex);
@@ -20301,10 +20341,10 @@ function processAnthropicStreamLine$1(line, state) {
20301
20341
  return out;
20302
20342
  }
20303
20343
  if (type === "message_delta") {
20304
- const delta = isRecord$42(event.delta) ? event.delta : void 0;
20344
+ const delta = isRecord$43(event.delta) ? event.delta : void 0;
20305
20345
  const stop = delta ? asString$22(delta.stop_reason) : void 0;
20306
20346
  if (stop !== void 0 && stop !== "") state.finishReason = stop;
20307
- const usage = isRecord$42(event.usage) ? event.usage : void 0;
20347
+ const usage = isRecord$43(event.usage) ? event.usage : void 0;
20308
20348
  if (usage) {
20309
20349
  state.sawUsage = true;
20310
20350
  state.outputTokens = numberOr$8(usage.output_tokens, state.outputTokens);
@@ -20316,7 +20356,7 @@ function processAnthropicStreamLine$1(line, state) {
20316
20356
  return closeStream$5(state);
20317
20357
  }
20318
20358
  if (type === "error") {
20319
- const error = isRecord$42(event.error) ? event.error : {};
20359
+ const error = isRecord$43(event.error) ? event.error : {};
20320
20360
  const message = asString$22(error.message) ?? "unknown error";
20321
20361
  state.streamError = {
20322
20362
  vocabulary: "anthropic",
@@ -20362,7 +20402,7 @@ function processResponsesStreamLine(line, state) {
20362
20402
  } catch {
20363
20403
  return [];
20364
20404
  }
20365
- if (!isRecord$42(parsed)) return [];
20405
+ if (!isRecord$43(parsed)) return [];
20366
20406
  const type = typeof parsed.type === "string" ? parsed.type : "";
20367
20407
  if (type === "response.output_text.delta") {
20368
20408
  const delta = typeof parsed.delta === "string" ? parsed.delta : "";
@@ -20389,7 +20429,7 @@ function processResponsesStreamLine(line, state) {
20389
20429
  }];
20390
20430
  }
20391
20431
  if (type === "response.output_item.added") {
20392
- const item = isRecord$42(parsed.item) ? parsed.item : null;
20432
+ const item = isRecord$43(parsed.item) ? parsed.item : null;
20393
20433
  if (item === null || item.type !== "function_call") return [];
20394
20434
  return startResponsesToolCall(state, parsed, item);
20395
20435
  }
@@ -20413,11 +20453,11 @@ function processResponsesStreamLine(line, state) {
20413
20453
  return [];
20414
20454
  }
20415
20455
  if (type === "response.completed" || type === "response.incomplete") {
20416
- const response = isRecord$42(parsed.response) ? parsed.response : null;
20456
+ const response = isRecord$43(parsed.response) ? parsed.response : null;
20417
20457
  if (response !== null) {
20418
20458
  if (response.status === "failed") {
20419
20459
  state.done = true;
20420
- const errorObj = isRecord$42(response.error) ? response.error : null;
20460
+ const errorObj = isRecord$43(response.error) ? response.error : null;
20421
20461
  const detail = errorObj && typeof errorObj.message === "string" ? errorObj.message : typeof response.error === "string" ? response.error : "";
20422
20462
  state.streamError = {
20423
20463
  vocabulary: "responses",
@@ -20430,7 +20470,7 @@ function processResponsesStreamLine(line, state) {
20430
20470
  }
20431
20471
  state.done = true;
20432
20472
  if (type === "response.incomplete") {
20433
- 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;
20434
20474
  const reason = details && typeof details.reason === "string" ? details.reason : "";
20435
20475
  state.finishReason = reason === "max_output_tokens" || reason === "length" || reason === "max_tokens" ? "length" : reason || "length";
20436
20476
  } else state.finishReason = "stop";
@@ -20438,8 +20478,8 @@ function processResponsesStreamLine(line, state) {
20438
20478
  }
20439
20479
  if (type === "response.failed" || type === "error") {
20440
20480
  state.done = true;
20441
- const errorObj = isRecord$42(parsed.error) ? parsed.error : isRecord$42(parsed.response) && isRecord$42(parsed.response.error) ? parsed.response.error : null;
20442
- 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 : "");
20443
20483
  state.streamError = {
20444
20484
  vocabulary: "responses",
20445
20485
  error: errorObj,
@@ -20527,15 +20567,15 @@ function responsesToolCall(state, key) {
20527
20567
  for (const call of state.toolCalls.values()) if (call.id === toToolCallId(key)) return call;
20528
20568
  }
20529
20569
  function readResponsesUsage(response, state) {
20530
- const usage = isRecord$42(response.usage) ? response.usage : null;
20570
+ const usage = isRecord$43(response.usage) ? response.usage : null;
20531
20571
  if (usage === null) return;
20532
- const cached = isRecord$42(usage.input_tokens_details) ? numberOrZero(usage.input_tokens_details.cached_tokens) : 0;
20533
- 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;
20534
20574
  state.inputTokens = Math.max(0, numberOrZero(usage.input_tokens) - cached - written);
20535
20575
  state.cacheReadTokens = cached;
20536
20576
  state.cacheWriteTokens = written;
20537
20577
  state.outputTokens = numberOrZero(usage.output_tokens);
20538
- 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;
20539
20579
  if (outputDetails !== null) state.reasoningTokens = numberOrZero(outputDetails.reasoning_tokens);
20540
20580
  state.sawUsage = true;
20541
20581
  }
@@ -20623,7 +20663,7 @@ const ANTHROPIC_IN_BAND_STATUS = {
20623
20663
  * Anything not recognized keeps the mapper's verdict.
20624
20664
  */
20625
20665
  function inBandResponsesCode(error, message) {
20626
- const fields = isRecord$41(error) ? error : {};
20666
+ const fields = isRecord$42(error) ? error : {};
20627
20667
  const rawCode = (asString$21(fields.code) ?? asString$21(fields.type) ?? "").toLowerCase();
20628
20668
  const text = message.toLowerCase();
20629
20669
  if (text.includes("rate limit") || rawCode === "rate_limit" || rawCode === "rate_limit_exceeded") return "RATE_LIMIT";
@@ -20682,13 +20722,13 @@ function reclassifyInBandResponsesError(thrown, rawError, message, classify) {
20682
20722
  }
20683
20723
  /** The wire `error` object of an in-band event, read defensively. */
20684
20724
  function readErrorType(error) {
20685
- if (!isRecord$41(error)) return null;
20725
+ if (!isRecord$42(error)) return null;
20686
20726
  return typeof error.type === "string" ? error.type : null;
20687
20727
  }
20688
20728
  function asString$21(value) {
20689
20729
  return typeof value === "string" ? value : void 0;
20690
20730
  }
20691
- function isRecord$41(value) {
20731
+ function isRecord$42(value) {
20692
20732
  return typeof value === "object" && value !== null && !Array.isArray(value);
20693
20733
  }
20694
20734
  //#endregion
@@ -23883,14 +23923,14 @@ const IN_BAND_SERVER = [
23883
23923
  function isAbort$3(error, signal) {
23884
23924
  return signal?.aborted === true || error instanceof Error && error.name === "AbortError";
23885
23925
  }
23886
- function isRecord$40(value) {
23926
+ function isRecord$41(value) {
23887
23927
  return typeof value === "object" && value !== null && !Array.isArray(value);
23888
23928
  }
23889
23929
  function asString$20(value) {
23890
23930
  return typeof value === "string" && value !== "" ? value : void 0;
23891
23931
  }
23892
23932
  function attachmentLabel$4(block) {
23893
- const attachment = isRecord$40(block.attachment) ? block.attachment : void 0;
23933
+ const attachment = isRecord$41(block.attachment) ? block.attachment : void 0;
23894
23934
  return asString$20(attachment?.name) || asString$20(attachment?.attachmentId);
23895
23935
  }
23896
23936
  function unavailableImageText$4(block) {
@@ -23926,7 +23966,7 @@ async function processMessageContent(content, model, attachments, signal) {
23926
23966
  textParts.push(part);
23927
23967
  continue;
23928
23968
  }
23929
- if (!isRecord$40(part)) continue;
23969
+ if (!isRecord$41(part)) continue;
23930
23970
  if (part.type === "text") {
23931
23971
  if (typeof part.text === "string" && part.text !== "") textParts.push(part.text);
23932
23972
  continue;
@@ -23955,7 +23995,7 @@ async function processMessageContent(content, model, attachments, signal) {
23955
23995
  continue;
23956
23996
  }
23957
23997
  const inline = part.source;
23958
- if (isRecord$40(inline)) {
23998
+ if (isRecord$41(inline)) {
23959
23999
  const data = inline.data;
23960
24000
  if (typeof data === "string" && data !== "") {
23961
24001
  images.push(encodeImage(data, asMediaType(inline.mediaType)));
@@ -23996,7 +24036,7 @@ async function toOllamaRequest(options, attachments, signal, declared) {
23996
24036
  const toolResultBlocks = message.content.filter((b) => b.type === "tool-result");
23997
24037
  if (toolResultBlocks.length > 0) {
23998
24038
  for (const block of toolResultBlocks) {
23999
- 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 : "") ?? "");
24000
24040
  const processed = await processMessageContent(block.content, options.model, attachments, signal);
24001
24041
  messages.push({
24002
24042
  role: "tool",
@@ -24607,7 +24647,7 @@ var OllamaAccountPool = class extends AccountPoolCore {
24607
24647
  };
24608
24648
  //#endregion
24609
24649
  //#region src/host/common/request-images.ts
24610
- function isRecord$39(value) {
24650
+ function isRecord$40(value) {
24611
24651
  return typeof value === "object" && value !== null && !Array.isArray(value);
24612
24652
  }
24613
24653
  function asString$19(value) {
@@ -24666,17 +24706,17 @@ const SUPPORTED_IMAGE_MEDIA_TYPES$3 = /* @__PURE__ */ new Set([
24666
24706
  ]);
24667
24707
  function attachmentOf$3(block) {
24668
24708
  const attachment = block.attachment;
24669
- if (!isRecord$39(attachment)) return void 0;
24709
+ if (!isRecord$40(attachment)) return void 0;
24670
24710
  return typeof attachment.attachmentId === "string" ? attachment : void 0;
24671
24711
  }
24672
24712
  function attachmentLabel$3(block) {
24673
- const attachment = isRecord$39(block.attachment) ? block.attachment : void 0;
24713
+ const attachment = isRecord$40(block.attachment) ? block.attachment : void 0;
24674
24714
  return asString$19(attachment?.name) || asString$19(attachment?.attachmentId);
24675
24715
  }
24676
24716
  function collectImageRefs$1(content, refs) {
24677
24717
  if (!Array.isArray(content)) return;
24678
24718
  for (const block of content) {
24679
- if (!isRecord$39(block)) continue;
24719
+ if (!isRecord$40(block)) continue;
24680
24720
  if (block.type === "image") {
24681
24721
  const attachment = attachmentOf$3(block);
24682
24722
  if (attachment) refs.set(attachment.attachmentId, attachment);
@@ -24706,7 +24746,7 @@ function base64Length$1(bytes) {
24706
24746
  */
24707
24747
  function requestImagePayloadLength(block, images) {
24708
24748
  let data = asString$19(block.data) || asString$19(block.base64);
24709
- const source = isRecord$39(block.source) ? block.source : void 0;
24749
+ const source = isRecord$40(block.source) ? block.source : void 0;
24710
24750
  if (!data && source) data = asString$19(source.data) || asString$19(source.base64);
24711
24751
  if (data?.startsWith("data:")) {
24712
24752
  const matched = data.match(/^data:([^;,]+);base64,(.*)$/s);
@@ -24732,7 +24772,7 @@ function requestImagePayloadLength(block, images) {
24732
24772
  function collectRequestImagePayloads(content, images, lengths) {
24733
24773
  if (!Array.isArray(content)) return;
24734
24774
  for (const block of content) {
24735
- if (!isRecord$39(block)) continue;
24775
+ if (!isRecord$40(block)) continue;
24736
24776
  if (block.type === "image") {
24737
24777
  const bytes = requestImagePayloadLength(block, images);
24738
24778
  if (bytes !== void 0) lengths.push(bytes);
@@ -24786,7 +24826,7 @@ function replaceOldestRequestImages(options, count, images) {
24786
24826
  let replaced = false;
24787
24827
  return {
24788
24828
  content: content.map((block) => {
24789
- if (remaining.count === 0 || !isRecord$39(block)) return block;
24829
+ if (remaining.count === 0 || !isRecord$40(block)) return block;
24790
24830
  if (block.type === "image") {
24791
24831
  if (requestImagePayloadLength(block, images) === void 0) return block;
24792
24832
  remaining.count -= 1;
@@ -24924,10 +24964,20 @@ function requestHasSendableImage(options, images) {
24924
24964
  * An unreadable image resolves to `unavailable` rather than disappearing, so
24925
24965
  * the model is told the picture is missing instead of answering about a blank.
24926
24966
  *
24927
- * An image whose long edge is over {@link REQUEST_IMAGE_MAX_EDGE} is sent as the
24928
- * harness's downscaled request version (see {@link requestImageTarget}). The
24929
- * stored image and durable history do not change, and an image that already
24930
- * 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.
24931
24981
  *
24932
24982
  * WHY scale instead of drop. `offloadOldestRequestImages` has to drop an image
24933
24983
  * once the request exceeds `MAX_REQUEST_IMAGE_BYTES`, and the number it drops
@@ -24937,7 +24987,7 @@ function requestHasSendableImage(options, images) {
24937
24987
  * outgrew the budget once stays inside it instead of breaking the cache every
24938
24988
  * time an image is added.
24939
24989
  */
24940
- async function resolveRequestImages$1(options, attachments, signal) {
24990
+ async function resolveRequestImages$1(options, attachments, signal, edge = REQUEST_IMAGE_MAX_EDGE) {
24941
24991
  const refs = /* @__PURE__ */ new Map();
24942
24992
  for (const message of options.messages) collectImageRefs$1(message.content, refs);
24943
24993
  if (refs.size === 0) return NO_RESOLVED_IMAGES$3;
@@ -24948,10 +24998,10 @@ async function resolveRequestImages$1(options, attachments, signal) {
24948
24998
  return;
24949
24999
  }
24950
25000
  try {
24951
- const target = requestImageTarget(ref);
25001
+ const target = requestImageTarget(ref, edge);
24952
25002
  if (target !== void 0) {
24953
25003
  const version = await attachments.readImageRequest(ref, target, signal);
24954
- 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" } : {
24955
25005
  kind: "inline",
24956
25006
  mediaType: version.mediaType,
24957
25007
  data: Buffer.from(version.data).toString("base64")
@@ -24977,7 +25027,7 @@ function unavailableImageText$3(block) {
24977
25027
  }
24978
25028
  function imageBlockToInline$2(block, images) {
24979
25029
  let data = asString$19(block.data) || asString$19(block.base64);
24980
- const source = isRecord$39(block.source) ? block.source : void 0;
25030
+ const source = isRecord$40(block.source) ? block.source : void 0;
24981
25031
  if (!data && source) data = asString$19(source.data) || asString$19(source.base64);
24982
25032
  let mediaType = asString$19(block.mimeType) || asString$19(block.mediaType) || (source ? asString$19(source.mimeType) || asString$19(source.mediaType) : void 0) || "image/png";
24983
25033
  if (data?.startsWith("data:")) {
@@ -26556,11 +26606,11 @@ const MODELS_PATH$2 = "/models";
26556
26606
  const USAGES_PATH = "/usages";
26557
26607
  /** The service reports money in fixed-point units of 1e-6 cents. */
26558
26608
  const FIXED_POINT_CENTS = 1e6;
26559
- function isRecord$38(value) {
26609
+ function isRecord$39(value) {
26560
26610
  return typeof value === "object" && value !== null && !Array.isArray(value);
26561
26611
  }
26562
26612
  function asRecord$13(value) {
26563
- return isRecord$38(value) ? value : void 0;
26613
+ return isRecord$39(value) ? value : void 0;
26564
26614
  }
26565
26615
  function asString$18(value) {
26566
26616
  if (typeof value === "string" && value.trim() !== "") return value;
@@ -26574,7 +26624,7 @@ function asNumber$4(value) {
26574
26624
  if (Number.isFinite(parsed)) return parsed;
26575
26625
  }
26576
26626
  }
26577
- function firstString(record, keys) {
26627
+ function firstString$1(record, keys) {
26578
26628
  for (const key of keys) {
26579
26629
  const value = asString$18(record[key]);
26580
26630
  if (value !== void 0) return value;
@@ -27195,7 +27245,7 @@ function parseUsageWindows(payload) {
27195
27245
  const duration = firstNumber(window ?? {}, ["duration"]);
27196
27246
  const unit = asString$18(window?.timeUnit ?? window?.time_unit);
27197
27247
  const minutes = duration === void 0 ? null : unit === "TIME_UNIT_HOUR" ? duration * 60 : duration;
27198
- 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 : {
27199
27249
  label: minutes === 300 ? "5-hour" : `${minutes}-minute`,
27200
27250
  minutes
27201
27251
  });
@@ -27294,7 +27344,7 @@ function membershipLevelName(level) {
27294
27344
  */
27295
27345
  function parsePlanName(payload) {
27296
27346
  const root = asRecord$13(payload) ?? {};
27297
- const direct = firstString(root, [
27347
+ const direct = firstString$1(root, [
27298
27348
  "user_level_name",
27299
27349
  "userLevelName",
27300
27350
  "planName",
@@ -27418,7 +27468,7 @@ function accountFromCredentials$1(credentials, payload, profile) {
27418
27468
  const planLevel = (profile === void 0 ? null : parsePlanLevel(profile)) ?? (payload === void 0 ? null : parsePlanLevel(payload));
27419
27469
  return {
27420
27470
  userId: credentials.userId ?? identity.userId ?? null,
27421
- nickname: (profile === void 0 ? null : firstString(asRecord$13(profile) ?? {}, [
27471
+ nickname: (profile === void 0 ? null : firstString$1(asRecord$13(profile) ?? {}, [
27422
27472
  "nickname",
27423
27473
  "username",
27424
27474
  "name"
@@ -27532,12 +27582,12 @@ async function testConnection$1(store, options = {}) {
27532
27582
  *
27533
27583
  * @module dsh-chatgpt-subscription/tool-schema
27534
27584
  */
27535
- function isRecord$37(value) {
27585
+ function isRecord$38(value) {
27536
27586
  return typeof value === "object" && value !== null && !Array.isArray(value);
27537
27587
  }
27538
27588
  function cloneJsonValue(value) {
27539
27589
  if (Array.isArray(value)) return value.map(cloneJsonValue);
27540
- if (isRecord$37(value)) {
27590
+ if (isRecord$38(value)) {
27541
27591
  const res = {};
27542
27592
  for (const [k, v] of Object.entries(value)) res[k] = cloneJsonValue(v);
27543
27593
  return res;
@@ -27552,7 +27602,7 @@ function resolveLocalJsonPointer(root, ref) {
27552
27602
  let current = root;
27553
27603
  for (const rawPart of ref.slice(2).split("/")) {
27554
27604
  const part = rawPart.replaceAll("~1", "/").replaceAll("~0", "~");
27555
- if (isRecord$37(current)) {
27605
+ if (isRecord$38(current)) {
27556
27606
  if (!Object.prototype.hasOwnProperty.call(current, part)) return { found: false };
27557
27607
  current = current[part];
27558
27608
  } else if (Array.isArray(current)) {
@@ -27568,7 +27618,7 @@ function resolveLocalJsonPointer(root, ref) {
27568
27618
  }
27569
27619
  function derefNode(node, root, visited) {
27570
27620
  if (Array.isArray(node)) return node.map((item) => derefNode(item, root, visited));
27571
- if (isRecord$37(node)) {
27621
+ if (isRecord$38(node)) {
27572
27622
  if (typeof node["$ref"] === "string" && (node["$ref"] === "#" || node["$ref"].startsWith("#/"))) {
27573
27623
  const ref = node["$ref"];
27574
27624
  if (visited.has(ref)) return node;
@@ -27577,7 +27627,7 @@ function derefNode(node, root, visited) {
27577
27627
  visited.add(ref);
27578
27628
  const inlined = derefNode(resolved.value, root, visited);
27579
27629
  visited.delete(ref);
27580
- if (isRecord$37(inlined)) {
27630
+ if (isRecord$38(inlined)) {
27581
27631
  const merged = { ...inlined };
27582
27632
  for (const [k, v] of Object.entries(node)) {
27583
27633
  if (k === "$ref") continue;
@@ -27597,7 +27647,7 @@ function derefNode(node, root, visited) {
27597
27647
  }
27598
27648
  function hasUnresolvedDefinitionRef(node, bucketKey) {
27599
27649
  if (Array.isArray(node)) return node.some((child) => hasUnresolvedDefinitionRef(child, bucketKey));
27600
- if (isRecord$37(node)) {
27650
+ if (isRecord$38(node)) {
27601
27651
  const ref = node["$ref"];
27602
27652
  if (typeof ref === "string" && ref.startsWith(`#/${bucketKey}/`)) return true;
27603
27653
  for (const [k, v] of Object.entries(node)) {
@@ -27628,19 +27678,19 @@ function inferTypeFromValues(values) {
27628
27678
  if (types.size === 1) return types.values().next().value;
27629
27679
  }
27630
27680
  function normalizeSchemaProperties(node) {
27631
- if (!isRecord$37(node)) return;
27632
- if (isRecord$37(node.properties)) {
27633
- 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)) {
27634
27684
  if (!propSchema.type && !propSchema.$ref) {
27635
27685
  if (Array.isArray(propSchema.enum) && propSchema.enum.length > 0) propSchema.type = inferTypeFromValues(propSchema.enum) ?? "string";
27636
27686
  else if (propSchema.const !== void 0) propSchema.type = inferValueType(propSchema.const) ?? "string";
27637
- else if (isRecord$37(propSchema.properties)) propSchema.type = "object";
27687
+ else if (isRecord$38(propSchema.properties)) propSchema.type = "object";
27638
27688
  else if (propSchema.items) propSchema.type = "array";
27639
27689
  }
27640
27690
  normalizeSchemaProperties(propSchema);
27641
27691
  }
27642
27692
  }
27643
- if (isRecord$37(node.items)) normalizeSchemaProperties(node.items);
27693
+ if (isRecord$38(node.items)) normalizeSchemaProperties(node.items);
27644
27694
  else if (Array.isArray(node.items)) for (const item of node.items) normalizeSchemaProperties(item);
27645
27695
  }
27646
27696
  /**
@@ -27648,7 +27698,7 @@ function normalizeSchemaProperties(node) {
27648
27698
  * inlining definitions ($defs/definitions/$ref), and ensuring property types for enums/consts.
27649
27699
  */
27650
27700
  function normalizeKimiToolSchema(schema) {
27651
- if (!isRecord$37(schema)) return {
27701
+ if (!isRecord$38(schema)) return {
27652
27702
  type: "object",
27653
27703
  properties: {}
27654
27704
  };
@@ -27666,7 +27716,7 @@ function stripMetaSchema$1(schema) {
27666
27716
  }
27667
27717
  //#endregion
27668
27718
  //#region src/host/common/prompt-estimate.ts
27669
- function isRecord$36(value) {
27719
+ function isRecord$37(value) {
27670
27720
  return typeof value === "object" && value !== null && !Array.isArray(value);
27671
27721
  }
27672
27722
  /** Tool-call arguments as the JSON string a wire carries, whatever shape they arrived in. */
@@ -27699,7 +27749,7 @@ function estimatedInputTokens(options) {
27699
27749
  const contentCharacters = (content) => {
27700
27750
  if (!Array.isArray(content)) return typeof content === "string" ? content.length : 0;
27701
27751
  return content.reduce((total, block) => {
27702
- if (!isRecord$36(block)) return total;
27752
+ if (!isRecord$37(block)) return total;
27703
27753
  if ((block.type === "text" || block.type === "reasoning") && typeof block.text === "string") return total + block.text.length;
27704
27754
  if (block.type === "tool-result") return total + contentCharacters(block.content);
27705
27755
  if (block.type === "tool-call") {
@@ -27800,7 +27850,7 @@ function videoOmissionText$1(reason, label) {
27800
27850
  }
27801
27851
  //#endregion
27802
27852
  //#region src/host/common/video-request.ts
27803
- function isRecord$35(value) {
27853
+ function isRecord$36(value) {
27804
27854
  return typeof value === "object" && value !== null && !Array.isArray(value);
27805
27855
  }
27806
27856
  function asString$17(value) {
@@ -27808,14 +27858,14 @@ function asString$17(value) {
27808
27858
  }
27809
27859
  function videoAttachmentOf(block) {
27810
27860
  const attachment = block.attachment;
27811
- if (!isRecord$35(attachment)) return void 0;
27861
+ if (!isRecord$36(attachment)) return void 0;
27812
27862
  if (typeof attachment.attachmentId !== "string") return void 0;
27813
27863
  return attachment;
27814
27864
  }
27815
27865
  function collectVideoRefs(content, refs) {
27816
27866
  if (!Array.isArray(content)) return;
27817
27867
  for (const block of content) {
27818
- if (!isRecord$35(block) || block.type !== "video") continue;
27868
+ if (!isRecord$36(block) || block.type !== "video") continue;
27819
27869
  const attachment = videoAttachmentOf(block);
27820
27870
  if (attachment) refs.set(attachment.attachmentId, attachment);
27821
27871
  }
@@ -27830,7 +27880,7 @@ function requestVideoBytes(block) {
27830
27880
  function collectRequestVideoBytes(content, lengths) {
27831
27881
  if (!Array.isArray(content)) return;
27832
27882
  for (const block of content) {
27833
- if (!isRecord$35(block) || block.type !== "video") continue;
27883
+ if (!isRecord$36(block) || block.type !== "video") continue;
27834
27884
  const bytes = requestVideoBytes(block);
27835
27885
  if (bytes !== void 0) lengths.push(bytes);
27836
27886
  }
@@ -27874,7 +27924,7 @@ function isAbortLike(error, signal) {
27874
27924
  }
27875
27925
  /** True when the request carries any video occurrence at all. */
27876
27926
  function requestHasVideo(options) {
27877
- 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"));
27878
27928
  }
27879
27929
  function omittedVideoText(omit, block) {
27880
27930
  return omit("unreadable", videoBlockLabel(block));
@@ -27909,7 +27959,7 @@ function offloadOldestRequestVideos(options, maxBytes) {
27909
27959
  if (remaining.count === 0 || !Array.isArray(message.content)) return message;
27910
27960
  let replaced = false;
27911
27961
  const content = message.content.map((block) => {
27912
- 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;
27913
27963
  if (requestVideoBytes(block) === void 0) return block;
27914
27964
  remaining.count -= 1;
27915
27965
  replaced = true;
@@ -27992,7 +28042,7 @@ function videoBlockToInline(block, videos, videoAccepted, omit) {
27992
28042
  *
27993
28043
  * See https://www.kimi.com/code/docs/en/kimi-code/error-reference.html
27994
28044
  */
27995
- function isRecord$34(value) {
28045
+ function isRecord$35(value) {
27996
28046
  return typeof value === "object" && value !== null && !Array.isArray(value);
27997
28047
  }
27998
28048
  function asString$16(value) {
@@ -28025,6 +28075,118 @@ const KNOWN_REASONING_KEYS = [
28025
28075
  /** Default outbound reasoning key, used until a response proves otherwise. */
28026
28076
  const DEFAULT_REASONING_KEY = KNOWN_REASONING_KEYS[0];
28027
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
+ /**
28028
28190
  * Per-endpoint reasoning-field dialect: observes what the endpoint actually
28029
28191
  * sends, and echoes thinking back under the same key.
28030
28192
  *
@@ -28036,16 +28198,40 @@ const DEFAULT_REASONING_KEY = KNOWN_REASONING_KEYS[0];
28036
28198
  */
28037
28199
  var ReasoningKeyDialect = class {
28038
28200
  detected;
28039
- /** Reasoning text on an inbound message or delta, remembering its key. */
28040
- observe(source) {
28041
- if (typeof source !== "object" || source === null) return void 0;
28042
- const record = source;
28043
- for (const key of KNOWN_REASONING_KEYS) {
28044
- const value = record[key];
28045
- if (typeof value !== "string") continue;
28046
- this.detected = key;
28047
- 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
+ }
28048
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;
28049
28235
  }
28050
28236
  /** The key to serialize thinking into on an outbound assistant message. */
28051
28237
  outboundKey() {
@@ -28213,7 +28399,7 @@ function textOf$6(content) {
28213
28399
  if (!Array.isArray(content)) return "";
28214
28400
  const parts = [];
28215
28401
  for (const block of content) {
28216
- if (!isRecord$34(block)) continue;
28402
+ if (!isRecord$35(block)) continue;
28217
28403
  if (block.type === "text" && typeof block.text === "string") parts.push(sanitizeText$6(block.text));
28218
28404
  else if (block.type === "tool-result") parts.push(textOf$6(block.content));
28219
28405
  }
@@ -28222,7 +28408,7 @@ function textOf$6(content) {
28222
28408
  function toolResultText$6(blocks) {
28223
28409
  if (!Array.isArray(blocks)) return "";
28224
28410
  return blocks.map((block) => {
28225
- if (!isRecord$34(block)) return "";
28411
+ if (!isRecord$35(block)) return "";
28226
28412
  if (block.type === "text" && typeof block.text === "string") return sanitizeText$6(block.text);
28227
28413
  if (block.type === "tool-result") return toolResultText$6(block.content);
28228
28414
  if (block.type === "image") return `[image: ${attachmentLabel$3(block) ?? "attached image"}]`;
@@ -28240,7 +28426,7 @@ function toolResultBlocks$1(blocks, images) {
28240
28426
  const out = [];
28241
28427
  let hasImage = false;
28242
28428
  for (const block of blocks) {
28243
- if (!isRecord$34(block)) continue;
28429
+ if (!isRecord$35(block)) continue;
28244
28430
  if (block.type === "text" && typeof block.text === "string") {
28245
28431
  out.push({
28246
28432
  type: "text",
@@ -28284,13 +28470,27 @@ function toolResultBlocks$1(blocks, images) {
28284
28470
  * OpenAI `image_url` blocks for one tool result. A `role: "tool"` message can
28285
28471
  * only carry text, so these ride on a `user` message appended after the run of
28286
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.
28287
28479
  */
28288
- function toolResultImageBlocks$3(blocks, images) {
28480
+ function toolResultImageBlocks$3(blocks, images, media = {}) {
28289
28481
  if (!Array.isArray(blocks)) return [];
28290
28482
  const out = [];
28291
28483
  for (const block of blocks) {
28292
- if (!isRecord$34(block)) continue;
28484
+ if (!isRecord$35(block)) continue;
28293
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
+ }
28294
28494
  const inline = imageBlockToInline$2(block, images);
28295
28495
  if (inline && SUPPORTED_IMAGE_MEDIA_TYPES$3.has(inline.mediaType)) out.push({
28296
28496
  type: "image_url",
@@ -28302,10 +28502,41 @@ function toolResultImageBlocks$3(blocks, images) {
28302
28502
  });
28303
28503
  continue;
28304
28504
  }
28305
- 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));
28306
28506
  }
28307
28507
  return out;
28308
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
+ }
28309
28540
  function toolCallArguments$5(raw) {
28310
28541
  if (typeof raw === "string") return raw;
28311
28542
  if (raw === void 0 || raw === null) return "{}";
@@ -28350,12 +28581,135 @@ function leadingSystemText$6(options) {
28350
28581
  function nonSystemMessages$6(options) {
28351
28582
  return options.messages.filter((message) => message.role !== "system");
28352
28583
  }
28353
- /** Concatenated reasoning text one assistant message carries, when it has any. */
28354
- function reasoningText(message) {
28355
- if (!Array.isArray(message.content)) return "";
28356
- const parts = [];
28357
- for (const block of message.content) if (isRecord$34(block) && block.type === "reasoning" && typeof block.text === "string") parts.push(sanitizeText$6(block.text));
28358
- 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
+ };
28359
28713
  }
28360
28714
  /**
28361
28715
  * A tool declaration that belongs to a message rather than the request.
@@ -28402,7 +28756,7 @@ function openAIUserContent$2(message, images, media = {}) {
28402
28756
  const parts = [];
28403
28757
  let hasRichPart = false;
28404
28758
  for (const block of message.content) {
28405
- if (!isRecord$34(block)) continue;
28759
+ if (!isRecord$35(block)) continue;
28406
28760
  if (block.type === "text" && typeof block.text === "string") {
28407
28761
  const text = sanitizeText$6(block.text);
28408
28762
  if (text !== "") parts.push({
@@ -28410,18 +28764,36 @@ function openAIUserContent$2(message, images, media = {}) {
28410
28764
  text
28411
28765
  });
28412
28766
  } else if (block.type === "image") {
28413
- const inline = imageBlockToInline$2(block, images);
28414
- if (inline && SUPPORTED_IMAGE_MEDIA_TYPES$3.has(inline.mediaType)) {
28767
+ const reference = hostedReference(media.imageReferences, block);
28768
+ if (reference !== void 0) {
28415
28769
  hasRichPart = true;
28416
28770
  parts.push({
28417
28771
  type: "image_url",
28418
- image_url: { url: `data:${inline.mediaType};base64,${inline.data}` }
28772
+ image_url: { url: reference }
28419
28773
  });
28420
- } else parts.push({
28421
- type: "text",
28422
- text: unavailableImageText$3(block)
28423
- });
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
+ }
28424
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
+ }
28425
28797
  const outcome = videoBlockToInline(block, media.videos ?? /* @__PURE__ */ new Map(), media.videoAccepted === true, videoOmissionText$1);
28426
28798
  if ("inline" in outcome) {
28427
28799
  hasRichPart = true;
@@ -28438,11 +28810,11 @@ function openAIUserContent$2(message, images, media = {}) {
28438
28810
  if (!hasRichPart) return parts.map((part) => typeof part.text === "string" ? part.text : "").join("");
28439
28811
  return parts;
28440
28812
  }
28441
- function openAIAssistantContent$2(message, toolCallIds) {
28813
+ function openAIAssistantContent$2(message, toolCallIds, reasoningKey) {
28442
28814
  const textParts = [];
28443
28815
  const toolCalls = [];
28444
28816
  for (const block of message.content) {
28445
- if (!isRecord$34(block)) continue;
28817
+ if (!isRecord$35(block)) continue;
28446
28818
  if (block.type === "text" && typeof block.text === "string") textParts.push(sanitizeText$6(block.text));
28447
28819
  else if (block.type === "tool-call" && typeof block.name === "string") toolCalls.push({
28448
28820
  id: toolCallIds.normalize(typeof block.id === "string" && block.id !== "" ? block.id : `call_${toolCalls.length}`),
@@ -28456,7 +28828,7 @@ function openAIAssistantContent$2(message, toolCallIds) {
28456
28828
  return {
28457
28829
  content: textParts.join(""),
28458
28830
  toolCalls,
28459
- reasoning: reasoningText(message)
28831
+ reasoning: replayedReasoning(message, reasoningKey)
28460
28832
  };
28461
28833
  }
28462
28834
  /** Why one declaration could not be put on the wire, as the model sees it. */
@@ -28518,6 +28890,58 @@ function declarationSlots(options, media) {
28518
28890
  }
28519
28891
  return slots;
28520
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
+ }
28521
28945
  /** Build one `/chat/completions` body. */
28522
28946
  function buildOpenAIRequest(options, images = NO_RESOLVED_IMAGES$3, preserveThinking = preserveThinkingEnabled(), media = {}) {
28523
28947
  const effort = mapReasoningEffort(options.reasoningEffort === void 0 ? void 0 : String(options.reasoningEffort));
@@ -28553,13 +28977,13 @@ function buildOpenAIRequest(options, images = NO_RESOLVED_IMAGES$3, preserveThin
28553
28977
  nonSystemIndex += 1;
28554
28978
  }
28555
28979
  const block = current.content[0];
28556
- const callId = isRecord$34(block) && typeof block.toolCallId === "string" ? block.toolCallId : "";
28980
+ const callId = isRecord$35(block) && typeof block.toolCallId === "string" ? block.toolCallId : "";
28557
28981
  messages.push({
28558
28982
  role: "tool",
28559
28983
  tool_call_id: toolCallIds.normalize(callId),
28560
28984
  content: toolResultText$6(current.content)
28561
28985
  });
28562
- imageBlocks.push(...toolResultImageBlocks$3(current.content, images));
28986
+ imageBlocks.push(...toolResultImageBlocks$3(current.content, images, media));
28563
28987
  index += 1;
28564
28988
  }
28565
28989
  index -= 1;
@@ -28570,12 +28994,13 @@ function buildOpenAIRequest(options, images = NO_RESOLVED_IMAGES$3, preserveThin
28570
28994
  continue;
28571
28995
  }
28572
28996
  if (message.role === "assistant") {
28573
- const { content, toolCalls, reasoning } = openAIAssistantContent$2(message, toolCallIds);
28574
- 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;
28575
29000
  const entry = { role: "assistant" };
28576
29001
  if (content !== "" || toolCalls.length === 0) entry.content = content;
28577
29002
  if (toolCalls.length > 0) entry.tool_calls = toolCalls;
28578
- if (thinkingOn) entry[reasoningDialect.outboundKey()] = reasoning;
29003
+ if (thinkingOn) applyReplayedReasoning(entry, reasoning, reasoningKey);
28579
29004
  messages.push(entry);
28580
29005
  continue;
28581
29006
  }
@@ -28639,7 +29064,7 @@ function anthropicUserContent$2(message, images, toolCallIds) {
28639
29064
  if (!Array.isArray(message.content)) return [];
28640
29065
  const blocks = [];
28641
29066
  for (const block of message.content) {
28642
- if (!isRecord$34(block)) continue;
29067
+ if (!isRecord$35(block)) continue;
28643
29068
  if (block.type === "video") {
28644
29069
  blocks.push({
28645
29070
  type: "text",
@@ -28683,7 +29108,7 @@ function anthropicUserContent$2(message, images, toolCallIds) {
28683
29108
  function anthropicAssistantContent$2(message, toolCallIds) {
28684
29109
  const blocks = [];
28685
29110
  for (const block of message.content) {
28686
- if (!isRecord$34(block)) continue;
29111
+ if (!isRecord$35(block)) continue;
28687
29112
  if (block.type === "text" && typeof block.text === "string") {
28688
29113
  const text = sanitizeText$6(block.text);
28689
29114
  if (text !== "") blocks.push({
@@ -28696,7 +29121,7 @@ function anthropicAssistantContent$2(message, toolCallIds) {
28696
29121
  type: "tool_use",
28697
29122
  id: toolCallIds.normalize(typeof block.id === "string" && block.id !== "" ? block.id : `toolu_${blocks.length}`),
28698
29123
  name: block.name,
28699
- input: isRecord$34(parsed) ? parsed : {}
29124
+ input: isRecord$35(parsed) ? parsed : {}
28700
29125
  });
28701
29126
  }
28702
29127
  }
@@ -28818,7 +29243,7 @@ function walkJson(value, visit) {
28818
29243
  for (const item of value) walkJson(item, visit);
28819
29244
  return;
28820
29245
  }
28821
- if (!isRecord$34(value)) return;
29246
+ if (!isRecord$35(value)) return;
28822
29247
  visit(value);
28823
29248
  for (const nested of Object.values(value)) walkJson(nested, visit);
28824
29249
  }
@@ -28834,12 +29259,12 @@ function requestBodyBreakdown(body, carriesVideo = false) {
28834
29259
  const totalBytes = Buffer.byteLength(serialized, "utf8");
28835
29260
  let imageBytes = 0;
28836
29261
  walkJson(body, (node) => {
28837
- 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;
28838
29263
  if (url?.startsWith("data:")) {
28839
29264
  imageBytes += Buffer.byteLength(url, "utf8");
28840
29265
  return;
28841
29266
  }
28842
- const source = isRecord$34(node.source) ? node.source : void 0;
29267
+ const source = isRecord$35(node.source) ? node.source : void 0;
28843
29268
  if (source && asString$16(source.data) !== void 0 && asString$16(source.media_type) !== void 0) imageBytes += Buffer.byteLength(asString$16(source.data) ?? "", "utf8");
28844
29269
  });
28845
29270
  const toolSchemaBytes = Buffer.byteLength(JSON.stringify(body.tools ?? []), "utf8");
@@ -28878,6 +29303,7 @@ function createStreamState$4(wire, sessionId) {
28878
29303
  sessionId: typeof sessionId === "string" && sessionId.trim() !== "" ? sessionId.trim() : void 0,
28879
29304
  blocks: [],
28880
29305
  current: null,
29306
+ reasoningRuns: /* @__PURE__ */ new Map(),
28881
29307
  toolCalls: /* @__PURE__ */ new Map(),
28882
29308
  contentIndexes: /* @__PURE__ */ new Map(),
28883
29309
  openContentIndex: null,
@@ -28964,9 +29390,9 @@ function processOpenAIStreamLine(line, state) {
28964
29390
  }
28965
29391
  if (payload === "") return [];
28966
29392
  const chunk = safeJsonParse$6(payload);
28967
- if (!isRecord$34(chunk)) return [];
29393
+ if (!isRecord$35(chunk)) return [];
28968
29394
  const out = [];
28969
- const errorPayload = isRecord$34(chunk.error) ? chunk.error : void 0;
29395
+ const errorPayload = isRecord$35(chunk.error) ? chunk.error : void 0;
28970
29396
  if (errorPayload !== void 0) {
28971
29397
  const message = asString$16(errorPayload.message) ?? "unknown error";
28972
29398
  state.streamError = {
@@ -28976,34 +29402,47 @@ function processOpenAIStreamLine(line, state) {
28976
29402
  };
28977
29403
  throw new LlmError$1(`Kimi Code stream error: ${message}`, isContextOverflow(errorPayload) ? CONTEXT_OVERFLOW_CODE : "PROVIDER_ERROR");
28978
29404
  }
28979
- const usage = isRecord$34(chunk.usage) ? chunk.usage : void 0;
29405
+ const usage = isRecord$35(chunk.usage) ? chunk.usage : void 0;
28980
29406
  if (usage) {
28981
29407
  state.sawUsage = true;
28982
29408
  const prompt = numberOr$7(usage.prompt_tokens, 0);
28983
- 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;
28984
29410
  const cached = details ? numberOr$7(details.cached_tokens, 0) : 0;
28985
29411
  const written = details ? numberOr$7(details.cache_write_tokens, 0) : 0;
28986
29412
  state.inputTokens = Math.max(0, prompt - cached - written);
28987
29413
  state.cacheReadTokens = cached;
28988
29414
  state.cacheWriteTokens = written;
28989
29415
  state.outputTokens = numberOr$7(usage.completion_tokens, state.outputTokens);
28990
- 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);
28991
29417
  }
28992
29418
  const choices = Array.isArray(chunk.choices) ? chunk.choices : [];
28993
- const choice = isRecord$34(choices[0]) ? choices[0] : void 0;
28994
- 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;
28995
29421
  if (delta) {
28996
- const reasoning = reasoningDialect.observe(delta);
28997
- 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;
28998
29425
  out.push(...closeToolCalls$4(state));
28999
29426
  if (state.current === null || state.current.type !== "reasoning") out.push(...openTextBlock$4(state, "reasoning"));
29000
- state.current.text += sanitizeText$6(reasoning);
29001
- state.hasContent = true;
29002
- out.push({
29003
- type: "reasoning-delta",
29004
- index: state.current.index,
29005
- 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
29006
29444
  });
29445
+ state.reasoningRuns.set(state.current.index, runs);
29007
29446
  }
29008
29447
  const content = asString$16(delta.content);
29009
29448
  if (content !== void 0 && content !== "") {
@@ -29019,7 +29458,7 @@ function processOpenAIStreamLine(line, state) {
29019
29458
  }
29020
29459
  const toolDeltas = Array.isArray(delta.tool_calls) ? delta.tool_calls : [];
29021
29460
  for (const entry of toolDeltas) {
29022
- if (!isRecord$34(entry)) continue;
29461
+ if (!isRecord$35(entry)) continue;
29023
29462
  out.push(...applyOpenAIToolDelta(entry, state));
29024
29463
  }
29025
29464
  }
@@ -29033,7 +29472,7 @@ function processOpenAIStreamLine(line, state) {
29033
29472
  }
29034
29473
  function applyOpenAIToolDelta(entry, state) {
29035
29474
  const wireIndex = typeof entry.index === "number" ? entry.index : 0;
29036
- const fn = isRecord$34(entry.function) ? entry.function : {};
29475
+ const fn = isRecord$35(entry.function) ? entry.function : {};
29037
29476
  const out = [];
29038
29477
  let call = state.toolCalls.get(wireIndex);
29039
29478
  if (call === void 0) {
@@ -29088,12 +29527,12 @@ function processAnthropicStreamLine(line, state) {
29088
29527
  const payload = trimmed.slice(5).trim();
29089
29528
  if (payload === "" || payload === "[DONE]") return [];
29090
29529
  const event = safeJsonParse$6(payload);
29091
- if (!isRecord$34(event)) return [];
29530
+ if (!isRecord$35(event)) return [];
29092
29531
  const type = asString$16(event.type);
29093
29532
  const out = [];
29094
29533
  if (type === "message_start") {
29095
- const message = isRecord$34(event.message) ? event.message : void 0;
29096
- 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;
29097
29536
  if (usage) {
29098
29537
  state.sawUsage = true;
29099
29538
  state.inputTokens = numberOr$7(usage.input_tokens, 0);
@@ -29107,7 +29546,7 @@ function processAnthropicStreamLine(line, state) {
29107
29546
  }
29108
29547
  if (type === "content_block_start") {
29109
29548
  const contentIndex = numberOr$7(event.index, 0);
29110
- const block = isRecord$34(event.content_block) ? event.content_block : {};
29549
+ const block = isRecord$35(event.content_block) ? event.content_block : {};
29111
29550
  const blockType = asString$16(block.type);
29112
29551
  out.push(...closeCurrent$4(state));
29113
29552
  if (blockType === "tool_use") {
@@ -29157,7 +29596,7 @@ function processAnthropicStreamLine(line, state) {
29157
29596
  }
29158
29597
  if (type === "content_block_delta") {
29159
29598
  const contentIndex = numberOr$7(event.index, 0);
29160
- const delta = isRecord$34(event.delta) ? event.delta : {};
29599
+ const delta = isRecord$35(event.delta) ? event.delta : {};
29161
29600
  const deltaType = asString$16(delta.type);
29162
29601
  if (deltaType === "input_json_delta") {
29163
29602
  const pending = state.toolCalls.get(contentIndex);
@@ -29230,10 +29669,10 @@ function processAnthropicStreamLine(line, state) {
29230
29669
  return out;
29231
29670
  }
29232
29671
  if (type === "message_delta") {
29233
- const delta = isRecord$34(event.delta) ? event.delta : void 0;
29672
+ const delta = isRecord$35(event.delta) ? event.delta : void 0;
29234
29673
  const stop = delta ? asString$16(delta.stop_reason) : void 0;
29235
29674
  if (stop !== void 0 && stop !== "") state.finishReason = stop;
29236
- const usage = isRecord$34(event.usage) ? event.usage : void 0;
29675
+ const usage = isRecord$35(event.usage) ? event.usage : void 0;
29237
29676
  if (usage) {
29238
29677
  state.sawUsage = true;
29239
29678
  state.outputTokens = numberOr$7(usage.output_tokens, state.outputTokens);
@@ -29245,7 +29684,7 @@ function processAnthropicStreamLine(line, state) {
29245
29684
  return closeStream$3(state);
29246
29685
  }
29247
29686
  if (type === "error") {
29248
- const error = isRecord$34(event.error) ? event.error : {};
29687
+ const error = isRecord$35(event.error) ? event.error : {};
29249
29688
  const message = asString$16(error.message) ?? "unknown error";
29250
29689
  state.streamError = {
29251
29690
  vocabulary: "anthropic",
@@ -29504,6 +29943,46 @@ function finishReasonFor$6(state) {
29504
29943
  if (state.hasToolCall || reason === "tool_calls" || reason === "tool_use") return { kind: "tool-calls" };
29505
29944
  return { kind: "stop" };
29506
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
+ }
29507
29986
  /** Flush every open block, then emit usage and the terminal finish. */
29508
29987
  function closeStream$3(state, accountId) {
29509
29988
  if (state.finished) return [];
@@ -29516,9 +29995,11 @@ function closeStream$3(state, accountId) {
29516
29995
  usage: tokenUsage$4(state)
29517
29996
  });
29518
29997
  }
29998
+ const replayState = reasoningReplayState(state);
29519
29999
  out.push({
29520
30000
  type: "finish",
29521
- reason: finishReasonFor$6(state)
30001
+ reason: finishReasonFor$6(state),
30002
+ ...replayState === void 0 ? {} : { replayState }
29522
30003
  });
29523
30004
  return out;
29524
30005
  }
@@ -29527,6 +30008,532 @@ function assertStreamComplete$4(state) {
29527
30008
  if (!state.done && state.finishReason === null) throw new LlmError$1("Kimi Code stream ended before its terminal event", "TRANSPORT");
29528
30009
  }
29529
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
29530
30537
  //#region src/host/kimi-code/adapter.ts
29531
30538
  /**
29532
30539
  * Rebuild attempts allowed while shrinking a request into its body ceiling.
@@ -29691,6 +30698,24 @@ const ACCOUNT_LIMIT_PATTERNS = [
29691
30698
  function matchesAny$1(text, patterns) {
29692
30699
  return patterns.some((pattern) => pattern.test(text));
29693
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
+ }
29694
30719
  /** Short, single-line excerpt of one error body, safe to show a user. */
29695
30720
  function summarizeFailureBody$1(raw) {
29696
30721
  const text = raw.replace(/[\r\n\t]+/g, " ").trim();
@@ -29726,6 +30751,129 @@ function isKimiBodySizeRefusal(detail) {
29726
30751
  return /\btotal message size\b[^;]{0,64}?\bexceed(?:s|ed)?\b[^;]{0,64}?\blimit\b/i.test(detail);
29727
30752
  }
29728
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
+ /**
29729
30877
  * Classify one non-2xx response for the retry policy.
29730
30878
  *
29731
30879
  * @param status - HTTP status the service answered with.
@@ -29877,6 +31025,19 @@ const ACCOUNT_LIMIT_COOLDOWN_MS = 300 * 6e4;
29877
31025
  const BILLING_CYCLE_COOLDOWN_MS = 720 * 60 * 6e4;
29878
31026
  /** Ceiling, so a malformed reset time cannot park an account for years. */
29879
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;
29880
31041
  var KimiCodeAdapter = class extends LlmAdapter {
29881
31042
  store;
29882
31043
  modelSettings;
@@ -29898,6 +31059,72 @@ var KimiCodeAdapter = class extends LlmAdapter {
29898
31059
  this.options = options;
29899
31060
  this.accountPool = accountPool ?? null;
29900
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
+ }
29901
31128
  providerInfo(provider) {
29902
31129
  return {
29903
31130
  id: provider,
@@ -29909,7 +31136,11 @@ var KimiCodeAdapter = class extends LlmAdapter {
29909
31136
  }
29910
31137
  imageRequestPricing() {}
29911
31138
  imageRequestTarget(_provider, _model, image) {
29912
- return requestImageTargetOrOriginal(image);
31139
+ return requestImageTarget(image, 2e3) ?? {
31140
+ width: image.width,
31141
+ height: image.height,
31142
+ maxBytes: image.bytes
31143
+ };
29913
31144
  }
29914
31145
  settings() {
29915
31146
  return this.preferences ? Promise.resolve(this.preferences.status()) : this.modelSettings.read();
@@ -30020,7 +31251,7 @@ var KimiCodeAdapter = class extends LlmAdapter {
30020
31251
  const wire = wireForCatalogEntry(options.model, catalog);
30021
31252
  const entry = catalog.find((model) => model.id === options.model);
30022
31253
  const normalized = normalizeGenerateOptions(options);
30023
- 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);
30024
31255
  const imageTrimmed = offloadOldestRequestImages$1(normalized, maxRequestImageBytes$1(), images);
30025
31256
  const gatedEffort = coerceThinkingEffort(options.model, mapReasoningEffort(options.reasoningEffort === void 0 || options.reasoningEffort === null ? void 0 : String(options.reasoningEffort)));
30026
31257
  if (gatedEffort.coercedFrom !== null) recordThinkingCoercion({
@@ -30036,11 +31267,33 @@ var KimiCodeAdapter = class extends LlmAdapter {
30036
31267
  };
30037
31268
  const videos = await resolveRequestVideos(requestOptions, this.options.videos, signal);
30038
31269
  const settings = await this.settings();
30039
- 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 = {
30040
31279
  videos,
30041
- videoAccepted: inputModalitiesForEntry(options.model, catalog).includes("video"),
31280
+ videoAccepted: modalities.includes("video"),
30042
31281
  messageTools: dynamicToolsForEntry(options.model, catalog)
30043
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
+ });
30044
31297
  const contextWindow = this.contextWindowFor(options.model, entry, settings.contextWindowOverrides);
30045
31298
  const requestedMax = options.maxTokens ?? maxOutputTokensFor$4(options.model, contextWindow);
30046
31299
  const inputCap = maxInputTokensForEntry(options.model, catalog);
@@ -30051,29 +31304,57 @@ var KimiCodeAdapter = class extends LlmAdapter {
30051
31304
  };
30052
31305
  const carriesVideo = requestHasVideo(requestOptions);
30053
31306
  const bodyLimit = carriesVideo ? MAX_VIDEO_MESSAGE_BODY_BYTES : maxRequestBodyBytesFor(kimiCodeModelDef(entry?.id ?? options.model), process.env);
30054
- let buildOptions = boundedOptions;
30055
- let body = "";
30056
- for (let attempt = 0;; attempt++) {
30057
- const built = buildRequest(buildOptions, wire, images, void 0, media);
30058
- body = JSON.stringify(built);
30059
- const bytes = Buffer.byteLength(body, "utf8");
30060
- if (bytes <= bodyLimit) break;
30061
- const context = () => ({
30062
- droppedImages: countOmitted(buildOptions, requestOptions, images),
30063
- sendableImagesRemaining: requestImagePayloadLengths(buildOptions, images).length,
30064
- excessBytes: bytes - bodyLimit
30065
- });
30066
- if (attempt > MAX_BODY_FIT_ATTEMPTS || !requestHasSendableImage(buildOptions, images)) {
30067
- assertRequestBodyFits$1(built, carriesVideo, context(), bodyLimit);
30068
- throw new Error("unreachable: assertRequestBodyFits returns or throws");
30069
- }
30070
- const drop = dropOldestImagesUntilFits(buildOptions, images, bytes - bodyLimit, requestOptions);
30071
- if (drop.dropped === 0) {
30072
- assertRequestBodyFits$1(built, carriesVideo, context(), bodyLimit);
30073
- 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;
30074
31345
  }
30075
- buildOptions = drop.options;
30076
- }
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;
30077
31358
  const pool = this.accountPool;
30078
31359
  const tried = /* @__PURE__ */ new Set();
30079
31360
  let servedByAccountId;
@@ -30107,19 +31388,69 @@ var KimiCodeAdapter = class extends LlmAdapter {
30107
31388
  const base = (apiCredentials.baseUrl ?? codingBaseUrl(region)).replace(/\/+$/, "");
30108
31389
  const endpoint = wire === "anthropic" ? `${base}/v1/messages?beta=true` : `${base}/v1/chat/completions`;
30109
31390
  const headers = await modelRequestHeaders$1(apiCredentials.accessToken, wire);
30110
- try {
30111
- response = await fetchWithContextBudgetRecovery(fetchFn)(endpoint, {
30112
- method: "POST",
30113
- headers,
30114
- body,
30115
- 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
30116
31440
  });
30117
- } catch (error) {
30118
- if (signal.aborted) throw new LlmError("Kimi Code request aborted", "ABORTED", { cause: error });
30119
- 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
+ }
30120
31452
  }
30121
31453
  if (response.ok) break;
30122
- const detail = (await response.text().catch(() => "")).slice(0, 2e3);
30123
31454
  const failure = classifyKimiFailure(response.status, detail);
30124
31455
  const after = response.status === 429 ? retryAfterMs$1(response.headers) : void 0;
30125
31456
  if (pool !== null && accountId !== void 0) {
@@ -62224,22 +63555,27 @@ var ClinePassAdapter = class extends LlmAdapter {
62224
63555
  const pinned = injectPrefs(body, meta, attempt);
62225
63556
  let response;
62226
63557
  let transportError;
62227
- for (let transportTry = 0; transportTry <= TRANSPORT_RETRIES$1; transportTry += 1) try {
62228
- response = await this.engine.openStream(account, pinned, withAttemptTimeout$1(upstream));
62229
- break;
62230
- } catch (error) {
62231
- if (signal.aborted) throw new LlmError("Cline Pass request aborted by caller", "ABORTED", { cause: error });
62232
- transportError = error;
62233
- if (transportTry < TRANSPORT_RETRIES$1 && isPreConnectionFailure$1(error)) {
62234
- try {
62235
- await setTimeout$1(TRANSPORT_RETRY_BASE_MS$1 * (transportTry + 1), void 0, { signal: upstream });
62236
- } catch (waitError) {
62237
- if (signal.aborted) throw new LlmError("Cline Pass request aborted by caller", "ABORTED", { cause: waitError });
62238
- 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;
62239
63576
  }
62240
- continue;
63577
+ break;
62241
63578
  }
62242
- break;
62243
63579
  }
62244
63580
  if (response === void 0) {
62245
63581
  const note = errorChain$2(transportError);
@@ -62418,9 +63754,16 @@ var ClinePassAdapter = class extends LlmAdapter {
62418
63754
  await this.accountPool.markCooldown(accountId, POOL_COOLDOWN_MS, `${PROVIDER_NAME$4} quota exhausted`).catch(() => void 0);
62419
63755
  }
62420
63756
  };
62421
- /** A caller signal combined with the attempt's header deadline. */
62422
- function withAttemptTimeout$1(signal) {
62423
- 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
+ };
62424
63767
  }
62425
63768
  /**
62426
63769
  * Whether a transport failure happened before any request was sent.
@@ -75906,9 +77249,8 @@ function delegationDenialReason(agent, toolName, args, preference, sessions, too
75906
77249
  return inheritedRouteDenial(agent, toolName, allowed);
75907
77250
  }
75908
77251
  if (declaresRouteParameters?.(agent, toolName) === false) {
75909
- if (requestedProvider !== void 0 && requestedModel !== void 0) return inheritOverrideReason(toolName, requestedProvider, requestedModel);
75910
- if (requestedProvider !== void 0 || requestedModel !== void 0) return routeFieldsUnavailableReason(toolName, inheritedRouteOf(agent), allowed);
75911
- return inheritedRouteDenial(agent, toolName, allowed);
77252
+ if (requestedProvider === void 0 && requestedModel === void 0) return inheritedRouteDenial(agent, toolName, allowed);
77253
+ if (requestedProvider === void 0 || requestedModel === void 0) return routeFieldsUnavailableReason(toolName, inheritedRouteOf(agent), allowed);
75912
77254
  }
75913
77255
  if (requestedProvider === void 0) return requestedModel === void 0 ? missingRouteReason(allowed) : partialRouteReason("provider", requestedModel, allowed);
75914
77256
  if (requestedModel === void 0) return partialRouteReason("model", requestedProvider, allowed);
@@ -75990,6 +77332,8 @@ function routeParameterProbe(ctx) {
75990
77332
  if (definition === void 0) return void 0;
75991
77333
  const parameters = asRecord$1(definition.parameters);
75992
77334
  if (parameters === void 0) return void 0;
77335
+ const properties = asRecord$1(parameters["properties"]);
77336
+ if (properties !== void 0) return "provider" in properties || "model" in properties;
75993
77337
  return "provider" in parameters || "model" in parameters;
75994
77338
  };
75995
77339
  }