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.
@@ -0,0 +1,201 @@
1
+ /**
2
+ * What one agent spent on ITS OWN model calls, kept exactly: tokens as the provider reported them, cost in integer
3
+ * micro-USD priced per turn (so cache hits, cache writes and price tiers are right), plus where each turn's price came
4
+ * from. Every agent records its own turns and saves them in its session; `usageReport()` then adds the agent and its
5
+ * workers up WITHOUT counting anything twice (a parent's `AgentResponse.usage` already contains its workers' usage, so
6
+ * responses are never summed). Pure functions, no Agent import.
7
+ */
8
+ import type { TokenUsage } from "../types/core.ts";
9
+ import type { PricedCost } from "./store.ts";
10
+
11
+ /** Raw totals of one agent's own model calls, as saved in its session (`usage`). */
12
+ export interface UsageTotals {
13
+ /** Model calls made. */
14
+ turns: number;
15
+ /** Input tokens; they INCLUDE the cache reads and writes. */
16
+ inputTokens: number;
17
+ cacheReadTokens: number;
18
+ cacheWriteTokens: number;
19
+ /** Output tokens; they INCLUDE the thinking tokens. */
20
+ outputTokens: number;
21
+ thinkingTokens: number;
22
+ /** Integer micro-USD, summed over the turns that had a price. */
23
+ costMicroUSD: { input: number; output: number; cacheRead: number; cacheWrite: number; total: number };
24
+ /** Turns by where their price came from: the provider's reported cost, the catalog, or nowhere (unknown pricing). */
25
+ turnsBy: { provider: number; catalog: number; unpriced: number };
26
+ }
27
+
28
+ /** A zeroed {@link UsageTotals}. */
29
+ export function emptyUsage(): UsageTotals {
30
+ return {
31
+ turns: 0,
32
+ inputTokens: 0,
33
+ cacheReadTokens: 0,
34
+ cacheWriteTokens: 0,
35
+ outputTokens: 0,
36
+ thinkingTokens: 0,
37
+ costMicroUSD: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
38
+ turnsBy: { provider: 0, catalog: 0, unpriced: 0 },
39
+ };
40
+ }
41
+
42
+ const count = (n: unknown): number => (typeof n === "number" && Number.isFinite(n) && n > 0 ? Math.floor(n) : 0);
43
+
44
+ /** A model reference (string or catalog spec) as a plain string for a report line. */
45
+ export function modelName(model: unknown): string {
46
+ return typeof model === "string" ? model : String((model as { id?: unknown } | null | undefined)?.id ?? "unknown");
47
+ }
48
+
49
+ /** Adds one model call (`priced` from `priceUsage`; undefined = unknown pricing, which is counted but never turned into $0). */
50
+ export function addTurn(totals: UsageTotals, usage: TokenUsage, priced: PricedCost | undefined): void {
51
+ const input = count(usage.inputTokens);
52
+ const output = count(usage.outputTokens);
53
+ totals.turns += 1;
54
+ totals.inputTokens += input;
55
+ totals.outputTokens += output;
56
+ totals.cacheReadTokens += Math.min(count(usage.cachedTokens ?? usage.cacheReadTokens), input);
57
+ totals.cacheWriteTokens += Math.min(count(usage.cacheWriteTokens), input);
58
+ totals.thinkingTokens += Math.min(count(usage.thinkingTokens), output);
59
+ if (!priced) {
60
+ totals.turnsBy.unpriced += 1;
61
+ return;
62
+ }
63
+ totals.turnsBy[priced.source === "provider" ? "provider" : "catalog"] += 1;
64
+ const b = priced.breakdown;
65
+ totals.costMicroUSD.input += b.inputMicroUSD;
66
+ totals.costMicroUSD.output += b.outputMicroUSD + b.audioMicroUSD;
67
+ totals.costMicroUSD.cacheRead += b.cacheReadMicroUSD;
68
+ totals.costMicroUSD.cacheWrite += b.cacheWriteMicroUSD;
69
+ totals.costMicroUSD.total += b.totalMicroUSD;
70
+ }
71
+
72
+ /** Adds `other` into `into` (workers into a total). */
73
+ export function mergeUsage(into: UsageTotals, other: UsageTotals): void {
74
+ for (const k of ["turns", "inputTokens", "cacheReadTokens", "cacheWriteTokens", "outputTokens", "thinkingTokens"] as const) into[k] += other[k];
75
+ for (const k of ["input", "output", "cacheRead", "cacheWrite", "total"] as const) into.costMicroUSD[k] += other.costMicroUSD[k];
76
+ for (const k of ["provider", "catalog", "unpriced"] as const) into.turnsBy[k] += other.turnsBy[k];
77
+ }
78
+
79
+ /** Reads saved totals back defensively (a hand-edited or older file never breaks a load); unusable input gives zeros. */
80
+ export function readUsage(saved: unknown): UsageTotals {
81
+ const out = emptyUsage();
82
+ const s = saved as Partial<UsageTotals> | null | undefined;
83
+ if (!s || typeof s !== "object") return out;
84
+ for (const k of ["turns", "inputTokens", "cacheReadTokens", "cacheWriteTokens", "outputTokens", "thinkingTokens"] as const) out[k] = count(s[k]);
85
+ for (const k of ["input", "output", "cacheRead", "cacheWrite", "total"] as const) out.costMicroUSD[k] = count(s.costMicroUSD?.[k]);
86
+ for (const k of ["provider", "catalog", "unpriced"] as const) out.turnsBy[k] = count(s.turnsBy?.[k]);
87
+ return out;
88
+ }
89
+
90
+ /** Where a total's price came from: `provider` (the provider's reported cost), `catalog`, `mixed` (both), or `unknown` (no turn had a price). */
91
+ export type CostSource = "provider" | "catalog" | "mixed" | "unknown";
92
+
93
+ /** One agent's (or the whole session's) spending in a form made to be read: tokens, cache hit rate, cost. */
94
+ export interface UsageLine {
95
+ /** Model calls made. */
96
+ turns: number;
97
+ tokens: {
98
+ /** Everything sent: `freshInput + cacheRead + cacheWrite`. */
99
+ input: number;
100
+ /** Input that was neither read from nor written to the cache. */
101
+ freshInput: number;
102
+ cacheRead: number;
103
+ cacheWrite: number;
104
+ /** Everything generated, thinking included. */
105
+ output: number;
106
+ /** The part of `output` that was thinking. */
107
+ thinking: number;
108
+ total: number;
109
+ };
110
+ /** `cacheRead / input` from 0 to 1; `null` when nothing was sent. */
111
+ cacheHitRate: number | null;
112
+ cost: {
113
+ source: CostSource;
114
+ /** Turns with no known price: when above 0 the totals are a lower bound. */
115
+ unpricedTurns: number;
116
+ /** USD; `null` when no turn had a price (unknown is never shown as 0). */
117
+ totalUsd: number | null;
118
+ /** The parts, in USD. For turns whose cost the provider reported, only the total is exact: the parts then add up to less than `totalUsd`. Absent when unknown. */
119
+ parts?: { inputUsd: number; outputUsd: number; cacheReadUsd: number; cacheWriteUsd: number };
120
+ };
121
+ }
122
+
123
+ /** Turns raw totals into a readable {@link UsageLine}. */
124
+ export function usageLine(t: UsageTotals): UsageLine {
125
+ const priced = t.turnsBy.provider + t.turnsBy.catalog;
126
+ const source: CostSource = priced === 0 ? "unknown" : t.turnsBy.provider > 0 && t.turnsBy.catalog > 0 ? "mixed" : t.turnsBy.provider > 0 ? "provider" : "catalog";
127
+ const usd = (micro: number): number => micro / 1_000_000;
128
+ const c = t.costMicroUSD;
129
+ return {
130
+ turns: t.turns,
131
+ tokens: {
132
+ input: t.inputTokens,
133
+ freshInput: Math.max(0, t.inputTokens - t.cacheReadTokens - t.cacheWriteTokens),
134
+ cacheRead: t.cacheReadTokens,
135
+ cacheWrite: t.cacheWriteTokens,
136
+ output: t.outputTokens,
137
+ thinking: t.thinkingTokens,
138
+ total: t.inputTokens + t.outputTokens,
139
+ },
140
+ cacheHitRate: t.inputTokens > 0 ? t.cacheReadTokens / t.inputTokens : null,
141
+ cost: {
142
+ source,
143
+ unpricedTurns: t.turnsBy.unpriced,
144
+ totalUsd: priced === 0 ? null : usd(c.total),
145
+ ...(priced === 0 ? {} : { parts: { inputUsd: usd(c.input), outputUsd: usd(c.output), cacheReadUsd: usd(c.cacheRead), cacheWriteUsd: usd(c.cacheWrite) } }),
146
+ },
147
+ };
148
+ }
149
+
150
+ /** One worker in the usage tree, with raw totals (the report turns them into lines). */
151
+ export interface UsageNode {
152
+ id: string;
153
+ name: string;
154
+ role?: string;
155
+ model: string;
156
+ status: "open" | "closed" | "fixed";
157
+ totals: UsageTotals;
158
+ workers: UsageNode[];
159
+ }
160
+
161
+ /** An agent or worker line of a {@link UsageReport}. */
162
+ export interface AgentUsage extends UsageLine {
163
+ id: string;
164
+ name: string;
165
+ model: string;
166
+ }
167
+
168
+ /** A worker line, with the workers it started (`open` and `closed` are `subagents`/`agent.workers` workers, `fixed` is a `subagents: [...]` worker). */
169
+ export interface WorkerUsage extends AgentUsage {
170
+ role?: string;
171
+ status: "open" | "closed" | "fixed";
172
+ workers: WorkerUsage[];
173
+ }
174
+
175
+ /** What `agent.usageReport()` returns. `agent` is the agent's own model calls only; `total` is the agent plus every worker below it. */
176
+ export interface UsageReport {
177
+ agent: AgentUsage;
178
+ workers: WorkerUsage[];
179
+ total: UsageLine;
180
+ }
181
+
182
+ // Adds a node and everything below it into `into`; in: a usage tree, out: the whole-session total.
183
+ function sumTree(into: UsageTotals, nodes: UsageNode[]): void {
184
+ for (const n of nodes) {
185
+ mergeUsage(into, n.totals);
186
+ sumTree(into, n.workers);
187
+ }
188
+ }
189
+
190
+ // One node as a report line, children included; in: a usage node, out: UsageReport.workers.
191
+ function nodeLine(n: UsageNode): WorkerUsage {
192
+ return { id: n.id, name: n.name, ...(n.role ? { role: n.role } : {}), model: n.model, status: n.status, ...usageLine(n.totals), workers: n.workers.map(nodeLine) };
193
+ }
194
+
195
+ /** Builds the report from the agent's own totals and its worker tree. */
196
+ export function buildUsageReport(own: { id: string; name: string; model: string; totals: UsageTotals }, workers: UsageNode[]): UsageReport {
197
+ const total = emptyUsage();
198
+ mergeUsage(total, own.totals);
199
+ sumTree(total, workers);
200
+ return { agent: { id: own.id, name: own.name, model: own.model, ...usageLine(own.totals) }, workers: workers.map(nodeLine), total: usageLine(total) };
201
+ }
@@ -14,6 +14,8 @@ export interface EventStreamOptions {
14
14
  includePartial?: boolean;
15
15
  /** Max buffered events when no iterator is attached. Default 1000. Oldest dropped. */
16
16
  maxBufferedEvents?: number;
17
+ /** `false`: events go to listeners and waiting iterators only, never into the buffer (for a caller that only wants `result()`). Default true. */
18
+ buffer?: boolean;
17
19
  /** Max approx bytes for a single delta event. Default 1MB. Oversized dropped. */
18
20
  maxEventBytes?: number;
19
21
  }
