# Technical Documentation: PrimeIntellect-ai/prime-agent

> ℹ️ **Provenance:** Multi-Tier Ingestion · [CodeWiki Reference](https://codewiki.google/github.com/PrimeIntellect-ai/prime-agent)

### Overview (`README.md`)

<p align="center">
  <a href="https://primeintellect.ai">
    <picture>
      <source media="(prefers-color-scheme: light)" srcset="https://github.com/user-attachments/assets/40c36e38-c5bd-4c5a-9cb3-f7b902cd155d">
      <source media="(prefers-color-scheme: dark)" srcset="https://github.com/user-attachments/assets/6414bc9b-126b-41ca-9307-9e982430cde8">
      <img alt="Prime Intellect" src="https://github.com/user-attachments/assets/6414bc9b-126b-41ca-9307-9e982430cde8" width="312" style="max-width: 100%;">
    </picture>
  </a>
</p>

<h3 align="center">
Prime Agent: A Self-Improving RLM Harness
</h3>

<p align="center">
  <a href="#install">Documentation</a> &bull;
  <a href="https://github.com/PrimeIntellect-ai/verifiers">Verifiers</a> &bull;
  <a href="https://github.com/PrimeIntellect-ai/prime-rl">PRIME-RL</a>
</p>

<p align="center">
  <a href="https://github.com/PrimeIntellect-ai/prime-agent/actions/workflows/ci.yml">
    <img src="https://github.com/PrimeIntellect-ai/prime-agent/actions/workflows/ci.yml/badge.svg" alt="CI" />
  </a>
  <a href="https://arxiv.org/abs/2608.23552">
    <img src="https://img.shields.io/badge/arXiv-2608.23552-b31b1b.svg" alt="arXiv" />
  </a>
</p>

<p align="center">
  <a href="https://trendshift.io/repositories/104249?utm_source=repository-badge&amp;utm_medium=badge&amp;utm_campaign=badge-repository-104249" target="_blank" rel="noopener noreferrer">
    <img src="https://trendshift.io/api/badge/repositories/104249" alt="PrimeIntellect-ai%2Fprime-agent | Trendshift" width="250" height="55" />
  </a>
</p>

Prime Agent is an open-source coding and research agent for general and long-running work. It is designed around two core abstractions:

- The **[Recursive Language Model (RLM)](https://www.primeintellect.ai/blog/rlm)** treats context as variables (*prompt-as-a-variable*) and tools like recursive subagents as function calls (*programmatic tool /sub-agent calling*) inside a persistent REPL.
- The **[Continual Harness](https://arxiv.org/abs/2605.09998)** stores supplemental prompts, memories, skill descriptions, and reusable subagent specifications as durable state that Prime Agent can refine through small, evidence-backed updates, local to the session by default.

## Install

Install the latest stable version with the one-command installer:

```bash
curl -fsSL https://app.primeintellect.ai/prime-agent/install.sh | sh
```

On Windows, install from PowerShell:

```powershell
irm https://app.primeintellect.ai/prime-agent/install.ps1 | iex
```

The Windows installer downloads the latest stable Windows x64 build and verifies its checksum before installation.

## Why Prime Agent

Prime Agent combines a persistent Python control environment with durable harness state, so useful working context and reusable operating patterns can outlive a single chat window.

- **Everything is programmatic:** a persistent Python REPL is the built-in model tool; file operations, shell commands, tool use, subagents, and context management happen through code.
- **Subagents are built in:** `rlm.spawn(...)` spawns real child agents for parallel or background work and returns their results programmatically.
- **The harness can improve:** `/refine` reviews the current trajectory and can apply small, evidence-backed updates to supplemental harness state. It never rewrites the immutable base system prompt, and recorded snapshots support rollback.
- **Skills are executable:** skills are importable Python packages, and the built-in skill creator can turn recurring workflows into project or personal skills.
- **Sessions run in the background:** daemon-backed agents keep running when the terminal disconnects and can be reattached later.
- **Agents communicate directly:** running agents can exchange messages and orchestrate one another without routing everything through the user.
- **Long tasks keep moving:** automatic compaction, persistent goals, heartbeats, schedules, autonomous mode, and retained subagents preserve progress across turns and terminal sessions.

## Slack

Connect Slack from `/mcp` or run `/mcp login slack` inside Prime Agent to search
your workspace and use Slack tools. See the [Slack connector guide](docs/slack-mcp.md)
for OAuth setup and troubleshooting.

## Clipboard over SSH and tmux

Prime Agent uses the local clipboard tool (such as `pbcopy`) when available. In tmux it asks the attached tmux client to forward a clipboard buffer if that client advertises the `Ms` capability. This works with tmux's default `set-clipboard external`, unlike application-origin OSC 52. Without tmux, remote copies request delivery using OSC 52. Terminal requests cannot confirm that your local clipboard changed, so Prime Agent reports them as unconfirmed.

Nested tmux servers and terminals may still block delivery. An outer tmux with `set-clipboard external` can reject the inner server's OSC 52. In that case, run `tmux set -s set-clipboard on` on the outer server, or use native terminal selection (Shift-drag in most terminals; Option-drag in iTerm2) followed by Cmd-C. macOS Terminal.app does not support OSC 52; iTerm2 requires its “Applications in terminal may access clipboard” setting.

## Custom certificate authorities

HTTPS requests trust your operating system's trust store together with the bundled Mozilla roots. For servers behind a custom CA, point `NODE_EXTRA_CA_CERTS` at a PEM bundle of your CA certificates — the same variable the Node/TypeScript build honors:

```bash
export NODE_EXTRA_CA_CERTS=/path/to/your-ca.pem
```

Each transport reads the variable once when its first client is initialized. The background daemon keeps the environment it started with, so run `prime-agent shutdown` after changing it.

## Getting Started

Start Prime Agent from the repository or directory you want it to work in:

```bash
cd /path/to/project
prime-agent
```

On first launch, run `/login` to choose a subscription or API-key provider. Prime Agent works in the current directory and can run commands and modify files there. Use a disposable clone, clean worktree, or another checkpoint you can inspect and restore.

> [!WARNING]
> Prime Agent executes model-generated Python and project commands with your user permissions. Its worker and kernel processes improve lifecycle isolation and recovery; they are **not** a security sandbox. Review changes and use trusted repositories, instructions, and skills only. Run untrusted code or instructions in an external sandbox or restricted environment.

Useful commands:

```bash
prime-agent agents                   # Browse running, idle, and saved sessions
prime-agent attach <agent>           # Reattach to a running session
prime-agent --resume [path|id]       # Browse sessions or resume one directly
prime-agent status                   # Inspect background service state
prime-agent doctor [--fix]           # Inspect or repair background services
prime-agent update [--force]         # Update Prime Agent
prime-agent shutdown [--force]       # Stop every agent, worker, and background service
```

## Built for Long-Running Work
Prime Agent is built for long-running work, especially for evaluations in research. These features are available in the TUI, and when run autonomously.

- **Continual Harness:** `/refine` can persist focused, reviewable lessons as supplemental prompts, memories, reusable skill descriptions, or subagent specifications, with recorded refinement history. It does not replace packaging and reviewing new executable skills.
- **Direct agent-to-agent communication:** running agents and retained subagents can discover one another, exchange messages, and steer active work.
- **Daemon-backed continuity:** active sessions, Python REPL state, schedules, and subagents keep running when the terminal detaches and can be reattached later.
- **Heartbeats and schedules:** `/heartbeat`, `rlm_heartbeat`, and `prime-agent schedule` can re-enter a session periodically or at a specific time.
- **Persistent goals:** `/goal` keeps an objective and its progress active across turns until it is completed, paused, or cleared.
- **Bounded autonomous mode:** `/autonomous` continues within configured turn, token, and time budgets and can run user-defined quality gates. A passed gate checks only what that gate verifies; reaching a limit does not imply task success.

## Contributing

Start with a GitHub Discussion for [general questions](https://github.com/PrimeIntellect-ai/prime-agent/discussions/categories/general), [bug reports](https://github.com/PrimeIntellect-ai/prime-agent/discussions/categories/bug-reports), and [feature requests](https://github.com/PrimeIntellect-ai/prime-agent/discussions/categories/feature-requests). Maintainers promote accepted work into Issues, and pull requests are reviewed from maintainers and vouched contributors.

## License

Prime Agent is fully open source and released under the [MIT License](LICENSE).

## Citation

If you use this codebase in your research, please cite Prime Agent:

```bibtex
@article{karten2026prime,
  title={Prime Agent: A Self-Improving RLM Harness},
  author={Karten, Seth and Zhang, Alex L. and Thomas, Kevin and Müller, Sebastian and Bakouch, Elie and Auras, Daniel and Senghaas, Mika and Obeid, Fares and Dunas, Konstantin and Hagemann, Johannes and Jaghouar, Sami},
  journal={arXiv preprint arXiv:2608.23552},
  year={2026}
}
```

Available at [https://arxiv.org/abs/2608.23552](https://arxiv.org/abs/2608.23552).

### Automatic compaction settings

`/settings` exposes the global automatic compaction percentage in five-point steps,
with a 95% default. A saved off-grid percentage stays selectable. The effective
trigger also reserves room for retained history and summaries; the model's input
and requested-output ceiling always takes priority. Manual compaction budgets are
unchanged. The underlying `compaction.thresholdPercent` setting accepts 1–100,
including fractional values; project settings can override the global setting.

For per-model early compaction, use the existing provider-scoped
`modelOverrides.<id>.contextWindow` option in `models.json`, for example:

```json
{
  "providers": {
    "prime-inference": {
      "modelOverrides": {
        "your-model-id": { "contextWindow": 524288 }
      }
    }
  }
}
```

Use the actual model ID for your provider. This sets that model's effective context
window; it does not change every model's global compaction percentage.

---

### Overview (`crates/pa-agent/README.md`)

# pa-agent

The agent loop.

## Scope
Turn loop: drives a provider stream, dispatches tool calls, enforces max-turn and sequencing semantics, handles partial streams, retries on stream failure, propagates aborts.

## Non-goals
No tool implementations (pa-core), no providers (pa-ai), no session persistence, no UI. The loop is tool-agnostic: it receives a `ToolDispatcher` boundary.

## Public API
`run_loop` (session inputs -> stream of loop events), `ToolDispatcher` trait. Loop internals `pub(crate)`.

## Depends on
No workspace dependencies.

---

### Overview (`crates/pa-ai/README.md`)

# pa-ai

Provider APIs and model registry.

## Scope
Provider trait + per-provider streaming clients (anthropic, openai-completions/responses, google, bedrock, mistral, azure, prime-inference), model registry/resolution, usage accounting, stream-failure retry, provider-error shapes (per-SDK user-facing texts, diagnostic error names, connection-error profiles), bedrock transport selection (h2c prior-knowledge HTTP/2 cleartext, h2-preferred TLS ALPN, the http1 AWS_BEDROCK_FORCE_HTTP1/proxy mode), overflow handling, JSON repair parsing, faux provider for tests.

## Non-goals
No agent loop, no tool execution, no session state, no UI. Receives/returns `pa-types` messages.

## Public API
`Provider` trait, `ProviderRegistry`, model lookup/resolution, faux provider. `utils::tls::http_client_builder` lets caller-owned HTTP transports apply the shared extra-CA trust configuration. Per-provider internals are `pub(crate)`.

## Depends on
pa-types (one-way).

---

### Overview (`crates/pa-cli/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), trace-sharing state (`client_traces.rs` — the settings flag writes and the credential resolution `/traces` shows; the upload subsystem stays unported), 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).

## Print runtime
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/refinement consumption, and the threshold arm,
the TS `_checkCompaction` flow), the json event stream, and the headless
terminal selection (stdout/stderr/exit code) mirroring modes/print-mode.ts.
Autonomous continuation (print_autonomous.rs, the #254 follow-up): the
composed in-run continuation hook (the pa-agent loop's
`getContinuationMessages` seam) runs the natural continuations inside
the one agent run (the TS shape: `turn_end` -> `turn_start` with the
continuation user row between them, no run boundary); the goal arm runs
first (exclusive priority), the boundary gates (queued input, a
requested compaction, the threshold arm) are shared, and a threshold
compaction holds its minted continuation for the boundary's queued
`followUp` admission (`TurnBoundary::admit_continuation`). The stop
surfaces only through the headless exit contract (TS: no row, no frame).
Goal continuation (print_goal.rs, the #252 residue): the `--goal` seed rides
the first turn; the in-loop continuation hook runs an active goal's natural
continuations inside the one agent run; 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/autonomous deferral behind unsettled work, and the exit
disposal (kernels snapshotted, drivers joined; no child outlives the
process).
Session slash commands (`print_session_command.rs`, the #253 residue): print
prompts that are `/compact`, `/refine`, `/goal`, or `/autonomous` execute
through the pa-core session-command executor (the daemon worker's seam — no
parallel implementation), streaming the TS shapes: the
`session_action_update` phase frames around the durable echo row, the
per-command events (`compaction_start`/`compaction_end` for `/compact`, the
refinement rows plus `refine_complete`/`refine_failed` for `/refine`, the
unconditional `goal_update` publish for `/goal`), the 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`, ...) stay model prompts — TS
`_normalizeSubmission` classifies only the four session commands before
admission, so they never cross the turn boundary.
The json stream emits the TS session-event surface byte-for-shape: the
session header row (version 3), the `message_update` streaming deltas with
the slim `assistantMessageEvent` (the daemon wire drops the nested
`partial`), `tool_execution_update`, the harness digest's message pair at
the first-turn boundary, the compaction `compaction_start`/`compaction_end`
pairs with their durable outcome rows, the `goal_update`/`session_action_update`
frames of the goal loop, and the refinement rows with
`refine_complete`/`refine_failed`. Verifier: the `print_runtime_e2e` rows.

## 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.

---

### Overview (`crates/pa-core/README.md`)

# pa-core

The session engine.

## Scope
MCP host side (`mcp/`): auth gating for built-in integrations (the
disabled-skill overrides and `/mcp list` status), the `mcpServers` settings
seam, and the `mcp.*` host-request handlers the kernel's generic MCP
client reaches through (`mcp.config`, `mcp.refresh`, optional
`mcp.begin_login`). The OAuth flow lives behind it (`oauth*` submodules:
RFC 9728 discovery, dynamic registration, PKCE, the local callback
server, token refresh; `McpOAuthConfig::callback_port` selects an exact registered
desktop callback port, while dynamic clients retain the candidate range.
Slack's endpoint selects its documented public desktop client; `login.rs`: login execution that persists the
endpoint-bound credential into the shared auth store and the
`wire_begin_login`/`McpOAuth` refresh integration hosts wire). The MCP
protocol itself runs kernel-side (Python
stdio/HTTP); the engine never spawns MCP servers. Tools (bash, edit, python_repl + internal rename/stdout), file mutation queue, truncation and rendering rules, RLM kernel lifecycle (IPython spawn/execute/revive), skills loading, system prompt assembly, compaction, harness refinement, settings/config, package manager (npm/git/local source install/remove/list/update against settings), session manager (persist/resume). Autonomous mode
(`autonomous`): runtime state with limit normalization, continuation and
gate-failure texts, shell quality gates with retry windows and git
worktree snapshotting, and the `AutonomousDriver` policy trait a turn
loop consults after every settled turn (the engine holds no autonomous
logic of its own). The RLM recursion host seam
(`session_engine::rlm_host`): the trait the kernel's `rlm.spawn`/
`rlm.create_session`/`rlm.list_subagents`/`rlm.collect`/
`rlm.delete_subagent` host requests call into, with the roster/collect/
selector-error vocabulary the daemon implements over the supervisor link.
Platform wall (`platform`): process control (signals/process groups, kernel exit waits), file locking, file permissions, and shell selection - every OS-specific behavior in the engine lives there behind cfg-gated implementations (the durable-write rename primitive is pa-telemetry's `rename_onto`, re-exported as `platform::rename_onto`). Scheduled jobs (`cron`, the `AgentCronJobStore` port of `core/cron-jobs.ts`): file-backed job state under session artifacts (`scheduled-jobs.json` partitions) with cross-process locking, plus the public read-only scan (`cron::store::read_scheduled_jobs_artifact`) the update flow's roster projection and boot re-arm read. Tools (bash, edit, python_repl + internal rename/stdout), file mutation queue, truncation and rendering rules, RLM kernel lifecycle (IPython spawn/execute/revive), skills loading, system prompt assembly, compaction, harness refinement, settings/config, package manager (npm/git/local source install/remove/list/update against settings, plus `resolve()`: precedence-ranked session resource resolution over configured packages, settings arrays, auto-discovery, and bundled skills), session manager (persist/resume). origin/main

Provider resilience policies in `session_engine`: the shared quick-retry
policy (TS `provider-retry.ts`: permanent-kind classification,
Retry-After-aware capped delays), the interactive auto-retry loop, and the
provider-failover driver (when a provider exhausts its quick retries and
another configured provider serves the same model, the failed turn
re-routes to the next provider in catalog order; the TS
`auto_retry_start`/`reason: "backup"` event vocabulary surfaces the
progression). The drivers are pure decision logic: the host (pa-daemon)
owns the attempt, the wait, and the model switch.

Session HTML export (`export_html`): the standalone viewer file for
`/export` and `session export` - the embedded product template
(assets/export-html, vendored marked/highlight.js attributed in its
NOTICE.md) plus the theme-to-CSS resolution over the bundled theme data
(pa-types) and custom themes under `<agent-dir>/themes/`, the tools
section mapping (`tools_section`: the session's tool registry to the
template's name/description/parameters list), and the custom-tool
pre-render pipeline (`ToolHtmlRenderer`/`pre_render_custom_tools` +
`ansi_to_html`: the exporter walks the entries, the session layer's
renderer produces line-oriented output, ANSI converts to inline-styled
HTML at the export step), and the two entry points
(`export_session_to_html` for the daemon worker's `export_html`
command, `export_from_file` for the CLI). Terminal theme rendering stays
pa-tui's; tool renderers live with the session's tool surface;
the share-viewer flow (gist creation) is the caller's.
Cloud workspace snapshots (`workspace_snapshot`): the portable, verifiable
capture of a git worktree's working state - tracked modifications, staged
paths, deletions, and nonignored untracked files - staged into an
owner-private directory as content-addressed blobs plus a deterministic
manifest that hashes them, with credential-shaped, symlink-ancestor,
escaping-symlink, nested-repo, and submodule exclusions recorded in the
manifest, hard entry/size bounds, race-free openat(O_NOFOLLOW) leaf reads
(Unix), and offline verification of a staged snapshot before upload. Capture
refuses on non-Unix targets: owner-only 0700 staging and 0600 files cannot be
enforced there. The baseline is an explicit caller choice (`BaselineMode`):
HEAD's tree content staged through the same filters as the delta (each entry
verified against HEAD's recorded object id and mode, no git history ships),
or an explicit external reference the consumer must reach itself. The secret
skip is a filename-shaped denylist only; ordinary-named files containing
secrets stage verbatim. Snapshot directories are secret-bearing: every
consumer must treat them as credentials. Capture does synchronous file I/O
inside its async entry point; callers must offload the entire operation with
`tokio::task::spawn_blocking` and `Handle::block_on`. Foundation plumbing
only: nothing wires it to a user-facing cloud toggle yet.

## Non-goals
No provider HTTP (pa-ai), no loop policy (pa-agent), no daemon supervision (pa-daemon), no TUI (pa-tui). No update coordination (the pa-cli coordinator owns the FSM; the update-flow seam here is the daemon-free support layer: `update::version` (semver/channel policy), `update::install` (the managed install-root layout), `update::release` (the channel manifest fetch), `update::download` (sha256-verified archive download + staging)). The package manager installs sources and resolves resource paths only. No workspace snapshot upload, transport, or remote materialization (staging and verification live here; the cloud attach surface owns the transport when it ships).

## Public API
`SessionEngine` (message in -> events out), `export_html` (the HTML exporter plus its renderer seam: `tools_section`, `pre_render_custom_tools` + `ToolHtmlRenderer`/`RenderedToolHtml`/`RenderedToolResult`, and `ansi_to_html::ansi_lines_to_html`/`tool_render::trim_rendered_result_lines` for renderer implementers), `ToolRegistry`, kernel manager, settings (`SettingsManager`), packages (`packages::PackageManager` + source types), `platform` (process control, file locking, permissions, shell selection - usable by higher crates, e.g. pa-cli's detached spawn), `session_engine::rlm_host::RlmSubagentHost` (implemented by pa-daemon for supervisor-backed children and by pa-cli's print mode for in-process children; the default is no children; the trait crate also owns the shared spawn/depth facts — `DEFAULT_RLM_MAX_DEPTH`, the depth-limit and name errors, the daemonless `create_session` refusal, `rlm_child_session_dir`, and the `persisted_rlm_max_depth`/`resolve_rlm_max_depth` precedence chain), `session_engine::semantic_edges` (the TS `semantic-edges.ts` WRITER: `SemanticEdgeRecorder`/`SemanticEdgeIdentity`, `semantic_edge_ledger_path` + `last_committed_request_id`, and the `X-ACP-Model-Request-ID`/`Idempotency-Key` constants — one durable per-session ledger of model-request events, byte-compatible with the TS lines; the fold and any publishing stay downstream), `mcp::McpManager` (the session's host-side MCP manager, exposed as `SessionEngine.mcp_manager`: auth gating for built-in integrations, the `mcpServers` setting seam, and the `mcp.*` host-request handlers - `mcp.config`/`mcp.refresh`/optional `mcp.begin_login` - the kernel's generic MCP client resolves through), `mcp::McpManager::connection_roster` + `mcp::McpConnectionEntry` (the `/mcp` view's roster: every resolved integration with its connected state, display kind, transport, and generic-listable flag), `mcp::McpManager::service_catalog_views` + `service_catalog_diagnostics` (the `/mcp` view's resolved service-catalog cards and their diagnostics, answered from local state - the TS `buildServiceCatalogViews` parity; the picker never round-trips the kernel), `mcp::McpManager::api_key_credential_views` + `mcp::API_KEY_CREDENTIALS`/`McpCredentialView` (the `/mcp` view's api-key credential rows - the stored keys the surface manages alongside the connections, e.g. the web-search key - configured from the shared auth store), `models` (the registry and resolver, plus `models::order_for_picker`: the picker-catalog order the composition root applies to its snapshot), `session_engine::provider_adapter` (`switchable_stream_fn`/`ProviderTarget`: the live provider-target slot a host swaps on `set_model`; the adapter bridges the loop's abort into pa-ai's `StreamOptions::signal` cancellation token, so a turn abort cancels the in-flight HTTP fetch at the transport — closed by `ModelStream::close` and the stream's drop), `session_engine::ipython_state` (the post-compaction `ipython_state` kernel-persistence notice: the `CompactionKernelProbe` view of the kernel provisioner the engine wiring binds via `AgentSession::set_kernel_state_probe`, and the notice row that rides `CompactRun` for the surfaces to broadcast). `session_engine::session_commands` (the `/compact`/`/refine`/`/goal`/`/autonomous` executor: the durable echo/result rows with their persistence order - the echo ahead of the command so its own work sees it - the compaction/refinement/goal/autonomous arms, and the post-execution live-context rebuild that keeps the rows in the agent state; hosts own the wire surface), `session_engine::auto_refine_trigger` (the compact-trigger auto-refine machine on `AgentSession`: the arm/discard/increment accessors and `consume_compact_auto_refine`, the shared gate/review/stamp sequence every scheduling surface applies - TS `_compactAutoRefinePending`/`_lastAutoRefineReviewAt`/`_assistantTurnsSinceAutoRefine` plus `_maybeAutoRefine`'s compact arm, the serialized checkpoint's compact step, and dispose's serialized drain, with the surface parameter carrying the one behavioral difference between a boundary and a disposal). `session_engine::goal_boundary` (the goal arms of the natural turn end on `SessionEngine`: the `--goal` construction seed with its next-turn context row - TS `_pendingNextTurnMessages` on `AgentSession` - the usage accounting, the budget-limit steer mint, the natural continuation mint with its slot rollback, the RLM deferral behind unsettled descendant work (mark/drop/take owed, the wake-up ownership probe, and the latest settled turn the post-settle deliveries decide from), and the terminal-error finish; the transports - the print driver's in-loop hook, the ACP settle loop, the daemon worker's queue - drive the one goal driver through these methods). `SessionEngine::expand_skill_submission` + `AgentSession`'s prompt-path expansion (the `/skill:<name>` submission seam, TS `_expandSkillCommand`: `skills::expand_skill_command` builds the `<skill ...>` block over the shared `pa_types::skill_blocks` parse, the accepted-turn row and the admitted user message agree on the expanded text, and the `skill_use_count` session counter counts at the admission). `workspace_snapshot` (`create_workspace_snapshot`: bounded, secret/nested-repo/submodule/symlink-ancestor-excluding capture of a git worktree into an owner-private staging directory, with the caller-chosen `BaselineMode` (HEAD-tree content verified against HEAD's object ids, or explicit external) and a `ConcurrentMutation` error when the worktree changes under the capture; `verify_workspace_snapshot`: offline manifest/blob/baseline integrity check; the `SnapshotManifest`/`WorkspaceSnapshot`/`SnapshotLimits`/`BaselineMode`/`SnapshotError` types). All subsystem internals `pub(crate)`; the engine is the only facade - subsystems must not import each other directly (the package manager consumes the public settings API only).

## Depends on
pa-types, pa-ai, pa-agent (one-way).

The Unix platform process API exposes `current_user_id()` for the daemon
endpoint namespace to match the TypeScript `process.getuid()` contract on
macOS as well as Linux. This reads the OS identity without filesystem or
subprocess work.

Automatic trace delivery (`agent_traces::ContinuousTraceUpload`) owns consent-gated
persist scheduling and startup outbox recovery alongside the existing manual uploader.
Hosts reuse `load_settings` to capture consent before their existing settings parse,
attach `persisted` after successful disk writes, and own the returned installation
for the session lifetime. Replacements use `rebind` with the effective cwd; forks
use `forked`. The installation never drains on drop. A detached process-wide runtime
owns all transcript reads, scans, settings reloads, networking and retries; a maximum
of 256 registered controllers and four deliveries bounds resident work. Admission
returns `None` at capacity and logs rejection; hosts attach no hook in that case.
Retired descriptors free capacity only after background recovery acknowledges them.
Session replacement transfers its existing slot and cancels the predecessor; forks
require a distinct slot. Replacement retains a bounded retirement flag until the
background recovery snapshot acknowledges its exact controller identity. The synchronous
persist seam checks the cached consent snapshot and performs an exists/create
pending-marker write; consent metadata reads and reloads stay in the worker after
the host captures its initial settings generation; it does not fsync the marker (matching TS v0.9.8 process-crash
recovery rather than promising power-loss durability). A short nonblocking mutation lease prevents cursor/prune races; contention
records one stable fallback marker per session, which is retained conservatively
until a future sender has delivered it. Its cost must be measured
separately; startup/paint parity is not established by scheduler unit tests.
Active requests monitor consent changes every second on the worker and cancel on
revocation/errors; bytes already sent cannot be recalled. Recovery belongs to all
live hosts sharing the agent directory and cancels when the last host disappears.
Catch-up uses at most three delivery cycles per pending entry, honors Retry-After,
and releases upload capacity during retry waits; exhaustion retains durable intent.

---

### Overview (`crates/pa-daemon/README.md`)

# pa-daemon

Session supervision and wire serving.

## Scope
ACP stdio transport (`acp`): the JSON-RPC serve surface for Agent
Client Protocol clients, served over a daemon session (initialize, session/new, session/prompt, session/close,
session/cancel, and the outgoing session/update notification with
namespaced `_meta` correlation), owned by this crate because
wire-protocol serving is its area; the worker turn loop owns compaction
arms, auto-refine, and goal continuation. The compact-trigger
auto-refine scheduling owns its surface here (`compact_autorefine.rs`
for the daemon worker: every compaction arm plus the manual `compact`
command arm the pa-core trigger, and the quiescent turn boundaries /
the command path consume the gated review - TS
`_scheduleAutoRefineAfterCompaction` plus the background
`_maybeAutoRefine("compact")`).
Supervisor process (one worker process per active session), restart/backoff
supervision, session registry/roster + worker self-registration (session
identity survives supervisor restarts: workers re-register with backoff and
the roster rebuilds), parent-death child cleanup (`supervisor_parent_death.rs`:
an unexpectedly exited worker's resident RLM children close with it - the
same plain-stop semantics as the worker-side #246 close, which SIGKILL
bypasses - via the durable create's `parentActiveSessionId` join), append-only session store ownership (including the
compaction-entry fold for compacted message reads), client attach/detach
(full-snapshot and chunked `session_snapshot_begin`/`chunk`/`end`
streaming), direct-attach transport (supervisor-issued single-use tickets
with a 10s TTL, worker-side peer grants burned on first use, session-plane
command gating on peer links), kernel agent_message/agent_observe bridge (`agent_messaging.rs`: the worker-side controllers over the supervisor link; the family view joins the supervisor roster with the session's own RLM children registry - the same registry `rlm.list_subagents` reads - so registry children are Child members addressable by name, RLM child id, and persisted session id, the spawning session is the Parent member for a subagent worker, and everything else stays a sibling; delivery tries the direct peer transport then the supervisor-routed `send_message`), worker-to-worker peer messaging (stage 3:
`worker`-purpose single-use grants minted by `get_worker_peer_transport`
for a source worker's kernel `agent_message.send`, direct
`worker_deliver_message` on the target worker's socket with the supervisor
routed `send_message` as the never-retried fallback),
saved-session wake
for non-resident `send_message` targets (`session_catalog.rs`: catalog
resolve by session-id prefix or exact name, cwd-scoped first; a live miss
lazily restores a matching file from `sessions-archive/` without
overwriting an existing live file; no new sessions are physically archived;
a resident worker hosting the file is reused, otherwise one spawns over it; RLM
children fall back to the spawn ledger's live child edges when the catalog
misses - children persist outside the sessions dir, under the parent's
session-artifacts tree - and wake over their own session file), wire protocol serve/negotiation (including the
`compact`/`abort_compaction`/`set_auto_compaction` commands and their
`compaction_start`/`compaction_end` events, and the live
`set_model`/`set_thinking_level` commands — `model_switch.rs`: registry
resolution, the durable `model_change`/`thinking_level_change` rows, the
settings defaults, and the engine's live agent/provider-target/level
switch), session export commands (`session_export.rs`: the worker-side
`export_html`/`export_jsonl` handlers; the HTML data carries the engine's
tools section and custom-tool pre-render through the
`SessionEngine::export_tools`/`export_rendered_tools` seams, built
lazily when an export precedes the first turn - the TS state exists from
create), the `get_model_catalog` command (the TS `refreshModelCatalog`:
registry refresh — live Prime Inference catalog fetch, disk cache at
`<agent-dir>/prime-inference-models-cache.json`, bundled fallback — then
the full catalog minus unauthorized private models plus the configured
providers; the worker create path fires the same refresh at startup), the
agent-roster arms
(`roster_subscribe`/`roster_unsubscribe` with the full snapshot, live
`roster_update` pushes keyed by the TS roster `agentId` = session id, and
authenticated `worker_roster_delta` self-reports so live status reaches
subscribers without polling) plus the live-roster ledger seed (TS
`seedRosterLedger`, `supervisor_roster.rs`: passivated ledger descendants
of resident workers seed into the live roster - roots are the resident
session files, descent is membership at any parent-walk step, and
present rows by agent id or session file are never overwritten - at
subscribe, spawn-admission, and worker-stop moments, so subscribers see
the full family, not just resident rows), cloud sandbox attach, session
leases (`core/session-lease.ts` port). Daemon-owned RLM spawn ledger (`rlm_ledger.rs`):
one append-only JSONL per sessions dir (spawn/rename/delete admissions with
the TS `rlm-ledger.ts` record grammar, bounds, stat-guarded replay, and
legacy-registry seeding) plus the per-child display files; family topology
is read from the ledger, never re-derived from session files. Passive-RLM
roster walk (`rlm_roster.rs`): `list --all` and the saved-session catalog
merge non-resident ledger children (walk roots = saved + resident session
files), so passivated children stay roster-visible (TS
`walkPassiveRlmSubagents` / `withPassiveRlmDescendantInfos`). The saved
session scan (`session_store.rs`) folds each file once into the durable
catalog row.
Supervisor-backed RLM child sessions
(`rlm_children.rs`, the daemon side of the pa-core `RlmSubagentHost` seam):
`rlm.spawn`/`rlm.create_session` create real daemon sessions through the
worker's supervisor link - one supervised worker process per child - and the
parent-side registry serves `rlm.list_subagents`/`rlm.collect`/
`rlm.delete_subagent` with TS-parity selector errors; child model resolution
and thinking-level validation live in `rlm_child_model.rs`; the create
command carries the RLM recursion identity (`rlmDepth`/`rlmMaxDepth`/
`parentSessionPath`/`thinking`) so respawned children keep their depth. Per-session model binding: the
create-config `provider`/`model`/`apiKey` are authoritative for worker model
resolution (explicit CLI flags reach the worker; env remains the no-flag
fallback). Worker session files carry the TS creation prefix
(`model_change`/`thinking_level_change`/`service_tier_change`), and queue
snapshots persist to the worker recovery journal, not the session file.
Queue-lane command surface (`queue_commands.rs`): the full TS
`DAEMON_COMMAND_TYPES` accept list with exhaustive router tables in
`protocol.rs`, plus the worker's `mutate_queued_message`/`resume_queue` arms
(`AgentSession.mutateQueuedMessage`/`resumeQueuedWork`: preview-addressed
delete/move/replace over the two lanes with the TS status vocabulary, and
the empty-queue resume refusal). The `sessionActions` queue projection
(`SessionActionSnapshot`) carries one Rust-native typed rider the TS wire
has no counterpart for: `rlmChildStatus`, the parked RLM child status
notices' lane indices, derived per item from the injected custom row (the
`rlm_child_terminal_notice`/`rlm_child_failure` kinds) at projection time —
so journal recovery re-derives it, the lane strings stay the TS
`queuedAgentMessagePreview` projection verbatim, and the condensed queue
strip folds exactly these rows (a user-typed lookalike never flags); the
rider serializes only when a notice is parked. The reserved kinds are
daemon provenance, never client data (`child_status_notices.rs`): the
prompt/steer/follow-up parse and the `restore_actions` validation answer a
caller-supplied custom row claiming one loudly — it never parks, never
folds — and the daemon's own notice injection rides the follow-up route
with a one-shot capability minted in the worker process (the wire's
optional `rlmNoticeNonce`), so the queue's classification only ever sees
daemon-authentic rows.
Prompt attachments: the `prompt`/`steer`/`follow_up` wire `images` array
(base64 payload + mime type) rides the queue item into the session engine
as multimodal user content (images on a queued prompt do not survive a
worker respawn - the recovery journal keeps the text lanes only, the TS
command-recovery shape).
Eager turn abort (TS `requestAbort`'s closing `agent.abort()`): every
worker abort surface — `abort`/`abort_and_clear_queue`, the compaction and
branch-navigation interrupt-and-settle waits, shutdown, kill, and the
`cancel_prompt_admission` cancel-owned arm — funnels through
`SessionEngine::abort_in_flight_turn` (the engine's build-time agent
mirror), so an abort landing mid-provider-wait cancels the in-flight fetch
immediately and the aborted turn settles on its zero-usage aborted message
(the usage-accounting parity of the #238 adjacent gap 3 fix; the transport
half is pa-core's provider-adapter cancellation token).
Queued-input suspension (TS `_sessionInputPumpSuspended`, the #227/#233
ruling): `abort`/`abort_and_clear_queue`/manual
`compact` suspend queued-input admission indefinitely; while suspended a
plain `prompt`/`prompt_and_wait` is rejected with the TS admission error
and the lanes park. Resume sites: a prompt carrying `streamingBehavior`,
`steer`/`follow_up`, `resume_queue` (even on the empty queue), an applied
`mutate_queued_message`, a cron/heartbeat fire, and a successful compact
with an active goal. Agent-message delivery is rejected while suspended
and idle (TS `acceptAgentMessagePrompt` runs with `resumeIfIdle: false`).
The compact-with-active-goal resume is also the TS post-compaction
continue (agent-session.ts `compact()`'s `didCompact` branch): with no
queued work parked, the worker mints the owed goal continuation through
the engine (`SessionEngine::mint_post_compaction_goal_continuation`, the
`_maybeResumeGoalContinuationAfterRlmWork` mint — the follow-up lane item
with the durable goal-context row), emits the mint's `goal_update`, then
resumes: the parked continuation crosses the suspension gate through the
resume site and the turn runner drives it (the `_schedulePostCompactionContinue`
schedule). The compact-trigger auto-refine defers behind that continuation
(TS `_compactAutoRefinePending`), servicing at its turn boundary.
Thread-goal durability (TS one-store parity): every `goal_update`
announcement mirrors the new state as a `thread_goal_state` custom row in
the worker session file (the turn emit closure; the compact mint persists
its row at the mint site, outside a turn), and the engine's session-build
adoption rehydrates the fresh goal driver from the moved branch's or the
session file's latest valid `thread_goal_state` entry
(`goal_state_persist.rs`) — status, objective, usage counters, and
continuation counts continue across a worker-recovery rebuild, and the
seeded published baseline keeps the rehydrated state from announcing
itself (TS loads at construction without emitting). Recovery branch
rebuild (TS one-store parity): the owned-session worker respawns with
`--resume <sessionFile>` (`createRpcRecoveryArgs`), so the rebuilt
session's branch is the durable history — while the Rust worker owns the
file writes and the engine keeps an in-memory manager, the engine's
session-build adoption rebuilds the branch from the worker session file
(the `pending_branch` seam's recovery twin), so a post-recovery compaction
walk (and the live loop context) reads the full durable history instead
of a fresh engine's empty branch skipping "Session is too short to
compact". Platform wall
(`platform`): per-OS endpoint naming and socket identity; the transport itself
is the shared trait in `pa_types::platform::transport`, and the private-frame
codec plus command planes are the shared wire contract in
`pa_types::daemon` (clients in pa-tui/pa-cli speak them).

Live token-stream rendering
(`streaming`): turn events forward from the hosted engine to the worker's
emit path as they arrive (one `message_update` per provider delta, the
full partial message per frame - never buffered until turn settle); the
worker coalesces them for broadcast in a single-slot coalescer with a
50ms flusher, while `message_start`/`message_end`/tool frames flush the
parked update first and go out directly, so wire order and event-sequence
order match uncoalesced streaming. The supervisor stays payload-free:
deltas ride the worker -> client session-event stream (direct-attach or
supervisor-routed).

Autonomous
continuation driving in the worker's engine: per-message usage accounting
runs in the agent-loop subscription, and the in-run continuation hook
(`autonomous_continuation.rs`) consults the pa-core `AutonomousDriver`
policy at the agent loop's natural turn end (product default: shell
quality gates in the session cwd; deterministic drivers injectable for
eval harnesses) — continuations mint as durable user rows churned
inside the one prompt wait (the TS `getContinuationMessages` shape), a
threshold compaction holds its minted continuation for the worker's
queued `followUp` admission, and gate pass/fail or limit stops surface no
row (the headless status request and the print exit contract carry
them).

Live MCP product-path verifier
(`tests/mcp_product_path_e2e.rs`): a settings-declared stdio MCP server
round-trips through a real worker session and kernel - the kernel cell's
`mcp.list_tools`/`mcp.call_tool` resolve `mcp.config` through the
session's host handlers, spawn the fixture
(`tests/fixtures/mcp_echo_server.py`), and echo back.

Update-prepare transaction (`update_prepare.rs`): the supervisor-side FSM
`Draining -> Fenced -> Snapshotted -> Prepared -> Stopping` with the
mutation-drain latch + admission gate (mutating commands refused with
"Daemon is preparing an update restart" while active; reads/attach and the
TS abort-family drain commands stay served), the hard prepare deadline
(90 s default) and the durable 45 s marker self-expiry as watchdogs — both
re-checked on a timer and on any later command — idempotent
`prepare_update_restart` on `updateId` (a different id is a typed refusal
the coordinator maps to Join), and `Aborted -> Serving` with prepared-dir
cleanup as the failure default. The prepare RPC drives accept
through the mutation drain to `Fenced`, snapshots every resident worker
(`update_snapshot`, `worker.rs`), assembles the roster (`update_roster.rs`:
sessions/workers/subagents from the ledger + displays, heartbeats as a
read-only `scheduled-jobs.json` projection), writes
`prepared/<id>/{roster,marker}.json` durably, and acks `Prepared` — all
inside the prepare budget. `commit_update_restart` consumes the
transaction (`update_stop.rs`): the acked worker `shutdown` (its handler is
the flush barrier) with the 30s + 30s budgets, abandon-on-refusal (the
supervisor resumes `Serving`, refused sessions untouched, stopped workers
relaunched — never a kill), or the update exit when all workers stopped
(descriptors survive for the new supervisor's create-or-adopt restore).
The new supervisor's boot owns the update's restore side
(`update_restore.rs`, spec §6): it consumes `PRIME_AGENT_UPDATE_ROSTER`
from its spawn env before the unconditional scratch-dir sweep (this
socket's `update-restarts/` subtree plus the TS-era legacy names — no
liveness checks, no exceptions), then restores the roster rows bottom-up
(create-or-adopt; per-session failure records, restore never fails the
boot), re-arms scheduled work (a boot scan of `scheduled-jobs.json`;
due active jobs of sessionless files are woken once, never archived), and
reports the pass over `update_restore_status` while `hello.update_resume`
carries the settle state; client attaches to a not-yet-restored roster row
queue behind the pass instead of failing.
For historical TypeScript update manifests, the restore pass recreates parents
before their children, remaps active parent IDs, and restores pending context
and action queues explicitly because those manifests predate Rust's journals.
Attaches remain gated until that replay completes.
The heartbeat-catalog change
broadcast owns its surface here too (the worker's cron-store
`on_heartbeat_change` listener emits a `heartbeats_changed` outbound frame
— TS daemon-mode's `broadcastGlobal` — and the supervisor re-broadcasts it
to every client, so any heartbeat mutation anywhere in the daemon reaches
attached UIs daemon-wide).

## Non-goals
No agent behavior inside workers beyond hosting a pa-core engine; no UI.

## Public API
Supervisor entrypoint, worker entrypoint, `mcp_login::{WorkerMcpLoginUi, wire_worker_mcp_login}` (the worker's browser+callback login behind `mcp.begin_login`; wired by the agent engine before sessions register host handlers), client connection API for pa-tui/pa-cli, `acp::daemon::{run_daemon_attached_acp_mode, DaemonAcpOptions}` (pa-cli dispatches `--mode acp` through it), `agent_messaging::LinkAgentMessageController` + `rlm_children::{SupervisorChildSessions, ParentIdentity, RlmChildIdentity}` (e2e verifiers construct the worker-side family controller and the children registry; the engine wires the same types), `agent_engine::AgentSessionEngine::dispose_kernel` (the session-end kernel teardown the worker invokes at kill/shutdown/orphan exit — the engine outlives the session, so the pa-core engine-drop teardown cannot run there), the worker's `get_mcp_connections` command (`mcp_connections.rs`: the `/mcp` view's roster from the session's MCP manager overlaid with the session kernel's per-server tool listing, plus the api-key credential rows the same response serves — Rust-native session-plane extension; the TS daemon has no counterpart). Supervision internals `pub(crate)`.


## Depends on
pa-types, pa-core (one-way).

The composition root initializes the packaged product version with
`protocol::configure_app_version` before entering daemon or worker mode.
`protocol::app_version` supplies that identity to both hello frames and update
checkpoints; library embedders fall back to the compiled version. Release
manifest restamping therefore remains consistent with CLI `--version`.

---

### Overview (`crates/pa-models/README.md`)

# pa-models

The live model catalog subsystem: fetch layer, no-cold-start chain, strict
schemas, transport pinning, and the credentialed Prime Inference catalog.
Port of the TS catalog client (branch `feat/catalog-client`:
`model-catalog-cache.ts`, `model-catalog.ts`, `model-compat-schema.ts`,
`provider-model-catalog.ts`, `bundled-model-catalog.ts`,
`prime-inference-model-catalog.ts`).

## Scope

- Fetch layer: unauthenticated GET on the two catalog URLs; 5 s timeout,
  8 MiB cap (content-length AND streaming), redirects refused, ETag
  `If-None-Match` with 304 snapshot reuse; `PI_OFFLINE` disables the network.
- No-cold-start chain: validated last-good disk cache -> build-time bundled
  snapshot -> compiled fallback (42 transport tuples + 110 offline Prime
  Inference entries).
- Strict schemas: serde `deny_unknown_fields`, version gates (models
  `schemaVersion == 1`), skip-invalid for remote model refresh, compat
  validation per `api`. Unsupported future versions keep last-good
  silently, forever.
- Transport pinning: every remote model must match a compiled
  `(provider, api, baseUrl)` tuple; catalog data never carries request
  headers.
- Prime Inference: live credentialed fetch, scope-keyed disk cache
  (HMAC-SHA256 of key over team), 401/403 clears only that scope.
- Refresh: hourly + startup + picker open + auth change; fire-and-forget;
  coalesced per source; a mid-session refresh never retargets the active
  model.

## Non-goals

- The plugins/MCP catalog serde + resolution (the plugins lane; this crate
  hands it the fetcher, the generic `CatalogCache`, and the bundled asset
  bytes).
- The build-time asset generator + release packer (the build lane).
- Session auth storage, `models.json` parsing, and registry merges
  (`pa-core`): the user's local `models.json` takes precedence over this
  catalog and is merged by its owner.
- The catalog repo's sync/export tooling (lives with the data).

## Public API

- `ModelCatalog` (chain resolution + refresh cadence),
  `RefreshTrigger`, `PrimeCredentials`
- `cache::CatalogCache<T>` (generic last-good cache; the plugins lane
  instantiates it with its own fail-closed parser),
  `cache::{RefreshOptions, PUBLIC_SCOPE}`
- `fetch::{CatalogFetcher, FetchOutcome, FetchError, MODEL_CATALOG_URL,
  MCP_SERVICE_CATALOG_URL}`
- `schema::{parse_model_catalog, InvalidEntries, ModelCatalogV1}`
- `compat::is_model_compat`
- `pinning::{parse_provider_model_catalog, pin_catalog_models, PinnedTemplates}`
- `transports::{compiled_models, compiled_transport_tuples,
  prime_inference_offline_entries}`
- `bundled::{BundledAssets, load_bundled_models, PACKAGED_MODEL_CATALOG_FILE,
  PACKAGED_MCP_CATALOG_FILE}`
- `prime_inference::{PrimeInferenceCatalog, PrimeInferenceCredentials,
  build_prime_inference_models, merge_prime_inference_models,
  parse_prime_inference_model_catalog, is_private_prime_inference_model_id,
  scope_key, PRIME_INFERENCE_BASE_URL}`
- `offline::is_catalog_offline`

## Dependencies (direction compliance)

Depends on `pa-ai` (compiled transport templates: `models_generated`) and
`pa-types` (shared `Model`). Nothing else in the workspace; no crate below
`pa-ai` is touched. Consumers live at or above `pa-agent` (registry wiring
in `pa-core`). The plugins lane must not place its catalog code in `pa-ai`
(pa-models already depends on it).

---

### Overview (`crates/pa-perf/README.md`)

# pa-perf

The prime-agent performance tooling: a deterministic corpus generator over
the product's real on-disk layout, and a pty bench harness that drives the
REAL release binary (`target/release/prime-agent`) headlessly. A dev/CI
crate at the top of the workspace dependency graph — nothing depends on it,
and it never ships (the release lanes build `-p pa-cli --bin prime-agent`
only).

## Scope and non-goals

- Scope: faking realistic agent-dir state at scale (sessions, subagent
  trees, artifacts, the spawn ledger, the cwd project tree), and measuring
  the interactive surfaces (start, agents view, open/switch session,
  @ completion) through the real binary.
- Non-goals: benchmarks of provider/LLM paths (the harness stays offline),
  and any product-code changes. The new `vt100` screen-model dependency
  is described below; clap and nix were already in the tree.

## The corpus generator

```
pa-perf corpus --profile medium --out DIR [--seed N] [knob overrides]
```

`DIR` must be absent or empty. A populated output is rejected before any writes,
so stale sessions cannot enter the new corpus or its restore baseline. Choose a
fresh directory for another standalone generation. The suite command can be run
again in the same output: it replaces only its own corpus directories after
verifying their manifests, and refuses unrelated populated directories.

Writes the product's real layout under `DIR/`:

```
DIR/
  corpus.json                      # manifest: counts, needle markers, the file-set baseline
  project/                         # the sessions' cwd + @-completion tree
  agent/                           # point PRIME_AGENT_CODING_AGENT_DIR here
    settings.json                  # onboarding pre-answered, traces off
    sessions/<uuid>.jsonl          # current-format session files (flat)
    sessions-archive/<uuid>.jsonl
    rlm-ledger/<hash16>.jsonl      # written through pa-daemon's RlmSpawnLedger
    session-artifacts/...          # kernel-state stubs, harness/, sub-<id>/ trees
```

Row payloads are serialized from the product's own types (pa-types message
shapes inside pa-daemon's `SessionEntry` envelope and `session_header_line`
header) — no hand-copied JSON. Entry ids are counter-based unique 8-hex: the
product's own ids are uuid4 prefixes with a 100-try collision check, which
collide around ~22K rows and silently break branch reconstruction.

### Knobs

`--profile small|medium|large|xl` picks the defaults; every knob overrides:

| flag | meaning | small | medium | large | xl |
|---|---|---|---|---|---|
| `--sessions` | top-level sessions | 10 | 100 | 1,000 | 2,500 |
| `--rows` | rows per session | 100 | 2,000 | 10,000 | 400 |
| `--huge-rows` | rows of the huge marker session | 2,000 | 4,000 | 20,000 | 2,000 |
| `--child-rows` | rows per subagent child | 40 | 100 | 150 | 80 |
| `--fanout` | children per parent per level | 2 | 3 | 3 | 2 |
| `--depth` | subagent tree depth | 2 | 2 | 3 | 2 |
| `--tree-parents` | sessions growing a tree | 2 | 5 | 8 | 10 |
| `--wide-children` | one parent's direct children (the operator's ~110-wide real parent) | 0 | 0 | 110 | 0 |
| `--archive` | sessions in sessions-archive/ | 3 | 20 | 200 | 500 |
| `--cwd-files` | files in the project tree | 200 | 2,000 | 4,000 | 2,000 |
| `--artifacts` | per-session artifact partitions (on/off) | on | on | on | on |
| `--cache` | cold/warm (see below) | cold | cold | cold | cold |
| `--seed` | the deterministic seed | 7 | 7 | 7 | 7 |

Total rows: small ~3.5K, medium ~250K, large ~10.1M, xl ~1.2M (medium is
~210 MB on disk; large ~3.5 GB; xl ~1 GB, generated in ~5 s).

### Determinism

Same seed + same canonical path => a byte-identical tree, file mtimes
included (roster order is mtime-desc). Two stamps are the product writer's
own wall clock and are excluded from that contract: the ledger records'
`at` fields, and the corpus-root path embedded in child headers/ledger
records (`parentSession`/`child` are canonical paths).

### Caches (cold vs warm)

- `--cache warm` runs the product's own `pa_core::session::window::WindowedSessionStore::open`
  over every openable session file, writing real `<id>.window-cache.json`
  sidecars. On a real store warm window-cache sidecars are the common state
  (the operator's live store: 71 of 111 sessions carry one).
- The `<id>.info-cache.json` sidecar is written only at worker lease
  release (pa-daemon, no public writer), so generated corpora carry none —
  matching real stores (9 of 111). The `agents-view-warm` scenario measures
  the warm scan via the daemon's in-process cache instead.
- `pa-perf run` generates BOTH corpora (cold for the cold scenarios, warm
  for warm-start/warm-ready) — every scenario runs on the state it needs.

## The bench harness

```
# the whole suite (CI + make perf): generates the corpora, routes every
# scenario to the corpus state it needs, writes one results/<scenario>.json
pa-perf run --binary target/release/prime-agent --profile medium --out DIR [--runs N]

# one scenario over one corpus (investigation)
pa-perf bench --binary target/release/prime-agent --corpus DIR --scenario S --runs 10 --json OUT
```

Scenarios (timed from a triggering action to an observable needle — literal
paints as byte needles, cell-diffed content through a vt100 screen model;
the liveness deadline only fails broken runs):

| scenario | corpus | trigger -> needle |
|---|---|---|
| `cold-start` | cold | exec -> the chat first paint (the splash paints before any daemon work — the product's own paint contract) |
| `cold-ready` | cold | the same launch, exec -> the tray's session-usage pill, the first attach-only content (daemon spawn + session create + attach included) |
| `warm-start` | warm | exec -> first paint, daemon pre-started |
| `warm-ready` | warm | the same launch, exec -> the attach pill |
| `agents-view` | cold | left-arrow -> the settled first catalog page (`Inactive (N)` plus the `M older sessions not loaded` footer account for every session) in the screen model |
| `agents-view-warm` | cold | the same daemon's second populate (its in-process scan cache warm) |
| `open-session` | cold | `--resume <huge session>` -> the huge session's tail marker (the final user row; its reply is one line) |
| `session-switch` | cold | enter on the uniquely-filtered switch-b row -> B's transcript marker (the final user row; its reply is one line) |
| `at-completion` | cold | the `@` keystroke -> the first completion menu row |

Mechanics (the repo's pty-e2e discipline): the TUI child runs in its own
session with NO controlling tty (`setsid` pre-exec; vendored crossterm
falls back to the stdio pty), 120x36, kitty probe answered identically every
run, `PRIME_AGENT_CODING_AGENT_DIR` + `--daemon-socket` isolate it, and the
env is rebuilt from scratch (no ambient credentials leak in). Between runs
the harness restores the corpus to the manifest's file set (the product's
new session files, sidecars, logs, and leases are deleted) so every run
measures identical state. A discarded warm-up run precedes the samples.

Teardown signals only the harness's own child pids; the daemon stops by the
shutdown protocol and the run FAILS if it keeps accepting — never a
pid/name-based kill.

## report + baseline

```
pa-perf report --baseline crates/pa-perf/baseline.json --results results/*.json [--markdown OUT]
```

Prints the aggregate table (scenario/profile, median, p95, baseline, delta)
and exits nonzero ONLY when a non-report-only median exceeds twice the
baseline. Noisy scenarios (>20% spread across 3 CI runs) are marked
`report_only` in the baseline: their deltas always print, the gate never
fails. `baseline.json` is committed from CI numbers, not laptop/sandbox
ones.

## Local run

`make perf` builds the release binaries and runs the whole suite
(`pa-perf run --profile small`) with the report table printed at the end.
Run it on a quiet machine; the numbers are wall-clock latencies.

## Dependencies

pa-perf adds ONE new third-party dependency: `vt100` (MIT), the screen
model behind the content needles (the agents view's heading counts repaint
as terminal cell diffs, which the raw byte stream cannot see). The crate
never ships — the release lanes build `-p pa-cli --bin prime-agent` only —
and `make deny` stays clean.

## CI

The bench matrix runs all four profiles at 10 runs each; the xl
(2500-session) agents-view populate measures ~2.7 s on an 8-core sandbox.

`.github/workflows/perf.yml`: nightly schedule + workflow_dispatch +
label-gated pull requests (`perf-test` label, [labeled, synchronize] —
labeling is the explicit opt-in). One release build, a bench matrix over
the four profiles, and a report job writing the `$GITHUB_STEP_SUMMARY`
table with the >2x gate.

---

### Overview (`crates/pa-telemetry/README.md`)

# pa-telemetry

Modular telemetry: the event schema, queueing/batching, sinks, and the
pseudonymous installation identity for Prime Agent product analytics.

## Scope

- `Properties`: primitive-only property maps (the privacy boundary — strings,
  numbers, booleans, null; structured values are rejected at insertion).
- `TelemetryEvent`: `{id, name, timestamp, properties}` records.
- `TelemetryClient`: non-blocking `track(event, properties)`, `flush`,
  `shutdown`. One background worker, capped queue (drop-oldest), batched
  flush (size + interval + explicit), fan-out to every sink. Best-effort by
  contract: never blocks, never panics, never fails the agent.
- `TelemetrySink` trait + shipped sinks: `AnalyticsSink` (the one product
  destination: the TS endpoint `ANALYTICS_ENDPOINT` and wire format
  `{installation_id, events: [{id, name, timestamp, properties}]}`, no
  credentials, 1.5s timeout; the platform backend forwards to PostHog),
  `FileSink` (local JSONL transparency mirror at `<agentDir>/telemetry.jsonl`),
  `NoopSink` (opt-out fast path), `MockSink` (tests, also re-exported for
  downstream crate tests).
- `install_id`: `<agentDir>/telemetry.json` `{version, installationId}`,
  exclusive 0600 create, validated on load (TS parity).
- `rename_onto`: the shared rename-onto-destination primitive for durable
  persist writes (TS `renameOntoSync`: bounded win32 destination-busy
  retry, `10ms * attempt` backoff, every failure immediate off Windows).
  Lives in this crate because every persist owner (pa-core's settings and
  session writes, pa-daemon's descriptors/journals/session store, this
  crate's install id) already depends on it while it depends on no other
  workspace crate; pa-core re-exports it as `platform::rename_onto` so its
  platform wall stays the engine's single platform entry.
- The TS boolean env parsing (`parse_bool_override`); the opt-out
  precedence (`PI_OFFLINE`, `DO_NOT_TRACK`, `PRIME_AGENT_TELEMETRY`,
  settings) lives in pa-core's `telemetry_switch`.

## Non-goals

- No settings reading: the enabled/disclosure flow is owned by pa-core
  settings + pa-cli wiring (this crate only answers "does the environment
  override?").
- No event emission from this crate: seams live in pa-core/pa-daemon/pa-cli,
  which call `track` with schema-documented events.
- No prompt/session content, tool arguments, file paths, or user identity —
  by construction (primitives-only properties).
- No retries or delivery guarantees: batches are best-effort and offline-safe
  (dropped on failure), matching the TS product.

## Public API

- `TelemetryClient` / `TelemetryClientConfig` (`track`, `flush`, `shutdown`,
  `dropped_count`, `install_id`, `spawn`)
- `TelemetrySink` trait, `SinkOutcome`
- `Properties`, `TelemetryEvent`
- `install_id(agent_dir)`
- `rename_onto(from, to)`
- `parse_bool_override(value)`, `existing_install_id(agent_dir)`
- Sinks: `AnalyticsSink` (+ `ANALYTICS_ENDPOINT`), `FileSink`, `NoopSink`, `MockSink`
  (+ `RecordedBatch`)
- `AnalyticsSink::new(endpoint)` and `with_timeout(endpoint, duration)` retain
  their default HTTP clients. `with_http_builder(endpoint, builder)` lets the
  caller supply its trust configuration while retaining the product timeout;
  pa-core composition supplies the shared extra-CA builder without adding a
  workspace dependency to this leaf crate.

## Placement

Depends on no workspace crate (sits beside pa-types at the bottom of the
dependency graph). Consumers: pa-core, pa-daemon, pa-cli.

---

### Overview (`crates/pa-tui/README.md`)

# pa-tui

The terminal UI.

## Scope
Rendering (markdown, themes, layout, tool panels, custom-message decorated rows: the agent-message summary rows render the viewer-relative notice (the shared `Agent message` label, the ↑ sent/queued and ↓ received arrows from the row's actual direction, the counterpart agent's name only, no collapsed body preview — the operator's 2026-09-25 directive), and the ipython cell renders the sent/queued receipt rows below the code, and the RLM child rows render the outcome diamond rows — accent diamond, semantic label colors, the failure error and the cancellation reason as the expandable body; the skill-invocation card — TS `SkillInvocationMessageComponent`: a user message whose text is one `<skill ...>` block renders the `[skill] <name>` card collapsed, the `**<name>**` + content markdown expanded over the shared `pa_types::skill_blocks` parse, and the trailing invocation args as their own user block), the working loader row (activity label, TS `formatWorkingElapsed`, and the tool-owned working note during python-kernel boot, sourced from `starting` tool partials), image support (the terminal-image library: capability detection, Kitty/iTerm2 protocol encoders, dimension parsing; the image component with the fullscreen compose fallback; `terminal.showImages` gating of tool-result image rows; clipboard image paste behind `app.clipboard.pasteImage` with `[image #N]` editor markers, byte-budgeted registry, and wire-image attach), editor (cursor/kill-ring/undo/history/word ops, with the live prompt highlight: argument tokens in their TS colors on every line and the first line's slash-command token in accent for any recognized builtin command (sanctioned divergence, operator ruling 2026-09-29: TS accents argument-taking commands only) — suppressed while the cursor sits inside it, TS `CustomEditor.styleDisplayText`), the transcript prompt-highlight surfaces (TS `PromptTokenMask`: the user-message block masks its accent command segment and argument tokens to same-width private-use placeholders before the markdown layout and restores them after, TS `UserMessageComponent`; the session-command echo row styles the typed command the TS `SlashCommandMessageComponent` way — accent command segment, colored tokens, styled wrap), and the suspend-to-background cycle (`app.suspend`, default ctrl+z, TS `handleCtrlZ`: SIGINT ignored for the stopped window, the TUI stopped with the terminal handed to the shell, the process group SIGTSTP'd; the SIGCONT resume re-applies raw mode, the alternate screen, and SGR mouse tracking — the terminal modes a suspend cycle can lose), keybindings (user-editable via `~/.prime/agent/keybindings.json`: TS parse/migration/merge semantics — legacy id migration on load and at startup, malformed values dropped, empty-array disables, same-scope default eviction, conflicts, case/order-insensitive matching — the effective set wired into every hint, handler, and picker), autocomplete (slash commands, path completion, and the `@` fuzzy file search: a parallel `ignore` walk with fd's semantics, off the UI thread), fullscreen/scrollback (the wheel-scroll surface: SGR mouse-report decoding, `?1002`+`?1006` tracking enabled blind at the fullscreen surface's entry and released on exit/suspend — tmux never answers DECRQM, unsupporting terminals ignore the mode-sets — wheel turns scroll the transcript three lines, consumed without scrolling while a picker owns the frame or when the `terminal.fullscreenMouse` setting disabled tracking; in-frame click/drag selection stays unported), input handling, and the double-Ctrl+C force-quit guard (the exit contract: two in-window Ctrl+C presses end the process within 1.5s of the second press regardless of daemon or loop state; cleanup inside that window is best-effort). Interactive sessions attached through the daemon: the prompt stash (TS `prompt-stash-state.ts`: the client-owned per-session draft store shared across a TUI process's chat views — a draft left behind on a session switch returns to the editor when the session's chat reopens, its pasted images rehydrated behind their markers; the capture on the agents-view handoffs (`/resume`, the rebound `app.session.resume` key, the scoped view, `/resume <selector>`) and the in-place `/switch`, the restore on reopen/switch-back, and the queued-stash semantics for multi-switch chains; the manual `app.prompt.stash` capture (default ctrl+s, TS `handlePromptStash`): the key stashes the live draft and clears the editor, restores it on the next press, and a manually stashed draft returns on that key or after the next sent prompt is admitted — never on a reopen or a switch landing), the JSONL client socket (hello handshake, command envelopes, session-event loop), slim-attach snapshot reconstruction, the direct worker link loss path (a killed/crashed worker tears the link; the UI re-attaches through the supervisor with the TS reconnect surface - the `Daemon connection lost; reconnecting…` warning, the resync, `Daemon reconnected`, the 60s TS window with TS backoff, and the terminal `⚠ Error: Daemon reconnection failed: <last>` arm with dispatch blocked so the UI never rides a silent spinner), prompt submission with streamed assistant output, live session list and switch, the inline pickers (`/model`: the TS inline menu-panel - the bordered `Search models` field, `>`-marker rows with the effort-square cluster and `current · require sign in · provider` trailing, the price detail block, and the model/effort key hint - over the catalog the daemon's `get_model_catalog` refresh supplies (bundled + models.json fallback), with the TS sort chain, the scored fuzzy filter, left/right effort adjustment, and apply through the daemon `set_model` command (plus `set_thinking_level` for a user-edited effort) with the `Model: <id>` status row; `/effort`: the session's thinking levels from `get_state`, the unsupported-model note, and apply through `set_thinking_level`; `/mcp`: the inline connections view over the daemon's `get_mcp_connections` roster - the TS MCP-Connections-tab panel (bordered `Search MCP connections` field, `›` rows with the status flush right on connected entries, the selected connection's detail block with its tool listing, the navigate/select/close hint), Enter resolving to the selected connection's login command through the interactive loop's client-command seam, plus the api-key credential rows the same response serves (the stored keys, e.g. the web-search one; Enter runs the paste-the-key prompt, a rust-only surface - the operator's `/login`-is-providers-only ruling moved the key here, TS still serves it on its `/login` MCP tab); `/heartbeats`: the inline management view (TS `HeartbeatManagerComponent`, inline-picker style per the lane directive): the session-scoped user and agent heartbeat catalog from the selector-less supervisor `heartbeats_list` — scoped like TS `scopeHeartbeatsToSession` to this session and its roster descendants — with the TS count label, `›` rows with status-colored trailing, the selection's detail line, the actions pane (pause/resume/stop through `heartbeat_manage`), the fetch/action error rows, the `heartbeats_changed` broadcast refresh through the update channel, and the tray heartbeat label `N heartbeats · M paused (Ctrl+R)`; ctrl+r (`app.heartbeats.open`) opens it from anywhere), the TS `showStatus` back-to-back rewrite (a status emitted with nothing after the previous one rewrites it in place), the session export and share commands (`/export [path]`: the path-argument parse, the `.jsonl` branch switch, the daemon-side `export_html`/`export_jsonl` requests, and the `Session exported to:` row; `/share`: the gh auth gate, the temp export, the background `gh gist create` run behind the bordered `Creating gist...` loader with Esc cancel, and the `Share URL:`/`Gist:` rows - the gist provider is the GitHub CLI exactly like TS), the visible follow-up queue (TS `queuedMessagesContainer`): prompts parked behind a running turn render as dim preview rows above the prompt dock with the TS prompt-highlight styling (accent on a leading slash command's `/name` segment, `success`/`mdLink` on `@path`/`--flag` argument tokens — TS `styleQueuedMessagePreview`) — `Steering:`/`Follow-up:` labels with the `╰─ <key> to browse and edit queued messages` hint, re-synced from `state.sessionActions` on attach and kept live by `session_action_update`; the queued internal prompts (heartbeat fires, agent messages, goal contexts, background-command notices) condense into one counted row below the human previews — "1 agent message, 1 heartbeat, and 1 other internal prompt queued": each origin's own count, singular/plural-correct, zero-count origins omitted (sanctioned divergence, operator requests 2026-09-24 + 2026-09-25: TS renders every internal prompt as its own preview row; the browse/edit affordances still walk every queued item, so only the strip rows condense — the four TS-internal labels classify by preview string, TS `isLabeledQueuedPreview`, the queue wire carries no provenance for them, so a human-typed prompt that begins with one of the internal labels classifies internal for the strip too: it still delivers, and the browse/edit affordance walks and shows it; the parked RLM child status notices — the `[child-exited: ...]`/`[child-failed ...]` lifecycle rows the queue-fold operator bug 2026-09-25 flooded the strip with — classify by WIRE-TYPED provenance instead, never by their text: the worker's `sessionActions.rlmChildStatus` rider carries the parked injected rows' lane indices, they count as their own origin ("N child status notices"), and a user-typed lookalike stays a human row); the collapsed conversation view (operator directive 2026-09-28, the undo of the 2026-09-25 condensed-run summary block): the collapse mode renders every activity item exactly as `details` does - every tool card, every notice, every agent-message row - with ONLY the thinking blocks hidden (the TS fork's shape), a chat starts at that collapsed level, and the Ctrl+O cycle from there is the thinking-visibility toggle (overview -> details -> all -> overview unchanged; a card click toggles only the clicked card's own expansion - TS's per-component `expanded` - and the Ctrl+O cycle resets every card to the new level) - purely render-time over the unchanged transcript model (no session jsonl schema change, no stored-event change); the follow-up key (`app.message.followUp`, default alt+enter) parks a submission on the follow-up lane (`streamingBehavior: "followUp"` + `queueIfBusy`), Enter on the steering lane; the browse affordances TS gives the rows (TS `QueueSelection` port): alt+up/alt+down walk the parked messages newest-first with the editor draft stashed and restored, the dim browse header (lane, index, key hints) renders as the editor box header row, Enter steers the edit (replace/move to steering, empty deletes), the follow-up key re-parks it, ctrl+alt+arrows reorder through `mutate_queued_message` (mirrored locally like TS — the event can land after the response), the clear key restores the draft, and queue-change statuses surface the TS status rows verbatim; and the utility slash commands (TS `dispatch_client_command` arms): `/btw`+`/side` (the side-question pane over the daemon `start_side_question`/`abort_side_question` flow — the popup-surface turns with the `/btw` header, follow-up bubbles, the slash/image rejection notices, the running/return hint rows, Esc cancel; replies while the pane is open seed follow-ups from the answered turns), `/fast` (the priority-tier toggle with the TS eligibility check and unavailable status), `/rlm-max-depth` (view/set with the `--global` arm and the TS status rows), `/name`+`/rename` (the daemon `set_session_name` rename whose persisted `session_info` entry and `session_info_changed` broadcast the daemon owns), `/settings` (the TS `SettingsList` grouped into tabs — the spaced tab strip (white-bold active tab) over label/value rows with descriptions, ←/→/Enter/Space value cycling (the arrows step a row's options in place; Tab/Shift+Tab and the number keys move the tabs), the detail block's separator rule and description-matched hint padding, the thinking/theme/warnings/service-tier submenus with the theme's live preview and restore, type-to-search; daemon switches for autocompact/steering/follow-up/transport/thinking, the client-settings seam for the persisted preferences), and `/reload` (the TS guards, the bordered reload box, the daemon reload request, keybinding/theme/catalog re-reads, and the outcome row); `/thinking` rides the registry alias into `/effort`. The client-settings seam (`client_settings::ClientSettings`, implemented by the composition root) is how pa-tui persists user preferences without a pa-core dependency; the autocomplete listing drops `/fast` when the current model is not fast-mode-eligible (the pa-types `supports_fast_mode` helper), the subagent summary line under the tray (TS `SubagentSummaryLine`: live descendant counts from the roster subscription, the dock renders in every session, all-zero included (the operator's 2026-09-28 directive; TS renders nothing at zero, a sanctioned divergence), and its Subagents group opens the scoped agents view even with no children, which shows the view's `No sessions yet.` empty state, the focus hand-off from the editor — Down at the end of the prompt or `app.subagents.focus` — the focused key contract — confirm/open into the focused group's own view, left/right stepping every rendered group (an empty one included, wrapping at the row's ends — the operator's 2026-09-26 muscle-memory directive), up/cancel/back to the editor, expand keeping the focus, other keys releasing it into the editor path — and the scoped-view hand-off; every dock panel's exit (ESC/left from the heartbeats view, the bash view, the goal panel, or the scoped agents view's back keys) restores the dock's focus on the panel's own group instead of the prompt bar — the operator's 2026-09-26 panel-exit ruling; TS has no multi-group dock, its panels close to the editor, a sanctioned divergence); and the agents view (the unified live-roster +saved-catalog session list: Running/Idle/Inactive sections, inline search(TS query language: case-insensitive substring tokens, `"quoted phrases"`,
`re:` regex, capped fuzzy fallback; the corpus is deliberately restricted to
the session name, the durable session id, and the cwd — the TS corpus fields
(first/last messages, the capped saved transcript, and
file paths) never match; Escape clears the query and the view restores the roster;
the query survives opening a chat and returning), open-to-attach/resume,
`n`-via-ctrl+n new session, live `roster_subscribe` pushes; the
selection is session-keyed, not index-keyed (TS
`resolveAgentsViewSelectionState`: row identity, then active-session id,
then session id re-find the same session across roster rebuilds — spawn
churn, activity re-sorts, and identical no-op re-pushes never move it;
a session that left the roster keeps the bounded position, TS parity),
and the list window follows the selection (TS `renderSessionRows`: the
slice centers on the selected row and clips the overflow behind
leading/trailing ellipses, so arrowing below the fold keeps the
selection on-screen instead of the view snapping back to the top of the
list; rows carry their session's `◷ N` heartbeat count from the
`heartbeats_list` catalog, refreshed on `heartbeats_changed`), ctrl+o's spawn-program surface (`app.agents.program`: the selected row's target — the row itself for a top-level agent, its parent otherwise — expands its list and shows the program rows, one code block per spawn cell above the children it launched, capped at 10 lines with the remainder marker, on the tool-panel background, skipped by the selection and the click surface, a second press hides it, and collapsing the list clears it), ctrl+r's rename mode (`app.agents.rename`: the prompt becomes the "Rename agent session" header over the name buffer — the search grammar, Enter submits the trimmed name through the daemon `rename`/`rename_saved_session`, Esc cancels back to the untouched query — a saved rename patches the catalog row in place), and the `tui agents action` adoption events (`program_shown`, `renamed`) that ride the view outcome out of the run.

## Non-goals
No session logic, no providers, no loop policy. The interactive UI renders daemon events and sends user intents (prompts, abort, switch) as daemon commands; the session loop itself lives in the pa-daemon worker. It never computes agent behavior and never spawns the supervisor (launch semantics live in pa-cli).

The client commands of the clipboard/auth/update group: `/copy` (the last assistant text through the platform clipboard tool chain with the OSC 52 fallback — `clipboard.rs` + `osc52.rs`, the same emitter the selection-copy work can reuse; the daemon `get_last_assistant_text` supplies the text), `/import` (the path argument parse, the TS confirm, the typed error surfaces — the missing file, the missing session cwd with its fallback-cwd retry — and the post-import transcript rebuild through the daemon's `import_jsonl`), `/login` + `/logout` (the TS providers selector — `provider_auth.rs`, the panel `showConfigurationMenu("providers")` mounts: the tab bar, the search, the status indicators, the in-panel API-key prompt — over the composition root's auth flows), `/traces` (the TS status block and settings writes; the upload subsystem stays unported, its arms keep the TS state shapes and report the unavailability), and `/update` (the TS busy guard, the split-CLI child mapping — packages then the binary — and the self-update relaunch through the composition root's hook).

## Public API
`custom_message::{custom_message_entries, AGENT_MESSAGE_CUSTOM_TYPE, skill_invocation::{SkillInvocationRow, skill_invocation_entries}, ...}` (the custom-type render dispatch shared by the live and replay paths; the user-message skill-invocation card decode; component row renderers are `pub(crate)`), `app::run_app` (replay), `interactive::{run_interactive, InteractiveOptions, ModelSelection, SessionSelection, UiMode, HeadlessPlan, InteractiveOutcome}` (daemon-attached), `daemon_client::{DaemonClient, DaemonClientEvent}` (wire client for the UI and the composition root), `session::SessionStream`, `agents_view::{run_agents_view, AgentsViewOptions, AgentsViewUiMode, AgentsViewOutcome}` (the view and its headless verifier seam), `interactive::{OnboardingTask, OnboardingSink}` (first-run flow surface; the composition root implements the sink against the settings manager), `keybindings::{KeybindingsManager, migrate_keybindings_file, KEYBINDING_NAME_MIGRATIONS, format_key_text}` (the effective-binding surface: the composition root loads the user config and passes the manager in; `migrate_keybindings_file` is the startup rewrite), `queued::{QueuedMessages, QueueSelection, QueueSelectionItem, QueueLane, QueueBrowseKeys, render_queue, browse_header_text, mirror_lane_move}` (the strip and browse state; the session UI owns the selection), `prompt_stash::PromptStashStore` (the per-process draft store the composition root owns; entry semantics are crate-internal), `client_settings::ClientSettings` (the client-process settings seam the interactive commands persist through — the composition root implements it over the real store), `client_auth::{ClientAuthCommands, ClientAuthCommandsHandle, run_mcp_auth_command}` (the `/mcp login`/`/mcp logout` client surface: the TUI owns the command UX and mounts the inline auth panel — TS usage wording — while the composition root runs the login against it). `mcp_view::{McpView, McpViewAction, McpConnection, McpToolInfo}` (the `/mcp` inline connections view: the daemon `get_mcp_connections` roster rendered in the TS MCP-Connections-tab panel shape, with the per-connection status and tool detail; Enter resolves to the selected connection's login command, dispatched through the interactive loop's client-command seam). `traces::{TracesCommands, TracesCommandsHandle, traces_base_url}` (the `/traces` settings + credential seam), `provider_auth::{ProviderAuthCommands, ProviderAuthCommandsHandle, ProviderRow, AuthType, AuthCategory, AuthFlow, AuthStatusIndicator, AuthStatusStyle, AuthSelectorKind, PRIME_INFERENCE_PROVIDER_ID}` (the `/login`+`/logout` provider-auth seam: rows, status indicators, and the flow hook), `auth_panel::{AuthPanel, AuthPanelHandle, AuthPanelRequest, PrimeTeamOption, PrimeTeamPick, PasteStyle}` (the inline auth panel the login flows render through — TS `LoginDialogComponent` + `PrimeTeamSelectorComponent`: progress lines, the browser URL block, the paste prompts, and the Prime team picker, all over the run loop's request/reply channel), `update_command::{UpdateCommands, UpdateCommandsHandle, UpdateOutcome, UpdateRunFuture}` (the `/update` out-of-band runner seam: the TUI confirms, then spawns the composition root's funnel task and folds the landed outcome row), `confirm::{ConfirmPanel, ConfirmAction}` (the Yes/No confirm panel), and `InteractiveOutcome::clipboard_emissions` (the headless OSC 52 capture the verifiers assert with). Component internals `pub(crate)`. `altscreen::{enter, leave}` is the process-wide alternate-screen ownership shared by the terminal surfaces: the first surface enters the screen, a real exit leaves it, and in-process view switches adopt the same buffer (no primary-screen flash between the agents view and a chat).

## Depends on
pa-types only (the daemon wire protocol types live there). The headless `UiMode` is the verifier seam: it drives the identical attach/submit/stream/render path without a TTY and captures rendered frames.

## Transcript caching

Fullscreen transcript frames render the visible entry window through a persistent entry cursor. Per-entry, per-detail line caches retain visited rows; unseen entries stay unrendered. Selection highlighting restyles only the rows the selection change touched (cached rows, never a rebuild from raw entries), and appends, entry growth, and tail re-anchors fold into the sparse window's bookkeeping instead of a geometry pass. Absolute-row operations, including paused detail changes, use separately cached count-only geometry and paint only the requested window. Cold geometry still scans uncached content; the exceptional copy fallbacks walk only the selected range from the viewport cursor. The frame's end sections — the splash and the streaming tail (the shortcut guide, pending bash output, the loaders) — render only when the visible window reaches them, and frames without a selection skip the restyle pass (its styled rows are the cached window verbatim). These caches and viewport coordinates are private to this crate; wire types and dependency direction are unchanged.

---

### Overview (`crates/pa-types/README.md`)

# pa-types

The single shared vocabulary crate. Nothing else is shared between crates.

## Scope

`compaction::is_valid_threshold_percent` is the shared percentage-domain validator
used by settings deserialization, persistence, runtime policy, and the TUI.
Wire and domain types + serde only: AI messages/content blocks/tool calls/usage/stream events, session JSONL entry schema, daemon wire protocol messages, worker frames.

Daemon wire mechanics shared by the serving side (pa-daemon) and clients (pa-tui/pa-cli) live here because pa-tui depends on pa-types alone:
- `daemon::framing`: the private-frame codec of the worker socket (direct-attach clients speak it too).
- `daemon::plane`: the session/control command-plane table (worker-side peer gating and client-side socket routing both read it).
- `daemon::{DaemonPeerTransportTicket, DaemonWorkerPeerGrant, DaemonPeerCommand}`: the direct-transport ticket and grant wire shapes.
- `goal`: the thread-goal wire state (`GoalState`/`GoalStatus`) shared by the
  goal engine (pa-core), the daemon wire (`goal_update` session events and
  the attach snapshot's `state.goal`), and the TUI (announcement rows, tray
  label). The goal *engine* (validation, accounting, continuation prompts)
  is pa-core's.
- `slash_commands`: the builtin slash-command table every surface shares
  (the TUI dispatch + autocomplete, the session engine's command admission,
  CLI suggestion help) plus its pure parse/suggestion helpers — the TS
  product keeps the same single table in core and imports it from its TUI.
- `incident`: the daemon incident classifier shared by the incident CLI
  (pa-cli's `prime-agent incident`, TS `src/cli/incident.ts`) and the
  agents-view incident notice (pa-tui's `incident_notices`, TS
  `src/modes/agents-view/incident-notices.ts`) — pa-tui depends on
  pa-types alone, and the TS product keeps the same single classifier in
  `cli/incident.ts` with both halves importing it. Pure log-line parsing
  (`agent.jsonl` records, per-daemon plain-text lines), worker pid
  attribution, event classification, and the stall/burst/gap anomaly
  computation — the message shapes are the TS regexes kept 1:1. The CLI's
  log-file discovery, `--since`/`--until` window parsing, and report
  rendering belong to pa-cli; the notice polling, rotation-safe
  incremental reads, and dismissal horizons belong to pa-tui.
- `themes`: the bundled theme definition files (`prime`/`dark`/`light`) as
  pure data, shared by the TUI's theme loader (pa-tui renders terminal
  colors from them) and the session HTML exporter (pa-core resolves them
  into CSS variables). Everything built on top of the files — terminal
  color rendering, export CSS generation, custom-theme discovery — belongs
  to the consuming crates.

- `daemon::update_flow`: the update-flow state machine's shared vocabulary:
  the coordinator FSM states + legal
  transition table, `UpdateId`, the on-disk artifact schemas (`intent.json`,
  `status.json` — TS status-file shape, `prepared/<id>/{roster,marker}.json`),
  the roster-snapshot projection (sessions/workers/subagents/heartbeats,
  durable session ids, heartbeat re-arm fields only), the timeout-budget
  table with `PRIME_AGENT_UPDATE_*_MS` overrides, and the artifact path
  layout under `<agent-dir>/update-restarts/`. Pure serde + pure data
  helpers; the FSM drivers and watchdogs are owned by pa-daemon/pa-cli. The
  prepare/commit wire commands (`prepare_update_restart`/`
  commit_update_restart`) and the TS mutation classification
  (`is_daemon_mutating_command`, `is_update_drain_command` — the
  update-flow admission gate's read tables) live in `daemon::{command,plane}`.
Platform contracts (`platform`): the cross-crate transport, process-identity, socket-identity, and home-dir helpers, plus the suspend-to-background signal control (`process::{stop_own_process_group, ignore_sigint_for_suspend, restore_default_sigint}`: the TUI's TS-`handleCtrlZ` cycle — SIGTSTP to the own process group, SIGINT ignored for the stopped window and restored on the SIGCONT resume; unsafe lives behind this wall because pa-tui opts into the workspace `unsafe_code` forbid) (`platform::dirs::home_dir`: `HOME`, then on Windows `USERPROFILE` / `HOMEDRIVE`+`HOMEPATH` - Node `os.homedir()` parity, returning `None` so each caller owns its fallback). pa-types is the only crate every transport consumer can depend on (pa-tui depends on pa-types alone), so the shared trait vocabulary and its cfg-gated platform implementations live here: AF_UNIX on Unix, named pipes (`\.\pipe\`, `platform/windows_pipe.rs`) on Windows. Remaining platform areas (process control, perms, ...) are implemented per platform behind the same traits, not re-plumbed in callers.

## Non-goals
No provider logic, no session logic, no UI. Beyond pure data helpers and the platform contracts, no behavior: a domain type that wants a method belongs in the owning crate.

## Public API
Everything in this crate is deliberately `pub` - it is the cross-crate contract. Unknown fields survive round-trips via catch-all maps so schema revisions stay compatible.

## Depends on
serde, serde_json, thiserror, anyhow, tokio, regex (the incident classifier's TS log-message patterns; see the `incident` scope entry). No workspace crates.

---

### Overview (`scripts/release/README.md`)

# Release publication

Eligible main commits still produce unique beta versions and tags through
`nightly.yml`. `release.yml` retains the five-platform build/reuse routes and
publishes versioned installer payloads to R2. Only stable tags create individual
GitHub releases; beta tags refresh the single prerelease named `nightly`.

For future beta versions, the public R2 prefix
`releases/v<version>/` retains the entire published payload: platform tarballs,
Linux debug decoders, the npm migration bridge, SHA256SUMS, merged manifest,
beta.json, and per-target SPDX SBOMs. Audit files upload before channel pointers
advance, even when a newer main commit has superseded the build. The existing
provenance attestation step continues to cover tarballs and decoders. The rolling
nightly release also carries the full current payload and checksums.
After a successful nightly asset publication, the same serialized refresh moves
`refs/tags/nightly` to `manifest.json`'s verified build commit and updates the
release title/notes with the version, source commit, and refresh time. It never
uses a newly read main HEAD. Failed uploads leave the existing tag unchanged;
a failed tag/metadata update fails the job and can be repaired by rerunning the
same version. GitHub's original release publication date is not rewritten.

Installers and native/npm updaters keep using the R2 channel manifests and
versioned payload paths anonymously; they do not require per-beta GitHub release
objects. Consumers of `/releases/download/v<beta>/...` must use the public R2
versioned prefix for future beta builds, or `nightly` for its current payload.
Stable GitHub asset URLs retain their existing behavior.

No historical releases or assets are removed. Versioned nightly asset names
continue to accumulate; retention and historical cleanup require a separate
decision. The R2 channel comparator repair remains in PR #3452; this change only
hardens the independent rolling GitHub newest-wins guard.

`python3 scripts/release/test_release_publication.py` executes the workflow shell
against local GitHub/R2 fakes, including stale reruns, failed reads, complete
payloads, interrupted bootstrap, and superseded beta archival. It performs no
network requests or real publication.

---

### Overview (`vendor/crossterm/README.md`)

<h1 align="center"><img width="440" src="docs/crossterm_full.png" /></h1>

[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=Z8QK6XU749JB2) ![Travis][s7] [![Latest Version][s1]][l1] [![MIT][s2]][l2] [![docs][s3]][l3] ![Lines of Code][s6] [![Join us on Discord][s5]][l5]

# Cross-platform Terminal Manipulation Library 

Crossterm is a pure-rust, terminal manipulation library that makes it possible to write cross-platform text-based interfaces (see [features](#features)). It supports all UNIX and Windows terminals down to Windows 7 (not all terminals are tested,
see [Tested Terminals](#tested-terminals) for more info).

## Table of Contents

- [Cross-platform Terminal Manipulation Library](#cross-platform-terminal-manipulation-library)
  - [Table of Contents](#table-of-contents)
  - [Features](#features)
    - [Tested Terminals](#tested-terminals)
  - [Getting Started](#getting-started)
    - [Feature Flags](#feature-flags)
    - [Dependency Justification](#dependency-justification)
    - [Other Resources](#other-resources)
  - [Used By](#used-by)
  - [Contributing](#contributing)
  - [Authors](#authors)
  - [License](#license)

## Features

- Cross-platform
- Multi-threaded (send, sync)
- Detailed documentation
- Few dependencies
- Full control over writing and flushing output buffer
- Is tty
- Cursor 
    - Move the cursor N times (up, down, left, right)
    - Move to previous / next line
    - Move to column
    - Set/get the cursor position
    - Store the cursor position and restore to it later
    - Hide/show the cursor
    - Enable/disable cursor blinking (not all terminals do support this feature)
- Styled output 
    - Foreground color (16 base colors)
    - Background color (16 base colors)
    - 256 (ANSI) color support (Windows 10 and UNIX only)
    - RGB color support (Windows 10 and UNIX only)
    - Text attributes like bold, italic, underscore, crossed, etc
- Terminal 
    - Clear (all lines, current line, from cursor down and up, until new line)
    - Scroll up, down
    - Set/get the terminal size
    - Exit current process
    - Alternate screen
    - Raw screen   
    - Set terminal title
    - Enable/disable line wrapping
- Event 
    - Input Events 
    - Mouse Events (press, release, position, button, drag)
    - Terminal Resize Events
    - Advanced modifier (SHIFT | ALT | CTRL) support for both mouse and key events and
    - futures Stream  (feature 'event-stream')
    - Poll/read API
    


### Tested Terminals

- Console Host
    - Windows 10 (Pro)
    - Windows 8.1 (N)
- Windows Terminal
    - Windows 10 x86_64 (Enterprise)
    - Windows 11 arm64 (Enterprise)
- Ubuntu Desktop Terminal
    - Ubuntu 23.04 64-bit
    - Ubuntu 17.10
    - Pop!_OS ( Ubuntu ) 20.04
- (Arch, Manjaro) KDE Konsole
- (Arch, NixOS) Kitty
- Linux Mint
- (OpenSuse) Alacritty
- (Chrome OS) Crostini
- Apple
    - macOS Monterey 12.7.1 (Intel-Chip)
    - macOS Sonama 14.4 (M1 Max, Apple Silicon-Chip)

This crate supports all UNIX terminals and Windows terminals down to Windows 7; however, not all of the
terminals have been tested. If you have used this library for a terminal other than the above list without
issues, then feel free to add it to the above list - I really would appreciate it!

## Getting Started
_see the [examples directory](examples/) and [documentation](https://docs.rs/crossterm/) for more advanced examples._

<details>
<summary>
Click to show Cargo.toml.
</summary>

```toml
[dependencies]
crossterm = "0.27"
```

</details>
<p></p>

```rust
use std::io::{stdout, Write};

use crossterm::{
    execute,
    style::{Color, Print, ResetColor, SetBackgroundColor, SetForegroundColor},
    ExecutableCommand,
    event,
};

fn main() -> std::io::Result<()> {
    // using the macro
    execute!(
        stdout(),
        SetForegroundColor(Color::Blue),
        SetBackgroundColor(Color::Red),
        Print("Styled text here."),
        ResetColor
    )?;

    // or using functions
    stdout()
        .execute(SetForegroundColor(Color::Blue))?
        .execute(SetBackgroundColor(Color::Red))?
        .execute(Print("Styled text here."))?
        .execute(ResetColor)?;
    
    Ok(())
}
```

Checkout this [list](https://docs.rs/crossterm/latest/crossterm/index.html#supported-commands) with all possible commands.

### Feature Flags

```toml
[dependencies.crossterm]
version = "0.27"
features = ["event-stream"] 
```

| Feature        | Description                                  |
|:---------------|:---------------------------------------------|
| `event-stream` | `futures::Stream` producing `Result<Event>`. |
| `serde`        | (De)serializing of events.                   |
| `events`        | Reading input/system events (enabled by default) |
| `filedescriptor` | Use raw filedescriptor for all events rather then mio dependency |


To use crossterm as a very thin layer you can disable the `events` feature or use `filedescriptor` feature. 
This can disable `mio` / `signal-hook` / `signal-hook-mio` dependencies.

### Dependency Justification

| Dependency     | Used for                                                                         | Included                              |
|:---------------|:---------------------------------------------------------------------------------|:--------------------------------------|
| `bitflags`     | `KeyModifiers`, those are differ based on input.                                 | always                                |
| `parking_lot`  | locking `RwLock`s with a timeout, const mutexes.                                 | always                                |
| `libc`         | UNIX terminal_size/raw modes/set_title and several other low level functionality. | optional (`events` feature), UNIX only |
| `Mio`          | event readiness polling, waking up poller                                        | optional (`events` feature), UNIX only |
| `signal-hook`  | signal-hook is used to handle terminal resize SIGNAL with Mio.                   |  optional (`events` feature),UNIX only |
| `winapi`       | Used for low-level windows system calls which ANSI codes can't replace           | windows only                          |
| `futures-core` | For async stream of events                                                       | only with `event-stream` feature flag |
| `serde`        | ***ser***ializing and ***de***serializing of events                              | only with `serde` feature flag        |

### Other Resources

- [API documentation](https://docs.rs/crossterm/)
- [Deprecated examples repository](https://github.com/crossterm-rs/examples)

## Used By

- [Broot](https://dystroy.org/broot/)
- [Cursive](https://github.com/gyscos/Cursive)
- [TUI](https://github.com/fdehau/tui-rs)
- [Rust-sloth](https://github.com/ecumene/rust-sloth)
- [Rusty-rain](https://github.com/cowboy8625/rusty-rain)

## Contributing
  
We highly appreciate when anyone contributes to this crate. Before you do, please,
read the [Contributing](docs/CONTRIBUTING.md) guidelines. 

## Authors

* **Timon Post** - *Project Owner & creator*

## License

This project, `crossterm` and all its sub-crates: `crossterm_screen`, `crossterm_cursor`, `crossterm_style`,
`crossterm_input`, `crossterm_terminal`, `crossterm_winapi`, `crossterm_utils` are licensed under the MIT
License - see the [LICENSE](https://github.com/crossterm-rs/crossterm/blob/master/LICENSE) file for details.

[s1]: https://img.shields.io/crates/v/crossterm.svg
[l1]: https://crates.io/crates/crossterm

[s2]: https://img.shields.io/badge/license-MIT-blue.svg
[l2]: ./LICENSE

[s3]: https://docs.rs/crossterm/badge.svg
[l3]: https://docs.rs/crossterm/

[s3]: https://docs.rs/crossterm/badge.svg
[l3]: https://docs.rs/crossterm/

[s5]: https://img.shields.io/discord/560857607196377088.svg?logo=discord
[l5]: https://discord.gg/K4nyTDB

[s6]: https://tokei.rs/b1/github/crossterm-rs/crossterm?category=code
[s7]: https://travis-ci.org/crossterm-rs/crossterm.svg?branch=master

---

### Overview (`scripts/release/native-compat/README.md`)

# Native installer compatibility assets

Shipped TypeScript native installers validate these paths before activating a
release and whenever validating an installed release for rollback. Rust ships
the real historical assets to preserve that contract. They are not loaded by
Rust's renderer or image implementation.

- `theme/prime.json` and `export-html/template.html` are unmodified from
  Prime Agent v0.9.8, commit `7d442aafa985f9342134fac16c2ef41f03fb45c1`, paths
  `packages/coding-agent/src/modes/interactive/theme/prime.json` and
  `packages/coding-agent/src/core/export-html/template.html`.
- `photon_rs_bg.wasm` and `PHOTON-LICENSE.md` are unmodified from
  `@silvia-odwyer/photon-node` 0.3.4, the dependency used by that release.
  Source: https://registry.npmjs.org/@silvia-odwyer/photon-node/-/photon-node-0.3.4.tgz
  Tarball SHA-256: `5a23015c1cd2c38e3c492dd96929985247b92f52d2ff0fb948d29edca52bc50a`.
  Wasm SHA-256: `10468181565c56004c867f3a4af96f89a0ef5a63a72f2b5fb12c1f1992a3615c`.

The assembler also supplies `package.json` for every release and copies the
maintained `install-rust.sh` to `install.sh`. The old updater keeps executing
its existing installer; the downloaded installer is not a migration hook.
The bundled installer is available for manual Rust installation/repair and
uses the Rust installer's arguments and environment contract.

Keep these wire-level file names unchanged while supporting those installers.

---

### Overview (`crates/pa-daemon/testdata/acp/README.md`)

# ACP differential corpus (slice 1)

Captures of the installed TS binary (`prime-agent --mode acp --no-session
--provider prime-inference --model z-ai/glm-5.3-flash`) speaking ACP over
stdio, one file per scenario. Every file is line-delimited JSON:

    {"scenario": ..., "direction": "in"|"out", "frame": {jsonrpc frame}}

`out` lines are what the scenario client sent; `in` lines are what the
binary answered, in order. The last line of each file is a `meta` row with
the process exit code and stderr.

## Scenarios

- `happy_path`: initialize, session/new (matching cwd), one text prompt,
  session/close. Shows the full completion envelope: chunks, then the
  `responseBoundary` info update (`terminalQuiescenceExpected: true`,
  `outcome`), the quiescence event, the `terminalQuiescence` envelope, then
  the `{stopReason}` response.
- `cwd_mismatch`: session/new with cwd `/tmp` while the process runs in
  another directory. The result carries `_meta.cwd {requested, actual}`.
- `errors`: unknown-session prompt/close, a second session/new on a live
  connection, an unknown method (`-32601` with the observed message
  shape), close. Also demonstrates the one-`initialize`-per-connection
  behavior of the served surface.
- `second_initialize`: a second `initialize` on the same connection is
  served normally.
- `cancel`: `session/cancel` mid-turn. The prompt resolves
  `{stopReason: "cancelled"}` with no boundary frames after the streamed
  chunks.
- `tool_call`: a turn that calls the ipython tool (`6*7`). Shows the
  `tool_call` / `tool_call_update` shapes including the `content` wrapper
  and the ipython rich-output `_meta` on results that carry attachments.
- `compact_command`: two turns, then `/compact`. The session is too short
  to compact, so the observable parity is the namespaced `compaction: {}`
  update (the TS `compaction_end` with an undefined result) followed by the
  normal completion envelope and `end_turn`.
- `goal_command`: `/goal <objective>` then `/goal status`. Shows the
  goal-start `_meta.goal` frame, mid-turn usage updates after each settled
  message, the completion observation, and the status-prompt frame.
- `autonomous_gate`: `--autonomous --autonomous-gate true`. The gate passes
  after the first turn: the completion and terminal envelopes carry the
  `_meta.autonomous` accounting and `remainingAutonomousContinuations`
  reflects the configured budget; the stop reason is `end_turn`.
- `autonomous_limit`: `--autonomous --autonomous-max-tokens 1`. The token
  limit stops the run: the envelopes carry the final accounting and the
  stop reason is `max_tokens`.
- `mcp_stdio`: `session/new` with two valid servers (one stdio with a
  literal environment entry, one http with a header) plus one
  schema-invalid entry (missing the required `env` array): the ACP SDK
  zod filter (`vecSkipError(zMcpServer)`) silently drops it, admission
  succeeds with the survivors, and close releases them.
- `mcp_replace`: admission, close, then a replacement admission with a
  different server list — the owner-scoped clear/re-release cycle.
- `mcp_errors`: the full resolve-stage rejection matrix, every entry
  schema-valid: bad name pattern, duplicate name, NUL in the stdio
  command, duplicate environment entry (case-sensitive), duplicate HTTP
  header (case-insensitive, the reason reports the second casing),
  unsupported sse transport, embedded URL credentials (all `-32602` with
  the exact TS `reason` payloads), then a name that passes the 64-char
  admission pattern but fails the 48-char tool-name pattern
  (`-32603` with `Invalid ACP MCP server name: ...` details), then a
  valid admission and close. Failed admissions keep the connection
  serving.

## The Rust captures in this directory

The `rust-*.jsonl` files are the evidence run for this slice: the same
scenario client against the Rust build with the same provider/model
(`z-ai/glm-5.3-flash`) and the kernel sidecar resolved through
`PI_PACKAGE_DIR` (a cargo-built binary has no sidecar next to the exe).

Since slice 4 the Rust ACP mode serves over the daemon-attached transport
(the TS `shouldUseDaemonClient` behavior): the binary spawns a supervisor
on the sandboxed `--daemon-socket`, hosts a client-owned daemon session,
and streams its session events as ACP updates. The daemon-attached
captures are: happy_path, cwd_mismatch, errors, second_initialize,
tool_call, compact_command, mcp_stdio, mcp_replace, mcp_errors — all
MATCH in full mode. `cancel`, `goal_command`, `autonomous_limit`, and
`autonomous_gate` were captured on the since-removed in-process
transport; recapture is pending (the files stay):

- `cancel`: the capture matched in full mode; the daemon-attached run
  resolves the same `{stopReason: "cancelled"}` shape with no boundary
  frames, but a fresh worker's first-token latency exceeds the
  scenario's 2s cancel window, so the pre-cancel chunk count differs
  per run. The daemon-attached cancel semantics are locked by the
  deterministic e2e (`acp_daemon_attached_cancels_mid_turn`).
- `goal_command` / `autonomous_limit` / `autonomous_gate`: the captures
  predate the daemon's `goal_update` / autonomous-accounting producers;
  recapture is pending.

## Comparing a Rust run

`compare_differential.py` normalizes both captures (volatile ids, version,
model-generated text, sequence numbers) and compares the frame shape
sequences field by field. `mcpCapabilities` is compared exactly: both
binaries advertise `{ "http": true }`:

    python3 compare_differential.py ts-happy_path.jsonl rust-happy_path.jsonl

Both captures must come from the same scenario client with the same
request order. `crates/pa-cli/tests/acp_mode_e2e.rs` locks the deterministic
scenarios offline over the daemon-attached transport (the scripted faux
worker); the network-dependent scenarios (tool_call, cancel mid-turn) are
verified by running the scenario client against both binaries on a
networked box.

`--landmarks` drops model-behavior-dependent work frames (tool calls,
chunk streams, and mid-turn goal-usage updates) and compares the protocol
envelopes only. Use it for scenarios where the two model runs legally
diverge in their tool usage (`goal_command`: one run answered directly,
the other ran several ipython cells; `autonomous_limit`: one run wrote
the essay with tool calls, the other without — both sequences are
correct protocol behavior). The goal and autonomous frames themselves are
locked field-by-field by the offline e2e tests. All other scenarios
(happy_path, cwd_mismatch, errors, second_initialize, tool_call,
compact_command, cancel, autonomous_gate, mcp_stdio, mcp_replace,
mcp_errors) match in full mode.

---

### Daily Stats (`docs/daily-stats.md`)

# Daily Prime Agent stats

The GitHub Actions workflow `Prime Agent daily stats` prepares a report at 9 AM
America/Los_Angeles, including daylight saving changes. GitHub may start scheduled
runs late. The report covers the previous completed **UTC day**, matching the
[Prime Agent Overview dashboard](https://eu.posthog.com/project/22174/dashboard/999378).

It reports DAU, WAU, MAU, token usage, completed runs, and run success rate. Daily
values compare with yesterday and the same weekday last week. WAU compares with
the preceding seven-day window, and MAU with the preceding 30-day window. Success
rate changes use percentage points. A zero baseline shows `new (baseline 0)`;
no completed runs gives an unavailable success rate.

Active users count anonymous installations that emitted `agent run completed`,
including successful, failed, and aborted runs. They do not count verified people.
Tokens sum `total_tokens`, including recorded cache usage; they do not measure
spend. Success rate divides runs with `outcome=success` by all completed runs.
The query excludes PostHog test accounts and version `0.0.0-benchmark`.

## Setup and activation

1. Add repository secret `POSTHOG_PERSONAL_API_KEY`: a
   [PostHog personal API key](https://posthog.com/docs/api/personal-api-keys) with
   read access to queries and insights, limited to project 22174. A project capture
   token cannot read analytics. The script uses the EU
   [query endpoint](https://posthog.com/docs/product-analytics/surfaces/api).
2. Set repository secret `SLACK_STATS_WEBHOOK_URL` to the engineering bot's incoming
   webhook for `#research-19-prime-agent`. The webhook chooses the channel and sender.
3. Merge the workflow to `main`. Run it manually with **publish unchecked** to
   validate access without sending a message. Logs show validation status only.
4. After reviewing a local report, set repository variable
   `PRIME_AGENT_STATS_ENABLED=true` to enable daily delivery. Keep this variable
   unset or `false` to stop delivery. Manual sends also require this variable,
   **publish checked**, and the `main` branch.

Neither Codex nor n8n runs the scheduled report. An Actions preview does not receive
the Slack secret. Metric values never enter job logs, summaries, or uploaded artifacts.

For a local preview, supply the PostHog key through the environment and run:

```sh
python3 scripts/daily_stats.py prepare --output /tmp/prime-agent-stats.json
```

Read the JSON file locally. Do not commit or upload it: it contains business metrics.
An existing query response can be replayed without network access:

```sh
python3 scripts/daily_stats.py prepare --date YYYY-MM-DD \
  --response-file /tmp/posthog-response.json --output /tmp/prime-agent-stats.json
```

## Delivery and recovery

The workflow serializes delivery attempts and saves an artifact named
`prime-agent-stats-YYYY-MM-DD` **before** contacting Slack. It contains only the
report date and workflow run ID, retained for 90 days. Manual report dates are
limited to completed UTC days within the last 60 days. A rerun with an existing
artifact skips delivery, including a timeout whose delivery outcome is unknown.
Failed preparation creates no delivery artifact and can be rerun safely.

An interruption after saving the artifact can leave a report unsent. Check the
channel and run first. If the report was never delivered, delete that date's
artifact in GitHub Actions before rerunning with publish enabled. Do not remove
the marker for a delivered report. The script never automatically retries Slack.
Incoming webhooks cannot delete messages; deletion requires the owning bot or a
Slack administrator.

Run the synthetic tests without credentials or network access:

```sh
python3 -m unittest discover -s scripts -p test_daily_stats.py -v
```

---

### Legacy Release Inventory.Json (`docs/legacy-release-inventory.json`)

{
  "retrieved_at": "2026-10-09",
  "github_release_pages": 2,
  "github_release_count": 107,
  "stable_count": 18,
  "releases": [
    {
      "tag": "v0.9.8",
      "target_commitish": "7d442aafa985f9342134fac16c2ef41f03fb45c1",
      "published_at": "2026-09-29T22:06:42Z",
      "prerelease": false,
      "url": "https://github.com/PrimeIntellect-ai/prime-agent/releases/tag/v0.9.8",
      "npm_assets": [
        {
          "name": "prime-agent-0.9.8.tgz",
          "digest": "sha256:d7b72785119efc28bfbca8ec4a7f47a1fcdcf47fcd8cebdb60bffaa79e3e1274",
          "browser_download_url": "https://github.com/PrimeIntellect-ai/prime-agent/releases/download/v0.9.8/prime-agent-0.9.8.tgz"
        }
      ]
    },
    {
      "tag": "beta",
      "target_commitish": "7d442aafa985f9342134fac16c2ef41f03fb45c1",
      "published_at": "2026-07-17T23:04:57Z",
      "prerelease": true,
      "url": "https://github.com/PrimeIntellect-ai/prime-agent/releases/tag/beta",
      "npm_assets": [
        {
          "name": "prime-agent-0.9.8-beta.2286.1.7d442aa.tgz",
          "digest": "sha256:7a311e2d7fd45c0b9f134197bb3101ebc11233c6c2bf649c4d574a0d98d3473b",
          "browser_download_url": "https://github.com/PrimeIntellect-ai/prime-agent/releases/download/beta/prime-agent-0.9.8-beta.2286.1.7d442aa.tgz"
        }
      ]
    },
    {
      "tag": "v0.9.7",
      "target_commitish": "08ff1b2e2794ea9e8f4a08d12bc95408a66e1074",
      "published_at": "2026-09-28T23:13:49Z",
      "prerelease": false,
      "url": "https://github.com/PrimeIntellect-ai/prime-agent/releases/tag/v0.9.7",
      "npm_assets": [
        {
          "name": "prime-agent-0.9.7.tgz",
          "digest": "sha256:d696f2636cd7780d2d67296142bc1d13d72960c8be4e5cfae0bbba82f9d8707d",
          "browser_download_url": "https://github.com/PrimeIntellect-ai/prime-agent/releases/download/v0.9.7/prime-agent-0.9.7.tgz"
        }
      ]
    },
    {
      "tag": "v0.9.6",
      "target_commitish": "e260085dd8f742e0def3d871860c9a888b114851",
      "published_at": "2026-09-24T01:01:53Z",
      "prerelease": false,
      "url": "https://github.com/PrimeIntellect-ai/prime-agent/releases/tag/v0.9.6",
      "npm_assets": [
        {
          "name": "prime-agent-0.9.6.tgz",
          "digest": "sha256:e5bf0e349e55b3f75c79e66006c993b10c51ed1c9bf15863b06658fcaf0232b2",
          "browser_download_url": "https://github.com/PrimeIntellect-ai/prime-agent/releases/download/v0.9.6/prime-agent-0.9.6.tgz"
        }
      ]
    },
    {
      "tag": "releases/v0.9.1-fhcache1",
      "target_commitish": "da819d32f6a3f57e71f5d5fb784a4958929dbdae",
      "published_at": "2026-09-20T03:45:42Z",
      "prerelease": true,
      "url": "https://github.com/PrimeIntellect-ai/prime-agent/releases/tag/releases/v0.9.1-fhcache1",
      "npm_assets": [
        {
          "name": "prime-agent-0.9.1-fhcache1.tgz",
          "digest": "sha256:4a461bf7fc9d8e6e4e61471b66341aff05ba94e02c6c25f678851d26d1b27d2c",
          "browser_download_url": "https://github.com/PrimeIntellect-ai/prime-agent/releases/download/releases/v0.9.1-fhcache1/prime-agent-0.9.1-fhcache1.tgz"
        }
      ]
    },
    {
      "tag": "fhcache-candidate-1",
      "target_commitish": "da819d32f6a3f57e71f5d5fb784a4958929dbdae",
      "published_at": "2026-09-20T03:33:40Z",
      "prerelease": true,
      "url": "https://github.com/PrimeIntellect-ai/prime-agent/releases/tag/fhcache-candidate-1",
      "npm_assets": [
        {
          "name": "prime-agent-0.9.1-fhcache1.tgz",
          "digest": "sha256:4a461bf7fc9d8e6e4e61471b66341aff05ba94e02c6c25f678851d26d1b27d2c",
          "browser_download_url": "https://github.com/PrimeIntellect-ai/prime-agent/releases/download/fhcache-candidate-1/prime-agent-0.9.1-fhcache1.tgz"
        }
      ]
    },
    {
      "tag": "v0.9.5",
      "target_commitish": "a7d791bc1be09793ed5f3ec05bf4cccbc60679ea",
      "published_at": "2026-09-16T00:13:49Z",
      "prerelease": false,
      "url": "https://github.com/PrimeIntellect-ai/prime-agent/releases/tag/v0.9.5",
      "npm_assets": [
        {
          "name": "prime-agent-0.9.5.tgz",
          "digest": "sha256:349f1682c7909550842f1b04a71ba95814341b136474ade736df93f8ec006876",
          "browser_download_url": "https://github.com/PrimeIntellect-ai/prime-agent/releases/download/v0.9.5/prime-agent-0.9.5.tgz"
        }
      ]
    },
    {
      "tag": "v0.9.4",
      "target_commitish": "f771dfcedd684d1afff84ca2c6fa95c7a21efbc2",
      "published_at": "2026-09-08T23:18:05Z",
      "prerelease": false,
      "url": "https://github.com/PrimeIntellect-ai/prime-agent/releases/tag/v0.9.4",
      "npm_assets": [
        {
          "name": "prime-agent-0.9.4.tgz",
          "digest": "sha256:b8d752a53d11a8c9a7580e1fb5fc24f7ce74ccad979c7e6e6aa8880fc3ad90b0",
          "browser_download_url": "https://github.com/PrimeIntellect-ai/prime-agent/releases/download/v0.9.4/prime-agent-0.9.4.tgz"
        }
      ]
    },
    {
      "tag": "v0.9.3",
      "target_commitish": "915c78f42c248b08238dd27fcd4bcab32c60beab",
      "published_at": "2026-09-06T10:44:04Z",
      "prerelease": false,
      "url": "https://github.com/PrimeIntellect-ai/prime-agent/releases/tag/v0.9.3",
      "npm_assets": [
        {
          "name": "prime-agent-0.9.3.tgz",
          "digest": "sha256:ce71049389877770aa31b9be64c473685a86159adbbd2466bd43e4a9113242f1",
          "browser_download_url": "https://github.com/PrimeIntellect-ai/prime-agent/releases/download/v0.9.3/prime-agent-0.9.3.tgz"
        }
      ]
    },
    {
      "tag": "v0.9.2",
      "target_commitish": "9c54a35dac3a2ad17910074d66664859ea175666",
      "published_at": "2026-09-05T19:14:39Z",
      "prerelease": false,
      "url": "https://github.com/PrimeIntellect-ai/prime-agent/releases/tag/v0.9.2",
      "npm_assets": [
        {
          "name": "prime-agent-0.9.2.tgz",
          "digest": "sha256:d649b9f0258c77de7d02aba33d786925ce41a36b601e8e24eef0f00bdc2f1f49",
          "browser_download_url": "https://github.com/PrimeIntellect-ai/prime-agent/releases/download/v0.9.2/prime-agent-0.9.2.tgz"
        }
      ]
    },
    {
      "tag": "v0.9.1",
      "target_commitish": "81ae3cb34d27d38ee37f9e205a1e73694993b344",
      "published_at": "2026-09-01T18:54:36Z",
      "prerelease": false,
      "url": "https://github.com/PrimeIntellect-ai/prime-agent/releases/tag/v0.9.1",
      "npm_assets": [
        {
          "name": "prime-agent-0.9.1.tgz",
          "digest": "sha256:573bce0cd004fc62052e9a924089941b7f39266ab71e66a94c85a1f9d35835ba",
          "browser_download_url": "https://github.com/PrimeIntellect-ai/prime-agent/releases/download/v0.9.1/prime-agent-0.9.1.tgz"
        }
      ]
    },
    {
      "tag": "v0.9.0",
      "target_commitish": "c394506e2f0dd887b3f94908da9f2910b43c846b",
      "published_at": "2026-09-01T18:18:51Z",
      "prerelease": false,
      "url": "https://github.com/PrimeIntellect-ai/prime-agent/releases/tag/v0.9.0",
      "npm_assets": [
        {
          "name": "prime-agent-0.9.0.tgz",
          "digest": "sha256:fc560ec74195297999405d4fbfd0d308598cf502e0fccef7f02eb60032213108",
          "browser_download_url": "https://github.com/PrimeIntellect-ai/prime-agent/releases/download/v0.9.0/prime-agent-0.9.0.tgz"
        }
      ]
    },
    {
      "tag": "v0.8.1",
      "target_commitish": "514633727bf26d74f39f3119c2b0e31a5ceb2a9d",
      "published_at": "2026-08-26T05:14:48Z",
      "prerelease": false,
      "url": "https://github.com/PrimeIntellect-ai/prime-agent/releases/tag/v0.8.1",
      "npm_assets": [
        {
          "name": "prime-agent-0.8.1.tgz",
          "digest": "sha256:46c24db1782dd31adc35d5c6cbcc75564faba6ced3bf2ccf03d836ee77134475",
          "browser_download_url": "https://github.com/PrimeIntellect-ai/prime-agent/releases/download/v0.8.1/prime-agent-0.8.1.tgz"
        }
      ]
    },
    {
      "tag": "v0.8.0",
      "target_commitish": "8d7deeab5861bf9d77bde3d8511046a5c799818d",
      "published_at": "2026-08-21T21:18:10Z",
      "prerelease": false,
      "url": "https://github.com/PrimeIntellect-ai/prime-agent/releases/tag/v0.8.0",
      "npm_assets": [
        {
          "name": "prime-agent-0.8.0.tgz",
          "digest": "sha256:f5b0093c7e0fddb73f94773d74383585456adfa84f12a4082d3098f23bb8fab6",
          "browser_download_url": "https://github.com/PrimeIntellect-ai/prime-agent/releases/download/v0.8.0/prime-agent-0.8.0.tgz"
        }
      ]
    },
    {
      "tag": "v0.7.4",
      "target_commitish": "af0b8e00b9f704e834787fd321065ca78281f2aa",
      "published_at": "2026-08-19T23:44:42Z",
      "prerelease": false,
      "url": "https://github.com/PrimeIntellect-ai/prime-agent/releases/tag/v0.7.4",
      "npm_assets": [
        {
          "name": "prime-agent-0.7.4.tgz",
          "digest": "sha256:dea045d7c170466a102e030e940f459cc13f2c113465f40be1c7505d80405d2e",
          "browser_download_url": "https://github.com/PrimeIntellect-ai/prime-agent/releases/download/v0.7.4/prime-agent-0.7.4.tgz"
        }
      ]
    },
    {
      "tag": "v0.7.3",
      "target_commitish": "61131b2d195ba7a67a4ce8ac60bb10cecae07b67",
      "published_at": "2026-08-17T22:38:17Z",
      "prerelease": false,
      "url": "https://github.com/PrimeIntellect-ai/prime-agent/releases/tag/v0.7.3",
      "npm_assets": [
        {
          "name": "prime-agent-0.7.3.tgz",
          "digest": "sha256:2a188738318b91ff91ea77b8a0b215c41543cf0483fcb18a53475dc4e7032784",
          "browser_download_url": "https://github.com/PrimeIntellect-ai/prime-agent/releases/download/v0.7.3/prime-agent-0.7.3.tgz"
        }
      ]
    },
    {
      "tag": "v0.7.2",
      "target_commitish": "83a0f9f9566219551fcb6ffaf7f519a815749a58",
      "published_at": "2026-08-11T19:00:48Z",
      "prerelease": false,
      "url": "https://github.com/PrimeIntellect-ai/prime-agent/releases/tag/v0.7.2",
      "npm_assets": [
        {
          "name": "prime-agent-0.7.2.tgz",
          "digest": "sha256:bc5471f2a626d727b88a45eb745fff93b10c554a3c4fc5912f25d8c64b987f5e",
          "browser_download_url": "https://github.com/PrimeIntellect-ai/prime-agent/releases/download/v0.7.2/prime-agent-0.7.2.tgz"
        }
      ]
    },
    {
      "tag": "v0.7.1",
      "target_commitish": "95afd319a78ae017a41241d50b013d656a0685ce",
      "published_at": "2026-08-07T18:39:08Z",
      "prerelease": false,
      "url": "https://github.com/PrimeIntellect-ai/prime-agent/releases/tag/v0.7.1",
      "npm_assets": [
        {
          "name": "prime-agent-0.7.1.tgz",
          "digest": "sha256:d68612c83239caafab72cc76c55ac572bfd07a059ea8fbd2a3ddbe1f2b55dcdb",
          "browser_download_url": "https://github.com/PrimeIntellect-ai/prime-agent/releases/download/v0.7.1/prime-agent-0.7.1.tgz"
        }
      ]
    },
    {
      "tag": "v0.7.0",
      "target_commitish": "be9e2fa0714e7cd1c6bd9bdb1b554d2cc6550387",
      "published_at": "2026-08-05T18:41:37Z",
      "prerelease": false,
      "url": "https://github.com/PrimeIntellect-ai/prime-agent/releases/tag/v0.7.0",
      "npm_assets": [
        {
          "name": "prime-agent-0.7.0.tgz",
          "digest": "sha256:88b6578518c72cd51a825bc80f28e0fef9a64c67de4a7d6fd7afd7ca1b34da0b",
          "browser_download_url": "https://github.com/PrimeIntellect-ai/prime-agent/releases/download/v0.7.0/prime-agent-0.7.0.tgz"
        }
      ]
    },
    {
      "tag": "v0.6.1",
      "target_commitish": "8bd7c18f16bfdc356c1cd20fb9fcf01119147cda",
      "published_at": "2026-08-05T14:55:26Z",
      "prerelease": false,
      "url": "https://github.com/PrimeIntellect-ai/prime-agent/releases/tag/v0.6.1",
      "npm_assets": [
        {
          "name": "prime-agent-0.6.1.tgz",
          "digest": "sha256:4c7150dbfe5807c8480676d20c3104e63045b8ca2463abf38f021062e9fcacef",
          "browser_download_url": "https://github.com/PrimeIntellect-ai/prime-agent/releases/download/v0.6.1/prime-agent-0.6.1.tgz"
        }
      ]
    },
    {
      "tag": "v0.6.0",
      "target_commitish": "7db7b69c60be0f7b271faf948864891813b27182",
      "published_at": "2026-08-05T04:36:20Z",
      "prerelease": false,
      "url": "https://github.com/PrimeIntellect-ai/prime-agent/releases/tag/v0.6.0",
      "npm_assets": [
        {
          "name": "prime-agent-0.6.0.tgz",
          "digest": "sha256:8ef229a0422398174ba8f0ba4f7101adfc614b023a8f819f6ef3920e5d2350c6",
          "browser_download_url": "https://github.com/PrimeIntellect-ai/prime-agent/releases/download/v0.6.0/prime-agent-0.6.0.tgz"
        }
      ]
    }
  ],
  "support_scope": "Prime Agent 0.6.0 onward",
  "rust_beta_tags": [
    "v0.9.9-beta.10",
    "v0.9.9-beta.4",
    "v0.9.9-beta.6",
    "v0.9.9-beta.7",
    "v0.9.9-beta.8",
    "v0.9.9-beta.9",
    "v0.9.9-beta.11",
    "v0.9.9-beta.17",
    "v0.9.9-beta.18",
    "v0.9.9-beta.21",
    "v0.9.9-beta.22",
    "v0.9.9-beta.23",
    "v0.9.9-beta.24",
    "v0.9.9-beta.25",
    "v0.9.9-beta.26",
    "v0.9.9-beta.27",
    "v0.9.9-beta.28",
    "v0.9.9-beta.29",
    "v0.9.9-beta.30",
    "v0.9.9-beta.31",
    "v0.9.9-beta.32",
    "v0.9.9-beta.33",
    "v0.9.9-beta.34",
    "v0.9.9-beta.35",
    "v0.9.9-beta.36",
    "v0.9.9-beta.37",
    "v0.9.9-beta.38",
    "v0.9.9-beta.39",
    "v0.9.9-beta.40",
    "v0.9.9-beta.41",
    "v0.9.9-beta.42",
    "v0.9.9-beta.43",
    "v0.9.9-beta.44",
    "v0.9.9-beta.45",
    "v0.9.9-beta.46",
    "v0.9.9-beta.47",
    "v0.9.9-beta.48",
    "v0.9.9-beta.49",
    "v0.9.9-beta.50",
    "v0.9.9-beta.51",
    "v0.9.9-beta.52",
    "v0.9.9-beta.53",
    "v0.9.9-beta.54",
    "v0.9.9-beta.55",
    "v0.9.9-beta.56",
    "v0.9.9-beta.57",
    "v0.9.9-beta.58",
    "v0.9.9-beta.59",
    "v0.9.9-beta.60"
  ]
}

---

### Legacy Upgrade Contract (`docs/legacy-upgrade-contract.md`)

# Legacy TypeScript upgrade contract

This audit covers published Prime Agent releases from v0.6.0 onward, including
18 stable TypeScript releases through v0.9.8 and relevant candidate and beta
artifacts. Earlier versions are excluded from the requested support scope.
Inventory and source comparison are not evidence that an installed-artifact
upgrade or every supported platform has passed testing.

## Published inventory in scope

A full two-page GitHub Releases API query on 2026-10-09 returned 107 releases.
The compact [release inventory](legacy-release-inventory.json) records the 18
stable releases in scope, independent GitHub npm-asset digests, source targets,
and relevant TS prereleases. The stable versions are 0.6.0–0.6.1,
0.7.0–0.7.4, 0.8.0–0.8.1, and 0.9.0–0.9.8.

All 18 have genuine published `prime-agent-VERSION.tgz` assets; this inventory
does not count inherited upstream Git tags as Prime releases. The unscoped
public npm registry endpoint for `prime-agent` returned 404 during this audit;
these npm packages are published as tarballs through GitHub and the release bucket.

Additional published TS artifacts are `0.9.1-fhcache1` (the same digest hosted by
both `fhcache-candidate-1` and `releases/v0.9.1-fhcache1`) and the rolling `beta`
release, currently `0.9.8-beta.2286.1.7d442aa`. The latter has npm and native
archives. The fhcache package digest, independently supplied by the GitHub API,
is `4a461bf7fc9d8e6e4e61471b66341aff05ba94e02c6c25f678851d26d1b27d2c`.
It has no published SHA256SUMS file. A locally computed digest alone is not
independent download verification.

The 49 versioned Rust `0.9.9-beta.*` releases are distinct from TS migration
fixtures and require Rust-to-Rust update coverage. Their exact published tags
are recorded in the inventory. The `nightly` release is a rolling Rust asset host.
Historical rolling TS beta assets are overwritten on GitHub; this inventory
cannot establish an exhaustive list of every beta ever distributed through the
bucket. Do not equate an absent GitHub beta tag with an unpublished build.

## Authoritative release sources

Use the GitHub release `target_commitish` below for source comparisons of the
audited versions, then verify behavior against the actual published artifacts.
Several local version tags resolve to upstream history with different package
names and no Prime Agent updater. Those tags are not suitable fixtures for these
releases. A recorded source target alone does not prove the bytes shipped; release
assets remain the authoritative end-to-end test inputs.

| Release | GitHub release target commit |
| --- | --- |
| v0.6.0 | `7db7b69c60be0f7b271faf948864891813b27182` |
| v0.6.1 | `8bd7c18f16bfdc356c1cd20fb9fcf01119147cda` |
| v0.7.0 | `be9e2fa0714e7cd1c6bd9bdb1b554d2cc6550387` |
| v0.7.1 | `95afd319a78ae017a41241d50b013d656a0685ce` |
| v0.7.2 | `83a0f9f9566219551fcb6ffaf7f519a815749a58` |
| v0.7.3 | `61131b2d195ba7a67a4ce8ac60bb10cecae07b67` |
| v0.7.4 | `af0b8e00b9f704e834787fd321065ca78281f2aa` |
| v0.8.0 | `8d7deeab5861bf9d77bde3d8511046a5c799818d` |
| v0.8.1 | `514633727bf26d74f39f3119c2b0e31a5ceb2a9d` |
| v0.9.0 | `c394506e2f0dd887b3f94908da9f2910b43c846b` |
| v0.9.1 | `81ae3cb34d27d38ee37f9e205a1e73694993b344` |
| v0.9.2 | `9c54a35dac3a2ad17910074d66664859ea175666` |
| v0.9.3 | `915c78f42c248b08238dd27fcd4bcab32c60beab` |
| v0.9.4 | `f771dfcedd684d1afff84ca2c6fa95c7a21efbc2` |
| v0.9.5 | `a7d791bc1be09793ed5f3ec05bf4cccbc60679ea` |
| v0.9.6 | `e260085dd8f742e0def3d871860c9a888b114851` |
| v0.9.7 | `08ff1b2e2794ea9e8f4a08d12bc95408a66e1074` |
| v0.9.8 | `7d442aafa985f9342134fac16c2ef41f03fb45c1` |

## Update families

- v0.6.0–v0.9.4 ship npm tarballs. Their
  `packages/coding-agent/src/utils/version-check.ts` files are byte-identical:
  SHA-256 `5a87851fc4f6473452607cb2816945e82f37e35f3aa2ecc846b3d41f1932aa29`.
- v0.9.5–v0.9.8 additionally ship managed native archives. Their manifest parsers
  are byte-identical: SHA-256
  `2a37f5116a6d855da564bb3efe4a6b62eb0d0a35523c369796b5529379dacff0`.
  Their `cli/native-update.ts` files are also byte-identical: SHA-256
  `707cdc796f035f01c08647c026a1f1a5aaeafbb3b4158a22e741b367a8d10f03`.

All stable clients read `latest.json` under `PRIME_AGENT_DOWNLOAD_BASE_URL`, or
the default release bucket. Beta clients read `beta.json`. The parser accepts
`version`, `package` (or `packageName`), and `tarball`; relative tarball URLs
resolve against the release bucket. Keep the npm tarball in the manifest even
when native artifacts are present.

## npm bridge requirements

The published package builder rewrites the source package to **`prime-agent`**
and the command to **`prime-agent`**, with entrypoint **`dist/bundle/cli.js`**.
Do not infer the published package identity from the source package.json.

The existing updater runs the detected global package manager against the
manifest tarball. Supported command builders include npm, pnpm, yarn, and Bun;
that source support is not a claim that each has been integration-tested.

Keep the package name and `dist/bundle/cli.js` and
`dist/bundle/cli-node.js` paths stable. The bridge also retains a thin
`dist/cli.js` alias; that compatibility alias does not expand the tested version
scope. In published v0.9.5–v0.9.8 npm
artifacts, the packer moves the TS bundle to `cli-node.js` and puts the old
native-migration shim at `cli.js`. When that shim falls back to Node, the
updater's original entrypoint is `cli-node.js`. After package replacement the
old CLI and TUI execute their original Node executable with that original
absolute JavaScript entrypoint path. Both paths must run the new Rust bridge;
a new command shim elsewhere does not repair this relaunch. The bridge must forward arguments, signals, terminal
I/O, and exit codes and remain usable when package-manager install scripts are
disabled. It must not remove its own package while the old process still needs
to relaunch through it.

### Failed migration and retry

The bridge includes the checksum-pinned, genuine v0.9.8 TypeScript package as
recovery. A host that cannot execute Rust must retain a usable public command,
daemon, worker, and saved sessions. Explicit updates report failure; ordinary
launches use recovery without repeatedly attempting the same failed install.

For bridge-driven channel installs, the native executable must pass the bounded
staged version probe before the installer replaces the public launcher. A valid
archive checksum alone does not establish that the host can execute its payload.

An explicit retry after failure must consult the current release through the
preserved updater, including downloading its updated bridge and installer.
Retrying only the failed bridge's pinned version would prevent a later release
from adding support for that host. The retry may report success only after a
working native version and its corresponding migration receipt are verified.

## Managed native requirements

The v0.9.5–v0.9.8 updater executes the installer bundled in the **old** release.
Replacing only the new archive's install.sh cannot affect that installation.
The old installer downloads
`releases/vVERSION/prime-agent-VERSION-PLATFORM.tar.gz`, checks its checksum,
and requires these regular files at the archive root:

- `prime-agent`
- `package.json` with the target version
- `install.sh`
- `prime-agent-runtime/pyproject.toml`
- `prime-agent-runtime/src/rlm/repl.py`
- `theme/prime.json`
- `export-html/template.html`
- `photon_rs_bg.wasm`

It requires `prime-agent --version` to print the bare target version and
`prime-agent --help` to succeed. It then installs to
`releases/VERSION-PLATFORM-SHA256[.SUFFIX]/`, writes `.archive-sha256` and
`.install-source`, and atomically changes `bin/prime-agent`. The managed-root
marker is `prime-agent-native-v1`.

These same legacy files are revalidated by the still-running TS client before
it can find the new launcher for restart. They must remain present after
activation, including the two metadata files written by the old installer.
The next Rust update must recognize this managed installation layout.

The manifest parser prefers `binariesV2` over `binaries`; each accepted entry
must have an exact platform-specific filename and lowercase 64-digit SHA-256.
Retain the legacy platforms in the manifest only when the corresponding
artifacts can actually run on those hosts.

## Daemon and TUI handoff

All audited versions launch the newly installed entrypoint with:

```text
update --internal-update-restart-coordinator --daemon-socket SOCKET
       --internal-update-restart-status STATUS_PATH
       [--internal-update-restart-origin SESSION_ID]
```

The TS parent creates the status directory but **does not create a staged
status file**. The coordinator must initialize it. TS polls a version-1 JSON
record with `requestId`, `socketPath`, `phase`, `coordinator.pid`,
`counts: {total, restored, resumed, failed}`, `startedAt`, and `updatedAt`.
Terminal phases are `complete`, `skipped`, and `failed`; other phases include
`starting`, `preparing`, `stopping`, `starting_daemon`, and `restoring`.
`heartbeatAt`, predecessor/successor identities, failures, and message are
optional. Keep progress live during a long restore.

This differs from the newer Rust staged-update FSM and its status schema.
Recognizing the flag alone is insufficient: a legacy adapter must initialize
and maintain TS-compatible status and use the TS daemon's
`prepare_update_restart` / shutdown / session restoration contract. The parent
otherwise reports a restart warning even if installation succeeded.

For `/update`, the old TUI runs the update subprocess with
`PRIME_AGENT_INTERACTIVE_SELF_UPDATE=1`, disposes its old connection, launches
the coordinator itself, and relaunches with its saved session file. Do not
restart the daemon prematurely from the installer or an executable probe.

The TS daemon persists its restart manifest before replying to preparation.
A lost reply therefore does not prove that preparation failed. Record a durable
binding to the predecessor, socket, target release, and attempt before sending
the request; recover a fresh scoped TS manifest only through that binding.
Retain pending source manifests through ordinary startup cleanup until their
state has been adopted. A definitive preparation refusal with no new manifest
must allow another attempt. Queued actions must remain paused while the resumed
continuation is admitted, so follow-up input cannot run ahead of that turn.

## Platform limits and release claims

The old native distribution supports Darwin arm64/x64 and Linux arm64/x64,
including `linux-arm64-musl`, `linux-x64-baseline`, `linux-x64-musl`, and
`linux-x64-musl-baseline`. The Rust release matrix currently ships GNU Linux
builds with a GLIBC 2.35 ceiling and no musl target. A GNU archive must never be
renamed to a musl suffix: that would turn a controlled refusal into a broken
installation. Supporting Alpine/musl requires real musl artifacts and runtime
verification. Older glibc hosts likewise need a compatible build or a clear
unsupported-platform result before activation.

The TS `baseline` suffix selects x64 CPUs without AVX2. Rust can reuse its
ordinary x64 artifact for that alias only after verifying its target CPU and
all bundled native dependencies work without AVX2; the absence of a separate
Rust baseline target is not by itself proof.

“All versions since 0.6” describes the source-version range, not an exemption
from platform support. Do not claim every prior platform migrates until those
platform gates pass. Test all 18 published npm releases, all four native source
releases, CLI and TUI entrypoints, session preservation, daemon handoff, and a
second Rust update using assembled artifacts.

---

### Legacy Upgrade Evidence (`docs/legacy-upgrade-evidence.md`)

# Legacy update compatibility evidence

This is unreleased-candidate evidence, not approval to publish v1. All builds
and runtime tests after the remote-only instruction ran in Prime sandboxes.
Support scope starts at 0.6.0; no earlier versions are required.

## Current verification status

The recovery-hardening snapshot passed the full repository gate, but its real
TS `/update` rerun exposed a request-ID collision after reconnecting: TS replayed
the preparation response instead of executing shutdown. The follow-up uses
distinct preparation and shutdown envelope IDs. A deliberate collision fails
the regression; the fix passes all six coordinator tests. Full gates and actual
migrations for this follow-up remain pending. Earlier results below are not
attributed to the latest source.

The consolidated full run encountered `ETXTBSY` (text file busy) in the existing
`update::download::tests::stages_a_local_payload_directory` test. A diagnostic
single-test run and the complete core library suite then passed (1,160 passed,
3 ignored), without product changes or retry logic. The cause is unproven; the
original failure is retained in `make-check-hardened-etxtbsy-red.log`. One fresh
full run passed; this does not by itself establish that the failure is fixed.

| Check | Verified result |
| --- | --- |
| npm CLI and TUI migration, all 18 stable TS versions | 36/36 on optimized candidate before final recovery hardening |
| Native CLI and TUI migration, TS 0.9.5–0.9.8 | 8/8 on that optimized candidate |
| Subsequent Rust 1.0.0 → 1.0.1 update | Passed through the original custom public command |
| Fresh curl installation and reinstallation | Passed with final staged-probe installer |
| Unusable native executable | Red reproduced; fixed installer retained usable TS recovery |
| Failed migration followed by a newer release | Original update command reached Rust 1.0.1 |
| Lost prepare response and retained queued work | Six focused coordinator tests passed; red-first regression reproduced |
| Startup cleanup preserves pending recovery manifests | Two focused tests passed |
| Busy-session continuation before follow-up input | Real worker regression reproduced red; 19 focused restore tests passed |
| npm bridge unit suite | 25 passed, including IPC, fd3, signals, retry, and prefix behavior |
| Packaging suites | 39 passed: native compatibility, restamping, decoder, catalog |
| Consolidated full repository gate | fmt, clippy, 5,206 tests passed (19 ignored), release build passed |

## Published-version matrix

The npm runs use unchanged, checksum-verified published packages and their real
public CLI/TUI entrypoints. Every version below passed both commands.

| Versions | CLI cases | TUI cases |
| --- | ---: | ---: |
| 0.6.0, 0.6.1 | 2 | 2 |
| 0.7.0–0.7.4 | 5 | 5 |
| 0.8.0, 0.8.1 | 2 | 2 |
| 0.9.0–0.9.8 | 9 | 9 |

Native installs additionally cover every stable native TS release,
0.9.5–0.9.8, using each archive's unchanged old installer.
The TUI runs require completed restoration, the original saved session attached
to a Rust worker, a preserved transcript prefix, responsive input, and no
restart warnings. CLI checks include version/help, checksum refusal, state
preservation, and paths with spaces.

The optimized Linux x64 snapshot tested before final recovery hardening:

- Rust 1.0.0 archive: `7a5e663b535d1755504f8315d4f13de0857ed746ac5c6fdc6c5e55db84ec11b3`.
- Rust 1.0.1 archive: `f46a36e1456dc6d7ddfefaf85bad68b29a0b06d863cb1577ce5e3445d4cad97a`.
- Shipped executable: `0dbfb29af17614870f613017de2c6a11b25aa80572390a61dbe4d42c36335227`.

Independent source archive digests and the exact version inventory live in
[legacy-release-inventory.json](legacy-release-inventory.json).
[The compatibility contract](legacy-upgrade-contract.md) describes the layouts,
wire fields, and required recovery behavior.

## Failure and usability evidence

**Unusable executable:** the actual TS 0.6.0 updater installed a checksum-valid
archive whose native executable named a nonexistent interpreter. With the
old probe condition, the npm launcher was replaced and the command exited 127.
With the fix, the staged probe refused activation, retained the npm symlink,
wrote no success receipt, and left a genuine TS 0.9.8 daemon/worker able to
resume the original session and accept input. The npm/native prefixes overlapped.

**Future retry:** after an unsupported architecture caused migration to fail,
the feed advanced from 1.0.0 to 1.0.1 and host support was restored. The original
public update command fetched the new bridge and installer, activated Rust
1.0.1, and resumed the saved session. An explicit retry that remains unsupported
must still fail, despite the old TS updater's warning-only success exit.

**Lost response:** a Unix-socket protocol fixture writes a TS-shaped manifest
containing queued work, then drops the prepare reply. The Rust coordinator
recovers it through its durable predecessor/socket/attempt binding before
shutdown. Disabling recovery makes the regression fail. Tests reject foreign
owners and stale data and permit retry after a definitive preparation refusal.
Startup cleanup preserves pending source manifests until adoption.

**Busy queue:** a real scheduled Rust worker with a scripted engine proves that
restored follow-up input stays parked until continuation admission. Disabling
the input pause consumes the queue too early; restoring it passes.

**Usability:** separate real TS 0.6.0 npm and TS 0.9.8 native handoffs execute
and persist a controlled Bash command after restoration, preserving prior
transcript bytes. No provider inference or IPython execution was tested.

The genuine fallback is the complete published TS 0.9.8 package, pinned to
SHA-256 `d7b72785119efc28bfbca8ec4a7f47a1fcdcf47fcd8cebdb60bffaa79e3e1274`.
The final staged-probe installer tested here is
`323328cea0ede4d60aedfef486ba383ea081457e65ccedb1782ca6a0575a7f09`.

## Reproduction and retained reports

Run these only in an isolated test host, with temporary homes and no real
credentials. Harnesses accept candidate archives and report paths; use their
`--help` for the published source archive/checksum/prefix arguments.

| Harness | Purpose |
| --- | --- |
| `scripts/release/verify_legacy_release.py` | Download and verify a genuine native TS release, then exercise its updater |
| `scripts/release/test_legacy_upgrade.py` | Native CLI, checksum refusal, custom paths, second Rust update |
| `scripts/release/test_legacy_npm_upgrade.py` | Genuine npm CLI migration |
| `scripts/release/test_legacy_tui_upgrade.py` | Actual npm/native TUI update with saved-session restoration |
| `scripts/release/test_legacy_npm_fallback.py` | Unsupported host, unusable payload, and later-release recovery |
| `scripts/release/test_legacy_runtime_restart.py` | Real post-migration worker command execution |
| `scripts/release/test_channel_install.py` | Current curl installer, reinstall, and user-file preservation |

Reports, hashes, TUI captures, and logs were downloaded without agent binaries
to `/tmp/prime-legacy-artifacts/` in the maintainer workspace. Key reports:
`native-upgrade-evidence-optimized.tar.gz`, `native-tui-optimized-0.9.{5,6,7,8}.json`,
`channel-install-probe-final.json`, `probe-red.json`, `probe-green.json`,
`future-release-recovery.json`, `legacy-recovery-red-green.tar.gz`, and
`make-check-hardened.log`. Matrix bundles contain each case's exact archive and
bridge hashes. Intermediate evidence remains in Git history and
`evidence-history-before-condensing.md` beside the raw reports.

## Remaining release gates and limits

- Complete full gates and the migration matrix against the corrected optimized artifact.
- Verify production artifacts on all supported hosts. The Linux candidate was
  built on Debian 12 with fixture catalog data. Its highest required GLIBC
  symbol is 2.34, but that is not an Ubuntu 22.04 runtime test.
- Stable release CI builds and livechecks Linux x64/arm64 in pinned Ubuntu 22.04,
  and gates fresh installation plus TS migration on Linux and macOS arm64.
  macOS x64 currently has a Rosetta version probe, not a full migration gate.
- Older macOS arm64 CLI runs, two Rust beta CLI runs, and retained TS prerelease
  runs passed earlier snapshots; they do not establish final platform coverage
  or coverage of every historical beta.
- GNU binaries do not establish musl, older-glibc, or unsupported-CPU support.
  A working TS fallback is not a successful Rust migration.
- npm was integration-tested; pnpm, Yarn, and Bun were not. Beta artifact reuse
  does not execute the new fresh-install gate.
- No final live-catalog, public stable endpoint, release-tag, or publication
  verification is claimed. Full rendered-frame and exhaustive wire-byte parity
  were not measured; the unchanged TS clients exercise the actual handoff.

---

### Slack Mcp (`docs/slack-mcp.md`)

# Slack MCP connector

Open `/mcp` (or `/plugins`), search for **Slack**, and connect. You can also
run `/mcp login slack` inside Prime Agent. Complete Slack's consent screen in
your browser. The connection becomes available to the current session through
the generic `mcp` Python API:

```python
tools = await mcp.list_tools("slack")
# Read the returned inputSchema before calling a tool.
```

Prime Agent connects to Slack's hosted Streamable HTTP server at
`https://mcp.slack.com/mcp`. It uses the public desktop OAuth client published
in [Slack's agent harness guide](https://docs.slack.dev/ai/slack-mcp-server/connect-to-harnesses/):
client ID `1601185624273.8899143856786` and callback
`http://localhost:3118/callback`. This is the client used by Slack's Claude Code
plugin, so Slack's consent screen may identify the app as Claude. Prime Agent
uses PKCE and state validation, then stores the grant in its own auth store;
no Claude installation or credentials are needed.

Slack does not offer dynamic client registration. Workspace administrators
still control app approval and granted scopes. The desktop client registration
is controlled by Slack and its partner; changes there can require an update.

If port `3118` is occupied, finish or close the other login attempt and retry.
Prime Agent reports the conflict instead of redirecting to an unregistered
port. For SSH sessions, forward local port `3118` to port `3118` on the host
running Prime Agent, or paste the final redirect URL into the auth panel.

Disconnect with `/mcp logout slack`. Connector usage contributes to the existing
`mcp_connector_use_count` session telemetry counter; server names and Slack
content are not recorded in that counter.

---

--- METRICS ---
- Files Extracted: 20
- Estimated Token Budget: ~39459 tokens
- Canonical Reference: https://codewiki.google/github.com/PrimeIntellect-ai/prime-agent
