subagent

package
v0.11.0-beta.2 Latest Latest
Warning

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

Go to latest
Published: Oct 23, 2026 License: Apache-2.0, MIT Imports: 19 Imported by: 0

Documentation

Overview

Package subagent — autonomous-execution loop.

AutonomousRunner takes a frozen Contract and drives a child Step interface to completion. It auto-compacts when context fills, watches budget, and hands back a structured FinalReport when the contract is satisfied, exhausted, violated, or handed off.

Package subagent — Contract type and validation/enforcement primitives.

A Contract is the structured handoff between a parent agent and a child sub-agent. The parent freezes it before spawning; the child runs to completion against it without further user/parent intervention. See master plan §5.3 for the autonomous-execution rationale.

Package subagent — contract enforcement at the child's tool boundary.

The enforcer wraps any tool the child uses so that filesystem-mutating calls are checked against contract Scope/OutOfScope before execution. Out-of-scope writes are denied with a ContractViolation and never reach the underlying tool.

Package subagent includes a text-only worker using an OpenAI-compatible API.

Index

Constants

View Source
const XIAOMI_BASE_URL = "https://token-plan-sgp.xiaomimimo.com/v1"

XIAOMI_BASE_URL is the Xiaomi Mimo API endpoint.

Variables

View Source
var ErrUnsupportedCapability = errors.New("subagent: unsupported capability")

ErrUnsupportedCapability means a configured child cannot execute a requested tool.

Functions

func DefaultAcceptanceRunner

func DefaultAcceptanceRunner(ctx context.Context, cmd string, timeout time.Duration, workdir string) (int, string, string, error)

DefaultAcceptanceRunner runs the command with `sh -c` in workdir.

WARNING: This executes arbitrary shell commands from AcceptanceCheck.Cmd, which may be LLM-generated or user-authored. Callers should prefer ScannedAcceptanceRunner with a command scanner to block known-dangerous patterns. This raw runner is provided for cases where no scanner is configured.

func IsAutoDeny

func IsAutoDeny(ctx context.Context) bool

IsAutoDeny returns true if the context signals auto-deny for permissions.

func NewFileAuditWriter

func NewFileAuditWriter(baseDir, id string) (*fileAuditWriter, error)

NewFileAuditWriter prepares a writer rooted at baseDir/<id>. baseDir is usually ~/.overkill/subagents. Returns nil writer + nil error when the dir can't be created (graceful degradation; auditing is best-effort).

func NewMemAuditWriter

func NewMemAuditWriter() *memAuditWriter

NewMemAuditWriter returns an in-memory audit writer. Useful in tests.

Types

type AcceptanceCheck

type AcceptanceCheck struct {
	Name                 string `json:"name"`
	Cmd                  string `json:"cmd"`
	ExpectExit           int    `json:"expect_exit"`
	ExpectStdoutContains string `json:"expect_stdout_contains,omitempty"`
	TimeoutSeconds       int    `json:"timeout_seconds,omitempty"`
}

AcceptanceCheck is a testable command + expectation pair.

type AcceptanceResult

type AcceptanceResult struct {
	Name     string `json:"name"`
	Passed   bool   `json:"passed"`
	ExitCode int    `json:"exit_code"`
	Stdout   string `json:"stdout,omitempty"`
	Stderr   string `json:"stderr,omitempty"`
	Reason   string `json:"reason,omitempty"`
}

AcceptanceResult is the outcome of running one AcceptanceCheck.

type AcceptanceRunner

type AcceptanceRunner func(ctx context.Context, cmd string, timeout time.Duration, workdir string) (exitCode int, stdout, stderr string, err error)

AcceptanceRunner is the surface RunAcceptance uses to invoke commands. Defaults to the OS shell; tests inject a fake.

func ScannedAcceptanceRunner

func ScannedAcceptanceRunner(scanner AcceptanceScanner) AcceptanceRunner

ScannedAcceptanceRunner wraps DefaultAcceptanceRunner with a command scanner. When scanner is nil, it delegates to DefaultAcceptanceRunner directly. When a scanner is provided, commands are checked before execution and blocked if the scanner returns blocked=true (B108).

type AcceptanceScanner

type AcceptanceScanner interface {
	Scan(cmd string) (blocked bool, err error)
}

AcceptanceScanner is a minimal command-scanning interface. Accept any implementation that has a Scan method returning a blocked boolean and error — no import dependency on internal/security required.

type AgentDef

type AgentDef struct {
	Name         string
	Command      string
	Args         []string
	Protocol     Protocol
	Model        string
	Env          map[string]string
	SystemPrompt string   // built-in agents only: injected as system prompt
	Tools        []string // built-in agents only: tool whitelist names

	// Claude Code parity fields — all optional.
	Description        string   `yaml:"description,omitempty"`        // when to auto-select this agent
	DisallowedTools    []string `yaml:"disallowedTools,omitempty"`    // tools to deny even if inherited
	MaxTurns           int      `yaml:"maxTurns,omitempty"`           // max agentic turns before stopping
	PermissionMode     string   `yaml:"permissionMode,omitempty"`     // acceptEdits/auto/bypass
	Skills             []string `yaml:"skills,omitempty"`             // skills to preload at startup
	Color              string   `yaml:"color,omitempty"`              // UI color for agent identification
	Effort             string   `yaml:"effort,omitempty"`             // thinking effort: off/minimal/low/medium/high/x-high
	InitialPrompt      string   `yaml:"initialPrompt,omitempty"`      // prepended to first user turn
	Background         bool     `yaml:"background,omitempty"`         // always run as background task
	Memory             string   `yaml:"memory,omitempty"`             // persistent memory: user/project/local
	MCPServers         []string `yaml:"mcpServers,omitempty"`         // MCP servers scoped to this agent
	Hooks              []string `yaml:"hooks,omitempty"`              // lifecycle hooks for this agent
	RequiredMCPServers []string `yaml:"requiredMcpServers,omitempty"` // MCP servers required for agent availability
	Filename           string   `yaml:"-"`                            // original filename (set at load time)
}

