AUP 协议规范
智能体 UI 协议定义了结构化跨平台 UI 渲染的语义节点图。智能体组合树;渲染器决定呈现方式。
概述
AUP is a semantic node graph for structured UI rendering. An agent writes an AUP document (a tree of components) to create any UI. The runtime renders the tree, handles events, and manages live data updates.
An AUP document is a JSON object written to a session tree or rendered via aup_render action.
文档格式
An AUP document is a JSON object with 8 core fields and 2 universal props.
Core Fields
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Unique node identifier. Must be unique across the entire tree. |
type | string | Yes | Primitive name from the registry. Read /primitives to see all available types. |
props | object | No | Primitive-specific attributes. Read /primitives/:name for full props schema of each type. |
src | string | No | AFS path for read-only data binding. The client reads this path on mount and subscribes for live updates. Supported by: chart, map, moonphase. |
bind | string | No | AFS path for read-write data binding. Primarily used with input types — value changes are written back to this path. |
state | object | No | UI-local state. Used for: overlay.open (boolean), input value (state.value), text heading level (state.level). State is maintained client-side. |
events | object | No | Event bindings. Each key is an event name (click, change, send, sort, select, confirm, cancel, dismiss). Two dispatch modes: (1) exec mode: { exec: '/afs/path', args: {} } triggers AFS exec server-side. (2) target mode: { target: 'nodeId', set: { src, props, state } } directly updates another node. Supports $args.* placeholders. Special target '_root' with set.page navigates to a named page. |
children | array | No | Child component nodes. Only containable primitives accept children: view, overlay, chat, deck, ticker, frame (via iframe), unknown. |
Universal Props
These props can appear on any node type:
| Field | Type | Required | Description |
|---|---|---|---|
region | string | No | Universal prop — overlay-grid placement region. Values: top-start, top-center, top-end, mid-start, mid-center, mid-end, lower-start, lower-center, lower-end, ticker, bottom-start, bottom-center, bottom-end. |
role | string | No | Universal prop — broadcast semantic role for overlay theme styling. The role declares WHAT the element is; the overlay theme handles HOW it looks. Roles: live-badge, clock, viewer-count, speaker-bar, hashtag, logo, data-widget, alert, featured-comment, score-bug, lower-third, ticker-item. |
空间意图
Fine-grained layout control for view containers. Pass an object as the layout prop instead of a string.
| Field | Type | Required | Description |
|---|---|---|---|
direction | string | No | row = horizontal flex, column = vertical flex (default), grid = auto-fill responsive grid, stack = all children layered on top of each other, overlay-grid = 15-region broadcast band system (TV industry standard) with pointer-events passthrough. |
align | string | No | Main axis alignment. Values: start, center, end, stretch. |
crossAlign | string | No | Cross axis alignment. Values: start, center, end, stretch. |
gap | number | string | No | Gap between children in px or CSS value. |
wrap | boolean | No | Allow flex wrapping. |
overflow | string | No | Overflow behavior. Values: visible, hidden, scroll, auto. |
Example
{
"layout": {
"direction": "row",
"align": "center",
"crossAlign": "center",
"gap": 16,
"wrap": true
}
}尺寸
Sizing constraints via the view.props.size object.
| Field | Type | Required | Description |
|---|---|---|---|
width | string | No | CSS width (e.g. "100%", "400px") |
maxWidth | string | No | CSS max-width (e.g. "800px") |
height | string | No | CSS height |
flex | string | No | CSS flex shorthand (e.g. "1", "0 0 auto") |
主题令牌
Themes provide color tokens. Set the theme in the aup_render action (style param) or page meta. Read /themes/:name for available tokens.
bgBackgroundsurfaceSurface / cardborderBorder colortextPrimary textaccentAccent / interactivemutedSecondary text覆盖层主题
Overlay themes are broadcast 'graphics packages' for overlay-grid layouts. Like TV graphics (CNN vs ESPN vs Apple Keynote), they control the visual identity of overlay elements. Agents declare semantic roles (WHAT), themes handle styling (HOW). Set theme prop on a view with layout: 'overlay-grid' to activate.
Presets
Default — subtle glass effects, system fonts. Works with any content underneath.
CNN Breaking News — red-dominant, bold condensed type (Barlow Condensed), sharp corners, high-contrast badges.
Apple Keynote — frosted glass, large radii, Inter font, premium feel, generous padding.
Roles (12)
live-badge— LIVE indicator, recording statusclock— time display with tabular-numsviewer-count— audience/viewer counterspeaker-bar— name + title card for current speakerhashtag— event hashtag or topic taglogo— brand logo placementdata-widget— live data card (stats, weather, stocks)alert— breaking/urgent notificationfeatured-comment— highlighted audience commentscore-bug— sports score compact displaylower-third— TV-standard name/title barticker-item— individual ticker content item
Custom Theme
Pass an object instead of a string for custom themes: { badgeBg: "#ff6b00", cardRadius: "0", fonts: ["CustomFont:wght@400;700"] }. camelCase keys are mapped to --overlay-* CSS variables.
Example
{
"id": "themed-overlay",
"type": "view",
"props": {
"layout": "overlay-grid",
"theme": "cnn"
},
"children": [
{
"id": "live",
"type": "text",
"props": {
"region": "top-start",
"role": "live-badge",
"content": "\u25cf LIVE"
}
},
{
"id": "speaker",
"type": "view",
"props": {
"region": "lower-start",
"role": "speaker-bar",
"layout": "column"
},
"children": [
{ "id": "sp-name", "type": "text",
"props": { "content": "Jane Doe" } },
{ "id": "sp-title", "type": "text",
"props": { "content": "CEO, Acme Corp",
"scale": "xs" } }
]
}
]
}国际化
AUP supports internationalization via server-side $t() resolution. Translation is transparent to the agent — use $t(key) in any string prop, and the server resolves it before the tree reaches the client.
Page Templates
Use $t(key) syntax in AUP node string props for translatable text. The pageResolver resolves all $t() placeholders server-side before rendering.
{
"id": "greeting",
"type": "text",
"props": { "content": "$t(welcome)" }
}Locale Files
Translation files live in .aup/locales/ as JSON:
en.json→{ "welcome": "Welcome" }zh.json→{ "welcome": "欢迎" }ja.json→{ "welcome": "ようこそ" }
Adding a new language = adding a new .json file.
AFS Data Convention
AFS data uses a suffix convention for locale variants: intro.json (default), intro.zh.json (Chinese), intro.ja.json (Japanese). When a node's src binds to an AFS path, the runtime reads with locale context. Fallback chain: zh-CN → zh → default.
Locale State
Locale is session-level. Set via ?locale=zh URL param or the settings bar. Changing locale re-renders the current page with the new locale. Pass locale in aup_render: { root, fullPage: true, style: "midnight", locale: "zh" }.
事件模型
Events flow: user interacts → client sends aup_event → server resolves node events config. Three dispatch modes:
- exec: calls AFS exec on the specified path; agent responds with
aup_patch. - target+set: directly patches another node (e.g. update surface src); no exec round-trip needed.
$args.*placeholders in set values are resolved from the event data payload. - page: navigates to a named page; AUP session resolves page tree + style from the app's pageResolver.
Event Payload Format
{
"type": "aup_event",
"nodeId": "string",
"event": "string",
"data": "object (optional)"
}Supported Events (11)
| Event | Description |
|---|---|
click | Fired by action buttons |
change | Fired by input fields; data: { value } |
send | Fired by chat input |
sort | Fired by table header click |
select | Fired by table row click; overlay choice submit |
confirm | Fired by overlay confirm button |
cancel | Fired by overlay cancel button |
dismiss | Fired by overlay toast dismiss |
complete | Fired by deck when reaching the last slide without loop |
load | Fired by frame when iframe finishes loading |
error | Fired by frame when iframe fails to load |
message | Fired by frame when bridge receives postMessage from iframe |
智能体工作流
How an agent creates and manages UI:
- Read /primitives to discover available components
- Read /primitives/:name for detailed props and examples
- Read /themes to choose a visual theme
- Read /examples for complete working documents
- Build an AUP node tree (JSON) with unique IDs
- Call aup_render action with { root, fullPage: true, style: "midnight", chrome: true, locale: "en" } — chrome shows lang/theme/mode toolbar
- Handle events: when user interacts, event triggers your AFS exec path
- Update UI: send aup_patch ops (create/update/remove/reorder nodes)
Frame 工作流
AUP is the OS, pages are applications running inside it. Use frame when you need a complete HTML page (complex JS, third-party libraries, Canvas/WebGL, or any content that needs its own document context). For standard UI, prefer native AUP primitives.
When to Use Frame
- Page needs its own JavaScript runtime (games, visualizations, third-party widgets)
- Content includes <script> tags or complex CSS that would conflict with the surface
- You want crash isolation — if the page breaks, the surface survives
- Embedding external URLs (documentation sites, dashboards, tools)
Steps
- Write a complete HTML page to AFS:
write('/ui/web/pages/mypage', { content: '<html>...</html>', format: 'html' }) - Reference it in the AUP tree:
{ type: 'frame', props: { src: '/pages/mypage', bridge: true } } - The surface resolves /pages/mypage to an HTTP URL with session tokens
- The iframe loads the page in a sandbox with the bridge script injected
Bridge API
When bridge: true, the page gets window.aup — a lightweight postMessage API for communicating with the parent surface. The bridge script is ~2KB, auto-injected.
| Method | Description |
|---|---|
aup.toast(message, intent) | Show a toast notification on the surface. intent: 'info' | 'success' | 'warning' | 'error' |
aup.navigate(path) | Navigate the iframe to another page. path: '/pages/other-page' |
aup.fetch(path) | Read AFS data from the surface. Returns a Promise with the result. |
aup.emit(event, data) | Send a custom event to the surface (routed as aup_event via WebSocket) |
aup.on(event, callback) | Listen for messages sent from the surface to the iframe |
Example
{
"id": "app",
"type": "view",
"props": {
"layout": { "direction": "row", "gap": 0 },
"size": { "height": "100vh" }
},
"children": [
{
"id": "sidebar",
"type": "view",
"props": {
"layout": "column",
"size": { "width": "240px" },
"variant": "card"
},
"children": [
{ "id": "nav", "type": "text",
"props": { "content": "Navigation", "level": 3 } }
]
},
{
"id": "main",
"type": "frame",
"props": {
"src": "/pages/sales-chart",
"bridge": true,
"size": { "height": "100%" }
}
}
]
}降级
When a device does not support a primitive, AUP applies degradation chains. Each chain is tried left-to-right; the first supported type wins. Degraded nodes are annotated with _degradedFrom in props.
| Primitive | Degradation Chain |
|---|---|
globe | map → media → text |
chart | table → text |
map | media → text |
editor | input → text |
canvas | media → text |
rtc | unsupported |
calendar | table → text |
time | text |
overlay | text |
The degradeTree(node, caps) function walks the AUP tree and degrades unsupported primitives. It returns a new tree (does not mutate the original). A PrimitiveCap of "native", "webview", or "partial" is considered supported; "unsupported" or missing means not supported.