@threadplane/a2ui 0.3.2 → 0.3.3
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/README.md +144 -0
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# @threadplane/a2ui
|
|
2
|
+
|
|
3
|
+
The A2UI (Agent-to-UI) protocol layer behind generative UI in [Threadplane](https://github.com/cacheplane/threadplane), the open-source thread-plane for agents, targeting the **A2UI v0.9.1 stable release**. It defines the wire format an agent uses to drive a UI surface, plus the parser and resolver that read it — framework-agnostic, pure TypeScript, no Angular dependency, usable in any TypeScript environment.
|
|
4
|
+
|
|
5
|
+
<p align="center">
|
|
6
|
+
<a href="https://www.npmjs.com/package/@threadplane/a2ui">
|
|
7
|
+
<img alt="npm version" src="https://img.shields.io/npm/v/@threadplane%2Fa2ui?color=15253E&labelColor=0A0A0A&style=flat-square" />
|
|
8
|
+
</a>
|
|
9
|
+
<a href="https://opensource.org/licenses/MIT">
|
|
10
|
+
<img alt="MIT" src="https://img.shields.io/badge/License-MIT-15253E?labelColor=0A0A0A&style=flat-square" />
|
|
11
|
+
</a>
|
|
12
|
+
</p>
|
|
13
|
+
|
|
14
|
+
## What it does
|
|
15
|
+
|
|
16
|
+
- **Protocol type system** — full TypeScript type vocabulary for every A2UI v0.9 message, component, dynamic value, action, and client→agent message.
|
|
17
|
+
- **Streaming message parser** — `createA2uiMessageParser()` returns a stateful parser that accepts JSONL chunks from a streaming agent response and emits typed `A2uiMessage` values as lines complete.
|
|
18
|
+
- **Dynamic value resolution** — `resolveDynamic()` resolves a dynamic value (bare literal or `{ path }` binding) against a client data model; `isPathRef` / `isFunctionCall` guards narrow dynamic values before use.
|
|
19
|
+
- **JSON-pointer utilities** — `getByPointer`, `setByPointer`, and `deleteByPointer` navigate and mutate the A2UI client data model using JSON-pointer paths, including the v0.9 array-delete rule (index set to `undefined`, length preserved).
|
|
20
|
+
- **Runtime-neutral** — pure TypeScript, no runtime dependencies, works in browsers and Node.js alike. Consumed by `@threadplane/chat` to render agent-emitted generative UI.
|
|
21
|
+
|
|
22
|
+
## Install
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm install @threadplane/a2ui
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
No peer dependencies required.
|
|
29
|
+
|
|
30
|
+
## Quick start
|
|
31
|
+
|
|
32
|
+
### Parse a streaming A2UI response
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
import { createA2uiMessageParser } from '@threadplane/a2ui';
|
|
36
|
+
import type { A2uiMessage } from '@threadplane/a2ui';
|
|
37
|
+
|
|
38
|
+
const parser = createA2uiMessageParser();
|
|
39
|
+
|
|
40
|
+
// Feed chunks as they arrive from your agent's streaming response:
|
|
41
|
+
function onChunk(chunk: string): void {
|
|
42
|
+
const messages: A2uiMessage[] = parser.push(chunk);
|
|
43
|
+
for (const msg of messages) {
|
|
44
|
+
if ('createSurface' in msg) {
|
|
45
|
+
console.log('New surface:', msg.createSurface.surfaceId);
|
|
46
|
+
} else if ('updateComponents' in msg) {
|
|
47
|
+
console.log('Components for:', msg.updateComponents.surfaceId);
|
|
48
|
+
} else if ('updateDataModel' in msg) {
|
|
49
|
+
console.log('Data model update at:', msg.updateDataModel.path ?? '/');
|
|
50
|
+
} else if ('deleteSurface' in msg) {
|
|
51
|
+
console.log('Delete surface:', msg.deleteSurface.surfaceId);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Every envelope carries `"version": "v0.9"`. The component whose `id` is `"root"` is the tree root — rendering can begin as soon as it arrives, and the tree fills in progressively.
|
|
58
|
+
|
|
59
|
+
### Resolve dynamic values against a data model
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
import { resolveDynamic } from '@threadplane/a2ui';
|
|
63
|
+
|
|
64
|
+
const model = { user: { name: 'Alice' } };
|
|
65
|
+
|
|
66
|
+
// Bare literal — passes through unchanged
|
|
67
|
+
const label = resolveDynamic('Submit', model);
|
|
68
|
+
// => 'Submit'
|
|
69
|
+
|
|
70
|
+
// Path binding — resolves against the model via JSON pointer
|
|
71
|
+
const name = resolveDynamic({ path: '/user/name' }, model);
|
|
72
|
+
// => 'Alice'
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### JSON-pointer utilities
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
import { getByPointer, setByPointer, deleteByPointer } from '@threadplane/a2ui';
|
|
79
|
+
|
|
80
|
+
const model = { items: [{ id: 1 }, { id: 2 }] };
|
|
81
|
+
|
|
82
|
+
getByPointer(model, '/items/0/id'); // => 1
|
|
83
|
+
const updated = setByPointer(model, '/items/0/id', 99);
|
|
84
|
+
const removed = deleteByPointer(updated, '/items/0/id');
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Capabilities
|
|
88
|
+
|
|
89
|
+
### Protocol message parsing
|
|
90
|
+
|
|
91
|
+
`createA2uiMessageParser()` returns an `A2uiMessageParser` with a single `push(chunk: string): A2uiMessage[]` method. The parser is stateful — it buffers partial lines between calls and emits complete messages as JSONL lines arrive. Malformed lines are silently skipped (safe for mid-stream partial JSON), and unknown envelope keys — such as future v1.0 messages — are skipped rather than treated as errors.
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
const parser = createA2uiMessageParser();
|
|
95
|
+
const messages = parser.push(chunk); // A2uiMessage[]
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Dynamic value resolution
|
|
99
|
+
|
|
100
|
+
`resolveDynamic(value, model, scope?)` resolves A2UI v0.9 dynamic values:
|
|
101
|
+
|
|
102
|
+
- Bare literals (string, number, boolean, string arrays, plain objects) — pass through unchanged.
|
|
103
|
+
- `{ path: string }` — looked up in `model` via JSON pointer. If an `A2uiScope` is provided, relative paths resolve against `scope.basePath` (used inside `children` templates).
|
|
104
|
+
- `{ call: string, args? }` — client-side function calls, executed through the registry passed as the fourth argument (`createA2uiFunctionRegistry()` provides the standard set: `formatString` with `${...}` interpolation, `formatNumber`, `formatCurrency`, `formatDate`, `pluralize`, `and`, `or`, `not`). Args resolve recursively. Without a registry, or for unknown names, calls resolve to `undefined`.
|
|
105
|
+
|
|
106
|
+
Type guards:
|
|
107
|
+
|
|
108
|
+
| Guard | Narrows to |
|
|
109
|
+
|---|---|
|
|
110
|
+
| `isPathRef(v)` | `{ path: string }` |
|
|
111
|
+
| `isFunctionCall(v)` | `{ call: string; args?: Record<string, unknown> }` |
|
|
112
|
+
|
|
113
|
+
### JSON-pointer utilities
|
|
114
|
+
|
|
115
|
+
Three immutable helpers operate on `Record<string, unknown>` data models:
|
|
116
|
+
|
|
117
|
+
| Function | Description |
|
|
118
|
+
|---|---|
|
|
119
|
+
| `getByPointer(model, pointer)` | Read the value at `pointer`. Returns `undefined` if the path does not exist. |
|
|
120
|
+
| `setByPointer(model, pointer, value)` | Return a new model with `value` written at `pointer`. |
|
|
121
|
+
| `deleteByPointer(model, pointer)` | Return a new model with the key at `pointer` removed. Array indices are set to `undefined`, preserving length (v0.9 rule). |
|
|
122
|
+
|
|
123
|
+
Pointers follow standard `/segment/segment` syntax. An empty pointer (`''` or `'/'`) targets the root.
|
|
124
|
+
|
|
125
|
+
### Type system
|
|
126
|
+
|
|
127
|
+
`@threadplane/a2ui` exports the complete A2UI v0.9 type vocabulary. Categories include:
|
|
128
|
+
|
|
129
|
+
- **Protocol constants** — `A2UI_WIRE_VERSION` (`'v0.9'`), `A2UI_MIME_TYPE` (`'application/a2ui+json'`), `A2UI_BASIC_CATALOG_ID`
|
|
130
|
+
- **Envelopes** — `A2uiMessage`, `A2uiCreateSurface`, `A2uiUpdateComponents`, `A2uiUpdateDataModel`, `A2uiDeleteSurface`
|
|
131
|
+
- **Components** — `A2uiComponent`, `A2uiComponentBase`, `A2uiCatalogComponent`, plus per-component shapes: `A2uiText`, `A2uiImage`, `A2uiIcon`, `A2uiVideo`, `A2uiAudioPlayer`, `A2uiRow`, `A2uiColumn`, `A2uiList`, `A2uiCard`, `A2uiTabs`, `A2uiModal`, `A2uiDivider`, `A2uiButton`, `A2uiCheckBox`, `A2uiTextField`, `A2uiDateTimeInput`, `A2uiChoicePicker`, `A2uiSlider`
|
|
132
|
+
- **Dynamic values** — `DynamicString`, `DynamicNumber`, `DynamicBoolean`, `DynamicStringList`, `DynamicValue`, `A2uiPathRef`, `A2uiFunctionCall`
|
|
133
|
+
- **Actions and checks** — `A2uiAction`, `A2uiEventAction`, `A2uiFunctionAction`, `A2uiCheck`, `A2uiCheckable`
|
|
134
|
+
- **Client → agent** — `A2uiActionMessage`, `A2uiErrorMessage`, `A2uiClientDataModel`, `A2uiClientCapabilities`
|
|
135
|
+
- **Theming** — `A2uiTheme`
|
|
136
|
+
- **Parser and scope** — `A2uiMessageParser`, `A2uiScope`, `A2uiChildren`
|
|
137
|
+
|
|
138
|
+
## Reliability
|
|
139
|
+
|
|
140
|
+
Pure TypeScript with no runtime dependencies. No `Buffer`, no `process`, no DOM — safe in any TypeScript environment. Follows the repo's patch-only `0.0.x` release policy. The "Library — lint / test / build" CI job runs on every pull request.
|
|
141
|
+
|
|
142
|
+
## License
|
|
143
|
+
|
|
144
|
+
MIT. See [LICENSE](../../LICENSE).
|
package/package.json
CHANGED