acp

package
v0.11.0-beta.2 Latest Latest
Warning

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

Go to latest
Published: Oct 23, 2026 License: Apache-2.0, MIT Imports: 29 Imported by: 0

Documentation

Overview

Package acp implements the Agent Communication Protocol — a small HTTP/SSE API that lets other agents (claude code, opencode, custom agents) send messages to a running overkill and receive streaming replies. Inverse of MCP.

Index

Constants

View Source
const (
	TaskMaxContentBytes  = 64 * 1024
	TaskMaxReplyBytes    = 256 * 1024
	TaskMaxErrorBytes    = 4096
	TaskMaxProgressBytes = 4096
	TaskMaxActive        = 256
	TaskConcurrency      = 4
	TaskRunTimeout       = 15 * time.Minute
	TaskMaxAwait         = 25 * time.Second
)
View Source
const DefaultRequestTimeout = 30 * time.Second

DefaultRequestTimeout bounds non-streaming requests, including body reads, when the caller has not supplied a deadline. SSE follows the caller context.

Variables

View Source
var (
	ErrTaskInvalid     = errors.New("invalid task request")
	ErrTaskConflict    = errors.New("task idempotency conflict")
	ErrTaskNotFound    = errors.New("task not found")
	ErrTaskCapacity    = errors.New("task active capacity reached")
	ErrTaskUnavailable = errors.New("task store unavailable")
)

Functions

func GenerateToken

func GenerateToken() string

GenerateToken returns a fresh 32-byte hex token.

func ReadPeerCredential

func ReadPeerCredential(path string) (string, error)

ReadPeerCredential reads a trusted local private reference. All errors remain generic: credential bytes/path contents never enter model-visible errors.

Types

type AgentEvent

type AgentEvent struct {
	Type     AgentEventType
	Content  string
	ToolName string
	ToolArgs string
	Error    error
}

AgentEvent is the wire-friendly StreamEvent the Sender hands to the server.

type AgentEventType

type AgentEventType int

AgentEventType enumerates the event categories the ACP server cares about. Defining a local enum keeps acp from importing internal/agent (which would create an import cycle: tools → acp → agent → tools).

const (
	AgentEventToken AgentEventType = iota
	AgentEventToolStart
	AgentEventToolOutput
	AgentEventDone
	AgentEventError
)

type Client

type Client struct {
	BaseURL string
	Token   string
	HTTP    *http.Client
}

Client speaks HTTP/SSE to a remote ACP agent. An injected HTTP client is reused unchanged, including its transport, connection pool and policies.

func NewClient

func NewClient(baseURL, token string) *Client

func (*Client) AwaitTask

func (c *Client) AwaitTask(ctx context.Context, id string, wait time.Duration) (Task, error)

AwaitTask bounds only this observation; timeout/disconnect never cancels work.

func (*Client) Cancel

func (c *Client) Cancel(ctx context.Context, messageID string) error

func (*Client) CancelTask

func (c *Client) CancelTask(ctx context.Context, id string) (Task, error)

func (*Client) Coordination

func (c *Client) Coordination(ctx context.Context, operation string, in CoordinationRequest) (CoordinationResponse, error)

Coordination uses a scoped peer credential, distinct from a global ACP token. Endpoint is explicit application configuration, never model input/discovery. Redirects are refused, including same-host redirects, before credential reuse.

func (*Client) GetInfo

func (c *Client) GetInfo(ctx context.Context) (Info, error)

func (*Client) ListSessions

func (c *Client) ListSessions(ctx context.Context) ([]json.RawMessage, error)

func (*Client) Ping

func (c *Client) Ping(ctx context.Context) error

func (*Client) Send

func (c *Client) Send(ctx context.Context, content string) (<-chan Event, error)

Send preserves the default-session API. Cancel ctx if you stop reading events.

func (*Client) SendTo

func (c *Client) SendTo(ctx context.Context, sessionID, content string) (<-chan Event, error)

SendTo addresses an existing remote session, or creates one when sessionID is empty. The POST is bounded independently of the subsequent agent stream.

