Purpose and Scope
A2UI bridges the gap between raw LLM outputs and rich user interfaces. It provides:- Incremental Updates: A JSON Patch-based protocol for efficient UI state transitions.
- Rich Components: Native support for tables, forms, charts, and sanitized markdown.
- Security Sandboxing: Strict validation of patch operations to prevent prototype pollution and unauthorized state access.
- Human-in-the-Loop: Interactive forms that allow users to submit structured data back to the agent.
System Architecture
The A2UI lifecycle involves the transformation of agent-generated patches into a validated UI state, which is then rendered by React components.A2UI Data Flow Diagram
This diagram illustrates the path from a patch operation to the final rendered React component. Sources:apps/web/src/a2ui/renderer.tsx:33-59(), apps/web/src/a2ui/patch.ts:47-89(), apps/web/src/a2ui/types.ts:1-20()
JSON Patch Protocol (patch.ts)
A2UI uses a versioned JSON Patch protocol (v1) to manipulate the UI state. This allows agents to send small updates (e.g., updating a single cell in a table) rather than the entire document.
Patch Operations
The system supports three primary operations defined inPatchOperationKind:
- add: Inserts a value at a specified path or appends to an array using the
-token. - replace: Updates an existing value at a path.
- remove: Deletes a value or array element.
Security and Sanitization
The patch engine implements several security layers to prevent malicious state manipulation:- Prototype Pollution Protection: The
FORBIDDEN_POINTER_TOKENSset (containing__proto__,prototype, andconstructor) is checked during path resolution to prevent attacks on the JavaScript object prototype. - Recursive Cloning: Every value added or replaced is passed through
cloneJsonValueto ensure no shared references exist between the patch and the state. - Budget Enforcement: The
PatchProcessingBudgetlimits the complexity of patches to prevent Denial of Service (DoS) via deeply nested objects or massive arrays.
| Constraint | Default Value | Purpose |
|---|---|---|
maxOpsPerPatch | 100 | Limits the number of operations in a single tick. |
maxPathLength | 1024 | Prevents extremely deep/complex JSON pointers. |
maxApplyMsPerTick | 50ms | Ensures the UI thread is not blocked by heavy patch logic. |
apps/web/src/a2ui/patch.ts:16-45(), apps/web/src/a2ui/patch.ts:69-86(), apps/web/src/a2ui/__tests__/patch.security.test.tsx:9-30()
The Renderer (renderer.tsx)
The A2uiRenderer is the entry point for turning a A2uiDocument into DOM elements. It iterates through a list of components and delegates rendering to the ComponentBody switcher.
Key Components
1. A2uiForm
TheA2uiForm component handles structured user input. It supports checkbox, select, number, and text fields.
- State Management: It maintains internal state for field values using
useStateand initializes them fromfield.defaultValue. - Submission: When the user clicks the submit button, it triggers
onFormSubmitwith anA2uiFormSubmitEvent, containing thecomponentIdand the collectedvalues.
2. A2uiTable
TheA2uiTable maps the agent’s tabular data into the EntityTable UI component.
- Row Headers: By default, the first column is treated as the row header (
isRowHeader: index === 0). - Dynamic Rows: Tables can be updated incrementally by the agent using array index patches (e.g.,
/components/2/props/rows/5).
3. SanitizedMarkdown
To prevent XSS, all markdown content is passed throughSanitizedMarkdown, which ensures that only safe HTML tags and attributes are rendered.
Sources: apps/web/src/a2ui/renderer.tsx:66-97(), apps/web/src/a2ui/renderer.tsx:130-177(), apps/web/src/a2ui/renderer.tsx:99-123()
State Resolution and Normalization
Before rendering, the rawJsonValue resulting from a patch application must be validated and converted into a typed A2uiDocument.
CanvasStatePatchRecord
In the persistence layer, A2UI state transitions are stored asCanvasStatePatchRecord entries. This allows the Web Console to “replay” the state of a UI surface by applying patches in sequence to an initial empty state.
Sources: apps/web/src/a2ui/types.ts:1-20(), apps/web/src/a2ui/renderer.tsx:33-49()
Fuzzing and Robustness
Because A2UI processes JSON payloads from potentially untrusted or hallucinatory agents, the system includes fuzz targets to ensure the parser’s stability.a2ui_json_parser Fuzz Target
Located infuzz/fuzz_targets/a2ui_json_parser.rs, this target stress-tests the Rust-side representation of A2UI data. It ensures that:
- Malformed JSON does not crash the daemon.
- Extremely large payloads are rejected.
- Deeply nested structures do not cause stack overflows.
workspace_patch_parser and process_runner_input_parser provide similar protections for related subsystems that share the JSON-based communication pattern.
Sources: fuzz/Cargo.toml:26-30(), fuzz/fuzz_targets/workspace_patch_parser.rs:29-41(), crates/palyra-common/src/process_runner_input.rs:26-31()