From c7a79a88e858ded5b69bdbffe105b4198a2179a5 Mon Sep 17 00:00:00 2001 From: Ashish Huddar Date: Sat, 9 May 2026 22:42:26 +0530 Subject: [PATCH] docs(canvases): document canvas-renderer plugin slot for v0.4 (true OSS extensibility) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous "Tier 3: add a renderer needs a core PR" framing wasn't honest open-source extensibility — it was just a permissive license. This commit reframes Tier 3 as a renderer plugin model: - Anyone can ship `@aoagents/ao-plugin-canvas-{name}` packages. - Plugin declares own canvasType id, payload schema (Zod), and React renderer. - AO's build-time plugin registry walks installed packages, generates a TypeScript file extending the CanvasArtifact discriminated union, and the web build bundles the renderers. - Trust boundary is `npm install` — same as every other AO plugin. - No core PR. No runtime code injection. Build-time only. Adds a new Tier 4: promote a popular renderer plugin into core's built-in set when the ecosystem has converged on it. The bar is "this is now standard infrastructure", not "this is a new idea". Updates the contract from "constrained UI in core" to three rules: 1. Anyone supplies any data, in any supported type. 2. Anyone ships a new type via plugin (npm trust). 3. Nobody dynamically loads JS at runtime. The non-goal list now distinguishes "dynamically-loaded React" (rejected) from "build-time-bundled renderer plugins" (the v0.4 unlock). Mirrors the change across docs/canvases.md (source of truth), docs/canvases-launch.{md,html} (showcase), and docs/canvases-feature.html (full writeup). No code change in v0.1. The plugin slot lands in v0.4 with its own design pass — this commit just documents the direction. Co-Authored-By: Claude Opus 4.7 (1M context) --- docs/canvases-feature.html | 58 ++++++++++++++++++++++++----------- docs/canvases-launch.html | 34 ++++++++++++--------- docs/canvases-launch.md | 37 +++++++++++++++-------- docs/canvases.md | 62 ++++++++++++++++++++++++++++++++++---- 4 files changed, 140 insertions(+), 51 deletions(-) diff --git a/docs/canvases-feature.html b/docs/canvases-feature.html index a90f7a94b..ca665a8fe 100644 --- a/docs/canvases-feature.html +++ b/docs/canvases-feature.html @@ -189,7 +189,7 @@ v0.1 — file-based + synthesized git diff 4 renderer types 13 codex review passes - no new plugin slot + v0.4: renderer plugins
@@ -435,24 +435,47 @@
TIER 3 -

Add a new CanvasType

- ~half a day · core PR required +

Ship a renderer plugin

+ ~half a day · v0.4 · no core PR
-

For data that genuinely doesn't fit the 4 existing renderers (flame graphs, Gantt charts, network topologies):

-
    -
  1. Extend the CanvasArtifact discriminated union in types.ts.
  2. -
  3. Extend the matching Zod schema in canvas-schema.ts.
  4. -
  5. Write a Canvas{NewType}.tsx renderer in packages/web/src/components/.
  6. -
  7. Add a case to the switch in CanvasRail.tsx.
  8. -
  9. Tests + docs paragraph.
  10. -
-

Gated behind a core PR because the renderer ships in the supervisor dashboard. Worth it once at least two real callers need the same shape.

+

For data that genuinely doesn't fit the 4 existing renderers (flame graphs, Gantt charts, network topologies), publish your own renderer as @aoagents/ao-plugin-canvas-{name} — the same package convention as every other AO plugin. The plugin declares its own canvasType id, payload schema (Zod), and React component.

+

At AO build time the plugin registry walks installed @aoagents/ao-plugin-canvas-* packages, generates a TypeScript file that imports each renderer plus extends the CanvasArtifact discriminated union, and the web build bundles them. The dashboard's CanvasRail switch dispatches to the plugin's renderer when it sees the new type.

