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 CHANGED
@@ -17,7 +17,7 @@ import { Agent } from "agent-accelerator";
17
17
  const agent = new Agent({
18
18
  model: "google/gemini-3.5-flash-lite",
19
19
  instructions: "You are a concise research engineer.",
20
- cache: { retention: "short" },
20
+ cache: "implicit",
21
21
  });
22
22
 
23
23
  const res = await agent.run("Explain HBM pricing in 3 bullets");
@@ -32,10 +32,10 @@ console.log(res.usage);
32
32
 
33
33
  ```bash
34
34
  # Bun (recommended)
35
- bun add agent-accelerator
35
+ bun add agent-accelerator zod
36
36
 
37
37
  # npm
38
- npm install agent-accelerator
38
+ npm install agent-accelerator zod
39
39
 
40
40
  # pnpm
41
41
  pnpm add agent-accelerator
@@ -46,7 +46,7 @@ yarn add agent-accelerator
46
46
 
47
47
  Requires `bun` or `node 22+`.
48
48
 
49
- Core dependencies are `zod` only. Document conversion additionally uses the optional `@firecrawl/anydoc` peer. All provider transports are native REST (`fetch`, no SDK).
49
+ The only dependency is `zod` (a peer dependency: install it next to the SDK and import `z` from `"zod"`). All provider transports are native REST (`fetch`, no SDK).
50
50
 
51
51
  ---
52
52
 
@@ -123,9 +123,8 @@ An `Agent` holds:
123
123
  | `subagentModel` | `string \| ModelSpec \| ModelProviderInstance` | Developer-only model for dynamically spawned workers. Never choosable by the Main Agent. Deprecated alias of `dynamicSubagents.model` (which wins). Falls back to `SUB_AGENT_MODEL`. |
124
124
  | `dynamicSubagents` | `DynamicSubagentsConfig` | Enables and constrains LLM-spawned stateless workers: `{ enabled, model?, maxSpawn?, thinkingLevel?, tools?, timeout?, allowScheduledActions? }`. See [Pre-defined vs Dynamic Delegation](#pre-defined-vs-dynamic-delegation) and [Scheduled actions](#scheduled-actions). |
125
125
  | `thinking` | `ThinkingOption` | Canonical reasoning option: `none \| dynamic \| minimal \| low \| medium \| high \| xhigh \| max`, or `{ level, budgetTokens }`. Legacy `thinkingLevel` still works (canonical wins). Validated against the model catalog before a request. |
126
- | `cache` | `CacheConfig` | `{ retention, sessionId, cachedContentId, ttlSeconds }`. Controls cache reuse. |
126
+ | `cache` | `CacheOption` | `"implicit" \| "5m" \| "30m" \| "1h" \| "12h"`. Prompt-cache lifetime; unsupported values warn and fall back (see [Cache](#cache)). |
127
127
  | `serviceTier` | `"flex" \| "priority"` | Cost / priority routing where supported. Omit for standard routing. |
128
- | `bypassInputFileModality` | `boolean` | When `true`, `file` parts are converted client-side to Markdown for models lacking native support. Capable models still receive files natively. Defaults to `false`. |
129
128
  | `maxTurns` | `number` | Maximum model → tool → model loops per `run`. Infinite by default (`0` or omitted = no limit). Hitting a finite limit with pending tool calls warns and sets `finishReason: "max_turns"`. |
130
129
  | `sessionId` | `string` | Stable identifier used for cache affinity. Auto-generated when omitted. |
131
130
  | `headers` | `Record<string,string>` | Additional headers merged into every request. |
@@ -239,7 +238,6 @@ const lead = new Agent({
239
238
  ### Differences from Agent
240
239
 
241
240
  * `model` resolves as `config.model ?? SUB_AGENT_MODEL ?? MODEL` and throws if empty.
242
- * `cache` defaults to `{ retention: "short" }`.
243
241
  * `.asTool(name?, desc?)` / `.toTool(name?, desc?)` converts the subagent into a `ToolDefinition` for manual registration.
244
242
  * `stateless: true` is supported for one-shot judges that must not retain history.
245
243
 
@@ -378,7 +376,8 @@ telemetryBus.onEvent((e) => {
378
376
  ## Tools
379
377
 
380
378
  ```ts
381
- import { Agent, tool, z } from "agent-accelerator";
379
+ import { Agent, tool } from "agent-accelerator";
380
+ import { z } from "zod";
382
381
 
