@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.
@@ -42,6 +42,7 @@
42
42
  */
43
43
  import type { ContentBlock, PromptMessage } from '@modelcontextprotocol/sdk/types.js';
44
44
  import type { CanonicalContentBlockParam as ContentBlockParam } from '../../session/canonical/index.js';
45
+ import type { UploadMediaFn } from '../../types/toolRuntime.js';
45
46
  /** Options threaded into per-item translation. */
46
47
  export type CallToolResultAdapterOptions = Readonly<{
47
48
  /**
@@ -49,6 +50,13 @@ export type CallToolResultAdapterOptions = Readonly<{
49
50
  * embedded resources. Example: `[Resource from <sourceName> at <uri>] ...`.
50
51
  */
51
52
  sourceName: string;
53
+ /**
54
+ * Host-injected media uploader (see `ToolRuntimeContext.uploadMedia`). When
55
+ * present, image content is materialized to a fetchable URL so the bytes
56
+ * leave the per-turn request; absent / null / throw → inline base64 (zero
57
+ * regression for the real MCP transport path, which passes nothing).
58
+ */
59
+ uploadMedia?: UploadMediaFn;
52
60
  }>;
53
61
  /**
54
62
  * Translate ONE MCP content item into zero or more Anthropic content blocks.
@@ -37,6 +37,7 @@ declare const outputSchema: () => z.ZodDiscriminatedUnion<[z.ZodObject<{
37
37
  "image/webp": "image/webp";
38
38
  }>;
39
39
  originalSize: z.ZodNumber;
40
+ url: z.ZodOptional<z.ZodString>;
40
41
  dimensions: z.ZodOptional<z.ZodObject<{
41
42
  originalWidth: z.ZodOptional<z.ZodNumber>;
42
43
  originalHeight: z.ZodOptional<z.ZodNumber>;
@@ -92,6 +93,13 @@ type ImageResult = {
92
93
  type: Base64ImageSource['media_type'];
93
94
  originalSize: number;
94
95
  dimensions?: ImageDimensions;
96
+ /**
97
+ * Set when a host uploader (context.uploadMedia) materialized the
98
+ * compressed bytes to a fetchable URL. The mapper then emits a `url`
99
+ * image source so the bytes leave the per-turn request; `base64` stays
100
+ * as the fallback field for renderers / hosts without the URL.
101
+ */
102
+ url?: string;
95
103
  };
96
104
  };
