@wrongstack/core 0.63.4 → 0.68.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 (57) hide show
  1. package/dist/{agent-bridge-B5rxWrg3.d.ts → agent-bridge-D-j6OOBT.d.ts} +1 -1
  2. package/dist/agent-subagent-runner-DRZ9-NnR.d.ts +1042 -0
  3. package/dist/{compactor-0vjZ8KTk.d.ts → compactor-D_ExJajC.d.ts} +1 -1
  4. package/dist/{config-BdDuaZmB.d.ts → config--86aHSln.d.ts} +1 -1
  5. package/dist/{context-iFMEO2rN.d.ts → context-y87Jc5ei.d.ts} +3 -3
  6. package/dist/coordination/index.d.ts +12 -12
  7. package/dist/coordination/index.js +265 -275
  8. package/dist/coordination/index.js.map +1 -1
  9. package/dist/defaults/index.d.ts +22 -22
  10. package/dist/defaults/index.js +181 -180
  11. package/dist/defaults/index.js.map +1 -1
  12. package/dist/{events-k8CHjcrN.d.ts → events-CIplI98R.d.ts} +1 -1
  13. package/dist/execution/index.d.ts +16 -385
  14. package/dist/execution/index.js +124 -146
  15. package/dist/execution/index.js.map +1 -1
  16. package/dist/extension/index.d.ts +6 -6
  17. package/dist/goal-store-C7jcumEh.d.ts +96 -0
  18. package/dist/{index-Bc6BiP5q.d.ts → index-DKUvyTvV.d.ts} +28 -442
  19. package/dist/{index-CWdW_CJt.d.ts → index-b5uhfTSl.d.ts} +8 -8
  20. package/dist/index.d.ts +34 -32
  21. package/dist/index.js +692 -750
  22. package/dist/index.js.map +1 -1
  23. package/dist/infrastructure/index.d.ts +6 -6
  24. package/dist/kernel/index.d.ts +9 -9
  25. package/dist/{mcp-servers-CwqQDMYy.d.ts → mcp-servers-DwoNBf6r.d.ts} +3 -3
  26. package/dist/models/index.d.ts +2 -2
  27. package/dist/{multi-agent-coordinator-CNUJYq7U.d.ts → multi-agent-coordinator-CWnH-CiX.d.ts} +10 -2
  28. package/dist/{null-fleet-bus-DRoJ0uOY.d.ts → null-fleet-bus-CuN0ObJr.d.ts} +24 -31
  29. package/dist/observability/index.d.ts +2 -2
  30. package/dist/parallel-eternal-engine-0UwotoSx.d.ts +483 -0
  31. package/dist/{path-resolver-C5sPVne8.d.ts → path-resolver-DVkEcIw8.d.ts} +2 -2
  32. package/dist/{permission-Ld-i5ugf.d.ts → permission-C1A5whY5.d.ts} +5 -1
  33. package/dist/{permission-policy-CL-mPufp.d.ts → permission-policy-B2dK-T5N.d.ts} +19 -5
  34. package/dist/{plan-templates-ThBHOjaM.d.ts → plan-templates-Bprrzhbu.d.ts} +4 -4
  35. package/dist/{provider-runner-DJQa211J.d.ts → provider-runner-mXvXGSIw.d.ts} +3 -3
  36. package/dist/{retry-policy-BfBScewS.d.ts → retry-policy-CG3qvH_e.d.ts} +1 -1
  37. package/dist/sdd/index.d.ts +8 -8
  38. package/dist/sdd/index.js +123 -146
  39. package/dist/sdd/index.js.map +1 -1
  40. package/dist/security/index.d.ts +3 -3
  41. package/dist/security/index.js +31 -22
  42. package/dist/security/index.js.map +1 -1
  43. package/dist/{selector-DxhW7ML3.d.ts → selector-RvBR_YRW.d.ts} +1 -1
  44. package/dist/session-event-bridge-CDHxcmQU.d.ts +93 -0
  45. package/dist/{session-reader-q2ThszgG.d.ts → session-reader-BIpwM60D.d.ts} +1 -1
  46. package/dist/storage/index.d.ts +7 -6
  47. package/dist/{system-prompt-7LHyBbIf.d.ts → system-prompt-b61lOd49.d.ts} +2 -2
  48. package/dist/types/index.d.ts +23 -14
  49. package/dist/types/index.js.map +1 -1
  50. package/dist/utils/index.d.ts +2 -2
  51. package/dist/utils/index.js.map +1 -1
  52. package/package.json +1 -1
  53. package/skills/multi-agent/SKILL.md +0 -2
  54. package/dist/agent-subagent-runner-Zc3f37Sg.d.ts +0 -182
  55. package/dist/goal-store-iHltMi5n.d.ts +0 -188
  56. package/dist/multi-agent-SASYOrWA.d.ts +0 -554
  57. package/dist/tool-executor-CIjpGaRA.d.ts +0 -111
