Module persistent

Module persistent 

Source
Expand description

PersistentSubprocessController: manages agent CLI as a long-running process with bidirectional NDJSON streaming via stdin/stdout.

Architecture: A single CLI process is spawned on first message and kept alive for the entire session. User messages are written as NDJSON lines to stdin without closing it. This enables mid-turn input (redirecting the agent while it is still processing).

State machine: INIT ─(first message)─> RUNNING ─(idle between turns)─> RUNNING RUNNING ─(kill/stop)─> DONE RUNNING ─(process crash)─> DONE (auto-restart possible via session_id)

I/O model (3 async tasks per session):

  1. stdin_writer: mpsc channel → process stdin (NDJSON lines)
  2. stdout_reader: process stdout → .jsonl persistence + WPS blockfile events
  3. process_waiter: wait for exit, update status

Structs§

PersistentInner 🔒
Inner state protected by mutex.
PersistentSpawnConfig
Configuration for spawning the persistent process.
PersistentSubprocessController
PersistentSubprocessController keeps a long-running CLI process alive, sending user messages as NDJSON lines on stdin.

Constants§

ANSWER_RESUME_FALLBACK_MS 🔒
How long to wait after delivering an AskUserQuestion answer before assuming the turn did not resume and re-delivering the answer as a follow-up message. See answer_question and SPEC_ASK_USER_QUESTION_2026_06_15.md §10.1.
BLOCK_CONTROLLER_PERSISTENT
PERSISTENT_OUTPUT_SUBJECT
WPS file subject name for persistent subprocess output.

Functions§

build_answer_resume_message 🔒
Compose the directive follow-up message used by the AskUserQuestion dead-air fallback. answers maps each question’s text to the selected label(s) or free text (the same object delivered in the control_response). The message is deliberately directive so the model resumes the task instead of treating it as a no-op (the “user sent an empty message” failure mode).
muxbus_agent_id_from_env 🔒
Resolve the muxbus address (the agent’s display name) from a spawn env map. AGENTMUX_AGENT_ID (= agent.name, set at block creation) is canonical; WAVEMUX_AGENT_ID is the legacy fallback. Returns None — i.e. not muxbus-addressable — when neither is present (a non-agent persistent block).