@opencode-ai/ai 0.0.0-dev-17708 → 0.0.0-dev-17714

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -309,7 +309,7 @@ Included providers: OpenAI, Anthropic, Google (Gemini), Google Vertex Gemini and
309
309
 
310
310
  ### Package-like entrypoints
311
311
 
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`, `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.
313
313
 
314
314
  ```ts
315
315
  import { model } from "@opencode-ai/ai/providers/openai/responses"
@@ -317,7 +317,6 @@ import { model } from "@opencode-ai/ai/providers/openai/responses"
317
317
  const selected = model("gpt-5", {
318
318
  apiKey: process.env.OPENAI_API_KEY,
319
319
  headers: { "x-application": "opencode" },
320
- limits: { context: 200_000, output: 64_000 },
321
320
  })
322
321
  ```
323
322
 
@@ -5,6 +5,7 @@ import { Lifecycle } from "./utils/lifecycle.js";
5
5
  import { ToolStream } from "./utils/tool-stream.js";
6
6
  export declare const DEFAULT_BASE_URL = "https://api.anthropic.com/v1";
7
7
  export declare const PATH = "/messages";
8
+ export declare const DEFAULT_MAX_TOKENS = 32000;
8
9
  export type ThinkingInput = {
9
10
  readonly type: "adaptive";
10
11
  readonly display?: "summarized" | "omitted";
@@ -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
  // =============================================================================
@@ -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,
@@ -773,11 +773,17 @@ const onOutputItemDone = Effect.fn("OpenResponses.onOutputItemDone")(function* (
773
773
  }
774
774
  return [state, NO_EVENTS];
775
775
  });
776
- const onResponseFinish = (state, event) => {
777
- const events = [];
776
+ const onResponseFinish = Effect.fn("OpenResponses.onResponseFinish")(function* (state, event) {
777
+ // Some compatible providers omit output_item.done even after completing the response.
778
+ const pending = event.type === "response.completed"
779
+ ? yield* ToolStream.finishAll(state.id, state.tools)
780
+ : { tools: state.tools, events: NO_EVENTS };
781
+ const events = [...pending.events];
782
+ const hasFunctionCall = pending.events.some((event) => LLMEvent.is.toolCall(event) || LLMEvent.is.toolInputError(event)) ||
783
+ state.hasFunctionCall;
778
784
  const lifecycle = Lifecycle.finish(state.lifecycle, events, {
779
785
  reason: {
780
- normalized: mapFinishReason(event, state.hasFunctionCall),
786
+ normalized: mapFinishReason(event, hasFunctionCall),
781
787
  raw: event.response?.incomplete_details?.reason,
782
788
  },
783
789
  usage: mapUsage(event.response?.usage, state.providerMetadataKey),
@@ -788,8 +794,8 @@ const onResponseFinish = (state, event) => {
788
794
  })
789
795
  : undefined,
790
796
  });
791
- return [{ ...state, lifecycle }, events];
792
- };
797
+ return [{ ...state, lifecycle, hasFunctionCall, tools: pending.tools }, events];
798
+ });
793
799
  // Build a single human-readable message from whatever the provider supplied.
794
800
  // When both code and message are present, prefix the code so consumers see
795
801
  // the failure mode (e.g. `rate_limit_exceeded: Slow down`) instead of just
@@ -857,7 +863,7 @@ export const step = (state, event) => {
857
863
  return onOutputItemDone(state, event);
858
864
  }
859
865
  if (event.type === "response.completed" || event.type === "response.incomplete")
860
- return Effect.succeed(onResponseFinish(state, event));
866
+ return onResponseFinish(state, event);
861
867
  if (event.type === "response.failed")
862
868
  return providerError(state, event, `${state.name} response failed`);
863
869
  if (event.type === "error")
@@ -3,11 +3,6 @@ export interface Settings extends Readonly<Record<string, unknown>> {
3
3
  readonly baseURL?: string;
4
4
  readonly headers?: Readonly<Record<string, string>>;
5
5
  readonly body?: Readonly<Record<string, unknown>>;
6
- readonly limits?: {
7
- readonly context: number;
8
- readonly input?: number;
9
- readonly output: number;
10
- };
11
6
  }
12
7
  export interface Definition<ProviderSettings extends Settings = Settings, Options extends ProviderOptions = ProviderOptions> {
13
8
  readonly model: (modelID: string, settings: ProviderSettings) => LanguageModel<Options>;
@@ -58,7 +58,6 @@ const config = (settings) => {
58
58
  credentials: settings.credentials,
59
59
  headers: settings.headers === undefined ? undefined : { ...settings.headers },
60
60
  http: settings.body === undefined ? undefined : { body: { ...settings.body } },
61
- limits: settings.limits,
62
61
  providerOptions: settings.providerOptions,
63
62
  region: settings.region,
64
63
  };
@@ -35,7 +35,6 @@ export const model = (modelID, settings) => {
35
35
  generation: settings.topP === undefined ? undefined : { topP: settings.topP },
36
36
  headers: settings.headers === undefined ? undefined : { ...settings.headers },
37
37
  http: settings.body === undefined ? undefined : { body: { ...settings.body } },
38
- limits: settings.limits,
39
38
  region: settings.region,
40
39
  }).model(modelID);
