@wrongstack/core 0.7.8 → 0.8.2

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 (41) hide show
  1. package/dist/{agent-bridge-hXRN-GO_.d.ts → agent-bridge-DPxcUVkn.d.ts} +1 -1
  2. package/dist/{agent-subagent-runner-B2zguWzh.d.ts → agent-subagent-runner-Cav3yEJM.d.ts} +9 -10
  3. package/dist/coordination/index.d.ts +9 -9
  4. package/dist/coordination/index.js +393 -179
  5. package/dist/coordination/index.js.map +1 -1
  6. package/dist/defaults/index.d.ts +13 -13
  7. package/dist/defaults/index.js +261 -64
  8. package/dist/defaults/index.js.map +1 -1
  9. package/dist/{events-D4pGukpI.d.ts → events-DyhxkstG.d.ts} +33 -0
  10. package/dist/execution/index.d.ts +24 -7
  11. package/dist/execution/index.js +158 -37
  12. package/dist/execution/index.js.map +1 -1
  13. package/dist/extension/index.d.ts +3 -3
  14. package/dist/{index-BtNRyJft.d.ts → index-oYZeWsuJ.d.ts} +2 -2
  15. package/dist/index.d.ts +16 -16
  16. package/dist/index.js +365 -68
  17. package/dist/index.js.map +1 -1
  18. package/dist/infrastructure/index.d.ts +2 -2
  19. package/dist/kernel/index.d.ts +3 -3
  20. package/dist/kernel/index.js +23 -0
  21. package/dist/kernel/index.js.map +1 -1
  22. package/dist/{multi-agent-7OK4pEKW.d.ts → multi-agent-CRMznZmf.d.ts} +64 -30
  23. package/dist/{multi-agent-coordinator-3Ypfg-hr.d.ts → multi-agent-coordinator-IQKrMfXz.d.ts} +11 -1
  24. package/dist/{null-fleet-bus-BZUrXVcd.d.ts → null-fleet-bus-sKnVwEd8.d.ts} +92 -6
  25. package/dist/observability/index.d.ts +1 -1
  26. package/dist/{path-resolver-DPUjF10O.d.ts → path-resolver-1CIYbH2Q.d.ts} +1 -1
  27. package/dist/{permission-policy-D5Gj1o2K.d.ts → permission-policy-BBa1M1xc.d.ts} +10 -1
  28. package/dist/{plan-templates-C-IOLJ8Q.d.ts → plan-templates-BnlpEkX8.d.ts} +1 -1
  29. package/dist/{provider-runner-iST8U3ni.d.ts → provider-runner-BrA0XR-l.d.ts} +1 -1
  30. package/dist/sdd/index.d.ts +5 -5
  31. package/dist/sdd/index.js +114 -28
  32. package/dist/sdd/index.js.map +1 -1
  33. package/dist/{secret-scrubber-DXkhwuka.d.ts → secret-scrubber-C0n1EqrC.d.ts} +1 -1
  34. package/dist/{secret-scrubber-nI8qjaqW.d.ts → secret-scrubber-CyE1-EMG.d.ts} +1 -1
  35. package/dist/security/index.d.ts +3 -3
  36. package/dist/security/index.js +29 -3
  37. package/dist/security/index.js.map +1 -1
  38. package/dist/storage/index.d.ts +2 -2
  39. package/dist/{tool-executor-CSwXjifK.d.ts → tool-executor-QwfWnQZ8.d.ts} +1 -1
  40. package/dist/types/index.d.ts +9 -9
  41. package/package.json +1 -1
@@ -1,4 +1,5 @@
1
- import { m as SubagentConfig, k as MultiAgentCoordinator, M as MultiAgentConfig, s as SubagentRunner, S as SpawnResult, u as TaskSpec, B as BridgeMessage, A as AgentBridge, j as CoordinatorStatus, t as TaskResult } from './multi-agent-7OK4pEKW.js';
1
+ import { n as SubagentConfig, l as MultiAgentCoordinator, M as MultiAgentConfig, t as SubagentRunner, S as SpawnResult, v as TaskSpec, B as BridgeMessage, A as AgentBridge, k as CoordinatorStatus, u as TaskResult } from './multi-agent-CRMznZmf.js';
2
+ import { F as FleetBus } from './agent-subagent-runner-Cav3yEJM.js';
2
3
  import { EventEmitter } from 'node:events';
