import type { Message } from '../types/messages.js'; import type { Principal } from '../types/principal.js'; import type { Store } from '../types/store.js'; import { type ProtocolGraph, type ProtocolInterrupt } from './shared.js'; export type { ProtocolGraph, ProtocolInterrupt, ProtocolResult, ProtocolRunOptions } from './shared.js'; /** The protocol version this module speaks. */ export declare const ACP_PROTOCOL_VERSION = 1; /** What a tool call does, so the editor can show it: ACP's tool kinds. */ export type AcpToolKind = 'read' | 'edit' | 'delete' | 'move' | 'search' | 'execute' | 'think' | 'fetch' | 'other'; /** Why a prompt's turn ended. */ export type AcpStopReason = 'end_turn' | 'max_tokens' | 'max_turn_requests' | 'refusal' | 'cancelled'; /** One block of a prompt: text, an image, audio, a file the editor embedded, or a link to one. */ export interface AcpContentBlock { /** `text`, `image`, `audio`, `resource`, `resource_link`, or a kind this agent does not read. */ type: string; /** A text block's text. */ text?: string; /** An image or audio block's bytes, as base64. */ data?: string; /** An image or audio block's media type, such as `image/png` or `audio/wav`. */ mimeType?: string; /** A link's URI. */ uri?: string; /** A link's name. */ name?: string; /** An embedded file. */ resource?: { uri: string; text?: string; mimeType?: string; }; } /** What the agent tells the editor during a turn: ACP's `session/update`. */ export type AcpSessionUpdate = { sessionUpdate: string; } & Record; /** An editor session, as `input` sees it. */ export interface AcpSessionInfo { /** Its id, which is also the graph's thread. */ id: string; /** The directory the editor opened the session in. */ cwd: string; /** The MCP servers the editor offered. */ mcpServers: unknown[]; } /** Where the agent reads and writes: `process.stdin` and `process.stdout`, or any pair like them. */ export interface AcpStreams { /** What the editor writes. */ input: AsyncIterable; /** Where the agent writes. */ output: { write(chunk: string): unknown; }; } /** Options for `serveAcp()`. */ export interface AcpOptions { /** The agent's name and version, as the editor shows them. */ agentInfo?: { name: string; version: string; title?: string; }; /** * The graph input for a prompt. Defaults to `{ messages }`: the session's conversation and the * prompt, which is what an agent from `createAgent()` takes. */ input?: (prompt: string, session: AcpSessionInfo, history: Message[], blocks: AcpContentBlock[]) => unknown; /** * The answer the next prompt gives a question that is not a tool approval. Such a question is * asked as the agent's message, and the turn ends. Defaults to the prompt's text. */ answer?: (prompt: string, interrupt: ProtocolInterrupt) => unknown; /** What a tool does, for the editor. Defaults to a guess from its name. */ kind?: (name: string, args: unknown) => AcpToolKind; /** Who the agent acts for: the person at the editor. */ principal?: Principal; /** * Where sessions are kept. Defaults to memory; `storeAcpSessions()` keeps them in a long-term store, * so an editor reopens a session with `session/load` after the agent restarts. A question the last * turn asked is kept too, and so is a turn the agent was in the middle of, which the load carries on. */ sessions?: AcpSessionStore; /** Gives the agent what the editor offers: its files, its terminal, and its MCP servers. */ editor?: AcpEditorBridge; } /** What the editor said it can do for the agent, in `initialize`. */ export interface AcpClientCapabilities { /** Reading and writing files through the editor, including ones it has open and unsaved. */ fs?: { readTextFile?: boolean; writeTextFile?: boolean; }; /** Running commands in the editor's terminals. */ terminal?: boolean; } /** The connection to the editor, as a bridge sees it. */ export interface AcpConnection { /** Asks the editor something, such as `fs/read_text_file`, and resolves with its answer or rejects with its error. */ request(method: string, params: unknown, signal?: AbortSignal): Promise; /** What the editor can do, from `initialize`. */ capabilities(): AcpClientCapabilities; /** A session's directory and MCP servers. */ session(sessionId: string): AcpSessionInfo | undefined; } /** Something that uses the editor's connection, such as `acpEditor()`. `serveAcp()` attaches it when it starts. */ export interface AcpEditorBridge { /** * The MCP transports it connects beyond stdio, which the agent tells the editor in `initialize` so * the editor offers servers over them too. */ readonly mcpCapabilities?: { http?: boolean; sse?: boolean; }; /** Receives the connection to the editor. */ attach(connection: AcpConnection): void; /** Called when the editor closes its end, or the server is closed: the place to stop what it started. */ detach?(): void | Promise; } /** A session as a store keeps it. */ export interface AcpSessionRecord { /** The session. */ info: AcpSessionInfo; /** The conversation, tool calls included. Audio is kept as a note of it, not its bytes. */ history: Message[]; /** The questions the last turn asked, which the next prompt answers. */ pending?: ProtocolInterrupt[]; /** Approvals already given for the paused run, sent with those answers. */ answered?: Record; /** Tool names a person chose to always allow (true) or always reject (false). */ always: Array<[string, boolean]>; /** The turn the agent is in the middle of, so a load after the agent stopped can carry it on. */ turn?: AcpTurnRecord; /** ISO-8601 time it was last written. */ updatedAt: string; } /** A turn in progress, as a session keeps it. */ export interface AcpTurnRecord { /** `invoke` for a prompt that started a run, `resume` for one answering the last turn's questions. */ kind: 'invoke' | 'resume'; /** The prompt, as the conversation keeps it. */ user: Message; /** The prompt's blocks, for an `input` that reads them, when the run must start over. */ blocks?: AcpContentBlock[]; /** The answers a resume gave, by interrupt id. */ answers?: Record; /** ISO-8601 time it began. */ startedAt: string; } /** Where an ACP agent keeps its sessions. */ export interface AcpSessionStore { /** A session's record. */ get(sessionId: string): Promise | AcpSessionRecord | undefined; /** Writes a session's record. */ put(record: AcpSessionRecord): Promise | void; } /** Sessions in this process: the default. */ export declare class MemoryAcpSessionStore implements AcpSessionStore { private readonly records; /** A session's record, as a copy. */ get(sessionId: string): AcpSessionRecord | undefined; /** Writes a session's record. */ put(record: AcpSessionRecord): void; } /** Sessions in any long-term `Store`: the memory, Redis, Postgres, or SQLite store. */ export declare function storeAcpSessions(store: Store, options?: { namespace?: readonly string[]; }): AcpSessionStore; /** A running ACP connection. */ export interface AcpServer { /** Resolves when the editor closes its end. */ closed: Promise; /** Stops every running prompt. */ close(): void; } /** * Serves a graph to an editor over ACP. Run it as the agent's process: * * ```ts * serveAcp(agent, { input: process.stdin, output: process.stdout }, { agentInfo: { name: 'reviewer', version: '1.0.0' } }); * ``` */ export declare function serveAcp(graph: ProtocolGraph, streams: AcpStreams, options?: AcpOptions): AcpServer;