@adia-ai/a2ui-runtime 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,215 @@
1
+ # Changelog — @adia-ai/a2ui-runtime
2
+
3
+ Follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and
4
+ [Semantic Versioning](https://semver.org/).
5
+
6
+ > **Renamed from `@adia-ai/a2ui-utils` in v0.3.0.** All releases
7
+ > through v0.2.5 published as `@adia-ai/a2ui-utils`. The package's own
8
+ > description has always called it the "A2UI runtime"; v0.3.0 aligns
9
+ > the package name with its purpose. See v0.3.0 entry for migration
10
+ > notes.
11
+
12
+ ## [Unreleased]
13
+
14
+ _No pending changes._
15
+
16
+ ## [0.3.0] - 2026-05-05
17
+
18
+ **9-package lockstep cut + package rename + LLM extraction.** All 9 published `@adia-ai/*` packages bump 0.2.5 → 0.3.0 per [`docs/specs/package-architecture.md` § 15](../../../docs/specs/package-architecture.md#15-versioning-policy). Internal `@adia-ai/*` dep ranges bump `^0.2.0` → `^0.3.0`.
19
+
20
+ This is a **minor cut on top of v0.2.5 with two BREAKING changes for npm consumers** (acceptable under pre-1.0 semver):
21
+ 1. **Package renamed** — `@adia-ai/a2ui-utils` → `@adia-ai/a2ui-runtime` (this package).
22
+ 2. New 9th lockstep package `@adia-ai/llm` extracted from `@adia-ai/a2ui-compose/llm` (sibling concern).
23
+
24
+ Lockstep policy expanded from 8 to 9 packages. The CI gate `lockstep-versioning` now enforces all 9 share one version.
25
+
26
+ ### Changed
27
+
28
+ - **Package name: `@adia-ai/a2ui-utils` → `@adia-ai/a2ui-runtime`.** Pure rename. No API change. Source path moved from `packages/a2ui/utils/` → `packages/a2ui/runtime/` to match the new name. The `name` field in package.json is the only consumer-visible change; all subpath exports (`./registry`, `./renderer`, `./streams`, `./surface`, `./wiring`, `./dockables`) preserved verbatim.
29
+ - `version`: `0.2.5` → `0.3.0`.
30
+
31
+ ### Migration
32
+
33
+ Update `package.json`:
34
+
35
+ ```diff
36
+ - "@adia-ai/a2ui-utils": "^0.2.0"
37
+ + "@adia-ai/a2ui-runtime": "^0.3.0"
38
+ ```
39
+
40
+ Update imports across the codebase:
41
+
42
+ ```diff
43
+ - import { A2UIRenderer } from '@adia-ai/a2ui-utils/renderer';
44
+ + import { A2UIRenderer } from '@adia-ai/a2ui-runtime/renderer';
45
+
46
+ - import { registry } from '@adia-ai/a2ui-utils/registry';
47
+ + import { registry } from '@adia-ai/a2ui-runtime/registry';
48
+ ```
49
+
50
+ The `@adia-ai/a2ui-utils@0.2.5` and earlier remain on npm and continue to work; the rename only affects new installs and explicit upgrades. There is no deprecated stub on the old name — consumers who don't upgrade keep their existing `@adia-ai/a2ui-utils@0.2.5` installation.
51
+
52
+ ### Why
53
+
54
+ "utils" undersold what this package is and collided visually with `web-modules/runtime/` (which holds `<a2ui-root>`, the surface element). The package's own description has always called it "A2UI runtime — renderer, registry, streams"; the name now matches.
55
+
56
+ ## [0.2.5] - 2026-05-04
57
+
58
+ **8-package lockstep cut.** All 8 published `@adia-ai/*` packages bump 0.2.4 → 0.2.5 per [`docs/specs/package-architecture.md` § 15](../../../docs/specs/package-architecture.md#15-versioning-policy). Internal `@adia-ai/*` dep ranges remain at `^0.2.0` (covers 0.2.5 under semver — patch-cut asymmetry).
59
+
60
+ This is a **patch cut on top of v0.2.4, no BREAKING changes.** Substantive content lives in `web-components` (new `<fields-ui>` form-grid primitive + `draggable-list-item` last-item drop-zone fix, both surfaced by the Tasks UI Playground at `docs/projects/tasks-playground/`); `a2ui-corpus` ships a catalog regen for the new `<fields-ui>` yaml. `a2ui-utils` rides along at version bump only — no source change.
61
+
62
+ ### Changed
63
+
64
+ - `version`: `0.2.4` → `0.2.5`.
65
+ - Internal `@adia-ai/*` dep ranges: unchanged at `^0.2.0` (covers `0.2.5` under semver — patch-cut asymmetry).
66
+
67
+ ## [0.2.4] - 2026-05-04
68
+
69
+ **8-package lockstep cut.** All 8 published `@adia-ai/*` packages bump 0.2.3 → 0.2.4 per [`docs/specs/package-architecture.md` § 15](../../../docs/specs/package-architecture.md#15-versioning-policy). Internal `@adia-ai/*` dep ranges remain at `^0.2.0` (covers 0.2.4 under semver — patch-cut asymmetry).
70
+
71
+ This is a **patch cut on top of v0.2.3, no BREAKING changes.** Substantive content lives in `web-components` (Tier 4 trait library lift, 11 new traits + 2 architectural rewrites, plus accumulated bug fixes); `a2ui-corpus` ships a catalog regen for the new `<demo-toggle-ui>` primitive. Six packages ride along at version + dep-range bump.
72
+
73
+ ### Changed
74
+
75
+ - `version`: `0.2.3` → `0.2.4`.
76
+ - `dependencies["@adia-ai/a2ui-utils"]`: `^0.2.0` (covers `0.2.4`).
77
+
78
+ This package's source tree is byte-identical to 0.2.3. The cut bumps version + the internal dep range only, per the lockstep policy.
79
+
80
+ _Nothing yet._
81
+
82
+ ---
83
+
84
+ ---
85
+
86
+ ## [0.2.3] - 2026-05-04
87
+
88
+ **Lockstep cut.** All 8 published `@adia-ai/*` packages bump
89
+ 0.2.2 → 0.2.3 per
90
+ [`docs/specs/package-architecture.md` § 15](../../../../docs/specs/package-architecture.md#15-versioning-policy).
91
+ Patch cut — no breaking changes.
92
+
93
+ ### Changed
94
+
95
+ - `version`: `0.2.2` → `0.2.3`.
96
+
97
+ ### No source changes
98
+
99
+ `@adia-ai/a2ui-utils` source is byte-identical to `0.2.2`. The cut bumps version only.
100
+
101
+ ## [0.2.2] — 2026-05-02
102
+
103
+ **Lockstep cut.** All 8 published `@adia-ai/*` packages bump 0.2.1 → 0.2.2 per [`docs/specs/package-architecture.md` § 15](../../../../docs/specs/package-architecture.md#15-versioning-policy). Patch cut — no breaking changes.
104
+
105
+ ### Changed
106
+
107
+ - `version`: `0.2.1` → `0.2.2`.
108
+
109
+ ### No source changes
110
+
111
+ `a2ui-utils` source is byte-identical to `0.2.1`. The cut bumps version only.
112
+
113
+ ---
114
+
115
+ ## [0.2.1] — 2026-05-02
116
+
117
+ **Lockstep cut + A2UI registry mappings (`Field`/`Rating`/`ListItem`/`NavItem`/`NavGroup`).** All 8 published `@adia-ai/*` packages bump 0.2.0 → 0.2.1 per [`docs/specs/package-architecture.md` § 15](../../../../docs/specs/package-architecture.md#15-versioning-policy). Patch cut — no breaking changes.
118
+
119
+ ### Changed
120
+
121
+ - `version`: `0.2.0` → `0.2.1`.
122
+
123
+ ### Added — A2UI registry mappings (2026-05-02)
124
+
125
+ Five new A2UI type → AdiaUI tag mappings in `registry.js`:
126
+
127
+ - `Field` → `field-ui`
128
+ - `Rating` → `rating-ui`
129
+ - `ListItem` → `list-item-ui`
130
+ - `NavItem` → `nav-item-ui`
131
+ - `NavGroup` → `nav-group-ui`
132
+
133
+ All 5 components existed in `@adia-ai/web-components`; the registry hadn't been kept in sync. Closing this gap fixes 28/113 hand-authored patterns that previously rendered as `[unknown: X]` placeholders when imported into `/site/playground/a2ui-editor`, plus the render-preview's `streamed-list` example. The `NavItem` and `NavGroup` additions also align with [ADR-0015](../../../.brain/adrs/0015-three-tier-naming-convention.md)'s nav consolidation (the retired `SectionNav`/`SectionNavItem` types are migrated in `@adia-ai/a2ui-corpus`).
134
+
135
+ Smoke side-effect: zettel score 91 → 92; `smoke:engines` flipped from `valid=false score=98/91` to `valid=true score=100/92` because the unregistered types were tripping validation. Per-finding triage in [`docs/reports/playground-examples-review-2026-05-02.md`](../../../docs/reports/playground-examples-review-2026-05-02.md).
136
+
137
+ ---
138
+
139
+ ## [0.2.0] — 2026-05-02
140
+
141
+ **Lockstep cut.** All 8 published `@adia-ai/*` packages now share one
142
+ version, governed by [`docs/specs/package-architecture.md` § 15
143
+ (Versioning Policy)](../../../../docs/specs/package-architecture.md#15-versioning-policy).
144
+
145
+ ### Changed
146
+
147
+ - `version`: `0.0.2` → `0.2.0`.
148
+
149
+ ### No source changes
150
+
151
+ A2UI runtime source (renderer, registry, streams, surface manifest,
152
+ wiring primitives, controllers, dockables) is byte-identical to 0.0.2.
153
+ The cut bumps the version only — utils has no internal `@adia-ai/*`
154
+ dependencies. Five sibling packages depend on this package via
155
+ `^0.0.2` (now `^0.2.0`), which under npm pre-1.0 semver locks them to
156
+ the exact version; the lockstep cut switches them to a working range.
157
+
158
+ ---
159
+
160
+ ## [0.0.2] — 2026-04-22
161
+
162
+ Controllers-move fix. `0.0.1` published but was broken for any
163
+ surface that declared a controller: `wiring-registry.js` held lazy
164
+ `await import('../controllers/*.js')` calls that resolved to
165
+ `packages/a2ui/controllers/` (nonexistent) because the controllers
166
+ subdir still lived under `packages/web-components/` at the time of
167
+ the initial split. `0.0.2` moves `controllers/` into this package
168
+ alongside the wiring code that uses them and retargets the dynamic
169
+ imports to `./controllers/`. **Consumers on `0.0.1` should upgrade
170
+ to `0.0.2`; `0.0.1` is broken for any surface that declares a
171
+ controller.**
172
+
173
+ ### Included
174
+
175
+ - **Controllers** — `controllers/` subdir with `FormController`,
176
+ `DataStreamController`, `SelectionController`, `ToggleController`,
177
+ `AccordionController`, `StateMachineController`, plus the shared
178
+ `BaseController`. Lazy-loaded via dynamic imports from
179
+ `wiringRegistry`. Pure JS — no web-component dependency; only
180
+ extend `BaseController` (host management + subscription + notify).
181
+ Moved in from `packages/web-components/controllers/` after the
182
+ initial carve-out surfaced that `wiring-registry.js` lazy-imports
183
+ these at runtime and the imports couldn't resolve — the six types
184
+ are registered in `wiringRegistry` for lazy resolution so a
185
+ surface only loads what it declares.
186
+ - **`A2UIRenderer`** — processes A2UI protocol messages (`createSurface`,
187
+ `updateComponents`, `wireComponents`, `updateDataModel`, `destroySurface`)
188
+ and writes custom elements into a container. Batched via
189
+ `requestAnimationFrame`.
190
+ - **Registry** — `registry`, `resolveTag`, `registerType`. A2UI protocol
191
+ component name → custom-element tag map, extensible at runtime.
192
+ - **Transports** — `sseStream`, `wsStream`, `mockStream`, `mcpStream`,
193
+ `jsonlStream`. Normalize inbound A2UI messages from the respective
194
+ transports into a consistent async iterable.
195
+ - **`SurfaceManifest`** — design-time manifest model for multi-surface
196
+ apps.
197
+ - **`Surface`** — runtime surface lifecycle primitive.
198
+ - **Wiring layer** — `WiringEngine`, `wiringRegistry`, `createDockables`,
199
+ and the dockable base classes (`Dockable`, `ControllerDock`,
200
+ `DataSourceDock`, `ActionDock`, `ProviderDock`, `LifecycleDock`).
201
+
202
+ ### Notes on the carve-out
203
+
204
+ - Dead exports from the old `web-components/a2ui/index.js`
205
+ (`ContextStore`, `ManifestRuntime`, `A2UIValidator`, `A2UIGenerator`,
206
+ `savePattern`, `importPattern`, `buildPatternJSON`, `retrieval/*`,
207
+ `feedbackStore`) were not carried forward — the referenced source
208
+ files did not exist in this repo. The new `index.js` exports only
209
+ live symbols.
210
+ - `manifest-runtime.js` was deleted during the move — it referenced
211
+ `./context-store.js` which had never been authored. Will return if
212
+ and when a context-store surface is designed.
213
+ - The `ActionDock` constant that mapped A2UI event names to DOM event
214
+ names was renamed to `A2UI_EVENT_TO_DOM` for clarity. Its shape is
215
+ unchanged.
package/README.md ADDED
@@ -0,0 +1,87 @@
1
+ # @adia-ai/a2ui-runtime
2
+
3
+ A2UI runtime — renderer, registry, streams, surface manifest, and wiring
4
+ primitives for the A2UI (Agent-to-UI) protocol. Framework-agnostic;
5
+ pairs with any A2UI-conformant component set.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install @adia-ai/a2ui-runtime
11
+ ```
12
+
13
+ Typically paired with `@adia-ai/web-components` (which provides the
14
+ custom-element catalog the renderer resolves against):
15
+
16
+ ```bash
17
+ npm install @adia-ai/a2ui-runtime @adia-ai/web-components
18
+ ```
19
+
20
+ ## What's in the box
21
+
22
+ - **`A2UIRenderer`** — processes A2UI protocol messages and writes
23
+ custom elements to a container.
24
+ - **`registry`** / **`resolveTag`** / **`registerType`** — A2UI protocol
25
+ component name → custom-element tag map. Extensible via
26
+ `registerType`.
27
+ - **Transports** — `sseStream`, `wsStream`, `mockStream`, `mcpStream`,
28
+ `jsonlStream`. Normalize inbound A2UI messages from SSE, WebSocket,
29
+ mock fixtures, MCP tool calls, or JSONL logs.
30
+ - **`SurfaceManifest`** / **`Surface`** — design-time surface shape +
31
+ runtime lifecycle for cross-surface data flow.
32
+ - **Wiring primitives** — `WiringEngine`, `wiringRegistry`,
33
+ `createDockables`, plus the dockable base classes
34
+ (`ControllerDock`, `DataSourceDock`, `ActionDock`, `ProviderDock`,
35
+ `LifecycleDock`).
36
+ - **Controllers** — lazy-loaded runtime state managers at
37
+ `controllers/`: `FormController`, `DataStreamController`,
38
+ `SelectionController`, `ToggleController`, `AccordionController`,
39
+ `StateMachineController`, plus `BaseController`. Registered in the
40
+ wiring registry for lazy resolution; surface only imports what it
41
+ declares.
42
+
43
+ ## Minimal usage
44
+
45
+ ```js
46
+ import { A2UIRenderer, jsonlStream } from '@adia-ai/a2ui-runtime';
47
+ import '@adia-ai/web-components'; // register the custom elements
48
+
49
+ const container = document.querySelector('#app');
50
+ const renderer = new A2UIRenderer(container);
51
+
52
+ for await (const msg of jsonlStream(fetch('/api/ui-trace.jsonl').then(r => r.body))) {
53
+ renderer.process(msg);
54
+ }
55
+ ```
56
+
57
+ For declarative embedding in HTML, use the `<a2ui-root>` element from
58
+ `@adia-ai/web-modules/runtime` — it wraps `A2UIRenderer` + stream
59
+ wiring in a custom element. (Was `@adia-ai/web-components/patterns/`
60
+ prior to the package split per ADR-0012.)
61
+
62
+ ## Public entry points
63
+
64
+ The default export bundles the common surface. Granular subpath
65
+ imports are available if you want to tree-shake:
66
+
67
+ ```js
68
+ import { A2UIRenderer } from '@adia-ai/a2ui-runtime/renderer';
69
+ import { resolveTag } from '@adia-ai/a2ui-runtime/registry';
70
+ import { sseStream } from '@adia-ai/a2ui-runtime/streams';
71
+ import { SurfaceManifest } from '@adia-ai/a2ui-runtime/surface';
72
+ import { WiringEngine } from '@adia-ai/a2ui-runtime/wiring';
73
+ import { ActionDock } from '@adia-ai/a2ui-runtime/dockables';
74
+ ```
75
+
76
+ ## Relationship to other packages
77
+
78
+ - **`@adia-ai/web-components`** — the custom-element catalog the
79
+ renderer resolves tags against. Depends on this package at runtime
80
+ (as of web-components v0.0.4).
81
+ - **`@adia-ai/web-modules/runtime/a2ui-root`** — the declarative
82
+ `<a2ui-root>` custom element; wraps this package's renderer + stream.
83
+ Extracted from `@adia-ai/web-components/patterns/` per ADR-0012.
84
+
85
+ ## License
86
+
87
+ (See repository root.)
@@ -0,0 +1,73 @@
1
+ import { BaseController } from './base.js';
2
+
3
+ /**
4
+ * Accordion controller — manages expandable section state.
5
+ * Sets [data-accordion-expanded] on expanded sections.
6
+ */
7
+ export class AccordionController extends BaseController {
8
+ static schema = Object.freeze({
9
+ name: 'accordion',
10
+ state: { expanded: 'Set', multiple: 'boolean' },
11
+ commands: ['toggle', 'expand', 'collapse', 'collapseAll', 'expandAll'],
12
+ attributes: ['data-accordion-expanded'],
13
+ });
14
+
15
+ #expanded = new Set();
16
+ #multiple = false;
17
+
18
+ constructor({ multiple = false, initial = [] } = {}) {
19
+ super();
20
+ this.#multiple = multiple;
21
+ for (const key of initial) this.#expanded.add(String(key));
22
+ }
23
+
24
+ onDisconnect(host) {
25
+ const sections = host.querySelectorAll('[data-accordion-expanded]');
26
+ for (const s of sections) s.removeAttribute('data-accordion-expanded');
27
+ }
28
+
29
+ getState() {
30
+ return { expanded: new Set(this.#expanded), multiple: this.#multiple };
31
+ }
32
+
33
+ reflect() {
34
+ if (!this.host) return;
35
+ const sections = this.host.querySelectorAll('[data-accordion-key]');
36
+ for (const section of sections) {
37
+ const key = section.getAttribute('data-accordion-key');
38
+ if (this.#expanded.has(key)) section.setAttribute('data-accordion-expanded', '');
39
+ else section.removeAttribute('data-accordion-expanded');
40
+ }
41
+ }
42
+
43
+ commands = {
44
+ toggle: (key) => {
45
+ key = String(key);
46
+ if (this.#expanded.has(key)) this.#expanded.delete(key);
47
+ else {
48
+ if (!this.#multiple) this.#expanded.clear();
49
+ this.#expanded.add(key);
50
+ }
51
+ this.notify();
52
+ },
53
+ expand: (key) => {
54
+ key = String(key);
55
+ if (!this.#multiple) this.#expanded.clear();
56
+ this.#expanded.add(key);
57
+ this.notify();
58
+ },
59
+ collapse: (key) => {
60
+ this.#expanded.delete(String(key));
61
+ this.notify();
62
+ },
63
+ collapseAll: () => {
64
+ this.#expanded.clear();
65
+ this.notify();
66
+ },
67
+ expandAll: (keys) => {
68
+ if (!this.#multiple) return;
69
+ for (const k of keys) this.#expanded.add(String(k));
70
+ this.notify();
71
+ },
72
+ };
73
+ }
@@ -0,0 +1,68 @@
1
+ /**
2
+ * BaseController — base class for all controllers.
3
+ *
4
+ * Provides: host management, subscription, notification, reflection.
5
+ * Subclass implements: onConnect, onDisconnect, getState, reflect, commands.
6
+ *
7
+ * Usage:
8
+ * class ToggleController extends BaseController {
9
+ * static schema = { name: 'toggle', state: { on: 'boolean' }, commands: ['toggle', 'set'], attributes: ['data-toggle-on'] };
10
+ * #on = false;
11
+ * getState() { return { on: this.#on }; }
12
+ * reflect() { ... }
13
+ * commands = { toggle: () => { ... this.notify(); } };
14
+ * }
15
+ */
16
+
17
+ const validated = new WeakSet();
18
+
19
+ export class BaseController {
20
+ #host = null;
21
+ #subs = new Set();
22
+
23
+ get host() { return this.#host; }
24
+
25
+ connect(host) {
26
+ const ctor = this.constructor;
27
+ if (!validated.has(ctor) && ctor !== BaseController) {
28
+ validated.add(ctor);
29
+ if (!ctor.schema) {
30
+ console.warn(`AdiaUI: ${ctor.name} extends BaseController without static schema`);
31
+ }
32
+ if (this.getState === BaseController.prototype.getState) {
33
+ console.error(`AdiaUI: ${ctor.schema?.name ?? ctor.name} must implement getState()`);
34
+ }
35
+ }
36
+ if (this.#host && this.#host !== host) {
37
+ console.warn(`AdiaUI: ${ctor.schema?.name ?? ctor.name} already connected to a different host`);
38
+ }
39
+ this.#host = host;
40
+ this.onConnect(host);
41
+ this.reflect();
42
+ }
43
+
44
+ disconnect(host) {
45
+ const h = host ?? this.#host;
46
+ if (!h) return;
47
+ this.onDisconnect(h);
48
+ this.#host = null;
49
+ }
50
+
51
+ subscribe(fn) {
52
+ this.#subs.add(fn);
53
+ return () => this.#subs.delete(fn);
54
+ }
55
+
56
+ notify() {
57
+ this.reflect();
58
+ for (const fn of this.#subs) fn();
59
+ }
60
+
61
+ onConnect(host) {}
62
+ onDisconnect(host) {}
63
+ reflect() {}
64
+
65
+ getState() {
66
+ throw new Error(`${this.constructor.schema?.name ?? this.constructor.name}: getState() not implemented`);
67
+ }
68
+ }