@opencode-ai/ai 0.0.0-dev-18409 → 0.0.0-dev-18411

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 (36) hide show
  1. package/dist/image.js +5 -4
  2. package/dist/llm.js +4 -7
  3. package/dist/protocols/anthropic-messages.js +7 -5
  4. package/dist/protocols/bedrock-converse.js +4 -6
  5. package/dist/protocols/bedrock-event-stream.js +16 -6
  6. package/dist/protocols/gemini.d.ts +1 -0
  7. package/dist/protocols/gemini.js +13 -1
  8. package/dist/protocols/google-images.js +8 -18
  9. package/dist/protocols/open-responses-channel.js +20 -9
  10. package/dist/protocols/open-responses-continuation.js +9 -8
  11. package/dist/protocols/open-responses.d.ts +1 -1
  12. package/dist/protocols/open-responses.js +7 -16
  13. package/dist/protocols/openai-chat.js +23 -18
  14. package/dist/protocols/openai-images.js +11 -16
  15. package/dist/protocols/openai-responses.js +1 -1
  16. package/dist/protocols/shared.d.ts +7 -3
  17. package/dist/protocols/shared.js +26 -13
  18. package/dist/protocols/utils/image-input.d.ts +4 -4
  19. package/dist/protocols/utils/image-input.js +6 -8
  20. package/dist/protocols/xai-images.js +7 -12
  21. package/dist/protocols/zai-images.js +5 -10
  22. package/dist/provider-error.d.ts +4 -4
  23. package/dist/provider-error.js +31 -32
  24. package/dist/route/auth.js +3 -5
  25. package/dist/route/client.js +29 -11
  26. package/dist/route/executor.d.ts +9 -5
  27. package/dist/route/executor.js +33 -66
  28. package/dist/route/framing.d.ts +2 -0
  29. package/dist/route/transport/http.js +7 -2
  30. package/dist/route/transport/index.d.ts +3 -1
  31. package/dist/route/transport/websocket-channel.d.ts +2 -1
  32. package/dist/route/transport/websocket.d.ts +2 -1
  33. package/dist/route/transport/websocket.js +70 -30
  34. package/dist/schema/errors.d.ts +71 -88
  35. package/dist/schema/errors.js +36 -85
  36. package/package.json +3 -3
@@ -1,6 +1,6 @@
1
1
  import { Cause, Context, Effect, Layer, Option, Schema, Stream } from "effect";
2
2
  import { FetchHttpClient, Headers, HttpClient, HttpClientError, HttpClientRequest, HttpClientResponse, } from "effect/unstable/http";
3
- import { HttpContext, HttpRateLimitDetails, HttpRequestDetails, HttpResponseDetails, AIError, TransportReason, } from "../schema/index.js";
3
+ import { HttpContext, HttpRateLimitDetails, AIError, TransportError } from "../schema/index.js";
4
4
  import { classifyProviderFailure } from "../provider-error.js";
5
5
  export class Service extends Context.Service()("@opencode/AI/RequestExecutor") {
6
6
  }
@@ -60,20 +60,11 @@ const rateLimitDetails = (headers, retryAfter) => {
60
60
  reset: Object.keys(reset).length === 0 ? undefined : reset,
61
61
  });
62
62
  };
