agent

package
v0.36.2 Latest Latest
Warning

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

Go to latest
Published: Mar 20, 2026 License: MIT Imports: 17 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)
Docker Support and Path Mapping

When running agents in Docker containers (like OpenCode), workspace directories on the host need to be translated to container paths. Use WithPathMapping to configure this translation:

// Configure path mapping for Docker-based agents
// Maps host paths to container paths
pathMapping := map[string]string{
    "/home/user/agent-workspaces": "/agent-workspaces",
}

ag, err := agent.New(
    agent.WithBaseURL("http://localhost:3000"),
    agent.WithModel("ollama/qwen3.5:2b"),
    agent.WithWorkingDir("/home/user/agent-workspaces"),
    agent.WithPathMapping(pathMapping),
)

// When creating a session, the directory is automatically translated
session, err := ag.NewSession(ctx,
    agent.WithTitle("Docker Agent Session"),
    agent.WithDirectory("/home/user/agent-workspaces/my-session"),
)
// The session will use "/agent-workspaces/my-session" inside the container

Why this is needed:

  • OpenCode runs in a Docker container with its own filesystem
  • When you specify a host directory, the agent needs to know the corresponding container path
  • Path mapping ensures files created/modified by the agent are persisted to the host

Docker Compose configuration:

opencode:
  image: ghcr.io/anomalyco/opencode:latest
  volumes:
    - .:/app/workspace
    - ${AGENT_WORKSPACE_DIR:-./workspaces}:/agent-workspaces  # Mount host workspaces
  environment:
    - OPENCODE_WORKING_DIR=/app/workspace
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

Note: OpenCode mounts agent workspaces to persist changes. See Docker Support and Path Mapping section.

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

Native Agentic Framework

The package also provides a standalone, LLM-agnostic agentic framework for building autonomous agents that can run locally using standard goframe components.

The native framework includes three core primitives:

1. Tool Registry (agent.Tool)

The Registry manages available tools with automatic schema generation:

registry := agent.NewRegistry()

// Create tool from function
searchTool, _ := agent.NewToolFromFunc(
    "search",
    "Search for documents matching a query",
    func(ctx context.Context, params SearchParams) (SearchResult, error) {
        // implementation
    },
)
registry.MustRegisterTool(searchTool)

2. Governance (agent.IntegrityCheck)

Integrity checks validate tool executions before they run:

governance := agent.NewGovernance(
    agent.NewPermissionCheck().Allow("read", "write").Deny("delete"),
    agent.NewParameterCheck().Require("write", "path"),
)

3. Autonomous Loop (agent.AgentLoop)

The AgentLoop manages the think-act-observe lifecycle:

loop, _ := agent.NewAgentLoop(model, registry,
    agent.WithLoopMaxIterations(20),
    agent.WithLoopGovernance(governance),
    agent.WithLoopSystemPrompt("You are a helpful assistant"),
)
result, _ := loop.Run(ctx, task, nil)

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 (
	// ErrHumanInterventionRequired signals that the agent needs human approval.
	ErrHumanInterventionRequired = errors.New("agent: human intervention required for high-risk operation")
	// ErrActionFailedVerification signals that an action did not achieve its intended goal.
	ErrActionFailedVerification = errors.New("agent: action failed verification")
	// ErrApprovalTimedOut signals that human approval was not received within the timeout.
	ErrApprovalTimedOut = errors.New("agent: human approval timed out")
)
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 (
	ErrMaxIterations = errors.New("agent: maximum iterations exceeded")
	ErrNoLLM         = errors.New("agent: no LLM model provided")
	ErrNoRegistry    = errors.New("agent: no tool registry provided")
	ErrLoopCancelled = errors.New("agent: loop cancelled by context")
)
View Source
var (
	ErrPathTraversal = errors.New("path traversal detected: path outside working directory")
	ErrEmptyPath     = errors.New("empty path provided")
)
View Source
var (
	ErrToolInvalidParams = errors.New("agent: invalid tool parameters")
	ErrToolExecution     = errors.New("agent: tool execution failed")
)
View Source
var (
	ErrGovernanceDenied = errors.New("agent: governance check denied tool execution")
)

Functions

func SaveConfig

func SaveConfig(config *Config, path string) error

func ValidatePath

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

Types

type ActionVerifier

type ActionVerifier interface {
	// VerifyAction checks if a tool execution achieved its goal.
	VerifyAction(ctx context.Context, toolName string, params map[string]any, result any) (VerifierResult, error)
}

