Skip to main content

ahp_types/
lib.rs

1//! Wire protocol types for the [Agent Host Protocol (AHP)][spec].
2//!
3//! `ahp-types` is the data-only crate of the AHP family. Every command,
4//! action, notification, and state object defined by the protocol has a
5//! Rust counterpart here — `Serialize` + `Deserialize`, using the exact
6//! same JSON field names as the wire format. There is no I/O, no async
7//! runtime, and no transport code; if you only need to parse or
8//! construct AHP messages, this is the only crate you need.
9//!
10//! These types are generated from the TypeScript source of truth in
11//! `types/*.ts`, so any change to the protocol surfaces here first.
12//!
13//! [spec]: https://microsoft.github.io/agent-host-protocol/
14//!
15//! # Companion crates
16//!
17//! | Crate | What it adds |
18//! |---|---|
19//! | [`ahp`](https://docs.rs/ahp) | Async client, reducers, and pluggable [`Transport`](https://docs.rs/ahp/latest/ahp/transport/trait.Transport.html) trait |
20//! | [`ahp-ws`](https://docs.rs/ahp-ws) | WebSocket transport built on `tokio-tungstenite` |
21//!
22//! # Module map
23//!
24//! | Module | Contents |
25//! |---|---|
26//! | [`state`]         | [`RootState`], [`SessionState`], [`TerminalState`], tool-call lifecycle |
27//! | [`actions`]       | [`StateAction`] discriminated union and [`ActionEnvelope`] |
28//! | [`commands`]      | Request/response parameter and result types (`initialize`, `subscribe`, …) |
29//! | [`notifications`] | Server-pushed protocol notification params ([`SessionAddedParams`], [`AuthRequiredParams`], …) |
30//! | [`messages`]      | JSON-RPC wire envelopes ([`JsonRpcMessage`] and friends) |
31//! | [`errors`]        | AHP and JSON-RPC [error codes][errors::AhpErrorCode] |
32//! | [`version`]       | Negotiation constants ([`PROTOCOL_VERSION`]) |
33//! | [`common`]        | Hand-written primitives ([`Uri`], [`StringOrMarkdown`], …) |
34//!
35//! # Examples
36//!
37//! ## Parse an action envelope
38//!
39//! ```
40//! use ahp_types::actions::{ActionEnvelope, StateAction};
41//!
42//! let json = r#"{
43//!   "channel": "ahp-session:/s1",
44//!   "action": { "type": "session/titleChanged", "title": "Hi" },
45//!   "serverSeq": 7
46//! }"#;
47//! let env: ActionEnvelope = serde_json::from_str(json)?;
48//! assert_eq!(env.server_seq, 7);
49//! assert_eq!(env.channel, "ahp-session:/s1");
50//! match env.action {
51//!     StateAction::SessionTitleChanged(a) => assert_eq!(a.title, "Hi"),
52//!     _ => panic!("unexpected variant"),
53//! }
54//! # Ok::<_, serde_json::Error>(())
55//! ```
56//!
57//! ## Build an `initialize` request
58//!
59//! ```
60//! use ahp_types::commands::{Implementation, InitializeParams};
61//! use ahp_types::messages::{JsonRpcMessage, JsonRpcRequest, JsonRpcVersion};
62//! use ahp_types::common::AnyValue;
63//!
64//! let params = InitializeParams {
65//!     channel: "ahp-root://".into(),
66//!     protocol_versions: vec![ahp_types::PROTOCOL_VERSION.to_string()],
67//!     client_id: "my-host/1.0".into(),
68//!     initial_subscriptions: Some(vec!["ahp-root://".into()]),
69//!     locale: Some("en".into()),
70//!     capabilities: None,
71//!     client_info: Some(Implementation {
72//!         name: "my-host".into(),
73//!         version: Some("1.0.0".into()),
74//!         title: Some("My Host".into()),
75//!     }),
76//! };
77//!
78//! let req = JsonRpcMessage::Request(JsonRpcRequest {
79//!     jsonrpc: JsonRpcVersion::V2,
80//!     id: 1,
81//!     method: "initialize".into(),
82//!     params: Some(AnyValue::from(serde_json::to_value(&params)?)),
83//! });
84//!
85//! let wire = serde_json::to_string(&req)?;
86//! assert!(wire.contains("\"method\":\"initialize\""));
87//! # Ok::<_, serde_json::Error>(())
88//! ```
89//!
90//! ## Inspect a session status bitset
91//!
92//! [`SessionStatus`](state::SessionStatus) packs activity and metadata
93//! flags into a single value — use bitwise checks rather than equality:
94//!
95//! `SessionStatus` is a `u32` bitset newtype: combine flags with `|`, test
96//! membership with [`contains`](state::SessionStatus::contains), and read the
97//! raw value (including unknown/forward-compat bits) with
98//! [`bits`](state::SessionStatus::bits).
99//!
100//! ```
101//! use ahp_types::state::SessionStatus;
102//!
103//! let status = SessionStatus::InProgress | SessionStatus::IsArchived;
104//! assert!(status.contains(SessionStatus::InProgress));
105//! assert!(status.contains(SessionStatus::IsArchived));
106//! assert!(!status.contains(SessionStatus::Idle));
107//! assert_eq!(status.bits(), 8 | 64);
108//! ```
109//!
110//! # Compatibility
111//!
112//! - JSON field names match the protocol exactly (`camelCase`); use the
113//!   provided `Serialize`/`Deserialize` derives rather than manually
114//!   building JSON.
115//! - Unknown enum variants surface as a generic `Unknown` arm where the
116//!   protocol allows forward-compatible extension (see e.g.
117//!   [`state::ToolCallState`]).
118//! - The protocol version this crate speaks is exposed as
119//!   [`PROTOCOL_VERSION`].
120
121#![forbid(unsafe_code)]
122#![warn(missing_docs)]
123#![cfg_attr(docsrs, feature(doc_cfg))]
124
125pub mod actions;
126pub mod commands;
127pub mod common;
128pub mod errors;
129pub mod messages;
130pub mod notifications;
131pub mod state;
132pub mod version;
133
134pub use actions::{ActionEnvelope, ActionOrigin, ActionType, StateAction};
135pub use common::{StringOrMarkdown, Uri, ROOT_RESOURCE_URI};
136pub use errors::{AhpErrorCode, JsonRpcErrorCode};
137pub use messages::{JsonRpcError, JsonRpcErrorResponse, JsonRpcMessage};
138pub use notifications::{
139    AuthRequiredParams, SessionAddedParams, SessionRemovedParams, SessionSummaryChangedParams,
140};
141pub use state::{Icon, ProtectedResourceMetadata, RootState, SessionState, TerminalState};
142pub use version::{PROTOCOL_VERSION, SUPPORTED_PROTOCOL_VERSIONS};