Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 15 additions & 6 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ Core product traits right now:
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 the compact `#<tag><payload>` form where the single tag char identifies the codec: `p` plain, `l` lz, `d` deflate, `a` arx, `b` arx2, `c` arx3, `e` arx4, `f` arx5. Legacy `#agent-render=v1.<codec>.<payload>` links (arx-family carry an extra `<dictVersion>.` segment) still decode but are no longer emitted.
- Artifact payloads live in the URL fragment, using the compact `#<tag><payload>` form where the single tag char identifies the codec: `p` plain, `l` lz, `d` deflate, `a` arx, `b` arx2, `c` arx3, `e` arx4, `f` arx5, `g` arx6. Legacy `#agent-render=v1.<codec>.<payload>` links (arx-family carry an extra `<dictVersion>.` segment) still decode but are no longer emitted.
- The deployed host should not receive artifact contents as part of the initial page request.
- Supported artifact kinds are `markdown`, `code`, `diff`, `csv`, and `json`.
- Supported codecs are `plain`, `lz`, `deflate`, `arx`, `arx2`, `arx3`, `arx4`, and `arx5`. Auto-emit prefers `arx5`; `arx3` and `arx4` remain decodable and explicitly encodable but are not auto-selected.
- Supported codecs are `plain`, `lz`, `deflate`, `arx`, `arx2`, `arx3`, `arx4`, `arx5`, and `arx6`. Default automatic encoding preserves the complete logical `arx5/arx2/arx/deflate/lz/plain` pool, then selects `arx6` only when both its conservative transport score and actual WHATWG-serialized fragment length are strictly shorter. Ties, losses, and candidate unavailability retain the exact old wire when that candidate meets the same requested budget. An explicit policy budget takes priority when only ARX6 fits it. Transport selection omits a Unicode wire only when a strict lower bound proves it cannot win; the result is identical to evaluating all wire shapes. `arx3` and `arx4` remain decodable and explicitly encodable but are not auto-selected.
- 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.

Expand Down Expand Up @@ -66,6 +66,7 @@ Describe and preserve what is already true in the repo today.
- 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.
- Browser codec work runs in one lazy Worker with serialized jobs, a cap of eight outstanding requests, a 60-second deadline, cancellation by termination, and a 15-second idle shutdown. Hash navigation must abort obsolete creator encoding before queuing the destination decode; do not wait for creator unmount. Worker failures are explicit; do not retry expensive coding on the main thread. Node and runtimes without Worker support keep the direct async API.

## Security and safety posture

Expand All @@ -84,11 +85,12 @@ The fragment transport is part of the product surface, not an implementation det

