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.
Files changed (57) hide show
  1. package/README.md +10 -17
  2. package/package.json +1 -1
  3. package/src/agent/agent.ts +92 -57
  4. package/src/agent/architecture.md +25 -0
  5. package/src/agent/context.ts +7 -3
  6. package/src/agent/delegation.ts +30 -96
  7. package/src/agent/loop.ts +54 -56
  8. package/src/agent/subagent.ts +4 -4
  9. package/src/agent/worker.ts +1 -3
  10. package/src/architecture.md +28 -0
  11. package/src/index.ts +13 -80
  12. package/src/models/architecture.md +14 -0
  13. package/src/models/catalog-cache.ts +10 -7
  14. package/src/models/catalog.ts +20 -13
  15. package/src/providers/architecture.md +24 -0
  16. package/src/providers/google.ts +24 -4
  17. package/src/providers/openai-compat.ts +29 -7
  18. package/src/providers/openai.ts +22 -6
  19. package/src/providers/openrouter.ts +14 -2
  20. package/src/providers/registry.ts +1 -0
  21. package/src/providers/shared.ts +4 -4
  22. package/src/providers.ts +9 -53
  23. package/src/scheduler/architecture.md +22 -0
  24. package/src/scheduler/scheduler.ts +131 -56
  25. package/src/scheduler/store.ts +274 -54
  26. package/src/scheduler/time.ts +2 -0
  27. package/src/session/architecture.md +19 -0
  28. package/src/session/store.ts +37 -22
  29. package/src/session/usage.ts +1 -0
  30. package/src/streaming/architecture.md +13 -0
  31. package/src/streaming/event-stream.ts +16 -11
  32. package/src/telemetry-file.ts +74 -24
  33. package/src/telemetry.ts +3 -2
  34. package/src/tools/architecture.md +19 -0
  35. package/src/tools/executor.ts +14 -1
  36. package/src/tools/schema.ts +1 -0
  37. package/src/tools/subagent-result.ts +1 -0
  38. package/src/tools/tool.ts +8 -8
  39. package/src/types/agent.ts +22 -20
  40. package/src/types/architecture.md +20 -0
  41. package/src/types/response.ts +8 -8
  42. package/src/types/scheduler.ts +2 -2
  43. package/src/types/tool.ts +5 -5
  44. package/src/update-models.ts +2 -2
  45. package/src/utils/architecture.md +25 -0
  46. package/src/utils/base64.ts +2 -0
  47. package/src/utils/cache.ts +6 -4
  48. package/src/utils/durable.ts +78 -3
  49. package/src/utils/env.ts +1 -1
  50. package/src/utils/errors.ts +4 -0
  51. package/src/utils/headers.ts +7 -4
  52. package/src/utils/home.ts +1 -1
  53. package/src/utils/media.ts +1 -0
  54. package/src/utils/raw-capture.ts +3 -0
  55. package/src/utils/retry.ts +2 -1
  56. package/src/utils/safe-io.ts +4 -0
  57. package/src/utils/session.ts +10 -166
@@ -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 (Q-12): the `__truncated_raw` marker means the
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.
@@ -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 (Q-55): 1–64 chars of letters, digits, `_`, `-`.
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 Q-52 canonical `thinking` option to a bare level.
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 Q-51 canonical busy policy. `whenBusy` wins over `mode`.
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 Q-52 `maxAttempts` alias to a `maxTries` value.
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` (Q-52). When both are set, `maxTries` wins.
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` (Q-55). When both are set,
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 (Q-55). Preferred over bare `maxTries` for new
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
- // Q-55: names are validated at registration (record-key fallback included).
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).*`.
@@ -10,7 +10,7 @@ import type { Agent } from "../agent/agent.ts";
10
10
  import type { z } from "zod";
11
11
 
12
12
  // ---------------------------------------------------------------------------
13
- // Q-52/Q-53/Q-54 canonical option types (all additive; nothing renamed).
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 (Q-52): bare level, or level + explicit token budget. */
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 (Q-54). Read by the loop via `(config as any).retry`. */
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 (Q-54). */
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 (Q-54). String form is the portable subset. */
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 (Q-54): sequence(s) halting generation. */
56
+ /** Canonical stop control: sequence(s) halting generation. */
57
57
  export type StopOption = string | string[];
58
58
 
59
- /** Canonical structured-output input (Q-61). Zod, JSON Schema, or explicit form. */
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 (Q-52, preferred over `thinkingLevel`).
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
- * `scheduled_actions/`, `telemetry/`). Defaults to `setHome()`, then the `AGENT_ACCEL_HOME` env var, then `.agent-accel/` in the working directory.
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
- // Q-54 missing knobs (all optional, additive; loop reads via `(config as any).*`).
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
- * text) to `<home>/telemetry/<date>.ndjson`. Off by default: the SDK writes nothing unless asked.
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 (Q-22). Turn/budget caps unlimited by default; network timeouts finite. */
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 (Q-22). */
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
- /** Q-51 canonical busy-policy alias: prefer `whenBusy`, `mode` kept for compat. */
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` (Q-51, preferred). When both are set,
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 (Q-52, preferred over `thinkingLevel`).
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
- // Q-54 per-run knobs (all optional, additive; `output` intentionally
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 (Q-39). All fields default to unlimited. */
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)` (Q-42). */
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.
@@ -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` (Q-04). */
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` (Q-04). */
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` (Q-04). */
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 (Q-40). Currently always 1 when present. */
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 (Q-40). */
256
+ /** Batch tool calls for `tool_start` events. */
257
257
  toolCalls?: ToolCallRecord[];
258
- /** Batch tool results for `tool_end` events (Q-40). */
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 (Q-04). */
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 (Q-40).
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 =
@@ -1,4 +1,4 @@
1
- /** Lifecycle of a scheduled action as stored in `tasks.json`. */
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
- /** One entry of `tasks.json`. All times are local clock strings `YYYY-MM-DDTHH:mm:ss` (no offset). */
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 (Q-54/Q-55, additive). */
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 (Q-55, additive). */
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
- // Q-55 Tool API v2 context additions (all optional, additive).
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
- // Q-55 Tool API v2 additions (all optional, additive; loop reads via `(toolDef as any).*`).
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 Q-55 Tool API v2 parity.
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"]`). */
@@ -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
- // Q-34: force is true only with explicit --force (previously also true when no --ttl).
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
- // Q-34: reject non-numeric TTLs (e.g. --ttl=abc) instead of silently caching NaN.
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.
@@ -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") {
@@ -11,7 +11,7 @@ export function clampCacheKey(key?: string): string | undefined {
11
11
  }
12
12
 
13
13
  // ---------------------------------------------------------------------------
14
- // Central redaction helpers (Q-11, temporary home to avoid owner conflicts).
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]" (Q-11).
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 (Q-11). Returns a shallow copy; input untouched.
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 (Q-11).
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 {