97
105
  /**
package/dist/cli.mjs CHANGED
@@ -5484,6 +5484,27 @@ var init_types = __esm(() => {
5484
5484
  CanonicalUserAbortError = APIUserAbortError;
5485
5485
  });
5486
5486
 
5487
+ // src/session/canonical/imageSource.ts
5488
+ function normalizeCanonicalImageSource(source) {
5489
+ if (!source || typeof source !== "object")
5490
+ return null;
5491
+ const s = source;
5492
+ if (s.type === "base64" && typeof s.data === "string") {
5493
+ return {
5494
+ kind: "base64",
5495
+ mediaType: typeof s.media_type === "string" ? s.media_type : "image/png",
5496
+ data: s.data
5497
+ };
5498
+ }
5499
+ if (s.type === "url" && typeof s.url === "string") {
5500
+ return { kind: "url", url: s.url };
5501
+ }
5502
+ return null;
5503
+ }
5504
+ function imageSourceToDataUri(img) {
5505
+ return img.kind === "base64" ? `data:${img.mediaType};base64,${img.data}` : img.url;
5506
+ }
5507
+
5487
5508
  // src/session/canonical/index.ts
5488
5509
  var init_canonical = __esm(() => {
5489
5510
  init_types();
@@ -84658,19 +84679,9 @@ function splitToolResultMedia(content) {
84658
84679
  continue;
84659
84680
  }
84660
84681
  if (block?.type === "image") {
84661
- const src = block.source;
84662
- if (src?.type === "base64" && typeof src.data === "string") {
84663
- images.push({
84664
- kind: "base64",
84665
- mediaType: src.media_type ?? "image/png",
84666
- data: src.data
84667
- });
84668
- continue;
84669
- }
84670
- if (src?.type === "url" && typeof src.url === "string") {
84671
- images.push({ kind: "url", url: src.url });
84672
- continue;
84673
- }
84682
+ const img = normalizeCanonicalImageSource(block.source);
84683
+ if (img)
84684
+ images.push(img);
84674
84685
  continue;
84675
84686
  }
84676
84687
  if (typeof block?.text === "string") {
@@ -84681,8 +84692,7 @@ function splitToolResultMedia(content) {
84681
84692
  `), images };
84682
84693
  }
84683
84694
  function toResponsesInputImagePart(img) {
84684
- const url3 = img.kind === "base64" ? `data:${img.mediaType};base64,${img.data}` : img.url;
84685
- return { type: "input_image", image_url: url3 };
84695
+ return { type: "input_image", image_url: imageSourceToDataUri(img) };
84686
84696
  }
84687
84697
  function convertContentBlocksToResponsesParts(content, role) {
84688
84698
  const textType = role === "assistant" ? "output_text" : "input_text";
@@ -84701,18 +84711,9 @@ function convertContentBlocksToResponsesParts(content, role) {
84701
84711
  case "image": {
84702
84712
  if (role === "assistant")
84703
84713
  break;
84704
- const source = block.source;
84705
- if (source?.type === "base64") {
84706
- parts.push({
84707
- type: "input_image",
84708
- image_url: `data:${source.media_type};base64,${source.data}`
84709
- });
84710
- } else if (source?.type === "url" && source.url) {
84711
- parts.push({
84712
- type: "input_image",
84713
- image_url: source.url
84714
- });
84715
- }
84714
+ const img = normalizeCanonicalImageSource(block.source);
84715
+ if (img)
84716
+ parts.push(toResponsesInputImagePart(img));
84716
84717
  break;
84717
84718
  }
84718
84719
  case "thinking":
@@ -85300,6 +85301,7 @@ var init_shim = __esm(() => {
85300
85301
  init_sdk();
85301
85302
  init_schema();
85302
85303
  init_capabilities2();
85304
+ init_canonical();
85303
85305
  });
85304
85306
 
85305
85307
  // src/providers/openai/shim.ts
@@ -85367,19 +85369,9 @@ function splitToolResultMedia2(content) {
85367
85369
  continue;
85368
85370
  }
85369
85371
  if (block?.type === "image") {
85370
- const source = block.source;
85371
- if (source?.type === "base64" && typeof source.data === "string") {
85372
- images.push({
85373
- kind: "base64",
85374
- mediaType: source.media_type ?? "image/png",
85375
- data: source.data
85376
- });
85377
- continue;
85378
- }
85379
- if (source?.type === "url" && typeof source.url === "string") {
85380
- images.push({ kind: "url", url: source.url });
85381
- continue;
85382
- }
85372
+ const img = normalizeCanonicalImageSource(block.source);
85373
+ if (img)
85374
+ images.push(img);
85383
85375
  continue;
85384
85376
  }
85385
85377
  if (block?.type === "tool_reference" && typeof block.tool_name === "string") {
@@ -85394,8 +85386,7 @@ function splitToolResultMedia2(content) {
85394
85386
  `), images };
85395
85387
  }
85396
85388
  function toOpenAIImageUrl(img) {
85397
- const url3 = img.kind === "base64" ? `data:${img.mediaType};base64,${img.data}` : img.url;
85398
- return { type: "image_url", image_url: { url: url3 } };
85389
+ return { type: "image_url", image_url: { url: imageSourceToDataUri(img) } };
85399
85390
  }
85400
85391
  function convertContentBlocks(content) {
85401
85392
  if (typeof content === "string")
@@ -85409,17 +85400,9 @@ function convertContentBlocks(content) {
85409
85400
  parts.push({ type: "text", text: block.text ?? "" });
85410
85401
  break;
85411
85402
  case "image": {
85412
- const src = block.source;
85413
- if (src?.type === "base64") {
85414
- parts.push({
85415
- type: "image_url",
85416
- image_url: {
85417
- url: `data:${src.media_type};base64,${src.data}`
85418
- }
85419
- });
85420
- } else if (src?.type === "url") {
85421
- parts.push({ type: "image_url", image_url: { url: src.url } });
85422
- }
85403
+ const img = normalizeCanonicalImageSource(block.source);
85404
+ if (img)
85405
+ parts.push(toOpenAIImageUrl(img));
85423
85406
  break;
85424
85407
  }
85425
85408
  case "tool_use":
@@ -86296,6 +86279,7 @@ var init_shim2 = __esm(() => {
86296
86279
  init_schemaSanitizer();
86297
86280
  init_providerProfile();
86298
86281
  init_capabilities2();
86282
+ init_canonical();
86299
86283
  OpenAIShimStream = class OpenAIShimStream {
86300
86284
  generator;
86301
86285
  controller = new AbortController;
@@ -94540,7 +94524,7 @@ function printStartupScreen() {
94540
94524
  const sLen = ` ● ${sL} Ready — type /help to begin`.length;
94541
94525
  out.push(boxRow(sRow, W2, sLen));
94542
94526
  out.push(`${rgb(...BORDER)}╚${"═".repeat(W2 - 2)}╝${RESET}`);
94543
- out.push(` ${DIM}${rgb(...DIMCOL)}opencow ${RESET}${rgb(...ACCENT)}v${"0.4.11"}${RESET}`);
94527
+ out.push(` ${DIM}${rgb(...DIMCOL)}opencow ${RESET}${rgb(...ACCENT)}v${"0.4.12"}${RESET}`);
94544
94528
  out.push("");
94545
94529
  process.stdout.write(out.join(`
94546
94530
  `) + `
@@ -120483,6 +120467,16 @@ async function validateContentTokens(content, ext, maxTokens) {
120483
120467
  throw new MaxFileReadTokenExceededError(effectiveCount, effectiveMaxTokens);
120484
120468
  }
120485
120469
  }
120470
+ async function maybeUploadBytes(uploadMedia, bytes, mediaType) {
120471
+ if (!uploadMedia)
120472
+ return null;
120473
+ try {
120474
+ return await uploadMedia({ bytes, mediaType });
120475
+ } catch (e) {
120476
+ logError(e);
120477
+ return null;
120478
+ }
120479
+ }
120486
120480
  function createImageResponse(buffer, mediaType, originalSize, dimensions) {
120487
120481
  return {
120488
120482
  type: "image",
@@ -120530,6 +120524,10 @@ async function callInner(file_path, fullFilePath, resolvedFilePath, ext, offset,
120530
120524
  if (IMAGE_EXTENSIONS.has(ext)) {
120531
120525
  const data2 = await readImageWithTokenBudget(resolvedFilePath, maxTokens);
120532
120526
  context3.nestedMemoryAttachmentTriggers?.add(fullFilePath);
120527
+ const uploadedUrl = await maybeUploadBytes(context3.uploadMedia, Buffer.from(data2.file.base64, "base64"), data2.file.type);
120528
+ if (uploadedUrl) {
120529
+ data2.file.url = uploadedUrl;
120530
+ }
120533
120531
  logFileOperation({
120534
120532
  operation: "read",
120535
120533
  tool: "FileReadTool",
@@ -120571,14 +120569,14 @@ async function callInner(file_path, fullFilePath, resolvedFilePath, ext, offset,
120571
120569
  const imgPath = path11.join(extractResult.data.file.outputDir, f);
120572
120570
  const imgBuffer = await getFsImplementation().readFile(imgPath);
120573
120571
  const resized = await maybeResizeAndDownsampleImageBuffer(imgBuffer, imgBuffer.length, "jpeg");
120574
- return {
120575
- type: "image",
120576
- source: {
120577
- type: "base64",
120578
- media_type: `image/${resized.mediaType}`,
120579
- data: resized.buffer.toString("base64")
120580
- }
120572
+ const mediaType = `image/${resized.mediaType}`;
120573
+ const uploadedUrl2 = await maybeUploadBytes(context3.uploadMedia, resized.buffer, mediaType);
120574
+ const source = uploadedUrl2 ? { type: "url", url: uploadedUrl2 } : {
120575
+ type: "base64",
120576
+ media_type: mediaType,
120577
+ data: resized.buffer.toString("base64")
120581
120578
  };
120579
+ return { type: "image", source };
120582
120580
  }));
120583
120581
  return {
120584
120582
  data: extractResult.data,
@@ -120626,20 +120624,17 @@ async function callInner(file_path, fullFilePath, resolvedFilePath, ext, offset,
120626
120624
  filePath: fullFilePath,
120627
120625
  content: pdfData.file.base64
120628
120626
  });
120627
+ const uploadedUrl = await maybeUploadBytes(context3.uploadMedia, Buffer.from(pdfData.file.base64, "base64"), "application/pdf");
120628
+ const documentSource = uploadedUrl ? { type: "url", url: uploadedUrl } : {
120629
+ type: "base64",
120630
+ media_type: "application/pdf",
120631
+ data: pdfData.file.base64
120632
+ };
120629
120633
  return {
120630
120634
  data: pdfData,
120631
120635
  newMessages: [
120632
120636
  createUserMessage({
120633
- content: [
120634
- {
120635
- type: "document",
120636
- source: {
120637
- type: "base64",
120638
- media_type: "application/pdf",
120639
- data: pdfData.file.base64
120640
- }
120641
- }
120642
- ],
120637
+ content: [{ type: "document", source: documentSource }],
120643
120638
  isMeta: true
120644
120639
  })
120645
120640
  ]
@@ -120846,6 +120841,7 @@ var init_FileReadTool = __esm(() => {
120846
120841
  base64: exports_external.string().describe("Base64-encoded image data"),
120847
120842
  type: imageMediaTypes.describe("The MIME type of the image"),
120848
120843
  originalSize: exports_external.number().describe("Original file size in bytes"),
120844
+ url: exports_external.string().optional().describe("Fetchable URL for the (compressed) image when a host uploader is configured; takes precedence over base64 for rendering"),
120849
120845
  dimensions: exports_external.object({
120850
120846
  originalWidth: exports_external.number().optional().describe("Original image width in pixels"),
120851
120847
  originalHeight: exports_external.number().optional().describe("Original image height in pixels"),
@@ -121074,17 +121070,18 @@ var init_FileReadTool = __esm(() => {
121074
121070
  mapToolResultToToolResultBlockParam(data, toolUseID) {
121075
121071
  switch (data.type) {
121076
121072
  case "image": {
121073
+ const source = data.file.url ? { type: "url", url: data.file.url } : {
121074
+ type: "base64",
121075
+ data: data.file.base64,
121076
+ media_type: data.file.type
121077
+ };
121077
121078
  return {
121078
121079
  tool_use_id: toolUseID,
121079
121080
  type: "tool_result",
121080
121081
  content: [
121081
121082
  {
121082
121083
  type: "image",
121083
- source: {
121084
- type: "base64",
121085
- data: data.file.base64,
121086
- media_type: data.file.type
121087
- }
121084
+ source
121088
121085
  }
121089
121086
  ]
121090
121087
  };
@@ -244410,7 +244407,7 @@ function getAnthropicEnvMetadata() {
244410
244407
  function getBuildAgeMinutes() {
244411
244408
  if (false)
244412
244409
  ;
244413
- const buildTime = new Date("2026-06-24T03:20:56.844Z").getTime();
244410
+ const buildTime = new Date("2026-06-24T10:02:32.669Z").getTime();
244414
244411
  if (isNaN(buildTime))
244415
244412
  return;
244416
244413
  return Math.floor((Date.now() - buildTime) / 60000);
@@ -293033,9 +293030,21 @@ var init_mcpWebSocketTransport = __esm(() => {
293033
293030
  });
293034
293031
 
293035
293032
  // src/capabilities/adapters/callToolResultAdapter.ts
293033
+ async function buildImageBlock(bytes, mediaType, uploadMedia) {
293034
+ let url3 = null;
293035
+ if (uploadMedia) {
293036
+ try {
293037
+ url3 = await uploadMedia({ bytes, mediaType });
293038
+ } catch (e) {
293039
+ logError(e);
293040
+ }
293041
+ }
293042
+ const source = url3 ? { type: "url", url: url3 } : { type: "base64", media_type: mediaType, data: bytes.toString("base64") };
293043
+ return { type: "image", source };
293044
+ }
293036
293045
  async function translateMcpContentItem(input) {
293037
293046
  const { item, options: options2 } = input;
293038
- const { sourceName } = options2;
293047
+ const { sourceName, uploadMedia } = options2;
293039
293048
  switch (item.type) {
293040
293049
  case "text":
293041
293050
  return [{ type: "text", text: item.text }];
@@ -293053,14 +293062,7 @@ async function translateMcpContentItem(input) {
293053
293062
  const ext = item.mimeType?.split("/")[1] || "png";
293054
293063
  const resized = await maybeResizeAndDownsampleImageBuffer(imageBuffer, imageBuffer.length, ext);
293055
293064
  return [
293056
- {
293057
- type: "image",
293058
- source: {
293059
- data: resized.buffer.toString("base64"),
293060
- media_type: `image/${resized.mediaType}`,
293061
- type: "base64"
293062
- }
293063
- }
293065
+ await buildImageBlock(resized.buffer, `image/${resized.mediaType}`, uploadMedia)
293064
293066
  ];
293065
293067
  }
293066
293068
  case "resource": {
@@ -293077,14 +293079,7 @@ async function translateMcpContentItem(input) {
293077
293079
  const resized = await maybeResizeAndDownsampleImageBuffer(imageBuffer, imageBuffer.length, ext);
293078
293080
  return [
293079
293081
  { type: "text", text: prefix },
293080
- {
293081
- type: "image",
293082
- source: {
293083
- data: resized.buffer.toString("base64"),
293084
- media_type: `image/${resized.mediaType}`,
293085
- type: "base64"
293086
- }
293087
- }
293082
+ await buildImageBlock(resized.buffer, `image/${resized.mediaType}`, uploadMedia)
293088
293083
  ];
293089
293084
  }
293090
293085
  return await persistBlobToTextBlock({
@@ -293130,6 +293125,7 @@ var IMAGE_MIME_TYPES;
293130
293125
  var init_callToolResultAdapter = __esm(() => {
293131
293126
  init_imageResizer();
293132
293127
  init_mcpOutputStorage();
293128
+ init_log2();
293133
293129
  IMAGE_MIME_TYPES = new Set([
293134
293130
  "image/jpeg",
293135
293131
  "image/png",
@@ -479786,7 +479782,7 @@ function buildPrimarySection() {
479786
479782
  }, undefined, false, undefined, this);
479787
479783
  return [{
479788
479784
  label: "Version",
479789
- value: "0.4.11"
479785
+ value: "0.4.12"
479790
479786
  }, {
479791
479787
  label: "Session name",
479792
479788
  value: nameValue
@@ -536108,7 +536104,7 @@ var init_bridge_kick = __esm(() => {
536108
536104
  var call58 = async () => {
536109
536105
  return {
536110
536106
  type: "text",
536111
- value: `${"99.0.0"} (built ${"2026-06-24T03:20:56.844Z"})`
536107
+ value: `${"99.0.0"} (built ${"2026-06-24T10:02:32.669Z"})`
536112
536108
  };
536113
536109
  }, version2, version_default;
536114
536110
  var init_version = __esm(() => {
@@ -558218,7 +558214,7 @@ function WelcomeV2() {
558218
558214
  dimColor: true,
558219
558215
  children: [
558220
558216
  "v",
558221
- "0.4.11",
558217
+ "0.4.12",
558222
558218
  " "
558223
558219
  ]
558224
558220
  }, undefined, true, undefined, this)
@@ -558418,7 +558414,7 @@ function WelcomeV2() {
558418
558414
  dimColor: true,
558419
558415
  children: [
558420
558416
  "v",
558421
- "0.4.11",
558417
+ "0.4.12",
558422
558418
  " "
558423
558419
  ]
558424
558420
  }, undefined, true, undefined, this)
@@ -558644,7 +558640,7 @@ function AppleTerminalWelcomeV2(t0) {
558644
558640
  dimColor: true,
558645
558641
  children: [
558646
558642
  "v",
558647
- "0.4.11",
558643
+ "0.4.12",
558648
558644
  " "
558649
558645
  ]
558650
558646
  }, undefined, true, undefined, this);
@@ -558898,7 +558894,7 @@ function AppleTerminalWelcomeV2(t0) {
558898
558894
  dimColor: true,
558899
558895
  children: [
558900
558896
  "v",
558901
- "0.4.11",
558897
+ "0.4.12",
558902
558898
  " "
558903
558899
  ]
558904
558900
  }, undefined, true, undefined, this);
@@ -569533,6 +569529,7 @@ class QueryEngine {
569533
569529
  agents: agents2 = [],
569534
569530
  setSDKStatus,
569535
569531
  onToolInvoke,
569532
+ uploadMedia,
569536
569533
  orphanedPermission
569537
569534
  } = this.config;
569538
569535
  this.discoveredSkillNames.clear();
@@ -569636,7 +569633,8 @@ class QueryEngine {
569636
569633
  });
569637
569634
  },
569638
569635
  setSDKStatus,
569639
- onToolInvoke
569636
+ onToolInvoke,
569637
+ uploadMedia
569640
569638
  };
569641
569639
  if (orphanedPermission && !this.hasHandledOrphanedPermission) {
569642
569640
  this.hasHandledOrphanedPermission = true;
@@ -569725,7 +569723,8 @@ class QueryEngine {
569725
569723
  setResponseLength: () => {},
569726
569724
  updateFileHistoryState: processUserInputContext.updateFileHistoryState,
569727
569725
  updateAttributionState: processUserInputContext.updateAttributionState,
569728
- setSDKStatus
569726
+ setSDKStatus,
569727
+ uploadMedia
569729
569728
  };
569730
569729
  headlessProfilerCheckpoint("before_skills_plugins");
569731
569730
  const [skills2, { enabled: enabledPlugins }] = await Promise.all([
@@ -570220,6 +570219,7 @@ async function* ask({
570220
570219
  agents: agents2 = [],
570221
570220
  setSDKStatus,
570222
570221
  onToolInvoke,
570222
+ uploadMedia,
570223
570223
  orphanedPermission
570224
570224
  }) {
570225
570225
  const engine = new QueryEngine({
@@ -570252,6 +570252,7 @@ async function* ask({
570252
570252
  includePartialMessages,
570253
570253
  setSDKStatus,
570254
570254
  onToolInvoke,
570255
+ uploadMedia,
570255
570256
  abortController,
570256
570257
  orphanedPermission,
570257
570258
  ...{}
@@ -579755,7 +579756,7 @@ Usage: claude --remote "your task description"`, () => gracefulShutdown(1));
579755
579756
  pendingHookMessages
579756
579757
  }, renderAndRun);
579757
579758
  }
579758
- }).version("0.4.11 (OpenCow)", "-v, --version", "Output the version number");
579759
+ }).version("0.4.12 (OpenCow)", "-v, --version", "Output the version number");
579759
579760
  program2.option("-w, --worktree [name]", "Create a new git worktree for this session (optionally specify a name)");
579760
579761
  program2.option("--tmux", "Create a tmux session for the worktree (requires --worktree). Uses iTerm2 native panes when available; use --tmux=classic for traditional tmux.");
579761
579762
  if (canUserConfigureAdvisor()) {
@@ -580401,7 +580402,7 @@ if (false) {}
580401
580402
  async function main2() {
580402
580403
  const args = process.argv.slice(2);
580403
580404
  if (args.length === 1 && (args[0] === "--version" || args[0] === "-v" || args[0] === "-V")) {
580404
- console.log(`${"0.4.11"} (OpenCow)`);
580405
+ console.log(`${"0.4.12"} (OpenCow)`);
580405
580406
  return;
580406
580407
  }
580407
580408
  if (args.includes("--provider")) {
@@ -580519,4 +580520,4 @@ async function main2() {
580519
580520
  }
580520
580521
  main2();
580521
580522
 
580522
- //# debugId=A3FE6F2E9266C7E764756E2164756E21
580523
+ //# debugId=61239C1856C7488B64756E2164756E21