@opencode-ai/ai 0.0.0-beta-17639 → 0.0.0-beta-17728

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 (72) hide show
  1. package/README.md +17 -6
  2. package/dist/cache-policy.js +3 -5
  3. package/dist/protocols/anthropic-messages.d.ts +26 -28
  4. package/dist/protocols/anthropic-messages.js +5 -5
  5. package/dist/protocols/gemini.d.ts +5 -9
  6. package/dist/protocols/gemini.js +5 -4
  7. package/dist/protocols/open-responses.d.ts +176 -38
  8. package/dist/protocols/open-responses.js +108 -33
  9. package/dist/protocols/openai-chat.d.ts +17 -15
  10. package/dist/protocols/openai-chat.js +8 -2
  11. package/dist/protocols/openai-compatible-chat.d.ts +3 -3
  12. package/dist/protocols/openai-compatible-responses.d.ts +27 -5
  13. package/dist/protocols/openai-responses.d.ts +151 -33
  14. package/dist/protocols/openai-responses.js +4 -2
  15. package/dist/protocols/shared.d.ts +8 -14
  16. package/dist/protocols/shared.js +8 -14
  17. package/dist/protocols/utils/bedrock-cache.js +3 -4
  18. package/dist/protocols/utils/cache.js +3 -6
  19. package/dist/protocols/utils/open-responses-options.d.ts +46 -15
  20. package/dist/protocols/utils/open-responses-options.js +38 -25
  21. package/dist/protocols/utils/openai-options.d.ts +7 -5
  22. package/dist/protocols/utils/openai-options.js +3 -3
  23. package/dist/provider-package.d.ts +0 -5
  24. package/dist/providers/amazon-bedrock-mantle.d.ts +43 -19
  25. package/dist/providers/amazon-bedrock-mantle.js +0 -1
  26. package/dist/providers/amazon-bedrock.d.ts +3 -9
  27. package/dist/providers/amazon-bedrock.js +0 -1
  28. package/dist/providers/anthropic-compatible.d.ts +11 -11
  29. package/dist/providers/anthropic-compatible.js +0 -1
  30. package/dist/providers/anthropic.d.ts +12 -12
  31. package/dist/providers/anthropic.js +0 -1
  32. package/dist/providers/azure.d.ts +40 -16
  33. package/dist/providers/azure.js +0 -1
  34. package/dist/providers/cloudflare.d.ts +11 -11
  35. package/dist/providers/google-vertex-chat.d.ts +5 -5
  36. package/dist/providers/google-vertex-chat.js +0 -1
  37. package/dist/providers/google-vertex-messages.d.ts +11 -11
  38. package/dist/providers/google-vertex-messages.js +0 -1
  39. package/dist/providers/google-vertex-responses.d.ts +29 -7
  40. package/dist/providers/google-vertex-responses.js +1 -2
  41. package/dist/providers/google-vertex.d.ts +5 -7
  42. package/dist/providers/google-vertex.js +1 -2
  43. package/dist/providers/google.d.ts +4 -4
  44. package/dist/providers/google.js +0 -1
  45. package/dist/providers/open-responses-options.d.ts +3 -14
  46. package/dist/providers/openai-compatible-responses.d.ts +29 -7
  47. package/dist/providers/openai-compatible-responses.js +0 -1
  48. package/dist/providers/openai-compatible.d.ts +13 -12
  49. package/dist/providers/openai-compatible.js +1 -1
  50. package/dist/providers/openai-options.d.ts +2 -4
  51. package/dist/providers/openai-options.js +3 -3
  52. package/dist/providers/openai.d.ts +44 -20
  53. package/dist/providers/openai.js +0 -1
  54. package/dist/providers/openrouter.d.ts +16 -17
  55. package/dist/providers/openrouter.js +1 -2
  56. package/dist/providers/xai.d.ts +46 -24
  57. package/dist/providers/xai.js +1 -2
  58. package/dist/route/client.d.ts +2 -6
  59. package/dist/route/client.js +1 -2
  60. package/dist/route/framing.d.ts +2 -2
  61. package/dist/route/protocol.d.ts +1 -2
  62. package/dist/route/protocol.js +1 -2
  63. package/dist/schema/errors.js +2 -1
  64. package/dist/schema/events.d.ts +5 -3
  65. package/dist/schema/events.js +5 -2
  66. package/dist/schema/ids.d.ts +0 -13
  67. package/dist/schema/ids.js +0 -9
  68. package/dist/schema/messages.d.ts +7 -7
  69. package/dist/schema/messages.js +5 -2
  70. package/dist/schema/options.d.ts +6 -22
  71. package/dist/schema/options.js +6 -30
  72. package/package.json +5 -5
