@opencode-ai/ai 0.0.0-dev-18049 → 0.0.0-dev-18052

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.
@@ -50,26 +50,50 @@ export declare const AnthropicMessagesBody: Schema.Struct<{
50
50
  }>>;
51
51
  }>, Schema.Struct<{
52
52
  readonly type: Schema.tag<"image">;
53
- readonly source: Schema.Struct<{
53
+ readonly source: Schema.Union<readonly [Schema.Struct<{
54
54
  readonly type: Schema.tag<"base64">;
55
55
  readonly media_type: Schema.String;
56
56
  readonly data: Schema.String;
57
- }>;
57
+ }>, Schema.Struct<{
58
+ readonly type: Schema.tag<"url">;
59
+ readonly url: Schema.String;
60
+ }>, Schema.Struct<{
61
+ readonly type: Schema.tag<"file">;
62
+ readonly file_id: Schema.String;
63
+ }>]>;
58
64
  readonly cache_control: Schema.optional<Schema.Struct<{
59
65
  readonly type: Schema.tag<"ephemeral">;
60
66
  readonly ttl: Schema.optional<Schema.Literals<readonly ["5m", "1h"]>>;
61
67
  }>>;
68
+ readonly transformations: Schema.optional<Schema.Struct<{
69
+ readonly oversized_image: Schema.optional<Schema.Literals<readonly ["downsize", "error"]>>;
70
+ }>>;
62
71
  }>, Schema.Struct<{
63
72
  readonly type: Schema.tag<"document">;
64
- readonly source: Schema.Struct<{
73
+ readonly source: Schema.Union<readonly [Schema.Struct<{
65
74
  readonly type: Schema.tag<"base64">;
66
75
  readonly media_type: Schema.Literal<"application/pdf">;
67
76
  readonly data: Schema.String;
68
- }>;
77
+ }>, Schema.Struct<{
78
+ readonly type: Schema.tag<"text">;
79
+ readonly media_type: Schema.Literal<"text/plain">;
80
+ readonly data: Schema.String;
81
+ }>, Schema.Struct<{
82
+ readonly type: Schema.tag<"url">;
83
+ readonly url: Schema.String;
84
+ }>, Schema.Struct<{
85
+ readonly type: Schema.tag<"file">;
86
+ readonly file_id: Schema.String;
87
+ }>]>;
69
88
  readonly cache_control: Schema.optional<Schema.Struct<{
70
89
  readonly type: Schema.tag<"ephemeral">;
71
90
  readonly ttl: Schema.optional<Schema.Literals<readonly ["5m", "1h"]>>;
72
91
  }>>;
92
+ readonly title: Schema.optional<Schema.String>;
93
+ readonly context: Schema.optional<Schema.String>;
94
+ readonly citations: Schema.optional<Schema.Struct<{
95
+ readonly enabled: Schema.Boolean;
96
+ }>>;
73
97
  }>, Schema.Struct<{
74
98
  readonly type: Schema.tag<"tool_result">;
75
99
  readonly tool_use_id: Schema.String;
@@ -82,26 +106,50 @@ export declare const AnthropicMessagesBody: Schema.Struct<{
82
106
  }>>;
83
107
  }>, Schema.Struct<{
84
108
  readonly type: Schema.tag<"image">;
85
- readonly source: Schema.Struct<{
109
+ readonly source: Schema.Union<readonly [Schema.Struct<{
86
110
  readonly type: Schema.tag<"base64">;
87
111
  readonly media_type: Schema.String;
88
112
  readonly data: Schema.String;
89
- }>;
113
+ }>, Schema.Struct<{
114
+ readonly type: Schema.tag<"url">;
115
+ readonly url: Schema.String;
116
+ }>, Schema.Struct<{
117
+ readonly type: Schema.tag<"file">;
118
+ readonly file_id: Schema.String;
119
+ }>]>;
90
120
  readonly cache_control: Schema.optional<Schema.Struct<{
91
121
  readonly type: Schema.tag<"ephemeral">;
92
122
  readonly ttl: Schema.optional<Schema.Literals<readonly ["5m", "1h"]>>;
93
123
  }>>;
124
+ readonly transformations: Schema.optional<Schema.Struct<{
125
+ readonly oversized_image: Schema.optional<Schema.Literals<readonly ["downsize", "error"]>>;
126
+ }>>;
94
127
  }>, Schema.Struct<{
95
128
  readonly type: Schema.tag<"document">;
96
- readonly source: Schema.Struct<{
129
+ readonly source: Schema.Union<readonly [Schema.Struct<{
97
130
  readonly type: Schema.tag<"base64">;
98
131
  readonly media_type: Schema.Literal<"application/pdf">;
99
132
  readonly data: Schema.String;
100
- }>;
133
+ }>, Schema.Struct<{
134
+ readonly type: Schema.tag<"text">;
135
+ readonly media_type: Schema.Literal<"text/plain">;
136
+ readonly data: Schema.String;
137
+ }>, Schema.Struct<{
138
+ readonly type: Schema.tag<"url">;
139
+ readonly url: Schema.String;
140
+ }>, Schema.Struct<{
141
+ readonly type: Schema.tag<"file">;
142
+ readonly file_id: Schema.String;
143
+ }>]>;
101
144
  readonly cache_control: Schema.optional<Schema.Struct<{
102
145
  readonly type: Schema.tag<"ephemeral">;
103
146
  readonly ttl: Schema.optional<Schema.Literals<readonly ["5m", "1h"]>>;
104
147
  }>>;
148
+ readonly title: Schema.optional<Schema.String>;
149
+ readonly context: Schema.optional<Schema.String>;
150
+ readonly citations: Schema.optional<Schema.Struct<{
151
+ readonly enabled: Schema.Boolean;
152
+ }>>;
105
153
  }>]>>]>;
106
154
  readonly is_error: Schema.optional<Schema.Boolean>;
