Objective — Put the flagship OpenGenerativeUI demo on the shipped canonical rails (generateSandboxedUiOpenGenerativeUIMiddleware → activity renderer) and adopt the canonical's streaming contract, sandbox isolation, and typed host bridge — without regressing the things the demo does better (plan/narrate protocol, design system, idiomorph morphing, ResizeObserver autosize, export, templates).

The strategic point: today the demo is a parallel implementation of the concept, not a showcase of the shipped feature — it renders via a custom useComponent widget, so none of its polish exercises (or improves) the canonical pipeline. Migrating it makes the demo a reference implementation, and every demo improvement upstreamable.


Current state (verified in source)

Piece Today File
Generation entry widgetRenderer registered via useComponent — args: title, description, html (one self-contained fragment with inline <style>/<script>) apps/app/src/components/generative-ui/widget-renderer.tsx, registered in src/hooks/use-generative-ui-examples.tsx
Design system Layered CSS injected in-page: theme variables (light/dark), SVG class kit (.c-purple.c-red, .box, .arr), form-control styles widget-renderer.tsx (THEME_CSS, SVG_CLASSES_CSS, FORM_STYLES_CSS)
⚠️ DS duplication The same CSS is forked into the MCP app with a literal WARNING: Keep in sync comment apps/mcp/src/renderer.ts
Streaming UX Partial html morphs into the iframe as tool args stream, via inlined idiomorph generative-ui/idiomorph-inline.ts
Sandbox Plain iframe, document assembled in-page; window-global bridge (sendPrompt, openLink) widget-renderer.tsx, export-utils.ts
Libraries Importmap: three, gsap, d3, chart.js via esm.sh; system prompt documents them widget-renderer.tsxapps/agent/main.py
Agent LangGraph Deep Agent (create_deep_agent) + CopilotKitMiddleware; skills dir; mandatory protocol: Acknowledge → plan_visualization → Build → Narrate; hard quality bar (“ALWAYS real WebGL… NEVER fake 3D with CSS”) apps/agent/main.py, apps/agent/skills/*/SKILL.md, src/plan.py
Extras worth protecting Hybrid typed components (barChart/pieChart preferred for simple charts), template library, export-to-standalone-HTML overlay, demo gallery, HITL form generative-ui/charts/*, template-library/*, export-overlay.tsx, src/form.py

Migration target

flowchart LR
  subgraph TODAY["Today — custom rails"]
    A1["Deep Agent calls widgetRenderer(title, description, html)"] --> A2["useComponent widget-renderer.tsx"]
    A2 --> A3["In-page assembly: theme + SVG kit + form CSS + importmap + idiomorph"]
    A3 --> A4["Plain iframe + window-global bridge"]
  end
  subgraph TARGET["Target — canonical rails"]
    B1["Deep Agent calls generateSandboxedUi (ordered params)"] --> B2["OpenGenerativeUIMiddleware — streaming activity events"]
    B2 --> B3["Assembler injects the SAME design system + importmap + CSP"]
    B3 --> B4["websandbox iframe + Zod-validated sandboxFunctions"]
  end
  TODAY -.->|"migrate, keeping demo strengths"| TARGET

Workstreams

WS1 — Dogfood the canonical rails (the centerpiece)

Replace the widgetRenderer path with the canonical pipeline: enable openGenerativeUI on the runtime (apps/app/src/app/api/copilotkit/route.ts) so OpenGenerativeUIMiddleware converts the agent’s generateSandboxedUi tool call into open-generative-ui activity events, rendered by the built-in OpenGenerativeUIActivityRenderer. The agent’s system prompt + skills switch from “call widgetRenderer” to the canonical tool contract. widget-renderer.tsx’s custom iframe code retires; its design-system CSS moves to WS4.

WS2 — Adopt the ordered streaming contract

Today one big html arg morphs in as it streams — intermediate states can render unstyled or broken mid-stream. Canonical’s contract fixes this: placeholderMessagescss (preview gated on cssComplete) → html chunks live → jsFunctionsjsExpressions one-by-one. The demo gains placeholder cycling + css-first gating + the “watch each expression take effect” beat; the plan-card stays as the pre-generation step.

WS3 — Harden the sandbox

Plain iframe + window-global sendPrompt/openLinkwebsandbox separate-origin RPC + Zod-validated sandboxFunctions (openGenerativeUI.sandboxFunctions): typed sendPrompt, openLink behind an origin allowlist, every call schema-checked. Generated JS loses any path to the host window.

WS4 — Single-source the design system

Extract the theme/SVG/form CSS (and importmap) from widget-renderer.tsx and apps/mcp/src/renderer.ts into one shared workspace package consumed by both. Kills the “keep in sync” fork — and produces exactly the artifact Plan B Phase 1 wants to adopt into the canonical assembler. This workstream is the bridge between the two plans.

WS5 — Procedural channel