@@ -61,6 +63,7 @@ export class AssistantMessageEventStream implements AsyncIterable<StreamEvent> {
61
63
  private resolveFinalResult!: (result: AgentResponse) => void;
62
64
  private rejectFinalResult!: (err: Error) => void;
63
65
  private readonly includePartial: boolean;
66
+ private readonly buffered: boolean;
64
67
  private readonly maxBufferedEvents: number;
65
68
  private readonly maxEventBytes: number;
66
69
  /** First listener exception captured for inspection (Q-29/Q-41). */
@@ -80,6 +83,7 @@ export class AssistantMessageEventStream implements AsyncIterable<StreamEvent> {
80
83
  */
81
84
  constructor(opts?: EventStreamOptions) {
82
85
  this.includePartial = opts?.includePartial !== false;
86
+ this.buffered = opts?.buffer !== false;
83
87
  this.maxBufferedEvents =
84
88
  typeof opts?.maxBufferedEvents === "number" && Number.isFinite(opts.maxBufferedEvents) && opts.maxBufferedEvents > 0
85
89
  ? Math.floor(opts.maxBufferedEvents)
@@ -151,8 +155,9 @@ export class AssistantMessageEventStream implements AsyncIterable<StreamEvent> {
151
155
  } else {
152
156
  // No waiter right now (typical between `next()` polls of a burst):
153
157
  // buffer for the iterator with a hard cap (Q-29). Overflow drops the
154
- // oldest with an anomaly report — see `enqueueBounded`.
155
- this.enqueueBounded(out);
158
+ // oldest with an anomaly report — see `enqueueBounded`. A stream built
159
+ // with `buffer: false` has no iterator to feed and keeps nothing.
160
+ if (this.buffered) this.enqueueBounded(out);
156
161
  }
157
162
  }
158
163
 
@@ -422,6 +422,21 @@ function waitForRetry(ms: number, signal?: AbortSignal): Promise<void> {
422
422
  });
423
423
  }
