agent

package
v0.31.0 Latest Latest
Warning

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

Go to latest
Published: Mar 4, 2026 License: MIT Imports: 14 Imported by: 0

README

Agent Package

The agent package provides an abstraction layer for managing communication with OpenCode in agent mode, with a focus on MCP (Model Context Protocol) server configuration.

Overview

This package enables programmatic control of AI agents through the OpenCode SDK, providing structured configuration for:

  • MCP Servers - Local (stdio) and remote (HTTP/SSE) server configuration
  • Permissions - Fine-grained control over agent capabilities
  • Sessions - Conversation context management
  • Events - Asynchronous response handling
  • Prompts - Structured message construction

Installation

go get github.com/sevigo/goframe/agent

Quick Start

package main

import (
    "context"
    "fmt"
    "log/slog"
    "os"

    "github.com/sevigo/goframe/agent"
)

func main() {
    ctx := context.Background()

    // Configure MCP servers
    mcpRegistry := agent.NewMCPRegistry(
        agent.LocalMCPServer("filesystem",
            []string{"mcp-filesystem", "/path/to/repo"},
            agent.WithEnv(map[string]string{"LOG_LEVEL": "debug"}),
        ),
        agent.RemoteMCPServer("brave-search",
            "https://mcp.brave.com/search",
            agent.WithHeaders(map[string]string{"Authorization": "Bearer token"}),
        ),
    )

    // Configure permissions
    permissions := agent.NewPermissions().
        AllowBash("go test", "go build").
        AllowEdit().
        DenyWebfetch().
        Build()

    // Create agent
    ag, err := agent.New(
        agent.WithModel("ollama/llama3"),
        agent.WithMCPRegistry(mcpRegistry),
        agent.WithPermissions(permissions),
        agent.WithLogger(slog.New(slog.NewTextHandler(os.Stderr, nil))),
    )
    if err != nil {
        panic(err)
    }

    // Create session
    session, err := ag.NewSession(ctx, agent.WithTitle("Code Review"))
    if err != nil {
        panic(err)
    }
    defer ag.DeleteSession(ctx, session.ID)

    // Send prompt
    response, err := session.Prompt(ctx, "Explain this code")
    if err != nil {
        panic(err)
    }

    fmt.Printf("Response: %s\n", response.Content)
}

Testing Locally

Prerequisites
  • Docker and Docker Compose
  • Go 1.21+
  • (Optional) API key for cloud models
Step 1: Start Docker Services
# Start all services (Qdrant, Ollama, OpenCode)
docker-compose up -d

# Check services are running
docker-compose ps

Expected output:

NAME        IMAGE                              STATUS
qdrant      qdrant/qdrant:v1.16.0              running
ollama      ollama/ollama:latest               running
opencode    ghcr.io/anomalyco/opencode:latest  running
Step 2: Pull Ollama Models

For local inference, pull a model:

# Pull Qwen 3.5 or qwen2.5-coder:3b
docker-compose exec ollama ollama pull qwen3.5:2b
# docker-compose exec ollama ollama pull qwen2.5-coder:3b

# Pull cloud version of GLM-5
docker-compose exec ollama ollama pull glm-5:cloud

# List available models
docker-compose exec ollama ollama list
Step 3: Set Environment Variables
# OpenCode server URL (default: http://localhost:3000)
export OPENCODE_BASE_URL=http://localhost:3000

# For cloud models (optional)
export OPENCODE_API_KEY=your-api-key

# Model to use
export OPENCODE_MODEL=ollama/qwen3.5:2b
# export OPENCODE_MODEL=ollama/qwen2.5-coder:3b
# export OPENCODE_MODEL=ollama/glm-5:cloud
Step 4: Run Tests
# Run all tests
make test

# Run only agent package tests
go test ./agent/... -v

# Run integration tests (requires OpenCode running)
go test ./agent/... -run Integration -v

# Run with race detector
make test-race
Step 5: Run the Example
# Navigate to examples
cd agent/examples/basic

# Run the example
go run main.go
Step 6: Run Linter
make lint
Manual API Testing

Check OpenCode API directly:

# Health check
curl http://localhost:3000/health

# List agents
curl http://localhost:3000/agents

# Create a session
curl -X POST http://localhost:3000/session \
  -H "Content-Type: application/json" \
  -d '{"directory": "."}'

# Send a prompt (replace SESSION_ID from previous response)
curl -X POST http://localhost:3000/session/SESSION_ID/message \
  -H "Content-Type: application/json" \
  -d '{"parts": [{"type": "text", "text": "Hello"}]}'
Quick Test Script

Create test-agent.sh:

#!/bin/bash
set -e

echo "=== Starting services ==="
docker-compose up -d

echo "=== Waiting for Ollama ==="
sleep 5

echo "=== Pulling model ==="
docker-compose exec -T ollama ollama pull llama3 || true

echo "=== Running tests ==="
go test ./agent/... -v

echo "=== Running linter ==="
make lint

echo "=== Running example ==="
cd agent/examples/basic && go run main.go

echo "=== Done ==="

Make it executable:

chmod +x test-agent.sh
./test-agent.sh

Core Components

