agent-accelerator 0.5.9 → 0.6.0

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 (67) hide show
  1. package/README.md +3 -1
  2. package/examples/prompts/SYSTEM_PROMPT.md +2 -0
  3. package/examples/prompts/SYSTEM_PROMPT_AGENT.md +4 -0
  4. package/examples/prompts/SYSTEM_PROMPT_TOOLS.md +1 -0
  5. package/package.json +4 -1
  6. package/src/agent/agent.ts +366 -161
  7. package/src/agent/architecture.md +13 -12
  8. package/src/agent/config-blocks.ts +39 -12
  9. package/src/agent/context.ts +8 -3
  10. package/src/agent/delegation.ts +82 -29
  11. package/src/agent/loop.ts +444 -365
  12. package/src/agent/subagent.ts +1 -3
  13. package/src/agent/subagents-tool.ts +45 -7
  14. package/src/agent/subagents.md +3 -0
  15. package/src/agent/turns.ts +3 -1
  16. package/src/agent/worker.ts +56 -27
  17. package/src/agent/workers.ts +113 -20
  18. package/src/architecture.md +16 -15
  19. package/src/index.ts +34 -10
  20. package/src/models/architecture.md +4 -3
  21. package/src/models/catalog-cache.ts +6 -5
  22. package/src/models/catalog.ts +39 -30
  23. package/src/providers/architecture.md +10 -8
  24. package/src/providers/google.ts +147 -59
  25. package/src/providers/openai-compat.ts +57 -49
  26. package/src/providers/openai.ts +54 -37
  27. package/src/providers/openrouter.ts +85 -30
  28. package/src/providers/registry.ts +6 -21
  29. package/src/providers/shared.ts +87 -33
  30. package/src/providers.ts +56 -115
  31. package/src/scheduler/architecture.md +7 -6
  32. package/src/scheduler/scheduled-actions.md +4 -0
  33. package/src/scheduler/scheduler.ts +186 -60
  34. package/src/scheduler/store.ts +23 -10
  35. package/src/scheduler/time.ts +17 -3
  36. package/src/scheduler/tool.ts +33 -7
  37. package/src/session/architecture.md +7 -6
  38. package/src/session/journal.ts +38 -12
  39. package/src/session/store.ts +122 -61
  40. package/src/session/usage.ts +58 -9
  41. package/src/streaming/architecture.md +4 -3
  42. package/src/streaming/event-stream.ts +26 -12
  43. package/src/telemetry.ts +6 -2
  44. package/src/tools/architecture.md +8 -7
  45. package/src/tools/executor.ts +50 -48
  46. package/src/tools/schema.ts +10 -6
  47. package/src/tools/subagent-result.ts +6 -1
  48. package/src/tools/tool.ts +9 -11
  49. package/src/types/agent.ts +2 -21
  50. package/src/types/architecture.md +11 -10
  51. package/src/types/core.ts +1 -9
  52. package/src/types/errors.ts +26 -4
  53. package/src/types/model.ts +0 -4
  54. package/src/types/tool.ts +12 -2
  55. package/src/update-models.ts +5 -5
  56. package/src/utils/architecture.md +16 -15
  57. package/src/utils/cache.ts +1 -3
  58. package/src/utils/durable.ts +17 -5
  59. package/src/utils/env.ts +9 -7
  60. package/src/utils/errors.ts +6 -14
  61. package/src/utils/headers.ts +3 -10
  62. package/src/utils/media.ts +23 -8
  63. package/src/utils/raw-capture.ts +9 -2
  64. package/src/utils/retry.ts +15 -3
  65. package/src/utils/safe-io.ts +29 -7
  66. package/src/utils/serialization.ts +1 -4
  67. package/src/utils/session.ts +1 -2
@@ -1,10 +1,26 @@
1
- import type { AgentConfig, EditableAgentConfig, AgentRunOptions, MidSessionConfig, ToolChoiceOption, RetryPolicyConfig, Budget, BudgetState, InterruptedRun } from "../types/agent.ts";
1
+ import type {
2
+ AgentConfig,
3
+ EditableAgentConfig,
4
+ AgentRunOptions,
5
+ MidSessionConfig,
6
+ ToolChoiceOption,
7
+ RetryPolicyConfig,
8
+ Budget,
9
+ BudgetState,
10
+ InterruptedRun,
11
+ } from "../types/agent.ts";
2
12
  import type { TokenUsage } from "../types/core.ts";
3
13
  import type { ProviderRequestOptions } from "../types/model.ts";
4
14
  import { normalizeThinkingV2, resolveWhenBusy } from "../tools/tool.ts";
5
15
  import type { ToolDefinition, ToolResultRecord } from "../types/tool.ts";
6
16
  import { isSubagentResult } from "../tools/subagent-result.ts";
7
- import { CACHE_OPTIONS, type ThinkingLevel, type ThinkingConfig, type CacheOption, type ServiceTier } from "../types/core.ts";
17
+ import {
18
+ CACHE_OPTIONS,
19
+ type ThinkingLevel,
20
+ type ThinkingConfig,
21
+ type CacheOption,
22
+ type ServiceTier,
23
+ } from "../types/core.ts";
8
24
  import type { ContentPart } from "../types/message.ts";
9
25
  import { AgentResponse, type StreamEvent } from "../types/response.ts";
10
26
  import { AssistantMessageEventStream } from "../streaming/event-stream.ts";
@@ -13,21 +29,23 @@ import { resolveModel } from "../providers/registry.ts";
13
29
  import { buildAgentTools } from "./delegation.ts";
14
30
  import { createSubagentsTool } from "./subagents-tool.ts";
15
31
  import { WorkerRegistry } from "./workers.ts";
16
- import {
17
- DEFAULT_MAX_DEPTH,
18
- normalizeToolMap,
19
- resolveDynamicModelInput,
20
- resolveMainModel,
21
- } from "./worker.ts";
32
+ import { DEFAULT_MAX_DEPTH, normalizeToolMap, resolveDynamicModelInput, resolveMainModel } from "./worker.ts";
22
33
  import * as path from "node:path";
23
34
  import { registerAgent } from "../scheduler/scheduler.ts";
24
- import { resolveHome } from "../scheduler/store.ts";
35
+ import { resolveHome } from "../utils/home.ts";
25
36
  import { persistTelemetry, registerSessionFolder, unregisterSessionFolder } from "../telemetry-file.ts";
26
37
  import { lockFolder } from "../utils/durable.ts";
27
38
  import { createScheduledActionsTool, getDatetimeTool } from "../scheduler/tool.ts";
28
39
  import { applyEditable, assertModelAllowed, modelKey, toEditableConfig } from "./config-blocks.ts";
29
40
  import { assertNoToolCollisions } from "../tools/executor.ts";
30
- import { runAgentLoop, streamAgentLoop, unfinishedToolCalls, endsWithStop, accumulateUsage, type SteerEntry } from "./loop.ts";
41
+ import {
42
+ runAgentLoop,
43
+ streamAgentLoop,
44
+ unfinishedToolCalls,
45
+ endsWithStop,
46
+ accumulateUsage,
47
+ type SteerEntry,
48
+ } from "./loop.ts";
31
49
  import { isRetryableRun, readRetryAfterMs, sleep } from "../utils/retry.ts";
32
50
  import { createSessionId, nowMs, newRunId, previewPrompt } from "../utils/session.ts";
