@opencode/ai 2.0.25 → 2.0.27

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 (93) hide show
  1. package/README.md +7 -1
  2. package/dist/experimental/system-one.js +1 -1
  3. package/dist/media.js +7 -6
  4. package/dist/protocols/anthropic-messages.js +1 -1
  5. package/dist/protocols/bedrock-converse.js +4 -3
  6. package/dist/protocols/bedrock-event-stream.js +4 -3
  7. package/dist/protocols/meta-responses.js +3 -2
  8. package/dist/protocols/mistral-chat.d.ts +1 -1
  9. package/dist/protocols/mistral-chat.js +1 -1
  10. package/dist/protocols/open-responses-channel.d.ts +1 -1
  11. package/dist/protocols/open-responses-channel.js +1 -1
  12. package/dist/protocols/openai-responses.d.ts +5 -5
  13. package/dist/protocols/openai-responses.js +4 -3
  14. package/dist/protocols/shared.d.ts +1 -1
  15. package/dist/protocols/shared.js +1 -1
  16. package/dist/protocols/utils/bedrock-auth.js +1 -1
  17. package/dist/protocols/utils/bedrock-media.js +4 -3
  18. package/dist/protocols/utils/fal-queue.d.ts +1 -1
  19. package/dist/protocols/utils/media-input.js +4 -3
  20. package/dist/protocols/utils/tool-schema.js +8 -2
  21. package/dist/providers/amazon-bedrock-mantle.js +2 -2
  22. package/dist/providers/anthropic-compatible.js +2 -2
  23. package/dist/providers/anthropic.js +2 -2
  24. package/dist/providers/azure.d.ts +2 -2
  25. package/dist/providers/azure.js +3 -3
  26. package/dist/providers/baseten.js +2 -2
  27. package/dist/providers/cerebras.js +2 -2
  28. package/dist/providers/cloudflare-ai-gateway.d.ts +2 -2
  29. package/dist/providers/cloudflare-ai-gateway.js +2 -2
  30. package/dist/providers/cloudflare-workers-ai.js +2 -2
  31. package/dist/providers/cohere/chat.js +1 -1
  32. package/dist/providers/cohere.js +1 -1
  33. package/dist/providers/deepinfra.js +2 -2
  34. package/dist/providers/deepseek.js +2 -2
  35. package/dist/providers/fireworks.js +2 -2
  36. package/dist/providers/google/interactions.js +2 -2
  37. package/dist/providers/google-vertex/interactions.d.ts +2 -0
  38. package/dist/providers/google-vertex/interactions.js +1 -0
  39. package/dist/providers/google-vertex/mistral.d.ts +2 -0
  40. package/dist/providers/google-vertex/mistral.js +1 -0
  41. package/dist/providers/google-vertex-chat.js +3 -3
  42. package/dist/providers/google-vertex-interactions.d.ts +112 -0
  43. package/dist/providers/google-vertex-interactions.js +64 -0
  44. package/dist/providers/google-vertex-messages.js +3 -3
  45. package/dist/providers/google-vertex-mistral.d.ts +124 -0
  46. package/dist/providers/google-vertex-mistral.js +69 -0
  47. package/dist/providers/google-vertex-responses.js +3 -3
  48. package/dist/providers/google-vertex-shared.d.ts +7 -2
  49. package/dist/providers/google-vertex-shared.js +36 -4
  50. package/dist/providers/google-vertex.js +3 -3
  51. package/dist/providers/google.js +2 -2
  52. package/dist/providers/groq.js +2 -2
  53. package/dist/providers/index.d.ts +2 -0
  54. package/dist/providers/index.js +2 -0
  55. package/dist/providers/meta.js +1 -1
  56. package/dist/providers/minimax/chat.js +1 -1
  57. package/dist/providers/minimax/responses.js +1 -1
  58. package/dist/providers/minimax.js +1 -1
  59. package/dist/providers/mistral.js +2 -2
  60. package/dist/providers/moonshot/messages.js +1 -1
  61. package/dist/providers/moonshot/responses.js +1 -1
  62. package/dist/providers/moonshot.js +1 -1
  63. package/dist/providers/openai-compatible-responses.js +2 -2
  64. package/dist/providers/openai-compatible.js +2 -2
  65. package/dist/providers/openai.d.ts +1 -1
  66. package/dist/providers/openai.js +2 -2
  67. package/dist/providers/openrouter.js +1 -1
  68. package/dist/providers/togetherai.js +2 -2
  69. package/dist/providers/venice.js +1 -1
  70. package/dist/providers/vercel-ai-gateway.js +2 -2
  71. package/dist/providers/xai.js +1 -1
  72. package/dist/providers/zai-coding-plan/messages.js +1 -1
  73. package/dist/providers/zai-coding-plan/responses.js +1 -1
  74. package/dist/providers/zai-coding-plan.js +1 -1
  75. package/dist/providers/zai.js +1 -1
  76. package/dist/route/auth.d.ts +1 -1
  77. package/dist/route/auth.js +2 -2
  78. package/dist/route/executor-service.d.ts +1 -1
  79. package/dist/route/executor.d.ts +1 -1
  80. package/dist/route/executor.js +1 -1
  81. package/dist/route/framing.js +1 -1
  82. package/dist/route/media-protocol.d.ts +1 -1
  83. package/dist/route/media-protocol.js +1 -1
  84. package/dist/route/media.js +1 -1
  85. package/dist/route/transport/http.d.ts +1 -1
  86. package/dist/route/transport/http.js +1 -1
  87. package/dist/route/transport/websocket-channel.d.ts +1 -1
  88. package/dist/route/transport/websocket.d.ts +4 -4
  89. package/dist/route/transport/websocket.js +4 -9
  90. package/dist/schema/messages.js +9 -8
  91. package/dist/tool.d.ts +10 -0
  92. package/dist/tool.js +21 -2
  93. package/package.json +5 -5
