Skip to content

Widgets

A widget is a screen your Agent Service publishes. It runs in the browser, acts as the signed-in person, and reads and writes the vault that person is working in. There is no backend of yours behind it and no copy of the data to hold.

What a widget does

  • Shows results. Render rows from the service’s cubbies and records from the vault’s Memory Bank. A widget reads from exactly those two places: a named query with sql, or one with tool.
  • Takes input. Record audio, upload a file, submit a form, and write it into the vault.
  • Starts and follows work. Publish an event, or workflow.start targeted at one workflow, then follow that stream’s events as they arrive.

Because a widget acts as the person, everything it does lands under that person’s own access to the vault, never under a credential of yours.

Where a widget comes from

Source Declared in Published by
An agent’s widget widgets: [...] in the agent’s cef.config.ts cef push, with the agent
A service widget Its own widget project cef widget push

ROC’s Widgets page lists every screen the service publishes.

With a workflow

A workflow’s cubby steps write rows; a widget is the screen onto them.

Pin it

Pin a service widget to a workflow and it opens from the end of the workflow, handed the run you are looking at, so it shows exactly that run’s rows. Build and publish the widget first: see Build a widget.

Every workflow canvas ends with an Outcome card.

  1. Click Pin a widget on the Outcome card. The picker lists the widgets your Agent Service has published.
  2. Pick one. The card shows the widget’s name; its menu has Open, Change…, and Unpin.
  3. Deploy. The pin is saved with the workflow’s canvas as pinnedWidgetId.

The same pinned widget appears in the Summary panel of a run. Opening it from a run passes that run to the widget.

The Outcome card with a pinned widget and its menu open A pin belongs to the Builder canvas. A workflow pushed from a repo has no canvas, so it carries no pin.

Read the run it was opened for

When a person opens the pinned widget from a run, the host hands the widget a context:

Field Value
runId The run’s id, run-<context>. The same value as {{ $runId }} in the workflow.
vaultId The vault the run happened in. The widget reads that vault’s data, not the viewer’s own vault.
const { context } = await window.WidgetRuntime.identity();
const runId = context?.runId;
const result = await window.WidgetRuntime.query("by-run", [runId]);

with a query declared on the widget such as:

SELECT ticket_id, category, priority FROM triage_tickets WHERE run_id = ? ORDER BY ticket_id

This works because the workflow’s cubby steps write {{ $runId }} into a run_id column; see Steps: Cubby query and Cubby exec. A share link to a widget carries the same values in its URL fragment: #vault=<vaultId>&run=<runId>. Without a runId (opened directly, not from a run), show the newest rows instead.

Start runs from it

Publish workflow.start with { target: "<agentServicePubkey>:<workflowId>" }, then subscribe to the stream for workflow.completed.

await WidgetRuntime.publish("workflow.start", input, requestId, {
target: `${agentServicePubkey}:${workflowId}`,
});
const stop = WidgetRuntime.subscribe(
requestId,
{ types: ["workflow.completed"] },
(events) => events.forEach(render),
);

With a code agent

Ship the widget with the agent: declare it in the agent’s widgets, and cef push uploads it next to the bundle. It reads the cubbies the agent writes with ctx.cubby, and publishes the events the agent’s @OnEvent handlers handle. Until the reader’s vault has connected the agent, its queries fail with AgentNotConnectedError; connectAgent() runs the connection. See Build a widget.

The runtime

@cef-ai/widget-runtime is the browser half of every widget. It boots from the manifest the CLI bakes, signs the reader in, and exposes window.WidgetRuntime:

Member Does
query Run a named query: sql against one of the service’s cubbies, or tool (search, get, neighbours, countByType) against the vault’s Memory Bank.
publish Publish an event into the widget’s scope; pass { target } to address one agent or workflow.
subscribe Follow one stream of the scope; new events arrive as ordered, de-duplicated batches.
connect, connectAgent, agentStatus Connect an agent and check its connection.
identity The resolved identity, and the vault and run a host named.

The runtime also renders config-driven kinds (record, list, dashboard, submit, conversation, composite) and carries the audio capture and upload path. Fully custom pages use the same surface.

A widget framed by ROC or your own page gets the reader’s identity from the host; opened by direct link, it signs the reader in itself. See How the reader is signed in.

Build one

Task Page
Declare, write, run locally, and ship a widget Build a widget
Render cubby rows and Memory Bank records, live Visualize vault data
Capture input and files into the vault Onboard data with a widget
Open it from a workflow’s runs Pin it