Skip to content
Merged
Show file tree
Hide file tree
Changes from 39 commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
18a692c
-
polina-c Aug 28, 2026
21149cd
Extend a2ui_core for agent SDKs, limited to protocol v0.9
polina-c Aug 28, 2026
1d7c91e
Correct the claim that only agents narrow catalogs
polina-c Aug 28, 2026
562329b
Drop a vague qualifier and fix formatting
polina-c Aug 28, 2026
01bb545
Itemise the core non-goals instead of explaining them
polina-c Aug 28, 2026
44d6e85
-
polina-c Aug 28, 2026
dd0054c
Update message-processor.conformance.test.ts
polina-c Aug 28, 2026
67373ba
-
polina-c Aug 28, 2026
687ee06
Merge branch 'main' into dart-a2ui-core
polina-c Aug 28, 2026
d15ddae
Update a2ui_core.blueprint.md
polina-c Aug 28, 2026
50c8fd0
Merge remote-tracking branch 'refs/remotes/origin/dart-a2ui-core' int…
polina-c Aug 28, 2026
872a574
Update a2ui_core.blueprint.md
polina-c Aug 28, 2026
714fe3a
Update a2ui_core.blueprint.md
polina-c Aug 28, 2026
2d9aa9c
Update a2ui_core.blueprint.md
polina-c Aug 28, 2026
7e00f66
Update a2ui_core.blueprint.md
polina-c Aug 28, 2026
37ac050
-
polina-c Aug 28, 2026
5b70af8
Update a2ui_core.blueprint.md
polina-c Aug 28, 2026
9b6d9a8
Update a2ui_core.blueprint.md
polina-c Aug 28, 2026
465f90f
Update a2ui_core.blueprint.md
polina-c Aug 28, 2026
e8d5798
Update a2ui_core.blueprint.md
polina-c Aug 28, 2026
cb64f10
-
polina-c Aug 28, 2026
e45b8c6
Pin dart format language version in fix_format.sh
polina-c Aug 28, 2026
5440a98
-
polina-c Aug 29, 2026
87ac783
Merge branch 'main' into dart-a2ui-core
polina-c Aug 31, 2026
fbfc441
Merge branch 'dart-a2ui-core-draft' into dart-a2ui-core
polina-c Aug 31, 2026
07834e0
Update a2ui_core.blueprint.md
polina-c Aug 31, 2026
722645d
Merge branch 'main' into dart-a2ui-core
polina-c Aug 31, 2026
b52522a
Merge branch 'main' of github.com:a2ui-project/a2ui into dart-a2ui-core
polina-c Sep 1, 2026
574b216
Update pubspec.yaml
polina-c Sep 1, 2026
58d07f4
-
polina-c Sep 1, 2026
9577dbc
-
polina-c Sep 1, 2026
6fcb015
-
polina-c Sep 2, 2026
3483953
-
polina-c Sep 2, 2026
d5a92a2
-
polina-c Sep 2, 2026
1f57886
-
polina-c Sep 2, 2026
1777790
Update a2ui_core.blueprint.md
polina-c Sep 2, 2026
b088ac0
Update a2ui_core.blueprint.md
polina-c Sep 2, 2026
bb8d0b3
Merge branch 'main' of https://github.com/a2ui-project/a2ui into dart…
polina-c Sep 2, 2026
65b6cac
Fix missing comma in web_core tsconfig.json
polina-c Sep 2, 2026
c777166
Delete CLAUDE.md
polina-c Sep 2, 2026
e233b37
-
polina-c Sep 3, 2026
6952efa
-
polina-c Sep 3, 2026
22bf371
Update CHANGELOG.md
polina-c Sep 3, 2026
9ec86e0
Update a2ui_core.blueprint.md
polina-c Sep 3, 2026
e9fe14e
added conformance tests
polina-c Sep 3, 2026
64daa41
address: The integrity check isn't invoked by the message processor.
polina-c Sep 3, 2026
d691920
Update processor.dart
polina-c Sep 3, 2026
253784b
Update validator.dart
polina-c Sep 3, 2026
5ea66da
Merge branch 'main' into dart-a2ui-core
polina-c Sep 3, 2026
96c7ca9
Delete SKILL.md
polina-c Sep 3, 2026
10c1cd0
Merge remote-tracking branch 'refs/remotes/origin/dart-a2ui-core' int…
polina-c Sep 3, 2026
f7eea87
Update ConformanceTest.kt
polina-c Sep 3, 2026
963ed55
-
polina-c Sep 3, 2026
8d9950c
-
polina-c Sep 3, 2026
4e19fee
-
polina-c Sep 3, 2026
615a5c4
-
polina-c Sep 3, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .agents/skills/coding/SKILL.md
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`.
9 changes: 9 additions & 0 deletions .claude/CLAUDE.md
Comment thread
polina-c marked this conversation as resolved.
Outdated
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>
```
2 changes: 1 addition & 1 deletion .github/workflows/flutter_packages_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ jobs:
- name: Generate testing matrix
id: generate_matrix
run: |
DIRS_TO_TEST=$(find samples -name pubspec.yaml -not -path "*/.dart_tool/*" -not -path "*/e2e_test/*" -exec dirname {} \;)
DIRS_TO_TEST=$(find samples dart -name pubspec.yaml -not -path "*/.dart_tool/*" -not -path "*/e2e_test/*" -exec dirname {} \;)

JSON_MATRIX="["
FIRST=true
Expand Down
86 changes: 67 additions & 19 deletions blueprints/modules/a2ui_core.blueprint.md
Original file line number Diff line number Diff line change
Expand Up @@ -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).

---

Expand All @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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.
Expand Down Expand Up @@ -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.

Expand Down Expand Up @@ -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.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The 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> {
Expand Down Expand Up @@ -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

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we could ignore v0.8

@polina-c polina-c Sep 2, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dropped and explained in "Package Boundary & Non-Goals".

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The 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.
Expand All @@ -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>;
}

Expand Down Expand Up @@ -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` |
Expand Down Expand Up @@ -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.
2 changes: 2 additions & 0 deletions conformance/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ Test suites are organized by functional domain:
- `core/catalog.yaml`: Contains test cases for catalog operations (prune, render, load).
- `core/accessibility.yaml`: Contains test cases for accessibility attributes and checks.
- `core/validator.yaml`: Contains test cases for schema and structural validators, verifying structural integrity, cycle detection, and reachability.
- `core/data_model.yaml`: Contains test cases for the reactive data model, verifying JSON Pointer reads and writes, container creation, deletion, and observer notification.
- `core/message_processor.yaml`: Contains test cases for the message processor's state machine. Written in the case vocabulary of the `v1_0` branch, whose suite of the same name is the primary one, so the two converge rather than conflict.

### Agent (`agent/`)

Expand Down
Loading
Loading