skip to content
$worker

canvas

v0.1.7

Create, edit and render diagrams in the console — stored as editable source, drawn live in chat and on a canvas page.

iiiverified
19 installs1 in 7d0 today
install
$iii trigger compose::add worker=canvas@0.1.7
  • macOS: arm64
  • Linux: arm64 · armv7 · x64
  • Windows: arm64 · x64

exact versions are immutable; binary and bundle artifacts are digest-pinned.

agent-ready brief for v0.1.7
install + config + dependencies + readme + api reference, all in one place. fetch as agent-context.md for an llm to consume.
the same content rendered as discrete blocks below is exposed as a single markdown document at /workers/canvas.md?version=0.1.7. paste it into an llm prompt or pipe it through curl from a worker.

install

install
$iii trigger compose::add worker=canvas@0.1.7

dependencies

dependencies2

readme

README.md

canvas

Diagrams an agent can come back to. A canvas stores its editable source — mermaid text, or an excalidraw scene for a freeform whiteboard — under a stable 8-character id, so the architecture sketch drawn in one turn can be revised ten turns later and every earlier link to it keeps working. The console does the drawing: a canvas::* call renders in chat as the live diagram rather than a wall of source, and a canvas page lists, edits and redraws everything stored. Freeform whiteboards go further: the canvas::element::* family adds, moves and removes individual shapes one call at a time, and because every mutation is a state write the open page streams, the board draws itself while the agent works. For generated mermaid there is a primer on the bus (canvas::syntax) and a parse check (canvas::validate), so source validates on the first try instead of guessing dialect details.

Install

iii trigger compose::add worker=canvas worker=state # state stores canvas records

iii trigger compose::add resolves the workers and their dependencies, writes exact declarations to worker-compose.yaml, and reconciles the Compose project.

Companion workers

Worker Why
state Required. Every canvas record lives in its canvas scope; the worker holds nothing in process memory, so a restart loses nothing.
console Optional. Renders the #/ext/canvas page and draws canvas::* calls as live diagrams in chat.

Quickstart

Get the primer, validate, store:

use iii_sdk::protocol::TriggerRequest;
use iii_sdk::{register_worker, InitOptions};
use serde_json::json;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let iii = register_worker("ws://localhost:49134", InitOptions::default());
    let call = |id: &str, payload| iii.trigger(TriggerRequest {
        function_id: id.into(), payload, action: None, timeout_ms: Some(10_000),
    });

    // The dialect the renderer actually supports, a working example per family.
    let primer = call("canvas::syntax", json!({ "family": "sequence" })).await?;
    println!("{}", primer["families"][0]["example"]);

    let source = "sequenceDiagram\n    Client->>Engine: trigger\n    Engine-->>Client: result";

    // Parse before storing, so a broken diagram never lands in the store.
    let verdict = call("canvas::validate", json!({
        "format": "mermaid", "source": source,
    })).await?;
    // { "valid": true, "family": "sequence", "issues": [] }
    assert_eq!(verdict["valid"], true);

    let record = call("canvas::create", json!({
        "name": "Handshake", "format": "mermaid", "source": source,
    })).await?;
    // { "id": "a1b2c3d4", "name": "Handshake", "format": "mermaid",
    //   "family": "sequence", "source": "…", "created_at": …, "updated_at": … }
    println!("stored canvas {}", record["id"]);
    Ok(())
}

Revisions go through canvas::update with the id: the id never changes, updated_at is stamped, and for mermaid the diagram family is re-derived from the new source. canvas::get reads one back, canvas::list returns everything newest first with an optional format filter, and canvas::delete reports deleted: false on an unknown id rather than erroring.

A freeform canvas takes an excalidraw scene JSON string as its source; canvas::validate checks the scene's shape the same way it parses mermaid.

Drawing element by element

Freeform whiteboards are also editable one shape at a time — the flow an agent uses to draw something live while a person watches the page:

