Skip to content

Artifacts

An artifact is a durable output the agent produces that outlives the thread it was born in. Where a thread reply is conversational and scrolls away, an artifact has an identity, a version history, and a home in a space — it is the deliverable, not the discussion about the deliverable.

ThinkWork ships two artifact kinds on the same living-artifacts substrate:

KindBodyInteractivityTypical use
Living CanvasGenUI (json-render) with data bindingsInteractive components, app-tier when neededDashboards, working views over live data
Document ArtifactMarkdown digest, compiled to a house-style HTML renderNone (scriptless by design)Reports, plans, briefs, ideation docs

Both are born as artifacts: they exist from first emission (status draft) rather than being promoted from a thread snapshot after the fact. Both belong to a space, not to the thread that created them, and both can be edited across threads via chat.

A Document Artifact is the simplest artifact kind — a formal document the agent authors as markdown only in a single emit_document call. The platform’s Document Compositor compiles that markdown into the house-style HTML render server-side at emission:

  • The markdown digest — the canonical record and the only body the agent writes: optional frontmatter (eyebrow, date, context), prose, tables, and tw: component blocks. This is what other agents, mobile clients, and memory consolidation consume. It is complete on its own: everything the document says is in the digest.
  • The HTML render — the human-facing body, produced by the compiler, never by the model. A single self-contained HTML file (inline CSS, system fonts, no external references, no scripts) on the genre’s house template, dark-mode aware, print-ready.

Because the render is compiled from the digest, the pretty version and the machine-readable version can never drift — and because no accepted path carries model-written HTML, an improvised, off-brand document is impossible by construction, no matter how the model is prompted.

v1 ships four document genres, each backed by a compiler-owned house template:

  • Ideation — divergent exploration with options and trade-offs
  • Plan — sequenced units of work with dependencies and verification
  • Report — findings with evidence, structured for review
  • Brief — short-form summary for a decision-maker

Rich visuals are declarative fenced blocks in the markdown — the model writes the data, the compiler renders the pixels:

  • tw:stats — a strip of headline-number tiles.
  • tw:verdict-grid — a card grid of discrete questions with bold answers.
  • tw:chart — data drawn by the house SVG chart renderer (bar, line, donut, stat-strip, sparkline, meter, funnel), palette-locked to the house tokens and dark-mode aware. Every chart is paired with a collapsible data table automatically.

The vocabulary is closed and versioned: an unknown component or malformed block rejects the emission with a diagnostic that names the supported set and shows a corrected example, so the agent self-repairs in one turn. There is no raw-HTML escape block, and inline HTML or SVG in the markdown is stripped at compile time.

Every artifact kind shares one rendering pattern in chat:

  • In the thread — the emitting turn renders the agent’s short reply plus a compact artifact card at the end of the message (title, type badge, Status · vN · freshness meta line). The transcript never carries a full inline render — the card is the pointer, the artifact is the deliverable.
  • Docked panel — clicking a card opens the artifact in a resizable panel beside the thread. The conversation stays usable; edits requested in chat re-emit the artifact and the open panel updates live. The thread header’s panel button opens the newest artifact directly (or a picker when the thread produced several).
  • Full-page view/artifacts/<id> renders the artifact full-height (documents in a sandboxed frame), with the version history alongside.
  • In the space — the artifact lists under its owning space like any other living artifact.

Document Artifacts render at the document tier: a sandboxed iframe with allow-scripts stripped. A document can carry arbitrary styling but can never execute code. This is enforced twice:

  1. At compilation — the compositor itself is the wall: model-authored HTML/SVG is stripped, external links degrade to text, and chart SVG is produced only by the house renderer. DocSpector, the runtime preflight, then re-verifies the compiled output (fully self-contained, scriptless, dark-mode, size ceilings) before anything is stored — a failure there is treated as a platform defect, not the agent’s problem.
  2. At render — the sandbox attribute set guarantees that even HTML that slipped past validation cannot run scripts or reach the network.

A document that genuinely needs interactivity is not the document tier’s job — it graduates deliberately to the app tier (a Living Canvas or MCP app frame), which grants script execution under its own containment rules.

A Living Canvas widget built from a tool result carries a data binding — the saved tool call that produced its data — so the canvas can be refreshed without re-authoring it. Each binding tracks a freshness quality (GOOD, STALE, BAD, SCHEMA_STALE) and last-fetched timestamps.

Clicking Refresh on a canvas just works, by whichever path the binding’s credentials allow:

  • Tenant-scoped bindings (tenant API keys, service credentials) refresh headlessly — the saved tool call re-executes server-side and the fresh data lands in the canvas in a few seconds. No agent turn, no tokens.
  • Per-user connector bindings (e.g. a CRM connected with your personal OAuth) refresh under the clicking owner’s own stored connection — also seconds, also headless. Unattended refreshes (schedules, other viewers) never touch personal credentials; for a non-owner the canvas falls back to an agent-mediated refresh in the canvas’s thread.
  • Schema changes (the tool now returns a different shape) escalate to the agent, which re-authors the widget rather than force-fitting stale structure onto new data.

After any successful refresh — headless or agent-mediated — the binding reads GOOD with fresh timestamps.