Personal fork of prime agent. Mirror of: https://mnemosyne.sigilzero.dev/sigilzero/prime-agent
README.md

pa-cli #

The prime-agent binary.

Scope #

Argument parsing matching the TS CLI exactly, mode selection (interactive/headless/json/daemon subcommands), binary wiring of crates into processes, exit codes and user-facing errors. The daemon-forensics CLI (incident.rs, TS src/cli/incident.ts): the prime-agent incident timeline command — log-source discovery (agent.jsonl plus its .old rotation, falling back to the newest per-daemon log), --since/--until window and --session filter parsing, and the report rendering; the shared classifier lives in pa-types (pa_types::incident) and the agents-view notice in pa-tui. Client-side MCP auth flows (mcp_login.rs): the /mcp login//mcp logout hook the TUI calls — the TS interactive client's placement — resolving a server (settings mcpServers + the builtin catalog), running the pa-core OAuth login on the suspended terminal, and persisting through the shared auth store. The same placement serves the TUI's client-command hooks: provider auth (provider_login.rs — the /login//logout catalog, the API-key store, the MCP device flow, the Prime Inference terminal login (prime_inference_login.rs — the API-key flow: prime-cli credential reuse, the paste prompt with the whoami access check, the numbered team selector); the subscription OAuth and the Prime browser challenge stay unported and report it), and the /update runner (client_update.rs — the out-of-band installer funnel with the output captured, the same body prime-agent update runs; installer_update.rs is that CLI command's own driver, --check included), plus the TS-parity staged update flow (update_flow/, self_update.rs — the package update self target's body, kept for the TS-parity package corpus).

Non-goals #

No business logic; it composes pa-daemon, pa-core, pa-tui, pa-ai via their public APIs only.

Public API #

The prime-agent executable surface (flags, subcommands, exit codes). Internal lib types pub(crate), plus the daemon wiring (interactive_mode::{ensure_daemon_running, ensure_daemon_running_with, resolve_socket_path}) exported for the integration verifier.

Depends on #

pa-types, pa-ai, pa-agent, pa-core, pa-daemon, pa-tui (one-way, composition root).

The headless print/json modes (print_runtime.rs) drive the in-process session engine directly: prompt admission, the turn-boundary compaction checks (print_boundary.rs — the overflow compact-and-retry arm, the model-requested compaction consumption, and the threshold arm), the json event stream, and the headless terminal selection (stdout/stderr/exit code) mirroring modes/print-mode.ts. Goal continuation (print_goal.rs, the #252 residue): the --goal seed rides the first turn; the in-loop continuation hook (the pa-agent loop's getContinuationMessages seam) runs an active goal's natural continuations inside the one agent run (the TS shape: turn_end -> turn_start with the continuation user row between them, no run boundary), gated by queued input, a requested compaction, and the threshold arm; a threshold compaction holds its minted continuation for the boundary's queued followUp admission (TurnBoundary::admit_continuation). The budget-limit steer and the threshold-held continuation drain as this invocation's follow-up runs with the TS session-action phase frames. Inline subagents (print_rlm.rs): the daemonless print session hosts rlm.spawn children in-process over a tree of session nodes — the RlmSubagentHost impl (depth gate, name uniqueness, model resolution, the child session/engine build, the kickoff row), the AgentMessageController (family roster, one steer-lane delivery path), the quiescence drive that holds the run open until every admitted descendant settles and its replies and notices drain (the #2270 fix), the goal deferral behind unsettled work, and the exit disposal (kernels snapshotted, drivers joined; no child outlives the process). Session slash commands (print_session_command.rs): /compact, /goal, and active Lua commands share the pa-core executor. The stream includes session_action_update phase frames around the durable echo row, compaction_start/compaction_end for /compact, the unconditional goal_update for /goal, result rows, and the settled queue frame. A /goal start (or resume) drains its scheduled continuation inside the same prompt wait; a failed command rejects the prompt wait (the raw error to stderr, exit 1, no later prompts). Client commands (/model, /tree, ...) and unknown slash input are rejected in headless mode instead of becoming model prompts. Active native plugin commands use the same core executor. /reload reconciles the plugin host without command history rows; prime-agent prompt includes its active contribution inventory. Dynamic context_contribution instructions are evaluated at the model boundary; the prompt dump does not preview them. Print mode joins admitted plugin jobs before the next input or process exit, without waiting for future timers. New command-job failures reject that command; automatic-job failures are diagnostics. JSON reports terminal jobs with plugin_job_update rows. The stream retains the session header (version 3), slim message_update deltas, tool_execution_update, compaction events and outcome rows, and goal session-action frames. Verifiers: print_runtime_e2e and plugin_headless_e2e.

Daemon client #

The daemon-backed public commands (list, stop, rename, send, schedule) talk to the pa-daemon supervisor over its JSONL Unix socket through the crate-private client module (daemon_client.rs). The client never spawns a daemon - the TS CLI only auto-starts one for the internal daemon start/open commands, which are not reachable from the public surface.

Packaged layout / kernel packaging #

The release artifact is exe-adjacent (TS install.sh native layout): the binary plus package.json (the version manifest --version reads at runtime, with the compiled-in version as the fallback), prime-agent-runtime/ (the kernel sidecar), skills/, and docs/. scripts/package_release.py (make package) assembles, validates, version-pins, hashes, and tars it; the hidden --prime-agent-bootstrap flag is the installer handoff that pre-bootstraps the kernel venv. Verifier: tests/packaged_layout_e2e.rs.

Daemon discovery #

The discovery commands (status, doctor [--fix], shutdown [--force], TS cli/daemon-ps.ts) live in the crate-private daemon_discovery module: an OS census of listening unix sockets owned by product processes, a socket-dir sweep, probing/classifying each discovered daemon, and the reap/shutdown planners and executors. Containment is part of the contract: every scan, probe, and stop is scoped to an explicit DaemonStateRoot (the env-resolved current root for the CLI), with a hard never-touch exclusion list for this sandbox's ambient mission daemons. All e2e daemons live in test-created fixture directories only.

Update flow (staged activation) #

prime-agent update (the update_flow module): the invoking CLI plans, downloads, and stages the candidate (Acquire..Staged), then spawns the detached coordinator - the new binary running from its release dir - which adopts the status file and owns the FSM to a terminal state (Preparing..Complete, Rollback first-class). intent.json is the coordinator lock (Join on a live holder, dead-holder steal), the status file keeps the TS schema plus updateId/state/epoch, and the activation owns the bin-symlink swap (bin/previous rollback pointer, .activation-state recovery record). Mechanism support (version/channel policy, install-root layout, manifest fetch, archive staging) lives in pa-core's update module; the daemon-side prepare/commit/stop drivers and the boot restore pass are pa-daemon (slices 2-3 and 5): the coordinator's Restoring phase polls the successor's update_restore_status RPC for the real per-session counts. The legacy restart adapter accepts the fresh status path supplied by shipped TypeScript updaters after they activate a release. It reports the original requestId/phase status schema, translates their session checkpoint to a Rust roster, and restarts the daemon without repeating activation. Recovery rosters remain on disk when restoration fails. Socket-scoped checkpoints resume after an interrupted handoff when no daemon is running, and reject a different installed version. The adapter holds the shipped TypeScript shutdown-admission lease in both historic supervisor registries until Rust owns the replacement socket, preventing old clients from launching a competing daemon. Admission records are refreshed and removed only while their ownership token matches.