subagent

package
v0.0.25 Latest Latest
Warning

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

Go to latest
Published: Apr 23, 2026 License: MIT Imports: 21 Imported by: 0

Documentation

Index

Constants

View Source
const (
	// DefaultAbsoluteTimeout is the maximum wall-clock time a subagent can run (10 minutes).
	DefaultAbsoluteTimeout = 10 * time.Minute

	// DefaultInactivityTimeout is how long a subagent can go without producing output (120 seconds).
	DefaultInactivityTimeout = 120 * time.Second
)

Default timeout values for subagent processes.

View Source
const DefaultPoolSize = 5

DefaultPoolSize is the default maximum number of concurrent subagents.

Variables

View Source
var DefaultEnvAllowlist = []string{

	"HOME",
	"USER",
	"LOGNAME",
	"SHELL",
	"PATH",
	"LANG",
	"LC_",
	"TERM",
	"TMPDIR",
	"XDG_",
	"EDITOR",
	"VISUAL",

	"GOPATH",
	"GOROOT",
	"GOBIN",
	"GOPROXY",
	"GONOSUMCHECK",
	"GONOSUMDB",
	"GOPRIVATE",
	"GOFLAGS",
	"CGO_ENABLED",

	"GIT_",
	"SSH_AUTH_SOCK",

	"PI_",

	"ANTHROPIC_API_KEY",
	"ANTHROPIC_AUTH_TOKEN",
	"ANTHROPIC_BASE_URL",
	"OPENAI_API_KEY",
	"OPENAI_BASE_URL",
	"GEMINI_API_KEY",
	"GOOGLE_API_KEY",
	"GEMINI_BASE_URL",
	"OLLAMA_HOST",

	"DOCKER_HOST",
	"KUBECONFIG",
	"NODE_PATH",
	"PYTHONPATH",
}

DefaultEnvAllowlist is the set of environment variable prefixes and exact names that are passed through to subagent processes. Everything else is filtered out for security — subagents should not inherit secrets, credentials, or tokens unless explicitly allowed.

Functions

func FilterEnv added in v0.0.6

func FilterEnv(allowlist []string) []string

FilterEnv returns a filtered copy of the current process environment, keeping only variables that match the allowlist. Entries are matched by exact name or by prefix (allowlist entries ending with "_" match any variable starting with that prefix).

Types

type AgentConfig added in v0.0.4

type AgentConfig struct {
	Name        string   // Agent identifier (e.g., "explore", "plan")
	Description string   // One-line description from frontmatter
	Role        string   // Config role name for model resolution (e.g., "smol", "plan", "slow")
	Worktree    bool     // Whether this agent runs in an isolated git worktree
	Timeout     int      // Absolute timeout in milliseconds (0 = use default)
	Instruction string   // System prompt (markdown body)
	Tools       []string // Allowed tool names (empty = all tools)
	Source      string   // "bundled", "user", or "project"
}

AgentConfig represents a parsed agent definition from markdown.

func FindAgent added in v0.0.4

func FindAgent(result *AgentDiscoveryResult, name string) (AgentConfig, bool)

FindAgent looks up an agent by name from a discovery result.

func LoadAgentsFromDir added in v0.0.4

func LoadAgentsFromDir(dir string) ([]AgentConfig, error)

LoadAgentsFromDir loads all agent markdown files from a directory. Returns empty slice if directory doesn't exist (not an error).

func LoadBundledAgents added in v0.0.5

func LoadBundledAgents() ([]AgentConfig, error)

LoadBundledAgents loads all embedded agent definitions. Returns agents with Source set to "bundled".

func ParseAgentFile added in v0.0.4

func ParseAgentFile(path string) (AgentConfig, error)

ParseAgentFile parses a single agent markdown file. Expected format: --- name: agent-name description: One-line description role: smol worktree: false tools: read, write, edit --- Markdown instruction body...

func ParseAgentFileFromFS added in v0.0.5

func ParseAgentFileFromFS(fsys fs.FS, path string) (AgentConfig, error)

ParseAgentFileFromFS parses an agent file from an embedded filesystem.

type AgentDiscoveryResult added in v0.0.4

type AgentDiscoveryResult struct {
	Bundled []AgentConfig
	User    []AgentConfig
	Project []AgentConfig
	All     []AgentConfig // Merged with priority: project > user > bundled
}

AgentDiscoveryResult contains all discovered agents.

func DiscoverAgents added in v0.0.4

func DiscoverAgents(cwd string, scope AgentScope) (*AgentDiscoveryResult, error)