3
4
 
4
5
  /**
@@ -175,6 +176,7 @@ declare class DefaultMultiAgentCoordinator extends EventEmitter implements Multi
175
176
  readonly coordinatorId: string;
176
177
  readonly config: MultiAgentConfig;
177
178
  private runner?;
179
+ private fleetBus?;
178
180
  private readonly subagents;
179
181
  private pendingTasks;
180
182
  private completedResults;
@@ -196,6 +198,12 @@ declare class DefaultMultiAgentCoordinator extends EventEmitter implements Multi
196
198
  * the coordinator's owning Director is built.
197
199
  */
198
200
  setRunner(runner: SubagentRunner): void;
201
+ /**
202
+ * Wire a FleetBus for director-mode event emission. Call after the
203
+ * FleetManager is constructed so the coordinator can emit lifecycle
204
+ * events the TUI and monitoring tools subscribe to.
205
+ */
206
+ setFleetBus(fleet: FleetBus): void;
199
207
  /**
200
208
  * Change the in-flight dispatch ceiling at runtime. Lowering does NOT
201
209
  * preempt running tasks — already-dispatched subagents finish their
@@ -226,6 +234,8 @@ declare class DefaultMultiAgentCoordinator extends EventEmitter implements Multi
226
234
  pending: number;
227
235
  completed: number;
228
236
  };
237
+ /** Emit a reactive coordinator.stats event on FleetBus so the TUI can subscribe. */
238
+ private emitCoordinatorStats;
229
239
  getStatus(): CoordinatorStatus;
230
240
  /** Expose snapshot of completed results — useful for callers awaiting all done. */
231
241
  results(): readonly TaskResult[];
@@ -1,10 +1,10 @@
1
- import { c as AgentPhase, b as AgentDefinition } from './multi-agent-coordinator-3Ypfg-hr.js';
2
- import { m as SubagentConfig, t as TaskResult, j as CoordinatorStatus, u as TaskSpec, M as MultiAgentConfig, s as SubagentRunner } from './multi-agent-7OK4pEKW.js';
1
+ import { c as AgentPhase, b as AgentDefinition, d as DefaultMultiAgentCoordinator, f as DispatchClassifier } from './multi-agent-coordinator-IQKrMfXz.js';
2
+ import { n as SubagentConfig, u as TaskResult, k as CoordinatorStatus, v as TaskSpec, M as MultiAgentConfig, t as SubagentRunner } from './multi-agent-CRMznZmf.js';
3
3
  import { B as SessionWriter, Q as Tool, y as SessionStore } from './context-z2x5gv_V.js';
4
4
  import { a as DirectorStateSnapshot } from './director-state-BmYi3DGA.js';
5
- import { I as InMemoryAgentBridge } from './agent-bridge-hXRN-GO_.js';
6
- import { F as FleetBus, e as FleetUsage, f as FleetUsageAggregator } from './agent-subagent-runner-B2zguWzh.js';
7
- import { E as EventBus } from './events-D4pGukpI.js';
5
+ import { I as InMemoryAgentBridge } from './agent-bridge-DPxcUVkn.js';
6
+ import { F as FleetBus, e as FleetUsage, f as FleetUsageAggregator } from './agent-subagent-runner-Cav3yEJM.js';
7
+ import { E as EventBus } from './events-DyhxkstG.js';
8
8
 
9
9
  /**
10
10
  * Agent catalog aggregator.
@@ -176,6 +176,11 @@ interface IFleetManager {
176
176
  * or null when no manifest path is configured.
177
177
  */
178
178
  writeManifest(): Promise<string | null>;
179
+ /**
180
+ * Bypass the debounce timer and write the manifest immediately.
181
+ * Clears any pending debounce timer before writing.
182
+ */
183
+ flushManifest(): Promise<void>;
179
184
  /**
180
185
  * Aggregate fleet-wide status: pending tasks with descriptions and
181
186
  * live subagent snapshot from the coordinator. Used by
@@ -204,6 +209,44 @@ interface IFleetManager {
204
209
  * pending list stays accurate.
205
210
  */
