Documentation
¶
Overview ¶
Package acp implements the Agent Communication Protocol — a WebSocket-based protocol that lets any client interact with an OK agent.
Handles WebSocket upgrade per RFC 6455: SHA1 key challenge → 101 response → bidirectional JSON frames. Falls back to SSE for read-only streaming.
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 OK. 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 ClientMsg
- 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 Factory
- type Implementation
- type InitializeParams
- type InitializeResult
- type MCPCapabilities
- 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 Server
- type ServerMsg
- type SessionCancelParams
- type SessionLoadParams
- type SessionLoadResult
- type SessionNewParams
- type SessionNewResult
- type SessionParams
- type SessionPromptParams
- type SessionPromptResult
- type SessionUpdateParams
- type StopReason
Constants ¶
const ( MsgSubmit = "submit" MsgCancel = "cancel" MsgApprove = "approve" MsgEvent = "event" )
Message types
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 canceled. It owns the JSON-RPC connection and the session registry; the Factory supplies the kernel wiring. This is the single entry point the `ok 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"`
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 canceled, or the connection closes. Satisfies notifier.
func (*Conn) Serve ¶
Serve reads inbound frames until the reader ends or ctx is canceled. 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 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 `ok 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. The capability flags match main exactly: sessions are created via session/new (no loadSession), prompts may carry inline resource text (embeddedContext) but not image/audio, and MCP is stdio-only (no http/sse).
type MCPCapabilities ¶
MCPCapabilities reports which MCP transports session/new accepts.
type MCPServerSpec ¶
type MCPServerSpec struct {
Name string `json:"name"`
Command string `json:"command,omitempty"`
Args []string `json:"args,omitempty"`
Env map[string]string `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" 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 "canceled".
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 Server ¶
type Server struct {
// contains filtered or unexported fields
}
func NewServer ¶
func NewServer(ctrl *control.Controller) *Server
type SessionCancelParams ¶
type SessionCancelParams struct {
SessionID string `json:"sessionId"`
}
SessionCancelParams cancels an in-progress turn.
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 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" )