package/README.md CHANGED
@@ -32,6 +32,11 @@ Anthropic Messages, Gemini, Bedrock Converse, and any OpenAI-compatible deployme
32
32
  `Google.configure({ apiKey }).interactions(modelID)` selects the Interactions API; `.model(modelID)` still selects
33
33
  GenerateContent. The package entrypoint is `@opencode/ai/providers/google/interactions`.
34
34
 
35
+ For Vertex AI, use `GoogleVertexInteractions.configure({ project, location: "global" }).model(modelID)` or
36
+ `@opencode/ai/providers/google-vertex/interactions`. It uses Application Default Credentials by default, accepts
37
+ an explicit `accessToken` or `auth` override, and supports Express mode with `apiKey`. Vertex metadata and raw usage
38
+ live under `providerMetadata.vertex`. The default is still full-history replay with `store: false`.
39
+
35
40
  ```ts
36
41
  const model = Google.configure({ apiKey }).interactions("gemini-3.8-flash")
37
42
  const response = yield* LLM.generate({
@@ -1316,13 +1321,14 @@ APIs have separate entrypoints:
1316
1321
  - `@opencode/ai/providers/openai-compatible/responses`
1317
1322
  - `@opencode/ai/providers/anthropic-compatible`
1318
1323
  - `@opencode/ai/providers/google-vertex/gemini`
1324
+ - `@opencode/ai/providers/google-vertex/interactions`
1319
1325
  - `@opencode/ai/providers/google-vertex/chat`
1320
1326
  - `@opencode/ai/providers/google-vertex/responses`
1321
1327
  - `@opencode/ai/providers/google-vertex/messages`
1322
1328
 
1323
1329
  OpenAI Responses has one semantic route and uses HTTP by default. Advanced callers may supply a per-call WebSocket channel executor through `StreamOptions`; transport policy does not change provider settings, model identity, or route identity. The provider-neutral Open Responses implementation owns the reusable WebSocket request and event contract, while each provider opts in with its own handshake and connection policy. Azure follows the same Chat/Responses split at `providers/azure/chat` and `providers/azure/responses`. Generic OpenAI-compatible Chat remains at `providers/openai-compatible`; the Responses adapter at `providers/openai-compatible/responses` uses the provider-neutral Open Responses protocol. OpenAI Responses extends that baseline with OpenAI tools, event variants, metadata, and defaults. Generic Anthropic Messages-compatible providers use `providers/anthropic-compatible`, which the named Anthropic provider composes. Google Gemini and Amazon Bedrock expose their single native API through their existing provider paths.
1324
1330
 
1325
- Vertex Gemini, Vertex Chat, Vertex Responses, and Vertex Messages are separate API entrypoints. All accept `project`, `location`, and an optional `accessToken`; when no explicit token or auth override is supplied they lazily use Google Application Default Credentials. Vertex Gemini instead selects express mode when `apiKey` or `GOOGLE_VERTEX_API_KEY` is present. Vertex Chat targets MaaS models through the OpenAI-compatible Chat Completions endpoint, while Vertex Responses targets Grok models and defaults `store` to `false` as required by Vertex. `providers/google-vertex` remains the default alias for `providers/google-vertex/gemini`.
1331
+ Vertex Gemini, Vertex Interactions, Vertex Chat, Vertex Responses, and Vertex Messages are separate API entrypoints. All accept `project`, `location`, and an optional `accessToken`; when no explicit token or auth override is supplied they lazily use Google Application Default Credentials. Vertex Gemini and Interactions instead select express mode when `apiKey` or `GOOGLE_VERTEX_API_KEY` is present (explicit `project` or `location` selects ADC over an ambient key). Vertex Chat targets MaaS models through the OpenAI-compatible Chat Completions endpoint, while Vertex Responses targets Grok models and defaults `store` to `false` as required by Vertex. `providers/google-vertex` remains the default alias for `providers/google-vertex/gemini`.
1326
1332
 
1327
1333
  Tuned Vertex Gemini deployments use model ids shaped like `endpoints/1234567890` and require OAuth or ADC; Vertex express-mode API keys support publisher models only.
1328
1334
 
@@ -1,5 +1,5 @@
1
1
  import { Effect, Schema } from "effect";
2
- import { Headers, HttpClientRequest } from "effect/unstable/http";
2
+ import { Headers, HttpClientRequest } from "effect/http";
3
3
  import { ChoiceQuestion, EvaluationInput, EvaluationModel, EvaluationResponse, EvaluationRounding, ScoreQuestion, } from "./evaluation.js";
4
4
  import { Auth } from "../route/auth.js";
5
5
  import { AIError, HttpContext, HttpOptions, InvalidProviderOutputError, InvalidRequestError, ModelID, Usage, mergeJsonRecords, } from "../schema/index.js";
package/dist/media.js CHANGED
@@ -1,6 +1,7 @@
1
1
  export * as Media from "./media.js";
2
- import { Effect, Encoding, FileSystem, Schema, SchemaGetter } from "effect";
3
- import { HttpClientRequest } from "effect/unstable/http";
2
+ import { Effect, FileSystem, Schema, SchemaGetter } from "effect";
3
+ import { Base64 } from "effect/encoding";
4
+ import { HttpClientRequest } from "effect/http";
4
5
  import { ProviderID } from "./schema/ids.js";
5
6
  import { AIError, HttpContext, InvalidProviderOutputError, InvalidRequestError } from "./schema/errors.js";
6
7
  import { ProviderMetadata } from "./schema/options.js";
@@ -105,7 +106,7 @@ export class Asset {
105
106
  const source = this.source;
106
107
  if (source.type !== "bytes" && source.type !== "base64")
107
108
  return undefined;
108
- const base64 = source.type === "base64" ? source.data : (this.#base64 ??= Encoding.encodeBase64(source.data));
109
+ const base64 = source.type === "base64" ? source.data : (this.#base64 ??= Base64.encode(source.data));
109
110
  const mime = this.mediaType.toLowerCase();
110
111
  return { mime, base64, dataUrl: `data:${mime};base64,${base64}` };
111
112
  }
@@ -120,7 +121,7 @@ export class Asset {
120
121
  if (source.type === "ref")
121
122
  return Effect.fail(invalid(`Cannot materialize provider ref ${source.provider}:${source.id}`));
122
123
  const decoded = source.type === "base64"
123
- ? Effect.fromResult(Encoding.decodeBase64(source.data)).pipe(Effect.mapError((cause) => invalid(`Media asset contains invalid base64 data`, cause)))
124
+ ? Effect.fromResult(Base64.decode(source.data)).pipe(Effect.mapError((cause) => invalid(`Media asset contains invalid base64 data`, cause)))
124
125
  : download(source, this.headers);
125
126
  return decoded.pipe(Effect.tap((data) => Effect.sync(() => (this.#bytes = data))));
126
127
  });
@@ -132,7 +133,7 @@ export class Asset {
132
133
  return Effect.succeed(source.data);
133
134
  if (this.#base64 !== undefined)
134
135
  return Effect.succeed(this.#base64);
135
- return this.bytes().pipe(Effect.map((data) => (this.#base64 = Encoding.encodeBase64(data))));
136
+ return this.bytes().pipe(Effect.map((data) => (this.#base64 = Base64.encode(data))));
136
137
  });
137
138
  }
138
139
  dataUrl() {
@@ -145,7 +146,7 @@ export class Asset {
145
146
  toJSON() {
146
147
  const source = this.source;
147
148
  return {
148
- source: source.type === "bytes" ? { ...source, data: Encoding.encodeBase64(source.data) } : source,
149
+ source: source.type === "bytes" ? { ...source, data: Base64.encode(source.data) } : source,
149
150
  info: this.info,
150
151
  providerMetadata: this.providerMetadata,
151
152
  };
@@ -6,7 +6,7 @@ import { Auth } from "../route/auth.js";
6
6
  import { Endpoint } from "../route/endpoint.js";
7
7
  import { Framing } from "../route/framing.js";
8
8
  import { Protocol } from "../route/protocol.js";
9
- import { Headers } from "effect/unstable/http";
9
+ import { Headers } from "effect/http";
10
10
  import { HttpTransport } from "../route/transport/index.js";
11
11
  import { AIError, HttpOptions, LLMRequest, LLMEvent, mergeJsonRecords, Usage, } from "../schema/index.js";
12
12
  import { JsonObject, knownString, optionalArray, optionalNull, ProviderShared } from "./shared.js";
@@ -1,4 +1,5 @@
1
- import { Effect, Encoding, Schema } from "effect";
1
+ import { Effect, Schema } from "effect";
2
+ import { Base64 } from "effect/encoding";
2
3
  import { Route } from "../route/client.js";
3
4
  import { Endpoint } from "../route/endpoint.js";
4
5
  import { Protocol } from "../route/protocol.js";
@@ -489,7 +490,7 @@ const mapUsage = (usage, providerMetadataKey) => {
489
490
  providerMetadata: { [providerMetadataKey]: usage },
490
491
  });
491
492
  };
492
- const encodeRedactedContent = (chunks) => Encoding.encodeBase64(concatBytes(chunks));
493
+ const encodeRedactedContent = (chunks) => Base64.encode(concatBytes(chunks));
493
494
  const step = (state, event) => Effect.gen(function* () {
494
495
  if (event.contentBlockStart?.start?.toolUse) {
495
496
  const index = event.contentBlockStart.contentBlockIndex;
@@ -530,7 +531,7 @@ const step = (state, event) => Effect.gen(function* () {
530
531
  const redactedChunk = yield* (() => {
531
532
  if (reasoning.redactedContent === undefined)
532
533
  return Effect.succeed(undefined);
533
- return Effect.fromResult(Encoding.decodeBase64(reasoning.redactedContent)).pipe(Effect.mapError((cause) => ProviderShared.eventError(ADAPTER, "Bedrock Converse reasoningContent.redactedContent contains invalid base64 data", undefined, cause)));
534
+ return Effect.fromResult(Base64.decode(reasoning.redactedContent)).pipe(Effect.mapError((cause) => ProviderShared.eventError(ADAPTER, "Bedrock Converse reasoningContent.redactedContent contains invalid base64 data", undefined, cause)));
534
535
  })();
535
536
  const redactedChunks = state.reasoningRedactedContent[index] ?? [];
536
537
  if (redactedChunk !== undefined)
@@ -1,6 +1,7 @@
1
1
  import { EventStreamCodec } from "@smithy/eventstream-codec";
2
2
  import { fromUtf8, toUtf8 } from "@smithy/util-utf8";
3
- import { Effect, Encoding, Stream } from "effect";
3
+ import { Effect, Stream } from "effect";
4
+ import { Base64 } from "effect/encoding";
4
5
  import { AIError, AIErrorReason, InvalidProviderOutputError } from "../schema/index.js";
5
6
  import { Framing } from "../route/framing.js";
6
7
  import { ProviderShared } from "./shared.js";
@@ -31,7 +32,7 @@ const consumeFrames = (route) => (state, input) => Effect.gen(function* () {
31
32
  route,
32
33
  classification: "incomplete-stream",
33
34
  message: `Incomplete Bedrock Converse event-stream frame: ${remaining.length} buffered bytes remain at end of stream`,
34
- body: Encoding.encodeBase64(remaining),
35
+ body: Base64.encode(remaining),
35
36
  }),
36
37
  });
37
38
  return [state, []];
@@ -45,7 +46,7 @@ const consumeFrames = (route) => (state, input) => Effect.gen(function* () {
45
46
  break;
46
47
  const decoded = yield* Effect.try({
47
48
  try: () => eventCodec.decode(view.subarray(0, totalLength)),
48
- catch: (error) => ProviderShared.eventError(route, `Failed to decode Bedrock Converse event-stream frame: ${error instanceof Error ? error.message : String(error)}`, Encoding.encodeBase64(view.subarray(0, totalLength)), error),
49
+ catch: (error) => ProviderShared.eventError(route, `Failed to decode Bedrock Converse event-stream frame: ${error instanceof Error ? error.message : String(error)}`, Base64.encode(view.subarray(0, totalLength)), error),
49
50
  });
50
51
  cursor = { buffer: cursor.buffer, offset: cursor.offset + totalLength };
51
52
  const payload = utf8.decode(decoded.body);
@@ -1,4 +1,5 @@
1
- import { Effect, Encoding, Schema } from "effect";
1
+ import { Effect, Schema } from "effect";
2
+ import { Base64 } from "effect/encoding";
2
3
  import { Protocol } from "../route/protocol.js";
3
4
  import { LLMEvent, LLMRequest, Message, ToolResultPart } from "../schema/index.js";
4
5
  import { OpenResponses } from "./open-responses.js";
@@ -80,7 +81,7 @@ const HOSTED_TOOLS = {
80
81
  return { type: "error", value: item.error };
81
82
  if (!item.result)
82
83
  return yield* ProviderShared.eventError(ADAPTER, "Meta returned an image without data", ProviderShared.encodeJson(raw));
83
- const data = yield* Effect.fromResult(Encoding.decodeBase64(item.result)).pipe(Effect.mapError((cause) => ProviderShared.eventError(ADAPTER, "Meta returned invalid image base64", ProviderShared.encodeJson(raw), cause)));
84
+ const data = yield* Effect.fromResult(Base64.decode(item.result)).pipe(Effect.mapError((cause) => ProviderShared.eventError(ADAPTER, "Meta returned invalid image base64", ProviderShared.encodeJson(raw), cause)));
84
85
  // Responses image items can omit output_format, including when PNG/JPEG was requested.
85
86
  const mime = item.output_format === undefined
86
87
  ? (detectMediaType(data) ?? "application/octet-stream")
@@ -32,7 +32,7 @@ export type ProviderOptionsInput = {
32
32
  readonly promptCacheKey?: string;
33
33
  readonly [key: string]: unknown;
34
34
  };
35
- declare const MistralBody: Schema.Struct<{
35
+ export declare const MistralBody: Schema.Struct<{
36
36
  readonly model: Schema.String;
37
37
  readonly messages: Schema.$Array<Schema.toTaggedUnion<"role", readonly [Schema.Struct<{
38
38
  readonly role: Schema.Literal<"system">;
@@ -78,7 +78,7 @@ const MistralOptions = Schema.Struct({
78
78
  promptMode: Schema.optional(Schema.Literal("reasoning")),
79
79
  promptCacheKey: Schema.optional(Schema.String),
80
80
  });
81
- const MistralBody = Schema.Struct({
81
+ export const MistralBody = Schema.Struct({
82
82
  model: Schema.String,
83
83
  messages: Schema.Array(MistralMessage),
84
84
  tools: optionalArray(MistralTool),
@@ -1,4 +1,4 @@
1
- import { Headers } from "effect/unstable/http";
1
+ import { Headers } from "effect/http";
2
2
  import { HttpTransport, type Transport, type WebSocketChannelDriver } from "../route/transport/index.js";
3
3
  import { OpenResponsesContinuation } from "./open-responses-continuation.js";
4
4
  export interface Options {
@@ -1,5 +1,5 @@
1
1
  import { Effect, Schema, Stream } from "effect";
2
- import { Headers } from "effect/unstable/http";
2
+ import { Headers } from "effect/http";
3
3
  import { Framing } from "../route/framing.js";
4
4
  import { HttpTransport, WebSocketTransport, } from "../route/transport/index.js";
5
5
  import * as ProviderShared from "./shared.js";
@@ -348,7 +348,7 @@ export declare const protocol: Protocol<{
348
348
  } | {
349
349
  readonly type: "image_generation";
350
350
  readonly size?: string | undefined;
351
- readonly action?: "generate" | "auto" | "edit" | undefined;
351
+ readonly action?: "auto" | "generate" | "edit" | undefined;
352
352
  readonly output_format?: "png" | "jpeg" | "webp" | undefined;
353
353
  readonly quality?: "auto" | "low" | "medium" | "high" | undefined;
354
354
  readonly background?: "auto" | "opaque" | "transparent" | undefined;
@@ -610,7 +610,7 @@ export declare const httpTransport: HttpTransport.HttpJsonTransport<{
610
610
  } | {
611
611
  readonly type: "image_generation";
612
612
  readonly size?: string | undefined;
613
- readonly action?: "generate" | "auto" | "edit" | undefined;
613
+ readonly action?: "auto" | "generate" | "edit" | undefined;
614
614
  readonly output_format?: "png" | "jpeg" | "webp" | undefined;
615
615
  readonly quality?: "auto" | "low" | "medium" | "high" | undefined;
616
616
  readonly background?: "auto" | "opaque" | "transparent" | undefined;
@@ -814,7 +814,7 @@ export declare const channelTransport: (options: import("./open-responses-channe
814
814
  } | {
815
815
  readonly type: "image_generation";
816
816
  readonly size?: string | undefined;
817
- readonly action?: "generate" | "auto" | "edit" | undefined;
817
+ readonly action?: "auto" | "generate" | "edit" | undefined;
818
818
  readonly output_format?: "png" | "jpeg" | "webp" | undefined;
819
819
  readonly quality?: "auto" | "low" | "medium" | "high" | undefined;
820
820
  readonly background?: "auto" | "opaque" | "transparent" | undefined;
@@ -1018,7 +1018,7 @@ export declare const transport: import("../route/transport/index.js").Transport<
1018
1018
  } | {
1019
1019
  readonly type: "image_generation";
1020
1020
  readonly size?: string | undefined;
1021
- readonly action?: "generate" | "auto" | "edit" | undefined;
1021
+ readonly action?: "auto" | "generate" | "edit" | undefined;
1022
1022
  readonly output_format?: "png" | "jpeg" | "webp" | undefined;
1023
1023
  readonly quality?: "auto" | "low" | "medium" | "high" | undefined;
1024
1024
  readonly background?: "auto" | "opaque" | "transparent" | undefined;
@@ -1222,7 +1222,7 @@ export declare const route: Route<{
1222
1222
  } | {
1223
1223
  readonly type: "image_generation";
1224
1224
  readonly size?: string | undefined;
1225
- readonly action?: "generate" | "auto" | "edit" | undefined;
1225
+ readonly action?: "auto" | "generate" | "edit" | undefined;
1226
1226
  readonly output_format?: "png" | "jpeg" | "webp" | undefined;
1227
1227
  readonly quality?: "auto" | "low" | "medium" | "high" | undefined;
1228
1228
  readonly background?: "auto" | "opaque" | "transparent" | undefined;
@@ -1,5 +1,6 @@
1
- import { Effect, Encoding, Schema } from "effect";
2
- import { Headers } from "effect/unstable/http";
1
+ import { Effect, Schema } from "effect";
2
+ import { Base64 } from "effect/encoding";
3
+ import { Headers } from "effect/http";
3
4
  import { Route } from "../route/client.js";
4
5
  import { Auth } from "../route/auth.js";
5
6
  import { Endpoint } from "../route/endpoint.js";
@@ -194,7 +195,7 @@ const checkpointBody = {
194
195
  const hostedToolResult = Effect.fnUntraced(function* (item) {
195
196
  const isError = item.error !== undefined && item.error !== null;
196
197
  if (item.type === "image_generation_call" && item.result) {
197
- yield* Effect.fromResult(Encoding.decodeBase64(item.result)).pipe(Effect.mapError((cause) => ProviderShared.eventError(ADAPTER, "OpenAI Responses returned invalid image base64", undefined, cause)));
198
+ yield* Effect.fromResult(Base64.decode(item.result)).pipe(Effect.mapError((cause) => ProviderShared.eventError(ADAPTER, "OpenAI Responses returned invalid image base64", undefined, cause)));
198
199
  const format = item.output_format ?? "png";
199
200
  return {
200
201
  type: "content",
@@ -1,6 +1,6 @@
1
1
  import { Tool } from "@opencode/schema/tool";
2
2
  import { Effect, Schema } from "effect";
3
- import { Headers, HttpClientRequest } from "effect/unstable/http";
3
+ import { Headers, HttpClientRequest } from "effect/http";
4
4
  import { Media } from "../media.js";
5
5
  import { AIError, LLMRequest, Message, ToolDefinition, type ContentPart, type MediaPart, type OpenString, type ProviderID, type ToolEntry, type ToolResultPart } from "../schema/index.js";
6
6
  import { Json, decodeJson, encodeJson } from "../utils/json.js";
@@ -1,6 +1,6 @@
1
1
  import { Tool } from "@opencode/schema/tool";
2
2
  import { Effect, Option, Schema } from "effect";
3
- import { Headers, HttpClientRequest } from "effect/unstable/http";
3
+ import { Headers, HttpClientRequest } from "effect/http";
4
4
  import { Media } from "../media.js";
5
5
  import { InvalidProviderOutputError, InvalidRequestError, UnsupportedOperationError, AIError, LLMRequest, Message, ToolDefinition, } from "../schema/index.js";
6
6
  import { Json, decodeJson, encodeJson } from "../utils/json.js";
@@ -1,6 +1,6 @@
1
1
  import { AwsV4Signer } from "aws4fetch";
2
2
  import { Effect } from "effect";
3
- import { Headers } from "effect/unstable/http";
3
+ import { Headers } from "effect/http";
4
4
  import { Auth } from "../../route/auth.js";
5
5
  import { AIError, AuthenticationError } from "../../schema/index.js";
6
6
  import { ProviderShared } from "../shared.js";
@@ -1,4 +1,5 @@
1
- import { Effect, Encoding, Schema } from "effect";
1
+ import { Effect, Schema } from "effect";
2
+ import { Base64 } from "effect/encoding";
2
3
  import { ProviderShared } from "../shared.js";
3
4
  // Bedrock Converse accepts image `format` as the file extension and
4
5
  // `source.bytes` as base64 in the JSON wire format.
@@ -63,8 +64,8 @@ function documentName(filename, names) {
63
64
  }
64
65
  const mediaBase64 = Effect.fnUntraced(function* (part) {
65
66
  const media = yield* ProviderShared.requireInlineMedia("Bedrock Converse", part.media);
66
- const bytes = yield* Effect.fromResult(Encoding.decodeBase64(media.base64)).pipe(Effect.mapError((cause) => ProviderShared.invalidRequest("Bedrock Converse media data must be valid base64", cause)));
67
- return Encoding.encodeBase64(bytes);
67
+ const bytes = yield* Effect.fromResult(Base64.decode(media.base64)).pipe(Effect.mapError((cause) => ProviderShared.invalidRequest("Bedrock Converse media data must be valid base64", cause)));
68
+ return Base64.encode(bytes);
68
69
  });
69
70
  // Route by MIME. Known image/document formats lower into a typed block; anything
70
71
  // else fails with a clear error instead of silently degrading to a malformed
@@ -1,5 +1,5 @@
1
1
  import { Effect, Schema } from "effect";
2
- import type { HttpClientResponse } from "effect/unstable/http";
2
+ import type { HttpClientResponse } from "effect/http";
3
3
  import type { Media } from "../../media.js";
4
4
  import { MediaProtocol } from "../../route/media-protocol.js";
5
5
  import type { AIError } from "../../schema/index.js";
@@ -1,4 +1,5 @@
1
- import { Effect, Encoding } from "effect";
1
+ import { Effect } from "effect";
2
+ import { Base64 } from "effect/encoding";
2
3
  import { Media } from "../../media.js";
3
4
  import { mergeJsonRecords } from "../../schema/index.js";
4
5
  import { encodeJson } from "../../utils/json.js";
@@ -10,7 +11,7 @@ export const inlineBytes = (route, asset) => {
10
11
  const inline = asset.inline();
11
12
  if (!inline)
12
13
  return Effect.fail(ProviderShared.inlineRequired(route, asset));
13
- return Effect.fromResult(Encoding.decodeBase64(inline.base64)).pipe(Effect.mapError((cause) => ProviderShared.invalidRequest(`${route} media contains invalid base64 data`, cause)));
14
+ return Effect.fromResult(Base64.decode(inline.base64)).pipe(Effect.mapError((cause) => ProviderShared.invalidRequest(`${route} media contains invalid base64 data`, cause)));
14
15
  };
15
16
  /** Copied because `BlobPart` requires a plain `ArrayBuffer`. */
16
17
  export const blob = (data, mediaType) => {
@@ -33,7 +34,7 @@ export const dimensions = (size) => {
33
34
  /** Provider file handle when the ref belongs to this provider; refs from other providers are never forwarded. */
34
35
  export const refID = (asset, provider) => asset.source.type === "ref" && asset.source.provider === provider ? asset.source.id : undefined;
35
36
  /** Decode a provider's base64 output once into an owned `bytes` asset, sniffing the type when it is not declared. */
36
- export const decodedAsset = (invalid, label, data, mediaType, options) => Effect.fromResult(Encoding.decodeBase64(data)).pipe(Effect.mapError((cause) => invalid(`${label} contains invalid base64 data`, cause)), Effect.map((bytes) => Media.bytes(bytes, mediaType, options)));
37
+ export const decodedAsset = (invalid, label, data, mediaType, options) => Effect.fromResult(Base64.decode(data)).pipe(Effect.mapError((cause) => invalid(`${label} contains invalid base64 data`, cause)), Effect.map((bytes) => Media.bytes(bytes, mediaType, options)));
37
38
  /** One image of an OpenAI-shaped `data` array, which carries either `b64_json` or a `url`. */
38
39
  export const imageOutput = (invalid, label, item, mediaType, options) => {
39
40
  if (item.b64_json)
@@ -56,9 +56,15 @@ const MODEL_NAMES = [
56
56
  [/kimi/i, "moonshot"],
57
57
  ];
58
58
  // Tool arguments are always a JSON object, and most providers reject a tool schema whose root does not
59
- // declare `type: "object"`, such as `{}` or a bare `properties` map. Effect encodes an empty struct as
60
- // `anyOf` object or array; every object matches its bare object branch, so that `anyOf` is dropped.
59
+ // declare `type: "object"`, such as `{}` or a bare `properties` map. An object root makes Effect's
60
+ // empty-struct markers redundant: `{ not: { type: "null" } }`, and `anyOf` object or array from older
61
+ // releases. Both are dropped so the root is a plain object schema.
61
62
  const objectRoot = (schema) => {
63
+ if ((schema.type === undefined || schema.type === "object") &&
64
+ isRecord(schema.not) &&
65
+ schema.not.type === "null" &&
66
+ Object.keys(schema.not).length === 1)
67
+ return { type: "object", ...Object.fromEntries(Object.entries(schema).filter(([key]) => key !== "not")) };
62
68
  if (schema.type !== undefined)
63
69
  return schema;
64
70
  if (Array.isArray(schema.anyOf) &&
@@ -93,8 +93,8 @@ const fromSettings = ({ apiKey, auth, baseURL, body, credentials, headers, profi
93
93
  baseURL,
94
94
  credentials,
95
95
  generation: topP === undefined ? undefined : { topP },
96
- headers: headers === undefined ? undefined : { ...headers },
97
- http: body === undefined ? undefined : { body: { ...body } },
96
+ headers,
97
+ http: body === undefined ? undefined : { body },
98
98
  profile,
99
99
  providerOptions,
100
100
  region,
@@ -42,8 +42,8 @@ export const model = (modelID, { apiKey, authToken, baseURL, body, headers, prov
42
42
  return configure({
43
43
  ...(authToken === undefined ? { apiKey: apiKey } : { auth: Auth.bearer(authToken) }),
44
44
  baseURL,
45
- headers: headers === undefined ? undefined : { ...headers },
46
- http: body === undefined ? undefined : { body: { ...body } },
45
+ headers,
46
+ http: body === undefined ? undefined : { body },
47
47
  provider,
48
48
  providerOptions,
49
49
  }).model(modelID);
@@ -35,8 +35,8 @@ export const model = (modelID, { apiKey, authToken, baseURL, body, headers, ...p
35
35
  return configure({
36
36
  ...(authToken === undefined ? { apiKey: apiKey } : { auth: Auth.bearer(authToken) }),
37
37
  baseURL,
38
- headers: headers === undefined ? undefined : { ...headers },
39
- http: body === undefined ? undefined : { body: { ...body } },
38
+ headers,
39
+ http: body === undefined ? undefined : { body },
40
40
  providerOptions,
41
41
  }).model(modelID);
42
42
  };
@@ -166,7 +166,7 @@ declare const responsesRoute: Route<{
166
166
  } | {
167
167
  readonly type: "image_generation";
168
168
  readonly size?: string | undefined;
169
- readonly action?: "generate" | "auto" | "edit" | undefined;
169
+ readonly action?: "auto" | "generate" | "edit" | undefined;
170
170
  readonly output_format?: "png" | "jpeg" | "webp" | undefined;
171
171
  readonly quality?: "auto" | "low" | "medium" | "high" | undefined;
172
172
  readonly background?: "auto" | "opaque" | "transparent" | undefined;
@@ -493,7 +493,7 @@ export declare const routes: (Route<{
493
493
  } | {
494
494
  readonly type: "image_generation";
495
495
  readonly size?: string | undefined;
496
- readonly action?: "generate" | "auto" | "edit" | undefined;
496
+ readonly action?: "auto" | "generate" | "edit" | undefined;
497
497
  readonly output_format?: "png" | "jpeg" | "webp" | undefined;
498
498
  readonly quality?: "auto" | "low" | "medium" | "high" | undefined;
499
499
  readonly background?: "auto" | "opaque" | "transparent" | undefined;
@@ -91,10 +91,10 @@ const config = ({ apiKey, apiVersion, baseURL, body, headers, queryParams, resou
91
91
  const common = {
92
92
  apiKey,
93
93
  apiVersion,
94
- headers: headers === undefined ? undefined : { ...headers },
95
- http: body === undefined ? undefined : { body: { ...body } },
94
+ headers,
95
+ http: body === undefined ? undefined : { body },
96
96
  providerOptions,
97
- queryParams: queryParams === undefined ? undefined : { ...queryParams },
97
+ queryParams,
98
98
  useDeploymentBasedUrls,
99
99
  };
100
100
  if (baseURL !== undefined)
@@ -31,8 +31,8 @@ export const provider = configure();
31
31
  export const model = (modelID, { apiKey, baseURL, body, headers, ...providerOptions }) => configure({
32
32
  apiKey,
33
33
  baseURL,
34
- headers: headers === undefined ? undefined : { ...headers },
35
- http: body === undefined ? undefined : { body: { ...body } },
34
+ headers,
35
+ http: body === undefined ? undefined : { body },
36
36
  providerOptions,
37
37
  }).model(modelID);
38
38
  export * as Baseten from "./baseten.js";
@@ -39,7 +39,7 @@ export const provider = configure();
39
39
  export const model = (modelID, { apiKey, baseURL, body, headers, ...providerOptions }) => configure({
40
40
  apiKey,
41
41
  baseURL,
42
- headers: headers === undefined ? undefined : { ...headers },
43
- http: body === undefined ? undefined : { body: { ...body } },
42
+ headers,
43
+ http: body === undefined ? undefined : { body },
44
44
  providerOptions,
45
45
  }).model(modelID);
@@ -172,7 +172,7 @@ export declare const responsesRoute: import("../route/client.js").Route<{
172
172
  } | {
173
173
  readonly type: "image_generation";
174
174
  readonly size?: string | undefined;
175
- readonly action?: "generate" | "auto" | "edit" | undefined;
175
+ readonly action?: "auto" | "generate" | "edit" | undefined;
176
176
  readonly output_format?: "png" | "jpeg" | "webp" | undefined;
177
177
  readonly quality?: "auto" | "low" | "medium" | "high" | undefined;
178
178
  readonly background?: "auto" | "opaque" | "transparent" | undefined;
@@ -1167,7 +1167,7 @@ export declare const routes: (import("../route/client.js").Route<{
1167
1167
  } | {
1168
1168
  readonly type: "image_generation";
1169
1169
  readonly size?: string | undefined;
1170
- readonly action?: "generate" | "auto" | "edit" | undefined;
1170
+ readonly action?: "auto" | "generate" | "edit" | undefined;
1171
1171
  readonly output_format?: "png" | "jpeg" | "webp" | undefined;
1172
1172
  readonly quality?: "auto" | "low" | "medium" | "high" | undefined;
1173
1173
  readonly background?: "auto" | "opaque" | "transparent" | undefined;
@@ -100,8 +100,8 @@ export const model = (modelID, { accountId, apiKey, baseURL: configuredBaseURL,
100
100
  collectLog,
101
101
  gatewayApiKey,
102
102
  gatewayId,
103
- headers: headers === undefined ? undefined : { ...headers },
104
- http: body === undefined ? undefined : { body: { ...body } },
103
+ headers,
104
+ http: body === undefined ? undefined : { body },
105
105
  metadata,
106
106
  providerOptions,
107
107
  skipCache,
@@ -43,8 +43,8 @@ export const model = (modelID, settings) => {
43
43
  return configure({
44
44
  apiKey,
45
45
  baseURL: baseURL(settings),
46
- headers: headers === undefined ? undefined : { ...headers },
47
- http: body === undefined ? undefined : { body: { ...body } },
46
+ headers,
47
+ http: body === undefined ? undefined : { body },
48
48
  providerOptions,
49
49
  }).model(modelID);
50
50
  };
@@ -3,6 +3,6 @@ export const model = (modelID, { apiKey, baseURL, body, headers, ...providerOpti
3
3
  apiKey,
4
4
  baseURL,
5
5
  headers,
6
- http: body === undefined ? undefined : { body: { ...body } },
6
+ http: body === undefined ? undefined : { body },
7
7
  providerOptions,
8
8
  }).chat(modelID);
@@ -42,7 +42,7 @@ export const model = (modelID, { apiKey, baseURL, body, headers, ...providerOpti
42
42
  apiKey,
43
43
  baseURL,
44
44
  headers,
45
- http: body === undefined ? undefined : { body: { ...body } },
45
+ http: body === undefined ? undefined : { body },
46
46
  providerOptions,
47
47
  }).model(modelID);
48
48
  export * as Cohere from "./cohere.js";
@@ -42,7 +42,7 @@ export const provider = configure();
42
42
  export const model = (modelID, { apiKey, baseURL, body, headers, ...providerOptions }) => configure({
43
43
  apiKey,
44
44
  baseURL,
45
- headers: headers === undefined ? undefined : { ...headers },
46
- http: body === undefined ? undefined : { body: { ...body } },
45
+ headers,
46
+ http: body === undefined ? undefined : { body },
47
47
  providerOptions,
48
48
  }).model(modelID);
@@ -34,8 +34,8 @@ export const provider = configure();
34
34
  export const model = (modelID, { apiKey, baseURL, body, headers, ...providerOptions }) => configure({
35
35
  apiKey,
36
36
  baseURL,
37
- headers: headers === undefined ? undefined : { ...headers },
38
- http: body === undefined ? undefined : { body: { ...body } },
37
+ headers,
38
+ http: body === undefined ? undefined : { body },
39
39
  providerOptions,
40
40
  }).model(modelID);
41
41
  export * as DeepSeek from "./deepseek.js";
@@ -31,8 +31,8 @@ export const provider = configure();
31
31
  export const model = (modelID, { apiKey, baseURL, body, headers, ...providerOptions }) => configure({
32
32
  apiKey,
33
33
  baseURL,
34
- headers: headers === undefined ? undefined : { ...headers },
35
- http: body === undefined ? undefined : { body: { ...body } },
34
+ headers,
35
+ http: body === undefined ? undefined : { body },
36
36
  providerOptions,
37
37
  }).model(modelID);
38
38
  export * as Fireworks from "./fireworks.js";
@@ -2,7 +2,7 @@ import { configure } from "../google.js";
2
2
  export const model = (modelID, { apiKey, baseURL, body, headers, ...providerOptions }) => configure({
3
3
  apiKey,
4
4
  baseURL,
5
- headers: headers === undefined ? undefined : { ...headers },
6
- http: body === undefined ? undefined : { body: { ...body } },
5
+ headers,
6
+ http: body === undefined ? undefined : { body },
7
7
  providerOptions,
8
8
  }).interactions(modelID);
@@ -0,0 +1,2 @@
1
+ export { model } from "../google-vertex-interactions.js";
2
+ export type { Settings } from "../google-vertex-interactions.js";
@@ -0,0 +1 @@
1
+ export { model } from "../google-vertex-interactions.js";
@@ -0,0 +1,2 @@
1
+ export { model } from "../google-vertex-mistral.js";
2
+ export type { Settings } from "../google-vertex-mistral.js";
@@ -0,0 +1 @@
1
+ export { model } from "../google-vertex-mistral.js";
@@ -17,7 +17,7 @@ const configuredRoute = (input) => {
17
17
  if ("apiKey" in input && input.apiKey !== undefined)
18
18
  throw new ProviderConfigurationError({ provider: id, message: "Google Vertex Chat does not support API keys" });
19
19
  const { accessToken: _accessToken, auth: _auth, baseURL, location: inputLocation, project: inputProject, ...rest } = input;
20
- const location = GoogleVertexShared.location(inputLocation, "global");
20
+ const location = GoogleVertexShared.location(inputLocation);
21
21
  const project = GoogleVertexShared.project(inputProject);
22
22
  return route.with({
23
23
  ...rest,
@@ -46,8 +46,8 @@ export const model = (modelID, { accessToken, apiKey, baseURL, body, headers, lo
46
46
  return configure({
47
47
  accessToken,
48
48
  baseURL,
49
- headers: headers === undefined ? undefined : { ...headers },
50
- http: body === undefined ? undefined : { body: { ...body } },
49
+ headers,
50
+ http: body === undefined ? undefined : { body },
51
51
  location,
52
52
  project,
53
53
  providerOptions,