runner

package
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Oct 8, 2026 License: MIT Imports: 17 Imported by: 0

Documentation

Overview

Package runner is ynf's runner port (ADR-012): what a lane's inner loop is. ynh is one provider and any command is another; the core only ever sees ynf's own outcome vocabulary.

Index

Constants

View Source
const (
	// FeatureHarnessPath is `ynh agent run --harness <path>`: a folder runs without an install.
	FeatureHarnessPath = "agent-run-harness-path"
	// FeatureFetchesIncludes is a run fetching the harness's includes at setup.
	FeatureFetchesIncludes = "agent-run-fetches-includes"
)

The ynh features ynf uses.

View Source
const (
	Converged     = "converged"
	Budget        = "budget"
	Stuck         = "stuck"
	Tamper        = "tamper"
	OperatorError = "operator_error"
	Error         = "error"
	Aborted       = "aborted"
)

Outcomes are ynf's vocabulary (ADR-012). Lane rules branch on these, never on exit codes.

View Source
const MinYnhCapabilities = "0.9.0"

MinYnhCapabilities is the first ynh capabilities version ynf supports as a detected runner: the one with `--auto-approve`, which the lanes ynf ships use (ADR-012).

View Source
const Ynh = "ynh"

Ynh is the ynh ynf asks and runs: whatever `ynh` resolves to on the PATH ynf was given, as a shell would find it. There is no other lookup (ADR-012).

Variables

View Source
var CoAuthorAddresses = map[string]string{
	"claude": "noreply@anthropic.com",
}

CoAuthorAddresses are the addresses for a commit's Co-Authored-By trailer, by vendor. ynf adds an address only when it knows the vendor's own convention; a vendor not listed gets no trailer.

View Source
var ModelHosts = map[string][]string{
	"claude":  {"api.anthropic.com"},
	"codex":   {"api.openai.com"},
	"cursor":  {"api2.cursor.sh"},
	"copilot": {"api.githubcopilot.com", "api.github.com"},
}

ModelHosts are the API hosts each vendor's CLI needs. The egress proxy allows the lane's vendor's without the lane listing them (ADR-007).

Functions

func AtLeast

func AtLeast(have, want string) bool

AtLeast compares dotted versions numerically; anything unparseable is too old.

func CoAuthorAddress

func CoAuthorAddress(r Result) string

CoAuthorAddress is the Co-Authored-By address for a result: by its backend, else the vendor named before the slash in its model. It is empty when ynf does not know the vendor's address.

func Narrows

func Narrows(declared, scope string) error

Narrows reports why scope is not the declared sensor command narrowed, or nil when it is. Both are split into words with shell quoting rules and not evaluated. The words must match one for one and in order, except that a path word of the declared command may be replaced by one or more path words beneath it (`./...` by `./x/...`, `.` by any relative path); a declared command with no path word may have path words appended at the end. The scope may hold no shell operator or expansion. The declared command's leading NAME=value assignments are kept exactly as written, expansions included, and the scope may not add, drop, reorder or change one.

Types

type CommandRunner

type CommandRunner struct{ Cmd policy.Command }

CommandRunner runs any command: exit 0 is converged, anything else an error, unless the command writes a result file saying more (ADR-012).

func (CommandRunner) Command

func (c CommandRunner) Command(s Spec) ([]string, error)

Command implements Runner. It never uses a shell: each argument is expanded on its own.

func (CommandRunner) Interpret

func (c CommandRunner) Interpret(exit int, stdout []byte, hostRunDir string) Result

Interpret implements Runner.

func (CommandRunner) Name

func (CommandRunner) Name() string

Name implements Runner.

type Detection

type Detection struct {
	Found        bool   `json:"found"`
	Version      string `json:"version,omitempty"`      // ynh's own version
	Capabilities string `json:"capabilities,omitempty"` // the capabilities version ynf checks abilities against
	Detail       string `json:"detail,omitempty"`       // why it was not detected
	// Features are the names ynh lists in `features`; absent means none. They are gated on by
	// name, never by comparing versions (ADR-012).
	Features []string `json:"features,omitempty"`
}

Detection is what detecting ynh on this host found (ADR-012).

func DetectYnh

func DetectYnh(ctx context.Context) Detection

DetectYnh asks the host's ynh for its version. It is detected when the binary answers `version --format json` and its capabilities version is one ynf supports.

func DetectedYnh

func DetectedYnh(ctx context.Context) Detection

DetectedYnh is DetectYnh, done once per process: detection is cheap, but a sweep resolves many lanes.

func (Detection) Has

func (d Detection) Has(feature string) bool

Has reports whether ynh listed the feature.

func (Detection) String

func (d Detection) String() string

