dart_acp

A Dart implementation of the Agent Client Protocol (ACP), providing both a library for building ACP clients and a full-featured command-line tool for interacting with ACP agents.

Overview

dart_acp enables Dart and Flutter applications to communicate with AI agents that implement the Agent Client Protocol. The package includes:

  • dart_acp library: A complete ACP client implementation for Dart applications
  • acpcli tool: A command-line interface for testing and interacting with ACP agents

Key Features

Protocol & Streaming

  • Full ACP Protocol Support: Compatible with the latest specification from agentclientprotocol.com
  • Typed Update Streaming: Sealed AcpUpdate types with exhaustive pattern matching
  • Turn-Scoped Streams: Prompt streams auto-close after TurnEnded for clean resource management
  • Session Replay: Replay buffers persist updates for session/load history playback
  • Tool Call Merging: Automatic merge semantics for tool_call_update (only non-null fields update)

Security

  • Workspace Jail: Path traversal prevention with symlink resolution and boundary enforcement
  • Terminal CWD Jailing: Terminal processes confined to workspace directory
  • Permission Policies: Pluggable permission provider with read/write/execute classification
  • Secure Path Handling: Home directory expansion, path normalization, and canonicalization

Session Management

  • Session State Tracking: Per-session workspace roots, mode state, and tool call history
  • Session Extensions: Support for session/list, session/resume, session/fork, session/set_config_option
  • Mode Switching: Query available modes and switch dynamically during sessions

Architecture

  • Provider Pattern: Pluggable FsProvider, PermissionProvider, and TerminalProvider with sensible defaults
  • Transport Abstraction: StdioTransport (spawn processes) and StdinTransport (piped I/O)
  • JSON-RPC 2.0: Built on json_rpc_2 package with bidirectional request/notification support

Tooling

  • CLI Tool (acpcli): Full-featured command-line client with multiple output modes
  • E2E Testing: Integration tests with real agents (Gemini, Claude, Codex)

Documentation


dart_acp Library

The dart_acp library provides a high-level API for building ACP client applications.

Quick Start

import 'dart:io';
import 'package:dart_acp/dart_acp.dart';

void main() async {
  // Create and start the client
  final client = await AcpClient.start(
    config: AcpConfig(
      agentCommand: 'npx',
      agentArgs: ['@zed-industries/claude-code-acp'],
    ),
  );

  // Initialize and create a session
  await client.initialize();
  final workspaceRoot = Directory.current.path;
  final sessionId = await client.newSession(workspaceRoot);

  // Send a prompt with @-mention support
  final stream = client.prompt(
    sessionId: sessionId,
    content: 'examine @main.dart and explain what it does.',
  );

  // Stream the response
  await for (final update in stream) {
    print(update.text);
  }

  // Clean up
  await client.dispose();
  exit(0);
}

See the full example: example/main.dart

Core Components

AcpClient

The main entry point for interacting with ACP agents:

final client = await AcpClient.start(
  config: AcpConfig(
    agentCommand: 'your-agent-binary',
    agentArgs: ['--flag'],
    envOverrides: {'API_KEY': 'value'},
    // Optional providers
    fsProvider: myFsProvider,
    permissionProvider: myPermissionProvider,
    terminalProvider: DefaultTerminalProvider(),
  ),
);

Session Management

// Create a new session
final sessionId = await client.newSession(workspaceRoot);

// Resume an existing session (if agent supports it)
await client.loadSession(
  sessionId: existingId,
  workspaceRoot: workspaceRoot,
);

// Subscribe to session updates
client.sessionUpdates(sessionId).listen((update) {
  if (update is PlanUpdate) {
    print('Plan: ${update.plan.title}');
  } else if (update is ToolCallUpdate) {
    print('Tool: ${update.toolCall.title}');
  }
});

@-Mention Support

The library automatically parses @-mentions in prompts:

// Local files: @file.txt, @"path with spaces/file.txt"
// URLs: @https://example.com/resource
// Home paths: @~/Documents/file.txt
final updates = client.prompt(
  sessionId: sessionId,
  content: 'Review @lib/src/main.dart and @README.md',
);

Update Types

The library provides strongly-typed update events:

  • MessageDelta: Assistant response text chunks
  • PlanUpdate: Execution plans with priorities
  • ToolCallUpdate: Tool invocations with status
  • DiffUpdate: File modification diffs
  • AvailableCommandsUpdate: Slash commands
  • ModeUpdate: Session mode changes
  • TurnEnded: End of response with stop reason

Advanced Features

Providers

