Integrations
ACP Mode
Agent Communication Protocol (ACP) provides session-managed, bidirectional communication with Autohand. Built for editor integrations and persistent agent connections.
Overview
ACP mode implements the Agent Communication Protocol using the @agentclientprotocol/sdk (v0.12.0). It provides a higher-level interface compared to RPC mode, with built-in session management, authentication, and model switching.
ACP vs RPC
| Feature | ACP Mode | RPC Mode |
|---|---|---|
| Protocol | ndJSON (newline-delimited JSON) | JSON-RPC 2.0 |
| Session management | Built-in (create, resume, fork, list) | Single session per process |
| Authentication | Built-in handshake | Inherits from CLI config |
| Model switching | Runtime switching supported | Fixed at startup |
| Permission modes | Configurable per session | Global only |
| Hook notifications | Streamed as events | Not available |
| Best for | Editor integrations, long-running agents | Scripts, CI/CD, simple automation |
When to use ACP: Use ACP when you need persistent sessions, model switching, or building editor extensions (like Zed or VS Code). Use RPC mode for simpler automation scripts and CI/CD pipelines.
Getting Started
Start ACP Mode
Launch Autohand in ACP mode:
# Start ACP server
autohand --acp
# The server listens on stdin/stdout using ndJSON
Connection Flow
ACP requires a handshake before sending prompts:
Client Server
| |
|--- initialize --------->|
|<-- capabilities --------|
| |
|--- authenticate ------->|
|<-- authenticated -------|
| |
|--- newSession --------->|
|<-- sessionCreated ------|
| |
|--- prompt ------------->|
|<-- streaming events ----|
|<-- promptComplete ------|
Core Methods
initialize
Start the connection and discover server capabilities:
// Request
{
"method": "initialize",
"params": {
"clientInfo": {
"name": "my-editor",
"version": "1.0.0"
}
}
}
// Response
{
"method": "initialize",
"result": {
"serverInfo": {
"name": "autohand",
"version": "0.8.0"
},
"capabilities": {
"sessions": true,
"modelSwitching": true,
"hooks": true
}
}
}
authenticate
Authenticate the connection. Uses the CLI's existing auth configuration:
// Request
{
"method": "authenticate",
"params": {
"token": "ah_..."
}
}
// Response
{
"method": "authenticate",
"result": {
"authenticated": true,
"user": "[email protected]"
}
}
newSession
Create a new agent session:
// Request
{
"method": "newSession",
"params": {
"workingDirectory": "/path/to/project",
"model": "nvidia/nemotron-3-super-120b-a12b:free",
"mode": "unrestricted"
}
}
// Response
{
"method": "newSession",
"result": {
"sessionId": "sess_abc123",
"model": "nvidia/nemotron-3-super-120b-a12b:free",
"mode": "unrestricted"
}
}
prompt
Send a prompt to the active session. Responses stream as events:
// Request
{
"method": "prompt",
"params": {
"sessionId": "sess_abc123",
"message": "Add error handling to the API routes",
"mentions": ["src/routes/api.ts"]
}
}
// Streaming events follow...
{"event": "messageStart", "data": {"role": "assistant"}}
{"event": "messageDelta", "data": {"content": "I'll add error "}}
{"event": "messageDelta", "data": {"content": "handling to your API routes."}}
{"event": "toolStart", "data": {"tool": "edit_file", "path": "src/routes/api.ts"}}
{"event": "toolEnd", "data": {"tool": "edit_file", "success": true}}
{"event": "promptComplete", "data": {"success": true}}
cancel
Cancel the current prompt execution:
{
"method": "cancel",
"params": {
"sessionId": "sess_abc123"
}
}
Session Management
ACP provides full session lifecycle management, letting you resume, fork, and switch between sessions.
listSessions
List all available sessions:
// Request
{"method": "listSessions"}
// Response
{
"method": "listSessions",
"result": {
"sessions": [
{
"id": "sess_abc123",
"model": "nvidia/nemotron-3-super-120b-a12b:free",
"mode": "unrestricted",
"messageCount": 12,
"createdAt": "2026-03-04T10:00:00Z"
},
{
"id": "sess_def456",
"model": "nvidia/nemotron-3-super-120b-a12b:free",
"mode": "restricted",
"messageCount": 5,
"createdAt": "2026-03-04T09:30:00Z"
}
]
}
}
resumeSession
Resume an existing session with its full conversation history:
// Request
{
"method": "resumeSession",
"params": {
"sessionId": "sess_abc123"
}
}
// Response
{
"method": "resumeSession",
"result": {
"sessionId": "sess_abc123",
"restored": true,
"messageCount": 12
}
}
forkSession
Create a new session branching from an existing one. The new session starts with a copy of the conversation up to a specific point:
// Request
{
"method": "forkSession",
"params": {
"sourceSessionId": "sess_abc123",
"atMessage": 8
}
}
// Response
{
"method": "forkSession",
"result": {
"sessionId": "sess_ghi789",
"forkedFrom": "sess_abc123",
"messageCount": 8
}
}
loadSession
Load a session from a saved transcript file:
// Request
{
"method": "loadSession",
"params": {
"path": "/path/to/session.jsonl"
}
}
// Response
{
"method": "loadSession",
"result": {
"sessionId": "sess_loaded_001",
"messageCount": 25
}
}
Session Modes
ACP supports configurable permission modes per session, controlling what the agent can do without asking.
setSessionMode
Change the permission mode for the current session:
{
"method": "setSessionMode",
"params": {
"sessionId": "sess_abc123",
"mode": "restricted"
}
}
| Mode | Behavior |
|---|---|
unrestricted |
Agent can read, write, and execute commands freely. Similar to YOLO mode in the CLI. |
restricted |
Agent asks for approval before writes and command execution. Default behavior. |
dry-run |
Agent can only read files and suggest changes. No writes or commands are executed. |
full-access |
Unrestricted with additional permissions for network and system operations. |
Model Switching
ACP supports changing the model mid-session without restarting.
unstable_setSessionModel
Switch the model for the current session:
// Request
{
"method": "unstable_setSessionModel",
"params": {
"sessionId": "sess_abc123",
"model": "anthropic/claude-opus-4"
}
}
// Response
{
"method": "unstable_setSessionModel",
"result": {
"previousModel": "nvidia/nemotron-3-super-120b-a12b:free",
"currentModel": "anthropic/claude-opus-4"
}
}
Unstable API: The unstable_ prefix means this method's interface may change in future releases. Pin your client to a specific CLI version if you depend on this.
Hook Notifications
ACP streams hook events to the client, letting editor integrations respond to lifecycle events like file saves, command execution, and permission checks.
// Hook fired before a tool executes
{
"event": "hookNotification",
"data": {
"hook": "PreToolExecution",
"tool": "bash",
"command": "npm test",
"sessionId": "sess_abc123"
}
}
// Hook fired after file changes
{
"event": "hookNotification",
"data": {
"hook": "PostToolExecution",
"tool": "edit_file",
"path": "src/auth.ts",
"sessionId": "sess_abc123"
}
}
Hook notifications are read-only events. To configure which hooks fire, set them up in the CLI configuration file (.autohand/hooks.json).
Integration Example
Zed Editor
The Zed editor uses ACP to embed Autohand as an inline agent. Here is how a typical integration works:
import { spawn } from "child_process";
import { createInterface } from "readline";
// Start ACP server
const agent = spawn("autohand", ["--acp"]);
const rl = createInterface({ input: agent.stdout });
function send(msg: object) {
agent.stdin.write(JSON.stringify(msg) + "\n");
}
// Handle streaming responses
rl.on("line", (line) => {
const msg = JSON.parse(line);
switch (msg.event) {
case "messageDelta":
// Render text in editor panel
editor.appendText(msg.data.content);
break;
case "toolStart":
// Show tool indicator
statusBar.show(`Running: ${msg.data.tool}`);
break;
case "toolEnd":
statusBar.clear();
break;
case "hookNotification":
// React to hooks (refresh file tree, etc.)
if (msg.data.tool === "edit_file") {
fileTree.refresh(msg.data.path);
}
break;
case "promptComplete":
statusBar.show("Done");
break;
}
});
// Initialize connection
send({
method: "initialize",
params: { clientInfo: { name: "zed", version: "0.170.0" } }
});
// After init response, authenticate and create session
send({
method: "authenticate",
params: { token: process.env.AUTOHAND_TOKEN }
});
send({
method: "newSession",
params: {
workingDirectory: projectPath,
model: "nvidia/nemotron-3-super-120b-a12b:free",
mode: "restricted"
}
});
VS Code Extension
For VS Code, the pattern is similar but uses the extension API for UI elements:
import * as vscode from "vscode";
import { spawn } from "child_process";
import { createInterface } from "readline";
export function activate(context: vscode.ExtensionContext) {
const agent = spawn("autohand", ["--acp"]);
const rl = createInterface({ input: agent.stdout });
function send(msg: object) {
agent.stdin.write(JSON.stringify(msg) + "\n");
}
// Initialize ACP connection
send({
method: "initialize",
params: { clientInfo: { name: "vscode-autohand", version: "1.0.0" } }
});
// Register command
const cmd = vscode.commands.registerCommand(
"autohand.prompt",
async () => {
const input = await vscode.window.showInputBox({
prompt: "Ask Autohand..."
});
if (input) {
send({
method: "prompt",
params: {
sessionId: currentSessionId,
message: input,
mentions: [
vscode.window.activeTextEditor?.document.uri.fsPath
].filter(Boolean)
}
});
}
}
);
context.subscriptions.push(cmd);
// Handle events
rl.on("line", (line) => {
const msg = JSON.parse(line);
if (msg.event === "messageDelta") {
outputChannel.append(msg.data.content);
}
});
context.subscriptions.push({
dispose: () => agent.kill()
});
}
Error Handling
ACP returns structured errors with codes and messages:
{
"method": "prompt",
"error": {
"code": "SESSION_NOT_FOUND",
"message": "Session sess_invalid does not exist"
}
}
{
"method": "authenticate",
"error": {
"code": "AUTH_FAILED",
"message": "Invalid or expired token"
}
}
| Error Code | Description |
|---|---|
AUTH_FAILED |
Authentication failed or token expired |
SESSION_NOT_FOUND |
Referenced session does not exist |
SESSION_BUSY |
Session is already processing a prompt |
INVALID_MODEL |
Requested model is not available |
INVALID_MODE |
Requested session mode is not valid |
NOT_INITIALIZED |
Called a method before initialization |