41
40
  };
@@ -37,7 +37,6 @@ export const model = (modelID, settings) => {
37
37
  baseURL: settings.baseURL,
38
38
  headers: settings.headers === undefined ? undefined : { ...settings.headers },
39
39
  http: settings.body === undefined ? undefined : { body: { ...settings.body } },
40
- limits: settings.limits,
41
40
  provider: settings.provider,
42
41
  providerOptions: settings.providerOptions,
43
42
  }).model(modelID);
@@ -34,7 +34,6 @@ export const model = (modelID, settings) => {
34
34
  baseURL: settings.baseURL,
35
35
  headers: settings.headers === undefined ? undefined : { ...settings.headers },
36
36
  http: settings.body === undefined ? undefined : { body: { ...settings.body } },
37
- limits: settings.limits,
38
37
  providerOptions: settings.providerOptions,
39
38
  }).model(modelID);
40
39
  };
@@ -74,7 +74,6 @@ const config = (settings) => {
74
74
  apiVersion: settings.apiVersion,
75
75
  headers: settings.headers === undefined ? undefined : { ...settings.headers },
76
76
  http: settings.body === undefined ? undefined : { body: { ...settings.body } },
77
- limits: settings.limits,
78
77
  providerOptions: settings.providerOptions,
79
78
  queryParams: settings.queryParams === undefined ? undefined : { ...settings.queryParams },
80
79
  useDeploymentBasedUrls: settings.useDeploymentBasedUrls,
@@ -42,7 +42,6 @@ export const model = (modelID, settings) => {
42
42
  baseURL: settings.baseURL,
43
43
  headers: settings.headers === undefined ? undefined : { ...settings.headers },
44
44
  http: settings.body === undefined ? undefined : { body: { ...settings.body } },
45
- limits: settings.limits,
46
45
  location: settings.location,
47
46
  project: settings.project,
48
47
  providerOptions: settings.providerOptions,
@@ -67,7 +67,6 @@ export const model = (modelID, settings) => {
67
67
  baseURL: settings.baseURL,
68
68
  headers: settings.headers === undefined ? undefined : { ...settings.headers },
69
69
  http: settings.body === undefined ? undefined : { body: { ...settings.body } },
70
- limits: settings.limits,
71
70
  location: settings.location,
72
71
  project: settings.project,
73
72
  providerOptions: settings.providerOptions,
@@ -43,7 +43,6 @@ export const model = (modelID, settings) => {
43
43
  baseURL: settings.baseURL,
44
44
  headers: settings.headers === undefined ? undefined : { ...settings.headers },
45
45
  http: settings.body === undefined ? undefined : { body: { ...settings.body } },
46
- limits: settings.limits,
47
46
  location: settings.location,
48
47
  project: settings.project,
49
48
  providerOptions: settings.providerOptions,
@@ -73,7 +73,6 @@ export const model = (modelID, settings) => {
73
73
  baseURL: settings.baseURL,
74
74
  headers: settings.headers === undefined ? undefined : { ...settings.headers },
75
75
  http: settings.body === undefined ? undefined : { body: { ...settings.body } },
76
- limits: settings.limits,
77
76
  location: settings.location,
78
77
  project: settings.project,
79
78
  providerOptions: settings.providerOptions,
@@ -37,7 +37,6 @@ export const model = (modelID, settings) => configure({
37
37
  baseURL: settings.baseURL,
38
38
  headers: settings.headers === undefined ? undefined : { ...settings.headers },
39
39
  http: settings.body === undefined ? undefined : { body: { ...settings.body } },
40
- limits: settings.limits,
41
40
  providerOptions: settings.providerOptions,
42
41
  }).model(modelID);
43
42
  export const image = provider.image;
@@ -27,7 +27,6 @@ export const model = (modelID, settings) => configure({
27
27
  baseURL: settings.baseURL,
28
28
  headers: settings.headers === undefined ? undefined : { ...settings.headers },
29
29
  http: settings.body === undefined ? undefined : { body: { ...settings.body } },
30
- limits: settings.limits,
31
30
  provider: settings.provider,
32
31
  providerOptions: settings.providerOptions,
33
32
  }).model(modelID);
@@ -43,7 +43,6 @@ export const model = (modelID, settings) => configure({
43
43
  baseURL: settings.baseURL,
44
44
  headers: settings.headers === undefined ? undefined : { ...settings.headers },
45
45
  http: settings.body === undefined ? undefined : { body: { ...settings.body } },
46
- limits: settings.limits,
47
46
  provider: settings.provider,
48
47
  providerOptions: settings.providerOptions,
49
48
  }).model(modelID);
@@ -69,7 +69,6 @@ const config = (settings) => {
69
69
  baseURL: settings.baseURL,
70
70
  headers: Object.keys(headers).length === 0 ? undefined : headers,
71
71
  http: settings.body === undefined ? undefined : { body: { ...settings.body } },
72
- limits: settings.limits,
73
72
  providerOptions: settings.providerOptions,
74
73
  queryParams: settings.queryParams === undefined ? undefined : { ...settings.queryParams },
75
74
  };
@@ -112,6 +112,5 @@ export const model = (modelID, settings) => configure({
112
112
  baseURL: settings.baseURL,
113
113
  headers: settings.headers,
114
114
  http: settings.body === undefined ? undefined : { body: { ...settings.body } },
115
- limits: settings.limits,
116
115
  providerOptions: settings.providerOptions,
117
116
  }).model(modelID);
@@ -71,7 +71,6 @@ export const model = (modelID, settings) => configure({
71
71
  baseURL: settings.baseURL,
72
72
  headers: settings.headers,
73
73
  http: settings.body === undefined ? undefined : { body: { ...settings.body } },
74
- limits: settings.limits,
75
74
  providerOptions: settings.providerOptions,
76
75
  }).model(modelID);
77
76
  export const responses = provider.responses;
@@ -7,7 +7,7 @@ import { HttpTransport } from "./transport/index.js";
7
7
  import type { HttpMiddleware, Transport, TransportRuntime, WebSocketChannelExecutor } from "./transport/index.js";
8
8
  import type { Protocol } from "./protocol.js";
9
9
  import type { ProtocolID, ProviderOptions } from "../schema/index.js";
10
- import { AIError, GenerationOptions, HttpOptions, LLMRequest, LLMResponse, LanguageModel, LanguageModelLimits, LLMEvent, ProviderID } from "../schema/index.js";
10
+ import { AIError, GenerationOptions, HttpOptions, LLMRequest, LLMResponse, LanguageModel, LLMEvent, ProviderID } from "../schema/index.js";
11
11
  export interface RouteBody<Body> {
12
12
  /** Schema for the validated provider-native body sent as the JSON request. */
13
13
  readonly schema: Schema.Codec<Body, unknown>;
@@ -36,14 +36,12 @@ export type RouteLanguageModelInput = Omit<LanguageModel.Input, "provider" | "ro
36
36
  export type RouteRoutedLanguageModelInput = Omit<LanguageModel.Input, "route">;
37
37
  export interface RouteDefaults {
38
38
  readonly headers?: Record<string, string>;
39
- readonly limits?: LanguageModelLimits;
40
39
  readonly generation?: GenerationOptions;
41
40
  readonly providerOptions?: ProviderOptions;
42
41
  readonly http?: HttpOptions;
43
42
  }
44
43
  export interface RouteDefaultsInput {
45
44
  readonly headers?: Record<string, string>;
46
- readonly limits?: LanguageModelLimits.Input;
47
45
  readonly generation?: GenerationOptions.Input;
48
46
  readonly providerOptions?: ProviderOptions;
49
47
  readonly http?: HttpOptions.Input;
@@ -6,7 +6,7 @@ import { Framing } from "./framing.js";
6
6
  import { HttpTransport } from "./transport/index.js";
7
7
  import { applyCachePolicy } from "../cache-policy.js";
8
8
  import * as ProviderShared from "../protocols/shared.js";
9
- import { AIError, GenerationOptions, HttpOptions, LLMRequest, LLMResponse, LanguageModel, LanguageModelLimits, LLMEvent, InvalidProviderOutputReason, ProviderID, mergeGenerationOptions, mergeHttpOptions, mergeProviderOptions, } from "../schema/index.js";
9
+ import { AIError, GenerationOptions, HttpOptions, LLMRequest, LLMResponse, LanguageModel, LLMEvent, InvalidProviderOutputReason, ProviderID, mergeGenerationOptions, mergeHttpOptions, mergeProviderOptions, } from "../schema/index.js";
10
10
  const makeRouteLanguageModel = (route, mapped) => {
11
11
  const provider = route.provider ?? ("provider" in mapped ? mapped.provider : undefined);
12
12
  if (!provider)
@@ -25,7 +25,6 @@ const mergeRouteDefaults = (base, patch) => {
25
25
  ...base,
26
26
  ...patch,
27
27
  headers,
28
- limits: patch.limits === undefined ? base?.limits : LanguageModelLimits.make(patch.limits),
29
28
  generation: mergeGenerationOptions(generationOptions(base?.generation), generationOptions(patch.generation)),
30
29
  providerOptions: mergeProviderOptions(base?.providerOptions, patch.providerOptions),
31
30
  http: mergeHttpOptions(base?.http, httpOptions(patch.http), headers === undefined ? undefined : new HttpOptions({ headers })),
@@ -49,20 +49,7 @@ export type GenerationOptionsFields = {
49
49
  };
50
50
  export type GenerationOptionsInput = GenerationOptions | GenerationOptionsFields;
51
51
  export declare const mergeGenerationOptions: (...items: ReadonlyArray<GenerationOptionsInput | undefined>) => GenerationOptions | undefined;
52
- declare const LanguageModelLimits_base: Schema.Class<LanguageModelLimits, Schema.Struct<{
53
- readonly context: Schema.optional<Schema.Number>;
54
- readonly input: Schema.optional<Schema.Number>;
55
- readonly output: Schema.optional<Schema.Number>;
56
- }>, {}>;
57
- export declare class LanguageModelLimits extends LanguageModelLimits_base {
58
- }
59
- export declare namespace LanguageModelLimits {
60
- type Input = LanguageModelLimits | ConstructorParameters<typeof LanguageModelLimits>[0];
61
- /** Normalize model limit input into the canonical `LanguageModelLimits` class. */
62
- const make: (input: Input | undefined) => LanguageModelLimits;
63
- }
64
52
  declare const LanguageModelDefaults_base: Schema.Class<LanguageModelDefaults, Schema.Struct<{
65
- readonly limits: Schema.optional<typeof LanguageModelLimits>;
66
53
  readonly generation: Schema.optional<typeof GenerationOptions>;
67
54
  readonly providerOptions: Schema.optional<Schema.$Record<Schema.String, Schema.Unknown>>;
68
55
  readonly http: Schema.optional<typeof HttpOptions>;
@@ -71,7 +58,6 @@ export declare class LanguageModelDefaults extends LanguageModelDefaults_base {
71
58
  }
72
59
  export declare namespace LanguageModelDefaults {
73
60
  type Input = LanguageModelDefaults | {
74
- readonly limits?: LanguageModelLimits.Input;
75
61
  readonly generation?: GenerationOptions.Input;
76
62
  readonly providerOptions?: ProviderOptions;
77
63
  readonly http?: HttpOptions.Input;
@@ -76,18 +76,7 @@ export const mergeGenerationOptions = (...items) => {
76
76
  });
77
77
  return Object.values(result).some((value) => value !== undefined) ? result : undefined;
78
78
  };
79
- export class LanguageModelLimits extends Schema.Class("LLM.LanguageModelLimits")({
80
- context: Schema.optional(Schema.Number),
81
- input: Schema.optional(Schema.Number),
82
- output: Schema.optional(Schema.Number),
83
- }) {
84
- }
85
- (function (LanguageModelLimits) {
86
- /** Normalize model limit input into the canonical `LanguageModelLimits` class. */
87
- LanguageModelLimits.make = (input) => input instanceof LanguageModelLimits ? input : new LanguageModelLimits(input ?? {});
88
- })(LanguageModelLimits || (LanguageModelLimits = {}));
89
79
  export class LanguageModelDefaults extends Schema.Class("LLM.LanguageModelDefaults")({
90
- limits: Schema.optional(LanguageModelLimits),
91
80
  generation: Schema.optional(GenerationOptions),
92
81
  providerOptions: Schema.optional(ProviderOptions),
93
82
  http: Schema.optional(HttpOptions),
@@ -99,7 +88,6 @@ export class LanguageModelDefaults extends Schema.Class("LLM.LanguageModelDefaul
99
88
  if (input instanceof LanguageModelDefaults)
100
89
  return input;
101
90
  return new LanguageModelDefaults({
102
- limits: input.limits === undefined ? undefined : LanguageModelLimits.make(input.limits),
103
91
  generation: input.generation === undefined ? undefined : GenerationOptions.make(input.generation),
104
92
  providerOptions: input.providerOptions,
105
93
  http: input.http === undefined ? undefined : HttpOptions.make(input.http),
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-17708",
3
+ "version": "0.0.0-dev-17714",
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.110",
33
- "@opencode-ai/http-recorder": "0.0.0-dev-17708",
33
+ "@opencode-ai/http-recorder": "0.0.0-dev-17714",
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-17708",
42
+ "@opencode-ai/schema": "0.0.0-dev-17714",
43
43
  "aws4fetch": "1.0.20",
44
44
  "effect": "4.0.0-rc.110",
45
45
  "google-auth-library": "10.5.0"