engine

package
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: May 12, 2026 License: MIT Imports: 28 Imported by: 0

Documentation

Index

Constants

View Source
const (
	DefaultExploreTurns = 15
	DefaultGeneralTurns = 20
	MaxAgentDepth       = 2
)

Sub-agent budget defaults per mode.

View Source
const ConfidenceThreshold = 0.7

ConfidenceThreshold is the minimum confidence score for auto-approval. Below this, the review will not approve even if no issues are found.

View Source
const DefaultSnapshotTTL = 10 * time.Second

DefaultSnapshotTTL is the default time-to-live for snapshot cache entries.

View Source
const DoomLoopThreshold = 3

DoomLoopThreshold is the number of repeated patterns before escalation (matches OpenCode).

View Source
const MaxAutoFixRetries = 3

MaxAutoFixRetries is the maximum number of times to retry auto-fixing a file.

Variables

View Source
var ExploreTools = []string{
	"Glob", "Grep", "Read", "Bash", "LS",
}

ExploreTools are the read-only tools available to explore-mode sub-agents.

Functions

func AutoFixPrompt

func AutoFixPrompt(path, content string, errors []ValidationError) string

AutoFixPrompt returns a prompt instructing the LLM to fix syntax errors in a file.

func BuildCompactPrompt

func BuildCompactPrompt(variant CompactVariant) string

BuildCompactPrompt constructs the full compaction prompt for LLM-based summarization.

func CollapseRepeatedMessages

func CollapseRepeatedMessages(msgs []client.EyrieMessage) []client.EyrieMessage

CollapseRepeatedMessages finds and collapses similar consecutive messages to save context tokens. It collapses:

  • 3+ consecutive tool_results with similar content into a summary
  • Repeated error messages into a count

func CompressForContext

func CompressForContext(text string, budget int) (string, int)

CompressForContext compresses text to fit within a token budget, returning the compressed text and the final token count.

func CountTokens

func CountTokens(text string) int

CountTokens returns a precise BPE-based token count for the given text.

func CountTokensFast

func CountTokensFast(text string) int

CountTokensFast returns a fast heuristic token estimate for the given text.

func DecomposePrompt

func DecomposePrompt() string

DecomposePrompt returns a system prompt that instructs the LLM to break a task into numbered subtasks with titles, descriptions, and file lists.

func DefaultCouncilModels

func DefaultCouncilModels() []string

DefaultCouncilModels returns diverse models from different providers.

func DynamicMaxTokens

func DynamicMaxTokens(messages []client.EyrieMessage, contextSize int, taskType string) int

DynamicMaxTokens calculates the optimal max_tokens for a request based on: - Whether the last few turns were tool-call-heavy (reduce to 4096) - Whether the user asked a question expecting text (8192) - Whether this is a code generation task (16384) - The remaining context budget (don't exceed model's limit)

Research basis: Output tokens are 3-5x more expensive than input tokens. Most tool-call turns only need 200-2000 tokens of output.

func EstimateTokens

func EstimateTokens(msgs []client.EyrieMessage) int

EstimateTokens provides a rough token estimate for messages.

func FormatCompactSummary

func FormatCompactSummary(raw string) string

FormatCompactSummary strips the <analysis> drafting block and extracts the <summary> content.

func FormatCostDisplay

func FormatCostDisplay(totalUSD float64) string

FormatCostDisplay returns a compact cost string for the status bar.

func FormatResults

func FormatResults(results []BackgroundResult) string

FormatResults formats background results for injection into the agent context.

func FormatTeachingMoment

func FormatTeachingMoment(action, reasoning string) string

FormatTeachingMoment wraps agent output with teaching context.

func LoadCostHistory

func LoadCostHistory() ([]analytics.CostEntry, error)

LoadHistory reads all historical cost entries from the JSONL file.

func ModelPricing

func ModelPricing(modelName string) (inputPricePerM, outputPricePerM float64)

ModelPricing returns input/output price per million tokens for a model.

func RemainingTime

func RemainingTime(ctx context.Context) string

RemainingTime returns a formatted remaining-time string derived from the context deadline set by WithTimeout. If no deadline is set it returns an empty string.

func SummarizeTrajectory

func SummarizeTrajectory(messages []client.EyrieMessage) string

SummarizeTrajectory extracts a concise summary from a sequence of messages: what was attempted, what failed, key decisions made, and files touched.

func TeachPromptAugment

func TeachPromptAugment(depth int) string

TeachPromptAugment returns a system prompt addition that instructs the agent to explain its reasoning at the given depth level.

func TimeoutMessage

func TimeoutMessage(elapsed time.Duration) string

TimeoutMessage returns a user-friendly message when the time budget is exhausted.

func WithTimeout

WithTimeout wraps a context with the total timeout and stores the deadline so that RemainingTime can report it.

Types

type APICompactConfig

type APICompactConfig struct {
	TriggerTokens    int
	KeepTargetTokens int
	ClearToolInputs  bool
	ClearThinking    bool
	PreserveMutating bool
}

APICompactConfig controls API-level compaction.

func DefaultAPICompactConfig

func DefaultAPICompactConfig() APICompactConfig

DefaultAPICompactConfig returns defaults matching the archive.

type APICompactStrategy

type APICompactStrategy struct{}

APICompactStrategy uses API-level context edits to clear thinking blocks and old tool inputs without mutating local message content.

func (*APICompactStrategy) Compact

func (s *APICompactStrategy) Compact(ctx context.Context, sess *Session) (*CompactResult, error)

func (*APICompactStrategy) Name

func (s *APICompactStrategy) Name() string

func (*APICompactStrategy) ShouldTrigger

func (s *APICompactStrategy) ShouldTrigger(msgs []client.EyrieMessage, tokenCount, threshold int) bool

type AdaptivePrompt

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

AdaptivePrompt adjusts system prompt sections based on user corrections. When the user says "don't do X" or "always do Y", the adaptive prompt system records it and injects it into future sessions.

func NewAdaptivePrompt

func NewAdaptivePrompt() *AdaptivePrompt

NewAdaptivePrompt creates an adaptive prompt backed by ~/.hawk/adaptive_prompt.json.

func (*AdaptivePrompt) Count

func (ap *AdaptivePrompt) Count() int

Count returns the number of active adjustments.

func (*AdaptivePrompt) FormatForPrompt

func (ap *AdaptivePrompt) FormatForPrompt() string

FormatForPrompt returns active adjustments as system prompt rules.

func (*AdaptivePrompt) LearnFromFeedback

func (ap *AdaptivePrompt) LearnFromFeedback(userMessage string)

LearnFromFeedback extracts prompt adjustments from user corrections.

type AutoCompactor

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

AutoCompactor orchestrates compaction with circuit breaker protection.

func NewAutoCompactor

func NewAutoCompactor(config CompactConfig) *AutoCompactor

NewAutoCompactor creates an auto-compactor with the given config.

func (*AutoCompactor) AutoCompactIfNeeded

func (ac *AutoCompactor) AutoCompactIfNeeded(ctx context.Context, sess *Session) (string, bool)

AutoCompactIfNeeded runs compaction if threshold is met. Returns the strategy name used and whether compaction occurred.

func (*AutoCompactor) GetAutoCompactThreshold

func (ac *AutoCompactor) GetAutoCompactThreshold() int

GetAutoCompactThreshold returns the token count at which auto-compaction triggers.

func (*AutoCompactor) LastStrategy

func (ac *AutoCompactor) LastStrategy() string

LastStrategy returns the name of the last strategy used.

func (*AutoCompactor) ResetFailures

func (ac *AutoCompactor) ResetFailures()

ResetFailures resets the circuit breaker failure count.

func (*AutoCompactor) RunCompaction

func (ac *AutoCompactor) RunCompaction(ctx context.Context, sess *Session) (string, error)

RunCompaction selects and executes the best compaction strategy.

func (*AutoCompactor) ShouldAutoCompact

func (ac *AutoCompactor) ShouldAutoCompact(sess *Session) bool

ShouldAutoCompact determines if compaction is needed based on current state.

type AutonomyConfig

type AutonomyConfig struct {
	Level           AutonomyLevel
	AutoContinue    bool
	AutoApplyEdits  bool
	AutoExecuteBash bool
	AutoCommit      bool
}

AutonomyConfig holds the derived permission flags for an autonomy level.

func PresetConfig

func PresetConfig(level AutonomyLevel) AutonomyConfig

