Documentation
¶
Overview ¶
Package agent provides the agent plugin system for Deputy.
The agent plugin architecture supports three types of plugins:
- Builtin: Compiled into the Deputy binary (Claude, Codex)
- External: Separate processes discovered via PATH (deputy-agent-<name>)
- Remote: gRPC/Connect servers at configured endpoints
Plugin Interface ¶
All agent plugins implement the AgentPlugin gRPC service defined in api/deputy/agent/v1/agent.proto. This includes:
- GetInfo: Returns plugin metadata and capabilities
- Execute: Runs the agent with streaming events
- Resume: Continues a previous session
- Approve: Handles approval requests
- Cancel: Gracefully terminates execution
Registry ¶
The Registry manages plugin discovery and lifecycle:
registry := agent.NewRegistry() registry.RegisterBuiltin(claude.NewPlugin()) registry.DiscoverExternal() // finds deputy-agent-* in PATH
Builtin Plugins ¶
Builtin plugins wrap existing ai.Provider implementations:
plugin := agent.WrapProvider(aiProvider)
External Plugins ¶
External plugins are discovered by searching PATH for executables matching the pattern "deputy-agent-<name>". When executed, they must serve the AgentPlugin gRPC service on a Unix socket or TCP port.
Sandboxed Execution ¶
Plugins can be run in sandboxed environments:
registry := agent.NewRegistry()
_ = registry.RegisterSandboxed("claude", agent.SandboxOptions{
Runtime: agent.RuntimeDocker,
Image: "deputy-agent:latest",
})
Package agent provides a plugin registry for AI agent implementations.
Plugins implement the generated agentv1connect.AgentPluginHandler interface.
Index ¶
- Constants
- Variables
- func CheckRuntime(runtime SandboxRuntime) error
- func DefaultSandboxImage(agentName string) string
- func DiscoverExternal(ctx context.Context) ([]string, error)
- func FindPluginInPath(name string) string
- func Get(name string) (agentv1connect.AgentPluginHandler, error)
- func GetOrDiscover(ctx context.Context, name string) (agentv1connect.AgentPluginHandler, error)
- func List() []string
- func ListAvailable() []string
- func MustRegisterBuiltin(name string, handler agentv1connect.AgentPluginHandler)
- func MustRegisterSandboxed(name string, opts SandboxOptions)
- func NewExternalPluginHandler(ctx context.Context, name, execPath string) (agentv1connect.AgentPluginHandler, func() error, error)
- func Register(entry *PluginEntry) error
- func RegisterBuiltin(name string, handler agentv1connect.AgentPluginHandler) error
- func RegisterSandboxed(name string, opts SandboxOptions) error
- type ClaudeHandler
- func (h *ClaudeHandler) Approve(ctx context.Context, req *connect.Request[agentv1.ApproveRequest]) (*connect.Response[agentv1.ApproveResponse], error)
- func (h *ClaudeHandler) Cancel(ctx context.Context, req *connect.Request[agentv1.CancelRequest]) (*connect.Response[agentv1.CancelResponse], error)
- func (h *ClaudeHandler) Execute(ctx context.Context, req *connect.Request[agentv1.ExecuteRequest], ...) error
- func (h *ClaudeHandler) ExecuteIter(ctx context.Context, req *agentv1.ExecuteRequest) iter.Seq2[*agentv1.ExecuteEvent, error]
- func (h *ClaudeHandler) GetInfo(ctx context.Context, req *connect.Request[agentv1.GetInfoRequest]) (*connect.Response[agentv1.GetInfoResponse], error)
- func (h *ClaudeHandler) Resume(ctx context.Context, req *connect.Request[agentv1.ResumeRequest], ...) error
- func (h *ClaudeHandler) ResumeIter(ctx context.Context, req *agentv1.ResumeRequest) iter.Seq2[*agentv1.ExecuteEvent, error]
- type CodexHandler
- func (h *CodexHandler) Approve(ctx context.Context, req *connect.Request[agentv1.ApproveRequest]) (*connect.Response[agentv1.ApproveResponse], error)
- func (h *CodexHandler) Cancel(ctx context.Context, req *connect.Request[agentv1.CancelRequest]) (*connect.Response[agentv1.CancelResponse], error)
- func (h *CodexHandler) Execute(ctx context.Context, req *connect.Request[agentv1.ExecuteRequest], ...) error
- func (h *CodexHandler) ExecuteIter(ctx context.Context, req *agentv1.ExecuteRequest) iter.Seq2[*agentv1.ExecuteEvent, error]
- func (h *CodexHandler) GetInfo(ctx context.Context, req *connect.Request[agentv1.GetInfoRequest]) (*connect.Response[agentv1.GetInfoResponse], error)
- func (h *CodexHandler) Resume(ctx context.Context, req *connect.Request[agentv1.ResumeRequest], ...) error
- func (h *CodexHandler) ResumeIter(ctx context.Context, req *agentv1.ResumeRequest) iter.Seq2[*agentv1.ExecuteEvent, error]
- type Executor
- type PluginEntry
- type PluginType
- type Registry
- func (r *Registry) Close() error
- func (r *Registry) DiscoverExternal(ctx context.Context) ([]string, error)
- func (r *Registry) Entries() []*PluginEntry
- func (r *Registry) Get(name string) (agentv1connect.AgentPluginHandler, error)
- func (r *Registry) GetEntry(name string) (*PluginEntry, error)
- func (r *Registry) GetOrDiscover(ctx context.Context, name string) (agentv1connect.AgentPluginHandler, error)
- func (r *Registry) List() []string
- func (r *Registry) ListAvailable() []string
- func (r *Registry) MustRegisterBuiltin(name string, handler agentv1connect.AgentPluginHandler)
- func (r *Registry) MustRegisterSandboxed(name string, opts SandboxOptions)
- func (r *Registry) Register(entry *PluginEntry) error
- func (r *Registry) RegisterBuiltin(name string, handler agentv1connect.AgentPluginHandler) error
- func (r *Registry) RegisterRemote(ctx context.Context, name, address string) error
- func (r *Registry) RegisterSandboxed(name string, opts SandboxOptions) error
- func (r *Registry) Unregister(name string) error
- type SandboxOptions
- type SandboxRuntime
- type SandboxedHandler
- func (h *SandboxedHandler) Approve(ctx context.Context, req *connect.Request[agentv1.ApproveRequest]) (*connect.Response[agentv1.ApproveResponse], error)
- func (h *SandboxedHandler) Cancel(ctx context.Context, req *connect.Request[agentv1.CancelRequest]) (*connect.Response[agentv1.CancelResponse], error)
- func (h *SandboxedHandler) Execute(ctx context.Context, req *connect.Request[agentv1.ExecuteRequest], ...) error
- func (h *SandboxedHandler) GetInfo(ctx context.Context, req *connect.Request[agentv1.GetInfoRequest]) (*connect.Response[agentv1.GetInfoResponse], error)
- func (h *SandboxedHandler) Resume(ctx context.Context, req *connect.Request[agentv1.ResumeRequest], ...) error
Constants ¶
const PluginPrefix = "deputy-plugin-"
PluginPrefix is the prefix for external plugin executables.
Variables ¶
var DefaultRegistry = NewRegistry()
DefaultRegistry is the global plugin registry.
Functions ¶
func CheckRuntime ¶
func CheckRuntime(runtime SandboxRuntime) error
CheckRuntime verifies the container runtime is available.
func DefaultSandboxImage ¶
DefaultSandboxImage returns a suggested image for sandboxed agent execution.
func DiscoverExternal ¶
DiscoverExternal discovers external plugins and adds them to the default registry.
func FindPluginInPath ¶
FindPluginInPath searches PATH for a plugin executable by name. It looks for "deputy-plugin-<name>" in PATH and returns the full path if found. Returns empty string if not found.
func Get ¶
func Get(name string) (agentv1connect.AgentPluginHandler, error)
Get retrieves a plugin from the default registry.
func GetOrDiscover ¶
func GetOrDiscover(ctx context.Context, name string) (agentv1connect.AgentPluginHandler, error)
GetOrDiscover retrieves a plugin from the default registry, discovering from PATH if needed.
func List ¶
func List() []string
List returns all registered plugin names from the default registry.
func ListAvailable ¶
func ListAvailable() []string
ListAvailable returns all available plugins from the default registry and PATH.
func MustRegisterBuiltin ¶
func MustRegisterBuiltin(name string, handler agentv1connect.AgentPluginHandler)
MustRegisterBuiltin registers a builtin plugin, panicking on error.
func MustRegisterSandboxed ¶
func MustRegisterSandboxed(name string, opts SandboxOptions)
MustRegisterSandboxed registers a sandboxed plugin to the default registry, panicking on error.
func NewExternalPluginHandler ¶
func NewExternalPluginHandler(ctx context.Context, name, execPath string) (agentv1connect.AgentPluginHandler, func() error, error)
NewExternalPluginHandler creates a handler from an external executable. It starts the plugin process and verifies it implements the protocol. Returns the handler and a closer function to clean up resources.
func Register ¶
func Register(entry *PluginEntry) error
Register adds a plugin to the default registry.
func RegisterBuiltin ¶
func RegisterBuiltin(name string, handler agentv1connect.AgentPluginHandler) error
RegisterBuiltin registers a builtin plugin to the default registry.
func RegisterSandboxed ¶
func RegisterSandboxed(name string, opts SandboxOptions) error
RegisterSandboxed registers a sandboxed plugin to the default registry.
Types ¶
type ClaudeHandler ¶
type ClaudeHandler struct {
agentv1connect.UnimplementedAgentPluginHandler
}
ClaudeHandler implements AgentPluginHandler using the Claude CLI.
func NewClaudeHandler ¶
func NewClaudeHandler() *ClaudeHandler
NewClaudeHandler creates a new Claude agent plugin handler.
func (*ClaudeHandler) Approve ¶
func (h *ClaudeHandler) Approve(ctx context.Context, req *connect.Request[agentv1.ApproveRequest]) (*connect.Response[agentv1.ApproveResponse], error)
Approve handles approval requests.
func (*ClaudeHandler) Cancel ¶
func (h *ClaudeHandler) Cancel(ctx context.Context, req *connect.Request[agentv1.CancelRequest]) (*connect.Response[agentv1.CancelResponse], error)
Cancel requests graceful termination.
func (*ClaudeHandler) Execute ¶
func (h *ClaudeHandler) Execute(ctx context.Context, req *connect.Request[agentv1.ExecuteRequest], stream *connect.ServerStream[agentv1.ExecuteEvent]) error
Execute runs the Claude CLI with the given request and streams events.
func (*ClaudeHandler) ExecuteIter ¶
func (h *ClaudeHandler) ExecuteIter(ctx context.Context, req *agentv1.ExecuteRequest) iter.Seq2[*agentv1.ExecuteEvent, error]
ExecuteIter implements Executor for in-process execution without a connect.ServerStream.
func (*ClaudeHandler) GetInfo ¶
func (h *ClaudeHandler) GetInfo(ctx context.Context, req *connect.Request[agentv1.GetInfoRequest]) (*connect.Response[agentv1.GetInfoResponse], error)
GetInfo returns metadata about the Claude plugin.
func (*ClaudeHandler) Resume ¶
func (h *ClaudeHandler) Resume(ctx context.Context, req *connect.Request[agentv1.ResumeRequest], stream *connect.ServerStream[agentv1.ExecuteEvent]) error
Resume continues a previous Claude session.
func (*ClaudeHandler) ResumeIter ¶
func (h *ClaudeHandler) ResumeIter(ctx context.Context, req *agentv1.ResumeRequest) iter.Seq2[*agentv1.ExecuteEvent, error]
ResumeIter implements Executor for resuming sessions without a connect.ServerStream.
type CodexHandler ¶
type CodexHandler struct {
agentv1connect.UnimplementedAgentPluginHandler
}
CodexHandler implements AgentPluginHandler using the OpenAI Codex CLI.
func NewCodexHandler ¶
func NewCodexHandler() *CodexHandler
NewCodexHandler creates a new Codex agent plugin handler.
func (*CodexHandler) Approve ¶
func (h *CodexHandler) Approve(ctx context.Context, req *connect.Request[agentv1.ApproveRequest]) (*connect.Response[agentv1.ApproveResponse], error)
Approve handles approval requests.
func (*CodexHandler) Cancel ¶
func (h *CodexHandler) Cancel(ctx context.Context, req *connect.Request[agentv1.CancelRequest]) (*connect.Response[agentv1.CancelResponse], error)
Cancel requests graceful termination.
func (*CodexHandler) Execute ¶
func (h *CodexHandler) Execute(ctx context.Context, req *connect.Request[agentv1.ExecuteRequest], stream *connect.ServerStream[agentv1.ExecuteEvent]) error
Execute runs the Codex CLI with the given request and streams events.
func (*CodexHandler) ExecuteIter ¶
func (h *CodexHandler) ExecuteIter(ctx context.Context, req *agentv1.ExecuteRequest) iter.Seq2[*agentv1.ExecuteEvent, error]
ExecuteIter implements Executor for in-process execution without a connect.ServerStream.
func (*CodexHandler) GetInfo ¶
func (h *CodexHandler) GetInfo(ctx context.Context, req *connect.Request[agentv1.GetInfoRequest]) (*connect.Response[agentv1.GetInfoResponse], error)
GetInfo returns metadata about the Codex plugin.
func (*CodexHandler) Resume ¶
func (h *CodexHandler) Resume(ctx context.Context, req *connect.Request[agentv1.ResumeRequest], stream *connect.ServerStream[agentv1.ExecuteEvent]) error
Resume continues a previous Codex session.
func (*CodexHandler) ResumeIter ¶
func (h *CodexHandler) ResumeIter(ctx context.Context, req *agentv1.ResumeRequest) iter.Seq2[*agentv1.ExecuteEvent, error]
ResumeIter implements Executor for resuming sessions without a connect.ServerStream.
type Executor ¶
type Executor interface {
// ExecuteIter executes the agent and returns an iterator over events.
ExecuteIter(ctx context.Context, req *agentv1.ExecuteRequest) iter.Seq2[*agentv1.ExecuteEvent, error]
// ResumeIter resumes a session and returns an iterator over events.
ResumeIter(ctx context.Context, req *agentv1.ResumeRequest) iter.Seq2[*agentv1.ExecuteEvent, error]
}
Executor is an optional interface that agent handlers can implement to support in-process execution without requiring a connect.ServerStream.
func AsExecutor ¶
func AsExecutor(handler agentv1connect.AgentPluginHandler) Executor
AsExecutor attempts to cast an AgentPluginHandler to an Executor. Returns nil if the handler doesn't implement Executor.
type PluginEntry ¶
type PluginEntry struct {
Name string
Type PluginType
Handler agentv1connect.AgentPluginHandler
Path string // For external plugins, the executable path
Address string // For remote plugins, the server address
// contains filtered or unexported fields
}
PluginEntry describes a registered plugin.
func (*PluginEntry) Close ¶
func (e *PluginEntry) Close() error
Close releases resources held by this plugin entry.
type PluginType ¶
type PluginType string
PluginType identifies the type of plugin.
const ( // PluginTypeBuiltin is a plugin compiled into the Deputy binary. PluginTypeBuiltin PluginType = "builtin" // PluginTypeExternal is a plugin discovered via PATH. PluginTypeExternal PluginType = "external" // PluginTypeRemote is a plugin accessed via gRPC/Connect. PluginTypeRemote PluginType = "remote" // PluginTypeSandboxed is a plugin running in a container. PluginTypeSandboxed PluginType = "sandboxed" )
type Registry ¶
type Registry struct {
// contains filtered or unexported fields
}
Registry manages agent plugin discovery and lifecycle.
func (*Registry) DiscoverExternal ¶
DiscoverExternal searches PATH for external agent plugins. External plugins are executables matching the pattern "deputy-plugin-<name>". This performs eager discovery - plugins are started and registered immediately.
func (*Registry) Entries ¶
func (r *Registry) Entries() []*PluginEntry
Entries returns all registered plugin entries.
func (*Registry) Get ¶
func (r *Registry) Get(name string) (agentv1connect.AgentPluginHandler, error)
Get retrieves a plugin handler by name.
func (*Registry) GetEntry ¶
func (r *Registry) GetEntry(name string) (*PluginEntry, error)
GetEntry retrieves a plugin entry by name.
func (*Registry) GetOrDiscover ¶
func (r *Registry) GetOrDiscover(ctx context.Context, name string) (agentv1connect.AgentPluginHandler, error)
GetOrDiscover retrieves a plugin by name, discovering it from PATH if not already registered. This enables natural usage like Get("gemini") which will auto-discover deputy-plugin-gemini.
func (*Registry) ListAvailable ¶
ListAvailable returns all available plugins: registered and discoverable from PATH. This combines registered plugin names with plugin names found in PATH.
func (*Registry) MustRegisterBuiltin ¶
func (r *Registry) MustRegisterBuiltin(name string, handler agentv1connect.AgentPluginHandler)
MustRegisterBuiltin registers a builtin plugin, panicking on error.
func (*Registry) MustRegisterSandboxed ¶
func (r *Registry) MustRegisterSandboxed(name string, opts SandboxOptions)
MustRegisterSandboxed registers a sandboxed plugin, panicking on error.
func (*Registry) Register ¶
func (r *Registry) Register(entry *PluginEntry) error
Register adds a plugin to the registry.
func (*Registry) RegisterBuiltin ¶
func (r *Registry) RegisterBuiltin(name string, handler agentv1connect.AgentPluginHandler) error
RegisterBuiltin registers a builtin plugin handler.
func (*Registry) RegisterRemote ¶
RegisterRemote registers a remote plugin at the given address.
func (*Registry) RegisterSandboxed ¶
func (r *Registry) RegisterSandboxed(name string, opts SandboxOptions) error
RegisterSandboxed registers a sandboxed plugin handler.
func (*Registry) Unregister ¶
Unregister removes a plugin from the registry.
type SandboxOptions ¶
type SandboxOptions struct {
// Runtime is the container runtime to use.
Runtime SandboxRuntime
// Image is the container image to run.
Image string
// NetworkMode controls network access ("none", "host", "bridge").
NetworkMode string
// ReadOnlyRoot makes the root filesystem read-only.
ReadOnlyRoot bool
// MemoryLimit is the memory limit (e.g., "512m", "2g").
MemoryLimit string
// CPULimit is the CPU limit (e.g., "1.0", "0.5").
CPULimit string
// Timeout is the maximum execution duration.
Timeout time.Duration
// WorkDir is the working directory to mount inside the container.
WorkDir string
// SocketPath is where to expose the plugin's gRPC socket.
SocketPath string
}
SandboxOptions configures sandboxed plugin execution.
type SandboxRuntime ¶
type SandboxRuntime string
SandboxRuntime identifies the container runtime for sandboxed execution.
const ( // RuntimeDocker uses Docker for sandboxing. RuntimeDocker SandboxRuntime = "docker" // RuntimeGVisor uses gVisor (runsc) for sandboxing. RuntimeGVisor SandboxRuntime = "gvisor" // RuntimePodman uses Podman for sandboxing. RuntimePodman SandboxRuntime = "podman" )
func AvailableRuntimes ¶
func AvailableRuntimes() []SandboxRuntime
AvailableRuntimes returns which container runtimes are available.
type SandboxedHandler ¶
type SandboxedHandler struct {
agentv1connect.UnimplementedAgentPluginHandler
// contains filtered or unexported fields
}
SandboxedHandler implements AgentPluginHandler by running agents in containers.
func NewSandboxedHandler ¶
func NewSandboxedHandler(name string, opts SandboxOptions) (*SandboxedHandler, error)
NewSandboxedHandler creates a sandboxed handler for running agents in containers.
func (*SandboxedHandler) Approve ¶
func (h *SandboxedHandler) Approve(ctx context.Context, req *connect.Request[agentv1.ApproveRequest]) (*connect.Response[agentv1.ApproveResponse], error)
Approve handles approval requests.
func (*SandboxedHandler) Cancel ¶
func (h *SandboxedHandler) Cancel(ctx context.Context, req *connect.Request[agentv1.CancelRequest]) (*connect.Response[agentv1.CancelResponse], error)
Cancel requests graceful termination.
func (*SandboxedHandler) Execute ¶
func (h *SandboxedHandler) Execute(ctx context.Context, req *connect.Request[agentv1.ExecuteRequest], stream *connect.ServerStream[agentv1.ExecuteEvent]) error
Execute runs the agent in a sandboxed container.
func (*SandboxedHandler) GetInfo ¶
func (h *SandboxedHandler) GetInfo(ctx context.Context, req *connect.Request[agentv1.GetInfoRequest]) (*connect.Response[agentv1.GetInfoResponse], error)
GetInfo returns metadata about the sandboxed plugin.
func (*SandboxedHandler) Resume ¶
func (h *SandboxedHandler) Resume(ctx context.Context, req *connect.Request[agentv1.ResumeRequest], stream *connect.ServerStream[agentv1.ExecuteEvent]) error
Resume continues a previous session.