app

package
v0.6.0 Latest Latest
Warning

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

Go to latest
Published: Jan 24, 2026 License: MIT Imports: 13 Imported by: 0

Documentation

Overview

Package app provides the main application structure.

Index

Constants

View Source
const (
	// DefaultMaxIterations is the maximum agent loop iterations before stopping.
	DefaultMaxIterations = 50

	// DefaultMaxTokens is the token budget for a single agent run.
	DefaultMaxTokens = 100000

	// DefaultLoopTimeout is the maximum duration for the entire loop.
	DefaultLoopTimeout = 30 * time.Minute

	// DefaultShutdownTimeout is the maximum time to wait for shutdown hooks.
	DefaultShutdownTimeout = 30 * time.Second
)

Loop configuration defaults.

View Source
const (
	// DefaultSubagentMaxTokens is the token budget for a subagent.
	DefaultSubagentMaxTokens = 100000

	// DefaultMaxConcurrentSubagents limits parallel subagent execution.
	DefaultMaxConcurrentSubagents = 5

	// DefaultMaxSubagents limits total subagents that can be spawned.
	DefaultMaxSubagents = 100

	// DefaultGlobalTokenBudget limits total tokens across all running subagents.
	// 1M tokens prevents OOM from 100 agents × 100k tokens scenarios.
	DefaultGlobalTokenBudget = 1_000_000
)

Subagent configuration defaults.

View Source
const DefaultReservationTimeout = 5 * time.Minute

DefaultReservationTimeout is the maximum time to wait for token reservation.

Variables

View Source
var ErrAllAgentsFailed = errors.New("all subagents failed")

ErrAllAgentsFailed is returned when all subagents fail execution.

View Source
var ErrBudgetClosed = errors.New("token budget closed")

ErrBudgetClosed indicates the token budget was closed.

View Source
var ErrMaxSubagentsReached = errors.New("maximum subagents reached")

ErrMaxSubagentsReached is returned when spawning would exceed the limit.

View Source
var ErrReservationTimeout = errors.New("token reservation timed out")

ErrReservationTimeout indicates token reservation timed out.

View Source
var ErrTokenBudgetExhausted = errors.New("global token budget exhausted")

ErrTokenBudgetExhausted is returned when the global token budget cannot accommodate a request.

Functions

func AggregateTokens

func AggregateTokens(results map[string]*SubagentResult) int

AggregateTokens sums token usage across results.

func FilterResults

func FilterResults(results map[string]*SubagentResult, successOnly bool) map[string]*SubagentResult

FilterResults filters subagent results by success status.

func MergeResults

func MergeResults(results map[string]*SubagentResult) []any

MergeResults combines outputs from multiple subagent results.

func StuckHint added in v0.5.0

func StuckHint(patternType string) string

StuckHint is a convenience function that returns guidance for a pattern type string. Accepts: "repeat_attempt", "oscillation", "flaky_test"

Types

type Action

type Action struct {
	Type         string         // "tool_call", "response", "delegate"
	ToolName     string         // For tool calls
	ToolInput    map[string]any // Tool arguments
	Response     string         // For direct responses
	Subagent     string         // For delegation
	SubagentTask string
}

Action represents an action to take.

type AgentLoop

type AgentLoop interface {
	// GatherContext collects relevant context for the current iteration.
	GatherContext(ctx context.Context, state *LoopState) (*LoopContext, error)

	// DecideAction determines what action to take based on context.
	DecideAction(ctx context.Context, state *LoopState) (*Action, error)

	// TakeAction executes the decided action.
	TakeAction(ctx context.Context, action *Action) (*Result, error)

	// Verify validates the result and provides feedback.
	Verify(ctx context.Context, state *LoopState) (*Feedback, error)

	// ShouldContinue determines if the loop should continue.
	ShouldContinue(state *LoopState) bool
}

AgentLoop defines the core agent loop interface. Pattern: gather context → take action → verify → repeat

type App

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

App represents the main application.

Lifecycle: App can be Run() multiple times if WithClient() was used to provide an externally-managed client. If the client is created internally (no WithClient), it will be closed after each Run() and recreated on the next call.

func New

func New(name, version string, opts ...Option) *App

New creates a new App with the given name and version.

func (*App) Client

func (a *App) Client() client.Client

Client returns the AI client.

func (*App) Config

func (a *App) Config() *config.Config

Config returns the application configuration.

func (*App) Output

func (a *App) Output() *output.Dispatcher

Output returns the output dispatcher.

func (*App) RootCmd

func (a *App) RootCmd() *cobra.Command