PresetConfig returns the AutonomyConfig for a given level.

func (AutonomyConfig) NeedsPermission

func (c AutonomyConfig) NeedsPermission(toolName string, isSafe bool) bool

NeedsPermission returns true when the tool call should prompt the user. isSafe indicates whether the specific invocation has been classified as safe (e.g. a non-destructive bash command).

type AutonomyLevel

type AutonomyLevel int

AutonomyLevel controls how much the agent can do without asking the user.

const (
	// AutonomySupervised asks for permission on every tool call.
	AutonomySupervised AutonomyLevel = 0
	// AutonomyBasic auto-allows read-only tools.
	AutonomyBasic AutonomyLevel = 1
	// AutonomySemi auto-allows reads and writes, asks for Bash.
	AutonomySemi AutonomyLevel = 2
	// AutonomyFull auto-allows everything except destructive commands.
	AutonomyFull AutonomyLevel = 3
	// AutonomyYOLO never asks for permission.
	AutonomyYOLO AutonomyLevel = 4
)

func ParseAutonomyLevel

func ParseAutonomyLevel(s string) AutonomyLevel

ParseAutonomyLevel converts a string name or number to an AutonomyLevel.

func (AutonomyLevel) String

func (l AutonomyLevel) String() string

String returns the human-readable name of an autonomy level.

type BackgroundAgentPool

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

