> ## Documentation Index
> Fetch the complete documentation index at: https://www.plungeai.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Ocean pager — the terminal UI

> Ocean pager is oceancode's terminal UI. A plain oceancode execs Ocean pager (ocean-pager); the pager and the Ocean engine run in one process over typed ACP channels — no HTTP between UI and engine. --db, --directory, --listen and --verbose are passed through.


Ocean pager is `oceancode`'s terminal UI. A plain `oceancode` execs Ocean pager (`ocean-pager`); the pager and the Ocean engine run in **one process** over typed ACP channels — no HTTP between UI and engine. `--db`, `--directory`, `--listen` and `--verbose` are passed through.

The first-party ratatui TUI was removed on 2026-10-05 (its guide pages are in `archive/native-tui/docs/`). For compatibility `--tui pager` and `OC_TUI=pager` are accepted and change nothing (`--tui grok` is refused as unknown); `--tui ocean` and `OC_TUI=ocean` fail with "the native TUI was removed; `oceancode` starts Ocean pager". The pager's own slash commands, keys and settings are in [Ocean pager reference](/oceancode/pager-reference).

## Binary

1. Build the pager: `bash scripts/release/build-pager.sh` (or `cd ocean-pager && cargo build --release -p ocean-pager-bin --locked`) → `target/release/ocean-pager`, the binary `oceancode` launches.
2. A plain debug build lands in `target/debug/ocean-pager` and is never launched. To run a pager from elsewhere, set `OC_PAGER_BIN`.

Discovery order: `OC_PAGER_BIN` → `ocean-pager` beside `oceancode` (and beside the binary a symlinked `oceancode` resolves to) → `ocean-pager` on `PATH`.

