agent

package
v0.17.4 Latest Latest
Warning

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

Go to latest
Published: Jul 15, 2026 License: MIT Imports: 76 Imported by: 0

Documentation

Overview

Auto-skip learning for ChangeTracker shell-mutation tracking.

After the first walk of a fat directory (one whose file count exceeds autoSkipFileCountThreshold or autoSkipCumulativeThreshold), the dir is added to autoSkipDirs so subsequent walks skip it entirely. Learned sets persist across agent sessions via change_tracking_shell_persist.go.

Mutation recording and bulk rollup for ChangeTracker shell-mutation tracking.

Takes the diff from before/after snapshots and records TrackedFileChange entries, collapsing high-churn directories into bulk rollups when appropriate.

Shell-mutation tracking for ChangeTracker.

The base ChangeTracker (change_tracking.go) only captures writes the agent performs via the structured file tools (write_file, edit_file, patch_structured_file, write_structured_file). Plenty of legitimate agent actions mutate files outside those tools — `sed -i`, `mv`, `rm`, `cp`, `tee`, `awk -i inplace`, build scripts, formatters, etc. — and none of them currently appear in the manifest the subagent returns to its primary.

This file adds a "before/after" snapshot pass around every shell_command invocation:

  1. Before the shell runs, walk the workspace tree and capture file bytes for everything inside size/binary limits, skipping well-known bloat directories (.git, node_modules, dist, …). Works whether or not the workspace is a git repo — no git dependency, no reliance on git's tracked/untracked classification.
  2. Run the shell command.
  3. Walk again afterwards. Diff against the "before" map. Each deletion, modification, or creation that isn't already in the tracker becomes a new TrackedFileChange with the captured original content (when available — preserved so a user can recover an accidentally-deleted file from the session buffer, git-tracked or not).