ActionVerifier verifies that actions achieved their intended goals.

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 AgentLoop

type AgentLoop struct {

	// GenerateTraceID generates a unique trace ID for the loop.
	// If nil, a default UUID-based ID is generated.
	GenerateTraceID func() string
	// contains filtered or unexported fields
}

AgentLoop manages the "Think-Act-Observe" lifecycle for autonomous agents.

func NewAgentLoop

func NewAgentLoop(model llms.Model, registry *Registry, opts ...NativeLoopOption) (*AgentLoop, error)

NewAgentLoop creates a new agent loop with the given configuration.

func (*AgentLoop) Run

func (l *AgentLoop) Run(ctx context.Context, task Task, history []schema.MessageContent) (*LoopResult, error)

Run executes the autonomous loop for a given task and session. The session maintains the conversation history across iterations.

func (*AgentLoop) RunStream

func (l *AgentLoop) RunStream(ctx context.Context, task Task, history []schema.MessageContent) (<-chan StreamResult, error)

RunStream executes the loop and streams results. This allows real-time monitoring of the agent's progress.

func (*AgentLoop) SetGovernance

func (l *AgentLoop) SetGovernance(g *Governance)

SetGovernance attaches a governance manager to the agent loop. This allows the agent to perform integrity checks, risk assessments, and validation before executing any tools.

type AgentLoopWithMiddleware

type AgentLoopWithMiddleware struct {
	*AgentLoop
	// contains filtered or unexported fields
}

AgentLoopWithMiddleware extends AgentLoop with agentic middleware.

func NewAgentLoopWithMiddleware

func NewAgentLoopWithMiddleware(
	model llms.Model,
	registry *Registry,
	mwOpts []MiddlewareOption,
	nativeOpts ...NativeLoopOption,
) (*AgentLoopWithMiddleware, error)

NewAgentLoopWithMiddleware now accepts both types of options.

func (*AgentLoopWithMiddleware) Run

Run executes the autonomous loop with middleware interception.

Middleware Flow:

  1. THINK: Call LLM with task + tool definitions
  2. ASSESS_RISK: If high-risk tool call, assess risk level
  3. REQUEST_APPROVAL: If above threshold, request human approval
  4. AWAIT_APPROVAL: Block until human responds
  5. ACT: Execute tool if approved, abort if rejected
  6. VERIFY: Check if action achieved intended goal
  7. SELF_HEAL: If verification failed, inject failure and retry
  8. OBSERVE: Record result in session history
  9. REPEAT until LLM produces final answer

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 ChannelApprovalHandler

type ChannelApprovalHandler struct {
	// ApprovalChan receives approval decisions from UI.
	ApprovalChan <-chan bool
	// RequestChan sends approval requests to UI.
	RequestChan chan<- HumanApprovalRequest
	// Timeout is the default approval timeout.
	Timeout time.Duration
	// Logger records approval events.
	Logger *slog.Logger
}

ChannelApprovalHandler implements HumanApprovalHandler using channels.

func NewChannelApprovalHandler

func NewChannelApprovalHandler(approvalChan <-chan bool, requestChan chan<- HumanApprovalRequest, timeout time.Duration) *ChannelApprovalHandler

NewChannelApprovalHandler creates a channel-based approval handler.

func (*ChannelApprovalHandler) RequestApproval

func (h *ChannelApprovalHandler) RequestApproval(ctx context.Context, req HumanApprovalRequest) (bool, error)

RequestApproval sends a request and waits for approval.

type CompositeCheck

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

CompositeCheck combines multiple checks into one.

func NewCompositeCheck

func NewCompositeCheck(checks ...IntegrityCheck) *CompositeCheck

func (*CompositeCheck) Add

func (c *CompositeCheck) Add(check IntegrityCheck)

func (*CompositeCheck) Validate

func (c *CompositeCheck) Validate(ctx context.Context, toolName string, params map[string]any) error

type Config