MCP Server Management
// Local MCP server (stdio transport)
localServer := agent.LocalMCPServer("filesystem",
    []string{"mcp-filesystem", "/path/to/repo"},
    agent.WithEnv(map[string]string{"LOG_LEVEL": "debug"}),
    agent.WithEnabled(true),
)

// Remote MCP server (HTTP/SSE transport)
remoteServer := agent.RemoteMCPServer("brave-search",
    "https://mcp.brave.com/search",
    agent.WithHeaders(map[string]string{"Authorization": "Bearer token"}),
    agent.WithEnabled(true),
)

// Create registry
registry := agent.NewMCPRegistry(localServer, remoteServer)

// Add to registry
registry.Add(agent.LocalMCPServer("tools", []string{"mcp-tools"}))

// List servers
for _, s := range registry.List() {
    fmt.Printf("%s (%s): enabled=%v\n", s.Name, s.Type, s.Enabled)
}
Permissions
// Build permissions fluently
perms := agent.NewPermissions().
    AllowBash("go test", "go build", "go run").
    AskBash("rm *", "git push").
    DenyBash("sudo *").
    AllowEdit().
    DenyWebfetch().
    Build()

// Use with agent
ag, _ := agent.New(agent.WithPermissions(perms))
Session Management
// Create new session
session, _ := ag.NewSession(ctx,
    agent.WithTitle("Code Review"),
    agent.WithDirectory("/path/to/project"),
)

// Send prompt
response, _ := session.Prompt(ctx, "Review main.go")

// Stream response
events, _ := ag.Stream(ctx, "Explain the architecture")
for event := range events {
    switch event.Type {
    case agent.EventTypeComplete:
        resp := event.Data.(agent.Response)
        fmt.Println(resp.Content)
    case agent.EventTypeError:
        log.Printf("Error: %v", event.Error)
    }
}

// List sessions
sessions, _ := ag.ListSessions(ctx)

// Delete session
ag.DeleteSession(ctx, session.ID)
Prompt Building
// Using prompt builder
builder := agent.NewPromptBuilder().
    AddText("Review this file:\n").
    AddFileFromContent("main.go", content, "text/x-go").
    AddText("\nFocus on error handling.")

config := builder.Build(
    agent.WithContext("Code review context"),
    agent.WithTemperature(0.7),
)

response, _ := session.Prompt(ctx, "", agent.WithParts(config.Parts...))

// Using prompt options directly
response, _ := session.Prompt(ctx,
    "Analyze these files",
    agent.WithParts(
        agent.FileFromContent("main.go", content1, "text/x-go"),
        agent.FileFromContent("utils.go", content2, "text/x-go"),
    ),
    agent.WithContext("Focus on performance"),
)
Feedback Loop

The feedback loop enables iterative implementation with automated code review:

Basic Usage
fl := agent.NewFeedbackLoop(ag, session,
    agent.WithMaxRetries(3),
)

result, err := fl.ImplementWithReview(ctx, agent.ImplementRequest{
    Task: "Write a function that validates email addresses",
    Context: "This is for a user registration API",
    Constraints: []string{
        "Handle edge cases",
        "Include documentation",
        "Follow Go naming conventions",
    },
})

if err != nil {
    // Could be ErrMaxRetries or other error
    fmt.Printf("Implementation failed: %v\n", err)
} else {
    fmt.Printf("Implementation: %s\n", result.Implementation)
}
Custom Review Handler
reviewHandler := func(ctx context.Context, session *agent.Session, implementation string) (*agent.ReviewResult, error) {
    // Custom review logic
    score := calculateScore(implementation)
    
    var feedback strings.Builder
    if !hasErrorHandling(implementation) {
        feedback.WriteString("- Missing error handling\n")
        score -= 20
    }
    if !hasDocumentation(implementation) {
        feedback.WriteString("- Missing documentation\n")
        score -= 15
    }
    
    return &agent.ReviewResult{
        Approved: score >= 70,
        Feedback: feedback.String(),
        Score:    float64(score),
    }, nil
}

fl := agent.NewFeedbackLoop(ag, session,
    agent.WithMaxRetries(3),
    agent.WithReviewHandler(reviewHandler),
)
MCP-Based Code Review and PR Creation
// Review using MCP tools
reviewHandler := func(ctx context.Context, session *agent.Session, implementation string) (*agent.ReviewResult, error) {
    reviewPrompt := fmt.Sprintf(
        "Use the code_review MCP tool to analyze:\n\n%s\n\n"+
            "Check for quality, security, and best practices.\n"+
            "Return APPROVE if score >= 70, otherwise REJECT with feedback.",
        implementation,
    )
    
    response, err := session.Prompt(ctx, reviewPrompt)
    if err != nil {
        return nil, err
    }
    
    approved := strings.Contains(strings.ToUpper(response.Content), "APPROVE")
    return &agent.ReviewResult{
        Approved: approved,
        Feedback: response.Content,
        Score:    score,
    }, nil
}

// Create PR on approval
prHandler := func(ctx context.Context, session *agent.Session, implementation string, review *agent.ReviewResult) error {
    if !review.Approved {
        return nil
    }
    
    prPrompt := fmt.Sprintf(
        "Use the github MCP tool to create a pull request:\n"+
            "Title: Implement user validation\n"+
            "Branch: feature/user-validation\n\n%s",
        implementation,
    )
    
    _, err := session.Prompt(ctx, prPrompt)
    return err
}

