agent-accelerator 0.5.6 → 0.5.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -17
- package/package.json +1 -1
- package/src/agent/agent.ts +92 -57
- package/src/agent/architecture.md +25 -0
- package/src/agent/context.ts +7 -3
- package/src/agent/delegation.ts +30 -96
- package/src/agent/loop.ts +54 -56
- package/src/agent/subagent.ts +4 -4
- package/src/agent/worker.ts +1 -3
- package/src/architecture.md +28 -0
- package/src/index.ts +13 -80
- package/src/models/architecture.md +14 -0
- package/src/models/catalog-cache.ts +10 -7
- package/src/models/catalog.ts +20 -13
- package/src/providers/architecture.md +24 -0
- package/src/providers/google.ts +24 -4
- package/src/providers/openai-compat.ts +29 -7
- package/src/providers/openai.ts +22 -6
- package/src/providers/openrouter.ts +14 -2
- package/src/providers/registry.ts +1 -0
- package/src/providers/shared.ts +4 -4
- package/src/providers.ts +9 -53
- package/src/scheduler/architecture.md +22 -0
- package/src/scheduler/scheduler.ts +131 -56
- package/src/scheduler/store.ts +274 -54
- package/src/scheduler/time.ts +2 -0
- package/src/session/architecture.md +19 -0
- package/src/session/store.ts +37 -22
- package/src/session/usage.ts +1 -0
- package/src/streaming/architecture.md +13 -0
- package/src/streaming/event-stream.ts +16 -11
- package/src/telemetry-file.ts +74 -24
- package/src/telemetry.ts +3 -2
- package/src/tools/architecture.md +19 -0
- package/src/tools/executor.ts +14 -1
- package/src/tools/schema.ts +1 -0
- package/src/tools/subagent-result.ts +1 -0
- package/src/tools/tool.ts +8 -8
- package/src/types/agent.ts +22 -20
- package/src/types/architecture.md +20 -0
- package/src/types/response.ts +8 -8
- package/src/types/scheduler.ts +2 -2
- package/src/types/tool.ts +5 -5
- package/src/update-models.ts +2 -2
- package/src/utils/architecture.md +25 -0
- package/src/utils/base64.ts +2 -0
- package/src/utils/cache.ts +6 -4
- package/src/utils/durable.ts +78 -3
- package/src/utils/env.ts +1 -1
- package/src/utils/errors.ts +4 -0
- package/src/utils/headers.ts +7 -4
- package/src/utils/home.ts +1 -1
- package/src/utils/media.ts +1 -0
- package/src/utils/raw-capture.ts +3 -0
- package/src/utils/retry.ts +2 -1
- package/src/utils/safe-io.ts +4 -0
- package/src/utils/session.ts +10 -166
package/src/tools/executor.ts
CHANGED
|
@@ -28,20 +28,24 @@ export interface ExecuteToolsOptions {
|
|
|
28
28
|
|
|
29
29
|
const DEFAULT_TOOL_CONCURRENCY = 8;
|
|
30
30
|
|
|
31
|
+
// Wall-clock milliseconds; in: the executor's timing, out: a number.
|
|
31
32
|
function nowMs(): number {
|
|
32
33
|
return Date.now();
|
|
33
34
|
}
|
|
34
35
|
|
|
36
|
+
// Milliseconds since `start`, at least 1 so a result never reports 0; in: result records, out: durationMs.
|
|
35
37
|
function elapsedMs(start: number): number {
|
|
36
38
|
return Math.max(1, nowMs() - start);
|
|
37
39
|
}
|
|
38
40
|
|
|
41
|
+
// A finite number >= 0 from a number or numeric string option, else undefined; in: tool options, out: timeout and limits.
|
|
39
42
|
function numericOption(value: number | string | undefined): number | undefined {
|
|
40
43
|
if (value === undefined || value === "") return undefined;
|
|
41
44
|
const parsed = typeof value === "number" ? value : Number(value);
|
|
42
45
|
return Number.isFinite(parsed) && parsed >= 0 ? parsed : undefined;
|
|
43
46
|
}
|
|
44
47
|
|
|
48
|
+
// numericOption rounded down to a whole number; in: tool options, out: attempts, concurrency and char caps.
|
|
45
49
|
function integerOption(value: number | string | undefined): number | undefined {
|
|
46
50
|
const parsed = numericOption(value);
|
|
47
51
|
return parsed === undefined ? undefined : Math.floor(parsed);
|
|
@@ -128,11 +132,13 @@ export function sanitizeErrorMessage(message: string): string {
|
|
|
128
132
|
return out;
|
|
129
133
|
}
|
|
130
134
|
|
|
135
|
+
// The tool's `maxResultChars` or the 20000 default; in: runSingle, out: the cap on a result kept for the model.
|
|
131
136
|
function resultCharLimit(toolDef: ToolDefinition): number {
|
|
132
137
|
const configured = integerOption((toolDef as any).maxResultChars);
|
|
133
138
|
return configured && configured > 0 ? configured : DEFAULT_MAX_RESULT_CHARS;
|
|
134
139
|
}
|
|
135
140
|
|
|
141
|
+
// Cuts a string to the limit and appends a `[Truncated: ...]` note; in: runSingle, out: the capped result.
|
|
136
142
|
function truncateResultString(value: string, limit: number): string {
|
|
137
143
|
if (value.length <= limit) return value;
|
|
138
144
|
return value.slice(0, limit) + `\n[Truncated: showing ${limit} of ${value.length} chars]`;
|
|
@@ -275,12 +281,14 @@ export function traceToolEvent(
|
|
|
275
281
|
});
|
|
276
282
|
}
|
|
277
283
|
|
|
284
|
+
// Scrubbed, JSON-safe value capped to `max` serialized characters for logs and events; in: progress and event tracing, out: a value or a truncated string.
|
|
278
285
|
function capJson(value: unknown, max = 2000): unknown {
|
|
279
286
|
const safe = scrubErrors(toJsonSafe(value));
|
|
280
287
|
const json = JSON.stringify(safe);
|
|
281
288
|
return typeof json === "string" && json.length > max ? `${json.slice(0, max)}…[truncated]` : safe;
|
|
282
289
|
}
|
|
283
290
|
|
|
291
|
+
// Edit distance between two strings; in: resolveTool's did-you-mean, out: a number.
|
|
284
292
|
function levenshtein(a: string, b: string): number {
|
|
285
293
|
const row = Array.from({ length: b.length + 1 }, (_, i) => i);
|
|
286
294
|
for (let i = 1; i <= a.length; i++) {
|
|
@@ -303,6 +311,7 @@ interface ResolvedTool {
|
|
|
303
311
|
suggestion?: string;
|
|
304
312
|
}
|
|
305
313
|
|
|
314
|
+
// Finds the tool a call names (exact, then case/format-insensitive), else suggests the closest name; in: runSingle and findToolDefinition, out: ResolvedTool.
|
|
306
315
|
function resolveTool(tools: Record<string, ToolDefinition>, requestedName: string): ResolvedTool {
|
|
307
316
|
const direct = tools[requestedName];
|
|
308
317
|
if (direct) return { definition: direct, name: direct.name || requestedName };
|
|
@@ -348,6 +357,7 @@ class Semaphore {
|
|
|
348
357
|
|
|
349
358
|
constructor(private readonly limit: number) {}
|
|
350
359
|
|
|
360
|
+
// Takes a slot (immediately if free, else waits in line), rejecting if the run aborts first; in: runSingle, out: a release function.
|
|
351
361
|
acquire(signal?: AbortSignal): Promise<() => void> {
|
|
352
362
|
if (signal?.aborted) return Promise.reject(new Error("Tool execution aborted"));
|
|
353
363
|
if (this.limit === Number.POSITIVE_INFINITY || this.active < this.limit) {
|
|
@@ -372,6 +382,7 @@ class Semaphore {
|
|
|
372
382
|
});
|
|
373
383
|
}
|
|
374
384
|
|
|
385
|
+
// Frees a slot and hands it to the next live waiter, dropping aborted ones; in: the function acquire returned, out: the next waiter's grant.
|
|
375
386
|
private release(): void {
|
|
376
387
|
this.active = Math.max(0, this.active - 1);
|
|
377
388
|
while (this.waiters.length > 0) {
|
|
@@ -405,6 +416,7 @@ function isTransientError(error: any): boolean {
|
|
|
405
416
|
return false;
|
|
406
417
|
}
|
|
407
418
|
|
|
419
|
+
// Waits the retry backoff, rejecting `Tool execution aborted` as soon as the run aborts; in: runSingle's retry loop, out: nothing.
|
|
408
420
|
function waitForRetry(ms: number, signal?: AbortSignal): Promise<void> {
|
|
409
421
|
if (ms <= 0) return Promise.resolve();
|
|
410
422
|
return new Promise((resolve, reject) => {
|
|
@@ -437,6 +449,7 @@ function raceAbort<T>(work: Promise<T>, signal: AbortSignal | undefined): Promis
|
|
|
437
449
|
});
|
|
438
450
|
}
|
|
439
451
|
|
|
452
|
+
// One attempt of a tool: runs it, raced against the run's abort and the per-attempt timeout (which also aborts the tool's signal); in: runSingle, out: the tool's result or a thrown timeout/abort.
|
|
440
453
|
async function executeAttempt(
|
|
441
454
|
toolDef: ToolDefinition,
|
|
442
455
|
parsedInput: unknown,
|
|
@@ -526,7 +539,7 @@ export async function executeToolCalls(
|
|
|
526
539
|
let attempts = 0;
|
|
527
540
|
let timedOut = false;
|
|
528
541
|
|
|
529
|
-
// Truncated streamed arguments
|
|
542
|
+
// Truncated streamed arguments: the `__truncated_raw` marker means the
|
|
530
543
|
// provider's partial JSON never parsed. Never execute or overwrite wire
|
|
531
544
|
// finishReason here — return a synthetic error so the model retries with
|
|
532
545
|
// complete JSON.
|
package/src/tools/schema.ts
CHANGED
|
@@ -160,6 +160,7 @@ function extractZodObjectSchema(zodSchema: any): Record<string, unknown> {
|
|
|
160
160
|
};
|
|
161
161
|
}
|
|
162
162
|
|
|
163
|
+
// Best-effort JSON Schema for one Zod property when toJSONSchema is unavailable; in: extractZodObjectSchema, out: a schema fragment.
|
|
163
164
|
function inferZodPropertyType(prop: any): Record<string, unknown> {
|
|
164
165
|
const unwrapped = prop.unwrap?.() || prop._def?.innerType || prop;
|
|
165
166
|
const description = prop.description || unwrapped.description || prop._def?.description;
|
|
@@ -34,6 +34,7 @@ export function isSubagentResult(value: unknown): value is SubagentResult {
|
|
|
34
34
|
return !!value && typeof value === "object" && (value as Record<symbol, unknown>)[SUBAGENT_RESULT_BRAND] === true;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
// A finite number >= 0, else 0; in: normalizeSubagentMetadata, out: a safe usage count.
|
|
37
38
|
const num = (v: unknown): number => (typeof v === "number" && Number.isFinite(v) && v >= 0 ? v : 0);
|
|
38
39
|
|
|
39
40
|
/**
|
package/src/tools/tool.ts
CHANGED
|
@@ -10,7 +10,7 @@ import { zodToJsonSchema } from "./schema.ts";
|
|
|
10
10
|
import { ConfigError } from "../types/errors.ts";
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
|
-
* Valid tool-name pattern
|
|
13
|
+
* Valid tool-name pattern: 1–64 chars of letters, digits, `_`, `-`.
|
|
14
14
|
* Names are validated at registration in {@link tool} (record-key fallback
|
|
15
15
|
* included); invalid names throw `ConfigError` with a fix.
|
|
16
16
|
*/
|
|
@@ -31,7 +31,7 @@ export function assertValidToolName(name: string): void {
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
/**
|
|
34
|
-
* Normalizes the
|
|
34
|
+
* Normalizes the canonical `thinking` option to a bare level.
|
|
35
35
|
* Accepts a level string or `{ level, budgetTokens }`; `budgetTokens` is
|
|
36
36
|
* carried separately by providers, so only the level is returned here.
|
|
37
37
|
* Resolution honors `thinking ?? thinkingLevel`.
|
|
@@ -50,7 +50,7 @@ export function normalizeThinkingV2(
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
/**
|
|
53
|
-
* Resolves the
|
|
53
|
+
* Resolves the canonical busy policy. `whenBusy` wins over `mode`.
|
|
54
54
|
*
|
|
55
55
|
* @example `resolveWhenBusy({ whenBusy: "queue", mode: "steer" }); // "queue"`
|
|
56
56
|
*/
|
|
@@ -62,7 +62,7 @@ export function resolveWhenBusy(
|
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
/**
|
|
65
|
-
* Normalizes the
|
|
65
|
+
* Normalizes the `maxAttempts` alias to a `maxTries` value.
|
|
66
66
|
* `maxTries` wins when both are set.
|
|
67
67
|
*
|
|
68
68
|
* @example `normalizeMaxAttempts({ maxAttempts: 3 }); // 3`
|
|
@@ -90,7 +90,7 @@ export interface CreateToolOptions<TInput = any, TOutput = any> {
|
|
|
90
90
|
/** Maximum total attempts for transient failures. `0`/omitted means no configured limit. */
|
|
91
91
|
maxTries?: number | string;
|
|
92
92
|
/**
|
|
93
|
-
* Canonical alias for `maxTries
|
|
93
|
+
* Canonical alias for `maxTries`. When both are set, `maxTries` wins.
|
|
94
94
|
* Normalized by `normalizeMaxAttempts`.
|
|
95
95
|
*/
|
|
96
96
|
maxAttempts?: number | string;
|
|
@@ -116,12 +116,12 @@ export interface CreateToolOptions<TInput = any, TOutput = any> {
|
|
|
116
116
|
/** Optional hook to redact/replace the error message sent back to the model. */
|
|
117
117
|
redactError?: (err: unknown) => string;
|
|
118
118
|
/**
|
|
119
|
-
* Canonical alias for `redactError
|
|
119
|
+
* Canonical alias for `redactError`. When both are set,
|
|
120
120
|
* `redactError` wins.
|
|
121
121
|
*/
|
|
122
122
|
redact?: (err: unknown) => string;
|
|
123
123
|
/**
|
|
124
|
-
* Explicit retry policy
|
|
124
|
+
* Explicit retry policy. Preferred over bare `maxTries` for new
|
|
125
125
|
* code; the executor reads via `(toolDef as any).retry`.
|
|
126
126
|
*/
|
|
127
127
|
retry?: { maxRetries?: number; baseDelayMs?: number; maxDelayMs?: number };
|
|
@@ -165,7 +165,7 @@ export interface CreateToolOptions<TInput = any, TOutput = any> {
|
|
|
165
165
|
export function tool<TInput = any, TOutput = any>(
|
|
166
166
|
options: CreateToolOptions<TInput, TOutput>
|
|
167
167
|
): ToolDefinition<TInput, TOutput> {
|
|
168
|
-
//
|
|
168
|
+
// Names are validated at registration (record-key fallback included).
|
|
169
169
|
if (options.name !== undefined) assertValidToolName(options.name);
|
|
170
170
|
// ToolDefinition (src/types/tool.ts) carries the v2 policies as typed
|
|
171
171
|
// optionals; the executor additionally reads them via `(toolDef as any).*`.
|
package/src/types/agent.ts
CHANGED
|
@@ -10,7 +10,7 @@ import type { Agent } from "../agent/agent.ts";
|
|
|
10
10
|
import type { z } from "zod";
|
|
11
11
|
|
|
12
12
|
// ---------------------------------------------------------------------------
|
|
13
|
-
//
|
|
13
|
+
// Canonical option types (all additive; nothing renamed).
|
|
14
14
|
//
|
|
15
15
|
// Canonical homes (one place per setting):
|
|
16
16
|
// - Worker model: `dynamicSubagents.model` only. Top-level `subagentModel`
|
|
@@ -27,10 +27,10 @@ import type { z } from "zod";
|
|
|
27
27
|
/** Canonical model reference: `provider/model` string, catalog spec, or provider helper result. */
|
|
28
28
|
export type ModelRef = string | ModelSpec | ModelProviderInstance;
|
|
29
29
|
|
|
30
|
-
/** Canonical reasoning option
|
|
30
|
+
/** Canonical reasoning option: bare level, or level + explicit token budget. */
|
|
31
31
|
export type ThinkingOption = ThinkingLevel | { level?: ThinkingLevel; budgetTokens?: number };
|
|
32
32
|
|
|
33
|
-
/** Canonical retry policy
|
|
33
|
+
/** Canonical retry policy. Read by the loop via `(config as any).retry`. */
|
|
34
34
|
export interface RetryPolicyConfig {
|
|
35
35
|
maxRetries?: number;
|
|
36
36
|
baseDelayMs?: number;
|
|
@@ -38,7 +38,7 @@ export interface RetryPolicyConfig {
|
|
|
38
38
|
budgetMs?: number;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
/** Minimal logger surface for Agent/loop telemetry
|
|
41
|
+
/** Minimal logger surface for Agent/loop telemetry. */
|
|
42
42
|
export interface AgentLogger {
|
|
43
43
|
debug?: (...args: unknown[]) => void;
|
|
44
44
|
info?: (...args: unknown[]) => void;
|
|
@@ -46,17 +46,17 @@ export interface AgentLogger {
|
|
|
46
46
|
error?: (...args: unknown[]) => void;
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
/** Canonical tool-selection control
|
|
49
|
+
/** Canonical tool-selection control. String form is the portable subset. */
|
|
50
50
|
export type ToolChoiceOption =
|
|
51
51
|
| "auto"
|
|
52
52
|
| "none"
|
|
53
53
|
| "required"
|
|
54
54
|
| { mode?: "auto" | "none" | "required"; tool?: string };
|
|
55
55
|
|
|
56
|
-
/** Canonical stop control
|
|
56
|
+
/** Canonical stop control: sequence(s) halting generation. */
|
|
57
57
|
export type StopOption = string | string[];
|
|
58
58
|
|
|
59
|
-
/** Canonical structured-output input
|
|
59
|
+
/** Canonical structured-output input. Zod, JSON Schema, or explicit form. */
|
|
60
60
|
export type StructuredOutputOption =
|
|
61
61
|
| z.ZodType
|
|
62
62
|
| Record<string, unknown>
|
|
@@ -90,7 +90,7 @@ export interface AgentConfig {
|
|
|
90
90
|
/** Requested reasoning level, validated against the model catalog. */
|
|
91
91
|
thinkingLevel?: ThinkingLevel;
|
|
92
92
|
/**
|
|
93
|
-
* Canonical reasoning option (
|
|
93
|
+
* Canonical reasoning option (preferred over `thinkingLevel`).
|
|
94
94
|
* Accepts a bare level or `{ level, budgetTokens }`. When both are set,
|
|
95
95
|
* `thinking` wins; resolution honors `thinking ?? thinkingLevel`.
|
|
96
96
|
* See `normalizeThinkingV2` (re-exported from the root entry).
|
|
@@ -151,11 +151,11 @@ export interface AgentConfig {
|
|
|
151
151
|
resume?: "automatic" | "explicit";
|
|
152
152
|
/**
|
|
153
153
|
* Folder for everything Agent Accelerator writes to disk (`sessions/`,
|
|
154
|
-
* `
|
|
154
|
+
* `scheduled/`, `telemetry/`). Defaults to `setHome()`, then the `AGENT_ACCEL_HOME` env var, then `.agent-accel/` in the working directory.
|
|
155
155
|
* An explicit `persist.dir` / `persist.file` still wins for sessions.
|
|
156
156
|
*/
|
|
157
157
|
home?: string;
|
|
158
|
-
//
|
|
158
|
+
// Additional options (all optional, additive; loop reads via `(config as any).*`).
|
|
159
159
|
/**
|
|
160
160
|
* Canonical tool-selection control (`"auto"` default). `"none"` applies to
|
|
161
161
|
* every turn of a run; `"required"` and a pinned tool (`{ tool: "name" }`)
|
|
@@ -193,8 +193,10 @@ export interface AgentConfig {
|
|
|
193
193
|
/** @deprecated Not implemented yet (accepted for forward compatibility; passing it emits a `config.unsupported_option` warning). */
|
|
194
194
|
contextManagement?: unknown;
|
|
195
195
|
/**
|
|
196
|
-
* Telemetry options. `persist: true` appends every `telemetryBus` event (except full reasoning
|
|
197
|
-
*
|
|
196
|
+
* Telemetry options. `persist: true` appends every `telemetryBus` event (except full reasoning text). An event
|
|
197
|
+
* that belongs to a persisted session (`data.sessionId`: tool events, resume and retry events) goes to
|
|
198
|
+
* `<session folder>/telemetry.ndjson`, next to the session's journal; every other event goes to
|
|
199
|
+
* `<home>/telemetry/<date>.ndjson`. Nothing is written outside the home. Off by default: the SDK writes nothing unless asked.
|
|
198
200
|
*/
|
|
199
201
|
telemetry?: { persist?: boolean };
|
|
200
202
|
/**
|
|
@@ -226,7 +228,7 @@ export interface AgentConfig {
|
|
|
226
228
|
* models with unknown pricing contribute $0, never a guess.
|
|
227
229
|
*/
|
|
228
230
|
budget?: Budget;
|
|
229
|
-
/** Run + network limits
|
|
231
|
+
/** Run + network limits. Turn/budget caps unlimited by default; network timeouts finite. */
|
|
230
232
|
limits?: AgentRunLimits;
|
|
231
233
|
/**
|
|
232
234
|
* The only models this agent may use, as `provider/model` strings compared as written. Unset (default) = any model.
|
|
@@ -260,7 +262,7 @@ export interface InterruptedRun {
|
|
|
260
262
|
pendingToolCalls: string[];
|
|
261
263
|
}
|
|
262
264
|
|
|
263
|
-
/** Per-request + run limits
|
|
265
|
+
/** Per-request + run limits. */
|
|
264
266
|
export interface AgentRunLimits {
|
|
265
267
|
maxTurns?: number;
|
|
266
268
|
deadlineMs?: number;
|
|
@@ -275,7 +277,7 @@ export interface AgentRunLimits {
|
|
|
275
277
|
/** Developer policy for mid-session new-query insertion while a run is active. */
|
|
276
278
|
export type MidSessionMode = "steer" | "queue" | "auto";
|
|
277
279
|
|
|
278
|
-
/**
|
|
280
|
+
/** Canonical busy-policy alias: prefer `whenBusy`, `mode` kept for compat. */
|
|
279
281
|
export type WhenBusyMode = MidSessionMode;
|
|
280
282
|
|
|
281
283
|
/** Developer-configured policy for LLM-spawned dynamic sub-agents. */
|
|
@@ -288,7 +290,7 @@ export interface MidSessionConfig {
|
|
|
288
290
|
*/
|
|
289
291
|
mode?: MidSessionMode;
|
|
290
292
|
/**
|
|
291
|
-
* Canonical alias for `mode` (
|
|
293
|
+
* Canonical alias for `mode` (preferred). When both are set,
|
|
292
294
|
* `whenBusy` wins. Accepted anywhere `mode` is read; resolution honors
|
|
293
295
|
* `whenBusy ?? mode ?? "auto"`.
|
|
294
296
|
*/
|
|
@@ -341,7 +343,7 @@ export interface AgentRunOptions {
|
|
|
341
343
|
/** Per-run reasoning-level override, validated against the selected model. */
|
|
342
344
|
thinkingLevel?: ThinkingLevel;
|
|
343
345
|
/**
|
|
344
|
-
* Canonical per-run reasoning option (
|
|
346
|
+
* Canonical per-run reasoning option (preferred over `thinkingLevel`).
|
|
345
347
|
* Resolution honors `thinking ?? thinkingLevel`.
|
|
346
348
|
*/
|
|
347
349
|
thinking?: ThinkingOption;
|
|
@@ -375,7 +377,7 @@ export interface AgentRunOptions {
|
|
|
375
377
|
wrapThinking?: boolean;
|
|
376
378
|
/** Called after every model/tool turn with a turn summary (also drives sub-agent step logs + durable persistence). */
|
|
377
379
|
onTurn?: (turn: AgentTurnEvent) => void;
|
|
378
|
-
//
|
|
380
|
+
// Per-run knobs (all optional, additive; `output` intentionally
|
|
379
381
|
// omitted here — it is agent-level only, see AgentConfig.output).
|
|
380
382
|
/** Per-run tool-selection override. */
|
|
381
383
|
toolChoice?: ToolChoiceOption;
|
|
@@ -409,7 +411,7 @@ export interface AgentTurnEvent {
|
|
|
409
411
|
toolResults?: Array<{ id: string; name: string; isError?: boolean }>;
|
|
410
412
|
}
|
|
411
413
|
|
|
412
|
-
/** Optional run budgets enforced by delegation helpers
|
|
414
|
+
/** Optional run budgets enforced by delegation helpers. All fields default to unlimited. */
|
|
413
415
|
export interface Budget {
|
|
414
416
|
/** Maximum total cost in USD across this agent + spawned workers. */
|
|
415
417
|
maxCostUsd?: number;
|
|
@@ -433,7 +435,7 @@ export interface BudgetState {
|
|
|
433
435
|
startedAtMs: number;
|
|
434
436
|
}
|
|
435
437
|
|
|
436
|
-
/** Run/turn lineage attached to sub-agent metadata via `(metadata as any)
|
|
438
|
+
/** Run/turn lineage attached to sub-agent metadata via `(metadata as any)`. */
|
|
437
439
|
export interface SubAgentRunIds {
|
|
438
440
|
/** Stable id for this worker batch/run (`newRunId`). */
|
|
439
441
|
runId?: string;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# src/types/
|
|
2
|
+
|
|
3
|
+
Shared contracts. Types only, plus a few tiny helpers that belong to a type.
|
|
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`. |
|
|
15
|
+
|
|
16
|
+
## Invariants
|
|
17
|
+
- Adding a field to a public type is a public API change: update `README.md` and `docs/`.
|
|
18
|
+
- Abort means `signal.aborted` or `name === "AbortError"`, never a message match.
|
|
19
|
+
- Error messages are one line and ANSI-free.
|
|
20
|
+
- No logic here beyond `normalizeFinishReason`, `isTruncatedArguments` and the error classes.
|
package/src/types/response.ts
CHANGED
|
@@ -67,7 +67,7 @@ export type FinishReason =
|
|
|
67
67
|
/** Maps a vendor finish reason to canonical FinishReason + raw passthrough.
|
|
68
68
|
* Known aliases collapse to the canonical set; empty/undefined stays
|
|
69
69
|
* undefined; any other non-empty string maps to `"error"` with the original
|
|
70
|
-
* preserved in `rawFinishReason
|
|
70
|
+
* preserved in `rawFinishReason`. */
|
|
71
71
|
export function normalizeFinishReason(raw?: string): {
|
|
72
72
|
finishReason?: FinishReason;
|
|
73
73
|
rawFinishReason?: string;
|
|
@@ -118,7 +118,7 @@ export interface AgentResponseJSON {
|
|
|
118
118
|
model: string;
|
|
119
119
|
provider: ProviderId | string;
|
|
120
120
|
finishReason?: FinishReason;
|
|
121
|
-
/** Original vendor finish string when it differs from canonical `finishReason
|
|
121
|
+
/** Original vendor finish string when it differs from canonical `finishReason`. */
|
|
122
122
|
rawFinishReason?: string;
|
|
123
123
|
durationMs: number;
|
|
124
124
|
raw: ProviderRawData;
|
|
@@ -140,7 +140,7 @@ export class AgentResponse {
|
|
|
140
140
|
readonly model: string;
|
|
141
141
|
readonly provider: ProviderId | string;
|
|
142
142
|
readonly finishReason?: FinishReason;
|
|
143
|
-
/** Original vendor finish string when it differs from canonical `finishReason
|
|
143
|
+
/** Original vendor finish string when it differs from canonical `finishReason`. */
|
|
144
144
|
readonly rawFinishReason?: string;
|
|
145
145
|
readonly durationMs: number;
|
|
146
146
|
readonly raw: ProviderRawData;
|
|
@@ -247,15 +247,15 @@ export type StreamEventType =
|
|
|
247
247
|
/** Payload for one streaming text, thinking, tool, usage, or lifecycle event. */
|
|
248
248
|
export interface StreamEvent {
|
|
249
249
|
type: StreamEventType;
|
|
250
|
-
/** Schema version for typed consumers
|
|
250
|
+
/** Schema version for typed consumers. Currently always 1 when present. */
|
|
251
251
|
schemaVersion?: 1;
|
|
252
252
|
delta?: string;
|
|
253
253
|
thinkingDelta?: string;
|
|
254
254
|
toolCall?: ToolCallRecord;
|
|
255
255
|
toolResult?: ToolResultRecord;
|
|
256
|
-
/** Batch tool calls for `tool_start` events
|
|
256
|
+
/** Batch tool calls for `tool_start` events. */
|
|
257
257
|
toolCalls?: ToolCallRecord[];
|
|
258
|
-
/** Batch tool results for `tool_end` events
|
|
258
|
+
/** Batch tool results for `tool_end` events. */
|
|
259
259
|
toolResults?: ToolResultRecord[];
|
|
260
260
|
subagent?: SubAgentExecutionMetadata;
|
|
261
261
|
/** Tracking id of the worker emitting a `subagent_delta` event. */
|
|
@@ -271,7 +271,7 @@ export interface StreamEvent {
|
|
|
271
271
|
/** Canonical finish reason when known; vendor strings pass through as-is
|
|
272
272
|
* for provider compat (normalize with `normalizeFinishReason`). */
|
|
273
273
|
finishReason?: string;
|
|
274
|
-
/** Original vendor finish string when it differs from canonical
|
|
274
|
+
/** Original vendor finish string when it differs from canonical. */
|
|
275
275
|
rawFinishReason?: string;
|
|
276
276
|
error?: Error | unknown;
|
|
277
277
|
partialText?: string;
|
|
@@ -279,7 +279,7 @@ export interface StreamEvent {
|
|
|
279
279
|
raw?: ProviderRawData;
|
|
280
280
|
}
|
|
281
281
|
|
|
282
|
-
/** Typed (discriminated) stream event schema, version 1
|
|
282
|
+
/** Typed (discriminated) stream event schema, version 1.
|
|
283
283
|
* `StreamEvent` stays the permissive compat shape; use this union when you
|
|
284
284
|
* want exhaustive switching (e.g. `tool_start`/`tool_end` lifecycle). */
|
|
285
285
|
export type StreamEventV2 =
|
package/src/types/scheduler.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** Lifecycle of a scheduled action as stored in `
|
|
1
|
+
/** Lifecycle of a scheduled action as stored in `task.json`. */
|
|
2
2
|
export type ScheduledState = "waiting" | "cooking" | "missed" | "done";
|
|
3
3
|
|
|
4
4
|
/** Outcome of one run. */
|
|
@@ -45,7 +45,7 @@ export type ScheduledActionPatch = Partial<Omit<ScheduledActionInput, "scheduled
|
|
|
45
45
|
scheduled_action?: Partial<Omit<ScheduledActionInput["scheduled_action"], "id">>;
|
|
46
46
|
};
|
|
47
47
|
|
|
48
|
-
/**
|
|
48
|
+
/** The record inside an action's `task.json`. All times are local clock strings `YYYY-MM-DDTHH:mm:ss` (no offset). */
|
|
49
49
|
export interface ScheduledActionRecord {
|
|
50
50
|
id: string;
|
|
51
51
|
name: string;
|
package/src/types/tool.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { z } from "zod";
|
|
2
2
|
|
|
3
|
-
/** Minimal logger surface accepted by tool contexts (
|
|
3
|
+
/** Minimal logger surface accepted by tool contexts (additive). */
|
|
4
4
|
export interface ToolLogger {
|
|
5
5
|
debug?: (...args: unknown[]) => void;
|
|
6
6
|
info?: (...args: unknown[]) => void;
|
|
@@ -8,7 +8,7 @@ export interface ToolLogger {
|
|
|
8
8
|
error?: (...args: unknown[]) => void;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
/** Live-event emitter handed to tools that stream progress (
|
|
11
|
+
/** Live-event emitter handed to tools that stream progress (additive). */
|
|
12
12
|
export type ToolEmitFn = (event: { type: string; [k: string]: unknown }) => void;
|
|
13
13
|
|
|
14
14
|
export interface ToolExecutionContext {
|
|
@@ -22,7 +22,7 @@ export interface ToolExecutionContext {
|
|
|
22
22
|
sessionId?: string;
|
|
23
23
|
/** Forwards live sub-agent deltas (used by the `subagents` tool for realtime tracking). */
|
|
24
24
|
onSubagentEvent?: (event: { trackingId: string; delta?: string; thinkingDelta?: string; partialText?: string; partialThinking?: string }) => void;
|
|
25
|
-
//
|
|
25
|
+
// Tool API v2 context additions (all optional, additive).
|
|
26
26
|
/** Stable id for this run (`newRunId`). Mirrors SubAgentRunIds.runId. */
|
|
27
27
|
runId?: string;
|
|
28
28
|
/** Live-event emitter for tools that stream progress. */
|
|
@@ -69,7 +69,7 @@ export interface ToolDefinition<TInput = any, TOutput = any> {
|
|
|
69
69
|
maxConcurrency?: number | string;
|
|
70
70
|
/** Function executed after validation; its result is made JSON-safe for model context. */
|
|
71
71
|
execute: ToolExecuteFn<TInput, TOutput>;
|
|
72
|
-
//
|
|
72
|
+
// Tool API v2 additions (all optional, additive; loop reads via `(toolDef as any).*`).
|
|
73
73
|
/**
|
|
74
74
|
* Canonical alias for `maxTries`. When both are set, `maxTries` wins.
|
|
75
75
|
* Normalized by `normalizeMaxAttempts` (see tools/tool.ts).
|
|
@@ -101,7 +101,7 @@ export interface ToolDefinition<TInput = any, TOutput = any> {
|
|
|
101
101
|
redactError?: (err: unknown) => string;
|
|
102
102
|
/**
|
|
103
103
|
* Canonical alias for `redactError`. When both are set, `redactError` wins.
|
|
104
|
-
* Kept short for
|
|
104
|
+
* Kept short for Tool API v2 parity.
|
|
105
105
|
*/
|
|
106
106
|
redact?: (err: unknown) => string;
|
|
107
107
|
/** Free-form tags for grouping/filtering tools (e.g. `["web", "readonly"]`). */
|
package/src/update-models.ts
CHANGED
|
@@ -12,7 +12,7 @@ import { refreshModelCatalog } from "./index.ts";
|
|
|
12
12
|
import { ConfigError } from "./types/errors.ts";
|
|
13
13
|
|
|
14
14
|
const args = process.argv.slice(2);
|
|
15
|
-
//
|
|
15
|
+
// Force is true only with explicit --force (previously also true when no --ttl).
|
|
16
16
|
const force = args.includes("--force");
|
|
17
17
|
|
|
18
18
|
let customTtlMs: number | undefined;
|
|
@@ -26,7 +26,7 @@ for (const arg of args) {
|
|
|
26
26
|
} else {
|
|
27
27
|
customTtlMs = parseFloat(val);
|
|
28
28
|
}
|
|
29
|
-
//
|
|
29
|
+
// Reject non-numeric TTLs (e.g. --ttl=abc) instead of silently caching NaN.
|
|
30
30
|
if (!Number.isFinite(customTtlMs)) {
|
|
31
31
|
throw new ConfigError(`Invalid --ttl value "${val}": expected a number with optional h/m suffix (e.g. --ttl=24h).`);
|
|
32
32
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# src/utils/
|
|
2
|
+
|
|
3
|
+
Small shared helpers. Each file has one job.
|
|
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` and pre-network modality guards. |
|
|
16
|
+
| `retry.ts` | `withRetries`, `isTransientError`, `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
|
+
|
|
21
|
+
## Invariants
|
|
22
|
+
- Never write state with a bare `writeFileSync`; use `writeFileDurable`.
|
|
23
|
+
- `media.ts` and `safe-io.ts` import `node:*` lazily so browser bundles keep working.
|
|
24
|
+
- Redaction is central (`cache.ts`); adapters must not keep their own deny-lists.
|
|
25
|
+
- `AGENT_ACCEL_HOME` is read at import time; `setHome()` after import relies on the catalog reload hook.
|
package/src/utils/base64.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/** Runtime-agnostic base64 helpers — no Node Buffer, safe in browsers. */
|
|
2
2
|
|
|
3
|
+
/** Decodes base64 (whitespace ignored) to bytes, with Buffer when present, else atob; in: media and session code, out: a Uint8Array. */
|
|
3
4
|
export function base64ToBytes(b64: string): Uint8Array {
|
|
4
5
|
const clean = b64.replace(/\s/g, "");
|
|
5
6
|
const nodeBuffer = (globalThis as any)?.Buffer;
|
|
@@ -13,6 +14,7 @@ export function base64ToBytes(b64: string): Uint8Array {
|
|
|
13
14
|
return out;
|
|
14
15
|
}
|
|
15
16
|
|
|
17
|
+
/** Encodes bytes as base64, with Buffer when present, else btoa in chunks; in: media and session code, out: a string. */
|
|
16
18
|
export function bytesToBase64(bytes: Uint8Array): string {
|
|
17
19
|
const nodeBuffer = (globalThis as any)?.Buffer;
|
|
18
20
|
if (nodeBuffer && typeof nodeBuffer.from === "function") {
|
package/src/utils/cache.ts
CHANGED
|
@@ -11,7 +11,7 @@ export function clampCacheKey(key?: string): string | undefined {
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
// ---------------------------------------------------------------------------
|
|
14
|
-
// Central redaction helpers (
|
|
14
|
+
// Central redaction helpers (temporary home to avoid owner conflicts).
|
|
15
15
|
// Providers must call these instead of maintaining local deny-lists.
|
|
16
16
|
// ---------------------------------------------------------------------------
|
|
17
17
|
|
|
@@ -28,11 +28,13 @@ const SENSITIVE_VALUE_PATTERNS: RegExp[] = [
|
|
|
28
28
|
/(^|[^A-Za-z0-9])AIza[A-Za-z0-9_-]{10,}/,
|
|
29
29
|
];
|
|
30
30
|
|
|
31
|
+
// True when a header name contains key, token, secret, cookie or authorization; in: redactHeadersCentral, out: a boolean.
|
|
31
32
|
function headerNameIsSensitive(name: string): boolean {
|
|
32
33
|
const lower = String(name ?? "").toLowerCase();
|
|
33
34
|
return REDACT_HEADER_SUBSTRINGS.some((s) => lower.includes(s));
|
|
34
35
|
}
|
|
35
36
|
|
|
37
|
+
// True when a string looks like a credential (sk-, gsk_, xox, AIza, long Bearer token); in: the redactors, out: a boolean.
|
|
36
38
|
function valueLooksSensitive(value: unknown): boolean {
|
|
37
39
|
if (typeof value !== "string") return false;
|
|
38
40
|
const v = value.trim();
|
|
@@ -43,7 +45,7 @@ function valueLooksSensitive(value: unknown): boolean {
|
|
|
43
45
|
}
|
|
44
46
|
|
|
45
47
|
/**
|
|
46
|
-
* Redacts a single value: sensitive strings become "[REDACTED]"
|
|
48
|
+
* Redacts a single value: sensitive strings become "[REDACTED]".
|
|
47
49
|
*/
|
|
48
50
|
export function redactValue(value: unknown): unknown {
|
|
49
51
|
if (typeof value !== "string") return value;
|
|
@@ -52,7 +54,7 @@ export function redactValue(value: unknown): unknown {
|
|
|
52
54
|
}
|
|
53
55
|
|
|
54
56
|
/**
|
|
55
|
-
* Redacts sensitive header values
|
|
57
|
+
* Redacts sensitive header values. Returns a shallow copy; input untouched.
|
|
56
58
|
* Deny-list (name contains): *key*, *token*, *secret*, cookie, authorization.
|
|
57
59
|
* Value patterns (redacted regardless of name): sk-, AIza, gsk-, xox-.
|
|
58
60
|
*/
|
|
@@ -69,7 +71,7 @@ export function redactHeadersCentral(
|
|
|
69
71
|
}
|
|
70
72
|
|
|
71
73
|
/**
|
|
72
|
-
* Redacts secret-looking substrings inside free text
|
|
74
|
+
* Redacts secret-looking substrings inside free text.
|
|
73
75
|
* Replaces `sk-...`, `AIza...`, `gsk-...`, `xox-...` tokens with "[REDACTED]".
|
|
74
76
|
*/
|
|
75
77
|
export function redactText(text: string): string {
|