Expand description
fno-agents substrate crate (Phase 6, ab-a09e1eaf).
This crate is the Rust substrate for PTY-managed agents (codex / gemini / future OpenCode). It is split per the design’s Locked Decisions:
- shared types (this module):
ShortId,AgentStatus,ParsedEvent(LD9, sealed enum),MonotonicTimestamp(count-during-sleep clock). pty: PTY spawn + bounded-ring output drainer (LD31).write_queue: bounded-backpressure stdin queue +write_queue::WriteMsg.supervisor:supervisor::RestartPolicystate machine + hard ceiling (LD36).readiness:readiness::ReadinessDetectortrait +UnknownReadinessSignal(Open Question #9: no generic byte-count fallback; per-CLI signal mandatory).
§Scope of Wave 1 (this PR)
Wave 0’s smoke prototype (cli/scripts/smoke/pty-survival/) refuted the
“direct daemon-owned PTY survives daemon restart” assertion: a child on a
PTY whose master the supervisor owns is SIGHUP’d and dies the instant the
master closes. The locked outcome (Outcome B) is a per-agent worker process
that owns the master and outlives the daemon. The substrate in this crate
is therefore written worker-side: pty::PtySession is what a worker
owns; the daemon (Wave 3) reconnects to workers over their sockets.
Deliberately deferred (documented seams, not gaps):
alacritty_terminalgrid wiring + per-CLIreadiness::ReadinessDetectorimpls -> Wave 2, alongside the smoke captures that define the grid patterns (the trait operates overreadiness::ScreenViewso Wave 2 only adds impls).tokioruntime integration -> Wave 3 (the daemon is its only consumer; the substrate stays runtime-agnostic and is driven fromspawn_blocking).
§Scope of Wave 2 (this PR)
Wave 2 fills the seams Wave 1 left:
provider:provider::Provider+provider::ProviderWithPtytraits (LD8) and the three impls (provider::ClaudeProvidershellout,provider::CodexProvider/provider::GeminiProviderPTY-managed).envelope:envelope::Envelopestructural anti-injection wrapper (LD15).screen: the terminal-grid construction behindreadiness::ScreenView(the per-CLIreadiness::ReadinessDetectorimpls now live inreadiness).
Modules§
- active_
backlog - Active backlog dispatcher: the drain-tick core + circuit breaker (node x-c070).
- agents_
config - Read
config.agents.<provider>.headless_yolofrom settings.yaml (bounded-posture amendment). - claude_
ask - Client-side
claude --bgask path (ab-cc926b4e). - client
- Client side of the daemon protocol (Wave 3): lazy-start the daemon, connect, and forward one request. Kept in the library so it is exercised by the integration tests without shelling out to the compiled binary.
- client_
verbs - Client-side
fno-agentsverbs ported from the Pythonfno agentsapp (the “Python-only verbs” bucket:drive-authority,trace,ping,attach,resume, plus the non-streaminglogspaths). - codex_
ask - Client-side
codex execask path (ab-0429c6e1). - daemon
- The supervisor daemon (Wave 3, tasks 3.0 + 3.4).
- drift
- Daemon binary-version drift detection (ab-1891cdff).
- drive
- Drive surface (Wave 4, ab-8d258ddb): WebSocket-backed interactive / watch / step takeover of a PTY-managed agent.
- drive_
client - Client side of
fno-agents drive(Wave 4, ab-8d258ddb). - envelope
- Structural anti-injection envelope (design module
envelope.rs, LD15). - events
- Operator-facing
events.jsonlemitter (Wave 3, task 3.2). - finalize
fno-agents finalize(control-plane step 6, ab-f8e5f214): the terminal-only WRITER the stop-hook shim invokes on a terminal-allowloop-checkdecision.- gemini_
ask - Client-side
gemini -pask path (ab-73da4ac2). - grid
fno agents grid- client-side TUI compositor (ab-3c063856).- kill_
criteria fno-agents kill-checkverb — Rust port ofscripts/lib/kill-criteria.sh(packaging EPIC ab-8bdb4642, eliminate-don’t-vendor leg).- logs
- Daemon-side
agent.logsstreaming handler (Category C / ab-d82655d7). - logs_
client - Client side of
fno agents logs <name> --followfor codex/gemini (Category C / ab-d82655d7). - loop_
dispatch - Shellout dispatcher that wraps the bash driver-lib contract.
- loop_
megatron - Megatron driver: MegatronQueue + MegatronDispatcher + the
loop run --driver megatronverb glue (group 3 of ab-ed61946d, node ab-9fd662c6). - loop_
megawalk - Megawalk driver: MegawalkQueue + MegawalkDispatcher + the
loop run --driver megawalkarm. - loop_
runtime - Unified loop runtime primitive for target, megawalk, and megatron drivers.
- loop_
target - Target driver: TargetQueue + the
loop runCLI verb. - loopcheck
fno-agents loop-checkverb (Task 1.1, ab-d0337fbc).- nudge
- P2 loop-boundary inbox nudge (ab-098967b4).
- paths
~/.fno/agents/filesystem layout (Wave 3).- protocol
- Unix-socket wire format (Wave 3, task 3.1).
- provider
- The
Providerabstraction (design moduleprovider.rs, LD8). - pty
- PTY spawn + bounded-ring output drainer (design module
pty.rs). - readiness
- Readiness detection (design module
readiness.rs). - screen
- Terminal-grid construction behind the
ScreenViewseam (Wave 2). - state
- Shared state files (Wave 3):
registry.json(schema v4) and per-agentstate.json(schema v1), plus the flock-protected, atomic read/modify/write helpers the daemon and worker share. - stream_
worker - Per-session stream-json worker (Group 1, Outcome B) — the claude analog of
the PTY
crate::worker. - subprocess_
ask - Shared one-shot-subprocess primitives for the client-side
askports (codex + gemini). Extracted fromcodex_ask.rs(ab-73da4ac2) so the SIGINT forwarding, process-group kill, grace reap, watchdog, and output tee live in ONE place and the PR #371/#372 hardening carveouts apply to every provider: - supervisor
- Restart policy state machine (design module
supervisor.rs). - verify_
evidence fno-agents verify-evidenceverb — Rust port ofscripts/lib/verify-event-evidence.sh(packaging EPIC ab-8bdb4642, eliminate-don’t-vendor leg).- worker
- Per-agent worker shim (Wave 3, task 3.3 — Outcome B).
- write_
queue - Bounded-backpressure stdin queue (design module
write_queue.rs).
Structs§
- Monotonic
Timestamp - A monotonic timestamp that counts during system sleep, used for drive-window heartbeat math (LD17 + Domain Pitfall: macOS/Linux suspend divergence).
- ShortId
Enums§
- Agent
Status - Agent lifecycle status.
state.statusis canonical;registry.statusis a denormalized projection of it (LD10). Serialized snake_case for the JSON state files and the cross-language schemas. - Parsed
Event - Sealed event vocabulary every provider parses INTO (LD9). Variant additions
are a one-line crate-wide change; no per-provider enums.
#[serde(tag="kind")]matches the wire shape in the design’s Architecture section. - Short
IdError - A short, opaque agent identifier (e.g.
wkA). Stored in the registry and used to name per-agent state directories. Validation is intentionally light at this layer; dispatch-layer validation (US1 invariant) owns argv rules.
Constants§
- KNOWN_
EVENT_ KINDS - All real operator-facing event kinds emitted by the Rust supervisor. Excludes test-only kinds (tick, heartbeat).
Functions§
- emit_
schema_ json - Build the Branch B (Rust/fno-agents) envelope JSON Schema and the
status-v1AgentState schema as static JSON objects.