docs/refactor/canvas.md
Canvas is low-use and experimental. Treat it as a bundled plugin, not a core feature. Core may keep generic gateway, node, HTTP, auth, config, and native-client plumbing, but Canvas-specific behavior should live under extensions/canvas.
Move Canvas ownership to extensions/canvas while preserving the current paired-node behavior:
canvas tool is registered by the Canvas pluginDone:
extensions/canvas.extensions/canvas/openclaw.plugin.json.canvas tool from src/agents/tools/canvas-tool.ts to extensions/canvas/src/tool.ts.createCanvasTool from src/agents/openclaw-tools.ts.src/canvas-host to extensions/canvas/src/host.extensions/canvas/runtime-api.ts as the plugin-owned compatibility barrel for tests, packaging, and external public Canvas helpers.src/gateway/canvas-documents.ts to extensions/canvas/src/documents.ts.extensions/canvas/src/cli.ts.extensions/canvas/src.nodeInvokePolicies.plugins.entries.canvas.config.host.api.registerNodeCliFeature(...) so Canvas can declare openclaw nodes canvas as a plugin-owned node feature without manually spelling the parent command path.src/** imports of extensions/canvas/runtime-api.js.apps/shared/OpenClawKit/Tools/CanvasA2UI to extensions/canvas/src/host/a2ui-app.extensions/canvas/scripts and replaced root build wiring with generic bundled-plugin asset hooks.canvasHost config alias.openclaw doctor --fix rewrites old canvasHost configs into plugins.entries.canvas.config.host.pluginSurfaceUrls.canvas plus node.pluginSurface.refresh; the deprecated canvasHostUrl, canvasCapability, and node.canvas.capability.refresh path is intentionally unsupported in this experimental refactor.docs/plugins/reference/canvas.md.Known remaining core-owned Canvas surfaces:
apps/ still intentionally consume the Canvas plugin surfaceapps/dist/canvas-host/a2ui for backwards-compatible runtime lookup, but the copy step is now plugin-ownedextensions/canvas should own:
Core should own only generic seams:
Native apps may keep Canvas command handlers as clients of the protocol. They are not the plugin runtime owner.
plugins.entries.canvas.config.host as the plugin-owned config surface.Before calling the refactor complete:
rg "src/canvas-host|../canvas-host" returns no live source imports.rg "canvas-tool|createCanvasTool" src finds no core-owned Canvas tool implementation.rg "canvas.present|canvas.snapshot|canvas.a2ui" src/gateway finds no hardcoded allowlist defaults outside generic plugin policy tests.rg "extensions/canvas/runtime-api" src --glob '!**/*.test.ts' is empty.rg "canvas-documents" src is empty.rg "registerNodesCanvasCommands|nodes-canvas" src is empty; the Canvas plugin registers openclaw nodes canvas through nested plugin CLI metadata.rg "createCanvasHostHandler|handleA2uiHttpRequest" src/gateway returns no gateway runtime ownership.rg "apps/shared/OpenClawKit/Tools/CanvasA2UI|canvas-a2ui-copy|extensions/canvas/src/host/a2ui" scripts .github package.json finds only compatibility wrappers or plugin-owned paths.pnpm plugins:inventory:check passes.pnpm plugin-sdk:api:check passes, or generated API baselines are intentionally updated and reviewed.Use targeted local checks while iterating:
pnpm test extensions/canvas/src/host/server.test.ts extensions/canvas/src/host/server.state-dir.test.ts extensions/canvas/src/host/file-resolver.test.ts
pnpm test src/gateway/server.plugin-node-capability-auth.test.ts src/gateway/server-import-boundary.test.ts
pnpm test extensions/canvas/src/config-migration.test.ts src/commands/doctor-legacy-config.migrations.test.ts
pnpm test test/scripts/changed-lanes.test.ts test/scripts/build-all.test.ts extensions/canvas/scripts/bundle-a2ui.test.ts test/scripts/bundled-plugin-assets.test.ts extensions/canvas/scripts/copy-a2ui.test.ts src/infra/run-node.test.ts
pnpm tsgo:extensions
pnpm plugins:inventory:check
pnpm plugin-sdk:api:check
Run pnpm build before push if runtime barrel, lazy import, packaging, or published plugin surfaces change.