Skip to content

Widget runtime (@cef-ai/widget-runtime)

The widget runtime is the browser code every widget runs. It reads the widget’s manifest from window.WidgetSandbox.manifest, signs the reader in, exposes window.WidgetRuntime, and renders the built-in kinds. cef build, cef widget push, and cef dev inject the runtime and the manifest into your entry HTML; you do not add them yourself.

cef build vendors the runtime version that the installed CLI resolves, and logs it. Upgrade the CLI to ship a newer runtime.

window.WidgetRuntime

Method Returns Does
query(ref, params?) Promise<QueryResult> Runs a declared query by id. params bind to ? in its SQL, or are the tool’s arguments for a Memory Bank query.
publish(type, payload, context?, options?) Promise<{ eventId }> Publishes an event into the widget’s scope as the reader. options.target: <asPubkey>:<alias>, the one agent the event is for.
subscribe(context, opts, onEvents) () => void (stop) Follows one stream; onEvents receives each poll’s new events as an ordered batch.
identity() Promise<Identity> The reader’s identity.
connect() Promise<Identity> Signs the reader in.
connectAgent() Promise<void> Connects the widget’s agent to the reader’s vault.
agentStatus() Promise<'connected' | 'disconnected'> Whether the agent is connected.
onIdentityChange(cb) () => void (unsubscribe) Called when the identity changes.
Type Shape
QueryResult { columns: string[]; rows: unknown[][]; meta: { rowCount: number } }
Identity { publicKey, status: 'anon' | 'connecting' | 'ready', context? }. context is { runId?, recordId?, vaultId? } when the host or link named one.
WidgetEvent<P> { eventId, type, context, payload, from?, timestamp }
subscribe options types?, intervalMs (default 2500), maxBackoffMs (default 30000), onError (default console.warn)

subscribe delivers events already in the stream in its first batch, and each event once. It stops when you call the returned function or when the reader becomes anon.

Errors

Error When
AgentNotConnectedError A read for a reader whose vault has not connected the agent. Has agentId. Offer connectAgent().
WidgetSignedOutError A framed widget’s host did not answer the identity handshake within 8 seconds, or answered malformed.
WidgetVaultUnreachableError The host or link named a vault the widget cannot open. The runtime does not fall back to another vault.
WidgetWalletUnconfiguredError A standalone widget’s manifest has no wallet origin.

Kinds

Set kind and config in the widget declaration; the runtime renders into #app.

Kind config
record { query, fields: [{ label, column, format? }], empty? }
list { query, item: { title, subtitle?, meta? }, empty?, limit? }
dashboard { panels: [{ title, query, render: 'metric' | 'bar' | 'table' | 'list', value?, label?, columns?, item? }] }
submit Form + audio capture → upload → publish → poll. See Onboard data.
conversation Turn-based audio: startEvent?, turnEvent, confirmEvent?, turnsQuery, roleColumn, textColumn, turnIdxColumn, statusQuery?, confirmWhen?, doneWhen?, poll, audio.
composite { groups, panels, defaultPanel, gating: { statusQuery, unlockWhen, firstRunPanel, hero? }, menu? }. Each panel holds any other kind, or { kind: 'custom', renderer, query? } drawn by a function registered with registerCompositePanel.
custom Your own page.

Field formats: text, multiline, date, reltime, number, written as "column:format".

Manifest

The manifest cef writes into the entry HTML (WidgetManifest):

Field Meaning
schemaVersion 1.
widgetId, name Identity.
agentId <asPubkey>:<alias>. Empty until --as-pubkey is given.
scope The vault scope the widget reads and publishes in.
cubbyAlias Default cubby for sql queries.
queries { id, label, sql?, cubby?, tool?, limit?, timeoutMs? }[]; tool is search, get, neighbours, or countByType.
events { type, schemaRef? }[].
kind, config Built-in kind.
wallet { appId, env, origin }. origin is the Manykind passkey wallet used for standalone sign-in.
endpoints vault, gar, marketplace, s3GatewayAuthInfo, rpc, from --env.

Sign-in

Opened Identity source
In a frame The host, over the postMessage handshake below. A framed widget never falls back to the standalone wallet.
Top-level The Manykind passkey wallet at wallet.origin. An active session resumes silently; otherwise the runtime shows a sign-in control and opens the wallet on click. Reads use a delegation, so the reader is not asked to sign each request.

A link can name what to show, in its fragment (preferred) or query: vaultId or vault, runId or run, recordId or record. A named vault is still checked against the reader’s own access.

Host contract

To frame a widget in your own page, mount a host:

import { createWidgetHost } from "@cef-ai/widget-runtime";
const dispose = createWidgetHost({
allowedOrigins: ["https://widget.example"], // required, canonical origins only, no "*"
widget: document.querySelector<HTMLIFrameElement>("iframe#cef-widget")!, // optional extra check
getIdentity: () => (signedIn ? { pubkey, sigType: "ed25519" } : null),
sign: async (bytes) => signRaw(bytes), // raw ed25519 over the exact bytes; null = declined
});
Message Direction Fields
cef-widget:identity-request widget → host requestId, v
cef-widget:identity-response host → widget requestId, v?, pubkey?, sigType? (ed25519 default, or sr25519), token?, context?: { runId?, recordId?, vaultId? }, error?, errorCode?
cef-widget:sign-request widget → host requestId, v, bytes: number[]
cef-widget:sign-response host → widget requestId, v?, signature: number[] | null, error?, errorCode?
Rule Value
Protocol version 1 (PROTOCOL_VERSION). A message without v is v1; an unknown v is rejected with errorCode: 'unsupported-version'.
Bytes number[] of integers 0–255, at most 64 KiB (MAX_SIGN_BYTES). Check with isByteArray.
Signing Sign the bytes verbatim (ed25519_signRaw), never with a message-wrapping sign method.
Timing The widget retries the identity request every 400 ms for 8 s; a sign request waits up to 60 s.
Sandboxed frames Their origin is "null"; pass allowedOrigins: ["null"] and also set widget.

Library exports

Export Use
createWidgetHost, createHostBridge Host side of the contract.
IDENTITY_REQUEST_TYPE, SIGN_REQUEST_TYPE, PROTOCOL_VERSION, MAX_SIGN_BYTES, isByteArray, WidgetProtocolVersionError, WidgetMalformedMessageError Contract constants and checks for a hand-written host.
mountKind, renderList, renderRecord, renderDashboard, renderSubmit, renderConversation, renderComposite, registerCompositePanel Render the built-in kinds yourself.
validateConfig Check a kind config.
captureAndUploadAudio, createVaultObjectUploader, createVaultObjectStore, encodeWav, segmentPcm, decodeToMono16k, requestMicStream Audio capture and upload.
connectStandaloneWallet, createCefWalletGate, requestSignIn, autoSignIn Standalone sign-in.
contextFromLocation, currentLinkContext Read a link’s context.
@cef-ai/widget-runtime/build-tools: buildWidgetManifest, injectWidgetBridge What the CLI uses to build and inject the manifest.