424
424
 
425
+ // Settles with `work`, or rejects "Tool execution aborted" the moment `signal` aborts, without waiting for a tool that never looks at its signal (it finishes in the background, its result is dropped); in: executeAttempt, out: the attempt's result or error.
426
+ function raceAbort<T>(work: Promise<T>, signal: AbortSignal | undefined): Promise<T> {
427
+ if (!signal) return work;
428
+ return new Promise<T>((resolve, reject) => {
429
+ const onAbort = () => reject(new Error("Tool execution aborted"));
430
+ if (signal.aborted) {
431
+ work.catch(() => {}); // the abandoned tool may still fail later: nobody listens
432
+ onAbort();
433
+ return;
434
+ }
435
+ signal.addEventListener("abort", onAbort, { once: true });
436
+ work.then(resolve, reject).finally(() => signal.removeEventListener("abort", onAbort));
437
+ });
438
+ }
439
+
425
440
  async function executeAttempt(
426
441
  toolDef: ToolDefinition,
427
442
  parsedInput: unknown,
@@ -429,7 +444,7 @@ async function executeAttempt(
429
444
  timeoutMs: number | undefined
430
445
  ): Promise<unknown> {
431
446
  if (!timeoutMs || timeoutMs <= 0) {
432
- return toolDef.execute(parsedInput as any, context);
447
+ return raceAbort(Promise.resolve(toolDef.execute(parsedInput as any, context)), context.signal);
433
448
  }
434
449
 
435
450
  const controller = new AbortController();
@@ -453,7 +468,7 @@ async function executeAttempt(
453
468
  }, timeoutMs);
454
469
  });