AgentDef describes an external agent that can be delegated work. When Command is empty the agent is a "built-in" — its definition lives in code and is used to configure a sub-agent internally (system prompt, tool whitelist, etc.) rather than spawned as a separate process.

func BuiltinAgents

func BuiltinAgents(resolve ModelResolver) []AgentDef

BuiltinAgents returns the four named built-in agent definitions wired at boot time — Explore, Plan, Verify, and General-purpose. Each agent's Model is resolved at registration time via the provided resolver so Explore gets the cheapest reading model, Verify gets the cheapest tool-capable model, etc.

The resolver is typically a closure over SmartRouter.CheapestModel(). If resolver is nil, all agents use empty model (inherit from caller).

type AgentFile

type AgentFile struct {
	Name               string     `yaml:"name"`
	Description        string     `yaml:"description"`
	Tools              csvStrings `yaml:"tools,omitempty"`
	DisallowedTools    csvStrings `yaml:"disallowedTools,omitempty"`
	Model              string     `yaml:"model,omitempty"`
	PermissionMode     string     `yaml:"permissionMode,omitempty"`
	MaxTurns           int        `yaml:"maxTurns,omitempty"`
	Skills             csvStrings `yaml:"skills,omitempty"`
	MCPServers         csvStrings `yaml:"mcpServers,omitempty"`
	Hooks              csvStrings `yaml:"hooks,omitempty"`
	Memory             string     `yaml:"memory,omitempty"` // user/project/local
	Color              string     `yaml:"color,omitempty"`
	Effort             string     `yaml:"effort,omitempty"`             // thinking effort level
	InitialPrompt      string     `yaml:"initialPrompt,omitempty"`      // prepended to first turn
	Background         bool       `yaml:"background,omitempty"`         // always run as background
	RequiredMCPServers csvStrings `yaml:"requiredMcpServers,omitempty"` // MCP patterns required
	// Body is the markdown after the frontmatter — the system prompt.
	Body string `yaml:"-"`
}

AgentFile is the on-disk representation of a sub-agent definition. Mirrors Claude Code's YAML frontmatter + markdown body format. All optional fields match the OpenClaude sub-agent spec exactly.

type AgentPriority

type AgentPriority int

AgentPriority determines which definition wins when multiple share a name. Higher priority (lower number) wins. Mirrors Claude Code's resolution.

const (
	PriorityManaged AgentPriority = 1 // organization-wide (managed settings)
	PriorityCLIFlag AgentPriority = 2 // --agents CLI flag (session only)
	PriorityProject AgentPriority = 3 // .overkill/agents/ (current project)
	PriorityUser    AgentPriority = 4 // ~/.overkill/agents/ (all projects)
	PriorityBuiltin AgentPriority = 5 // hardcoded in Go (lowest)
)

type AgentRegistry

type AgentRegistry struct {

	// FailedFiles tracks files that couldn't be parsed (for diagnostics).
	FailedFiles []FailedAgentFile
	// contains filtered or unexported fields
}

AgentRegistry discovers, resolves, and manages sub-agent definitions. It implements Claude Code's priority-based resolution: when multiple agents share the same Name, the highest priority (lowest number) wins.

Discovery paths (in priority order, highest first):

  • ~/.overkill/agents/managed/ (PriorityManaged)
  • .overkill/agents/ (PriorityProject)
  • ~/.overkill/agents/ (PriorityUser)

Built-in agents (PriorityBuiltin) are registered programmatically and overridden by any file-based agent with the same name.

func NewAgentRegistry

func NewAgentRegistry(projectDir, userDir string) *AgentRegistry

NewAgentRegistry creates a registry that scans the given directories. projectDir is typically <workdir>/.overkill/agents/. userDir is typically ~/.overkill/agents/.

func (*AgentRegistry) FilterByMCPServers

func (r *AgentRegistry) FilterByMCPServers(availableServers []string) []AgentDef

FilterByMCPServers returns only agents whose RequiredMCPServers are all available. Agents with no requirements always pass. Matches OpenClaude's filterAgentsByMcpRequirements.

func (*AgentRegistry) Get

func (r *AgentRegistry) Get(name string) *AgentDef

Get returns the resolved agent definition for the given name, or nil if no agent is registered.

func (*AgentRegistry) List

func (r *AgentRegistry) List() []AgentDef

List returns all resolved agent definitions, sorted by priority then name.

func (*AgentRegistry) RegisterBuiltin

func (r *AgentRegistry) RegisterBuiltin(def AgentDef)

RegisterBuiltin registers a hardcoded agent at PriorityBuiltin. File-based agents with the same name will override it.

func (*AgentRegistry) RegisterCLI

func (r *AgentRegistry) RegisterCLI(def AgentDef)

RegisterCLI registers a session-only agent from --agents JSON at PriorityCLIFlag. These override everything except managed agents.

func (*AgentRegistry) Scan

func (r *AgentRegistry) Scan() error

Scan discovers agent files recursively in all configured directories and registers them at their respective priority levels. Lower-priority entries for the same name are silently replaced.

func (*AgentRegistry) SelectBest

func (r *AgentRegistry) SelectBest(taskDescription string) *AgentDef

SelectBest finds the agent whose Description best matches the given task description. Returns nil if no agent matches well enough. Simple keyword-overlap scoring for now — embed-based matching can be added later when the embedding bridge is available.