@@ -0,0 +1,1042 @@
1
+ import { t as ToolRegistry, m as ProviderRegistry, f as AgentPipelines, q as ToolExecutorLike, E as ExtensionRegistry, d as AgentInit, e as AgentInput, R as RunResult, p as ToolCallPipelinePayload, u as ToolWrapper, S as SystemPromptContributor } from './index-DKUvyTvV.js';
2
+ import { C as Container, e as Renderer, R as ReadonlyPipeline } from './system-prompt-b61lOd49.js';
3
+ import { E as EventBus, k as EventName, L as Listener } from './events-CIplI98R.js';
4
+ import { a as RetryPolicy, E as ErrorHandler } from './retry-policy-CG3qvH_e.js';
5
+ import { a as Logger } from './logger-DDd5C--Z.js';
6
+ import { T as Tracer } from './observability-BhnVLBLS.js';
7
+ import { a as PermissionPolicy } from './permission-C1A5whY5.js';
8
+ import { d as Context, Q as Tool, u as RunOptions, J as JSONSchema, p as Request, q as Response, c as ContentBlock, T as TextBlock, m as Provider, $ as Usage } from './context-y87Jc5ei.js';
9
+ import { l as HookEvent, n as HookMatcher, I as InProcessHook, a as Config } from './config--86aHSln.js';
10
+ import { W as WireFamily } from './models-registry-BcYJDKLm.js';
11
+
12
+ declare class Agent {
13
+ readonly container: Container;
14
+ readonly tools: ToolRegistry;
15
+ readonly providers: ProviderRegistry;
16
+ readonly events: EventBus;
17
+ readonly pipelines: AgentPipelines;
18
+ readonly ctx: Context;
19
+ readonly maxIterations: number;
20
+ readonly executionStrategy: 'parallel' | 'sequential' | 'smart';
21
+ readonly perIterationOutputCapBytes: number;
22
+ private readonly plugins;
23
+ readonly toolExecutor: ToolExecutorLike;
24
+ readonly autoExtendLimit: boolean;
25
+ private readonly autonomousContinue;
26
+ readonly tracer: Tracer | undefined;
27
+ readonly extensions: ExtensionRegistry;
28
+ private readonly _toolHandler;
29
+ private readonly _responseHandler;
30
+ private readonly _loopHandler;
31
+ constructor(init: AgentInit);
32
+ get logger(): Logger;
33
+ get retry(): RetryPolicy;
34
+ get errorHandler(): ErrorHandler;
35
+ get permission(): PermissionPolicy;
36
+ get renderer(): Renderer | undefined;
37
+ disableInteractiveConfirmation(): void;
38
+ register(tool: Tool): void;
39
+ use(plugin: Plugin, api: PluginAPI): Promise<void>;
40
+ teardown(): Promise<void>;
41
+ run(userInput: AgentInput, opts?: RunOptions): Promise<RunResult>;
42
+ }
43
+
44
+ /**
45
+ * A slash command registered with the CLI or available to plugins.
46
+ * Plugins receive a view of the registry via PluginAPI.slashCommands.
47
+ *
48
+ * Commands registered by plugins use a namespaced name: `pluginName:commandName`.
49
+ * This prevents collisions with built-in commands and other plugins.
50
+ */
51
+ interface SlashCommand {
52
+ /** Unique command name. For plugins: `pluginName:commandName`. */
53
+ name: string;
54
+ /** Short aliases — also prefixed automatically: `pluginName:alias`. */
55
+ aliases?: string[];
56
+ description: string;
57
+ /**
58
+ * Optional compact argument hint for interactive menus. This is not parsed
59
+ * by the registry; it only helps TUI/REPL surfaces show the expected shape,
60
+ * for example `[list|install <alias>|disable <name>]`.
61
+ */
62
+ argsHint?: string;
63
+ /**
64
+ * Optional detailed help shown by `/help <name>`. Use this for usage,
65
+ * arguments, examples, side-effects — anything that doesn't fit in
66
+ * `description`. Renders verbatim, so format with line breaks.
67
+ * If absent, `/help <name>` falls back to `description`.
68
+ */
69
+ help?: string;
70
+ /**
71
+ * Execute the command.
72
+ * @param args Everything after the command name (trimmed by dispatch).
73
+ * @param ctx The current agent context.
74
+ * @returns `{ exit: true }` to quit the REPL. `{ message }` to print and
75
+ * continue. `{ runText }` to send a follow-up user-role message to the
76
+ * model immediately (e.g. `/steer <text>` builds a STEERING preamble
77
+ * here and asks the TUI to run it as the next turn). The TUI prints
78
+ * `message` first (if any) so the user sees the slash result before
79
+ * the model's response starts streaming. `{ metadata }` carries
80
+ * structured data for the REPL/TUI to act on (e.g. SDD session state).
81
+ */
82
+ run(args: string, ctx: Context): Promise<{
83
+ exit?: boolean;
84
+ message?: string;
85
+ runText?: string;
86
+ metadata?: Record<string, unknown>;
87
+ } | void>;
88
+ }
89
+
90
+ interface ToolRegistryView {
91
+ register(t: Tool): void;
92
+ unregister(name: string): void;
93
+ /** Wrap (decorate) an existing tool. The wrapper gets the current tool and returns the decorated version. */
94
+ wrap(name: string, wrapper: ToolWrapper): void;
95
+ get(name: string): Tool | undefined;
96
+ list(): Tool[];
97
+ }
98
+ interface ProviderFactory {
99
+ type: string;
100
+ family: WireFamily;
101
+ create(cfg: unknown): Provider;
102
+ }
103
+ interface ProviderRegistryView {
104
+ register(f: ProviderFactory): void;
105
+ create(cfg: {
106
+ type: string;
107
+ } & Record<string, unknown>): Provider;
108
+ list(): string[];
109
+ }
110
+ interface MCPRegistryView {
111
+ start(cfg: unknown): Promise<void>;
112
+ stop(name: string): Promise<void>;
113
+ restart(name: string): Promise<void>;
114
+ list(): {
115
+ name: string;
116
+ state: string;
117
+ toolCount: number;
118
+ }[];
119
+ }
120
+ interface SlashCommandRegistryView {
121
+ register(cmd: SlashCommand): void;
122
+ unregister(name: string): boolean;
123
+ get(name: string): SlashCommand | undefined;
124
+ list(): SlashCommand[];
125
+ }
126
+ /**
127
+ * Read-only view of the session writer. Plugins can append custom events
128
+ * to the JSONL session log and read the transcript path.
129
+ *
130
+ * The `append` method accepts any JSON-serializable payload — custom
131
+ * event types are persisted verbatim next to the built-in events.
132
+ */
133
+ interface SessionWriterView {
134
+ readonly transcriptPath?: string;
135
+ append(event: Record<string, unknown> & {
136
+ type: string;
137
+ ts: string;
138
+ }): Promise<void>;
139
+ }
140
+ /**
141
+ * Metrics sink scoped to a plugin. The host auto-prefixes metric names
142
+ * with `plugin.<pluginName>.` so plugins don't need to namespace
143
+ * manually. Plugins call counter/histogram/gauge directly; the values
144
+ * flow to the host's MetricsSink (Prometheus, OTLP, or noop).
145
+ */
146
+ interface MetricsSinkView {
147
+ counter(name: string, value?: number, labels?: Record<string, string>): void;
148
+ histogram(name: string, value: number, labels?: Record<string, string>): void;
149
+ gauge(name: string, value: number, labels?: Record<string, string>): void;
150
+ }
151
+ interface PluginPipelines {
152
+ request: ReadonlyPipeline<Request>;
153
+ response: ReadonlyPipeline<Response>;
154
+ toolCall: ReadonlyPipeline<ToolCallPipelinePayload>;
155
+ userInput: ReadonlyPipeline<{
156
+ content: ContentBlock[];
157
+ text: string;
158
+ ctx: Context;
159
+ }>;
160
+ assistantOutput: ReadonlyPipeline<TextBlock>;
161
+ contextWindow: ReadonlyPipeline<Context>;
162
+ [k: string]: ReadonlyPipeline<any>;
163
+ }
164
+ interface PluginAPI {
165
+ container: Container;
166
+ pipelines: PluginPipelines;
167
+ events: EventBus;
168
+ tools: ToolRegistryView;
169
+ providers: ProviderRegistryView;
170
+ mcp: MCPRegistryView;
171
+ slashCommands: SlashCommandRegistryView;
172
+ /** Live session writer — plugins can append custom events here. */
173
+ session: SessionWriterView;
174
+ /** Scoped metrics sink — counters/histograms/gauges auto-namespaced under `plugin.<name>.` */
175
+ metrics: MetricsSinkView;
176
+ /** Registry for agent lifecycle extensions — hooks like beforeRun, beforeIteration, onError, etc. */
177
+ extensions: ExtensionRegistry;
178
+ /**
179
+ * Register a system prompt contributor. Plugins call this to inject
180
+ * ephemeral TextBlocks into the system prompt on every build.
181
+ * Returns an unregister function.
182
+ */
183
+ registerSystemPromptContributor(c: SystemPromptContributor): () => void;
184
+ /**
185
+ * Register an in-process lifecycle hook. `matcher` is a tool-name filter for
186
+ * `PreToolUse`/`PostToolUse` (`"Bash"`, `"edit|write"`, `"*"`) and ignored
187
+ * for other events. The hook can block, rewrite tool input, or inject extra
188
+ * context — see `HookOutcome`. Automatically removed when the plugin is
189
+ * uninstalled. Returns an unregister function.
190
+ */
191
+ registerHook(event: HookEvent, matcher: HookMatcher | undefined, hook: InProcessHook): () => void;
192
+ config: Config;
193
+ log: Logger;
194
+ /**
195
+ * Register a one-time event listener. The handler is automatically removed
196
+ * after the first emission, or when the plugin is uninstalled — whichever
197
+ * comes first.
198
+ */
199
+ onEvent<K extends EventName>(event: K, handler: Listener<K>): () => void;
200
+ /**
201
+ * Subscribe to all events matching a glob-style pattern.
202
+ * `'tool.*'` matches all tool events. `'*'` matches everything.
203
+ * Returns an unsubscribe function.
204
+ */
205
+ onPattern(pattern: string, handler: (event: string, payload: unknown) => void): () => void;
206
+ /**
207
+ * Emit a custom event on the agent's EventBus. Use for inter-plugin
208
+ * communication or to surface plugin-specific state to the host.
209
+ *
210
+ * Custom events use a `pluginName:eventName` convention to avoid
211
+ * collisions with built-in events (e.g. `my-plugin:cache_hit`).
212
+ * The payload is passed through to all subscribers.
213
+ */
214
+ emitCustom(event: string, payload: unknown): void;
215
+ /**
216
+ * Register a callback that fires when the configuration changes at
217
+ * runtime (e.g. via `/config` slash command or programmatic update).
218
+ * The handler receives the new and previous config snapshots.
219
+ * Returns an unsubscribe function.
220
+ */
221
+ onConfigChange(handler: (next: Readonly<Config>, prev: Readonly<Config>) => void): () => void;
222
+ }
223
+ /**
224
+ * Capability declaration — informs the host which subsystems a plugin
225
+ * intends to touch. Used for diagnostics and per-plugin enable/disable UX
226
+ * (e.g. "this plugin registers tools — disable to remove them"). Not
227
+ * enforced at runtime: a plugin that declares `tools: false` can still
228
+ * call `api.tools.register()`, but the host can flag the discrepancy.
229
+ */
230
+ interface PluginCapabilities {
231
+ /** Will register tools via `api.tools.register()`. */
232
+ tools?: boolean;
233
+ /** Will register provider factories via `api.providers.register()`. */
234
+ providers?: boolean;
235
+ /**
236
+ * Pipelines the plugin hooks into. Use the standard names
237
+ * (`request | response | toolCall | userInput | assistantOutput | contextWindow`)
238
+ * or custom pipeline names exposed by other plugins.
239
+ */
240
+ pipelines?: string[];
241
+ /** Will register slash commands via `api.slashCommands.register()`. */
242
+ slashCommands?: boolean;
243
+ /** Will start MCP servers via `api.mcp.start()`. */
244
+ mcp?: boolean;
245
+ }
246
+ /**
247
+ * Structured dependency declaration. The string form (`dependsOn: ['foo']`)
248
+ * is shorthand for `[{ name: 'foo' }]` — both work. Use the structured form
249
+ * when you need a version constraint:
250
+ *
251
+ * dependsOn: [{ name: 'wstack-auth', version: '^1.2.0' }]
252
+ */
253
+ interface PluginDependency {
254
+ name: string;
255
+ /** npm-style semver range. Supports `^`, `~`, exact, and unprefixed. */
256
+ version?: string;
257
+ }
258
+ interface Plugin {
259
+ name: string;
260
+ version?: string;
261
+ /** One-line summary for `wstack plugins list` and error messages. */
262
+ description?: string;
263
+ /** Semver range against the kernel API version (KERNEL_API_VERSION). */
264
+ apiVersion: string;
265
+ /**
266
+ * Capability hints — what subsystems the plugin will register against.
267
+ * Optional; provided for diagnostics and UX. The loader does not enforce
268
+ * these, but mismatch is surfaced via logger at warn level.
269
+ */
270
+ capabilities?: PluginCapabilities;
271
+ /**
272
+ * JSON Schema for the options under `Config.plugins[<name>].options`.
273
+ * When present, the loader validates that section before calling `setup`
274
+ * and rejects the plugin with a clear error path on failure.
275
+ */
276
+ configSchema?: JSONSchema;
277
+ /**
278
+ * Mandatory plugin dependencies — loading fails if any are absent or
279
+ * version-incompatible. Accepts both the legacy string-array form and
280
+ * the structured form with version constraints.
281
+ */
282
+ dependsOn?: (string | PluginDependency)[];
283
+ /** Optional plugin dependencies — silently skipped if absent. */
284
+ optionalDeps?: (string | PluginDependency)[];
285
+ conflictsWith?: string[];
286
+ /**
287
+ * Default configuration values, deep-merged under the plugin's options
288
+ * key before `configSchema` validation. User-provided values take
289
+ * precedence over defaults — this is a fallback, not an override.
290
+ *
291
+ * @example
292
+ * defaultConfig: { ttl: 3600, maxSize: 100 }
293
+ */
294
+ defaultConfig?: Record<string, unknown>;
295
+ setup(api: PluginAPI): void | Promise<void>;
296
+ teardown?(api: PluginAPI): void | Promise<void>;
297
+ /**
298
+ * Optional health check. Called by the host (e.g. `/diag plugins` slash
299
+ * command or health endpoint) to surface plugin status. Return
300
+ * `{ ok: false, message: '...' }` when the plugin is degraded.
301
+ */
302
+ health?(): Promise<{
303
+ ok: boolean;
304
+ message?: string;
305
+ }>;
306
+ }
307
+
308
+ type BridgeMessageType = 'task' | 'result' | 'progress' | 'error' | 'heartbeat' | 'stop' | 'delegate' | 'budget_threshold';
309
+ interface BridgeMessage<T = unknown> {
310
+ id: string;
311
+ type: BridgeMessageType;
312
+ from: string;
313
+ to?: string;
314
+ payload: T;
315
+ timestamp: number;
316
+ priority?: 'low' | 'normal' | 'high' | 'critical';
317
+ }
318
+ interface AgentBridgeConfig {
319
+ agentId: string;
320
+ coordinatorId: string;
321
+ timeoutMs?: number;
322
+ bufferSize?: number;
323
+ }
324
+ interface AgentBridge {
325
+ readonly agentId: string;
326
+ readonly coordinatorId: string;
327
+ send(msg: BridgeMessage): Promise<void>;
328
+ broadcast(msg: BridgeMessage): Promise<void>;
329
+ subscribe(handler: (msg: BridgeMessage) => void | Promise<void>): () => void;
330
+ request<T>(msg: BridgeMessage, timeoutMs?: number): Promise<BridgeMessage<T>>;
331
+ stop(): Promise<void>;
332
+ }
333
+ interface BridgeTransport {
334
+ send(msg: BridgeMessage, to: string): Promise<void>;
335
+ subscribe(agentId: string, handler: (msg: BridgeMessage) => void | Promise<void>): () => void;
336
+ close(agentId: string): Promise<void>;
337
+ }
338
+
339
+ type BudgetKind = 'tool_calls' | 'iterations' | 'tokens' | 'timeout' | 'idle_timeout' | 'cost';
340
+ declare class BudgetExceededError extends Error {
341
+ readonly kind: BudgetKind;
342
+ readonly limit: number;
343
+ readonly observed: number;
344
+ constructor(kind: BudgetKind, limit: number, observed: number);
345
+ }
346
+ interface BudgetLimits {
347
+ maxIterations?: number;
348
+ maxToolCalls?: number;
349
+ maxTokens?: number;
350
+ /** Estimated USD cost ceiling. */
351
+ maxCostUsd?: number;
352
+ /**
353
+ * Hard wall-clock timeout measured from `start()`. Off by default — set it
354
+ * explicitly only when a task must finish within an absolute window. For
355
+ * the everyday "don't kill an agent that's still working" guard, prefer
356
+ * `idleTimeoutMs`, which resets on activity.
357
+ */
358
+ timeoutMs?: number;
359
+ /**
360
+ * Idle timeout: the maximum gap (ms) between activity signals (iterations,
361
+ * tool calls, token usage, streamed progress) before the subagent is
362
+ * considered hung and reaped. Unlike `timeoutMs`, an actively-working
363
+ * agent continuously resets this clock via `markActivity()`, so it never
364
+ * trips on a long-but-productive run — only on a genuine stall.
365
+ */
366
+ idleTimeoutMs?: number;
367
+ }
368
+ /**
369
+ * Controls how the budget behaves when `onThreshold` is set and a limit is hit.
370
+ *
371
+ * `'auto'` — emit `budget.threshold_reached` on the EventBus and wait for a
372
+ * coordinator response (extend/stop). If no listener responds within
373
+ * `DECISION_TIMEOUT_MS` the decision defaults to `'stop'`.
374
+ * `'sync'` — do not emit any event; treat the threshold as a hard stop and
375
+ * throw `BudgetExceededError` synchronously. Useful for fire-and-forget
376
+ * subagents that have an `onThreshold` handler for logging/metrics but are
377
+ * not wired into a coordinator.
378
+ *
379
+ * @default 'auto'
380
+ */
381
+ type BudgetNegotiationMode = 'auto' | 'sync';
382
+ interface BudgetUsage {
383
+ iterations: number;
384
+ toolCalls: number;
385
+ tokens: {
386
+ input: number;
387
+ output: number;
388
+ total: number;
389
+ };
390
+ costUsd: number;
391
+ elapsedMs: number;
392
+ }
393
+ /**
394
+ * Thrown by `SubagentBudget.record*` when a soft limit is hit and
395
+ * an `onThreshold` handler is configured that wants to ask the
396
+ * coordinator (via `budget.threshold_reached` event). The runner
397
+ * catches this and awaits the embedded `decision` promise to get
398
+ * the coordinator's extend/stop decision.
399
+ *
400
+ * Distinct from `BudgetExceededError` which is a hard stop.
401
+ */
402
+ declare class BudgetThresholdSignal extends Error {
403
+ readonly kind: BudgetKind;
404
+ readonly limit: number;
405
+ readonly used: number;
406
+ /** Resolves to 'extend' (with optional new limits) or 'stop' */
407
+ readonly decision: Promise<BudgetThresholdDecision>;
408
+ constructor(kind: BudgetKind, limit: number, used: number, decision: Promise<BudgetThresholdDecision>);
409
+ }
410
+ type BudgetThresholdDecision = 'stop' | {
411
+ extend: Partial<BudgetLimits>;
412
+ };
413
+ /**
414
+ * Callback invoked when a budget limit is about to be exceeded.
415
+ * Return 'throw' for hard stop (default — throws BudgetExceededError).
416
+ * Return 'continue' to allow one more unit and re-check next time.
417
+ * Return a Promise to ask the coordinator via `budget.threshold_reached`
418
+ * event (uses the same grant/deny pattern as `iteration.limit_reached`).
419
+ */
420
+ type BudgetThresholdHandler = (info: {
421
+ kind: BudgetKind;
422
+ used: number;
423
+ limit: number;
424
+ requestDecision: () => Promise<BudgetThresholdDecision>;
425
+ }) => 'throw' | 'continue' | Promise<BudgetThresholdDecision>;
426
+ /**
427
+ * Per-subagent budget enforcement. Each subagent gets its own instance so a
428
+ * runaway agent can't drain the cost ceiling of its siblings. All record/check
429
+ * methods are O(1) and safe to call from hot paths.
430
+ *
431
+ * Behavior without `onThreshold`: hard stops synchronously on every limit hit.
432
+ *
433
+ * Behavior with `onThreshold` and `_mode === 'auto'`: emits `budget.threshold_reached`
434
+ * on the EventBus and throws `BudgetThresholdSignal`. The coordinator's verdict
435
+ * (extend/stop) resolves the embedded promise. If no listener responds within
436
+ * `DECISION_TIMEOUT_MS` the decision defaults to `'stop'`.
437
+ *
438
+ * Behavior with `onThreshold` and `_mode === 'sync'`: throws `BudgetExceededError`
439
+ * synchronously regardless of EventBus state or listener presence. This is useful
440
+ * for fire-and-forget subagents that have an `onThreshold` handler for logging/metrics
441
+ * but are not wired into a coordinator — the `'sync'` mode makes the hard-stop
442
+ * behavior explicit and means tests can use `expect().toThrow()` even without
443
+ * a fully-wired EventBus.
444
+ */
445
+ declare class SubagentBudget {
446
+ readonly limits: Readonly<BudgetLimits>;
447
+ private iterations;
448
+ private toolCalls;
449
+ private tokenInput;
450
+ private tokenOutput;
451
+ private costUsd;
452
+ private startTime;
453
+ /**
454
+ * Timestamp of the most recent activity (iteration / tool call / token
455
+ * usage / streamed progress). Drives the idle timeout — reset by
456
+ * `markActivity()`. Initialised to `start()` time so a never-active agent
457
+ * still eventually trips its idle window.
458
+ */
459
+ private lastActivityTime;
460
+ private _onThreshold;
461
+ /**
462
+ * Hard cap on how long `_negotiateExtension` waits for the coordinator to
463
+ * respond before defaulting to 'stop'. Without this fallback an absent
464
+ * or hung listener (Director not built / event filter detached mid-run)
465
+ * leaves the budget over-limit and never enforces anything.
466
+ */
467
+ private static readonly DECISION_TIMEOUT_MS;
468
+ /**
469
+ * Injected by the runner when wiring the budget to its EventBus.
470
+ * Used to emit `budget.threshold_reached` events in `'auto'` mode.
471
+ */
472
+ _events?: EventBus;
473
+ /**
474
+ * Negotiation mode — controls whether a threshold hit tries to emit
475
+ * `budget.threshold_reached` and wait for a coordinator decision, or
476
+ * falls straight through to a synchronous hard stop.
477
+ *
478
+ * `'auto'` (default) — emit on the EventBus and wait; times out to 'stop'.
479
+ * `'sync'` — throw `BudgetExceededError` immediately regardless of listeners.
480
+ */
481
+ private _mode;
482
+ /**
483
+ * Optional callback for soft-limit handling. When set, the budget will
484
+ * invoke it rather than throw immediately. The handler decides whether to
485
+ * throw synchronously, continue, or ask the coordinator for an extension.
486
+ */
487
+ get onThreshold(): BudgetThresholdHandler | undefined;
488
+ set onThreshold(fn: BudgetThresholdHandler | undefined);
489
+ /** Returns the current negotiation mode. */
490
+ get mode(): BudgetNegotiationMode;
491
+ constructor(limits?: BudgetLimits, mode?: BudgetNegotiationMode);
492
+ start(): void;
493
+ /**
494
+ * Reset the idle clock. Called on any sign of forward progress —
495
+ * iterations, tool calls, token usage, and streamed tool/text progress —
496
+ * so a long-but-productive subagent never trips its `idleTimeoutMs`.
497
+ */
498
+ markActivity(): void;
499
+ /**
500
+ * Milliseconds since the last activity signal. Returns 0 before `start()`
501
+ * (nothing to measure yet). Used by the coordinator watchdog to decide
502
+ * whether to re-arm (still active) or reap (genuinely idle).
503
+ */
504
+ idleMs(): number;
505
+ /** Returns true if we're within 10% of any limit — useful for pre-flight checks. */
506
+ isNearLimit(): boolean;
507
+ /**
508
+ * Synchronous budget check. Always throws synchronously so callers (especially
509
+ * test event handlers using `expect().toThrow()`) get an unhandled rejection
510
+ * when the budget is exceeded without a handler.
511
+ *
512
+ * Decision table:
513
+ * - no `onThreshold` handler → throw `BudgetExceededError` (hard stop, always)
514
+ * - `mode === 'sync'` → throw `BudgetExceededError` (hard stop, always)
515
+ * - `mode === 'auto'` + no listener → throw `BudgetExceededError` (hard stop; no one to ask)
516
+ * - `mode === 'auto'` + listener → throw `BudgetThresholdSignal` with async decision promise
517
+ */
518
+ /**
519
+ * Collects all exceeded budget kinds into a single NOOP-free negotiation.
520
+ * Called by recordIteration / recordToolCall / recordUsage — each may call
521
+ * this for its own kind. The first call starts the negotiation and stores
522
+ * the Promise in _pendingNegotiation. Subsequent calls for DIFFERENT
523
+ * kinds (while a negotiation is in flight) are NOOPs — they don't start
524
+ * new conversations with the coordinator. This prevents an EventBus flood
525
+ * when multiple budget kinds are exceeded simultaneously in one iteration.
526
+ *
527
+ * Returns the kinds that were found to be exceeded (for logging/debugging).
528
+ */
529
+ private checkLimits;
530
+ /**
531
+ * Per-kind in-flight negotiation Promises. Each budget kind can have its
532
+ * own concurrent negotiation — e.g. iterations and timeout can both
533
+ * be exceeded simultaneously without blocking each other. The same kind
534
+ * cannot start two concurrent negotiations (dedup guard).
535
+ * Cleared in `_negotiateExtension`'s `finally` block.
536
+ */
537
+ private _pendingNegotiations;
538
+ /**
539
+ * Drive the threshold handler to a decision. Resolves with `'stop'`
540
+ * (signal the runner to abort) or `{ extend: ... }` (limits already
541
+ * patched in-place; the runner should not abort). Clears the
542
+ * per-kind slot in `_pendingNegotiations` in `finally`.
543
+ *
544
+ * The 'continue' return from a sync handler is treated as
545
+ * `{ extend: {} }` — keep going without patching; next overrun fires
546
+ * a fresh signal.
547
+ */
548
+ private _negotiateExtension;
549
+ recordIteration(): void;
550
+ recordToolCall(): void;
551
+ recordUsage(usage: Usage, costUsd?: number): void;
552
+ /**
553
+ * Wall-clock / idle budget check. Delegates to `checkLimits(elapsed)`, so
554
+ * `timeout` and `idle_timeout` follow the SAME negotiation path as the other
555
+ * kinds — they are NOT a special-cased hard stop. This is deliberate: a
556
+ * heartbeat-aware policy (see `attachAutoExtend` and `CollabSession`) grants
557
+ * a timeout extension only while the agent is making progress and denies it
558
+ * once the agent is genuinely stuck, which is safer than an unconditional
559
+ * hard kill of a long-but-working agent. The runner translates the resulting
560
+ * `BudgetThresholdSignal` decision (`extend` → patch limits in place,
561
+ * `stop` → abort) just like every other kind.
562
+ *
563
+ * Decision table (same as `checkLimits`):
564
+ * - no `onThreshold` handler → throw `BudgetExceededError` (hard stop)
565
+ * - `mode === 'sync'` → throw `BudgetExceededError` (hard stop)
566
+ * - `mode === 'auto'` + no listener → throw `BudgetExceededError` (no one to ask)
567
+ * - `mode === 'auto'` + listener → throw `BudgetThresholdSignal` (negotiated;
568
+ * a heartbeat-aware policy may extend the timeout)
569
+ */
570
+ checkTimeout(): void;
571
+ /** Returns true if a wall-clock or idle timeout has occurred without throwing. */
572
+ isTimedOut(): boolean;
573
+ usage(): BudgetUsage;
574
+ }
575
+
576
+ interface SubagentConfig {
577
+ id?: string;
578
+ name: string;
579
+ role?: string;
580
+ prompt?: string;
581
+ maxIterations?: number;
582
+ maxToolCalls?: number;
583
+ maxTokens?: number;
584
+ maxCostUsd?: number;
585
+ /** Hard wall-clock cap (ms) from start. Opt-in; prefer `idleTimeoutMs`. */
586
+ timeoutMs?: number;
587
+ /**
588
+ * Idle timeout (ms): reap the subagent only after this long with no
589
+ * activity. Resets on every iteration / tool call / streamed progress, so
590
+ * an actively-working agent runs until its task naturally ends. This is the
591
+ * default reaper for delegated subagents (see `applyRosterBudget`).
592
+ */
593
+ idleTimeoutMs?: number;
594
+ tools?: string[];
595
+ model?: string;
596
+ priority?: number;
597
+ /**
598
+ * Working directory for this subagent's tools. Defaults to the factory's
599
+ * cwd. AutoPhase sets this to a per-phase git worktree so parallel phases
600
+ * edit isolated checkouts instead of clobbering one shared working tree.
601
+ * `projectRoot` is intentionally left unchanged — tools resolve the
602
+ * worktree's `.git` gitlink from `cwd` while staying bounded to the repo.
603
+ */
604
+ cwd?: string;
605
+ /**
606
+ * Provider registry id (e.g. `'anthropic'`, `'openai'`, `'google'`).
607
+ * Allows a director to mix providers across siblings — one subagent on
608
+ * Sonnet, another on GPT-5, another on Haiku. Falls back to the
609
+ * factory's default provider when omitted, which is the legacy
610
+ * single-provider behavior.
611
+ */
612
+ provider?: string;
613
+ /**
614
+ * Per-subagent session JSONL path. When omitted the orchestrator-
615
+ * supplied factory derives a path under `<sessionRoot>/<runId>/`.
616
+ * Override to redirect the transcript elsewhere (long-term storage,
617
+ * a different filesystem, etc.).
618
+ */
619
+ sessionPath?: string;
620
+ /**
621
+ * Additional text appended to the role's base system prompt. Does not
622
+ * replace it. Useful for last-mile guidance like "you may only call
623
+ * read tools, never write" or "respond in JSON only".
624
+ */
625
+ systemPromptOverride?: string;
626
+ /**
627
+ * Routing for streaming output. `'director'` (default) forwards
628
+ * text/tool events to the parent's FleetBus so the director can read
629
+ * the subagent's stream. `'silent'` keeps everything subagent-local;
630
+ * the director only sees the final task result. `'user'` forwards
631
+ * direct to the user-facing renderer (gate this behind an explicit
632
+ * config flag — it can confuse the chat surface).
633
+ */
634
+ textStream?: 'director' | 'silent' | 'user';
635
+ toolStream?: 'director' | 'silent' | 'user';
636
+ }
637
+ /**
638
+ * Discriminator for every distinct failure mode a subagent can hit. The
639
+ * coordinator's classifier (`classifySubagentError` in
640
+ * coordination/multi-agent-coordinator.ts) maps raw exceptions to one of
641
+ * these — callers (delegate tool, /agents UI, retry policies) can then
642
+ * branch on `kind` instead of grepping `error.message`. Each kind
643
+ * documents its retryability so an orchestrator can act on it without
644
+ * extra knowledge.
645
+ */
646
+ type SubagentErrorKind =
647
+ /** Provider returned 5xx. Transient server-side issue — safe to retry with backoff. */
648
+ 'provider_5xx'
649
+ /** Provider returned 429. Rate-limited — retry with `backoffMs` delay. */
650
+ | 'provider_rate_limit'
651
+ /** Provider call timed out at the network layer (TCP / TLS / read). Retry safe. */
652
+ | 'provider_timeout'
653
+ /** Provider rejected the credentials (401/403). NOT retryable — config fix required. */
654
+ | 'provider_auth'
655
+ /** Model returned a "context length exceeded" error. Retrying without trimming will fail again. */
656
+ | 'context_overflow'
657
+ /** A tool's `execute()` returned `ok:false`. Logical task failure, not a crash. */
658
+ | 'tool_failed'
659
+ /** A tool's `execute()` threw an exception. Often retryable but cause-dependent. */
660
+ | 'tool_threw'
661
+ /** Hit the per-subagent `maxIterations` budget. Either raise budget or narrow task. */
662
+ | 'budget_iterations'
663
+ /** Hit the per-subagent `maxToolCalls` budget. Either raise budget or narrow task. */
664
+ | 'budget_tool_calls'
665
+ /** Hit the per-subagent `maxTokens` budget. */
666
+ | 'budget_tokens'
667
+ /** Hit the per-subagent `maxCostUsd` budget. */
668
+ | 'budget_cost'
669
+ /** Hit the per-subagent `timeoutMs` wall-clock budget. */
670
+ | 'budget_timeout'
671
+ /** Parent agent's AbortController fired (user Ctrl+C, parent unwound, sibling failure cascade). */
672
+ | 'aborted_by_parent'
673
+ /** LLM returned end_turn with no textual content. Often a prompt issue. */
674
+ | 'empty_response'
675
+ /** Parent-child bridge transport failed (rare — IPC / writer crash). */
676
+ | 'bridge_failed'
677
+ /** Everything else. Classifier fallback — should narrow over time as new modes appear. */
678
+ | 'unknown';
679
+ /**
680
+ * Structured failure envelope. Replaces the prior `error?: string` so
681
+ * callers can switch on `kind`, respect `retryable`, and apply
682
+ * provider-suggested `backoffMs` instead of guessing from substring
683
+ * matches on the message.
684
+ */
685
+ interface SubagentError {
686
+ /** Discriminator — see SubagentErrorKind doc strings for semantics. */
687
+ kind: SubagentErrorKind;
688
+ /** Human-readable summary, suitable for direct UI display. Always populated. */
689
+ message: string;
690
+ /** True if the operation can be retried as-is (possibly with backoff). */
691
+ retryable: boolean;
692
+ /** Suggested backoff before retry, in ms. Set for `provider_rate_limit` and `provider_5xx`. */
693
+ backoffMs?: number;
694
+ /** Original cause snapshot for diagnostics — never used for control flow. */
695
+ cause?: {
696
+ name: string;
697
+ message: string;
698
+ stack?: string;
699
+ };
700
+ }
701
+ interface TaskResult<T = unknown> {
702
+ subagentId: string;
703
+ taskId: string;
704
+ status: 'success' | 'failed' | 'timeout' | 'stopped';
705
+ result?: T;
706
+ /**
707
+ * Structured failure envelope. Populated whenever `status !== 'success'`.
708
+ * Prefer reading `error.kind` over substring-matching `error.message`.
709
+ */
710
+ error?: SubagentError;
711
+ iterations: number;
712
+ toolCalls: number;
713
+ durationMs: number;
714
+ }
715
+ interface TaskSpec {
716
+ id: string;
717
+ description: string;
718
+ subagentId?: string;
719
+ priority?: number;
720
+ maxToolCalls?: number;
721
+ timeoutMs?: number;
722
+ context?: Record<string, unknown>;
723
+ }
724
+ interface DoneCondition {
725
+ type: 'iterations' | 'tool_calls' | 'output_match' | 'custom' | 'all_tasks_done' | 'directive';
726
+ maxIterations?: number;
727
+ maxToolCalls?: number;
728
+ pattern?: string;
729
+ predicate?: string;
730
+ /**
731
+ * For `directive` type — stop when model emits [done] and keep going
732
+ * on [continue]/[next step]/[proceed] WITHOUT returning to the outer runner.
733
+ * When false (default), the runner behaves normally (one agent.run per loop).
734
+ * When true, the runner passes `autonomousContinue: true` to the agent and
735
+ * re-runs internally when the model signals continue.
736
+ */
737
+ autonomous?: boolean;
738
+ }
739
+ interface MultiAgentConfig {
740
+ coordinatorId: string;
741
+ leaderSystemPrompt?: string;
742
+ subagents?: SubagentConfig[];
743
+ maxConcurrent?: number;
744
+ doneCondition: DoneCondition;
745
+ timeoutMs?: number;
746
+ /**
747
+ * Optional default budget applied to every spawned subagent. Per-subagent
748
+ * fields in `SubagentConfig` override these. Coordinator enforces them by
749
+ * constructing a `SubagentBudget` per spawn — see `SubagentRunContext.budget`.
750
+ */
751
+ defaultBudget?: {
752
+ maxIterations?: number;
753
+ maxToolCalls?: number;
754
+ maxTokens?: number;
755
+ maxCostUsd?: number;
756
+ timeoutMs?: number;
757
+ idleTimeoutMs?: number;
758
+ };
759
+ }
760
+ interface SpawnResult {
761
+ subagentId: string;
762
+ agentId: string;
763
+ }
764
+ interface TaskDelegation {
765
+ task: TaskSpec;
766
+ subagentId: string;
767
+ }
768
+ interface CoordinatorEvents {
769
+ 'task.assigned': {
770
+ task: TaskSpec;
771
+ subagentId: string;
772
+ };
773
+ 'task.completed': {
774
+ task: TaskSpec;
775
+ result: TaskResult;
776
+ };
777
+ 'subagent.started': {
778
+ subagent: SubagentConfig;
779
+ };
780
+ 'subagent.stopped': {
781
+ subagentId: string;
782
+ reason: string;
783
+ };
784
+ done: {
785
+ results: TaskResult[];
786
+ totalIterations: number;
787
+ };
788
+ }
789
+ interface MultiAgentCoordinator {
790
+ readonly coordinatorId: string;
791
+ readonly config: MultiAgentConfig;
792
+ spawn(subagent: SubagentConfig): Promise<SpawnResult>;
793
+ assign(task: TaskSpec): Promise<void>;
794
+ delegate(to: string, msg: BridgeMessage): Promise<void>;
795
+ stop(subagentId: string): Promise<void>;
796
+ stopAll(): Promise<void>;
797
+ /**
798
+ * Stop a subagent and remove it from the coordinator. Releases all
799
+ * associated resources. The subagent id can be reused in a future spawn.
800
+ */
801
+ remove(subagentId: string): Promise<void>;
802
+ getStatus(): CoordinatorStatus;
803
+ /**
804
+ * Wait for one or more tasks to complete and return their results.
805
+ * If a task is already done when called, returns immediately.
806
+ * Resolves to an array in the same order as `taskIds`.
807
+ */
808
+ awaitTasks(taskIds: string[]): Promise<TaskResult[]>;
809
+ /** Snapshot of completed task results. */
810
+ results(): readonly TaskResult[];
811
+ }
812
+ /**
813
+ * Caller-supplied runner that actually executes a task. The coordinator
814
+ * provides isolated state (own budget, own AbortSignal, own bridge handle)
815
+ * and enforces concurrency limits — the runner just runs the task and reports
816
+ * the outcome. This is the injection seam that decouples the coordinator
817
+ * from `Agent` so it can be tested with mocks and reused for non-Agent
818
+ * subagents (workers, MCP-driven subagents, etc.).
819
+ */
820
+ type SubagentRunner = (task: TaskSpec, ctx: SubagentRunContext) => Promise<SubagentRunOutcome>;
821
+ interface SubagentRunContext {
822
+ subagentId: string;
823
+ config: SubagentConfig;
824
+ budget: SubagentBudget;
825
+ signal: AbortSignal;
826
+ /** Null until `setSubagentBridge` is called for this subagent. */
827
+ bridge: AgentBridge | null;
828
+ }
829
+ interface SubagentRunOutcome {
830
+ result?: unknown;
831
+ iterations: number;
832
+ toolCalls: number;
833
+ }
834
+ interface CoordinatorStatus {
835
+ coordinatorId: string;
836
+ subagents: {
837
+ id: string;
838
+ name: string;
839
+ status: 'running' | 'idle' | 'stopped' | 'error';
840
+ currentTask?: string;
841
+ /** Cumulative budget auto-extensions granted to this subagent, when the
842
+ * status is produced by a Director that tracks them. */
843
+ extensions?: number;
844
+ }[];
845
+ pendingTasks: number;
846
+ completedTasks: number;
847
+ totalIterations: number;
848
+ done: boolean;
849
+ }
850
+ interface SubagentContext {
851
+ subagentId: string;
852
+ tasks: TaskSpec[];
853
+ /**
854
+ * Two-phase initialization: `spawn()` creates the subagent before the
855
+ * bridge is wired (`setSubagentBridge()`), so `parentBridge` is nullable
856
+ * by design. Readers must `hasParentBridge()`-guard or null-check before
857
+ * use; the prior `null as unknown as AgentBridge` cast was a type lie
858
+ * that hid this from the compiler.
859
+ */
860
+ parentBridge: AgentBridge | null;
861
+ doneCondition: DoneCondition;
862
+ maxConcurrent: number;
863
+ }
864
+
865
+ /**
866
+ * Single fleet-wide event with subagent attribution. Whatever a child
867
+ * agent emits on its own EventBus gets re-published here, prefixed with
868
+ * `subagentId` so a single subscriber can multiplex across the fleet.
869
+ *
870
+ * The director uses `FleetBus.filter('tool.executed', …)` to see every
871
+ * tool call across the fleet; the TUI uses
872
+ * `FleetBus.subscribe(id, handler)` to render a per-subagent panel.
873
+ */
874
+ interface FleetEvent {
875
+ subagentId: string;
876
+ taskId?: string;
877
+ ts: number;
878
+ type: string;
879
+ payload: unknown;
880
+ }
881
+ type FleetHandler = (event: FleetEvent) => void;
882
+ /**
883
+ * Fan-in for per-subagent EventBuses. Each subagent's bus is plugged in
884
+ * via `attach()`; the FleetBus re-emits every event with subagent
885
+ * attribution. Detachment is automatic via the returned disposer — call
886
+ * it when a subagent terminates so we don't leak listeners.
887
+ *
888
+ * The bus exposes two subscription modes: by `subagentId` (everything
889
+ * from one child) and by `type` (one event-type across the fleet). They
890
+ * compose — if you need a per-subagent + per-type slice, subscribe by
891
+ * type and filter on `event.subagentId` in your handler.
892
+ */
893
+ declare class FleetBus {
894
+ private readonly byId;
895
+ private readonly byType;
896
+ private readonly any;
897
+ /**
898
+ * Hook a subagent's EventBus into the fleet. Uses `onAny()` (an alias for
899
+ * `onPattern('*')`) to forward all events with subagent attribution, so
900
+ * new kernel event types are automatically forwarded without any manual
901
+ * registration. `subagent.*` events are excluded because they originate
902
+ * from MultiAgentHost on the parent bus, not the subagent's own bus.
903
+ *
904
+ * Returns a disposer that detaches every subscription; call on
905
+ * subagent teardown so the listeners don't outlive the run.
906
+ */
907
+ attach(subagentId: string, bus: EventBus, taskId?: string): () => void;
908
+ /** Subscribe to every event from one subagent. */
909
+ subscribe(subagentId: string, handler: FleetHandler): () => void;
910
+ /** Subscribe to one event type across all subagents. */
911
+ filter(type: string, handler: FleetHandler): () => void;
912
+ /** Subscribe to literally everything. The fleet roll-up uses this. */
913
+ onAny(handler: FleetHandler): () => void;
914
+ emit(event: FleetEvent): void;
915
+ }
916
+ /**
917
+ * Roll-up of token usage + cost across an entire director run. The
918
+ * director's `fleet_status` tool returns this so the model can reason
919
+ * about budget in its next turn ("the researcher already burned $0.40,
920
+ * lean on summaries for the next task").
921
+ */
922
+ interface FleetUsage {
923
+ total: {
924
+ input: number;
925
+ output: number;
926
+ cacheRead: number;
927
+ cacheWrite: number;
928
+ cost: number;
929
+ };
930
+ perSubagent: Record<string, SubagentUsageSnapshot>;
931
+ }
932
+ interface SubagentUsageSnapshot {
933
+ subagentId: string;
934
+ provider?: string;
935
+ model?: string;
936
+ input: number;
937
+ output: number;
938
+ cacheRead: number;
939
+ cacheWrite: number;
940
+ cost: number;
941
+ toolCalls: number;
942
+ iterations: number;
943
+ startedAt: number;
944
+ lastEventAt: number;
945
+ }
946
+ /**
947
+ * Aggregates provider.response + tool.executed events from the FleetBus
948
+ * into a live `FleetUsage` snapshot. Costs are computed by the caller
949
+ * via a `priceLookup(subagentId)` so we don't bake provider-pricing
950
+ * coupling into core; the CLI/tests supply a function that resolves
951
+ * each subagent's per-token rates from the models registry.
952
+ */
953
+ declare class FleetUsageAggregator {
954
+ private readonly priceLookup?;
955
+ private readonly metaLookup?;
956
+ private readonly perSubagent;
957
+ private readonly total;
958
+ private readonly unsub;
959
+ constructor(bus: FleetBus, priceLookup?: ((subagentId: string, provider?: string, model?: string) => {
960
+ input?: number;
961
+ output?: number;
962
+ cacheRead?: number;
963
+ cacheWrite?: number;
964
+ } | undefined) | undefined, metaLookup?: ((subagentId: string) => {
965
+ provider?: string;
966
+ model?: string;
967
+ } | undefined) | undefined);
968
+ /**
969
+ * Remove a terminated subagent's data from the aggregator and subtract its
970
+ * contribution from the running totals. Call this when a subagent is removed
971
+ * from the fleet so the aggregator doesn't accumulate unbounded data for
972
+ * entities that will never emit events again.
973
+ */
974
+ removeSubagent(subagentId: string): void;
975
+ /** Disposes all fleet-bus subscriptions. Call when the aggregator is no longer needed. */
976
+ dispose(): void;
977
+ /** Live snapshot — safe to call from a tool's execute() body. */
978
+ snapshot(): FleetUsage;
979
+ private ensure;
980
+ private onProviderResponse;
981
+ private onToolExecuted;
982
+ private onIterationStarted;
983
+ }
984
+
985
+ /**
986
+ * Caller-supplied factory that builds an isolated `Agent` for a subagent.
987
+ * The factory MUST construct a fresh `Context` per call — sharing context
988
+ * between subagents defeats isolation. Each Agent should also use either
989
+ * its own `EventBus` or a forwarded view, so per-subagent metrics can be
990
+ * attributed correctly.
991
+ */
992
+ type AgentFactory = (config: SubagentConfig) => Promise<AgentFactoryResult>;
993
+ interface AgentFactoryResult {
994
+ agent: Agent;
995
+ /** Event bus the factory wired to this agent — required for budget hookup. */
996
+ events: EventBus;
997
+ /**
998
+ * Optional cleanup hook invoked in the runner's `finally` block once
999
+ * the task ends (success, failure, abort — same exit path). Factories
1000
+ * that own resources scoped to a single task (per-subagent JSONL
1001
+ * writers, transient providers, throwaway containers) implement this
1002
+ * to close them deterministically instead of relying on GC. Errors
1003
+ * thrown here are swallowed so a flaky cleanup can't mask the task's
1004
+ * real result.
1005
+ */
1006
+ dispose?: () => Promise<void> | void;
1007
+ }
1008
+ interface AgentRunnerOptions {
1009
+ factory: AgentFactory;
1010
+ /**
1011
+ * Format a TaskSpec into the user input the agent will receive. Defaults
1012
+ * to `task.description ?? ''`. Override when subagents expect structured
1013
+ * input (e.g. JSON contracts, role-prefixed prompts).
1014
+ */
1015
+ formatTaskInput?: (task: TaskSpec, config: SubagentConfig) => AgentInput;
1016
+ /**
1017
+ * When set, the runner attaches the subagent's EventBus to this FleetBus
1018
+ * on task start and detaches it when the task finishes. This is the
1019
+ * injection seam that lets the TUI fleet panel observe subagent activity
1020
+ * live — without it, FleetBus stays empty.
1021
+ */
1022
+ fleetBus?: FleetBus;
1023
+ }
1024
+ /**
1025
+ * Builds a `SubagentRunner` that drives a real `Agent` per task while honoring
1026
+ * the coordinator's budget and abort signal. This is the production adapter —
1027
+ * the coordinator's `runner` option in CLI/TUI assemblies points here.
1028
+ *
1029
+ * Lifecycle per task:
1030
+ * 1. factory(config) → fresh Agent + EventBus.
1031
+ * 2. Subscribe to events to feed the budget (tool calls, token usage).
1032
+ * 3. Call agent.run(input, { signal }) — the coordinator's signal cancels.
1033
+ * 4. Map RunResult.status onto a `SubagentRunOutcome` or throw on failure.
1034
+ * 5. Unsubscribe and let the factory's resources be GC'd.
1035
+ *
1036
+ * The budget is checked synchronously from event handlers — a runaway agent
1037
+ * that crosses its tool-call limit triggers `BudgetExceededError`, which the
1038
+ * coordinator surfaces as `status: 'failed'` on the task result.
1039
+ */
1040
+ declare function makeAgentSubagentRunner(opts: AgentRunnerOptions): SubagentRunner;
1041
+
1042
+ export { Agent as A, type BridgeMessage as B, type CoordinatorEvents as C, type DoneCondition as D, type ProviderFactory as E, FleetBus as F, type ProviderRegistryView as G, type SlashCommand as H, type SlashCommandRegistryView as I, type SpawnResult as J, SubagentBudget as K, type SubagentConfig as L, type MCPRegistryView as M, type SubagentContext as N, type SubagentError as O, type Plugin as P, type SubagentErrorKind as Q, type SubagentRunContext as R, type SessionWriterView as S, type SubagentRunOutcome as T, type SubagentRunner as U, type SubagentUsageSnapshot as V, type TaskDelegation as W, type TaskResult as X, type TaskSpec as Y, type ToolRegistryView as Z, makeAgentSubagentRunner as _, type AgentBridge as a, type AgentBridgeConfig as b, type AgentFactory as c, type AgentFactoryResult as d, type AgentRunnerOptions as e, type BridgeTransport as f, BudgetExceededError as g, type BudgetKind as h, type BudgetLimits as i, type BudgetNegotiationMode as j, type BudgetThresholdDecision as k, type BudgetThresholdHandler as l, BudgetThresholdSignal as m, type BudgetUsage as n, type CoordinatorStatus as o, type FleetEvent as p, type FleetHandler as q, type FleetUsage as r, FleetUsageAggregator as s, type MetricsSinkView as t, type MultiAgentConfig as u, type MultiAgentCoordinator as v, type PluginAPI as w, type PluginCapabilities as x, type PluginDependency as y, type PluginPipelines as z };