/** * The ACP transport seam — ADR 0062, slice 2. * * ACP frames JSON-RPC 2.0 as **newline-delimited JSON over stdio** (one complete * message per line, UTF-8, no `Content-Length` headers — that is LSP/MCP framing, * not ACP). {@link AcpConnection} speaks only to this narrow {@link AcpTransport} * port, so the JSON-RPC peer never knows whether it is wired to a spawned * `opencode acp` subprocess ({@link ./spawn.ts}) or, in a test, to an in-memory * fake agent ({@link inMemoryTransportPair}). Transport is a seam, never * re-implemented per backend (AGENTS.md: no drift surfaces). */ /** * A bidirectional stream of already-parsed JSON-RPC messages. Implementations own * the newline framing and `JSON.parse`/`stringify` at the byte boundary; the peer * above works purely in terms of JSON values. */ export interface AcpTransport { /** Serialise and write one JSON-RPC message toward the peer. */ send(message: unknown): void; /** * Register the handler for inbound messages. Called once by the connection on * construction. A malformed line is surfaced via {@link onError} rather than * delivered here. */ onMessage(handler: (message: unknown) => void): void; /** Register a handler for transport-level errors (e.g. an unparseable line). */ onError(handler: (error: Error) => void): void; /** Close the transport and release its underlying resource. Idempotent. */ close(): void; } /** * Split a byte stream into complete newline-delimited JSON messages. Handles * chunk boundaries that fall mid-line (buffering the remainder) and ignores * blank lines. Each decoded value is handed to `onMessage`; a line that fails to * parse goes to `onError` and does not abort the stream. */ export declare class NewlineJsonDecoder { #private; constructor(onMessage: (message: unknown) => void, onError: (error: Error) => void); /** Feed a decoded string chunk; emits every complete line it now contains. */ push(chunk: string): void; /** * Deliver any final buffered line at stream end (EOF without a trailing * newline). A compliant peer newline-terminates every message, but on abrupt * process/pipe EOF a complete final message can sit unterminated in the buffer; * flushing it surfaces the message (or a parse error) instead of silently * dropping it. Idempotent: it clears the buffer, so a second call is a no-op. */ flush(): void; } /** Serialise a JSON-RPC message as a single ACP wire line (JSON + `\n`). */ export declare function encodeMessageLine(message: unknown): string; /** * A pair of transports wired directly to each other in memory: what one `send`s * the other receives (after a `queueMicrotask` hop, so delivery is asynchronous * like a real pipe and never re-enters the sender synchronously). The reference * substrate for driving a fake agent in tests without spawning a process. */ export declare function inMemoryTransportPair(): { client: AcpTransport; agent: AcpTransport; };