Help us shape the future of Go! Take the 2026 Go Developer Survey

control

package
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Mar 11, 2026 License: AGPL-3.0 Imports: 12 Imported by: 0

Documentation

Overview

Package control provides control flow hardening for the agent loop.

This package implements 5 layers of hardening to prevent:

  • Premature completion (intent detection)
  • Raw tool output leakage (parser hardening)
  • Step exhaustion without synthesis (synthesis budget)
  • Leaked markup in responses (output sanitization)
  • Empty responses despite gathered information (buffer extraction)

Index

Constants

View Source
const (
	// DefaultMaxHistory is the maximum number of violations to track.
	DefaultMaxHistory = 10

	// DefaultStreakThreshold triggers stuck on N same-type violations.
	DefaultStreakThreshold = 3

	// DefaultCategoryThreshold triggers stuck on N same-category violations.
	DefaultCategoryThreshold = 4

	// DefaultOscillationWindow detects A-B-A-B patterns in last N violations.
	DefaultOscillationWindow = 6
)

Default configuration values.

Variables

This section is empty.

Functions

func QuickSanitize

func QuickSanitize(content string) string

QuickSanitize is a package-level function for one-off sanitization.

Description:

Uses a cached sanitizer with default config for efficient repeated use.
For model-specific sanitization, use QuickSanitizeForModel instead.

Inputs:

content - Response text to sanitize.

Outputs:

string - Sanitized text.

Thread Safety: Safe for concurrent use.

func QuickSanitizeForModel

func QuickSanitizeForModel(content string, model agent.ModelType) string

QuickSanitizeForModel sanitizes with model awareness.

Inputs:

content - Response text to sanitize.
model - The LLM model type.

Outputs:

string - Sanitized text.

func RecordStuckMetric

func RecordStuckMetric(result StuckResult)

RecordStuckMetric records when stuck state is entered.

Types

type AgentStateInput

type AgentStateInput struct {
	// FinalResponse is the synthesized response (if any).
	FinalResponse string

	// ToolResults are the tool execution results.
	ToolResults []ToolResultInput

	// Query is the original user query.
	Query string
}

AgentStateInput provides the agent state for extraction.

type AmbiguousRule

type AmbiguousRule struct {
	// FollowedByColon: if true, phrase + colon = intent; false = answer
	FollowedByColon bool
}

AmbiguousRule defines how to handle ambiguous phrases.

type BudgetConfig

type BudgetConfig struct {
	// TotalSteps is the total step budget (default: 15).
	TotalSteps int

	// DefaultSynthesisRatio is default synthesis reserve (default: 0.20).
	DefaultSynthesisRatio float64

	// SimpleQueryRatio is exploration ratio for simple queries (default: 0.50).
	SimpleQueryRatio float64

	// MediumQueryRatio is exploration ratio for medium queries (default: 0.70).
	MediumQueryRatio float64

	// ComplexQueryRatio is exploration ratio for complex queries (default: 0.85).
	ComplexQueryRatio float64
}

BudgetConfig allows configuring budget behavior.

func DefaultBudgetConfig

func DefaultBudgetConfig() BudgetConfig

DefaultBudgetConfig returns production defaults.

type BudgetStatus

type BudgetStatus struct {
	// CurrentStep is the current step number.
	CurrentStep int

	// TotalSteps is the total step budget.
	TotalSteps int

	// ExplorationBudget is steps available for exploration.
	ExplorationBudget int

	// SynthesisBudget is steps reserved for synthesis.
	SynthesisBudget int

	// InSynthesisPhase indicates if we're in synthesis phase.
	InSynthesisPhase bool

	// Complexity is the detected query complexity.
	Complexity QueryComplexity

	// RemainingSteps is steps remaining in current phase.
	RemainingSteps int
}

BudgetStatus contains budget state for logging/debugging.

type BufferExtractor

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

BufferExtractor generates fallback responses from accumulated state.

Description:

When the agent gathers information during exploration but fails to
synthesize a response (due to step exhaustion, timeout, or other issues),
the BufferExtractor constructs a fallback response from accumulated
tool results to avoid returning empty responses.

Thread Safety: Safe for concurrent use (stateless).

func NewBufferExtractor

func NewBufferExtractor(config ExtractionConfig, logger *slog.Logger) *BufferExtractor

NewBufferExtractor creates a new extractor.

Inputs:

config - Configuration for extraction behavior.
logger - Logger for diagnostic output (nil for default).

Outputs:

*BufferExtractor - The configured extractor.

func (*BufferExtractor) Extract

Extract generates a response from accumulated state.

Description:

