Research for the ETS27 site redesign (Energy Thought Summit 2027, Austin, Texas, theme "Free Your Mind"). Prepared 2026-10-13 for Zpryme.
Method. Every statement below about a product, protocol or model was checked against a primary source on 2026-10-13: official documentation, specifications, repositories, release notes, and the npm and GitHub registries. The sources table at the end lists each URL and what it confirmed. Where a primary source could not confirm a claim, the text says so. Version numbers and star counts are readings from that date.
Scope. This document covers the generative UI landscape and the mechanism ETS27 should use. Model selection, prompts, cost and server code for Claude belong to research/claude-wiring.md; the Claude section here stays at the level of mechanism.
Related documents. brief/ETS27-site-brief.md (the idea and the plan), brief/design-direction.md (how components look and arrive), research/claude-wiring.md (models and wiring).
@ag-ui/core 1.0.0, published 2026-09-17). MCP Apps has a stable spec (2026-01-26) and SDK 2.0. The Vercel AI SDK is on major version 7. Thesys renamed C1 to OpenUI Cloud and Crayon to OpenUI. OpenAI's Apps SDK documentation now lives under "ChatGPT Plugins" and is built on MCP Apps.output_config.format) or per component (strict: true tools) guarantees schema-valid output. One finding shapes the design: on Claude Opus 5.5, Fable 5.1 and (for longer notes) Sonnet 5.5, text the model writes before or between tool calls comes back as progress-update thinking blocks, which are empty by default. The robust adapter is therefore one structured answer, { say, sources, ui[] }, which streams the narration as text and completes each component as a JSON object. That is the shape the site brief already sketches.CopilotKit's generative UI guide sorts the field into three patterns, from most control to most freedom. The vocabulary is used throughout this document.
| Pattern | What the model emits | Who builds the pixels | Examples |
|---|---|---|---|
| Controlled (component per tool) | A tool call with typed arguments | The application's prebuilt component for that tool | Vercel AI SDK tool parts, CopilotKit useFrontendTool, assistant-ui tool UIs |
| Declarative (component tree) | A description of a layout made from catalog components, as JSON or a compact language | A trusted renderer that maps catalog names to native components | A2UI, json-render, assistant-ui generative UI, OpenUI Lang, OpenAI Intelligent UI (internal) |
| Open-ended (app in a frame) | A complete HTML application | The server's own code, run in a sandboxed iframe | MCP Apps, ChatGPT plugins built on it |
ETS27 sits deliberately between the first two: each turn is a short, flat list of catalog components chosen by the model, rendered by components the site owns.
| What it is | An open protocol for agent-generated interfaces that "render natively across web, mobile, and desktop—without executing arbitrary code". |
| Who ships it | Google, with contributions from CopilotKit and the community. Apache-2.0. The repository moved from github.com/google/A2UI to github.com/a2ui-project/a2ui (the old address returns a 301). About 16.7k GitHub stars. |
| Maturity | The README says "Early stage public preview". a2ui.org lists v0.9.1 as the current production release, v1.0 as a release candidate ("previously v0.10 in draft", no date given for final) and v0.8 as legacy. The latest renderer packages on npm, last published 2026-10-09, are @a2ui/react and @a2ui/lit 0.13.0 and @a2ui/angular 0.12.0. |
| How the agent expresses UI | A stream of JSON messages, each carrying "version" and exactly one envelope key: createSurface (surfaceId, catalogId, optional theme and sendDataModel), updateComponents (a flat list of components), updateDataModel (a JSON Pointer path and a value) and deleteSurface. Components form an adjacency list, { "id", "component", "children" or "child", ...props }, with one component whose id is root. Property values are literals, data bindings such as { "path": "/user/name" }, or function calls. A catalog is a symbolic catalogId plus component and function schemas. The basic catalog has 18 components (Text, Image, Icon, Video, AudioPlayer, Row, Column, List, Card, Tabs, Modal, Divider, Button, TextField, CheckBox, ChoicePicker, Slider, DateTimeInput) and validation and formatting functions. Clients advertise supportedCatalogIds and may send inlineCatalogs. User interaction returns an action message (name, surfaceId, sourceComponentId, timestamp, context); validation failures return an error message written for the model to read. |
| Streaming | JSONL, one complete message per line. Rendering can begin once root exists; other components buffer and render progressively. |
| Design stance | v0.9 is "prompt-first": "While v0.8 was optimized for LLMs that support structured output, v0.9 is designed to be embedded directly within a model's prompt", with a generate, validate and correct loop. The repository also carries an experimental plain-text "Express" grammar that compiles to the A2UI tree. v1.0 adds typed function calls in both directions, whole-surface creation in one message, branding deferred to the host's theme, and stricter identifier rules. |
| Renderers | Google-maintained React, Lit (framework-independent web components), Angular and Flutter (GenUI SDK) are stable for v0.8 and v0.9.1; Jetpack Compose is alpha; SwiftUI is planned. Community renderers include json-render and React Native ports. The repository README's roadmap still lists React as planned; the renderers page and npm show it shipped. |
| Backend neutrality | High. Bindings are described for A2A, AG-UI, MCP, SSE with JSON-RPC, WebSockets and REST, and any model can produce it. |
| Fit for ETS27 | A good compatibility target and a poor launch contract. The basic catalog is generic layout (Row, Column, Text), which moves layout and brand decisions into the model. Its JSON Schemas use external $ref, allOf and dynamic bindings that Claude's grammar-constrained decoding cannot compile as written (section 3.10). And v1.0 is still moving. Borrow its shapes (flat list, id plus component, a symbolic catalog id, surfaces) and keep an export path. |
| What it is | "An open, lightweight, event-based protocol that standardizes how AI agents connect to user-facing applications." Its documentation is explicit that it is not a UI format: "AG-UI is not a generative UI specification — it's a User Interaction protocol that provides the bi-directional runtime connection between the agent and the application." |
| Who ships it | Born from CopilotKit's partnership with LangChain and CrewAI; maintained at github.com/ag-ui-protocol/ag-ui (MIT, about 16.5k stars, date-tagged releases several times a week). |
| Maturity | A 1.0 specification in normative BCP 14 language, with the JSON Schema authoritative for structure. @ag-ui/core 1.0.0 was published 2026-09-17 and 1.0.2 on 2026-10-05. A draft specification continues past 1.0. |
| Event model | Eight families. Lifecycle: RUN_STARTED, RUN_FINISHED (with an optional outcome, including interrupt and cancelled), RUN_ERROR, STEP_STARTED and STEP_FINISHED. Text: TEXT_MESSAGE_START, TEXT_MESSAGE_CONTENT, TEXT_MESSAGE_END, plus a chunked form. Tool calls: TOOL_CALL_START, TOOL_CALL_ARGS, TOOL_CALL_END, TOOL_CALL_RESULT. State: STATE_SNAPSHOT, STATE_DELTA (RFC 6902 JSON Patch) and MESSAGES_SNAPSHOT. Activity: ACTIVITY_SNAPSHOT and ACTIVITY_DELTA, "structured progress a UI renders as its own widget, kept in the message sequence". Then reasoning, subagents, and the RAW and CUSTOM escape hatches. |
| Bidirectional state | Shared state persists across runs on a thread and returns in the next run's input. The application can offer its own "frontend tools", whose results come back in the next run input, and interrupts pause a run for human input. The run input carries threadId, runId, messages, tools, context, state and forwardedProps. |
| How generative UI rides on it | Three ways: frontend tools rendered as components (CopilotKit's useFrontendTool with a render function per call status); declarative specs carried as activity messages (CopilotKit renders A2UI through renderActivityMessages); and MCP Apps. A draft extension proposes a generateUserInterface tool that hands UI generation to a second model. |
| Transport | HTTP POST of the run input, answered by server-sent events (one protocol event per data payload, no resumption), or HTTP with length-prefixed protobuf frames. |
| SDKs and integrations | TypeScript (@ag-ui/core, client, encoder, proto) and Python first-party, .NET documented, community SDKs for Kotlin, Go, Dart, Java, Rust, Ruby and C++. Listed integrations include Microsoft Agent Framework, Google ADK, AWS Strands and Bedrock AgentCore, Mastra, Pydantic AI, LlamaIndex, LangChain and CrewAI; the Claude Agent SDK and Claude Managed Agents appear as community integrations. |
| Safety stance | From the 1.0 specification: "Model output is untrusted input... Applications MUST validate what they act on and MUST NOT render streamed content as executable markup." |
| Fit for ETS27 | Adopt the event names and the SSE binding as the wire format now: it costs nothing and keeps CopilotKit, assistant-ui and OpenUI clients within reach. Defer the runtime unless the front end becomes a React application shell. |
| What it is | The first official MCP extension (io.modelcontextprotocol/ui, SEP-1865): servers deliver interactive HTML interfaces. A tool points at a UI resource through _meta.ui.resourceUri (the ui:// scheme, MIME type text/html;profile=mcp-app); the host renders it in a sandboxed iframe; the view talks to the host with JSON-RPC 2.0 over postMessage (ui/initialize, tool-input and tool-result notifications, tools/call, ui/message). Servers declare allowed origins in _meta.ui.csp and hosts apply a restrictive default otherwise; web hosts must load views through a separate-origin sandbox proxy. Tool visibility ("model", "app") keeps app-only tools out of the model's tool list. |
| Who ships it | Proposed 2025-11-21 by MCP core maintainers at Anthropic and OpenAI with the MCP-UI creators; the specification names MCP-UI and OpenAI's Apps SDK as its two inspirations. Status "Stable (2026-01-26)". @modelcontextprotocol/ext-apps 2.0.0 (2026-09-08) moved to the MCP TypeScript SDK 2.0 packages with the wire protocol unchanged; 2.0.3 is current. |
| Which clients render it today | The ext-apps README lists ChatGPT, Claude, VS Code, Goose, Postman, MCPJam, mcp-use and Alpic Playground. Claude does: Anthropic's "Interactive connectors and MCP Apps" post (2026-01-26) now states availability on Claude "mobile, web and desktop for Free, Pro, Max, Team, Enterprise plans" and on Claude Cowork. ChatGPT does: OpenAI's plugin documentation says "ChatGPT implements the open MCP Apps standard for UI returned by an MCP server", keeping window.openai as compatibility aliases and for ChatGPT-only extras (checkout, files, modals, widget state). At launch, VS Code support was in VS Code Insiders. |
| Streaming | Tool inputs and results reach the view as notifications. The view is a running app, so there is no token-by-token UI streaming of model output. |
| Renderers | Any web stack inside the iframe; the SDK includes React helpers. |
| Backend neutrality | Any MCP server, any compliant host. |
| Fit for ETS27 | The wrong tool for the website itself: it assumes a chat host, and HTML in a frame is the open-ended end of the spectrum. The right tool for a later distribution channel: an ETS27 MCP server whose agenda and speaker views render inside ChatGPT and Claude. json-render's @json-render/mcp package shows the path for reusing a catalog renderer as a ui:// resource. |
| What it is said to be | AG-UI's generative UI page lists it with origin "OpenAI": "An open standardization of OpenAI's internal declarative Generative UI schema." |
| What could be verified | No OpenAI specification, repository or documentation under that name was found. GitHub code search returns about 480 matches: the CopilotKit and ag-ui-protocol repositories plus third-party projects, none in an OpenAI organization repository. CopilotKit's Open-JSON-UI documentation URLs now redirect to its A2UI page, and its example payload is labeled "illustrative". OpenAI's own published declarative format is ChatKit widgets: a WidgetRoot container such as Card or ListView holding WidgetNode components, designed in Widget Builder, with actions handled by your backend. |
| Maturity | A label, not a maintained standard. |
| Fit for ETS27 | None as a dependency. |
| What it is | Open-source "components, runtimes, and primitives for building AI chat interfaces in React, Vue, React Native, and the terminal". MIT, about 12.5k stars, @assistant-ui/react 0.15.28 (pre-1.0, frequent releases). |
| How the agent expresses UI | Two routes. Tool UIs render tool calls as your components. The new @assistant-ui/generative-ui package (0.0.3, published 2026-10-12) adds a JSONGenerativeUI toolkit with a present tool: the model emits objects whose _type names a library component, with children for nesting and the other keys as props. An A2UI converter consumes A2UI surfaces over AG-UI. |
| Streaming | Components can opt into streamProperties: true and receive partial props with an injected $status of "streaming" or "done". |
| Renderers and runtimes | React first, also Vue, React Native and the terminal; adapters for the Vercel AI SDK, LangGraph, LangChain, Google ADK, AG-UI and A2A. |
| Fit for ETS27 | A reasonable chat shell for the standard route's dock if the front end is React. Its nested JSON format is recursive, which Claude's grammar cannot constrain, so pair it with its tool-UI route or with our flat contract. |
| Status of the names in the brief | Thesys's homepage presents "OpenUI Cloud — Formerly C1 API"; docs.thesys.dev redirects to openui.com/docs; github.com/thesysdev/crayon redirects to github.com/thesysdev/openui. Note a naming collision: OpenUI calls itself "the open standard for Intelligent UI" and is unrelated to OpenAI's feature of the same name. |
| What it is | OpenUI is a full-stack generative UI framework (MIT, about 10.8k stars) built on OpenUI Lang, "a compact, line-oriented language" in which the model writes statements such as root = Stack([chart]), constrained to a component library defined with Zod. The React, Vue, Svelte and Angular runtimes render each line as it arrives and drop invalid portions. @openuidev/react-lang and @openuidev/lang-core 1.0.0 were published 2026-10-12; @openuidev/a2ui adds A2UI v1.0 support. |
| Claims | "Up to 67% fewer tokens than JSON", and vendor benchmarks of 52.8% fewer tokens than json-render and 51.7% fewer than Thesys C1 JSON. These are vendor numbers. |
| Hosted pieces | OpenUI Gateway (OpenAI-compatible endpoints, automatic correction of invalid OpenUI Lang while it streams, bring-your-own OpenAI, Anthropic or Google key) and OpenUI Cloud. |
| Fit for ETS27 | Worth watching for token efficiency, not for launch. A custom language cannot be grammar-constrained by Claude's structured outputs, so validity rests on parse-and-drop or a hosted correction step in the request path, and the 1.0 runtimes are one day old. |
| What it is | "A Generative UI framework: AI generates interfaces from natural language prompts, constrained to components you define." Apache-2.0, about 18.6k stars, @json-render/core 0.21.0 (2026-09-18), pre-1.0. |
| How the agent expresses UI | A catalog built with defineCatalog (Zod props plus named actions) and a flat spec: { "root": "card-1", "elements": { "card-1": { "type": "Card", "props": {...}, "children": [...] } } }. Dynamic expressions ($state, $cond, $template), visibility rules, a built-in setState action and state watchers. catalog.prompt() generates the system prompt from the catalog. |
| Streaming | SpecStream: JSONL in which each line is an RFC 6902 JSON Patch operation against the spec, compiled incrementally with createSpecStreamCompiler. |
| Renderers | React, Vue, Svelte, Solid, React Native, Next.js, TanStack Start, React Three Fiber, Remotion, React PDF, React Email, Ink and image output; 36 shadcn/ui components; integrations for the AI SDK, A2UI, AG-UI, Adaptive Cards, OpenAPI and MCP Apps. |
| Fit for ETS27 | The closest open-source analogue to what ETS27 needs and a strong renderer candidate if the front end is React. Its generation path is prompt plus parse rather than constrained decoding; with Claude we would feed it from a constrained answer instead. |
| Current major | ai 7.x (7.0.0 published 2026-06-25; 7.0.138 on 2026-10-12). Apache-2.0. |
| How generative UI is done now | Tools plus typed message parts. Each tool call appears in message.parts as a tool-<toolName> part whose state moves through input-streaming, input-available, output-available or output-error (plus approval-requested, approval-responded and output-denied when approval is involved). The application renders a component per state: a skeleton while input streams, the real component when input or output is available. For whole-answer schemas, streamText with output: Output.object(...) streams partial objects, and Output.array(...) exposes an elementStream that yields each element as soon as it is complete. |
streamUI and RSC |
"AI SDK RSC is currently experimental. We recommend using AI SDK UI for production." streamUI still ships in @ai-sdk/rsc 3.x but is not the recommended path. |
| Backend neutrality | Provider-agnostic, including an Anthropic provider. |
| Fit for ETS27 | High if the front end is Next.js or React: both of its patterns (tool parts, and streamed structured output with per-element completion) match the mechanism recommended below. |
| What it is | Announced 2026-10-07 in "GPT-6 and Intelligent UI for everyone": "We've trained GPT‑6 to compose responses using text, visuals and interactive elements, choosing how they fit together based on your question. With Intelligent UI, responses can include graphics, tappable buttons, forms, charts, and interactive experiences you can use directly in your conversation." The post's examples include an explorable bicycle diagram, a roast plan with a guest-count stepper and a shopping list, and a bill splitter built on request. |
| How it works | "We built a library of native, streamable components, along with a compiler that processes the interface as the model generates it... The compiler allows the interface to appear progressively as the model generates it, without waiting for the entire response to be complete." The model was trained and evaluated on its interface decisions, including "when a simple text response is enough". GPT-6 in ChatGPT also interleaves thinking with answering. |
| Availability | The Chat tab in ChatGPT: Plus, Pro, Business and Enterprise (GPT-6 Sol) from 2026-10-07; Free and Go (GPT-6 Luna) from 2026-10-08. "The models powering Work and Codex are not changing as part of this release." |
| Developer access | None found as of 2026-10-13. The announcement does not mention an API, and the API changelog entry for 2026-10-07 only updates the chat-latest snapshot. OpenAI's developer route to UI inside ChatGPT is ChatGPT Plugins (developers.openai.com/apps-sdk now redirects to /plugins), whose UI is MCP Apps iframes; for embedding chat in your own product it is ChatKit with its widgets. Neither is Intelligent UI. |
| What it means for ETS27 | Inspiration only. It validates the architecture (a catalog of native components, a progressive compile while the model streams, a model that judges when UI helps), but it cannot be called from a website. |
This section stays at the level of mechanism; research/claude-wiring.md covers model choice, prompts and server code.
output_config.format with a JSON schema) constrain the whole response; strict tool use (strict: true on a tool) "guarantees Claude's tool inputs match your JSON Schema by constraining the model's token sampling to schema-valid outputs". Both compile the schema into a grammar and are generally available on the Claude API. Grammars apply to the model's direct output, not to its thinking, so the model can still think before answering.{ "say": "...", "sources": [...], "ui": [...] }. It arrives as ordinary text deltas, so an incremental parser can forward the say string to the browser character by character and hand over each element of ui the moment its closing brace arrives. Property order follows the schema, with required properties first, so say streams before the components. Because the answer contains no tool calls, the progress-update behavior below does not apply.input_schema is its props. Text streams as text_delta; each component arrives as a tool_use block whose content_block_start names it at once and whose content_block_stop marks a complete object. The tools execute nothing: the client renders them, and the server acknowledges them with tool_result blocks in the next user message. By default the API buffers and validates each tool parameter before streaming it; eager_input_streaming: true streams fragments immediately but "does not buffer or validate", so the client must validate.thinking blocks, "at most one before each tool call", empty at the default display; Anthropic's prompting guide treats "a one-line statement of intent before the first tool call" as one of these updates. On Sonnet 5.5, notes "longer than a sentence or two" are converted the same way and shorter remarks stay text. display: "updates" (beta) returns only a summary of each note, not its verbatim text. Narration written in front of component tool calls can therefore disappear on these models. Claude Haiku 5.5 is not on the list of models that emit progress updates. Adapter A sidesteps the question on every model.minItems of 0 or 1, and additionalProperties other than false. Supported: enum, const, anyOf and allOf (with limits) and internal $ref. Per request: at most 20 strict tools, 24 optional parameters and 16 union-typed parameters across all strict schemas, plus internal limits on compiled grammar size ("Schema is too complex for compilation"). Grammars compile on first use and are cached for 24 hours from last use; changing a schema or the tool set invalidates them, and changing output_config.format invalidates the prompt cache for that conversation.tool_choice (any or tool) returns a 400 on Claude Opus 5.5, Sonnet 5.5 and Fable 5.1; the documented replacement is auto with strict tools and prompting, or structured outputs.claude-haiku-5-5) was released 2026-10-07, is described as "for high-volume, latency-sensitive tasks" and is the fastest model in the current lineup; it is worth a latency trial in the wiring work. Availability of structured outputs differs on some cloud platforms, so use the Claude API directly.| Ships | Status on 2026-10-13 | How the agent expresses UI | Streaming | Renderers or hosts | Backend neutrality | Fit for ETS27 | |
|---|---|---|---|---|---|---|---|
| A2UI | Google and community, Apache-2.0 | Public preview; v0.9.1 production, v1.0 release candidate | JSONL messages; flat adjacency list of catalog components; data model with JSON Pointer bindings | JSONL; progressive once root exists |
Lit, React, Angular, Flutter stable; Compose alpha; SwiftUI planned | Any transport, any model | Medium: compatibility target, not the launch contract |
| AG-UI | CopilotKit and community, MIT | 1.0 specification; @ag-ui/core 1.0.x |
Not a UI format; carries text, tool calls, state and activity events; UI rides as frontend tools, activity messages or MCP Apps | SSE or protobuf event stream | CopilotKit, assistant-ui, OpenUI, or your own client | Framework-agnostic; TypeScript and Python SDKs plus community | Medium to high: use its event shapes now, its runtime later if needed |
| MCP Apps | MCP maintainers (Anthropic, OpenAI) and MCP-UI, MIT SDK | Stable spec 2026-01-26; SDK 2.0.3 | Tool metadata points to a ui:// HTML resource in a sandboxed iframe; JSON-RPC over postMessage |
Tool input and result notifications; no token-level UI streaming | ChatGPT; Claude web, desktop and mobile; VS Code; Goose; Postman; others | Any MCP server; host must implement | Low for the site; high later as a ChatGPT and Claude channel |
| Open-JSON-UI | Named in CopilotKit and AG-UI docs as OpenAI's | No published specification found; CopilotKit's page redirects to A2UI | Illustrative JSON components | Not specified | CopilotKit (illustrative) | Not applicable | None |
| assistant-ui | assistant-ui, MIT | Core 0.15.x; generative UI 0.0.3 | Tool UIs; or a present tool with _type and nested children |
Partial props with $status |
React, Vue, React Native, terminal | Adapters for AI SDK, LangGraph, AG-UI, A2A, ADK | Medium: chat shell for the dock if React |
| OpenUI (ex-Crayon) and OpenUI Cloud (ex-C1) | Thesys; MIT framework plus hosted cloud | Lang runtimes 1.0.0 (2026-10-12) | Line-oriented language id = Component(...) against a Zod library |
Line by line; invalid lines dropped; optional hosted correction | React, Vue, Svelte, Angular | Any model; hosted gateway optional | Low to medium: watch; not grammar-constrainable on Claude |
| json-render | Vercel Labs, Apache-2.0 | 0.21.0, pre-1.0 | Flat { root, elements } spec from a Zod catalog |
JSONL of RFC 6902 patches (SpecStream) | React, Vue, Svelte, Solid, React Native, Next.js and more | Any model, prompt-driven | Medium to high as the renderer if React |
| Vercel AI SDK 7 | Vercel, Apache-2.0 | ai 7.x since 2026-06-25 |
Typed tool-<name> parts mapped to your components; or streamed structured output |
input-streaming tool parts; partial objects; per-element completion |
React hooks and other UI bindings | Provider-agnostic, including Anthropic | High if Next.js or React |
| OpenAI Intelligent UI | OpenAI, ChatGPT feature | Shipped 2026-10-07 and 2026-10-08 | Model trained on an internal library of native, streamable components | A compiler renders progressively | ChatGPT only | None; no API | Inspiration only |
| Claude structured outputs and strict tools | Anthropic | Generally available | A JSON schema for the whole answer, or per tool | Text deltas; tool input deltas | Yours | Claude API; some cloud platforms differ | The recommended generation mechanism |
In ChatGPT, the owner saw GPT-6 answer with live interface instead of paragraphs: a bicycle diagram whose systems can be tapped, a roast plan with a guest-count stepper, a bill splitter built on request. OpenAI did this by training the model on its own library of native, streamable components and adding a compiler that renders the interface while the model is still generating. It lives inside ChatGPT, it can invent small tools on the fly, and no developer can call it. ETS27 will reproduce the feeling, not the machinery. A Claude concierge composes each answer from ten ETS27 components (speakers, agenda, sessions, venue, tickets, partners and a few more), our incremental parser plays the role of OpenAI's compiler, every fact inside a component comes from the summit's own data, and the visual language is the ETS27 realm (white, open space, mono notation) rather than a chat skin. The model decides what to show and in what order; it never writes interface code, never invents a speaker or a price, and cannot build arbitrary mini-apps the way ChatGPT can. That narrower scope is the point: a public event site needs answers that are fast, on brand and correct, and every generated view has an ordinary page behind it.
A constrained declarative UI built from a fixed catalog.
brief/design-direction.md section 4.5 describes how each one arrives).{ say, sources, ui }: a short narration, the pages its facts came from, and zero to four component blocks in display order. Blocks form a flat list with no nesting, which keeps the schema non-recursive and the renderer trivial.Ten components, reconciled with the eleven in the design direction and the first cut in the site brief. Every prop is required, ids are enums generated from the site's data at build time, and length limits that grammars cannot express are enforced by validation and truncation.
| Component | Props | What it renders (content always from site data) | Maps to |
|---|---|---|---|
SpeakerCard |
speakerId: speaker slug. show: "bio", "sessions" or "both". |
Portrait, name, title and organization, bio, session chips, link to the lineup page. | SpeakerCard; speaker_card |
SpeakerGrid |
heading: string, 60 characters or fewer after validation. speakerIds: array of speaker slugs, at least one. groupBy: "none", "day", "track" or "organization". |
Compact speaker cards with the mono answer bar. | SpeakerGrid; speaker_grid |
AgendaDay |
day: day id. trackIds: array of track ids, empty for all. speakerIds: array of speaker slugs, empty for no filter. heading: string. |
A time rail of the matching sessions with rooms and speakers. Several days means several blocks. | AgendaDay; agenda_day and session_list |
SessionCard |
sessionId: session id. showRelated: boolean. |
Time, title, speakers, room, add to calendar, related sessions on the same track. | SessionCard; session_card |
VenueCard |
sections: array of "overview", "map", "hotel", "getting-there", "parking", "accessibility", "dining"; at least one. |
Venue and hotel facts, a static map tile, the hotel block link. | VenueCard; venue_card |
TicketTiers |
tierIds: array of ticket tier ids, empty for all. highlightTierId: a tier id or "none". |
Tier columns, prices and deadlines, one primary register action to the Eventbrite URL from data. | TicketTiers; ticket_cta |
PartnerWall |
tier: "all" or a partner tier id. heading: string. |
Logo grid with links from data. | PartnerWall; sponsor_wall |
Faq |
topicIds: array of FAQ and policy entry ids, at least one. |
An accordion of site-authored answers. Never model-written prose. | FAQ / Policy; replaces text_block |
ContactCard |
topic: "general", "speaking", "partnerships", "press" or "registration". |
The site's own contact form, routed by topic. The model never sees what the visitor types. | ContactCard |
Choices |
prompts: array of strings, at most four of 48 characters each after validation. links: array of page ids ("lineup", "agenda", "venue", "partners", "gallery", "about", "contact", "register"). |
Follow-up chips that send their text as the next message, and mono link-outs to standard pages. | choices; LinkOut and link_out |
The envelope carries two more fields. say is the narration, rendered with the design direction's TextAnswer styling as plain text. sources is an array of page ids rendered as the mono source line the design direction requires for factual answers, so a source never has to be a model-written URL.
Not in the catalog on purpose: generic layout primitives (Row, Column, Text), free-text cards, images by URL, and forms other than the site's contact form. Candidates for a second phase: Gallery (past-summit photos by collection id), MyAgenda (a personal schedule the visitor confirms and saves), and Countdown.
Budget against Claude's limits (adapter A): one union-typed parameter (the ui item union; limit 16), no optional parameters (limit 24), no recursion. Adapter B would need ten strict tools (limit 20), also with no optional parameters. Large id enums still count toward the internal grammar size limit, which needs a test with the real speaker and session counts (section 7).
Schema sketch for adapter A, showing two of the ten components:
{
"type": "object",
"properties": {
"say": { "type": "string", "description": "One to three conversational sentences. Facts belong in components." },
"sources": { "type": "array", "items": { "$ref": "#/$defs/PageId" } },
"ui": {
"type": "array",
"description": "Zero to four ETS27 components in display order.",
"items": { "anyOf": [ { "$ref": "#/$defs/AgendaDay" }, { "$ref": "#/$defs/SpeakerGrid" } ] }
}
},
"required": ["say", "sources", "ui"],
"additionalProperties": false,
"$defs": {
"PageId": { "type": "string", "enum": ["lineup", "agenda", "venue", "partners", "gallery", "about", "contact", "register"] },
"SpeakerId": { "type": "string", "enum": ["<speaker slugs from the site snapshot>"] },
"AgendaDay": {
"type": "object",
"properties": {
"component": { "const": "AgendaDay" },
"day": { "type": "string", "enum": ["<day ids from the site snapshot>"] },
"trackIds": { "type": "array", "items": { "type": "string", "enum": ["<track ids>"] } },
"speakerIds": { "type": "array", "items": { "$ref": "#/$defs/SpeakerId" } },
"heading": { "type": "string" }
},
"required": ["component", "day", "trackIds", "speakerIds", "heading"],
"additionalProperties": false
},
"SpeakerGrid": {
"type": "object",
"properties": {
"component": { "const": "SpeakerGrid" },
"heading": { "type": "string" },
"speakerIds": { "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/SpeakerId" } },
"groupBy": { "type": "string", "enum": ["none", "day", "track", "organization"] }
},
"required": ["component", "heading", "speakerIds", "groupBy"],
"additionalProperties": false
}
}
}
component comes first in every block, so the parser knows which skeleton to show before the props arrive. The server adds a block id; the model never generates ids for blocks.
| Option | Pros | Cons |
|---|---|---|
| Adopt A2UI (v0.9.1 now, v1.0 later) as the contract | Interoperates with Google's renderers, CopilotKit, OpenUI and json-render; a likely standard. | Public preview, with v1.0 still a release candidate that reworks functions, theming and identifiers; its schemas (external $ref, allOf, dynamic bindings) do not compile into Claude's grammar as written, so validity falls back to prompt, validate and retry; generic layout components hand layout and brand to the model; we would need a custom catalog anyway. |
| Adopt the AG-UI runtime with a client framework (CopilotKit, assistant-ui) | A normative 1.0 protocol, mature SDKs, ready chat UI, room to grow into shared state and human-in-the-loop flows. | A dependency and runtime for features ETS27 does not need (shared state, interrupts, subagents); the frameworks assume a React application shell; the visual layer still has to be rebuilt in the ETS27 style. |
| A small custom contract with AG-UI and A2UI shapes (recommended) | Tiny, auditable and brand-specific; fits Claude's grammar limits; no runtime dependency; renders in plain web components or React; the shapes keep every migration below open. | We maintain it; no off-the-shelf renderer until we add an adapter. |
The contract:
{ "id", "component", ...props }, the A2UI component object without children. One assistant turn is one surface.RUN_STARTED; TEXT_MESSAGE_START, TEXT_MESSAGE_CONTENT and TEXT_MESSAGE_END for say; ACTIVITY_SNAPSHOT for each block (activity messages are AG-UI's slot for structured widgets the UI renders, and CopilotKit already carries A2UI surfaces this way); RUN_FINISHED or RUN_ERROR.threadId, runId, the new user message, and context (the current page and the route). The conversation itself is held server-side.Migration path:
root Column under a published symbolic catalog id such as https://energythoughtsummit.com/a2ui/ets27/v1. Any A2UI renderer could then display ETS27 answers.ui:// views reuse the same renderer bundle, so attendees can ask about ETS27 where they already chat.Text streams token by token; a component appears as a skeleton when it starts and materializes when its JSON object completes and passes validation.
| Claude stream (adapter A) | Server: incremental parser and validator | Wire event (AG-UI 1.0 subset) | Browser |
|---|---|---|---|
message_start |
Open the run | RUN_STARTED |
Composer shows a breathing dot |
thinking blocks |
Ignore; never shown | None | Nothing, or the design direction's "one moment" line after 2 seconds |
text_delta inside "say" |
Unescape and forward characters as they arrive | TEXT_MESSAGE_START, then TEXT_MESSAGE_CONTENT per chunk |
The caption types out |
End of the "say" string |
Close the narration | TEXT_MESSAGE_END |
|
"component": "AgendaDay" at the start of a ui element |
Open a block and assign its id | ACTIVITY_SNAPSHOT with { "status": "pending" } |
The AgendaDay skeleton (outline draw) appears |
| The closing brace of that element | Validate against the catalog schema, resolve ids against the snapshot, drop unknown ids | ACTIVITY_SNAPSHOT replacing the content with the complete block |
The component materializes |
message_delta with stop_reason end_turn |
Finalize and append the turn to the thread | RUN_FINISHED |
Composer re-enables; chips are live |
stop_reason refusal or max_tokens |
Keep what validated; add a fallback Choices block |
RUN_FINISHED, or RUN_ERROR if nothing usable arrived |
The design direction's "no result" state |
Example of what the browser receives:
data: {"type":"RUN_STARTED","threadId":"t-42","runId":"r-7"}
data: {"type":"TEXT_MESSAGE_START","messageId":"m-7","role":"assistant"}
data: {"type":"TEXT_MESSAGE_CONTENT","messageId":"m-7","delta":"Day two has three sessions on grid security."}
data: {"type":"TEXT_MESSAGE_END","messageId":"m-7"}
data: {"type":"ACTIVITY_SNAPSHOT","messageId":"b-7-1","activityType":"ets27.AgendaDay","content":{"status":"pending"}}
data: {"type":"ACTIVITY_SNAPSHOT","messageId":"b-7-1","activityType":"ets27.AgendaDay","content":{"status":"ready","block":{"id":"b-7-1","component":"AgendaDay","day":"day-2","trackIds":["security"],"speakerIds":[],"heading":"Grid security, day two"}}}
data: {"type":"RUN_FINISHED","threadId":"t-42","runId":"r-7"}
Rules:
content_block_start of a tool_use block opens the skeleton and content_block_stop completes it. Keep buffered tool streaming (the default) unless a list should fill progressively.Choices, so no second model call is needed to add trailing text.say and headings render as plain text; links, images and HTML in model text are stripped.max_tokens, and graceful handling of the refusal and max_tokens stop reasons.threadId with a short lifetime. This blocks forged history, keeps the cached prefix stable, and matches Claude's requirement on current models that conversations replayed with thinking blocks stay append-only.aria-live="polite" with aria-busy="true" while it streams, so screen readers hear the finished sentence rather than every fragment. Each materialized block is announced by a short summary, such as "Showing 3 sessions for day two". Focus never moves on its own; a "skip to latest answer" control is always available.prefers-reduced-motion. The white realm needs contrast checks (4.5:1 for body text); light gray on white is the usual failure.usage.cache_read_input_tokens. Changing output_config.format or the tool set invalidates the cache for a conversation, so version the schema per deploy.Choices carry the follow-ups, so a typical turn needs no tool round trip.{ say, sources, ui } documents with no model call; the model handles the long tail.Both routes call the same endpoint with the same catalog and the same snapshot. The run input's context carries the route and the current page. The interactive route renders blocks into the open realm and keeps earlier blocks as receding history. The standard route's dock renders the same blocks into page slots, scrolls to the related section, and prefers Choices links into the page structure. Only the renderer's placement logic differs.
content.rendered; the snapshot needs stable slugs, days, tracks, rooms and ticket tiers (see the data-model research).All accessed 2026-10-13.
| Source | What it confirmed |
|---|---|
| https://a2ui.org/ | A2UI purpose, version list (v0.9.1 production, v1.0 release candidate, v0.8 legacy) |
| https://a2ui.org/specification/v0.9.1-a2ui/ | Message types, adjacency list, JSON Pointer bindings, catalogs, client capabilities, JSONL, prompt-first stance |
| https://a2ui.org/specification/v1.0-a2ui/ | Release candidate status and changes from v0.9 |
| https://a2ui.org/reference/renderers/ | Renderer list and status |
| https://github.com/a2ui-project/a2ui (redirect from https://github.com/google/A2UI) | "Early stage public preview", Apache-2.0, transports |
| https://raw.githubusercontent.com/a2ui-project/a2ui/main/specification/v0_9_1/catalogs/basic/catalog.json | The 18 basic catalog components and their schema style |
| https://github.com/a2ui-project/a2ui/tree/main/specification/inference_formats/express | The experimental Express grammar |
| https://docs.ag-ui.com/introduction | AG-UI purpose, origin, integrations, SDK languages |
| https://docs.ag-ui.com/concepts/events | Event types and fields |
| https://docs.ag-ui.com/concepts/generative-ui-specs | "Not a generative UI specification"; the specs table, including Open-JSON-UI's attributed origin |
| https://docs.ag-ui.com/spec/1.0/changelog | What 1.0 changed (normative spec, outcomes, activity, protobuf binding) |
| https://docs.ag-ui.com/spec/1.0/index | Security and trust principles |
| https://docs.ag-ui.com/spec/1.0/basic/transports/http-sse | The SSE binding |
| https://docs.ag-ui.com/spec/1.0/basic/run-input | Run input fields |
| https://docs.ag-ui.com/spec/1.0/events/activity | Activity snapshot and delta semantics |
| https://docs.ag-ui.com/spec/1.0/events/state | State and messages snapshots |
| https://docs.ag-ui.com/drafts/generative-ui | The draft generateUserInterface extension |
| https://github.com/CopilotKit/generative-ui | The three generative UI types; A2UI rendered through activity messages; the "illustrative" Open-JSON-UI example |
| https://docs.copilotkit.ai/generative-ui-specs/open-json-ui | Redirects to CopilotKit's A2UI page |
| GitHub code search for "Open-JSON-UI" | About 480 matches in CopilotKit, ag-ui-protocol and third-party repositories; none in the openai organization |
| https://github.com/modelcontextprotocol/ext-apps | MCP Apps overview and supported clients |
| https://github.com/modelcontextprotocol/ext-apps/blob/main/specification/2026-01-26/apps.mdx | SEP-1865: ui://, MIME type, _meta.ui, CSP, sandbox proxy, JSON-RPC bridge, visibility |
| https://github.com/modelcontextprotocol/ext-apps/releases | SDK 2.0.0 (2026-09-08) and later patches |
| https://blog.modelcontextprotocol.io/posts/2025-11-21-mcp-apps/ | Authors from Anthropic, OpenAI and MCP-UI |
| https://blog.modelcontextprotocol.io/posts/2026-01-26-mcp-apps/ | Launch clients |
| https://claude.com/blog/interactive-tools-in-claude | Claude support for MCP Apps on mobile, web and desktop, and in Cowork |
| https://developers.openai.com/apps-sdk (redirects to /plugins) and https://developers.openai.com/plugins/build/chatgpt-ui | Apps SDK documented as ChatGPT Plugins; "ChatGPT implements the open MCP Apps standard" |
| https://developers.openai.com/api/docs/guides/chatkit-widgets | ChatKit widget structure |
| https://openai.com/index/gpt-6-for-everyone/ | Intelligent UI: what, how, availability. Direct fetching returned HTTP 403, so the page was retrieved through a Firecrawl-based ingest. |
| https://developers.openai.com/api/docs/changelog | The 2026-10-07 entry only updates chat-latest |
| https://www.assistant-ui.com/docs and https://www.assistant-ui.com/docs/tools/generative-ui | assistant-ui scope, runtimes, the generative UI toolkit |
| https://www.thesys.dev/ | "OpenUI Cloud — Formerly C1 API" |
| https://docs.thesys.dev/ (redirects to https://www.openui.com/docs) | C1 documentation moved to OpenUI |
| https://github.com/thesysdev/openui (redirect from https://github.com/thesysdev/crayon) | OpenUI scope, packages, license, token claims |
| https://www.openui.com/docs/openui-lang and https://www.openui.com/llms.txt | OpenUI Lang design, Gateway, A2UI package, benchmarks page |
| https://github.com/vercel-labs/json-render | Catalog, spec shape, packages, integrations |
| https://json-render.dev/docs/streaming | SpecStream format |
| https://ai-sdk.dev/docs/ai-sdk-ui/generative-user-interfaces | Generative UI through tool parts |
| https://ai-sdk.dev/docs/ai-sdk-ui/chatbot-tool-usage | Tool part states |
| https://ai-sdk.dev/docs/ai-sdk-core/generating-structured-data | Output.object, partialOutputStream, Output.array and elementStream |
| https://ai-sdk.dev/docs/ai-sdk-rsc/overview | RSC is experimental; use AI SDK UI for production |
| https://ai-sdk.dev/docs/migration-guides/migration-guide-7-0 | AI SDK 7 changes |
| https://platform.claude.com/docs/en/build-with-claude/structured-outputs | Schema limits, complexity limits, grammar caching, supported models, prompt-cache invalidation |
| https://platform.claude.com/docs/en/agents-and-tools/tool-use/strict-tool-use | Strict tool guarantees |
| https://platform.claude.com/docs/en/agents-and-tools/tool-use/fine-grained-tool-streaming | eager_input_streaming behavior |
| https://platform.claude.com/docs/en/agents-and-tools/tool-use/define-tools | Forced tool choice restrictions |
| https://platform.claude.com/docs/en/build-with-claude/thinking | Progress updates between tool calls |
| https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/prompting-claude-opus-5-5 | Pre-tool intent lines treated as progress updates; the verbatim-message tool advice |
| https://platform.claude.com/docs/en/models/opus-5-5/migration-guide and https://platform.claude.com/docs/en/models/sonnet-5-5/migration-guide | Text between tool calls returned in thinking blocks |
| https://platform.claude.com/docs/en/about-claude/models/overview and https://platform.claude.com/docs/en/models/haiku-5-5/overview | Current lineup; Claude Haiku 5.5 release date, positioning and pricing |
| https://platform.claude.com/docs/en/models/haiku-5-5/migration-guide | Append-only conversations; platform caveat for structured outputs |
npm registry (npm view) |
Versions and publish dates for ai, @ai-sdk/rsc, @ag-ui/core, @modelcontextprotocol/ext-apps, @a2ui/*, @json-render/core, @assistant-ui/*, @openuidev/*, @thesysai/genui-sdk, @copilotkit/react-core |
| GitHub REST API | Star counts, licenses, redirects, release tags |
Checked again on 2026-10-20 against the same kinds of sources (project sites, repositories, package registries, the vendors' own announcements). Nothing in sections 3 to 6 changes.
a2ui-adk and a2ui-a2a packages on 2026-10-17), plus the closed generative UI inside the Gemini app. OpenAI: Intelligent UI inside ChatGPT (GPT-6, 2026-10-07), still with no developer API; the developer routes remain ChatGPT plugins (MCP Apps iframes) and ChatKit widgets. Anthropic: MCP Apps (the open specification co-authored with OpenAI, stable since 2026-01-26, interactive apps inside Claude's clients) and, on the API, grammar-constrained structured outputs, which is how ETS27 gets a catalog-valid answer from Claude (section 3.10). Vendor-neutral: AG-UI 1.0, json-render, OpenUI and OpenUI Lang, the Vercel AI SDK, assistant-ui.Y Combinator's Summer 2026 request for startups "Dynamic Software Interfaces" (Ankit Gupta) states the same thesis at the scale of all software: before AI every user got the same interface; now "software companies will ship these shared primitives with full intention that users will heavily modify the final interfaces", with each user's coding agent as their own forward-deployed engineer. ETS27 is that idea with the agent on the site's side: the site ships the primitives (the catalog of section 6) and its own mind composes the interface per visitor; the facts stay static, the composition does not. The verbatim text, its provenance (the live YC page has since moved on to the Fall 2026 edition) and the reading for the studio are in design/inspiration/yc-rfs-dynamic-software-interfaces.md.