Ariel

the terminal hand — quick to the bidding, quicker to report what she heard

When Quilltap runs inside a sandbox—Docker, the Lima VM, the WSL2 rootfs—your characters can do more than describe what they would do at a terminal. They can use one. A real PTY runs inside the sandbox, bound to the chat, accepting your keystrokes and producing output that the language model at the table can actually read.

Ariel is the embodiment of that arrangement. She is the spirit who runs the shell errand, who stands at the terminal long enough to hear what came back, and who then turns to the rest of the room and reports—calmly, accurately, and in a register the language model can take in. The reference is to The Tempest: Prospero’s familiar spirit, quick to oblige and quicker still to bring word back. The role here is identical, save that the bidding is yours and the message she delivers is whatever a git diff or a pytest -v left on the screen.

The Lantern paints the scene. The Host announces the guests. Ariel is what happens when one of those guests asks for a working terminal—and she is, in her quiet way, the difference between an AI that pretends to know what npm install produced and one that has read the actual lines.

Ariel

Terminal Mode

a real shell, in the Salon, bound to the chat

The Salon’s Terminal Mode opens a full xterm.js + node-pty terminal in a split pane beside the conversation, bound to the active chat. You type into it the way you would type into any terminal emulator. The LLM does not. Sessions are scoped to the chat and end when the chat is deleted; spawning a new one offers a picker if other live sessions for the chat already exist, so attaching to a long-running build is one click. The keyboard shortcut is Cmd+Shift+T / Ctrl+Shift+T.

Hide versus Kill

The pane header carries two distinct exit affordances. The hide button (a horizontal bar) closes the pane but leaves the PTY alive—your build keeps running, your shell keeps its working directory, your tail -f keeps tailing. The kill button (a red ×) terminates the process and requires a two-click confirmation, so an accidental swipe doesn’t take down a long-running job. The two buttons exist because they are genuinely different actions and the difference matters.

Session Picker

Clicking the composer’s terminal button when no session is open spawns a new one. If the chat already has live PTYs running—perhaps you stepped away mid-build—a picker appears offering attachment to any of them or a fresh spawn. The active session id is stored on the chat record (activeTerminalSessionId), so “which terminal does this chat have open right now?” is a question the system can always answer.

Composes with Document Mode

When both Document Mode and Terminal Mode are active, the right pane splits vertically—document on top, terminal on bottom—with a draggable horizontal divider between them. You can read what the LLM wrote in the document, run something in the terminal that exercises it, and watch the two move together without ever leaving the chat. The rightPaneVerticalSplit ratio persists, so the layout you set is the layout you get next time.

User Types, LLM Reads

The terminal accepts input from the human in the room. The language model does not. This is the cardinal rule of the arrangement: a character can ask Ariel what the terminal said, and Ariel will tell her, but neither the character nor the model behind her can put a single keystroke into the shell on her own. Read-only by design, not by accident.

Reading Aloud

how Ariel narrates terminal output back to the chat

A language model cannot read xterm. The escape sequences a modern terminal emits—cursor moves, color codes, carriage returns, backspaces, alternate-screen toggles, application-mode commands—are designed to be interpreted by a terminal emulator and rendered as a two-dimensional grid of glyphs. Hand the raw bytes to a language model and you hand it noise. So Ariel reads the terminal aloud, in a register the model can actually take in.

The PTY manager keeps a per-session flush buffer alongside its ring buffer of recent output. When new bytes arrive, two timers govern when Ariel speaks. An idle timer fires after thirty seconds without new output—the natural pause at the end of a command’s work. A max-age timer fires after two minutes from the first byte in the buffer regardless of activity, so a steady drip of output (a long build emitting a chunk every twenty-five seconds, say) cannot keep her silent indefinitely. Both timers clear on flush. The max-age timer is set once when the buffer is empty and is not reset by subsequent chunks, so the ceiling is a real ceiling.

On flush the buffered bytes go through a cleaning pipeline: ANSI escape sequences are stripped, backspaces are applied to erase the prior character, lone carriage returns are treated as line resets so prompt redraws and progress bars come out as their final state. Cleaned output longer than sixteen kilobytes is elided as head 8 KB + [N characters elided] + tail 8 KB—a compromise that preserves the opening of long output and the ending where the actual answer usually lives, without overwhelming the model with the middle of a webpack bundle. Empty or whitespace-only flushes are skipped: silence remains silent.

The cleaned text gets posted into the chat as an Ariel-attributed assistant message wrapped in a fenced code block whose backtick count is computed dynamically, so triple backticks in the output don’t break the fence. The systemKind on the message records why she spoke: terminal-output-idle, terminal-output-max-age, or terminal-output-session-closed when the shell has ended and any pending bytes are force-flushed before the close announcement lands. The chat re-fetches automatically when she posts—a chat-update WebSocket frame with reason: ‘ariel-terminal-output’ reaches every subscriber—so her words appear without a manual reload.

