The complete surface of the Mini App layer. For what these mean, see Mini Apps; for how to combine them, see Building Mini Apps.
Everything here is defined in packages/app-runtime, which the web runtime, the
nodetool app debug harness, and the eval suites all share.
Widgets
Every widget accepts binding, visibleWhen, disabledWhen, and format on
top of its own fields.
Display
| Widget | Shows |
|---|---|
| Heading | Static text at H1–H3. |
| Text | Static or formatted text. |
| Markdown | Rendered Markdown. The right choice for streamed prose. |
| Image | An image value. Fit contain or cover, fixed height, placeholder. |
| Audio | An audio value with a player. |
| Video | A video value with a player, max height, placeholder. |
| JSON | A structured value, formatted. |
| Table | An array value as rows. Max height, placeholder. |
| Output | A value whose type varies; renders by shape. |
| Progress | The bound operation’s progress. |
Inputs
| Widget | Writes | Commits |
|---|---|---|
| Workflow Input | The bound Input node, rendered from its declared type. | no |
| Text Input | Text. Single-line or multiline. | yes |
| Number Input | A number. Min, max, step. | yes |
| Slider | A number. Min, max, step. | yes |
| Switch | A boolean. | no |
| Select | One of a fixed option list. | no |
| Image Input | An image. | no |
| Audio Input | An audio file. | no |
| Video Input | A video file. | no |
| Document Input | A document. | no |
| Color Input | A color. | no |
| Resource Picker | The selected resource of a resource binding. | no |
| Resource Gallery | The selected resource, from a grid. Tile size. | no |
| Storyboard Scenes | Edits the bound storyboard through the resource provider. Fires no event. | no |
“Commits” means the control reports a settled value on release or blur, which is
what makes release pacing meaningful.
Actions and layout
| Widget | Does |
|---|---|
| Button | Fires its click events. Variant contained/outlined/text, color primary/secondary/warning. |
| Panel | A titled container holding other widgets. |
| Columns | Two slots, left and right. |
| Divider | A horizontal rule. |
Binding grammar
A binding is one string. Bindings key on node IDs, so renaming a node in the graph editor never breaks an app.
| Token | Addresses |
|---|---|
op:<opId>/in:<nodeId> |
An operation input. |
op:<opId>/out:<nodeId> |
An operation output. |
op:<opId>/prop:<nodeId>#<prop> |
A node property driven by a widget. |
op:<opId>/exec#<field> |
Execution state: running, progress, error, activity. |
var:<variableId> |
A declared app variable. |
view:<componentId>#<prop> |
Widget-local state. Never persisted. |
node:<nodeId>#<prop> |
Legacy node property, resolved against the default operation. |
<name> |
Legacy bare name, resolved against the live graph. |
A bare name resolves by how the widget uses it: a write widget looks for an
Input node, a read widget looks for an Output node and then a variable, and a
condition or format token tries outputs, then inputs, then variables. A name
that matches nothing is a validation error, not a silent no-op.
Actions
A widget event dispatches one action. Actions are data the runtime interprets.
| Action | Fields | Effect |
|---|---|---|
run |
operationId |
Runs the operation, subject to its policy. |
cancel |
operationId, optional invocationId |
Cancels the operation’s live runs, or one specific run. |
setVariable |
variableId, value or the firing widget’s value |
Writes a variable. |
toggleVariable |
variableId |
Inverts a boolean variable. |
resourceCommand |
resourceBindingId, command |
read, create, update, delete, or upload on a resource binding. |
openResource |
resourceBindingId |
Opens the bound resource in its editor. |
The visual editor exposes Run workflow, Cancel run, Set variable, and Toggle variable. The resource actions are set through the agent or by editing the document.
Triggers and pacing
Events fire on click (Button) or change (input widgets). A change event has a
pace:
| Pace | Fires |
|---|---|
live |
On every change. |
release |
When the control commits — slider release, input blur. Only offered on committing controls. |
debounce |
Trailing, after a quiet moment. |
Conditions
visibleWhen and disabledWhen each hold a binding, an operator, and a
literal. An unresolvable condition is treated as no condition, so a broken
binding never silently hides a widget.
| Operator | Editor label | True when |
|---|---|---|
notEmpty |
is not empty | The value is set, non-empty, and not false. |
empty |
is empty | The value is unset, empty, or false. |
eq |
equals | Equal, after coercing the literal to the value’s type. |
neq |
does not equal | Not equal. |
gt / gte |
is greater than / is at least | Numeric comparison. A non-numeric value never satisfies one. |
lt / lte |
is less than / is at most | Numeric comparison. |
contains |
contains | A string contains the substring, or an array contains the item. |
Literals are stored as strings and coerced to the shape of the value they are
compared against, so count gt "3" compares numbers and dark eq "true"
compares booleans.
Format templates
format renders {binding} tokens in place of the raw value, with one optional
filter: {op:main/out:n1|truncate:80}. An unknown filter or unresolvable
binding renders as the empty string.
| Filter | Argument | Result |
|---|---|---|
number |
digits | The value as a number, optionally fixed to N digits. |
date |
short |
Locale date-time, or locale date with short. |
upper |
— | Uppercased. |
lower |
— | Lowercased. |
join |
separator | An array joined; defaults to ", ". |
truncate |
length | Cut to N characters with an ellipsis. |
Document schema
workflow.app_doc holds an ApplicationDocument. Schema version 3 is current;
version 1 and 2 documents are lifted to 3 on load, with one implicit main
operation bound to the host workflow.
interface ApplicationDocument {
schemaVersion: number; // 3
ui: PuckData; // { root, content, zones }
operations: OperationBinding[];
resources: ResourceBinding[];
variables: VariableDeclaration[];
theme?: { id: string };
}
Operations
interface OperationBinding {
id: string;
name: string;
workflowId: string;
workflowVersion?: number; // pinned in a release, floating in a draft
inputs: Record<string, InputMapping>; // keyed by node id
outputs: Record<string, OutputMapping>; // keyed by node id
policy: "parallel" | "replace" | "queue";
timeoutMs?: number;
}
| Input mapping | Value comes from |
|---|---|
{ from: "widget" } |
The bound widget. The default when an input has no mapping. |
{ from: "variable", variableId } |
An app variable. |
{ from: "constant", value } |
A fixed value. |
{ from: "resource", resourceBindingId } |
The resource currently selected for that binding. |
| Output mapping | Value goes to |
|---|---|
{ to: "display" } |
The output slot display widgets read. |
{ to: "variable", variableId } |
An app variable, and the display slot. |
| Policy | On a second run while one is live |
|---|---|
parallel |
Start anyway. |
replace |
Cancel the live runs, then start. The default. |
queue |
Wait for them to settle, then start. |
Variables
interface VariableDeclaration {
id: string;
name: string;
type?: { type: string; optional?: boolean } | null;
default?: unknown;
scope: "instance" | "user"; // this open app, or persisted per user
persist: boolean; // only user-scoped variables may persist
}
The visual editor’s variable picker lists the Set Variable channels the graph publishes. Typed variables with a scope, default, and persistence are declared through the agent or by editing the document.
Resources
interface ResourceBinding {
id: string;
name: string;
kind: "asset" | "timeline" | "storyboard" | "sketch";
scope: { projectId?: string; fixedId?: string };
operations: ("read" | "create" | "update" | "delete")[];
}
Instance state
| Namespace | Key | Holds |
|---|---|---|
inputs |
opId:nodeId, or opId:nodeId#prop |
{ value, dirty, revision }. dirty is true once a widget rather than a default wrote it. |
outputs |
opId:nodeId |
{ value, invocationId, status, revision }. Status is empty, pending, streaming, or done. |
variables |
variable id | The current value. |
view |
componentId:prop |
Widget-local state. |
invocations |
job id | { id, operationId, status, progress, error, startedAt }. |
Invocation status is pending, running, completed, failed, or
cancelled. Streamed values append: strings concatenate, structured items
collect into a list. A message whose job_id this instance did not start is
dropped.
Agent tools
The builder agent edits the open document through these frontend tools:
| Area | Tools |
|---|---|
| Layout | ui_app_get_snapshot, ui_app_list_component_types, ui_app_add_component, ui_app_update_component, ui_app_remove_component, ui_app_select_component, ui_app_set_title |
| Operations | ui_app_list_operations, ui_app_add_operation, ui_app_update_operation, ui_app_remove_operation |
| Variables | ui_app_list_variables, ui_app_declare_variable, ui_app_update_variable, ui_app_remove_variable |
| Resources | ui_app_list_resources, ui_app_add_resource, ui_app_remove_resource |
| Bindings | ui_app_get_binding_targets |
CLI
npm run dev:nodetool -- app debug <workflow_id>
npm run dev:nodetool -- app debug workflow.json --params '{"prompt":"hi"}'
npm run dev:nodetool -- app debug <id> --no-run # static wiring check
npm run dev:nodetool -- app debug <id> --json # full AppDebugReport
# Scripted interactions
npm run dev:nodetool -- app debug <id> --interact \
'[{"set":{"key":"prompt","value":"hi"}},{"click":"Button-1"}]'
npm run dev:nodetool -- app debug <id> --interact \
'[{"set":{"key":"tone","value":"terse","operationId":"draft"}},{"run":"draft"}]'
The harness runs every declared operation, not only the first. Widgets are
clicked by component id, unique type, or unique label. The bundle lands in
nodetool-debug/app-<id>-<ts>/ with report.json, report.md, app.json,
workflow.json, and one server/run-N.messages.jsonl per triggered run.
Not simulated: visibleWhen, disabledWhen, format, and from: "resource"
inputs.
Related
- Mini Apps — concepts and runtime
- Building Mini Apps — recipes per use case
- App Builder — the editor
- CLI — the full command reference