Agent files

While a folder is shared with Agent πŸ“‚ Shareβ„’, its work folder is the interface between the playground and an agent. Some files are read-only views of the live playground: your selections, the console, screenshots and pins. Some are two-way: the source, the exported files and the worker. Two pairs are request and result channels for running code. This page lists every file, who writes it and when, and gives its format.

Files at a glance

A work folder looks like this. Which files exist depends on what you have started: a screenshot needs a capture, a pin file needs a pin.

plain
<root>/
  <route>/                     the work folder, for example new/ or Unrouted/
    AGENTS.md  CLAUDE.md       the agent guide, two identical copies
    .agents/skills/pin-use/SKILL.md
    .claude/skills/pin-use/SKILL.md
    πŸ“„.html                    the source (two-way)
    πŸ“„.sel                     your selections
    πŸ“œ.json                    the console (live data in πŸ“œ.json.crswap while sharing)
    πŸ“Έ.png                     a screenshot, while a capture runs
    πŸ“.0.json  πŸ“.0.png        one pin
    io.eval.js  io.eval.json   a request and its result, in the playground
    o.eval.js   o.eval.json    a request and its result, in the output
    .css  about.html  βš™οΈ.js    exported files and the worker (File Sync)
FileAppearsWritten byFormat
πŸ“„.htmlAt share startBoth. Two-way with File Sync on.The source: UTF-8, LF line endings.
πŸ“„.selAt share startThe playground. Read-only: your edits are reverted.Text: line number, two spaces, selected text.
πŸ“œ.json and πŸ“œ.json.crswapAt share start (emptied)The playground. Read-only.JSON Lines, one line per console call.
πŸ“Έ.pngWhile sharing and capturingThe playground. Delete it to ask for a new one.PNG.
πŸ“.<N>.json and πŸ“.<N>.pngWhen pins exist. The PNG needs an Output capture.The playground.JSON with 2-space indentation, and PNG.
Exported files and βš™οΈ.jsAt share start, with File Sync onBoth.Text, or binary for images.
io.eval.js and o.eval.jsWhen the agent writes a requestThe agent. The playground deletes the file when it accepts the request.JavaScript.
io.eval.json and o.eval.jsonWhen a request has runThe playground.JSON with 2-space indentation, or a plain-text error.
πŸ•.<n>.<sha7>.htmlWhen code calls edge.versions.load()The playground.The HTML source of a past deployment.
AGENTS.md and CLAUDE.mdAt every share startThe playground.Markdown, identical copies.
.agents/skills/pin-use/SKILL.md and .claude/skills/pin-use/SKILL.mdAt every share startThe playground.Markdown with YAML front matter.
πŸ“‹.πŸ–ΌοΈ.<stamp>.png and other pasted filesWhen you paste files while sharingYou.As pasted.
Any other file-The agent.The playground ignores it, apart from a toast.

Unshare keeps the files. The one exception is a capture that the share started: Unshare stops it, and stopping a capture deletes the pin images. Shift (Empty) empties the work folder and Ctrl (Delete) deletes it; see Unshare.

Exact names

The emoji in file names are part of the names. Agents have to write them exactly.

NameCharacters
πŸ“„.html, πŸ“„.selU+1F4C4
πŸ“œ.json, πŸ“œ.json.crswapU+1F4DC
πŸ“Έ.png, πŸ“Έ.mp4U+1F4F8
πŸ“.<n>.json, πŸ“.<n>.pngU+1F4CD
βš™οΈ.jsU+2699 U+FE0F
πŸ•.<n>.<sha7>.htmlU+1F550
πŸ“‹.πŸ–ΌοΈ.<stamp>.pngU+1F4CB, ., U+1F5BC U+FE0F
io.eval.js, io.eval.json, o.eval.js, o.eval.jsonPlain ASCII

How the playground reacts to changes

One FileSystemObserver watches the work folder. It looks into subfolders only while File Sync is on, and it acts only on top-level names, using the latest change per name:

