@wrongstack/core 0.87.0 → 0.89.3

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-C9P_HPez.d.ts → agent-bridge-DbVe1dHT.d.ts} +1 -1
  2. package/dist/{agent-subagent-runner-2Aq0jOSj.d.ts → agent-subagent-runner-C6eqID1t.d.ts} +477 -476
  3. package/dist/{events-DnRqXaZ3.d.ts → brain-s9DyD_Vf.d.ts} +185 -92
  4. package/dist/{compactor-CJq7LQev.d.ts → compactor-DXLxLcmU.d.ts} +2 -2
  5. package/dist/{config-_DZ7dN-T.d.ts → config-84VaZpH6.d.ts} +45 -38
  6. package/dist/{context-ToHAp4-U.d.ts → context-CNRYfhUv.d.ts} +1 -1
  7. package/dist/coordination/index.d.ts +16 -17
  8. package/dist/coordination/index.js +208 -93
  9. package/dist/coordination/index.js.map +1 -1
  10. package/dist/{default-config-DEXI4jsl.d.ts → default-config-CXsDvOmP.d.ts} +3 -1
  11. package/dist/defaults/index.d.ts +32 -34
  12. package/dist/defaults/index.js +744 -274
  13. package/dist/defaults/index.js.map +1 -1
  14. package/dist/{director-state-CgIc30qi.d.ts → director-state-BfeCUbmk.d.ts} +1 -1
  15. package/dist/execution/index.d.ts +18 -19
  16. package/dist/execution/index.js +79 -64
  17. package/dist/execution/index.js.map +1 -1
  18. package/dist/extension/index.d.ts +9 -10
  19. package/dist/extension/index.js +8 -6
  20. package/dist/extension/index.js.map +1 -1
  21. package/dist/{index-BNOLadHw.d.ts → index-CqrroYcU.d.ts} +13 -15
  22. package/dist/{index-N0_c4bHQ.d.ts → index-D3Nax3YU.d.ts} +11 -10
  23. package/dist/index.d.ts +191 -178
  24. package/dist/index.js +1419 -526
  25. package/dist/index.js.map +1 -1
  26. package/dist/infrastructure/index.d.ts +9 -9
  27. package/dist/infrastructure/index.js +16 -5
  28. package/dist/infrastructure/index.js.map +1 -1
  29. package/dist/kernel/index.d.ts +14 -16
  30. package/dist/kernel/index.js +17 -0
  31. package/dist/kernel/index.js.map +1 -1
  32. package/dist/{logger-B72yyPc6.d.ts → logger-B63L5bTg.d.ts} +1 -1
  33. package/dist/{logger-C_27pj9i.d.ts → logger-B9J5puGM.d.ts} +1 -1
  34. package/dist/{mcp-servers-Dck3T85_.d.ts → mcp-servers-D3E5ixAR.d.ts} +3 -3
  35. package/dist/{mode-CHo2XtHs.d.ts → mode-ARA3HrkY.d.ts} +1 -1
  36. package/dist/models/index.d.ts +5 -5
  37. package/dist/models/index.js +8 -6
  38. package/dist/models/index.js.map +1 -1
  39. package/dist/{models-registry-Be3osGt5.d.ts → models-registry-B6_KfS65.d.ts} +1 -1
  40. package/dist/{models-registry-Boz639EI.d.ts → models-registry-DU64QxQa.d.ts} +1 -1
  41. package/dist/{multi-agent-coordinator-DllpCVkF.d.ts → multi-agent-coordinator-DHNrjPaA.d.ts} +2 -2
  42. package/dist/{null-fleet-bus-BY0AN-sr.d.ts → null-fleet-bus-BzzciAc4.d.ts} +39 -32
  43. package/dist/observability/index.d.ts +3 -3
  44. package/dist/{observability-CoSNZdhX.d.ts → observability-D-HZN_mF.d.ts} +1 -1
  45. package/dist/{parallel-eternal-engine-D402RASp.d.ts → parallel-eternal-engine-CTpcrb9O.d.ts} +34 -33
  46. package/dist/{path-resolver-UPFTsDyD.d.ts → path-resolver-B7hgMAVe.d.ts} +3 -3
  47. package/dist/{permission-14CChMmO.d.ts → permission-BDv7z0mk.d.ts} +7 -2
  48. package/dist/{permission-policy-gW5htOo1.d.ts → permission-policy-dF74EpDp.d.ts} +2 -2
  49. package/dist/{system-prompt-C0rLCeyn.d.ts → pipeline-C1Ad9OjI.d.ts} +73 -73
  50. package/dist/{plan-templates-DRvPgkfZ.d.ts → plan-templates-CNphsz0n.d.ts} +79 -11
  51. package/dist/{provider-runner-COAJM8tC.d.ts → provider-runner-cqvlB_oS.d.ts} +5 -5
  52. package/dist/{retry-policy-DSu6O6rD.d.ts → retry-policy-BcmuT_V0.d.ts} +2 -2
  53. package/dist/sdd/index.d.ts +12 -33
  54. package/dist/sdd/index.js +110 -50
  55. package/dist/sdd/index.js.map +1 -1
  56. package/dist/{secret-scrubber-yGBFQYju.d.ts → secret-vault-DrOhc2i5.d.ts} +7 -7
  57. package/dist/security/index.d.ts +4 -5
  58. package/dist/security/index.js +22 -4
  59. package/dist/security/index.js.map +1 -1
  60. package/dist/{selector-11-fm95U.d.ts → selector-C7wcdqMA.d.ts} +1 -1
  61. package/dist/{session-event-bridge-D0u-x576.d.ts → session-event-bridge-IVzs2GpE.d.ts} +1 -1
  62. package/dist/{session-reader-BQU-toaN.d.ts → session-reader-DDz1Ek4V.d.ts} +2 -2
  63. package/dist/{skill-BJeF2DwY.d.ts → skill-BJxzIyyN.d.ts} +1 -1
  64. package/dist/skills/index.d.ts +1 -1
  65. package/dist/skills/index.js +3 -9
  66. package/dist/skills/index.js.map +1 -1
  67. package/dist/storage/index.d.ts +134 -14
  68. package/dist/storage/index.js +982 -248
  69. package/dist/storage/index.js.map +1 -1
  70. package/dist/{task-graph-CikNdRTG.d.ts → task-graph-DJBqO0EY.d.ts} +1 -1
  71. package/dist/types/index.d.ts +26 -28
  72. package/dist/types/index.js +62 -37
  73. package/dist/types/index.js.map +1 -1
  74. package/dist/utils/expect-defined.d.ts +7 -0
  75. package/dist/utils/expect-defined.js +11 -0
  76. package/dist/utils/expect-defined.js.map +1 -0
  77. package/dist/utils/index.d.ts +11 -20
  78. package/dist/utils/index.js +61 -43
  79. package/dist/utils/index.js.map +1 -1
  80. package/dist/{wstack-paths-BQMvEllz.d.ts → wstack-paths-_lqjzErq.d.ts} +1 -1
  81. package/package.json +5 -1
  82. package/dist/memory-CEXuo7sz.d.ts +0 -16
  83. package/dist/secret-scrubber-3MHDDAtm.d.ts +0 -6