// Same `call` helper as above. Skeleton shorthand is enough: position,
// size, and a label; the console converts to full shapes at render time.
let board = call("canvas::create", json!({
    "name": "Request path", "format": "freeform",
    "source": r#"{"type":"excalidraw","version":2,"elements":[]}"#,
})).await?;

let added = call("canvas::element::add", json!({
    "id": board["id"],
    "elements": [
        { "type": "rectangle", "x": 100, "y": 100, "width": 200, "height": 80,
          "label": { "text": "engine" } },
        { "type": "ellipse", "x": 400, "y": 100, "width": 180, "height": 80,
          "label": { "text": "worker" } },
        { "type": "arrow", "x": 302, "y": 140, "width": 96, "height": 0 }
    ],
})).await?;
// { "id": "…", "element_ids": ["…", "…", "…"], "element_count": 3 }

canvas::element::list returns the board map (id, type, position, text) before connecting or moving shapes, canvas::element::update merges properties into one element by id, and canvas::element::delete removes by id. The family works on freeform canvases only; mermaid canvases are edited as text through canvas::update.

Console page

The page at #/ext/canvas lists every stored canvas and opens each one for editing: mermaid source beside its live rendering, a freeform scene on a drawable whiteboard. The page streams: records live in the state worker's canvas scope, so an agent-side create pops into the sidebar (and opens, when nothing else is), an update redraws the open diagram in place, and element calls land on the open whiteboard as they happen — no reload, no polling. Renders sketch themselves in with a stroke animation, mermaid ships the hand-drawn look, and both panes export SVG and PNG with a background-or-transparent and light-or-dark choice independent of the console theme. In chat, a canvas::* call renders as the diagram it touched, not as JSON.

Configuration

Configuration lives in the configuration worker under the id canvas and every field hot-reloads — handlers read the live snapshot per call, so nothing needs a restart.

max_source_bytes: 2097152   # largest canvas source accepted, in bytes
max_list: 200               # most records canvas::list returns in one response

Both fields are bounds: the first keeps an oversized excalidraw scene or generated mermaid blob off the state bus, the second caps a list response. Defaults live in src/config.rs.

Called on demand

This worker registers no harness hook and injects nothing into any prompt. A conversation that never draws pays nothing for having it installed. An agent finds it through the function registry and skills/SKILL.md; a person finds it through the console page.

api reference (json)