BackgroundAgentPool manages async sub-agents that run in the background. When background agents finish, their results are collected for re-injection into the main agent loop (inspired by herm's completion cycles).

func NewBackgroundAgentPool

func NewBackgroundAgentPool() *BackgroundAgentPool

NewBackgroundAgentPool creates a pool with configurable wait limits.

func (*BackgroundAgentPool) AllResults

func (p *BackgroundAgentPool) AllResults() []BackgroundResult

AllResults returns all results collected so far (completed background tasks).

func (*BackgroundAgentPool) Collect

func (p *BackgroundAgentPool) Collect() []BackgroundResult

Collect gathers all completed background results without blocking. Returns immediately with whatever results are available.

func (*BackgroundAgentPool) HasPending

func (p *BackgroundAgentPool) HasPending() bool

HasPending returns true if background agents are still running.

func (*BackgroundAgentPool) PendingCount

func (p *BackgroundAgentPool) PendingCount() int

PendingCount returns the number of in-flight background agents.

func (*BackgroundAgentPool) Submit

func (p *BackgroundAgentPool) Submit(id, prompt string, spawn func(ctx context.Context, prompt string) (string, error))

Submit launches a background sub-agent. The spawn function runs asynchronously.

func (*BackgroundAgentPool) WaitAll

func (p *BackgroundAgentPool) WaitAll() []BackgroundResult

WaitAll blocks until all pending tasks complete or timeout.

type BackgroundResult

type BackgroundResult struct {
	ID      string
	Prompt  string
	Output  string
	Error   error
	Elapsed time.Duration
}

BackgroundResult holds the output of a completed background agent.

type BacktrackEngine

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

BacktrackEngine records decision points during agent execution and provides the ability to identify the most recent failure and generate a retry prompt with alternative approaches.

func NewBacktrackEngine

func NewBacktrackEngine() *BacktrackEngine

NewBacktrackEngine creates a new backtrack engine that retains at most 50 decision points.

func (*BacktrackEngine) FindBacktrackPoint

func (be *BacktrackEngine) FindBacktrackPoint() *DecisionPoint

FindBacktrackPoint returns the most recent failed decision point that has alternative approaches available, or nil if none exists.

func (*BacktrackEngine) GenerateRetryPrompt

func (be *BacktrackEngine) GenerateRetryPrompt(dp *DecisionPoint) string

GenerateRetryPrompt builds a prompt that tells the agent what failed and suggests alternative approaches.

func (*BacktrackEngine) MarkOutcome

func (be *BacktrackEngine) MarkOutcome(turnIdx int, outcome string)

MarkOutcome sets the outcome ("success" or "failure") for the decision at the given turn index. If multiple decisions exist at the same turn index, the most recent one is updated.

func (*BacktrackEngine) RecordDecision

func (be *BacktrackEngine) RecordDecision(turnIdx int, desc string, alternatives []string, msgs []client.EyrieMessage)

RecordDecision saves a decision point with the current conversation state. If the number of recorded points exceeds the maximum, the oldest point is removed.

func (*BacktrackEngine) RestoreState

func (be *BacktrackEngine) RestoreState(dp *DecisionPoint) []client.EyrieMessage

RestoreState returns the conversation messages captured at the given decision point, representing the state just before (not including) the failed decision. This allows the agent to retry from that point.

func (*BacktrackEngine) Size

func (be *BacktrackEngine) Size() int

Size returns the number of recorded decision points.

type Belief

type Belief struct {
	ID           string
	Category     string  // "file_purpose", "function_behavior", "dependency", "architecture"
	Subject      string  // file or symbol name
	Content      string  // what we believe about it
	Confidence   float64 // 0-1
	DiscoveredAt int     // turn index when discovered
	LastVerified int     // turn index when last confirmed
}

Belief represents a single piece of discovered knowledge about the codebase.

type BeliefState

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

BeliefState tracks what the agent has discovered about the codebase to prevent forgetting across long conversations. Beliefs are keyed by a generated ID.

func NewBeliefState

func NewBeliefState() *BeliefState

NewBeliefState creates an empty belief state.

func (*BeliefState) FormatForPrompt

func (bs *BeliefState) FormatForPrompt() string

FormatForPrompt returns a formatted string summarizing all current beliefs, suitable for injection into the system prompt.

func (*BeliefState) Get

func (bs *BeliefState) Get(subject string) []*Belief

Get returns all beliefs about a given subject.

func (*BeliefState) Invalidate

func (bs *BeliefState) Invalidate(subject string)

Invalidate marks all beliefs about a subject as stale by halving their confidence. This should be called when a file is modified, since our beliefs about it may no longer hold.

func (*BeliefState) Prune

func (bs *BeliefState) Prune(currentTurn int)

Prune removes beliefs that have not been verified in the last 20 turns, keeping the belief state manageable in long conversations.

func (*BeliefState) Record

func (bs *BeliefState) Record(category, subject, content string, turn int)

Record adds or updates a belief. If a belief with the same category and subject already exists, it is updated with the new content and turn index.

func (*BeliefState) Size

func (bs *BeliefState) Size() int

Size returns the number of active beliefs.

type CascadeRouter

type CascadeRouter struct {
	Enabled      bool
	FrugalMode   bool   // more aggressive downgrading
	DefaultModel string // fallback when classification is inconclusive
	Roles        routing.ModelRoles
	// contains filtered or unexported fields
}

CascadeRouter selects the optimal model for each request based on task complexity. It uses pre-request classification (before we have a response) to route:

  • simple/chat tasks -> cheap model (haiku)
  • debug/review/refactor -> mid model (sonnet)
  • generation -> expensive model (opus)

The router also tracks routing decisions for analytics.

func NewCascadeRouter

func NewCascadeRouter(defaultModel string, roles routing.ModelRoles) *CascadeRouter

NewCascadeRouter creates a router with sensible defaults. The defaultModel is used as the fallback when classification yields no strong signal and no role-specific model is configured.

func (*CascadeRouter) DecisionCount

func (cr *CascadeRouter) DecisionCount() int

DecisionCount returns how many routing decisions have been recorded.

func (*CascadeRouter) Decisions

func (cr *CascadeRouter) Decisions() []RoutingDecision

Decisions returns a snapshot of all routing decisions made so far.

func (*CascadeRouter) Savings

func (cr *CascadeRouter) Savings() float64

Savings estimates the USD saved by routing decisions compared to always using the most expensive model. This is a rough heuristic: it sums the per-million-token price difference for each decision where the selected model is cheaper than the original.

func (*CascadeRouter) SelectModel

func (cr *CascadeRouter) SelectModel(prompt string, currentModel string, userOverride string) string

SelectModel picks the best model for a given prompt. If userOverride is non-empty the user's explicit choice always wins (override is never downgraded). The returned string is the model name to use for the API call.

func (*CascadeRouter) Summary

func (cr *CascadeRouter) Summary() string

Summary returns a human-readable summary of routing activity.

type CompactConfig

type CompactConfig struct {
	AutoEnabled       bool
	ContextWindowSize int
	AutoCompactBuffer int
	MaxOutputTokens   int
	MaxFailures       int
}

CompactConfig controls auto-compaction behavior.

func DefaultCompactConfig

func DefaultCompactConfig() CompactConfig

DefaultCompactConfig returns sensible defaults matching the archive behavior.

type CompactResult

type CompactResult struct {
	Messages     []client.EyrieMessage
	Summary      string
	TokensBefore int
	TokensAfter  int
	Strategy     string
}

CompactResult holds the outcome of a compaction operation.

type CompactStrategy

type CompactStrategy interface {
	Name() string
	ShouldTrigger(msgs []client.EyrieMessage, tokenCount, threshold int) bool
	Compact(ctx context.Context, s *Session) (*CompactResult, error)
}

CompactStrategy defines a conversation compaction approach.

type CompactVariant

type CompactVariant int

CompactVariant determines which compaction prompt style to use.

const (
	CompactBase    CompactVariant = iota // Full conversation
	CompactPartial                       // Recent messages only
	CompactUpTo                          // Prefix summarization
)

type ContextAllocation

type ContextAllocation struct {
	SystemPrompt   int
	ToolDefs       int
	RepoMap        int
	Memory         int
	Workspace      int
	PreloadedFiles int
	Conversation   int
	OutputReserve  int
	SafetyMargin   int
	Remaining      int // should be ~0 if properly allocated
}

ContextAllocation shows where tokens are going for the current conversation state.

type ContextBudget

type ContextBudget struct {
	Total int // model's full context window

	// Fixed allocations
	SystemPrompt int // 3000-5000 tokens (rules, identity)
	ToolDefs     int // 2000-3000 tokens (tool descriptions)
	RepoMap      int // 2000-4000 tokens (ranked symbol map)
	Memory       int // 1000-2000 tokens (yaad/zenbrain context)
	Workspace    int // 500 tokens (git status, branch, recent commits)

	// Adaptive allocations
	PreloadedFiles int // 10000-30000 tokens (relevant code context)
	Conversation   int // remaining (managed by compaction)

	// Reserved
	OutputReserve int // 16000-20000 tokens (model response space)
	SafetyMargin  int // 10000-15000 tokens (estimation errors, API overhead)
}

ContextBudget allocates the model's context window across different content categories. Based on research: proper allocation prevents context overflow and optimizes information density per token spent.

The allocator ensures:

  • Fixed allocations for system prompt, repo map, memory, workspace context
  • Adaptive allocation for pre-loaded files (expands when conversation is short)
  • Managed conversation history (triggers compaction when exceeded)
  • Reserved budget for model output and safety margin

Research basis: All top coding agents (Claude Code, Cursor, Aider) manage context, but none formalize it as an explicit budget with categories. This is the missing architectural glue.

func NewContextBudget

func NewContextBudget(contextSize int) *ContextBudget

NewContextBudget creates a budget for the given model context size. Allocations scale proportionally with the context window while respecting sensible floors and ceilings per category.

func (*ContextBudget) Allocate

func (b *ContextBudget) Allocate(conversationTokens int) *ContextAllocation

Allocate distributes the budget based on current conversation length. As conversation grows, PreloadedFiles shrinks to make room.

func (*ContextBudget) FilesBudget

func (b *ContextBudget) FilesBudget(conversationTokens int) int

FilesBudget returns the current budget available for pre-loaded file context.

func (*ContextBudget) ShouldCompact

func (b *ContextBudget) ShouldCompact(conversationTokens int) bool

ShouldCompact returns true if conversation exceeds its allocation.

func (*ContextBudget) UsageReport

func (b *ContextBudget) UsageReport(conversationTokens int) string

UsageReport returns a human-readable breakdown of current allocation.

type Cost

type Cost struct {
	Model            string
	PromptTokens     int
	CompletionTokens int
	CacheReadTokens  int
	CacheWriteTokens int
	TotalCostUSD     float64
	// contains filtered or unexported fields
}

Cost tracks token usage and estimated cost.

func (*Cost) Add

func (c *Cost) Add(prompt, completion int)

Add records token usage from a response.

func (*Cost) AddCacheTokens

func (c *Cost) AddCacheTokens(read, write int)

AddCacheTokens records cache token usage (priced at ~10% of input).

func (*Cost) Summary

func (c *Cost) Summary() string

Summary returns a formatted cost string.

func (*Cost) Total

func (c *Cost) Total() float64

Total returns the estimated total cost in USD.

func (*Cost) TotalUSD

func (c *Cost) TotalUSD() float64

TotalUSD returns the estimated total cost (same as Total — unified pricing).

type CostEntry

type CostEntry struct {
	SessionID string
	TaskGoal  string
	TotalCost float64
	Duration  time.Duration
	Success   bool
	Timestamp time.Time
}

CostEntry represents a single cost data point recorded at session end.

type CostTracker

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

CostTracker records per-request cost entries for analytics and optimization. Data is appended to ~/.hawk/cost.jsonl for cross-session analysis.

func NewCostTracker

func NewCostTracker(sessionID string) *CostTracker

NewCostTracker creates a tracker that persists to ~/.hawk/cost.jsonl.

func (*CostTracker) Entries

func (ct *CostTracker) Entries() []analytics.CostEntry

Entries returns all recorded entries for this session.

func (*CostTracker) Record

func (ct *CostTracker) Record(entry analytics.CostEntry) error

Record adds a cost entry and persists it.

func (*CostTracker) SessionTotal

func (ct *CostTracker) SessionTotal() float64

SessionTotal returns total USD spent in the current session.

type CostTrackerInterface

type CostTrackerInterface interface {
	Record(entry CostEntry) error
	SessionTotal() float64
}

CostTrackerInterface abstracts cost recording and querying.

type CouncilConfig

type CouncilConfig struct {
	Models   []string // council member model names
	Chairman string   // chairman model (synthesizer)
}

CouncilConfig controls the Karpathy LLM Council pattern.

type CouncilRanking

type CouncilRanking struct {
	Model   string
	Ranking string
}

CouncilRanking holds one model's ranking of responses.

type CouncilResponse

type CouncilResponse struct {
	Model    string
	Response string
}

CouncilResponse holds one model's contribution.

type CouncilResult

type CouncilResult struct {
	Responses []CouncilResponse
	Rankings  []CouncilRanking
	Synthesis string
}

CouncilResult holds the full council output.

func RunCouncil

func RunCouncil(ctx context.Context, query string, cfg CouncilConfig, sess *Session) (*CouncilResult, error)

RunCouncil implements Karpathy's 3-stage LLM Council pattern:

  1. Send query to all models in parallel, collect responses
  2. Anonymize responses, send ranking prompt to all models in parallel
  3. Send all responses + rankings to chairman for synthesis

type Critic

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

Critic provides fast pre-validation of patches using a cheap model before expensive execution. It generates a prompt for the cheap model and parses the response into a structured verdict.

func NewCritic

func NewCritic(model string) *Critic

NewCritic creates a new critic that will use the given model for screening.

func (*Critic) BuildPrompt

func (c *Critic) BuildPrompt(original, patched, intent string) string

BuildPrompt constructs a prompt for the cheap model to evaluate a patch.

func (*Critic) Model

func (c *Critic) Model() string

Model returns the model name used for pre-screening.

func (*Critic) ParseVerdict

func (c *Critic) ParseVerdict(response string) *PatchVerdict

ParseVerdict parses a model response into a structured PatchVerdict.

func (*Critic) PreScreenPatch

func (c *Critic) PreScreenPatch(originalContent, patchedContent, intent string) *PatchVerdict

PreScreenPatch asks the cheap model whether a patch looks correct given the stated intent. It builds a prompt, and returns a verdict. In this implementation, the caller is expected to send the prompt to the model and pass the response to ParseVerdict. This method constructs a PatchVerdict based on a simple heuristic comparison when no model call is available.

func (*Critic) ShouldBlock

func (c *Critic) ShouldBlock(verdict *PatchVerdict) bool

ShouldBlock returns true if the verdict indicates the patch should be blocked (verdict is "incorrect" with confidence > 0.8).

type DecisionPoint

type DecisionPoint struct {
	TurnIndex    int
	Description  string                // what was decided
	Alternatives []string              // other options available
	Outcome      string                // "success", "failure", or "" (pending)
	Messages     []client.EyrieMessage // conversation state at this point
}

DecisionPoint captures a point in the conversation where the agent made a choice that can potentially be rolled back if it leads to failure.

type DiffSandbox

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

DiffSandbox holds pending file changes so the user can review diffs before applying.

func NewDiffSandbox

func NewDiffSandbox() *DiffSandbox

NewDiffSandbox creates a new, enabled DiffSandbox.

func (*DiffSandbox) Apply

func (ds *DiffSandbox) Apply(path string) error

Apply writes one pending change to disk and removes it from the sandbox.

func (*DiffSandbox) ApplyAll

func (ds *DiffSandbox) ApplyAll() (int, error)

ApplyAll writes all pending changes to disk and clears the sandbox.

func (*DiffSandbox) DiffAll

func (ds *DiffSandbox) DiffAll() string

DiffAll returns all diffs combined.

func (*DiffSandbox) DiffFor

func (ds *DiffSandbox) DiffFor(path string) string

DiffFor returns the unified diff for a single pending file.

func (*DiffSandbox) Disable

func (ds *DiffSandbox) Disable()

Disable deactivates the sandbox.

func (*DiffSandbox) Enable

func (ds *DiffSandbox) Enable()

Enable activates the sandbox.

func (*DiffSandbox) Format

func (ds *DiffSandbox) Format() string

Format returns a human-readable summary of all pending changes.

func (*DiffSandbox) Get

func (ds *DiffSandbox) Get(path string) *PendingChange

Get returns the pending change for a specific path, or nil.

func (*DiffSandbox) IsEnabled

func (ds *DiffSandbox) IsEnabled() bool

IsEnabled returns whether the sandbox is active.

func (*DiffSandbox) List

func (ds *DiffSandbox) List() []*PendingChange

List returns all pending changes sorted by path.

func (*DiffSandbox) Reject

func (ds *DiffSandbox) Reject(path string)

Reject discards the pending change for one file.

func (*DiffSandbox) RejectAll

func (ds *DiffSandbox) RejectAll()

RejectAll discards all pending changes.

func (*DiffSandbox) Stage

func (ds *DiffSandbox) Stage(path, action, oldContent, newContent string)

Stage records a pending file change and computes a unified diff.

type ErrorPattern

type ErrorPattern struct {
	Trigger    string    `json:"trigger"`    // error message pattern
	RootCause  string    `json:"root_cause"` // why it happens
	Resolution string    `json:"resolution"` // how to fix it
	HitCount   int       `json:"hit_count"`  // times encountered
	LastSeen   time.Time `json:"last_seen"`
}

ErrorPattern records a known error trigger, root cause, and resolution.

type ErrorPatternDB

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

ErrorPatternDB learns from tool failures and prevents repeating mistakes.

func NewErrorPatternDB

func NewErrorPatternDB() *ErrorPatternDB

NewErrorPatternDB creates a database backed by ~/.hawk/error_patterns.json.

func (*ErrorPatternDB) FormatHints

func (db *ErrorPatternDB) FormatHints(errorMsg string) string

FormatHints returns actionable hints for an error from known patterns.

func (*ErrorPatternDB) Match

func (db *ErrorPatternDB) Match(errorMsg string) []ErrorPattern

Match finds patterns that match the given error message.

func (*ErrorPatternDB) Record

func (db *ErrorPatternDB) Record(trigger, rootCause, resolution string)

Record adds or updates an error pattern.

type EvolvingMemoryAdapter

type EvolvingMemoryAdapter struct {
	EM *memory.EvolvingMemory
}

EvolvingMemoryAdapter bridges memory.EvolvingMemory to the EvolvingMemoryInterface.

func (*EvolvingMemoryAdapter) Format

func (a *EvolvingMemoryAdapter) Format() string

func (*EvolvingMemoryAdapter) Learn

func (a *EvolvingMemoryAdapter) Learn(pattern, lesson string) error

func (*EvolvingMemoryAdapter) Retrieve

func (a *EvolvingMemoryAdapter) Retrieve(query string) []string

type EvolvingMemoryInterface

type EvolvingMemoryInterface interface {
	Learn(pattern, lesson string) error
	Retrieve(query string) []string
	Format() string
}

EvolvingMemoryInterface abstracts guideline retrieval and learning so the lifecycle can be tested without real storage.

type FewShotExample

type FewShotExample struct {
	Prompt    string    `json:"prompt"`
	Response  string    `json:"response"`
	TaskType  string    `json:"task_type"`
	Quality   float64   `json:"quality"` // 0-1, based on whether output was kept
	CreatedAt time.Time `json:"created_at"`
	UsedCount int       `json:"used_count"`
}

FewShotExample is a recorded successful interaction.

type FewShotStore

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

FewShotStore collects successful (prompt, response) pairs and injects the most relevant as few-shot examples into the system prompt.

func NewFewShotStore

func NewFewShotStore() *FewShotStore

NewFewShotStore creates a store backed by ~/.hawk/fewshot.json.

func (*FewShotStore) FormatForPrompt

func (fs *FewShotStore) FormatForPrompt(prompt string) string

FormatForPrompt returns few-shot examples formatted for system prompt injection.

func (*FewShotStore) Record

func (fs *FewShotStore) Record(prompt, response, taskType string)

Record saves a successful interaction as a potential few-shot example.

func (*FewShotStore) Retrieve

func (fs *FewShotStore) Retrieve(prompt string, topK int) []FewShotExample

Retrieve finds the most relevant few-shot examples for a given prompt.

type LLMClient

type LLMClient interface {
	Chat(ctx context.Context, msgs []client.EyrieMessage, opts client.ChatOptions) (*client.EyrieResponse, error)
}

LLMClient is the interface for sending chat requests to an LLM provider. It is satisfied by *client.EyrieClient.

type LimitTracker

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

LimitTracker tracks usage against limits.

func NewLimitTracker

func NewLimitTracker(limits SafetyLimits) *LimitTracker

NewLimitTracker creates a LimitTracker with the given limits.

func (*LimitTracker) IsExceeded

func (lt *LimitTracker) IsExceeded() (bool, string)

IsExceeded returns true and a human-readable reason when any limit is breached.

func (*LimitTracker) RecordCost

func (lt *LimitTracker) RecordCost(usd float64)

RecordCost adds to the running cost total.

func (*LimitTracker) RecordTokens

func (lt *LimitTracker) RecordTokens(n int)

RecordTokens adds output tokens to the running total.

func (*LimitTracker) RecordToolCall

func (lt *LimitTracker) RecordToolCall(toolName string)

RecordToolCall records a tool invocation. Tools named "Bash" or "bash" also increment the bash command counter; "Write" or "Edit" increment file writes.

func (*LimitTracker) RecordTurn

func (lt *LimitTracker) RecordTurn()

RecordTurn increments the turn counter.

func (*LimitTracker) Summary

func (lt *LimitTracker) Summary() string

Summary returns a one-line summary of usage vs limits.

type LoopDetector

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

LoopDetector detects repeated identical tool call patterns using SHA-256 signatures. Supports two severity levels: warning (soft) and doom loop (hard escalation).

func NewLoopDetector

func NewLoopDetector(windowSize, maxRepeats int) *LoopDetector

NewLoopDetector creates a detector with a sliding window.

func (*LoopDetector) DoomLoopWarning

func (ld *LoopDetector) DoomLoopWarning() string

DoomLoopWarning returns the hard escalation message (repeated loops).

func (*LoopDetector) Escalated

func (ld *LoopDetector) Escalated() bool

Escalated returns whether the detector has already fired a warning.

func (*LoopDetector) IsDoomLoop

func (ld *LoopDetector) IsDoomLoop() bool

IsDoomLoop returns true when the agent has been stuck for DoomLoopThreshold consecutive escalation attempts — it should hard-stop and ask the user.

func (*LoopDetector) IsLooping

func (ld *LoopDetector) IsLooping() bool

IsLooping returns true if any signature appears more than maxRepeats times in the window.

func (*LoopDetector) LoopWarning

func (ld *LoopDetector) LoopWarning() string

LoopWarning returns the soft warning message (first detection).

func (*LoopDetector) MarkEscalated

func (ld *LoopDetector) MarkEscalated()

MarkEscalated records that a warning was shown.

func (*LoopDetector) RecordStep

func (ld *LoopDetector) RecordStep(toolNames []string, inputs []string, outputs []string)

RecordStep hashes the tool calls and results from a single agent step.

func (*LoopDetector) Reset

func (ld *LoopDetector) Reset()

Reset clears the escalation state (e.g., after user provides new direction).

type MemoryRecaller

type MemoryRecaller interface {
	Recall(query string, tokenBudget int) (string, error)
	Remember(content, category string) error
}

MemoryRecaller abstracts memory recall/remember so engine avoids importing memory directly.

type MicroCompactConfig

type MicroCompactConfig struct {
	CompactableTools map[string]bool
	TimeGapMins      float64
	KeepRecent       int
}

MicroCompactConfig controls micro-compaction behavior.

func DefaultMicroCompactConfig

func DefaultMicroCompactConfig() MicroCompactConfig

DefaultMicroCompactConfig returns the default micro-compaction settings.

type MicroCompactStrategy

type MicroCompactStrategy struct{}

MicroCompactStrategy clears old tool result content while preserving message structure.

func (*MicroCompactStrategy) Compact

func (s *MicroCompactStrategy) Compact(ctx context.Context, sess *Session) (*CompactResult, error)

func (*MicroCompactStrategy) Name

func (s *MicroCompactStrategy) Name() string

func (*MicroCompactStrategy) ShouldTrigger

func (s *MicroCompactStrategy) ShouldTrigger(msgs []client.EyrieMessage, tokenCount, threshold int) bool

ShouldTrigger fires when there are enough messages with compactable tool results and sufficient time has passed since the last assistant message (cache is cold).

type ModelTier

type ModelTier int

ModelTier represents the cost tier of a model.

const (
	TierCheap     ModelTier = iota // haiku-class
	TierMid                        // sonnet-class
	TierExpensive                  // opus-class
)

type PatchVerdict

type PatchVerdict struct {
	Likely     string   // "correct", "incorrect", "uncertain"
	Issues     []string // specific issues found
	Confidence float64  // 0-1
}

PatchVerdict is the result of a critic's pre-screening of a patch.

type PendingChange

type PendingChange struct {
	Path       string
	Action     string // "create", "edit", "overwrite"
	OldContent string
	NewContent string
	Diff       string
	CreatedAt  time.Time
}

PendingChange represents a staged file modification that has not yet been applied to disk.

type PermissionEngine

type PermissionEngine struct {
	Memory     *PermissionMemory
	AutoMode   *permissions.AutoModeState
	Classifier *permissions.Classifier
	BypassKill *permissions.BypassKillswitch
	Mode       PermissionMode
	Autonomy   AutonomyLevel
	PromptFn   func(PermissionRequest) // callback to ask user
}

PermissionEngine encapsulates all permission-checking logic. Extracted from Session to keep the god object lean.

func NewPermissionEngine

func NewPermissionEngine() *PermissionEngine

NewPermissionEngine creates a PermissionEngine with sensible defaults.

func (*PermissionEngine) ApplyToolState

func (pe *PermissionEngine) ApplyToolState(name string)

ApplyToolState updates permission mode based on plan mode tools.

func (*PermissionEngine) CheckTool

func (pe *PermissionEngine) CheckTool(ctx context.Context, tc toolCallInfo) (bool, string)

CheckTool determines if a tool call is allowed, denied, or needs user prompt. Returns (granted bool, denyReason string). If the user must be asked, it blocks on PromptFn with a 5-minute timeout.

func (*PermissionEngine) SetMode

func (pe *PermissionEngine) SetMode(mode string) error

SetMode applies a permission mode string.

type PermissionMemory

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

PermissionMemory stores always-allow and always-deny rules.

func NewPermissionMemory

func NewPermissionMemory() *PermissionMemory

func (*PermissionMemory) AllowSpec

func (pm *PermissionMemory) AllowSpec(spec string)

AllowSpec applies an archive-style permission rule, e.g. "Bash(git:*)".

func (*PermissionMemory) AlwaysAllow

func (pm *PermissionMemory) AlwaysAllow(toolName string)

AlwaysAllow marks a tool as always allowed.

func (*PermissionMemory) AlwaysAllowPattern

func (pm *PermissionMemory) AlwaysAllowPattern(pattern string)

AlwaysAllowPattern adds a pattern rule (e.g. "bash:go *").

func (*PermissionMemory) AlwaysDeny

func (pm *PermissionMemory) AlwaysDeny(toolName string)

AlwaysDeny marks a tool as always denied.

func (*PermissionMemory) AlwaysDenyPattern

func (pm *PermissionMemory) AlwaysDenyPattern(pattern string)

AlwaysDenyPattern adds a deny pattern rule.

func (*PermissionMemory) Check

func (pm *PermissionMemory) Check(toolName string, summary string) *bool

Check returns: true=allowed, false=denied, nil=ask user.

func (*PermissionMemory) DenySpec

func (pm *PermissionMemory) DenySpec(spec string)

DenySpec applies an archive-style deny rule, e.g. "Write(*.env)".

type PermissionMode

type PermissionMode string

PermissionMode controls how permission prompts are handled.

const (
	PermissionModeDefault           PermissionMode = "default"
	PermissionModeAcceptEdits       PermissionMode = "acceptEdits"
	PermissionModeBypassPermissions PermissionMode = "bypassPermissions"
	PermissionModeDontAsk           PermissionMode = "dontAsk"
	PermissionModePlan              PermissionMode = "plan"
)

type PermissionRequest

type PermissionRequest struct {
	ToolName string
	ToolID   string
	Summary  string
	Response chan bool
}

PermissionRequest is sent from engine to TUI when a tool needs approval.

type PlanState

type PlanState struct {
	Name     string
	Subtasks []Subtask
	Current  int
	Active   bool
}

PlanState tracks progress through a set of subtasks.

func NewPlanState

func NewPlanState(name string) *PlanState

NewPlanState creates a new plan with the given name and no subtasks.

func (*PlanState) Format

func (ps *PlanState) Format() string

Format returns a human-readable display of the plan state.

func (*PlanState) MarkDone

func (ps *PlanState) MarkDone(id int)

MarkDone sets the subtask with the given ID to "done".

func (*PlanState) Next

func (ps *PlanState) Next() *Subtask

Next returns the next pending subtask and marks it as in_progress, or nil if none remain.

func (*PlanState) Progress

func (ps *PlanState) Progress() string

Progress returns a short progress string like "3/7 subtasks complete".

func (*PlanState) Skip

func (ps *PlanState) Skip(id int)

Skip sets the subtask with the given ID to "skipped".

type PromptAdjustment

type PromptAdjustment struct {
	Rule       string    `json:"rule"`       // "always use tabs" or "never add comments"
	Source     string    `json:"source"`     // user message that triggered this
	Polarity   string    `json:"polarity"`   // "do" or "dont"
	Confidence float64   `json:"confidence"` // increases with reinforcement
	Active     bool      `json:"active"`
	CreatedAt  time.Time `json:"created_at"`
	LastUsed   time.Time `json:"last_used"`
}

PromptAdjustment is a user-derived prompt modification.

type PromptTuner

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

PromptTuner optimizes system prompt sections based on session outcomes. Tracks which prompt configurations lead to successful sessions and adjusts over time (OPRO/EvoPrompt pattern without LLM calls).

func NewPromptTuner

func NewPromptTuner() *PromptTuner

NewPromptTuner creates a tuner backed by ~/.hawk/prompt_tuning.json.

func (*PromptTuner) BestVariant

func (pt *PromptTuner) BestVariant(section string) (string, float64)

BestVariant returns the highest-scoring variant for a section.

func (*PromptTuner) RecordOutcome

func (pt *PromptTuner) RecordOutcome(section, content string, success bool)

RecordOutcome updates the variant score based on a session outcome.

func (*PromptTuner) Report

func (pt *PromptTuner) Report() string

Report returns a summary of all tracked variants sorted by score.

type PromptVariant

type PromptVariant struct {
	Section   string    `json:"section"`   // which section was varied
	Content   string    `json:"content"`   // the variant content
	Score     float64   `json:"score"`     // success rate (0-1)
	Uses      int       `json:"uses"`      // times used
	Successes int       `json:"successes"` // successful sessions with this variant
	LastUsed  time.Time `json:"last_used"`
}

PromptVariant is a tracked prompt configuration with its performance score.

type ProtectedPaths

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

ProtectedPaths tracks file paths that are read-only within the session. Tools that write or edit files should check IsProtected before proceeding.

func NewProtectedPaths

func NewProtectedPaths() *ProtectedPaths

NewProtectedPaths creates an empty ProtectedPaths set.

func (*ProtectedPaths) Add

func (p *ProtectedPaths) Add(path string)

Add marks a path as protected (read-only). The path is cleaned before storage for consistent lookups.

func (*ProtectedPaths) Format

func (p *ProtectedPaths) Format() string

Format returns a human-readable block suitable for system prompt injection.

func (*ProtectedPaths) IsProtected

func (p *ProtectedPaths) IsProtected(path string) bool

IsProtected returns true when path (or any ancestor directory) is protected.

func (*ProtectedPaths) List

func (p *ProtectedPaths) List() []string

List returns a sorted slice of all protected paths.

func (*ProtectedPaths) Remove

func (p *ProtectedPaths) Remove(path string)

Remove unmarks a path so it is no longer protected.

type Reflection

type Reflection struct {
	Attempt    int       // which attempt number this reflects on
	TaskGoal   string    // what the task was trying to accomplish
	WhatFailed string    // what specifically went wrong
	WhyFailed  string    // root cause analysis
	WhatToDo   string    // what should be done differently next time
	Timestamp  time.Time // when the reflection was generated
}

Reflection captures a structured verbal self-reflection on a failed attempt.

type Reflector

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

Reflector generates verbal self-reflections after task attempts. Based on Reflexion (Shinn et al., NeurIPS 2023): verbal reinforcement learning achieves 91% on HumanEval by storing natural-language reflections in an episodic memory buffer for subsequent attempts.

Instead of mechanical extraction ("tools used: X, files touched: Y"), we ask the LLM: "Analyze what went wrong and what should be done differently." This produces richer, actionable feedback that subsequent attempts can use.

func NewReflector

func NewReflector(llm LLMClient, model string) *Reflector

NewReflector creates a Reflector that uses the given LLM client and model to generate verbal reflections on failed task attempts.

func (*Reflector) History

func (r *Reflector) History() []Reflection

History returns a copy of all reflections generated so far.

func (*Reflector) InjectReflections

func (r *Reflector) InjectReflections() string

InjectReflections formats all accumulated reflections into a block of text suitable for prepending to the next attempt's prompt. If there are no reflections yet, it returns an empty string.

func (*Reflector) Reflect

func (r *Reflector) Reflect(ctx context.Context, goal string, messages []client.EyrieMessage, errorMsg string) (*Reflection, error)

Reflect generates a verbal self-reflection on a failed attempt by asking the LLM to analyze the conversation history and error. The resulting Reflection is appended to the internal history for later injection.

func (*Reflector) Reset

func (r *Reflector) Reset()

Reset clears the reflection history.

type RoutingDecision

type RoutingDecision struct {
	OriginalModel string    `json:"original_model"`
	SelectedModel string    `json:"selected_model"`
	TaskType      string    `json:"task_type"`
	Reason        string    `json:"reason"`
	Timestamp     time.Time `json:"timestamp"`
}

RoutingDecision records a single model selection event.

type SafetyLimits

type SafetyLimits struct {
	MaxToolCalls    int     // max total tool invocations (default: 200)
	MaxFileWrites   int     // max files created/modified (default: 50)
	MaxBashCommands int     // max bash executions (default: 100)
	MaxCostUSD      float64 // max spend (default: from MaxBudgetUSD)
	MaxTurns        int     // max LLM turns (default: from MaxTurns)
	MaxOutputTokens int     // max total output tokens (default: 500K)
}

SafetyLimits caps what the agent can do in a single session.

func DefaultLimits

func DefaultLimits() SafetyLimits

DefaultLimits returns conservative safety limits for normal interactive use.

func ResearchLimits

func ResearchLimits() SafetyLimits

ResearchLimits returns strict per-iteration limits suitable for research tasks.

func VibeLimits

func VibeLimits() SafetyLimits

VibeLimits returns more permissive limits for autonomous/vibe mode.

type SelfReviewResult

type SelfReviewResult struct {
	Approved    bool     // whether the change should be applied
	Issues      []string // problems found during review
	Suggestions []string // improvements that could be made
	Confidence  float64  // 0.0-1.0: how confident the reviewer is in its assessment
}

SelfReviewResult holds the outcome of an LLM self-review of a proposed change.

func ReviewBeforeWrite

func ReviewBeforeWrite(ctx context.Context, llm LLMClient, model string,
	intent string, filePath string, oldContent string, newContent string) (*SelfReviewResult, error)

ReviewBeforeWrite asks the model to review its own proposed changes before they are applied to a file. It compares old and new content in the context of the user's original intent and returns approval or a list of issues.

If confidence is below ConfidenceThreshold (0.7), the review will suggest fixes rather than approving, regardless of whether explicit issues were found.

type Session

type Session struct {
	Cost   Cost
	Router *modelPkg.Router
	Perm   *PermissionEngine // extracted permission subsystem
	// Backward-compatible accessors below (will be removed after full migration)
	Permissions    *PermissionMemory             // use Perm.Memory
	AutoMode       *permissions.AutoModeState    // use Perm.AutoMode
	Classifier     *permissions.Classifier       // use Perm.Classifier
	BypassKill     *permissions.BypassKillswitch // use Perm.BypassKill
	Mode           PermissionMode                // use Perm.Mode
	MaxTurns       int
	MaxBudgetUSD   float64
	AllowedDirs    []string
	PermissionFn   func(PermissionRequest) // use Perm.PromptFn
	AgentSpawnFn   func(ctx context.Context, prompt string) (string, error)
	AskUserFn      func(question string) (string, error)
	Memory         MemoryRecaller
	YaadBridge     *memory.YaadBridge
	EnhancedMemory *memory.EnhancedMemoryManager

	PinnedMessages          int // messages to protect from compaction (from /pin)
	AutoCompactThresholdPct int // token % to trigger auto-compact (default 85)

	// Cost optimization
	Cascade     *CascadeRouter    // cascade.go — model tier routing
	Lifecycle   *SessionLifecycle // lifecycle.go — self-improvement loop
	Reflector   *Reflector        // reflect.go — verbal self-reflection
	CostTracker *CostTracker      // cost_tracker.go — per-request cost persistence

	// Advanced features
	Autonomy       AutonomyLevel           // autonomy.go — permission level
	Sandbox        *DiffSandbox            // diffsandbox.go — staged file changes
	Plan           *PlanState              // subtask.go — user-activated plan
	Beliefs        *BeliefState            // belief.go — discovered knowledge
	Critic         *Critic                 // critic.go — patch pre-screening
	Backtrack      *BacktrackEngine        // backtrack.go — decision recording
	Limits         *LimitTracker           // limits.go — safety limits
	Teach          TeachConfig             // teach.go — explanation depth
	Trajectory     *TrajectoryDistiller    // trajectory.go — multi-run distillation
	Shadow         *ShadowWorkspace        // shadow.go — edit pre-validation
	Snapshots      SnapshotTracker         // snapshot integration for auto-tracking
	ConvoDAG       *convodag.DAG           // conversation DAG for branching/forking
	Sleeptime      *memory.SleeptimeAgent  // sleeptime.go — background memory consolidation
	Activity       *memory.ActivityTracker // activity.go — memory save nudging (Engram pattern)
	SkillDistiller *memory.SkillDistiller  // skill_distill.go — auto-skill extraction
	Tracer         *trace.Tracer           // trace.go — distributed tracing spans
	// contains filtered or unexported fields
}

Session manages a conversation with an LLM via eyrie.

func NewSession

func NewSession(provider, model, systemPrompt string, registry *tool.Registry) *Session

NewSession creates a new conversation session.

func (*Session) AddAssistant

func (s *Session) AddAssistant(content string)

func (*Session) AddUser

func (s *Session) AddUser(content string)

func (*Session) AppendSystemContext

func (s *Session) AppendSystemContext(content string)

AppendSystemContext adds runtime context, such as /add-dir, to future model calls.

func (*Session) AutoCompactIfNeeded

func (s *Session) AutoCompactIfNeeded() bool

AutoCompactIfNeeded runs compaction when the conversation exceeds the threshold.

func (*Session) Compact

func (s *Session) Compact()

Compact reduces conversation history (boundary-aware truncation).

func (*Session) ConvoHead

func (s *Session) ConvoHead() string

ConvoHead returns the current conversation head node ID.

func (*Session) ForkConversation

func (s *Session) ForkConversation(nodeID string) (string, error)

ForkConversation creates a new branch from a specific point in history. Returns the fork node ID and the messages up to that point.

func (*Session) ListBranches

func (s *Session) ListBranches(nodeID string) ([]*convodag.Node, error)

ListBranches returns child nodes (alternative branches) from a given node.

func (*Session) LoadMessages

func (s *Session) LoadMessages(msgs []client.EyrieMessage)

func (*Session) MessageCount

func (s *Session) MessageCount() int

func (*Session) Metrics

func (s *Session) Metrics() *metrics.Registry

func (*Session) Model

func (s *Session) Model() string

func (*Session) Provider

func (s *Session) Provider() string

func (*Session) RawMessages

func (s *Session) RawMessages() []client.EyrieMessage

RawMessages returns the conversation messages for persistence.

func (*Session) RemoveLastExchange

func (s *Session) RemoveLastExchange()

RemoveLastExchange removes the last user+assistant message pair.

func (*Session) SetAPIKey

func (s *Session) SetAPIKey(provider, apiKey string)

SetAPIKey updates a provider API key for subsequent requests.

func (*Session) SetAPIKeys

func (s *Session) SetAPIKeys(apiKeys map[string]string)

SetAPIKeys updates all known provider API keys for subsequent requests.

func (*Session) SetAllowedDirs

func (s *Session) SetAllowedDirs(dirs []string)

SetAllowedDirs sets directories that file tools are allowed to access.

func (*Session) SetLogger

func (s *Session) SetLogger(l *logger.Logger)

SetLogger replaces the session logger.

func (*Session) SetMaxBudgetUSD

func (s *Session) SetMaxBudgetUSD(amount float64) error

SetMaxBudgetUSD caps estimated API spend for this session.

func (*Session) SetMaxTurns

func (s *Session) SetMaxTurns(turns int) error

SetMaxTurns caps the number of model turns in the agent loop.

func (*Session) SetModel

func (s *Session) SetModel(model string)

SetModel updates the active model for subsequent requests.

func (*Session) SetPermissionMode

func (s *Session) SetPermissionMode(mode string) error

SetPermissionMode applies an archive-compatible permission mode.

func (*Session) SetProvider

func (s *Session) SetProvider(provider string)

SetProvider updates the active provider for subsequent requests.

func (*Session) ShouldAutoCompact

func (s *Session) ShouldAutoCompact() bool

ShouldAutoCompact returns true if the conversation is approaching context limits.

func (*Session) SmartCompact

func (s *Session) SmartCompact()

SmartCompact reduces conversation history using LLM-generated summaries.

func (*Session) Stream

func (s *Session) Stream(ctx context.Context) (<-chan StreamEvent, error)

Stream runs the agentic loop: LLM → tool_use → execute → loop.

func (*Session) SwitchBranch

func (s *Session) SwitchBranch(nodeID string) error

SwitchBranch navigates to a different branch point and rebuilds messages.

func (*Session) WireAgentTool

func (s *Session) WireAgentTool()

WireAgentTool sets up sub-agent spawning with two modes:

  • explore: fast/cheap model, read-only tools, higher turn budget
  • general: full model, all tools, standard budget

type SessionLifecycle

type SessionLifecycle struct {
	Memory      EvolvingMemoryInterface
	SkillStore  SkillStoreInterface
	CostTracker CostTrackerInterface
}

SessionLifecycle manages the start and end of agent sessions, implementing the self-improvement loop that makes hawk better over time.

Research basis: - Reflexion (NeurIPS 2023): 91% HumanEval via episodic memory of reflections - ExpeL (AAAI 2024): extract insights from task experiences - Voyager: 15.3x faster with accumulated skill library - DSPy (Stanford): 25-65% improvement via curated few-shot examples

The closed loop: SESSION START:

  1. Retrieve relevant EvolvingMemory guidelines
  2. Inject few-shot examples from prior successes
  3. Load yaad context (conventions, active tasks, stale warnings)

SESSION END:

  1. Generate LLM reflection ("what worked, what failed, why")
  2. Extract guidelines via EvolvingMemory.Learn()
  3. Distill successful approaches into skills
  4. Record cost/performance metrics
  5. Trigger yaad consolidation

func (*SessionLifecycle) OnSessionEnd

func (l *SessionLifecycle) OnSessionEnd(_ context.Context, session *Session, outcome SessionOutcome) error

OnSessionEnd performs post-session learning.

func (*SessionLifecycle) OnSessionStart

func (l *SessionLifecycle) OnSessionStart(_ context.Context, initialPrompt string) string

OnSessionStart prepares context for a new session. Returns context to inject into the system prompt.

type SessionMemoryConfig

type SessionMemoryConfig struct {
	MinTokens            int
	MinTextBlockMessages int
	MaxTokens            int
}

SessionMemoryConfig controls session memory compaction thresholds.

func DefaultSessionMemoryConfig

func DefaultSessionMemoryConfig() SessionMemoryConfig

DefaultSessionMemoryConfig returns defaults matching the archive.

type SessionMemoryStrategy

type SessionMemoryStrategy struct{}

SessionMemoryStrategy uses the session memory file as a compaction summary instead of making an LLM call.

func (*SessionMemoryStrategy) Compact

func (s *SessionMemoryStrategy) Compact(ctx context.Context, sess *Session) (*CompactResult, error)

func (*SessionMemoryStrategy) Name

func (s *SessionMemoryStrategy) Name() string

func (*SessionMemoryStrategy) ShouldTrigger

func (s *SessionMemoryStrategy) ShouldTrigger(msgs []client.EyrieMessage, tokenCount, threshold int) bool

type SessionOutcome

type SessionOutcome struct {
	Success      bool
	TaskGoal     string
	FilesChanged []string
	ToolsUsed    []string
	TotalCost    float64
	Duration     time.Duration
	UserFeedback string // empty if none
}

SessionOutcome captures the results of a completed session.

type ShadowWorkspace

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

ShadowWorkspace provides a temporary directory where file edits can be validated (e.g. via `go vet`, `tsc`, `pylint`) without touching the original source tree.

func NewShadowWorkspace

func NewShadowWorkspace() (*ShadowWorkspace, error)

NewShadowWorkspace creates a new temporary directory for shadow validation.

func (*ShadowWorkspace) Close

func (sw *ShadowWorkspace) Close()

Close removes the shadow workspace temp directory and all its contents.

func (*ShadowWorkspace) TempDir

func (sw *ShadowWorkspace) TempDir() string

TempDir returns the path to the shadow workspace temp directory.

func (*ShadowWorkspace) ValidateEdit

func (sw *ShadowWorkspace) ValidateEdit(originalPath, newContent string) []ValidationError

ValidateEdit copies a file into the shadow workspace, writes newContent to the copy, runs the language-appropriate validation tool, and returns any errors found. The temp copy is cleaned up before returning.

func (*ShadowWorkspace) ValidateMultipleEdits

func (sw *ShadowWorkspace) ValidateMultipleEdits(edits map[string]string) map[string][]ValidationError

ValidateMultipleEdits validates several files at once and returns a map of file path to validation errors.

type SkillDistillerAdapter

type SkillDistillerAdapter struct {
	SD *memory.SkillDistiller
}

SkillDistillerAdapter bridges memory.SkillDistiller to SkillStoreInterface. Skill distillation builds a prompt for LLM extraction — the actual distilled skills are stored as files in hawk-skills/.

func (*SkillDistillerAdapter) Distill

func (a *SkillDistillerAdapter) Distill(goal string, steps []string, outcome string) error

func (*SkillDistillerAdapter) Retrieve

func (a *SkillDistillerAdapter) Retrieve(_ string) []string

type SkillStoreInterface

type SkillStoreInterface interface {
	Distill(goal string, steps []string, outcome string) error
	Retrieve(query string) []string
}

SkillStoreInterface abstracts skill distillation and retrieval.

type SmartCompactStrategy

type SmartCompactStrategy struct{}

SmartCompactStrategy uses LLM to generate a conversation summary.

func (*SmartCompactStrategy) Compact

func (s *SmartCompactStrategy) Compact(ctx context.Context, sess *Session) (*CompactResult, error)

func (*SmartCompactStrategy) Name

func (s *SmartCompactStrategy) Name() string

func (*SmartCompactStrategy) ShouldTrigger

func (s *SmartCompactStrategy) ShouldTrigger(msgs []client.EyrieMessage, tokenCount, threshold int) bool

type SnapshotCache

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

SnapshotCache is a TTL-based cache for project snapshots.

func NewSnapshotCache

func NewSnapshotCache(ttl time.Duration) *SnapshotCache

NewSnapshotCache creates a new SnapshotCache with the given TTL. If ttl is zero, DefaultSnapshotTTL is used.

func (*SnapshotCache) Get

func (sc *SnapshotCache) Get(key string) (string, bool)

Get returns the cached value for the key if it exists and has not expired.

func (*SnapshotCache) GetOrCompute

func (sc *SnapshotCache) GetOrCompute(key string, fn func() (string, error)) (string, error)

GetOrCompute returns the cached value for the key, or computes and caches it using the provided function if the value is missing or expired.

func (*SnapshotCache) Set

func (sc *SnapshotCache) Set(key, value string)

Set stores a value in the cache with the configured TTL.

type SnapshotTracker

type SnapshotTracker interface {
	Track(message string) (string, error)
}

SnapshotTracker abstracts the snapshot system so engine doesn't import snapshot directly.

type SnowballDetector

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

SnowballDetector detects when token consumption is growing faster than progress, signalling that the agent is stuck in a snowball pattern where each turn consumes more tokens without proportional progress.

func NewSnowballDetector

func NewSnowballDetector(maxTokens int) *SnowballDetector

NewSnowballDetector creates a detector with the given absolute token ceiling. The default growth threshold is 2.0x (last 3 turns vs first 3 turns).

func (*SnowballDetector) IsSnowballing

func (sd *SnowballDetector) IsSnowballing() bool

IsSnowballing returns true if the last 3 turns consumed 2x+ tokens compared to the first 3 turns AND progress per token is declining.

func (*SnowballDetector) RecordTurn

func (sd *SnowballDetector) RecordTurn(tokens int, progress float64)

RecordTurn records token usage and estimated progress for a single turn. Progress should be in the range [0, 1].

func (*SnowballDetector) Reset

func (sd *SnowballDetector) Reset()

Reset clears all recorded data.

func (*SnowballDetector) ShouldAbort

func (sd *SnowballDetector) ShouldAbort() bool

ShouldAbort returns true if total tokens exceed maxTokens or the growth rate exceeds 3x between the first and last 3-turn windows.

func (*SnowballDetector) String

func (sd *SnowballDetector) String() string

String implements the Stringer interface for SnowballDetector.

func (*SnowballDetector) Summary

func (sd *SnowballDetector) Summary() string

Summary returns a human-readable summary of the snowball state.

type StrategyRegistry

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

StrategyRegistry manages compaction strategies in priority order.

func NewStrategyRegistry

func NewStrategyRegistry(config CompactConfig) *StrategyRegistry

NewStrategyRegistry creates a registry with default strategies.

func (*StrategyRegistry) SelectStrategy

func (r *StrategyRegistry) SelectStrategy(msgs []client.EyrieMessage, tokenCount int) CompactStrategy

SelectStrategy picks the highest-priority strategy whose trigger fires.

type StreamEvent

type StreamEvent struct {
	Type     string // content, thinking, tool_use, tool_result, usage, done, error
	Content  string
	ToolName string
	ToolID   string
	Usage    *StreamUsage // usage data for this event
}

StreamEvent is sent from the engine to the TUI.

type StreamUsage

type StreamUsage struct {
	PromptTokens     int `json:"prompt_tokens"`
	CompletionTokens int `json:"completion_tokens"`
	CacheReadTokens  int `json:"cache_read_tokens,omitempty"`
	CacheWriteTokens int `json:"cache_write_tokens,omitempty"`
}

StreamUsage tracks token usage for a single stream event.

type SubAgentMode

type SubAgentMode string

SubAgentMode determines the capabilities and cost profile of a sub-agent.

const (
	SubAgentExplore SubAgentMode = "explore"
	SubAgentGeneral SubAgentMode = "general"
)

type Subtask

type Subtask struct {
	ID          int
	Title       string
	Description string
	Files       []string
	Status      string // "pending", "in_progress", "done", "skipped"
}

Subtask represents a single unit of work within a plan.

func ParseSubtasks

func ParseSubtasks(output string) []Subtask

ParseSubtasks parses LLM output formatted as numbered subtasks. Expected format:

  1. Title here Description of what to do Files: path/to/file.go, another/file.go

type TeachConfig

type TeachConfig struct {
	Enabled bool
	Depth   int // 1=what, 2=why, 3=how (default: 2)
}

TeachConfig controls explanation depth.

type TimeoutConfig

type TimeoutConfig struct {
	Total     time.Duration // total time for the entire operation
	PerTurn   time.Duration // max time per LLM turn (default: 60s)
	PerTool   time.Duration // max time per tool execution (default: 120s)
	Countdown bool          // show remaining time in output
}

TimeoutConfig controls operation time budgets.

func DefaultTimeoutConfig

func DefaultTimeoutConfig() TimeoutConfig

DefaultTimeoutConfig returns a TimeoutConfig with sensible per-turn and per-tool defaults. Total is left at zero (no overall deadline) so the caller can set it.

type TrajectoryDistiller

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

TrajectoryDistiller runs a task multiple times, summarizing what worked, and retrying with distilled knowledge from previous attempts.

func NewTrajectoryDistiller

func NewTrajectoryDistiller(session *Session, maxRuns int) *TrajectoryDistiller

NewTrajectoryDistiller creates a new distiller wrapping the given session.

func (*TrajectoryDistiller) BestRun

func (td *TrajectoryDistiller) BestRun(runs []TrajectoryRun) *TrajectoryRun

BestRun returns the run with Success=true (preferring earlier), or the highest-quality failure (most messages, fewest errors).

func (*TrajectoryDistiller) RunWithDistillation

func (td *TrajectoryDistiller) RunWithDistillation(ctx context.Context, prompt string) (string, error)

RunWithDistillation executes the prompt, and if it fails, retries with accumulated trajectory summaries from prior attempts. Returns the best result.

type TrajectoryRun

type TrajectoryRun struct {
	ID       int
	Messages []client.EyrieMessage
	Success  bool
	Summary  string // distilled lessons from this run
	Tokens   int
}

TrajectoryRun records a single attempt at completing a task.

type TransferLearning

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

TransferLearning enables cross-session knowledge transfer. Extracts generalizable patterns from completed sessions and applies them to new sessions on similar codebases.

func NewTransferLearning

func NewTransferLearning() *TransferLearning

NewTransferLearning creates a store backed by ~/.hawk/transfer.json.

func (*TransferLearning) Apply

func (tl *TransferLearning) Apply(language, taskDescription string) []TransferPattern

Apply finds patterns relevant to the current task.

func (*TransferLearning) FormatForPrompt

func (tl *TransferLearning) FormatForPrompt(language, task string) string

FormatForPrompt returns transfer patterns as system prompt context.

func (*TransferLearning) Learn

func (tl *TransferLearning) Learn(language, category, pattern, approach string)

Learn extracts a transferable pattern from a successful session.

type TransferPattern

type TransferPattern struct {
	Language    string    `json:"language"`   // go, python, typescript, etc.
	Category    string    `json:"category"`   // "fix", "refactor", "feature", "test"
	Pattern     string    `json:"pattern"`    // generalized description
	Approach    string    `json:"approach"`   // what worked
	Confidence  float64   `json:"confidence"` // based on success rate
	UsedCount   int       `json:"used_count"`
	SuccessRate float64   `json:"success_rate"`
	CreatedAt   time.Time `json:"created_at"`
}

TransferPattern is a reusable pattern extracted from a successful session.

type TruncateStrategy

type TruncateStrategy struct{}

TruncateStrategy is the fallback that does boundary-aware truncation.

func (*TruncateStrategy) Compact

func (s *TruncateStrategy) Compact(ctx context.Context, sess *Session) (*CompactResult, error)

func (*TruncateStrategy) Name

func (s *TruncateStrategy) Name() string

func (*TruncateStrategy) ShouldTrigger

func (s *TruncateStrategy) ShouldTrigger(_ []client.EyrieMessage, tokenCount, threshold int) bool

type ValidationError

type ValidationError struct {
	File    string
	Line    int
	Column  int
	Message string
}

ValidationError represents a single validation issue.

type ValidationResult

type ValidationResult struct {
	Valid  bool
	Errors []ValidationError
}

ValidationResult holds the outcome of validating a file.

func ValidateFile

func ValidateFile(path string) *ValidationResult

ValidateFile determines the language from the file extension and runs the appropriate syntax checker.

Jump to

Keyboard shortcuts

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