import type { Backend } from "./backend.js"; import { PooledConnection, SessionHandle, type AcpSessionOptions } from "./acp-client.js"; import { type ClientHandlers } from "./client-handlers.js"; import type { AcpEventSink } from "./events.js"; import type { ElicitationResolver, PermissionResolver } from "./permissions.js"; import type { AuthStore } from "./auth/auth-store.js"; import type { ProviderStore } from "./provider-store.js"; interface IdleTimer { set(callback: () => void, ms: number): { unref?(): void; }; clear(timer: { unref?(): void; }): void; } export interface AcpPoolOptions { /** Steady-state processes to keep PER backend. Default 1; falls back to AGENTPRISM_ACP_POOL_SIZE. */ size?: number; /** Client-side ACP fs/terminal handlers advertised at initialize and routed per session. */ clientHandlers?: ClientHandlers; } /** Internal wiring the runner injects (NOT part of the public AcpPoolOptions surface): the typed * event sink and runner-default permission resolver forwarded to every PooledConnection. */ export interface AcpPoolDeps { onEvent?: AcpEventSink; permissionResolver?: PermissionResolver; elicitationResolver?: ElicitationResolver; advertiseElicitation?: boolean; /** Initialize-time client auth advertisement (§1.2), forwarded to every PooledConnection. * Undefined omits the `auth` capability — the default-OFF baseline. */ authCapabilities?: { terminal?: boolean; gateway?: boolean; }; /** The runner's single auth store (§2). When present, connection selection is generation-gated: * no session is ever opened on a connection whose applied intent-generation is stale. Undefined * => no gating, byte-identical to the pre-auth baseline. */ authStore?: AuthStore; /** The runner's single provider-intent store. When present, connection selection is also gated * on the provider-routing generation, so no session is ever opened on a process still running * under stale (or missing) client-configured provider routing. Undefined or never-recorded => * no gating, byte-identical baseline. */ providerStore?: ProviderStore; /** Deterministic elastic-idle clock seam. Production uses the platform timers. */ idleTimer?: IdleTimer; } /** Resolve the per-backend pool size: explicit option wins, else env, else 1. Clamped to >= 1. */ export declare function resolvePoolSize(option?: number): number; export declare class AcpAgentPool { private readonly deps; private readonly size; private readonly clientHandlers; private readonly byBackend; /** Connections whose async disposal is still in progress, including ones already removed from admission. */ private readonly disposingConnections; /** Warm keep-alive timers for currently idle connections above the steady-state pool size. */ private readonly elasticIdleTimers; private readonly idleTimer; private readonly onProcessExit; private exitHookInstalled; private disposed; constructor(options?: AcpPoolOptions, deps?: AcpPoolDeps); /** Acquire a session for one agent() run: get/grow a pooled connection and open a session. */ acquire(backend: Backend, opts: AcpSessionOptions): Promise; /** Acquire a connection slot, then let the caller prepare session/new after initialize. */ acquirePrepared(backend: Backend, prepare: (connection: PooledConnection) => AcpSessionOptions | Promise, context?: { signal?: AbortSignal; label?: string; injected?: boolean; }): Promise; /** Acquire a ready connection slot and reattach by the best capability negotiated on it. */ acquirePreparedReattach(backend: Backend, sessionId: string, prepare: (connection: PooledConnection) => AcpSessionOptions | Promise, context?: { signal?: AbortSignal; label?: string; injected?: boolean; }): Promise<{ handle: SessionHandle; method: "resume" | "load"; }>; /** * Pick the connection to host the next session. Runs SYNCHRONOUSLY (no await) through both the * injected-process reservation here and the load reservation in openSession(), so concurrent * acquires never over-spawn or double-book a connection. */ private selectConnection; /** Release the selection-owned reservation only after SessionHandle.release() has completed, * then retain surplus processes warm for one idle keep-alive before shrinking. */ private releaseSelection; /** Reconcile every live connection for a key to the current generation (§2.6). Stale-but-busy * connections are DRAINED (recycled on release), not disposed synchronously, so in-flight prompts * finish under the auth they started with. Never blocks. */ private reconcileStale; /** Reconcile every live connection for a key to the current provider-routing generation. There * is no live re-apply lane here (provider changes are rare host-level config): an idle stale * process is recycled now, a busy one drains and recycles on release — mirroring the * disk branch of reconcileStale. Never blocks. */ private reconcileProviderStale; /** Public: reconcile every live connection for a backend to the current generation (§2.6). Called * by the runner immediately after a host-completed auth — or a provider-routing change — so a * subsequent run() lands current. */ recycle(poolKey: string): void; /** Spawn a fresh pooled connection (a fresh process primes the current intent at initialize). */ private spawn; private connectionsFor; /** Evict a dead connection so it is never handed out again. */ private drop; /** * Remove a stale connection from admission while retaining its disposal promise. A later pool * shutdown must await this graceful teardown, and its deadline must still be able to force-kill * the process tree if it has not settled yet. */ private disposeAndDrop; /** Retain one memoized connection disposal until it settles, without leaking a rejection. */ private trackDisposal; /** Schedule one warm-idle reap for a surplus connection. Floor connections stay pinned. */ private scheduleElasticReap; /** A synchronous admission cancels the idle countdown before any await can let it fire. */ private cancelElasticReap; private clearElasticReaps; /** * Close every pooled process and clear the admission registry. Connections remain reachable * through `disposingConnections` until their asynchronous graceful teardown settles so a host * lifecycle deadline can still synchronously force-kill them. */ dispose(): Promise; /** Synchronously kill live pooled and in-progress-disposal backend process trees. */ forceKill(): void; private allConnections; private installExitHook; private removeExitHook; /** Synchronous best-effort child kill for the process-exit hook (no async work is possible). */ private killAllSync; } export {}; //# sourceMappingURL=pool.d.ts.map