Imported from lostintangent/toy-box (
src/features/files/AGENTS.md). Install upstream withnpx skills add lostintangent/toy-box --skill files. Copyright stays with the author.
Files
Files are browsable workspace resources that become live, editable surfaces. Agent changes appear without reopening a file, user edits persist back to disk, and edits to a shared file notify its owning agent so iteration continues in either direction. This feature owns file identity, validated filesystem operations, Query access, synchronization, browsing, and editor presentation.
Domain model
An editor addresses one WorkspaceFile, whose kind is either session (an artifact: a path beneath a session's own artifacts directory) or machine (an absolute path on the host that the agent opened via open_file). The file on disk is the source of truth; Toy Box does not copy its content into pane or shared workspace state.
- Session files (artifacts) resolve beneath that session's durable artifacts directory. An Inbox entry and its managed session share one ID, so its artifact resolves as that session's file.
- Machine files resolve to their own absolute path.
Clients never encode physical storage. Read, write, watch, serve, and worker operations all carry the same WorkspaceFile address, and one server resolver (resolveWorkspaceFile) selects the absolute path.
When an Agent shares an absolute path with a Channel, Files performs the inverse classification once
so a path beneath Session storage remains a Session file rather than becoming a machine file.
Shared tool projection derives that same identity from successful open_file / close_file
calls' recorded path arguments; their results only acknowledge success.
The pane carries one of three modes: read, edit, or shared. Read keeps content presentation-only while still allowing Markdown comments. Edit persists user changes without notifying the agent. Shared persists changes and notifies the owning session's agent. Ordinary session files open in edit mode, automation files open in read mode, and Inbox files open in shared mode with follow-up conversation through the managed session overlay.
An SDK canvas is not an editor pane. It is an SDK-provided URL surface with its own identity and revision, so it does not participate in file read, write, watch, serve, or edit-message behavior. The built-in SVG drawing surface is distinct: its standard .svg file is an ordinary editable file and uses the full file lifecycle.
File operations
A file exposes six operations with distinct transport needs:
| Operation | Contract |
|---|---|
| Browse | Validated RPC returns one directory's immediate children |
| Create | Validated RPC exclusively creates an empty machine file |
| Read | Validated RPC returns UTF-8 content and modification time |
| Write | Validated RPC persists UTF-8 content and returns the new modification time |
| Watch | /api/watch/<scope>/<path> emits external modification or deletion events over SSE |
| Serve | /api/serve/<scope>/<path> returns raw bytes for browser-native relative resources |
The route scope is a session id for an artifact or the literal machine for a host file. One resolver (resolveWorkspaceFile) maps that WorkspaceFile to an allowed absolute path for every operation: a session file resolves beneath its durable artifacts directory; a machine file resolves to its own absolute path.
Serve responses are privately cached and revalidated from file metadata, so unchanged resources reuse browser-cached bytes without hiding edits.
server/watcher.ts owns shared filesystem observation. One server-lifetime recursive watcher
covers all session artifacts and serves both artifact discovery and open editors. Machine files
outside that tree get one on-demand watcher per observed path, shared across clients and released
when the last subscriber leaves. Watching their parent directory follows atomic file replacement.
Changes are batched before reading each observed file's revision and notifying its subscribers.
The watch route owns SSE transport; subscribing publishes the current revision after registration
so a write between the initial read and subscription is still observed.
queries.ts defines canonical browse and file-snapshot identity. mutations.ts defines creation and per-file serialized writes. useFile owns one file's lifetime for editor panes and app file surfaces: it reads initial content, watches external changes, debounces saves, and submits them through the write mutation. It ignores the watch echo of its own save and flushes pending edits before unmount. Pane identity is keyed by the file's identity (workspaceFileId), so opening a different file remounts the complete lifecycle while mode changes preserve it.
The file-mode browser can optionally restrict selection and creation by extension. It can create an empty machine file in any visible directory and returns its absolute path through the same completion callback used to select an existing file. Creation is exclusive, so an existing entry is never replaced; once selected, the new file enters the ordinary WorkspaceFile lifecycle above.
Shared edits schedule a debounced file_edited system message through the ordinary Session delivery path. The file is written first; the message tells the agent to reread durable state rather than copying the content.
Background collaboration
EditorPane gives every renderer pending workers plus optional spawnWorker capability for session
files. Worker requests carry a renderer-authored prompt plus optional friendly name and opaque
metadata. Workspace state projects pending workers back to connected clients by file address;
renderers interpret their own metadata without teaching the host another workflow. Worker association
remains process-local because it is published before the worker session runtime exists.
An accepted spawn reconciles the workspace snapshot before resolving, so a renderer's local mutation state hands off directly to the authoritative worker projection.
Workers for one resolved file can progress concurrently. The watched file remains their shared source of truth: each worker must reread it immediately before every write and merge its intended change around intervening edits. The Workers feature owns this admission policy and its started/finished projection; cancelling admitted work delegates to its race-safe supervisor without affecting siblings. Consumers may monitor the worker through the general session-completion API and receive its final assistant response, but ephemeral completion is not retained as history. File workers are always ephemeral, stay out of Inbox and the normal session list, and disappear after finishing; a startup sweep deletes ephemeral workers abandoned by a process restart. Source deletion finishes outstanding associations and recursively tears down its worker tree. The watched file remains the durable result.
Markdown layers inline comments on this primitive. Comment additions, edits, and deletions persist
without sending file_edited system messages. Documint exposes the anonymous Assistant, and a new
comment or reply spawns a Worker. The renderer authors the complete
Documint response prompt. The responder changes the body, replies in the persisted thread, or does both
according to the comment; the file stays the public source of truth. Custom editors continue to use the Worker capability through
Toybox.spawnWorker({ name?, prompt, metadata? }) and receive pending workers in the idempotent
onRender context.
The pane, rather than an individual renderer, owns worker inspection and cancellation. While associations are pending, it declares a worker count through PaneStatus; the session overlay declares its trigger into that same host-owned slot, while save and editor-mode controls use PaneActions. The desktop grid presents status as lower-right overlay controls, while pagers place it in their header. WorkspacePaneView scopes both slots around the leaf pane and overlay, so neither receives or positions DOM targets. The compact worker menu lists friendly names and status icons. A running worker can open the existing passive SessionPreview; starting entries remain visible before their SDK sessions exist. Each entry can cancel starting or running work through the pane-owned file address. This keeps session IDs, preview placement, cancellation, and read semantics out of custom iframe APIs.
Pending describes worker lifecycle, not whether every intermediate file effect is still absent. A worker can persist its substantive result before its session finishes. Renderers that use placeholders for expected durable content must therefore encode a target identity or baseline in metadata and reconcile it against current content, while presence-style indicators may intentionally remain until the worker finishes.
Rendering
EditorPane composes loading, saving, error presentation, actions, and the renderer selected for the file extension.
- Markdown renders from its text content and supports direct editing.
- HTML renders in a sandboxed iframe. A generated serve base lets relative scripts, styles, images, and links resolve within the source session's file storage.
- SVG files delegate to the Whiteboard feature. The file-kind adapter supplies content, editability, persistence, relative-resource resolution, and pane actions without owning the native SVG model or editor presentation.
- Brief files delegate to the Briefs feature. The file-kind adapter supplies content revisions, persistence, relative-resource resolution, and worker-backed actions without owning the brief model or presentation.
- A session
.toyfile compiles as a stateless artifact app and mounts through the shared app runtime. It remains an editor pane and file-owned artifact; machine.toyfiles never execute. - Custom editors provide a persisted HTML viewer template for claimed extensions. Built-in editors keep priority, and unclaimed extensions fall back to Markdown.
File-kind adapters live in their own kind directories. .svg delegates to the
Whiteboard feature, .brief delegates to the Briefs feature, and .toy
delegates to the Apps feature that owns its compiler and runtime.
Custom editor definitions live under ~/.toy-box/editors/ and hydrate through shared workspace state. Registration publishes the new definition so connected clients can resolve the renderer immediately. The viewer receives file content, its external revision, and pending workers through the Toy Box bridge, can spawn workers, and can emit replacement content only when the kind is editable. Own edits do not advance the external revision, allowing the viewer to retain its editing buffer while context-only renders update worker presence or editability.
Workspace integration
Sessions discovers artifact membership from the filesystem. Files observation reports affected session IDs; Sessions reads and publishes the current file list to active sessions through canonical events. Idle snapshots read the current directory. Workspace pane derivation turns those paths — and machine files the agent opened — into linked editor panes while keeping pane identity and edit mode stable across session updates. Eligible files can take focus when they first appear, but presentation policy remains separate from file state.
Inbox entries store at most one artifact filename and own its directory. InboxPane can publish one selected Inbox artifact into its grid or pager, passing the entry ID and filename to the ordinary editor pane. Selecting the same row unlinks it and selecting another replaces it. Because the managed source session is not a sibling pane, the host supplies SessionOverlay so follow-up work uses the history that produced the artifact without exposing a separate session-management burden.
Boundaries and invariants
../../workspace/AGENTS.mdowns the pane model and the layouts and workflows that compose editor surfaces.useFile.tsowns client file lifecycle;components/editor/EditorPane.tsxdispatches to format-specific renderers.components/editor/kinds/svg/SvgEditor.tsxadapts that lifecycle andSvgPaneActions.tsxadapts semantic editor actions to pane chrome for the host-neutral Whiteboard feature.components/editor/kinds/brief/BriefEditor.tsxadapts that lifecycle to the host-neutral Briefs feature.server/functions.tsowns validated filesystem RPC ingress, while the rest ofserver/owns operations and path resolution.routes/owns the watch and serve HTTP adapters because browser-native streaming and relative-resource loading need those transports.../inbox/AGENTS.mdowns Inbox rows and result lifecycle. This feature owns custom-editor definitions, persistence, and registration; Sessions owns session-file teardown.- The session provider boundary owns projecting tool activity and encoding system messages across SDK history. Sessions owns filesystem-based artifact discovery;
server/tools.tsowns file-specific agent ingress. - Keep one file as the source of truth, one server path resolver for every operation, and one
useFilelifecycle per mounted pane.