Documentation
¶
Overview ¶
Package harness runs a workflow of minimal markdown skills: a router picks the skills for a task, each skill is one narrow prompt with a success check, and a loop runs them in order, feeding each stage's output to the next and stopping at the first stage that fails its check after retries.
The three layers live in their own files: router.go, skill.go and loop.go; workflow.go parses the workflow file that ties them together. Each stage is an ordinary single tinyagent run.
Index ¶
- Constants
- func LoadSkills(dir string) (map[string]*Skill, error)
- func Prompt(idx, n int, st Stage, task, prev, prevName, cwd, feedback string) string
- type Attempt
- type Check
- type Options
- type Pipeline
- type Result
- type Route
- type RouteError
- type RunAgent
- type Settings
- type Skill
- type Stage
- type Workflow
Constants ¶
const ( StatusPassed = "Passed" StatusFailed = "Failed" StatusInterrupted = "Interrupted" )
Workflow statuses.
const ( MaxCheckOutput = 8 << 10 MaxFeedback = 4 << 10 )
MaxCheckOutput caps a check command's recorded output, and MaxFeedback the failure reason handed to a retry.
const ( // DefaultRetries is how many times a failed stage is retried. DefaultRetries = 1 // MaxRetries bounds retries per stage. MaxRetries = 5 // DefaultStepLimit is each stage run's --step-limit unless overridden. DefaultStepLimit = 50 // DefaultCheckTimeout bounds a check command, in seconds. DefaultCheckTimeout = 300 // MaxStages bounds a workflow's length. MaxStages = 20 // MaxWorkflowBytes caps the workflow file. MaxWorkflowBytes = 256 << 10 )
Defaults and bounds.
const MaxSkillBytes = 256 << 10
MaxSkillBytes caps the size of one skill file.
const MinRouteScore = 2
MinRouteScore is how many distinct content words a task must share with an intent for the router to pick it.
Variables ¶
This section is empty.
Functions ¶
func LoadSkills ¶
LoadSkills reads every skill in dir: <name>.md files and <name>/SKILL.md folders. A name defined both ways, an invalid name or an unparseable file is an error.
Types ¶
type Attempt ¶
type Attempt struct {
Stage int `json:"stage"` // 1-based
Skill string `json:"skill"`
Attempt int `json:"attempt"` // 1-based
Input string `json:"input"`
Output string `json:"output"`
ExitStatus string `json:"exit_status"`
Check Check `json:"check"`
Steps int `json:"steps"`
CostUSD float64 `json:"cost_usd"`
Trajectory string `json:"trajectory,omitempty"`
Log string `json:"log,omitempty"`
DurationMS int64 `json:"duration_ms"`
}
Attempt is one run of one stage.
type Check ¶
type Check struct {
Method string `json:"method"` // "command" or "model"
Passed bool `json:"passed"`
Reason string `json:"reason,omitempty"`
Argv []string `json:"argv,omitempty"`
ExitCode int `json:"exit_code,omitempty"`
TimedOut bool `json:"timed_out,omitempty"`
Output string `json:"output,omitempty"`
}
Check is the outcome of a stage's success check.
type Options ¶
type Options struct {
Route Route
Task string
Cwd string // absolute working directory shared by every stage
// ExtraArgs are forwarded to every stage run before the harness's own.
ExtraArgs []string
// Dir receives each attempt's trajectory and log.
Dir string
RunAgent RunAgent
// Emit, if set, receives workflow.* and stage.* events.
Emit func(kind string, fields map[string]any)
// Progress, if set, receives one line per stage attempt.
Progress io.Writer
}
Options configure Run.
type Pipeline ¶
type Pipeline struct {
Name string `json:"name"`
Intent string `json:"intent,omitempty"`
Stages []Stage `json:"stages"`
}
Pipeline is one named, resolved workflow.
type Result ¶
type Result struct {
SchemaVersion int `json:"schema_version"`
Kind string `json:"kind"` // "workflow"
Workflow string `json:"workflow"`
Route Route `json:"route"`
Task string `json:"task"`
Cwd string `json:"cwd"`
Status string `json:"status"`
FailedStage string `json:"failed_stage,omitempty"`
Reason string `json:"reason,omitempty"`
Output string `json:"output,omitempty"` // the last stage's output
Stages []Stage `json:"stages"`
Attempts []Attempt `json:"attempts"`
CostUSD float64 `json:"cost_usd"`
DurationMS int64 `json:"duration_ms"`
}
Result is a whole workflow run; it is what --output writes.
type Route ¶
type Route struct {
Pipeline *Pipeline `json:"-"`
Name string `json:"name"`
Kind string `json:"kind"` // "workflow" or "skill"
// How is "named" (--workflow-name), "intent" (matched the task) or
// "default" (the file's default).
How string `json:"how"`
Score int `json:"score,omitempty"`
}
Route is the router's decision: the pipeline to run and why.
type RouteError ¶
type RouteError struct{ Msg string }
RouteError is returned when the router can't choose; no stage has run.
func (*RouteError) Error ¶
func (e *RouteError) Error() string
type Settings ¶
type Settings struct {
Model string `yaml:"model" json:"model,omitempty"`
Profile string `yaml:"profile" json:"profile,omitempty"`
StepLimit *int `yaml:"step_limit" json:"step_limit,omitempty"`
CostLimit *float64 `yaml:"cost_limit" json:"cost_limit,omitempty"`
TimeLimit *int `yaml:"time_limit" json:"time_limit,omitempty"`
Retries *int `yaml:"retries" json:"retries,omitempty"`
CheckTimeout *int `yaml:"check_timeout" json:"check_timeout,omitempty"`
}
Settings are the overridable per-stage settings; zero values inherit.
type Skill ¶
type Skill struct {
Name string `json:"name"`
Title string `json:"title,omitempty"`
Intent string `json:"intent"`
Inputs []string `json:"inputs"`
SuccessCheck string `json:"success_check,omitempty"`
// Body is the file's Markdown with any YAML frontmatter removed; it
// is the stage's instructions.
Body string `json:"-"`
Path string `json:"path"`
}
Skill is one parsed skill file: a heading, an intent line, declared inputs and a one-line success check. Everything else (model, limits, retries, check commands) comes from the workflow file's defaults.
type Stage ¶
type Stage struct {
Skill *Skill `json:"skill"`
// Check is a command whose exit status decides the stage's success
// check; when empty the model's own verdict on the skill's success
// check line decides.
Check []string `json:"check,omitempty"`
CheckTimeout int `json:"check_timeout"`
Retries int `json:"retries"`
Model string `json:"model,omitempty"`
Profile string `json:"profile,omitempty"`
StepLimit int `json:"step_limit"`
CostLimit float64 `json:"cost_limit,omitempty"`
TimeLimit int `json:"time_limit,omitempty"`
Inputs map[string]string `json:"inputs,omitempty"`
}
Stage is one resolved workflow step.
type Workflow ¶
type Workflow struct {
Path string
SkillsDir string
Default string
Skills map[string]*Skill
Pipelines map[string]*Pipeline
// contains filtered or unexported fields
}
Workflow is a parsed, validated workflow file.
func Load ¶
Load parses and validates the workflow file at path and every skill in its skills directory. Any problem is returned before a model is called.
func (*Workflow) Route ¶
Route picks what to run for task. An explicit name (a workflow or a skill) wins; otherwise the task is matched against every workflow's intent and every skill's intent line, and the single best match with at least MinRouteScore shared words is chosen. With no match (or a tie) the file's default is used if it has one; otherwise Route returns a RouteError rather than guessing.