455
470
  const result = await Promise.race([
456
- Promise.resolve(toolDef.execute(parsedInput as any, executionContext)),
471
+ raceAbort(Promise.resolve(toolDef.execute(parsedInput as any, executionContext)), context.signal),
457
472
  timeout,
458
473
  ]);
459
474
  // A resolved promise can beat a timer callback even when the event loop
@@ -2,7 +2,7 @@ import type { SubAgentExecutionMetadata } from "../types/response.ts";
2
2
 
3
3
  /**
4
4
  * Module-private brand proving a tool result was produced by the SDK's own
5
- * delegation tools (`agentToTool` / `spawn_subagents`). Symbols cannot be
5
+ * delegation tools (`agentToTool` / the `subagents` tool). Symbols cannot be
6
6
  * forged by JSON a tool fetched from the web, so only branded results get
7
7
  * their `_subagentMetadata` trusted (usage/cost rolled up, XML shown to the
8
8
  * model).
package/src/tools/tool.ts CHANGED
@@ -61,22 +61,6 @@ export function resolveWhenBusy(
61
61
  return v === "steer" || v === "queue" ? v : "auto";
62
62
  }
63
63
 
64
- /**
65
- * Maps the Q-52 canonical worker-timeout knobs to the legacy `timeout` value
66
- * (`0` = no limit, `-1` = model sets per-task `timeoutMs`, `>0` = fixed ms).
67
- * An explicit `timeout`/`workerTimeoutMs` wins over `modelMaySetTimeout`.
68
- *
69
- * @example `normalizeWorkerTimeout({ modelMaySetTimeout: true }); // -1`
70
- */
71
- export function normalizeWorkerTimeout(
72
- config?: { timeout?: number; workerTimeoutMs?: number; modelMaySetTimeout?: boolean }
73
- ): number {
74
- const explicit = config?.workerTimeoutMs ?? config?.timeout;
75
- if (Number.isFinite(explicit as number)) return Math.floor(explicit as number);
76
- if (config?.modelMaySetTimeout === true) return -1;
77
- return 0;
78
- }
79
-
80
64
  /**
81
65
  * Normalizes the Q-52 `maxAttempts` alias to a `maxTries` value.
82
66
  * `maxTries` wins when both are set.
@@ -121,7 +121,8 @@ export interface AgentConfig {
121
121
  * - `{ mode: "steer" }`: only steer (inject into current turn) is allowed.
122
122
  * - `{ mode: "queue" }`: only queue (next-turn follow-up) is allowed.
123
123
  * - `{ mode: "auto" }` (default): caller picks per message via `enqueue` or `steer()`/`queue()`.
124
- * `interrupt` (cancel stream now) is always available via `interrupt()`/`cancel()`/`AbortSignal`.
124
+ * `interrupt` (stop the active run now, optionally start a new message) is always available, whatever the mode:
125
+ * `interrupt()`, `interrupt(message)`, `enqueue: "interrupt"`, `cancel()` and `AbortSignal`.
125
126
  */