First checks if there's a valid final response. If not, generates
a fallback response from accumulated tool results. If no tool results
exist, returns a "no information" message.

Inputs:

state - Current agent state with tool results.

Outputs:

ExtractionResult - Generated response with metadata.

Thread Safety: Safe for concurrent use.

func (*BufferExtractor) HasUsableContent

func (e *BufferExtractor) HasUsableContent(state AgentStateInput) bool

HasUsableContent checks if there's enough content for extraction.

Description:

Quick check to determine if extraction would produce meaningful
output. Useful for deciding whether to attempt fallback extraction.

Inputs:

state - Agent state to check.

Outputs:

bool - True if meaningful extraction is possible.

Thread Safety: Safe for concurrent use.

type ExtractionConfig

type ExtractionConfig struct {
	// MinFinalResponseLength is the minimum chars for a valid final response.
	MinFinalResponseLength int

	// MaxSummaryLength is the max chars per finding summary.
	MaxSummaryLength int

	// MaxFindings is the max number of findings to include in fallback.
	MaxFindings int

	// IncludeDisclaimer adds a disclaimer to fallback responses.
	IncludeDisclaimer bool
}

ExtractionConfig configures buffer extraction behavior.

func DefaultExtractionConfig

func DefaultExtractionConfig() ExtractionConfig

DefaultExtractionConfig returns production defaults.

type ExtractionResult

type ExtractionResult struct {
	// Response is the generated response text.
	Response string

	// IsFallback indicates if this is a fallback response.
	IsFallback bool

	// SourceCount is the number of tool results used.
	SourceCount int

	// FileCount is the number of unique files referenced.
	FileCount int
}

ExtractionResult contains the extracted response.

type FrustrationConfig

type FrustrationConfig struct {
	// MaxHistory is how many violations to remember (default: 10).
	MaxHistory int

	// StreakThreshold triggers STUCK on N same-type violations (default: 3).
	StreakThreshold int

	// CategoryThreshold triggers STUCK on N same-category violations (default: 4).
	CategoryThreshold int

	// OscillationWindow detects A-B-A-B patterns in last N (default: 6).
	OscillationWindow int
}

FrustrationConfig configures detection thresholds.

func DefaultFrustrationConfig

func DefaultFrustrationConfig() FrustrationConfig

DefaultFrustrationConfig returns sensible defaults.

type FrustrationTracker

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

FrustrationTracker detects behavioral deadlocks.

Description:

Tracks violations and detects patterns that indicate the agent is stuck
in an unresolvable loop. Uses three detection rules:
1. Same-type streak: N consecutive violations of the same type
2. Category saturation: N consecutive violations in the same category
3. Oscillation: Alternating between two violation types

Thread Safety: Safe for concurrent use via mutex.

func NewFrustrationTracker

func NewFrustrationTracker(config FrustrationConfig) *FrustrationTracker

NewFrustrationTracker creates a tracker with the given configuration.

Description:

Creates a FrustrationTracker with configured thresholds. Uses defaults
for any zero values in the config.

Inputs:

config - Configuration options. Uses defaults if zero values.

Outputs:

*FrustrationTracker - The configured tracker.

func (*FrustrationTracker) GetRecentViolations

func (f *FrustrationTracker) GetRecentViolations(n int) []Violation

GetRecentViolations returns the last N violations.

Description:

Returns a copy of the most recent violations for inspection.
Returns fewer than N if not enough violations recorded.

Inputs:

n - Maximum number of violations to return.

Outputs:

[]Violation - Copy of recent violations (newest last).

Thread Safety: This method is safe for concurrent use.

func (*FrustrationTracker) IsStuck

func (f *FrustrationTracker) IsStuck() bool

IsStuck checks current state without adding a violation.

Description:

Checks if the current violation history indicates a stuck state.
Does not modify the violation history.

Outputs:

bool - True if the agent is stuck.

Thread Safety: This method is safe for concurrent use.

func (*FrustrationTracker) RecordViolation

func (f *FrustrationTracker) RecordViolation(v Violation) StuckResult

RecordViolation adds a violation and checks for deadlock.

Description:

Records the violation, trims history if needed, checks all detection
rules, and returns a StuckResult indicating whether the agent is stuck.

Inputs:

v - The violation to record.

Outputs:

StuckResult - The detection result.

Thread Safety: This method is safe for concurrent use.

func (*FrustrationTracker) Reset

func (f *FrustrationTracker) Reset()

Reset clears violation history.

Description:

Clears all recorded violations. Call this after user provides
clarification to give the agent a fresh start.

Thread Safety: This method is safe for concurrent use.