String says what was found, for people: "ynh 0.10.0" or "ynh not found".

type Focus

type Focus struct {
	Prompt  string `json:"prompt"`
	Profile string `json:"profile"`
}

Focus is a ynh focus: a named prompt, optionally with a profile.

func ResolveFocus

func ResolveFocus(harnessDir, name string) (Focus, error)

ResolveFocus reads a focus from the harness manifest in harnessDir.

type Harness

type Harness struct {
	// ID is the installed harness's id, when it was read from an installed ynh (an image's).
	ID      string           `json:"-"`
	Focuses map[string]Focus `json:"focuses"`
	// EnvPassthrough is every variable ynh lets reach the agent worker. ynh strips everything
	// else, deliberately, model credentials and proxy settings included.
	EnvPassthrough []string `json:"env_passthrough"`
	// Agent is the harness's own budgets, which a lane may only tighten (ADR-006).
	Agent struct {
		MaxTurns  int    `json:"max_turns"`
		MaxTokens int    `json:"max_tokens"`
		MaxWall   string `json:"max_wall"`
	} `json:"agent"`
	// Sensors are the sensors the harness declares, by name; a lane may only scope these.
	Sensors map[string]json.RawMessage `json:"sensors"`
}

Harness is what ynf reads from a ynh harness manifest: from the image that runs it (ADR-012), or from the folder ynh runs it from on the host.

func ParseManifest

func ParseManifest(b []byte) (Harness, error)

ParseManifest reads a harness manifest.

func ReadHarness

func ReadHarness(harnessDir string) (Harness, error)

ReadHarness reads the manifest of the harness in harnessDir.

func (Harness) CheckLane

func (h Harness) CheckLane(y policy.Ynh) error

CheckLane holds a lane to its harness (ADR-006): budgets may only tighten the harness's own, and a sensor scope may only name a sensor the harness declares and narrow its command. With no item's labels yet, a scope's placeholders stand for a safe path segment, so its shape is still checked; CheckScopes checks it with an item's own.

func (Harness) CheckScopes

func (h Harness) CheckScopes(y policy.Ynh, labels []string) error

CheckScopes holds a lane to its harness as CheckLane does, with the scopes' placeholders filled from the item's labels.

func (Harness) Focus

func (h Harness) Focus(name string) (Focus, error)

Focus returns a named focus. `ynh agent run` takes a focus or a task, never both, and ynf always has a task (the ticket), so it puts the focus's prompt in the task itself and passes the focus's profile.

func (Harness) NotPassed

func (h Harness) NotPassed(names []string) []string

NotPassed returns the names the harness does not pass through to its worker.

type HarnessSource

type HarnessSource struct {
	Dir string
	Pin *policy.Pin
}

