acp

package
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 5, 2026 License: MIT Imports: 18 Imported by: 0

Documentation

Overview

Package acp provides an Agent Client Protocol (ACP) engine for agentrun.

Unlike CLI backends that parse per-tool stdout formats, ACP uses JSON-RPC 2.0 over stdin/stdout with a persistent subprocess. The subprocess stays alive across turns — MCP servers boot once, subsequent turns are instant.

This implementation targets ACP spec v0.10.8 (protocol version 1).

ACP is a standardized protocol supported by OpenCode, Goose, OpenHands, and other agent runtimes. One engine handles all ACP-speaking agents, parameterized by binary name.

engine := acp.NewEngine(acp.WithBinary("opencode"), acp.WithArgs("acp"))
proc, err := engine.Start(ctx, session)

update.go maps ACP session/update notifications to agentrun.Message values.

ACP session/update notifications arrive as a two-level envelope:

outer: {"sessionId":"...", "update": <inner>}
inner: {"sessionUpdate":"agent_message_chunk", "content":{...}}

makeUpdateHandler (in process.go) unpacks the outer sessionNotification, then calls parseSessionUpdate(inner) which dispatches on the "sessionUpdate" discriminator field via the updateParsers map.

Adding a new update type = one map entry + one function.

Index

Constants

View Source
const (
	MethodInitialize       = "initialize"
	MethodSessionNew       = "session/new"
	MethodSessionLoad      = "session/load"
	MethodSessionPrompt    = "session/prompt"
	MethodSessionUpdate    = "session/update"
	MethodSessionCancel    = "session/cancel"
	MethodSessionSetMode   = "session/set_mode"
	MethodSessionSetConfig = "session/set_config_option"
	MethodRequestPerm      = "session/request_permission"
	MethodShutdown         = "shutdown"
)

JSON-RPC 2.0 method constants for the Agent Client Protocol.

View Source
const ErrCodeToolCallFailed = "tool_call_failed"

ErrCodeToolCallFailed is the ErrorCode for failed tool calls. Library-defined (not from ACP wire format) — the ACP protocol has no structured error code on tool_call_update failures.

Variables

This section is empty.

Functions

This section is empty.

Types

type Conn

type Conn struct {
	// contains filtered or unexported fields
}

Conn is a bidirectional JSON-RPC 2.0 multiplexer over newline-delimited JSON.

Conn serializes outbound messages (Call, Notify) via a mutex-protected encoder and dispatches inbound messages (responses, notifications, method calls) in ReadLoop. All handlers must be registered before ReadLoop starts.

The synchronization model uses sync.Mutex + map[int64]chan for pending calls. On ReadLoop exit, all pending channels receive an error — preventing goroutine leaks.

func (*Conn) Call

func (c *Conn) Call(ctx context.Context, method string, params, result any) error

Call sends a JSON-RPC request and blocks until the response arrives or ctx expires.

func (*Conn) Done

func (c *Conn) Done() <-chan struct{}

Done returns a channel that is closed when ReadLoop exits.

func (*Conn) Err

func (c *Conn) Err() error

Err returns the ReadLoop error after it exits. Returns nil if ReadLoop hasn't finished or exited cleanly (reader closed with no scanner error).

func (*Conn) Notify

func (c *Conn) Notify(method string, params any) error

Notify sends a JSON-RPC notification (no id, no response expected).

func (*Conn) OnMethod

func (c *Conn) OnMethod(method string, h func(json.RawMessage) (any, error))

OnMethod registers a handler for JSON-RPC method calls (has id field, expects response). The handler runs in a dedicated goroutine to avoid blocking ReadLoop. Must be called before ReadLoop starts.

func (*Conn) OnNotification

func (c *Conn) OnNotification(method string, h func(json.RawMessage))

OnNotification registers a handler for JSON-RPC notifications (no id field). Must be called before ReadLoop starts.

func (*Conn) ReadLoop

func (c *Conn) ReadLoop()

ReadLoop reads and dispatches inbound JSON-RPC messages until the reader closes or an unrecoverable error occurs. On exit, all pending Call channels are closed with an error. Must be called exactly once.

type Engine

