Skip to main content

Crate ahp

Crate ahp 

Source
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

ItemUse it for
ClientConnect to a server, subscribe to resources, dispatch actions
reducersApply StateAction to local state in a fully deterministic way
TransportPluggable trait for any framed message stream
ClientError / TransportErrorError taxonomy

§Companion crates

  • ahp_types — wire types only, no I/O
  • ahp-ws — WebSocket transport built on tokio-tungstenite

§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 session
  • terminal:/<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;

Modules§

client
Async JSON-RPC client.
error
Error types used across the SDK.
hosts
Multi-host client SDK.
multi_host_state_mirror
Host-aware reducer façade for multi-host consumers.
reducers
Pure state reducers ported from types/reducers.ts.
transport
Transport abstraction.