DiscoverAgents loads agents from bundled, user, and project directories. Priority: project > user > bundled (later sources override earlier ones by name).

type AgentInput

type AgentInput struct {
	Type         string `json:"type"`                    // Agent type name
	Prompt       string `json:"prompt"`                  // Task prompt for the agent
	Worktree     *bool  `json:"worktree,omitempty"`      // Override worktree setting
	WorktreeName string `json:"worktree_name,omitempty"` // Optional worktree/branch name prefix
	WorkDir      string `json:"work_dir,omitempty"`      // Override working directory (e.g. existing worktree path)
	Background   bool   `json:"background,omitempty"`    // Run in background
	SkipCleanup  bool   `json:"skip_cleanup,omitempty"`  // Don't auto-cleanup worktree on completion
}

AgentInput is the legacy input to spawn a subagent (deprecated, use SpawnInput). It is kept for backward compatibility with existing callers. Convert to SpawnInput using ToSpawnInput() which looks up the bundled agent.

func (AgentInput) ToSpawnInput added in v0.0.5

func (a AgentInput) ToSpawnInput() (SpawnInput, error)

ToSpawnInput converts a legacy AgentInput to the new SpawnInput format. It looks up the agent config from bundled agents.

type AgentOutput

type AgentOutput struct {
	AgentID  string `json:"agent_id"`
	Type     string `json:"type"`
	Result   string `json:"result"`
	Error    string `json:"error,omitempty"`
	Duration string `json:"duration"`
}

AgentOutput is the result of a completed subagent.

type AgentScope added in v0.0.4

type AgentScope string

AgentScope defines the scope for agent discovery.

const (
	ScopeBoth    AgentScope = "both"    // Bundled + user/project
	ScopeBundled AgentScope = "bundled" // Only embedded agents
	ScopeProject AgentScope = "project" // Only user/project agents
)

type AgentStatus

type AgentStatus struct {
	AgentID   string    `json:"agent_id"`
	Type      string    `json:"type"`
	Status    string    `json:"status"` // "running", "completed", "failed", "canceled", "killed"
	Prompt    string    `json:"prompt"`
	StartedAt time.Time `json:"started_at"`
	Duration  string    `json:"duration,omitempty"`
}

AgentStatus represents the current state of a subagent.

type Event

type Event struct {
	Type       string `json:"type"`                 // "text_delta", "tool_call", "tool_result", "message_end", "error"
	Content    string `json:"content,omitempty"`    // Text content for text_delta
	Error      string `json:"error,omitempty"`      // Error message for error events
	SessionID  string `json:"session_id,omitempty"` // Subprocess session ID (from message_start)
	StopReason string `json:"stopReason,omitempty"` // ACP stopReason on message_end
}

Event is a streaming event from a subagent process.

type InactivityTimer added in v0.0.6

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

InactivityTimer tracks output activity and signals when the inactivity timeout expires.

func NewInactivityTimer added in v0.0.6

func NewInactivityTimer(timeout time.Duration) *InactivityTimer

NewInactivityTimer creates a timer that fires after the given duration of inactivity.

func (*InactivityTimer) C added in v0.0.6

func (t *InactivityTimer) C() <-chan time.Time

C returns the channel that fires when the inactivity timeout expires.

func (*InactivityTimer) Reset added in v0.0.6

func (t *InactivityTimer) Reset()

Reset restarts the inactivity countdown. Call this when output is received.

func (*InactivityTimer) Stop added in v0.0.6

func (t *InactivityTimer) Stop()

Stop stops the timer. Call when the process completes normally.

type Orchestrator

type Orchestrator struct {

	// LLM provider settings passed to child subagents
	BaseURL  string   // LLM API base URL
	Insecure bool     // Skip TLS verification
	Headers  []string // Extra HTTP headers
	// contains filtered or unexported fields
}

Orchestrator composes Pool, Spawner, and WorktreeManager to manage subagent lifecycle.

func NewOrchestrator

func NewOrchestrator(cfg *config.Config, repoRoot string, agentConfigs []AgentConfig) *Orchestrator

NewOrchestrator creates an Orchestrator from config. repoRoot is the git repository root (empty string disables worktree support). agentConfigs are the discovered agent definitions (from DiscoverAgents + bundled).

func (*Orchestrator) AgentNames added in v0.0.6

func (o *Orchestrator) AgentNames() []string

AgentNames returns the names of all registered agents.

func (*Orchestrator) Cancel

func (o *Orchestrator) Cancel(agentID string) error

Cancel cancels a running agent by ID.

func (*Orchestrator) FindRecentTask added in v0.0.15

