Documentation
¶
Overview ¶
Package state holds the per-agent runtime state machine. The machine is a tiny FSM driven by AgentEvent.Type values; consumers (UI, dashboard, idle timer) ask for the current state via Current().
Transitions are intentionally lenient: unexpected events don't reject — they just keep the current state. Real CLIs sometimes reorder events (delta before start, etc.), and crashing on every surprise would make agents fragile.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Lifecycle ¶
type Lifecycle int
Lifecycle is the high-level subprocess view that the Backends UI renders. Orthogonal to State (substate within an active spawn): State answers "what is the agent doing right now"; Lifecycle answers "is the subprocess alive, and if so what shape".
Transitions:
(zero) → Spawning when the pool starts a spawn Spawning → Working on the first event from the CLI Working → Idle on Done / Error (substate flips to Idle too) Idle → Working on the next event any → Killed on subprocess exit (idle TTL, Stop, crash)
The Idle state runs an auto-kill countdown (LastActive + IdleTimeout → process killed); the UI renders that as a remaining-time badge.
const ( // LifecycleSpawning: subprocess started, waiting for first stream // event. Renders "loading" in the UI. LifecycleSpawning Lifecycle = iota // LifecycleWorking: subprocess alive and currently processing a // turn. Substate (Thinking / RunningTool / Responding) gives the // detail. LifecycleWorking // LifecycleIdle: subprocess alive but no active turn — auto-kill // countdown is running. LifecycleIdle // LifecycleKilled: subprocess no longer alive. Reason is on the // spawn log's exit event. LifecycleKilled )
type Machine ¶
type Machine struct {
// contains filtered or unexported fields
}
Machine is goroutine-safe. Apply is called from the parser-reader goroutine; Current / LastActive can be read from any goroutine.
func New ¶
New returns a fresh machine in Idle. Caller can inject a clock for deterministic tests; pass nil for time.Now.
func (*Machine) Apply ¶
func (m *Machine) Apply(ev event.AgentEvent) State
Apply transitions the machine based on an incoming event. Unknown event types are no-ops on state but still bump LastActive — the CLI is producing output, which means it's not idle.
Returns the resulting state for callers that want to log transitions.
func (*Machine) LastActive ¶
LastActive returns the timestamp of the most recent event applied. The pool's idle timer compares this against IdleTimeoutSec to decide when to kill the subprocess.
func (*Machine) MarkIdle ¶
func (m *Machine) MarkIdle()
MarkIdle forces the machine back to Idle without touching LastActive. Used when the subprocess is killed externally (TTL, shutdown) so a stale Responding state doesn't linger in the UI.
func (*Machine) MarkKilled ¶
func (m *Machine) MarkKilled()
MarkKilled flips the lifecycle to Killed. Pool calls this from the OnExit hook regardless of exit reason; the reason is recorded on the spawn log's exit event, not here.
func (*Machine) MarkSpawning ¶
func (m *Machine) MarkSpawning()
MarkSpawning resets the machine to the Spawning lifecycle. Pool calls this when (re-)spawning a subprocess; idempotent.