@@ -1,387 +1,176 @@
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-N0_c4bHQ.js';
2
- import { C as Container, e as Renderer, R as ReadonlyPipeline } from './system-prompt-C0rLCeyn.js';
3
- import { E as EventBus, k as EventName, L as Listener } from './events-DnRqXaZ3.js';
4
- import { a as RetryPolicy, E as ErrorHandler } from './retry-policy-DSu6O6rD.js';
5
- import { a as Logger } from './logger-B72yyPc6.js';
6
- import { T as Tracer } from './observability-CoSNZdhX.js';
7
- import { a as PermissionPolicy } from './permission-14CChMmO.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-ToHAp4-U.js';
9
- import { l as HookEvent, n as HookMatcher, I as InProcessHook, a as Config } from './config-_DZ7dN-T.js';
10
- import { W as WireFamily } from './models-registry-Be3osGt5.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-D3Nax3YU.js';
2
+ import { C as Container, R as ReadonlyPipeline, a as Renderer } from './pipeline-C1Ad9OjI.js';
3
+ import { E as EventBus, a as EventName, L as Listener } from './brain-s9DyD_Vf.js';
4
+ import { R as RetryPolicy, E as ErrorHandler } from './retry-policy-BcmuT_V0.js';
5
+ import { L as Logger } from './logger-B63L5bTg.js';
6
+ import { T as Tracer } from './observability-D-HZN_mF.js';
7
+ import { P as PermissionPolicy } from './permission-BDv7z0mk.js';
8
+ import { U as Usage, C as Context, J as JSONSchema, R as Request, b as Response, g as ContentBlock, l as TextBlock, T as Tool, P as Provider, o as RunOptions } from './context-CNRYfhUv.js';
9
+ import { H as HookEvent, a as HookMatcher, I as InProcessHook, C as Config } from './config-84VaZpH6.js';
10
+ import { W as WireFamily } from './models-registry-B6_KfS65.js';
11
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>;
12
+ /**
13
+ * Single fleet-wide event with subagent attribution. Whatever a child
14
+ * agent emits on its own EventBus gets re-published here, prefixed with
15
+ * `subagentId` so a single subscriber can multiplex across the fleet.
16
+ *
17
+ * The director uses `FleetBus.filter('tool.executed', …)` to see every
18
+ * tool call across the fleet; the TUI uses
19
+ * `FleetBus.subscribe(id, handler)` to render a per-subagent panel.
20
+ */
21
+ interface FleetEvent {
22
+ subagentId: string;
23
+ taskId?: string | undefined;
24
+ ts: number;
25
+ type: string;
26
+ payload: unknown;
42
27
  }