fl := agent.NewFeedbackLoop(ag, session,
    agent.WithMaxRetries(3),
    agent.WithReviewHandler(reviewHandler),
    agent.WithPRHandler(prHandler),
)
Available Options
fl := agent.NewFeedbackLoop(ag, session,
    agent.WithMaxRetries(5),                    // Max implementation attempts
    agent.WithReviewTool("code_review"),        // MCP tool for review (alternative to handler)
    agent.WithPRTool("create_pr"),              // MCP tool for PR creation (alternative to handler)
    agent.WithReviewHandler(customHandler),     // Custom review function
    agent.WithPRHandler(customPRHandler),       // Custom PR creation function
)

See examples/feedback-loop/ and examples/mcp-feedback/ for complete examples.

Event Handling
// Create event handler
handler := agent.NewEventHandler()
handler.OnTextPart(func(ctx context.Context, text string) error {
    fmt.Print(text)
    return nil
})
handler.OnError(func(ctx context.Context, err error) error {
    log.Printf("Error: %v", err)
    return nil
})
handler.OnComplete(func(ctx context.Context, resp agent.Response) error {
    fmt.Printf("\nTokens: input=%.0f, output=%.0f\n",
        resp.Tokens.Input, resp.Tokens.Output)
    return nil
})

// Attach to agent
ag, _ := agent.New(
    agent.WithModel("ollama/llama3"),
    agent.WithEventHandlers(handler.Handle),
)
Configuration Files
// Load from opencode.json
config, _ := agent.LoadConfigFromDir(".")

// Create config programmatically
config := agent.NewConfigBuilder().
    WithModel("anthropic/claude-3-5-sonnet").
    WithSmallModel("anthropic/claude-3-haiku").
    AddLocalMCP("filesystem", []string{"mcp-filesystem"}, nil).
    WithPermissions(agent.NewPermissions().AllowEdit().Build()).
    Build()

// Save to opencode.json
agent.SaveConfig(config, "./opencode.json")

Docker Compose Services

The docker-compose.yml includes:

Service Image Port Purpose
qdrant qdrant/qdrant:v1.16.0 6333, 6334 Vector database
ollama ollama/ollama:latest 11434 Local LLM inference
opencode ghcr.io/anomalyco/opencode:latest 3000 Agent API server
Environment Variables
Variable Default Description
OPENCODE_API_KEY (empty) API key for cloud providers
OPENCODE_MODEL ollama/glm-5:cloud Default model to use
OPENCODE_LOG_LEVEL info Logging level
OLLAMA_HOST ollama:11434 Ollama connection (internal)

API Reference

Full API documentation is available at pkg.go.dev.

Troubleshooting

# View logs
docker-compose logs -f opencode
docker-compose logs -f ollama

# Restart services
docker-compose restart

# Clean up everything
docker-compose down -v

# Check service health
docker-compose ps
docker-compose exec ollama ollama list
curl http://localhost:3000/health

Examples

See examples/ directory for complete working examples:

Basic Example (examples/basic/)

Demonstrates core features:

  • MCP server configuration
  • Session management
  • Simple prompts and streaming
  • Prompt builder with files
  • Configuration save/load
go run ./agent/examples/basic/main.go
Feedback Loop Example (examples/feedback-loop/)

Shows the feedback loop pattern:

  • Basic iterative implementation
  • Custom review handlers with scoring
  • Automatic PR creation on approval
go run ./agent/examples/feedback-loop/main.go
MCP Feedback Example (examples/mcp-feedback/)

Advanced feedback loop with MCP tools:

  • MCP-based code review using code_review tool
  • Automated PR creation using github MCP tool
  • Multi-stage implementation pipeline
  • Custom review criteria with weights
go run ./agent/examples/mcp-feedback/main.go

License

MIT License - see LICENSE for details.

Documentation

Overview

Package agent provides an abstraction layer for managing communication with OpenCode in agent mode, with a focus on MCP (Model Context Protocol) server configuration.

The agent package enables programmatic control of AI agents through the OpenCode SDK, providing structured configuration for MCP servers, permissions, sessions, and prompts.

Overview

The package is built around several core concepts:

  • Agent: The main entry point for interacting with OpenCode
  • MCP (Model Context Protocol): Server configuration for tool access
  • Session: Conversation context management
  • Permission: Fine-grained control over agent capabilities
  • Prompt: Structured message construction
  • Event: Asynchronous response handling

MCP Server Configuration

MCP servers are the primary focus of this package. They enable agents to access external tools and resources. Two transport types are supported:

Local (stdio) servers run as subprocesses:

server := agent.LocalMCPServer("filesystem",
    []string{"mcp-filesystem", "/path/to/repo"},
    agent.WithEnv(map[string]string{"LOG_LEVEL": "debug"}),
)

Remote (HTTP/SSE) servers connect over HTTP:

server := agent.RemoteMCPServer("brave-search",
    "https://mcp.brave.com/search",
    agent.WithHeaders(map[string]string{"Authorization": "Bearer token"}),
)

Servers are managed through an MCPRegistry:

