-
Notifications
You must be signed in to change notification settings - Fork 1.3k
[dart] Extend a2ui_core for agent SDKs, limited to protocol v0.9 #2439
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 39 commits
18a692c
21149cd
1d7c91e
562329b
01bb545
44d6e85
dd0054c
67373ba
687ee06
d15ddae
50c8fd0
872a574
714fe3a
2d9aa9c
7e00f66
37ac050
5b70af8
9b6d9a8
465f90f
e8d5798
cb64f10
e45b8c6
5440a98
87ac783
fbfc441
07834e0
722645d
b52522a
574b216
58d07f4
9577dbc
6fcb015
3483953
d5a92a2
1f57886
1777790
b088ac0
bb8d0b3
65b6cac
c777166
e233b37
6952efa
22bf371
9ec86e0
e9fe14e
64daa41
d691920
253784b
5ea66da
96c7ca9
10c1cd0
f7eea87
963ed55
8d9950c
4e19fee
615a5c4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| --- | ||
| name: coding | ||
| description: > | ||
| Language agnostic coding skills. | ||
| --- | ||
|
|
||
| # Coding Skills | ||
|
|
||
| This skill provides coding best practices, adopted by the team, not specific to any single language or framework. | ||
|
|
||
| ## Code visibility | ||
|
|
||
| Make every code element as private as it can be. If tests need access, use the | ||
| language's test-visibility mechanism instead of making the element public. | ||
| For example, in Dart, annotate the element with `@visibleForTesting`. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| # Claude Code Context | ||
|
|
||
| Repository skills live in `.agent/skills/`, shared with the other agent tools | ||
| this repo supports. Claude Code only discovers skills under `.claude/skills/`, | ||
| so symlink the skills: | ||
|
|
||
| ```bash | ||
| ln -s ../../.agent/skills/<name> .claude/skills/<name> | ||
| ``` |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,9 +8,11 @@ description: Core SDK specification for catalog representation, reactive state m | |
|
|
||
| This document describes the detailed programmatic specification and architecture of the A2UI Core SDK. The Core SDK serves as the foundational data, state, and processing layer of A2UI. | ||
|
|
||
| This layer handles JSON parsing, state models, JSON pointers, catalogs, and schemas. This logic remains completely framework-agnostic, allowing it to be implemented identically across all target environments (including agent-side or headless languages where there is no renderer). | ||
| This layer handles JSON parsing, state models, JSON pointers, catalogs, and schemas. This logic remains completely framework-agnostic, allowing it to be implemented identically across all target environments (including [**agent**](../../docs/public/concepts/glossary.md#a2ui-agent-and-a2ui-renderer)-side or headless languages where there is no [**renderer**](../../docs/public/concepts/glossary.md#a2ui-agent-and-a2ui-renderer)). | ||
|
|
||
| For a high-level overview of the entire A2UI ecosystem (including the Inference SDK and Framework Adapter structure), see the [A2UI Unified SDK Architecture](../../specification/v0_9_1/docs/sdks_spec.md). For UI framework integration and rendering details, see the [A2UI Framework Adapter Blueprint](a2ui_framework_adapter.blueprint.md). | ||
| For a high-level overview of the entire A2UI ecosystem (including the Inference SDK and [Framework Adapter](../../docs/public/concepts/glossary.md#fw-adapter) structure), see the [A2UI Unified SDK Architecture](../../specification/v0_9_1/docs/sdks_spec.md). For UI framework integration and rendering details, see the [A2UI Framework Adapter Blueprint](a2ui_framework_adapter.blueprint.md). | ||
|
|
||
| Terms in **bold** on first use are defined in the [Glossary](../../docs/public/concepts/glossary.md). | ||
|
|
||
| --- | ||
|
|
||
|
|
@@ -20,15 +22,21 @@ The A2UI Core SDK acts as the central state coordinator. It is designed to repre | |
|
|
||
| Its core responsibilities include: | ||
|
|
||
| 1. **Catalog Representation:** Define `Catalog` structures and pure technical component metadata/schemas (`ComponentApi`, `FunctionApi`). | ||
| 1. **[Catalog](../../docs/public/concepts/glossary.md#catalog) Representation:** Define `Catalog` structures and pure technical [**component**](../../docs/public/concepts/glossary.md#genui-component) metadata/schemas (`ComponentApi`, `FunctionApi`). | ||
| 2. **Protocol Definitions:** Model strongly-typed inbound and outbound message structures (e.g., `RendererToAgent`, `AgentToRenderer`, etc.). | ||
| 3. **Surface State Containers:** Track mutable, long-lived rendering states via `SurfaceModel`, `ComponentModel`, and `DataModel`. | ||
| 3. **[Surface](../../docs/public/concepts/glossary.md#surface) State Containers:** Track mutable, long-lived rendering states via `SurfaceModel`, `ComponentModel`, and `DataModel`. | ||
| 4. **Message Processor:** Parse inbound message sequences to mutate local state containers via `MessageProcessor`. | ||
| 5. **JSON Pointer Scope:** Standardize relative pointer evaluation and reactivity via scoped context managers. | ||
| 6. **Validation:** Performs structural JSON Schema checks, reference checks, loop/recursion analysis, and layout integrity checks. | ||
| 7. **Resolution:** Resolves bound context paths and binds state variables to components for local evaluation. | ||
| 8. **Multi-Version Protocol Branching:** Supports multiple versions of the protocol. | ||
|
|
||
| #### Package Boundary & Non-Goals | ||
|
|
||
| Core implements the responsibilities above and nothing beyond them. Functionality that the [Agent SDK](a2ui_agent.blueprint.md) or [Framework Adapter](a2ui_framework_adapter.blueprint.md) blueprint assigns to its own layer does not belong in core, even where core defines the types that functionality would operate on. Those two blueprints are the reference for what each layer owns. | ||
|
|
||
| Protocol v0.8 is also out of scope. Core implements v0.9 and later; v0.8 adapters, schemas, and capabilities objects are not described here. | ||
|
|
||
| --- | ||
|
|
||
| ### A. High-Level Layer Architecture | ||
|
|
@@ -120,7 +128,6 @@ The core modular components are organized within the `a2ui.core` namespace. Publ | |
| a2ui/core/ | ||
| ├── exceptions # Root exception hierarchy | ||
| ├── basic_catalog/ # Bundled default components and operators | ||
| │ ├── v0_8/ # Conforms to spec v0.8 | ||
| │ ├── v0_9/ # Conforms to spec v0.9, v0.9.1 | ||
| │ └── v1_0/ # Conforms to spec v1.0 | ||
| ├── catalog/ # Catalog declarations | ||
|
|
@@ -138,7 +145,6 @@ a2ui/core/ | |
| │ └── adapters/ # Spec Version Adapters | ||
| │ ├── base # VersionAdapter interface & A2uiProtocolVersion enum | ||
| │ ├── factory # VersionAdapterFactory (hardcoded adapter resolution) | ||
| │ ├── v0_8 # v0.8 adapter | ||
| │ ├── v0_9 # v0.9 adapter | ||
| │ └── v1_0 # v1.0 adapter | ||
| ├── validation/ # Layout validation layer | ||
|
|
@@ -149,11 +155,6 @@ a2ui/core/ | |
| │ ├── node_graph # Reactive node graph traversal engine | ||
| │ └── data_context # Path binding & function evaluator (Internal) | ||
| └── schema/ # Autogenerated protocol models | ||
| ├── v0_8/ # Models for spec v0.8 | ||
| │ ├── common_types | ||
| │ ├── agent_to_renderer | ||
| │ ├── renderer_to_agent | ||
| │ └── renderer_capabilities | ||
| ├── v0_9/ # Models for spec v0.9 and v0.9.1 | ||
| │ ├── common_types | ||
| │ ├── agent_to_renderer | ||
|
|
@@ -178,21 +179,25 @@ A catalog groups component definitions and function definitions together, along | |
|
|
||
| ```typescript | ||
| export enum A2uiProtocolVersion { | ||
| V0_8 = 'v0.8', | ||
| V0_9 = 'v0.9', | ||
| V0_9_1 = 'v0.9.1', | ||
| V1_0 = 'v1.0', | ||
| } | ||
|
|
||
| export interface Catalog<TComponent extends ComponentApi, TFunction extends FunctionApi> { | ||
| readonly id: string; | ||
| readonly protocolVersion: A2uiProtocolVersion; | ||
| readonly components: ReadonlyMap<string, TComponent>; | ||
| readonly functions?: ReadonlyMap<string, TFunction>; | ||
| readonly themeSchema?: Record<string, any>; | ||
| } | ||
| ``` | ||
|
|
||
| A `Catalog` is immutable once constructed. | ||
|
|
||
| A catalog is **version-agnostic**: it declares no protocol version, and a [`protocolVersion` field](../../specification/v1_0/json/catalog_definition.json) in a catalog document is ignored rather than checked against the version the SDK implements. The version comes from the party using the catalog, never from the catalog itself — a validator is constructed for a version, and capabilities objects are keyed by version — so one catalog can serve several protocol versions. | ||
|
|
||
| Parsing a catalog document is parsing untrusted input: raise `A2uiCatalogError` for a missing or non-object document or a `catalogId` conflict. | ||
|
|
||
| #### `ComponentApi` | ||
|
|
||
| The framework-agnostic definition of a component. It defines the name and the exact JSON schema footprint of the component, without any rendering logic. It acts as the single source of truth for the component's contract. | ||
|
|
@@ -250,12 +255,12 @@ Functions in A2UI accept statically resolved values as input arguments (not obse | |
| Functions generally fall into a few common patterns: | ||
|
|
||
| 1. **Pure Logic (Synchronous)**: Functions like `add` or `concat`. Their logic is immediate and depends only on their inputs. They typically return a static value. | ||
| 2. **External State (Reactive)**: Functions like `clock()` or `networkStatus()`. These return long-lived streams that push updates to the UI independently of data model changes. | ||
| 3. **Effect Functions**: Side-effect handlers (e.g., `openUrl`, `closeModal`) that return `void`. These are triggered by user actions rather than interpolation. | ||
| 2. **External State (Reactive)**: Functions like `clock()` or `networkStatus()`. These return long-lived streams that push updates to the UI independently of [**data model**](../../docs/public/concepts/glossary.md#data-model) changes. | ||
| 3. **Effect Functions**: Side-effect handlers (e.g., `openUrl`, `closeModal`) that return `void`. These are triggered by user [**actions**](../../docs/public/concepts/glossary.md#action) rather than interpolation. | ||
|
|
||
| If a function returns a reactive stream, it MUST use an idiomatic listening mechanism that supports standard unsubscription. To properly support an AI agent, functions SHOULD include a schema to generate accurate renderer capabilities. | ||
|
|
||
| #### The Basic Catalog Standard (Core APIs) | ||
| #### The [Basic Catalog](../../docs/public/concepts/glossary.md#basic-catalog) Standard (Core APIs) | ||
|
|
||
| The standard A2UI Basic Catalog specifies a set of core components (Button, Text, Row, Column) and functions. | ||
|
|
||
|
|
@@ -333,7 +338,7 @@ myCustomCatalog = Catalog( | |
|
|
||
| #### `MessageProcessor` | ||
|
|
||
| The "Controller" that accepts the raw stream of A2UI messages, parses them, and mutates the Models. It also handles the aggregation of renderer state for synchronization. | ||
| The "Controller" that accepts the raw stream of [**A2UI messages**](../../docs/public/concepts/glossary.md#a2ui-message), parses them, and mutates the Models. It also handles the aggregation of renderer state for synchronization. | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I like that we're going to encourage agents to read the glossary! I could imagine that improving their performance! |
||
|
|
||
| ```typescript | ||
| class MessageProcessor<T extends ComponentApi> { | ||
|
|
@@ -370,6 +375,12 @@ When a surface is created with `sendDataModel: true`, the renderer is responsibl | |
| - **Surface Lifecycle**: It is an error to receive a `createSurface` message for a `surfaceId` that is already active; `surfaceId` must be globally unique per client session. The processor MUST throw an error or report a validation failure if this occurs. | ||
| - **Component Lifecycle**: If an `updateComponents` message provides an existing `id` but a _different_ `type`, the processor MUST remove the old component and create a fresh one to ensure framework renderers correctly reset their internal state. | ||
|
|
||
| #### [Capabilities Objects](../../docs/public/concepts/glossary.md#capabilities-object) | ||
|
|
||
| Both sides advertise their capabilities to each other. | ||
|
|
||
| Schemas live in `specification/<version>/json/`. v1.0 names the pair [`renderer_capabilities.json`](../../specification/v1_0/json/renderer_capabilities.json) and [`agent_capabilities.json`](../../specification/v1_0/json/agent_capabilities.json). v0.9 and v0.9.1 name the same pair [`client_capabilities.json`](../../specification/v0_9_1/json/client_capabilities.json) and [`server_capabilities.json`](../../specification/v0_9_1/json/server_capabilities.json), carried as `a2uiClientCapabilities` and `a2uiServerCapabilities`. | ||
|
|
||
| #### Generating Renderer Capabilities and Schema Types | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we could ignore v0.8
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Dropped and explained in "Package Boundary & Non-Goals".
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We don't have any Dart/Flutter users on v0.8, so it is okay to drop it for Dart. However, the removal of v0.8 from the blueprint also impacts other SDKs. The Python and TypeScript SDKs need to support v0.8 for backward compatibility. Should we make the drop limited to Dart SDK? |
||
|
|
||
| To dynamically generate the `A2uiRendererCapabilities` payload (specifically `inlineCatalogs`), the processor must convert internal component schemas into valid JSON Schemas. | ||
|
|
@@ -392,7 +403,7 @@ Version adapters isolate minor syntactic differences across protocol specificati | |
| ```typescript | ||
| export interface VersionAdapter { | ||
| readonly version: A2uiProtocolVersion; | ||
| /** Extracts 'theme' (v0.8/v0.9) or 'surfaceProperties' (v1.0+) from createSurface payload. */ | ||
| /** Extracts 'theme' (v0.9) or 'surfaceProperties' (v1.0+) from createSurface payload. */ | ||
| extractSurfaceProperties(payload: Record<string, any>): Record<string, any>; | ||
| } | ||
|
|
||
|
|
@@ -470,7 +481,7 @@ The matrix below details the specific validation checks, their responsible compo | |
| | Validation Category | Specific Validation Check | Responsible Component / Implementation | Raised Error Type | | ||
| | :----------------------- | :------------------------------------------------------------------------------------------------ | :------------------------------------------------------------------------ | :-------------------- | | ||
| | **Protocol Envelope** | Single update type per message (`createSurface`, `updateComponents`, etc.) | `A2uiValidator` (Zod envelope schema) | `A2uiValidationError` | | ||
| | **Protocol Envelope** | Valid `version` tag (`v0.8`, `v0.9`, `v1.0`) & required envelope keys | `A2uiValidator` (Zod envelope schema) | `A2uiValidationError` | | ||
| | **Protocol Envelope** | Valid `version` tag (`v0.9`, `v1.0`) & required envelope keys | `A2uiValidator` (Zod envelope schema) | `A2uiValidationError` | | ||
| | **Surface Lifecycle** | Surface non-existence on `createSurface` (no duplicates) | `MessageProcessor.processCreateSurface()` (`SurfaceGroupModel`) | `A2uiIntegrityError` | | ||
| | **Surface Lifecycle** | Surface existence on `updateComponents`, `updateDataModel`, `deleteSurface` | `MessageProcessor.processUpdateComponents()` / `processUpdateDataModel()` | `A2uiIntegrityError` | | ||
| | **Catalog Negotiation** | `createSurface.catalogId` and component/function `catalogId` match negotiated renderer capability | `new MessageProcessor({ catalogs: [negotiatedCatalog] })` | `A2uiCatalogError` | | ||
|
|
@@ -751,4 +762,41 @@ export class A2uiRecursionError extends A2uiError { | |
| this.name = 'A2uiRecursionError'; | ||
| } | ||
| } | ||
|
|
||
| /** Raised when a data model read or write cannot be satisfied at the given path. */ | ||
| export class A2uiDataError extends A2uiError { | ||
| /* name = 'A2uiDataError' */ | ||
| } | ||
|
|
||
| /** Raised when the surface or component state machine is asked for an illegal transition. */ | ||
| export class A2uiStateError extends A2uiError { | ||
| /* name = 'A2uiStateError' */ | ||
| } | ||
|
|
||
| /** Raised when raw model output cannot be extracted or decoded into a payload. */ | ||
| export class A2uiParseError extends A2uiError { | ||
| /* name = 'A2uiParseError' */ | ||
| } | ||
|
|
||
| /** Raised when a source syntax (e.g. the EXPRESS DSL) cannot be compiled to A2UI messages. */ | ||
| export class A2uiCompileError extends A2uiError { | ||
| /* name = 'A2uiCompileError' */ | ||
| } | ||
|
|
||
| /** Raised when a bound expression cannot be evaluated. */ | ||
| export class A2uiExpressionError extends A2uiError { | ||
| /* name = 'A2uiExpressionError' */ | ||
| } | ||
| ``` | ||
|
|
||
| **Parsing wire JSON raises from this hierarchy, never from the language.** Check the shape before casting and raise `A2uiValidationError` with the offending value attached. A raw `TypeError` or `ClassCastException` escapes the hierarchy a caller can catch. | ||
|
|
||
| **`expect_error.category` in `conformance/conformance_schema.json` names these classes** without the `A2ui` prefix. Add a category alongside the first suite that asserts it. | ||
|
|
||
| --- | ||
|
|
||
| ## 4. Conformance Test Plan | ||
|
|
||
| See [Conformance README](../../conformance/README.md) for setup and schema definitions. | ||
|
|
||
| `conformance/core/` covers this module. | ||
Uh oh!
There was an error while loading. Please reload this page.