func (*FrustrationTracker) ViolationCount

func (f *FrustrationTracker) ViolationCount() int

ViolationCount returns the number of recorded violations.

Thread Safety: This method is safe for concurrent use.

type HelpRequest

type HelpRequest struct {
	// Title is the help request title.
	Title string

	// WhatITried lists attempted actions.
	WhatITried []string

	// TheProblem explains the issue.
	TheProblem string

	// Suggestions lists how the user can help.
	Suggestions []string

	// CanSkip indicates if this step is optional.
	CanSkip bool

	// SkipMessage explains what happens if skipped.
	SkipMessage string
}

HelpRequest is a structured request for user assistance.

func (HelpRequest) String

func (h HelpRequest) String() string

String returns a formatted help request message.

type IntentCache

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

IntentCache caches classification results for unchanged responses.

Thread Safety: Safe for concurrent use via RWMutex.

type IntentClassifier

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

IntentClassifier detects when LLM outputs intent rather than answer.

Description:

Analyzes LLM response text to determine if it represents a statement
of future intent ("I will explore...") vs an actual answer. Intent
statements should trigger continuation, not completion.

Thread Safety: Safe for concurrent use (stateless methods, thread-safe cache).

func NewIntentClassifier

func NewIntentClassifier(config IntentConfig) *IntentClassifier

NewIntentClassifier creates a classifier with default indicators.

Inputs:

config - Configuration for intent detection behavior.

Outputs:

*IntentClassifier - The configured classifier.

func (*IntentClassifier) CacheStats

func (c *IntentClassifier) CacheStats() (size int, capacity int)

CacheStats returns cache statistics.

Thread Safety: Safe for concurrent use.

func (*IntentClassifier) ClearCache

func (c *IntentClassifier) ClearCache()

ClearCache clears the intent cache.

Thread Safety: Safe for concurrent use.

func (*IntentClassifier) IsIntent

func (c *IntentClassifier) IsIntent(content string) IntentResult

IsIntent returns true if content appears to be intent, not answer.

Inputs:

content - LLM response text to classify.

Outputs:

IntentResult - Classification result with score and reason.

Thread Safety: Safe for concurrent use.

type IntentConfig

type IntentConfig struct {
	// MaxIntentLength is max chars for content to be considered intent (default: 500).
	MaxIntentLength int

	// HighScoreThreshold is score for high confidence (default: 3).
	HighScoreThreshold int

	// ShortResponseLen is "short" response threshold (default: 300).
	ShortResponseLen int

	// EnableCache enables caching of classification results.
	EnableCache bool

	// CacheTTL is TTL for cache entries.
	CacheTTL time.Duration

	// CacheMaxSize is max entries in cache (LRU eviction).
	CacheMaxSize int
}

IntentConfig allows tuning intent detection behavior.

func DefaultIntentConfig

func DefaultIntentConfig() IntentConfig

DefaultIntentConfig returns production defaults.

type IntentResult

type IntentResult struct {
	// IsIntent is true if content is an intent statement, not an answer.
	IsIntent bool

	// Score is the confidence score (0-10).
	Score int

	// Reason explains the classification.
	Reason string

	// Cached indicates if result came from cache.
	Cached bool
}

IntentResult contains the result of intent classification.

type OutputSanitizer

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

OutputSanitizer removes leaked tool markup from responses.

Description:

Provides a final defense layer that strips leaked tool markup
before returning responses to the user. Model-aware to avoid
stripping native formats for their respective models.

Thread Safety: Safe for concurrent use (stateless, compiled regex).

func NewOutputSanitizer

func NewOutputSanitizer(config SanitizeConfig) *OutputSanitizer

NewOutputSanitizer creates a sanitizer for the given model.

Description:

Compiles combined regex at construction time for O(n) performance.
The sanitizer is model-aware to preserve native formats.

Inputs:

config - Configuration for sanitization behavior.

Outputs:

*OutputSanitizer - The configured sanitizer.

func (*OutputSanitizer) ContainsLeakedMarkup

func (s *OutputSanitizer) ContainsLeakedMarkup(content string) bool

ContainsLeakedMarkup checks if content contains leaked tool markup.

Description:

Quick check to determine if sanitization is needed. Useful for
metrics and logging without performing full sanitization.

Inputs:

content - Response text to check.

Outputs:

bool - True if leaked markup is detected.

Thread Safety: Safe for concurrent use.

func (*OutputSanitizer) GetModel

func (s *OutputSanitizer) GetModel() agent.ModelType

GetModel returns the configured model type.

func (*OutputSanitizer) Sanitize