registry := agent.NewMCPRegistry(server1, server2)
agent, _ := agent.New(
    agent.WithMCPRegistry(registry),
    agent.WithModel("anthropic/claude-3-5-sonnet"),
)

Permissions

Permissions control what actions agents can take. Use the PermissionBuilder for fluent configuration:

perms := agent.NewPermissions().
    AllowBash("go test", "go build").
    AskBash("rm *").
    AllowEdit().
    DenyWebfetch().
    Build()

agent, _ := agent.New(agent.WithPermissions(perms))

Sessions and Prompts

Agents operate within sessions that maintain conversation context:

session, _ := agent.NewSession(ctx, agent.WithTitle("Code Review"))
defer agent.DeleteSession(ctx, session.ID)

response, _ := session.Prompt(ctx, "Explain this code")

Prompts can include files and other content:

response, _ := session.Prompt(ctx,
    "Compare these files",
    agent.WithFiles("main.go", "utils.go"),
    agent.WithTemperature(0.7),
)

Event Streaming

For async responses, use streaming:

events, _ := agent.Stream(ctx, "Write a haiku")
for event := range events {
    switch event.Type {
    case agent.EventTypeComplete:
        // Handle completion
    case agent.EventTypeError:
        // Handle error
    }
}

Configuration Files

Load configuration from opencode.json files:

config, err := agent.LoadConfigFromDir(".")
agent, _ := agent.New(
    agent.WithModel(config.Model),
    agent.WithMCPRegistry(agent.NewMCPRegistry(config.GetMCPServers()...)),
)

Thread Safety

The Agent type is safe for concurrent use. All exported methods handle their own synchronization internally.

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrNilClient is returned when a nil OpenCode client is provided
	ErrNilClient = errors.New("client cannot be nil")
	// ErrNoModel is returned when no model is specified
	ErrNoModel = errors.New("no model specified")
	// ErrInvalidModel is returned when the model format is invalid
	ErrInvalidModel = errors.New("invalid model format, expected provider/model")
	// ErrSessionNotFound is returned when a session cannot be found
	ErrSessionNotFound = errors.New("session not found")
	// ErrSessionAborted is returned when a session is aborted
	ErrSessionAborted = errors.New("session was aborted")
	// ErrNoMCPServers is returned when no MCP servers are configured
	ErrNoMCPServers = errors.New("no MCP servers configured")
	// ErrMCPServerExists is returned when trying to add a duplicate MCP server
	ErrMCPServerExists = errors.New("MCP server already exists")
	// ErrMCPServerNotFound is returned when an MCP server cannot be found
	ErrMCPServerNotFound = errors.New("MCP server not found")
	// ErrInvalidMCPConfig is returned when MCP server configuration is invalid
	ErrInvalidMCPConfig = errors.New("invalid MCP server configuration")
	// ErrPermissionDenied is returned when an action is denied by permissions
	ErrPermissionDenied = errors.New("permission denied")
	// ErrPromptFailed is returned when a prompt operation fails
	ErrPromptFailed = errors.New("prompt failed")
	// ErrStreamFailed is returned when a stream operation fails
	ErrStreamFailed = errors.New("stream failed")
)

Error types for the agent package

View Source
var (
	ErrReviewRejected  = errors.New("review rejected")
	ErrMaxRetries      = errors.New("max retries exceeded")
	ErrToolNotFound    = errors.New("tool not found")
	ErrReviewFailed    = errors.New("review failed")
	ErrExecutionFailed = errors.New("execution failed")
)
View Source
var (
	ErrPathTraversal = errors.New("path traversal detected: path outside working directory")
	ErrEmptyPath     = errors.New("empty path provided")
)

Functions

func SaveConfig

func SaveConfig(config *Config, path string) error

func ValidatePath

func ValidatePath(path, workingDir string) (string, error)

Types

type Agent

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

func New

func New(opts ...Option) (*Agent, error)

func (*Agent) AddMCPServer

func (a *Agent) AddMCPServer(ctx context.Context, server *MCPServer) error

func (*Agent) Ask

func (a *Agent) Ask(ctx context.Context, prompt string, opts ...PromptOption) (*Response, error)

Ask sends a one-off prompt and returns the response. Note: This creates a new session for each call. For multiple prompts, use NewSession and session.Prompt instead to reuse the session.

func (*Agent) DeleteSession

func (a *Agent) DeleteSession(ctx context.Context, id string) error

func (*Agent) GetClient

func (a *Agent) GetClient() *opencode.Client

func (*Agent) GetConfig

func (a *Agent) GetConfig() *Config

func (*Agent) GetEventHandler

func (a *Agent) GetEventHandler() *EventHandler

func (*Agent) GetSession

func (a *Agent) GetSession(ctx context.Context, id string) (*Session, error)

func (*Agent) ListMCPServers

func (a *Agent) ListMCPServers(ctx context.Context) []*MCPServer

func (*Agent) ListSessions

func (a *Agent) ListSessions(ctx context.Context) ([]*Session, error)

func (*Agent) NewSession

func (a *Agent) NewSession(ctx context.Context, opts ...SessionOption) (*Session, error)

func (*Agent) RemoveMCPServer

func (a *Agent) RemoveMCPServer(ctx context.Context, name string)

