@opencow-ai/opencow-agent-sdk 0.4.11 → 0.4.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.
package/dist/sdk.js CHANGED
@@ -31267,6 +31267,27 @@ var init_types3 = __esm(() => {
31267
31267
  CanonicalUserAbortError = APIUserAbortError;
31268
31268
  });
31269
31269
 
31270
+ // src/session/canonical/imageSource.ts
31271
+ function normalizeCanonicalImageSource(source) {
31272
+ if (!source || typeof source !== "object")
31273
+ return null;
31274
+ const s = source;
31275
+ if (s.type === "base64" && typeof s.data === "string") {
31276
+ return {
31277
+ kind: "base64",
31278
+ mediaType: typeof s.media_type === "string" ? s.media_type : "image/png",
31279
+ data: s.data
31280
+ };
31281
+ }
31282
+ if (s.type === "url" && typeof s.url === "string") {
31283
+ return { kind: "url", url: s.url };
31284
+ }
31285
+ return null;
31286
+ }
31287
+ function imageSourceToDataUri(img) {
31288
+ return img.kind === "base64" ? `data:${img.mediaType};base64,${img.data}` : img.url;
31289
+ }
31290
+
31270
31291
  // src/session/canonical/index.ts
31271
31292
  var init_canonical = __esm(() => {
31272
31293
  init_types3();
@@ -95407,19 +95428,9 @@ function splitToolResultMedia(content) {
95407
95428
  continue;
95408
95429
  }
95409
95430
  if (block?.type === "image") {
95410
- const src = block.source;
95411
- if (src?.type === "base64" && typeof src.data === "string") {
95412
- images.push({
95413
- kind: "base64",
95414
- mediaType: src.media_type ?? "image/png",
95415
- data: src.data
95416
- });
95417
- continue;
95418
- }
95419
- if (src?.type === "url" && typeof src.url === "string") {
95420
- images.push({ kind: "url", url: src.url });
95421
- continue;
95422
- }
95431
+ const img = normalizeCanonicalImageSource(block.source);
95432
+ if (img)
95433
+ images.push(img);
95423
95434
  continue;
95424
95435
  }
95425
95436
  if (typeof block?.text === "string") {
@@ -95430,8 +95441,7 @@ function splitToolResultMedia(content) {
95430
95441
  `), images };
95431
95442
  }
95432
95443
  function toResponsesInputImagePart(img) {
95433
- const url3 = img.kind === "base64" ? `data:${img.mediaType};base64,${img.data}` : img.url;
95434
- return { type: "input_image", image_url: url3 };
95444
+ return { type: "input_image", image_url: imageSourceToDataUri(img) };
95435
95445
  }
95436
95446
  function convertContentBlocksToResponsesParts(content, role) {
95437
95447
  const textType = role === "assistant" ? "output_text" : "input_text";
@@ -95450,18 +95460,9 @@ function convertContentBlocksToResponsesParts(content, role) {
95450
95460
  case "image": {
95451
95461
  if (role === "assistant")
95452
95462
  break;
95453
- const source = block.source;
95454
- if (source?.type === "base64") {
95455
- parts.push({
95456
- type: "input_image",
95457
- image_url: `data:${source.media_type};base64,${source.data}`
95458
- });
95459
- } else if (source?.type === "url" && source.url) {
95460
- parts.push({
95461
- type: "input_image",
95462
- image_url: source.url
95463
- });
95464
- }
95463
+ const img = normalizeCanonicalImageSource(block.source);
95464
+ if (img)
95465
+ parts.push(toResponsesInputImagePart(img));
95465
95466
  break;
95466
95467
  }
95467
95468
  case "thinking":
@@ -96049,6 +96050,7 @@ var init_shim = __esm(() => {
96049
96050
  init_sdk();
96050
96051
  init_schema();
96051
96052
  init_capabilities2();
96053
+ init_canonical();
96052
96054
  });
96053
96055
 
96054
96056
  // src/providers/shared/providerRecommendation.ts
@@ -96191,19 +96193,9 @@ function splitToolResultMedia2(content) {
96191
96193
  continue;
96192
96194
  }
96193
96195
  if (block?.type === "image") {
96194
- const source = block.source;
96195
- if (source?.type === "base64" && typeof source.data === "string") {
96196
- images.push({
96197
- kind: "base64",
96198
- mediaType: source.media_type ?? "image/png",
96199
- data: source.data
96200
- });
96201
- continue;
96202
- }
96203
- if (source?.type === "url" && typeof source.url === "string") {
96204
- images.push({ kind: "url", url: source.url });
96205
- continue;
96206
- }
96196
+ const img = normalizeCanonicalImageSource(block.source);
96197
+ if (img)
96198
+ images.push(img);
96207
96199
  continue;
96208
96200
  }
96209
96201
  if (block?.type === "tool_reference" && typeof block.tool_name === "string") {
@@ -96218,8 +96210,7 @@ function splitToolResultMedia2(content) {
96218
96210
  `), images };
96219
96211
  }
96220
96212
  function toOpenAIImageUrl(img) {
96221
- const url3 = img.kind === "base64" ? `data:${img.mediaType};base64,${img.data}` : img.url;
96222
- return { type: "image_url", image_url: { url: url3 } };
96213
+ return { type: "image_url", image_url: { url: imageSourceToDataUri(img) } };
96223
96214
  }
96224
96215
  function convertContentBlocks(content) {
96225
96216
  if (typeof content === "string")
@@ -96233,17 +96224,9 @@ function convertContentBlocks(content) {
96233
96224
  parts.push({ type: "text", text: block.text ?? "" });
96234
96225
  break;
96235
96226
  case "image": {
96236
- const src = block.source;
96237
- if (src?.type === "base64") {
96238
- parts.push({
96239
- type: "image_url",
96240
- image_url: {
96241
- url: `data:${src.media_type};base64,${src.data}`
96242
- }
96243
- });
96244
- } else if (src?.type === "url") {
96245
- parts.push({ type: "image_url", image_url: { url: src.url } });
96246
- }
96227
+ const img = normalizeCanonicalImageSource(block.source);
96228
+ if (img)
96229
+ parts.push(toOpenAIImageUrl(img));
96247
96230
  break;
96248
96231
  }
96249
96232
  case "tool_use":
@@ -97120,6 +97103,7 @@ var init_shim2 = __esm(() => {
97120
97103
  init_schemaSanitizer();
97121
97104
  init_providerProfile();
97122
97105
  init_capabilities2();
97106
+ init_canonical();
97123
97107
  OpenAIShimStream = class OpenAIShimStream {
97124
97108
  generator;
97125
97109
  controller = new AbortController;
@@ -273622,9 +273606,21 @@ var init_mcpWebSocketTransport = __esm(() => {
273622
273606
  });
273623
273607
 
273624
273608
  // src/capabilities/adapters/callToolResultAdapter.ts
273609
+ async function buildImageBlock(bytes, mediaType, uploadMedia) {
273610
+ let url3 = null;
273611
+ if (uploadMedia) {
273612
+ try {
273613
+ url3 = await uploadMedia({ bytes, mediaType });
273614
+ } catch (e) {
273615
+ logError(e);
273616
+ }
273617
+ }
273618
+ const source = url3 ? { type: "url", url: url3 } : { type: "base64", media_type: mediaType, data: bytes.toString("base64") };
273619
+ return { type: "image", source };
273620
+ }
273625
273621
  async function translateMcpContentItem(input) {
273626
273622
  const { item, options: options2 } = input;
273627
- const { sourceName } = options2;
273623
+ const { sourceName, uploadMedia } = options2;
273628
273624
  switch (item.type) {
273629
273625
  case "text":
273630
273626
  return [{ type: "text", text: item.text }];
@@ -273642,14 +273638,7 @@ async function translateMcpContentItem(input) {
273642
273638
  const ext = item.mimeType?.split("/")[1] || "png";
273643
273639
  const resized = await maybeResizeAndDownsampleImageBuffer(imageBuffer, imageBuffer.length, ext);
273644
273640
  return [
273645
- {
273646
- type: "image",
273647
- source: {
273648
- data: resized.buffer.toString("base64"),
273649
- media_type: `image/${resized.mediaType}`,
273650
- type: "base64"
273651
- }
273652
- }
273641
+ await buildImageBlock(resized.buffer, `image/${resized.mediaType}`, uploadMedia)
273653
273642
  ];
273654
273643
  }
273655
273644
  case "resource": {
@@ -273666,14 +273655,7 @@ async function translateMcpContentItem(input) {
273666
273655
  const resized = await maybeResizeAndDownsampleImageBuffer(imageBuffer, imageBuffer.length, ext);
273667
273656
  return [
273668
273657
  { type: "text", text: prefix },
273669
- {
273670
- type: "image",
273671
- source: {
273672
- data: resized.buffer.toString("base64"),
273673
- media_type: `image/${resized.mediaType}`,
273674
- type: "base64"
273675
- }
273676
- }
273658
+ await buildImageBlock(resized.buffer, `image/${resized.mediaType}`, uploadMedia)
273677
273659
  ];
273678
273660
  }
273679
273661
  return await persistBlobToTextBlock({
@@ -273727,6 +273709,7 @@ var IMAGE_MIME_TYPES;
273727
273709
  var init_callToolResultAdapter = __esm(() => {
273728
273710
  init_imageResizer();
273729
273711
  init_mcpOutputStorage();
273712
+ init_log2();
273730
273713
  IMAGE_MIME_TYPES = new Set([
273731
273714
  "image/jpeg",
273732
273715
  "image/png",
@@ -282692,7 +282675,7 @@ function getAnthropicEnvMetadata() {
282692
282675
  function getBuildAgeMinutes() {
282693
282676
  if (false)
282694
282677
  ;
282695
- const buildTime = new Date("2026-06-24T03:20:56.844Z").getTime();
282678
+ const buildTime = new Date("2026-06-24T10:02:32.669Z").getTime();
282696
282679
  if (isNaN(buildTime))
282697
282680
  return;
282698
282681
  return Math.floor((Date.now() - buildTime) / 60000);
@@ -286016,6 +285999,16 @@ async function validateContentTokens(content, ext, maxTokens) {
286016
285999
  throw new MaxFileReadTokenExceededError(effectiveCount, effectiveMaxTokens);
286017
286000
  }
286018
286001
  }
286002
+ async function maybeUploadBytes(uploadMedia, bytes, mediaType) {
286003
+ if (!uploadMedia)
286004
+ return null;
286005
+ try {
286006
+ return await uploadMedia({ bytes, mediaType });
286007
+ } catch (e) {
286008
+ logError(e);
286009
+ return null;
286010
+ }
286011
+ }
286019
286012
  function createImageResponse(buffer, mediaType, originalSize, dimensions) {
286020
286013
  return {
286021
286014
  type: "image",
@@ -286063,6 +286056,10 @@ async function callInner(file_path, fullFilePath, resolvedFilePath, ext, offset,
286063
286056
  if (IMAGE_EXTENSIONS.has(ext)) {
286064
286057
  const data2 = await readImageWithTokenBudget(resolvedFilePath, maxTokens);
286065
286058
  context4.nestedMemoryAttachmentTriggers?.add(fullFilePath);
286059
+ const uploadedUrl = await maybeUploadBytes(context4.uploadMedia, Buffer.from(data2.file.base64, "base64"), data2.file.type);
286060
+ if (uploadedUrl) {
286061
+ data2.file.url = uploadedUrl;
286062
+ }
286066
286063
  logFileOperation({
286067
286064
  operation: "read",
286068
286065
  tool: "FileReadTool",
@@ -286104,14 +286101,14 @@ async function callInner(file_path, fullFilePath, resolvedFilePath, ext, offset,
286104
286101
  const imgPath = path11.join(extractResult.data.file.outputDir, f);
286105
286102
  const imgBuffer = await getFsImplementation().readFile(imgPath);
286106
286103
  const resized = await maybeResizeAndDownsampleImageBuffer(imgBuffer, imgBuffer.length, "jpeg");
286107
- return {
286108
- type: "image",
286109
- source: {
286110
- type: "base64",
286111
- media_type: `image/${resized.mediaType}`,
286112
- data: resized.buffer.toString("base64")
286113
- }
286104
+ const mediaType = `image/${resized.mediaType}`;
286105
+ const uploadedUrl2 = await maybeUploadBytes(context4.uploadMedia, resized.buffer, mediaType);
286106
+ const source = uploadedUrl2 ? { type: "url", url: uploadedUrl2 } : {
286107
+ type: "base64",
286108
+ media_type: mediaType,
286109
+ data: resized.buffer.toString("base64")
286114
286110
  };
286111
+ return { type: "image", source };
286115
286112
  }));
286116
286113
  return {
286117
286114
  data: extractResult.data,
@@ -286159,20 +286156,17 @@ async function callInner(file_path, fullFilePath, resolvedFilePath, ext, offset,
286159
286156
  filePath: fullFilePath,
286160
286157
  content: pdfData.file.base64
286161
286158
  });
286159
+ const uploadedUrl = await maybeUploadBytes(context4.uploadMedia, Buffer.from(pdfData.file.base64, "base64"), "application/pdf");
286160
+ const documentSource = uploadedUrl ? { type: "url", url: uploadedUrl } : {
286161
+ type: "base64",
286162
+ media_type: "application/pdf",
286163
+ data: pdfData.file.base64
286164
+ };
286162
286165
  return {
286163
286166
  data: pdfData,
286164
286167
  newMessages: [
286165
286168
  createUserMessage({
286166
- content: [
286167
- {
286168
- type: "document",
286169
- source: {
286170
- type: "base64",
286171
- media_type: "application/pdf",
286172
- data: pdfData.file.base64
286173
- }
286174
- }
286175
- ],
286169
+ content: [{ type: "document", source: documentSource }],
286176
286170
  isMeta: true
286177
286171
  })
286178
286172
  ]
@@ -286379,6 +286373,7 @@ var init_FileReadTool = __esm(() => {
286379
286373
  base64: exports_external2.string().describe("Base64-encoded image data"),
286380
286374
  type: imageMediaTypes.describe("The MIME type of the image"),
286381
286375
  originalSize: exports_external2.number().describe("Original file size in bytes"),
286376
+ url: exports_external2.string().optional().describe("Fetchable URL for the (compressed) image when a host uploader is configured; takes precedence over base64 for rendering"),
286382
286377
  dimensions: exports_external2.object({
286383
286378
  originalWidth: exports_external2.number().optional().describe("Original image width in pixels"),
286384
286379
  originalHeight: exports_external2.number().optional().describe("Original image height in pixels"),
@@ -286607,17 +286602,18 @@ var init_FileReadTool = __esm(() => {
286607
286602
  mapToolResultToToolResultBlockParam(data, toolUseID) {
286608
286603
  switch (data.type) {
286609
286604
  case "image": {
286605
+ const source = data.file.url ? { type: "url", url: data.file.url } : {
286606
+ type: "base64",
286607
+ data: data.file.base64,
286608
+ media_type: data.file.type
286609
+ };
286610
286610
  return {
286611
286611
  tool_use_id: toolUseID,
286612
286612
  type: "tool_result",
286613
286613
  content: [
286614
286614
  {
286615
286615
  type: "image",
286616
- source: {
286617
- type: "base64",
286618
- data: data.file.base64,
286619
- media_type: data.file.type
286620
- }
286616
+ source
286621
286617
  }
286622
286618
  ]
286623
286619
  };
@@ -300013,6 +300009,7 @@ class QueryEngine {
300013
300009
  agents = [],
300014
300010
  setSDKStatus,
300015
300011
  onToolInvoke,
300012
+ uploadMedia,
300016
300013
  orphanedPermission
300017
300014
  } = this.config;
300018
300015
  this.discoveredSkillNames.clear();
@@ -300116,7 +300113,8 @@ class QueryEngine {
300116
300113
  });
300117
300114
  },
300118
300115
  setSDKStatus,
300119
- onToolInvoke
300116
+ onToolInvoke,
300117
+ uploadMedia
300120
300118
  };
300121
300119
  if (orphanedPermission && !this.hasHandledOrphanedPermission) {
300122
300120
  this.hasHandledOrphanedPermission = true;
@@ -300205,7 +300203,8 @@ class QueryEngine {
300205
300203
  setResponseLength: () => {},
300206
300204
  updateFileHistoryState: processUserInputContext.updateFileHistoryState,
300207
300205
  updateAttributionState: processUserInputContext.updateAttributionState,
300208
- setSDKStatus
300206
+ setSDKStatus,
300207
+ uploadMedia
300209
300208
  };
300210
300209
  headlessProfilerCheckpoint("before_skills_plugins");
300211
300210
  const [skills, { enabled: enabledPlugins }] = await Promise.all([
@@ -300700,6 +300699,7 @@ async function* ask({
300700
300699
  agents = [],
300701
300700
  setSDKStatus,
300702
300701
  onToolInvoke,
300702
+ uploadMedia,
300703
300703
  orphanedPermission
300704
300704
  }) {
300705
300705
  const engine = new QueryEngine({
@@ -300732,6 +300732,7 @@ async function* ask({
300732
300732
  includePartialMessages,
300733
300733
  setSDKStatus,
300734
300734
  onToolInvoke,
300735
+ uploadMedia,
300735
300736
  abortController,
300736
300737
  orphanedPermission,
300737
300738
  ...{}
@@ -329479,7 +329480,7 @@ function toInternalTool(sdkTool) {
329479
329480
  });
329480
329481
  const content = await translateCallToolResultContent({
329481
329482
  content: result.content,
329482
- options: { sourceName: sdkTool.name }
329483
+ options: { sourceName: sdkTool.name, uploadMedia: context4.uploadMedia }
329483
329484
  });
329484
329485
  return { data: content };
329485
329486
  }
@@ -330002,6 +330003,7 @@ function runSdkQueryRuntime(params) {
330002
330003
  includePartialMessages: Boolean(options2.includePartialMessages),
330003
330004
  agents: mergedAgents,
330004
330005
  onToolInvoke: typeof options2.onToolInvoke === "function" ? options2.onToolInvoke : undefined,
330006
+ uploadMedia: typeof options2.uploadMedia === "function" ? options2.uploadMedia : undefined,
330005
330007
  setSDKStatus: (status) => {
330006
330008
  turnStatusStream.enqueue({
330007
330009
  type: "system",
@@ -336055,4 +336057,4 @@ export {
336055
336057
  AbortError2 as AbortError
336056
336058
  };
336057
336059
 
336058
- //# debugId=7DBFD07CC9AC2F7C64756E2164756E21
336060
+ //# debugId=72C79E88061B251D64756E2164756E21
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Shared, url-aware normalization for canonical image sources.
3
+ *
4
+ * Canonical image blocks carry a discriminated `source` union
5
+ * (`CanonicalImageSource`: base64 | url). Every wire adapter that has to
6
+ * render an image — OpenAI Chat Completions, OpenAI Responses, and the
7
+ * tool_result fan-out on both — needs the same "is this base64 or a url"
8
+ * decision. This module owns that single decision so the providers stay in
9
+ * lock-step (truthiness checks, the `image/png` fallback, and "drop unknown
10
+ * sources silently" all live here, not duplicated four times).
11
+ */
12
+ /** Result of normalizing a raw image `source` into a wire-agnostic shape. */
13
+ export type NormalizedImageSource = {
14
+ kind: 'base64';
15
+ mediaType: string;
16
+ data: string;
17
+ } | {
18
+ kind: 'url';
19
+ url: string;
20
+ };
21
+ /**
22
+ * Reduce a raw (untrusted) image `source` to a normalized base64/url shape,
23
+ * or `null` when it is neither. Callers treat `null` as "drop this image"
24
+ * — silently, to avoid smuggling a misleading placeholder into the model's
25
+ * context.
26
+ *
27
+ * Takes `unknown` because the source arrives off a loosely-typed message
28
+ * payload (history replay, MCP tool output) where the discriminant is not
29
+ * statically guaranteed.
30
+ */
31
+ export declare function normalizeCanonicalImageSource(source: unknown): NormalizedImageSource | null;
32
+ /**
33
+ * Render a normalized image as a single string usable wherever a wire format
34
+ * wants one field: a `data:` URI for base64, or the url verbatim. Used by the
35
+ * OpenAI `image_url` and Responses `input_image` parts.
36
+ */
37
+ export declare function imageSourceToDataUri(img: NormalizedImageSource): string;
@@ -5,4 +5,5 @@
5
5
  * future refactors (e.g. splitting types/runtime validators/brands) do
6
6
  * not ripple through consumer imports.
7
7
  */
8
- export { CANONICAL_VERSION, CanonicalAPIError, CanonicalUserAbortError, type CanonicalBase64ImageSource, type CanonicalContentBlock, type CanonicalContentBlockParam, type CanonicalImageBlockParam, type CanonicalMessage, type CanonicalMessageParam, type CanonicalStreamEvent, type CanonicalTextBlock, type CanonicalTextBlockParam, type CanonicalRedactedThinkingBlock, type CanonicalRedactedThinkingBlockParam, type CanonicalThinkingBlock, type CanonicalThinkingBlockParam, type CanonicalToolResultBlockParam, type CanonicalToolUnion, type CanonicalToolUseBlock, type CanonicalToolUseBlockParam, type CanonicalUsage, type CanonicalVersion, } from './types.js';
8
+ export { CANONICAL_VERSION, CanonicalAPIError, CanonicalUserAbortError, type CanonicalBase64ImageSource, type CanonicalURLImageSource, type CanonicalImageSource, type CanonicalContentBlock, type CanonicalContentBlockParam, type CanonicalImageBlockParam, type CanonicalMessage, type CanonicalMessageParam, type CanonicalStreamEvent, type CanonicalTextBlock, type CanonicalTextBlockParam, type CanonicalRedactedThinkingBlock, type CanonicalRedactedThinkingBlockParam, type CanonicalThinkingBlock, type CanonicalThinkingBlockParam, type CanonicalToolResultBlockParam, type CanonicalToolUnion, type CanonicalToolUseBlock, type CanonicalToolUseBlockParam, type CanonicalUsage, type CanonicalVersion, } from './types.js';
9
+ export { imageSourceToDataUri, normalizeCanonicalImageSource, type NormalizedImageSource, } from './imageSource.js';
@@ -45,7 +45,7 @@
45
45
  * `src/session/canonical/index.js`.
46
46
  */
47
47
  import { APIError, APIUserAbortError } from '@anthropic-ai/sdk';
48
- import type { BetaBase64ImageSource, BetaContentBlock, BetaContentBlockParam, BetaImageBlockParam, BetaMessage, BetaMessageParam, BetaRawMessageStreamEvent, BetaRedactedThinkingBlock, BetaRedactedThinkingBlockParam, BetaTextBlock, BetaTextBlockParam, BetaThinkingBlock, BetaThinkingBlockParam, BetaToolResultBlockParam, BetaToolUnion, BetaToolUseBlock, BetaToolUseBlockParam, BetaUsage } from '@anthropic-ai/sdk/resources/beta/messages/messages.mjs';
48
+ import type { BetaBase64ImageSource, BetaURLImageSource, BetaContentBlock, BetaContentBlockParam, BetaImageBlockParam, BetaMessage, BetaMessageParam, BetaRawMessageStreamEvent, BetaRedactedThinkingBlock, BetaRedactedThinkingBlockParam, BetaTextBlock, BetaTextBlockParam, BetaThinkingBlock, BetaThinkingBlockParam, BetaToolResultBlockParam, BetaToolUnion, BetaToolUseBlock, BetaToolUseBlockParam, BetaUsage } from '@anthropic-ai/sdk/resources/beta/messages/messages.mjs';
49
49
  export type CanonicalVersion = 'v1';
50
50
  /**
51
51
  * Current canonical schema version. Consumers that persist canonical
@@ -98,6 +98,10 @@ export type CanonicalStreamEvent = BetaRawMessageStreamEvent;
98
98
  export type CanonicalImageBlockParam = BetaImageBlockParam;
99
99
  /** Base64-encoded image source inside an image block. */
100
100
  export type CanonicalBase64ImageSource = BetaBase64ImageSource;
101
+ /** External-URL image source inside an image block. */
102
+ export type CanonicalURLImageSource = BetaURLImageSource;
103
+ /** Discriminated union of every image-source variant carried by an image block. */
104
+ export type CanonicalImageSource = CanonicalBase64ImageSource | CanonicalURLImageSource;
101
105
  /** Token-usage metadata carried on assistant messages and deltas. */
102
106
  export type CanonicalUsage = BetaUsage;
103
107
  /**
@@ -48,6 +48,22 @@ export type ValidationResult = {
48
48
  message: string;
49
49
  errorCode: number;
50
50
  };
51
+ /**
52
+ * Host-supplied media uploader (see `Options.uploadMedia`). Given media
53
+ * bytes + media type, returns a fetchable URL the model side can render,
54
+ * or `null` when upload is unavailable. Media-neutral: serves any binary a
55
+ * tool produces (image / PDF / extracted page image, ...), driven by
56
+ * `mediaType`. The SDK passes the **compressed** buffer (post token-budget),
57
+ * so the materialized bytes stay bounded.
58
+ *
59
+ * Returning `null` or throwing means "uploader unavailable" — callers fall
60
+ * back to inline base64. Pure port: no SDK/CLI behavior depends on it being
61
+ * set, so standalone runs are unaffected.
62
+ */
63
+ export type UploadMediaFn = (input: {
64
+ bytes: Uint8Array;
65
+ mediaType: string;
66
+ }) => Promise<string | null>;
51
67
  export type ToolPermissionContext = DeepImmutable<{
52
68
  mode: PermissionMode;
53
69
  additionalWorkingDirectories: Map<string, AdditionalWorkingDirectory>;
@@ -143,6 +159,13 @@ export type ToolRuntimeContext = {
143
159
  toolInput: Record<string, unknown>;
144
160
  toolUseId: string;
145
161
  }) => void | Promise<void>;
162
+ /**
163
+ * Host-injected media uploader (see `Options.uploadMedia`). When present,
164
+ * media-producing tools (FileReadTool) upload the compressed bytes and
165
+ * carry a URL instead of inline base64. Absent → base64 (zero-config
166
+ * standalone behavior).
167
+ */
168
+ uploadMedia?: UploadMediaFn;
146
169
  updateFileHistoryState: (updater: (prev: FileHistoryState) => FileHistoryState) => void;
147
170
  updateAttributionState: (updater: (prev: AttributionState) => AttributionState) => void;
148
171
  setConversationId?: (id: UUID) => void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opencow-ai/opencow-agent-sdk",
3
- "version": "0.4.11",
3
+ "version": "0.4.12",
4
4
  "description": "Claude Code opened to any LLM — OpenAI, Gemini, DeepSeek, Ollama, and 200+ models",
5
5
  "type": "module",
6
6
  "main": "./dist/sdk.js",