type AgentRunner

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

AgentRunner spawns and manages sub-agents with full OpenClaude parity. Handles: model cascade, permission mode cascade, effort override, slim context for read-only agents, initial prompt injection, and background auto-deny.

func NewAgentRunner

func NewAgentRunner(cfg SpawnConfig) (*AgentRunner, error)

NewAgentRunner creates a runner from a SpawnConfig. Resolves effective model, max steps, and permission mode using OpenClaude's cascade logic.

func (*AgentRunner) AgentID

func (r *AgentRunner) AgentID() string

AgentID returns the unique identifier for this agent run.

func (*AgentRunner) AgentName

func (r *AgentRunner) AgentName() string

AgentName returns the agent definition name.

func (*AgentRunner) Run

func (r *AgentRunner) Run(ctx context.Context) (*Result, error)

Run executes the sub-agent and returns the result. For async agents without permission prompts, auto-deny is active.

func (*AgentRunner) StartedAt

func (r *AgentRunner) StartedAt() time.Time

StartedAt returns when the agent was spawned.

type AuditEntry

type AuditEntry struct {
	Timestamp time.Time `json:"ts"`
	Tool      string    `json:"tool"`
	Path      string    `json:"path,omitempty"`
	Allowed   bool      `json:"allowed"`
	Reason    string    `json:"reason,omitempty"`
	ArgsLen   int       `json:"args_len"`
}

AuditEntry is one row of the per-child contract-audit log.

type AuditWriter

type AuditWriter interface {
	Write(entry AuditEntry) error
	Path() string
}

AuditWriter persists AuditEntry rows. Returns the path it wrote to (if any).

type AutonomousConfig

type AutonomousConfig struct {
	Contract *Contract
	Driver   StepDriver
	Workdir  string
	// Runner overrides AcceptanceRunner; defaults to DefaultAcceptanceRunner.
	Runner AcceptanceRunner
	// Handoff, if non-nil, receives one StatusReport on handoff/exhaustion.
	// Buffered channel recommended (size 1).
	Handoff chan StatusReport
}

AutonomousConfig configures an AutonomousRunner.

type AutonomousRunner

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

AutonomousRunner drives a StepDriver against a Contract.

func NewAutonomousRunner

func NewAutonomousRunner(cfg AutonomousConfig) (*AutonomousRunner, error)

NewAutonomousRunner constructs a runner. Validates the contract.

func (*AutonomousRunner) Cancel

func (r *AutonomousRunner) Cancel()

Cancel marks the runner as cancelled. Run loop checks on each iteration.

func (*AutonomousRunner) FinalReport

func (r *AutonomousRunner) FinalReport() *FinalReport

FinalReport returns the terminal report once Run has finished. Nil while the runner is still active.

func (*AutonomousRunner) Run

func (r *AutonomousRunner) Run(ctx context.Context) (rep *FinalReport, err error)

Run drives the StepDriver to completion. It NEVER asks the user for input; when the contract cannot be satisfied autonomously it returns a FinalReport describing why.

func (*AutonomousRunner) Status

func (r *AutonomousRunner) Status() StatusReport

Status returns a copy of the current StatusReport.

type BudgetSnapshot

type BudgetSnapshot struct {
	CurrentTokens int
	MaxTokens     int
}

BudgetSnapshot is the runner's view of the child's context budget.

func (BudgetSnapshot) Utilization

func (b BudgetSnapshot) Utilization() float64

Utilization is convenience math for the snapshot.

type ChildRef

type ChildRef struct {
	ID        string
	Goal      string
	Model     string
	Status    string
	StartedAt time.Time
	Cancel    context.CancelFunc
	Result    *Result
	Depth     int
	Role      Role
}

ChildRef is a live handle to a spawned sub-agent.

type CompactionTask

type CompactionTask struct {
	Messages      []providers.Message
	TargetTokens  int
	OverrideModel string
}

CompactionTask summarizes conversation history into a concise summary.

func (*CompactionTask) Context

func (t *CompactionTask) Context() string

func (*CompactionTask) Goal

func (t *CompactionTask) Goal() string

func (*CompactionTask) MaxIterations

func (t *CompactionTask) MaxIterations() int

func (*CompactionTask) Model

func (t *CompactionTask) Model() string

func (*CompactionTask) Toolset

func (t *CompactionTask) Toolset() []string

func (*CompactionTask) Validate

func (t *CompactionTask) Validate() error

type Config

type Config struct {
	MaxDepth     int
	MaxChildren  int
	ChildTimeout time.Duration
	CurrentDepth int // fixed parent depth; concurrent children are siblings

	// MaxTasksPerChild is the soft limit on how many tasks can be
	// dispatched in a single SpawnBatch call. When the input exceeds
	// this, SpawnBatchAutoSplit automatically splits into sequential
	// batches. Defaults to MaxChildren when unset (0).
	MaxTasksPerChild int
}

Config controls the limits and behaviour of a Manager.

type ContextExport

type ContextExport struct {
	SessionID       string        `json:"session_id"`
	Goal            string        `json:"goal"`
	Context         ExportContext `json:"context"`
	OverkillVersion string        `json:"overkill_version"`
}

ContextExport is the JSON envelope for cross-agent delegation. It carries session metadata plus the filtered context payload.

func ContextExportFromJSON

func ContextExportFromJSON(data string) (ContextExport, error)

ContextExportFromJSON unmarshals a JSON string into a ContextExport.

func (ContextExport) FilterSecrets

func (e ContextExport) FilterSecrets() ContextExport

FilterSecrets returns a copy of the ContextExport with secrets redacted from all string fields, including top-level Goal and SessionID.

