a4aa 0.5.0

Adapter-For-All-Agents — unified agent client adapter: process supervision, stdio/PTY transports, JSON-RPC and ACP lanes, CLI drivers, and a stdio sidecar for hosts in other languages.
Documentation
# a4aa — Adapter For All Agents

One crate owns every agent-client adaptation path a host needs. Any Rust
application can embed this library in-process; hosts in other languages
(Go, TypeScript, …) spawn the `a4aa serve` sidecar and get the identical
adapter behavior over stdio JSON-RPC.

Design premise: **the native agent is maximal** — A4AA translates what
agents natively offer into a common language, bottom-up. See
[docs/design-direction.md](docs/design-direction.md).

## What it provides

| Layer | Module | Responsibility |
| --- | --- | --- |
| Supervision | `supervision` | Process-group spawn (`command-group`: Unix pgid, Windows Job Object), sanitized environment, bounded pipes, `interrupt` (SIGINT → grace → kill) and tree teardown |
| Transport | `transport` | Piped stdio, Unix PTY (raw slave, streaming master, resize) and loopback WebSocket (RFC 6455 client for agent-owned daemons, e.g. `droid daemon --port <n>` announced on stdout) |
| Framing | `framing` | Bounded NDJSON frames (1 MiB default cap) |
| RPC | `rpc` | Bidirectional JSON-RPC 2.0 peer: correlated outbound calls, peer-initiated requests dispatched per-task and notifications delivered in wire order, half-close and fail-closed pending calls |
| Lanes | `proto` | `acp` (official `agent-client-protocol` 3.x), `rpc` (host-declared methods/params over stdio or an announced WebSocket daemon), `cli` (argv template or stdin prompt, stdio or PTY streaming), `serve` (the agent's own first-party HTTP server, e.g. `opencode serve --port 0` — discovered on stdout, driven over loopback REST) |
| Facade | `adapter` | `Adapter` / `AgentSession` lane-erased API: `open_session`, `prompt`, `cancel`, `close`, `before_turn` hook, permission handler |
| Sidecar | `src/bin/a4aa.rs` | `a4aa serve` — `a4aa.v1` NDJSON-RPC on its own stdio; `a4aa fixture-agent` — deterministic test agent |
| Mechanisms | `mechanisms` + `dialect` | Semantic declarations (`MechanismSet`) and the descriptor-driven adaptation engine producing an `AdaptationPlan` of artifacts + support report |

## Mechanism adaptation and per-agent crates

Beyond driving agents, a4aa adapts *configuration*: a `MechanismSet`
(hooks, rules, skills, commands, subagents, plugins, permissions, MCP
servers, native-profile selection) is translated into an agent-native
`AdaptationPlan` — the concrete config fragments to write plus a per-item
`native | adapted | unsupported` report.

Hook installation is owned by the Agent Hook Engine crate
(`ahe::install`, re-exported as `a4aa::hooks`): the `HookDecl`/`HookEvent`
declaration contract, the `HookSurface` store descriptors, the lowering
engine and the `a4aa::hooks::harnesses` per-agent constants all live
there. `Dialect::adapt` projects the returned `InstallationPlan` into the
`AdaptationPlan` unchanged — artifacts stay user-home relative.

Agent token counters and vendor-shaped usage normalization are provided by token-usage-detector from Agent-Statistics. A4AA acquires protocol records and forwards the reported counters; it does not aggregate charges or own a usage ledger.

Produced-artifact operators are owned by the Agent Context Operators
crate (re-exported as `a4aa::context`): the canonical conversation model,
the `Codec` contract and capability matrix, and the search / export /
transform / import / edit / snapshot engines over session stores. The
per-agent `acop-<agent>` codec crates live in that repository and project
each `a4aa-<agent>` card's `history` roots into ACO's `HistoryRoot`
vocabulary — the card stays the single authority for store locations.

Agent knowledge does not live in this crate. Each agent owns one small
descriptor crate under `crates/`, which depends only on `a4aa`:

```toml
# A host that drives only Pi installs exactly one adapter.
a4aa-pi = "0.5"
```

```rust
let plan = a4aa_pi::adapt(&mechanisms); // or a4aa::adapt(a4aa_pi::dialect(), &set)
```

| Crate | Agent knowledge it owns |
| --- | --- |
| `a4aa` (this crate) | Shared vocabulary only: `MechanismSet`, `Dialect`/surface types, the `adapt` engine, `Dialect::generic()`; hook surfaces and the hook-installation engine come from `a4aa::hooks` (`ahe::install`) |
| `crates/a4aa-<agent>` | That agent's `Dialect` const: skills/commands/subagents paths, permission/MCP config surfaces, and a reference to its AHE-owned hook descriptor |
| `crates/a4aa-dialects` | Optional name→`Dialect` registry for multi-agent hosts. Pulls in all adapters; single-agent hosts should not use it |

Every adapter crate is independently installable and independently
maintained — a change to `a4aa-codex`'s descriptor never touches
`a4aa-pi`'s dependency graph. A multi-agent host picks exactly the adapter
crates it needs — or `a4aa-dialects` for runtime name lookup; no mandatory
registry stands between a host and a single agent adapter.

Each adapter crate also publishes a dated native-capability table
(`NATIVE` in `crates/a4aa-<agent>/src/native.rs`) covering interaction
surfaces, MCP registration, config locations, and AwP primitives.
[`docs/native-capability.md`](docs/native-capability.md) is the aggregated
master table. `last_verified` is a freshness gate enforced by release
tooling before a release ships.

## Invariants

- **Zero-assumption output** — natural language is a valid turn result; no
  lane forces a schema onto agent output.
- **Fail closed** — unknown inbound requests are rejected; unresolved
  `EnvValue::Secret` and a missing permission handler never grant.
- **Explicit cancellation only** — `cancel()` is the sole cancel path;
  observer drops or transport errors are never rewritten as user cancels.
- **Bounded resources** — output caps drain-and-discard; a chatty agent can
  never exhaust host memory or stall its own pipes. Event channels are
  intentionally unbounded queues between lane and consumer: the consumer
  must drain them promptly, since the lane cannot apply backpressure to a
  host that stops reading.

## Rust usage

```rust
use a4aa::{Adapter, AgentEvent, AgentSpec, CliLaneSpec, CommandSpec,
           LaneSpec, SessionRequest, TurnRequest};
use tokio::sync::mpsc;

let spec = AgentSpec {
    id: "my-agent".into(),
    client_name: None,
    lane: LaneSpec::Cli(Box::new(CliLaneSpec {
        args: vec!["{prompt}".into()],
        ..Default::default()
    })),
    command: CommandSpec {
        program: "my-agent-cli".into(),
        ..Default::default()
    },
    max_output_bytes: None,
    seatbelted_permission: None,
};
let adapter = Adapter::new(spec);
let (events, mut rx) = mpsc::unbounded_channel::<AgentEvent>();
let session = adapter
    .open_session(SessionRequest::default(), events)
    .await?;
let outcome = session
    .prompt(TurnRequest {
        text: "hello".into(),
        ..Default::default()
    })
    .await?;
session.close().await?;
```

## Sidecar usage (Go / TypeScript)

Spawn `a4aa serve`; speak NDJSON JSON-RPC `a4aa.v1` on its stdio:

```jsonc
// → request
{"jsonrpc":"2.0","id":1,"method":"session/open","params":{"spec":{...},"options":{...}}}
// → request (returns when the turn ends)
{"jsonrpc":"2.0","id":2,"method":"session/prompt","params":{"sessionId":"...","text":"..."}}
// → request — cancel must be id-bearing, and the response must be checked:
//   a notification-form cancel is ignored (with a stderr warning), because
//   its outcome (cancelled, or unknown session) cannot be reported back
{"jsonrpc":"2.0","id":3,"method":"session/cancel","params":{"sessionId":"..."}}
// ← events stream back as notifications
{"jsonrpc":"2.0","method":"session/event","params":{"sessionId":"...","event":{...}}}
```

Permission requests are forwarded as server→client requests
(`permission/request`); the host answers with the selected option or lets it
fail closed to `cancelled`. The full method table is
[docs/sidecar-protocol.md](docs/sidecar-protocol.md).

## Verify

```sh
scripts/verify.sh   # fmt + clippy + cargo test --all-targets
```

The workspace builds against three sibling checkouts — Cargo path
dependencies resolve to `../Hook-Engine` (the
`Unka-Malloc/Agent-Hook-Engine` repository, providing `ahe`), `../ACOP`
(the `Unka-Malloc/Agent-Context-Operators` repository, providing `acop`)
and `../Metera` (the `Unka-Malloc/Metera` repository, providing
`token-usage-detector`)
at the versions `Cargo.toml` declares. Clone them beside this checkout
before building.

## Status

The crate is named `a4aa` (Apache-2.0); the sidecar protocol is versioned
as `a4aa.v1`.

RPC lane output limits apply before notification delivery is queued. Slow event
callbacks do not stop response or permission processing, including an RPC call
made from a callback. Successful turns wait for their admitted callbacks, and
new turns drain earlier admitted events before resetting the output budget.
`max_output_bytes: None` explicitly permits unbounded output retention. Generic
`JsonRpcPeer` delegates can use `prepare_notification` for synchronous ingress
admission; its default retains all notifications without a queue budget.

RPC turns belong to the session driver until a terminal result arrives. Dropping
the future waiting for a turn does not cancel it, discard its cancellation target,
or admit a second overlapping turn. If a dispatched turn loses its outcome,
`agent_session_recovery_required` prevents another turn on that session; close
the session and reconcile the uncertain outcome before submitting more work.

Generated Pi/OpenCode-style extension hooks are observers: only `Notify` intent
is adapted. `Enforce`, `Inject` and `Rewrite` are reported as `Unsupported` and
produce no observer artifact pretending to implement those semantics.