206
211
  removePendingTask(taskId: string): void;
212
+ /**
213
+ * Wire the coordinator so `getFleetStats()` can delegate to it.
214
+ * Called by `Director` after constructing the coordinator so
215
+ * FleetManager's stats reflect live subagent data.
216
+ */
217
+ setCoordinator(coordinator: {
218
+ getStats(): {
219
+ total: number;
220
+ running: number;
221
+ idle: number;
222
+ stopped: number;
223
+ inFlight: number;
224
+ pending: number;
225
+ completed: number;
226
+ };
227
+ }): void;
228
+ /**
229
+ * Coordinator stats snapshot for the TUI and monitoring tools.
230
+ * Returns actionable counts (total/running/idle/stopped/inFlight/
231
+ * pending/completed) plus per-subagent status details.
232
+ * Delegates to the coordinator when available; returns zeros
233
+ * if the coordinator has not yet been set.
234
+ */
235
+ getFleetStats(): {
236
+ total: number;
237
+ running: number;
238
+ idle: number;
239
+ stopped: number;
240
+ inFlight: number;
241
+ pending: number;
242
+ completed: number;
243
+ subagentStatuses: {
244
+ subagentId: string;
245
+ taskId: string;
246
+ status: string;
247
+ assigned: boolean;
248
+ }[];
249
+ };
207
250
  }
208
251
 
209
252
  /** Options for constructing a FleetManager. */