63
- const requestDetails = (request) => new HttpRequestDetails({
64
- method: request.method,
65
- url: request.url,
66
- headers: headerDetails(request.headers),
67
- });
68
- const responseDetails = (response) => new HttpResponseDetails({
63
+ export const responseHttp = (response) => new HttpContext({
64
+ url: response.request.url,
69
65
  status: response.status,
70
66
  headers: headerDetails(response.headers),
71
67
  });
72
- const responseBody = (body) => {
73
- if (body === undefined)
74
- return {};
75
- return { body };
76
- };
77
68
  const decodeProviderBody = Schema.decodeUnknownOption(Schema.fromJsonString(Schema.Struct({
78
69
  message: Schema.optionalKey(Schema.String),
79
70
  error: Schema.optionalKey(Schema.Struct({ message: Schema.optionalKey(Schema.String) })),
@@ -83,60 +74,36 @@ const providerMessage = (status, body) => {
83
74
  return ([decoded?.error?.message, decoded?.message].find((message) => message?.trim()) ??
84
75
  `Provider request failed with HTTP ${status}`);
85
76
  };
86
- const responseHttp = (input) => new HttpContext({
87
- request: requestDetails(input.request),
88
- response: responseDetails(input.response),
89
- ...input.body,
90
- rateLimit: input.rateLimit,
91
- });
92
- const statusError = (request) => (response) => Effect.gen(function* () {
77
+ const statusError = (response) => Effect.gen(function* () {
93
78
  if (response.status < 400)
94
79
  return response;
95
- const body = yield* response.text.pipe(Effect.catch(() => Effect.void));
96
- const headers = normalizedHeaders(response.headers);
97
- const retryAfter = retryAfterMs(headers);
98
- const rateLimit = rateLimitDetails(headers, retryAfter);
99
- const details = responseBody(body);
100
- return yield* new AIError({
101
- module: "RequestExecutor",
102
- method: "execute",
103
- reason: classifyProviderFailure({
104
- status: response.status,
105
- message: providerMessage(response.status, body),
106
- retryAfterMs: retryAfter,
107
- rateLimit,
108
- http: responseHttp({
109
- request,
110
- response,
111
- body: details,
112
- rateLimit,
113
- }),
114
- }),
80
+ const result = yield* response.text.pipe(Effect.result);
81
+ return yield* httpFailure({
82
+ message: providerMessage(response.status, result._tag === "Success" ? result.success : undefined),
83
+ url: response.request.url,
84
+ status: response.status,
85
+ responseHeaders: headerDetails(response.headers),
86
+ responseBody: result._tag === "Success" ? result.success : undefined,
87
+ cause: result._tag === "Failure" ? (result.failure.cause ?? result.failure) : undefined,
115
88
  });
116
89
  });
117
- // Classifies an HTTP failure captured outside the executor (for example by the
118
- // AI SDK's own fetch) onto the same reason types and HttpContext that
119
- // executor-driven requests produce. The originating request is not available on
120
- // that path, so the method is assumed (language model calls are always POST),
121
- // request headers are empty.
122
- export const classifyHttpFailure = (input) => {
90
+ /** Preserve HTTP diagnostics for executor and externally captured failures alike. */
91
+ export const httpFailure = (input) => {
123
92
  const headers = normalizedHeaders(Headers.fromInput(input.responseHeaders));
124
93
  const retryAfter = retryAfterMs(headers);
125
94
  const rateLimit = rateLimitDetails(headers, retryAfter);
126
- const details = responseBody(input.responseBody);
127
- return classifyProviderFailure({
128
- message: input.message,
129
- status: input.status,
130
- code: input.code,
131
- retryAfterMs: retryAfter,
132
- rateLimit,
133
- http: new HttpContext({
134
- request: new HttpRequestDetails({ method: "POST", url: input.url, headers: {} }),
135
- response: input.status === undefined
136
- ? undefined
137
- : new HttpResponseDetails({ status: input.status, headers: headerDetails(Headers.fromInput(headers)) }),
138
- ...details,
95
+ return new AIError({
96
+ reason: classifyProviderFailure({
97
+ message: input.message,
98
+ status: input.status,
99
+ data: input.data,
100
+ rawBody: input.responseBody,
101
+ retryAfterMs: retryAfter,
139
102
  rateLimit,
103
+ cause: input.cause,
104
+ http: input.status === undefined || input.url === undefined
105
+ ? undefined
106
+ : new HttpContext({ url: input.url, status: input.status, headers }),
140
107
  }),
141
108
  });
142
109
  };
@@ -160,19 +127,18 @@ const nativeTransportFailure = (error) => {
160
127
  const httpError = (input) => {
161
128
  const request = HttpClientError.isHttpClientError(input.error) ? input.error.request : input.request;
162
129
  const transportError = (failure) => new AIError({
163
- module: "RequestExecutor",
164
- method: input.operation,
165
- reason: new TransportReason({
130
+ reason: new TransportError({
166
131
  message: failure.message,
132
+ cause: source,
133
+ http: input.http,
167
134
  transport: "http",
168
135
  operation: input.operation,
169
136
  code: failure.code,
170
137
  url: request.url,
171
- http: new HttpContext({ request: requestDetails(request) }),
172
138
  }),
173
139
  });
174
140
  const source = HttpClientError.isHttpClientError(input.error) && "cause" in input.error.reason
175
- ? input.error.reason.cause
141
+ ? (input.error.reason.cause ?? input.error)
176
142
  : input.error;
177
143
  const native = nativeTransportFailure(source);
178
144
  const code = native?.code;
@@ -194,19 +160,20 @@ const httpError = (input) => {
194
160
  code: code ?? input.error.reason._tag,
195
161
  });
196
162
  };
163
+ export const responseStream = (response) => response.stream.pipe(Stream.mapError((error) => httpError({ error, request: response.request, operation: "read", http: responseHttp(response) })));
197
164
  export const stream = (executor, request, middleware) => Stream.unwrap(Effect.gen(function* () {
198
165
  const response = yield* executor.execute(request, middleware);
199
- return response.stream.pipe(Stream.mapError((error) => httpError({ error, request: response.request, operation: "read" })));
166
+ return responseStream(response);
200
167
  }));
201
168
  export const layer = Layer.effect(Service, Effect.gen(function* () {
202
169
  const http = yield* HttpClient.HttpClient;
203
170
  const executeOnce = (request, middleware) => Effect.gen(function* () {
204
171
  if (!middleware)
205
- return yield* http.execute(request).pipe(Effect.mapError((error) => httpError({ error, request, operation: "request" })), Effect.flatMap(statusError(request)));
172
+ return yield* http.execute(request).pipe(Effect.mapError((error) => httpError({ error, request, operation: "request" })), Effect.flatMap(statusError));
206
173
  const response = yield* middleware(request, (input) => http
207
174
  .execute(input)
208
175
  .pipe(Effect.mapError((cause) => (cause instanceof Error ? cause : new Error(String(cause)))))).pipe(Effect.mapError((error) => httpError({ error, request, operation: "request" })));
209
- return yield* statusError(response.request)(response);
176
+ return yield* statusError(response);
210
177
  });
211
178
  return Service.of({
212
179
  execute: executeOnce,
@@ -17,6 +17,8 @@ import type { AIError } from "../schema/index.js";
17
17
  export interface Definition<Frame> {
18
18
  readonly id: string;
19
19
  readonly frame: (bytes: Stream.Stream<Uint8Array, AIError>) => Stream.Stream<Frame, AIError>;
20
+ /** Original wire representation when framing transforms the provider payload. */
21
+ readonly body?: (frame: Frame) => string | undefined;
20
22
  }
21
23
  /** Server-Sent Events framing. Used by every JSON-streaming HTTP provider. */
22
24
  export declare const sse: Definition<string>;
@@ -53,8 +53,13 @@ export const httpJson = (input) => ({
53
53
  middleware: prepareInput.middleware,
54
54
  };
55
55
  }),
56
- execute: (prepared, _request, runtime) => Effect.succeed({
57
- frames: prepared.framing.frame(RequestExecutor.stream(runtime.http, prepared.request, prepared.middleware)),
56
+ execute: (prepared, _request, runtime) => Effect.gen(function* () {
57
+ const response = yield* runtime.http.execute(prepared.request, prepared.middleware);
58
+ return {
59
+ frames: prepared.framing.frame(RequestExecutor.responseStream(response)),
60
+ http: RequestExecutor.responseHttp(response),
61
+ body: prepared.framing.body,
62
+ };
58
63
  }),
59
64
  });
60
65
  export const sseJson = {
@@ -3,12 +3,14 @@ import { Endpoint } from "../endpoint.js";
3
3
  import { Auth } from "../auth.js";
4
4
  import type { HttpMiddleware, Interface as RequestExecutorInterface } from "../executor.js";
5
5
  import type { WebSocketChannelExecutor } from "./websocket-channel.js";
6
- import type { AIError, LLMRequest } from "../../schema/index.js";
6
+ import type { AIError, HttpContext, LLMRequest } from "../../schema/index.js";
7
7
  export interface TransportRuntime {
8
8
  readonly http: RequestExecutorInterface;
9
9
  }
10
10
  export interface TransportExecution<Frame> {
11
11
  readonly frames: Stream.Stream<Frame, AIError>;
12
+ readonly http?: HttpContext;
13
+ body?(frame: Frame): string | undefined;
12
14
  /** Optional successful-consumption acknowledgement. HTTP leaves this absent. */
13
15
  readonly complete?: Effect.Effect<void>;
14
16
  }
@@ -1,11 +1,12 @@
1
1
  import type { Effect, Scope, Stream } from "effect";
2
2
  import type { Headers } from "effect/unstable/http";
3
- import type { AIError } from "../../schema/index.js";
3
+ import type { AIError, HttpContext } from "../../schema/index.js";
4
4
  export interface WebSocketChannelExecutor {
5
5
  readonly execute: (exchange: WebSocketChannelExchange) => Effect.Effect<WebSocketChannelExecution, AIError, Scope.Scope>;
6
6
  }
7
7
  export interface WebSocketChannelExecution {
8
8
  readonly frames: Stream.Stream<string, AIError>;
9
+ readonly http?: HttpContext;
9
10
  /** Commits staged state after the decoded Route stream ends successfully. */
10
11
  readonly complete: Effect.Effect<void>;
11
12
  }
@@ -1,7 +1,7 @@
1
1
  import { Effect, Stream } from "effect";
2
2
  import { Headers } from "effect/unstable/http";
3
3
  import { Socket } from "effect/unstable/socket";
4
- import { AIError } from "../../schema/index.js";
4
+ import { AIError, type HttpContext } from "../../schema/index.js";
5
5
  import type { Transport } from "./index.js";
6
6
  import type { WebSocketChannelExecutor } from "./websocket-channel.js";
7
7
  export interface WebSocketRequest {
@@ -9,6 +9,7 @@ export interface WebSocketRequest {
9
9
  readonly headers: Headers.Headers;
10
10
  }
11
11
  export interface WebSocketConnection {
12
+ readonly http?: HttpContext;
12
13
  readonly sendText: (message: string) => Effect.Effect<void, AIError>;
13
14
  readonly messages: Stream.Stream<string | Uint8Array, AIError>;
14
15
  readonly close: Effect.Effect<void, never>;
@@ -1,14 +1,14 @@
1
1
  import { Cause, Effect, Queue, Stream } from "effect";
2
2
  import { Headers } from "effect/unstable/http";
3
3
  import { Socket } from "effect/unstable/socket";
4
- import { AIError, TransportReason } from "../../schema/index.js";
4
+ import { AIError, AIErrorReason, TransportError, } from "../../schema/index.js";
5
5
  import * as HttpTransport from "./http.js";
6
6
  const MAX_FRAME_BYTES = 16 * 1024 * 1024;
7
- const transportError = (method, message, input) => new AIError({
8
- module: "WebSocketConnector",
9
- method,
10
- reason: new TransportReason({
7
+ const transportError = (message, input) => new AIError({
8
+ reason: new TransportError({
11
9
  message,
10
+ body: input.body,
11
+ cause: input.cause,
12
12
  transport: "websocket",
13
13
  operation: input.operation,
14
14
  url: input.url,
@@ -19,18 +19,12 @@ const transportError = (method, message, input) => new AIError({
19
19
  });
20
20
  const annotateTransportError = (error, input) => error.reason._tag === "Transport"
21
21
  ? new AIError({
22
- module: error.module,
23
- method: error.method,
24
- reason: new TransportReason({
22
+ reason: new TransportError({
23
+ ...error.reason,
25
24
  message: error.reason.message,
26
- transport: error.reason.transport,
27
- operation: error.reason.operation,
28
- code: error.reason.code,
29
- url: error.reason.url,
30
- http: error.reason.http,
25
+ cause: error.reason.cause,
31
26
  phase: input.phase,
32
27
  delivery: input.delivery,
33
- recovery: error.reason.recovery,
34
28
  }),
35
29
  })
36
30
  : error;
@@ -52,7 +46,7 @@ const waitOpen = (ws, input) => {
52
46
  if (ws.readyState === globalThis.WebSocket.OPEN)
53
47
  return Effect.void;
54
48
  if (ws.readyState === globalThis.WebSocket.CLOSING || ws.readyState === globalThis.WebSocket.CLOSED) {
55
- return Effect.fail(transportError("open", `WebSocket closed before opening (state ${ws.readyState})`, {
49
+ return Effect.fail(transportError(`WebSocket closed before opening (state ${ws.readyState})`, {
56
50
  url: input.url,
57
51
  operation: "request",
58
52
  code: "closed",
@@ -78,7 +72,8 @@ const waitOpen = (ws, input) => {
78
72
  };
79
73
  const onError = (event) => {
80
74
  cleanup();
81
- resume(Effect.fail(transportError("open", `Failed to open WebSocket: ${eventMessage(event)}`, {
75
+ resume(Effect.fail(transportError(`Failed to open WebSocket: ${eventMessage(event)}`, {
76
+ cause: "error" in event ? (event.error ?? event) : event,
82
77
  url: input.url,
83
78
  operation: "request",
84
79
  phase: "connect",
@@ -87,7 +82,9 @@ const waitOpen = (ws, input) => {
87
82
  };
88
83
  const onClose = (event) => {
89
84
  cleanup();
90
- resume(Effect.fail(transportError("open", `WebSocket closed before opening with code ${event.code}`, {
85
+ resume(Effect.fail(transportError(`WebSocket closed before opening with code ${event.code}`, {
86
+ body: event.reason,
87
+ cause: event,
91
88
  url: input.url,
92
89
  operation: "request",
93
90
  code: String(event.code),
@@ -114,7 +111,8 @@ export const toWebSocketUrl = (value) => Effect.try({
114
111
  }
115
112
  throw new Error(`Unsupported WebSocket URL protocol ${url.protocol}`);
116
113
  },
117
- catch: (error) => transportError("prepare", error instanceof Error ? error.message : "Invalid WebSocket URL", {
114
+ catch: (error) => transportError(error instanceof Error ? error.message : "Invalid WebSocket URL", {
115
+ cause: error,
118
116
  url: value,
119
117
  operation: "request",
120
118
  code: "invalid-url",
@@ -131,7 +129,8 @@ export const open = (input) => Effect.gen(function* () {
131
129
  constructor(input.url, {
132
130
  headers: input.headers,
133
131
  }),
134
- catch: (error) => transportError("open", error instanceof Error ? error.message : "Failed to construct WebSocket", {
132
+ catch: (error) => transportError(error instanceof Error ? error.message : "Failed to construct WebSocket", {
133
+ cause: error,
135
134
  url: input.url,
136
135
  operation: "request",
137
136
  phase: "connect",
@@ -147,7 +146,8 @@ export const fromWebSocket = (ws, input) => Effect.gen(function* () {
147
146
  const rejectOversized = (message) => {
148
147
  if (!oversized(message))
149
148
  return false;
150
- Queue.failCauseUnsafe(messages, Cause.fail(transportError("message", "WebSocket message exceeds the 16 MiB limit", {
149
+ Queue.failCauseUnsafe(messages, Cause.fail(transportError("WebSocket message exceeds the 16 MiB limit", {
150
+ body: typeof message === "string" ? message : new TextDecoder().decode(message),
151
151
  url: input.url,
152
152
  operation: "read",
153
153
  code: "message-too-large",
@@ -162,7 +162,8 @@ export const fromWebSocket = (ws, input) => Effect.gen(function* () {
162
162
  return;
163
163
  if (Queue.offerUnsafe(messages, message))
164
164
  return;
165
- Queue.failCauseUnsafe(messages, Cause.fail(transportError("message", "WebSocket inbound queue overflow", {
165
+ Queue.failCauseUnsafe(messages, Cause.fail(transportError("WebSocket inbound queue overflow", {
166
+ body: typeof message === "string" ? message : new TextDecoder().decode(message),
166
167
  url: input.url,
167
168
  operation: "read",
168
169
  code: "queue-overflow",
@@ -175,7 +176,8 @@ export const fromWebSocket = (ws, input) => Effect.gen(function* () {
175
176
  const binary = binaryMessage(event.data);
176
177
  if (binary)
177
178
  return offer(binary);
178
- Queue.failCauseUnsafe(messages, Cause.fail(transportError("message", "Unsupported WebSocket message payload", {
179
+ Queue.failCauseUnsafe(messages, Cause.fail(transportError("Unsupported WebSocket message payload", {
180
+ cause: event,
179
181
  url: input.url,
180
182
  operation: "read",
181
183
  code: "message",
@@ -183,7 +185,8 @@ export const fromWebSocket = (ws, input) => Effect.gen(function* () {
183
185
  })));
184
186
  };
185
187
  const onError = (event) => {
186
- Queue.failCauseUnsafe(messages, Cause.fail(transportError("message", `WebSocket error: ${eventMessage(event)}`, {
188
+ Queue.failCauseUnsafe(messages, Cause.fail(transportError(`WebSocket error: ${eventMessage(event)}`, {
189
+ cause: "error" in event ? (event.error ?? event) : event,
187
190
  url: input.url,
188
191
  operation: "read",
189
192
  code: "message",
@@ -191,7 +194,9 @@ export const fromWebSocket = (ws, input) => Effect.gen(function* () {
191
194
  })));
192
195
  };
193
196
  const onClose = (event) => {
194
- Queue.failCauseUnsafe(messages, Cause.fail(transportError("message", `WebSocket closed with code ${event.code}`, {
197
+ Queue.failCauseUnsafe(messages, Cause.fail(transportError(`WebSocket closed with code ${event.code}`, {
198
+ body: event.reason,
199
+ cause: event,
195
200
  url: input.url,
196
201
  operation: "read",
197
202
  code: String(event.code),
@@ -209,7 +214,7 @@ export const fromWebSocket = (ws, input) => Effect.gen(function* () {
209
214
  return {
210
215
  sendText: (message) => Effect.suspend(() => {
211
216
  if (ws.readyState !== globalThis.WebSocket.OPEN)
212
- return Effect.fail(transportError("sendText", `WebSocket is not open (state ${ws.readyState})`, {
217
+ return Effect.fail(transportError(`WebSocket is not open (state ${ws.readyState})`, {
213
218
  url: input.url,
214
219
  operation: "write",
215
220
  phase: "send",
@@ -217,7 +222,8 @@ export const fromWebSocket = (ws, input) => Effect.gen(function* () {
217
222
  }));
218
223
  return Effect.try({
219
224
  try: () => ws.send(message),
220
- catch: (error) => transportError("sendText", error instanceof Error ? error.message : "Failed to send WebSocket message", {
225
+ catch: (error) => transportError(error instanceof Error ? error.message : "Failed to send WebSocket message", {
226
+ cause: error,
221
227
  url: input.url,
222
228
  operation: "write",
223
229
  phase: "send",
@@ -246,17 +252,51 @@ export const makeDirect = (connector) => ({
246
252
  .open(exchange.connect)
247
253
  .pipe(Effect.mapError((error) => annotateTransportError(error, { phase: "connect", delivery: "not-sent" }))), (connection) => connection.close);
248
254
  const create = yield* exchange.driver.create(undefined);
249
- yield* connection.sendText(create.message);
255
+ yield* connection.sendText(create.message).pipe(Effect.mapError((error) => new AIError({
256
+ reason: AIErrorReason.make({
257
+ ...error.reason,
258
+ message: error.reason.message,
259
+ cause: error.reason.cause,
260
+ http: error.reason.http ?? connection.http,
261
+ }),
262
+ })));
250
263
  const decoder = new TextDecoder();
251
264
  let observed = false;
252
265
  return {
266
+ http: connection.http,
253
267
  frames: connection.messages.pipe(Stream.map((message) => {
254
268
  observed = true;
255
269
  return messageText(message, decoder);
256
270
  }), Stream.mapError((error) => annotateTransportError(error, {
257
271
  phase: error.reason._tag === "Transport" && error.reason.phase === "close" ? "close" : "receive",
258
272
  delivery: observed ? "accepted" : "ambiguous",
259
- })), Stream.mapEffect((frame) => exchange.driver.observe(create, frame)), Stream.takeUntil(observationTerminal), Stream.mapEffect(observationFrame)),
273
+ })), Stream.mapEffect((frame) => exchange.driver.observe(create, frame).pipe(Effect.mapError((error) => new AIError({
274
+ reason: AIErrorReason.make({
275
+ ...error.reason,
276
+ message: error.reason.message,
277
+ cause: error.reason.cause,
278
+ body: frame,
279
+ }),
280
+ })), Effect.map((observation) => "error" in observation
281
+ ? {
282
+ ...observation,
283
+ error: new AIError({
284
+ reason: AIErrorReason.make({
285
+ ...observation.error.reason,
286
+ message: observation.error.reason.message,
287
+ cause: observation.error.reason.cause,
288
+ body: frame,
289
+ }),
290
+ }),
291
+ }
292
+ : observation))), Stream.takeUntil(observationTerminal), Stream.mapEffect(observationFrame), Stream.mapError((error) => new AIError({
293
+ reason: AIErrorReason.make({
294
+ ...error.reason,
295
+ message: error.reason.message,
296
+ cause: error.reason.cause,
297
+ http: error.reason.http ?? connection.http,
298
+ }),
299
+ }))),
260
300
  complete: Effect.void,
261
301
  };
262
302
  }),
@@ -283,7 +323,7 @@ export const json = (input) => ({
283
323
  execute: (prepared, request, _runtime, options) => {
284
324
  const webSocket = options?.webSocket;
285
325
  if (!webSocket) {
286
- return Effect.fail(transportError("json", "WebSocket JSON transport requires StreamOptions.webSocket", {
326
+ return Effect.fail(transportError("WebSocket JSON transport requires StreamOptions.webSocket", {
287
327
  url: prepared.url,
288
328
  operation: "request",
289
329
  code: "unavailable",
@@ -298,7 +338,7 @@ export const json = (input) => ({
298
338
  const exchange = {
299
339
  id: request.id ?? "request",
300
340
  connect: { url: prepared.url, headers: prepared.headers },
301
- fallback: () => Stream.fail(transportError("fallback", "WebSocket JSON transport does not provide HTTP fallback", {
341
+ fallback: () => Stream.fail(transportError("WebSocket JSON transport does not provide HTTP fallback", {
302
342
  url: prepared.url,
303
343
  operation: "request",
304
344
  code: "websocket",