func (s *OutputSanitizer) Sanitize(content string) SanitizeResult

Sanitize removes leaked tool markup while preserving legitimate content.

Description:

Removes tool markup patterns from the response while preserving
markdown code blocks and inline code. Uses a single-pass approach
for O(n) performance.

Inputs:

content - Response text potentially containing leaked markup.

Outputs:

SanitizeResult - Sanitized text and metadata.

Thread Safety: Safe for concurrent use.

func (*OutputSanitizer) SanitizeString

func (s *OutputSanitizer) SanitizeString(content string) string

SanitizeString is a convenience method that returns just the sanitized string.

Inputs:

content - Response text to sanitize.

Outputs:

string - Sanitized text.

Thread Safety: Safe for concurrent use.

type QueryComplexity

type QueryComplexity int

QueryComplexity categorizes query complexity for budget allocation.

const (
	// ComplexitySimple is for direct, single-entity questions.
	ComplexitySimple QueryComplexity = iota

	// ComplexityMedium is for multi-entity or explanation questions.
	ComplexityMedium

	// ComplexityComplex is for architecture or cross-cutting questions.
	ComplexityComplex
)

func (QueryComplexity) String

func (c QueryComplexity) String() string

String returns the string representation of complexity.

type SanitizeConfig

type SanitizeConfig struct {
	// Model is the LLM model type for model-aware sanitization.
	Model agent.ModelType

	// PreserveCodeBlocks preserves markdown code blocks during sanitization.
	PreserveCodeBlocks bool

	// PreserveInlineCode preserves inline code during sanitization.
	PreserveInlineCode bool

	// StripThinkTags removes <think> tags.
	StripThinkTags bool

	// StripReasoningTags removes <thought>, <reasoning>, <reflection> tags.
	StripReasoningTags bool
}

SanitizeConfig configures output sanitization behavior.

func DefaultSanitizeConfig

func DefaultSanitizeConfig() SanitizeConfig

DefaultSanitizeConfig returns production defaults.

type SanitizeResult

type SanitizeResult struct {
	// Content is the sanitized text.
	Content string

	// Stripped indicates if any content was stripped.
	Stripped bool

	// StrippedCount is the number of patterns stripped.
	StrippedCount int
}

SanitizeResult contains the result of sanitization.

type StepBudget

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

StepBudget manages step allocation between exploration and synthesis.

Description:

Reserves a configurable percentage of the step budget for the synthesis
phase, ensuring the agent has steps available to synthesize a response
even when exploration is exhaustive.

Thread Safety: Safe for concurrent use via mutex.

func NewStepBudget

func NewStepBudget(config BudgetConfig) *StepBudget

NewStepBudget creates a new budget with the given config.

Inputs:

config - Configuration for budget behavior.

Outputs:

*StepBudget - The configured budget manager.

func (*StepBudget) CanExplore

func (b *StepBudget) CanExplore() bool

CanExplore returns true if exploration steps remain.

Description:

Checks if the current step is within the exploration budget.
Once exploration budget is exhausted, the agent must synthesize.

Outputs:

bool - True if more exploration steps are available.

Thread Safety: Safe for concurrent use.

func (*StepBudget) DetectComplexity

func (b *StepBudget) DetectComplexity(query string)

DetectComplexity analyzes the query to determine complexity.

Description:

Examines the query for complexity indicators and adjusts the
synthesis budget accordingly. More complex queries get more
exploration budget.

Inputs:

query - The user's query string.

Thread Safety: Safe for concurrent use.

func (*StepBudget) EnterSynthesis

func (b *StepBudget) EnterSynthesis()

EnterSynthesis forces entry into synthesis phase.

Description:

Explicitly enters synthesis phase regardless of current step.
Called when the agent decides it has enough information to synthesize.

Thread Safety: Safe for concurrent use.

func (*StepBudget) GetComplexity

func (b *StepBudget) GetComplexity() QueryComplexity

GetComplexity returns the detected query complexity.

Thread Safety: Safe for concurrent use.

func (*StepBudget) IncrementStep

func (b *StepBudget) IncrementStep()

IncrementStep increments the step counter.

Thread Safety: Safe for concurrent use.

func (*StepBudget) IsInSynthesis

func (b *StepBudget) IsInSynthesis() bool

IsInSynthesis returns true if explicitly in synthesis phase.

Thread Safety: Safe for concurrent use.

func (*StepBudget) MustSynthesize

func (b *StepBudget) MustSynthesize() bool

MustSynthesize returns true if we're in the synthesis phase.

Description:

Returns true when exploration budget is exhausted and the agent
must synthesize a response from gathered information.

