Documentation
¶
Overview ¶
Package automate provides shared workflow discovery and validation for the automate/ feature used by both the CLI (cmd/automate.go) and the agent tool layer (pkg/agent/tool_handlers_automate.go).
Index ¶
- Constants
- func CheckMemoryFloor() error
- func Dir() string
- func DirIn(workspaceDir string) string
- func ExtractDescription(path string) (string, error)
- func FinalizeSessionFile(sproutDir string, sessionID string, exitCode int) error
- func FinalizeSessionFileByPath(path string, exitCode int) error
- func GetAutomateSessionDir(baseDir string) (string, error)
- func IsNotExists(err error) bool
- func IsProcessAlive(pid int) bool
- func IsValidFilename(name string) bool
- func RemoveSessionFile(sproutDir string, sessionID string) error
- func ResolvePath(dir string, name string) (string, error)
- func StopProcess(pid int) (bool, error)
- func SweepStaleSessions(sproutDir string) (int, error)
- func VerifyProcessStartedBefore(pid int, startedAt time.Time) bool
- func WriteSessionFile(sproutDir string, sessionID string, info *AutomateSessionInfo) error
- type AllowedPathSummary
- type AutomateSessionInfo
- type BudgetSummary
- type Entry
- type InitialSummary
- type StepSummary
- type SubagentOverrideSummary
- type Summary
Constants ¶
const DefaultMinAvailableBytes int64 = 1536 << 20
DefaultMinAvailableBytes is the MemAvailable floor below which automate workflows refuse to start. A workflow agent is a full sprout process (~400MB+ RSS); launching one on a memory-starved host gets the process killed by the OS low-memory killer minutes in — exit -1, no session trail, tokens already spent. Refusing up front turns that into an actionable error.
Variables ¶
This section is empty.
Functions ¶
func CheckMemoryFloor ¶ added in v0.17.18
func CheckMemoryFloor() error
CheckMemoryFloor returns an error when available memory is below the launch floor. Floor precedence: $SPROUT_AUTOMATE_MIN_MEM_MB (0 disables the check) → DefaultMinAvailableBytes. Platforms without a reader and unreadable metrics never block a launch — measurement failure is not a reason to refuse.
func Dir ¶
func Dir() string
Dir returns the default automate directory path (cwd + "/automate").
CAUTION: in daemon mode (SPROUT_SERVICE=1), os.Getwd() returns the daemon root, not the active workspace. Tool handlers in the agent must use DirIn with the workspace root from the active Agent (a.GetWorkspaceRoot()) or context (pkg/filesystem.WorkspaceRootFromContext), not Dir(). See SP-119 for the workspace-aware flow.
func DirIn ¶ added in v0.17.2
DirIn returns the automate directory inside the given workspace directory. Returns the CWD-based Dir() when workspaceDir is empty (or whitespace-only), preserving CLI behavior where the user's shell CWD IS the workspace root.
This is the workspace-aware counterpart to Dir(). Use DirIn from any code that knows the active workspace (e.g., agent tools running inside a daemon where os.Getwd() returns the daemon root, not the user's workspace).
func ExtractDescription ¶
ExtractDescription reads a workflow JSON file and returns its description field.
func FinalizeSessionFile ¶ added in v0.17.18
FinalizeSessionFile records the process outcome on an existing session record: EndedAt, ExitCode, and a Status of "success" (exit 0) or "error". The PID is zeroed so IsProcessAlive never matches a recycled PID later.
func FinalizeSessionFileByPath ¶ added in v0.17.20
FinalizeSessionFileByPath is FinalizeSessionFile for callers that hold the record path itself — the detached workflow child, which only knows the path the launcher passed it via --automate-session-file and must write its own end state because no process is waiting on it.
func GetAutomateSessionDir ¶ added in v0.16.4
GetAutomateSessionDir returns the .sprout/automate/ directory path. It resolves the sprout directory relative to the given base (typically project root). Creates the directory if it doesn't exist.
func IsNotExists ¶
IsNotExists returns true if the error indicates a missing file or directory.
func IsProcessAlive ¶ added in v0.16.4
IsProcessAlive (Unix) — see pidalive_windows.go for the rationale and SP-112-3 for the deduplication.
func IsValidFilename ¶
IsValidFilename checks if a filename is safe for use as a workflow filename. Only allows alphanumeric characters, dots, underscores, and hyphens, followed by .json. Prevents shell injection via filenames.
func RemoveSessionFile ¶ added in v0.16.4
RemoveSessionFile removes the PID file for a session.
func ResolvePath ¶
ResolvePath finds a workflow file by name, with or without .json extension, and verifies the resolved path stays under the given directory to prevent path traversal attacks.
func StopProcess ¶ added in v0.16.4
StopProcess escalates signals to gracefully (then forcefully) stop a process. It sends SIGINT, waits 10s, then SIGTERM, waits 5s, then SIGKILL, waits 2s. Returns true if the process is confirmed dead after escalation.
func SweepStaleSessions ¶ added in v0.16.4
SweepStaleSessions removes session files for dead sessions whose retention window has passed: finalized records older than sessionRetention after EndedAt, legacy unfinalized records older than sessionRetention after StartedAt. Live or recently-ended records are kept. Returns the number of removed entries. Errors from listing or reading the session directory are returned; errors from individual file removals are silently ignored.
func VerifyProcessStartedBefore ¶ added in v0.16.18
VerifyProcessStartedBefore returns true if the process at the given PID is confirmed to have started before the cutoff time, providing protection against PID reuse. The caller should record the session's start time and pass it here before signaling.
Returns false when the process at this PID started after the cutoff (indicating the original process died and the OS recycled the PID). Returns true when the process is not alive (nothing to signal) or on platforms where the check is unavailable (fail-open).
func WriteSessionFile ¶ added in v0.16.4
func WriteSessionFile(sproutDir string, sessionID string, info *AutomateSessionInfo) error
WriteSessionFile writes a session info JSON to .sprout/automate/<sessionID>.json. Creates the directory if needed. A record with empty Status is normalized to "running" so consumers never see a third state.
Types ¶
type AllowedPathSummary ¶ added in v0.17.7
type AllowedPathSummary struct {
Path string `json:"path"`
Mode string `json:"mode"`
Reason string `json:"reason,omitempty"`
}
AllowedPathSummary is the display-only mirror of workflow.AllowedPath. It deliberately does NOT carry a Validate method — the parser runs workflow.AllowedPath.Validate() once during Summarize, so the summary only contains entries that already passed validation.
type AutomateSessionInfo ¶ added in v0.16.4
type AutomateSessionInfo struct {
Workflow string `json:"workflow"`
PID int `json:"pid"`
StartedAt time.Time `json:"started_at"`
OutputFilePath string `json:"output_file_path,omitempty"`
BudgetUSD *float64 `json:"budget_usd,omitempty"`
Kind string `json:"kind"` // always "automate"
// EndedAt, ExitCode, and Status are set when the session's process
// exits (FinalizeSessionFile). Empty Status means the session was
// written by a pre-finalization build and never finalized — PID
// liveness is the fallback for those legacy records.
EndedAt *time.Time `json:"ended_at,omitempty"`
ExitCode *int `json:"exit_code,omitempty"`
Status string `json:"status,omitempty"`
}
AutomateSessionInfo is the schema for .sprout/automate/<session_id>.json PID files.
func ListSessionFiles ¶ added in v0.16.4
func ListSessionFiles(sproutDir string) ([]AutomateSessionInfo, error)
ListSessionFiles reads all session files in .sprout/automate/ and returns them.
func ReadSessionFile ¶ added in v0.16.4
func ReadSessionFile(sproutDir string, sessionID string) (*AutomateSessionInfo, error)
ReadSessionFile reads and parses a single session file.
type BudgetSummary ¶ added in v0.16.4
BudgetSummary mirrors the cmd-level budget config in a package that has no cmd dependency, so the overview renderer can display it.
type Entry ¶
type Entry struct {
Filename string `json:"name"`
FilePath string
Description string `json:"description,omitempty"`
}
Entry represents a discovered workflow file with its metadata.
type InitialSummary ¶ added in v0.16.4
type InitialSummary struct {
Persona string `json:"persona,omitempty"`
Provider string `json:"provider,omitempty"`
Model string `json:"model,omitempty"`
MaxIterations int `json:"max_iterations"`
RiskProfile string `json:"risk_profile,omitempty"`
HasPrompt bool `json:"has_prompt"`
SubagentOverrides []SubagentOverrideSummary `json:"subagent_overrides,omitempty"`
AllowedPaths []AllowedPathSummary `json:"allowed_paths,omitempty"`
}
InitialSummary describes the initial run.
type StepSummary ¶ added in v0.16.4
type StepSummary struct {
Name string `json:"name,omitempty"`
Kind string `json:"kind"`
Persona string `json:"persona,omitempty"`
Provider string `json:"provider,omitempty"`
Model string `json:"model,omitempty"`
When string `json:"when,omitempty"`
CommandPreview string `json:"command_preview,omitempty"`
AllowedPaths []AllowedPathSummary `json:"allowed_paths,omitempty"`
}
StepSummary describes a single workflow step.
Kind is one of "agent" (LLM inference) or "shell" (raw command). For shell steps, CommandPreview holds a single-line excerpt of the command for display.
type SubagentOverrideSummary ¶ added in v0.16.4
type SubagentOverrideSummary struct {
Persona string `json:"persona"`
Provider string `json:"provider,omitempty"`
Model string `json:"model,omitempty"`
}
SubagentOverrideSummary describes one entry of subagent_overrides for display.
type Summary ¶ added in v0.16.4
type Summary struct {
Description string `json:"description,omitempty"`
ContinueOnError bool `json:"continue_on_error,omitempty"`
NoWebUI bool `json:"no_web_ui,omitempty"`
Initial *InitialSummary `json:"initial,omitempty"`
Steps []StepSummary `json:"steps,omitempty"`
Budget *BudgetSummary `json:"budget,omitempty"`
// RequiresApproval reports whether the run_automate tool path should
// prompt the user before launching this workflow. nil means the field
// was unset in JSON (defaults to true). Explicit false marks the
// workflow as agent-runnable without user confirmation. Serialized
// as `null` when nil — the field is intentionally NOT omitempty so
// the WebUI can distinguish "unset (defaults to required)" from
// "absent (treated as not_required)".
RequiresApproval *bool `json:"requires_approval"`
// SubagentTimeoutSeconds overrides the per-run_subagent tool timeout
// (default 1800 = 30 minutes). nil means use the default. Same nil-
// semantics as RequiresApproval — serialized as `null` when unset.
SubagentTimeoutSeconds *int `json:"subagent_timeout_seconds"`
// AllowedPaths is the display-only view of the workflow's
// declared allowed_paths entries. Populated by Summarize after
// the same Validate() that the loader runs, so a malformed entry
// surfaces as a parse error rather than silently dropping the
// whole field. Entries are sorted by path for stable display.
AllowedPaths []AllowedPathSummary `json:"allowed_paths,omitempty"`
// Warnings collects advisory messages produced while building the
// summary — currently the system-prefix warning when an
// allowed_path falls under /etc, /usr, /var, etc. The CLI and
// WebUI render these alongside the allowed_paths block so the
// user sees the "this workflow touches platform infrastructure"
// heads-up even when the path itself is well-formed.
Warnings []string `json:"warnings,omitempty"`
}
Summary describes the structure of a workflow file at a glance. It is produced by Summarize and used by the CLI to render a human-readable overview before kicking off the workflow. JSON tags use snake_case to match the rest of the WebUI API surface; nil pointers are serialized as `null` (no omitempty) so `requires_approval` and `subagent_timeout_seconds` are always visible to the frontend — the original 3-state semantics (unset = default, true, false) must round-trip through the wire without collapsing to "absent."
func Summarize ¶ added in v0.16.4
Summarize parses a workflow file and returns its high-level structure. Fields the JSON does not specify are left at their zero value.
func (*Summary) IsApprovalRequired ¶ added in v0.16.4
IsApprovalRequired returns true unless the workflow JSON explicitly declared requires_approval: false. Used by the agent tool path to decide whether to surface the intent-confirmation prompt.