func (*Agent) Stream

func (a *Agent) Stream(ctx context.Context, prompt string, opts ...PromptOption) (<-chan Event, error)

Stream sends a one-off prompt and returns a channel for streaming responses. Note: This creates a new session for each call. For multiple prompts, use NewSession and session.PromptStream instead to reuse the session.

type AgentConfig

type AgentConfig struct {
	Description string            `json:"description"`
	Prompt      string            `json:"prompt"`
	Model       string            `json:"model"`
	Temperature *float64          `json:"temperature,omitempty"`
	TopP        *float64          `json:"top_p,omitempty"`
	Mode        AgentMode         `json:"mode"`
	Permissions *PermissionConfig `json:"permissions,omitempty"`
	Tools       map[string]bool   `json:"tools,omitempty"`
}

type AgentError

type AgentError struct {
	// Op is the operation that failed
	Op string
	// Session is the session ID if applicable
	Session string
	// Err is the underlying error
	Err error
	// Details contains additional error context
	Details map[string]interface{}
}

AgentError represents an error from agent operations

func (*AgentError) Error

func (e *AgentError) Error() string

Error implements the error interface

func (*AgentError) Unwrap

func (e *AgentError) Unwrap() error

Unwrap returns the underlying error

type AgentMode

type AgentMode string
const (
	AgentModeSubagent AgentMode = "subagent"
	AgentModePrimary  AgentMode = "primary"
	AgentModeAll      AgentMode = "all"
)

type AgentRefPart

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

func (*AgentRefPart) ToInput

type AgentType

type AgentType string
const (
	AgentTypeBuild   AgentType = "build"
	AgentTypePlan    AgentType = "plan"
	AgentTypeGeneral AgentType = "general"
)

type Config

type Config struct {
	Model       string
	SmallModel  string
	AgentType   AgentType
	Permissions *PermissionConfig
	Hooks       *HookConfig
	WorkingDir  string

	Tools map[string]bool
	// contains filtered or unexported fields
}

func LoadConfig

func LoadConfig(path string) (*Config, error)

func LoadConfigFromDir

func LoadConfigFromDir(dir string) (*Config, error)

func (*Config) GetAgents

func (c *Config) GetAgents() map[string]AgentConfig

func (*Config) GetMCPServers

func (c *Config) GetMCPServers() []*MCPServer

type ConfigBuilder

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

func NewConfigBuilder

func NewConfigBuilder() *ConfigBuilder

func (*ConfigBuilder) AddLocalMCP

func (b *ConfigBuilder) AddLocalMCP(name string, command []string, env map[string]string) *ConfigBuilder

func (*ConfigBuilder) AddRemoteMCP

func (b *ConfigBuilder) AddRemoteMCP(name string, url string, headers map[string]string) *ConfigBuilder

func (*ConfigBuilder) Build

func (b *ConfigBuilder) Build() *Config

func (*ConfigBuilder) WithAgent

func (b *ConfigBuilder) WithAgent(name string, config AgentConfig) *ConfigBuilder

func (*ConfigBuilder) WithModel

func (b *ConfigBuilder) WithModel(model string) *ConfigBuilder

func (*ConfigBuilder) WithPermissions

func (b *ConfigBuilder) WithPermissions(perm *PermissionConfig) *ConfigBuilder

func (*ConfigBuilder) WithSmallModel

func (b *ConfigBuilder) WithSmallModel(model string) *ConfigBuilder

type Event

type Event struct {
	Type      EventType
	SessionID string
	MessageID string
	PartID    string
	Data      interface{}
	Error     error
}

type EventHandler

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

func NewEventHandler

func NewEventHandler() *EventHandler

func (*EventHandler) Clear

func (h *EventHandler) Clear(eventType EventType)

func (*EventHandler) ClearAll

func (h *EventHandler) ClearAll()

func (*EventHandler) Handle

func (h *EventHandler) Handle(ctx context.Context, event Event) error

func (*EventHandler) On

func (h *EventHandler) On(eventType EventType, handler EventHandlerFunc)

func (*EventHandler) OnComplete

func (h *EventHandler) OnComplete(handler func(ctx context.Context, resp Response) error)

func (*EventHandler) OnError

func (h *EventHandler) OnError(handler func(ctx context.Context, err error) error)

func (*EventHandler) OnFile

func (h *EventHandler) OnFile(handler func(ctx context.Context, file FileInfo) error)

func (*EventHandler) OnMessage

func (h *EventHandler) OnMessage(handler func(ctx context.Context, msg Message) error)

func (*EventHandler) OnPermission

func (h *EventHandler) OnPermission(handler func(ctx context.Context, req PermissionRequest) error)

func (*EventHandler) OnTextPart

func (h *EventHandler) OnTextPart(handler func(ctx context.Context, text string) error)

func (*EventHandler) OnToolCall

func (h *EventHandler) OnToolCall(handler func(ctx context.Context, tool ToolCall) error)

type EventHandlerFunc

type EventHandlerFunc func(ctx context.Context, event Event) error

type EventType

type EventType string
const (
	EventTypeMessage    EventType = "message"
	EventTypePart       EventType = "part"
	EventTypeTool       EventType = "tool"
	EventTypeFile       EventType = "file"
	EventTypeError      EventType = "error"
	EventTypeComplete   EventType = "complete"
	EventTypePermission EventType = "permission"
)

