Documentation
¶
Overview ¶
Package agents implements infrastructure adapters for AI agent integration.
The agents package provides concrete implementations of the AgentProvider and AgentRegistry ports defined in the domain layer, enabling workflow steps to invoke AI agents (Claude, Gemini, Codex, OpenCode, and OpenAI-compatible endpoints) for code generation, analysis, and decision-making tasks. Each provider wraps a CLI executor and handles model-specific invocation patterns, streaming output, and error mapping.
Architecture Role ¶
In the hexagonal architecture:
- Implements domain/ports.AgentProvider (per-provider adapters)
- Implements domain/ports.AgentRegistry (provider registration and lookup)
- Implements domain/ports.CLIExecutor (CLI command execution)
- Application layer orchestrates agent steps via these port interfaces
- Domain layer defines agent contracts without implementation coupling
All agent providers delegate CLI execution to an injected CLIExecutor, allowing test isolation via mock executors. The registry supports runtime provider registration and enables workflow steps to reference agents by name (e.g., "claude", "gemini").
Agent Providers ¶
## ClaudeProvider (claude_provider.go)
Anthropic Claude provider:
- Execute: Single-shot prompt execution via Claude CLI
- ExecuteConversation: Multi-turn conversation with context preservation
- Name: Returns "claude" for registry lookup
- Validate: Checks required options (model, temperature)
Supported models: claude-3-opus, claude-3-sonnet, claude-3-haiku, claude-2.1, claude-2
## GeminiProvider (gemini_provider.go)
Google Gemini provider:
- Execute: Single-shot prompt execution via Gemini CLI
- ExecuteConversation: Multi-turn conversation support
- Name: Returns "gemini"
- Validate: Checks model and API key configuration
## CodexProvider (codex_provider.go)
OpenAI Codex provider (code-focused GPT models):
- Execute: Single-shot code generation via OpenAI CLI
- ExecuteConversation: Not supported (returns error)
- Name: Returns "codex"
- Validate: Checks API key and model configuration
## OpenCodeProvider (opencode_provider.go)
Open-source code generation models (StarCoder, CodeLlama, etc.):
- Execute: Single-shot execution via custom CLI wrapper
- ExecuteConversation: Limited support (model-dependent)
- Name: Returns "opencode"
- Validate: Checks CLI tool availability
## OpenAICompatibleProvider (openai_compatible_provider.go)
HTTP adapter for any OpenAI-compatible API endpoint (Ollama, LM Studio, vLLM, etc.):
- Execute: Single-shot prompt via Chat Completions API
- ExecuteConversation: Multi-turn conversation with history
- Name: Returns "openai_compatible" for registry lookup
- Validate: Checks base_url and model configuration
Configuration via agent options: base_url, api_key, model, temperature. Falls back to OPENAI_BASE_URL / OPENAI_MODEL / OPENAI_API_KEY env vars.
Registry and Discovery ¶
## AgentRegistry (registry.go)
Provider registration and lookup:
- Register: Add provider by name (thread-safe)
- Get: Retrieve provider by name
- List: Enumerate registered provider names
- Has: Check if provider exists
- RegisterDefaults: Pre-populate with built-in providers
CLI Execution ¶
## ExecCLIExecutor (cli_executor.go)
External binary execution:
- Run: Execute command with streaming stdout/stderr, context cancellation
- Process cleanup: Kills descendant processes on cancellation
- Signal propagation: Forwards SIGINT/SIGTERM to child processes
Used by all agent providers to invoke CLI tools (claude, gemini, gpt, etc.).
Provider Options ¶
## Functional Options Pattern (options.go)
Provider configuration via option functions:
- WithClaudeExecutor: Inject custom executor for Claude provider
- WithGeminiExecutor: Inject custom executor for Gemini provider
- WithCodexExecutor: Inject custom executor for Codex provider
- WithOpenCodeExecutor: Inject custom executor for OpenCode provider
- WithHTTPClient: Inject custom HTTP client for OpenAICompatible provider
Each provider accepts zero or more options at construction time for dependency injection.
Index ¶
- type AgentRegistry
- type ClaudeProvider
- func (p *ClaudeProvider) Execute(ctx context.Context, prompt string, options map[string]any) (*workflow.AgentResult, error)
- func (p *ClaudeProvider) ExecuteConversation(ctx context.Context, state *workflow.ConversationState, prompt string, ...) (*workflow.ConversationResult, error)
- func (p *ClaudeProvider) Name() string
- func (p *ClaudeProvider) Validate() error
- type ClaudeProviderOption
- type CodexProvider
- func (p *CodexProvider) Execute(ctx context.Context, prompt string, options map[string]any) (*workflow.AgentResult, error)
- func (p *CodexProvider) ExecuteConversation(ctx context.Context, state *workflow.ConversationState, prompt string, ...) (*workflow.ConversationResult, error)
- func (p *CodexProvider) Name() string
- func (p *CodexProvider) Validate() error
- type CodexProviderOption
- type ExecCLIExecutor
- type GeminiProvider
- func (p *GeminiProvider) Execute(ctx context.Context, prompt string, options map[string]any) (*workflow.AgentResult, error)
- func (p *GeminiProvider) ExecuteConversation(ctx context.Context, state *workflow.ConversationState, prompt string, ...) (*workflow.ConversationResult, error)
- func (p *GeminiProvider) Name() string
- func (p *GeminiProvider) Validate() error
- type GeminiProviderOption
- type MockCall
- type MockProvider
- func (m *MockProvider) CallCount() int
- func (m *MockProvider) Calls() []MockCall
- func (m *MockProvider) ConversationCallCount() int
- func (m *MockProvider) Execute(ctx context.Context, prompt string, options map[string]any) (*workflow.AgentResult, error)
- func (m *MockProvider) ExecuteCallCount() int
- func (m *MockProvider) ExecuteConversation(ctx context.Context, state *workflow.ConversationState, prompt string, ...) (*workflow.ConversationResult, error)
- func (m *MockProvider) Name() string
- func (m *MockProvider) Reset()
- func (m *MockProvider) Validate() error
- func (m *MockProvider) WithConversationResponse(pattern string, result *workflow.ConversationResult) *MockProvider
- func (m *MockProvider) WithDefaultConversationResponse(result *workflow.ConversationResult) *MockProvider
- func (m *MockProvider) WithDefaultResponse(result *workflow.AgentResult) *MockProvider
- func (m *MockProvider) WithDelay(d time.Duration) *MockProvider
- func (m *MockProvider) WithResponse(pattern string, result *workflow.AgentResult) *MockProvider
- func (m *MockProvider) WithValidateError(err error) *MockProvider
- type OpenAICompatibleProvider
- func (p *OpenAICompatibleProvider) Execute(ctx context.Context, prompt string, options map[string]any) (*workflow.AgentResult, error)
- func (p *OpenAICompatibleProvider) ExecuteConversation(ctx context.Context, state *workflow.ConversationState, prompt string, ...) (*workflow.ConversationResult, error)
- func (p *OpenAICompatibleProvider) Name() string
- func (p *OpenAICompatibleProvider) Validate() error
- type OpenAICompatibleProviderOption
- type OpenCodeProvider
- func (p *OpenCodeProvider) Execute(ctx context.Context, prompt string, options map[string]any) (*workflow.AgentResult, error)
- func (p *OpenCodeProvider) ExecuteConversation(ctx context.Context, state *workflow.ConversationState, prompt string, ...) (*workflow.ConversationResult, error)
- func (p *OpenCodeProvider) Name() string
- func (p *OpenCodeProvider) Validate() error
- type OpenCodeProviderOption
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type AgentRegistry ¶
type AgentRegistry struct {
// contains filtered or unexported fields
}
AgentRegistry manages registered agent providers.
func NewAgentRegistry ¶
func NewAgentRegistry() *AgentRegistry
func (*AgentRegistry) Get ¶
func (r *AgentRegistry) Get(name string) (ports.AgentProvider, error)
func (*AgentRegistry) Has ¶
func (r *AgentRegistry) Has(name string) bool
func (*AgentRegistry) List ¶
func (r *AgentRegistry) List() []string
func (*AgentRegistry) Register ¶
func (r *AgentRegistry) Register(provider ports.AgentProvider) error
func (*AgentRegistry) RegisterDefaults ¶
func (r *AgentRegistry) RegisterDefaults() error
RegisterDefaults registers all default providers. It continues registering even if individual providers fail, collecting all errors and returning them aggregated.
type ClaudeProvider ¶
type ClaudeProvider struct {
// contains filtered or unexported fields
}
ClaudeProvider implements AgentProvider for Claude CLI. Invokes: claude -p "prompt" --output-format json
func NewClaudeProvider ¶
func NewClaudeProvider(l ...ports.Logger) *ClaudeProvider
func NewClaudeProviderWithOptions ¶
func NewClaudeProviderWithOptions(opts ...ClaudeProviderOption) *ClaudeProvider
func (*ClaudeProvider) Execute ¶
func (p *ClaudeProvider) Execute(ctx context.Context, prompt string, options map[string]any) (*workflow.AgentResult, error)
func (*ClaudeProvider) ExecuteConversation ¶
func (p *ClaudeProvider) ExecuteConversation(ctx context.Context, state *workflow.ConversationState, prompt string, options map[string]any) (*workflow.ConversationResult, error)
ExecuteConversation invokes the Claude CLI with conversation history for multi-turn interactions.
func (*ClaudeProvider) Name ¶
func (p *ClaudeProvider) Name() string
func (*ClaudeProvider) Validate ¶
func (p *ClaudeProvider) Validate() error
type ClaudeProviderOption ¶
type ClaudeProviderOption func(*ClaudeProvider)
func WithClaudeExecutor ¶
func WithClaudeExecutor(executor ports.CLIExecutor) ClaudeProviderOption
type CodexProvider ¶
type CodexProvider struct {
// contains filtered or unexported fields
}
CodexProvider implements AgentProvider for Codex CLI. Invokes: codex --prompt "prompt" --quiet
func NewCodexProvider ¶
func NewCodexProvider() *CodexProvider
func NewCodexProviderWithOptions ¶
func NewCodexProviderWithOptions(opts ...CodexProviderOption) *CodexProvider
func (*CodexProvider) Execute ¶
func (p *CodexProvider) Execute(ctx context.Context, prompt string, options map[string]any) (*workflow.AgentResult, error)
func (*CodexProvider) ExecuteConversation ¶
func (p *CodexProvider) ExecuteConversation(ctx context.Context, state *workflow.ConversationState, prompt string, options map[string]any) (*workflow.ConversationResult, error)
func (*CodexProvider) Name ¶
func (p *CodexProvider) Name() string
Name returns the provider identifier.
func (*CodexProvider) Validate ¶
func (p *CodexProvider) Validate() error
Validate checks if the Codex CLI is installed and accessible.
type CodexProviderOption ¶
type CodexProviderOption func(*CodexProvider)
func WithCodexExecutor ¶
func WithCodexExecutor(executor ports.CLIExecutor) CodexProviderOption
func WithCodexLogger ¶
func WithCodexLogger(l ports.Logger) CodexProviderOption
type ExecCLIExecutor ¶
type ExecCLIExecutor struct{}
ExecCLIExecutor implements CLIExecutor using os/exec for direct binary execution. Unlike shell execution via detected shell ($SHELL), this executes binaries directly without shell interpretation, making it suitable for invoking external CLI tools like claude, gemini, codex, etc.
func NewExecCLIExecutor ¶
func NewExecCLIExecutor() *ExecCLIExecutor
type GeminiProvider ¶
type GeminiProvider struct {
// contains filtered or unexported fields
}
GeminiProvider implements AgentProvider for Gemini CLI. Invokes: gemini -p "prompt"
func NewGeminiProvider ¶
func NewGeminiProvider() *GeminiProvider
func NewGeminiProviderWithOptions ¶
func NewGeminiProviderWithOptions(opts ...GeminiProviderOption) *GeminiProvider
func (*GeminiProvider) Execute ¶
func (p *GeminiProvider) Execute(ctx context.Context, prompt string, options map[string]any) (*workflow.AgentResult, error)
func (*GeminiProvider) ExecuteConversation ¶
func (p *GeminiProvider) ExecuteConversation(ctx context.Context, state *workflow.ConversationState, prompt string, options map[string]any) (*workflow.ConversationResult, error)
func (*GeminiProvider) Name ¶
func (p *GeminiProvider) Name() string
extractSessionID parses a session identifier from Gemini CLI output. Looks for a "Session: <id>" line and returns the trimmed ID. Returns empty string and error if not found (caller falls back to stateless).
func (*GeminiProvider) Validate ¶
func (p *GeminiProvider) Validate() error
type GeminiProviderOption ¶
type GeminiProviderOption func(*GeminiProvider)
func WithGeminiExecutor ¶
func WithGeminiExecutor(executor ports.CLIExecutor) GeminiProviderOption
type MockCall ¶
type MockCall struct {
Method string
Prompt string
Options map[string]any
State *workflow.ConversationState
}
MockCall records a call made to the mock provider.
type MockProvider ¶
type MockProvider struct {
// contains filtered or unexported fields
}
MockProvider provides deterministic responses for testing without external CLI calls. It implements ports.AgentProvider interface.
func NewMockProvider ¶
func NewMockProvider(name string) *MockProvider
NewMockProvider creates a new mock provider with the given name.
func (*MockProvider) CallCount ¶
func (m *MockProvider) CallCount() int
CallCount returns the number of calls made.
func (*MockProvider) Calls ¶
func (m *MockProvider) Calls() []MockCall
Calls returns all recorded calls.
func (*MockProvider) ConversationCallCount ¶
func (m *MockProvider) ConversationCallCount() int
ConversationCallCount returns the number of ExecuteConversation calls.
func (*MockProvider) Execute ¶
func (m *MockProvider) Execute(ctx context.Context, prompt string, options map[string]any) (*workflow.AgentResult, error)
Execute implements ports.AgentProvider.
func (*MockProvider) ExecuteCallCount ¶
func (m *MockProvider) ExecuteCallCount() int
ExecuteCallCount returns the number of Execute calls.
func (*MockProvider) ExecuteConversation ¶
func (m *MockProvider) ExecuteConversation(ctx context.Context, state *workflow.ConversationState, prompt string, options map[string]any) (*workflow.ConversationResult, error)
ExecuteConversation implements ports.AgentProvider.
func (*MockProvider) Name ¶
func (m *MockProvider) Name() string
Name implements ports.AgentProvider.
func (*MockProvider) Validate ¶
func (m *MockProvider) Validate() error
Validate implements ports.AgentProvider.
func (*MockProvider) WithConversationResponse ¶
func (m *MockProvider) WithConversationResponse(pattern string, result *workflow.ConversationResult) *MockProvider
WithConversationResponse configures a conversation response for prompts containing the pattern.
func (*MockProvider) WithDefaultConversationResponse ¶
func (m *MockProvider) WithDefaultConversationResponse(result *workflow.ConversationResult) *MockProvider
WithDefaultConversationResponse sets the default conversation response.
func (*MockProvider) WithDefaultResponse ¶
func (m *MockProvider) WithDefaultResponse(result *workflow.AgentResult) *MockProvider
WithDefaultResponse sets the default response when no pattern matches.
func (*MockProvider) WithDelay ¶
func (m *MockProvider) WithDelay(d time.Duration) *MockProvider
WithDelay sets the simulated processing delay.
func (*MockProvider) WithResponse ¶
func (m *MockProvider) WithResponse(pattern string, result *workflow.AgentResult) *MockProvider
WithResponse configures a response for prompts containing the given pattern.
func (*MockProvider) WithValidateError ¶
func (m *MockProvider) WithValidateError(err error) *MockProvider
WithValidateError configures the provider to return an error on Validate().
type OpenAICompatibleProvider ¶
type OpenAICompatibleProvider struct {
// contains filtered or unexported fields
}
OpenAICompatibleProvider implements AgentProvider via the Chat Completions HTTP API. Compatible with OpenAI, Ollama, vLLM, Groq, and any OpenAI-compatible backend.
func NewOpenAICompatibleProvider ¶
func NewOpenAICompatibleProvider(opts ...OpenAICompatibleProviderOption) *OpenAICompatibleProvider
func (*OpenAICompatibleProvider) Execute ¶
func (p *OpenAICompatibleProvider) Execute(ctx context.Context, prompt string, options map[string]any) (*workflow.AgentResult, error)
func (*OpenAICompatibleProvider) ExecuteConversation ¶
func (p *OpenAICompatibleProvider) ExecuteConversation(ctx context.Context, state *workflow.ConversationState, prompt string, options map[string]any) (*workflow.ConversationResult, error)
func (*OpenAICompatibleProvider) Name ¶
func (p *OpenAICompatibleProvider) Name() string
func (*OpenAICompatibleProvider) Validate ¶
func (p *OpenAICompatibleProvider) Validate() error
type OpenAICompatibleProviderOption ¶
type OpenAICompatibleProviderOption func(*OpenAICompatibleProvider)
func WithHTTPClient ¶
func WithHTTPClient(client *httpx.Client) OpenAICompatibleProviderOption
type OpenCodeProvider ¶
type OpenCodeProvider struct {
// contains filtered or unexported fields
}
OpenCodeProvider implements AgentProvider for OpenCode CLI. Invokes: opencode run "prompt"
func NewOpenCodeProvider ¶
func NewOpenCodeProvider() *OpenCodeProvider
NewOpenCodeProvider creates a new OpenCodeProvider. If no executor is provided, ExecCLIExecutor is used by default.
func NewOpenCodeProviderWithOptions ¶
func NewOpenCodeProviderWithOptions(opts ...OpenCodeProviderOption) *OpenCodeProvider
NewOpenCodeProviderWithOptions creates a new OpenCodeProvider with functional options.
func (*OpenCodeProvider) Execute ¶
func (p *OpenCodeProvider) Execute(ctx context.Context, prompt string, options map[string]any) (*workflow.AgentResult, error)
Execute invokes the OpenCode CLI with the given prompt and options.
func (*OpenCodeProvider) ExecuteConversation ¶
func (p *OpenCodeProvider) ExecuteConversation(ctx context.Context, state *workflow.ConversationState, prompt string, options map[string]any) (*workflow.ConversationResult, error)
ExecuteConversation invokes the OpenCode CLI with conversation history for multi-turn interactions.
func (*OpenCodeProvider) Name ¶
func (p *OpenCodeProvider) Name() string
Name returns the provider identifier.
func (*OpenCodeProvider) Validate ¶
func (p *OpenCodeProvider) Validate() error
Validate checks if the OpenCode CLI is installed and accessible.
type OpenCodeProviderOption ¶
type OpenCodeProviderOption func(*OpenCodeProvider)
func WithOpenCodeExecutor ¶
func WithOpenCodeExecutor(executor ports.CLIExecutor) OpenCodeProviderOption