ROBIONDocs

The phone client

The phone is a small web app the Robion server serves at /m/, reached over your Tailscale network. This file covers pairing, installing it, push, its screens, the chat and its limits.

What it is

  • A web app (PWA) served by the Robion server itself — the Mac’s server or a Linux server. It shows the state of every task, lets you answer permissions and questions, sends prompts and photos, reviews and merges changes, and reads the Inbox and the board. It is not a terminal: the terminal tab shows the text of the screen, with a key bar for dialogs. Dividers the agent drew across the wide desktop terminal are shortened to the phone’s width there (since 0.20.28), so a rule is one line, not five. Since 0.20.29 the screen is read cell by cell, so a line that wrapped in a narrow pane keeps the space at the wrap (words ran together before).
  • The server stays on loopback; Tailscale Serve exposes it as HTTPS to your tailnet only. Every phone authenticates with its own token (a cookie set at pairing); each pairing can be revoked separately (paired devices under ⚙ → Servers).
  • Language and theme follow the server (⚙ → Appearance); the page reloads when they change.
  • Swipe from the left edge to go back. An offline strip at the top says “no connection to the server — retrying”; when a new Robion ships, a banner offers reload.

Pairing

  • On the Mac: ⚙ → Remote access → enable. Prerequisites: Tailscale.app signed in and HTTPS certificates enabled for the tailnet (admin console → DNS → HTTPS Certificates). The section reports the problem otherwise: “Tailscale not found”, “Tailscale is not running”, “the tailnet has HTTPS certificates disabled”, or “Tailscale Serve does not point at the server — try disabling and enabling again”.
  • Then pair phone: a QR code and a link (/pair?code=…), valid 10 minutes and single use; new QR code mints another. Scan it on the phone: the browser sets the cookie and opens the app. Allowed tailnet accounts restricts who may pair (empty = anyone in the tailnet who has the link).
  • On a Linux server: robion-server pair prints the same one-time link.
  • The same pairing serves the desktop in a browser: a link ending in &to=d (⚙ → Remote access → desktop in a browser, or robion-server pair --desktop) lands on /d/, the whole window, instead of the phone client — workspace.md, “The desktop in a browser”.
  • A phone that is not paired shows “This phone is not paired” with the steps. unpair this phone in the phone’s ⚙ deletes the cookie; pairing again needs a new QR code.
  • New token (⚙ → Remote access → generate a new token) signs out every device and the hooks (they are reinstalled); the phone must be paired again.

Installing it as an app

  • Push notifications work only in the installed app, so the phone guides you to install it: on iOS, Safari Share → Add to Home Screen, then open Robion from its icon; on Android a real install the app button; elsewhere the browser menu → “Install app”. The hint can be hidden; the same steps sit in the phone’s ⚙.
  • Without the tailnet HTTPS certificate the PWA cannot be installed on iOS.
  • The status bar is opaque in the app’s background colour (it follows the theme) since 0.20.12. Before, the app drew under it and iOS 26 blurred the top of every screen (the task name and back looked washed out). iOS reads that setting only when the app is added to the home screen: after updating, remove the Robion icon and add it again from Safari, otherwise the blur stays.

Push notifications

  • Web Push straight from the server to the installed app, end-to-end encrypted, no third-party service. Enable it in the phone’s ⚙ → notifications → enable notifications; send a test notification there, or from the Mac (⚙ → Notifications → send a test notification, “Push to phones”).
  • A push arrives when an agent starts waiting for you (a permission: “agent asks for permission”; a question or a prompt in the terminal: “agent waits for a reply”) or finishes a turn nobody has seen yet (“done”). The title is the task’s name (for a relayed server’s task followed by ” · ” and the server’s name), the detail follows. No push while a focused client already shows that pane (for a relayed server the Mac’s own view of it counts); the same news for the same pane repeats at most once a minute.
  • A permission push carries Allow / Deny buttons — answer from the lock screen. A tap on any push lands in the task, even when the app was closed.
  • Since 0.20.28 the terminal of the task shows a toast when the phone decided (“the phone allowed Bash”, “the phone answered: Tea”), so whoever sits at the Mac knows why the dialog went.
  • Automations can push too (the “push to the phone” action) and the Steward pushes its plan.
  • States shown: “enabled on this phone”, “blocked by the system — allow notifications for Robion in Settings”, “this browser cannot push”, “notifications work only in the installed app”.
  • So the phone has time to react, the Mac holds a permission request for the phone: ⚙ → Remote access → Hold permissions for remote access (max. 55 s: Claude Code draws the dialog after a few seconds anyway and stops listening to the hook at 60 s). While held, the terminal’s status line reads “robion: the permission is on the phone” (since 0.20.28). The hold ends when a client on the Mac opens the pane or when the time is up — then the answer is typed into the terminal instead.

