// Package agent provides a unified interface for executing prompts via // coding agents (Claude Code, Codex, OpenCode). It mirrors the happy-cli AgentBackend // pattern, translated to idiomatic Go. package agent import ( "context" "fmt" "log/slog" "time" ) // Backend is the unified interface for executing prompts via coding agents. type Backend interface { // Execute runs a prompt and returns a Session for streaming results. // The caller should read from Session.Messages (optional) and wait on // Session.Result for the final outcome. Execute(ctx context.Context, prompt string, opts ExecOptions) (*Session, error) } // ExecOptions configures a single execution. type ExecOptions struct { Cwd string Model string SystemPrompt string MaxTurns int Timeout time.Duration ResumeSessionID string // if non-empty, resume a previous agent session } // Session represents a running agent execution. type Session struct { // Messages streams events as the agent works. The channel is closed // when the agent finishes (before Result is sent). Messages <-chan Message // Result receives exactly one value — the final outcome — then closes. Result <-chan Result } // MessageType identifies the kind of Message. type MessageType string const ( MessageText MessageType = "text" MessageThinking MessageType = "thinking" MessageToolUse MessageType = "tool-use" MessageToolResult MessageType = "tool-result" MessageStatus MessageType = "status" MessageError MessageType = "error" MessageLog MessageType = "log" ) // Message is a unified event emitted by an agent during execution. type Message struct { Type MessageType Content string // text content (Text, Error, Log) Tool string // tool name (ToolUse, ToolResult) CallID string // tool call ID (ToolUse, ToolResult) Input map[string]any // tool input (ToolUse) Output string // tool output (ToolResult) Status string // agent status string (Status) Level string // log level (Log) } // Result is the final outcome after an agent session completes. type Result struct { Status string // "completed", "failed", "aborted", "timeout" Output string // accumulated text output Error string // error message if failed DurationMs int64 SessionID string } // Config configures a Backend instance. type Config struct { ExecutablePath string // path to CLI binary (claude, codex, or opencode) Env map[string]string // extra environment variables Logger *slog.Logger } // New creates a Backend for the given agent type. // Supported types: "claude", "codex", "opencode". func New(agentType string, cfg Config) (Backend, error) { if cfg.Logger == nil { cfg.Logger = slog.Default() } switch agentType { case "claude": return &claudeBackend{cfg: cfg}, nil case "codex": return &codexBackend{cfg: cfg}, nil case "opencode": return &opencodeBackend{cfg: cfg}, nil default: return nil, fmt.Errorf("unknown agent type: %q (supported: claude, codex, opencode)", agentType) } } // DetectVersion runs the agent CLI with --version and returns the output. func DetectVersion(ctx context.Context, executablePath string) (string, error) { return detectCLIVersion(ctx, executablePath) }