func (*Client) Stream

func (c *Client) Stream(ctx context.Context, messageID string) (<-chan Event, error)

Stream delivers SSE frames until one done/error event. A malformed or dropped connection produces one terminal error instead of appearing successful. Callers MUST cancel ctx when abandoning the receiver. Cancellation releases blocked sends and closes the HTTP body. If the buffer is full during failure, its oldest pending nonterminal event is displaced to retain the terminal error.

func (*Client) SubmitTask

func (c *Client) SubmitTask(ctx context.Context, input TaskSubmitRequest) (Task, error)

SubmitTask persists a request and returns its handle without waiting for the agent. The caller must retain/reuse the idempotency key across uncertain POSTs.

func (*Client) TaskStatus

func (c *Client) TaskStatus(ctx context.Context, id string) (Task, error)

type Config

type Config struct {
	Coordination   *coordination.Store
	Tasks          *TaskStore
	Addr           string
	Token          string
	AllowedOrigins []string
	Agent          Sender
	// ResolveSender selects the actual executing session. It may create a new
	// persisted session when the ID is empty; its returned identity is checked.
	ResolveSender func(context.Context, string) (Sender, error)
	Store         session.Store
	Name          string
	Version       string
	// JobStore and JobWorker are optional. When both are non-nil the server
	// registers the /v1/jobs endpoints and wires job submission through the worker.
	JobStore  *daemon.JobStore
	JobWorker *daemon.Worker
}

Config is the constructor input.

type CoordinationRequest

type CoordinationRequest struct {
	Execution        *coordination.TransportExecution `json:"execution,omitempty"`
	Send             *coordination.SendInput          `json:"send,omitempty"`
	MessageID        string                           `json:"message_id,omitempty"`
	Payload          json.RawMessage                  `json:"payload,omitempty"`
	IdempotencyKey   string                           `json:"idempotency_key,omitempty"`
	ExpectedRevision int64                            `json:"expected_revision,omitempty"`
	Event            string                           `json:"event,omitempty"`
	AfterRevision    int64                            `json:"after_revision,omitempty"`
	Limit            int                              `json:"limit,omitempty"`
	WaitMilliseconds int                              `json:"wait_milliseconds,omitempty"`
}

CoordinationRequest deliberately has no sender, project or credential fields. The authenticated peer token alone supplies the actor for every operation.

type CoordinationResponse

type CoordinationResponse struct {
	NextRevision int64                  `json:"next_revision,omitempty"`
	HasMore      bool                   `json:"has_more,omitempty"`
	Self         *coordination.Peer     `json:"self,omitempty"`
	Message      *coordination.Message  `json:"message,omitempty"`
	Messages     []coordination.Message `json:"messages,omitempty"`
	Peers        []coordination.Peer    `json:"peers,omitempty"`
	Events       []coordination.Event   `json:"events,omitempty"`
	TimedOut     bool                   `json:"timed_out,omitempty"`
}

type Event

type Event struct {
	Type      string         `json:"type"` // text_delta | tool_call | done | error
	Content   string         `json:"content,omitempty"`
	ToolName  string         `json:"tool_name,omitempty"`
	ToolArgs  string         `json:"tool_args,omitempty"`
	Error     string         `json:"error,omitempty"`
	Timestamp time.Time      `json:"timestamp"`
	Extra     map[string]any `json:"extra,omitempty"`
}

Event is a single SSE frame on /v1/messages/{id}/events.

type InboundLog

type InboundLog struct {
	From      string    `json:"from"`
	MessageID string    `json:"messageID"`
	Snippet   string    `json:"snippet"`
	At        time.Time `json:"at"`
}

InboundLog records that a peer sent us a message; the /acp dialog displays the last few entries.

type Info

type Info struct {
	Name         string   `json:"name"`
	Version      string   `json:"version"`
	Model        string   `json:"model"`
	Capabilities []string `json:"capabilities"`
}

Info is the response body of GET /v1/info.

