Documentation
¶
Overview ¶
Package acp implements the Agent Client Protocol (https://agentclientprotocol.com) transport: a stdio JSON-RPC 2.0 agent that editors and other host clients speak to drive fairpeer. Many tools integrated with the v1 (main-branch) agent over ACP, so v2 keeps the wire contract identical — the wire types in this file are a faithful port of main's src/acp/protocol.ts (ACP protocol version 1).
The package is an adapter layer over the v2 kernel and depends only on stable contracts: it maps the agent's typed event.Event stream onto session/update notifications (see dispatch.go), bridges permission.Approver onto session/request_permission round-trips (see permission.go), and exposes the whole thing over NDJSON JSON-RPC (see server.go). How a per-session agent is actually assembled — provider, tools rooted at the session cwd, per-session MCP — is left to a Factory the composition root supplies (see service.go), so this package stays independent of the cli wiring.
Index ¶
- Constants
- func FlattenPrompt(blocks []ContentBlock) string
- func Serve(ctx context.Context, r io.Reader, w io.Writer, factory Factory, info AgentInfo) error
- type AgentCapabilities
- type AgentInfo
- type Conn
- func (c *Conn) Handle(method string, h RequestHandler)
- func (c *Conn) HandleNotify(method string, h NotificationHandler)
- func (c *Conn) Notify(method string, params any) error
- func (c *Conn) Request(ctx context.Context, method string, params any) (json.RawMessage, error)
- func (c *Conn) Serve(ctx context.Context) error
- type ContentBlock
- type EmptyCapability
- type EnvVariable
- type Factory
- type Implementation
- type InitializeParams
- type InitializeResult
- type MCPCapabilities
- type MCPEnv
- type MCPServerSpec
- type NotificationHandler
- type PermissionOption
- type PermissionOptionKind
- type PermissionOutcome
- type PermissionRequestParams
- type PermissionRequestResult
- type PermissionToolCall
- type PromptCapabilities
- type RPCError
- type RequestHandler
- type ResourceContents
- type SessionCancelParams
- type SessionCapabilities
- type SessionCloseParams
- type SessionCloseResult
- type SessionDeleteParams
- type SessionDeleteResult
- type SessionDirProvider
- type SessionInfo
- type SessionListParams
- type SessionListResult
- type SessionLoadParams
- type SessionLoadResult
- type SessionNewParams
- type SessionNewResult
- type SessionParams
- type SessionPromptParams
- type SessionPromptResult
- type SessionResumeParams
- type SessionResumeResult
- type SessionUpdateParams
- type StopReason
Constants ¶
const ( ErrParse = -32700 ErrInvalidRequest = -32600 ErrMethodNotFound = -32601 ErrInvalidParams = -32602 ErrInternal = -32603 )
JSON-RPC 2.0 error codes (subset used on the wire). Mirrors protocol.ts.
const ProtocolVersion = 1
ProtocolVersion is the ACP version this agent implements. Matches main.
Variables ¶
This section is empty.
Functions ¶
func FlattenPrompt ¶
func FlattenPrompt(blocks []ContentBlock) string
FlattenPrompt extracts the user-visible prompt text out of ACP content blocks. Text blocks contribute their text; resource blocks contribute their inline text when present (embeddedContext). Other block kinds are dropped. Ported from protocol.ts flattenPrompt.
func Serve ¶
Serve runs an ACP agent on r/w (stdin/stdout in production) until the input ends or ctx is cancelled. It owns the JSON-RPC connection and the session registry; the Factory supplies the kernel wiring. This is the single entry point the `fairpeer acp` command calls.
stdout is the JSON-RPC channel: callers must keep all other output (logs, diagnostics) off w and on stderr, or the wire corrupts.
Types ¶
type AgentCapabilities ¶
type AgentCapabilities struct {
LoadSession bool `json:"loadSession"`
SessionCapabilities SessionCapabilities `json:"sessionCapabilities,omitempty"`
PromptCapabilities PromptCapabilities `json:"promptCapabilities"`
MCPCapabilities MCPCapabilities `json:"mcpCapabilities"`
}
AgentCapabilities is the agentCapabilities object in InitializeResult.
type Conn ¶
type Conn struct {
// contains filtered or unexported fields
}
Conn is one NDJSON JSON-RPC 2.0 connection over a reader/writer pair (stdin/ stdout in production). It dispatches inbound requests and notifications to registered handlers, and can itself send outbound notifications (session/update) and requests (session/request_permission), correlating replies by id.
Writes are serialized by a mutex, so handlers running on separate goroutines (a long session/prompt alongside a session/cancel) never interleave a line. It implements notifier, the dependency the dispatch sink takes.
func NewConn ¶
NewConn wires a connection over r (inbound) and w (outbound). Register handlers with Handle / HandleNotify before calling Serve. The encoder disables HTML escaping so payloads match main's JSON.stringify output byte-for-byte.
func (*Conn) Handle ¶
func (c *Conn) Handle(method string, h RequestHandler)
Handle registers a request handler for method. Not safe to call concurrently with Serve; wire all handlers up first.
func (*Conn) HandleNotify ¶
func (c *Conn) HandleNotify(method string, h NotificationHandler)
HandleNotify registers a notification handler for method.
func (*Conn) Request ¶
Request sends an outbound request and blocks until the peer responds, ctx is cancelled, or the connection closes. Satisfies notifier.
func (*Conn) Serve ¶
Serve reads inbound frames until the reader ends or ctx is cancelled. Each inbound request/notification runs on its own goroutine so a long-running prompt does not block cancellation or permission replies. When the read loop ends it cancels in-flight handlers (so prompts abort) and waits for them to return — flushing fast responses and unwinding aborted ones — before failing any outstanding outbound requests. Returns nil on clean EOF.
type ContentBlock ¶
type ContentBlock struct {
Type string `json:"type"`
Text string `json:"text,omitempty"`
Resource *ResourceContents `json:"resource,omitempty"`
MimeType string `json:"mimeType,omitempty"`
Data string `json:"data,omitempty"`
}
ContentBlock is one piece of a prompt. The agent reads text blocks and the inline text of resource blocks (embeddedContext); image/audio are accepted on the wire but ignored, matching the advertised capabilities.
type EmptyCapability ¶
type EmptyCapability struct{}
EmptyCapability serializes to {} for ACP capability flags.
type EnvVariable ¶
EnvVariable is one official ACP MCP environment variable entry.
type Factory ¶
type Factory interface {
NewSession(ctx context.Context, p SessionParams) (*control.Controller, error)
}
Factory builds the per-session controller. The composition root (the cli's `fairpeer acp` command) implements it by reusing setup()'s assembly: a Provider for Model, a tool Registry rooted at Cwd via builtin.Workspace, a per-session MCP host from MCPServers, the event Sink, all wired into a control.Controller. The returned controller owns its own cleanup (Close stops MCP subprocesses), so the service calls ctrl.Close() on teardown.
type Implementation ¶
type Implementation struct {
Name string `json:"name"`
Title string `json:"title,omitempty"`
Version string `json:"version,omitempty"`
}
Implementation names a participant (client or agent) on the wire.
type InitializeParams ¶
type InitializeParams struct {
ProtocolVersion int `json:"protocolVersion"`
ClientInfo *Implementation `json:"clientInfo,omitempty"`
}
InitializeParams is the client's handshake. We accept and ignore its capabilities/info — the agent advertises a fixed capability set in reply.
type InitializeResult ¶
type InitializeResult struct {
ProtocolVersion int `json:"protocolVersion"`
AgentCapabilities AgentCapabilities `json:"agentCapabilities"`
AgentInfo Implementation `json:"agentInfo"`
AuthMethods []any `json:"authMethods"`
}
InitializeResult advertises what this agent supports: persisted session load, ACP v1 session lifecycle helpers, inline resource text (embeddedContext) but not image/audio, and stdio-only MCP (no http/sse).
type MCPCapabilities ¶
MCPCapabilities reports which MCP transports session/new accepts.
type MCPEnv ¶
MCPEnv accepts ACP's official EnvVariable[] shape while still accepting the older map shape that fairpeer v1 clients used.
func (*MCPEnv) UnmarshalJSON ¶
type MCPServerSpec ¶
type MCPServerSpec struct {
Name string `json:"name"`
Type string `json:"type,omitempty"`
Command string `json:"command,omitempty"`
Args []string `json:"args,omitempty"`
Env MCPEnv `json:"env,omitempty"`
}
MCPServerSpec describes one stdio MCP server the client asks the agent to run.
type NotificationHandler ¶
type NotificationHandler func(ctx context.Context, params json.RawMessage)
NotificationHandler reacts to an inbound notification. It cannot reply, so it returns nothing — errors have nowhere to go on the wire (stderr would corrupt stdout, which is the JSON-RPC channel).
type PermissionOption ¶
type PermissionOption struct {
OptionID string `json:"optionId"`
Name string `json:"name"`
Kind PermissionOptionKind `json:"kind"`
}
PermissionOption is one choice offered to the user for a permission request.
type PermissionOptionKind ¶
type PermissionOptionKind string
PermissionOptionKind classifies an option for host UI styling. Matches main.
const ( OptAllowOnce PermissionOptionKind = "allow_once" OptAllowAlways PermissionOptionKind = "allow_always" OptAllowPersistent PermissionOptionKind = "allow_persistent" OptRejectOnce PermissionOptionKind = "reject_once" OptRejectAlways PermissionOptionKind = "reject_always" )
type PermissionOutcome ¶
type PermissionOutcome struct {
Outcome string `json:"outcome"`
OptionID string `json:"optionId,omitempty"`
}
PermissionOutcome is "selected" (with optionId) or "cancelled".
type PermissionRequestParams ¶
type PermissionRequestParams struct {
SessionID string `json:"sessionId"`
ToolCall PermissionToolCall `json:"toolCall"`
Options []PermissionOption `json:"options"`
}
PermissionRequestParams asks the client to approve a pending tool call.
type PermissionRequestResult ¶
type PermissionRequestResult struct {
Outcome PermissionOutcome `json:"outcome"`
}
PermissionRequestResult is the client's reply to a permission request.
type PermissionToolCall ¶
type PermissionToolCall struct {
ToolCallID string `json:"toolCallId"`
Title string `json:"title,omitempty"`
Kind string `json:"kind,omitempty"`
Status string `json:"status,omitempty"`
RawInput json.RawMessage `json:"rawInput,omitempty"`
}
PermissionToolCall describes the call awaiting approval.
type PromptCapabilities ¶
type PromptCapabilities struct {
Image bool `json:"image"`
Audio bool `json:"audio"`
EmbeddedContext bool `json:"embeddedContext"`
}
PromptCapabilities reports which content-block kinds prompts may carry.
type RequestHandler ¶
RequestHandler answers an inbound JSON-RPC request. The returned value is marshaled as the response result. To control the error code, return a *RPCError; any other error becomes ErrInternal.
type ResourceContents ¶
type ResourceContents struct {
URI string `json:"uri"`
MimeType string `json:"mimeType,omitempty"`
Text string `json:"text,omitempty"`
}
ResourceContents is the embedded resource of a "resource" content block.
type SessionCancelParams ¶
type SessionCancelParams struct {
SessionID string `json:"sessionId"`
}
SessionCancelParams cancels an in-progress turn.
type SessionCapabilities ¶
type SessionCapabilities struct {
List *EmptyCapability `json:"list,omitempty"`
Resume *EmptyCapability `json:"resume,omitempty"`
Close *EmptyCapability `json:"close,omitempty"`
Delete *EmptyCapability `json:"delete,omitempty"`
}
SessionCapabilities advertises optional session lifecycle methods.
type SessionCloseParams ¶
type SessionCloseParams struct {
SessionID string `json:"sessionId"`
}
SessionCloseParams closes an active session and releases its resources.
type SessionCloseResult ¶
type SessionCloseResult struct{}
SessionCloseResult is the empty close ack.
type SessionDeleteParams ¶
type SessionDeleteParams struct {
SessionID string `json:"sessionId"`
}
SessionDeleteParams removes a session from future session/list results.
type SessionDeleteResult ¶
type SessionDeleteResult struct{}
SessionDeleteResult is the empty delete ack.
type SessionDirProvider ¶
type SessionDirProvider interface {
SessionDir() string
}
SessionDirProvider lets a Factory expose the persistent session directory without forcing session/list to build a controller first.
type SessionInfo ¶
type SessionInfo struct {
SessionID string `json:"sessionId"`
Cwd string `json:"cwd"`
Title string `json:"title,omitempty"`
UpdatedAt string `json:"updatedAt,omitempty"`
Meta map[string]any `json:"_meta,omitempty"`
}
SessionInfo is the ACP session/list item shape.
type SessionListParams ¶
type SessionListParams struct {
Cwd string `json:"cwd,omitempty"`
Cursor string `json:"cursor,omitempty"`
}
SessionListParams lists known sessions, optionally filtered by cwd.
type SessionListResult ¶
type SessionListResult struct {
Sessions []SessionInfo `json:"sessions"`
NextCursor string `json:"nextCursor,omitempty"`
}
SessionListResult is the first and only page of sessions fairpeer currently returns. NextCursor is omitted because the in-process list is unpaged.
type SessionLoadParams ¶
type SessionLoadParams struct {
SessionID string `json:"sessionId"`
Cwd string `json:"cwd,omitempty"`
MCPServers []MCPServerSpec `json:"mcpServers,omitempty"`
}
SessionLoadParams resumes a session saved under sessionId (the id a prior session/new returned), optionally re-rooting it at cwd with fresh MCP servers. The agent replays the stored conversation as session/update notifications before the request returns.
type SessionLoadResult ¶
type SessionLoadResult struct{}
SessionLoadResult is the empty ack; the conversation has already arrived as a burst of session/update notifications by the time it is sent.
type SessionNewParams ¶
type SessionNewParams struct {
Cwd string `json:"cwd,omitempty"`
MCPServers []MCPServerSpec `json:"mcpServers,omitempty"`
}
SessionNewParams opens a session rooted at cwd, optionally with stdio MCP servers the agent should connect for the session's lifetime.
type SessionNewResult ¶
type SessionNewResult struct {
SessionID string `json:"sessionId"`
}
SessionNewResult returns the opaque id used to address the session thereafter.
type SessionParams ¶
SessionParams is everything a Factory needs to assemble one ACP session's controller. Sink is owned by this package (an updateSink bound to the session id) and must be wired into the controller's event sink; the controller's interactive approval (see control.Controller.EnableInteractiveApproval) then routes "ask" decisions back through that sink as ApprovalRequest events, which the sink forwards to the client over session/request_permission.
The Factory picks the model (ACP's session/new carries no model selection). Cwd roots the session's file tools and bash (built via builtin.Workspace). MCPServers are the stdio MCP servers the client asked the agent to connect for this session.
type SessionPromptParams ¶
type SessionPromptParams struct {
SessionID string `json:"sessionId"`
Prompt []ContentBlock `json:"prompt"`
}
SessionPromptParams sends a turn's prompt to a session.
type SessionPromptResult ¶
type SessionPromptResult struct {
StopReason StopReason `json:"stopReason"`
TranscriptPath *string `json:"transcriptPath,omitempty"`
}
SessionPromptResult ends a session/prompt. TranscriptPath is reserved for a future on-disk transcript pointer; omitted (null) for now.
type SessionResumeParams ¶
type SessionResumeParams struct {
SessionID string `json:"sessionId"`
Cwd string `json:"cwd,omitempty"`
MCPServers []MCPServerSpec `json:"mcpServers,omitempty"`
}
SessionResumeParams resumes a session without replaying its transcript.
type SessionResumeResult ¶
type SessionResumeResult struct{}
SessionResumeResult is the empty ack returned once the session is ready.
type SessionUpdateParams ¶
SessionUpdateParams wraps one update for a session.
type StopReason ¶
type StopReason string
StopReason tells the client why a turn ended. Values match main's wire.
const ( StopEndTurn StopReason = "end_turn" StopCancelled StopReason = "cancelled" StopError StopReason = "error" )