omnigateway 0.12.10 → 0.12.12

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.
Files changed (3) hide show
  1. package/bin/omni.js +256 -20
  2. package/gateway.js +377 -32
  3. package/package.json +1 -1
package/bin/omni.js CHANGED
@@ -20257,7 +20257,7 @@ function blockTokens(block) {
20257
20257
  case "toolUse":
20258
20258
  return BLOCK_OVERHEAD + fromText(block.name) + fromText(safeJson(block.input));
20259
20259
  case "toolResult":
20260
- return BLOCK_OVERHEAD + fromText(block.toolUseId) + fromText(block.content);
20260
+ return BLOCK_OVERHEAD + fromText(block.toolUseId) + fromText(block.content) + (block.images?.length ?? 0) * IMAGE_TOKENS + (block.files ?? []).reduce((sum, f) => sum + fromText(f.data), 0) + (block.native ?? []).reduce((sum, n) => sum + blockTokens(n), 0);
20261
20261
  case "providerNative":
20262
20262
  return BLOCK_OVERHEAD + fromText(block.blockType) + fromText(safeJson(block.data));
20263
20263
  }
@@ -20322,6 +20322,9 @@ function estimateCachedInputTokens(request) {
20322
20322
  running += blockTokens(block);
20323
20323
  if (cacheControlOf(block) !== undefined)
20324
20324
  cached2 = running;
20325
+ if (block.type === "toolResult" && [...block.images ?? [], ...block.native ?? []].some((p) => p.cacheControl !== undefined)) {
20326
+ cached2 = running;
20327
+ }
20325
20328
  }
20326
20329
  }
20327
20330
  return cached2;
@@ -27070,6 +27073,69 @@ function systemText(system, provider, note2) {
27070
27073
  `);
27071
27074
  }
27072
27075
 
27076
+ // packages/providers/src/toolResultFiles.ts
27077
+ function isTextual(mediaType) {
27078
+ const type = mediaType.split(";")[0]?.trim().toLowerCase() ?? "";
27079
+ return type.startsWith("text/") || type.endsWith("+json") || type.endsWith("+xml") || TEXTUAL.has(type);
27080
+ }
27081
+ var TEXTUAL = new Set([
27082
+ "application/json",
27083
+ "application/x-ndjson",
27084
+ "application/xml",
27085
+ "application/yaml",
27086
+ "application/x-yaml",
27087
+ "application/toml",
27088
+ "application/javascript",
27089
+ "application/typescript",
27090
+ "application/sql",
27091
+ "application/x-sh",
27092
+ "message/rfc822"
27093
+ ]);
27094
+ function fileText(file2) {
27095
+ return Buffer.from(file2.data, "base64").toString("utf8");
27096
+ }
27097
+ function splitFiles(files, carries) {
27098
+ const text = [];
27099
+ const carried = [];
27100
+ let dropped = false;
27101
+ for (const file2 of files) {
27102
+ if (isTextual(file2.mediaType))
27103
+ text.push(fileText(file2));
27104
+ else if (carries(file2.mediaType))
27105
+ carried.push(file2);
27106
+ else
27107
+ dropped = true;
27108
+ }
27109
+ return { text, carried, dropped };
27110
+ }
27111
+ function withFileText(content, text) {
27112
+ return [content, ...text].filter((t) => t.length > 0).join(`
27113
+
27114
+ `);
27115
+ }
27116
+ var RESPONSES_FILE_TYPES = Object.assign(Object.create(null), {
27117
+ "application/pdf": "pdf",
27118
+ "application/rtf": "rtf",
27119
+ "application/msword": "doc",
27120
+ "application/vnd.ms-excel": "xls",
27121
+ "application/vnd.ms-powerpoint": "ppt",
27122
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document": "docx",
27123
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "xlsx",
27124
+ "application/vnd.openxmlformats-officedocument.presentationml.presentation": "pptx"
27125
+ });
27126
+ function fileName(file2) {
27127
+ return file2.filename ?? `file.${RESPONSES_FILE_TYPES[file2.mediaType] ?? "bin"}`;
27128
+ }
27129
+ function responsesFiles(files) {
27130
+ const { text, carried, dropped } = splitFiles(files, (t) => (t in RESPONSES_FILE_TYPES));
27131
+ const parts = carried.map((file2) => ({
27132
+ type: "input_file",
27133
+ filename: fileName(file2),
27134
+ file_data: `data:${file2.mediaType};base64,${file2.data}`
27135
+ }));
27136
+ return { parts, text, dropped };
27137
+ }
27138
+
27073
27139
  // packages/providers/src/anthropic/wire.ts
27074
27140
  var OAUTH_IDENTITY = "You are Claude Code, Anthropic's official CLI for Claude.";
27075
27141
  function wireCacheControl(c) {
@@ -27106,14 +27172,27 @@ function encodeBlock(b, cloak) {
27106
27172
  input: b.input,
27107
27173
  ...cache
27108
27174
  };
27109
- case "toolResult":
27175
+ case "toolResult": {
27176
+ const files = splitFiles(b.files ?? [], (t) => t === "application/pdf");
27177
+ const text = withFileText(b.content, files.text);
27178
+ const documents = files.carried.map((file2) => ({
27179
+ type: "document",
27180
+ source: { type: "base64", media_type: file2.mediaType, data: file2.data },
27181
+ ...file2.filename !== undefined && { title: file2.filename }
27182
+ }));
27183
+ const parts = [
27184
+ ...(b.images ?? []).map((image) => encodeBlock(image, cloak)),
27185
+ ...documents,
27186
+ ...(b.native ?? []).map((part) => encodeBlock(part, cloak))
27187
+ ];
27110
27188
  return {
27111
27189
  type: "tool_result",
27112
27190
  tool_use_id: b.toolUseId,
27113
- content: b.content,
27191
+ content: parts.length === 0 ? text : [...text.length > 0 ? [{ type: "text", text }] : [], ...parts],
27114
27192
  is_error: b.isError,
27115
27193
  ...cache
27116
27194
  };
27195
+ }
27117
27196
  case "providerNative":
27118
27197
  if (b.provider !== "anthropic") {
27119
27198
  throw new GatewayError("INTERNAL", `anthropic adapter received a ${b.provider} provider-native block`);
@@ -27283,6 +27362,17 @@ function toWire(req, model, opts) {
27283
27362
  const systemCache = systemCacheControl(req);
27284
27363
  if (systemCache.lost)
27285
27364
  note2("anthropic:system-turn-cache-control-dropped");
27365
+ for (const m of req.messages) {
27366
+ for (const b of m.content) {
27367
+ if (b.type === "toolResult" && b.files !== undefined) {
27368
+ const files = splitFiles(b.files, (t) => t === "application/pdf");
27369
+ if (files.text.length > 0)
27370
+ note2("anthropic:tool-result-files-inlined");
27371
+ if (files.dropped)
27372
+ note2("anthropic:files-dropped");
27373
+ }
27374
+ }
27375
+ }
27286
27376
  const body = {
27287
27377
  model,
27288
27378
  messages: req.messages.flatMap((m) => {
@@ -28367,17 +28457,28 @@ function toAntigravityWire(req, model, identity) {
28367
28457
  });
28368
28458
  break;
28369
28459
  case "toolResult": {
28460
+ if (block.native !== undefined)
28461
+ note2("antigravity:provider-native-block-dropped");
28370
28462
  const name = names.get(block.toolUseId);
28371
28463
  if (name === undefined)
28372
28464
  note2("antigravity:tool-result-unmatched");
28373
28465
  if (block.isError === true)
28374
28466
  note2("antigravity:tool-result-error-flag-dropped");
28467
+ const files = splitFiles(block.files ?? [], () => true);
28468
+ if (files.text.length > 0)
28469
+ note2("antigravity:tool-result-files-inlined");
28375
28470
  parts.push({
28376
28471
  functionResponse: {
28377
28472
  name: cloakName2(cloak, name ?? block.toolUseId),
28378
- response: { output: block.content }
28473
+ response: { output: withFileText(block.content, files.text) }
28379
28474
  }
28380
28475
  });
28476
+ for (const image of block.images ?? []) {
28477
+ parts.push({ inlineData: { mimeType: image.mediaType, data: image.data } });
28478
+ }
28479
+ for (const file2 of files.carried) {
28480
+ parts.push({ inlineData: { mimeType: file2.mediaType, data: file2.data } });
28481
+ }
28381
28482
  role = "user";
28382
28483
  break;
28383
28484
  }
@@ -30181,13 +30282,23 @@ function toCustomChatWire(req, model) {
30181
30282
  function: { name: block.name, arguments: JSON.stringify(block.input) }
30182
30283
  });
30183
30284
  break;
30184
- case "toolResult":
30285
+ case "toolResult": {
30286
+ if (block.native !== undefined)
30287
+ note2("custom:anthropic-native-block-dropped");
30288
+ const files = splitFiles(block.files ?? [], () => false);
30185
30289
  messages.push({
30186
30290
  role: "tool",
30187
30291
  tool_call_id: block.toolUseId,
30188
- content: block.content
30292
+ content: withFileText(block.content, files.text)
30189
30293
  });
30294
+ if (block.images !== undefined)
30295
+ note2("custom:images-dropped");
30296
+ if (files.text.length > 0)
30297
+ note2("custom:tool-result-files-inlined");
30298
+ if (files.dropped)
30299
+ note2("custom:files-dropped");
30190
30300
  break;
30301
+ }
30191
30302
  case "providerNative":
30192
30303
  note2("custom:anthropic-native-block-dropped");
30193
30304
  break;
@@ -30301,14 +30412,35 @@ function toCustomResponsesWire(req, model) {
30301
30412
  arguments: JSON.stringify(block.input)
30302
30413
  });
30303
30414
  break;
30304
- case "toolResult":
30415
+ case "toolResult": {
30416
+ if (block.native !== undefined)
30417
+ note2("custom:anthropic-native-block-dropped");
30305
30418
  flush();
30419
+ const files = responsesFiles(block.files ?? []);
30306
30420
  input2.push({
30307
30421
  type: "function_call_output",
30308
30422
  call_id: block.toolUseId,
30309
- output: block.content
30423
+ output: withFileText(block.content, files.text)
30310
30424
  });
30425
+ if (block.images !== undefined) {
30426
+ note2("custom:tool-result-images-moved");
30427
+ for (const image of block.images) {
30428
+ parts.push({
30429
+ type: "input_image",
30430
+ image_url: `data:${image.mediaType};base64,${image.data}`
30431
+ });
30432
+ }
30433
+ }
30434
+ if (files.parts.length > 0) {
30435
+ note2("custom:tool-result-files-moved");
30436
+ parts.push(...files.parts);
30437
+ }
30438
+ if (files.text.length > 0)
30439
+ note2("custom:tool-result-files-inlined");
30440
+ if (files.dropped)
30441
+ note2("custom:files-dropped");
30311
30442
  break;
30443
+ }
30312
30444
  case "providerNative":
30313
30445
  note2("custom:anthropic-native-block-dropped");
30314
30446
  break;
@@ -30666,14 +30798,35 @@ function toGrokWire(req, model) {
30666
30798
  arguments: JSON.stringify(block.input)
30667
30799
  });
30668
30800
  break;
30669
- case "toolResult":
30801
+ case "toolResult": {
30802
+ if (block.native !== undefined)
30803
+ note2("grok:anthropic-native-block-dropped");
30670
30804
  flush();
30805
+ const files = responsesFiles(block.files ?? []);
30671
30806
  input2.push({
30672
30807
  type: "function_call_output",
30673
30808
  call_id: block.toolUseId,
30674
- output: block.content
30809
+ output: withFileText(block.content, files.text)
30675
30810
  });
30811
+ if (block.images !== undefined) {
30812
+ note2("grok:tool-result-images-moved");
30813
+ for (const image of block.images) {
30814
+ parts.push({
30815
+ type: "input_image",
30816
+ image_url: `data:${image.mediaType};base64,${image.data}`
30817
+ });
30818
+ }
30819
+ }
30820
+ if (files.parts.length > 0) {
30821
+ note2("grok:tool-result-files-moved");
30822
+ parts.push(...files.parts);
30823
+ }
30824
+ if (files.text.length > 0)
30825
+ note2("grok:tool-result-files-inlined");
30826
+ if (files.dropped)
30827
+ note2("grok:files-dropped");
30676
30828
  break;
30829
+ }
30677
30830
  case "providerNative":
30678
30831
  note2("grok:anthropic-native-block-dropped");
30679
30832
  break;
@@ -31080,6 +31233,7 @@ function toKiloWire(req, model) {
31080
31233
  for (const message of req.messages) {
31081
31234
  let parts = [];
31082
31235
  const toolCalls = [];
31236
+ const toolImages = [];
31083
31237
  const flush = () => {
31084
31238
  if (toolCalls.length > 0) {
31085
31239
  messages.push({ role: message.role, content: collapse(parts), tool_calls: [...toolCalls] });
@@ -31110,20 +31264,47 @@ function toKiloWire(req, model) {
31110
31264
  function: { name: block.name, arguments: JSON.stringify(block.input) }
31111
31265
  });
31112
31266
  break;
31113
- case "toolResult":
31267
+ case "toolResult": {
31268
+ if (block.native !== undefined)
31269
+ note2("kilo:anthropic-native-block-dropped");
31114
31270
  flush();
31271
+ const files = splitFiles(block.files ?? [], (t) => t === "application/pdf");
31115
31272
  messages.push({
31116
31273
  role: "tool",
31117
31274
  tool_call_id: block.toolUseId,
31118
- content: block.content
31275
+ content: withFileText(block.content, files.text)
31119
31276
  });
31277
+ if (files.text.length > 0)
31278
+ note2("kilo:tool-result-files-inlined");
31279
+ if (files.dropped)
31280
+ note2("kilo:files-dropped");
31281
+ for (const image of block.images ?? []) {
31282
+ note2("kilo:tool-result-images-moved");
31283
+ toolImages.push({
31284
+ type: "image_url",
31285
+ image_url: { url: `data:${image.mediaType};base64,${image.data}` }
31286
+ });
31287
+ }
31288
+ for (const file2 of files.carried) {
31289
+ note2("kilo:tool-result-files-moved");
31290
+ toolImages.push({
31291
+ type: "file",
31292
+ file: {
31293
+ filename: fileName(file2),
31294
+ file_data: `data:${file2.mediaType};base64,${file2.data}`
31295
+ }
31296
+ });
31297
+ }
31120
31298
  break;
31299
+ }
31121
31300
  case "providerNative":
31122
31301
  note2("kilo:anthropic-native-block-dropped");
31123
31302
  break;
31124
31303
  }
31125
31304
  }
31126
31305
  flush();
31306
+ if (toolImages.length > 0)
31307
+ messages.push({ role: "user", content: toolImages });
31127
31308
  }
31128
31309
  const body = {
31129
31310
  model,
@@ -31369,13 +31550,23 @@ function toChatWire(req, model, vendor = "kimi") {
31369
31550
  function: { name: block.name, arguments: JSON.stringify(block.input) }
31370
31551
  });
31371
31552
  break;
31372
- case "toolResult":
31553
+ case "toolResult": {
31554
+ if (block.native !== undefined)
31555
+ note2("kimi:anthropic-native-block-dropped");
31556
+ const files = splitFiles(block.files ?? [], () => false);
31373
31557
  messages.push({
31374
31558
  role: "tool",
31375
31559
  tool_call_id: block.toolUseId,
31376
- content: block.content
31560
+ content: withFileText(block.content, files.text)
31377
31561
  });
31562
+ if (block.images !== undefined)
31563
+ note2("kimi:images-dropped");
31564
+ if (files.text.length > 0)
31565
+ note2("kimi:tool-result-files-inlined");
31566
+ if (files.dropped)
31567
+ note2("kimi:files-dropped");
31378
31568
  break;
31569
+ }
31379
31570
  case "providerNative":
31380
31571
  note2("kimi:anthropic-native-block-dropped");
31381
31572
  break;
@@ -31774,14 +31965,35 @@ function toMuseWire(req, model) {
31774
31965
  arguments: JSON.stringify(block.input)
31775
31966
  });
31776
31967
  break;
31777
- case "toolResult":
31968
+ case "toolResult": {
31969
+ if (block.native !== undefined)
31970
+ note2("muse:foreign-native-block-dropped");
31778
31971
  flush();
31972
+ const files = responsesFiles(block.files ?? []);
31779
31973
  input2.push({
31780
31974
  type: "function_call_output",
31781
31975
  call_id: block.toolUseId,
31782
- output: block.content
31976
+ output: withFileText(block.content, files.text)
31783
31977
  });
31978
+ if (block.images !== undefined) {
31979
+ note2("muse:tool-result-images-moved");
31980
+ for (const image of block.images) {
31981
+ parts.push({
31982
+ type: "input_image",
31983
+ image_url: `data:${image.mediaType};base64,${image.data}`
31984
+ });
31985
+ }
31986
+ }
31987
+ if (files.parts.length > 0) {
31988
+ note2("muse:tool-result-files-moved");
31989
+ parts.push(...files.parts);
31990
+ }
31991
+ if (files.text.length > 0)
31992
+ note2("muse:tool-result-files-inlined");
31993
+ if (files.dropped)
31994
+ note2("muse:files-dropped");
31784
31995
  break;
31996
+ }
31785
31997
  case "providerNative":
31786
31998
  if (block.provider === "muse") {
31787
31999
  flush();
@@ -32179,14 +32391,35 @@ function toResponsesWire(req, model, opts = { oauth: false }) {
32179
32391
  arguments: JSON.stringify(block.input)
32180
32392
  });
32181
32393
  break;
32182
- case "toolResult":
32394
+ case "toolResult": {
32395
+ if (block.native !== undefined)
32396
+ note2("openai:anthropic-native-block-dropped");
32183
32397
  flush();
32398
+ const files = responsesFiles(block.files ?? []);
32184
32399
  input2.push({
32185
32400
  type: "function_call_output",
32186
32401
  call_id: block.toolUseId,
32187
- output: block.content
32402
+ output: withFileText(block.content, files.text)
32188
32403
  });
32404
+ if (block.images !== undefined) {
32405
+ note2("openai:tool-result-images-moved");
32406
+ for (const image of block.images) {
32407
+ parts.push({
32408
+ type: "input_image",
32409
+ image_url: `data:${image.mediaType};base64,${image.data}`
32410
+ });
32411
+ }
32412
+ }
32413
+ if (files.parts.length > 0) {
32414
+ note2("openai:tool-result-files-moved");
32415
+ parts.push(...files.parts);
32416
+ }
32417
+ if (files.text.length > 0)
32418
+ note2("openai:tool-result-files-inlined");
32419
+ if (files.dropped)
32420
+ note2("openai:files-dropped");
32189
32421
  break;
32422
+ }
32190
32423
  case "providerNative":
32191
32424
  if (block.provider === "openai") {
32192
32425
  flush();
@@ -32779,12 +33012,15 @@ function requiredProviders(request2) {
32779
33012
  for (const block of message.content) {
32780
33013
  if (block.type === "providerNative")
32781
33014
  owners.add(block.provider);
33015
+ if (block.type === "toolResult")
33016
+ for (const n of block.native ?? [])
33017
+ owners.add(n.provider);
32782
33018
  }
32783
33019
  }
32784
33020
  return owners;
32785
33021
  }
32786
33022
  function requiredCapabilities(request2) {
32787
- const hasImage = (blocks) => blocks.some((b) => b.type === "image");
33023
+ const hasImage = (blocks) => blocks.some((b) => b.type === "image" || b.type === "toolResult" && (b.images !== undefined || b.files !== undefined));
32788
33024
  const images = hasImage(request2.system ?? []) || request2.messages.some((m) => hasImage(m.content));
32789
33025
  return {
32790
33026
  tools: (request2.tools?.length ?? 0) > 0,
@@ -37919,7 +38155,7 @@ function createServiceDeps(input2) {
37919
38155
  }
37920
38156
 
37921
38157
  // apps/cli/src/version.ts
37922
- var VERSION = "0.12.10";
38158
+ var VERSION = "0.12.12";
37923
38159
 
37924
38160
  // apps/cli/src/commands/service.ts
37925
38161
  function gatewayArgv(root) {
package/gateway.js CHANGED
@@ -25673,7 +25673,7 @@ function blockTokens(block) {
25673
25673
  case "toolUse":
25674
25674
  return BLOCK_OVERHEAD + fromText(block.name) + fromText(safeJson(block.input));
25675
25675
  case "toolResult":
25676
- return BLOCK_OVERHEAD + fromText(block.toolUseId) + fromText(block.content);
25676
+ return BLOCK_OVERHEAD + fromText(block.toolUseId) + fromText(block.content) + (block.images?.length ?? 0) * IMAGE_TOKENS + (block.files ?? []).reduce((sum, f) => sum + fromText(f.data), 0) + (block.native ?? []).reduce((sum, n) => sum + blockTokens(n), 0);
25677
25677
  case "providerNative":
25678
25678
  return BLOCK_OVERHEAD + fromText(block.blockType) + fromText(safeJson(block.data));
25679
25679
  }
@@ -25738,6 +25738,9 @@ function estimateCachedInputTokens(request) {
25738
25738
  running += blockTokens(block);
25739
25739
  if (cacheControlOf(block) !== undefined)
25740
25740
  cached2 = running;
25741
+ if (block.type === "toolResult" && [...block.images ?? [], ...block.native ?? []].some((p) => p.cacheControl !== undefined)) {
25742
+ cached2 = running;
25743
+ }
25741
25744
  }
25742
25745
  }
25743
25746
  return cached2;
@@ -32632,6 +32635,69 @@ function systemText(system, provider, note) {
32632
32635
  `);
32633
32636
  }
32634
32637
 
32638
+ // packages/providers/src/toolResultFiles.ts
32639
+ function isTextual(mediaType) {
32640
+ const type = mediaType.split(";")[0]?.trim().toLowerCase() ?? "";
32641
+ return type.startsWith("text/") || type.endsWith("+json") || type.endsWith("+xml") || TEXTUAL.has(type);
32642
+ }
32643
+ var TEXTUAL = new Set([
32644
+ "application/json",
32645
+ "application/x-ndjson",
32646
+ "application/xml",
32647
+ "application/yaml",
32648
+ "application/x-yaml",
32649
+ "application/toml",
32650
+ "application/javascript",
32651
+ "application/typescript",
32652
+ "application/sql",
32653
+ "application/x-sh",
32654
+ "message/rfc822"
32655
+ ]);
32656
+ function fileText(file2) {
32657
+ return Buffer.from(file2.data, "base64").toString("utf8");
32658
+ }
32659
+ function splitFiles(files, carries) {
32660
+ const text = [];
32661
+ const carried = [];
32662
+ let dropped = false;
32663
+ for (const file2 of files) {
32664
+ if (isTextual(file2.mediaType))
32665
+ text.push(fileText(file2));
32666
+ else if (carries(file2.mediaType))
32667
+ carried.push(file2);
32668
+ else
32669
+ dropped = true;
32670
+ }
32671
+ return { text, carried, dropped };
32672
+ }
32673
+ function withFileText(content, text) {
32674
+ return [content, ...text].filter((t) => t.length > 0).join(`
32675
+
32676
+ `);
32677
+ }
32678
+ var RESPONSES_FILE_TYPES = Object.assign(Object.create(null), {
32679
+ "application/pdf": "pdf",
32680
+ "application/rtf": "rtf",
32681
+ "application/msword": "doc",
32682
+ "application/vnd.ms-excel": "xls",
32683
+ "application/vnd.ms-powerpoint": "ppt",
32684
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document": "docx",
32685
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "xlsx",
32686
+ "application/vnd.openxmlformats-officedocument.presentationml.presentation": "pptx"
32687
+ });
32688
+ function fileName(file2) {
32689
+ return file2.filename ?? `file.${RESPONSES_FILE_TYPES[file2.mediaType] ?? "bin"}`;
32690
+ }
32691
+ function responsesFiles(files) {
32692
+ const { text, carried, dropped } = splitFiles(files, (t) => (t in RESPONSES_FILE_TYPES));
32693
+ const parts = carried.map((file2) => ({
32694
+ type: "input_file",
32695
+ filename: fileName(file2),
32696
+ file_data: `data:${file2.mediaType};base64,${file2.data}`
32697
+ }));
32698
+ return { parts, text, dropped };
32699
+ }
32700
+
32635
32701
  // packages/providers/src/anthropic/wire.ts
