@opencode/ai 0.0.0-dev-20536 → 0.0.0-dev-20537
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/dist/protocols/anthropic-messages.js +4 -4
- package/dist/protocols/bedrock-converse.js +3 -3
- package/dist/protocols/gemini.js +2 -2
- package/dist/protocols/google-interactions.js +3 -3
- package/dist/protocols/mistral-chat.js +5 -5
- package/dist/protocols/open-responses.js +10 -6
- package/dist/protocols/openai-chat.js +6 -6
- package/dist/protocols/openai-responses.js +2 -2
- package/dist/protocols/shared.js +2 -2
- package/dist/protocols/utils/bedrock-media.js +2 -2
- package/dist/route/framing.js +23 -23
- package/package.json +3 -3
|
@@ -462,7 +462,7 @@ const serverToolResultType = (name) => {
|
|
|
462
462
|
return "web_fetch_tool_result";
|
|
463
463
|
return undefined;
|
|
464
464
|
};
|
|
465
|
-
const lowerServerToolResult = Effect.
|
|
465
|
+
const lowerServerToolResult = Effect.fnUntraced(function* (part, providerMetadataKey) {
|
|
466
466
|
const wireType = serverToolResultType(part.name);
|
|
467
467
|
if (!wireType)
|
|
468
468
|
return yield* invalid(`Anthropic Messages does not know how to round-trip server tool result for ${part.name}`);
|
|
@@ -538,7 +538,7 @@ const citationsFromMetadata = (metadata) => {
|
|
|
538
538
|
return undefined;
|
|
539
539
|
};
|
|
540
540
|
const isHttpUrl = (value) => /^https?:\/\//i.test(value.trim());
|
|
541
|
-
const lowerMedia = Effect.
|
|
541
|
+
const lowerMedia = Effect.fnUntraced(function* (part, breakpoints) {
|
|
542
542
|
const mime = part.media.mediaType.toLowerCase();
|
|
543
543
|
const cacheControlValue = breakpoints ? cacheControl(breakpoints, part.cache) : undefined;
|
|
544
544
|
const fileId = fileIdFromMetadata(part.metadata);
|
|
@@ -716,7 +716,7 @@ const splitsLocalToolResults = (messages, index) => {
|
|
|
716
716
|
}
|
|
717
717
|
return pending.size > 0;
|
|
718
718
|
};
|
|
719
|
-
const lowerNativeSystemUpdate = Effect.
|
|
719
|
+
const lowerNativeSystemUpdate = Effect.fnUntraced(function* (message, breakpoints) {
|
|
720
720
|
const content = yield* ProviderShared.systemUpdateText("Anthropic Messages", message);
|
|
721
721
|
return {
|
|
722
722
|
role: "system",
|
|
@@ -727,7 +727,7 @@ const lowerNativeSystemUpdate = Effect.fn("AnthropicMessages.lowerNativeSystemUp
|
|
|
727
727
|
})),
|
|
728
728
|
};
|
|
729
729
|
});
|
|
730
|
-
const lowerMessages = Effect.
|
|
730
|
+
const lowerMessages = Effect.fnUntraced(function* (request, breakpoints) {
|
|
731
731
|
const messages = [];
|
|
732
732
|
const providerMetadataKey = request.model.route.providerMetadataKey ?? String(request.model.provider);
|
|
733
733
|
for (const [index, message] of request.messages.entries()) {
|
|
@@ -211,7 +211,7 @@ const lowerToolCall = (part, normalizeID) => ({
|
|
|
211
211
|
input: removeEmptyToolInputKeys(part.input),
|
|
212
212
|
},
|
|
213
213
|
});
|
|
214
|
-
const lowerToolResultContent = Effect.
|
|
214
|
+
const lowerToolResultContent = Effect.fnUntraced(function* (part, documentNames) {
|
|
215
215
|
if (part.result.type === "text" || part.result.type === "error")
|
|
216
216
|
return [{ text: ProviderShared.toolResultText(part) }];
|
|
217
217
|
if (part.result.type === "json")
|
|
@@ -227,7 +227,7 @@ const lowerToolResultContent = Effect.fn("BedrockConverse.lowerToolResultContent
|
|
|
227
227
|
}
|
|
228
228
|
return content;
|
|
229
229
|
});
|
|
230
|
-
const lowerToolResult = Effect.
|
|
230
|
+
const lowerToolResult = Effect.fnUntraced(function* (part, documentNames, normalizeID) {
|
|
231
231
|
return {
|
|
232
232
|
toolResult: {
|
|
233
233
|
toolUseId: normalizeID(part.id),
|
|
@@ -238,7 +238,7 @@ const lowerToolResult = Effect.fn("BedrockConverse.lowerToolResult")(function* (
|
|
|
238
238
|
});
|
|
239
239
|
// Keep Claude and Nova tool-result images inline; put other models' images beside the result.
|
|
240
240
|
const keepToolImagesInline = (id) => id.includes("anthropic.claude-") || id.includes("amazon.nova-");
|
|
241
|
-
const lowerMessages = Effect.
|
|
241
|
+
const lowerMessages = Effect.fnUntraced(function* (request, breakpoints) {
|
|
242
242
|
const messages = [];
|
|
243
243
|
const documentNames = new Set();
|
|
244
244
|
// Mistral can reject replay IDs even when they satisfy Converse's broader ID syntax.
|
package/dist/protocols/gemini.js
CHANGED
|
@@ -201,7 +201,7 @@ const lowerToolConfig = (toolChoice) => ProviderShared.matchToolChoice("Gemini",
|
|
|
201
201
|
required: () => ({ functionCallingConfig: { mode: "ANY" } }),
|
|
202
202
|
tool: (name) => ({ functionCallingConfig: { mode: "ANY", allowedFunctionNames: [name] } }),
|
|
203
203
|
});
|
|
204
|
-
const lowerContentPart = Effect.
|
|
204
|
+
const lowerContentPart = Effect.fnUntraced(function* (part) {
|
|
205
205
|
if (part.type === "text")
|
|
206
206
|
return { text: part.text };
|
|
207
207
|
return yield* GeminiGenerateContent.mediaPart("Gemini", part.media);
|
|
@@ -217,7 +217,7 @@ const lowerToolCall = (part, omitIds, metadataKey) => ({
|
|
|
217
217
|
functionCall: { ...(omitIds ? {} : { id: part.id }), name: part.name, args: part.input },
|
|
218
218
|
thoughtSignature: thoughtSignature(part.providerMetadata, metadataKey),
|
|
219
219
|
});
|
|
220
|
-
const lowerMessages = Effect.
|
|
220
|
+
const lowerMessages = Effect.fnUntraced(function* (request) {
|
|
221
221
|
const contents = [];
|
|
222
222
|
const metadataKey = request.model.route.providerMetadataKey ?? String(request.model.provider);
|
|
223
223
|
const omitCallIds = omitsFunctionCallIds(request.model.id);
|
|
@@ -140,7 +140,7 @@ const Event = Schema.Union([
|
|
|
140
140
|
// =============================================================================
|
|
141
141
|
// Request Body Construction
|
|
142
142
|
// =============================================================================
|
|
143
|
-
const mediaContent = Effect.
|
|
143
|
+
const mediaContent = Effect.fnUntraced(function* (asset) {
|
|
144
144
|
if (asset.kind !== "image" &&
|
|
145
145
|
asset.kind !== "audio" &&
|
|
146
146
|
asset.kind !== "video" &&
|
|
@@ -160,7 +160,7 @@ const signature = (metadata, key) => {
|
|
|
160
160
|
? value.interactionSignature
|
|
161
161
|
: undefined;
|
|
162
162
|
};
|
|
163
|
-
const lowerMessages = Effect.
|
|
163
|
+
const lowerMessages = Effect.fnUntraced(function* (request) {
|
|
164
164
|
const steps = [];
|
|
165
165
|
const key = request.model.route.providerMetadataKey ?? String(request.model.provider);
|
|
166
166
|
for (const message of request.messages) {
|
|
@@ -223,7 +223,7 @@ const lowerMessages = Effect.fn("GoogleInteractions.lowerMessages")(function* (r
|
|
|
223
223
|
}
|
|
224
224
|
return steps;
|
|
225
225
|
});
|
|
226
|
-
const lowerToolResult = Effect.
|
|
226
|
+
const lowerToolResult = Effect.fnUntraced(function* (part) {
|
|
227
227
|
if (part.result.type === "json" && ProviderShared.isRecord(part.result.value))
|
|
228
228
|
return part.result.value;
|
|
229
229
|
if (part.result.type !== "content")
|
|
@@ -143,7 +143,7 @@ const MistralEvent = Schema.StructWithRest(Schema.Struct({
|
|
|
143
143
|
error: optionalNull(MistralError),
|
|
144
144
|
}), [Schema.Record(Schema.String, Schema.Unknown)]);
|
|
145
145
|
const MistralStreamEvent = Schema.Union([Schema.Literal(DONE), Protocol.jsonEvent(MistralEvent)]);
|
|
146
|
-
const lowerMedia = Effect.
|
|
146
|
+
const lowerMedia = Effect.fnUntraced(function* (part) {
|
|
147
147
|
const mime = part.media.mediaType.toLowerCase();
|
|
148
148
|
const url = ProviderShared.mediaUrl(part.media) ??
|
|
149
149
|
(yield* ProviderShared.requireInlineMedia("Mistral Chat", part.media)).dataUrl;
|
|
@@ -153,7 +153,7 @@ const lowerMedia = Effect.fn("MistralChat.lowerMedia")(function* (part) {
|
|
|
153
153
|
return { type: "document_url", document_url: url };
|
|
154
154
|
return yield* ProviderShared.invalidRequest(`Mistral Chat does not support media type ${part.media.mediaType}`);
|
|
155
155
|
});
|
|
156
|
-
const lowerUser = Effect.
|
|
156
|
+
const lowerUser = Effect.fnUntraced(function* (message) {
|
|
157
157
|
const content = [];
|
|
158
158
|
for (const part of message.content) {
|
|
159
159
|
if (part.type === "text") {
|
|
@@ -175,7 +175,7 @@ const lowerToolCall = (part, normalizeID) => ({
|
|
|
175
175
|
type: "function",
|
|
176
176
|
function: { name: part.name, arguments: ProviderShared.encodeJson(part.input) },
|
|
177
177
|
});
|
|
178
|
-
const lowerAssistant = Effect.
|
|
178
|
+
const lowerAssistant = Effect.fnUntraced(function* (message, normalizeID, prefix) {
|
|
179
179
|
const structured = message.content.some((part) => part.type === "reasoning" && isMistralThinkingContent(part.providerMetadata?.mistral?.thinking));
|
|
180
180
|
const content = [];
|
|
181
181
|
const text = [];
|
|
@@ -211,7 +211,7 @@ const lowerAssistant = Effect.fn("MistralChat.lowerAssistant")(function* (messag
|
|
|
211
211
|
...(prefix ? { prefix: true } : {}),
|
|
212
212
|
};
|
|
213
213
|
});
|
|
214
|
-
const lowerToolResults = Effect.
|
|
214
|
+
const lowerToolResults = Effect.fnUntraced(function* (message, normalizeID) {
|
|
215
215
|
const output = [];
|
|
216
216
|
for (const part of message.content) {
|
|
217
217
|
if (part.type !== "tool-result")
|
|
@@ -244,7 +244,7 @@ const lowerToolResults = Effect.fn("MistralChat.lowerToolResults")(function* (me
|
|
|
244
244
|
}
|
|
245
245
|
return output;
|
|
246
246
|
});
|
|
247
|
-
const lowerMessages = Effect.
|
|
247
|
+
const lowerMessages = Effect.fnUntraced(function* (request) {
|
|
248
248
|
const normalizeID = MistralToolID.normalizer(request);
|
|
249
249
|
const messages = request.system.length === 0
|
|
250
250
|
? []
|
|
@@ -289,7 +289,7 @@ const BASE_ADAPTER = { id: ADAPTER, name: NAME };
|
|
|
289
289
|
// =============================================================================
|
|
290
290
|
// Request Lowering
|
|
291
291
|
// =============================================================================
|
|
292
|
-
export const lowerTool = Effect.
|
|
292
|
+
export const lowerTool = Effect.fnUntraced(function* (protocolName, tool) {
|
|
293
293
|
if (tool.native !== undefined)
|
|
294
294
|
return yield* ProviderShared.invalidRequest(`${protocolName} does not support provider-native tool ${tool.name}`);
|
|
295
295
|
return {
|
|
@@ -343,12 +343,15 @@ const lowerReasoning = (part, providerMetadataKey) => {
|
|
|
343
343
|
encrypted_content: encryptedContent,
|
|
344
344
|
};
|
|
345
345
|
};
|
|
346
|
-
const
|
|
346
|
+
const decodeImageDetail = ProviderShared.validateWith(Schema.decodeUnknownEffect(OpenResponsesInputImage.fields.detail));
|
|
347
|
+
const decodeMessageMetadata = ProviderShared.validateWith(Schema.decodeUnknownEffect(MessageMetadata));
|
|
348
|
+
const lowerMedia = Effect.fnUntraced(function* (part, request, adapter, target) {
|
|
347
349
|
const media = part.media.inline();
|
|
348
350
|
const providerMedia = adapter.lowerMedia?.({ part, media, request });
|
|
349
351
|
if (providerMedia)
|
|
350
352
|
return providerMedia;
|
|
351
|
-
const
|
|
353
|
+
const rawDetail = part.providerMetadata?.[metadataKey(request.model)]?.detail;
|
|
354
|
+
const detail = rawDetail === undefined ? undefined : yield* decodeImageDetail(rawDetail);
|
|
352
355
|
const mime = part.media.mediaType.toLowerCase();
|
|
353
356
|
const url = ProviderShared.mediaUrl(part.media);
|
|
354
357
|
const location = url ?? (yield* ProviderShared.requireInlineMedia(adapter.name, part.media)).dataUrl;
|
|
@@ -403,11 +406,12 @@ const lowerToolResultOutput = Effect.fnUntraced(function* (part, request, adapte
|
|
|
403
406
|
return yield* Effect.forEach(content, (item) => lowerToolResultContentItem(item, request, adapter));
|
|
404
407
|
});
|
|
405
408
|
const DEFAULT_EFFORT = "medium";
|
|
406
|
-
const lowerMessages = Effect.
|
|
409
|
+
const lowerMessages = Effect.fnUntraced(function* (request, adapter) {
|
|
407
410
|
const input = [];
|
|
408
411
|
const providerMetadataKey = metadataKey(request.model);
|
|
409
412
|
for (const message of request.messages) {
|
|
410
|
-
const
|
|
413
|
+
const rawMetadata = message.providerMetadata?.[providerMetadataKey];
|
|
414
|
+
const metadata = rawMetadata === undefined ? undefined : yield* decodeMessageMetadata(rawMetadata);
|
|
411
415
|
if (message.role === "system") {
|
|
412
416
|
const update = effortUpdate(message);
|
|
413
417
|
if (update) {
|
|
@@ -555,7 +559,7 @@ const lowerMessages = Effect.fn("OpenResponses.lowerMessages")(function* (reques
|
|
|
555
559
|
}
|
|
556
560
|
return input;
|
|
557
561
|
});
|
|
558
|
-
export const lowerConversation = Effect.
|
|
562
|
+
export const lowerConversation = Effect.fnUntraced(function* (request, adapter) {
|
|
559
563
|
const instructions = ProviderShared.joinText(request.system);
|
|
560
564
|
return {
|
|
561
565
|
model: request.model.id,
|
|
@@ -233,7 +233,7 @@ const lowerToolCall = (part, options) => ({
|
|
|
233
233
|
},
|
|
234
234
|
extra_content: decodeExtraContent(part.providerMetadata?.[options.providerMetadataKey]?.extraContent),
|
|
235
235
|
});
|
|
236
|
-
const lowerMedia = Effect.
|
|
236
|
+
const lowerMedia = Effect.fnUntraced(function* (part) {
|
|
237
237
|
// Chat Completions accepts PDFs, and no other documents, as inline `file` parts; file URLs are not supported.
|
|
238
238
|
if (part.media.mediaType.toLowerCase() === "application/pdf")
|
|
239
239
|
return {
|
|
@@ -273,7 +273,7 @@ const lowerReasoningDetail = (detail) => {
|
|
|
273
273
|
return detail;
|
|
274
274
|
};
|
|
275
275
|
const isKimiDetail = (detail) => detail.type === "summary" || detail.type === "encrypted";
|
|
276
|
-
const lowerUserMessage = Effect.
|
|
276
|
+
const lowerUserMessage = Effect.fnUntraced(function* (message, options) {
|
|
277
277
|
const content = [];
|
|
278
278
|
for (const part of message.content) {
|
|
279
279
|
if (part.type === "text") {
|
|
@@ -293,7 +293,7 @@ const lowerUserMessage = Effect.fn("OpenAIChat.lowerUserMessage")(function* (mes
|
|
|
293
293
|
};
|
|
294
294
|
return { role: "user", content };
|
|
295
295
|
});
|
|
296
|
-
const lowerAssistantMessage = Effect.
|
|
296
|
+
const lowerAssistantMessage = Effect.fnUntraced(function* (message, configuredField, requireReasoning, options) {
|
|
297
297
|
const content = [];
|
|
298
298
|
const reasoning = [];
|
|
299
299
|
const toolCalls = [];
|
|
@@ -359,7 +359,7 @@ const lowerAssistantMessage = Effect.fn("OpenAIChat.lowerAssistantMessage")(func
|
|
|
359
359
|
return result;
|
|
360
360
|
return { ...result, [field]: reasoningText };
|
|
361
361
|
});
|
|
362
|
-
const lowerToolMessages = Effect.
|
|
362
|
+
const lowerToolMessages = Effect.fnUntraced(function* (message, options) {
|
|
363
363
|
const messages = [];
|
|
364
364
|
const attachments = [];
|
|
365
365
|
for (const part of message.content) {
|
|
@@ -383,14 +383,14 @@ const toolMessage = (toolCallID, text, cacheControl) => ({
|
|
|
383
383
|
tool_call_id: toolCallID,
|
|
384
384
|
content: cacheControl === undefined ? text : [{ type: "text", text, cache_control: cacheControl }],
|
|
385
385
|
});
|
|
386
|
-
const lowerMessage = Effect.
|
|
386
|
+
const lowerMessage = Effect.fnUntraced(function* (message, reasoningField, requireReasoning, options) {
|
|
387
387
|
if (message.role === "user")
|
|
388
388
|
return [yield* lowerUserMessage(message, options)];
|
|
389
389
|
if (message.role === "assistant")
|
|
390
390
|
return [yield* lowerAssistantMessage(message, reasoningField, requireReasoning, options)];
|
|
391
391
|
return (yield* lowerToolMessages(message, options)).messages;
|
|
392
392
|
});
|
|
393
|
-
const lowerMessages = Effect.
|
|
393
|
+
const lowerMessages = Effect.fnUntraced(function* (request, options) {
|
|
394
394
|
const system = request.system.length === 0
|
|
395
395
|
? []
|
|
396
396
|
: request.system.some((part) => part.cache !== undefined) && options.cacheControl !== undefined
|
|
@@ -126,7 +126,7 @@ const nativeImageTool = (tool) => {
|
|
|
126
126
|
const native = nativeImageToolInput(tool);
|
|
127
127
|
return Schema.is(OpenAIResponsesImageGenerationTool)(native) ? native : undefined;
|
|
128
128
|
};
|
|
129
|
-
const lowerTool = Effect.
|
|
129
|
+
const lowerTool = Effect.fnUntraced(function* (tool) {
|
|
130
130
|
const native = nativeImageToolInput(tool);
|
|
131
131
|
if (native !== undefined) {
|
|
132
132
|
if (Schema.is(OpenAIResponsesImageGenerationTool)(native))
|
|
@@ -137,7 +137,7 @@ const lowerTool = Effect.fn("OpenAIResponses.lowerTool")(function* (tool) {
|
|
|
137
137
|
});
|
|
138
138
|
// Native namespaces hold only function tools, so deeper levels flatten into
|
|
139
139
|
// the leaf names the same way non-native protocols flatten the whole tree.
|
|
140
|
-
const lowerToolEntry = Effect.
|
|
140
|
+
const lowerToolEntry = Effect.fnUntraced(function* (tool) {
|
|
141
141
|
if (tool.type === "tool")
|
|
142
142
|
return yield* lowerTool(tool);
|
|
143
143
|
// OpenAI requires a namespace description; fall back to a generic one so a
|
package/dist/protocols/shared.js
CHANGED
|
@@ -108,7 +108,7 @@ export const wrapSystemUpdate = (parts) => `<system-update>\n${escapeSystemUpdat
|
|
|
108
108
|
* raw retrieved, tool, or web content into privileged updates: keep untrusted
|
|
109
109
|
* data in ordinary user/tool messages instead.
|
|
110
110
|
*/
|
|
111
|
-
export const systemUpdateText = Effect.
|
|
111
|
+
export const systemUpdateText = Effect.fnUntraced(function* (route, message) {
|
|
112
112
|
const content = [];
|
|
113
113
|
for (const part of message.content) {
|
|
114
114
|
if (!supportsContent(part, ["text"]))
|
|
@@ -118,7 +118,7 @@ export const systemUpdateText = Effect.fn("ProviderShared.systemUpdateText")(fun
|
|
|
118
118
|
return content;
|
|
119
119
|
});
|
|
120
120
|
/** Lower an unsupported privileged update into visible, in-order user text. */
|
|
121
|
-
export const wrappedSystemUpdate = Effect.
|
|
121
|
+
export const wrappedSystemUpdate = Effect.fnUntraced(function* (route, message) {
|
|
122
122
|
const content = yield* systemUpdateText(route, message);
|
|
123
123
|
return { type: "text", text: wrapSystemUpdate(content), cache: content.at(-1)?.cache };
|
|
124
124
|
});
|
|
@@ -61,7 +61,7 @@ function documentName(filename, names) {
|
|
|
61
61
|
names.add(name);
|
|
62
62
|
return name;
|
|
63
63
|
}
|
|
64
|
-
const mediaBase64 = Effect.
|
|
64
|
+
const mediaBase64 = Effect.fnUntraced(function* (part) {
|
|
65
65
|
const media = yield* ProviderShared.requireInlineMedia("Bedrock Converse", part.media);
|
|
66
66
|
const bytes = yield* Effect.fromResult(Encoding.decodeBase64(media.base64)).pipe(Effect.mapError((cause) => ProviderShared.invalidRequest("Bedrock Converse media data must be valid base64", cause)));
|
|
67
67
|
return Encoding.encodeBase64(bytes);
|
|
@@ -71,7 +71,7 @@ const mediaBase64 = Effect.fn("BedrockMedia.mediaBase64")(function* (part) {
|
|
|
71
71
|
// document block. Image MIME types not in `IMAGE_FORMATS` (e.g. `image/svg+xml`)
|
|
72
72
|
// get an image-specific error so the caller knows it's a format-support issue,
|
|
73
73
|
// not a kind-detection issue.
|
|
74
|
-
export const lower = Effect.
|
|
74
|
+
export const lower = Effect.fnUntraced(function* (part, documentNames) {
|
|
75
75
|
const mime = part.media.mediaType.toLowerCase();
|
|
76
76
|
const imageFormat = IMAGE_FORMATS[mime];
|
|
77
77
|
if (imageFormat) {
|
package/dist/route/framing.js
CHANGED
|
@@ -14,32 +14,32 @@ export const sseFraming = (bytes, events, includeDone = false) => bytes.pipe(Str
|
|
|
14
14
|
return {
|
|
15
15
|
output,
|
|
16
16
|
parser: makeParser((event) => {
|
|
17
|
-
if (event._tag === "Event"
|
|
18
|
-
|
|
17
|
+
if (event._tag === "Event" &&
|
|
18
|
+
(events === undefined || events.has(event.event)) &&
|
|
19
|
+
event.data.length > 0 &&
|
|
20
|
+
// Some OpenAI-compatible proxies serialize an empty flush as a bare
|
|
21
|
+
// `data: null`, between events or after `[DONE]`. No protocol has a
|
|
22
|
+
// null event, so it carries nothing and must not abort the stream.
|
|
23
|
+
event.data !== "null" &&
|
|
24
|
+
// Vertex AI partner models (e.g. `xai/grok-4.6`) send their SSE
|
|
25
|
+
// keepalive comment as `data: : keepalive` while reasoning.
|
|
26
|
+
event.data !== ": keepalive" &&
|
|
27
|
+
(event.data !== "[DONE]" || includeDone || (events !== undefined && event.event !== "message")))
|
|
28
|
+
output.push(event.data);
|
|
19
29
|
}),
|
|
20
30
|
};
|
|
21
|
-
}, (state, chunk) =>
|
|
31
|
+
}, (state, chunk) => {
|
|
22
32
|
const error = state.parser.feed(chunk);
|
|
23
|
-
if (error)
|
|
24
|
-
return
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
})), Stream.filter((event) => (events === undefined || events.has(event.event)) &&
|
|
34
|
-
event.data.length > 0 &&
|
|
35
|
-
// Some OpenAI-compatible proxies serialize an empty flush as a bare
|
|
36
|
-
// `data: null`, between events or after `[DONE]`. No protocol has a
|
|
37
|
-
// null event, so it carries nothing and must not abort the stream.
|
|
38
|
-
event.data !== "null" &&
|
|
39
|
-
// Vertex AI partner models (e.g. `xai/grok-4.6`) send their SSE
|
|
40
|
-
// keepalive comment as `data: : keepalive` while reasoning.
|
|
41
|
-
event.data !== ": keepalive" &&
|
|
42
|
-
(event.data !== "[DONE]" || includeDone || (events !== undefined && event.event !== "message"))), Stream.map((event) => event.data));
|
|
33
|
+
if (!error)
|
|
34
|
+
return Effect.succeed([state, state.output.splice(0)]);
|
|
35
|
+
const reason = new InvalidProviderOutputError({
|
|
36
|
+
route: "sse",
|
|
37
|
+
message: error.message,
|
|
38
|
+
body: chunk,
|
|
39
|
+
cause: error,
|
|
40
|
+
});
|
|
41
|
+
return Effect.fail(new AIError({ reason }));
|
|
42
|
+
}));
|
|
43
43
|
/** Server-Sent Events framing. Used by every JSON-streaming HTTP provider. */
|
|
44
44
|
export const sse = { id: "sse", frame: sseFraming };
|
|
45
45
|
/** Server-Sent Events framing that retains the conventional `[DONE]` sentinel. */
|
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-
|
|
3
|
+
"version": "0.0.0-dev-20537",
|
|
4
4
|
"name": "@opencode/ai",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"devDependencies": {
|
|
35
35
|
"@clack/prompts": "1.0.0-alpha.1",
|
|
36
36
|
"@effect/platform-node": "4.0.0-rc.112",
|
|
37
|
-
"@opencode/http-recorder": "0.0.0-dev-
|
|
37
|
+
"@opencode/http-recorder": "0.0.0-dev-20537",
|
|
38
38
|
"@tsconfig/bun": "1.0.9",
|
|
39
39
|
"@types/bun": "1.4.0",
|
|
40
40
|
"@typescript/native-preview": "7.0.0-dev.20251207.1",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"@aws-sdk/credential-providers": "3.1057.0",
|
|
45
45
|
"@smithy/eventstream-codec": "4.2.14",
|
|
46
46
|
"@smithy/util-utf8": "4.2.2",
|
|
47
|
-
"@opencode/schema": "0.0.0-dev-
|
|
47
|
+
"@opencode/schema": "0.0.0-dev-20537",
|
|
48
48
|
"aws4fetch": "1.0.20",
|
|
49
49
|
"effect": "4.0.0-rc.112",
|
|
50
50
|
"google-auth-library": "10.5.0"
|