This file is the operating guide for contributors and coding agents working in agent-render.
Ground every change in the current shipped product, not speculative roadmap ideas. If the code and this file diverge, trust the code first and update this file.
agent-render is a fully static, zero-retention artifact viewer for AI-generated outputs.
It is meant to make markdown, code, diffs, CSV, and JSON readable across chat surfaces that do a poor job rendering rich artifacts inline.
Core product traits right now:
- open source
- publicly hostable
- self-hostable
- static-export friendly
- fragment-based transport so artifact contents stay out of the request URL and off the server request path
Treat these as core constraints unless the owner explicitly changes the product direction.
- The app is a single exported client-side shell, not a backend product.
- Artifact payloads live in the URL fragment, using
#agent-render=v1.<codec>.<payload>forplain|lz|deflate,#agent-render=v1.arx.<dictVersion>.<payload>forarx, and#agent-render=v1.arx2.<dictVersion>.<payload>forarx2. - The deployed host should not receive artifact contents as part of the initial page request.
- Supported artifact kinds are
markdown,code,diff,csv, andjson. - Supported codecs are
plain,lz,deflate,arx, andarx2. - The product is zero-retention by host design, not secret-safe in an absolute sense.
- Links may still leak through browser history, copied URLs, screenshots, and any future client-side analytics.
Do not casually introduce:
- server persistence
- databases
- auth requirements for the core viewing path
- request-body upload flows for the main sharing workflow
- normal query-param transport for artifact contents
Describe and preserve what is already true in the repo today.
- The app renders as one export-friendly shell.
- The empty state explains the product and exposes sample fragment presets.
- A built-in link creator can generate fragment-based links locally in the browser.
- When a valid fragment is present, the app switches to a viewer-first artifact layout.
- The artifact stage toolbar exposes copy-to-clipboard, file download, and (for markdown) browser print-to-PDF.
activeArtifactIdcontrols which artifact opens first.- Internal diff file navigation stays in UI state and does not repurpose the fragment.
markdownrenders as sanitized GFM and supports download plus browser print-to-PDF.- Markdown code fences reuse the CodeMirror viewer approach instead of a second highlighting stack.
- Markdown
```mermaidfenced code blocks render as interactive diagrams via the dynamically importedmermaidlibrary with theme-aware rendering and strict security. codeuses a read-only CodeMirror surface with language-aware loading.diffuses a review-style git patch viewer with unified and split modes.csvrenders as a readable table/grid.jsonrenders as a lightweight structured tree plus raw fallback behavior.
- Heavy renderers are dynamically imported so the initial shell stays lighter.
- The diff stack remains the heaviest deferred renderer and is kept because the UX is worth it.
- On-demand language loading is preferred over bundling every language path up front.
Treat every payload as untrusted input.
Rules:
- keep artifact contents out of
dangerouslySetInnerHTML - do not enable raw HTML markdown rendering without explicit sanitization review
- preserve sanitization on the markdown path
- fail clearly on malformed or oversized payloads before renderer mount when possible
- do not market the product as magically private beyond the actual host-retention boundary
The fragment transport is part of the product surface, not an implementation detail.
Current rules:
- fragment key:
agent-render - format:
v1.<codec>.<payload>forplain|lz|deflate,v1.arx.<dictVersion>.<payload>forarx, andv1.arx2.<dictVersion>.<payload>forarx2 - codecs:
plain,lz,deflate,arx, andarx2 - fragment size budget:
8192characters - decoded payload budget:
200000characters - packed wire transport (
p: 1) is allowed and must decode back to the standard envelope - bundles must contain at least one artifact
- artifact ids must be unique within a bundle
- invalid
activeArtifactIdvalues normalize to the first artifact
Diff artifacts:
- prefer a real unified git patch in
patch oldContentplusnewContentis also supportedviewmay beunifiedorsplit
If you change the payload contract, update the code, docs, examples, and the OpenClaw skill together.
src/components/viewer-shell.tsx- main shell, fragment-driven state, empty state, artifact-stage layoutsrc/components/viewer/artifact-selector.tsx- bundle artifact switching UIsrc/components/viewer/fragment-details-disclosure.tsx- fragment inspector/status disclosuresrc/components/home/link-creator.tsx- browser-side link creation UX
src/components/renderers/markdown-renderer.tsxsrc/components/renderers/mermaid-block.tsx- inline mermaid diagram renderer used within markdown fencessrc/components/renderers/code-renderer.tsxsrc/components/renderers/diff-renderer.tsxsrc/components/renderers/csv-renderer.tsxsrc/components/renderers/json-renderer.tsx
src/lib/payload/schema.ts- type surface, limits, fragment key, supported kinds/codecssrc/lib/payload/fragment.ts- encode/decode logic and transport behaviorsrc/lib/payload/arx-codec.ts- arx/arx2 codecs: dictionary substitution, tuple overlay, brotli, base76/base1k/baseBMP/base64url encodingpublic/arx-dictionary.json- shared substitution dictionary for the arx codec (served as a static endpoint)public/arx-dictionary.json.br- pre-compressed brotli variant of the dictionarypublic/arx2-dictionary.json- overlay substitution dictionary for arx2 tuple-envelope transportscripts/compress-dictionary.mjs- minifies and brotli-compresses the dictionary filescripts/bench-codecs.mjs- arx/arx2 benchmark gate used bynpm run bench:codecsscripts/bench-baseline.json- committed codec benchmark baselinesrc/lib/payload/envelope.ts- bundle normalization and validationsrc/lib/payload/link-creator.ts- draft-to-link generation helperssrc/lib/payload/examples.ts- sample envelopes and example fragments
src/lib/diff/git-patch.ts- patch parsing support for diff rendering
selfhosted/server.ts- HTTP server with API routes and UUID page renderingselfhosted/db.ts- SQLite persistence (CRUD, TTL refresh, cleanup)selfhosted/ttl.ts- TTL constants and helpersselfhosted/validate.ts- payload validation for the APIselfhosted/tsconfig.json- TypeScript config for server compilationselfhosted/Dockerfile- multi-stage Docker buildselfhosted/docker-compose.yml- Docker Compose deployment
README.mddocs/architecture.mddocs/payload-format.mddocs/deployment.mddocs/dependency-notes.mddocs/testing.mdskills/agent-render-linking/SKILL.mdskills/selfhosted-agent-render/SKILL.md
Install and run:
npm install
npm run devExport-style local preview:
npm run build
npm run previewSubpath preview check:
NEXT_PUBLIC_BASE_PATH=/agent-render npm run build
npm run previewValidation:
npm run lint
npm run test
npm run typecheck
npm run test:e2e
npm run test:ci
npm run checkFirst-time Playwright browser install:
npm run test:browsersWhen making changes, preserve the product shape unless the owner explicitly wants a direction change.
- keep the core experience static-host friendly
- add a preceding
/** ... */block for public exported functions/components insrc/lib/**andsrc/components/** - keep the fragment transport client-side
- prefer small, explicit protocol changes
- update docs when changing user-visible behavior or protocol semantics
- keep examples representative of the real supported envelope format
- verify visual changes with Playwright when they affect layout or renderer presentation
- review dependency licenses before introducing anything with unclear terms
- add backend requirements just to make sharing easier
- move artifact bodies into normal query params
- promise unsupported artifact kinds in docs or UI copy
- leave the skill contract stale when the app contract changes
- describe roadmap ideas in this file as if they already ship
If any of these change, check the rest in the same pass:
- fragment format
- supported artifact kinds
- payload size limits
- zero-retention wording
- renderer capabilities
- local commands
- deployment assumptions
At minimum, verify alignment across:
README.mddocs/architecture.mddocs/payload-format.mddocs/deployment.mddocs/dependency-notes.mddocs/testing.mdskills/agent-render-linking/SKILL.md
Be conservative with product claims and precise with protocol changes.
agent-render is useful because it is simple:
- static
- linkable
- open
- self-hostable
- readable across chat platforms
Protect that simplicity.