32636
32702
  var OAUTH_IDENTITY = "You are Claude Code, Anthropic's official CLI for Claude.";
32637
32703
  function wireCacheControl(c) {
@@ -32668,14 +32734,27 @@ function encodeBlock(b, cloak) {
32668
32734
  input: b.input,
32669
32735
  ...cache
32670
32736
  };
32671
- case "toolResult":
32737
+ case "toolResult": {
32738
+ const files = splitFiles(b.files ?? [], (t) => t === "application/pdf");
32739
+ const text = withFileText(b.content, files.text);
32740
+ const documents = files.carried.map((file2) => ({
32741
+ type: "document",
32742
+ source: { type: "base64", media_type: file2.mediaType, data: file2.data },
32743
+ ...file2.filename !== undefined && { title: file2.filename }
32744
+ }));
32745
+ const parts = [
32746
+ ...(b.images ?? []).map((image) => encodeBlock(image, cloak)),
32747
+ ...documents,
32748
+ ...(b.native ?? []).map((part) => encodeBlock(part, cloak))
32749
+ ];
32672
32750
  return {
32673
32751
  type: "tool_result",
32674
32752
  tool_use_id: b.toolUseId,
32675
- content: b.content,
32753
+ content: parts.length === 0 ? text : [...text.length > 0 ? [{ type: "text", text }] : [], ...parts],
32676
32754
  is_error: b.isError,
32677
32755
  ...cache
32678
32756
  };
32757
+ }
32679
32758
  case "providerNative":
32680
32759
  if (b.provider !== "anthropic") {
32681
32760
  throw new GatewayError("INTERNAL", `anthropic adapter received a ${b.provider} provider-native block`);
@@ -32845,6 +32924,17 @@ function toWire(req, model, opts) {
32845
32924
  const systemCache = systemCacheControl(req);
32846
32925
  if (systemCache.lost)
32847
32926
  note("anthropic:system-turn-cache-control-dropped");
32927
+ for (const m of req.messages) {
32928
+ for (const b of m.content) {
32929
+ if (b.type === "toolResult" && b.files !== undefined) {
32930
+ const files = splitFiles(b.files, (t) => t === "application/pdf");
32931
+ if (files.text.length > 0)
32932
+ note("anthropic:tool-result-files-inlined");
32933
+ if (files.dropped)
32934
+ note("anthropic:files-dropped");
32935
+ }
32936
+ }
32937
+ }
32848
32938
  const body = {
32849
32939
  model,
32850
32940
  messages: req.messages.flatMap((m) => {
@@ -33929,17 +34019,28 @@ function toAntigravityWire(req, model, identity) {
33929
34019
  });
33930
34020
  break;
33931
34021
  case "toolResult": {
34022
+ if (block.native !== undefined)
34023
+ note("antigravity:provider-native-block-dropped");
33932
34024
  const name = names.get(block.toolUseId);
33933
34025
  if (name === undefined)
33934
34026
  note("antigravity:tool-result-unmatched");
33935
34027
  if (block.isError === true)
33936
34028
  note("antigravity:tool-result-error-flag-dropped");
34029
+ const files = splitFiles(block.files ?? [], () => true);
34030
+ if (files.text.length > 0)
34031
+ note("antigravity:tool-result-files-inlined");
33937
34032
  parts.push({
33938
34033
  functionResponse: {
33939
34034
  name: cloakName2(cloak, name ?? block.toolUseId),
33940
- response: { output: block.content }
34035
+ response: { output: withFileText(block.content, files.text) }
33941
34036
  }
33942
34037
  });
34038
+ for (const image of block.images ?? []) {
34039
+ parts.push({ inlineData: { mimeType: image.mediaType, data: image.data } });
34040
+ }
34041
+ for (const file2 of files.carried) {
34042
+ parts.push({ inlineData: { mimeType: file2.mediaType, data: file2.data } });
34043
+ }
33943
34044
  role = "user";
33944
34045
  break;
33945
34046
  }
@@ -35743,13 +35844,23 @@ function toCustomChatWire(req, model) {
35743
35844
  function: { name: block.name, arguments: JSON.stringify(block.input) }
35744
35845
  });
35745
35846
  break;
35746
- case "toolResult":
35847
+ case "toolResult": {
35848
+ if (block.native !== undefined)
35849
+ note("custom:anthropic-native-block-dropped");
35850
+ const files = splitFiles(block.files ?? [], () => false);
35747
35851
  messages.push({
35748
35852
  role: "tool",
35749
35853
  tool_call_id: block.toolUseId,
35750
- content: block.content
35854
+ content: withFileText(block.content, files.text)
35751
35855
  });
35856
+ if (block.images !== undefined)
35857
+ note("custom:images-dropped");
35858
+ if (files.text.length > 0)
35859
+ note("custom:tool-result-files-inlined");
35860
+ if (files.dropped)
35861
+ note("custom:files-dropped");
35752
35862
  break;
35863
+ }
35753
35864
  case "providerNative":
35754
35865
  note("custom:anthropic-native-block-dropped");
35755
35866
  break;
@@ -35863,14 +35974,35 @@ function toCustomResponsesWire(req, model) {
35863
35974
  arguments: JSON.stringify(block.input)
35864
35975
  });
35865
35976
  break;
35866
- case "toolResult":
35977
+ case "toolResult": {
35978
+ if (block.native !== undefined)
35979
+ note("custom:anthropic-native-block-dropped");
35867
35980
  flush();
35981
+ const files = responsesFiles(block.files ?? []);
35868
35982
  input2.push({
35869
35983
  type: "function_call_output",
35870
35984
  call_id: block.toolUseId,
35871
- output: block.content
35985
+ output: withFileText(block.content, files.text)
35872
35986
  });
35987
+ if (block.images !== undefined) {
35988
+ note("custom:tool-result-images-moved");
35989
+ for (const image of block.images) {
35990
+ parts.push({
35991
+ type: "input_image",
35992
+ image_url: `data:${image.mediaType};base64,${image.data}`
35993
+ });
35994
+ }
35995
+ }
35996
+ if (files.parts.length > 0) {
35997
+ note("custom:tool-result-files-moved");
35998
+ parts.push(...files.parts);
35999
+ }
36000
+ if (files.text.length > 0)
36001
+ note("custom:tool-result-files-inlined");
36002
+ if (files.dropped)
36003
+ note("custom:files-dropped");
35873
36004
  break;
36005
+ }
35874
36006
  case "providerNative":
35875
36007
  note("custom:anthropic-native-block-dropped");
35876
36008
  break;
@@ -36228,14 +36360,35 @@ function toGrokWire(req, model) {
36228
36360
  arguments: JSON.stringify(block.input)
36229
36361
  });
36230
36362
  break;
36231
- case "toolResult":
36363
+ case "toolResult": {
36364
+ if (block.native !== undefined)
36365
+ note("grok:anthropic-native-block-dropped");
36232
36366
  flush();
36367
+ const files = responsesFiles(block.files ?? []);
36233
36368
  input2.push({
36234
36369
  type: "function_call_output",
36235
36370
  call_id: block.toolUseId,
36236
- output: block.content
36371
+ output: withFileText(block.content, files.text)
36237
36372
  });
36373
+ if (block.images !== undefined) {
36374
+ note("grok:tool-result-images-moved");
36375
+ for (const image of block.images) {
36376
+ parts.push({
36377
+ type: "input_image",
36378
+ image_url: `data:${image.mediaType};base64,${image.data}`
36379
+ });
36380
+ }
36381
+ }
36382
+ if (files.parts.length > 0) {
36383
+ note("grok:tool-result-files-moved");
36384
+ parts.push(...files.parts);
36385
+ }
36386
+ if (files.text.length > 0)
36387
+ note("grok:tool-result-files-inlined");
36388
+ if (files.dropped)
36389
+ note("grok:files-dropped");
36238
36390
  break;
36391
+ }
36239
36392
  case "providerNative":
36240
36393
  note("grok:anthropic-native-block-dropped");
36241
36394
  break;
@@ -36642,6 +36795,7 @@ function toKiloWire(req, model) {
36642
36795
  for (const message of req.messages) {
36643
36796
  let parts = [];
36644
36797
  const toolCalls = [];
36798
+ const toolImages = [];
36645
36799
  const flush = () => {
36646
36800
  if (toolCalls.length > 0) {
36647
36801
  messages.push({ role: message.role, content: collapse(parts), tool_calls: [...toolCalls] });
@@ -36672,20 +36826,47 @@ function toKiloWire(req, model) {
36672
36826
  function: { name: block.name, arguments: JSON.stringify(block.input) }
36673
36827
  });
36674
36828
  break;
36675
- case "toolResult":
36829
+ case "toolResult": {
36830
+ if (block.native !== undefined)
36831
+ note("kilo:anthropic-native-block-dropped");
36676
36832
  flush();
36833
+ const files = splitFiles(block.files ?? [], (t) => t === "application/pdf");
36677
36834
  messages.push({
36678
36835
  role: "tool",
36679
36836
  tool_call_id: block.toolUseId,
36680
- content: block.content
36837
+ content: withFileText(block.content, files.text)
36681
36838
  });
36839
+ if (files.text.length > 0)
36840
+ note("kilo:tool-result-files-inlined");
36841
+ if (files.dropped)
36842
+ note("kilo:files-dropped");
36843
+ for (const image of block.images ?? []) {
36844
+ note("kilo:tool-result-images-moved");
36845
+ toolImages.push({
36846
+ type: "image_url",
36847
+ image_url: { url: `data:${image.mediaType};base64,${image.data}` }
36848
+ });
36849
+ }
36850
+ for (const file2 of files.carried) {
36851
+ note("kilo:tool-result-files-moved");
36852
+ toolImages.push({
36853
+ type: "file",
36854
+ file: {
36855
+ filename: fileName(file2),
36856
+ file_data: `data:${file2.mediaType};base64,${file2.data}`
36857
+ }
36858
+ });
36859
+ }
36682
36860
  break;
36861
+ }
36683
36862
  case "providerNative":
36684
36863
  note("kilo:anthropic-native-block-dropped");
36685
36864
  break;
36686
36865
  }
36687
36866
  }
36688
36867
  flush();
36868
+ if (toolImages.length > 0)
36869
+ messages.push({ role: "user", content: toolImages });
36689
36870
  }
36690
36871
  const body = {
36691
36872
  model,
@@ -36931,13 +37112,23 @@ function toChatWire(req, model, vendor = "kimi") {
36931
37112
  function: { name: block.name, arguments: JSON.stringify(block.input) }
36932
37113
  });
36933
37114
  break;
36934
- case "toolResult":
37115
+ case "toolResult": {
37116
+ if (block.native !== undefined)
37117
+ note("kimi:anthropic-native-block-dropped");
37118
+ const files = splitFiles(block.files ?? [], () => false);
36935
37119
  messages.push({
36936
37120
  role: "tool",
36937
37121
  tool_call_id: block.toolUseId,
36938
- content: block.content
37122
+ content: withFileText(block.content, files.text)
36939
37123
  });
37124
+ if (block.images !== undefined)
37125
+ note("kimi:images-dropped");
37126
+ if (files.text.length > 0)
37127
+ note("kimi:tool-result-files-inlined");
37128
+ if (files.dropped)
37129
+ note("kimi:files-dropped");
36940
37130
  break;
37131
+ }
36941
37132
  case "providerNative":
36942
37133
  note("kimi:anthropic-native-block-dropped");
36943
37134
  break;
@@ -37336,14 +37527,35 @@ function toMuseWire(req, model) {
37336
37527
  arguments: JSON.stringify(block.input)
37337
37528
  });
37338
37529
  break;
37339
- case "toolResult":
37530
+ case "toolResult": {
37531
+ if (block.native !== undefined)
37532
+ note("muse:foreign-native-block-dropped");
37340
37533
  flush();
37534
+ const files = responsesFiles(block.files ?? []);
37341
37535
  input2.push({
37342
37536
  type: "function_call_output",
37343
37537
  call_id: block.toolUseId,
37344
- output: block.content
37538
+ output: withFileText(block.content, files.text)
37345
37539
  });
37540
+ if (block.images !== undefined) {
37541
+ note("muse:tool-result-images-moved");
37542
+ for (const image of block.images) {
37543
+ parts.push({
37544
+ type: "input_image",
37545
+ image_url: `data:${image.mediaType};base64,${image.data}`
37546
+ });
37547
+ }
37548
+ }
37549
+ if (files.parts.length > 0) {
37550
+ note("muse:tool-result-files-moved");
37551
+ parts.push(...files.parts);
37552
+ }
37553
+ if (files.text.length > 0)
37554
+ note("muse:tool-result-files-inlined");
37555
+ if (files.dropped)
37556
+ note("muse:files-dropped");
37346
37557
  break;
37558
+ }
37347
37559
  case "providerNative":
37348
37560
  if (block.provider === "muse") {
37349
37561
  flush();
@@ -37741,14 +37953,35 @@ function toResponsesWire(req, model, opts = { oauth: false }) {
37741
37953
  arguments: JSON.stringify(block.input)
37742
37954
  });
37743
37955
  break;
37744
- case "toolResult":
37956
+ case "toolResult": {
37957
+ if (block.native !== undefined)
37958
+ note("openai:anthropic-native-block-dropped");
37745
37959
  flush();
37960
+ const files = responsesFiles(block.files ?? []);
37746
37961
  input2.push({
37747
37962
  type: "function_call_output",
37748
37963
  call_id: block.toolUseId,
37749
- output: block.content
37964
+ output: withFileText(block.content, files.text)
37750
37965
  });
37966
+ if (block.images !== undefined) {
37967
+ note("openai:tool-result-images-moved");
37968
+ for (const image of block.images) {
37969
+ parts.push({
37970
+ type: "input_image",
37971
+ image_url: `data:${image.mediaType};base64,${image.data}`
37972
+ });
37973
+ }
37974
+ }
37975
+ if (files.parts.length > 0) {
37976
+ note("openai:tool-result-files-moved");
37977
+ parts.push(...files.parts);
37978
+ }
37979
+ if (files.text.length > 0)
37980
+ note("openai:tool-result-files-inlined");
37981
+ if (files.dropped)
37982
+ note("openai:files-dropped");
37751
37983
  break;
37984
+ }
37752
37985
  case "providerNative":
37753
37986
  if (block.provider === "openai") {
37754
37987
  flush();
@@ -38265,12 +38498,15 @@ function requiredProviders(request2) {
38265
38498
  for (const block of message.content) {
38266
38499
  if (block.type === "providerNative")
38267
38500
  owners.add(block.provider);
38501
+ if (block.type === "toolResult")
38502
+ for (const n of block.native ?? [])
38503
+ owners.add(n.provider);
38268
38504
  }
38269
38505
  }
38270
38506
  return owners;
38271
38507
  }
38272
38508
  function requiredCapabilities(request2) {
38273
- const hasImage = (blocks) => blocks.some((b) => b.type === "image");
38509
+ const hasImage = (blocks) => blocks.some((b) => b.type === "image" || b.type === "toolResult" && (b.images !== undefined || b.files !== undefined));
38274
38510
  const images = hasImage(request2.system ?? []) || request2.messages.some((m) => hasImage(m.content));
38275
38511
  return {
38276
38512
  tools: (request2.tools?.length ?? 0) > 0,
@@ -60822,18 +61058,84 @@ var KNOWN = [
60822
61058
  "thinking",
60823
61059
  "metadata"
60824
61060
  ];
60825
- function flattenToolResult(content) {
61061
+ var TOOL_RESULT_NATIVE = new Set(["document", "search_result"]);
61062
+ function readToolResult(content) {
60826
61063
  if (typeof content === "string")
60827
- return content;
61064
+ return { content };
60828
61065
  if (!Array.isArray(content))
60829
- return "";
60830
- return content.map((part) => {
60831
- if (typeof part === "string")
60832
- return part;
61066
+ return { content: "" };
61067
+ const text = [];
61068
+ const images = [];
61069
+ const files = [];
61070
+ const native = [];
61071
+ for (const part of content) {
61072
+ if (typeof part === "string") {
61073
+ text.push(part);
61074
+ continue;
61075
+ }
61076
+ const image = imageBlock.safeParse(part);
61077
+ if (image.success) {
61078
+ images.push({
61079
+ type: "image",
61080
+ mediaType: image.data.source.media_type,
61081
+ data: image.data.source.data,
61082
+ ...irCacheControl(image.data.cache_control)
61083
+ });
61084
+ continue;
61085
+ }
60833
61086
  const p = part !== null && typeof part === "object" ? part : undefined;
60834
- return p?.type === "text" && typeof p.text === "string" ? p.text : JSON.stringify(part);
60835
- }).join(`
60836
- `);
61087
+ const nativeSchema = p?.type !== undefined && TOOL_RESULT_NATIVE.has(p.type) ? nativeSchemas[p.type] : undefined;
61088
+ const parsed2 = nativeSchema?.safeParse(part);
61089
+ if (p?.type === "document" && parsed2?.success === true) {
61090
+ const portable = portableDocument(parsed2.data);
61091
+ if (portable !== undefined) {
61092
+ if (typeof portable === "string")
61093
+ text.push(portable);
61094
+ else
61095
+ files.push(portable);
61096
+ continue;
61097
+ }
61098
+ }
61099
+ if (p?.type !== undefined && parsed2?.success === true) {
61100
+ const { type: _type, cache_control, ...data } = parsed2.data;
61101
+ native.push({
61102
+ type: "providerNative",
61103
+ provider: "anthropic",
61104
+ blockType: p.type,
61105
+ data,
61106
+ ...cache_control === undefined || cache_control === null ? {} : irCacheControl(cache_control)
61107
+ });
61108
+ continue;
61109
+ }
61110
+ text.push(p?.type === "text" && typeof p.text === "string" ? p.text : JSON.stringify(part));
61111
+ }
61112
+ return {
61113
+ content: text.join(`
61114
+ `),
61115
+ ...images.length > 0 && { images },
61116
+ ...files.length > 0 && { files },
61117
+ ...native.length > 0 && { native }
61118
+ };
61119
+ }
61120
+ function portableDocument(doc2) {
61121
+ if (doc2.citations !== undefined && doc2.citations !== null || doc2.context !== undefined && doc2.context !== null || doc2.cache_control !== undefined && doc2.cache_control !== null) {
61122
+ return;
61123
+ }
61124
+ const source = doc2.source;
61125
+ const title = typeof doc2.title === "string" ? doc2.title : undefined;
61126
+ if (source.type === "base64" && typeof source.data === "string") {
61127
+ return {
61128
+ type: "file",
61129
+ mediaType: "application/pdf",
61130
+ data: source.data,
61131
+ ...title !== undefined && { filename: title }
61132
+ };
61133
+ }
61134
+ if (source.type === "text" && typeof source.data === "string") {
61135
+ return title === undefined ? source.data : `${title}
61136
+ ${source.data}`;
61137
+ }
61138
+ return;
60837
61139
  }
60838
61140
  function toIrBlock(b) {
60839
61141
  switch (b.type) {
@@ -60869,7 +61171,7 @@ function toIrBlock(b) {
60869
61171
  return {
60870
61172
  type: "toolResult",
60871
61173
  toolUseId: b.tool_use_id,
60872
- content: flattenToolResult(b.content),
61174
+ ...readToolResult(b.content),
60873
61175
  isError: b.is_error ?? false,
60874
61176
  ...irCacheControl(b.cache_control)
60875
61177
  };
@@ -61174,13 +61476,17 @@ function parseOpenAIRequest(body2, headers) {
61174
61476
  if (m.tool_call_id === undefined) {
61175
61477
  throw new GatewayError("BAD_REQUEST", "messages: tool message requires tool_call_id");
61176
61478
  }
61479
+ const parts = contentBlocks(Array.isArray(m.content) ? m.content.filter((p) => p.type !== "image_url" || p.image_url.url.startsWith("data:")) : m.content);
61480
+ const images = parts.filter((b) => b.type === "image");
61177
61481
  messages.push({
61178
61482
  role: "user",
61179
61483
  content: [
61180
61484
  {
61181
61485
  type: "toolResult",
61182
61486
  toolUseId: m.tool_call_id,
61183
- content: typeof m.content === "string" ? m.content : "",
61487
+ content: typeof m.content === "string" ? m.content : parts.flatMap((b) => b.type === "text" ? [b.text] : []).join(`
61488
+ `),
61489
+ ...images.length > 0 && { images },
61184
61490
  isError: false,
61185
61491
  ...looseCacheControl(m.cache_control)
61186
61492
  }
@@ -61334,6 +61640,45 @@ function contentPart(part2) {
61334
61640
  }
61335
61641
  throw new GatewayError("BAD_REQUEST", `input: unsupported content part type "${safeToken(type)}"`);
61336
61642
  }
61643
+ function readToolOutput(output2) {
61644
+ if (typeof output2 === "string")
61645
+ return { content: output2 };
61646
+ if (!Array.isArray(output2))
61647
+ return { content: JSON.stringify(output2 ?? "") };
61648
+ const text = [];
61649
+ const images = [];
61650
+ const files = [];
61651
+ for (const part2 of output2) {
61652
+ if (isRecord9(part2) && (part2.type === "input_text" || part2.type === "output_text")) {
61653
+ text.push(String(part2.text ?? ""));
61654
+ } else if (isRecord9(part2) && part2.type === "input_image" && typeof part2.image_url === "string" && part2.image_url.startsWith("data:")) {
61655
+ images.push({ type: "image", ...parseDataUrl(part2.image_url) });
61656
+ } else if (isRecord9(part2) && part2.type === "input_file" && typeof part2.file_data === "string") {
61657
+ files.push({
61658
+ type: "file",
61659
+ ...fileData(part2.file_data),
61660
+ ...typeof part2.filename === "string" && { filename: part2.filename }
61661
+ });
61662
+ } else {
61663
+ text.push(JSON.stringify(part2));
61664
+ }
61665
+ }
61666
+ return {
61667
+ content: text.join(`
61668
+ `),
61669
+ ...images.length > 0 && { images },
61670
+ ...files.length > 0 && { files }
61671
+ };
61672
+ }
61673
+ function fileData(raw) {
61674
+ const url2 = /^data:([^;,]+);base64,(.*)$/s.exec(raw);
61675
+ if (url2 !== null)
61676
+ return { mediaType: url2[1], data: url2[2] };
61677
+ return {
61678
+ mediaType: raw.startsWith("JVBERi") ? "application/pdf" : "application/octet-stream",
61679
+ data: raw
61680
+ };
61681
+ }
61337
61682
  function messageBlocks(content) {
61338
61683
  if (typeof content === "string")
61339
61684
  return [{ type: "text", text: content }];
@@ -61390,7 +61735,7 @@ function readInputItems(items) {
61390
61735
  {
61391
61736
  type: "toolResult",
61392
61737
  toolUseId: clampCallId(callId),
61393
- content: typeof raw.output === "string" ? raw.output : JSON.stringify(raw.output ?? ""),
61738
+ ...readToolOutput(raw.output),
61394
61739
  isError: false
61395
61740
  }
61396
61741
  ]);
@@ -62779,7 +63124,7 @@ function createRing(limits = DEFAULT_RING_LIMITS) {
62779
63124
  }
62780
63125
 
62781
63126
  // apps/gateway/src/version.ts
62782
- var VERSION = "0.12.10";
63127
+ var VERSION = "0.12.12";
62783
63128
 
62784
63129
  // apps/gateway/src/app.ts
62785
63130
  var ADMIN_SESSION_TTL_MS = 12 * 60 * 60 * 1000;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "omnigateway",
3
- "version": "0.12.10",
3
+ "version": "0.12.12",
4
4
  "description": "Self-hosted AI gateway with Anthropic- and OpenAI-compatible APIs, an admin console, and a CLI",
5
5
  "license": "MIT",
6
6
  "author": "Harismawan <mail@harismawan.com>",