type ExecutionResult

type ExecutionResult struct {
	Success      bool
	Output       string
	FilesChanged []string
	Error        error
}

type FeedbackLoop

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

func NewFeedbackLoop

func NewFeedbackLoop(agent *Agent, session *Session, opts ...FeedbackLoopOption) *FeedbackLoop

func (*FeedbackLoop) ImplementWithReview

func (fl *FeedbackLoop) ImplementWithReview(ctx context.Context, req ImplementRequest) (*ImplementResult, error)

type FeedbackLoopOption

type FeedbackLoopOption func(*FeedbackLoop)

func WithMaxRetries

func WithMaxRetries(retries int) FeedbackLoopOption

func WithPRHandler

func WithPRHandler(handler PRHandler) FeedbackLoopOption

func WithPRTool

func WithPRTool(tool string) FeedbackLoopOption

func WithReviewHandler

func WithReviewHandler(handler ReviewHandler) FeedbackLoopOption

func WithReviewTool

func WithReviewTool(tool string) FeedbackLoopOption

type FileInfo

type FileInfo struct {
	Path     string
	Content  []byte
	MimeType string
}

type FilePart

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

func (*FilePart) GetContent

func (p *FilePart) GetContent() ([]byte, error)

func (*FilePart) ToInput

type HookConfig

type HookConfig struct {
	OnSessionComplete []HookFunc
	OnFileEdited      []HookFunc
}

type HookEvent

type HookEvent struct {
	Type      string
	SessionID string
	Data      map[string]interface{}
}

type HookFunc

type HookFunc func(ctx context.Context, event HookEvent) error

type ImplementRequest

type ImplementRequest struct {
	Task        string
	Context     string
	Files       []string
	Constraints []string
}

type ImplementResult

type ImplementResult struct {
	Implementation string
	Response       *Response
	FilesCreated   []string
	FilesModified  []string
}

type MCPError

type MCPError struct {
	// Server is the name of the MCP server
	Server string
	// Err is the underlying error
	Err error
	// Details contains additional error context
	Details map[string]interface{}
}

MCPError represents an error from MCP server operations

func (*MCPError) Error

func (e *MCPError) Error() string

Error implements the error interface

func (*MCPError) Unwrap

func (e *MCPError) Unwrap() error

Unwrap returns the underlying error

type MCPOption

type MCPOption func(*MCPServer)

MCPOption configures an MCP server

func WithDisabled

func WithDisabled() MCPOption

WithDisabled marks the server as disabled

func WithEnabled

func WithEnabled(enabled bool) MCPOption

WithEnabled sets whether the server is enabled

func WithEnv

func WithEnv(env map[string]string) MCPOption

WithEnv sets environment variables for local MCP servers

func WithHeaders

func WithHeaders(headers map[string]string) MCPOption

WithHeaders sets HTTP headers for remote MCP servers

type MCPRegistry

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

MCPRegistry manages a collection of MCP servers

func NewMCPRegistry

func NewMCPRegistry(servers ...*MCPServer) *MCPRegistry

NewMCPRegistry creates a new registry with the given servers

func (*MCPRegistry) Add

func (r *MCPRegistry) Add(server *MCPServer) error

Add adds an MCP server to the registry

func (*MCPRegistry) Clear

func (r *MCPRegistry) Clear()

Clear removes all servers from the registry

func (*MCPRegistry) Count

func (r *MCPRegistry) Count() int

Count returns the number of servers in the registry

func (*MCPRegistry) Get

func (r *MCPRegistry) Get(name string) (*MCPServer, bool)

Get retrieves an MCP server by name

func (*MCPRegistry) List

func (r *MCPRegistry) List() []*MCPServer

List returns all MCP servers in the registry

func (*MCPRegistry) Merge

func (r *MCPRegistry) Merge(other *MCPRegistry) error

Merge combines another registry into this one

func (*MCPRegistry) Remove

func (r *MCPRegistry) Remove(name string)

Remove removes an MCP server from the registry

func (*MCPRegistry) ToConfigMap

func (r *MCPRegistry) ToConfigMap() map[string]opencode.ConfigMcp

ToConfigMap converts all servers to the OpenCode SDK configuration format

type MCPServer

type MCPServer struct {
	// Name is the unique identifier for the server
	Name string `json:"name"`
	// Type specifies whether this is a local or remote server
	Type MCPServerType `json:"type"`
	// Command is the executable and arguments for local servers
	Command []string `json:"command,omitempty"`
	// Environment variables for local servers
	Environment map[string]string `json:"environment,omitempty"`
	// URL is the endpoint for remote servers
	URL string `json:"url,omitempty"`
	// Headers for remote server authentication
	Headers map[string]string `json:"headers,omitempty"`
	// Enabled determines if the server is active
	Enabled bool `json:"enabled"`
	// Disabled explicitly disables a server
	Disabled bool `json:"disabled,omitempty"`
}

MCPServer defines an MCP (Model Context Protocol) server configuration

func LocalMCPServer

func LocalMCPServer(name string, command []string, opts ...MCPOption) *MCPServer