+
@aoagents/ao-plugin-canvas-flamegraph/
+├── package.json          # name: @aoagents/ao-plugin-canvas-flamegraph
+├── src/
+│   ├── index.ts          # manifest + payload schema (Zod)
+│   └── renderer.tsx      # React component, default export
+
// src/index.ts
+import { z } from "zod";
+export const manifest = {
+  name: "flamegraph",
+  slot: "canvas-renderer" as const,
+  canvasType: "flamegraph",         // new CanvasType id
+  version: "0.1.0",
+};
+export const payloadSchema = z.object({
+  samples: z.array(z.object({
+    name: z.string(),
+    value: z.number(),
+  })).max(10_000),
+  unit: z.enum(["ms", "samples"]),
+});
+

No core PR needed. Trust boundary: npm install. Same trust check as adding any other AO plugin. No runtime code injection — plugins are bundled at build time, not loaded dynamically. v0.1 ships the 4 built-ins; v0.4 (queued) ships the plugin loader.

+
+ +
+
+ TIER 4 +

Promote a renderer plugin into core

+ rare · core PR · ecosystem consensus +
+

Once a renderer plugin has multiple production callers and the type is genuinely general (not specific to your stack), propose promoting it into core's built-in set via PR. The bar here is "this is now standard infrastructure", not "this is a new idea worth trying". Tier 3 is for trying ideas; Tier 4 is for blessing what already worked.

- The contract — expressive data, constrained UI. Anyone supplies any data in any supported type (no permission needed). New types come from PRs (UI in core needs review). No third party ships JS into the supervisor dashboard (never planned). Keeps install one-step, the renderer set consistent across every AO instance, and the supervisor sandbox-safe. + The contract — three rules across all four tiers. Anyone supplies any data, in any supported type (no permission needed). Anyone ships a new type via plugin (same npm install trust as any other AO plugin). Nobody dynamically loads JS at runtime (no remote code, no agent-emitted React). This keeps the trust boundary at install time where it belongs, the renderer set consistent within a given AO build, and the supervisor sandbox-safe.

6. Built with paranoia

@@ -549,19 +572,20 @@ EOF VersionAddsStatus v0.1File reader, GET endpoint, 4 renderers, synthesized git-diff, right-rail with auto-expand, 5s REST pollShipped - v0.2CanvasProducer.listCanvases invoked on agent / scm / tracker pluginsQueued + v0.2CanvasProducer.listCanvases invoked on agent / scm / tracker plugins (Tier 2 above)Queued v0.3Mux WebSocket topic for live updates, replacing 5s pollQueued + v0.4canvas-renderer plugin slot (Tier 3 above). Build-time bundling of @aoagents/ao-plugin-canvas-*, dynamic discriminated-union extension, type-id collision detectionQueued — the OSS extensibility unlock MobileBottom-sheet or full-screen takeover for canvases on small viewportsDeferred - Custom UISandboxed iframe escape hatch, build-time allowlisted renderer packagesOnly if justified + Sandboxed iframeRuntime-isolated escape hatch for genuinely-untrusted content (e.g. third-party HTML emitted by an agent we can't trust)Only if a real use case shows up

10. Deliberate non-goals

    -
  • Custom React renderers from third-party plugins. Security boundary; never planned. New renderer types come from core PRs.
  • +
  • Dynamically-loaded React from third-party sources at runtime. URL-injected, agent-emitted, or remote-loaded renderers — never planned. v0.4's plugin slot bundles renderers at build time only, with the same npm install trust check as every other AO plugin. That's the OSS extensibility line: trust at install, not at runtime.
  • Write APIs from the dashboard back into canvases. Read-only surface in v0.1. Bidirectional adds a whole new failure mode (auth, ordering, conflict).
  • Action buttons that mutate session state. A canvas displays; a button-click does. The latter is a different feature with different security implications.
  • -
  • A 9th plugin slot for canvases. Considered and rejected — canvas is product output, not infrastructure. Existing plugins opt-in via CanvasProducer.
  • +
  • A 9th plugin slot for canvas producers. Considered and rejected — producing a canvas is session output, not infrastructure. Existing plugins opt-in via CanvasProducer. (v0.4's canvas-renderer slot is different: that's UI infrastructure, where a slot makes sense.)