package/README.md CHANGED
@@ -237,11 +237,11 @@ Prompt caching is **on by default**. Every `LLMRequest` resolves to `cache: "aut
237
237
 
238
238
  ### Auto placement
239
239
 
240
- `"auto"` places up to four breakpoints — the last tool definition, the first system part, the last system part when distinct, and the final message boundary. These expose successively larger reusable prefixes for tools, the base agent, project instructions, and the active conversation. The rolling final-message boundary is the load-bearing detail in tool loops: it advances on every request so the previous cache entry stays within Anthropic's 20-block lookback.
240
+ `"auto"` places up to four breakpoints — the last tool definition, the first system part, the last system part when distinct, and the final message boundary. These expose successively larger reusable prefixes for tools, the base agent, project instructions, and the active conversation. The rolling final-message boundary advances on every request so recent conversation prefixes remain reusable during tool loops.
241
241
 
242
242
  Tools precede every system and conversation block in the provider prefix, so tool definitions must remain byte-stable and deterministically ordered for downstream breakpoints to remain reusable.
243
243
 
244
- The math justifies the default: Anthropic's 5-minute cache write is 1.25× base, read is 0.1×, so a single reuse within 5 minutes already wins. One-shot completions below the per-model minimum-cacheable-token threshold silently no-op on the wire, so the worst case is harmless.
244
+ Requests below a provider's minimum cacheable size simply do not produce a reusable cache entry.
245
245
 
246
246
  ### Opting out
247
247
 
@@ -285,6 +285,7 @@ LLM.request({
285
285
  | ----------------------- | ------------------------------------------------------------------------- |
286
286
  | Anthropic Messages | emits up to 4 `cache_control` markers (4-breakpoint cap enforced) |
287
287
  | Bedrock Converse | emits up to 4 `cachePoint` blocks (4-breakpoint cap enforced) |
288
+ | OpenRouter | emits up to 4 `cache_control` markers |
288
289
  | OpenAI Chat / Responses | no-op (implicit caching above 1024 tokens) |
289
290
  | Gemini | no-op (implicit caching on 2.5+; explicit `CachedContent` is out-of-band) |
290
291
 
@@ -308,7 +309,7 @@ Included providers: OpenAI, Anthropic, Google (Gemini), Google Vertex Gemini and
308
309
 
309
310
  ### Package-like entrypoints
310
311
 
311
- Native catalog integrations load provider behavior through package-like entrypoints. These are export paths from the same `@opencode-ai/ai` npm package, not independently published packages. Each entrypoint exports the same `model(modelID, settings)` contract, and `settings` contains serializable provider configuration plus common `headers`, `body`, and `limits` overlays.
312
+ Native catalog integrations load provider behavior through package-like entrypoints. These are export paths from the same `@opencode-ai/ai` npm package, not independently published packages. Each entrypoint exports the same `model(modelID, settings)` contract, and `settings` contains serializable provider configuration plus common `headers` and `body` overlays.
312
313
 
313
314
  ```ts
314
315
  import { model } from "@opencode-ai/ai/providers/openai/responses"
@@ -316,7 +317,6 @@ import { model } from "@opencode-ai/ai/providers/openai/responses"
316
317
  const selected = model("gpt-5", {
317
318
  apiKey: process.env.OPENAI_API_KEY,
318
319
  headers: { "x-application": "opencode" },
319
- limits: { context: 200_000, output: 64_000 },
320
320
  })
