Architecture
Canvas layer model and ownership boundaries.
Canvas is organized as an isolated agent workspace topology:
@agent-html/kernel
-> framework-neutral protocol, policy, validation, diagnostics, and runtime catalog
@agent-html/react
-> React rendering adapter and interaction provider
agent-html/*
-> agent operating context and reusable resources
agent-html/artifacts/*
-> durable artifact composition and content
packages/cli/src/*
-> Vite middleware dev host, validation, and stylesheet entrypoints
packages/cli/src/host/*
-> sandbox host, inspector, and prompt bridge
packages/cli/src/host/styles/*
-> host chrome implementation styles that consume Canvas tokens
apps/desktop
-> project selection, session state, runtime supervision, preferences,
packaged Node, Desktop chrome, and the persisted Canvas theme snapshotEach layer owns one decision. The protocol marks stable regions and emits
instrumented state changes. The agent-html workspace provides durable
context agents can read and reuse. Artifacts compose those resources into UI.
The CLI discovers, validates, serves, and routes artifacts through Vite middleware.
The host observes and operates on rendered metadata.
Kernel rules are non-overridable. All machine-verifiable protocol, dependency,
workspace, and visual violations are blocking errors. Subjective composition
judgment remains in TASTE.md and is not represented as a machine rule.
Desktop
apps/desktop is the packaged product shell. Its React renderer owns
Workspace Home, recovery, switching, settings, and the compact frame around the
existing Host. Its Tauri layer owns canonical project selection, recents,
per-launch credentials, sidecar lifecycle, logs, capabilities, and packaging.
The selected project remains the source owner. Desktop normalizes either a
project root or its agent-html/ directory to one canonical project root.
Preferences and credentials never enter project source.
Canvas Style is the single theme authority. The Host publishes a versioned, allowlisted snapshot of its active mode, preset variables, and live draft to its Desktop parent. Desktop accepts messages only from the active runtime frame and origin, applies the snapshot to its own root, and persists the last valid snapshot globally for Workspace Home and future Canvas sessions. A Canvas host requests that snapshot before publishing its initial theme; Desktop sends it only to the active runtime frame and origin. Canvas validates and applies the bootstrap, then resumes publishing canonical theme changes. Standalone hosts retain local preference persistence.
Desktop resolves packaged runtime resources through a versioned manifest. Its
runtime supervisor is the only owner of child-process path projection, command
construction, output capture, readiness, exit classification, and shutdown.
Canonical paths stay internal; child arguments receive platform-compatible
paths at the process boundary. Workspace initialization is transactional and
publishes agent-html/ only after the template copy completes.
Desktop Runtime is an immutable, content-addressed store under the app local data directory:
runtime/
current.json
runtimes/<fingerprint>/
bin/node[.exe]
node_modules/
runtime-manifest.jsonruntime:build publishes one verified fingerprint without selecting it.
runtime:ensure single-flights the build, rechecks after the lock, and
atomically selects it while retaining the previous fingerprint for recovery.
runtime:start consumes only the selected manifest. runtime:stage copies the
selected immutable version into the packaging seed and is forbidden from the
development startup path. Cache hits do not run npm install; Canvas init
never installs project dependencies.
The manifest owns Node and CLI entries, protocol and schema versions, the dependency-lock hash, Canvas dependency names, and Canvas browser entries. The fingerprint covers runtime source, dependency closure, Node binary, target, and the runtime build recipe. Rust passes its path and runtime fingerprint to the CLI. Vite keys optimized dependencies by runtime fingerprint, workspace root, and a browser optimization plan. That plan combines manifest-owned Canvas entries with Host entries derived from production Host source and disables undeclared dependency discovery. Runtime publication never replaces a directory used by a running sidecar.
The Rust supervisor owns the full runtime process tree. Windows assigns it to a kill-on-close Job Object. Unix launches it through a new-session supervisor whose control pipe kills the process group when Desktop disappears. Graceful shutdown remains the primary path; forced cleanup is bounded and tree-wide.
agent-html runtime is the controller contract. It binds a random loopback
port, emits one JSON runtime-ready event, authenticates every request,
exposes versioned health and shutdown routes, and closes Vite, registry
watchers, and agent bridges before exit. agent-html dev remains the
unauthenticated development contract. The packaged app supplies Node and CLI
resources; projects do not supply a script, install, or runtime.
Protocol
Artifact and Block are headless protocol markers.
They own metadata, anchors, and accessibility minimums.
Artifact also owns the readable root container, with layout parameters
configured by Canvas CSS tokens instead of artifact source props. Block is
fully protocol-only: it accepts id, title, and children. It does not own
width, spacing, colors, borders, radius, shadow, cards, panels, grid structure,
or props passthrough.
@agent-html/react exports InteractionProvider,
useArtifactInteraction, useEmitArtifactStateChange,
useInstrumentedValueChange, and useInstrumentedCheckedChange. These helpers
record local control state and dispatch agent-html:state-change events.
Resources
Canvas resources live under agent-html:
AGENTS.md
package.json
components.json
tsconfig.json
styles/index.css
styles/base.css
styles/materials/*
styles/layouts/index.css
styles/layouts/composition.css
styles/internal/*
theme/*
components/ui/*
components/*.tsx
hooks/*
lib/*
types/*
assets/*
public/*
artifacts/*
artifacts/*/data/*Artifacts should consume these resources before adding local one-off helpers.
Composition
Artifacts decide block content layout explicitly:
<Artifact title="Project Brief">
<Block id="summary" title="Summary">
<Card>...</Card>
</Block>
</Artifact>Block marks an inspectable region. It is not a card, panel, or section style.
Do not put layout classes on Block; put layout inside the block content.
Host
The host renders artifacts, discovers blocks, displays validation diagnostics, overlays
block prompt actions, handles floating prompts, applies theme presets, and
loads Canvas stylesheets. Host controls use Canvas UI primitives and theme
resources through #agent-html-playground/components/ui/* and
#agent-html-playground/theme/*; the host must not define artifact visual
language.
Host workbench code is a consumer and composer of runtime pipelines. Its owner map is:
host/api
-> dev-server API contract
host/artifact
-> artifact registry consumption and artifact module runtime
host/overlay
-> rendered Block metadata measurement and prompt anchors
host/prompt
-> floating prompt host state, drafts, and block message lifecycle
host/pipeline
-> Codex/example backend adapters and prompt formatting calls
host/interaction
-> artifact state-change event snapshots for prompt context
host/preferences
-> local host preference persistence
host/theme
-> host theme mode, preset layout, draft preview, and runtime variables
host/navigation
-> sidebar command surfaceReactCanvasHostWorkbench composes those owners. It should not directly own
dev-server route semantics, workspace validation, prompt backend execution,
message store publishing, theme side effects, artifact registry refresh rules,
or Canvas protocol semantics.
Pipeline Map
This page is the authoritative Canvas pipeline map. The shared block prompt model is:
artifactEntry + blockId + implementationPath + interactionSnapshot| Pipeline | Owner | Input | Output | Forbidden dependencies | Primary tests |
|---|---|---|---|---|---|
| Workspace source | agent-html | artifact entries, split block files, local resources | portable source workspace | generated bundles, lockfiles, vendored installs | workspace-contract.test.mjs |
| Workspace file/path helpers | react-canvas/paths.mjs + react-canvas/workspace-file.mjs | root path or source file path | POSIX workspace path or text source | generic filesystem abstraction, mutation APIs, host state | pipeline-ownership-contract.test.mjs, workspace-contract.test.mjs |
| Artifact discovery | react-canvas/paths.mjs | agent-html/artifacts/*.artifact.tsx | artifact entry file paths | app shell, runtime parser, host UI | dev-host.test.mjs, pipeline-ownership-contract.test.mjs |
| Artifact entry validation | Kernel validator + CLI file adapter | artifact entry source | versioned blocking diagnostics | duplicated protocol parsing in CLI or Host | Kernel validation tests, validation.test.mjs |
| Block implementation validation | Kernel validator + CLI file adapter | agent-html/artifacts/**/*.block.tsx | versioned blocking diagnostics | raw visual classes, native primitive bypasses | Kernel validation tests, validation.test.mjs, style-ownership-contract.test.mjs |
| Workspace boundary validation | Kernel validator + react-canvas/validation.mjs | artifact and block source imports | workspace diagnostics | application imports, public-file imports | validation.test.mjs, workspace-contract.test.mjs, architecture-boundaries.test.mjs |
| Dependency ownership | Kernel runtime catalog + generated manifests | Canvas source package import | one CLI-supplied version | hand-maintained workspace dependency versions | Kernel policy tests, package-runtime-contract.test.mjs |
| Dependency resolution | dev-server/vite.mjs | source import specifier | CLI-provided runtime module | agent-html/node_modules, source-owned runtime installs | runtime-resolution-contract.test.mjs |
| Module transform and render | Vite middleware + host artifact runtime | artifact entry file path | mounted artifact DOM with HMR-capable module graph | application shell and Host internals | dev-host.test.mjs, artifact-runtime.test.ts |
| Public/static asset serving | dev-server/routes.mjs + host/api URL helpers | public asset path or proxied font URL | served asset response or host URL | direct Host endpoint strings outside host/api | dev-host.test.mjs, routes.test.mjs, api.test.ts, architecture-boundaries.test.mjs |
| Artifact registry consumption | host/artifact/use-artifact-registry.ts | artifact registry response and pending artifact path | active artifact, diagnostics, registry refresh operations | dev-server route implementation, workspace validation | app.test.tsx, pipeline-ownership-contract.test.mjs |
| Block overlay | packages/react + host overlay | rendered data-agent-html-block-* metadata | hover highlight and prompt target | artifact source mutation, host APIs in artifacts | block-overlay.test.ts, index.test.tsx |
| Block implementation lookup | dev server + host API | artifactEntry, blockId | implementationPath | selected source payloads, implementation source payloads | block-implementation.test.mjs, dev-host.test.mjs |
| Block prompt | react-canvas/prompt.mjs | artifactEntry, blockId, optional implementationPath, host interactionSnapshot | minimal prompt packet | selected source payloads, implementation source payloads | prompt.test.mjs |
| Create artifact | host/pipeline + react-canvas/prompt.mjs | create request and generated artifact file path | Codex/example turn request | dev-server implementation, artifact source mutation in Host | pipeline.test.ts, app.test.tsx |
| Validation fix | host/pipeline + react-canvas/prompt.mjs | blocking diagnostics for one artifact | Codex/example turn request | warnings-only prompt payloads, host UI state payloads | pipeline.test.ts, prompt.test.mjs |
| Interaction state | packages/react event protocol + host store | artifact-local control changes | host prompt interaction snapshot | filesystem, shell, MCP, privileged host APIs in artifact code | index.test.tsx, interaction-store.test.ts, prompt.test.mjs |
| Preferences persistence | host/preferences | local host preference state | persisted host preference patch | artifact source state, dev-server state | canvas-host-preferences.test.ts, use-canvas-host-preferences.test.ts |
| CSS/theme | agent-html/styles + package host styles + host theme owner | CSS tokens, content classes, theme presets, host chrome CSS | stylesheet and runtime variables | artifact-owned root chrome, sidebar token overrides, host chrome implementation in artifact context | style-ownership-contract.test.mjs, style-resource-contract.test.mjs, theme-preset.test.ts, theme-layout.test.ts |
| Runtime control | dev-server/runtime-session.mjs + runtime-command.mjs | per-launch credential, canonical root, pipeline | ready event, authenticated Host, health, graceful shutdown | persisted credentials, fixed ports, project Node | runtime-session.test.mjs, runtime-server.test.mjs |
| Desktop session | apps/desktop/src-tauri + apps/desktop/src/session.ts | selected folder, initialization choice, runtime manifest, preferences | one supervised workspace runtime and typed recovery errors | artifact layout, Host route semantics, project preferences, multiple active runtimes | Desktop Vitest, Cargo tests, runtime smoke, packaged E2E |
| Desktop visual ownership | apps/desktop/src/styles.css + Desktop primitives | semantic and --canvas-desktop-* tokens | borderless product chrome | workspace UI imports, raw colors, palette utilities, local visual scales | apps/desktop/src/style-ownership.test.ts |
Dev server routes are orchestration only. They are grouped into explicit runtime adapter pipelines:
host-shell
runtime-module
styles-and-assets
public-asset
artifact-registry-and-validation-report
artifact-source-mutation
block-lookup
codex-bridge
runtime-controlThe dev server owns server lifecycle, route dispatch, Vite runtime creation, dependency supply, and asset serving. Host shell, artifact module, stylesheet, public asset, artifact registry and validation report, artifact mutation, block lookup, and Codex bridge requests enter through those named pipelines. The dev server delegates host and artifact module transforms to Vite.
It does not own protocol semantics, host interaction state, artifact visual
language, or block prompt meaning. Those remain owned by @agent-html/react,
the host interaction store, Canvas styles/resources, and the prompt pipeline.
Boundary Governance
Canvas boundary checks follow the layer topology:
packages/kernel
-> framework-neutral contract and policy; no React, filesystem, Host,
workspace, docs, app, or archive imports
packages/react
-> React adapter only; imports Kernel but no host, workspace, docs, app,
or archive code
packages/cli/src/react-canvas
-> discovery, validation, prompt, and workspace path orchestration
-> no host UI, artifact source, Canvas UI, theme, docs, app, or archive imports
packages/cli/src/host
-> host inspector chrome and pipeline calls
-> Canvas UI/theme only through #agent-html-playground/components/ui/* and
#agent-html-playground/theme/*
agent-html/artifacts
-> artifact source
-> no host, app, docs, archive, public-file imports, or internal style importspackages/cli/src/react-canvas/architecture-boundaries.test.mjs owns the
topology checks. Use it as the executable boundary matrix before adding new
validation behavior. Workspace source constraints live in
workspace-contract.test.mjs, package/runtime ownership lives in
package-runtime-contract.test.mjs, pipeline ownership lives in
pipeline-ownership-contract.test.mjs, archive separation lives in
archive-runtime-boundary-contract.test.mjs, and style ownership lives in
style-ownership-contract.test.mjs plus style-resource-contract.test.mjs.
style-ownership-contract.test.mjs also records the current fixed-format
artifact layout exceptions for media, map, and carousel blocks. Those
exceptions are not a new styling surface; new artifact code should prefer Canvas
public composition classes and shrink that list when equivalent composition
classes exist.
Dependency Resolution
agent-html/package.json records source dependency metadata for local Canvas
resources. It is not an installation target. The CLI package provides runtime
dependencies. The dev host is the only mapping layer between source imports and
runtime modules: it resolves workspace imports through CLI-owned dependencies,
exact package aliases, Vite import-entry resolution, and explicit
optimizeDeps.
React and React DOM stay pinned through dedicated aliases so the host and artifacts share one renderer instance. Named exports used by workspace source must be present on the resolved runtime entries; this catches ESM/CJS mismatch before browser module loading fails.
Host browser dependencies are a separate CLI-owned contract. The dev server derives its entries from production Host imports, requires their packages in the CLI production manifest, and combines them with Canvas entries only for Vite optimization. Host entries do not expand the dependency surface available to Canvas source. The combined plan digest owns cache identity and completeness, so changing an entry cannot reuse a cache prepared for a different browser graph.
The dependency contract is executable: workspace-contract.test.mjs checks
source-only workspace files, source metadata, and forbidden vendored installs.
package-runtime-contract.test.mjs checks CLI runtime ownership.
runtime-resolution-contract.test.mjs checks Vite dependency aliases, ESM
import entries, runtime exports, React singleton resolution, explicit
optimizeDeps, and filesystem allow-list boundaries. routes.test.mjs checks
route pipeline ownership. dev-host.test.mjs checks artifact rendering and
fresh-project startup through the real dev host.
Diagnostic Categories
Canvas validation returns one versioned diagnostic list. protocol, style,
and workspace diagnostics are produced from source; dependency and
manifest diagnostics enforce the Kernel runtime catalog. Every diagnostic is
blocking and identifies its code, file, location, message, policy version, and
optional fix suggestion. The dev host displays the same contract returned by
the CLI validation pipeline.
Interaction Source Of Truth
@agent-html/react owns portable instrumentation and the event protocol. Its
artifact-local snapshot helps controls compose locally, but it is not the
prompt context source of truth.
The host interaction store owns prompt context. It records
agent-html:state-change events by artifactEntry + blockId, compacts them,
and passes the resulting interactionSnapshot into the block prompt pipeline.