Documentation
¶
Overview ¶
Package harness steps a session: load the file, build the context, call the provider, append what comes back, execute the calls, repeat until the model stops calling tools.
Index ¶
- Constants
- func Handler(ctx context.Context, h *Harness) http.Handler
- func System(model string) string
- func Tools(name string, input []string) []provider.Tool
- type Create
- type Harness
- func (h *Harness) Changed(id string) <-chan struct{}
- func (h *Harness) Create(ctx context.Context, c Create, via string) (string, error)
- func (h *Harness) Delete(ctx context.Context, id, via string) error
- func (h *Harness) Info(id string) (Info, error)
- func (h *Harness) Kick(ctx context.Context, id string)
- func (h *Harness) List() ([]Info, error)
- func (h *Harness) Post(ctx context.Context, id, via, key string, parts []Part) (int, error)
- func (h *Harness) Resume(ctx context.Context) error
- func (h *Harness) Retry(ctx context.Context, id string)
- func (h *Harness) Run(ctx context.Context, id string) (statefile.Status, error)
- func (h *Harness) SetName(id, via, n string) (int, error)
- func (h *Harness) SetProviders(ps map[string]provider.Provider)
- func (h *Harness) SetWorkers(id, via string, workers []statefile.Worker) (int, error)
- func (h *Harness) Step(ctx context.Context, id string) (status statefile.Status, err error)
- func (h *Harness) Stop(ctx context.Context, id, via string) error
- type Info
- type Listed
- type Output
- type Part
- type Pool
- type ProviderInfo
- type Remote
- func (r *Remote) Drives() []worker.Drive
- func (r *Remote) Job(ctx context.Context, session, interpreter, code, job string, ...) (int, string, error)
- func (r *Remote) Kill(ctx context.Context, session string) error
- func (r *Remote) Read(ctx context.Context, path string) ([]byte, error)
- func (r *Remote) Run(ctx context.Context, session, interpreter, code string, timeout int) (Output, error)
- func (r *Remote) Update(ctx context.Context, cmd, v string) error
- func (r *Remote) Write(ctx context.Context, path string, data []byte) error
- type Worker
Constants ¶
const MaxName = 100
MaxName bounds a session's name: a title a list shows on one line.
const MaxPost = 8 << 20
MaxPost bounds one post: a paragraph and a screenshot, typed or pasted by a person. A file goes to a worker, not into the state file.
Variables ¶
This section is empty.
Functions ¶
func Handler ¶
Handler is the harness's HTTP surface: ten verbs on a session, two reads of the environment, and removing a worker. Records go out in the state file's own bytes, one after another.
GET /sessions ?status= ?limit= ?after=<id> -> [Info], newest updated first
POST /sessions Create body; ?via= required with a name -> {"id"}
GET /sessions/{id} -> Info
GET /sessions/{id}/records ?after=N ?wait=30s to long poll -> records
POST /sessions/{id}/records one body, or multipart; ?via= required, -> {"n"}
who is posting. one message record per
part, each with its Content-Type. With
an Idempotency-Key header, a repost
appends only the parts not yet held
under that key and returns the post's n
POST /sessions/{id}/retry step from the tip, whatever it is -> {"status"}
POST /sessions/{id}/stop ?via= required. cut a running step, write -> 204; the records
it down; kill its environments and jobs say what happened
PUT /sessions/{id}/workers [{"name"}]; ?via= required. a config record -> {"n"}
with these workers; the next step sees them
PUT /sessions/{id}/name the name as text; ?via= required. a name -> {"n"}
record; empty removes the name
DELETE /sessions/{id} ?via= required. the file is gone for good; -> 204
a running step is stopped first
GET /workers -> connected workers' hellos, and refused
ones with the reason
DELETE /workers/{name} the worker leaves the mesh for good; its -> 204
device code still joins new machines
GET /providers -> [{"name","shape","url"}]: what this harness
serves as name/model; no secrets
ctx outlives every request: it is what steps run under.
Types ¶
type Create ¶
type Create struct {
From string `json:"from,omitempty"`
N int `json:"n,omitempty"`
Model string `json:"model,omitempty"`
ContextLimit int `json:"context_limit,omitempty"`
Workers []statefile.Worker `json:"workers,omitempty"`
Tools string `json:"tools,omitempty"`
Input []string `json:"input,omitempty"`
Params map[string]json.RawMessage `json:"params,omitempty"`
Preset string `json:"preset,omitempty"`
Name string `json:"name,omitempty"`
}
Create is the body of POST /sessions. From set is a copy of records 1 through N-1 of that session; otherwise a fresh session from Model, ContextLimit, and Workers. Workers are not checked: the config is the whitelist, and a worker named before it dials in is a worker the session waits for.
type Harness ¶
type Harness struct {
Sessions statefile.Sessions
Providers map[string]provider.Provider // read and replaced under pmu once the harness runs
Workers *Pool
Remove func(name string) error // the coordinator's: the worker leaves the mesh for good
// contains filtered or unexported fields
}
func (*Harness) Create ¶
Create makes a session and returns its id. A fresh one starts with the system prompt, the AGENTS.md scan, and the worker list, so all three reach the provider as system text ahead of the first message. A copy is byte for byte and gets nothing appended: it already holds both.
func (*Harness) Delete ¶ added in v0.5.4
Delete stops the session, then removes its file for good. Append-only is of records: none is ever changed, and a session goes whole. It waits for the stopped step to end, so no step writes to a file that is gone; under the lock a post takes, so no post does. Copies keep their own files, workers their logs.
func (*Harness) Kick ¶
Kick steps a session in the background until it is not open. A kick during a run makes the runner look once more before it stops, so a message that lands as a run ends is not left waiting. Each pass gets its own context so a stop ends one pass and not the next. A pass that ends closed or failed copies the file to the session's workers.
func (*Harness) List ¶
List is every session, newest updated first, from the index. Entries a write touched since the last list are read again; the rest are served from memory. The mark is cleared before the read, so a write that lands during the read marks it again. A file that does not read is logged and left out until its next write. Resume fills the index on start, and touch adds a created session.
func (*Harness) Post ¶
Post appends one message record per part under one lock and kicks the session once, so a text and an image reach the model as one turn. The append is the acknowledgement: a closed session starts stepping on it, a stepping one sees it at its next context build. Returns the last n. A post with a key appends only the parts the file does not hold with that key, so a connector that lost the reply posts again and the post ends whole, once.
func (*Harness) Resume ¶
Resume kicks every session that needs a step. This is the open-files index, rebuilt from the tips on start; a failed session waits for a run or a message. A file that does not read is logged and left out, not a reason to stay down.
func (*Harness) Retry ¶
Retry steps a failed session from its tip: after a stop it continues, after exhausted provider retries it tries again, past the limit it gets the same error again. Nothing is cleared.
func (*Harness) SetName ¶ added in v0.5.3
SetName appends a name record under the lock a post takes. An empty name removes the session's name. The model never sees it and status is unchanged.
func (*Harness) SetProviders ¶ added in v0.4.0
SetProviders replaces the providers: a session's next step uses the new ones, a step running keeps its own.
func (*Harness) SetWorkers ¶ added in v0.5.0
SetWorkers appends a config record that is the newest one with these workers, under the lock a post takes. Nothing else in the config changes. A step running keeps the config it started with; the next step lists the new workers to the model as a change.
func (*Harness) Step ¶
Step runs one cycle and returns the status after it. A failed session is stepped only when a run asked for it; every step spends the ask, so a run on a closed session does not carry over to a later failure. Interrupted sessions get a result for every orphan call and become open; the next cycle steps. Past the context limit nothing is called: the session's environments are killed, the error says so, and the session is failed. A retry of a failed session adds resume as a message from retry. The worker list enters as a system record whenever it differs from the last one the model saw.
func (*Harness) Stop ¶
Stop ends everything the session is doing. A running step is cut now: the provider stream is cut, every call in flight is answered "stopped, effect unknown", a call of a recorded step not yet started is answered "not run", and the last record is an error naming via; a session not stepping gets a system record from stop instead. Either way every environment and job the session has on its connected workers is killed.
type Info ¶
type Info struct {
ID string `json:"id"`
Name string `json:"name,omitempty"`
Status statefile.Status `json:"status"`
Config statefile.Conf `json:"config"`
Created time.Time `json:"created"`
Updated time.Time `json:"updated"`
N int `json:"n"`
ContextUsed int `json:"context_used"`
Usage statefile.Usage `json:"usage"`
}
Info is what GET /sessions/{id} and each entry of GET /sessions say about a session: everything the tip states and its name, no cost. ContextUsed is the last step's prompt in tokens, against Config.ContextLimit. Updated is the newest record that is not a name, so a rename does not reorder a list.
type Listed ¶
Listed is a worker as GET /workers shows it: its hello, and why its last connection was refused, when it was.
type Output ¶
Output is what a run left: the tail the worker's side kept, the exit code, the lines dropped before the tail, and the file on the worker holding all of it when the output went over the limits.
type Pool ¶
type Pool struct {
// contains filtered or unexported fields
}
Pool is the set of workers connected right now, keyed by the name the coordinator gave each one's node. Workers dial in over the mesh and are known the moment they say hello; a lost connection forgets the worker until it dials again.
func (*Pool) List ¶
List is every connected worker and every refused one not connected now, sorted by name.
func (*Pool) Resend ¶ added in v0.5.0
func (p *Pool) Resend()
Resend sends each connected worker its drives when they changed: after the config, or the workers on the mesh, did.
func (*Pool) Serve ¶ added in v0.3.0
Serve holds every worker that dials l, the harness's own node on the mesh, until l closes. WireGuard admits only nodes the coordinator gave keys to, and the source address is checked against node, the coordinator's registry, which also names the worker, so no secret rides the connection and no worker can take another's name. A worker that dials again under a name still held replaces the old entry: the old connection is dead or dying, and the new one is the worker as it is now.
type ProviderInfo ¶ added in v0.5.4
type ProviderInfo struct {
Name string `json:"name"`
Shape string `json:"shape"`
URL string `json:"url"`
}
ProviderInfo is a provider as GET /providers shows it: no key.
type Remote ¶
Remote is a worker on the far end of a connection. Its hello header says what it is. Requests are multiplexed by id; a call waits for its terminal frame, the connection's end, or ctx: the worker's timeout bounds a run, and a stop cuts it short.
func Connect ¶
Connect reads the worker's hello, checks the connection with admit, whose error is why it is refused and whose name is the worker's, and starts routing its replies. A worker from another release is told to update to this one and refused: it comes back as this release, and the error says whether the update began or why it failed. That worker is returned with the error, so the pool can list it.
func (*Remote) Drives ¶ added in v0.5.0
Drives is every drive the worker serves or mounts, as it last said.
func (*Remote) Job ¶ added in v0.5.5
func (r *Remote) Job(ctx context.Context, session, interpreter, code, job string, done func(Output, error)) (int, string, error)
Job starts code as a background job named job and returns its pid and log once the worker runs it. The call stays open until the job ends: done gets the log's tail and exit, or the error that ended the call, such as a lost connection. A job a kill ended answers done, and done is not called: a stop ended it on purpose.
func (*Remote) Run ¶
func (r *Remote) Run(ctx context.Context, session, interpreter, code string, timeout int) (Output, error)
Run streams the output and keeps a tail within the result limits. Past twice the byte limit the front is dropped at a line start and the lines counted, a line cut in the middle counting as one; the worker has the whole in the file the result names. timeout is in seconds, and zero is runTimeout.
type Worker ¶
type Worker interface {
Run(ctx context.Context, session, interpreter, code string, timeout int) (Output, error)
Job(ctx context.Context, session, interpreter, code, job string, done func(Output, error)) (int, string, error)
Read(ctx context.Context, path string) ([]byte, error)
Write(ctx context.Context, path string, data []byte) error
Kill(ctx context.Context, session string) error
}
Worker is the three primitives every worker has, Job, and Kill. Tools are built on the primitives here. Write creates missing parents. Run's environment persists per session and interpreter until Kill; the harness decides when a session's environments stop being worth keeping. A job runs in an interpreter of its own until its code ends or Kill.