agent-accelerator 0.5.0 → 0.5.2
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 +66 -59
- package/package.json +7 -14
- package/src/agent/agent.ts +14 -49
- package/src/agent/context.ts +5 -4
- package/src/agent/delegation.ts +1 -9
- package/src/agent/loop.ts +22 -84
- package/src/agent/subagent.ts +1 -2
- package/src/agent/worker.ts +3 -6
- package/src/index.ts +6 -41
- package/src/providers/google.ts +390 -154
- package/src/providers/openai-compat.ts +12 -27
- package/src/providers/openai.ts +13 -35
- package/src/providers/openrouter.ts +11 -29
- package/src/providers.ts +32 -124
- package/src/session/store.ts +18 -29
- package/src/types/agent.ts +10 -15
- package/src/types/core.ts +5 -32
- package/src/types/message.ts +15 -1
- package/src/types/model.ts +7 -3
- package/src/types/response.ts +5 -0
- package/src/utils/cache.ts +1 -137
- package/src/utils/errors.ts +1 -4
- package/src/utils/headers.ts +3 -19
- package/src/utils/retry.ts +2 -1
- package/src/utils/safe-io.ts +2 -2
- package/src/utils/documents.ts +0 -577
package/src/session/store.ts
CHANGED
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
import * as fs from "node:fs";
|
|
25
25
|
import * as path from "node:path";
|
|
26
26
|
import type { Message, ContentPart } from "../types/message.ts";
|
|
27
|
-
import type
|
|
27
|
+
import { CACHE_OPTIONS, type TokenUsage, type CacheOption, type ServiceTier } from "../types/core.ts";
|
|
28
28
|
import type { ModelSpec } from "../types/model.ts";
|
|
29
29
|
import type { AgentResponse } from "../types/response.ts";
|
|
30
30
|
import { base64ToBytes, bytesToBase64 } from "../utils/base64.ts";
|
|
@@ -68,12 +68,11 @@ export interface PersistedAgentSession {
|
|
|
68
68
|
model: string;
|
|
69
69
|
subagentModel?: string;
|
|
70
70
|
thinkingLevel?: string;
|
|
71
|
-
cache?:
|
|
71
|
+
cache?: CacheOption;
|
|
72
72
|
/** Legacy read-only: pre-compact files duplicated base instructions here;
|
|
73
73
|
* no longer written (`systemPrompt` already embeds them). */
|
|
74
74
|
instructions?: string;
|
|
75
75
|
systemPrompt?: string;
|
|
76
|
-
cachedContentId?: string;
|
|
77
76
|
messages: Message[];
|
|
78
77
|
totals?: SessionTotals;
|
|
79
78
|
/** Parent session this snapshot was spawned from, when applicable. */
|
|
@@ -283,11 +282,11 @@ export interface SessionAgentLike {
|
|
|
283
282
|
modelStringOrSpec: unknown;
|
|
284
283
|
subagentModel?: unknown;
|
|
285
284
|
thinkingConfig?: { level?: string };
|
|
286
|
-
|
|
285
|
+
cache?: CacheOption;
|
|
287
286
|
instructions?: string;
|
|
288
287
|
parentSessionId?: unknown;
|
|
289
288
|
subagentTraces?: Record<string, PersistedSubAgentTrace>;
|
|
290
|
-
context: { systemPrompt?: string; messages: Message[]
|
|
289
|
+
context: { systemPrompt?: string; messages: Message[] };
|
|
291
290
|
}
|
|
292
291
|
|
|
293
292
|
function snapshotSubagents(traces: Record<string, PersistedSubAgentTrace> | undefined): Record<string, PersistedSubAgentTrace> | undefined {
|
|
@@ -416,17 +415,6 @@ export function buildSessionData(agent: SessionAgentLike, telemetry?: SessionTel
|
|
|
416
415
|
typeof (agent as any)?.parentSessionId === "string" ? (agent as any).parentSessionId as string : undefined;
|
|
417
416
|
const subagents = snapshotSubagents((agent as any)?.subagentTraces as Record<string, PersistedSubAgentTrace> | undefined);
|
|
418
417
|
const messages = inlineBinaryMedia(stripRedundantRawArguments(agent.context.messages));
|
|
419
|
-
// `cache.sessionId` echoes the top-level `sessionId` (affinity alias): drop
|
|
420
|
-
// it from the file when identical so it isn't stored twice. Readers still
|
|
421
|
-
// accept legacy files carrying it (`importSession`, loop fallbacks).
|
|
422
|
-
const cacheForFile = (() => {
|
|
423
|
-
if (!agent.cacheConfig) return undefined;
|
|
424
|
-
if (agent.cacheConfig.sessionId === agent.sessionId) {
|
|
425
|
-
const { sessionId: _dropped, ...rest } = agent.cacheConfig;
|
|
426
|
-
return Object.keys(rest).length > 0 ? rest : undefined;
|
|
427
|
-
}
|
|
428
|
-
return agent.cacheConfig;
|
|
429
|
-
})();
|
|
430
418
|
// Field order is the on-disk layout contract: identity → config →
|
|
431
419
|
// systemPrompt → totals → messages → lineage/traces. `instructions` is
|
|
432
420
|
// intentionally never written: `systemPrompt` already embeds it (plus the
|
|
@@ -438,9 +426,8 @@ export function buildSessionData(agent: SessionAgentLike, telemetry?: SessionTel
|
|
|
438
426
|
model,
|
|
439
427
|
...(subagentModel ? { subagentModel: String(subagentModel) } : {}),
|
|
440
428
|
...(agent.thinkingConfig?.level ? { thinkingLevel: agent.thinkingConfig.level as string } : {}),
|
|
441
|
-
...(
|
|
429
|
+
...(agent.cache ? { cache: agent.cache } : {}),
|
|
442
430
|
...(agent.context.systemPrompt ? { systemPrompt: agent.context.systemPrompt } : {}),
|
|
443
|
-
...(agent.context.cachedContentId ? { cachedContentId: agent.context.cachedContentId } : {}),
|
|
444
431
|
...(totals ? { totals } : {}),
|
|
445
432
|
messages,
|
|
446
433
|
...(parentSessionId ? { parentSessionId } : {}),
|
|
@@ -511,6 +498,14 @@ export interface SessionLimits {
|
|
|
511
498
|
/** Marker for a structurally valid payload that exceeds the configured limits. */
|
|
512
499
|
const OVER_LIMIT = Symbol("session-over-limit");
|
|
513
500
|
|
|
501
|
+
// Reads a stored `cache` field: a current string value, or a pre-0.5.1 `{ retention }` object (short=5m, medium=1h, long=12h); in: a parsed session file, out: `PersistedAgentSession.cache`.
|
|
502
|
+
function toCacheOption(raw: unknown): CacheOption | undefined {
|
|
503
|
+
if (typeof raw === "string") return (CACHE_OPTIONS as readonly string[]).includes(raw) ? (raw as CacheOption) : undefined;
|
|
504
|
+
const legacy: Record<string, CacheOption> = { implicit: "implicit", short: "5m", medium: "1h", long: "12h" };
|
|
505
|
+
const retention = (raw as { retention?: unknown } | null)?.retention;
|
|
506
|
+
return typeof retention === "string" ? legacy[retention] : undefined;
|
|
507
|
+
}
|
|
508
|
+
|
|
514
509
|
function parseSession(raw: string, limits?: SessionLimits): PersistedAgentSession | null | typeof OVER_LIMIT {
|
|
515
510
|
const maxMessages = limits?.maxMessages ?? MAX_SESSION_MESSAGES;
|
|
516
511
|
const maxBytes = limits?.maxBytes ?? MAX_SESSION_JSON_BYTES;
|
|
@@ -525,8 +520,7 @@ function parseSession(raw: string, limits?: SessionLimits): PersistedAgentSessio
|
|
|
525
520
|
let model = "";
|
|
526
521
|
let subagentModel: string | undefined;
|
|
527
522
|
let thinkingLevel: string | undefined;
|
|
528
|
-
let cache:
|
|
529
|
-
let cachedContentId: string | undefined;
|
|
523
|
+
let cache: CacheOption | undefined;
|
|
530
524
|
let systemPrompt: string | undefined;
|
|
531
525
|
let instructions: string | undefined;
|
|
532
526
|
let parentSessionId: string | undefined;
|
|
@@ -540,8 +534,7 @@ function parseSession(raw: string, limits?: SessionLimits): PersistedAgentSessio
|
|
|
540
534
|
const obj: any = JSON.parse(t);
|
|
541
535
|
if (obj.type === "session") {
|
|
542
536
|
sessionId = obj.id ?? obj.sessionId ?? sessionId;
|
|
543
|
-
cache = obj.cache ?? cache;
|
|
544
|
-
cachedContentId = obj.cachedContentId ?? cachedContentId;
|
|
537
|
+
cache = toCacheOption(obj.cache) ?? cache;
|
|
545
538
|
model = obj.model ?? model;
|
|
546
539
|
thinkingLevel = obj.thinkingLevel ?? thinkingLevel;
|
|
547
540
|
parentSessionId = obj.parentSessionId ?? parentSessionId;
|
|
@@ -553,8 +546,7 @@ function parseSession(raw: string, limits?: SessionLimits): PersistedAgentSessio
|
|
|
553
546
|
subagentModel = obj.id ?? obj.model ?? subagentModel;
|
|
554
547
|
} else if (obj.type === "metrics" || obj.type === "totals") {
|
|
555
548
|
totals = (obj.metrics ?? obj.totals ?? totals) as SessionTotals | undefined;
|
|
556
|
-
cache = obj.cache ?? cache;
|
|
557
|
-
cachedContentId = obj.cachedContentId ?? cachedContentId;
|
|
549
|
+
cache = toCacheOption(obj.cache) ?? cache;
|
|
558
550
|
} else if (obj.type === "modelChange") {
|
|
559
551
|
model = obj.modelId ?? obj.model ?? model;
|
|
560
552
|
} else if (obj.type === "thinkingLevelChange") {
|
|
@@ -581,7 +573,6 @@ function parseSession(raw: string, limits?: SessionLimits): PersistedAgentSessio
|
|
|
581
573
|
...(cache ? { cache } : {}),
|
|
582
574
|
...(instructions ? { instructions } : {}),
|
|
583
575
|
...(systemPrompt ? { systemPrompt } : {}),
|
|
584
|
-
...(cachedContentId ? { cachedContentId } : {}),
|
|
585
576
|
messages,
|
|
586
577
|
...(totals ? { totals } : {}),
|
|
587
578
|
...(parentSessionId ? { parentSessionId } : {}),
|
|
@@ -602,10 +593,9 @@ function parseSession(raw: string, limits?: SessionLimits): PersistedAgentSessio
|
|
|
602
593
|
model: obj.model ?? "",
|
|
603
594
|
subagentModel: obj.subagentModel,
|
|
604
595
|
thinkingLevel: obj.thinkingLevel ?? obj.thinkingLevelChange,
|
|
605
|
-
cache: obj.cache,
|
|
596
|
+
cache: toCacheOption(obj.cache),
|
|
606
597
|
instructions: obj.instructions,
|
|
607
598
|
systemPrompt: obj.context.systemPrompt,
|
|
608
|
-
cachedContentId: obj.cachedContentId ?? obj.context.cachedContentId,
|
|
609
599
|
messages: obj.context.messages as Message[],
|
|
610
600
|
totals: obj.totals ?? obj.metrics,
|
|
611
601
|
parentSessionId: obj.parentSessionId,
|
|
@@ -622,10 +612,9 @@ function parseSession(raw: string, limits?: SessionLimits): PersistedAgentSessio
|
|
|
622
612
|
model: obj.model ?? "",
|
|
623
613
|
subagentModel: obj.subagentModel,
|
|
624
614
|
thinkingLevel: obj.thinkingLevel,
|
|
625
|
-
cache: obj.cache,
|
|
615
|
+
cache: toCacheOption(obj.cache),
|
|
626
616
|
instructions: obj.instructions,
|
|
627
617
|
systemPrompt: obj.systemPrompt,
|
|
628
|
-
cachedContentId: obj.cachedContentId,
|
|
629
618
|
messages: Array.isArray(obj.messages) ? (obj.messages as Message[]) : [],
|
|
630
619
|
totals: obj.totals,
|
|
631
620
|
parentSessionId: obj.parentSessionId,
|
package/src/types/agent.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { ModelSpec } from "./model.ts";
|
|
|
2
2
|
import type { ToolDefinition } from "./tool.ts";
|
|
3
3
|
import type {
|
|
4
4
|
ThinkingLevel,
|
|
5
|
-
|
|
5
|
+
CacheOption,
|
|
6
6
|
ServiceTier,
|
|
7
7
|
} from "./core.ts";
|
|
8
8
|
import type { ModelProviderInstance } from "../providers/registry.ts";
|
|
@@ -17,7 +17,7 @@ import type { z } from "zod";
|
|
|
17
17
|
// is a deprecated alias (still honored: dynamicSubagents.model ??
|
|
18
18
|
// subagentModel ?? SUB_AGENT_MODEL ?? parent model).
|
|
19
19
|
// - Session identity: agent-level `sessionId` only (per-run `sessionId`
|
|
20
|
-
// selects a named session entry
|
|
20
|
+
// selects a named session entry).
|
|
21
21
|
// - Model identity: `ModelRef` = string (`provider/model`) | catalog
|
|
22
22
|
// `ModelSpec` | `ModelProviderInstance` ({ provider, model, apiKey?,
|
|
23
23
|
// baseUrl? }). Explicit `Agent` config always wins over env; constructing
|
|
@@ -96,17 +96,13 @@ export interface AgentConfig {
|
|
|
96
96
|
* See `normalizeThinkingV2` (re-exported from the root entry).
|
|
97
97
|
*/
|
|
98
98
|
thinking?: ThinkingOption;
|
|
99
|
-
/** Prompt-cache retention and session-affinity settings. */
|
|
100
|
-
cache?: CacheConfig;
|
|
101
|
-
/** Provider service tier, when supported. */
|
|
102
|
-
serviceTier?: ServiceTier;
|
|
103
99
|
/**
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
* natively; unknown models count as capable). Also auto-registers the
|
|
107
|
-
* `convert_document_to_markdown` tool for path/URL mentions in plain text.
|
|
100
|
+
* Prompt-cache lifetime: `"implicit" | "5m" | "30m" | "1h" | "12h"`. Omit for the provider default.
|
|
101
|
+
* A value the provider does not support falls back to its default with one `provider_warning`.
|
|
108
102
|
*/
|
|
109
|
-
|
|
103
|
+
cache?: CacheOption;
|
|
104
|
+
/** Provider service tier, when supported. */
|
|
105
|
+
serviceTier?: ServiceTier;
|
|
110
106
|
/** Stable conversation/cache session ID. */
|
|
111
107
|
sessionId?: string;
|
|
112
108
|
/** Headers merged into every provider request. */
|
|
@@ -186,10 +182,9 @@ export interface AgentConfig {
|
|
|
186
182
|
*/
|
|
187
183
|
telemetry?: { persist?: boolean };
|
|
188
184
|
/**
|
|
189
|
-
* Local file policy. With `roots` set, every local-path media part
|
|
190
|
-
*
|
|
191
|
-
*
|
|
192
|
-
* unrestricted and the model-callable tool is limited to `process.cwd()`.
|
|
185
|
+
* Local file policy. With `roots` set, every local-path media part must
|
|
186
|
+
* resolve inside them (`[]` denies all local paths). Without it, local
|
|
187
|
+
* paths in message parts are unrestricted.
|
|
193
188
|
*/
|
|
194
189
|
fileAccess?: { roots?: string[] };
|
|
195
190
|
/**
|
package/src/types/core.ts
CHANGED
|
@@ -24,40 +24,13 @@ export interface ThinkingConfig {
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
/**
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
* - "short": 5 minutes explicit TTL
|
|
30
|
-
* - "medium": 1 hour explicit TTL
|
|
31
|
-
* - "long": 12 hours explicit TTL
|
|
32
|
-
* - undefined: no explicit caching enforced (implicit may still happen)
|
|
27
|
+
* Prompt-cache lifetime: `"implicit"` (automatic prefix caching) or an explicit TTL. Each provider honours only
|
|
28
|
+
* the values it supports; others fall back to its default with one warning. Omit for the provider default.
|
|
33
29
|
*/
|
|
34
|
-
|
|
35
|
-
export type CacheRetention = "implicit" | "short" | "medium" | "long";
|
|
30
|
+
export type CacheOption = "implicit" | "5m" | "30m" | "1h" | "12h";
|
|
36
31
|
|
|
37
|
-
/**
|
|
38
|
-
export
|
|
39
|
-
/**
|
|
40
|
-
* Retention duration:
|
|
41
|
-
* - "implicit": automatic prefix caching ($0.00 storage fee, no explicit cloud cache entities created)
|
|
42
|
-
* - "short": 5 minutes TTL
|
|
43
|
-
* - "medium": 1 hour TTL
|
|
44
|
-
* - "long": 12 hours TTL
|
|
45
|
-
* - undefined: no explicit caching enforced (implicit may still happen)
|
|
46
|
-
*/
|
|
47
|
-
retention?: CacheRetention;
|
|
48
|
-
/**
|
|
49
|
-
* Explicit cache ID / reference if using pre-created context cache
|
|
50
|
-
*/
|
|
51
|
-
cachedContentId?: string;
|
|
52
|
-
/**
|
|
53
|
-
* Session ID for cache affinity routing (e.g. x-session-id)
|
|
54
|
-
*/
|
|
55
|
-
sessionId?: string;
|
|
56
|
-
/**
|
|
57
|
-
* Explicit TTL in seconds for created caches (overrides retention mapping)
|
|
58
|
-
*/
|
|
59
|
-
ttlSeconds?: number;
|
|
60
|
-
}
|
|
32
|
+
/** Every accepted {@link CacheOption}, for runtime validation. */
|
|
33
|
+
export const CACHE_OPTIONS: readonly CacheOption[] = ["implicit", "5m", "30m", "1h", "12h"];
|
|
61
34
|
|
|
62
35
|
/**
|
|
63
36
|
* Service tier controls — bloatfree: only flex / priority (standard is default, no flag needed)
|
package/src/types/message.ts
CHANGED
|
@@ -9,6 +9,9 @@ export interface TextPart {
|
|
|
9
9
|
thoughtSignature?: string;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
+
/** Detail level a provider spends on one image or video (more detail = more input tokens). Google only; other providers ignore it with a warning. */
|
|
13
|
+
export type MediaResolution = "low" | "medium" | "high" | "ultra_high";
|
|
14
|
+
|
|
12
15
|
/** Image content accepted as a URL, data, path, or binary value. */
|
|
13
16
|
export interface ImagePart {
|
|
14
17
|
type: "image";
|
|
@@ -17,6 +20,8 @@ export interface ImagePart {
|
|
|
17
20
|
*/
|
|
18
21
|
image: string | Uint8Array | ArrayBuffer;
|
|
19
22
|
mimeType?: string;
|
|
23
|
+
/** Detail level for this image (Google: `low` 280, `medium` 560, `high` 1120, `ultra_high` 2240 tokens). */
|
|
24
|
+
resolution?: MediaResolution;
|
|
20
25
|
}
|
|
21
26
|
|
|
22
27
|
/** Audio content accepted as a URL, data, path, or binary value. */
|
|
@@ -48,6 +53,8 @@ export interface VideoPart {
|
|
|
48
53
|
*/
|
|
49
54
|
video: string | Uint8Array | ArrayBuffer;
|
|
50
55
|
mimeType?: string;
|
|
56
|
+
/** Detail level per frame (Google: `low`/`medium` 70, `high` 280 tokens). */
|
|
57
|
+
resolution?: MediaResolution;
|
|
51
58
|
}
|
|
52
59
|
|
|
53
60
|
/** Assistant-generated structured tool invocation. */
|
|
@@ -100,12 +107,20 @@ export type ContentPart =
|
|
|
100
107
|
|
|
101
108
|
export type MessageRole = "system" | "user" | "assistant" | "tool";
|
|
102
109
|
|
|
110
|
+
/** A provider's raw output steps for one assistant turn, kept to replay history byte-exact (only the tagged provider reads them). */
|
|
111
|
+
export interface ProviderSteps {
|
|
112
|
+
provider: string;
|
|
113
|
+
steps: unknown[];
|
|
114
|
+
}
|
|
115
|
+
|
|
103
116
|
/** Normalized conversation message. */
|
|
104
117
|
export interface Message {
|
|
105
118
|
role: MessageRole;
|
|
106
119
|
content: string | ContentPart[];
|
|
107
120
|
name?: string;
|
|
108
121
|
thoughtSignature?: string;
|
|
122
|
+
/** Raw provider output steps of an assistant turn (opaque; e.g. Google `steps`). */
|
|
123
|
+
providerSteps?: ProviderSteps;
|
|
109
124
|
}
|
|
110
125
|
|
|
111
126
|
/**
|
|
@@ -116,5 +131,4 @@ export interface ProviderContext {
|
|
|
116
131
|
systemPrompt?: string;
|
|
117
132
|
messages: Message[];
|
|
118
133
|
tools?: Record<string, unknown>[];
|
|
119
|
-
cachedContentId?: string;
|
|
120
134
|
}
|
package/src/types/model.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { ThinkingConfig,
|
|
2
|
-
import type { ProviderContext } from "./message.ts";
|
|
1
|
+
import type { ThinkingConfig, CacheOption, ServiceTier, TokenUsage } from "./core.ts";
|
|
2
|
+
import type { ProviderContext, ProviderSteps } from "./message.ts";
|
|
3
3
|
import type { StandardToolDeclaration, ToolCallRecord } from "./tool.ts";
|
|
4
4
|
import type { AssistantMessageEventStream } from "../streaming/event-stream.ts";
|
|
5
5
|
|
|
@@ -166,7 +166,7 @@ export interface ProviderRequestOptions {
|
|
|
166
166
|
baseUrl?: string;
|
|
167
167
|
headers?: Record<string, string>;
|
|
168
168
|
thinking?: ThinkingConfig;
|
|
169
|
-
cache?:
|
|
169
|
+
cache?: CacheOption;
|
|
170
170
|
serviceTier?: ServiceTier;
|
|
171
171
|
tools?: StandardToolDeclaration[];
|
|
172
172
|
toolChoice?: "auto" | "none" | "required" | { type: "function"; function: { name: string } };
|
|
@@ -226,6 +226,8 @@ export interface ProviderGenerateResult {
|
|
|
226
226
|
provider: ProviderId;
|
|
227
227
|
raw: ProviderRawData;
|
|
228
228
|
durationMs: number;
|
|
229
|
+
/** Raw output steps for exact history replay (stored on the assistant message). */
|
|
230
|
+
providerSteps?: ProviderSteps;
|
|
229
231
|
}
|
|
230
232
|
|
|
231
233
|
/** Provider contract implemented by native REST transports. */
|
|
@@ -244,4 +246,6 @@ export interface Provider {
|
|
|
244
246
|
context: ProviderContext,
|
|
245
247
|
options?: ProviderRequestOptions
|
|
246
248
|
): AssistantMessageEventStream;
|
|
249
|
+
/** True when this call may legitimately run far past the request timeouts (e.g. Google long decode); the loop then leaves timing to the adapter's own per-request timeouts. */
|
|
250
|
+
isLongRunning?(model: string | ModelSpec, options?: ProviderRequestOptions): boolean;
|
|
247
251
|
}
|
package/src/types/response.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { TokenUsage } from "./core.ts";
|
|
2
2
|
import type { ToolCallRecord, ToolResultRecord } from "./tool.ts";
|
|
3
3
|
import type { ProviderRawData, ProviderId } from "./model.ts";
|
|
4
|
+
import type { ProviderSteps } from "./message.ts";
|
|
4
5
|
|
|
5
6
|
/** One logged step inside a sub-agent worker run (surfaced via `agent.track(id)`). */
|
|
6
7
|
export interface SubAgentStep {
|
|
@@ -142,6 +143,8 @@ export class AgentResponse {
|
|
|
142
143
|
readonly durationMs: number;
|
|
143
144
|
readonly raw: ProviderRawData;
|
|
144
145
|
readonly turns: number;
|
|
146
|
+
/** Raw provider output steps of a single streamed turn (history replay only; not in `toJSON`). */
|
|
147
|
+
readonly providerSteps?: ProviderSteps;
|
|
145
148
|
|
|
146
149
|
/**
|
|
147
150
|
* Constructs a normalized response. Agent runs create this automatically;
|
|
@@ -167,6 +170,7 @@ export class AgentResponse {
|
|
|
167
170
|
durationMs: number;
|
|
168
171
|
raw: ProviderRawData;
|
|
169
172
|
turns?: number;
|
|
173
|
+
providerSteps?: ProviderSteps;
|
|
170
174
|
}) {
|
|
171
175
|
this.text = data.text;
|
|
172
176
|
if (data.parsed !== undefined) (this as { parsed?: unknown }).parsed = data.parsed;
|
|
@@ -184,6 +188,7 @@ export class AgentResponse {
|
|
|
184
188
|
this.durationMs = data.durationMs;
|
|
185
189
|
this.raw = data.raw;
|
|
186
190
|
this.turns = data.turns ?? 1;
|
|
191
|
+
if (data.providerSteps) this.providerSteps = data.providerSteps;
|
|
187
192
|
}
|
|
188
193
|
|
|
189
194
|
/** Returns the final assistant text. */
|
package/src/utils/cache.ts
CHANGED
|
@@ -1,14 +1,4 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Battle-tested cache helper — single source for 80-90% hit rate
|
|
3
|
-
* Based on agent-accel: openai-prompt-cache.ts + applyAnthropicCacheControl
|
|
4
|
-
* - First turn already cache-optimized: system + tools + first user get cache_control
|
|
5
|
-
* - Stable sessionId (64 clamp) + prompt_cache_key ensures affinity
|
|
6
|
-
* - 4 breakpoint cap (Anthropic limit)
|
|
7
|
-
* - Retention mapping: short=5m (no ttl), medium=1h, long=24h (prompt) / 1h (anthropic)
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import type { CacheRetention } from "../types/core.ts";
|
|
11
|
-
import { getApiKey } from "./env.ts";
|
|
1
|
+
/** Cache-key clamping and central secret redaction shared by the provider adapters. */
|
|
12
2
|
|
|
13
3
|
const CACHE_KEY_MAX = 64;
|
|
14
4
|
|
|
@@ -20,132 +10,6 @@ export function clampCacheKey(key?: string): string | undefined {
|
|
|
20
10
|
return chars.slice(0, CACHE_KEY_MAX).join("");
|
|
21
11
|
}
|
|
22
12
|
|
|
23
|
-
/** Maps retention to explicit-cache TTL seconds. short=5m, medium=1h, long=12h. Explicit ttlSeconds wins. */
|
|
24
|
-
export function retentionToTtlSeconds(retention?: CacheRetention, ttlSeconds?: number): number | undefined {
|
|
25
|
-
if (ttlSeconds && ttlSeconds > 0) return Math.floor(ttlSeconds);
|
|
26
|
-
if (!retention || retention === "implicit") return undefined;
|
|
27
|
-
if (retention === "short") return 300;
|
|
28
|
-
if (retention === "medium") return 3600;
|
|
29
|
-
if (retention === "long") return 43200;
|
|
30
|
-
return undefined;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/** Maps retention settings to OpenRouter prompt-cache TTL values. */
|
|
34
|
-
export function getPromptCacheRetention(retention?: CacheRetention, supportsLong = true): "24h" | "1h" | undefined {
|
|
35
|
-
if (!retention || retention === "implicit") return undefined;
|
|
36
|
-
if (retention === "long" && supportsLong) return "24h";
|
|
37
|
-
if (retention === "medium" && supportsLong) return "1h";
|
|
38
|
-
if (retention === "long") return "24h"; // fallback even if supportsLong false, provider may ignore
|
|
39
|
-
return undefined;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// ============================================================================
|
|
43
|
-
// Explicit Context Caching (Google cachedContents API)
|
|
44
|
-
// ============================================================================
|
|
45
|
-
|
|
46
|
-
export interface CachedContentMetadata {
|
|
47
|
-
name: string;
|
|
48
|
-
displayName?: string;
|
|
49
|
-
model: string;
|
|
50
|
-
createTime: string;
|
|
51
|
-
updateTime: string;
|
|
52
|
-
expireTime: string;
|
|
53
|
-
usageMetadata?: {
|
|
54
|
-
totalTokenCount?: number;
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export interface CreateExplicitCacheOptions {
|
|
59
|
-
model: string;
|
|
60
|
-
contents?: any[];
|
|
61
|
-
systemInstruction?: string;
|
|
62
|
-
tools?: any[];
|
|
63
|
-
toolConfig?: any;
|
|
64
|
-
displayName?: string;
|
|
65
|
-
ttlSeconds?: number;
|
|
66
|
-
retention?: CacheRetention;
|
|
67
|
-
expireTime?: string | Date;
|
|
68
|
-
apiKey?: string;
|
|
69
|
-
baseUrl?: string;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* Creates an explicit cached content object using Google Gemini's cachedContents API.
|
|
74
|
-
* REST: POST https://generativelanguage.googleapis.com/v1beta/cachedContents (key in the `x-goog-api-key` header)
|
|
75
|
-
*/
|
|
76
|
-
/**
|
|
77
|
-
* Creates a Google Gemini cachedContents resource through the REST API.
|
|
78
|
-
*
|
|
79
|
-
* @example `const cache = await createExplicitCache({ model: "google/gemini-3.5-flash-lite", contents });`
|
|
80
|
-
*/
|
|
81
|
-
export async function createExplicitCache(
|
|
82
|
-
options: CreateExplicitCacheOptions
|
|
83
|
-
): Promise<CachedContentMetadata> {
|
|
84
|
-
const apiKey = getApiKey("google", options.apiKey);
|
|
85
|
-
if (!apiKey) {
|
|
86
|
-
throw new Error("API key is required to create explicit cache (GEMINI_API_KEY)");
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
const baseUrl = options.baseUrl || "https://generativelanguage.googleapis.com/v1beta";
|
|
90
|
-
const ttlSeconds = retentionToTtlSeconds(options.retention, options.ttlSeconds) ?? 3600;
|
|
91
|
-
const ttl = options.expireTime ? undefined : `${ttlSeconds}s`;
|
|
92
|
-
|
|
93
|
-
let modelName = options.model;
|
|
94
|
-
if (!modelName.startsWith("models/")) {
|
|
95
|
-
modelName = `models/${modelName.replace(/^google\//, "")}`;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
const payload: Record<string, unknown> = {
|
|
99
|
-
model: modelName,
|
|
100
|
-
contents: options.contents ?? [],
|
|
101
|
-
...(ttl ? { ttl } : {}),
|
|
102
|
-
...(options.expireTime
|
|
103
|
-
? { expireTime: options.expireTime instanceof Date ? options.expireTime.toISOString() : options.expireTime }
|
|
104
|
-
: {}),
|
|
105
|
-
};
|
|
106
|
-
|
|
107
|
-
if (options.displayName) {
|
|
108
|
-
payload.displayName = options.displayName;
|
|
109
|
-
(payload as any).display_name = options.displayName;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
if (options.systemInstruction) {
|
|
113
|
-
payload.systemInstruction = {
|
|
114
|
-
parts: [{ text: options.systemInstruction }],
|
|
115
|
-
};
|
|
116
|
-
(payload as any).system_instruction = payload.systemInstruction;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
if (options.tools && options.tools.length > 0) {
|
|
120
|
-
payload.tools = options.tools;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
if (options.toolConfig) {
|
|
124
|
-
payload.toolConfig = options.toolConfig;
|
|
125
|
-
(payload as any).tool_config = options.toolConfig;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// The key travels in a header, never in the URL (URLs end up in proxy and access logs).
|
|
129
|
-
const url = `${baseUrl}/cachedContents`;
|
|
130
|
-
const response = await fetch(url, {
|
|
131
|
-
method: "POST",
|
|
132
|
-
headers: {
|
|
133
|
-
"Content-Type": "application/json",
|
|
134
|
-
"x-goog-api-key": apiKey,
|
|
135
|
-
},
|
|
136
|
-
body: JSON.stringify(payload),
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
if (!response.ok) {
|
|
140
|
-
const errorBody = await response.text();
|
|
141
|
-
throw new Error(
|
|
142
|
-
`Failed to create explicit cache (${response.status} ${response.statusText}): ${errorBody}`
|
|
143
|
-
);
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
return (await response.json()) as CachedContentMetadata;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
13
|
// ---------------------------------------------------------------------------
|
|
150
14
|
// Central redaction helpers (Q-11, temporary home to avoid owner conflicts).
|
|
151
15
|
// Providers must call these instead of maintaining local deny-lists.
|
package/src/utils/errors.ts
CHANGED
|
@@ -140,11 +140,8 @@ export function assertModalitiesSupported(
|
|
|
140
140
|
if (!supported) return;
|
|
141
141
|
const missing = [...needed].filter((k) => !supported.includes(k));
|
|
142
142
|
if (missing.length === 0) return;
|
|
143
|
-
const docHint = missing.includes("pdf")
|
|
144
|
-
? " Documents can be converted client-side with the convert_document_to_markdown tool (bun add @firecrawl/anydoc) or Agent bypassInputFileModality: true."
|
|
145
|
-
: "";
|
|
146
143
|
const err = new Error(
|
|
147
|
-
`[${displayModel(providerId, modelId)}] unsupported ${missing.join("+")} input (supports: ${supported.join(", ") || "text"}). Use a capable model or drop the ${missing.join("+")} part
|
|
144
|
+
`[${displayModel(providerId, modelId)}] unsupported ${missing.join("+")} input (supports: ${supported.join(", ") || "text"}). Use a capable model or drop the ${missing.join("+")} part.`
|
|
148
145
|
);
|
|
149
146
|
err.name = "AgentAccelProviderError";
|
|
150
147
|
Object.defineProperties(err, {
|
package/src/utils/headers.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { ProviderId } from "../types/model.ts";
|
|
2
|
-
import type { CacheConfig } from "../types/core.ts";
|
|
3
2
|
import { clampCacheKey } from "./cache.ts";
|
|
4
3
|
|
|
5
4
|
export function isBrowserRuntime(): boolean {
|
|
@@ -24,7 +23,7 @@ export function getPackageVersion(): string {
|
|
|
24
23
|
const v = (globalThis as any).process?.env?.npm_package_version;
|
|
25
24
|
if (typeof v === "string" && v) return v;
|
|
26
25
|
} catch {}
|
|
27
|
-
return "0.5.
|
|
26
|
+
return "0.5.2";
|
|
28
27
|
}
|
|
29
28
|
|
|
30
29
|
function getAgentAccelUserAgent(): string {
|
|
@@ -106,19 +105,6 @@ export function setHeaderCaseInsensitive(
|
|
|
106
105
|
// Thought-signature namespacing (Q-18).
|
|
107
106
|
// ---------------------------------------------------------------------------
|
|
108
107
|
|
|
109
|
-
/**
|
|
110
|
-
* Tags a captured thought signature with its issuing provider so history
|
|
111
|
-
* builders echo it only on the same provider. Untagged (legacy) signatures
|
|
112
|
-
* echo everywhere for backward compat.
|
|
113
|
-
*/
|
|
114
|
-
export function withProviderSignature(
|
|
115
|
-
sig: string | undefined,
|
|
116
|
-
provider: string
|
|
117
|
-
): { thoughtSignature?: string; thoughtSignatureProvider?: string } {
|
|
118
|
-
if (!sig) return {};
|
|
119
|
-
return { thoughtSignature: sig, thoughtSignatureProvider: provider };
|
|
120
|
-
}
|
|
121
|
-
|
|
122
108
|
/**
|
|
123
109
|
* Returns true when a stored signature may be echoed on `currentProvider`:
|
|
124
110
|
* untagged (legacy) echoes everywhere, tagged echoes only on a match.
|
|
@@ -163,11 +149,10 @@ function stripForBrowser(headers: Record<string, string>): Record<string, string
|
|
|
163
149
|
* runtimes this returns only CORS-safe attribution headers (OpenRouter
|
|
164
150
|
* `HTTP-Referer` / `X-Title`); session affinity still flows via `providerOptions`
|
|
165
151
|
* (`promptCacheKey`), never via headers.
|
|
166
|
-
* @example `const headers = buildSessionHeaders("google",
|
|
152
|
+
* @example `const headers = buildSessionHeaders("google", undefined, "session-123");`
|
|
167
153
|
*/
|
|
168
154
|
export function buildSessionHeaders(
|
|
169
155
|
provider: ProviderId | string,
|
|
170
|
-
cache?: CacheConfig,
|
|
171
156
|
customHeaders?: Record<string, string>,
|
|
172
157
|
explicitSessionId?: string
|
|
173
158
|
): Record<string, string> {
|
|
@@ -183,8 +168,7 @@ export function buildSessionHeaders(
|
|
|
183
168
|
headers[k] = v;
|
|
184
169
|
}
|
|
185
170
|
|
|
186
|
-
const
|
|
187
|
-
const sessionId = clampCacheKey(rawSessionId);
|
|
171
|
+
const sessionId = clampCacheKey(explicitSessionId);
|
|
188
172
|
const isFirstClass = FIRST_CLASS.has(provider);
|
|
189
173
|
|
|
190
174
|
const getHeader = (name: string): string | undefined => {
|
package/src/utils/retry.ts
CHANGED
|
@@ -43,7 +43,8 @@ function readRetryAfterMs(error: unknown): number | undefined {
|
|
|
43
43
|
return Math.min(60_000, secs * 1000);
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
// Waits `ms`, rejecting with an AbortError as soon as `signal` aborts; in: retry backoff and Google background polling, out: the awaiting caller.
|
|
47
|
+
export function sleep(ms: number, signal?: AbortSignal): Promise<void> {
|
|
47
48
|
if (ms <= 0) return Promise.resolve();
|
|
48
49
|
return new Promise((resolve, reject) => {
|
|
49
50
|
const timer = setTimeout(() => {
|
package/src/utils/safe-io.ts
CHANGED
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
import type { Message } from "../types/message.ts";
|
|
4
4
|
import { SecurityPolicyError } from "../types/errors.ts";
|
|
5
5
|
|
|
6
|
-
/** Default cap for one remote media
|
|
6
|
+
/** Default cap for one remote media download (bytes). */
|
|
7
7
|
export const MAX_REMOTE_FETCH_BYTES = 200_000_000;
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
-
* True when a media
|
|
10
|
+
* True when a media string would be read from the local filesystem
|
|
11
11
|
* by `normalizeMediaInput` (i.e. it is not a data URL, an http(s) URL, or an
|
|
12
12
|
* embedded base64 payload). Mirrors the classification order used there.
|
|
13
13
|
*/
|