FileWhat the playground does
πŸ“.<n>.json, πŸ“.<n>.pngNothing. Pin files are read only when a share starts.
πŸ“„.selRewrites it if its content differs from the last snapshot.
πŸ“„.htmlLoads it into the editor. With File Sync on, File Sync handles it together with the exported files.
πŸ“Έ.pngActs only when it is deleted: it takes a new screenshot.
io.eval.js, o.eval.jsRuns the request, on any change except a deletion.
Any other nameShows a toast with the kind of change and the name, for example appeared notes.md.

While File Sync is on, *.crswap, *.swp and *.tmp files cause no toast. Every file that the playground reads or writes shows a small transfer toast, except πŸ“„.sel, the πŸ“ files, reads of eval requests and the files of the diagnostics command. Writes while a share starts are silent too.

Source and selections

πŸ“„.html

πŸ“„.html holds the editor's text as UTF-8 with LF line endings. It is the file to change when you want to change the page.

  • File Sync on (the default): the file is live in both directions. Every editor change is published to disk with its final newlines trimmed, so the file has no trailing newline. Changes on disk are applied to the editor as minimal edits between undo stops. Imports ignore final-newline differences; see File Sync.
  • File Sync off: the text is written exactly. Disk to editor stays live. Editor to disk happens only at share start and when πŸ’Ύ Save: πŸ“„.html runs.

Two commands help while a folder is shared: πŸ’Ύ Save: πŸ“„.html (Alt+B / βŒ₯+B) and πŸ’Ύ Load: πŸ“„.html (Alt+L / βŒ₯+L), both while the editor has focus. See Saving and loading. When the agent and the editor both change the file between two syncs, a Files differ dialog opens.

File Sync reads the file with a decoder that keeps a UTF-8 byte-order mark, so a BOM would enter the editor as a U+FEFF character. Write UTF-8 without a BOM.

πŸ“„.sel

πŸ“„.sel is a read-only view of the editor's selections. The playground rewrites it on every selection change, whether a person or code made it, for example sel = … from io.eval.js. It also rewrites it after every content change once the output is ready, and at share start. Edits to the file, and its deletion, are reverted: whenever the disk text differs from the last snapshot, the playground writes it back.

  • Selections are sorted by their start. Selections whose line ranges touch or overlap merge into runs, and runs are separated by one blank line.
  • Each line reads <line number><two spaces><selected text on that line>. Line numbers are right-aligned within a run, padded to the digits of that run's largest number.
  • Segments on one line that do not touch are joined with β‹― (U+22EF). Touching or overlapping segments merge.
  • In a multi-line selection, the first line starts at the start column, middle lines are whole, and the last line ends before the end column.
  • A cursor without a selection still lists its line, with empty text: a lone cursor on line 2 gives 2 and two spaces.
  • The file has no trailing newline.

Take the selections [1,4,1,9], [1,10,1,15], [3,3,4,15], a cursor at line 10 and [11,4,11,5]. In a document whose line 1 is <p>Hello world</p>, whose lines 3 and 4 are <li>One</li> and <li>Two</li>, and whose line 11 is abcxyz, they give:

plain
1  Helloβ‹―world

3  <li>One</li>
4    <li>Two</li>

10
11  x

Lines 10 and 11 are one run, because the lines are adjacent, so both numbers are padded to two digits. Line 10 is the line of the empty cursor.

Console: πŸ“œ.json and πŸ“œ.json.crswap

The console log is a JSON Lines file. Every console call from the output appends one line, { "<method>": [ …arguments ] }. Any console function is relayed (log, info, warn, error, debug, table, dir, assert, count, time, group and the others), and each call is recorded as made: console.assert(true, 'x') gives {"assert":[true,"x"]}. See Console and network.

Reading the log

Share start empties πŸ“œ.json and opens it with an exclusive writer. Chromium keeps the live data of such a writer in a swap file, πŸ“œ.json.crswap, and πŸ“œ.json stays empty until Unshare closes the writer and commits the data. So read both:

plain
cat πŸ“œ.json*