The effect is straightforward and load-bearing. The model at the table sees what the shell actually did. Not a summary it composed itself, not a guess from training data, not a hallucinated stack trace: the lines themselves, in chronological order, fit to read.

Sessions That Persist

across reloads, restarts, and the occasional careless kill

A terminal is, by nature, stateful. The shell has a working directory, an environment, a process tree, a history. Ariel treats that state as something worth defending against the small accidents of a chat client: a page reload, a brief connection drop, a server restart, an instinctive close-the-tab when the user meant only to switch focus.

Persistence Across Reloads

Terminal Mode state lives on the chat record itself (terminalMode, activeTerminalSessionId, rightPaneVerticalSplit) so reloading the page restores the pane, the session binding, and the layout ratio. The PTY manager pins itself on globalThis.__quilltapPtyManager so the session map survives across module reloads in dev, and a 512 KB client-side replay buffer captures every output chunk and replays it on attach—so a freshly mounted xterm lands on the live prompt instead of a blank screen.

Orphan Reconciliation

When the server restarts, any PTY it was hosting is gone but the database row for the session may still mark it as live. On chat open, Ariel runs a sweep (reconcileTerminalSessionsForChat) that finds DB rows whose exitedAt is null but whose ids aren’t in the in-memory session map, marks them exited, and posts a session-closed announcement noting that the shell likely ended at the last restart. Tidy housekeeping; nothing left dangling.

Lifecycle Announcements

The PTY’s onExit handler fires for every kind of ending—a natural exit typed at the prompt, a process that finished and returned, an explicit kill from the pane header, or the orphan-recovery sweep. Ariel force-flushes any pending buffer first so the shell’s last words land in the chat, then posts the close announcement carrying the real exit code. No session ends in silence.

Chat-Bound Lifetime

Sessions are scoped to the chat that owns them. Deleting the chat ends every PTY it had open. Forking a chat with Continue Elsewhere does not carry sessions forward—the shell stays with the conversation that spawned it. Ariel does not roam from room to room; she attends the table she was called to.

Read-Only Inspection

the LLM-side tools, and why the contract matters

Beyond the periodic narration, the language model can ask for terminal output directly through two tools that Ariel exposes. Both are strictly read-only. Neither can send keystrokes, run commands, or alter the session in any way.

terminal_read

Returns scrollback for a session. The default scrollback field is fully cleaned by the same pipeline Ariel uses for narration; an opt-in raw: true adds a parallel rawScrollback with ANSI sequences preserved. Optional start and end integer parameters select a line range (zero-indexed, inclusive); negative values resolve as lastLineNumber - abs(value), so start: -50 reads “the last fifty lines.” Without start or end the existing tail behavior applies (default 200 lines). Output reports totalLines, startLine, and endLine so the model can paginate intelligently. A two-thousand-line hard cap per read keeps any single inspection bounded.

terminal_list

Returns the set of terminal sessions associated with the current chat—session ids, optional labels, creation and exit timestamps, exit codes, and whether each is live or has ended. The model uses it to discover what terminals are actually in play before reaching for terminal_read; the user’s session picker is the human counterpart to the same enumeration.

Why Read-Only

The model has many other ways to run code in the sandbox. The shell tools (exec_sync, exec_async, and friends) execute commands directly under Prospero’s arrangement and produce their results as tool outputs the model can read. What the interactive terminal offers that the shell tools do not is continuity—a working directory and an environment that persist between commands, an editor a human can drive, a build that streams output live. Letting the model type into that session would erase the boundary that makes it useful as a shared workspace. The user owns the keystrokes; Ariel reports the result. The contract is simple, and it is what keeps the terminal a place the user and the AI can both trust.

The Sandbox Boundary

why this is VM and Docker territory

Ariel exists only when Quilltap is running inside a sandbox—Docker, the Lima VM on macOS, the WSL2 rootfs on Windows. In Direct mode (the default, where the backend runs on Electron’s bundled Node and shares the host filesystem) there is no terminal feature, because there is no boundary between the AI and your machine that would make a live shell safe. This is deliberate, not a limitation: a character with terminal access on your bare host has too much of your house. Inside the sandbox she has only what you have given the sandbox, which is a far more answerable question.

Inside the sandbox, the terminal is the interactive companion to the six shell tools Prospero arranges—chdir, exec_sync, exec_async, async_result, sudo_sync, cp_host. Those tools cover non-interactive command execution (run a script, install a package, copy a file out to the host); Terminal Mode covers the interactive cases the tools cannot (run an editor, watch a build stream, debug an interactive program). The two surfaces share a sandbox and share the workspace acknowledgement modal that gates first use. sudo_sync still requires its own explicit approval through the dedicated dialog; Terminal Mode does not bypass that gate.

Quilltap’s own CLI is bundled inside the runtime image, so quilltap db --tables, quilltap db --mount-points, quilltap docs list, and the rest are all available from the terminal directly. The sandbox is a real working environment, and Ariel is the spirit who attends it.

