
A 2x2 group of durable Moor sessions with the sidebar tree and fleet telemetry
Supported agents
Desk has a built-in integration for each of:- Claude Code
- OpenAI Codex
- OpenCode
- Qwen Code
- Kimi Code
- Grok
- bash (or any custom command)
“Integration” here, not “profile”. Profile means one thing in Desk: a named
provider account a session runs under — see
agent profiles.
resume
in the manifest works for all of them.
Claude Code, Codex, and OpenCode sessions can render as a native chat surface
(uiMode: native); Qwen, Kimi, and Grok are terminal-only;
bash and custom-command sessions render as terminals. Each SDK-backed session
can instead run in terminal mode — the raw CLI TUI in a terminal cell — by
setting uiMode: terminal on the session, and the edit modal switches a
session between modes in place.
The native chat surface

Native agent sessions: markdown transcripts, tool rows, and a composer per cell
- Transcript — user and assistant turns render as markdown with role-accented rows and turn separators. Long feeds are virtualized, so thousand-row histories scroll smoothly; an unread marker shows what arrived since you last looked, and a jump pill returns you to the live tail after scrolling up.
- Tool calls — every tool invocation is a compact row with a status dot, elapsed time, and a disclosure that opens the exact input and output the agent saw. Sub-agent activity nests under the tool row that spawned it.
- Composer — a resizable input with file attachments (button, paste, or drag-and-drop), a slash-command palette fed by the commands the agent actually advertises, and a Send control that becomes Stop while a turn runs. A pulsing indicator marks the whole time the agent is working — from the moment you send until the reply, including tool execution.
- Permissions — approval requests dock above the composer with the proposed command or file diff, and resolve inline.
- Continuity — transcripts survive reloads and browser switches: the server keeps the session’s event history and replays it to every surface, and the agent process itself lives in its Moor session, so nothing dies with the tab.

