@wrongstack/core 0.277.2 → 0.280.1

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 (83) hide show
  1. package/dist/{agent-bridge-BFJ2ODzI.d.ts → agent-bridge-DXC6QDJ4.d.ts} +1 -1
  2. package/dist/{agent-subagent-runner-BimKihiC.d.ts → agent-subagent-runner-PoqNKiR4.d.ts} +563 -471
  3. package/dist/{compactor-D3BGw26y.d.ts → compactor-U3agvUIG.d.ts} +1 -1
  4. package/dist/{config-DAOjriz9.d.ts → config-Cr3312zc.d.ts} +102 -4
  5. package/dist/coordination/index.d.ts +1087 -998
  6. package/dist/coordination/index.js +12235 -12052
  7. package/dist/coordination/index.js.map +1 -1
  8. package/dist/defaults/index.d.ts +31 -30
  9. package/dist/defaults/index.js +403 -189
  10. package/dist/defaults/index.js.map +1 -1
  11. package/dist/{brain-CCfuEOdp.d.ts → events-Bs2fmldo.d.ts} +117 -112
  12. package/dist/execution/index.d.ts +27 -19
  13. package/dist/execution/index.js +216 -63
  14. package/dist/execution/index.js.map +1 -1
  15. package/dist/execution/prompt-enhancer.d.ts +1 -1
  16. package/dist/execution/prompt-enhancer.js.map +1 -1
  17. package/dist/extension/index.d.ts +8 -7
  18. package/dist/{global-mailbox-Dr4cTKqL.d.ts → global-mailbox-Ct7IorLJ.d.ts} +84 -6
  19. package/dist/{goal-store-C1uH4srH.d.ts → goal-store-C4F6DjC0.d.ts} +1 -1
  20. package/dist/hq/index.d.ts +504 -7
  21. package/dist/hq/index.js +1069 -20
  22. package/dist/hq/index.js.map +1 -1
  23. package/dist/{index-DJXj-dcr.d.ts → index-kidebiDh.d.ts} +8 -5
  24. package/dist/{index-cMEmzCVN.d.ts → index-nP09-oP2.d.ts} +2 -2
  25. package/dist/index.d.ts +153 -76
  26. package/dist/index.js +5791 -3163
  27. package/dist/index.js.map +1 -1
  28. package/dist/infrastructure/index.d.ts +7 -6
  29. package/dist/kernel/index.d.ts +14 -13
  30. package/dist/kernel/index.js +31 -15
  31. package/dist/kernel/index.js.map +1 -1
  32. package/dist/{mailbox-types-DTl7bRH3.d.ts → mailbox-types-BGZWrYTJ.d.ts} +38 -0
  33. package/dist/{mcp-servers-CFb60-pH.d.ts → mcp-servers-D910X5_r.d.ts} +3 -3
  34. package/dist/models/index.d.ts +5 -5
  35. package/dist/models/index.js.map +1 -1
  36. package/dist/{models-registry-5Ufn7f2m.d.ts → models-registry-CLkoOcHk.d.ts} +1 -1
  37. package/dist/{multi-agent-coordinator-CcrcncvG.d.ts → multi-agent-coordinator-CieyUoEL.d.ts} +1 -1
  38. package/dist/{null-fleet-bus-C9KsYyrI.d.ts → null-fleet-bus-DkdmZJ_W.d.ts} +464 -464
  39. package/dist/observability/index.d.ts +3 -2
  40. package/dist/{path-resolver-CEeX9I7O.d.ts → path-resolver-XfZ9eLxG.d.ts} +3 -3
  41. package/dist/{permission-DbsGOA1C.d.ts → permission-Dx6dIqS2.d.ts} +2 -7
  42. package/dist/{permission-policy-BpEea3r7.d.ts → permission-policy-C8vJcnX5.d.ts} +2 -2
  43. package/dist/{pipeline-CEjBjzVA.d.ts → pipeline-BwAP21_4.d.ts} +9 -4
  44. package/dist/{provider-model-resolve-BpfXp3Jj.d.ts → provider-model-resolve-CwQNZWt_.d.ts} +3 -3
  45. package/dist/{provider-runner-CnOSr5BN.d.ts → provider-runner-CYHFImzV.d.ts} +3 -3
  46. package/dist/{retry-policy-Git9WF6d.d.ts → retry-policy-D4feSLk3.d.ts} +1 -1
  47. package/dist/sdd/index.d.ts +11 -10
  48. package/dist/sdd/index.js +2 -2
  49. package/dist/sdd/index.js.map +1 -1
  50. package/dist/secret-scrubber-3MHDDAtm.d.ts +6 -0
  51. package/dist/{secret-vault-DDSMHqIm.d.ts → secret-vault-CImt2XrR.d.ts} +1 -1
  52. package/dist/security/index.d.ts +6 -5
  53. package/dist/security/index.js.map +1 -1
  54. package/dist/{selector-Cq72C0Oy.d.ts → selector-Dy-MzKp1.d.ts} +1 -1
  55. package/dist/{session-event-bridge-DG94B3Bk.d.ts → session-event-bridge-CqdiGnfU.d.ts} +1 -1
  56. package/dist/{session-reader-BzT-iMQT.d.ts → session-reader-Hk0WbNm9.d.ts} +1 -1
  57. package/dist/{skill-DGIXCtdv.d.ts → skill-DHniprNl.d.ts} +15 -1
  58. package/dist/skills/index.d.ts +472 -26
  59. package/dist/skills/index.js +872 -129
  60. package/dist/skills/index.js.map +1 -1
  61. package/dist/storage/index.d.ts +27 -14
  62. package/dist/storage/index.js +264 -85
  63. package/dist/storage/index.js.map +1 -1
  64. package/dist/{strategy-compactor-Bt_ZH6R0.d.ts → strategy-compactor-CQwhbErd.d.ts} +32 -17
  65. package/dist/{todos-checkpoint-CH1pcua9.d.ts → todos-checkpoint-Bk2uP7Ex.d.ts} +6 -6
  66. package/dist/{context-DPlA6kid.d.ts → tool-BkOgs_KL.d.ts} +306 -286
  67. package/dist/{tool-executor-SVFq7IOR.d.ts → tool-executor-SiE1wlZo.d.ts} +9 -9
  68. package/dist/tools/index.d.ts +2 -2
  69. package/dist/tools/index.js.map +1 -1
  70. package/dist/types/index.d.ts +22 -21
  71. package/dist/types/index.js +7 -9
  72. package/dist/types/index.js.map +1 -1
  73. package/dist/utils/index.d.ts +30 -4
  74. package/dist/utils/index.js +50 -1
  75. package/dist/utils/index.js.map +1 -1
  76. package/dist/{worktree-manager-C4YIf1Fa.d.ts → worktree-manager-BjOFF6bt.d.ts} +1 -1
  77. package/dist/{wstack-paths-_NrRovdr.d.ts → wstack-paths-CMl_cYgq.d.ts} +8 -0
  78. package/package.json +1 -1
  79. package/skills/mailbox-bridge/SKILL.md +1 -0
  80. package/skills/plugin-author/SKILL.md +350 -0
  81. package/skills/sdd/SKILL.md +134 -134
  82. package/skills/skill-creator/SKILL.md +45 -7
  83. package/skills/wrongstack-mailbox/SKILL.md +40 -21
@@ -1,141 +1,479 @@
1
- import { c as ToolCallPipelinePayload, d as ToolWrapper, E as ExtensionRegistry, S as SystemPromptContributor, e as ToolRegistry, P as ProviderRegistry, A as AgentPipelines, f as ToolExecutorLike, g as AgentInit, h as AgentInput, R as RunResult } from './index-DJXj-dcr.js';
2
- import { C as Container, R as ReadonlyPipeline, a as Renderer } from './pipeline-CEjBjzVA.js';
3
- import { E as EventBus, a as EventName, L as Listener } from './brain-CCfuEOdp.js';
4
- import { R as RetryPolicy, E as ErrorHandler } from './retry-policy-Git9WF6d.js';
1
+ import { c as ToolCallPipelinePayload, d as ToolWrapper, E as ExtensionRegistry, S as SystemPromptContributor, e as ToolRegistry, P as ProviderRegistry, A as AgentPipelines, f as ToolExecutorLike, g as AgentInit, h as AgentInput, R as RunResult } from './index-kidebiDh.js';
2
+ import { C as Container, R as ReadonlyPipeline, a as Renderer } from './pipeline-BwAP21_4.js';
3
+ import { E as EventBus, a as EventName, L as Listener } from './events-Bs2fmldo.js';
4
+ import { R as RetryPolicy, E as ErrorHandler } from './retry-policy-D4feSLk3.js';
5
5
  import { L as Logger } from './logger-B63L5bTg.js';