43
-
28
+ type FleetHandler = (event: FleetEvent) => void;
44
29
  /**
45
- * A slash command registered with the CLI or available to plugins.
46
- * Plugins receive a view of the registry via PluginAPI.slashCommands.
30
+ * Fan-in for per-subagent EventBuses. Each subagent's bus is plugged in
31
+ * via `attach()`; the FleetBus re-emits every event with subagent
32
+ * attribution. Detachment is automatic via the returned disposer — call
33
+ * it when a subagent terminates so we don't leak listeners.
47
34
  *
48
- * Commands registered by plugins use a namespaced name: `pluginName:commandName`.
49
- * This prevents collisions with built-in commands and other plugins.
35
+ * The bus exposes two subscription modes: by `subagentId` (everything
36
+ * from one child) and by `type` (one event-type across the fleet). They
37
+ * compose — if you need a per-subagent + per-type slice, subscribe by
38
+ * type and filter on `event.subagentId` in your handler.
50
39
  */
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[] | undefined;
56
- description: string;
40
+ declare class FleetBus {
41
+ private readonly byId;
42
+ private readonly byType;
43
+ private readonly any;
57
44
  /**
58
- * Category used to group commands in the slash picker. Defaults to 'App'
59
- * when omitted.
45
+ * Hook a subagent's EventBus into the fleet. Uses `onAny()` (an alias for
46
+ * `onPattern('*')`) to forward all events with subagent attribution, so
47
+ * new kernel event types are automatically forwarded without any manual
48
+ * registration. `subagent.*` events are excluded because they originate
49
+ * from MultiAgentHost on the parent bus, not the subagent's own bus.
50
+ *
51
+ * Returns a disposer that detaches every subscription; call on
52
+ * subagent teardown so the listeners don't outlive the run.
60
53
  */
61
- category?: 'Run' | 'Session' | 'Inspect' | 'Agent' | 'Config' | 'App' | undefined;
54
+ attach(subagentId: string, bus: EventBus, taskId?: string): () => void;
55
+ /** Subscribe to every event from one subagent. */
56
+ subscribe(subagentId: string, handler: FleetHandler): () => void;
57
+ /** Subscribe to one event type across all subagents. */
58
+ filter(type: string, handler: FleetHandler): () => void;
59
+ /** Subscribe to literally everything. The fleet roll-up uses this. */
60
+ onAny(handler: FleetHandler): () => void;
61
+ emit(event: FleetEvent): void;
62
+ clear(): void;
63
+ }
64
+ /**
65
+ * Roll-up of token usage + cost across an entire director run. The
66
+ * director's `fleet_status` tool returns this so the model can reason
67
+ * about budget in its next turn ("the researcher already burned $0.40,
68
+ * lean on summaries for the next task").
69
+ */
70
+ interface FleetUsage {
71
+ total: {
72
+ input: number;
73
+ output: number;
74
+ cacheRead: number;
75
+ cacheWrite: number;
76
+ cost: number;
77
+ };
78
+ perSubagent: Record<string, SubagentUsageSnapshot>;
79
+ }
80
+ interface SubagentUsageSnapshot {
81
+ subagentId: string;
82
+ provider?: string | undefined;
83
+ model?: string | undefined;
84
+ input: number;
85
+ output: number;
86
+ cacheRead: number;
87
+ cacheWrite: number;
88
+ cost: number;
89
+ toolCalls: number;
90
+ iterations: number;
91
+ startedAt: number;
92
+ lastEventAt: number;
93
+ }
94
+ /**
95
+ * Aggregates provider.response + tool.executed events from the FleetBus
96
+ * into a live `FleetUsage` snapshot. Costs are computed by the caller
97
+ * via a `priceLookup(subagentId)` so we don't bake provider-pricing
98
+ * coupling into core; the CLI/tests supply a function that resolves
99
+ * each subagent's per-token rates from the models registry.
100
+ */
101
+ declare class FleetUsageAggregator {
102
+ private readonly priceLookup?;
103
+ private readonly metaLookup?;
104
+ private readonly perSubagent;
105
+ private readonly total;
106
+ private readonly unsub;
107
+ constructor(bus: FleetBus, priceLookup?: ((subagentId: string, provider?: string | undefined, model?: string | undefined) => {
108
+ input?: number | undefined;
109
+ output?: number | undefined;
110
+ cacheRead?: number | undefined;
111
+ cacheWrite?: number | undefined;
112
+ } | undefined) | undefined, metaLookup?: ((subagentId: string) => {
113
+ provider?: string | undefined;
114
+ model?: string | undefined;
115
+ } | undefined) | undefined);
62
116
  /**
63
- * Optional compact argument hint for interactive menus. This is not parsed
64
- * by the registry; it only helps TUI/REPL surfaces show the expected shape,
65
- * for example `[list|install <alias>|disable <name>]`.
117
+ * Remove a terminated subagent's data from the aggregator and subtract its
118
+ * contribution from the running totals. Call this when a subagent is removed
119
+ * from the fleet so the aggregator doesn't accumulate unbounded data for
120
+ * entities that will never emit events again.
66
121
  */
67
- argsHint?: string | undefined;
122
+ removeSubagent(subagentId: string): void;
123
+ /** Disposes all fleet-bus subscriptions. Call when the aggregator is no longer needed. */
124
+ dispose(): void;
125
+ /** Live snapshot — safe to call from a tool's execute() body. */
126
+ snapshot(): FleetUsage;
127
+ private ensure;
128
+ private onProviderResponse;
129
+ private onToolExecuted;
130
+ private onIterationStarted;
131
+ }
132
+
133
+ type BudgetKind = 'tool_calls' | 'iterations' | 'tokens' | 'timeout' | 'idle_timeout' | 'cost';
134
+ declare class BudgetExceededError extends Error {
135
+ readonly kind: BudgetKind;
136
+ readonly limit: number;
137
+ readonly observed: number;
138
+ constructor(kind: BudgetKind, limit: number, observed: number);
139
+ }
140
+ interface BudgetLimits {
141
+ maxIterations?: number | undefined;
142
+ maxToolCalls?: number | undefined;
143
+ maxTokens?: number | undefined;
144
+ /** Estimated USD cost ceiling. */
145
+ maxCostUsd?: number | undefined;
68
146
  /**
69
- * Optional detailed help shown by `/help <name>`. Use this for usage,
70
- * arguments, examples, side-effects anything that doesn't fit in
71
- * `description`. Renders verbatim, so format with line breaks.
72
- * If absent, `/help <name>` falls back to `description`.
147
+ * Hard wall-clock timeout measured from `start()`. Off by default set it
148
+ * explicitly only when a task must finish within an absolute window. For
149
+ * the everyday "don't kill an agent that's still working" guard, prefer
150
+ * `idleTimeoutMs`, which resets on activity.
73
151
  */
74
- help?: string | undefined;
152
+ timeoutMs?: number | undefined;
75
153
  /**
76
- * Execute the command.
77
- * @param args Everything after the command name (trimmed by dispatch).
78
- * @param ctx The current agent context.
79
- * @returns `{ exit: true }` to quit the REPL. `{ message }` to print and
80
- * continue. `{ runText }` to send a follow-up user-role message to the
81
- * model immediately (e.g. `/steer <text>` builds a STEERING preamble
82
- * here and asks the TUI to run it as the next turn). The TUI prints
83
- * `message` first (if any) so the user sees the slash result before
84
- * the model's response starts streaming. `{ metadata }` carries
85
- * structured data for the REPL/TUI to act on (e.g. SDD session state).
154
+ * Idle timeout: the maximum gap (ms) between activity signals (iterations,
155
+ * tool calls, token usage, streamed progress) before the subagent is
156
+ * considered hung and reaped. Unlike `timeoutMs`, an actively-working
157
+ * agent continuously resets this clock via `markActivity()`, so it never
158
+ * trips on a long-but-productive run only on a genuine stall.
86
159
  */
87
- run(args: string, ctx: Context): Promise<{
88
- exit?: boolean | undefined;
89
- message?: string | undefined;
90
- runText?: string | undefined;
91
- metadata?: Record<string, unknown>;
92
- } | void>;
93
- }
94
-
95
- interface ToolRegistryView {
96
- register(t: Tool): void;
97
- unregister(name: string): void;
98
- /** Wrap (decorate) an existing tool. The wrapper gets the current tool and returns the decorated version. */
99
- wrap(name: string, wrapper: ToolWrapper): void;
100
- get(name: string): Tool | undefined;
101
- list(): Tool[];
102
- }
103
- interface ProviderFactory {
104
- type: string;
105
- family: WireFamily;
106
- create(cfg: unknown): Provider;
107
- }
108
- interface ProviderRegistryView {
109
- register(f: ProviderFactory): void;
110
- create(cfg: {
111
- type: string;
112
- } & Record<string, unknown>): Provider;
113
- list(): string[];
114
- }
115
- interface MCPRegistryView {
116
- start(cfg: unknown): Promise<void>;
117
- stop(name: string): Promise<void>;
118
- restart(name: string): Promise<void>;
119
- list(): {
120
- name: string;
121
- state: string;
122
- toolCount: number;
123
- }[];
124
- }
125
- interface SlashCommandRegistryView {
126
- register(cmd: SlashCommand): void;
127
- unregister(name: string): boolean;
128
- get(name: string): SlashCommand | undefined;
129
- list(): SlashCommand[];
160
+ idleTimeoutMs?: number | undefined;
130
161
  }
