TLDR
anyagent has three layers. Your app talks to the top one. The bottom one talks to the agent process. The middle one is where every rule lives.Codebase map
The seam between engine and adapter
The engine and an adapter talk through two private enums. This is the whole contract an adapter has to meet.
An adapter never sends
TurnStarted, TurnEnded, RequestClosed,
SessionUpdated, or StatusChanged as content. The engine owns those and
drops any attempt.
Adapter::connect is the only other thing an adapter implements. It
launches the process, does the handshake, and returns a DriverInfo: the
agent’s details and capabilities, the initial configuration, the resume
token, and whether this wire ends turns with its own frame.
One prompt, end to end
- Attribution. Frames that arrive before
TurnAckbelong to the previous turn and are dropped instead of leaking into the new one. - Request tracking. Every
RequestOpenedis remembered until your answer or a cancel closes it, soNeedsInputandRequestClosedare always right. An answer the request did not offer is rejected without touching the wire. - Promotion. When a turn ends and the queue is not empty, the next
prompt starts immediately, and the status stays
Workingwithout flashingIdle.
How a turn ends
Not every wire says “done”. The engine handles both cases and tells you which one you got throughCompletionSource.
Every shipped adapter ends prompted turns with its own frame. ACP agents
have no frame for agent-originated turns (the agent waking itself after
background work), so those are inferred. Frames that arrive after the end
and carry no new work (a late tool status, a usage update) are applied
without a turn. A subagent’s end frame never ends its parent’s turn.
Native vs ACP
There are two kinds of adapter. The choice per agent is about what the wire can express.
When an agent has both wires installed, discovery prefers the richer one.
Adding an agent
For an ACP agent, skip step 2: the shared adapter already speaks it. A
catalog entry is only needed for verified quirks and login commands.
The conformance suite in
src/adapter/conformance.rs runs the engine over
the mock adapter and pins the contract every adapter benefits from: one
TurnEnded per turn, steer or queue, request lifetimes, cancel, close.
Because the rules live in the engine, a new adapter gets them without
writing any.