33
51
  import {
@@ -38,25 +56,43 @@ import {
38
56
  listSubAgentTraceIdsForSessions,
39
57
  type SubAgentTrace,
40
58
  } from "./delegation.ts";
41
- import { addTurn, buildUsageReport, emptyUsage, mergeUsage, modelName, readUsage, type UsageNode, type UsageReport, type UsageTotals } from "../session/usage.ts";
42
- import { saveSessionFile, sessionDirFor, loadSessionDir, findInvalidMessage, priceUsage, telemetryBus, type SessionRunMark, type SessionRunEnd, type SessionPending, type SessionToolRecord } from "../session/store.ts";
59
+ import {
60
+ addTurn,
61
+ buildUsageReport,
62
+ emptyUsage,
63
+ mergeUsage,
64
+ modelName,
65
+ readUsage,
66
+ type UsageNode,
67
+ type UsageReport,
68
+ type UsageTotals,
69
+ } from "../session/usage.ts";
70
+ import {
71
+ saveSessionFile,
72
+ sessionDirFor,
73
+ loadSessionDir,
74
+ findInvalidMessage,
75
+ priceUsage,
76
+ telemetryBus,
77
+ type SessionRunMark,
78
+ type SessionRunEnd,
79
+ type SessionPending,
80
+ type SessionToolRecord,
81
+ } from "../session/store.ts";
43
82
  import { SessionJournal } from "../session/journal.ts";
44
83
  import { ToolLogs, type ToolLogWriter } from "../session/tool-log.ts";
45
84
  import { isLocalPathInput } from "../utils/safe-io.ts";
46
- import { clearSessionRouting, normalizeStructuredOutput, normalizeStop } from "../providers.ts";
85
+ import { clearSessionRouting, normalizeStructuredOutput } from "../providers.ts";
47
86
  import type { StructuredOutputSpec } from "../types/model.ts";
48
87
  import { clearInteractionChains } from "../providers/google.ts";
49
88
  import { validateModelThinking, ensureModelCatalogFresh } from "../models/catalog.ts";
50
- import { buildSessionData, type PersistedAgentSession, type SessionTotals,} from "../session/store.ts";
89
+ import { buildSessionData, type PersistedAgentSession, type SessionTotals } from "../session/store.ts";
51
90
  import { SessionTelemetry } from "../session/store.ts";
52
91
 
53
92
  /**
54
93
  * Resolves a per-run thinking override without mutating agent config.
55
94
  */
56
- export function resolveEffectiveThinking(
57
- base?: ThinkingConfig,
58
- overrideLevel?: string
59
- ): ThinkingConfig | undefined {
95
+ export function resolveEffectiveThinking(base?: ThinkingConfig, overrideLevel?: string): ThinkingConfig | undefined {
60
96
  if (!overrideLevel) return base;
61
97
  if (overrideLevel === "none") return { enabled: false, level: "none", budgetTokens: 0 };
62
98
  if (overrideLevel === "dynamic") return { enabled: true, level: "dynamic", budgetTokens: -1 };
@@ -94,8 +130,7 @@ export interface AgentRunLimits {
94
130
  /** Resolves `(config as any).limits` with defaults (timeouts tolerate reasoning). */
95
131
  function resolveAgentLimits(config: { limits?: Record<string, unknown> }): AgentRunLimits {
96
132
  const raw = (config as { limits?: Record<string, unknown> }).limits ?? {};
97
- const num = (v: unknown): number | undefined =>
98
- typeof v === "number" && Number.isFinite(v) ? v : undefined;
133
+ const num = (v: unknown): number | undefined => (typeof v === "number" && Number.isFinite(v) ? v : undefined);
99
134
  // A network timeout: `0` or `Infinity` turns it off, any other number is milliseconds, anything else is unset (the default applies).
100
135
  const timeout = (v: unknown): number | undefined => (typeof v === "number" && !Number.isNaN(v) ? v : undefined);
101
136
  return {
@@ -104,7 +139,9 @@ function resolveAgentLimits(config: { limits?: Record<string, unknown> }): Agent
104
139
  ...(num(raw.maxToolCallsPerRun) !== undefined
105
140
  ? { maxToolCallsPerRun: Math.max(1, Math.floor(num(raw.maxToolCallsPerRun)!)) }
106
141
  : {}),
107
- ...(num(raw.maxTotalTokens) !== undefined ? { maxTotalTokens: Math.max(1, Math.floor(num(raw.maxTotalTokens)!)) } : {}),
142
+ ...(num(raw.maxTotalTokens) !== undefined
143
+ ? { maxTotalTokens: Math.max(1, Math.floor(num(raw.maxTotalTokens)!)) }
144
+ : {}),
108
145
  requestTimeoutMs: timeout(raw.requestTimeoutMs) ?? 300_000,
109
146
  firstByteTimeoutMs: timeout(raw.firstByteTimeoutMs) ?? 120_000,
110
147
  streamIdleTimeoutMs: timeout(raw.streamIdleTimeoutMs) ?? 300_000,
@@ -216,8 +253,6 @@ export class Agent {
216
253
  private readonly callDefaults: {
217
254
  toolChoice?: ToolChoiceOption;
218
255
  maxOutputTokens?: number;
219
- stop?: string | string[];
220
- seed?: number;
221
256
  retry?: RetryPolicyConfig;
222
257
  env?: Record<string, string>;
223
258
  fetch?: typeof fetch;
@@ -325,34 +360,50 @@ export class Agent {
325
360
  this.subagentModel = dynModel as string | any | undefined;
326
361
  const dynEnabled = dynRaw ? (dynRaw.enabled ?? true) : false;
327
362
  if (dynRaw) {
328
- const dynTools = normalizeToolMap((dynRaw as { tools?: Record<string, ToolDefinition> | ToolDefinition[] }).tools);
363
+ const dynTools = normalizeToolMap(
364
+ (dynRaw as { tools?: Record<string, ToolDefinition> | ToolDefinition[] }).tools
365
+ );
329
366
  const rawDepth = (dynRaw as { maxDepth?: unknown }).maxDepth;
330
367
  // Depth is unlimited unless the developer sets a cap (a whole number >= 1; Infinity also means unlimited).
331
- if (rawDepth !== undefined && rawDepth !== Infinity && !(Number.isInteger(rawDepth) && (rawDepth as number) >= 1)) {
332
- throw new ConfigError(`[Agent Accelerator] Invalid dynamicSubagents.maxDepth ${JSON.stringify(rawDepth)}: expected a whole number >= 1, or omit it for no limit; set dynamicSubagents.enabled: false to turn workers off.`);
368
+ if (
369
+ rawDepth !== undefined &&
370
+ rawDepth !== Infinity &&
371
+ !(Number.isInteger(rawDepth) && (rawDepth as number) >= 1)
372
+ ) {
373
+ throw new ConfigError(
374
+ `[Agent Accelerator] Invalid dynamicSubagents.maxDepth ${JSON.stringify(rawDepth)}: expected a whole number >= 1, or omit it for no limit; set dynamicSubagents.enabled: false to turn workers off.`
375
+ );
333
376
  }
334
377
  const rawBudget = (dynRaw as { workerBudget?: { maxCostUsd?: unknown; maxTotalTokens?: unknown } }).workerBudget;
335
378
  for (const key of ["maxCostUsd", "maxTotalTokens"] as const) {
336
379
  const v = rawBudget?.[key];
337
380
  if (v !== undefined && !(typeof v === "number" && Number.isFinite(v) && v > 0)) {
338
- throw new ConfigError(`[Agent Accelerator] Invalid dynamicSubagents.workerBudget.${key} ${JSON.stringify(v)}: expected a number greater than 0, or omit it for no limit.`);
381
+ throw new ConfigError(
382
+ `[Agent Accelerator] Invalid dynamicSubagents.workerBudget.${key} ${JSON.stringify(v)}: expected a number greater than 0, or omit it for no limit.`
383
+ );
339
384
  }
340
385
  }
341
386
  // Open workers are unlimited unless the developer sets a cap (a number >= 1; Infinity also means unlimited).
342
387
  const rawMax = (dynRaw as { maxSpawn?: unknown }).maxSpawn;
343
388
  if (rawMax !== undefined && (typeof rawMax !== "number" || Number.isNaN(rawMax))) {
344
- throw new ConfigError(`[Agent Accelerator] Invalid dynamicSubagents.maxSpawn ${JSON.stringify(rawMax)}: expected a whole number >= 1, or omit it for no limit.`);
389
+ throw new ConfigError(
390
+ `[Agent Accelerator] Invalid dynamicSubagents.maxSpawn ${JSON.stringify(rawMax)}: expected a whole number >= 1, or omit it for no limit.`
391
+ );
345
392
  }
346
393
  const maxSpawn = rawMax === undefined || rawMax === Infinity ? Infinity : Math.max(1, Math.floor(rawMax));
347
394
  // Only scheduled actions are timed: no limit unless the developer sets one (0 and Infinity mean none).
348
395
  for (const removed of ["timeout", "modelMaySetTimeout"]) {
349
396
  if ((dynRaw as Record<string, unknown>)[removed] !== undefined) {
350
- throw new ConfigError(`[Agent Accelerator] dynamicSubagents.${removed} was removed: set dynamicSubagents.workerTimeoutMs (the longest a scheduled run may take, optional) and let each scheduled action carry its own timeoutMs.`);
397
+ throw new ConfigError(
398
+ `[Agent Accelerator] dynamicSubagents.${removed} was removed: set dynamicSubagents.workerTimeoutMs (the longest a scheduled run may take, optional) and let each scheduled action carry its own timeoutMs.`
399
+ );
351
400
  }
352
401
  }
353
402
  const rawTimeout = (dynRaw as { workerTimeoutMs?: unknown }).workerTimeoutMs;
354
403
  if (rawTimeout !== undefined && (typeof rawTimeout !== "number" || Number.isNaN(rawTimeout) || rawTimeout < 0)) {
355
- throw new ConfigError(`[Agent Accelerator] Invalid dynamicSubagents.workerTimeoutMs ${JSON.stringify(rawTimeout)}: expected a number of milliseconds > 0, or omit it (0 and Infinity mean no limit).`);
404
+ throw new ConfigError(
405
+ `[Agent Accelerator] Invalid dynamicSubagents.workerTimeoutMs ${JSON.stringify(rawTimeout)}: expected a number of milliseconds > 0, or omit it (0 and Infinity mean no limit).`
406
+ );
356
407
  }
357
408
  const workerTimeoutMs = !rawTimeout ? Infinity : Math.max(1, Math.floor(rawTimeout));
358
409
  this.dynamicSubagents = {
@@ -419,17 +470,20 @@ export class Agent {
419
470
  }
420
471
  this.cache = config.cache;
421
472
  if (config.resume !== undefined && config.resume !== "automatic" && config.resume !== "explicit") {
422
- throw new ConfigError(`[Agent Accelerator] Invalid resume ${JSON.stringify(config.resume)}: expected "automatic" (default) or "explicit".`);
473
+ throw new ConfigError(
474
+ `[Agent Accelerator] Invalid resume ${JSON.stringify(config.resume)}: expected "automatic" (default) or "explicit".`
475
+ );
423
476
  }
424
477
  this.resumeMode = config.resume ?? "automatic";
425
478
  if (config.runRetries !== undefined && !(Number.isInteger(config.runRetries) && config.runRetries >= 0)) {
426
- throw new ConfigError(`[Agent Accelerator] Invalid runRetries ${JSON.stringify(config.runRetries)}: expected a whole number >= 0 (default 3, 0 turns it off).`);
479
+ throw new ConfigError(
480
+ `[Agent Accelerator] Invalid runRetries ${JSON.stringify(config.runRetries)}: expected a whole number >= 0 (default 3, 0 turns it off).`
481
+ );
427
482
  }
428
483
  this.runRetries = config.runRetries ?? 3;
429
484
 
430
485
  this.serviceTier = config.serviceTier;
431
- this.onFailure =
432
- config.onFailure === "rollback" || config.onFailure === "keep" ? config.onFailure : "auto";
486
+ this.onFailure = config.onFailure === "rollback" || config.onFailure === "keep" ? config.onFailure : "auto";
433
487
  this.forkConfig = { ...config };
434
488
  this.captureRaw = config.captureRaw;
435
489
  this.budget = config.budget ? { ...config.budget } : undefined;
@@ -448,8 +502,6 @@ export class Agent {
448
502
  this.callDefaults = {
449
503
  toolChoice: config.toolChoice,
450
504
  maxOutputTokens: config.maxOutputTokens,
451
- stop: config.stop,
452
- seed: config.seed,
453
505
  retry: config.retry,
454
506
  env: config.env,
455
507
  fetch: config.fetch,
@@ -466,7 +518,6 @@ export class Agent {
466
518
  Object.assign(this.tools, normalizeToolMap(config.tools));
467
519
  }
468
520
 
469
-
470
521
  if (Array.isArray(config.subagents)) {
471
522
  const subagentTools = buildAgentTools(config.subagents);
472
523
  for (const [toolName, toolDef] of Object.entries(subagentTools)) {
@@ -574,7 +625,11 @@ export class Agent {
574
625
 
575
626
  // Records one model call of THIS agent (priced per turn, with the service tier); in: the loop's onTurnUsage, out: this.usageTotals (saved by the next persist).
576
627
  private recordTurnUsage(usage: TokenUsage, spec: Parameters<typeof priceUsage>[1]): void {
577
- addTurn(this.usageTotals, usage, priceUsage(usage, spec, this.serviceTier ? { serviceTier: this.serviceTier } : undefined));
628
+ addTurn(
629
+ this.usageTotals,
630
+ usage,
631
+ priceUsage(usage, spec, this.serviceTier ? { serviceTier: this.serviceTier } : undefined)
632
+ );
578
633
  }
579
634
 
580
635
  /** Adds another agent's spending to this one (a fixed worker's per-call copy into its template). @internal */
@@ -589,7 +644,10 @@ export class Agent {
589
644
  * below it. Spending of one-shot scheduled-action workers is in their run files, not here.
590
645
  */
591
646
  usageReport(): UsageReport {
592
- return buildUsageReport({ id: this.sessionId, name: this.name, model: modelName(this.modelStringOrSpec), totals: this.usageTotals }, [...this.workers.usageNodes(), ...this.fixedWorkerNodes()]);
647
+ return buildUsageReport(
648
+ { id: this.sessionId, name: this.name, model: modelName(this.modelStringOrSpec), totals: this.usageTotals },
649
+ [...this.workers.usageNodes(), ...this.fixedWorkerNodes()]
650
+ );
593
651
  }
594
652
 
595
653
  // The `subagents: [...]` workers as usage nodes (their per-call copies' spending is merged into them); in: the constructor config, out: usageReport.
@@ -598,14 +656,25 @@ export class Agent {
598
656
  for (const entry of this.forkConfig.subagents ?? []) {
599
657
  const worker = "agent" in entry ? entry.agent : entry;
600
658
  const name = "agent" in entry ? entry.name : worker.name;
601
- nodes.push({ id: name, name, model: modelName(worker.modelStringOrSpec), status: "fixed", totals: worker.usageTotals, workers: [] });
659
+ nodes.push({
660
+ id: name,
661
+ name,
662
+ model: modelName(worker.modelStringOrSpec),
663
+ status: "fixed",
664
+ totals: worker.usageTotals,
665
+ workers: [],
666
+ });
602
667
  }
603
668
  return nodes;
604
669
  }
605
670
 
606
671
  // Says on the telemetry bus that resuming (or a prompt that was waiting) failed; in: a rejected resume, out: telemetryBus.
607
672
  private reportResumeFailure(err: unknown): void {
608
- telemetryBus.emitWarning("agent.resume_failed", `[Agent Accelerator] Resuming session ${this.sessionId} failed: ${err instanceof Error ? err.message : String(err)}`, { sessionId: this.sessionId });
673
+ telemetryBus.emitWarning(
674
+ "agent.resume_failed",
675
+ `[Agent Accelerator] Resuming session ${this.sessionId} failed: ${err instanceof Error ? err.message : String(err)}`,
676
+ { sessionId: this.sessionId }
677
+ );
609
678
  }
610
679
 
611
680
  /** Runs that a crash cut off (they began, never ended and are not running now), oldest first. Needs `persist: { dir }` and the same `sessionId` as before the restart. */
@@ -629,7 +698,8 @@ export class Agent {
629
698
  */
630
699
  resume(): Promise<AgentResponse | undefined> {
631
700
  if (this.resuming) return this.resuming;
632
- if (this.currentRun) throw new Error("[Agent Accelerator] resume() needs an idle agent: a run is already active.");
701
+ if (this.currentRun)
702
+ throw new ValidationError("[Agent Accelerator] resume() needs an idle agent: a run is already active.");
633
703
  const mark = this.runMarks.filter((m) => !m.end && m !== this.activeMark).pop();
634
704
  const last = this.context.messages[this.context.messages.length - 1];
635
705
  const nothingOpen = unfinishedToolCalls(this.context.messages).length === 0;
@@ -642,7 +712,11 @@ export class Agent {
642
712
  telemetryBus.emit({
643
713
  code: "agent.resumed",
644
714
  message: `[Agent Accelerator] resuming run ${mark.id} of session ${this.sessionId}`,
645
- data: { sessionId: this.sessionId, runId: mark.id, pendingToolCalls: unfinishedToolCalls(this.context.messages).map((c) => c.name) },
715
+ data: {
716
+ sessionId: this.sessionId,
717
+ runId: mark.id,
718
+ pendingToolCalls: unfinishedToolCalls(this.context.messages).map((c) => c.name),
719
+ },
646
720
  });
647
721
  const run = this.startNonStreamingRun("", undefined, mark);
648
722
  const clear = (): void => {
@@ -655,7 +729,18 @@ export class Agent {
655
729
 
656
730
  // The finished tool calls of the current run as results the loop can reuse; in: toolRecords, out: loopConfig.resume.stored.
657
731
  private storedToolResults(): Map<string, ToolResultRecord> {
658
- return new Map(this.toolRecords.map((t) => [t.id, { id: t.id, name: t.name, result: t.result, ...(t.isError ? { isError: true } : {}), ...(t.stopped ? { stopped: true } : {}) } as ToolResultRecord]));
732
+ return new Map(
733
+ this.toolRecords.map((t) => [
734
+ t.id,
735
+ {
736
+ id: t.id,
737
+ name: t.name,
738
+ result: t.result,
739
+ ...(t.isError ? { isError: true } : {}),
740
+ ...(t.stopped ? { stopped: true } : {}),
741
+ } as ToolResultRecord,
742
+ ])
743
+ );
659
744
  }
660
745
 
661
746
  // Closes a run's mark with how it ended and saves that at once (not on the next tick), so the disk is up to date when `await run()` returns; in: the run's promise handlers, out: runMarks + journal.
@@ -683,9 +768,7 @@ export class Agent {
683
768
  const base = this.instructions || "";
684
769
  let out = base;
685
770
  const isThinkingEnabled =
686
- this.thinkingConfig?.enabled !== false &&
687
- this.thinkingConfig?.level &&
688
- this.thinkingConfig?.level !== "none";
771
+ this.thinkingConfig?.enabled !== false && this.thinkingConfig?.level && this.thinkingConfig?.level !== "none";
689
772
  if (isThinkingEnabled && !out.includes("[Reasoning Directive]")) {
690
773
  const guidance =
691
774
  "[Reasoning Directive]\n" +
@@ -707,8 +790,12 @@ export class Agent {
707
790
  this.toolRecords.length = 0; // a cleared conversation has no run to resume, so its finished tool results go too
708
791
  this.activeMark = undefined;
709
792
  // Reset also drops session-scoped provider chaining/routing.
710
- try { clearInteractionChains(sid); } catch {}
711
- try { clearSessionRouting(sid); } catch {}
793
+ try {
794
+ clearInteractionChains(sid);
795
+ } catch {}
796
+ try {
797
+ clearSessionRouting(sid);
798
+ } catch {}
712
799
  }
713
800
 
714
801
  /**
@@ -727,17 +814,27 @@ export class Agent {
727
814
  /** Drops one named session. No-op for unknown ids. */
728
815
  disposeSession(id: string): void {
729
816
  unregisterSessionFolder(id);
730
- try { this.sessions.delete(id); } catch {}
731
- try { clearInteractionChains(id); } catch {}
732
- try { clearSessionRouting(id); } catch {}
817
+ try {
818
+ this.sessions.delete(id);
819
+ } catch {}
820
+ try {
821
+ clearInteractionChains(id);
822
+ } catch {}
823
+ try {
824
+ clearSessionRouting(id);
825
+ } catch {}
733
826
  }
734
827
 
735
828
  /** Drops all named sessions except the default context. */
736
829
  clearSessions(): void {
737
830
  try {
738
831
  for (const id of [...this.sessions.keys()]) {
739
- try { clearInteractionChains(id); } catch {}
740
- try { clearSessionRouting(id); } catch {}
832
+ try {
833
+ clearInteractionChains(id);
834
+ } catch {}
835
+ try {
836
+ clearSessionRouting(id);
837
+ } catch {}
741
838
  }
742
839
  this.sessions.clear();
743
840
  } catch {}
@@ -753,8 +850,12 @@ export class Agent {
753
850
  for (const [key, entry] of [...this.sessions.entries()]) {
754
851
  if (now - entry.lastAccess > SESSION_TTL_MS) {
755
852
  this.sessions.delete(key);
756
- try { clearInteractionChains(key); } catch {}
757
- try { clearSessionRouting(key); } catch {}
853
+ try {
854
+ clearInteractionChains(key);
855
+ } catch {}
856
+ try {
857
+ clearSessionRouting(key);
858
+ } catch {}
758
859
  }
759
860
  }
760
861
  } catch {}
@@ -771,12 +872,19 @@ export class Agent {
771
872
  let oldest: string | undefined;
772
873
  let oldestTs = Infinity;
773
874
  for (const [key, entry] of this.sessions) {
774
- if (entry.lastAccess < oldestTs) { oldestTs = entry.lastAccess; oldest = key; }
875
+ if (entry.lastAccess < oldestTs) {
876
+ oldestTs = entry.lastAccess;
877
+ oldest = key;
878
+ }
775
879
  }
776
880
  if (!oldest) break;
777
881
  this.sessions.delete(oldest);
778
- try { clearInteractionChains(oldest); } catch {}
779
- try { clearSessionRouting(oldest); } catch {}
882
+ try {
883
+ clearInteractionChains(oldest);
884
+ } catch {}
885
+ try {
886
+ clearSessionRouting(oldest);
887
+ } catch {}
780
888
  }
781
889
  } catch {}
782
890
  return fresh;
@@ -789,8 +897,12 @@ export class Agent {
789
897
  const err = Object.assign(new Error("Queue cleared"), { name: "AbortError" });
790
898
  const pending = this.pendingQueue.splice(0);
791
899
  for (const entry of pending) {
792
- try { entry.reject(err); } catch {}
793
- try { entry.deferredStream?.fail(err instanceof Error ? err : new Error(String(err))); } catch {}
900
+ try {
901
+ entry.reject(err);
902
+ } catch {}
903
+ try {
904
+ entry.deferredStream?.fail(err instanceof Error ? err : new Error(String(err)));
905
+ } catch {}
794
906
  }
795
907
  this.persistNow();
796
908
  return n;
@@ -800,7 +912,7 @@ export class Agent {
800
912
  private assertSteerRoom(inbox: SteerEntry[]): void {
801
913
  const max = this.maxSteers ?? 20;
802
914
  if (inbox.length >= max) {
803
- throw new Error(
915
+ throw new ValidationError(
804
916
  `[Agent Accelerator] Steer inbox is full (${inbox.length}/${max}). Wait for the active run to drain or increase midSession.maxSteers.`
805
917
  );
806
918
  }
@@ -858,7 +970,9 @@ export class Agent {
858
970
  interrupt(arg?: Error | string | ContentPart[], options?: AgentRunOptions): void | Promise<AgentResponse> {
859
971
  if (typeof arg === "string" || Array.isArray(arg)) {
860
972
  if (options?.stream === true) {
861
- throw new Error('[Agent Accelerator] interrupt(message) is non-streaming. Use stream(prompt, { enqueue: "interrupt" }) for a streaming interrupt.');
973
+ throw new ValidationError(
974
+ '[Agent Accelerator] interrupt(message) is non-streaming. Use stream(prompt, { enqueue: "interrupt" }) for a streaming interrupt.'
975
+ );
862
976
  }
863
977
  return this.run(arg, { ...options, enqueue: "interrupt" });
864
978
  }
@@ -900,12 +1014,12 @@ export class Agent {
900
1014
  steer(prompt: string | ContentPart[], options?: AgentRunOptions): Promise<AgentResponse> {
901
1015
  this.assertPromptValid(prompt);
902
1016
  if (options?.stream === true) {
903
- throw new Error(
1017
+ throw new ValidationError(
904
1018
  '[Agent Accelerator] steer() is non-streaming. Use stream(prompt, { enqueue: "steer" }) for a streaming steer.'
905
1019
  );
906
1020
  }
907
1021
  if (this.midSessionConfig.mode === "queue") {
908
- throw new Error(
1022
+ throw new ConfigError(
909
1023
  '[Agent Accelerator] steer() is disabled by midSession.mode "queue". Use queue() or set mode to "auto"/"steer".'
910
1024
  );
911
1025
  }
@@ -958,19 +1072,19 @@ export class Agent {
958
1072
  queue(prompt: string | ContentPart[], options?: AgentRunOptions): Promise<AgentResponse> {
959
1073
  this.assertPromptValid(prompt);
960
1074
  if (options?.stream === true) {
961
- throw new Error(
1075
+ throw new ValidationError(
962
1076
  '[Agent Accelerator] queue() is non-streaming. Use stream(prompt, { enqueue: "queue" }) for a streaming follow-up.'
963
1077
  );
964
1078
  }
965
1079
  if (this.midSessionConfig.mode === "steer") {
966
- throw new Error(
1080
+ throw new ConfigError(
967
1081
  '[Agent Accelerator] queue() is disabled by midSession.mode "steer". Use steer() or set mode to "auto"/"queue".'
968
1082
  );
969
1083
  }
970
1084
  if (!this.currentRun) return this.run(prompt, options) as Promise<AgentResponse>;
971
1085
  const max = this.midSessionConfig.maxQueued ?? 20;
972
1086
  if (this.pendingQueue.length >= max) {
973
- throw new Error(
1087
+ throw new ValidationError(
974
1088
  `[Agent Accelerator] Queue is full (${this.pendingQueue.length}/${max}). Wait for the active run to finish or increase midSession.maxQueued.`
975
1089
  );
976
1090
  }
@@ -1001,8 +1115,12 @@ export class Agent {
1001
1115
  reason instanceof Error ? reason : new Error("Queued run aborted before start"),
1002
1116
  { name: "AbortError" }
1003
1117
  );
1004
- try { next.reject(abortErr); } catch {}
1005
- try { next.deferredStream?.fail(abortErr instanceof Error ? abortErr : new Error(String(abortErr))); } catch {}
1118
+ try {
1119
+ next.reject(abortErr);
1120
+ } catch {}
1121
+ try {
1122
+ next.deferredStream?.fail(abortErr instanceof Error ? abortErr : new Error(String(abortErr)));
1123
+ } catch {}
1006
1124
  queueMicrotask(() => this.pumpQueue());
1007
1125
  return;
1008
1126
  }
@@ -1011,30 +1129,48 @@ export class Agent {
1011
1129
  const deferred = next.deferredStream;
1012
1130
  try {
1013
1131
  const real = this.startStreamingRun(next.prompt, next.options);
1014
- try { (deferred as any).__piped = true; } catch {}
1132
+ try {
1133
+ (deferred as any).__piped = true;
1134
+ } catch {}
1015
1135
  const forward = (e: StreamEvent) => {
1016
- try { deferred.push(e); } catch {}
1136
+ try {
1137
+ deferred.push(e);
1138
+ } catch {}
1017
1139
  };
1018
1140
  real.on("*", forward as any);
1019
1141
  try {
1020
1142
  deferred.onCancel(() => {
1021
- try { real.cancel(); } catch {}
1143
+ try {
1144
+ real.cancel();
1145
+ } catch {}
1022
1146
  });
1023
1147
  } catch {}
1024
1148
  real.result().then(
1025
1149
  (res) => {
1026
- try { next.resolve(res); } catch {}
1027
- try { deferred.end(res); } catch {}
1150
+ try {
1151
+ next.resolve(res);
1152
+ } catch {}
1153
+ try {
1154
+ deferred.end(res);
1155
+ } catch {}
1028
1156
  },
1029
1157
  (err) => {
1030
- try { next.reject(err); } catch {}
1158
+ try {
1159
+ next.reject(err);
1160
+ } catch {}
1031
1161
  // Pipe failures into the deferred stream so for-await sees them.
1032
- try { deferred.fail(err instanceof Error ? err : new Error(String(err))); } catch {}
1162
+ try {
1163
+ deferred.fail(err instanceof Error ? err : new Error(String(err)));
1164
+ } catch {}
1033
1165
  }
1034
1166
  );
1035
1167
  } catch (err) {
1036
- try { next.reject(err); } catch {}
1037
- try { deferred.fail(err instanceof Error ? err : new Error(String(err))); } catch {}
1168
+ try {
1169
+ next.reject(err);
1170
+ } catch {}
1171
+ try {
1172
+ deferred.fail(err instanceof Error ? err : new Error(String(err)));
1173
+ } catch {}
1038
1174
  // Keep draining even if starting failed.
1039
1175
  queueMicrotask(() => this.pumpQueue());
1040
1176
  }
@@ -1047,7 +1183,11 @@ export class Agent {
1047
1183
  private finishCurrentRun(): void {
1048
1184
  // Leftover steer entries become queued follow-ups instead of dropping.
1049
1185
  let leftover: SteerEntry[] = [];
1050
- try { leftover = this.currentRun?.steerInbox?.splice(0) ?? []; } catch { leftover = []; }
1186
+ try {
1187
+ leftover = this.currentRun?.steerInbox?.splice(0) ?? [];
1188
+ } catch {
1189
+ leftover = [];
1190
+ }
1051
1191
  this.currentRun = null;
1052
1192
  if (leftover.length > 0) {
1053
1193
  try {
@@ -1057,12 +1197,18 @@ export class Agent {
1057
1197
  for (const entry of leftover) {
1058
1198
  try {
1059
1199
  if ((entry.options as { signal?: AbortSignal } | undefined)?.signal?.aborted) {
1060
- entry.settle?.reject(Object.assign(new Error("Steer aborted before it could run"), { name: "AbortError" }));
1200
+ entry.settle?.reject(
1201
+ Object.assign(new Error("Steer aborted before it could run"), { name: "AbortError" })
1202
+ );
1061
1203
  continue;
1062
1204
  }
1063
- } catch { continue; }
1205
+ } catch {
1206
+ continue;
1207
+ }
1064
1208
  if (this.pendingQueue.length + lateSteers.length >= max) {
1065
- entry.settle?.reject(new Error("[Agent Accelerator] Steer dropped: the follow-up queue is full."));
1209
+ entry.settle?.reject(
1210
+ new ValidationError("[Agent Accelerator] Steer dropped: the follow-up queue is full.")
1211
+ );
1066
1212
  continue;
1067
1213
  }
1068
1214
  // The steer was never injected: it now runs as a follow-up, and the
@@ -1091,7 +1237,9 @@ export class Agent {
1091
1237
  try {
1092
1238
  const dispose = mergeDisposers.get(runController);
1093
1239
  if (dispose) {
1094
- try { dispose(); } catch {}
1240
+ try {
1241
+ dispose();
1242
+ } catch {}
1095
1243
  mergeDisposers.delete(runController);
1096
1244
  }
1097
1245
  } catch {}
@@ -1105,7 +1253,9 @@ export class Agent {
1105
1253
  try {
1106
1254
  runController.abort((userSignal as any).reason);
1107
1255
  } catch {
1108
- try { runController.abort(); } catch {}
1256
+ try {
1257
+ runController.abort();
1258
+ } catch {}
1109
1259
  }
1110
1260
  return runController.signal;
1111
1261
  }
@@ -1121,13 +1271,19 @@ export class Agent {
1121
1271
  try {
1122
1272
  runController.abort((userSignal as any).reason);
1123
1273
  } catch {
1124
- try { runController.abort(); } catch {}
1274
+ try {
1275
+ runController.abort();
1276
+ } catch {}
1125
1277
  }
1126
1278
  };
1127
- try { userSignal.addEventListener("abort", forward, { once: true }); } catch {}
1279
+ try {
1280
+ userSignal.addEventListener("abort", forward, { once: true });
1281
+ } catch {}
1128
1282
  try {
1129
1283
  mergeDisposers.set(runController, () => {
1130
- try { userSignal.removeEventListener("abort", forward); } catch {}
1284
+ try {
1285
+ userSignal.removeEventListener("abort", forward);
1286
+ } catch {}
1131
1287
  });
1132
1288
  } catch {}
1133
1289
  return runController.signal;
@@ -1223,9 +1379,7 @@ export class Agent {
1223
1379
  const toolChoice = normalizeToolChoiceOption(runOptions?.toolChoice ?? d.toolChoice);
1224
1380
  const retry = { ...d.retry, ...runOptions?.retry };
1225
1381
  const env = { ...d.env, ...runOptions?.env };
1226
- const stop = normalizeStop(runOptions?.stop ?? d.stop);
1227
1382
  const maxOutputTokens = runOptions?.maxOutputTokens ?? d.maxOutputTokens;
1228
- const seed = runOptions?.seed ?? d.seed;
1229
1383
  const fetchImpl = runOptions?.fetch ?? d.fetch;
1230
1384
  return {
1231
1385
  apiKey: this.apiKey,
@@ -1239,8 +1393,6 @@ export class Agent {
1239
1393
  ...(this.googleTier ? { googleTier: this.googleTier } : {}),
1240
1394
  ...(toolChoice !== undefined ? { toolChoice } : {}),
1241
1395
  ...(maxOutputTokens !== undefined ? { maxOutputTokens } : {}),
1242
- ...(stop ? { stop } : {}),
1243
- ...(seed !== undefined ? { seed } : {}),
1244
1396
  ...(Object.keys(env).length > 0 ? { env } : {}),
1245
1397
  ...(fetchImpl ? { fetch: fetchImpl } : {}),
1246
1398
  ...(retry.maxRetries !== undefined ? { maxRetries: retry.maxRetries } : {}),
@@ -1314,13 +1466,18 @@ export class Agent {
1314
1466
  const failedAttempts: FailedAttempt[] = [];
1315
1467
  for (let attempt = 0; ; attempt++) {
1316
1468
  try {
1317
- res = await runAgentLoop(attempt === 0 ? loopConfig : { ...loopConfig, resume: { stored: this.storedToolResults() } });
1469
+ res = await runAgentLoop(
1470
+ attempt === 0 ? loopConfig : { ...loopConfig, resume: { stored: this.storedToolResults() } }
1471
+ );
1318
1472
  break;
1319
1473
  } catch (err) {
1320
1474
  if (attempt >= this.runRetries || runOptions?.signal?.aborted || !isRetryableRun(err)) throw err;
1321
1475
  const partial = (err as { partial?: FailedAttempt }).partial;
1322
1476
  if (partial?.usage) failedAttempts.push(partial);
1323
- const delayMs = Math.max(RUN_RETRY_DELAYS_MS[Math.min(attempt, RUN_RETRY_DELAYS_MS.length - 1)]!, readRetryAfterMs(err) ?? 0);
1477
+ const delayMs = Math.max(
1478
+ RUN_RETRY_DELAYS_MS[Math.min(attempt, RUN_RETRY_DELAYS_MS.length - 1)]!,
1479
+ readRetryAfterMs(err) ?? 0
1480
+ );
1324
1481
  telemetryBus.emitWarning(
1325
1482
  "agent.run_retry",
1326
1483
  `[Agent Accelerator] Run failed (${err instanceof Error ? err.message : String(err)}); retry ${attempt + 1} of ${this.runRetries} in ${delayMs} ms.`,
@@ -1345,7 +1502,9 @@ export class Agent {
1345
1502
  (err) => this.endRun(mark, isAbortError(err) ? "aborted" : "failed")
1346
1503
  );
1347
1504
  const cleanup = () => {
1348
- try { this.cleanupMerge(runController); } catch {}
1505
+ try {
1506
+ this.cleanupMerge(runController);
1507
+ } catch {}
1349
1508
  if (this.currentRun?.runPromise === task) this.finishCurrentRun();
1350
1509
  };
1351
1510
  task.then(cleanup, cleanup);
@@ -1353,10 +1512,7 @@ export class Agent {
1353
1512
  }
1354
1513
 
1355
1514
  // Starts one streaming run: validates the model and thinking level synchronously, adds the prompt, builds the loop config and returns the loop's event stream, recording the run ending when it settles; in: stream()/pumpQueue, out: an AssistantMessageEventStream.
1356
- private startStreamingRun(
1357
- prompt: string | ContentPart[],
1358
- options?: AgentRunOptions
1359
- ): AssistantMessageEventStream {
1515
+ private startStreamingRun(prompt: string | ContentPart[], options?: AgentRunOptions): AssistantMessageEventStream {
1360
1516
  const resolved = resolveModel(this.modelStringOrSpec);
1361
1517
  const effectiveLevel = normalizeThinkingV2(options?.thinking, options?.thinkingLevel) || this.thinkingConfig?.level;
1362
1518
  if (effectiveLevel) {
@@ -1408,7 +1564,9 @@ export class Agent {
1408
1564
  this.mergeSubagentTraces(res.subagents as any);
1409
1565
  this.persistNow();
1410
1566
  } catch {}
1411
- try { this.cleanupMerge(runController); } catch {}
1567
+ try {
1568
+ this.cleanupMerge(runController);
1569
+ } catch {}
1412
1570
  if (this.stateless) {
1413
1571
  targetContext.messages = [];
1414
1572
  targetContext.thoughtSignatures = [];
@@ -1417,7 +1575,9 @@ export class Agent {
1417
1575
  },
1418
1576
  (err) => {
1419
1577
  this.endRun(mark, isAbortError(err) ? "aborted" : "failed");
1420
- try { this.cleanupMerge(runController); } catch {}
1578
+ try {
1579
+ this.cleanupMerge(runController);
1580
+ } catch {}
1421
1581
  if (this.currentRun?.outerStream === s) this.finishCurrentRun();
1422
1582
  }
1423
1583
  );
@@ -1534,12 +1694,24 @@ export class Agent {
1534
1694
  else if (cfg.file) saveSessionFile(cfg.file, this as any, null);
1535
1695
  } catch (err) {
1536
1696
  // Surface via hook instead of swallowing silently.
1537
- try { (cfg as unknown as { onPersistError?: (e: unknown) => void }).onPersistError?.(err); } catch {}
1697
+ try {
1698
+ (cfg as unknown as { onPersistError?: (e: unknown) => void }).onPersistError?.(err);
1699
+ } catch {}
1538
1700
  }
1539
1701
  }
1540
1702
 
1541
1703
  /** Merges completed worker metadata (incl. steps) into this agent's trace registry. */
1542
- private mergeSubagentTraces(subagents: Array<{ trackingId?: string; name: string; sessionId?: string; parentSessionId?: string; task: string; steps?: SubAgentTrace["steps"]; [k: string]: unknown }>): void {
1704
+ private mergeSubagentTraces(
1705
+ subagents: Array<{
1706
+ trackingId?: string;
1707
+ name: string;
1708
+ sessionId?: string;
1709
+ parentSessionId?: string;
1710
+ task: string;
1711
+ steps?: SubAgentTrace["steps"];
1712
+ [k: string]: unknown;
1713
+ }>
1714
+ ): void {
1543
1715
  if (!subagents || subagents.length === 0) return;
1544
1716
  const rawOf = (id?: string): string | undefined => {
1545
1717
  if (!id) return undefined;
@@ -1641,15 +1813,19 @@ export class Agent {
1641
1813
  try {
1642
1814
  const g: any = globalThis as any;
1643
1815
  this.context.messages = Array.isArray(data.messages)
1644
- ? (typeof g.structuredClone === "function"
1645
- ? g.structuredClone(data.messages)
1646
- : JSON.parse(JSON.stringify(data.messages)))
1816
+ ? typeof g.structuredClone === "function"
1817
+ ? g.structuredClone(data.messages)
1818
+ : JSON.parse(JSON.stringify(data.messages))
1647
1819
  : [];
1648
1820
  } catch {
1649
1821
  this.context.messages = Array.isArray(data.messages) ? [...data.messages] : [];
1650
1822
  }
1651
1823
  this.context.thoughtSignatures = [];
1652
- this.runMarks.splice(0, this.runMarks.length, ...(data.runs ?? []).filter((r) => r.i < this.context.messages.length));
1824
+ this.runMarks.splice(
1825
+ 0,
1826
+ this.runMarks.length,
1827
+ ...(data.runs ?? []).filter((r) => r.i < this.context.messages.length)
1828
+ );
1653
1829
  this.restoreUsage(data.usage);
1654
1830
  if (data.systemPrompt) {
1655
1831
  this.context.systemPrompt = data.systemPrompt;
@@ -1658,10 +1834,18 @@ export class Agent {
1658
1834
  }
1659
1835
  // Importing a snapshot invalidates prior provider chaining/routing
1660
1836
  // for both the previous and the restored session.
1661
- try { clearInteractionChains(prevSid); } catch {}
1662
- try { clearSessionRouting(prevSid); } catch {}
1663
- try { clearInteractionChains(this.sessionId); } catch {}
1664
- try { clearSessionRouting(this.sessionId); } catch {}
1837
+ try {
1838
+ clearInteractionChains(prevSid);
1839
+ } catch {}
1840
+ try {
1841
+ clearSessionRouting(prevSid);
1842
+ } catch {}
1843
+ try {
1844
+ clearInteractionChains(this.sessionId);
1845
+ } catch {}
1846
+ try {
1847
+ clearSessionRouting(this.sessionId);
1848
+ } catch {}
1665
1849
  }
1666
1850
 
1667
1851
  /**
@@ -1691,7 +1875,11 @@ export class Agent {
1691
1875
  }
1692
1876
 
1693
1877
  // Adds the user prompt to the target context (fresh context when stateless, stable system prompt, additionalContext as a user prefix) and marks the run start for the default session; in: a run start, out: the new SessionRunMark.
1694
- private prepareTurn(prompt: string | ContentPart[], options?: AgentRunOptions, target?: AgentContext): SessionRunMark | undefined {
1878
+ private prepareTurn(
1879
+ prompt: string | ContentPart[],
1880
+ options?: AgentRunOptions,
1881
+ target?: AgentContext
1882
+ ): SessionRunMark | undefined {
1695
1883
  // Prompt validation throws synchronously in both run() and
1696
1884
  // stream() (same timing; documented here).
1697
1885
  if (Array.isArray(prompt) && prompt.length === 0) {
@@ -1750,23 +1938,14 @@ export class Agent {
1750
1938
  run(
1751
1939
  prompt: string | ContentPart[],
1752
1940
  options: AgentRunOptions & { stream: true } & (
1753
- | { onDelta: NonNullable<AgentRunOptions["onDelta"]> }
1754
- | { onThinkingDelta: NonNullable<AgentRunOptions["onThinkingDelta"]> }
1755
- | { onEvent: NonNullable<AgentRunOptions["onEvent"]> }
1756
- )
1941
+ | { onDelta: NonNullable<AgentRunOptions["onDelta"]> }
1942
+ | { onThinkingDelta: NonNullable<AgentRunOptions["onThinkingDelta"]> }
1943
+ | { onEvent: NonNullable<AgentRunOptions["onEvent"]> }
1944
+ )
1757
1945
  ): Promise<AgentResponse> & AsyncIterable<StreamEvent>;
1758
- run(
1759
- prompt: string | ContentPart[],
1760
- options: AgentRunOptions & { stream: true }
1761
- ): AssistantMessageEventStream;
1762
- run(
1763
- prompt: string | ContentPart[],
1764
- options?: AgentRunOptions
1765
- ): Promise<AgentResponse>;
1766
- run(
1767
- prompt: string | ContentPart[],
1768
- options?: AgentRunOptions
1769
- ): Promise<AgentResponse> | AssistantMessageEventStream {
1946
+ run(prompt: string | ContentPart[], options: AgentRunOptions & { stream: true }): AssistantMessageEventStream;
1947
+ run(prompt: string | ContentPart[], options?: AgentRunOptions): Promise<AgentResponse>;
1948
+ run(prompt: string | ContentPart[], options?: AgentRunOptions): Promise<AgentResponse> | AssistantMessageEventStream {
1770
1949
  // Prompt validation throws synchronously in both run() and
1771
1950
  // stream() (same timing; async work starts only after validation).
1772
1951
  this.assertPromptValid(prompt);
@@ -1795,12 +1974,14 @@ export class Agent {
1795
1974
  // Mid-session: busy agent serializes via steer (same turn) or queue (next turn), or stops the active run and starts this prompt (interrupt).
1796
1975
  if (this.currentRun) {
1797
1976
  if (options?.enqueue === "interrupt") {
1798
- return new Promise<AgentResponse>((resolve, reject) => this.queueUrgent({ prompt, options, isStream: false, resolve, reject }));
1977
+ return new Promise<AgentResponse>((resolve, reject) =>
1978
+ this.queueUrgent({ prompt, options, isStream: false, resolve, reject })
1979
+ );
1799
1980
  }
1800
1981
  const enforced = this.midSessionConfig.mode;
1801
1982
  const requested = options?.enqueue;
1802
1983
  if (requested && (enforced === "steer" || enforced === "queue") && requested !== enforced) {
1803
- throw new Error(
1984
+ throw new ConfigError(
1804
1985
  `[Agent Accelerator] enqueue "${requested}" is disabled by midSession.mode "${enforced}". Use ${enforced}() or set mode to "auto"/"${requested}".`
1805
1986
  );
1806
1987
  }
@@ -1810,14 +1991,14 @@ export class Agent {
1810
1991
  return this.steerInto(this.currentRun, prompt, options);
1811
1992
  } else {
1812
1993
  if (this.midSessionConfig.mode === "steer") {
1813
- throw new Error(
1994
+ throw new ConfigError(
1814
1995
  '[Agent Accelerator] enqueue "queue" is disabled by midSession.mode "steer". Use steer() or set mode to "auto"/"queue".'
1815
1996
  );
1816
1997
  }
1817
1998
  this.assertPromptValid(prompt);
1818
1999
  const max = this.midSessionConfig.maxQueued ?? 20;
1819
2000
  if (this.pendingQueue.length >= max) {
1820
- throw new Error(
2001
+ throw new ValidationError(
1821
2002
  `[Agent Accelerator] Queue is full (${this.pendingQueue.length}/${max}). Wait for the active run to finish or increase midSession.maxQueued.`
1822
2003
  );
1823
2004
  }
@@ -1846,13 +2027,9 @@ export class Agent {
1846
2027
  prompt: string | ContentPart[],
1847
2028
  optionsOrStream?: boolean | AgentRunOptions
1848
2029
  ): Promise<AgentResponse> & AsyncIterable<string | StreamEvent> {
1849
- const isStream =
1850
- typeof optionsOrStream === "boolean"
1851
- ? optionsOrStream
1852
- : optionsOrStream?.stream === true;
2030
+ const isStream = typeof optionsOrStream === "boolean" ? optionsOrStream : optionsOrStream?.stream === true;
1853
2031
 
1854
- const runOpts: AgentRunOptions =
1855
- typeof optionsOrStream === "object" ? optionsOrStream : { stream: isStream };
2032
+ const runOpts: AgentRunOptions = typeof optionsOrStream === "object" ? optionsOrStream : { stream: isStream };
1856
2033
 
1857
2034
  if (isStream) {
1858
2035
  const stream = this.stream(prompt, runOpts);
@@ -1860,7 +2037,8 @@ export class Agent {
1860
2037
  if (runOpts.onDelta || runOpts.onThinkingDelta || runOpts.onEvent) {
1861
2038
  if (runOpts.onEvent) stream.on("*", runOpts.onEvent as any);
1862
2039
  if (runOpts.onDelta) stream.on("text_delta", (e: any) => runOpts.onDelta!(e.delta!, e));
1863
- if (runOpts.onThinkingDelta) stream.on("thinking_delta", (e: any) => runOpts.onThinkingDelta!(e.thinkingDelta!, e));
2040
+ if (runOpts.onThinkingDelta)
2041
+ stream.on("thinking_delta", (e: any) => runOpts.onThinkingDelta!(e.thinkingDelta!, e));
1864
2042
  }
1865
2043
  const stringStream: any = {
1866
2044
  [Symbol.asyncIterator]: async function* () {
@@ -1901,10 +2079,7 @@ export class Agent {
1901
2079
  * }
1902
2080
  * ```
1903
2081
  */
1904
- stream(
1905
- prompt: string | ContentPart[],
1906
- options?: AgentRunOptions
1907
- ): AssistantMessageEventStream {
2082
+ stream(prompt: string | ContentPart[], options?: AgentRunOptions): AssistantMessageEventStream {
1908
2083
  // Same sync validation timing as run().
1909
2084
  this.assertPromptValid(prompt);
1910
2085
  // Mid-session: busy agent serializes via steer (same turn) or queue (next turn).
@@ -1912,8 +2087,12 @@ export class Agent {
1912
2087
  const enforcedStream = this.midSessionConfig.mode;
1913
2088
  const urgent = options?.enqueue === "interrupt"; // stops the active run and goes first, in every mode and whatever the queue holds
1914
2089
  const requestedStream = options?.enqueue === "interrupt" ? undefined : options?.enqueue;
1915
- if (requestedStream && (enforcedStream === "steer" || enforcedStream === "queue") && requestedStream !== enforcedStream) {
1916
- throw new Error(
2090
+ if (
2091
+ requestedStream &&
2092
+ (enforcedStream === "steer" || enforcedStream === "queue") &&
2093
+ requestedStream !== enforcedStream
2094
+ ) {
2095
+ throw new ConfigError(
1917
2096
  `[Agent Accelerator] enqueue "${requestedStream}" is disabled by midSession.mode "${enforcedStream}". Use ${enforcedStream}() or set mode to "auto"/"${requestedStream}".`
1918
2097
  );
1919
2098
  }
@@ -1937,28 +2116,40 @@ export class Agent {
1937
2116
  activePromise.then(
1938
2117
  (res) => {
1939
2118
  try {
1940
- stub.push({ type: "done", delta: "", usage: res.usage, finishReason: res.finishReason, responseId: res.responseId } as any);
2119
+ stub.push({
2120
+ type: "done",
2121
+ delta: "",
2122
+ usage: res.usage,
2123
+ finishReason: res.finishReason,
2124
+ responseId: res.responseId,
2125
+ } as any);
2126
+ } catch {}
2127
+ try {
2128
+ stub.end(res);
1941
2129
  } catch {}
1942
- try { stub.end(res); } catch {}
1943
2130
  },
1944
2131
  (err) => {
1945
- try { stub.fail(err instanceof Error ? err : new Error(String(err))); } catch {}
2132
+ try {
2133
+ stub.fail(err instanceof Error ? err : new Error(String(err)));
2134
+ } catch {}
1946
2135
  }
1947
2136
  );
1948
2137
  } else {
1949
- try { stub.fail(new Error("[Agent Accelerator] No active run to steer.")); } catch {}
2138
+ try {
2139
+ stub.fail(new ValidationError("[Agent Accelerator] No active run to steer."));
2140
+ } catch {}
1950
2141
  }
1951
2142
  return stub;
1952
2143
  }
1953
2144
  if (!urgent && this.midSessionConfig.mode === "steer") {
1954
- throw new Error(
2145
+ throw new ConfigError(
1955
2146
  '[Agent Accelerator] enqueue "queue" is disabled by midSession.mode "steer". Use steer() or set mode to "auto"/"queue".'
1956
2147
  );
1957
2148
  }
1958
2149
  this.assertPromptValid(prompt);
1959
2150
  const max = this.midSessionConfig.maxQueued ?? 20;
1960
2151
  if (!urgent && this.pendingQueue.length >= max) {
1961
- throw new Error(
2152
+ throw new ValidationError(
1962
2153
  `[Agent Accelerator] Queue is full (${this.pendingQueue.length}/${max}). Wait for the active run to finish or increase midSession.maxQueued.`
1963
2154
  );
1964
2155
  }
@@ -2005,7 +2196,9 @@ export class Agent {
2005
2196
  if (idx >= 0) {
2006
2197
  this.pendingQueue.splice(idx, 1);
2007
2198
  queuedEntry.reject(Object.assign(new Error("Stream aborted"), { name: "AbortError" }));
2008
- try { deferred.fail(Object.assign(new Error("Stream aborted"), { name: "AbortError" })); } catch {}
2199
+ try {
2200
+ deferred.fail(Object.assign(new Error("Stream aborted"), { name: "AbortError" }));
2201
+ } catch {}
2009
2202
  }
2010
2203
  } catch {}
2011
2204
  });
@@ -2023,7 +2216,13 @@ export class Agent {
2023
2216
  // Otherwise (edge), end it here.
2024
2217
  try {
2025
2218
  if ((deferred as any).__piped !== true) {
2026
- deferred.push({ type: "done", delta: "", usage: res.usage, finishReason: res.finishReason, responseId: res.responseId } as any);
2219
+ deferred.push({
2220
+ type: "done",
2221
+ delta: "",
2222
+ usage: res.usage,
2223
+ finishReason: res.finishReason,
2224
+ responseId: res.responseId,
2225
+ } as any);
2027
2226
  deferred.end(res);
2028
2227
  }
2029
2228
  } catch {}
@@ -2045,10 +2244,7 @@ export class Agent {
2045
2244
  }
2046
2245
 
2047
2246
  // Attaches onEvent/onDelta/onThinkingDelta to a stream and, with wrapThinking, adds display-only <think> tags and whitespace hygiene on the outer events; in: stream()/pumpQueue, out: listeners on the stream.
2048
- private wireStreamCallbacks(
2049
- s: AssistantMessageEventStream,
2050
- options?: AgentRunOptions
2051
- ): void {
2247
+ private wireStreamCallbacks(s: AssistantMessageEventStream, options?: AgentRunOptions): void {
2052
2248
  // Wire one-liner callbacks so `stream:true` + onDelta is enough — no manual for-await needed
2053
2249
  if (options?.wrapThinking) {
2054
2250
  // Auto-wrap reasoning as <think>…</think> per-turn — clean boundaries across multi-turn agent runs.
@@ -2125,7 +2321,9 @@ export class Agent {
2125
2321
  const raw: string = e.delta ?? "";
2126
2322
  if (!justClosedThink) return raw;
2127
2323
  const stripped = raw.replace(/^\n+/, "");
2128
- try { e.delta = stripped; } catch {}
2324
+ try {
2325
+ e.delta = stripped;
2326
+ } catch {}
2129
2327
  if (!stripped) return "";
2130
2328
  justClosedThink = false;
2131
2329
  return stripped;
@@ -2222,11 +2420,13 @@ export class Agent {
2222
2420
  closeSynthetic();
2223
2421
  if (justClosedThink) {
2224
2422
  const stripped: string = (e.delta ?? "").replace(/^\n+/, "");
2225
- try { e.delta = stripped; } catch {}
2423
+ try {
2424
+ e.delta = stripped;
2425
+ } catch {}
2226
2426
  if (!stripped) return;
2227
2427
  justClosedThink = false;
2228
2428
  }
2229
- needThinkNewline = !((e.delta ?? "").endsWith("\n"));
2429
+ needThinkNewline = !(e.delta ?? "").endsWith("\n");
2230
2430
  });
2231
2431
  s.on("tool_call_start" as any, () => {
2232
2432
  closeSynthetic();
@@ -2304,7 +2504,12 @@ export function normalizeThinking(config: AgentConfig, fallbackLevel?: string):
2304
2504
  try {
2305
2505
  const rawModel: any = (config.model as any)?.model ? (config.model as any).model : config.model;
2306
2506
  const modelStr = typeof rawModel === "string" ? rawModel : rawModel?.id || "";
2307
- const provStr = typeof rawModel === "object" && rawModel?.provider ? rawModel.provider : modelStr.includes("/") ? modelStr.split("/")[0] : "";
2507
+ const provStr =
2508
+ typeof rawModel === "object" && rawModel?.provider
2509
+ ? rawModel.provider
2510
+ : modelStr.includes("/")
2511
+ ? modelStr.split("/")[0]
2512
+ : "";
2308
2513
  const actualModelId = modelStr.includes("/") ? modelStr.split("/").slice(1).join("/") : modelStr;
2309
2514
  if (actualModelId) {
2310
2515
  validateModelThinking(provStr || "openrouter", actualModelId, level);