RootCmd returns the root cobra command.

func (*App) Run

func (a *App) Run() error

Run executes the application.

func (*App) Tools

func (a *App) Tools() *tools.Registry

Tools returns the tool registry.

type Feedback

type Feedback struct {
	Valid    bool
	Issues   []string
	Warnings []string
	Score    float64 // 0.0 to 1.0
}

Feedback represents verification feedback.

type LoopConfig

type LoopConfig struct {
	// MaxIterations limits loop cycles (safety). 0 = unlimited.
	MaxIterations int

	// MaxTokens limits total tokens before compaction.
	MaxTokens int

	// Timeout for the entire loop.
	Timeout time.Duration

	// ShutdownTimeout is the maximum time to wait for shutdown hooks.
	// Default: 30 seconds.
	ShutdownTimeout time.Duration

	// StopOnError halts the loop on first error.
	StopOnError bool

	// MinScore minimum verification score to continue.
	MinScore float64

	// StuckDetection enables automatic stuck pattern detection.
	// If nil, stuck detection is disabled.
	StuckDetection *StuckConfig

	// Hooks for extensibility
	OnIterationStart func(state *LoopState)
	OnIterationEnd   func(state *LoopState)
	OnError          func(err error, state *LoopState)

	// OnStuckPattern is called when a stuck pattern is detected.
	// Return true to continue the loop, false to stop.
	// If nil and a pattern is detected, the loop stops with an error.
	OnStuckPattern func(pattern *StuckPattern, state *LoopState) bool
}

LoopConfig configures the agent loop behavior.

func DefaultLoopConfig

func DefaultLoopConfig() *LoopConfig

DefaultLoopConfig returns sensible defaults.

type LoopContext

type LoopContext struct {
	Messages   []Message
	Tools      []ToolInfo
	State      map[string]any
	TokenCount int
}

LoopContext holds gathered context for an iteration.

type LoopRunner

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

LoopRunner executes an agent loop.

func NewLoopRunner

func NewLoopRunner(loop AgentLoop, config *LoopConfig) *LoopRunner

NewLoopRunner creates a new loop runner.

func (*LoopRunner) OnShutdown

func (r *LoopRunner) OnShutdown(hook ShutdownHook)

OnShutdown registers a hook to be called during graceful shutdown. Hooks are called in reverse registration order (LIFO). The hook receives a context with the shutdown timeout applied.

func (*LoopRunner) Run

func (r *LoopRunner) Run(ctx context.Context) (state *LoopState, err error)

Run executes the agent loop until completion or limit. Shutdown hooks are called when the loop exits (for any reason).

type LoopState

type LoopState struct {
	Iteration   int
	Context     *LoopContext
	LastAction  *Action
	LastResult  *Result
	LastVerify  *Feedback
	StartedAt   time.Time
	CompletedAt time.Time
}

LoopState represents the current state of an agent loop iteration.

type Message

type Message struct {
	Role    string
	Content string
}

Message represents a conversation message.

type Option

type Option func(*App)

Option is a functional option for configuring App.

func WithClient

func WithClient(c client.Client) Option

WithClient sets a custom client (useful for testing).

func WithModel

func WithModel(model string) Option

WithModel sets the AI model.

func WithOutputFormat

func WithOutputFormat(formatter output.Formatter) Option

WithOutputFormat registers a custom output formatter.

func WithProvider

func WithProvider(provider string) Option

WithProvider sets the AI provider.

func WithRunFunc

func WithRunFunc(fn RunFunc) Option

WithRunFunc sets the custom run function.

func WithSDKOption

func WithSDKOption(opt claude.ClientOption) Option

WithSDKOption adds a Claude SDK client option.

func WithSystemPrompt

func WithSystemPrompt(prompt string) Option

WithSystemPrompt sets the system prompt.

func WithTool

func WithTool(tool *tools.Tool) Option

WithTool adds a tool to the registry.

type Result

type Result struct {
	Success bool
	Output  any
	Error   error
	Tokens  int
}

Result represents the outcome of an action.

type RunFunc

type RunFunc func(ctx context.Context, app *App, args []string) error

RunFunc is the main application run function.

type ShutdownHook

type ShutdownHook func(ctx context.Context) error

ShutdownHook is a function called during graceful shutdown. The context has the shutdown timeout applied.

type SimpleLoop

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

SimpleLoop provides a basic AgentLoop implementation.

func NewSimpleLoop

func NewSimpleLoop(opts ...SimpleLoopOption) *SimpleLoop

NewSimpleLoop creates a configurable simple loop.

func (*SimpleLoop) DecideAction

func (l *SimpleLoop) DecideAction(ctx context.Context, state *LoopState) (*Action, error)

DecideAction implements AgentLoop.

func (*SimpleLoop) GatherContext

func (l *SimpleLoop) GatherContext(ctx context.Context, state *LoopState) (*LoopContext, error)

GatherContext implements AgentLoop.

func (*SimpleLoop) ShouldContinue

func (l *SimpleLoop) ShouldContinue(state *LoopState) bool

ShouldContinue implements AgentLoop.

func (*SimpleLoop) TakeAction

func (l *SimpleLoop) TakeAction(ctx context.Context, action *Action) (*Result, error)

TakeAction implements AgentLoop.

func (*SimpleLoop) Verify

func (l *SimpleLoop) Verify(ctx context.Context, state *LoopState) (*Feedback, error)

Verify implements AgentLoop.

type SimpleLoopOption

type SimpleLoopOption func(*SimpleLoop)

SimpleLoopOption configures a SimpleLoop.

func WithActionFunc

func WithActionFunc(fn func(ctx context.Context, action *Action) (*Result, error)) SimpleLoopOption

WithActionFunc sets the action execution function.

func WithContinueFunc

func WithContinueFunc(fn func(state *LoopState) bool) SimpleLoopOption

WithContinueFunc sets the continuation check function.

func WithDecideFunc

func WithDecideFunc(fn func(ctx context.Context, state *LoopState) (*Action, error)) SimpleLoopOption

WithDecideFunc sets the action decision function.

func WithGatherFunc

func WithGatherFunc(fn func(ctx context.Context, state *LoopState) (*LoopContext, error)) SimpleLoopOption

WithGatherFunc sets the context gathering function.

func WithVerifyFunc

func WithVerifyFunc(fn func(ctx context.Context, state *LoopState) (*Feedback, error)) SimpleLoopOption

WithVerifyFunc sets the verification function.

type StuckConfig added in v0.5.0

type StuckConfig struct {
	// RepeatThreshold is how many identical errors trigger repeat_attempt pattern.
	// Default: 3
	RepeatThreshold int

	// OscillationWindow is how many results to check for oscillation.
	// Default: 4
	OscillationWindow int

	// ErrorHistorySize is how many errors to retain per task.
	// Default: 5
	ErrorHistorySize int

	// PassHistorySize is how many pass/fail results to retain.
	// Default: 6
	PassHistorySize int
}

StuckConfig holds detection thresholds.

func DefaultStuckConfig added in v0.5.0

func DefaultStuckConfig() *StuckConfig

DefaultStuckConfig returns sensible defaults.

type StuckDetector added in v0.5.0

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

StuckDetector identifies stuck patterns from task history.

func NewStuckDetector added in v0.5.0

func NewStuckDetector(cfg *StuckConfig) *StuckDetector

NewStuckDetector creates a detector with the given config. If cfg is nil, defaults are used.

func (*StuckDetector) Hint added in v0.5.0

func (d *StuckDetector) Hint(patternType StuckPatternType) string

Hint returns detailed guidance for a stuck pattern type.

func (*StuckDetector) RecordError added in v0.5.0

func (d *StuckDetector) RecordError(taskID, normalizedError string) *StuckPattern

RecordError records an error for a task and checks for repeat pattern. Returns a StuckPattern if detected, nil otherwise.

func (*StuckDetector) RecordResult added in v0.5.0

func (d *StuckDetector) RecordResult(taskID string, passed bool) *StuckPattern

RecordResult records a pass/fail result and checks for oscillation. Returns a StuckPattern if detected, nil otherwise.

func (*StuckDetector) Reset added in v0.5.0

func (d *StuckDetector) Reset(taskID string)

Reset clears history for a task.

func (*StuckDetector) ResetAll added in v0.5.0

func (d *StuckDetector) ResetAll()

ResetAll clears all task history.

type StuckPattern added in v0.5.0

type StuckPattern struct {
	Type    StuckPatternType
	TaskID  string
	Message string
}

StuckPattern represents a detected stuck pattern.

type StuckPatternType added in v0.5.0

type StuckPatternType int

StuckPatternType categorizes stuck patterns.

const (
	// PatternRepeatAttempt indicates the same error occurred repeatedly.
	PatternRepeatAttempt StuckPatternType = iota
	// PatternOscillation indicates alternating pass/fail results.
	PatternOscillation
	// PatternFlakyTest indicates unreliable test results.
	PatternFlakyTest
)

func (StuckPatternType) String added in v0.5.0

func (t StuckPatternType) String() string

type Subagent

type Subagent struct {
	ID      string
	Name    string
	Task    string
	Context *SubagentContext
	Result  *SubagentResult
	// contains filtered or unexported fields
}

Subagent represents an isolated child agent with its own context.

type SubagentConfig

type SubagentConfig struct {
	// MaxConcurrent limits parallel subagent execution.
	MaxConcurrent int

	// MaxSubagents limits total subagents that can be spawned (0 = unlimited).
	MaxSubagents int

	// IsolateContext creates fresh context per subagent.
	IsolateContext bool

	// ShareTools allows subagents to access parent tools.
	ShareTools bool

	// PropagateCancel cancels children when parent cancels.
	PropagateCancel bool

	// GlobalTokenBudget limits total tokens across all running subagents.
	// When set, subagents must reserve tokens before execution.
	// Use WithGlobalTokenBudget to configure.
	GlobalTokenBudget *TokenBudget

	// WaitForTokens determines behavior when budget is exhausted.
	// If true, Spawn/Run blocks until tokens available.
	// If false, returns ErrTokenBudgetExhausted immediately.
	WaitForTokens bool
}

SubagentConfig configures subagent behavior.

func DefaultSubagentConfig

func DefaultSubagentConfig() *SubagentConfig

DefaultSubagentConfig returns sensible defaults.

func (*SubagentConfig) ApplyOptions added in v0.3.0

func (c *SubagentConfig) ApplyOptions(opts ...SubagentConfigOption)

ApplyOptions applies configuration options to a SubagentConfig.

type SubagentConfigOption added in v0.3.0

type SubagentConfigOption func(*SubagentConfig)

SubagentConfigOption modifies a SubagentConfig.

func WithGlobalTokenBudget added in v0.3.0

func WithGlobalTokenBudget(tokens int64) SubagentConfigOption

WithGlobalTokenBudget sets a global token limit across all running subagents. This prevents OOM from scenarios like 100 agents × 100k tokens = 10M tokens.

func WithWaitForTokens added in v0.3.0

func WithWaitForTokens(wait bool) SubagentConfigOption

WithWaitForTokens configures whether to block when budget is exhausted. If true, Run blocks until tokens are available. If false, returns ErrTokenBudgetExhausted immediately.

type SubagentContext

type SubagentContext struct {
	Messages     []Message
	Tools        []ToolInfo
	SystemPrompt string
	State        map[string]any
	MaxTokens    int
}

SubagentContext holds isolated context for a subagent.

type SubagentExecutor

type SubagentExecutor interface {
	Execute(ctx context.Context, agent *Subagent) (*SubagentResult, error)
}

SubagentExecutor defines how to run a subagent.

type SubagentExecutorFunc

type SubagentExecutorFunc func(ctx context.Context, agent *Subagent) (*SubagentResult, error)

SubagentExecutorFunc is a function adapter for SubagentExecutor.

func (SubagentExecutorFunc) Execute

func (f SubagentExecutorFunc) Execute(ctx context.Context, agent *Subagent) (*SubagentResult, error)

type SubagentManager

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

SubagentManager coordinates multiple subagents.

Graceful shutdown behavior:

  • When a context passed to RunAll/RunAgents is cancelled, all running subagents receive cancellation through their context.
  • RunAll returns promptly after cancellation; it does not wait for subagents to finish their current work.
  • The Shutdown method can be used to cancel all running subagents spawned by this manager.
  • Executors MUST respect context cancellation for graceful shutdown to work.

func NewSubagentManager

func NewSubagentManager(config *SubagentConfig, executor SubagentExecutor) *SubagentManager

NewSubagentManager creates a new subagent manager.

func (*SubagentManager) Clear

func (m *SubagentManager) Clear()

Clear removes all subagents.

func (*SubagentManager) Close added in v0.6.0

func (m *SubagentManager) Close() error

Close releases resources and wakes any blocked goroutines waiting on token budget. This should be called during graceful shutdown after Shutdown(). Safe to call multiple times.

func (*SubagentManager) Get

func (m *SubagentManager) Get(id string) *Subagent

Get retrieves a subagent by ID.

func (*SubagentManager) List

func (m *SubagentManager) List() []*Subagent

List returns all subagents.

func (*SubagentManager) Run

func (m *SubagentManager) Run(ctx context.Context, agent *Subagent) (*SubagentResult, error)

Run executes a single subagent.

func (*SubagentManager) RunAgents

func (m *SubagentManager) RunAgents(ctx context.Context, agents ...*Subagent) (map[string]*SubagentResult, error)

RunAgents executes specific subagents concurrently with graceful shutdown support.

Context cancellation behavior: - When ctx is cancelled, all running subagents receive cancellation immediately - RunAgents returns promptly; it does not wait for subagents to complete - Subagents that were cancelled will have Error set to context.Canceled - Results collected before cancellation are still returned

The executor MUST respect context cancellation for this to work properly.

func (*SubagentManager) RunAll

func (m *SubagentManager) RunAll(ctx context.Context) (map[string]*SubagentResult, error)

RunAll executes all spawned subagents concurrently.

func (*SubagentManager) Running added in v0.3.0

func (m *SubagentManager) Running() int

Running returns the number of currently executing subagents.

func (*SubagentManager) Shutdown added in v0.3.0

func (m *SubagentManager) Shutdown()

Shutdown cancels all running subagents and cleans up resources.

This method is safe to call from a signal handler or concurrent goroutine. It cancels all in-flight subagent executions by invoking their context cancel functions. Executors that respect context cancellation will stop promptly; those that don't may continue until they naturally complete.

After Shutdown returns: - All cancel functions have been invoked - The cancel function map is cleared - Subagent definitions remain (call Clear() to remove them)

Shutdown is idempotent; calling it multiple times is safe.

func (*SubagentManager) Spawn

func (m *SubagentManager) Spawn(name, task string, opts ...SubagentOption) (*Subagent, error)

Spawn creates a new subagent with isolated context. Returns nil and ErrMaxSubagentsReached if the limit is exceeded.

func (*SubagentManager) TokenBudgetStatus added in v0.3.0

func (m *SubagentManager) TokenBudgetStatus() (total, reserved, available int64)

TokenBudgetStatus returns the current token budget state. Returns (total, reserved, available). All zeros if no budget configured.

type SubagentOption

type SubagentOption func(*Subagent)

SubagentOption configures a subagent.

func WithSubagentMaxTokens

func WithSubagentMaxTokens(max int) SubagentOption

WithSubagentMaxTokens sets the token limit.

func WithSubagentMessages

func WithSubagentMessages(messages []Message) SubagentOption

WithSubagentMessages sets initial messages.

func WithSubagentPrompt

func WithSubagentPrompt(prompt string) SubagentOption

WithSubagentPrompt sets the subagent's system prompt.

func WithSubagentState

func WithSubagentState(state map[string]any) SubagentOption

WithSubagentState sets initial state.

func WithSubagentTools

func WithSubagentTools(tools []ToolInfo) SubagentOption

WithSubagentTools sets available tools.

type SubagentResult

type SubagentResult struct {
	Success bool
	Output  any
	Error   error
	Tokens  int
}

SubagentResult contains the outcome of a subagent's work.

type TokenBudget added in v0.3.0

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

TokenBudget manages a global token pool with thread-safe reservation and release. It prevents OOM by limiting total concurrent token usage across all subagents.

func NewTokenBudget added in v0.3.0

func NewTokenBudget(total int64) *TokenBudget

NewTokenBudget creates a budget pool with the specified total tokens.

func (*TokenBudget) Available added in v0.3.0

func (tb *TokenBudget) Available() int64

Available returns the number of tokens currently available.

func (*TokenBudget) Close added in v0.6.0

func (tb *TokenBudget) Close() error

Close releases all waiting goroutines and prevents new reservations. Should be called during graceful shutdown. Safe to call multiple times.

func (*TokenBudget) Release added in v0.3.0

func (tb *TokenBudget) Release(tokens int64)

Release returns tokens to the pool and wakes any waiting reservations.

func (*TokenBudget) Reserve added in v0.3.0

func (tb *TokenBudget) Reserve(tokens int64) bool

Reserve attempts to reserve tokens from the pool. Returns true if reservation succeeded, false if insufficient budget or closed.

func (*TokenBudget) ReserveWait added in v0.3.0

func (tb *TokenBudget) ReserveWait(ctx context.Context, tokens int64) error

ReserveWait blocks until tokens are available, then reserves them. Returns an error if: - The context is cancelled while waiting - The reservation times out (DefaultReservationTimeout) - The budget is closed

func (*TokenBudget) Reserved added in v0.3.0

func (tb *TokenBudget) Reserved() int64

Reserved returns the number of tokens currently reserved.

func (*TokenBudget) Total added in v0.3.0

func (tb *TokenBudget) Total() int64

Total returns the total budget capacity.

type ToolInfo

type ToolInfo struct {
	Name        string
	Description string
}

ToolInfo describes an available tool.

Jump to

Keyboard shortcuts

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