agent-accelerator 0.5.9 → 0.6.5

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 (85) hide show
  1. package/README.md +64 -16
  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 +447 -172
  7. package/src/agent/architecture.md +16 -12
  8. package/src/agent/config-blocks.ts +39 -12
  9. package/src/agent/context.ts +9 -3
  10. package/src/agent/defaults.ts +88 -0
  11. package/src/agent/delegation.ts +86 -28
  12. package/src/agent/loop.ts +474 -374
  13. package/src/agent/subagent.ts +1 -3
  14. package/src/agent/subagents-tool.ts +50 -19
  15. package/src/agent/turns.ts +3 -1
  16. package/src/agent/usage-limit.ts +92 -0
  17. package/src/agent/worker.ts +73 -30
  18. package/src/agent/workers.ts +126 -21
  19. package/src/architecture.md +17 -15
  20. package/src/index.ts +43 -19
  21. package/src/models/architecture.md +4 -3
  22. package/src/models/catalog-cache.ts +6 -5
  23. package/src/models/catalog.ts +39 -30
  24. package/src/prompts/additional-context.md +2 -0
  25. package/src/prompts/architecture.md +19 -0
  26. package/src/prompts/prompts.ts +81 -0
  27. package/src/prompts/reasoning-directive.md +5 -0
  28. package/src/{scheduler/scheduled-actions.md → prompts/scheduled-actions-tool.md} +4 -0
  29. package/src/prompts/structured-output.md +2 -0
  30. package/src/{agent/subagents.md → prompts/subagents-tool.md} +3 -0
  31. package/src/prompts/tool-bridge.md +1 -0
  32. package/src/prompts/tool-interrupted.md +1 -0
  33. package/src/prompts/tool-not-found.md +1 -0
  34. package/src/prompts/tool-repeat-blocked.md +1 -0
  35. package/src/prompts/tool-schema-error.md +1 -0
  36. package/src/prompts/tool-truncated-arguments.md +1 -0
  37. package/src/prompts/worker-failed.md +1 -0
  38. package/src/prompts/worker-replied.md +2 -0
  39. package/src/providers/architecture.md +10 -8
  40. package/src/providers/google.ts +228 -69
  41. package/src/providers/openai-compat.ts +74 -56
  42. package/src/providers/openai.ts +64 -41
  43. package/src/providers/openrouter.ts +270 -48
  44. package/src/providers/registry.ts +6 -21
  45. package/src/providers/shared.ts +87 -33
  46. package/src/providers.ts +115 -124
  47. package/src/scheduler/architecture.md +8 -7
  48. package/src/scheduler/scheduler.ts +226 -60
  49. package/src/scheduler/store.ts +23 -10
  50. package/src/scheduler/time.ts +17 -3
  51. package/src/scheduler/tool.ts +38 -16
  52. package/src/session/architecture.md +7 -6
  53. package/src/session/journal.ts +39 -12
  54. package/src/session/store.ts +126 -61
  55. package/src/session/usage.ts +58 -9
  56. package/src/streaming/architecture.md +4 -3
  57. package/src/streaming/event-stream.ts +26 -12
  58. package/src/telemetry.ts +6 -2
  59. package/src/tools/architecture.md +9 -7
  60. package/src/tools/executor.ts +109 -62
  61. package/src/tools/schema.ts +10 -6
  62. package/src/tools/subagent-result.ts +6 -1
  63. package/src/tools/tool-content.ts +32 -0
  64. package/src/tools/tool.ts +9 -11
  65. package/src/types/agent.ts +37 -22
  66. package/src/types/architecture.md +11 -10
  67. package/src/types/core.ts +5 -9
  68. package/src/types/errors.ts +26 -4
  69. package/src/types/message.ts +7 -0
  70. package/src/types/model.ts +4 -5
  71. package/src/types/scheduler.ts +4 -4
  72. package/src/types/tool.ts +14 -2
  73. package/src/update-models.ts +5 -5
  74. package/src/utils/architecture.md +16 -15
  75. package/src/utils/cache.ts +1 -3
  76. package/src/utils/durable.ts +17 -5
  77. package/src/utils/env.ts +9 -7
  78. package/src/utils/errors.ts +9 -17
  79. package/src/utils/headers.ts +3 -10
  80. package/src/utils/media.ts +23 -8
  81. package/src/utils/raw-capture.ts +9 -2
  82. package/src/utils/retry.ts +15 -3
  83. package/src/utils/safe-io.ts +29 -7
  84. package/src/utils/serialization.ts +1 -4
  85. package/src/utils/session.ts +1 -2
@@ -15,9 +15,15 @@ import { sessionDirFor, loadSessionDir } from "../session/store.ts";
15
15
  import { telemetryBus } from "../telemetry.ts";
16
16
  import { hashSessionPart, newRunId, newTurnId, nowMs } from "../utils/session.ts";
17
17
  import { buildDynamicWorkerConfig, grantWorkerTools, sanitizeToolName } from "./worker.ts";
18
- import { createFixedChildSessionId, openWorkerTrace, reserveSpawn, workerMetadata, type WorkerRunResult } from "./delegation.ts";
18
+ import {
19
+ createFixedChildSessionId,
20
+ openWorkerTrace,
21
+ reserveSpawn,
22
+ workerMetadata,
23
+ type WorkerRunResult,
24
+ } from "./delegation.ts";
19
25
  import { finishedTurns, historyUpTo, type Turn } from "./turns.ts";
