Documentation
¶
Index ¶
- Constants
- func ApplyWorkflowCommandOverrides(cfg *AgentWorkflowConfig, overrides *CLIOverrides)
- func ApplyWorkflowInitialOverrides(chatAgent *agent.Agent, cfg *AgentWorkflowConfig, overrides *CLIOverrides) error
- func ApplyWorkflowRuntimeOverrides(chatAgent *agent.Agent, runtime AgentWorkflowRuntime, overrides *CLIOverrides) error
- func ApplyWorkflowSubagentOverrides(subagentTypes map[string]configuration.SubagentType, ...)
- func AttachWorkflowBudget(chatAgent *agent.Agent, cfg *AgentWorkflowConfig) (stop func())
- func EmitWorkflowOrchestrationEvent(cfg *AgentWorkflowConfig, eventType string, payload map[string]interface{}) error
- func FindSubagentTypeMapKey(subagentTypes map[string]configuration.SubagentType, normalizedID string) (string, bool)
- func IsValidWorkflowWhen(v string) bool
- func LoadLoopCheckpoint(workDir string) (int, error)
- func LoopCheckpointFilePath(workDir string) string
- func NormalizeReasoningEffort(v string) string
- func NormalizeWorkflowPaths(paths []string) []string
- func NormalizeWorkflowPersonaID(raw string) string
- func NormalizeWorkflowWhen(v string) string
- func ParseBudgetWarnList(s string) ([]float64, error)
- func PersistLoopCheckpoint(workDir string, lineNum int) error
- func PersistWorkflowCheckpoint(cfg *AgentWorkflowConfig, state *WorkflowExecutionState, ...) error
- func PersistWorkflowConversationState(chatAgent *agent.Agent, cfg *AgentWorkflowConfig) error
- func PersistWorkflowExecutionState(cfg *AgentWorkflowConfig, state *WorkflowExecutionState) error
- func PrepareWorkflowRuntimeRestorer(chatAgent *agent.Agent, cfg *AgentWorkflowConfig, overrides *CLIOverrides) (func() error, error)
- func RemoveLoopCheckpoint(workDir string)
- func ResolveStepPrompt(step AgentWorkflowStep) (string, error)
- func ResolveWorkflowInitialPrompt(cliQuery string, cfg *AgentWorkflowConfig) (string, error)
- func ResolveWorkflowTextOrFile(text, filePath, label string) (string, error)
- func RestoreWorkflowConversationState(chatAgent *agent.Agent, cfg *AgentWorkflowConfig, ...) error
- func RunAgentWorkflow(ctx context.Context, chatAgent *agent.Agent, eventBus *events.EventBus, ...) (bool, error)
- func RunAgentWorkflowLoop(ctx context.Context, chatAgent *agent.Agent, eventBus *events.EventBus, ...) (bool, error)
- func ShouldRestoreWorkflowConversationState(state *WorkflowExecutionState) bool
- func ShouldRunWorkflowStep(when string, hasError bool) bool
- func ShouldYieldBeforeWorkflowStep(cfg *AgentWorkflowConfig, state *WorkflowExecutionState, ...) bool
- func StepFileTriggersSatisfied(step AgentWorkflowStep) (bool, error)
- func WorkflowEffectiveStepProvider(chatAgent *agent.Agent, step AgentWorkflowStep) string
- func WriteFileAtomic(path string, data []byte, perm os.FileMode) error
- type AgentWorkflowBudgetConfig
- type AgentWorkflowConfig
- func (c *AgentWorkflowConfig) IsApprovalRequired() bool
- func (c *AgentWorkflowConfig) OrchestrationEnabled() bool
- func (c *AgentWorkflowConfig) OrchestrationResumeEnabled() bool
- func (c *AgentWorkflowConfig) OrchestrationYieldOnProviderHandoff() bool
- func (c *AgentWorkflowConfig) ShouldPersistRuntimeOverrides() bool
- func (c *AgentWorkflowConfig) Validate() error
- type AgentWorkflowInitial
- type AgentWorkflowLoopConfig
- type AgentWorkflowOrchestrationConfig
- type AgentWorkflowProgressConfig
- type AgentWorkflowRuntime
- type AgentWorkflowStep
- type CLIOverrides
- type QueryExecutor
- type WorkflowExecutionState
- type WorkflowSubagentOverride
- type WorkflowSubagentOverrides
Constants ¶
const ( WorkflowWhenAlways = "always" WorkflowWhenOnSuccess = "on_success" WorkflowWhenOnError = "on_error" DefaultWorkflowOrchestrationStateFile = ".sprout/workflow_state.json" DefaultWorkflowOrchestrationEventsFile = ".sprout/workflow_events.jsonl" DefaultWorkflowConversationSessionID = "workflow" )
Variables ¶
This section is empty.
Functions ¶
func ApplyWorkflowCommandOverrides ¶
func ApplyWorkflowCommandOverrides(cfg *AgentWorkflowConfig, overrides *CLIOverrides)
func ApplyWorkflowInitialOverrides ¶
func ApplyWorkflowInitialOverrides(chatAgent *agent.Agent, cfg *AgentWorkflowConfig, overrides *CLIOverrides) error
func ApplyWorkflowRuntimeOverrides ¶
func ApplyWorkflowRuntimeOverrides(chatAgent *agent.Agent, runtime AgentWorkflowRuntime, overrides *CLIOverrides) error
func ApplyWorkflowSubagentOverrides ¶
func ApplyWorkflowSubagentOverrides(subagentTypes map[string]configuration.SubagentType, overrides WorkflowSubagentOverrides)
ApplyWorkflowSubagentOverrides patches the SubagentTypes map entries matching the given overrides. No error is returned for unknown personas — they are skipped. Log lines are emitted for every skip and every successful apply so that silent divergence between the workflow JSON and the actual SubagentTypes is visible.
func AttachWorkflowBudget ¶
func AttachWorkflowBudget(chatAgent *agent.Agent, cfg *AgentWorkflowConfig) (stop func())
AttachWorkflowBudget wires the workflow's USD budget and progress heartbeat onto the agent. Returns a stop function the caller MUST invoke before the agent shuts down — it unregisters callbacks and stops the heartbeat goroutine. If no budget is configured the returned stop is a no-op and no goroutines are started.
Heartbeat semantics:
- Default cadence: 600s when a budget is configured, off otherwise.
- cfg.Progress.HeartbeatSeconds > 0 overrides the cadence.
- The heartbeat prints to stdout in a single line so it composes with existing console output without clobbering it.
func EmitWorkflowOrchestrationEvent ¶
func EmitWorkflowOrchestrationEvent(cfg *AgentWorkflowConfig, eventType string, payload map[string]interface{}) error
func FindSubagentTypeMapKey ¶
func FindSubagentTypeMapKey(subagentTypes map[string]configuration.SubagentType, normalizedID string) (string, bool)
FindSubagentTypeMapKey finds the original map key in SubagentTypes matching the given normalized persona ID. It mirrors the lookup logic in config.go GetSubagentType.
func IsValidWorkflowWhen ¶
func LoadLoopCheckpoint ¶
LoadLoopCheckpoint reads the fallback checkpoint file and returns the line number. Returns (0, nil) if the file doesn't exist.
func LoopCheckpointFilePath ¶
LoopCheckpointFilePath returns the path to the lightweight fallback checkpoint file that stores just the TODO line number.
func NormalizeWorkflowPaths ¶
func NormalizeWorkflowPersonaID ¶
NormalizeWorkflowPersonaID normalizes a persona ID the same way config.go does.
func NormalizeWorkflowWhen ¶
func ParseBudgetWarnList ¶
ParseBudgetWarnList parses a comma-separated list of fractional thresholds (e.g. "0.5,0.8") into a sorted []float64. Each value must be in (0, 1].
func PersistLoopCheckpoint ¶
PersistLoopCheckpoint writes just the line number to the fallback checkpoint file using an atomic write (temp file + rename).
func PersistWorkflowCheckpoint ¶
func PersistWorkflowCheckpoint(cfg *AgentWorkflowConfig, state *WorkflowExecutionState, chatAgent *agent.Agent) error
func PersistWorkflowConversationState ¶
func PersistWorkflowConversationState(chatAgent *agent.Agent, cfg *AgentWorkflowConfig) error
func PersistWorkflowExecutionState ¶
func PersistWorkflowExecutionState(cfg *AgentWorkflowConfig, state *WorkflowExecutionState) error
func PrepareWorkflowRuntimeRestorer ¶
func PrepareWorkflowRuntimeRestorer(chatAgent *agent.Agent, cfg *AgentWorkflowConfig, overrides *CLIOverrides) (func() error, error)
func RemoveLoopCheckpoint ¶
func RemoveLoopCheckpoint(workDir string)
RemoveLoopCheckpoint deletes the fallback checkpoint file, ignoring not-found errors.
func ResolveStepPrompt ¶
func ResolveStepPrompt(step AgentWorkflowStep) (string, error)
func ResolveWorkflowInitialPrompt ¶
func ResolveWorkflowInitialPrompt(cliQuery string, cfg *AgentWorkflowConfig) (string, error)
func RestoreWorkflowConversationState ¶
func RestoreWorkflowConversationState(chatAgent *agent.Agent, cfg *AgentWorkflowConfig, state *WorkflowExecutionState) error
func RunAgentWorkflow ¶
func RunAgentWorkflow(ctx context.Context, chatAgent *agent.Agent, eventBus *events.EventBus, cfg *AgentWorkflowConfig, state *WorkflowExecutionState, queryExecutor QueryExecutor, overrides *CLIOverrides) (bool, error)
func RunAgentWorkflowLoop ¶
func RunAgentWorkflowLoop(ctx context.Context, chatAgent *agent.Agent, eventBus *events.EventBus, cfg *AgentWorkflowConfig, state *WorkflowExecutionState, queryExecutor QueryExecutor, overrides *CLIOverrides) (bool, error)
RunAgentWorkflowLoop iterates over unchecked TODO items, processing each with a fresh agent context. Between items, the conversation is cleared.
func ShouldRestoreWorkflowConversationState ¶
func ShouldRestoreWorkflowConversationState(state *WorkflowExecutionState) bool
func ShouldRunWorkflowStep ¶
func ShouldYieldBeforeWorkflowStep ¶
func ShouldYieldBeforeWorkflowStep(cfg *AgentWorkflowConfig, state *WorkflowExecutionState, nextStep AgentWorkflowStep, chatAgent *agent.Agent) bool
func StepFileTriggersSatisfied ¶
func StepFileTriggersSatisfied(step AgentWorkflowStep) (bool, error)
func WorkflowEffectiveStepProvider ¶
func WorkflowEffectiveStepProvider(chatAgent *agent.Agent, step AgentWorkflowStep) string
Types ¶
type AgentWorkflowBudgetConfig ¶
type AgentWorkflowBudgetConfig struct {
// USD is the hard cap on cumulative cost across the workflow.
// <= 0 means no cap.
USD float64 `json:"usd,omitempty"`
// WarnAt is a list of fractional thresholds (0.0–1.0). When the
// cumulative spend first crosses each threshold, a single warning
// is emitted to stdout and (when wired) the event bus.
// Empty defaults to [0.50, 0.80].
WarnAt []float64 `json:"warn_at,omitempty"`
// OnExceed controls what happens when USD is reached.
// "truncate" (default) sets the truncation flag so the run finishes
// the current LLM response and stops gracefully. "stop" is reserved
// for future hard-kill behavior; today it's treated like truncate.
OnExceed string `json:"on_exceed,omitempty"`
}
AgentWorkflowBudgetConfig caps the total USD spend of a workflow run (primary agent + every subagent it spawns share the same budget).
USD-denominated rather than tokens because mixed-provider workflows route different personas to different price tiers — a token cap that covers an Opus orchestrator would let a DeepSeek coder consume 50× the work for the same budget, defeating the cap.
type AgentWorkflowConfig ¶
type AgentWorkflowConfig struct {
Description string `json:"description,omitempty"`
Initial *AgentWorkflowInitial `json:"initial,omitempty"`
Steps []AgentWorkflowStep `json:"steps"`
ContinueOnError bool `json:"continue_on_error,omitempty"`
PersistRuntimeOverrides *bool `json:"persist_runtime_overrides,omitempty"`
Orchestration *AgentWorkflowOrchestrationConfig `json:"orchestration,omitempty"`
NoWebUI *bool `json:"no_web_ui,omitempty"`
WebPort *int `json:"web_port,omitempty"`
Daemon *bool `json:"daemon,omitempty"`
Budget *AgentWorkflowBudgetConfig `json:"budget,omitempty"`
Progress *AgentWorkflowProgressConfig `json:"progress,omitempty"`
// SubagentTimeoutSeconds overrides the per-run_subagent tool timeout
// (default 1800 = 30 minutes). Set higher for very large refactors.
SubagentTimeoutSeconds *int `json:"subagent_timeout_seconds,omitempty"`
// RequiresApproval controls whether the run_automate agent tool must
// surface an intent-confirmation prompt to the user before launching
// this workflow. Pointer so we can distinguish "unset" (default: true)
// from explicit false. Set to false for workflows that exist
// specifically so an agent can invoke them mid-task — e.g. a
// validation workflow referenced from AGENTS.md that the model must
// run before considering work done. Anyone with workflow-file access
// can flip this, so the security implication should be obvious to a
// reader of the JSON.
//
// Only affects the agent tool path. The CLI (`sprout automate run`)
// always prompts unless --yes is passed, because a human at the
// keyboard might still fat-finger the wrong workflow.
RequiresApproval *bool `json:"requires_approval,omitempty"`
// Loop configures the workflow to iterate over a TODO file, processing
// each unchecked item independently with a fresh agent context.
// When Loop is set, Steps are ignored — the loop IS the execution plan.
Loop *AgentWorkflowLoopConfig `json:"loop,omitempty"`
}
AgentWorkflowConfig defines non-interactive workflow orchestration.
func LoadAgentWorkflowConfig ¶
func LoadAgentWorkflowConfig(path string) (*AgentWorkflowConfig, error)
func (*AgentWorkflowConfig) IsApprovalRequired ¶
func (c *AgentWorkflowConfig) IsApprovalRequired() bool
IsApprovalRequired reports whether the run_automate tool path should surface an intent-confirmation prompt before launching this workflow. Defaults to true when unset.
func (*AgentWorkflowConfig) OrchestrationEnabled ¶
func (c *AgentWorkflowConfig) OrchestrationEnabled() bool
func (*AgentWorkflowConfig) OrchestrationResumeEnabled ¶
func (c *AgentWorkflowConfig) OrchestrationResumeEnabled() bool
func (*AgentWorkflowConfig) OrchestrationYieldOnProviderHandoff ¶
func (c *AgentWorkflowConfig) OrchestrationYieldOnProviderHandoff() bool
func (*AgentWorkflowConfig) ShouldPersistRuntimeOverrides ¶
func (c *AgentWorkflowConfig) ShouldPersistRuntimeOverrides() bool
func (*AgentWorkflowConfig) Validate ¶
func (c *AgentWorkflowConfig) Validate() error
type AgentWorkflowInitial ¶
type AgentWorkflowInitial struct {
Prompt string `json:"prompt,omitempty"`
PromptFile string `json:"prompt_file,omitempty"`
AgentWorkflowRuntime
}
AgentWorkflowInitial is the first run definition (can replace CLI prompt).
type AgentWorkflowLoopConfig ¶
type AgentWorkflowLoopConfig struct {
// TodoFile is the markdown file to scan for [ ] items. Default: TODO.md.
TodoFile string `json:"todo_file,omitempty"`
// GatePromptFile is the system prompt for the gate LLM call that
// parses each TODO section into a structured delegation prompt.
// Required. The gate call uses the agent's existing client.
GatePromptFile string `json:"gate_prompt_file,omitempty"`
// MaxRetries is the number of retry attempts on build failure
// before skipping an item. Default: 2.
MaxRetries int `json:"max_retries,omitempty"`
// MaxIterations caps the agent iterations per item. Default: 50.
MaxIterations int `json:"max_iterations,omitempty"`
// BuildCommand is run after each item to verify. Default: "go build ./...".
BuildCommand string `json:"build_command,omitempty"`
}
AgentWorkflowLoopConfig configures the workflow to iterate over a TODO file, processing each unchecked item independently with a fresh agent context. When Loop is set, Steps are ignored — the loop IS the execution plan.
type AgentWorkflowOrchestrationConfig ¶
type AgentWorkflowOrchestrationConfig struct {
Enabled bool `json:"enabled,omitempty"`
Resume *bool `json:"resume,omitempty"`
YieldOnProviderHandoff *bool `json:"yield_on_provider_handoff,omitempty"`
StateFile string `json:"state_file,omitempty"`
EventsFile string `json:"events_file,omitempty"`
ConversationSessionID string `json:"conversation_session_id,omitempty"`
}
AgentWorkflowOrchestrationConfig enables external orchestration integration.
type AgentWorkflowProgressConfig ¶
type AgentWorkflowProgressConfig struct {
// HeartbeatSeconds is the interval at which the workflow prints a
// progress line ([budget] $X of $Y · iter N · elapsed Tm).
// <= 0 disables the heartbeat. Default 600 (10 min) when Budget is set.
HeartbeatSeconds int `json:"heartbeat_seconds,omitempty"`
}
AgentWorkflowProgressConfig controls runtime visibility of the workflow.
type AgentWorkflowRuntime ¶
type AgentWorkflowRuntime struct {
SkipPrompt *bool `json:"skip_prompt,omitempty"`
Provider string `json:"provider,omitempty"`
Model string `json:"model,omitempty"`
Persona string `json:"persona,omitempty"`
DryRun *bool `json:"dry_run,omitempty"`
MaxIterations *int `json:"max_iterations,omitempty"`
NoStream *bool `json:"no_stream,omitempty"`
SystemPrompt string `json:"system_prompt,omitempty"`
SystemPromptFile string `json:"system_prompt_file,omitempty"`
Unsafe *bool `json:"unsafe,omitempty"`
NoSubagents *bool `json:"no_subagents,omitempty"`
ResourceDirectory string `json:"resource_directory,omitempty"`
ReasoningEffort string `json:"reasoning_effort,omitempty"`
SubagentOverrides WorkflowSubagentOverrides `json:"subagent_overrides,omitempty"`
// RiskProfile selects a named shell-command risk cascade preset
// for this step / initial run (SP-058). One of: readonly,
// cautious, default, permissive, unrestricted. Per-step values
// override the workflow-level initial setting and the global
// config. Unknown values fall through to the agent's default
// resolution chain (override > config > "default").
RiskProfile string `json:"risk_profile,omitempty"`
}
AgentWorkflowRuntime contains runtime options aligned with agent CLI flags.
func (*AgentWorkflowRuntime) Validate ¶
func (r *AgentWorkflowRuntime) Validate(prefix string) error
type AgentWorkflowStep ¶
type AgentWorkflowStep struct {
Name string `json:"name,omitempty"`
Prompt string `json:"prompt,omitempty"`
PromptFile string `json:"prompt_file,omitempty"`
Command string `json:"command,omitempty"`
CommandFile string `json:"command_file,omitempty"`
When string `json:"when,omitempty"`
FileExists []string `json:"file_exists,omitempty"`
FileNotExists []string `json:"file_not_exists,omitempty"`
AgentWorkflowRuntime
}
AgentWorkflowStep is a single step executed after the initial query.
A step is either an agent step (Prompt or PromptFile) or a shell step (Command or CommandFile). The two kinds are mutually exclusive — validation fails if both are set or neither is set.
Shell steps run the command via the user's $SHELL (or /bin/sh) with the workflow's working directory and inherit stdout/stderr. They do NOT trigger model inference; they are useful for cheap, deterministic steps like `make build`, `git status`, or invoking a custom script that prepares state for the next agent step.
func (AgentWorkflowStep) IsShellStep ¶
func (s AgentWorkflowStep) IsShellStep() bool
IsShellStep reports whether the step is configured to run a shell command instead of triggering model inference.
type CLIOverrides ¶
type CLIOverrides struct {
SetWebUI func(disabled bool)
SetWebPort func(port int)
SetDaemon func(enabled bool)
SetNoStream func(enabled bool)
GetNoStream func() bool
// Budget/heartbeat CLI overrides. Zero values mean "inherit JSON".
BudgetUSD float64
BudgetWarn string
HeartbeatSeconds int
}
CLIOverrides provides the callback functions that applyWorkflowCommandOverrides needs to mutate CLI-level global flags (web UI, port, daemon, streaming, budget, heartbeat). The cmd/ package constructs this with closures over its real flag variables.
type QueryExecutor ¶
type QueryExecutor func(ctx context.Context, chatAgent *agent.Agent, eventBus *events.EventBus, query string) error
QueryExecutor is the signature of cmd.ProcessQuery. The runner and loop accept this as a dependency so the workflow package never imports cmd/.
type WorkflowExecutionState ¶
type WorkflowExecutionState struct {
Version int `json:"version"`
InitialCompleted bool `json:"initial_completed"`
NextStepIndex int `json:"next_step_index"`
CurrentTodoLineNum int `json:"current_todo_line_num,omitempty"`
HasError bool `json:"has_error"`
FirstError string `json:"first_error,omitempty"`
LastProvider string `json:"last_provider,omitempty"`
Complete bool `json:"complete"`
UpdatedAt string `json:"updated_at,omitempty"`
}
WorkflowExecutionState tracks workflow execution progress for checkpoint/resume support.
func LoadWorkflowExecutionState ¶
func LoadWorkflowExecutionState(cfg *AgentWorkflowConfig) (*WorkflowExecutionState, error)
func NewWorkflowExecutionState ¶
func NewWorkflowExecutionState() *WorkflowExecutionState
type WorkflowSubagentOverride ¶
type WorkflowSubagentOverride struct {
Provider string `json:"provider,omitempty"`
Model string `json:"model,omitempty"`
}
WorkflowSubagentOverride defines per-persona subagent provider/model overrides.
type WorkflowSubagentOverrides ¶
type WorkflowSubagentOverrides map[string]WorkflowSubagentOverride
WorkflowSubagentOverrides maps persona IDs to their subagent routing overrides. Keys are normalized persona IDs (lowercase, hyphens→underscores). Values override provider/model for subagents with that persona.