@opencode-ai/ai 0.0.0-beta-18148 → 0.0.0-beta-18219

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,7 +1,7 @@
1
1
  import { Context, Effect, Layer } from "effect";
2
2
  import { RequestExecutor } from "./route/executor.js";
3
+ import { type AIError } from "./schema/index.js";
3
4
  import type { ImageOptions, ImageRequestFor, ImageResponse } from "./image.js";
4
- import type { AIError } from "./schema/index.js";
5
5
  export type Execute = RequestExecutor.Interface["execute"];
6
6
  export interface Interface {
7
7
  readonly generate: <Options extends ImageOptions>(request: ImageRequestFor<Options>) => Effect.Effect<ImageResponse, AIError>;
@@ -1,5 +1,7 @@
1
1
  import { Context, Effect, Layer } from "effect";
2
2
  import { RequestExecutor } from "./route/executor.js";
3
+ import { mergeHttpOptions } from "./schema/index.js";
4
+ import { sanitizeSurrogates } from "./utils/sanitize.js";
3
5
  export class Service extends Context.Service()("@opencode/ImageClient") {
4
6
  }
5
7
  export const generate = (request) => Effect.gen(function* () {
@@ -9,7 +11,14 @@ export const generate = (request) => Effect.gen(function* () {
9
11
  export const layer = Layer.effect(Service, Effect.gen(function* () {
10
12
  const executor = yield* RequestExecutor.Service;
11
13
  return Service.of({
12
- generate: (request) => request.model.route.generate(request, executor.execute),
14
+ generate: (request) => request.model.route.generate({
15
+ ...sanitizeSurrogates({
16
+ ...request,
17
+ model: undefined,
18
+ http: mergeHttpOptions(request.model.http, request.http),
19
+ }),
20
+ model: request.model,
21
+ }, executor.execute),
13
22
  });
14
23
  }));
15
24
  export const ImageClient = {
@@ -584,15 +584,7 @@ export declare const protocol: Protocol<{
584
584
  readonly type?: string | undefined;
585
585
  readonly message?: string | undefined;
586
586
  } | undefined;
587
- readonly delta?: {
588
- readonly type?: string | undefined;
589
- readonly text?: string | undefined;
590
- readonly thinking?: string | undefined;
591
- readonly signature?: string | undefined;
592
- readonly partial_json?: string | undefined;
593
- readonly stop_reason?: string | null | undefined;
594
- readonly stop_sequence?: string | null | undefined;
595
- } | undefined;
587
+ readonly delta?: unknown;
596
588
  readonly index?: number | undefined;
597
589
  readonly usage?: {
598
590
  readonly [x: string]: unknown;
@@ -609,18 +601,7 @@ export declare const protocol: Protocol<{
609
601
  readonly thinking_tokens?: number | undefined;
610
602
  } | null | undefined;
611
603
  } | undefined;
612
- readonly content_block?: {
613
- readonly type: string;
614
- readonly id?: string | undefined;
615
- readonly data?: string | undefined;
616
- readonly name?: string | undefined;
617
- readonly input?: unknown;
618
- readonly text?: string | undefined;
619
- readonly content?: unknown;
620
- readonly thinking?: string | undefined;
621
- readonly signature?: string | undefined;
622
- readonly tool_use_id?: string | undefined;
623
- } | undefined;
604
+ readonly content_block?: unknown;
624
605
  }, {
625
606
  tools: Partial<Record<number, ToolStream.PendingTool>>;
626
607
  reasoningSignatures: {};
@@ -1,5 +1,5 @@
1
1
  import { Buffer } from "node:buffer";
2
- import { Effect, Schema } from "effect";
2
+ import { Effect, Option, Schema } from "effect";
3
3
  import { Tool } from "@opencode-ai/schema/tool";
4
4
  import { Route } from "../route/client.js";
5
5
  import { Auth } from "../route/auth.js";
@@ -264,6 +264,7 @@ const AnthropicStreamBlock = Schema.Struct({
264
264
  tool_use_id: Schema.optional(Schema.String),
265
265
  content: Schema.optional(Schema.Unknown),
266
266
  });
267
+ const decodeAnthropicStreamBlock = Schema.decodeUnknownOption(AnthropicStreamBlock);
267
268
  const AnthropicStreamDelta = Schema.Struct({
268
269
  type: Schema.optional(Schema.String),
269
270
  text: Schema.optional(Schema.String),
@@ -273,12 +274,13 @@ const AnthropicStreamDelta = Schema.Struct({
273
274
  stop_reason: optionalNull(Schema.String),
274
275
  stop_sequence: optionalNull(Schema.String),
275
276
  });
277
+ const decodeAnthropicStreamDelta = Schema.decodeUnknownOption(AnthropicStreamDelta);
276
278
  const AnthropicEvent = Schema.Struct({
277
279
  type: Schema.String,
278
280
  index: Schema.optional(Schema.Number),
279
281
  message: Schema.optional(Schema.Struct({ usage: Schema.optional(AnthropicUsage) })),
280
- content_block: Schema.optional(AnthropicStreamBlock),
281
- delta: Schema.optional(AnthropicStreamDelta),
282
+ content_block: Schema.optional(Schema.Unknown),
283
+ delta: Schema.optional(Schema.Unknown),
282
284
  usage: Schema.optional(AnthropicUsage),
283
285
  // `type` and `message` are both required per Anthropic's spec, but
284
286
  // OpenAI-compatible proxies and gateway translations occasionally drop one
@@ -1044,6 +1046,8 @@ const onContentBlockStart = (state, event) => {
1044
1046
  const onContentBlockDelta = Effect.fn("AnthropicMessages.onContentBlockDelta")(function* (state, event) {
1045
1047
  const delta = event.delta;
1046
1048
  if (delta?.type === "text_delta" && delta.text) {
1049
+ if (!state.lifecycle.text.has(`text-${event.index ?? 0}`))
1050
+ return [state, NO_EVENTS];
1047
1051
  const events = [];
1048
1052
  return [
1049
1053
  { ...state, lifecycle: Lifecycle.textDelta(state.lifecycle, events, `text-${event.index ?? 0}`, delta.text) },
@@ -1051,6 +1055,8 @@ const onContentBlockDelta = Effect.fn("AnthropicMessages.onContentBlockDelta")(f
1051
1055
  ];
1052
1056
  }
1053
1057
  if (delta?.type === "thinking_delta" && delta.thinking) {
1058
+ if (!state.lifecycle.reasoning.has(`reasoning-${event.index ?? 0}`))
1059
+ return [state, NO_EVENTS];
1054
1060
  const events = [];
1055
1061
  return [
1056
1062
  {
@@ -1062,6 +1068,8 @@ const onContentBlockDelta = Effect.fn("AnthropicMessages.onContentBlockDelta")(f
1062
1068
  }
1063
1069
  if (delta?.type === "signature_delta" && delta.signature) {
1064
1070
  const index = event.index ?? 0;
1071
+ if (!state.lifecycle.reasoning.has(`reasoning-${index}`))
1072
+ return [state, NO_EVENTS];
1065
1073
  return [
1066
1074
  {
1067
1075
  ...state,
@@ -1148,25 +1156,62 @@ const onError = (event) => Effect.fail(new AIError({
1148
1156
  method: "stream",
1149
1157
  reason: classifyProviderFailure({ message: providerErrorMessage(event), code: event.error?.type }),
1150
1158
  }));
1159
+ const isKnownStreamBlockType = (type) => type === "text" ||
1160
+ type === "thinking" ||
1161
+ type === "redacted_thinking" ||
1162
+ type === "tool_use" ||
1163
+ type === "server_tool_use" ||
1164
+ isServerToolResultType(type);
1165
+ const isKnownStreamDeltaType = (type) => type === "text_delta" || type === "thinking_delta" || type === "signature_delta" || type === "input_json_delta";
1166
+ const invalidStreamEvent = (event) => Effect.fail(ProviderShared.eventError(ADAPTER, "Invalid anthropic/anthropic-messages stream event", ProviderShared.encodeJson(event)));
1151
1167
  const step = (state, event) => {
1168
+ if (!SSE_EVENTS.has(event.type))
1169
+ return Effect.succeed([state, NO_EVENTS]);
1170
+ if (event.type !== "content_block_start" &&
1171
+ event.content_block !== undefined &&
1172
+ Option.isNone(decodeAnthropicStreamBlock(event.content_block)))
1173
+ return invalidStreamEvent(event);
1174
+ if (event.type !== "content_block_delta" &&
1175
+ event.delta !== undefined &&
1176
+ Option.isNone(decodeAnthropicStreamDelta(event.delta)))
1177
+ return invalidStreamEvent(event);
1152
1178
  if (event.type === "message_start")
1153
1179
  return Effect.succeed(onMessageStart(state, event));
1154
1180
  if (event.type === "content_block_start") {
1155
- const block = event.content_block;
1156
- if (block && (block.type === "tool_use" || block.type === "server_tool_use")) {
1181
+ if (!ProviderShared.isRecord(event.content_block) || typeof event.content_block.type !== "string")
1182
+ return invalidStreamEvent(event);
1183
+ if (!isKnownStreamBlockType(event.content_block.type))
1184
+ return Effect.succeed([state, NO_EVENTS]);
1185
+ const decoded = decodeAnthropicStreamBlock(event.content_block);
1186
+ if (Option.isNone(decoded))
1187
+ return invalidStreamEvent(event);
1188
+ const block = decoded.value;
1189
+ if (block.type === "tool_use" || block.type === "server_tool_use") {
1157
1190
  if (event.index === undefined)
1158
1191
  return Effect.fail(ProviderShared.eventError(ADAPTER, `Anthropic ${block.type} missing index`));
1159
1192
  if (!block.id)
1160
1193
  return Effect.fail(ProviderShared.eventError(ADAPTER, `Anthropic tool_use missing id at index ${event.index}`));
1161
1194
  }
1162
- return Effect.succeed(onContentBlockStart(state, event));
1195
+ return Effect.succeed(onContentBlockStart(state, { ...event, content_block: block }));
1196
+ }
1197
+ if (event.type === "content_block_delta") {
1198
+ if (!ProviderShared.isRecord(event.delta))
1199
+ return invalidStreamEvent(event);
1200
+ if (typeof event.delta.type === "string" && !isKnownStreamDeltaType(event.delta.type))
1201
+ return Effect.succeed([state, NO_EVENTS]);
1202
+ const decoded = decodeAnthropicStreamDelta(event.delta);
1203
+ if (Option.isNone(decoded))
1204
+ return invalidStreamEvent(event);
1205
+ return onContentBlockDelta(state, { ...event, delta: decoded.value });
1163
1206
  }
1164
- if (event.type === "content_block_delta")
1165
- return onContentBlockDelta(state, event);
1166
1207
  if (event.type === "content_block_stop")
1167
1208
  return onContentBlockStop(state, event);
1168
- if (event.type === "message_delta")
1169
- return Effect.succeed(onMessageDelta(state, event));
1209
+ if (event.type === "message_delta") {
1210
+ const decoded = decodeAnthropicStreamDelta(event.delta);
1211
+ if (Option.isNone(decoded))
1212
+ return invalidStreamEvent(event);
1213
+ return Effect.succeed(onMessageDelta(state, { ...event, delta: decoded.value }));
1214
+ }
1170
1215
  if (event.type === "message_stop")
1171
1216
  return onMessageStop(state);
1172
1217
  if (event.type === "error")
@@ -273,6 +273,14 @@ export declare const protocol: Protocol<{
273
273
  } | undefined;
274
274
  readonly metrics?: unknown;
275
275
  } | undefined;
276
+ readonly exception?: {
277
+ readonly type: string;
278
+ readonly details: {
279
+ readonly message?: string | undefined;
280
+ readonly originalMessage?: string | undefined;
281
+ readonly originalStatusCode?: number | undefined;
282
+ };
283
+ } | undefined;
276
284
  readonly messageStart?: {
277
285
  readonly role: string;
278
286
  } | undefined;
@@ -307,31 +315,6 @@ export declare const protocol: Protocol<{
307
315
  readonly stopReason: string;
308
316
  readonly additionalModelResponseFields?: unknown;
309
317
  } | undefined;
310
- readonly internalServerException?: {
311
- readonly message?: string | undefined;
312
- readonly originalMessage?: string | undefined;
313
- readonly originalStatusCode?: number | undefined;
314
- } | undefined;
315
- readonly modelStreamErrorException?: {
316
- readonly message?: string | undefined;
317
- readonly originalMessage?: string | undefined;
318
- readonly originalStatusCode?: number | undefined;
319
- } | undefined;
320
- readonly validationException?: {
321
- readonly message?: string | undefined;
322
- readonly originalMessage?: string | undefined;
323
- readonly originalStatusCode?: number | undefined;
324
- } | undefined;
325
- readonly throttlingException?: {
326
- readonly message?: string | undefined;
327
- readonly originalMessage?: string | undefined;
328
- readonly originalStatusCode?: number | undefined;
329
- } | undefined;
330
- readonly serviceUnavailableException?: {
331
- readonly message?: string | undefined;
332
- readonly originalMessage?: string | undefined;
333
- readonly originalStatusCode?: number | undefined;
334
- } | undefined;
335
318
  }, {
336
319
  readonly metadata?: {
337
320
  readonly usage?: {
@@ -343,6 +326,14 @@ export declare const protocol: Protocol<{
343
326
  } | undefined;
344
327
  readonly metrics?: unknown;
345
328
  } | undefined;
329
+ readonly exception?: {
330
+ readonly type: string;
331
+ readonly details: {
332
+ readonly message?: string | undefined;
333
+ readonly originalMessage?: string | undefined;
334
+ readonly originalStatusCode?: number | undefined;
335
+ };
336
+ } | undefined;
346
337
  readonly messageStart?: {
347
338
  readonly role: string;
348
339
  } | undefined;
@@ -377,31 +368,6 @@ export declare const protocol: Protocol<{
377
368
  readonly stopReason: string;
378
369
  readonly additionalModelResponseFields?: unknown;
379
370
  } | undefined;
380
- readonly internalServerException?: {
381
- readonly message?: string | undefined;
382
- readonly originalMessage?: string | undefined;
383
- readonly originalStatusCode?: number | undefined;
384
- } | undefined;
385
- readonly modelStreamErrorException?: {
386
- readonly message?: string | undefined;
387
- readonly originalMessage?: string | undefined;
388
- readonly originalStatusCode?: number | undefined;
389
- } | undefined;
390
- readonly validationException?: {
391
- readonly message?: string | undefined;
392
- readonly originalMessage?: string | undefined;
393
- readonly originalStatusCode?: number | undefined;
394
- } | undefined;
395
- readonly throttlingException?: {
396
- readonly message?: string | undefined;
397
- readonly originalMessage?: string | undefined;
398
- readonly originalStatusCode?: number | undefined;
399
- } | undefined;
400
- readonly serviceUnavailableException?: {
401
- readonly message?: string | undefined;
402
- readonly originalMessage?: string | undefined;
403
- readonly originalStatusCode?: number | undefined;
404
- } | undefined;
405
371
  }, ParserState>;
406
372
  export declare const route: Route<{
407
373
  readonly messages: readonly ({
@@ -149,11 +149,7 @@ const BedrockEvent = Schema.Struct({
149
149
  usage: Schema.optional(BedrockUsageSchema),
150
150
  metrics: Schema.optional(Schema.Unknown),
151
151
  })),
152
- internalServerException: Schema.optional(BedrockStreamException),
153
- modelStreamErrorException: Schema.optional(BedrockStreamException),
154
- validationException: Schema.optional(BedrockStreamException),
155
- throttlingException: Schema.optional(BedrockStreamException),
156
- serviceUnavailableException: Schema.optional(BedrockStreamException),
152
+ exception: Schema.optional(Schema.Struct({ type: Schema.String, details: BedrockStreamException })),
157
153
  });
158
154
  // =============================================================================
159
155
  // Request Lowering
@@ -507,20 +503,13 @@ const step = (state, event) => Effect.gen(function* () {
507
503
  [],
508
504
  ];
509
505
  }
510
- const exception = [
511
- ["internalServerException", event.internalServerException],
512
- ["modelStreamErrorException", event.modelStreamErrorException],
513
- ["serviceUnavailableException", event.serviceUnavailableException],
514
- ["throttlingException", event.throttlingException],
515
- ["validationException", event.validationException],
516
- ].find((entry) => entry[1] !== undefined);
517
- if (exception) {
506
+ if (event.exception) {
518
507
  return yield* new AIError({
519
508
  module: ADAPTER,
520
509
  method: "stream",
521
510
  reason: classifyProviderFailure({
522
- message: exception[1]?.message ?? exception[1]?.originalMessage ?? "Bedrock Converse stream error",
523
- code: exception[0],
511
+ message: event.exception.details.message ?? event.exception.details.originalMessage ?? "Bedrock Converse stream error",
512
+ code: event.exception.type,
524
513
  }),
525
514
  });
526
515
  }
@@ -565,7 +554,7 @@ export const protocol = Protocol.make({
565
554
  reasoningSignatures: {},
566
555
  }),
567
556
  step,
568
- onHalt,
557
+ onHalt: (state) => Effect.succeed(onHalt(state)),
569
558
  },
570
559
  });
571
560
  export const route = Route.make({
@@ -56,7 +56,7 @@ const consumeFrames = (route) => (state, chunk) => Effect.gen(function* () {
56
56
  // against ad-hoc `JSON.parse` calls.
57
57
  const parsed = (yield* ProviderShared.parseJson(route, payload, "Failed to parse Bedrock Converse event-stream payload"));
58
58
  delete parsed.p;
59
- out.push({ [eventType]: parsed });
59
+ out.push(messageType === "exception" ? { exception: { type: eventType, details: parsed } } : { [eventType]: parsed });
60
60
  }
61
61
  return [cursor, out];
62
62
  });
@@ -574,7 +574,7 @@ export const protocol = Protocol.make({
574
574
  lifecycle: Lifecycle.initial(),
575
575
  }),
576
576
  step,
577
- onHalt: finish,
577
+ onHalt: (state) => Effect.succeed(finish(state)),
578
578
  },
579
579
  });
580
580
  export const route = Route.make({
@@ -133,7 +133,12 @@ export const driver = (input) => {
133
133
  ...observation,
134
134
  checkpoint: {
135
135
  protocol: PROTOCOL,
136
- value: { version: VERSION, responseID, request, output: output.slice() },
136
+ value: {
137
+ version: VERSION,
138
+ responseID,
139
+ request,
140
+ output: event.response?.output ? [...event.response.output] : output.slice(),
141
+ },
137
142
  },
138
143
  };
139
144
  }),
@@ -384,8 +384,10 @@ export declare const decodeKnownErrorEvent: (event: Event) => Effect.Effect<{
384
384
  export declare const Event: Schema.StructWithRest<Schema.Struct<{
385
385
  readonly type: Schema.String;
386
386
  readonly delta: Schema.optional<Schema.String>;
387
+ readonly arguments: Schema.optional<Schema.String>;
387
388
  readonly text: Schema.optional<Schema.String>;
388
389
  readonly item_id: Schema.optional<Schema.String>;
390
+ readonly output_index: Schema.optional<Schema.Number>;
389
391
  readonly summary_index: Schema.optional<Schema.Number>;
390
392
  readonly item: Schema.optional<Schema.StructWithRest<Schema.Struct<{
391
393
  readonly type: Schema.String;
@@ -401,6 +403,14 @@ export declare const Event: Schema.StructWithRest<Schema.Struct<{
401
403
  readonly incomplete_details: Schema.optional<Schema.NullOr<Schema.Struct<{
402
404
  readonly reason: Schema.optional<Schema.String>;
403
405
  }>>>;
406
+ readonly output: Schema.optional<Schema.$Array<Schema.StructWithRest<Schema.Struct<{
407
+ readonly type: Schema.String;
408
+ readonly id: Schema.optional<Schema.String>;
409
+ readonly call_id: Schema.optional<Schema.String>;
410
+ readonly name: Schema.optional<Schema.String>;
411
+ readonly arguments: Schema.optional<Schema.String>;
412
+ readonly encrypted_content: Schema.optional<Schema.NullOr<Schema.String>>;
413
+ }>, readonly [Schema.$Record<Schema.String, Schema.Unknown>]>>>;
404
414
  readonly usage: Schema.optional<Schema.NullOr<Schema.Struct<{
405
415
  readonly input_tokens: Schema.optional<Schema.Number>;
406
416
  readonly input_tokens_details: Schema.optional<Schema.NullOr<Schema.Struct<{
@@ -452,6 +462,7 @@ export interface ParserState {
452
462
  readonly tools: ToolStream.State<string>;
453
463
  readonly hasFunctionCall: boolean;
454
464
  readonly lifecycle: Lifecycle.State;
465
+ readonly outputItems: Readonly<Record<number, string>>;
455
466
  readonly messageItems: ReadonlySet<string>;
456
467
  readonly messagePhases: Readonly<Record<string, MessagePhase | null>>;
457
468
  readonly reasoningItems: Readonly<Record<string, ReasoningStreamItem>>;
@@ -652,10 +663,11 @@ export declare const fromRequest: (request: LLMRequest) => Effect.Effect<{
652
663
  export declare const providerMetadata: (state: ParserState, metadata: Record<string, unknown>) => ProviderMetadata;
653
664
  export type StepResult = readonly [ParserState, ReadonlyArray<LLMEvent>];
654
665
  export declare const terminal: (event: Event) => boolean;
666
+ export declare const outputItemID: (state: ParserState, event: Event) => string | undefined;
655
667
  export declare const onReasoningDelta: (state: ParserState, event: Event, itemID: string) => StepResult;
656
668
  export declare const onReasoningDone: (state: ParserState, event: Event, itemID: string) => StepResult;
657
669
  export declare const providerFailure: (id: string, event: Event, fallback: string) => AIError;
658
- export declare const step: (state: ParserState, event: Event) => AIError | Effect.Effect<StepResult, never, never> | Effect.Effect<[ParserState, readonly ({
670
+ export declare const step: (state: ParserState, input: Event) => AIError | Effect.Effect<[ParserState, readonly ({
659
671
  readonly type: "step-start";
660
672
  readonly index: number;
661
673
  } | {
@@ -837,7 +849,7 @@ export declare const step: (state: ParserState, event: Event) => AIError | Effec
837
849
  };
838
850
  } | undefined;
839
851
  readonly classification?: "context-overflow" | "payload-too-large" | undefined;
840
- })[]], AIError, never>;
852
+ })[]], AIError, never> | Effect.Effect<StepResult, never, never>;
841
853
  /**
842
854
  * The provider-neutral Open Responses protocol. Provider-specific Responses
843
855
  * implementations compose this baseline with their own tools and event variants.
@@ -986,6 +998,15 @@ export declare const protocol: Protocol<{
986
998
  readonly response?: {
987
999
  readonly [x: string]: unknown;
988
1000
  readonly id?: string | undefined;
1001
+ readonly output?: readonly {
1002
+ readonly [x: string]: unknown;
1003
+ readonly type: string;
1004
+ readonly id?: string | undefined;
1005
+ readonly name?: string | undefined;
1006
+ readonly arguments?: string | undefined;
1007
+ readonly encrypted_content?: string | null | undefined;
1008
+ readonly call_id?: string | undefined;
1009
+ }[] | undefined;
989
1010
  readonly error?: {
990
1011
  readonly type?: string | null | undefined;
991
1012
  readonly code?: string | null | undefined;
@@ -1009,9 +1030,11 @@ export declare const protocol: Protocol<{
1009
1030
  readonly reason?: string | undefined;
1010
1031
  } | null | undefined;
1011
1032
  } | undefined;
1033
+ readonly arguments?: string | undefined;
1012
1034
  readonly param?: string | null | undefined;
1013
1035
  readonly status_code?: unknown;
1014
1036
  readonly item_id?: string | undefined;
1037
+ readonly output_index?: number | undefined;
1015
1038
  readonly summary_index?: number | undefined;
1016
1039
  }, ParserState>;
1017
1040
  export declare const httpTransport: HttpTransport.HttpJsonTransport<{
@@ -204,14 +204,17 @@ export const decodeKnownErrorEvent = (event) => decodeWebSocketErrorEvent({
204
204
  export const Event = Schema.StructWithRest(Schema.Struct({
205
205
  type: Schema.String,
206
206
  delta: Schema.optional(Schema.String),
207
+ arguments: Schema.optional(Schema.String),
207
208
  text: Schema.optional(Schema.String),
208
209
  item_id: Schema.optional(Schema.String),
210
+ output_index: Schema.optional(Schema.Number),
209
211
  summary_index: Schema.optional(Schema.Number),
210
212
  item: Schema.optional(StreamItem),
211
213
  response: Schema.optional(Schema.StructWithRest(Schema.Struct({
212
214
  id: Schema.optional(Schema.String),
213
215
  service_tier: optionalNull(Schema.String),
214
216
  incomplete_details: optionalNull(Schema.Struct({ reason: Schema.optional(Schema.String) })),
217
+ output: Schema.optional(Schema.Array(StreamItem)),
215
218
  usage: optionalNull(OpenResponsesUsage),
216
219
  error: optionalNull(OpenResponsesErrorPayload),
217
220
  }), [Schema.Record(Schema.String, Schema.Unknown)])),
@@ -461,16 +464,11 @@ const lowerMessages = Effect.fn("OpenResponses.lowerMessages")(function* (reques
461
464
  });
462
465
  }
463
466
  }
464
- // With store:false, Responses APIs only accept previous reasoning items when the
465
- // complete item has encrypted state. Summary blocks for one item may carry
466
- // that state only on the last block, so filter after they have been joined.
467
- return store === false
468
- ? input.filter((item) => !("type" in item) || item.type !== "reasoning" || typeof item.encrypted_content === "string")
469
- : input;
467
+ return input;
470
468
  });
471
469
  const lowerOptions = (request) => {
472
470
  const options = OpenResponsesOptions.resolve(request);
473
- const cacheKey = ProviderShared.clampPromptCacheKey(request.promptCacheKey);
471
+ const cacheKey = ProviderShared.promptCacheKey(request);
474
472
  const parallelToolCalls = resolveParallelToolCalls(request);
475
473
  return {
476
474
  ...(options.instructions ? { instructions: options.instructions } : {}),
@@ -602,6 +600,7 @@ const onOutputTextDone = (state, event, id) => {
602
600
  const events = [];
603
601
  return [{ ...state, lifecycle: Lifecycle.textEnd(state.lifecycle, events, id) }, events];
604
602
  };
603
+ export const outputItemID = (state, event) => event.output_index === undefined ? event.item_id : (state.outputItems[event.output_index] ?? event.item_id);
605
604
  export const onReasoningDelta = (state, event, itemID) => {
606
605
  const item = state.reasoningItems[itemID];
607
606
  if (!event.delta || !item)
@@ -756,9 +755,23 @@ const onReasoningSummaryPartDone = (state, event) => {
756
755
  ];
757
756
  };
758
757
  const onFunctionCallArgumentsDelta = Effect.fn("OpenResponses.onFunctionCallArgumentsDelta")(function* (state, event) {
759
- if (!event.item_id || !event.delta || !state.tools[event.item_id])
758
+ if (!event.item_id)
759
+ return [state, NO_EVENTS];
760
+ const tool = state.tools[event.item_id];
761
+ if (!tool)
760
762
  return [state, NO_EVENTS];
761
- const result = ToolStream.appendExisting(state.id, state.tools, event.item_id, event.delta, `${state.name} tool argument delta is missing its tool call`);
763
+ const final = event.type === "response.function_call_arguments.done" ? event.arguments : undefined;
764
+ if (event.type === "response.function_call_arguments.done" && final === undefined)
765
+ return [state, NO_EVENTS];
766
+ if (final !== undefined && !final.startsWith(tool.input))
767
+ return [
768
+ { ...state, tools: ToolStream.start(state.tools, event.item_id, { ...tool, input: final }) },
769
+ NO_EVENTS,
770
+ ];
771
+ const delta = final === undefined ? event.delta : final.slice(tool.input.length);
772
+ if (!delta)
773
+ return [state, NO_EVENTS];
774
+ const result = ToolStream.appendExisting(state.id, state.tools, event.item_id, delta, `${state.name} tool argument delta is missing its tool call`);
762
775
  if (ToolStream.isError(result))
763
776
  return yield* result;
764
777
  const events = [];
@@ -840,27 +853,37 @@ const onOutputItemDone = Effect.fn("OpenResponses.onOutputItemDone")(function* (
840
853
  return [state, NO_EVENTS];
841
854
  });
842
855
  const onResponseFinish = Effect.fn("OpenResponses.onResponseFinish")(function* (state, event) {
856
+ const reconciled = event.type === "response.completed"
857
+ ? yield* Effect.reduce(event.response?.output ?? [], () => [state, NO_EVENTS], ([current, events], item) => {
858
+ if (!item.id ||
859
+ ((item.type !== "function_call" || !current.tools[item.id]) &&
860
+ (item.type !== "reasoning" || !current.reasoningItems[item.id])))
861
+ return Effect.succeed([current, events]);
862
+ return onOutputItemDone(current, { type: "response.output_item.done", item }).pipe(Effect.map(([next, emitted]) => [next, [...events, ...emitted]]));
863
+ })
864
+ : [state, NO_EVENTS];
865
+ const current = reconciled[0];
843
866
  // Some compatible providers omit output_item.done even after completing the response.
844
867
  const pending = event.type === "response.completed"
845
- ? yield* ToolStream.finishAll(state.id, state.tools)
846
- : { tools: state.tools, events: NO_EVENTS };
847
- const events = [...pending.events];
868
+ ? yield* ToolStream.finishAll(current.id, current.tools)
869
+ : { tools: current.tools, events: NO_EVENTS };
870
+ const events = [...reconciled[1], ...pending.events];
848
871
  const hasFunctionCall = pending.events.some((event) => LLMEvent.is.toolCall(event) || LLMEvent.is.toolInputError(event)) ||
849
- state.hasFunctionCall;
850
- const lifecycle = Lifecycle.finish(state.lifecycle, events, {
872
+ current.hasFunctionCall;
873
+ const lifecycle = Lifecycle.finish(current.lifecycle, events, {
851
874
  reason: {
852
875
  normalized: mapFinishReason(event, hasFunctionCall),
853
876
  raw: event.response?.incomplete_details?.reason,
854
877
  },
855
- usage: mapUsage(event.response?.usage, state.providerMetadataKey),
878
+ usage: mapUsage(event.response?.usage, current.providerMetadataKey),
856
879
  providerMetadata: event.response?.id || event.response?.service_tier
857
- ? providerMetadata(state, {
880
+ ? providerMetadata(current, {
858
881
  responseId: event.response.id,
859
882
  serviceTier: event.response.service_tier,
860
883
  })
861
884
  : undefined,
862
885
  });
863
- return [{ ...state, lifecycle, hasFunctionCall, tools: pending.tools }, events];
886
+ return [{ ...current, lifecycle, hasFunctionCall, tools: pending.tools }, events];
864
887
  });
865
888
  // Build the prettiest summary available from whatever the provider supplied.
866
889
  // When both code and message are present, prefix the code so consumers see
@@ -902,7 +925,10 @@ export const providerFailure = (id, event, fallback) => {
902
925
  });
903
926
  };
904
927
  const providerError = (state, event, fallback) => providerFailure(state.id, event, fallback);
905
- export const step = (state, event) => {
928
+ export const step = (state, input) => {
929
+ const event = input.item_id && outputItemID(state, input) !== input.item_id
930
+ ? { ...input, item_id: outputItemID(state, input) }
931
+ : input;
906
932
  if (event.type === "response.output_text.delta" || event.type === "response.output_text.done") {
907
933
  if (!event.item_id)
908
934
  return ProviderShared.eventError(state.id, `${event.type} is missing item_id`);
@@ -941,9 +967,11 @@ export const step = (state, event) => {
941
967
  if (event.type === "response.output_item.added") {
942
968
  if (event.item?.type === "message" && !event.item.id)
943
969
  return ProviderShared.eventError(state.id, `${event.type} message is missing id`);
944
- return Effect.succeed(onOutputItemAdded(state, event));
970
+ return Effect.succeed(onOutputItemAdded(event.output_index !== undefined && event.item?.id
971
+ ? { ...state, outputItems: { ...state.outputItems, [event.output_index]: event.item.id } }
972
+ : state, event));
945
973
  }
946
- if (event.type === "response.function_call_arguments.delta")
974
+ if (event.type === "response.function_call_arguments.delta" || event.type === "response.function_call_arguments.done")
947
975
  return event.item_id
948
976
  ? onFunctionCallArgumentsDelta(state, event)
949
977
  : ProviderShared.eventError(state.id, `${event.type} is missing item_id`);
@@ -974,6 +1002,7 @@ export const initial = (request, extension = BASE) => ({
974
1002
  hasFunctionCall: false,
975
1003
  tools: ToolStream.empty(),
976
1004
  lifecycle: Lifecycle.initial(),
1005
+ outputItems: {},
977
1006
  messageItems: new Set(),
978
1007
  messagePhases: {},
979
1008
  reasoningItems: {},
@@ -204,7 +204,7 @@ declare const OpenAIChatBody: Schema.Struct<{
204
204
  stop: Schema.optional<Schema.$Array<Schema.String>>;
205
205
  }>;
206
206
  export type OpenAIChatBody = Schema.Schema.Type<typeof OpenAIChatBody>;
207
- export declare const OpenAIChatEvent: Schema.Struct<{
207
+ export declare const OpenAIChatEvent: Schema.StructWithRest<Schema.Struct<{
208
208
  readonly choices: Schema.optional<Schema.NullOr<Schema.$Array<Schema.StructWithRest<Schema.Struct<{
209
209
  readonly delta: Schema.optional<Schema.NullOr<Schema.StructWithRest<Schema.Struct<{
210
210
  readonly content: Schema.optional<Schema.NullOr<Schema.String>>;
@@ -257,11 +257,11 @@ export declare const OpenAIChatEvent: Schema.Struct<{
257
257
  readonly rejected_prediction_tokens: Schema.optional<Schema.NullOr<Schema.Number>>;
258
258
  }>, readonly [Schema.$Record<Schema.String, Schema.Unknown>]>>>;
259
259
  }>, readonly [Schema.$Record<Schema.String, Schema.Unknown>]>>>;
260
- readonly error: Schema.optional<Schema.NullOr<Schema.Struct<{
260
+ readonly error: Schema.optional<Schema.NullOr<Schema.StructWithRest<Schema.Struct<{
261
261
  readonly code: Schema.optional<Schema.NullOr<Schema.Union<readonly [Schema.String, Schema.Number]>>>;
262
262
  readonly message: Schema.String;
263
- }>>>;
264
- }>;
263
+ }>, readonly [Schema.$Record<Schema.String, Schema.Unknown>]>>>;
264
+ }>, readonly [Schema.$Record<Schema.String, Schema.Unknown>]>;
265
265
  export type OpenAIChatEvent = Schema.Schema.Type<typeof OpenAIChatEvent>;
266
266
  interface PendingToolDelta {
267
267
  readonly id?: string;
@@ -281,6 +281,7 @@ export interface ParserState {
281
281
  readonly reasoningEmitted: boolean;
282
282
  readonly latestToolIndex?: number;
283
283
  readonly nextToolIndex: number;
284
+ readonly requireFinishReason: boolean;
284
285
  }
285
286
  interface LoweringOptions {
286
287
  readonly cacheControl?: (cache: CacheHint | undefined) => Schema.Schema.Type<typeof OpenAIChatCacheControl> | undefined;
@@ -585,7 +586,9 @@ export declare const protocol: Protocol<{
585
586
  readonly frequency_penalty?: number | undefined;
586
587
  readonly presence_penalty?: number | undefined;
587
588
  }, string, {
589
+ readonly [x: string]: unknown;
588
590
  readonly error?: {
591
+ readonly [x: string]: unknown;
589
592
  readonly message: string;
590
593
  readonly code?: string | number | null | undefined;
591
594
  } | null | undefined;