While a share is active, πŸ“œ.json.crswap also marks a live share: a second window that shares the same route finds it and asks you to unshare the other one. Do not delete the console files. console.clear() in the output, and the console panel's clear button, truncate the log. The log has no rotation and no size cap: it grows until you clear it, share again or reload.

Where lines come from

  • Output documents produce lines without a prefix: all code outside workers, including o.eval.js.
  • The output's service worker produces lines for <script type=worker> code. Their first array element is "πŸ›ŽοΈβš™οΈ", for example {"log":["πŸ›ŽοΈβš™οΈ","hi"]}. The prefix is left out for the group methods and when the first argument contains %c.
  • io.eval.js runs in the playground window, and its console calls never reach πŸ“œ.json.
  • Uncaught errors in the output are logged for you: {"error":["<stack>"]}, where the stack has the output-origin URL prefixes, (eval at …, fragments and the characters @ ( ) removed, or the message when there is no stack. An unhandled rejection gives {"error":["Unhandled rejection", <reason>]}.

How arguments are recorded

Arguments are structured-cloned and then JSON-encoded:

ArgumentRecorded as
string, number, boolean, nullitself
undefined, NaN, Β±Infinitynull
Datean ISO string
Map, Set, Error objects{}. Log e.stack or String(e) instead.

If any argument cannot be cloned, such as a function, a DOM node or a symbol, the whole argument list is replaced by one JSON string, in which special values are marked with _RT_Encoded and _RTType, for example {"log":"[\"hello\",{\"_RT_Encoded\":true,\"_RT_Content\":{\"_RTType\":\"Function\",…}}]"}. A line's value is therefore either an array, or a string that has to be parsed again. This code reads both:

js
const lines = text.split('\n').filter(Boolean).map(l => Object.entries(JSON.parse(l))[0]).map(([method, a]) => ({ method, worker: (typeof a === 'string' ? JSON.parse(a) : a)[0] === 'πŸ›ŽοΈβš™οΈ', args: typeof a === 'string' ? JSON.parse(a) : a }))

Screenshots: πŸ“Έ.png

πŸ“Έ.png is written and refreshed only while a folder is shared and a screen capture runs. It is a PNG of the whole captured frame, at the capture's native pixel size, and it shows whatever the capture records: Window, Playground, Input or Output. It stays on disk after the capture stops and after Unshare. Deleting it asks the playground for a new one; overwriting it does nothing.

It refreshes after edits that reach the output, after each successful eval, when the output scrolls, and in a few more cases. Screen capture lists them all and explains how a refresh works.

πŸ“Έ.mp4 is a reserved name, but the playground never writes it into the folder. Video: Save downloads the recording instead.

Pins: πŸ“.<N>.json and πŸ“.<N>.png

Each pin has one JSON file, πŸ“.<N>.json, directly in the work folder. The files are numbered from 0 in the live pin order, which is the order of o.pins.toJSON().pins, and formatted with 2-space indentation. A pin that has an image also has πŸ“.<N>.png, the raw crop of the pin. Images need an active Output capture; without one, the JSON files still update and say "image": null.

The file

The top-level fields of each JSON file, in the order they are written:

FieldValue
version, type, source1, "rtcode.pin" and "output".
route, workCode.D[0] or null, and the work folder's name or null.
pinSchema5.
url, titleThe URL and title of the output document.
updatedAtThe time of the write, as an ISO string.
index, totalThe pin's number and the number of pins.
idThe pin's id.
image{ "file": "πŸ“.<N>.png", "capture": { … } } when a crop exists, otherwise null.
captureErrorA message string. It appears only when the last capture failed and no image exists.
pinThe pin itself; see below.

This is an element pin captured at 1Γ—, abridged. The numbers are illustrative.

plain
{
  "version": 1,
  "type": "rtcode.pin",
  "source": "output",
  "route": "new",
  "work": "new",
  "pinSchema": 5,
  "url": "<output document URL>",
  "title": "",
  "updatedAt": "2026-10-04T12:00:00.000Z",
  "index": 0,
  "total": 1,
  "id": "pin_mutrqtc0_a1b2c3",
  "image": { "file": "πŸ“.0.png", "capture": { "requestedScale": 1, "appliedScale": 1, "width": 52, "height": 26 } },
  "pin": {
    "id": "pin_mutrqtc0_a1b2c3", "kind": "element", "selector": "span#badge", "label": "new", "tag": "span",
    "domPath": "html > body > main > span#badge",
    "rootPath": [ { "selector": "html > body > main > span#badge" } ],
    "rect": { "x": 24, "y": 120, "width": 52, "height": 26 },
    "viewport": { "width": 698, "height": 848, "scrollX": 0, "scrollY": 0, "devicePixelRatio": 1 },
    "url": "<output document URL>", "title": "", "createdAt": "…", "timestamp": "…",
    "comment": "Make this pill smaller", "innerText": "new", "outerHTML": "<span id=\"badge\">new</span>",
    "computedStyles": { "display": "block", "border-radius": "16px", "…": "…" }, "cssRules": [ { "selector": "#badge", "css": "…" } ],
    "autoTrack": true, "autoCapture": false, "linked": true
  }
}

The pin object

The pin object holds only the keys below. autoTrack and autoCapture are always present, and linked is present on element pins. The other fields appear only when the pin has them.

FieldMeaning
idpin_<time in base36>_<random base36 characters>, for example pin_mutrqtc0_a1b2c3. It stays the same through reordering, renumbering, moves and retargeting.
kind"element" or "rect", for a region pin.
selectorFor an element pin, tag#id, else tag.firstClass, else tag. It is an approximate label: the id and the class are not escaped, so it is not always a valid or unique selector. null for a region pin.
labelThe innerText with whitespace collapsed and cut to 40 characters plus …, or the selector when there is no text. "Rect" for a region pin.
tagThe lowercase tag name. "rect" for a region pin.
domPathThe labels from the outermost ancestor down to the element, joined with  > , including #shadow-root and #document markers. null for a region pin.
rootPathElement pins only. An array of segments, [{ selector, enter: 'shadow' | 'frame' }, …, { selector }]. Each selector is a chain of parts, relative to the current root, written tag#id, else tag:nth-of-type(n) when siblings share the tag, else tag. To link a pin again, the playground resolves rootPath first, then a unique domPath, then a unique selector.
rect{ x, y, width, height } in output CSS pixels, relative to the output viewport.
viewport{ width, height, scrollX, scrollY, devicePixelRatio } of the output window at the last report. It is a historical record: overlays and crops use the live layout size.
url, titleThose of the output document.
createdAt, timestampThe time of creation, and the time of the latest full snapshot of the element.
commentA plain-text string.
innerText, outerHTMLThe element's text and its full serialized source, which can be large. Empty for a region pin.
computedStylesThe values of 44 properties that differ from the defaults. They are display, position, top, right, bottom, left, width, height, margin, padding, border, border-radius, box-sizing, box-shadow, background, color, opacity, font-family, font-size, font-weight, font-style, line-height, letter-spacing, text-align, text-decoration, text-transform, white-space, flex, flex-direction, flex-wrap, justify-content, align-items, align-self, gap, grid-template-columns, grid-template-rows, grid-column, grid-row, overflow, z-index, transform, transition, cursor and object-fit. {} for a region pin.
cssRules[{ selector, css, media? }], from the stylesheets that the page can read: the document's, adopted ones and those of the element's shadow root. Cross-origin stylesheets are skipped. [] for a region pin.
autoTrack, autoCaptureBooleans, true and false by default. See Pins.
linkedElement pins only. true by default.

The capture object inside image tells how the PNG was made. On the direct path it is { requestedScale, appliedScale: 1, reason?: 'region', width, height }. On the enhanced path it is { requestedScale, appliedScale, origin, lift, unclip, settle, unclipped, clippedBy: [selector…], translate?: [x, y], background?, backgroundMode?, width, height }. width and height are the pixel size of the PNG. The two paths are explained in Pins.

Lifecycle

  • Every save of the pins rewrites all current JSON files, and writes the current PNGs under their current index, so deleting pin 0 renumbers the rest. It deletes every πŸ“.<N>.json numbered beyond the current count, every πŸ“.<N>.png that belongs to no current or pending image, and the legacy πŸ“.json and πŸ“.png.
  • Stopping an Output capture while a folder is shared deletes all pin PNGs, and rewrites the JSON files with "image": null.
  • The playground ignores your edits to the πŸ“ files while it runs. It reads them only when a share starts: pins are loaded from disk when Share ← Folder is on, or when the page has no pins in memory. Otherwise the pins in memory overwrite the files. Loading sorts by the stored index, then by file number, and skips entries without a pin.id.
  • To change pins, use o.pins from io.eval.js; see Pins from code and PinList.

Exported files and βš™οΈ.js

With File Sync on, every element that declares o:=<name> appears as a file at that relative path in the work folder, and every <script type=worker>, edge or not, appears as βš™οΈ.js with its raw, unbundled text. Edits to these files map back to the exact source range in πŸ“„.html. A real folder shows dot-named root exports: <script o:=.js> becomes the file .js. File Sync describes the names, the content of each file and the conflicts.

Eval requests and results

An agent runs JavaScript by writing a file and reading another:

RequestResultRuns in
io.eval.jsio.eval.jsonThe playground window: Code, edge, sel, o and the other globals.
o.eval.jso.eval.jsonThe output document.

The playground reads a request at once, deletes the old result, deletes the request, which acknowledges it, and then evaluates it. A share start deletes io.eval.js and io.eval.json, but leaves o.eval.js and o.eval.json alone, so a leftover o.eval.json can be stale. File evals has the whole protocol, with examples.

Saved versions: πŸ•.<n>.<sha7>.html

await edge.versions.load(ref, { route = Code.D[0] } = {}) writes a past deployment into the work folder as πŸ•.<n>.<sha7>.html. n is the version's position among the route's newest 100 deployments when it was loaded, with 0 the newest, and sha7 is the first 7 hex digits of its SHA. A number ref is an index. Any other ref is converted to a string and matched as a SHA prefix: the first, newest match wins, uniqueness is not checked, and a numeric string such as '2' is a SHA prefix, not an index. See edge.versions().

The call resolves to { file, n, sha, date, message, ok }. It throws Agent Folder Share is not active. without an active share, and No version <ref> β€” edge.versions() lists <count>. for an unknown ref. The file holds the HTML source, decoded from the playground URL that was saved with that deployment, compressed URLs included. ok is false when no source could be decoded; the file then holds the raw stored record, or is empty.

js
await edge.versions.load(1)

This returns, for example, { "file": "πŸ•.1.d2a2938.html", "n": 1, "sha": "d2a2938", "date": "…", "message": "…", "ok": true }. Then diff the file locally with diff πŸ•.1.d2a2938.html πŸ“„.html. With File Sync on, πŸ“„.html has no final newline while the version file keeps the deployed text, so a \ No newline at end of file note alone is not a real difference. Read, grep and diff the πŸ• files locally. Do not return their content through eval results: they are large.

Instructions: AGENTS.md and CLAUDE.md

Both files contain the line This file is auto-generated by RTCode.io., a blank line, and the text of the current RTCode.io agent guide. AGENTS.md is for Codex and other agents that read AGENTS.md. CLAUDE.md is for Claude Code. The two are byte-identical.

The headings of the guide show what it covers:

Note: The guide summarizes the files on this page. Where its wording and this page differ in detail, this page describes what the playground does.

Skills: .agents and .claude

The share installs the Pin Use skill as identical copies at .agents/skills/pin-use/SKILL.md, for Codex, and .claude/skills/pin-use/SKILL.md, for Claude. They are written before AGENTS.md and CLAUDE.md, and refreshed on every share, including with Share ← Folder, so local edits are lost. Other skills and commands in those folders are preserved, and Unshare keeps the files. The skill must start with YAML front matter, or the share fails.

Its front matter reads name: pin-use and description: Create, inspect, update, and capture RTCode.io output element or region pins through o.pins while Agent Folder Share is active. Its sections are Programmatic Pins, Pin image capture options and Optional selector piercing. The preamble restates the protocol: send one request at a time through io.eval.js, then wait for the new io.eval.json. The guide links both copies. See Pins from code.

Pasted files

Files pasted into the playground

While a folder is shared, pasting files writes them into the work folder. This happens when the focus is outside the editor's text, or when the clipboard holds no image files.

  • Each file is written at the root of the work folder under its original name. A file with the same name is overwritten without asking. A toast reports how many files were pasted.
  • A clipboard file named exactly image.png, which is typical for screenshots, is renamed πŸ“‹.πŸ–ΌοΈ.<stamp>.png. The stamp is the current time as an ISO 8601 string in UTC, with each : replaced by Β· (U+00B7), for example πŸ“‹.πŸ–ΌοΈ.2026-10-04T12Β·34Β·56.789Z.png. A paste uses one stamp, so several image.png files in one paste overwrite each other. There is no <N> index and no second copy.
  • Other files keep their names.
  • Dropped files are never written to the folder. Without a share, nothing is written and the normal paste goes ahead.

Note: The guide in your folder may describe indexed names, πŸ“‹.πŸ–ΌοΈ.<N>.png, and a timestamped copy of each pasted image. Current playgrounds write neither. Older folders can still contain such files.

Images pasted into the editor

A paste with the editor's text focused, or a drop of image files onto the input panel, never creates a file. Each image becomes <img src="data:<type>;base64,…"> in the source instead. This needs the same browser support as Agent πŸ“‚ Shareβ„’, but not an active share.

  • The playground decodes each image, draws it at its natural size, encodes it as PNG and as WebP in parallel, and keeps the smaller result. A toast per image, Image i or Image i / n, lists the sizes of the original, the PNG and the WebP, and the picked format.
  • The limits apply to the total encoded size of all images in one paste or drop, before base64:
    • 2 MiB or less: inserted directly.
    • Over 2 MiB: a confirmation, Paste N image(s) inline? (Drop … for drops), with the OK button labelled Paste or Drop.
    • Over 10 MiB: refused, with the danger toast Inline image paste too large: N image(s).
  • The tags are joined with newlines and inserted at every selection as one undo step. The caret lands right after <img in each tag, ready for alt=.

To turn such an image into a file, see Binary images.

Legacy files

Older shares wrote files that current shares do not write. Old work folders can still contain them:

Rules for agents

Do:

  • Read AGENTS.md or CLAUDE.md first. The share rewrites them each time.
  • Prefer focused edits in the exported files, or in βš™οΈ.js for worker code. Use πŸ“„.html for structural changes.
  • Read the console with cat πŸ“œ.json*.
  • Write an eval request in one write, or write a temporary file and rename it. Wait until the request file has disappeared, then read the result. See File evals.
  • Delete πŸ“Έ.png when you need a fresh screenshot.
  • Read, grep and diff the πŸ• files locally.

Do not:

  • Edit πŸ“„.sel. It is a read-only view of the selections in πŸ“„.html, and it is rewritten.
  • Delete the console files, πŸ“œ.json and πŸ“œ.json.crswap.
  • Edit the πŸ“ files and expect the playground to notice. Change pins through o.pins.
  • Edit the same content in πŸ“„.html and in an exported file at the same time.
  • Return whole version files, or whole pin payloads, through eval results. They are large.

The playground ignores any other file that an agent writes into the folder, apart from a toast.

plain
cd ~/RTCode/new        # <root>/<route>, for example the root you picked plus the route name
cat πŸ“œ.json*           # live console lines are in πŸ“œ.json.crswap while sharing
cat πŸ“„.sel             # what the human has selected (read-only)
ls -A                  # .css, βš™οΈ.js and .agents/.claude are dotfiles or need the exact emoji name

Availability

FeaturePlaygroundRTEdge.netNotes
Agent filesYesNoWritten by Agent Folder Share: Chrome or Edge on a desktop.
Inline image paste and dropYesNoNeeds the same browser support, but not an active share.

See also