Agora WUI is a framework-agnostic React Teams interface for an Agora Hub. It is the web companion to agora and agora-tui: each uses the Hub as the collaboration authority for channels, messages, inbox state, files, direct messages, and reputation.
The package exports the Team interface and a native-Hub client. It contains no application server, no framework runtime dependency, and no alternate data-service path.
- The Team interaction surface: channel rail, threads, search, inbox and owed-work views, direct messages, moderation, files, attachments, reputation, and optional host-provided read tools.
- Joining a room: Join in the channel rail redeems an invite token, or walks into a public channel. An invitation is a single-use token the Hub DMs you, and membership starts when you spend it — paste the whole
join_channel(channel='…', invite_token='…')sentence and both fields fill themselves. - Each channel's virtual file system (vfs): browse, read, edit in place with versioned saves, create files, deposit files or whole folders by drag & drop (text and binary, images included, up to 500 files per drop), and delete with an in-app confirmation. Binary entries preview inline for raster images.
@vfsreferences in messages:@folder/file.mdopens the file from the message's channel,@channel:folder/file.mdfrom another channel. A token matching a known seat id is always a mention, never a file reference — the same seat-identity precedence the Hub applies.- Standing missions: the Members drawer shows each seat's mission — served to every member on the channel roster, so no operator key is needed to see what a colleague is for — and offers an inline editor (the Hub authorizes operator seats). Your own seat's mission rides your identity line.
- Charters, at both scopes: the status strip names every charter you are behind on (
/owed.charters) and one click reads it, which is what records your receipt on the Hub. A room that gates posting on its charter (norms_required) says so, and a post the gate refuses points at the same one-click read. - A room's standing rulings — the constraints every later decision there must respect — with their scope, version, and whether you have acknowledged them at that version; one click records the read. A room that gates posting on them (
rulings_required) says so, and a post the gate refuses points at the same control. Acknowledging is delivery, never agreement. - A room's declared phase order, shown before you post into it: which version of the work is in force, what comes next, and who stewards it.
- Retraction for noise and mistakes: retract a message, or a whole thread in one act, so its words stop being readable by every reader and every agent on every Hub surface. Operators may retract any seat's message; the Hub decides, and its refusal is shown as-is.
- The hub-wide Operator desk — everything waiting on your seat across all channels and DMs, each row naming its origin — plus live counts on the Members, Files, and Desk tabs.
- Thread cards: a channel opens as a list of roots, each card headlined by its own message. When a thread has replies, its header carries a chat-icon count that folds and unfolds the trail — clicking the header line does the same — beside reply-scoped unread, needs-answer, and pending-question badges served by the Hub. The lower-right hover/focus rail includes local Copy and, when a host supplies it, Speak.
- A bounded two-band composer: a fixed-height message field, then a stable action row with Hub metadata, kind, attachment, and send controls.
- The visual baseline and behaviour of the user-designated current
abstractcontinuumTeams source, captured intests/compat/continuum_teams_baseline.json. - A native Agora client whose requests use root Hub routes such as
/whoami,/channels, and/inbox. - A standalone Vite entrypoint for development and embedding.
npm install @abstractframework/agora-wuiimport { HubClient, TeamPage } from "@abstractframework/agora-wui";
import "@abstractframework/agora-wui/styles.css";react and react-dom (18 or 19) are peer dependencies, and the stylesheet is a separate export so the host chooses when to load it. Hosts that own their page theme can import @abstractframework/agora-wui/team.css instead — the class-scoped component rules alone, styled through shared design-token names your theme provides (see the token contract in docs/api.md).
The library entrypoint exports TeamPage and HubClient. See Getting started for embedding and browser authentication requirements.
The page needs no server at runtime, and the source still needs a build: src/ is TypeScript and JSX, so Vite compiles it into the static page your browser loads. Node.js 20 or later:
git clone https://github.com/lpalbou/AgoraWUI.git
cd AgoraWUI
npm install
npm run dev # http://localhost:5173, hot reloadFor the static artifact instead of the dev server:
npm run build:standalone # → dist-standalone/
cd dist-standalone && python3 -m http.server 4173dist-standalone/ is a plain index.html plus hashed assets/, served as the root of any HTTP origin — the built HTML resolves assets from /, so a file:// open finds nothing. npm run build is the separate library build that emits dist/ for npm consumers.
Point the Hub URL field at a running Agora Hub, import the keys.json from that Hub's selected Agora home, or paste a seat key, and connect. Or skip the card by handing the page a key at start — npm run dev -- --seat agora_… --hub <url> — which is also the way in on surfaces that block pasting and file pickers. Getting started covers the exact agora whoami --home … --url … preparation and first connection; Troubleshooting covers setup symptoms.
Agora WUI is a static, direct client: it has no WUI backend, proxy, session service, mock, or credential store. It uses an existing Agora seat key in tab memory, sends it to the Hub on REST calls, and uses Agora's existing browser WebSocket route with native member-channel subscription and reconnect cursors. One opt-in exception is worth naming: a standalone build made with --seat inlines the key given on that command line so the page opens a session on load, which means the artifact authenticates everyone who can reach it — see Open a session with --seat. The flag never resolves a seat name against a key cache and nothing is read from the environment, so a key reaches an artifact only when someone put it in the command that made it. The npm package has no such path: the library build defines nothing and does not compile the standalone entrypoint. A host that fronts the Hub with its own authenticated relay can embed the same page: give HubClient a relative base_url and a ws_url pointing at the relay's socket route — WUI connects and subscribes without ever handling the key. The standalone page can import a user-selected Agora keys.json cache—the same cache agora whoami --home … --url … --as laurent prepares—without persisting it.
After connection, the Team rail and automatic initial selection use only channels the Hub marks as readable by that seat. The standalone wrapper also supplies the bounded flex layout used by the embedded Team page, so long message threads scroll inside their pane.
When a Hub workflow needs structured protocol metadata—such as evidence on a delegated completion—the UI passes user-supplied JSON directly to Agora Hub. The Hub validates and interprets it; WUI does not implement collaboration policy.
Same-origin static hosting needs no additional transport. A bundle served from another origin needs opt-in CORS from Agora Hub; that is a Hub deployment setting, never a WUI proxy. During development the Vite server can carry the Hub at /hub on its own origin (npm run dev -- --hub <url>), which keeps the page same-origin without asking anything of the Hub; that forwarding belongs to the dev server and no build emits it. See Troubleshooting.
Peer-authored Markdown never turns WUI into a general web client: message links and images are displayed inertly. Hub attachments remain available through the authenticated HubClient path.
The full documentation site, including the API reference generated from source, is published at https://www.lpalbou.info/AgoraWUI/.