agent-accelerator 0.5.8 → 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 +26 -5
  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 +374 -167
  7. package/src/agent/architecture.md +14 -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 +449 -373
  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 +58 -28
  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 +46 -49
  23. package/src/providers/architecture.md +10 -8
  24. package/src/providers/google.ts +150 -62
  25. package/src/providers/openai-compat.ts +60 -58
  26. package/src/providers/openai.ts +57 -39
  27. package/src/providers/openrouter.ts +87 -32
  28. package/src/providers/registry.ts +12 -26
  29. package/src/providers/shared.ts +89 -39
  30. package/src/providers.ts +56 -115
  31. package/src/scheduler/architecture.md +9 -8
  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 +50 -44
  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 +17 -31
  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
@@ -2,20 +2,21 @@
2
2
 
3
3
  The agent and everything that runs under it.
4
4
 
5
- | File | What it is | Used by |
6
- | :-- | :-- | :-- |
7
- | `agent.ts` | `Agent`: `run`/`stream`/`ask`, `steer`/`queue`/`interrupt`, persistence hooks, `resume()`/`interrupted()`, `usageReport()`, `fork()`, `toConfig()`/`fromConfig()`, the `workers` getter. | callers, workers, scheduler |
8
- | `loop.ts` | `runAgentLoop` / `streamAgentLoop`: per turn budget, pairing repair, file access, provider call, tool execution, repeat guard. Also `ensureToolPairing`, `accumulateUsage`. | `Agent` |
9
- | `context.ts` | `AgentContext`: the validated message list with checkpoint/rollback. | `Agent`, loops |
10
- | `delegation.ts` | Fixed workers (`agentToTool`, `buildAgentTools`), the live worker trace store, session-id helpers, budget helpers, `openWorkerTrace`/`workerMetadata`. | `Agent`, `workers.ts` |
11
- | `worker.ts` | Worker core: model/tool normalization, `buildDynamicWorkerConfig` (shared preset), `createLiveWorker`. | `Agent`, registry, scheduler |
12
- | `workers.ts` | `WorkerRegistry` (`agent.workers`): persistent multi-turn workers. | `Agent`, `subagents` tool |
13
- | `subagents-tool.ts` + `subagents.md` | The one model-facing `subagents` tool; the `.md` is its description. | `Agent` constructor |
14
- | `subagent.ts` | `SubAgent` class and `agentFromEnv` (thin, kept for compatibility). | callers |
15
- | `turns.ts` | Pure grouping of messages into finished turns (history cut for workers). | `workers.ts` |
16
- | `config-blocks.ts` | RSI blocks: only `model`, `instructions`, `tools` are editable; `allowedModels` check. | `Agent` |
5
+ | File | What it is | Used by |
6
+ | :----------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------- |
7
+ | `agent.ts` | `Agent`: `run`/`stream`/`ask`, `steer`/`queue`/`interrupt`, persistence hooks, `resume()`/`interrupted()`, `usageReport()`, `fork()`, `toConfig()`/`fromConfig()`, the `workers` getter. | callers, workers, scheduler |
8
+ | `loop.ts` | `runAgentLoop` / `streamAgentLoop`: per turn budget, pairing repair, file access, provider call, tool execution, repeat guard. Also `ensureToolPairing`, `accumulateUsage`. | `Agent` |
9
+ | `context.ts` | `AgentContext`: the validated message list with checkpoint/rollback. | `Agent`, loops |
10
+ | `delegation.ts` | Fixed workers (`agentToTool`, `buildAgentTools`), the live worker trace store, session-id helpers, budget helpers, `openWorkerTrace`/`workerMetadata`. | `Agent`, `workers.ts` |
11
+ | `worker.ts` | Worker core: model/tool normalization, `buildDynamicWorkerConfig` (shared preset), `createLiveWorker`. | `Agent`, registry, scheduler |
12
+ | `workers.ts` | `WorkerRegistry` (`agent.workers`): persistent multi-turn workers. | `Agent`, `subagents` tool |
13
+ | `subagents-tool.ts` + `subagents.md` | The one model-facing `subagents` tool; the `.md` is its description. | `Agent` constructor |
14
+ | `subagent.ts` | `SubAgent` class and `agentFromEnv` (thin, kept for compatibility). | callers |
15
+ | `turns.ts` | Pure grouping of messages into finished turns (history cut for workers). | `workers.ts` |
16
+ | `config-blocks.ts` | RSI blocks: only `model`, `instructions`, `tools` are editable; `allowedModels` check. | `Agent` |
17
17
 
18
18
  ## Invariants
19
+
19
20
  - `runAgentLoop` and `streamAgentLoop` duplicate their turn logic: every loop change goes into both.
20
21
  - An `Agent` runs one thing at a time; delegation forks a copy per call so parallel calls never queue.
21
22
  - A worker is a plain `Agent` with a parent link and presets. Never duplicate logic between Agent and worker.
@@ -23,3 +24,4 @@ The agent and everything that runs under it.
23
24
  - Exactly-once worker calls rest on the call key (parent run mark id + tool call id) and `meta.last`.
24
25
  - Only results branded by `makeSubagentResult` have their usage metadata trusted.
25
26
  - Each Agent records only its own model turns; never sum `AgentResponse.usage` for totals.
27
+ - `reset()` clears the conversation (messages, run marks, the run's saved tool results) but never the spending totals.
@@ -22,25 +22,37 @@ export function modelKey(model: unknown): string | undefined {
22
22
  export function assertModelAllowed(allowed: unknown, model: string | undefined): void {
23
23
  if (allowed === undefined) return;
24
24
  if (!Array.isArray(allowed) || allowed.length === 0 || allowed.some((m) => typeof m !== "string" || !m.trim())) {
25
- throw new ConfigError(`[Agent Accelerator] Invalid allowedModels ${JSON.stringify(allowed)}: expected a non-empty list of "provider/model" strings, or omit it to allow any model.`);
25
+ throw new ConfigError(
26
+ `[Agent Accelerator] Invalid allowedModels ${JSON.stringify(allowed)}: expected a non-empty list of "provider/model" strings, or omit it to allow any model.`
27
+ );
26
28
  }
27
29
  if (!model || !allowed.includes(model)) {
28
- throw new ConfigError(`[Agent Accelerator] Model ${JSON.stringify(model ?? null)} is not in allowedModels. Allowed: ${allowed.map((m) => JSON.stringify(m)).join(", ")}.`);
30
+ throw new ConfigError(
31
+ `[Agent Accelerator] Model ${JSON.stringify(model ?? null)} is not in allowedModels. Allowed: ${allowed.map((m) => JSON.stringify(m)).join(", ")}.`
32
+ );
29
33
  }
30
34
  }
31
35
 
32
36
  // The editable part of an agent as plain JSON; in: the Agent's model, instructions and the developer's tool list (not the built-in delegation/scheduler tools), out: Agent.toConfig.
33
- export function toEditableConfig(model: unknown, instructions: string, tools: AgentConfig["tools"]): Required<EditableAgentConfig> {
37
+ export function toEditableConfig(
38
+ model: unknown,
39
+ instructions: string,
40
+ tools: AgentConfig["tools"]
41
+ ): Required<EditableAgentConfig> {
34
42
  return { model: modelKey(model) ?? "", instructions, tools: Object.keys(normalizeToolMap(tools)) };
35
43
  }
36
44
 
37
45
  // Merges what an agent edited into the developer's config: only model/instructions/tools can change; tools are names picked from base.tools; the model is checked against allowedModels when the Agent is built; in: Agent.fromConfig args, out: the AgentConfig for `new Agent`.
38
46
  export function applyEditable(editable: EditableAgentConfig, base: AgentConfig): AgentConfig {
39
47
  if (!editable || typeof editable !== "object" || Array.isArray(editable)) {
40
- throw new ConfigError(`[Agent Accelerator] Invalid editable config ${JSON.stringify(editable)}: expected an object like { model, instructions, tools } (see Agent.toConfig()).`);
48
+ throw new ConfigError(
49
+ `[Agent Accelerator] Invalid editable config ${JSON.stringify(editable)}: expected an object like { model, instructions, tools } (see Agent.toConfig()).`
50
+ );
41
51
  }
42
52
  const e = editable as Record<string, unknown>;
43
- const ignored = Object.keys(e).filter((k) => !(EDITABLE_FIELDS as readonly string[]).includes(k) && e[k] !== undefined);
53
+ const ignored = Object.keys(e).filter(
54
+ (k) => !(EDITABLE_FIELDS as readonly string[]).includes(k) && e[k] !== undefined
55
+ );
44
56
  if (ignored.length > 0) {
45
57
  telemetryBus.emitWarning(
46
58
  "config.not_editable",
@@ -51,26 +63,41 @@ export function applyEditable(editable: EditableAgentConfig, base: AgentConfig):
51
63
  const out: AgentConfig = { ...base };
52
64
  if (e.model !== undefined) {
53
65
  const next = typeof e.model === "string" ? e.model.trim() : "";
54
- if (!next) throw new ConfigError(`[Agent Accelerator] Invalid editable model ${JSON.stringify(e.model)}: expected a "provider/model" string.`);
66
+ if (!next)
67
+ throw new ConfigError(
68
+ `[Agent Accelerator] Invalid editable model ${JSON.stringify(e.model)}: expected a "provider/model" string.`
69
+ );
55
70
  // A model helper's key and base URL belong to its own provider: they are never sent to another one.
56
71
  const prev = base.model as { model?: unknown; apiKey?: string; baseUrl?: string } | string | undefined;
57
72
  const sameProvider = next.includes("/") && next.split("/")[0] === modelKey(prev)?.split("/")[0];
58
- out.model = prev && typeof prev === "object" && typeof prev.model === "string"
59
- ? { ...prev, model: next, ...(sameProvider ? {} : { apiKey: undefined, baseUrl: undefined }) }
60
- : next;
73
+ out.model =
74
+ prev && typeof prev === "object" && typeof prev.model === "string"
75
+ ? { ...prev, model: next, ...(sameProvider ? {} : { apiKey: undefined, baseUrl: undefined }) }
76
+ : next;
61
77
  }
62
78
  if (e.instructions !== undefined) {
63
- if (typeof e.instructions !== "string") throw new ConfigError(`[Agent Accelerator] Invalid editable instructions: expected a string, got ${typeof e.instructions}.`);
79
+ if (typeof e.instructions !== "string")
80
+ throw new ConfigError(
81
+ `[Agent Accelerator] Invalid editable instructions: expected a string, got ${typeof e.instructions}.`
82
+ );
64
83
  out.instructions = e.instructions;
65
84
  }
66
85
  if (e.tools !== undefined) {
67
86
  if (!Array.isArray(e.tools) || e.tools.some((n) => typeof n !== "string")) {
68
- throw new ConfigError(`[Agent Accelerator] Invalid editable tools ${JSON.stringify(e.tools)}: expected a list of tool names from the developer's tool pool.`);
87
+ throw new ConfigError(
88
+ `[Agent Accelerator] Invalid editable tools ${JSON.stringify(e.tools)}: expected a list of tool names from the developer's tool pool.`
89
+ );
69
90
  }
70
91
  const pool = normalizeToolMap(base.tools);
71
92
  const unknown = (e.tools as string[]).filter((n) => !Object.hasOwn(pool, n));
72
93
  if (unknown.length > 0) {
73
- throw new ConfigError(`[Agent Accelerator] Unknown tool ${unknown.map((n) => JSON.stringify(n)).join(", ")} in the editable config. Available: ${Object.keys(pool).map((n) => JSON.stringify(n)).join(", ") || "none"}.`);
94
+ throw new ConfigError(
95
+ `[Agent Accelerator] Unknown tool ${unknown.map((n) => JSON.stringify(n)).join(", ")} in the editable config. Available: ${
96
+ Object.keys(pool)
97
+ .map((n) => JSON.stringify(n))
98
+ .join(", ") || "none"
99
+ }.`
100
+ );
74
101
  }
75
102
  const picked: Record<string, ToolDefinition> = {};
76
103
  for (const n of e.tools as string[]) picked[n] = pool[n]!;
@@ -3,12 +3,13 @@ import type { ToolCallRecord, ToolResultRecord } from "../types/tool.ts";
3
3
  import { safeStringify } from "../utils/serialization.ts";
4
4
  import { clearSessionRouting } from "../providers.ts";
5
5
  import { clearInteractionChains } from "../providers/google.ts";
6
+ import { ValidationError } from "../types/errors.ts";
6
7
 
7
8
  const USER_PART_TYPES = new Set(["text", "image", "audio", "video", "file"]);
8
9
 
9
10
  // Throws the AgentContext validation error with the SDK prefix; in: the add* validators, out: a thrown Error.
10
11
  function fail(message: string): never {
11
- throw new Error(`[Agent Accelerator] AgentContext: ${message}`);
12
+ throw new ValidationError(`[Agent Accelerator] AgentContext: ${message}`);
12
13
  }
13
14
 
14
15
  /** Deep clone that preserves Uint8Array/ArrayBuffer media (structuredClone may be absent). */
@@ -192,8 +193,12 @@ export class AgentContext {
192
193
  if (Array.isArray(cp?.messages)) this.messages = deepCloneValue(cp.messages);
193
194
  if (Array.isArray(cp?.signatures)) this.thoughtSignatures = [...cp.signatures];
194
195
  if (sessionId) {
195
- try { clearInteractionChains(sessionId); } catch {}
196
- try { clearSessionRouting(sessionId); } catch {}
196
+ try {
197
+ clearInteractionChains(sessionId);
198
+ } catch {}
199
+ try {
200
+ clearSessionRouting(sessionId);
201
+ } catch {}
197
202
  }
198
203
  }
199
204
  }
@@ -61,7 +61,7 @@ export function createFixedChildSessionId(parentId: string, name: string): strin
61
61
  return `${parentId.slice(0, maxParent - 9)}-${parentHash}${suffix}`;
62
62
  }
63
63
  if (maxParent > 0) return parentId.slice(0, maxParent) + suffix;
64
- return (`${parentId}-sub-${name}`).slice(0, 64);
64
+ return `${parentId}-sub-${name}`.slice(0, 64);
65
65
  }
66
66
 
67
67
  /**
@@ -225,7 +225,9 @@ function notifyTrace(trackingId: string): void {
225
225
  if (!listeners || listeners.size === 0) return;
226
226
  const snap = snapshotTrace(t);
227
227
  for (const fn of [...listeners]) {
228
- try { fn(snap); } catch {}
228
+ try {
229
+ fn(snap);
230
+ } catch {}
229
231
  }
230
232
  }
231
233
 
@@ -280,15 +282,23 @@ export function subscribeToSubAgent(id: string, fn: (trace: SubAgentTrace) => vo
280
282
  }
281
283
  set.add(fn);
282
284
  const timer = setTimeout(() => {
283
- try { subagentListeners.get(key)?.delete(fn); } catch {}
285
+ try {
286
+ subagentListeners.get(key)?.delete(fn);
287
+ } catch {}
284
288
  }, SUBAGENT_SUBSCRIBE_TTL_MS);
285
- try { (timer as unknown as { unref?: () => void }).unref?.(); } catch {}
289
+ try {
290
+ (timer as unknown as { unref?: () => void }).unref?.();
291
+ } catch {}
286
292
  let done = false;
287
293
  return () => {
288
294
  if (done) return;
289
295
  done = true;
290
- try { clearTimeout(timer); } catch {}
291
- try { subagentListeners.get(key)?.delete(fn); } catch {}
296
+ try {
297
+ clearTimeout(timer);
298
+ } catch {}
299
+ try {
300
+ subagentListeners.get(key)?.delete(fn);
301
+ } catch {}
292
302
  };
293
303
  }
294
304
 
@@ -334,7 +344,13 @@ export function openWorkerTrace(a: {
334
344
  role?: string;
335
345
  /** When given, the trace is also kept in this agent's own registry and saved with its session. */
336
346
  parent?: Agent;
337
- emit?: (event: { trackingId: string; delta?: string; thinkingDelta?: string; partialText?: string; partialThinking?: string }) => void;
347
+ emit?: (event: {
348
+ trackingId: string;
349
+ delta?: string;
350
+ thinkingDelta?: string;
351
+ partialText?: string;
352
+ partialThinking?: string;
353
+ }) => void;
338
354
  }): WorkerTraceHandle {
339
355
  const trackingId = createTrackingId();
340
356
  const displayId = `${a.name}-${trackingId}`;
@@ -360,9 +376,19 @@ export function openWorkerTrace(a: {
360
376
  trim: (t) => trimTraceSteps(t),
361
377
  touch: (id) => touchTrace(id),
362
378
  notify: (id) => notifyTrace(id),
363
- ...(a.parent ? { persist: () => { try { a.parent!.persistNow(); } catch {} } } : {}),
379
+ ...(a.parent
380
+ ? {
381
+ persist: () => {
382
+ try {
383
+ a.parent!.persistNow();
384
+ } catch {}
385
+ },
386
+ }
387
+ : {}),
364
388
  emit: (ev) => {
365
- try { a.emit?.(ev); } catch {}
389
+ try {
390
+ a.emit?.(ev);
391
+ } catch {}
366
392
  },
367
393
  });
368
394
  const settle = (): void => {
@@ -386,7 +412,11 @@ export function openWorkerTrace(a: {
386
412
  t.turns = result.turns ?? t.turns;
387
413
  t.text = result.text;
388
414
  if (result.usage) {
389
- t.usage = { inputTokens: result.usage.inputTokens ?? 0, outputTokens: result.usage.outputTokens ?? 0, totalTokens: result.usage.totalTokens ?? 0 };
415
+ t.usage = {
416
+ inputTokens: result.usage.inputTokens ?? 0,
417
+ outputTokens: result.usage.outputTokens ?? 0,
418
+ totalTokens: result.usage.totalTokens ?? 0,
419
+ };
390
420
  }
391
421
  }
392
422
  settle();
@@ -492,7 +522,8 @@ function ensureBudgetState(parentAgent: Agent): BudgetState {
492
522
  function usageTokens(usage: unknown): number {
493
523
  const u = usage as { totalTokens?: unknown; inputTokens?: unknown; outputTokens?: unknown } | undefined;
494
524
  if (!u || typeof u !== "object") return 0;
495
- if (typeof u.totalTokens === "number" && Number.isFinite(u.totalTokens)) return Math.max(0, Math.floor(u.totalTokens));
525
+ if (typeof u.totalTokens === "number" && Number.isFinite(u.totalTokens))
526
+ return Math.max(0, Math.floor(u.totalTokens));
496
527
  const inp = typeof u.inputTokens === "number" && Number.isFinite(u.inputTokens) ? u.inputTokens : 0;
497
528
  const out = typeof u.outputTokens === "number" && Number.isFinite(u.outputTokens) ? u.outputTokens : 0;
498
529
  return Math.max(0, Math.floor(inp + out));
@@ -532,10 +563,9 @@ export function checkBudget(
532
563
  );
533
564
  }
534
565
  if (budget.maxTotalTokens !== undefined && totalTokens > budget.maxTotalTokens) {
535
- throw new BudgetExceededError(
536
- `Token budget exceeded: ${totalTokens} > maxTotalTokens ${budget.maxTotalTokens}.`,
537
- { context: { partial } }
538
- );
566
+ throw new BudgetExceededError(`Token budget exceeded: ${totalTokens} > maxTotalTokens ${budget.maxTotalTokens}.`, {
567
+ context: { partial },
568
+ });
539
569
  }
540
570
  if (budget.maxCostUsd !== undefined && totalCostUsd > budget.maxCostUsd) {
541
571
  throw new BudgetExceededError(
@@ -601,9 +631,7 @@ export interface AgentAsToolTarget {
601
631
  *
602
632
  * @example `const researchTool = researcher.asTool("research");`
603
633
  */
604
- export function agentToTool(
605
- input: Agent | AgentAsToolTarget
606
- ): ToolDefinition {
634
+ export function agentToTool(input: Agent | AgentAsToolTarget): ToolDefinition {
607
635
  const agentInstance = "agent" in input ? input.agent : input;
608
636
  const rawName =
609
637
  ("name" in input && (input as any).name) ||
@@ -635,28 +663,55 @@ export function agentToTool(
635
663
  task,
636
664
  emit: (ev) => ctx?.onSubagentEvent?.(ev),
637
665
  });
638
- const lineage = { name, displayId: live.displayId, sessionId: subSessionId, parentSessionId, task, runId: callRunId, turnId: callTurnId };
666
+ const lineage = {
667
+ name,
668
+ displayId: live.displayId,
669
+ sessionId: subSessionId,
670
+ parentSessionId,
671
+ task,
672
+ runId: callRunId,
673
+ turnId: callTurnId,
674
+ };
639
675
  try {
640
676
  // One isolated copy per call: the shared instance is single-flight, so
641
677
  // calling it directly would serialize (or steer) parallel tool calls.
642
678
  const worker: Agent =
643
- typeof (agentInstance as Agent).fork === "function" ? agentInstance.fork(subSessionId, parentSessionId) : agentInstance;
644
- const workerStream = worker.stream(task, { signal: ctx?.signal, sessionId: subSessionId, onTurn: live.onWorkerTurn } as any);
645
- const detachWorkerListeners = live.attachToStream(workerStream as unknown as Parameters<typeof live.attachToStream>[0]);
679
+ typeof (agentInstance as Agent).fork === "function"
680
+ ? agentInstance.fork(subSessionId, parentSessionId)
681
+ : agentInstance;
682
+ const workerStream = worker.stream(task, {
683
+ signal: ctx?.signal,
684
+ sessionId: subSessionId,
685
+ onTurn: live.onWorkerTurn,
686
+ } as any);
687
+ const detachWorkerListeners = live.attachToStream(
688
+ workerStream as unknown as Parameters<typeof live.attachToStream>[0]
689
+ );
646
690
  let response: AgentResponse;
647
691
  try {
648
692
  response = await workerStream.result();
649
693
  } finally {
650
- try { detachWorkerListeners(); } catch {}
694
+ try {
695
+ detachWorkerListeners();
696
+ } catch {}
651
697
  // The per-call session id is never reused: drop its provider chain/routing state.
652
698
  if (worker !== agentInstance) {
653
- try { agentInstance.absorbUsage(worker.usageTotals); } catch {} // the template keeps what its copies spent
654
- try { worker.reset(); } catch {}
699
+ try {
700
+ agentInstance.absorbUsage(worker.usageTotals);
701
+ } catch {} // the template keeps what its copies spent
702
+ try {
703
+ worker.reset();
704
+ } catch {}
655
705
  }
656
706
  }
657
707
  live.finish(response);
658
708
  return makeSubagentResult(response.text, [
659
- workerMetadata({ ...lineage, durationMs: Math.max(0, nowMs() - startTime), result: response, steps: live.steps() }),
709
+ workerMetadata({
710
+ ...lineage,
711
+ durationMs: Math.max(0, nowMs() - startTime),
712
+ result: response,
713
+ steps: live.steps(),
714
+ }),
660
715
  ]);
661
716
  } catch (err: any) {
662
717
  const msg = err?.message || String(err);
@@ -674,9 +729,7 @@ export function agentToTool(
674
729
  *
675
730
  * @example `const tools = buildAgentTools([researcher, reviewer]);`
676
731
  */
677
- export function buildAgentTools(
678
- agents?: (Agent | AgentAsToolTarget)[]
679
- ): Record<string, ToolDefinition> {
732
+ export function buildAgentTools(agents?: (Agent | AgentAsToolTarget)[]): Record<string, ToolDefinition> {
680
733
  if (!agents || agents.length === 0) return {};
681
734
 
682
735
  const tools: Record<string, ToolDefinition> = {};