artifacty 0.13.1 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,652 @@
1
+ # Supporting Claude Formats and Codex Continuation Artifacts
2
+
3
+ Status: Implemented. Phases 0-6 are complete. The v10 section records the
4
+ Mermaid CORS fix and browser smoke-test evidence.
5
+
6
+ This document records how Artifacty imports, stores, and renders Claude formats
7
+ and Codex continuation artifacts. The implementation preserves four rules:
8
+
9
+ - Route shared behavior through the shared library.
10
+ - Append immutable versions.
11
+ - Serve assets locally.
12
+ - Treat artifact content as untrusted.
13
+
14
+ The starting model, gap analysis, design, and phased plan describe the original
15
+ proposal. Dated implementation sections record the work completed at each stage.
16
+
17
+ ## Goal
18
+
19
+ Users can import Claude artifacts through MCP `artifacty_import`, CLI `import`,
20
+ or `POST /api/import`. Artifacty preserves their source and makes them available
21
+ for listing, search, and browser viewing. The viewer uses local assets and keeps
22
+ untrusted content inside the existing sandbox.
23
+
24
+ Artifacty also imports Codex continuation artifacts as handoffs, bundles, reviews,
25
+ diff walkthroughs, and verification reports. Converters avoid unsupported
26
+ assumptions about the source.
27
+
28
+ ## Background: Claude artifact types
29
+
30
+ Claude emits a small set of artifact types. Each has a MIME-like `type`
31
+ identifier. Code artifacts also have a `language` field.
32
+
33
+ | Claude type | Identifier | Extra fields | What it is |
34
+ |--------------------|----------------------------------|--------------|------------|
35
+ | Markdown / document| `text/markdown` | — | Formatted prose |
36
+ | Code | `application/vnd.ant.code` | `language` | Syntax-highlighted source, not executed |
37
+ | HTML page | `text/html` | — | Single-file web page, rendered (inline CSS/JS) |
38
+ | SVG image | `image/svg+xml` | — | Vector graphic |
39
+ | Mermaid diagram | `application/vnd.ant.mermaid` | — | Diagram described in Mermaid source |
40
+ | React component | `application/vnd.ant.react` | — | Interactive JSX component |
41
+
42
+ > These identifiers depend on an external product. Before implementing converter
43
+ > mappings, verify the identifiers against current Claude documentation. This
44
+ > table describes the intended mapping, not a fixed external contract.
45
+
46
+ Two import shapes matter:
47
+
48
+ 1. **Structured payload** (preferred): the caller supplies the artifact `type`,
49
+ `language`, and raw source. This preserves the source without loss. MCP and
50
+ CLI callers should use this format.
51
+ 2. **Browser-saved HTML**: "Save page as…" includes the claude.ai interface and
52
+ an inner `frame.claudeusercontent.com` iframe. Existing imported artifacts
53
+ used this method. It loses the original `type`; only HTML and SVG remain as
54
+ raw markup. Detection must fall back to content inspection.
55
+
56
+ ## Codex artifact categories
57
+
58
+ Codex output does not use the same small MIME-like taxonomy as Claude artifacts.
59
+ For Artifacty, treat Codex output as work products from repository changes. These
60
+ include source files, diffs, handoffs, reviews, verification evidence, and local
61
+ previews. Set `sourceAgent` to `codex`. Use `artifactType` to describe how the next
62
+ agent will use the artifact.
63
+
64
+ | Codex output | Recommended Artifacty type | Format | What to preserve |
65
+ |--------------|----------------------------|--------|------------------|
66
+ | Source or file bundle | `bundle` | `json` | Paths, file contents, content types, hashes, and optional patch metadata |
67
+ | Patch / diff walkthrough | `diff-walkthrough` | `markdown` or `text` | Git diff, changed-file list, rationale, and migration notes |
68
+ | Handoff note | `handoff` | `markdown` | Goal, current state, changed files, decisions, blockers, and next steps |
69
+ | Code review result | `code-review` | `markdown` | Findings ordered by severity, file/line references, open questions, and test gaps |
70
+ | Test / verification report | `test-report` | `markdown`, `text`, or `json` | Commands run, pass/fail status, key output, environment, and residual risk |
71
+ | Plan / design document | `document` or `design-option` | `markdown` | Architecture options, sequencing, tradeoffs, and acceptance criteria |
72
+ | HTML/dashboard preview | `html-page` or `dashboard` | `html` | Self-contained local preview rendered in the existing sandboxed iframe |
73
+ | Logs / terminal transcript | `document` | `text` | Relevant command output, stack traces, and reproduction notes |
74
+
75
+ Most Codex continuation artifacts use the existing Markdown, JSON, text, or HTML
76
+ renderer. A separate renderer is needed only when the payload contains source
77
+ for the `code`, `svg`, `mermaid`, or `react` formats.
78
+
79
+ Prioritize **`handoff`**, **`bundle`**, **`diff-walkthrough`**, **`code-review`**,
80
+ and **`test-report`** for Codex support. These types explain what changed, why it
81
+ changed, how to verify it, and what the next agent should do.
82
+
83
+ Practical import shapes for Codex:
84
+
85
+ 1. **Direct Artifacty publish** — Codex calls MCP/CLI with `sourceAgent: "codex"`,
86
+ explicit `artifactType`, and Markdown/JSON content.
87
+ 2. **File bundle import** — Codex uses or extends Artifacty's existing
88
+ `files[]`/`bundle` conversion path by packaging generated or modified files;
89
+ Artifacty stores them as a portable `bundle` JSON artifact.
90
+ 3. **Handoff JSON** — Codex exports a structured object containing `title`,
91
+ `content` or `markdown`, `artifactType: "handoff"`, `tags`, and optional
92
+ verification metadata.
93
+ 4. **Patch-oriented handoff** — Codex includes a unified diff plus a prose summary;
94
+ Artifacty stores it as `diff-walkthrough` unless full files are included, in
95
+ which case use `bundle`.
96
+
97
+ Codex conversion should not assume that every Markdown note is a handoff. It
98
+ should not assume that a diff is safe to apply or an HTML preview is trusted.
99
+ Preserve the source. Record its origin under `metadata.artifactyImport`. The
100
+ viewer must continue to treat the content as untrusted.
101
+
102
+ ## Starting Artifacty model (before Phase 0)
103
+
104
+ - **Formats** are normalized to one of four values in `src/lib/storage.js`
105
+ (`FORMAT_TO_EXTENSION`, `FORMAT_TO_CONTENT_TYPE`, `normalizeFormat`):
106
+ `html | markdown | text | json`. `md` aliases to `markdown`. Max 16 MiB.
107
+ - **`artifactType`** is a *separate*, descriptive taxonomy (not a renderer):
108
+ `document, html-page, handoff, code-review, test-report, dashboard,
109
+ design-option, diff-walkthrough, bundle, asset, unknown`.
110
+ - **Rendering** lives in `src/lib/render.js` `renderContent(format, content)`:
111
+ - `html` → sandboxed `<iframe srcdoc>` with `sandbox="allow-scripts allow-forms
112
+ allow-popups"` (no `allow-same-origin`) plus a height-reporter that lets the
113
+ parent auto-size the frame (`htmlFrameContent` + `frameResizeScript`).
114
+ - `markdown` → in-house `markdownToHtml` into `.artifact-doc`.
115
+ - `json` / `text` → escaped `<pre><code>`.
116
+ - **Detection** lives in `src/lib/converters.js` `detectFormat(...)` (content-type,
117
+ extension, then content sniffing) and `normalizeArtifactType(...)`.
118
+ - **Client assets are vendored locally**, never from a CDN: the `/vendor/npm/<pkg>`
119
+ route in `src/server.js` serves ESM from `node_modules` for an allowlist in
120
+ `src/lib/editor-assets.js` (`EDITOR_VENDOR_PACKAGES`), wired through an importmap.
121
+ The editor already vendors the CodeMirror 6 family this way.
122
+ - **CSP** (`sendHtml` in `src/server.js`) is local-only:
123
+ `default-src 'self' data: blob:; frame-src 'self' data: blob:;
124
+ img-src 'self' data: blob:; style-src 'self' 'unsafe-inline';
125
+ script-src 'self' 'unsafe-inline'; object-src 'none'; base-uri 'none';
126
+ form-action 'self'`. A `srcdoc` iframe **inherits this CSP**, so artifact code may
127
+ only use inline + same-origin (`/vendor/npm/*`) resources — never a CDN.
128
+
129
+ ## Gap analysis (before Phase 0)
130
+
131
+ | Claude type | Today | Target |
132
+ |-------------|-------|--------|
133
+ | Markdown | Supported (`markdown`) | Keep; optionally upgrade the renderer (tables, fenced-lang) |
134
+ | HTML | Supported (`html`, sandboxed iframe + auto-resize) | Keep as-is |
135
+ | Code | Falls back to `text` (no language, no highlight) | New `code` format with `language` metadata + read-only highlight (reuse vendored CodeMirror) |
136
+ | SVG | Sniffs to `text` (shown as source) | New `svg` format, rendered as an image in a **scriptless** sandbox |
137
+ | Mermaid | `text` (source only) | New `mermaid` format, rendered to SVG by a vendored Mermaid runtime in a sandboxed iframe |
138
+ | React | `text` (source only) | New `react` format, compiled + run in a sandboxed iframe with vendored React/ReactDOM + a JSX transform |
139
+
140
+ By v10, Phases 0-6 had closed these storage, conversion, and viewer gaps. React
141
+ execution remains opt-in.
142
+
143
+ ## Design
144
+
145
+ ### 1. Format taxonomy
146
+
147
+ Extend the four normalized formats to include the formats that need distinct
148
+ renderers. For each new format, update `storage.js` and every public interface.
149
+ Add a regression test. Apply the shared-library rule from `CLAUDE.md`:
150
+
151
+ - `storage.js`: add to `FORMAT_TO_EXTENSION`, `FORMAT_TO_CONTENT_TYPE`,
152
+ `normalizeFormat` (and any alias, e.g. `jsx → react`, `svg+xml → svg`).
153
+ - `converters.js`: map Claude identifiers and sniff fallbacks in `detectFormat`.
154
+ - `render.js`: a `renderContent` branch per format.
155
+ - MCP (`mcp-server.js`): extend the `format` enum on the relevant tool schemas.
156
+ - CLI (`cli.js`): no enum to change, but document the new `--format` values.
157
+ - Browser editor (`render.js` `formatSelect`, `editor-assets.js` language modes):
158
+ add the option and a CodeMirror language mode where one exists.
159
+ - Badges (`render.js`): add `.badge.f-svg/.f-mermaid/.f-react/.f-code` colors.
160
+
161
+ Proposed new formats: **`code`, `svg`, `mermaid`, `react`**. Keep `language`
162
+ (for `code`) in the version record `metadata`, not as a new column.
163
+
164
+ Rejected alternative: keep four formats and select the renderer through
165
+ `artifactType` + `metadata.renderer`. The existing `format` field already selects
166
+ the renderer in `renderContent`. MCP, CLI, and the editor also use it. A second
167
+ selection field would make the contract ambiguous.
168
+
169
+ ### 2. Detection / conversion (`converters.js`)
170
+
171
+ Priority order in `convertAgentArtifact` → `detectFormat`:
172
+
173
+ 1. Explicit `format` argument (caller override) wins.
174
+ 2. Structured payload `type`/`mimeType` → map identifier to format:
175
+ - `text/markdown` → `markdown`
176
+ - `text/html` → `html`
177
+ - `image/svg+xml` → `svg`
178
+ - `application/vnd.ant.code` → `code` (carry `language` into metadata)
179
+ - `application/vnd.ant.mermaid` → `mermaid`
180
+ - `application/vnd.ant.react` → `react`
181
+ 3. Filename extension (`.svg`, `.jsx`/`.tsx`, `.mmd`).
182
+ 4. Content sniffing fallback (for browser-saved/raw input):
183
+ - starts with `<svg` or `<?xml … <svg` → `svg`
184
+ - first non-comment line matches a Mermaid graph keyword
185
+ (`graph`, `flowchart`, `sequenceDiagram`, `classDiagram`, `stateDiagram`,
186
+ `erDiagram`, `gantt`, `pie`, `mindmap`, `journey`) → `mermaid`
187
+ - contains JSX/`export default`/`import React` signals → `react`
188
+ - existing html/markdown/json/text rules otherwise.
189
+
190
+ Record the original agent, Claude `type`, and `language` under
191
+ `metadata.artifactyImport`. This makes the mapping auditable.
192
+
193
+ ### 3. Rendering pipeline (`render.js renderContent`)
194
+
195
+ The **artifact source** is the source of truth: JSX, Mermaid, SVG, or code text.
196
+ Render it when the user opens the viewer. Never store compiled output. `/raw`
197
+ returns the original bytes, and immutable version files preserve the source.
198
+
199
+ Use the existing sandboxed iframe pattern for executable or graphical content.
200
+ It resizes the frame and contains untrusted code. Inert code text needs no frame:
201
+
202
+ - **`code`** → render read-only highlighted source. Reuse the vendored CodeMirror
203
+ language modes (`@codemirror/lang-*`) in a read-only EditorView, or a lighter
204
+ static highlighter. No iframe needed (it is inert text); escape as today but with
205
+ token spans. Show the `language` label in the meta strip.
206
+ - **`svg`** → embed the SVG in a sandboxed iframe **without** `allow-scripts`
207
+ (SVG can carry `<script>`/`on*` handlers). Optionally also strip script nodes and
208
+ event-handler attributes server-side as defense-in-depth. Center it, fit width.
209
+ - **`mermaid`** → a sandboxed iframe (`allow-scripts`, no `allow-same-origin`) whose
210
+ `srcdoc` contains: an importmap to the vendored Mermaid module, an inline boot
211
+ script that calls `mermaid.render(source)`, and the height reporter. The Mermaid
212
+ source is injected as a data island, not interpolated into script.
213
+ - **`react`** → a sandboxed iframe (`allow-scripts`, no `allow-same-origin`) that
214
+ imports vendored React + ReactDOM and a JSX transform, compiles the component
215
+ source at runtime, and mounts it. Same height reporter. This is the most
216
+ security-sensitive and heaviest path (see Security).
217
+
218
+ Reuse the existing HTML parent listener (`frameResizeScript`) and reporter
219
+ injection for SVG, Mermaid, and React frames.
220
+
221
+ ### 4. Vendored client assets (`editor-assets.js`)
222
+
223
+ Add new runtimes to the allowlist and route mechanism that serves CodeMirror.
224
+ This keeps assets on the same origin and within the CSP:
225
+
226
+ - Mermaid: `mermaid` (note: this is a **large** ESM bundle).
227
+ - React: `react`, `react-dom`, plus a JSX transform. Prefer a small standalone JSX
228
+ transformer over full Babel if feasible; Babel-standalone is large.
229
+ - Add each to `EDITOR_VENDOR_PACKAGES` (or a parallel `RENDER_VENDOR_PACKAGES`) and,
230
+ if their entry point differs, to `EDITOR_VENDOR_ENTRY_OVERRIDES`.
231
+
232
+ These packages become `dependencies` in `package.json`. CodeMirror had already
233
+ changed the original zero-runtime-dependency policy. The phased proposal keeps
234
+ the larger additions opt-in.
235
+
236
+ ### 5. Storage & schema
237
+
238
+ - No new columns. `language` and any render hint go in the version
239
+ `metadata` JSON. `format` gains the new enum values.
240
+ - `artifactType` mapping suggestions: `mermaid`/`svg` → `asset` or a new
241
+ `diagram`; `react` → `design-option` or a new `component`; `code` → `code-review`
242
+ is wrong semantically, prefer a new `snippet`/`code` artifactType. Decide as part
243
+ of the taxonomy review (Open Questions).
244
+
245
+ ## Security considerations
246
+
247
+ The viewer renders **untrusted** content. Apply these security requirements:
248
+
249
+ - Keep CSP local-only. Vendored libs are served from `/vendor/npm/*` (same origin);
250
+ no CDN, no `unsafe-eval` unless a renderer strictly requires it (a JSX transform
251
+ may — scope it to the artifact iframe via a per-iframe CSP, not the whole app).
252
+ - Keep `allow-same-origin` **off** for any scripted artifact iframe (Mermaid,
253
+ React, HTML). Without it the iframe is an opaque origin and cannot touch the
254
+ store, cookies, or `server.json`; cross-frame messaging stays `postMessage`-only
255
+ and the parent validates `event.source` (already done).
256
+ - SVG renders **scriptless**; additionally sanitize `<script>` and `on*` attributes
257
+ server-side.
258
+ - React/JSX runs arbitrary author code. If a runtime needs `eval`/`new Function`,
259
+ confine it with a dedicated, tighter CSP applied to that iframe's `srcdoc`, and
260
+ document the residual risk. Consider gating `react` rendering behind an explicit
261
+ opt-in (config flag) given the blast radius.
262
+ - Enforce `MAX_ARTIFACT_BYTES` and the existing secret-scan before persisting, for
263
+ all new formats.
264
+
265
+ ## Phased plan
266
+
267
+ Start with stable contracts. Then add renderers in order of increasing risk.
268
+ Each phase must be independently shippable and include regression tests. Each
269
+ phase must preserve the source and derive rendered output at view time.
270
+
271
+ ### Phase 0 — Contract lock and fixtures
272
+
273
+ **Goal:** freeze the target behavior before adding new renderers.
274
+
275
+ - Confirm Claude artifact identifiers and the exact `language` field shape against
276
+ current Claude documentation or exported payloads.
277
+ - Add fixture files for every target input: Claude markdown, code, HTML, SVG,
278
+ Mermaid, React, plus Codex handoff, bundle, diff, review, and test report.
279
+ - Add failing regression tests for converter mapping, storage round-trip, MCP tool
280
+ schemas, browser rendering containers, and `/raw` source fidelity.
281
+ - Decide the taxonomy additions before implementation: either reuse existing
282
+ `artifactType` values or add `diagram`, `component`, and `snippet`.
283
+
284
+ **Exit criteria:** tests describe all target formats. They expose the current
285
+ gaps without changing production behavior.
286
+
287
+ ### Phase 1 — Schema, storage, and surface plumbing
288
+
289
+ **Goal:** let every API surface accept the new formats without rendering them yet.
290
+
291
+ - Extend `storage.js` format normalization, extensions, and content types for
292
+ `code`, `svg`, `mermaid`, and `react`.
293
+ - Extend `artifactType` validation if the taxonomy decision adds new values.
294
+ - Update MCP schemas, CLI help text, browser form options, badges, i18n labels,
295
+ and schema documentation.
296
+ - Store `language`, source MIME type, and converter provenance in version metadata.
297
+ - Keep unknown/unsupported legacy values mapped to `unknown` during import rather
298
+ than rejecting external artifacts.
299
+
300
+ **Exit criteria:** all surfaces can create, update, import, list, and read the new
301
+ formats, even if the viewer still falls back to escaped source rendering.
302
+
303
+ ### Phase 0-1 implementation status (2026-06-24)
304
+
305
+ Implemented in the repository:
306
+
307
+ - Added storage-level `ARTIFACT_FORMATS` for `code`, `svg`, `mermaid`, and
308
+ `react` alongside the existing `html`, `markdown`, `text`, and `json` formats.
309
+ - Added `diagram`, `component`, and `snippet` to the `artifactType` taxonomy.
310
+ - Added converter coverage for Claude structured payload identifiers, SVG/Mermaid
311
+ filename and content sniffing, React component payloads, and explicit Codex
312
+ continuation artifact types.
313
+ - Updated MCP schemas, CLI help, browser form options, editor format detection,
314
+ format/type badges, README, and schema documentation.
315
+ - Added fixtures for Claude code/SVG/Mermaid/React and Codex diff/review/test
316
+ report artifacts.
317
+
318
+ Verification:
319
+
320
+ - `npm run release:check` passed with 25 tests.
321
+
322
+ Remaining for later phases:
323
+
324
+ - Phase 3 still needs real viewer rendering for `code` and `svg`; per user
325
+ decision, code rendering should reuse CodeMirror in read-only mode.
326
+ - Phase 4 still needs the Mermaid dependency and sandboxed local renderer.
327
+ - Phase 5 still needs gated React execution with
328
+ `ARTIFACTY_ENABLE_REACT_RENDERER=true` defaulting to disabled.
329
+
330
+ ### Phase 2 — Codex continuation artifacts
331
+
332
+ **Goal:** make Codex output useful for agent-to-agent handoff before adding heavy
333
+ viewer dependencies.
334
+
335
+ - Add Codex-aware converter paths for structured handoff JSON, file bundles,
336
+ patch/diff walkthroughs, code reviews, and verification reports.
337
+ - Preserve file paths, hashes, content types, changed-file summaries, commands run,
338
+ test status, blockers, and next-step metadata.
339
+ - Add CLI/MCP examples for `sourceAgent: "codex"` with `handoff`, `bundle`,
340
+ `diff-walkthrough`, `code-review`, and `test-report`.
341
+ - Add tests proving Codex Markdown is not over-inferred: only structured or
342
+ explicit payloads become handoffs/reports.
343
+
344
+ **Exit criteria:** another agent can find the Codex artifact and read its source.
345
+ The artifact explains the changes well enough to continue without copying chat
346
+ context.
347
+
348
+ ### Phase 2 implementation status (2026-06-24)
349
+
350
+ Implemented in the repository:
351
+
352
+ - Added Codex structured continuation conversion for handoff, diff walkthrough,
353
+ code review, and verification report payloads when the payload explicitly
354
+ identifies Codex through `agent` or `sourceAgent`.
355
+ - Preserved structured continuation metadata under `metadata.codexContinuation`,
356
+ including changed files, commands, tests, test status, findings, blockers,
357
+ decisions, next steps, diffs, and residual risk.
358
+ - Added Codex bundle metadata preservation so file bundles can carry changed-file,
359
+ test, and next-step context alongside per-file paths, content types, sizes, and
360
+ hashes.
361
+ - Added CLI/MCP documentation examples for Codex continuation artifacts.
362
+ - Added regression tests proving plain Codex Markdown remains a normal `document`
363
+ unless structured payloads or explicit `artifactType` values are supplied.
364
+
365
+ Verification:
366
+
367
+ - `npm run release:check` passed with 28 tests.
368
+ - CLI structured Codex import/list/show was verified against an isolated temporary
369
+ store.
370
+
371
+ Remaining for later phases:
372
+
373
+ - Phase 3 must implement actual viewer rendering for `code` and `svg`.
374
+ - Per user decision, `code` should use a CodeMirror read-only viewer rather than
375
+ only escaped `<pre><code>` output.
376
+
377
+ ### Phase 3 — Low-risk Claude renderers: code and SVG
378
+
379
+ **Goal:** support the highest-value Claude formats without adding large runtime
380
+ dependencies or executing artifact code.
381
+
382
+ - Implement `code` rendering as inert read-only source with language metadata shown
383
+ in the viewer. Reuse CodeMirror where practical, but avoid broad viewer coupling.
384
+ - Implement `svg` rendering inside a scriptless sandboxed iframe.
385
+ - Add SVG defense-in-depth sanitization for `<script>` nodes and `on*` attributes.
386
+ - Add tests for iframe sandbox flags, raw source fidelity, metadata display, and
387
+ sanitizer behavior.
388
+
389
+ **Exit criteria:** the viewer displays Claude code and SVG artifacts. Malicious
390
+ SVG handlers do not execute. `/raw` returns the original source.
391
+
392
+ ### Phase 3 implementation status (2026-06-24)
393
+
394
+ Implemented in the repository:
395
+
396
+ - Added `/assets/viewer.js`, a progressive-enhancement viewer client that mounts
397
+ CodeMirror in read-only mode for `code` artifacts.
398
+ - Added direct `@codemirror/lang-javascript` dependency for JavaScript, TypeScript,
399
+ JSX, and TSX read-only code viewing.
400
+ - Added `code` artifact rendering with a preserved escaped fallback and CodeMirror
401
+ enhancement when JavaScript loads.
402
+ - Added `svg` artifact rendering in a sandboxed iframe without `allow-scripts`.
403
+ - Added SVG defense-in-depth sanitization for `<script>`, `on*` attributes, and
404
+ `javascript:` href values while keeping `/raw` source faithful.
405
+ - Added HTTP regression tests for code viewer script wiring, SVG sandbox flags,
406
+ sanitizer behavior, and raw source preservation.
407
+
408
+ Verification:
409
+
410
+ - `npm run release:check` passed with 28 tests.
411
+
412
+ Remaining for later phases:
413
+
414
+ - Phase 4 must add the Mermaid dependency and sandboxed local renderer.
415
+ - Phase 5 must add gated React execution behind
416
+ `ARTIFACTY_ENABLE_REACT_RENDERER=true`.
417
+
418
+ ### Phase 4 — Mermaid renderer
419
+
420
+ **Goal:** render Mermaid diagrams locally without CDN access.
421
+
422
+ - Add `mermaid` as a dependency and serve it through the existing vendored npm
423
+ allowlist route.
424
+ - Render Mermaid inside a sandboxed iframe with `allow-scripts` but without
425
+ `allow-same-origin`.
426
+ - Inject Mermaid source through a data island rather than interpolating it into
427
+ executable script text.
428
+ - Add viewer error states for invalid diagrams and tests for sandbox flags, local
429
+ asset loading, and invalid-source handling.
430
+
431
+ **Exit criteria:** Mermaid artifacts render to SVG with local assets. Invalid
432
+ diagrams show readable errors without breaking the artifact page.
433
+
434
+ ### Phase 4 implementation status (2026-06-24)
435
+
436
+ Implemented in the repository:
437
+
438
+ - Added Mermaid as a default dependency.
439
+ - Extended `/vendor/npm/<package>/<subpath>` serving so allowlisted package
440
+ subpaths and Mermaid chunk imports can be loaded locally.
441
+ - Added a Mermaid importmap override to point `mermaid` at
442
+ `/vendor/npm/mermaid/dist/mermaid.esm.min.mjs`.
443
+ - Added `mermaid` artifact rendering in a sandboxed iframe with `allow-scripts`
444
+ and without `allow-same-origin`.
445
+ - Injected Mermaid source through a JSON data island and rendered through the
446
+ vendored local Mermaid module.
447
+ - Added readable iframe error output for invalid Mermaid source.
448
+ - Added tests for sandbox flags, vendor subpath serving, raw source preservation,
449
+ and blocked path traversal attempts.
450
+
451
+ Verification:
452
+
453
+ - `npm run release:check` passed with 28 tests.
454
+
455
+ Remaining for later phases:
456
+
457
+ - Phase 5 must add gated React execution behind
458
+ `ARTIFACTY_ENABLE_REACT_RENDERER=true`.
459
+
460
+ ### Phase 5 — React renderer behind an explicit gate
461
+
462
+ **Goal:** support Claude React artifacts without making arbitrary JS execution the
463
+ default behavior.
464
+
465
+ - Add React/ReactDOM and a JSX transform only after selecting the smallest viable
466
+ runtime strategy.
467
+ - Gate React execution behind an explicit config flag such as
468
+ `ARTIFACTY_ENABLE_REACT_RENDERER=true`.
469
+ - Default disabled behavior should show source plus a clear non-executing preview
470
+ state.
471
+ - If the transform requires `eval` or `new Function`, scope the relaxed CSP to the
472
+ artifact iframe only, never the parent app.
473
+ - Add tests proving the parent origin remains isolated and `allow-same-origin` is
474
+ never present on scripted artifact iframes.
475
+
476
+ **Exit criteria:** operators who enable React can use React artifacts. The
477
+ default installation keeps execution disabled and remains local-first.
478
+
479
+ ### Phase 5 implementation status (2026-06-24)
480
+
481
+ Implemented in the repository:
482
+
483
+ - Added React, ReactDOM, and Babel standalone as optional dependencies.
484
+ - Added `react` artifact source-only rendering as the default behavior.
485
+ - Added `ARTIFACTY_ENABLE_REACT_RENDERER=true` gate for executing React artifacts.
486
+ - Added `/artifacts/:id/react-frame?version=n`, which renders React artifacts in
487
+ a sandboxed frame only when the gate is enabled.
488
+ - Kept `allow-same-origin` off the React iframe.
489
+ - Scoped `unsafe-eval` to the React frame response CSP instead of the parent app
490
+ CSP, so Babel can transform JSX without weakening the main Artifacty UI.
491
+ - Added tests for disabled default behavior, enabled iframe wiring, React frame
492
+ CSP, vendored React/ReactDOM/Babel assets, and sandbox flags.
493
+
494
+ Verification:
495
+
496
+ - `npm run release:check` passed with 28 tests.
497
+
498
+ Remaining for later phases:
499
+
500
+ - Phase 6 must update release docs, run npm packaging checks, and record known
501
+ limitations for Mermaid bundle size and React opt-in execution risk.
502
+
503
+ ### Phase 6 — Release hardening and documentation
504
+
505
+ **Goal:** make the new format support maintainable for public users and future
506
+ agents.
507
+
508
+ - Update README, `docs/integrations.md`, `docs/artifact-schema-v1.md`, and MCP
509
+ examples with the new formats and Codex artifact categories.
510
+ - Add release checklist items for dependency size, sandbox flags, CSP behavior,
511
+ secret scanning, and offline rendering.
512
+ - Run full verification: lint, tests, smoke, npm pack dry run, browser smoke for
513
+ each renderer, and MCP create/import checks.
514
+ - Record known limitations: React opt-in risk, large Mermaid bundle size, and any
515
+ unsupported Claude payload shape.
516
+
517
+ **Exit criteria:** the feature is ready for a documented minor release. The
518
+ documentation explains its security boundaries and gives reproducible
519
+ verification evidence.
520
+
521
+ ## Testing
522
+
523
+ Per `CLAUDE.md`, every new format/route/tool/converter behavior needs a regression
524
+ test with an isolated temporary store:
525
+
526
+ - `converters.test.js`: each Claude identifier and each sniff fallback maps to the
527
+ right `format` (+ `language` for code); browser-saved-HTML fallback path.
528
+ - `storage.test.js`: round-trip of each new format (extension, content-type,
529
+ size, sha) and `normalizeFormat` aliases.
530
+ - `server.test.js`: `GET /artifacts/:id` renders the expected container per format
531
+ (iframe sandbox flags for svg/mermaid/react; highlighted block for code); `/raw`
532
+ returns untouched source.
533
+ - `mcp-server.test.js`: `artifacty_import` and `artifacty_create` accept the new
534
+ `format` enum values and echo them in `structuredContent`.
535
+ - A security test asserting SVG script/`on*` stripping and that scripted artifact
536
+ iframes never carry `allow-same-origin`.
537
+
538
+ ## Open questions (need a decision before building)
539
+
540
+ 1. **Confirm Claude's current type identifiers and `language` field shape.**
541
+ 2. **Taxonomy:** add `diagram`/`component`/`snippet` artifactTypes, or reuse
542
+ existing ones?
543
+ 3. **Dependency budget:** is vendoring Mermaid and React/JSX acceptable given the
544
+ project's minimalism, or should Mermaid/React be opt-in extras the operator
545
+ enables explicitly?
546
+ 4. **React safety bar:** ship behind a config flag, or render a static
547
+ "source only, click to run" affordance by default?
548
+ 5. **Code highlighting:** reuse CodeMirror read-only views, or add a smaller
549
+ static highlighter to avoid pulling more of CodeMirror into the viewer path?
550
+
551
+
552
+ ---
553
+
554
+ ## Phase 6 Implementation Status
555
+
556
+ Status: Complete.
557
+
558
+ Implemented:
559
+
560
+ - Updated README, integration guide, schema reference, and release checklist with renderer behavior and security boundaries.
561
+ - Narrowed npm package contents to explicit public docs so the local Claude format roadmap draft is not shipped.
562
+ - Documented CodeMirror, SVG, Mermaid, and opt-in React renderer policies for operators.
563
+
564
+ Verification:
565
+
566
+ - `npm run release:check`: passed with 28 Node tests and the local smoke test.
567
+ - `npm pack --dry-run`: passed; package contains 28 files, excludes `docs/claude-artifact-formats.md`, and reports about 1.3 MB packed size.
568
+ - Restarted the local server on `http://127.0.0.1:8787`.
569
+ - HTTP route checks passed for `/assets/viewer.js`, Mermaid, React, and Babel
570
+ vendor assets. Route status did not prove that Mermaid rendered in its
571
+ sandboxed iframe. The v10 section adds browser smoke-test evidence.
572
+ - Default app CSP was checked and does not include `unsafe-eval`; eval permission is limited to the gated React frame response.
573
+
574
+ Known limitations:
575
+
576
+ - React rendering remains disabled by default because it executes arbitrary component code when enabled.
577
+ - Mermaid and optional React rendering depend on bundled npm packages, so `npm pack --dry-run` remains required before publishing.
578
+
579
+
580
+ ---
581
+
582
+ ## v10 Mermaid CORS Hotfix and Documentation Corrections
583
+
584
+ Status: Complete.
585
+
586
+ Implemented:
587
+
588
+ - Added conditional `Access-Control-Allow-Origin: null` for `Origin: null` JavaScript asset requests served by `sendJavaScriptFile`.
589
+ - Kept Mermaid frames sandboxed with `allow-scripts` and without
590
+ `allow-same-origin`. The fix permits opaque-origin ESM imports and preserves
591
+ iframe isolation.
592
+ - Added regression tests for ACAO headers on `/assets/editor.js`, `/assets/viewer.js`, and the Mermaid vendor ESM route.
593
+ - Corrected this document's status, scope, historical model heading, and Phase 6 verification wording.
594
+
595
+ Verification:
596
+
597
+ - `node --test test/server.test.js`: passed.
598
+ - `npm run release:check`: passed with 28 Node tests and the local smoke test.
599
+ - `curl -I` with `Origin: null` confirmed `Access-Control-Allow-Origin: null` on `/vendor/npm/mermaid/dist/mermaid.esm.min.mjs` and `/assets/viewer.js`; ordinary same-origin requests do not receive a CORS header.
600
+ - In-app browser smoke opened `http://127.0.0.1:8787/artifacts/mermaid-cors-smoke-3d198d73`; the Mermaid iframe produced `svgCount=1`, `errorCount=0`, visible diagram labels, and no console errors.
601
+
602
+ Residual risk:
603
+
604
+ - The Node suite checks the CORS header. Before release, changes to sandbox
605
+ flags, CSP, or vendor asset routes still need a browser smoke test to verify
606
+ rendering.
607
+
608
+
609
+ ---
610
+
611
+ ## v11 Narrowed CORS Policy
612
+
613
+ Status: Complete.
614
+
615
+ Implemented:
616
+
617
+ - Replaced the wildcard JavaScript asset CORS response with a conditional policy.
618
+ - Ordinary same-origin JavaScript asset requests receive no CORS header.
619
+ - Requests with `Origin: null`, which are produced by sandboxed opaque-origin iframes, receive `Access-Control-Allow-Origin: null` and `Vary: Origin`.
620
+ - Mermaid iframe isolation remains unchanged: `allow-scripts` is present and `allow-same-origin` is still absent.
621
+
622
+ Verification:
623
+
624
+ - `node --test test/server.test.js`: passed.
625
+ - `npm run release:check`: passed with 28 Node tests and the local smoke test.
626
+ - `curl -I` confirmed no ACAO header on ordinary Mermaid/viewer asset requests.
627
+ - `curl -I -H 'Origin: null'` confirmed `Access-Control-Allow-Origin: null` and `Vary: Origin` on Mermaid/viewer asset requests.
628
+ - In-app browser smoke confirmed the Mermaid iframe still rendered with `svgCount=1`, `errorCount=0`, and no console errors.
629
+
630
+
631
+ ---
632
+
633
+ ## v12 MCP Timeout Alignment
634
+
635
+ Status: Complete.
636
+
637
+ Implemented:
638
+
639
+ - Centralized the 30 second MCP timeout default used by Artifacty installers.
640
+ - Codex config generation now supports custom `--timeout <ms>` by converting it to `startup_timeout_sec`.
641
+ - Gemini config generation uses the same default and supports custom `--timeout <ms>`.
642
+ - The Claude installer does not write a per-server `timeout` field. Claude Code
643
+ reads its startup timeout from the parent `MCP_TIMEOUT` environment variable.
644
+ The per-server field controls tool execution timeout.
645
+ - Updated CLI help, README, and integration docs with timeout guidance.
646
+
647
+ Verification:
648
+
649
+ - `node --test test/installer.test.js`: passed.
650
+ - `npm run release:check`: passed with 29 Node tests and the local smoke test.
651
+ - `npm pack --dry-run`: passed with 28 package files.
652
+ - Dry-run install checks confirmed Codex `--timeout 45000` produces `startup_timeout_sec = 45.0`, Gemini produces `timeout: 45000`, and Claude does not emit a misleading per-server timeout field.
@@ -1,5 +1,7 @@
1
1
  # Agent Integration
2
2
 
3
+ English | [한국어](ko/integrations.md)
4
+
3
5
  Artifacty exposes one shared MCP stdio server and one local browser server.
4
6
 
5
7
  Use Node.js 22.12.0 or newer. The optional Babel 8 React renderer requires Node `^22.18.0 || >=24.11.0`. Mermaid 12.1.0 requires an ES2024-capable browser, such as Safari 17.4 or newer.