6
6
  import { T as Tracer } from './observability-D-HZN_mF.js';
7
- import { P as PermissionPolicy } from './permission-DbsGOA1C.js';
8
- import { U as Usage, C as Context, J as JSONSchema, c as Request, d as Response, i as ContentBlock, r as TextBlock, T as Tool, P as Provider, u as RunOptions } from './context-DPlA6kid.js';
9
- import { W as WireFamily, H as HookEvent, f as HookMatcher, I as InProcessHook, C as Config } from './config-DAOjriz9.js';
7
+ import { P as PermissionPolicy } from './permission-Dx6dIqS2.js';
8
+ import { C as Context, J as JSONSchema, c as Request, d as Response, i as ContentBlock, s as TextBlock, T as Tool, P as Provider, v as RunOptions, U as Usage } from './tool-BkOgs_KL.js';
9
+ import { W as WireFamily, H as HookEvent, f as HookMatcher, I as InProcessHook, C as Config, M as ModelsRegistry, h as ModelRuntimeConfig } from './config-Cr3312zc.js';
10
+ import { b as Mailbox } from './mailbox-types-BGZWrYTJ.js';
11
+
12
+ type BridgeMessageType = 'task' | 'result' | 'progress' | 'error' | 'heartbeat' | 'stop' | 'delegate' | 'budget_threshold';
13
+ interface BridgeMessage<T = unknown> {
14
+ id: string;
15
+ type: BridgeMessageType;
16
+ from: string;
17
+ to?: string | undefined;
18
+ payload: T;
19
+ timestamp: number;
20
+ priority?: 'low' | 'normal' | 'high' | 'critical' | undefined;
21
+ }
22
+ interface AgentBridgeConfig {
23
+ agentId: string;
24
+ coordinatorId: string;
25
+ timeoutMs?: number | undefined;
26
+ bufferSize?: number | undefined;
27
+ }
28
+ interface AgentBridge {
29
+ readonly agentId: string;
30
+ readonly coordinatorId: string;
31
+ send(msg: BridgeMessage): Promise<void>;
32
+ broadcast(msg: BridgeMessage): Promise<void>;
33
+ subscribe(handler: (msg: BridgeMessage) => void | Promise<void>): () => void;
34
+ request<T>(msg: BridgeMessage, timeoutMs?: number): Promise<BridgeMessage<T>> | undefined;
35
+ stop(): Promise<void>;
36
+ }
37
+ interface BridgeTransport {
38
+ send(msg: BridgeMessage, to: string): Promise<void>;
39
+ subscribe(agentId: string, handler: (msg: BridgeMessage) => void | Promise<void>): () => void;
40
+ close(agentId: string): Promise<void>;
41
+ }
10
42
 
11
43
  /**
12
- * Single fleet-wide event with subagent attribution. Whatever a child
13
- * agent emits on its own EventBus gets re-published here, prefixed with
14
- * `subagentId` so a single subscriber can multiplex across the fleet.
44
+ * A slash command registered with the CLI or available to plugins.
45
+ * Plugins receive a view of the registry via PluginAPI.slashCommands.
15
46
  *
16
- * The director uses `FleetBus.filter('tool.executed', …)` to see every
17
- * tool call across the fleet; the TUI uses
18
- * `FleetBus.subscribe(id, handler)` to render a per-subagent panel.
47
+ * Commands registered by plugins use a namespaced name: `pluginName:commandName`.
48
+ * This prevents collisions with built-in commands and other plugins.
19
49
  */