131
162
  /**
132
- * Read-only view of the session writer. Plugins can append custom events
133
- * to the JSONL session log and read the transcript path.
163
+ * Controls how the budget behaves when `onThreshold` is set and a limit is hit.
134
164
  *
135
- * The `append` method accepts any JSON-serializable payload custom
136
- * event types are persisted verbatim next to the built-in events.
137
- */
138
- interface SessionWriterView {
139
- readonly transcriptPath?: string | undefined;
140
- append(event: Record<string, unknown> & {
141
- type: string;
142
- ts: string;
143
- }): Promise<void>;
144
- }
145
- /**
146
- * Metrics sink scoped to a plugin. The host auto-prefixes metric names
147
- * with `plugin.<pluginName>.` so plugins don't need to namespace
148
- * manually. Plugins call counter/histogram/gauge directly; the values
149
- * flow to the host's MetricsSink (Prometheus, OTLP, or noop).
150
- */
151
- interface MetricsSinkView {
152
- counter(name: string, value?: number | undefined, labels?: Record<string, string>): void;
153
- histogram(name: string, value: number, labels?: Record<string, string>): void;
154
- gauge(name: string, value: number, labels?: Record<string, string>): void;
155
- }
156
- interface PluginPipelines {
157
- request: ReadonlyPipeline<Request>;
158
- response: ReadonlyPipeline<Response>;
159
- toolCall: ReadonlyPipeline<ToolCallPipelinePayload>;
160
- userInput: ReadonlyPipeline<{
161
- content: ContentBlock[];
162
- text: string;
163
- ctx: Context;
164
- }>;
165
- assistantOutput: ReadonlyPipeline<TextBlock>;
166
- contextWindow: ReadonlyPipeline<Context>;
167
- [k: string]: ReadonlyPipeline<any>;
168
- }
169
- interface PluginAPI {
170
- container: Container;
171
- pipelines: PluginPipelines;
172
- events: EventBus;
173
- tools: ToolRegistryView;
174
- providers: ProviderRegistryView;
175
- mcp: MCPRegistryView;
176
- slashCommands: SlashCommandRegistryView;
177
- /** Live session writer — plugins can append custom events here. */
178
- session: SessionWriterView;
179
- /** Scoped metrics sink — counters/histograms/gauges auto-namespaced under `plugin.<name>.` */
180
- metrics: MetricsSinkView;
181
- /** Registry for agent lifecycle extensions — hooks like beforeRun, beforeIteration, onError, etc. */
182
- extensions: ExtensionRegistry;
183
- /**
184
- * Register a system prompt contributor. Plugins call this to inject
185
- * ephemeral TextBlocks into the system prompt on every build.
186
- * Returns an unregister function.
187
- */
188
- registerSystemPromptContributor(c: SystemPromptContributor): () => void;
189
- /**
190
- * Register an in-process lifecycle hook. `matcher` is a tool-name filter for
191
- * `PreToolUse`/`PostToolUse` (`"Bash"`, `"edit|write"`, `"*"`) and ignored
192
- * for other events. The hook can block, rewrite tool input, or inject extra
193
- * context — see `HookOutcome`. Automatically removed when the plugin is
194
- * uninstalled. Returns an unregister function.
195
- */
196
- registerHook(event: HookEvent, matcher: HookMatcher | undefined, hook: InProcessHook): () => void;
197
- config: Config;
198
- log: Logger;
199
- /**
200
- * Register a one-time event listener. The handler is automatically removed
201
- * after the first emission, or when the plugin is uninstalled — whichever
202
- * comes first.
203
- */
204
- onEvent<K extends EventName>(event: K, handler: Listener<K>): () => void;
205
- /**
206
- * Subscribe to all events matching a glob-style pattern.
207
- * `'tool.*'` matches all tool events. `'*'` matches everything.
208
- * Returns an unsubscribe function.
209
- */
210
- onPattern(pattern: string, handler: (event: string, payload: unknown) => void): () => void;
211
- /**
212
- * Emit a custom event on the agent's EventBus. Use for inter-plugin
213
- * communication or to surface plugin-specific state to the host.
214
- *
215
- * Custom events use a `pluginName:eventName` convention to avoid
216
- * collisions with built-in events (e.g. `my-plugin:cache_hit`).
217
- * The payload is passed through to all subscribers.
218
- */
219
- emitCustom(event: string, payload: unknown): void;
220
- /**
221
- * Register a callback that fires when the configuration changes at
222
- * runtime (e.g. via `/config` slash command or programmatic update).
223
- * The handler receives the new and previous config snapshots.
224
- * Returns an unsubscribe function.
225
- */
226
- onConfigChange(handler: (next: Readonly<Config>, prev: Readonly<Config>) => void): () => void;
227
- }
228
- /**
229
- * Capability declaration — informs the host which subsystems a plugin
230
- * intends to touch. Used for diagnostics and per-plugin enable/disable UX
231
- * (e.g. "this plugin registers tools — disable to remove them"). Not
232
- * enforced at runtime: a plugin that declares `tools: false` can still
233
- * call `api.tools.register()`, but the host can flag the discrepancy.
234
- */
235
- interface PluginCapabilities {
236
- /** Will register tools via `api.tools.register()`. */
237
- tools?: boolean | undefined;
238
- /** Will register provider factories via `api.providers.register()`. */
239
- providers?: boolean | undefined;
240
- /**
241
- * Pipelines the plugin hooks into. Use the standard names
242
- * (`request | response | toolCall | userInput | assistantOutput | contextWindow`)
243
- * or custom pipeline names exposed by other plugins.
244
- */
245
- pipelines?: string[] | undefined;
246
- /** Will register slash commands via `api.slashCommands.register()`. */
247
- slashCommands?: boolean | undefined;
248
- /** Will start MCP servers via `api.mcp.start()`. */
249
- mcp?: boolean | undefined;
250
- }
251
- /**
252
- * Structured dependency declaration. The string form (`dependsOn: ['foo']`)
253
- * is shorthand for `[{ name: 'foo' }]` — both work. Use the structured form
254
- * when you need a version constraint:
255
- *
256
- * dependsOn: [{ name: 'wstack-auth', version: '^1.2.0' }]
257
- */
258
- interface PluginDependency {
259
- name: string;
260
- /** npm-style semver range. Supports `^`, `~`, exact, and unprefixed. */
261
- version?: string | undefined;
262
- }
263
- interface Plugin {
264
- name: string;
265
- version?: string | undefined;
266
- /** One-line summary for `wstack plugins list` and error messages. */
267
- description?: string | undefined;
268
- /** Semver range against the kernel API version (KERNEL_API_VERSION). */
269
- apiVersion: string;
270
- /**
271
- * Capability hints — what subsystems the plugin will register against.
272
- * Optional; provided for diagnostics and UX. The loader does not enforce
273
- * these, but mismatch is surfaced via logger at warn level.
274
- */
275
- capabilities?: PluginCapabilities | undefined;
276
- /**
277
- * JSON Schema for the options under `Config.plugins[<name>].options`.
278
- * When present, the loader validates that section before calling `setup`
279
- * and rejects the plugin with a clear error path on failure.
280
- */
281
- configSchema?: JSONSchema | undefined;
282
- /**
283
- * Mandatory plugin dependencies — loading fails if any are absent or
284
- * version-incompatible. Accepts both the legacy string-array form and
285
- * the structured form with version constraints.
286
- */
287
- dependsOn?: (string | PluginDependency)[] | undefined;
288
- /** Optional plugin dependencies — silently skipped if absent. */
289
- optionalDeps?: (string | PluginDependency)[] | undefined;
290
- conflictsWith?: string[] | undefined;
291
- /**
292
- * Default configuration values, deep-merged under the plugin's options
293
- * key before `configSchema` validation. User-provided values take
294
- * precedence over defaults — this is a fallback, not an override.
295
- *
296
- * @example
297
- * defaultConfig: { ttl: 3600, maxSize: 100 }
298
- */
299
- defaultConfig?: Record<string, unknown>;
300
- setup(api: PluginAPI): void | Promise<void>;
301
- teardown?(api: PluginAPI): void | Promise<void>;
302
- /**
303
- * Optional health check. Called by the host (e.g. `/diag plugins` slash
304
- * command or health endpoint) to surface plugin status. Return
305
- * `{ ok: false, message: '...' }` when the plugin is degraded.
306
- */
307
- health?(): Promise<{
308
- ok: boolean;
309
- message?: string | undefined;
310
- }>;
311
- }
312
-
313
- type BridgeMessageType = 'task' | 'result' | 'progress' | 'error' | 'heartbeat' | 'stop' | 'delegate' | 'budget_threshold';
314
- interface BridgeMessage<T = unknown> {
315
- id: string;
316
- type: BridgeMessageType;
317
- from: string;
318
- to?: string | undefined;
319
- payload: T;
320
- timestamp: number;
321
- priority?: 'low' | 'normal' | 'high' | 'critical' | undefined;
322
- }
323
- interface AgentBridgeConfig {
324
- agentId: string;
325
- coordinatorId: string;
326
- timeoutMs?: number | undefined;
327
- bufferSize?: number | undefined;
328
- }
329
- interface AgentBridge {
330
- readonly agentId: string;
331
- readonly coordinatorId: string;
332
- send(msg: BridgeMessage): Promise<void>;
333
- broadcast(msg: BridgeMessage): Promise<void>;
334
- subscribe(handler: (msg: BridgeMessage) => void | Promise<void>): () => void;
335
- request<T>(msg: BridgeMessage, timeoutMs?: number): Promise<BridgeMessage<T>> | undefined;
336
- stop(): Promise<void>;
337
- }
338
- interface BridgeTransport {
339
- send(msg: BridgeMessage, to: string): Promise<void>;
340
- subscribe(agentId: string, handler: (msg: BridgeMessage) => void | Promise<void>): () => void;
341
- close(agentId: string): Promise<void>;
342
- }
343
-
344
- type BudgetKind = 'tool_calls' | 'iterations' | 'tokens' | 'timeout' | 'idle_timeout' | 'cost';
345
- declare class BudgetExceededError extends Error {
346
- readonly kind: BudgetKind;
347
- readonly limit: number;
348
- readonly observed: number;
349
- constructor(kind: BudgetKind, limit: number, observed: number);
350
- }
351
- interface BudgetLimits {
352
- maxIterations?: number | undefined;
353
- maxToolCalls?: number | undefined;
354
- maxTokens?: number | undefined;
355
- /** Estimated USD cost ceiling. */
356
- maxCostUsd?: number | undefined;
357
- /**
358
- * Hard wall-clock timeout measured from `start()`. Off by default — set it
359
- * explicitly only when a task must finish within an absolute window. For
360
- * the everyday "don't kill an agent that's still working" guard, prefer
361
- * `idleTimeoutMs`, which resets on activity.
362
- */
363
- timeoutMs?: number | undefined;
364
- /**
365
- * Idle timeout: the maximum gap (ms) between activity signals (iterations,
366
- * tool calls, token usage, streamed progress) before the subagent is
367
- * considered hung and reaped. Unlike `timeoutMs`, an actively-working
368
- * agent continuously resets this clock via `markActivity()`, so it never
369
- * trips on a long-but-productive run — only on a genuine stall.
370
- */
371
- idleTimeoutMs?: number | undefined;
372
- }
373
- /**
374
- * Controls how the budget behaves when `onThreshold` is set and a limit is hit.
375
- *
376
- * `'auto'` — emit `budget.threshold_reached` on the EventBus and wait for a
377
- * coordinator response (extend/stop). If no listener responds within
378
- * `DECISION_TIMEOUT_MS` the decision defaults to `'stop'`.
379
- * `'sync'` — do not emit any event; treat the threshold as a hard stop and
380
- * throw `BudgetExceededError` synchronously. Useful for fire-and-forget
381
- * subagents that have an `onThreshold` handler for logging/metrics but are
382
- * not wired into a coordinator.
383
- *
384
- * @default 'auto'
165
+ * `'auto'` — emit `budget.threshold_reached` on the EventBus and wait for a
166
+ * coordinator response (extend/stop). If no listener responds within
167
+ * `DECISION_TIMEOUT_MS` the decision defaults to `'stop'`.
168
+ * `'sync'` — do not emit any event; treat the threshold as a hard stop and
169
+ * throw `BudgetExceededError` synchronously. Useful for fire-and-forget
170
+ * subagents that have an `onThreshold` handler for logging/metrics but are
171
+ * not wired into a coordinator.
172
+ *
173
+ * @default 'auto'
385
174
  */
