arete-desktop
June 14, 2026 · View on GitHub
A minimal, agent-driven Generative UI shell for enterprise apps — built on Google's A2UI v0.9 protocol.
Executive Summary
arete-desktop is a React framework that lets end-users reshape their enterprise application UI through natural-language conversation with an agent, with every change gated by a visual diff before it commits. It is deliberately small: arete-desktop ships an app Shell, a multi-surface Page workspace, a Chat panel, a Visual Diff Engine, and a Page Operations Harness that gives the agent typed, structural control of the workspace. Everything else — components, theming, the agent, auth, data, persistence — is plugged in from outside. The first reference plug-in uses PrimeReact, but arete-desktop core has no dependency on it.
arete-desktop builds on Google's A2UI (Agent-to-User Interface) v0.9 protocol and reuses A2UI's renderer and agent SDKs unchanged. Its net-new contribution is the shell pattern, the diff engine, and the structural-operation harness — none of which A2UI provides on its own.
The Problem
Enterprise apps (ERP, CRM, ITSM) are dense, rigid, and built for the median of many personas at once. The result is familiar:
- One-size-fits-none layouts. A page tuned for everyone is optimal for no one. Cognitive load rises, throughput drops.
- High mutation cost. Changing a layout, adding a panel, surfacing a new data stream all require frontend work, design review, and regression risk.
- Administrative chokepoints. Layout flexibility is gated behind admin roles. Power users get a handful of templates, never a workspace tuned to their actual day.
The result is a frozen UI that can't keep up with how individuals actually work.
The Solution
arete-desktop shifts UI customization from an engineering ticket to a conversational act. A user says what they want — "give me a panel of overdue approvals grouped by urgency", "pin this widget to the Tickets page", "make Reports a 3×3 grid" — and an agent emits the change. Before anything mutates, the user sees a visual diff: green outlines on added components, red on removed, yellow on moved. One click commits; one click rejects.
[User intent] → [Agent] → [Shadow Surface + Diff Overlay] → [User approves] → [Live UI]
Three properties make this safe enough for enterprise use:
- Diff-gated by default. The agent never silently mutates the user's workspace. Every structural and content change passes through the same approve/reject pipeline.
- Per-surface granularity. Multiple pending diffs across multiple surfaces resolve independently — no all-or-nothing commits.
- Deterministic guardrails. Lifecycle hooks let host apps reject, rewrite, or audit any agent-proposed change before it can paint, including RBAC and data-resolution policy.
Architectural Scope
arete-desktop is intentionally narrow. It owns the shell, the workspace, the chat, the diff, and the structural-op harness — and nothing else.
| In scope (core) | Out of scope (plugged from outside) |
|---|---|
| App shell with left tab rail, top-bar slot, dockable chat | Authentication, RBAC enforcement, business-data persistence |
| Multi-surface page workspace with configurable regions | The catalog of UI components (consumer plugs in any A2UI v0.9 catalog) |
| Visual Diff Engine (shadow surface + overlay highlights + approve/reject) | The agent runtime and LLM transport (consumer supplies the message stream) |
| Page Operations Harness (typed structural ops the agent can invoke) | Long-term persistence of layout / conversation / approval history |
| Lifecycle hooks (onBeforeApply, resolveDataPath, onUserAction, onPrompt, onPageOp) | Domain-specific components (ERP, CRM, ITSM widgets live in adapters, not core) |
For every out-of-scope concern, arete-desktop exposes a clean hook or pluggable interface; it never bundles policy.
Technical Foundation
Protocol target: A2UI v0.9
v0.9 is the current published, feature-complete A2UI specification with renderer support across React, Angular, Lit, and Flutter. v0.8 is closed legacy; v0.10 is in draft and v1.0 is roadmapped for Q4 2026. arete-desktop v1 targets v0.9 and tracks v0.10 evolution without implementing it until v0.10 closes.
Dependencies and reuse (we do not rebuild A2UI)
arete-desktop consumes A2UI's existing primitives as-is:
- Renderer:
@a2ui/react/v0_9(A2uiSurface,basicCatalog,createComponentImplementation) and@a2ui/web_core/v0_9(MessageProcessor,SurfaceModel,SurfaceGroupModel,DataModel,ComponentsModel). - Agent SDK: A2UI's Python and Kotlin agent SDKs already ship streaming parsing (
A2uiStreamParser), inference strategies and prompt generation (A2uiSchemaManager,A2uiTemplateManager,InferenceStrategy), schema validation (A2uiValidator), payload repair (PayloadFixer), and a standard agent toolset (SendA2uiToClientToolset). Host apps reuse these directly. - Theming pattern: A2UI's CSS-variable theming via
:where(:root)is reused. arete-desktop adds no new theming abstraction.
What arete-desktop adds on top of A2UI
A2UI is a content protocol: surfaces, components, data, actions. It says nothing about:
- A multi-tab application shell with a dockable chat panel.
- A persistent multi-surface workspace (every A2UI sample today is a single-pane chat).
- Diffing or staging —
updateComponentsis applied immediately by the message processor. - Structural operations on a workspace (pin a widget to a page, reshape a page's region layout).
These four gaps are exactly the surface arete-desktop fills.
Differentiation from existing A2UI tools
| Existing A2UI artifact | What it is | Why arete-desktop is distinct |
|---|---|---|
tools/composer/ | CopilotKit-driven designer widget-authoring tool | arete-desktop runs inside a live app for end-users, gates mutations through a diff, and defines an app shell |
tools/editor/ | One-shot single-prompt UI generator | arete-desktop mutates a persistent multi-surface, multi-page workspace incrementally |
samples/client/react/shell/ | Single-pane chat → renders A2UI reply | arete-desktop is shell-shaped (rail, tabs, dockable chat), not single-pane chat |
Scope: What arete-desktop Ships
arete-desktop core is one React package: four components plus a page-operations harness plus a lifecycle-hook API.
1. <Shell> — application frame
The top-level container. Mounted once.
- Left edge rail: vertical column of tab icons. Consumer registers tabs as
{ id, icon, label, render }. Click switches the active tab. Icon-only, tooltip on hover, ~48–56px wide. - Top-bar slot: fully consumer-controlled (
topBar={<MyTopBar />}). arete-desktop ships no defaults for title, search, user menu — those belong to the host app. - Main content area: renders the active tab's
renderoutput. - Right-edge chat dock: optional. When
chatTabis configured, the chat is full-page on its own tab AND simultaneously docked to the right edge on every other tab. The dock collapses to a vertical-strip rail with a chevron expand/collapse affordance. Dock state persists across tab switches. - State surface:
state+onStateChangelets consumers persist Shell state however they want (localStorage, backend, nothing). Core ships no persistence.
2. <Page> — multi-surface agent-driven workspace
A convenience component for tabs whose content is a multi-surface, agent-mutated workspace. Optional — any React component can live in a tab. <Page> is what consumers use when they want the agent → diff → approve loop on a multi-region layout.
- Wraps
SurfaceGroupModelfrom@a2ui/web_core/v0_9. - Consumer configures named docked regions; surfaces are routed to regions by surfaceId→region mapping.
- Each region renders an
A2uiSurfacefrom@a2ui/react/v0_9. - Layout topology is serializable JSON (consumer persists it).
- Every surface inside
<Page>is automatically wrapped in<DiffOverlay>.
3. <Chat> — chat panel (full-page or docked)
A single component instance powers both render modes: full-page on the chat tab, docked on other tabs.
- Render modes:
"page"(full main area),"dock"(right edge, ~360–420px),"rail"(collapsed vertical strip). - Surface list: each agent-emitted surface is appended chronologically. Each entry uses
<A2uiSurface>with an optional user-prompt header. The list itself is plain React — not a new A2UI catalog widget; BasicCatalog already ships an in-surfaceList. - Chat input: text + send button, plain React (not an A2UI catalog widget). On submit, calls a consumer-supplied
onPrompt(text)callback. arete-desktop does not own the agent transport. - Shared state: conversation history is preserved across tab switches and dock/page/rail transitions.
4. <DiffOverlay> + Shadow Surface — Visual Diff Engine
The flagship feature. Used automatically by <Page>; exported as a primitive for consumers wrapping their own surfaces.
- Shadow routing: when an agent message (
updateComponents/createSurface/deleteSurface) arrives for a surface wrapped by<DiffOverlay>, it is written to a shadowSurfaceModelinstead of the live one. - Overlay highlights:
- green = added component
- red = removed component (live still visible underneath, dimmed)
- yellow = moved or attribute-changed component
- Approve / Reject bar: floating, anchor-configurable. Approve flushes shadow → live via the underlying
MessageProcessor. Reject drops the shadow. - Tree-level diff: computed using component IDs from A2UI's flat
updateComponentslist. - Per-surface granularity: each surface gets its own pending shadow + approve/reject. Concurrent pending diffs resolve independently.
- Diff hooks:
onProposed(diff),onApprove(diff),onReject(diff)— consumers wire to audit, undo, persistence.
5. Page Operations Harness — structural commands
A2UI's protocol mutates surface contents only. arete-desktop adds typed structural commands the agent can invoke against the workspace itself.
Shipped operations (v1):
| Operation | Purpose |
|---|---|
pinSurface(surfaceId, pageId, region?) | Promote a surface from the chat scroll into a page region |
unpinSurface(surfaceId) | Return a pinned surface to the chat scroll |
setPageLayout(pageId, layoutDescriptor) | Change a page's region layout (e.g. 3×3 grid, 2-column split, custom dock) |
moveSurface(surfaceId, targetRegion) | Move a pinned surface between regions on the same page |
setPageRegion(pageId, regionId, surfaceId | null) | Explicit slot assignment |
Design rules:
- These operations are first-class arete-desktop actions, not A2UI messages. arete-desktop defines their JSON schemas and ships their implementations.
- Consumers expose them to their agent however they want — typically as an ADK / OpenAI / vendor toolset, analogous to A2UI's
SendA2uiToClientToolset. arete-desktop ships only the schemas and implementations, not the agent-side toolset wrappers. - All structural ops flow through the Diff Engine by default — "change layout to 3×3" previews the new layout before committing. Consumers can mark specific ops as
autoApproveper-op or per-tab. - All ops emit through the same
onProposed/onApprove/onRejectstream as content diffs, so audit and undo are unified across content and structure.
Surface State History (navigation is NOT special-cased)
Do NOT implement "Back" as a bespoke primitive (no pushHistory flag, no
history.back() host function). Back is just one case of "restore a prior
surface state."
- Capture generically. Every surface render is appended to a per-surface, globally-ordered timeline (snapshot = components + dataModel + trigger + seq + ts), persisted like the chat transcript.
- Expose to the LLM via tools. The agent studies the timeline through tools
(e.g.
getSurfaceHistory) and decides what to render next. - Expose to handlers generically. Widget Manager handlers read the same
timeline (
surface.history) and restore a prior snapshot by index — or render something fresh. It is the LLM's choice what JS to write (restore vs rebuild).
There is no dedicated back/forward API — all navigation is "pick a state from the generic timeline (or build a new one)."
Lifecycle hooks
Exposed at the Shell level, threaded through every component and the harness:
| Hook | Purpose |
|---|---|
onBeforeApply(messages, ctx) | Sanitize, validate, RBAC-gate agent output before it touches any shadow. Return modified messages or null to reject. |
resolveDataPath(path, ctx) | Wrap JSON Pointer resolution with enterprise auth, tenant prefixing, redaction. |
onUserAction(action) | Audit hook on every A2UI action dispatch. |
onPrompt(text) | Consumer routes the user's chat-input prompt to its own agent/transport. |
onPageOp(op, ctx) | Fires for every page-harness operation; consumers can deny, transform, or log. |
Pluggable surface (everything else lives outside)
- Catalog: any A2UI v0.9
Cataloginstance. Reference plug-in (separate package, not in core):@arete-desktop/adapter-primereact— maps PrimeReact components to A2UI implementations viacreateComponentImplementation. arete-desktop core has zero PrimeReact dependency. - Agent / transport: consumer brings the A2UI message stream (SSE / WebSocket / A2A / mock). arete-desktop exposes
ingest(stream). - Page roster: consumer declares which tabs exist, their icons/labels, and what renders in each.
- Top-bar content: consumer-owned React.
- Persistence: none in core. Layout JSON, conversation history, approval log, undo stack — all consumer-owned via the hook APIs.
Routing Model
How agent output finds its destination:
- Default for agent replies with UI: every reply lands as a new surface in the chat scroll. The agent cannot silently mutate an active page just because the user is looking at it.
- Moving widgets onto a page is an explicit act: the user says "pin this widget to this page" (or "the second one", "this card") and the agent invokes
pinSurface(...)from the harness. The pin op flows through the Diff Engine — user approves, surface promotes from chat scroll into the page region. - Layout changes are agent-issued via the harness: "change this page into a 3×3 grid" →
setPageLayout(pageId, gridDescriptor)→ preview overlay → approve commits. - Acknowledgement bubbles: a structural op may optionally push a short text surface into the chat scroll ("Pinned KPI panel to top-left of Reports"). arete-desktop emits a default ack; consumers can override or suppress.
Reference Webapp: examples/erp-sandbox
A local-first React SPA that demonstrates the full plug-in surface end-to-end. Not part of arete-desktop core.
- Shell composition: 1 chat tab + 2 workspace tabs (e.g., "Tickets", "Reports") using
<Page>. - Catalog plug-in:
@arete-desktop/adapter-primereact, consumingprimereactUI components. - Mock agent: emits canned `updateComponents$ \text{and} \text{page}-\text{op} \text{messages} \text{in} \text{response} \text{to} \text{a} \text{prompt} \text{fixture} \text{set}:
- "\text{Group} \text{approvals} \text{by} \text{urgency}"
- "\text{Add} \text{an} \text{outstanding}-\text{invoices} \text{panel}"
- "\text{Swap} \text{KPI} \text{card} \text{with} \text{sparkline}"
- "\text{Pin} \text{the} \text{second} \text{card} \text{to} \text{Tickets}"
- "\text{Make} \text{Reports} \text{a} 3 \times 3 \text{grid}"
- \text{Persistence} \text{demo}: $better-sqlite3
stores Shell state, conversation history, and approval log — wiringonStateChangeandonApprove` to a real backend. Used only by the demo, not by core.
The sandbox demonstrates: chat full-page on chat tab; chat docked on other tabs; chat minimized to rail; agent reply lands in chat scroll; pinSurface op overlays target region → approve → surface promotes; setPageLayout op previews 3×3 grid → approve.
Critical Files to Reference
- Renderer primitives:
renderers/react/src/v0_9/,renderers/web_core/src/v0_9/processing/message-processor.ts,renderers/web_core/src/v0_9/state/(SurfaceModel,SurfaceGroupModel,DataModel,ComponentsModel). - Streaming + multi-surface reference:
samples/client/react/shell/. - Custom-component registration pattern (for
@arete-desktop/adapter-primereact, not core):samples/client/lit/custom-components-example/. - Theming reference:
docs/guides/theming.md,renderers/react/src/v0_8/styles/(CSS-var conventions). - A2UI specification:
specification/v0_9/docs/a2ui_protocol.md,specification/v0_9/json/client_capabilities.json. - Agent SDK reference:
agent_sdks/agent_sdk_guide.md,agent_sdks/python/src/a2ui/.
Out of Scope
- Authentication, RBAC enforcement, business-data persistence, raw API routing.
- ERP-specific components — those live in catalog adapters, never in core.
- Server-side LLM hosting or agent runtime.
- Layout editor UI (drag-to-rearrange panels) — agent-mediated only in v1.
- v0.10 / v1.0 A2UI support until v0.10 closes.
- Non-React framework support in v1; Angular / Lit / Flutter adapters are future work.
Delivery Plan & Verification Checklist
Workstream A: Core library (@arete-desktop/core)
<Shell>chrome: left rail renders ≥3 tab icons; click switches active tab; top-bar slot accepts arbitrary consumer React.<Chat>full-page mode: renders main area as vertical surface list + sticky input.<Chat>dock mode: switching off chat tab moves the same chat instance to right edge; minimize collapses it to a vertical rail strip; conversation history preserved across switches.<Page>topology: configure named docked regions on a non-chat tab; agent-created surfaces slot into regions per surfaceId mapping; configured regions render even when empty.- Shadow-diff loop: mock agent emits
updateComponents→ overlay renders with green / red / yellow outlines → Approve mutates live → Reject leaves live unchanged. Repeat forcreateSurfaceanddeleteSurface. - Per-surface diff isolation: two surfaces with concurrent pending shadows resolve independently.
- Lifecycle hooks:
onBeforeApplyrejecting a message stops shadow from painting;resolveDataPathprefix-injection resolves correctly through agent-bound paths;onApprovefires with the committed diff. - Page Operations Harness:
pinSurfacepreviews target region → approve promotes from chat to page.setPageLayoutwith a 3×3 grid descriptor previews → approve commits. Reject leaves state untouched in both cases. - Harness JSON schemas: every page op has a published JSON schema; wrapping into an ADK toolset (parallel to
SendA2uiToClientToolset) is mechanical. - Conformance: any wrapped A2UI bits (
MessageProcessorsubclass etc.) pass relevant v0.9 conformance suites (agent_sdks/conformance/suites/).
Workstream B: Reference plug-ins and sandbox
@arete-desktop/adapter-primereactseparate package: mapsprimereactcomponents to A2UI implementations viacreateComponentImplementation.- Catalog plug-in proof: the same
@arete-desktop/adapter-primereactcatalog is used in both the erp-sandbox and arete-chat apps without touching arete-desktop core. examples/erp-sandbox: full Shell + Page + Chat + Diff loop with the prompt fixture set above. Mock agent + SQLite persistence demonstrate the hook wiring.- Agent reply routing: prompt typed in docked chat on a non-chat tab — reply lands as a new surface in the chat scroll, NOT silently on the page.
Todos
Actionable components (framework + adapter)
- More PrimeReact adapter components — DataTable, Dialog, Calendar, Dropdown, MultiSelect, AutoComplete, TreeTable, FileUpload, TabView, Carousel, Paginator, Sidebar, Toast, Accordion, OrderList/PickList. Each wires through
useActionfrom@arete-desktop/corewith the category-specific auto-context shape documented inpackages/core/src/types/action.ts. - Migrate CheckBox and TextField to
useAction— currently use raw callable / two-way binding; align with the canonical pattern so value changes flow throughonUserAction. - Action gating / approval — for destructive actions (delete, archive), add a per-action
confirm: booleanpolicy that gates dispatch through a confirm-dialog before firing the user-action hook. Mirrors the diff-approval pattern. - A2UI expression evaluator wiring — resolve
{path: "/items/n/x"}and{call: "...", args: {...}}inaction.event.contextso per-item bindings work end-to-end. Today we only forward literal contexts plus component auto-context.
Page-perception extensions
- UI-runtime state beyond data model — focused element id, scroll position per surface, hover targets. Not canonical in A2UI v0.9; arete-desktop extension via a
clientUIStatefield on the per-prompt context. - Streaming data-model updates — today we poll the live processor at prompt-build time. Move to the canonical A2UI
metadata.a2uiClientDataModelattachment on every client→server message once we switch transports from HTTP polling to SSE / WebSocket.
Agent loop quality
- Multi-step action chains — action → form modal → submit → next action. Requires the agent to track an action-flow state machine across turns.
- Server-side validation depth — schema-validate every emission's component-specific props (Chart needs
labels.length === data.length; Calendar needs valid date ranges; etc.). Today onlyid/childrenreferences are validated. - Widget handler sandbox hardening — agent-authored Widget Manager handlers run in the webview's native JS engine via
new Functionwith shadowed globals (a soft sandbox; a runaway loop can freeze the UI). Harden to a sandboxed iframe / Web Worker for real isolation + a terminable timeout (runaway-loop protection). - Cursor Next/Prev DataTable mode — pagination today defaults to client-side (fetch the rows once, paginate in-browser), which is reliable for bounded datasets but requires fetching everything up front. For genuinely large, cursor-paginated tools (opaque sequential tokens, no jump-to-page), add a first-class server-paging mode: a prev/next-only paginator (no page numbers) with
hasNext/hasPrevflags and a directionalpageActionthat carries the stored opaque cursor — so the agent never has to map a row offset (ctx.first) to a cursor. Until then, large lists use a capped client-side fetch ("first N").
Workspace
- Out-of-the-box catalogs beyond PrimeReact — fw-dew adapter (Freshworks design system), MUI adapter, Ant Design adapter. Each is a new
packages/adapter-<name>package wired to the same@arete-desktop/corehooks. - Persistence reference adapter — the chat app and sandbox each have their own SQLite layer; extract a reusable
@arete-desktop/persistence-restadapter so consumers don't have to roll their own. - Agent support for deletePage — user-driven page deletion works; agent-invoked
deletePagepage op is deferred. - Persist pending (un-approved) diffs across reload — shadow surface state is ephemeral; persist to survive browser refresh.
Roadmap & Vision (2026 pivot): from ERP reference app to A2UI-native chat product
Direction: evolve arete-desktop's flagship from the
erp-sandboxexample into a self-hostable, general-purpose chat product — UX like Claude/Gemini chat — whose differentiator is that it is A2UI-based, so agents mutate a persistent multi-surface workspace, not just render rich inline components, with every mutation gated by arete-desktop's per-surface visual diff (approve/reject). It adds MCP and Skills support via a stronger agentic loop. The core design in this README (Shell, Page, Visual Diff Engine, Page Ops harness) is kept as-is and becomes the governance layer.
Why this pivot
The diff-gated, agent-mutable workspace is more broadly valuable than ERP layout customization. Packaged as a chat product, it competes with mainstream AI chat UIs on familiarity while offering something none of them do: the agent can restructure a durable workspace, and the human approves each change visually.
Competitive cross-check (the landscape we build on, not against)
The "agent mutates UI" space converged on three complementary layers — we combine them:
| Layer | Standard | Role | Our stance |
|---|---|---|---|
| UI description | A2UI (Google, Apache-2.0) | Agent describes UI as data; client renders from a trusted catalog | Already using (core + adapters) |
| Agent↔frontend loop | AG-UI (CopilotKit; broad framework support: LangGraph, CrewAI, Mastra, MS Agent Framework, Google ADK, PydanticAI) | Event stream: streaming text, tool calls, state patches (RFC 6902), lifecycle, INTERRUPT (HITL) | Adopt as transport |
| Tool-served UI | MCP Apps / MCP-UI (official MCP extension) | MCP servers return UI in sandboxed iframes | Consume (render in surfaces; later phase) |
Frameworks/products surveyed: CopilotKit (spans all three, already pairs A2UI+AG-UI+MCP — but an SDK,
not a product, and no visual-diff governance); LibreChat / Open WebUI / assistant-ui / Open Canvas /
Jan (shipped chat products with MCP + artifacts, but inline/artifact generative UI — no persistent
agent-mutable workspace with approve/reject diffs). Skills = Anthropic Agent Skills (SKILL.md)
open standard (folder + SKILL.md; adopted across 30+ tools incl. Claude Code).
Moat: nobody ships our exact product — a Claude/Gemini-style chat where the agent mutates a persistent
workspace gated by per-surface visual diffs, A2UI-based, with MCP + Skills. AG-UI's coarse INTERRUPT
is an action gate, not a per-surface visual shadow-diff; the Visual Diff Engine + Page workspace remain
the uncontested differentiator. Loop, MCP, and Skills are solved by the ecosystem — we adopt, not rebuild.
Decisions
- Transport/loop: adopt AG-UI; route its UI/state events through arete-desktop's existing Diff Engine.
- Agent runtime: Vercel AI SDK (already a dep) + an AG-UI adapter; MCP client + multi-step tool calling.
- Product shape: self-hostable OSS product on arete-desktop core (replaces
erp-sandboxas flagship; the sandbox is demoted to a test fixture/example). - Skills (v1): Anthropic Agent Skills (
SKILL.md) instruction/resource bundles loaded into context.
Target architecture — AG-UI is the seam
arete-desktop (this repo): chat product + core (UNCHANGED moat: Shell · Page · Visual Diff Engine · Page Ops)
▲ AG-UI client (grow the existing `ingest()` into an AG-UI ingest adapter)
│ AG-UI event stream (SSE/WS)
▼
Agent runtime (v1: in-repo `packages/agent`; FUTURE: standalone `arete-agent` service)
Vercel AI SDK loop · MCP client(s) · SKILL.md loader · emits A2UI surfaces + pageOps + state deltas
AG-UI event → arete-desktop pipeline (the core technical mapping):
TEXT_MESSAGE_*→ chat scroll (ChatStore).- A2UI surface emissions →
DiffRouter.route(...)into the shadow model; pinned surfaces gated viarouter.gateSurface(...)→ approve/reject overlay. - Page ops →
PageOpsHarness.apply(...)(already diff-gated; auto-switches to the target tab). STATE_SNAPSHOT/STATE_DELTA(RFC 6902) → workspace state, diffable before commit.TOOL_CALL_*→ tool status in chat;INTERRUPT→ mapped to an approve/reject gate.
Phased roadmap
| Phase | Status |
|---|---|
| Phase 0 — PoC | Done |
| Phase 1 — Chat product | In progress (partial) |
| Phase 2 — MCP + Skills | In progress (MCP done; Skills upcoming) |
| Phase 3 — Standalone agent + advanced | Upcoming |
- Phase 0 — PoC (done): AG-UI ingest adapter in core (
@arete-desktop/agui→AgUiDecoder); Vercel AI SDK backend (@arete-desktop/agent→runAgentTurn,createAgentRouter); one MCP server (in-memoryget_ticket_statsvia@modelcontextprotocol/sdk); oneSKILL.mdloaded into system prompt. Full loop proven: agent mutates a surface → visual diff → approve/reject. - Phase 1 — Chat product (in progress):
arete-chatflagship app (apps/chat) with chat-first UX, dynamic page creation via agent, SQLite persistence (better-sqlite3), the full arete-desktop core lifecycle, and a settings UI (/api/settings+SettingsPanel) — model + Ollama URL, MCP server add/remove/toggle, and gate-diffs, all persisted to SQLite and applied live (the agent router reads settings per turn viaresolveOptions; no restart). Deferred: multi-conversation. - Phase 2 — MCP + Skills as features (MCP done; Skills upcoming):
- MCP server connection management — done: config-driven external servers, live add/remove/toggle (Phase 1 settings UI), plus per-server connection status/health (
GET /api/agui/mcp-status: connected/failed + discovered tools + error) and manual reconnect (POST /api/agui/mcp-reconnect), surfaced inSettingsPanel. Remaining: OAuth flows for remote servers. - Render MCP Apps / MCP-UI resources inside arete surfaces — done: MCP tool results carrying
resource/resource_link(inlinetext/html,ui://, ortext/uri-list) are captured per turn and rendered as framework-synthesizedEmbedsurfaces (sandboxed iframe, noallow-same-origin) in the chat scroll. Remaining: bidirectional MCP-UIpostMessage(tool calls from inside the iframe). - Skills management UI — install/enable/disable SKILL.md skills (today filesystem-loaded) (upcoming)
- MCP server connection management — done: config-driven external servers, live add/remove/toggle (Phase 1 settings UI), plus per-server connection status/health (
- Phase 3 — Standalone agent + advanced (upcoming):
- Standalone
arete-agentservice (HITL and headless) consumed over AG-UI —packages/agentis the seam - Sandboxed skill script execution (beyond instruction bundles) with an exec/security model
- Multi-agent (A2A) coordination
- Other RDBMS store implementations (e.g. Postgres) behind the existing
Storeinterface
- Standalone
Seam / agent-loop polish (ongoing)
- Token-by-token streaming — today the reply arrives whole as START→CONTENT→END; the decoder and wire format support streaming deltas but the agent emits full replies.
- AG-UI
STATE_SNAPSHOT/STATE_DELTA(RFC 6902) — decoder handles these events; pending: route through diff-gated workspace state. - AG-UI
INTERRUPT— pending: map to the approve/reject gate. - Multi-arg MCP tools — JSON-Schema→zod conversion; the in-memory demo tool is no-arg only.
Phase 1 product gaps (deferred)
- Multi-conversation — multiple chats;
threadIdfield exists in AG-UI events butChatStoreis single-flat-list. - Agent
deletePage— user-driven delete works; agent-invoked delete page op deferred. - Persist pending (un-approved) diffs across reload — shadow surface state is ephemeral.
- Auto-title pages/conversations from the first prompt.
Reuse (do not rebuild)
Moat unchanged (shell/, page/, harness/, diff/). Agent-loop scaffold already built — agent/transcript.ts,
agent/contract.ts, agent/context.ts, diagnostics/*, and the server's no-op/diagnostic/correction loop —
migrates onto the AG-UI backend rather than being discarded. ingest() remains the low-level A2UI message
entry point; @arete-desktop/agui (AgUiDecoder) is the AG-UI client entry point for streaming agent runs.