/** * BackgroundManager - Minimal background task orchestration * * Handles: * - Launching background agent sessions (fire-and-forget) * - Tracking task status * - Detecting completion via session.idle events * - Generating completion notifications */ import type { OpencodeClient } from "@opencode-ai/sdk"; import type { BackgroundTask, LaunchInput, CompletionNotification } from "./types"; import type { TaskToastManager } from "../features/task-toast"; export interface BackgroundLimits { /** Max simultaneously running tasks across all sessions. */ maxConcurrent: number; /** Max total tasks a single parent session may spawn (runaway circuit breaker). */ maxPerSession: number; } export declare const DEFAULT_BACKGROUND_LIMITS: BackgroundLimits; /** Thrown by launch() when a concurrency/circuit-breaker limit is hit. */ export declare class BackgroundLimitError extends Error { constructor(message: string); } /** Notifies the parent session when a background task completes or fails. */ export type CompletionNotifier = (notification: CompletionNotification) => void; export declare class BackgroundManager { private tasks; private mainSessionID; private toastManager; private completionNotifier; /** Parent sessions that were deleted; their task completions are silenced. */ private detachedParentSessions; private limits; /** Total tasks ever launched per parent session (for the lifetime circuit breaker). */ private spawnCounts; /** * Slots reserved synchronously at launch() entry, before the async * session.create resolves. Counted toward concurrency so a burst of parallel * launches cannot all pass checkLaunchAllowed and overshoot the cap (TOCTOU). */ private reserved; /** Set once dispose() runs; guards against in-flight launches mutating state. */ private disposed; setLimits(limits: Partial | undefined): void; getLimits(): BackgroundLimits; /** * Authoritative, synchronous admission check. Returns null if a launch is * allowed right now, or a human-readable rejection reason. Counts in-flight * reservations so concurrent launches can't collectively overshoot the cap. * * NOTE: this is the single source of truth, also called inside launch() in * the same synchronous critical section as the reservation increment, so the * check and the reserve are atomic (no TOCTOU gap). */ checkLaunchAllowed(parentSessionID: string): string | null; setToastManager(manager: TaskToastManager): void; /** * Register a callback used to push a completion/failure notification to the * owning parent session (e.g. when a task fails to start, since that path is * not driven by a session.idle event). */ setCompletionNotifier(notifier: CompletionNotifier): void; setMainSession(sessionID: string | undefined): void; getMainSession(): string | undefined; private generateTaskId; launch(client: OpencodeClient, input: LaunchInput): Promise; /** Completes the prompt dispatch after the task record is registered. */ private continueLaunch; getTask(taskId: string): BackgroundTask | undefined; findTaskBySessionID(sessionID: string): BackgroundTask | undefined; getOutput(client: OpencodeClient, taskId: string): Promise; cancel(client: OpencodeClient, taskId: string): Promise; handleSessionIdle(sessionID: string): CompletionNotification | null; /** * Build a completion notification for a single parent session. Tasks from * other sessions are never included, preventing cross-session bleed. * `justFinished` is the task that triggered this notification (so the partial * message names the correct task regardless of Map insertion order). */ getCompletionStatusForSession(parentSessionID: string, justFinished?: BackgroundTask): CompletionNotification | null; listActiveTasks(): BackgroundTask[]; listAllTasks(): BackgroundTask[]; clearCompleted(): void; /** * Mark a parent session as detached (e.g. it was deleted). Finished tasks for * it are dropped immediately; still-running tasks finish silently (no * notification) since there's no session left to notify. */ detachParentSession(sessionID: string): void; /** * Evict the oldest finished tasks when the retained-task count exceeds the cap. * Running tasks are never evicted. Finished tasks whose parent session still * has running tasks are also preserved, so a pending "all complete" summary * for that session is never missing tasks. */ private trimRetainedTasks; /** * Release all in-memory state. Called from the plugin `dispose` hook on teardown. */ dispose(): void; }