type Config struct {
	Model       string
	SmallModel  string
	AgentType   AgentType
	Permissions *PermissionConfig
	Hooks       *HookConfig
	WorkingDir  string
	// PathMapping maps host paths to container paths for Docker-based agents
	PathMapping map[string]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 ContentSafetyCheck

type ContentSafetyCheck struct {
	// BlockedPatterns maps tool names to patterns that should block execution.
	BlockedPatterns map[string][]string
}

ContentSafetyCheck validates content for safety concerns. This is a simple implementation - production systems should use more sophisticated checks.

func NewContentSafetyCheck

func NewContentSafetyCheck() *ContentSafetyCheck

func (*ContentSafetyCheck) BlockPattern

func (c *ContentSafetyCheck) BlockPattern(tool string, patterns ...string) *ContentSafetyCheck

func (*ContentSafetyCheck) Validate

func (c *ContentSafetyCheck) Validate(ctx context.Context, toolName string, params map[string]any) error

type DefaultRiskAssessor

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

DefaultRiskAssessor provides basic risk assessment.

func NewDefaultRiskAssessor

func NewDefaultRiskAssessor() *DefaultRiskAssessor

NewDefaultRiskAssessor creates a risk assessor with sensible defaults.

func (*DefaultRiskAssessor) AddCriticalRiskTool

func (r *DefaultRiskAssessor) AddCriticalRiskTool(toolName string)

AddCriticalRiskTool marks a tool as critical-risk.

func (*DefaultRiskAssessor) AddHighRiskTool

func (r *DefaultRiskAssessor) AddHighRiskTool(toolName string)

AddHighRiskTool marks a tool as high-risk.

func (*DefaultRiskAssessor) AssessRisk

func (r *DefaultRiskAssessor) AssessRisk(ctx context.Context, toolName string, params map[string]any) RiskLevel

AssessRisk evaluates the risk level of a tool execution.

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 Governance

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

Governance manages integrity checks for tool execution.

func NewGovernance

func NewGovernance(checks ...IntegrityCheck) *Governance

NewGovernance creates a new governance manager with optional checks.

func (*Governance) AddCheck

func (g *Governance) AddCheck(check IntegrityCheck)

AddCheck appends a new integrity check.

func (*Governance) RemoveAllChecks

func (g *Governance) RemoveAllChecks()

RemoveAllChecks clears all integrity checks.

func (*Governance) SetLogger

func (g *Governance) SetLogger(logger *slog.Logger)

SetLogger configures the governance logger.

func (*Governance) Validate

func (g *Governance) Validate(ctx context.Context, toolName string, params map[string]any) error

Validate runs all integrity checks for a tool execution. Returns nil if all checks pass, or an error from the first failing check.

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 HumanApprovalHandler

type HumanApprovalHandler interface {
	// RequestApproval requests human approval and blocks until received.
	// Returns true if approved, false if rejected, or error on timeout/failure.
	RequestApproval(ctx context.Context, req HumanApprovalRequest) (bool, error)
}

HumanApprovalHandler handles human approval requests.

type HumanApprovalRequest

type HumanApprovalRequest struct {
	// ToolName is the tool being executed.
	ToolName string
	// Params are the parameters for the tool call.
	Params map[string]any
	// Reason explains why this action is necessary.
	Reason string
	// Impact describes the potential impact.
	Impact string
	// RiskLevel is the assessed risk level.
	RiskLevel RiskLevel
	// Timeout is the maximum wait time for approval.
	Timeout time.Duration
}

HumanApprovalRequest represents a request for human approval.

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 IntegrityCheck

type IntegrityCheck interface {
	// Validate checks if a tool execution should be allowed.
	// Returns nil to allow execution, or an error to deny it.
	// The error message will be provided to the agent as observation for self-correction.
	Validate(ctx context.Context, toolName string, params map[string]any) error
}

IntegrityCheck validates tool executions before they run. Implementations can enforce policies, validate inputs, or prevent dangerous operations.

type IntegrityCheckFunc

type IntegrityCheckFunc func(ctx context.Context, toolName string, params map[string]any) error

IntegrityCheckFunc is an adapter to use a function as an IntegrityCheck.

func (IntegrityCheckFunc) Validate

func (f IntegrityCheckFunc) Validate(ctx context.Context, toolName string, params map[string]any) error

type LLMAssistedVerifier

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

LLMAssistedVerifier uses an LLM to verify action results.

func NewLLMAssistedVerifier

func NewLLMAssistedVerifier(model Model, opts ...LLMAssistedVerifierOption) *LLMAssistedVerifier

NewLLMAssistedVerifier creates a verifier that uses an LLM for verification.

func (*LLMAssistedVerifier) VerifyAction

func (v *LLMAssistedVerifier) VerifyAction(ctx context.Context, toolName string, params map[string]any, result any) (VerifierResult, error)

VerifyAction verifies that a tool execution achieved its intended goal.

type LLMAssistedVerifierOption

type LLMAssistedVerifierOption func(*LLMAssistedVerifier)

LLMAssistedVerifierOption configures the verifier.

func WithVerifierLogger

func WithVerifierLogger(logger *slog.Logger) LLMAssistedVerifierOption

WithVerifierLogger sets the logger.

func WithVerifierMaxRetries

func WithVerifierMaxRetries(n int) LLMAssistedVerifierOption

WithVerifierMaxRetries sets the maximum verification retries.

type LoopResult

type LoopResult struct {
	// Response is the final answer from the LLM.
	Response string
	// ToolCalls is the list of tool calls made during execution.
	ToolCalls []ToolCallRecord
	// Iterations is the number of think-act-observe cycles completed.
	Iterations int
	// Tokens is the total token usage across all LLM calls.
	Tokens TokenUsage
	// State is the final loop state.
	State LoopState
	// TraceID is a unique identifier for tracing loop execution.
	TraceID string
}

LoopResult represents the final result of an agent loop execution.

type LoopState

type LoopState string

LoopState represents the current state of the agent loop.

const (
	StateThinking  LoopState = "thinking"
	StateActing    LoopState = "acting"
	StateObserving LoopState = "observing"
	StateComplete  LoopState = "complete"
	StateError     LoopState = "error"
)

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 MiddlewareConfig

type MiddlewareConfig struct {
	// HumanApprovalHandler handles high-risk tool approval requests.
	HumanApprovalHandler HumanApprovalHandler
	// RiskAssessor evaluates the risk level of tool calls.
	RiskAssessor RiskAssessor
	// ActionVerifier verifies that actions achieved their goals.
	ActionVerifier ActionVerifier
	// VerificationEnabled enables action verification after tool calls.
	VerificationEnabled bool
	// HighRiskThreshold is the minimum risk level requiring human approval.
	HighRiskThreshold RiskLevel
	// ApprovalTimeout is the maximum wait time for human approval.
	ApprovalTimeout time.Duration
	// MaxSelfHealingAttempts is the maximum retry attempts for self-healing.
	MaxSelfHealingAttempts int
}

MiddlewareConfig configures agentic middleware for the agent loop.

func DefaultMiddlewareConfig

func DefaultMiddlewareConfig() MiddlewareConfig

DefaultMiddlewareConfig returns sensible defaults.

type MiddlewareOption

type MiddlewareOption func(*MiddlewareConfig)

MiddlewareOption configures the middleware.

func WithActionVerifier

func WithActionVerifier(verifier ActionVerifier) MiddlewareOption

WithActionVerifier sets the action verifier.

func WithApprovalTimeout

func WithApprovalTimeout(timeout time.Duration) MiddlewareOption

WithApprovalTimeout sets the human approval timeout.

func WithHighRiskThreshold

func WithHighRiskThreshold(level RiskLevel) MiddlewareOption

WithHighRiskThreshold sets the risk level that requires human approval.

func WithHumanApprovalHandler

func WithHumanApprovalHandler(handler HumanApprovalHandler) MiddlewareOption

WithHumanApprovalHandler sets the human approval handler.

func WithMaxSelfHealingAttempts

func WithMaxSelfHealingAttempts(n int) MiddlewareOption

WithMaxSelfHealingAttempts sets the maximum retry attempts for self-healing.

func WithRiskAssessor

func WithRiskAssessor(assessor RiskAssessor) MiddlewareOption

WithRiskAssessor sets the risk assessor.

func WithVerification

func WithVerification(enabled bool) MiddlewareOption

WithVerification enables or disables action verification.

type MockApprovalHandler

type MockApprovalHandler struct {
	AutoApprove bool
	AutoReject  bool
	Delay       time.Duration
	Logger      *slog.Logger
}

MockApprovalHandler is a simple approval handler for testing.

func NewMockApprovalHandler

func NewMockApprovalHandler(autoApprove bool) *MockApprovalHandler

NewMockApprovalHandler creates a mock handler for testing.

func (*MockApprovalHandler) RequestApproval

func (h *MockApprovalHandler) RequestApproval(ctx context.Context, req HumanApprovalRequest) (bool, error)

RequestApproval returns a configured response for testing.

type Model

type Model interface {
	Call(ctx context.Context, prompt string) (string, error)
}

Model is a minimal LLM interface for verification.

type NativeLoopOption

type NativeLoopOption func(*AgentLoop)

NativeLoopOption configures the agent loop.

func WithLoopGovernance

func WithLoopGovernance(g *Governance) NativeLoopOption

WithLoopGovernance sets the governance checks.

func WithLoopLogger

func WithLoopLogger(logger *slog.Logger) NativeLoopOption

WithLoopLogger sets the logger for the loop.

func WithLoopMaxImagesInContext added in v0.35.2

func WithLoopMaxImagesInContext(n int) NativeLoopOption

WithLoopMaxImagesInContext limits the number of images kept in conversation history. Set to 0 (default) to keep all images, or a positive integer to limit. This helps prevent context overflow when many screenshots are taken. Recommended value: 2-4 for most use cases.

func WithLoopMaxIterations

func WithLoopMaxIterations(n int) NativeLoopOption

WithLoopMaxIterations sets the maximum number of iterations.

func WithLoopSystemPrompt

func WithLoopSystemPrompt(prompt string) NativeLoopOption

WithLoopSystemPrompt sets the system prompt for the LLM.

func WithLoopTemperature

func WithLoopTemperature(temp float64) NativeLoopOption

WithLoopTemperature sets the LLM temperature.

func WithLoopTraceID

func WithLoopTraceID(gen func() string) NativeLoopOption

WithLoopTraceID sets a custom trace ID generator. The trace ID is useful for correlating logs across a multi-step agent execution.

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 WithPathMapping

func WithPathMapping(mapping map[string]string) Option

WithPathMapping configures path translation for Docker-based agents. It maps host paths to container paths so that when the agent creates a session, the directory is correctly translated for the container. Example: {"/home/user/agent-workspaces": "/agent-workspaces"}

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 ParameterCheck

type ParameterCheck struct {
	// Required maps tool names to their required parameter keys.
	Required map[string][]string
	// Forbidden maps tool names to parameters that must not be present.
	Forbidden map[string][]string
}

ParameterCheck validates tool parameters against required fields.

func NewParameterCheck

func NewParameterCheck() *ParameterCheck

func (*ParameterCheck) Forbid

func (p *ParameterCheck) Forbid(tool string, params ...string) *ParameterCheck

func (*ParameterCheck) Require

func (p *ParameterCheck) Require(tool string, params ...string) *ParameterCheck

func (*ParameterCheck) Validate

func (p *ParameterCheck) Validate(ctx context.Context, toolName string, params map[string]any) 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 PermissionCheck

type PermissionCheck struct {
	// Allowed is a set of tool names that are permitted.
	Allowed map[string]bool
	// Denied is a set of tool names that are explicitly blocked.
	Denied map[string]bool
}

PermissionCheck validates tool execution based on a permission map.

func NewPermissionCheck

func NewPermissionCheck() *PermissionCheck

func (*PermissionCheck) Allow

func (p *PermissionCheck) Allow(tools ...string) *PermissionCheck

func (*PermissionCheck) Deny

func (p *PermissionCheck) Deny(tools ...string) *PermissionCheck

func (*PermissionCheck) Validate

func (p *PermissionCheck) Validate(ctx context.Context, toolName string, params map[string]any) error

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 RateLimitCheck

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

RateLimitCheck enforces rate limits on tool execution.

func NewRateLimitCheck

func NewRateLimitCheck() *RateLimitCheck

func (*RateLimitCheck) Reset

func (r *RateLimitCheck) Reset(tool string)

func (*RateLimitCheck) SetLimit

func (r *RateLimitCheck) SetLimit(tool string, maxPerSession int) *RateLimitCheck

func (*RateLimitCheck) Validate

func (r *RateLimitCheck) Validate(ctx context.Context, toolName string, params map[string]any) error

type Registry

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

Registry manages available tools for an agent. It provides lookup, validation, and execution capabilities.

func NewRegistry

func NewRegistry() *Registry

NewRegistry creates an empty tool registry.

func NewRegistryWithTools

func NewRegistryWithTools(tools ...Tool) (*Registry, error)

NewRegistryWithTools creates a registry pre-populated with tools.

func (*Registry) Definitions

func (r *Registry) Definitions() []map[string]any

Definitions returns tool definitions in the format expected by LLMs. This returns a slice format suitable for most LLM APIs.

func (*Registry) Execute

func (r *Registry) Execute(ctx context.Context, name string, params map[string]any) (any, error)

Execute runs a tool by name with the given parameters. Returns ErrToolNotFound if the tool doesn't exist, or ErrToolExecution if execution fails.

func (*Registry) Get

func (r *Registry) Get(name string) (Tool, error)

Get retrieves a tool by name. Returns ErrToolNotFound if the tool doesn't exist.

func (*Registry) GetSchemaMap

func (r *Registry) GetSchemaMap() map[string]map[string]any

GetSchemaMap returns tool schemas as a map keyed by tool name. This format is useful for parallel tool-calling protocols and supports complex tool orchestration workflows.

Example:

schemas := registry.GetSchemaMap()
searchSchema := schemas["search"]
// Use schema for validation or introspection

func (*Registry) List

func (r *Registry) List() []Tool

List returns all registered tools.

func (*Registry) MustRegisterTool

func (r *Registry) MustRegisterTool(tool Tool)

MustRegisterTool registers a tool and panics on error. Use for static initialization of tools.

func (*Registry) Register

func (r *Registry) Register(tool Tool) error

Register adds a tool to the registry. Returns an error if a tool with the same name already exists.

func (*Registry) Unregister

func (r *Registry) Unregister(name string)

Unregister removes a tool from the registry.

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 RiskAssessor

type RiskAssessor interface {
	// AssessRisk returns the risk level for a tool execution.
	AssessRisk(ctx context.Context, toolName string, params map[string]any) RiskLevel
}

RiskAssessor evaluates the risk level of tool executions.

type RiskLevel

type RiskLevel int

RiskLevel categorizes tool execution risk levels.

const (
	RiskLow      RiskLevel = iota // Safe operations (read, search)
	RiskMedium                    // Moderate operations (write, update)
	RiskHigh                      // Destructive operations (delete, navigate external)
	RiskCritical                  // Irreversible operations (format disk, publish)
)

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 StreamResult

type StreamResult struct {
	// State indicates the current loop state.
	State LoopState
	// Text is the partial text content from the LLM.
	Text string
	// ToolCall is a tool call being executed.
	ToolCall *ToolCallRecord
	// Error is any error that occurred.
	Error error
	// Done indicates if the loop has completed.
	Done bool
}

StreamResult represents a partial result during streaming execution.

type SymbolPart

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

func (*SymbolPart) ToInput

type Task

type Task struct {
	// ID is a unique identifier for the task.
	ID string
	// Description is the human-readable task description.
	Description string
	// Context provides additional context for the task.
	Context string
	// Priority indicates task priority (higher = more important).
	Priority int
}

Task represents a unit of work for the agent.

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 Tool

type Tool interface {
	// Name returns the tool's unique identifier.
	Name() string

	// Description returns a human-readable description of what the tool does.
	Description() string

	// ParametersSchema returns a JSON Schema describing the tool's parameters.
	ParametersSchema() map[string]any

	// Execute runs the tool with the given parameters.
	// The params map contains the arguments parsed from the LLM's tool call.
	Execute(ctx context.Context, params map[string]any) (any, error)
}

Tool defines the interface for agent tools. Tools are functions that an agent can call during execution.

func NewToolFromFunc

func NewToolFromFunc(name, description string, fn any) (Tool, error)

NewToolFromFunc creates a Tool from a function using reflection. The function must have a signature like: func(ctx context.Context, params T) (R, error) where T is a struct with JSON tags and R is the return type.

Example:

tool, err := NewToolFromFunc(
    "search",
    "Search for documents matching a query",
    func(ctx context.Context, params SearchParams) (SearchResult, error) {
        // implementation
    },
)

func ToolLogger

func ToolLogger(tool Tool, logger *slog.Logger) Tool

ToolLogger wraps a tool with logging.

type ToolCall

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

type ToolCallRecord

type ToolCallRecord struct {
	// Name is the tool that was called.
	Name string
	// Params are the parameters passed to the tool.
	Params map[string]any
	// Result is the tool's return value.
	Result any
	// Error is any error that occurred during execution.
	Error error
}

ToolCallRecord records a single tool execution.

type ToolFunc

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

ToolFunc is an adapter to use a function as a Tool.

func (*ToolFunc) Description

func (t *ToolFunc) Description() string

func (*ToolFunc) Execute

func (t *ToolFunc) Execute(ctx context.Context, params map[string]any) (any, error)

func (*ToolFunc) Name

func (t *ToolFunc) Name() string

func (*ToolFunc) ParametersSchema

func (t *ToolFunc) ParametersSchema() map[string]any

type VerifierResult

type VerifierResult struct {
	// Verified indicates if the action achieved its intended goal.
	Verified bool
	// Reason explains why verification failed (if not verified).
	Reason string
	// Correction proposes a fix for failed actions.
	Correction string
	// Screenshot hints for UI (optional).
	ScreenshotHint string
}

VerifierResult represents the outcome of action verification.

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