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.
- package/README.md +26 -5
- package/examples/prompts/SYSTEM_PROMPT.md +2 -0
- package/examples/prompts/SYSTEM_PROMPT_AGENT.md +4 -0
- package/examples/prompts/SYSTEM_PROMPT_TOOLS.md +1 -0
- package/package.json +4 -1
- package/src/agent/agent.ts +374 -167
- package/src/agent/architecture.md +14 -12
- package/src/agent/config-blocks.ts +39 -12
- package/src/agent/context.ts +8 -3
- package/src/agent/delegation.ts +82 -29
- package/src/agent/loop.ts +449 -373
- package/src/agent/subagent.ts +1 -3
- package/src/agent/subagents-tool.ts +45 -7
- package/src/agent/subagents.md +3 -0
- package/src/agent/turns.ts +3 -1
- package/src/agent/worker.ts +58 -28
- package/src/agent/workers.ts +113 -20
- package/src/architecture.md +16 -15
- package/src/index.ts +34 -10
- package/src/models/architecture.md +4 -3
- package/src/models/catalog-cache.ts +6 -5
- package/src/models/catalog.ts +46 -49
- package/src/providers/architecture.md +10 -8
- package/src/providers/google.ts +150 -62
- package/src/providers/openai-compat.ts +60 -58
- package/src/providers/openai.ts +57 -39
- package/src/providers/openrouter.ts +87 -32
- package/src/providers/registry.ts +12 -26
- package/src/providers/shared.ts +89 -39
- package/src/providers.ts +56 -115
- package/src/scheduler/architecture.md +9 -8
- package/src/scheduler/scheduled-actions.md +4 -0
- package/src/scheduler/scheduler.ts +186 -60
- package/src/scheduler/store.ts +23 -10
- package/src/scheduler/time.ts +17 -3
- package/src/scheduler/tool.ts +33 -7
- package/src/session/architecture.md +7 -6
- package/src/session/journal.ts +38 -12
- package/src/session/store.ts +122 -61
- package/src/session/usage.ts +58 -9
- package/src/streaming/architecture.md +4 -3
- package/src/streaming/event-stream.ts +26 -12
- package/src/telemetry.ts +6 -2
- package/src/tools/architecture.md +8 -7
- package/src/tools/executor.ts +50 -48
- package/src/tools/schema.ts +10 -6
- package/src/tools/subagent-result.ts +6 -1
- package/src/tools/tool.ts +9 -11
- package/src/types/agent.ts +2 -21
- package/src/types/architecture.md +11 -10
- package/src/types/core.ts +1 -9
- package/src/types/errors.ts +26 -4
- package/src/types/model.ts +0 -4
- package/src/types/tool.ts +12 -2
- package/src/update-models.ts +5 -5
- package/src/utils/architecture.md +16 -15
- package/src/utils/cache.ts +1 -3
- package/src/utils/durable.ts +17 -5
- package/src/utils/env.ts +9 -7
- package/src/utils/errors.ts +50 -44
- package/src/utils/headers.ts +3 -10
- package/src/utils/media.ts +23 -8
- package/src/utils/raw-capture.ts +9 -2
- package/src/utils/retry.ts +17 -31
- package/src/utils/safe-io.ts +29 -7
- package/src/utils/serialization.ts +1 -4
- package/src/utils/session.ts +1 -2
package/src/providers/google.ts
CHANGED
|
@@ -29,7 +29,7 @@ import type {
|
|
|
29
29
|
ProviderGenerateResult,
|
|
30
30
|
ProviderRawData,
|
|
31
31
|
} from "../types/model.ts";
|
|
32
|
-
import type { ProviderContext,
|
|
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 = [
|
|
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 = [
|
|
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 = [
|
|
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",
|
|
98
|
-
"
|
|
99
|
-
"
|
|
100
|
-
"
|
|
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 = {
|
|
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(
|
|
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
|
|
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)
|
|
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 =
|
|
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(
|
|
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
|
-
|
|
622
|
-
|
|
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
|
|
823
|
-
Object.assign(generationConfig, mapGenerationParamsToGoogle(options
|
|
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(
|
|
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:
|
|
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(
|
|
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,
|
|
1055
|
+
this.throwIfError(res.status, url, res.text, modelId, res.headers);
|
|
993
1056
|
try {
|
|
994
|
-
return {
|
|
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(
|
|
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
|
|
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(
|
|
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(
|
|
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
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
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
|
|
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: () =>
|
|
1169
|
-
|
|
1170
|
-
|
|
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> & {
|
|
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"])
|
|
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 })))
|
|
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)
|
|
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" &&
|
|
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
|