`oceancode` sets `$OC_PAGER_HOME` to `<data>/oceancode/pager` (`$XDG_DATA_HOME`, else `~/.local/share`) — the directory a bare `ocean-pager` uses too — so the pager never shares `~/.grok`. Auto-update, remote fetch, feedback and voice are off. Every pager variable: [Environment](#environment).

## Environment

Read by `ocean-pager`, not by the engine. Every pager variable is `OC_PAGER_*`; the old `GROK_*` names are not read.

| Variable | Effect |
|---|---|
| `OC_PAGER_HOME` | The pager's state directory (`config.toml`, sessions, debug logs, the extracted ripgrep under `bin/`). `oceancode` sets it; unset → `$XDG_DATA_HOME/oceancode/pager`, else `~/.local/share/oceancode/pager`. `GROK_HOME` is ignored. |
| `OC_PAGER_LOG_FILE` | Write the pager's debug log to exactly this path (honors `RUST_LOG`). |
| `OC_PAGER_DEBUG_LOG` | A path, or `1`/`true` for `<pager home>/debug/tui-<pid>.txt`: turns the debug log on. |
| `OC_PAGER_CONFIG` | Inline JSON config overlay, merged above `config.toml`. |
| `OC_PAGER_CONFIG_PATH` | An extra JSON or TOML config-overlay file (read by extension), merged above `config.toml`. |
| `OC_PAGER_MANAGED_CONFIG_FAIL_CLOSED` | Overrides the requirements fail-closed setting (applies to `requirements.toml` only). |
| `OC_PAGER_SHELL` | Shell the pager spawns: an executable path (bash, zsh, …); on Windows `pwsh`, `powershell` or `cmd`. |
| `OC_PAGER_THEME` | Force a theme name, or `auto`, without editing the config. |
| `LC_OC_PAGER_THEME` | Same as `OC_PAGER_THEME`; the `LC_` form survives SSH `SendEnv LC_*`. |
| `OC_PAGER_APPEARANCE` | `dark` or `light`: the terminal background `auto` themes assume. |
| `LC_OC_PAGER_APPEARANCE` | Same as `OC_PAGER_APPEARANCE`, SSH-forwardable. |
| `OC_PAGER_TERMINAL_THEME` | Follow the terminal's own palette (on/off; a requirement pin wins). |
| `OC_PAGER_NERD_FONTS` | Nerd Font glyphs in the git status (on/off). |
| `OC_PAGER_FORCE_LEGACY_CONSOLE` | Windows: ASCII-safe glyphs for the legacy console. |
| `OC_PAGER_SCREEN_MODE` | Internal: the screen mode a `/fullscreen` / `/minimal` relaunch hands to the new process (read and removed at startup). |
| `OC_PAGER_SCREEN_MODE_SWITCH` | `exec` forces the legacy re-exec switch between screen modes. |
| `OC_PAGER_SCROLL_SPEED` | Mouse-wheel scroll speed; applied on first load only (the settings modal persists it). |
| `OC_PAGER_SCROLL_MODE` | Mouse-wheel scroll mode; first load only. |
| `OC_PAGER_SCROLL_LINES` | Lines per wheel notch; first load only. |
| `OC_PAGER_INVERT_SCROLL` | Invert the wheel direction; first load only. |
| `OC_PAGER_SCROLL_CADENCE_MS` | Redraw cadence while scrolling (ms). |
| `OC_PAGER_MIN_DRAW_MS` | Minimum interval between frames (ms). |
| `OC_PAGER_DISPLAY_REFRESH_PROBE_ENABLED` | Probe the display refresh rate at startup (on/off). |
| `OC_PAGER_DISPLAY_REFRESH_AUTO_CADENCE` | Derive the draw cadence from the probed refresh rate (on/off). |
| `OC_PAGER_SCROLL_DEBUG` | Show the scroll debug HUD. |
| `OC_PAGER_SCROLL_LOG` | Log scroll input events (unset or `0` = off). |
| `OC_PAGER_FPS` | Show the frames-per-second HUD. |
| `OC_PAGER_MEMTRACE` | Periodic memory trace in the debug log; `0`/`false`/`off` disables. |
| `OC_PAGER_MEMTRACE_INTERVAL_SECS` | Memory-trace interval (seconds). |
| `OC_PAGER_MEMTRACE_THRESHOLD_MB` | Memory trace logs only above this resident size (MB). |
| `OC_PAGER_MOUSE_REPORTING_TOGGLE` | Enable the mouse-reporting toggle (on/off). |
| `OC_PAGER_SHOW_THINKING_BLOCKS` | Show model thinking blocks (on/off; overrides the setting). |
| `OC_PAGER_GROUP_TOOL_VERBS` | Group consecutive tool calls by verb (on/off; overrides the setting). |
| `OC_PAGER_SHOW_EDIT_DETAILS` | Show each edit's diff inline (on/off; overrides `[ui] show_edit_details`). |
| `OC_PAGER_SHOW_RUN_DETAILS` | Finished agent Runs show the command and first output lines (on/off; overrides `[ui] show_run_details`). |
| `OC_PAGER_COLLAPSED_EDIT_BLOCKS` | Legacy: start edit blocks collapsed (on/off); read only when `show_edit_details` is unset everywhere, as its inverse. |
| `OC_PAGER_CONTEXTUAL_HINTS` | Contextual hints on/off (overrides `[ui.contextual_hints]`). |
| `OC_PAGER_TIPS_OVERRIDE` | Debug builds only: `|`-separated tips that replace the configured ones. |
| `OC_PAGER_SLASH_COMMAND_TAGS` | JSON map of tags shown beside slash commands in the `/` menu. |
| `OC_PAGER_DEFAULT_SELECTED_PERMISSION` | Which permission-card option the cursor starts on. |
| `OC_PAGER_ESC_DOUBLE_PRESS_MS` | Window for the double-Esc gesture (ms). |
| `OC_PAGER_DOCK` | The dock above the prompt on/off; on by default (`0` brings back the Tasks pane at the top; a requirement pin wins, `[features] dock` in the pager config comes after). |
| `OC_PAGER_DOCK_V2` | Same switch as `OC_PAGER_DOCK`, read after it. |
| `OC_PAGER_AGENT_DASHBOARD` | `0` turns `/dashboard` off. |
| `OC_PAGER_WORKSPACE_DASHBOARD` | Use the workspace (v2) dashboard layout instead of the persisted v1 layout. |
| `OC_PAGER_SESSION_PICKER_GROUPED` | `1`/`0`: group the session picker (overrides `[cli] session_picker_grouped`, default on). |
| `OC_PAGER_HUNK_TRACKER` | Hunk-tracker mode (overrides `[ui] hunk_tracker_mode`). |
| `OC_PAGER_SUGGESTIONS` | Shell-command ghost suggestions (off by default). They request `ocean/suggest`, which the Ocean engine does not serve, so nothing shows. |
| `OC_PAGER_SUGGESTIONS_AI` | AI variant of `OC_PAGER_SUGGESTIONS` (same limitation). |
| `OC_PAGER_SUGGESTIONS_AI_MODEL` | Model id for `OC_PAGER_SUGGESTIONS_AI`. |
| `OC_PAGER_MCP_PUSH_SERVER_STATUS` | Push MCP server status updates into the UI (default on). |
| `OC_PAGER_ENVRC_TIMEOUT_SECS` | Timeout for evaluating a directory's `.envrc` (seconds; `0` disables). |
| `OC_PAGER_CONNECT_UI_TIMEOUT_SECS` | Seconds before startup shows the connect-failure report (6 s floor). |
| `OC_PAGER_PROMPT_ACK_TIMEOUT_SECS` | Prompt acknowledgement timeout (seconds). |
| `OC_PAGER_EXIT_TIMEOUT_SECS` | Force-exit this many seconds after a quit if teardown hangs (default 20; `0` disables). |
| `OC_PAGER_COPY_FILE` | Backup file every copy is also written to (default `<pager home>/last-copy.txt`). |
| `OC_PAGER_OSC52_SINK` | Emit clipboard writes as OSC 52 for a wrapping terminal. |
| `LC_OC_PAGER_OSC52_SINK` | Same as `OC_PAGER_OSC52_SINK`, SSH-forwardable. |
| `OC_PAGER_CLIPBOARD_NO_OSC52` | Never emit OSC 52 (for hosts that paint it as garbage). |
| `OC_PAGER_CLIPBOARD_NO_NATIVE_READ` | macOS: don't read the native pasteboard. |
| `OC_PAGER_CLIPBOARD_NO_DATA_CONTROL` | Linux: skip the Wayland data-control clipboard protocol. |
| `OC_PAGER_SQLITE_JOURNAL_MODE` | SQLite journal mode for the pager's local databases (the WAL kill switch). |
| `OC_PAGER_RESET_CHILD_OOM` | Linux, internal: set on child processes so they reset the inherited OOM score. |
| `OC_PAGER_EVENT` | Set for notification hook commands: the event name. |
| `OC_PAGER_MESSAGE` | Set for notification hook commands: the message text. |
| `OC_PAGER_SESSION_ID` | Set for notification hook commands: the session id. |
| `OC_PAGER_BUNDLE_RG_PATH` | Build time: embed this ripgrep binary instead of downloading the release tarball. |
| `OC_PAGER_RG_VER` | Build-internal: embedded ripgrep version, set by `ocean-pager-ripgrep/build.rs`. |
| `OC_PAGER_RG_TARGET` | Build-internal: embedded ripgrep target triple. |
| `OC_PAGER_RG_SHA256` | Build-internal: SHA-256 the extracted ripgrep is verified against. |
| `OC_PAGER_CHANGELOG_OFFLINE` | Tests: never fetch the changelog. |
| `OC_PAGER_MDM_NO_EXPAND_TEST` | Tests: macOS managed-preferences layer. |
| `OC_PAGER_TEST_OPEN_URL_FILE` | Tests: write opened URLs to this file instead of launching a browser. |
| `OC_PAGER_TEST_FRAME_WRITE_DELAY_MS` | Tests: delay each frame write (ms). |
| `OC_PAGER_TEST_HOLD_TEARDOWN_SECS` | Tests: hold teardown to exercise the exit timeout. |

## What is wired to the Ocean engine

`/`, `/mcps`, `/skills`, `/plugins`, `/hooks`, `/workflows`, `/model`, and `/effort` use the pager chrome and Ocean `Services`. MCP add/toggle, plugin enable/disable, hook reload, and tool permission cards go through the same ACP methods Grok Build uses. The `/workflows` picker lists built-in and discovered project/user workflows over `ocean/workflows/list` (empty when the `workflows` section is disabled); launching, pausing, resuming and stopping a run go through the `workflow` tool, not an ext request.

**Skills complete in the composer.** Typing `/` and a prefix lists your **enabled**, user-invocable skills alongside the pager's own commands — a plugin skill under Claude Code's plugin-qualified name (`/superpowers:writing-plans`, so `/super` finds every superpowers skill), a project or user skill under its bare name (`/formatcode`); the prefix narrows the list, `Tab`/`Enter` inserts `/<skill> ` into the message box so you can keep typing, and sending dispatches it — the engine expands the skill body into the turn (a typed bare `/writing-plans` still runs the plugin skill too). The candidates come from `ocean/skills/list` (fetched at session start; the Ocean engine does not advertise skills as ACP commands), so a **disabled** skill is never offered. A `Space` toggle on the Skills tab of the capabilities picker updates the popup live, and the `<available_skills>` block and `skill_*` tools shrink on the next step ([09 · Capabilities](/oceancode/capabilities#enabling-and-disabling-skills)).

**The capabilities picker.** Each tab (`/skills`, `/plugins`, `/hooks`, `/mcps`, `/workflows`, Marketplace) lists only its own kind. On the Skills tab, `Space` toggles the skill under the cursor and the change sticks (it writes `~/.oceancode/skill-state.json`); `/` opens a search that filters the rows by the skill's name, its slash id, its description and its author, case-insensitively, and a query that matches nothing reads `No matches`. `a` / `d` enable / disable every skill behind a confirm prompt. Each row shows its source — `project`, `user` or `plugin: <name>` — grouped by source.

**Group and master toggles.** On the Skills, Plugins, Hooks and MCP tabs `Space` also works above the single row; every change applies on the next step and costs one cache rewrite ([09 · Capabilities](/oceancode/capabilities#enabling-and-disabling-skills)):

| Row / key | Action |
|---|---|
| `Everything` (first row) + `Space` | Master switch: off removes skills, MCP tools, hooks and plugin rules from the agent's context, built-in tools stay; asks first |
| `All skills` / `All plugins` / `All MCP` / `All hooks` (second row) + `Space` | The whole kind on or off; asks first |
| `a` / `d` (Skills tab) | Same as `All skills` on / off |
| A `Plugin: <name>` header (Skills, MCP) or a hook source header + `Space` | Every row in the group; asks first above 10 rows; an all-off group reads `[off]` |
| A row + `Space` | That one skill, plugin, hook or MCP server |

The picker's last row shows the live counts (`skills 22/259 · plugins 2/21 · mcp 1/10 · hooks 8/8 · applies on the next step (one cache rewrite)`), prefixed `EVERYTHING OFF` while the master switch is off. The Plugins tab also lists Claude Code's cached plugins under `User (Claude)`. The **Marketplace** tab reads `~/.oceancode/marketplaces/**` only when you open it (opening `/skills` or `/plugins` no longer touches the marketplace), and marketplace content never reaches the prompt or the tool list.

## Pasted images

Paste an image from the clipboard (`Ctrl+V` / `Cmd+V`) or drag an image file onto the composer; it shows as `[Image #1]` in your text and goes to the model with the prompt ("what's wrong with this layout? [Image #1]"). Every route carries it — Anthropic, OpenAI chat and Responses (xAI, Groq, OpenRouter, …), Gemini. With several images, each is labelled `[Image #N]` in the request, so the model matches the name in your text to the right picture.

Limits: at most 20 images per prompt, **3.75 MiB** per image once stored (about 5 MB once base64-encoded, the strictest limit of the routes), and **8000 px** on either side (Anthropic refuses a larger image before the model runs; `sips -Z 2576 <file>` brings it under). The engine sends PNG, JPEG, GIF and WebP — the types every route accepts, decided by the image's bytes, not its declared type. The pager re-encodes a TIFF (what a macOS screenshot usually is on the clipboard), BMP, ICO or GIF as PNG before it sends, so those paste and drag in like any other; PNG, JPEG and WebP go as they are. An image over the limits after that, a type the pager cannot decode, or an `http(s)` source refuses the whole prompt with a message naming the image; nothing is dropped silently.

**Dragging from another app.** In Ocean AI Terminal a file dragged in from Photos or Finder becomes a chip the same way, and since desktop build 0.8.11 the drop also brings the terminal window to the front: macOS does not activate the window a drag lands on, so with an older build the keys you type next, Enter included, still go to the app the image came from until you click the terminal once.

**Resizing.** Images are resized so they never make a session fail (the same rules apply to images the `read` tool returns):

- An image over **2576 px** on its long edge (the most a current Claude model reads; larger is downscaled by the provider anyway) is shrunk to 2576 px when it is pasted or read. The resize is Lanczos3 with the aspect ratio kept; PNG stays PNG and JPEG stays JPEG, GIF and WebP become PNG.
- When one request carries **more than 20 images**, Anthropic applies a stricter size limit to every image in it, so any image over **2000 px** is sent as a 2000 px copy. The stored session is not changed.
- If the provider still refuses an image for its size, the engine retries once with every image at or under the limit the provider named, and keeps that limit for the rest of the session.
- Past **100 images** or **24 MiB** of image data in one request, the oldest images are replaced by a one-line note (`[<name>, <W>x<H> omitted from this request: …; read the file again to see it]`).

**Text-only models.** When the catalog says the current model reads no images (its models.dev entry lists input modalities without `image`), the text still goes and the image does not; a session note says so: `Image #1 not sent: <model> doesn't accept images`. A model the catalog cannot describe — an offline seed entry or one you declared under `providers.*.models` — gets the image. Switching to a text-only model after pasting drops the media from the request on the next turn — and an image a tool returns (`read` on a PNG) is replaced by a one-line note on such a model — so the provider does not answer 400.

The image is stored inline on the user row of `oceancode.db` (`"files":[{"uri":"data:image/png;base64,…`); a reopened session replays that row as text only, and the headless and server prompt bodies ([07 · Headless and server](/oceancode/headless-and-server)) carry text only for now.

## The dock: sub-agents above the prompt

Running sub-agents, background commands, monitors, loops and queued prompts live in one dock directly above the
prompt, one section each (`Agents`, `Tasks`, `Watchers`, `Queued`). While a turn runs, its status rides the dock's
first header instead of taking a row of its own:

```
▾ Agents 2  ⸬ Working 2m31s                                     1m20s ⇣12k [stop]
 ⸬ Find every caller of resolveWorkflowAccess — Running: rg -n …   1m15s silent · 4 tools · 38k tok · 2m13s
 ✓ Map the picker code paths                                       9 tools · 52k tok · 48s
```

A sub-agent row is the task's description, led by a badge only when the mission gave the sub-agent a persona or role
(`Researcher`), then its live activity; the right column shows its tool calls, tokens and elapsed time, plus
`Ns silent` once it has sent nothing for 10 s — amber from a minute, the one to open. The dock counts the agents, so
the Ocean bar does not. `Ctrl+G` hides and shows the dock; a section folds on `Enter` or a click on its header, `show
N more` opens a long one, `↗` opens a row and `[stop]` stops it. One blank row separates the transcript from what
sits under it; nothing else is spaced.

The transcript keeps a record: `Subagent running: "…"` when a sub-agent starts and `Subagent completed in 48s: "…" ·
52k tok · 9 tools` when it ends; the running line does not repeat the dock's live figures. `OC_PAGER_DOCK=0` (or
`[features] dock = false`) brings back the Tasks pane at the top, the live figures on the transcript row and the
`agents N` count in the bar.

Under a work mode with an at-once limit ([23](/oceancode/work-modes#limits)), a sub-agent that must wait for a free slot
gets a dock row of its own with a hollow glyph, `waiting (4/4 running)`, which turns into its running row when a slot
frees (or closes if the turn is stopped first). Model, the default, has no such limit, so this row never appears
there.

## The work-mode picker (`F4`)

`F4` or `/mode` opens the work-mode picker: who decides how agents work together — the model (Model, the default),
Jev (Auto), or you (seven diagrams). The list sits on the left and the highlighted mode's picture on the right; moving
the cursor redraws it at once. `Enter` uses the mode for this session, `d` makes it the current agent's default, `e`
copies a built-in into `.oceancode/modes/` to edit, `Esc` closes. `/mode <id>` and `/mode default` do the same
without the picker. Every mode but Model shows its badge on the prompt's top border, after the `ultracode` badge when
that is on (`delegate ≤4`, `race ×3`, `auto → goal · Jev 0.82`); Model shows none, so the border is unchanged. F2 ›
Agent defaults holds a mode row per ring agent, and F2 › Sub-agents the three sub-agent limits
([22](/oceancode/pager-reference#editor-agent-and-models)). The modes, limits and requirements are on
[23 · Work modes](/oceancode/work-modes).

## Watching a sub-agent

Open a sub-agent with a double-click or `Enter` on its dock row (`↗`), its transcript row or its Agent Dashboard row:
the view fills with what the sub-agent does as it does it — its bash commands and their output, reads, greps, edits
with their diff, and its text and thinking — with its own context bar. `q` or `Esc` closes it.

The engine streams a running sub-agent's activity live under the sub-agent's own session id, while the parent's turn
runs and afterwards for a background sub-agent. A finished sub-agent whose view holds nothing yet — or whose view was
freed after you closed it — is rebuilt from `oceancode.db` when you open it (`ocean/session/replay`). Sub-agents of a
session you resumed, or of an earlier pager run, do not open yet.

## Goal mode (`orchestrate: always`)

Set `orchestrate: "always"` in the `workflows` section of `oceancode.json` and a plain prompt is
no longer a single turn — it launches the built-in `goal` workflow, which plans the objective
into verifiable deliverables, works in rounds with one resumed worker, and converges only when a
panel of adversarial skeptics fails to refute completion (identical gaps two rounds running, or a
spent budget, pause instead of looping). The pager's Workflows dock shows the run advancing
through its **Plan → Work → Verify** phases, with the sub-agents of each round beneath it. The
Claude-settings `ultracode` flag does **not** turn this on — only the explicit `orchestrate`
opt-in does. Headless, the same goal run is `oceancode goal "<objective>"` (see
`07-headless-and-server.md`). Since work modes, `orchestrate: "always"` with no `mode` set is read as the Goal work
mode ([23](/oceancode/work-modes)).

## What the pager does not do

Login, billing, worktrees, `/imagine` and Grok memory flush are unsupported (`-32601`).

Connecting a provider is in the pager: pick a gray `/model` row, or `/connect <provider>`. OpenCode-style auth methods:

- **API key** — most providers (Google, Groq, Perplexity, …).
- **ChatGPT Plus/Pro** — OpenAI, via OpenCode’s JSON deviceauth (`auth.openai.com/codex/device`), or paste an API key.
- **Claude Pro/Max** — Anthropic PKCE (paste the browser code), or paste an API key.
- **GitHub Copilot** — GitHub device flow (`github.com/login/device`).
- **claude-code** — `oceancode claude` (Claude CLI login).
- **Bedrock / Vertex** — environment/config, not a prompt.

Already-connected providers open a manage overlay: switch API key ↔ subscription, or **d** to disconnect (removes `auth.json`; env-backed providers stay connected until the env var is unset). Credentials go to the same `auth.json` as `oceancode key` / `oceancode login`.

## Expired subscription

Ocean runs on the Claude subscription by consuming the active slot Claude Code keeps in
`~/.claude/.credentials.json` (`claudeAiOauth`); it never rotates that slot's refresh token. On
macOS, once that file's token is within five minutes of expiry, Ocean also reads the Keychain item
Claude Code refreshes itself into (`Claude Code-credentials`, read only) and uses whichever carries
the later expiry, so a self-refresh by Claude Code no longer strands Ocean an hour later; no file
still means no login. When the slot's access token has expired and carries no refresh token, a
prompt cannot be sent, and the
turn fails in the transcript with a **terminal, actionable** message —
`Claude subscription token has expired: Run oceancode login to reconnect your Claude account.` —
and **no** retry countdown or network banner (the failure is not a transport error and is never
retried). Run `oceancode login`, complete the Claude Pro/Max browser flow, and send the prompt
again. (Headless, `oceancode run` prints the same message to stderr and exits non-zero.)

The same holds when the slot expires **mid-session**: it was valid when the turn started, so the
request reached Anthropic and the server rejected the now-dead token with an HTTP 401. On the
subscription route that 401 (and a 403) is the same terminal `run oceancode login` failure — it is
never retried — whereas a 401 on an API-key provider stays a generic error (its fix is a new key,
not a re-login).

## Coming from the native TUI (removed 2026-10-05)

The pager renames some surfaces of the removed native TUI and has no `ctrl+x` leader — it uses
direct chords. The pager equivalents:

| Native TUI | Ocean pager | Notes |
|---|---|---|
| `/copy` | `/toggle-mouse-reporting` | Toggle terminal mouse capture so text selection/copy works. |
| `/mcp` | `/mcps` | The MCP-servers picker. |
| `/models` | `/model` | The model picker (also gates provider `/connect`). |
| `/cockpit` | status bar + `/context` | No unified cockpit panel; the status bar plus `/context` show the same figures. |
| `ctrl+x` leader chords | direct chords | The pager has no `ctrl+x` prefix. Use `ctrl+p` (command palette), `ctrl+\` (Agent Dashboard), `ctrl+t` (todos pane), `ctrl+m` (multiline / model picker off the prompt). |

## Shift+Tab ring and permission modes

`Shift+Tab` cycles Build → Auto → Plan → Ask → Debug → Build. Build, Plan, Ask and
Debug are the engine's agents (the curated ring in `crates/ocean-acp/src/modes.rs`);
Auto is the one permission stop, Claude Code's "accept edits": the agent stays Build,
the prompt border shows `auto` in yellow, and the engine's permission mode is
`acceptEdits` — edits run without a card, bash and outside-directory asks still
prompt (or go to Jev, see [13](/oceancode/jev-decisions)), and the `question` tool always
asks. Cycling on from Auto drops back to ask before the switch to Plan. The stop is
absent while always-approve is on, and when the auto feature is gated off
(`[auto_mode] enabled = false` in the pager config).

The same mode is reachable from `/auto`, `/always-approve` (`ctrl+o`; the engine's
`bypassPermissions`, every card auto-answered by the pager) and the `permission_mode`
row of the settings modal. Each change is persisted to the pager config and sent to
the engine as `ocean/yolo_mode_changed`, so the engine's mode and the border flag
agree (see [12 · Permissions and sandbox](/oceancode/permissions-and-sandbox#permission-modes)).
A subagent's ask surfaces as its own card, labelled with the subagent.

The agent is the bold flag on the prompt's bottom border (`Build` blue, `Plan` violet,
`Ask` green, `Debug` orange); the permission mode follows it (`auto` yellow,
`always-approve` red). The Plan agent is plan mode, so it shows once, as `Plan`;
`plan approval` and `commenting L3` appear beside it while a plan is under review.

Each agent can carry a default model and effort (`agents.<id>.{model,effort}` in
`oceancode.json`, edited under F2 › Agent defaults): Shift+Tab to that agent puts the
session on them, the effort clamped to the levels the model offers, and status-bar row 1
follows. `Option+Tab` (or `F3`, for terminals that don't send Option as Meta) steps the
session's effort up through the current model's levels, wrapping to the lowest, and
prints the same "Switched to …" line as `/effort`. That change lasts for the session:
the next Shift+Tab loads the agent's own default again. An agent with no defaults keeps
the session's model and effort.

A settings push reaches an open session live — a fleet-router Apply, an F2 edit, or a
hand edit of `oceancode.json` or `~/.claude/settings.json`. The pager shows one toast naming what
moved, for example `Settings applied: sub-agents effort medium · build effort max`, and
status-bar row 1 shows the new values. The values it names are the main model and effort, ultracode,
the sub-agent model and effort, FORCE, and each agent's model and effort. When the push changes
the row of the session's current agent and the session still runs that row's old value, the session
moves to the new one. A Mesh › Main change moves a session on the previous Main model unless its
agent has a row of its own: the row wins (today that leaves Debug, which has no row). A model or effort
you changed by hand in this session (`/model`, Option+Tab) is kept.

## Question cards

When the agent asks with the `question` tool, a card replaces the composer: `↑/↓` walk
the answers, `Enter` picks one (or starts typing on the last row, where you answer in
your own words — that text reaches the model as written), `←/→` move between questions.
An option can carry a **preview** (a snippet, a layout, a diff to compare), shown under the
options while that option is focused, and the recommended option says why after its description.
Nothing else in the agent's response runs while you are asked: a tool call it made after the
question is answered "not run" and the agent makes it again once it has your answer, if it still
needs to; calls made before the question finish before the card appears.

- **`Ctrl+B` back to chat** leaves the question to give the agent more first — notes,
  pasted images, links. The turn stops, the answers you had picked land in the composer
  as `My answers so far:`, and the agent is told you stopped to add context; send your
  message and it thinks again and asks only what is still open.
- **`Esc`** steps back (out of the text row, then clears a pick); **`X`** dismisses the
  card without answering; **`Ctrl+C`** cancels the turn and the open question with it.
  A second `Ctrl+C` while the turn is still cancelling arms quit, and a third quits.

## Resuming: the session tree

`/resume` (and the home screen's list) shows your **main sessions** only, most recently active
first. A session that spawned sub-agents carries a fold glyph and says how many in the right
column — `› Pager fixes   3 sub · 5m ago` — and the sub-sessions stay out of the way until you
open it:

- **`→`** (or `e`) on a parent row opens it: its sub-sessions are fetched the first time and
  drawn indented underneath; a sub-session that spawned its own folds the same way, as deep as
  the work went. On a row with nothing to open, `→` shows the detail card as before.
- **`←`** closes the detail card if it is open, then folds the sub-sessions away, then moves the
  selection up to the row's parent.
- **Typing** searches titles on the engine, across main sessions and sub-sessions alike. A hit
  is drawn under its main session, with the rows between them dimmed when they did not match,
  so you can tell which run it belongs to. Clearing the search brings the folded list back.
- A row badged **`open`** is already running in another pager window on this machine; open it
  there instead of resuming it twice.
- `Enter` resumes whichever row is selected, a sub-session included.

The engine names a session after the first prompt that says something — `Hi`, `Yeah`, `Do it`,
`Continue`, `[Image #1]` and the like name it only until a real request arrives, so a list of
greetings does not happen. `/rename` still wins over any automatic title.

## Search counter and tideline tiers

The top-right status row shows a `search N · 0.3s` chip once the session has run a
`grep` or `glob` tool call: `N` is the number of calls, the time is the last call's
latency (green ≤ 1 s, yellow ≤ 5 s, orange above). Click it for the breakdown —
calls by tool, last / average / total latency, bytes and files scanned, and how
many recursive `grep -r` / `find` the model ran through bash (and how many of
those were rewritten to `rg`). The figures are folded from each tool result's
metadata (`elapsed_ms`, `files`, `bytes`, `search_redirect`) in `oceancode.db`,
so they survive a restart; nothing is counted in memory.

The Ocean context row (`ctx … N% full · tideline …`) is tiered against the
tideline limit: green under 80 % of it, yellow from 80 %, orange at or over it
(the gauge, the percentage and the tideline clause share the tone). With the
ledger off the same tiers apply against the context window. `tideline ×12 @30% …`
carries the number of turns past the tideline (`/tideline` prints the same
figure); no `×N` while it is zero.

The `bash` tool rewrites a plain recursive `grep -r …` to the bundled ripgrep
(POSIX shells; every flag must have an `rg` equivalent, patterns are literal-safe)
and prepends `[oceancode] ran as: rg …` to the result; any other recursive
`grep` or a `find … -name` runs as typed under a one-line hint naming the grep /
glob tools. A leading `cd <dir> &&`/`;` prefix, a trailing `2>/dev/null`,
`| grep -v …` / `| head` followers, and BRE `\|` alternation are carried or
translated verbatim, so the common slow pipelines still rewrite. The ripgrep directory leads the child `PATH`, so `rg` is available
in bash on every OS where a binary resolves (`OC_RG_BIN` overrides; see 02).

## Ocean session commands

These act on the Ocean engine over `_ocean/*` / `ocean/*` ext methods (Ocean backend only):

| Command | What it does |
|---|---|
| `/tideline` | Bare shows the ledger state; `/tideline now` cuts the ledger; `<n>%`, `<tokens>`, or `default` set the tideline (the floor and the presets were removed — a second number or a preset word is refused with the usage line). The old `/shelve` still works as a hidden alias. |
| `/retention` | Bare shows how long idle sessions are kept and what the next sweep would delete (`retention 7 days · 12 sessions pending · first sweep after <date> (grace)`, or `… · last sweep 5m ago, 340 swept`); `<days>` sets `retention.days` in the global config and re-arms the sweep at once; `off` is `0` (keep everything). An out-of-range number is refused with the engine's message. While sessions wait out the 24-hour grace window the context row shows `retention N pending` and one notice is toasted. See [18 · Operations](/oceancode/operations#retention-and-vacuum). |
| `/budget` | Bare shows the spend cap; `$5` (or `5`) caps USD, `off` clears it, `subscription` switches to list-price mode. |
| `/limits` | Bare shows the per-turn bounds; `steps <n>` caps steps, `time <90m\|1h30m>` bounds wall-clock. |
| `/subagent` | Bare reports the effective sub-agent model + source; `<provider/model>` pins one, `default` / `off` / `clear` removes the override. |
| `/mode` | Bare opens the work-mode picker (as `F4`); `<id>` sets this session's work mode; `default` clears it, so the agent's or the config's mode applies again. Answered with a toast, `Mode: <label>`. See [23](/oceancode/work-modes). |
| `/plan add\|done\|cancel\|edit\|remove` | Edit the live todo table (1-based rows); bare `/plan` still enters plan mode. |
| `/redo` | Re-apply the turn a `/undo` (`/rewind`) reverted, and the files it put back. |
| `/share` | Share this session via a public URL (Ocean backend). |
| `/unshare` | Revoke the current session's share link. |
| `/notify` | Toggle desktop notifications; `/notify on\|off` sets it explicitly. |
