AFSHomeShowcase

AUP 协议规范

智能体 UI 协议定义了结构化跨平台 UI 渲染的语义节点图。智能体组合树;渲染器决定呈现方式。

概述

Version 0.4Agentic UI Protocol

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

FieldTypeRequiredDescription
idstringYesUnique node identifier. Must be unique across the entire tree.
typestringYesPrimitive name from the registry. Read /primitives to see all available types.
propsobjectNoPrimitive-specific attributes. Read /primitives/:name for full props schema of each type.
srcstringNoAFS path for read-only data binding. The client reads this path on mount and subscribes for live updates. Supported by: chart, map, moonphase.
bindstringNoAFS path for read-write data binding. Primarily used with input types — value changes are written back to this path.
stateobjectNoUI-local state. Used for: overlay.open (boolean), input value (state.value), text heading level (state.level). State is maintained client-side.
eventsobjectNoEvent 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.
childrenarrayNoChild 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:

FieldTypeRequiredDescription
regionstringNoUniversal 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.
rolestringNoUniversal 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.

FieldTypeRequiredDescription
directionstringNorow = 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.
alignstringNoMain axis alignment. Values: start, center, end, stretch.
crossAlignstringNoCross axis alignment. Values: start, center, end, stretch.
gapnumber | stringNoGap between children in px or CSS value.
wrapbooleanNoAllow flex wrapping.
overflowstringNoOverflow 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.

FieldTypeRequiredDescription
widthstringNoCSS width (e.g. "100%", "400px")
maxWidthstringNoCSS max-width (e.g. "800px")
heightstringNoCSS height
flexstringNoCSS 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.

bgBackground
surfaceSurface / card
borderBorder color
textPrimary text
accentAccent / interactive
mutedSecondary 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

minimal

Default — subtle glass effects, system fonts. Works with any content underneath.

cnn

CNN Breaking News — red-dominant, bold condensed type (Barlow Condensed), sharp corners, high-contrast badges.

apple

Apple Keynote — frosted glass, large radii, Inter font, premium feel, generous padding.

Roles (12)

  • live-badge — LIVE indicator, recording status
  • clock — time display with tabular-nums
  • viewer-count — audience/viewer counter
  • speaker-bar — name + title card for current speaker
  • hashtag — event hashtag or topic tag
  • logo — brand logo placement
  • data-widget — live data card (stats, weather, stocks)
  • alert — breaking/urgent notification
  • featured-comment — highlighted audience comment
  • score-bug — sports score compact display
  • lower-third — TV-standard name/title bar
  • ticker-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:

  1. exec: calls AFS exec on the specified path; agent responds with aup_patch.
  2. 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.
  3. 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)

EventDescription
clickFired by action buttons
changeFired by input fields; data: { value }
sendFired by chat input
sortFired by table header click
selectFired by table row click; overlay choice submit
confirmFired by overlay confirm button
cancelFired by overlay cancel button
dismissFired by overlay toast dismiss
completeFired by deck when reaching the last slide without loop
loadFired by frame when iframe finishes loading
errorFired by frame when iframe fails to load
messageFired by frame when bridge receives postMessage from iframe

智能体工作流

How an agent creates and manages UI:

  1. Read /primitives to discover available components
  2. Read /primitives/:name for detailed props and examples
  3. Read /themes to choose a visual theme
  4. Read /examples for complete working documents
  5. Build an AUP node tree (JSON) with unique IDs
  6. Call aup_render action with { root, fullPage: true, style: "midnight", chrome: true, locale: "en" } — chrome shows lang/theme/mode toolbar
  7. Handle events: when user interacts, event triggers your AFS exec path
  8. 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

  1. Write a complete HTML page to AFS: write('/ui/web/pages/mypage', { content: '<html>...</html>', format: 'html' })
  2. Reference it in the AUP tree: { type: 'frame', props: { src: '/pages/mypage', bridge: true } }
  3. The surface resolves /pages/mypage to an HTTP URL with session tokens
  4. 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.

MethodDescription
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.

PrimitiveDegradation Chain
globemap → media → text
charttable → text
mapmedia → text
editorinput → text
canvasmedia → text
rtcunsupported
calendartable → text
timetext
overlaytext

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.