agent-accelerator 0.5.8 → 0.6.0

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 (67) hide show
  1. package/README.md +26 -5
  2. package/examples/prompts/SYSTEM_PROMPT.md +2 -0
  3. package/examples/prompts/SYSTEM_PROMPT_AGENT.md +4 -0
  4. package/examples/prompts/SYSTEM_PROMPT_TOOLS.md +1 -0
  5. package/package.json +4 -1
  6. package/src/agent/agent.ts +374 -167
  7. package/src/agent/architecture.md +14 -12
  8. package/src/agent/config-blocks.ts +39 -12
  9. package/src/agent/context.ts +8 -3
  10. package/src/agent/delegation.ts +82 -29
  11. package/src/agent/loop.ts +449 -373
  12. package/src/agent/subagent.ts +1 -3
  13. package/src/agent/subagents-tool.ts +45 -7
  14. package/src/agent/subagents.md +3 -0
  15. package/src/agent/turns.ts +3 -1
  16. package/src/agent/worker.ts +58 -28
  17. package/src/agent/workers.ts +113 -20
  18. package/src/architecture.md +16 -15
  19. package/src/index.ts +34 -10
  20. package/src/models/architecture.md +4 -3
  21. package/src/models/catalog-cache.ts +6 -5
  22. package/src/models/catalog.ts +46 -49
  23. package/src/providers/architecture.md +10 -8
  24. package/src/providers/google.ts +150 -62
  25. package/src/providers/openai-compat.ts +60 -58
  26. package/src/providers/openai.ts +57 -39
  27. package/src/providers/openrouter.ts +87 -32
  28. package/src/providers/registry.ts +12 -26
  29. package/src/providers/shared.ts +89 -39
  30. package/src/providers.ts +56 -115
  31. package/src/scheduler/architecture.md +9 -8
  32. package/src/scheduler/scheduled-actions.md +4 -0
  33. package/src/scheduler/scheduler.ts +186 -60
  34. package/src/scheduler/store.ts +23 -10
  35. package/src/scheduler/time.ts +17 -3
  36. package/src/scheduler/tool.ts +33 -7
  37. package/src/session/architecture.md +7 -6
  38. package/src/session/journal.ts +38 -12
  39. package/src/session/store.ts +122 -61
  40. package/src/session/usage.ts +58 -9
  41. package/src/streaming/architecture.md +4 -3
  42. package/src/streaming/event-stream.ts +26 -12
  43. package/src/telemetry.ts +6 -2
  44. package/src/tools/architecture.md +8 -7
  45. package/src/tools/executor.ts +50 -48
  46. package/src/tools/schema.ts +10 -6
  47. package/src/tools/subagent-result.ts +6 -1
  48. package/src/tools/tool.ts +9 -11
  49. package/src/types/agent.ts +2 -21
  50. package/src/types/architecture.md +11 -10
  51. package/src/types/core.ts +1 -9
  52. package/src/types/errors.ts +26 -4
  53. package/src/types/model.ts +0 -4
  54. package/src/types/tool.ts +12 -2
  55. package/src/update-models.ts +5 -5
  56. package/src/utils/architecture.md +16 -15
  57. package/src/utils/cache.ts +1 -3
  58. package/src/utils/durable.ts +17 -5
  59. package/src/utils/env.ts +9 -7
  60. package/src/utils/errors.ts +50 -44
  61. package/src/utils/headers.ts +3 -10
  62. package/src/utils/media.ts +23 -8
  63. package/src/utils/raw-capture.ts +9 -2
  64. package/src/utils/retry.ts +17 -31
  65. package/src/utils/safe-io.ts +29 -7
  66. package/src/utils/serialization.ts +1 -4
  67. package/src/utils/session.ts +1 -2
@@ -29,7 +29,7 @@ import type {
29
29
  ProviderGenerateResult,
30
30
  ProviderRawData,
31
31
  } from "../types/model.ts";
32
- import type { ProviderContext, Message, ContentPart, ProviderSteps, ToolResultPart, MediaResolution } from "../types/message.ts";
32
+ import type { ProviderContext, ContentPart, ProviderSteps, ToolResultPart, MediaResolution } from "../types/message.ts";
33
33
  import type { StandardToolDeclaration, ToolCallRecord } from "../types/tool.ts";
34
34
  import type { TokenUsage } from "../types/core.ts";
35
35
  import { AssistantMessageEventStream } from "../streaming/event-stream.ts";
@@ -53,7 +53,6 @@ import {
53
53
  combinedSignal,
54
54
  timeoutFor,
55
55
  hasAuthHeader,
56
- clampToolCallId,
57
56
  openStreamResponse,
58
57
  retryOptionsFrom,
59
58
  headersToRecord,
@@ -85,19 +84,70 @@ import {
85
84
  // ---------------------------------------------------------------------------
86
85
 
87
86
  /** Image MIME types Google accepts inline. */
88
- export const GOOGLE_IMAGE_MIME = ["image/png", "image/jpeg", "image/webp", "image/heic", "image/heif", "image/gif", "image/bmp", "image/tiff"] as const;
87
+ export const GOOGLE_IMAGE_MIME = [
88
+ "image/png",
89
+ "image/jpeg",
90
+ "image/webp",
91
+ "image/heic",
92
+ "image/heif",
93
+ "image/gif",
94
+ "image/bmp",
95
+ "image/tiff",
96
+ ] as const;
89
97
  /** Audio MIME types Google accepts inline (audio is never fetched by URL). */
90
- export const GOOGLE_AUDIO_MIME = ["audio/wav", "audio/mp3", "audio/aiff", "audio/aac", "audio/ogg", "audio/flac", "audio/mpeg", "audio/m4a", "audio/l16", "audio/opus", "audio/alaw", "audio/mulaw", "audio/webm"] as const;
98
+ export const GOOGLE_AUDIO_MIME = [
99
+ "audio/wav",
100
+ "audio/mp3",
101
+ "audio/aiff",
102
+ "audio/aac",
103
+ "audio/ogg",
104
+ "audio/flac",
105
+ "audio/mpeg",
106
+ "audio/m4a",
107
+ "audio/l16",
108
+ "audio/opus",
109
+ "audio/alaw",
110
+ "audio/mulaw",
111
+ "audio/webm",
112
+ ] as const;
91
113
  /** Video MIME types Google accepts inline. */
92
- export const GOOGLE_VIDEO_MIME = ["video/mp4", "video/mpeg", "video/mpg", "video/mov", "video/avi", "video/x-flv", "video/webm", "video/wmv", "video/3gpp"] as const;
114
+ export const GOOGLE_VIDEO_MIME = [
115
+ "video/mp4",
116
+ "video/mpeg",
117
+ "video/mpg",
118
+ "video/mov",
119
+ "video/avi",
120
+ "video/x-flv",
121
+ "video/webm",
122
+ "video/wmv",
123
+ "video/3gpp",
124
+ ] as const;
93
125
  /** Document MIME types Google accepts (the only two). */
94
126
  export const GOOGLE_DOCUMENT_MIME = ["application/pdf", "text/csv"] as const;
95
127
  /** MIME types Google fetches itself from a public `https://` URL. */
96
128
  export const GOOGLE_URL_MIME = [
97
- "text/html", "text/css", "text/plain", "text/xml", "text/csv", "text/rtf", "text/javascript",
98
- "application/json", "application/pdf",
99
- "image/bmp", "image/jpeg", "image/png", "image/webp",
100
- "video/mp4", "video/mpeg", "video/quicktime", "video/avi", "video/x-flv", "video/mpg", "video/webm", "video/wmv", "video/3gpp",
129
+ "text/html",
130
+ "text/css",
131
+ "text/plain",
132
+ "text/xml",
133
+ "text/csv",
134
+ "text/rtf",
135
+ "text/javascript",
136
+ "application/json",
137
+ "application/pdf",
138
+ "image/bmp",
139
+ "image/jpeg",
140
+ "image/png",
141
+ "image/webp",
142
+ "video/mp4",
143
+ "video/mpeg",
144
+ "video/quicktime",
145
+ "video/avi",
146
+ "video/x-flv",
147
+ "video/mpg",
148
+ "video/webm",
149
+ "video/wmv",
150
+ "video/3gpp",
101
151
  ] as const;
102
152
  /** Common MIME spellings mapped to Google's inline names. */
103
153
  export const GOOGLE_INLINE_MIME_ALIASES: Readonly<Record<string, string>> = {
@@ -174,7 +224,13 @@ const YOUTUBE_URL = /^https:\/\/(www\.|m\.)?(youtube\.com|youtu\.be)\//i;
174
224
  // Interactions wire shapes (subset used by this adapter)
175
225
  // ---------------------------------------------------------------------------
176
226
 
177
- type MediaBlock = { type: MediaBlockType; mime_type?: string; data?: string; uri?: string; resolution?: MediaResolution };
227
+ type MediaBlock = {
228
+ type: MediaBlockType;
229
+ mime_type?: string;
230
+ data?: string;
231
+ uri?: string;
232
+ resolution?: MediaResolution;
233
+ };
178
234
  type InteractionContentBlock = { type: "text"; text: string } | MediaBlock;
179
235
 
180
236
  type InteractionInputStep =
@@ -317,7 +373,10 @@ function liveChainFor(sessionId: string | undefined, options?: ProviderRequestOp
317
373
  * below the previous turn's total (Rule 3: a caller dropped the chain without
318
374
  * rehydrating history). Never throws; observability-only.
319
375
  */
320
- function checkTurnTokens(sessionId: string | undefined, usage: { inputTokens: number; outputTokens: number; cachedTokens?: number }): void {
376
+ function checkTurnTokens(
377
+ sessionId: string | undefined,
378
+ usage: { inputTokens: number; outputTokens: number; cachedTokens?: number }
379
+ ): void {
321
380
  try {
322
381
  if (!sessionId) return;
323
382
  const total = (usage.inputTokens ?? 0) + (usage.outputTokens ?? 0);
@@ -383,7 +442,7 @@ function cleanModelId(model: string | ModelSpec): string {
383
442
  function assertSupportedGeneration(clean: string): void {
384
443
  const match = clean.match(/^gemini-(\d+)(?:[.\-]|$)/i);
385
444
  if (match && Number(match[1]) < 3) {
386
- throw new Error(
445
+ throw new ConfigError(
387
446
  `[Agent Accelerator] [google/${clean}] Unsupported generation: Gemini ${match[1]}.x and older are strictly rejected by this SDK. ` +
388
447
  `Use a Gemini 3 or later model for full support and feature coverage.`
389
448
  );
@@ -409,9 +468,7 @@ function toGoogleTools(tools?: StandardToolDeclaration[]): Array<Record<string,
409
468
  type: "function",
410
469
  name: t.name,
411
470
  description: t.description,
412
- parameters: stripSchemaForGoogle(
413
- (t.parameters || { type: "object", properties: {} }) as Record<string, unknown>
414
- ),
471
+ parameters: stripSchemaForGoogle((t.parameters || { type: "object", properties: {} }) as Record<string, unknown>),
415
472
  }));
416
473
  }
417
474
 
@@ -443,7 +500,9 @@ function uriBlock(type: MediaBlockType, value: string, mimeType?: string): Media
443
500
  if (type === "video" && YOUTUBE_URL.test(value)) return withMime(explicit);
444
501
  const mime = explicit ?? bareMime(inferMimeType(value, ""));
445
502
  if (value.startsWith(FILES_API_PREFIX) || value.startsWith("gs://")) return withMime(mime);
446
- return value.startsWith("https://") && mime && (GOOGLE_URL_MIME as readonly string[]).includes(mime) ? withMime(mime) : undefined;
503
+ return value.startsWith("https://") && mime && (GOOGLE_URL_MIME as readonly string[]).includes(mime)
504
+ ? withMime(mime)
505
+ : undefined;
447
506
  }
448
507
 
449
508
  // Inlines a media part as base64 under Google's MIME name, failing before the network on an unsupported type; in: any media value (bytes, data URL, path, http URL), out: contentPartsToBlocks.
@@ -452,7 +511,8 @@ async function inlineBlock(type: MediaBlockType, value: MediaValue, mimeType?: s
452
511
  const raw = bareMime(norm.mimeType) ?? "";
453
512
  const mime = GOOGLE_INLINE_MIME_ALIASES[raw] ?? raw;
454
513
  if (!INLINE_MIME[type].includes(mime)) {
455
- const what = type === "document" ? "Google reads only PDF and CSV documents" : `Google does not accept ${type} type "${mime}"`;
514
+ const what =
515
+ type === "document" ? "Google reads only PDF and CSV documents" : `Google does not accept ${type} type "${mime}"`;
456
516
  throw new ValidationError(
457
517
  `[Agent Accelerator] ${what} (got "${mime}"; supported: ${INLINE_MIME[type].join(", ")}). Convert the file or pass the correct mimeType.`
458
518
  );
@@ -534,7 +594,9 @@ function functionResultStep(part: ToolResultPart): InteractionInputStep {
534
594
  * behind them. Sending only the last message would drop the tool results and
535
595
  * leave the chained interaction waiting on its function calls.
536
596
  */
537
- async function latestTurnInput(context: ProviderContext): Promise<string | InteractionContentBlock[] | InteractionInputStep[]> {
597
+ async function latestTurnInput(
598
+ context: ProviderContext
599
+ ): Promise<string | InteractionContentBlock[] | InteractionInputStep[]> {
538
600
  const messages = context.messages;
539
601
  let lastAssistant = -1;
540
602
  for (let i = messages.length - 1; i >= 0; i--) {
@@ -617,10 +679,11 @@ async function fullHistorySteps(context: ProviderContext): Promise<InteractionIn
617
679
  const sig = shouldEchoSignature(partProvider ?? msgProvider, "google")
618
680
  ? part.thoughtSignature || m.thoughtSignature || ""
619
681
  : "";
620
- const thoughtStep: { type: "thought"; signature?: string; summary: Array<{ type: string; text?: string }> } = {
621
- type: "thought",
622
- summary: [{ type: "text", text: part.thinking }],
623
- };
682
+ const thoughtStep: { type: "thought"; signature?: string; summary: Array<{ type: string; text?: string }> } =
683
+ {
684
+ type: "thought",
685
+ summary: [{ type: "text", text: part.thinking }],
686
+ };
624
687
  if (sig) thoughtStep.signature = sig;
625
688
  steps.push(thoughtStep);
626
689
  } else if (part.type === "tool_call") {
@@ -733,11 +796,7 @@ function parseInteraction(
733
796
  // truncated marker); absent args mean a zero-argument call.
734
797
  const rawArgs: unknown = step.arguments;
735
798
  const args =
736
- rawArgs && typeof rawArgs === "object"
737
- ? rawArgs
738
- : typeof rawArgs === "string"
739
- ? parseArguments(rawArgs)
740
- : {};
799
+ rawArgs && typeof rawArgs === "object" ? rawArgs : typeof rawArgs === "string" ? parseArguments(rawArgs) : {};
741
800
  toolCalls.push({
742
801
  id: step.id || newToolCallId(),
743
802
  name: step.name || "unknown",
@@ -815,12 +874,11 @@ export class GoogleInteractionsProvider implements Provider {
815
874
  // only signatures return and canonical `thinking` would always be empty.
816
875
  // Enabled whenever thinking is active — including the default/dynamic
817
876
  // case where no explicit level is sent — and off only when disabled.
818
- const thinkingActive =
819
- options?.thinking?.enabled !== false && (options?.thinking?.level ?? "dynamic") !== "none";
877
+ const thinkingActive = options?.thinking?.enabled !== false && (options?.thinking?.level ?? "dynamic") !== "none";
820
878
  if (thinkingActive) generationConfig["thinking_summaries"] = "auto";
821
879
  if (toolChoice) generationConfig["tool_choice"] = toolChoice;
822
- // max_output_tokens only; stop and seed warn+drop.
823
- Object.assign(generationConfig, mapGenerationParamsToGoogle(options, `google/${modelId}`));
880
+ // max_output_tokens only.
881
+ Object.assign(generationConfig, mapGenerationParamsToGoogle(options));
824
882
 
825
883
  const body: InteractionRequestBody = {
826
884
  model: modelId,
@@ -834,7 +892,9 @@ export class GoogleInteractionsProvider implements Provider {
834
892
  let outputEmulated = false;
835
893
  let outputSpec: import("../types/model.ts").StructuredOutputSpec | undefined;
836
894
  try {
837
- outputSpec = normalizeStructuredOutput(options?.output ?? (options as { outputSchema?: unknown })?.outputSchema);
895
+ outputSpec = normalizeStructuredOutput(
896
+ options?.output ?? (options as { outputSchema?: unknown })?.outputSchema
897
+ );
838
898
  } catch {
839
899
  outputSpec = undefined;
840
900
  }
@@ -846,7 +906,8 @@ export class GoogleInteractionsProvider implements Provider {
846
906
  provider: "google",
847
907
  capability: "structured output",
848
908
  requested: `${outputSpec.name} (google/${modelId})`,
849
- reason: "the catalog reports structured_output: false for this model, so native response_format is unsupported.",
909
+ reason:
910
+ "the catalog reports structured_output: false for this model, so native response_format is unsupported.",
850
911
  fallback: "emulated JSON instruction + client validation (no response_format is sent)",
851
912
  });
852
913
  }
@@ -927,7 +988,10 @@ export class GoogleInteractionsProvider implements Provider {
927
988
  apiKey: string,
928
989
  signal?: AbortSignal
929
990
  ): Promise<{ status: number; statusText: string; headers: Record<string, string>; text: string }> {
930
- const { signal: effective, cleanup } = combinedSignal(signal, timeoutFor(options as { timeoutMs?: number } | undefined));
991
+ const { signal: effective, cleanup } = combinedSignal(
992
+ signal,
993
+ timeoutFor(options as { timeoutMs?: number } | undefined)
994
+ );
931
995
  try {
932
996
  const res = await (options?.fetch ?? fetch)(url, this.requestInit(method, body, options, apiKey, effective));
933
997
  const text = await res.text();
@@ -944,7 +1008,6 @@ export class GoogleInteractionsProvider implements Provider {
944
1008
  // Throws a concise provider error (status, code, Retry-After, key hint) for a non-2xx reply; in: every Google HTTP call, out: nothing or a thrown error.
945
1009
  private throwIfError(
946
1010
  status: number,
947
- statusText: string,
948
1011
  url: string,
949
1012
  bodyText: string,
950
1013
  modelId: string,
@@ -989,9 +1052,14 @@ export class GoogleInteractionsProvider implements Provider {
989
1052
  modelId: string
990
1053
  ): Promise<InteractionReply> {
991
1054
  const res = await this.doFetch(url, method, body, options, apiKey, options?.signal);
992
- this.throwIfError(res.status, res.statusText, url, res.text, modelId, res.headers);
1055
+ this.throwIfError(res.status, url, res.text, modelId, res.headers);
993
1056
  try {
994
- return { status: res.status, statusText: res.statusText, headers: res.headers, interaction: JSON.parse(res.text) as InteractionObject };
1057
+ return {
1058
+ status: res.status,
1059
+ statusText: res.statusText,
1060
+ headers: res.headers,
1061
+ interaction: JSON.parse(res.text) as InteractionObject,
1062
+ };
995
1063
  } catch {
996
1064
  throw toConciseProviderError(
997
1065
  Object.assign(new Error("Invalid JSON response from Google Interactions API"), {
@@ -1006,7 +1074,12 @@ export class GoogleInteractionsProvider implements Provider {
1006
1074
  }
1007
1075
 
1008
1076
  // Fire-and-forget cancel so an aborted background run stops billing; in: an aborted generate/stream, out: POST {id}/cancel.
1009
- private cancelInteraction(baseUrl: string, id: string, options: ProviderRequestOptions | undefined, apiKey: string): void {
1077
+ private cancelInteraction(
1078
+ baseUrl: string,
1079
+ id: string,
1080
+ options: ProviderRequestOptions | undefined,
1081
+ apiKey: string
1082
+ ): void {
1010
1083
  void this.doFetch(`${baseUrl}/interactions/${id}/cancel`, "POST", undefined, options, apiKey).catch(() => {});
1011
1084
  }
1012
1085
 
@@ -1022,7 +1095,7 @@ export class GoogleInteractionsProvider implements Provider {
1022
1095
  assertSupportedGeneration(clean);
1023
1096
  const apiKey = resolveApiKey(options);
1024
1097
  if (!apiKey) {
1025
- throw new Error(
1098
+ throw new ConfigError(
1026
1099
  "[Agent Accelerator] Missing Google API key. Set GEMINI_API_KEY (or GOOGLE_API_KEY) or pass apiKey."
1027
1100
  );
1028
1101
  }
@@ -1060,7 +1133,10 @@ export class GoogleInteractionsProvider implements Provider {
1060
1133
  try {
1061
1134
  for (let poll = 0; isRunning(reply.interaction.status); poll++) {
1062
1135
  await sleep(this.pollDelaysMs[poll === 0 ? 0 : 1], options?.signal);
1063
- reply = await withRetries(() => this.fetchInteraction(`${url}/${id}`, "GET", undefined, options, apiKey, clean), retryOptions);
1136
+ reply = await withRetries(
1137
+ () => this.fetchInteraction(`${url}/${id}`, "GET", undefined, options, apiKey, clean),
1138
+ retryOptions
1139
+ );
1064
1140
  }
1065
1141
  return reply;
1066
1142
  } catch (err) {
@@ -1071,7 +1147,10 @@ export class GoogleInteractionsProvider implements Provider {
1071
1147
 
1072
1148
  // Create (retried as a whole), wait if it runs in the background, then record the chain and map the result.
1073
1149
  const run = async (): Promise<ProviderGenerateResult> => {
1074
- let reply = await withRetries(() => this.fetchInteraction(url, "POST", body, options, apiKey, clean), retryOptions);
1150
+ let reply = await withRetries(
1151
+ () => this.fetchInteraction(url, "POST", body, options, apiKey, clean),
1152
+ retryOptions
1153
+ );
1075
1154
  if (body.background) reply = await waitForCompletion(reply);
1076
1155
  const { interaction } = reply;
1077
1156
  if (interaction.id && sessionId && body.store !== false) {
@@ -1081,12 +1160,10 @@ export class GoogleInteractionsProvider implements Provider {
1081
1160
  lastTurnTotals.delete(sessionId);
1082
1161
  }
1083
1162
  noteProviderTurn(sessionId, "google");
1084
- const parsed = parseInteraction(
1085
- interaction,
1086
- clean,
1087
- Date.now() - startTime,
1088
- { request: rawRequest, response: { status: reply.status, statusText: reply.statusText, headers: reply.headers, body: interaction } }
1089
- );
1163
+ const parsed = parseInteraction(interaction, clean, Date.now() - startTime, {
1164
+ request: rawRequest,
1165
+ response: { status: reply.status, statusText: reply.statusText, headers: reply.headers, body: interaction },
1166
+ });
1090
1167
  checkTurnTokens(sessionId, parsed.usage);
1091
1168
  return parsed;
1092
1169
  };
@@ -1133,7 +1210,7 @@ export class GoogleInteractionsProvider implements Provider {
1133
1210
  assertSupportedGeneration(clean);
1134
1211
  const apiKey = resolveApiKey(options);
1135
1212
  if (!apiKey) {
1136
- throw new Error(
1213
+ throw new ConfigError(
1137
1214
  "[Agent Accelerator] Missing Google API key. Set GEMINI_API_KEY (or GOOGLE_API_KEY) or pass apiKey."
1138
1215
  );
1139
1216
  }
@@ -1165,9 +1242,16 @@ export class GoogleInteractionsProvider implements Provider {
1165
1242
  // (429/5xx/network) are retried before the first byte; a live stream is never retried.
1166
1243
  const open = (target: string, method: "POST" | "GET"): Promise<Response> =>
1167
1244
  openStreamResponse({
1168
- fetchOnce: () => withAbortTimer(linked, streamTimeoutMs, () => (options?.fetch ?? fetch)(target, this.requestInit(method, method === "POST" ? body : undefined, options, apiKey, linked.signal))),
1169
- onHttpError: (r, text) => this.throwIfError(r.status, r.statusText, target, text, clean, headersToRecord(r.headers)),
1170
- noBodyError: () => toConciseProviderError(new Error("Google streaming response had no body"), "google", clean),
1245
+ fetchOnce: () =>
1246
+ withAbortTimer(linked, streamTimeoutMs, () =>
1247
+ (options?.fetch ?? fetch)(
1248
+ target,
1249
+ this.requestInit(method, method === "POST" ? body : undefined, options, apiKey, linked.signal)
1250
+ )
1251
+ ),
1252
+ onHttpError: (r, text) => this.throwIfError(r.status, target, text, clean, headersToRecord(r.headers)),
1253
+ noBodyError: () =>
1254
+ toConciseProviderError(new Error("Google streaming response had no body"), "google", clean),
1171
1255
  retry: retryOptionsFrom(options),
1172
1256
  signal: linked.signal,
1173
1257
  label: { providerId: "google", modelId: clean },
@@ -1177,7 +1261,11 @@ export class GoogleInteractionsProvider implements Provider {
1177
1261
  res.headers.forEach((v, k) => {
1178
1262
  responseHeaders[k] = v;
1179
1263
  });
1180
- const responseMeta: Record<string, unknown> & { status: number; statusText: string; headers: Record<string, string> } = { status: res.status, statusText: res.statusText, headers: responseHeaders };
1264
+ const responseMeta: Record<string, unknown> & {
1265
+ status: number;
1266
+ statusText: string;
1267
+ headers: Record<string, string>;
1268
+ } = { status: res.status, statusText: res.statusText, headers: responseHeaders };
1181
1269
  eventStream.push({ type: "start", raw: { request: rawRequest } } as never);
1182
1270
 
1183
1271
  let reader: StreamReader | undefined;
@@ -1286,7 +1374,8 @@ export class GoogleInteractionsProvider implements Provider {
1286
1374
  thinking += t;
1287
1375
  eventStream.push({ type: "thinking_delta", thinkingDelta: t, partialThinking: thinking });
1288
1376
  }
1289
- if (!signature && typeof step["signature"] === "string" && step["signature"]) signature = step["signature"];
1377
+ if (!signature && typeof step["signature"] === "string" && step["signature"])
1378
+ signature = step["signature"];
1290
1379
  } else if (step["type"] === "function_call") {
1291
1380
  const args = step["arguments"];
1292
1381
  const startArgs = typeof args === "string" ? args : args ? JSON.stringify(args) : "";
@@ -1343,7 +1432,8 @@ export class GoogleInteractionsProvider implements Provider {
1343
1432
  break;
1344
1433
  }
1345
1434
  if (chunk.done) break;
1346
- for (const m of parser.feed(decoder.decode(chunk.value, { stream: true }))) handleMessage(m.event, m.data);
1435
+ for (const m of parser.feed(decoder.decode(chunk.value, { stream: true })))
1436
+ handleMessage(m.event, m.data);
1347
1437
  }
1348
1438
  for (const m of parser.flush()) handleMessage(m.event, m.data);
1349
1439
  } finally {
@@ -1383,7 +1473,8 @@ export class GoogleInteractionsProvider implements Provider {
1383
1473
  }
1384
1474
 
1385
1475
  if (sessionId) {
1386
- if (responseId && body.store !== false) setChain(sessionId, { interactionId: responseId, model: clean, at: Date.now() });
1476
+ if (responseId && body.store !== false)
1477
+ setChain(sessionId, { interactionId: responseId, model: clean, at: Date.now() });
1387
1478
  else if (body.store === false) {
1388
1479
  interactionChains.delete(sessionId);
1389
1480
  lastTurnTotals.delete(sessionId);
@@ -1402,7 +1493,8 @@ export class GoogleInteractionsProvider implements Provider {
1402
1493
  const cleanThinkingEarly = thinking.replace(/\n{3,}/g, "\n\n").trim();
1403
1494
  const ctEntry = Object.entries(responseHeaders).find(([k]) => k.toLowerCase() === "content-type");
1404
1495
  const isSSEPayload =
1405
- (typeof ctEntry?.[1] === "string" && ((ctEntry[1] as string).includes("event-stream") || (ctEntry[1] as string).startsWith("text/"))) ||
1496
+ (typeof ctEntry?.[1] === "string" &&
1497
+ ((ctEntry[1] as string).includes("event-stream") || (ctEntry[1] as string).startsWith("text/"))) ||
1406
1498
  framesSeen > 0 ||
1407
1499
  badFrames > 0;
1408
1500
  if (!hasTerminal && !text && !cleanThinkingEarly && toolCalls.length === 0 && isSSEPayload) {
@@ -1433,14 +1525,10 @@ export class GoogleInteractionsProvider implements Provider {
1433
1525
  } catch (err: unknown) {
1434
1526
  const raw = err instanceof Error ? err : new Error(String(err));
1435
1527
  const isAbort =
1436
- linked.signal.aborted ||
1437
- eventStream.isCancelled() ||
1438
- (raw as { name?: string }).name === "AbortError";
1528
+ linked.signal.aborted || eventStream.isCancelled() || (raw as { name?: string }).name === "AbortError";
1439
1529
  if (isAbort) {
1440
1530
  cancelBackground();
1441
- eventStream.fail(
1442
- Object.assign(new Error("Stream aborted"), { name: "AbortError" })
1443
- );
1531
+ eventStream.fail(Object.assign(new Error("Stream aborted"), { name: "AbortError" }));
1444
1532
  } else if (evictSessionId && usedChain && isNotFoundChainError(raw)) {
1445
1533
  // Reactive self-heal, fail-clean variant (Rule 2): the `start`
1446
1534
  // event is already delivered, so a transparent in-stream retry