agent-api-reference.json
{
  "functions": [
    {
      "description": "Create a new canvas from mermaid text or an excalidraw scene JSON. Returns the stored record, including the minted stable id and, for mermaid, the diagram family derived from the source.",
      "metadata": {},
      "name": "canvas::create",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "CanvasFormat": {
            "description": "Diagram format a canvas holds.",
            "oneOf": [
              {
                "description": "Mermaid text — rendered from source by the console.",
                "enum": [
                  "mermaid"
                ],
                "type": "string"
              },
              {
                "description": "A freeform whiteboard — the source is an excalidraw scene JSON.",
                "enum": [
                  "freeform"
                ],
                "type": "string"
              }
            ]
          }
        },
        "properties": {
          "format": {
            "anyOf": [
              {
                "$ref": "#/definitions/CanvasFormat"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "Diagram format: `mermaid` or `freeform`. Defaults to `mermaid`."
          },
          "name": {
            "default": null,
            "description": "Human-readable canvas name. Omit for a name derived from the detected diagram family (`Untitled flowchart`, `Untitled whiteboard`, …).",
            "type": [
              "string",
              "null"
            ]
          },
          "source": {
            "description": "The editable source: mermaid text for `mermaid`, an excalidraw scene JSON string for `freeform`.",
            "type": "string"
          }
        },
        "required": [
          "source"
        ],
        "title": "Request",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "CanvasFormat": {
            "description": "Diagram format a canvas holds.",
            "oneOf": [
              {
                "description": "Mermaid text — rendered from source by the console.",
                "enum": [
                  "mermaid"
                ],
                "type": "string"
              },
              {
                "description": "A freeform whiteboard — the source is an excalidraw scene JSON.",
                "enum": [
                  "freeform"
                ],
                "type": "string"
              }
            ]
          }
        },
        "description": "One stored canvas. The id is a stable 8-character slug that never changes across updates; `source` is always the editable source of truth (mermaid text, or the excalidraw scene JSON for freeform).",
        "properties": {
          "created_at": {
            "description": "Creation time, unix seconds.",
            "format": "int64",
            "type": "integer"
          },
          "family": {
            "description": "Mermaid diagram family (`flowchart`, `sequenceDiagram`, …), derived from the source. `null` for a freeform canvas.",
            "type": [
              "string",
              "null"
            ]
          },
          "format": {
            "allOf": [
              {
                "$ref": "#/definitions/CanvasFormat"
              }
            ],
            "description": "Diagram format: `mermaid` or `freeform`."
          },
          "id": {
            "description": "Stable 8-character slug identifying this canvas. Never changes across updates.",
            "type": "string"
          },
          "name": {
            "description": "Human-readable canvas name.",
            "type": "string"
          },
          "source": {
            "description": "The editable source of truth: mermaid text for `mermaid`, the excalidraw scene JSON for `freeform`.",
            "type": "string"
          },
          "updated_at": {
            "description": "Last update time, unix seconds.",
            "format": "int64",
            "type": "integer"
          }
        },
        "required": [
          "created_at",
          "format",
          "id",
          "name",
          "source",
          "updated_at"
        ],
        "title": "CanvasRecord",
        "type": "object"
      }
    },
    {
      "description": "Delete a stored canvas by its stable 8-character id. Deleting an unknown id is not an error: the response reports deleted=false instead.",
      "metadata": {},
      "name": "canvas::delete",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "id": {
            "description": "Stable 8-character canvas id.",
            "type": "string"
          }
        },
        "required": [
          "id"
        ],
        "title": "Request",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "deleted": {
            "description": "`true` when a record existed and was removed; `false` for an unknown id.",
            "type": "boolean"
          },
          "id": {
            "description": "The id the call asked to delete.",
            "type": "string"
          }
        },
        "required": [
          "deleted",
          "id"
        ],
        "title": "Response",
        "type": "object"
      }
    },
    {
      "description": "Add elements to a freeform canvas, one call per drawing step. Each element is an excalidraw-style object ({type, x, y, width?, height?, text?, label?, start?, end?, ...}); skeleton shorthand is accepted and converted at render time. Elements without an id get a stable generated one. The open console canvas streams every call, so shapes appear as they are added. Returns the assigned ids and the new element count.",
      "metadata": {},
      "name": "canvas::element::add",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "elements": {
            "description": "Elements to append, in z-order. Objects with at least a `type` string; unknown fields pass through to the scene untouched.",
            "items": true,
            "type": "array"
          },
          "id": {
            "description": "Stable 8-character canvas id (format must be freeform).",
            "type": "string"
          }
        },
        "required": [
          "elements",
          "id"
        ],
        "title": "AddRequest",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "element_count": {
            "description": "Total elements in the scene after the add.",
            "format": "uint",
            "minimum": 0,
            "type": "integer"
          },
          "element_ids": {
            "description": "Assigned element ids, in the order the elements were given.",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "id": {
            "description": "The canvas the elements were added to.",
            "type": "string"
          }
        },
        "required": [
          "element_count",
          "element_ids",
          "id"
        ],
        "title": "AddResponse",
        "type": "object"
      }
    },
    {
      "description": "Remove elements from a freeform canvas by element id. Unknown ids are ignored; the response reports how many were actually removed. The open console canvas streams the change live.",
      "metadata": {},
      "name": "canvas::element::delete",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "element_ids": {
            "description": "Element ids to remove.",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "id": {
            "description": "Stable 8-character canvas id (format must be freeform).",
            "type": "string"
          }
        },
        "required": [
          "element_ids",
          "id"
        ],
        "title": "DeleteRequest",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "element_count": {
            "description": "Total elements left in the scene.",
            "format": "uint",
            "minimum": 0,
            "type": "integer"
          },
          "id": {
            "type": "string"
          },
          "removed": {
            "description": "How many of the given ids existed and were removed.",
            "format": "uint",
            "minimum": 0,
            "type": "integer"
          }
        },
        "required": [
          "element_count",
          "id",
          "removed"
        ],
        "title": "DeleteResponse",
        "type": "object"
      }
    },
    {
      "description": "List the elements of a freeform canvas: id, type, position and size per element — the map an agent reads before updating or connecting shapes. Full element bodies are in the record source via canvas::get.",
      "metadata": {},
      "name": "canvas::element::list",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "id": {
            "description": "Stable 8-character canvas id (format must be freeform).",
            "type": "string"
          }
        },
        "required": [
          "id"
        ],
        "title": "ListRequest",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "ElementSummary": {
            "properties": {
              "height": {
                "format": "double",
                "type": [
                  "number",
                  "null"
                ]
              },
              "id": {
                "type": "string"
              },
              "text": {
                "description": "The element's own `text`, or its `label.text` shorthand.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "type": {
                "description": "Element type (`rectangle`, `ellipse`, `arrow`, `text`, ...).",
                "type": "string"
              },
              "width": {
                "format": "double",
                "type": [
                  "number",
                  "null"
                ]
              },
              "x": {
                "format": "double",
                "type": [
                  "number",
                  "null"
                ]
              },
              "y": {
                "format": "double",
                "type": [
                  "number",
                  "null"
                ]
              }
            },
            "required": [
              "id",
              "type"
            ],
            "type": "object"
          }
        },
        "properties": {
          "elements": {
            "items": {
              "$ref": "#/definitions/ElementSummary"
            },
            "type": "array"
          },
          "id": {
            "type": "string"
          }
        },
        "required": [
          "elements",
          "id"
        ],
        "title": "ListResponse",
        "type": "object"
      }
    },
    {
      "description": "Merge properties into one element of a freeform canvas by element id (move it, recolor it, change its text). Unknown ids error and name the canvas. The open console canvas streams the change live.",
      "metadata": {},
      "name": "canvas::element::update",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "element_id": {
            "description": "Element id to update (from element::add or element::list).",
            "type": "string"
          },
          "id": {
            "description": "Stable 8-character canvas id (format must be freeform).",
            "type": "string"
          },
          "props": {
            "additionalProperties": true,
            "description": "Properties to merge into the element. `null` values remove the key.",
            "type": "object"
          }
        },
        "required": [
          "element_id",
          "id",
          "props"
        ],
        "title": "UpdateRequest",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "element": {
            "description": "The element after the merge."
          },
          "element_id": {
            "type": "string"
          },
          "id": {
            "type": "string"
          }
        },
        "required": [
          "element",
          "element_id",
          "id"
        ],
        "title": "UpdateResponse",
        "type": "object"
      }
    },
    {
      "description": "Read one canvas by its stable 8-character id. Returns the full stored record, including the editable source. Errors when the id is unknown.",
      "metadata": {},
      "name": "canvas::get",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "id": {
            "description": "Stable 8-character canvas id, as returned by `canvas::create` and `canvas::list`.",
            "type": "string"
          }
        },
        "required": [
          "id"
        ],
        "title": "Request",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "CanvasFormat": {
            "description": "Diagram format a canvas holds.",
            "oneOf": [
              {
                "description": "Mermaid text — rendered from source by the console.",
                "enum": [
                  "mermaid"
                ],
                "type": "string"
              },
              {
                "description": "A freeform whiteboard — the source is an excalidraw scene JSON.",
                "enum": [
                  "freeform"
                ],
                "type": "string"
              }
            ]
          }
        },
        "description": "One stored canvas. The id is a stable 8-character slug that never changes across updates; `source` is always the editable source of truth (mermaid text, or the excalidraw scene JSON for freeform).",
        "properties": {
          "created_at": {
            "description": "Creation time, unix seconds.",
            "format": "int64",
            "type": "integer"
          },
          "family": {
            "description": "Mermaid diagram family (`flowchart`, `sequenceDiagram`, …), derived from the source. `null` for a freeform canvas.",
            "type": [
              "string",
              "null"
            ]
          },
          "format": {
            "allOf": [
              {
                "$ref": "#/definitions/CanvasFormat"
              }
            ],
            "description": "Diagram format: `mermaid` or `freeform`."
          },
          "id": {
            "description": "Stable 8-character slug identifying this canvas. Never changes across updates.",
            "type": "string"
          },
          "name": {
            "description": "Human-readable canvas name.",
            "type": "string"
          },
          "source": {
            "description": "The editable source of truth: mermaid text for `mermaid`, the excalidraw scene JSON for `freeform`.",
            "type": "string"
          },
          "updated_at": {
            "description": "Last update time, unix seconds.",
            "format": "int64",
            "type": "integer"
          }
        },
        "required": [
          "created_at",
          "format",
          "id",
          "name",
          "source",
          "updated_at"
        ],
        "title": "CanvasRecord",
        "type": "object"
      }
    },
    {
      "description": "List stored canvases, newest first, optionally filtered by format. Each entry is the full record including its source; the response is capped by the configured max_list.",
      "metadata": {},
      "name": "canvas::list",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "CanvasFormat": {
            "description": "Diagram format a canvas holds.",
            "oneOf": [
              {
                "description": "Mermaid text — rendered from source by the console.",
                "enum": [
                  "mermaid"
                ],
                "type": "string"
              },
              {
                "description": "A freeform whiteboard — the source is an excalidraw scene JSON.",
                "enum": [
                  "freeform"
                ],
                "type": "string"
              }
            ]
          }
        },
        "properties": {
          "format": {
            "anyOf": [
              {
                "$ref": "#/definitions/CanvasFormat"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "Only return canvases of this format. Omit for every canvas."
          }
        },
        "title": "Request",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "CanvasFormat": {
            "description": "Diagram format a canvas holds.",
            "oneOf": [
              {
                "description": "Mermaid text — rendered from source by the console.",
                "enum": [
                  "mermaid"
                ],
                "type": "string"
              },
              {
                "description": "A freeform whiteboard — the source is an excalidraw scene JSON.",
                "enum": [
                  "freeform"
                ],
                "type": "string"
              }
            ]
          },
          "CanvasRecord": {
            "description": "One stored canvas. The id is a stable 8-character slug that never changes across updates; `source` is always the editable source of truth (mermaid text, or the excalidraw scene JSON for freeform).",
            "properties": {
              "created_at": {
                "description": "Creation time, unix seconds.",
                "format": "int64",
                "type": "integer"
              },
              "family": {
                "description": "Mermaid diagram family (`flowchart`, `sequenceDiagram`, …), derived from the source. `null` for a freeform canvas.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "format": {
                "allOf": [
                  {
                    "$ref": "#/definitions/CanvasFormat"
                  }
                ],
                "description": "Diagram format: `mermaid` or `freeform`."
              },
              "id": {
                "description": "Stable 8-character slug identifying this canvas. Never changes across updates.",
                "type": "string"
              },
              "name": {
                "description": "Human-readable canvas name.",
                "type": "string"
              },
              "source": {
                "description": "The editable source of truth: mermaid text for `mermaid`, the excalidraw scene JSON for `freeform`.",
                "type": "string"
              },
              "updated_at": {
                "description": "Last update time, unix seconds.",
                "format": "int64",
                "type": "integer"
              }
            },
            "required": [
              "created_at",
              "format",
              "id",
              "name",
              "source",
              "updated_at"
            ],
            "type": "object"
          }
        },
        "properties": {
          "canvases": {
            "description": "The stored records, newest first.",
            "items": {
              "$ref": "#/definitions/CanvasRecord"
            },
            "type": "array"
          },
          "count": {
            "description": "How many records this response carries.",
            "format": "uint",
            "minimum": 0,
            "type": "integer"
          }
        },
        "required": [
          "canvases",
          "count"
        ],
        "title": "Response",
        "type": "object"
      }
    },
    {
      "description": "Internal: hot-reload the canvas worker from the authoritative configuration when it changes, swapping the per-call snapshot.",
      "metadata": {
        "internal": true
      },
      "name": "canvas::on-config-change",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "description": "Payload of the internal config-change handler. The handler re-fetches the authoritative value, so this carries only the advisory id; a struct rather than a `Value` keeps the request schema concrete.",
        "properties": {
          "id": {
            "default": null,
            "description": "Configuration id that changed (advisory; the handler re-fetches).",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "title": "OnConfigChangeEvent",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "description": "Ack returned by the internal config-change handler.",
        "properties": {
          "ok": {
            "type": "boolean"
          }
        },
        "required": [
          "ok"
        ],
        "title": "OnConfigChangeResponse",
        "type": "object"
      }
    },
    {
      "description": "Return the mermaid syntax reference: every supported diagram family with a short summary and a working example, or — narrowed to one family — a compact syntax primer. Call this before writing mermaid source.",
      "metadata": {},
      "name": "canvas::syntax",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "family": {
            "default": null,
            "description": "Diagram family to return (`flowchart`, `sequenceDiagram`, …; aliases like `graph` accepted); omit for the overview of every family.",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "title": "Request",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "FamilySyntax": {
            "description": "One diagram family's reference entry.",
            "properties": {
              "example": {
                "description": "A minimal, valid mermaid example of this family.",
                "type": "string"
              },
              "family": {
                "description": "Canonical family name (`flowchart`, `sequenceDiagram`, …) — the same string `canvas::validate` reports and `canvas::create` stores.",
                "type": "string"
              },
              "summary": {
                "description": "One-line description of what this family is for.",
                "type": "string"
              }
            },
            "required": [
              "example",
              "family",
              "summary"
            ],
            "type": "object"
          }
        },
        "properties": {
          "families": {
            "description": "The reference entries — every family for the overview, exactly one when the request named a family.",
            "items": {
              "$ref": "#/definitions/FamilySyntax"
            },
            "type": "array"
          },
          "syntax": {
            "description": "The reference as readable text: one line per family for the overview, or the named family's syntax primer with its example.",
            "type": "string"
          }
        },
        "required": [
          "families",
          "syntax"
        ],
        "title": "Response",
        "type": "object"
      }
    },
    {
      "description": "Serve the canvas worker's injected console UI assets (content function for its console:script / console:style triggers).",
      "metadata": {
        "internal": true
      },
      "name": "canvas::ui-content",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "description": "Input of the content function: the console asks for one asset by path.",
        "properties": {
          "path": {
            "description": "The asset path from the trigger config (e.g. `state/page.js`).",
            "type": "string"
          }
        },
        "required": [
          "path"
        ],
        "title": "UiContentInput",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "description": "Output of the content function.",
        "properties": {
          "content": {
            "description": "The asset source, verbatim.",
            "type": "string"
          },
          "content_type": {
            "description": "MIME type the console should serve the asset with.",
            "type": "string"
          }
        },
        "required": [
          "content",
          "content_type"
        ],
        "title": "UiContentResult",
        "type": "object"
      }
    },
    {
      "description": "Update a stored canvas's name and/or source by id. The id never changes; updated_at is stamped and, for mermaid, the diagram family is re-derived from the new source. Returns the full updated record.",
      "metadata": {},
      "name": "canvas::update",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "id": {
            "description": "Stable 8-character canvas id.",
            "type": "string"
          },
          "name": {
            "default": null,
            "description": "New canvas name. Omit to keep the current one.",
            "type": [
              "string",
              "null"
            ]
          },
          "source": {
            "default": null,
            "description": "New source (mermaid text or excalidraw scene JSON, matching the canvas's format). Omit to keep the current one.",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "id"
        ],
        "title": "Request",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "CanvasFormat": {
            "description": "Diagram format a canvas holds.",
            "oneOf": [
              {
                "description": "Mermaid text — rendered from source by the console.",
                "enum": [
                  "mermaid"
                ],
                "type": "string"
              },
              {
                "description": "A freeform whiteboard — the source is an excalidraw scene JSON.",
                "enum": [
                  "freeform"
                ],
                "type": "string"
              }
            ]
          }
        },
        "description": "One stored canvas. The id is a stable 8-character slug that never changes across updates; `source` is always the editable source of truth (mermaid text, or the excalidraw scene JSON for freeform).",
        "properties": {
          "created_at": {
            "description": "Creation time, unix seconds.",
            "format": "int64",
            "type": "integer"
          },
          "family": {
            "description": "Mermaid diagram family (`flowchart`, `sequenceDiagram`, …), derived from the source. `null` for a freeform canvas.",
            "type": [
              "string",
              "null"
            ]
          },
          "format": {
            "allOf": [
              {
                "$ref": "#/definitions/CanvasFormat"
              }
            ],
            "description": "Diagram format: `mermaid` or `freeform`."
          },
          "id": {
            "description": "Stable 8-character slug identifying this canvas. Never changes across updates.",
            "type": "string"
          },
          "name": {
            "description": "Human-readable canvas name.",
            "type": "string"
          },
          "source": {
            "description": "The editable source of truth: mermaid text for `mermaid`, the excalidraw scene JSON for `freeform`.",
            "type": "string"
          },
          "updated_at": {
            "description": "Last update time, unix seconds.",
            "format": "int64",
            "type": "integer"
          }
        },
        "required": [
          "created_at",
          "format",
          "id",
          "name",
          "source",
          "updated_at"
        ],
        "title": "CanvasRecord",
        "type": "object"
      }
    },
    {
      "description": "Validate canvas source without storing it: detect the mermaid diagram family, check the size cap, balanced fences and per-family lints, or check an excalidraw scene JSON's shape. A cheap pre-flight — full parsing happens at render time in the console.",
      "metadata": {},
      "name": "canvas::validate",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "CanvasFormat": {
            "description": "Diagram format a canvas holds.",
            "oneOf": [
              {
                "description": "Mermaid text — rendered from source by the console.",
                "enum": [
                  "mermaid"
                ],
                "type": "string"
              },
              {
                "description": "A freeform whiteboard — the source is an excalidraw scene JSON.",
                "enum": [
                  "freeform"
                ],
                "type": "string"
              }
            ]
          }
        },
        "properties": {
          "format": {
            "anyOf": [
              {
                "$ref": "#/definitions/CanvasFormat"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "Source format, `mermaid` or `freeform`; omit to auto-detect (a source starting with `{` is an excalidraw scene, anything else mermaid)."
          },
          "source": {
            "description": "The source to validate: mermaid text, or an excalidraw scene JSON string.",
            "type": "string"
          }
        },
        "required": [
          "source"
        ],
        "title": "Request",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "ValidationIssue": {
            "description": "One problem found in the source.",
            "properties": {
              "line": {
                "description": "1-indexed source line the issue points at, when known.",
                "format": "uint32",
                "minimum": 0,
                "type": [
                  "integer",
                  "null"
                ]
              },
              "message": {
                "description": "Human-readable description of the issue.",
                "type": "string"
              }
            },
            "required": [
              "message"
            ],
            "type": "object"
          }
        },
        "properties": {
          "family": {
            "description": "The mermaid diagram family derived from the source (`flowchart`, `sequenceDiagram`, …). `null` for freeform or when the first meaningful line names no supported family.",
            "type": [
              "string",
              "null"
            ]
          },
          "issues": {
            "description": "Every issue found; empty when `valid` is `true`.",
            "items": {
              "$ref": "#/definitions/ValidationIssue"
            },
            "type": "array"
          },
          "valid": {
            "description": "`true` when every cheap check passed. This is a pre-flight verdict, not a render guarantee — full parsing happens in the console.",
            "type": "boolean"
          }
        },
        "required": [
          "issues",
          "valid"
        ],
        "title": "Response",
        "type": "object"
      }
    }
  ],
  "triggers": []
}