agenthost

package
v0.0.0-...-7c80309 Latest Latest
Warning

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

Go to latest
Published: Jan 22, 2026 License: MIT Imports: 19 Imported by: 0

Documentation

Overview

Package agenthost provides client/server communication with agent host processes.

Package agenthost defines the IPC protocol for agent host processes.

The agent host is a lightweight process that wraps a Claude Code agent, allowing the daemon to restart and reattach to running agent subprocesses. The host maintains its own Unix socket for bidirectional communication.

Protocol Overview

The protocol uses JSON-encoded request/response messaging over Unix sockets, following the same envelope pattern as the main daemon protocol.

Versioning

The protocol includes a version field for compatibility checking. Clients should verify ProtocolVersion matches before proceeding. Breaking changes increment the major version; additive changes increment minor.

Package agenthost implements the agent host process that wraps Claude Code agents.

Index

Constants

View Source
const BroadcastTimeout = 100 * time.Millisecond

BroadcastTimeout is how long to wait for a client write before giving up.

View Source
const ConnectTimeout = 2 * time.Second

ConnectTimeout is the default timeout for connecting to an agent host.

View Source
const HistoryBufferSize = 1000

HistoryBufferSize is the number of stream events to keep in the ring buffer.

View Source
const MinProtocolVersion = "1.0"

MinProtocolVersion is the minimum supported protocol version. Hosts and clients should reject connections with older versions.

View Source
const ProtocolVersion = "1.0"

ProtocolVersion is the current agent host protocol version. Format: major.minor where major changes break compatibility.

View Source
const RequestTimeout = 10 * time.Second

RequestTimeout is the default timeout for request/response operations.

Variables

View Source
var (
	ErrNotConnected = errors.New("not connected to agent host")
	ErrHostNotFound = errors.New("agent host not found")
)

Client errors.

View Source
var (
	ErrAgentNotRunning = errors.New("agent is not running")
	ErrAgentNotSet     = errors.New("agent not set")
)

Errors returned by manager operations.

Functions

func DecodePayload

func DecodePayload[T any](payload any) (*T, error)

DecodePayload is a generic helper that decodes a response payload to type T.

func UnmarshalPayload

func UnmarshalPayload(payload any, dst any) error

UnmarshalPayload converts a generic payload to a specific type. This handles the JSON round-trip needed because payloads arrive as map[string]any.

Types

type AgentInfo

type AgentInfo struct {
	ID          string    `json:"id"`                    // Agent ID
	Project     string    `json:"project"`               // Project name
	State       string    `json:"state"`                 // starting, running, idle, done, error
	PID         int       `json:"pid"`                   // Agent subprocess PID (0 if not running)
	Worktree    string    `json:"worktree"`              // Worktree path
	StartedAt   time.Time `json:"started_at"`            // When agent started
	Task        string    `json:"task,omitempty"`        // Current task ID
	Description string    `json:"description,omitempty"` // Agent description
	Backend     string    `json:"backend,omitempty"`     // CLI backend (claude, codex)
}

AgentInfo contains agent subprocess status.

type AttachRequest

type AttachRequest struct {
	Offset int64 `json:"offset,omitempty"` // Stream offset to resume from (0 = beginning)
}

AttachRequest is the payload for host.attach requests.

type AttachResponse

type AttachResponse struct {
	AgentID      string `json:"agent_id"`      // Agent being attached to
	StreamOffset int64  `json:"stream_offset"` // Current stream position
}

AttachResponse is the payload for host.attach responses.

type Client

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

Client connects to an agent host process over Unix socket.

func NewClient

func NewClient(agentID string) (*Client, error)

NewClient creates a new agent host client for the given agent ID. The socket path is determined from the agent ID using paths.AgentHostSocketPath.

func NewClientWithSocket

func NewClientWithSocket(agentID, socketPath string) *Client

NewClientWithSocket creates a new agent host client with an explicit socket path. This is useful for testing or when the socket path is already known.

func (*Client) AgentID

func (c *Client) AgentID() string

AgentID returns the agent ID this client is for.

func (*Client) Attach

func (c *Client) Attach(offset int64) (*AttachResponse, error)

Attach attaches to the agent's output stream. After attaching, use RecvStreamEvent to receive events.

func (*Client) Close

func (c *Client) Close() error

Close closes the connection to the agent host.

func (*Client) Connect

func (c *Client) Connect() error

Connect establishes a connection to the agent host.

func (*Client) Detach

func (c *Client) Detach() error

Detach detaches from the agent's output stream.

func (*Client) IsConnected

func (c *Client) IsConnected() bool

IsConnected returns true if the client is connected.

func (*Client) List

func (c *Client) List() (*ListResponse, error)

List returns all agents managed by this host.

func (*Client) Ping

func (c *Client) Ping() (*PingResponse, error)

Ping sends a ping request to check host connectivity.