Customize behavior with pluggable providers:

// File system provider with workspace jail
class MyFsProvider implements FsProvider {
  Future<String> readTextFile(String path, {int? line, int? limit}) async {
    // Custom implementation
  }

  Future<void> writeTextFile(String path, String content) async {
    // Custom implementation
  }
}

// Permission provider for security
class MyPermissionProvider implements PermissionProvider {
  Future<PermissionOutcome> requestPermission(
    PermissionOptions options,
  ) async {
    // Return allow, deny, or cancelled
  }
}

Session Modes (Extension)

// Get available modes
final modes = client.sessionModes(sessionId);
print('Current: ${modes?.currentModeId}');
print('Available: ${modes?.availableModes}');

// Switch mode
await client.setMode(sessionId: sessionId, modeId: 'edit');

acpcli Tool

A comprehensive command-line interface for testing and interacting with ACP agents.

Installation & Setup

  1. Configure agents in example/acpcli/settings.json:
{
  "agent_servers": {
    "claude": {
      "command": "npx",
      "args": ["@zed-industries/claude-code-acp"],
      "env": {
        "ACP_PERMISSION_MODE": "acceptEdits",
        "ACP_DEBUG": "true"
      }
    },
    "gemini": {
      "command": "gemini",
      "args": ["--experimental-acp"],
      "env": {
        "GEMINI_MODEL": "gemini-2.5-pro"
      }
    },
    "codex": {
      "command": "npx",
      "args": ["@zed-industries/codex-acp"]
    }
  }
}
  1. Run the CLI:
dart example/acpcli/acpcli.dart "Your prompt here"

See the full implementation: example/acpcli/acpcli.dart

Usage

dart example/acpcli/acpcli.dart [options] [--] [prompt]

Options:
  -h, --help              Show help and exit
  -a, --agent <name>      Select agent from settings.json
  -o, --outputmode <mode> Output mode: text, simple, jsonl, json (default: text)
  --settings <path>       Use specific settings.json file
  --write                 Enable write operations (confined to CWD)
  --yolo                  Enable read-anywhere; writes remain confined to CWD
  --list-caps             Show agent capabilities
  --list-modes            Show available session modes
  --list-commands         Show slash commands
  --list-sessions         List existing sessions (if agent supports)
  --mode <id>             Set session mode
  --resume <id>           Resume existing session
  --save-session <path>   Save session ID to file

Prompt:
  Provide as positional argument or pipe via stdin
  Use @-mentions for files: @path, @"file.txt", @https://example.com/doc

Examples:
  dart example/acpcli/acpcli.dart -a gemini "Summarize README.md"
  echo "List commands" | dart example/acpcli/acpcli.dart -o jsonl
  dart example/acpcli/acpcli.dart "Review @lib/src/main.dart"

Key Features

Agent Selection

Select from configured agents or use the default:

# Use default (first in settings.json)
dart example/acpcli/acpcli.dart "Hello"

# Select specific agent
dart example/acpcli/acpcli.dart -a claude "Hello"
dart example/acpcli/acpcli.dart -a gemini "Hello"
dart example/acpcli/acpcli.dart -a codex "Hello"

Output Modes

Mode Flag Description Use Case
text -o text (default) Human-readable with metadata Interactive use
simple -o simple Assistant text only Clean output
jsonl -o jsonl Raw protocol frames Debugging/automation
json -o json Alias for jsonl Protocol analysis

@-Mention Support

Reference files and URLs directly in prompts:

# Local files
dart example/acpcli/acpcli.dart "Review @src/main.dart"
dart example/acpcli/acpcli.dart "Analyze @\"my file.txt\""

# URLs
dart example/acpcli/acpcli.dart "Summarize @https://example.com/api-docs"

# Home directory
dart example/acpcli/acpcli.dart "Read @~/Documents/notes.txt"

Permission Control

Non-interactive permission handling via flags:

# Enable writes (confined to CWD)
dart example/acpcli/acpcli.dart --write "Create a new file"

# Read anywhere; writes remain confined to CWD
dart example/acpcli/acpcli.dart --yolo "Search system files"

Discovery Commands

Explore agent capabilities without sending prompts:

# List capabilities
dart example/acpcli/acpcli.dart -a claude --list-caps

# List session modes
dart example/acpcli/acpcli.dart -a claude --list-modes

# List slash commands
dart example/acpcli/acpcli.dart -a claude --list-commands

# List existing sessions (if agent supports)
dart example/acpcli/acpcli.dart -a claude --list-sessions

# Combine multiple lists
dart example/acpcli/acpcli.dart --list-caps --list-modes --list-commands

Session Management

Save and resume sessions (if agent supports loadSession):

# Save session
dart example/acpcli/acpcli.dart --save-session /tmp/session.txt "Initial prompt"

# Resume later
dart example/acpcli/acpcli.dart --resume $(cat /tmp/session.txt) "Continue"

Protocol Debugging

Use JSONL mode to inspect raw ACP protocol:

# Mirror all JSON-RPC frames
dart example/acpcli/acpcli.dart -o jsonl "Test" | jq '.'

# Filter specific message types
dart example/acpcli/acpcli.dart -o jsonl "Test" | grep tool_call

Tool Monitoring

In text mode, the CLI displays rich information about tool usage:

[tool] read Read file @ src/main.py
[tool.in] {"path":"src/main.py","line":1,"limit":200}
[tool.out] "import sys..."

Plans and Progress

Track agent execution plans:

dart example/acpcli/acpcli.dart "Create a plan to refactor the auth module"
# [plan] {"title": "Refactoring", "steps": [...], "status": "in_progress"}

Terminal Support

When agents execute commands:

dart example/acpcli/acpcli.dart "Run: npm test"
# [term] created id=term_001 cmd=npm
# [term] output id=term_001
# [term] exited id=term_001 code=0

acpcomply Compliance App (Experimental)

Note: This tool is experimental and under active development. Test coverage and reliability may vary.

The compliance runner executes a suite of ACP agent compliance tests and prints a Markdown report to stdout.

  • Entrypoint: example/acpcomply/acpcomply.dart
  • Tests: example/acpcomply/compliance-tests/*.jsont
  • Requirements/spec: specs/acpcomply-requirements.md

What It Verifies

  • Initialization and session setup
  • Prompt turns and cancellation semantics
  • Capability respect (FS/terminal disabled)
  • Error handling (unknown methods, invalid params)
  • File system and terminal flows
  • Plans, slash commands, and streaming chunks
  • Session modes and session load replay
  • Tool calls, diffs, locations, and permission flows
  • MCP stdio session setup (optional)

How It Works

  • Creates a per-test sandbox workspace and writes declared files
  • Reuses AcpClient for transport and Agent→Client handling
  • Matches server responses/notifications via regex subset matching
  • Observes Agent→Client requests (fs/*, terminal/*, session/request_permission)
  • Prints an agent-first Markdown report (no summary table): header per agent, then one H2 per test with actionable status and diffs

Run

dart run example/acpcomply/acpcomply.dart

Notes:

  • Some tests are optional and will be marked NA when agents lack the corresponding capability
  • The MCP stdio test forwards a local MCP server definition at tool/mcp_server/bin/server.dart to the agent

Installing Agents

Gemini CLI

# Install via npm (requires Node.js)
npm install -g @google/gemini-cli@0.17.1

# Authenticate
gemini auth login

# Configure in settings.json:
# "gemini": {
#   "command": "gemini",
#   "args": ["--experimental-acp"],
#   "env": { "GEMINI_MODEL": "gemini-2.5-pro" }
# }

Claude Code Adapter

# Run via npx (recommended) - requires Node.js v22 (LTS)
npx @zed-industries/claude-code-acp

# Note: Ensure you are using a stable Node.js version (e.g., v22).
# Experimental versions (like v25) may cause crashes.

Codex Adapter

# Run via npx (recommended)
npx @zed-industries/codex-acp

Testing

Unit Tests

Run fast unit tests without agents:

dart test --exclude-tags e2e

E2E Tests

Run integration tests with real agents:

# All tests including e2e
dart test

# Only e2e tests
dart test --tags e2e

# Specific agent tests
dart test --tags e2e -n "gemini"
dart test --tags e2e -n "claude"
dart test --tags e2e -n "codex"

Tests use test/test_settings.json for agent configuration.


Troubleshooting

Gemini "Invalid argument" Errors

Some Gemini models have ACP implementation bugs. Solution:

  • Don't set GEMINI_MODEL environment variable
  • Use the default model or test specific models first

Permission Denied

Agents need explicit permissions for file operations:

  • Use --write for write operations (confined to the workspace/CWD)
  • Use --yolo for read-anywhere; writes still remain confined to the workspace

Authentication Required

Authenticate with the agent's native tools first:

gemini auth login
# or follow Claude Code OAuth flow

Settings Not Found

Ensure example/acpcli/settings.json exists or use --settings to specify a path.


License

See LICENSE file for details.

Libraries

dart_acp