Outputs:

bool - True if synthesis is required.

Thread Safety: Safe for concurrent use.

func (*StepBudget) RemainingExplorationSteps

func (b *StepBudget) RemainingExplorationSteps() int

RemainingExplorationSteps returns steps remaining for exploration.

Thread Safety: Safe for concurrent use.

func (*StepBudget) RemainingSteps

func (b *StepBudget) RemainingSteps() int

RemainingSteps returns steps remaining in total budget.

Thread Safety: Safe for concurrent use.

func (*StepBudget) Reset

func (b *StepBudget) Reset()

Reset resets the budget for a new session.

Thread Safety: Safe for concurrent use.

func (*StepBudget) SetTotalSteps

func (b *StepBudget) SetTotalSteps(steps int)

SetTotalSteps updates the total step budget.

Thread Safety: Safe for concurrent use.

func (*StepBudget) Status

func (b *StepBudget) Status() BudgetStatus

Status returns current budget status for logging.

Thread Safety: Safe for concurrent use.

type StuckResult

type StuckResult struct {
	// IsStuck indicates if the agent is in a deadlock.
	IsStuck bool

	// ViolationType is the primary violation type.
	ViolationType ViolationType

	// ViolationCategory is the primary violation category.
	ViolationCategory ViolationCategory

	// Streak is the number of consecutive same-type violations.
	Streak int

	// DetectionRule is which rule triggered stuck ("same_type", "category_saturation", "oscillation").
	DetectionRule string

	// Violations are the recent violations for context.
	Violations []Violation

	// HelpRequest is the generated help message.
	HelpRequest HelpRequest
}

StuckResult is returned when frustration threshold is exceeded.

type ToolResultInput

type ToolResultInput struct {
	// ToolName is the name of the tool that was executed.
	ToolName string

	// Content is the output from the tool.
	Content string

	// Success indicates if the tool execution succeeded.
	Success bool
}

ToolResultInput represents a tool execution result for extraction.

type Violation

type Violation struct {
	// Type is the specific violation type.
	Type ViolationType

	// Category is the violation category (derived from Type).
	Category ViolationCategory

	// Message is a human-readable description.
	Message string

	// Context contains additional details (e.g., {"path": "/config.yaml"}).
	Context map[string]string

	// Timestamp is when the violation occurred.
	Timestamp time.Time

	// Attempt is which retry attempt this was.
	Attempt int
}

Violation records a single failure event.

func NewViolation

func NewViolation(vt ViolationType, message string, context map[string]string) Violation

NewViolation creates a violation with auto-derived category.

Description:

Creates a new Violation with the category automatically derived
from the violation type.

Inputs:

vt - The violation type.
message - Human-readable description.
context - Additional context (can be nil).

Outputs:

Violation - The created violation.

type ViolationCategory

type ViolationCategory string

ViolationCategory groups violation types for category-based detection.

const (
	// CategoryResource covers file, network, and resource access issues.
	CategoryResource ViolationCategory = "resource"

	// CategoryValidation covers safety layer rejections.
	CategoryValidation ViolationCategory = "validation"

	// CategorySemantic covers understanding and context issues.
	CategorySemantic ViolationCategory = "semantic"

	// CategoryUnknown for unclassified violations.
	CategoryUnknown ViolationCategory = "unknown"
)

func CategoryForType

func CategoryForType(vt ViolationType) ViolationCategory

CategoryForType returns the category for a violation type.

type ViolationType

type ViolationType string

ViolationType represents categories of agent failures.

const (
	// Resource violations - external dependencies unavailable.
	ViolationFileNotFound      ViolationType = "file_not_found"
	ViolationPermissionDenied  ViolationType = "permission_denied"
	ViolationNetworkError      ViolationType = "network_error"
	ViolationResourceExhausted ViolationType = "resource_exhausted"

	// Validation violations - safety layers rejecting output.
	ViolationIntentLoop      ViolationType = "intent_loop"
	ViolationMalformedTool   ViolationType = "malformed_tool"
	ViolationInvalidCitation ViolationType = "invalid_citation"
	ViolationConstraint      ViolationType = "constraint_violation"

	// Semantic violations - understanding issues.
	ViolationAmbiguous      ViolationType = "ambiguous_request"
	ViolationConflicting    ViolationType = "conflicting_info"
	ViolationMissingContext ViolationType = "missing_context"
	ViolationScopeUnclear   ViolationType = "scope_unclear"

	// Unknown violation type.
	ViolationUnknown ViolationType = "unknown"
)

Jump to

Keyboard shortcuts

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