@wrongstack/core 0.7.8 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) 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-Bzeueq2J.d.ts} +9 -10
  3. package/dist/coordination/index.d.ts +8 -8
  4. package/dist/coordination/index.js +393 -179
  5. package/dist/coordination/index.js.map +1 -1
  6. package/dist/defaults/index.d.ts +10 -10
  7. package/dist/defaults/index.js +190 -55
  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 +6 -6
  11. package/dist/execution/index.js +116 -31
  12. package/dist/execution/index.js.map +1 -1
  13. package/dist/extension/index.d.ts +2 -2
  14. package/dist/{index-BtNRyJft.d.ts → index-BciJeH8g.d.ts} +1 -1
  15. package/dist/index.d.ts +13 -13
  16. package/dist/index.js +294 -59
  17. package/dist/index.js.map +1 -1
  18. package/dist/infrastructure/index.d.ts +2 -2
  19. package/dist/kernel/index.d.ts +2 -2
  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-tlSWD4cE.d.ts} +11 -1
  24. package/dist/{null-fleet-bus-BZUrXVcd.d.ts → null-fleet-bus-DXEvZKHW.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/{plan-templates-C-IOLJ8Q.d.ts → plan-templates-BnlpEkX8.d.ts} +1 -1
  28. package/dist/{provider-runner-iST8U3ni.d.ts → provider-runner-BrA0XR-l.d.ts} +1 -1
  29. package/dist/sdd/index.d.ts +4 -4
  30. package/dist/sdd/index.js +114 -28
  31. package/dist/sdd/index.js.map +1 -1
  32. package/dist/storage/index.d.ts +2 -2
  33. package/dist/{tool-executor-CSwXjifK.d.ts → tool-executor-Cx2ndr0L.d.ts} +1 -1
  34. package/dist/types/index.d.ts +7 -7
  35. package/package.json +1 -1
@@ -1,18 +1,18 @@
1
1
  export { D as DefaultLogger, a as DefaultLoggerOptions } from '../logger-bOzkF5LL.js';
2
- export { A as AbandonedSession, a as AttachmentStoreOptions, C as ConfigLoaderOptions, b as ConfigMigration, c as ConfigMigrationError, d as ConfigSource, D as DEFAULT_CONFIG_MIGRATIONS, e as DefaultAttachmentStore, f as DefaultConfigLoader, g as DefaultConfigStore, h as DefaultMemoryStore, i as DefaultSessionStore, M as MemoryStoreOptions, j as MigrationContext, k as MigrationResult, P as PersistedQueueItem, l as PlanFile, m as PlanItem, n as PlanTemplate, Q as QueueStore, R as RecoveryLock, o as RecoveryLockOptions, S as SessionAnalyzer, p as SessionStoreOptions, T as TodosCheckpointFile, q as addPlanItem, r as attachPlanCheckpoint, s as attachTodosCheckpoint, t as clearPlan, u as deriveTodosFromPlanItem, v as emptyPlan, w as formatPlan, x as formatPlanTemplates, y as getPlanTemplate, z as listPlanTemplates, B as loadPlan, E as loadTodosCheckpoint, F as removePlanItem, G as runConfigMigrations, H as savePlan, I as saveTodosCheckpoint, J as setPlanItemStatus } from '../plan-templates-C-IOLJ8Q.js';
2
+ export { A as AbandonedSession, a as AttachmentStoreOptions, C as ConfigLoaderOptions, b as ConfigMigration, c as ConfigMigrationError, d as ConfigSource, D as DEFAULT_CONFIG_MIGRATIONS, e as DefaultAttachmentStore, f as DefaultConfigLoader, g as DefaultConfigStore, h as DefaultMemoryStore, i as DefaultSessionStore, M as MemoryStoreOptions, j as MigrationContext, k as MigrationResult, P as PersistedQueueItem, l as PlanFile, m as PlanItem, n as PlanTemplate, Q as QueueStore, R as RecoveryLock, o as RecoveryLockOptions, S as SessionAnalyzer, p as SessionStoreOptions, T as TodosCheckpointFile, q as addPlanItem, r as attachPlanCheckpoint, s as attachTodosCheckpoint, t as clearPlan, u as deriveTodosFromPlanItem, v as emptyPlan, w as formatPlan, x as formatPlanTemplates, y as getPlanTemplate, z as listPlanTemplates, B as loadPlan, E as loadTodosCheckpoint, F as removePlanItem, G as runConfigMigrations, H as savePlan, I as saveTodosCheckpoint, J as setPlanItemStatus } from '../plan-templates-BnlpEkX8.js';
3
3
  export { D as DefaultSessionReader } from '../session-reader-DsadjyF9.js';
4
4
  export { D as DirectorStateCheckpoint, a as DirectorStateSnapshot, b as DirectorSubagentState, c as DirectorTaskState, l as loadDirectorState } from '../director-state-BmYi3DGA.js';
5
5
  export { D as DefaultSecretScrubber, a as DefaultSecretVault, S as SecretVaultOptions, d as decryptConfigSecrets, e as encryptConfigSecrets, m as migratePlaintextSecrets, r as rewriteConfigEncrypted } from '../secret-scrubber-DXkhwuka.js';
6
6
  export { A as AutoApprovePermissionPolicy, D as DefaultPermissionPolicy, P as PermissionPolicyOptions } from '../permission-policy-D5Gj1o2K.js';
7
- export { C as CompactorOptions, a as DefaultErrorHandler, b as DefaultRetryPolicy, H as HybridCompactor, T as ToolExecutor } from '../tool-executor-CSwXjifK.js';
7
+ export { C as CompactorOptions, a as DefaultErrorHandler, b as DefaultRetryPolicy, H as HybridCompactor, T as ToolExecutor } from '../tool-executor-Cx2ndr0L.js';
8
8
  export { AutoCompactionMiddleware, AutonomousRunner, AutonomousRunnerOptions, AutonomyPromptContributorOptions, DefaultSkillLoader, DoneCheckResult, DoneConditionChecker, EternalAutonomyEngine, EternalAutonomyOptions, EternalEngineState, IntelligentCompactor, IntelligentCompactorOptions, ParallelEngineState, ParallelEternalEngine, ParallelEternalOptions, SelectiveCompactor, SelectiveCompactorOptions, SkillLoaderOptions, buildGoalPreamble, makeAutonomyPromptContributor } from '../execution/index.js';
9
- import { P as ProviderRunner, R as RunProviderOptions } from '../provider-runner-iST8U3ni.js';
9
+ import { P as ProviderRunner, R as RunProviderOptions } from '../provider-runner-BrA0XR-l.js';
10
10
  import { q as Response } from '../context-z2x5gv_V.js';
11
- export { a as AGENTS_BY_PHASE, b as AGENT_CATALOG, c as ALL_AGENT_DEFINITIONS, d as ALL_FLEET_AGENTS, e as AUDIT_LOG_AGENT, f as AutoExtendCeiling, g as AutoExtendPolicy, B as BUG_HUNTER_AGENT, C as CreateDelegateToolOptions, D as DEFAULT_DIRECTOR_PREAMBLE, h as DEFAULT_SUBAGENT_BASELINE, i as DelegateHost, j as Director, k as DirectorPromptParts, l as DirectorSessionFactory, m as DirectorSessionFactoryOptions, F as FLEET_ROSTER, n as FLEET_ROSTER_BUDGETS, s as FleetRosterBudget, t as FleetSpawnBudgetError, I as ICoordinator, u as IFleetManager, N as NULL_FLEET_BUS, R as REFACTOR_PLANNER_AGENT, S as SECURITY_SCANNER_AGENT, v as SubagentPromptParts, w as applyRosterBudget, x as attachAutoExtend, y as composeDirectorPrompt, z as composeSubagentPrompt, E as createDelegateTool, G as getAgentDefinition, H as makeDirectorSessionFactory, J as rosterSummaryFromConfigs } from '../null-fleet-bus-BZUrXVcd.js';
12
- export { A as AgentBudgetTier, a as AgentCapability, b as AgentDefinition, c as AgentPhase, D as DEFAULT_DISPATCH_ROLE, d as DefaultMultiAgentCoordinator, e as DispatchCandidate, f as DispatchClassifier, g as DispatchMethod, h as DispatchOptions, i as DispatchResult, j as MultiAgentCoordinatorOptions, k as dispatchAgent, m as makeLLMClassifier, s as scoreAgents } from '../multi-agent-coordinator-3Ypfg-hr.js';
13
- export { c as BudgetExceededError, d as BudgetKind, e as BudgetLimits, i as BudgetUsage, l as SubagentBudget } from '../multi-agent-7OK4pEKW.js';
14
- export { A as AgentFactory, a as AgentFactoryResult, b as AgentRunnerOptions, F as FleetBus, c as FleetEvent, d as FleetHandler, e as FleetUsage, f as FleetUsageAggregator, S as SubagentUsageSnapshot, m as makeAgentSubagentRunner } from '../agent-subagent-runner-B2zguWzh.js';
15
- export { I as InMemoryAgentBridge, a as InMemoryBridgeTransport, c as createMessage } from '../agent-bridge-hXRN-GO_.js';
11
+ export { a as AGENTS_BY_PHASE, b as AGENT_CATALOG, c as ALL_AGENT_DEFINITIONS, d as ALL_FLEET_AGENTS, e as AUDIT_LOG_AGENT, f as AutoExtendCeiling, g as AutoExtendPolicy, B as BUG_HUNTER_AGENT, C as CreateDelegateToolOptions, D as DEFAULT_DIRECTOR_PREAMBLE, h as DEFAULT_SUBAGENT_BASELINE, i as DelegateHost, j as Director, k as DirectorPromptParts, l as DirectorSessionFactory, m as DirectorSessionFactoryOptions, F as FLEET_ROSTER, n as FLEET_ROSTER_BUDGETS, s as FleetRosterBudget, t as FleetSpawnBudgetError, I as ICoordinator, u as IFleetManager, N as NULL_FLEET_BUS, R as REFACTOR_PLANNER_AGENT, S as SECURITY_SCANNER_AGENT, v as SubagentPromptParts, w as applyRosterBudget, x as attachAutoExtend, y as composeDirectorPrompt, z as composeSubagentPrompt, E as createDelegateTool, G as getAgentDefinition, H as makeDirectorSessionFactory, J as rosterSummaryFromConfigs } from '../null-fleet-bus-DXEvZKHW.js';
12
+ export { A as AgentBudgetTier, a as AgentCapability, b as AgentDefinition, c as AgentPhase, D as DEFAULT_DISPATCH_ROLE, d as DefaultMultiAgentCoordinator, e as DispatchCandidate, f as DispatchClassifier, g as DispatchMethod, h as DispatchOptions, i as DispatchResult, j as MultiAgentCoordinatorOptions, k as dispatchAgent, m as makeLLMClassifier, s as scoreAgents } from '../multi-agent-coordinator-tlSWD4cE.js';
13
+ export { c as BudgetExceededError, d as BudgetKind, e as BudgetLimits, j as BudgetUsage, m as SubagentBudget } from '../multi-agent-CRMznZmf.js';
14
+ export { A as AgentFactory, a as AgentFactoryResult, b as AgentRunnerOptions, F as FleetBus, c as FleetEvent, d as FleetHandler, e as FleetUsage, f as FleetUsageAggregator, S as SubagentUsageSnapshot, m as makeAgentSubagentRunner } from '../agent-subagent-runner-Bzeueq2J.js';
15
+ export { I as InMemoryAgentBridge, a as InMemoryBridgeTransport, c as createMessage } from '../agent-bridge-DPxcUVkn.js';
16
16
  export { D as DefaultModelsRegistry, a as DefaultModelsRegistryOptions, c as classifyFamily } from '../models-registry-OG_30xqZ.js';
17
17
  export { DefaultModeStore, LLMSelector, LLMSelectorOptions, ModeLoaderOptions, loadProjectModes, loadUserModes } from '../models/index.js';
18
18
  export { AISpecBuilder, AISpecBuilderOptions, AISpecPhase, AISpecSession, AutoExecutor, AutoExecutorOptions, BottleneckTask, CollectedAnswer, CriticalPathAnalysis, DefaultTaskStore, ExecutionSummary, GeneratedTask, RunResult, SPEC_TEMPLATES, SddParallelRun, SddParallelRunOptions, SddProgress, SddTaskDecomposer, SddTaskDecomposerOptions, SpecDiff, SpecDrivenDev, SpecDrivenDevOptions, SpecIndexEntry, SpecParser, SpecStore, SpecStoreOptions, SpecVersion, SpecVersioning, TaskBatch, TaskExecutionContext, TaskExecutionResult, TaskFlow, TaskFlowEventMap, TaskFlowEventName, TaskFlowExecutionContext, TaskFlowOptions, TaskFlowPhase, TaskGenerator, TaskGeneratorOptions, TaskGraphIndexEntry, TaskGraphStore, TaskGraphStoreOptions, TaskStore, TaskTracker, TaskTrackerOptions, TaskTransition, WaveResult, analyzeCriticalPath, createAutoExecutor, getTemplate, listTemplates, renderProgress, renderSpecAnalysis, renderTaskGraph, renderTaskList, templateToMarkdown } from '../sdd/index.js';
@@ -20,7 +20,7 @@ export { DefaultHealthRegistry, InMemoryMetricsSink, MetricsServerHandle, Metric
20
20
  export { C as ContextManagerAction, a as ContextManagerInput, b as ContextManagerResult, c as ContextManagerToolOptions, d as allServers, e as awsServer, f as blockServer, g as braveSearchServer, h as context7Server, i as contextManagerTool, j as createContextManagerTool, k as everArtServer, l as filesystemServer, m as githubServer, n as googleMapsServer, o as miniMaxVisionServer, s as sentinelServer, p as slackServer, z as zaiVisionServer } from '../mcp-servers-n2L9ohMX.js';
21
21
  export { D as DEFAULT_CONTEXT_CONFIG, a as DEFAULT_TOOLS_CONFIG } from '../default-config-DvRSTELf.js';
22
22
  import '../logger-DDd5C--Z.js';
23
- import '../events-D4pGukpI.js';
23
+ import '../events-DyhxkstG.js';
24
24
  import '../memory-CEXuo7sz.js';
25
25
  import '../wstack-paths-gCrJ631C.js';
26
26
  import '../config-Bi4Q0fnz.js';
@@ -30,7 +30,7 @@ import '../secret-scrubber-nI8qjaqW.js';
30
30
  import '../input-reader-E-ffP2ee.js';
31
31
  import '../compactor-Mw7-rNyb.js';
32
32
  import '../retry-policy-OwtKNxo8.js';
33
- import '../index-BtNRyJft.js';
33
+ import '../index-BciJeH8g.js';
34
34
  import '../system-prompt-CWA6ml-d.js';
35
35
  import '../observability-BhnVLBLS.js';
36
36
  import '../selector-DkvgYVS4.js';
@@ -6094,29 +6094,35 @@ var SubagentBudget = class _SubagentBudget {
6094
6094
  * same kind are no-ops — without this dedup, every `recordIteration`
6095
6095
  * after the limit is reached spawns a fresh decision Promise (until
6096
6096
  * the first one lands and patches limits), flooding the FleetBus
6097
- * with redundant threshold events. Cleared in `checkLimitAsync`'s
6097
+ * with redundant threshold events. Cleared in `negotiateExtension`'s
6098
6098
  * `finally`.
6099
6099
  */
6100
6100
  pendingExtensions = /* @__PURE__ */ new Set();
6101
6101
  /**
6102
- * Hard cap on how long `checkLimitAsync` waits for the coordinator to
6102
+ * Hard cap on how long `negotiateExtension` waits for the coordinator to
6103
6103
  * respond before defaulting to 'stop'. Without this fallback an absent
6104
6104
  * or hung listener (Director not built / event filter detached mid-run)
6105
- * leaves the budget over-limit and never enforces anything, since
6106
- * `checkLimit` returns synchronously via `void this.checkLimitAsync`.
6107
- * Raised from 30s to 60s to give subagents more headroom before
6108
- * the threshold negotiation times out and triggers a hard stop.
6105
+ * leaves the budget over-limit and never enforces anything.
6109
6106
  */
6110
6107
  static DECISION_TIMEOUT_MS = 6e4;
6111
6108
  /**
6112
6109
  * Injected by the runner when wiring the budget to its EventBus.
6113
- * Used by `checkLimitAsync` to emit `budget.threshold_reached` events.
6110
+ * Used to emit `budget.threshold_reached` events in `'auto'` mode.
6114
6111
  */
6115
6112
  _events;
6113
+ /**
6114
+ * Negotiation mode — controls whether a threshold hit tries to emit
6115
+ * `budget.threshold_reached` and wait for a coordinator decision, or
6116
+ * falls straight through to a synchronous hard stop.
6117
+ *
6118
+ * `'auto'` (default) — emit on the EventBus and wait; times out to 'stop'.
6119
+ * `'sync'` — throw `BudgetExceededError` immediately regardless of listeners.
6120
+ */
6121
+ _mode;
6116
6122
  /**
6117
6123
  * Optional callback for soft-limit handling. When set, the budget will
6118
- * call this instead of throwing when a limit is first reached. The
6119
- * handler decides whether to throw, continue, or ask the coordinator.
6124
+ * invoke it rather than throw immediately. The handler decides whether to
6125
+ * throw synchronously, continue, or ask the coordinator for an extension.
6120
6126
  */
6121
6127
  get onThreshold() {
6122
6128
  return this._onThreshold;
@@ -6124,7 +6130,12 @@ var SubagentBudget = class _SubagentBudget {
6124
6130
  set onThreshold(fn) {
6125
6131
  this._onThreshold = fn;
6126
6132
  }
6127
- constructor(limits = {}) {
6133
+ /** Returns the current negotiation mode. */
6134
+ get mode() {
6135
+ return this._mode;
6136
+ }
6137
+ constructor(limits = {}, mode = "auto") {
6138
+ this._mode = mode;
6128
6139
  this.limits = { ...limits };
6129
6140
  }
6130
6141
  start() {
@@ -6140,16 +6151,23 @@ var SubagentBudget = class _SubagentBudget {
6140
6151
  return false;
6141
6152
  }
6142
6153
  /**
6143
- * Synchronous budget check always throws synchronously so callers
6144
- * (especially test event handlers using `expect().toThrow()`) get an
6145
- * unhandled rejection when the budget is exceeded without a handler.
6146
- * When `onThreshold` IS configured, the actual async threshold-handling
6147
- * is dispatched as a fire-and-forget promise.
6154
+ * Synchronous budget check. Always throws synchronously so callers (especially
6155
+ * test event handlers using `expect().toThrow()`) get an unhandled rejection
6156
+ * when the budget is exceeded without a handler.
6157
+ *
6158
+ * Decision table:
6159
+ * - no `onThreshold` handler → throw `BudgetExceededError` (hard stop, always)
6160
+ * - `mode === 'sync'` → throw `BudgetExceededError` (hard stop, always)
6161
+ * - `mode === 'auto'` + no listener → throw `BudgetExceededError` (hard stop; no one to ask)
6162
+ * - `mode === 'auto'` + listener → throw `BudgetThresholdSignal` with async decision promise
6148
6163
  */
6149
6164
  checkLimit(kind, used, limit) {
6150
6165
  if (!this._onThreshold) {
6151
6166
  throw new BudgetExceededError(kind, limit, used);
6152
6167
  }
6168
+ if (this._mode === "sync") {
6169
+ throw new BudgetExceededError(kind, limit, used);
6170
+ }
6153
6171
  const bus = this._events;
6154
6172
  if (!bus || !bus.hasListenerFor("budget.threshold_reached")) {
6155
6173
  throw new BudgetExceededError(kind, limit, used);
@@ -6166,8 +6184,8 @@ var SubagentBudget = class _SubagentBudget {
6166
6184
  * `pendingExtensions` slot in `finally`.
6167
6185
  *
6168
6186
  * The 'continue' return from a sync handler is treated as
6169
- * `{ extend: {} }` — keep going without patching, next overrun will
6170
- * fire a fresh signal.
6187
+ * `{ extend: {} }` — keep going without patching; next overrun fires
6188
+ * a fresh signal.
6171
6189
  */
6172
6190
  async negotiateExtension(kind, used, limit) {
6173
6191
  try {
@@ -6175,11 +6193,6 @@ var SubagentBudget = class _SubagentBudget {
6175
6193
  kind,
6176
6194
  used,
6177
6195
  limit,
6178
- // Inject a requestDecision helper the handler can call to emit the
6179
- // budget.threshold_reached event and wait for the coordinator's verdict.
6180
- // A hard fallback timer guarantees the promise eventually resolves
6181
- // even if no listener responds — without it, an absent/detached
6182
- // Director would leave the budget permanently in "asking" state.
6183
6196
  requestDecision: () => {
6184
6197
  const bus = this._events;
6185
6198
  if (!bus || !bus.hasListenerFor("budget.threshold_reached")) {
@@ -6263,12 +6276,12 @@ var SubagentBudget = class _SubagentBudget {
6263
6276
  }
6264
6277
  }
6265
6278
  /**
6266
- * Wall-clock budget check. Unlike other limits, timeout is treated as a
6267
- * warning-only event it NEVER hard-stops the subagent. When the
6268
- * elapsed time exceeds timeoutMs, emits `budget.threshold_reached` with
6269
- * kind='timeout' so the Director can decide whether to extend or warn.
6270
- * Call this from the iteration loop so a hung tool gets a chance to
6271
- * negotiate more time before the coordinator's Promise.race kills it.
6279
+ * Wall-clock budget check. Unlike other limits, timeout check passes through
6280
+ * `checkLimit` and is subject to the same negotiation-mode decision table.
6281
+ * In practice, `'sync'` mode (the usual test configuration) means a timeout
6282
+ * immediately throws `BudgetExceededError`. In production with a coordinator,
6283
+ * a timeout emits `budget.threshold_reached` so the Director can decide whether
6284
+ * to extend or abort.
6272
6285
  */
6273
6286
  checkTimeout() {
6274
6287
  if (this.startTime === null || this.limits.timeoutMs === void 0) return;
@@ -8649,8 +8662,7 @@ function scoreAgents(task, catalog = AGENT_CATALOG) {
8649
8662
  const hay = normalize(task);
8650
8663
  const out = [];
8651
8664
  for (const def of Object.values(catalog)) {
8652
- const role = def.config.role;
8653
- if (!role) continue;
8665
+ if (!def?.config?.role) continue;
8654
8666
  let score = 0;
8655
8667
  const matched = [];
8656
8668
  for (const kw of def.capability.keywords) {
@@ -8662,7 +8674,7 @@ function scoreAgents(task, catalog = AGENT_CATALOG) {
8662
8674
  }
8663
8675
  }
8664
8676
  if (score > 0) {
8665
- out.push({ role, name: def.config.name, score, matched });
8677
+ out.push({ role: def.config.role, name: def.config.name, score, matched });
8666
8678
  }
8667
8679
  }
8668
8680
  out.sort((a, b) => b.score - a.score);
@@ -9033,6 +9045,7 @@ var DefaultMultiAgentCoordinator = class extends EventEmitter {
9033
9045
  coordinatorId;
9034
9046
  config;
9035
9047
  runner;
9048
+ fleetBus;
9036
9049
  subagents = /* @__PURE__ */ new Map();
9037
9050
  pendingTasks = [];
9038
9051
  completedResults = [];
@@ -9061,6 +9074,14 @@ var DefaultMultiAgentCoordinator = class extends EventEmitter {
9061
9074
  setRunner(runner) {
9062
9075
  this.runner = runner;
9063
9076
  }
9077
+ /**
9078
+ * Wire a FleetBus for director-mode event emission. Call after the
9079
+ * FleetManager is constructed so the coordinator can emit lifecycle
9080
+ * events the TUI and monitoring tools subscribe to.
9081
+ */
9082
+ setFleetBus(fleet) {
9083
+ this.fleetBus = fleet;
9084
+ }
9064
9085
  /**
9065
9086
  * Change the in-flight dispatch ceiling at runtime. Lowering does NOT
9066
9087
  * preempt running tasks — already-dispatched subagents finish their
@@ -9096,6 +9117,18 @@ var DefaultMultiAgentCoordinator = class extends EventEmitter {
9096
9117
  abortController: new AbortController()
9097
9118
  });
9098
9119
  this.emit("subagent.started", { subagent: { ...subagent, id } });
9120
+ this.fleetBus?.emit({
9121
+ subagentId: id,
9122
+ ts: Date.now(),
9123
+ type: "subagent.assigned",
9124
+ payload: {
9125
+ subagentId: id,
9126
+ name: subagent.name,
9127
+ provider: subagent.provider,
9128
+ model: subagent.model
9129
+ }
9130
+ });
9131
+ this.emitCoordinatorStats();
9099
9132
  return { subagentId: id, agentId: id };
9100
9133
  }
9101
9134
  async assign(task) {
@@ -9128,6 +9161,13 @@ var DefaultMultiAgentCoordinator = class extends EventEmitter {
9128
9161
  subagent.currentTask = void 0;
9129
9162
  subagent.context.parentBridge = null;
9130
9163
  this.emit("subagent.stopped", { subagentId, reason: "stopped by coordinator" });
9164
+ this.fleetBus?.emit({
9165
+ subagentId,
9166
+ ts: Date.now(),
9167
+ type: "subagent.stopped",
9168
+ payload: { subagentId, reason: "stopped by coordinator" }
9169
+ });
9170
+ this.emitCoordinatorStats();
9131
9171
  }
9132
9172
  async stopAll() {
9133
9173
  this.drainPendingAsAborted("Coordinator stopAll() drained the pending queue");
@@ -9159,6 +9199,22 @@ var DefaultMultiAgentCoordinator = class extends EventEmitter {
9159
9199
  completed: this.completedResults.length
9160
9200
  };
9161
9201
  }
9202
+ /** Emit a reactive coordinator.stats event on FleetBus so the TUI can subscribe. */
9203
+ emitCoordinatorStats() {
9204
+ const stats = this.getStats();
9205
+ const subagentStatuses = Array.from(this.subagents.entries()).map(([id, s]) => ({
9206
+ subagentId: id,
9207
+ taskId: s.currentTask ?? "",
9208
+ status: s.status,
9209
+ assigned: s.context.parentBridge !== null
9210
+ }));
9211
+ this.fleetBus?.emit({
9212
+ subagentId: this.coordinatorId,
9213
+ ts: Date.now(),
9214
+ type: "coordinator.stats",
9215
+ payload: { ...stats, subagentStatuses }
9216
+ });
9217
+ }
9162
9218
  getStatus() {
9163
9219
  return {
9164
9220
  coordinatorId: this.coordinatorId,
@@ -9333,7 +9389,15 @@ var DefaultMultiAgentCoordinator = class extends EventEmitter {
9333
9389
  subagent.currentTask = task.id;
9334
9390
  task.subagentId = subagentId;
9335
9391
  subagent.context.tasks.push(task);
9392
+ this.fleetBus?.emit({
9393
+ subagentId,
9394
+ taskId: task.id,
9395
+ ts: Date.now(),
9396
+ type: "subagent.running",
9397
+ payload: { subagentId, taskId: task.id }
9398
+ });
9336
9399
  this.emit("task.assigned", { task, subagentId });
9400
+ this.emitCoordinatorStats();
9337
9401
  const rawMaxIterations = subagent.config.maxIterations;
9338
9402
  const rawMaxToolCalls = subagent.config.maxToolCalls;
9339
9403
  const rawMaxTokens = subagent.config.maxTokens;
@@ -9477,13 +9541,34 @@ var DefaultMultiAgentCoordinator = class extends EventEmitter {
9477
9541
  if (subagent.abortController.signal.aborted) {
9478
9542
  subagent.abortController = new AbortController();
9479
9543
  }
9544
+ this.fleetBus?.emit({
9545
+ subagentId: result.subagentId,
9546
+ ts: Date.now(),
9547
+ type: "subagent.idle",
9548
+ payload: { subagentId: result.subagentId }
9549
+ });
9480
9550
  }
9481
9551
  this.terminating.delete(result.subagentId);
9482
9552
  this.emit("task.completed", {
9483
9553
  task: subagent?.context.tasks.find((t) => t.id === result.taskId) ?? { id: result.taskId },
9484
9554
  result
9485
9555
  });
9556
+ this.fleetBus?.emit({
9557
+ subagentId: result.subagentId,
9558
+ taskId: result.taskId,
9559
+ ts: Date.now(),
9560
+ type: "subagent.completed",
9561
+ payload: {
9562
+ subagentId: result.subagentId,
9563
+ taskId: result.taskId,
9564
+ status: result.status,
9565
+ iterations: result.iterations,
9566
+ toolCalls: result.toolCalls,
9567
+ durationMs: result.durationMs
9568
+ }
9569
+ });
9486
9570
  this.tryDispatchNext();
9571
+ this.emitCoordinatorStats();
9487
9572
  if (this.isDone()) {
9488
9573
  this.emit("done", {
9489
9574
  results: this.completedResults,
@@ -10334,18 +10419,17 @@ var FleetBus = class {
10334
10419
  byType = /* @__PURE__ */ new Map();
10335
10420
  any = /* @__PURE__ */ new Set();
10336
10421
  /**
10337
- * Hook a subagent's EventBus into the fleet. EventBus is strongly
10338
- * typed and doesn't expose an `onAny` hook, so we subscribe to the
10339
- * canonical set of event types a subagent emits during a run. New
10340
- * event types added to the kernel must be added here too — but the
10341
- * cost is a tiny single line per type, and the explicit list keeps
10342
- * the wire format clear.
10422
+ * Hook a subagent's EventBus into the fleet. Uses `onAny()` (an alias for
10423
+ * `onPattern('*')`) to forward all events with subagent attribution, so
10424
+ * new kernel event types are automatically forwarded without any manual
10425
+ * registration. `subagent.*` events are excluded because they originate
10426
+ * from MultiAgentHost on the parent bus, not the subagent's own bus.
10343
10427
  *
10344
10428
  * Returns a disposer that detaches every subscription; call on
10345
10429
  * subagent teardown so the listeners don't outlive the run.
10346
10430
  */
10347
10431
  attach(subagentId, bus, taskId) {
10348
- const off = bus.onPattern("*", (type, payload) => {
10432
+ const off = bus.onAny((type, payload) => {
10349
10433
  if (type.startsWith("subagent.")) return;
10350
10434
  this.emit({ subagentId, taskId, ts: Date.now(), type, payload });
10351
10435
  });
@@ -10467,7 +10551,7 @@ var FleetUsageAggregator = class {
10467
10551
  this.total.output += usage.output ?? 0;
10468
10552
  this.total.cacheRead += usage.cacheRead ?? 0;
10469
10553
  this.total.cacheWrite += usage.cacheWrite ?? 0;
10470
- const price = this.priceLookup?.(e.subagentId);
10554
+ const price = this.priceLookup?.(e.subagentId, snap.provider, snap.model);
10471
10555
  if (price) {
10472
10556
  const delta = (usage.input ?? 0) / 1e6 * (price.input ?? 0) + (usage.output ?? 0) / 1e6 * (price.output ?? 0) + (usage.cacheRead ?? 0) / 1e6 * (price.cacheRead ?? 0) + (usage.cacheWrite ?? 0) / 1e6 * (price.cacheWrite ?? 0);
10473
10557
  snap.cost += delta;
@@ -10490,8 +10574,9 @@ function makeSpawnTool(director, roster) {
10490
10574
  const inputSchema = {
10491
10575
  type: "object",
10492
10576
  properties: {
10493
- role: { type: "string", description: "Roster role id (preferred). When set, the spawn uses the matching config from the roster and ignores other fields." },
10494
- name: { type: "string", description: "Display name for the subagent. Required when not using roster." },
10577
+ role: { type: "string", description: "Roster role id. When set, the spawn uses the matching config from the roster and ignores other fields." },
10578
+ description: { type: "string", description: "Free-form task description. When `role` is not set, the director uses the smart dispatcher to route this to the best-matching catalog agent. Use this when you don't know the exact role name." },
10579
+ name: { type: "string", description: "Display name for the subagent. Used as a fallback when description-based dispatch does not resolve a role." },
10495
10580
  provider: { type: "string", description: 'Provider id (e.g. "anthropic", "openai"). Defaults to the leader provider when omitted.' },
10496
10581
  model: { type: "string", description: "Model id within the provider. Defaults to the leader model when omitted." },
10497
10582
  systemPromptOverride: { type: "string", description: "Extra prompt text appended after the role-base prompt." },
@@ -10504,18 +10589,38 @@ function makeSpawnTool(director, roster) {
10504
10589
  return {
10505
10590
  name: "spawn_subagent",
10506
10591
  description: "Create a new subagent under this director. Returns the subagent id.",
10507
- usageHint: "Either pass `role` (matches the roster) OR pass `name` + optional `provider`/`model`. Returns `{ subagentId }`.",
10592
+ usageHint: "Pass `role` (matches the roster), `description` (smart dispatch to best agent), or `name` + `provider`/`model`. Returns `{ subagentId }`.",
10508
10593
  permission: "auto",
10509
10594
  mutating: false,
10510
10595
  inputSchema,
10511
10596
  async execute(input) {
10512
10597
  const i = input ?? {};
10513
10598
  const role = typeof i.role === "string" ? i.role : void 0;
10514
- const base = role && roster ? roster[role] : void 0;
10515
- if (role && !base) {
10516
- return { error: `unknown role "${role}". roster has: ${roster ? Object.keys(roster).join(", ") : "(empty)"}` };
10599
+ const description = typeof i.description === "string" ? i.description : void 0;
10600
+ let cfg;
10601
+ if (role && roster) {
10602
+ const base = roster[role];
10603
+ if (!base) return { error: `unknown role "${role}". roster has: ${Object.keys(roster).join(", ")}` };
10604
+ cfg = instantiateRosterConfig(role, base);
10605
+ } else if (description && !role) {
10606
+ const dispatchResult = await dispatchAgent(description, {
10607
+ classifier: director.dispatchClassifier,
10608
+ catalog: roster
10609
+ });
10610
+ const dispatchRole = dispatchResult.role;
10611
+ if (roster && roster[dispatchRole]) {
10612
+ cfg = instantiateRosterConfig(dispatchRole, roster[dispatchRole]);
10613
+ } else {
10614
+ const def = dispatchResult.definition;
10615
+ cfg = {
10616
+ name: def.config.name ?? dispatchRole,
10617
+ role: dispatchRole,
10618
+ provider: def.config.provider,
10619
+ model: def.config.model
10620
+ };
10621
+ }
10517
10622
  }
10518
- const cfg = base ? instantiateRosterConfig(role, base) : { name: i.name ?? "subagent" };
10623
+ cfg ??= { name: i.name ?? "subagent" };
10519
10624
  if (typeof i.name === "string") cfg.name = i.name;
10520
10625
  if (typeof i.provider === "string") cfg.provider = i.provider;
10521
10626
  if (typeof i.model === "string") cfg.model = i.model;
@@ -10525,7 +10630,7 @@ function makeSpawnTool(director, roster) {
10525
10630
  if (typeof i.maxCostUsd === "number") cfg.maxCostUsd = i.maxCostUsd;
10526
10631
  try {
10527
10632
  const subagentId = await director.spawn(cfg);
10528
- return { subagentId, provider: cfg.provider, model: cfg.model, name: cfg.name };
10633
+ return { subagentId, provider: cfg.provider, model: cfg.model, name: cfg.name, role: cfg.role };
10529
10634
  } catch (err) {
10530
10635
  if (err instanceof FleetSpawnBudgetError) {
10531
10636
  return { error: err.message, kind: err.kind, limit: err.limit, observed: err.observed };
@@ -10649,12 +10754,21 @@ function makeTerminateTool(director) {
10649
10754
  function makeFleetStatusTool(director) {
10650
10755
  return {
10651
10756
  name: "fleet_status",
10652
- description: "Snapshot of the fleet \u2014 every subagent's current status, pending vs. completed task counts.",
10757
+ description: "Snapshot of the fleet \u2014 every subagent's current status, coordinator counts (total/running/idle/stopped), pending task descriptions, and usage rollup.",
10653
10758
  permission: "auto",
10654
10759
  mutating: false,
10655
10760
  inputSchema: { type: "object", properties: {}, required: [] },
10656
10761
  async execute() {
10657
- return director.status();
10762
+ const base = director.status();
10763
+ const fm = director.fleetManager;
10764
+ const stats = fm?.getFleetStats();
10765
+ const fleetStatus = fm?.getFleetStatus();
10766
+ return {
10767
+ subagents: base.subagents,
10768
+ coordinatorStats: stats ? { total: stats.total, running: stats.running, idle: stats.idle, stopped: stats.stopped } : void 0,
10769
+ pending: fleetStatus?.pending ?? [],
10770
+ usage: fm?.snapshot()
10771
+ };
10658
10772
  }
10659
10773
  };
10660
10774
  }
@@ -10856,6 +10970,8 @@ var Director = class {
10856
10970
  * default cap.
10857
10971
  */
10858
10972
  taskCompletedListener = null;
10973
+ /** Optional LLM classifier for smart dispatch. Passed from options. */
10974
+ dispatchClassifier;
10859
10975
  constructor(opts) {
10860
10976
  this.id = opts.config.coordinatorId || randomUUID();
10861
10977
  this.manifestPath = opts.manifestPath;
@@ -10868,6 +10984,7 @@ var Director = class {
10868
10984
  this.spawnDepth = opts.spawnDepth ?? 0;
10869
10985
  this.sessionWriter = opts.sessionWriter ?? null;
10870
10986
  this.manifestDebounceMs = opts.manifestDebounceMs ?? 2e3;
10987
+ this.dispatchClassifier = opts.dispatchClassifier;
10871
10988
  this.maxFleetCostUsd = opts.directorBudget?.maxCostUsd ?? Number.POSITIVE_INFINITY;
10872
10989
  this.maxBudgetExtensions = opts.maxBudgetExtensions ?? 5;
10873
10990
  this.sessionsRoot = opts.sessionsRoot;
@@ -10897,7 +11014,10 @@ var Director = class {
10897
11014
  this.fleet = new FleetBus();
10898
11015
  this.usage = new FleetUsageAggregator(
10899
11016
  this.fleet,
10900
- (id) => this.priceLookups.get(id),
11017
+ (id, provider, model) => {
11018
+ if (provider && model) return this.priceLookups.get(`${provider}/${model}`);
11019
+ return void 0;
11020
+ },
10901
11021
  (id) => this.subagentMeta.get(id)
10902
11022
  );
10903
11023
  }
@@ -10905,6 +11025,8 @@ var Director = class {
10905
11025
  { ...opts.config, coordinatorId: this.id },
10906
11026
  { runner: opts.runner }
10907
11027
  );
11028
+ this.coordinator.setFleetBus(this.fleet);
11029
+ this.fleetManager?.setCoordinator(this.coordinator);
10908
11030
  this.taskCompletedListener = (payload) => {
10909
11031
  const r = payload.result;
10910
11032
  this.completed.set(r.taskId, r);
@@ -10939,7 +11061,11 @@ var Director = class {
10939
11061
  title
10940
11062
  }
10941
11063
  );
10942
- this.scheduleManifest();
11064
+ if (this.fleetManager) {
11065
+ this.fleetManager.flushManifest();
11066
+ } else {
11067
+ this.scheduleManifest();
11068
+ }
10943
11069
  };
10944
11070
  this.coordinator.on("task.completed", this.taskCompletedListener);
10945
11071
  const extendCounts = /* @__PURE__ */ new Map();
@@ -11031,10 +11157,17 @@ var Director = class {
11031
11157
  }
11032
11158
  }
11033
11159
  /** Debounced manifest writer. A burst of spawn/assign/complete events
11034
- * collapses into one write. Set `manifestDebounceMs` to 0 to disable. */
11160
+ * collapses into one write. Set `manifestDebounceMs` to 0 to write
11161
+ * synchronously (no debounce); set to negative to disable entirely. */
11035
11162
  scheduleManifest() {
11036
- if (!this.manifestPath || this.manifestDebounceMs <= 0) return;
11037
- if (this.manifestTimer) return;
11163
+ if (!this.manifestPath) return;
11164
+ if (this.manifestDebounceMs === 0) {
11165
+ void this.writeManifest().catch(
11166
+ (err) => this.logShutdownError("manifest_write_debounced", err)
11167
+ );
11168
+ return;
11169
+ }
11170
+ if (this.manifestDebounceMs < 0) return;
11038
11171
  this.manifestTimer = setTimeout(() => {
11039
11172
  this.manifestTimer = null;
11040
11173
  void this.writeManifest().catch(
@@ -11083,7 +11216,9 @@ var Director = class {
11083
11216
  provider: config.provider,
11084
11217
  model: config.model
11085
11218
  });
11086
- if (priceLookup) this.priceLookups.set(result.subagentId, priceLookup);
11219
+ if (priceLookup && config.provider && config.model) {
11220
+ this.priceLookups.set(`${config.provider}/${config.model}`, priceLookup);
11221
+ }
11087
11222
  }
11088
11223
  const subagentBridge = new InMemoryAgentBridge(
11089
11224
  { agentId: result.subagentId, coordinatorId: this.id },