Some checks failed
ci / Validate workspace (push) Successful in 12m32s
landing-page-ci / Validate landing page (push) Successful in 9m41s
landing-page-deploy / Deploy landing page (push) Failing after 5m23s
github-metrics / Generate repository metrics SVG (push) Failing after 2m3s
refresh-contributors-wall / Refresh contributors wall cache bust (push) Failing after 11s
This repository contains the open-design daemon CLI source code, built and packaged at https://helix-mind.ai/cli/open-design/latest.tgz for use by the HelixMind /design slash command. Licenses: Apache-2.0 (root) + MIT (skills/*)
988 lines
47 KiB
Markdown
988 lines
47 KiB
Markdown
# Live Artifacts via Agent Skills
|
|
|
|
**Status:** Draft · 2026-04-29
|
|
**Parent:** [`docs/spec.md`](../../docs/spec.md)
|
|
**Siblings:** [`docs/skills-protocol.md`](../../docs/skills-protocol.md) · [`docs/agent-adapters.md`](../../docs/agent-adapters.md) · [`docs/modes.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:
|
|
|
|
1. User chats with the selected agent.
|
|
2. Agent uses a skill to plan and create a live artifact.
|
|
3. OD persists the artifact as project-scoped files and metadata.
|
|
4. UI previews the artifact in the existing iframe/file viewer model.
|
|
5. 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:
|
|
|
|
```text
|
|
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/chat` does 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 in `packages/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.ts`
|
|
- `apps/controller/src/connectors/service.ts`
|
|
- `apps/controller/src/routes/connectors.ts`
|
|
- `apps/controller/src/tools/connectors.ts`
|
|
- `apps/controller/src/live-artifacts/schema.ts`
|
|
- `apps/controller/src/live-artifacts/render.ts`
|
|
- `apps/controller/src/live-artifacts/refresh.ts`
|
|
- `apps/controller/src/routes/live-artifacts.ts`
|
|
- `apps/controller/src/tools/live-artifacts.ts`
|
|
- `apps/controller/src/chat-storage.ts`
|
|
- `specs/2026-04-27-live-artifacts/spec.md`
|
|
|
|
## 4. Target architecture
|
|
|
|
```text
|
|
┌──────────────────────────────────────────────────────────────────┐
|
|
│ 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:
|
|
|
|
```text
|
|
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
|
|
|
|
```yaml
|
|
---
|
|
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:
|
|
|
|
1. Determine whether the user wants a live artifact or a normal static artifact.
|
|
2. Query available connectors and allowed read-only operations.
|
|
3. 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.
|
|
4. Create a safe render model, not raw provider output.
|
|
5. Write `template.html`, `data.json`, `artifact.json`, and `provenance.json` into the live artifact workspace directory; treat `index.html` as derived preview output.
|
|
6. Register the artifact through daemon tooling.
|
|
7. Include provenance and refresh source metadata.
|
|
8. 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:
|
|
|
|
```bash
|
|
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_URL`
|
|
- `OD_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:
|
|
|
|
```bash
|
|
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
|
|
|
|
```http
|
|
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:
|
|
|
|
```ts
|
|
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:
|
|
|
|
```http
|
|
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:
|
|
|
|
```ts
|
|
type ConnectorExecuteRequest = {
|
|
connectorId: string;
|
|
toolName: string;
|
|
input: BoundedJsonObject;
|
|
purpose: 'agent_preview' | 'artifact_refresh';
|
|
};
|
|
```
|
|
|
|
Response:
|
|
|
|
```ts
|
|
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_TOKEN` bound 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`, and `unknown` tools for `artifact_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:
|
|
|
|
```http
|
|
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:
|
|
|
|
```http
|
|
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:
|
|
|
|
```ts
|
|
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`:
|
|
|
|
```text
|
|
<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:
|
|
|
|
```http
|
|
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
|
|
|
|
```ts
|
|
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/contracts` to every persisted or agent-supplied `BoundedJsonValue` / `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`:
|
|
|
|
```text
|
|
template.html + data.json → daemon render step → index.html / preview response
|
|
```
|
|
|
|
Rules:
|
|
|
|
- `template.html` is authored by the agent during create/update.
|
|
- Refreshable values must come from `data.json`, not be hardcoded only in HTML.
|
|
- `html_template_v1` supports **Mustache-style escaped interpolation plus a minimal `data-od-repeat` structural directive**. It does not support arbitrary JavaScript, expression evaluation, helper functions, filters, partials, or raw HTML injection.
|
|
- Refresh updates `data.json` and 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.html` may 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 from `data.json`.
|
|
- Paths must start with `data` and 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. `null` and missing values render as an empty string. Objects and arrays are invalid interpolation targets except inside a supported repeat context.
|
|
- **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-repeat` directives are disallowed in MVP.
|
|
- `data-od-repeat` is removed from the generated output.
|
|
- **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 as `onclick`, 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 `href` and `src` must resolve to allowed `http:` or `https:` 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
|
|
|
|
```text
|
|
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
|
|
|
|
```text
|
|
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.jsonl` with status, duration, connector metadata, and compact error.
|
|
- On daemon restart, recover refreshes stuck in `running` past timeout as `failed` and 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:
|
|
|
|
```ts
|
|
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`, `manage` imply 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/execute` re-checks allowlist, current scopes, tool safety, and connector status.
|
|
- Saved artifact policy cannot grant new permission by itself.
|
|
- `unknown`, `write`, and `destructive` tools 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-artifacts` or 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.search`
|
|
- `project_files.read_json`
|
|
- `git.summary`
|
|
- `github.public_repo_summary` using 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:
|
|
|
|
- `Live`
|
|
- `Refreshable`
|
|
- `Refreshing...`
|
|
- `Refresh failed`
|
|
- `Archived`
|
|
|
|
### 10.4 Preview panel
|
|
|
|
Reuse existing iframe/file viewer where possible:
|
|
|
|
- Load `GET /api/live-artifacts/:artifactId/preview` in the iframe instead of opening nested files directly.
|
|
- Add read-only viewer toolbar tabs using the existing `FileViewer.tsx` / `HtmlViewer` tab 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 `refreshStatus` is `running`, the button should be disabled and show a loading state to prevent duplicate refreshes.
|
|
|
|
Data sources for viewer tabs:
|
|
|
|
- `Source`: `artifact.json` `sourceJson` fields with credentials redacted.
|
|
- `Data`: current `data.json` and tile render summaries.
|
|
- `Provenance`: `provenance.json` and connector/account labels.
|
|
- `Refresh history`: parsed `refreshes.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:
|
|
|
|
```ts
|
|
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, especially `apps/daemon/src/live-artifacts/schema.ts`, and should consume or mirror the shared contract types without adding daemon internals to `packages/contracts`.
|
|
- Add or update contract files such as:
|
|
- `packages/contracts/src/api/live-artifacts.ts`
|
|
- `packages/contracts/src/api/connectors.ts`
|
|
- `packages/contracts/src/sse/chat.ts`
|
|
- `packages/contracts/src/examples.ts`
|
|
- `packages/contracts/src/index.ts`
|
|
- Add fixture artifacts under `specs/2026-04-29-live-artifacts/examples/`.
|
|
- Extend `packages/contracts/src/errors.ts` with live artifact / connector error codes instead of defining a second error envelope.
|
|
- Define `html_template_v1` binding grammar and example `template.html + data.json`.
|
|
|
|
Exit criteria:
|
|
|
|
- Schemas reject raw provider response fields and credential-like values.
|
|
- Example artifact can be rendered from `template.html + data.json` through 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/create` and `list`.
|
|
- Add `GET /api/live-artifacts?projectId=...` and `GET /api/live-artifacts/:artifactId`.
|
|
- Add run-scoped `OD_TOOL_TOKEN` for tool endpoints.
|
|
|
|
Exit criteria:
|
|
|
|
- A static `html_template_v1` artifact 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.json` into a sandboxed iframe response.
|
|
- Fetch live artifacts alongside project files and show them as virtual nodes in the artifact tree.
|
|
- Add a `LiveArtifactViewer` path in `FileViewer.tsx` that reuses the existing HTML viewer toolbar/iframe patterns.
|
|
- Add read-only `Preview`, `Source`, `Data`, `Provenance`, and `Refresh history` viewer 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 under `apps/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.jsonl` audit 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/list` and `/api/tools/connectors/execute`.
|
|
- Add read-only tool classification.
|
|
- Add first real read-only connector.
|
|
- Extend `live-artifact` skill 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.md` plus `od 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:
|
|
|
|
```text
|
|
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 using `OD_DAEMON_URL` and `OD_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/create`
|
|
- `od_live_artifacts_list` → `GET /api/tools/live-artifacts/list`
|
|
- `od_live_artifacts_update` → `POST /api/tools/live-artifacts/update`
|
|
- `od_live_artifacts_refresh` → `POST /api/tools/live-artifacts/refresh`
|
|
- `od_connectors_list` → `GET /api/tools/connectors/list`
|
|
- `od_connectors_execute` → `POST /api/tools/connectors/execute`
|
|
- **No project overrides:** tool input schemas must not accept `projectId`; project/run scope is always derived from `OD_TOOL_TOKEN` by 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 example `apps/daemon/src/mcp/live-artifacts-server.ts` plus small CLI dispatch in `apps/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:
|
|
|
|
```text
|
|
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:
|
|
|
|
```text
|
|
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/web/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 bearer `OD_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.json` compact 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
|
|
|
|
1. Should `od tools ...` be the only wrapper surface, or should generated per-project wrappers also be provided for easier agent access?
|
|
2. How should agent adapters advertise `shell` availability for skill gating?
|
|
3. How much refresh history should be retained before compaction?
|
|
4. 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-artifact` skill 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:
|
|
|
|
1. `skills/live-artifact/SKILL.md`
|
|
2. `packages/contracts/src/api/live-artifacts.ts`
|
|
3. `apps/daemon/src/live-artifacts/schema.ts`
|
|
4. `apps/daemon/src/live-artifacts/store.ts`
|
|
5. `POST /api/tools/live-artifacts/create`
|
|
6. `GET /api/live-artifacts?projectId=...`
|
|
7. `GET /api/live-artifacts/:artifactId/preview`
|
|
8. 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.
|