func (ContextExport) ToJSON

func (e ContextExport) ToJSON() (string, error)

ToJSON marshals the ContextExport to a JSON string.

type ContextRef

type ContextRef struct {
	Type  ContextRefKind `json:"type"`
	Value string         `json:"value"`
}

ContextRef is one input the parent passes to the child.

type ContextRefKind

type ContextRefKind string

ContextRefKind classifies an Inputs entry the parent passes down.

const (
	CtxRefFile        ContextRefKind = "file"
	CtxRefPriorResult ContextRefKind = "prior_result"
	CtxRefSpec        ContextRefKind = "spec"
	CtxRefNote        ContextRefKind = "note"
)

type Contract

type Contract struct {
	ID                string              `json:"id"`
	ParentSession     string              `json:"parent_session"`
	Goal              string              `json:"goal"`
	Scope             []string            `json:"scope"`
	OutOfScope        []string            `json:"out_of_scope"`
	Inputs            []ContextRef        `json:"inputs"`
	ExpectedOutputs   []Output            `json:"expected_outputs"`
	IntegrationPoints []Integration       `json:"integration_points,omitempty"`
	Acceptance        []AcceptanceCheck   `json:"acceptance"`
	Budget            ContractBudget      `json:"budget"`
	OnContextFull     OnContextFullAction `json:"on_context_full"`
}

Contract is the frozen agreement between parent and child.

func (*Contract) CheckScope

func (c *Contract) CheckScope(path string) (allowed bool, reason string)

CheckScope reports whether the given filesystem path is allowed for write under this contract. Out-of-scope patterns take precedence over scope. An empty Scope means "no scope restriction" (everything allowed except out_of_scope). If both are empty, all writes are allowed.

func (*Contract) RunAcceptance

func (c *Contract) RunAcceptance(ctx context.Context, workdir string, runner AcceptanceRunner) []AcceptanceResult

RunAcceptance executes every acceptance check against workdir and reports per-check outcomes. Never returns nil; an empty slice means no checks.

func (*Contract) Validate

func (c *Contract) Validate() error

Validate enforces well-formedness invariants. Returns the first failure encountered; callers should fix one error at a time.

type ContractBudget

type ContractBudget struct {
	Tokens      int           `json:"tokens"`
	Steps       int           `json:"steps"`
	WallSeconds time.Duration `json:"wall_seconds"`
}

ContractBudget caps a child's resource consumption.

type ContractViolation

type ContractViolation struct {
	Criterion string `json:"criterion"`
	Reason    string `json:"reason"`
	Evidence  string `json:"evidence,omitempty"`
}

ContractViolation describes a criterion the child failed to satisfy.

func (ContractViolation) Error

func (v ContractViolation) Error() string

type CostRollup

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

CostRollup folds child agent token/cost spend into an aggregated total. All methods are safe for concurrent use.

func NewCostRollup

func NewCostRollup(sessionID string) *CostRollup

NewCostRollup creates a new CostRollup associated with the given session.

func (*CostRollup) AddChild

func (c *CostRollup) AddChild(result *Result)

AddChild folds one child's token usage and cost into the rollup. Passing nil is a safe no-op.

func (*CostRollup) Summary

func (c *CostRollup) Summary() RollupSummary

Summary returns a snapshot of the aggregated totals.

type Decomposer

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

Decomposer splits a natural-language multi-item goal into discrete tasks. Uses separator patterns and heuristics — no LLM call. When a user dumps "fix X in package A, also wire Y in package B, and then refactor Z" this extracts the individual items so they can be dispatched as parallel sub-agents instead of overwhelming one with a giant context window.

This is intentionally self-contained in the subagent package (no dependency on internal/agent) so it can be called from the delegation path without creating a circular import.

func NewDecomposer

func NewDecomposer() *Decomposer

NewDecomposer returns a Decomposer with sensible defaults.

func (*Decomposer) Decompose

func (d *Decomposer) Decompose(input string) []string

