@opencode-ai/ai 0.0.0-dev-18158 → 0.0.0-dev-18164

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.
@@ -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")
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-18158",
3
+ "version": "0.0.0-dev-18164",
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.111",
33
- "@opencode-ai/http-recorder": "0.0.0-dev-18158",
33
+ "@opencode-ai/http-recorder": "0.0.0-dev-18164",
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-18158",
42
+ "@opencode-ai/schema": "0.0.0-dev-18164",
43
43
  "aws4fetch": "1.0.20",
44
44
  "effect": "4.0.0-rc.111",
45
45
  "google-auth-library": "10.5.0"