@@ -271,8 +314,16 @@ declare class FleetManager implements IFleetManager {
271
314
  private readonly pendingTasks;
272
315
  private readonly subagentMeta;
273
316
  private readonly priceLookups;
317
+ /** The coordinator (wired via setCoordinator by Director after construction). */
318
+ private coordinator;
274
319
  constructor(opts?: FleetManagerOptions);
275
320
  get fleetBus(): FleetBus;
321
+ /**
322
+ * Wire the coordinator after Director construction. The coordinator
323
+ * is not available when FleetManager is constructed standalone.
324
+ * Once set, `getFleetStats()` delegates to `coordinator.getStats()`.
325
+ */
326
+ setCoordinator(coordinator: DefaultMultiAgentCoordinator): void;
276
327
  snapshot(): FleetUsage;
277
328
  getSubagentMeta(id: string): {
278
329
  provider?: string;
@@ -313,12 +364,33 @@ declare class FleetManager implements IFleetManager {
313
364
  /**
314
365
  * Debounced manifest write. Call after any state mutation
315
366
  * (spawn, assign, complete) so a burst collapses into one write.
367
+ * When `manifestDebounceMs` is 0, writes are synchronous (no debounce).
316
368
  */
317
369
  scheduleManifest(): void;
370
+ /**
371
+ * Bypass the debounce timer and write the manifest immediately.
372
+ * Clears any pending debounce timer before writing.
373
+ */
374
+ flushManifest(): Promise<void>;
318
375
  /** Best-effort session event writer. Swallows failures. */
319
376
  private appendSessionEvent;
320
377
  addPendingTask(taskId: string, subagentId: string, description: string): void;
321
378
  removePendingTask(taskId: string): void;
379
+ getFleetStats(): {
380
+ total: number;
381
+ running: number;
382
+ idle: number;
383
+ stopped: number;
384
+ inFlight: number;
385
+ pending: number;
386
+ completed: number;
387
+ subagentStatuses: {
388
+ subagentId: string;
389
+ taskId: string;
390
+ status: string;
391
+ assigned: boolean;
392
+ }[];
393
+ };
322
394
  getFleetStatus(): {
323
395
  pending: {
324
396
  taskId: string;
@@ -492,6 +564,17 @@ interface DirectorOptions {
492
564
  * (same behavior as before this field was added).
493
565
  */
494
566
  fleetManager?: FleetManager;
567
+ /**
568
+ * Optional LLM classifier for the smart dispatcher. When set, the
569
+ * `spawn_subagent` tool can accept a free-form `description` field
570
+ * and the director will automatically route to the best-matching
571
+ * catalog agent using `dispatchAgent()`. When omitted, routing is
572
+ * pure heuristic (no provider call) — sufficient for most tasks.
573
+ *
574
+ * Build from a `complete(prompt) => string` function using
575
+ * `makeLLMClassifier(complete)` from the dispatcher module.
576
+ */
577
+ dispatchClassifier?: DispatchClassifier;
495
578
  }
496
579
  /**
497
580
  * Thrown by `Director.spawn()` when a configured spawn cap (`maxSpawns`,
@@ -612,6 +695,8 @@ declare class Director implements ICoordinator {
612
695
  * default cap.
613
696
  */
614
697
  private taskCompletedListener;
698
+ /** Optional LLM classifier for smart dispatch. Passed from options. */
699
+ readonly dispatchClassifier?: DispatchClassifier;
615
700
  constructor(opts: DirectorOptions);
616
701
  /**
617
702
  * Record a granted budget extension and broadcast it on the FleetBus so
@@ -625,7 +710,8 @@ declare class Director implements ICoordinator {
625
710
  * must not break a fleet run because the session JSONL handle closed. */
626
711
  private appendSessionEvent;
627
712
  /** Debounced manifest writer. A burst of spawn/assign/complete events
628
- * collapses into one write. Set `manifestDebounceMs` to 0 to disable. */
713
+ * collapses into one write. Set `manifestDebounceMs` to 0 to write
714
+ * synchronously (no debounce); set to negative to disable entirely. */
629
715
  private scheduleManifest;
630
716
  /**
631
717
  * Spawn a subagent. Identical to the coordinator's `spawn()` but
@@ -1,5 +1,5 @@
1
1
  import { e as MetricsSink, M as MetricLabels, f as MetricsSnapshot, b as HealthRegistry, H as HealthCheck, A as AggregateHealth, T as Tracer, S as Span } from '../observability-BhnVLBLS.js';
2
- import { E as EventBus } from '../events-D4pGukpI.js';
2
+ import { E as EventBus } from '../events-DyhxkstG.js';
3
3
  import '../context-z2x5gv_V.js';
4
4
 
5
5
  /**
@@ -1,4 +1,4 @@
1
- import { E as EventBus } from './events-D4pGukpI.js';
1
+ import { E as EventBus } from './events-DyhxkstG.js';
2
2
  import { b as ModelsRegistry, R as ResolvedModel } from './models-registry-BcYJDKLm.js';
3
3
  import { O as TokenCounter, $ as Usage, C as CacheStats } from './context-z2x5gv_V.js';
4
4
  import { P as PathResolver } from './path-resolver-CPRj4bFY.js';
@@ -1,6 +1,6 @@
1
1
  import { Q as Tool, d as Context } from './context-z2x5gv_V.js';
2
2
  import { I as InputReader } from './input-reader-E-ffP2ee.js';
3
- import { a as PermissionPolicy, P as PermissionDecision } from './secret-scrubber-nI8qjaqW.js';
3
+ import { a as PermissionPolicy, P as PermissionDecision } from './secret-scrubber-CyE1-EMG.js';
4
4
 
5
5
  interface PermissionPolicyOptions {
6
6
  trustFile: string;
@@ -100,6 +100,15 @@ declare class DefaultPermissionPolicy implements PermissionPolicy {
100
100
  * subagent capability override, not a deny-bypass).
101
101
  */
102
102
  declare class AutoApprovePermissionPolicy implements PermissionPolicy {
103
+ /**
104
+ * Tools that are too dangerous to auto-approve even in a delegated
105
+ * subagent context. Subagents run non-interactively under a director
106
+ * and cannot answer prompts, but inherited authorization does not
107
+ * imply blanket permission for destructive or privilege-escalating
108
+ * operations. These tools remain at their declared `permission`
109
+ * level so the leader must explicitly allow them per-spawn.
110
+ */
111
+ private static readonly DENY;
103
112
  evaluate(tool: Tool): Promise<PermissionDecision>;
104
113
  trust(): Promise<void>;
105
114
  deny(): Promise<void>;
@@ -1,4 +1,4 @@
1
- import { E as EventBus } from './events-D4pGukpI.js';
1
+ import { E as EventBus } from './events-DyhxkstG.js';
2
2
  import { y as SessionStore, x as SessionMetadata, B as SessionWriter, r as ResumedSession, S as SessionData, z as SessionSummary, c as ContentBlock, w as SessionEvent, N as TodoItem, f as ConversationState } from './context-z2x5gv_V.js';
3
3
  import { e as AttachmentStore, A as AddAttachmentInput, d as AttachmentRef, a as Attachment } from './session-reader-DsadjyF9.js';
4
4
  import { b as MemoryStore, a as MemoryScope } from './memory-CEXuo7sz.js';
@@ -1,4 +1,4 @@
1
- import { E as EventBus } from './events-D4pGukpI.js';
1
+ import { E as EventBus } from './events-DyhxkstG.js';
2
2
  import { a as Logger } from './logger-DDd5C--Z.js';
3
3
  import { T as Tracer } from './observability-BhnVLBLS.js';
4
4
  import { m as Provider, p as Request, d as Context, q as Response } from './context-z2x5gv_V.js';
@@ -1,13 +1,13 @@
1
1
  import { h as Specification, S as SpecAnalysis, g as SpecValidationResult, l as TaskGraph, m as TaskNode, k as TaskFilter, p as TaskSort, o as TaskProgress, r as TaskType, n as TaskPriority, e as SpecStatus, f as SpecTemplate, b as SpecRequirement } from '../task-graph-D1YQbpxF.js';
2
- import { E as EventBus } from '../events-D4pGukpI.js';
3
- import { D as DoneCondition, t as TaskResult } from '../multi-agent-7OK4pEKW.js';
4
- import { c as Agent } from '../index-BtNRyJft.js';
5
- import { A as AgentFactory } from '../agent-subagent-runner-B2zguWzh.js';
2
+ import { E as EventBus } from '../events-DyhxkstG.js';
3
+ import { D as DoneCondition, u as TaskResult } from '../multi-agent-CRMznZmf.js';
4
+ import { c as Agent } from '../index-oYZeWsuJ.js';
5
+ import { A as AgentFactory } from '../agent-subagent-runner-Cav3yEJM.js';
6
6
  import '../context-z2x5gv_V.js';
7
7
  import '../logger-DDd5C--Z.js';
8
8
  import '../system-prompt-CWA6ml-d.js';
9
9
  import '../observability-BhnVLBLS.js';
10
- import '../secret-scrubber-nI8qjaqW.js';
10
+ import '../secret-scrubber-CyE1-EMG.js';
11
11
  import '../config-Bi4Q0fnz.js';
12
12
  import '../models-registry-BcYJDKLm.js';
13
13
 
package/dist/sdd/index.js CHANGED
@@ -2627,29 +2627,35 @@ var SubagentBudget = class _SubagentBudget {
2627
2627
  * same kind are no-ops — without this dedup, every `recordIteration`
2628
2628
  * after the limit is reached spawns a fresh decision Promise (until
2629
2629
  * the first one lands and patches limits), flooding the FleetBus
2630
- * with redundant threshold events. Cleared in `checkLimitAsync`'s
2630
+ * with redundant threshold events. Cleared in `negotiateExtension`'s
2631
2631
  * `finally`.
2632
2632
  */
2633
2633
  pendingExtensions = /* @__PURE__ */ new Set();
2634
2634
  /**
2635
- * Hard cap on how long `checkLimitAsync` waits for the coordinator to
2635
+ * Hard cap on how long `negotiateExtension` waits for the coordinator to
2636
2636
  * respond before defaulting to 'stop'. Without this fallback an absent
2637
2637
  * or hung listener (Director not built / event filter detached mid-run)
2638
- * leaves the budget over-limit and never enforces anything, since
2639
- * `checkLimit` returns synchronously via `void this.checkLimitAsync`.
2640
- * Raised from 30s to 60s to give subagents more headroom before
2641
- * the threshold negotiation times out and triggers a hard stop.
2638
+ * leaves the budget over-limit and never enforces anything.
2642
2639
  */
2643
2640
  static DECISION_TIMEOUT_MS = 6e4;
2644
2641
  /**
2645
2642
  * Injected by the runner when wiring the budget to its EventBus.
2646
- * Used by `checkLimitAsync` to emit `budget.threshold_reached` events.
2643
+ * Used to emit `budget.threshold_reached` events in `'auto'` mode.
2647
2644
  */
2648
2645
  _events;
2646
+ /**
2647
+ * Negotiation mode — controls whether a threshold hit tries to emit
2648
+ * `budget.threshold_reached` and wait for a coordinator decision, or
2649
+ * falls straight through to a synchronous hard stop.
2650
+ *
2651
+ * `'auto'` (default) — emit on the EventBus and wait; times out to 'stop'.
2652
+ * `'sync'` — throw `BudgetExceededError` immediately regardless of listeners.
2653
+ */
2654
+ _mode;
2649
2655
  /**
2650
2656
  * Optional callback for soft-limit handling. When set, the budget will
2651
- * call this instead of throwing when a limit is first reached. The
2652
- * handler decides whether to throw, continue, or ask the coordinator.
2657
+ * invoke it rather than throw immediately. The handler decides whether to
2658
+ * throw synchronously, continue, or ask the coordinator for an extension.
2653
2659
  */
2654
2660
  get onThreshold() {
2655
2661
  return this._onThreshold;
@@ -2657,7 +2663,12 @@ var SubagentBudget = class _SubagentBudget {
2657
2663
  set onThreshold(fn) {
2658
2664
  this._onThreshold = fn;
2659
2665
  }
2660
- constructor(limits = {}) {
2666
+ /** Returns the current negotiation mode. */
2667
+ get mode() {
2668
+ return this._mode;
2669
+ }
2670
+ constructor(limits = {}, mode = "auto") {
2671
+ this._mode = mode;
2661
2672
  this.limits = { ...limits };
2662
2673
  }
2663
2674
  start() {
@@ -2673,16 +2684,23 @@ var SubagentBudget = class _SubagentBudget {
2673
2684
  return false;
2674
2685
  }
2675
2686
  /**
2676
- * Synchronous budget check always throws synchronously so callers
2677
- * (especially test event handlers using `expect().toThrow()`) get an
2678
- * unhandled rejection when the budget is exceeded without a handler.
2679
- * When `onThreshold` IS configured, the actual async threshold-handling
2680
- * is dispatched as a fire-and-forget promise.
2687
+ * Synchronous budget check. Always throws synchronously so callers (especially
2688
+ * test event handlers using `expect().toThrow()`) get an unhandled rejection
2689
+ * when the budget is exceeded without a handler.
2690
+ *
2691
+ * Decision table:
2692
+ * - no `onThreshold` handler → throw `BudgetExceededError` (hard stop, always)
2693
+ * - `mode === 'sync'` → throw `BudgetExceededError` (hard stop, always)
2694
+ * - `mode === 'auto'` + no listener → throw `BudgetExceededError` (hard stop; no one to ask)
2695
+ * - `mode === 'auto'` + listener → throw `BudgetThresholdSignal` with async decision promise
2681
2696
  */
2682
2697
  checkLimit(kind, used, limit) {
2683
2698
  if (!this._onThreshold) {
2684
2699
  throw new BudgetExceededError(kind, limit, used);
2685
2700
  }
2701
+ if (this._mode === "sync") {
2702
+ throw new BudgetExceededError(kind, limit, used);
2703
+ }
2686
2704
  const bus = this._events;
2687
2705
  if (!bus || !bus.hasListenerFor("budget.threshold_reached")) {
2688
2706
  throw new BudgetExceededError(kind, limit, used);
@@ -2699,8 +2717,8 @@ var SubagentBudget = class _SubagentBudget {
2699
2717
  * `pendingExtensions` slot in `finally`.
2700
2718
  *
2701
2719
  * The 'continue' return from a sync handler is treated as
2702
- * `{ extend: {} }` — keep going without patching, next overrun will
2703
- * fire a fresh signal.
2720
+ * `{ extend: {} }` — keep going without patching; next overrun fires
2721
+ * a fresh signal.
2704
2722
  */
2705
2723
  async negotiateExtension(kind, used, limit) {
2706
2724
  try {
@@ -2708,11 +2726,6 @@ var SubagentBudget = class _SubagentBudget {
2708
2726
  kind,
2709
2727
  used,
2710
2728
  limit,
2711
- // Inject a requestDecision helper the handler can call to emit the
2712
- // budget.threshold_reached event and wait for the coordinator's verdict.
2713
- // A hard fallback timer guarantees the promise eventually resolves
2714
- // even if no listener responds — without it, an absent/detached
2715
- // Director would leave the budget permanently in "asking" state.
2716
2729
  requestDecision: () => {
2717
2730
  const bus = this._events;
2718
2731
  if (!bus || !bus.hasListenerFor("budget.threshold_reached")) {
@@ -2796,12 +2809,12 @@ var SubagentBudget = class _SubagentBudget {
2796
2809
  }
2797
2810
  }
2798
2811
  /**
2799
- * Wall-clock budget check. Unlike other limits, timeout is treated as a
2800
- * warning-only event it NEVER hard-stops the subagent. When the
2801
- * elapsed time exceeds timeoutMs, emits `budget.threshold_reached` with
2802
- * kind='timeout' so the Director can decide whether to extend or warn.
2803
- * Call this from the iteration loop so a hung tool gets a chance to
2804
- * negotiate more time before the coordinator's Promise.race kills it.
2812
+ * Wall-clock budget check. Unlike other limits, timeout check passes through
2813
+ * `checkLimit` and is subject to the same negotiation-mode decision table.
2814
+ * In practice, `'sync'` mode (the usual test configuration) means a timeout
2815
+ * immediately throws `BudgetExceededError`. In production with a coordinator,
2816
+ * a timeout emits `budget.threshold_reached` so the Director can decide whether
2817
+ * to extend or abort.
2805
2818
  */
2806
2819
  checkTimeout() {
2807
2820
  if (this.startTime === null || this.limits.timeoutMs === void 0) return;
@@ -5366,6 +5379,7 @@ var DefaultMultiAgentCoordinator = class extends EventEmitter {
5366
5379
  coordinatorId;
5367
5380
  config;
5368
5381
  runner;
5382
+ fleetBus;
5369
5383
  subagents = /* @__PURE__ */ new Map();
5370
5384
  pendingTasks = [];
5371
5385
  completedResults = [];
@@ -5394,6 +5408,14 @@ var DefaultMultiAgentCoordinator = class extends EventEmitter {
5394
5408
  setRunner(runner) {
5395
5409
  this.runner = runner;
5396
5410
  }
5411
+ /**
5412
+ * Wire a FleetBus for director-mode event emission. Call after the
5413
+ * FleetManager is constructed so the coordinator can emit lifecycle
5414
+ * events the TUI and monitoring tools subscribe to.
5415
+ */
5416
+ setFleetBus(fleet) {
5417
+ this.fleetBus = fleet;
5418
+ }
5397
5419
  /**
5398
5420
  * Change the in-flight dispatch ceiling at runtime. Lowering does NOT
5399
5421
  * preempt running tasks — already-dispatched subagents finish their
@@ -5429,6 +5451,18 @@ var DefaultMultiAgentCoordinator = class extends EventEmitter {
5429
5451
  abortController: new AbortController()
5430
5452
  });
5431
5453
  this.emit("subagent.started", { subagent: { ...subagent, id } });
5454
+ this.fleetBus?.emit({
5455
+ subagentId: id,
5456
+ ts: Date.now(),
5457
+ type: "subagent.assigned",
5458
+ payload: {
5459
+ subagentId: id,
5460
+ name: subagent.name,
5461
+ provider: subagent.provider,
5462
+ model: subagent.model
5463
+ }
5464
+ });
5465
+ this.emitCoordinatorStats();
5432
5466
  return { subagentId: id, agentId: id };
5433
5467
  }
5434
5468
  async assign(task) {
@@ -5461,6 +5495,13 @@ var DefaultMultiAgentCoordinator = class extends EventEmitter {
5461
5495
  subagent.currentTask = void 0;
5462
5496
  subagent.context.parentBridge = null;
5463
5497
  this.emit("subagent.stopped", { subagentId, reason: "stopped by coordinator" });
5498
+ this.fleetBus?.emit({
5499
+ subagentId,
5500
+ ts: Date.now(),
5501
+ type: "subagent.stopped",
5502
+ payload: { subagentId, reason: "stopped by coordinator" }
5503
+ });
5504
+ this.emitCoordinatorStats();
5464
5505
  }
5465
5506
  async stopAll() {
5466
5507
  this.drainPendingAsAborted("Coordinator stopAll() drained the pending queue");
@@ -5492,6 +5533,22 @@ var DefaultMultiAgentCoordinator = class extends EventEmitter {
5492
5533
  completed: this.completedResults.length
5493
5534
  };
5494
5535
  }
5536
+ /** Emit a reactive coordinator.stats event on FleetBus so the TUI can subscribe. */
5537
+ emitCoordinatorStats() {
5538
+ const stats = this.getStats();
5539
+ const subagentStatuses = Array.from(this.subagents.entries()).map(([id, s]) => ({
5540
+ subagentId: id,
5541
+ taskId: s.currentTask ?? "",
5542
+ status: s.status,
5543
+ assigned: s.context.parentBridge !== null
5544
+ }));
5545
+ this.fleetBus?.emit({
5546
+ subagentId: this.coordinatorId,
5547
+ ts: Date.now(),
5548
+ type: "coordinator.stats",
5549
+ payload: { ...stats, subagentStatuses }
5550
+ });
5551
+ }
5495
5552
  getStatus() {
5496
5553
  return {
5497
5554
  coordinatorId: this.coordinatorId,
@@ -5666,7 +5723,15 @@ var DefaultMultiAgentCoordinator = class extends EventEmitter {
5666
5723
  subagent.currentTask = task.id;
5667
5724
  task.subagentId = subagentId;
5668
5725
  subagent.context.tasks.push(task);
5726
+ this.fleetBus?.emit({
5727
+ subagentId,
5728
+ taskId: task.id,
5729
+ ts: Date.now(),
5730
+ type: "subagent.running",
5731
+ payload: { subagentId, taskId: task.id }
5732
+ });
5669
5733
  this.emit("task.assigned", { task, subagentId });
5734
+ this.emitCoordinatorStats();
5670
5735
  const rawMaxIterations = subagent.config.maxIterations;
5671
5736
  const rawMaxToolCalls = subagent.config.maxToolCalls;
5672
5737
  const rawMaxTokens = subagent.config.maxTokens;
@@ -5810,13 +5875,34 @@ var DefaultMultiAgentCoordinator = class extends EventEmitter {
5810
5875
  if (subagent.abortController.signal.aborted) {
5811
5876
  subagent.abortController = new AbortController();
5812
5877
  }
5878
+ this.fleetBus?.emit({
5879
+ subagentId: result.subagentId,
5880
+ ts: Date.now(),
5881
+ type: "subagent.idle",
5882
+ payload: { subagentId: result.subagentId }
5883
+ });
5813
5884
  }
5814
5885
  this.terminating.delete(result.subagentId);
5815
5886
  this.emit("task.completed", {
5816
5887
  task: subagent?.context.tasks.find((t) => t.id === result.taskId) ?? { id: result.taskId },
5817
5888
  result
5818
5889
  });
5890
+ this.fleetBus?.emit({
5891
+ subagentId: result.subagentId,
5892
+ taskId: result.taskId,
5893
+ ts: Date.now(),
5894
+ type: "subagent.completed",
5895
+ payload: {
5896
+ subagentId: result.subagentId,
5897
+ taskId: result.taskId,
5898
+ status: result.status,
5899
+ iterations: result.iterations,
5900
+ toolCalls: result.toolCalls,
5901
+ durationMs: result.durationMs
5902
+ }
5903
+ });
5819
5904
  this.tryDispatchNext();
5905
+ this.emitCoordinatorStats();
5820
5906
  if (this.isDone()) {
5821
5907
  this.emit("done", {
5822
5908
  results: this.completedResults,