Expand description
Agent Host Protocol SDK — async client, reducers, and pluggable transports.
AHP is a JSON-RPC protocol that lets a host (an editor, IDE, shell, or test harness) talk to an agent backend through a small set of subscribe / dispatch / reduce primitives. This crate is the Rust implementation of the client side.
For a tour of the protocol itself, see the protocol documentation.
§Crate layout
| Item | Use it for |
|---|---|
Client | Connect to a server, subscribe to resources, dispatch actions |
reducers | Apply StateAction to local state in a fully deterministic way |
Transport | Pluggable trait for any framed message stream |
ClientError / TransportError | Error taxonomy |
§Companion crates
§Quickstart (WebSocket)
Connect over WebSocket, initialize, and stream events from a
session. The example below uses the ahp-ws
crate; replace the transport line with any other Transport
implementation if you have one.
use ahp::{Client, ClientConfig, SubscriptionEvent};
use ahp_ws::WebSocketTransport;
let transport = WebSocketTransport::connect("ws://localhost:12345").await?;
let client = Client::connect(transport, ClientConfig::default()).await?;
client.initialize("my-client".into(), vec!["0.1.0".into()], vec![]).await?;
let (_snap, mut sub) = client.subscribe("ahp-session:/s1".into()).await?;
while let Some(SubscriptionEvent::Action(env)) = sub.recv().await {
println!("seq={} action={:?}", env.server_seq, env.action);
}
client.shutdown().await;§Quickstart (any transport)
The same flow works against any Transport implementation:
use ahp::{Client, ClientConfig, SubscriptionEvent};
let client = Client::connect(transport, ClientConfig::default()).await?;
client.initialize("my-client".into(), vec!["0.1.0".into()], vec!["ahp-root://".into()]).await?;
let mut sub = client.attach_subscription("ahp-root://").await;
while let Some(ev) = sub.recv().await {
match ev {
SubscriptionEvent::Action(a) => println!("seq={}", a.server_seq),
_ => {}
}
}
client.shutdown().await;§Subscriptions
Subscribe to a URI to receive its SubscriptionEvent stream:
ahp-root://— global agent host state (agents, session index)ahp-session:/<id>— a single chat sessionterminal:/<id>— a terminal
Client::subscribe sends a subscribe request and returns the
initial snapshot together with a SessionSubscription handle.
Client::attach_subscription creates a local handle without an
extra round-trip — useful when the URI was already passed to
initialize via initialSubscriptions.
Multiple SessionSubscriptions can fan out from a single URI;
each handle has its own broadcast cursor. Drop the handle to stop
receiving events; call Client::unsubscribe to release the
server-side subscription.
§Dispatching actions
Local UI mutations are dispatched through Client::dispatch. The
client assigns a monotonically increasing clientSeq and sends a
dispatchAction notification. The server eventually echoes the
action back as a normal envelope; reducers can be applied identically
to both sources, so write-ahead state is naturally reconciled.
§Reducers
Reducers are pure functions that translate a StateAction
into mutations on RootState,
SessionState, or
TerminalState.
use ahp::reducers::{apply_action_to_root, ReduceOutcome};
use ahp::ahp_types::actions::{RootActiveSessionsChangedAction, StateAction};
use ahp::ahp_types::state::RootState;
let mut root = RootState {
agents: vec![],
active_sessions: None,
terminals: None,
config: None,
meta: None,
};
let action = StateAction::RootActiveSessionsChanged(
RootActiveSessionsChangedAction { active_sessions: 3 },
);
assert_eq!(apply_action_to_root(&mut root, &action), ReduceOutcome::Applied);
assert_eq!(root.active_sessions, Some(3));Each reducer returns a ReduceOutcome that distinguishes mutations
(ReduceOutcome::Applied), recognized but inert events
(ReduceOutcome::NoOp), and out-of-scope routing
(ReduceOutcome::OutOfScope). A client holding all three state
trees can blindly fan every action out to every reducer without
special-casing.
§Cancellation and shutdown
All async client APIs are cancel-safe at await points. The background
driver is owned by the Client and aborted when the last clone is
dropped, or when Client::shutdown is called. In-flight requests
resolve with ClientError::Shutdown in either case.
Re-exports§
pub use client::Client;pub use client::ClientConfig;pub use client::ClientEvent;pub use client::ClientEventStream;pub use client::DispatchHandle;pub use client::ResourceRequestHandlers;pub use client::ServerRequestFuture;pub use client::ServerRequestHandler;pub use client::SessionSubscription;pub use client::SubscriptionEvent;pub use error::ClientError;pub use error::TransportError;pub use multi_host_state_mirror::HostedResourceKey;pub use multi_host_state_mirror::MultiHostStateMirror;pub use reducers::apply_action_to_root;pub use reducers::apply_action_to_session;pub use reducers::apply_action_to_terminal;pub use reducers::ReduceOutcome;pub use transport::BoxedTransport;pub use transport::DynTransport;pub use transport::Transport;pub use transport::TransportMessage;pub use ahp_types;