20
- import { modelName, readUsage, type UsageNode } from "../session/usage.ts";
26
+ import { mergeUsage, modelName, readUsage, type UsageNode } from "../session/usage.ts";
21
27
 
22
28
  /** What `spawn` needs to start a worker. */
23
29
  export interface WorkerSpec {
@@ -59,7 +65,13 @@ export interface WorkerCallOptions {
59
65
  /** The calling tool call's id: makes the call exactly-once across a crash and replay (the `subagents` tool passes it). */
60
66
  callId?: string;
61
67
  /** Receives the worker's live text and thinking deltas. */
62
- onSubagentEvent?: (event: { trackingId: string; delta?: string; thinkingDelta?: string; partialText?: string; partialThinking?: string }) => void;
68
+ onSubagentEvent?: (event: {
69
+ trackingId: string;
70
+ delta?: string;
71
+ thinkingDelta?: string;
72
+ partialText?: string;
73
+ partialThinking?: string;
74
+ }) => void;
63
75
  }
64
76
 
65
77
  // Saved in `worker.json`: everything needed to rebuild a worker after a restart.
@@ -104,7 +116,11 @@ function lastAssistantText(messages: Message[]): string | undefined {
104
116
  }
105
117
 
106
118
  // Reads a worker that is not in memory from its folder: its saved spending, model, and the workers saved inside it; in: a worker's session folder, out: usageNodes.
107
- function readSavedWorker(dir: string | undefined): { model: string; totals: ReturnType<typeof readUsage>; workers: UsageNode[] } {
119
+ function readSavedWorker(dir: string | undefined): {
120
+ model: string;
121
+ totals: ReturnType<typeof readUsage>;
122
+ workers: UsageNode[];
123
+ } {
108
124
  if (!dir) return { model: "unknown", totals: readUsage(undefined), workers: [] };
109
125
  let session: ReturnType<typeof loadSessionDir>;
110
126
  try {
@@ -125,7 +141,15 @@ function readSavedWorker(dir: string | undefined): { model: string; totals: Retu
125
141
  try {
126
142
  const meta = JSON.parse(fs.readFileSync(path.join(root, d.name, META_FILE), "utf8")) as WorkerMeta;
127
143
  const inner = readSavedWorker(path.join(root, d.name));
128
- workers.push({ id: meta.id, name: meta.name, ...(meta.role ? { role: meta.role } : {}), model: inner.model, status: meta.status, totals: inner.totals, workers: inner.workers });
144
+ workers.push({
145
+ id: meta.id,
146
+ name: meta.name,
147
+ ...(meta.role ? { role: meta.role } : {}),
148
+ model: inner.model,
149
+ status: meta.status,
150
+ totals: inner.totals,
151
+ workers: inner.workers,
152
+ });
129
153
  } catch {
130
154
  // a damaged worker.json is skipped, as in load()
131
155
  }
@@ -133,6 +157,17 @@ function readSavedWorker(dir: string | undefined): { model: string; totals: Retu
133
157
  return { model: session?.session.model || "unknown", totals: readUsage(session?.session.usage), workers };
134
158
  }
135
159
 
160
+ /** What a saved session folder and every worker saved below it spent in total (read from disk): the starting point of a chat-wide usage cap after a restart; in: Agent.beginChat, out: usage totals. */
161
+ export function savedSpend(dir: string): ReturnType<typeof readUsage> {
162
+ const total = readUsage(undefined);
163
+ const add = (node: { totals: ReturnType<typeof readUsage>; workers: UsageNode[] }): void => {
164
+ mergeUsage(total, node.totals);
165
+ for (const w of node.workers) add(w);
166
+ };
167
+ add(readSavedWorker(dir));
168
+ return total;
169
+ }
170
+
136
171
  /** The workers of one parent `Agent` (`agent.workers`). */
137
172
  export class WorkerRegistry {
138
173
  private readonly entries = new Map<string, Entry>();
@@ -167,10 +202,15 @@ export class WorkerRegistry {
167
202
  const dir = path.join(root, d.name);
168
203
  try {
169
204
  const meta = JSON.parse(fs.readFileSync(path.join(dir, META_FILE), "utf8")) as WorkerMeta;
170
- if (typeof meta.id !== "string" || typeof meta.sessionId !== "string" || !Array.isArray(meta.tools)) throw new Error("missing fields");
205
+ if (typeof meta.id !== "string" || typeof meta.sessionId !== "string" || !Array.isArray(meta.tools))
206
+ throw new Error("missing fields");
171
207
  this.entries.set(meta.id, { meta, dir, busy: false });
172
208
  } catch (err) {
173
- telemetryBus.emitWarning("worker.invalid", `[Agent Accelerator] ${path.join(dir, META_FILE)} is not a valid worker and is skipped: ${err instanceof Error ? err.message : String(err)}`, { dir });
209
+ telemetryBus.emitWarning(
210
+ "worker.invalid",
211
+ `[Agent Accelerator] ${path.join(dir, META_FILE)} is not a valid worker and is skipped: ${err instanceof Error ? err.message : String(err)}`,
212
+ { dir }
213
+ );
174
214
  }
175
215
  }
176
216
  }
@@ -219,11 +259,17 @@ export class WorkerRegistry {
219
259
  /** Closes a worker: it can no longer be messaged, its session stays on disk (and in `usageReport`). */
220
260
  close(id: string): WorkerInfo {
221
261
  const entry = this.open(id);
222
- if (entry.busy) throw new ValidationError(`Worker "${id}" is busy with a call; wait for it to finish before closing it.`);
262
+ if (entry.busy)
263
+ throw new ValidationError(`Worker "${id}" is busy with a call; wait for it to finish before closing it.`);
223
264
  entry.meta.status = "closed";
224
265
  entry.meta.closedAt = Date.now();
225
266
  this.save(entry);
226
- if (entry.agent) entry.kept = { model: modelName(entry.agent.modelStringOrSpec), totals: readUsage(entry.agent.usageTotals), workers: entry.agent.workers.usageNodes() };
267
+ if (entry.agent)
268
+ entry.kept = {
269
+ model: modelName(entry.agent.modelStringOrSpec),
270
+ totals: readUsage(entry.agent.usageTotals),
271
+ workers: entry.agent.workers.usageNodes(),
272
+ };
227
273
  entry.agent?.disposeSession(entry.meta.sessionId); // frees its provider chain; never reset(): that could rewrite the saved history
228
274
  entry.agent = undefined;
229
275
  return this.info(entry);
@@ -244,7 +290,13 @@ export class WorkerRegistry {
244
290
  return [...this.entries.values()].map((entry) => {
245
291
  const { id, name, role, status } = entry.meta;
246
292
  const base = { id, name, ...(role ? { role } : {}), status };
247
- if (entry.agent) return { ...base, model: modelName(entry.agent.modelStringOrSpec), totals: entry.agent.usageTotals, workers: entry.agent.workers.usageNodes() };
293
+ if (entry.agent)
294
+ return {
295
+ ...base,
296
+ model: modelName(entry.agent.modelStringOrSpec),
297
+ totals: entry.agent.usageTotals,
298
+ workers: entry.agent.workers.usageNodes(),
299
+ };
248
300
  return { ...base, ...(entry.kept ?? readSavedWorker(entry.dir)) };
249
301
  });
250
302
  }
@@ -253,7 +305,8 @@ export class WorkerRegistry {
253
305
  private open(id: string): Entry {
254
306
  const entry = this.entries.get(id);
255
307
  if (!entry) throw new ValidationError(`No worker with id "${id}". Use list to see the open workers.`);
256
- if (entry.meta.status === "closed") throw new ValidationError(`Worker "${id}" is closed. Spawn a new worker instead.`);
308
+ if (entry.meta.status === "closed")
309
+ throw new ValidationError(`Worker "${id}" is closed. Spawn a new worker instead.`);
257
310
  return entry;
258
311
  }
259
312
 
@@ -264,7 +317,13 @@ export class WorkerRegistry {
264
317
  }
265
318
 
266
319
  // Checks a new worker's request, builds its Agent (seeded with the copied history) and saves `worker.json`; in: spawn, out: a new open entry.
267
- private async create(id: string, name: string, task: string, key: string | undefined, spec: WorkerSpec): Promise<Entry> {
320
+ private async create(
321
+ id: string,
322
+ name: string,
323
+ task: string,
324
+ key: string | undefined,
325
+ spec: WorkerSpec
326
+ ): Promise<Entry> {
268
327
  const parent = this.parent;
269
328
  if (!parent.subagentModel) {
270
329
  throw new ConfigError(
@@ -273,7 +332,11 @@ export class WorkerRegistry {
273
332
  }
274
333
  const maxOpen = parent.dynamicSubagents?.maxSpawn ?? Infinity;
275
334
  if (this.list().length >= maxOpen) {
276
- throw new ValidationError(`Too many open workers (${maxOpen} is the limit). Close one first; open workers: ${this.list().map((w) => w.id).join(", ")}.`);
335
+ throw new ValidationError(
336
+ `Too many open workers (${maxOpen} is the limit). Close one first; open workers: ${this.list()
337
+ .map((w) => w.id)
338
+ .join(", ")}.`
339
+ );
277
340
  }
278
341
  const pool = parent.dynamicSubagents?.tools ?? {};
279
342
  const requested = spec.tools ?? [];
@@ -281,7 +344,8 @@ export class WorkerRegistry {
281
344
  if (unknown.length > 0) {
282
345
  const names = Object.keys(pool);
283
346
  throw new ValidationError(
284
- `tools: unknown worker tool(s) ${unknown.map((n) => `"${n}"`).join(", ")}; ` + (names.length > 0 ? `the pool is [${names.join(", ")}].` : "no worker tools are available, omit tools.")
347
+ `tools: unknown worker tool(s) ${unknown.map((n) => `"${n}"`).join(", ")}; ` +
348
+ (names.length > 0 ? `the pool is [${names.join(", ")}].` : "no worker tools are available, omit tools.")
285
349
  );
286
350
  }
287
351
  const history = spec.history ?? "none";
@@ -331,6 +395,7 @@ export class WorkerRegistry {
331
395
  })
332
396
  );
333
397
  (agent as unknown as { parentSessionId?: string }).parentSessionId = this.parent.sessionId;
398
+ agent.chat = this.parent.chat; // the worker's turns count into the chat-wide cap and are checked against it
334
399
  // A cut-off run was already loaded by the constructor; otherwise bring back what the worker said so far.
335
400
  const saved = entry.dir && agent.context.messages.length === 0 ? loadSessionDir(entry.dir)?.session : undefined;
336
401
  if (saved) agent.importSession(saved);
@@ -338,8 +403,16 @@ export class WorkerRegistry {
338
403
  }
339
404
 
340
405
  // Sends one text to a worker and turns what happens into a reply: runs it, or after a crash continues the cut-off run or returns the answer it had already given; in: spawn and message, out: a WorkerReply.
341
- private async deliver(entry: Entry, text: string, key: string | undefined, opts: WorkerCallOptions): Promise<WorkerReply> {
342
- if (entry.busy) throw new ValidationError(`Worker "${entry.meta.id}" is busy with another call; wait for it, or message another worker.`);
406
+ private async deliver(
407
+ entry: Entry,
408
+ text: string,
409
+ key: string | undefined,
410
+ opts: WorkerCallOptions
411
+ ): Promise<WorkerReply> {
412
+ if (entry.busy)
413
+ throw new ValidationError(
414
+ `Worker "${entry.meta.id}" is busy with another call; wait for it, or message another worker.`
415
+ );
343
416
  entry.busy = true;
344
417
  const { meta } = entry;
345
418
  const startTime = nowMs();
@@ -354,14 +427,24 @@ export class WorkerRegistry {
354
427
  parent: this.parent,
355
428
  emit: opts.onSubagentEvent,
356
429
  });
357
- const lineage = { name: meta.name, displayId: live.displayId, sessionId: meta.sessionId, parentSessionId: this.parent.sessionId, role: meta.role, task: text, runId: newRunId(), turnId: newTurnId() };
430
+ const lineage = {
431
+ name: meta.name,
432
+ displayId: live.displayId,
433
+ sessionId: meta.sessionId,
434
+ parentSessionId: this.parent.sessionId,
435
+ role: meta.role,
436
+ task: text,
437
+ runId: newRunId(),
438
+ turnId: newTurnId(),
439
+ };
358
440
  const replayed = this.isReplay(entry, key, text) && agent.runMarks.length > meta.last!.marks;
359
441
  if (!replayed) {
360
442
  if (key) meta.last = { key, text: hashSessionPart(text), marks: agent.runMarks.length };
361
443
  else delete meta.last;
362
444
  this.save(entry);
363
445
  }
364
- const onAbort = (): void => agent.interrupt(opts.signal?.reason instanceof Error ? opts.signal.reason : undefined);
446
+ const onAbort = (): void =>
447
+ agent.interrupt(opts.signal?.reason instanceof Error ? opts.signal.reason : undefined);
365
448
  try {
366
449
  if (opts.signal?.aborted) throw new Error("aborted before the worker started");
367
450
  opts.signal?.addEventListener("abort", onAbort, { once: true });
@@ -387,14 +470,36 @@ export class WorkerRegistry {
387
470
  if (!response) throw new Error("the worker had nothing to resume");
388
471
  result = response;
389
472
  }
390
- const reply = result.text.trim() ? result.text : `(the worker produced no text${result.finishReason ? `; finish reason: ${result.finishReason}` : ""})`;
473
+ const reply = result.text.trim()
474
+ ? result.text
475
+ : `(the worker produced no text${result.finishReason ? `; finish reason: ${result.finishReason}` : ""})`;
391
476
  live.finish(result);
392
- return { id: meta.id, text: reply, metadata: workerMetadata({ ...lineage, durationMs: Math.max(0, nowMs() - startTime), result, steps: live.steps() }) };
477
+ return {
478
+ id: meta.id,
479
+ text: reply,
480
+ metadata: workerMetadata({
481
+ ...lineage,
482
+ durationMs: Math.max(0, nowMs() - startTime),
483
+ result,
484
+ steps: live.steps(),
485
+ }),
486
+ };
393
487
  } catch (err) {
394
488
  const message = err instanceof Error ? err.message : String(err);
395
489
  live.fail(message);
396
490
  const errorText = `Error in worker ${meta.id}: ${message}`;
397
- return { id: meta.id, text: errorText, error: message, metadata: workerMetadata({ ...lineage, durationMs: Math.max(0, nowMs() - startTime), error: message, errorText, steps: live.steps() }) };
491
+ return {
492
+ id: meta.id,
493
+ text: errorText,
494
+ error: message,
495
+ metadata: workerMetadata({
496
+ ...lineage,
497
+ durationMs: Math.max(0, nowMs() - startTime),
498
+ error: message,
499
+ errorText,
500
+ steps: live.steps(),
501
+ }),
502
+ };
398
503
  } finally {
399
504
  opts.signal?.removeEventListener("abort", onAbort);
400
505
  }
@@ -2,24 +2,26 @@
2
2
 
3
3
  The whole SDK. `index.ts` is the only public entry (`main`/`module`/`types`); everything else is reached through it.
4
4
 
5
- | File / folder | What it is |
6
- | :-- | :-- |
7
- | `index.ts` | The public barrel. Adding or removing an export is a public API change. |
8
- | `telemetry.ts` | The in-process event bus (`telemetryBus`). Imports nothing from the SDK, so any module may use it. |
5
+ | File / folder | What it is |
6
+ | :------------------ | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
7
+ | `index.ts` | The public barrel. Adding or removing an export is a public API change. |
8
+ | `telemetry.ts` | The in-process event bus (`telemetryBus`). Imports nothing from the SDK, so any module may use it. |
9
9
  | `telemetry-file.ts` | Opt-in NDJSON sink for the bus: a persisted session's events go to `<session folder>/telemetry.ndjson`, the rest to `<home>/telemetry/<date>.ndjson`; never outside a home. |
10
- | `update-models.ts` | CLI behind `bun run update-models` (refreshes the model catalog cache). |
11
- | `providers.ts` | Provider-agnostic mappers (thinking, cache, tool choice, structured output, generation params). No HTTP. |
12
- | `agent/` | `Agent`, the model-to-tools loop, workers, delegation. |
13
- | `providers/` | One adapter per wire (Google, OpenAI, OpenRouter, OpenAI-compatible), the registry, shared helpers. |
14
- | `models/` | The model catalog (models.dev) and its cache. |
15
- | `tools/` | `tool()`, schemas, the executor. |
16
- | `streaming/` | The event stream and the SSE parser. |
17
- | `session/` | Persistence (snapshot + journal), usage accounting, tool logs. |
18
- | `scheduler/` | Scheduled actions. |
19
- | `types/` | Shared contracts (no logic except small helpers). |
20
- | `utils/` | Small shared helpers (durable writes, home, env, media, retry, safe I/O). |
10
+ | `update-models.ts` | CLI behind `bun run update-models` (refreshes the model catalog cache). |
11
+ | `providers.ts` | Provider-agnostic mappers (thinking, cache, tool choice, structured output, generation params). No HTTP. |
12
+ | `agent/` | `Agent`, the model-to-tools loop, workers, delegation. |
13
+ | `providers/` | One adapter per wire (Google, OpenAI, OpenRouter, OpenAI-compatible), the registry, shared helpers. |
14
+ | `models/` | The model catalog (models.dev) and its cache. |
15
+ | `tools/` | `tool()`, schemas, the executor. |
16
+ | `streaming/` | The event stream and the SSE parser. |
17
+ | `session/` | Persistence (snapshot + journal), usage accounting, tool logs. |
18
+ | `prompts/` | The SDK's own prompts as `.md` files, `DEFAULT_PROMPTS`, `AgentConfig.prompts`. |
19
+ | `scheduler/` | Scheduled actions. |
20
+ | `types/` | Shared contracts (no logic except small helpers). |
21
+ | `utils/` | Small shared helpers (durable writes, home, env, media, retry, safe I/O). |
21
22
 
22
23
  ## Rules for every file here
24
+
23
25
  - Imports use `.ts` extensions; type-only imports use `import type`.
24
26
  - No `console.*`: warnings and traces go to `telemetryBus` (exceptions: the `update-models.ts` CLI and one compat `console.warn` in `providers.ts`).
25
27
  - All state is written under the one home folder (`utils/home.ts`). Files are replaced through `utils/durable.ts`; the append-only ones are the session journal and tool logs (fsynced per line) and the opt-in telemetry file (not fsynced).
package/src/index.ts CHANGED
@@ -31,13 +31,24 @@ export {
31
31
  } from "./tools/tool.ts";
32
32
  export type { CreateToolOptions } from "./tools/tool.ts";
33
33
  export { zodToJsonSchema, cleanJsonSchema } from "./tools/schema.ts";
34
+ export { toolContent } from "./tools/tool-content.ts";
35
+ export { DEFAULT_PROMPTS } from "./prompts/prompts.ts";
36
+ export type { PromptName, PromptOverrides } from "./prompts/prompts.ts";
37
+ export type { ToolContent, ToolContentPart } from "./tools/tool-content.ts";
34
38
  /**
35
39
  * @deprecated Internal executor surface. Prefer `Agent` runs; for direct
36
40
  * execution import from the future `/internal` entry.
37
41
  */
38
42
  export { executeToolCalls } from "./tools/executor.ts";
39
43
 
40
- export { assertPathAllowed, enforceFileAccess, fetchRemoteBytes, isPrivateAddress, RemoteFetchError, MAX_REMOTE_FETCH_BYTES } from "./utils/safe-io.ts";
44
+ export {
45
+ assertPathAllowed,
46
+ enforceFileAccess,
47
+ fetchRemoteBytes,
48
+ isPrivateAddress,
49
+ RemoteFetchError,
50
+ MAX_REMOTE_FETCH_BYTES,
51
+ } from "./utils/safe-io.ts";
41
52
 
42
53
  // Multi-Agent Delegation & Tools
43
54
  export {
@@ -53,17 +64,12 @@ export {
53
64
  export type { AgentAsToolTarget, SubAgentTrace } from "./agent/delegation.ts";
54
65
  export type { WorkerRegistry, WorkerSpec, WorkerInfo, WorkerReply, WorkerCallOptions } from "./agent/workers.ts";
55
66
 
56
- // Scheduled actions: dynamic sub-agents that run later and save their answer to a file
57
- export {
58
- createScheduledAction,
59
- listScheduledActions,
60
- updateScheduledAction,
61
- runScheduledAction,
62
- skipScheduledAction,
63
- deleteScheduledAction,
64
- } from "./scheduler/scheduler.ts";
67
+ // Scheduled actions: dynamic sub-agents that run later and save their answer to a file (use `agent.scheduled`)
68
+ export type { ScheduledActions } from "./scheduler/scheduler.ts";
65
69
  export { getDatetimeTool } from "./scheduler/tool.ts";
66
70
  export { setHome, resolveHome } from "./utils/home.ts";
71
+ export { configure } from "./agent/defaults.ts";
72
+ export type { AgentDefaults } from "./agent/defaults.ts";
67
73
  export type {
68
74
  ScheduledActionInput,
69
75
  ScheduledActionPatch,
@@ -126,12 +132,8 @@ export {
126
132
  GoogleInteractionsProvider,
127
133
  clearInteractionChains,
128
134
  } from "./providers/google.ts";
129
- export {
130
- OpenRouterChatCompletionsProvider,
131
- } from "./providers/openrouter.ts";
132
- export {
133
- OpenAIResponsesProvider,
134
- } from "./providers/openai.ts";
135
+ export { OpenRouterChatCompletionsProvider } from "./providers/openrouter.ts";
136
+ export { OpenAIResponsesProvider } from "./providers/openai.ts";
135
137
  /**
136
138
  * @deprecated Internal provider mappers/routing helpers. Prefer the
137
139
  * provider-agnostic registry (`resolveModel`/`getProvider`); direct mapper
@@ -251,14 +253,36 @@ export {
251
253
  emptyTotals,
252
254
  } from "./session/store.ts";
253
255
  export type { UsageReport, UsageLine, UsageTotals, AgentUsage, WorkerUsage, CostSource } from "./session/usage.ts";
254
- export type { SessionLimits, SessionPayloadStatus, PersistedAgentSession, SessionTotals, SessionAgentLike, LoadedSessionDir, PersistedSubAgentTrace, SessionRunMark, SessionRunEnd, SessionPending, SessionPendingPrompt, SessionToolRecord } from "./session/store.ts";
256
+ export type {
257
+ SessionLimits,
258
+ SessionPayloadStatus,
259
+ PersistedAgentSession,
260
+ SessionTotals,
261
+ SessionAgentLike,
262
+ LoadedSessionDir,
263
+ PersistedSubAgentTrace,
264
+ SessionRunMark,
265
+ SessionRunEnd,
266
+ SessionPending,
267
+ SessionPendingPrompt,
268
+ SessionToolRecord,
269
+ } from "./session/store.ts";
255
270
 
256
271
  // Utilities
257
272
  /**
258
273
  * @deprecated Session-routing internals. Prefer agent-level `sessionId`;
259
274
  * direct use moves to the future `/internal` entry.
260
275
  */
261
- export { createSessionId, hashSessionPart, createTrackingId, nowMs, newRunId, newTurnId, previewPrompt, parseModelRef } from "./utils/session.ts";
276
+ export {
277
+ createSessionId,
278
+ hashSessionPart,
279
+ createTrackingId,
280
+ nowMs,
281
+ newRunId,
282
+ newTurnId,
283
+ previewPrompt,
284
+ parseModelRef,
285
+ } from "./utils/session.ts";
262
286
  export type { ParsedModelRef } from "./utils/session.ts";
263
287
  /**
264
288
  * @deprecated Raw env access. Prefer explicit `Agent` config (which wins over
@@ -298,6 +322,7 @@ export type {
298
322
  ProviderContext,
299
323
  ProviderSteps,
300
324
  MediaResolution,
325
+ VideoProcessing,
301
326
  } from "./types/message.ts";
302
327
 
303
328
  export type {
@@ -344,7 +369,6 @@ export type {
344
369
  RetryPolicyConfig,
345
370
  AgentLogger,
346
371
  ToolChoiceOption,
347
- StopOption,
348
372
  Budget,
349
373
  BudgetState,
350
374
  SubAgentRunIds,
@@ -2,12 +2,13 @@
2
2
 
3
3
  The model catalog: context limits, prices, modalities and reasoning options come from models.dev, not from code.
4
4
 
5
- | File | What it is |
6
- | :-- | :-- |
5
+ | File | What it is |
6
+ | :----------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
7
7
  | `catalog-cache.ts` | Downloads and caches `models.dev/api.json` in `<home>/catalog/` (or `AGENT_CACHE_DIR`). Stale-while-revalidate: an expired catalog is served at once and refreshed in the background (8 s timeout, 10 min backoff after a failure); only a cold start waits. `AGENT_CATALOG_REFRESH=off` stops automatic refreshes. |
8
- | `catalog.ts` | Lookups (`getModelFromCatalog`), `registerModel`, thinking validation (`validateModelThinking`, which throws `ThinkingLevelError`, a one-line `ConfigError`), and the exported `GOOGLE_/OPENAI_/OPENROUTER_MODELS` arrays (rebuilt in place when the catalog changes). |
8
+ | `catalog.ts` | Lookups (`getModelFromCatalog`), `registerModel`, thinking validation (`validateModelThinking`, which throws `ThinkingLevelError`, a one-line `ConfigError`), and the exported `GOOGLE_/OPENAI_/OPENROUTER_MODELS` arrays (rebuilt in place when the catalog changes). |
9
9
 
10
10
  ## Invariants
11
+
11
12
  - Never hand-edit the cache file; register custom models with `registerModel()`. Verified modality fixes go in `MODALITY_OVERRIDES`.
12
13
  - The global bare-id fallback applies to first-class providers and explicit bare-id lookups only. A custom prefix never borrows another provider's price or modalities: unknown stays unknown.
13
14
  - A payload that is not a catalog (fewer than 3 providers with models) never overwrites the cache.
@@ -313,7 +313,7 @@ export async function refreshModelCatalog(options: RefreshCatalogOptions = {}):
313
313
  fallbackToDiskCache();
314
314
  return getCatalogStatus();
315
315
  }
316
- } catch (err: any) {
316
+ } catch {
317
317
  clearTimeout(timer);
318
318
  // If download fails, retain disk cache if available
319
319
  fallbackToDiskCache();
@@ -356,7 +356,6 @@ export async function refreshModelCatalog(options: RefreshCatalogOptions = {}):
356
356
  return getCatalogStatus();
357
357
  }
358
358
 
359
-
360
359
  /** True when `AGENT_CATALOG_REFRESH` is `off`/`0`/`false`: never touch the network automatically. */
361
360
  function automaticRefreshDisabled(): boolean {
362
361
  const v = getEnv("AGENT_CATALOG_REFRESH")?.trim().toLowerCase();
@@ -388,9 +387,11 @@ export async function ensureModelCatalogFresh(options: RefreshCatalogOptions = {
388
387
  const start = (): Promise<CatalogStatus> => {
389
388
  if (!inFlightRefresh) {
390
389
  const timeoutMs = options.timeoutMs ?? (current.loaded ? BACKGROUND_REFRESH_TIMEOUT_MS : undefined);
391
- inFlightRefresh = refreshModelCatalog({ ...options, ...(timeoutMs !== undefined ? { timeoutMs } : {}) }).finally(() => {
392
- inFlightRefresh = null;
393
- });
390
+ inFlightRefresh = refreshModelCatalog({ ...options, ...(timeoutMs !== undefined ? { timeoutMs } : {}) }).finally(
391
+ () => {
392
+ inFlightRefresh = null;
393
+ }
394
+ );
394
395
  }
395
396
  return inFlightRefresh;
396
397
  };
@@ -5,21 +5,10 @@
5
5
  * rebuilt in place whenever it changes.
6
6
  */
7
7
 
8
- import {
9
- getActiveCatalog,
10
- registerCatalogUpdateListener,
11
- refreshModelCatalog,
12
- ensureModelCatalogFresh,
13
- getCatalogStatus,
14
- setCatalogTTL,
15
- getCatalogTTL,
16
- DEFAULT_CATALOG_TTL_MS,
17
- type CatalogStatus,
18
- type RefreshCatalogOptions,
19
- } from "./catalog-cache.ts";
8
+ import { getActiveCatalog, registerCatalogUpdateListener } from "./catalog-cache.ts";
20
9
  import type { ModelSpec, ProviderId, ModelLimit, ModelCost, ModelModalities } from "../types/model.ts";
21
10
  import type { ThinkingLevel } from "../types/core.ts";
22
- import { ConfigError } from "../types/errors.ts";
11
+ import { ConfigError, ValidationError } from "../types/errors.ts";
23
12
 
24
13
  // ---------------------------------------------------------------------------
25
14
  // Provider aliases — maps our internal ids to models.dev keys
@@ -49,8 +38,8 @@ function applyModalityOverrides(provider: string, modelId: string, base: ModelMo
49
38
  MODALITY_OVERRIDES[`${provider}/${normalizeModelIdForLookup(modelId)}`];
50
39
  if (!hit) return base;
51
40
  return {
52
- input: ((hit.input ?? base.input) as ModelModalities["input"]),
53
- output: ((hit.output ?? base.output) as ModelModalities["output"]),
41
+ input: (hit.input ?? base.input) as ModelModalities["input"],
42
+ output: (hit.output ?? base.output) as ModelModalities["output"],
54
43
  };
55
44
  }
56
45
 
@@ -107,7 +96,7 @@ function customRegistryKey(provider: string, modelId: string): string {
107
96
  */
108
97
  export function registerModel(spec: ModelSpec): void {
109
98
  if (!spec || typeof spec.id !== "string" || typeof spec.provider !== "string") {
110
- throw new Error("[Agent Accelerator] registerModel requires spec.id and spec.provider strings");
99
+ throw new ValidationError("[Agent Accelerator] registerModel requires spec.id and spec.provider strings");
111
100
  }
112
101
  customModelRegistry.set(customRegistryKey(String(spec.provider), String(spec.id)), { ...spec });
113
102
  // Invalidate derived caches so the custom model is visible immediately.
@@ -136,30 +125,38 @@ export function getModelSource(provider: string, modelId: string): "catalog" | "
136
125
  if (customModelRegistry.has(customRegistryKey(p, m)) || customModelRegistry.has(customRegistryKey(p, norm))) {
137
126
  return "override";
138
127
  }
139
- if (
140
- MODALITY_OVERRIDES[`${p}/${m}`] !== undefined ||
141
- MODALITY_OVERRIDES[`${p}/${norm}`] !== undefined
142
- ) {
128
+ if (MODALITY_OVERRIDES[`${p}/${m}`] !== undefined || MODALITY_OVERRIDES[`${p}/${norm}`] !== undefined) {
143
129
  return "override";
144
130
  }
145
131
  return getModelFromCatalog(provider, modelId) ? "catalog" : "unknown";
146
132
  }
147
133
 
148
134
  // Derives capability flags (thinking, caching, tools, structured output, modalities) from a raw catalog entry; in: mapModelSpec, out: ModelCapabilities.
149
- function mapCapabilities(raw: any, cost: ModelCost, reasoning: boolean, toolCall: boolean, modalities: ModelModalities) {
135
+ function mapCapabilities(
136
+ raw: any,
137
+ cost: ModelCost,
138
+ reasoning: boolean,
139
+ toolCall: boolean,
140
+ modalities: ModelModalities
141
+ ) {
150
142
  return {
151
143
  supportsThinking: reasoning,
152
144
  supportsThinkingLevel: !!raw.reasoning_options,
153
- supportsThinkingBudget: Array.isArray(raw.reasoning_options) && raw.reasoning_options.some((o: any) => o.type === "budget_tokens"),
145
+ supportsThinkingBudget:
146
+ Array.isArray(raw.reasoning_options) && raw.reasoning_options.some((o: any) => o.type === "budget_tokens"),
154
147
  supportsImplicitCaching: cost.cache_read !== undefined || cost.cache_write !== undefined,
155
148
  supportsLongCacheRetention: cost.cache_read !== undefined,
156
149
  supportsExplicitCaching: cost.cache_read !== undefined,
157
150
  supportsParallelToolCalls: toolCall,
158
151
  supportsStreaming: true,
159
152
  supportsStructuredOutput: raw.structured_output === undefined ? true : !!raw.structured_output,
160
- supportsReasoningToggle: Array.isArray(raw.reasoning_options) && raw.reasoning_options.some((o: any) => o.type === "toggle"),
161
- supportsReasoningEffort: Array.isArray(raw.reasoning_options) && raw.reasoning_options.some((o: any) => o.type === "effort"),
162
- modalities: [...(modalities.input || []), ...(modalities.output || [])].filter((v: any, i: any, a: any) => a.indexOf(v) === i),
153
+ supportsReasoningToggle:
154
+ Array.isArray(raw.reasoning_options) && raw.reasoning_options.some((o: any) => o.type === "toggle"),
155
+ supportsReasoningEffort:
156
+ Array.isArray(raw.reasoning_options) && raw.reasoning_options.some((o: any) => o.type === "effort"),
157
+ modalities: [...(modalities.input || []), ...(modalities.output || [])].filter(
158
+ (v: any, i: any, a: any) => a.indexOf(v) === i
159
+ ),
163
160
  };
164
161
  }
165
162
 
@@ -268,7 +265,9 @@ export function getModelFromCatalog(providerInput: string, modelIdInput: string)
268
265
  }
269
266
 
270
267
  const aliasProviders = PROVIDER_ALIASES[provider] || [provider];
271
- const tryProviders = [...new Set([...aliasProviders, provider, provider.replace("-zen", ""), provider.replace("-go", "")])];
268
+ const tryProviders = [
269
+ ...new Set([...aliasProviders, provider, provider.replace("-zen", ""), provider.replace("-go", "")]),
270
+ ];
272
271
  const catalog = getActiveCatalog();
273
272
 
274
273
  for (const p of tryProviders) {
@@ -292,7 +291,11 @@ export function getModelFromCatalog(providerInput: string, modelIdInput: string)
292
291
  // or normalized equality. The old loose `modelId.endsWith(key)` is removed
293
292
  // (e.g. "gpt-4o-mini-extra" must not match "mini").
294
293
  for (const [key, raw] of Object.entries(providerData.models as Record<string, any>)) {
295
- if (key === modelId || key.endsWith(`/${modelId}`) || normalizeModelIdForLookup(key) === normalizeModelIdForLookup(modelId)) {
294
+ if (
295
+ key === modelId ||
296
+ key.endsWith(`/${modelId}`) ||
297
+ normalizeModelIdForLookup(key) === normalizeModelIdForLookup(modelId)
298
+ ) {
296
299
  result = mapModelSpec(p as ProviderId, key, raw as any);
297
300
  setLookupCache(cacheKey, result);
298
301
  return result;
@@ -411,7 +414,8 @@ export function getModelThinkingInfo(provider: string, modelId: string): ModelTh
411
414
  allowed.add("dynamic");
412
415
 
413
416
  const allowedLevels = Array.from(allowed);
414
- const supportsDisable = allowed.has("none") || !!toggleOpt || Boolean(budgetOpt && (budgetOpt.min === undefined || budgetOpt.min <= 0));
417
+ const supportsDisable =
418
+ allowed.has("none") || !!toggleOpt || Boolean(budgetOpt && (budgetOpt.min === undefined || budgetOpt.min <= 0));
415
419
 
416
420
  return {
417
421
  supportsThinking: true,
@@ -444,7 +448,8 @@ export class ThinkingLevelError extends ConfigError {
444
448
  reason: string;
445
449
  remedy?: string;
446
450
  }) {
447
- const allowed = opts.allowedLevels.length > 0 ? ` Allowed levels: ${opts.allowedLevels.map((l) => `"${l}"`).join(", ")}.` : "";
451
+ const allowed =
452
+ opts.allowedLevels.length > 0 ? ` Allowed levels: ${opts.allowedLevels.map((l) => `"${l}"`).join(", ")}.` : "";
448
453
  const remedy = opts.remedy ? ` How to fix: ${opts.remedy}` : "";
449
454
  super(
450
455
  `[Agent Accelerator] Thinking level "${opts.requestedLevel}" does not fit "${opts.provider}/${opts.modelId}": ${opts.reason}${allowed}${remedy}`,
@@ -464,7 +469,11 @@ export class ThinkingLevelError extends ConfigError {
464
469
  *
465
470
  * @example `validateModelThinking("google", "gemini-3.5-flash-lite", "medium");`
466
471
  */
467
- export function validateModelThinking(provider: string, modelId: string, requestedLevel?: ThinkingLevel | string): void {
472
+ export function validateModelThinking(
473
+ provider: string,
474
+ modelId: string,
475
+ requestedLevel?: ThinkingLevel | string
476
+ ): void {
468
477
  if (!requestedLevel) return;
469
478
  const level = String(requestedLevel).toLowerCase().trim();
470
479
  const info = getModelThinkingInfo(provider, modelId);
@@ -0,0 +1,2 @@
1
+ [Additional Context]
2
+ {{context}}