383
382
  const agent = new Agent({
384
383
  model: "google/gemini-3.5-flash-lite",
@@ -404,7 +403,6 @@ const agent = new Agent({
404
403
  * `zodToJsonSchema(schema)` — converts Zod schemas using native conversion with a fallback extractor.
405
404
  * `cleanJsonSchema(schema)` — removes `$schema`, `$defs`, and `definitions`, resolves `$ref`, and preserves explicit `additionalProperties`.
406
405
  * `executeToolCalls({ tools, toolCalls, agentName?, parallel?, signal?, sessionId? })` — executes tool calls. Calls validate Zod input, use bounded parallelism, enforce per-tool timeouts, retry transient failures, recover namespace/camel-case tool-name aliases, and return `ToolResultRecord[]`. Missing tools and aborts are represented as error results rather than thrown.
407
- * `convert_document_to_markdown` — built-in, model-callable version of `convertDocumentToMarkdown` (PDF/Word/PowerPoint/Excel/OpenDocument/RTF/EPUB/CSV, local path or URL) for models without native document parsing. Returns a `<Document>` envelope, is auto-registered with `bypassInputFileModality: true`, and requires the optional `@firecrawl/anydoc` peer.
408
406
 
409
407
  The agent loop answers the 3rd consecutive identical call (same name and arguments) with a `repeat_blocked` error, so the model can reuse the prior result or change its arguments. Tools marked `repeatable: true`, or `idempotent: true` unless `repeatable` is set, are exempt.
410
408
 
@@ -522,6 +520,24 @@ Model resolution never silently selects a default model: an empty model throws.
522
520
 
523
521
  An unknown prefix with no catalog match still routes to a custom provider, allowing private model IDs and endpoints.
524
522
 
523
+ ### Google long decode (Gemini 4)
524
+
525
+ Google caps one request at 65,536 output tokens. Set `maxOutputTokens` above
526
+ that on a `gemini-4-*` model and the SDK runs the request as a background
527
+ interaction automatically (no extra option): up to 1,048,576 tokens minus the
528
+ input, for up to 24 hours. `run()` polls until it finishes (2 s, then every
529
+ 5 s); `stream()` reconnects after a dropped or silent connection
530
+ (`last_event_id`, 15 min of silence counts as dropped). Aborting cancels the
531
+ interaction on Google's side, so it stops billing. Each HTTP request is still
532
+ timed by `limits.requestTimeoutMs`, but the whole run is not. Any other Google
533
+ model with `maxOutputTokens` above 65,536 fails before the network with a
534
+ `ConfigError`. Use it for hard reasoning, not chat: you pay for every token of
535
+ every 64K hop.
536
+
537
+ A custom `Provider` can opt into the same treatment with the optional
538
+ `isLongRunning(model, options)` method: when it returns `true`, the loop skips
539
+ its whole-call, first-byte and idle timers and leaves timing to the adapter.
540
+
525
541
  ### Registry
526
542
 
527
543
  * `resolveModel("google/x" | ModelSpec)` → `{ provider, modelId, modelSpec? }`. Use to inspect routing before execution.
@@ -587,11 +603,10 @@ Google-specific handling includes:
587
603
 
588
604
  * Isolating `thoughtSignature` per part for prefix stability.
589
605
  * Converting JSON Schema to OpenAPI 3.0 through `stripSchemaForGoogle`.
590
- * Implicit/automatic caching only; explicit retention and `cachedContentId` are dropped (see [Cache](#cache)).
606
+ * Implicit caching only; other `cache` values warn and fall back (see [Cache](#cache)).
591
607
 
592
608
  Helpers:
593
609
 
594
- * `createExplicitCache({ model, systemInstruction?, contents?, tools?, displayName?, ttlSeconds?, expireTime?, apiKey?, baseUrl? })`
595
610
  * `isValidThoughtSignature(sig)`
596
611
  * `retainThoughtSignature(existing, incoming)`
597
612
  * `extractGoogleThoughtSignature(obj)`
@@ -724,26 +739,20 @@ Fixed-reasoning models reject `none`.
724
739
  ## Cache
725
740
 
726
741
  ```ts
727
- CacheConfig {
728
- retention?,
729
- cachedContentId?,
730
- sessionId?,
731
- ttlSeconds?
732
- }
742
+ cache?: "implicit" | "5m" | "30m" | "1h" | "12h" // CacheOption; omit for the provider default
733
743
  ```
734
744
 
735
- ```ts
736
- CacheRetention =
737
- | "implicit"
738
- | "short"
739
- | "medium"
740
- | "long";
741
- ```
745
+ One value sets the prompt-cache lifetime. Each provider honours only the values it supports; any other value falls back to the provider default and emits one `provider_warning` naming the supported values.
742
746
 
743
- ### Retention
747
+ | Provider | Supported | Sent on the wire |
748
+ | --- | --- | --- |
749
+ | Google (Interactions) | `implicit` | nothing (implicit caching is always on) |
750
+ | OpenRouter, `anthropic/*` models | `implicit`, `5m`, `1h` | top-level `cache_control: { type: "ephemeral", ttl }` |
751
+ | OpenRouter, other models | `implicit` | nothing (automatic caching + sticky routing) |
752
+ | OpenAI | `implicit`, `12h` | `12h` sends `prompt_cache_retention: "24h"` when the catalog allows long retention |
753
+ | Custom endpoints | `implicit` | nothing (strict endpoints reject unknown fields) |
744
754
 
745
- * `implicit` — automatic prefix reuse without an explicit cache object or storage fee. This is the only retention the native adapters act on (by doing nothing special — stable prompts plus session affinity do the work).
746
- * `short` / `medium` / `long` — TTL hints (`5m` / `1h` / `12h`) consumed only by the opt-in `createExplicitCache` helper. Provider adapters warn about and drop any non-`implicit` retention: none of the native transports expose retention control.
755
+ Session files written before 0.5.1 with the old `{ retention }` object still load (`short`=`5m`, `medium`=`1h`, `long`=`12h`). Passing the object form to `new Agent()` throws `ConfigError`.
747
756
 
748
757
  ### Session Affinity
749
758
 
@@ -757,26 +766,7 @@ Reuse the same session ID across turns when cache affinity is desired.
757
766
 
758
767
  In browser runtimes custom `x-*` headers are stripped to avoid CORS preflights: OpenAI keeps affinity through its body key, while custom endpoints lose affinity entirely on browsers.
759
768
 
760
- ### Explicit Caches
761
-
762
- `createExplicitCache` mints a Google `cachedContents/...` resource directly (TTL via `retention`/`ttlSeconds`).
763
-
764
- `cachedContentId` is accepted and carried in context, but no native adapter currently sends it — explicit references are warned about and dropped, so stable prompts plus session affinity remain the cache-reuse path.
765
-
766
- `ttlSeconds` overrides the retention mapping.
767
-
768
- ### Provider Cache Mechanisms
769
-
770
- * **Google** keeps system prompts and tools stable and isolates signatures (implicit caching only).
771
- * **OpenAI** pins affinity with `prompt_cache_key`.
772
- * **OpenRouter** pins affinity with body `session_id` plus headers (sticky routing from the first request).
773
- * **Custom endpoints** get headers-only affinity; anything else cache-related is warned about and dropped.
774
-
775
- The following cache helpers are exported:
776
-
777
- * `getPromptCacheRetention`
778
- * `retentionToTtlSeconds`
779
- * `clampCacheKey`
769
+ `clampCacheKey` (exported) shortens affinity keys to the provider-safe 64 characters.
780
770
 
781
771
  For the best cache reuse, keep `instructions` and the tool set stable. Put changing data in user messages or `additionalContext`.
782
772
 
@@ -1146,18 +1136,26 @@ Message {
1146
1136
  role: "system" | "user" | "assistant" | "tool",
1147
1137
  content: string | ContentPart[],
1148
1138
  name?,
1149
- thoughtSignature?
1139
+ thoughtSignature?,
1140
+ providerSteps? // { provider, steps }: raw output steps of an assistant turn
1150
1141
  }
1151
1142
  ```
1152
1143
 
1144
+ `providerSteps` keeps a provider's raw output for one assistant turn (today:
1145
+ Google's `steps`, including thought signatures). When Google needs the full
1146
+ history again (after a restart, an expired chain or a provider switch), those
1147
+ turns are resent exactly as received, which keeps reasoning continuity and
1148
+ the implicit-cache prefix intact. Other providers ignore it. Adapters return
1149
+ it on `ProviderGenerateResult` / per-turn stream `AgentResponse`; it is never
1150
+ part of `toJSON()`.
1151
+
1153
1152
  ### ProviderContext
1154
1153
 
1155
1154
  ```ts
1156
1155
  ProviderContext {
1157
1156
  systemPrompt?,
1158
1157
  messages,
1159
- tools?,
1160
- cachedContentId?
1158
+ tools?
1161
1159
  }
1162
1160
  ```
1163
1161
 
@@ -1169,9 +1167,9 @@ Supported content parts include:
1169
1167
  * `ThinkingPart { type:"thinking", thinking, thoughtSignature? }` — preserved reasoning.
1170
1168
  * `ToolCallPart { type:"tool_call", id, name, arguments, rawArguments?, thoughtSignature? }`
1171
1169
  * `ToolResultPart { type:"tool_result", id, name, result, isError? }`
1172
- * `ImagePart { type:"image", image, mimeType? }`
1170
+ * `ImagePart { type:"image", image, mimeType?, resolution? }`
1173
1171
  * `AudioPart { type:"audio", audio, mimeType? }`
1174
- * `VideoPart { type:"video", video, mimeType? }`
1172
+ * `VideoPart { type:"video", video, mimeType?, resolution? }`
1175
1173
  * `FilePart { type:"file", file, mimeType?, filename? }` — PDFs/documents.
1176
1174
 
1177
1175
  `image`, `audio`, `video`, and `file` inputs accept:
@@ -1200,6 +1198,20 @@ Media normalization is handled automatically by providers (mapped to provider-na
1200
1198
  Provider matrix: text + image + wav/mp3 audio + PDF work on all supported
1201
1199
  providers. Video works on Gemini only.
1202
1200
 
1201
+ On Google, links Google fetches itself go as a `uri` without downloading:
1202
+ YouTube links, Files API links, `gs://` objects and public `https://` URLs of
1203
+ URL-supported types (PDF, CSV, text, JSON, PNG/JPEG/WebP/BMP, common video;
1204
+ max 15 MB). Audio URLs and `http://` links are downloaded and inlined. Inline
1205
+ types are checked before the network; Google reads only PDF and CSV
1206
+ documents. The supported lists are the `GOOGLE_*_MIME` constants at the top of
1207
+ `src/providers/google.ts`.
1208
+
1209
+ `resolution` (`MediaResolution`: `"low" | "medium" | "high" | "ultra_high"`)
1210
+ sets how much detail Google spends on one image or video, and so how many
1211
+ input tokens it costs (image: 280 / 560 / 1120 / 2240; video per frame:
1212
+ `low` and `medium` 70, `high` 280). Omit it for Google's default. Other
1213
+ providers ignore it with one `provider_warning`.
1214
+
1203
1215
  Before any network call, the executor checks `image` / `audio` / `video` /
1204
1216
  `file`-as-PDF parts against the catalog's `modalities.input` for that
1205
1217
  provider/model and fails fast with a one-line error naming the gap
@@ -1219,8 +1231,6 @@ never in the gitignored snapshot.
1219
1231
  * `getApiKey(provider, explicit?, env?)` — resolves API keys using the configured environment lookup order.
1220
1232
  * `buildSessionHeaders(provider, cache?, custom?, sessionId?)` — builds provider-specific affinity headers. Normally handled automatically.
1221
1233
  * `withRetries(fn, { maxRetries?, maxRetryDelayMs?, signal?, label? })` / `isTransientError(err)` — bounded retries for transient provider failures (defaults: 2 retries, 5s cap, aborts never retried).
1222
- * `convertDocumentToMarkdown(input, { filename?, format?, mimeType?, maxChars? })` — converts PDF/Word/PowerPoint/Excel/OpenDocument/RTF/EPUB/CSV to Markdown via the optional `@firecrawl/anydoc` peer. Throws `DocumentConversionError` on failure.
1223
- * `z` — re-exported from Zod so tools do not require a separate Zod import.
1224
1234
 
1225
1235
  ---
1226
1236
  ## Examples
@@ -1253,9 +1263,6 @@ bun run examples/09-multimodal_document.ts
1253
1263
  bun run examples/08-multimodal_video.ts
1254
1264
  # video input (video-capable model required)
1255
1265
 
1256
- bun run examples/10-document_markdown.ts
1257
- # document → Markdown preprocessing (any model, optional @firecrawl/anydoc peer)
1258
-
1259
1266
  bun run examples/11-session-identity.ts
1260
1267
  # sub-agent TrackingIDs, live worker boxes, lineage, track(), durable sessions
1261
1268
 
@@ -1319,7 +1326,7 @@ saveSessionFile(".session.json", agent, telemetry);
1319
1326
 
1320
1327
  * `agent.exportSession(telemetry)` / `agent.importSession(saved)` round-trip messages, system prompt, model, thinking level, instructions, cache, worker model, and sub-agent traces without touching `AgentContext` internals.
1321
1328
  * `SessionTelemetry` accumulates `input / output / cacheRead / cacheWrite / reasoning / cost`, with clamped `turnHitRate()` / `totalHitRate()`, a dual `formatBar(res)`, and `formatSessionBanner(saved, telemetry, path)` for startup. Cost follows the [TokenUsage](#response-usage-and-raw-data) rules.
1322
- * Files are JSON with one line per top-level field and one line per turn (2-space indent), written atomically (temp + rename).
1329
+ * Files are JSON with one line per top-level field and one line per turn (2-space indent), written atomically (temp + rename). Google turns also store their raw `providerSteps`, so a resumed session replays them exactly.
1323
1330
  * Binary media (bytes, data URLs, base64) is extracted to `media/` on save via `saveSessionDir("sessions", agent, telemetry)` → `sessions/<sessionId>/{session.json, media/*}`; remote URLs and local paths stay references. `loadSessionDir(dir)` resolves `media/…` refs back to absolute paths, and `findLatestSessionDir("sessions")` resumes the most recent session.
1324
1331
  * Sub-agent traces persist in a dedicated `subagents` section keyed by TrackingID (`{ trackingId, name, sessionId, parentSessionId, status, task, turns, usage, steps, text }`). Files stay lean: redundant `rawArguments` and repeated step text are dropped on save (rebuilt/kept live in memory).
1325
1332
  * With `persist: { dir }` or `{ file }`, the session file is rewritten after every step, so a crash loses at most the in-flight step.
@@ -1347,7 +1354,7 @@ src/
1347
1354
  ├── tools/ # tool(), schema, executor
1348
1355
  ├── streaming/ # event stream, SSE parser
1349
1356
  ├── types/ # agent, core, message, model, response, tool
1350
- └── utils/ # base64, cache, env, headers, media, serialization, session, thought-signature, documents, retry, errors
1357
+ └── utils/ # base64, cache, env, headers, media, serialization, session, thought-signature, retry, errors
1351
1358
  examples/ # live demos 01-14 (see Examples), plus files/, prompts/, research-agent.ts, _shared.ts
1352
1359
  test/ # unit + mocked-provider tests mirroring src/
1353
1360
  ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-accelerator",
3
- "version": "0.5.0",
3
+ "version": "0.5.2",
4
4
  "description": "High-performance SDK for AI agents with a unified API across AI providers. Optimized for high cache hit rates, observability, and seamless agent orchestration. Built to power production-grade agentic systems and harnesses for teams at any scale.",
5
5
  "license": "MIT",
6
6
  "author": "Sashvat Bharat",
@@ -42,22 +42,15 @@
42
42
  "scripts": {
43
43
  "typecheck": "tsc --noEmit",
44
44
  "test": "bun test test/",
45
- "update-models": "bun src/update-models.ts"
46
- },
47
- "devDependencies": {
48
- "@firecrawl/anydoc": "^0.2.4",
49
- "@types/bun": "latest",
50
- "typescript": "^7.0.0"
45
+ "update-models": "bun src/update-models.ts",
46
+ "fetch-latest-provider-docs": "bun references/documentations/fetch-latest.ts"
51
47
  },
52
48
  "peerDependencies": {
53
- "@firecrawl/anydoc": "^0.2.4"
54
- },
55
- "peerDependenciesMeta": {
56
- "@firecrawl/anydoc": {
57
- "optional": true
58
- }
49
+ "zod": "^4.4.3"
59
50
  },
60
- "dependencies": {
51
+ "devDependencies": {
52
+ "@types/bun": "latest",
53
+ "typescript": "^7.0.0",
61
54
  "zod": "^4.4.3"
62
55
  }
63
56
  }
@@ -3,7 +3,7 @@ import type { TokenUsage } from "../types/core.ts";
3
3
  import type { ProviderRequestOptions } from "../types/model.ts";
4
4
  import { normalizeThinkingV2, resolveWhenBusy, normalizeWorkerTimeout } from "../tools/tool.ts";
5
5
  import type { ToolDefinition } from "../types/tool.ts";
6
- import type { ThinkingLevel, ThinkingConfig, CacheConfig, ServiceTier } from "../types/core.ts";
6
+ import { CACHE_OPTIONS, type ThinkingLevel, type ThinkingConfig, type CacheOption, type ServiceTier } from "../types/core.ts";
7
7
  import type { ContentPart } from "../types/message.ts";
8
8
  import { AgentResponse, type StreamEvent } from "../types/response.ts";
9
9
  import { AssistantMessageEventStream } from "../streaming/event-stream.ts";
@@ -16,7 +16,6 @@ import {
16
16
  resolveMainModel,
17
17
  } from "./worker.ts";
18
18
  import * as path from "node:path";
19
- import { createConvertDocumentTool } from "../utils/documents.ts";
20
19
  import { registerAgent } from "../scheduler/scheduler.ts";
21
20
  import { resolveHome } from "../scheduler/store.ts";
22
21
  import { persistTelemetry } from "../telemetry-file.ts";
@@ -171,17 +170,13 @@ export class Agent {
171
170
  readonly output?: unknown;
172
171
  /** Normalized structured-output spec sent to providers. */
173
172
  readonly outputSpec?: StructuredOutputSpec;
174
- /** Cache retention/session settings. */
175
- readonly cacheConfig?: CacheConfig;
173
+ /** Prompt-cache lifetime (see `AgentConfig.cache`); unset = provider default. */
174
+ readonly cache?: CacheOption;
176
175
  /** Provider service tier. */
177
176
  readonly serviceTier?: ServiceTier;
178
- /** Convert `file` parts client-side for pdf-incapable models. */
179
- readonly bypassInputFileModality: boolean;
180
177
  /**
181
- * Local-file policy. `roots` set: every local-path media part and the
182
- * built-in document tool must resolve inside them (`[]` denies all paths).
183
- * Unset: developer-supplied parts are unrestricted and the model-callable
184
- * tool is limited to the working directory.
178
+ * Local-file policy. `roots` set: every local-path media part must resolve
179
+ * inside them (`[]` denies all paths). Unset: local paths are unrestricted.
185
180
  */
186
181
  readonly fileAccess?: { roots?: string[] };
187
182
  /** Failure policy for the conversation (see `AgentConfig.onFailure`). */
@@ -310,7 +305,7 @@ export class Agent {
310
305
  : Number.isFinite(config.maxTurns as number)
311
306
  ? Math.max(1, Math.floor(config.maxTurns as number))
312
307
  : Infinity;
313
- this.sessionId = config.sessionId || config.cache?.sessionId || createSessionId();
308
+ this.sessionId = config.sessionId || createSessionId();
314
309
  (this as any).subagentTraces = {};
315
310
  this.home = resolveHome(config.home);
316
311
  // `persist: {}` (neither dir nor file) means the default sessions folder under home.
@@ -346,16 +341,14 @@ export class Agent {
346
341
  this.outputSpec = normalizeStructuredOutput(config.output);
347
342
  }
348
343
 
349
- // DX5: cache retention short|medium|long, undefined = no explicit
350
- this.cacheConfig = {
351
- sessionId: this.sessionId,
352
- ...(config.cache ?? {}),
353
- };
354
- // C11: wire explicit cachedContentId into context for Google explicit cache
355
- const initialCachedId = (this.cacheConfig as any)?.cachedContentId;
344
+ if (config.cache !== undefined && !CACHE_OPTIONS.includes(config.cache)) {
345
+ throw new ConfigError(
346
+ `[Agent Accelerator] Invalid cache ${JSON.stringify(config.cache)}: expected one of ${CACHE_OPTIONS.map((v) => `"${v}"`).join(", ")}, or omit it for the provider default.`
347
+ );
348
+ }
349
+ this.cache = config.cache;
356
350
 
357
351
  this.serviceTier = config.serviceTier;
358
- this.bypassInputFileModality = config.bypassInputFileModality ?? false;
359
352
  this.onFailure =
360
353
  config.onFailure === "rollback" || config.onFailure === "keep" ? config.onFailure : "auto";
361
354
  this.forkConfig = { ...config };
@@ -400,10 +393,6 @@ export class Agent {
400
393
  }
401
394
  }
402
395
 
403
- if (this.bypassInputFileModality && !this.tools["convert_document_to_markdown"]) {
404
- this.tools["convert_document_to_markdown"] = createConvertDocumentTool({ roots: this.fileAccess?.roots });
405
- }
406
-
407
396
  const dyn = this.dynamicSubagents;
408
397
  if ((dyn?.enabled === true || dyn?.allowScheduledActions) && !this.subagentModel) {
409
398
  throw new SubAgentModelError(
@@ -425,9 +414,6 @@ export class Agent {
425
414
  assertNoToolCollisions(this.tools);
426
415
 
427
416
  this.context = new AgentContext(this.getFullInstructions());
428
- if (initialCachedId) {
429
- this.context.cachedContentId = initialCachedId;
430
- }
431
417
  if (dyn?.allowScheduledActions) registerAgent(this);
432
418
  }
433
419
 
@@ -474,7 +460,6 @@ export class Agent {
474
460
  const sid = this.sessionId;
475
461
  this.context.messages = [];
476
462
  this.context.thoughtSignatures = [];
477
- this.context.cachedContentId = (this.cacheConfig as any)?.cachedContentId;
478
463
  this.context.systemPrompt = this.getFullInstructions();
479
464
  // Q-25: reset also drops session-scoped provider chaining/routing.
480
465
  try { clearInteractionChains(sid); } catch {}
@@ -533,10 +518,6 @@ export class Agent {
533
518
  return existing.context;
534
519
  }
535
520
  const fresh = new AgentContext(this.getFullInstructions());
536
- try {
537
- const cached = (this.cacheConfig as unknown as { cachedContentId?: string })?.cachedContentId;
538
- if (cached) fresh.cachedContentId = cached;
539
- } catch {}
540
521
  this.sessions.set(sid, { context: fresh, lastAccess: now });
541
522
  // LRU eviction (oldest lastAccess first).
542
523
  try {
@@ -961,9 +942,7 @@ export class Agent {
961
942
  baseUrl: this.baseUrl,
962
943
  headers: { ...(this.customHeaders ?? {}), ...(runOptions?.headers ?? {}) },
963
944
  thinking: resolveEffectiveThinking(this.thinkingConfig, runOptions?.thinkingLevel),
964
- cache: this.stateless
965
- ? { sessionId }
966
- : { ...this.cacheConfig, sessionId },
945
+ ...(this.cache ? { cache: this.cache } : {}),
967
946
  serviceTier: this.serviceTier,
968
947
  sessionId,
969
948
  ...(this.outputSpec ? { output: this.outputSpec } : {}),
@@ -1013,7 +992,6 @@ export class Agent {
1013
992
  options: providerOptions,
1014
993
  runOptions,
1015
994
  maxTurns: this.maxTurns,
1016
- bypassInputFileModality: this.bypassInputFileModality,
1017
995
  ...(this.fileAccess ? { fileAccess: this.fileAccess } : {}),
1018
996
  onFailure: this.onFailure,
1019
997
  ...(this.captureRaw !== undefined ? { captureRaw: this.captureRaw } : {}),
@@ -1076,7 +1054,6 @@ export class Agent {
1076
1054
  options: providerOptions,
1077
1055
  runOptions,
1078
1056
  maxTurns: this.maxTurns,
1079
- bypassInputFileModality: this.bypassInputFileModality,
1080
1057
  ...(this.fileAccess ? { fileAccess: this.fileAccess } : {}),
1081
1058
  onFailure: this.onFailure,
1082
1059
  ...(this.captureRaw !== undefined ? { captureRaw: this.captureRaw } : {}),
@@ -1291,13 +1268,7 @@ export class Agent {
1291
1268
  : { enabled: true, level: lvl };
1292
1269
  }
1293
1270
  if (typeof data.instructions === "string") this.instructions = data.instructions;
1294
- if (data.cache) {
1295
- (this as any).cacheConfig = {
1296
- ...this.cacheConfig,
1297
- ...data.cache,
1298
- sessionId: data.sessionId ?? this.sessionId,
1299
- };
1300
- }
1271
+ if (data.cache) (this as { cache?: CacheOption }).cache = data.cache;
1301
1272
  if (data.subagentModel) {
1302
1273
  (this as any).subagentModel = data.subagentModel;
1303
1274
  const dyn = (this as any).dynamicSubagents;
@@ -1319,8 +1290,6 @@ export class Agent {
1319
1290
  } else {
1320
1291
  this.context.systemPrompt = this.getFullInstructions();
1321
1292
  }
1322
- this.context.cachedContentId =
1323
- data.cachedContentId ?? (this.cacheConfig as any)?.cachedContentId;
1324
1293
  // Q-25: importing a snapshot invalidates prior provider chaining/routing
1325
1294
  // for both the previous and the restored session.
1326
1295
  try { clearInteractionChains(prevSid); } catch {}
@@ -1383,10 +1352,6 @@ export class Agent {
1383
1352
  } else if (ctx.systemPrompt !== fullInstructions) {
1384
1353
  ctx.systemPrompt = fullInstructions;
1385
1354
  }
1386
- // C11: keep context cachedContentId in sync with cacheConfig if updated via options
1387
- if (options?.headers && (this.cacheConfig as any)?.cachedContentId && !ctx.cachedContentId) {
1388
- ctx.cachedContentId = (this.cacheConfig as any).cachedContentId;
1389
- }
1390
1355
  ctx.addUserMessage(prompt);
1391
1356
  }
1392
1357
 
@@ -1,4 +1,4 @@
1
- import type { Message, ContentPart } from "../types/message.ts";
1
+ import type { Message, ContentPart, ProviderSteps } from "../types/message.ts";
2
2
  import type { ToolCallRecord, ToolResultRecord } from "../types/tool.ts";
3
3
  import { safeStringify } from "../utils/serialization.ts";
4
4
  import { clearSessionRouting } from "../providers.ts";
@@ -27,7 +27,6 @@ export class AgentContext {
27
27
  systemPrompt?: string;
28
28
  messages: Message[] = [];
29
29
  thoughtSignatures: string[] = [];
30
- cachedContentId?: string;
31
30
 
32
31
  constructor(systemPrompt?: string) {
33
32
  this.systemPrompt = systemPrompt;
@@ -57,11 +56,13 @@ export class AgentContext {
57
56
  });
58
57
  }
59
58
 
59
+ // Stores one model turn as an assistant message (text, thinking, tool calls, raw provider steps for replay); in: a provider turn from the loop, out: history.
60
60
  addAssistantMessage(
61
61
  text: string,
62
62
  toolCalls?: ToolCallRecord[],
63
63
  thinking?: string,
64
- thoughtSignature?: string
64
+ thoughtSignature?: string,
65
+ providerSteps?: ProviderSteps
65
66
  ): void {
66
67
  const parts: ContentPart[] = [];
67
68
  const hasToolCalls = Boolean(toolCalls && toolCalls.length > 0);
@@ -111,6 +112,7 @@ export class AgentContext {
111
112
  role: "assistant",
112
113
  content: parts.length > 0 ? parts : text,
113
114
  thoughtSignature,
115
+ ...(providerSteps ? { providerSteps } : {}),
114
116
  });
115
117
 
116
118
  if (thoughtSignature) {
@@ -158,7 +160,6 @@ export class AgentContext {
158
160
  }
159
161
  }
160
162
  next.thoughtSignatures = [...this.thoughtSignatures];
161
- next.cachedContentId = this.cachedContentId;
162
163
  return next;
163
164
  }
164
165
 
@@ -140,10 +140,6 @@ export const SUBAGENT_SUBSCRIBE_TTL_MS = 5 * 60 * 1000;
140
140
  /** Last-write wall-clock per canonical trackingId (drives TTL; reads do not extend it). */
141
141
  const traceTouchedAt = new Map<string, number>();
142
142
 
143
- function canonicalOf(trace: SubAgentTrace): string {
144
- return trace.trackingId;
145
- }
146
-
147
143
  function deleteTraceByCanonical(canonical: string): void {
148
144
  for (const [key, t] of [...subagentTraces]) {
149
145
  if (t.trackingId === canonical) subagentTraces.delete(key);
@@ -381,10 +377,6 @@ function readBudget(parentAgent: Agent): Budget | undefined {
381
377
  return (parentAgent as unknown as { budget?: Budget }).budget;
382
378
  }
383
379
 
384
- function readBudgetState(parentAgent: Agent): BudgetState | undefined {
385
- return (parentAgent as unknown as { budgetState?: BudgetState }).budgetState;
386
- }
387
-
388
380
  function ensureBudgetState(parentAgent: Agent): BudgetState {
389
381
  const host = parentAgent as unknown as { budgetState?: BudgetState };
390
382
  if (!host.budgetState) {
@@ -711,7 +703,7 @@ export function createSubagentSpawnTool(parentAgent: Agent): ToolDefinition {
711
703
  try { (parentAgent as any).persistNow?.(); } catch {}
712
704
  };
713
705
  // Unified live worker state machine (shared with fixed workers).
714
- const { onWorkerTurn, onWorkerDelta, attachToStream } = createLiveWorker(trace, trackingId, displayId, {
706
+ const { onWorkerTurn, attachToStream } = createLiveWorker(trace, trackingId, displayId, {
715
707
  trim: (t) => trimTraceSteps(t),
716
708
  touch: (id) => touchTrace(id),
717
709
  notify: (id) => notifyTrace(id),