Current rules:
- fragment key: `agent-render` (legacy decode path only; the compact form has no key)
- emitted format: compact `#<tag><payload>`, where the single tag char identifies the codec (`p` plain, `l` lz, `d` deflate, `a` arx, `b` arx2, `c` arx3, `e` arx4, `f` arx5); the compact tag does not carry a dictionary version — arx-family tags imply the build's current dictionary (the build pins the newest supported version and refuses to decode a newer one)
- emitted format: compact `#<tag><payload>`, where the single tag char identifies the codec (`p` plain, `l` lz, `d` deflate, `a` arx, `b` arx2, `c` arx3, `e` arx4, `f` arx5, `g` arx6). ARX through ARX5 compact tags imply their pinned dictionary/model. New ARX6 links carry explicit model version `3`: `#g3<prior><digits>`; `#g2<prior><digits>` and original `#g<prior><fraction>` links use their frozen models
- legacy format (still decodable, no longer emitted): `agent-render=v1.<codec>.<payload>` for `plain|lz|deflate`, `agent-render=v1.arx.<dictVersion>.<payload>` for `arx`, `agent-render=v1.arx2.<dictVersion>.<payload>` for `arx2`, `agent-render=v1.arx3.<dictVersion>.<payload>` for `arx3`, `agent-render=v1.arx4.<dictVersion>.<payload>` for `arx4`, and `agent-render=v1.arx5.<dictVersion>.<payload>` for `arx5`
- codecs: `plain`, `lz`, `deflate`, `arx`, `arx2`, `arx3`, `arx4`, and `arx5`
- `arx5` is the emitted mixer codec (ARX 4.5): ARX4's context mixer on ARX2's tuple/overlay pipeline, with every wire scored by honest serialized transport length. `arx3` and `arx4` stay decodable but are no longer auto-emitted because they score dense Unicode by visible character count
- `arx4`/`arx5` payloads carry one extra leading char after the tag, the prior id (`m`, `c`, `j`, `s`, or `n`), naming the priming corpus the context mixer ran before the payload; `m`/`c`/`j` additionally need `/arx4-priors.json`, and an encoder that cannot load it emits `s` instead
- codecs: `plain`, `lz`, `deflate`, `arx`, `arx2`, `arx3`, `arx4`, `arx5`, and `arx6`
- `arx6` defaults to model version 3 in `arx6-v3-model.ts` to code raw artifact bodies followed by the arx2 tuple JSON with body-length placeholders. Canonical WTF-8 preserves all UTF-16 strings, including lone surrogates. Its `#g3<prior><digits>` wire uses `0-9A-Za-z-._~`, a canonical shortest mixed-radix fraction whose numerator embeds CRC32 binding the raw bytes to the version and prior. CRC32 detects accidental corruption; it is not authentication. Model v3 adds contexts for recent nonword bytes and digit-masked history without changing raw framing or prior bytes. Model v2 (`#g2`) and the original `#gm`/`#gc`/`#gj`/`#gs`/`#gn` decoder and models stay frozen; explicit `arx6CompressEnvelope(envelope, priorId, 2)` encoding remains available. The separate experimental `#g1L` wire is not a viewer format
- `arx5` (ARX 4.5) is ARX4's context mixer on ARX2's tuple/overlay pipeline, with every wire scored by honest serialized transport length. `arx3` and `arx4` stay decodable but are no longer auto-emitted because they score dense Unicode by visible character count
- `arx4`/`arx5` payloads carry a prior id immediately after the tag; new `arx6` payloads carry it after version `3` (or `2` for v2 links). Prior ids are `m`, `c`, `j`, `s`, and `n`. Curated `m`/`c`/`j` priors need `/arx4-priors.json`; when unavailable, `s` supplies the shared-prior candidate. ARX6 default encoding additionally compares the available kind prior against unprimed `n`, choosing `n` only for a strictly shorter complete wire. Legacy `#gn`, v2 `#g2n`, and v3 `#g3n` decoding skip dictionary/prior fetches. Explicit `{ codec: "arx6" }` encoding can emit unprimed v3 when pinned assets are unavailable or newer than supported; automatic selection still applies the legacy pool's version checks. A primed decoder must load the exact prior named by the link or fail, never substitute another prior
- fragment size budget: `8192` characters
- decoded payload budget: `200000` characters
- Discord markdown link limit: `2000` characters for the full formatted `[label](url)` string; `createGeneratedArtifactLink*` returns `discordMarkdownLinkWarning` when exceeded
Expand Down Expand Up @@ -124,8 +126,15 @@ If you change the payload contract, update the code, docs, examples, and the Ope
- `src/lib/markdown-link.ts` - markdown link formatting and Discord length warnings
- `src/lib/payload/schema.ts` - type surface, limits, fragment key, supported kinds/codecs
- `src/lib/payload/fragment.ts` - encode/decode logic and transport behavior
- `src/lib/payload/browser-codec.ts` - bounded, cancellable browser Worker bridge
- `src/lib/payload/codec.worker.ts` - static Worker entry point for browser encode/decode
- `src/lib/payload/arx-codec.ts` - arx/arx2/arx3 codecs: dictionary substitution, tuple overlay, brotli, base76/base1k/baseBMP/base64url encoding
- `src/lib/payload/arx4-codec.ts` - arx4/arx5 mixer: the arx2 stages with brotli replaced by a deterministic integer context mixer, plus the curated priors it primes on
- `src/lib/payload/arx6-codec.ts` - arx6: raw container, arithmetic coder, and mixed-radix fraction wire, primed with the arx4 priors
- `src/lib/payload/arx6-model.ts` - frozen original ARX6 model for existing unversioned `#g` links
- `src/lib/payload/arx6-v2-model.ts` - frozen ARX6 v2 context mixer for `#g2` links
- `src/lib/payload/arx6-v3-model.ts` - ARX6 v3 context mixer for new `#g3` links; incompatible model changes require a new wire version
- `src/lib/payload/arx6-bytes.ts` - canonical WTF-8 and CRC32 framing helpers
- `public/arx4-priors.json` - curated per-kind arx4 priming corpora (and `.br` pre-compressed variant), regenerated by `scripts/build-arx4-priors.mjs`
- `public/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 dictionary
Expand Down
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,19 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and
- Link creator can load a local text file into the draft (client-side only) instead of requiring a paste.
- In-viewer edit-and-reshare: open any artifact, correct its content, and generate a new fragment link without leaving the viewer.
- `arx5` (ARX 4.5): ARX4's context mixer on ARX2's tuple pipeline, scored by honest serialized transport length so Discord and WhatsApp no longer detonate Unicode wires.
- ARX6 model version 3 (`#g3`): lossless raw-text context mixing, contexts for recent nonword bytes and digit patterns, mixed-radix fraction transport, a corruption checksum, and exact UTF-16 preservation. Existing v2 (`#g2`) and unversioned ARX6 links retain their frozen decoders; explicit v2 encoding remains available. See `docs/arx6-research.md` for the consolidated findings and qualified measurements.
- Browser codec Worker with serialized jobs, cancellation, queue bounds, a 60-second deadline, and idle shutdown.