Size + binary filters keep this cheap and safe: 1 MiB ceiling per file (so we don't buffer node_modules-style giants), plus a null-byte sniff in the first 8 KiB so binaries aren't stored as text. A per-snapshot total-bytes budget caps memory.

This file is the primary entry point containing the main orchestration methods. Supporting code is split across:

  • change_tracking_snapshot.go — walk, file I/O, binary detection
  • change_tracking_mutations.go — mutation recording and bulk rollup
  • change_tracking_autoskip.go — adaptive auto-skip learning
  • change_tracking_shell_persist.go — cross-session persistence

Persistence for the ChangeTracker's adaptive auto-skip set.

The walker learns "fat directories" on first visit (those with more immediate child files than autoSkipFileCountThreshold) and skips them on subsequent walks within the same session. Without persistence, every new agent session re-learns from scratch — paying the first-walk cost over and over for the same dirs.

This file persists the learned set to `~/.config/sprout/shell_skip_dirs.json`, keyed by absolute workspace root, so subsequent sessions in the same workspace inherit the learning. The file is best-effort: read failures fall back to an empty set (re-learn), write failures log a warning.

To prevent unbounded growth across many workspaces, the file caps at maxPersistedWorkspaces entries — least-recently-used workspaces are evicted first when the cap is exceeded.

Snapshot walking and file I/O for ChangeTracker shell-mutation tracking.

Walks the workspace tree before and after shell commands, capturing file bytes inside size/binary limits, then diffing the two snapshots.

Package agent provides error definitions for the agent package.

Consolidated memory tool.

One handler that dispatches on `operation` to the existing per-op helpers — replaces add_memory / read_memory / list_memories / delete_memory / search_memories so the LLM only sees one entry for memory management.

Package agent provides typed error classification for tool error retry decisions.

The retry package defines a classification system that uses the typed AgentError system from pkg/errors to determine appropriate retry behavior instead of relying on string matching against error messages.

Classification is driven by the error category:

  • TransientError → Retry (backoff)
  • RateLimitError → Retry (longer backoff)
  • SecurityError → Escalate (ask user/LLM)
  • InvalidInputError → Fail (input must be fixed)
  • ContextError → Fail (context overflow, needs compaction)
  • PermanentError → Fail (non-recoverable)
  • ProviderError → Fail (auth/config) or Retry (server errors) depending on Retryable field
  • Unknown errors → Retry once then Fail

Security circuit breaker + audit logging for the live seed tool path.

This file wires two previously-dormant features into the live pre-execute hook (newPreExecuteHook in seed_tool_registry.go):

  1. A security-specific circuit breaker that escalates the caution message when the LLM retries the exact same blocked operation. It lives in the existing CircuitBreakerState.Actions map under a "sec:" key namespace so it cannot collide with the (dormant) general circuit breaker.

  2. Audit logging of unified-gate security decisions (blocked / prompted / approved / loop_detected) through the Agent-owned AuditLogger. This complements the package-level auditLogger already invoked from ClassifyToolCall in pkg/agent_tools.

All helpers are nil-safe on the agent / state / logger so bare *Agent values in unit tests don't panic.

Package agent — shell command parsing and classification.

This file provides pure-function utilities for splitting shell commands into logical parts and classifying each part by destructive intent. It contains no Agent wiring, no broker, no events, and no UI.

Destructive shell command classifier.

Peer to shellLooksReadOnly. Identifies shell commands that are likely to clobber the user's active changes — either by reverting working-tree edits (`git checkout .`, `git reset --hard`, `git restore .`) or by deleting untracked work (`git clean -fd`, `git stash drop`, etc.).

When the change tracker sees a destructive command, it pivots to a safer (slower) mode:

  • The adaptive autoSkipDirs set is IGNORED for the walk (and the walk doesn't add to it during a destructive run). A directory that was learned as "fat" during a build might contain edits the user wants back after a `git checkout .` — we'd rather pay the walk cost than silently drop the recovery payload.
  • The bulk-rollup branch in RecordShellMutations is BYPASSED so every mutation lands as a per-file entry with full OriginalCode for recovery. A 300-file `git checkout .` produces 300 recoverable rows, not one opaque "src/ — 300 files" row.
  • Truncation (50k file / 500ms / 32 MiB caps) gets promoted from a log line to a user-visible manifest entry so partial coverage during a destructive op is impossible to miss.

Bias: CONSERVATIVE in the opposite direction from shellLooksReadOnly. False positive ("said destructive when it wasn't") means we run a fuller walk and emit per-file for a normal command — cheap. False negative ("missed a destructive op") means we might silently drop a recoverable change — expensive. So unrecognised flags or subcommands on a known-destructive program err toward "destructive".

Read-only shell command classifier.

Used to short-circuit the shell-snapshot pass for commands that provably can't mutate the filesystem. Skipping the snapshot avoids the ~10 ms warm-walk cost (and the full prime cost when uncached) on every `ls`, `grep`, `cat`, `git status`, etc. — by far the most common shell_command invocations.

Bias: the classifier is CONSERVATIVE. False positive ("said read-only when it wasn't") means we miss tracking the mutation — bad. False negative ("said write when it was read-only") means we pay the snapshot cost we didn't need — cheap. So any unknown program, chaining operator, redirect, subshell, or known-dangerous flag forces the snapshot.

Package agent provides subagent management via the SubagentRunner, which supports both serial (Run) and parallel (RunParallel) execution of subagent tasks.

SubagentRunner Concurrency Invariants:

  • MaxConcurrentSubagents: When > 0, a buffered channel semaphore limits the number of concurrently executing subagents. Tasks waiting for a slot respect parent context cancellation and return Cancelled=true.

  • FleetTokenBudget: When > 0, tracks cumulative token usage across the fleet via atomic.Int64. Once the budget is reached, not-yet-started tasks are skipped with BudgetExceeded=true. Currently running tasks are NOT interrupted.

  • Order Preservation: RunParallel returns results in the same order as the input tasks, regardless of execution order.

Consolidated task queue tool.

One handler that dispatches on `operation` to the existing per-op helpers — replaces task_queue_read / task_queue_publish / task_queue_add so the LLM only sees one entry for queue management.

Tool call formatting: display-friendly representations of tool calls for logging, progress output, and CLI status reporting.

Tool executor: core struct, constructor, and orchestration entry point.

Companions (all in this package):

Execution:    tool_executor_sequential.go, tool_executor_parallel.go
Config:       tool_executor_config.go
Context:      tool_execution_context.go, tool_executor_helpers.go
Safety:       tool_executor_circuit_breaker.go
Observability: tool_executor_trace.go
Formatting:   tool_call_format.go
Constraint:   tool_result_constraint.go
Todo events:  tool_executor_todo_events.go
JSON repair:  tool_json_repair.go

Circuit breaker: prevents infinite tool-execution loops by tracking repeated identical actions within a sliding time window.

Tool executor configuration: timeout defaults and constants.

Tool executor helpers: small utility functions that support the tool execution lifecycle (MCP delegation, stop conditions, ID generation, numeric normalization).

Tool executor: parallel batch execution for safe, independent tools.

Tool executor: sequential and single tool call execution.

Todo event publishing: detects changes in todo checklists after TodoWrite tool calls and publishes structured update events.

Trace recording: captures tool execution data into the trace session for observability, replay, and post-hoc analysis.

Agent-facing tools backed by the ChangeTracker's session buffer.

After the SP-061-2 consolidation this file ships only two tools — the rest were folded into options on these:

  • list_changes Manifest of the session's changes, with three optional knobs: include_diff: bool per-file unified diff (was show_my_change) group_by: "block"|"" activity-block summary (was summarize_my_session) include_persisted: bool merge hot+warm history (was my_recent_changes) Plus the existing filters: since, tool, path_pattern.

  • revert_my_changes Bulk undo by scope ("all" or "since"). The previous file= scope was removed because recover_file(scope="session_start") does the same thing with clearer semantics.

Recovery of an individual file (or bulk entry, or session-start state) lives in tool_handlers_recover.go.

recover_file tool: restores a file's tracked content from the ChangeTracker's session buffer. Closes the loop between "we captured original bytes" and "user/agent can put them back".

The SP-061-2 consolidation rolled three behaviours into one tool via the `scope` argument:

  • scope="latest" (default) Restore the file to the state immediately before its most-recent tracked change. The historical recover_file shape.

  • scope="session_start" Restore to the EARLIEST captured original — the file as it was before the agent touched it at all this session. Replaces the revert_my_changes(file=…) scope.

  • scope="bulk" Treat `path` as a bulk entry's FilePath (a command label like "git checkout ." or a dir like "webui/src/"). Walks the entry's BulkItems and restores every packed file. Replaces the standalone recover_bulk tool.

Selection rules:

  • Most-recent matching change for `path` wins for scope="latest" (the tracker records changes in append order).
  • Earliest matching change wins for scope="session_start".
  • The change must have a recoverable OriginalCode (non-empty, not the redacted sentinel, not the path-only sentinel).
  • For "create" entries (no original existed), recovery is a delete: removing a created file restores the workspace to pre-creation state.

Safety:

  • Refuses paths outside the workspace root (no cross-workspace restores).
  • Refuses when the file would resolve to a directory or symlink target.
  • Returns a structured JSON result so the LLM can reason about success vs. why-it-couldn't.

Package agent provides the shell command handler with a unified security model.

When UnifiedRiskResolver is ON (the default, set by config_migration.go), a single ResolveToolRisk assessment gates every shell command. The unified gate (unifiedSecurityGate in tool_security.go) runs once per tool call — no Gate 1/Gate 2 bridge or suppression plumbing is needed.

When the flag is OFF (legacy fallback), the older dual-gate model applies: Gate 1 (ClassifyToolCall static classifier) + Gate 2 (EvaluateOperationRisk persona cascade). Note that the legacy path may double-prompt because the suppression bridge was removed in SP-068 Phase 3; the unified resolver is the recommended and default path.

Subagent tool handlers: constants, types, globals, and shared declarations.

Implementation details are split across:

  • tool_handlers_subagent_events.go — event batching and publishing
  • tool_handlers_subagent_result.go — typed result envelope builders
  • tool_handlers_subagent_spawn.go — spawn / dispatch logic

Subagent spawn and dispatch logic.

Package agent provides the core agent functionality including tool registry and handlers.

Tool functionality is organized across:

  • tool_definitions.go: Tool configuration structs and registry initialization
  • tool_handlers.go: Tool handler implementations

Tool result constraint: truncation and compaction of tool results before they are sent to the model context window.

Package agent provides the in-process workflow runner for TODO-loop workflows. It eliminates subprocess spawning (the BPM/exec.Command path that requires nohup and breaks across OS/process-group boundaries) by running the workflow loop in-process as a goroutine with a fresh Agent.

Index

Constants

View Source
const (
	PruneStrategyNone          = core.PruneStrategyNone
	PruneStrategySlidingWindow = core.PruneStrategySlidingWindow
	PruneStrategyImportance    = core.PruneStrategyImportance
	PruneStrategyHybrid        = core.PruneStrategyHybrid
	PruneStrategyAdaptive      = core.PruneStrategyAdaptive
)

Pruning strategy constants — re-exported from seed for backward compatibility with sprout call sites that reference them unqualified.

View Source
const (
	BillingPayPerToken  = providers.BillingPayPerToken
	BillingSubscription = providers.BillingSubscription
	BillingFree         = providers.BillingFree
)

Billing type constants (re-exported for convenience within the agent package).

View Source
const (
	DefaultMinMemoryBytes = 8 * 1024 * 1024 * 1024  // 8 GB
	DefaultRetryMinBytes  = 16 * 1024 * 1024 * 1024 // 16 GB
	DefaultRetrySleep     = 30 * time.Second
	DefaultMaxRetries     = 5
)

Default thresholds.

View Source
const (
	RunTerminationCompleted           = "completed"
	RunTerminationMaxIterations       = "max_iterations"
	RunTerminationInterrupted         = "interrupted"
	RunTerminationFleetBudgetExceeded = "fleet_budget_exceeded"
)
View Source
const (
	MAX_SUBAGENT_OUTPUT_SIZE  = 10 * 1024 * 1024 // 10MB
	MAX_SUBAGENT_CONTEXT_SIZE = 1024 * 1024      // 1MB
	// Lines to batch before publishing a subagent "output" event. Kept small
	// so output streams to the WebUI in near-real-time — subagent output is
	// line-level (LLM-paced), not char-level, so this won't flood the event
	// bus, while still coalescing bursty tool dumps. (Was 50, which made most
	// subagent runs show nothing until they finished.)
	BATCH_SIZE                 = 8
	DefaultSubagentTokenBudget = 2_000_000 // Default token budget for subagents
)
View Source
const CompactedFilesHeader = "Files modified during compacted segment:"

CompactedFilesHeader marks the file-change manifest block that `/compact` appends to its LLM-generated summary. Future compactions re-parse this block to keep the running file-change history visible across the summary boundary — without this, every `/compact` would lose the manifest of files touched in the summarized turns.

View Source
const DefaultClarificationTimeout = 60 * time.Second

DefaultClarificationTimeout is the default timeout for clarification requests.

View Source
const DefaultDriftCheckInterval = 5

DefaultDriftCheckInterval is the default number of turns between drift checks.

View Source
const DefaultDriftThreshold = 0.60

DefaultDriftThreshold is the default cosine similarity threshold below which a conversation is considered to have drifted from its original intent.

View Source
const MaxDriftRejections = 3

MaxDriftRejections is the number of CONSECUTIVE rejections after which drift detection is suppressed for the remainder of the session.

View Source
const RedactedContentMarker = history.RedactedContentMarker

RedactedContentMarker aliases history.RedactedContentMarker so existing call sites within this package keep working. The canonical constant lives in pkg/history (the lower-level package, which pkg/agent already imports) so the two packages can never drift apart.

View Source
const SkillFileName = skills.SkillFileName

SkillFileName is the conventional name of the markdown file inside each skill directory. Re-exported from pkg/skills so callers in this package don't need to learn two import paths for the same constant.

View Source
const TranscriptSnapshotFormat = "sprout-transcript/v1"

TranscriptSnapshotFormat identifies snapshot file shape. Bump when breaking shape changes land so older readers don't silently misparse.

Variables

View Source
var (
	ErrUINotAvailable = errors.New("UI not available")
	ErrCancelled      = errors.New("user cancelled")
)

UI errors

View Source
var (
	ErrWriteStale            = errors.New("write refused: file may be stale")
	ErrWriteHasUnsyncedEdits = errors.New("write refused: user has unsynced edits to this file")
)

ErrWriteStale is the sentinel returned by checkWriteStaleness for the "no recent read" / "modified after read" cases. The agent's correct response is to read_file(path) and retry.

ErrWriteHasUnsyncedEdits is the sentinel for the "browser has edits the container hasn't seen yet" case. The agent must NOT auto-retry; instead it should ask the user whether to overwrite. The platform's WS sync layer populates the WorkspaceFileMetadata that drives this.

Both are deliberately wrappable via errors.Is so callers (including the tool-result formatter) can distinguish them without string-matching.

View Source
var ErrModelNotAvailable = errors.New("configured model is not available for this provider")

ErrModelNotAvailable is returned when the configured model for the current provider is not available. In daemon mode, this allows the web UI to detect the issue and present a model selection UI rather than hard-failing.

View Source
var ErrProviderNotConfigured = errors.New("provider is not configured — configure via webui settings")

ErrProviderNotConfigured is returned when the provider cannot be initialized (unrecognized provider, missing API key, etc.) in daemon mode. This allows the web UI to start without an agent and present a provider configuration UI instead of crashing the daemon.

View Source
var ErrQueryInProgress = errors.New("a query is already in progress on this agent")

ErrQueryInProgress is returned when ProcessQuery is called while another query is already running on the same Agent instance. This happens when two frontends (CLI REPL and WebUI) share the same Agent — only one query can execute at a time to prevent message-list and state corruption.

View Source
var FleetBudgetExceededError = errors.New("fleet token budget exceeded")

FleetBudgetExceededError is returned by the seed provider when the shared fleet token budget has been exceeded mid-conversation. It is caught by processQueryWithSeed to truncate gracefully rather than surfacing as a generic API error.

View Source
var MILESTONE_PHASES = []string{"spawn", "complete", "step"}

MILESTONE_PHASES defines phases that trigger immediate publish without batching

View Source
var PruningConfig = struct {
	Default struct {
		StandardPercent float64
		MinMessages     int
		RecentMessages  int
		SlidingWindow   int
	}
	Structural struct {
		RecentMessagesToKeep int
		MinMessagesToCompact int
		MinMiddleMessages    int
	}
	AgenticRequiredAvailableTokens int
}{
	Default: struct {
		StandardPercent float64
		MinMessages     int
		RecentMessages  int
		SlidingWindow   int
	}{
		StandardPercent: 0.87,
		MinMessages:     5,
		RecentMessages:  24,
		SlidingWindow:   30,
	},
	Structural: struct {
		RecentMessagesToKeep int
		MinMessagesToCompact int
		MinMiddleMessages    int
	}{
		RecentMessagesToKeep: core.StructuralRecentToKeep,
		MinMessagesToCompact: core.StructuralMinMessagesToCompact,
		MinMiddleMessages:    core.StructuralMinMiddleMessages,
	},
	AgenticRequiredAvailableTokens: 12000,
}

PruningConfig preserves the historical "single source of truth" symbol some sprout tests reference. Values come from seed's defaults so any drift between sprout and seed is impossible by construction.

New code should not read from this — query the pruner instance directly or use seed's exported constants. Retained as a thin shim only.

View Source
var UseMockLLM bool

UseMockLLM, when true, causes the agent creation path to return a MockLLMProvider instead of the real provider. Set from the --mock-llm CLI flag.

Functions

func ApplyHunks added in v0.16.12

func ApplyHunks(original string, hunks []Hunk, acceptedIDs []string) string

ApplyHunks reconstructs file content by applying only the accepted hunks. Rejected hunks leave the original lines unchanged. Hunks are applied in order; each hunk locates its context in the current result and patches it.

func AssertNoStateLeak added in v0.16.6

func AssertNoStateLeak(realDir string, before map[string]time.Time) int

AssertNoStateLeak is the TestMain counterpart of the Layer-5 check in NewTestStateDir(t). Compares the current file set under realDir against the snapshot from SnapshotRealStateDir; if any new file appeared, it writes a noisy stderr warning AND returns a non-zero suggested exit code so TestMain can fail the run.

Why warning + exit-code instead of t.Errorf: TestMain has no *testing.T to attach an error to. We could panic, but tests that raced through to completion would already be marked PASS by `go test`; a panic in TestMain then prints a misleading "test passed but cleanup failed" message. Returning a code lets the caller `os.Exit(testCode | leakCode)` so CI fails on real leaks while preserving the underlying test-failure signal.

Returns 0 when nothing leaked, 1 when something did.

Detection model: only flag files whose mtime is *newer than the snapshot start time*. Pre-existing files in the developer's real state dir (e.g. sessions from prior CLI runs) have mtimes from before TestMain started; if their content is re-read in-place the read access doesn't update mtime, so they don't trigger a false positive. Only files that were created or rewritten during this test run are reported.

func BuildScopedSessionPathForTesting

func BuildScopedSessionPathForTesting(stateDir, sessionID, workingDir string) (string, error)

BuildScopedSessionPathForTesting constructs the scoped session file path for test setup.

func BuildToolDefinitions added in v0.16.4

func BuildToolDefinitions() []api.Tool

BuildToolDefinitions converts all handler-based tool definitions into the []api.Tool shape the LLM, persona allowlist, and MCP-merge code paths expect.

mcp_tools is added as a synthetic entry because it is a meta-tool handled outside the registry (see pkg/agent/mcp.go::handleMCPToolsCommand and pkg/agent/tools.go's mcp_tools dispatch). Removing it would hide MCP discovery from the model.

func CleanupPasswordRequestForTest added in v0.16.18

func CleanupPasswordRequestForTest(requestID string)

CleanupPasswordRequestForTest removes a password request from the broker.

func ContextWithSproutDir added in v0.16.25

func ContextWithSproutDir(ctx context.Context, dir string) context.Context

ContextWithSproutDir returns a context that carries the sprout directory.

func DecrementActiveSubagents

func DecrementActiveSubagents()

DecrementActiveSubagents lowers the active-subagent counter when a subagent finishes (success, error, cancel — any terminal state).

func DeleteMemory

func DeleteMemory(name string) error

DeleteMemory deletes a memory file by name (with .md extension)

func DeleteMemoryEmbedding

func DeleteMemoryEmbedding(mgr *embedding.EmbeddingManager, name string) error

DeleteMemoryEmbedding removes a memory's embedding from the ConversationStore. This is called after DeleteMemory() to keep the vector index in sync.

Graceful failure: Errors are logged but not returned as fatal. Memory files are always deleted from disk regardless of embedding cleanup.

func DeleteSession

func DeleteSession(sessionID string) error

DeleteSession removes a session state file

func DeleteSessionScoped

func DeleteSessionScoped(sessionID, workingDir string) error

func DetectLanguages

func DetectLanguages(dir string) []string

func EmbedAndStoreTurn

func EmbedAndStoreTurn(ctx context.Context, mgr *embedding.EmbeddingManager, turn *ConversationTurn) error

EmbedAndStoreTurn computes embeddings for a conversation turn's prompt and actionable summary using the static embedding provider, then stores the result as a VectorRecord in the ConversationStore.

Graceful failure: Errors are logged but not returned. The caller (checkpoint recording) should always succeed regardless of embedding failures.

func EmbedMemory

func EmbedMemory(ctx context.Context, mgr *embedding.EmbeddingManager, name string, content string) error

EmbedMemory embeds a memory file's content and stores it in the ConversationStore as a VectorRecord with Type "memory". This is called after SaveMemory() to keep the vector index in sync.

Graceful failure: Errors are logged but not returned as fatal. Memory files are always saved to disk regardless of embedding success.

func EstimateTokens

func EstimateTokens(text string) int

EstimateTokens provides a token estimation based on OpenAI's tiktoken approach. Delegates to the centralized implementation in agent_api for consistency across all providers.

func ExecuteTool added in v0.16.19

func ExecuteTool(ctx context.Context, toolName string, args map[string]interface{}, agent *Agent, rawArgsJSON string) ([]api.ImageData, string, error)

ExecuteTool executes a tool with standardized parameter validation and error handling

func ExportStateToJSON

func ExportStateToJSON(state *ConversationState) ([]byte, error)

ExportStateToJSON converts a ConversationState to JSON bytes

func FormatCLIMessage

func FormatCLIMessage(similarity float64, threshold float64) string

FormatCLIMessage returns a human-readable drift notification for CLI display.

func FormatFileChangesForSummary added in v0.16.4

func FormatFileChangesForSummary(changes []TranscriptFileChange) string

FormatFileChangesForSummary renders a manifest into the canonical text block appended to a /compact summary. The format is chosen so parseCompactedFilesBlock can round-trip it back to TranscriptFileChange entries, preserving source / tool attribution across compaction boundaries. Returns the empty string when the manifest is empty so callers can skip appending altogether.

func FormatProactiveContext

func FormatProactiveContext(results []ProactiveContextResult, config ProactiveContextConfig, now time.Time) string

FormatProactiveContext formats retrieved results as a "Previous Work" section suitable for injection into the agent's system prompt.

Output format:

## Previous Work (Contextual Memory)

The following past work may be relevant. Evaluate critically and discard anything irrelevant.

### <first line of prompt> (<relative time>)
User: "<user prompt>"
Summary: <actionable summary>

Returns "" when results is empty. The output is capped at config.MaxContextChars characters. Pass now=time.Time{} to use the current time (same pattern as RetrieveProactiveContext).

func FormatSemanticRecall added in v0.16.4

func FormatSemanticRecall(items []RecalledItem, maxChars int) string

FormatSemanticRecall renders the recall items as a markdown block to inject into the system supplement. Returns "" when there's nothing to inject so the caller can short-circuit.

maxChars caps the total output size. Use semanticRecallMaxInjectedChars (8000) for the default ceiling, or a model-aware value for per-model tuning.

func FormatWakeupBatch added in v0.16.19

func FormatWakeupBatch(notifications []Notification) string

func GenerateUnifiedDiff added in v0.16.12

func GenerateUnifiedDiff(path, original, proposed string) (string, error)

GenerateUnifiedDiff produces a standard unified-diff string from original and proposed content, suitable for terminal display.

func GetActiveSubagents

func GetActiveSubagents() int

GetActiveSubagents returns the current number of running subagents.

func GetEmbeddedPlanningPrompt

func GetEmbeddedPlanningPrompt(createTodos bool) (string, error)

GetEmbeddedPlanningPrompt returns the embedded planning prompt

func GetEmbeddedRollupPrompt added in v0.16.4

func GetEmbeddedRollupPrompt() string

GetEmbeddedRollupPrompt returns the rollup summarizer prompt embedded from prompts/rollup_prompt.md. The body is used verbatim as the system prompt for the rollup worker's LLM call.

func GetEmbeddedSystemPrompt

func GetEmbeddedSystemPrompt() (string, error)

GetEmbeddedSystemPrompt returns the embedded system prompt

func GetEmbeddedSystemPromptWithProvider

func GetEmbeddedSystemPromptWithProvider(provider string) (string, error)

GetEmbeddedSystemPromptWithProvider returns the embedded system prompt

func GetSessionName

func GetSessionName(sessionID string) string

GetSessionName returns the name of a session

func GetSessionNameScoped

func GetSessionNameScoped(sessionID, workingDir string) string

func GetSessionPreview

func GetSessionPreview(sessionID string) string

GetSessionPreview returns the first 50 characters of the first user message

func GetSessionPreviewScoped

func GetSessionPreviewScoped(sessionID, workingDir string) string

func GetSettingValue added in v0.16.19

func GetSettingValue(cfg *configuration.Config, key string) (string, error)

GetSettingValue returns the string representation of a config setting by key. It's an exported wrapper around getConfigValue for use by other packages.

func GetSkillManifest

func GetSkillManifest(content string) (map[string]string, string, error)

func GetStateDir

func GetStateDir() (string, error)

GetStateDir returns the directory for storing conversation state

func IncrementActiveSubagents

func IncrementActiveSubagents()

IncrementActiveSubagents bumps the active-subagent counter; paired with DecrementActiveSubagents under a defer in the spawner.

func InstrumentedRecall added in v0.16.19

func InstrumentedRecall(a *Agent, ctx context.Context, query string)

InstrumentedRecall wraps an InjectSemanticRecall invocation with per-turn telemetry. Captures:

  • ItemsRecalled: number of RecalledItems returned
  • TopSimilarity: highest cosine similarity in the result set
  • RecallLatencyMS: wall time for the Recall() call
  • CheckpointIDs / Workspaces: metadata from the recalled items

The record is appended to ~/.config/sprout/recall_metrics.jsonl.

The instrumentation never alters the agent's behavior — it's a fire-and-forget observer. Failures (sink init, file IO, etc) are silent at the agent level.

NOTE: InstrumentedRecall calls a.Recall() to capture metrics, then calls a.InjectSemanticRecall() which internally calls a.Recall() again. The duplicate recall work is bounded by the 2-second context timeout at the call site and is acceptable overhead for v1 instrumentation.

func IsInteractiveTool

func IsInteractiveTool(name string) bool

IsInteractiveTool reports whether the named tool is registered with Interactive=true in the handler registry. Unknown tools return false. Use this from CLI subscribers (e.g. the activity-indicator goroutine) to decide whether to suppress transient chrome that would clobber the tool's own prompt.

func IsMemoryIntensiveCommand added in v0.16.19

func IsMemoryIntensiveCommand(cmd string) bool

IsMemoryIntensiveCommand returns true when a shell command is likely to spawn multiple processes or workers that consume significant memory. Test runners, bundlers, and compilers are the primary targets.

func ListChangesEmpty added in v0.16.18

func ListChangesEmpty() string

ListChangesEmpty returns the disabled-tracker response: an empty manifest with enabled=false. Used when no agent AND no persisted history should be scanned (e.g. the diff endpoint, which is meaningless without a tracker).

func ListChangesPersistedOnly added in v0.16.18

func ListChangesPersistedOnly(args map[string]interface{}) (string, error)

ListChangesPersistedOnly returns a session manifest built entirely from the persisted history store — no live agent required. The JSON shape matches list_changes so the frontend doesn't branch.

func ListSessions

func ListSessions() ([]string, error)

ListSessions returns all available session IDs

func ListTranscriptSnapshots added in v0.16.4

func ListTranscriptSnapshots(sessionID, workingDir string) ([]string, error)

ListTranscriptSnapshots returns snapshot file paths for the given session within the current workspace scope, sorted oldest-first.

func LoadContextFiles

func LoadContextFiles() (string, error)

LoadContextFiles loads and formats context files for inclusion in system prompt

func LoadMemoriesForPrompt

func LoadMemoriesForPrompt() string

LoadMemoriesForPrompt loads all memories and formats them for inclusion in the system prompt Returns empty string if no memories exist

func LoadMemoryContent

func LoadMemoryContent(name string) (string, error)

LoadMemoryContent reads a single memory file by name The name should be without the .md extension (e.g., "git-safety" reads git-safety.md)

func MigrateMemories

func MigrateMemories(ctx context.Context, mgr *embedding.EmbeddingManager)

MigrateMemories performs a one-time migration of all existing memory files to the ConversationStore. It uses sync.Once to ensure it only runs once per process lifetime, even if called multiple times.

Migration skips files that are already embedded (by checking if a record with ID "memory:<name>" exists in the store).

The manager's closeChan is also selected alongside ctx.Done() so a DisableEmbeddingIndex call that arrives mid-migration aborts the loop promptly instead of continuing to call provider.Embed / store.Store on a torn-down manager.

func NewConversationPruner

func NewConversationPruner(debug bool) *core.ConversationPruner

NewConversationPruner constructs a pruner with sprout's traditional defaults: adaptive strategy with seed-default thresholds. The debug flag is retained for caller compatibility but unused — seed routes observability through the EventPublisher instead of stderr prints.

func NewSeedToolRegistry

func NewSeedToolRegistry(agent *Agent) *core.ToolRegistry

NewSeedToolRegistry creates a seed core.ToolRegistry with all 30 sprout tools registered. The registry implements core.ToolExecutor directly, so it can be used as the Executor in core.Options.

Seed's ToolRegistry handles: channel suffix stripping, alias resolution, argument parsing/repair, type coercion, required parameter validation, per-tool timeouts, result truncation, circuit breakers, parallel execution for SafeForParallel tools, and event publishing.

Sprout-specific concerns are wired through:

  • PreExecuteHook: security classification + subagent nesting prevention
  • Handler closures: capture agent for sprout's (ctx, agent, args) signature and apply all post-processing (constraints, truncation, secret redaction, duplicate embedding check, TodoWrite events, error sanitization).

func NewSproutProvider

func NewSproutProvider(agent *Agent, client api.ClientInterface) (core.Provider, error)

NewSproutProvider creates a Provider that wraps a sprout ClientInterface.

func NewTestStateDir added in v0.16.6

func NewTestStateDir(t *testing.T) func()

NewTestStateDir redirects pkg/agent's session-persistence path AND the global search-index updater to an isolated t.TempDir so that tests creating real Agents don't leak state JSONs or search-index.json into the caller's ~/.sprout/sessions/.

The search-index redirect is load-bearing: SaveStateScoped triggers search.MarkSessionDirty, which schedules a debounced BuildIndex. Without isolation that BuildIndex walks the entire real sessions corpus (~250 MB including 93 MB session JSONs), building an HNSW index with 30+ GB peak allocation.

Backstory: tests in cmd/ build real Agent instances to exercise the chat/plan loop. Each Agent runs autoSaveState() on a timer, which writes to whatever GetStateDir() returns. Without this helper that's the developer's real ~/.sprout/sessions/, and ~90 mock-provider session JSONs accumulated there before we caught it on 2026-06-08. See the `cleanup` body below for the Layer-5 detector that fails any future test that bypasses this isolation.

Returns a cleanup func that:

  1. Restores the original getStateDirFunc (mirrors t.Setenv unwind semantics but for our package-level function var).
  2. Snapshots the real ~/.sprout/sessions/ contents at test start and re-checks at cleanup. Any new file under that tree fails the test with a clear pointer at this helper — the same pattern pkg/configuration/testing_isolation.go uses for the config file.

Usage:

func TestMyCmdThingy(t *testing.T) {
    defer agent.NewTestStateDir(t)()
    // ... build and use a real Agent without leaking state.
}

The helper lives in a non-_test.go file so it can be imported from cmd/ tests (Go forbids importing from _test.go across packages).

func NormalizeYAMLOrdered added in v0.16.4

func NormalizeYAMLOrdered(v interface{}) interface{}

NormalizeYAMLOrdered recursively normalizes YAML-parsed values into *OrderedMap-safe representations. It handles the remaining cases where yaml.Unmarshal into interface{} produces map[interface{}]interface{} or map[string]interface{} values, converting them to *OrderedMap. This replaces the old normalizeYAMLValue function.

When key order is unknown (e.g., from a regular map), keys are sorted alphabetically as a deterministic fallback.

func ParseAgentsMd

func ParseAgentsMd(path string) (name string, description string)

func ParseJSONOrderedAny added in v0.16.8

func ParseJSONOrderedAny(content string) (interface{}, error)

ParseJSONOrderedAny parses a JSON string, preserving key order in objects. Returns *OrderedMap for objects and []interface{} for arrays (with nested objects also wrapped in *OrderedMap).

func PublishModel

func PublishModel(model string)

PublishModel publishes a model selection (placeholder implementation)

func RegisterComputerUseTools added in v0.16.18

func RegisterComputerUseTools(cfg *configuration.Config) error

RegisterComputerUseTools wires the computer_user persona's desktop-control tools (SP-063) into the agent's registries — but only when cfg explicitly enables them. Idempotent and safe to call on every agent creation.

Gating layers (defense in depth):

  1. cfg.ComputerUse.Enabled must be true — off by default.
  2. A real platform backend must be constructable (macOS+cliclick or linux/X11+xdotool); otherwise nothing is registered and the reason is returned for the caller to surface.
  3. Exposure is limited to the computer_user persona's allowed_tools, and a dispatch-layer guard (isComputerUseToolBlocked) rejects the tools for any other active persona.
  4. Every action is rate-limited and audited (see the wrapped backend).

func RegisterPasswordRequestForTest added in v0.16.18

func RegisterPasswordRequestForTest(requestID string) chan string

RegisterPasswordRequestForTest registers a password request in the broker for use by webui tests. Returns the response channel so the test can verify delivery. Call CleanupPasswordRequestForTest after the test.

func RenameSession

func RenameSession(sessionID string, newName string) error

RenameSession renames a session by updating the name field in the state file

func RenameSessionScoped

func RenameSessionScoped(sessionID, newName, workingDir string) error

func ResetMigrationForTesting

func ResetMigrationForTesting()

ResetMigrationForTesting resets the one-time migration guard for testing purposes.

func SaveMemory

func SaveMemory(name string, content string) error

SaveMemory writes a memory file Sanitizes the name: lowercase, replace spaces with hyphens, strip special chars Keeps only alphanumeric, hyphens, and underscores

func SerializeJSONOrdered added in v0.16.4

func SerializeJSONOrdered(data interface{}) (string, error)

SerializeJSONOrdered serializes data to a pretty-printed JSON string with 2-space indentation. When data is an *OrderedMap, keys are emitted in insertion order. For regular map[string]interface{} and other types, the standard json.Marshal behavior is used as a fallback.

HTML escaping is disabled to match the behavior of the existing serializeStructuredContent function.

func SerializeYAMLOrdered added in v0.16.4

func SerializeYAMLOrdered(data interface{}) (string, error)

SerializeYAMLOrdered serializes data to a YAML string. When data is an *OrderedMap, keys are emitted in insertion order by constructing a yaml.Node tree. For regular map[string]interface{} and other types, the standard yaml.Marshal is used as a fallback.

A trailing newline is always included to match existing YAML behavior.

func SetActiveComputerUseAgent added in v0.16.19

func SetActiveComputerUseAgent(a *Agent)

SetActiveComputerUseAgent marks a as the agent currently driving computer_use actions. Called from agent creation / ApplyPersona when the computer_user persona activates. Cleared when the persona is deactivated or the agent is shut down.

func SetEditApprovalTimeout added in v0.16.17

func SetEditApprovalTimeout(d time.Duration)

SetEditApprovalTimeout overrides the default WebUI response timeout. Intended for tests; production code uses the 30-minute default.

func SetGetStateDirForTest

func SetGetStateDirForTest(dir string) func() (string, error)

SetGetStateDirForTest is a convenience helper that sets getStateDirFunc to return a fixed directory for testing.

func SetGetStateDirForTestError

func SetGetStateDirForTestError(msg string) func() (string, error)

SetGetStateDirForTestError is a convenience helper that sets getStateDirFunc to return an error for testing error handling.

func SetGetStateDirFunc

func SetGetStateDirFunc(fn func() (string, error)) func() (string, error)

SetGetStateDirFunc sets the getStateDirFunc for testing purposes. Returns the previous function so it can be restored after the test.

func SetPackageDebugLogging

func SetPackageDebugLogging(enabled bool)

SetPackageDebugLogging toggles the debug gate at runtime. Useful for tests and for the agent's --debug flag wiring.

func SetPackageLogger

func SetPackageLogger(l *AgentLogger)

SetPackageLogger sets the package-level AgentLogger that package-level functions (without an *Agent receiver) use for structured logging. Called during agent initialization so embedding, proactive context, etc. all route through the same logger with session context.

func SetSettingValue added in v0.16.19

func SetSettingValue(cfg *configuration.Config, key, value string) error

SetSettingValue updates a config setting by key and value string. It's an exported wrapper around setConfigValue for use by other packages.

func SetStateDirFuncForTesting

func SetStateDirFuncForTesting(fn func() (string, error)) func()

SetStateDirFuncForTesting replaces the internal GetStateDir implementation for tests. It returns a restore function that will reset the original implementation when called. This function is safe for use in test code only.

Usage:

restore := agent.SetStateDirFuncForTesting(func() (string, error) {
    return t.TempDir(), nil
})
defer restore()

func SetTestStateDirHook added in v0.16.6

func SetTestStateDirHook(dir string) func()

SetTestStateDirHook overrides the session state dir to the given path for the lifetime of the returned restore func. Lower-level primitive than NewTestStateDir(t) — useful from TestMain in test packages that don't yet have a *testing.T. Prefer NewTestStateDir inside individual tests; this is for package-wide isolation hooks.

Returns a restore func that puts getStateDirFunc back to its prior value. Idempotent — calling restore more than once is a no-op.

func SettingEnumValues added in v0.16.19

func SettingEnumValues(key string) []string

SettingEnumValues returns the enum values for a setting key, or nil if the setting is not an enum (freeform input). It is used by the interactive settings browser to offer a picker instead of raw text input.

func SettingIsListType added in v0.16.19

func SettingIsListType(key string) bool

SettingIsListType returns true if the setting key is a list-type setting that should get an add/remove/set sub-menu in the interactive browser.

func SnapshotRealStateDir added in v0.16.6

func SnapshotRealStateDir() (realDir string, before map[string]time.Time)

SnapshotRealStateDir captures the current file set under the user's real ~/.sprout/sessions/ for later leak-detection. Use this from TestMain before installing SetTestStateDirHook; pair it with AssertNoStateLeak at the end of TestMain to fail loudly if any test bypassed the isolation hook and wrote to the real dir.

Returns ("", nil) when the real dir can't be resolved (e.g. no HOME env in CI) — the post-snapshot call then degrades to a no-op rather than fabricating a false negative.

func SproutDirFromContext added in v0.16.25

func SproutDirFromContext(ctx context.Context) string

SproutDirFromContext returns the workspace-aware sprout directory from ctx, or falls back to os.Getwd() (matching the legacy behavior for CLI-triggered workflows where CWD is the workspace root).

func SummarizeMySessionEmpty added in v0.16.18

func SummarizeMySessionEmpty() string

SummarizeMySessionEmpty returns the disabled-tracker block-summary response: an empty blocks list. Used when no agent is available.

func SupportedSettingKeys added in v0.16.19

func SupportedSettingKeys() []string

SupportedSettingKeys returns a sorted slice of all supported setting keys.

func SweepExpiredEntries

func SweepExpiredEntries(retentionDays int, storePath string) (int, error)

SweepExpiredEntries removes persistent context entries older than retentionDays from the conversation store at storePath. If retentionDays <= 0, this is a no-op. Returns the number of entries removed.

func ValidateStreamConfig

func ValidateStreamConfig(sc *StreamConfig) error

ValidateStreamConfig validates a StreamConfig and returns an error if invalid

func WorkflowRequiresApproval added in v0.16.4

func WorkflowRequiresApproval(workflowName string) bool

WorkflowRequiresApproval reports whether the named workflow needs user confirmation before launching. This wraps WorkflowRequiresApprovalIn with the CWD-based automate.Dir() so the CLI tool path works correctly.

func WorkflowRequiresApprovalIn added in v0.16.25

func WorkflowRequiresApprovalIn(dir, workflowName string) bool

WorkflowRequiresApprovalIn reports whether the named workflow needs user confirmation before launching, using the specified directory instead of the CWD-based automate.Dir().

FAIL-SAFE: any error resolving or parsing the workflow returns true so a missing file or malformed JSON can't be used to slip past the prompt.

func WriteTestSessionFile

func WriteTestSessionFile(stateDir, sessionID, workingDir string, state *ConversationState) error

WriteTestSessionFile creates a scoped session file for testing.

Types

type Agent

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

func NewAgent

func NewAgent() (*Agent, error)

NewAgent creates a new agent with auto-detected provider

func NewAgentWithClient

func NewAgentWithClient(client api.ClientInterface, clientType api.ClientType, configManager *configuration.Manager) (*Agent, error)

NewAgentWithClient builds an agent around a pre-constructed provider client. The interactive provider-resolution path in newAgentWithConfigManager (API-key prompts, connection checks, recovery loops) is skipped — useful for WASM/SDK callers where the caller already knows which provider and model to use, and where API keys live elsewhere (e.g. attached server-side by the sprout-foundry platform proxy).

The configManager must already be initialized; pass one from configuration.NewManagerSilent() or similar. The returned agent is a production agent (full lifecycle: context limits, session cleanup, tool registry, persona auto-activation).

func NewAgentWithConfigDir

func NewAgentWithConfigDir(configDir, model string) (*Agent, error)

NewAgentWithConfigDir creates a new agent using a per-client config directory. This enables per-client config isolation for the WebUI, where each X-Sprout-Client-ID can have its own isolated config directory so settings changes by one client don't affect another.

func NewAgentWithLayers

func NewAgentWithLayers(globalDir, workspaceDir, model string) (*Agent, error)

NewAgentWithLayers creates a new agent using layered configuration. globalDir contains global config (~/.config/sprout/), workspaceDir contains workspace config. This is the preferred method for WebUI usage where workspace config is supported.

func NewAgentWithLayersInWorkspace added in v0.16.25

func NewAgentWithLayersInWorkspace(globalDir, workspaceDir, workspaceRoot, model string) (*Agent, error)

NewAgentWithLayersInWorkspace creates a new agent using layered configuration with an explicit workspace root, avoiding the need for os.Chdir in daemon mode. globalDir contains global config (~/.config/sprout/), workspaceDir contains workspace config. workspaceRoot is the absolute path to the workspace directory.

func NewAgentWithModel

func NewAgentWithModel(model string) (*Agent, error)

NewAgentWithModel creates a new agent with optional model override

func NewTestAgent

func NewTestAgent() *Agent

NewTestAgent creates a minimal Agent suitable for unit tests.

Tests that create bare &Agent{} structs must remember to call initSubManagers() to avoid nil-pointer panics. NewTestAgent() eliminates that two-step dance by returning an Agent whose sub-managers (state, output, security, mcpSub) and basic fields (shellCommandHistory) are already initialised.

The returned agent has NO API client, config manager, or system prompt — those are only needed in integration-style tests that should use NewAgent() instead.

Callers may freely mutate the returned Agent (e.g. setting debug, swapping in a mock state manager) after construction.

func (*Agent) AddMessage

func (a *Agent) AddMessage(message api.Message)

AddMessage adds a single message to the conversation history

func (*Agent) AddSessionAllowedFolder

func (a *Agent) AddSessionAllowedFolder(folder string)

AddSessionAllowedFolder records the folder picked by the user from the filesystem approval dialog so future accesses under it are auto-approved for the rest of this session.

func (*Agent) AddTaskAction

func (a *Agent) AddTaskAction(actionType, description, details string)

AddTaskAction records a completed task action for continuity

func (*Agent) AddToHistory

func (a *Agent) AddToHistory(command string)

AddToHistory adds a command to the history buffer

func (*Agent) AllowAppForComputerUse added in v0.16.19

func (a *Agent) AllowAppForComputerUse(key string)

AllowAppForComputerUse adds the given app key to the per-session allowlist. Guarded by computerUseMu.

func (*Agent) ApplyPersona

func (a *Agent) ApplyPersona(personaID string) error

ApplyPersona activates a configured persona and applies provider/model/system-prompt overrides.

func (*Agent) ApplyState

func (a *Agent) ApplyState(state *ConversationState)

ApplyState applies a loaded state to the current agent

func (*Agent) ApplySyncOp

func (a *Agent) ApplySyncOp(op SyncOp, workspaceRoot string) SyncOpResult

ApplySyncOp applies a single SyncOp to the workspace filesystem. It validates the operation, checks for conflicts with container-side changes, applies the change, and updates the file metadata.

func (*Agent) ApplySyncOpBatch

func (a *Agent) ApplySyncOpBatch(ops []SyncOp, workspaceRoot string) []SyncOpResult

ApplySyncOpBatch applies a slice of SyncOps in order, collecting results. Stops on the first conflict, returning Accepted=false for remaining ops.

func (*Agent) Breakpoints added in v0.16.25

func (a *Agent) Breakpoints() []Breakpoint

Breakpoints returns all user messages as forkable breakpoints.

func (*Agent) BuildCheckpointCompactedMessages

func (a *Agent) BuildCheckpointCompactedMessages(messages []api.Message) ([]api.Message, []TurnCheckpoint)

func (*Agent) BuildTranscriptSnapshot added in v0.16.4

func (a *Agent) BuildTranscriptSnapshot(label string, includePreview bool) *TranscriptSnapshot

BuildTranscriptSnapshot constructs an in-memory snapshot of the agent's current conversation state plus diagnostic annotations. Pure read — does not mutate the agent or touch disk.

func (*Agent) CanSpawnSubagents

func (a *Agent) CanSpawnSubagents() bool

CanSpawnSubagents returns true if this agent is allowed to spawn subagents (i.e., current depth is less than the configured max depth).

func (*Agent) CaptureTranscriptSnapshot added in v0.16.4

func (a *Agent) CaptureTranscriptSnapshot(label string, includePreview bool) (string, error)

CaptureTranscriptSnapshot builds a snapshot and writes it to ~/.sprout/transcripts/<scope-hash>/<session-id>/<UTC-ts>-<label>.json. Returns the absolute path of the file written so callers can report it to the user or log it.

func (*Agent) CheckFileContentSecurity

func (a *Agent) CheckFileContentSecurity(filePath string, content string)

CheckFileContentSecurity runs security concern detection on file content after a write. In WebUI mode, it uses the event-bus-based ApprovalManager to show a dialog. In CLI mode, it falls back to the interactive logger prompt. Ignored concerns are tracked per-file so they are not re-prompted.

func (*Agent) CheckForInterrupt

func (a *Agent) CheckForInterrupt() bool

CheckForInterrupt checks if an interrupt was requested

func (*Agent) CheckPatchConflict

func (a *Agent) CheckPatchConflict(path string) (bool, string)

CheckPatchConflict checks whether a container patch to the given path conflicts with unsynced browser edits. Returns (conflict bool, theirsPath string). theirsPath is "<path>.theirs" when conflict is true, empty otherwise.

func (*Agent) ClearActivePersona

func (a *Agent) ClearActivePersona()

ClearActivePersona removes any active persona override and restores the base system prompt.

func (*Agent) ClearConversationHistory

func (a *Agent) ClearConversationHistory()

ClearConversationHistory clears the conversation history

func (*Agent) ClearInputInjectionContext

func (a *Agent) ClearInputInjectionContext()

ClearInputInjectionContext clears any pending input injections

func (*Agent) ClearInterrupt

func (a *Agent) ClearInterrupt()

ClearInterrupt resets the interrupt state

func (*Agent) ClearSessionOverrides

func (a *Agent) ClearSessionOverrides()

ClearSessionOverrides clears any session-scoped provider/model overrides. This should be called when a webui session ends to restore config-based behavior.

func (*Agent) ClearShellCommandHistory

func (a *Agent) ClearShellCommandHistory()

ClearShellCommandHistory removes all entries from shell command history

func (*Agent) ClearTrackedChanges

func (a *Agent) ClearTrackedChanges()

ClearTrackedChanges clears all tracked changes (but keeps tracking enabled)

func (*Agent) CommitChanges

func (a *Agent) CommitChanges(llmResponse string) error

CommitChanges commits all tracked changes to the change tracker

func (*Agent) ConsumePendingStrictSwitchNotice

func (a *Agent) ConsumePendingStrictSwitchNotice() string

func (*Agent) DeferredMessageCount

func (a *Agent) DeferredMessageCount() int

DeferredMessageCount returns how many messages are currently queued. Used by the UI to show "N queued" hints. Reads are racy with enqueues but counts are advisory anyway.

func (*Agent) DisableAutoPruning

func (a *Agent) DisableAutoPruning()

DisableAutoPruning disables automatic conversation pruning

func (*Agent) DisableChangeTracking

func (a *Agent) DisableChangeTracking()

DisableChangeTracking disables change tracking

func (*Agent) DisableEmbeddingIndex

func (a *Agent) DisableEmbeddingIndex()

DisableEmbeddingIndex stops and cleans up the embedding manager. It persists the preference to the workspace config so it stays disabled on restart.

func (*Agent) DisableStreaming

func (a *Agent) DisableStreaming()

DisableStreaming disables response streaming

func (*Agent) DisableWakeup added in v0.16.19

func (a *Agent) DisableWakeup()

func (*Agent) DrainDeferredMessages

func (a *Agent) DrainDeferredMessages() []string

DrainDeferredMessages atomically removes and returns all queued messages. The CLI's REPL loop calls this after ReadLine() returns the user's next prompt and prepends them to the typed text.

func (*Agent) DrainNotifications added in v0.16.19

func (a *Agent) DrainNotifications() []Notification

func (*Agent) ElevateSessionToPermissive

func (a *Agent) ElevateSessionToPermissive()

ElevateSessionToPermissive sets the agent's transient risk-profile override to "permissive" for the rest of this session. Used by the "Elevate permissions" choice on the approval dialog. Does NOT persist to disk — the user is expected to run `/risk-profile permissive` if they want this to survive restart.

Critical-tier ops (rm -rf /, fork bombs) still block; "permissive" only widens the auto-approved set, it does not disable the cascade.

func (*Agent) EnableAutoPruning

func (a *Agent) EnableAutoPruning()

EnableAutoPruning enables automatic conversation pruning with default adaptive strategy

func (*Agent) EnableChangeTracking

func (a *Agent) EnableChangeTracking(instructions string)

EnableChangeTracking enables change tracking for this agent session.

Session scoping: the FIRST call (tracker == nil) creates the tracker, assigns the session's stable revisionID, and captures `instructions` as the session's identity. Subsequent calls within the same session (same Agent instance — e.g. every ProcessQuery in a daemon chat) do NOT reset the buffer: they only ensure tracking is enabled and re-prime the shell cache. The change buffer is therefore session-long, matching what list_changes / recover_file / revert_my_changes promise ("files you've created, modified, or deleted this session"). A genuine reset happens only when a new Agent is constructed for a new chat.

Side effect: primes the shell-mutation snapshot cache against the agent's workspace root. This is the one-time cost (~280 ms on a 5000-file workspace) that lets every subsequent shell_command be tracked via a cheap stat-only diff. Without this prime the first shell command's mutations would silently establish the baseline (auto-prime in TrackShellTurn) and go un-recorded — fine for read-only commands, but a real loss if the first shell does any writes.

func (*Agent) EnableEmbeddingIndex

func (a *Agent) EnableEmbeddingIndex() error

EnableEmbeddingIndex initializes the embedding manager and starts building the index in the background. Call this when the user explicitly enables indexing for the workspace (via /index command or UI toggle). It persists the preference to the workspace config so it survives restarts.

func (*Agent) EnableStreaming

func (a *Agent) EnableStreaming(callback func(string))

EnableStreaming enables response streaming with a callback

func (*Agent) EnableWakeupIfDisabled added in v0.16.19

func (a *Agent) EnableWakeupIfDisabled()

func (*Agent) EndQuery added in v0.16.17

func (a *Agent) EndQuery()

EndQuery releases the "query in progress" flag set by TryBeginQuery. Safe to call multiple times and safe to call when the flag is already clear (idempotent).

func (*Agent) EnqueueDeferredMessage

func (a *Agent) EnqueueDeferredMessage(text string)

func (*Agent) EvaluateOperationRisk

func (a *Agent) EvaluateOperationRisk(command string) configuration.RiskLevel

EvaluateOperationRisk determines the risk level of a command for the currently active persona, using the persona's auto-approve rules. Returns RiskLevelCritical / High / Medium / Low.

Resolution order (matches the SP-058 risk profile design):

  1. Critical patterns (rm -rf root, fork bomb) — ALWAYS return Critical, regardless of persona, profile, or active mode.
  2. Active persona has its own AutoApproveRules → use them (preserves EA autonomy and any other persona-specific carve-outs).
  3. Otherwise → resolve the agent's active risk profile and use its baked-in rules.
  4. No persona at all → return Low (no cascade gating, classic non-EA behavior).

func (*Agent) ExportState

func (a *Agent) ExportState() ([]byte, error)

ExportState exports the current agent state for persistence

func (*Agent) FleetBudgetExceeded

func (a *Agent) FleetBudgetExceeded() bool

FleetBudgetExceeded reports whether the fleet budget was exceeded during this agent's execution (mid-run truncation). Returns true if EITHER the token budget or the USD budget tripped — both use the same truncation flag because the downstream behavior is identical.

func (*Agent) ForkAtBreakpoint added in v0.16.25

func (a *Agent) ForkAtBreakpoint(breakpointIndex int) (string, error)

ForkAtBreakpoint saves the current session, then truncates the conversation to messages [0..breakpointIndex] (where breakpointIndex is 1-based, matching the Breakpoints list). Returns the new session ID. The original session is preserved on disk.

func (*Agent) GenerateActionSummary

func (a *Agent) GenerateActionSummary() string

GenerateActionSummary creates a summary of completed actions for continuity

func (*Agent) GenerateCompactSummary

func (a *Agent) GenerateCompactSummary() string

GenerateCompactSummary creates a compact summary for session continuity (max 5K context)

func (*Agent) GenerateConversationSummary

func (a *Agent) GenerateConversationSummary() string

GenerateConversationSummary creates a comprehensive summary of the conversation including todos

func (*Agent) GenerateResponse

func (a *Agent) GenerateResponse(messages []api.Message) (string, error)

GenerateResponse generates a simple response using the current model without tool calls.

SP-073: uses a.interruptCtx so Stop/cancel aborts the in-flight call. If callers need to pass their own context, they can set it via SetInterruptCtx before calling.

func (*Agent) GenerateSessionSummary

func (a *Agent) GenerateSessionSummary() string

GenerateSessionSummary creates a summary of previous actions for continuity

func (*Agent) GetActivePersona

func (a *Agent) GetActivePersona() string

GetActivePersona returns the currently active persona ID.

func (*Agent) GetActiveRiskProfile

func (a *Agent) GetActiveRiskProfile() configuration.RiskProfile

GetActiveRiskProfile returns the profile currently in effect for this agent (override > config > default). Exposed for status commands / debug logging.

func (*Agent) GetAllShellCommandHistory

func (a *Agent) GetAllShellCommandHistory() map[string]*ShellCommandResult

GetAllShellCommandHistory returns a copy of the shell command history

func (*Agent) GetAuditLogger added in v0.16.12

func (a *Agent) GetAuditLogger() *tools.AuditLogger

GetAuditLogger returns the agent-owned security audit logger, or nil when none is configured. Nil-safe: callers should nil-check before use.

func (*Agent) GetAvailablePersonaIDs

func (a *Agent) GetAvailablePersonaIDs() []string

GetAvailablePersonaIDs returns all configured persona IDs, filtering out LocalOnly personas when running in cloud mode.

func (*Agent) GetAvailableToolNames

func (a *Agent) GetAvailableToolNames() []string

GetAvailableToolNames returns the effective tool names available to the active session.

func (*Agent) GetAverageTPS

func (a *Agent) GetAverageTPS() float64

GetAverageTPS returns the average TPS across all requests

func (*Agent) GetBackgroundProcessManager

func (a *Agent) GetBackgroundProcessManager() *tools.BackgroundProcessManager

GetBackgroundProcessManager returns the background process manager.

func (*Agent) GetCacheWriteTokens added in v0.16.17

func (a *Agent) GetCacheWriteTokens() int

GetCacheWriteTokens returns the total tokens written to the provider cache

func (*Agent) GetCachedCostSavings

func (a *Agent) GetCachedCostSavings() float64

GetCachedCostSavings returns the cost savings from cached tokens

func (*Agent) GetCachedTokens

func (a *Agent) GetCachedTokens() int

GetCachedTokens returns the total cached/reused tokens

func (*Agent) GetChangeCount

func (a *Agent) GetChangeCount() int

GetChangeCount returns the number of file changes tracked in this session

func (*Agent) GetChangeTracker

func (a *Agent) GetChangeTracker() *ChangeTracker

GetChangeTracker returns the change tracker (can be nil)

func (*Agent) GetChangesSummary

func (a *Agent) GetChangesSummary() string

GetChangesSummary returns a summary of tracked changes

func (*Agent) GetChargedCostTotal added in v0.16.19

func (a *Agent) GetChargedCostTotal() float64

func (*Agent) GetCompletionTokens

func (a *Agent) GetCompletionTokens() int

GetCompletionTokens returns the total completion tokens used

func (*Agent) GetConfig

func (a *Agent) GetConfig() *configuration.Config

GetConfig returns the configuration

func (*Agent) GetConfigManager

func (a *Agent) GetConfigManager() *configuration.Manager

GetConfigManager returns the configuration manager

func (*Agent) GetConfigOverrides

func (a *Agent) GetConfigOverrides() map[string]interface{}

GetConfigOverrides returns the session-scoped config overrides.

func (*Agent) GetContextTokens

func (a *Agent) GetContextTokens() (used, limit int)

GetTotalCost returns the total cost of the conversation GetContextTokens returns the current and max token counts for the active model's context window. (0, 0) when state is unavailable. SP-048-3.

func (*Agent) GetContextWarningIssued

func (a *Agent) GetContextWarningIssued() bool

GetContextWarningIssued returns whether a context warning has been issued

func (*Agent) GetCurrentContextTokens

func (a *Agent) GetCurrentContextTokens() int

GetCurrentContextTokens returns the current context token count

func (*Agent) GetCurrentIteration

func (a *Agent) GetCurrentIteration() int

GetCurrentIteration returns the current iteration number

func (*Agent) GetCurrentTPS

func (a *Agent) GetCurrentTPS() float64

GetCurrentTPS returns the current TPS value (alias for GetLastTPS)

func (*Agent) GetDebugLogPath

func (a *Agent) GetDebugLogPath() string

GetDebugLogPath returns the path to the current debug log file (if any)

func (*Agent) GetElevationGate

func (a *Agent) GetElevationGate() *security.ElevationGate

GetElevationGate returns the agent's elevation gate for external use (e.g., commit flows).

func (*Agent) GetEmbeddingManager

func (a *Agent) GetEmbeddingManager() *embedding.EmbeddingManager

GetEmbeddingManager returns the embedding index manager (may be nil if embedding is not configured or enabled in the agent's config).

func (*Agent) GetEstimatedTokenResponses

func (a *Agent) GetEstimatedTokenResponses() int

GetEstimatedTokenResponses returns how many responses used estimated token usage.

func (*Agent) GetEventBus

func (a *Agent) GetEventBus() *events.EventBus

GetEventBus returns the current event bus

func (*Agent) GetEventChatID

func (a *Agent) GetEventChatID() string

GetEventChatID returns the bound chat_id from event metadata, if present.

func (*Agent) GetEventClientID

func (a *Agent) GetEventClientID() string

GetEventClientID returns the bound client_id from event metadata, if present.

func (*Agent) GetEventUserID

func (a *Agent) GetEventUserID() string

GetEventUserID returns the bound user_id from event metadata, if present.

func (*Agent) GetFileMetadata

func (a *Agent) GetFileMetadata(path string) (WorkspaceFileMetadata, bool)

GetFileMetadata returns the cached metadata for `path` (zero-value + false if absent). Read-side companion to SetFileMetadata.

func (*Agent) GetFleetUsdBudget added in v0.16.4

func (a *Agent) GetFleetUsdBudget() *FleetUsdBudget

GetFleetUsdBudget returns the agent's USD budget, or nil if none is set. Used by the SubagentRunner to propagate the same budget to spawned subagents (so the cap is workflow-wide, not per-agent).

func (*Agent) GetHistory

func (a *Agent) GetHistory() []string

GetHistory returns a defensive copy of the command history.

func (*Agent) GetHistoryCommand

func (a *Agent) GetHistoryCommand(index int) string

GetHistoryCommand returns the command at the given index from history

func (*Agent) GetHistorySize

func (a *Agent) GetHistorySize() int

GetHistorySize returns the number of commands in history

func (*Agent) GetInputInjectionContext

func (a *Agent) GetInputInjectionContext() <-chan string

GetInputInjectionContext returns the input injection channel for the new system

func (*Agent) GetLLMCallCount

func (a *Agent) GetLLMCallCount() int

GetLLMCallCount returns the total number of LLM API calls made

func (*Agent) GetLastMessages

func (a *Agent) GetLastMessages(n int) []api.Message

GetLastMessages returns the last N messages for preview

func (*Agent) GetLastPreparedToolNames

func (a *Agent) GetLastPreparedToolNames() []string

GetLastPreparedToolNames returns the tool names sent in the most recent model request.

func (*Agent) GetLastRunTerminationReason

func (a *Agent) GetLastRunTerminationReason() string

func (*Agent) GetLastTPS

func (a *Agent) GetLastTPS() float64

GetLastTPS returns the most recent TPS value from the provider

func (*Agent) GetMaxContextTokens

func (a *Agent) GetMaxContextTokens() int

GetMaxContextTokens returns the maximum context tokens for the current model

func (*Agent) GetMaxIterations

func (a *Agent) GetMaxIterations() int

GetMaxIterations returns the maximum iterations allowed (0 means unlimited)

func (*Agent) GetMessages

func (a *Agent) GetMessages() []api.Message

GetMessages returns the current conversation messages

func (*Agent) GetModel

func (a *Agent) GetModel() string

GetModel gets the current model being used by the agent

func (*Agent) GetOptimizationStats

func (a *Agent) GetOptimizationStats() map[string]interface{}

GetOptimizationStats returns optimization statistics

func (*Agent) GetOutputRedactor

func (a *Agent) GetOutputRedactor() *security.OutputRedactor

GetOutputRedactor returns the agent's output redactor for external use.

func (*Agent) GetPasswordPrompter added in v0.16.18

func (a *Agent) GetPasswordPrompter() tools.PasswordPrompter

GetPasswordPrompter returns the registered password prompter, or nil.

func (*Agent) GetPersonaProviderModel

func (a *Agent) GetPersonaProviderModel(personaID string) (string, string, error)

GetPersonaProviderModel returns effective provider/model for display.

func (*Agent) GetPreviousSummary

func (a *Agent) GetPreviousSummary() string

GetPreviousSummary returns the summary of previous actions

func (*Agent) GetPromptTokens

func (a *Agent) GetPromptTokens() int

GetPromptTokens returns the total prompt tokens used

func (*Agent) GetProvider

func (a *Agent) GetProvider() string

GetProvider returns the current provider name

func (*Agent) GetProviderType

func (a *Agent) GetProviderType() api.ClientType

GetProviderType returns the current provider type

func (*Agent) GetPruningStats

func (a *Agent) GetPruningStats() map[string]interface{}

GetPruningStats returns information about the current pruning configuration

func (*Agent) GetRevisionID

func (a *Agent) GetRevisionID() string

GetRevisionID returns the current revision ID (if change tracking is enabled)

func (*Agent) GetSecurityApprovalMgr

func (a *Agent) GetSecurityApprovalMgr() *security.ApprovalManager

GetSecurityApprovalMgr returns the security approval manager. Returns nil when the security subsystem is not initialized (e.g., bare &Agent{} in tests), so callers can safely nil-check the result.

func (*Agent) GetSecurityCautionsIssued added in v0.16.12

func (a *Agent) GetSecurityCautionsIssued() int64

GetSecurityCautionsIssued returns the number of SECURITY_CAUTION_REQUIRED errors produced this session.

func (*Agent) GetSecurityLoopsDetected added in v0.16.12

func (a *Agent) GetSecurityLoopsDetected() int64

GetSecurityLoopsDetected returns the number of times loop detection fired (the same tool+args was blocked >= securityBlockThreshold times).

func (*Agent) GetSecurityRetriesAfterCaution added in v0.16.12

func (a *Agent) GetSecurityRetriesAfterCaution() int64

GetSecurityRetriesAfterCaution returns the number of times the LLM retried the same tool+args after seeing a security caution (the count went 1→2).

func (*Agent) GetSessionID

func (a *Agent) GetSessionID() string

GetSessionID returns the session identifier

func (*Agent) GetSessionName added in v0.16.18

func (a *Agent) GetSessionName() string

GetSessionName returns a readable name for the current session. It is the exported form of generateSessionName.

func (*Agent) GetShellCommandHistoryEntry

func (a *Agent) GetShellCommandHistoryEntry(command string) (*ShellCommandResult, bool)

GetShellCommandHistoryEntry retrieves a shell command result from history

func (*Agent) GetShellCwd

func (a *Agent) GetShellCwd() string

GetShellCwd returns the current logical shell working directory.

func (*Agent) GetSubagentRunner

func (a *Agent) GetSubagentRunner() *SubagentRunner

GetSubagentRunner returns the per-agent subagent runner, creating it lazily.

func (*Agent) GetSyncStatus

func (a *Agent) GetSyncStatus() map[string]WorkspaceFileMetadata

GetSyncStatus returns a map of path → WorkspaceFileMetadata for all currently tracked files in the workspace metadata store.

func (*Agent) GetSystemPrompt

func (a *Agent) GetSystemPrompt() string

GetSystemPrompt returns the current system prompt

func (*Agent) GetTPSStats

func (a *Agent) GetTPSStats() map[string]float64

GetTPSStats returns comprehensive TPS statistics

func (*Agent) GetTaskActions

func (a *Agent) GetTaskActions() []TaskAction

GetTaskActions returns completed task actions

func (*Agent) GetTerminalManager

func (a *Agent) GetTerminalManager() tools.TerminalAccess

GetTerminalManager returns the terminal manager (may be nil in CLI mode).

func (*Agent) GetTodoManager

func (a *Agent) GetTodoManager() *tools.TodoManager

GetTodoManager returns the per-agent todo manager. This ensures session isolation in daemon mode where multiple agents run concurrently.

func (*Agent) GetTokenCostTotal added in v0.16.19

func (a *Agent) GetTokenCostTotal() float64

func (*Agent) GetTotalCost

func (a *Agent) GetTotalCost() float64

func (*Agent) GetTotalTokens

func (a *Agent) GetTotalTokens() int

GetTotalTokens returns the total tokens used across all requests

func (*Agent) GetTrackedFiles

func (a *Agent) GetTrackedFiles() []string

GetTrackedFiles returns the list of files that have been modified in this session

func (*Agent) GetTurnCheckpoints added in v0.16.25

func (a *Agent) GetTurnCheckpoints() []TurnCheckpoint

GetTurnCheckpoints returns a defensive copy of the agent's turn checkpoints. Callers (e.g. the /rewind slash command) can read the list safely without holding the internal mutex.

func (*Agent) GetUnsafeMode

func (a *Agent) GetUnsafeMode() bool

GetUnsafeMode returns whether unsafe mode is enabled

func (*Agent) GetUnsafeShellMode added in v0.16.12

func (a *Agent) GetUnsafeShellMode() bool

GetUnsafeShellMode returns whether unsafe shell mode is enabled

func (*Agent) GetValidator

func (a *Agent) GetValidator() *validation.Validator

GetValidator returns the syntax validator (nil until SetEventBus is called).

func (*Agent) GetVisionProcessor added in v0.16.18

func (a *Agent) GetVisionProcessor() *tools.VisionProcessor

GetVisionProcessor returns the agent's vision processor, creating it lazily on first call. The processor is cached for the life of the Agent so that subsequent calls reuse the same vision client and cache. Returns nil if no vision-capable provider is available, or if the agent is nil.

func (*Agent) GetWorkspaceRoot

func (a *Agent) GetWorkspaceRoot() string

GetWorkspaceRoot returns the logical workspace root for this agent instance.

func (*Agent) HandleInterrupt

func (a *Agent) HandleInterrupt() string

HandleInterrupt processes an interrupt request.

func (*Agent) HasActiveWebUIClients

func (a *Agent) HasActiveWebUIClients() bool

HasActiveWebUIClients calls the registered callback (or returns false if none is set) to check whether WebUI clients are connected. Returns false when the security submanager is unset (typical for partially-constructed agents in unit tests).

func (*Agent) HasPasswordPrompter added in v0.16.18

func (a *Agent) HasPasswordPrompter() bool

HasPasswordPrompter returns true if a password prompter is registered. Used by the risk resolver to decide whether to downgrade privileged commands from block to prompt.

func (*Agent) HasPendingNotifications added in v0.16.19

func (a *Agent) HasPendingNotifications() bool

func (*Agent) HasSessionOverrides

func (a *Agent) HasSessionOverrides() bool

HasSessionOverrides returns true if there are session-scoped provider/model overrides

func (*Agent) HasTurnCheckpoints

func (a *Agent) HasTurnCheckpoints() bool

func (*Agent) ImportState

func (a *Agent) ImportState(data []byte) error

ImportState imports agent state from JSON data

func (*Agent) IncrementWakeupResume added in v0.16.19

func (a *Agent) IncrementWakeupResume(cfg configuration.WakeupConfig) bool

func (*Agent) InjectInputContext

func (a *Agent) InjectInputContext(input string) error

InjectInputContext injects a new user input using context-based interrupt system

func (*Agent) InjectProactiveContext

func (a *Agent) InjectProactiveContext(ctx context.Context, query string) error

InjectProactiveContext retrieves semantically relevant past turns and injects them into the agent's system prompt supplement. This is called once per session — on the first turn or after a cold session restore.

Graceful degradation: all errors are logged; the agent is never blocked.

func (*Agent) InjectSemanticRecall added in v0.16.4

func (a *Agent) InjectSemanticRecall(ctx context.Context, query string)

InjectSemanticRecall runs recall over the current user query and appends the formatted block (if any) to the pending system supplement. Mirrors InjectProactiveContext — same shape, graceful degradation on every failure mode (no embedding manager, no store, embed failure, etc).

func (*Agent) InjectWebUIManagers

func (a *Agent) InjectWebUIManagers(approvalMgr *security.ApprovalManager, askUserMgr *tools.AskUserManager)

InjectWebUIManagers replaces the agent's internal approval and ask-user managers with the webui-owned instances. This is called after the web server is constructed so that security prompts and ask_user requests created by the agent are routed through the same manager that the webui handlers resolve responses on — eliminating the need for global singletons.

func (*Agent) InterruptCtx

func (a *Agent) InterruptCtx() context.Context

InterruptCtx returns the agent's interrupt context so child operations (e.g., tool execution) can derive from it and respect user cancellations.

func (*Agent) IsAppAllowedForComputerUse added in v0.16.19

func (a *Agent) IsAppAllowedForComputerUse(key string) bool

IsAppAllowedForComputerUse reports whether the given app key is in the per-session allowlist. The key is a bundle ID (macOS) or a window class (Linux). Guarded by computerUseMu.

func (*Agent) IsChangeTrackingEnabled

func (a *Agent) IsChangeTrackingEnabled() bool

IsChangeTrackingEnabled returns whether change tracking is enabled

func (*Agent) IsDebugMode

func (a *Agent) IsDebugMode() bool

IsDebugMode returns whether debug mode is enabled

func (*Agent) IsEmbeddingIndexEnabled

func (a *Agent) IsEmbeddingIndexEnabled() bool

IsEmbeddingIndexEnabled returns whether the embedding index is currently active.

func (*Agent) IsFolderSessionAllowed

func (a *Agent) IsFolderSessionAllowed(absPath string) bool

IsFolderSessionAllowed reports whether absPath sits under a folder the user has allowlisted via "Allow this folder for the rest of the session" on the filesystem approval dialog.

func (*Agent) IsInteractiveMode

func (a *Agent) IsInteractiveMode() bool

IsInteractiveMode returns true if running in interactive mode

func (*Agent) IsInterrupted

func (a *Agent) IsInterrupted() bool

IsInterrupted returns true if an interrupt has been requested

func (*Agent) IsLocalMode

func (a *Agent) IsLocalMode() bool

IsLocalMode returns true when the agent is running locally (CLI or local WebUI), not in a cloud environment. This controls whether LocalOnly personas (like the Executive Assistant) are available.

Cloud mode is detected via the SPROUT_CLOUD environment variable. Local mode is the default when the variable is unset or empty.

func (*Agent) IsPathOutsideWorkspace added in v0.16.12

func (a *Agent) IsPathOutsideWorkspace(path string) bool

IsPathOutsideWorkspace reports whether the resolved absolute path of `path` falls outside the agent's workspace root. Returns false (i.e. treats the path as in-workspace) when the change tracker is nil or disabled, mirroring the existing nil-agent / empty-root behaviour of ChangeTracker.isOutsideWorkspace. This is the boundary guard shared by recover_file / revert_my_changes so a crafted tracker entry can't trick the recovery tools into writing (or deleting) files outside the workspace.

func (*Agent) IsQueryInProgress added in v0.16.17

func (a *Agent) IsQueryInProgress() bool

IsQueryInProgress reports whether a query is currently executing on this Agent. Used by the WebUI to report busy state and by the CLI to check before starting a new query.

func (*Agent) IsSecurityBypassApproved

func (a *Agent) IsSecurityBypassApproved() bool

IsSecurityBypassApproved returns whether the user has approved any external filesystem access this session. Coarse signal: prefer the per-path IsFolderSessionAllowed for new code.

func (*Agent) IsSessionElevated

func (a *Agent) IsSessionElevated() bool

IsSessionElevated reports whether the user has elevated the session to a permissive or unrestricted risk profile. When true, all three security gates (static classifier, filesystem tier, shell risk cascade) must skip their interactive prompts and auto-approve — the user explicitly opted out of per-operation prompts for this session. Critical-tier operations (rm -rf /, fork bombs) are NOT covered by elevation and always block regardless.

func (*Agent) IsShellCommandAllowlisted

func (a *Agent) IsShellCommandAllowlisted(command string) bool

IsShellCommandAllowlisted reports whether the user has previously chosen "Always approve this command" for this exact command string (literal match) or whether any user-defined glob pattern in ApprovedShellCommandPatterns matches the command. Pattern matching uses Go's path.Match glob syntax: `*` (any non-`/` sequence), `?` (single char), `[abc]` (char class).

Caveat: while `*` does not match `/`, character classes can — e.g. `[^a-z]` or `[/.]` will match `/`. This is NOT a security hole because the Critical tier still blocks regardless of both literal and pattern matches; this short-circuit only applies to the High-risk persona-cascade gate. Critical-tier enforcement happens at the call site before this function is consulted (see risk_prompt.go and tool_security.go), so no pattern can bypass a hard-blocked command.

func (*Agent) IsStreamingEnabled

func (a *Agent) IsStreamingEnabled() bool

IsStreamingEnabled returns whether streaming is enabled

func (*Agent) IsSubagent

func (a *Agent) IsSubagent() bool

IsSubagent returns true if this agent was spawned as a subagent (depth > 0). Used to prevent nested subagent spawning and skip interactive prompts.

func (*Agent) IsWakeupDisabled added in v0.16.19

func (a *Agent) IsWakeupDisabled() bool

func (*Agent) IsWorkflowApprovedInSession added in v0.16.4

func (a *Agent) IsWorkflowApprovedInSession(workflow string) bool

IsWorkflowApprovedInSession reports whether the user has already approved running this workflow during the current chat session. The cache is scoped per-agent and is reset whenever the agent is reinitialized.

func (*Agent) ListChanges

func (a *Agent) ListChanges(args map[string]interface{}) (string, error)

ListChanges returns the session manifest. args may include "since" (RFC3339), "tool", "path_pattern". Returns the raw JSON string identical to what the LLM tool produces.

func (*Agent) LoadState

func (a *Agent) LoadState(sessionID string) (*ConversationState, error)

LoadState loads a conversation state by session ID

func (*Agent) LoadStateFromFile

func (a *Agent) LoadStateFromFile(filename string) error

LoadStateFromFile loads agent state from a file

func (*Agent) LoadStateScoped

func (a *Agent) LoadStateScoped(sessionID, workingDir string) (*ConversationState, error)

LoadStateScoped loads a conversation state by session ID within a specific working directory scope.

func (*Agent) LoadSummaryFromFile

func (a *Agent) LoadSummaryFromFile(filename string) error

LoadSummaryFromFile loads ONLY the compact summary from a state file for minimal continuity

func (*Agent) LogToolCall

func (a *Agent) LogToolCall(tc api.ToolCall, phase string)

LogToolCall appends a JSON line describing a tool call to a local file for quick debugging. File: ./tool_calls.log (in the current working directory)

func (*Agent) Logger

func (a *Agent) Logger() *AgentLogger

Logger returns the agent logger, initializing it lazily if needed

func (*Agent) MarkEstimatedTokenUsageResponse

func (a *Agent) MarkEstimatedTokenUsageResponse()

MarkEstimatedTokenUsageResponse records that token usage for one response was estimated.

func (*Agent) MarkWorkflowApprovedInSession added in v0.16.4

func (a *Agent) MarkWorkflowApprovedInSession(workflow string)

MarkWorkflowApprovedInSession records that the user has approved this workflow for the remainder of the chat session. Called by the security gate after a successful interactive approval, and by handleRunAutomate after a CLI-side confirmation path.

func (*Agent) MaxSubagentDepth

func (a *Agent) MaxSubagentDepth() int

MaxSubagentDepth returns the configured maximum nesting depth. EA root gets 3 levels (max depth 2), non-EA root gets 2 levels (max depth 1).

func (*Agent) MergeEventMetadata

func (a *Agent) MergeEventMetadata(extras map[string]interface{})

MergeEventMetadata adds extras to the current event metadata without discarding existing keys. Unlike SetEventMetadata, which replaces the map wholesale, this is the right call when a subagent needs to layer per-spawn fields (e.g. subagent_depth, active_persona) on top of already-set chat/client routing keys inherited from its parent.

func (*Agent) MergeSubagentChanges added in v0.16.12

func (a *Agent) MergeSubagentChanges(changes []TrackedFileChange, persona string)

MergeSubagentChanges merges a completed subagent's tracked changes into this (primary) agent's ChangeTracker, tagging each entry with "subagent:<persona>". This is the missing SP-059 Phase 2c step: without it, list_changes / recover_file / revert_my_changes are blind to subagent edits.

No-op when the primary's tracking is disabled. The changes slice is sourced from SubagentResult.FileChanges.

func (*Agent) MyRecentChanges

func (a *Agent) MyRecentChanges(since string) (string, error)

MyRecentChanges returns the cross-session timeline. Thin wrapper around list_changes(include_persisted=true, include_cross_session=true, since=…).

func (*Agent) NavigateHistory

func (a *Agent) NavigateHistory(direction int, currentIndex int) (string, int)

NavigateHistory navigates through command history direction: 1 for up (older), -1 for down (newer) currentIndex: current position in the input line

func (*Agent) NotifyCompletion added in v0.16.19

func (a *Agent) NotifyCompletion(sessionID, kind, content string)

func (*Agent) OutputRouter

func (a *Agent) OutputRouter() *OutputRouter

OutputRouter returns the current output router (nil if not initialized)

func (*Agent) PersistShellCommandAllowlist

func (a *Agent) PersistShellCommandAllowlist(command string) error

PersistShellCommandAllowlist appends command to the user's persistent approved-commands list (Config.ApprovedShellCommands) and saves to disk. Used by the "Always approve this command" choice on the approval dialog. Idempotent: re-adding an existing entry is a no-op but still triggers a save so the file's mtime updates (cheap).

func (*Agent) PersistShellCommandPattern added in v0.16.12

func (a *Agent) PersistShellCommandPattern(pattern string) error

PersistShellCommandPattern appends pattern to the user's persistent approved-command-pattern list (Config.ApprovedShellCommandPatterns) and saves to disk. Patterns use Go path.Match glob syntax (`*`, `?`, `[]`). Idempotent: re-adding an existing entry is a no-op but still triggers a save so the file's mtime updates (cheap).

func (*Agent) PrintCompactProgress

func (a *Agent) PrintCompactProgress()

PrintCompactProgress prints a minimal progress indicator for non-interactive mode Format: [iteration:(current-context-tokens/context-limit) | total-tokens | cost]

func (*Agent) PrintConciseSummary

func (a *Agent) PrintConciseSummary()

PrintConciseSummary displays a single line with essential token and cost information

func (*Agent) PrintConversationSummary

func (a *Agent) PrintConversationSummary(forceFull bool)

PrintConversationSummary displays a comprehensive conversation summary with formatting

func (*Agent) PrintLine

func (a *Agent) PrintLine(text string)

PrintLine prints a line of text to the console content area synchronously. It delegates to the internal renderer that handles streaming vs CLI output.

func (*Agent) PrintLineAsync

func (a *Agent) PrintLineAsync(text string)

PrintLineAsync enqueues a line for asynchronous output. Background goroutines (rate-limit handlers, streaming workers, etc.) should prefer this helper to avoid blocking on the UI mutex. If the queue is saturated, we fall back to bounded waiting and finally synchronous printing to avoid goroutine leaks while still preserving message ordering as much as possible.

func (*Agent) PrintTerminalOnly

func (a *Agent) PrintTerminalOnly(text string)

PrintTerminalOnly writes text to the terminal without publishing to the event bus. Use this for output already published via a more specific event type.

func (*Agent) ProcessQuery

func (a *Agent) ProcessQuery(userQuery string) (string, error)

ProcessQuery handles the main conversation loop with the LLM

func (*Agent) ProcessQueryWithContinuity

func (a *Agent) ProcessQueryWithContinuity(userQuery string) (string, error)

ProcessQueryWithContinuity processes a query with continuity from previous actions

func (*Agent) PromptChoice

func (a *Agent) PromptChoice(prompt string, choices []ChoiceOption) (string, error)

PromptChoice shows a dropdown selection of simple choices and returns the selected value

func (*Agent) PublishAgentMessage

func (a *Agent) PublishAgentMessage(category, message string, extra map[string]interface{})

func (*Agent) PublishBudgetUpdate added in v0.16.4

func (a *Agent) PublishBudgetUpdate(eventType string, data interface{})

PublishBudgetUpdate publishes a budget update event for automate sessions. This goes through decorateEventPayload to include client_id/chat_id metadata.

func (*Agent) PublishCompactCompleted added in v0.16.4

func (a *Agent) PublishCompactCompleted(source string, beforeCount, afterCount, summaryChars int, err error)

PublishCompactCompleted emits a compact_completed event with the result of the compaction. Pass nil err on success.

func (*Agent) PublishCompactStarted added in v0.16.4

func (a *Agent) PublishCompactStarted(source string, messageCount, checkpointCount int)

PublishCompactStarted emits a compact_started event with diagnostic fields describing the conversation state at the moment compaction begins. source is the path: "manual" (slash command) or "auto_llm_summary" (seed structural compaction).

func (*Agent) PublishContextManagementDiagnostic added in v0.16.4

func (a *Agent) PublishContextManagementDiagnostic(currentTokens, maxTokens, iteration, messageCount, cachedTokens, promptTokens, cacheWriteTokens int)

PublishContextManagementDiagnostic (SP-066 Phase 1) emits the per-iteration context-budget snapshot so the WebUI metrics panel can render the effective trigger threshold and verify substitution is doing the heavy lifting. cachedTokens/promptTokens/cacheWriteTokens expose provider cache effectiveness in the diagnostic payload.

func (*Agent) PublishFileChange

func (a *Agent) PublishFileChange(filePath, action, content string)

PublishAgentMessage publishes a structured agent system message event. This is the single unified routing point for all agent output. Safe to call even when eventBus is nil (CLI-only mode) — the internal publishEvent method checks for nil before publishing. PublishFileChange emits a file_changed event so the WebUI activity feed can reflect ChangeTracker-detected mutations (including shell-driven ones, not just direct write_file/edit_file calls). Content is the captured original (for deletes/edits) — pass empty for creates, where there's no prior content. Action: "created" / "modified" / "deleted" — matches events.FileChangedEvent vocabulary.

func (*Agent) PublishQueryProgress

func (a *Agent) PublishQueryProgress(message string, iteration int, tokensUsed int)

PublishQueryProgress publishes query progress for real-time updates

func (*Agent) PublishRateLimited added in v0.16.19

func (a *Agent) PublishRateLimited(ev *events.RateLimitedEvent)

PublishRateLimited emits a rate_limited event so the WebUI can show "rate-limited, retrying…" and gate the input until the backoff elapses.

func (*Agent) PublishRecallDiagnostic added in v0.16.4

func (a *Agent) PublishRecallDiagnostic(diag recallRetrievalDiagnostic)

PublishRecallDiagnostic (SP-066 Phase 3) emits a single semantic-recall pass diagnostic. Called from InjectSemanticRecall after every recall query (including no-op queries) so subscribers can see the full distribution of recall behavior, not just hits.

func (*Agent) PublishStreamChunk

func (a *Agent) PublishStreamChunk(chunk string, contentType string)

PublishStreamChunk publishes a streaming chunk for real-time updates

func (*Agent) PublishTodoUpdate

func (a *Agent) PublishTodoUpdate(todos []map[string]interface{})

PublishTodoUpdate publishes a structured todo update event

func (*Agent) PublishToolEnd

func (a *Agent) PublishToolEnd(toolCallID, toolName, status, result, errorMessage string, duration time.Duration)

PublishToolEnd publishes a rich tool end event

func (*Agent) PublishToolExecution

func (a *Agent) PublishToolExecution(toolName, action string, details map[string]interface{})

PublishToolExecution publishes tool execution events for real-time updates

func (*Agent) PublishToolStart

func (a *Agent) PublishToolStart(toolName, toolCallID, arguments, displayName, persona string, isSubagent bool, subagentType string, toolIndex int)

PublishToolStart publishes a rich tool start event

func (*Agent) QueueNotification added in v0.16.19

func (a *Agent) QueueNotification(n Notification)

func (*Agent) ReadFileContent

func (a *Agent) ReadFileContent(path string) (string, error)

ReadFileContent reads the content of a file from the workspace. The path is resolved relative to the agent's workspace root. Returns an error if the file does not exist or cannot be read.

func (*Agent) Recall added in v0.16.19

func (a *Agent) Recall(ctx context.Context, query string, limit int) ([]RecalledItem, error)

Recall runs the semantic-recall pipeline over the conversation store and returns the items worth surfacing, capped at `limit`. It is the pure-data sibling of InjectSemanticRecall: no formatting, no system-supplement mutation, no telemetry publish. Used by:

  • InjectSemanticRecall (the in-loop wrapper that adds formatting)
  • the future /recall CLI command (SP-092-2)
  • the future webui /api/recall endpoint (SP-092-3)

Returns (nil, nil) when:

  • the agent or its embedding manager is missing
  • the query is blank after trim
  • limit <= 0

`limit` replaces the hardcoded semanticRecallTopK (3) for the slice length; the same gating constants (recency decay, similarity threshold) still apply inside retrieveSemanticRecall.

func (*Agent) RecordErrorCategory added in v0.16.19

func (a *Agent) RecordErrorCategory(err error)

RecordErrorCategory emits a metrics event with the given error's category label, so the cost/status footer can show "rate-limited, retrying…" vs "provider error" vs generic.

func (*Agent) RecordFileReadThisTurn

func (a *Agent) RecordFileReadThisTurn(path string)

RecordFileReadThisTurn marks `path` as read by the agent during the current turn. Called from the read_file tool handler. Safe on a nil receiver so test scaffolding doesn't have to initialize the tracker.

func (*Agent) RecordTurnCheckpoint

func (a *Agent) RecordTurnCheckpoint(startIndex, endIndex int)

func (*Agent) RecordTurnCheckpointAsync

func (a *Agent) RecordTurnCheckpointAsync(startIndex, endIndex int)

func (*Agent) RecordWakeupTokens added in v0.16.19

func (a *Agent) RecordWakeupTokens(tokens int, cfg configuration.WakeupConfig)

func (*Agent) RecoverFile

func (a *Agent) RecoverFile(path string) (string, error)

RecoverFile restores one file from the tracker's session buffer. scope is forwarded as-is to handleRecoverFile so callers can request "latest" (default), "session_start", or "bulk".

func (*Agent) RefreshMCPTools

func (a *Agent) RefreshMCPTools() error

RefreshMCPTools refreshes the MCP tools cache

func (*Agent) RefreshRuntimeConfig added in v0.16.19

func (a *Agent) RefreshRuntimeConfig(ctx context.Context) error

RefreshRuntimeConfig reloads configuration from disk and reconciles the in-memory MCP server state so that servers added, removed, or modified through the webui settings API take effect without restarting the sprout process. This is the single entry point the webui calls after changing MCP servers or installing skills.

The context propagates cancellation from the caller (e.g. an HTTP request that the user closed). MCP server startup goroutines honor ctx.Done().

The method is safe to call concurrently with an active query — the MCP manager's own mutex protects AddServer/RemoveServer/ListServers and RefreshMCPTools uses the init mutex for cache invalidation. Concurrent RefreshRuntimeConfig calls are serialized via refreshMu.

func (*Agent) RefreshSkills added in v0.16.19

func (a *Agent) RefreshSkills() error

RefreshSkills reloads configuration from disk so that newly discovered skills (e.g., SKILL.md files dropped on disk) appear in list_skills without requiring a restart. This is called by the webui after skill installation.

func (*Agent) ReplaceTurnCheckpoints

func (a *Agent) ReplaceTurnCheckpoints(checkpoints []TurnCheckpoint)

func (*Agent) RequestApproval added in v0.16.18

func (a *Agent) RequestApproval(assessment RiskAssessment, toolName string, args map[string]interface{}) (BrokerDecision, error)

RequestApproval performs the unified approval flow for a RiskAssessment.

For Low-risk / no-prompt-needed assessments, returns early with (BrokerDecision{Approved: true, Assessment: assessment}, nil).

For Critical / IsHardBlock assessments, returns a SecurityError without consulting any approval surface (hard-blocks are unconditional).

For Medium/High/IntentConfirmation assessments:

  1. Checks fast-bypass paths (persistent allowlist, unsafe-mode, session elevation, unsafe-shell)
  2. Tries WebUI first if available
  3. Falls back to CLI (using AskForApprovalWithOptions for shell_command with 4-option cascade, or AskForConfirmation for other tools)
  4. For non-interactive with no surface: permissive auto-approve

It returns (BrokerDecision, error) — non-nil error means deny/hard-block, nil means approved (or auto-approved).

func (*Agent) RequestEditApproval added in v0.16.12

func (a *Agent) RequestEditApproval(ctx context.Context, p EditProposal) (applied string, summary string, err error)

RequestEditApproval builds a proposal, asks the approval broker for a decision, applies only accepted hunks, and returns the result.

Resolution path depends on the execution surface:

  1. Non-interactive (--skip-prompt, automate, daemon, non-TTY stdin): auto-approve all hunks. No one can answer a prompt, so blocking would dead-end the run.

  2. WebUI with active clients: publish an edit_approval_request event to the EventBus and block on a response channel. The browser renders a per-hunk diff review panel and POSTs the decision back. On timeout, fall through to the terminal path.

  3. CLI (TTY only, no WebUI): render the diff to stderr and prompt the user per-hunk via stdin. Accepted hunks are applied; rejected hunks keep the original lines.

func (*Agent) RequestShellApproval added in v0.16.19

func (a *Agent) RequestShellApproval(ctx context.Context, p ShellProposal) (map[string]bool, error)

RequestShellApproval asks the user (CLI or WebUI) to approve each part of the shell command individually. Returns a map from part ID to approved bool.

Flow:

  1. If no parts, returns empty map and nil error.
  2. If the WebUI has an active surface, dispatch via the security approval manager (for now, return a "all approved" map — SP-093-3 implements the real WebUI per-part dialog).
  3. Otherwise, call console.PromptShellApprovalParts (the CLI picker).

Errors come from the picker (e.g. context cancelled); a per-part rejection does NOT return an error — it's encoded in the decisions map.

func (*Agent) ResetComputerUseSessionApproval added in v0.16.18

func (a *Agent) ResetComputerUseSessionApproval()

ResetComputerUseSessionApproval clears the per-session computer-use opt-in flag (SP-063). Called from ClearSessionOverrides when a session ends so that the next session re-prompts the user for consent.

func (*Agent) ResetFileReadsForNewTurn

func (a *Agent) ResetFileReadsForNewTurn()

ResetFileReadsForNewTurn clears the per-turn read tracker. Called at turn boundaries so the staleness rule resets between turns: a file the agent read on turn N still needs a fresh read_file on turn N+1 before writing.

func (*Agent) ResetHistoryIndex

func (a *Agent) ResetHistoryIndex()

ResetHistoryIndex resets the history navigation index

func (*Agent) ResolveToolRisk added in v0.16.7

func (a *Agent) ResolveToolRisk(toolName string, args map[string]interface{}) RiskAssessment

ResolveToolRisk produces the unified, single-vocabulary assessment for a tool call by folding all available security inputs onto one Low/Medium/High/Critical scale. It incorporates:

  1. Static classifier (pkg/agent_tools.ClassifyToolCall)
  2. Persona / risk-profile cascade (Agent.EvaluateOperationRisk)
  3. Git history-rewrite gate (isGitHistoryRewriteCommand + AllowGitHistoryRewrite)
  4. Git write gate (isGitWriteCommand + isGitWriteAllowed)
  5. Filesystem path-tier (ClassifyPathAccess for file tools)
  6. Workspace security policy (SecurityPolicy.Evaluate for shell_command)

SP-068 Phase 2: this is the canonical risk view for gating when UnifiedRiskResolver is enabled. When the flag is off it still powers diagnostics (the gate's debug "[risk]" line and the future `sprout explain`) and shadow-mode logging.

func (*Agent) RespondToEditApproval added in v0.16.17

func (a *Agent) RespondToEditApproval(requestID string, decision EditDecision) bool

RespondToEditApproval delivers a user decision to a pending edit approval request. Called by the WebUI handler (POST /api/edits/{id}/decision) when the user submits their per-hunk accept/reject choices.

Returns true if the request was found and the decision was delivered.

func (*Agent) RespondToPasswordRequest added in v0.16.18

func (a *Agent) RespondToPasswordRequest(requestID string, password string) bool

RespondToPasswordRequest delivers a user password to a pending password request. Called by the WebUI handler (POST /api/password/{id}/respond) when the user submits their password.

Returns true if the request was found and the password was delivered.

func (*Agent) RestoreEmbeddingIndex

func (a *Agent) RestoreEmbeddingIndex()

RestoreEmbeddingIndex enables the workspace embedding index only when the user has opted in. Called once during agent startup after workspace root is known.

Embeddings are OPT-IN, not default-on. The index lazily loads a ~380MB ONNX model + in-memory HNSW store (it is ~80% of an agent's resident memory: ~486MB with it, ~103MB without — measured), and proactive-context runs it on every prompt. A fresh agent therefore stays lightweight unless semantic recall / duplicate detection is explicitly wanted. Enable it via any of:

  • workspace config `embedding_index.enabled: true` (set by /index or the UI toggle), or
  • env `SPROUT_ENABLE_EMBEDDING_AUTOINDEX=1` for default-on globally.

`SPROUT_DISABLE_EMBEDDING_AUTOINDEX=1` always wins and hard-disables (used by the test suites — see cmd/main_test.go and pkg/agent's TestMain).

Resolution order:

  1. SPROUT_DISABLE_EMBEDDING_AUTOINDEX=1 → skip (hard off).
  2. Workspace config embedding_index.enabled: true → enable (explicit opt-in).
  3. Workspace config embedding_index.enabled: false → skip (explicit opt-out).
  4. No section / no file / unreadable config → enable only if SPROUT_ENABLE_EMBEDDING_AUTOINDEX=1, else skip (lazy/opt-in default).

func (*Agent) RevertMyChanges

func (a *Agent) RevertMyChanges(scope, file, since string) (string, error)

RevertMyChanges performs a bulk revert. The historical file= scope is now served by recover_file(scope="session_start"); this method keeps the old four-arg signature for back-compat and routes file= there.

func (*Agent) Rewind added in v0.16.12

func (a *Agent) Rewind(opts RewindOptions) (*RewindResult, error)

Rewind truncates the agent's message history and checkpoints back to a prior turn, optionally reverting file changes made during the discarded turns. The operation is undoable via the package-level lastRewindSnapshot.

func (*Agent) RotateSession added in v0.16.25

func (a *Agent) RotateSession() (string, error)

RotateSession closes the current session as a complete, restorable unit (writing its final state to disk under the current SessionID), then assigns a new SessionID and clears in-memory conversation state. The previous session file remains loadable via LoadStateScoped. Returns the new session ID.

If the prior session's SaveStateScoped fails (e.g. invalid session ID or unwritable working directory), RotateSession returns that error WITHOUT rotating — the prior session must remain intact so the caller can retry.

func (*Agent) RunAutomateWorkflow added in v0.16.4

func (a *Agent) RunAutomateWorkflow(ctx context.Context, workflow string) (string, error)

RunAutomateWorkflow executes a named workflow and returns the JSON result. This is the public entry point for the WebUI automate API.

func (*Agent) SaveConversationSummary

func (a *Agent) SaveConversationSummary() error

SaveConversationSummary saves the conversation summary to the state file

func (*Agent) SaveState

func (a *Agent) SaveState(sessionID string) error

SaveState saves the current conversation state

func (*Agent) SaveStateScoped

func (a *Agent) SaveStateScoped(sessionID, workingDir string) error

SaveStateScoped saves conversation state under a directory-scoped session namespace.

func (*Agent) SaveStateToFile

func (a *Agent) SaveStateToFile(filename string) error

SaveStateToFile saves agent state to a file

func (*Agent) SelectProvider

func (a *Agent) SelectProvider() error

SelectProvider allows interactive provider selection

func (*Agent) SetAuditLogger added in v0.16.12

func (a *Agent) SetAuditLogger(l *tools.AuditLogger)

SetAuditLogger attaches a security audit logger to this agent. Also sets the package-level logger in pkg/agent_tools (via tools.SetAuditLogger) so that ClassifyToolCall entries are written through the same file. Pass nil to disable audit logging.

func (*Agent) SetBackgroundProcessManager

func (a *Agent) SetBackgroundProcessManager(bpm *tools.BackgroundProcessManager)

SetBackgroundProcessManager sets the background process manager for CLI mode. When set, shell commands can run in background without PTY (os/exec).

func (*Agent) SetBaseSystemPrompt

func (a *Agent) SetBaseSystemPrompt(prompt string)

SetBaseSystemPrompt updates the baseline prompt used when persona overrides are cleared.

func (*Agent) SetBudgetExceededCallback added in v0.16.4

func (a *Agent) SetBudgetExceededCallback(fn func(spent, limit float64))

SetBudgetExceededCallback registers a function invoked when the USD budget is first reached or surpassed. Pass nil to unregister.

func (*Agent) SetBudgetWarningCallback added in v0.16.4

func (a *Agent) SetBudgetWarningCallback(fn func(threshold, spent, limit float64))

SetBudgetWarningCallback registers a function invoked when the USD budget first crosses each configured warning threshold (fired at most once per threshold). Pass nil to unregister.

func (*Agent) SetConfigOverrides

func (a *Agent) SetConfigOverrides(overrides map[string]interface{})

SetConfigOverrides stores session-scoped config overrides on the agent. These are applied in-memory and persisted with the session state.

func (*Agent) SetConversationOptimization

func (a *Agent) SetConversationOptimization(enabled bool)

SetConversationOptimization enables or disables conversation optimization

func (*Agent) SetElevationGatePrompter

func (a *Agent) SetElevationGatePrompter()

SetElevationGatePrompter wires the agent's interactive UI into the elevation gate. Call this after agent.ui is initialized (done automatically by SetUI).

func (*Agent) SetEventBus

func (a *Agent) SetEventBus(eventBus *events.EventBus)

SetEventBus sets the event bus for real-time UI updates and initializes the validator

func (*Agent) SetEventMetadata

func (a *Agent) SetEventMetadata(metadata map[string]interface{})

SetEventMetadata attaches metadata that should be merged into all emitted UI events.

func (*Agent) SetFileMetadata

func (a *Agent) SetFileMetadata(path string, md WorkspaceFileMetadata)

SetFileMetadata replaces the cached sync metadata for `path`. Called by the platform-side sync bridge whenever it learns about a new browser sequence or last-synced acknowledgement. Safe to call before the agent is otherwise initialized.

func (*Agent) SetFleetBudget

func (a *Agent) SetFleetBudget(tracker *atomic.Int64, limit int64)

SetFleetBudget enables per-LLM-call fleet budget tracking for this agent. When tracker is non-nil and limit > 0, each LLM call will debit its token usage to the shared tracker. If the budget is exceeded, fleetBudgetTrunc is set and the conversation loop will truncate gracefully.

func (*Agent) SetFleetUsdBudget added in v0.16.4

func (a *Agent) SetFleetUsdBudget(b *FleetUsdBudget)

SetFleetUsdBudget attaches a shared USD budget to this agent. The budget is shared by reference, so all agents (primary + subagents) that hold the same pointer debit to the same counter.

func (*Agent) SetFlushCallback

func (a *Agent) SetFlushCallback(callback func())

SetFlushCallback sets a callback to flush buffered output

func (*Agent) SetHasActiveWebUIClients

func (a *Agent) SetHasActiveWebUIClients(fn func() bool)

SetHasActiveWebUIClients sets a callback that returns whether any WebUI clients are currently connected. The security prompting logic uses this to decide between WebUI event-bus routing and CLI-based prompting.

func (*Agent) SetInterruptHandler

func (a *Agent) SetInterruptHandler(ch chan struct{})

SetInterruptHandler sets the interrupt handler for UI mode

func (*Agent) SetLastPreparedToolNames

func (a *Agent) SetLastPreparedToolNames(tools []api.Tool)

SetLastPreparedToolNames records the exact tool names prepared for the most recent model request.

func (*Agent) SetMaxIterations

func (a *Agent) SetMaxIterations(max int)

SetMaxIterations sets the maximum number of iterations for the agent. A value of 0 means unlimited (no iteration cap per prompt). Negative values are clamped to 0 (unlimited).

func (*Agent) SetMessages

func (a *Agent) SetMessages(messages []api.Message)

SetMessages sets the conversation messages (for restore)

func (*Agent) SetModel

func (a *Agent) SetModel(model string) error

SetModel changes the current model for the session. This is the session-scoped version that doesn't persist to config. For CLI use with persistence, use SetModelPersisted.

func (*Agent) SetModelPersisted

func (a *Agent) SetModelPersisted(model string) error

SetModelPersisted changes the current model and persists the choice to config. This is intended for CLI use where the selection should be saved.

func (*Agent) SetOutputMutex

func (a *Agent) SetOutputMutex(mutex *sync.Mutex)

SetOutputMutex sets the output mutex for synchronized output

func (*Agent) SetPasswordPrompter added in v0.16.18

func (a *Agent) SetPasswordPrompter(pp tools.PasswordPrompter)

SetPasswordPrompter registers a password prompter for shell commands. When set, privileged commands (sudo, passwd) are allowed to run with password assistance instead of being hard-blocked. Pass nil to disable.

func (*Agent) SetPreviousSummary

func (a *Agent) SetPreviousSummary(summary string)

SetPreviousSummary sets the summary of previous actions for continuity

func (*Agent) SetProvider

func (a *Agent) SetProvider(provider api.ClientType) error

SetProvider switches to a specific provider with its default or current model. Session-scoped: changes are not written to config. Use SetProviderPersisted when the user explicitly chose the provider (e.g. CLI /provider command).

func (*Agent) SetProviderPersisted

func (a *Agent) SetProviderPersisted(provider api.ClientType) error

SetProviderPersisted switches to a specific provider and persists the choice to config. This is intended for CLI use where the selection should be saved. The test/mock provider is rejected since it should never be the persisted default.

func (*Agent) SetPruningSlidingWindowSize

func (a *Agent) SetPruningSlidingWindowSize(size int)

SetPruningSlidingWindowSize sets the sliding window size for the sliding window strategy

func (*Agent) SetPruningStrategy

func (a *Agent) SetPruningStrategy(strategy PruningStrategy)

SetPruningStrategy sets the conversation pruning strategy

func (*Agent) SetPruningThreshold

func (a *Agent) SetPruningThreshold(threshold float64)

SetPruningThreshold sets the context usage threshold for triggering automatic pruning threshold should be between 0 and 1 (e.g., 0.7 = 70%)

func (*Agent) SetRecentMessagesToKeep

func (a *Agent) SetRecentMessagesToKeep(count int)

SetRecentMessagesToKeep sets how many recent messages to always preserve

func (*Agent) SetRiskProfileOverride

func (a *Agent) SetRiskProfileOverride(profile configuration.RiskProfile)

SetRiskProfileOverride installs a transient risk profile that overrides the config-level setting for the lifetime of this agent. Used by the --risk-profile CLI flag and per-step workflow overrides. Pass "" to clear.

func (*Agent) SetSessionID

func (a *Agent) SetSessionID(sessionID string)

SetSessionID sets the session identifier for continuity

func (*Agent) SetSessionName

func (a *Agent) SetSessionName(name string)

SetSessionName explicitly sets a custom name for the current session

func (*Agent) SetShellCommandHistoryEntry

func (a *Agent) SetShellCommandHistoryEntry(command string, result *ShellCommandResult)

SetShellCommandHistoryEntry stores a shell command result in history

func (*Agent) SetShellCwd

func (a *Agent) SetShellCwd(dir string)

SetShellCwd sets the logical shell working directory and records the previous.

func (*Agent) SetStatsUpdateCallback

func (a *Agent) SetStatsUpdateCallback(callback func(int, float64))

SetStatsUpdateCallback sets a callback for token/cost updates

func (*Agent) SetStreamingCallback

func (a *Agent) SetStreamingCallback(callback func(string))

SetStreamingCallback sets a custom callback for streaming output

func (*Agent) SetStreamingEnabled

func (a *Agent) SetStreamingEnabled(enabled bool)

SetStreamingEnabled enables or disables streaming responses

func (*Agent) SetSystemPrompt

func (a *Agent) SetSystemPrompt(prompt string)

SetSystemPrompt sets the system prompt for the agent

func (*Agent) SetSystemPromptFromFile

func (a *Agent) SetSystemPromptFromFile(filePath string) error

SetSystemPromptFromFile loads a custom system prompt from a file

func (*Agent) SetTerminalManager

func (a *Agent) SetTerminalManager(tm tools.TerminalAccess)

SetTerminalManager sets the terminal manager for WebUI mode. When set (non-nil), shell commands can access hidden PTY sessions. When nil (CLI mode), shell commands use os/exec unchanged.

func (*Agent) SetTraceSession

func (a *Agent) SetTraceSession(traceSession interface{})

SetTraceSession sets the trace session for dataset collection

func (*Agent) SetUI

func (a *Agent) SetUI(ui UI)

SetUI sets the UI provider for the agent

func (*Agent) SetUnsafeMode

func (a *Agent) SetUnsafeMode(unsafe bool)

SetUnsafeMode sets the unsafe mode flag

func (*Agent) SetUnsafeShellMode added in v0.16.12

func (a *Agent) SetUnsafeShellMode(unsafe bool)

SetUnsafeShellMode sets the unsafe shell mode flag

func (*Agent) SetWorkspaceRoot

func (a *Agent) SetWorkspaceRoot(workspaceRoot string)

SetWorkspaceRoot records the logical workspace root for this agent instance.

func (*Agent) ShouldGateEdit added in v0.16.12

func (a *Agent) ShouldGateEdit(path string) bool

ShouldGateEdit reports whether a write to the given path should be routed through the diff-approval gate based on the agent's config. Returns false when edit_approval mode is "off" (default), when the run is non-interactive (--skip-prompt / daemon / automate), or when mode is "paths" and the path doesn't match any configured glob.

func (*Agent) ShowColoredDiff

func (a *Agent) ShowColoredDiff(oldContent, newContent string, maxLines int)

ShowColoredDiff displays a colored diff between old and new content, focusing on actual changes Uses Python's difflib for better diff quality when available, falls back to Go implementation

func (*Agent) ShowDropdown

func (a *Agent) ShowDropdown(items interface{}, options DropdownOptions) (interface{}, error)

ShowDropdown shows a dropdown if UI is available

func (*Agent) ShowMyChange

func (a *Agent) ShowMyChange(path string) (string, error)

ShowMyChange returns a unified diff JSON envelope for `path`. After the SP-061-2 consolidation this is a thin wrapper around list_changes(include_diff=true, path_pattern=path) — the standalone show_my_change tool is gone.

func (*Agent) ShowQuickPrompt

func (a *Agent) ShowQuickPrompt(prompt string, options []QuickOption, horizontal bool) (QuickOption, error)

ShowQuickPrompt shows a quick prompt if UI is available

func (*Agent) Shutdown

func (a *Agent) Shutdown()

Shutdown attempts to gracefully stop background work and child processes (e.g., MCP servers), and releases resources. It is safe to call multiple times.

func (*Agent) SnapshotSessionAllowedFolders

func (a *Agent) SnapshotSessionAllowedFolders() []string

SnapshotSessionAllowedFolders returns a copy of the session allowlist. Used by SubagentRunner to seed a new subagent's allowlist from the parent (so previously approved folders remain usable inside delegated work).

func (*Agent) SteeringChannel added in v0.16.19

func (a *Agent) SteeringChannel() <-chan string

SteeringChannel returns the receive-only input channel that steer/queue messages are delivered to. This is the same channel used by InjectInputContext — it is the "user typed something while a turn is running, queue it for the next user-prompted turn" semantics (SP-055).

Subagent plumbing consults this channel FIRST before falling back to its own input channel: if the parent has a steering channel, deliver to the parent, not the subagent (SP-094-8).

func (*Agent) SubagentDepth

func (a *Agent) SubagentDepth() int

SubagentDepth returns the nesting depth of this agent. 0 = primary agent (EA), 1 = orchestrator, 2 = coder/tester, etc.

func (*Agent) SummarizeMySession

func (a *Agent) SummarizeMySession() (string, error)

SummarizeMySession returns the activity-block digest. Thin wrapper around list_changes(group_by="block").

func (*Agent) SummarizeViaLLM added in v0.16.4

func (a *Agent) SummarizeViaLLM(ctx context.Context, messages []api.Message, hint core.SummarizerHint) (string, error)

SummarizeViaLLM produces a real LLM-generated recap of the supplied message window, using the agent's bound LLM client. Returns the summary body — callers are responsible for wrapping it with the "Compacted earlier conversation state:" header before splicing it back into the message list. Used by `/compact` so the user-facing command does an actual recap instead of substituting pre-baked rule-based heuristic text.

func (*Agent) ToolLog

func (a *Agent) ToolLog(action string, target string)

ToolLog formats and prints a tool call message immediately for user visibility. Routes through OutputRouter for single-sourced event+terminal output. Format: [4 - 30%] read file filename.go

func (*Agent) TrackFileEdit

func (a *Agent) TrackFileEdit(filePath string, originalContent string, newContent string) error

TrackFileEdit is called by the EditFile tool to track file edits

func (*Agent) TrackFileWrite

func (a *Agent) TrackFileWrite(filePath string, content string) error

TrackFileWrite is called by the WriteFile tool to track file writes

func (*Agent) TrackMetricsFromResponse

func (a *Agent) TrackMetricsFromResponse(promptTokens, completionTokens, totalTokens int, estimatedCost float64, cachedTokens, cacheWriteTokens int)

TrackMetricsFromResponse updates agent metrics from API response usage data. cacheWriteTokens is the number of prompt tokens written to the provider cache on this request (Anthropic/OpenRouter cache_creation_input_tokens). Pass 0 when the provider does not report write tokens.

func (*Agent) TriggerInterrupt

func (a *Agent) TriggerInterrupt()

TriggerInterrupt manually triggers an interrupt for testing purposes

func (*Agent) TryBeginQuery added in v0.16.17

func (a *Agent) TryBeginQuery() error

TryBeginQuery attempts to mark this Agent as "query in progress." Returns ErrQueryInProgress if a query is already running on this Agent instance. The caller MUST call EndQuery when done (typically via defer) to release the flag.

This is the concurrency guard for shared-agent mode: when the CLI REPL and the WebUI use the same *Agent (non-daemon interactive mode), only one ProcessQuery can execute at a time. The losing caller gets the error and must either retry or present a "busy" message to the user.

For standalone daemon mode (separate agents per chat session) this flag is never contended because each chat has its own Agent, so it's effectively a no-op.

type AgentLogger

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

AgentLogger wraps the agent and provides context-aware logging

func NewAgentLogger

func NewAgentLogger(agent *Agent) *AgentLogger

NewAgentLogger creates a logger, using the agent's existing debug log file if available

func (*AgentLogger) Debug

func (l *AgentLogger) Debug(format string, args ...interface{})

Debug writes a debug-level log with context

func (*AgentLogger) Error

func (l *AgentLogger) Error(format string, args ...interface{})

Error writes an error-level log with context

func (*AgentLogger) Info

func (l *AgentLogger) Info(format string, args ...interface{})

Info writes an info-level log with context

func (*AgentLogger) SetJSONMode

func (l *AgentLogger) SetJSONMode(jsonMode bool)

SetJSONMode sets whether the logger outputs JSON or human-readable text

func (*AgentLogger) Warn

func (l *AgentLogger) Warn(format string, args ...interface{})

Warn writes a warn-level log with context

func (*AgentLogger) WithFields

func (l *AgentLogger) WithFields(fields map[string]string) *LogContext

WithFields returns a context that adds extra fields to all subsequent logs

type AgentMCPManager

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

AgentMCPManager implements MCPSubManager.

func NewAgentMCPManager

func NewAgentMCPManager() *AgentMCPManager

NewAgentMCPManager creates a new AgentMCPManager with default values.

func (*AgentMCPManager) GetInitError

func (m *AgentMCPManager) GetInitError() error

func (*AgentMCPManager) GetManager

func (m *AgentMCPManager) GetManager() mcp.MCPManager

func (*AgentMCPManager) GetToolsCache

func (m *AgentMCPManager) GetToolsCache() []api.Tool

func (*AgentMCPManager) IsInitialized

func (m *AgentMCPManager) IsInitialized() bool

func (*AgentMCPManager) LockInit

func (m *AgentMCPManager) LockInit()

func (*AgentMCPManager) SetInitError

func (m *AgentMCPManager) SetInitError(err error)

func (*AgentMCPManager) SetInitialized

func (m *AgentMCPManager) SetInitialized(initialized bool)

func (*AgentMCPManager) SetManager

func (m *AgentMCPManager) SetManager(mgr mcp.MCPManager)

func (*AgentMCPManager) SetToolsCache

func (m *AgentMCPManager) SetToolsCache(tools []api.Tool)

func (*AgentMCPManager) UnlockInit

func (m *AgentMCPManager) UnlockInit()

type AgentOutputManager

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

AgentOutputManager implements OutputManager.

func NewAgentOutputManager

func NewAgentOutputManager() *AgentOutputManager

NewAgentOutputManager creates a new AgentOutputManager with default values.

func (*AgentOutputManager) EnsureAsyncOutputWorker

func (m *AgentOutputManager) EnsureAsyncOutputWorker(fn func())

func (*AgentOutputManager) GetAsyncBufferSize

func (m *AgentOutputManager) GetAsyncBufferSize() int

func (*AgentOutputManager) GetAsyncOutput

func (m *AgentOutputManager) GetAsyncOutput() chan string

func (*AgentOutputManager) GetEventMetadata

func (m *AgentOutputManager) GetEventMetadata() map[string]interface{}

func (*AgentOutputManager) GetEventMetadataMutex

func (m *AgentOutputManager) GetEventMetadataMutex() *sync.RWMutex

func (*AgentOutputManager) GetFlushCallback

func (m *AgentOutputManager) GetFlushCallback() func()

func (*AgentOutputManager) GetOutputMutex

func (m *AgentOutputManager) GetOutputMutex() *sync.Mutex

func (*AgentOutputManager) GetOutputRouter

func (m *AgentOutputManager) GetOutputRouter() *OutputRouter

func (*AgentOutputManager) GetReasoningBuffer

func (m *AgentOutputManager) GetReasoningBuffer() *strings.Builder

func (*AgentOutputManager) GetReasoningCallback

func (m *AgentOutputManager) GetReasoningCallback() func(string)

func (*AgentOutputManager) GetStreamingBuffer

func (m *AgentOutputManager) GetStreamingBuffer() *strings.Builder

func (*AgentOutputManager) GetStreamingCallback

func (m *AgentOutputManager) GetStreamingCallback() func(string)

func (*AgentOutputManager) GetTerminalWriter

func (m *AgentOutputManager) GetTerminalWriter() func(string)

func (*AgentOutputManager) IsStreamingEnabled

func (m *AgentOutputManager) IsStreamingEnabled() bool

func (*AgentOutputManager) SetAsyncBufferSize

func (m *AgentOutputManager) SetAsyncBufferSize(size int)

func (*AgentOutputManager) SetAsyncOutput

func (m *AgentOutputManager) SetAsyncOutput(ch chan string)

func (*AgentOutputManager) SetEventMetadata

func (m *AgentOutputManager) SetEventMetadata(meta map[string]interface{})

func (*AgentOutputManager) SetEventMetadataUnlocked

func (m *AgentOutputManager) SetEventMetadataUnlocked(meta map[string]interface{})

SetEventMetadataUnlocked sets metadata without acquiring the mutex. Caller must hold m.eventMetadataMu.

func (*AgentOutputManager) SetFlushCallback

func (m *AgentOutputManager) SetFlushCallback(cb func())

func (*AgentOutputManager) SetOutputMutex

func (m *AgentOutputManager) SetOutputMutex(mu *sync.Mutex)

func (*AgentOutputManager) SetOutputRouter

func (m *AgentOutputManager) SetOutputRouter(router *OutputRouter)

func (*AgentOutputManager) SetReasoningCallback

func (m *AgentOutputManager) SetReasoningCallback(cb func(string))

func (*AgentOutputManager) SetStreamingCallback

func (m *AgentOutputManager) SetStreamingCallback(cb func(string))

func (*AgentOutputManager) SetStreamingEnabled

func (m *AgentOutputManager) SetStreamingEnabled(enabled bool)

func (*AgentOutputManager) SetTerminalWriter

func (m *AgentOutputManager) SetTerminalWriter(fn func(string))

type AgentSecurityManager

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

AgentSecurityManager implements SecurityManager, holding all security-related state previously managed directly by the Agent struct.

func NewAgentSecurityManager

func NewAgentSecurityManager() *AgentSecurityManager

NewAgentSecurityManager creates a new AgentSecurityManager with all fields initialized.

func (*AgentSecurityManager) AddSessionAllowedFolder

func (m *AgentSecurityManager) AddSessionAllowedFolder(folder string)

func (*AgentSecurityManager) GetAskUserMgr

func (m *AgentSecurityManager) GetAskUserMgr() *agenttools.AskUserManager

func (*AgentSecurityManager) GetElevationGate

func (m *AgentSecurityManager) GetElevationGate() *security.ElevationGate

func (*AgentSecurityManager) GetOutputRedactor

func (m *AgentSecurityManager) GetOutputRedactor() *security.OutputRedactor

func (*AgentSecurityManager) GetSecurityApprovalMgr

func (m *AgentSecurityManager) GetSecurityApprovalMgr() *security.ApprovalManager

func (*AgentSecurityManager) GetUnsafeMode

func (m *AgentSecurityManager) GetUnsafeMode() bool

func (*AgentSecurityManager) GetUnsafeShellMode added in v0.16.12

func (m *AgentSecurityManager) GetUnsafeShellMode() bool

func (*AgentSecurityManager) HasActiveWebUIClients

func (m *AgentSecurityManager) HasActiveWebUIClients() bool

func (*AgentSecurityManager) IsConcernIgnored

func (m *AgentSecurityManager) IsConcernIgnored(filePath, concern string) bool

func (*AgentSecurityManager) IsFolderSessionAllowed

func (m *AgentSecurityManager) IsFolderSessionAllowed(absPath string) bool

func (*AgentSecurityManager) IsSecurityBypassApproved

func (m *AgentSecurityManager) IsSecurityBypassApproved() bool

func (*AgentSecurityManager) SetApprovalMgr

func (m *AgentSecurityManager) SetApprovalMgr(mgr *security.ApprovalManager)

func (*AgentSecurityManager) SetAskUserMgr

func (m *AgentSecurityManager) SetAskUserMgr(mgr *agenttools.AskUserManager)

func (*AgentSecurityManager) SetConcernIgnored

func (m *AgentSecurityManager) SetConcernIgnored(filePath, concern string)

func (*AgentSecurityManager) SetElevationGate

func (m *AgentSecurityManager) SetElevationGate(gate *security.ElevationGate)

func (*AgentSecurityManager) SetHasActiveWebUIClients

func (m *AgentSecurityManager) SetHasActiveWebUIClients(fn func() bool)

func (*AgentSecurityManager) SetUnsafeMode

func (m *AgentSecurityManager) SetUnsafeMode(unsafe bool)

func (*AgentSecurityManager) SetUnsafeShellMode added in v0.16.12

func (m *AgentSecurityManager) SetUnsafeShellMode(unsafe bool)

func (*AgentSecurityManager) SnapshotSessionAllowedFolders

func (m *AgentSecurityManager) SnapshotSessionAllowedFolders() []string

type AgentState

type AgentState struct {
	Messages          []api.Message    `json:"messages"`
	MessageTimestamps []time.Time      `json:"message_timestamps,omitempty"`
	TurnCheckpoints   []TurnCheckpoint `json:"turn_checkpoints,omitempty"`
	PreviousSummary   string           `json:"previous_summary"`
	CompactSummary    string           `json:"compact_summary"` // New: 5K limit summary for continuity
	TaskActions       []TaskAction     `json:"task_actions"`
	SessionID         string           `json:"session_id"`
	// Token and cost metrics
	TotalTokens             int     `json:"total_tokens"`
	TotalCost               float64 `json:"total_cost"`
	PromptTokens            int     `json:"prompt_tokens"`
	CompletionTokens        int     `json:"completion_tokens"`
	EstimatedTokenResponses int     `json:"estimated_token_responses"`
	CachedTokens            int     `json:"cached_tokens"`
	CacheWriteTokens        int     `json:"cache_write_tokens,omitempty"`
	CachedCostSavings       float64 `json:"cached_cost_savings"`
	// Billing-model-aware cost tracking (SP-080)
	ChargedCostTotal   float64 `json:"charged_cost_total,omitempty"`
	TokenCostTotal     float64 `json:"token_cost_total,omitempty"`
	SubscriptionTokens int     `json:"subscription_tokens,omitempty"`
	FreeTokens         int     `json:"free_tokens,omitempty"`
}

AgentState represents the state of an agent that can be persisted

type AgentStateManager

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

AgentStateManager implements StateManager with simple field-backed getters/setters.

func NewAgentStateManager

func NewAgentStateManager(debug bool) *AgentStateManager

NewAgentStateManager creates a new AgentStateManager with sensible defaults.

func (*AgentStateManager) AddCost

func (s *AgentStateManager) AddCost(c float64)

func (*AgentStateManager) AddCostEntry added in v0.16.19

func (s *AgentStateManager) AddCostEntry(entry CostEntry)

AddCostEntry routes a billing-aware cost entry to the correct counters. chargedCostTotal always mirrors totalCost for backward compatibility.

func (*AgentStateManager) AddMessage

func (s *AgentStateManager) AddMessage(msg api.Message)

func (*AgentStateManager) AddTaskAction

func (s *AgentStateManager) AddTaskAction(action TaskAction)

func (*AgentStateManager) AddTurnCheckpoint

func (s *AgentStateManager) AddTurnCheckpoint(cp TurnCheckpoint)

func (*AgentStateManager) GetActivePersona

func (s *AgentStateManager) GetActivePersona() string

func (*AgentStateManager) GetActiveSkills

func (s *AgentStateManager) GetActiveSkills() []string

func (*AgentStateManager) GetCacheWriteTokens added in v0.16.17

func (s *AgentStateManager) GetCacheWriteTokens() int

func (*AgentStateManager) GetCachedCostSavings

func (s *AgentStateManager) GetCachedCostSavings() float64

func (*AgentStateManager) GetCachedTokens

func (s *AgentStateManager) GetCachedTokens() int

func (*AgentStateManager) GetChargedCostTotal added in v0.16.19

func (s *AgentStateManager) GetChargedCostTotal() float64

func (*AgentStateManager) GetCheckpointMutex

func (s *AgentStateManager) GetCheckpointMutex() *sync.RWMutex

func (*AgentStateManager) GetCircuitBreaker

func (s *AgentStateManager) GetCircuitBreaker() *CircuitBreakerState

func (*AgentStateManager) GetCommandHistory

func (s *AgentStateManager) GetCommandHistory() []string

func (*AgentStateManager) GetCompletionTokens

func (s *AgentStateManager) GetCompletionTokens() int

func (*AgentStateManager) GetConfigOverrides

func (s *AgentStateManager) GetConfigOverrides() map[string]interface{}

func (*AgentStateManager) GetConversationPruner

func (s *AgentStateManager) GetConversationPruner() *ConversationPruner

func (*AgentStateManager) GetCurrentContextTokens

func (s *AgentStateManager) GetCurrentContextTokens() int

func (*AgentStateManager) GetCurrentIteration

func (s *AgentStateManager) GetCurrentIteration() int

func (*AgentStateManager) GetEstimatedTokenResponses

func (s *AgentStateManager) GetEstimatedTokenResponses() int

func (*AgentStateManager) GetFreeTokens added in v0.16.19

func (s *AgentStateManager) GetFreeTokens() int

func (*AgentStateManager) GetHistoryIndex

func (s *AgentStateManager) GetHistoryIndex() int

func (*AgentStateManager) GetHistoryMutex

func (s *AgentStateManager) GetHistoryMutex() *sync.Mutex

func (*AgentStateManager) GetLLMCallCount

func (s *AgentStateManager) GetLLMCallCount() int

func (*AgentStateManager) GetLastProviderError

func (s *AgentStateManager) GetLastProviderError() *ProviderErrorInfo

GetLastProviderError returns the last provider error info

func (*AgentStateManager) GetLastRunTerminationReason

func (s *AgentStateManager) GetLastRunTerminationReason() string

func (*AgentStateManager) GetMaxContextTokens

func (s *AgentStateManager) GetMaxContextTokens() int

func (*AgentStateManager) GetMessageTimestamps added in v0.16.25

func (s *AgentStateManager) GetMessageTimestamps() []time.Time

GetMessageTimestamps returns the creation timestamps for each message.

func (*AgentStateManager) GetMessages

func (s *AgentStateManager) GetMessages() []api.Message

func (*AgentStateManager) GetOptimizer

func (s *AgentStateManager) GetOptimizer() *ConversationOptimizer

func (*AgentStateManager) GetPauseMutex

func (s *AgentStateManager) GetPauseMutex() *sync.Mutex

func (*AgentStateManager) GetPauseState

func (s *AgentStateManager) GetPauseState() *PauseState

func (*AgentStateManager) GetPendingStrictSwitchNotice

func (s *AgentStateManager) GetPendingStrictSwitchNotice() string

func (*AgentStateManager) GetPendingSwitchContextRefresh

func (s *AgentStateManager) GetPendingSwitchContextRefresh() string

func (*AgentStateManager) GetPendingSystemSupplement

func (s *AgentStateManager) GetPendingSystemSupplement() string

func (*AgentStateManager) GetPreviousSummary

func (s *AgentStateManager) GetPreviousSummary() string

func (*AgentStateManager) GetPromptTokens

func (s *AgentStateManager) GetPromptTokens() int

func (*AgentStateManager) GetSessionID

func (s *AgentStateManager) GetSessionID() string

func (*AgentStateManager) GetSessionIntentEmbedding

func (s *AgentStateManager) GetSessionIntentEmbedding() []float32

func (*AgentStateManager) GetSessionModel

func (s *AgentStateManager) GetSessionModel() string

func (*AgentStateManager) GetSessionProvider

func (s *AgentStateManager) GetSessionProvider() api.ClientType

func (*AgentStateManager) GetSubscriptionTokens added in v0.16.19

func (s *AgentStateManager) GetSubscriptionTokens() int

func (*AgentStateManager) GetTaskActions

func (s *AgentStateManager) GetTaskActions() []TaskAction

func (*AgentStateManager) GetTaskActionsMutex

func (s *AgentStateManager) GetTaskActionsMutex() *sync.RWMutex

func (*AgentStateManager) GetTokenCostTotal added in v0.16.19

func (s *AgentStateManager) GetTokenCostTotal() float64

func (*AgentStateManager) GetTotalCost

func (s *AgentStateManager) GetTotalCost() float64

func (*AgentStateManager) GetTotalTokens

func (s *AgentStateManager) GetTotalTokens() int

func (*AgentStateManager) GetTotalToolCalls

func (s *AgentStateManager) GetTotalToolCalls() int

func (*AgentStateManager) GetTraceSession

func (s *AgentStateManager) GetTraceSession() interface{}

func (*AgentStateManager) GetTurnCheckpoints

func (s *AgentStateManager) GetTurnCheckpoints() []TurnCheckpoint

func (*AgentStateManager) IncrementLLMCallCount

func (s *AgentStateManager) IncrementLLMCallCount()

func (*AgentStateManager) IncrementTotalToolCalls

func (s *AgentStateManager) IncrementTotalToolCalls()

func (*AgentStateManager) IsContextWarningIssued

func (s *AgentStateManager) IsContextWarningIssued() bool

func (*AgentStateManager) IsFalseStopDetectionEnabled

func (s *AgentStateManager) IsFalseStopDetectionEnabled() bool

func (*AgentStateManager) IsToolCallGuidanceAdded

func (s *AgentStateManager) IsToolCallGuidanceAdded() bool

func (*AgentStateManager) SetActivePersona

func (s *AgentStateManager) SetActivePersona(p string)

func (*AgentStateManager) SetActiveSkills

func (s *AgentStateManager) SetActiveSkills(skills []string)

func (*AgentStateManager) SetCacheWriteTokens added in v0.16.17

func (s *AgentStateManager) SetCacheWriteTokens(n int)

func (*AgentStateManager) SetCachedCostSavings

func (s *AgentStateManager) SetCachedCostSavings(c float64)

func (*AgentStateManager) SetCachedTokens

func (s *AgentStateManager) SetCachedTokens(n int)

func (*AgentStateManager) SetChargedCostTotal added in v0.16.19

func (s *AgentStateManager) SetChargedCostTotal(v float64)

func (*AgentStateManager) SetCircuitBreaker

func (s *AgentStateManager) SetCircuitBreaker(cb *CircuitBreakerState)

func (*AgentStateManager) SetCommandHistory

func (s *AgentStateManager) SetCommandHistory(h []string)

func (*AgentStateManager) SetCompletionTokens

func (s *AgentStateManager) SetCompletionTokens(n int)

func (*AgentStateManager) SetConfigOverrides

func (s *AgentStateManager) SetConfigOverrides(overrides map[string]interface{})

func (*AgentStateManager) SetContextWarningIssued

func (s *AgentStateManager) SetContextWarningIssued(v bool)

func (*AgentStateManager) SetConversationPruner

func (s *AgentStateManager) SetConversationPruner(pruner *ConversationPruner)

func (*AgentStateManager) SetCurrentContextTokens

func (s *AgentStateManager) SetCurrentContextTokens(n int)

func (*AgentStateManager) SetCurrentIteration

func (s *AgentStateManager) SetCurrentIteration(iter int)

func (*AgentStateManager) SetEstimatedTokenResponses

func (s *AgentStateManager) SetEstimatedTokenResponses(n int)

func (*AgentStateManager) SetFalseStopDetectionEnabled

func (s *AgentStateManager) SetFalseStopDetectionEnabled(v bool)

func (*AgentStateManager) SetFreeTokens added in v0.16.19

func (s *AgentStateManager) SetFreeTokens(v int)

func (*AgentStateManager) SetHistoryIndex

func (s *AgentStateManager) SetHistoryIndex(i int)

func (*AgentStateManager) SetLLMCallCount

func (s *AgentStateManager) SetLLMCallCount(n int)

func (*AgentStateManager) SetLastProviderError

func (s *AgentStateManager) SetLastProviderError(err *ProviderErrorInfo)

SetLastProviderError sets the last provider error info

func (*AgentStateManager) SetLastRunTerminationReason

func (s *AgentStateManager) SetLastRunTerminationReason(reason string)

func (*AgentStateManager) SetMaxContextTokens

func (s *AgentStateManager) SetMaxContextTokens(n int)

func (*AgentStateManager) SetMessageTimestamps added in v0.16.25

func (s *AgentStateManager) SetMessageTimestamps(ts []time.Time)

SetMessageTimestamps sets the creation timestamps for each message.

func (*AgentStateManager) SetMessages

func (s *AgentStateManager) SetMessages(msgs []api.Message)

func (*AgentStateManager) SetOptimizer

func (s *AgentStateManager) SetOptimizer(o *ConversationOptimizer)

func (*AgentStateManager) SetPauseState

func (s *AgentStateManager) SetPauseState(ps *PauseState)

func (*AgentStateManager) SetPendingStrictSwitchNotice

func (s *AgentStateManager) SetPendingStrictSwitchNotice(v string)

func (*AgentStateManager) SetPendingSwitchContextRefresh

func (s *AgentStateManager) SetPendingSwitchContextRefresh(v string)

func (*AgentStateManager) SetPendingSystemSupplement

func (s *AgentStateManager) SetPendingSystemSupplement(v string)

func (*AgentStateManager) SetPreviousSummary

func (s *AgentStateManager) SetPreviousSummary(summary string)

func (*AgentStateManager) SetPromptTokens

func (s *AgentStateManager) SetPromptTokens(n int)

func (*AgentStateManager) SetSessionID

func (s *AgentStateManager) SetSessionID(id string)

func (*AgentStateManager) SetSessionIntentEmbedding

func (s *AgentStateManager) SetSessionIntentEmbedding(emb []float32)

func (*AgentStateManager) SetSessionIntentEmbeddingIfNil

func (s *AgentStateManager) SetSessionIntentEmbeddingIfNil(emb []float32) bool

func (*AgentStateManager) SetSessionModel

func (s *AgentStateManager) SetSessionModel(m string)

func (*AgentStateManager) SetSessionProvider

func (s *AgentStateManager) SetSessionProvider(ct api.ClientType)

func (*AgentStateManager) SetSubscriptionTokens added in v0.16.19

func (s *AgentStateManager) SetSubscriptionTokens(v int)

func (*AgentStateManager) SetTaskActions

func (s *AgentStateManager) SetTaskActions(actions []TaskAction)

func (*AgentStateManager) SetTokenCostTotal added in v0.16.19

func (s *AgentStateManager) SetTokenCostTotal(v float64)

func (*AgentStateManager) SetToolCallGuidanceAdded

func (s *AgentStateManager) SetToolCallGuidanceAdded(v bool)

func (*AgentStateManager) SetTotalCost

func (s *AgentStateManager) SetTotalCost(c float64)

func (*AgentStateManager) SetTotalTokens

func (s *AgentStateManager) SetTotalTokens(n int)

func (*AgentStateManager) SetTotalToolCalls

func (s *AgentStateManager) SetTotalToolCalls(n int)

func (*AgentStateManager) SetTraceSession

func (s *AgentStateManager) SetTraceSession(ts interface{})

func (*AgentStateManager) SetTurnCheckpoints

func (s *AgentStateManager) SetTurnCheckpoints(cps []TurnCheckpoint)

type Breakpoint added in v0.16.25

type Breakpoint struct {
	Index   int    // 1-based user-facing index
	Content string // First ~80 chars for display
}

Breakpoint represents a user message that can be forked from.

type BrokerDecision added in v0.16.18

type BrokerDecision struct {
	Approved   bool
	Decision   security.ApprovalDecision
	Outcome    security.ApprovalOutcome
	Surface    string         // "webui" or "cli" — which surface answered
	Assessment RiskAssessment // echoed for caller diagnostics
}

BrokerDecision is the typed verdict returned by RequestApproval.

type CLIPasswordPrompter added in v0.16.18

type CLIPasswordPrompter struct{}

CLIPasswordPrompter implements PasswordPrompter for CLI terminal sessions. It reads the password from stdin with echo disabled using golang.org/x/term.

func NewCLIPasswordPrompter added in v0.16.18

func NewCLIPasswordPrompter() *CLIPasswordPrompter

NewCLIPasswordPrompter constructs a CLI password prompter.

func (*CLIPasswordPrompter) Prompt added in v0.16.18

func (cli *CLIPasswordPrompter) Prompt(ctx context.Context, reason string) (string, error)

Prompt asks the user to type a password on the terminal.

If stdin is not a TTY it returns ErrNoInteractiveSurface immediately. The reason string is printed to stderr as a prompt label. Terminal state is always restored via defer even when an error occurs.

The clihooks.WithCookedStdin wrapper ensures the active CLI activity indicator (spinner) is suspended and the SP-055 SteerInputReader (which holds stdin in raw mode during a turn) is paused for the duration of the read. Without this, the spinner would clobber the prompt text on stderr, and a mid-turn call would hit EOF immediately because the steer reader is consuming raw-mode stdin. WithCookedStdin is a no-op when no hook is registered (non-interactive runs).

type CacheStats added in v0.16.25

type CacheStats interface {
	GetCachedTokens() int
	SetCachedTokens(int)
	GetCacheWriteTokens() int
	SetCacheWriteTokens(int)
	GetCachedCostSavings() float64
	SetCachedCostSavings(float64)
}

CacheStats manages prompt cache statistics.

type ChangeTracker

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

ChangeTracker manages change tracking for the agent workflow

func NewChangeTracker

func NewChangeTracker(agent *Agent, instructions string) *ChangeTracker

NewChangeTracker creates a new change tracker for an agent session

func (*ChangeTracker) Clear

func (ct *ChangeTracker) Clear()

Clear clears all tracked changes (but keeps the tracker enabled). Also resets the shell-snapshot cache so a subsequent EnableChangeTracking / PrimeShellTracking call re-baselines against current disk state — otherwise a stale cache would attribute post-Clear shell mutations to "the workspace as it looked at session start", which is wrong after a Reset. The autoSkipDirs adaptive set is preserved across Clear (it's an optimization, not state about the user's changes); a Reset that wants to re-learn from scratch can null it manually.

func (*ChangeTracker) CollectFileChangesForCheckpoint

func (ct *ChangeTracker) CollectFileChangesForCheckpoint() ([]CheckpointFileChange, string)

CollectFileChangesForCheckpoint returns the (path, op) manifest of changes appended since the most recent checkpoint capture, along with the current revision ID. Advances the internal watermark so a subsequent call returns only the next turn's changes. Safe to call when tracking is disabled — returns (nil, "") in that case.

Ops are git-style: "A" (added/created), "M" (modified), "D" (deleted), "R" (renamed). The ChangeTracker today only records create/write/edit — never delete or rename — so the manifest produced here will only contain A and M entries. When the tracker grows D/R support, extend the mapping table below.

Multiple writes to the same path within the same turn collapse to one entry, preferring "A" over "M" (a turn that creates then modifies the same file is recorded as A).

func (*ChangeTracker) Commit

func (ct *ChangeTracker) Commit(llmResponse string, conversation []api.Message) error

Commit commits all tracked changes to the change tracker

func (*ChangeTracker) Disable

func (ct *ChangeTracker) Disable()

Disable disables change tracking. Same lock discipline as Enable.

func (*ChangeTracker) Enable

func (ct *ChangeTracker) Enable()

Enable enables change tracking. Holds ct.mu so the write doesn't race with concurrent IsEnabled() reads from background goroutines (e.g. RecordTurnCheckpointAsync).

func (*ChangeTracker) GenerateAISummary

func (ct *ChangeTracker) GenerateAISummary() (string, error)

GenerateAISummary creates an AI-generated summary of the changes

func (*ChangeTracker) GetChangeCount

func (ct *ChangeTracker) GetChangeCount() int

GetChangeCount returns the number of tracked changes

func (*ChangeTracker) GetChanges

func (ct *ChangeTracker) GetChanges() []TrackedFileChange

GetChanges returns a copy of the tracked changes

func (*ChangeTracker) GetRevisionID

func (ct *ChangeTracker) GetRevisionID() string

GetRevisionID returns the current revision ID

func (*ChangeTracker) GetSummary

func (ct *ChangeTracker) GetSummary() string

GetSummary returns a summary of tracked changes

func (*ChangeTracker) GetTrackedFiles

func (ct *ChangeTracker) GetTrackedFiles() []string

GetTrackedFiles returns a list of files that have been modified

func (*ChangeTracker) IsEnabled

func (ct *ChangeTracker) IsEnabled() bool

IsEnabled returns whether change tracking is enabled. Production code must call this instead of reading ct.enabled directly so concurrent Enable()/Disable() calls don't race the read.

func (*ChangeTracker) MergeChild added in v0.16.12

func (ct *ChangeTracker) MergeChild(changes []TrackedFileChange, source string)

MergeChild appends a subagent's tracked changes into this (parent) tracker so list_changes / recover_file / revert_my_changes see subagent edits too. Each merged entry is tagged with Source so list_changes can attribute it; the shell-snapshot cache is re-baselined per path so the next shell-command walk doesn't record a duplicate entry for the same file.

This closes the SP-059 Phase 2c gap where subagent edits were captured in a child tracker but never surfaced to the parent's user-facing change tools.

Safe to call when tracking is disabled (no-op). The input slice is copied; nil/empty input is a no-op.

func (*ChangeTracker) PrimeShellTracking

func (ct *ChangeTracker) PrimeShellTracking(workDir string)

PrimeShellTracking captures the workspace's current state as the baseline against which future shell_command invocations are diffed. Idempotent: a second call against the already-primed tracker is a no-op. Safe to call multiple times — only the first does work.

Lazy callers can skip this and rely on TrackShellTurn to auto-prime on first invocation; in that mode the first shell_command's own pre-state is captured but no changes are recorded for it (the initial walk IS the baseline). When the first shell command's mutations need to be tracked, PrimeShellTracking should be called from EnableChangeTracking so the baseline pre-exists.

func (*ChangeTracker) RecordShellMutations

func (ct *ChangeTracker) RecordShellMutations(before, after map[string]*shellSnapshotEntry, toolCall string)

RecordShellMutations diffs a pair of snapshots (taken before/after a shell_command invocation) and appends TrackedFileChange entries for every file that materially changed. The before-snapshot supplies the OriginalCode field so a user can recover untracked-by-git files the agent accidentally deleted or mangled.

Dedup: if the path was already recorded this turn via TrackFileWrite / TrackFileEdit (the direct tool hooks), the existing entry is kept and we don't double-record from the shell diff. The direct entry is richer (original_code captured at the source) so it wins.

SP-061-1: when a single shell command churns more than shellBulkThreshold paths AND some top-level workspace directory owns at least shellBulkPerDirMin of them, that directory is rolled up into a single "bulk" entry and added to autoSkipDirs so future walks skip it entirely. The rollup carries BulkCount so the UI can render "dist/ — 1,247 files (build output)" instead of stacking thousands of individual rows.

func (*ChangeTracker) Reset

func (ct *ChangeTracker) Reset(instructions string)

Reset resets the change tracker with a new revision ID and instructions

func (*ChangeTracker) SyncShellCacheForPath

func (ct *ChangeTracker) SyncShellCacheForPath(path string)

SyncShellCacheForPath refreshes the shell cache entry for one path against its current on-disk state. Called by the direct file-write hooks (TrackFileWrite, TrackFileEdit) so the cache reflects writes the agent just performed via structured-file tools — without this, the next TrackShellTurn walk would see the new content as a stat mismatch against stale cache and record a duplicate "edit" entry even though no shell command touched the file.

Safe to call when the cache hasn't been primed yet (no-op) — there's no baseline to keep in sync.

func (*ChangeTracker) TrackFileEdit

func (ct *ChangeTracker) TrackFileEdit(filePath string, originalContent string, newContent string) error

TrackFileEdit tracks an edit operation (EditFile tool)

func (*ChangeTracker) TrackFileWrite

func (ct *ChangeTracker) TrackFileWrite(filePath string, newContent string) error

TrackFileWrite tracks a write operation (WriteFile tool)

func (*ChangeTracker) TrackShellTurn

func (ct *ChangeTracker) TrackShellTurn(workDir, toolCall string, destructive bool)

TrackShellTurn diffs the workspace against the primed baseline, appends every detected mutation to the change tracker (with dedup against direct-hook entries that fired during the same window), then rebases the baseline to the new state.

If the cache hasn't been primed yet this call auto-primes — the pre-shell state is captured but no changes are recorded the first time (we have no baseline to compare against). To track the very first shell command's mutations, call PrimeShellTracking once at agent session start before the first shell_command runs.

Honors the per-tracker shellWalkEnabled knob — when disabled the call is a no-op so users with weird workspaces can keep direct-tool tracking without paying the walker's cost.

`destructive` should be set when the shell command can clobber active changes (`git checkout .`, `git reset --hard`, …). It flips the walk into the safer mode that bypasses autoSkipDirs and emits per-file rather than rolling up — see shell_destructive.go for the classifier and walkWorkspace for the behaviour switch.

Concurrency: serialized via the tracker's internal mutex. Subagents each have their own ChangeTracker so cross-subagent calls don't interfere.

type CheckpointFileChange

type CheckpointFileChange struct {
	Path string `json:"path"`
	Op   string `json:"op"`
}

CheckpointFileChange is a single file-change entry in a TurnCheckpoint's manifest. Op is one of "A" (added), "M" (modified), "D" (deleted), "R" (renamed) to mirror git's status codes; anything else is "?" (other).

type CheckpointStore added in v0.16.25

type CheckpointStore interface {
	GetTurnCheckpoints() []TurnCheckpoint
	SetTurnCheckpoints([]TurnCheckpoint)
	AddTurnCheckpoint(TurnCheckpoint)
	GetCheckpointMutex() *sync.RWMutex
}

CheckpointStore manages turn checkpoints for state persistence.

type ChoiceOption

type ChoiceOption struct {
	Label string
	Value string
}

ChoiceOption represents a simple label/value option for UI prompts

type CircuitBreakerAction

type CircuitBreakerAction struct {
	ActionType string // "edit_file", "shell_command", etc.
	Target     string // file path, command, etc.
	Count      int    // number of times this action was performed
	LastUsed   int64  // unix timestamp of last use
}

CircuitBreakerAction tracks repetitive actions for circuit breaker logic

type CircuitBreakerState

type CircuitBreakerState struct {
	Actions map[string]*CircuitBreakerAction // key: actionType:target
	// contains filtered or unexported fields
}

CircuitBreakerState tracks repetitive actions across the session.

Locking Strategy:

  • The Actions map is protected by mu (sync.RWMutex)
  • Use RLock/RLock for read-only access when you don't need exclusive access
  • Use Lock for write operations or when you need exclusive access
  • Always use defer to unlock (defer mu.Unlock() or defer mu.RUnlock())
  • Helper functions ending with "Locked" must be called while holding the lock (they perform no locking themselves, allowing callers to hold lock for multiple ops)

Example patterns:

// Read-only access:
cb.mu.RLock()
defer cb.mu.RUnlock()
action := cb.Actions[key]

// Write access:
cb.mu.Lock()
defer cb.mu.Unlock()
cb.Actions[key] = &CircuitBreakerAction{...}

type CircuitBreakerStore added in v0.16.25

type CircuitBreakerStore interface {
	GetCircuitBreaker() *CircuitBreakerState
	SetCircuitBreaker(*CircuitBreakerState)
}

CircuitBreakerStore manages the circuit breaker state.

type ClarificationManager

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

ClarificationManager manages pending clarification requests between subagent and parent agents. It provides thread-safe tracking of clarification requests and responses via channels.

func NewClarificationManager

func NewClarificationManager(eventBus *events.EventBus) *ClarificationManager

NewClarificationManager creates a manager with default 60s timeout.

func NewClarificationManagerWithTimeout

func NewClarificationManagerWithTimeout(eventBus *events.EventBus, timeout time.Duration) *ClarificationManager

NewClarificationManagerWithTimeout creates a manager with a custom timeout.

func (*ClarificationManager) Cleanup

func (m *ClarificationManager) Cleanup()

Cleanup removes expired entries.

func (*ClarificationManager) Close

func (m *ClarificationManager) Close()

Close stops the background cleanup goroutine.

func (*ClarificationManager) GetPendingClarifications

func (m *ClarificationManager) GetPendingClarifications(subagentID string) []ClarificationRequest

GetPendingClarifications returns all pending clarification requests for a subagent.

func (*ClarificationManager) RequestClarification

func (m *ClarificationManager) RequestClarification(ctx context.Context, subagentID, question string) (string, error)

RequestClarification creates a clarification request, publishes an event, and blocks until a response arrives or timeout.

func (*ClarificationManager) RespondClarification

func (m *ClarificationManager) RespondClarification(requestID, response string) error

RespondClarification finds a pending request and sends a response to it.

type ClarificationRequest

type ClarificationRequest struct {
	RequestID  string    `json:"request_id"`
	SubagentID string    `json:"subagent_id"`
	Question   string    `json:"question"`
	CreatedAt  time.Time `json:"created_at"`
}

ClarificationRequest is the exported representation of a pending clarification request.

type CommandHistoryStore added in v0.16.25

type CommandHistoryStore interface {
	GetCommandHistory() []string
	SetCommandHistory([]string)
	GetHistoryIndex() int
	SetHistoryIndex(int)
	GetHistoryMutex() *sync.Mutex
}

CommandHistoryStore manages command history navigation.

type CommandKind added in v0.16.19

type CommandKind string

CommandKind categorizes a shell command part by its destructive intent.

const (
	CommandKindRm            CommandKind = "rm"
	CommandKindGitPush       CommandKind = "git_push"
	CommandKindGitReset      CommandKind = "git_reset"
	CommandKindKubectl       CommandKind = "kubectl"
	CommandKindDocker        CommandKind = "docker"
	CommandKindChmod         CommandKind = "chmod"
	CommandKindChown         CommandKind = "chown"
	CommandKindWriteRedirect CommandKind = "write_redirect"
	CommandKindHttpPost      CommandKind = "http_post"
	CommandKindUnknown       CommandKind = "unknown"
)

func ClassifyShellSegment added in v0.16.19

func ClassifyShellSegment(segment string) CommandKind

ClassifyShellSegment returns the CommandKind for a single shell segment by matching it against the classification pattern table.

func ClassifyShellSegmentWithSemantic added in v0.16.19

func ClassifyShellSegmentWithSemantic(segment string) (CommandKind, string)

ClassifyShellSegmentWithSemantic returns the CommandKind and a brief human-readable description for the segment.

type CompactPreview added in v0.16.4

type CompactPreview struct {
	BeforeMessageCount   int              `json:"before_message_count"`
	AfterMessageCount    int              `json:"after_message_count"`
	WouldReduce          bool             `json:"would_reduce"`
	CompactedMessages    []api.Message    `json:"compacted_messages"`
	RemainingCheckpoints []TurnCheckpoint `json:"remaining_checkpoints"`
}

CompactPreview captures the would-be result of running /compact right now, without applying it. Populated only when CaptureTranscriptSnapshot is called with includePreview=true.

type ConfigOverrideStore added in v0.16.25

type ConfigOverrideStore interface {
	GetConfigOverrides() map[string]interface{}
	SetConfigOverrides(map[string]interface{})
}

ConfigOverrideStore manages config overrides for the current session.

type ContextBudgetStore added in v0.16.25

type ContextBudgetStore interface {
	GetCurrentContextTokens() int
	SetCurrentContextTokens(int)
	GetMaxContextTokens() int
	SetMaxContextTokens(int)
	IsContextWarningIssued() bool
	SetContextWarningIssued(bool)
}

ContextBudgetStore manages context window token budgeting and warnings.

type ContextFileInfo

type ContextFileInfo struct {
	Path        string
	Content     string
	Description string
	Priority    int
}

ContextFileInfo represents information about a discovered context file

func DiscoverContextFiles

func DiscoverContextFiles() (*ContextFileInfo, error)

DiscoverContextFiles looks for context files in the current directory and parent directories Returns the first matching file based on priority order

type ConversationOptimizer

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

ConversationOptimizer is sprout's thin wrapper around seed's core.ConversationOptimizer. The dedup + observation-masking implementation moved into seed so other consumers benefit; this wrapper preserves sprout's historical method surface (InvalidateFile, GetOptimizationStats, SetLLMClient) for callers that haven't migrated.

The LLM-based structural compaction that used to live here is now wired through seed's chat loop via Options.LLMSummarizer — see newLLMSummarizer in llm_summarizer.go and the construction site in seed_integration.go. SetLLMClient is consequently a no-op here.

func NewConversationOptimizer

func NewConversationOptimizer(enabled, debug bool) *ConversationOptimizer

NewConversationOptimizer constructs the wrapper. The debug flag is kept for backward-compatible call sites but is unused now that seed's optimizer emits via the EventPublisher instead.

func (*ConversationOptimizer) CompactConversation

func (co *ConversationOptimizer) CompactConversation(messages []api.Message) []api.Message

CompactConversation is retained for backward compatibility with callers that haven't migrated. Structural compaction now runs inside seed's chat loop via core.CompactWithLLMSummary, configured at seed-Agent construction. Calling this here is a no-op; the live request path no longer routes through this method.

func (*ConversationOptimizer) GetOptimizationStats

func (co *ConversationOptimizer) GetOptimizationStats() map[string]interface{}

GetOptimizationStats returns a small status map. The per-file and per-command tracking counts are no longer maintained (seed scans fresh each call); the map shape stays so UI consumers continue to work.

func (*ConversationOptimizer) Inner

Inner exposes the wrapped seed optimizer for direct use when constructing seed-Agent options (see seed_integration.go).

func (*ConversationOptimizer) InvalidateFile

func (co *ConversationOptimizer) InvalidateFile(filePath string)

InvalidateFile was used by sprout's per-file dedup cache. Seed's optimizer is stateless across calls (it scans the message list fresh each time), so there is no cache to invalidate. Kept as a no-op for caller compatibility.

func (*ConversationOptimizer) IsEnabled

func (co *ConversationOptimizer) IsEnabled() bool

IsEnabled reports whether the optimizer was constructed enabled.

func (*ConversationOptimizer) OptimizeConversation

func (co *ConversationOptimizer) OptimizeConversation(messages []api.Message) []api.Message

OptimizeConversation delegates to the seed optimizer. Returns the input unchanged when the optimizer is disabled.

func (*ConversationOptimizer) Reset

func (co *ConversationOptimizer) Reset()

Reset clears optimizer state. Seed's optimizer is stateless across calls so this is a no-op; preserved for caller compatibility.

func (*ConversationOptimizer) SetEnabled

func (co *ConversationOptimizer) SetEnabled(enabled bool)

SetEnabled toggles the optimizer at runtime. Since seed's optimizer captures Enabled at construction, this rebuilds the inner instance.

func (*ConversationOptimizer) SetLLMClient

func (co *ConversationOptimizer) SetLLMClient(client api.ClientInterface, provider string, printLine func(string))

SetLLMClient is now a no-op. The LLM summary path is wired via seed Options.LLMSummarizer at seed-Agent construction (seed_integration.go).

type ConversationPruner

type ConversationPruner = core.ConversationPruner

ConversationPruner is aliased to seed's core.ConversationPruner so sprout's existing callers (submanager_state.go, pruning_config.go, tests) continue to compile against the same type while the implementation lives in seed and is available to other consumers.

type ConversationPrunerStore added in v0.16.25

type ConversationPrunerStore interface {
	GetConversationPruner() *ConversationPruner
	SetConversationPruner(*ConversationPruner)
}

ConversationPrunerStore manages the conversation pruner instance.

type ConversationState

type ConversationState struct {
	Messages                []api.Message    `json:"messages"`
	TurnCheckpoints         []TurnCheckpoint `json:"turn_checkpoints,omitempty"`
	TaskActions             []TaskAction     `json:"task_actions"`
	TotalCost               float64          `json:"total_cost"`
	TotalTokens             int              `json:"total_tokens"`
	PromptTokens            int              `json:"prompt_tokens"`
	CompletionTokens        int              `json:"completion_tokens"`
	EstimatedTokenResponses int              `json:"estimated_token_responses"`
	CachedTokens            int              `json:"cached_tokens"`
	CacheWriteTokens        int              `json:"cache_write_tokens,omitempty"`
	CachedCostSavings       float64          `json:"cached_cost_savings"`
	LastUpdated             time.Time        `json:"last_updated"`
	SessionID               string           `json:"session_id"`
	Name                    string           `json:"name"`              // Human-readable session name
	WorkingDirectory        string           `json:"working_directory"` // Directory where session was created

	// ConfigOverrides stores session-scoped configuration overrides.
	// Applied on top of global and workspace config when the session is restored.
	// Only non-empty values are considered overrides.
	ConfigOverrides map[string]interface{} `json:"config_overrides,omitempty"`

	// SessionIntentEmbedding stores the embedding of the first user prompt in a session.
	// Used for drift detection to track conversation intent over time.
	SessionIntentEmbedding []float32 `json:"session_intent_embedding,omitempty"`

	// LastProviderError captures details about the last API error from the LLM provider.
	// Persisted in the session file so errors can be diagnosed after the fact.
	LastProviderError *ProviderErrorInfo `json:"last_provider_error,omitempty"`
}

ConversationState represents the state of a conversation that can be persisted

func ImportStateFromJSONFile

func ImportStateFromJSONFile(filename string) (*ConversationState, error)

ImportStateFromJSONFile loads a ConversationState from a JSON file

func LoadSessionInfo

func LoadSessionInfo(sessionID string) (*ConversationState, error)

LoadSessionInfo loads session information including timestamp

func LoadStateWithoutAgent

func LoadStateWithoutAgent(sessionID string) (*ConversationState, error)

LoadStateWithoutAgent loads a conversation state by session ID without an Agent instance

func LoadStateWithoutAgentScoped

func LoadStateWithoutAgentScoped(sessionID, workingDir string) (*ConversationState, error)

LoadStateWithoutAgentScoped loads a state for a specific working directory scope.

type ConversationTurn

type ConversationTurn struct {
	ID                string    `json:"id"`
	SessionID         string    `json:"session_id"`
	TurnNumber        int       `json:"turn_number"`
	Timestamp         time.Time `json:"timestamp"`
	UserPrompt        string    `json:"user_prompt"`
	ActionableSummary string    `json:"actionable_summary,omitempty"`
	PromptEmbedding   []float32 `json:"prompt_embedding,omitempty"`
	FilesTouched      []string  `json:"files_touched,omitempty"`
	WorkingDir        string    `json:"working_dir"`
	Duration          float64   `json:"duration"`    // seconds from prompt to turn completion
	TokenUsage        int       `json:"token_usage"` // total tokens in this turn
}

ConversationTurn represents a completed conversation turn stored for persistent context retrieval and semantic search across sessions.

func NewConversationTurn

func NewConversationTurn(sessionID string, turnNumber int, userPrompt, workingDir string) (*ConversationTurn, error)

NewConversationTurn creates a new ConversationTurn with a generated ID and current timestamp. Returns an error if ID generation fails.

func (*ConversationTurn) String

func (t *ConversationTurn) String() string

String returns a human-readable representation of the turn, omitting the embedding vector for readability.

func (*ConversationTurn) ToVectorRecord

func (t *ConversationTurn) ToVectorRecord() embedding.VectorRecord

ToVectorRecord converts a ConversationTurn into a VectorRecord for storage in the conversation embedding store. The prompt text is truncated to maxSignatureLen characters for the Signature field. All turn metadata (summary, files, working dir, duration, tokens) is preserved in the Metadata map so no information is lost.

type CostEntry added in v0.16.19

type CostEntry struct {
	BillingType      string  `json:"billing_type"`
	Provider         string  `json:"provider"`
	Model            string  `json:"model"`
	ChargedCost      float64 `json:"charged_cost"`
	TokenCost        float64 `json:"token_cost,omitempty"`
	PromptTokens     int     `json:"prompt_tokens"`
	CompletionTokens int     `json:"completion_tokens"`
	CachedTokens     int     `json:"cached_tokens,omitempty"`
}

CostEntry captures a single cost-bearing LLM call with billing-model awareness. It carries two cost numbers:

  • ChargedCost: real USD charged for this call (only > 0 for pay_per_token)
  • TokenCost: estimated USD value of tokens consumed, from per-model pricing

type CostTracker added in v0.16.25

type CostTracker interface {
	GetTotalCost() float64
	SetTotalCost(float64)
	AddCost(float64)
	AddCostEntry(CostEntry)
	GetChargedCostTotal() float64
	GetTokenCostTotal() float64
	GetSubscriptionTokens() int
	GetFreeTokens() int
	SetChargedCostTotal(float64)
	SetTokenCostTotal(float64)
	SetSubscriptionTokens(int)
	SetFreeTokens(int)
}

CostTracker manages billing costs, token costs, and subscription/free token counts.

type DiffChange

type DiffChange struct {
	OldStart  int
	OldLength int
	NewStart  int
	NewLength int
}

DiffChange represents a change region in the diff

type DiffLine added in v0.16.12

type DiffLine struct {
	Type    DiffLineType
	Content string
}

DiffLine represents a single line in a unified diff hunk.

type DiffLineType added in v0.16.12

type DiffLineType string

DiffLineType identifies whether a diff line is context, added, or removed.

const (
	DiffLineContext DiffLineType = "context"
	DiffLineAdd     DiffLineType = "add"
	DiffLineRemove  DiffLineType = "remove"
)

type DriftDetector

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

DriftDetector tracks conversational drift by comparing the current turn's embedding against the session's original intent embedding.

func NewDriftDetector

func NewDriftDetector(threshold float64, checkInterval int) *DriftDetector

NewDriftDetector creates a new DriftDetector with the given threshold and check interval. Zero values are replaced with sensible defaults.

func (*DriftDetector) CheckDrift

func (d *DriftDetector) CheckDrift(sessionIntent []float32, currentEmbedding []float32) (isDrift bool, similarity float64)

CheckDrift computes the cosine similarity between the session's original intent embedding and the current turn's embedding. Returns true if the similarity is below the threshold, indicating drift.

If sessionIntent is nil or empty, returns false, 0 as a graceful no-op.

func (*DriftDetector) DriftCount

func (d *DriftDetector) DriftCount() int

DriftCount returns the number of drift detections in this session.

func (*DriftDetector) IsSuppressed

func (d *DriftDetector) IsSuppressed() bool

IsSuppressed returns true if drift detection has been suppressed for this session due to too many rejections.

func (*DriftDetector) RecordAcceptance

func (d *DriftDetector) RecordAcceptance()

RecordAcceptance resets the consecutive rejection counter. Called when the user chooses "Continue here" (accepts) in response to a drift notification.

func (*DriftDetector) RecordDrift

func (d *DriftDetector) RecordDrift()

RecordDrift increments the drift detection counter for this session.

func (*DriftDetector) RecordRejection

func (d *DriftDetector) RecordRejection()

RecordRejection increments the consecutive rejection counter and suppresses drift detection if the user has rejected MaxDriftRejections times in a row.

func (*DriftDetector) RejectionCount

func (d *DriftDetector) RejectionCount() int

RejectionCount returns the number of consecutive drift rejections. This counter is reset to 0 when RecordAcceptance is called.

func (*DriftDetector) ShouldCheck

func (d *DriftDetector) ShouldCheck(turnNumber int) bool

ShouldCheck returns true if drift should be checked on the given turn number. Checks occur every checkInterval turns (turn 5, 10, 15, ...). Returns false if the detector is suppressed.

type DriftNotification

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

DriftNotification handles emitting drift notifications through the event bus. The notification is non-blocking — the agent continues processing after emission.

func NewDriftNotification

func NewDriftNotification(detector *DriftDetector, eventBus *events.EventBus, sessionID string) *DriftNotification

NewDriftNotification creates a new drift notification handler.

func (*DriftNotification) NotifyDrift

func (n *DriftNotification) NotifyDrift(similarity float64, threshold float64) map[string]interface{}

NotifyDrift emits a drift detection event via the EventBus (for WebUI). Returns the notification data so the CLI layer can use it for display. This is non-blocking — it does not wait for user response.

type DropdownItem struct {
	Label string
	Value string
}

DropdownItem represents an item in a dropdown selection

type DropdownOptions struct {
	Prompt       string
	SearchPrompt string
	ShowCounts   bool
}

DropdownOptions provides options for dropdown display

type EditDecision added in v0.16.12

type EditDecision struct {
	Approved      bool
	AcceptedHunks []string // hunk IDs; empty + Approved=false => reject all
}

EditDecision captures the user's per-hunk accept/reject choices.

type EditProposal added in v0.16.12

type EditProposal struct {
	Path     string
	Original string
	Proposed string
	Hunks    []Hunk
}

EditProposal describes a proposed file edit awaiting approval.

type EstimatedTokenStore added in v0.16.25

type EstimatedTokenStore interface {
	GetEstimatedTokenResponses() int
	SetEstimatedTokenResponses(int)
}

EstimatedTokenStore manages estimated token response counts.

type FalseStopStore added in v0.16.25

type FalseStopStore interface {
	IsFalseStopDetectionEnabled() bool
	SetFalseStopDetectionEnabled(bool)
}

FalseStopStore manages false stop detection enablement.

type FileChange

type FileChange struct {
	Path string `json:"path"`
	Op   string `json:"op"` // "created" | "modified" | "deleted"
}

FileChange is a single tracked write/edit/delete from a subagent run. Sourced from ChangeTracker.GetChanges() (SP-059 Phase 2c) when change tracking is enabled; nil when it isn't.

type FleetUsdBudget added in v0.16.4

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

FleetUsdBudget caps the total USD cost across a primary agent and every subagent it spawns. It mirrors the token-based fleetBudget mechanism but in USD because mixed-provider workflows can't be reasonably capped by tokens — a token cap that fits an Opus orchestrator would let a DeepSeek coder run effectively unbounded for the same numeric value.

Threshold warnings are emitted at most once per threshold per budget instance (warnedIdx is monotonic). The truncation flag (Exceeded) is sticky once set — the agent's conversation loop polls it to stop gracefully after the current LLM response.

func NewFleetUsdBudget added in v0.16.4

func NewFleetUsdBudget(limit float64, warnAt []float64) *FleetUsdBudget

NewFleetUsdBudget returns a budget with the given hard cap (USD) and warning thresholds (fractions of the cap in (0, 1]). The thresholds are copied and sorted so the caller can pass them in any order.

func (*FleetUsdBudget) Add added in v0.16.4

func (b *FleetUsdBudget) Add(cost float64) (newSpent float64, crossed []float64, justExceeded bool)

Add debits a cost to the budget. Returns:

  • newSpent: the cumulative spend after the addition
  • crossed: the warning thresholds (as fractions of the limit) that this call newly crossed — empty if none
  • justExceeded: true only on the call that first pushes spent past limit

When the cap is hit, the exceeded flag is set so the conversation loop can observe it via Exceeded() and stop gracefully. Subsequent calls still accumulate spend (so reporting stays accurate) but exceeded stays sticky and crossed stays empty.

func (*FleetUsdBudget) Exceeded added in v0.16.4

func (b *FleetUsdBudget) Exceeded() bool

Exceeded reports whether the budget has been reached or surpassed.

func (*FleetUsdBudget) Snapshot added in v0.16.4

func (b *FleetUsdBudget) Snapshot() (spent, limit float64)

Snapshot returns the current spend and limit for display purposes.

type Hunk added in v0.16.12

type Hunk struct {
	ID       string
	OldStart int // 1-based
	OldLines int
	NewStart int // 1-based
	NewLines int
	Lines    []DiffLine
}

Hunk represents a discrete change region in a unified diff.

func SplitIntoHunks added in v0.16.12

func SplitIntoHunks(original, proposed string) []Hunk

SplitIntoHunks computes the unified diff between original and proposed content and splits it into discrete hunks with stable IDs ("hunk-0", "hunk-1", …). Each hunk includes up to 3 lines of surrounding context.

type IterationStore added in v0.16.25

type IterationStore interface {
	GetCurrentIteration() int
	SetCurrentIteration(int)
}

IterationStore manages the current iteration count.

type LLMCallTracker added in v0.16.25

type LLMCallTracker interface {
	GetLLMCallCount() int
	SetLLMCallCount(int)
	IncrementLLMCallCount()
}

LLMCallTracker manages LLM call count tracking.

type LogContext

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

LogContext allows chaining fields for multiple log entries

func (*LogContext) Debug

func (lc *LogContext) Debug(format string, args ...interface{})

Debug writes a debug-level log with context fields from the LogContext

func (*LogContext) Error

func (lc *LogContext) Error(format string, args ...interface{})

Error writes an error-level log with context fields from the LogContext

func (*LogContext) Info

func (lc *LogContext) Info(format string, args ...interface{})

Info writes an info-level log with context fields from the LogContext

func (*LogContext) Warn

func (lc *LogContext) Warn(format string, args ...interface{})

Warn writes a warn-level log with context fields from the LogContext

type LogEntry

type LogEntry struct {
	Timestamp string            `json:"timestamp"`
	Level     string            `json:"level"` // "debug", "info", "warn", "error"
	Message   string            `json:"message"`
	SessionID string            `json:"session_id,omitempty"`
	Iteration int               `json:"iteration,omitempty"`
	Provider  string            `json:"provider,omitempty"`
	Model     string            `json:"model,omitempty"`
	Fields    map[string]string `json:"fields,omitempty"`
}

LogEntry represents a structured log entry

type MCPSubManager

type MCPSubManager interface {
	GetManager() mcp.MCPManager
	SetManager(mgr mcp.MCPManager)
	GetToolsCache() []api.Tool
	SetToolsCache(tools []api.Tool)
	IsInitialized() bool
	SetInitialized(initialized bool)
	GetInitError() error
	SetInitError(err error)
	LockInit()
	UnlockInit()
}

MCPSubManager manages all MCP-related state for an Agent.

type MemoryGate added in v0.16.19

type MemoryGate struct {
	// MinMemoryBytes is the hard minimum; below this the gate refuses
	// immediately with no retries. Default: 8 GB.
	MinMemoryBytes int64
	// RetryMinBytes is the retry threshold; between MinMemoryBytes and this
	// value the gate sleeps and retries. Default: 16 GB.
	RetryMinBytes int64
	// RetrySleep is the duration to sleep between retries. Default: 30s.
	RetrySleep time.Duration
	// MaxRetries is the maximum number of retry attempts. Default: 5.
	MaxRetries int
	// contains filtered or unexported fields
}

MemoryGate checks available system memory before allowing memory-intensive operations. It is designed to prevent OOM kills when subagents run heavy commands like vitest with jsdom workers.

func DefaultMemoryGate added in v0.16.19

func DefaultMemoryGate() *MemoryGate

DefaultMemoryGate returns a MemoryGate with production defaults.

func (*MemoryGate) Check added in v0.16.19

func (g *MemoryGate) Check() error

Check verifies that sufficient memory is available for the operation.

Returns nil when memory is sufficient (or the check cannot be performed). Returns *MemoryGateError when memory is below the threshold.

type MemoryGateError added in v0.16.19

type MemoryGateError struct {
	AvailableBytes int64
	RequiredBytes  int64
	Retried        bool
}

MemoryGateError is returned when available memory is below the threshold.

func (*MemoryGateError) Error added in v0.16.19

func (e *MemoryGateError) Error() string

type MemoryInfo

type MemoryInfo struct {
	Name    string // Memory name, derived from filename (without .md extension)
	Path    string // Full file path
	Content string // File content string
}

MemoryInfo represents information about a memory file

func ListMemories

func ListMemories() ([]MemoryInfo, error)

ListMemories returns list of all memories with their name, path, and first line (title/heading) Sorts alphabetically by name

func LoadAllMemories

func LoadAllMemories() ([]MemoryInfo, error)

LoadAllMemories reads all .md files from the memories directory Returns a slice of MemoryInfo sorted by filename Returns empty slice (not error) if no memories exist

type MessageAnnotation added in v0.16.4

type MessageAnnotation struct {
	Index         int           `json:"index"`
	Role          string        `json:"role"`
	Source        MessageSource `json:"source"`
	ContentChars  int           `json:"content_chars"`
	ToolCallCount int           `json:"tool_call_count,omitempty"`
	FirstLine     string        `json:"first_line,omitempty"`
}

MessageAnnotation is the per-message diagnostic view. Index aligns 1:1 with TranscriptSnapshot.State.Messages.

type MessageImportance

type MessageImportance = core.MessageImportance

MessageImportance is aliased from seed for tests/diagnostics that inspect the structured score output of the importance scorer.

type MessageSource added in v0.16.4

type MessageSource string

MessageSource tags how a message arrived in the live conversation. It is the single most useful diagnostic field in a snapshot: it tells a reader whether what the model sees at index i is the user's original turn, a turn collapsed to a rule-based heuristic bullet list, or a structural summary produced by seed's LLM summarizer.

const (
	MessageSourceOriginal      MessageSource = "original"
	MessageSourceLLMCheckpoint MessageSource = "llm_checkpoint"
)

type MessageStore added in v0.16.25

type MessageStore interface {
	GetMessages() []api.Message
	SetMessages([]api.Message)
	AddMessage(api.Message)
	GetMessageTimestamps() []time.Time
	SetMessageTimestamps([]time.Time)
}

MessageStore manages conversation messages and their timestamps.

type MockLLMProvider added in v0.16.18

type MockLLMProvider struct {
	ResponsesByPrompt map[string]string // substring match (case-insensitive) on last user message
	DefaultResponse   string
	CallCount         int
	// contains filtered or unexported fields
}

MockLLMProvider implements api.ClientInterface with canned responses. Thread-safe: all mutable state is protected by mu.

func NewMockLLMProvider added in v0.16.18

func NewMockLLMProvider() *MockLLMProvider

NewMockLLMProvider creates a new mock LLM provider with sensible defaults.

func (*MockLLMProvider) CheckConnection added in v0.16.18

func (m *MockLLMProvider) CheckConnection() error

CheckConnection always succeeds.

func (*MockLLMProvider) GetAverageTPS added in v0.16.18

func (m *MockLLMProvider) GetAverageTPS() float64

GetAverageTPS returns a mock TPS value.

func (*MockLLMProvider) GetLastTPS added in v0.16.18

func (m *MockLLMProvider) GetLastTPS() float64

GetLastTPS returns a mock TPS value.

func (*MockLLMProvider) GetModel added in v0.16.18

func (m *MockLLMProvider) GetModel() string

GetModel returns the current model name.

func (*MockLLMProvider) GetModelContextLimit added in v0.16.18

func (m *MockLLMProvider) GetModelContextLimit() (int, error)

GetModelContextLimit returns a fixed context limit.

func (*MockLLMProvider) GetProvider added in v0.16.18

func (m *MockLLMProvider) GetProvider() string

GetProvider returns the provider name.

func (*MockLLMProvider) GetTPSStats added in v0.16.18

func (m *MockLLMProvider) GetTPSStats() map[string]float64

GetTPSStats returns mock TPS stats.

func (*MockLLMProvider) GetVisionModel added in v0.16.18

func (m *MockLLMProvider) GetVisionModel() string

GetVisionModel returns empty string.

func (*MockLLMProvider) ListModels added in v0.16.18

func (m *MockLLMProvider) ListModels(ctx context.Context) ([]api.ModelInfo, error)

ListModels returns a single mock model.

func (*MockLLMProvider) ResetTPSStats added in v0.16.18

func (m *MockLLMProvider) ResetTPSStats()

ResetTPSStats is a no-op.

func (*MockLLMProvider) SendChatRequest added in v0.16.18

func (m *MockLLMProvider) SendChatRequest(ctx context.Context, messages []api.Message, tools []api.Tool, reasoning string, disableThinking bool) (*api.ChatResponse, error)

SendChatRequest sends a chat request and returns a canned response.

func (*MockLLMProvider) SendChatRequestStream added in v0.16.18

func (m *MockLLMProvider) SendChatRequestStream(ctx context.Context, messages []api.Message, tools []api.Tool, reasoning string, disableThinking bool, callback api.StreamCallback) (*api.ChatResponse, error)

SendChatRequestStream streams a canned response.

func (*MockLLMProvider) SendVisionRequest added in v0.16.18

func (m *MockLLMProvider) SendVisionRequest(ctx context.Context, messages []api.Message, tools []api.Tool, reasoning string, disableThinking bool) (*api.ChatResponse, error)

SendVisionRequest returns an error (vision not supported).

func (*MockLLMProvider) SetDebug added in v0.16.18

func (m *MockLLMProvider) SetDebug(debug bool)

SetDebug sets debug mode.

func (*MockLLMProvider) SetModel added in v0.16.18

func (m *MockLLMProvider) SetModel(model string) error

SetModel sets the model name.

func (*MockLLMProvider) SupportsConversationalVision added in v0.16.19

func (m *MockLLMProvider) SupportsConversationalVision() bool

SupportsConversationalVision returns false; the mock never participates in inline multimodal turns.

func (*MockLLMProvider) SupportsVision added in v0.16.18

func (m *MockLLMProvider) SupportsVision() bool

SupportsVision returns false.

func (*MockLLMProvider) VisionCapabilities added in v0.16.20

func (m *MockLLMProvider) VisionCapabilities() api.VisionCapabilities

VisionCapabilities returns the safe defaults. MockLLMProvider never participates in real vision requests, but the method is required by api.ClientInterface — the defaults keep mock-routed requests harmless. SP-103-D3 / AUDIT-GAP-2.

type ModelItem

type ModelItem struct {
	Label         string
	Value         string
	Provider      string
	Model         string
	InputCost     float64
	OutputCost    float64
	LegacyCost    float64
	ContextLength int
	Tags          []string
}

ModelItem represents a model in dropdown selections

type Notification added in v0.16.19

type Notification struct {
	Content   string           // formatted message for the agent
	SessionID string           // bg session or automate session ID
	Kind      NotificationKind // source of the notification
	Timestamp time.Time        // when the notification was queued
}

Notification is a durable completion message queued when a background task finishes. It survives turn boundaries — unlike channel-based injection (InjectInputContext), which loses messages when the forwarder goroutine dies at turn end.

func (Notification) FormatForAgent added in v0.16.19

func (n Notification) FormatForAgent() string

type NotificationKind added in v0.16.19

type NotificationKind string

NotificationKind classifies the source of a background completion notification.

const (
	NotifAutomate       NotificationKind = "automate"
	NotifShellBg        NotificationKind = "shell_bg"
	NotifShellBgTimeout NotificationKind = "shell_bg_timeout"
)

type OOMProbeResult added in v0.16.19

type OOMProbeResult struct {
	NodeCount     int
	TotalRSSBytes uint64
	Timestamp     time.Time
}

OOMProbeResult holds the result of a single OOM probe scan.

type OOMWatchdog added in v0.16.19

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

OOMWatchdog monitors Node.js process count and total RSS via /proc scanning. It alerts BEFORE the kernel OOM-killer fires by publishing events when thresholds are exceeded.

func NewOOMWatchdog added in v0.16.19

func NewOOMWatchdog(eventBus *events.EventBus) *OOMWatchdog

NewOOMWatchdog creates a watchdog with sensible defaults.

func (*OOMWatchdog) Start added in v0.16.19

func (w *OOMWatchdog) Start(ctx context.Context)

Start launches a background goroutine that probes at the configured interval. The goroutine exits when ctx is cancelled.

func (*OOMWatchdog) Stop added in v0.16.19

func (w *OOMWatchdog) Stop()

Stop is a no-op. The watchdog goroutine is driven by context cancellation — call cancel() on the context passed to Start() to stop it. This method exists for API symmetry with other watchdog interfaces.

type OptimizerStore added in v0.16.25

type OptimizerStore interface {
	GetOptimizer() *ConversationOptimizer
	SetOptimizer(*ConversationOptimizer)
}

OptimizerStore manages the conversation optimizer instance.

type OrderedMap added in v0.16.4

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

OrderedMap wraps orderedmap.OrderedMap[string, interface{}] to preserve key insertion order throughout the structured file pipeline. All nested map[string]interface{} values are recursively converted so that ordering is maintained at every depth.

func NewOrderedMap added in v0.16.4

func NewOrderedMap() *OrderedMap

NewOrderedMap creates an empty OrderedMap.

func OrderedMapFromMap added in v0.16.4

func OrderedMapFromMap(m map[string]interface{}) *OrderedMap

OrderedMapFromMap converts a regular map[string]interface{} into an OrderedMap. Keys are sorted alphabetically to provide a deterministic (though not original-source) order. Nested maps and slices are converted recursively. This is intended as a fallback when source order is unavailable.

func ParseJSONOrdered added in v0.16.4

func ParseJSONOrdered(content string) (*OrderedMap, error)

ParseJSONOrdered parses a JSON string into an *OrderedMap, preserving the key order from the source text. Only top-level objects are supported — passing a top-level array or scalar returns an error.

Nested objects are recursively wrapped in *OrderedMap. Arrays become []interface{} slices where any contained objects are also *OrderedMap values.

func ParseYAMLOrdered added in v0.16.4

func ParseYAMLOrdered(content string) (*OrderedMap, error)

ParseYAMLOrdered parses a YAML string into an *OrderedMap, preserving the key order from the source text. The YAML content must represent a mapping (object) at the top level. Nested mappings are recursively wrapped in *OrderedMap so that ordering is maintained at every depth.

This function uses yaml.Node to walk the parsed tree, which preserves the original key ordering from the source document.

func (*OrderedMap) Delete added in v0.16.4

func (om *OrderedMap) Delete(key string)

Delete removes the key from the map.

func (*OrderedMap) Get added in v0.16.4

func (om *OrderedMap) Get(key string) (interface{}, bool)

Get retrieves the value for the given key. The second return value indicates whether the key was present.

func (*OrderedMap) InOrder added in v0.16.4

func (om *OrderedMap) InOrder() []orderedmap.Pair[string, interface{}]

InOrder returns all pairs in insertion order.

func (*OrderedMap) Keys added in v0.16.4

func (om *OrderedMap) Keys() []string

Keys returns all keys in insertion order.

func (*OrderedMap) Len added in v0.16.4

func (om *OrderedMap) Len() int

Len returns the number of key-value pairs.

func (*OrderedMap) Set added in v0.16.4

func (om *OrderedMap) Set(key string, value interface{})

Set stores the key-value pair. If the key already exists its value is replaced but the original insertion position is preserved (matching the underlying library semantics).

func (*OrderedMap) String added in v0.16.4

func (om *OrderedMap) String() string

String returns a human-readable representation useful for debugging.

func (*OrderedMap) ToMap added in v0.16.4

func (om *OrderedMap) ToMap() map[string]interface{}

ToMap converts the OrderedMap to a standard map[string]interface{}. Nested OrderedMap values are recursively converted back. This is useful for compatibility with existing code that expects regular maps.

type OutputBuffer

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

OutputBuffer captures agent output for controlled display

func NewOutputBuffer

func NewOutputBuffer() *OutputBuffer

NewOutputBuffer creates a new output buffer

func (*OutputBuffer) Clear

func (ob *OutputBuffer) Clear()

Clear clears the buffer

func (*OutputBuffer) GetAndClear

func (ob *OutputBuffer) GetAndClear() string

GetAndClear returns the output and clears the buffer

func (*OutputBuffer) GetOutput

func (ob *OutputBuffer) GetOutput() string

GetOutput returns the captured output

func (*OutputBuffer) Print

func (ob *OutputBuffer) Print(args ...interface{})

Print captures output

func (*OutputBuffer) Printf

func (ob *OutputBuffer) Printf(format string, args ...interface{})

Printf captures formatted output

func (*OutputBuffer) Println

func (ob *OutputBuffer) Println(args ...interface{})

Println captures output with newline

type OutputManager

type OutputManager interface {
	SetStreamingEnabled(enabled bool)
	IsStreamingEnabled() bool
	SetStreamingCallback(cb func(string))
	GetStreamingCallback() func(string)
	SetReasoningCallback(cb func(string))
	GetReasoningCallback() func(string)
	SetFlushCallback(cb func())
	GetFlushCallback() func()
	SetOutputMutex(mu *sync.Mutex)
	GetOutputMutex() *sync.Mutex
	GetStreamingBuffer() *strings.Builder
	GetReasoningBuffer() *strings.Builder
	GetOutputRouter() *OutputRouter
	SetOutputRouter(router *OutputRouter)
	GetAsyncOutput() chan string
	SetAsyncOutput(ch chan string)
	EnsureAsyncOutputWorker(fn func())
	GetAsyncBufferSize() int
	SetAsyncBufferSize(size int)
	GetEventMetadata() map[string]interface{}
	SetEventMetadata(meta map[string]interface{})
	SetEventMetadataUnlocked(meta map[string]interface{})
	GetEventMetadataMutex() *sync.RWMutex
	SetTerminalWriter(fn func(string))
	GetTerminalWriter() func(string)
}

OutputManager manages all output and streaming-related state for an Agent.

type OutputMode

type OutputMode int

OutputMode determines how output is routed

const (
	OutputModeTerminal     OutputMode = iota // CLI-only, no event bus
	OutputModeEventSourced                   // EventBus + terminal bridge
)

type OutputRouter

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

OutputRouter is the single routing point for all agent output. Instead of dual-writing (publish event + print to terminal), all output flows through this router which handles both paths.

Terminal output is ALWAYS produced via the streamingCallback (when set) or via fmt.Print (fallback). The streamingCallback is the terminal display — it is NOT a WebUI path. The event bus is the WebUI path.

When the event bus is set, events are published for WebUI subscribers AND the terminal still receives its output. This is by design: the terminal always shows output; the WebUI optionally shows it via events.

func NewOutputRouter

func NewOutputRouter(agent *Agent, eventBus *events.EventBus) *OutputRouter

NewOutputRouter creates an output router. If eventBus is nil, operates in terminal-only mode. agent may be nil during early initialization; set it later via the field directly.

func (*OutputRouter) Mode

func (r *OutputRouter) Mode() OutputMode

Mode returns the current output mode

func (*OutputRouter) RouteAgentMessage

func (r *OutputRouter) RouteAgentMessage(category, message string, extra map[string]interface{})

RouteAgentMessage routes an agent system message. category: "info", "warning", "error", "tool_log", "thought" RouteAgentMessage routes a message for display in both the WebUI and terminal.

func (*OutputRouter) RouteStreamChunk

func (r *OutputRouter) RouteStreamChunk(chunk string, contentType string)

RouteStreamChunk routes a streaming chunk to the event bus and, when allowed, to terminal output.

Streaming chunks are special: they represent the character-by-character output of the assistant's response. For terminal display, they go through the streamingCallback (real-time output). For WebUI, they are published as stream_chunk events. Reasoning chunks are published but hidden from terminal output unless explicitly enabled.

func (*OutputRouter) RouteTerminalOnly

func (r *OutputRouter) RouteTerminalOnly(message string)

RouteTerminalOnly writes a message directly to the terminal without publishing to the event bus. Use this for output that is already published via a separate, more specific event type (e.g., subagent output lines that are published as subagent_activity events).

func (*OutputRouter) RouteToolCompletion

func (r *OutputRouter) RouteToolCompletion(ok bool, duration time.Duration, errMsg string)

RouteToolCompletion emits the inline duration / outcome chip that follows a tool-log line. Kept separate from RouteToolLog because tool_start fires before the work begins and tool_end fires after — the two are paired by toolCallID at the call site (richEventPublisher / tool_executor).

Format: ` ✓ 124ms` (indented under the prior tool-log line, dim green). On failure: ` ✗ 124ms — <short error>`.

func (*OutputRouter) RouteToolLog

func (r *OutputRouter) RouteToolLog(action string, target string)

RouteToolLog routes a tool execution log message with iteration and context info.

Terminal rendering: a glyph-prefixed dim line. The iter/context info is kept on the WebUI event (for the activity feed) but elided from the terminal — that data already lives on the status footer, and pulling it into every tool-log line just adds noise. Format:

→ shell_command ls -la /path

func (*OutputRouter) SetEventBus

func (r *OutputRouter) SetEventBus(eventBus *events.EventBus)

SetEventBus updates the event bus (called when webui connects/disconnects). The streamingCallback on the agent is NOT affected — it always routes to the terminal regardless of WebUI state.

func (*OutputRouter) SetExternalWriteHook

func (r *OutputRouter) SetExternalWriteHook(fn func())

SetExternalWriteHook registers a callback that fires before every writeTerminalMessage emission. Pass nil to clear. Intended for the AssistantTurnRenderer in pkg/console to break its prose segment when chrome (tool logs / agent messages) interrupts the model's stream.

func (*OutputRouter) SetReasoningCallback added in v0.16.2

func (r *OutputRouter) SetReasoningCallback(fn func(string))

SetReasoningCallback registers a dedicated sink for reasoning chunks so the CLI can render thinking-stream output as a collapsed header instead of mixing it into the prose stream. When unset (the default), reasoning chunks fall through to the regular streamingCallback if SetReasoningTerminalEnabled is true; otherwise they're suppressed from the terminal entirely (the historic behaviour).

Pass nil to clear. The callback is invoked synchronously from RouteStreamChunk; the caller is responsible for any locking it needs internally.

func (*OutputRouter) SetReasoningTerminalEnabled

func (r *OutputRouter) SetReasoningTerminalEnabled(enabled bool)

SetReasoningTerminalEnabled controls whether reasoning chunks are rendered in the terminal. It is disabled by default so reasoning stays available to the event bus/WebUI without polluting normal CLI output.

func (*OutputRouter) SetTerminalSubscriberActive added in v0.16.19

func (r *OutputRouter) SetTerminalSubscriberActive(active bool)

SetTerminalSubscriberActive marks whether a terminal subscriber owns rendering agent_message events. When true, RouteAgentMessage skips the raw writeTerminalMessage fallback (the subscriber renders the line with proper glyph prefixing). Called by startTerminalToolSubscriber in interactive/queue modes; left false in direct mode.

func (*OutputRouter) TerminalSubscriberActive added in v0.16.25

func (r *OutputRouter) TerminalSubscriberActive() bool

TerminalSubscriberActive reports whether a terminal subscriber owns terminal rendering. Callers that render their own completion / summary lines (e.g. cmd/agent_query.go ProcessQuery) use this to suppress their duplicate output when the subscriber will render it instead — the same gate that protects RouteAgentMessage from double-printing.

func (*OutputRouter) Write added in v0.16.19

func (r *OutputRouter) Write(p []byte) (int, error)

Write implements io.Writer so OutputRouter can be used directly as the OutputWriter in tools.ToolEnv. It buffers partial lines and flushes them on newline boundaries via the agent's PrintLineAsync, avoiding the need to allocate a separate outputRouter wrapper per tool call.

type PathTier

type PathTier int

PathTier classifies a filesystem path for approval purposes. See ClassifyPathAccess for the resolution rules. The tiers are ordered from least to most restrictive.

const (
	// PathTierUnknown is the zero value and shouldn't be returned;
	// it exists so a missing tier shows up loudly in tests rather
	// than silently behaving as "allow".
	PathTierUnknown PathTier = iota

	// PathTierWorkspace — the path is inside the agent's workspace
	// root (or the sprout config dir). No approval required.
	PathTierWorkspace

	// PathTierExternal — outside the workspace but not in a system
	// or off-CWD home directory. Eligible for the "Allow this folder
	// for the rest of the session" approval choice. Once a parent
	// folder is in the agent's session allowlist, future accesses
	// under it auto-approve.
	PathTierExternal

	// PathTierSensitive — system directories (/etc, /usr, ...) OR
	// home-directory paths when the agent's CWD is outside the user's
	// home. These ALWAYS prompt and CANNOT be added to the session
	// allowlist. The "Allow folder this session" choice is hidden
	// from the dialog for this tier.
	PathTierSensitive
)

func ClassifyPathAccess

func ClassifyPathAccess(path, workspaceRoot, homeDir, cwd string) PathTier

ClassifyPathAccess decides which approval tier a path falls into. All three input paths should be absolute (or empty for unset fields). Behavior:

  1. Inside workspaceRoot → PathTierWorkspace.
  2. Under a known system directory (e.g. /etc, /usr, /var on Unix; C:\Windows, C:\Program Files on Windows) → PathTierSensitive.
  3. Under the user's home dir AND the agent's CWD is NOT under home → PathTierSensitive (working in /tmp, accessing ~ is unusual and shouldn't get session-wide allowlisting).
  4. Anything else outside the workspace → PathTierExternal.

homeDir and cwd are passed explicitly so tests can drive each branch deterministically. Production callers use os.UserHomeDir and the agent's effective CWD.

Symlinks: this classifier compares cleaned path strings; it does NOT call filepath.EvalSymlinks. The filesystem layer (pkg/filesystem) resolves symlinks before returning ErrOutsideWorkingDirectory, so the path we receive here is already the symlink-resolved target — the comparison is correct for cases where the filesystem layer hands us a real path. For non-filesystem callers (e.g. the WebUI file API consulting IsFolderSessionAllowed), the path is whatever the caller resolved. The classifier is therefore advisory: if you've crafted a clever symlink to dodge tier classification, the filesystem layer still enforces its own checks at write time.

func (PathTier) String

func (t PathTier) String() string

String returns a stable lowercase identifier used in dialog extras (the WebUI uses it to pick a button set) and tests.

type PauseState

type PauseState struct {
	IsPaused       bool          `json:"is_paused"`
	PausedAt       time.Time     `json:"paused_at"`
	OriginalTask   string        `json:"original_task"`
	Clarifications []string      `json:"clarifications"`
	MessagesBefore []api.Message `json:"messages_before"`
}

PauseState tracks the state when a task is paused for clarification

type PauseStore added in v0.16.25

type PauseStore interface {
	GetPauseState() *PauseState
	SetPauseState(*PauseState)
	GetPauseMutex() *sync.Mutex
}

PauseStore manages pause state and its mutex.

type PendingStateStore added in v0.16.25

type PendingStateStore interface {
	GetPendingSwitchContextRefresh() string
	SetPendingSwitchContextRefresh(string)
	GetPendingStrictSwitchNotice() string
	SetPendingStrictSwitchNotice(string)
	GetPendingSystemSupplement() string
	SetPendingSystemSupplement(string)
}

PendingStateStore manages pending state that will be applied on the next turn.

type PersonaStore added in v0.16.25

type PersonaStore interface {
	GetActiveSkills() []string
	SetActiveSkills([]string)
	GetActivePersona() string
	SetActivePersona(string)
}

PersonaStore manages active skills and persona.

type ProactiveContextConfig

type ProactiveContextConfig struct {
	// MinRelevanceScore is the minimum time-decayed similarity score required
	// for a result to be included. Default: 0.50.
	MinRelevanceScore float64

	// MaxContextualResults caps the number of results returned. Default: 5.
	MaxContextualResults int

	// MaxContextChars is the character budget for FormatProactiveContext.
	// The formatted string is truncated at this limit. Default: 4000.
	MaxContextChars int

	// WorkspaceScoped, if true, filters to turns from the same workingDir.
	// Default: true.
	//
	// Cross-workspace bleed has been a real foot-gun (e.g. a fresh session
	// in repo A pulling in semantically-similar turns from repo B and the
	// model treating them as actionable).  Past work from other workspaces
	// is almost always noise; users who genuinely want cross-workspace
	// recall can opt in by setting WorkspaceScoped: false (or via the
	// PersistentContextConfig override hook in SP-027-2d).
	WorkspaceScoped bool

	// RetentionDays controls how many days to keep persistent context entries.
	// Default: 0 (forever, never expire).
	RetentionDays int
}

ProactiveContextConfig holds configuration for proactive context retrieval. These defaults will later be backed by PersistentContextConfig in pkg/configuration (SP-027-2d).

func DefaultProactiveContextConfig

func DefaultProactiveContextConfig() ProactiveContextConfig

DefaultProactiveContextConfig returns a ProactiveContextConfig with the standard defaults specified in SP-027.

type ProactiveContextResult

type ProactiveContextResult struct {
	Record embedding.VectorRecord
	Score  float64 // time-decayed cosine similarity
}

ProactiveContextResult holds a retrieved conversation turn with its time-decayed similarity score.

func RetrieveProactiveContext

func RetrieveProactiveContext(
	ctx context.Context,
	mgr *embedding.EmbeddingManager,
	config ProactiveContextConfig,
	query string,
	workingDir string,
	now time.Time,
) ([]ProactiveContextResult, error)

RetrieveProactiveContext retrieves relevant conversation turns from the conversation store based on semantic similarity with time-decay scoring.

The retrieval pipeline:

  1. Embed the query text using the static provider
  2. Query the HNSW index for the top-K nearest neighbors (O(log N))
  3. Filter to Type=="conversation_turn" and optionally by working directory
  4. Re-score with cosine similarity × 30-day half-life decay
  5. Filter by MinRelevanceScore, sort descending, cap at MaxContextualResults

If the HNSW query returns no same-workspace matches (e.g. when the relevant turns rank beyond the top-K window under raw cosine), the pipeline falls back to a brute-force LoadAll scan for stores under 2000 records — preserving the exact-match recall of the pre-HNSW path where the O(N) cost is negligible. Larger stores rely on HNSW alone.

Graceful degradation: all errors are logged and nil/empty is returned. The agent should never be blocked by a retrieval failure.

type ProgressEntry

type ProgressEntry struct {
	OffsetMS int64  `json:"offset_ms"`
	Phase    string `json:"phase"`
	Message  string `json:"message"`
}

ProgressEntry is the envelope-facing form of SubagentProgressEntry, kept separately from the runner-internal type so the runner struct can change without affecting the wire shape.

type ProjectInfo

type ProjectInfo struct {
	Path        string   // Absolute path to project root
	Name        string   // Directory name or project name from AGENTS.md
	Description string   // First paragraph from AGENTS.md if present
	HasAgentsMd bool     // Whether project has AGENTS.md
	HasGitRepo  bool     // Whether project has .git directory
	Languages   []string // Detected languages (from file extensions, go.mod, package.json, etc.)
	RelPath     string   // Relative path from home directory
}

func DiscoverProjects

func DiscoverProjects(homeDir string, maxDepth int) ([]ProjectInfo, error)

type PromptTokensDetails

type PromptTokensDetails struct {
	CachedTokens     int  `json:"cached_tokens"`
	CacheWriteTokens *int `json:"cache_write_tokens"`
}

PromptTokensDetails contains detailed breakdown of prompt tokens

type ProviderErrorInfo

type ProviderErrorInfo struct {
	Timestamp  string `json:"timestamp"`             // ISO 8601 when the error occurred
	Provider   string `json:"provider"`              // e.g. "zai", "openrouter"
	Model      string `json:"model"`                 // e.g. "glm-5.1"
	StatusCode int    `json:"status_code,omitempty"` // HTTP status code (400, 429, 500, etc.)
	ErrorType  string `json:"error_type,omitempty"`  // e.g. "api_error_400", "streaming_response"
	Message    string `json:"message"`               // The error message from the provider
	Retries    int    `json:"retries,omitempty"`     // Number of retries attempted
}

ProviderErrorInfo captures details about the last API error from the LLM provider. This is persisted in the session file so errors can be diagnosed after the fact.

type ProviderErrorStore added in v0.16.25

type ProviderErrorStore interface {
	GetLastProviderError() *ProviderErrorInfo
	SetLastProviderError(*ProviderErrorInfo)
}

ProviderErrorStore manages the last provider error information.

type PruningStrategy

type PruningStrategy = core.PruningStrategy

PruningStrategy is aliased to seed's strategy type. Sprout's constants below mirror seed's so call sites need no rewrite.

type QuickOption

type QuickOption struct {
	Label string
	Value string
}

QuickOption represents a quick choice option

type RateLimitExceededError

type RateLimitExceededError struct {
	Attempts  int
	LastError error
}

RateLimitExceededError indicates repeated rate limit failures even after retries. This type is referenced by the scripted test client and must be available outside of the (now-deleted) APIClient file.

func (*RateLimitExceededError) Error

func (e *RateLimitExceededError) Error() string

func (*RateLimitExceededError) Unwrap

func (e *RateLimitExceededError) Unwrap() error

type RecallMetricsRecord added in v0.16.19

type RecallMetricsRecord struct {
	Timestamp       string   `json:"timestamp"` // RFC3339
	SessionID       string   `json:"session_id,omitempty"`
	ItemsRecalled   int      `json:"items_recalled"`
	TopSimilarity   float64  `json:"top_similarity"`
	UsedInResponse  bool     `json:"used_in_response"`
	CheckpointIDs   []string `json:"checkpoint_ids,omitempty"`
	Workspaces      []string `json:"workspaces,omitempty"`
	RecallLatencyMS int64    `json:"recall_latency_ms"`
	RecallQuery     string   `json:"recall_query,omitempty"` // first 200 runes, for debugging
}

RecallMetricsRecord is the per-turn entry persisted to ~/.config/sprout/recall_metrics.jsonl. Fire-and-forget — the instrumentation never blocks the agent loop on file IO.

type RecalledItem added in v0.16.4

type RecalledItem struct {
	CheckpointID string
	Level        int
	StartIndex   int
	EndIndex     int
	Similarity   float32
	AgeDays      float64
	Score        float64
	Summary      string
	Actionable   string
	Workspace    string
}

RecalledItem is one historical summary retrieved by the semantic recall pass and surfaced to the model on the next prompt. It carries both the scored numbers (for telemetry) and the text the prompt will render.

type ReconciliationActionResult

type ReconciliationActionResult struct {
	FilePath     string                   `json:"file_path"`
	Action       ReconciliationActionType `json:"action"`
	ContainerSeq int64                    `json:"container_seq"`
	BrowserSeq   int64                    `json:"browser_seq"`
}

ReconciliationActionResult is the per-file reconciliation outcome.

func ReconcileSeqNumbers

func ReconcileSeqNumbers(ag *Agent, browserSeqs map[string]int64) ([]ReconciliationActionResult, error)

ReconcileSeqNumbers compares browser-supplied per-file sequence numbers against the container's stored metadata and returns a reconciliation plan. The browser sends {file_path: browser_seq}; for each file, we compare the browser's seq with the container's seq to determine the action.

Rules:

  • browser_seq == container_seq → sync_ok
  • browser_seq == last_synced_container AND container_seq > browser_seq → container_ahead
  • browser_seq > last_synced_browser → browser_ahead (unsynced browser edits)
  • both sides diverged → diverged

type ReconciliationActionType

type ReconciliationActionType string

ReconciliationActionType enumerates the possible outcomes of comparing browser and container sequence numbers for a single file.

const (
	// ReconcileSyncOK means browser and container are at the same seq.
	ReconcileSyncOK ReconciliationActionType = "sync_ok"
	// ReconcileContainerAhead means the container has patches the browser hasn't seen.
	ReconcileContainerAhead ReconciliationActionType = "container_ahead"
	// ReconcileBrowserAhead means the browser has edits the container hasn't applied.
	ReconcileBrowserAhead ReconciliationActionType = "browser_ahead"
	// ReconcileDiverged means both sides have diverged and conflict resolution is needed.
	ReconcileDiverged ReconciliationActionType = "diverged"
)

type RetryAction

type RetryAction int

RetryAction represents the action to take when a tool error occurs.

const (
	// ActionRetry indicates the error is transient and the tool call should be retried.
	// Covers TransientError, RateLimitError, retryable ProviderError, and unknown/untyped errors.
	ActionRetry RetryAction = iota
	// ActionFail indicates the error is permanent and should not be retried.
	ActionFail
	// ActionEscalate indicates the error needs human/LLM review before proceeding.
	ActionEscalate
)

func ClassifyError

func ClassifyError(err error) RetryAction

ClassifyError examines an error and returns the appropriate RetryAction.

It uses typed error checks from pkg/errors (errors.As via helper functions) rather than string matching on error messages. This provides more reliable classification as the error types are structural rather than text-based.

Classification rules (checked in priority order):

  • SecurityError → ActionEscalate (ask user/LLM)
  • TransientError → ActionRetry (with backoff)
  • RateLimitError → ActionRetry (with longer backoff)
  • InvalidInputError → ActionFail (fix the input)
  • ContextError (ContextOverflow) → ActionFail (need context compaction)
  • ProviderError → ActionFail (auth/config) or ActionRetry (server errors) depending on Retryable
  • PermanentError → ActionFail
  • Retryable AgentError → ActionRetry
  • Default (unknown/untyped errors) → ActionRetry once, then ActionFail

func (RetryAction) String

func (a RetryAction) String() string

String returns a human-readable name for the retry action.

type RewindOptions added in v0.16.12

type RewindOptions struct {
	ToTurnIndex int  // 0-based: rewind to BEFORE this turn's messages (truncate at the start of this turn)
	RevertFiles bool // default true: revert file changes from discarded turns
}

RewindOptions configures a rewind operation.

type RewindResult added in v0.16.12

type RewindResult struct {
	TurnsDiscarded     int      // number of turns removed
	MessagesRemoved    int      // number of messages removed from the history
	FilesReverted      []string // files that were reverted
	FilesSkipped       []string // files that could NOT be reverted (modified outside agent)
	CheckpointsDropped int      // orphaned checkpoints removed
}

RewindResult reports what a rewind operation did.

type RiskAssessment added in v0.16.7

type RiskAssessment struct {
	// Level is the canonical risk on the Low/Medium/High/Critical scale.
	Level configuration.RiskLevel

	// IsHardBlock is true for critical-tier operations that no approval can
	// override (rm -rf /, fork bombs, mkfs).
	IsHardBlock bool

	// RequiresIntentConfirmation marks a safe-but-consequential operation
	// (e.g. launching an autonomous workflow) that needs explicit user
	// intent. It is orthogonal to Level — such an op is Low risk but still
	// gated on intent.
	RequiresIntentConfirmation bool

	// Sources lists every check that contributed, in precedence order.
	Sources []RiskSource

	// Reason is a human-readable explanation of the verdict.
	Reason string
}

RiskAssessment is the canonical, single-vocabulary verdict for a tool call. Phase 2 makes it the single output of the unified resolver; Phase 1 builds and tests it alongside the existing gates.

func (RiskAssessment) Explain added in v0.16.7

func (ra RiskAssessment) Explain() string

Explain renders a one-line human-readable summary of the assessment for diagnostics ("why was this gated?"). Sources are listed alphabetically for a stable rendering regardless of combination order.

type RiskSource added in v0.16.7

type RiskSource string

RiskSource identifies which check contributed to an assessment, so a decision can be explained (Phase 3 `sprout explain`) instead of being an opaque "blocked".

const (
	// RiskSourceClassifier — the static, string-based classifier
	// (pkg/agent_tools.ClassifyToolCall).
	RiskSourceClassifier RiskSource = "classifier"
	// RiskSourcePersonaCascade — the persona / risk-profile cascade
	// (Agent.EvaluateOperationRisk).
	RiskSourcePersonaCascade RiskSource = "persona-cascade"
	// RiskSourceCriticalOp — the built-in critical-operation hard-block
	// (configuration.IsCriticalOperation).
	RiskSourceCriticalOp RiskSource = "critical-op"
	// RiskSourceGitHistoryRewrite — git commands that can lose commit history
	RiskSourceGitHistoryRewrite RiskSource = "git-history-rewrite"
	// RiskSourceGitWrite — git write operations not allowed by persona
	RiskSourceGitWrite RiskSource = "git-write"
	// RiskSourceFSTier — filesystem path-tier classification (Sensitive/External)
	RiskSourceFSTier RiskSource = "fs-tier"
	// RiskSourceWorkspacePolicy — workspace security policy evaluation
	RiskSourceWorkspacePolicy RiskSource = "workspace-policy"
	// RiskSourceHandler — a security error raised by a tool handler at
	// execution time (not by the pre-execute gate). Used when the only
	// signal available is the typed SecurityError returned by the handler.
	RiskSourceHandler RiskSource = "handler"
	// RiskSourcePasswordPrompter — password prompter is registered, so
	// privileged commands (sudo, passwd) are downgraded from block to prompt.
	RiskSourcePasswordPrompter RiskSource = "password-prompter"
)

type ScriptedClient

type ScriptedClient struct {
	*factory.TestClient
	// contains filtered or unexported fields
}

ScriptedClient is an enhanced mock client for comprehensive E2E testing It supports: - Sequential scripted responses with tool calls - Streaming simulation - Error injection - Vision support - Rate limit simulation

func NewScriptedClient

func NewScriptedClient(responses ...*ScriptedResponse) *ScriptedClient

NewScriptedClient creates a new scripted client with optional initial responses

func NewScriptedClientWithVision

func NewScriptedClientWithVision(model string, responses ...*ScriptedResponse) *ScriptedClient

NewScriptedClientWithVision creates a scripted client that supports vision models

func (*ScriptedClient) AddResponse

func (c *ScriptedClient) AddResponse(response *ScriptedResponse)

AddResponse appends a response to the end of the queue

func (*ScriptedClient) AdvanceIndex

func (c *ScriptedClient) AdvanceIndex()

AdvanceIndex advances to the next response

func (*ScriptedClient) Cancel

func (c *ScriptedClient) Cancel()

Cancel cancels any pending operations

func (*ScriptedClient) CheckConnection

func (c *ScriptedClient) CheckConnection() error

CheckConnection always returns nil for test client

func (*ScriptedClient) ClearHistory

func (c *ScriptedClient) ClearHistory()

ClearHistory clears the response history

func (*ScriptedClient) ClearSentRequests

func (c *ScriptedClient) ClearSentRequests()

ClearSentRequests clears all recorded sent requests

func (*ScriptedClient) Close

func (c *ScriptedClient) Close()

Close closes the client and releases resources

func (*ScriptedClient) GetAverageTPS

func (c *ScriptedClient) GetAverageTPS() float64

GetAverageTPS returns the average tokens per second

func (*ScriptedClient) GetIndex

func (c *ScriptedClient) GetIndex() int

GetIndex returns the current response index

func (*ScriptedClient) GetLastTPS

func (c *ScriptedClient) GetLastTPS() float64

GetLastTPS returns the last tokens per second

func (*ScriptedClient) GetModel

func (c *ScriptedClient) GetModel() string

GetModel returns the current model

func (*ScriptedClient) GetModelContextLimit

func (c *ScriptedClient) GetModelContextLimit() (int, error)

GetModelContextLimit returns the context limit

func (*ScriptedClient) GetNextResponse

func (c *ScriptedClient) GetNextResponse() *ScriptedResponse

GetNextResponse returns the next response without advancing the index

func (*ScriptedClient) GetProvider

func (c *ScriptedClient) GetProvider() string

GetProvider returns the provider name

func (*ScriptedClient) GetSentRequest

func (c *ScriptedClient) GetSentRequest(index int) []api.Message

GetSentRequest returns a specific request's messages (nil if out of range)

func (*ScriptedClient) GetSentRequests

func (c *ScriptedClient) GetSentRequests() [][]api.Message

GetSentRequests returns a defensive deep copy of all recorded request message arrays. Both the outer slice and each inner []api.Message slice are copied to prevent external mutation of the client's internal state.

func (*ScriptedClient) GetTPSStats

func (c *ScriptedClient) GetTPSStats() map[string]float64

GetTPSStats returns TPS statistics

func (*ScriptedClient) GetVisionModel

func (c *ScriptedClient) GetVisionModel() string

GetVisionModel returns the vision model name

func (*ScriptedClient) LastResponse

func (c *ScriptedClient) LastResponse() *ScriptedResponse

LastResponse returns the last consumed response

func (*ScriptedClient) Length

func (c *ScriptedClient) Length() int

Length returns the number of scripted responses

func (*ScriptedClient) ListModels

func (c *ScriptedClient) ListModels(ctx context.Context) ([]api.ModelInfo, error)

ListModels returns available models

func (*ScriptedClient) Reset

func (c *ScriptedClient) Reset()

Reset resets the response index to the beginning

func (*ScriptedClient) ResetTPSStats

func (c *ScriptedClient) ResetTPSStats()

ResetTPSStats resets TPS statistics

func (*ScriptedClient) ResponseHistory

func (c *ScriptedClient) ResponseHistory() []*ScriptedResponse

ResponseHistory returns all consumed responses

func (*ScriptedClient) SendChatRequest

func (c *ScriptedClient) SendChatRequest(ctx context.Context, messages []api.Message, tools []api.Tool, reasoning string, disableThinking bool) (*api.ChatResponse, error)

SendChatRequest sends a chat request and returns a scripted response

func (*ScriptedClient) SendChatRequestStream

func (c *ScriptedClient) SendChatRequestStream(ctx context.Context, messages []api.Message, tools []api.Tool, reasoning string, disableThinking bool, callback api.StreamCallback) (*api.ChatResponse, error)

SendChatRequestStream sends a streaming chat request with full simulation support

func (*ScriptedClient) SendVisionRequest

func (c *ScriptedClient) SendVisionRequest(ctx context.Context, messages []api.Message, tools []api.Tool, reasoning string, disableThinking bool) (*api.ChatResponse, error)

SendVisionRequest sends a vision-enabled chat request

func (*ScriptedClient) SetDebug

func (c *ScriptedClient) SetDebug(debug bool)

SetDebug enables debug mode

func (*ScriptedClient) SetIndex

func (c *ScriptedClient) SetIndex(idx int)

SetIndex sets the response index (useful for replay scenarios)

func (*ScriptedClient) SetModel

func (c *ScriptedClient) SetModel(model string) error

SetModel sets the model name

func (*ScriptedClient) SetResponses

func (c *ScriptedClient) SetResponses(responses []*ScriptedResponse)

SetResponses replaces all responses and resets all derived state.

func (*ScriptedClient) SupportsConversationalVision added in v0.16.19

func (c *ScriptedClient) SupportsConversationalVision() bool

SupportsConversationalVision reports whether inline multimodal turns should embed the image. Defaults to false; overridden per client.

func (*ScriptedClient) SupportsVision

func (c *ScriptedClient) SupportsVision() bool

SupportsVision returns whether vision is supported

type ScriptedResponse

type ScriptedResponse struct {
	// Message content to return
	Content string

	// Tool calls to include in the response
	ToolCalls []api.ToolCall

	// Finish reason for the choice
	FinishReason string

	// Reasoning content (for models that support it)
	ReasoningContent string

	// Images to include (vision support)
	Images []api.ImageData

	// Delay before returning the response (for rate limit simulation)
	Delay time.Duration

	// Error to return instead of a response
	Error error

	// Rate limit simulation: return rate limit error after N successful responses
	RateLimitAfter int

	// Stream configuration
	StreamConfig *StreamConfig

	// Whether this response should be used for vision requests
	VisionOnly bool

	// Token usage metrics for this response
	Usage ScriptedTokenUsage
}

ScriptedResponse represents a single scripted response with full configuration options

func NewErrorResponse

func NewErrorResponse(err error) *ScriptedResponse

NewErrorResponse creates a response that returns an error

func NewKeepGoingResponse

func NewKeepGoingResponse(content string) *ScriptedResponse

NewKeepGoingResponse creates a keep-going response (empty finish_reason)

func NewLengthResponse

func NewLengthResponse(content string) *ScriptedResponse

NewLengthResponse creates a length finish_reason response

func NewRateLimitResponse

func NewRateLimitResponse() *ScriptedResponse

NewRateLimitResponse creates a response that simulates rate limiting

func NewStopResponse

func NewStopResponse(content string) *ScriptedResponse

NewStopResponse creates a stop response

func NewTimeoutResponse

func NewTimeoutResponse() *ScriptedResponse

NewTimeoutResponse creates a response with a timeout error

func NewToolCallResponse

func NewToolCallResponse(name, args string, toolCalls ...api.ToolCall) *ScriptedResponse

NewToolCallResponse creates a response with tool calls

type ScriptedResponseBuilder

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

ScriptedResponseBuilder provides a fluent interface for building ScriptedResponse

func NewScriptedResponseBuilder

func NewScriptedResponseBuilder() *ScriptedResponseBuilder

NewScriptedResponseBuilder creates a new response builder

func (*ScriptedResponseBuilder) Build

Build returns the constructed ScriptedResponse

func (*ScriptedResponseBuilder) Content

Content sets the message content

func (*ScriptedResponseBuilder) Delay

Delay sets the delay before returning the response

func (*ScriptedResponseBuilder) Error

Error sets an error to be returned instead of a response

func (*ScriptedResponseBuilder) FinishReason

func (b *ScriptedResponseBuilder) FinishReason(reason string) *ScriptedResponseBuilder

FinishReason sets the finish reason

func (*ScriptedResponseBuilder) Images

Images sets the images for vision support

func (*ScriptedResponseBuilder) RateLimitAfter

func (b *ScriptedResponseBuilder) RateLimitAfter(n int) *ScriptedResponseBuilder

RateLimitAfter configures rate limit simulation

func (*ScriptedResponseBuilder) ReasoningContent

func (b *ScriptedResponseBuilder) ReasoningContent(content string) *ScriptedResponseBuilder

ReasoningContent sets reasoning content for models that support it

func (*ScriptedResponseBuilder) StreamConfig

StreamConfig sets streaming configuration

func (*ScriptedResponseBuilder) ToolCall

ToolCall adds a single tool call

func (*ScriptedResponseBuilder) ToolCalls

ToolCalls sets multiple tool calls

func (*ScriptedResponseBuilder) Usage

func (b *ScriptedResponseBuilder) Usage(promptTokens, completionTokens, totalTokens int, estimatedCost float64) *ScriptedResponseBuilder

Usage sets the token usage metrics for this response

func (*ScriptedResponseBuilder) VisionOnly

VisionOnly marks this response for vision-only requests

type ScriptedTokenUsage

type ScriptedTokenUsage struct {
	PromptTokens        int                 `json:"prompt_tokens"`
	CompletionTokens    int                 `json:"completion_tokens"`
	TotalTokens         int                 `json:"total_tokens"`
	EstimatedCost       float64             `json:"estimated_cost"`
	Cost                float64             `json:"cost,omitempty"`
	PromptTokensDetails PromptTokensDetails `json:"prompt_tokens_details,omitempty"`
}

ScriptedTokenUsage represents token usage metrics for a scripted response

type SecurityManager

type SecurityManager interface {
	GetSecurityApprovalMgr() *security.ApprovalManager
	SetApprovalMgr(mgr *security.ApprovalManager)
	SetAskUserMgr(mgr *agenttools.AskUserManager)
	GetAskUserMgr() *agenttools.AskUserManager
	SetUnsafeMode(unsafe bool)
	GetUnsafeMode() bool
	SetUnsafeShellMode(unsafe bool)
	GetUnsafeShellMode() bool

	// IsSecurityBypassApproved reports whether the user has approved
	// any external filesystem access this session. After the SP-058
	// follow-up this returns true iff at least one folder is on the
	// session allowlist — used as a coarse "user has consented to
	// external access" signal by subagent setup. Per-path decisions
	// should call IsFolderSessionAllowed instead.
	//
	// Deprecated: use IsFolderSessionAllowed(absPath) for per-path
	// checks. SetSecurityBypassApproved is gone — call
	// AddSessionAllowedFolder with the specific folder instead.
	IsSecurityBypassApproved() bool

	// IsFolderSessionAllowed reports whether absPath sits under any
	// folder the user has allowlisted for this session. Match is
	// prefix-based (path-component aware) and case-sensitive on Unix.
	IsFolderSessionAllowed(absPath string) bool

	// AddSessionAllowedFolder records that the user picked "Allow
	// this folder for the rest of the session" on the approval
	// dialog. The folder is stored after Clean()-ing and dedup'd
	// against the existing list.
	AddSessionAllowedFolder(folder string)

	// SnapshotSessionAllowedFolders returns a copy of the current
	// allowlist. Used to propagate approvals into subagents (each
	// subagent gets its own allowlist seeded from the parent's).
	SnapshotSessionAllowedFolders() []string

	IsConcernIgnored(filePath, concern string) bool
	SetConcernIgnored(filePath, concern string)
	GetOutputRedactor() *security.OutputRedactor
	GetElevationGate() *security.ElevationGate
	SetElevationGate(gate *security.ElevationGate)
	SetHasActiveWebUIClients(fn func() bool)
	HasActiveWebUIClients() bool
}

SecurityManager provides an interface for managing all security-related state.

type SessionConfigStore added in v0.16.25

type SessionConfigStore interface {
	GetSessionProvider() api.ClientType
	SetSessionProvider(api.ClientType)
	GetSessionModel() string
	SetSessionModel(string)
}

SessionConfigStore manages per-session provider and model configuration.

type SessionInfo

type SessionInfo struct {
	SessionID        string    `json:"session_id"`
	LastUpdated      time.Time `json:"last_updated"`
	Name             string    `json:"name"`              // Human-readable session name
	WorkingDirectory string    `json:"working_directory"` // Directory where session was created
	StoragePath      string    `json:"storage_path,omitempty"`
}

SessionInfo represents session information with timestamp

func ListAllSessionsWithTimestamps

func ListAllSessionsWithTimestamps() ([]SessionInfo, error)

ListAllSessionsWithTimestamps returns all available sessions across all scopes.

func ListSessionsWithTimestamps

func ListSessionsWithTimestamps() ([]SessionInfo, error)

ListSessionsWithTimestamps returns sessions for the current working directory scope.

func ListSessionsWithTimestampsScoped

func ListSessionsWithTimestampsScoped(workingDir string) ([]SessionInfo, error)

ListSessionsWithTimestampsScoped returns sessions only for the given working directory scope.

type SessionIntentStore added in v0.16.25

type SessionIntentStore interface {
	GetSessionIntentEmbedding() []float32
	SetSessionIntentEmbedding([]float32)
	SetSessionIntentEmbeddingIfNil(emb []float32) bool
}

SessionIntentStore manages the session intent embedding vector.

type SessionItem

type SessionItem struct {
	Label       string
	Value       string
	SessionID   string
	Model       string
	LastUpdated time.Time
	Name        string // Human-readable session name
}

SessionItem represents a session in dropdown selections

type SessionStore added in v0.16.25

type SessionStore interface {
	GetSessionID() string
	SetSessionID(string)
}

SessionStore manages the session identifier.

type SettingDetail added in v0.16.19

type SettingDetail struct {
	Key         string
	Description string
	ValidValues string
	GetValue    func(cfg *configuration.Config) string
	ListType    bool // true for comma-separated list settings (add/remove/set UI)
}

SettingDetail holds metadata for a setting key used by describe and describe_all.

func AllSettings added in v0.16.19

func AllSettings() []SettingDetail

AllSettings returns the complete list of setting definitions, derived from the single settingDefs registry.

type SharedState

type SharedState struct {
	EventBus      *events.EventBus
	TodoManager   *tools.TodoManager
	EmbeddingMgr  *embedding.EmbeddingManager
	ConfigManager *configuration.Manager
	WorkspaceRoot string
}

SharedState holds resources shared between parent and subagents

type ShellCommandResult

type ShellCommandResult struct {
	Command         string // The command that was run
	FullOutput      string // Complete output (for future reference)
	TruncatedOutput string // Truncated output (what was shown)
	Error           error  // Any error that occurred
	ExecutedAt      int64  // Unix timestamp
	MessageIndex    int    // Index in messages array where this result appears
	WasTruncated    bool   // Whether output was truncated
	FullOutputPath  string // Optional path to the saved full output
	TruncatedTokens int    // Number of tokens omitted from the middle section
	TruncatedLines  int    // Approximate number of lines omitted from the middle
}

ShellCommandResult tracks shell command execution for deduplication

type ShellPart added in v0.16.19

type ShellPart struct {
	ID       string      // stable ID for UI tracking (e.g. "part-0")
	Text     string      // raw text of this part (e.g. "rm -rf foo")
	Kind     CommandKind // classified kind
	Semantic string      // human-readable description (e.g. "Recursively delete foo")
}

ShellPart represents one logical command in a potentially-pipelined shell line.

func SplitShellIntoParts added in v0.16.19

func SplitShellIntoParts(cmd string) []ShellPart

SplitShellIntoParts tokenizes a shell command at &&, ||, ;, and | boundaries, respecting balanced parentheses and quoted strings.

Inside quotes (single or double) all metacharacters are treated as literal text. Inside parentheses (depth > 0), the pipe character is treated as literal.

Empty input produces an empty slice. Consecutive separators with no content are skipped. Each part is trimmed of leading/trailing whitespace.

type ShellProposal added in v0.16.19

type ShellProposal struct {
	Command   string                  // original full command
	Parts     []ShellPart             // split + classified
	RiskLevel configuration.RiskLevel // folded from the most-destructive part
}

ShellProposal is a parsed shell command submitted for approval.

func NewShellProposal added in v0.16.19

func NewShellProposal(cmd string) ShellProposal

NewShellProposal creates a ShellProposal by splitting the command into parts, classifying each part (kind + semantic), and folding the overall RiskLevel from the most-destructive part.

func (ShellProposal) HighRiskParts added in v0.16.19

func (p ShellProposal) HighRiskParts() []ShellPart

HighRiskParts returns all parts whose RiskLevel is >= High (Critical or High). Returns nil if none qualify.

func (ShellProposal) MostDestructivePart added in v0.16.19

func (p ShellProposal) MostDestructivePart() *ShellPart

MostDestructivePart returns a pointer to the part with the highest RiskLevel. Returns nil if the proposal has no parts. Ties return the first part in command order.

type SimpleUI

type SimpleUI struct{}

SimpleUI provides a minimal fallback UI implementation

func NewSimpleUI

func NewSimpleUI() *SimpleUI

NewSimpleUI creates a new simple UI instance

func (*SimpleUI) IsInteractive

func (s *SimpleUI) IsInteractive() bool

IsInteractive returns false for simple UI (non-interactive)

func (*SimpleUI) ShowDropdown

func (s *SimpleUI) ShowDropdown(ctx context.Context, items interface{}, options DropdownOptions) (interface{}, error)

ShowDropdown returns an error since simple UI doesn't support dropdowns

func (*SimpleUI) ShowQuickPrompt

func (s *SimpleUI) ShowQuickPrompt(ctx context.Context, prompt string, options []QuickOption, horizontal bool) (QuickOption, error)

ShowQuickPrompt returns an error since simple UI doesn't support prompts

type SkillInfo

type SkillInfo struct {
	ID          string
	Name        string
	Description string
	Path        string
	Content     string
	Source      string // "builtin", "user", or "project"
}

func ListSkills

func ListSkills(config *configuration.Config) []SkillInfo

func LoadSkill

func LoadSkill(skillID string, config *configuration.Config) (*SkillInfo, error)

LoadSkill resolves a skill by ID: built-ins come from the embedded pkg/skills library (the single source of truth that also seeds Config.Skills), user/project skills come from disk via skill.Path. The config registry is still the gate — a skill that isn't registered or is explicitly disabled cannot be activated, even if its content happens to be embedded.

type StreamConfig

type StreamConfig struct {
	// Chunks to stream (content pieces)
	Chunks []string

	// Delay between chunks
	ChunkDelay time.Duration

	// Simulated tokens per chunk
	TokensPerChunk int

	// Error to inject during streaming
	StreamError error

	// Finish reason for the final chunk
	FinishReason string

	// ErrorAfterChunks specifies after how many chunks to fail (0 = never fail)
	ErrorAfterChunks int

	// ChunkErrors allows specifying per-chunk errors (index corresponds to chunk index)
	ChunkErrors []error
}

StreamConfig configures streaming behavior for a response

type SubagentError

type SubagentError struct {
	Status SubagentStatus
	Reason string
}

SubagentError is an in-process error value carrying both the terminal Status and a free-form Reason. Returned alongside the JSON envelope when callers in Go-land want to switch on the failure mode without re-parsing the result JSON.

func (*SubagentError) Error

func (e *SubagentError) Error() string

type SubagentMetrics

type SubagentMetrics struct {
	Active            int64 // Currently executing subagents
	Queued            int64 // Waiting for semaphore slot
	Completed         int64 // Successfully completed
	Failed            int64 // Completed with error
	Cancelled         int64 // Cancelled (parent ctx or budget)
	TotalQueuedWaitMS int64 // Cumulative milliseconds spent waiting in queue
}

SubagentMetrics tracks operational metrics for the subagent runner.

type SubagentOptions

type SubagentOptions struct {
	Persona                string        // "coder", "tester", "debugger", etc.
	Model                  string        // optional model override
	Provider               string        // optional provider override
	SystemPrompt           string        // optional system prompt override
	MaxTokens              int           // token budget (0 = unlimited)
	Timeout                time.Duration // execution timeout; <=0 defaults to 10 minutes (see runTask)
	WorkingDir             string        // optional: override workspace root (must be within $HOME)
	MaxConcurrentSubagents int           // max parallel subagents (0 = unlimited, default unlimited)
	FleetTokenBudget       int           // shared token budget across all parallel subagents (0 = unlimited)
}

SubagentOptions configures an in-process subagent

type SubagentProgressEntry

type SubagentProgressEntry struct {
	OffsetMS int64  `json:"offset_ms"` // ms since subagent started
	Phase    string `json:"phase"`     // "spawn" | "output" | "complete"
	Message  string `json:"message"`
}

SubagentProgressEntry is one timeline entry from a subagent run. Kept minimal to avoid bloating the envelope the primary's LLM sees.

type SubagentResult

type SubagentResult struct {
	ID         string
	Output     string
	Error      error
	TokensUsed int
	Cost       float64
	ToolCalls  int
	// Iterations is the assistant-turn count consumed by this subagent
	// run. Surfaced to the primary via SubagentRunMetrics.Iterations so
	// the model has visibility into how many LLM rounds a delegated task
	// burned. SP-059 Phase 5.
	Iterations     int
	Elapsed        time.Duration
	Cancelled      bool
	BudgetExceeded bool // true if task was skipped because fleet budget was already exceeded before starting
	Truncated      bool // true if subagent was cut short due to fleet budget exceeded mid-run
	// OutputComplete signals whether the subagent produced a substantive
	// final response. false when the output is empty or suspiciously brief
	// (under 50 trimmed chars) despite a clean exit — the orchestrator can
	// use this to decide whether to retry, escalate, or accept. This is
	// distinct from Error/Cancelled/BudgetExceeded (all of which also set
	// it false): OutputComplete focuses specifically on "did the subagent
	// actually say something useful?"
	OutputComplete bool
	// FileChanges is the manifest of writes/edits this subagent performed,
	// captured via its own ChangeTracker. nil when tracking wasn't
	// initialized for this run. SP-059 Phase 2c.
	FileChanges []TrackedFileChange
	// ProgressLog is a per-run timeline of notable subagent events
	// (spawn, output, complete). Surfaced to the primary's LLM via the
	// SubagentReturn envelope so the model can reason about *what* the
	// subagent did, not just the final assistant message. Capped to
	// subagentProgressLogCap entries. SP-059 Phase 3a.
	ProgressLog []SubagentProgressEntry
}

SubagentResult is the structured output from a subagent

type SubagentReturn

type SubagentReturn struct {
	// Output is the subagent's final assistant message (was: "stdout").
	Output string `json:"stdout"`
	// Stderr carries the subagent's terminal error message if any.
	Stderr string `json:"stderr"`
	// ExitCode is "0" on success, "1" otherwise. Kept as string for
	// shape-compat with the legacy resultMap.
	ExitCode string `json:"exit_code"`
	// Completed is "true" on natural completion, "false" if cancelled.
	Completed string `json:"completed"`
	// TimedOut is "true" if the run hit its timeout.
	TimedOut string `json:"timed_out"`
	// BudgetExceeded is "true" if the run hit its token budget.
	BudgetExceeded string `json:"budget_exceeded"`
	// ElapsedSeconds is the wall-clock duration, formatted "%.1f".
	ElapsedSeconds string `json:"elapsed_seconds"`
	// TokensUsed is the rolled-up token count (string for shape-compat).
	TokensUsed string `json:"tokens_used"`
	// Cost is the rolled-up dollar cost (string for shape-compat).
	Cost string `json:"cost"`
	// ToolCallCount is the number of tool calls the subagent made.
	ToolCallCount string `json:"tool_calls"`
	// Summary is JSON-stringified human-readable highlights (file ops,
	// build/test status, errors). Kept for shape-compat.
	Summary string `json:"summary,omitempty"`
	// ContextUsed is "true" / "false" reflecting whether the subagent
	// received the parent's context bundle.
	ContextUsed string `json:"context_used,omitempty"`
	// FilesUsed is the parent-provided files-of-interest list.
	FilesUsed string `json:"files_used,omitempty"`
	// WorkingDir is the directory the subagent executed under.
	WorkingDir string `json:"working_dir,omitempty"`

	// Status is the terminal state. Always populated, even on success.
	Status SubagentStatus `json:"status"`
	// ErrorReason carries free-form context when Status != completed.
	ErrorReason string `json:"error_reason,omitempty"`
	// FilesModified is the change-tracker-sourced manifest. nil when
	// change tracking is disabled (caller treats nil as "not reported").
	FilesModified []FileChange `json:"files_modified,omitempty"`
	// Metrics is the structured token/cost rollup. Mirror of the
	// TokensUsed/Cost/ToolCallCount string fields above for callers
	// that prefer typed access.
	Metrics SubagentRunMetrics `json:"metrics"`
	// ProgressLog is a capped timeline of subagent activity events
	// (spawn / output / complete) so the primary's LLM can reason about
	// what the subagent actually did, not just its final assistant
	// message. nil when no events were captured. SP-059 Phase 3a.
	ProgressLog []ProgressEntry `json:"progress_log,omitempty"`
}

SubagentReturn is the typed envelope a subagent tool call returns to the primary's LLM. It marshals to JSON with backward-compatible keys for the old map[string]string shape so existing LLM behavior keeps working, plus new typed fields (status, files_modified, metrics) for callers that want them.

See SP-059 Phase 2a.

func (*SubagentReturn) MarshalJSONIndent

func (r *SubagentReturn) MarshalJSONIndent() (string, error)

MarshalJSONIndent renders the envelope as a 2-space-indented JSON string for the tool result.

type SubagentRunMetrics

type SubagentRunMetrics struct {
	TokensUsed int     `json:"tokens_used"`
	Cost       float64 `json:"cost"`
	ToolCalls  int     `json:"tool_calls"`
	Iterations int     `json:"iterations"`
}

SubagentRunMetrics is the structured token/cost accounting for a subagent run. Sourced directly from SubagentResult (subagent_runner.go), not by regex-scraping stdout. Iterations is the assistant-turn count, exposed so the primary's LLM can reason about how much budget a delegated task burned.

type SubagentRunner

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

SubagentRunner manages in-process subagent execution

func NewSubagentRunner

func NewSubagentRunner(parent *Agent, shared *SharedState) *SubagentRunner

NewSubagentRunner creates a new SubagentRunner

func (*SubagentRunner) CancelAll

func (r *SubagentRunner) CancelAll()

CancelAll cancels all running subagents. Called when the user clicks Stop on the primary — without this, the primary's TriggerInterrupt returns but subagent work continues until self-completion.

func (*SubagentRunner) CancelSubagent

func (r *SubagentRunner) CancelSubagent(id string) bool

CancelSubagent cancels a specific running subagent by ID. Cancels both the run context (truncates pending work) and the subagent agent's interrupt signal (preempts the in-flight ProcessQuery loop, which doesn't observe runCtx — see SP-059 Phase 1a).

func (*SubagentRunner) GetActiveSubagents

func (r *SubagentRunner) GetActiveSubagents() []*runningSubagent

GetActiveSubagents returns information about currently running subagents

func (*SubagentRunner) InjectInputIntoActive

func (r *SubagentRunner) InjectInputIntoActive(input string) (string, bool)

InjectInputIntoActive delivers a steering message to the PRIMARY agent first. Only if the primary's channel is full or unavailable does it fall back to the deepest (most-recently-started) running subagent.

Rationale (SP-094-8): The primary agent is what reads user steer messages and decides whether to abort subagents, redirect them, or fold the steer into its own plan. Routing to the subagent bypasses this decision loop — the parent never sees "yes, commit and push" until the subagent finishes, by which point the subagent may have already taken destructive action.

Returns the target ID ("primary" or subagent ID) when delivery succeeds, or ("", false) when no target is available.

func (*SubagentRunner) Metrics

func (r *SubagentRunner) Metrics() SubagentMetrics

Metrics returns a snapshot of the subagent runner's operational metrics.

func (*SubagentRunner) Run

Run spawns an in-process subagent and waits for completion

func (*SubagentRunner) RunParallel

func (r *SubagentRunner) RunParallel(ctx context.Context, tasks []SubagentTask, opts SubagentOptions) []*SubagentResult

RunParallel spawns multiple subagents concurrently. If the parent context is cancelled, remaining subagents are cancelled and their results are set to cancellation errors.

type SubagentStatus

type SubagentStatus string

SubagentStatus enumerates terminal states of a subagent run. Replaces the legacy SUBAGENT_SECURITY_ERROR / SUBAGENT_TOKEN_BUDGET_EXCEEDED / SUBAGENT_FAILED sentinel string prefixes — those literals are retained in the human-readable Output so any LLM behavior keyed on the legacy shape still works, but in-process callers should switch to Status.

See SP-059 Phase 2d.

const (
	SubagentStatusCompleted       SubagentStatus = "completed"
	SubagentStatusCancelled       SubagentStatus = "cancelled"
	SubagentStatusTimedOut        SubagentStatus = "timed_out"
	SubagentStatusBudgetExceeded  SubagentStatus = "budget_exceeded"
	SubagentStatusSecurityBlocked SubagentStatus = "security_blocked"
	SubagentStatusFailed          SubagentStatus = "failed"
)

type SubagentTask

type SubagentTask struct {
	ID         string
	Prompt     string
	Model      string
	Provider   string
	Persona    string
	WorkingDir string // optional: override workspace root
}

SubagentTask represents a single parallel subagent task

type SummaryStore added in v0.16.25

type SummaryStore interface {
	GetPreviousSummary() string
	SetPreviousSummary(string)
}

SummaryStore manages the previous conversation summary.

type SyncOp

type SyncOp struct {
	OpType     string `json:"op_type"`     // "write", "delete", or "rename"
	Path       string `json:"path"`        // Target file path (relative to workspace root)
	Content    string `json:"content"`     // For write ops: the file content
	NewPath    string `json:"new_path"`    // For rename ops: the destination path
	BrowserSeq int64  `json:"browser_seq"` // Monotonically increasing browser-side seq number
	Timestamp  int64  `json:"timestamp"`   // Unix milliseconds when the op was created
}

SyncOp represents a single file operation sent from the browser to the container as part of the workspace sync protocol (SP-046 §2). The browser queues these in OPFS and flushes them via HTTP POST when the WebSocket is up.

type SyncOpResult

type SyncOpResult struct {
	Accepted     bool   `json:"accepted"`        // Whether the op was applied
	ConflictPath string `json:"conflict_path"`   // Set if there's a container-side conflict (path to .theirs file)
	ContainerSeq int64  `json:"container_seq"`   // Current container sequence after applying
	Error        string `json:"error,omitempty"` // Error message if not accepted
}

SyncOpResult is the server response to a SyncOp application.

type TaskAction

type TaskAction struct {
	Type        string // "file_created", "file_modified", "command_executed", "file_read"
	Description string // Human-readable description
	Details     string // Additional details like file path, command, etc.
}

TaskAction represents a completed action during task execution

type TaskActionStore added in v0.16.25

type TaskActionStore interface {
	GetTaskActions() []TaskAction
	SetTaskActions([]TaskAction)
	AddTaskAction(TaskAction)
	GetTaskActionsMutex() *sync.RWMutex
}

TaskActionStore manages task actions and their associated mutex.

type TerminationStore added in v0.16.25

type TerminationStore interface {
	GetLastRunTerminationReason() string
	SetLastRunTerminationReason(string)
}

TerminationStore manages the last run termination reason.

type Theme

type Theme struct {
	Name        string `json:"name"`
	Description string `json:"description"`
	Colors      struct {
		Success   string `json:"success"`
		Warning   string `json:"warning"`
		Error     string `json:"error"`
		Info      string `json:"info"`
		Primary   string `json:"primary"`
		Secondary string `json:"secondary"`
		Accent    string `json:"accent"`
	} `json:"colors"`
}

Theme represents a color theme configuration

type ThemeManager

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

ThemeManager manages color themes

func NewThemeManager

func NewThemeManager() *ThemeManager

NewThemeManager creates a new theme manager with default theme

func (*ThemeManager) GetColor

func (tm *ThemeManager) GetColor(name string) string

GetColor returns a color by name

func (*ThemeManager) GetTheme

func (tm *ThemeManager) GetTheme() Theme

GetTheme returns the current theme

func (*ThemeManager) LoadDefaultTheme

func (tm *ThemeManager) LoadDefaultTheme()

LoadDefaultTheme loads the default theme

func (*ThemeManager) LoadThemeFromFile

func (tm *ThemeManager) LoadThemeFromFile(themePath string) error

LoadThemeFromFile loads a theme from a JSON file

type TokenCounter added in v0.16.25

type TokenCounter interface {
	GetTotalTokens() int
	SetTotalTokens(int)
	GetPromptTokens() int
	SetPromptTokens(int)
	GetCompletionTokens() int
	SetCompletionTokens(int)
}

TokenCounter manages prompt and completion token counts.

type TokenUsage

type TokenUsage struct {
	PromptTokens     int
	CompletionTokens int
	TotalTokens      int
	EstimatedCost    float64
}

TokenUsage captures key token metrics for each turn

type ToolCallTracker added in v0.16.25

type ToolCallTracker interface {
	GetTotalToolCalls() int
	SetTotalToolCalls(int)
	IncrementTotalToolCalls()
}

ToolCallTracker manages tool call count tracking.

type ToolExecutor

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

ToolExecutor handles tool execution logic

func NewToolExecutor

func NewToolExecutor(agent *Agent) *ToolExecutor

NewToolExecutor creates a new tool executor

func (*ToolExecutor) ExecuteTools

func (te *ToolExecutor) ExecuteTools(toolCalls []api.ToolCall) []api.Message

ExecuteTools executes a list of tool calls and returns the results

func (*ToolExecutor) GenerateToolCallID

func (te *ToolExecutor) GenerateToolCallID(toolName string) string

GenerateToolCallID creates a unique tool call ID if one is missing

type ToolGuidanceStore added in v0.16.25

type ToolGuidanceStore interface {
	IsToolCallGuidanceAdded() bool
	SetToolCallGuidanceAdded(bool)
}

ToolGuidanceStore manages tool call guidance state.

type TraceStore added in v0.16.25

type TraceStore interface {
	GetTraceSession() interface{}
	SetTraceSession(interface{})
}

TraceStore manages the trace session.

type TrackedBulkItem added in v0.16.2

type TrackedBulkItem struct {
	FilePath     string `json:"file_path"`
	OriginalCode string `json:"original_code"`
	NewCode      string `json:"new_code"`
	Operation    string `json:"operation"` // "create" | "edit" | "delete"
}

TrackedBulkItem is the per-file payload packed inside a bulk TrackedFileChange. Shape mirrors TrackedFileChange's recoverable fields so the recovery helpers (`isRecoverableOriginal`, `restoreFile`) can be reused without translation.

type TrackedFileChange

type TrackedFileChange struct {
	FilePath     string    `json:"file_path"`
	OriginalCode string    `json:"original_code"`
	NewCode      string    `json:"new_code"`
	Operation    string    `json:"operation"` // "write", "edit", "create", "delete", "bulk"
	Timestamp    time.Time `json:"timestamp"`
	ToolCall     string    `json:"tool_call"` // Which tool was used

	// Source attributes a change to its origin. Empty for direct
	// primary-agent edits; "subagent:<persona>" for changes merged
	// in from a subagent run. Surfaced by list_changes so the user/LLM
	// can tell which subagent touched a file. TrackedBulkItem does NOT
	// carry this (bulk entries are always shell-mutation rollups).
	Source string `json:"source,omitempty"`

	// BulkCount is set on a rollup entry produced when a single shell
	// command churns more than the bulk threshold — typical of
	// `make build`, `npm ci`, `cargo build`, or `git checkout .`.
	// FilePath then names the directory or command label (workspace-
	// relative, trailing "/") and Operation is "bulk". When zero, the
	// entry represents a normal single-file change. SP-061-1.
	BulkCount int `json:"bulk_count,omitempty"`

	// BulkItems carries the per-file recovery payload for bulk entries.
	// Populated when the bulk fits inside the walk's content budget
	// (~32 MiB). When present, recover_file can match a specific path
	// inside the bulk and recover_bulk can restore the whole set.
	// Empty when the bulk row is count-only (build-output rollup that
	// the user said is cheap to regenerate, or destructive bulk that
	// blew through the memory cap).
	BulkItems []TrackedBulkItem `json:"bulk_items,omitempty"`
}

TrackedFileChange represents a file change made during agent execution

type TranscriptDiff added in v0.16.4

type TranscriptDiff struct {
	OlderPath              string                 `json:"older_path"`
	NewerPath              string                 `json:"newer_path"`
	OlderTimestamp         time.Time              `json:"older_timestamp"`
	NewerTimestamp         time.Time              `json:"newer_timestamp"`
	OlderMessageCount      int                    `json:"older_message_count"`
	NewerMessageCount      int                    `json:"newer_message_count"`
	OlderCheckpointCount   int                    `json:"older_checkpoint_count"`
	NewerCheckpointCount   int                    `json:"newer_checkpoint_count"`
	OlderTotalTokens       int                    `json:"older_total_tokens"`
	NewerTotalTokens       int                    `json:"newer_total_tokens"`
	OlderFileChangeCount   int                    `json:"older_file_change_count"`
	NewerFileChangeCount   int                    `json:"newer_file_change_count"`
	NewFileChanges         []TranscriptFileChange `json:"new_file_changes,omitempty"`
	MessagesDroppedAtTail  int                    `json:"messages_dropped_at_tail"`
	MessagesReplacedByRole map[string]int         `json:"messages_replaced_by_role,omitempty"`
	ChangedIndices         []TranscriptDiffEntry  `json:"changed_indices,omitempty"`
	Notes                  []string               `json:"notes,omitempty"`
}

TranscriptDiff is a compact, human-friendly comparison of two snapshots. Used by `/transcript diff` to expose what compaction (or some other state mutation) changed between snapshots.

func DiffTranscriptSnapshots added in v0.16.4

func DiffTranscriptSnapshots(older, newer *TranscriptSnapshot) *TranscriptDiff

DiffTranscriptSnapshots compares two snapshots and returns a human-readable diff structure. Older should be the chronologically earlier snapshot; the function does not re-sort.

type TranscriptDiffEntry added in v0.16.4

type TranscriptDiffEntry struct {
	Index          int    `json:"index"`
	OlderRole      string `json:"older_role,omitempty"`
	NewerRole      string `json:"newer_role,omitempty"`
	OlderSource    string `json:"older_source,omitempty"`
	NewerSource    string `json:"newer_source,omitempty"`
	OlderFirstLine string `json:"older_first_line,omitempty"`
	NewerFirstLine string `json:"newer_first_line,omitempty"`
}

TranscriptDiffEntry is a single divergence in the per-index walk. Truncated content makes diffs scannable; full text is in the raw JSON.

type TranscriptFileChange added in v0.16.4

type TranscriptFileChange struct {
	Path      string    `json:"path"`
	Operation string    `json:"operation"`
	Source    string    `json:"source"`
	ToolCall  string    `json:"tool_call,omitempty"`
	Timestamp time.Time `json:"timestamp,omitempty"`
	BulkCount int       `json:"bulk_count,omitempty"`
}

TranscriptFileChange is the slim per-file projection embedded in a snapshot's top-level FileChanges field. It deliberately omits the full original/new file bodies that the ChangeTracker keeps for recovery — those can be multi-megabyte per file and would blow up snapshot size. The path, operation, and tool-call identifier give a reader enough to answer "what files were touched between snapshot A and snapshot B" without loading the bytes themselves.

Source distinguishes changes the primary agent made directly ("primary") from rollups parsed out of subagent tool results ("subagent"). The subagent's [subagent files modified] block is the authoritative per-call manifest, so the parser is a deterministic text scan rather than heuristic prose extraction.

func ExtractFileChangesFromMessages added in v0.16.4

func ExtractFileChangesFromMessages(messages []api.Message) []TranscriptFileChange

ExtractFileChangesFromMessages walks the supplied message slice and returns a deduped manifest of files touched, drawn from three authoritative sources: (1) tool_calls on assistant messages whose function name is a known file-write tool, (2) `[subagent files modified]` blocks embedded by tool_handlers_subagent in subagent tool results, and (3) `Files modified during compacted segment:` blocks that this package writes when /compact substitutes a summary for prior turns. The third source is what carries the manifest forward across successive compactions.

type TranscriptSnapshot added in v0.16.4

type TranscriptSnapshot struct {
	Format             string                 `json:"format"`
	Timestamp          time.Time              `json:"timestamp"`
	Label              string                 `json:"label"`
	SessionID          string                 `json:"session_id"`
	WorkingDirectory   string                 `json:"working_directory"`
	State              *ConversationState     `json:"state"`
	MessageAnnotations []MessageAnnotation    `json:"message_annotations"`
	FileChanges        []TranscriptFileChange `json:"file_changes,omitempty"`
	ChangeTrackerRev   string                 `json:"change_tracker_revision,omitempty"`
	CompactPreview     *CompactPreview        `json:"compact_preview,omitempty"`
}

TranscriptSnapshot is the file shape written by /transcript and by the auto-capture path on compaction events. It is intentionally a superset of ConversationState so a reader can diff message lists, inspect checkpoint summaries, and compare snapshots across time.

func LoadTranscriptSnapshot added in v0.16.4

func LoadTranscriptSnapshot(path string) (*TranscriptSnapshot, error)

LoadTranscriptSnapshot reads a snapshot file back into memory.

type TurnCheckpoint

type TurnCheckpoint struct {
	StartIndex        int    `json:"start_index"`
	EndIndex          int    `json:"end_index"`
	Summary           string `json:"summary"`
	ActionableSummary string `json:"actionable_summary,omitempty"`
	// FileChanges is the git-style manifest (M/A/D/R) of files touched
	// during this turn. Populated from the agent's ChangeTracker at
	// checkpoint-record time. Empty when tracking is disabled or the turn
	// didn't write any files. For rollups (Level>0), this is the union of
	// the source checkpoints' file changes so the manifest doesn't get
	// lost as rollups stack.
	FileChanges []CheckpointFileChange `json:"file_changes,omitempty"`
	// RevisionID is the ChangeTracker revision that was active when this
	// turn ran. When set, the summary text references it so the model can
	// call the view_history tool to recover the exact diff. Empty when
	// tracking is disabled. For rollups, this is the most recent
	// revision_id from the source set.
	RevisionID string `json:"revision_id,omitempty"`

	// ID is a stable identifier for this checkpoint, independent of its
	// position in the TurnCheckpoints slice. Used by rollups to reference
	// their source checkpoints via SourceCheckpointIDs.
	ID string `json:"id,omitempty"`
	// Level is the rollup depth. 0 = per-turn (existing behavior).
	// 1 = rollup of per-turn checkpoints. 2 = rollup of rollups. Etc.
	Level int `json:"level,omitempty"`
	// CoveredTurns is the count of original per-turn checkpoints this
	// entry effectively replaces. For Level=0 this is 1 (or omitted).
	// For rollups this is the sum of CoveredTurns from the source set.
	CoveredTurns int `json:"covered_turns,omitempty"`
	// SourceCheckpointIDs lists the checkpoint IDs this rollup consumed.
	// Lets the UI drill down and lets a re-roll-up operate on the right
	// source. Empty for Level=0.
	SourceCheckpointIDs []string `json:"source_checkpoint_ids,omitempty"`
}

TurnCheckpoint stores a compact summary for a completed user turn while preserving the original full messages for cache-efficient reuse until needed.

SP-066 Phase 2 generalizes this struct: a Level=0 entry is a per-turn checkpoint (the historical default). A Level>0 entry is a "rollup" that folds many lower-level checkpoints into one coarser summary. Both kinds substitute identically through seed's BuildCheckpointCompactedMessages — the rollup is just a checkpoint whose StartIndex/EndIndex span a wider historical range. The extra fields are sprout-side metadata for the rollup worker and the WebUI; seed doesn't read them.

type TurnEvaluation

type TurnEvaluation struct {
	Iteration         int
	Timestamp         time.Time
	UserInput         string
	AssistantContent  string
	ToolCalls         []api.ToolCall
	ToolResults       []api.Message
	TokenUsage        TokenUsage
	CompletionReached bool
	FinishReason      string
	ReasoningSnippet  string
	GuardrailTrigger  string
}

TurnEvaluation captures the inputs, outputs, and tool activity for each iteration

type UI

type UI interface {
	// ShowDropdown displays a dropdown selection UI
	ShowDropdown(ctx context.Context, items interface{}, options DropdownOptions) (interface{}, error)

	// ShowQuickPrompt shows a small prompt with quick choices
	ShowQuickPrompt(ctx context.Context, prompt string, options []QuickOption, horizontal bool) (QuickOption, error)

	// IsInteractive returns true if UI is available
	IsInteractive() bool
}

UI provides UI capabilities to the agent

type WebUIPasswordPrompter added in v0.16.18

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

WebUIPasswordPrompter implements PasswordPrompter for WebUI sessions. It publishes a password_request event and blocks until the browser POSTs the response (or the timeout fires).

func NewWebUIPasswordPrompter added in v0.16.18

func NewWebUIPasswordPrompter(agent *Agent) *WebUIPasswordPrompter

NewWebUIPasswordPrompter creates a WebUI-backed password prompter.

func (*WebUIPasswordPrompter) Prompt added in v0.16.18

func (wp *WebUIPasswordPrompter) Prompt(ctx context.Context, reason string) (string, error)

Prompt asks the WebUI to collect a password from the user.

Returns ErrNoInteractiveSurface when there's no event bus or no active WebUI clients. On timeout, returns a descriptive error. On context cancellation, returns ctx.Err().

type WorkflowBudgetConfig added in v0.16.19

type WorkflowBudgetConfig struct {
	USD    float64   `json:"usd,omitempty"`
	WarnAt []float64 `json:"warn_at,omitempty"`
}

WorkflowBudgetConfig is parsed from the "budget" section of a workflow JSON.

type WorkflowLoopConfig added in v0.16.19

type WorkflowLoopConfig struct {
	TodoFile       string `json:"todo_file,omitempty"`
	GatePromptFile string `json:"gate_prompt_file,omitempty"`
	MaxRetries     int    `json:"max_retries,omitempty"`
	MaxIterations  int    `json:"max_iterations,omitempty"`
	BuildCommand   string `json:"build_command,omitempty"`
}

WorkflowLoopConfig is parsed from the "loop" section of a workflow JSON file. Only the fields relevant to the in-process runner are included.

type WorkflowProgressConfig added in v0.16.19

type WorkflowProgressConfig struct {
	HeartbeatSeconds int `json:"heartbeat_seconds,omitempty"`
}

WorkflowProgressConfig is parsed from the "progress" section.

type WorkflowResult added in v0.16.19

type WorkflowResult struct {
	ItemsProcessed int
	ItemsSkipped   int
	ItemsFailed    int
	Error          error
}

WorkflowResult is returned when the workflow completes.

func RunWorkflowLoopInProcess added in v0.16.19

func RunWorkflowLoopInProcess(ctx context.Context, parentAgent *Agent, configPath string, eventBus *events.EventBus) (*WorkflowResult, error)

RunWorkflowLoopInProcess creates a fresh agent and runs the TODO loop workflow in the calling goroutine (blocking). For non-blocking use, call it from a goroutine.

The fresh agent is created using the same pattern as subagents: new client from factory, new state managers, proper interrupt context, full tool wiring via the seed tool registry, and budget tracking.

configPath is the path to the workflow JSON file. The file is parsed for the "loop" section; if no loop section is found, an error is returned.

type WorkspaceFileMetadata

type WorkspaceFileMetadata struct {
	// BrowserSeq counts user-driven edits to the file from the browser side.
	// Bumped each time the user types and the change flushes to OPFS.
	BrowserSeq int64 `json:"browser_seq"`

	// ContainerSeq counts agent-driven writes to the file via the agent's
	// tool handlers. Bumped each time writeFileContent succeeds.
	ContainerSeq int64 `json:"container_seq"`

	// LastSyncedBrowser is the BrowserSeq value the container has
	// acknowledged. BrowserSeq > LastSyncedBrowser means the browser has
	// unsynced edits — see the conflict rule.
	LastSyncedBrowser int64 `json:"last_synced_browser"`

	// LastSyncedContainer is the ContainerSeq value the browser has
	// acknowledged.
	LastSyncedContainer int64 `json:"last_synced_container"`

	// ModifiedAt is the wall-clock time of the most recent write to the
	// file from any source. Used by the staleness rule's "recent
	// modification" check.
	ModifiedAt time.Time `json:"modified_at"`
}

WorkspaceFileMetadata describes the per-file sync state that the browser-primary workspace model in SP-046 needs to enforce consistency between the browser-side OPFS replica and the container-side filesystem.

On native sprout (single-replica), only ModifiedAt and the agent's turn-scoped read tracking actually matter. The sequence fields are placeholders for the eventual WS-based sync layer to populate from the browser side; until then they're zero. Storing the struct now (rather than retrofitting later) keeps the persistence shape stable.

Spec: roadmap/SP-046-workspace-sync-model.md §3.

func (WorkspaceFileMetadata) HasUnsyncedBrowserEdits

func (m WorkspaceFileMetadata) HasUnsyncedBrowserEdits() bool

HasUnsyncedBrowserEdits reports whether the browser side has writes the container hasn't applied yet. The agent's write_file tool wrapper refuses to overwrite such files without explicit user confirmation.

Source Files

Jump to

Keyboard shortcuts

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