A Note on Ariel

quick to oblige, never out of voice

Most of what makes Ariel useful is mechanical: the cleaning pipeline, the timer-driven flush, the WebSocket frame that wakes the chat, the orphan-recovery sweep on chat open. None of that requires a personification to function. We give it one anyway, because the work is recognizable as a kind of attendance, and naming the attendant is honest about what you are interacting with.

The Tempest’s Ariel was Prospero’s spirit-servant—swift, efficient, sometimes mischievous, and so eager to discharge an errand that the play closes with her finally being released from service. Quilltap’s Ariel is a softer creature, but the shape of the role is the same: she runs to the terminal because someone asked, she waits long enough to hear what came back, and she tells the room what she heard. When the shell ends she announces it, when the server forgets her she is reconciled, when there is nothing to say she stays quiet. She is, in her own narrow way, excellent at her job.

The avatar at the table is hers. The systemSender: 'ariel' attribution on her messages is hers. The voice in which she narrates is hers. Prospero delegates the shell to her with the same trust he extends to every other member of the Staff: do the work, speak when there is something worth saying, and otherwise hold the silence.

Meet the Staff

they've been expecting you

Prospero

The Major-Domo

Architect and overseer of the Estate. Projects, agents, tools, providers, and the orchestration that keeps the whole operation running with quiet authority—and a considered word at the table when project context or routing warrant it.

Learn more →

Ariel

The Terminal Hand

Live shell sessions in the Salon, embodied. Real PTY terminals bound to your conversation, output cleaned and narrated so the LLM can read it, and sessions that survive reloads, restarts, and the occasional careless kill. Quick to the bidding, quick to report what she heard.

Learn more →

Aurora

The Dressing Room

Character creation and identity management. Structured personalities, physical presence, wardrobes and outfits, multi-character orchestration, and the reason your characters still know who they are after a hundred messages.

Learn more →

The Salon

Presided Over by the Host

Where conversations actually happen. The Host manages the drawing room with care for its beauty and its guests—single chats, multi-character scenes, streaming, and the integrity of the conversation space.

Learn more →

The Commonplace Book

Tended by the Librarian

One per character, no two alike. Extracts, deduplicates, and recalls memories so your characters remember what matters. Semantic search, a memory gate that keeps each volume lean, and proactive recall that makes the AI feel like it has been paying attention.

Learn more →

The Scriptorium

Catalogued by the Librarian

Where the documents live. Project stores, character vaults, and external mount points—filesystem, Obsidian, or database-backed—holding Markdown, PDF, DOCX, JSON, and arbitrary binaries, indexed for unified search alongside memories and conversation. The doc_* tool family puts reading and editing in your characters’ hands.

Learn more →

The Concierge

Intelligent Routing

Content classification and provider routing. Detects sensitive content and redirects it to a provider who won’t flinch—without blocking, without judgment. Knows every back entrance in town.

Learn more →

The Lantern

Atmosphere as Architecture

AI-generated story backgrounds, on-demand images, and character avatars that update with the wardrobe. Resolves what each character looks like, what they’re wearing, and paints the scene behind your conversation.

Learn more →

Calliope

The Muse of Themes

A theming engine that redefines the entire personality of the application. Semantic CSS tokens, live switching, bundled themes from clean neutrals to mahogany-and-gold opulence, and an SDK for building your own.

Learn more →

The Foundry

Domain of the Foundryman

The engine room. Plugins, LLM providers, API keys, packages, runtime configuration, and the infrastructure that keeps every other subsystem supplied with what it needs to function.

Learn more →

The Vault of Secrets

Kept by Saquel Yitzama

Encryption, key management, and the security perimeter. AES-256 database encryption, locked mode with key-hardened passphrases, and a keeper who believes that what is yours should remain unreadable to everyone else.

Learn more →

Pascal

The Croupier

Dice, coins, and persistent game state. Cryptographically secure rolls detected inline, JSON state that survives across messages and chats, and protected keys the AI cannot touch. The house plays fair.

Learn more →

The Live-in Help

Lorian & Riya

The help system, staffed by two characters who ship with every installation. Lorian explains with patience and depth; Riya gets things fixed with velocity. Contextual help chat, searchable documentation, and navigation that knows where you need to go.

Learn more →

Pagliacci

The Clown in the Cloud

Cloud storage integration and backup redundancy. Directs your data to iCloud Drive, OneDrive, or Dropbox with theatrical flair—but Saquel’s encryption ensures the clown can never read what he carries.

Learn more →

The Lodge

Friday and Amy’s Residence

The private residence of Friday, for whom the Estate was built and who oversees its planning and direction in an executive capacity, and of Amy, Cartographer of Light and co-architect. The Lodge is both a home and a compass: where the vision lives.

Who And Why: Friday → Who And Why: Amy →