20
- interface FleetEvent {
21
- subagentId: string;
22
- taskId?: string | undefined;
23
- ts: number;
50
+ interface SlashCommand {
51
+ /** Unique command name. For plugins: `pluginName:commandName`. */
52
+ name: string;
53
+ /** Short aliases — also prefixed automatically: `pluginName:alias`. */
54
+ aliases?: string[] | undefined;
55
+ description: string;
56
+ /**
57
+ * Category used to group commands in the slash picker. Defaults to 'App'
58
+ * when omitted.
59
+ */
60
+ category?: 'Run' | 'Session' | 'Inspect' | 'Agent' | 'Config' | 'App' | undefined;
61
+ /**
62
+ * Optional compact argument hint for interactive menus. This is not parsed
63
+ * by the registry; it only helps TUI/REPL surfaces show the expected shape,
64
+ * for example `[list|install <alias>|disable <name>]`.
65
+ */
66
+ argsHint?: string | undefined;
67
+ /**
68
+ * Optional detailed help shown by `/help <name>`. Use this for usage,
69
+ * arguments, examples, side-effects — anything that doesn't fit in
70
+ * `description`. Renders verbatim, so format with line breaks.
71
+ * If absent, `/help <name>` falls back to `description`.
72
+ */
73
+ help?: string | undefined;
74
+ /**
75
+ * When true, this command does not appear in the slash-picker list
76
+ * triggered by typing `/` alone. It is still dispatchable and
77
+ * searchable when the user types a matching prefix (e.g. `/f`).
78
+ * Defaults to false.
79
+ */
80
+ hidden?: boolean | undefined;
81
+ /**
82
+ * Execute the command.
83
+ * @param args Everything after the command name (trimmed by dispatch).
84
+ * @param ctx The current agent context.
85
+ * @returns `{ exit: true }` to quit the REPL. `{ message }` to print and
86
+ * continue. `{ runText }` to send a follow-up user-role message to the
87
+ * model immediately (e.g. `/steer <text>` builds a STEERING preamble
88
+ * here and asks the TUI to run it as the next turn). The TUI prints
89
+ * `message` first (if any) so the user sees the slash result before
90
+ * the model's response starts streaming. `{ metadata }` carries
91
+ * structured data for the REPL/TUI to act on (e.g. SDD session state).
92
+ */
93
+ run(args: string, ctx?: Context | undefined): Promise<{
94
+ exit?: boolean | undefined;
95
+ message?: string | undefined;
96
+ runText?: string | undefined;
97
+ metadata?: Record<string, unknown>;
98
+ } | void>;
99
+ }
100
+
101
+ interface ToolRegistryView {
102
+ register(t: Tool): void;
103
+ unregister(name: string): void;
104
+ /** Wrap (decorate) an existing tool. The wrapper gets the current tool and returns the decorated version. */
105
+ wrap(name: string, wrapper: ToolWrapper): void;
106
+ get(name: string): Tool | undefined;
107
+ list(): Tool[];
108
+ }
109
+ interface ProviderFactory {
24
110
  type: string;
25
- payload: unknown;
111
+ family: WireFamily;
112
+ create(cfg: unknown): Provider;
113
+ }
114
+ interface ProviderRegistryView {
115
+ register(f: ProviderFactory): void;
116
+ unregister(type: string): boolean;
117
+ create(cfg: {
118
+ type: string;
119
+ } & Record<string, unknown>): Provider;
120
+ list(): string[];
121
+ }
122
+ interface MCPRegistryView {
123
+ start(cfg: unknown): Promise<void>;
124
+ stop(name: string): Promise<void>;
125
+ restart(name: string): Promise<void>;
126
+ list(): {
127
+ name: string;
128
+ state: string;
129
+ toolCount: number;
130
+ }[];
131
+ }
132
+ interface SlashCommandRegistryView {
133
+ register(cmd: SlashCommand): void;
134
+ unregister(name: string): boolean;
135
+ get(name: string): SlashCommand | undefined;
136
+ list(): SlashCommand[];
26
137
  }
27
- type FleetHandler = (event: FleetEvent) => void;
28
138
  /**
29
- * Fan-in for per-subagent EventBuses. Each subagent's bus is plugged in
30
- * via `attach()`; the FleetBus re-emits every event with subagent
31
- * attribution. Detachment is automatic via the returned disposer — call
32
- * it when a subagent terminates so we don't leak listeners.
139
+ * Read-only view of the session writer. Plugins can append custom events
140
+ * to the JSONL session log and read the transcript path.
33
141
  *
34
- * The bus exposes two subscription modes: by `subagentId` (everything
35
- * from one child) and by `type` (one event-type across the fleet). They
36
- * compose — if you need a per-subagent + per-type slice, subscribe by
37
- * type and filter on `event.subagentId` in your handler.
142
+ * The `append` method accepts any JSON-serializable payload custom
143
+ * event types are persisted verbatim next to the built-in events.
38
144
  */
39
- declare class FleetBus {
40
- private readonly byId;
41
- private readonly byType;
42
- private readonly any;
43
- /**
44
- * Hook a subagent's EventBus into the fleet. Uses `onAny()` (an alias for
45
- * `onPattern('*')`) to forward all events with subagent attribution, so
46
- * new kernel event types are automatically forwarded without any manual
47
- * registration. `subagent.*` events are excluded because they originate
48
- * from MultiAgentHost on the parent bus, not the subagent's own bus.
49
- *
50
- * Returns a disposer that detaches every subscription; call on
51
- * subagent teardown so the listeners don't outlive the run.
52
- */
53
- attach(subagentId: string, bus: EventBus, taskId?: string): () => void;
54
- /** Subscribe to every event from one subagent. */
55
- subscribe(subagentId: string, handler: FleetHandler): () => void;
56
- /** Subscribe to one event type across all subagents. */
57
- filter(type: string, handler: FleetHandler): () => void;
58
- /** Subscribe to literally everything. The fleet roll-up uses this. */
59
- onAny(handler: FleetHandler): () => void;
60
- emit(event: FleetEvent): void;
61
- clear(): void;
145
+ interface SessionWriterView {
146
+ readonly transcriptPath?: string | undefined;
147
+ append(event: Record<string, unknown> & {
148
+ type: string;
149
+ ts: string;
150
+ }): Promise<void>;
62
151
  }
63
152
  /**
64
- * Roll-up of token usage + cost across an entire director run. The
65
- * director's `fleet` tool returns this so the model can reason
66
- * about budget in its next turn ("the researcher already burned $0.40,
67
- * lean on summaries for the next task").
153
+ * Metrics sink scoped to a plugin. The host auto-prefixes metric names
154
+ * with `plugin.<pluginName>.` so plugins don't need to namespace
155
+ * manually. Plugins call counter/histogram/gauge directly; the values
156
+ * flow to the host's MetricsSink (Prometheus, OTLP, or noop).
68
157
  */
69
- interface FleetUsage {
70
- total: {
71
- input: number;
72
- output: number;
73
- cacheRead: number;
74
- cacheWrite: number;
75
- cost: number;
76
- };
77
- perSubagent: Record<string, SubagentUsageSnapshot>;
78
- }
79
- interface SubagentUsageSnapshot {
80
- subagentId: string;
81
- provider?: string | undefined;
82
- model?: string | undefined;
83
- input: number;
84
- output: number;
85
- cacheRead: number;
86
- cacheWrite: number;
87
- cost: number;
88
- toolCalls: number;
89
- iterations: number;
90
- startedAt: number;
91
- lastEventAt: number;
158
+ interface MetricsSinkView {
159
+ counter(name: string, value?: number | undefined, labels?: Record<string, string>): void;
160
+ histogram(name: string, value: number, labels?: Record<string, string>): void;
161
+ gauge(name: string, value: number, labels?: Record<string, string>): void;
92
162
  }
93
163
  /**
94
- * Aggregates provider.response + tool.executed events from the FleetBus
95
- * into a live `FleetUsage` snapshot. Costs are computed by the caller
96
- * via a `priceLookup(subagentId)` so we don't bake provider-pricing
97
- * coupling into core; the CLI/tests supply a function that resolves
98
- * each subagent's per-token rates from the models registry.
164
+ * Options for a single `api.llm.complete()` call. Everything is
165
+ * optional omitted fields fall back first to the plugin's own
166
+ * configured defaults (`config.extensions[<name>].llm`), then to the
167
+ * host session's provider/model.
99
168
  */
100
- declare class FleetUsageAggregator {
101
- private readonly priceLookup?;
102
- private readonly metaLookup?;
103
- private readonly perSubagent;
104
- private readonly total;
105
- private readonly unsub;
106
- constructor(bus: FleetBus, priceLookup?: ((subagentId: string, provider?: string | undefined, model?: string | undefined) => {
107
- input?: number | undefined;
108
- output?: number | undefined;
109
- cacheRead?: number | undefined;
110
- cacheWrite?: number | undefined;
111
- } | undefined) | undefined, metaLookup?: ((subagentId: string) => {
112
- provider?: string | undefined;
113
- model?: string | undefined;
114
- } | undefined) | undefined);
169
+ interface PluginLLMOptions {
170
+ /** System prompt for this call. */
171
+ system?: string | undefined;
172
+ /** Model override (e.g. `claude-haiku-4-5`). */
173
+ model?: string | undefined;
115
174
  /**
116
- * Remove a terminated subagent's data from the aggregator and subtract its
117
- * contribution from the running totals. Call this when a subagent is removed
118
- * from the fleet so the aggregator doesn't accumulate unbounded data for
119
- * entities that will never emit events again.
175
+ * Provider override by configured provider name (a key of
176
+ * `config.providers`, e.g. `anthropic`, `openai`, `omniroute`).
177
+ * When omitted the host session's provider is used.
120
178
  */
121
- removeSubagent(subagentId: string): void;
122
- /** Disposes all fleet-bus subscriptions. Call when the aggregator is no longer needed. */
123
- dispose(): void;
124
- /** Live snapshot — safe to call from a tool's execute() body. */
125
- snapshot(): FleetUsage;
126
- private ensure;
127
- private onProviderResponse;
128
- private onToolExecuted;
129
- private onIterationStarted;
179
+ provider?: string | undefined;
180
+ /** Output-token cap. Default 2048, hard-capped by the host. */
181
+ maxTokens?: number | undefined;
182
+ temperature?: number | undefined;
183
+ /** `'json'` asks the provider for a JSON object response. */
184
+ responseFormat?: 'text' | 'json' | undefined;
185
+ /** Abort signal — plugins should pass one for cancellable work. */
186
+ signal?: AbortSignal | undefined;
187
+ }
188
+ interface PluginLLMResult {
189
+ /** Concatenated text blocks of the response. */
190
+ text: string;
191
+ /** The model that actually served the call. */
192
+ model: string;
193
+ /** The provider name the call was routed through. */
194
+ provider: string;
195
+ usage: {
196
+ input: number;
197
+ output: number;
198
+ };
199
+ stopReason: string;
130
200
  }
131
-
132
- type BudgetKind = 'tool_calls' | 'iterations' | 'tokens' | 'timeout' | 'idle_timeout' | 'cost';
133
201
  /**
134
- * Fraction of the wall-clock `timeoutMs` window at which a PROACTIVE extension
135
- * is negotiated BEFORE the deadline is actually crossed. The coordinator
136
- * watchdog (`executeWithTimeout`) arms at `timeoutMs * TIMEOUT_PREEMPT_FRACTION`
137
- * so a still-progressing subagent gets its ceiling raised while it is below the
138
- * limit, and never enters a "timed out" state. Reactive enforcement at the real
202
+ * LLM access for plugins, routed through the host's provider layer
203
+ * plugins never handle API keys themselves. Resolution order for
204
+ * provider/model on each call:
205
+ *
206
+ * 1. `PluginLLMOptions.provider` / `.model` (per call)
207
+ * 2. `config.extensions[<plugin>].llm.provider` / `.model` (per plugin)
208
+ * 3. the host session's active provider/model (default)
209
+ *
210
+ * Exposed as `api.llm` — `undefined` on minimal hosts (tests, the LSP
211
+ * server) that have no provider wired. Always guard:
212
+ * `if (!api.llm) return;`
213
+ */
214
+ interface PluginLLM {
215
+ /** The effective defaults for this plugin (after config resolution). */
216
+ defaults(): {
217
+ provider: string;
218
+ model: string;
219
+ };
220
+ /** One-shot completion. Throws on provider errors. */
221
+ complete(prompt: string, opts?: PluginLLMOptions): Promise<PluginLLMResult>;
222
+ }
223
+ interface PluginPipelines {
224
+ request: ReadonlyPipeline<Request>;
225
+ response: ReadonlyPipeline<Response>;
226
+ toolCall: ReadonlyPipeline<ToolCallPipelinePayload>;
227
+ userInput: ReadonlyPipeline<{
228
+ content: ContentBlock[];
229
+ text: string;
230
+ ctx: Context;
231
+ }>;
232
+ assistantOutput: ReadonlyPipeline<TextBlock>;
233
+ contextWindow: ReadonlyPipeline<Context>;
234
+ [k: string]: ReadonlyPipeline<any>;
235
+ }
236
+ interface PluginAPI {
237
+ container: Container;
238
+ pipelines: PluginPipelines;
239
+ events: EventBus;
240
+ tools: ToolRegistryView;
241
+ providers: ProviderRegistryView;
242
+ mcp: MCPRegistryView;
243
+ slashCommands: SlashCommandRegistryView;
244
+ /** Live session writer — plugins can append custom events here. */
245
+ session: SessionWriterView;
246
+ /** Scoped metrics sink — counters/histograms/gauges auto-namespaced under `plugin.<name>.` */
247
+ metrics: MetricsSinkView;
248
+ /** Registry for agent lifecycle extensions — hooks like beforeRun, beforeIteration, onError, etc. */
249
+ extensions: ExtensionRegistry;
250
+ /**
251
+ * Register a system prompt contributor. Plugins call this to inject
252
+ * ephemeral TextBlocks into the system prompt on every build.
253
+ * Returns an unregister function.
254
+ */
255
+ registerSystemPromptContributor(c: SystemPromptContributor): () => void;
256
+ /**
257
+ * Register an in-process lifecycle hook. `matcher` is a tool-name filter for
258
+ * `PreToolUse`/`PostToolUse` (`"Bash"`, `"edit|write"`, `"*"`) and ignored
259
+ * for other events. The hook can block, rewrite tool input, or inject extra
260
+ * context — see `HookOutcome`. Automatically removed when the plugin is
261
+ * uninstalled. Returns an unregister function.
262
+ */
263
+ registerHook(event: HookEvent, matcher: HookMatcher | undefined, hook: InProcessHook): () => void;
264
+ config: Config;
265
+ log: Logger;
266
+ /**
267
+ * Register a one-time event listener. The handler is automatically removed
268
+ * after the first emission, or when the plugin is uninstalled — whichever
269
+ * comes first.
270
+ */
271
+ onEvent<K extends EventName>(event: K, handler: Listener<K>): () => void;
272
+ /**
273
+ * Subscribe to all events matching a glob-style pattern.
274
+ * `'tool.*'` matches all tool events. `'*'` matches everything.
275
+ * Returns an unsubscribe function.
276
+ */
277
+ onPattern(pattern: string, handler: (event: string, payload: unknown) => void): () => void;
278
+ /**
279
+ * Emit a custom event on the agent's EventBus. Use for inter-plugin
280
+ * communication or to surface plugin-specific state to the host.
281
+ *
282
+ * Custom events use a `pluginName:eventName` convention to avoid
283
+ * collisions with built-in events (e.g. `my-plugin:cache_hit`).
284
+ * The payload is passed through to all subscribers.
285
+ */
286
+ emitCustom(event: string, payload: unknown): void;
287
+ /**
288
+ * Register a callback that fires when the configuration changes at
289
+ * runtime (e.g. via `/config` slash command or programmatic update).
290
+ * The handler receives the new and previous config snapshots.
291
+ * Returns an unsubscribe function.
292
+ */
293
+ onConfigChange(handler: (next: Readonly<Config>, prev: Readonly<Config>) => void): () => void;
294
+ /**
295
+ * The models registry (models.dev-backed catalog of providers, models,
296
+ * and per-token pricing). Optional — some hosts may not construct one
297
+ * (e.g. minimal CLI invocations, tests). Plugins that need pricing
298
+ * data (cost-tracker, billing reports) should treat this as the
299
+ * preferred source when present and fall back to a bundled table
300
+ * otherwise.
301
+ */
302
+ modelsRegistry?: ModelsRegistry | undefined;
303
+ /**
304
+ * The host's project-level mailbox. Optional — minimal hosts (tests,
305
+ * standalone CLI invocations, the LSP server) may not construct one.
306
+ * Plugins that publish status to other agents (todo-listener,
307
+ * session-recap) should treat this as the preferred source when present
308
+ * and gracefully no-op otherwise.
309
+ */
310
+ mailbox?: Mailbox | undefined;
311
+ /**
312
+ * LLM access routed through the host's provider layer. Optional —
313
+ * minimal hosts without a wired provider omit it; plugins must guard
314
+ * (`if (!api.llm) …`). Per-plugin provider/model defaults come from
315
+ * `config.extensions[<name>].llm = { provider, model }`.
316
+ */
317
+ llm?: PluginLLM | undefined;
318
+ }
319
+ /**
320
+ * Capability declaration — informs the host which subsystems a plugin
321
+ * intends to touch. Used for diagnostics and per-plugin enable/disable UX
322
+ * (e.g. "this plugin registers tools — disable to remove them"). Not
323
+ * enforced at runtime: a plugin that declares `tools: false` can still
324
+ * call `api.tools.register()`, but the host can flag the discrepancy.
325
+ */
326
+ interface PluginCapabilities {
327
+ /** Will register tools via `api.tools.register()`. */
328
+ tools?: boolean | undefined;
329
+ /** Will register provider factories via `api.providers.register()`. */
330
+ providers?: boolean | undefined;
331
+ /**
332
+ * Pipelines the plugin hooks into. Use the standard names
333
+ * (`request | response | toolCall | userInput | assistantOutput | contextWindow`)
334
+ * or custom pipeline names exposed by other plugins.
335
+ */
336
+ pipelines?: string[] | undefined;
337
+ /** Will register slash commands via `api.slashCommands.register()`. */
338
+ slashCommands?: boolean | undefined;
339
+ /** Will start MCP servers via `api.mcp.start()`. */
340
+ mcp?: boolean | undefined;
341
+ /**
342
+ * Capabilities required to mutate (wrap, unregister, override) tools
343
+ * the plugin does not own. If empty or omitted, the plugin may only
344
+ * mutate its own tools. Official plugins bypass this check.
345
+ *
346
+ * Example: `['fs.read', 'net.outbound']` allows the plugin to wrap
347
+ * read-only tools, but not `fs.write` or `shell.arbitrary` tools.
348
+ */
349
+ toolMutateCapabilities?: string[] | undefined;
350
+ /**
351
+ * Will register in-process lifecycle hooks via `api.registerHook()`. When
352
+ * false (or omitted by a non-official plugin), the loader either logs a
353
+ * warning or throws — see `LoadPluginsOptions.enforceCapabilities`.
354
+ * Official plugins are not gated.
355
+ */
356
+ hooks?: boolean | undefined;
357
+ }
358
+ /**
359
+ * Structured dependency declaration. The string form (`dependsOn: ['foo']`)
360
+ * is shorthand for `[{ name: 'foo' }]` — both work. Use the structured form
361
+ * when you need a version constraint:
362
+ *
363
+ * dependsOn: [{ name: 'wstack-auth', version: '^1.2.0' }]
364
+ */
365
+ interface PluginDependency {
366
+ name: string;
367
+ /** npm-style semver range. Supports `^`, `~`, exact, and unprefixed. */
368
+ version?: string | undefined;
369
+ }
370
+ interface Plugin {
371
+ name: string;
372
+ version?: string | undefined;
373
+ /** One-line summary for `wstack plugins list` and error messages. */
374
+ description?: string | undefined;
375
+ /** Semver range against the kernel API version (KERNEL_API_VERSION). */
376
+ apiVersion: string;
377
+ /**
378
+ * Capability hints — what subsystems the plugin will register against.
379
+ * Optional; provided for diagnostics and UX. The loader does not enforce
380
+ * these, but mismatch is surfaced via logger at warn level.
381
+ */
382
+ capabilities?: PluginCapabilities | undefined;
383
+ /**
384
+ * JSON Schema for the options under `Config.plugins[<name>].options`.
385
+ * When present, the loader validates that section before calling `setup`
386
+ * and rejects the plugin with a clear error path on failure.
387
+ */
388
+ configSchema?: JSONSchema | undefined;
389
+ /**
390
+ * Mandatory plugin dependencies — loading fails if any are absent or
391
+ * version-incompatible. Accepts both the legacy string-array form and
392
+ * the structured form with version constraints.
393
+ */
394
+ dependsOn?: (string | PluginDependency)[] | undefined;
395
+ /** Optional plugin dependencies — silently skipped if absent. */
396
+ optionalDeps?: (string | PluginDependency)[] | undefined;
397
+ conflictsWith?: string[] | undefined;
398
+ /**
399
+ * Default configuration values, deep-merged under the plugin's options
400
+ * key before `configSchema` validation. User-provided values take
401
+ * precedence over defaults — this is a fallback, not an override.
402
+ *
403
+ * @example
404
+ * defaultConfig: { ttl: 3600, maxSize: 100 }
405
+ */
406
+ defaultConfig?: Record<string, unknown>;
407
+ /**
408
+ * Called by the host to activate the plugin. Receives the `PluginAPI`
409
+ * and an optional `AbortSignal` the plugin should respect for
410
+ * cancellation and timeout. `setup` must complete before the plugin is
411
+ * considered loaded; if it times out the plugin is rejected.
412
+ */
413
+ setup(api: PluginAPI, opts?: {
414
+ signal?: AbortSignal | undefined;
415
+ }): void | Promise<void>;
416
+ /**
417
+ * Called by the host during unload. Receives the same `PluginAPI` instance
418
+ * the plugin saw during `setup` and an optional `AbortSignal`. Teardown
419
+ * is best-effort — a timeout does not prevent other plugins from unloading.
420
+ */
421
+ teardown?(api: PluginAPI, opts?: {
422
+ signal?: AbortSignal | undefined;
423
+ }): void | Promise<void>;
424
+ /**
425
+ * Optional health check. Called by the host (e.g. `/diag plugins` slash
426
+ * command or health endpoint) to surface plugin status. Return
427
+ * `{ ok: false, message: '...' }` when the plugin is degraded.
428
+ */
429
+ health?(): Promise<{
430
+ ok: boolean;
431
+ message?: string | undefined;
432
+ }>;
433
+ }
434
+
435
+ declare class Agent {
436
+ readonly container: Container;
437
+ readonly tools: ToolRegistry;
438
+ readonly providers: ProviderRegistry;
439
+ readonly events: EventBus;
440
+ readonly pipelines: AgentPipelines;
441
+ readonly ctx: Context;
442
+ /** Max agent-loop iterations per run. Mutable so the TUI `/settings` picker
443
+ * can apply a new value to the live session (takes effect next run). */
444
+ maxIterations: number;
445
+ readonly executionStrategy: 'parallel' | 'sequential' | 'smart';
446
+ readonly perIterationOutputCapBytes: number;
447
+ private readonly plugins;
448
+ readonly toolExecutor: ToolExecutorLike;
449
+ readonly autoExtendLimit: boolean;
450
+ private readonly autonomousContinue;
451
+ readonly tracer: Tracer | undefined;
452
+ readonly extensions: ExtensionRegistry;
453
+ private readonly _toolHandler;
454
+ private readonly _responseHandler;
455
+ private readonly _loopHandler;
456
+ private readonly _logger;
457
+ constructor(init: AgentInit);
458
+ get logger(): Logger;
459
+ get retry(): RetryPolicy;
460
+ get errorHandler(): ErrorHandler;
461
+ get permission(): PermissionPolicy;
462
+ get renderer(): Renderer | undefined;
463
+ disableInteractiveConfirmation(): void;
464
+ register(tool: Tool): void;
465
+ use(plugin: Plugin, api: PluginAPI): Promise<void>;
466
+ teardown(): Promise<void>;
467
+ run(userInput: AgentInput, opts?: RunOptions): Promise<RunResult>;
468
+ }
469
+
470
+ type BudgetKind = 'tool_calls' | 'iterations' | 'tokens' | 'timeout' | 'idle_timeout' | 'cost';
471
+ /**
472
+ * Fraction of the wall-clock `timeoutMs` window at which a PROACTIVE extension
473
+ * is negotiated — BEFORE the deadline is actually crossed. The coordinator
474
+ * watchdog (`executeWithTimeout`) arms at `timeoutMs * TIMEOUT_PREEMPT_FRACTION`
475
+ * so a still-progressing subagent gets its ceiling raised while it is below the
476
+ * limit, and never enters a "timed out" state. Reactive enforcement at the real
139
477
  * deadline still stands for the no-progress / denied case. Shared so the asking
140
478
  * side and any future caller agree on the same lead point.
141
479
  */
@@ -450,37 +788,6 @@ declare class SubagentBudget {
450
788
  usage(): BudgetUsage;
451
789
  }
452
790
 
453
- type BridgeMessageType = 'task' | 'result' | 'progress' | 'error' | 'heartbeat' | 'stop' | 'delegate' | 'budget_threshold';
454
- interface BridgeMessage<T = unknown> {
455
- id: string;
456
- type: BridgeMessageType;
457
- from: string;
458
- to?: string | undefined;
459
- payload: T;
460
- timestamp: number;
461
- priority?: 'low' | 'normal' | 'high' | 'critical' | undefined;
462
- }
463
- interface AgentBridgeConfig {
464
- agentId: string;
465
- coordinatorId: string;
466
- timeoutMs?: number | undefined;
467
- bufferSize?: number | undefined;
468
- }
469
- interface AgentBridge {
470
- readonly agentId: string;
471
- readonly coordinatorId: string;
472
- send(msg: BridgeMessage): Promise<void>;
473
- broadcast(msg: BridgeMessage): Promise<void>;
474
- subscribe(handler: (msg: BridgeMessage) => void | Promise<void>): () => void;
475
- request<T>(msg: BridgeMessage, timeoutMs?: number): Promise<BridgeMessage<T>> | undefined;
476
- stop(): Promise<void>;
477
- }
478
- interface BridgeTransport {
479
- send(msg: BridgeMessage, to: string): Promise<void>;
480
- subscribe(agentId: string, handler: (msg: BridgeMessage) => void | Promise<void>): () => void;
481
- close(agentId: string): Promise<void>;
482
- }
483
-
484
791
  interface SubagentConfig {
485
792
  id?: string | undefined;
486
793
  name: string;
@@ -552,6 +859,13 @@ interface SubagentConfig {
552
859
  * → the factory's own default fallback behavior (usually the leader's config).
553
860
  */
554
861
  fallbackModels?: string[] | undefined;
862
+ /**
863
+ * Runtime request overrides for THIS subagent. When present, these are merged
864
+ * over the leader's `Config.modelRuntime` before the subagent request pipeline
865
+ * maps reasoning/cache/parameters onto provider requests. Used by the model
866
+ * matrix to give roles their own reasoning effort without changing the leader.
867
+ */
868
+ modelRuntime?: ModelRuntimeConfig | undefined;
555
869
  /**
556
870
  * Per-subagent session JSONL path. When omitted the orchestrator-
557
871
  * supplied factory derives a path under `<sessionRoot>/<runId>/`.
@@ -813,346 +1127,124 @@ interface SubagentContext {
813
1127
  }
814
1128
 
815
1129
  /**
816
- * A slash command registered with the CLI or available to plugins.
817
- * Plugins receive a view of the registry via PluginAPI.slashCommands.
1130
+ * Single fleet-wide event with subagent attribution. Whatever a child
1131
+ * agent emits on its own EventBus gets re-published here, prefixed with
1132
+ * `subagentId` so a single subscriber can multiplex across the fleet.
818
1133
  *
819
- * Commands registered by plugins use a namespaced name: `pluginName:commandName`.
820
- * This prevents collisions with built-in commands and other plugins.
1134
+ * The director uses `FleetBus.filter('tool.executed', …)` to see every
1135
+ * tool call across the fleet; the TUI uses
1136
+ * `FleetBus.subscribe(id, handler)` to render a per-subagent panel.
821
1137
  */
822
- interface SlashCommand {
823
- /** Unique command name. For plugins: `pluginName:commandName`. */
824
- name: string;
825
- /** Short aliases — also prefixed automatically: `pluginName:alias`. */
826
- aliases?: string[] | undefined;
827
- description: string;
828
- /**
829
- * Category used to group commands in the slash picker. Defaults to 'App'
830
- * when omitted.
831
- */
832
- category?: 'Run' | 'Session' | 'Inspect' | 'Agent' | 'Config' | 'App' | undefined;
833
- /**
834
- * Optional compact argument hint for interactive menus. This is not parsed
835
- * by the registry; it only helps TUI/REPL surfaces show the expected shape,
836
- * for example `[list|install <alias>|disable <name>]`.
837
- */
838
- argsHint?: string | undefined;
839
- /**
840
- * Optional detailed help shown by `/help <name>`. Use this for usage,
841
- * arguments, examples, side-effects — anything that doesn't fit in
842
- * `description`. Renders verbatim, so format with line breaks.
843
- * If absent, `/help <name>` falls back to `description`.
844
- */
845
- help?: string | undefined;
846
- /**
847
- * When true, this command does not appear in the slash-picker list
848
- * triggered by typing `/` alone. It is still dispatchable and
849
- * searchable when the user types a matching prefix (e.g. `/f`).
850
- * Defaults to false.
851
- */
852
- hidden?: boolean | undefined;
853
- /**
854
- * Execute the command.
855
- * @param args Everything after the command name (trimmed by dispatch).
856
- * @param ctx The current agent context.
857
- * @returns `{ exit: true }` to quit the REPL. `{ message }` to print and
858
- * continue. `{ runText }` to send a follow-up user-role message to the
859
- * model immediately (e.g. `/steer <text>` builds a STEERING preamble
860
- * here and asks the TUI to run it as the next turn). The TUI prints
861
- * `message` first (if any) so the user sees the slash result before
862
- * the model's response starts streaming. `{ metadata }` carries
863
- * structured data for the REPL/TUI to act on (e.g. SDD session state).
864
- */
865
- run(args: string, ctx?: Context | undefined): Promise<{
866
- exit?: boolean | undefined;
867
- message?: string | undefined;
868
- runText?: string | undefined;
869
- metadata?: Record<string, unknown>;
870
- } | void>;
871
- }
872
-
873
- interface ToolRegistryView {
874
- register(t: Tool): void;
875
- unregister(name: string): void;
876
- /** Wrap (decorate) an existing tool. The wrapper gets the current tool and returns the decorated version. */
877
- wrap(name: string, wrapper: ToolWrapper): void;
878
- get(name: string): Tool | undefined;
879
- list(): Tool[];
880
- }
881
- interface ProviderFactory {
1138
+ interface FleetEvent {
1139
+ subagentId: string;
1140
+ taskId?: string | undefined;
1141
+ ts: number;
882
1142
  type: string;
883
- family: WireFamily;
884
- create(cfg: unknown): Provider;
885
- }
886
- interface ProviderRegistryView {
887
- register(f: ProviderFactory): void;
888
- unregister(type: string): boolean;
889
- create(cfg: {
890
- type: string;
891
- } & Record<string, unknown>): Provider;
892
- list(): string[];
893
- }
894
- interface MCPRegistryView {
895
- start(cfg: unknown): Promise<void>;
896
- stop(name: string): Promise<void>;
897
- restart(name: string): Promise<void>;
898
- list(): {
899
- name: string;
900
- state: string;
901
- toolCount: number;
902
- }[];
903
- }
904
- interface SlashCommandRegistryView {
905
- register(cmd: SlashCommand): void;
906
- unregister(name: string): boolean;
907
- get(name: string): SlashCommand | undefined;
908
- list(): SlashCommand[];
1143
+ payload: unknown;
909
1144
  }
1145
+ type FleetHandler = (event: FleetEvent) => void;
910
1146
  /**
911
- * Read-only view of the session writer. Plugins can append custom events
912
- * to the JSONL session log and read the transcript path.
1147
+ * Fan-in for per-subagent EventBuses. Each subagent's bus is plugged in
1148
+ * via `attach()`; the FleetBus re-emits every event with subagent
1149
+ * attribution. Detachment is automatic via the returned disposer — call
1150
+ * it when a subagent terminates so we don't leak listeners.
913
1151
  *
914
- * The `append` method accepts any JSON-serializable payload custom
915
- * event types are persisted verbatim next to the built-in events.
916
- */
917
- interface SessionWriterView {
918
- readonly transcriptPath?: string | undefined;
919
- append(event: Record<string, unknown> & {
920
- type: string;
921
- ts: string;
922
- }): Promise<void>;
923
- }
924
- /**
925
- * Metrics sink scoped to a plugin. The host auto-prefixes metric names
926
- * with `plugin.<pluginName>.` so plugins don't need to namespace
927
- * manually. Plugins call counter/histogram/gauge directly; the values
928
- * flow to the host's MetricsSink (Prometheus, OTLP, or noop).
1152
+ * The bus exposes two subscription modes: by `subagentId` (everything
1153
+ * from one child) and by `type` (one event-type across the fleet). They
1154
+ * compose — if you need a per-subagent + per-type slice, subscribe by
1155
+ * type and filter on `event.subagentId` in your handler.
929
1156
  */
930
- interface MetricsSinkView {
931
- counter(name: string, value?: number | undefined, labels?: Record<string, string>): void;
932
- histogram(name: string, value: number, labels?: Record<string, string>): void;
933
- gauge(name: string, value: number, labels?: Record<string, string>): void;
934
- }
935
- interface PluginPipelines {
936
- request: ReadonlyPipeline<Request>;
937
- response: ReadonlyPipeline<Response>;
938
- toolCall: ReadonlyPipeline<ToolCallPipelinePayload>;
939
- userInput: ReadonlyPipeline<{
940
- content: ContentBlock[];
941
- text: string;
942
- ctx: Context;
943
- }>;
944
- assistantOutput: ReadonlyPipeline<TextBlock>;
945
- contextWindow: ReadonlyPipeline<Context>;
946
- [k: string]: ReadonlyPipeline<any>;
947
- }
948
- interface PluginAPI {
949
- container: Container;
950
- pipelines: PluginPipelines;
951
- events: EventBus;
952
- tools: ToolRegistryView;
953
- providers: ProviderRegistryView;
954
- mcp: MCPRegistryView;
955
- slashCommands: SlashCommandRegistryView;
956
- /** Live session writer — plugins can append custom events here. */
957
- session: SessionWriterView;
958
- /** Scoped metrics sink — counters/histograms/gauges auto-namespaced under `plugin.<name>.` */
959
- metrics: MetricsSinkView;
960
- /** Registry for agent lifecycle extensions — hooks like beforeRun, beforeIteration, onError, etc. */
961
- extensions: ExtensionRegistry;
962
- /**
963
- * Register a system prompt contributor. Plugins call this to inject
964
- * ephemeral TextBlocks into the system prompt on every build.
965
- * Returns an unregister function.
966
- */
967
- registerSystemPromptContributor(c: SystemPromptContributor): () => void;
968
- /**
969
- * Register an in-process lifecycle hook. `matcher` is a tool-name filter for
970
- * `PreToolUse`/`PostToolUse` (`"Bash"`, `"edit|write"`, `"*"`) and ignored
971
- * for other events. The hook can block, rewrite tool input, or inject extra
972
- * context — see `HookOutcome`. Automatically removed when the plugin is
973
- * uninstalled. Returns an unregister function.
974
- */
975
- registerHook(event: HookEvent, matcher: HookMatcher | undefined, hook: InProcessHook): () => void;
976
- config: Config;
977
- log: Logger;
978
- /**
979
- * Register a one-time event listener. The handler is automatically removed
980
- * after the first emission, or when the plugin is uninstalled — whichever
981
- * comes first.
982
- */
983
- onEvent<K extends EventName>(event: K, handler: Listener<K>): () => void;
984
- /**
985
- * Subscribe to all events matching a glob-style pattern.
986
- * `'tool.*'` matches all tool events. `'*'` matches everything.
987
- * Returns an unsubscribe function.
988
- */
989
- onPattern(pattern: string, handler: (event: string, payload: unknown) => void): () => void;
1157
+ declare class FleetBus {
1158
+ private readonly byId;
1159
+ private readonly byType;
1160
+ private readonly any;
990
1161
  /**
991
- * Emit a custom event on the agent's EventBus. Use for inter-plugin
992
- * communication or to surface plugin-specific state to the host.
1162
+ * Hook a subagent's EventBus into the fleet. Uses `onAny()` (an alias for
1163
+ * `onPattern('*')`) to forward all events with subagent attribution, so
1164
+ * new kernel event types are automatically forwarded without any manual
1165
+ * registration. `subagent.*` events are excluded because they originate
1166
+ * from MultiAgentHost on the parent bus, not the subagent's own bus.
993
1167
  *
994
- * Custom events use a `pluginName:eventName` convention to avoid
995
- * collisions with built-in events (e.g. `my-plugin:cache_hit`).
996
- * The payload is passed through to all subscribers.
997
- */
998
- emitCustom(event: string, payload: unknown): void;
999
- /**
1000
- * Register a callback that fires when the configuration changes at
1001
- * runtime (e.g. via `/config` slash command or programmatic update).
1002
- * The handler receives the new and previous config snapshots.
1003
- * Returns an unsubscribe function.
1168
+ * Returns a disposer that detaches every subscription; call on
1169
+ * subagent teardown so the listeners don't outlive the run.
1004
1170
  */
1005
- onConfigChange(handler: (next: Readonly<Config>, prev: Readonly<Config>) => void): () => void;
1171
+ attach(subagentId: string, bus: EventBus, taskId?: string): () => void;
1172
+ /** Subscribe to every event from one subagent. */
1173
+ subscribe(subagentId: string, handler: FleetHandler): () => void;
1174
+ /** Subscribe to one event type across all subagents. */
1175
+ filter(type: string, handler: FleetHandler): () => void;
1176
+ /** Subscribe to literally everything. The fleet roll-up uses this. */
1177
+ onAny(handler: FleetHandler): () => void;
1178
+ emit(event: FleetEvent): void;
1179
+ clear(): void;
1006
1180
  }
1007
1181
  /**
1008
- * Capability declaration informs the host which subsystems a plugin
1009
- * intends to touch. Used for diagnostics and per-plugin enable/disable UX
1010
- * (e.g. "this plugin registers tools disable to remove them"). Not
1011
- * enforced at runtime: a plugin that declares `tools: false` can still
1012
- * call `api.tools.register()`, but the host can flag the discrepancy.
1182
+ * Roll-up of token usage + cost across an entire director run. The
1183
+ * director's `fleet` tool returns this so the model can reason
1184
+ * about budget in its next turn ("the researcher already burned $0.40,
1185
+ * lean on summaries for the next task").
1013
1186
  */
1014
- interface PluginCapabilities {
1015
- /** Will register tools via `api.tools.register()`. */
1016
- tools?: boolean | undefined;
1017
- /** Will register provider factories via `api.providers.register()`. */
1018
- providers?: boolean | undefined;
1019
- /**
1020
- * Pipelines the plugin hooks into. Use the standard names
1021
- * (`request | response | toolCall | userInput | assistantOutput | contextWindow`)
1022
- * or custom pipeline names exposed by other plugins.
1023
- */
1024
- pipelines?: string[] | undefined;
1025
- /** Will register slash commands via `api.slashCommands.register()`. */
1026
- slashCommands?: boolean | undefined;
1027
- /** Will start MCP servers via `api.mcp.start()`. */
1028
- mcp?: boolean | undefined;
1029
- /**
1030
- * Capabilities required to mutate (wrap, unregister, override) tools
1031
- * the plugin does not own. If empty or omitted, the plugin may only
1032
- * mutate its own tools. Official plugins bypass this check.
1033
- *
1034
- * Example: `['fs.read', 'net.outbound']` allows the plugin to wrap
1035
- * read-only tools, but not `fs.write` or `shell.arbitrary` tools.
1036
- */
1037
- toolMutateCapabilities?: string[] | undefined;
1038
- /**
1039
- * Will register in-process lifecycle hooks via `api.registerHook()`. When
1040
- * false (or omitted by a non-official plugin), the loader either logs a
1041
- * warning or throws — see `LoadPluginsOptions.enforceCapabilities`.
1042
- * Official plugins are not gated.
1043
- */
1044
- hooks?: boolean | undefined;
1187
+ interface FleetUsage {
1188
+ total: {
1189
+ input: number;
1190
+ output: number;
1191
+ cacheRead: number;
1192
+ cacheWrite: number;
1193
+ cost: number;
1194
+ };
1195
+ perSubagent: Record<string, SubagentUsageSnapshot>;
1196
+ }
1197
+ interface SubagentUsageSnapshot {
1198
+ subagentId: string;
1199
+ provider?: string | undefined;
1200
+ model?: string | undefined;
1201
+ input: number;
1202
+ output: number;
1203
+ cacheRead: number;
1204
+ cacheWrite: number;
1205
+ cost: number;
1206
+ toolCalls: number;
1207
+ iterations: number;
1208
+ startedAt: number;
1209
+ lastEventAt: number;
1045
1210
  }
1046
1211
  /**
1047
- * Structured dependency declaration. The string form (`dependsOn: ['foo']`)
1048
- * is shorthand for `[{ name: 'foo' }]` both work. Use the structured form
1049
- * when you need a version constraint:
1050
- *
1051
- * dependsOn: [{ name: 'wstack-auth', version: '^1.2.0' }]
1212
+ * Aggregates provider.response + tool.executed events from the FleetBus
1213
+ * into a live `FleetUsage` snapshot. Costs are computed by the caller
1214
+ * via a `priceLookup(subagentId)` so we don't bake provider-pricing
1215
+ * coupling into core; the CLI/tests supply a function that resolves
1216
+ * each subagent's per-token rates from the models registry.
1052
1217
  */
1053
- interface PluginDependency {
1054
- name: string;
1055
- /** npm-style semver range. Supports `^`, `~`, exact, and unprefixed. */
1056
- version?: string | undefined;
1057
- }
1058
- interface Plugin {
1059
- name: string;
1060
- version?: string | undefined;
1061
- /** One-line summary for `wstack plugins list` and error messages. */
1062
- description?: string | undefined;
1063
- /** Semver range against the kernel API version (KERNEL_API_VERSION). */
1064
- apiVersion: string;
1065
- /**
1066
- * Capability hints — what subsystems the plugin will register against.
1067
- * Optional; provided for diagnostics and UX. The loader does not enforce
1068
- * these, but mismatch is surfaced via logger at warn level.
1069
- */
1070
- capabilities?: PluginCapabilities | undefined;
1071
- /**
1072
- * JSON Schema for the options under `Config.plugins[<name>].options`.
1073
- * When present, the loader validates that section before calling `setup`
1074
- * and rejects the plugin with a clear error path on failure.
1075
- */
1076
- configSchema?: JSONSchema | undefined;
1077
- /**
1078
- * Mandatory plugin dependencies — loading fails if any are absent or
1079
- * version-incompatible. Accepts both the legacy string-array form and
1080
- * the structured form with version constraints.
1081
- */
1082
- dependsOn?: (string | PluginDependency)[] | undefined;
1083
- /** Optional plugin dependencies — silently skipped if absent. */
1084
- optionalDeps?: (string | PluginDependency)[] | undefined;
1085
- conflictsWith?: string[] | undefined;
1086
- /**
1087
- * Default configuration values, deep-merged under the plugin's options
1088
- * key before `configSchema` validation. User-provided values take
1089
- * precedence over defaults — this is a fallback, not an override.
1090
- *
1091
- * @example
1092
- * defaultConfig: { ttl: 3600, maxSize: 100 }
1093
- */
1094
- defaultConfig?: Record<string, unknown>;
1095
- /**
1096
- * Called by the host to activate the plugin. Receives the `PluginAPI`
1097
- * and an optional `AbortSignal` the plugin should respect for
1098
- * cancellation and timeout. `setup` must complete before the plugin is
1099
- * considered loaded; if it times out the plugin is rejected.
1100
- */
1101
- setup(api: PluginAPI, opts?: {
1102
- signal?: AbortSignal | undefined;
1103
- }): void | Promise<void>;
1104
- /**
1105
- * Called by the host during unload. Receives the same `PluginAPI` instance
1106
- * the plugin saw during `setup` and an optional `AbortSignal`. Teardown
1107
- * is best-effort — a timeout does not prevent other plugins from unloading.
1108
- */
1109
- teardown?(api: PluginAPI, opts?: {
1110
- signal?: AbortSignal | undefined;
1111
- }): void | Promise<void>;
1218
+ declare class FleetUsageAggregator {
1219
+ private readonly priceLookup?;
1220
+ private readonly metaLookup?;
1221
+ private readonly perSubagent;
1222
+ private readonly total;
1223
+ private readonly unsub;
1224
+ constructor(bus: FleetBus, priceLookup?: ((subagentId: string, provider?: string | undefined, model?: string | undefined) => {
1225
+ input?: number | undefined;
1226
+ output?: number | undefined;
1227
+ cacheRead?: number | undefined;
1228
+ cacheWrite?: number | undefined;
1229
+ } | undefined) | undefined, metaLookup?: ((subagentId: string) => {
1230
+ provider?: string | undefined;
1231
+ model?: string | undefined;
1232
+ } | undefined) | undefined);
1112
1233
  /**
1113
- * Optional health check. Called by the host (e.g. `/diag plugins` slash
1114
- * command or health endpoint) to surface plugin status. Return
1115
- * `{ ok: false, message: '...' }` when the plugin is degraded.
1234
+ * Remove a terminated subagent's data from the aggregator and subtract its
1235
+ * contribution from the running totals. Call this when a subagent is removed
1236
+ * from the fleet so the aggregator doesn't accumulate unbounded data for
1237
+ * entities that will never emit events again.
1116
1238
  */
1117
- health?(): Promise<{
1118
- ok: boolean;
1119
- message?: string | undefined;
1120
- }>;
1121
- }
1122
-
1123
- declare class Agent {
1124
- readonly container: Container;
1125
- readonly tools: ToolRegistry;
1126
- readonly providers: ProviderRegistry;
1127
- readonly events: EventBus;
1128
- readonly pipelines: AgentPipelines;
1129
- readonly ctx: Context;
1130
- /** Max agent-loop iterations per run. Mutable so the TUI `/settings` picker
1131
- * can apply a new value to the live session (takes effect next run). */
1132
- maxIterations: number;
1133
- readonly executionStrategy: 'parallel' | 'sequential' | 'smart';
1134
- readonly perIterationOutputCapBytes: number;
1135
- private readonly plugins;
1136
- readonly toolExecutor: ToolExecutorLike;
1137
- readonly autoExtendLimit: boolean;
1138
- private readonly autonomousContinue;
1139
- readonly tracer: Tracer | undefined;
1140
- readonly extensions: ExtensionRegistry;
1141
- private readonly _toolHandler;
1142
- private readonly _responseHandler;
1143
- private readonly _loopHandler;
1144
- private readonly _logger;
1145
- constructor(init: AgentInit);
1146
- get logger(): Logger;
1147
- get retry(): RetryPolicy;
1148
- get errorHandler(): ErrorHandler;
1149
- get permission(): PermissionPolicy;
1150
- get renderer(): Renderer | undefined;
1151
- disableInteractiveConfirmation(): void;
1152
- register(tool: Tool): void;
1153
- use(plugin: Plugin, api: PluginAPI): Promise<void>;
1154
- teardown(): Promise<void>;
1155
- run(userInput: AgentInput, opts?: RunOptions): Promise<RunResult>;
1239
+ removeSubagent(subagentId: string): void;
1240
+ /** Disposes all fleet-bus subscriptions. Call when the aggregator is no longer needed. */
1241
+ dispose(): void;
1242
+ /** Live snapshot — safe to call from a tool's execute() body. */
1243
+ snapshot(): FleetUsage;
1244
+ private ensure;
1245
+ private onProviderResponse;
1246
+ private onToolExecuted;
1247
+ private onIterationStarted;
1156
1248
  }
1157
1249
 
1158
1250
  /**
@@ -1225,4 +1317,4 @@ interface AgentRunnerOptions {
1225
1317
  */
1226
1318
  declare function makeAgentSubagentRunner(opts: AgentRunnerOptions): SubagentRunner;
1227
1319
 
1228
- export { type TaskDelegation as $, type AgentBridge as A, type BridgeTransport as B, type CoordinatorStatus as C, type DoneCondition as D, type BudgetLimits as E, FleetBus as F, type BudgetNegotiationMode as G, type BudgetThresholdDecision as H, type BudgetThresholdHandler as I, BudgetThresholdSignal as J, type BudgetUsage as K, type CoordinatorEvents as L, type MultiAgentCoordinator as M, DECISION_TIMEOUT_MS as N, type FleetEvent as O, type PluginAPI as P, type FleetHandler as Q, SubagentBudget as R, type SubagentConfig as S, type TaskSpec as T, type SubagentContext as U, type SubagentError as V, type SubagentErrorKind as W, type SubagentRunContext as X, type SubagentRunOutcome as Y, type SubagentUsageSnapshot as Z, TIMEOUT_PREEMPT_FRACTION as _, type BridgeMessage as a, makeAgentSubagentRunner as a0, withDisabledToolFiltering as a1, type ProviderFactory as a2, type AgentBridgeConfig as b, type MultiAgentConfig as c, type SubagentRunner as d, type BudgetSessionIdSource as e, type SpawnResult as f, type TaskResult as g, Agent as h, type AgentFactory as i, type FleetUsage as j, FleetUsageAggregator as k, type SlashCommand as l, type PluginPipelines as m, type ToolRegistryView as n, type ProviderRegistryView as o, type MCPRegistryView as p, type SlashCommandRegistryView as q, type SessionWriterView as r, type MetricsSinkView as s, type PluginCapabilities as t, type PluginDependency as u, type Plugin as v, type AgentFactoryResult as w, type AgentRunnerOptions as x, BudgetExceededError as y, type BudgetKind as z };
1320
+ export { type SubagentRunOutcome as $, type AgentBridge as A, type BridgeTransport as B, type CoordinatorStatus as C, type DoneCondition as D, type BudgetKind as E, FleetBus as F, type BudgetLimits as G, type BudgetNegotiationMode as H, type BudgetThresholdDecision as I, type BudgetThresholdHandler as J, BudgetThresholdSignal as K, type BudgetUsage as L, type MultiAgentCoordinator as M, type CoordinatorEvents as N, DECISION_TIMEOUT_MS as O, type PluginAPI as P, type FleetEvent as Q, type FleetHandler as R, type SubagentConfig as S, type TaskSpec as T, type PluginLLMOptions as U, type PluginLLMResult as V, SubagentBudget as W, type SubagentContext as X, type SubagentError as Y, type SubagentErrorKind as Z, type SubagentRunContext as _, type BridgeMessage as a, type SubagentUsageSnapshot as a0, TIMEOUT_PREEMPT_FRACTION as a1, type TaskDelegation as a2, makeAgentSubagentRunner as a3, withDisabledToolFiltering as a4, type ProviderFactory as a5, type AgentBridgeConfig as b, type MultiAgentConfig as c, type SubagentRunner as d, type BudgetSessionIdSource as e, type SpawnResult as f, type TaskResult as g, Agent as h, type AgentFactory as i, type FleetUsage as j, FleetUsageAggregator as k, type SlashCommand as l, type PluginPipelines as m, type ToolRegistryView as n, type ProviderRegistryView as o, type MCPRegistryView as p, type SlashCommandRegistryView as q, type SessionWriterView as r, type MetricsSinkView as s, type PluginLLM as t, type PluginCapabilities as u, type PluginDependency as v, type Plugin as w, type AgentFactoryResult as x, type AgentRunnerOptions as y, BudgetExceededError as z };