Architecture¶
Artifacts¶
flowchart BT
chat["ai-elements-chat<br/>model · ChatEvent · ChatBackend · ChatController"]
core["ai-elements-core<br/>protocols · model APIs · agent loop · MCP · skills · auth"] --> chat
ui["ai-elements-ui<br/>Compose elements"] --> chat
genui["ai-elements-genui<br/>A2UI · JSX"] --> ui
apps["ai-elements-mcp-apps"] --> ui
apps --> core
a2a["ai-elements-a2a"] --> core
acp["ai-elements-acp"] --> core
koog["ai-elements-koog"] --> core
harness["harness-*"] --> core
ai-elements-chatis the protocol-independent layer: the chat model (≈ AI SDKUIMessage),ChatEvent,ChatBackendandChatController(≈useChat). No networking, no Compose.ai-elements-coreholds the protocol clients (AI SDK, AG-UI), model APIs, the agent loop, sub-agents, skills, MCP and OAuth. Each is aChatBackendor a capability.ai-elements-uiholds the Compose elements. It depends onai-elements-chatonly, so it renders any backend, including one you write.- Optional artifacts bring heavier dependencies or other runtimes: A2A (official Java SDK), ACP (official Kotlin SDK), Koog, the in-app harness capabilities, generative UI and MCP Apps.
A new protocol is a ChatBackend in core or in its own artifact. It never needs a UI change.
Data flow¶
sequenceDiagram
participant UI as Elements
participant C as ChatController
participant B as ChatBackend
participant A as Agent / model
UI->>C: send("…")
C->>B: stream(history)
B->>A: protocol request
A-->>B: protocol events
B-->>C: ChatEvent (TextDelta, ToolInputAvailable, …)
C-->>UI: ChatState (messages, parts)
B->>C: approver.decide(toolCallId)
C-->>UI: Confirmation
UI->>C: respondToApproval(…)
C-->>B: ToolDecision
ChatController folds the events into messages (MessageReducer), publishes ChatState, queues
messages sent while a turn runs, keeps branches (regenerated answers) and checkpoints, and implements
the ToolApprover the backend asks.
UI elements are protocol-independent¶
Elements render the in-process model only. They never import protocol, provider, agent or MCP code,
and never interpret tool names or protocol conventions. What a part means is a model field set
upstream: ToolPart.kind (Function, Delegation, Skill) and ToolPart.source are declared by
on-device tools or mapped from a protocol in core; shared agent state is DataPart.STATE. A unit
test (LayeringTest) enforces this.
Packages in ai-elements-core¶
chat (controller, events, reducer) · model · protocol.aisdk / protocol.agui · provider.*
(model APIs) · http (internal transport helpers) · agent (tools, capabilities, loop,
sub-agents) · mcp · skills · auth · config. Dependencies point downwards: protocol and
provider use chat, agent, http and model, never the reverse.