LocalMCPServer creates a local MCP server that runs as a subprocess using stdio for communication.

Example:

server := LocalMCPServer("filesystem",
    []string{"mcp-filesystem", "/path/to/repo"},
    WithEnv(map[string]string{"LOG_LEVEL": "debug"}),
    WithEnabled(true),
)

func RemoteMCPServer

func RemoteMCPServer(name string, url string, opts ...MCPOption) *MCPServer

RemoteMCPServer creates a remote MCP server that connects via HTTP/SSE.

Example:

server := RemoteMCPServer("brave-search",
    "https://mcp.brave.com/search",
    WithHeaders(map[string]string{"Authorization": "Bearer token"}),
    WithEnabled(true),
)

func (*MCPServer) ToConfig

func (s *MCPServer) ToConfig() (opencode.ConfigMcp, error)

ToConfig converts the MCPServer to the OpenCode SDK configuration format

func (*MCPServer) Validate

func (s *MCPServer) Validate() error

Validate checks if the MCP server configuration is valid

type MCPServerType

type MCPServerType string

MCPServerType defines the type of MCP server connection

const (
	// MCPServerTypeLocal represents a local MCP server using stdio transport
	MCPServerTypeLocal MCPServerType = "local"
	// MCPServerTypeRemote represents a remote MCP server using HTTP/SSE transport
	MCPServerTypeRemote MCPServerType = "remote"
)

type Message

type Message struct {
	ID        string
	Role      string
	SessionID string
	Content   string
	Parts     []Part
	CreatedAt int64
}

type Option

type Option func(*Agent) error

func WithAgentType

func WithAgentType(agentType AgentType) Option

func WithBaseURL

func WithBaseURL(baseURL string) Option

func WithClient

func WithClient(client *opencode.Client) Option

func WithLogger

func WithLogger(logger *slog.Logger) Option

func WithMCPRegistry

func WithMCPRegistry(registry *MCPRegistry) Option

func WithMCPServers

func WithMCPServers(servers ...*MCPServer) Option

func WithModel

func WithModel(model string) Option

func WithPermissionHandler

func WithPermissionHandler(handler PermissionHandler) Option

func WithPermissions

func WithPermissions(perm *PermissionConfig) Option

func WithSmallModel

func WithSmallModel(model string) Option

func WithWorkingDir

func WithWorkingDir(dir string) Option

type PRHandler

type PRHandler func(ctx context.Context, session *Session, implementation string, review *ReviewResult) error

type Part

type Part struct {
	ID   string
	Type string
	Text string
	Tool *ToolCall
	File *FileInfo
}

type PermissionBuilder

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

func NewPermissions

func NewPermissions() *PermissionBuilder

func (*PermissionBuilder) AllowBash

func (b *PermissionBuilder) AllowBash(patterns ...string) *PermissionBuilder

func (*PermissionBuilder) AllowEdit

func (b *PermissionBuilder) AllowEdit() *PermissionBuilder

func (*PermissionBuilder) AllowWebfetch

func (b *PermissionBuilder) AllowWebfetch() *PermissionBuilder

func (*PermissionBuilder) AskBash

func (b *PermissionBuilder) AskBash(patterns ...string) *PermissionBuilder

func (*PermissionBuilder) AskEdit

func (b *PermissionBuilder) AskEdit() *PermissionBuilder

func (*PermissionBuilder) AskWebfetch

func (b *PermissionBuilder) AskWebfetch() *PermissionBuilder

func (*PermissionBuilder) Build

func (b *PermissionBuilder) Build() *PermissionConfig

func (*PermissionBuilder) DenyBash

func (b *PermissionBuilder) DenyBash(patterns ...string) *PermissionBuilder

func (*PermissionBuilder) DenyEdit

func (b *PermissionBuilder) DenyEdit() *PermissionBuilder

func (*PermissionBuilder) DenyWebfetch

func (b *PermissionBuilder) DenyWebfetch() *PermissionBuilder

type PermissionConfig

type PermissionConfig struct {
	Bash     PermissionLevel            `json:"bash"`
	BashMap  map[string]PermissionLevel `json:"bashMap,omitempty"`
	Edit     PermissionLevel            `json:"edit"`
	Webfetch PermissionLevel            `json:"webfetch"`
}

type PermissionHandler

type PermissionHandler func(ctx context.Context, req *PermissionRequest) (*PermissionResponse, error)

func AllowAllPermissionHandler

func AllowAllPermissionHandler() PermissionHandler

func DefaultPermissionHandler

func DefaultPermissionHandler() PermissionHandler

func DenyAllPermissionHandler

func DenyAllPermissionHandler() PermissionHandler

type PermissionLevel

type PermissionLevel string
const (
	PermissionAsk   PermissionLevel = "ask"
	PermissionAllow PermissionLevel = "allow"
	PermissionDeny  PermissionLevel = "deny"
)

type PermissionRequest

type PermissionRequest struct {
	ID      string
	Session string
	Type    PermissionType
	Details map[string]interface{}
}

type PermissionResponse

type PermissionResponse struct {
	Allow  bool
	Reason string
}

type PermissionType

type PermissionType string
const (
	PermissionTypeBash     PermissionType = "bash"
	PermissionTypeEdit     PermissionType = "edit"
	PermissionTypeWebfetch PermissionType = "webfetch"
)