The slash palette lists the commands the connected agent advertises
Durable Moor sessions
Every managed session runs under a Moor holder keyed by its durablesessionId. This gives Desk
three important properties:
- closing the browser does not kill the agent
- restarting Desk reattaches to running work
- sessions can be captured, restarted, or booted without changing the UI model
The sidebar
The sidebar is a projects → groups → sessions tree:- Session rows show a live status lamp: running, needs input (pulsing), or missing. Attention bubbles up to collapsed group and project rows.
- Group rows show running/total counts and a boot-missing action when some sessions are down.
- A filter row narrows the tree by session, group, or project substring, and a needs-input chip filters to sessions waiting on you, with a count.
- Projects, groups, and sessions reorder by drag and drop, and a session can be dragged into another group to move it. Order persists to the manifest.
- Row actions cover add, edit, info, restart (with confirmation — it kills the running agent), and delete.
Groups and layouts
Groups organize agents by project, lane, or responsibility. Layout kinds are1x1, 2x2, 3x3, 4x4, linear (all cells in one row), and custom,
with 1–16 cells. The layout badge in the multiplexer header switches kinds
in place; +/− buttons add and remove cells.
Cells are resizable: drag the separators and the split proportions persist per
group in the manifest, restoring exactly after a reload. Sessions map to cells
by assignment — drag a session tab onto a cell, or tap an empty cell to assign
one from an inline picker.
Group switches are cheap by design: recently visited groups stay mounted with
live cells (a warm budget of roughly 40 sessions on desktop), so flipping
between groups opens no new connections and loses no transcript or terminal
state.
Terminal rendering
Terminal cells — bash sessions, custom commands, and SDK agents running withuiMode: terminal — use xterm.js in the browser and a server-side terminal
daemon for transport. One binary WebSocket per browser tab carries the tab’s
terminal surfaces. A hidden surface keeps its channel but relinquishes input,
resize, and output-delivery authority until reveal. The daemon owns the Moor
holder connections, fans each session to every viewer (a desktop tab and a
phone see the same process), retains one bounded shared output suffix, and
catches a same-channel reveal up with one contiguous packet when its cursor and
geometry revision are still valid. A gap or revision change falls back to a
current-screen snapshot; retained history remains available through the frozen
scrollback viewer. Output streams only to visible cells. Unsubscribe is reserved for actual
surface removal or transport loss. SUBSCRIBE carries the fitted geometry for
new and reconnected channels; later RESIZE frames are sent and applied only
when that geometry changes, so revealing a cell cannot advance the terminal
revision solely because its layout observer repeated the same dimensions.
Rendering uses hardware WebGL where available, under a shared budget of 8
contexts — cells beyond the budget (and machines with software-only GL) fall
back to the DOM renderer. Hidden cells release their context to visible ones,
and only the focused cell blinks its cursor.
File paths printed by an agent are links: click one to open it in the editor.
Relative paths resolve against the folder the session was launched in (a cd
inside the session is not tracked), and a guessed path that is not on disk is
refused with one notice instead of opening.
Scrollback: append-style agent output scrolls natively in the live buffer,
which already carries the daemon’s retained history (colors and layout
preserved, native scrolling and selection); full-screen TUI programs get
application-owned scrolling with agent-aware key encoding. A custom scroll
rail on the cell edge tracks position — except on Grok panes, whose TUI draws
its own scrollbar, so Desk’s rail steps aside instead of competing with it.
Terminals self-heal: if the connection drops, cells show a reconnect overlay
and automatically re-arm on tab return, network recovery, or the first
successful poll after an outage — waking a laptop reconnects the whole wall
without a click.
Attention signals
Session state comes from the agent itself. Desk installs lifecycle hooks into each supported CLI, and those hooks post typed events — turn opened, turn finished, tool started, tool ended, permission requested — to the daemon, which owns one canonical state per session. Native-mode sessions report the same facts through the SDK. Desk does not read the terminal to work out what an agent is doing, and a session whose hooks have not fired reads as unknown rather than being guessed at. That state surfaces as:- a pulsing lamp on the session row and its collapsed ancestors,
- an entry in the events drawer with kind filters, unread tracking, and mark-all-read,
- an attention sound (respecting the mute toggle).
If a session sits on
unknown, run desk hooks install and restart it — hooks
are read at launch, so a session started before they existed never reports.Command palette and keyboard
Ctrl+K opens the session quick-switcher — attention-needing sessions first,
then recent, then tree order, fuzzy-matched across session, group, project,
and session ids. Ctrl+Shift+K opens it even while a terminal has focus.
Ctrl+Alt+1..9 focuses cell N; Ctrl+Alt+←/→ cycles sessions tree-wide.
See Keyboard shortcuts for the full map, including
in-terminal copy, paste, and find.
The header
The header carries fleet stats (project/group/agent counts, RUN and MISS chips — MISS is clickable and boots the missing sessions), host telemetry cells with sparklines (CPU, RAM, GPU, network, disk), a clock, and the toolbar: Refresh, Up (start all missing), the emergency kill switch, sound toggle, events drawer, and settings. The config-path button opensdesk.yml
directly in the editor. On phones the toolbar collapses into a burger menu
and telemetry into a compact strip.
Mobile
Below 860 px the active subsystem owns the screen: sidebars become slide-over drawers with a tap-to-close scrim, and the multiplexer becomes a swipeable one-cell-per-screen pager whose indicator diamonds are state-tinted — the active cell expands into a named pill, and attention pulses so you can see who is screaming from the pager alone.Operational controls
- Refresh re-reads fleet state; a 2-second pulse keeps liveness, attention, and telemetry current in between (paused while the tab is hidden), so an externally killed session flips to missing within a tick — no manual refresh.
- Up starts every missing session from the manifest without touching running ones; groups and individual cells have their own boot actions.
- Restart kills and relaunches one session (confirmed first).
- KILL is the emergency stop: it kills all Claude Code and Codex CLI processes found by the host sweep and retires every configured Moor session. It confirms with an alarm before acting. Use it as a last resort, not a routine control.