type RoutePattern

type RoutePattern struct {
	Path    string   // e.g. "/v1/info", "/v1/messages/{id}/events"
	Methods []string // expected methods; empty = test all standard methods
}

RoutePattern is one registered route with its HTTP method expectation.

type SendRequest

type SendRequest struct {
	From      string         `json:"from"`
	Content   string         `json:"content"`
	SessionID string         `json:"sessionID,omitempty"`
	Options   map[string]any `json:"options,omitempty"`
}

SendRequest is the body of POST /v1/messages.

type SendResponse

type SendResponse struct {
	MessageID string `json:"messageID"`
	SessionID string `json:"sessionID"`
}

SendResponse is returned from POST /v1/messages.

type Sender

type Sender interface {
	StreamACP(ctx context.Context, userInput string) (<-chan AgentEvent, error)
	Model() string
	SessionID() string
}

Sender is the slice of agent.Agent that the ACP server actually needs. Defining it locally keeps the package free of circular imports.

type Server

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

Server exposes the ACP HTTP+SSE surface.

func NewServer

func NewServer(cfg Config) *Server

func (*Server) Addr

func (s *Server) Addr() string

Addr returns the server's listen address.

func (*Server) Handler

func (s *Server) Handler() http.Handler

Handler returns the http.Handler so callers can mount it under their own mux (and so tests can drive it without a TCP listener). Also populates s.routes so the auth guard test can auto-discover all routes. Safe for concurrent use.

func (*Server) RecentInbound

func (s *Server) RecentInbound() []InboundLog

RecentInbound is a snapshot of the inbound ring buffer.

func (*Server) Routes

func (s *Server) Routes() []RoutePattern

Routes returns every registered route pattern. The auth guard test (§8.7.5) uses this to auto-discover all routes and assert 401 on each, so a new route added without auth fails the test automatically.

func (*Server) Shutdown

func (s *Server) Shutdown(ctx context.Context) error

func (*Server) Start

func (s *Server) Start() error

Start binds the server and runs http.Serve in a goroutine. Use Shutdown to stop it.

func (*Server) StartTasks

func (s *Server) StartTasks(ctx context.Context) error

StartTasks starts this server's bounded durable task workers. Mounted Handler users call it explicitly; Handler itself never starts background work.

func (*Server) Token

func (s *Server) Token() string

Token exposes the bearer token for clients (read by /acp dialog and `overkill acp token`).

type Task

type Task struct {
	WorkerError string    `json:"workerError,omitempty"`
	TaskID      string    `json:"taskID"`
	MessageID   string    `json:"messageID"`
	SessionID   string    `json:"sessionID"`
	Status      string    `json:"status"`
	Reply       string    `json:"reply,omitempty"`
	Error       string    `json:"error,omitempty"`
	Progress    string    `json:"progress,omitempty"`
	CreatedAt   time.Time `json:"createdAt"`
	UpdatedAt   time.Time `json:"updatedAt"`
}

func (Task) Terminal

func (t Task) Terminal() bool

type TaskStore

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

func NewTaskStore

func NewTaskStore(db *sql.DB) *TaskStore

func (*TaskStore) Await

func (s *TaskStore) Await(ctx context.Context, id string, wait time.Duration) (Task, error)

func (*TaskStore) Cancel

func (s *TaskStore) Cancel(ctx context.Context, id string) (Task, error)

func (*TaskStore) Get

func (s *TaskStore) Get(ctx context.Context, id string) (Task, error)

func (*TaskStore) Migrate

func (s *TaskStore) Migrate(ctx context.Context) error

func (*TaskStore) Submit

func (s *TaskStore) Submit(ctx context.Context, req TaskSubmitRequest) (Task, error)

type TaskSubmitRequest

type TaskSubmitRequest struct {
	From           string `json:"from,omitempty"`
	Content        string `json:"content"`
	SessionID      string `json:"sessionID"`
	IdempotencyKey string `json:"idempotencyKey"`
}

Jump to

Keyboard shortcuts

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