Documentation
¶
Overview ¶
Package shared provides common flag types and options for loop subcommands.
Index ¶
- Constants
- func AddLoopFlags(cmd *cobra.Command, opts *LoopOptions)
- func ApplyLoopConfigDefaults(loopOpts *LoopOptions, flags *pflag.FlagSet, cfg *config.LoopConfig)
- func BuildSystemPrompt(additional string) string
- func CountCompletionIndicators(output string) int
- func DefaultHookFiles() map[string][]byte
- func DetectRateLimitError(output string) bool
- func ExtractErrorSignature(output string) string
- func GenerateAgentName() string
- func InjectLoopHooks(ctx context.Context, cfgGateway config.Config, containerID string, ...) error
- func MakeCreateContainerFunc(cfg *LoopContainerConfig) func(context.Context) (*ContainerStartConfig, error)
- func MarkVerboseExclusive(cmd *cobra.Command)
- func NewTextAccumulator() (*TextAccumulator, *StreamHandler)
- func ResolveLoopImage(ctx context.Context, client *docker.Client, ios *iostreams.IOStreams, ...) (string, error)
- func ResolvePrompt(prompt, promptFile string) (string, error)
- func ResolveTasksPrompt(tasksFile, taskPrompt, taskPromptFile string) (string, error)
- func WireLoopDashboard(opts *Options, ch chan<- LoopDashEvent, setup *LoopContainerResult, ...)
- func WriteResult(out, errOut io.Writer, result *Result, format *cmdutil.FormatFlags) error
- type AnalysisResult
- type AssistantEvent
- type AssistantMessage
- type CircuitBreaker
- func (cb *CircuitBreaker) Check() bool
- func (cb *CircuitBreaker) ConsecutiveTestLoops() int
- func (cb *CircuitBreaker) IsTripped() bool
- func (cb *CircuitBreaker) NoProgressCount() int
- func (cb *CircuitBreaker) Reset()
- func (cb *CircuitBreaker) RestoreState(state CircuitBreakerState)
- func (cb *CircuitBreaker) SameErrorCount() int
- func (cb *CircuitBreaker) State() CircuitBreakerState
- func (cb *CircuitBreaker) Threshold() int
- func (cb *CircuitBreaker) TripReason() string
- func (cb *CircuitBreaker) Update(status *Status) (tripped bool, reason string)
- func (cb *CircuitBreaker) UpdateWithAnalysis(status *Status, analysis *AnalysisResult) UpdateResult
- type CircuitBreakerConfig
- type CircuitBreakerState
- type CircuitHistory
- type CircuitHistoryEntry
- type CircuitState
- type CompactMetadata
- type ConcurrencyAction
- type ConcurrencyCheckConfig
- type Config
- func (c Config) GetCallsPerHour() int
- func (c Config) GetCompletionThreshold() int
- func (c Config) GetLoopDelaySeconds() int
- func (c Config) GetMaxConsecutiveTestLoops() int
- func (c Config) GetMaxLoops() int
- func (c Config) GetOutputDeclineThreshold() int
- func (c Config) GetSafetyCompletionThreshold() int
- func (c Config) GetSameErrorThreshold() int
- func (c Config) GetSessionExpirationHours() int
- func (c Config) GetStagnationThreshold() int
- func (c Config) Timeout() time.Duration
- type ContainerStartConfig
- type ContentBlock
- type EventType
- type HistoryStore
- func (h *HistoryStore) AddCircuitEntry(project, agent, fromState, toState, reason string, ...) error
- func (h *HistoryStore) AddSessionEntry(project, agent, event, status, errorMsg string, loopCount int) error
- func (h *HistoryStore) DeleteCircuitHistory(project, agent string) error
- func (h *HistoryStore) DeleteSessionHistory(project, agent string) error
- func (h *HistoryStore) LoadCircuitHistory(project, agent string) (*CircuitHistory, error)
- func (h *HistoryStore) LoadSessionHistory(project, agent string) (*SessionHistory, error)
- func (h *HistoryStore) SaveCircuitHistory(history *CircuitHistory) error
- func (h *HistoryStore) SaveSessionHistory(history *SessionHistory) error
- type HookConfig
- type HookHandler
- type HookMatcherGroup
- type LoopContainerConfig
- type LoopContainerResult
- type LoopDashEvent
- type LoopDashEventKind
- type LoopDashboardConfig
- type LoopDashboardResult
- type LoopOptions
- type Monitor
- func (m *Monitor) FormatAPILimitError(isInteractive bool) string
- func (m *Monitor) FormatLoopEnd(loopNum int, status *Status, err error, outputSize int, elapsed time.Duration) string
- func (m *Monitor) FormatLoopProgress(loopNum int, status *Status, circuit *CircuitBreaker) string
- func (m *Monitor) FormatLoopStart(loopNum int) string
- func (m *Monitor) FormatRateLimitWait(resetTime time.Time) string
- func (m *Monitor) FormatResult(result *Result) string
- func (m *Monitor) PrintLoopEnd(loopNum int, status *Status, err error, outputSize int, elapsed time.Duration)
- func (m *Monitor) PrintLoopProgress(loopNum int, status *Status, circuit *CircuitBreaker)
- func (m *Monitor) PrintLoopStart(loopNum int)
- func (m *Monitor) PrintResult(result *Result)
- type MonitorOptions
- type Options
- type OutputKind
- type RateLimitState
- type RateLimiter
- func (r *RateLimiter) Allow() bool
- func (r *RateLimiter) CallCount() int
- func (r *RateLimiter) IsEnabled() bool
- func (r *RateLimiter) Limit() int
- func (r *RateLimiter) Record()
- func (r *RateLimiter) Remaining() int
- func (r *RateLimiter) ResetTime() time.Time
- func (r *RateLimiter) RestoreState(state RateLimitState) bool
- func (r *RateLimiter) State() RateLimitState
- type Result
- type ResultEvent
- type ResultOutput
- type RunLoopConfig
- type Runner
- func (r *Runner) GetCircuitState(project, agent string) (*CircuitState, error)
- func (r *Runner) GetSession(project, agent string) (*Session, error)
- func (r *Runner) ResetCircuit(project, agent string) error
- func (r *Runner) ResetSession(project, agent string) error
- func (r *Runner) Run(ctx context.Context, opts Options) (*Result, error)
- func (r *Runner) StartContainer(ctx context.Context, log *logger.Logger, projectCfg *config.Project, ...) (string, *ResultEvent, int, error)
- type Session
- type SessionHistory
- type SessionHistoryEntry
- type SessionStore
- func (s *SessionStore) DeleteCircuitState(project, agent string) error
- func (s *SessionStore) DeleteSession(project, agent string) error
- func (s *SessionStore) ListSessions(project string) ([]*Session, error)
- func (s *SessionStore) LoadCircuitState(project, agent string) (*CircuitState, error)
- func (s *SessionStore) LoadSession(project, agent string) (*Session, error)
- func (s *SessionStore) LoadSessionWithExpiration(project, agent string, expirationHours int) (*Session, bool, error)
- func (s *SessionStore) SaveCircuitState(state *CircuitState) error
- func (s *SessionStore) SaveSession(session *Session) error
- type Status
- type StreamAPIEvent
- type StreamContentBlock
- type StreamDelta
- type StreamDeltaEvent
- type StreamHandler
- type StreamMessageDelta
- type SystemEvent
- type TextAccumulator
- type TokenUsage
- type UpdateResult
- type UserEvent
- type UserEventMessage
Constants ¶
const ( WorkTypeImplementation = "IMPLEMENTATION" WorkTypeTesting = "TESTING" WorkTypeDocumentation = "DOCUMENTATION" WorkTypeRefactoring = "REFACTORING" )
Work type constants.
const ( EventStop = "Stop" EventSessionStart = "SessionStart" EventPreToolUse = "PreToolUse" EventPostToolUse = "PostToolUse" EventNotification = "Notification" )
Hook event names matching Claude Code settings.json.
const ( HandlerCommand = "command" HandlerPrompt = "prompt" HandlerAgent = "agent" )
Hook handler types matching Claude Code settings.json.
const ( HookScriptDir = "/tmp/clawker-hooks" StopCheckScriptPath = "/tmp/clawker-hooks/stop-check.js" )
Script paths inside the container.
const ( // ReadyLogPrefix is the prefix for the ready signal log line. ReadyLogPrefix = "[clawker] ready" // ErrorLogPrefix is the prefix for error signal log lines. ErrorLogPrefix = "[clawker] error" )
const ( DefaultMaxLoops = 50 DefaultStagnationThreshold = 3 DefaultTimeoutMinutes = 15 DefaultCallsPerHour = 100 DefaultCompletionThreshold = 2 DefaultSessionExpirationHours = 24 DefaultSameErrorThreshold = 5 DefaultOutputDeclineThreshold = 70 DefaultMaxConsecutiveTestLoops = 3 DefaultSafetyCompletionThreshold = 5 // Force exit after N consecutive loops with completion indicators DefaultLoopDelaySeconds = 3 // Seconds to wait between loop iterations )
Default configuration values.
const ( SystemSubtypeInit = "init" SystemSubtypeCompactBoundary = "compact_boundary" )
System event subtypes.
const ( ResultSubtypeSuccess = "success" ResultSubtypeErrorMaxTurns = "error_max_turns" ResultSubtypeErrorDuringExecution = "error_during_execution" ResultSubtypeErrorMaxBudget = "error_max_budget_usd" )
Result event subtypes.
const ( ContentTypeText = "text" ContentTypeToolUse = "tool_use" ContentTypeToolResult = "tool_result" ContentTypeThinking = "thinking" )
Content block types within messages.
const LoopStatusInstructions = `` /* 1167-byte string literal not displayed */
LoopStatusInstructions is the default system prompt that instructs the agent to output a structured LOOP_STATUS block at the end of each response. This block is parsed by the loop engine for circuit breaker and progress tracking.
The example block within this prompt is intentionally valid — it is used in tests to verify that the prompt stays in sync with ParseStatus.
const (
// MaxHistoryEntries is the maximum number of history entries to keep.
MaxHistoryEntries = 50
)
const StatusBlocked = "BLOCKED"
StatusBlocked indicates the agent is blocked.
const StatusComplete = "COMPLETE"
StatusComplete indicates the task is complete.
const StatusPending = "IN_PROGRESS"
StatusPending indicates work is still in progress.
const TestsFailing = "FAILING"
TestsFailing indicates tests are failing.
const TestsNotRun = "NOT_RUN"
TestsNotRun indicates tests were not run.
const TestsPassing = "PASSING"
TestsPassing indicates tests are passing.
Variables ¶
This section is empty.
Functions ¶
func AddLoopFlags ¶
func AddLoopFlags(cmd *cobra.Command, opts *LoopOptions)
AddLoopFlags registers shared loop flags on the Cobra command. Call this before AddFormatFlags to ensure correct PreRunE chain ordering.
func ApplyLoopConfigDefaults ¶
func ApplyLoopConfigDefaults(loopOpts *LoopOptions, flags *pflag.FlagSet, cfg *config.LoopConfig)
ApplyLoopConfigDefaults applies config.LoopConfig values to LoopOptions fields that are consumed before the runner is created (e.g., hooks_file, append_system_prompt). Call this after loading the config but before SetupLoopContainer and BuildRunnerOptions.
func BuildSystemPrompt ¶
BuildSystemPrompt combines the default LOOP_STATUS instructions with optional additional instructions provided by the user via --append-system-prompt. If additional is empty, returns only the default instructions.
func CountCompletionIndicators ¶
CountCompletionIndicators counts how many completion phrases appear in the output.
func DefaultHookFiles ¶
DefaultHookFiles returns the scripts that default hooks reference. Keys are absolute paths inside the container; values are file contents.
func DetectRateLimitError ¶
DetectRateLimitError checks if the output contains Claude's rate limit error. Returns true if a rate limit error is detected.
func ExtractErrorSignature ¶
ExtractErrorSignature extracts a normalized error signature from output. The signature can be used to detect repeated identical errors. Returns empty string if no error pattern is found.
func GenerateAgentName ¶
func GenerateAgentName() string
GenerateAgentName creates a unique agent name for a loop session. Format: loop-<adjective>-<noun> (e.g., "loop-brave-turing"). The generated name is always a valid Docker resource name.
func InjectLoopHooks ¶
func InjectLoopHooks(ctx context.Context, cfgGateway config.Config, containerID string, hooksFile string, copyFn containershared.CopyToContainerFn, readFn containershared.CopyFromContainerFn, log *logger.Logger) error
InjectLoopHooks injects hook configuration and scripts into a created (not started) container. If hooksFile is empty, default hooks are used. If provided, the file is read as a complete replacement. Hook scripts referenced by default hooks are also injected.
Hooks are merged into the existing settings.json to preserve any pre-existing configuration (MCP servers, skills, extensions) set up by containerfs or post-init.
func MakeCreateContainerFunc ¶
func MakeCreateContainerFunc(cfg *LoopContainerConfig) func(context.Context) (*ContainerStartConfig, error)
MakeCreateContainerFunc creates a factory closure that creates a new container for each loop iteration. The returned containers are created with hooks injected but NOT started — the Runner's StartContainer handles attachment and start.
func MarkVerboseExclusive ¶
MarkVerboseExclusive marks --verbose as mutually exclusive with output format flags. Must be called after both AddLoopFlags and AddFormatFlags.
func NewTextAccumulator ¶
func NewTextAccumulator() (*TextAccumulator, *StreamHandler)
NewTextAccumulator creates a TextAccumulator and returns it along with a StreamHandler wired to collect text. Pass the handler to ParseStream.
func ResolveLoopImage ¶
func ResolveLoopImage(ctx context.Context, client *docker.Client, ios *iostreams.IOStreams, loopOpts *LoopOptions, projectName string) (string, error)
ResolveLoopImage resolves the container image for loop execution. If an image is explicitly set on loopOpts, it's returned as-is. Otherwise, the Docker client's image resolution chain is used.
func ResolvePrompt ¶
ResolvePrompt resolves the prompt from either an inline string or a file path. Exactly one of prompt or promptFile should be non-empty.
func ResolveTasksPrompt ¶
ResolveTasksPrompt builds the prompt for a task-driven loop. It reads the tasks file and combines it with either:
- the default template (if no custom prompt/file given)
- a custom inline template (taskPrompt)
- a custom template file (taskPromptFile)
Templates containing %s get the tasks substituted in; otherwise tasks are appended.
func WireLoopDashboard ¶
func WireLoopDashboard(opts *Options, ch chan<- LoopDashEvent, setup *LoopContainerResult, maxLoops int, log *logger.Logger)
WireLoopDashboard sets Runner callback options to send events on the dashboard channel. It configures OnLoopStart, OnLoopEnd, and OnOutput callbacks. It does NOT close the channel — the goroutine wrapping runner.Run() is responsible for that.
func WriteResult ¶
WriteResult writes the loop result in the appropriate format.
Types ¶
type AnalysisResult ¶
type AnalysisResult struct {
Status *Status
RateLimitHit bool
ErrorSignature string
OutputSize int
CompletionCount int
// Stream metadata (populated by AnalyzeStreamResult, zero when using AnalyzeOutput).
NumTurns int
TotalCostUSD float64
DurationMS int
}
AnalysisResult contains the full analysis of a loop's output.
func AnalyzeOutput ¶
func AnalyzeOutput(output string) *AnalysisResult
AnalyzeOutput performs full analysis of a loop's output.
func AnalyzeStreamResult ¶
func AnalyzeStreamResult(text string, result *ResultEvent) *AnalysisResult
AnalyzeStreamResult produces an AnalysisResult by combining text analysis (from TextAccumulator output) with stream ResultEvent metadata. Use this instead of AnalyzeOutput when processing --output-format stream-json output.
The text parameter should contain the concatenated assistant text from TextAccumulator.Text(). The result parameter is the terminal ResultEvent from ParseStream (may be nil if the stream ended prematurely).
type AssistantEvent ¶
type AssistantEvent struct {
Type EventType `json:"type"`
UUID string `json:"uuid,omitempty"`
SessionID string `json:"session_id"`
ParentToolUseID *string `json:"parent_tool_use_id"`
Message AssistantMessage `json:"message"`
}
AssistantEvent is a complete assistant message containing text and/or tool invocations.
type AssistantMessage ¶
type AssistantMessage struct {
ID string `json:"id"`
Role string `json:"role"`
Model string `json:"model"`
StopReason string `json:"stop_reason"`
Content []ContentBlock `json:"content"`
Usage *TokenUsage `json:"usage,omitempty"`
}
AssistantMessage is the Anthropic API message object embedded in an AssistantEvent.
func (*AssistantMessage) ExtractText ¶
func (m *AssistantMessage) ExtractText() string
ExtractText returns the concatenated text from all text content blocks.
func (*AssistantMessage) ToolUseBlocks ¶
func (m *AssistantMessage) ToolUseBlocks() []ContentBlock
ToolUseBlocks returns all tool_use content blocks from the message.
type CircuitBreaker ¶
type CircuitBreaker struct {
// contains filtered or unexported fields
}
CircuitBreaker detects stagnation in loop iterations and trips when unhealthy patterns persist. Trip conditions (any one triggers a trip):
- Stagnation: N consecutive loops without progress (threshold)
- No LOOP_STATUS: N consecutive loops where status block is missing (threshold)
- Same error: N identical error signatures in a row (sameErrorThreshold)
- Output decline: Output size shrinks >= threshold% for 2 consecutive loops
- Test-only loops: N consecutive TESTING-only work type loops (maxConsecutiveTestLoops)
- Safety completion: N consecutive loops with completion indicators but no EXIT_SIGNAL (safetyCompletionThreshold)
- Blocked status: Trips immediately when agent reports BLOCKED
func NewCircuitBreaker ¶
func NewCircuitBreaker(threshold int) *CircuitBreaker
NewCircuitBreaker creates a new circuit breaker with the given threshold.
func NewCircuitBreakerWithConfig ¶
func NewCircuitBreakerWithConfig(cfg CircuitBreakerConfig) *CircuitBreaker
NewCircuitBreakerWithConfig creates a circuit breaker with full configuration.
func (*CircuitBreaker) Check ¶
func (cb *CircuitBreaker) Check() bool
Check returns true if the circuit is still open (not tripped).
func (*CircuitBreaker) ConsecutiveTestLoops ¶
func (cb *CircuitBreaker) ConsecutiveTestLoops() int
ConsecutiveTestLoops returns the count of consecutive test-only loops.
func (*CircuitBreaker) IsTripped ¶
func (cb *CircuitBreaker) IsTripped() bool
IsTripped returns true if the circuit has tripped.
func (*CircuitBreaker) NoProgressCount ¶
func (cb *CircuitBreaker) NoProgressCount() int
NoProgressCount returns the current count of loops without progress.
func (*CircuitBreaker) Reset ¶
func (cb *CircuitBreaker) Reset()
Reset clears the circuit breaker state.
func (*CircuitBreaker) RestoreState ¶
func (cb *CircuitBreaker) RestoreState(state CircuitBreakerState)
RestoreState restores circuit breaker state from persistence.
func (*CircuitBreaker) SameErrorCount ¶
func (cb *CircuitBreaker) SameErrorCount() int
SameErrorCount returns the current count of same-error loops.
func (*CircuitBreaker) State ¶
func (cb *CircuitBreaker) State() CircuitBreakerState
State returns the current circuit breaker state for persistence.
func (*CircuitBreaker) Threshold ¶
func (cb *CircuitBreaker) Threshold() int
Threshold returns the configured stagnation threshold.
func (*CircuitBreaker) TripReason ¶
func (cb *CircuitBreaker) TripReason() string
TripReason returns the reason the circuit tripped, or empty if not tripped.
func (*CircuitBreaker) Update ¶
func (cb *CircuitBreaker) Update(status *Status) (tripped bool, reason string)
Update evaluates the status and updates the circuit breaker state. Returns true and a reason if the circuit has tripped.
func (*CircuitBreaker) UpdateWithAnalysis ¶
func (cb *CircuitBreaker) UpdateWithAnalysis(status *Status, analysis *AnalysisResult) UpdateResult
UpdateWithAnalysis evaluates the full analysis and updates circuit state. It checks all trip conditions in order: safety completion, strict completion (success path), nil status, blocked status, same-error sequence, output decline, test-only loops, and finally no-progress stagnation. Returns an UpdateResult indicating whether the circuit tripped, the reason, or whether strict completion criteria were met.
type CircuitBreakerConfig ¶
type CircuitBreakerConfig struct {
StagnationThreshold int
SameErrorThreshold int
OutputDeclineThreshold int
MaxConsecutiveTestLoops int
CompletionThreshold int
SafetyCompletionThreshold int
}
CircuitBreakerConfig holds configuration for the circuit breaker.
func DefaultCircuitBreakerConfig ¶
func DefaultCircuitBreakerConfig() CircuitBreakerConfig
DefaultCircuitBreakerConfig returns default configuration.
type CircuitBreakerState ¶
type CircuitBreakerState struct {
NoProgressCount int `json:"no_progress_count"`
Tripped bool `json:"tripped"`
TripReason string `json:"trip_reason,omitempty"`
SameErrorCount int `json:"same_error_count"`
LastErrorSignature string `json:"last_error_signature,omitempty"`
DeclineCount int `json:"decline_count"`
LastOutputSize int `json:"last_output_size"`
ConsecutiveTestLoops int `json:"consecutive_test_loops"`
ConsecutiveCompletionCount int `json:"consecutive_completion_count"`
}
CircuitBreakerState holds the state for persistence.
type CircuitHistory ¶
type CircuitHistory struct {
Project string `json:"project"`
Agent string `json:"agent"`
Entries []CircuitHistoryEntry `json:"entries"`
}
CircuitHistory tracks circuit breaker state changes.
type CircuitHistoryEntry ¶
type CircuitHistoryEntry struct {
Timestamp time.Time `json:"timestamp"`
FromState string `json:"from_state"` // closed, tripped
ToState string `json:"to_state"`
Reason string `json:"reason,omitempty"`
NoProgressCount int `json:"no_progress_count"`
SameErrorCount int `json:"same_error_count,omitempty"`
TestLoopCount int `json:"test_loop_count,omitempty"`
CompletionCount int `json:"completion_count,omitempty"`
}
CircuitHistoryEntry represents a circuit breaker state change.
type CircuitState ¶
type CircuitState struct {
Project string `json:"project"`
Agent string `json:"agent"`
NoProgressCount int `json:"no_progress_count"`
Tripped bool `json:"tripped"`
TripReason string `json:"trip_reason,omitempty"`
TrippedAt *time.Time `json:"tripped_at,omitempty"`
UpdatedAt time.Time `json:"updated_at"`
}
CircuitState represents the persistent state of the circuit breaker.
type CompactMetadata ¶
CompactMetadata describes a conversation compaction event.
type ConcurrencyAction ¶
type ConcurrencyAction int
ConcurrencyAction represents the user's chosen action after a concurrency check.
const ( // ActionProceed continues without worktree isolation. ActionProceed ConcurrencyAction = iota // ActionWorktree uses a worktree for isolation. ActionWorktree // ActionAbort cancels the operation. ActionAbort )
func CheckConcurrency ¶
func CheckConcurrency(ctx context.Context, cfg *ConcurrencyCheckConfig) (ConcurrencyAction, error)
CheckConcurrency checks for running containers in the same project and working directory. If a conflict is found, it either warns (non-interactive) or prompts the user to choose between worktree isolation, proceeding anyway, or aborting.
type ConcurrencyCheckConfig ¶
type ConcurrencyCheckConfig struct {
// Client is the Docker client for listing containers.
Client *docker.Client
// Project is the project name to check.
Project string
// WorkDir is the current absolute host working directory.
WorkDir string
// IOStreams is used for warning output.
IOStreams *iostreams.IOStreams
// Prompter returns the interactive prompter. Nil means non-interactive.
Prompter func() *prompter.Prompter
}
ConcurrencyCheckConfig holds inputs for CheckConcurrency.
type Config ¶
type Config struct {
MaxLoops int `yaml:"max_loops" mapstructure:"max_loops"`
StagnationThreshold int `yaml:"stagnation_threshold" mapstructure:"stagnation_threshold"`
TimeoutMinutes int `yaml:"timeout_minutes" mapstructure:"timeout_minutes"`
AutoConfirm bool `yaml:"auto_confirm" mapstructure:"auto_confirm"`
CallsPerHour int `yaml:"calls_per_hour" mapstructure:"calls_per_hour"`
CompletionThreshold int `yaml:"completion_threshold" mapstructure:"completion_threshold"`
SessionExpirationHours int `yaml:"session_expiration_hours" mapstructure:"session_expiration_hours"`
SameErrorThreshold int `yaml:"same_error_threshold" mapstructure:"same_error_threshold"`
OutputDeclineThreshold int `yaml:"output_decline_threshold" mapstructure:"output_decline_threshold"`
MaxConsecutiveTestLoops int `yaml:"max_consecutive_test_loops" mapstructure:"max_consecutive_test_loops"`
LoopDelaySeconds int `yaml:"loop_delay_seconds" mapstructure:"loop_delay_seconds"`
SafetyCompletionThreshold int `yaml:"safety_completion_threshold" mapstructure:"safety_completion_threshold"`
}
Config holds loop-specific configuration.
func DefaultConfig ¶
func DefaultConfig() Config
DefaultConfig returns the default loop configuration.
func (Config) GetCallsPerHour ¶
GetCallsPerHour returns the calls per hour limit with default fallback. Returns 0 to disable rate limiting.
func (Config) GetCompletionThreshold ¶
GetCompletionThreshold returns the completion indicator threshold with default fallback.
func (Config) GetLoopDelaySeconds ¶
GetLoopDelaySeconds returns the loop delay in seconds with default fallback.
func (Config) GetMaxConsecutiveTestLoops ¶
GetMaxConsecutiveTestLoops returns the max consecutive test loops with default fallback.
func (Config) GetMaxLoops ¶
GetMaxLoops returns the max loops with default fallback.
func (Config) GetOutputDeclineThreshold ¶
GetOutputDeclineThreshold returns the output decline threshold percentage with default fallback.
func (Config) GetSafetyCompletionThreshold ¶
GetSafetyCompletionThreshold returns the safety completion threshold with default fallback.
func (Config) GetSameErrorThreshold ¶
GetSameErrorThreshold returns the same error threshold with default fallback.
func (Config) GetSessionExpirationHours ¶
GetSessionExpirationHours returns the session expiration hours with default fallback.
func (Config) GetStagnationThreshold ¶
GetStagnationThreshold returns the stagnation threshold with default fallback.
type ContainerStartConfig ¶
type ContainerStartConfig struct {
ContainerID string
Cleanup func()
}
ContainerStartConfig is returned by the per-iteration container factory. It provides the container ID and a cleanup function that removes the container and its associated volumes.
type ContentBlock ¶
type ContentBlock struct {
Type string `json:"type"`
// Text block: populated when Type == "text".
Text string `json:"text,omitempty"`
// Tool use block: populated when Type == "tool_use".
ID string `json:"id,omitempty"`
Name string `json:"name,omitempty"`
Input json.RawMessage `json:"input,omitempty"`
// Tool result block: populated when Type == "tool_result".
// Content is json.RawMessage because the API sends either a string or an array.
ToolUseID string `json:"tool_use_id,omitempty"`
Content json.RawMessage `json:"content,omitempty"`
IsError bool `json:"is_error,omitempty"`
// Thinking block: populated when Type == "thinking".
Thinking string `json:"thinking,omitempty"`
}
ContentBlock is a polymorphic element in a message's content array. Discriminate on Type to determine which fields are populated.
func (*ContentBlock) ToolResultText ¶
func (b *ContentBlock) ToolResultText() string
ToolResultText extracts the tool result content as a string. Handles both the string form and the array-of-blocks form.
type HistoryStore ¶
type HistoryStore struct {
// contains filtered or unexported fields
}
HistoryStore manages history persistence.
func DefaultHistoryStore ¶
func DefaultHistoryStore() (*HistoryStore, error)
DefaultHistoryStore returns a history store using the default clawker directory.
func NewHistoryStore ¶
func NewHistoryStore(baseDir string) *HistoryStore
NewHistoryStore creates a new history store at the given base directory.
func (*HistoryStore) AddCircuitEntry ¶
func (h *HistoryStore) AddCircuitEntry(project, agent, fromState, toState, reason string, noProgressCount, sameErrorCount, testLoopCount, completionCount int) error
AddCircuitEntry adds an entry to circuit history, trimming to MaxHistoryEntries.
func (*HistoryStore) AddSessionEntry ¶
func (h *HistoryStore) AddSessionEntry(project, agent, event, status, errorMsg string, loopCount int) error
AddSessionEntry adds an entry to session history, trimming to MaxHistoryEntries.
func (*HistoryStore) DeleteCircuitHistory ¶
func (h *HistoryStore) DeleteCircuitHistory(project, agent string) error
DeleteCircuitHistory removes circuit history from disk.
func (*HistoryStore) DeleteSessionHistory ¶
func (h *HistoryStore) DeleteSessionHistory(project, agent string) error
DeleteSessionHistory removes session history from disk.
func (*HistoryStore) LoadCircuitHistory ¶
func (h *HistoryStore) LoadCircuitHistory(project, agent string) (*CircuitHistory, error)
LoadCircuitHistory loads circuit history from disk. Returns an empty history if no file exists.
func (*HistoryStore) LoadSessionHistory ¶
func (h *HistoryStore) LoadSessionHistory(project, agent string) (*SessionHistory, error)
LoadSessionHistory loads session history from disk. Returns an empty history if no file exists.
func (*HistoryStore) SaveCircuitHistory ¶
func (h *HistoryStore) SaveCircuitHistory(history *CircuitHistory) error
SaveCircuitHistory saves circuit history to disk.
func (*HistoryStore) SaveSessionHistory ¶
func (h *HistoryStore) SaveSessionHistory(history *SessionHistory) error
SaveSessionHistory saves session history to disk.
type HookConfig ¶
type HookConfig map[string][]HookMatcherGroup
HookConfig maps event names to matcher groups. This is the value of the "hooks" key in Claude Code's settings.json.
func DefaultHooks ¶
func DefaultHooks() HookConfig
DefaultHooks returns the default hook configuration for loop execution. The Stop hook blocks Claude from stopping unless a LOOP_STATUS block is present. The SessionStart hook re-injects a LOOP_STATUS reminder after context compaction.
func ResolveHooks ¶
func ResolveHooks(hooksFile string) (HookConfig, map[string][]byte, error)
ResolveHooks loads hook configuration from a file or returns defaults. If hooksFile is empty, returns DefaultHooks() and DefaultHookFiles(). If hooksFile is provided, it is read as a complete HookConfig replacement with no default hook files (custom hooks manage their own scripts).
func (HookConfig) MarshalSettingsJSON ¶
func (hc HookConfig) MarshalSettingsJSON() ([]byte, error)
MarshalSettingsJSON serializes the hook config as a settings.json fragment with the "hooks" wrapper key: {"hooks": {...}}.
func (HookConfig) Validate ¶
func (hc HookConfig) Validate() error
Validate checks the hook configuration for structural errors. It verifies that event names are non-empty, handler types are valid, required fields are populated for each handler type, timeouts are non-negative, and each matcher group has at least one hook.
type HookHandler ¶
type HookHandler struct {
Type string `json:"type"`
Command string `json:"command,omitempty"`
Prompt string `json:"prompt,omitempty"`
Timeout int `json:"timeout,omitempty"`
}
HookHandler is a single hook action matching the Claude Code settings.json format.
type HookMatcherGroup ¶
type HookMatcherGroup struct {
Matcher string `json:"matcher,omitempty"`
Hooks []HookHandler `json:"hooks"`
}
HookMatcherGroup is a matcher regex paired with one or more hook handlers.
type LoopContainerConfig ¶
type LoopContainerConfig struct {
// Client is the Docker client.
Client *docker.Client
Command []string
// Config is the config.Config interface providing Project(), Settings(), etc.
Config config.Config
// ProjectName is the resolved project name (from project.ProjectManager).
// Empty string when no project is registered.
ProjectName string
// LoopOpts holds the shared loop flags (agent, image, worktree, etc.).
LoopOpts *LoopOptions
// Flags is the command's pflag.FlagSet for Changed() detection.
Flags *pflag.FlagSet
// Version is the clawker build version.
Version string
// ProjectManager returns the project manager for worktree registration.
ProjectManager func() (project.ProjectManager, error)
// HostProxy returns the host proxy service.
HostProxy func() hostproxy.HostProxyService
// Firewall returns the firewall manager.
Firewall func(context.Context) (firewall.FirewallManager, error)
// SocketBridge returns the socket bridge manager.
SocketBridge func() socketbridge.SocketBridgeManager
// IOStreams is the I/O streams for spinner output during creation.
IOStreams *iostreams.IOStreams
// Log is the file logger for diagnostic output.
Log *logger.Logger
}
LoopContainerConfig holds all inputs needed to set up a container for loop execution.
type LoopContainerResult ¶
type LoopContainerResult struct {
// ContainerID is the Docker container ID.
ContainerID string
// ContainerName is the full container name (clawker.project.agent).
ContainerName string
// AgentName is the resolved agent name.
AgentName string
// ProjectName is the resolved project name (from project.ProjectManager).
ProjectName string
// ProjectCfg is the project configuration.
ProjectCfg *config.Project
// WorkDir is the host working directory for this session.
WorkDir string
}
LoopContainerResult holds outputs from container setup.
func SetupLoopContainer
deprecated
func SetupLoopContainer(ctx context.Context, cfg *LoopContainerConfig) (*LoopContainerResult, func(), error)
SetupLoopContainer creates, configures, and starts a container for loop execution.
Deprecated: Use ResolveLoopImage + MakeCreateContainerFunc for per-iteration containers.
The cleanup function uses context.Background() so it runs even after cancellation.
type LoopDashEvent ¶
type LoopDashEvent struct {
Kind LoopDashEventKind
Iteration int
MaxIterations int
AgentName string
Project string
// Status (populated on IterEnd)
StatusText string
TasksCompleted int
FilesModified int
TestsStatus string
ExitSignal bool
// Circuit breaker
CircuitProgress int
CircuitThreshold int
CircuitTripped bool
// Rate limiter
RateRemaining int
RateLimit int
// Timing
IterDuration time.Duration
// Completion
ExitReason string
Error error
// Session totals
TotalTasks int
TotalFiles int
// Cost/token data (populated on IterEnd from ResultEvent)
IterCostUSD float64
IterTokens int
IterTurns int
// Output
OutputChunk string
OutputKind OutputKind
}
LoopDashEvent is sent on the channel to update the dashboard.
type LoopDashEventKind ¶
type LoopDashEventKind int
LoopDashEventKind discriminates dashboard events.
const ( // LoopDashEventStart is sent once at the beginning of a loop session. LoopDashEventStart LoopDashEventKind = iota // LoopDashEventIterStart is sent when an iteration begins. LoopDashEventIterStart // LoopDashEventIterEnd is sent when an iteration completes. LoopDashEventIterEnd // LoopDashEventOutput is sent with output chunks for the streaming feed. LoopDashEventOutput // LoopDashEventRateLimit is sent when the rate limiter triggers a wait. LoopDashEventRateLimit // LoopDashEventComplete is sent when the loop session finishes. LoopDashEventComplete )
func (LoopDashEventKind) String ¶
func (k LoopDashEventKind) String() string
String returns a human-readable name for the event kind.
type LoopDashboardConfig ¶
LoopDashboardConfig configures the dashboard.
type LoopDashboardResult ¶
type LoopDashboardResult struct {
Err error // display error only
Detached bool // user pressed q/Esc — loop continues, switch to minimal output
Interrupted bool // user pressed Ctrl+C — stop the loop
}
LoopDashboardResult is returned when the dashboard exits.
func RunLoopDashboard ¶
func RunLoopDashboard(ios *iostreams.IOStreams, cfg LoopDashboardConfig, ch <-chan LoopDashEvent) LoopDashboardResult
RunLoopDashboard runs the loop dashboard display, consuming events from ch until the channel is closed. Returns when the BubbleTea program exits.
type LoopOptions ¶
type LoopOptions struct {
// Loop control
MaxLoops int
StagnationThreshold int
TimeoutMinutes int
LoopDelay int
// Circuit breaker tuning
SameErrorThreshold int
OutputDeclineThreshold int
MaxConsecutiveTestLoops int
SafetyCompletionThreshold int
CompletionThreshold int
StrictCompletion bool
// Execution
SkipPermissions bool
CallsPerHour int
ResetCircuit bool
// Hooks
HooksFile string
// System prompt
AppendSystemPrompt string
// Container — Agent is set programmatically by iterate/tasks run functions
// (auto-generated via loop.GenerateAgentName), not exposed as a CLI flag.
// Status and reset commands register their own --agent flag independently.
Agent string
Worktree string
Image string
// Output
Verbose bool
}
LoopOptions holds flags shared between loop iterate and loop tasks commands. Command-specific Options structs embed this to get the common flags.
func NewLoopOptions ¶
func NewLoopOptions() *LoopOptions
NewLoopOptions creates a LoopOptions with zero values. Defaults are applied via Cobra flag registration in AddLoopFlags.
type Monitor ¶
type Monitor struct {
// contains filtered or unexported fields
}
Monitor provides real-time progress output for loop iterations.
func NewMonitor ¶
func NewMonitor(opts MonitorOptions) *Monitor
NewMonitor creates a new monitor with the given options.
func (*Monitor) FormatAPILimitError ¶
FormatAPILimitError returns the API limit error message.
func (*Monitor) FormatLoopEnd ¶
func (m *Monitor) FormatLoopEnd(loopNum int, status *Status, err error, outputSize int, elapsed time.Duration) string
FormatLoopEnd returns the loop end message.
func (*Monitor) FormatLoopProgress ¶
func (m *Monitor) FormatLoopProgress(loopNum int, status *Status, circuit *CircuitBreaker) string
FormatLoopProgress returns the progress line for monitor mode. Format: [Loop 3/50] IN_PROGRESS | Tasks: 2 | Files: 5 | Rate: 97/100 | Circuit: 0/3
func (*Monitor) FormatLoopStart ¶
FormatLoopStart returns the loop start message.
func (*Monitor) FormatRateLimitWait ¶
FormatRateLimitWait returns the rate limit wait message.
func (*Monitor) FormatResult ¶
FormatResult returns the final result summary.
func (*Monitor) PrintLoopEnd ¶
func (m *Monitor) PrintLoopEnd(loopNum int, status *Status, err error, outputSize int, elapsed time.Duration)
PrintLoopEnd writes the loop end message.
func (*Monitor) PrintLoopProgress ¶
func (m *Monitor) PrintLoopProgress(loopNum int, status *Status, circuit *CircuitBreaker)
PrintLoopProgress writes the progress line.
func (*Monitor) PrintLoopStart ¶
PrintLoopStart writes the loop start message.
func (*Monitor) PrintResult ¶
PrintResult writes the final result summary.
type MonitorOptions ¶
type MonitorOptions struct {
// Writer is where monitor output goes (typically os.Stderr).
Writer io.Writer
// MaxLoops is the total max loops for progress calculation.
MaxLoops int
// ShowRateLimit shows rate limiter status if enabled.
ShowRateLimit bool
// RateLimiter to query for status.
RateLimiter *RateLimiter
// Verbose enables detailed output.
Verbose bool
}
MonitorOptions configures the monitor output.
type Options ¶
type Options struct {
// CreateContainer creates a new container for each iteration.
// Returns a ContainerStartConfig with the container ID and cleanup function.
CreateContainer func(ctx context.Context) (*ContainerStartConfig, error)
// ProjectCfg is the project configuration.
ProjectCfg *config.Project
// ProjectName is the project identity string used for session/circuit
// storage keys and container naming. Resolved from project.ProjectManager
// at command level; empty string when no project is registered.
ProjectName string
// Agent is the agent name.
Agent string
// Prompt is the prompt used for each iteration (-p flag).
Prompt string
// MaxLoops is the maximum number of loops to run.
MaxLoops int
// StagnationThreshold is how many loops without progress before tripping.
StagnationThreshold int
// Timeout is the per-loop timeout.
Timeout time.Duration
// ResetCircuit resets the circuit breaker before starting.
ResetCircuit bool
// CallsPerHour is the rate limit (0 to disable).
CallsPerHour int
// CompletionThreshold is the number of completion indicators required.
CompletionThreshold int
// SessionExpirationHours is the session TTL (0 for default).
SessionExpirationHours int
// SameErrorThreshold is how many same-error loops before tripping.
SameErrorThreshold int
// OutputDeclineThreshold is the percentage decline that triggers trip.
OutputDeclineThreshold int
// MaxConsecutiveTestLoops is how many test-only loops before tripping.
MaxConsecutiveTestLoops int
// LoopDelaySeconds is the delay between loop iterations.
LoopDelaySeconds int
// SafetyCompletionThreshold is loops with completion indicators but no exit signal before trip.
SafetyCompletionThreshold int
// UseStrictCompletion requires both EXIT_SIGNAL and completion indicators.
UseStrictCompletion bool
// SkipPermissions passes --dangerously-skip-permissions to claude.
SkipPermissions bool
// SystemPrompt is the full system prompt appended via --append-system-prompt.
// Built by BuildSystemPrompt() which combines the default LOOP_STATUS
// instructions with any user-provided additional instructions.
SystemPrompt string
// Monitor is the optional monitor for live output.
Monitor *Monitor
// WorkDir is the host working directory for this session.
WorkDir string
// Logger is the diagnostic file logger (from Factory).
Logger *logger.Logger
// Verbose enables verbose logging.
Verbose bool
// OnLoopStart is called before each loop iteration.
OnLoopStart func(loopNum int)
// OnLoopEnd is called after each loop iteration with optional cost data.
OnLoopEnd func(loopNum int, status *Status, resultEvent *ResultEvent, err error)
// OnOutput is called with text output chunks during execution.
OnOutput func(chunk []byte)
// OnStreamEvent is called for each raw streaming event (content_block_start,
// content_block_delta, etc.). Enables rich TUI updates like tool activity
// indicators. When set alongside OnOutput, both fire independently.
OnStreamEvent func(*StreamDeltaEvent)
// OnRateLimitHit is called when Claude's API limit is detected.
// Return true to wait and retry, false to exit.
OnRateLimitHit func() bool
}
Options configures the loop execution.
func BuildRunnerOptions ¶
func BuildRunnerOptions( loopOpts *LoopOptions, project *config.Project, projectName string, agent, prompt, workDir string, createContainer func(ctx context.Context) (*ContainerStartConfig, error), flags *pflag.FlagSet, loopCfg *config.LoopConfig, log *logger.Logger, ) Options
BuildRunnerOptions maps CLI LoopOptions + command context into Options. Config overrides are applied for any flags the user did not explicitly set.
type OutputKind ¶
type OutputKind int
OutputKind distinguishes output chunk types.
const ( // OutputText is a raw text chunk from the assistant. OutputText OutputKind = iota // OutputToolStart indicates a tool invocation began. OutputToolStart )
type RateLimitState ¶
RateLimitState represents the persistent state of the rate limiter.
type RateLimiter ¶
type RateLimiter struct {
// contains filtered or unexported fields
}
RateLimiter tracks API calls per hour and enforces a configurable limit.
func NewRateLimiter ¶
func NewRateLimiter(limit int) *RateLimiter
NewRateLimiter creates a new rate limiter with the given limit per hour. A limit of 0 or less disables rate limiting.
func (*RateLimiter) Allow ¶
func (r *RateLimiter) Allow() bool
Allow checks if a call is allowed and records it if so. Returns true if the call is allowed, false if rate limited.
func (*RateLimiter) CallCount ¶
func (r *RateLimiter) CallCount() int
CallCount returns the current number of calls in this window.
func (*RateLimiter) IsEnabled ¶
func (r *RateLimiter) IsEnabled() bool
IsEnabled returns true if rate limiting is enabled.
func (*RateLimiter) Record ¶
func (r *RateLimiter) Record()
Record manually records a call without checking the limit. Useful when a call has already been made.
func (*RateLimiter) Remaining ¶
func (r *RateLimiter) Remaining() int
Remaining returns the number of calls remaining in the current window.
func (*RateLimiter) ResetTime ¶
func (r *RateLimiter) ResetTime() time.Time
ResetTime returns when the current rate limit window will reset.
func (*RateLimiter) RestoreState ¶
func (r *RateLimiter) RestoreState(state RateLimitState) bool
RestoreState restores the rate limiter from a persisted state. Returns true if the state was actually restored, false if it was discarded due to expiration or invalid values.
func (*RateLimiter) State ¶
func (r *RateLimiter) State() RateLimitState
State returns the current state for persistence.
type Result ¶
type Result struct {
// LoopsCompleted is the number of loops that ran.
LoopsCompleted int
// FinalStatus is the last parsed status.
FinalStatus *Status
// ExitReason describes why the loop exited.
ExitReason string
// Session is the final session state.
Session *Session
// Error is set if the loop exited due to an error.
Error error
// RateLimitHit is true if the loop hit Claude's API rate limit.
RateLimitHit bool
}
Result represents the outcome of running the loop.
func RunLoop ¶
func RunLoop(ctx context.Context, cfg RunLoopConfig) (*Result, error)
RunLoop executes the loop with either the TUI dashboard (TTY default) or the text monitor (verbose/json/quiet/non-TTY). Returns the result and any error. This consolidates the output mode selection shared by iterate and tasks.
type ResultEvent ¶
type ResultEvent struct {
Type EventType `json:"type"`
Subtype string `json:"subtype"`
SessionID string `json:"session_id"`
IsError bool `json:"is_error"`
DurationMS int `json:"duration_ms"`
DurationAPIMS int `json:"duration_api_ms"`
NumTurns int `json:"num_turns"`
TotalCostUSD float64 `json:"total_cost_usd"`
Usage *TokenUsage `json:"usage,omitempty"`
// Success-only fields.
Result string `json:"result,omitempty"`
// Error-only fields.
Errors []string `json:"errors,omitempty"`
}
ResultEvent is the final event in the stream, indicating completion or error.
func ParseStream ¶
func ParseStream(ctx context.Context, r io.Reader, handler *StreamHandler, log *logger.Logger) (*ResultEvent, error)
ParseStream reads NDJSON lines from r, dispatches events to handler, and returns the final ResultEvent. Returns an error if the stream ends without a result event, on context cancellation, or on scan failure.
Before parsing NDJSON, ParseStream waits for a ready signal line (ReadyLogPrefix). Lines before the signal are debug-logged and skipped. An error signal (ErrorLogPrefix) during init returns an error immediately. If the stream ends before the ready signal, an error is returned.
Malformed lines are debug-logged and skipped. Unrecognized event types are silently skipped for forward compatibility. Known event types that fail to parse (system, assistant, user) are warn-logged and skipped. A malformed result event returns an error (terminal event corruption).
func (*ResultEvent) CombinedText ¶
func (r *ResultEvent) CombinedText() string
CombinedText returns the result text (success) or joined error messages.
func (*ResultEvent) IsSuccess ¶
func (r *ResultEvent) IsSuccess() bool
IsSuccess returns true if this result represents a successful completion.
type ResultOutput ¶
type ResultOutput struct {
LoopsCompleted int `json:"loops_completed"`
ExitReason string `json:"exit_reason"`
Success bool `json:"success"`
Error string `json:"error,omitempty"`
TotalTasksCompleted int `json:"total_tasks_completed,omitempty"`
TotalFilesModified int `json:"total_files_modified,omitempty"`
FinalStatus string `json:"final_status,omitempty"`
RateLimitHit bool `json:"rate_limit_hit,omitempty"`
}
ResultOutput is the structured output for loop results.
func NewResultOutput ¶
func NewResultOutput(result *Result) *ResultOutput
NewResultOutput maps a Result into a ResultOutput for serialization.
type RunLoopConfig ¶
type RunLoopConfig struct {
Runner *Runner
RunnerOpts Options
TUI interface{} // unused after refactor, kept for API compat (TODO: remove)
IOStreams *iostreams.IOStreams
Setup *LoopContainerResult
Format *cmdutil.FormatFlags
Verbose bool
CommandName string // "iterate" or "tasks"
}
RunLoopConfig holds all inputs for RunLoop.
type Runner ¶
type Runner struct {
// contains filtered or unexported fields
}
Runner executes autonomous loops.
func NewRunnerWith ¶
func NewRunnerWith(client *docker.Client, store *SessionStore, history *HistoryStore) *Runner
NewRunnerWith creates a Runner with explicit store and history dependencies. This is useful for testing with custom storage directories.
func (*Runner) GetCircuitState ¶
func (r *Runner) GetCircuitState(project, agent string) (*CircuitState, error)
GetCircuitState returns the circuit breaker state for a project/agent.
func (*Runner) GetSession ¶
GetSession returns the current session for a project/agent.
func (*Runner) ResetCircuit ¶
ResetCircuit resets the circuit breaker for a project/agent.
func (*Runner) ResetSession ¶
ResetSession resets both session and circuit for a project/agent.
func (*Runner) Run ¶
Run executes the loop until completion, error, or max loops. Each iteration creates a new container via opts.CreateContainer, attaches, parses stream-json output, and cleans up the container.
func (*Runner) StartContainer ¶
func (r *Runner) StartContainer(ctx context.Context, log *logger.Logger, projectCfg *config.Project, containerConfig *ContainerStartConfig, onOutput func([]byte), onStreamEvent func(*StreamDeltaEvent)) (string, *ResultEvent, int, error)
StartContainer attaches to the container, starts it, parses stream-json output via io.Pipe + ParseStream, and returns the accumulated text, the terminal ResultEvent, the exit code, and any error.
Streaming events are forwarded to onStreamEvent for rich TUI updates (tool starts, text deltas). Text deltas are also forwarded to onOutput for simple text display. Complete assistant messages are accumulated via TextAccumulator for LOOP_STATUS parsing.
type Session ¶
type Session struct {
// Project is the clawker project name.
Project string `json:"project"`
// Agent is the agent name.
Agent string `json:"agent"`
// WorkDir is the host working directory for this session.
WorkDir string `json:"work_dir,omitempty"`
// StartedAt is when the session started.
StartedAt time.Time `json:"started_at"`
// UpdatedAt is when the session was last updated.
UpdatedAt time.Time `json:"updated_at"`
// LoopsCompleted is the number of loops completed.
LoopsCompleted int `json:"loops_completed"`
// Status is the last known status.
Status string `json:"status"`
// NoProgressCount is the current count without progress.
NoProgressCount int `json:"no_progress_count"`
// TotalTasksCompleted is the cumulative tasks completed.
TotalTasksCompleted int `json:"total_tasks_completed"`
// TotalFilesModified is the cumulative files modified.
TotalFilesModified int `json:"total_files_modified"`
// LastError is the last error message, if any.
LastError string `json:"last_error,omitempty"`
// InitialPrompt is the prompt that started the session.
InitialPrompt string `json:"initial_prompt,omitempty"`
// RateLimitState tracks the rate limiter state for persistence.
RateLimitState *RateLimitState `json:"rate_limit_state,omitempty"`
}
Session represents the persistent state of a loop session.
func NewSession ¶
NewSession creates a new session.
func (*Session) Update ¶
Update updates the session with loop results. It always increments LoopsCompleted and refreshes UpdatedAt. A non-nil loopErr is recorded as LastError; nil clears it. When status is non-nil, the session's Status, TotalTasksCompleted, and TotalFilesModified are updated from the status fields, and NoProgressCount is reset if the status shows progress. When status is nil (no LOOP_STATUS block found), the loop is treated as a no-progress iteration.
type SessionHistory ¶
type SessionHistory struct {
Project string `json:"project"`
Agent string `json:"agent"`
Entries []SessionHistoryEntry `json:"entries"`
}
SessionHistory tracks session lifecycle events.
type SessionHistoryEntry ¶
type SessionHistoryEntry struct {
Timestamp time.Time `json:"timestamp"`
Event string `json:"event"` // created, updated, expired, deleted, repeated_error
LoopCount int `json:"loop_count"`
Status string `json:"status,omitempty"`
Error string `json:"error,omitempty"`
}
SessionHistoryEntry represents a session state transition.
type SessionStore ¶
type SessionStore struct {
// contains filtered or unexported fields
}
SessionStore manages session and circuit breaker persistence.
func DefaultSessionStore ¶
func DefaultSessionStore() (*SessionStore, error)
DefaultSessionStore returns a session store using the default clawker directory.
func NewSessionStore ¶
func NewSessionStore(baseDir string, log ...*logger.Logger) *SessionStore
NewSessionStore creates a new session store at the given base directory.
func (*SessionStore) DeleteCircuitState ¶
func (s *SessionStore) DeleteCircuitState(project, agent string) error
DeleteCircuitState removes circuit breaker state from disk.
func (*SessionStore) DeleteSession ¶
func (s *SessionStore) DeleteSession(project, agent string) error
DeleteSession removes a session from disk.
func (*SessionStore) ListSessions ¶
func (s *SessionStore) ListSessions(project string) ([]*Session, error)
ListSessions returns all sessions for a given project. Malformed session files are silently skipped.
func (*SessionStore) LoadCircuitState ¶
func (s *SessionStore) LoadCircuitState(project, agent string) (*CircuitState, error)
LoadCircuitState loads circuit breaker state from disk. Returns nil if no state exists.
func (*SessionStore) LoadSession ¶
func (s *SessionStore) LoadSession(project, agent string) (*Session, error)
LoadSession loads a session from disk. Returns nil if no session exists.
func (*SessionStore) LoadSessionWithExpiration ¶
func (s *SessionStore) LoadSessionWithExpiration(project, agent string, expirationHours int) (*Session, bool, error)
LoadSessionWithExpiration loads a session and auto-resets if expired. Returns nil (and deletes the old session) if the session is expired.
func (*SessionStore) SaveCircuitState ¶
func (s *SessionStore) SaveCircuitState(state *CircuitState) error
SaveCircuitState saves circuit breaker state to disk.
func (*SessionStore) SaveSession ¶
func (s *SessionStore) SaveSession(session *Session) error
SaveSession saves a session to disk.
type Status ¶
type Status struct {
// Status is one of: IN_PROGRESS, COMPLETE, BLOCKED
Status string
// TasksCompleted is the number of tasks completed in this loop.
TasksCompleted int
// FilesModified is the number of files modified in this loop.
FilesModified int
// TestsStatus is one of: PASSING, FAILING, NOT_RUN
TestsStatus string
// WorkType describes the type of work done (IMPLEMENTATION, TESTING, etc.)
WorkType string
// ExitSignal indicates whether Claude requested to exit the loop.
ExitSignal bool
// Recommendation is a one-line recommendation for next steps.
Recommendation string
// CompletionIndicators is the count of completion phrases found in output.
CompletionIndicators int
}
Status represents the parsed LOOP_STATUS block from Claude's output.
func ParseStatus ¶
ParseStatus extracts the LOOP_STATUS block from output and parses it. Returns nil if no valid status block is found.
func (*Status) HasProgress ¶
HasProgress returns true if the status indicates meaningful progress.
func (*Status) IsComplete ¶
IsComplete returns true if the status indicates completion. This is the basic check: STATUS: COMPLETE or EXIT_SIGNAL: true
func (*Status) IsCompleteStrict ¶
IsCompleteStrict returns true only if BOTH conditions are met: - EXIT_SIGNAL is true - CompletionIndicators >= threshold This prevents premature exit due to false positives.
func (*Status) IsTestOnly ¶
IsTestOnly returns true if this loop was test-only work.
type StreamAPIEvent ¶
type StreamAPIEvent struct {
Type string `json:"type"` // message_start, content_block_start, content_block_delta, content_block_stop, message_delta, message_stop
Index int `json:"index,omitempty"`
ContentBlock *StreamContentBlock `json:"content_block,omitempty"`
Delta *StreamDelta `json:"delta,omitempty"`
Message *StreamMessageDelta `json:"message,omitempty"`
Usage *TokenUsage `json:"usage,omitempty"`
}
StreamAPIEvent is the inner SSE event from the Anthropic streaming API.
type StreamContentBlock ¶
type StreamContentBlock struct {
Type string `json:"type"` // "text", "tool_use"
ID string `json:"id,omitempty"` // block ID
Name string `json:"name,omitempty"` // tool name when Type == "tool_use"
}
StreamContentBlock describes a content block in a content_block_start event.
type StreamDelta ¶
type StreamDelta struct {
Type string `json:"type"` // "text_delta", "input_json_delta"
Text string `json:"text,omitempty"` // text content when Type == "text_delta"
PartialJSON string `json:"partial_json,omitempty"` // partial JSON when Type == "input_json_delta"
}
StreamDelta holds incremental content from a content_block_delta event.
type StreamDeltaEvent ¶
type StreamDeltaEvent struct {
Type EventType `json:"type"`
UUID string `json:"uuid,omitempty"`
SessionID string `json:"session_id"`
ParentToolUseID *string `json:"parent_tool_use_id"`
Event StreamAPIEvent `json:"event"`
}
StreamDeltaEvent wraps a raw Claude API streaming event. Emitted with --include-partial-messages for real-time token display.
func (*StreamDeltaEvent) IsContentBlockStop ¶
func (e *StreamDeltaEvent) IsContentBlockStop() bool
IsContentBlockStop returns true if this event ends a content block.
func (*StreamDeltaEvent) IsToolStart ¶
func (e *StreamDeltaEvent) IsToolStart() bool
IsToolStart returns true if this event starts a tool_use content block.
func (*StreamDeltaEvent) TextDelta ¶
func (e *StreamDeltaEvent) TextDelta() string
TextDelta returns text if this is a text_delta event, empty string otherwise.
func (*StreamDeltaEvent) ToolName ¶
func (e *StreamDeltaEvent) ToolName() string
ToolName returns the tool name if this is a content_block_start for a tool_use block.
type StreamHandler ¶
type StreamHandler struct {
// OnSystem is called for system events (init, compact_boundary).
OnSystem func(*SystemEvent)
// OnAssistant is called for each complete assistant message.
OnAssistant func(*AssistantEvent)
// OnUser is called for each tool result message.
OnUser func(*UserEvent)
// OnResult is called when the final result event arrives.
OnResult func(*ResultEvent)
// OnStreamEvent is called for real-time streaming events (token deltas).
// Requires --include-partial-messages flag.
OnStreamEvent func(*StreamDeltaEvent)
}
StreamHandler receives parsed stream-json events via callbacks. All callbacks are optional — nil callbacks are skipped.
type StreamMessageDelta ¶
type StreamMessageDelta struct {
StopReason string `json:"stop_reason,omitempty"`
}
StreamMessageDelta holds message-level delta fields from a message_delta event.
type SystemEvent ¶
type SystemEvent struct {
Type EventType `json:"type"`
UUID string `json:"uuid,omitempty"`
Subtype string `json:"subtype"`
SessionID string `json:"session_id"`
Model string `json:"model,omitempty"`
Tools []string `json:"tools,omitempty"`
CWD string `json:"cwd,omitempty"`
PermissionMode string `json:"permissionMode,omitempty"`
// CompactBoundary-only fields.
CompactMetadata *CompactMetadata `json:"compact_metadata,omitempty"`
}
SystemEvent is emitted once at session start (init) or on conversation compaction.
type TextAccumulator ¶
type TextAccumulator struct {
// contains filtered or unexported fields
}
TextAccumulator collects assistant text output across multiple messages. It is a convenience handler that aggregates text for LOOP_STATUS parsing and output analysis after the stream completes.
func (*TextAccumulator) Text ¶
func (a *TextAccumulator) Text() string
Text returns all accumulated assistant text joined by newlines.
func (*TextAccumulator) ToolCallCount ¶
func (a *TextAccumulator) ToolCallCount() int
ToolCallCount returns the total number of tool invocations observed.
type TokenUsage ¶
type TokenUsage struct {
InputTokens int `json:"input_tokens"`
OutputTokens int `json:"output_tokens"`
CacheCreationInputTokens int `json:"cache_creation_input_tokens,omitempty"`
CacheReadInputTokens int `json:"cache_read_input_tokens,omitempty"`
}
TokenUsage tracks API token consumption.
func (*TokenUsage) Total ¶
func (u *TokenUsage) Total() int
Total returns the sum of input and output tokens. Cache tokens are not added separately because the Anthropic API's input_tokens field already accounts for cache reads in billing.
type UpdateResult ¶
type UpdateResult struct {
Tripped bool
Reason string
IsComplete bool // True if strict completion criteria met
CompletionMsg string
}
UpdateResult contains the result of a circuit breaker update.
type UserEvent ¶
type UserEvent struct {
Type EventType `json:"type"`
UUID string `json:"uuid,omitempty"`
SessionID string `json:"session_id"`
ParentToolUseID *string `json:"parent_tool_use_id"`
Message UserEventMessage `json:"message"`
}
UserEvent is a tool result message returned after tool execution.
type UserEventMessage ¶
type UserEventMessage struct {
Role string `json:"role"`
Content []ContentBlock `json:"content"`
}
UserEventMessage is the user message embedded in a UserEvent.