Documentation
¶
Overview ¶
Package app provides the main application structure.
Index ¶
- Constants
- Variables
- func AggregateTokens(results map[string]*SubagentResult) int
- func FilterResults(results map[string]*SubagentResult, successOnly bool) map[string]*SubagentResult
- func MergeResults(results map[string]*SubagentResult) []any
- func StuckHint(patternType string) string
- type Action
- type AgentLoop
- type App
- type Feedback
- type LoopConfig
- type LoopContext
- type LoopRunner
- type LoopState
- type Message
- type Option
- func WithClient(c client.Client) Option
- func WithModel(model string) Option
- func WithOutputFormat(formatter output.Formatter) Option
- func WithProvider(provider string) Option
- func WithRunFunc(fn RunFunc) Option
- func WithSDKOption(opt claude.ClientOption) Option
- func WithSystemPrompt(prompt string) Option
- func WithTool(tool *tools.Tool) Option
- type Result
- type RunFunc
- type ShutdownHook
- type SimpleLoop
- func (l *SimpleLoop) DecideAction(ctx context.Context, state *LoopState) (*Action, error)
- func (l *SimpleLoop) GatherContext(ctx context.Context, state *LoopState) (*LoopContext, error)
- func (l *SimpleLoop) ShouldContinue(state *LoopState) bool
- func (l *SimpleLoop) TakeAction(ctx context.Context, action *Action) (*Result, error)
- func (l *SimpleLoop) Verify(ctx context.Context, state *LoopState) (*Feedback, error)
- type SimpleLoopOption
- func WithActionFunc(fn func(ctx context.Context, action *Action) (*Result, error)) SimpleLoopOption
- func WithContinueFunc(fn func(state *LoopState) bool) SimpleLoopOption
- func WithDecideFunc(fn func(ctx context.Context, state *LoopState) (*Action, error)) SimpleLoopOption
- func WithGatherFunc(fn func(ctx context.Context, state *LoopState) (*LoopContext, error)) SimpleLoopOption
- func WithVerifyFunc(fn func(ctx context.Context, state *LoopState) (*Feedback, error)) SimpleLoopOption
- type StuckConfig
- type StuckDetector
- func (d *StuckDetector) Hint(patternType StuckPatternType) string
- func (d *StuckDetector) RecordError(taskID, normalizedError string) *StuckPattern
- func (d *StuckDetector) RecordResult(taskID string, passed bool) *StuckPattern
- func (d *StuckDetector) Reset(taskID string)
- func (d *StuckDetector) ResetAll()
- type StuckPattern
- type StuckPatternType
- type Subagent
- type SubagentConfig
- type SubagentConfigOption
- type SubagentContext
- type SubagentExecutor
- type SubagentExecutorFunc
- type SubagentManager
- func (m *SubagentManager) Clear()
- func (m *SubagentManager) Close() error
- func (m *SubagentManager) Get(id string) *Subagent
- func (m *SubagentManager) List() []*Subagent
- func (m *SubagentManager) Run(ctx context.Context, agent *Subagent) (*SubagentResult, error)
- func (m *SubagentManager) RunAgents(ctx context.Context, agents ...*Subagent) (map[string]*SubagentResult, error)
- func (m *SubagentManager) RunAll(ctx context.Context) (map[string]*SubagentResult, error)
- func (m *SubagentManager) Running() int
- func (m *SubagentManager) Shutdown()
- func (m *SubagentManager) Spawn(name, task string, opts ...SubagentOption) (*Subagent, error)
- func (m *SubagentManager) TokenBudgetStatus() (total, reserved, available int64)
- type SubagentOption
- type SubagentResult
- type TokenBudget
- func (tb *TokenBudget) Available() int64
- func (tb *TokenBudget) Close() error
- func (tb *TokenBudget) Release(tokens int64)
- func (tb *TokenBudget) Reserve(tokens int64) bool
- func (tb *TokenBudget) ReserveWait(ctx context.Context, tokens int64) error
- func (tb *TokenBudget) Reserved() int64
- func (tb *TokenBudget) Total() int64
- type ToolInfo
Constants ¶
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.
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.
const DefaultReservationTimeout = 5 * time.Minute
DefaultReservationTimeout is the maximum time to wait for token reservation.
Variables ¶
var ErrAllAgentsFailed = errors.New("all subagents failed")
ErrAllAgentsFailed is returned when all subagents fail execution.
var ErrBudgetClosed = errors.New("token budget closed")
ErrBudgetClosed indicates the token budget was closed.
var ErrMaxSubagentsReached = errors.New("maximum subagents reached")
ErrMaxSubagentsReached is returned when spawning would exceed the limit.
var ErrReservationTimeout = errors.New("token reservation timed out")
ErrReservationTimeout indicates token reservation timed out.
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.
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 (*App) Output ¶
func (a *App) Output() *output.Dispatcher
Output returns the output dispatcher.
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 ¶
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.
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 Option ¶
type Option func(*App)
Option is a functional option for configuring App.
func WithClient ¶
WithClient sets a custom client (useful for testing).
func WithOutputFormat ¶
WithOutputFormat registers a custom output formatter.
func WithSDKOption ¶
func WithSDKOption(opt claude.ClientOption) Option
WithSDKOption adds a Claude SDK client option.
func WithSystemPrompt ¶
WithSystemPrompt sets the system prompt.
type ShutdownHook ¶
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 ¶
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 ¶
TakeAction implements AgentLoop.
type SimpleLoopOption ¶
type SimpleLoopOption func(*SimpleLoop)
SimpleLoopOption configures a SimpleLoop.
func WithActionFunc ¶
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 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) 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 ¶
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.