diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 2a79b9bb1..3d331d139 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -73,14 +73,7 @@ jobs: run: pip install pytest && pytest docs/scripts/test_convert_docs.py - name: Copy schemas to documentation - run: | - cp -R specification/* docs/specification/ - # Flatten JSON files to version roots to support clean URLs without redirects - for version in v0_8 v0_9; do - if [ -d "docs/specification/$version/json" ]; then - cp docs/specification/$version/json/*.json docs/specification/$version/ - fi - done + run: python docs/scripts/prepare_docs.py - name: Convert Admonitions in Documentation run: python docs/scripts/convert_docs.py diff --git a/.gitignore b/.gitignore index 9af10ea4c..91ced783d 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,7 @@ site/ # Python virtual environment .venv/ + +# Copied specification files +docs/specification/v*/ + diff --git a/docs/concepts/components.md b/docs/concepts/components.md index 877d44167..636cce3de 100644 --- a/docs/concepts/components.md +++ b/docs/concepts/components.md @@ -21,15 +21,17 @@ A2UI uses an **adjacency list model** for component hierarchies. Instead of nest ```json { - "surfaceUpdate": { + "version": "v0.9", + "updateComponents": { + "surfaceId": "main", "components": [ - {"id": "root", "component": {"Column": {"children": {"explicitList": ["greeting", "buttons"]}}}}, - {"id": "greeting", "component": {"Text": {"text": {"literalString": "Hello"}}}}, - {"id": "buttons", "component": {"Row": {"children": {"explicitList": ["cancel-btn", "ok-btn"]}}}}, - {"id": "cancel-btn", "component": {"Button": {"child": "cancel-text", "action": {"name": "cancel"}}}}, - {"id": "cancel-text", "component": {"Text": {"text": {"literalString": "Cancel"}}}}, - {"id": "ok-btn", "component": {"Button": {"child": "ok-text", "action": {"name": "ok"}}}}, - {"id": "ok-text", "component": {"Text": {"text": {"literalString": "OK"}}}} + {"id": "root", "component": "Column", "children": ["greeting", "buttons"]}, + {"id": "greeting", "component": "Text", "text": "Hello"}, + {"id": "buttons", "component": "Row", "children": ["cancel-btn", "ok-btn"]}, + {"id": "cancel-btn", "component": "Button", "child": "cancel-text", "action": {"name": "cancel"}}, + {"id": "cancel-text", "component": "Text", "text": "Cancel"}, + {"id": "ok-btn", "component": "Button", "child": "ok-text", "action": {"name": "ok"}}, + {"id": "ok-text", "component": "Text", "text": "OK"} ] } } @@ -46,7 +48,7 @@ Every component has: 3. **Properties**: Configuration specific to that type ```json -{"id": "welcome", "component": {"Text": {"text": {"literalString": "Hello"}, "usageHint": "h1"}}} +{"id": "welcome", "component": "Text", "text": "Hello", "variant": "h1"} ``` ## The Standard Catalog @@ -62,14 +64,14 @@ For the complete component gallery with examples, see [Component Reference](../r ## Static vs. Dynamic Children -**Static (`explicitList`)** - Fixed list of child IDs: +**Static (`children` array)** - Fixed list of child IDs: ```json -{"children": {"explicitList": ["back-btn", "title", "menu-btn"]}} +{"children": ["back-btn", "title", "menu-btn"]} ``` -**Dynamic (`template`)** - Generate children from data array: +**Dynamic (`children` template)** - Generate children from data array: ```json -{"children": {"template": {"dataBinding": "/items", "componentId": "item-template"}}} +{"children": {"template": {"path": "/items", "componentId": "item-template"}}} ``` For each item in `/items`, render the `item-template`. See [Data Binding](data-binding.md) for details. @@ -78,26 +80,26 @@ For each item in `/items`, render the `item-template`. See [Data Binding](data-b Components get their values two ways: -- **Literal** - Fixed value: `{"text": {"literalString": "Welcome"}}` -- **Data-bound** - From data model: `{"text": {"path": "/user/name"}}` - -LLMs can generate components with literal values or bind them to data paths for dynamic content. - -## Composing Surfaces - -Components compose into **surfaces** (widgets): - -1. LLM generates component definitions via `surfaceUpdate` -2. LLM populates data via `dataModelUpdate` -3. LLM signals render via `beginRendering` -4. Client renders all components as native widgets - -A surface is a complete, cohesive UI (form, dashboard, chat, etc.). - -## Incremental Updates - -- **Add** - Send new `surfaceUpdate` with new component IDs -- **Update** - Send `surfaceUpdate` with existing ID and new properties +81: - **Literal** - Fixed value: `{"text": "Welcome"}` +82: - **Data-bound** - From data model: `{"text": {"path": "/user/name"}}` +83: +84: LLMs can generate components with literal values or bind them to data paths. +85: +86: ## Composing Surfaces +87: +88: Components compose into **surfaces** (widgets): +89: +90: 1. LLM generates component definitions via `updateComponents` +91: 2. LLM populates data via `updateDataModel` +92: 3. LLM signals render via `createSurface` +93: 4. Client renders all components as native widgets +94: +95: A surface is a complete, cohesive UI (form, dashboard, chat, etc.). +96: +97: ## Incremental Updates +98: +99: - **Add** - Send new `updateComponents` with new component IDs +100: - **Update** - Send `updateComponents` with existing ID and new properties - **Remove** - Update parent's `children` list to exclude removed IDs The flat structure makes all updates simple ID-based operations. diff --git a/docs/concepts/data-binding.md b/docs/concepts/data-binding.md index a97448d6d..3fce2ef1a 100644 --- a/docs/concepts/data-binding.md +++ b/docs/concepts/data-binding.md @@ -40,18 +40,19 @@ Each surface has a JSON object holding state: ``` - `/user/name` → `"Alice"` -- `/items/0` → `"Apple"` +- `/user/name` → `"Alice"` +- `/items/0` → `"Apple"` ## Literal vs. Path Values **Literal (fixed):** ```json -{"id": "title", "component": {"Text": {"text": {"literalString": "Welcome"}}}} +{"id": "title", "component": "Text", "text": "Welcome"} ``` **Data-bound (reactive):** ```json -{"id": "username", "component": {"Text": {"text": {"path": "/user/name"}}}} +{"id": "username", "component": "Text", "text": {"path": "/user/name"}} ``` When `/user/name` changes from "Alice" to "Bob", the text **automatically updates** to "Bob". @@ -61,11 +62,11 @@ When `/user/name` changes from "Alice" to "Bob", the text **automatically update Components bound to data paths automatically update when the data changes: ```json -{"id": "status", "component": {"Text": {"text": {"path": "/order/status"}}}} +{"id": "status", "component": "Text", "text": {"path": "/order/status"}} ``` -- **Initial:** `/order/status` = "Processing..." → displays "Processing..." -- **Update:** Send `dataModelUpdate` with `status: "Shipped"` → displays "Shipped" +- **Initial:** `/order/status` = "Processing..." → displays "Processing..." +- **Update:** Send `updateDataModel` with `status: "Shipped"` → displays "Shipped" No component updates needed—just data updates. @@ -76,11 +77,8 @@ Use templates to render arrays: ```json { "id": "product-list", - "component": { - "Column": { - "children": {"template": {"dataBinding": "/products", "componentId": "product-card"}} - } - } + "component": "Column", + "children": {"template": {"path": "/products", "componentId": "product-card"}} } ``` @@ -96,11 +94,11 @@ Use templates to render arrays: Inside a template, paths are scoped to the array item: ```json -{"id": "product-name", "component": {"Text": {"text": {"path": "/name"}}}} +{"id": "product-name", "component": "Text", "text": {"path": "/name"}} ``` -- For `/products/0`, `/name` resolves to `/products/0/name` → "Widget" -- For `/products/1`, `/name` resolves to `/products/1/name` → "Gadget" +- For `/products/0`, `/name` resolves to `/products/0/name` → "Widget" +- For `/products/1`, `/name` resolves to `/products/1/name` → "Gadget" Adding/removing items automatically updates the rendered components. @@ -110,15 +108,15 @@ Interactive components update the data model bidirectionally: | Component | Example | User Action | Data Update | |-----------|---------|-------------|-------------| -| **TextField** | `{"text": {"path": "/form/name"}}` | Types "Alice" | `/form/name` = "Alice" | +| **TextField** | `{"value": {"path": "/form/name"}}` | Types "Alice" | `/form/name` = "Alice" | | **CheckBox** | `{"value": {"path": "/form/agreed"}}` | Checks box | `/form/agreed` = true | -| **MultipleChoice** | `{"selections": {"path": "/form/country"}}` | Selects "Canada" | `/form/country` = ["ca"] | +| **ChoicePicker** | `{"value": {"path": "/form/country"}}` | Selects "Canada" | `/form/country` = ["ca"] | ## Best Practices **1. Use granular updates** - Update only changed paths: ```json -{"dataModelUpdate": {"path": "/user", "contents": [{"key": "name", "valueString": "Alice"}]}} +{"version": "v0.9", "updateDataModel": {"surfaceId": "main", "path": "/user", "value": {"name": "Alice"}}} ``` **2. Organize by domain** - Group related data: @@ -130,3 +128,9 @@ Interactive components update the data model bidirectionally: ```json {"price": "$19.99"} // Not: {"price": 19.99} ``` + +**4. Use Client-Side Formatting** - For dynamic localization or complex updates, use client-side functions: +```json +{"text": {"call": "formatCurrency", "args": {"value": 19.99, "currency": "USD"}}} +``` +See the **[Functions Reference](../reference/functions.md)** for a complete list of available functions. diff --git a/docs/concepts/data-flow.md b/docs/concepts/data-flow.md index 99fe1bc8d..72030f5b5 100644 --- a/docs/concepts/data-flow.md +++ b/docs/concepts/data-flow.md @@ -17,9 +17,9 @@ Client (Stream Reader) → Message Parser → Renderer → Native UI A2UI defines a sequence of JSON messages that describe the UI. When streamed, these messages are often formatted as **JSON Lines (JSONL)**, where each line is a complete JSON object. ```jsonl -{"surfaceUpdate":{"surfaceId":"main","components":[...]}} -{"dataModelUpdate":{"surfaceId":"main","contents":[{"key":"user","valueMap":[{"key":"name","valueString":"Alice"}]}]}} -{"beginRendering":{"surfaceId":"main","root":"root-component"}} +{"version": "v0.9", "createSurface": {"surfaceId": "main", "catalogId": "..."}} +{"version": "v0.9", "updateComponents": {"surfaceId": "main", "components": [...]}} +{"version": "v0.9", "updateDataModel": {"surfaceId": "main", "value": {"user": "Alice"}}} ``` **Why this format?** @@ -30,30 +30,44 @@ A sequence of self-contained JSON objects is streaming-friendly, easy for LLMs t **User:** "Book a table for 2 tomorrow at 7pm" -**1. Agent defines UI structure:** +**1. Agent signals render (v0.9 starts with creation):** ```json -{"surfaceUpdate": {"surfaceId": "booking", "components": [ - {"id": "root", "component": {"Column": {"children": {"explicitList": ["header", "guests-field", "submit-btn"]}}}}, - {"id": "header", "component": {"Text": {"text": {"literalString": "Confirm Reservation"}, "usageHint": "h1"}}}, - {"id": "guests-field", "component": {"TextField": {"label": {"literalString": "Guests"}, "text": {"path": "/reservation/guests"}}}}, - {"id": "submit-btn", "component": {"Button": {"child": "submit-text", "action": {"name": "confirm", "context": [{"key": "details", "value": {"path": "/reservation"}}]}}}} -]}} +{"version": "v0.9", "createSurface": {"surfaceId": "booking", "catalogId": "https://a2ui.org/specification/v0_9/standard_catalog.json"}} ``` -**2. Agent populates data:** +**2. Agent defines UI structure:** ```json -{"dataModelUpdate": {"surfaceId": "booking", "path": "/reservation", "contents": [ - {"key": "datetime", "valueString": "2025-12-16T19:00:00Z"}, - {"key": "guests", "valueString": "2"} -]}} +{ + "version": "v0.9", + "updateComponents": { + "surfaceId": "booking", + "components": [ + {"id": "root", "component": "Column", "children": ["header", "guests-field", "submit-btn"]}, + {"id": "header", "component": "Text", "text": "Confirm Reservation", "variant": "h1"}, + {"id": "guests-field", "component": "TextField", "label": "Guests", "value": {"path": "/reservation/guests"}}, + {"id": "submit-btn", "component": "Button", "child": "submit-text", "action": {"name": "confirm", "context": {"path": "/reservation"}}}, + {"id": "submit-text", "component": "Text", "text": "Confirm"} + ] + } +} ``` -**3. Agent signals render:** +**3. Agent populates data:** ```json -{"beginRendering": {"surfaceId": "booking", "root": "root"}} +{ + "version": "v0.9", + "updateDataModel": { + "surfaceId": "booking", + "path": "/reservation", + "value": { + "datetime": "2025-12-16T19:00:00Z", + "guests": 2 + } + } +} ``` **4. User edits guests to "3"** → Client updates `/reservation/guests` automatically (no message to agent yet) @@ -61,10 +75,10 @@ A sequence of self-contained JSON objects is streaming-friendly, easy for LLMs t **5. User clicks "Confirm"** → Client sends action with updated data: ```json -{"userAction": {"name": "confirm", "surfaceId": "booking", "context": {"details": {"datetime": "2025-12-16T19:00:00Z", "guests": "3"}}}} +{"action": {"name": "confirm", "surfaceId": "booking", "context": {"datetime": "2025-12-16T19:00:00Z", "guests": 3}}} ``` -**6. Agent responds** → Updates UI or sends `{"deleteSurface": {"surfaceId": "booking"}}` to clean up +**6. Agent responds** → Updates UI or sends `{"version": "v0.9", "deleteSurface": {"surfaceId": "booking"}}` to clean up ## Transport Options diff --git a/docs/concepts/overview.md b/docs/concepts/overview.md index af1a6b2b5..600dda368 100644 --- a/docs/concepts/overview.md +++ b/docs/concepts/overview.md @@ -25,9 +25,9 @@ How components connect to application state using JSON Pointer paths. Covers rea A2UI uses four message types: -- **`surfaceUpdate`**: Define or update UI components -- **`dataModelUpdate`**: Update application state -- **`beginRendering`**: Signal the client to render +- **`createSurface`**: Initialize a surface and catalog +- **`updateComponents`**: Define or update UI components +- **`updateDataModel`**: Update application state - **`deleteSurface`**: Remove a UI surface For complete technical details, see [Message Reference](../reference/messages.md). diff --git a/docs/guides/agent-development.md b/docs/guides/agent-development.md index 1215af8ba..9510bce95 100644 --- a/docs/guides/agent-development.md +++ b/docs/guides/agent-development.md @@ -76,7 +76,7 @@ root_agent = Agent( ) ``` -Don't forget to set the `GOOGLE_API_KEY` environment variable to run this example. +Don't forget to set the `GOOGLE_API_KEY` environment variable to run this example. ```bash echo 'GOOGLE_API_KEY="YOUR_API_KEY"' > .env @@ -92,7 +92,7 @@ Select `my_agent` from the list, and ask questions about restaurants in New York ## Generating A2UI Messages -Getting the LLM to generate A2UI messages requires some prompt engineering. +Getting the LLM to generate A2UI messages requires some prompt engineering. > ⚠️ **Attention** > @@ -131,7 +131,7 @@ To generate the response, you MUST follow these rules: 4. The JSON part MUST validate against the A2UI JSON SCHEMA provided below. --- UI TEMPLATE RULES --- -- If the query is for a list of restaurants, use the restaurant data you have already received from the `get_restaurants` tool to populate the `dataModelUpdate.contents` array (e.g., as a `valueMap` for the "items" key). +- If the query is for a list of restaurants, use the restaurant data you have already received from the `get_restaurants` tool to populate the `updateDataModel.contents` object (e.g., set the "items" key to the list of restaurants). - If the number of restaurants is 5 or fewer, you MUST use the `SINGLE_COLUMN_LIST_EXAMPLE` template. - If the number of restaurants is more than 5, you MUST use the `TWO_COLUMN_LIST_EXAMPLE` template. - If the query is to book a restaurant (e.g., "USER_WANTS_TO_BOOK..."), you MUST use the `BOOKING_FORM_EXAMPLE` template. @@ -159,8 +159,9 @@ Your agent will no longer strictly output text. Instead, it will output text and The `A2UI_SCHEMA` that we imported is a standard JSON schema that defines valid operations like: -* `render` (displaying a UI) -* `update` (changing data in an existing UI) +* `createSurface` (displaying a UI) +* `updateComponents` (changing the UI structure) +* `updateDataModel` (changing data in an existing UI) Because the output is structured JSON, you may parse and validate it before sending it to the client. diff --git a/docs/guides/client-setup.md b/docs/guides/client-setup.md index 03e88f74a..fa9986417 100644 --- a/docs/guides/client-setup.md +++ b/docs/guides/client-setup.md @@ -94,7 +94,7 @@ TODO: Add action handling examples. ## Error Handling Common errors to handle: -- **Invalid Surface ID**: Surface referenced before `beginRendering` was received +- **Invalid Surface ID**: Surface referenced before `createSurface` was received - **Invalid Component ID**: Component IDs must be unique within a surface - **Invalid Data Path**: Check data model structure and JSON Pointer syntax - **Schema Validation Failed**: Verify message format matches A2UI specification diff --git a/docs/guides/custom-components.md b/docs/guides/custom-components.md index 9582021f1..d29a305fe 100644 --- a/docs/guides/custom-components.md +++ b/docs/guides/custom-components.md @@ -23,7 +23,7 @@ You register entire catalogs with your client application, not individual compon 2. **Client Registers Catalog**: You register the catalog (and its component implementations) with your client app. 3. **Client Announces Support**: The client informs the agent which catalogs it supports. 4. **Agent Selects Catalog**: The agent chooses a catalog for a given UI surface. -5. **Agent Generates UI**: The agent generates `surfaceUpdate` messages using components from that catalog by name. +5. **Agent Generates UI**: The agent generates `updateComponents` messages using components from that catalog by name. ## Defining Custom Catalogs @@ -45,9 +45,9 @@ TODO: Add detailed guide for defining custom catalogs for each platform. ## Agent-Side: Using Components from a Custom Catalog -Once a catalog is registered on the client, agents can use components from it in `surfaceUpdate` messages. +Once a catalog is registered on the client, agents can use components from it in `updateComponents` messages. -The agent specifies which catalog to use via the `catalogId` in the `beginRendering` message. +The agent specifies which catalog to use via the `catalogId` in the `createSurface` message. TODO: Add examples of: - How agents select catalogs diff --git a/docs/guides/renderer-development.md b/docs/guides/renderer-development.md index 7dd1d2ad7..a97161739 100644 --- a/docs/guides/renderer-development.md +++ b/docs/guides/renderer-development.md @@ -9,44 +9,43 @@ This section details the fundamental mechanics of the A2UI protocol. A compliant ### Message Processing & State Management - **JSONL Stream Parsing**: Implement a parser that can read a streaming response line by line, decoding each line as a distinct JSON object. -- **Message Dispatcher**: Create a dispatcher to identify the message type (`beginRendering`, `surfaceUpdate`, `dataModelUpdate`, `deleteSurface`) and route it to the correct handler. +- **Message Dispatcher**: Create a dispatcher to identify the message type (`createSurface`, `updateComponents`, `updateDataModel`, `deleteSurface`) and route it to the correct handler. - **Surface Management**: - Implement a data structure to manage multiple UI surfaces, each keyed by its `surfaceId`. - - Handle `surfaceUpdate`: Add or update components in the specified surface's component buffer. + - Handle `createSurface`: Initialize the surface state and catalog. + - Handle `updateComponents`: Add or update components in the specified surface's component buffer. Flattened components should be stored by ID. - Handle `deleteSurface`: Remove the specified surface and all its associated data and components. -- **Component Buffering (Adjacency List)**: - - For each surface, maintain a component buffer (e.g., a `Map`) to store all component definitions by their `id`. - - Be able to reconstruct the UI tree at render time by resolving `id` references in container components (`children.explicitList`, `child`, `contentChild`, etc.). +- **Component Buffering**: + - For each surface, maintain a component buffer to store all component definitions by their `id`. + - Be able to reconstruct the UI tree at render time by resolving `id` references in container components (`children`). - **Data Model Store**: - - For each surface, maintain a separate data model store (e.g., a JSON object or a `Map`). - - Handle `dataModelUpdate`: Update the data model at the specified `path`. The `contents` will be in an adjacency list format (e.g., `[{ "key": "name", "valueString": "Bob" }]`). + - For each surface, maintain a separate data model store (e.g., a JSON object). + - Handle `updateDataModel`: Update the data model at the specified `path`. If `value` is an object, merge it; otherwise replace. ### Rendering Logic - **Progressive Rendering Control**: - - Buffer all incoming `surfaceUpdate` and `dataModelUpdate` messages without rendering immediately. - - Handle `beginRendering`: This message acts as the explicit signal to perform the initial render of a surface and set the root component ID. - - Start rendering from the specified `root` component ID. - - If a `catalogId` is provided, ensure the corresponding component catalog is used (defaulting to the standard catalog if omitted). - - Apply any global `styles` (e.g., `font`, `primaryColor`) provided in this message. + - Buffer all incoming messages. + - Handle `createSurface`: This message acts as the explicit signal to create the surface. + - If a `catalogId` is provided, ensure the corresponding component catalog is used. + - Apply any global `theme` provided in this message. - **Data Binding Resolution**: - - Implement a resolver for `BoundValue` objects found in component properties. - - If only a `literal*` value is present (`literalString`, `literalNumber`, etc.), use it directly. - - If only a `path` is present, resolve it against the surface's data model. - - If both `path` and `literal*` are present, first update the data model at `path` with the literal value, then bind the component property to that `path`. + - Implement a resolver for properties found in component definitions. + - If the value is a direct primitive/object, use it directly. + - If the value is a {path: ...} object, resolve it against the surface's data model. - **Dynamic List Rendering**: - - For containers with a `children.template`, iterate over the data list found at `template.dataBinding` (which resolves to a list in the data model). + - For containers with a `children.template`, iterate over the data list found at `template.path` (which resolves to a list in the data model). - For each item in the data list, render the component specified by `template.componentId`, making the item's data available for relative data binding within the template. ### Client-to-Server Communication - **Event Handling**: - - When a user interacts with a component that has an `action` defined, construct a `userAction` payload. + - When a user interacts with a component that has an `action` defined, construct an `action` payload. - Resolve all data bindings within the `action.context` against the data model. - - Send the complete `userAction` object to the server's event handling endpoint. + - Send the complete `action` object to the server's event handling endpoint. - **Client Capabilities Reporting**: - In **every** A2A message sent to the server (as part of the metadata), include an `a2uiClientCapabilities` object. - - This object should declare the component catalog your client supports via `supportedCatalogIds` (e.g., including the URI for the standard 0.8 catalog). + - This object should declare the component catalog your client supports via `supportedCatalogIds` (e.g., including the URI for the standard 0.9 catalog). - Optionally, if the server supports it, provide `inlineCatalogs` for custom, on-the-fly component definitions. - **Error Reporting**: Implement a mechanism to send an `error` message to the server to report any client-side errors (e.g., failed data binding, unknown component type). @@ -56,8 +55,8 @@ To ensure a consistent user experience across platforms, A2UI defines a standard ### Basic Content -- **Text**: Render text content. Must support data binding on `text` and a `usageHint` for styling (h1-h5, body, caption). -- **Image**: Render an image from a URL. Must support `fit` (cover, contain, etc.) and `usageHint` (avatar, hero, etc.) properties. +- **Text**: Render text content. Must support data binding on `text` and a `variant` for styling (h1-h5, body, caption). +- **Image**: Render an image from a URL. Must support `fit` (cover, contain, etc.) and `variant` (avatar, hero, etc.) properties. - **Icon**: Render a predefined icon from the standard set specified in the catalog. - **Video**: Render a video player for a given URL. - **AudioPlayer**: Render an audio player for a given URL, optionally with a description. @@ -65,18 +64,18 @@ To ensure a consistent user experience across platforms, A2UI defines a standard ### Layout & Containers -- **Row**: Arrange children horizontally. Must support `distribution` (justify-content) and `alignment` (align-items). Children can have a `weight` property to control flex-grow behavior. -- **Column**: Arrange children vertically. Must support `distribution` and `alignment`. Children can have a `weight` property to control flex-grow behavior. -- **List**: Render a scrollable list of items. Must support `direction` (`horizontal`/`vertical`) and `alignment`. +- **Row**: Arrange children horizontally. Must support `justify` (justify-content) and `align` (align-items). Children can have a `weight` property to control flex-grow behavior. +- **Column**: Arrange children vertically. Must support `justify` and `align`. Children can have a `weight` property to control flex-grow behavior. +- **List**: Render a scrollable list of items. Must support `direction` (`horizontal`/`vertical`) and `align`. - **Card**: A container that visually groups its child content, typically with a border, rounded corners, and/or shadow. Has a single `child`. -- **Tabs**: A container that displays a set of tabs. Includes `tabItems`, where each item has a `title` and a `child`. -- **Modal**: A dialog that appears on top of the main content. It is triggered by an `entryPointChild` (e.g. a button) and displays the `contentChild` when activated. +- **Tabs**: A container that displays a set of tabs. Includes `tabs`, where each item has a `title` and a `child`. +- **Modal**: A dialog that appears on top of the main content. It is triggered by a `trigger` (e.g. a button) and displays the `content` when activated. ### Interactive & Input Components -- **Button**: A clickable element that triggers a `userAction`. Must be able to contain a `child` component (typically Text or Icon) and may vary in style based on the `primary` boolean. +- **Button**: A clickable element that triggers an `action`. Must be able to contain a `child` component (typically Text or Icon) and may vary in style based on the `variant` (primary, borderless). - **CheckBox**: A checkbox that can be toggled, reflecting a boolean value. -- **TextField**: An input field for text. Must support a `label`, `text` (value), `textFieldType` (`shortText`, `longText`, `number`, `obscured`, `date`), and `validationRegexp`. +- **TextField**: An input field for text. Must support a `label`, `value`, `variant` (`shortText`, `longText`, `number`, `obscured`), and `validationRegexp`. - **DateTimeInput**: A dedicated input for selecting a date and/or time. Must support `enableDate` and `enableTime`. -- **MultipleChoice**: A component for selecting one or more options from a list (`options`). Must support `maxAllowedSelections` and bind `selections` to a list or single value. -- **Slider**: A slider for selecting a numeric value (`value`) from a defined range (`minValue`, `maxValue`). +- **ChoicePicker**: A component for selecting one or more options from a list (`options`). Must support `variant` (`multipleSelection`, `mutuallyExclusive`) and bind `value` to a list (even for single selection). +- **Slider**: A slider for selecting a numeric value (`value`) from a defined range (`min`, `max`). diff --git a/docs/guides/theming.md b/docs/guides/theming.md index 5a37f5d85..35e88adf3 100644 --- a/docs/guides/theming.md +++ b/docs/guides/theming.md @@ -22,7 +22,7 @@ A2UI styling works in layers: ```mermaid flowchart TD - A["1. Semantic Hints
Agent provides hints
(e.g., usageHint: 'h1')"] + A["1. Semantic Hints
Agent provides hints
(e.g., variant: 'h1')"] B["2. Theme Configuration
You configure
(colors, fonts, spacing)"] C["3. Component Overrides
You customize
(CSS/styles for specific components)"] D["4. Rendered Output
Native platform widgets"] @@ -37,16 +37,13 @@ Agents provide semantic hints (not visual styles) to guide client rendering: ```json { "id": "title", - "component": { - "Text": { - "text": {"literalString": "Welcome"}, - "usageHint": "h1" - } - } + "component": "Text", + "text": "Welcome", + "variant": "h1" } ``` -**Common `usageHint` values:** +**Common `variant` values:** - Text: `h1`, `h2`, `h3`, `h4`, `h5`, `body`, `caption` - Other components have their own hints (see [Component Reference](../reference/components.md)) @@ -130,28 +127,22 @@ TODO: Add custom font examples. ### 1. Use Semantic Hints, Not Visual Properties -Agents should provide semantic hints (`usageHint`), never visual styles: +Agents should provide semantic hints (`variant`), never visual styles: ```json // ✅ Good: Semantic hint { - "component": { - "Text": { - "text": {"literalString": "Welcome"}, - "usageHint": "h1" - } - } + "component": "Text", + "text": "Welcome", + "variant": "h1" } // ❌ Bad: Visual properties (not supported) { - "component": { - "Text": { - "text": {"literalString": "Welcome"}, - "fontSize": 24, - "color": "#FF0000" - } - } + "component": "Text", + "text": "Welcome", + "fontSize": 24, + "color": "#FF0000" } ``` diff --git a/docs/index.md b/docs/index.md index 1f0122bfe..bf8e4032c 100644 --- a/docs/index.md +++ b/docs/index.md @@ -20,16 +20,16 @@ A2UI enables AI agents to generate rich, interactive user interfaces that render -> ⚠️ **️Status: Early Stage Public Preview** +> ⚠️ **️Status: Active Development** > -> A2UI is currently in **v0.8 (Public Preview)**. The specification and -> implementations are functional but are still evolving. We are opening the project to +> A2UI is currently at version **v0.9**. The specification and +> implementations are still evolving. We are opening the project to > foster collaboration, gather feedback, and solicit contributions (e.g., on client renderers). > Expect changes. ## At a Glance -A2UI is currently [v0.8](specification/v0.8-a2ui.md), +A2UI is currently [v0.9](specification/v0.9-a2ui.md), Apache 2.0 licensed, created by Google with contributions from CopilotKit and the open source community, and is in active development [on GitHub](https://github.com/google/A2UI). @@ -39,10 +39,10 @@ The problem A2UI solves is: **how can AI agents safely send rich UIs across trus Instead of text-only responses or risky code execution, A2UI lets agents send **declarative component descriptions** that clients render using their own native widgets. It's like having agents speak a universal UI language. In this repo you will find -[A2UI specifications](specification/v0.8-a2ui.md) +[A2UI specifications](specification/v0.9-a2ui.md) and implementations for [renderers](renderers.md) (eg: Angular, Flutter, etc.) on the client side, -and [transports](/transports.md) (eg: A2A, etc.) which communicate A2UI messages between agents and clients. +and [transports](transports.md) (eg: A2A, etc.) which communicate A2UI messages between agents and clients.
@@ -100,13 +100,13 @@ and [transports](/transports.md) (eg: A2A, etc.) which communicate A2UI messages [:octicons-arrow-right-24: Start building](guides/client-setup.md) -- :material-file-document:{ .lg .middle } **[Protocol Reference](specification/v0.8-a2ui.md)** +- :material-file-document:{ .lg .middle } **[Protocol Reference](specification/v0.9-a2ui.md)** --- Dive into the complete technical specification and message types. - [:octicons-arrow-right-24: Read the spec](specification/v0.8-a2ui.md) + [:octicons-arrow-right-24: Read the spec](specification/v0.9-a2ui.md)
diff --git a/docs/introduction/what-is-a2ui.md b/docs/introduction/what-is-a2ui.md index ed3d68e52..db62e4fdd 100644 --- a/docs/introduction/what-is-a2ui.md +++ b/docs/introduction/what-is-a2ui.md @@ -41,22 +41,40 @@ A2UI: JSON messages describing UI that: ### Example ```json -{"surfaceUpdate": {"surfaceId": "booking", "components": [ - {"id": "title", "component": {"Text": {"text": {"literalString": "Book Your Table"}, "usageHint": "h1"}}}, - {"id": "datetime", "component": {"DateTimeInput": {"value": {"path": "/booking/date"}, "enableDate": true}}}, - {"id": "submit-text", "component": {"Text": {"text": {"literalString": "Confirm"}}}}, - {"id": "submit-btn", "component": {"Button": {"child": "submit-text", "action": {"name": "confirm_booking"}}}} -]}} +{ + "version": "v0.9", + "updateComponents": { + "surfaceId": "booking", + "components": [ + {"id": "title", "component": "Text", "text": "Book Your Table", "variant": "h1"}, + {"id": "datetime", "component": "DateTimeInput", "value": {"path": "/booking/date"}, "enableDate": true}, + {"id": "submit-text", "component": "Text", "text": "Confirm"}, + {"id": "submit-btn", "component": "Button", "child": "submit-text", "action": {"name": "confirm_booking"}} + ] + } +} ``` ```json -{"dataModelUpdate": {"surfaceId": "booking", "contents": [ - {"key": "booking", "valueMap": [{"key": "date", "valueString": "2025-12-16T19:00:00Z"}]} -]}} +{ + "version": "v0.9", + "updateDataModel": { + "surfaceId": "booking", + "value": { + "booking": {"date": "2025-12-16T19:00:00Z"} + } + } +} ``` ```json -{"beginRendering": {"surfaceId": "booking", "root": "title"}} +{ + "version": "v0.9", + "createSurface": { + "surfaceId": "booking", + "catalogId": "https://a2ui.org/specification/v0_9/standard_catalog.json" + } +} ``` Client renders these messages as native components (Angular, Flutter, React, etc.). @@ -90,6 +108,6 @@ Client renders these messages as native components (Angular, Flutter, React, etc - **Component**: UI element (Button, TextField, Card, etc.) - **Data Model**: Application state, components bind to it - **Catalog**: Available component types -- **Message**: JSON object (`surfaceUpdate`, `dataModelUpdate`, `beginRendering`, etc.) +- **Message**: JSON object (`createSurface`, `updateComponents`, `updateDataModel`, etc.) For a comparison of similar projects, see [Agent UI Ecosystem](agent-ui-ecosystem.md). diff --git a/docs/introduction/who-is-it-for.md b/docs/introduction/who-is-it-for.md index f4cbf4d45..1ccc3c215 100644 --- a/docs/introduction/who-is-it-for.md +++ b/docs/introduction/who-is-it-for.md @@ -34,7 +34,7 @@ Build agents that generate forms, dashboards, and interactive workflows. ### 3. Platform Builders (SDK Creators) -Build agent orchestration platforms, frameworks, or UI integrations. +Build agent orchestration platforms, frameworks, or UI integrations. Do you bring remote agents into your app? @@ -63,4 +63,4 @@ Do you ship your agent into other apps you don't necessarily control? - ❌ **Remote widgets not integrated with client** - Use iframes, like MCP Apps - ❌ **Rapid UI + Agent app built together** - Use AG UI / CopilotKit - + diff --git a/docs/quickstart.md b/docs/quickstart.md index c380e4d6d..94508c602 100644 --- a/docs/quickstart.md +++ b/docs/quickstart.md @@ -2,6 +2,9 @@ Get hands-on with A2UI by running the restaurant finder demo. This guide will have you experiencing agent-generated UI in less than 5 minutes. +> Currently, most renderers in the repo are not yet updated to the latest spec (v0.9), so the messages generated +> may not be as described in the v0.9 documentation. + ## What You'll Build By the end of this quickstart, you'll have: @@ -113,66 +116,49 @@ Let's peek at what the agent is sending. Here's a simplified example of the JSON ```json { - "surfaceUpdate": { + "version": "v0.9", + "updateComponents": { "surfaceId": "main", "components": [ { "id": "header", - "component": { - "Text": { - "text": {"literalString": "Book Your Table"}, - "usageHint": "h1" - } - } + "component": "Text", + "text": "Book Your Table", + "variant": "h1" }, { "id": "date-picker", - "component": { - "DateTimeInput": { - "label": {"literalString": "Select Date"}, - "value": {"path": "/reservation/date"}, - "enableDate": true - } - } + "component": "DateTimeInput", + "label": "Select Date", + "value": { "path": "/reservation/date" } }, { "id": "submit-btn", - "component": { - "Button": { - "child": "submit-text", - "action": {"name": "confirm_booking"} - } - } - }, - { - "id": "submit-text", - "component": { - "Text": {"text": {"literalString": "Confirm Reservation"}} - } + "component": "Button", + "label": "Confirm Reservation", + "action": { "name": "confirm_booking" } } ] } } ``` -This defines the UI components for the surface: a text header, a date picker, and a button. +This defines the UI components for the surface. ### Populating Data ```json { - "dataModelUpdate": { + "version": "v0.9", + "updateDataModel": { "surfaceId": "main", - "contents": [ - { - "key": "reservation", - "valueMap": [ - {"key": "date", "valueString": "2025-12-15"}, - {"key": "time", "valueString": "19:00"}, - {"key": "guests", "valueInt": 2} - ] + "value": { + "reservation": { + "date": "2025-12-15", + "time": "19:00", + "guests": 2 } - ] + } } } ``` @@ -182,10 +168,16 @@ This populates the data model that components can bind to. ### Signaling Render ```json -{"beginRendering": {"surfaceId": "main", "root": "header"}} +{ + "version": "v0.9", + "createSurface": { + "surfaceId": "main", + "catalogId": "https://a2ui.org/specification/v0_9/standard_catalog.json" + } +} ``` -This tells the client it has enough information to render the UI. +This tells the client to create the surface and prepare for rendering with the standard catalog. > 💡 **It's Just JSON** > diff --git a/docs/reference/components.md b/docs/reference/components.md index 9e81a937c..ff3be2095 100644 --- a/docs/reference/components.md +++ b/docs/reference/components.md @@ -1,6 +1,6 @@ # Component Gallery -This page showcases all standard A2UI components with examples and usage patterns. For the complete technical specification, see the [Standard Catalog Definition](https://a2ui.org/specification/v0_8/standard_catalog_definition.json). +This page showcases all standard A2UI components with examples and usage patterns. For the complete technical specification, see the [Standard Catalog Definition](../specification/v0_9/standard_catalog.json). ## Layout Components @@ -11,20 +11,17 @@ Horizontal layout container. Children are arranged left-to-right. ```json { "id": "toolbar", - "component": { - "Row": { - "children": {"explicitList": ["btn1", "btn2", "btn3"]}, - "alignment": "center" - } - } + "component": "Row", + "children": ["btn1", "btn2", "btn3"], + "align": "center" } ``` **Properties:** -- `children`: Static array (`explicitList`) or dynamic `template` -- `distribution`: Horizontal distribution of children (`start`, `center`, `end`, `spaceBetween`, `spaceAround`, `spaceEvenly`) -- `alignment`: Vertical alignment (`start`, `center`, `end`, `stretch`) +- `children`: Static array of IDs or dynamic `template` +- `justify`: Horizontal distribution (`start`, `center`, `end`, `spaceBetween`, `spaceAround`, `spaceEvenly`) +- `align`: Vertical alignment (`start`, `center`, `end`, `stretch`) ### Column @@ -33,19 +30,16 @@ Vertical layout container. Children are arranged top-to-bottom. ```json { "id": "content", - "component": { - "Column": { - "children": {"explicitList": ["header", "body", "footer"]} - } - } + "component": "Column", + "children": ["header", "body", "footer"] } ``` **Properties:** -- `children`: Static array (`explicitList`) or dynamic `template` -- `distribution`: Vertical distribution of children (`start`, `center`, `end`, `spaceBetween`, `spaceAround`, `spaceEvenly`) -- `alignment`: Horizontal alignment (`start`, `center`, `end`, `stretch`) +- `children`: Static array of IDs or dynamic `template` +- `justify`: Vertical distribution (`start`, `center`, `end`, `spaceBetween`, `spaceAround`, `spaceEvenly`) +- `align`: Horizontal alignment (`start`, `center`, `end`, `stretch`) ## Display Components @@ -56,16 +50,13 @@ Display text content with optional styling. ```json { "id": "title", - "component": { - "Text": { - "text": {"literalString": "Welcome to A2UI"}, - "usageHint": "h1" - } - } + "component": "Text", + "text": "Welcome to A2UI", + "variant": "h1" } ``` -**`usageHint` values:** `h1`, `h2`, `h3`, `h4`, `h5`, `caption`, `body` +**`variant` values:** `h1`, `h2`, `h3`, `h4`, `h5`, `caption`, `body` ### Image @@ -74,14 +65,19 @@ Display images from URLs. ```json { "id": "logo", - "component": { - "Image": { - "url": {"literalString": "https://example.com/logo.png"} - } - } + "component": "Image", + "url": "https://example.com/logo.png", + "fit": "contain", + "variant": "mediumFeature" } ``` +**Properties:** + +- `url`: The URL of the image. +- `fit`: CSS-like object-fit (`contain`, `cover`, `fill`, `none`, `scale-down`). +- `variant`: Image style nuance (`icon`, `avatar`, `smallFeature`, `mediumFeature`, `largeFeature`, `header`). + ### Icon Display icons using Material Icons or custom icon sets. @@ -89,11 +85,33 @@ Display icons using Material Icons or custom icon sets. ```json { "id": "check-icon", - "component": { - "Icon": { - "name": {"literalString": "check"} - } - } + "component": "Icon", + "name": "check" +} +``` + +### Video + +Display video content. + +```json +{ + "id": "promo-video", + "component": "Video", + "url": "https://example.com/video.mp4" +} +``` + +### AudioPlayer + +Play audio files. + +```json +{ + "id": "podcast-player", + "component": "AudioPlayer", + "url": "https://example.com/podcast.mp3", + "description": "Episode 1: Introduction" } ``` @@ -104,11 +122,8 @@ Visual separator line. ```json { "id": "separator", - "component": { - "Divider": { - "axis": "horizontal" - } - } + "component": "Divider", + "axis": "horizontal" } ``` @@ -121,27 +136,21 @@ Clickable button with action support. ```json { "id": "submit-btn-text", - "component": { - "Text": { - "text": { "literalString": "Submit" } - } - } + "component": "Text", + "text": "Submit" } { "id": "submit-btn", - "component": { - "Button": { - "child": "submit-btn-text", - "primary": true, - "action": {"name": "submit_form"} - } - } + "component": "Button", + "child": "submit-btn-text", + "variant": "primary", + "action": {"name": "submit_form"} } ``` **Properties:** -- `child`: The ID of the component to display in the button (e.g., a Text or Icon). -- `primary`: Boolean indicating if this is a primary action. +- `child`: The ID of the component to display in the button. +- `variant`: Button style (`primary`, `borderless`). - `action`: The action to perform on click. ### TextField @@ -151,29 +160,78 @@ Text input field. ```json { "id": "email-input", - "component": { - "TextField": { - "label": {"literalString": "Email Address"}, - "text": {"path": "/user/email"}, - "textFieldType": "shortText" - } - } + "component": "TextField", + "label": "Email Address", + "value": {"path": "/user/email"}, + "variant": "shortText" } ``` -**`textFieldType` values:** `date`, `longText`, `number`, `shortText`, `obscured` +**`variant` values:** `longText`, `number`, `shortText`, `obscured` + + + +### CheckBox Boolean toggle. ```json { "id": "terms-checkbox", - "component": { - "CheckBox": { - "label": {"literalString": "I agree to the terms"}, - "value": {"path": "/form/agreedToTerms"} - } - } + "component": "CheckBox", + "label": "I agree to the terms", + "value": {"path": "/form/agreedToTerms"} +} +``` + +### ChoicePicker + +Select from a list of options. + +```json +{ + "id": "toppings-picker", + "component": "ChoicePicker", + "label": "Choose toppings", + "options": [ + {"label": "Cheese", "value": "cheese"}, + {"label": "Pepperoni", "value": "pepperoni"}, + {"label": "Mushrooms", "value": "mushrooms"} + ], + "value": {"path": "/order/toppings"}, + "variant": "multipleSelection" +} +``` + +**`variant` values:** `multipleSelection` (checkboxes/chips), `mutuallyExclusive` (radio buttons) + +### Slider + +Select a numeric value from a range. + +```json +{ + "id": "volume-slider", + "component": "Slider", + "label": "Volume", + "min": 0, + "max": 100, + "value": {"path": "/settings/volume"} +} +``` + +### DateTimeInput + +Date and time selection. + +```json +{ + "id": "appointment-date", + "component": "DateTimeInput", + "label": "Appointment Date", + "enableDate": true, + "enableTime": true, + "value": {"path": "/booking/date"} } ``` @@ -186,11 +244,8 @@ Container with elevation/border and padding. ```json { "id": "info-card", - "component": { - "Card": { - "child": "card-content" - } - } + "component": "Card", + "child": "card-content" } ``` @@ -201,12 +256,9 @@ Overlay dialog. ```json { "id": "confirmation-modal", - "component": { - "Modal": { - "entryPointChild": "open-modal-btn", - "contentChild": "modal-content" - } - } + "component": "Modal", + "trigger": "open-modal-btn", + "content": "modal-content" } ``` @@ -217,31 +269,27 @@ Tabbed interface. ```json { "id": "settings-tabs", - "component": { - "Tabs": { - "tabItems": [ - {"title": {"literalString": "General"}, "child": "general-settings"}, - {"title": {"literalString": "Privacy"}, "child": "privacy-settings"}, - {"title": {"literalString": "Advanced"}, "child": "advanced-settings"} - ] - } - } + "component": "Tabs", + "tabs": [ + {"title": "General", "child": "general-settings"}, + {"title": "Privacy", "child": "privacy-settings"}, + {"title": "Advanced", "child": "advanced-settings"} + ] } ``` +### List + Scrollable list of items. ```json { "id": "message-list", - "component": { - "List": { - "children": { - "template": { - "dataBinding": "/messages", - "componentId": "message-item" - } - } + "component": "List", + "children": { + "template": { + "path": "/messages", + "componentId": "message-item" } } } @@ -252,7 +300,7 @@ Scrollable list of items. Most components support these common properties: - `id` (required): Unique identifier for the component instance. -- `weight`: Flex-grow value when the component is a direct child of a Row or Column. This property is specified alongside `id` and `component`. +- `weight`: Flex-grow value when the component is a direct child of a Row or Column. ## Live Examples @@ -267,6 +315,7 @@ This launches a live gallery with all components, their variations, and interact ## Further Reading -- **[Standard Catalog Definition](../../specification/v0_9/json/standard_catalog_definition.json)**: Complete technical specification +- **[Standard Catalog Definition](../specification/v0_9/standard_catalog.json)**: Complete technical specification +- **[Client-Side Functions](functions.md)**: Standard catalog functions for validation and formatting - **[Custom Components Guide](../guides/custom-components.md)**: Build your own components - **[Theming Guide](../guides/theming.md)**: Style components to match your brand diff --git a/docs/reference/functions.md b/docs/reference/functions.md new file mode 100644 index 000000000..e6bbca3ba --- /dev/null +++ b/docs/reference/functions.md @@ -0,0 +1,115 @@ +# Client-Side Functions + +This page documents the standard client-side functions available in the A2UI catalog. These functions can be used for data validation, string formatting, and logical operations within your UI definitions. + +## Usage + +Functions are invoked using the `FunctionCall` object structure: + +```json +{ + "call": "functionName", + "args": { + "arg1": "value1", + "arg2": {"path": "/data/path"} + } +} +``` + +## Validation Functions + +These functions return a boolean and are primarily used in the `checks` property of input components. + +### required +Checks that the value is not null, undefined, or empty. +- **args**: + - `value`: The value to check. + +### regex +Checks that the value matches a regular expression string. +- **args**: + - `value`: The string to check. + - `pattern`: The regex pattern to match against. + +### length +Checks string length constraints. +- **args**: + - `value`: The string to check. + - `min` (optional): Minimum length. + - `max` (optional): Maximum length. + +### numeric +Checks numeric range constraints. +- **args**: + - `value`: The number to check. + - `min` (optional): Minimum value. + - `max` (optional): Maximum value. + +### email +Checks that the value is a valid email address. +- **args**: + - `value`: The string to check. + +## Formatting Functions + +These functions return a formatted string and are often used in `Text` components or `formatString` calls. + +### formatString +Performs string interpolation. +- **args**: + - `value`: The format string containing `${expression}` placeholders. +- **Usage**: `${formatString(value:'Hello ${/name}')}` or simply as a direct function call. + +### formatNumber +Formats a number with grouping and precision. +- **args**: + - `value`: The number to format. + - `decimals` (optional): Number of decimal places. + - `grouping` (optional): Whether to use grouping separators (default: true). + +### formatCurrency +Formats a number as a currency string. +- **args**: + - `value`: The amount. + - `currency`: ISO 4217 currency code (e.g., 'USD'). + - `decimals` (optional): Number of decimal places. + - `grouping` (optional): Whether to use grouping separators. + +### formatDate +Formats a timestamp using a pattern. +- **args**: + - `value`: The date/time to format. + - `format`: Unicode TR35 date pattern (e.g., 'MMM dd, yyyy'). + +### pluralize +Selects a localized string based on a count. +- **args**: + - `value`: The count. + - `other`: Fallback string. + - `zero`, `one`, `two`, `few`, `many` (optional): Category-specific strings. + +## Logical Functions + +Used to combine multiple boolean conditions. + +### and +Logical AND. +- **args**: + - `values`: Array of boolean values. + +### or +Logical OR. +- **args**: + - `values`: Array of boolean values. + +### not +Logical NOT. +- **args**: + - `value`: Boolean value to negate. + +## Utility Functions + +### openUrl +Opens a URL in a browser or handler. +- **args**: + - `url`: The URL to open. diff --git a/docs/reference/messages.md b/docs/reference/messages.md index 2396cf317..815532c48 100644 --- a/docs/reference/messages.md +++ b/docs/reference/messages.md @@ -1,75 +1,80 @@ # Message Types -This reference provides detailed documentation for all A2UI message types. +This reference provides detailed documentation for all A2UI message types in the v0.9 specification. ## Message Format -All A2UI messages are JSON objects sent as JSON Lines (JSONL). Each line contains exactly one message, and each message contains exactly one of these four keys: +All A2UI messages are JSON objects sent as JSON Lines (JSONL). Each line contains exactly one message envelope, which must contain a `version` (set to "v0.9") and exactly one of the following message keys: -- `beginRendering` -- `surfaceUpdate` -- `dataModelUpdate` +- `createSurface` +- `updateComponents` +- `updateDataModel` - `deleteSurface` -## beginRendering +## createSurface -Signals the client that it has enough information to perform the initial render of a surface. +Signals the client to create a new surface and prepare it for rendering. ### Schema ```typescript { - beginRendering: { - surfaceId: string; // Required: Unique surface identifier - root: string; // Required: The ID of the root component to render - catalogId?: string; // Optional: URL of component catalog - styles?: object; // Optional: Styling information + version: "v0.9", // Required: Protocol version + createSurface: { + surfaceId: string; // Required: Unique surface identifier + catalogId: string; // Required: URI of the component catalog + theme?: object; // Optional: Theme overrides + sendDataModel?: boolean; // Optional: If true, client sends data model in A2A metadata } } ``` ### Properties -| Property | Type | Required | Description | -| ----------- | ------ | -------- | --------------------------------------------------------------------------------------- | -| `surfaceId` | string | ✅ | Unique identifier for this surface. | -| `root` | string | ✅ | The `id` of the component that should be the root of the UI tree for this surface. | -| `catalogId` | string | ❌ | Identifier for the component catalog. Defaults to the v0.8 standard catalog if omitted. | -| `styles` | object | ❌ | Styling information for the UI, as defined by the catalog. | +| Property | Type | Required | Description | +| --------------- | ------- | -------- | --------------------------------------------------------------------------- | +| `surfaceId` | string | ✅ | Unique identifier for this surface. | +| `catalogId` | string | ✅ | URI for the component catalog (e.g., `https://a2ui.org/specification/v0_9/standard_catalog.json`). | +| `theme` | object | ❌ | Theme values (e.g., `primaryColor`) to apply to this surface. | +| `sendDataModel` | boolean | ❌ | If true, the client acts as the source of truth for data and syncs it back. | ### Examples -**Basic render signal:** +**Basic surface creation:** ```json { - "beginRendering": { + "version": "v0.9", + "createSurface": { "surfaceId": "main", - "root": "root-component" + "catalogId": "https://a2ui.org/specification/v0_9/standard_catalog.json" } } ``` -**With a custom catalog:** +**With styling:** ```json { - "beginRendering": { + "version": "v0.9", + "createSurface": { "surfaceId": "custom-ui", - "root": "root-custom", - "catalogId": "https://my-company.com/a2ui/v0.8/my_custom_catalog.json" + "catalogId": "https://example.com/catalogs/v1.json", + "theme": { + "primaryColor": "#007bff", + "fontFamily": "Inter, sans-serif" + } } } ``` ### Usage Notes -- Must be sent after the client has received the component definitions for the root component and its initial children. -- The client should buffer `surfaceUpdate` and `dataModelUpdate` messages and only render the UI for a surface after receiving its corresponding `beginRendering` message. +- Must be sent before any `updateComponents` or `updateDataModel` messages for this surface are processed. --- -## surfaceUpdate +## updateComponents Add or update components within a surface. @@ -77,15 +82,13 @@ Add or update components within a surface. ```typescript { - surfaceUpdate: { + version: "v0.9", // Required: Protocol version + updateComponents: { surfaceId: string; // Required: Target surface components: Array<{ // Required: List of components id: string; // Required: Component ID - component: { // Required: Wrapper for component data - [ComponentType]: { // Required: Exactly one component type - ...properties // Component-specific properties - } - } + component: string; // Required: Component type name (e.g., "Text") + [key: string]: any; // Component-specific properties }> } } @@ -96,14 +99,15 @@ Add or update components within a surface. | Property | Type | Required | Description | | ------------ | ------ | -------- | ------------------------------ | | `surfaceId` | string | ✅ | ID of the surface to update | -| `components` | array | ✅ | Array of component definitions | +| `components` | array | ✅ | Array of flattened component definitions | ### Component Object -Each object in the `components` array must have: +Each object in the `components` array is a **flattened** definition: -- `id` (string, required): Unique identifier within the surface -- `component` (object, required): A wrapper object that contains exactly one key, which is the component type (e.g., `Text`, `Button`). +- `id` (string, required): Unique identifier within the surface. +- `component` (string, required): The type of component (e.g., `Button`, `Column`). +- Other properties are direct keys (e.g., `text`, `children`, `variant`). ### Examples @@ -111,17 +115,15 @@ Each object in the `components` array must have: ```json { - "surfaceUpdate": { + "version": "v0.9", + "updateComponents": { "surfaceId": "main", "components": [ { "id": "greeting", - "component": { - "Text": { - "text": {"literalString": "Hello, World!"}, - "usageHint": "h1" - } - } + "component": "Text", + "text": "Hello, World!", + "variant": "h1" } ] } @@ -132,89 +134,43 @@ Each object in the `components` array must have: ```json { - "surfaceUpdate": { + "version": "v0.9", + "updateComponents": { "surfaceId": "main", "components": [ { "id": "root", - "component": { - "Column": { - "children": {"explicitList": ["header", "body"]} - } - } + "component": "Column", + "children": ["header", "body"] }, { "id": "header", - "component": { - "Text": { - "text": {"literalString": "Welcome"} - } - } + "component": "Text", + "text": "Welcome" }, { "id": "body", - "component": { - "Card": { - "child": "content" - } - } + "component": "Card", + "child": "content" }, { "id": "content", - "component": { - "Text": { - "text": {"path": "/message"} - } - } - } - ] - } -} -``` - -**Updating existing component:** - -```json -{ - "surfaceUpdate": { - "surfaceId": "main", - "components": [ - { - "id": "greeting", - "component": { - "Text": { - "text": {"literalString": "Hello, Alice!"}, - "usageHint": "h1" - } - } + "component": "Text", + "text": { "path": "/message" } } ] } } ``` -The component with `id: "greeting"` is updated (not duplicated). - ### Usage Notes -- One component must be designated as the `root` in the `beginRendering` message to serve as the tree root. -- Components form an adjacency list (flat structure with ID references). -- Sending a component with an existing ID updates that component. -- Children are referenced by ID. -- Components can be added incrementally (streaming). - -### Errors - -| Error | Cause | Solution | -| ---------------------- | -------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- | -| Surface not found | `surfaceId` does not exist | Ensure a unique `surfaceId` is used consistently for a given surface. Surfaces are implicitly created on first update. | -| Invalid component type | Unknown component type | Check component type exists in the negotiated catalog. | -| Invalid property | Property doesn't exist for this type | Verify against catalog schema. | -| Circular reference | Component references itself as a child | Fix component hierarchy. | +- The structure is **flat**: `component: "Type"` instead of nested `component: { "Type": ... }`. +- Properties use native JSON types. --- -## dataModelUpdate +## updateDataModel Update the data model that components bind to. @@ -222,16 +178,11 @@ Update the data model that components bind to. ```typescript { - dataModelUpdate: { + version: "v0.9", // Required: Protocol version + updateDataModel: { surfaceId: string; // Required: Target surface - path?: string; // Optional: Path to a location in the model - contents: Array<{ // Required: Data entries - key: string; - valueString?: string; - valueNumber?: number; - valueBoolean?: boolean; - valueMap?: Array<{...}>; - }> + path?: string; // Optional: Path to update (defaults to root) + value?: any; // Optional: Data to upsert (omission implies removal) } } ``` @@ -241,62 +192,58 @@ Update the data model that components bind to. | Property | Type | Required | Description | | ----------- | ------ | -------- | ---------------------------------------------------------------------------------------------------- | | `surfaceId` | string | ✅ | ID of the surface to update. | -| `path` | string | ❌ | Path to a location within the data model (e.g., 'user'). If omitted, the update applies to the root. | -| `contents` | array | ✅ | An array of data entries as an adjacency list. Each entry has a `key` and a typed `value*` property. | - -### The `contents` Adjacency List - -The `contents` array is a list of key-value pairs. Each object in the array must have a `key` and exactly one `value*` property (`valueString`, `valueNumber`, `valueBoolean`, or `valueMap`). This structure is LLM-friendly and avoids issues with inferring types from a generic `value` field. +| `path` | string | ❌ | The JSON Pointer path to update (e.g., `/user/name`). Defaults to root `/` if omitted. | +| `value` | any | ❌ | The value to set. If omitted, the key at `path` is removed. | ### Examples -**Initialize entire model:** - -If `path` is omitted, `contents` replaces the entire data model for the surface. +**Update multiple fields (root merge):** ```json { - "dataModelUpdate": { + "version": "v0.9", + "updateDataModel": { "surfaceId": "main", - "contents": [ - { - "key": "user", - "valueMap": [ - { "key": "name", "valueString": "Alice" }, - { "key": "email", "valueString": "alice@example.com" } - ] + "value": { + "user": { + "name": "Alice", + "email": "alice@example.com" }, - { "key": "items", "valueMap": [] } - ] + "items": [] + } } } ``` -**Update nested property:** - -If `path` is provided, `contents` updates the data at that location. +**Update specific path:** ```json { - "dataModelUpdate": { + "version": "v0.9", + "updateDataModel": { "surfaceId": "main", - "path": "user", - "contents": [ - { "key": "email", "valueString": "alice@newdomain.com" } - ] + "path": "/user/email", + "value": "new@example.com" } } ``` -This will change `/user/email` without affecting `/user/name`. +**Remove a field:** + +```json +{ + "version": "v0.9", + "updateDataModel": { + "surfaceId": "main", + "path": "/user/tempData" + } +} +``` ### Usage Notes -- Data model is per-surface. -- Components automatically re-render when their bound data changes. -- Prefer granular updates to specific paths over replacing the entire model. -- Data model is a plain JSON object. -- Any data transformation (e.g., formatting a date) must be done by the server before sending the `dataModelUpdate` message. +- `value` is a standard JSON object/value, not an adjacency list. +- Updates are generally deep merged (if value is object) or replaced. --- @@ -308,89 +255,39 @@ Remove a surface and all its components and data. ```typescript { + version: "v0.9", // Required: Protocol version deleteSurface: { surfaceId: string; // Required: Surface to delete } } ``` -### Properties - -| Property | Type | Required | Description | -| ----------- | ------ | -------- | --------------------------- | -| `surfaceId` | string | ✅ | ID of the surface to delete | - ### Examples -**Delete a surface:** - ```json { + "version": "v0.9", "deleteSurface": { "surfaceId": "modal" } } ``` -**Delete multiple surfaces:** - -```json -{"deleteSurface": {"surfaceId": "sidebar"}} -{"deleteSurface": {"surfaceId": "content"}} -``` - -### Usage Notes - -- Removes all components associated with the surface -- Clears the data model for the surface -- Client should remove the surface from the UI -- Safe to delete non-existent surface (no-op) -- Use when closing modals, dialogs, or navigating away - -### Errors - -| Error | Cause | Solution | -| ------------------------------- | ----- | -------- | -| (None - deletes are idempotent) | | | - --- ## Message Ordering -### Requirements - -1. `beginRendering` must come after the initial `surfaceUpdate` messages for that surface. -2. `surfaceUpdate` can come before or after `dataModelUpdate`. -3. Messages for different surfaces are independent. -4. Multiple messages can update the same surface incrementally. - -### Recommended Order +### Recommended Sequence ```jsonl -{"surfaceUpdate": {"surfaceId": "main", "components": [...]}} -{"dataModelUpdate": {"surfaceId": "main", "contents": {...}}} -{"beginRendering": {"surfaceId": "main", "root": "root-id"}} -``` - -### Progressive Building - -```jsonl -{"surfaceUpdate": {"surfaceId": "main", "components": [...]}} // Header -{"surfaceUpdate": {"surfaceId": "main", "components": [...]}} // Body -{"beginRendering": {"surfaceId": "main", "root": "root-id"}} // Initial render -{"surfaceUpdate": {"surfaceId": "main", "components": [...]}} // Footer (after initial render) -{"dataModelUpdate": {"surfaceId": "main", "contents": {...}}} // Populate data +{"version": "v0.9", "createSurface": {"surfaceId": "main", "catalogId": "..."}} +{"version": "v0.9", "updateDataModel": {"surfaceId": "main", "value": {"user": "Alice"}}} +{"version": "v0.9", "updateComponents": {"surfaceId": "main", "components": [{"id": "root", "component": "Text", "text": "Hi"}]}} ``` ## Validation All messages should be validated against: -- **[server_to_client.json](https://a2ui.org/specification/v0_8/server_to_client.json)**: Message envelope schema -- **[standard_catalog_definition.json](https://a2ui.org/specification/v0_8/standard_catalog_definition.json)**: Component schemas - -## Further Reading - -- **[Component Gallery](components.md)**: All available component types -- **[Data Binding Guide](../concepts/data-binding.md)**: How data binding works -- **[Agent Development Guide](../guides/agent-development.md)**: Generate valid messages +- **[server_to_client.json](../specification/v0_9/server_to_client.json)**: Message envelope schema. +- **[standard_catalog.json](../specification/v0_9/standard_catalog.json)**: Component schemas. diff --git a/docs/renderers.md b/docs/renderers.md index f12c4cb07..934d8f7fb 100644 --- a/docs/renderers.md +++ b/docs/renderers.md @@ -12,11 +12,14 @@ You have a lot of flexibility, to bring custom comonents to a renderer, or build | Renderer | Platform | Status | Links | |----------|----------|--------|-------| -| **Lit (Web Components)** | Web | ✅ Stable | [Code](https://github.com/google/A2UI/tree/main/renderers/lit) | -| **Angular** | Web | ✅ Stable | [Code](https://github.com/google/A2UI/tree/main/renderers/angular) | -| **Flutter (GenUI SDK)** | Mobile/Desktop/Web | ✅ Stable | [Docs](https://docs.flutter.dev/ai/genui) · [Code](https://github.com/flutter/genui) | +| **Lit (Web Components)** | Web | 🚧 In Progress | [Code](https://github.com/google/A2UI/tree/main/renderers/lit) | +| **Angular** | Web | 🚧 In Progress | [Code](https://github.com/google/A2UI/tree/main/renderers/angular) | +| **Flutter (GenUI SDK)** | Mobile/Desktop/Web | 🚧 In Progress | [Docs](https://docs.flutter.dev/ai/genui) · [Code](https://github.com/flutter/genui) | | **React** | Web | 🚧 In Progress | Coming Q1 2026 | +> Currently, most renderers in the repo are not yet updated to the latest spec (v0.9), so the messages generated +> may not be as described in the v0.9 documentation. + Check the [Roadmap](roadmap.md) for more. ## How Renderers Work diff --git a/docs/roadmap.md b/docs/roadmap.md index 2656b78ae..32c48e80d 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -8,13 +8,17 @@ This roadmap outlines the current state and future plans for the A2UI project. T | Version | Status | Notes | |---------|--------|-------| -| **v0.8** | ✅ Stable | Initial public release | -| **v0.9** | 🚧 In Progress | Draft specification improvements | +| **v0.10** | ✅ Draft | Draft | +| **v0.9** | ✅ Current | Current stable release | +| **v0.8** | ⏸️ Previous | Initial public release | + +> Currently, most renderers in the repo are not yet updated to the latest spec (v0.9), so the messages generated +> may not be as described in the v0.9 documentation. Key features: - ✅ Streaming JSONL message format -- ✅ Four core message types (`surfaceUpdate`, `dataModelUpdate`, `beginRendering`, `deleteSurface`) +- ✅ Four core message types (`createSurface`, `updateComponents`, `updateDataModel`, `deleteSurface`) - ✅ Adjacency list component model - ✅ JSON Pointer-based data binding - ✅ Separation of structure and state diff --git a/docs/scripts/prepare_docs.py b/docs/scripts/prepare_docs.py new file mode 100755 index 000000000..daa7ea698 --- /dev/null +++ b/docs/scripts/prepare_docs.py @@ -0,0 +1,139 @@ +#!/usr/bin/env python3 +import os +import shutil +import glob + +def main(): + """Calculates the source and destination paths for the specification files.""" + repo_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..')) + spec_source_root = os.path.join(repo_root, 'specification') + docs_spec_root = os.path.join(repo_root, 'docs', 'specification') + + print(f"Preparing documentation...") + + # Iterate over all directories in specification/ + for item in os.listdir(spec_source_root): + source_path = os.path.join(spec_source_root, item) + if not os.path.isdir(source_path): + continue + + # We assume directories in specification/ are versions (e.g. v0_8, v0_9) + dest_path = os.path.join(docs_spec_root, item) + + print(f"Processing {item}...") + + # Remove destination if it exists + if os.path.exists(dest_path): + shutil.rmtree(dest_path) + + # Copy the directory structure, ignoring specified patterns + # We explicitly exclude 'eval' and 'test' directories as requested. + shutil.copytree(source_path, dest_path, ignore=shutil.ignore_patterns( + 'node_modules', '__pycache__', '.*', 'dist', 'build', 'eval', 'test' + )) + + print(f" Copied to {dest_path}") + + # Flatten JSON files: move json/*.json to the version root + json_dir = os.path.join(dest_path, 'json') + if os.path.exists(json_dir) and os.path.isdir(json_dir): + json_files = glob.glob(os.path.join(json_dir, '*.json')) + if json_files: + print(f" Moving {len(json_files)} JSON files to {dest_path}") + for json_file in json_files: + shutil.move(json_file, dest_path) + shutil.rmtree(json_dir) + + # Rewrite links in copied markdown files to work in the new context + if item == 'v0_8': + # Rewrite custom_catalog_changes.md + ccc_path = os.path.join(dest_path, 'docs', 'custom_catalog_changes.md') + if os.path.exists(ccc_path): + with open(ccc_path, 'r') as f: + content = f.read() + + # Rewrites for custom_catalog_changes.md + content = content.replace('(./a2ui_extension_specification.md)', '(v0.8-extension-spec.md)') + content = content.replace('(./a2ui_protocol.md', '(v0.8-a2ui.md') # Note the missing closing paren to match anchor links too + content = content.replace('../json/', 'v0_8/') + content = content.replace('(../json/', '(v0_8/') + + with open(ccc_path, 'w') as f: + f.write(content) + print(f" Rewrote links in {ccc_path}") + + # Rewrite a2ui_protocol.md to point to the wrapper if it links to custom_catalog_changes.md + protocol_path = os.path.join(dest_path, 'docs', 'a2ui_protocol.md') + if os.path.exists(protocol_path): + with open(protocol_path, 'r') as f: + content = f.read() + + content = content.replace('(custom_catalog_changes.md)', '(v0.8-custom-catalog-changes.md)') + content = content.replace('../json/', 'v0_8/') + + with open(protocol_path, 'w') as f: + f.write(content) + print(f" Rewrote links in {protocol_path}") + + if item == 'v0_9': + protocol_path = os.path.join(dest_path, 'docs', 'a2ui_protocol.md') + if os.path.exists(protocol_path): + with open(protocol_path, 'r') as f: + content = f.read() + + # Fix relative links that break when included from a parent directory + # evolution_guide.md is in v0_9/docs/ relative to docs/specification/ + content = content.replace('(evolution_guide.md)', '(v0.9-evolution-guide.md)') + # JSON files are flattened to v0_9/ relative to docs/specification/ + content = content.replace('../json/', 'v0_9/') + + with open(protocol_path, 'w') as f: + f.write(content) + print(f" Rewrote links in {protocol_path}") + + # Rewrite a2ui_custom_functions.md + cf_path = os.path.join(dest_path, 'docs', 'a2ui_custom_functions.md') + if os.path.exists(cf_path): + with open(cf_path, 'r') as f: + content = f.read() + + content = content.replace('(a2ui_protocol.md', '(v0.9-a2ui.md') + content = content.replace('../json/', 'v0_9/') + + with open(cf_path, 'w') as f: + f.write(content) + print(f" Rewrote links in {cf_path}") + + if item == 'v0_10': + protocol_path = os.path.join(dest_path, 'docs', 'a2ui_protocol.md') + if os.path.exists(protocol_path): + with open(protocol_path, 'r') as f: + content = f.read() + + # Fix relative links that break when included from a parent directory + # evolution_guide.md is in v0_10/docs/ relative to docs/specification/ + content = content.replace('(evolution_guide.md)', '(v0.10-evolution-guide.md)') + # JSON files are flattened to v0_10/ relative to docs/specification/ + content = content.replace('../json/', 'v0_10/') + + with open(protocol_path, 'w') as f: + f.write(content) + print(f" Rewrote links in {protocol_path}") + + # Rewrite a2ui_custom_functions.md + cf_path = os.path.join(dest_path, 'docs', 'a2ui_custom_functions.md') + if os.path.exists(cf_path): + with open(cf_path, 'r') as f: + content = f.read() + + content = content.replace('(a2ui_protocol.md', '(v0.10-a2ui.md') + content = content.replace('../json/', 'v0_10/') + + with open(cf_path, 'w') as f: + f.write(content) + print(f" Rewrote links in {cf_path}") + + print(f"Successfully prepared documentation.") + +if __name__ == '__main__': + main() diff --git a/docs/specification/v0.10-a2ui.md b/docs/specification/v0.10-a2ui.md new file mode 100644 index 000000000..34f4add67 --- /dev/null +++ b/docs/specification/v0.10-a2ui.md @@ -0,0 +1,17 @@ +# A2UI Protocol v0.10 (Draft) + +> ⚠️ **Draft Release** +> +> Version 0.10 is a draft release and is subject to change. The current stable release is [v0.9](v0.9-a2ui.md). + +> ℹ️ **Living Document** +> +> This specification is automatically included from `specification/v0_10/docs/a2ui_protocol.md`. Any updates to the specification will automatically appear here. + +**See also:** +- [v0.9 Protocol Specification](v0.9-a2ui.md) (Stable) +- [Evolution Guide: v0.9 → v0.10](v0.10-evolution-guide.md) + +--- + +--8<-- "docs/specification/v0_10/docs/a2ui_protocol.md" diff --git a/docs/specification/v0.10-custom-functions.md b/docs/specification/v0.10-custom-functions.md new file mode 100644 index 000000000..d6674561f --- /dev/null +++ b/docs/specification/v0.10-custom-functions.md @@ -0,0 +1,12 @@ +# A2UI Custom Functions Guide v0.10 + +> ℹ️ **Living Document** +> +> This guide is automatically included from `specification/v0_10/docs/a2ui_custom_functions.md`. Any updates will automatically appear here. + +**Related Documentation:** +- [A2UI Protocol v0.10](v0.10-a2ui.md) + +--- + +--8<-- "docs/specification/v0_10/docs/a2ui_custom_functions.md" diff --git a/docs/specification/v0.10-evolution-guide.md b/docs/specification/v0.10-evolution-guide.md new file mode 100644 index 000000000..e2bb81333 --- /dev/null +++ b/docs/specification/v0.10-evolution-guide.md @@ -0,0 +1,13 @@ +# Evolution Guide: v0.9 → v0.10 + +> ℹ️ **Living Document** +> +> This guide is automatically included from `specification/v0_10/docs/evolution_guide.md`. Any updates will automatically appear here. + +**Related Documentation:** +- [A2UI Protocol v0.9](v0.9-a2ui.md) (Stable - what you're migrating from) +- [A2UI Protocol v0.10](v0.10-a2ui.md) (Draft - what you're migrating to) + +--- + +--8<-- "docs/specification/v0_10/docs/evolution_guide.md" diff --git a/docs/specification/v0.10-extension-spec.md b/docs/specification/v0.10-extension-spec.md new file mode 100644 index 000000000..c8efa14bd --- /dev/null +++ b/docs/specification/v0.10-extension-spec.md @@ -0,0 +1,12 @@ +# A2UI Extension Specification v0.10 + +> ℹ️ **Living Document** +> +> This specification is automatically included from `specification/v0_10/docs/a2ui_extension_specification.md`. Any updates will automatically appear here. + +**Related Documentation:** +- [A2UI Protocol v0.10](v0.10-a2ui.md) + +--- + +--8<-- "docs/specification/v0_10/docs/a2ui_extension_specification.md" diff --git a/docs/specification/v0.8-a2ui.md b/docs/specification/v0.8-a2ui.md index 89353e279..677ad5af0 100644 --- a/docs/specification/v0.8-a2ui.md +++ b/docs/specification/v0.8-a2ui.md @@ -1,18 +1,18 @@ -# A2UI Protocol v0.8 (Stable) +# A2UI Protocol v0.8 (Previous Version) -> ✅ **Stable Release** +> ⚠️ **Previous Version** > -> Version 0.8 is the current stable release, recommended for production use. +> Version 0.8 is the previous stable release. The current stable release is [v0.9](v0.9-a2ui.md). > ℹ️ **Living Document** > > This specification is automatically included from `specification/v0_8/docs/a2ui_protocol.md`. Any updates to the specification will automatically appear here. **See also:** -- [v0.9 Protocol Specification](v0.9-a2ui.md) (Draft) +- [v0.9 Protocol Specification](v0.9-a2ui.md) (Stable) - [Evolution Guide: v0.8 → v0.9](v0.9-evolution-guide.md) -- [A2A Extension Specification](v0.8-a2a-extension.md) (for v0.8) +- [A2A Extension Specification](v0.8-extension-spec.md) (for v0.8) --- ---8<-- "specification/v0_8/docs/a2ui_protocol.md" +--8<-- "docs/specification/v0_8/docs/a2ui_protocol.md" diff --git a/docs/specification/v0.8-custom-catalog-changes.md b/docs/specification/v0.8-custom-catalog-changes.md new file mode 100644 index 000000000..27fd775dc --- /dev/null +++ b/docs/specification/v0.8-custom-catalog-changes.md @@ -0,0 +1,12 @@ +# A2UI Custom Catalog Negotiation Changes (v0.8) + +> ℹ️ **Living Document** +> +> This guide is automatically included from `specification/v0_8/docs/custom_catalog_changes.md`. Any updates will automatically appear here. + +**Related Documentation:** +- [A2UI Protocol v0.8](v0.8-a2ui.md) + +--- + +--8<-- "docs/specification/v0_8/docs/custom_catalog_changes.md" diff --git a/docs/specification/v0.8-a2a-extension.md b/docs/specification/v0.8-extension-spec.md similarity index 88% rename from docs/specification/v0.8-a2a-extension.md rename to docs/specification/v0.8-extension-spec.md index e8b28327a..e212cf5e0 100644 --- a/docs/specification/v0.8-a2a-extension.md +++ b/docs/specification/v0.8-extension-spec.md @@ -2,7 +2,7 @@ > ℹ️ **Living Document** -> +> > This specification is automatically included from `specification/v0_8/docs/a2ui_extension_specification.md`. Any updates to the specification will automatically appear here. > 📝 **Version Compatibility** @@ -15,4 +15,4 @@ --- ---8<-- "specification/v0_8/docs/a2ui_extension_specification.md" +--8<-- "docs/specification/v0_8/docs/a2ui_extension_specification.md" diff --git a/docs/specification/v0.9-a2ui.md b/docs/specification/v0.9-a2ui.md index 731b7c2ec..c167b8382 100644 --- a/docs/specification/v0.9-a2ui.md +++ b/docs/specification/v0.9-a2ui.md @@ -1,17 +1,17 @@ -# A2UI Protocol v0.9 (Draft) +# A2UI Protocol v0.9 (Stable) -> ⚠️ **Draft Status** +> ✅ **Stable Release** > -> Version 0.9 is currently in draft status. For production use, consider [v0.8 (Stable)](v0.8-a2ui.md). +> Version 0.9 is the current stable release, recommended for new projects. > ℹ️ **Living Document** -> +> > This specification is automatically included from `specification/v0_9/docs/a2ui_protocol.md`. Any updates to the specification will automatically appear here. **See also:** -- [v0.8 Protocol Specification](v0.8-a2ui.md) (Stable) +- [v0.8 Protocol Specification](v0.8-a2ui.md) (Previous Version) - [Evolution Guide: v0.8 → v0.9](v0.9-evolution-guide.md) --- ---8<-- "specification/v0_9/docs/a2ui_protocol.md" +--8<-- "docs/specification/v0_9/docs/a2ui_protocol.md" diff --git a/docs/specification/v0.9-custom-functions.md b/docs/specification/v0.9-custom-functions.md new file mode 100644 index 000000000..6209bf2ad --- /dev/null +++ b/docs/specification/v0.9-custom-functions.md @@ -0,0 +1,12 @@ +# A2UI Custom Functions Guide v0.9 + +> ℹ️ **Living Document** +> +> This guide is automatically included from `specification/v0_9/docs/a2ui_custom_functions.md`. Any updates will automatically appear here. + +**Related Documentation:** +- [A2UI Protocol v0.9](v0.9-a2ui.md) + +--- + +--8<-- "docs/specification/v0_9/docs/a2ui_custom_functions.md" diff --git a/docs/specification/v0.9-evolution-guide.md b/docs/specification/v0.9-evolution-guide.md index 662b5421e..d542e992e 100644 --- a/docs/specification/v0.9-evolution-guide.md +++ b/docs/specification/v0.9-evolution-guide.md @@ -1,13 +1,13 @@ # Evolution Guide: v0.8 → v0.9 > ℹ️ **Living Document** -> +> > This guide is automatically included from `specification/v0_9/docs/evolution_guide.md`. Any updates will automatically appear here. **Related Documentation:** - [A2UI Protocol v0.8](v0.8-a2ui.md) (Stable - what you're migrating from) -- [A2UI Protocol v0.9](v0.9-a2ui.md) (Draft - what you're migrating to) +- [A2UI Protocol v0.9](v0.9-a2ui.md) (Stable - what you're migrating to) --- ---8<-- "specification/v0_9/docs/evolution_guide.md" +--8<-- "docs/specification/v0_9/docs/evolution_guide.md" diff --git a/docs/specification/v0.9-extension-spec.md b/docs/specification/v0.9-extension-spec.md new file mode 100644 index 000000000..dcf8eae37 --- /dev/null +++ b/docs/specification/v0.9-extension-spec.md @@ -0,0 +1,12 @@ +# A2UI Extension Specification v0.9 + +> ℹ️ **Living Document** +> +> This specification is automatically included from `specification/v0_9/docs/a2ui_extension_specification.md`. Any updates will automatically appear here. + +**Related Documentation:** +- [A2UI Protocol v0.9](v0.9-a2ui.md) + +--- + +--8<-- "docs/specification/v0_9/docs/a2ui_extension_specification.md" diff --git a/docs/transports.md b/docs/transports.md index 84fbaa6a0..5e2f186a5 100644 --- a/docs/transports.md +++ b/docs/transports.md @@ -39,7 +39,7 @@ If you are using A2A, this should be nearly automatic. TODO: Add a detailed guide. -**See:** [A2A Extension Specification](specification/v0.8-a2a-extension.md) +**See:** [A2A Extension Specification](specification/v0.8-extension-spec.md) ## AG UI @@ -74,4 +74,4 @@ You can use any transport that sends JSON: ## Next Steps - **[A2A Protocol Docs](https://a2a-protocol.org)**: Learn about A2A -- **[A2A Extension Spec](specification/v0.8-a2a-extension.md)**: A2UI + A2A details \ No newline at end of file +- **[A2A Extension Spec](specification/v0.8-extension-spec.md)**: A2UI + A2A details \ No newline at end of file diff --git a/mkdocs.yaml b/mkdocs.yaml index 4031e954c..e4b04f08e 100644 --- a/mkdocs.yaml +++ b/mkdocs.yaml @@ -44,6 +44,7 @@ nav: - Developer Guides: - Client Setup: guides/client-setup.md - Agent Development: guides/agent-development.md + - Renderer Development: guides/renderer-development.md - Custom Components: guides/custom-components.md - Theming & Styling: guides/theming.md - Core Concepts: @@ -52,12 +53,20 @@ nav: - Components & Structure: concepts/components.md - Data Binding: concepts/data-binding.md - Specifications: - - v0.8 (Stable): + - v0.8 (Previous): - A2UI Specification: specification/v0.8-a2ui.md - - A2A Extension: specification/v0.8-a2a-extension.md - - v0.9 (Draft): + - A2A Extension: specification/v0.8-extension-spec.md + - Custom Catalog Changes: specification/v0.8-custom-catalog-changes.md + - v0.9 (Current): - A2UI Specification: specification/v0.9-a2ui.md - Evolution Guide: specification/v0.9-evolution-guide.md + - Extension Specification: specification/v0.9-extension-spec.md + - Custom Functions: specification/v0.9-custom-functions.md + - v0.10 (Draft): + - A2UI Specification: specification/v0.10-a2ui.md + - Evolution Guide: specification/v0.10-evolution-guide.md + - Extension Specification: specification/v0.10-extension-spec.md + - Custom Functions: specification/v0.10-custom-functions.md - Renderers (Clients): renderers.md - Transports (Message Passing): transports.md - Agents (Server-side): agents.md @@ -66,9 +75,18 @@ nav: - Reference: - Component Reference: reference/components.md - Message Reference: reference/messages.md + - Client-Side Functions: reference/functions.md exclude_docs: | scripts/** + specification/**/docs/a2ui_protocol.md + specification/**/docs/a2ui_extension_specification.md + specification/**/docs/a2ui_custom_functions.md + specification/**/docs/evolution_guide.md + specification/**/docs/custom_catalog_changes.md + specification/**/README.md + specification/**/json/README.md + # Repository repo_name: google/A2UI @@ -176,11 +194,5 @@ markdown_extensions: plugins: - search - macros - # - include-markdown - mermaid2 - # - llmstxt: - # full_output: llms-full.txt - # sections: - # "Specification": - # - a2ui_protocol.md diff --git a/specification/v0_10/README.md b/specification/v0_10/README.md index 346bfbdce..f749c229d 100644 --- a/specification/v0_10/README.md +++ b/specification/v0_10/README.md @@ -9,6 +9,6 @@ If you have proposed changes or new features, please open an issue or submit a p ## Documentation - [Protocol](docs/a2ui_protocol.md) -- [Custom Functions](docs/custom_functions.md) +- [Custom Functions](docs/a2ui_custom_functions.md) - [Extension Specification](docs/a2ui_extension_specification.md) - [Evolution Guide](docs/evolution_guide.md) diff --git a/specification/v0_8/docs/a2ui_protocol.md b/specification/v0_8/docs/a2ui_protocol.md index 967eee8f3..2fd49d520 100644 --- a/specification/v0_8/docs/a2ui_protocol.md +++ b/specification/v0_8/docs/a2ui_protocol.md @@ -163,7 +163,8 @@ The following is a complete, minimal example of a JSONL stream that renders a us A2UI's component model is designed for flexibility, separating the protocol from the component set. -### 2.1. Catalog Negotiation +### 2.1. Catalog Negotiation {: #catalog-negotiation } + A **Catalog** defines the contract between the server and the client for the UI that can be rendered. It contains a list of supported component types (e.g., `Row`, `Text`), their properties, and available styles. A catalog is defined by a **Catalog Definition Document**. @@ -633,7 +634,7 @@ A robust client-side interpreter for A2UI should be composed of several key comp This section provides the formal JSON Schema for a single server-to-client message in the A2UI JSONL stream. Each line in the stream must be a valid JSON object that conforms to this schema. It includes the entire base catalog of components, but the components may be swapped out for other components supported by the client. It is optimized to be able to be generated in structured output mode from various LLMs. ```json -{% include "../specification/json/server_to_client_with_standard_catalog.json" %} +{% include "specification/v0_8/json/server_to_client_with_standard_catalog.json" %} ``` ## Section 8: Complete A2UI Client to Server JSON Schema @@ -641,5 +642,5 @@ This section provides the formal JSON Schema for a single server-to-client messa This section provides the formal JSON Schema for a single client-to-server message in the A2UI protocol. ```json -{% include "../specification/json/client_to_server.json" %} +{% include "specification/v0_8/json/client_to_server.json" %} ``` diff --git a/specification/v0_9/README.md b/specification/v0_9/README.md index 0d0a84ebd..ce5dcf94d 100644 --- a/specification/v0_9/README.md +++ b/specification/v0_9/README.md @@ -4,11 +4,11 @@ This directory contains the specification for version 0.9 of A2UI, is the curren **This specification is closed.** -Please do not propose changes here. If you have proposed changes or new features, please open an issue or submit a pull request to the [A2UI repository](https://github.com/gspencergoog/A2UI) for the [v0.10 specification](../v0_10). +Please do not propose changes here. If you have proposed changes or new features, please open an issue or submit a pull request to the [A2UI repository](https://github.com/gspencergoog/A2UI) for the [v0.10 specification](../v0_10/README.md). ## v0.9 Documentation - [Protocol](docs/a2ui_protocol.md) -- [Custom Functions](docs/custom_functions.md) +- [Custom Functions](docs/a2ui_custom_functions.md) - [Extension Specification](docs/a2ui_extension_specification.md) - [Evolution Guide](docs/evolution_guide.md)