@opencode-ai/ai 0.0.0-dev-18554 → 0.0.0-dev-18558

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.
@@ -1,5 +1,6 @@
1
1
  import { Effect, Schema } from "effect";
2
2
  import { Route } from "../route/client.js";
3
+ import { Framing } from "../route/framing.js";
3
4
  import { HttpTransport } from "../route/transport/index.js";
4
5
  import { Protocol } from "../route/protocol.js";
5
6
  import { AIError, LLMEvent, Usage, type FinishReasonDetails, type CacheHint, type LLMRequest } from "../schema/index.js";
@@ -587,7 +588,7 @@ export declare const protocol: Protocol<{
587
588
  readonly tool_stream?: boolean | undefined;
588
589
  readonly frequency_penalty?: number | undefined;
589
590
  readonly presence_penalty?: number | undefined;
590
- }, string, {
591
+ }, string, "[DONE]" | {
591
592
  readonly [x: string]: unknown;
592
593
  readonly error?: {
593
594
  readonly [x: string]: unknown;
@@ -655,6 +656,7 @@ export declare const protocol: Protocol<{
655
656
  readonly native_finish_reason?: string | null | undefined;
656
657
  }[] | null | undefined;
657
658
  }, ParserState>;
659
+ export declare const framing: Framing.Definition<string>;
658
660
  export declare const httpTransport: HttpTransport.HttpJsonTransport<{
659
661
  readonly model: string;
660
662
  readonly messages: readonly ({
@@ -3,6 +3,7 @@ import { Tool } from "@opencode-ai/schema/tool";
3
3
  import { Route } from "../route/client.js";
4
4
  import { Auth } from "../route/auth.js";
5
5
  import { Endpoint } from "../route/endpoint.js";
6
+ import { Framing } from "../route/framing.js";
6
7
  import { HttpTransport } from "../route/transport/index.js";
7
8
  import { Protocol } from "../route/protocol.js";
8
9
  import { AIError, AIErrorReason, InvalidProviderOutputError, LLMEvent, ProviderInternalError, UnknownProviderError, Usage, } from "../schema/index.js";
@@ -176,6 +177,8 @@ export const OpenAIChatEvent = Schema.StructWithRest(Schema.Struct({
176
177
  usage: optionalNull(OpenAIChatUsage),
177
178
  error: optionalNull(OpenAIChatError),
178
179
  }), [Schema.Record(Schema.String, Schema.Unknown)]);
180
+ const DONE = "[DONE]";
181
+ const OpenAIChatStreamEvent = Schema.Union([Schema.Literal(DONE), Protocol.jsonEvent(OpenAIChatEvent)]);
179
182
  const lowerTool = (tool, inputSchema, options, supportsStrictMode) => ({
180
183
  type: "function",
181
184
  function: {
@@ -917,7 +920,7 @@ export const protocol = Protocol.make({
917
920
  from: fromRequest,
918
921
  },
919
922
  stream: {
920
- event: Protocol.jsonEvent(OpenAIChatEvent),
923
+ event: OpenAIChatStreamEvent,
921
924
  initial: (request) => ({
922
925
  providerMetadataKey: request.model.route.providerMetadataKey ?? String(request.model.provider),
923
926
  tools: ToolStream.empty(),
@@ -931,11 +934,13 @@ export const protocol = Protocol.make({
931
934
  nextToolIndex: 0,
932
935
  requireFinishReason: request.model.compatibility?.requireFinishReason ?? true,
933
936
  }),
934
- step,
937
+ step: (state, event) => (event === DONE ? Effect.succeed([state, []]) : step(state, event)),
938
+ terminal: (event) => event === DONE,
935
939
  onHalt: finishEvents,
936
940
  },
937
941
  });
938
- export const httpTransport = HttpTransport.sseJson.with();
942
+ export const framing = Framing.sseWithDone;
943
+ export const httpTransport = HttpTransport.sseJson.with().with({ framing });
939
944
  export const route = Route.make({
940
945
  id: ADAPTER,
941
946
  provider: "openai",
@@ -1,6 +1,5 @@
1
1
  import { Route } from "../route/client.js";
2
2
  import { Endpoint } from "../route/endpoint.js";
3
- import { Framing } from "../route/framing.js";
4
3
  import * as OpenAIChat from "./openai-chat.js";
5
4
  const ADAPTER = "openai-compatible-chat";
6
5
  /**
@@ -15,6 +14,6 @@ export const route = Route.make({
15
14
  providerMetadataKey: "openai",
16
15
  protocol: OpenAIChat.protocol,
17
16
  endpoint: Endpoint.path("/chat/completions"),
18
- framing: Framing.sse,
17
+ framing: OpenAIChat.framing,
19
18
  });
20
19
  export * as OpenAICompatibleChat from "./openai-compatible-chat.js";
@@ -117,13 +117,13 @@ export declare const toolResultText: (part: ToolResultPart) => string;
117
117
  export declare const errorText: (error: unknown) => string;
118
118
  /**
119
119
  * `framing` step for Server-Sent Events. Decodes UTF-8, runs the SSE channel
120
- * decoder, optionally filters named events, and drops empty / `[DONE]`
121
- * keep-alive events so the protocol event schema sees one JSON string per
122
- * element. Retry control events are ignored without interrupting the stream.
120
+ * decoder, optionally filters named events, and drops empty events. `[DONE]`
121
+ * is dropped by default or retained for protocols that use it as their stream
122
+ * boundary. Retry control events are ignored without interrupting the stream.
123
123
  * Decoder failures become provider output errors so the public error channel
124
124
  * stays `AIError`.
125
125
  */
126
- export declare const sseFraming: (bytes: Stream.Stream<Uint8Array, AIError>, events?: ReadonlySet<string>) => Stream.Stream<string, AIError>;
126
+ export declare const sseFraming: (bytes: Stream.Stream<Uint8Array, AIError>, events?: ReadonlySet<string>, includeDone?: boolean) => Stream.Stream<string, AIError>;
127
127
  /**
128
128
  * Canonical invalid-request constructor shared by protocol lowering.
129
129
  */
@@ -158,13 +158,13 @@ export const errorText = (error) => {
158
158
  };
159
159
  /**
160
160
  * `framing` step for Server-Sent Events. Decodes UTF-8, runs the SSE channel
161
- * decoder, optionally filters named events, and drops empty / `[DONE]`
162
- * keep-alive events so the protocol event schema sees one JSON string per
163
- * element. Retry control events are ignored without interrupting the stream.
161
+ * decoder, optionally filters named events, and drops empty events. `[DONE]`
162
+ * is dropped by default or retained for protocols that use it as their stream
163
+ * boundary. Retry control events are ignored without interrupting the stream.
164
164
  * Decoder failures become provider output errors so the public error channel
165
165
  * stays `AIError`.
166
166
  */
167
- export const sseFraming = (bytes, events) => bytes.pipe(Stream.decodeText(), Stream.mapAccumEffect(() => {
167
+ export const sseFraming = (bytes, events, includeDone = false) => bytes.pipe(Stream.decodeText(), Stream.mapAccumEffect(() => {
168
168
  const output = [];
169
169
  return {
170
170
  output,
@@ -180,7 +180,7 @@ export const sseFraming = (bytes, events) => bytes.pipe(Stream.decodeText(), Str
180
180
  return [state, state.output.splice(0)];
181
181
  })), Stream.filter((event) => (events === undefined || events.has(event.event)) &&
182
182
  event.data.length > 0 &&
183
- (event.data !== "[DONE]" || (events !== undefined && event.event !== "message"))), Stream.map((event) => event.data));
183
+ (event.data !== "[DONE]" || includeDone || (events !== undefined && event.event !== "message"))), Stream.map((event) => event.data));
184
184
  /**
185
185
  * Canonical invalid-request constructor shared by protocol lowering.
186
186
  */
@@ -125,7 +125,7 @@ export declare const protocol: Protocol<{
125
125
  readonly parallel_tool_calls?: boolean | undefined;
126
126
  readonly reasoning_format?: "parsed" | undefined;
127
127
  readonly include_reasoning?: boolean | undefined;
128
- }, string, {
128
+ }, string, "[DONE]" | {
129
129
  readonly [x: string]: unknown;
130
130
  readonly error?: {
131
131
  readonly [x: string]: unknown;
@@ -4,7 +4,6 @@ import { ProviderShared } from "../protocols/shared.js";
4
4
  import { AuthOptions } from "../route/auth-options.js";
5
5
  import { Route } from "../route/client.js";
6
6
  import { Endpoint } from "../route/endpoint.js";
7
- import { Framing } from "../route/framing.js";
8
7
  import { Protocol } from "../route/protocol.js";
9
8
  import { ProviderID } from "../schema/index.js";
10
9
  import { profiles } from "./openai-compatible-profile.js";
@@ -47,7 +46,7 @@ export const route = Route.make({
47
46
  providerMetadataKey: "openai",
48
47
  protocol,
49
48
  endpoint: Endpoint.path("/chat/completions", { baseURL: profiles.groq.baseURL }),
50
- framing: Framing.sse,
49
+ framing: OpenAIChat.framing,
51
50
  });
52
51
  export const configure = (input = {}) => {
53
52
  const { apiKey: _apiKey, auth: _auth, baseURL, ...defaults } = input;
@@ -282,7 +282,7 @@ export declare const protocol: Protocol<{
282
282
  readonly tool_stream?: boolean | undefined;
283
283
  readonly frequency_penalty?: number | undefined;
284
284
  readonly presence_penalty?: number | undefined;
285
- }, string, {
285
+ }, string, "[DONE]" | {
286
286
  readonly [x: string]: unknown;
287
287
  readonly error?: {
288
288
  readonly [x: string]: unknown;
@@ -1,7 +1,6 @@
1
1
  import { Effect, Schema } from "effect";
2
2
  import { Route } from "../route/client.js";
3
3
  import { Endpoint } from "../route/endpoint.js";
4
- import { Framing } from "../route/framing.js";
5
4
  import { Protocol } from "../route/protocol.js";
6
5
  import { AuthOptions } from "../route/auth-options.js";
7
6
  import { ProviderID } from "../schema/index.js";
@@ -87,7 +86,7 @@ export const route = Route.make({
87
86
  providerMetadataKey: "openrouter",
88
87
  protocol,
89
88
  endpoint: Endpoint.path("/chat/completions", { baseURL: profile.baseURL }),
90
- framing: Framing.sse,
89
+ framing: OpenAIChat.framing,
91
90
  });
92
91
  export const routes = [route];
93
92
  const configuredRoute = (input) => {
@@ -6,8 +6,8 @@ import type { AIError } from "../schema/index.js";
6
6
  * `Framing` is the byte-stream-shaped seam between transport and protocol:
7
7
  *
8
8
  * - SSE (`Framing.sse`) — UTF-8 decode the body, run the SSE channel decoder,
9
- * drop empty / `[DONE]` keep-alives. Each emitted frame is the JSON `data:`
10
- * payload of one event.
9
+ * and emit the `data:` payload of each non-empty event. The default drops
10
+ * `[DONE]`; protocols that use it as a terminal select `sseWithDone`.
11
11
  * - AWS event stream — length-prefixed binary frames with CRC checksums.
12
12
  * Each emitted frame is one parsed binary event record.
13
13
  *
@@ -22,6 +22,8 @@ export interface Definition<Frame> {
22
22
  }
23
23
  /** Server-Sent Events framing. Used by every JSON-streaming HTTP provider. */
24
24
  export declare const sse: Definition<string>;
25
+ /** Server-Sent Events framing that retains the conventional `[DONE]` sentinel. */
26
+ export declare const sseWithDone: Definition<string>;
25
27
  /** SSE framing restricted to protocol-recognized event names. */
26
28
  export declare const sseEvents: (events: ReadonlySet<string>) => Definition<string>;
27
29
  export * as Framing from "./framing.js";
@@ -1,6 +1,11 @@
1
1
  import * as ProviderShared from "../protocols/shared.js";
2
2
  /** Server-Sent Events framing. Used by every JSON-streaming HTTP provider. */
3
3
  export const sse = { id: "sse", frame: ProviderShared.sseFraming };
4
+ /** Server-Sent Events framing that retains the conventional `[DONE]` sentinel. */
5
+ export const sseWithDone = {
6
+ id: "sse",
7
+ frame: (bytes) => ProviderShared.sseFraming(bytes, undefined, true),
8
+ };
4
9
  /** SSE framing restricted to protocol-recognized event names. */
5
10
  export const sseEvents = (events) => ({
6
11
  id: "sse",
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-18554",
3
+ "version": "0.0.0-dev-18558",
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.112",
33
- "@opencode-ai/http-recorder": "0.0.0-dev-18554",
33
+ "@opencode-ai/http-recorder": "0.0.0-dev-18558",
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-18554",
42
+ "@opencode-ai/schema": "0.0.0-dev-18558",
43
43
  "aws4fetch": "1.0.20",
44
44
  "effect": "4.0.0-rc.112",
45
45
  "google-auth-library": "10.5.0"