321
321
  ```
322
322
 
@@ -371,11 +371,23 @@ Request options in order of stability:
371
371
 
372
372
  1. **`generation`** — portable knobs (`maxTokens`, `temperature`, `topP`, `topK`, penalties, seed, stop).
373
373
  2. **`promptCacheKey`** — stable cache affinity lowered by every protocol that supports it.
374
- 3. **`providerOptions: { <provider>: {...} }`** — typed-at-the-facade provider-specific knobs (OpenAI `store`, Anthropic `thinking`, Gemini `thinkingConfig`, OpenRouter routing).
374
+ 3. **`providerOptions: { ... }`** — flat options inferred from the selected model (OpenAI `store`, Anthropic `thinking`, Gemini `thinkingConfig`, OpenRouter routing).
375
375
  4. **`http: { body, headers, query }`** — last-resort serializable overlays merged into the final HTTP request. Reach for this only when a stable typed path doesn't yet exist.
376
376
 
377
377
  Route/provider defaults are overridden by request-level values for each axis.
378
378
 
379
+ The selected model supplies the provider-specific option type, so per-request overrides stay flat while the canonical runtime request remains provider-neutral:
380
+
381
+ ```ts
382
+ LLM.request({
383
+ model,
384
+ prompt,
385
+ providerOptions: {
386
+ reasoningEffort: "low",
387
+ },
388
+ })
389
+ ```
390
+
379
391
  ## Routes
380
392
 
381
393
  Adding a new model or deployment is usually 5-15 lines using `Route.make({ protocol, endpoint, auth, framing, ... })`. The route owns endpoint/auth/framing and the protocol owns body construction plus stream parsing. Transports are reusable IO templates that receive route endpoint/auth at compile time. Capability/catalog metadata lives outside this low-level package; unsupported request shapes fail during protocol lowering. See `AGENTS.md` for the architectural detail.
@@ -387,6 +399,5 @@ This package is built on Effect. Public methods return `Effect` or `Stream`; pro
387
399
  ## See also
388
400
 
389
401
  - `AGENTS.md` — architecture, route construction, contributor guide
390
- - `STATUS.md` — native provider parity status and AI SDK migration gaps
391
402
  - `example/tutorial.ts` — runnable end-to-end walkthrough
392
403
  - `test/provider/*.test.ts` — fixture-first protocol tests; `*.recorded.test.ts` files cover live cassettes
@@ -5,8 +5,8 @@
5
5
  // The default `"auto"` shape places breakpoints at the last tool definition,
6
6
  // the first and last distinct system parts, and the conversation tail. This
7
7
  // exposes reusable tool, base-agent, project, and session prefixes while
8
- // advancing the tail after each tool result keeps the previous cache entry
9
- // within Anthropic's 20-block lookback during long agent turns.
8
+ // advancing the tail after each tool result keeps recent conversation prefixes
9
+ // reusable during long agent runs.
10
10
  //
11
11
  // Manual `cache: CacheHint` placements on individual parts are preserved and
12
12
  // count against the four-breakpoint budget; auto only fills remaining slots.
@@ -20,9 +20,7 @@ const AUTO = {
20
20
  const NONE = {};
21
21
  const BREAKPOINT_CAP = 4;
22
22
  // Resolution rules:
23
- // - undefined → "auto" — caching is on by default. The math favors it:
24
- // Anthropic 5m-cache write is 1.25x base, read is 0.1x,
25
- // so a single reuse within 5 minutes already wins.
23
+ // - undefined → "auto" — caching is on by default.
26
24
  // - "auto" → tools + first/last system + final message boundary.
27
25
  // - "none" → no auto placement; manual `CacheHint`s still flow.
28
26
  // - object form → exactly what the caller asked for.
@@ -1,11 +1,11 @@
1
1
  import { Schema } from "effect";
2
2
  import { Route } from "../route/client.js";
3
3
  import { Protocol } from "../route/protocol.js";
4
- import { type ProviderOptions } from "../schema/index.js";
5
4
  import { Lifecycle } from "./utils/lifecycle.js";
6
5
  import { ToolStream } from "./utils/tool-stream.js";
7
6
  export declare const DEFAULT_BASE_URL = "https://api.anthropic.com/v1";
8
7
  export declare const PATH = "/messages";
8
+ export declare const DEFAULT_MAX_TOKENS = 32000;
9
9
  export type ThinkingInput = {
10
10
  readonly type: "adaptive";
11
11
  readonly display?: "summarized" | "omitted";
@@ -25,9 +25,7 @@ export interface OptionsInput {
25
25
  readonly thinking?: ThinkingInput;
26
26
  readonly effort?: string;
27
27
  }
28
- export type ProviderOptionsInput = ProviderOptions & {
29
- readonly anthropic?: OptionsInput;
30
- };
28
+ export type ProviderOptionsInput = OptionsInput;
31
29
  export declare const AnthropicMessagesBody: Schema.Struct<{
32
30
  model: Schema.String;
33
31
  system: Schema.optional<Schema.$Array<Schema.Struct<{
@@ -207,8 +205,8 @@ export declare const AnthropicMessagesBody: Schema.Struct<{
207
205
  export type AnthropicMessagesBody = Schema.Schema.Type<typeof AnthropicMessagesBody>;
208
206
  /**
209
207
  * The Anthropic Messages protocol — request body construction, body schema,
210
- * and the streaming-event state machine. Used by native Anthropic Cloud and
211
- * (once registered) Vertex Anthropic / Bedrock-hosted Anthropic passthrough.
208
+ * and the streaming-event state machine shared by Anthropic-compatible and
209
+ * Vertex-hosted Messages routes.
212
210
  */
