32 KiB
id, title, status, system, owners, created, last_updated, requirements
| id | title | status | system | owners | created | last_updated | requirements | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| canvases-agent-authored-web-apps-design | Agent-authored web-app canvases system design | draft | canvases |
|
2026-08-26 | 2026-09-19 |
|
Agent-authored web-app canvases system design
Purpose and boundaries
The Canvases system owns the lifecycle that turns an agent-authored task application into a workspace application. It owns task creation context, promotion, release selection, editing sessions, discovery, and canvas host surfaces.
The Plugins system owns package validation, browser trust, data access, state, events, grants, and runtime tokens. This design uses the plugin web-application contract.
See declarative canvas and marketplace sharing.
Requirement mapping
| Requirement | Design sections |
|---|---|
REQ-CANVASES-AGENT-WEB-APPS-001 |
Agent authoring lifecycle, Agent tools |
REQ-CANVASES-AGENT-WEB-APPS-002 |
Canvas model, Publish and rollback |
REQ-CANVASES-AGENT-WEB-APPS-003 |
Promotion and permissions |
REQ-CANVASES-AGENT-WEB-APPS-004 |
Quick Chat editing |
REQ-CANVASES-AGENT-WEB-APPS-005 |
Desktop information architecture |
REQ-CANVASES-AGENT-WEB-APPS-006 |
Mobile design contract |
REQ-CANVASES-AGENT-WEB-APPS-007 |
Host state and recovery, Observability |
REQ-CANVASES-AGENT-WEB-APPS-008 |
Authoring admission and limits |
REQ-CANVASES-AGENT-WEB-APPS-009 |
Guided canvas task launch |
System relationship
Task or Quick Chat agent
|
| create, inspect, publish
v
Canvas lifecycle service
|
| owns scope and source lineage
v
Scoped plugin instance --------------+
| |
| active immutable release | effective grants
v v
Sandboxed web application <---- Plugin web runtime
| |
| HTTP + Server-Sent Events | Host data and state services
+-----------------------------+
|
v
Tasks, workflows, and events
Canvas code does not call a canvas-specific JavaScript object. It uses the standard web protocol that the Plugins system owns.
Implementation baseline and feature gate
PR #3061's declarative implementation is superseded, not a prerequisite.
The runtime flag identity is features.canvases. Its environment variable is
KANDEV_FEATURES_CANVASES. The first implementation sets prod, dev, and
e2e profile defaults to false. Focused development and end-to-end tests set
the environment variable explicitly.
The flag requires a restart because it changes MCP tool registration and backend composition. When the flag is off, Kandev does not register backend canvas entry points. These entry points include MCP, HTTP, WebSocket, SSE, background workers, and boot data. The frontend does not register canvas routes, navigation, or settings surfaces. Database migrations can run while the feature is off. No canvas operation can read or change canvas data.
Canvas model
Use a small lifecycle record instead of declarative canvas blocks.
canvases
idplugin_instance_idworkspace_id- nullable
task_idwhile task-scoped - nullable
origin_task_idfor promoted-canvas provenance titlecreated_by_session_id- nullable
promoted_by_user_id - nullable
promoted_at - nullable
archived_at created_atandupdated_at
The plugin instance owns scope_kind, active release, status, and grants. The
canvas service is the only service that changes the task or workspace scope of
a canvas instance.
The canvas and plugin instance have a one-to-one relationship. Canvas removal removes the plugin instance, runtime tokens, grants, state, pending releases, and retained artifacts through the plugin lifecycle service.
Task removal removes each task canvas in the same cleanup flow. A promoted
canvas has no current task_id, so task cleanup preserves it. The
origin_task_id can become null after task removal.
Workspace removal removes task and promoted canvases, plugin instances, grants, state, pending releases, and runtime capabilities. The workspace delete transaction records artifact-cleanup jobs before it removes release ownership. The plugin cleanup worker removes retained files after commit and retries after restart.
Canvas state machine
draft
| first valid release
v
task_active ---- archive ----> archived
| promote |
v | restore
workspace_active <--------------+
|
+---- edit draft ---- publish ----> workspace_active
|
+---- remove ----------------------> removed
A pending release does not change the canvas state. It becomes active after a permission approval or disappears after replacement or removal.
There is no workspace-to-task demotion in the first delivery.
Authoring admission and limits
The canvas service enforces these initial limits before it creates or restores a canvas:
- 10 non-removed task canvases for one task
- 100 non-removed canvases for one workspace across task and workspace scopes
Archived canvases count toward both limits. Create and restore use the same repository transaction and lock as the count. Concurrent operations cannot admit more than the limit.
The publish service permits 10 attempts for one agent session in a rolling five-minute window. One canvas can have only one publish in progress. Publish admission reserves plugin artifact bytes before source transfer and validation. The plugin runtime also enforces its workspace and installation storage limits.
A limit error returns a stable safe code. It does not change the active release, grants, state, archive status, or retained release set.
Agent authoring lifecycle
The normal flow starts in a task conversation:
- The user asks the task agent to create a canvas.
- The agent calls
create_canvas_kandevwith a title and application summary. - The backend derives the task, workspace, session, and user from MCP context.
- The service creates a task-scoped canvas and inactive plugin instance.
- The tool returns the canvas ID, source directory, manifest scaffold, current permission ceiling, and the command to read the bundled authoring skill.
- The agent reads the version-matched authoring skill through Canvas MCP.
- The agent writes source and built assets under the returned directory.
- The agent calls
publish_canvas_kandevwith the canvas ID and source path. - Kandev reads that directory through the trusted task execution file API.
- The Plugins service validates and stores an immutable release.
- The canvas activates when its permissions fit existing grants or its recorded initial creation authority.
- The task client receives a lifecycle event and opens the new canvas.
The source directory is workspace-relative and scoped to the current execution. The publish service rejects traversal, links, another canvas identity, and a directory outside the assigned canvas root.
The backend must use the execution file API for local, Docker, and remote executors. It must not assume that the agent workspace exists on the Kandev host filesystem.
Agentctl adds one authenticated streaming tar endpoint for a workspace-relative root. The canvas service supplies only the assigned canvas source root. The endpoint rejects absolute paths, traversal, links, devices, and files outside that root. It permits at most 512 files, 25 MiB of file data, and 30 MiB on the wire. Cancellation stops traversal and closes the stream. The backend sends the stream directly to the plugin validator and does not make one JSON request for each file.
Guided canvas task launch
The guided canvas task launch design owns creation entry points, the editable preset, saved-prompt preparation, and authoring prompt guidance. It maps the launch requirement and its desktop and phone outcomes. The runtime authoring bundle remains below.
Agent authoring guidance
Discovery and creation prompts
The shared task prompt uses the
agent discovery contract.
When the resolved session profile includes CapabilityCanvas, it also includes
this compact instruction:
For a requested Kandev canvas, discover
create_canvas_kandev,read_canvas_authoring_skill_kandev, andpublish_canvas_kandev. Create the draft in Kandev before writing application files. Read the authoring skill once and edit only inside the returned source directory. Publish through MCP and report the returned release status. If publication is unsuccessful, report the failure and do not claim that the canvas is published. Files or a successful local build do not create a published Kandev canvas.
The optional section is absent when the resolved profile lacks canvas tools. This covers canvas requests inside ordinary task conversations, independently of the guided task preset.
CanvasTaskCreateLauncher retains the visible, editable
canvases:createCanvasTaskPrompt preset in the normal TaskCreateDialog.
The locale catalog owns the exact wording. It names discovery, creation,
assigned-root authoring, live data, the single core skill read, publication,
and honest outcome reporting. It explains permission review and promotion
when required, and does not equate local files or a build with publication.
The existing selected question capability governs any clarification. The preset cannot grant a question tool to an autopilot session that lacks one. An application goal already supplied by the user needs no repeated interview.
English, Portuguese, and Simplified Chinese catalogs receive equivalent wording. Traditional Chinese catalogs use the repository generator. Tool names remain literal protocol identifiers. The pseudo-locale uses its generator.
The nearest mobile exemplar is the existing full-screen TaskCreateDialog
opened from workspace Canvases settings. This is a content change within that
surface. Its scroll owner, touch controls, and navigation remain unchanged.
Focused desktop and mobile tests cover the longer editable prompt and its
submitted value. Tests inspect the real catalog value before replacing any
description with mock-agent commands.
Bundled authoring skill
Kandev bundles a read-only kandev-canvas-authoring system skill in a canvas
embed that is separate from the Office skill embed. At startup, Kandev writes
the current skill and its supporting files to
<kandev-home>/system-skills/kandev-canvas-authoring/. A Kandev update replaces
these Kandev-owned files with the version from the new binary. It does not
change user-owned skills.
Kanban task agents do not receive this skill in .agents/skills,
.claude/skills, or another task-workspace skill directory. The Canvas MCP
server reads the canonical files from the Kandev home directory and returns
them through read_canvas_authoring_skill_kandev. A call without a path returns
one compact core bundle. The bundle contains the complete normal workflow,
manifest contract, browser protocol summary, appearance rules, minimal
scaffold, and exact supporting-file inventory. The optional path input reads
one detailed reference from that inventory.
The handler rejects absolute paths, traversal, links, and files outside the inventory. It does not return a Kandev host path to the agent. This rule keeps local, Docker, and remote executors on one contract without copying the skill into the task workspace.
create_canvas_kandev materializes the version-matched minimal scaffold in the
assigned source directory. It returns the system-skill slug, version, exact
scaffold inventory, and an instruction to make at most one core skill-read
call. Canvas edit Quick Chat prompts give the same instruction. After the core
read, agents use their native file tools for the scaffold and request a
detailed reference only when the core bundle directs them to it.
Office role and operating skills keep their existing per-task workspace
deployment. The canvas-authoring read path does not change Office skill
selection, injection, or synchronization. Office skill discovery and
SyncSystemSkills do not inspect the canvas system-skill directory.
The canvas-authoring skill tells the agent to:
- use the generated manifest and source directory
- bundle executable frontend dependencies
- add a responsive viewport declaration
- use relative
./_kandev/v1data, state, action, and event paths - keep Kandev domain data as the source of truth
- store only application-specific shared state in instance state
- use memory for temporary values; use instance state for shared values and do not depend on host browser-storage keys
- publish after local build checks
- read validation diagnostics and correct rejected releases
- use semantic appearance variables and apply live host appearance messages
Supporting files include the browser API, manifest, data and state, events and recovery, security, and UI pattern references. The scaffold includes a minimal no-build HTML application and bundled Kandev-compatible canvas styles. It can also include optional build examples without making Node a host-runtime dependency.
Agent tools
Replace the declarative block tools with these task-aware tools:
list_canvases_kandevread_canvas_authoring_skill_kandevcreate_canvas_kandevget_canvas_kandevpublish_canvas_kandevget_canvas_state_kandevset_canvas_state_kandev
Task agents can create and publish task canvases for their trusted task. An edit Quick Chat session can publish only the canvas that started that session.
State tools derive the plugin instance from the canvas. They use the plugin state service and revision preconditions. They do not accept another user, workspace, task, session, or plugin instance.
Agents cannot promote, approve permissions, roll back, archive, restore, or remove a canvas. Agents use existing Kandev tools to change tasks and workflows that a canvas displays.
Publish and rollback
The publish command resolves the source through trusted session context. It streams a bounded package to the plugin release validator.
If the declaration does not exceed existing grants, the release activates in
one transaction. The service publishes canvas.release.activated after commit.
If the declaration needs more permission, the release gets
pending_permission status. The service publishes
canvas.release.permission_required. The active release remains unchanged.
Recorded owner-authorized first publication uses the creation authority transaction instead. The generic publish and manual review paths do not infer that authority.
Rollback selects the retained prior valid release. It does not restore old grants that the user revoked. If the current grants do not cover the old release, rollback needs a permission review.
Promotion and permissions
Promotion is a human-only canvas operation. The backend authorizes the current user for the canvas workspace before it returns promotion data.
The promotion dialog shows:
- the canvas title and origin task
- the active release and source actor
- each Kandev data read
- each Kandev data write
- each event subject
- shared state access
- each exact external network origin
- the change from task scope to workspace scope
- the new workspace navigation placement
The user can cancel without a state change. Confirmation creates the approved workspace grants and changes the plugin instance scope in one transaction. The canvas keeps its ID, plugin instance, active release, state, and release history.
Promotion publishes one lifecycle event after commit. The task and workspace navigation projections then refresh.
A release that adds permissions uses the same review component. Removing a declaration does not need approval. The effective grant contracts to the new declaration after activation.
Readable review surfaces
CanvasReleaseDialog and CanvasPromotionDialog share a permission view model
and human-readable provenance. Show release creation time, author kind, source
task title, and source session name when currently authorized. Use an
unavailable-source label for deleted or inaccessible records. Raw UUIDs stay in
API identities and action payloads, never headings, accessible labels, or copy.
Add optional source labels to the HTTP release/promotion projections through
authorized task/session lookups; avoid client-side lookup waterfalls.
Select the pending release by default, otherwise the active release. A compact
release selector preserves access to retained history. Distinguish Active from
Previous for valid releases by comparing with active_release_id. Render one
grouped declaration with newly requested rows marked. Use localized semantic
labels for reads, writes, state, and supported event/resource names; preserve
exact normalized network origins. Unknown values have a localized unsupported
permission label and cannot be approved silently. Keep rejection reasons and
real validation errors separate from normal permission review.
On desktop use a viewport-bounded wider dialog (target 48rem maximum width) with a fixed title and action footer. The single middle region can scroll; remove the 288px cap and repeated declared/missing-permission blocks. Pending release actions are Approve and Reject, retained release action is Roll back, and current release has Close. Reuse unchanged stale-review safeguards.
On phones use the full-height focused surface pattern from task-layout.tsx,
with header/footer and internal scrolling mechanics from
mobile-menu-sheet.tsx. This deep release review warrants a full-height
surface; the short host action chooser remains an inset bottom drawer.
Use useResponsiveBreakpoint, dynamic viewport height, safe areas, and
touch-only targets of at least 44px. Desktop controls remain 28px. Share
selection, actions, loading, and errors between presentations. Dismissal returns
focus to the opener; a release change during review refreshes the selection and
disables stale actions. No horizontal document overflow is permitted.
Quick Chat editing
The workspace canvas host has an Edit canvas action. It calls a canvas edit endpoint instead of invoking a one-shot utility agent.
The endpoint performs these steps:
- Authorize the canvas and workspace.
- Create a normal Quick Chat session with
origin: canvas_editmetadata. - Record the target canvas and active release in trusted session metadata.
- Materialize the active release source in the Quick Chat workspace.
- Open Quick Chat with a prompt that describes the requested edit workflow.
- Restrict canvas publish tools to the target canvas.
The user describes changes in Quick Chat. The agent edits the materialized source, builds static assets, and calls the normal publish tool. The active release changes only after validation and permission checks succeed.
The Quick Chat workspace remains disposable. The immutable release store owns published source. Quick Chat cleanup cannot remove an active or prior release.
Runtime data and state
The iframe uses the plugin web protocol. For a task board, the expected data flow is:
Initial page load
-> GET ./_kandev/v1/data/tasks
-> GET ./_kandev/v1/data/workflows/{id}/steps
-> read each task.workflow_step_id
-> render tasks grouped by workflow and workflow step
User continues a task
-> POST ./_kandev/v1/data/tasks/{id}/messages { text: "continue" }
-> the shared message service queues, resumes, or starts the task session
-> normal task and session events reach the iframe
User moves a card
-> choose the next ordered workflow step
-> PATCH ./_kandev/v1/data/tasks/{id} { workflow_step_id: nextStepId }
-> task service updates the task
-> task.state_changed event is published
-> the iframe receives the event through Server-Sent Events
Agent moves a task
-> existing Kandev task MCP tool
-> task service updates the task
-> the same event reaches the iframe
The task store remains the source of truth. Canvas instance state stores only data that has no Kandev domain owner. Examples include saved board filters, custom lane definitions, or application annotations.
The agent can read and change instance state with the canvas state tools. The iframe uses the relative state routes. Both paths use the same revision and conflict service.
HTTP API
Add owner-authorized canvas endpoints for:
- list task canvases
- list workspace canvases
- get canvas metadata and host state
- request a runtime URL
- get promotion preview
- confirm promotion
- get release history
- approve a pending release
- roll back a release
- start an edit Quick Chat session
- archive, restore, and remove a workspace canvas
The first delivery has no manual create, source upload, package import, package export, or demotion endpoint.
The publish path is agent-facing through MCP. It can use a narrow internal HTTP or WebSocket transfer between the backend and agent execution layer. It is not a browser upload route.
WebSocket lifecycle events
Canvas metadata changes use the existing Kandev WebSocket gateway. Add these owner-scoped events:
canvas.createdcanvas.release.activatedcanvas.release.permission_requiredcanvas.promotedcanvas.archivedcanvas.restoredcanvas.removed
Events contain canvas identity, scope, host state, and release metadata. They do not contain source files, HTML, JavaScript, state values, or runtime tokens.
Application data updates do not use this channel. The iframe receives its declared events through the plugin Server-Sent Events protocol.
Desktop information architecture
Keep the first-party Canvases section folded on first use. A direct canvas route does not force it open. An explicit user toggle remains in the persisted sidebar preference. The section lists active workspace canvases for the active workspace. It does not list task canvases.
The section contains:
- a workspace canvas count
- one row for each active workspace canvas
- a settings shortcut
- an empty setup row that opens the guided task dialog
The empty setup row opens the guided task dialog on the current route. The separate settings shortcut opens workspace canvas management. The task agent creates the canvas after submission; opening the dialog does not create canvas metadata. There is no package import or blank canvas builder action.
Routes are:
/canvases/:canvasIdfor the focused host surface/settings/workspaces/:workspaceId/canvasesfor workspace canvas management
The shared feature-aware workspace tab catalog includes Canvases. The settings
tree, tab strip, headings, and links derive from this catalog. The Canvases page
uses WorkspaceSettingsShell, shows active and archived workspace canvases,
and supports open, create through a task, edit, permissions, releases, archive,
restore, and remove. It does not import packages or create a blank canvas.
Wide workspace cards add an active-canvas count tile and include that count in the resource total. Narrow cards retain the current readable tile widths and omit the canvas tile. The settings tab remains available at every supported width. Canvas count requests do not run while the feature is disabled.
The task workbench uses one generic canvas panel. The panel receives
canvasId, resolves the active release, and renders the shared web-application
host. Multiple task canvases use one picker and one panel type.
The task workbench watches committed canvas lifecycle invalidations. When the
active task receives its first activated or pending-permission release, it adds
and activates canvas:<canvasId>. It does not add a duplicate panel or take
focus for another task. A pending release opens the host recovery state instead
of an empty iframe. On a phone, the same event navigates to the focused canvas
route because Dockview is not mounted.
Host chrome stays outside the iframe. Desktop chrome shows the title, runtime state, Edit, Promote for task canvases, Open full canvas, and an overflow menu. Release, permission, promotion, and disabled controls have pointer and keyboard help. The mobile action drawer shows equivalent descriptions without hover.
The Dockview canvas renderer provides a full-height flex boundary around the persistent portal. The shared host and iframe then fill the area below host chrome. This change does not alter the direct route or phone viewport formula.
Mobile design contract
- Outcome and entry: A user opens task canvases from the task mobile navigation. A user opens workspace canvases from the workspace mobile menu.
- Exemplars: Use
task-layout.tsxfor full-height composition. Usemobile-picker-sheet.tsxfor the canvas and action picker. Usekanban-with-preview.tsxfor direct phone navigation. - Hierarchy: Show the canvas title, runtime state, focused application, and one primary host action. Put secondary host actions in a visible overflow control.
- Presentation: Use a direct
h-dvhroute on phones. Do not mount Dockview or a desktop sidebar. - Navigation: Use one focused canvas. Use an inset bottom drawer to choose another canvas or a host action.
- Creation: Keep Create canvas visible on workspace Canvases settings. Open the standard full-screen task dialog with the shared canvas preset.
- Action help: Show lifecycle action descriptions inside the action drawer. Do not require hover.
- Geometry: Give the iframe the remaining viewport after fixed host chrome. Use one host scroll owner, safe-area padding, and 44-pixel host controls.
- Application responsibility: The canvas package owns responsive layout inside the iframe. The scaffold and acceptance fixture must support a phone viewport without document-level horizontal overflow.
Desktop and mobile use the same canvas metadata, plugin instance, active release, grants, runtime URL service, and lifecycle actions.
Host state and recovery
The shared host component has these states:
loading_metadatapending_first_releasepending_permissionloading_runtimereadyofflineinvalid_releaseunavailablearchived
The iframe can remain visible during an event-stream reconnect. The host shows the offline state outside the application. If the runtime token expires, the host requests a new URL and reloads the same active release.
Receiving a descriptor keeps loading_runtime and mounts the frame behind its
loading cover. CanvasHostSurface must mount during this state, not only Ready.
Only the current plugin startup acknowledgement
sets Ready. A failed or absent acknowledgement within 15 seconds tears down the
frame and exposes Retry and Releases. Do not claim a specific CSP or network
cause from the generic timeout. Retry, renewal, navigation, and authority
changes fence old callbacks. Appearance remains applied before reveal.
If the release is unavailable, the host offers Edit, Releases, Roll back, or Remove according to current permissions. It never loads a rejected artifact.
Authorization
Every human endpoint uses current workspace authorization. A foreign and a missing canvas return the same not-visible result.
Every agent operation derives user, task, workspace, and session from the MCP connection. An edit Quick Chat session also verifies its trusted target canvas metadata.
Task canvases cannot appear in another task. Workspace canvases can serve task surfaces only inside their workspace. Promotion does not grant access to tasks, repositories, sessions, or external services by itself.
The plugin runtime repeats resource authorization for each request. Canvas authorization does not replace task or workspace authorization.
Failure handling
- A missing task agent disables the create prompt and shows how to start one.
- A source read error preserves the active release.
- A package validation error returns safe file and rule diagnostics to the authoring agent.
- A permission increase after initial owner-authorized publication creates a pending release.
- A promotion transaction either changes scope and grants together or changes neither.
- A Quick Chat launch error leaves releases unchanged.
- A runtime token error reloads through the host before it shows unavailable.
- A state conflict returns the current revision to the writer.
- A removed task removes unpromoted canvases through a retryable cleanup path.
- A removed workspace records artifact cleanup before it removes database ownership.
- A count, rate, or storage rejection preserves the active release.
Observability
Add content-free metrics for:
- canvas create result
- source collection and publish result
- release activation and pending-permission result
- promotion result
- edit Quick Chat launch result
- runtime host state
- archive, restore, rollback, and removal result
- canvas count, publish-rate, and artifact-storage admission result
Logs can include canvas ID, plugin instance ID, release ID, workspace ID, task ID, actor kind, operation, safe result, and duration. Logs omit canvas title, prompt, source, application content, state values, package bodies, and runtime tokens.
Test strategy
- Canvas repository tests cover task cleanup, promoted preservation, scope transitions, and release references.
- Service tests cover trusted agent context, publish authorization, promotion, permissions, rollback, edit sessions, workspace removal, and limits.
- Execution integration tests cover source collection from standalone, Docker, and remote streaming archive readers.
- MCP tests cover tool inventory, trusted context, assigned source roots, state conflicts, and forbidden lifecycle actions.
- Gateway tests cover lifecycle event scope and content limits.
- Frontend tests cover sidebar filtering, task picker, host state, promotion, permission review, release history, Quick Chat launch, task presets, workspace tab catalogs, card breakpoints, action help, and panel deduplication.
- Desktop Playwright covers agent creation, live task data, promotion, sidebar discovery, task-based creation, automatic panel opening, host geometry, editing, a pending permission, activation, and rollback.
- Mobile Playwright covers task entry, workspace navigation, the focused route, task-based creation, bottom drawer help, 44-pixel host controls, safe areas, and no host overflow.
- A test web application covers HTML, CSS, JavaScript, state, task reads, task writes, Server-Sent Events, appearance changes, desktop layout, and phone layout.
Related decisions
- Plugin-backed web-app canvases
- Plugin agent tools through Kandev MCP
- Host utility agentctl for sessionless flows
- Superseded declarative canvases
Canvas host chrome
The single-header design defines embedded and standalone chrome, status presentation, and shared panel toolbar integration.