joule-ui takes in OpenUI Lang 1.0 the way it takes in A2UI: a model writes a surface as a program, one widget a line, and joule-ui-lang builds a widget tree against the host's registry as the program streams. After every chunk a snapshot holds the tree as far as it can be built, each widget checked by joule-ui's own validate, and every error with its source line and reason code. State, queries, operators, conditionals, built-in functions and sequences of actions are parsed and refused not-declarative. A canonical writer turns a tree back into the language; it parses back to the same tree. - joule-ui-core: WidgetSchema::positional, the order a positional call passes props in (SPEC §3); absent from the wire form when undeclared. - joule-ui-interaction: begin_interaction and Pending::finish, the lifecycle's three phases in two steps for a host whose handler is asynchronous (SPEC §6); run_interaction is built on them and seals what it sealed before. - conformance/lang/v1: L1 statements, L2 ingest, L3 streaming, L4 the canonical form, L5 refusals, with an independent Python verifier written from §11. - docs/create.md: the design of Create. Co-Authored-By: Claude Opus 5.5 <[email protected]>
80 lines
4.1 KiB
Markdown
80 lines
4.1 KiB
Markdown
# joule-ui
|
||
|
||
A typed declarative protocol for **agent-emitted user interfaces**.
|
||
|
||
joule-ui is the sibling to [JouleClaw](../jouleclaw/) for the moment when an
|
||
agent's resolution has to become something a human can see and interact
|
||
with. The agent emits a **widget tree** — `(name, props, children)`,
|
||
recursively — against a **widget registry** the host has published. The
|
||
host renders. No React in the spec, no HTML over the wire, no agent
|
||
holding the renderer.
|
||
|
||
The shape the field has converged on for this is *declarative components
|
||
against a registry*: agents are far more reliable emitting structured
|
||
data than emitting code, and renderers are far easier to harden when they
|
||
own their own widgets. joule-ui pins that shape as a protocol; the
|
||
renderer is product surface, owned by the consumer.
|
||
|
||
## What ships in v1
|
||
|
||
- `joule-ui-rs/crates/joule-ui-core/` — the protocol types (`Widget`,
|
||
`PropValue`, `WidgetSchema`, `Registry`) and the recursive validator.
|
||
Pure Rust, no IO, JSON-serialisable.
|
||
|
||
- `joule-ui-rs/crates/joule-ui-interaction/` (**v0.2**) — the layer no
|
||
other UI protocol carries: when a human *or an agent* fires an
|
||
`Action`, the invocation is an **accountable event**. Every fire names
|
||
the principal that made it — a `did:key`, the same whether a person or
|
||
an agent (the protocol draws no distinction; both are traced).
|
||
`run_interaction` authorizes it against a capability *before it runs*
|
||
(a denied action's handler never executes), meters it in joules,
|
||
charges its cost (a reading or an estimate, and which), and an
|
||
`InteractionLog` seals it as the next
|
||
link of a **hash-chained** receipt. The actor is bound into the signed
|
||
core, so an interaction cannot be re-attributed without breaking the
|
||
chain. A joule-ui session becomes a tamper-evident ledger of *who fired
|
||
it, what it computed, and what it cost* — verifiable by anyone holding
|
||
the host's key. The metering (Sandbox `PowerMeter`) and
|
||
the capability (JCP grant) are **recognized edges**; the receipt, the
|
||
chain, and the lifecycle are joule-ui's. See [`SPEC.md`](SPEC.md) §5–§7
|
||
and `examples/metered_surface.rs` for the full loop.
|
||
|
||
- `joule-ui-rs/crates/joule-ui-lang/` (**v0.3**) — the line language: a
|
||
model writes a surface as a short program, one widget a line
|
||
(OpenUI Lang 1.0; see [`NOTICE`](NOTICE)), and joule-ui takes it in as it
|
||
streams. After every chunk a snapshot holds the tree as far as it can be
|
||
built, each widget validated against the registry, and every error with its
|
||
line and reason; a canonical writer turns a tree back into the language.
|
||
State, queries, expressions and sequences of actions are refused
|
||
`not-declarative`: joule-ui carries no behaviour on the wire. See
|
||
[`SPEC.md`](SPEC.md) §11, the pack in [`conformance/lang/`](conformance/lang/)
|
||
with its independent verifier, and [`docs/create.md`](docs/create.md), the
|
||
design of Create, the page that builds a WAI app as a model describes it.
|
||
|
||
The surface layer makes joule-ui a peer of the declarative-UI protocols
|
||
(A2UI, MCP Apps). The interaction layer makes it the only one you can
|
||
hand to an auditor: a UI whose every interaction is energy-accounted and
|
||
sealed, by construction.
|
||
|
||
What is **not** here:
|
||
|
||
- A renderer. The renderer is owned by the host (browser app, native
|
||
shell, terminal UI). joule-ui validates the surface and seals the
|
||
interaction; it does not paint pixels.
|
||
- A transport. The validated spec and the interaction stream ride MCP,
|
||
REST, SSE, gRPC, Web MoQ — any bytes-out protocol. Transport-agnostic.
|
||
- A "model emits React" path. The agent emits a declarative tree against
|
||
the registry; arbitrary code emission is out of scope.
|
||
|
||
## Why a sibling, not inside JouleClaw
|
||
|
||
The spec layer is protocol — small, stable, conformant. The renderer
|
||
registry is product surface — many of them, evolving fast. Splitting
|
||
them protects the *tools for use* line: JouleClaw stays the runtime
|
||
substrate; joule-ui is the UI protocol; consumers ship their own
|
||
widgets.
|
||
|
||
## Licence
|
||
|
||
Specification text and protocol document: CC-BY-4.0 (see [`LICENSE`](LICENSE)).
|
||
Reference implementation in `joule-ui-rs/`: Apache-2.0 (see [`joule-ui-rs/LICENSE`](joule-ui-rs/LICENSE)).
|