126
127
  midSession?: MidSessionConfig;
127
128
  /**
@@ -227,6 +228,25 @@ export interface AgentConfig {
227
228
  budget?: Budget;
228
229
  /** Run + network limits (Q-22). Turn/budget caps unlimited by default; network timeouts finite. */
229
230
  limits?: AgentRunLimits;
231
+ /**
232
+ * The only models this agent may use, as `provider/model` strings compared as written. Unset (default) = any model.
233
+ * Checked when the Agent is built (also for a config an agent edited through {@link Agent.fromConfig}); a model
234
+ * outside the list throws a `ConfigError`. Workers and `subagentModel` are not affected.
235
+ */
236
+ allowedModels?: string[];
237
+ }
238
+
239
+ /**
240
+ * The part of an agent that an agent may edit (self-improvement): see {@link Agent.toConfig} and {@link Agent.fromConfig}.
241
+ * Plain JSON. Every other setting (limits, budget, cache, thinking, keys, ...) stays with the developer.
242
+ */
243
+ export interface EditableAgentConfig {
244
+ /** `provider/model`; must be in `allowedModels` when the developer set it. */
245
+ model?: string;
246
+ /** System instructions. */
247
+ instructions?: string;
248
+ /** Names of tools, picked from the developer's tool pool (`base.tools` of `fromConfig`). */
249
+ tools?: string[];
230
250
  }
231
251
 
232
252
  /** A run that a crash cut off (see {@link Agent.interrupted}). */
