Build a custom UI
@coframe/editor-session gives you the building blocks Coframe Agent‘s own
frontend is built on: the full RoomState types, the typed RPC interface
(EditSessionAgentLike), and the realtime wire-protocol parser. Reach for it
when you want to design your own editor UI with full control over state and
rendering, rather than streaming a curated event feed.
Installation
Section titled “Installation”pnpm add @coframe/editor-sessionHow it compares
Section titled “How it compares”@coframe/agent-client | @coframe/editor-session | |
|---|---|---|
| Events | A curated, app-facing event feed | The full RoomState snapshot + event stream |
| RPC | Send messages, approvals, cancel | Every typed method the editor frontend calls |
| State | You keep your own | Typed RoomState slices for every service |
| Best for | Rendering the agent’s work | A bespoke editor frontend with full control |
Read the session state
Section titled “Read the session state”RoomState is the single broadcast snapshot every viewer sees, with a typed
slice per concern:
import type { RoomState } from "@coframe/editor-session/state";
function render(state: RoomState) { state.sandbox; // dev sandbox lifecycle + preview URL state.kernel; // live browser status + live-view URL state.planning; // task list the agent is working through state.storybook; // Storybook service status state.vitest; // unit-test runner status state.testResults; // live test results streamed from the sandbox state.filesWatch; // file-watch subscriptions state.skills; // available skills state.requirements; // requirements the agent is gathering state.pendingAnnotationSubmission; // latest human annotation, if any}Call the agent (typed RPC)
Section titled “Call the agent (typed RPC)”EditSessionAgentLike is a type-only mirror of the callable RPC surface. Every
method takes a typed input object:
import type { EditSessionAgentLike } from "@coframe/editor-session/rpc";
// `agent` is your Agents SDK RPC stub, typed as EditSessionAgentLike.await agent.approveToolCall({ toolCallId });await agent.rejectToolCall({ toolCallId, reason: "not now" });await agent.stopAgentTurn();
await agent.ensureKernelBrowser();await agent.reloadKernelBrowser();
await agent.writeFile({ path: "src/App.tsx", content });const file = await agent.readFile({ path: "src/App.tsx" });
await agent.upsertTask({ /* … */ });await agent.completeTask({ taskId });
await agent.restartService({ service: "storybook" });await agent.setPreviewTarget({ url: "https://example.com" });Parse the wire protocol
Section titled “Parse the wire protocol”Frames arriving on the Agents SDK WebSocket are parsed with
parseRealtimeEnvelope, which classifies each frame into one of three kinds:
import { parseRealtimeEnvelope } from "@coframe/editor-session/realtime";
socket.addEventListener("message", (event) => { const envelope = parseRealtimeEnvelope(event.data); if (!envelope) return; // SDK framing or unrecognized — skip
switch (envelope.kind) { case "snapshot": // Full replay (e.g. on reconnect) — fold each event into your state. for (const evt of envelope.events) apply(evt); break; case "event": // Incremental update — apply a single event. apply(envelope.event); break; case "error": console.error("Session error:", envelope.message); break; }});Putting it together
Section titled “Putting it together”A custom frontend follows the same shape Coframe Agent uses internally:
- Open the Agents SDK WebSocket for the session.
- Parse each frame with
parseRealtimeEnvelope. - Fold events into local state typed as
RoomState. - Call
EditSessionAgentLikemethods on your RPC stub to drive the session. - Render your own components from the typed state.
Live demo
Section titled “Live demo”Connect to the session WebSocket and watch RoomState snapshots arrive in
real time: