agent-accelerator 0.3.0 → 0.4.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.
- package/package.json +1 -1
- package/src/agent/agent.ts +845 -116
- package/src/agent/context.ts +35 -0
- package/src/agent/delegation.ts +390 -36
- package/src/agent/loop.ts +785 -110
- package/src/agent/subagent.ts +59 -0
- package/src/index.ts +173 -59
- package/src/models/catalog-cache.ts +68 -25
- package/src/models/catalog.ts +123 -18
- package/src/providers/google.ts +152 -43
- package/src/providers/openai-compat.ts +246 -96
- package/src/providers/openai.ts +274 -79
- package/src/providers/openrouter.ts +191 -82
- package/src/providers/registry.ts +16 -12
- package/src/providers/shared.ts +190 -0
- package/src/providers.ts +259 -9
- package/src/session/store.ts +521 -33
- package/src/streaming/event-stream.ts +156 -41
- package/src/tools/executor.ts +248 -15
- package/src/tools/tool.ts +147 -3
- package/src/types/agent.ts +244 -2
- package/src/types/errors.ts +133 -0
- package/src/types/model.ts +22 -1
- package/src/types/response.ts +133 -3
- package/src/types/tool.ts +69 -0
- package/src/update-models.ts +8 -2
- package/src/utils/cache.ts +68 -1
- package/src/utils/env.ts +23 -13
- package/src/utils/errors.ts +15 -1
- package/src/utils/headers.ts +153 -19
- package/src/utils/retry.ts +27 -13
- package/src/utils/session.ts +284 -0
- package/src/providers/openrouter-responses.ts +0 -949
- package/src/types/provider-payloads.ts +0 -352
package/src/tools/tool.ts
CHANGED
|
@@ -4,7 +4,90 @@ import type {
|
|
|
4
4
|
StandardToolDeclaration,
|
|
5
5
|
ToolExecuteFn,
|
|
6
6
|
} from "../types/tool.ts";
|
|
7
|
+
import type { ThinkingLevel } from "../types/core.ts";
|
|
8
|
+
import type { ThinkingOption } from "../types/agent.ts";
|
|
7
9
|
import { zodToJsonSchema } from "./schema.ts";
|
|
10
|
+
import { ConfigError } from "../types/errors.ts";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Valid tool-name pattern (Q-55): 1–64 chars of letters, digits, `_`, `-`.
|
|
14
|
+
* Names are validated at registration in {@link tool} (record-key fallback
|
|
15
|
+
* included); invalid names throw `ConfigError` with a fix.
|
|
16
|
+
*/
|
|
17
|
+
export const TOOL_NAME_PATTERN = /^[a-zA-Z0-9_-]{1,64}$/;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Throws `ConfigError` when `name` is not a valid tool name.
|
|
21
|
+
*
|
|
22
|
+
* @example `assertValidToolName("get_status");`
|
|
23
|
+
*/
|
|
24
|
+
export function assertValidToolName(name: string): void {
|
|
25
|
+
if (!TOOL_NAME_PATTERN.test(name)) {
|
|
26
|
+
throw new ConfigError(
|
|
27
|
+
`[Agent Accelerator] Invalid tool name "${name}": expected 1-64 characters of letters, digits, "_" or "-". ` +
|
|
28
|
+
`Fix: rename the tool (e.g. "get_status").`
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Normalizes the Q-52 canonical `thinking` option to a bare level.
|
|
35
|
+
* Accepts a level string or `{ level, budgetTokens }`; `budgetTokens` is
|
|
36
|
+
* carried separately by providers, so only the level is returned here.
|
|
37
|
+
* Resolution honors `thinking ?? thinkingLevel`.
|
|
38
|
+
*
|
|
39
|
+
* @example `normalizeThinkingV2({ level: "medium" }, "low"); // "medium"`
|
|
40
|
+
*/
|
|
41
|
+
export function normalizeThinkingV2(
|
|
42
|
+
thinking?: ThinkingOption,
|
|
43
|
+
thinkingLevel?: ThinkingLevel
|
|
44
|
+
): ThinkingLevel | undefined {
|
|
45
|
+
if (thinking !== undefined) {
|
|
46
|
+
if (typeof thinking === "string") return thinking;
|
|
47
|
+
return thinking.level ?? thinkingLevel;
|
|
48
|
+
}
|
|
49
|
+
return thinkingLevel;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Resolves the Q-51 canonical busy policy. `whenBusy` wins over `mode`.
|
|
54
|
+
*
|
|
55
|
+
* @example `resolveWhenBusy({ whenBusy: "queue", mode: "steer" }); // "queue"`
|
|
56
|
+
*/
|
|
57
|
+
export function resolveWhenBusy(
|
|
58
|
+
config?: { mode?: "steer" | "queue" | "auto"; whenBusy?: "steer" | "queue" | "auto" }
|
|
59
|
+
): "steer" | "queue" | "auto" {
|
|
60
|
+
const v = config?.whenBusy ?? config?.mode ?? "auto";
|
|
61
|
+
return v === "steer" || v === "queue" ? v : "auto";
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Maps the Q-52 canonical worker-timeout knobs to the legacy `timeout` value
|
|
66
|
+
* (`0` = no limit, `-1` = model sets per-task `timeoutMs`, `>0` = fixed ms).
|
|
67
|
+
* An explicit `timeout`/`workerTimeoutMs` wins over `modelMaySetTimeout`.
|
|
68
|
+
*
|
|
69
|
+
* @example `normalizeWorkerTimeout({ modelMaySetTimeout: true }); // -1`
|
|
70
|
+
*/
|
|
71
|
+
export function normalizeWorkerTimeout(
|
|
72
|
+
config?: { timeout?: number; workerTimeoutMs?: number; modelMaySetTimeout?: boolean }
|
|
73
|
+
): number {
|
|
74
|
+
const explicit = config?.workerTimeoutMs ?? config?.timeout;
|
|
75
|
+
if (Number.isFinite(explicit as number)) return Math.floor(explicit as number);
|
|
76
|
+
if (config?.modelMaySetTimeout === true) return -1;
|
|
77
|
+
return 0;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Normalizes the Q-52 `maxAttempts` alias to a `maxTries` value.
|
|
82
|
+
* `maxTries` wins when both are set.
|
|
83
|
+
*
|
|
84
|
+
* @example `normalizeMaxAttempts({ maxAttempts: 3 }); // 3`
|
|
85
|
+
*/
|
|
86
|
+
export function normalizeMaxAttempts(
|
|
87
|
+
config?: { maxTries?: number | string; maxAttempts?: number | string }
|
|
88
|
+
): number | string | undefined {
|
|
89
|
+
return config?.maxTries ?? config?.maxAttempts;
|
|
90
|
+
}
|
|
8
91
|
|
|
9
92
|
/** Complete configuration accepted by {@link tool}. */
|
|
10
93
|
export interface CreateToolOptions<TInput = any, TOutput = any> {
|
|
@@ -22,8 +105,54 @@ export interface CreateToolOptions<TInput = any, TOutput = any> {
|
|
|
22
105
|
timeoutMs?: number;
|
|
23
106
|
/** Maximum total attempts for transient failures. `0`/omitted means no configured limit. */
|
|
24
107
|
maxTries?: number | string;
|
|
108
|
+
/**
|
|
109
|
+
* Canonical alias for `maxTries` (Q-52). When both are set, `maxTries` wins.
|
|
110
|
+
* Normalized by `normalizeMaxAttempts`.
|
|
111
|
+
*/
|
|
112
|
+
maxAttempts?: number | string;
|
|
25
113
|
/** Maximum simultaneous executions of this tool. `0`/omitted uses the global pool. */
|
|
26
114
|
maxConcurrency?: number | string;
|
|
115
|
+
/**
|
|
116
|
+
* Whether this tool is safe to retry on transient failures. Retry defaults
|
|
117
|
+
* to a single attempt unless this is `true` (bounded retries) or `maxTries`
|
|
118
|
+
* explicitly overrides the attempt count.
|
|
119
|
+
*/
|
|
120
|
+
idempotent?: boolean;
|
|
121
|
+
/**
|
|
122
|
+
* Whether an identical call may repeat across consecutive turns. Defaults to
|
|
123
|
+
* `true` for idempotent tools, `false` otherwise. Honored by repeat guards.
|
|
124
|
+
*/
|
|
125
|
+
repeatable?: boolean;
|
|
126
|
+
/**
|
|
127
|
+
* Maximum characters of a string tool result kept for model context.
|
|
128
|
+
* Longer strings are truncated with a `[Truncated: ...]` marker.
|
|
129
|
+
* `0`/omitted uses the executor default (20000).
|
|
130
|
+
*/
|
|
131
|
+
maxResultChars?: number | string;
|
|
132
|
+
/** Optional hook to redact/replace the error message sent back to the model. */
|
|
133
|
+
redactError?: (err: unknown) => string;
|
|
134
|
+
/**
|
|
135
|
+
* Canonical alias for `redactError` (Q-55). When both are set,
|
|
136
|
+
* `redactError` wins.
|
|
137
|
+
*/
|
|
138
|
+
redact?: (err: unknown) => string;
|
|
139
|
+
/**
|
|
140
|
+
* Explicit retry policy (Q-55). Preferred over bare `maxTries` for new
|
|
141
|
+
* code; the executor reads via `(toolDef as any).retry`.
|
|
142
|
+
*/
|
|
143
|
+
retry?: { maxRetries?: number; baseDelayMs?: number; maxDelayMs?: number };
|
|
144
|
+
/** Free-form tags for grouping/filtering tools (e.g. `["web", "readonly"]`). */
|
|
145
|
+
tags?: string[];
|
|
146
|
+
/**
|
|
147
|
+
* When true, the host must approve this call before execution. Stored only;
|
|
148
|
+
* enforcement is wired by the host, default open.
|
|
149
|
+
*/
|
|
150
|
+
needsApproval?: boolean;
|
|
151
|
+
/**
|
|
152
|
+
* Optional output schema (Zod or JSON Schema, stored as `unknown` so `zod`
|
|
153
|
+
* stays optional for type-only consumers).
|
|
154
|
+
*/
|
|
155
|
+
output?: unknown;
|
|
27
156
|
/** Implementation invoked with validated input and execution metadata. */
|
|
28
157
|
execute: ToolExecuteFn<TInput, TOutput>;
|
|
29
158
|
}
|
|
@@ -46,7 +175,7 @@ export interface CreateToolOptions<TInput = any, TOutput = any> {
|
|
|
46
175
|
* maxTries: 2,
|
|
47
176
|
* maxConcurrency: 2,
|
|
48
177
|
* execute: async ({ username }, ctx) => {
|
|
49
|
-
* return username === "
|
|
178
|
+
* return username === "alice" ? "Valid" : "Invalid";
|
|
50
179
|
* },
|
|
51
180
|
* });
|
|
52
181
|
* ```
|
|
@@ -57,6 +186,10 @@ export interface CreateToolOptions<TInput = any, TOutput = any> {
|
|
|
57
186
|
export function tool<TInput = any, TOutput = any>(
|
|
58
187
|
options: CreateToolOptions<TInput, TOutput>
|
|
59
188
|
): ToolDefinition<TInput, TOutput> {
|
|
189
|
+
// Q-55: names are validated at registration (record-key fallback included).
|
|
190
|
+
if (options.name !== undefined) assertValidToolName(options.name);
|
|
191
|
+
// ToolDefinition (src/types/tool.ts) carries the v2 policies as typed
|
|
192
|
+
// optionals; the executor additionally reads them via `(toolDef as any).*`.
|
|
60
193
|
return {
|
|
61
194
|
name: options.name,
|
|
62
195
|
description: options.description,
|
|
@@ -64,10 +197,21 @@ export function tool<TInput = any, TOutput = any>(
|
|
|
64
197
|
parameters: options.parameters,
|
|
65
198
|
strict: options.strict,
|
|
66
199
|
timeoutMs: options.timeoutMs,
|
|
67
|
-
maxTries: options
|
|
200
|
+
maxTries: normalizeMaxAttempts(options),
|
|
68
201
|
maxConcurrency: options.maxConcurrency,
|
|
69
202
|
execute: options.execute,
|
|
70
|
-
|
|
203
|
+
...(options.idempotent !== undefined ? { idempotent: options.idempotent } : {}),
|
|
204
|
+
...(options.repeatable !== undefined ? { repeatable: options.repeatable } : {}),
|
|
205
|
+
...(options.maxResultChars !== undefined ? { maxResultChars: options.maxResultChars } : {}),
|
|
206
|
+
...(options.redactError !== undefined ? { redactError: options.redactError } : {}),
|
|
207
|
+
...(options.redactError === undefined && options.redact !== undefined ? { redactError: options.redact } : {}),
|
|
208
|
+
...(options.redact !== undefined ? { redact: options.redact } : {}),
|
|
209
|
+
...(options.retry !== undefined ? { retry: options.retry } : {}),
|
|
210
|
+
...(options.tags !== undefined ? { tags: options.tags } : {}),
|
|
211
|
+
...(options.needsApproval !== undefined ? { needsApproval: options.needsApproval } : {}),
|
|
212
|
+
...(options.output !== undefined ? { output: options.output } : {}),
|
|
213
|
+
...(options.maxAttempts !== undefined ? { maxAttempts: options.maxAttempts } : {}),
|
|
214
|
+
} as ToolDefinition<TInput, TOutput>;
|
|
71
215
|
}
|
|
72
216
|
|
|
73
217
|
/**
|
package/src/types/agent.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ModelSpec } from "./model.ts";
|
|
2
2
|
import type { ToolDefinition } from "./tool.ts";
|
|
3
3
|
import type {
|
|
4
4
|
ThinkingLevel,
|
|
@@ -7,6 +7,71 @@ import type {
|
|
|
7
7
|
} from "./core.ts";
|
|
8
8
|
import type { ModelProviderInstance } from "../providers/registry.ts";
|
|
9
9
|
import type { Agent } from "../agent/agent.ts";
|
|
10
|
+
import type { z } from "zod";
|
|
11
|
+
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
// Q-52/Q-53/Q-54 canonical option types (all additive; nothing renamed).
|
|
14
|
+
//
|
|
15
|
+
// Canonical homes (one place per setting):
|
|
16
|
+
// - Worker model: `dynamicSubagents.model` only. Top-level `subagentModel`
|
|
17
|
+
// is a deprecated alias (still honored: dynamicSubagents.model ??
|
|
18
|
+
// subagentModel ?? SUB_AGENT_MODEL ?? parent model).
|
|
19
|
+
// - Session identity: agent-level `sessionId` only (per-run `sessionId`
|
|
20
|
+
// selects a named session entry; `cache.sessionId` is a deprecated alias).
|
|
21
|
+
// - Model identity: `ModelRef` = string (`provider/model`) | catalog
|
|
22
|
+
// `ModelSpec` | `ModelProviderInstance` ({ provider, model, apiKey?,
|
|
23
|
+
// baseUrl? }). Explicit `Agent` config always wins over env; constructing
|
|
24
|
+
// an `Agent` with an explicit `model` reads no env for routing.
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
|
|
27
|
+
/** Canonical model reference: `provider/model` string, catalog spec, or provider helper result. */
|
|
28
|
+
export type ModelRef = string | ModelSpec | ModelProviderInstance;
|
|
29
|
+
|
|
30
|
+
/** Canonical reasoning option (Q-52): bare level, or level + explicit token budget. */
|
|
31
|
+
export type ThinkingOption = ThinkingLevel | { level?: ThinkingLevel; budgetTokens?: number };
|
|
32
|
+
|
|
33
|
+
/** Canonical sampling knobs (Q-54). Read by the loop via `(config as any).sampling`. */
|
|
34
|
+
export interface SamplingConfig {
|
|
35
|
+
temperature?: number;
|
|
36
|
+
topP?: number;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Canonical retry policy (Q-54). Read by the loop via `(config as any).retry`. */
|
|
40
|
+
export interface RetryPolicyConfig {
|
|
41
|
+
maxRetries?: number;
|
|
42
|
+
baseDelayMs?: number;
|
|
43
|
+
maxDelayMs?: number;
|
|
44
|
+
budgetMs?: number;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Minimal logger surface for Agent/loop telemetry (Q-54). */
|
|
48
|
+
export interface AgentLogger {
|
|
49
|
+
debug?: (...args: unknown[]) => void;
|
|
50
|
+
info?: (...args: unknown[]) => void;
|
|
51
|
+
warn?: (...args: unknown[]) => void;
|
|
52
|
+
error?: (...args: unknown[]) => void;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Canonical tool-selection control (Q-54). String form is the portable subset. */
|
|
56
|
+
export type ToolChoiceOption =
|
|
57
|
+
| "auto"
|
|
58
|
+
| "none"
|
|
59
|
+
| "required"
|
|
60
|
+
| { mode?: "auto" | "none" | "required"; tool?: string };
|
|
61
|
+
|
|
62
|
+
/** Canonical stop control (Q-54): sequence(s) halting generation. */
|
|
63
|
+
export type StopOption = string | string[];
|
|
64
|
+
|
|
65
|
+
/** Canonical structured-output input (Q-61). Zod, JSON Schema, or explicit form. */
|
|
66
|
+
export type StructuredOutputOption =
|
|
67
|
+
| z.ZodType
|
|
68
|
+
| Record<string, unknown>
|
|
69
|
+
| {
|
|
70
|
+
name?: string;
|
|
71
|
+
description?: string;
|
|
72
|
+
schema: z.ZodType | Record<string, unknown>;
|
|
73
|
+
strict?: boolean;
|
|
74
|
+
};
|
|
10
75
|
|
|
11
76
|
/** Configuration used to construct an {@link Agent}. */
|
|
12
77
|
export interface AgentConfig {
|
|
@@ -30,6 +95,13 @@ export interface AgentConfig {
|
|
|
30
95
|
tools?: Record<string, ToolDefinition> | ToolDefinition[];
|
|
31
96
|
/** Requested reasoning level, validated against the model catalog. */
|
|
32
97
|
thinkingLevel?: ThinkingLevel;
|
|
98
|
+
/**
|
|
99
|
+
* Canonical reasoning option (Q-52, preferred over `thinkingLevel`).
|
|
100
|
+
* Accepts a bare level or `{ level, budgetTokens }`. When both are set,
|
|
101
|
+
* `thinking` wins; resolution honors `thinking ?? thinkingLevel`.
|
|
102
|
+
* See `normalizeThinkingV2` (re-exported from the root entry).
|
|
103
|
+
*/
|
|
104
|
+
thinking?: ThinkingOption;
|
|
33
105
|
/** Prompt-cache retention and session-affinity settings. */
|
|
34
106
|
cache?: CacheConfig;
|
|
35
107
|
/** Provider service tier, when supported. */
|
|
@@ -45,12 +117,23 @@ export interface AgentConfig {
|
|
|
45
117
|
sessionId?: string;
|
|
46
118
|
/** Headers merged into every provider request. */
|
|
47
119
|
headers?: Record<string, string>;
|
|
48
|
-
/**
|
|
120
|
+
/**
|
|
121
|
+
* Maximum model/tool turns per run. `0` or omitted means infinite
|
|
122
|
+
* (legacy `0` alias, normalized to `Infinity`; prefer omitting).
|
|
123
|
+
*/
|
|
49
124
|
maxTurns?: number;
|
|
50
125
|
/** Fixed worker agents exposed as delegation tools. */
|
|
51
126
|
subagents?: (Agent | { name: string; description: string; agent: Agent })[];
|
|
52
127
|
/** Clears conversation state before and after every run. */
|
|
53
128
|
stateless?: boolean;
|
|
129
|
+
/**
|
|
130
|
+
* Mid-session queue policy for new queries arriving while a run is active.
|
|
131
|
+
* - `{ mode: "steer" }`: only steer (inject into current turn) is allowed.
|
|
132
|
+
* - `{ mode: "queue" }`: only queue (next-turn follow-up) is allowed.
|
|
133
|
+
* - `{ mode: "auto" }` (default): caller picks per message via `enqueue` or `steer()`/`queue()`.
|
|
134
|
+
* `interrupt` (cancel stream now) is always available via `interrupt()`/`cancel()`/`AbortSignal`.
|
|
135
|
+
*/
|
|
136
|
+
midSession?: MidSessionConfig;
|
|
54
137
|
/**
|
|
55
138
|
* Durable session persistence: when set, the session file is rewritten
|
|
56
139
|
* after every step (user turn, assistant turn, tool batch, sub-agent
|
|
@@ -59,6 +142,84 @@ export interface AgentConfig {
|
|
|
59
142
|
* pretty `.session.json` file. Writes are atomic and never fail a turn.
|
|
60
143
|
*/
|
|
61
144
|
persist?: { dir?: string; file?: string };
|
|
145
|
+
// Q-54 missing knobs (all optional, additive; loop reads via `(config as any).*`).
|
|
146
|
+
/** Canonical tool-selection control (`"auto"` default). */
|
|
147
|
+
toolChoice?: ToolChoiceOption;
|
|
148
|
+
/** Maximum output tokens for one model call. */
|
|
149
|
+
maxOutputTokens?: number;
|
|
150
|
+
/** Sampling knobs. */
|
|
151
|
+
sampling?: SamplingConfig;
|
|
152
|
+
/** Stop sequence(s). */
|
|
153
|
+
stop?: StopOption;
|
|
154
|
+
/** Deterministic seed, when the provider supports it. */
|
|
155
|
+
seed?: number;
|
|
156
|
+
/**
|
|
157
|
+
* Optional output schema (Zod, JSON Schema, or `{ name, schema, strict }`,
|
|
158
|
+
* see `StructuredOutputOption`, stored as `unknown` so `zod`
|
|
159
|
+
* stays optional for type-only consumers). Agent-level only; per-run
|
|
160
|
+
* overrides are intentionally unsupported. When set, the model answer is
|
|
161
|
+
* constrained to the schema on all providers and `AgentResponse.parsed`
|
|
162
|
+
* carries the validated value.
|
|
163
|
+
*/
|
|
164
|
+
output?: unknown;
|
|
165
|
+
/** Retry policy for provider calls. */
|
|
166
|
+
retry?: RetryPolicyConfig;
|
|
167
|
+
/** Fetch implementation override (defaults to global `fetch`). */
|
|
168
|
+
fetch?: typeof fetch;
|
|
169
|
+
/** Provider/env overrides forwarded to key resolution. */
|
|
170
|
+
env?: Record<string, string>;
|
|
171
|
+
/** Structured logger for loop telemetry (defaults to no-op). */
|
|
172
|
+
logger?: AgentLogger;
|
|
173
|
+
/** Control hooks (Q-59). All optional, fail-open by default. */
|
|
174
|
+
hooks?: unknown;
|
|
175
|
+
/** Threshold compaction policy (Q-60a). Fully configurable/replaceable. */
|
|
176
|
+
contextManagement?: unknown;
|
|
177
|
+
/** Telemetry bus (Q-41). Default no-op; pretty logger lives in /cli. */
|
|
178
|
+
telemetry?: { onEvent?: (e: any) => void } | unknown;
|
|
179
|
+
/** Local file access roots (Q-08). Default deny; `path` sources must resolve inside roots. */
|
|
180
|
+
fileAccess?: { roots?: string[] };
|
|
181
|
+
/** Raw capture policy (Q-11). Default "redacted": no bodies. */
|
|
182
|
+
captureRaw?: false | "redacted" | true;
|
|
183
|
+
/** Opt-in run budgets (Q-39). All fields default unlimited. */
|
|
184
|
+
budget?: Budget;
|
|
185
|
+
/** Run + network limits (Q-22). Turn/budget caps unlimited by default; network timeouts finite. */
|
|
186
|
+
limits?: AgentRunLimits;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/** Per-request + run limits (Q-22). */
|
|
190
|
+
export interface AgentRunLimits {
|
|
191
|
+
maxTurns?: number;
|
|
192
|
+
deadlineMs?: number;
|
|
193
|
+
maxTotalTokens?: number;
|
|
194
|
+
maxToolCallsPerRun?: number;
|
|
195
|
+
requestTimeoutMs?: number;
|
|
196
|
+
firstByteTimeoutMs?: number;
|
|
197
|
+
streamIdleTimeoutMs?: number;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/** Developer policy for mid-session new-query insertion while a run is active. */
|
|
201
|
+
export type MidSessionMode = "steer" | "queue" | "auto";
|
|
202
|
+
|
|
203
|
+
/** Q-51 canonical busy-policy alias: prefer `whenBusy`, `mode` kept for compat. */
|
|
204
|
+
export type WhenBusyMode = MidSessionMode;
|
|
205
|
+
|
|
206
|
+
/** Developer-configured policy for LLM-spawned dynamic sub-agents. */
|
|
207
|
+
export interface MidSessionConfig {
|
|
208
|
+
/**
|
|
209
|
+
* Which mid-session method is allowed while a run is active:
|
|
210
|
+
* - `"steer"`: only steer (inject into current turn) is allowed.
|
|
211
|
+
* - `"queue"`: only queue (next-turn follow-up) is allowed.
|
|
212
|
+
* - `"auto"` (default): caller/user picks per message via `enqueue` or `steer()`/`queue()`.
|
|
213
|
+
*/
|
|
214
|
+
mode?: MidSessionMode;
|
|
215
|
+
/**
|
|
216
|
+
* Canonical alias for `mode` (Q-51, preferred). When both are set,
|
|
217
|
+
* `whenBusy` wins. Accepted anywhere `mode` is read; resolution honors
|
|
218
|
+
* `whenBusy ?? mode ?? "auto"`.
|
|
219
|
+
*/
|
|
220
|
+
whenBusy?: MidSessionMode;
|
|
221
|
+
/** Max queued (follow-up) requests buffered while busy. Extra `queue()` calls throw. Defaults to 20. */
|
|
222
|
+
maxQueued?: number;
|
|
62
223
|
}
|
|
63
224
|
|
|
64
225
|
/** Developer-configured policy for LLM-spawned dynamic sub-agents. */
|
|
@@ -75,12 +236,29 @@ export interface DynamicSubagentsConfig {
|
|
|
75
236
|
tools?: Record<string, ToolDefinition> | ToolDefinition[];
|
|
76
237
|
/** Per-worker timeout in ms. `0` (default) = no limit. `-1` = the Main Agent sets a per-task `timeoutMs`. `>0` = fixed timeout for every worker. */
|
|
77
238
|
timeout?: number;
|
|
239
|
+
/**
|
|
240
|
+
* Canonical per-worker timeout in ms (Q-52, preferred over `timeout`).
|
|
241
|
+
* Maps to `timeout`: `workerTimeoutMs ?? timeout ?? 0`. See
|
|
242
|
+
* `normalizeWorkerTimeout` (re-exported from the root entry).
|
|
243
|
+
*/
|
|
244
|
+
workerTimeoutMs?: number;
|
|
245
|
+
/**
|
|
246
|
+
* When true, the Main Agent sets a per-task `timeoutMs` (maps to
|
|
247
|
+
* `timeout: -1`). When both are set, an explicit `timeout`/`workerTimeoutMs`
|
|
248
|
+
* wins over `modelMaySetTimeout`.
|
|
249
|
+
*/
|
|
250
|
+
modelMaySetTimeout?: boolean;
|
|
78
251
|
}
|
|
79
252
|
|
|
80
253
|
/** Per-run overrides for {@link Agent.run}, {@link Agent.ask}, and {@link Agent.stream}. */
|
|
81
254
|
export interface AgentRunOptions {
|
|
82
255
|
/** Per-run reasoning-level override, validated against the selected model. */
|
|
83
256
|
thinkingLevel?: ThinkingLevel;
|
|
257
|
+
/**
|
|
258
|
+
* Canonical per-run reasoning option (Q-52, preferred over `thinkingLevel`).
|
|
259
|
+
* Resolution honors `thinking ?? thinkingLevel`.
|
|
260
|
+
*/
|
|
261
|
+
thinking?: ThinkingOption;
|
|
84
262
|
/** Return a streaming object instead of waiting for a final response. */
|
|
85
263
|
stream?: boolean;
|
|
86
264
|
/** Cancels provider, tool, and sub-agent work. */
|
|
@@ -89,6 +267,14 @@ export interface AgentRunOptions {
|
|
|
89
267
|
sessionId?: string;
|
|
90
268
|
/** Context added only to this user turn, preserving the stable system prompt. */
|
|
91
269
|
additionalContext?: string;
|
|
270
|
+
/**
|
|
271
|
+
* Mid-session method to use when this call arrives while another run is active.
|
|
272
|
+
* Only honored when `midSession.mode` is `"auto"` (default). When the developer
|
|
273
|
+
* enforces `"steer"` or `"queue"`, that policy wins and this field is ignored.
|
|
274
|
+
* When idle, this field is ignored and the call runs immediately.
|
|
275
|
+
* Defaults to `"queue"` (finish current turn, run as next turn).
|
|
276
|
+
*/
|
|
277
|
+
enqueue?: "steer" | "queue";
|
|
92
278
|
/** Headers merged for this run only. */
|
|
93
279
|
headers?: Record<string, string>;
|
|
94
280
|
/** Called for each text delta as it streams (only when stream:true). */
|
|
@@ -101,6 +287,26 @@ export interface AgentRunOptions {
|
|
|
101
287
|
wrapThinking?: boolean;
|
|
102
288
|
/** Called after every model/tool turn with a turn summary (also drives sub-agent step logs + durable persistence). */
|
|
103
289
|
onTurn?: (turn: AgentTurnEvent) => void;
|
|
290
|
+
// Q-54 per-run knobs (all optional, additive; `output` intentionally
|
|
291
|
+
// omitted here — it is agent-level only, see AgentConfig.output).
|
|
292
|
+
/** Per-run tool-selection override. */
|
|
293
|
+
toolChoice?: ToolChoiceOption;
|
|
294
|
+
/** Per-run output-token cap. */
|
|
295
|
+
maxOutputTokens?: number;
|
|
296
|
+
/** Per-run sampling override. */
|
|
297
|
+
sampling?: SamplingConfig;
|
|
298
|
+
/** Per-run stop sequence(s). */
|
|
299
|
+
stop?: StopOption;
|
|
300
|
+
/** Per-run seed. */
|
|
301
|
+
seed?: number;
|
|
302
|
+
/** Per-run retry override. */
|
|
303
|
+
retry?: RetryPolicyConfig;
|
|
304
|
+
/** Per-run fetch override. */
|
|
305
|
+
fetch?: typeof fetch;
|
|
306
|
+
/** Per-run env overrides. */
|
|
307
|
+
env?: Record<string, string>;
|
|
308
|
+
/** Per-run logger. */
|
|
309
|
+
logger?: AgentLogger;
|
|
104
310
|
}
|
|
105
311
|
|
|
106
312
|
/** Turn summary emitted to `AgentRunOptions.onTurn` after each loop turn. */
|
|
@@ -116,3 +322,39 @@ export interface AgentTurnEvent {
|
|
|
116
322
|
/** Tool results executed this turn, if any. */
|
|
117
323
|
toolResults?: Array<{ id: string; name: string; isError?: boolean }>;
|
|
118
324
|
}
|
|
325
|
+
|
|
326
|
+
/** Optional run budgets enforced by delegation helpers (Q-39). All fields default to unlimited. */
|
|
327
|
+
export interface Budget {
|
|
328
|
+
/** Maximum total cost in USD across this agent + spawned workers. */
|
|
329
|
+
maxCostUsd?: number;
|
|
330
|
+
/** Maximum total tokens across this agent + spawned workers. */
|
|
331
|
+
maxTotalTokens?: number;
|
|
332
|
+
/** Maximum wall/monotonic duration in ms since budget tracking started. */
|
|
333
|
+
maxDurationMs?: number;
|
|
334
|
+
/** Maximum total dynamic sub-agents spawned via `spawn_subagents`. */
|
|
335
|
+
maxTotalSubagents?: number;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/** Mutable counters backing a {@link Budget}. Stored on the agent instance (untyped `budgetState`). */
|
|
339
|
+
export interface BudgetState {
|
|
340
|
+
/** Workers spawned so far (incremented by `reserveSpawn`). */
|
|
341
|
+
spawned: number;
|
|
342
|
+
/** Accumulated total tokens charged via `chargeBudgetUsage`. */
|
|
343
|
+
totalTokens: number;
|
|
344
|
+
/** Accumulated total cost in USD charged via `chargeBudgetUsage`. */
|
|
345
|
+
totalCostUsd: number;
|
|
346
|
+
/** Monotonic start time (`nowMs()` clock) for `maxDurationMs` checks. */
|
|
347
|
+
startedAtMs: number;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/** Run/turn lineage attached to sub-agent metadata via `(metadata as any)` (Q-42). */
|
|
351
|
+
export interface SubAgentRunIds {
|
|
352
|
+
/** Stable id for this worker batch/run (`newRunId`). */
|
|
353
|
+
runId?: string;
|
|
354
|
+
/** Stable id for this worker turn (`newTurnId`). */
|
|
355
|
+
turnId?: string;
|
|
356
|
+
/** Parent run id when known (opaque, forwarded when the host exposes one). */
|
|
357
|
+
parentRunId?: string;
|
|
358
|
+
/** Root run id when known (opaque, forwarded when the host exposes one). */
|
|
359
|
+
rootRunId?: string;
|
|
360
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import type { TokenUsage } from "./core.ts";
|
|
2
|
+
import type { ToolCallRecord, ToolResultRecord } from "./tool.ts";
|
|
3
|
+
|
|
4
|
+
/** Machine-readable error codes. No ANSI in messages. */
|
|
5
|
+
export type AgentAccelErrorCode =
|
|
6
|
+
| "config"
|
|
7
|
+
| "provider"
|
|
8
|
+
| "timeout"
|
|
9
|
+
| "budget_exceeded"
|
|
10
|
+
| "tool"
|
|
11
|
+
| "validation"
|
|
12
|
+
| "session_load"
|
|
13
|
+
| "security_policy"
|
|
14
|
+
| "abort"
|
|
15
|
+
| "incomplete_stream"
|
|
16
|
+
| "context_overflow";
|
|
17
|
+
|
|
18
|
+
export interface AgentAccelErrorContext {
|
|
19
|
+
provider?: string;
|
|
20
|
+
model?: string;
|
|
21
|
+
status?: number;
|
|
22
|
+
requestId?: string;
|
|
23
|
+
retryable?: boolean;
|
|
24
|
+
runId?: string;
|
|
25
|
+
turnId?: string;
|
|
26
|
+
docs?: string;
|
|
27
|
+
fixes?: string[];
|
|
28
|
+
partial?: {
|
|
29
|
+
usage?: TokenUsage;
|
|
30
|
+
turns?: number;
|
|
31
|
+
text?: string;
|
|
32
|
+
toolCalls?: ToolCallRecord[];
|
|
33
|
+
toolResults?: ToolResultRecord[];
|
|
34
|
+
};
|
|
35
|
+
[k: string]: unknown;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Base for all SDK errors. Message is one line, no ANSI. */
|
|
39
|
+
export class AgentAccelError extends Error {
|
|
40
|
+
readonly code: AgentAccelErrorCode;
|
|
41
|
+
readonly retryable: boolean;
|
|
42
|
+
readonly context: AgentAccelErrorContext;
|
|
43
|
+
constructor(code: AgentAccelErrorCode, message: string, opts?: { retryable?: boolean; context?: AgentAccelErrorContext; cause?: unknown }) {
|
|
44
|
+
super(message);
|
|
45
|
+
this.name = "AgentAccelError";
|
|
46
|
+
this.code = code;
|
|
47
|
+
this.retryable = opts?.retryable ?? false;
|
|
48
|
+
this.context = opts?.context ?? {};
|
|
49
|
+
if (opts?.cause !== undefined) (this as { cause?: unknown }).cause = opts.cause;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export class ConfigError extends AgentAccelError {
|
|
54
|
+
constructor(message: string, context?: AgentAccelErrorContext) {
|
|
55
|
+
super("config", message, { retryable: false, context });
|
|
56
|
+
this.name = "ConfigError";
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export class ProviderError extends AgentAccelError {
|
|
61
|
+
readonly status?: number;
|
|
62
|
+
constructor(message: string, opts?: { status?: number; provider?: string; model?: string; requestId?: string; retryable?: boolean; context?: AgentAccelErrorContext }) {
|
|
63
|
+
super("provider", message, {
|
|
64
|
+
retryable: opts?.retryable ?? false,
|
|
65
|
+
context: { ...opts?.context, provider: opts?.provider, model: opts?.model, status: opts?.status, requestId: opts?.requestId },
|
|
66
|
+
});
|
|
67
|
+
this.name = "ProviderError";
|
|
68
|
+
this.status = opts?.status;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export class TimeoutError extends AgentAccelError {
|
|
73
|
+
constructor(message: string, context?: AgentAccelErrorContext) {
|
|
74
|
+
super("timeout", message, { retryable: true, context });
|
|
75
|
+
this.name = "TimeoutError";
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export class BudgetExceededError extends AgentAccelError {
|
|
80
|
+
constructor(message: string, context?: AgentAccelErrorContext) {
|
|
81
|
+
super("budget_exceeded", message, { retryable: false, context });
|
|
82
|
+
this.name = "BudgetExceededError";
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export class ToolError extends AgentAccelError {
|
|
87
|
+
readonly expose: boolean;
|
|
88
|
+
constructor(message: string, opts?: { retryable?: boolean; expose?: boolean; context?: AgentAccelErrorContext }) {
|
|
89
|
+
super("tool", message, { retryable: opts?.retryable ?? false, context: opts?.context });
|
|
90
|
+
this.name = "ToolError";
|
|
91
|
+
this.expose = opts?.expose ?? true;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export class ValidationError extends AgentAccelError {
|
|
96
|
+
constructor(message: string, context?: AgentAccelErrorContext) {
|
|
97
|
+
super("validation", message, { retryable: false, context });
|
|
98
|
+
this.name = "ValidationError";
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export class SessionLoadError extends AgentAccelError {
|
|
103
|
+
constructor(message: string, context?: AgentAccelErrorContext) {
|
|
104
|
+
super("session_load", message, { retryable: false, context });
|
|
105
|
+
this.name = "SessionLoadError";
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export class SecurityPolicyError extends AgentAccelError {
|
|
110
|
+
constructor(message: string, context?: AgentAccelErrorContext) {
|
|
111
|
+
super("security_policy", message, { retryable: false, context });
|
|
112
|
+
this.name = "SecurityPolicyError";
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** Abort is signal.aborted or name === AbortError only. Never message regex. */
|
|
117
|
+
export function isAbortError(error: unknown): boolean {
|
|
118
|
+
const err = error as { name?: unknown } | null;
|
|
119
|
+
if (!err || typeof err !== "object") return false;
|
|
120
|
+
if ((err as { name?: unknown }).name === "AbortError") return true;
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** Format without ANSI for CLI pretty-printing. */
|
|
125
|
+
export function formatErrorPlain(err: unknown): string {
|
|
126
|
+
if (err instanceof AgentAccelError) {
|
|
127
|
+
const ctx = err.context?.provider ? ` [${err.context.provider}${err.context.model ? `/${err.context.model}` : ""}]` : "";
|
|
128
|
+
const status = err.context?.status ? ` (${err.context.status})` : "";
|
|
129
|
+
return `[${err.code}]${ctx}${status}: ${err.message}`;
|
|
130
|
+
}
|
|
131
|
+
if (err instanceof Error) return `${err.name}: ${err.message}`;
|
|
132
|
+
return String(err);
|
|
133
|
+
}
|
package/src/types/model.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ThinkingConfig, CacheConfig, ServiceTier, TokenUsage } from "./core.ts";
|
|
2
|
-
import type { ProviderContext
|
|
2
|
+
import type { ProviderContext } from "./message.ts";
|
|
3
3
|
import type { StandardToolDeclaration, ToolCallRecord } from "./tool.ts";
|
|
4
4
|
import type { AssistantMessageEventStream } from "../streaming/event-stream.ts";
|
|
5
5
|
|
|
@@ -87,6 +87,7 @@ export interface ModelCapabilities {
|
|
|
87
87
|
supportsLongCacheRetention?: boolean;
|
|
88
88
|
supportsParallelToolCalls?: boolean;
|
|
89
89
|
supportsStreaming?: boolean;
|
|
90
|
+
supportsStructuredOutput?: boolean;
|
|
90
91
|
modalities?: ("text" | "image" | "audio" | "video" | "pdf")[];
|
|
91
92
|
// derived
|
|
92
93
|
supportsReasoningToggle?: boolean;
|
|
@@ -138,6 +139,24 @@ export interface ModelSpec {
|
|
|
138
139
|
raw?: RawModelData;
|
|
139
140
|
}
|
|
140
141
|
|
|
142
|
+
// ---------------------------------------------------------------------------
|
|
143
|
+
// Structured outputs — normalized, provider-agnostic spec.
|
|
144
|
+
// Canonical input is `AgentConfig.output` (Zod, JSON Schema, or explicit
|
|
145
|
+
// `{ name, schema, strict }`); `normalizeStructuredOutput` in
|
|
146
|
+
// `src/providers.ts` produces this shape for adapters.
|
|
147
|
+
// ---------------------------------------------------------------------------
|
|
148
|
+
/** Normalized structured-output request passed from Agent to providers. */
|
|
149
|
+
export interface StructuredOutputSpec {
|
|
150
|
+
/** Format name (1-64 chars, `[a-zA-Z0-9_-]`). Defaults to `structured_output`. */
|
|
151
|
+
name: string;
|
|
152
|
+
/** JSON Schema for the final answer (already converted from Zod when needed). */
|
|
153
|
+
schema: Record<string, unknown>;
|
|
154
|
+
/** Strict schema adherence (OpenAI/OpenRouter only; ignored by Google). Defaults to true. */
|
|
155
|
+
strict?: boolean;
|
|
156
|
+
/** Optional description of what the format is for. */
|
|
157
|
+
description?: string;
|
|
158
|
+
}
|
|
159
|
+
|
|
141
160
|
// ---------------------------------------------------------------------------
|
|
142
161
|
// Provider plumbing — unchanged API
|
|
143
162
|
// ---------------------------------------------------------------------------
|
|
@@ -156,6 +175,8 @@ export interface ProviderRequestOptions {
|
|
|
156
175
|
env?: Record<string, string>;
|
|
157
176
|
maxRetries?: number;
|
|
158
177
|
maxRetryDelayMs?: number;
|
|
178
|
+
/** Normalized structured-output spec (see `normalizeStructuredOutput`). */
|
|
179
|
+
output?: StructuredOutputSpec;
|
|
159
180
|
}
|
|
160
181
|
|
|
161
182
|
/** Auditable request/response wire payload captured in AgentResponse. */
|