Decompose splits input into discrete task descriptions. Returns nil if fewer than 2 items are found (single-item input doesn't need decomposition).

type EnforcedTool

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

EnforcedTool wraps a Tool and gates its Execute call through a Contract.

func NewEnforcedTool

func NewEnforcedTool(inner Tool, contract *Contract, audit AuditWriter, violCh chan<- ContractViolation) *EnforcedTool

NewEnforcedTool wraps inner with contract enforcement. violCh, if non-nil, receives every denied call (best-effort, non-blocking).

func (*EnforcedTool) Execute

func (e *EnforcedTool) Execute(ctx context.Context, input json.RawMessage) (json.RawMessage, error)

func (*EnforcedTool) Name

func (e *EnforcedTool) Name() string

type ExportContext

type ExportContext struct {
	FilesModified       []string `json:"files_modified,omitempty"`
	RecentChanges       string   `json:"recent_changes,omitempty"`
	ProjectStructure    string   `json:"project_structure,omitempty"`
	Constraints         string   `json:"constraints,omitempty"`
	Language            string   `json:"language,omitempty"`
	RelatedConversation string   `json:"related_conversation,omitempty"`
}

ExportContext holds the structured context passed between agents during delegation.

type ExternalDelegator

type ExternalDelegator struct {

	// Registry, when set, provides file-based agent discovery and
	// auto-selection via SelectBest. Populated at boot time.
	Registry *AgentRegistry
	// contains filtered or unexported fields
}

ExternalDelegator manages registration and delegation of tasks to external agent processes. All public methods are safe for concurrent use.

func NewExternalDelegator

func NewExternalDelegator(workDir string, timeout time.Duration, fileState *FileStateTracker) *ExternalDelegator

NewExternalDelegator creates a delegator that runs external agents in workDir with the given default timeout. If fileState is nil, a new tracker is created.

func (*ExternalDelegator) Delegate

func (d *ExternalDelegator) Delegate(ctx context.Context, agentName, goal string) (*Result, error)

Delegate sends a goal to the named external agent. If agentName is empty and a Registry is configured, SelectBest is used for auto-selection. It is a convenience wrapper around DelegateWithExport using a minimal ContextExport.

func (*ExternalDelegator) DelegateWithExport

func (d *ExternalDelegator) DelegateWithExport(ctx context.Context, agentName string, export ContextExport) (*Result, error)

DelegateWithExport sends a filtered ContextExport to the named external agent process, waits for it to finish (or timeout), and returns the result.

Steps:

  1. Look up agent by name → error if not found
  2. exec.LookPath(agent.Command) → error if not in PATH
  3. Call export.FilterSecrets() then export.ToJSON()
  4. Create context with timeout
  5. Build exec.CommandContext(ctx, agent.Command, append(agent.Args, inputData)...)
  6. Set cmd.Dir to workDir
  7. Capture stdout/stderr via bytes.Buffer
  8. Run command
  9. On error: check ctx.Err() for DeadlineExceeded → "timeout", Canceled → "interrupted", else "failed"
  10. Return Result with Summary=stdout, status/exitReason set accordingly

func (*ExternalDelegator) ListAgents

func (d *ExternalDelegator) ListAgents() []AgentDef

ListAgents returns all registered agent definitions in an indeterminate order. When a Registry is configured, it returns the registry's resolved list (which includes file-based, built-in, and CLI agents).

func (*ExternalDelegator) Register

func (d *ExternalDelegator) Register(def AgentDef)

Register adds an external agent definition. If an agent with the same name already exists it is replaced.

type FailedAgentFile

type FailedAgentFile struct {
	Path  string
	Error string
}

FailedAgentFile records a file that looked like an agent definition but couldn't be parsed. Matches OpenClaude's failedFiles pattern.

type FileStateTracker

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

FileStateTracker tracks which files each task (agent) reads and writes. It detects conflicts when a child agent modifies files the parent previously read. All methods are safe for concurrent use by multiple goroutines.

func NewFileStateTracker

func NewFileStateTracker() *FileStateTracker

NewFileStateTracker creates a new, ready-to-use FileStateTracker.

func (*FileStateTracker) KnownReads

func (t *FileStateTracker) KnownReads(taskID string) []string

KnownReads returns a copy of all normalized file paths that taskID has read. Returns an empty (non-nil) slice for unknown tasks.

func (*FileStateTracker) RecordRead

func (t *FileStateTracker) RecordRead(taskID, path string)

RecordRead records that taskID read the file at path. The path is normalized to an absolute path via filepath.Abs.

func (*FileStateTracker) RecordWrite

func (t *FileStateTracker) RecordWrite(taskID, path string)

RecordWrite records that taskID wrote to the file at path. The path is normalized to an absolute path via filepath.Abs.

func (*FileStateTracker) WritesByTask

func (t *FileStateTracker) WritesByTask(taskID string) []string

WritesByTask returns a copy of all normalized file paths that taskID has written to. Returns an empty (non-nil) slice for unknown tasks.

func (*FileStateTracker) WritesSince

func (t *FileStateTracker) WritesSince(taskID string, since time.Time, knownReads []string) []string

WritesSince returns the intersection of knownReads and the files written by taskID since the given time. This detects conflicts: files that the parent read and the child wrote. Pass time.Time{} (zero value) to include all writes regardless of timestamp.

type FinalReport

type FinalReport struct {
	ContractID string              `json:"contract_id"`
	Status     string              `json:"status"` // completed | violated | exhausted | handed_off | failed
	Outputs    []OutputResult      `json:"outputs"`
	Acceptance []AcceptanceResult  `json:"acceptance"`
	Violations []ContractViolation `json:"violations,omitempty"`
	TokensUsed int                 `json:"tokens_used"`
	StepsUsed  int                 `json:"steps_used"`
	Elapsed    time.Duration       `json:"elapsed"`
	Reason     string              `json:"reason,omitempty"`
	LastOutput string              `json:"last_output,omitempty"`
}

FinalReport is the terminal outcome of a Run.

type GenericTask

type GenericTask struct {
	GoalStr     string
	ContextStr  string
	ToolsetVal  []string
	ModelVal    string
	MaxStepsVal int
}

GenericTask is a general-purpose task that satisfies the Task interface. It is used by the delegate_task tool to spawn arbitrary sub-agent work.

func (GenericTask) Context

func (t GenericTask) Context() string

func (GenericTask) Goal

func (t GenericTask) Goal() string

func (GenericTask) MaxIterations

func (t GenericTask) MaxIterations() int

func (GenericTask) Model

func (t GenericTask) Model() string

func (GenericTask) Toolset

func (t GenericTask) Toolset() []string

func (GenericTask) Validate

func (t GenericTask) Validate() error

type HandoffFailureSink

type HandoffFailureSink interface {
	OnDelegationFailure(parentSession string, contract *Contract, report *FinalReport, err error)
}

HandoffFailureSink receives a structured record of every cross-agent failure so the parent's journal can attribute the fault to the delegation decision, not just to the child.

type Integration

type Integration struct {
	Description string `json:"description"`
	Reference   string `json:"reference,omitempty"`
}

Integration captures an interface the child's output must conform to.

type Manager

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

Manager coordinates sub-agent spawning with depth/capacity limits and parallel execution. All public methods are safe for concurrent use.

func NewManager

func NewManager(cfg Config) *Manager

NewManager creates a Manager with sensible defaults.

Defaults: MaxDepth=2, MaxChildren=3, ChildTimeout=120s (when <= 0).

func (*Manager) ActiveChildren

func (m *Manager) ActiveChildren() []ChildRef

ActiveChildren returns a snapshot of currently running children. Safe for concurrent use; the returned slice is owned by the caller.

func (*Manager) ActiveCount

func (m *Manager) ActiveCount() int

ActiveCount returns the number of currently running children (Spawn path).

func (*Manager) AutonomousCancel

func (m *Manager) AutonomousCancel(id string) bool

AutonomousCancel signals the child runner to stop and unblocks AutonomousWait. Returns false when no such child exists.

func (*Manager) AutonomousList

func (m *Manager) AutonomousList() []string

AutonomousList returns the IDs of all registered contract-driven children.

func (*Manager) AutonomousReport

func (m *Manager) AutonomousReport(id string) (report *FinalReport, running bool, err error)

AutonomousReport returns the FinalReport once the child has finished. When running is true, the child is still in flight.

func (*Manager) AutonomousStatus

func (m *Manager) AutonomousStatus(id string) (StatusReport, bool)

AutonomousStatus returns the live status of a contract-driven child. The boolean is false when no such child exists.

func (*Manager) AutonomousWait

func (m *Manager) AutonomousWait(ctx context.Context, id string) (*FinalReport, error)

AutonomousWait blocks until the child finishes, or ctx is cancelled.

func (*Manager) CostSummary

func (m *Manager) CostSummary() RollupSummary

CostSummary returns a snapshot of the aggregated child costs.

func (*Manager) FileState

func (m *Manager) FileState() *FileStateTracker

FileState returns the manager's file-state tracker.

func (*Manager) HasDriverFactory

func (m *Manager) HasDriverFactory() bool

HasDriverFactory reports whether a factory has been configured.

func (*Manager) SetDriverFactory

func (m *Manager) SetDriverFactory(f func(*Contract) (StepDriver, error))

SetDriverFactory wires a per-contract StepDriver builder. cmd/overkill calls this once at startup with a closure that constructs a clean child agent.

func (*Manager) SetFailureSink

func (m *Manager) SetFailureSink(s HandoffFailureSink)

SetFailureSink wires a delegation-failure observer (master plan §5.3). Pass nil to clear.

func (*Manager) SetRegistry

func (m *Manager) SetRegistry(r *AgentRegistry)

SetRegistry wires an agent registry for file-based agent discovery and auto-selection. Pass nil to clear.

func (*Manager) SetRouter

func (m *Manager) SetRouter(r TaskRouter)

SetRouter wires a task-level model router. Pass nil to clear.

func (*Manager) SetWorkerFactory

func (m *Manager) SetWorkerFactory(factory WorkerFactory)

SetWorkerFactory overrides worker construction. Passing nil restores the production text-only worker. Tool-capable delegation uses SetDriverFactory.

func (*Manager) Spawn

func (m *Manager) Spawn(ctx context.Context, task Task) (*Result, error)

Spawn executes a single task. Initialization and execution failures return a failed Result and a non-nil error; no simulated work runs in production.

func (*Manager) SpawnBatch

func (m *Manager) SpawnBatch(ctx context.Context, tasks []Task) ([]*Result, error)

SpawnBatch validates all tasks before dispatch and reserves capacity for the whole batch. Independent siblings finish even if one fails. Results retain their input indices; the returned error joins every indexed child failure.

func (*Manager) SpawnBatchAutoSplit

func (m *Manager) SpawnBatchAutoSplit(ctx context.Context, tasks []Task) ([]*Result, error)

SpawnBatchAutoSplit is like SpawnBatch but automatically splits oversized task lists into sequential batches. When len(tasks) > batchSize(), tasks are sliced into batches of batchSize(), each run via SpawnBatch in order, and results are concatenated. This prevents single sub-agents from hitting max_iterations when the parent dumps 9+ tasks on them.

func (*Manager) SpawnContract

func (m *Manager) SpawnContract(ctx context.Context, contract *Contract, driver StepDriver, workdir string, runner AcceptanceRunner) (string, error)

SpawnContract registers a contract-driven autonomous child and runs it in a background goroutine. Returns the contract ID immediately; the parent calls Status / Wait / Cancel against that ID.

Workdir, if non-empty, is the working directory passed to AcceptanceRunner. Runner overrides the default acceptance runner (useful in tests).

func (*Manager) SpawnDecomposed

func (m *Manager) SpawnDecomposed(ctx context.Context, goal, contextStr string) ([]*Result, error)

SpawnDecomposed handles a single goal string that may contain multiple independent items. It decomposes the goal via Decomposer — if 2+ items are found, they're dispatched as parallel sub-agents through SpawnBatchAutoSplit. If only one item (or decomposition fails), it falls back to a normal single-task Spawn.

This is the smart delegation path: instead of dumping "fix X in package A, also wire Y in package B, and refactor Z" onto one overwhelmed sub-agent, the Decomposer splits it into 3 discrete tasks that run in parallel.

func (*Manager) SpawnFromFactory

func (m *Manager) SpawnFromFactory(ctx context.Context, c *Contract, workdir string) (string, error)

SpawnFromFactory uses the configured factory to build a driver and then SpawnContract under it. Returns an error if no factory is set.

type ModelHint

type ModelHint struct {
	NeedsTools  bool
	NeedsVision bool
}

ModelHint describes the capabilities a built-in agent needs. Used by BuiltinAgents to resolve the cheapest capable model via the router.

type ModelResolver

type ModelResolver func(hint ModelHint) (modelID, provider string)

ModelResolver returns a model ID and provider for the given hint. The router implementation picks the cheapest model matching the capabilities.

type OnContextFullAction

type OnContextFullAction string

OnContextFullAction tells the autonomous runner what to do when its child's context budget approaches the configured cap.

const (
	OnContextFullCompactAndContinue OnContextFullAction = "compact_and_continue"
	OnContextFullHandoffToParent    OnContextFullAction = "handoff_to_parent"
	OnContextFullFail               OnContextFullAction = "fail"
)

type Output

type Output struct {
	Kind OutputKind `json:"kind"`
	Spec string     `json:"spec"`
}

Output is one concrete deliverable the child must produce.

type OutputKind

type OutputKind string

OutputKind classifies an ExpectedOutputs entry.

const (
	OutFile     OutputKind = "file"
	OutSymbol   OutputKind = "symbol"
	OutBehavior OutputKind = "behavior"
	OutTest     OutputKind = "test"
)

type OutputResult

type OutputResult struct {
	Spec      string `json:"spec"`
	Kind      string `json:"kind"`
	Satisfied bool   `json:"satisfied"`
}

OutputResult is the per-ExpectedOutput verdict.

type PersonalityUpdateTask

type PersonalityUpdateTask struct {
	SessionMessages []providers.Message
	CurrentLevel    personality.Level
	AgentName       string
}

PersonalityUpdateTask analyzes conversation for relationship milestones.

func (*PersonalityUpdateTask) Context

func (t *PersonalityUpdateTask) Context() string

func (*PersonalityUpdateTask) Goal

func (t *PersonalityUpdateTask) Goal() string

func (*PersonalityUpdateTask) MaxIterations

func (t *PersonalityUpdateTask) MaxIterations() int

func (*PersonalityUpdateTask) Model

func (t *PersonalityUpdateTask) Model() string

func (*PersonalityUpdateTask) Toolset

func (t *PersonalityUpdateTask) Toolset() []string

func (*PersonalityUpdateTask) Validate

func (t *PersonalityUpdateTask) Validate() error

type Protocol

type Protocol int

Protocol determines how the host communicates with an external agent process.

const (
	ProtocolStdio Protocol = iota
	ProtocolACP
	ProtocolPipe
)

type RealWorker

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

RealWorker performs one text-only completion from the supplied context. It cannot inspect the workspace or execute tools; those tasks require a contract StepDriver configured by the application.

func NewRealWorker

func NewRealWorker(cfg RealWorkerConfig) (*RealWorker, error)

NewRealWorker creates a RealWorker using the available API key for the provider. Falls back to XIAOMI_API_KEY for "xiaomi"; supports DEEPSEEK_API_KEY for "deepseek". Returns an error when no key is available for the requested provider.

func (*RealWorker) Run

func (w *RealWorker) Run(ctx context.Context) (*Result, error)

Run executes a text-only task against the configured provider. Errors are returned directly; worker simulation exists only in explicitly injected tests.

type RealWorkerConfig

type RealWorkerConfig struct {
	Tools     []string // explicit tool capabilities requested by this task
	Goal      string
	Context   string
	MaxSteps  int
	Timeout   time.Duration
	Model     string // from SmartRouter.Route().ModelID or fallback "mimo-v2-pro"
	Provider  string // from SmartRouter.Route().Provider or fallback "xiaomi"
	TaskIndex int    // index assigned by the manager for result tracking
	MaxTokens int    // completion limit override; 0 uses model/provider default
}

RealWorkerConfig configures a real LLM-backed worker.

type Result

type Result struct {
	TaskIndex    int         `json:"task_index"`
	Status       string      `json:"status"`
	Summary      string      `json:"summary"`
	Error        string      `json:"error,omitempty"`
	TokensIn     int64       `json:"tokens_in"`
	TokensOut    int64       `json:"tokens_out"`
	CostUSD      float64     `json:"cost_usd"`
	DurationMs   int64       `json:"duration_ms"`
	ToolTrace    []ToolEntry `json:"tool_trace,omitempty"`
	FilesRead    []string    `json:"files_read,omitempty"`
	FilesWritten []string    `json:"files_written,omitempty"`
	ExitReason   string      `json:"exit_reason"`
}

Result captures the outcome of a single sub-agent task execution. This is the authoritative definition — cost.go references it.

type Role

type Role int

Role determines whether a sub-agent is a worker or an orchestrator.

const (
	RoleWorker Role = iota
	RoleOrchestrator
)

type RollupSummary

type RollupSummary struct {
	ChildrenCount int
	TotalIn       int64
	TotalOut      int64
	TotalCost     float64
}

RollupSummary is a point-in-time snapshot of aggregated child agent costs.

type SessionNameTask

type SessionNameTask struct {
	FirstMessage string
	MaxTokens    int
}

SessionNameTask generates a concise session title from the first message.

func (*SessionNameTask) Context

func (t *SessionNameTask) Context() string

func (*SessionNameTask) Goal

func (t *SessionNameTask) Goal() string

func (*SessionNameTask) MaxIterations

func (t *SessionNameTask) MaxIterations() int

func (*SessionNameTask) Model

func (t *SessionNameTask) Model() string

func (*SessionNameTask) Toolset

func (t *SessionNameTask) Toolset() []string

func (*SessionNameTask) Validate

func (t *SessionNameTask) Validate() error

type SpawnConfig

type SpawnConfig struct {
	// Agent definition (from registry or built-in).
	Agent AgentDef

	// Prompt messages — the goal/context for this agent run.
	Goal    string
	Context string

	// Whether this is an async/background agent. Async agents
	// auto-deny permission prompts since there's no UI.
	IsAsync bool

	// When true, the agent CAN show permission prompts even if async.
	CanShowPermissionPrompts bool

	// Forked context from the parent conversation.
	ForkContext string

	// Model override from the caller.
	ModelOverride    string
	ProviderOverride string

	// Max turns/steps override.
	MaxStepsOverride int

	// Max tokens override.
	MaxTokensOverride int

	// Timeout override.
	TimeoutOverride time.Duration

	// Tool whitelist for this agent session (agent.Tools if empty).
	AllowedTools []string

	// Worktree path if agent has isolation: "worktree".
	WorktreePath string

	// Parent agent ID for hierarchy tracking.
	ParentAgentID string

	// Task index for result tracking.
	TaskIndex int
}

SpawnConfig holds all parameters for spawning a sub-agent with full Claude Code / OpenClaude parity. Every field that OpenClaude's runAgent accepts is represented here.

type StatusReport

type StatusReport struct {
	ContractID            string              `json:"contract_id"`
	Done                  []string            `json:"done"`
	Pending               []string            `json:"pending"`
	Violations            []ContractViolation `json:"violations,omitempty"`
	TokensUsed            int                 `json:"tokens_used"`
	StepsUsed             int                 `json:"steps_used"`
	Elapsed               time.Duration       `json:"elapsed"`
	NextRecommendedAction string              `json:"next_recommended_action"`
}

StatusReport is a mid-flight snapshot of the runner.

type StepDriver

type StepDriver interface {
	// Step performs exactly one unit of work. Implementations should not
	// loop internally — the runner owns the iteration.
	Step(ctx context.Context) StepResult
	// Budget returns a current snapshot of token usage.
	Budget() BudgetSnapshot
	// Compact attempts to reduce context to fit. Implementations should
	// return the post-compaction BudgetSnapshot.
	Compact(ctx context.Context) (BudgetSnapshot, error)
	// CheckOutput returns the list of ExpectedOutputs (by Spec) that have
	// been satisfied so far. Implementations may use simple file-existence
	// checks for OutFile, or model-side heuristics for other kinds.
	CheckOutput(ctx context.Context, outputs []Output) (satisfiedSpecs []string)
}

StepDriver is the small interface AutonomousRunner consumes. Real agents (internal/agent.Agent) adapt to this via a thin shim; tests provide fakes.

type StepResult

type StepResult struct {
	// ToolCalls executed during the step (used for step counting).
	ToolCalls int
	// Tokens used (cumulative or delta — runner only inspects the
	// CurrentTokens snapshot from BudgetSnapshot).
	Tokens int
	// Done is true when the underlying agent reports task completion (e.g. a
	// final assistant turn with no further tool calls).
	Done bool
	// Output is the assistant text produced this step (last seen).
	Output string
	// Err, if non-nil, is a per-step error. Tool-level ContractViolations
	// should be returned as an error implementing the violation interface
	// so the runner can surface them in FinalReport.Violations.
	Err error
}

StepResult is the agent-agnostic outcome of one driver step.

type Task

type Task interface {
	Goal() string
	Context() string
	Toolset() []string
	Model() string
	MaxIterations() int
	Validate() error
}

Task is the interface every sub-agent task must implement.

type TaskRouter

type TaskRouter interface {
	RouteTask(ctx context.Context, goal, contextStr string) (modelID, provider string, ok bool)
}

TaskRouter picks a model for a sub-agent task. Satisfied by *routing.SmartRouter via a thin adapter at the wiring site. Lives here (not in routing) to avoid import cycles:

agent → subagent → routing → agent  ✗

type Tool

type Tool interface {
	Name() string
	Execute(ctx context.Context, input json.RawMessage) (json.RawMessage, error)
}

Tool is the minimal tool surface the enforcer wraps. Defined locally to avoid an import cycle with internal/tools (which already depends on this package via the delegate tool).

type ToolEntry

type ToolEntry struct {
	Tool       string `json:"tool"`
	ArgsLen    int    `json:"args_len"`
	Path       string `json:"path,omitempty"`
	Success    bool   `json:"success"`
	Error      string `json:"error,omitempty"`
	InputHash  string `json:"input_hash,omitempty"`
	OutputHash string `json:"output_hash,omitempty"`
}

ToolEntry records a single tool invocation during task execution.

type ToolWithPath

type ToolWithPath interface {
	IntendedPath(input json.RawMessage) string
}

ToolWithPath is a small interface that, if implemented by a Tool, lets the enforcer skip the JSON-decode dance and ask the tool directly which path it intends to mutate. Optional — most tools won't implement it.

type VisualDebugTask

type VisualDebugTask struct {
	ScreenshotPath string
	ExpectedPath   string
}

VisualDebugTask compares two screenshots and reports visual differences.

func (*VisualDebugTask) Context

func (t *VisualDebugTask) Context() string

func (*VisualDebugTask) Goal

func (t *VisualDebugTask) Goal() string

func (*VisualDebugTask) MaxIterations

func (t *VisualDebugTask) MaxIterations() int

func (*VisualDebugTask) Model

func (t *VisualDebugTask) Model() string

func (*VisualDebugTask) Toolset

func (t *VisualDebugTask) Toolset() []string

func (*VisualDebugTask) Validate

func (t *VisualDebugTask) Validate() error

type WorkerFactory

type WorkerFactory func(RealWorkerConfig) (WorkerRunner, error)

WorkerFactory builds a fresh runner for each task. Supplying a factory is an explicit dependency override; provider failures never select another runner.

type WorkerRunner

type WorkerRunner interface {
	Run(context.Context) (*Result, error)
}

WorkerRunner executes one task and must honor context cancellation. Production managers use RealWorker. Tests can inject a deterministic implementation.

Jump to

Keyboard shortcuts

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