type Engine struct {
	// contains filtered or unexported fields
}

Engine is an ACP engine that communicates with agents via JSON-RPC 2.0 over a persistent subprocess's stdin/stdout.

func NewEngine

func NewEngine(opts ...EngineOption) *Engine

NewEngine creates an ACP engine. Use EngineOption functions to customize the binary, arguments, buffer sizes, and permission handling.

func (*Engine) Start

func (e *Engine) Start(ctx context.Context, session agentrun.Session, opts ...agentrun.Option) (agentrun.Process, error)

Start spawns the ACP subprocess, performs the initialize + session handshake, and returns a Process ready for multi-turn conversation.

func (*Engine) Validate

func (e *Engine) Validate() error

Validate checks that the engine's binary is configured and available on PATH.

type EngineOption

type EngineOption func(*EngineOptions)

EngineOption configures an Engine at construction time.

func WithArgs

func WithArgs(args ...string) EngineOption

WithArgs sets additional arguments passed to the binary.

func WithBinary

func WithBinary(binary string) EngineOption

WithBinary sets the ACP agent executable name or path.

func WithGracePeriod

func WithGracePeriod(d time.Duration) EngineOption

WithGracePeriod sets the duration to wait after SIGTERM before sending SIGKILL. Values <= 0 are ignored.

func WithHandshakeTimeout

func WithHandshakeTimeout(d time.Duration) EngineOption

WithHandshakeTimeout sets the deadline for the initialize + session handshake. Values <= 0 are ignored.

func WithOutputBuffer

func WithOutputBuffer(size int) EngineOption

WithOutputBuffer sets the channel buffer size for process output messages. Values <= 0 are ignored.

func WithPermissionHandler

func WithPermissionHandler(h PermissionHandler) EngineOption

WithPermissionHandler sets the callback for agent permission requests.

func WithPermissionTimeout

func WithPermissionTimeout(d time.Duration) EngineOption

WithPermissionTimeout sets the deadline for the permission handler callback. Values <= 0 are ignored.

type EngineOptions

type EngineOptions struct {
	// Binary is the ACP agent executable name or path.
	Binary string

	// Args are additional arguments passed to the binary (e.g., ["acp"]).
	Args []string

	// OutputBuffer is the channel buffer size for process output messages.
	OutputBuffer int

	// GracePeriod is the duration to wait after SIGTERM before sending SIGKILL.
	GracePeriod time.Duration

	// HandshakeTimeout is the deadline for initialize + session/new during Start().
	HandshakeTimeout time.Duration

	// MaxMessageSize is the maximum JSON-RPC message size in bytes for the scanner.
	MaxMessageSize int

	// PermissionTimeout is the deadline for the PermissionHandler callback.
	PermissionTimeout time.Duration

	// PermissionHandler is called when the agent requests client-side permission.
	PermissionHandler PermissionHandler
}

EngineOptions holds resolved construction-time configuration for an ACP engine.

type PermissionHandler

type PermissionHandler func(ctx context.Context, req PermissionRequest) (approved bool, err error)

PermissionHandler is called when the agent requests client-side permission. Runs in a dedicated goroutine (not blocking ReadLoop). Return true to approve. The engine maps the boolean to the ACP option-based wire format internally: true → allow_once (prefer) or allow_always; false → reject_once or reject_always.

This collapses ACP's richer option-based outcomes to binary approve/deny. A future version may expose the full option set via PermissionRequest if consumers need to distinguish allow_once from allow_always. If nil, permission requests are auto-denied (unless HITL is off).

type PermissionRequest

type PermissionRequest struct {
	SessionID   string
	ToolName    string // from toolCallUpdate.Title
	ToolCallID  string // from toolCallUpdate.ToolCallID
	Description string // from toolCallUpdate.Kind
}

PermissionRequest carries the agent's permission request to the handler. The handler returns a simple approve/deny decision; the engine maps this to the ACP option-based wire format internally.

type RPCError

type RPCError struct {
	Code    int
	Message string
}

RPCError is an exported error type for JSON-RPC errors returned by Call.

func (*RPCError) Error

func (e *RPCError) Error() string

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL