Documentation
¶
Overview ¶
Package runner provides a durable, authenticated local execution service.
Index ¶
- Constants
- type Artifact
- type ArtifactResponse
- type ArtifactSpec
- type CancelRequest
- type Capabilities
- type Capacity
- type Clarification
- type Config
- type Error
- type Event
- type ExecutionLimits
- type HarnessCapability
- type HarnessCredential
- type PermissionRequest
- type Phase
- type Receipt
- type RepositoryConfig
- type RepositorySource
- type ResourceConfig
- type ResourceRequest
- type ResumeRequest
- type Runner
- func (r *Runner) Cancel(_ context.Context, request CancelRequest) (Receipt, error)
- func (r *Runner) Capabilities() Capabilities
- func (r *Runner) Close() error
- func (r *Runner) Handler() http.Handler
- func (r *Runner) Inspect(_ context.Context, attemptID string) (Receipt, error)
- func (r *Runner) ListenAndServe(ctx context.Context) error
- func (r *Runner) Resume(ctx context.Context, request ResumeRequest) (Receipt, error)
- func (r *Runner) Start(ctx context.Context, request StartRequest) (Receipt, error)
- type StartRequest
- type VerificationRequirements
Constants ¶
const ( EventAccepted = "accepted" EventStarted = "started" EventProgress = "progress" EventCheckpoint = "checkpoint" EventNeedsInput = "needs_input" EventArtifact = "artifact" EventCompleted = "completed" EventFailed = "failed" EventCancelled = "cancelled" )
EventType values are the bounded, replayable events exposed by the runner.
const ( ErrorInvalidRequest = "invalid_request" ErrorForbidden = "forbidden" ErrorUnsupportedVersion = "unsupported_version" ErrorUnsupportedCapability = "unsupported_capability" ErrorBusy = "busy" ErrorStaleAttempt = "stale_attempt" ErrorIdempotencyConflict = "idempotency_conflict" ErrorCursorExpired = "cursor_expired" )
ErrorCode values are stable protocol errors. Callers should branch on Code, not on Message.
const ( PermissionAllow = "allow" PermissionDeny = "deny" )
PermissionDecision values are the verdicts a resume may carry. They are spelled out rather than being a bool so that "no decision" is a distinct, refusable state: a resume that forgot to say must not silently allow.
const ProtocolVersion = "runner/v1"
ProtocolVersion is the wire protocol implemented by the local runner.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Artifact ¶
type Artifact struct {
ID string `json:"id"`
Name string `json:"name"`
Digest string `json:"digest"`
Length int64 `json:"length"`
MediaType string `json:"mediaType"`
CreatedAt time.Time `json:"createdAt"`
}
Artifact is an immutable, named result reference.
type ArtifactResponse ¶
type ArtifactResponse struct {
Artifact
}
ArtifactResponse contains immutable artifact metadata and bytes are served by the artifact endpoint. It is kept separate so callers never receive a filesystem path.
type ArtifactSpec ¶
type ArtifactSpec struct {
Name string `json:"name"`
Path string `json:"path,omitempty"`
MediaType string `json:"mediaType,omitempty"`
Digest string `json:"digest,omitempty"`
}
ArtifactSpec is an approved logical artifact. Path is relative to the task worktree and is only used when the adapter reports an artifact event.
type CancelRequest ¶
type CancelRequest struct {
ProtocolVersion string `json:"protocolVersion,omitempty"`
RequestID string `json:"requestID"`
TaskID string `json:"taskID"`
AttemptID string `json:"attemptID"`
AttemptEpoch uint64 `json:"attemptEpoch"`
}
CancelRequest requests cancellation of an active attempt.
type Capabilities ¶
type Capabilities struct {
ProtocolVersion string `json:"protocolVersion"`
RunnerID string `json:"runnerID"`
Version string `json:"version"`
OS string `json:"os"`
Architecture string `json:"architecture"`
Harnesses []HarnessCapability `json:"harnesses,omitempty"`
Toolchains []string `json:"toolchains,omitempty"`
Environment []string `json:"environment,omitempty"`
Verification []string `json:"verificationCapabilities,omitempty"`
Capacity Capacity `json:"capacity"`
Ready bool `json:"ready"`
Reasons []string `json:"reasons,omitempty"`
}
Capabilities is returned by GET /runner/v1/capabilities.
type Clarification ¶
Clarification is a bounded product question captured from a genuine request-user-input harness interaction. Its ID is stable for one session, turn, and interaction item and must be echoed by a matching resume.
type Config ¶
type Config struct {
ProtocolVersion string `json:"protocolVersion,omitempty"`
RunnerID string `json:"runnerID"`
Version string `json:"version,omitempty"`
Listen string `json:"listen,omitempty"`
StateDir string `json:"stateDir"`
TokenFile string `json:"tokenFile"`
Token string `json:"-"`
Toolchains []string `json:"toolchains,omitempty"`
Environment []string `json:"environment,omitempty"`
Verification []string `json:"verificationCapabilities,omitempty"`
MaximumCapacity int `json:"maximumCapacity,omitempty"`
MaxEvents int `json:"maxEvents,omitempty"`
MaxEventBytes int `json:"maxEventBytes,omitempty"`
MaxBodyBytes int `json:"maxBodyBytes,omitempty"`
MaxArtifactSize int64 `json:"maxArtifactSize,omitempty"`
Repositories map[string]RepositoryConfig `json:"repositories,omitempty"`
Resources map[string]ResourceConfig `json:"resources,omitempty"`
}
Config contains local runner identity, enrollment, persistence, and capability settings. It is intentionally independent of the Codex adapter.
func LoadConfig ¶
LoadConfig reads a JSON runner configuration. A missing path is equivalent to an empty configuration and is useful for callers that supply Config directly; the CLI requires a path when --config is set.
type Error ¶
type Error struct {
Code string `json:"code"`
Retryable bool `json:"retryable"`
Message string `json:"message"`
Receipt *Receipt `json:"receipt,omitempty"`
SnapshotRequired bool `json:"snapshotRequired,omitempty"`
}
Error describes a structured protocol failure.
type Event ¶
type Event struct {
Cursor uint64 `json:"cursor"`
Timestamp time.Time `json:"timestamp"`
AttemptEpoch uint64 `json:"attemptEpoch"`
Type string `json:"type"`
Message string `json:"message,omitempty"`
Data json.RawMessage `json:"data,omitempty"`
}
Event is an ordered, durable attempt event. Data is bounded by the runner before the event is persisted.
type ExecutionLimits ¶
type ExecutionLimits struct {
MaxDurationSeconds int `json:"maxDurationSeconds,omitempty"`
MaxOutputBytes int `json:"maxOutputBytes,omitempty"`
MaxTurns int `json:"maxTurns,omitempty"`
}
ExecutionLimits bounds adapter execution. The runner enforces duration and emitted-output limits; MaxTurns is limited to one because one adapter launch represents one turn in this protocol revision.
type HarnessCapability ¶
type HarnessCapability struct {
Name string `json:"name"`
Version string `json:"version"`
Ready bool `json:"ready"`
Reasons []string `json:"reasons,omitempty"`
}
HarnessCapability describes an installed harness and its readiness.
type HarnessCredential ¶ added in v0.2.8
type HarnessCredential struct {
// Kind is one of the harness.Credential kinds: claude-oauth,
// claude-apikey, codex-auth.
Kind string `json:"kind"`
// Value is the credential itself. It is never logged, never written to
// disk by the runner, and never placed on a command line.
Value string `json:"value"`
}
HarnessCredential is the caller's harness identity for one attempt.
Like RepositorySource it is DISPATCH DATA, not approved attempt input: Start and Resume strip it before the request is fingerprinted or persisted, so it never reaches the runner's durable journal and a retry carrying a freshly minted value is still the same request. The runner holds it in memory for the life of the attempt and hands it to the adapter for each launch.
A runner has no identity of its own. Every start must carry one, which is what keeps a machine that hosts a harness from being a shared model account.
type PermissionRequest ¶ added in v0.2.8
type PermissionRequest struct {
ID string `json:"id"`
Tool string `json:"tool"`
// Input is a bounded JSON rendering of the tool's input, and it is what the
// verdict authorizes: that call, those arguments.
Input string `json:"input,omitempty"`
}
PermissionRequest is a harness permission prompt waiting on a human.
It is NOT a Clarification, and a caller must not treat it as one. A clarification is a product question answered with text, which becomes the next turn's instructions. A permission request is a named tool call, with the input the harness is holding, waiting on a verdict that is delivered back into the call that is still open — so the same turn continues.
Its ID is stable for one session and one tool call and must be echoed by the matching resume, exactly as a Clarification's is.
type Phase ¶
type Phase string
Phase describes the durable lifecycle of an attempt.
func (Phase) IsTerminal ¶
IsTerminal reports whether p no longer has a live execution.
type Receipt ¶
type Receipt struct {
ProtocolVersion string `json:"protocolVersion"`
TaskID string `json:"taskID"`
AttemptID string `json:"attemptID"`
AttemptEpoch uint64 `json:"attemptEpoch"`
Phase Phase `json:"phase"`
SessionID string `json:"sessionID,omitempty"`
Workdir string `json:"workdir,omitempty"`
Cursor uint64 `json:"cursor"`
Blocker string `json:"blocker,omitempty"`
Clarification *Clarification `json:"clarification,omitempty"`
// Permission is set when the attempt is parked on a permission prompt
// rather than on a question. At most one of Clarification and Permission is
// ever set: they are answered differently and a caller has to know which.
Permission *PermissionRequest `json:"permission,omitempty"`
Resources []string `json:"resources,omitempty"`
LastError *Error `json:"lastError,omitempty"`
Artifacts []Artifact `json:"artifacts,omitempty"`
AcceptedAt time.Time `json:"acceptedAt"`
UpdatedAt time.Time `json:"updatedAt"`
}
Receipt is the durable view of an attempt returned by start and inspect.
type RepositoryConfig ¶
type RepositoryConfig struct {
Source string `json:"source"`
BaseCommit string `json:"baseCommit,omitempty"`
FetchRemoteURL string `json:"fetchRemoteURL,omitempty"`
}
RepositoryConfig pins what the runner may do with one repository. Every field is optional, and the whole map usually is: a runner clones what the coordinator names with an attempt into a copy it owns, so nothing has to be staged on the host. An enrollment is for the two cases where the host has something to say — Source names an existing checkout to work from instead, FetchRemoteURL a remote to use when no attempt supplies one, and BaseCommit pins the single commit this runner will accept.
type RepositorySource ¶ added in v0.2.7
type RepositorySource struct {
// RemoteURL is the Git URL to clone from. HTTPS and SSH are accepted;
// the URL itself must never carry embedded credentials.
RemoteURL string `json:"remoteURL"`
// Username accompanies Token for HTTPS remotes. Empty means the usual
// token-bearing placeholder.
Username string `json:"username,omitempty"`
// Token is a short-lived read-only credential. It is used for one fetch
// and is never written to disk, logged, or passed on the command line.
Token string `json:"token,omitempty"`
}
RepositorySource tells the runner where to get a repository it does not keep a local checkout of. The coordinator sends it with each start; the runner clones into a cache it owns under its state directory.
It is dispatch data, not approved attempt input: Start strips it before the request is fingerprinted or persisted, so the credential never reaches the runner's durable state and a retry carrying a freshly minted one is still the same request.
type ResourceConfig ¶
type ResourceConfig struct {
Kind string `json:"kind,omitempty"`
Capacity int `json:"capacity,omitempty"`
Labels map[string]string `json:"labels,omitempty"`
}
ResourceConfig names a preconfigured shared resource. Capacity is a count of simultaneous reservations; it does not cause the runner to provision the resource.
type ResourceRequest ¶
type ResourceRequest struct {
Name string `json:"name"`
}
ResourceRequest identifies a preconfigured shared resource. Resource values are names only: the runner never provisions ports, containers, or clusters.
type ResumeRequest ¶
type ResumeRequest struct {
ProtocolVersion string `json:"protocolVersion,omitempty"`
RequestID string `json:"requestID"`
TaskID string `json:"taskID"`
AttemptID string `json:"attemptID"`
AttemptEpoch uint64 `json:"attemptEpoch"`
SessionID string `json:"sessionID,omitempty"`
ClarificationID string `json:"clarificationID,omitempty"`
ApprovedInput json.RawMessage `json:"approvedInput,omitempty"`
Resolution string `json:"resolution,omitempty"`
Instructions string `json:"instructions,omitempty"`
HarnessCredential *HarnessCredential `json:"harnessCredential,omitempty"`
// PermissionID answers an outstanding Receipt.Permission, and must equal
// its ID. It is the fence: a verdict that names a different request is
// refused rather than applied to whatever the harness is waiting on now.
PermissionID string `json:"permissionID,omitempty"`
// PermissionDecision is the verdict, PermissionAllow or PermissionDeny. It
// is required whenever PermissionID is set, and forbidden otherwise.
//
// A permission resume does NOT relaunch the harness: the tool call is still
// open on a child that never stopped, so the verdict is handed to it and
// the SAME turn continues. Resolution, when present, is the human's own
// words and is passed to the harness as the reason for a denial.
PermissionDecision string `json:"permissionDecision,omitempty"`
}
ResumeRequest continues a needs-input attempt in its existing session.
It carries the harness credential again rather than relying on one the runner kept: a resume may arrive after a runner restart, and nothing about the caller's identity survives on the host.
type Runner ¶
type Runner struct {
// contains filtered or unexported fields
}
Runner is the generic local execution service. It owns protocol state and workspace/resource boundaries; harness.Adapter owns the child process.
func New ¶
New creates a runner, loads durable state, probes the harness without model calls, and acquires the state-directory singleton lock.
func (*Runner) Cancel ¶
Cancel requests adapter cancellation. Active attempts remain cancelling until adapter exit. Unseen attempts receive a durable cancellation fence before acknowledgement, so a delayed Start cannot launch them.
func (*Runner) Capabilities ¶
func (r *Runner) Capabilities() Capabilities
Capabilities returns a copy of the current discovery document.
func (*Runner) Close ¶
Close gracefully drains active adapters, waits for them to exit, closes the listener, and releases the singleton state lock. An adapter interrupted by this shutdown remains resumable; an explicit Cancel request is still terminal.
func (*Runner) Handler ¶
Handler returns the authenticated loopback runner protocol handler. The handler itself does not bind a socket; ListenAndServe enforces loopback.
func (*Runner) ListenAndServe ¶
ListenAndServe starts the runner on the configured loopback address and stops when ctx is cancelled.
type StartRequest ¶
type StartRequest struct {
ProtocolVersion string `json:"protocolVersion,omitempty"`
RequestID string `json:"requestID"`
TaskID string `json:"taskID"`
AttemptID string `json:"attemptID"`
AttemptEpoch uint64 `json:"attemptEpoch"`
// RepositoryID and BaseCommit are required unless WorkspaceID is set.
RepositoryID string `json:"repositoryID,omitempty"`
BaseCommit string `json:"baseCommit,omitempty"`
// WorkspaceID names a directory the runner owns and keeps across
// attempts, under its managed worktree root. Mutually exclusive with
// RepositoryID: a workspace has no approved commit to verify.
WorkspaceID string `json:"workspaceID,omitempty"`
// SessionID continues an existing harness session in a NEW attempt, which
// is how consecutive turns of one conversation stay one session. Resuming
// a parked attempt uses ResumeRequest instead.
SessionID string `json:"sessionID,omitempty"`
// HarnessCredential is the identity this attempt runs as. Required, and
// stripped before anything durable is derived from the request.
HarnessCredential *HarnessCredential `json:"harnessCredential,omitempty"`
Instructions string `json:"instructions"`
Model string `json:"model,omitempty"`
ApprovedInput json.RawMessage `json:"approvedInput,omitempty"`
RequiredCapabilities []string `json:"requiredCapabilities,omitempty"`
RequiredToolchains []string `json:"requiredToolchains,omitempty"`
RequiredEnvironment []string `json:"requiredEnvironment,omitempty"`
RequiredHarness string `json:"requiredHarness,omitempty"`
RequiredHarnessVersion string `json:"requiredHarnessVersion,omitempty"`
// AskPermission opts this attempt into the permission round-trip: a tool
// call the harness's permission mode does not pre-approve becomes a
// needs_input on the attempt, carrying Receipt.Permission, instead of being
// denied where nobody can see it. The resume answers it with a verdict.
//
// It is opt-in because answering one is work a coordinator has to do. A
// coordinator that does not send it — or an older one that has never heard
// of the field — gets exactly the behaviour it had before: anything that
// would have prompted is denied and the turn carries on.
AskPermission bool `json:"askPermission,omitempty"`
ExportGitResult bool `json:"exportGitResult,omitempty"`
// CommitMessage is the subject the exported git result's snapshot commit
// is stamped with: one line of coordinator-authored public text, such as
// the ticket's title or "Address review on #7". It is the coordinator's
// to choose and the coordinator's to verify downstream — nothing the model
// wrote reaches git metadata. Empty keeps the canonical message.
CommitMessage string `json:"commitMessage,omitempty"`
Limits ExecutionLimits `json:"limits,omitempty"`
Verification VerificationRequirements `json:"verification,omitempty"`
Resources []ResourceRequest `json:"resources,omitempty"`
Artifacts []ArtifactSpec `json:"artifacts,omitempty"`
Repository *RepositorySource `json:"repository,omitempty"`
}
StartRequest is the approved execution envelope accepted by Start.
There are two shapes. A REPOSITORY attempt names RepositoryID and BaseCommit and runs in a fresh task clone at that commit; this is what a coding coordinator sends. A WORKSPACE attempt names WorkspaceID instead and runs in a directory the runner keeps across attempts; this is what a conversational agent sends, where there is no commit to pin and the working files are the point. The two are mutually exclusive.
type VerificationRequirements ¶
type VerificationRequirements struct {
Names []string `json:"names,omitempty"`
Commands []string `json:"commands,omitempty"`
}
VerificationRequirements records the checks approved for an attempt.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package client reaches one enrolled railgrid runner through the edges provider's published Service proxy, speaking the typed runner/v1 protocol in pkg/runner.
|
Package client reaches one enrolled railgrid runner through the edges provider's published Service proxy, speaking the typed runner/v1 protocol in pkg/runner. |
|
Package dispatch is the runner/v1 attempt lifecycle, written once.
|
Package dispatch is the runner/v1 attempt lifecycle, written once. |
|
Package harness defines the public execution seam for runner adapters.
|
Package harness defines the public execution seam for runner adapters. |
|
claude
Package claude adapts headless Claude Code to the runner harness.
|
Package claude adapts headless Claude Code to the runner harness. |
|
codex
Package codex adapts the Codex app-server protocol to the runner harness.
|
Package codex adapts the Codex app-server protocol to the runner harness. |
|
internal/proc
Package proc holds the process-group primitives every harness adapter needs.
|
Package proc holds the process-group primitives every harness adapter needs. |
|
Package runnercli holds the launch logic shared by the standalone railgrid-runner binary (cmd/railgrid-runner) and the `railgrid runner run` subcommand of the main CLI (pkg/cli/cmd/runner.go).
|
Package runnercli holds the launch logic shared by the standalone railgrid-runner binary (cmd/railgrid-runner) and the `railgrid runner run` subcommand of the main CLI (pkg/cli/cmd/runner.go). |