import { ArchetypeLoader } from '../../agents/archetypes.js'; import { AgentMessageBus } from '../../agents/message-bus.js'; import { WrfcController } from '../../agents/wrfc-controller.js'; import type { ConfigManager } from '../../config/manager.js'; import type { ConversationMessageSnapshot } from '../../core/conversation.js'; import type { RuntimeEventBus } from '../../runtime/events/index.js'; import type { ExecutionIntent } from '../../runtime/execution-intents.js'; import type { AgentInput } from './schema.js'; import type { ProviderRegistry } from '../../providers/registry.js'; import type { WrfcAgentRole } from '../../agents/wrfc-types.js'; import type { TurnInjectionRecord } from '../../agents/turn-knowledge-injection.js'; import type { ProgressBearingRecord } from '../../agents/progress-audience.js'; export type AgentExecutor = { runAgent(record: AgentRecord): Promise; }; export interface AgentManagerDependencies { readonly archetypeLoader?: Pick | undefined; readonly messageBus?: Pick | undefined; readonly wrfcController?: Pick | null | undefined; readonly executor?: AgentExecutor | null | undefined; readonly configManager?: Pick | undefined; /** * Bound on how many finished agents' final conversation snapshot are kept * in the retention ring (see getConversationSnapshot). Defaults to * DEFAULT_CONVERSATION_SNAPSHOT_RETENTION. Test-only knob in practice. */ readonly conversationSnapshotRetention?: number | undefined; /** The live provider registry, when wired up, enables bare model id resolution for spawn() overrides. */ readonly providerRegistry?: Pick | undefined; } /** * Conversation-snapshot tab attach point (Part C6): default bound on how many recently * finished agents' final conversation snapshot AgentManager keeps around * after their live source is released. Without a bound, a long-lived process * that spawns many short-lived agents would retain every finished agent's * full message history forever, this is the "leaking unbounded memory" the * brief calls out. RUNNING agents are unaffected by this bound: their * snapshot is read live from the still-open ConversationManager, whose size * is already governed by the existing context-window compaction machinery * (core/context-compaction.ts), not by this retention ring. */ export declare const DEFAULT_CONVERSATION_SNAPSHOT_RETENTION = 20; export declare const AGENT_TEMPLATES: Record; export interface AgentRecord extends ProgressBearingRecord { id: string; task: string; template: string; model?: string | undefined; provider?: string | undefined; fallbackModels?: string[] | undefined; routing?: AgentInput['routing'] | undefined; executionIntent?: ExecutionIntent | undefined; reasoningEffort?: string | undefined; context?: string | undefined; tools: string[]; /** Bound write authority for this run's `profile` tool; see AgentInput.captureAuthority. */ captureAuthority?: import('../../personal-capture/index.js').CaptureAuthorityDecision | undefined; status: 'pending' | 'running' | 'completed' | 'failed' | 'cancelled'; /** * Set by cancel(id, kind) when status transitions to 'cancelled'. Distinguishes * a graceful interrupt request from a hard kill for display purposes * (verb formalization) without overloading `status`, which is * consumed widely (ledger parse, orchestrator finalize, exportState/ * importState). Absent on records cancelled before this field existed, and * on any record cancelled via the single-arg cancel(id) call, both default * to 'kill' at the read site (fleet/adapters/agent.ts deriveAgentState). */ terminationKind?: 'interrupt' | 'kill' | undefined; startedAt: number; completedAt?: number | undefined; toolCallCount: number; usage?: { inputTokens: number; outputTokens: number; cacheReadTokens: number; cacheWriteTokens: number; reasoningTokens?: number | undefined; llmCallCount: number; turnCount: number; reasoningSummaryCount?: number | undefined; }; error?: string | undefined; /** Per-spawn turn-budget override; replaces the agents.maxTurns default for this run, capped by agents.maxTurnsCap. */ maxTurns?: number | undefined; /** The applied turn budget + its source, stamped on a turn-budget-exhaustion failure so the outcome can report it. */ turnBudget?: { limit: number; source: 'default' | 'spawn-override' | 'policy-bound'; } | undefined; /** Machine-readable failure reason set at the source (e.g. 'max_turns'), not derived from prose. */ failureReason?: string | undefined; fullOutput?: string | undefined; streamingContent?: string | undefined; wrfcId?: string | undefined; wrfcRole?: WrfcAgentRole | undefined; wrfcPhaseOrder?: number | undefined; wrfcSubtaskId?: string | undefined; wrfcRouteReason?: string | undefined; wrfcSubtasks?: AgentInput['wrfcSubtasks'] | undefined; /** Set when this owner agent's chain was created by collapsing a requested fan-out (schema.ts FanoutCollapseInfo). */ fanoutCollapse?: AgentInput['fanoutCollapse'] | undefined; dangerously_disable_wrfc?: boolean | undefined; /** Completion report, or reply to a person; see AgentInput.replyStyle. Absent ⇒ 'report'. */ replyStyle?: 'report' | 'conversational' | undefined; /** * Orchestration engine tag: set by phase-runner.ts when it * spawns an agent to run one WorkItem through one Phase. Mirrors * wrfcId/wrfcSubtaskId, the fleet's agent adapter uses it to parent this * agent node under its work-item ProcessNode (adapters/agent.ts * resolveParentId), separate from the WRFC parenting track so the two * systems' agents are never conflated. */ workItemId?: string | undefined; /** * Overrides this agent's tool working directory (absolute path), see * AgentInput.workingDirectory. Copied from the spawn input at construction * (NOT settable post-hoc like workItemId: AgentOrchestrator.runAgent reads * it synchronously to select/build the per-cwd ToolRegistry before the * caller of spawn() gets its return value back). Absent ⇒ the * orchestrator's default working directory, unchanged from before this * field existed. */ workingDirectory?: string | undefined; cohort?: string | undefined; orchestrationGraphId?: string | undefined; orchestrationNodeId?: string | undefined; orchestrationDepth: number; parentAgentId?: string | undefined; parentNodeId?: string | undefined; capabilityCeilingTools?: string[] | undefined; successCriteria?: string[] | undefined; requiredEvidence?: string[] | undefined; writeScope?: string[] | undefined; executionProtocol: 'direct' | 'gather-plan-apply'; reviewMode: 'none' | 'wrfc'; communicationLane: 'parent-only' | 'parent-and-children' | 'cohort' | 'direct'; /** Appended verbatim to the system prompt when the agent runs. Used by WRFC to inject constraint addenda. */ systemPromptAddendum?: string | undefined; knowledgeInjections?: Array<{ id: string; cls: string; summary: string; reason: string; confidence: number; reviewState: 'fresh' | 'reviewed' | 'stale' | 'contradicted'; }>; /** * Bounded ring of per-turn passive-injection honesty * records, one entry per turn that actually ran retrieval (turns that * reused the prior turn's cached block, or that ran with the feature * flag/budget off, append nothing). See turn-knowledge-injection.ts for * the record shape and recordTurnInjection for the ring-eviction policy. * Deliberately a plain field (no new KnowledgeEvent contract member), * the same entries are also appended to the agent's session transcript * via `session.appendMessage({type:'knowledge_injection', ...})`. */ turnInjections?: TurnInjectionRecord[] | undefined; /** * Transient wake seed set by {@link AgentManager.wakeWithSteer} when a steer * re-triggers a wedged (terminally-failed) agent. runAgentTask consumes it on * the next run: it seeds the fresh conversation with a summary of the prior * run's transcript tail (honest context, not a risky tool-call replay) and the * steer as a user turn, then clears the field. Never persisted across a clean * completion. */ resumeSteer?: { readonly steer: string; readonly priorSummary?: string | undefined; } | undefined; } export declare class AgentManager { private agents; private runtimeBus; private orchestrationGraphs; private readonly archetypeLoader; private readonly messageBus; private wrfcController; private executor; private readonly configManager; /** * Live snapshot accessors for RUNNING agents (conversation-snapshot bridge, Part C6). * Registered by the executor (orchestrator-runner.ts) right after it * creates the agent's ConversationManager; the manager never stores * messages itself while an agent is running, it just holds a callback. */ private readonly conversationSources; /** * Cooperative cancellation bridge: per-agent AbortSignal * registered by an orchestration-engine work item for the duration of one * phase run. AgentOrchestrator reads this via * setCancellationSource/getCancellationSignal and threads it into * toolRegistry.execute opts so opted-in tools (exec, fetch) can abort an * in-flight child process/request immediately, instead of only at the next * turn boundary's status poll. Purely additive, no caller is required to * register anything, and an agent with no registered signal behaves * exactly as before this change. */ private readonly cancellationSignals; /** * Manager-owned abort controllers, one per agent, the seam that lets * cancel()/kill genuinely abort an in-flight provider call (not only * cooperatively at the next turn/tool boundary). An orchestration engine may * ALSO register its own signal via registerCancellationSignal (that one wins * in getCancellationSignal so the engine's own kill path is unchanged); a * plain broker/ACP-spawned agent has no external signal and falls back to this * owned controller, which cancel() aborts. */ private readonly cancellationControllers; /** * Frozen final snapshots for agents whose live source was released (their * run ended). Map insertion order doubles as the bounded ring's age order: * oldest entry (first key) is evicted once conversationSnapshotRetention is * exceeded. See getConversationSnapshot for the read-side contract. */ private readonly frozenConversationSnapshots; private readonly conversationSnapshotRetention; private readonly providerRegistry; constructor(deps?: AgentManagerDependencies); setRuntimeBus(runtimeBus: RuntimeEventBus | null): void; private deriveEffectiveTools; spawn(input: AgentInput): AgentRecord; /** * Re-trigger a wedged agent's processing loop with a steer message as input. * * Only a terminally-FAILED agent is woken: its turn loop has definitively * exited (an exhausted turn/circuit-breaker loop, idle-after-error, or a * watchdog kill), so re-running cannot race a still-live promise, the honest, * safe subset of "wedged". A genuinely-running agent is left alone (its steer * is delivered through the message bus and drained at its next turn boundary, * exactly as today); a completed or cancelled agent is not auto-woken. The * re-run restores context from the frozen transcript tail (a summary, not a * risky tool-call replay) and appends the steer as a fresh user turn. */ wakeWithSteer(agentId: string, steer: string): { woke: boolean; reason: string; }; /** Build an honest prior-context summary from the frozen transcript tail for a wake. */ private summarizeTranscriptTailForWake; getStatus(id: string): AgentRecord | null; cancel(id: string, kind?: 'interrupt' | 'kill'): boolean; /** * Register the live conversation-snapshot source for a running agent * (conversation-snapshot bridge, Part C6). Called by the executor (orchestrator-runner.ts) * once its ConversationManager exists, `source` is invoked on demand by * getConversationSnapshot(); the manager never copies or stores the * messages itself while the agent is running. */ registerConversationSource(agentId: string, source: () => ConversationMessageSnapshot[]): void; /** * Cooperative cancellation bridge: register the AbortSignal * an orchestration engine's cancellation registry created for a work * item's current agent. Called by the engine right after * AgentManager.spawn() so the signal is in place before the agent's first * turn/tool call. */ registerCancellationSignal(agentId: string, signal: AbortSignal): void; /** Drop the registered signal + owned controller once the run ends (success, failure, or cancel). Safe to call unconditionally. */ releaseCancellationSignal(agentId: string): void; /** * The cancellation signal for an agent's in-flight work. An * engine-registered external signal wins (keeps the orchestration engine's own * kill path authoritative); otherwise a manager-owned controller's signal is * returned (created on first read), so a plain broker/ACP-spawned agent still * has an abortable signal that cancel() will trip. */ getCancellationSignal(agentId: string): AbortSignal | undefined; /** * Release the live source for an agent whose run has ended, freezing one * final snapshot into the bounded retention ring (see * DEFAULT_CONVERSATION_SNAPSHOT_RETENTION) so a transcript tab that was * open at the moment of completion keeps showing content instead of going * blank. Once evicted (oldest-first, beyond the retention bound), * getConversationSnapshot falls back to an empty array, callers past that * point are expected to degrade to the on-disk event ledger (TUI * Part C6's documented fallback for completed/detached agents). * * Safe to call even when no source was ever registered for this agentId * (e.g. a WRFC owner agent, which never runs its own turn loop). */ releaseConversationSource(agentId: string): void; /** * The conversation-snapshot tab attach point: a full-fidelity conversation history for a * fleet agent (ConversationMessageSnapshot[], the same shape the main * session surface renders via MessageLineCache/conversation.ts). * * - RUNNING agent with a registered source → the current live snapshot. * - Agent whose run just ended → the frozen final snapshot, until evicted * from the bounded retention ring (oldest-first beyond * conversationSnapshotRetention completed agents). * - Unknown agent, or one long since evicted → empty array. The disk * ledger (.jsonl, written by AgentSession) is NOT a substitute * for this array, it is a truncated event log (tool args/results * sliced to 500 chars, no assistant message text), so callers past * eviction get a degraded activity view, never a fabricated replay. */ getConversationSnapshot(agentId: string): ConversationMessageSnapshot[]; listByGraph(graphId: string): AgentRecord[]; cancelSubtree(rootAgentId: string): string[]; cancelGraph(graphId: string): string[]; list(): AgentRecord[]; listByCohort(cohort: string): AgentRecord[]; clear(): void; exportState(): AgentRecord[]; importState(records: AgentRecord[]): void; setExecutor(executor: AgentExecutor | null): void; setWrfcController(wrfcController: Pick | null): void; } //# sourceMappingURL=manager.d.ts.map