386
175
  type BudgetNegotiationMode = 'auto' | 'sync';
387
176
  interface BudgetUsage {
@@ -578,14 +367,45 @@ declare class SubagentBudget {
578
367
  usage(): BudgetUsage;
579
368
  }
580
369
 
581
- interface SubagentConfig {
582
- id?: string | undefined;
583
- name: string;
584
- role?: string | undefined;
585
- prompt?: string | undefined;
586
- maxIterations?: number | undefined;
587
- maxToolCalls?: number | undefined;
588
- maxTokens?: number | undefined;
370
+ type BridgeMessageType = 'task' | 'result' | 'progress' | 'error' | 'heartbeat' | 'stop' | 'delegate' | 'budget_threshold';
371
+ interface BridgeMessage<T = unknown> {
372
+ id: string;
373
+ type: BridgeMessageType;
374
+ from: string;
375
+ to?: string | undefined;
376
+ payload: T;
377
+ timestamp: number;
378
+ priority?: 'low' | 'normal' | 'high' | 'critical' | undefined;
379
+ }
380
+ interface AgentBridgeConfig {
381
+ agentId: string;
382
+ coordinatorId: string;
383
+ timeoutMs?: number | undefined;
384
+ bufferSize?: number | undefined;
385
+ }
386
+ interface AgentBridge {
387
+ readonly agentId: string;
388
+ readonly coordinatorId: string;
389
+ send(msg: BridgeMessage): Promise<void>;
390
+ broadcast(msg: BridgeMessage): Promise<void>;
391
+ subscribe(handler: (msg: BridgeMessage) => void | Promise<void>): () => void;
392
+ request<T>(msg: BridgeMessage, timeoutMs?: number): Promise<BridgeMessage<T>> | undefined;
393
+ stop(): Promise<void>;
394
+ }
395
+ interface BridgeTransport {
396
+ send(msg: BridgeMessage, to: string): Promise<void>;
397
+ subscribe(agentId: string, handler: (msg: BridgeMessage) => void | Promise<void>): () => void;
398
+ close(agentId: string): Promise<void>;
399
+ }
400
+
401
+ interface SubagentConfig {
402
+ id?: string | undefined;
403
+ name: string;
404
+ role?: string | undefined;
405
+ prompt?: string | undefined;
406
+ maxIterations?: number | undefined;
407
+ maxToolCalls?: number | undefined;
408
+ maxTokens?: number | undefined;
589
409
  maxCostUsd?: number | undefined;
590
410
  /** Hard wall-clock cap (ms) from start. Opt-in; prefer `idleTimeoutMs`. */
591
411
  timeoutMs?: number | undefined;
@@ -876,123 +696,304 @@ interface SubagentContext {
876
696
  }
877
697
 
878
698
  /**
879
- * Single fleet-wide event with subagent attribution. Whatever a child
880
- * agent emits on its own EventBus gets re-published here, prefixed with
881
- * `subagentId` so a single subscriber can multiplex across the fleet.
699
+ * A slash command registered with the CLI or available to plugins.
700
+ * Plugins receive a view of the registry via PluginAPI.slashCommands.
882
701
  *
883
- * The director uses `FleetBus.filter('tool.executed', …)` to see every
884
- * tool call across the fleet; the TUI uses
885
- * `FleetBus.subscribe(id, handler)` to render a per-subagent panel.
702
+ * Commands registered by plugins use a namespaced name: `pluginName:commandName`.
703
+ * This prevents collisions with built-in commands and other plugins.
886
704
  */
887
- interface FleetEvent {
888
- subagentId: string;
889
- taskId?: string | undefined;
890
- ts: number;
705
+ interface SlashCommand {
706
+ /** Unique command name. For plugins: `pluginName:commandName`. */
707
+ name: string;
708
+ /** Short aliases — also prefixed automatically: `pluginName:alias`. */
709
+ aliases?: string[] | undefined;
710
+ description: string;
711
+ /**
712
+ * Category used to group commands in the slash picker. Defaults to 'App'
713
+ * when omitted.
714
+ */
715
+ category?: 'Run' | 'Session' | 'Inspect' | 'Agent' | 'Config' | 'App' | undefined;
716
+ /**
717
+ * Optional compact argument hint for interactive menus. This is not parsed
718
+ * by the registry; it only helps TUI/REPL surfaces show the expected shape,
719
+ * for example `[list|install <alias>|disable <name>]`.
720
+ */
721
+ argsHint?: string | undefined;
722
+ /**
723
+ * Optional detailed help shown by `/help <name>`. Use this for usage,
724
+ * arguments, examples, side-effects — anything that doesn't fit in
725
+ * `description`. Renders verbatim, so format with line breaks.
726
+ * If absent, `/help <name>` falls back to `description`.
727
+ */
728
+ help?: string | undefined;
729
+ /**
730
+ * Execute the command.
731
+ * @param args Everything after the command name (trimmed by dispatch).
732
+ * @param ctx The current agent context.
733
+ * @returns `{ exit: true }` to quit the REPL. `{ message }` to print and
734
+ * continue. `{ runText }` to send a follow-up user-role message to the
735
+ * model immediately (e.g. `/steer <text>` builds a STEERING preamble
736
+ * here and asks the TUI to run it as the next turn). The TUI prints
737
+ * `message` first (if any) so the user sees the slash result before
738
+ * the model's response starts streaming. `{ metadata }` carries
739
+ * structured data for the REPL/TUI to act on (e.g. SDD session state).
740
+ */
741
+ run(args: string, ctx: Context): Promise<{
742
+ exit?: boolean | undefined;
743
+ message?: string | undefined;
744
+ runText?: string | undefined;
745
+ metadata?: Record<string, unknown>;
746
+ } | void>;
747
+ }
748
+
749
+ interface ToolRegistryView {
750
+ register(t: Tool): void;
751
+ unregister(name: string): void;
752
+ /** Wrap (decorate) an existing tool. The wrapper gets the current tool and returns the decorated version. */
753
+ wrap(name: string, wrapper: ToolWrapper): void;
754
+ get(name: string): Tool | undefined;
755
+ list(): Tool[];
756
+ }
757
+ interface ProviderFactory {
891
758
  type: string;
892
- payload: unknown;
759
+ family: WireFamily;
760
+ create(cfg: unknown): Provider;
761
+ }
762
+ interface ProviderRegistryView {
763
+ register(f: ProviderFactory): void;
764
+ create(cfg: {
765
+ type: string;
766
+ } & Record<string, unknown>): Provider;
767
+ list(): string[];
768
+ }
769
+ interface MCPRegistryView {
770
+ start(cfg: unknown): Promise<void>;
771
+ stop(name: string): Promise<void>;
772
+ restart(name: string): Promise<void>;
773
+ list(): {
774
+ name: string;
775
+ state: string;
776
+ toolCount: number;
777
+ }[];
778
+ }
779
+ interface SlashCommandRegistryView {
780
+ register(cmd: SlashCommand): void;
781
+ unregister(name: string): boolean;
782
+ get(name: string): SlashCommand | undefined;
783
+ list(): SlashCommand[];
893
784
  }
894
- type FleetHandler = (event: FleetEvent) => void;
895
785
  /**
896
- * Fan-in for per-subagent EventBuses. Each subagent's bus is plugged in
897
- * via `attach()`; the FleetBus re-emits every event with subagent
898
- * attribution. Detachment is automatic via the returned disposer — call
899
- * it when a subagent terminates so we don't leak listeners.
786
+ * Read-only view of the session writer. Plugins can append custom events
787
+ * to the JSONL session log and read the transcript path.
900
788
  *
901
- * The bus exposes two subscription modes: by `subagentId` (everything
902
- * from one child) and by `type` (one event-type across the fleet). They
903
- * compose — if you need a per-subagent + per-type slice, subscribe by
904
- * type and filter on `event.subagentId` in your handler.
789
+ * The `append` method accepts any JSON-serializable payload custom
790
+ * event types are persisted verbatim next to the built-in events.
905
791
  */
906
- declare class FleetBus {
907
- private readonly byId;
908
- private readonly byType;
909
- private readonly any;
792
+ interface SessionWriterView {
793
+ readonly transcriptPath?: string | undefined;
794
+ append(event: Record<string, unknown> & {
795
+ type: string;
796
+ ts: string;
797
+ }): Promise<void>;
798
+ }
799
+ /**
800
+ * Metrics sink scoped to a plugin. The host auto-prefixes metric names
801
+ * with `plugin.<pluginName>.` so plugins don't need to namespace
802
+ * manually. Plugins call counter/histogram/gauge directly; the values
803
+ * flow to the host's MetricsSink (Prometheus, OTLP, or noop).
804
+ */
805
+ interface MetricsSinkView {
806
+ counter(name: string, value?: number | undefined, labels?: Record<string, string>): void;
807
+ histogram(name: string, value: number, labels?: Record<string, string>): void;
808
+ gauge(name: string, value: number, labels?: Record<string, string>): void;
809
+ }
810
+ interface PluginPipelines {
811
+ request: ReadonlyPipeline<Request>;
812
+ response: ReadonlyPipeline<Response>;
813
+ toolCall: ReadonlyPipeline<ToolCallPipelinePayload>;
814
+ userInput: ReadonlyPipeline<{
815
+ content: ContentBlock[];
816
+ text: string;
817
+ ctx: Context;
818
+ }>;
819
+ assistantOutput: ReadonlyPipeline<TextBlock>;
820
+ contextWindow: ReadonlyPipeline<Context>;
821
+ [k: string]: ReadonlyPipeline<any>;
822
+ }
823
+ interface PluginAPI {
824
+ container: Container;
825
+ pipelines: PluginPipelines;
826
+ events: EventBus;
827
+ tools: ToolRegistryView;
828
+ providers: ProviderRegistryView;
829
+ mcp: MCPRegistryView;
830
+ slashCommands: SlashCommandRegistryView;
831
+ /** Live session writer — plugins can append custom events here. */
832
+ session: SessionWriterView;
833
+ /** Scoped metrics sink — counters/histograms/gauges auto-namespaced under `plugin.<name>.` */
834
+ metrics: MetricsSinkView;
835
+ /** Registry for agent lifecycle extensions — hooks like beforeRun, beforeIteration, onError, etc. */
836
+ extensions: ExtensionRegistry;
910
837
  /**
911
- * Hook a subagent's EventBus into the fleet. Uses `onAny()` (an alias for
912
- * `onPattern('*')`) to forward all events with subagent attribution, so
913
- * new kernel event types are automatically forwarded without any manual
914
- * registration. `subagent.*` events are excluded because they originate
915
- * from MultiAgentHost on the parent bus, not the subagent's own bus.
838
+ * Register a system prompt contributor. Plugins call this to inject
839
+ * ephemeral TextBlocks into the system prompt on every build.
840
+ * Returns an unregister function.
841
+ */
842
+ registerSystemPromptContributor(c: SystemPromptContributor): () => void;
843
+ /**
844
+ * Register an in-process lifecycle hook. `matcher` is a tool-name filter for
845
+ * `PreToolUse`/`PostToolUse` (`"Bash"`, `"edit|write"`, `"*"`) and ignored
846
+ * for other events. The hook can block, rewrite tool input, or inject extra
847
+ * context — see `HookOutcome`. Automatically removed when the plugin is
848
+ * uninstalled. Returns an unregister function.
849
+ */
850
+ registerHook(event: HookEvent, matcher: HookMatcher | undefined, hook: InProcessHook): () => void;
851
+ config: Config;
852
+ log: Logger;
853
+ /**
854
+ * Register a one-time event listener. The handler is automatically removed
855
+ * after the first emission, or when the plugin is uninstalled — whichever
856
+ * comes first.
857
+ */
858
+ onEvent<K extends EventName>(event: K, handler: Listener<K>): () => void;
859
+ /**
860
+ * Subscribe to all events matching a glob-style pattern.
861
+ * `'tool.*'` matches all tool events. `'*'` matches everything.
862
+ * Returns an unsubscribe function.
863
+ */
864
+ onPattern(pattern: string, handler: (event: string, payload: unknown) => void): () => void;
865
+ /**
866
+ * Emit a custom event on the agent's EventBus. Use for inter-plugin
867
+ * communication or to surface plugin-specific state to the host.
916
868
  *
917
- * Returns a disposer that detaches every subscription; call on
918
- * subagent teardown so the listeners don't outlive the run.
869
+ * Custom events use a `pluginName:eventName` convention to avoid
870
+ * collisions with built-in events (e.g. `my-plugin:cache_hit`).
871
+ * The payload is passed through to all subscribers.
919
872
  */
920
- attach(subagentId: string, bus: EventBus, taskId?: string): () => void;
921
- /** Subscribe to every event from one subagent. */
922
- subscribe(subagentId: string, handler: FleetHandler): () => void;
923
- /** Subscribe to one event type across all subagents. */
924
- filter(type: string, handler: FleetHandler): () => void;
925
- /** Subscribe to literally everything. The fleet roll-up uses this. */
926
- onAny(handler: FleetHandler): () => void;
927
- emit(event: FleetEvent): void;
873
+ emitCustom(event: string, payload: unknown): void;
874
+ /**
875
+ * Register a callback that fires when the configuration changes at
876
+ * runtime (e.g. via `/config` slash command or programmatic update).
877
+ * The handler receives the new and previous config snapshots.
878
+ * Returns an unsubscribe function.
879
+ */
880
+ onConfigChange(handler: (next: Readonly<Config>, prev: Readonly<Config>) => void): () => void;
928
881
  }
929
882
  /**
930
- * Roll-up of token usage + cost across an entire director run. The
931
- * director's `fleet_status` tool returns this so the model can reason
932
- * about budget in its next turn ("the researcher already burned $0.40,
933
- * lean on summaries for the next task").
883
+ * Capability declaration informs the host which subsystems a plugin
884
+ * intends to touch. Used for diagnostics and per-plugin enable/disable UX
885
+ * (e.g. "this plugin registers tools disable to remove them"). Not
886
+ * enforced at runtime: a plugin that declares `tools: false` can still
887
+ * call `api.tools.register()`, but the host can flag the discrepancy.
934
888
  */
935
- interface FleetUsage {
936
- total: {
937
- input: number;
938
- output: number;
939
- cacheRead: number;
940
- cacheWrite: number;
941
- cost: number;
942
- };
943
- perSubagent: Record<string, SubagentUsageSnapshot>;
944
- }
945
- interface SubagentUsageSnapshot {
946
- subagentId: string;
947
- provider?: string | undefined;
948
- model?: string | undefined;
949
- input: number;
950
- output: number;
951
- cacheRead: number;
952
- cacheWrite: number;
953
- cost: number;
954
- toolCalls: number;
955
- iterations: number;
956
- startedAt: number;
957
- lastEventAt: number;
889
+ interface PluginCapabilities {
890
+ /** Will register tools via `api.tools.register()`. */
891
+ tools?: boolean | undefined;
892
+ /** Will register provider factories via `api.providers.register()`. */
893
+ providers?: boolean | undefined;
894
+ /**
895
+ * Pipelines the plugin hooks into. Use the standard names
896
+ * (`request | response | toolCall | userInput | assistantOutput | contextWindow`)
897
+ * or custom pipeline names exposed by other plugins.
898
+ */
899
+ pipelines?: string[] | undefined;
900
+ /** Will register slash commands via `api.slashCommands.register()`. */
901
+ slashCommands?: boolean | undefined;
902
+ /** Will start MCP servers via `api.mcp.start()`. */
903
+ mcp?: boolean | undefined;
958
904
  }
959
905
  /**
960
- * Aggregates provider.response + tool.executed events from the FleetBus
961
- * into a live `FleetUsage` snapshot. Costs are computed by the caller
962
- * via a `priceLookup(subagentId)` so we don't bake provider-pricing
963
- * coupling into core; the CLI/tests supply a function that resolves
964
- * each subagent's per-token rates from the models registry.
906
+ * Structured dependency declaration. The string form (`dependsOn: ['foo']`)
907
+ * is shorthand for `[{ name: 'foo' }]` both work. Use the structured form
908
+ * when you need a version constraint:
909
+ *
910
+ * dependsOn: [{ name: 'wstack-auth', version: '^1.2.0' }]
965
911
  */
966
- declare class FleetUsageAggregator {
967
- private readonly priceLookup?;
968
- private readonly metaLookup?;
969
- private readonly perSubagent;
970
- private readonly total;
971
- private readonly unsub;
972
- constructor(bus: FleetBus, priceLookup?: ((subagentId: string, provider?: string | undefined, model?: string | undefined) => {
973
- input?: number | undefined;
974
- output?: number | undefined;
975
- cacheRead?: number | undefined;
976
- cacheWrite?: number | undefined;
977
- } | undefined) | undefined, metaLookup?: ((subagentId: string) => {
978
- provider?: string | undefined;
979
- model?: string | undefined;
980
- } | undefined) | undefined);
912
+ interface PluginDependency {
913
+ name: string;
914
+ /** npm-style semver range. Supports `^`, `~`, exact, and unprefixed. */
915
+ version?: string | undefined;
916
+ }
917
+ interface Plugin {
918
+ name: string;
919
+ version?: string | undefined;
920
+ /** One-line summary for `wstack plugins list` and error messages. */
921
+ description?: string | undefined;
922
+ /** Semver range against the kernel API version (KERNEL_API_VERSION). */
923
+ apiVersion: string;
981
924
  /**
982
- * Remove a terminated subagent's data from the aggregator and subtract its
983
- * contribution from the running totals. Call this when a subagent is removed
984
- * from the fleet so the aggregator doesn't accumulate unbounded data for
985
- * entities that will never emit events again.
925
+ * Capability hints what subsystems the plugin will register against.
926
+ * Optional; provided for diagnostics and UX. The loader does not enforce
927
+ * these, but mismatch is surfaced via logger at warn level.
986
928
  */
987
- removeSubagent(subagentId: string): void;
988
- /** Disposes all fleet-bus subscriptions. Call when the aggregator is no longer needed. */
989
- dispose(): void;
990
- /** Live snapshot safe to call from a tool's execute() body. */
991
- snapshot(): FleetUsage;
992
- private ensure;
993
- private onProviderResponse;
994
- private onToolExecuted;
995
- private onIterationStarted;
929
+ capabilities?: PluginCapabilities | undefined;
930
+ /**
931
+ * JSON Schema for the options under `Config.plugins[<name>].options`.
932
+ * When present, the loader validates that section before calling `setup`
933
+ * and rejects the plugin with a clear error path on failure.
934
+ */
935
+ configSchema?: JSONSchema | undefined;
936
+ /**
937
+ * Mandatory plugin dependencies — loading fails if any are absent or
938
+ * version-incompatible. Accepts both the legacy string-array form and
939
+ * the structured form with version constraints.
940
+ */
941
+ dependsOn?: (string | PluginDependency)[] | undefined;
942
+ /** Optional plugin dependencies — silently skipped if absent. */
943
+ optionalDeps?: (string | PluginDependency)[] | undefined;
944
+ conflictsWith?: string[] | undefined;
945
+ /**
946
+ * Default configuration values, deep-merged under the plugin's options
947
+ * key before `configSchema` validation. User-provided values take
948
+ * precedence over defaults — this is a fallback, not an override.
949
+ *
950
+ * @example
951
+ * defaultConfig: { ttl: 3600, maxSize: 100 }
952
+ */
953
+ defaultConfig?: Record<string, unknown>;
954
+ setup(api: PluginAPI): void | Promise<void>;
955
+ teardown?(api: PluginAPI): void | Promise<void>;
956
+ /**
957
+ * Optional health check. Called by the host (e.g. `/diag plugins` slash
958
+ * command or health endpoint) to surface plugin status. Return
959
+ * `{ ok: false, message: '...' }` when the plugin is degraded.
960
+ */
961
+ health?(): Promise<{
962
+ ok: boolean;
963
+ message?: string | undefined;
964
+ }>;
965
+ }
966
+
967
+ declare class Agent {
968
+ readonly container: Container;
969
+ readonly tools: ToolRegistry;
970
+ readonly providers: ProviderRegistry;
971
+ readonly events: EventBus;
972
+ readonly pipelines: AgentPipelines;
973
+ readonly ctx: Context;
974
+ readonly maxIterations: number;
975
+ readonly executionStrategy: 'parallel' | 'sequential' | 'smart';
976
+ readonly perIterationOutputCapBytes: number;
977
+ private readonly plugins;
978
+ readonly toolExecutor: ToolExecutorLike;
979
+ readonly autoExtendLimit: boolean;
980
+ private readonly autonomousContinue;
981
+ readonly tracer: Tracer | undefined;
982
+ readonly extensions: ExtensionRegistry;
983
+ private readonly _toolHandler;
984
+ private readonly _responseHandler;
985
+ private readonly _loopHandler;
986
+ constructor(init: AgentInit);
987
+ get logger(): Logger;
988
+ get retry(): RetryPolicy;
989
+ get errorHandler(): ErrorHandler;
990
+ get permission(): PermissionPolicy;
991
+ get renderer(): Renderer | undefined;
992
+ disableInteractiveConfirmation(): void;
993
+ register(tool: Tool): void;
994
+ use(plugin: Plugin, api: PluginAPI): Promise<void>;
995
+ teardown(): Promise<void>;
996
+ run(userInput: AgentInput, opts?: RunOptions): Promise<RunResult>;
996
997
  }
997
998
 
998
999
  /**
@@ -1052,4 +1053,4 @@ interface AgentRunnerOptions {
1052
1053
  */
1053
1054
  declare function makeAgentSubagentRunner(opts: AgentRunnerOptions): SubagentRunner;
1054
1055
 
1055
- 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 };
1056
+ export { type AgentBridge as A, type BridgeTransport as B, type CoordinatorStatus as C, type PluginPipelines as D, type ToolRegistryView as E, FleetBus as F, type ProviderRegistryView as G, type MCPRegistryView as H, type SlashCommandRegistryView as I, type SessionWriterView as J, type MetricsSinkView as K, type Plugin as L, type MultiAgentCoordinator as M, type CoordinatorEvents as N, type DoneCondition as O, type PluginAPI as P, type PluginCapabilities as Q, type PluginDependency 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 TaskDelegation as Z, type ProviderFactory as _, type BridgeMessage as a, type AgentBridgeConfig as b, type MultiAgentConfig as c, type SubagentRunner as d, type SpawnResult as e, type TaskResult as f, Agent as g, type AgentFactory as h, type AgentFactoryResult as i, type AgentRunnerOptions as j, BudgetExceededError as k, type BudgetKind as l, type BudgetLimits as m, type BudgetNegotiationMode as n, type BudgetThresholdDecision as o, type BudgetThresholdHandler as p, BudgetThresholdSignal as q, type BudgetUsage as r, type FleetEvent as s, type FleetHandler as t, type FleetUsage as u, FleetUsageAggregator as v, SubagentBudget as w, type SubagentUsageSnapshot as x, makeAgentSubagentRunner as y, type SlashCommand as z };