func (*Client) RecvStreamEvent

func (c *Client) RecvStreamEvent() (*StreamEvent, error)

RecvStreamEvent receives the next stream event from an attached connection. Only call this after Attach has been called.

func (*Client) Send

func (c *Client) Send(req *Request) (*Response, error)

Send sends a request and waits for the response.

func (*Client) SendInput

func (c *Client) SendInput(input string) error

SendInput sends input to the agent.

func (*Client) SocketPath

func (c *Client) SocketPath() string

SocketPath returns the socket path this client connects to.

func (*Client) Status

func (c *Client) Status() (*StatusResponse, error)

Status gets the detailed host and agent status.

func (*Client) Stop

func (c *Client) Stop(force bool, timeout int, reason string) (*StopResponse, error)

Stop gracefully stops the agent and host.

type DiscoveredHost

type DiscoveredHost struct {
	AgentID    string          // Agent ID extracted from socket filename
	SocketPath string          // Full path to the socket file
	Status     *StatusResponse // Status from the host (nil if probe failed)
}

DiscoveredHost contains information about a discovered agent host.

func DiscoverActiveHosts

func DiscoverActiveHosts() ([]DiscoveredHost, error)

DiscoverActiveHosts scans the hosts directory for socket files and probes each one. Returns a list of hosts that successfully responded to a status request.

func DiscoverHostsForProject

func DiscoverHostsForProject(projectName string) ([]DiscoveredHost, error)

DiscoverHostsForProject returns active hosts that belong to a specific project.

type HistoryEntry

type HistoryEntry struct {
	Role       string `json:"role"`                  // assistant, user, tool
	Content    string `json:"content,omitempty"`     // Message content
	ToolName   string `json:"tool_name,omitempty"`   // For tool calls
	ToolInput  string `json:"tool_input,omitempty"`  // Tool input data
	ToolResult string `json:"tool_result,omitempty"` // Tool result data
	Timestamp  string `json:"timestamp"`             // RFC3339 timestamp
}

HistoryEntry represents a single chat history entry. This mirrors daemon.ChatEntryDTO but is specific to the agent host protocol.

type HistoryRequest

type HistoryRequest struct {
	Limit int `json:"limit,omitempty"` // Maximum entries to return (0 = all)
}

HistoryRequest is the payload for host.history requests.

type HistoryResponse

type HistoryResponse struct {
	AgentID string         `json:"agent_id"`        // Agent ID
	Entries []HistoryEntry `json:"entries"`         // Chat history entries
	Offset  int64          `json:"offset"`          // Stream offset at time of response
	Total   int            `json:"total,omitempty"` // Total entries available (before limit)
}

HistoryResponse is the payload for host.history responses.

type HostInfo

type HostInfo struct {
	PID             int       `json:"pid"`              // Host process ID
	Version         string    `json:"version"`          // Host version
	ProtocolVersion string    `json:"protocol_version"` // Protocol version
	StartedAt       time.Time `json:"started_at"`       // When host started
	SocketPath      string    `json:"socket_path"`      // Path to host socket
}

HostInfo contains host process status.

type ListResponse

type ListResponse struct {
	Agents []AgentInfo `json:"agents"`
}

ListResponse is the payload for host.list responses.

type Manager

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

Manager manages a single agent and its output streaming. It wraps an agent.Agent and provides methods for the host server.

func NewManager

func NewManager(ag *agent.Agent) *Manager

NewManager creates a new manager for the given agent.

func (*Manager) AgentID

func (m *Manager) AgentID() string

AgentID returns the agent ID.

func (*Manager) AgentInfo

func (m *Manager) AgentInfo() AgentInfo

AgentInfo returns the current agent info for status reporting.

func (*Manager) GetBufferedEvents

func (m *Manager) GetBufferedEvents(fromOffset int64) []*StreamEvent

GetBufferedEvents returns events from the history buffer starting from the given offset. Events are returned in chronological order (sorted by offset).

func (*Manager) SendMessage

func (m *Manager) SendMessage(content string) error

SendMessage sends a message to the agent.

func (*Manager) SetServer

func (m *Manager) SetServer(s *Server)

SetServer sets the server for broadcasting events.

func (*Manager) StartReadLoop

func (m *Manager) StartReadLoop() error

StartReadLoop starts the agent's read loop with callbacks for streaming.

func (*Manager) Stop

func (m *Manager) Stop(force bool, timeout time.Duration) error

Stop stops the agent.

func (*Manager) StreamOffset

func (m *Manager) StreamOffset() int64

StreamOffset returns the current stream offset.

type MessageType

type MessageType string

MessageType identifies the type of agent host IPC message.

