import { DirectorStateCheckpoint, type DirectorStateSnapshot } from '../storage/director-state.js'; import type { Logger } from '../types/logger.js'; import type { AwaitAnyResult, CoordinatorStatus, MultiAgentConfig, SubagentConfig, SubagentRunner, TaskResult, TaskSpec } from '../types/multi-agent.js'; import type { SessionWriter } from '../types/session.js'; import type { Tool } from '../types/tool.js'; import type { WorktreeManager } from '../worktree/worktree-manager.js'; import { InMemoryAgentBridge } from './agent-bridge.js'; import type { BrainArbiter } from './brain.js'; import type { CollabDebugReport, CollabSessionOptions } from './collab-debug.js'; import { FleetBus, type FleetUsage, FleetUsageAggregator } from './fleet-bus.js'; import type { FleetManager } from './fleet-manager.js'; import { type DirectorFleetHost } from './fleet-spawn.js'; import type { ICoordinator } from './icoordinator.js'; import { InMemoryBridgeTransport } from './in-memory-transport.js'; import { LargeAnswerStore } from './large-answer-store.js'; import { type ModelMatrixSource } from './model-matrix.js'; import { DefaultMultiAgentCoordinator } from './multi-agent-coordinator.js'; import type { ProviderModelStatusTracker } from './provider-status-tracker.js'; import { type FleetWorktreePolicy, type WorktreeTaskStateUpdate } from './worktree-task-runner.js'; /** * Director — high-level orchestrator that owns a `MultiAgentCoordinator`, * a `FleetBus`, and a `FleetUsageAggregator`. Exposes a small imperative * API (`spawn`, `assign`, `awaitTasks`, `terminate`, `status`, `usage`) * that's easy to test, and a `tools()` factory that wraps the same API * as agent-callable `Tool`s so an LLM can drive the orchestration. * * This class is intentionally *not* an `Agent`. It's a coordinator + * observability surface. To make it LLM-driven, construct an Agent * with `director.tools()` registered. That keeps the construction * symmetric with how other agents are built and avoids smuggling a * heavy LLM dependency into core just for the director path. */ /** * Payload handed to {@link DirectorOptions.taskResultNotifier} when a * fire-and-forget task completes (no `awaitTasks` waiter was pending). */ export interface TaskResultNotification { taskId: string; /** Task description (or task id when no description was recorded). */ title: string; status: TaskResult['status']; subagentId: string; /** Human-readable subagent name from the fleet manifest, when known. */ subagentName?: string | undefined; /** The subagent's textual result (stringified when structured). */ resultText?: string | undefined; /** Flattened `kind: message` error string for non-success statuses. */ errorText?: string | undefined; /** Bounded incomplete text recovered before a non-successful exit. */ partialText?: string | undefined; /** Machine-readable result submitted through submit_result. */ report?: TaskResult['report'] | undefined; iterations: number; toolCalls: number; durationMs: number; } interface DirectorOptions { config: MultiAgentConfig; runner?: SubagentRunner | undefined; /** Optional Brain arbiter above the director for policy/decision escalation. */ brain?: BrainArbiter | undefined; /** * Called when a task completes with NO pending `awaitTasks` waiter — * i.e. a fire-and-forget `assign_task`. Wire this to the project * mailbox so the result reaches the leader's next model evaluation * automatically instead of waiting for a poll. Synchronous `delegate` calls attach a * waiter up front, so they never double-report through this hook. * Internal tasks (shadow passes) are excluded. Errors are swallowed. */ taskResultNotifier?: ((n: TaskResultNotification) => void | Promise) | undefined; /** * Remove a spawned or between-task subagent after this much idle time. * Undefined disables idle retirement. This is separate from the in-task * activity watchdog enforced by SubagentBudget. */ subagentIdleTimeoutMs?: number | undefined; /** * Retire a subagent immediately after its final task result when the * coordinator did not reuse it for queued work in the same dispatch cycle. * Default false for embedders; runtime hosts opt in. */ retireSubagentOnTaskComplete?: boolean | undefined; /** Optional logger for structured debug/error logging. Falls back to console if omitted. */ logger?: Logger | undefined; /** * When set, the director writes a `fleet.json` manifest to this path * recording every spawned subagent (id, provider, model, role, task * ids). Used by `wstack replay ` to rehydrate a fleet. Pass an * absolute file path — the directory must already exist (the * director-session factory creates it when used together). */ manifestPath?: string | undefined; /** * Optional roster used by `leaderSystemPrompt()` to render a roles * summary into the leader's preamble. Same shape as the roster passed * to `tools()` — typically the same value. */ roster?: Record; /** * Override the built-in fleet preamble (see `DEFAULT_DIRECTOR_PREAMBLE`). * Pass an empty string to suppress the preamble entirely. */ directorPreamble?: string | undefined; /** * Override the built-in subagent baseline (see * `DEFAULT_SUBAGENT_BASELINE`). Pass an empty string to suppress. */ subagentBaseline?: string | undefined; /** * Absolute path to a directory the fleet can use as a shared scratchpad * (read + write by every subagent). When set, the director creates it on * construction and `subagentSystemPrompt()` automatically injects a * "Shared notes" block telling subagents where to drop their findings. * This is the cheap fleet-coordination channel — agents don't need each * other's transcripts, just each other's conclusions. * * Convention: under a fleet run rooted at `//`, * pass `//shared/` here. */ sharedScratchpadPath?: string | undefined; /** * Maximum number of spawns this director can perform across its * lifetime. Default: unlimited. Acts as a hard fleet-wide cost cap — * a runaway leader that keeps spawning workers gets cut off cleanly * instead of burning provider tokens until the user kills the * process. The N+1-th spawn call rejects with a `FleetSpawnBudgetError`. */ maxSpawns?: number | undefined; /** * Maximum nesting depth for spawns. The director constructed by the * user is at depth `spawnDepth` (default 0); any subagent that itself * acts as a director would construct its own `Director` with * `spawnDepth: parent.spawnDepth + 1`. When `spawnDepth >= maxSpawnDepth`, * `spawn()` rejects. The process-wide hard ceiling is 2; configuration can * narrow it but cannot widen it. Default: 2 (root director can spawn workers; a * worker that becomes a sub-director cannot itself spawn further). * This stops infinite recursive director chains from a hostile or * confused prompt. */ maxSpawnDepth?: number | undefined; /** * Current spawn-chain depth for this director instance. Defaults to 0. * A nested director should pass `parent.spawnDepth + 1`. Together with * `maxSpawnDepth` this bounds the chain. */ spawnDepth?: number | undefined; /** * Absolute path to a director-state checkpoint file. When set, the * director writes an incremental snapshot of pending/running/completed * tasks + spawned subagents on every state mutation. Distinct from * `manifestPath`: the manifest is a final record written on shutdown, * the checkpoint is a live mirror useful for crash recovery and the * `wstack resume` "you had N tasks in flight" banner. */ stateCheckpointPath?: string | undefined; /** * Session writer the director should forward task lifecycle events to * (`agent_spawned`, `task_created`, `task_completed`, `task_failed`). * When omitted these events stay in-memory only — useful for tests but * lossy in production. Production callers (the CLI) pass the same * writer the host Agent uses so all events land in a single JSONL. */ sessionWriter?: SessionWriter | undefined; /** * Session id for live fleet/coordinator events. Defaults to * `sessionWriter.id` when a writer is available; accepts a getter so * embedding surfaces can follow session swaps. */ sessionId?: string | (() => string | undefined) | undefined; /** * Debounce window for periodic manifest writes triggered by spawn/ * assign/complete events. Default: 2000ms. Pass 0 to disable periodic * writes (the manifest will then only be written on `shutdown()`). */ manifestDebounceMs?: number | undefined; /** * Fleet-wide cost ceiling. When set, `spawn()` refuses any new subagent * that would push the fleet's total cost above this limit. The cap * is checked BEFORE the spawn is recorded — a refused spawn must not * leak partial state into the manifest or fleet bus. Let in-flight * tasks complete; refuse new spawns only. When omitted or Infinity, * no cost cap applies. * * Distinct from SubagentBudget.maxCostUsd (per-subagent spend) — this * field caps the *entire fleet* total. */ directorBudget?: { /** * Maximum total USD the fleet may spend across all subagents. * Default: Infinity (no cap). */ maxCostUsd?: number | undefined; /** Maximum cumulative input+output tokens across the fleet. */ maxTokens?: number | undefined; } | undefined; /** * Maximum auto-extensions per subagent per budget kind before the * director denies further extensions. A subagent hitting the same * soft limit repeatedly (e.g. 3× budget.threshold_reached for * tool_calls) is likely looping on a prompt/config issue, not * making legitimate progress. Default: 12. Set to Infinity to * disable the cap (use with caution — a misconfigured subagent * could burn unlimited budget). */ maxBudgetExtensions?: number | undefined; /** * Debounce window for state-checkpoint writes. Default: 250ms. * Bursts of spawn/assign/complete events collapse into one disk * hit. Higher values reduce write amplification on fast machines; * lower values improve crash-recovery fidelity (less state lost * on sudden process exit). */ checkpointDebounceMs?: number | undefined; /** * Sessions root directory for per-subagent JSONL transcripts. * When set, the director can read subagent transcripts directly for * `fleet` tool (action: session) — no bridge round-trip needed. Path convention: * `//.jsonl`. */ sessionsRoot?: string | undefined; /** * Director run id — namespaced under `sessionsRoot` to locate per-subagent * JSONLs. Defaults to the director's own `id` when omitted. */ directorRunId?: string | undefined; /** * Pre-built fleet manager. When provided the Director delegates all * fleet-level policy (spawn budgets, manifest assembly, checkpointing) * to this instance and takes ownership of the coordinator + bridge * layer only. This enables unit-testing fleet policy in isolation * and swapping implementations without changing the Director surface. * * When omitted the Director creates its own fleet infrastructure * (same behavior as before this field was added). */ fleetManager?: FleetManager | undefined; /** * Optional LLM classifier for the smart dispatcher. When set, the * `spawn_subagent` tool can accept a free-form `description` field * and the director will automatically route to the best-matching * catalog agent using `dispatchAgent()`. When omitted, routing is * pure heuristic (no provider call) — sufficient for most tasks. * * Build from a `complete(prompt) => string` function using * `makeLLMClassifier(complete)` from the dispatcher module. */ dispatchClassifier?: import('../coordination/dispatcher.js').DispatchClassifier | undefined; /** * Maximum context load (as a fraction of maxContext) the leader agent * is allowed to reach before a new spawn is rejected. Default: 0.85. * When the leader's context pressure exceeds this threshold, spawning * a new subagent is refused — the leader must compact first. * Only used when no `fleetManager` is provided (inline mode). * Set to 1.0 to disable this check. */ maxLeaderContextLoad?: number | undefined; /** * Provider's max context window in tokens. Used with `maxLeaderContextLoad` * to compute the absolute token threshold. Default: 128_000. * Only used when no `fleetManager` is provided (inline mode). * * A function may be supplied when the leader can switch models at runtime; * spawn() reads it lazily so the threshold follows the active model. */ maxContext?: number | (() => number | undefined) | undefined; /** * Per-task model matrix (Config.modelMatrix). When set, a spawn whose * config has no explicit `model` is resolved against this matrix by role * (→ phase → `*`) before the subagent is built — so the spawned event, * manifest, and the agent itself all run the matched model. Explicit * per-spawn `model` overrides always win. * * Pass a **function** (not a snapshot) when the matrix can change at * runtime (the CLI passes `() => configStore.get().modelMatrix`) so a * mid-session `/setmodel` takes effect on the next spawn. A static record * is also accepted for tests and one-shot runs. */ modelMatrix?: ModelMatrixSource | undefined; /** * Optional git-worktree manager for Director fleet isolation. When supplied, * task runners can execute side-effectful subagents in per-task worktrees and * squash-merge successful branches back into the base checkout. */ worktrees?: WorktreeManager | undefined; /** Worktree policy. Defaults to `auto` when `worktrees` is provided. */ worktreePolicy?: FleetWorktreePolicy | undefined; /** * Optional guarded merge-conflict resolver invoked by WorktreeManager when a * successful task branch cannot squash-merge cleanly. */ worktreeConflictResolver?: ((info: { task: TaskSpec; config: SubagentConfig; conflictFiles: string[]; cwd: string; }) => Promise) | undefined; /** * Shared provider/model status tracker. When set, the director checks * whether the resolved provider/model is blocked BEFORE spawning the * subagent — so a blocked model is never assigned to a new worker. */ statusTracker?: ProviderModelStatusTracker | undefined; /** * Session/leader's own provider id — independent per-field fallback * when matrix resolution and explicit config both leave provider * undefined. Both this and sessionModel must be set for every spawn * to be guaranteed credentialed. */ sessionProvider?: string | undefined; /** * Session/leader's own model id — independent per-field fallback * when matrix resolution and explicit config both leave model * undefined. Each field falls back independently; the two fields * are not required to be paired. */ sessionModel?: string | undefined; } export { FleetContextOverflowError, FleetCostCapError, FleetSpawnBudgetError, FleetTokenCapError, } from './director/director-errors.js'; /** @deprecated Import from model-matrix.ts; retained for public compatibility. */ export type { ModelMatrixSource } from './model-matrix.js'; export declare class Director implements DirectorFleetHost, ICoordinator { private static _asManifestEntry; /** Alias for the ICoordinator contract. `id` is retained for backward compatibility. */ get coordinatorId(): string; readonly id: string; /** * The fleet event bus. Backed by `fleetManager?.fleet` when a FleetManager * is injected; otherwise own FleetBus instance (preserves existing behavior). */ readonly fleet: FleetBus; /** * Usage rollup. Backed by `fleetManager?.usage` when a FleetManager is * injected; otherwise own FleetUsageAggregator. */ readonly usage: FleetUsageAggregator; /** * Update the leader agent's current context pressure (full request tokens: * messages + systemPrompt + toolDefs). The director checks this before every * spawn — if the pressure exceeds `maxLeaderContextLoad * maxContext`, * spawning is refused with a `FleetContextOverflowError`. * * Call this after each leader agent iteration to keep the pressure current. * The compactor's `CompactReport.fullRequestTokensAfter` is a natural source. */ setLeaderContextPressure(tokens: number): void; /** * Read the leader agent's current context pressure. */ getLeaderContextPressure(): number; /** * Remaining USD budget for the entire fleet (when a cap is configured). * Returns `undefined` when no cap was set (Infinity). */ getRemainingBudgetUsd(): number | undefined; resolveMaxContext(): number; private currentSessionId; /** * Optional fleet-level policy container. When provided the Director * delegates spawn budgeting, manifest entries, and checkpointing to it * instead of managing those internally. All other behavior is unchanged. */ readonly fleetManager: FleetManager | undefined; /** * Director-side bridge endpoint. Subagents are wired to the same * in-memory transport so the director can `ask()` them synchronously * and they can `send()` progress back. Exposed so external code (e.g. * the TUI) can subscribe to inbound messages. */ readonly bridge: InMemoryAgentBridge; readonly transport: InMemoryBridgeTransport; readonly coordinator: DefaultMultiAgentCoordinator; /** Canonical task identity, ownership, result cache, and waiter state. */ private readonly tasks; /** Per-subagent provider/model metadata, captured at spawn time so the * FleetUsageAggregator's metaLookup can surface readable rows. */ readonly subagentMeta: Map; readonly priceLookups: Map; /** Bridge endpoints we created per subagent (so we can `stop()` them * on shutdown and free transport subscriptions). */ readonly subagentBridges: Map; /** Tracks per-spawn config + assigned task ids for manifest writing. */ readonly manifestEntries: Map; /** Tracks assigned nicknames so the same name is never reused in one fleet. */ readonly usedNicknames: Set; private readonly manifestPath?; private readonly roster?; private readonly directorPreamble; private readonly subagentBaseline; /** See {@link DirectorOptions.taskResultNotifier}. */ private readonly taskResultNotifier?; private readonly subagentIdleTimeoutMs; private readonly retireSubagentOnTaskComplete; private readonly subagentIdleTimers; /** Absolute path to the fleet's shared scratchpad directory, or null * when none was configured. Exposed as a readonly getter for callers * that need to surface the path to the user (e.g. the CLI logging * the location after `--director` boots). */ readonly sharedScratchpadPath: string | null; /** Spawn cap (lifetime total). Infinity means unlimited. */ readonly maxSpawns: number; /** Nesting cap. The N-th director in a chain has `spawnDepth = N-1`. */ readonly maxSpawnDepth: number; /** This director's position in a director chain. Root director = 0. */ readonly spawnDepth: number; /** Live spawn counter for `maxSpawns` enforcement. */ spawnCount: number; /** Optional checkpoint mirror — writes the live task graph + roster to disk. */ readonly stateCheckpoint: DirectorStateCheckpoint | null; /** Optional session writer for emitting task_* / agent_* lifecycle events. */ private readonly sessionWriter; private readonly sessionIdSource; /** Debounce timer for periodic manifest writes. */ private manifestTimer; private manifestWriteChain; private readonly manifestDebounceMs; /** Fleet-wide cost cap (entire fleet total, distinct from SubagentBudget limits). Infinity means no cap. */ readonly maxFleetCostUsd: number; /** Fleet-wide input+output token cap. Infinity means no cap. */ readonly maxFleetTokens: number; /** Sessions root for direct subagent JSONL reads (fleet tool, action: session). */ private readonly sessionsRoot?; /** Director run id for JSONL path resolution. */ private readonly directorRunId; /** Optional logger for structured logging. Falls back to noop when omitted. */ private readonly logger; /** Latest worktree state per task id (allocated/committed/merged/conflict/etc.). */ readonly taskWorktrees: Map; /** Budget admission, progress heartbeat, and extension authority. */ private readonly budgetPolicy; /** * Handle to the coordinator-side `task.completed` listener so we can * unsubscribe in `shutdown()`. Without this, repeated Director * construction (e.g. tests, hot reloads) accumulates listeners on a * cached coordinator and slowly drifts the EventEmitter past its * default cap. */ private taskCompletedListener; /** * Unsub handles for the two `FleetBus.filter()` calls installed in the * constructor for timeout-heartbeat tracking. Without capturing these * and calling them in `shutdown()`, repeated Director construction * (tests, hot reloads, `--director` restarts) accumulates 2 dangling * listeners per Director on the FleetBus, slowly drifting the * EventEmitter past its default cap. Mirrors the rationale on * `taskCompletedListener` above. */ /** Optional LLM classifier for smart dispatch. Passed from options. */ readonly dispatchClassifier?: import('../coordination/dispatcher.js').DispatchClassifier | undefined; /** Leader agent's current context pressure (full request tokens). */ leaderContextPressure: number; /** Maximum context load fraction before spawn is refused. */ readonly maxLeaderContextLoad: number; /** Provider's max context window in tokens, or a live resolver for runtime model switches. */ private readonly maxContext; /** Per-task model matrix (static record or live getter); resolved * per-spawn when no explicit model is set. */ readonly modelMatrix?: ModelMatrixSource | undefined; /** * When set by `workComplete()`, the director stops dispatching new tasks * and terminates all running subagents. Used when the director's LLM decides * the goal is satisfied and no further spawns are needed — prevents the * coordinator from keeping workers alive for tasks that will never arrive. */ workCompleteFlag: boolean; /** Pending /btw notes stashed by the leader agent (see setLeaderBtwNote). * Owned by DirectorBtwNotes (R4); the public btw methods delegate to it. */ private readonly btwNotes; /** Owns active collab-debug sessions (spawn/cancel/alert/list). Extracted * from Director in R4; the public collab methods below delegate to it. */ private readonly collab; /** Prevents large `ask_subagent` answers from bloating the leader's context window. */ readonly largeAnswerStore: LargeAnswerStore; /** Shared provider/model status tracker, or undefined. */ private readonly statusTracker; /** Session/leader's provider id — absolute last-resort fallback for every spawn. */ private readonly sessionProvider; /** Session/leader's model id — paired with sessionProvider above. */ private readonly sessionModel; constructor(opts: DirectorOptions); private handleTaskCompleted; /** Cumulative auto-extension count for one subagent (0 when never extended). */ extensionsFor(subagentId: string): number; /** * Signal that the director's work is done. Once called: * - `spawn()` throws `FleetSpawnBudgetError('max_spawns', …)` — no new * subagents can be created * - Running subagents are NOT forcibly stopped — they finish naturally, * but no new tasks are dispatched to them * * This lets the director LLM say "I'm satisfied with the results, stop * spawning and wind down" — without killing in-flight work mid-execution. * Call `terminateAll()` separately if you need immediate teardown. * * Idempotent — calling twice is a no-op. */ workComplete(): void; /** Returns true if `workComplete()` has been called on this director. */ isWorkComplete(): boolean; /** * Stashes a /btw note on the leader agent's context. The leader's agent loop * calls `consumeBtwNotes()` at each iteration boundary and folds pending notes * into the conversation as a visible block — no abort, no restart, just a * "by the way" nudge the model picks up on its next turn. * * This is the entry point for the host (CLI, TUI) to inject /btw notes * programmatically without going through the slash-command path. */ setLeaderBtwNote(note: string): number; /** * Read and clear all pending /btw notes the leader has stashed. * Returns them in FIFO order (empty array when none). * * Called by CollabSession when a budget threshold event fires so the * Director can inspect accumulated /btw notes before deciding whether * to cancel the collab session or let it continue. */ getLeaderBtwNotes(): string[]; /** * Peek at pending /btw notes without consuming them. * Useful for UI to show "N pending notes" without clearing them. */ peekLeaderBtwNotes(): string[]; /** * Drain (read + clear) all /btw notes in one call. * Alias for getLeaderBtwNotes() — kept for symmetry with consumeBtwNotes() * in the agent's btw.ts. The Director calls this at the point where it * makes a cancellation decision, not on every budget event. */ drainLeaderBtwNotes(): string[]; /** * Cancel an active collab session by its id. * Emits `director.cancel_collab` on the FleetBus so the session's agents * finish early with a 'cancelled' disposition. * * Returns silently if the session id is not tracked or already settled. * The CollabDebugReport will reflect 'cancelled' disposition when awaited. */ cancelCollabSession(sessionId: string, reason?: string): void; /** * Subscribe a callback to be notified whenever a collab session raises * an alert (warning level). The callback receives the full DirectorAlert * payload so the host (CLI, TUI) can display it to the user. * Returns an unsubscribe function. */ onCollabAlert(handler: (alert: import('./collab-debug.js').DirectorAlert) => void): () => void; /** * Returns all active (not yet settled) collab session ids. * Useful for the TUI to render a "N active sessions" badge and for * the host to know what can be cancelled. */ activeCollabSessions(): string[]; /** Best-effort session-writer append. Swallows failures — the director * must not break a fleet run because the session JSONL handle closed. */ appendSessionEvent(event: Parameters[0]): Promise; /** Debounced manifest writer. A burst of spawn/assign/complete events * collapses into one write. Set `manifestDebounceMs` to 0 to write * synchronously (no debounce); set to negative to disable entirely. */ scheduleManifest(): void; private clearManifestTimer; private recordWorktreeTaskUpdate; /** * Spawn a subagent. Delegates the core spawn mechanics to `fleetSpawn()` * in fleet-spawn.ts which is the single source of truth for spawn logic. * Director-specific pre-processing (session fallback, status tracker) runs * before delegation; idle-retirement arming runs after. * * Task assignment, ownership, result retention, and waiting are owned by * DirectorTaskRegistry; this method delegates spawn admission to fleet-spawn. * * Caller-supplied `priceLookup` is optional but recommended — without * it the `cost` column in `usage.snapshot()` stays at 0. */ spawn(callerConfig: SubagentConfig, priceLookup?: { input?: number | undefined; output?: number | undefined; cacheRead?: number | undefined; cacheWrite?: number | undefined; }): Promise; private resolveSpawnModel; /** * Synchronously ask a subagent something via the bridge. Sends a * `task` message addressed to the subagent and awaits a matching * reply (matched by message id). Subagent runners that handle these * requests subscribe to `ctx.bridge` and reply with a message whose * `id` equals the incoming request's id (see `InMemoryAgentBridge`'s * `request` implementation). * * Returns the response payload directly (the bridge wrapper is * unwrapped for ergonomics). Times out after `timeoutMs` (default * matches the bridge's own default of 30s) — surface those rejections * to the caller as actionable errors instead of letting tools hang. */ ask(subagentId: string, payload: unknown, timeoutMs?: number): Promise; /** * Read completed task results and format them as a structured text * block the director's LLM can paste into its own context. The * Director keeps every completed `TaskResult` in `completed` so this * is a pure read — no bridge round-trip, cheap to call. * * The returned string is intentionally markdown-flavored: headers per * subagent, a one-line meta row (iter / tools / ms), and the task's * result text. Pass `style: 'json'` for a programmatic shape instead * (useful when the director model is doing structured-output work). */ rollUp(taskIds: string[], style?: 'markdown' | 'json'): string; /** * Write the fleet manifest to `manifestPath`. Returns the path written * or null when no path was configured. Captures every spawn + its * assigned tasks — paired with per-subagent JSONLs, this is enough to * replay an entire director run. */ writeManifest(): Promise; private writeManifestNow; /** * Cancel the pending manifest debounce timer and drain any in-flight * manifest write so that, once this resolves, no Director-owned manifest * write is armed or running. * * Unlike `shutdown()`, this does NOT tear down the coordinator, bridges, * or waiters — it is the lightweight quiesce that `MultiAgentHost.stopAll()` * needs. `stopAll()` flushes the FleetManager's manifest but never touched * the Director's own writer, leaving its armed debounce timer to fire ~2s * later. Under CPU starvation that late, un-awaited `atomicWrite` (temp * sibling + rename) races a caller that deletes the manifest directory, * producing an `ENOTEMPTY` on the parent `rmdir` (Windows especially). */ quiesceManifest(): Promise; /** * Tear down the director: stop every subagent, close every bridge * endpoint, and (when configured) write the final manifest. Idempotent * — calling shutdown twice is a no-op on the second invocation. */ shutdown(): Promise; /** * Funnel for shutdown-phase errors. We can't throw — `shutdown()` is * called from process-exit paths where an uncaught throw would lose * the manifest write that comes after. But we MUST NOT silently * swallow either — a persistent bridge-close failure would otherwise * mask a real bug. `process.emitWarning` is the right tier: * surfaces on stderr by default, lets the host plug a warning * listener for structured collection, and never affects exit code. */ private logShutdownError; /** * Hand a task to the coordinator. Returns the assigned task id so * callers can wait on it via `awaitTasks([id])`. The coordinator's * concurrency limit applies — the task may queue before running. */ assign(task: TaskSpec): Promise; /** * Assign infrastructure-owned work directly to the coordinator without * manifest/session/checkpoint bookkeeping. The task still uses the normal * subagent runner, budget, and completion events, but it is excluded from * rollups and persisted fleet task history. */ assignInternal(task: TaskSpec): Promise; /** * Block until every task id resolves. Returns results in the same * order as the input. If any task hasn't completed by the time this * is called, the promise hangs until it does — pair with a timeout * at the caller if that's a concern. Resolves immediately for ids * whose results were already cached. */ awaitTasks(taskIds: string[]): Promise; /** * Wait until AT LEAST ONE of the named tasks completes, then return every * requested result already cached plus the still-pending ids. This is the * leader's "handle whichever finishes next" primitive — unlike * `awaitTasks` it never blocks on the slowest sibling, and the siblings * that complete later still reach the leader through the fire-and-forget * report-back (see the consumed-in-band rule in `taskCompletedListener`). * * Deliberately does NOT register `taskWaiters` entries for the pending * ids: a `taskWaiters` entry would mark those results as in-band-consumed * and silence the mailbox notifier for results this caller never receives. * * With `timeoutMs`, resolves (never rejects) `{timedOut: true}` and zero * completions when the window elapses first. */ awaitTasksAny(taskIds: string[], opts?: { timeoutMs?: number; }): Promise; /** Defensive snapshot of tasks still queued (not yet dispatched). */ listPendingTasks(): readonly TaskSpec[]; /** * Re-pin a still-pending task to a different subagent (`undefined` = * unpin) — the rebalancing primitive used by the FleetSupervisor and * available to hosts. Only queued tasks can move; a dispatched task * returns `false` (steer or terminate its worker instead). Keeps * ownership bookkeeping (taskOwners, checkpoint, fleet manifest) in * sync with the coordinator's queue. */ retargetPendingTask(taskId: string, subagentId: string | undefined): boolean; terminate(subagentId: string): Promise; terminateAll(): Promise; remove(subagentId: string): Promise; private clearSubagentIdleRetirement; /** * Arm one lifecycle timer per worker. The callback re-checks coordinator * state so a task assigned in the same turn always wins over retirement. */ private armSubagentIdleRetirement; status(): CoordinatorStatus; /** * Subscribe to coordinator events. Currently only `task.completed` is * exposed (the others are internal lifecycle). Returns an unsubscribe * function. External callers (e.g. the CLI's `MultiAgentHost`) use this * to drive their own pending/results tracking without poking the * coordinator directly. */ on(event: 'task.completed', handler: (payload: { task: TaskSpec; result: TaskResult; }) => void): () => void; /** * Snapshot of every task that has resolved (success, failed, timeout, * stopped) since the director started. Returned in completion order * via the internal map's iteration order. Used by `/fleet status` to * paint the completed table without reaching into private state. */ completedResults(): TaskResult[]; /** * Inject a previously-saved checkpoint snapshot. Call this right after * constructing a Director during a `--resume` run so the in-memory state * (subagents, tasks, waiters) reflects the pre-crash reality instead of * starting from a blank slate. The director then resumes from there — * completing any in-flight tasks and ignoring tasks that already reached * a terminal state in the prior run. */ setCheckpointState(snapshot: DirectorStateSnapshot): void; /** * Read a subagent's JSONL transcript directly from disk (no bridge * round-trip needed). Returns the last assistant text, stop reason, * tool-use count, and line count — or null if the file is unavailable. * Requires `sessionsRoot` to be set on construction. */ readSession(subagentId: string, tail?: number | undefined): Promise<{ lastAssistantText?: string | undefined; lastStopReason?: string | undefined; toolUsesObserved: number; events: number; path?: string | undefined; } | null>; snapshot(): FleetUsage; /** * Look up provider/model metadata for a spawned subagent. Returns * undefined when the subagent id is unknown (not yet spawned, or * already torn down). Callers — notably the TUI fleet panel — use * this to render human-readable provider/model tags next to each * subagent row without reaching into private state. */ getSubagentMeta(id: string): { provider?: string | undefined; model?: string | undefined; name?: string | undefined; } | undefined; /** * Compose the leader/director-agent system prompt: fleet preamble + * (optional) roster summary + user base prompt. Pass the result to your * leader Agent's `ctx.systemPrompt` when constructing it. * * `basePrompt` defaults to `config.leaderSystemPrompt` so callers can * use the no-arg form when the multi-agent config already carries it. */ leaderSystemPrompt(basePrompt?: string): string; /** * Compose a subagent's system prompt for a given `SubagentConfig`: * baseline + role + task + per-spawn override. Returned by value — does * not mutate the config. Factories (the user-supplied `AgentFactory`) * should call this when building each subagent's Agent so the bridge * contract, role context, and override are all surfaced. * * When `taskBrief` is omitted the Task section is dropped. Pass the * actual task description here to reinforce it in the system prompt * (the runner already passes it as user input — duplicating in the * system prompt is optional but improves anchoring on small models). */ subagentSystemPrompt(config: SubagentConfig, taskBrief?: string): string; /** * Build the tool set the LLM-driven director uses to orchestrate. * Returns an array of `Tool` definitions; register these on the * director's `Agent` to expose `spawn_subagent`, `assign_task`, etc. * Each tool's `execute()` delegates straight to the matching method * above. * * Tools all carry `permission: 'auto'` — the *user* has already * approved running the director when they kicked off the run, so * gating individual orchestration calls behind a confirm prompt * would just be noise. The actual subagent tools they spawn are * still permission-checked normally. */ tools(roster?: Record): Tool[]; /** * Attempt to acquire the checkpoint lock. Must be called before * resuming — if another director process is alive, this returns * false and the caller should not proceed with the resume. */ acquireCheckpointLock(): Promise; /** * Start a collaborative debugging session: BugHunter, RefactorPlanner, * and Critic run in parallel on the same target files, with findings * flowing through the FleetBus (bug.found → refactor.plan → * critic.evaluation). Returns a structured CollabDebugReport when all * three agents complete or the session times out. */ spawnCollab(options: CollabSessionOptions): Promise; /** * Resume from a prior checkpoint snapshot (loaded via * `loadDirectorState()`). Re-attach to the fleet mid-flight so * subsequent spawn/assign calls update the checkpoint normally. */ resumeFromCheckpoint(snapshot: DirectorStateSnapshot): void; } //# sourceMappingURL=director.d.ts.map