func (o *Orchestrator) FindRecentTask(prompt string) *RecentTaskResult

FindRecentTask checks if a task matching the given prompt was completed recently. Returns the result if found and not expired, nil otherwise.

func (*Orchestrator) List

func (o *Orchestrator) List() []AgentStatus

List returns the status of all tracked agents.

func (*Orchestrator) LookupAgent added in v0.0.6

func (o *Orchestrator) LookupAgent(name string) (AgentConfig, error)

LookupAgent returns the AgentConfig for the given name, or an error if not found.

func (*Orchestrator) RecordTask added in v0.0.15

func (o *Orchestrator) RecordTask(prompt string, summary, status string)

RecordTask marks a completed subagent task for deduplication.

func (*Orchestrator) RegisterAgents added in v0.0.6

func (o *Orchestrator) RegisterAgents(configs []AgentConfig)

RegisterAgents replaces the agent registry with the given configs.

func (*Orchestrator) SetACPLogPath added in v0.0.23

func (o *Orchestrator) SetACPLogPath(path string)

SetACPLogPath configures where ACP subagent events (claude, gemini) are captured as JSONL. Pass an empty string to disable.

func (*Orchestrator) SetProviderOptions added in v0.0.18

func (o *Orchestrator) SetProviderOptions(baseURL string, insecure bool, headers []string)

SetProviderOptions configures the LLM provider settings that will be passed to child subagents. Call this after NewOrchestrator to propagate CLI flags.

func (*Orchestrator) Shutdown

func (o *Orchestrator) Shutdown()

Shutdown cancels all running agents and cleans up worktrees. For production use, prefer ShutdownWithTimeout.

func (*Orchestrator) ShutdownWithTimeout added in v0.0.14

func (o *Orchestrator) ShutdownWithTimeout(timeout time.Duration)

ShutdownWithTimeout gracefully cancels all running agents with the given timeout, then forces cleanup of worktrees. The timeout applies to the entire shutdown operation, not individual agents.

func (*Orchestrator) Spawn

func (o *Orchestrator) Spawn(ctx context.Context, input SpawnInput) (<-chan Event, string, error)

Spawn starts a new subagent and returns an event channel. It acquires a pool slot, optionally creates a worktree, and spawns the pi process.

func (*Orchestrator) SpawnWithInput added in v0.0.5

func (o *Orchestrator) SpawnWithInput(ctx context.Context, input AgentInput) (<-chan Event, string, error)

SpawnWithInput is the legacy method that accepts AgentInput for backward compatibility. It converts the input to SpawnInput and calls Spawn. Deprecated: Use Spawn with SpawnInput directly.

func (*Orchestrator) SpawnWithRetry added in v0.0.23

func (o *Orchestrator) SpawnWithRetry(ctx context.Context, input SpawnInput) (<-chan Event, string, error)

SpawnWithRetry spawns a subagent with automatic retry on crash (up to maxRetries). It monitors the subagent and re-spawns if the subagent crashes with status "failed" or "killed". Returns the final events channel, agentID, and error (nil on success).

func (*Orchestrator) Worktree

func (o *Orchestrator) Worktree() *WorktreeManager

Worktree returns the WorktreeManager (may be nil if worktrees are disabled).

type Pool

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

Pool manages concurrency for subagent processes using a buffered channel semaphore.

func NewPool

func NewPool(maxConcurrent int) *Pool

NewPool creates a concurrency pool that allows at most maxConcurrent active agents.

func (*Pool) Acquire

func (p *Pool) Acquire(ctx context.Context) error

Acquire blocks until a slot is available or the context is canceled.

func (*Pool) Available

func (p *Pool) Available() int

Available returns the number of currently available slots.

func (*Pool) Release

func (p *Pool) Release()

Release returns a slot to the pool. Must be called after Acquire.

func (*Pool) Size

func (p *Pool) Size() int

Size returns the maximum concurrency limit.

type Process

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

Process represents a running subagent pi process.

func (*Process) Cancel

func (p *Process) Cancel()

Cancel kills the subagent process.

func (*Process) Events

func (p *Process) Events() <-chan Event

Events returns a channel that receives streaming events from the subagent. The channel is closed when the process exits.

func (*Process) Wait

func (p *Process) Wait() (string, error)

Wait blocks until the process exits and returns the accumulated result or an error.

type RecentTaskResult added in v0.0.15

type RecentTaskResult struct {
	CompletedAt time.Time
	Summary     string
	Status      string
}

RecentTaskResult holds the result of a recently completed subagent task.

type SpawnInput added in v0.0.5

