import { type AgentResult, type AgentRunner, type RunOptions } from "@automatalabs/shared-types"; import type { AuthenticateRequest, AuthenticateResponse, AuthMethod, DisableProviderRequest, DisableProviderResponse, ListProvidersResponse, ListSessionsResponse, LogoutResponse, SetProviderRequest, SetProviderResponse, SessionConfigOption } from "@agentclientprotocol/sdk"; import type { TSchema } from "typebox"; import { type AcpPoolOptions } from "./pool.js"; import { type AcpEventListener, type AcpEventName } from "./events.js"; import type { Backend } from "./backend.js"; import { InteractiveSession, type InteractiveSessionOptions } from "./interactive.js"; import { type BackendRegistry, type CustomBackendConfig } from "./registry.js"; import { type AuthMethodDescriptor, type AuthResolution, type AuthResolver } from "./auth/auth-types.js"; import { type AuthMethodType, type BackendAuthState } from "./auth/auth-store.js"; import type { ElicitationResolver, PermissionResolver } from "./permissions.js"; export interface ProbedConfigOptions { backendId: string; /** The agent-advertised options, verbatim ACP shapes (id, name, type, currentValue, choices). */ options: SessionConfigOption[]; } export interface ProbeConfigOptionsOptions { cwd?: string; /** Apply the routed model selection before returning the echoed catalog. Default false. */ selectModel?: boolean; } interface LifecycleRoutingOptions { /** Model spec used only to select the backend process. */ model?: string; /** Coarse tier consulted only when `model` is unset. */ tier?: string; /** Event/telemetry label used in strict capability errors. */ label?: string; /** Host-owned cancellation while the lifecycle request is in flight. */ signal?: AbortSignal; } /** Options for AcpAgentRunner.authMethods(). */ export interface AuthMethodsOptions { /** Model spec used only to select the backend process. */ model?: string; /** Coarse tier consulted only when `model` is unset. */ tier?: string; } /** Options for AcpAgentRunner.listSessions(). */ export interface ListSessionsOptions extends LifecycleRoutingOptions { /** Optional absolute working-directory filter. */ cwd?: string; /** Opaque pagination cursor from the previous response. */ cursor?: string; /** Generic ACP `_meta` passthrough for session/list. */ meta?: Record; } /** Options for AcpAgentRunner.deleteSession(). */ export interface DeleteSessionOptions extends LifecycleRoutingOptions { /** Session id returned by session/list or previously persisted by the backend. */ sessionId: string; /** Generic ACP `_meta` passthrough for session/delete. */ meta?: Record; } interface AuthProviderRoutingOptions extends LifecycleRoutingOptions { /** Generic ACP `_meta` passthrough for the request. */ meta?: Record; } /** Options for AcpAgentRunner.authenticate(). */ export interface AuthenticateOptions extends AuthProviderRoutingOptions { /** Authentication method id advertised by runner.authMethods(). */ methodId: AuthenticateRequest["methodId"]; } /** Options for AcpAgentRunner.listProviders(). */ export interface ListProvidersOptions extends AuthProviderRoutingOptions { } /** Options for AcpAgentRunner.setProvider(). */ export interface SetProviderOptions extends AuthProviderRoutingOptions { providerId: SetProviderRequest["providerId"]; apiType: SetProviderRequest["apiType"]; baseUrl: SetProviderRequest["baseUrl"]; headers?: SetProviderRequest["headers"]; /** Durable Vertex project/region — required by the Claude agent's `vertex` apiType * (claude-agent-acp >= 0.60.0), sent as `_meta.claudeCode.vertex` on the wire AND recorded so * every replayed `providers/set` on a fresh pooled connection is accepted. */ vertex?: { projectId: string; region: string; }; } /** Options for AcpAgentRunner.disableProvider(). */ export interface DisableProviderOptions extends AuthProviderRoutingOptions { providerId: DisableProviderRequest["providerId"]; } /** Options for AcpAgentRunner.logout(). */ export interface LogoutOptions extends AuthProviderRoutingOptions { } /** Options for AcpAgentRunner.loadSession(), resumeSession(), and forkSession(). */ export interface ReattachSessionOptions extends InteractiveSessionOptions { /** Existing backend session id to reattach, or the source session id for forkSession(). */ sessionId: string; /** Alias for onPermissionRequest for hosts that name the resolver by role. */ permissionResolver?: PermissionResolver; } /** Options for AcpAgentRunner.completeAuth() (§1.3, §4.1). */ export interface CompleteAuthOptions extends AuthMethodsOptions { /** A method id from describeAuthMethods(). */ methodId: string; /** The host-collected resolution (env values / gateway meta / completed / cancelled) (§1.3). */ resolution: AuthResolution; /** Event/telemetry label used in strict capability errors. */ label?: string; signal?: AbortSignal; } /** The outcome of a host-completed auth step (§1.3, §4.1). Carries no secret. */ export type AuthOutcome = { status: "authenticated" | "cancelled"; methodId: string; recycled: boolean; }; /** Redacted status view surfaced by the controller, MCP tool, and web (canonical shape; §2.14). */ export interface AuthStatusSnapshot { backendId: string; poolKey: string; state: BackendAuthState; authenticated: boolean; canResume: boolean; methods: { id: string; type: AuthMethodType; name?: string; }[]; } /** The `runner.auth` controller — the auth verbs as one addressable object (§2.10, §4.1). */ export interface AuthController { /** Alias of describeAuthMethods(). */ methods(opts?: AuthMethodsOptions): Promise; /** Alias of completeAuth(). */ authenticate(opts: CompleteAuthOptions): Promise; /** Clears the AuthStore for the backend, zeroizes secrets (§2.14), and recycles the pool. */ logout(opts?: LogoutOptions): Promise; /** Redacted, synchronous snapshot — ids/types/names + state only, NEVER secrets (§2.14). */ status(opts?: { backend?: string; }): AuthStatusSnapshot[]; /** Cold-resume re-arm predicate (§2.13): true iff state ∈ {authenticated,credentials_held} or diskBacked. */ canResume(backendId: string): boolean; } /** Structural capability interface the MCP composition root duck-types to register auth tools * without widening the frozen `AgentRunner` seam (§4.1). `AcpAgentRunner` implements it. */ export interface AuthCapableRunner { describeAuthMethods(opts?: AuthMethodsOptions): Promise; completeAuth(opts: CompleteAuthOptions): Promise; /** Ids of every configured backend (built-ins + AcpRunnerOptions.backends), whether or not it * yet has a BackendAuthMachine. */ listBackends(): string[]; readonly auth: AuthController; } /** Structural capability interface the MCP composition root duck-types to register the provider * tools, symmetric to AuthCapableRunner and equally seam-preserving. `AcpAgentRunner` implements * it. This is the GENERIC base-spec `providers/*` surface: every method is advertise-gated per * backend (`agentCapabilities.providers`), so any spec-conformant agent that advertises the * unstable providers block is served with zero agent-specific code. */ export interface ProviderCapableRunner { listProviders(opts?: ListProvidersOptions): Promise; setProvider(opts: SetProviderOptions): Promise; disableProvider(opts: DisableProviderOptions): Promise; /** Ids of every configured backend (built-ins + AcpRunnerOptions.backends). */ listBackends(): string[]; } /** Constructor options for the runner: pool sizing, client-side handlers, and the custom-backend * registry. `backends` merges over (and wins against) env-declared AGENTPRISM_BACKENDS entries. */ export interface AcpRunnerOptions extends AcpPoolOptions { /** Custom ACP backends, keyed by registered name (see registry.ts for the config shape * and the routing rules). Names are ASCII-case-insensitive and shadow built-in ids. */ backends?: Record; /** Runner-wide human-in-the-loop permission resolver. When set, it replaces ToolPolicy * auto-decisions for every session that does not provide its own resolver. */ onPermissionRequest?: PermissionResolver; /** Runner-wide ACP elicitation responder. When set, initialize advertises unstable * elicitation form/url support on every connection; sessions may override the resolver. */ onElicitation?: ElicitationResolver; /** Which auth method TYPES this host can complete (§1.2). When set, initialize advertises the * matching client auth capability (`auth.terminal` + top-level `_meta["terminal-auth"]`, and/or * `auth._meta.gateway`) on every connection, fixed for the connection lifetime. Unset (and * `onAuth` unset) omits the `auth` capability entirely — the default-OFF, zero-behavior-change * baseline. When `onAuth` is set but this is unset it derives to `{ terminal: false, gateway: true }` * (§1.2). A native-TTY CLI host passes `{ terminal: true, gateway: true }`. */ authCapabilities?: { terminal?: boolean; gateway?: boolean; }; /** Inline auth resolver (§1.3, §2.11). When set, a -32000 at session/new resolves-and-retries-once * and the run NEVER pauses; when unset, a -32000 run pauses with reason:"auth_required" (§2.12, * PR4). Mutually exclusive with pause by construction. */ onAuth?: AuthResolver; } /** * ACP-backed AgentRunner implementation. The caller that constructs an AcpAgentRunner owns it: * pass it into managers/runs as needed, then call dispose() (or use `await using`) when that * owner is done with the pooled and dedicated backend processes. */ export declare class AcpAgentRunner implements AgentRunner, AuthCapableRunner, ProviderCapableRunner { private readonly pool; /** The resolved custom-backend registry (env + option, validated at construction). */ private readonly backends; /** Typed bus carrying every ACP event from every pooled or interactive session. Beyond the * AgentRunner seam (additive observability) — subscribing never affects a run and never enters * the resume hash. */ private readonly events; private readonly emitEvent; /** Client-side handlers and the runner-wide permission resolver are initialize/session wiring, * so dedicated interactive connections must receive the SAME deps the pool receives. */ private readonly clientHandlers; private readonly permissionResolver; private readonly elicitationResolver; /** Client auth advertisement, derived ONCE at construction and fixed for every connection this * runner opens (pooled and dedicated). Undefined => the `auth` capability is omitted (§1.2). */ private readonly authCapabilities; /** Inline auth resolver (§2.11). When set, run() resolves a -32000 and retries once instead of * surfacing AUTH_REQUIRED. Undefined => the (PR4) pause-and-resume path. */ private readonly onAuth; /** The single per-runner auth store (§2.2). Holds every backend's `BackendAuthMachine`; the only * home for credential material in the library. Threaded into the pool and every dedicated * connection so all connection types reconcile to the same intent. */ private readonly authStore; /** The single per-runner provider-intent store — the providers/* sibling of the AuthStore. * setProvider records here after the wire call succeeds; every connection (pooled and * dedicated) replays the recorded routing at initialize, so client-configured providers * survive pool recycles and dispose-after-use dedicated connections. */ private readonly providerStore; /** The auth verbs as one addressable object (§2.10). */ readonly auth: AuthController; private readonly structuredOutputTools; /** Held-open interactive sessions own dedicated ACP processes outside the pool. The runner * tracks their connections so dispose() can release them and the process-exit hook can * synchronously kill any dedicated children if the host exits without release(). */ private readonly interactiveSessions; private readonly onProcessExit; private exitHookInstalled; private disposed; constructor(options?: AcpRunnerOptions); /** * Listen in on the live ACP stream. `name` is an ACP `sessionUpdate` discriminant * ("agent_message_chunk", "tool_call", "usage_update", …) or one of the cross-cutting events * ("session_update" catch-all, "permission_pending", "permission_request", * "elicitation_pending", "elicitation_request", "elicitation_complete", "raw_message", * "session_open", "session_close", "backend_error"). The listener is typed to the event. * Returns an unsubscribe thunk. A pooled runner multiplexes many concurrent runs, so each * event carries `{ sessionId, backendId, label?, runId? }` for filtering. Listeners are * best-effort observers: a throwing listener is isolated and never affects the run. */ on(name: K, listener: AcpEventListener): () => void; /** Subscribe once; the listener auto-unsubscribes after its first delivery. */ once(name: K, listener: AcpEventListener): () => void; off(name: K, listener: AcpEventListener): void; removeAllListeners(name?: AcpEventName): void; listenerCount(name: AcpEventName): number; /** * Open a held ACP session for multi-turn callers. Unlike run(), this does NOT acquire a pool * slot: it spawns one dedicated backend process, opens one ACP session on it, and hands the * caller an InteractiveSession that must be released. The dedicated process means a long-lived * chat/debug loop never starves one-shot run() calls on the same backend (the default pool size * is one). */ openSession(opts: InteractiveSessionOptions): Promise; /** Route a model spec, open exactly one session without prompting, and return the agent's * advertised config-option catalog verbatim. */ probeConfigOptions(spec?: string, opts?: ProbeConfigOptionsOptions): Promise; /** Return the selected backend's initialize-advertised authentication methods. */ authMethods(opts?: AuthMethodsOptions): Promise; /** Drive ACP authenticate on the selected backend. REBUILT off dispose-after-authenticate (§2.9): * instead of opening a dedicated connection and disposing it in `finally` — which lost any * in-process (gateway) credential the agent stored on that process (gap 3) — this records the * credential into the durable `AuthStore` and recycles the pool. A method with `_meta` records an * in-process/disk intent replayed on every pooled connection's initialize; a bare method with no * `_meta` fires the one-shot `agent-login` RPC so the agent runs its own login. */ authenticate(opts: AuthenticateOptions): Promise; /** Proactively enumerate the selected backend's advertised methods, already type-dispatched (§1.3) * and label-enriched by the backend's `AuthProfile.describe` (§3.1, identity for a profile-less * custom backend). A read-only probe: opens a dedicated connection, reads the initialize-advertised * methods, runs the base dispatcher through the profile seam, and disposes. */ describeAuthMethods(opts?: AuthMethodsOptions): Promise; /** Record the host-collected resolution into the AuthStore, advance the generation, and recycle * the pool (§2.9/§2.6) so a subsequent run() always lands on a current connection. */ completeAuth(opts: CompleteAuthOptions): Promise; /** Ids of every configured backend (built-ins + AcpRunnerOptions.backends). */ listBackends(): string[]; /** The configured DEFAULT backend id — the registry's own routing for * an omitted model (`selectBackend({})`: the `AGENTPRISM_DEFAULT_BACKEND` * env-configured backend when registered, the built-in `claude` * otherwise). The repl-engine's broker serves this to the guest library * (verify/judgePanel resolve their reviewer/grader spec through it — a * real registered segment, never the deleted reserved sentinel). */ defaultBackendId(): string; /** List configurable providers from the selected backend. */ listProviders(opts?: ListProvidersOptions): Promise; /** Configure one provider on the selected backend. The wire call validates against the live * agent (unknown providerId/apiType errors surface immediately); on success the routing is * recorded as a durable intent — provider config is in-process agent state for e.g. codex-acp, * so without the record this dedicated connection's dispose would silently discard it (the * providers/* sibling of the dispose-after-authenticate bug). Every later connection replays * the intent at initialize, and the pool recycles so no session runs under stale routing. * The durable `meta` (e.g. the vertex `_meta.claudeCode.vertex` project/region that the 0.60.0+ * Claude agent stores as provider config) is recorded alongside and replayed verbatim, so a * reconstructed `providers/set` on a fresh connection is accepted rather than rejected. */ setProvider(opts: SetProviderOptions): Promise; /** Disable one provider on the selected backend, drop its recorded routing intent, and recycle * the pool so no future session replays it. Idempotent like the wire method. */ disableProvider(opts: DisableProviderOptions): Promise; /** Logout through the selected backend. REBUILT (§2.9): first clear the AuthStore machine * (zeroizing `authenticateMeta`/`envValues`, §2.14) and recycle the pool so no pooled process * replays a stale gateway credential, THEN issue the agent `logout` RPC only where advertised * (gated on `supportsLogout`; opencode advertises none → store-clear + recycle, no RPC, §3.4). */ logout(opts?: LogoutOptions): Promise; /** List persisted ACP sessions from the selected backend. */ listSessions(opts?: ListSessionsOptions): Promise; /** Delete a persisted ACP session from the selected backend. */ deleteSession(opts: DeleteSessionOptions): Promise; /** Load an existing ACP session and return a live, routed InteractiveSession. */ loadSession(opts: ReattachSessionOptions): Promise; /** * Fork an existing ACP session into a new independent session seeded with the source's context. * The returned InteractiveSession and its sessionRef carry the NEW session id. This unstable SDK * method is gated on sessionCapabilities.fork and fails before any session/fork wire request when * the selected backend does not advertise it. */ forkSession(opts: ReattachSessionOptions): Promise; /** Resume an existing ACP session without replay and return a live, routed InteractiveSession. */ resumeSession(opts: ReattachSessionOptions): Promise; run(prompt: string, options?: RunOptions): Promise>; /** Tear down the whole pool (close every long-lived process). Call when the run ends / the * runner is disposed. Beyond the AgentRunner seam (additive) — never enters the resume hash. */ dispose(): Promise; [Symbol.asyncDispose](): Promise; /** * Synchronously force-kill every backend process tree. Hosts use this only after a bounded * graceful dispose deadline; it is additive to the normal asynchronous dispose() contract. */ forceKill(): void; private createInteractiveSession; private createDedicatedConnection; /** Read the selected backend's initialize-advertised auth methods on a dedicated connection and * build their type-dispatched descriptors (§1.3). A read-only probe; the connection is disposed. */ private probeAuthMethods; /** The shared write path (§2.9) behind completeAuth, the inline resolver (§2.11), and the rebuilt * legacy authenticate(). Records the payload/methodType from the outcome but DERIVES `klass` from * the chosen method's type + `_meta` shape (§2.1) — never from the resolution outcome. */ private applyResolution; /** Inline resolve-and-retry-once at the run() session-acquisition seam (§2.11). Builds the * AuthContext from the backend's advertised methods, invokes `onAuth`, and applies the result. * Returns false on a cancelled/absent resolution (the caller propagates AUTH_REQUIRED). */ private resolveInlineAuth; /** Redacted status snapshots (§2.10/§4.1). Enumerates every configured backend when `backend` is * omitted; never exposes secrets. */ private authStatus; private snapshotFor; /** Cold-resume re-arm predicate (§2.13). */ canResume(backendId: string): boolean; /** Build the backend choice, model-selection spec, tool policy, and session/new options in one * place for run() and openSession() so new AcpSessionOptions fields cannot drift by path. */ private prepareSession; private installExitHook; private removeExitHook; /** Synchronous best-effort child kill for the process-exit hook (no async work is possible). */ private killAllSync; } /** Factory the mcp-server composition root calls to inject the runner into the engine. The pool * size and the custom-backend registry are runner-level options — NOT RunOptions fields, so they * never enter hashAgentCall / the resume identity. Env fallbacks: AGENTPRISM_ACP_POOL_SIZE for * size, AGENTPRISM_BACKENDS (JSON) for backends. */ export declare function createAcpRunner(options?: AcpRunnerOptions): AcpAgentRunner; /** Pick the backend for the effective model spec (`model` wins over `tier`). The first segment * routes only when it is a registered custom or built-in harness name; everything else goes to * the configured default backend without interpretation. */ export declare function selectBackend(opts: { model?: string; tier?: string; }, registry?: BackendRegistry): Backend; export {}; //# sourceMappingURL=runner.d.ts.map