213
211
  export declare const protocol: Protocol<{
214
212
  readonly max_tokens: number;
@@ -346,14 +344,6 @@ export declare const protocol: Protocol<{
346
344
  }[];
347
345
  })[];
348
346
  readonly stream: true;
349
- readonly system?: readonly {
350
- readonly type: "text";
351
- readonly text: string;
352
- readonly cache_control?: {
353
- readonly type: "ephemeral";
354
- readonly ttl?: "1h" | "5m" | undefined;
355
- } | undefined;
356
- }[] | undefined;
357
347
  readonly tools?: readonly {
358
348
  readonly description: string;
359
349
  readonly name: string;
@@ -365,6 +355,14 @@ export declare const protocol: Protocol<{
365
355
  readonly ttl?: "1h" | "5m" | undefined;
366
356
  } | undefined;
367
357
  }[] | undefined;
358
+ readonly system?: readonly {
359
+ readonly type: "text";
360
+ readonly text: string;
361
+ readonly cache_control?: {
362
+ readonly type: "ephemeral";
363
+ readonly ttl?: "1h" | "5m" | undefined;
364
+ } | undefined;
365
+ }[] | undefined;
368
366
  readonly temperature?: number | undefined;
369
367
  readonly thinking?: {
370
368
  readonly type: "enabled";
@@ -376,7 +374,7 @@ export declare const protocol: Protocol<{
376
374
  readonly type: "disabled";
377
375
  } | undefined;
378
376
  readonly tool_choice?: {
379
- readonly type: "none" | "auto" | "any";
377
+ readonly type: "auto" | "none" | "any";
380
378
  } | {
381
379
  readonly type: "tool";
382
380
  readonly name: string;
@@ -389,10 +387,6 @@ export declare const protocol: Protocol<{
389
387
  } | undefined;
390
388
  }, string, {
391
389
  readonly type: string;
392
- readonly error?: {
393
- readonly type?: string | undefined;
394
- readonly message?: string | undefined;
395
- } | undefined;
396
390
  readonly message?: {
397
391
  readonly usage?: {
398
392
  readonly [x: string]: unknown;
@@ -410,6 +404,10 @@ export declare const protocol: Protocol<{
410
404
  } | null | undefined;
411
405
  } | undefined;
412
406
  } | undefined;
407
+ readonly error?: {
408
+ readonly type?: string | undefined;
409
+ readonly message?: string | undefined;
410
+ } | undefined;
413
411
  readonly delta?: {
414
412
  readonly type?: string | undefined;
415
413
  readonly text?: string | undefined;
@@ -588,14 +586,6 @@ export declare const route: Route<{
588
586
  }[];
589
587
  })[];
590
588
  readonly stream: true;
591
- readonly system?: readonly {
592
- readonly type: "text";
593
- readonly text: string;
594
- readonly cache_control?: {
595
- readonly type: "ephemeral";
596
- readonly ttl?: "1h" | "5m" | undefined;
597
- } | undefined;
598
- }[] | undefined;
599
589
  readonly tools?: readonly {
600
590
  readonly description: string;
601
591
  readonly name: string;
@@ -607,6 +597,14 @@ export declare const route: Route<{
607
597
  readonly ttl?: "1h" | "5m" | undefined;
608
598
  } | undefined;
609
599
  }[] | undefined;
600
+ readonly system?: readonly {
601
+ readonly type: "text";
602
+ readonly text: string;
603
+ readonly cache_control?: {
604
+ readonly type: "ephemeral";
605
+ readonly ttl?: "1h" | "5m" | undefined;
606
+ } | undefined;
607
+ }[] | undefined;
610
608
  readonly temperature?: number | undefined;
611
609
  readonly thinking?: {
612
610
  readonly type: "enabled";
@@ -618,7 +616,7 @@ export declare const route: Route<{
618
616
  readonly type: "disabled";
619
617
  } | undefined;
620
618
  readonly tool_choice?: {
621
- readonly type: "none" | "auto" | "any";
619
+ readonly type: "auto" | "none" | "any";
622
620
  } | {
623
621
  readonly type: "tool";
624
622
  readonly name: string;
@@ -15,6 +15,7 @@ import { ToolStream } from "./utils/tool-stream.js";
15
15
  const ADAPTER = "anthropic-messages";
16
16
  export const DEFAULT_BASE_URL = "https://api.anthropic.com/v1";
17
17
  export const PATH = "/messages";
18
+ export const DEFAULT_MAX_TOKENS = 32_000;
18
19
  // =============================================================================
19
20
  // Request Body Schema
20
21
  // =============================================================================
@@ -468,7 +469,7 @@ const lowerMessages = Effect.fn("AnthropicMessages.lowerMessages")(function* (re
468
469
  return messages;
469
470
  });
470
471
  const resolveOptions = Effect.fn("AnthropicMessages.resolveOptions")(function* (request) {
471
- const input = request.providerOptions?.anthropic;
472
+ const input = request.providerOptions;
472
473
  return {
473
474
  thinking: yield* resolveThinking(input?.thinking),
474
475
  effort: typeof input?.effort === "string" ? input.effort : undefined,
@@ -501,7 +502,6 @@ const resolveThinking = Effect.fn("AnthropicMessages.resolveThinking")(function*
501
502
  const fromRequest = Effect.fn("AnthropicMessages.fromRequest")(function* (request) {
502
503
  const generation = request.generation;
503
504
  const toolSchemaCompatibility = request.model.compatibility?.toolSchema;
504
- const outputLimit = request.model.defaults?.limits?.output ?? request.model.route.defaults.limits?.output ?? 4096;
505
505
  // Allocate the 4-breakpoint budget in invalidation order: tools → system →
506
506
  // messages. Tools live highest in the cache hierarchy, so when callers
507
507
  // over-mark we keep their tool hints and shed the message-tail ones first.
@@ -530,7 +530,7 @@ const fromRequest = Effect.fn("AnthropicMessages.fromRequest")(function* (reques
530
530
  tools,
531
531
  tool_choice: toolChoice,
532
532
  stream: true,
533
- max_tokens: generation?.maxTokens ?? outputLimit,
533
+ max_tokens: generation?.maxTokens ?? DEFAULT_MAX_TOKENS,
534
534
  temperature: generation?.temperature,
535
535
  top_p: generation?.topP,
536
536
  top_k: generation?.topK,
@@ -844,8 +844,8 @@ const step = (state, event) => {
844
844
  // =============================================================================
845
845
  /**
846
846
  * The Anthropic Messages protocol — request body construction, body schema,
847
- * and the streaming-event state machine. Used by native Anthropic Cloud and
848
- * (once registered) Vertex Anthropic / Bedrock-hosted Anthropic passthrough.
847
+ * and the streaming-event state machine shared by Anthropic-compatible and
848
+ * Vertex-hosted Messages routes.
849
849
  */
850
850
  export const protocol = Protocol.make({
851
851
  id: ADAPTER,
@@ -1,7 +1,6 @@
1
1
  import { Schema } from "effect";
2
2
  import { Route } from "../route/client.js";
3
3
  import { Protocol } from "../route/protocol.js";
4
- import { type ProviderOptions } from "../schema/index.js";
5
4
  import { Lifecycle } from "./utils/lifecycle.js";
6
5
  export declare const DEFAULT_BASE_URL = "https://generativelanguage.googleapis.com/v1beta";
7
6
  export interface OptionsInput {
@@ -18,9 +17,7 @@ export interface OptionsInput {
18
17
  readonly thinkingLevel?: "minimal" | "low" | "medium" | "high" | (string & {});
19
18
  };
20
19
  }
21
- export type ProviderOptionsInput = ProviderOptions & {
22
- readonly gemini?: OptionsInput;
23
- };
20
+ export type ProviderOptionsInput = OptionsInput;
24
21
  declare const GeminiBody: Schema.Struct<{
25
22
  cachedContent: Schema.optional<Schema.String>;
26
23
  contents: Schema.$Array<Schema.Struct<{
@@ -98,12 +95,11 @@ declare const GeminiBody: Schema.Struct<{
98
95
  export type GeminiBody = Schema.Schema.Type<typeof GeminiBody>;
99
96
  /**
100
97
  * The Gemini protocol — request body construction, body schema, and the
101
- * streaming-event state machine. Used by Google AI Studio Gemini and (once
102
- * registered) Vertex Gemini.
98
+ * streaming-event state machine shared by Google AI Studio and Vertex Gemini.
103
99
  */
104
100
  export declare const protocol: Protocol<{
105
101
  readonly contents: readonly {
106
- readonly role: "user" | "model";
102
+ readonly role: "model" | "user";
107
103
  readonly parts: readonly ({
108
104
  readonly inlineData: {
109
105
  readonly mimeType: string;
@@ -181,7 +177,7 @@ export declare const protocol: Protocol<{
181
177
  }, string, {
182
178
  readonly candidates?: readonly {
183
179
  readonly content?: {
184
- readonly role: "user" | "model";
180
+ readonly role: "model" | "user";
185
181
  readonly parts: readonly ({
186
182
  readonly inlineData: {
187
183
  readonly mimeType: string;
@@ -234,7 +230,7 @@ export declare const protocol: Protocol<{
234
230
  }>;
235
231
  export declare const route: Route<{
236
232
  readonly contents: readonly {
237
- readonly role: "user" | "model";
233
+ readonly role: "model" | "user";
238
234
  readonly parts: readonly ({
239
235
  readonly inlineData: {
240
236
  readonly mimeType: string;
@@ -201,7 +201,9 @@ const lowerMessages = Effect.fn("Gemini.lowerMessages")(function* (request) {
201
201
  if (message.role === "system") {
202
202
  const part = yield* ProviderShared.wrappedSystemUpdate("Gemini", message);
203
203
  const previous = contents.at(-1);
204
- if (previous?.role === "user")
204
+ // Gemini rejects a continuation whose function-response turn carries extra
205
+ // parts, so an update after a tool result starts its own user turn.
206
+ if (previous?.role === "user" && !previous.parts.some((item) => "functionResponse" in item))
205
207
  contents[contents.length - 1] = { role: "user", parts: [...previous.parts, { text: part.text }] };
206
208
  else
207
209
  contents.push({ role: "user", parts: [{ text: part.text }] });
@@ -293,7 +295,7 @@ const lowerMessages = Effect.fn("Gemini.lowerMessages")(function* (request) {
293
295
  return contents;
294
296
  });
295
297
  const resolveOptions = (request) => {
296
- const input = request.providerOptions?.gemini;
298
+ const input = request.providerOptions;
297
299
  const value = input?.thinkingConfig;
298
300
  const thinkingConfig = {
299
301
  thinkingBudget: ProviderShared.isRecord(value) && typeof value.thinkingBudget === "number" ? value.thinkingBudget : undefined,
@@ -491,8 +493,7 @@ const step = (state, event) => {
491
493
  // =============================================================================
492
494
  /**
493
495
  * The Gemini protocol — request body construction, body schema, and the
494
- * streaming-event state machine. Used by Google AI Studio Gemini and (once
495
- * registered) Vertex Gemini.
496
+ * streaming-event state machine shared by Google AI Studio and Vertex Gemini.
496
497
  */
497
498
  export const protocol = Protocol.make({
498
499
  id: ADAPTER,