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
@@ -0,0 +1,32 @@
1
+ /**
2
+ * A tool result made of text and media. A tool returns `toolContent([...])` when the model should SEE a file (a chart
3
+ * image, a scanned page, a recording) and not only read text about it. The brand is a private Symbol, so only the
4
+ * tool's own code can produce it: JSON a tool fetched from the web can never become media for the provider.
5
+ */
6
+ import type { ContentPart } from "../types/message.ts";
7
+
8
+ /** Parts a tool may return: plain strings (text) and the usual text, image, audio, video and file parts. */
9
+ export type ToolContentPart = string | Extract<ContentPart, { type: "text" | "image" | "audio" | "video" | "file" }>;
10
+
11
+ const TOOL_CONTENT_BRAND: unique symbol = Symbol("agent-accelerator.tool-content") as never;
12
+
13
+ /** What `toolContent` returns; the executor turns it into a tool result with `parts`. */
14
+ export interface ToolContent {
15
+ readonly parts: ToolContentPart[];
16
+ readonly [TOOL_CONTENT_BRAND]: true;
17
+ }
18
+
19
+ /**
20
+ * Wraps text and media for a tool's return value, e.g.
21
+ * `return toolContent(["instrument.jpg", { type: "image", image: bytes, mimeType: "image/jpeg" }])`.
22
+ * Media may be bytes, a data URL, a path or a URL (the tool's own code is trusted with these). Google passes the media
23
+ * to the model inside the function result; other providers get the text only, with one warning.
24
+ */
25
+ export function toolContent(parts: ToolContentPart[]): ToolContent {
26
+ return { parts: [...parts], [TOOL_CONTENT_BRAND]: true };
27
+ }
28
+
29
+ /** True only for values made by {@link toolContent}. */
30
+ export function isToolContent(value: unknown): value is ToolContent {
31
+ return !!value && typeof value === "object" && (value as Record<symbol, unknown>)[TOOL_CONTENT_BRAND] === true;
32
+ }
package/src/tools/tool.ts CHANGED
@@ -1,9 +1,5 @@
1
1
  import { z } from "zod";
2
- import type {
3
- ToolDefinition,
4
- StandardToolDeclaration,
5
- ToolExecuteFn,
6
- } from "../types/tool.ts";
2
+ import type { ToolDefinition, StandardToolDeclaration, ToolExecuteFn } from "../types/tool.ts";
7
3
  import type { ThinkingLevel } from "../types/core.ts";
8
4
  import type { ThinkingOption } from "../types/agent.ts";
9
5
  import { zodToJsonSchema } from "./schema.ts";
