@opencode/ai 0.0.0-dev-20498 → 0.0.0-dev-20501

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 (53) hide show
  1. package/README.md +25 -0
  2. package/dist/protocols/alibaba-chat.d.ts +3 -3
  3. package/dist/protocols/alibaba-responses.d.ts +16 -12
  4. package/dist/protocols/alibaba-responses.js +3 -5
  5. package/dist/protocols/google-interactions.d.ts +236 -0
  6. package/dist/protocols/google-interactions.js +478 -0
  7. package/dist/protocols/index.d.ts +1 -0
  8. package/dist/protocols/index.js +1 -0
  9. package/dist/protocols/meta-responses.d.ts +31 -58
  10. package/dist/protocols/meta-responses.js +5 -24
  11. package/dist/protocols/mistral-chat.d.ts +1 -1
  12. package/dist/protocols/open-responses.d.ts +406 -91
  13. package/dist/protocols/open-responses.js +12 -6
  14. package/dist/protocols/openai-chat.d.ts +5 -5
  15. package/dist/protocols/openai-compatible-chat.d.ts +1 -1
  16. package/dist/protocols/openai-compatible-responses.d.ts +16 -4
  17. package/dist/protocols/openai-responses.d.ts +87 -223
  18. package/dist/protocols/openai-responses.js +3 -10
  19. package/dist/protocols/xai-responses.d.ts +18 -21
  20. package/dist/protocols/xai-responses.js +2 -8
  21. package/dist/protocols/zai-chat.d.ts +3 -3
  22. package/dist/providers/alibaba.d.ts +17 -13
  23. package/dist/providers/amazon-bedrock-mantle.d.ts +17 -5
  24. package/dist/providers/azure.d.ts +13 -61
  25. package/dist/providers/baseten.d.ts +2 -2
  26. package/dist/providers/cerebras.d.ts +2 -2
  27. package/dist/providers/cloudflare-ai-gateway.d.ts +14 -62
  28. package/dist/providers/cloudflare-workers-ai.d.ts +2 -2
  29. package/dist/providers/cohere.d.ts +2 -2
  30. package/dist/providers/deepinfra.d.ts +2 -2
  31. package/dist/providers/deepseek.d.ts +2 -2
  32. package/dist/providers/digitalocean.d.ts +5 -5
  33. package/dist/providers/fireworks.d.ts +2 -2
  34. package/dist/providers/google/interactions.d.ts +7 -0
  35. package/dist/providers/google/interactions.js +8 -0
  36. package/dist/providers/google-vertex-chat.d.ts +1 -1
  37. package/dist/providers/google-vertex-responses.d.ts +16 -4
  38. package/dist/providers/google.d.ts +94 -3
  39. package/dist/providers/google.js +8 -1
  40. package/dist/providers/groq.d.ts +4 -4
  41. package/dist/providers/meta.d.ts +15 -28
  42. package/dist/providers/minimax.d.ts +17 -5
  43. package/dist/providers/moonshot.d.ts +17 -5
  44. package/dist/providers/openai-compatible-responses.d.ts +16 -4
  45. package/dist/providers/openai-compatible.d.ts +1 -1
  46. package/dist/providers/openai.d.ts +7 -31
  47. package/dist/providers/openrouter.d.ts +5 -5
  48. package/dist/providers/togetherai.d.ts +2 -2
  49. package/dist/providers/xai.d.ts +1 -1
  50. package/dist/providers/zai-coding-plan.d.ts +17 -5
  51. package/dist/providers/zai.d.ts +1 -1
  52. package/dist/route/client.js +2 -5
  53. package/package.json +3 -3