type SpawnInput struct {
	Agent        AgentConfig `json:"agent"`                   // Agent configuration
	Prompt       string      `json:"prompt"`                  // Task prompt for the agent
	Worktree     *bool       `json:"worktree,omitempty"`      // Override worktree setting
	WorktreeName string      `json:"worktree_name,omitempty"` // Optional worktree/branch name prefix
	WorkDir      string      `json:"work_dir,omitempty"`      // Override working directory (e.g. existing worktree path)
	Background   bool        `json:"background,omitempty"`    // Run in background
	SkipCleanup  bool        `json:"skip_cleanup,omitempty"`  // Don't auto-cleanup worktree on completion
	Env          []string    `json:"env,omitempty"`           // Additional environment variables
	MaxRetries   int         `json:"max_retries,omitempty"`   // Max retry attempts on crash (default 0, max 3)
}

SpawnInput is the input to spawn a subagent with an AgentConfig.

type SpawnOpts

type SpawnOpts struct {
	AgentID     string   // Unique ID for this agent
	Model       string   // Model name to use
	WorkDir     string   // Working directory for the process
	Prompt      string   // Task prompt to send
	Instruction string   // System instruction for the subagent
	Timeout     int      // Timeout in milliseconds (0 = use default)
	Env         []string // Additional environment variables (merged with filtered process env)
	BaseURL     string   // LLM API base URL (passed as --url flag)
	Insecure    bool     // Skip TLS verification (passed as --insecure flag)
	Headers     []string // Extra HTTP headers (passed as --header flags)
}

SpawnOpts holds options for spawning a subagent process.

type Spawner

type Spawner struct {
	// PiBinary is the path to the pi binary. Defaults to os.Executable() when
	// created via NewSpawner(""), ensuring subagents match the parent version.
	PiBinary string
}

Spawner creates and manages subagent pi processes.

func NewSpawner

func NewSpawner(piBinary string) *Spawner

NewSpawner creates a new Spawner. If piBinary is empty, uses the currently running executable path (via os.Executable) to ensure subagents run the same version as the parent process.

func (*Spawner) Spawn

func (s *Spawner) Spawn(ctx context.Context, opts SpawnOpts) (*Process, error)

Spawn starts a pi subprocess in JSON mode and returns a Process handle for streaming events.

type TimeoutConfig added in v0.0.6

type TimeoutConfig struct {
	Absolute   time.Duration // Maximum wall-clock time.
	Inactivity time.Duration // Maximum time without output.
}

TimeoutConfig holds resolved timeout values for a subagent.

func ResolveTimeout added in v0.0.6

func ResolveTimeout(agentTimeoutMs int) TimeoutConfig

ResolveTimeout determines the timeout configuration for a subagent. Priority: agent frontmatter > PI_SUBAGENT_TIMEOUT_MS env var > default. agentTimeoutMs is the timeout field from AgentConfig (0 means unset).

type WorktreeManager

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

WorktreeManager manages git worktrees for isolated subagent execution.

func NewWorktreeManager

func NewWorktreeManager(repoRoot string) *WorktreeManager

NewWorktreeManager creates a new WorktreeManager rooted at the given git repo.

func (*WorktreeManager) Active

func (m *WorktreeManager) Active() int

Active returns the number of active worktrees.

func (*WorktreeManager) Cleanup

func (m *WorktreeManager) Cleanup(agentID string) error

Cleanup removes the worktree and branch for the given agent ID. After CleanupAll has started (closed=true), late Cleanup calls are no-ops — CleanupAll owns all remaining entries at that point.

func (*WorktreeManager) CleanupAll

func (m *WorktreeManager) CleanupAll() error

CleanupAll removes all active worktrees. Used during shutdown. It sets the closed flag to reject late Cleanup calls from completion goroutines, waits for in-flight cleanups, then retries remaining entries up to maxPasses.

func (*WorktreeManager) Create

func (m *WorktreeManager) Create(agentID string, requestedName ...string) (string, error)

Create creates a new git worktree for the given agent ID. Returns the filesystem path to the worktree.

func (*WorktreeManager) MergeBack

func (m *WorktreeManager) MergeBack(agentID string) (string, error)

MergeBack merges the worktree branch back into the current branch of the main worktree. Returns the merge output.

func (*WorktreeManager) PathFor

func (m *WorktreeManager) PathFor(agentID string) string

PathFor returns the worktree path for the given agent ID, or empty string if none.

func (*WorktreeManager) RepoRoot added in v0.0.10

func (m *WorktreeManager) RepoRoot() string

RepoRoot returns the git repository root path.

Jump to

Keyboard shortcuts

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