You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: specification/0.9/docs/a2ui_protocol.md
+78-13Lines changed: 78 additions & 13 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -82,8 +82,8 @@ A2UI v0.9 is defined by three interacting JSON schemas.
82
82
83
83
The [`common_types.json`] schema defines reusable primitives used throughout the protocol.
84
84
85
-
-**`stringOrPath` / `numberOrPath` / `booleanOrPath` / `stringArrayOrPath`**: The core of the data binding system. Any property that can be bound to data is defined as an object that accepts either a literal value OR a `path` string ([JSON Pointer]).
86
-
-**`childrenProperty`**: Defines how containers hold children. It supports:
85
+
-**`DynamicString` / `DynamicNumber` / `DynamicBoolean` / `DynamicStringList`**: The core of the data binding system. Any property that can be bound to data is defined as a `Dynamic*` type. It accepts either a literal value, a `path` string ([JSON Pointer]), or a `FunctionCall` (function call).
86
+
-**`ChildList`**: Defines how containers hold children. It supports:
87
87
88
88
-`array`: A static array of string component IDs.
89
89
-`object`: A template for generating children from a data binding list (requires a template `componentId` and a data binding `path`).
@@ -169,8 +169,7 @@ This message is used to send or update the data that populates the UI components
169
169
170
170
-`surfaceId` (string, required): The unique identifier for the UI surface this data model update applies to.
171
171
-`path` (string, optional): A JSON Pointer to a specific location within the data model (e.g., `/user/name`). If omitted or set to `/`, the entire data model for the surface will be replaced.
172
-
-`op` (string, optional): The operation to perform on the data model. Must be 'add', 'replace' or 'remove'. If omitted, defaults to 'replace'.
173
-
-`value` (object): The data to be updated in the data model. This can be any valid JSON object. Required if `op` is 'add' or 'replace'. Not allowed if `op` is 'remove'.
172
+
-`value` (object): The data to be updated in the data model. If present, the value at `path` is updated/created. If this field is omitted, the data at `path` is **removed**.
174
173
175
174
**Example:**
176
175
@@ -179,7 +178,6 @@ This message is used to send or update the data that populates the UI components
179
178
"updateDataModel": {
180
179
"surfaceId": "user_profile_card",
181
180
"path": "/user",
182
-
"op": "replace",
183
181
"value": {
184
182
"name": "Jane Doe",
185
183
"title": "Software Engineer"
@@ -212,8 +210,8 @@ The following example demonstrates a complete interaction to render a Contact Fo
{"updateComponents":{"surfaceId":"contact_form_1","components":[{"id":"root","component":"Column","children":["first_name_label","first_name_field","last_name_label","last_name_field","email_label","email_field","phone_label","phone_field","notes_label","notes_field","submit_button"]},{"id":"first_name_label","component":"Text","text":"First Name"},{"id":"first_name_field","component":"TextField","label":"First Name","value":{"path":"/contact/firstName"},"variant":"shortText"},{"id":"last_name_label","component":"Text","text":"Last Name"},{"id":"last_name_field","component":"TextField","label":"Last Name","value":{"path":"/contact/lastName"},"variant":"shortText"},{"id":"email_label","component":"Text","text":"Email"},{"id":"email_field","component":"TextField","label":"Email","value":{"path":"/contact/email"},"variant":"shortText","checks":[{"call":"email","message":"Please enter a valid email address."}]},{"id":"phone_label","component":"Text","text":"Phone"},{"id":"phone_field","component":"TextField","label":"Phone","value":{"path":"/contact/phone"},"variant":"shortText"},{"id":"notes_label","component":"Text","text":"Notes"},{"id":"notes_field","component":"TextField","label":"Notes","value":{"path":"/contact/notes"},"variant":"longText"},{"id":"submit_button_label","component":"Text","text":"Submit"},{"id":"submit_button","component":"Button","child":"submit_button_label","action":{"name":"submitContactForm"}}]}}
@@ -283,9 +281,11 @@ By default, all components operate in the **Root Scope**.
283
281
- The Root Scope corresponds to the top-level object of the `value` provided in `updateDataModel`.
284
282
- Paths starting with `/` (e.g., `/user/profile/name`) are **Absolute Paths**. They always resolve from the root of the Data Model, regardless of where the component is nested in the UI tree.
285
283
284
+
285
+
286
286
#### Collection Scopes (Relative Paths)
287
287
288
-
When a container component (such as `Column`, `Row`, or `List`) utilizes the **Template** feature of `childrenProperty`, it creates a new **Child Scope** for each item in the bound array.
288
+
When a container component (such as `Column`, `Row`, or `List`) utilizes the **Template** feature of `ChildList`, it creates a new **Child Scope** for each item in the bound array.
289
289
290
290
-**Template Definition:** When a container binds its children to a path (e.g., `path: "/users"`), the client iterates over the array found at that location.
291
291
-**Scope Instantiation:** For every item in the array, the client instantiates the template component.
@@ -341,7 +341,7 @@ When a container component (such as `Column`, `Row`, or `List`) utilizes the **T
341
341
342
342
### Two-Way Binding & Input Components
343
343
344
-
Interactive components that accept user input (`TextField`, `CheckBox`, `Slider`, `MultipleChoice`, `DateTimeInput`) establish a **Two-Way Binding** with the Data Model.
344
+
Interactive components that accept user input (`TextField`, `CheckBox`, `Slider`, `ChoicePicker`, `DateTimeInput`) establish a **Two-Way Binding** with the Data Model.
345
345
346
346
#### The Read/Write Contract
347
347
@@ -362,7 +362,7 @@ It is critical to note that Two-Way Binding is **local to the client**.
362
362
363
363
- User inputs (keystrokes, toggles) do **not** automatically trigger network requests to the server.
364
364
- The updated state is sent to the server only when a specific **User Action** is triggered (e.g., a `Button` click).
365
-
- When a `userAction` is dispatched, the `context` property of the action can reference the modified data paths to send the user's input back to the server.
365
+
- When a `action` is dispatched, the `context` property of the action can reference the modified data paths to send the user's input back to the server.
366
366
367
367
#### Example: Form Submission Pattern
368
368
@@ -379,7 +379,61 @@ It is critical to note that Two-Way Binding is **local to the client**.
379
379
}
380
380
```
381
381
382
-
4. **Send:** When clicked, the client resolves `/formData/email` (getting "jane@example.com") and sends it in the `userAction` payload.
382
+
4. **Send:** When clicked, the client resolves `/formData/email` (getting "jane@example.com") and sends it in the `action` payload.
383
+
384
+
385
+
## Client-Side Logic & Validation
386
+
387
+
A2UI v0.9 generalizes client-side logic into **Functions**. These can be used for validation, data transformation, and dynamic property binding.
388
+
389
+
### Registered Functions
390
+
391
+
The client registers a set of named **Functions** (e.g., `required`, `regex`, `email`, `add`, `concat`) in a `FunctionCatalog`. The server references these functions by name. This avoids sending executable code.
392
+
393
+
Input components (like `TextField`, `CheckBox`) can define a list of checks. Each failure produces a specific error message that can be displayed when the component is rendered. Note that for validation checks, the function must return a boolean.
394
+
395
+
```json
396
+
"checks": [
397
+
{
398
+
"call": "required",
399
+
"args": { "value": { "path": "/formData/zip" } },
400
+
"message": "Zip code is required"
401
+
},
402
+
{
403
+
"call": "regex",
404
+
"args": {
405
+
"value": { "path": "/formData/zip" },
406
+
"pattern": "^[0-9]{5}$"
407
+
},
408
+
"message": "Must be a 5-digit zip code"
409
+
}
410
+
]
411
+
```
412
+
413
+
### Example: Button Validation
414
+
415
+
Buttons can also define `checks`. If any check fails, the button is automatically disabled. This allows the button's state to depend on the validity of data in the model.
"message": "You must accept terms AND provide either email or phone"
433
+
}
434
+
]
435
+
}
436
+
```
383
437
384
438
## Standard Component Catalog
385
439
@@ -448,7 +502,7 @@ If validation fails, the client (or the system acting on behalf of the client) s
448
502
449
503
The protocol also defines messages that the client can send to the server, which are defined in the [`client_to_server.json`] schema. These are used for handling user interactions and reporting client-side information.
450
504
451
-
### `userAction`
505
+
### `action`
452
506
453
507
This message is sent when the user interacts with a component that has an `action` defined, such as a `Button`.
454
508
@@ -460,11 +514,22 @@ This message is sent when the user interacts with a component that has an `actio
460
514
-`timestamp` (string, required): An ISO 8601 timestamp.
461
515
-`context` (object, required): A JSON object containing any context provided in the component's `action` property.
462
516
517
+
### `capabilities`
518
+
519
+
This message is sent by the client upon connection to inform the server of its capabilities, including supported component catalogs and validation catalogs.
520
+
521
+
**Properties:**
522
+
523
+
-`supportedCatalogIds` (array of strings, required): URIs of supported component catalogs.
524
+
-`supportedFunctionCatalogIds`: A list of URIs for the function catalogs supported by the client.
525
+
-`inlineCatalogs`: An array of inline component catalog definitions provided directly by the client (useful for custom or ad-hoc components).
526
+
-`inlineFunctionCatalogs`: An array of function catalog definitions provided directly by the client (useful for custom or ad-hoc functions).
527
+
463
528
### `error`
464
529
465
530
This message is used to report a client-side error to the server.
-**Standard JSON**: The `value` property is now a standard **JSON object**.
179
-
-**Op**: The `op` property is added to allow for more complex updates (e.g., `replace`, `remove`).
181
+
-**Simplified**: The system relies on upsert semantics, so the client will create or update the data model at the specified path, or remove it if the value is null.
180
182
-**Structure**: `{ "name": "Alice" }`
181
183
-**Reason**: LLMs are trained to generate JSON objects. Forcing them to generate an "adjacency list" representation of a map was inefficient and error-prone.
182
184
@@ -204,17 +206,17 @@ Specifying an unknown surfaceId will cause an error. It is recommended that clie
204
206
205
207
**v0.9:**
206
208
207
-
-**Implicit Typing**: `stringOrPath`, `numberOrPath`, etc. are defined in `common_types.json`.
209
+
-**Implicit Typing**: `DynamicString`, `DynamicNumber`, etc. are defined in `common_types.json`.
208
210
-**Structure**: The schema allows `string` OR `{ "path": "..." }`.
209
-
-**Reason**: Much more natural JSON. `{ "text": "Hello" }` is valid. `{ "text": { "path": "/msg" } }` is valid. No need for `{ "text": { "literalString": "Hello" } }`.
211
+
-**Reason**: Much more natural JSON. `{ "text": "Hello" }` is valid. `{ "value": { "path": "/msg" } }` is valid. No need for `{ "text": { "literalString": "Hello" } }`.
210
212
211
213
## 6. Component-Specific Changes
212
214
213
215
### 6.1. Button Context
214
216
215
217
**v0.8.1:**
216
218
217
-
-**Array of Pairs**: `context: [{ "key": "id", "value": "123" }]`
-**Reason**: Consistency with `Text` and `Image` components which already used `usageHint`.
236
+
- Property: **`variant`**.
237
+
- Validation: **`checks`** (generic list of function calls).
238
+
-**Reason**: Consistency with `Text` and `Image` components which already used `variant`. Validation is now more flexible and reusable. Also, `text` was renamed to **`value`** to match other input components.
- Properties: **`value`** (array), **`usageHint`** (enum: `multipleSelection`, `mutuallyExclusive`). The `maxAllowedSelections` property was removed.
247
-
-**Reason**: `ChoicePicker` is a more generic name that covers both radio buttons (mutually exclusive) and checkboxes (multiple selection). The `usageHint` controls the behavior, simplifying the component surface area.
250
+
- Properties: **`value`** (array), **`variant`** (enum: `multipleSelection`, `mutuallyExclusive`). The `maxAllowedSelections` property was removed.
251
+
-**Reason**: `ChoicePicker` is a more generic name that covers both radio buttons (mutually exclusive) and checkboxes (multiple selection). The `variant` controls the behavior, simplifying the component surface area.
248
252
249
253
### 6.4. Slider
250
254
@@ -274,3 +278,20 @@ Specifying an unknown surfaceId will cause an error. It is recommended that clie
274
278
}
275
279
```
276
280
-**Result**: The LLM sees this and can "self-correct" in the next turn.
0 commit comments