Skip to main content

Overview

The Stream interface and utilities enable bidirectional communication between ACP clients and agents. Streams handle the serialization and deserialization of JSON-RPC messages over various transport layers.

Stream Interface

The Stream type represents a bidirectional communication channel that:
  • Receives JSON-RPC messages through the readable stream
  • Sends JSON-RPC messages through the writable stream

Properties

writable

A writable stream for sending JSON-RPC messages. Messages written to this stream are serialized and sent to the remote peer.

readable

A readable stream for receiving JSON-RPC messages. Messages arriving from the remote peer are deserialized and made available through this stream.

ndJsonStream

Creates an ACP Stream from a pair of newline-delimited JSON streams. This is the typical way to handle ACP connections over stdio or other byte-oriented transports. Parameters:
  • output (WritableStream<Uint8Array>) - The writable stream to send encoded messages to
  • input (ReadableStream<Uint8Array>) - The readable stream to receive encoded messages from
Returns: A Stream object for bidirectional ACP communication

How It Works

The ndJsonStream function:
  1. Encoding (Writable Stream):
    • Takes AnyMessage objects
    • Serializes them to JSON
    • Appends a newline character
    • Encodes as UTF-8 bytes
    • Writes to the output stream
  2. Decoding (Readable Stream):
    • Reads UTF-8 bytes from the input stream
    • Buffers incomplete lines
    • Splits on newline characters
    • Parses each line as JSON
    • Emits AnyMessage objects

Error Handling

The function handles errors gracefully:
  • Parse errors: Logged to console but don’t stop the stream
  • Empty lines: Silently skipped
  • Incomplete messages: Buffered until complete

Usage Examples

Creating a Stream from stdin/stdout (Node.js)

Creating a Stream from stdin/stdout (Deno)

Creating a Stream from WebSockets

Creating a Custom Stream Implementation

For custom transport layers, you can create Stream objects directly without using ndJsonStream:

Testing with In-Memory Streams

Stream Lifecycle

Connection Establishment

  1. Create a Stream with your transport layer
  2. Pass the Stream to AgentSideConnection or ClientSideConnection
  3. The connection automatically starts reading from the stream

Message Flow

Connection Closure

The connection closes when:
  • The readable stream ends (remote peer disconnected)
  • An unrecoverable error occurs
  • You explicitly close the underlying transport

Best Practices

1. Use ndJsonStream for stdio

For most use cases with stdin/stdout, ndJsonStream is the recommended approach:

2. Handle Transport Errors

Ensure your transport layer handles errors gracefully:

3. Clean Up Resources

Close streams and connections when done:

4. Buffer Management

Be mindful of buffering when implementing custom streams:

See Also