@@ -54,9 +50,10 @@ export function normalizeThinkingV2(
54
50
  *
55
51
  * @example `resolveWhenBusy({ whenBusy: "queue", mode: "steer" }); // "queue"`
56
52
  */
57
- export function resolveWhenBusy(
58
- config?: { mode?: "steer" | "queue" | "auto"; whenBusy?: "steer" | "queue" | "auto" }
59
- ): "steer" | "queue" | "auto" {
53
+ export function resolveWhenBusy(config?: {
54
+ mode?: "steer" | "queue" | "auto";
55
+ whenBusy?: "steer" | "queue" | "auto";
56
+ }): "steer" | "queue" | "auto" {
60
57
  const v = config?.whenBusy ?? config?.mode ?? "auto";
61
58
  return v === "steer" || v === "queue" ? v : "auto";
62
59
  }
@@ -67,9 +64,10 @@ export function resolveWhenBusy(
67
64
  *
68
65
  * @example `normalizeMaxAttempts({ maxAttempts: 3 }); // 3`
69
66
  */
70
- export function normalizeMaxAttempts(
71
- config?: { maxTries?: number | string; maxAttempts?: number | string }
72
- ): number | string | undefined {
67
+ export function normalizeMaxAttempts(config?: {
68
+ maxTries?: number | string;
69
+ maxAttempts?: number | string;
70
+ }): number | string | undefined {
73
71
  return config?.maxTries ?? config?.maxAttempts;
74
72
  }
75
73
 
@@ -1,13 +1,10 @@
1
1
  import type { ModelSpec } from "./model.ts";
2
2
  import type { ToolDefinition } from "./tool.ts";
3
- import type {
4
- ThinkingLevel,
5
- CacheOption,
6
- ServiceTier,
7
- } from "./core.ts";
3
+ import type { ThinkingLevel, CacheOption, ServiceTier } from "./core.ts";
8
4
  import type { ModelProviderInstance } from "../providers/registry.ts";
9
5
  import type { Agent } from "../agent/agent.ts";
10
6
  import type { z } from "zod";
7
+ import type { PromptOverrides } from "../prompts/prompts.ts";
11
8
 
12
9
  // ---------------------------------------------------------------------------
13
10
  // Canonical option types (all additive; nothing renamed).
@@ -51,10 +48,8 @@ export type ToolChoiceOption =
51
48
  | "auto"
52
49
  | "none"
53
50
  | "required"
54
- | { mode?: "auto" | "none" | "required"; tool?: string };
55
-
56
- /** Canonical stop control: sequence(s) halting generation. */
57
- export type StopOption = string | string[];
51
+ | "validated"
52
+ | { mode?: "auto" | "none" | "required" | "validated"; tool?: string };
58
53
 
59
54
  /** Canonical structured-output input. Zod, JSON Schema, or explicit form. */
60
55
  export type StructuredOutputOption =
@@ -160,17 +155,16 @@ export interface AgentConfig {
160
155
  * Canonical tool-selection control (`"auto"` default). `"none"` applies to
161
156
  * every turn of a run; `"required"` and a pinned tool (`{ tool: "name" }`)
162
157
  * apply to the FIRST model turn only, since forcing a tool call on every
163
- * turn would make a run unable to finish.
158
+ * turn would make a run unable to finish. `"validated"` (Google only) lets the
159
+ * model decide whether to call a tool but makes it keep every call valid against
160
+ * the tool's schema; it holds for the whole run. Other providers ignore it
161
+ * with one warning.
164
162
  */
165
163
  toolChoice?: ToolChoiceOption;
166
164
  /** Google Interactions storage tier for the chain-TTL gate: `"free"` (23h, default) or `"paid"` (54d). */
167
165
  googleTier?: "free" | "paid";
168
166
  /** Maximum output tokens for one model call. */
169
167
  maxOutputTokens?: number;
170
- /** Stop sequence(s). */
171
- stop?: StopOption;
172
- /** Deterministic seed, when the provider supports it. */
173
- seed?: number;
174
168
  /**
175
169
  * Optional output schema (Zod, JSON Schema, or `{ name, schema, strict }`,
176
170
  * see `StructuredOutputOption`, stored as `unknown` so `zod`
@@ -230,12 +224,29 @@ export interface AgentConfig {
230
224
  budget?: Budget;
231
225
  /** Run + network limits. Turn/budget caps unlimited by default; network timeouts finite. */
232
226
  limits?: AgentRunLimits;
227
+ /**
228
+ * A spending cap for the WHOLE chat, for its whole life: what the agent, all its workers (the `subagents` tool,
229
+ * `agent.workers` at any depth, fixed `subagents`) and their model calls cost, added up. It survives restarts: a
230
+ * persisted agent starts from what its saved sessions already spent. Once the total reaches `maxCostUsd` or
231
+ * `maxTotalTokens`, the next model call anywhere in the chat throws a `BudgetExceededError` ("Usage limit reached").
232
+ * Unlike `budget` (per run) it never resets. Scheduled actions are separate and never count. Models with unknown
233
+ * pricing add tokens but $0 to the cost. Unset = no cap.
234
+ */
235
+ usageLimit?: Pick<Budget, "maxCostUsd" | "maxTotalTokens">;
233
236
  /**
234
237
  * The only models this agent may use, as `provider/model` strings compared as written. Unset (default) = any model.
235
238
  * Checked when the Agent is built (also for a config an agent edited through {@link Agent.fromConfig}); a model
236
239
  * outside the list throws a `ConfigError`. Workers and `subagentModel` are not affected.
237
240
  */
238
241
  allowedModels?: string[];
242
+ /**
243
+ * Replaces single prompts the SDK puts in front of the model (the reasoning directive, tool refusals, the worker
244
+ * headers, the tool manuals ...). Every prompt you leave out keeps its default; see `DEFAULT_PROMPTS` for the names, the
245
+ * shipped text and the `{{placeholders}}` the SDK fills in. Keeping the original text is recommended: it is what the SDK
246
+ * is tested with, and a good reference for writing your own. Workers inherit it. A prompt is part of the cached prompt
247
+ * prefix when it is in the system prompt or a tool description, so change it once, not per run.
248
+ */
249
+ prompts?: PromptOverrides;
239
250
  }
240
251
 
241
252
  /**
@@ -316,12 +327,20 @@ export interface DynamicSubagentsConfig {
316
327
  /** Fixed reasoning level for all dynamic workers. The Main Agent cannot override it. Falls back to the parent level when omitted. */
317
328
  thinkingLevel?: ThinkingLevel;
318
329
  /**
319
- * Spending cap of EACH worker (the `subagents` tool, `agent.workers` and scheduled actions), set as that worker's own
320
- * per-run `budget`: a spawn, a message or a scheduled run is one run, and the usage of the worker's own workers counts
321
- * toward it. Exceeding it fails that call with a budget error and leaves the worker open. Default: unlimited. Models
322
- * with unknown pricing count $0 toward `maxCostUsd`. Passed down to the workers' own workers.
330
+ * Spending cap of EACH chat worker (the `subagents` tool and `agent.workers`), set as that worker's own per-run
331
+ * `budget`: a spawn or a message is one run, and the usage of the worker's own workers counts toward it. Exceeding it
332
+ * fails that call with a budget error and leaves the worker open. Default: unlimited. Models with unknown pricing count
333
+ * $0 toward `maxCostUsd`. Passed down to the workers' own workers. Scheduled actions have their own cap:
334
+ * `scheduledActionBudget`.
323
335
  */
324
336
  workerBudget?: Pick<Budget, "maxCostUsd" | "maxTotalTokens">;
337
+ /**
338
+ * Spending cap of EACH scheduled-action run, set as that run's own per-run `budget` (attempts included: a retry is part
339
+ * of the same run). Scheduled actions are detached from the chat, so neither `workerBudget` nor `usageLimit` applies to
340
+ * them. Exceeding it records a failed run. Default: unlimited. Models with unknown pricing count $0 toward
341
+ * `maxCostUsd`.
342
+ */
343
+ scheduledActionBudget?: Pick<Budget, "maxCostUsd" | "maxTotalTokens">;
325
344
  /**
326
345
  * How many levels of workers may exist below this agent. Default: no limit. `1` = workers cannot start workers of their
327
346
  * own; `3` = a worker may start workers that start workers. A worker with levels left gets its own `subagents` tool, and
@@ -383,10 +402,6 @@ export interface AgentRunOptions {
383
402
  toolChoice?: ToolChoiceOption;
384
403
  /** Per-run output-token cap. */
385
404
  maxOutputTokens?: number;
386
- /** Per-run stop sequence(s). */
387
- stop?: StopOption;
388
- /** Per-run seed. */
389
- seed?: number;
390
405
  /** Per-run retry override. */
391
406
  retry?: RetryPolicyConfig;
392
407
  /** Per-run fetch override. */
@@ -2,18 +2,19 @@
2
2
 
3
3
  Shared contracts. Types only, plus a few tiny helpers that belong to a type.
4
4
 
5
- | File | What it defines |
6
- | :-- | :-- |
7
- | `agent.ts` | `AgentConfig`, `AgentRunOptions`, `DynamicSubagentsConfig`, `Budget`, `EditableAgentConfig`, run/turn event shapes. |
8
- | `core.ts` | `ThinkingLevel`, `CacheOption` (+ `CACHE_OPTIONS`), `ServiceTier`, `TokenUsage`. |
9
- | `message.ts` | `Message` and the content parts (text, image, audio, video, file, tool call/result, thinking), `ProviderSteps`. |
10
- | `model.ts` | `ModelSpec`, `Provider`, `ProviderRequestOptions`, `ProviderGenerateResult`, structured-output spec. |
11
- | `response.ts` | `AgentResponse`, `FinishReason` + `normalizeFinishReason`, `StreamEvent`, worker metadata. |
12
- | `tool.ts` | `ToolDefinition`, `ToolExecutionContext` (`log`, `stop`, `emit`), call/result records, the truncated-arguments marker. |
13
- | `scheduler.ts` | Scheduled-action input, record and delivery types. |
14
- | `errors.ts` | The typed error family (`AgentAccelError` and subclasses), `isAbortError`. |
5
+ | File | What it defines |
6
+ | :------------- | :--------------------------------------------------------------------------------------------------------------------- |
7
+ | `agent.ts` | `AgentConfig`, `AgentRunOptions`, `DynamicSubagentsConfig`, `Budget`, `EditableAgentConfig`, run/turn event shapes. |
8
+ | `core.ts` | `ThinkingLevel`, `CacheOption` (+ `CACHE_OPTIONS`), `ServiceTier`, `TokenUsage`. |
9
+ | `message.ts` | `Message` and the content parts (text, image, audio, video, file, tool call/result, thinking), `ProviderSteps`. |
10
+ | `model.ts` | `ModelSpec`, `Provider`, `ProviderRequestOptions`, `ProviderGenerateResult`, structured-output spec. |
11
+ | `response.ts` | `AgentResponse`, `FinishReason` + `normalizeFinishReason`, `StreamEvent`, worker metadata. |
12
+ | `tool.ts` | `ToolDefinition`, `ToolExecutionContext` (`log`, `stop`, `emit`), call/result records, the truncated-arguments marker. |
13
+ | `scheduler.ts` | Scheduled-action input, record and delivery types. |
14
+ | `errors.ts` | The typed error family (`AgentAccelError` and subclasses), `isAbortError`. |
15
15
 
16
16
  ## Invariants
17
+
17
18
  - Adding a field to a public type is a public API change: update `README.md` and `docs/`.
18
19
  - Abort means `signal.aborted` or `name === "AbortError"`, never a message match.
19
20
  - Error messages are one line and ANSI-free.
package/src/types/core.ts CHANGED
@@ -2,15 +2,7 @@
2
2
  * Level-based reasoning — single flag for all levels (bloatfree)
3
3
  */
4
4
  /** Public reasoning control accepted by AgentConfig. */
5
- export type ThinkingLevel =
6
- | "none"
7
- | "dynamic"
8
- | "minimal"
9
- | "low"
10
- | "medium"
11
- | "high"
12
- | "xhigh"
13
- | "max";
5
+ export type ThinkingLevel = "none" | "dynamic" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max";
14
6
 
15
7
  // Internal normalized config (SDK-internal, not exposed as Agent flag)
16
8
  /** Internal normalized reasoning configuration passed to providers. */
@@ -50,6 +42,10 @@ export interface TokenUsage {
50
42
  cacheReadTokens?: number;
51
43
  cacheWriteTokens?: number;
52
44
  thinkingTokens?: number;
45
+ /** Media part of `inputTokens` (a subset, report only: pricing never reads it). OpenRouter reports audio and video. */
46
+ inputDetails?: { audioTokens?: number; videoTokens?: number };
47
+ /** Media part of `outputTokens` (a subset, report only). OpenRouter reports audio and image. */
48
+ outputDetails?: { audioTokens?: number; imageTokens?: number };
53
49
  cost?: {
54
50
  inputCost?: number;
55
51
  outputCost?: number;
@@ -40,7 +40,11 @@ export class AgentAccelError extends Error {
40
40
  readonly code: AgentAccelErrorCode;
41
41
  readonly retryable: boolean;
42
42
  readonly context: AgentAccelErrorContext;
43
- constructor(code: AgentAccelErrorCode, message: string, opts?: { retryable?: boolean; context?: AgentAccelErrorContext; cause?: unknown }) {
43
+ constructor(
44
+ code: AgentAccelErrorCode,
45
+ message: string,
46
+ opts?: { retryable?: boolean; context?: AgentAccelErrorContext; cause?: unknown }
47
+ ) {
44
48
  super(message);
45
49
  this.name = "AgentAccelError";
46
50
  this.code = code;
@@ -59,10 +63,26 @@ export class ConfigError extends AgentAccelError {
59
63
 
60
64
  export class ProviderError extends AgentAccelError {
61
65
  readonly status?: number;
62
- constructor(message: string, opts?: { status?: number; provider?: string; model?: string; requestId?: string; retryable?: boolean; context?: AgentAccelErrorContext }) {
66
+ constructor(
67
+ message: string,
68
+ opts?: {
69
+ status?: number;
70
+ provider?: string;
71
+ model?: string;
72
+ requestId?: string;
73
+ retryable?: boolean;
74
+ context?: AgentAccelErrorContext;
75
+ }
76
+ ) {
63
77
  super("provider", message, {
64
78
  retryable: opts?.retryable ?? false,
65
- context: { ...opts?.context, provider: opts?.provider, model: opts?.model, status: opts?.status, requestId: opts?.requestId },
79
+ context: {
80
+ ...opts?.context,
81
+ provider: opts?.provider,
82
+ model: opts?.model,
83
+ status: opts?.status,
84
+ requestId: opts?.requestId,
85
+ },
66
86
  });
67
87
  this.name = "ProviderError";
68
88
  this.status = opts?.status;
@@ -124,7 +144,9 @@ export function isAbortError(error: unknown): boolean {
124
144
  /** Format without ANSI for CLI pretty-printing. */
125
145
  export function formatErrorPlain(err: unknown): string {
126
146
  if (err instanceof AgentAccelError) {
127
- const ctx = err.context?.provider ? ` [${err.context.provider}${err.context.model ? `/${err.context.model}` : ""}]` : "";
147
+ const ctx = err.context?.provider
148
+ ? ` [${err.context.provider}${err.context.model ? `/${err.context.model}` : ""}]`
149
+ : "";
128
150
  const status = err.context?.status ? ` (${err.context.status})` : "";
129
151
  return `[${err.code}]${ctx}${status}: ${err.message}`;
130
152
  }
@@ -12,6 +12,9 @@ export interface TextPart {
12
12
  /** Detail level a provider spends on one image or video (more detail = more input tokens). Google (images and videos) and OpenRouter (images, as `image_url.detail`) honour it; other providers ignore it with a warning. */
13
13
  export type MediaResolution = "low" | "medium" | "high" | "ultra_high";
14
14
 
15
+ /** How a provider reads a video: `"static"` (fixed-rate frames, the default) or `"agentic"` (the model navigates the timeline and loads only what the prompt needs). Only Google honours it, and agentic mode only on some Gemini models; other providers ignore it with a warning. */
16
+ export type VideoProcessing = "static" | "agentic";
17
+
15
18
  /** Image content accepted as a URL, data, path, or binary value. */
16
19
  export interface ImagePart {
17
20
  type: "image";
@@ -55,6 +58,8 @@ export interface VideoPart {
55
58
  mimeType?: string;
56
59
  /** Detail level per frame (Google: `low`/`medium` 70, `high` 280 tokens). */
57
60
  resolution?: MediaResolution;
61
+ /** How the video is read (Google: `"agentic"` lets the model explore a long video instead of loading every frame). */
62
+ processing?: VideoProcessing;
58
63
  }
59
64
 
60
65
  /** Assistant-generated structured tool invocation. */
@@ -89,6 +94,8 @@ export interface ToolResultPart {
89
94
  logFile?: string;
90
95
  /** The tool ended the run with `ctx.stop(reason)` (see `ToolExecutionContext.stop`). */
91
96
  stopped?: boolean;
97
+ /** Media the tool returned with `toolContent` (inline data URLs; `result` holds its text). Google sends it to the model. */
98
+ parts?: ContentPart[];
92
99
  }
93
100
 
94
101
  /** Provider reasoning/thinking content. */
@@ -2,6 +2,7 @@ import type { ThinkingConfig, CacheOption, ServiceTier, TokenUsage } from "./cor
2
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
+ import type { Prompts } from "../prompts/prompts.ts";
5
6
 
6
7
  // ---------------------------------------------------------------------------
7
8
  // Provider identity — battle-tested: matches models.dev provider keys
@@ -169,7 +170,7 @@ export interface ProviderRequestOptions {
169
170
  cache?: CacheOption;
170
171
  serviceTier?: ServiceTier;
171
172
  tools?: StandardToolDeclaration[];
172
- toolChoice?: "auto" | "none" | "required" | { type: "function"; function: { name: string } };
173
+ toolChoice?: "auto" | "none" | "required" | "validated" | { type: "function"; function: { name: string } };
173
174
  signal?: AbortSignal;
174
175
  sessionId?: string;
175
176
  env?: Record<string, string>;
@@ -185,12 +186,10 @@ export interface ProviderRequestOptions {
185
186
  googleTier?: "free" | "paid";
186
187
  /** Upper bound on generated tokens for one model call. */
187
188
  maxOutputTokens?: number;
188
- /** Stop sequence(s). Chat Completions only; other wires warn+drop. */
189
- stop?: string[];
190
- /** Deterministic-sampling seed. Chat Completions only; other wires warn+drop. */
191
- seed?: number;
192
189
  /** `fetch` implementation used for the provider request (defaults to global `fetch`). */
193
190
  fetch?: typeof fetch;
191
+ /** The SDK prompts adapters put in a request (`AgentConfig.prompts`); defaults when unset. */
192
+ prompts?: Pick<Prompts, "structuredOutput" | "toolBridge">;
194
193
  /** Base backoff delay in ms for provider retries (default 250). */
195
194
  retryBaseDelayMs?: number;
196
195
  /** Total time budget in ms for provider retries (default 30000). */
@@ -18,12 +18,12 @@ export interface ScheduleSpec {
18
18
  /**
19
19
  * What happens when the host program was not running at a delivery time. `"skip"`: move on to the next time (a one-shot
20
20
  * is done). `"run"`: run it as soon as the program is back. Omitted: the action is marked `missed` and waits for
21
- * `runScheduledAction` or `skipScheduledAction`.
21
+ * `agent.scheduled.run` or `agent.scheduled.skip`.
22
22
  */
23
23
  onMissed?: "skip" | "run";
24
24
  }
25
25
 
26
- /** What `createScheduledAction` receives: dynamic sub-agent fields plus the schedule. */
26
+ /** What `agent.scheduled.create` receives: dynamic sub-agent fields plus the schedule. */
27
27
  export interface ScheduledActionInput {
28
28
  role?: string;
29
29
  instructions: string;
@@ -40,7 +40,7 @@ export interface ScheduledActionInput {
40
40
  };
41
41
  }
42
42
 
43
- /** What `updateScheduledAction` may change. `id` and the creating agent are immutable. */
43
+ /** What `agent.scheduled.update` may change. `id` and the creating agent are immutable. */
44
44
  export type ScheduledActionPatch = Partial<Omit<ScheduledActionInput, "scheduled_action">> & {
45
45
  scheduled_action?: Partial<Omit<ScheduledActionInput["scheduled_action"], "id">>;
46
46
  };
@@ -81,7 +81,7 @@ export interface ScheduledDelivery {
81
81
  late: boolean;
82
82
  }
83
83
 
84
- /** Result of `deleteScheduledAction`. */
84
+ /** Result of `agent.scheduled.delete`. */
85
85
  export type DeleteResult =
86
86
  | { ok: true }
87
87
  | { ok: false; reason: "not_found" }
package/src/types/tool.ts CHANGED
@@ -21,7 +21,13 @@ export interface ToolExecutionContext {
21
21
  /** Session identifier associated with this invocation. */
22
22
  sessionId?: string;
23
23
  /** Forwards live sub-agent deltas (used by the `subagents` tool for realtime tracking). */
24
- onSubagentEvent?: (event: { trackingId: string; delta?: string; thinkingDelta?: string; partialText?: string; partialThinking?: string }) => void;
24
+ onSubagentEvent?: (event: {
25
+ trackingId: string;
26
+ delta?: string;
27
+ thinkingDelta?: string;
28
+ partialText?: string;
29
+ partialThinking?: string;
30
+ }) => void;
25
31
  // Tool API v2 context additions (all optional, additive).
26
32
  /** Stable id for this run (`newRunId`). Mirrors SubAgentRunIds.runId. */
27
33
  runId?: string;
@@ -147,6 +153,8 @@ export interface ToolResultRecord {
147
153
  logFile?: string;
148
154
  /** The tool ended the run with `ctx.stop(reason)`; `result` is the reason. */
149
155
  stopped?: boolean;
156
+ /** Media returned with `toolContent`, as inline data URLs (`result` is its text). Set only by the executor. */
157
+ parts?: import("./message.ts").ContentPart[];
150
158
  }
151
159
 
152
160
  /**
@@ -161,5 +169,9 @@ export const TRUNCATED_ARGS_KEY = "__truncated_raw";
161
169
  export function isTruncatedArguments(args: unknown): boolean {
162
170
  if (!args || typeof args !== "object" || Array.isArray(args)) return false;
163
171
  const keys = Object.keys(args as Record<string, unknown>);
164
- return keys.length === 1 && keys[0] === TRUNCATED_ARGS_KEY && typeof (args as Record<string, unknown>)[TRUNCATED_ARGS_KEY] === "string";
172
+ return (
173
+ keys.length === 1 &&
174
+ keys[0] === TRUNCATED_ARGS_KEY &&
175
+ typeof (args as Record<string, unknown>)[TRUNCATED_ARGS_KEY] === "string"
176
+ );
165
177
  }
@@ -28,7 +28,9 @@ for (const arg of args) {
28
28
  }
29
29
  // Reject non-numeric TTLs (e.g. --ttl=abc) instead of silently caching NaN.
30
30
  if (!Number.isFinite(customTtlMs)) {
31
- throw new ConfigError(`Invalid --ttl value "${val}": expected a number with optional h/m suffix (e.g. --ttl=24h).`);
31
+ throw new ConfigError(
32
+ `Invalid --ttl value "${val}": expected a number with optional h/m suffix (e.g. --ttl=24h).`
33
+ );
32
34
  }
33
35
  }
34
36
  }
@@ -42,13 +44,11 @@ try {
42
44
  const ttlHours = (result.ttlMs / (1000 * 60 * 60)).toFixed(1);
43
45
 
44
46
  if (result.fromCache) {
45
- console.log(
46
- `\x1b[32m✓\x1b[0m Models catalog is already fresh (within ${ttlHours}h TTL).`
47
- );
47
+ console.log(`\x1b[32m✓\x1b[0m Models catalog is already fresh (within ${ttlHours}h TTL).`);
48
48
  } else {
49
49
  console.log(
50
50
  `\x1b[32m✓\x1b[0m Successfully downloaded and cached \x1b[1m${result.modelCount.toLocaleString()}\x1b[0m models ` +
51
- `across \x1b[1m${result.providerCount}\x1b[0m providers in ${duration}s.`
51
+ `across \x1b[1m${result.providerCount}\x1b[0m providers in ${duration}s.`
52
52
  );
53
53
  }
54
54
  console.log(` \x1b[90mCache location: ${result.cachePath}\x1b[0m`);
@@ -2,23 +2,24 @@
2
2
 
3
3
  Small shared helpers. Each file has one job.
4
4
 
5
- | File | What it does |
6
- | :-- | :-- |
7
- | `durable.ts` | The ONE disk writer (`writeFileDurable`: temp, fsync, `.bak`, rename, folder fsync), `readWithBackup`, `lockFolder` (one process per folder, used for session folders; stale-lock takeover, temp sweep) and `withFileMutex` (short per-file lock for state several processes change, e.g. one scheduled action). |
8
- | `home.ts` | `resolveHome` / `setHome`: the single folder everything is stored in. |
9
- | `env.ts` | Process-env lookups and API key resolution. No `globalThis` fallback. |
10
- | `headers.ts` | Session-affinity headers, opt-in attribution, browser stripping, package version fallback. |
11
- | `media.ts` | Normalizes a path, URL, data URL, base64 or bytes to base64 and a MIME type; magic-byte sniffing. |
12
- | `safe-io.ts` | `fileAccess.roots` containment and the guarded remote fetch (private hosts refused, redirects re-checked, size cap). |
13
- | `raw-capture.ts` | The `captureRaw` policy for `AgentResponse.raw`. |
14
- | `cache.ts` | Cache-key clamp (64 chars) and the central secret redaction. |
15
- | `errors.ts` | `toConciseProviderError` (any raw provider failure becomes one-line `ProviderError`: `code` `"provider"`, `retryable` from status/code, the provider's code as `providerCode`; SDK errors pass through), `isTransientError`, and the pre-network modality guards (`ValidationError`). |
16
- | `retry.ts` | `withRetries`, `isTransientError` (re-exported from `errors.ts`), `isRetryableRun`, `sleep`. Structured data only (status, code, flags), never message text. |
17
- | `session.ts` | Id helpers (`createSessionId`, `hashSessionPart`, `createTrackingId`, run/turn ids), `parseModelRef`, `previewPrompt`. |
18
- | `serialization.ts` | `toJsonSafe`, `safeStringify`, `escapeXml`. |
19
- | `base64.ts`, `thought-signature.ts` | Runtime-agnostic base64; Gemini thought-signature helpers. |
5
+ | File | What it does |
6
+ | :---------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
7
+ | `durable.ts` | The ONE disk writer (`writeFileDurable`: temp, fsync, `.bak`, rename, folder fsync), `readWithBackup`, `lockFolder` (one process per folder, used for session folders; stale-lock takeover, temp sweep) and `withFileMutex` (short per-file lock for state several processes change, e.g. one scheduled action). |
8
+ | `home.ts` | `resolveHome` / `setHome`: the single folder everything is stored in. |
9
+ | `env.ts` | Process-env lookups and API key resolution. No `globalThis` fallback. |
10
+ | `headers.ts` | Session-affinity headers, opt-in attribution, browser stripping, package version fallback. |
11
+ | `media.ts` | Normalizes a path, URL, data URL, base64 or bytes to base64 and a MIME type; magic-byte sniffing. |
12
+ | `safe-io.ts` | `fileAccess.roots` containment and the guarded remote fetch (private hosts refused, redirects re-checked, size cap). |
13
+ | `raw-capture.ts` | The `captureRaw` policy for `AgentResponse.raw`. |
14
+ | `cache.ts` | Cache-key clamp (64 chars) and the central secret redaction. |
15
+ | `errors.ts` | `toConciseProviderError` (any raw provider failure becomes one-line `ProviderError`: `code` `"provider"`, `retryable` from status/code, the provider's code as `providerCode`; SDK errors pass through), `isTransientError`, and the pre-network modality guards (`ValidationError`). |
16
+ | `retry.ts` | `withRetries`, `isTransientError` (re-exported from `errors.ts`), `isRetryableRun`, `sleep`. Structured data only (status, code, flags), never message text. |
17
+ | `session.ts` | Id helpers (`createSessionId`, `hashSessionPart`, `createTrackingId`, run/turn ids), `parseModelRef`, `previewPrompt`. |
18
+ | `serialization.ts` | `toJsonSafe`, `safeStringify`, `escapeXml`. |
19
+ | `base64.ts`, `thought-signature.ts` | Runtime-agnostic base64; Gemini thought-signature helpers. |
20
20
 
21
21
  ## Invariants
22
+
22
23
  - Never write state with a bare `writeFileSync`; use `writeFileDurable`.
23
24
  - `media.ts` and `safe-io.ts` import `node:*` lazily so browser bundles keep working.
24
25
  - Redaction is central (`cache.ts`); adapters must not keep their own deny-lists.
@@ -58,9 +58,7 @@ export function redactValue(value: unknown): unknown {
58
58
  * Deny-list (name contains): *key*, *token*, *secret*, cookie, authorization.
59
59
  * Value patterns (redacted regardless of name): sk-, AIza, gsk-, xox-.
60
60
  */
61
- export function redactHeadersCentral(
62
- headers: Record<string, unknown> | undefined | null
63
- ): Record<string, unknown> {
61
+ export function redactHeadersCentral(headers: Record<string, unknown> | undefined | null): Record<string, unknown> {
64
62
  if (!headers || typeof headers !== "object") return {};
65
63
  const out: Record<string, unknown> = {};
66
64
  for (const [k, v] of Object.entries(headers)) {
@@ -32,7 +32,11 @@ export function fsyncFolder(dir: string): void {
32
32
  * Durable atomic write: temp file (0600) -> fsync -> previous version kept as `<file>.bak` (hard link, no copy)
33
33
  * -> rename -> fsync of the folder. Throws on failure; the temp file is removed. `backup: false` skips the `.bak`.
34
34
  */
35
- export function writeFileDurable(filePath: string, content: string | Uint8Array, opts: { backup?: boolean } = {}): void {
35
+ export function writeFileDurable(
36
+ filePath: string,
37
+ content: string | Uint8Array,
38
+ opts: { backup?: boolean } = {}
39
+ ): void {
36
40
  const dir = path.dirname(filePath);
37
41
  try {
38
42
  fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
@@ -73,7 +77,10 @@ export function writeFileDurable(filePath: string, content: string | Uint8Array,
73
77
  * Reads `file`; when `isValid` rejects it, falls back to `<file>.bak`. `undefined` when the file is missing (a deleted
74
78
  * file is never resurrected from its backup). When both are invalid the primary text comes back, `fromBackup: false`.
75
79
  */
76
- export function readWithBackup(file: string, isValid: (text: string) => boolean): { text: string; fromBackup: boolean } | undefined {
80
+ export function readWithBackup(
81
+ file: string,
82
+ isValid: (text: string) => boolean
83
+ ): { text: string; fromBackup: boolean } | undefined {
77
84
  let text: string;
78
85
  try {
79
86
  text = fs.readFileSync(file, "utf8");
@@ -234,7 +241,8 @@ const heldMutexes = new Set<string>();
234
241
  */
235
242
  export function withFileMutex<T>(lockFile: string, fn: () => T): T {
236
243
  const file = path.resolve(lockFile);
237
- if (heldMutexes.has(file)) throw new ConfigError(`[Agent Accelerator] Mutex "${file}" is already held by this process (nested use).`);
244
+ if (heldMutexes.has(file))
245
+ throw new ConfigError(`[Agent Accelerator] Mutex "${file}" is already held by this process (nested use).`);
238
246
  const mine = JSON.stringify({ pid: process.pid, host: os.hostname(), at: Date.now() });
239
247
  const deadline = Date.now() + MUTEX_WAIT_MS;
240
248
  let unreadable = 0;
@@ -265,7 +273,8 @@ export function withFileMutex<T>(lockFile: string, fn: () => T): T {
265
273
  owner = undefined;
266
274
  }
267
275
  let stale = false;
268
- if (typeof owner?.pid !== "number") stale = ++unreadable > 5; // empty or garbage: another process may be mid-write
276
+ if (typeof owner?.pid !== "number")
277
+ stale = ++unreadable > 5; // empty or garbage: another process may be mid-write
269
278
  else if (owner.host === os.hostname() && !processAlive(owner.pid)) stale = true;
270
279
  else if (Date.now() - mtime > MUTEX_MAX_AGE_MS) stale = true;
271
280
  if (stale) {
@@ -276,7 +285,10 @@ export function withFileMutex<T>(lockFile: string, fn: () => T): T {
276
285
  }
277
286
  continue;
278
287
  }
279
- if (Date.now() > deadline) throw new ConfigError(`[Agent Accelerator] Timed out after ${MUTEX_WAIT_MS} ms waiting for the lock "${file}" (held by process ${String(owner?.pid)}).`);
288
+ if (Date.now() > deadline)
289
+ throw new ConfigError(
290
+ `[Agent Accelerator] Timed out after ${MUTEX_WAIT_MS} ms waiting for the lock "${file}" (held by process ${String(owner?.pid)}).`
291
+ );
280
292
  pause(typeof owner?.pid === "number" ? 2 : 40);
281
293
  }
282
294
  heldMutexes.add(file);
package/src/utils/env.ts CHANGED
@@ -11,7 +11,10 @@ export function getEnv(key: string, fallback?: string): string | undefined {
11
11
  if (typeof process !== "undefined" && process.env && process.env[key]) {
12
12
  // Trim quotes/whitespace from .env files (`sk-..."\n` -> 401 otherwise).
13
13
  const raw = String(process.env[key]);
14
- const trimmed = raw.trim().replace(/^["']|["']$/g, "").trim();
14
+ const trimmed = raw
15
+ .trim()
16
+ .replace(/^["']|["']$/g, "")
17
+ .trim();
15
18
  if (trimmed) return trimmed;
16
19
  }
17
20
  return fallback;
@@ -27,7 +30,10 @@ export type ProviderEnv = Record<string, string>;
27
30
  export function getApiKey(provider: string, explicitKey?: string, env?: ProviderEnv): string | undefined {
28
31
  const clean = (v: unknown): string | undefined => {
29
32
  if (typeof v !== "string") return undefined;
30
- const t = v.trim().replace(/^["']|["']$/g, "").trim();
33
+ const t = v
34
+ .trim()
35
+ .replace(/^["']|["']$/g, "")
36
+ .trim();
31
37
  return t || undefined;
32
38
  };
33
39
  const cleanedExplicit = clean(explicitKey);
@@ -56,11 +62,7 @@ export function getApiKey(provider: string, explicitKey?: string, env?: Provider
56
62
  switch (provider.toLowerCase()) {
57
63
  case "google":
58
64
  case "gemini":
59
- return (
60
- getEnv("GEMINI_API_KEY") ||
61
- getEnv("GOOGLE_API_KEY") ||
62
- getEnv("GOOGLE_GENAI_API_KEY")
63
- );
65
+ return getEnv("GEMINI_API_KEY") || getEnv("GOOGLE_API_KEY") || getEnv("GOOGLE_GENAI_API_KEY");
64
66
  case "openrouter":
65
67
  return getEnv("OPENROUTER_API_KEY");
66
68
  case "openai":