107
155
  readonly cache_control: Schema.optional<Schema.Struct<{
@@ -232,22 +280,46 @@ export declare const protocol: Protocol<{
232
280
  readonly type: "base64";
233
281
  readonly media_type: string;
234
282
  readonly data: string;
283
+ } | {
284
+ readonly type: "url";
285
+ readonly url: string;
286
+ } | {
287
+ readonly type: "file";
288
+ readonly file_id: string;
235
289
  };
236
290
  readonly cache_control?: {
237
291
  readonly type: "ephemeral";
238
292
  readonly ttl?: "1h" | "5m" | undefined;
239
293
  } | undefined;
294
+ readonly transformations?: {
295
+ readonly oversized_image?: "error" | "downsize" | undefined;
296
+ } | undefined;
240
297
  } | {
241
298
  readonly type: "document";
242
299
  readonly source: {
243
300
  readonly type: "base64";
244
301
  readonly media_type: "application/pdf";
245
302
  readonly data: string;
303
+ } | {
304
+ readonly type: "text";
305
+ readonly media_type: "text/plain";
306
+ readonly data: string;
307
+ } | {
308
+ readonly type: "url";
309
+ readonly url: string;
310
+ } | {
311
+ readonly type: "file";
312
+ readonly file_id: string;
246
313
  };
314
+ readonly title?: string | undefined;
315
+ readonly context?: string | undefined;
247
316
  readonly cache_control?: {
248
317
  readonly type: "ephemeral";
249
318
  readonly ttl?: "1h" | "5m" | undefined;
250
319
  } | undefined;
320
+ readonly citations?: {
321
+ readonly enabled: boolean;
322
+ } | undefined;
251
323
  } | {
252
324
  readonly type: "tool_result";
253
325
  readonly content: string | readonly ({
@@ -263,22 +335,46 @@ export declare const protocol: Protocol<{
263
335
  readonly type: "base64";
264
336
  readonly media_type: string;
265
337
  readonly data: string;
338
+ } | {
339
+ readonly type: "url";
340
+ readonly url: string;
341
+ } | {
342
+ readonly type: "file";
343
+ readonly file_id: string;
266
344
  };
267
345
  readonly cache_control?: {
268
346
  readonly type: "ephemeral";
269
347
  readonly ttl?: "1h" | "5m" | undefined;
270
348
  } | undefined;
349
+ readonly transformations?: {
350
+ readonly oversized_image?: "error" | "downsize" | undefined;
351
+ } | undefined;
271
352
  } | {
272
353
  readonly type: "document";
273
354
  readonly source: {
274
355
  readonly type: "base64";
275
356
  readonly media_type: "application/pdf";
276
357
  readonly data: string;
358
+ } | {
359
+ readonly type: "text";
360
+ readonly media_type: "text/plain";
361
+ readonly data: string;
362
+ } | {
363
+ readonly type: "url";
364
+ readonly url: string;
365
+ } | {
366
+ readonly type: "file";
367
+ readonly file_id: string;
277
368
  };
369
+ readonly title?: string | undefined;
370
+ readonly context?: string | undefined;
278
371
  readonly cache_control?: {
279
372
  readonly type: "ephemeral";
280
373
  readonly ttl?: "1h" | "5m" | undefined;
281
374
  } | undefined;
375
+ readonly citations?: {
376
+ readonly enabled: boolean;
377
+ } | undefined;
282
378
  })[];
283
379
  readonly tool_use_id: string;
284
380
  readonly cache_control?: {
@@ -477,22 +573,46 @@ export declare const route: Route<{
477
573
  readonly type: "base64";
478
574
  readonly media_type: string;
479
575
  readonly data: string;
576
+ } | {
577
+ readonly type: "url";
578
+ readonly url: string;
579
+ } | {
580
+ readonly type: "file";
581
+ readonly file_id: string;
480
582
  };
481
583
  readonly cache_control?: {
482
584
  readonly type: "ephemeral";
483
585
  readonly ttl?: "1h" | "5m" | undefined;
484
586
  } | undefined;
587
+ readonly transformations?: {
588
+ readonly oversized_image?: "error" | "downsize" | undefined;
589
+ } | undefined;
485
590
  } | {
486
591
  readonly type: "document";
487
592
  readonly source: {
488
593
  readonly type: "base64";
489
594
  readonly media_type: "application/pdf";
490
595
  readonly data: string;
596
+ } | {
597
+ readonly type: "text";
598
+ readonly media_type: "text/plain";
599
+ readonly data: string;
600
+ } | {
601
+ readonly type: "url";
602
+ readonly url: string;
603
+ } | {
604
+ readonly type: "file";
605
+ readonly file_id: string;
491
606
  };
607
+ readonly title?: string | undefined;
608
+ readonly context?: string | undefined;
492
609
  readonly cache_control?: {
493
610
  readonly type: "ephemeral";
494
611
  readonly ttl?: "1h" | "5m" | undefined;
495
612
  } | undefined;
613
+ readonly citations?: {
614
+ readonly enabled: boolean;
615
+ } | undefined;
496
616
  } | {
497
617
  readonly type: "tool_result";
498
618
  readonly content: string | readonly ({
@@ -508,22 +628,46 @@ export declare const route: Route<{
508
628
  readonly type: "base64";
509
629
  readonly media_type: string;
510
630
  readonly data: string;
631
+ } | {
632
+ readonly type: "url";
633
+ readonly url: string;
634
+ } | {
635
+ readonly type: "file";
636
+ readonly file_id: string;
511
637
  };
512
638
  readonly cache_control?: {
513
639
  readonly type: "ephemeral";
514
640
  readonly ttl?: "1h" | "5m" | undefined;
515
641
  } | undefined;
642
+ readonly transformations?: {
643
+ readonly oversized_image?: "error" | "downsize" | undefined;
644
+ } | undefined;
516
645
  } | {
517
646
  readonly type: "document";
518
647
  readonly source: {
519
648
  readonly type: "base64";
520
649
  readonly media_type: "application/pdf";
521
650
  readonly data: string;
651
+ } | {
652
+ readonly type: "text";
653
+ readonly media_type: "text/plain";
654
+ readonly data: string;
655
+ } | {
656
+ readonly type: "url";
657
+ readonly url: string;
658
+ } | {
659
+ readonly type: "file";
660
+ readonly file_id: string;
522
661
  };
662
+ readonly title?: string | undefined;
663
+ readonly context?: string | undefined;
523
664
  readonly cache_control?: {
524
665
  readonly type: "ephemeral";
525
666
  readonly ttl?: "1h" | "5m" | undefined;
526
667
  } | undefined;
668
+ readonly citations?: {
669
+ readonly enabled: boolean;
670
+ } | undefined;
527
671
  })[];
528
672
  readonly tool_use_id: string;
529
673
  readonly cache_control?: {
@@ -1,3 +1,4 @@
1
+ import { Buffer } from "node:buffer";
1
2
  import { Effect, Schema } from "effect";
2
3
  import { Tool } from "@opencode-ai/schema/tool";
3
4
  import { Route } from "../route/client.js";
@@ -40,23 +41,57 @@ const AnthropicTextBlock = Schema.Struct({
40
41
  text: Schema.String,
41
42
  cache_control: Schema.optional(AnthropicCacheControl),
42
43
  });
44
+ // SDK: Base64ImageSource:201 {type:"base64", media_type:"image/jpeg"|... , data}, URLImageSource:3817 {type:"url", url}, FileImageSource:2350 {type:"file", file_id}
45
+ // SDK: ImageBlockParam:2356 {source: Base64|URL|File, cache_control, transformations:2381 {oversized_image?}}
46
+ const AnthropicBase64ImageSource = Schema.Struct({
47
+ type: Schema.tag("base64"),
48
+ media_type: Schema.String,
49
+ data: Schema.String,
50
+ });
51
+ const AnthropicURLImageSource = Schema.Struct({ type: Schema.tag("url"), url: Schema.String });
52
+ const AnthropicFileImageSource = Schema.Struct({ type: Schema.tag("file"), file_id: Schema.String });
53
+ const AnthropicImageSource = Schema.Union([
54
+ AnthropicBase64ImageSource,
55
+ AnthropicURLImageSource,
56
+ AnthropicFileImageSource,
57
+ ]);
58
+ const AnthropicImageTransformations = Schema.Struct({
59
+ oversized_image: Schema.optional(Schema.Literals(["downsize", "error"])),
60
+ });
43
61
  const AnthropicImageBlock = Schema.Struct({
44
62
  type: Schema.tag("image"),
45
- source: Schema.Struct({
46
- type: Schema.tag("base64"),
47
- media_type: Schema.String,
48
- data: Schema.String,
49
- }),
63
+ source: AnthropicImageSource,
50
64
  cache_control: Schema.optional(AnthropicCacheControl),
65
+ transformations: Schema.optional(AnthropicImageTransformations),
66
+ });
67
+ // SDK: Base64PDFSource:209 {type:"base64", media_type:"application/pdf", data}, PlainTextSource:2716 {type:"text", media_type:"text/plain", data},
68
+ // SDK: URLPDFSource:3823 {type:"url", url}, FileDocumentSource:2344 {type:"file", file_id}, ContentBlockSource:2266 {type:"content", content}
69
+ // SDK: DocumentBlockParam:2297 {source: 5-way union, cache_control, citations, context, title}
70
+ const AnthropicBase64PDFSource = Schema.Struct({
71
+ type: Schema.tag("base64"),
72
+ media_type: Schema.Literal("application/pdf"),
73
+ data: Schema.String,
74
+ });
75
+ const AnthropicPlainTextSource = Schema.Struct({
76
+ type: Schema.tag("text"),
77
+ media_type: Schema.Literal("text/plain"),
78
+ data: Schema.String,
51
79
  });
80
+ const AnthropicURLPDFSource = Schema.Struct({ type: Schema.tag("url"), url: Schema.String });
81
+ const AnthropicFileDocumentSource = Schema.Struct({ type: Schema.tag("file"), file_id: Schema.String });
82
+ const AnthropicDocumentSource = Schema.Union([
83
+ AnthropicBase64PDFSource,
84
+ AnthropicPlainTextSource,
85
+ AnthropicURLPDFSource,
86
+ AnthropicFileDocumentSource,
87
+ ]);
52
88
  const AnthropicDocumentBlock = Schema.Struct({
53
89
  type: Schema.tag("document"),
54
- source: Schema.Struct({
55
- type: Schema.tag("base64"),
56
- media_type: Schema.Literal("application/pdf"),
57
- data: Schema.String,
58
- }),
90
+ source: AnthropicDocumentSource,
59
91
  cache_control: Schema.optional(AnthropicCacheControl),
92
+ title: Schema.optional(Schema.String),
93
+ context: Schema.optional(Schema.String),
94
+ citations: Schema.optional(Schema.Struct({ enabled: Schema.Boolean })),
60
95
  });
61
96
  const AnthropicThinkingBlock = Schema.Struct({
62
97
  type: Schema.tag("thinking"),
@@ -325,7 +360,149 @@ const lowerServerToolResult = Effect.fn("AnthropicMessages.lowerServerToolResult
325
360
  const payload = part.providerMetadata?.anthropic?.["result"] ?? part.result.value;
326
361
  return { type: wireType, tool_use_id: scrubToolCallID(part.id), content: payload };
327
362
  });
328
- const lowerMedia = Effect.fn("AnthropicMessages.lowerMedia")(function* (part) {
363
+ const fileIdFromMetadata = (metadata) => {
364
+ if (!ProviderShared.isRecord(metadata))
365
+ return undefined;
366
+ const anthropic = metadata.anthropic;
367
+ if (ProviderShared.isRecord(anthropic)) {
368
+ if (typeof anthropic.file_id === "string")
369
+ return anthropic.file_id;
370
+ if (typeof anthropic.fileId === "string")
371
+ return anthropic.fileId;
372
+ }
373
+ if (typeof metadata.file_id === "string")
374
+ return metadata.file_id;
375
+ if (typeof metadata.fileId === "string")
376
+ return metadata.fileId;
377
+ return undefined;
378
+ };
379
+ const transformationsFromMetadata = (metadata) => {
380
+ if (!ProviderShared.isRecord(metadata))
381
+ return undefined;
382
+ const anthropic = ProviderShared.isRecord(metadata.anthropic) ? metadata.anthropic : undefined;
383
+ const raw = anthropic?.transformations ?? metadata.transformations;
384
+ if (ProviderShared.isRecord(raw)) {
385
+ const value = raw.oversized_image;
386
+ if (value === "downsize" || value === "error")
387
+ return { oversized_image: value };
388
+ }
389
+ if (anthropic && (anthropic.oversized_image === "downsize" || anthropic.oversized_image === "error"))
390
+ return { oversized_image: anthropic.oversized_image };
391
+ return undefined;
392
+ };
393
+ const documentTitleFromPart = (part) => {
394
+ if (ProviderShared.isRecord(part.metadata)) {
395
+ const anthropic = part.metadata.anthropic;
396
+ if (ProviderShared.isRecord(anthropic) && typeof anthropic.title === "string")
397
+ return anthropic.title;
398
+ if (typeof part.metadata.title === "string")
399
+ return part.metadata.title;
400
+ }
401
+ if (typeof part.filename === "string" && part.filename.length > 0)
402
+ return part.filename;
403
+ return undefined;
404
+ };
405
+ const documentContextFromMetadata = (metadata) => {
406
+ if (!ProviderShared.isRecord(metadata))
407
+ return undefined;
408
+ const anthropic = ProviderShared.isRecord(metadata.anthropic) ? metadata.anthropic : undefined;
409
+ if (anthropic && typeof anthropic.context === "string")
410
+ return anthropic.context;
411
+ if (typeof metadata.context === "string")
412
+ return metadata.context;
413
+ return undefined;
414
+ };
415
+ const citationsFromMetadata = (metadata) => {
416
+ if (!ProviderShared.isRecord(metadata))
417
+ return undefined;
418
+ const raw = ProviderShared.isRecord(metadata.anthropic)
419
+ ? (metadata.anthropic.citations ?? metadata.citations)
420
+ : metadata.citations;
421
+ if (ProviderShared.isRecord(raw) && typeof raw.enabled === "boolean")
422
+ return { enabled: raw.enabled };
423
+ return undefined;
424
+ };
425
+ const isHttpUrl = (value) => /^https?:\/\//i.test(value.trim());
426
+ const lowerMedia = Effect.fn("AnthropicMessages.lowerMedia")(function* (part, breakpoints) {
427
+ const mime = part.mediaType.toLowerCase();
428
+ const cacheControlValue = breakpoints ? cacheControl(breakpoints, part.cache) : undefined;
429
+ const fileId = fileIdFromMetadata(part.metadata);
430
+ // SDK file sources: FileImageSource:2350 / FileDocumentSource:2344 {type:"file", file_id}
431
+ if (fileId) {
432
+ if (mime.startsWith("image/"))
433
+ return {
434
+ type: "image",
435
+ source: { type: "file", file_id: fileId },
436
+ ...(cacheControlValue === undefined ? {} : { cache_control: cacheControlValue }),
437
+ ...(transformationsFromMetadata(part.metadata) === undefined
438
+ ? {}
439
+ : { transformations: transformationsFromMetadata(part.metadata) }),
440
+ };
441
+ return {
442
+ type: "document",
443
+ source: { type: "file", file_id: fileId },
444
+ ...(cacheControlValue === undefined ? {} : { cache_control: cacheControlValue }),
445
+ ...(documentTitleFromPart(part) === undefined ? {} : { title: documentTitleFromPart(part) }),
446
+ ...(documentContextFromMetadata(part.metadata) === undefined
447
+ ? {}
448
+ : { context: documentContextFromMetadata(part.metadata) }),
449
+ ...(citationsFromMetadata(part.metadata) === undefined
450
+ ? {}
451
+ : { citations: citationsFromMetadata(part.metadata) }),
452
+ };
453
+ }
454
+ const rawString = typeof part.data === "string" ? part.data.trim() : undefined;
455
+ // SDK URL sources: URLImageSource:3817 / URLPDFSource:3823 {type:"url", url}
456
+ if (rawString && isHttpUrl(rawString) && !rawString.startsWith("data:")) {
457
+ if (mime.startsWith("image/"))
458
+ return {
459
+ type: "image",
460
+ source: { type: "url", url: rawString },
461
+ ...(cacheControlValue === undefined ? {} : { cache_control: cacheControlValue }),
462
+ ...(transformationsFromMetadata(part.metadata) === undefined
463
+ ? {}
464
+ : { transformations: transformationsFromMetadata(part.metadata) }),
465
+ };
466
+ if (mime === "application/pdf")
467
+ return {
468
+ type: "document",
469
+ source: { type: "url", url: rawString },
470
+ ...(cacheControlValue === undefined ? {} : { cache_control: cacheControlValue }),
471
+ ...(documentTitleFromPart(part) === undefined ? {} : { title: documentTitleFromPart(part) }),
472
+ ...(documentContextFromMetadata(part.metadata) === undefined
473
+ ? {}
474
+ : { context: documentContextFromMetadata(part.metadata) }),
475
+ ...(citationsFromMetadata(part.metadata) === undefined
476
+ ? {}
477
+ : { citations: citationsFromMetadata(part.metadata) }),
478
+ };
479
+ }
480
+ // SDK PlainTextSource:2716 {type:"text", media_type:"text/plain", data}
481
+ if (mime === "text/plain") {
482
+ const textData = typeof part.data !== "string"
483
+ ? Buffer.from(part.data).toString("utf8")
484
+ : part.data.startsWith("data:")
485
+ ? (() => {
486
+ const comma = part.data.indexOf(",");
487
+ const payload = comma >= 0 ? part.data.slice(comma + 1) : part.data;
488
+ return part.data.includes(";base64")
489
+ ? Buffer.from(payload, "base64").toString("utf8")
490
+ : decodeURIComponent(payload);
491
+ })()
492
+ : part.data;
493
+ return {
494
+ type: "document",
495
+ source: { type: "text", media_type: "text/plain", data: textData },
496
+ ...(cacheControlValue === undefined ? {} : { cache_control: cacheControlValue }),
497
+ ...(documentTitleFromPart(part) === undefined ? {} : { title: documentTitleFromPart(part) }),
498
+ ...(documentContextFromMetadata(part.metadata) === undefined
499
+ ? {}
500
+ : { context: documentContextFromMetadata(part.metadata) }),
501
+ ...(citationsFromMetadata(part.metadata) === undefined
502
+ ? {}
503
+ : { citations: citationsFromMetadata(part.metadata) }),
504
+ };
505
+ }
329
506
  const media = ProviderShared.normalizeMedia(part);
330
507
  if (media.mime === "application/pdf")
331
508
  return {
@@ -335,6 +512,14 @@ const lowerMedia = Effect.fn("AnthropicMessages.lowerMedia")(function* (part) {
335
512
  media_type: "application/pdf",
336
513
  data: media.base64,
337
514
  },
515
+ ...(cacheControlValue === undefined ? {} : { cache_control: cacheControlValue }),
516
+ ...(documentTitleFromPart(part) === undefined ? {} : { title: documentTitleFromPart(part) }),
517
+ ...(documentContextFromMetadata(part.metadata) === undefined
518
+ ? {}
519
+ : { context: documentContextFromMetadata(part.metadata) }),
520
+ ...(citationsFromMetadata(part.metadata) === undefined
521
+ ? {}
522
+ : { citations: citationsFromMetadata(part.metadata) }),
338
523
  };
339
524
  if (!media.mime.startsWith("image/"))
340
525
  return yield* invalid(`Anthropic Messages does not support media type ${part.mediaType}`);
@@ -345,6 +530,10 @@ const lowerMedia = Effect.fn("AnthropicMessages.lowerMedia")(function* (part) {
345
530
  media_type: media.mime,
346
531
  data: media.base64,
347
532
  },
533
+ ...(cacheControlValue === undefined ? {} : { cache_control: cacheControlValue }),
534
+ ...(transformationsFromMetadata(part.metadata) === undefined
535
+ ? {}
536
+ : { transformations: transformationsFromMetadata(part.metadata) }),
348
537
  };
349
538
  });
350
539
  // Tool results may carry structured text, images, and documents. Keep media as provider-native
@@ -440,7 +629,7 @@ const lowerMessages = Effect.fn("AnthropicMessages.lowerMessages")(function* (re
440
629
  continue;
441
630
  }
442
631
  if (part.type === "media") {
443
- content.push(yield* lowerMedia(part));
632
+ content.push(yield* lowerMedia(part, breakpoints));
444
633
  continue;
445
634
  }
446
635
  return yield* ProviderShared.unsupportedContent("Anthropic Messages", "user", ["text", "media"]);
@@ -434,6 +434,7 @@ export declare const Event: Schema.StructWithRest<Schema.Struct<{
434
434
  readonly headers: Schema.optional<Schema.Unknown>;
435
435
  }>, readonly [Schema.$Record<Schema.String, Schema.Unknown>]>;
436
436
  export type Event = Schema.Schema.Type<typeof Event>;
437
+ export type ItemKind = "message" | "reasoning" | "function-call" | "reference";
437
438
  export interface Extension {
438
439
  readonly id: string;
439
440
  readonly name: string;
@@ -442,6 +443,7 @@ export interface Extension {
442
443
  readonly media: ProviderShared.NormalizedMedia;
443
444
  readonly request: LLMRequest;
444
445
  }) => MediaInput | undefined;
446
+ readonly acceptsItemID?: (kind: ItemKind, id: string) => boolean;
445
447
  }
446
448
  export interface ParserState {
447
449
  readonly id: string;
@@ -245,26 +245,34 @@ export const lowerToolChoice = (protocolName, toolChoice) => ProviderShared.matc
245
245
  required: () => "required",
246
246
  tool: (toolName) => ({ type: "function", name: toolName }),
247
247
  });
248
+ // Servers validate item ids on replayed history, and a malformed or oversized
249
+ // id can fail an otherwise valid request. Only server-issued tokens are worth
250
+ // resending; anything else is treated as absent so the item is resent without
251
+ // an id (or skipped, for items that cannot be expressed without one).
252
+ const ITEM_ID_PATTERN = /^[A-Za-z0-9_-]{1,64}$/;
248
253
  const itemID = (providerMetadata, providerMetadataKey) => {
249
254
  const metadata = providerMetadata?.[providerMetadataKey];
250
- return ProviderShared.isRecord(metadata) && typeof metadata.itemId === "string" && metadata.itemId.length > 0
255
+ return ProviderShared.isRecord(metadata) &&
256
+ typeof metadata.itemId === "string" &&
257
+ ITEM_ID_PATTERN.test(metadata.itemId)
251
258
  ? metadata.itemId
252
259
  : undefined;
253
260
  };
254
- const lowerToolCall = (part, providerMetadataKey) => {
261
+ const acceptsItemID = (extension, kind, id) => id !== undefined && (extension.acceptsItemID?.(kind, id) ?? true);
262
+ const lowerToolCall = (part, providerMetadataKey, extension) => {
255
263
  const id = itemID(part.providerMetadata, providerMetadataKey);
256
264
  return {
257
265
  type: "function_call",
258
- ...(id ? { id } : {}),
266
+ ...(acceptsItemID(extension, "function-call", id) ? { id } : {}),
259
267
  call_id: part.id,
260
268
  name: part.name,
261
269
  arguments: ProviderShared.encodeJson(part.input),
262
270
  };
263
271
  };
264
- const lowerReasoning = (part, providerMetadataKey) => {
272
+ const lowerReasoning = (part, providerMetadataKey, extension) => {
265
273
  const metadata = part.providerMetadata?.[providerMetadataKey];
266
274
  const id = itemID(part.providerMetadata, providerMetadataKey);
267
- if (!ProviderShared.isRecord(metadata) || !id)
275
+ if (!ProviderShared.isRecord(metadata) || !acceptsItemID(extension, "reasoning", id))
268
276
  return undefined;
269
277
  const encryptedContent = typeof metadata.reasoningEncryptedContent === "string" || metadata.reasoningEncryptedContent === null
270
278
  ? metadata.reasoningEncryptedContent
@@ -359,7 +367,8 @@ const lowerMessages = Effect.fn("OpenResponses.lowerMessages")(function* (reques
359
367
  return;
360
368
  const groups = content.reduce((groups, part) => {
361
369
  const metadata = part.providerMetadata?.[providerMetadataKey];
362
- const id = itemID(part.providerMetadata, providerMetadataKey);
370
+ const rawID = itemID(part.providerMetadata, providerMetadataKey);
371
+ const id = acceptsItemID(extension, "message", rawID) ? rawID : undefined;
363
372
  const phase = ProviderShared.isRecord(metadata) ? messagePhase(metadata.phase) : undefined;
364
373
  const group = groups.at(-1);
365
374
  if (group && group.id === id && group.phase === phase)
@@ -384,7 +393,7 @@ const lowerMessages = Effect.fn("OpenResponses.lowerMessages")(function* (reques
384
393
  }
385
394
  if (part.type === "reasoning") {
386
395
  flushText();
387
- const reasoning = lowerReasoning(part, providerMetadataKey);
396
+ const reasoning = lowerReasoning(part, providerMetadataKey, extension);
388
397
  if (!reasoning)
389
398
  continue;
390
399
  if (store !== false) {
@@ -408,14 +417,15 @@ const lowerMessages = Effect.fn("OpenResponses.lowerMessages")(function* (reques
408
417
  flushText();
409
418
  if (part.providerExecuted === true)
410
419
  continue;
411
- input.push(lowerToolCall(part, providerMetadataKey));
420
+ input.push(lowerToolCall(part, providerMetadataKey, extension));
412
421
  continue;
413
422
  }
414
423
  if (part.type === "tool-result" && part.providerExecuted === true) {
415
424
  flushText();
416
425
  const id = itemID(part.providerMetadata, providerMetadataKey);
417
- if (store !== false && id && !hostedToolReferences.has(id))
418
- input.push({ type: "item_reference", id });
426
+ const reference = acceptsItemID(extension, "reference", id) ? id : undefined;
427
+ if (store !== false && reference && !hostedToolReferences.has(reference))
428
+ input.push({ type: "item_reference", id: reference });
419
429
  if (store === false) {
420
430
  // The server is not storing this exchange, so the tool outcome has to
421
431
  // travel in the input. Non-content results degrade to their text form.
@@ -427,8 +437,8 @@ const lowerMessages = Effect.fn("OpenResponses.lowerMessages")(function* (reques
427
437
  content: yield* Effect.forEach(content, (item) => lowerHostedToolResultContentItem(item, request, extension)),
428
438
  });
429
439
  }
430
- if (id)
431
- hostedToolReferences.add(id);
440
+ if (reference)
441
+ hostedToolReferences.add(reference);
432
442
  continue;
433
443
  }
434
444
  return yield* ProviderShared.unsupportedContent(extension.name, "assistant", [
@@ -43,9 +43,27 @@ const OpenAIResponsesBody = Schema.Struct({
43
43
  ...OpenAIResponsesCoreFields,
44
44
  stream: Schema.Literal(true),
45
45
  });
46
+ // Replayed items are paired with stored server state by id, so a foreign or
47
+ // synthetic token can fail request validation even when `call_id` pairing is
48
+ // intact. Only resend ids in each item kind's own grammar; hosted tool
49
+ // references keep generic validation because every hosted tool mints its own
50
+ // prefix. The same allowlist approach codex uses before resending history
51
+ // (codex-rs core/src/client.rs, `prepare_response_items_for_request`).
52
+ const ITEM_ID_PREFIXES = {
53
+ message: ["msg_"],
54
+ reasoning: ["rs_"],
55
+ "function-call": ["fc_"],
56
+ // Every hosted tool mints its own id prefix, so references keep generic
57
+ // validation only.
58
+ reference: [],
59
+ };
46
60
  const extension = {
47
61
  id: ADAPTER,
48
62
  name: NAME,
63
+ acceptsItemID: (kind, id) => {
64
+ const prefixes = ITEM_ID_PREFIXES[kind];
65
+ return prefixes.length === 0 || prefixes.some((prefix) => id.startsWith(prefix));
66
+ },
49
67
  };
50
68
  const nativeImageToolInput = (tool) => {
51
69
  const native = tool.native?.openai;
@@ -29,6 +29,7 @@ export declare const lower: (part: {
29
29
  readonly metadata?: {
30
30
  readonly [x: string]: unknown;
31
31
  } | undefined;
32
+ readonly cache?: import("../../schema/options.js").CacheHint | undefined;
32
33
  readonly filename?: string | undefined;
33
34
  }) => Effect.Effect<{
34
35
  readonly document: {
@@ -41,22 +41,46 @@ export declare const routes: import("../route/client.js").Route<{
41
41
  readonly type: "base64";
42
42
  readonly media_type: string;
43
43
  readonly data: string;
44
+ } | {
45
+ readonly type: "url";
46
+ readonly url: string;
47
+ } | {
48
+ readonly type: "file";
49
+ readonly file_id: string;
44
50
  };
45
51
  readonly cache_control?: {
46
52
  readonly type: "ephemeral";
47
53
  readonly ttl?: "1h" | "5m" | undefined;
48
54
  } | undefined;
55
+ readonly transformations?: {
56
+ readonly oversized_image?: "error" | "downsize" | undefined;
57
+ } | undefined;
49
58
  } | {
50
59
  readonly type: "document";
51
60
  readonly source: {
52
61
  readonly type: "base64";
53
62
  readonly media_type: "application/pdf";
54
63
  readonly data: string;
64
+ } | {
65
+ readonly type: "text";
66
+ readonly media_type: "text/plain";
67
+ readonly data: string;
68
+ } | {
69
+ readonly type: "url";
70
+ readonly url: string;
71
+ } | {
72
+ readonly type: "file";
73
+ readonly file_id: string;
55
74
  };
75
+ readonly title?: string | undefined;
76
+ readonly context?: string | undefined;
56
77
  readonly cache_control?: {
57
78
  readonly type: "ephemeral";
58
79
  readonly ttl?: "1h" | "5m" | undefined;
59
80
  } | undefined;
81
+ readonly citations?: {
82
+ readonly enabled: boolean;
83
+ } | undefined;
60
84
  } | {
61
85
  readonly type: "tool_result";
62
86
  readonly content: string | readonly ({
@@ -72,22 +96,46 @@ export declare const routes: import("../route/client.js").Route<{
72
96
  readonly type: "base64";
73
97
  readonly media_type: string;
74
98
  readonly data: string;
99
+ } | {
100
+ readonly type: "url";
101
+ readonly url: string;
102
+ } | {
103
+ readonly type: "file";
104
+ readonly file_id: string;
75
105
  };
76
106
  readonly cache_control?: {
77
107
  readonly type: "ephemeral";
78
108
  readonly ttl?: "1h" | "5m" | undefined;
79
109
  } | undefined;
110
+ readonly transformations?: {
111
+ readonly oversized_image?: "error" | "downsize" | undefined;
112
+ } | undefined;
80
113
  } | {
81
114
  readonly type: "document";
82
115
  readonly source: {
83
116
  readonly type: "base64";
84
117
  readonly media_type: "application/pdf";
85
118
  readonly data: string;
119
+ } | {
120
+ readonly type: "text";
121
+ readonly media_type: "text/plain";
122
+ readonly data: string;
123
+ } | {
124
+ readonly type: "url";
125
+ readonly url: string;
126
+ } | {
127
+ readonly type: "file";
128
+ readonly file_id: string;
86
129
  };
130
+ readonly title?: string | undefined;
131
+ readonly context?: string | undefined;
87
132
  readonly cache_control?: {
88
133
  readonly type: "ephemeral";
89
134
  readonly ttl?: "1h" | "5m" | undefined;
90
135
  } | undefined;
136
+ readonly citations?: {
137
+ readonly enabled: boolean;
138
+ } | undefined;
91
139
  })[];
92
140
  readonly tool_use_id: string;
93
141
  readonly cache_control?: {
@@ -25,22 +25,46 @@ export declare const routes: import("../route/client.js").Route<{
25
25
  readonly type: "base64";
26
26
  readonly media_type: string;
27
27
  readonly data: string;
28
+ } | {
29
+ readonly type: "url";
30
+ readonly url: string;
31
+ } | {
32
+ readonly type: "file";
33
+ readonly file_id: string;
28
34
  };
29
35
  readonly cache_control?: {
30
36
  readonly type: "ephemeral";
31
37
  readonly ttl?: "1h" | "5m" | undefined;
32
38
  } | undefined;
39
+ readonly transformations?: {
40
+ readonly oversized_image?: "error" | "downsize" | undefined;
41
+ } | undefined;
33
42
  } | {
34
43
  readonly type: "document";
35
44
  readonly source: {
36
45
  readonly type: "base64";
37
46
  readonly media_type: "application/pdf";
38
47
  readonly data: string;
48
+ } | {
49
+ readonly type: "text";
50
+ readonly media_type: "text/plain";
51
+ readonly data: string;
52
+ } | {
53
+ readonly type: "url";
54
+ readonly url: string;
55
+ } | {
56
+ readonly type: "file";
57
+ readonly file_id: string;
39
58
  };
59
+ readonly title?: string | undefined;
60
+ readonly context?: string | undefined;
40
61
  readonly cache_control?: {
41
62
  readonly type: "ephemeral";
42
63
  readonly ttl?: "1h" | "5m" | undefined;
43
64
  } | undefined;
65
+ readonly citations?: {
66
+ readonly enabled: boolean;
67
+ } | undefined;
44
68
  } | {
45
69
  readonly type: "tool_result";
46
70
  readonly content: string | readonly ({
@@ -56,22 +80,46 @@ export declare const routes: import("../route/client.js").Route<{
56
80
  readonly type: "base64";
57
81
  readonly media_type: string;
58
82
  readonly data: string;
83
+ } | {
84
+ readonly type: "url";
85
+ readonly url: string;
86
+ } | {
87
+ readonly type: "file";
88
+ readonly file_id: string;
59
89
  };
60
90
  readonly cache_control?: {
61
91
  readonly type: "ephemeral";
62
92
  readonly ttl?: "1h" | "5m" | undefined;
63
93
  } | undefined;
94
+ readonly transformations?: {
95
+ readonly oversized_image?: "error" | "downsize" | undefined;
96
+ } | undefined;
64
97
  } | {
65
98
  readonly type: "document";
66
99
  readonly source: {
67
100
  readonly type: "base64";
68
101
  readonly media_type: "application/pdf";
69
102
  readonly data: string;
103
+ } | {
104
+ readonly type: "text";
105
+ readonly media_type: "text/plain";
106
+ readonly data: string;
107
+ } | {
108
+ readonly type: "url";
109
+ readonly url: string;
110
+ } | {
111
+ readonly type: "file";
112
+ readonly file_id: string;
70
113
  };
114
+ readonly title?: string | undefined;
115
+ readonly context?: string | undefined;
71
116
  readonly cache_control?: {
72
117
  readonly type: "ephemeral";
73
118
  readonly ttl?: "1h" | "5m" | undefined;
74
119
  } | undefined;
120
+ readonly citations?: {
121
+ readonly enabled: boolean;
122
+ } | undefined;
75
123
  })[];
76
124
  readonly tool_use_id: string;
77
125
  readonly cache_control?: {
@@ -58,22 +58,46 @@ export declare const routes: Route<{
58
58
  readonly type: "base64";
59
59
  readonly media_type: string;
60
60
  readonly data: string;
61
+ } | {
62
+ readonly type: "url";
63
+ readonly url: string;
64
+ } | {
65
+ readonly type: "file";
66
+ readonly file_id: string;
61
67
  };
62
68
  readonly cache_control?: {
63
69
  readonly type: "ephemeral";
64
70
  readonly ttl?: "1h" | "5m" | undefined;
65
71
  } | undefined;
72
+ readonly transformations?: {
73
+ readonly oversized_image?: "error" | "downsize" | undefined;
74
+ } | undefined;
66
75
  } | {
67
76
  readonly type: "document";
68
77
  readonly source: {
69
78
  readonly type: "base64";
70
79
  readonly media_type: "application/pdf";
71
80
  readonly data: string;
81
+ } | {
82
+ readonly type: "text";
83
+ readonly media_type: "text/plain";
84
+ readonly data: string;
85
+ } | {
86
+ readonly type: "url";
87
+ readonly url: string;
88
+ } | {
89
+ readonly type: "file";
90
+ readonly file_id: string;
72
91
  };
92
+ readonly title?: string | undefined;
93
+ readonly context?: string | undefined;
73
94
  readonly cache_control?: {
74
95
  readonly type: "ephemeral";
75
96
  readonly ttl?: "1h" | "5m" | undefined;
76
97
  } | undefined;
98
+ readonly citations?: {
99
+ readonly enabled: boolean;
100
+ } | undefined;
77
101
  } | {
78
102
  readonly type: "tool_result";
79
103
  readonly content: string | readonly ({
@@ -89,22 +113,46 @@ export declare const routes: Route<{
89
113
  readonly type: "base64";
90
114
  readonly media_type: string;
91
115
  readonly data: string;
116
+ } | {
117
+ readonly type: "url";
118
+ readonly url: string;
119
+ } | {
120
+ readonly type: "file";
121
+ readonly file_id: string;
92
122
  };
93
123
  readonly cache_control?: {
94
124
  readonly type: "ephemeral";
95
125
  readonly ttl?: "1h" | "5m" | undefined;
96
126
  } | undefined;
127
+ readonly transformations?: {
128
+ readonly oversized_image?: "error" | "downsize" | undefined;
129
+ } | undefined;
97
130
  } | {
98
131
  readonly type: "document";
99
132
  readonly source: {
100
133
  readonly type: "base64";
101
134
  readonly media_type: "application/pdf";
102
135
  readonly data: string;
136
+ } | {
137
+ readonly type: "text";
138
+ readonly media_type: "text/plain";
139
+ readonly data: string;
140
+ } | {
141
+ readonly type: "url";
142
+ readonly url: string;
143
+ } | {
144
+ readonly type: "file";
145
+ readonly file_id: string;
103
146
  };
147
+ readonly title?: string | undefined;
148
+ readonly context?: string | undefined;
104
149
  readonly cache_control?: {
105
150
  readonly type: "ephemeral";
106
151
  readonly ttl?: "1h" | "5m" | undefined;
107
152
  } | undefined;
153
+ readonly citations?: {
154
+ readonly enabled: boolean;
155
+ } | undefined;
108
156
  })[];
109
157
  readonly tool_use_id: string;
110
158
  readonly cache_control?: {
@@ -34,6 +34,7 @@ export declare const MediaPart: Schema.Struct<{
34
34
  readonly mediaType: Schema.String;
35
35
  readonly data: Schema.Union<readonly [Schema.String, Schema.Uint8Array]>;
36
36
  readonly filename: Schema.optional<Schema.String>;
37
+ readonly cache: Schema.optional<typeof CacheHint>;
37
38
  readonly metadata: Schema.optional<Schema.$Record<Schema.String, Schema.Unknown>>;
38
39
  }>;
39
40
  export type MediaPart = Schema.Schema.Type<typeof MediaPart>;
@@ -175,6 +176,7 @@ export declare const ContentPart: Schema.toTaggedUnion<"type", readonly [Schema.
175
176
  readonly mediaType: Schema.String;
176
177
  readonly data: Schema.Union<readonly [Schema.String, Schema.Uint8Array]>;
177
178
  readonly filename: Schema.optional<Schema.String>;
179
+ readonly cache: Schema.optional<typeof CacheHint>;
178
180
  readonly metadata: Schema.optional<Schema.$Record<Schema.String, Schema.Unknown>>;
179
181
  }>, Schema.Struct<{
180
182
  readonly type: Schema.Literal<"tool-call">;
@@ -247,6 +249,7 @@ declare const Message_base: Schema.Class<Message, Schema.Struct<{
247
249
  readonly mediaType: Schema.String;
248
250
  readonly data: Schema.Union<readonly [Schema.String, Schema.Uint8Array]>;
249
251
  readonly filename: Schema.optional<Schema.String>;
252
+ readonly cache: Schema.optional<typeof CacheHint>;
250
253
  readonly metadata: Schema.optional<Schema.$Record<Schema.String, Schema.Unknown>>;
251
254
  }>, Schema.Struct<{
252
255
  readonly type: Schema.Literal<"tool-call">;
@@ -33,6 +33,7 @@ export const MediaPart = Schema.Struct({
33
33
  mediaType: Schema.String,
34
34
  data: Schema.Union([Schema.String, Schema.Uint8Array]),
35
35
  filename: Schema.optional(Schema.String),
36
+ cache: Schema.optional(CacheHint),
36
37
  metadata: Schema.optional(Schema.Record(Schema.String, Schema.Unknown)),
37
38
  }).annotate({ identifier: "LLM.Content.Media" });
38
39
  const isToolResultValue = (value) => isRecord(value) &&
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
- "version": "0.0.0-dev-18049",
3
+ "version": "0.0.0-dev-18052",
4
4
  "name": "@opencode-ai/ai",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -30,7 +30,7 @@
30
30
  "devDependencies": {
31
31
  "@clack/prompts": "1.0.0-alpha.1",
32
32
  "@effect/platform-node": "4.0.0-rc.111",
33
- "@opencode-ai/http-recorder": "0.0.0-dev-18049",
33
+ "@opencode-ai/http-recorder": "0.0.0-dev-18052",
34
34
  "@tsconfig/bun": "1.0.9",
35
35
  "@types/bun": "1.3.13",
36
36
  "@typescript/native-preview": "7.0.0-dev.20251207.1",
@@ -39,7 +39,7 @@
39
39
  "dependencies": {
40
40
  "@smithy/eventstream-codec": "4.2.14",
41
41
  "@smithy/util-utf8": "4.2.2",
42
- "@opencode-ai/schema": "0.0.0-dev-18049",
42
+ "@opencode-ai/schema": "0.0.0-dev-18052",
43
43
  "aws4fetch": "1.0.20",
44
44
  "effect": "4.0.0-rc.111",
45
45
  "google-auth-library": "10.5.0"