HarnessSource is a harness to install for one run: a folder (on disk, in the run's checkout or absolute), or a pin from a repository. Exactly one of Dir and Pin is set.

func (HarnessSource) String

func (s HarnessSource) String() string

type Installed

type Installed struct {
	// ID is what `ynh agent run --harness` takes for it.
	ID      string
	Name    string
	Version string
	// Commit is the commit it was installed from; empty for a folder, which has none.
	Commit string
	// Pin is the repository and ref it was installed from, for a pin; empty for a folder.
	Pin string
	// PinURL is Pin with the repository as ynh was given it (a bare host and path made
	// https://host/path); empty for a folder.
	PinURL string
	// Path is where ynh keeps the installed harness, which holds its manifest.
	Path string
}

Installed is a harness as ynh installed it into a run's own home.

func InstallHarness

func InstallHarness(ctx context.Context, src HarnessSource, ynhHome string) (Installed, error)

InstallHarness installs src into a ynh home of its own, ynhHome, and says what ynh installed. A pin is made an id this way, and so is a folder for a ynh that does not list agent-run-harness-path; one that does is given the folder directly and never reaches here. A folder is run by ynh from where it is; a pin is cloned at its tag or commit, which is the one step that reaches the network, and it happens here, before the run starts and on the host, never inside the run's containment. The home is passed as YNH_HOME to every ynh command here and replaces the operator's, so the operator's own home is never read or written. The run needs the same YNH_HOME.

func (Installed) Label

func (i Installed) Label() string

Label is id@version, as ynh reports a harness in a run's result.

type Resolved

type Resolved struct {
	Runner Runner
	// Detected is true when the lane named no runner and detection chose this one.
	Detected bool
	// Note says what an unnamed lane resolved to on this host, such as "ynh (detected 0.10.0)" or
	// "command (ynh not found)"; empty when the lane named its runner.
	Note string
}

Resolved is the runner a lane gets and how it got it.

func Resolve

func Resolve(lane policy.Lane, ynh Detection) (Resolved, error)

Resolve is the precedence of ADR-012: the runner a lane names, else ynh when it has a ynh block and ynh was detected, else its command. A lane that names a runner never falls back.

type Result

type Result struct {
	Outcome string `json:"outcome"`
	Detail  string `json:"detail,omitempty"`
	Model   string `json:"model,omitempty"`   // backend/model, when the runner reports one
	Session string `json:"session,omitempty"` // runner session id (ynh: session_id)
	Usage
}

Result is a run's outcome in ynf's terms.

type Runner

type Runner interface {
	Name() string
	// Command returns the argv to run inside the worktree.
	Command(spec Spec) ([]string, error)
	// Interpret maps an exit code (and anything the run wrote to hostRunDir) to a Result.
	Interpret(exitCode int, stdout []byte, hostRunDir string) Result
}

Runner builds the command a lane runs and interprets how it ended.

func For

func For(lane policy.Lane) (Runner, error)

For returns the runner a lane names. A lane that names none is Resolve's.

type Spec

type Spec struct {
	Lane     policy.Lane
	Labels   []string
	Task     string // the task text, written to TaskFile by the engine
	TaskFile string // path inside the worktree's step folder, as the command sees it
	RunDir   string // the step folder, as the command sees it
	Feedback string
	// InImage is set when the run happens in an image built by `ynh image --entrypoint agent`,
	// whose entrypoint is already `ynh agent run --harness local/<name>`.
	InImage bool
	// Contained is set when the run is contained (ADR-007): in an image, or inline in a container
	// the operator provides.
	Contained bool
	// Focus is the lane's focus, resolved from the harness: its prompt is already in the task.
	Focus *Focus
	// HostAutoApprove is --auto-approve asked for by the person who started the work, for a run on
	// their own machine (ADR-007). A lane's setting never applies outside containment.
	HostAutoApprove string
}

Spec is what a runner is asked to do.

type Usage

type Usage struct {
	Effort string `json:"effort,omitempty"` // reasoning effort, when the runner reports it
	// EffortRequested is the effort the run was asked for, when it was: a lane's effort or the
	// harness's default. A backend may run at another, which Effort then shows.
	EffortRequested string `json:"effort_requested,omitempty"`
	Turns           int    `json:"turns,omitempty"`  // agent turns
	Tokens          int    `json:"tokens,omitempty"` // tokens consumed: input and output
	// The split, when the runner reports it. Cache reads are not in Tokens, and are often most of
	// what a long session costs.
	InputTokens     int `json:"input_tokens,omitempty"`
	OutputTokens    int `json:"output_tokens,omitempty"`
	CacheReadTokens int `json:"cache_read_tokens,omitempty"`
	// Backend is the vendor CLI that ran, such as claude: what is known when the model is not.
	Backend string  `json:"backend,omitempty"`
	CostUSD float64 `json:"cost_usd,omitempty"` // when the runner reports it; ynf never prices tokens
	BoundBy string  `json:"bound_by,omitempty"` // the cap that ended the run, if one did
	// Harness is name@version, and HarnessSHA the commit it was installed from: a harness change is
	// a confounder when comparing models, not a model effect.
	Harness       string `json:"harness,omitempty"`
	HarnessSHA    string `json:"harness_sha,omitempty"`
	RunnerVersion string `json:"runner_version,omitempty"` // ynh's version
	// FailedSensors names the sensors that had failed when the run ended, in the runner's order.
	FailedSensors []string `json:"failed_sensors,omitempty"`
	AutoApprove   string   `json:"auto_approve,omitempty"`
}

Usage is what a run spent and on what, when its runner reports it, so outcomes can be compared by model and effort (ADR-011). A command runner's result file may report it too.

type YnhRunner

type YnhRunner struct{ Cfg policy.Ynh }

YnhRunner runs `ynh agent run` (ADR-012). It owns the mapping from ynh's exit codes to ynf's outcomes, and every ynh-only setting: budgets, sensor scoping, the trajectory path.

func (YnhRunner) Command

func (y YnhRunner) Command(s Spec) ([]string, error)

Command implements Runner. In an agent image the entrypoint already names ynh agent run and the harness, so only the flags are passed.

func (YnhRunner) Interpret

func (y YnhRunner) Interpret(exit int, stdout []byte, _ string) Result

Interpret implements Runner. ynh prints one JSON object on every path with --format json.

func (YnhRunner) Name

func (YnhRunner) Name() string

Name implements Runner.

func (YnhRunner) Vendor

func (y YnhRunner) Vendor() string

Vendor is the lane's vendor, claude unless it says otherwise.

Jump to

Keyboard shortcuts

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