@ljoukov/llm 5.0.3 → 6.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -318,11 +318,6 @@ function getGeminiImagePricing(modelId) {
318
318
  }
319
319
 
320
320
  // src/openai/pricing.ts
321
- var OPENAI_GPT_52_PRICING = {
322
- inputRate: 1.75 / 1e6,
323
- cachedRate: 0.175 / 1e6,
324
- outputRate: 14 / 1e6
325
- };
326
321
  var OPENAI_GPT_54_PRICING = {
327
322
  inputRate: 2.5 / 1e6,
328
323
  cachedRate: 0.25 / 1e6,
@@ -333,37 +328,31 @@ var OPENAI_GPT_54_PRIORITY_PRICING = {
333
328
  cachedRate: 0.5 / 1e6,
334
329
  outputRate: 30 / 1e6
335
330
  };
336
- var OPENAI_GPT_53_CODEX_PRICING = {
337
- inputRate: 1.25 / 1e6,
338
- cachedRate: 0.125 / 1e6,
339
- outputRate: 10 / 1e6
340
- };
341
- var OPENAI_GPT_5_MINI_PRICING = {
331
+ var OPENAI_GPT_54_MINI_PRICING = {
342
332
  inputRate: 0.25 / 1e6,
343
333
  cachedRate: 0.025 / 1e6,
344
334
  outputRate: 2 / 1e6
345
335
  };
336
+ var OPENAI_GPT_54_NANO_PRICING = {
337
+ inputRate: 0.05 / 1e6,
338
+ cachedRate: 5e-3 / 1e6,
339
+ outputRate: 0.4 / 1e6
340
+ };
346
341
  function getOpenAiPricing(modelId) {
347
342
  if (modelId.includes("gpt-5.4-fast")) {
348
343
  return OPENAI_GPT_54_PRIORITY_PRICING;
349
344
  }
350
- if (modelId.includes("gpt-5.4")) {
351
- return OPENAI_GPT_54_PRICING;
352
- }
353
- if (modelId.includes("gpt-5.3-codex-spark")) {
354
- return OPENAI_GPT_5_MINI_PRICING;
355
- }
356
- if (modelId.includes("gpt-5.3-codex")) {
357
- return OPENAI_GPT_53_CODEX_PRICING;
345
+ if (modelId.includes("gpt-5.4-mini")) {
346
+ return OPENAI_GPT_54_MINI_PRICING;
358
347
  }
359
- if (modelId.includes("gpt-5.2")) {
360
- return OPENAI_GPT_52_PRICING;
348
+ if (modelId.includes("gpt-5.4-nano")) {
349
+ return OPENAI_GPT_54_NANO_PRICING;
361
350
  }
362
- if (modelId.includes("gpt-5-mini")) {
363
- return OPENAI_GPT_5_MINI_PRICING;
351
+ if (modelId.includes("gpt-5.3-codex-spark")) {
352
+ return OPENAI_GPT_54_MINI_PRICING;
364
353
  }
365
- if (modelId.includes("gpt-5.1-codex-mini")) {
366
- return OPENAI_GPT_5_MINI_PRICING;
354
+ if (modelId.includes("gpt-5.4")) {
355
+ return OPENAI_GPT_54_PRICING;
367
356
  }
368
357
  return void 0;
369
358
  }
@@ -2832,22 +2821,15 @@ async function runOpenAiCall(fn, modelId, runOptions) {
2832
2821
  }
2833
2822
 
2834
2823
  // src/openai/models.ts
2835
- var OPENAI_MODEL_IDS = [
2836
- "gpt-5.4",
2837
- "gpt-5.3-codex",
2838
- "gpt-5.2",
2839
- "gpt-5.1-codex-mini"
2840
- ];
2824
+ var OPENAI_MODEL_IDS = ["gpt-5.4", "gpt-5.4-mini", "gpt-5.4-nano"];
2841
2825
  function isOpenAiModelId(value) {
2842
2826
  return OPENAI_MODEL_IDS.includes(value);
2843
2827
  }
2844
2828
  var CHATGPT_MODEL_IDS = [
2845
2829
  "chatgpt-gpt-5.4",
2846
2830
  "chatgpt-gpt-5.4-fast",
2847
- "chatgpt-gpt-5.3-codex",
2848
- "chatgpt-gpt-5.3-codex-spark",
2849
- "chatgpt-gpt-5.2",
2850
- "chatgpt-gpt-5.1-codex-mini"
2831
+ "chatgpt-gpt-5.4-mini",
2832
+ "chatgpt-gpt-5.3-codex-spark"
2851
2833
  ];
2852
2834
  function isChatGptModelId(value) {
2853
2835
  return CHATGPT_MODEL_IDS.includes(value);
@@ -4531,6 +4513,72 @@ function parseCanonicalGeminiFileId(fileUri) {
4531
4513
  const fileId = fileUri.slice(CANONICAL_GEMINI_FILE_URI_PREFIX.length).trim();
4532
4514
  return fileId.length > 0 ? fileId : void 0;
4533
4515
  }
4516
+ function isLlmMediaResolution(value) {
4517
+ return value === "auto" || value === "low" || value === "medium" || value === "high" || value === "original";
4518
+ }
4519
+ function resolveEffectiveMediaResolution(detail, fallback) {
4520
+ return detail ?? fallback;
4521
+ }
4522
+ function supportsOpenAiOriginalImageDetail(model) {
4523
+ if (!model) {
4524
+ return false;
4525
+ }
4526
+ const providerModel = isChatGptModelId(model) ? resolveChatGptProviderModel(model) : model;
4527
+ const match = /^gpt-(\d+)(?:\.(\d+))?/u.exec(providerModel);
4528
+ if (!match) {
4529
+ return false;
4530
+ }
4531
+ const major = Number(match[1]);
4532
+ const minor = Number(match[2] ?? "0");
4533
+ if (!Number.isFinite(major) || !Number.isFinite(minor)) {
4534
+ return false;
4535
+ }
4536
+ return major > 5 || major === 5 && minor >= 4;
4537
+ }
4538
+ function toOpenAiImageDetail(mediaResolution, model) {
4539
+ switch (mediaResolution) {
4540
+ case "low":
4541
+ return "low";
4542
+ case "medium":
4543
+ return "high";
4544
+ case "high":
4545
+ return "high";
4546
+ case "original":
4547
+ return supportsOpenAiOriginalImageDetail(model) ? "original" : "high";
4548
+ case "auto":
4549
+ default:
4550
+ return "auto";
4551
+ }
4552
+ }
4553
+ function toGeminiMediaResolution(mediaResolution) {
4554
+ switch (mediaResolution) {
4555
+ case "low":
4556
+ return import_genai2.MediaResolution.MEDIA_RESOLUTION_LOW;
4557
+ case "medium":
4558
+ return import_genai2.MediaResolution.MEDIA_RESOLUTION_MEDIUM;
4559
+ case "high":
4560
+ case "original":
4561
+ return import_genai2.MediaResolution.MEDIA_RESOLUTION_HIGH;
4562
+ case "auto":
4563
+ default:
4564
+ return void 0;
4565
+ }
4566
+ }
4567
+ function toGeminiPartMediaResolution(mediaResolution) {
4568
+ switch (mediaResolution) {
4569
+ case "low":
4570
+ return import_genai2.PartMediaResolutionLevel.MEDIA_RESOLUTION_LOW;
4571
+ case "medium":
4572
+ return import_genai2.PartMediaResolutionLevel.MEDIA_RESOLUTION_MEDIUM;
4573
+ case "high":
4574
+ return import_genai2.PartMediaResolutionLevel.MEDIA_RESOLUTION_HIGH;
4575
+ case "original":
4576
+ return import_genai2.PartMediaResolutionLevel.MEDIA_RESOLUTION_ULTRA_HIGH;
4577
+ case "auto":
4578
+ default:
4579
+ return void 0;
4580
+ }
4581
+ }
4534
4582
  function cloneContentPart(part) {
4535
4583
  switch (part.type) {
4536
4584
  case "text":
@@ -4659,7 +4707,8 @@ function convertGeminiContentToLlmContent(content) {
4659
4707
  parts: convertGooglePartsToLlmParts(content.parts ?? [])
4660
4708
  };
4661
4709
  }
4662
- function toGeminiPart(part) {
4710
+ function toGeminiPart(part, options) {
4711
+ const defaultMediaResolution = options?.defaultMediaResolution;
4663
4712
  switch (part.type) {
4664
4713
  case "text":
4665
4714
  return {
@@ -4667,6 +4716,18 @@ function toGeminiPart(part) {
4667
4716
  thought: part.thought === true ? true : void 0
4668
4717
  };
4669
4718
  case "inlineData": {
4719
+ if (isInlineImageMime(part.mimeType)) {
4720
+ const mimeType = part.mimeType ?? "application/octet-stream";
4721
+ const geminiPart = (0, import_genai2.createPartFromBase64)(
4722
+ part.data,
4723
+ mimeType,
4724
+ toGeminiPartMediaResolution(defaultMediaResolution)
4725
+ );
4726
+ if (part.filename && geminiPart.inlineData) {
4727
+ geminiPart.inlineData.displayName = part.filename;
4728
+ }
4729
+ return geminiPart;
4730
+ }
4670
4731
  const inlineData = {
4671
4732
  data: part.data,
4672
4733
  mimeType: part.mimeType
@@ -4679,31 +4740,35 @@ function toGeminiPart(part) {
4679
4740
  };
4680
4741
  }
4681
4742
  case "input_image": {
4743
+ const mediaResolution = resolveEffectiveMediaResolution(part.detail, defaultMediaResolution);
4744
+ const geminiPartMediaResolution = toGeminiPartMediaResolution(mediaResolution);
4682
4745
  if (part.file_id) {
4683
- return {
4684
- fileData: {
4685
- fileUri: buildCanonicalGeminiFileUri(part.file_id),
4686
- mimeType: inferToolOutputMimeTypeFromFilename(part.filename) ?? "application/octet-stream"
4687
- }
4688
- };
4746
+ return (0, import_genai2.createPartFromUri)(
4747
+ buildCanonicalGeminiFileUri(part.file_id),
4748
+ inferToolOutputMimeTypeFromFilename(part.filename) ?? "application/octet-stream",
4749
+ geminiPartMediaResolution
4750
+ );
4689
4751
  }
4690
4752
  if (typeof part.image_url !== "string" || part.image_url.trim().length === 0) {
4691
4753
  throw new Error("input_image requires image_url or file_id.");
4692
4754
  }
4693
4755
  const parsed = parseDataUrlPayload(part.image_url);
4694
4756
  if (parsed) {
4695
- const geminiPart = (0, import_genai2.createPartFromBase64)(parsed.dataBase64, parsed.mimeType);
4757
+ const geminiPart = (0, import_genai2.createPartFromBase64)(
4758
+ parsed.dataBase64,
4759
+ parsed.mimeType,
4760
+ geminiPartMediaResolution
4761
+ );
4696
4762
  if (part.filename && geminiPart.inlineData) {
4697
4763
  geminiPart.inlineData.displayName = part.filename;
4698
4764
  }
4699
4765
  return geminiPart;
4700
4766
  }
4701
- return {
4702
- fileData: {
4703
- fileUri: part.image_url,
4704
- mimeType: inferToolOutputMimeTypeFromFilename(part.filename) ?? "application/octet-stream"
4705
- }
4706
- };
4767
+ return (0, import_genai2.createPartFromUri)(
4768
+ part.image_url,
4769
+ inferToolOutputMimeTypeFromFilename(part.filename) ?? "application/octet-stream",
4770
+ geminiPartMediaResolution
4771
+ );
4707
4772
  }
4708
4773
  case "input_file": {
4709
4774
  if (part.file_id) {
@@ -4746,11 +4811,11 @@ function toGeminiPart(part) {
4746
4811
  throw new Error("Unsupported LLM content part");
4747
4812
  }
4748
4813
  }
4749
- function convertLlmContentToGeminiContent(content) {
4814
+ function convertLlmContentToGeminiContent(content, options) {
4750
4815
  const role = content.role === "assistant" ? "model" : "user";
4751
4816
  return {
4752
4817
  role,
4753
- parts: content.parts.map(toGeminiPart)
4818
+ parts: content.parts.map((part) => toGeminiPart(part, options))
4754
4819
  };
4755
4820
  }
4756
4821
  function resolveProvider(model) {
@@ -4931,7 +4996,7 @@ async function storeCanonicalPromptFile(options) {
4931
4996
  mimeType: options.mimeType
4932
4997
  };
4933
4998
  }
4934
- async function prepareOpenAiPromptContentItem(item) {
4999
+ async function prepareOpenAiPromptContentItem(item, options) {
4935
5000
  if (!isOpenAiNativeContentItem(item)) {
4936
5001
  return item;
4937
5002
  }
@@ -4950,7 +5015,10 @@ async function prepareOpenAiPromptContentItem(item) {
4950
5015
  });
4951
5016
  return {
4952
5017
  type: "input_image",
4953
- detail: item.detail === "high" || item.detail === "low" ? item.detail : "auto",
5018
+ detail: toOpenAiImageDetail(
5019
+ isLlmMediaResolution(item.detail) ? item.detail : void 0,
5020
+ options?.model
5021
+ ),
4954
5022
  file_id: uploaded.fileId
4955
5023
  };
4956
5024
  }
@@ -4987,7 +5055,7 @@ async function prepareOpenAiPromptContentItem(item) {
4987
5055
  }
4988
5056
  return item;
4989
5057
  }
4990
- async function prepareOpenAiPromptInput(input) {
5058
+ async function prepareOpenAiPromptInput(input, options) {
4991
5059
  const prepareItem = async (item) => {
4992
5060
  if (!item || typeof item !== "object") {
4993
5061
  return item;
@@ -4997,7 +5065,7 @@ async function prepareOpenAiPromptInput(input) {
4997
5065
  return {
4998
5066
  ...record,
4999
5067
  content: await Promise.all(
5000
- record.content.map((part) => prepareOpenAiPromptContentItem(part))
5068
+ record.content.map((part) => prepareOpenAiPromptContentItem(part, options))
5001
5069
  )
5002
5070
  };
5003
5071
  }
@@ -5005,19 +5073,19 @@ async function prepareOpenAiPromptInput(input) {
5005
5073
  return {
5006
5074
  ...record,
5007
5075
  output: await Promise.all(
5008
- record.output.map((part) => prepareOpenAiPromptContentItem(part))
5076
+ record.output.map((part) => prepareOpenAiPromptContentItem(part, options))
5009
5077
  )
5010
5078
  };
5011
5079
  }
5012
- return await prepareOpenAiPromptContentItem(item);
5080
+ return await prepareOpenAiPromptContentItem(item, options);
5013
5081
  };
5014
5082
  return await Promise.all(input.map((item) => prepareItem(item)));
5015
5083
  }
5016
- async function maybePrepareOpenAiPromptInput(input) {
5084
+ async function maybePrepareOpenAiPromptInput(input, options) {
5017
5085
  if (estimateOpenAiInlinePromptBytes(input) <= INLINE_ATTACHMENT_PROMPT_THRESHOLD_BYTES) {
5018
5086
  return Array.from(input);
5019
5087
  }
5020
- return await prepareOpenAiPromptInput(input);
5088
+ return await prepareOpenAiPromptInput(input, options);
5021
5089
  }
5022
5090
  function estimateGeminiInlinePromptBytes(contents) {
5023
5091
  let total = 0;
@@ -5048,22 +5116,25 @@ async function prepareGeminiPromptContents(contents) {
5048
5116
  for (const part of content.parts ?? []) {
5049
5117
  const canonicalFileId = parseCanonicalGeminiFileId(part.fileData?.fileUri);
5050
5118
  if (canonicalFileId) {
5119
+ const mediaResolution = part.mediaResolution?.level;
5051
5120
  await getCanonicalFileMetadata(canonicalFileId);
5052
5121
  if (backend === "api") {
5053
5122
  const mirrored = await ensureGeminiFileMirror(canonicalFileId);
5054
- parts.push((0, import_genai2.createPartFromUri)(mirrored.uri, mirrored.mimeType));
5123
+ parts.push((0, import_genai2.createPartFromUri)(mirrored.uri, mirrored.mimeType, mediaResolution));
5055
5124
  } else {
5056
5125
  const mirrored = await ensureVertexFileMirror(canonicalFileId);
5057
5126
  parts.push({
5058
5127
  fileData: {
5059
5128
  fileUri: mirrored.fileUri,
5060
5129
  mimeType: mirrored.mimeType
5061
- }
5130
+ },
5131
+ ...mediaResolution ? { mediaResolution: { level: mediaResolution } } : {}
5062
5132
  });
5063
5133
  }
5064
5134
  continue;
5065
5135
  }
5066
5136
  if (part.inlineData?.data) {
5137
+ const mediaResolution = part.mediaResolution?.level;
5067
5138
  const mimeType = part.inlineData.mimeType ?? "application/octet-stream";
5068
5139
  const filename = normaliseAttachmentFilename(
5069
5140
  getInlineAttachmentFilename(part.inlineData) ?? part.inlineData.displayName ?? guessInlineDataFilename(mimeType),
@@ -5076,14 +5147,15 @@ async function prepareGeminiPromptContents(contents) {
5076
5147
  });
5077
5148
  if (backend === "api") {
5078
5149
  const mirrored = await ensureGeminiFileMirror(stored.fileId);
5079
- parts.push((0, import_genai2.createPartFromUri)(mirrored.uri, mirrored.mimeType));
5150
+ parts.push((0, import_genai2.createPartFromUri)(mirrored.uri, mirrored.mimeType, mediaResolution));
5080
5151
  } else {
5081
5152
  const mirrored = await ensureVertexFileMirror(stored.fileId);
5082
5153
  parts.push({
5083
5154
  fileData: {
5084
5155
  fileUri: mirrored.fileUri,
5085
5156
  mimeType: mirrored.mimeType
5086
- }
5157
+ },
5158
+ ...mediaResolution ? { mediaResolution: { level: mediaResolution } } : {}
5087
5159
  });
5088
5160
  }
5089
5161
  continue;
@@ -5546,7 +5618,7 @@ function resolveTextContents(input) {
5546
5618
  }
5547
5619
  return contents;
5548
5620
  }
5549
- function toOpenAiInput(contents) {
5621
+ function toOpenAiInput(contents, options) {
5550
5622
  const OPENAI_ROLE_FROM_LLM = {
5551
5623
  user: "user",
5552
5624
  assistant: "assistant",
@@ -5554,6 +5626,8 @@ function toOpenAiInput(contents) {
5554
5626
  developer: "developer",
5555
5627
  tool: "assistant"
5556
5628
  };
5629
+ const defaultMediaResolution = options?.defaultMediaResolution;
5630
+ const model = options?.model;
5557
5631
  return contents.map((content) => {
5558
5632
  const parts = [];
5559
5633
  for (const part of content.parts) {
@@ -5568,7 +5642,7 @@ function toOpenAiInput(contents) {
5568
5642
  const imagePart = {
5569
5643
  type: "input_image",
5570
5644
  image_url: dataUrl,
5571
- detail: "auto"
5645
+ detail: toOpenAiImageDetail(defaultMediaResolution, model)
5572
5646
  };
5573
5647
  setInlineAttachmentFilename(
5574
5648
  imagePart,
@@ -5585,11 +5659,15 @@ function toOpenAiInput(contents) {
5585
5659
  break;
5586
5660
  }
5587
5661
  case "input_image": {
5662
+ const mediaResolution = resolveEffectiveMediaResolution(
5663
+ part.detail,
5664
+ defaultMediaResolution
5665
+ );
5588
5666
  const imagePart = {
5589
5667
  type: "input_image",
5590
5668
  ...part.file_id ? { file_id: part.file_id } : {},
5591
5669
  ...part.image_url ? { image_url: part.image_url } : {},
5592
- detail: part.detail === "high" || part.detail === "low" ? part.detail : "auto"
5670
+ detail: toOpenAiImageDetail(mediaResolution, model)
5593
5671
  };
5594
5672
  if (part.filename) {
5595
5673
  setInlineAttachmentFilename(imagePart, part.filename);
@@ -5622,9 +5700,11 @@ function toOpenAiInput(contents) {
5622
5700
  };
5623
5701
  });
5624
5702
  }
5625
- function toChatGptInput(contents) {
5703
+ function toChatGptInput(contents, options) {
5626
5704
  const instructionsParts = [];
5627
5705
  const input = [];
5706
+ const defaultMediaResolution = options?.defaultMediaResolution;
5707
+ const model = options?.model;
5628
5708
  for (const content of contents) {
5629
5709
  if (content.role === "system" || content.role === "developer") {
5630
5710
  for (const part of content.parts) {
@@ -5660,7 +5740,7 @@ function toChatGptInput(contents) {
5660
5740
  parts.push({
5661
5741
  type: "input_image",
5662
5742
  image_url: dataUrl,
5663
- detail: "auto"
5743
+ detail: toOpenAiImageDetail(defaultMediaResolution, model)
5664
5744
  });
5665
5745
  } else {
5666
5746
  parts.push({
@@ -5674,14 +5754,19 @@ function toChatGptInput(contents) {
5674
5754
  }
5675
5755
  break;
5676
5756
  }
5677
- case "input_image":
5757
+ case "input_image": {
5758
+ const mediaResolution = resolveEffectiveMediaResolution(
5759
+ part.detail,
5760
+ defaultMediaResolution
5761
+ );
5678
5762
  parts.push({
5679
5763
  type: "input_image",
5680
5764
  ...part.file_id ? { file_id: part.file_id } : {},
5681
5765
  ...part.image_url ? { image_url: part.image_url } : {},
5682
- detail: part.detail === "high" || part.detail === "low" ? part.detail : "auto"
5766
+ detail: toOpenAiImageDetail(mediaResolution, model)
5683
5767
  });
5684
5768
  break;
5769
+ }
5685
5770
  case "input_file":
5686
5771
  parts.push({
5687
5772
  type: "input_file",
@@ -6074,6 +6159,9 @@ function isLlmToolOutputContentItem(value) {
6074
6159
  return false;
6075
6160
  }
6076
6161
  }
6162
+ if (value.detail !== void 0 && value.detail !== null && !isLlmMediaResolution(value.detail)) {
6163
+ return false;
6164
+ }
6077
6165
  return value.image_url !== void 0 || value.file_id !== void 0;
6078
6166
  }
6079
6167
  if (itemType === "input_file") {
@@ -6088,17 +6176,30 @@ function isLlmToolOutputContentItem(value) {
6088
6176
  }
6089
6177
  return false;
6090
6178
  }
6091
- function toOpenAiToolOutput(value) {
6179
+ function toOpenAiToolOutput(value, options) {
6180
+ const normalizeImageItem = (item) => {
6181
+ if (item.type !== "input_image") {
6182
+ return item;
6183
+ }
6184
+ const mediaResolution = resolveEffectiveMediaResolution(
6185
+ item.detail,
6186
+ options?.defaultMediaResolution
6187
+ );
6188
+ return {
6189
+ ...item,
6190
+ detail: toOpenAiImageDetail(mediaResolution, options?.model)
6191
+ };
6192
+ };
6092
6193
  if (isLlmToolOutputContentItem(value)) {
6093
- return [value];
6194
+ return [normalizeImageItem(value)];
6094
6195
  }
6095
6196
  if (Array.isArray(value) && value.every((item) => isLlmToolOutputContentItem(item))) {
6096
- return value;
6197
+ return value.map((item) => normalizeImageItem(item));
6097
6198
  }
6098
6199
  return mergeToolOutput(value);
6099
6200
  }
6100
- function toChatGptToolOutput(value) {
6101
- const toolOutput = toOpenAiToolOutput(value);
6201
+ function toChatGptToolOutput(value, options) {
6202
+ const toolOutput = toOpenAiToolOutput(value, options);
6102
6203
  if (typeof toolOutput === "string") {
6103
6204
  return toolOutput;
6104
6205
  }
@@ -6110,7 +6211,12 @@ function toChatGptToolOutput(value) {
6110
6211
  type: "input_image",
6111
6212
  ...item.file_id ? { file_id: item.file_id } : {},
6112
6213
  ...item.image_url ? { image_url: item.image_url } : {},
6113
- ...item.detail ? { detail: item.detail } : {}
6214
+ ...item.detail ? {
6215
+ detail: toOpenAiImageDetail(
6216
+ resolveEffectiveMediaResolution(item.detail, options?.defaultMediaResolution),
6217
+ options?.model
6218
+ )
6219
+ } : {}
6114
6220
  };
6115
6221
  });
6116
6222
  }
@@ -6369,34 +6475,41 @@ async function maybeSpillCombinedToolCallOutputs(callResults, options) {
6369
6475
  })
6370
6476
  );
6371
6477
  }
6372
- function buildGeminiToolOutputMediaPart(item) {
6478
+ function buildGeminiToolOutputMediaPart(item, options) {
6373
6479
  if (item.type === "input_image") {
6480
+ const mediaResolution = resolveEffectiveMediaResolution(
6481
+ item.detail,
6482
+ options?.defaultMediaResolution
6483
+ );
6484
+ const geminiPartMediaResolution = toGeminiPartMediaResolution(mediaResolution);
6374
6485
  if (typeof item.file_id === "string" && item.file_id.trim().length > 0) {
6375
- return {
6376
- fileData: {
6377
- fileUri: buildCanonicalGeminiFileUri(item.file_id),
6378
- mimeType: inferToolOutputMimeTypeFromFilename(item.filename) ?? "application/octet-stream"
6379
- }
6380
- };
6486
+ return (0, import_genai2.createPartFromUri)(
6487
+ buildCanonicalGeminiFileUri(item.file_id),
6488
+ inferToolOutputMimeTypeFromFilename(item.filename) ?? "application/octet-stream",
6489
+ geminiPartMediaResolution
6490
+ );
6381
6491
  }
6382
6492
  if (typeof item.image_url !== "string" || item.image_url.trim().length === 0) {
6383
6493
  return null;
6384
6494
  }
6385
6495
  const parsed = parseDataUrlPayload(item.image_url);
6386
6496
  if (parsed) {
6387
- const part = (0, import_genai2.createPartFromBase64)(parsed.dataBase64, parsed.mimeType);
6497
+ const part = (0, import_genai2.createPartFromBase64)(
6498
+ parsed.dataBase64,
6499
+ parsed.mimeType,
6500
+ geminiPartMediaResolution
6501
+ );
6388
6502
  const displayName = item.filename?.trim();
6389
6503
  if (displayName && part.inlineData) {
6390
6504
  part.inlineData.displayName = displayName;
6391
6505
  }
6392
6506
  return part;
6393
6507
  }
6394
- return {
6395
- fileData: {
6396
- fileUri: item.image_url,
6397
- mimeType: inferToolOutputMimeTypeFromFilename(item.filename) ?? "application/octet-stream"
6398
- }
6399
- };
6508
+ return (0, import_genai2.createPartFromUri)(
6509
+ item.image_url,
6510
+ inferToolOutputMimeTypeFromFilename(item.filename) ?? "application/octet-stream",
6511
+ geminiPartMediaResolution
6512
+ );
6400
6513
  }
6401
6514
  if (item.type === "input_file") {
6402
6515
  if (typeof item.file_id === "string" && item.file_id.trim().length > 0) {
@@ -6474,7 +6587,9 @@ function buildGeminiFunctionResponseParts(options) {
6474
6587
  }
6475
6588
  const responseOutput = outputItems.map((item) => toGeminiToolOutputPlaceholder(item));
6476
6589
  const responseParts = outputItems.flatMap((item) => {
6477
- const mediaPart = buildGeminiToolOutputMediaPart(item);
6590
+ const mediaPart = buildGeminiToolOutputMediaPart(item, {
6591
+ defaultMediaResolution: options.defaultMediaResolution
6592
+ });
6478
6593
  return mediaPart ? [mediaPart] : [];
6479
6594
  });
6480
6595
  const responsePayload = { output: responseOutput };
@@ -7241,6 +7356,7 @@ function startLlmCallLoggerFromContents(options) {
7241
7356
  ...options.request.imageAspectRatio ? { imageAspectRatio: options.request.imageAspectRatio } : {},
7242
7357
  ...options.request.imageSize ? { imageSize: options.request.imageSize } : {},
7243
7358
  ...options.request.thinkingLevel ? { thinkingLevel: options.request.thinkingLevel } : {},
7359
+ ...options.request.mediaResolution ? { mediaResolution: options.request.mediaResolution } : {},
7244
7360
  ...options.request.openAiTextFormat ? { openAiTextFormat: sanitiseLogValue(options.request.openAiTextFormat) } : {},
7245
7361
  ...getCurrentToolCallContext() ? { toolContext: getCurrentToolCallContext() } : {}
7246
7362
  },
@@ -7351,7 +7467,13 @@ async function runTextCall(params) {
7351
7467
  const { result } = await collectFileUploadMetrics(async () => {
7352
7468
  try {
7353
7469
  if (provider === "openai") {
7354
- const openAiInput = await maybePrepareOpenAiPromptInput(toOpenAiInput(contents));
7470
+ const openAiInput = await maybePrepareOpenAiPromptInput(
7471
+ toOpenAiInput(contents, {
7472
+ defaultMediaResolution: request.mediaResolution,
7473
+ model: request.model
7474
+ }),
7475
+ { model: request.model }
7476
+ );
7355
7477
  const openAiTools = toOpenAiTools(request.tools);
7356
7478
  const reasoningEffort = resolveOpenAiReasoningEffort(
7357
7479
  modelForProvider,
@@ -7425,7 +7547,10 @@ async function runTextCall(params) {
7425
7547
  }
7426
7548
  }, modelForProvider);
7427
7549
  } else if (provider === "chatgpt") {
7428
- const chatGptInput = toChatGptInput(contents);
7550
+ const chatGptInput = toChatGptInput(contents, {
7551
+ defaultMediaResolution: request.mediaResolution,
7552
+ model: request.model
7553
+ });
7429
7554
  const reasoningEffort = resolveOpenAiReasoningEffort(request.model, request.thinkingLevel);
7430
7555
  const openAiTools = toOpenAiTools(request.tools);
7431
7556
  const requestPayload = {
@@ -7522,12 +7647,18 @@ async function runTextCall(params) {
7522
7647
  }, modelForProvider);
7523
7648
  } else {
7524
7649
  const geminiContents = await maybePrepareGeminiPromptContents(
7525
- contents.map(convertLlmContentToGeminiContent)
7650
+ contents.map(
7651
+ (content2) => convertLlmContentToGeminiContent(content2, {
7652
+ defaultMediaResolution: request.mediaResolution
7653
+ })
7654
+ )
7526
7655
  );
7527
7656
  const thinkingConfig = resolveGeminiThinkingConfig(modelForProvider, request.thinkingLevel);
7657
+ const mediaResolution = toGeminiMediaResolution(request.mediaResolution);
7528
7658
  const config = {
7529
7659
  maxOutputTokens: 32e3,
7530
7660
  ...thinkingConfig ? { thinkingConfig } : {},
7661
+ ...mediaResolution ? { mediaResolution } : {},
7531
7662
  ...request.responseMimeType ? { responseMimeType: request.responseMimeType } : {},
7532
7663
  ...request.responseJsonSchema ? { responseJsonSchema: request.responseJsonSchema } : {},
7533
7664
  ...request.responseModalities ? { responseModalities: Array.from(request.responseModalities) } : {},
@@ -8205,7 +8336,10 @@ async function runToolLoop(request) {
8205
8336
  summary: "detailed"
8206
8337
  };
8207
8338
  let previousResponseId;
8208
- let input = toOpenAiInput(contents);
8339
+ let input = toOpenAiInput(contents, {
8340
+ defaultMediaResolution: request.mediaResolution,
8341
+ model: request.model
8342
+ });
8209
8343
  for (let stepIndex = 0; stepIndex < maxSteps; stepIndex += 1) {
8210
8344
  const turn = stepIndex + 1;
8211
8345
  const stepStartedAtMs = Date.now();
@@ -8232,7 +8366,9 @@ async function runToolLoop(request) {
8232
8366
  let reasoningSummary = "";
8233
8367
  let stepToolCallText;
8234
8368
  let stepToolCallPayload;
8235
- const preparedInput = await maybePrepareOpenAiPromptInput(input);
8369
+ const preparedInput = await maybePrepareOpenAiPromptInput(input, {
8370
+ model: request.model
8371
+ });
8236
8372
  const stepRequestPayload = {
8237
8373
  model: providerInfo.model,
8238
8374
  input: preparedInput,
@@ -8363,7 +8499,10 @@ async function runToolLoop(request) {
8363
8499
  const stepToolCalls = [];
8364
8500
  if (responseToolCalls.length === 0) {
8365
8501
  const steeringInput2 = steeringInternal?.drainPendingContents() ?? [];
8366
- const steeringItems2 = steeringInput2.length > 0 ? toOpenAiInput(steeringInput2) : [];
8502
+ const steeringItems2 = steeringInput2.length > 0 ? toOpenAiInput(steeringInput2, {
8503
+ defaultMediaResolution: request.mediaResolution,
8504
+ model: request.model
8505
+ }) : [];
8367
8506
  finalText = responseText;
8368
8507
  finalThoughts = reasoningSummary;
8369
8508
  const stepCompletedAtMs2 = Date.now();
@@ -8494,13 +8633,19 @@ async function runToolLoop(request) {
8494
8633
  toolOutputs.push({
8495
8634
  type: "custom_tool_call_output",
8496
8635
  call_id: entry.call.call_id,
8497
- output: toOpenAiToolOutput(outputPayload)
8636
+ output: toOpenAiToolOutput(outputPayload, {
8637
+ defaultMediaResolution: request.mediaResolution,
8638
+ model: request.model
8639
+ })
8498
8640
  });
8499
8641
  } else {
8500
8642
  toolOutputs.push({
8501
8643
  type: "function_call_output",
8502
8644
  call_id: entry.call.call_id,
8503
- output: toOpenAiToolOutput(outputPayload)
8645
+ output: toOpenAiToolOutput(outputPayload, {
8646
+ defaultMediaResolution: request.mediaResolution,
8647
+ model: request.model
8648
+ })
8504
8649
  });
8505
8650
  }
8506
8651
  }
@@ -8525,7 +8670,10 @@ async function runToolLoop(request) {
8525
8670
  timing
8526
8671
  });
8527
8672
  const steeringInput = steeringInternal?.drainPendingContents() ?? [];
8528
- const steeringItems = steeringInput.length > 0 ? toOpenAiInput(steeringInput) : [];
8673
+ const steeringItems = steeringInput.length > 0 ? toOpenAiInput(steeringInput, {
8674
+ defaultMediaResolution: request.mediaResolution,
8675
+ model: request.model
8676
+ }) : [];
8529
8677
  stepCallLogger?.complete({
8530
8678
  responseText,
8531
8679
  toolCallText: stepToolCallText,
@@ -8570,7 +8718,10 @@ async function runToolLoop(request) {
8570
8718
  const openAiNativeTools = toOpenAiTools(request.modelTools);
8571
8719
  const openAiTools = openAiNativeTools ? [...openAiNativeTools, ...openAiAgentTools] : [...openAiAgentTools];
8572
8720
  const reasoningEffort = resolveOpenAiReasoningEffort(request.model, request.thinkingLevel);
8573
- const toolLoopInput = toChatGptInput(contents);
8721
+ const toolLoopInput = toChatGptInput(contents, {
8722
+ defaultMediaResolution: request.mediaResolution,
8723
+ model: request.model
8724
+ });
8574
8725
  const conversationId = `tool-loop-${(0, import_node_crypto2.randomBytes)(8).toString("hex")}`;
8575
8726
  const promptCacheKey = conversationId;
8576
8727
  let input = [...toolLoopInput.input];
@@ -8674,7 +8825,10 @@ async function runToolLoop(request) {
8674
8825
  stepToolCallText = serialiseLogArtifactText(stepToolCallPayload);
8675
8826
  if (responseToolCalls.length === 0) {
8676
8827
  const steeringInput2 = steeringInternal?.drainPendingContents() ?? [];
8677
- const steeringItems2 = steeringInput2.length > 0 ? toChatGptInput(steeringInput2).input : [];
8828
+ const steeringItems2 = steeringInput2.length > 0 ? toChatGptInput(steeringInput2, {
8829
+ defaultMediaResolution: request.mediaResolution,
8830
+ model: request.model
8831
+ }).input : [];
8678
8832
  finalText = responseText;
8679
8833
  finalThoughts = reasoningSummaryText;
8680
8834
  const stepCompletedAtMs2 = Date.now();
@@ -8806,7 +8960,10 @@ async function runToolLoop(request) {
8806
8960
  toolOutputs.push({
8807
8961
  type: "custom_tool_call_output",
8808
8962
  call_id: entry.ids.callId,
8809
- output: toChatGptToolOutput(outputPayload)
8963
+ output: toChatGptToolOutput(outputPayload, {
8964
+ defaultMediaResolution: request.mediaResolution,
8965
+ model: request.model
8966
+ })
8810
8967
  });
8811
8968
  } else {
8812
8969
  toolOutputs.push({
@@ -8820,7 +8977,10 @@ async function runToolLoop(request) {
8820
8977
  toolOutputs.push({
8821
8978
  type: "function_call_output",
8822
8979
  call_id: entry.ids.callId,
8823
- output: toChatGptToolOutput(outputPayload)
8980
+ output: toChatGptToolOutput(outputPayload, {
8981
+ defaultMediaResolution: request.mediaResolution,
8982
+ model: request.model
8983
+ })
8824
8984
  });
8825
8985
  }
8826
8986
  }
@@ -8844,7 +9004,10 @@ async function runToolLoop(request) {
8844
9004
  timing
8845
9005
  });
8846
9006
  const steeringInput = steeringInternal?.drainPendingContents() ?? [];
8847
- const steeringItems = steeringInput.length > 0 ? toChatGptInput(steeringInput).input : [];
9007
+ const steeringItems = steeringInput.length > 0 ? toChatGptInput(steeringInput, {
9008
+ defaultMediaResolution: request.mediaResolution,
9009
+ model: request.model
9010
+ }).input : [];
8848
9011
  stepCallLogger?.complete({
8849
9012
  responseText,
8850
9013
  toolCallText: stepToolCallText,
@@ -9175,7 +9338,11 @@ async function runToolLoop(request) {
9175
9338
  const geminiFunctionTools = buildGeminiFunctionDeclarations(request.tools);
9176
9339
  const geminiNativeTools = toGeminiTools(request.modelTools);
9177
9340
  const geminiTools = geminiNativeTools ? geminiNativeTools.concat(geminiFunctionTools) : geminiFunctionTools;
9178
- const geminiContents = contents.map(convertLlmContentToGeminiContent);
9341
+ const geminiContents = contents.map(
9342
+ (content) => convertLlmContentToGeminiContent(content, {
9343
+ defaultMediaResolution: request.mediaResolution
9344
+ })
9345
+ );
9179
9346
  for (let stepIndex = 0; stepIndex < maxSteps; stepIndex += 1) {
9180
9347
  const turn = stepIndex + 1;
9181
9348
  const stepStartedAtMs = Date.now();
@@ -9193,6 +9360,7 @@ async function runToolLoop(request) {
9193
9360
  }
9194
9361
  };
9195
9362
  const thinkingConfig = resolveGeminiThinkingConfig(request.model, request.thinkingLevel);
9363
+ const mediaResolution = toGeminiMediaResolution(request.mediaResolution);
9196
9364
  const config = {
9197
9365
  maxOutputTokens: 32e3,
9198
9366
  tools: geminiTools,
@@ -9201,7 +9369,8 @@ async function runToolLoop(request) {
9201
9369
  mode: import_genai2.FunctionCallingConfigMode.VALIDATED
9202
9370
  }
9203
9371
  },
9204
- ...thinkingConfig ? { thinkingConfig } : {}
9372
+ ...thinkingConfig ? { thinkingConfig } : {},
9373
+ ...mediaResolution ? { mediaResolution } : {}
9205
9374
  };
9206
9375
  const onEvent = request.onEvent;
9207
9376
  const preparedGeminiContents = await maybePrepareGeminiPromptContents(geminiContents);
@@ -9357,7 +9526,13 @@ async function runToolLoop(request) {
9357
9526
  } else if (response.responseText.length > 0) {
9358
9527
  geminiContents.push({ role: "model", parts: [{ text: response.responseText }] });
9359
9528
  }
9360
- geminiContents.push(...steeringInput2.map(convertLlmContentToGeminiContent));
9529
+ geminiContents.push(
9530
+ ...steeringInput2.map(
9531
+ (content) => convertLlmContentToGeminiContent(content, {
9532
+ defaultMediaResolution: request.mediaResolution
9533
+ })
9534
+ )
9535
+ );
9361
9536
  continue;
9362
9537
  }
9363
9538
  const toolCalls = [];
@@ -9449,7 +9624,8 @@ async function runToolLoop(request) {
9449
9624
  ...buildGeminiFunctionResponseParts({
9450
9625
  toolName: entry.toolName,
9451
9626
  callId: entry.call.id,
9452
- outputPayload
9627
+ outputPayload,
9628
+ defaultMediaResolution: request.mediaResolution
9453
9629
  })
9454
9630
  );
9455
9631
  }
@@ -9494,7 +9670,13 @@ async function runToolLoop(request) {
9494
9670
  geminiContents.push({ role: "user", parts: responseParts });
9495
9671
  const steeringInput = steeringInternal?.drainPendingContents() ?? [];
9496
9672
  if (steeringInput.length > 0) {
9497
- geminiContents.push(...steeringInput.map(convertLlmContentToGeminiContent));
9673
+ geminiContents.push(
9674
+ ...steeringInput.map(
9675
+ (content) => convertLlmContentToGeminiContent(content, {
9676
+ defaultMediaResolution: request.mediaResolution
9677
+ })
9678
+ )
9679
+ );
9498
9680
  }
9499
9681
  } catch (error) {
9500
9682
  stepCallLogger?.fail(error, {
@@ -9750,7 +9932,7 @@ async function generateImages(request) {
9750
9932
  }
9751
9933
  return image;
9752
9934
  })(),
9753
- model: "gpt-5.2"
9935
+ model: "gpt-5.4-mini"
9754
9936
  })
9755
9937
  )
9756
9938
  );
@@ -9966,7 +10148,6 @@ var spawnAgentInputSchema = import_zod4.z.object({
9966
10148
  "When true, fork the current thread history into the new agent before sending the initial prompt. This must be used when you want the new agent to have exactly the same context as you."
9967
10149
  ),
9968
10150
  instructions: import_zod4.z.string().nullish().describe("Optional extra instructions for this subagent instance."),
9969
- model: import_zod4.z.string().nullish().describe("Optional model override. Must be one of this package's supported text model ids."),
9970
10151
  max_steps: import_zod4.z.number().int().min(1).max(MAX_SUBAGENT_MAX_STEPS).nullish().describe("Optional max step budget for each subagent run.")
9971
10152
  });
9972
10153
  var sendInputSchema = import_zod4.z.object({
@@ -10057,7 +10238,6 @@ function resolveSubagentToolConfig(selection, currentDepth) {
10057
10238
  maxWaitTimeoutMs,
10058
10239
  promptPattern,
10059
10240
  ...instructions ? { instructions } : {},
10060
- ...config.model ? { model: config.model } : {},
10061
10241
  ...maxSteps ? { maxSteps } : {},
10062
10242
  inheritTools: config.inheritTools !== false,
10063
10243
  inheritFilesystemTool: config.inheritFilesystemTool !== false
@@ -10109,13 +10289,6 @@ function createSubagentToolController(options) {
10109
10289
  `Subagent depth limit reached (${options.config.maxDepth}). Cannot spawn at depth ${childDepth}.`
10110
10290
  );
10111
10291
  }
10112
- let model = options.config.model ?? options.parentModel;
10113
- if (input.model) {
10114
- if (!isLlmTextModelId(input.model)) {
10115
- throw new Error(`Unsupported subagent model id: ${input.model}`);
10116
- }
10117
- model = input.model;
10118
- }
10119
10292
  const id = `agent_${(0, import_node_crypto3.randomBytes)(6).toString("hex")}`;
10120
10293
  const now = Date.now();
10121
10294
  const { roleName, roleInstructions } = resolveAgentType(input.agent_type);
@@ -10135,7 +10308,7 @@ function createSubagentToolController(options) {
10135
10308
  const agent = {
10136
10309
  id,
10137
10310
  depth: childDepth,
10138
- model,
10311
+ model: options.parentModel,
10139
10312
  ...nickname ? { nickname } : {},
10140
10313
  agentRole: roleName,
10141
10314
  status: "idle",
@@ -11890,7 +12063,8 @@ async function viewImageCodex(input, options) {
11890
12063
  return [
11891
12064
  {
11892
12065
  type: "input_image",
11893
- image_url: `data:${mimeType};base64,${bytes.toString("base64")}`
12066
+ image_url: `data:${mimeType};base64,${bytes.toString("base64")}`,
12067
+ ...options.mediaResolution ? { detail: options.mediaResolution } : {}
11894
12068
  }
11895
12069
  ];
11896
12070
  }
@@ -12570,7 +12744,11 @@ async function runAgentLoopInternal(request, context) {
12570
12744
  const toolLoopRequestWithSteering = toolLoopRequest.steering === steeringChannel ? toolLoopRequest : { ...toolLoopRequest, steering: steeringChannel };
12571
12745
  const filesystemSelection = filesystemTool ?? filesystem_tool;
12572
12746
  const subagentSelection = subagentTool ?? subagent_tool ?? subagents;
12573
- const filesystemTools = resolveFilesystemTools(request.model, filesystemSelection);
12747
+ const filesystemTools = resolveFilesystemTools(
12748
+ request.model,
12749
+ filesystemSelection,
12750
+ request.mediaResolution
12751
+ );
12574
12752
  const resolvedSubagentConfig = resolveSubagentToolConfig(subagentSelection, context.depth);
12575
12753
  const subagentController = createSubagentController({
12576
12754
  runId,
@@ -12722,24 +12900,47 @@ async function runAgentLoopInternal(request, context) {
12722
12900
  await subagentController?.closeAll();
12723
12901
  }
12724
12902
  }
12725
- function resolveFilesystemTools(model, selection) {
12903
+ function resolveFilesystemTools(model, selection, defaultMediaResolution) {
12904
+ const withDefaultMediaResolution = (options) => {
12905
+ if (defaultMediaResolution === void 0) {
12906
+ return options;
12907
+ }
12908
+ return {
12909
+ mediaResolution: defaultMediaResolution,
12910
+ ...options ?? {}
12911
+ };
12912
+ };
12726
12913
  if (selection === void 0 || selection === false) {
12727
12914
  return {};
12728
12915
  }
12729
12916
  if (selection === true) {
12730
- return createFilesystemToolSetForModel(model, "auto");
12917
+ return createFilesystemToolSetForModel(model, withDefaultMediaResolution(void 0) ?? {});
12731
12918
  }
12732
12919
  if (typeof selection === "string") {
12733
- return createFilesystemToolSetForModel(model, selection);
12920
+ return createFilesystemToolSetForModel(model, selection, withDefaultMediaResolution(void 0));
12734
12921
  }
12735
12922
  if (selection.enabled === false) {
12736
12923
  return {};
12737
12924
  }
12738
12925
  if (selection.options && selection.profile !== void 0) {
12739
- return createFilesystemToolSetForModel(model, selection.profile, selection.options);
12926
+ return createFilesystemToolSetForModel(
12927
+ model,
12928
+ selection.profile,
12929
+ withDefaultMediaResolution(selection.options)
12930
+ );
12740
12931
  }
12741
12932
  if (selection.options) {
12742
- return createFilesystemToolSetForModel(model, selection.options);
12933
+ return createFilesystemToolSetForModel(
12934
+ model,
12935
+ withDefaultMediaResolution(selection.options) ?? {}
12936
+ );
12937
+ }
12938
+ if (defaultMediaResolution !== void 0) {
12939
+ return createFilesystemToolSetForModel(
12940
+ model,
12941
+ selection.profile ?? "auto",
12942
+ withDefaultMediaResolution(void 0)
12943
+ );
12743
12944
  }
12744
12945
  return createFilesystemToolSetForModel(model, selection.profile ?? "auto");
12745
12946
  }
@@ -12762,7 +12963,7 @@ function createSubagentController(params) {
12762
12963
  return createSubagentToolController({
12763
12964
  config: params.resolvedSubagentConfig,
12764
12965
  parentDepth: params.depth,
12765
- parentModel: params.resolvedSubagentConfig.model ?? params.model,
12966
+ parentModel: params.model,
12766
12967
  forkContextMessages: normalizeForkContextMessages(params.toolLoopRequest.input),
12767
12968
  onBackgroundMessage: (message) => {
12768
12969
  params.steering?.append({ role: "user", content: message });
@@ -12782,6 +12983,7 @@ function createSubagentController(params) {
12782
12983
  modelTools: params.toolLoopRequest.modelTools,
12783
12984
  maxSteps: subagentRequest.maxSteps,
12784
12985
  thinkingLevel: params.toolLoopRequest.thinkingLevel,
12986
+ mediaResolution: params.toolLoopRequest.mediaResolution,
12785
12987
  signal: subagentRequest.signal
12786
12988
  },
12787
12989
  {