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 +84 -37
- package/examples/prompts/SYSTEM_PROMPT_AGENT.md +14 -20
- package/package.json +1 -1
- package/src/agent/agent.ts +181 -56
- package/src/agent/config-blocks.ts +80 -0
- package/src/agent/context.ts +1 -1
- package/src/agent/delegation.ts +208 -524
- package/src/agent/loop.ts +18 -14
- package/src/agent/subagents-tool.ts +87 -0
- package/src/agent/subagents.md +31 -0
- package/src/agent/turns.ts +60 -0
- package/src/agent/worker.ts +43 -33
- package/src/agent/workers.ts +405 -0
- package/src/index.ts +6 -5
- package/src/providers/openrouter.ts +49 -225
- package/src/providers/shared.ts +2 -2
- package/src/scheduler/scheduled-actions.md +4 -2
- package/src/scheduler/scheduler.ts +87 -50
- package/src/scheduler/store.ts +23 -1
- package/src/scheduler/tool.ts +9 -8
- package/src/session/journal.ts +12 -1
- package/src/session/store.ts +24 -10
- package/src/session/usage.ts +201 -0
- package/src/streaming/event-stream.ts +7 -2
- package/src/tools/executor.ts +17 -2
- package/src/tools/subagent-result.ts +1 -1
- package/src/tools/tool.ts +0 -16
- package/src/types/agent.ts +44 -16
- package/src/types/scheduler.ts +9 -2
- package/src/types/tool.ts +1 -1
- package/src/utils/headers.ts +1 -1
|
@@ -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
|
-
|
|
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
|
|
package/src/tools/executor.ts
CHANGED
|
@@ -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` / `
|
|
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.
|
package/src/types/agent.ts
CHANGED
|
@@ -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` (
|
|
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
|
-
/**
|
|
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
|
|
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
|
-
*
|
|
301
|
-
*
|
|
302
|
-
* `
|
|
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
|
|
420
|
+
/** Maximum total dynamic sub-agents spawned in one run (`subagents` tool or `agent.workers.spawn`). */
|
|
393
421
|
maxTotalSubagents?: number;
|
|
394
422
|
}
|
|
395
423
|
|
package/src/types/scheduler.ts
CHANGED
|
@@ -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 `
|
|
78
|
-
export type
|
|
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 `
|
|
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. */
|
package/src/utils/headers.ts
CHANGED
|
@@ -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.
|
|
26
|
+
return "0.5.5";
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
function getAgentAccelUserAgent(): string {
|