* docs: fix - update prompts path from web to daemon in README files Update all references from apps/web/src/prompts/ to apps/daemon/src/prompts/ across all README language files. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * docs: fix - update prompts path in specs and fix French README parity P2 — Current specs: - specs/current/critique-theater.md - specs/current/critique-theater-plan.md Update prompts path from apps/web/src/prompts/ to apps/daemon/src/prompts/ to reflect daemon ownership of prompt composition. P3 — Historical spec: - specs/2026-04-29-live-artifacts/spec.md Update to current path (historical snapshots kept accurate to latest codebase). P3 — Language parity: - README.fr.md Fix missing prompt references in Anti-AI-slop and References sections to match other language variants (6 refs total). Migration context: Prompts moved from web to daemon as an ownership/boundary change (daemon composes and injects prompts at agent spawn time), not a mechanical rename. Web app no longer owns prompt composition logic. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * docs: fix - update git command path in critique-theater-plan.md Fix missed git add command example at line 1585. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * docs: fix - update prompt test paths from web/tests to daemon/tests Update test file paths to match daemon ownership of prompts. Tests for apps/daemon/src/prompts/* should live in apps/daemon/tests/prompts/. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
46 KiB
Live Artifacts via Agent Skills
Status: Draft · 2026-04-29
Parent: docs/spec.md
Siblings: docs/skills-protocol.md · docs/agent-adapters.md · docs/modes.md
Reference implementation: ~/Projects/monet connectors + live artifacts
This spec defines how to bring Monet's connectors and live artifacts ideas into Open Design, but implement the agent-facing surface as file-based agent skills plus daemon-owned local tools, not as an in-process tool registry or MCP-first integration.
1. Product goal
Open Design should let an agent create previewable artifacts that are not just one-off generated files, but live, refreshable, auditable views backed by external or local data sources.
Examples:
- “Create a live GitHub release dashboard.”
- “Make a Notion project status page and let me refresh it tomorrow.”
- “Turn this folder of JSON files into a polished stakeholder report.”
- “Create a design-system coverage artifact that can be refreshed after code changes.”
The user experience should feel like the existing OD artifact flow:
- User chats with the selected agent.
- Agent uses a skill to plan and create a live artifact.
- OD persists the artifact as project-scoped files and metadata.
- UI previews the artifact in the existing iframe/file viewer model.
- User can refresh the artifact later without asking the agent to redesign it from scratch.
2. Key decision
2.1 Use skill + daemon tool endpoint, not MCP-first
Monet exposes connectors and live artifacts through a controller-owned tool registry. OD should not copy that exact runtime shape because OD's core architecture is different: OD delegates to external CLI agents such as Claude Code, Codex, Cursor Agent, Gemini CLI, OpenCode, and Qwen.
The agent-facing interface should therefore be:
skills/live-artifact/SKILL.md
↓ instructs the agent to call
daemon local HTTP endpoints or wrapper CLI commands
↓ backed by
daemon-owned connector + artifact services
↓ persisted as
project workspace files + metadata
MCP may be added later as a wrapper over the same daemon services, but it should not be the first or only interface.
Reasons:
- Multi-agent compatibility: every supported agent can read a skill and execute shell commands; MCP support varies by agent and CLI version.
- Lower migration cost: current daemon
/api/chatdoes not support per-run MCP binding. - Centralized safety: daemon endpoints can enforce project, path, connector, and output-size policies consistently.
- Skill-native product model: OD's extension point is already
skills/+SKILL.md, so live artifacts should feel like another OD capability, not a separate agent protocol.
2.2 Keep live artifacts distinct, but project-native
Live artifacts are a distinct persisted model integrated into the existing project UI. They must not be represented as a new static ArtifactKind in the existing artifact model, because they require ID-based identity, directory-shaped runtime storage, refresh/provenance history, connector permissions, locking, and server-rendered preview behavior.
Product terms:
- Design / project: the workspace container.
- Artifact: a static generated file inside a design.
- Live artifact: a refreshable, data-backed artifact inside a design.
- Connector: an external or local data source available to live artifacts.
Implementation boundaries:
- Keep dedicated live-artifact storage under
.live-artifacts/, dedicated/api/live-artifacts/*endpoints, and dedicated live-artifact DTOs inpackages/contracts. - Reuse the existing project scope, workspace tabs, file tree, viewer primitives, chat SSE stream, and API error envelope so live artifacts feel native without polluting the simple static artifact path.
- Do not expose
.live-artifacts/through generic project file APIs; all mutation should go through live-artifact or tool endpoints.
3. What to migrate from Monet
3.1 Concepts to preserve
From ~/Projects/monet:
- Static connector catalog plus dynamic connection status.
- Connector tool safety classification.
- Read-only-first connector policy.
- Live artifact / tile / source / provenance separation.
- HTML document template plus data-binding contract.
- Declarative output mapping from tool output to
data.json/ render models. - Strict render JSON validation.
- Refresh source validation before re-execution.
- Refresh audit trail with step-level status.
- Failure fallback: invalid refresh output should not blank the artifact.
3.2 Concepts to adapt
| Monet concept | OD adaptation |
|---|---|
Controller ToolRegistry |
Daemon service endpoints and optional CLI wrappers |
Chat tools create_live_artifact, update_live_artifact, list_live_artifacts |
Skill instructions that call od-tools live-artifacts ... or localhost daemon endpoints |
| Connector tools dynamically injected into tool registry | Connector catalog exposed through daemon endpoints; skill asks agent to query/use them explicitly |
| SQLite-first artifact storage | Project-scoped metadata files first; SQLite optional later if indexing becomes necessary |
| Controller-owned agent loop | External CLI agent loop; OD only injects skills and receives output/events |
3.3 Monet files used as source material
apps/controller/src/connectors/catalog.tsapps/controller/src/connectors/service.tsapps/controller/src/routes/connectors.tsapps/controller/src/tools/connectors.tsapps/controller/src/live-artifacts/schema.tsapps/controller/src/live-artifacts/render.tsapps/controller/src/live-artifacts/refresh.tsapps/controller/src/routes/live-artifacts.tsapps/controller/src/tools/live-artifacts.tsapps/controller/src/chat-storage.tsspecs/2026-04-27-live-artifacts/spec.md
4. Target architecture
┌──────────────────────────────────────────────────────────────────┐
│ Web App │
│ chat · artifact tree · live artifact list · refresh button │
│ iframe preview · source/provenance panels │
└───────────────┬──────────────────────────────────────────────────┘
│ HTTP/SSE
┌───────────────▼──────────────────────────────────────────────────┐
│ Local Daemon │
│ │
│ Agent session broker │
│ Skill registry │
│ Built-in tool endpoints │
│ /api/tools/live-artifacts/* │
│ /api/tools/connectors/* │
│ /api/connectors/* │
│ Artifact store │
│ Connector service │
│ Refresh runner + audit log │
└───────────────┬──────────────────────────────────────────────────┘
│ spawn / stdio
┌───────────────▼──────────────────────────────────────────────────┐
│ External Agent CLI │
│ Claude Code · Codex · Cursor Agent · Gemini CLI · OpenCode · Qwen │
│ │
│ Receives SKILL.md instructions and calls daemon tools via shell │
└──────────────────────────────────────────────────────────────────┘
5. User-facing skill shape
Add a built-in skill:
skills/live-artifact/
├── SKILL.md
├── references/
│ ├── artifact-schema.md
│ ├── connector-policy.md
│ └── refresh-contract.md
└── assets/
└── templates/
├── dashboard.html
└── report.html
5.1 SKILL.md frontmatter
---
name: live-artifact
description: |
Create refreshable, auditable Open Design artifacts backed by connector or local data.
Trigger when the user asks for live dashboards, refreshable reports, synced views, or reusable data-backed artifacts.
triggers:
- live artifact
- refreshable dashboard
- live report
- synced view
- 可刷新
- 实时看板
od:
mode: prototype
preview:
type: html
entry: index.html
reload: debounce-100
design_system:
requires: true
outputs:
primary: index.html
secondary:
- template.html
- artifact.json
- data.json
- provenance.json
capabilities_required:
- shell
- file_write
---
5.2 Skill body responsibilities
The skill should instruct the agent to:
- Determine whether the user wants a live artifact or a normal static artifact.
- Query available connectors and allowed read-only operations.
- Fetch from the named connected connector/source when available; ask for a data source only when no matching connected source exists, multiple candidates are equally plausible, or the request lacks any searchable topic/page/database clue.
- Create a safe render model, not raw provider output.
- Write
template.html,data.json,artifact.json, andprovenance.jsoninto the live artifact workspace directory; treatindex.htmlas derived preview output. - Register the artifact through daemon tooling.
- Include provenance and refresh source metadata.
- Never store credentials, raw OAuth responses, headers, cookies, or tokens.
5.3 Agent-callable command surface
Prefer a small od wrapper command over raw curl in the skill body:
od tools live-artifacts create --input artifact.json
od tools live-artifacts list --format compact
od tools live-artifacts update --artifact-id "$ID" --input artifact.json
od tools live-artifacts refresh --artifact-id "$ID"
od tools connectors list --format compact
od tools connectors execute --connector github --tool list_releases --input input.json
The wrapper should be implemented as TypeScript source under apps/daemon/src and call daemon endpoints using injected runtime values:
OD_DAEMON_URLOD_TOOL_TOKEN
The daemon injects these into the system prompt or skill preamble at runtime. The agent should not choose or override projectId; /api/tools/* derives project/run scope from OD_TOOL_TOKEN. If standalone JavaScript wrappers are later exposed, they must be generated build output from TypeScript source, not project-owned .js source files.
Raw HTTP is for developer debugging only and must include the run-scoped bearer token:
curl -s -X POST "$OD_DAEMON_URL/api/tools/live-artifacts/create" \
-H 'content-type: application/json' \
-H "authorization: Bearer $OD_TOOL_TOKEN" \
-d @artifact.json
6. Daemon API design
6.1 Connector endpoints
GET /api/connectors
GET /api/connectors/:connectorId
POST /api/connectors/:connectorId/connect
DELETE /api/connectors/:connectorId/connection
MVP may stub OAuth-backed connectors and start with local/read-only connectors, but the API should preserve Monet's split between catalog and connection status. OAuth callback routes are deferred until OAuth-backed connectors are implemented.
Connector response shape:
type ConnectorDetail = {
id: string;
label: string;
category: 'code' | 'docs' | 'files' | 'analytics' | 'custom';
status: 'available' | 'connected' | 'error' | 'disabled';
accountLabel?: string;
featuredTools: ConnectorToolSummary[];
allowedTools: ConnectorToolSummary[];
minimumApprovalPolicy: 'read_only_auto' | 'confirm_write' | 'disabled';
errorCode?: string;
};
6.2 Connector tool endpoints
Agent and refresh-runner connector execution must use the same daemon-owned execution path:
GET /api/tools/connectors/list
POST /api/tools/connectors/execute
/api/tools/connectors/list returns a compact list of connected, allowed, read-only-first tools for the current run token.
/api/tools/connectors/execute request:
type ConnectorExecuteRequest = {
connectorId: string;
toolName: string;
input: BoundedJsonObject;
purpose: 'agent_preview' | 'artifact_refresh';
};
Response:
type ConnectorExecuteResponse =
| {
ok: true;
connectorId: string;
accountLabel?: string;
toolName: string;
safety: ConnectorToolSafety;
output: BoundedJsonValue;
outputSummary?: string;
providerExecutionId?: string;
metadata?: BoundedJsonObject;
}
| ApiErrorResponse;
Execution rules:
- Require a valid
OD_TOOL_TOKENbound to the active run/project. - Reject tools that are not in the connector catalog allowlist.
- Re-classify tool safety at execution time; catalog metadata alone is not authorization.
- Reject
write,destructive, andunknowntools forartifact_refresh. - Bound output size before it is returned to the agent.
- Redact credentials and raw provider envelope fields before returning or persisting anything.
- Record
providerExecutionId, connector/account labels, and safety policy for provenance.
6.3 Live artifact endpoints
Agent/tool endpoints:
POST /api/tools/live-artifacts/create
GET /api/tools/live-artifacts/list
POST /api/tools/live-artifacts/update
POST /api/tools/live-artifacts/refresh
UI endpoints:
GET /api/live-artifacts?projectId=...
POST /api/live-artifacts
GET /api/live-artifacts/:artifactId
PATCH /api/live-artifacts/:artifactId
POST /api/live-artifacts/:artifactId/refresh
GET /api/live-artifacts/:artifactId/preview
The /api/tools/* endpoints are optimized for agent consumption: compact JSON, concise errors, and explicit machine-readable validation failures. They never accept an arbitrary projectId; project/run scope comes from OD_TOOL_TOKEN. The /api/live-artifacts/* endpoints are optimized for UI state and use the web app's normal project context.
Both endpoint families must call the same service-layer validation and storage code. Only authentication and response verbosity should differ; errors should share the ApiErrorResponse envelope from packages/contracts.
Agent-facing tool endpoints should reuse the shared API error envelope from packages/contracts/src/errors.ts instead of introducing a parallel error type:
type LiveArtifactToolResponse<TSuccess> = TSuccess | ApiErrorResponse;
Add live-artifact and connector-specific ApiErrorCode values such as TOOL_TOKEN_INVALID, TOOL_TOKEN_EXPIRED, CONNECTOR_NOT_CONNECTED, CONNECTOR_SAFETY_DENIED, REFRESH_LOCKED, REFRESH_TIMED_OUT, OUTPUT_TOO_LARGE, TEMPLATE_BINDING_INVALID, and REDACTION_REQUIRED. Validation details should live in the existing error details field so web, daemon, and tests share one error model.
7. Data model
7.1 Storage layout
Use project-scoped files under the daemon runtime data directory first. OD_DATA_DIR may override the default; otherwise <RUNTIME_DATA_DIR> is <repo>/.od:
<RUNTIME_DATA_DIR>/projects/<projectId>/
└── .live-artifacts/
└── <artifactId>/
├── artifact.json
├── template.html
├── index.html
├── data.json
├── provenance.json
├── refreshes.jsonl
└── snapshots/
└── <refreshId>/
├── data.json
└── provenance.json
The dot-prefixed .live-artifacts/ directory keeps implementation files out of the generic project file tree while preserving OD's file-first, inspectable-on-disk artifact philosophy. Add SQLite later only for cross-project indexing or high-volume refresh history.
index.html is a generated preview artifact, not the source of truth for refreshable data. The UI should load live artifacts through:
GET /api/live-artifacts/:artifactId/preview
The preview route may serve the stored index.html for static cases, but for refreshable HTML it should render template.html + data.json and apply iframe sandbox/CSP headers. snapshots/ should be hidden from the normal artifact tree unless the user explicitly opens refresh history.
7.2 Core types
type BoundedJsonValue =
| null
| boolean
| number
| string
| BoundedJsonValue[]
| { [key: string]: BoundedJsonValue };
type BoundedJsonObject = { [key: string]: BoundedJsonValue };
type LiveArtifact = {
schemaVersion: 1;
id: string;
projectId: string;
sessionId?: string;
createdByRunId?: string;
title: string;
slug: string;
status: 'active' | 'archived' | 'error';
pinned: boolean;
preview: {
type: 'html' | 'jsx' | 'markdown';
entry: string;
};
refreshStatus: 'never' | 'idle' | 'running' | 'succeeded' | 'failed';
createdAt: string;
updatedAt: string;
lastRefreshedAt?: string;
document?: LiveArtifactDocument;
};
type LiveArtifactDocument = {
format: 'html_template_v1';
templatePath: 'template.html';
generatedPreviewPath: 'index.html';
dataPath: 'data.json';
dataJson: BoundedJsonObject;
dataSchemaJson?: BoundedJsonObject;
sourceJson?: LiveArtifactSource;
};
type LiveArtifactSource = {
type: 'local_file' | 'daemon_tool' | 'connector_tool';
toolName?: string;
input: BoundedJsonObject;
connector?: {
connectorId: string;
accountLabel?: string;
toolName: string;
approvalPolicy: 'read_only_auto' | 'manual_refresh_granted_for_read_only';
};
outputMapping?: {
dataPaths?: Array<{ from: string; to: string }>;
transform?: 'identity' | 'compact_table' | 'metric_summary';
};
refreshPermission: 'none' | 'manual_refresh_granted_for_read_only';
};
type LiveArtifactProvenance = {
generatedAt: string;
generatedBy: 'agent' | 'refresh_runner';
notes?: string;
sources: Array<{
label: string;
type: 'connector' | 'local_file' | 'user_input' | 'derived';
ref?: string;
}>;
};
7.3 Validation rules
Port Monet's strict validation posture:
- Apply the shared bounded JSON constraints in
packages/contractsto every persisted or agent-suppliedBoundedJsonValue/BoundedJsonObject. - Reject keys such as
raw,rawResponse,payload,body,headers,cookie,authorization,token,secret,credential,password. - Redact suspicious source inputs before persistence.
- Reject source inputs that still contain credential-like values after redaction.
- HTML preview files must be generated from the document contract; refresh updates
data.json, not arbitrary script.
7.3.1 Shared bounded JSON constraints
The shared live-artifact JSON envelope is intentionally small enough to validate synchronously, store in project files, display in the UI, and include in agent-facing error details without leaking raw provider payloads.
Define and export these constants from packages/contracts as LIVE_ARTIFACT_BOUNDED_JSON_CONSTRAINTS:
| Constraint | Value | Applies to |
|---|---|---|
| Maximum object/array depth | 8 |
Any BoundedJsonValue; count the root object or array as depth 1. |
| Maximum object keys | 100 |
Any single object. |
| Maximum array length | 500 |
Any single array. |
| Maximum string length | 16 KiB |
Any single string value, measured in UTF-16 code units before persistence. |
| Maximum serialized payload size | 256 KiB |
Any complete bounded JSON document, measured as UTF-8 bytes of canonical JSON.stringify output. |
Validation must fail closed when a value exceeds any limit. Persisted files and create/update inputs must use the same limits so valid agent input remains valid after storage round-trips. Future connector-specific limits may be stricter, but must not exceed this shared envelope for values persisted into live artifact files.
7.4 HTML document model
MVP live HTML artifacts should use html_template_v1:
template.html + data.json → daemon render step → index.html / preview response
Rules:
template.htmlis authored by the agent during create/update.- Refreshable values must come from
data.json, not be hardcoded only in HTML. html_template_v1supports Mustache-style escaped interpolation plus a minimaldata-od-repeatstructural directive. It does not support arbitrary JavaScript, expression evaluation, helper functions, filters, partials, or raw HTML injection.- Refresh updates
data.jsonand snapshots. It does not let connector output rewrite arbitrary HTML. - If a presentation redesign is needed, the user should ask the agent to update the artifact; refresh is for data changes.
index.htmlmay be regenerated after successful refresh, but it is derived output.- The preview route must serve the document in a sandboxed iframe context with a restrictive CSP. External scripts are disallowed in MVP unless vendored and allowlisted.
7.4.1 html_template_v1 binding contract
MVP binding syntax is intentionally small and deterministic:
- Escaped interpolation:
{{data.path.to.value}}inserts a scalar value fromdata.json.- Paths must start with
dataand use dot-separated object keys, e.g.{{data.summary.title}}. - Numeric array indexes are allowed only as path segments, e.g.
{{data.metrics.0.value}}. - Keys must match
/^[A-Za-z_][A-Za-z0-9_-]*$/; bracket notation, computed paths, wildcards, function calls, and expressions are invalid. - Values render as strings.
nulland missing values render as an empty string. Objects and arrays are invalid interpolation targets except inside a supported repeat context.
- Paths must start with
- Repeat directive:
data-od-repeat="item in data.items"repeats the annotated element once for each object in an array.- The left side is a local alias matching
/^[A-Za-z_][A-Za-z0-9_]*$/. - The right side must be a
data.*path resolving to an array. - Inside the repeated element, interpolation may reference the local alias, e.g.
{{item.name}}, using the same path grammar. - Nested
data-od-repeatdirectives are disallowed in MVP. data-od-repeatis removed from the generated output.
- The left side is a local alias matching
- Conditional directives: none in MVP. Optional sections should be represented by empty strings, zero-length arrays, or separate agent-authored template variants during update.
- Attribute bindings: interpolation may appear in text nodes and ordinary HTML attribute values, but not in attribute names, tag names, comments,
<script>,<style>,<iframe srcdoc>, event-handler attributes such asonclick, or URL-bearing attributes that fail URL validation.
All interpolation is HTML-escaped by default:
- Text-node interpolation escapes
&,<,>,", and'. - Attribute interpolation escapes the same characters and must not be allowed to break out of the containing attribute.
- URL-bearing attributes such as
hrefandsrcmust resolve to allowedhttp:orhttps:URLs, root-relative paths, same-artifact relative asset paths, or fragments;javascript:,data:,blob:, and other unsupported schemes are rejected.
Raw / unescaped interpolation is explicitly forbidden in MVP:
- No triple braces such as
{{{data.html}}}. - No ampersand form such as
{{& data.html}}. - No
data-od-html,data-od-raw,data-od-bind-html, or equivalent raw insertion attributes. - No opt-out flag in artifact metadata or tool input.
The daemon must validate template.html before persistence and again before preview rendering. Validation failures must use the shared API error envelope with field/path details in details.
8. Runtime flows
8.1 Create flow
User asks for a live dashboard
↓
OD selects/injects live-artifact skill
↓
Agent queries connectors or local sources through daemon wrapper
↓
Agent writes template.html + artifact.json + data.json + provenance.json
↓
Agent calls live-artifacts create endpoint
↓
Daemon validates schemas, source metadata, file paths, and template binding
↓
Daemon stores artifact metadata and returns compact summary
↓
Web UI opens /api/live-artifacts/:artifactId/preview
8.2 Refresh flow
User clicks Refresh
↓
UI POST /api/live-artifacts/:id/refresh
↓
Daemon loads artifact.json and the refreshable document source
↓
For the document source:
- verify refreshPermission
- verify connector still connected
- verify tool is still read-only
- verify accountLabel/connectorId did not drift
- verify saved input matches current schema
- execute source
- map output through declarative outputMapping.dataPaths
- update candidate dataJson
- validate sanitized data
↓
Write refresh step to refreshes.jsonl
↓
If the refreshable source succeeds, commit data.json, snapshot, and regenerated preview
If it fails, keep previous data/preview, write failed refresh record, and surface the error
MVP refresh is artifact-level all-or-nothing.
Refresh runner requirements:
- Acquire a per-artifact refresh lock. Reject or queue concurrent refreshes.
- Assign a monotonic
refreshId; stale refreshes cannot overwrite newer committed data. - Enforce per-source and total refresh timeouts.
- Persist every step to
refreshes.jsonlwith status, duration, connector metadata, and compact error. - On daemon restart, recover refreshes stuck in
runningpast timeout asfailedand keep the last valid preview. - Write snapshots only after validation succeeds, or write failed attempts under a separate
snapshots/<refreshId>/failed/directory that is not used for preview.
8.3 Update flow
Agent or UI can update title, pinned status, archive status, preview entry, or non-source presentation fields. Updating sourceJson requires the same validation as create.
9. Connector strategy
9.1 Read-only v1
MVP should only expose read-only connector tools to automatic or refresh execution.
Write actions can exist later, but they must require explicit user confirmation and should not be refreshable.
Safety classification:
type ConnectorToolSafety = {
sideEffect: 'read' | 'write' | 'destructive' | 'unknown';
approval: 'auto' | 'confirm' | 'disabled';
reason: string;
};
Rules:
- OAuth scopes or names containing
write,create,update,delete,admin,send,post,manageimply write/confirm. - Destructive hints imply destructive/disabled for refresh.
- Explicit read-only hints can be read/auto.
- Unknown defaults to write/confirm, not read/auto.
9.2 Execute-time enforcement
Connector policy must be enforced at execution and refresh time, not only when the catalog is built:
- Catalog classification is metadata, not authorization.
/api/tools/connectors/executere-checks allowlist, current scopes, tool safety, and connector status.- Saved artifact policy cannot grant new permission by itself.
unknown,write, anddestructivetools are never refreshable.- If a previously read-only tool later appears write-capable because scopes or provider metadata changed, refresh must fail closed.
- A write action may be supported in the future with explicit confirmation, but it must not be stored as a refreshable source.
- Agent calls and refresh-runner calls must share the same connector execution service so audit and safety behavior cannot drift.
9.3 Credential storage
Default decision:
- OAuth connection state and credentials live outside project artifacts, under a daemon-controlled global store such as
~/.open-design/connectors/or an app database. - Project artifacts only store stable references:
connectorId,accountLabel, provider tool id/name, minimized input, and provenance. - Access tokens, refresh tokens, headers, cookies, OAuth state, and raw provider responses are never written under
<RUNTIME_DATA_DIR>/projects/<projectId>/.live-artifactsor any other project artifact directory. - Refresh resolves credentials through the daemon connector service at execution time.
- UI must show the connector/account label so users understand which global connection backs a project artifact.
9.4 Initial connector candidates
MVP can avoid OAuth complexity by shipping local daemon tools first:
project_files.searchproject_files.read_jsongit.summarygithub.public_repo_summaryusing unauthenticated public API or user-provided token later
OAuth-backed providers can follow the Monet pattern after the artifact pipeline is stable:
- GitHub
- Notion
- Google Drive
- Linear
10. UI changes
10.1 Entry header connector tab
Add a Connectors tab to the entry-header navigation. When selected, it should show cards for available external and local connectors.
Connector cards should include, as data becomes available:
- connector name and provider/category
- connection status
- connected account label, when connected
- connect, disconnect, or configure action
- available read-only tools/capabilities, when useful
This tab is a workspace-level connector management surface, not a separate project type. Phase 1B may show stubbed or local-only connector card data; full external connector status, connect/disconnect actions, and OAuth-backed flows belong to the connector phase.
10.2 New project live artifact entry
Add New live artifact to the new project tabs. Selecting it should start the normal project creation flow with live-artifact intent and the live-artifact skill path preselected or strongly hinted.
This creates a normal project/design whose first output is expected to be a live artifact. It should not create a separate top-level project type unless the product model changes later.
Live artifacts should also be listed in the existing Designs tab. The Designs tab should continue to show normal designs/projects, and it should additionally surface live artifacts as first-class selectable entries associated with their parent project/design. Live artifact entries should be visually distinguishable with Live / Refreshable status and should open directly into the project workspace with the corresponding live artifact selected. Parent design cards may also show a small live-artifact count or status summary.
10.3 Artifact tree
Show live artifacts as a first-class virtual group in the existing artifact tree, not as raw nested files. The tree should show one node per live artifact and hide implementation files such as snapshots/ by default.
ProjectView.tsx should fetch GET /api/live-artifacts?projectId=... alongside the existing project file fetch, then merge live artifacts into the workspace state as a discriminated item such as kind: 'live-artifact'. Use namespaced tab IDs such as live:<artifactId> so live artifacts cannot collide with file paths. FileWorkspace.tsx should render these items in a virtual group and route open/select actions to a live artifact viewer without treating artifact IDs as normal project file paths.
Badges:
LiveRefreshableRefreshing...Refresh failedArchived
10.4 Preview panel
Reuse existing iframe/file viewer where possible:
- Load
GET /api/live-artifacts/:artifactId/previewin the iframe instead of opening nested files directly. - Add read-only viewer toolbar tabs using the existing
FileViewer.tsx/HtmlViewertab pattern:Preview,Source,Data,Provenance,Refresh history. Do not require a new split-pane side panel in Phase 1B. - Show refresh button only when at least one tile is refreshable. When
refreshStatusisrunning, the button should be disabled and show a loading state to prevent duplicate refreshes.
Data sources for viewer tabs:
Source:artifact.jsonsourceJsonfields with credentials redacted.Data: currentdata.jsonand tile render summaries.Provenance:provenance.jsonand connector/account labels.Refresh history: parsedrefreshes.jsonl, newest first.
10.5 Chat integration
When an agent creates or updates a live artifact, the daemon should emit an agent/UI event similar to produced files so the UI can open it automatically. For MVP, extend the existing chat SSE stream in packages/contracts/src/sse/chat.ts rather than creating a second live-artifact SSE connection. apps/web/src/providers/daemon.ts should translate the SSE payload into a UI AgentEvent such as kind: 'live_artifact_update', and ProjectView.tsx should use that event to refresh the live artifact list and auto-open the artifact using the same open-request flow used for produced files.
Suggested event:
type LiveArtifactEvent = {
type: 'live_artifact_created' | 'live_artifact_updated' | 'live_artifact_refresh_completed';
artifactId: string;
title: string;
previewUrl?: string;
status: string;
};
11. Implementation plan
Phase 0 — Contracts first
- Add this spec.
- Add shared TypeScript DTOs under
packages/contracts, keeping the package pure TypeScript and free of Next.js, Express, filesystem/process APIs, browser APIs, SQLite, daemon internals, and sidecar control-plane dependencies. - Add shared contract DTOs for
LiveArtifact,LiveArtifactSource, and connector catalog entries. Runtime validation schemas belong under daemon source, especiallyapps/daemon/src/live-artifacts/schema.ts, and should consume or mirror the shared contract types without adding daemon internals topackages/contracts. - Add or update contract files such as:
packages/contracts/src/api/live-artifacts.tspackages/contracts/src/api/connectors.tspackages/contracts/src/sse/chat.tspackages/contracts/src/examples.tspackages/contracts/src/index.ts
- Add fixture artifacts under
specs/2026-04-29-live-artifacts/examples/. - Extend
packages/contracts/src/errors.tswith live artifact / connector error codes instead of defining a second error envelope. - Define
html_template_v1binding grammar and exampletemplate.html + data.json.
Exit criteria:
- Schemas reject raw provider response fields and credential-like values.
- Example artifact can be rendered from
template.html + data.jsonthrough the preview contract.
Phase 1A — Register static local live artifacts
- Implement daemon live artifact service.
- Implement project-scoped file storage under
<RUNTIME_DATA_DIR>/projects/<projectId>/.live-artifacts. - Add
/api/tools/live-artifacts/createandlist. - Add
GET /api/live-artifacts?projectId=...andGET /api/live-artifacts/:artifactId. - Add run-scoped
OD_TOOL_TOKENfor tool endpoints.
Exit criteria:
- A static
html_template_v1artifact can be registered without connectors or refresh. - The daemon rejects invalid paths, raw provider fields, and credential-like values.
Phase 1B — UI preview integration
- Add
GET /api/live-artifacts/:artifactId/preview. - Render
template.html + data.jsoninto a sandboxed iframe response. - Fetch live artifacts alongside project files and show them as virtual nodes in the artifact tree.
- Add a
LiveArtifactViewerpath inFileViewer.tsxthat reuses the existing HTML viewer toolbar/iframe patterns. - Add read-only
Preview,Source,Data,Provenance, andRefresh historyviewer tabs.
Exit criteria:
- UI can list and preview a registered live artifact.
- Preview does not require exposing
snapshots/or implementation files as normal project files.
Phase 1C — Built-in skill and wrapper command
- Add built-in
skills/live-artifact/SKILL.md. - Add
od tools live-artifacts ...and connector command handlers from TypeScript source underapps/daemon/src. - Inject daemon URL and short-lived tool token into skill preamble.
Exit criteria:
- Agent can create a live artifact using local data.
- UI can list and preview it.
- No MCP configuration is required.
Phase 2 — Refresh runner
- Add
refreshes.jsonlaudit log. - Implement manual refresh for local daemon tools.
- Implement per-artifact refresh lock, timeout, stale-write protection, and crash recovery.
- Preserve previous render on validation failure.
- Emit chat-stream UI refresh events and update viewer refresh loading/failure state.
Exit criteria:
- User can click Refresh and see updated data.
- Failed refresh leaves old preview intact and shows actionable error.
Phase 3 — Connector catalog and read-only connector tools
- Port Monet connector catalog/service shape.
- Add connector endpoints.
- Add
/api/tools/connectors/listand/api/tools/connectors/execute. - Add read-only tool classification.
- Add first real read-only connector.
- Extend
live-artifactskill references with connector usage instructions.
Exit criteria:
- Agent can query available connectors.
- Agent can create a refreshable artifact from a read-only connector.
- Refresh revalidates connector, account, tool, and approval policy.
Phase 4 — Optional MCP wrapper
- Confirmation after the skill + wrapper path: MCP is not needed for MVP correctness because all supported agents can use
SKILL.mdplusod tools ...wrappers, and Phase 1C/Phase 3 command surfaces cover live artifact creation, listing, update, refresh, connector listing, and read-only connector execution. MCP is only worth adding as an additive compatibility layer for agents with mature MCP support and must not replace, weaken, or fork the daemon-owned service/policy path. - Wrap the daemon's existing live artifact and connector services as an MCP server for agents that support MCP well.
- Do not make MCP required.
- Do not mutate global user MCP config automatically.
11.5.1 Optional MCP server design
The MCP integration, if added, should be a thin stdio adapter over the existing daemon tool endpoints, not a second tool implementation. The MCP process should be launched only for an agent run that explicitly supports MCP and receives the same injected runtime environment as the wrapper CLI:
MCP-capable agent
⇄ stdio MCP protocol
od mcp live-artifacts # TypeScript source under apps/daemon/src, built into the od bin
⇄ local HTTP with Authorization: Bearer $OD_TOOL_TOKEN
/api/tools/live-artifacts/* and /api/tools/connectors/*
⇄ daemon live artifact, refresh, connector, auth, validation, and policy services
Design constraints:
- Single policy path: the MCP server must call the existing
/api/tools/*endpoints usingOD_DAEMON_URLandOD_TOOL_TOKEN. It must not import store/service modules to bypass token scoping, connector policy, output redaction, rate limits, or route validation. - Run scoped: one MCP server instance is scoped to one agent run and one project through the bearer token. It exits when stdio closes; daemon token expiry/revocation remains authoritative.
- Equivalent tools only: expose MCP tools that mirror the CLI/API surface, with the same schemas and compact results:
od_live_artifacts_create→POST /api/tools/live-artifacts/createod_live_artifacts_list→GET /api/tools/live-artifacts/listod_live_artifacts_update→POST /api/tools/live-artifacts/updateod_live_artifacts_refresh→POST /api/tools/live-artifacts/refreshod_connectors_list→GET /api/tools/connectors/listod_connectors_execute→POST /api/tools/connectors/execute
- No project overrides: tool input schemas must not accept
projectId; project/run scope is always derived fromOD_TOOL_TOKENby daemon routes. - No global config mutation: OD may display or generate an ephemeral MCP launch descriptor for compatible agents, but must not edit user-level MCP config files automatically.
- No primary-path dependency:
SKILL.md,od tools ..., and raw-token debugging remain unchanged and continue to work when MCP is disabled or unsupported. - Typed implementation: project-owned MCP code should be TypeScript source under
apps/daemon/src(for exampleapps/daemon/src/mcp/live-artifacts-server.tsplus small CLI dispatch inapps/daemon/src/cli.ts). Any JavaScript entrypoint must be generated build output or an explicitly documented compatibility artifact.
MCP tool errors should translate daemon ApiErrorResponse values into MCP tool errors without expanding secret-bearing details. Validation field details may be included only when they are already safe to return from the corresponding /api/tools/* route.
Exit criteria:
- Claude Code or another MCP-capable agent can discover equivalent tools through MCP.
- Skill + CLI path still works unchanged.
12. File-level landing plan
Likely new files:
packages/contracts/src/api/live-artifacts.ts
packages/contracts/src/api/connectors.ts
packages/contracts/src/examples.ts
apps/daemon/src/live-artifacts/schema.ts
apps/daemon/src/live-artifacts/store.ts
apps/daemon/src/live-artifacts/render.ts
apps/daemon/src/live-artifacts/refresh.ts
apps/daemon/src/live-artifacts/routes.ts
apps/daemon/src/connectors/catalog.ts
apps/daemon/src/connectors/service.ts
apps/daemon/src/connectors/routes.ts
apps/daemon/src/tools/live-artifacts.ts
apps/daemon/src/tools/connectors.ts
skills/live-artifact/SKILL.md
skills/live-artifact/references/artifact-schema.md
skills/live-artifact/references/connector-policy.md
skills/live-artifact/references/refresh-contract.md
Likely touched files:
packages/contracts/src/errors.ts
packages/contracts/src/index.ts
packages/contracts/src/sse/chat.ts
apps/daemon/src/server.ts
apps/daemon/src/skills.ts
apps/daemon/src/cli.ts
apps/web/src/providers/daemon.ts
apps/web/src/providers/registry.ts
apps/web/src/components/EntryView.tsx
apps/web/src/components/NewProjectPanel.tsx
apps/web/src/components/ProjectView.tsx
apps/web/src/components/DesignsTab.tsx
apps/web/src/components/FileWorkspace.tsx
apps/web/src/components/FileViewer.tsx
apps/web/src/components/PreviewModal.tsx
apps/web/src/types.ts
apps/daemon/src/prompts/system.ts
Keep the first implementation small: current daemon route handlers live in apps/daemon/src/server.ts, so either mount live artifact routes there first or add small TypeScript route modules that are imported by server.ts. Do not add project-owned .js source files; JavaScript should only be generated build output or an explicitly documented compatibility artifact.
13. Security and trust model
13.1 Daemon must enforce
/api/tools/*requires a short-lived bearerOD_TOOL_TOKEN.- Tool tokens are minted per agent run and bind
runId,projectId, allowed endpoints, allowed operations, and expiry. /api/tools/*derives project/run scope from the token and rejects request-supplied project overrides.- CORS for local daemon tool endpoints is closed by default; UI endpoints use the web app's normal origin/session checks.
- Defend against CSRF and DNS rebinding on localhost endpoints.
- Project ID exists and maps to the active workspace.
- All file paths stay inside the project workspace.
- Tool output size is bounded.
- Snapshot/history size and retention are bounded.
- Refresh execution has timeout and cancellation.
- Connector credentials never reach agent output or artifact files.
- Source input is minimized and redacted.
- Read-only refreshes cannot drift into write-capable tools.
- Preview responses use iframe sandboxing and restrictive CSP.
13.2 Skill must instruct
- Do not paste raw connector responses into
artifact.json. - Do not store tokens, headers, cookies, or credentials.
- Prefer summaries, normalized rows, and derived metrics.
- Keep
data.jsoncompact and preview-oriented. - Use daemon tool endpoints for registration and refresh metadata.
- Use wrapper commands rather than constructing raw HTTP unless debugging.
13.3 UI must communicate
- Which connector/account backs the artifact.
- When it was last refreshed.
- Whether refresh is manual only.
- Why a refresh failed.
14. Non-goals
- No MCP-first implementation in MVP.
- No arbitrary write-capable connector refresh.
- No raw provider response storage.
- No multi-user auth model.
- No cloud-hosted connector broker in v1.
- No new canvas abstraction separate from OD's existing artifact/preview model.
15. Open questions
- Should
od tools ...be the only wrapper surface, or should generated per-project wrappers also be provided for easier agent access? - How should agent adapters advertise
shellavailability for skill gating? - How much refresh history should be retained before compaction?
- Should failed refresh attempt payloads be retained in a hidden failed snapshot directory, or only summarized in
refreshes.jsonl?
16. Acceptance criteria
- A built-in
live-artifactskill can be discovered by the existing skill registry. - An agent can create a live artifact without MCP.
- The daemon validates and persists live artifact metadata.
- The UI can list and preview the artifact.
- Manual refresh works for at least one local read-only source.
- Refresh failures are audited and do not destroy the last valid preview.
- Connector-backed refresh is read-only and revalidated before every run.
/api/tools/*calls require run-scoped auth and cannot override project scope.- No persisted artifact fixture contains raw credentials, headers, cookies, or full provider payloads.
17. Recommended first slice
Implement the smallest useful vertical slice:
skills/live-artifact/SKILL.mdpackages/contracts/src/api/live-artifacts.tsapps/daemon/src/live-artifacts/schema.tsapps/daemon/src/live-artifacts/store.tsPOST /api/tools/live-artifacts/createGET /api/live-artifacts?projectId=...GET /api/live-artifacts/:artifactId/preview- UI list + virtual live-artifact node + sandboxed preview
This proves the skill-based interface and storage model before adding connectors, OAuth, refresh runner complexity, or MCP wrappers.