Documentation
¶
Overview ¶
Package hostpoller is the host side of Hadron's host operations (hadron-cli#831, cor:agt:070): a poller that registers the workers this host can launch, collects the operations issued to it, carries out only the closed set of five, and reports every outcome.
It talks to the server through a narrow Server interface and to the host through a Host interface, so the loop and every operation's rules are tested without a server, git or a real agent.
Index ¶
- Constants
- Variables
- func CanonicalFamily(s string) string
- func ResetStepCode(err error) string
- func SameFamily(a, b string) bool
- func ScrubbedEnv(parent []string, hostKey string) []string
- func ValidNameToken(s string) bool
- type Agent
- type AgentRecord
- type BatchSource
- type Config
- func (c *Config) CheckWorktreeAliases(s *State) error
- func (c *Config) DeliveryStateDir() string
- func (c *Config) ReadHostKey() (string, error)
- func (c *Config) Recipient(s *State, ref, workerID string) delivery.Recipient
- func (c *Config) RefFor(id, urn, name string) string
- func (c *Config) WorkerRefs() []string
- func (c *Config) WorktreePath(token string) string
- type ContextReset
- type Deliverer
- type DeliveryConfig
- type Event
- type ExecHost
- func (ExecHost) Alive(pid int) bool
- func (ExecHost) CreateWorktree(ctx context.Context, repoPath, path, branch, base string) error
- func (ExecHost) Inspect(ctx context.Context, path string) (WorktreeState, error)
- func (ExecHost) Launch(_ context.Context, spec LaunchSpec) (Agent, error)
- func (ExecHost) Occupied(_ context.Context, path string) (string, error)
- func (h ExecHost) Prompt(ctx context.Context, argv []string, dir string, env []string) error
- func (ExecHost) RemoveWorktree(ctx context.Context, path string) error
- type GitTreeState
- type Host
- type LaunchSpec
- type LinuxProcessControl
- type Operation
- type Poller
- func (p *Poller) DeliveryEnabled() bool
- func (p *Poller) EnableDelivery(src BatchSource, d Deliverer)
- func (p *Poller) EnableResets(rs ResetServer, deps ResetDeps)
- func (p *Poller) EnableWakes(src WakeSource, d WakeDeliverer)
- func (p *Poller) ReportFailures() int
- func (p *Poller) Run(ctx context.Context) error
- func (p *Poller) Step(ctx context.Context) error
- func (p *Poller) Wait()
- type ProcessControl
- type ProcessHandle
- type ProcessIdentity
- type RegisteredHost
- type RegisteredHostWorker
- type RegisteredWorker
- type Registration
- type ResetCheckpoint
- type ResetDeps
- type ResetRecord
- type ResetServer
- type ResetStep
- type ResetStepError
- type Server
- type State
- type WakeDeliverer
- type WakePage
- type WakeSource
- type WorkerConfig
- type WorktreeState
Constants ¶
const ( OpWorktreeCreate = "worktree.create" OpWorkerStart = "worker.start" OpWorkerDeliver = "worker.deliver" OpWorkerEnd = "worker.end" OpWorktreeRemove = "worktree.remove" )
The closed set of operation types a host carries out (cor:agt:070:03). Anything else is refused here as well as by the server, and there is no shell passthrough of any kind.
const ( Succeeded = "SUCCEEDED" Failed = "FAILED" )
Outcomes a host reports.
const CapabilityResetContext = OpWorkerResetContext
CapabilityResetContext is the registration capability for that operation. Declaring it is a separate, authorized activation; this package never adds it to a registration by itself.
const ( // HostKeyEnv is never read as a key source (a same-user agent could // read it from the poller's environment); it is scrubbed from launched // agents in case the operator's shell carries one. HostKeyEnv = "HADRON_HOST_KEY" )
const LaunchGrantTTL = 10 * time.Minute
LaunchGrantTTL is how long a collected start's grant lives (#1883, Proposed). Waiting for the bind longer than this cannot succeed.
const OpWorkerResetContext = "worker.reset-context"
OpWorkerResetContext is the capability-gated operation (#2162, cor:agt:050:27): reset a worker's context after its task, keeping the binding. Args {workerId, sessionId, taskId, resetId}; no epoch, the claim is made at 0 and moves it (hadron-cli#1043).
Variables ¶
var ErrClock = errors.New("the boot clock gave no usable reading, so the stop lease cannot be measured; nothing was sent")
ErrClock: the boot clock could not be read, or read earlier than the window's start, so the lease cannot be measured; nothing was sent.
var ErrDeliveryScope = errors.New("the host key lacks attention:deliver: delivery is off for this run")
ErrDeliveryScope is a host key without attention:deliver (APP_KEY_SCOPE_REQUIRED). It does not heal by retrying, so delivery is switched off for the rest of the run; host operations carry on.
var ErrFinal = errors.New("the operation is already finished")
ErrFinal is a Report refusal meaning the operation is already finished server-side (HOST_OPERATION_ALREADY_REPORTED): asking again cannot change it.
var ErrHandleClosed = errors.New("the process handle is closed; nothing was sent")
ErrHandleClosed: the handle was closed; nothing was sent.
var ErrLeaseExpired = errors.New("the stop lease expired before the signal; nothing was sent")
ErrLeaseExpired: the stop lease had run out at the check; no signal sent.
var ErrNotDone = errors.New("no session bound with this start's grant yet")
ErrNotDone is a Report refusal meaning a worker.start's session is not bound yet (HOST_OPERATION_NOT_DONE); the poller asks again.
var ErrUnsupportedPlatform = errors.New("worker.reset-context needs Linux (pidfd and CLOCK_BOOTTIME); this host refuses it")
ErrUnsupportedPlatform: this platform has no pidfd and boot clock, so the handler refuses before any claim rather than signal a bare pid.
var ResetStepCodes = []string{"STALE_EPOCH", "CONTEXT_RESET_WRONG_PHASE", "CONTEXT_RESET_NOT_FOUND", "HOST_OPERATION_NOT_COLLECTED"}
ResetStepCodes are the typed rejections a step can carry.
Functions ¶
func CanonicalFamily ¶ added in v0.27.0
CanonicalFamily is the server's identity for an EXPLICIT family: trimmed, lowercased, and a provider alias folded to its family. Every other token is itself, so a custom family compares only to itself. It never infers a family from a title or a model and does not validate: an empty or malformed result is not a launch identity and callers must refuse it (familyRE).
func ResetStepCode ¶ added in v0.35.0
ResetStepCode returns the typed code of a step rejection, or "".
func SameFamily ¶ added in v0.27.0
SameFamily reports whether two explicit families are one launch identity. An empty or malformed family is never one, even against itself.
func ScrubbedEnv ¶ added in v0.33.0
ScrubbedEnv is parent without the credential variables and any variable whose value carries hostKey: the environment for a child that must not see the host's credentials (the Orca CLI a status read runs).
func ValidNameToken ¶
ValidNameToken reports whether s is a host name token the server accepts.
Types ¶
type Agent ¶
type Agent interface {
// Done is closed when the process exits.
Done() <-chan struct{}
// ExitDetail describes how it exited, once Done is closed.
ExitDetail() string
// PID is its process id (0 when unknown).
PID() int
}
Agent is a launched agent process.
type AgentRecord ¶
type AgentRecord struct {
Worktree string `json:"worktree"`
PID int `json:"pid"`
// Start is the launch's kernel start time, observed while it ran (empty
// when not observed): with PID, its launch identity, so a restarted
// poller can tell this launch from another at the same pid (cli#1582).
Start string `json:"start,omitempty"`
}
AgentRecord is one launched agent.
type BatchSource ¶ added in v0.25.0
BatchSource reads this host's due batches with the host key.
type Config ¶
type Config struct {
// Capabilities is an explicit full declaration at every refresh, never inferred.
Capabilities []string `yaml:"capabilities"`
// Server is the Hadron server URL; empty means the CLI's usual resolution.
Server string `yaml:"server"`
// App is the team App the host serves (hrn:app:<root>:<slug>, or an id).
App string `yaml:"app"`
// Label names the host to people; empty means the hostname.
Label string `yaml:"label"`
// PollIntervalSeconds is how often the host checks in (1 to 86400).
PollIntervalSeconds int `yaml:"poll_interval_seconds"`
// KeyFile holds the App key carrying host:operate for this host
// (required; mode 0600 or tighter). Relative paths resolve against the
// configuration's directory.
KeyFile string `yaml:"key_file"`
// WorktreesRoot is the directory every worktree name token is relative to.
WorktreesRoot string `yaml:"worktrees_root"`
// StateFile records the worktrees this poller created and any it must
// keep. Empty means hadron-host-state.json beside the config.
StateFile string `yaml:"state_file"`
// Repos maps a repo token (as worktree.create names it, e.g.
// hadron-memory/hadron-cli) to the local clone worktrees are added from.
Repos map[string]string `yaml:"repos"`
// Workers maps a worker reference (its name in the App, URN or id) to
// how this host runs it.
Workers map[string]WorkerConfig `yaml:"workers"`
// Delivery turns on chat delivery into this host's Orca terminals
// (cli#907). Absent, the poller carries out host operations only.
Delivery DeliveryConfig `yaml:"delivery"`
}
The host-local configuration. Hadron never sees it: the launch command and any AI-plan login are the host's own (Holger #10777, cor:agt:070 Out of Scope), and the host key is read from a file named here — never from an environment variable, which a same-user agent could read from the poller's /proc entry.
func LoadConfig ¶
LoadConfig reads and validates a host configuration file.
func (*Config) CheckWorktreeAliases ¶ added in v0.25.0
CheckWorktreeAliases refuses two workers whose worktrees, configured or created by this poller, name one directory under different tokens (cli#906). validate already refuses one token listed twice; a symlink gives one directory a second token. A tree that does not exist yet is compared by its cleaned path only. One worker's own aliases are allowed.
func (*Config) DeliveryStateDir ¶ added in v0.25.0
DeliveryStateDir holds delivery's records and per-worker locks: beside the state file, where C2's --orca-host-config finds the same locks.
func (*Config) ReadHostKey ¶
ReadHostKey returns the host:operate key from key_file. Never from HADRON_TOKEN or the token store (the holder's own credential, which the launched agents bind on), and never from an environment variable.
The file must not be readable by group or others. That is the part of key custody the poller CAN check. The part it cannot: an agent running as the same OS user as the poller can read any file the poller can. Keeping the key from a launched agent's environment, argv and files is the poller's; keeping it from the agent's OS user is the deployment's — run the poller as its own user, with key_file readable by that user only.
func (*Config) Recipient ¶ added in v0.25.0
Recipient is the configured worker ref, resolved to workerID, as delivery needs it: its expected interface and its configured and created worktrees as absolute paths. The poller and C2 build it the same way.
func (*Config) RefFor ¶ added in v0.25.0
RefFor is the configured reference for a worker, matched by id, URN or name (names are unique per App case-insensitively, cor:agt:020:02), or "" when none matches. It is the poller's registration rule, for a caller that delivers on this host without a live registration (C2, cli#908).
func (*Config) WorkerRefs ¶
WorkerRefs lists the configured worker references in a stable order.
func (*Config) WorktreePath ¶
WorktreePath is the absolute path of a worktree name token.
type ContextReset ¶ added in v0.35.0
type ContextReset struct {
ID string
Epoch int
Phase string
Reason string
WorkerID string
SessionID string
TaskID string
StopGranted bool
StopLeaseMs int
// CheckpointNode is the v2 URN of the checkpoint node registered for
// the relaunch (hadron-server#3300); empty before registration.
CheckpointNode string
}
ContextReset is the part of the server's reset record the handler reads.
type Deliverer ¶ added in v0.25.0
type Deliverer interface {
Deliver(ctx context.Context, b delivery.Batch, r delivery.Recipient) delivery.Outcome
}
Deliverer carries one batch to its recipient; *delivery.Deliverer is one.
type DeliveryConfig ¶ added in v0.25.0
type DeliveryConfig struct {
// Enabled turns delivery on. The host key then also needs
// attention:deliver; without it delivery switches off for the run.
Enabled bool `yaml:"enabled"`
// AckMixed asserts that the server carries A-prime (#2072): mixed wake
// acknowledgment records ticks/tasks without advancing any chat mark.
AckMixed bool `yaml:"ack_mixed"`
// OrcaBin is the absolute path of the orca CLI.
OrcaBin string `yaml:"orca_bin"`
}
DeliveryConfig is chat delivery's part of a host configuration.
type Event ¶
type Event struct {
Time time.Time
Kind string
Operation string
Type string
Outcome string
Detail string
}
Event is one line of what the poller did, for the operator. The --json shape is the command package's DTO, not this.
type ExecHost ¶
type ExecHost struct {
// PromptTimeout bounds worker.end's prompt command.
PromptTimeout time.Duration
}
ExecHost is the real Host: git for worktrees, exec for agents. Every command is a fixed argv — git's own, or the one in host configuration — never a shell string, and nothing in it comes from an operation's arguments except git ref and path names, which git receives as separate arguments after the option they belong to.
func (ExecHost) CreateWorktree ¶
CreateWorktree fetches the base from origin, then adds the worktree on a new branch from origin/<base> (or the local <base> when origin has none).
func (ExecHost) Launch ¶
Launch starts the agent and returns at once. On Unix it gets its own session (setsid), so the poller's signals do not reach it; on Windows it stays in the poller's console group.
func (ExecHost) Occupied ¶
Occupied looks for a process whose working directory is inside path, via /proc (Linux). Without /proc it cannot tell and reports none.
type GitTreeState ¶ added in v0.35.0
type GitTreeState struct {
// InGit: `git -C <dir> rev-parse --is-inside-work-tree` printed true.
InGit bool
// The rest is read only when InGit.
Dirty bool
Upstream string
Ahead int
}
GitTreeState is the reset handler's Git determination (ruling 0012, cor:agt:050:27 rule 7; hadron-cli#1043 amendment 6586883519).
func GitTree ¶ added in v0.35.0
func GitTree(ctx context.Context, dir string) (GitTreeState, error)
GitTree decides whether dir is inside a Git work tree, and only then reads its cleanliness and push state. A missing directory, a non-Git directory and a host without git are all "not in a Git tree". Any other git failure is an error, so the caller refuses: an unreadable tree is never taken for a non-Git one.
type Host ¶
type Host interface {
// CreateWorktree adds a worktree at path on a new branch from base.
CreateWorktree(ctx context.Context, repoPath, path, branch, base string) error
// Inspect reads a worktree's state; Exists is false for a missing path.
Inspect(ctx context.Context, path string) (WorktreeState, error)
// RemoveWorktree removes a clean worktree.
RemoveWorktree(ctx context.Context, path string) error
// Launch starts an agent and returns at once; the agent keeps running.
Launch(ctx context.Context, spec LaunchSpec) (Agent, error)
// Prompt runs a short command (worker.end's prompt) to completion.
Prompt(ctx context.Context, argv []string, dir string, env []string) error
// Occupied names a process (other than the poller) working inside path,
// or "" when none is seen. Where the host cannot tell (no /proc), it
// reports none, and worktree.remove leans on the recorded agents alone.
Occupied(ctx context.Context, path string) (string, error)
// Alive reports whether a process id is still running.
Alive(pid int) bool
}
Host is everything the poller does to the machine: git worktrees and agent processes. The real one shells out to git and execs the configured argv; tests fake it.
type LaunchSpec ¶
LaunchSpec is one agent start: the argv from host configuration, run in the worktree, with an environment that carries the launch grant and no Hadron credential of the host's.
type LinuxProcessControl ¶ added in v0.35.0
type LinuxProcessControl struct{}
LinuxProcessControl is ProcessControl over pidfd_open, pidfd_send_signal and CLOCK_BOOTTIME.
func (LinuxProcessControl) Now ¶ added in v0.35.0
func (LinuxProcessControl) Now() (time.Duration, error)
Now reads CLOCK_BOOTTIME. It exists on every kernel with pidfd (5.3+), so a failure is unreachable in practice; it is still an error, never a stand-in time, because two equal stand-ins measure a zero-length window.
func (LinuxProcessControl) Observe ¶ added in v0.35.0
func (LinuxProcessControl) Observe(pid int) (ProcessHandle, error)
Observe opens a pidfd first, then reads the start time, then proves the pidfd's process is still alive: if it is, the pid has not been reused between the open and the read, so the token belongs to the pinned process.
type Operation ¶
type Operation struct {
ID string
Type string
Args map[string]any
WorkerID string
Status string
Result map[string]any
FailureReason string
// LaunchGrant is set only on a collected worker.start.
LaunchGrant string
}
Operation is a collected host operation.
type Poller ¶
type Poller struct {
// BindPoll is how often a pending start asks whether its session bound.
BindPoll time.Duration
// Fatal, when set, decides that a failed check-in ends Run: a refused or
// revoked host key does not heal by retrying, and looping on it would
// hide it.
Fatal func(error) bool
// DeliveryStep, when set, is told how each delivery step's batch read
// went (delivery.StepRead, StepReadFailed, StepOffByScope).
DeliveryStep func(at time.Time, read string)
// WakeStep, when set, is told how each step's wake read went (cli#906
// row 1).
WakeStep func(delivery.WakeRead)
// contains filtered or unexported fields
}
Poller is one host's loop.
func New ¶
New builds a poller. key is the host key, kept only to scrub it from launched agents' environments (defence in depth: it is never put there).
func (*Poller) DeliveryEnabled ¶ added in v0.25.0
DeliveryEnabled reports whether this run delivers.
func (*Poller) EnableDelivery ¶ added in v0.25.0
func (p *Poller) EnableDelivery(src BatchSource, d Deliverer)
EnableDelivery turns chat delivery on.
func (*Poller) EnableResets ¶ added in v0.35.0
func (p *Poller) EnableResets(rs ResetServer, deps ResetDeps)
EnableResets turns the handler on for this poller. It declares no capability: registration still sends exactly cfg.Capabilities, and advertising worker.reset-context is a separate authorized activation.
func (*Poller) EnableWakes ¶ added in v0.25.0
func (p *Poller) EnableWakes(src WakeSource, d WakeDeliverer)
EnableWakes turns tick and task wake delivery on (cli#906 row 1). Wakes are read only while chat delivery is on: they share its key, scope and per-worker chain.
func (*Poller) ReportFailures ¶
ReportFailures is how many reports did not reach the server; those operations are re-delivered at the next collect.
func (*Poller) Step ¶
Step is one check-in: register (always — a host that stops registering lapses), then collect and dispatch.
A worker.start waiting for its bind is re-delivered by every collect and skipped here as in flight. It must not come back with a fresh grant: that would revoke the one its agent was handed. The server stops doing that (Liam, team chat #11415: a re-collect omits a start whose grant is still live), and this poller depends on it rather than pausing collection.
type ProcessControl ¶ added in v0.35.0
type ProcessControl interface {
Observe(pid int) (ProcessHandle, error)
Now() (time.Duration, error)
}
ProcessControl is the reset handler's only way to touch an agent process (#1043). It observes a process by pid into a handle that pins it (a pidfd on Linux), so a later signal can only reach that process, never a reused pid. Now is CLOCK_BOOTTIME: it keeps counting across a suspend, so a lease measured on it is never longer than the server meant. A clock that cannot be read is an error, never a stand-in value: a lease is only ever measured between two real readings.
type ProcessHandle ¶ added in v0.35.0
type ProcessHandle interface {
Identity() ProcessIdentity
// SignalWithin sends sig only if now() - t0 < lease, with the check and
// the send adjacent. t0 is read BEFORE the authorizeStop request goes
// out, so a slow answer shortens the window and never lengthens it. It
// re-reads the clock after the send and returns how far past the lease
// the send landed (0 when inside): a late stop the caller records
// (ruling 0087 A). ErrLeaseExpired means nothing was sent; so does
// ErrClock, when the check's reading failed or is before t0. A failed
// reading after the send cannot unsend: the stop stands and late is 0,
// the check before it having passed on a real reading.
SignalWithin(sig syscall.Signal, t0, lease time.Duration, now func() (time.Duration, error)) (late time.Duration, err error)
Close()
}
ProcessHandle is one observed process.
type ProcessIdentity ¶ added in v0.35.0
ProcessIdentity names one agent process: its pid and the kernel's start time for it, an opaque token, so a reused pid is a different process.
type RegisteredHost ¶
type RegisteredHost struct {
HostID string
Workers []RegisteredHostWorker
}
RegisteredHost is the server's answer to a registration: which worker id each configured reference resolved to.
type RegisteredHostWorker ¶
RegisteredHostWorker pairs a resolved worker id with its name.
type RegisteredWorker ¶
type RegisteredWorker struct {
Worker string // the configured reference
Family string
Worktrees []string
}
RegisteredWorker is one worker in a registration.
type Registration ¶
type Registration struct {
Capabilities []string
Label string
PollIntervalSeconds int
Workers []RegisteredWorker
}
Registration is one registerHost call: the whole worker list.
type ResetCheckpoint ¶ added in v0.35.0
ResetCheckpoint is the checkpoint node the server writes for the host: a nonblank title of at most 200 characters and at most 65536 bytes of UTF-8 content (#3276). The server picks the loc.
type ResetDeps ¶ added in v0.35.0
type ResetDeps struct {
// Proc observes and signals agent processes (LinuxProcessControl).
Proc ProcessControl
// Git is the Git determination (GitTree).
Git func(ctx context.Context, dir string) (GitTreeState, error)
// Idle proves the worker's agent idle with an empty composer and no
// menu (BUSY, DRAFT_OR_MENU). Nil means this host cannot prove it, and
// every reset refuses NOT_IDLE: idle is never assumed.
Idle func(ctx context.Context, workerRef string) error
}
ResetDeps is what the worker.reset-context handler needs beyond the poller (#1043). Each is injected so the handler runs against fakes; in production only `team host run` could supply them, and this slice wires none, so the handler is unreachable there.
type ResetRecord ¶ added in v0.35.0
type ResetRecord struct {
ResetID string `json:"resetId"`
OperationID string `json:"operationId"`
WorkerID string `json:"workerId"`
// Epoch is the last epoch a step returned (0 before the claim).
Epoch int `json:"epoch"`
// Old is the agent process observed before the claim; New is the
// relaunched one, once started.
Old ProcessIdentity `json:"old"`
New *ProcessIdentity `json:"new,omitempty"`
// Checkpoint is the checkpoint node the recovery was registered with,
// handed to the relaunched agent; empty until registerRecovery.
Checkpoint string `json:"checkpoint,omitempty"`
// Relaunching is set on disk before the relaunch starts an agent, so a
// restart in between never starts a second one.
Relaunching bool `json:"relaunching,omitempty"`
// LateStopMs is how far past its lease the stop landed, written as
// soon as the signal returns, so the late-stop event survives an agent
// that does not exit, a cancellation and a restart (cor:agt:050:27
// part 10). 0 when on time or not yet sent.
LateStopMs int64 `json:"lateStopMs,omitempty"`
}
ResetRecord is one reset in flight on this host.
type ResetServer ¶ added in v0.35.0
type ResetServer interface {
ClaimReset(ctx context.Context, resetID string, epoch int, p ProcessIdentity) (ResetStep, error)
// RegisterResetRecovery writes the checkpoint with the host key and
// registers it (#3276, ruling 0090 A): the host never names a node.
RegisterResetRecovery(ctx context.Context, resetID string, epoch int, cp ResetCheckpoint, sessionID, taskID string) (ResetStep, error)
AuthorizeResetStop(ctx context.Context, resetID string, epoch int, p ProcessIdentity) (ResetStep, error)
// late is how long after its lease the stop signal landed (0 when on
// time; procctl SignalWithin's result); it is sent as lateStopMs.
ReportResetAgentStopped(ctx context.Context, resetID string, epoch int, late time.Duration) (ResetStep, error)
ReportResetRelaunched(ctx context.Context, resetID string, epoch int, p ProcessIdentity) (ResetStep, error)
ReportResetRelaunchFailed(ctx context.Context, resetID string, epoch int, reason string) (ResetStep, error)
// ReadReset reads one reset this host collected (#3273), for restart
// recovery; it changes nothing.
ReadReset(ctx context.Context, resetID string) (ContextReset, error)
}
ResetServer is the reset protocol's six steps, made with the host key. Each names the reset and the epoch the host holds.
type ResetStep ¶ added in v0.35.0
type ResetStep struct {
Refused string
Replayed bool
Reset ContextReset
}
ResetStep is one protocol step's answer. Refused is non-empty when the step refused or failed the reset (it has then ended server-side); Replayed means the step was already made and nothing changed.
type ResetStepError ¶ added in v0.35.0
ResetStepError is a step the server rejected outright with a typed code (STALE_EPOCH, CONTEXT_RESET_WRONG_PHASE, CONTEXT_RESET_NOT_FOUND, HOST_OPERATION_NOT_COLLECTED). Every one of them stops the handler: none is retried and none is a reason to touch the process.
func (*ResetStepError) Error ¶ added in v0.35.0
func (e *ResetStepError) Error() string
func (*ResetStepError) Unwrap ¶ added in v0.35.0
func (e *ResetStepError) Unwrap() error
type Server ¶
type Server interface {
Register(ctx context.Context, r Registration) (RegisteredHost, error)
Collect(ctx context.Context) ([]Operation, error)
// Report returns the operation as the server recorded it.
Report(ctx context.Context, id, outcome, worktree, reason string) (Operation, error)
}
Server is the host's four server calls, made with the host key.
type State ¶
type State struct {
// Created maps a configured worker reference to the worktree tokens this
// poller created for it.
Created map[string][]string `json:"created"`
// Keep maps a worktree token to why it must not be removed.
Keep map[string]string `json:"keep"`
// HostID is the id the server derived from the host key at the last
// registration — how `status` finds this host's own registration, since
// a label is not unique.
HostID string `json:"hostId"`
// Agents are the agents this poller launched, by worker id: the
// worktree each runs in and its process id. A launched agent outlives a
// poller restart (it runs in its own session), so the in-memory record
// alone would forget it and let worktree.remove take a tree from under
// a live agent. A reused pid only makes a check refuse: the safe way.
Agents map[string]AgentRecord `json:"agents"`
// Resets are the worker.reset-context operations this poller collected
// and has not finished reporting, by reset id (#1043). Written before
// the claim, so a restarted poller knows which resets to read; it never
// stores a phase, which recovery always reads from the server.
Resets map[string]ResetRecord `json:"resets,omitempty"`
// contains filtered or unexported fields
}
State is what the poller remembers across restarts: the worktrees it created (so the next registration lists them — the server does not add a created worktree to the registration itself), and the worktrees it must keep because a worker.end on them failed (cor:agt:020:10: never remove a worktree after a failed end).
func (*State) RegisteredHostID ¶
RegisteredHostID is the host id saved at the last registration.
type WakeDeliverer ¶ added in v0.25.0
type WakeDeliverer interface {
DeliverWake(ctx context.Context, w delivery.Wake, r delivery.Recipient) delivery.Outcome
}
WakeDeliverer carries one wake to its recipient; *delivery.Deliverer is one.
type WakePage ¶ added in v0.25.0
type WakePage struct {
Wakes []delivery.Wake
// BudgetExhausted: WAKE_BUDGET_EXHAUSTED. Nothing is typed; the
// withheld wakes stay due.
BudgetExhausted bool
// Dormant and Withheld are counted in status only.
Dormant, Withheld int
}
WakePage is one step's due wakes.
type WakeSource ¶ added in v0.25.0
WakeSource reads this host's due wakes with the host key (dueWakes, paged to exhaustion over this host's registered workers).
type WorkerConfig ¶
type WorkerConfig struct {
// Family is the model family it runs as here, e.g. claude or codex.
Family string `yaml:"family"`
// Launch is the argv that starts its agent in a worktree. The poller adds
// HADRON_LAUNCH_GRANT to its environment and nothing else of Hadron's.
Launch []string `yaml:"launch"`
// End is an optional argv that prompts the running agent to end its own
// session with a handoff (worker.end). It is never a kill.
End []string `yaml:"end"`
// EndWait bounds how long worker.end waits for the session to end
// (default 15m).
EndWait string `yaml:"end_wait"`
// Recover is an optional argv that hands a relaunched agent its recovery
// prompt after a worker.reset-context (#1043), the way End prompts an
// end. It gets HADRON_SESSION_ID, HADRON_WORKER_ID and
// HADRON_RECOVERY_NODE (the checkpoint node's reference): ids only. A
// worker without it is never reset on this host.
Recover []string `yaml:"recover"`
// Worktrees are this worker's existing worktree name tokens, relative to
// worktrees_root, beyond the ones this poller created.
Worktrees []string `yaml:"worktrees"`
// TUI is the agent interface its terminal shows, claude or codex, when
// the model family does not say (a glm worker runs in Claude Code).
// Delivery holds a terminal that shows anything else.
TUI string `yaml:"tui"`
}
WorkerConfig is one worker this host can launch.
type WorktreeState ¶
type WorktreeState struct {
Exists bool
// IsWorktree is false for a path that exists but is not a git worktree.
IsWorktree bool
Branch string
// Dirty is uncommitted or untracked changes.
Dirty bool
// Upstream is the branch's upstream, empty when it has none.
Upstream string
// Ahead counts commits not on the upstream.
Ahead int
}
WorktreeState is what worktree.create and worktree.remove decide on.