A transport-agnostic, bidirectional protocol for communication between AI agents and rendering surfaces. One agent, every screen.
AI agents produce structured output — tables, forms, charts, status messages, code blocks — but the rendering of that output is tightly coupled to the transport and frontend framework. An agent built for a web chat cannot render on a desktop app. An agent behind an SSE stream cannot serve a mobile client expecting a different protocol.
Every new rendering surface requires custom integration code.
ARP defines a protocol — not a framework, not a library. Like how Wayland decoupled applications from display servers, ARP decouples agents from renderers.
The agent decides what to display. The renderer decides how to display it.
Agent (backend) Renderer (web, CLI, mobile)
─────────────── ──────────────────────────
Owns surfaces ── render / delta ──► Owns display
Owns UI state ◄── input events ──── Owns input routingtable with headers and rows — not HTML.Every ARP message is JSON with at minimum { v: 1, type: "<type>" }.
| Type | Purpose |
|---|---|
hello | Capability handshake on connect |
delta | Incremental text chunk |
tool_start | Tool execution started |
tool_end | Tool execution finished |
render | Generative UI component |
patch | Incremental component update |
error | Error event |
commit | Stream complete |
| Type | Input Type | Purpose |
|---|---|---|
input | text | User text message |
input | action | Button click / UI action |
input | form_submit | Form submission |
Every ARP-conformant renderer must support at least text. Components declare fallback chains — chart falls back to table, which falls back to text.
/_arp/v1import { ArpClient } from '@haira/arp'
const client = new ArpClient('ws://localhost:8080/_arp/v1', {
onDelta: (text) => appendToChat(text),
onRender: (event) => renderComponent(event.component, event.props),
onDone: () => markStreamComplete(),
})
client.connect()
client.sendText('Show me the sales data')import { ArpChat } from '@haira/arp-react'
function App() {
return (
<ArpChat
url="ws://localhost:8080/_arp/v1"
theme="dark"
title="Data Explorer"
/>
)
}Also available: @haira/arp-vue for Vue 3 and github.com/haira-lang/arp-go for Go backends.
Every Haira server speaks ARP natively. No configuration needed.
import "ui"
tool query_database(query: string) -> any {
"""Executes a SQL query and displays results."""
rows, err = postgres.query(db, query)
if err != nil {
return ui.status_card("error", "Query Failed", conv.to_string(err))
}
return ui.table("Query Results", headers, rows)
}
agent DataExplorer {
provider: OpenAI
tools: [query_database]
ui: ui
}
@webhook("/chat")
workflow Chat(message: string, session_id: string) -> stream {
return DataExplorer.stream(message, session: session_id)
}New components follow a three-phase lifecycle inspired by Wayland:
x-vendor-names-name| Level | Required Components | Target |
|---|---|---|
| Minimal | text + text input | Voice assistants, IoT |
| Basic | text, table, form, confirm, choices | CLI terminals |
| Standard | All core components, full object model | Web / desktop |
| Full | Standard + streaming, multi-surface, file upload | Rich web apps |