const (
	// Host management
	MsgHostPing   MessageType = "host.ping"   // Health check and version info
	MsgHostStatus MessageType = "host.status" // Get detailed host and agent status

	// Agent listing
	MsgHostList MessageType = "host.list" // List all agents managed by this host

	// Stream management
	MsgHostAttach MessageType = "host.attach" // Attach to agent output stream
	MsgHostDetach MessageType = "host.detach" // Detach from agent output stream

	// History retrieval
	MsgHostHistory MessageType = "host.history" // Retrieve buffered chat history

	// Agent communication
	MsgHostSend MessageType = "host.send" // Send input to the agent

	// Lifecycle control
	MsgHostStop MessageType = "host.stop" // Gracefully stop the agent and host
)

type PingResponse

type PingResponse struct {
	Version         string    `json:"version"`          // Host process version
	ProtocolVersion string    `json:"protocol_version"` // IPC protocol version
	Uptime          string    `json:"uptime"`           // Human-readable uptime
	StartedAt       time.Time `json:"started_at"`       // When the host started
}

PingResponse is the payload for host.ping responses.

type Request

type Request struct {
	Type    MessageType `json:"type"`
	ID      string      `json:"id,omitempty"`      // Optional request ID for correlation
	Payload any         `json:"payload,omitempty"` // Type-specific payload
}

Request is the envelope for all agent host IPC requests.

type Response

type Response struct {
	Type    MessageType `json:"type"`
	ID      string      `json:"id,omitempty"` // Correlates with request ID
	Success bool        `json:"success"`
	Error   string      `json:"error,omitempty"`
	Payload any         `json:"payload,omitempty"` // Type-specific payload
}

Response is the envelope for all agent host IPC responses.

func ErrorResponse

func ErrorResponse(req *Request, msg string) *Response

ErrorResponse creates an error response for a request.

func SuccessResponse

func SuccessResponse(req *Request, payload any) *Response

SuccessResponse creates a success response for a request.

type SendRequest

type SendRequest struct {
	Input string `json:"input"` // Raw input to send to agent
}

SendRequest is the payload for host.send requests.

type Server

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

Server is the Unix socket RPC server for an agent host process. Each agent host manages a single coding agent and its associated socket.

func NewServer

func NewServer(socketPath string, manager *Manager) *Server

NewServer creates a new agent host server.

func (*Server) AttachedCount

func (s *Server) AttachedCount() int

AttachedCount returns the number of attached streaming clients.

func (*Server) Broadcast

func (s *Server) Broadcast(event *StreamEvent)

Broadcast sends a stream event to all attached clients.

func (*Server) SocketPath

func (s *Server) SocketPath() string

SocketPath returns the socket path this server listens on.

func (*Server) Start

func (s *Server) Start() error

Start begins listening on the Unix socket. Returns an error if the server is already running or cannot bind.

func (*Server) Stop

func (s *Server) Stop() error

Stop gracefully shuts down the server.

type StatusResponse

type StatusResponse struct {
	Host  HostInfo  `json:"host"`
	Agent AgentInfo `json:"agent"`
}

StatusResponse is the payload for host.status responses.

func ProbeHost

func ProbeHost(agentID string) (*StatusResponse, error)

ProbeHost attempts to connect to an agent host and returns its status. Returns ErrHostNotFound if the host socket doesn't exist or isn't responding.

type StopRequest

type StopRequest struct {
	Force   bool   `json:"force,omitempty"`   // Force kill (SIGKILL) vs graceful (/quit)
	Timeout int    `json:"timeout,omitempty"` // Graceful shutdown timeout in seconds (default: 30)
	Reason  string `json:"reason,omitempty"`  // Optional reason for stopping
}

StopRequest is the payload for host.stop requests.

type StopResponse

type StopResponse struct {
	Stopped    bool   `json:"stopped"`             // Whether agent was successfully stopped
	ExitCode   int    `json:"exit_code,omitempty"` // Agent exit code if available
	Graceful   bool   `json:"graceful"`            // Whether shutdown was graceful
	Duration   string `json:"duration,omitempty"`  // How long shutdown took
	FinalState string `json:"final_state"`         // Final agent state before exit
}

StopResponse is the payload for host.stop responses.

type StreamEvent

type StreamEvent struct {
	Type      string `json:"type"`                 // output, state, chat_entry, error
	AgentID   string `json:"agent_id"`             // Agent that produced the event
	Offset    int64  `json:"offset"`               // Stream position of this event
	Timestamp string `json:"timestamp"`            // RFC3339 timestamp
	Data      string `json:"data,omitempty"`       // For output events: raw output data
	State     string `json:"state,omitempty"`      // For state events: new state
	ChatEntry any    `json:"chat_entry,omitempty"` // For chat_entry events: parsed chat message
	Error     string `json:"error,omitempty"`      // For error events: error message
}

StreamEvent is sent to attached clients when agent output occurs. This mirrors the daemon StreamEvent but is specific to a single agent.

Jump to

Keyboard shortcuts

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