@@ -246,6 +266,7 @@ export interface AgentRunLimits {
246
266
  deadlineMs?: number;
247
267
  maxTotalTokens?: number;
248
268
  maxToolCallsPerRun?: number;
269
+ /** Network timeouts in ms. Defaults 300 000 / 120 000 / 300 000. `0` or `Infinity` turns one off. */
249
270
  requestTimeoutMs?: number;
250
271
  firstByteTimeoutMs?: number;
251
272
  streamIdleTimeoutMs?: number;
@@ -278,7 +299,7 @@ export interface MidSessionConfig {
278
299
 
279
300
  /** Developer-configured policy for LLM-spawned dynamic sub-agents. */
280
301
  export interface DynamicSubagentsConfig {
281
- /** Enables the automatic `spawn_subagents` tool. Defaults to true when the object is present. */
302
+ /** Adds the `subagents` tool (spawn, message, close, list persistent workers). Defaults to true when the object is present; `agent.workers` works either way. */
282
303
  enabled?: boolean;
283
304
  /**
284
305
  * Lets the Main Agent schedule dynamic sub-agents to run later (once or on
@@ -288,26 +309,31 @@ export interface DynamicSubagentsConfig {
288
309
  allowScheduledActions?: boolean;
289
310
  /** Developer-only worker model override. Never choosable by the Main Agent LLM. Falls back to top-level `subagentModel`, then `SUB_AGENT_MODEL` env, then the parent model. */
290
311
  model?: string | ModelSpec | ModelProviderInstance;
291
- /** Max workers the Main Agent may spawn in a single `spawn_subagents` call. Extras are trimmed safely. Defaults to 4. */
312
+ /** Max workers that may be open at once on this agent (the model must close one before spawning another). Default: no limit; set a whole number >= 1 to cap it. */
292
313
  maxSpawn?: number;
293
314
  /** Fixed reasoning level for all dynamic workers. The Main Agent cannot override it. Falls back to the parent level when omitted. */
294
315
  thinkingLevel?: ThinkingLevel;
316
+ /**
317
+ * Spending cap of EACH worker (the `subagents` tool, `agent.workers` and scheduled actions), set as that worker's own
318
+ * per-run `budget`: a spawn, a message or a scheduled run is one run, and the usage of the worker's own workers counts
319
+ * toward it. Exceeding it fails that call with a budget error and leaves the worker open. Default: unlimited. Models
320
+ * with unknown pricing count $0 toward `maxCostUsd`. Passed down to the workers' own workers.
321
+ */
322
+ workerBudget?: Pick<Budget, "maxCostUsd" | "maxTotalTokens">;
323
+ /**
324
+ * How many levels of workers may exist below this agent. Default: no limit. `1` = workers cannot start workers of their
325
+ * own; `3` = a worker may start workers that start workers. A worker with levels left gets its own `subagents` tool, and
326
+ * its children can only be granted the tools the worker itself was granted. A whole number >= 1, or omit it for no limit.
327
+ */
328
+ maxDepth?: number;
295
329
  /** Tool pool available to dynamic workers. Workers receive zero tools unless the Main Agent grants a per-task `tools` subset by name. */
296
330
  tools?: Record<string, ToolDefinition> | ToolDefinition[];
297
- /** Per-worker timeout in ms. `0` (default) = no limit. `-1` = the Main Agent sets a per-task `timeoutMs`. `>0` = fixed timeout for every worker. */
298
- timeout?: number;
299
331
  /**
300
- * Canonical per-worker timeout in ms (Q-52, preferred over `timeout`).
301
- * Maps to `timeout`: `workerTimeoutMs ?? timeout ?? 0`. See
302
- * `normalizeWorkerTimeout` (re-exported from the root entry).
332
+ * Longest a SCHEDULED ACTION's run may take, in ms (the `subagents` tool and `agent.workers` have no timeouts). An
333
+ * action can set its own shorter `timeoutMs`; this is the ceiling for it, and the limit for actions that set none.
334
+ * Default: no limit (`0` and `Infinity` mean the same).
303
335
  */
304
336
  workerTimeoutMs?: number;
305
- /**
306
- * When true, the Main Agent sets a per-task `timeoutMs` (maps to
307
- * `timeout: -1`). When both are set, an explicit `timeout`/`workerTimeoutMs`
308
- * wins over `modelMaySetTimeout`.
309
- */
310
- modelMaySetTimeout?: boolean;
311
337
  }
312
338
 
313
339
  /** Per-run overrides for {@link Agent.run}, {@link Agent.ask}, and {@link Agent.stream}. */
@@ -333,8 +359,10 @@ export interface AgentRunOptions {
333
359
  * enforces `"steer"` or `"queue"`, that policy wins and this field is ignored.
334
360
  * When idle, this field is ignored and the call runs immediately.
335
361
  * Defaults to `"queue"` (finish current turn, run as next turn).
362
+ * `"interrupt"` is honored in every mode: it stops the active run (provider request, tools, workers) and starts this
363
+ * prompt as the next run, ahead of queued follow-ups.
336
364
  */
337
- enqueue?: "steer" | "queue";
365
+ enqueue?: "steer" | "queue" | "interrupt";
338
366
  /** Headers merged for this run only. */
339
367
  headers?: Record<string, string>;
340
368
  /** Called for each text delta as it streams (only when stream:true). */
@@ -389,7 +417,7 @@ export interface Budget {
389
417
  maxTotalTokens?: number;
390
418
  /** Maximum wall/monotonic duration in ms since budget tracking started. */
391
419
  maxDurationMs?: number;
392
- /** Maximum total dynamic sub-agents spawned via `spawn_subagents`. */
420
+ /** Maximum total dynamic sub-agents spawned in one run (`subagents` tool or `agent.workers.spawn`). */
393
421
  maxTotalSubagents?: number;
394
422
  }
395
423
 
@@ -15,6 +15,12 @@ export interface ScheduleSpec {
15
15
  repeatEvery?: string;
16
16
  /** Extra attempts if a run fails, only while time remains before delivery. Default 0. */
17
17
  retries?: number;
18
+ /**
19
+ * What happens when the host program was not running at a delivery time. `"skip"`: move on to the next time (a one-shot
20
+ * is done). `"run"`: run it as soon as the program is back. Omitted: the action is marked `missed` and waits for
21
+ * `runScheduledAction` or `skipScheduledAction`.
22
+ */
23
+ onMissed?: "skip" | "run";
18
24
  }
19
25
 
20
26
  /** What `createScheduledAction` receives: dynamic sub-agent fields plus the schedule. */
@@ -24,6 +30,7 @@ export interface ScheduledActionInput {
24
30
  task: string;
25
31
  /** Names from the developer's `dynamicSubagents.tools` pool. */
26
32
  tools?: string[];
33
+ /** Longest one run may take, in ms. Never above `dynamicSubagents.workerTimeoutMs` when the developer set one. */
27
34
  timeoutMs?: number;
28
35
  scheduled_action: ScheduleSpec & {
29
36
  /** Human label. */
@@ -74,8 +81,8 @@ export interface ScheduledDelivery {
74
81
  late: boolean;
75
82
  }
76
83
 
77
- /** Result of `killScheduledAction`. */
78
- export type KillResult =
84
+ /** Result of `deleteScheduledAction`. */
85
+ export type DeleteResult =
79
86
  | { ok: true }
80
87
  | { ok: false; reason: "not_found" }
81
88
  | { ok: false; reason: "running"; message: string };
package/src/types/tool.ts CHANGED
@@ -20,7 +20,7 @@ export interface ToolExecutionContext {
20
20
  signal?: AbortSignal;
21
21
  /** Session identifier associated with this invocation. */
22
22
  sessionId?: string;
23
- /** Forwards live sub-agent deltas (used by `spawn_subagents` for realtime tracking). */
23
+ /** Forwards live sub-agent deltas (used by the `subagents` tool for realtime tracking). */
24
24
  onSubagentEvent?: (event: { trackingId: string; delta?: string; thinkingDelta?: string; partialText?: string; partialThinking?: string }) => void;
25
25
  // Q-55 Tool API v2 context additions (all optional, additive).
26
26
  /** Stable id for this run (`newRunId`). Mirrors SubAgentRunIds.runId. */
@@ -23,7 +23,7 @@ export function getPackageVersion(): string {
23
23
  const v = (globalThis as any).process?.env?.npm_package_version;
24
24
  if (typeof v === "string" && v) return v;
25
25
  } catch {}
26
- return "0.5.3";
26
+ return "0.5.5";
27
27
  }
28
28
 
29
29
  function getAgentAccelUserAgent(): string {