### Fixed

- Unprimed legacy `#gn`, v2 `#g2n`, and v3 `#g3n` links decode without dictionary/prior fetches; explicit ARX6 encoding retains its unprimed fallback with newer dictionary/overlay assets.
- Hash navigation aborts obsolete creator encoding before queuing the destination decode.

### Changed

- Auto-emit no longer selects `arx3` or `arx4`. Existing `#c` and `#e` links still decode.
- Legacy radix encoding uses balanced integer conversion while preserving the existing wire bytes, reducing work on large candidate payloads. Transport selection skips materializing Unicode wires only when a strict lower bound proves they cannot win; the logical candidate pool and selected output are unchanged.
- Default automatic encoding compares ARX6 against the complete existing ARX5/ARX2/ARX/deflate/LZ/plain pool and selects it only for a strict serialized-length improvement. Ties and losses retain the old wire exactly.
- Highlighting consolidated on `@pierre/diffs` (Shiki) for diffs, standalone code, markdown fences, JSON raw views, and the artifact editor; CodeMirror and `@git-diff-view` are removed.

## [0.1.0] - 2026-05-05
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ Built for the OpenClaw ecosystem, `agent-render` focuses on fragment-based shari
## Status

- Markdown, code, diff, CSV, and JSON all render in the static shell
- Fragment transport supports `plain`, `lz`, `deflate`, `arx`, `arx2`, `arx3`, `arx4`, and `arx5`, with automatic shortest-fragment selection across available wire formats. Auto-emit prefers `arx5`; `arx3` and `arx4` remain decodable but are no longer selected
- The `arx` substitution dictionary is served at `/arx-dictionary.json` with a pre-compressed `/arx-dictionary.json.br` variant; the `arx2` tuple-envelope overlay is served at `/arx2-dictionary.json` with a pre-compressed `/arx2-dictionary.json.br` variant; `arx5` (ARX 4.5) applies the context mixer from `arx4` on top of the `arx2` tuple pipeline and scores every wire by honest serialized transport length; `arx3`/`arx4` stay decodable and explicitly encodable but are not auto-selected, because their visible-length Unicode scoring detonates on Discord and WhatsApp. Mixer priors live at `/arx4-priors.json` with a pre-compressed `/arx4-priors.json.br` variant
- Fragment transport supports `plain`, `lz`, `deflate`, `arx`, `arx2`, `arx3`, `arx4`, `arx5`, and `arx6`. Default automatic selection preserves the complete existing codec pool and adds ARX6 only when it produces a strictly shorter serialized fragment; ties and losses keep the existing wire. ARX3 and ARX4 remain readable and explicitly encodable, but are excluded from automatic selection
- ARX6 model version 3 combines raw artifact text, an adaptive context mixer with syntax and digit-pattern contexts, and a URL-safe mixed-radix fraction wire. New `#g3` links preserve every JavaScript string and include a corruption checksum. Existing `#g2` and unversioned ARX6 links keep their frozen decoders. The shared dictionaries and mixer priors remain static assets; no artifact is uploaded or added to a prior. See [the consolidated research findings](docs/arx6-research.md) for mechanisms, measurements, and limits
Comment thread
greptile-apps[bot] marked this conversation as resolved.
- The viewer toolbar copies artifact bodies to the clipboard, downloads them as files, (for markdown) supports browser print-to-PDF, and can edit the open artifact then reshare it as a new fragment link
- Deployment target: static hosting, including Cloudflare Pages

Expand Down
Loading
Loading