On a tablet

The client splits the display by its width (CSS px; a rotation changes it):

  • Under 768 px (a phone): one screen at a time; ‹ back and a swipe from the left edge go back.
  • 768 to 1023 px (a tablet held upright; since 0.20.37): a task fills the display and its header has show the overview (the panel icon) in place of ‹ back: it slides the overview, with the bottom bar under it, over the task from the left edge; a swipe from the left edge does the same. Picking a task or a tab of the bottom bar closes it; so does a tap on the dimmed task. The overview, inbox, board and settings fill the display as on a phone; a diff and the controls keep ‹ back. Before 0.20.37 the overview stayed docked at this width and left the task barely half of it.
  • From 1024 px (a tablet on its side, a browser window): the overview with the bottom bar stays docked on the left (340 px) and the task opens next to it; ‹ back closes the task and leaves the overview alone.

Screens

  • The bottom bar (since 0.20.16): overview, inbox (with the count of items as a badge), board, settings — on every top-level screen; a task or a diff fills the display without it. Before, the board and the inbox hid behind ⚙ and the inbox pill.
  • Overview (the first screen): every project as a large row — icon, name and one line summing up its tasks (“1 waiting for you · 3 waiting for a prompt”, amber when something waits for you, green for a finished turn nobody has seen, blue while an agent is working, “no running task” otherwise); a tap on the row folds the project to that one line (▸) and a tap unfolds it (▾); the phone remembers the folded projects per device. fold all projects / unfold all projects sits at the bottom next to new project. The project row is a raised band; under an unfolded project its open tasks hang indented on a guide line below the project’s icon (since 0.20.27 — a task used to sit further left than the project’s name and read as one), the ones needing you first, with a state dot and label; allow / deny straight on a waiting row. Projects nobody touched for the Mac’s Inactive projects days (⚙ → Application on the Mac, default 14; no prompt, no new task, no agent at work or waiting) sit under one inactive · n header at the end of their server’s list, folded until tapped — each server’s group on its own, remembered per device (since 0.20.27; per server since 0.20.31). On the right of each project row the icon buttons controls (▦, when the project has a panel) and new task (+): the form opens under the row. N waiting for you in the header and the subscription limits (“5 h x % · 7 d y %”). “no project — add one on the Mac”: projects are added on the desktop. Coming back from a task, the list stands where you left it (since 0.20.32; it used to jump to the top).
  • Servers on the phone (since 0.20.17): the phone pairs with one server — the one that served it (the Mac’s, or a Linux server paired with robion-server pair) — and that server relays every other server the Mac’s ⚙ → Servers knows and has paired (the hub). Once the Mac knows another server, the overview groups projects under a strip per server: this server (with its host) first, then each relayed server by its name and state — connected, connecting…, unreachable — retrying, not paired on the Mac (the box is in ⚙ → Servers but was never paired there), another version — waiting for its update (a protocol mismatch; the Mac updates the box by itself). A relayed server’s tasks open, answer, chat, send photos, show changes and controls like the home server’s; their pushes arrive from the home server with the server’s name after the task (“build · lab”), and no push comes while the Mac shows that pane. What stays the home server’s: the Inbox, the board, automations, the Steward and the limits. A Linux server relays nothing (it has no desktop pairings), so its own phone app sees only itself. Pairing a box on the Mac is enough: nothing to set up on the phone.
  • Task: the header shows the state, the task’s branch (a worktree task), the permission mode chip and the agent’s step list (“3/7”, steps / hide); tabs chat, terminal, changes, aside (side questions, since 0.20.35; a Claude Code task), and design for a task with a design record (workshop projects). seen marks a finished turn reviewed; close task ends the session.
  • changes: “since the agent’s last turn” or “uncommitted”; each file with its status (new, modified, deleted, renamed, conflict) opens a red/green diff; the branch line and Merge into main for a worktree task (below).
  • INBOX (#/inbox): what the automations left for you — “an agent proposes a rule”, “proposed — waiting for your go”, “blocked by your limits”, “failed”, “started by the Steward”, “finished — the agent’s last message”; buttons enable, open, accept, run anyway, dismiss, snooze (until the next working day 08:00). Since 0.20.4: the header pill N need you; each item shows its age (“5 min ago”) and “updated 5 min ago” when a later turn refreshed it; dismiss on a proposal, a proposed rule, a blocked or a failed item asks why? — the Steward reads it tomorrow with one-tap chips already done / not now / not needed / wrong premise plus or in your own words…, then dismiss / back (a finished or started item dismisses at once); a group of two or more finished, failed or blocked items offers dismiss all (“Dismiss all N items of this group?”). The Steward line at the top: Steward: planning…, Steward: last plan 3 h ago · 7 min · 46 turns · $1.23, Steward: no plan yet or the error in red; plan now when idle, cancel while it runs. See automations.md.
  • BOARD (#/board, the board tab): your cards as three sections todo / doing / done (done shows the last week, the rest folds into + N older); each card with its project’s colour, the first line of its note, the project, from the Steward / from an agent, and the state of the task that came out of it (task running, task waits for you, task finished — review, task idle, task closed). new card… at the top: type a title and a note (optional) and the project picker (no project allowed) appear, then add. A tap on a card opens it as a sheet: title, note, “project · column · origin · created N ago”, the ticket link when it mirrors one; buttons start a task (Claude Code in the card’s project with the card as its first prompt; the card follows the task to doing; a task needs a project — edit the card without one), open task once it has one, → doing / → done / → todo (no drag on a phone), edit (title, note, project; save / cancel), delete (“Delete this card?” → delete / cancel), close.
  • AUTOMATIONS: every rule with its switch (“N rules, M on”); rules are written by an agent on the Mac. Since 0.20.4 each rule also has run now (running… while in flight; the result lands in the rule’s line as last 12:40 · ran / blocked / failed — / dry run, “no runs yet” before the first), and switching one off asks why off? (optional) → switch off / back; a paused rule shows off: under its name.
  • CONTROLS: the same cockpit as the desktop CONTROLS panel, per project: the project and task sections, run buttons (“run”, “started”), send buttons (“sent”), values; actions the phone cannot do say “Mac only”; agent-defined command probes run only after allow. “no controls — ask the agent on the Mac to define some”.
  • ⚙ (SETTINGS), the settings tab: server (host and connection state), tailnet login, version (“server X · phone Y” with reload the app for when the update banner did not show); servers (since 0.20.17, only when the Mac relays other servers): each relayed server with its state and version, and the line “a server paired in ⚙ → Servers on the Mac shows up here with its projects; its pushes come through this server”; appearance on this phone (since 0.20.16): theme = server (follow the Mac’s ⚙ → Appearance, the default) / system (this phone’s OS setting) / dark / light, and text size = smaller / normal / larger — both stored on the phone only, so the phone can be light in daylight while the Mac stays dark; limits (since 0.20.16): each subscription window (5 hours, 7 days, per-model windows by name) as a bar with “n % · resets 16:30” (a weekday when further away), the Claude account, and “last known values — the refresh failed” / “no Claude sign-in on the server” / “the limits are not available” when that is the case; notifications; the automations button; the install steps; unpair this phone.

New task and new project

  • new task under a project: a task title, an optional “first instruction for the agent” (it gets it right after start) and own worktree (“the task gets its own worktree; main stays yours”). The switch is pre-set from ⚙ → project → New task by hand: own worktree on the Mac.
  • new project (since 0.9): the kind (Workshop — “I want to make a thing: on the Mac the design panel leads” — or Development) and a folder name; Robion creates the folder with git init under the Folder for new projects set on the Mac (⚙ → Application). Without it: “first set ‘Folder for new projects’ on the Mac”. A folder created this way is pre-trusted, so Claude Code does not stop at “do you trust this folder?” (since 0.12.11).

The chat

  • The conversation from the Claude Code transcript: you and agent rows, the agent’s replies rendered as markdown, tool calls expandable (output / hide, “running…”, “error”), edits as a red/green diff, “interrupted”, “context compacted”, older messages.

  • message the agent… → send. The box grows for multi-line prompts and a half-typed draft survives the app being killed. Since 0.20.28 the message reaches the session through Robion’s hooks module as a prompt of your own (no paste, no pause, no “pasted text” marker); a slash command, a shell pane or a session whose module is not listening still get it as one paste with Enter a beat later. iOS autocorrect and capitalization are on.

  • A sent message shows at once as “sent · waiting for the agent · tap to hide” until the transcript has it; a busy agent takes it up at the end of its turn. Since 0.20.16 the bubble also goes when the agent’s turn ends after the send, after 20 minutes at the latest, and a tap hides it by hand; a slash command (/clear, /compact) shows no bubble at all, Claude Code runs it without a chat row. Before, a prompt of 20+ characters (Claude Code wraps such a paste in <pasted_content> tags) or a slash command left the bubble hanging for good. While a permission is open the box says “the agent is waiting for an answer — allow or deny first”; while a question is open it hides under the question sheet.

  • attach image: photo library, camera or clipboard paste; “N images” pending, “uploading images…”. On Android, share a screenshot straight to Robion (share target) and pick the task. The files land in the task’s .robion/uploads/ and the prompt gets an [Attached image: …] line per file; the agent reads them itself.

  • Pictures the agent shows: a markdown image in its reply (path relative to the working directory) or an open_file / open_files on a picture (PNG, JPG, SVG and other browser image types; a Gerber file renders as SVG). A tap opens it full screen: pinch zooms, one finger pans, a double tap zooms in and back out. close the picture returns.

  • Since 0.20.28 the chat is fed by the session itself (Robion’s hooks module inside Claude Code): a prompt, a tool row, a result or a reply shows the moment the session stores it, and the agent’s reply appears while it is being written (plain text with a cursor, replaced by the rendered reply when it is done). The transcript file on disk only supplies the history.

  • The chat holds what the terminal shows of the agent’s words. A short note the agent leaves between tool calls (what it found, what it does next) is an agent row like a reply (since 0.20.34); Claude Code stores such a note as a thinking block with text, and before it was left out of the chat while the terminal showed it. Thinking blocks without text stay out.

  • Coming back to a chat that moved on by more than a page (80 rows) while the phone was elsewhere or asleep, the chat shows the newest page whole and older messages loads the rest in order (since 0.20.34). Before, the rows from the last visit stayed in front of the newest page with a hole between them, the stretch in the hole could not be loaded, and a reply read while it was being stored could stay a second time at the top.

  • Since 0.20.36 the chat ends with the newest row whatever the transcript’s size. The file is read as it grows (the last 32 MB at first, then only what was written since); before, a 2 MB window at its end was all the file supplied, and a session whose transcript held many screenshots the agent looked at (half a megabyte a row) had most of its chat outside the window — those rows were taken for newer than the file and laid after its newest ones, so the chat ended with an old tool call while the terminal showed the reply (since 0.20.28). older messages reaches back as far as that first window.

  • While the agent works and no reply is streaming yet, a dim “the agent is working…” bubble with three pulsing dots sits at the foot of the chat (since 0.20.30) — the header says “working” too, but the bubble is where you look after sending a message. The streamed reply takes its place.

Side questions (the aside tab)

  • The aside tab (since 0.20.35) asks about the task beside its conversation while the agent keeps working: type into side question… → ask. A fork of the session answers from what the conversation already holds, with no tools; neither the question nor the answer is written into the agent’s conversation, the chat or the terminal. What it knows and what it cannot do is the same as on the desktop (tasks.md → Side questions).
  • It has its own prompt line, so it also works while the agent waits for a permission or holds a question for you (the chat’s prompt line is hidden then). “answering…” usually takes a few seconds; a dot pulses on the aside tab meanwhile, so you can go back to the chat.
  • The thread is the one the desktop’s side question window shows (last 20 questions, gone when the task closes or the server restarts); the bin button clears it.
  • No answer, each with ask again: “This session cannot take side questions (Robion’s hooks module is not running in it) — restart it.”; “Nothing to ask about yet: the agent has not replied in this conversation.”; “No answer came in time.”; “The answer failed.”.

The agent’s question (AskUserQuestion)

  • “the agent asks”: a sheet of cards. A single choice sends on the tap of its chevron; several questions or a multi-select (“pick everything that applies”) are marked first and sent with the send answer button that stays in view under the list. own answer… is a card that turns into a text field.
  • answer in the terminal at the foot of the sheet (since 0.20.24) opens the terminal tab: the same dialog as text, with the key bar (1–3, Enter) to answer it there.
  • Since 0.20.28, while nobody on the Mac shows the pane, the question is held inside the session for as long as a permission would be (the status line reads “robion: the question is on the phone”): the phone’s answer becomes the tool’s result and no dialog is drawn. Otherwise the answer is typed into the task’s terminal as keystrokes, so the server reads the screen first (since 0.20.24). When the dialog is not there — the session that asked runs in another client, such as the Claude app or Remote Control continuing this conversation as a fork — the phone says “the question is not in this task’s terminal — the session that asked runs in another client; answer it there” and the cards stay until that client answers. Before, such a question showed as an empty “the agent asks for permission” card whose allow typed 1 into the terminal (troubleshooting.md).
  • A plan (plan mode) shows whole, with the terminal’s real choices: approve + auto-accept edits, approve (manual approvals), keep planning with optional feedback.
  • A choice in a dialog (elicitation) is Mac only: “the agent waits for a choice in a dialog”.

Permission prompts on the phone

  • “the agent asks for permission” with the whole request — the command, or the file and the replace/with text of an edit, a pattern, a URL. Buttons: allow, allow and don’t ask again, deny, deny with a reason (“why / what to do instead” — the agent gets it).
  • Several requests can be open at once: subagents working in parallel each ask on their own (the main agent asks one thing at a time). The card shows the oldest — the one Claude Code shows on top in the terminal — with “1 of N” after the tool name; allow / deny answer that one and the next moves up. A tap that arrives after its request was answered elsewhere is refused (“that permission request has been answered already — the pane shows the next one”) instead of landing on the next request.
  • Under the buttons: “the server holds the hook — the answer goes straight to Claude Code” or, after the hold expired, “the prompt is already on screen — the answer is sent as keystrokes”.
  • The permission mode chip switches the session: manual (every edit and command asks first), accept edits, plan mode (read-only), auto (a model reviews permissions).

The terminal tab and the key bar

  • The text of the screen (“last N lines”, more history, refresh) — reading, not a shell. keys for the terminal: Esc, Tab, ⇧Tab, arrows, ⏎, ^C, 1–3 — for the dialogs Claude Code draws itself: the folder trust check, a login, an update prompt.
  • A freshly launched Claude Code that stays silent for 10 seconds gets a hint in the chat: “Claude Code has not reported yet — it is probably waiting for an answer in the terminal”.
  • “Robion’s hooks module did not load in this session (Claude Code x.y.z)” (since 0.20.28): the session started without Robion’s plugin; the task’s state stays unknown and the chat stays empty until it is restarted from Robion (troubleshooting.md).

Task state on the phone

  • The same reading as the desktop: “working”, “waiting for you”, “waiting for a prompt” (idle — the agent finished and waits for your next message; it read “waiting for input” before 0.20.27, which sounded like a question), “done — not reviewed” (a finished turn nobody looked at), “error”, “session ended”. A task whose agent waits on a background shell reads “working · ⏳ shell still running: ” until that shell’s result has been handled (since 0.20.27; tasks.md).
  • A task an automation or the Steward started carries a Steward tag. A worktree task shows its branch; Merge into main (since 0.20) opens a sheet with squash, fast-forward, merge commit or pull request and names the blockers first: uncommitted changes in the worktree, “the agent is still working”, files also changed in main; “main has moved — update the worktree first” (the update is done on the Mac).

What the phone cannot show, and limits

  • No desktop panels: no file editor, tree, 3D, media, LOG or web preview, no diffs beyond the changes tab. What the agent wants you to see on the phone must be a picture it opens (open_file) or a markdown image in its reply; only pictures reach the chat this way.
  • Projects are created only under the new-projects folder; adding an existing folder, defining controls, writing automation rules and resolving a dialog choice happen on the Mac.
  • Controls actions marked “Mac only” (opening a file or URL on the desktop) do nothing here.