@@ -0,0 +1,478 @@
1
+ import { Effect, Schema } from "effect";
2
+ import { Route } from "../route/client.js";
3
+ import { Auth } from "../route/auth.js";
4
+ import { Endpoint } from "../route/endpoint.js";
5
+ import { Framing } from "../route/framing.js";
6
+ import { Protocol } from "../route/protocol.js";
7
+ import { AIError, LLMEvent, ProviderID, Usage, } from "../schema/index.js";
8
+ import { Media } from "../media.js";
9
+ import { classifyProviderFailure, providerErrorMessage } from "../provider-error.js";
10
+ import { encodeJson } from "../utils/json.js";
11
+ import { JsonObject, knownString, lenient, optionalNull, ProviderShared } from "./shared.js";
12
+ import { Lifecycle } from "./utils/lifecycle.js";
13
+ import { MediaInput } from "./utils/media-input.js";
14
+ import { ToolStream } from "./utils/tool-stream.js";
15
+ const ADAPTER = "google-interactions";
16
+ export const DEFAULT_BASE_URL = "https://generativelanguage.googleapis.com/v1beta";
17
+ // =============================================================================
18
+ // Public Model Input
19
+ // =============================================================================
20
+ const ThinkingLevel = knownString();
21
+ const Options = Schema.Struct({
22
+ previousInteractionId: lenient(Schema.String),
23
+ store: lenient(Schema.Boolean),
24
+ thinkingLevel: lenient(ThinkingLevel),
25
+ thinkingSummaries: lenient(knownString()),
26
+ serviceTier: lenient(knownString()),
27
+ });
28
+ const decodeOptions = ProviderShared.validateWith(Schema.decodeUnknownEffect(Options));
29
+ // =============================================================================
30
+ // Request Body Schema
31
+ // =============================================================================
32
+ const Text = Schema.Struct({ type: Schema.Literal("text"), text: Schema.String });
33
+ const MediaContent = Schema.Struct({
34
+ type: Schema.Literals(["image", "audio", "video", "document"]),
35
+ data: Schema.optional(Schema.String),
36
+ uri: Schema.optional(Schema.String),
37
+ mime_type: Schema.String,
38
+ });
39
+ const Content = Schema.Union([Text, MediaContent]);
40
+ const InputStep = Schema.Union([
41
+ Schema.Struct({ type: Schema.Literals(["user_input", "model_output"]), content: Schema.Array(Content) }),
42
+ Schema.Struct({
43
+ type: Schema.Literal("thought"),
44
+ signature: Schema.optional(Schema.String),
45
+ summary: Schema.optional(Schema.Array(Text)),
46
+ }),
47
+ Schema.Struct({
48
+ type: Schema.Literal("function_call"),
49
+ id: Schema.String,
50
+ name: Schema.String,
51
+ arguments: Schema.Unknown,
52
+ signature: Schema.optional(Schema.String),
53
+ }),
54
+ Schema.Struct({
55
+ type: Schema.Literal("function_result"),
56
+ call_id: Schema.String,
57
+ name: Schema.String,
58
+ result: Schema.Unknown,
59
+ is_error: Schema.optional(Schema.Boolean),
60
+ }),
61
+ ]);
62
+ const ToolChoice = Schema.Union([
63
+ Schema.Literals(["auto", "any", "none"]),
64
+ Schema.Struct({ allowed_tools: Schema.Struct({ mode: Schema.Literal("any"), tools: Schema.Array(Schema.String) }) }),
65
+ ]);
66
+ const Body = Schema.Struct({
67
+ model: Schema.String,
68
+ input: Schema.Array(InputStep),
69
+ stream: Schema.Literal(true),
70
+ store: Schema.Boolean,
71
+ previous_interaction_id: Schema.optional(Schema.String),
72
+ system_instruction: Schema.optional(Schema.String),
73
+ service_tier: Schema.optional(Schema.String),
74
+ tools: Schema.optional(Schema.Array(Schema.Struct({
75
+ type: Schema.Literal("function"),
76
+ name: Schema.String,
77
+ description: Schema.String,
78
+ parameters: JsonObject,
79
+ }))),
80
+ generation_config: Schema.Struct({
81
+ max_output_tokens: Schema.optional(Schema.Number),
82
+ temperature: Schema.optional(Schema.Number),
83
+ top_p: Schema.optional(Schema.Number),
84
+ seed: Schema.optional(Schema.Number),
85
+ stop_sequences: Schema.optional(Schema.Array(Schema.String)),
86
+ thinking_level: Schema.optional(ThinkingLevel),
87
+ thinking_summaries: Schema.optional(Schema.String),
88
+ tool_choice: Schema.optional(ToolChoice),
89
+ }),
90
+ });
91
+ // =============================================================================
92
+ // Streaming Event Schema
93
+ // =============================================================================
94
+ const RawUsage = Schema.StructWithRest(Schema.Struct({
95
+ total_input_tokens: optionalNull(Schema.Number),
96
+ total_cached_tokens: optionalNull(Schema.Number),
97
+ total_output_tokens: optionalNull(Schema.Number),
98
+ total_thought_tokens: optionalNull(Schema.Number),
99
+ total_tokens: optionalNull(Schema.Number),
100
+ raw_prompt_token: optionalNull(Schema.Number),
101
+ }), [Schema.Record(Schema.String, Schema.Unknown)]);
102
+ const OutputStep = Schema.Struct({
103
+ type: Schema.String,
104
+ id: Schema.optional(Schema.String),
105
+ name: Schema.optional(Schema.String),
106
+ arguments: Schema.optional(JsonObject),
107
+ signature: Schema.optional(Schema.String),
108
+ summary: Schema.optional(Schema.Array(Text)),
109
+ content: Schema.optional(Schema.Array(Schema.Struct({ type: Schema.String, text: Schema.optional(Schema.String) }))),
110
+ });
111
+ const Delta = Schema.Union([
112
+ Schema.Struct({ type: Schema.Literal("text"), text: Schema.String }),
113
+ Schema.Struct({ type: Schema.Literal("arguments_delta"), arguments: Schema.String }),
114
+ Schema.Struct({ type: Schema.Literal("thought_signature"), signature: Schema.String }),
115
+ Schema.Struct({ type: Schema.Literal("thought_summary"), content: Text }),
116
+ // Unknown output modalities must fail explicitly rather than disappearing from a successful response.
117
+ Schema.Struct({ type: Schema.String }),
118
+ ]);
119
+ const Interaction = Schema.StructWithRest(Schema.Struct({
120
+ id: Schema.optional(Schema.String),
121
+ status: Schema.String,
122
+ usage: Schema.optional(RawUsage),
123
+ }), [Schema.Record(Schema.String, Schema.Unknown)]);
124
+ const Event = Schema.Union([
125
+ Schema.Struct({ event_type: Schema.Literal("step.start"), index: Schema.Number, step: OutputStep }),
126
+ Schema.Struct({ event_type: Schema.Literal("step.delta"), index: Schema.Number, delta: Delta }),
127
+ Schema.Struct({ event_type: Schema.Literal("step.stop"), index: Schema.Number }),
128
+ Schema.Struct({
129
+ event_type: Schema.Literal("interaction.created"),
130
+ interaction: Schema.Struct({ id: Schema.optional(Schema.String) }),
131
+ }),
132
+ Schema.Struct({
133
+ event_type: Schema.Literal("interaction.status_update"),
134
+ interaction_id: Schema.optional(Schema.String),
135
+ status: Schema.String,
136
+ }),
137
+ Schema.Struct({ event_type: Schema.Literal("interaction.completed"), interaction: Interaction }),
138
+ Schema.Struct({ event_type: Schema.Literal("error"), error: Schema.Unknown }),
139
+ ]);
140
+ // =============================================================================
141
+ // Request Body Construction
142
+ // =============================================================================
143
+ const mediaContent = Effect.fn("GoogleInteractions.mediaContent")(function* (asset) {
144
+ if (asset.kind !== "image" &&
145
+ asset.kind !== "audio" &&
146
+ asset.kind !== "video" &&
147
+ asset.mediaType !== "application/pdf" &&
148
+ asset.mediaType !== "text/csv")
149
+ return yield* ProviderShared.invalidRequest(`Google Interactions does not support ${asset.mediaType} document input`);
150
+ const type = asset.kind === "image" || asset.kind === "audio" || asset.kind === "video" ? asset.kind : "document";
151
+ const uri = MediaInput.refID(asset, ProviderID.make("google"));
152
+ if (uri !== undefined)
153
+ return { type, uri, mime_type: asset.mediaType };
154
+ const inline = yield* ProviderShared.requireInlineMedia("Google Interactions", asset);
155
+ return { type, data: inline.base64, mime_type: inline.mime };
156
+ });
157
+ const signature = (metadata, key) => {
158
+ const value = metadata?.[key];
159
+ return ProviderShared.isRecord(value) && typeof value.interactionSignature === "string"
160
+ ? value.interactionSignature
161
+ : undefined;
162
+ };
163
+ const lowerMessages = Effect.fn("GoogleInteractions.lowerMessages")(function* (request) {
164
+ const steps = [];
165
+ const key = request.model.route.providerMetadataKey ?? String(request.model.provider);
166
+ for (const message of request.messages) {
167
+ if (message.role === "system") {
168
+ const part = yield* ProviderShared.wrappedSystemUpdate("Google Interactions", message);
169
+ steps.push({ type: "user_input", content: [{ type: "text", text: part.text }] });
170
+ continue;
171
+ }
172
+ const start = steps.length;
173
+ // Consecutive ordinary content remains one native message; tools and thoughts retain their chronology.
174
+ const append = (content) => {
175
+ const type = message.role === "assistant" ? "model_output" : "user_input";
176
+ const last = steps.at(-1);
177
+ if (steps.length > start && last?.type === type)
178
+ steps[steps.length - 1] = { type, content: [...last.content, content] };
179
+ else
180
+ steps.push({ type, content: [content] });
181
+ };
182
+ for (const part of message.content) {
183
+ if (message.role === "tool") {
184
+ if (part.type !== "tool-result")
185
+ return yield* ProviderShared.unsupportedContent(ADAPTER, "tool", ["tool-result"]);
186
+ steps.push({
187
+ type: "function_result",
188
+ call_id: part.id,
189
+ name: part.name,
190
+ result: yield* lowerToolResult(part),
191
+ is_error: part.result.type === "error" || undefined,
192
+ });
193
+ continue;
194
+ }
195
+ if (part.type === "text") {
196
+ append({ type: "text", text: part.text });
197
+ continue;
198
+ }
199
+ if (part.type === "media") {
200
+ append(yield* mediaContent(part.media));
201
+ continue;
202
+ }
203
+ if (message.role === "assistant" && part.type === "reasoning") {
204
+ steps.push({
205
+ type: "thought",
206
+ signature: signature(part.providerMetadata, key),
207
+ summary: part.text ? [{ type: "text", text: part.text }] : undefined,
208
+ });
209
+ continue;
210
+ }
211
+ if (message.role === "assistant" && part.type === "tool-call") {
212
+ steps.push({
213
+ type: "function_call",
214
+ id: part.id,
215
+ name: part.name,
216
+ arguments: part.input,
217
+ signature: signature(part.providerMetadata, key),
218
+ });
219
+ continue;
220
+ }
221
+ return yield* ProviderShared.unsupportedContent(ADAPTER, message.role, message.role === "user" ? ["text", "media"] : ["text", "media", "reasoning", "tool-call"]);
222
+ }
223
+ }
224
+ return steps;
225
+ });
226
+ const lowerToolResult = Effect.fn("GoogleInteractions.lowerToolResult")(function* (part) {
227
+ if (part.result.type === "json" && ProviderShared.isRecord(part.result.value))
228
+ return part.result.value;
229
+ if (part.result.type !== "content")
230
+ return ProviderShared.toolResultText(part);
231
+ return yield* Effect.forEach(part.result.value, (item) => {
232
+ if (item.type === "text")
233
+ return Effect.succeed({ type: "text", text: item.text });
234
+ return mediaContent(ProviderShared.toolFileMedia(item).media);
235
+ });
236
+ });
237
+ const fromRequest = Effect.fn("GoogleInteractions.fromRequest")(function* (request) {
238
+ const options = yield* decodeOptions(request.providerOptions ?? {});
239
+ const flattened = ProviderShared.flattenToolRequest(request);
240
+ if (flattened.tools.some((tool) => tool.native !== undefined))
241
+ return yield* ProviderShared.invalidRequest("Google Interactions hosted tools are not supported");
242
+ if (request.generation?.topK !== undefined ||
243
+ request.generation?.frequencyPenalty !== undefined ||
244
+ request.generation?.presencePenalty !== undefined)
245
+ return yield* ProviderShared.invalidRequest("Google Interactions does not support topK, frequencyPenalty, or presencePenalty");
246
+ const choice = request.toolChoice === undefined
247
+ ? undefined
248
+ : yield* ProviderShared.matchToolChoice(ADAPTER, request.toolChoice, {
249
+ auto: () => "auto",
250
+ none: () => "none",
251
+ required: () => "any",
252
+ tool: (name) => ({ allowed_tools: { mode: "any", tools: [name] } }),
253
+ });
254
+ return {
255
+ model: request.model.id,
256
+ input: yield* lowerMessages(flattened.request),
257
+ stream: true,
258
+ // Full-history replay need not create retained provider resources. Continuation callers opt in to storage.
259
+ store: options.store ?? false,
260
+ previous_interaction_id: options.previousInteractionId,
261
+ system_instruction: request.system.length ? ProviderShared.joinText(request.system) : undefined,
262
+ service_tier: options.serviceTier,
263
+ tools: flattened.tools.length
264
+ ? flattened.tools.map((tool) => ({
265
+ type: "function",
266
+ name: tool.name,
267
+ description: tool.description,
268
+ parameters: tool.inputSchema,
269
+ }))
270
+ : undefined,
271
+ generation_config: {
272
+ max_output_tokens: request.generation?.maxTokens,
273
+ temperature: request.generation?.temperature,
274
+ top_p: request.generation?.topP,
275
+ seed: request.generation?.seed,
276
+ stop_sequences: request.generation?.stop,
277
+ thinking_level: options.thinkingLevel,
278
+ thinking_summaries: options.thinkingSummaries,
279
+ tool_choice: choice,
280
+ },
281
+ };
282
+ });
283
+ // =============================================================================
284
+ // Stream Parsing
285
+ // =============================================================================
286
+ const metadata = (state, step) => ({
287
+ [state.metadataKey]: { interactionSignature: step.signature },
288
+ });
289
+ const mapUsage = (usage, key) => {
290
+ if (!usage)
291
+ return undefined;
292
+ const input = usage.total_input_tokens ?? undefined;
293
+ const cached = usage.total_cached_tokens ?? undefined;
294
+ const reasoning = usage.total_thought_tokens ?? undefined;
295
+ const output = usage.total_output_tokens === undefined || usage.total_output_tokens === null
296
+ ? undefined
297
+ : usage.total_output_tokens + (reasoning ?? 0);
298
+ return new Usage({
299
+ contextTokens: usage.raw_prompt_token ?? input,
300
+ inputTokens: input,
301
+ outputTokens: output,
302
+ nonCachedInputTokens: ProviderShared.subtractTokens(input, cached),
303
+ cacheReadInputTokens: cached,
304
+ reasoningTokens: reasoning,
305
+ totalTokens: usage.total_tokens ?? undefined,
306
+ providerMetadata: { [key]: usage },
307
+ });
308
+ };
309
+ const onStart = Effect.fn("GoogleInteractions.onStart")(function* (state, index, step) {
310
+ const events = [];
311
+ let lifecycle = Lifecycle.stepStart(state.lifecycle, events);
312
+ let tools = state.tools;
313
+ const id = String(index);
314
+ if (step.type === "thought") {
315
+ lifecycle = Lifecycle.reasoningStart(lifecycle, events, id, metadata(state, step));
316
+ for (const part of step.summary ?? [])
317
+ lifecycle = Lifecycle.reasoningDelta(lifecycle, events, id, part.text);
318
+ }
319
+ else if (step.type === "model_output") {
320
+ lifecycle = Lifecycle.textStart(lifecycle, events, id);
321
+ for (const part of step.content ?? []) {
322
+ if (part.type !== "text")
323
+ return yield* ProviderShared.eventError(ADAPTER, `Unsupported Interactions output: ${part.type}`, encodeJson(step));
324
+ if (part.text)
325
+ lifecycle = Lifecycle.textDelta(lifecycle, events, id, part.text);
326
+ }
327
+ }
328
+ else if (step.type === "function_call") {
329
+ if (!step.id || !step.name)
330
+ return yield* ProviderShared.eventError(ADAPTER, "Interactions function call lacks id or name", encodeJson(step));
331
+ tools = ToolStream.start(tools, index, {
332
+ id: step.id,
333
+ name: step.name,
334
+ providerMetadata: metadata(state, step),
335
+ input: step.arguments && Object.keys(step.arguments).length ? encodeJson(step.arguments) : "",
336
+ });
337
+ events.push(LLMEvent.toolInputStart({ id: step.id, name: step.name, providerMetadata: metadata(state, step) }));
338
+ }
339
+ else
340
+ return yield* ProviderShared.eventError(ADAPTER, `Unsupported Interactions step: ${step.type}`, encodeJson(step));
341
+ return [{ ...state, lifecycle, tools, steps: { ...state.steps, [index]: step } }, events];
342
+ });
343
+ const onDelta = Effect.fn("GoogleInteractions.onDelta")(function* (state, index, delta) {
344
+ const step = state.steps[index];
345
+ if (!step)
346
+ return yield* ProviderShared.eventError(ADAPTER, "Interactions delta without step.start", encodeJson(delta));
347
+ const events = [];
348
+ if (delta.type === "text" && "text" in delta && step.type === "model_output")
349
+ return [
350
+ { ...state, lifecycle: Lifecycle.textDelta(state.lifecycle, events, String(index), delta.text) },
351
+ events,
352
+ ];
353
+ if (delta.type === "thought_summary" && "content" in delta && step.type === "thought")
354
+ return [
355
+ { ...state, lifecycle: Lifecycle.reasoningDelta(state.lifecycle, events, String(index), delta.content.text) },
356
+ events,
357
+ ];
358
+ if (delta.type === "thought_signature" && "signature" in delta) {
359
+ const next = { ...step, signature: delta.signature };
360
+ const tool = state.tools[index];
361
+ return [
362
+ {
363
+ ...state,
364
+ steps: { ...state.steps, [index]: next },
365
+ tools: tool ? { ...state.tools, [index]: { ...tool, providerMetadata: metadata(state, next) } } : state.tools,
366
+ },
367
+ events,
368
+ ];
369
+ }
370
+ if (delta.type === "arguments_delta" && "arguments" in delta && step.type === "function_call") {
371
+ const result = ToolStream.appendExisting(ADAPTER, state.tools, index, delta.arguments, "Interactions arguments without function call");
372
+ if (ToolStream.isError(result))
373
+ return yield* result;
374
+ return [{ ...state, tools: result.tools }, result.events];
375
+ }
376
+ return yield* ProviderShared.eventError(ADAPTER, `Unsupported Interactions delta: ${delta.type}`, encodeJson(delta));
377
+ });
378
+ const onStop = Effect.fn("GoogleInteractions.onStop")(function* (state, index) {
379
+ const step = state.steps[index];
380
+ if (!step)
381
+ return yield* ProviderShared.eventError(ADAPTER, "Interactions step.stop without step.start");
382
+ const events = [];
383
+ if (step.type === "thought")
384
+ return [
385
+ { ...state, lifecycle: Lifecycle.reasoningEnd(state.lifecycle, events, String(index), metadata(state, step)) },
386
+ events,
387
+ ];
388
+ if (step.type === "model_output")
389
+ return [
390
+ { ...state, lifecycle: Lifecycle.textEnd(state.lifecycle, events, String(index)) },
391
+ events,
392
+ ];
393
+ const result = yield* ToolStream.finish(ADAPTER, state.tools, index);
394
+ return [{ ...state, tools: result.tools }, result.events ?? []];
395
+ });
396
+ const step = Effect.fn("GoogleInteractions.step")(function* (state, event) {
397
+ switch (event.event_type) {
398
+ case "step.start":
399
+ return yield* onStart(state, event.index, event.step);
400
+ case "step.delta":
401
+ return yield* onDelta(state, event.index, event.delta);
402
+ case "step.stop":
403
+ return yield* onStop(state, event.index);
404
+ case "interaction.created":
405
+ case "interaction.status_update":
406
+ return [state, []];
407
+ case "error":
408
+ return yield* new AIError({
409
+ reason: classifyProviderFailure({
410
+ message: providerErrorMessage(encodeJson(event)) ?? "Google Interactions stream error",
411
+ data: event.error,
412
+ rawBody: encodeJson(event),
413
+ }),
414
+ });
415
+ case "interaction.completed": {
416
+ const interaction = event.interaction;
417
+ if (interaction.status === "failed" || interaction.status === "cancelled")
418
+ return yield* new AIError({
419
+ reason: classifyProviderFailure({
420
+ message: `Google Interactions ${interaction.status}`,
421
+ data: interaction,
422
+ rawBody: encodeJson(event),
423
+ }),
424
+ });
425
+ if (!["completed", "requires_action", "incomplete"].includes(interaction.status))
426
+ return yield* ProviderShared.eventError(ADAPTER, `Unexpected terminal Interactions status: ${interaction.status}`, encodeJson(event));
427
+ const pending = yield* ToolStream.finishAll(ADAPTER, state.tools);
428
+ const events = [...pending.events];
429
+ const lifecycle = Lifecycle.finish(state.lifecycle, events, {
430
+ reason: {
431
+ normalized: interaction.status === "requires_action"
432
+ ? "tool-calls"
433
+ : interaction.status === "incomplete"
434
+ ? "length"
435
+ : "stop",
436
+ raw: interaction.status,
437
+ },
438
+ usage: mapUsage(interaction.usage, state.metadataKey),
439
+ providerMetadata: { [state.metadataKey]: { interactionId: interaction.id } },
440
+ });
441
+ return [{ ...state, lifecycle, tools: pending.tools, completed: true }, events];
442
+ }
443
+ }
444
+ });
445
+ // =============================================================================
446
+ // Protocol And Route
447
+ // =============================================================================
448
+ export const protocol = Protocol.make({
449
+ id: ADAPTER,
450
+ sanitizer: "gemini",
451
+ body: { schema: Body, from: fromRequest },
452
+ stream: {
453
+ event: Protocol.jsonEvent(Event),
454
+ initial: (request) => ({
455
+ route: `${request.model.provider}/${ADAPTER}`,
456
+ metadataKey: request.model.route.providerMetadataKey ?? String(request.model.provider),
457
+ lifecycle: Lifecycle.initial(),
458
+ steps: {},
459
+ tools: ToolStream.empty(),
460
+ completed: false,
461
+ }),
462
+ step,
463
+ terminal: (event) => event.event_type === "interaction.completed",
464
+ onHalt: (state) => state.completed
465
+ ? Effect.succeed([])
466
+ : Effect.fail(ProviderShared.eventError(ADAPTER, "Google Interactions stream ended before interaction.completed")),
467
+ },
468
+ });
469
+ export const route = Route.make({
470
+ id: ADAPTER,
471
+ provider: "google",
472
+ providerMetadataKey: "google",
473
+ protocol,
474
+ endpoint: Endpoint.path("/interactions", { baseURL: DEFAULT_BASE_URL }),
475
+ auth: Auth.none,
476
+ framing: Framing.sse,
477
+ });
478
+ export * as GoogleInteractions from "./google-interactions.js";
@@ -2,6 +2,7 @@ export * as AnthropicMessages from "./anthropic-messages.js";
2
2
  export * as BedrockConverse from "./bedrock-converse.js";
3
3
  export * as CohereChat from "./cohere-chat.js";
4
4
  export * as Gemini from "./gemini.js";
5
+ export * as GoogleInteractions from "./google-interactions.js";
5
6
  export * as MistralChat from "./mistral-chat.js";
6
7
  export * as OpenAIChat from "./openai-chat.js";
7
8
  export * as OpenAIImages from "./openai-images.js";
@@ -2,6 +2,7 @@ export * as AnthropicMessages from "./anthropic-messages.js";
2
2
  export * as BedrockConverse from "./bedrock-converse.js";
3
3
  export * as CohereChat from "./cohere-chat.js";
4
4
  export * as Gemini from "./gemini.js";
5
+ export * as GoogleInteractions from "./google-interactions.js";
5
6
  export * as MistralChat from "./mistral-chat.js";
6
7
  export * as OpenAIChat from "./openai-chat.js";
7
8
  export * as OpenAIImages from "./openai-images.js";