import type { Message, Provider, ThinkingLevel } from "@kenkaiiii/gg-ai"; import { type AgentDefinition } from "./agents.js"; import { SubAgentStore } from "./subagent-store.js"; import type { AgentNotificationQueue } from "./agent-notifications.js"; import { type SubAgentTokenUsage } from "../tools/subagent-shared.js"; export type SubAgentState = "starting" | "running" | "completed" | "failed" | "interrupted" | "closed" | "reaped"; export interface SubAgentSnapshot { agent_id: string; task_name: string; state: SubAgentState; started_at: number; updated_at: number; elapsed_ms: number; current_activity?: string; turn_count: number; tool_use_count: number; token_usage: SubAgentTokenUsage; output?: string; error?: string; agent_name?: string; provider?: string; model?: string; child_session_id?: string; child_session_path?: string; collected?: boolean; recovered?: boolean; } export interface SubAgentManagerOptions { cwd: string; agents: AgentDefinition[]; getProvider: () => Provider; getModel: () => string; getThinkingLevel: () => ThinkingLevel | undefined; getCacheKey?: () => string | undefined; getBaseUrl?: () => string | undefined; /** Optional per-model concurrency cap (subagentMaxPerModel setting). Counted * against the RESOLVED child model — read-only agents may run on the fast * model, so they count under that model, not the parent's. Only ever * reduces concurrency below the global ACTIVE_LIMIT. */ getMaxPerModel?: () => number | undefined; onState?: (snapshot: SubAgentSnapshot) => void; /** * Push queue for child-completion notifications. When set, a finished child * announces itself into the parent's next turn instead of waiting to be * discovered by `wait_agent` or the pre-stop completion gate. */ notifications?: AgentNotificationQueue; workerEntry?: string; idleTimeoutMs?: number; /** Poll interval for adopting turn records of children orphaned mid-turn. * Test-only knob; production default 5s. */ adoptionPollMs?: number; store?: SubAgentStore; sessionRootDir?: string; } /** * Wait budgets, aligned with the child's own `SUB_AGENT_TIMEOUT_MS` (10 min). * The previous 30s default and 5-min ceiling meant a parent waiting on a * legitimately long child gave up ~20x early and finalized on a half-answer. */ export declare const DEFAULT_WAIT_MS = 120000; export declare const MAX_WAIT_MS: number; /** Shared pre-finalization hook used by both AgentSession and the Ink host. */ export declare function buildSubAgentCompletionFollowUp(manager: Pick | undefined): Message[] | null; export declare class SubAgentManager { private readonly options; private readonly workers; private readonly snapshots; private readonly listeners; private shuttingDown; private shutdownPromise?; private readonly store; private readonly sessionRootDir; private parentSessionId?; private persistQueue; /** Bounded watcher for turn records of children orphaned mid-turn. */ private adoptionWatcher?; private adoptionDeadline; private persistPending; private persistScheduled; /** Agent definitions `spawn` resolves names against — the delegation roster. */ get agents(): readonly AgentDefinition[]; constructor(options: SubAgentManagerOptions); /** Restore bounded history; dead in-flight workers become honestly interrupted. */ hydrate(parentSessionId: string): Promise; /** * Adopt durable turn records for snapshots that were mid-turn at restart. * A record newer than the parent's last observation of that child is the * child's own terminal verdict — it outranks the pessimistic "interrupted". * Still-running orphans get a bounded watcher: the record usually lands * within one turn timeout, and adoption then notifies the parent's next * turn like any other completion. */ private settleAdoptableTurns; /** Fold a durable turn record into the snapshot and announce the adoption. */ private adoptTurnRecord; /** Rebind durable child history after parent compaction creates a continuation. */ rebindParentSession(parentSessionId: string): Promise; /** A genuinely new parent starts with no unrelated child history. */ resetParentSession(parentSessionId: string): Promise; waitForPersistence(): Promise; spawn(taskName: string, task: string, agentName?: string): Promise; sendMessage(agentId: string, message: string): Promise; followup(agentId: string, task: string): Promise; interrupt(agentId: string, collectResult?: boolean): Promise; subscribe(listener: (snapshot: SubAgentSnapshot) => void): () => void; list(): SubAgentSnapshot[]; wait(agentIds?: string[], condition?: "any" | "all", timeoutMs?: number): Promise<{ timed_out: boolean; agents: SubAgentSnapshot[]; }>; completionGate(): { active: SubAgentSnapshot[]; uncollected: SubAgentSnapshot[]; unresolved: number; }; completionGateMessage(): string | undefined; interruptAll(): Promise; shutdownAll(): Promise; /** Synchronous process-exit fallback: terminate detached process groups immediately. */ shutdownAllNow(): void; private markCollected; private spawnWorkerProcess; private respawnRecovered; private attach; private handleFrame; /** * Announce a finished child into the parent's next turn. Carries the id, the * state and a short digest — NOT the output, which stays behind `wait_agent` * so a chatty child cannot dump its transcript into the parent's context. */ private notifyTerminal; private request; private waitForChange; private publish; private snapshot; private queuePersist; private elapsed; private activeCount; /** Enforce the optional per-model cap against the resolved child model. */ private assertModelCapacity; private isActive; private isTerminal; private requireWorker; private assertAvailable; private assertChildSessionPath; private createId; private scheduleIdleReap; private reapExcessIdle; private fail; private close; private kill; } //# sourceMappingURL=subagent-manager.d.ts.map