Documentation
¶
Overview ¶
Package runner runs a delegated role through a command-line harness the operator named, with the same brief, inputs and output contract the host's own sub-agent gets (itd-2609201916056194, spc-2609221533057881).
A role's route is roles.<role>.runner in the layered configuration (config.go): host, the default, or a runner this machine enabled under runner.<name>. The shipped runners are the claude CLI in print mode with the bare flag (claude.go) and opencode's run mode (opencode.go). Each is started as a process (proc.go): the argv is a vector and never a shell line, the binary is resolved on PATH and refused inside the repository, the environment is the parent's scrubbed of every git repository-selection and config-injection variable, stdin is the null device, output is bounded, and a run past its time is killed with the process group it leads.
The fallback is decided in one place (dispatch.go): an absent, refusing, failing or unparsable runner, or an answer the contract's validator refuses, hands the role to the host session, or with no host session to the host the operator configured (runner.fallback_host), and writes one receipt naming the role, the runner asked for, the reason and the route that ran. Tally counts those receipts per runner and per role for the run's summary.
The package never prints. It starts processes only through Dispatch and the adapters' Run, and never logs a harness in: a harness's credential is its own, read from its own environment or configuration, and abcd never puts one on a command line, in an error or in a receipt.
Index ¶
- Constants
- Variables
- type Answer
- type ClaudeCLI
- type Command
- type Config
- type Counts
- type Dispatcher
- type Failure
- type FallbackReceipt
- type HistoryStore
- type OpenCodeCLI
- type Outcome
- type Output
- type Quota
- type QuotaReporter
- type Reason
- type Request
- type RoleReceipt
- type Route
- type RouteRecord
- type Runner
- type RunnerConfig
- type TranscriptStore
Constants ¶
const ( Host = "host" Claude = "claude" OpenCode = "opencode" )
The routes a role may name. Host is the default.
const DefaultTimeout = 60 * time.Minute
DefaultTimeout bounds one run when the request names no timeout.
Variables ¶
var ( ErrNotOnPath = errors.New("not on PATH") ErrRefused = errors.New("refused before it is run") ErrNotStarted = errors.New("could not be started") )
The launch stages a Failure can name through errors.Is: the program was not found, it was found and refused, or it was admitted and could not start. Every other failure (a non-zero exit, a run past its time, an overrun bound) is none of them.
Functions ¶
This section is empty.
Types ¶
type Answer ¶
type Answer struct {
// Text is the harness's final message.
Text string
// Model is the model the harness reports, or the one it was asked for when
// it reports none.
Model string
// SessionID is the harness's own session id, "" when it reports none.
SessionID string
}
Answer is the one answer shape every adapter parses its harness's events into. The contract's own answer is the receipt file; this is what the harness said about the run.
type ClaudeCLI ¶
type ClaudeCLI struct {
// contains filtered or unexported fields
}
ClaudeCLI is the claude CLI runner.
type Command ¶ added in v0.13.0
type Command struct {
// Name is the program's fixed name, looked up on PATH; a name carrying a
// path separator is refused, so no caller can point Exec at a file.
Name string
// Args is the argument vector, never a shell line.
Args []string
// Dir is the working directory the program runs in; empty runs it at the
// file-system root, never in the caller's directory, which for abcd is
// usually a project whose settings a vendor binary must not read.
Dir string
// Guards are the folders a program resolving inside is refused from,
// lexically or through a link: repository content is never run. A caller
// running in or for a project passes it here; Exec cannot know it.
Guards []string
// Timeout bounds the run; past it the program's group is killed. A
// command without one is never run.
Timeout time.Duration
// MaxStdout and MaxStderr bound each stream; past either the run fails.
MaxStdout, MaxStderr int
}
Command is one program found on PATH that another package runs under this file's discipline: the one launch primitive abcd has for a vendor binary, so a second copy of the admission, the group kill and the bounds is never written. Zero output bounds take a harness's own.
type Config ¶
type Config struct {
// Diagnostics are the non-fatal reports the read produced, one line each,
// for a front door to print on stderr.
Diagnostics []string
// contains filtered or unexported fields
}
Config is the runner configuration one invocation read.
func Load ¶
Load reads and validates the runner configuration. A fault is an error naming the file and the key; none falls through to a default.
func (*Config) FallbackHost ¶
FallbackHost is the runner that runs a role when there is no host session, "" when none is configured.
func (*Config) Quota ¶ added in v0.14.0
Quota asks the runner the machine enabled under name for its remaining quota. The host, and a name the machine has not enabled, report none.
type Counts ¶
type Counts struct {
Total int `json:"total"`
ByRunner map[string]int `json:"by_runner"`
ByRole map[string]int `json:"by_role"`
}
Counts is the fallback intel a run's summary reports.
func Tally ¶
func Tally(rs []FallbackReceipt) Counts
Tally counts fallback receipts per runner asked for and per role. A run with none reports zero, with empty maps rather than none.
type Dispatcher ¶
type Dispatcher struct {
// Config is the runner configuration read at the lane's start.
Config *Config
// HostSession is true when a host session drives the loop and can take a
// role handed back to it.
HostSession bool
// Attended is true when the person is at the terminal with no host session
// (the plain-Terminal interviews, spc-2610030911534855): a role routed to a
// runner runs there although no runner.fallback_host is configured, and a
// runner that does not answer stops the run, its fallback receipt naming
// none as the route that ran, rather than leaving the role nowhere to land.
// A role on the host still needs a host session or a configured host.
Attended bool
// Validate is the contract's validator: the same check the host sub-agent's
// answer passes. It is handed the runner that ran the role, so a caller
// whose validator also records the answer (the loop's receipt verifier)
// can name the route that ran.
Validate func(runner string, req Request, ans Answer) error
// Transcripts is where every runner's transcript lands.
Transcripts TranscriptStore
// Record appends one fallback receipt to the run's state.
Record func(FallbackReceipt) error
// Prepare, when set, readies req's files for the runner about to run it,
// before each runner starts: the routed one and a fallback host alike. A
// *Failure it returns is that runner's failure, recorded and fallen back
// on as a launch's would be, with nothing launched; any other error ends
// the dispatch.
Prepare func(runner string, req Request) error
// Now is the clock the receipts are stamped with; time.Now when nil.
Now func() time.Time
}
Dispatcher runs roles by their routes.
type Failure ¶
type Failure struct {
Runner string
Reason Reason
Detail string
// contains filtered or unexported fields
}
Failure is a runner's failure. Detail is written by abcd, never copied from the harness's output, so it cannot carry anything the harness printed, a credential it echoed included.
type FallbackReceipt ¶
type FallbackReceipt struct {
At time.Time `json:"at"`
Role string `json:"role"`
Asked string `json:"asked"`
Reason Reason `json:"reason"`
// Detail is abcd's own account of the reason; it never carries the
// harness's output.
Detail string `json:"detail"`
Ran string `json:"ran"`
}
FallbackReceipt is one fallback: the role, the runner asked for, the reason, and the route that ran instead.
type HistoryStore ¶
HistoryStore is the production store: the repository's lane of the user-level transcript store, keyed on its root commit.
type OpenCodeCLI ¶
type OpenCodeCLI struct {
// contains filtered or unexported fields
}
OpenCodeCLI is the opencode runner.
type Outcome ¶
type Outcome struct {
// Handoff is true when the host session runs the role, exactly as it does
// with no runner configured: the caller hands the host the brief and
// awaits its receipt.
Handoff bool
// Answer is the runner's parsed answer when a runner ran the role.
Answer *Answer
// Fallback is the receipt this dispatch recorded, nil when none.
Fallback *FallbackReceipt
// Receipt is the role's receipt block.
Receipt RoleReceipt
}
Outcome is one dispatch's result.
type Output ¶ added in v0.13.0
type Output struct {
Stdout, Stderr []byte
}
Output is what an Exec run wrote, each stream up to its bound.
func Exec ¶ added in v0.13.0
Exec admits c.Name exactly as a harness is admitted (admit) and runs it exactly as a harness is run (run): the scrubbed environment, the null device on stdin, the bounds, and the process group killed through this handle at the timeout or when ctx ends. A failure is a *Failure, whose errors.Is names the stage for ErrNotOnPath, ErrRefused and ErrNotStarted; the output is returned whenever the program produced some.
type Quota ¶ added in v0.14.0
type Quota struct {
Remaining int `json:"remaining"`
}
Quota is what a runner reports of its budget before a run starts (itd-2609201925079472 criterion 7): the agent runs it can still start in its current window. A runner whose harness counts its budget in another unit converts it, or reports none.
type QuotaReporter ¶ added in v0.14.0
QuotaReporter is a Runner that reports its remaining quota. Neither shipped runner is one: the claude CLI and opencode expose no remaining quota a launch can read before it starts, so a run routed to either names it and skips the budget check out loud.
type Reason ¶
type Reason string
Reason is why a runner did not answer: the kinds the fallback records.
const ( // ReasonAbsent: the runner is not enabled on this machine, or its binary // is not on PATH or is refused. ReasonAbsent Reason = "absent" // ReasonRefused: the harness ran and reported that it refused or erred. ReasonRefused Reason = "refused" // ReasonFailed: the harness could not start, exited non-zero, ran out of // time or overran its output bound. ReasonFailed Reason = "failed" // ReasonUnparsable: the harness's output is not its structured events. ReasonUnparsable Reason = "unparsable" // ReasonInvalid: the answer failed the contract's validator. ReasonInvalid Reason = "invalid" // ReasonRateLimited: the harness reported a rate-limit response and did // not finish. It is never fallen back on (dispatch.go): every route spends // the budget the run's window paces, so the caller ends the window // (itd-2609201925079472 criterion 8). ReasonRateLimited Reason = "rate-limited" )
type Request ¶
type Request struct {
// Role is the agent the run plays: an agent in the roster, or the
// implementer.
Role string
// Brief is the absolute path of the brief the role follows; it states the
// task and the output contract.
Brief string
// Receipt is the absolute path the contract says the answer is written to.
Receipt string
// Dir is the absolute path of the working tree the role runs in.
Dir string
// Checkout is the absolute path of the checkout the run belongs to when
// Dir is a worktree of it (a lane's worktree lives outside it); a program
// inside it is repository content and is never run, as one inside Dir is
// not. Empty when Dir is the checkout itself.
Checkout string
// Tools are the tools the role's contract grants, granted without a
// prompt; none granted when empty.
Tools []string
// SessionID names the run's transcript record when the harness reports no
// session of its own.
SessionID string
// Timeout bounds the run; DefaultTimeout when zero.
Timeout time.Duration
}
Request is what a role is handed: the same the host sub-agent is handed.
type RoleReceipt ¶
type RoleReceipt struct {
Role string `json:"role"`
Brief string `json:"brief"`
// Contract is the receipt path the contract names.
Contract string `json:"contract"`
Route RouteRecord `json:"route"`
}
RoleReceipt is a role run's receipt block. Everything but Route is the request's, so two runs of one request differ in their route alone.
type RouteRecord ¶
type RouteRecord struct {
// Asked is the route the configuration named: host or a runner.
Asked string `json:"asked"`
// Ran is the route that ran the role: host, a runner, or none.
Ran string `json:"ran"`
// Model is the model the runner that ran it reported; "" on the host,
// whose own receipt reports it.
Model string `json:"model"`
}
RouteRecord is the route a role asked for and the one that ran it.
type Runner ¶
type Runner interface {
// Name is the route's name, as roles.<role>.runner spells it.
Name() string
// Run runs the role and returns the parsed answer and the transcript, the
// harness's raw event stream. A failure is a *Failure; the transcript is
// returned whenever the harness produced one, failure or not.
Run(ctx context.Context, req Request) (Answer, []byte, error)
}
Runner is one route a role can run through.
type RunnerConfig ¶
type RunnerConfig struct {
Name string
// Model is the admitted <provider>/<model> route, "" for the harness's own
// default.
Model string
Origin string
}
RunnerConfig is one runner this machine enabled.