type PromptBuilder

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

func NewPromptBuilder

func NewPromptBuilder() *PromptBuilder

func (*PromptBuilder) AddAgentRef

func (b *PromptBuilder) AddAgentRef(name string) *PromptBuilder

func (*PromptBuilder) AddFile

func (b *PromptBuilder) AddFile(path string) *PromptBuilder

func (*PromptBuilder) AddFileFromContent

func (b *PromptBuilder) AddFileFromContent(path string, content []byte, mime string) *PromptBuilder

func (*PromptBuilder) AddSymbol

func (b *PromptBuilder) AddSymbol(path string, name string, kind int) *PromptBuilder

func (*PromptBuilder) AddText

func (b *PromptBuilder) AddText(content string) *PromptBuilder

func (*PromptBuilder) Build

func (b *PromptBuilder) Build(opts ...PromptOption) *PromptConfig

type PromptConfig

type PromptConfig struct {
	Parts       []PromptPart
	Context     string
	System      string
	Temperature *float64
	Model       string
	Agent       string
}

type PromptOption

type PromptOption func(*PromptConfig)

func WithAgent

func WithAgent(name string) PromptOption

func WithContext

func WithContext(ctx string) PromptOption

func WithFiles

func WithFiles(paths ...string) PromptOption

func WithParts

func WithParts(parts ...PromptPart) PromptOption

func WithPromptModel

func WithPromptModel(model string) PromptOption

func WithSystemPrompt

func WithSystemPrompt(prompt string) PromptOption

func WithTemperature

func WithTemperature(temp float64) PromptOption

type PromptPart

type PromptPart interface {
	ToInput() (opencode.SessionPromptParamsPartUnion, error)
}

func AgentRef

func AgentRef(name string) PromptPart

func File

func File(path string) PromptPart

func FileFromContent

func FileFromContent(path string, content []byte, mime string) PromptPart

func FileWithWorkingDir

func FileWithWorkingDir(path string, workingDir string) PromptPart

func Symbol

func Symbol(path string, name string, kind int) PromptPart

func SymbolFromContent

func SymbolFromContent(path string, name string, kind int, content []byte) PromptPart

func SymbolWithWorkingDir

func SymbolWithWorkingDir(path string, name string, kind int, workingDir string) PromptPart

func Text

func Text(content string) PromptPart

type Response

type Response struct {
	SessionID string
	MessageID string
	Content   string
	Parts     []Part
	Tokens    TokenUsage
	Cost      float64
	Error     error
}

type ReviewHandler

type ReviewHandler func(ctx context.Context, session *Session, implementation string) (*ReviewResult, error)

type ReviewResult

type ReviewResult struct {
	Approved bool
	Feedback string
	Score    float64
	Details  map[string]interface{}
}

type Session

type Session struct {
	ID        string
	Title     string
	Directory string
	ProjectID string
	Created   float64
	Updated   float64
	// contains filtered or unexported fields
}

func (*Session) Abort

func (s *Session) Abort(ctx context.Context) error

func (*Session) Close

func (s *Session) Close() error

func (*Session) Messages

func (s *Session) Messages(ctx context.Context) ([]Message, error)

func (*Session) Prompt

func (s *Session) Prompt(ctx context.Context, prompt string, opts ...PromptOption) (*Response, error)

func (*Session) PromptStream

func (s *Session) PromptStream(ctx context.Context, prompt string, opts ...PromptOption) (<-chan Event, error)

func (*Session) Refresh

func (s *Session) Refresh(ctx context.Context) error

func (*Session) Revert

func (s *Session) Revert(ctx context.Context, messageID string) error

func (*Session) Share

func (s *Session) Share(ctx context.Context) (string, error)

func (*Session) Summarize

func (s *Session) Summarize(ctx context.Context) error

func (*Session) Unshare

func (s *Session) Unshare(ctx context.Context) error

func (*Session) UpdateTitle

func (s *Session) UpdateTitle(ctx context.Context, title string) error

type SessionConfig

type SessionConfig struct {
	Title     string
	ParentID  string
	Directory string
	ProjectID string
}

type SessionOption

type SessionOption func(*SessionConfig)

func WithDirectory

func WithDirectory(dir string) SessionOption

func WithParentID

func WithParentID(parentID string) SessionOption

func WithProjectID

func WithProjectID(projectID string) SessionOption

func WithTitle

func WithTitle(title string) SessionOption

type SymbolPart

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

func (*SymbolPart) ToInput

type TextPart

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

func (*TextPart) ToInput

type TextPartData

type TextPartData struct {
	Text string
}

type TokenUsage

type TokenUsage struct {
	Input      float64
	Output     float64
	Reasoning  float64
	CacheRead  float64
	CacheWrite float64
}

type ToolCall

type ToolCall struct {
	ID     string
	Name   string
	Input  map[string]interface{}
	State  string
	Output interface{}
	Error  string
}

Directories

Path Synopsis
examples
basic command
Example demonstrates basic usage of the agent package with MCP servers
Example demonstrates basic usage of the agent package with MCP servers
feedback-loop command
mcp-feedback command

Jump to

Keyboard shortcuts

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