sparrow-cli 0.5.1

A local-first Rust agent cockpit — route, run, replay, rewind
Documentation
# Architecture

## Conceptual model

Sparrow is built around one primitive: **AgentRun**.

```
AgentRun = Identity + BrainPolicy + AutonomyContract + ToolSet + Memory + Workspace
```

Everything else is a configuration of this primitive. A swarm is multiple runs
sharing a workspace. A scheduled job is a run triggered by cron. A gateway
message is a run owned by an external transport.

## Module tiers (v0.2.0)

```
Tier 0  config · auth · provider · tools · sandbox · redaction        (foundations)
Tier 1  router · engine · memory · context · permissions              (core logic)
Tier 2  autonomy · agent · capabilities · hooks · plan · commands     (safety + learning)
Tier 3  orchestrator · scheduler · runtime · github · security        (coordination)
Tier 4  tui · cli · console (WebView) · gateway                       (surfaces)
```

Each tier depends only on lower tiers. Every module exposes a trait (or pure
functions) so tests can wire seams in isolation.

## Event stream architecture

All surfaces consume the same `Event` enum (see `src/event.rs`):

```
Engine ──EventBus──→ TUI         (ratatui cockpit, fold/unfold groups)
              ├────→ CLI         (NDJSON --json)
              ├────→ WebView     (WebSocket /ws + REST panels)
              └────→ Gateway     (Telegram / Discord / Slack / Email / WS)
```

Surfaces are thin renderers. No business logic outside the engine. The same
event stream feeds `FsRecorder` for replay (`sparrow replay <run-id>`).

## Headless runtime

`sparrow daemon` owns persistent agents, the scheduler, and the gateway. It
serves surfaces over local transport (TCP + WebSocket) and is the single
source of truth.

## Agentic loop

```
classify task → route model → assemble context
     → stream tokens
         → tool use? → permission gate → autonomy gate → hook PreToolUse
             → execute in sandbox → hook PostToolUse → observe
         → mutating? → auto-checkpoint (Git) → continue
     → transcript > 120k chars? → auto-compact + HandoffDoc + Event::Compacted
     → repeat until done or guarded
```

Key invariants:

- `permissions.deny` wins over `allow` and overrides autonomy.
- Autonomy returns a rich verdict, not just yes/no: `decision`,
  `needs_checkpoint`, `notify`, and `reason`. Trusted mutating/exec actions
  therefore surface a notification and checkpoint intent before execution.
- Every `Mutating`/`Exec`/`Destructive` tool triggers a checkpoint AND fires
  `HookEvent::PreCheckpoint`/`PostCheckpoint`.
- A `Mutating` tool also flips `had_mutation`, which schedules the
  `verify_command` after the model says it's done.
- `MAX_TURNS = 60` is a hard safety cap independent of budget.
- Compaction never drops the first user message; it summarises the middle and
  keeps the last `COMPACT_KEEP_LAST = 6` messages verbatim.

## Sandboxes

| Backend | Module | Notes |
|---|---|---|
| `local` | `sandbox::LocalSandbox` | Workdir must be inside root; denied-path + env-allowlist policy applied |
| `local-hardened` | same, network off | Linux: optional firejail/bwrap/unshare wrap |
| `docker` | `sandbox::backends::DockerSandbox` | Mounted workspace, `--network=none` option |
| `ssh:<host>` | `sandbox::backends::SshSandbox` | Remote execution via `ssh` |
| `worktree` | `sandbox::backends::WorktreeSandbox` | Dedicated `git worktree`; fails clearly on non-git roots |
| `modal` / `daytona` / `vercel` / `singularity` | `sandbox::backends::*Sandbox` | Vendor CLI wrappers; honest exit 127 when CLI missing |

## Memory

Two tiers, both in `src/memory/`:

- **SQLite facts** — durable, structured (`Fact { id, key, value, updated_at }`).
- **Bounded docs**`MEMORY.md` (~2.2k chars) and `USER.md` (~1.4k chars)
  injected as durable context but explicitly NOT executable.

Anti-injection guards reject oversized writes, credential-exfil patterns,
invisible-Unicode, and prompt-injection phrases like
`"ignore previous instructions"`.

## Surfaces

| Surface | Module | Notes |
|---|---|---|
| CLI | `cli::Cli` | `sparrow <verb>` — plan, run, agent, memory, permissions, tools, security, github, compact, … |
| TUI | `tui::Tui` | ratatui cockpit, slash + `@` autocomplete, fold/unfold, theme variants |
| WebView | `console::WebViewServer` | port 9339; `/`, `/run`, `/plan`, `/memory`, `/plugins`, `/tools`, `/permissions`, `/security`, `/sessions`, `/upload`, `/artifacts`, `/ws` |
| Gateway | `gateway::*` | Telegram, Discord, Slack, Email, WS API on port 9338 |
| GitHub Action | `action.yml` + `sparrow github *` | composite action; CLI is the gh wrapper |

## What grows with you

- `MEMORY.md` / `USER.md` accumulate durable facts.
- `~/.config/sparrow/skills/` collects learned skills (curator loop).
- Skills use progressive disclosure: relevant matching loads only `SKILL.md`,
  while explicit invocation can load declared references, templates, scripts and
  assets. These paths are restricted to the skill folder, and curator rewrites
  `SKILL.md` without deleting those companion files.
- `~/.config/sparrow/plugins/` holds installed plugins.
- `~/.config/sparrow/agents/` holds `.soul.toml` and `.agent.md` agents.
- `<state>/sparrow/transcripts/` records every run for replay.
- `<state>/sparrow/sessions.db` records every session for FTS search and
  cross-surface continuity.