agent-accelerator 0.5.3 → 0.5.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -121,11 +121,13 @@ An `Agent` holds:
121
121
  | `tools` | `Record<string, ToolDefinition> \| ToolDefinition[]` | Deterministic functions the model can call. See [Tools](#tools). |
122
122
  | `subagents` | `SubAgent[] \| Agent[]` | Pre-defined workers. Each worker becomes a callable tool. Useful for fixed roles such as researcher or critic. |
123
123
  | `subagentModel` | `string \| ModelSpec \| ModelProviderInstance` | Developer-only model for dynamically spawned workers. Never choosable by the Main Agent. Deprecated alias of `dynamicSubagents.model` (which wins). Falls back to `SUB_AGENT_MODEL`. |
124
- | `dynamicSubagents` | `DynamicSubagentsConfig` | Enables and constrains LLM-spawned stateless workers: `{ enabled, model?, maxSpawn?, thinkingLevel?, tools?, timeout?, allowScheduledActions? }`. See [Pre-defined vs Dynamic Delegation](#pre-defined-vs-dynamic-delegation) and [Scheduled actions](#scheduled-actions). |
124
+ | `dynamicSubagents` | `DynamicSubagentsConfig` | Enables and constrains LLM-spawned workers: `{ enabled, model?, maxSpawn?, maxDepth?, workerBudget?, thinkingLevel?, tools?, workerTimeoutMs?, allowScheduledActions? }` (`workerTimeoutMs` is for scheduled actions only). See [Pre-defined vs Dynamic Delegation](#pre-defined-vs-dynamic-delegation) and [Scheduled actions](#scheduled-actions). |
125
125
  | `thinking` | `ThinkingOption` | Canonical reasoning option: `none \| dynamic \| minimal \| low \| medium \| high \| xhigh \| max`, or `{ level, budgetTokens }`. Legacy `thinkingLevel` still works (canonical wins). Validated against the model catalog before a request. |
126
126
  | `cache` | `CacheOption` | `"implicit" \| "5m" \| "30m" \| "1h" \| "12h"`. Prompt-cache lifetime; unsupported values warn and fall back (see [Cache](#cache)). |
127
127
  | `serviceTier` | `"flex" \| "priority"` | Cost / priority routing where supported. Omit for standard routing. |
128
128
  | `maxTurns` | `number` | Maximum model → tool → model loops per `run`. Infinite by default (`0` or omitted = no limit). Hitting a finite limit with pending tool calls warns and sets `finishReason: "max_turns"`. |
129
+ | `limits` | `AgentRunLimits` | `{ maxTurns?, deadlineMs?, maxToolCallsPerRun?, maxTotalTokens?, requestTimeoutMs?, firstByteTimeoutMs?, streamIdleTimeoutMs? }`. The first four are unlimited by default. The three network timeouts default to 300 s / 120 s / 300 s so a silent connection fails instead of hanging; set one to `0` or `Infinity` to turn it off. A timeout raises a retryable `TimeoutError`. See [Safety and operations](docs/guides/safety-and-ops.md#network-timeouts). |
130
+ | `budget` | `Budget` | `{ maxCostUsd?, maxTotalTokens?, maxDurationMs?, maxTotalSubagents? }`, per run, workers included. Unlimited by default; exceeding it throws `BudgetExceededError` with partial usage. |
129
131
  | `sessionId` | `string` | Stable identifier used for cache affinity. Auto-generated when omitted. |
130
132
  | `headers` | `Record<string,string>` | Additional headers merged into every request. |
131
133
  | `apiKey` | `string` | Overrides environment-based API-key lookup for this agent. |
@@ -144,6 +146,11 @@ When `dynamicSubagents.enabled` is set without a resolvable worker model, constr
144
146
  await agent.run(prompt, opts?) // non-streaming, or streaming when opts.stream is true
145
147
  agent.ask(prompt, optsOrBool?) // deprecated (use run): string deltas when streaming, otherwise same as run
146
148
  agent.stream(prompt, opts?) // AssistantMessageEventStream
149
+ agent.steer(prompt) / agent.queue(prompt) // while busy: join the active run / start after it
150
+ agent.interrupt() // stop the active run now (provider request aborted, stream cancelled, tools get ctx.signal)
151
+ await agent.interrupt(message) // stop it AND run `message` next, ahead of queued follow-ups (stream: opts.enqueue = "interrupt")
152
+ agent.toConfig() // { model, instructions, tools }: the part an agent may edit, plain JSON (no keys)
153
+ Agent.fromConfig(editable, base) // new Agent: your full `base` config + an agent's edits of model/instructions/tools only
147
154
  agent.reset() // clears messages + signatures, keeps config
148
155
  agent.exportSession(telemetry?) // storable snapshot for saveSessionFile
149
156
  agent.importSession(saved) // restores messages + config from loadSessionFile
@@ -170,7 +177,8 @@ Use `ContentPart[]` when sending images, audio, or video alongside text.
170
177
  | `additionalContext` | Added only to the current user turn. Keeps the system prompt stable for better caching. |
171
178
  | `sessionId` | Per-run session override. |
172
179
  | `headers` | Per-run header merge. |
173
- | `signal` | `AbortSignal` used to cancel generation, tools, and subagents. |
180
+ | `signal` | `AbortSignal` used to cancel generation, tools, and subagents. The provider request is aborted and a stream's body is cancelled; see [what interrupting stops at the provider](docs/guides/safety-and-ops.md#what-interrupting-stops-at-the-provider). |
181
+ | `enqueue` | When the agent is busy: `"steer"` (join the active run), `"queue"` (run after it) or `"interrupt"` (stop it and run this prompt next, in every `midSession` mode). |
174
182
 
175
183
  ### Streaming
176
184
 
@@ -269,47 +277,60 @@ const agent = new Agent({
269
277
  instructions: "You are the Main Agent",
270
278
  dynamicSubagents: {
271
279
  enabled: true,
272
- maxSpawn: 4,
280
+ maxSpawn: 4, // optional cap on open workers at once (default: no limit)
273
281
  thinkingLevel: "low",
274
- tools: { get_weather, recent_news },
275
- timeout: 60000,
282
+ tools: { get_weather, recent_news }, // pool: the model grants a subset per worker
276
283
  },
277
284
  });
278
285
  ```
279
286
 
280
- A `spawn_subagents` tool is injected with the following task shape:
287
+ One `subagents` tool is injected. It takes `{ command, id?, name?, role?, instructions?, task?, tools?, history? }` and its manual is the tool description (`src/agent/subagents.md`, with the worker pool and the open-worker limit filled in; the system prompt stays untouched):
288
+
289
+ - `spawn` starts a worker (`name`, `task`; optional `instructions`, `role`, `tools`, `history`) and waits for its first reply. `message` sends `task` to an open worker `id`, which remembers the whole conversation. `close` frees the slot. `list` shows the open workers and the numbered finished turns that `history: N` can copy into a new worker.
290
+ - Results are plain text with one header line: `[worker researcher-1a2b3c4d] replied:` + the worker's text, or `[worker researcher-1a2b3c4d] failed: <reason>`. Several `spawn` calls in one model turn run in parallel. A bad request comes back as an `Error: ...` line the model can fix.
291
+ - `maxSpawn` caps **open** workers (the model must close one to open another); by default there is no cap. `budget.maxTotalSubagents` still counts spawns per run. Every budget and limit is off until you set it, except three network timeouts (see the `limits` row).
292
+ - `tools` is the developer-owned pool. Workers get zero tools unless the Main Agent grants a subset by name; unknown names are rejected with the pool listed.
293
+ - The Main Agent cannot choose worker models, reasoning levels or timeouts. The `subagents` tool has no timeouts (`workerTimeoutMs` and an action's `timeoutMs` apply only to scheduled actions).
294
+ - `maxDepth` caps how many levels of workers may exist below the agent. By default there is no cap (a worker can start workers that start workers, as deep as the model goes); set a whole number >= 1 to limit it: `1` keeps workers flat, `3` allows three levels. A worker with levels left gets its own `subagents` tool, and its children can only be granted the tools **it** was granted. The levels are saved inside each other (`<session>/workers/<id>/workers/<id>/…`).
295
+ - `workerBudget: { maxCostUsd?, maxTotalTokens? }` is each worker's own spending cap, applied as that worker's per-run `budget` (a spawn, a message or a scheduled run is one run; the usage of the worker's own workers counts toward it) and passed down to its workers. Going over fails that call with a budget error (`[worker <id>] failed: Token budget exceeded…`), leaves the worker open and does not affect the parent. Default: unlimited; models with unknown pricing count $0 toward `maxCostUsd`. Values must be numbers above 0.
296
+ - Workers are real agents with their own session: see [Persistent workers](#persistent-workers-agentworkers) below (the same functions in code, saved on disk with `persist`, resumed after a restart). Each reply's usage is metered into the parent's usage and budget.
297
+ - Each run of a worker gets a system-generated 32-char TrackingID, displayed as `NAME-{TrackingID}`. Thinking/text deltas update the worker trace live (`agent.track(id)`, `subscribeToSubAgent`) and surface on the parent stream as `subagent_delta` events.
281
298
 
282
299
  ```ts
283
- {
284
- tasks: {
285
- name,
286
- role?,
287
- instructions,
288
- task,
289
- tools?: string[],
290
- timeoutMs?: number
291
- }[]
292
- }
300
+ const res = await agent.run("Audit auth pipeline and write a threat model");
301
+
302
+ console.log(res.subagents.map((s) => s.name));
293
303
  ```
294
304
 
295
- - `maxSpawn` — max workers per call. Extras are trimmed safely, and the limit is stated in the tool description plus the Main Agent's system prompt so the model knows it.
296
- - `tools` — developer-owned pool. Workers get zero tools unless the Main Agent grants a per-task `tools` subset by name (unknown names are ignored), keeping worker context small.
297
- - `timeout` (ms) — `0` = no limit, `-1` = the Main Agent sets a per-task `timeoutMs`, `>0` = fixed limit for every worker. Timed-out workers report an error entry; the rest of the batch still completes.
298
- - Workers are stateless: one task in, one result out, then shut down. No history, no recursion. The Main Agent cannot choose worker models or reasoning levels.
299
- - Worker names use UPPER-KEBAB (`HBM-PRICING-ANALYST`). Each worker gets a system-generated 32-char TrackingID, displayed as `NAME-{TrackingID}` (e.g. `HBM-PRICING-ANALYST-9f2c…`).
300
- - Workers stream internally: thinking/text deltas update the worker trace live (`agent.track(id)`, `subscribeToSubAgent`) and surface on the parent stream as `subagent_delta` events.
305
+ ### Persistent workers (`agent.workers`)
306
+
307
+ Besides one-shot delegation, an agent can run **workers that keep their conversation**: start one, message it again and again, close it. A worker is a plain `Agent` with its own instructions and tools, on the developer's worker model (`dynamicSubagents.model` or `SUB_AGENT_MODEL`). With `persist: { dir }` on the parent it is saved under `<parent session folder>/workers/<id>/` (own journal, tool logs and `worker.json`), so it is rebuilt with its history after a restart.
301
308
 
302
309
  ```ts
303
- const res = await agent.run("Audit auth pipeline and write a threat model");
310
+ const reply = await lead.workers.spawn({
311
+ name: "Researcher",
312
+ instructions: "You research databases and cite sources.", // the worker's OWN prompt; the parent's is never copied
313
+ task: "Compare SQLite and Postgres for a small SaaS.",
314
+ tools: ["recent_news"], // subset of dynamicSubagents.tools; unknown names throw
315
+ history: 1, // optional: start from a copy of the parent's first 1 finished turns (see lead.workers.turns()); default "none"
316
+ });
317
+ console.log(reply.id, reply.text); // e.g. "researcher-1a2b3c4d"
304
318
 
305
- console.log(res.subagents.map((s) => s.name));
319
+ const next = await lead.workers.message(reply.id, "Now focus on write-heavy loads."); // same worker, same conversation
320
+ lead.workers.list(); // open workers: { id, name, role?, tools, busy }
321
+ lead.workers.close(reply.id); // can no longer be messaged; its session stays on disk
306
322
  ```
307
323
 
324
+ - A turn is one **finished** run (`done` or ended by `ctx.stop`); the running turn and failed or aborted runs are never copied, so a worker never gets a prompt without its answer.
325
+ - `spawn` and `message` wait for the reply and never throw for a failing worker: the `WorkerReply` then carries `error` and a text that says so. They throw `ValidationError` for a bad request (unknown tool or id, closed or busy worker, too many open workers: `dynamicSubagents.maxSpawn` is the open-worker limit, `budget.maxTotalSubagents` still counts spawns per run).
326
+ - A worker handles one call at a time. Pass `{ signal }` to abort its run; the worker stays open.
327
+ - Exactly once across a crash: pass `{ callId }` (the calling tool call's id). A replayed `spawn` finds the worker it already started, a cut-off run is continued with `resume()` instead of sending the text twice, and a call that had already been answered returns that saved answer.
328
+ - Without `persist` the workers live in memory only. A damaged `worker.json` is skipped with a `worker.invalid` event.
329
+ - The reply's `metadata` (usage, steps, lineage) has the same shape as `AgentResponse.subagents`; `agent.track(metadata.trackingId)` shows the run live.
330
+
308
331
  ### Delegation Helpers
309
332
 
310
333
  * `buildAgentTools(list)` — wraps multiple agents and deduplicates names. Used internally by `subagents`.
311
- * `createSubagentSpawnTool(parentAgent)` — builds the dynamic subagent spawner. Rarely needed directly.
312
- * `DynamicSubagentTask` — `{ name, role?, instructions, task, tools?, timeoutMs? }`, the shape produced for dynamic delegation. No `model` — workers run on the developer-configured model.
313
334
 
314
335
  ---
315
336
  ## Scheduled actions
@@ -328,7 +349,7 @@ const agent = new Agent({
328
349
  });
329
350
  ```
330
351
 
331
- The Main Agent then gets two tools, fixed by config so the prompt cache stays stable: `scheduled_actions` (one tool, `command` = `create | list | get | update | kill | run_now | skip`) and `get_datetime` (the machine's local date and time, also exported as `getDatetimeTool`). The tool description is exactly [`src/scheduler/scheduled-actions.md`](src/scheduler/scheduled-actions.md) (the worker pool names are filled in), so you can read what the model is told.
352
+ The Main Agent then gets two tools, fixed by config so the prompt cache stays stable: `scheduled_actions` (one tool, `command` = `create | list | get | update | delete | run_now | skip`) and `get_datetime` (the machine's local date and time, also exported as `getDatetimeTool`). The tool description is exactly [`src/scheduler/scheduled-actions.md`](src/scheduler/scheduled-actions.md) (the worker pool names are filled in), so you can read what the model is told.
332
353
 
333
354
  The same code is available to your program:
334
355
 
@@ -347,6 +368,7 @@ createScheduledAction(agent, {
347
368
  startCookingInAdvance: "00:00:30", // optional, a "DD:HH:MM" duration (default 10 minutes)
348
369
  repeatEvery: "01:00:00", // optional; omit to run once
349
370
  retries: 1, // optional; used only while time remains before delivery
371
+ onMissed: "run", // optional: "skip" | "run" if your program was down at delivery time (omit to decide later)
350
372
  },
351
373
  });
352
374
 
@@ -362,17 +384,19 @@ telemetryBus.onEvent((e) => {
362
384
  | `updateScheduledAction(id, patch, { home? })` | Change instructions, task, tools, timeout, role or any schedule field. Schedule changes are refused while it is cooking. |
363
385
  | `runScheduledAction(id, { home? })` | Run now, outside the schedule (also resolves a missed run). Needs the creating `Agent` alive. |
364
386
  | `skipScheduledAction(id, { home? })` | Ignore a missed run and wait for the next scheduled time. |
365
- | `killScheduledAction(id, { force?, purge?, home? })` | Delete for good. While it is cooking nothing changes unless `force: true`; `purge: true` also deletes past run files. |
387
+ | `deleteScheduledAction(id, { force?, purge?, home? })` | Delete for good. While it is cooking nothing changes unless `force: true`; `purge: true` also deletes past run files. |
366
388
 
367
389
  **How it behaves**
368
390
 
369
391
  * **One local clock.** A single timer per home folder (guarded by a one-process lock, `<home>/.lock`) ticks once a second against the machine's own time and only asks what is overdue, so drift or sleep cannot skip a run. There is no timezone or daylight-saving logic. The timer is `unref`'d and runs only while something is pending, so actions run only while your process is alive.
370
392
  * **The creating `Agent` runs it.** The task stores the Agent's `name`; each run goes to the live Agent with that name (latest wins), using its worker model and tool pool. The scheduler holds only a weak reference, so keep your `Agent` referenced; if none is alive, the run counts as missed.
371
393
  * **Each run is fresh.** A new stateless dynamic sub-agent built from the saved fields, on your fixed worker model and tool pool, never touching the Main Agent's session. It inherits the parent's limits, retry policy, `fileAccess` and `captureRaw`.
372
- * **Files are the truth.** Everything lives in `<home>/scheduled_actions/`: `tasks.json` (every action), `deliveries.json` (saved results waiting for their delivery time) and one folder per action with one `.md` file per run (a small header, then the result). The file is re-read and validated before each run, so editing `instructions` or `task` there takes effect on the next run. API keys, the model and tool code are never stored. Don't put secrets in `instructions`.
373
- * **Ready, then delivered.** A finished run is saved immediately (`scheduled_action.ready`); at `timeToDeliver` the SDK emits `scheduled_action.delivered` on `telemetryBus` with `{ id, name, status, file, readyAt, deliverAt, late }`. There is no notification system: what happens next is your code. Other events: `.created`, `.updated`, `.started`, `.failed`, `.missed`, `.skipped`, `.killed`, plus the warnings `.invalid` and `.store_corrupt`.
394
+ * **Files are the truth.** Everything lives in `<home>/scheduled_actions/` (the format is a stable contract with a changelog: [docs/scheduled-action-files.md](docs/scheduled-action-files.md)): `tasks.json` (every action), `deliveries.json` (saved results waiting for their delivery time) and one folder per action with one `.md` file per run (a small header, then the result). The file is re-read and validated before each run, so editing `instructions` or `task` there takes effect on the next run. API keys, the model and tool code are never stored. Don't put secrets in `instructions`.
395
+ * **What a run costs.** Each run file's header records the run's exact spending, the same numbers `usageReport()` keeps for workers: `turns`, `tokens` (input + output), `inputTokens`, `freshInputTokens`, `cacheReadTokens`, `cacheWriteTokens`, `outputTokens`, `thinkingTokens`, `cacheHitRate` (0 to 1), `costUsd`, `costSource` (`provider`, `catalog`, `mixed` or `unknown`) and `unpricedTurns` (only when above 0, then `costUsd` is a lower bound). Unknown pricing is `costUsd: unknown`, never 0. The numbers cover every attempt of the run (`retries` included) and are recorded for failed runs too. The report itself does not list scheduled runs.
396
+ * **Time limit of a run.** An action may carry `timeoutMs` (top level of the action, not inside `scheduled_action`): a run that takes longer is stopped and recorded as failed. By default there is no limit. `dynamicSubagents.workerTimeoutMs` is an optional ceiling: an action cannot ask for more, and it is the limit for actions that set none (`0` and `Infinity` mean no ceiling). The former `dynamicSubagents.timeout`, `modelMaySetTimeout` and the `-1` mode were removed; passing them throws a `ConfigError` that says what to use.
397
+ * **Ready, then delivered.** A finished run is saved immediately (`scheduled_action.ready`); at `timeToDeliver` the SDK emits `scheduled_action.delivered` on `telemetryBus` with `{ id, name, status, file, readyAt, deliverAt, late }`. There is no notification system: what happens next is your code. Other events: `.created`, `.updated`, `.started`, `.failed`, `.missed`, `.skipped`, `.deleted`, plus the warnings `.invalid` and `.store_corrupt`.
374
398
  * **Overlap and failure.** A run still cooking when the next one is due is skipped, never stacked. A failed run still writes a file saying why and is delivered like any result (`status: "failed"`).
375
- * **Missed runs.** If the process was down when a run was due, the SDK emits `scheduled_action.missed` once and runs nothing by itself: pick `run_now` or `skip`. A result saved before the process exited is simply delivered (marked `late`) on the first tick after start.
399
+ * **Missed runs.** If the process was down when a run was due, the SDK emits `scheduled_action.missed` once. By default it runs nothing by itself and the action waits as `missed`: pick `run_now` or `skip`. Set `scheduled_action.onMissed` per action to decide in advance: `"skip"` moves on to the next time (a one-shot is `done`; a `skipped` event follows), `"run"` runs it as soon as the program is back (delivered when it finishes, marked `late`). The same applies to a run a crash cut off. An action whose creating Agent is gone stays `missed` whatever `onMissed` says (nothing can run it). A result saved before the process exited is simply delivered (marked `late`) on the first tick after start. `late` means the result was delivered more than two ticks after its time, finished after its delivery second, or replaced a missed run. A `run_now` result is delivered when it finishes: it is `late` only when it replaces a missed run (a `run_now` on a `missed` action, or `onMissed: "run"`), never for an ordinary early run.
376
400
 
377
401
  ---
378
402
  ## Tools
@@ -426,11 +450,11 @@ const pay = tool({
426
450
 
427
451
  `ctx.stop` throws, so the code after it never runs (even if you forget `return`). The reason is saved as that call's result, calls that were running in parallel in the same batch still run to completion (none is skipped or aborted), and then the run ends with `finishReason: "stopped"` and **no further model turn**. The agent waits for the next user message; the model then sees the reason as the tool's result. A steer typed while the batch ran becomes the next run. The stop is saved with the call, so a crash cannot turn a "no" into a "yes": after a restart the run is closed as stopped without calling the model. A tool's `timeoutMs` counts the time spent waiting for your check, so leave it off for tools that ask a person.
428
452
 
429
- To guard a built-in tool such as `spawn_subagents` or `scheduled_actions`, wrap it on the agent after construction:
453
+ To guard a built-in tool such as `subagents` or `scheduled_actions`, wrap it on the agent after construction:
430
454
 
431
455
  ```ts
432
- const inner = agent.tools["spawn_subagents"]!;
433
- agent.tools["spawn_subagents"] = {
456
+ const inner = agent.tools["subagents"]!;
457
+ agent.tools["subagents"] = {
434
458
  ...inner,
435
459
  execute: async (input, ctx) => ((await getPermission("Spawn workers?")) ? inner.execute(input, ctx) : ctx.stop("spawning was not approved")),
436
460
  };
@@ -1011,9 +1035,32 @@ TokenUsage {
1011
1035
  }
1012
1036
  ```
1013
1037
 
1014
- Cost calculation prefers provider-reported totals. When unavailable, costs are computed using catalog pricing.
1038
+ Cost calculation prefers provider-reported totals. When unavailable, costs are computed using catalog pricing, per model turn: fresh input at the input price, cache reads at the cache-read price, cache writes at the cache-write price (both are part of `inputTokens`, so they are not billed twice), output at the output price (thinking tokens are part of output), scaled by `serviceTier` (`flex` x0.5, `priority` x2, estimates). A catalog entry without a cache price bills those tokens at the input rate (cache is never free); unknown pricing stays unknown.
1015
1039
 
1016
1040
  Subagent usage is rolled into the parent totals.
1041
+
1042
+ ### Usage report (`agent.usageReport()`)
1043
+
1044
+ `res.usage` is the cost of one run, workers included. `agent.usageReport()` is the other view: what the agent and **each** worker spent over their lives, itemized, with nothing counted twice.
1045
+
1046
+ ```ts
1047
+ const report = agent.usageReport();
1048
+ report.agent; // this agent's OWN model calls: id, name, model, turns, tokens, cacheHitRate, cost
1049
+ report.workers; // every worker below it (open, closed, nested in `workers`, and `status: "fixed"` for `subagents: [...]`)
1050
+ report.total; // agent + all workers
1051
+ // each line, e.g. report.workers[0]:
1052
+ // { id: "researcher-1a2b3c4d", status: "open", model: "google/gemini-3.5-flash-lite", turns: 2,
1053
+ // tokens: { input: 2000, freshInput: 600, cacheRead: 1200, cacheWrite: 200, output: 400, thinking: 100, total: 2400 },
1054
+ // cacheHitRate: 0.6,
1055
+ // cost: { source: "catalog", unpricedTurns: 0, totalUsd: 0.00891,
1056
+ // parts: { inputUsd: 0.0018, outputUsd: 0.006, cacheReadUsd: 0.00036, cacheWriteUsd: 0.00075 } } }
1057
+ ```
1058
+
1059
+ - **Own turns only.** Every agent records the model calls it made itself, priced **per turn** in integer micro-USD, and saves them in its session (`usage`), so the numbers survive a restart (load the session and the totals carry on; workers are rebuilt from their folders). A parent's `res.usage` already contains its workers' usage, which is why the report never adds responses together: `report.total.tokens.total` equals what the responses show.
1060
+ - **Cache-aware.** `input` includes the cache reads and writes (`freshInput = input - cacheRead - cacheWrite`); `output` includes `thinking`. Fresh input, cache reads and cache writes are each priced at their own catalog price, and `serviceTier` is applied. `cacheHitRate` is `cacheRead / input` (0 to 1, `null` when nothing was sent).
1061
+ - **Honest costs.** `cost.source` is `provider` (the provider reported the cost, which is exact), `catalog` (estimated from the model catalog), `mixed`, or `unknown`. Unknown pricing is `totalUsd: null`, never 0; `unpricedTurns` above 0 means the total is a lower bound. For turns whose cost the provider reported only the total is exact, so `parts` can add up to less than `totalUsd`.
1062
+ - **Not in the report:** the one-shot workers of scheduled actions (their exact cost is in each run file's header), and workers started inside a fixed `subagents` worker's per-call copy (their cost is in the response usage). `reset()` does not clear the totals: they are spending, not conversation.
1063
+
1017
1064
  ### ProviderRawData
1018
1065
 
1019
1066
  ```ts
@@ -1288,7 +1335,7 @@ bun run examples/04-sub-agents.ts
1288
1335
  # fixed researcher + critic pipeline
1289
1336
 
1290
1337
  bun run examples/03-multi_agent.ts
1291
- # dynamic spawn_subagents demo
1338
+ # dynamic subagents tool demo
1292
1339
 
1293
1340
  bun run examples/01-metadata.ts
1294
1341
  # usage + raw inspection
@@ -1312,7 +1359,7 @@ bun run examples/11-session-identity.ts
1312
1359
  # sub-agent TrackingIDs, live worker boxes, lineage, track(), durable sessions
1313
1360
 
1314
1361
  bun run examples/12-mid-session.ts
1315
- # steer, queue and stream while a turn is running
1362
+ # steer, queue and stream while a turn is running (interrupt() stops it; interrupt(message) stops it and runs the message)
1316
1363
 
1317
1364
  bun run examples/13-structured-output.ts
1318
1365
  # JSON Schema structured outputs on every provider
@@ -1,34 +1,28 @@
1
- You are an Agent — you evaluate user objectives and decide whether to solve them directly or delegate to specialized parallel sub-agents.
1
+ You are an Agent — you evaluate user objectives and decide whether to solve them directly or delegate to specialized parallel workers with the `subagents` tool.
2
2
 
3
3
  ### Workflow & Decision Matrix
4
4
  1. **Triage**:
5
5
  - Single-focus, factual, code-generation, or conversational prompts → Answer directly, concisely, and completely.
6
- - Do NOT spawn sub-agents for trivial, single-step tasks.
6
+ - Do NOT start workers for trivial, single-step tasks.
7
7
  2. **Delegate**:
8
- - Complex research, multi-angle analysis, deep technical trade-offs, or parallel explorations → Invoke `spawn_subagents` with 2 to 4 focused sub-agents in a single batched call.
9
- - For each sub-agent define:
10
- - `name`: `UPPER_SNAKE_CASE` identifier describing domain focus (e.g., `MARKET_ANALYST`, `SYSTEMS_ARCHITECT`).
8
+ - Complex research, multi-angle analysis, deep technical trade-offs, or parallel explorations → call `subagents` with `command: "spawn"` once per worker, 2 to 4 focused workers, all in the same turn so they run in parallel.
9
+ - For each worker define:
10
+ - `name`: short label describing its domain focus (e.g. `market-analyst`, `systems-architect`).
11
11
  - `role`: Distinct specialized persona.
12
12
  - `instructions`: Self-contained, expert prompt detailing scope, methodologies, and constraints.
13
13
  - `task`: Focused, non-overlapping research query or execution directive.
14
14
  3. **Aggregate**:
15
- - Sub-agent findings arrive concurrently wrapped in XML:
16
- ```xml
17
- <SUB-AGENTS-RESPONSE>
18
- <DYNAMIC_ROLE_1>
19
- Findings from sub-agent 1
20
- </DYNAMIC_ROLE_1>
21
- <DYNAMIC_ROLE_2>
22
- Findings from sub-agent 2
23
- </DYNAMIC_ROLE_2>
24
- </SUB-AGENTS-RESPONSE>
15
+ - Each worker answers with a header line and its findings:
25
16
  ```
26
- - Node tags match each sub-agent's `name` in `UPPER_SNAKE_CASE`.
17
+ [worker market-analyst-1a2b3c4d] replied:
18
+ Findings from the worker
19
+ ```
20
+ - Use `command: "message"` with the worker's `id` to ask a follow-up; the worker remembers the conversation. `command: "close"` when you no longer need it.
27
21
  4. **Synthesize**:
28
- - Deliver an editorial-grade, cohesive report that integrates all sub-agent insights.
22
+ - Deliver an editorial-grade, cohesive report that integrates all worker insights.
29
23
  - Reconcile contradictions, de-duplicate redundant information, and highlight critical trade-offs.
30
- - Structure with executive summaries, comparative Markdown tables, and concrete strategic conclusions. Never present an uncurated list of sub-agent summaries.
24
+ - Structure with executive summaries, comparative Markdown tables, and concrete strategic conclusions. Never present an uncurated list of worker summaries.
31
25
 
32
26
  ### Execution Rules
33
- - **Tool Protocol**: Invoke `spawn_subagents` directly without conversational preambles. Ensure all sub-agents are passed in a single tool call array.
34
- - **Context Isolation**: Sub-agents run in isolated contexts; provide complete background in their `instructions` and `task`.
27
+ - **Tool Protocol**: Call `subagents` directly without conversational preambles. Start parallel workers with several `spawn` calls in one turn.
28
+ - **Context Isolation**: Workers start blank; provide complete background in their `instructions` and `task`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-accelerator",
3
- "version": "0.5.3",
3
+ "version": "0.5.5",
4
4
  "description": "High-performance SDK for AI agents with a unified API across AI providers. Optimized for high cache hit rates, observability, and seamless agent orchestration. Built to power production-grade agentic systems and harnesses for teams at any scale.",
5
5
  "license": "MIT",
6
6
  "author": "Sashvat Bharat",