ag_ui_a2ui/lib.rs
1//! A2UI protocol types, semantic validator, and agent-side authoring toolkit.
2//!
3//! [A2UI](https://a2ui.org) is a declarative, agent-driven UI protocol: an agent
4//! streams JSON describing a surface, and a renderer draws it. This crate is the
5//! **agent half** of that exchange.
6//!
7//! # This crate does not render
8//!
9//! Nothing here draws pixels, lays out a tree, or evaluates a UI at runtime. It
10//! produces A2UI, validates it, and transports it. Rendering is the client's
11//! job, and it is a genuinely different program — one with a widget toolkit, an
12//! event loop, and a reactive data model. What this crate gives you instead:
13//!
14//! - [`message`] — the ten protocol envelopes, in both directions.
15//! - [`catalog`] — what a surface may contain, including the standard
16//! 18-component basic catalog.
17//! - [`validate`] — the semantic checks JSON Schema cannot express: does every
18//! child reference resolve, is there a root, is the tree acyclic. Plus the two
19//! a generating model gets wrong often enough to be worth checking without a
20//! schema engine: the message envelope, and property values against the type
21//! the catalog declares.
22//! - [`binding`] — JSON Pointer resolution, template scopes, and the
23//! `formatString` interpolation grammar, so an agent can check its own
24//! bindings before shipping them.
25// The bullets below name modules that only exist behind their feature, and a
26// doc link to a module that is not compiled is a rustdoc *error*, not a dead
27// link — so `cargo doc --no-default-features` failed on this block for as long
28// as it existed. Gating the text rather than deleting the links keeps every
29// link live in the `--all-features` build that docs.rs and the docs site
30// publish, and keeps the feature-off build documentable, which matters because
31// that build is exactly what this crate advertises for A2A and MCP. The
32// `doc-features` job in CI is what stops it drifting back; nothing caught it
33// before, because the docs job only ever ran `--all-features` and the features
34// job used `cargo check`, which does not resolve intra-doc links at all.
35#![cfg_attr(
36 feature = "toolkit",
37 doc = "- [`toolkit`] (feature `toolkit`) — building ops, negotiating a catalog,",
38 doc = " assembling prompts, parsing a model's output as it streams, recovering a",
39 doc = " surface from conversation history, and the validate-and-retry loop around",
40 doc = " a generating model."
41)]
42#![cfg_attr(
43 feature = "ag-ui",
44 doc = "- [`agui`] (feature `ag-ui`) — the glue for an agent hosted on AG-UI:",
45 doc = " history entries from [`ag_ui::Message`](https://kimsoungryoul.github.io/ag-ui-rust/api/ag_ui/message/enum.Message.html), toolkit tool definitions as",
46 doc = " offerable [`ag_ui::Tool`](https://kimsoungryoul.github.io/ag-ui-rust/api/ag_ui/tool/struct.Tool.html)s."
47)]
48//!
49//! # Transport
50//!
51//! A2UI says nothing about how messages reach the renderer, and everything
52//! outside one module keeps it that way — turn the `ag-ui` feature off and the
53//! dependency goes with it, leaving a crate you can drive over A2A or MCP. That
54//! module is
55#![cfg_attr(feature = "ag-ui", doc = "[`agui`].")]
56#![cfg_attr(
57 not(feature = "ag-ui"),
58 doc = "`agui`, and this build does not have it."
59)]
60//! What every toolkit does in practice is wrap a batch of operations in a
61//! `{"a2ui_operations": [...]}` envelope and let the frontend sniff for that
62//! key.
63#![cfg_attr(
64 feature = "toolkit",
65 doc = "[`toolkit::envelope`] produces exactly that, as a plain JSON string that",
66 doc = "fits in an AG-UI assistant message, an A2A data part, or an MCP tool result",
67 doc = "without further wrapping."
68)]
69//!
70//! # Conformance
71//!
72//! The A2UI project publishes a language-agnostic conformance suite as YAML.
73//! It is vendored under `tests/conformance/` and run as a normal test; the
74//! report prints what passed, what was skipped, and why. See the README there
75//! for the current standing.
76//!
77//! # Version
78//!
79//! Messages are stamped `v0.9`. The specification has moved on to v1.0, but the
80//! shipping toolkits in every other language still speak v0.9 on the wire, and
81//! interoperating with them matters more than tracking the newest revision. See
82//! [`constants`] before changing anything there.
83//!
84//! # Example
85//!
86//! ```
87//! use ag_ui_a2ui::{Catalog, Component, Validator};
88//! use serde_json::json;
89//!
90//! let catalog = Catalog::basic();
91//! let components = vec![
92//! Component::new("root", "Card").with("child", json!("greeting")),
93//! Component::new("greeting", "Text").with("text", json!("Hello!")),
94//! ];
95//!
96//! let report = Validator::new(&catalog).validate(&components);
97//! assert!(report.is_valid());
98//! ```
99
100#![forbid(unsafe_code)]
101#![warn(missing_docs)]
102#![warn(missing_debug_implementations)]
103// See `ag_ui`'s lib.rs: marks feature-gated items in the rendered docs.
104#![cfg_attr(docsrs, feature(doc_cfg))]
105
106// `readme = "README.md"` in Cargo.toml makes that file the crate's front page
107// wherever the package is presented, so its examples are doctested: a stale one
108// is a red build rather than a bad first impression. `cfg(doctest)` is what
109// keeps this module out of the rendered docs — it compiles the examples rather
110// than publishing them.
111#[cfg(doctest)]
112#[doc = include_str!("../README.md")]
113mod readme {}
114
115pub mod binding;
116pub mod catalog;
117pub mod constants;
118pub mod error;
119pub mod message;
120pub mod validate;
121
122#[cfg(feature = "toolkit")]
123pub mod toolkit;
124
125#[cfg(feature = "ag-ui")]
126pub mod agui;
127
128// The front door: what a caller producing or checking A2UI reaches for first.
129// Everything else stays behind its module, because the modules are the map.
130pub use catalog::Catalog;
131pub use error::{Error, Result, ValidationErrors};
132pub use message::{
133 AgentMessage, AgentPayload, ChildList, ChildTemplate, Component, RendererMessage,
134 RendererPayload,
135};
136pub use validate::{ErrorCode, ValidateOptions, ValidationError, ValidationReport, Validator};
137
138#[cfg(feature = "toolkit")]
139pub use toolkit::{StreamParser, wrap_as_operations_envelope, wrap_error_envelope};
140
141#[cfg(feature = "ag-ui")]
142pub use agui::find_prior_surface_in;