Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,17 @@

## Unreleased

- Fixed the menu bar's Down arrow doing nothing past the first press: it was wired to "open the submenu", which selects item 0 unconditionally, so every press re-selected the same row and the items below the first were unreachable by keyboard. Up now walks back up and pops out to the section row from the first item. This is what made the planning backend picker (Agenda → Planning Model, the second item) look like it did not exist.
- Fixed the planning pane's context meter reporting `~0k` for any thread under 1000 tokens — `used / 1000` in integer arithmetic — which read as a meter that was not measuring anything rather than one reporting a small number. Counts below 1k now keep their digits.
- The context meter also now reports what a turn actually sent. Tool results are consumed inside a turn and never persisted (deliberately — it is what keeps a thread portable between a 32k local model and a 200k hosted one), so a turn that read half the repository left a transcript of a few hundred tokens and a meter that barely moved. Each reply carries the peak request size back, shown as `~868/131k (peak 5.4k)`. The first number is still what the *next* turn starts from; the peak belongs to the turn that produced it and is not restored when a thread is reopened.
- Planning turns get 40 tool round-trips instead of 10, and the ceiling is now a per-backend `max_tool_iterations` (an API-class orchestrator's own setting is inherited by its derived backend). Ten hops is a couple of files — not enough to ground a plan in a codebase, which is a grep-read-grep-read walk. Hitting the ceiling also no longer throws the turn away: the final pass runs with the tools removed, so you get an answer written from what was gathered rather than "[tool iteration limit reached — ask a narrower question]".
- A planning turn's failure message no longer collapses to "api error: unknown" against a local server. OpenAI and Anthropic send `{"error": {"message": ...}}`; LM Studio and llama.cpp often send a bare string, and only `["message"]` was read — so the one class of backend whose failures you most need to debug was the one that reported nothing. (What it was hiding here: `Failed to load model "..."`.)
- Added `alt-shift-p`: the planning pane takes the whole output region, docking itself first if it was closed. The session bar and status panel stay visible, so the sessions being planned against remain on screen and keep updating; only their output is covered, and no PTY is resized. `esc` steps back to the split.
- The planning backend picker (`alt-m`, or Agenda → Planning Model) now asks each endpoint what it is serving and lets you pick a model from the answer, instead of only choosing between endpoints on whatever model id the config happened to name. `/v1/models` was already being probed, but the result was used for nothing but a count in the corner of the picker. `Enter`/`→` on an endpoint descends into its model list, `←` backs out, `r` re-probes a server that has since loaded something else. Hosted catalogues are still not probed — they list hundreds of models and are not the thing that changes under you.
- The planning pane no longer requires a second copy of an endpoint you have already configured. Every `[agents.NAME]` and an API-class `[orchestrator]` are offered as planning backends under their own names; an explicit `[planning.backends.NAME]` shadows the derived one. Previously a config with no `[planning.backends.*]` — which is every config that predates the pane — offered nothing to select and cycling the picker failed silently.

- Added the planning pane (`alt-p`): a persistent, read-only, single-agent design chat that docks into a split leaf beside a session. Where the orchestrator chat is an append-only log, a planning thread is a document — it lives on disk, survives a restart, is scrolled back through, and distills into a plan revision handed to an implementation session. The pane shows a thread list, the scope root the thread is grounded in with a count of files that changed since it read them, per-message model attribution with a rule marking where a thread switched models, a context-budget meter, and a backend picker (`alt-m`). Threads over the selected backend's window prompt to compact or switch rather than compacting silently, which would drop the early turns a plan rests on. `alt-i` hands a committed plan to a session as work — a file path plus a freshly recomputed staleness warning, queued if the target is mid-turn.

- Fixed two OpenCode sessions in the same directory reporting identical tokens, context and state: both watchers bound to the newest db session in that directory. Each watcher now claims a distinct one, and releases it when its session goes away.
- Fixed OpenCode sessions getting stuck on WAITING indefinitely. A permission dialog is only visible to terminal pattern matching, so it has to outrank the db watcher's Running/Thinking — but it was never given up, and the watcher only re-reported on a *change*. WAITING now releases when the dialog leaves the screen, when input is sent to the session, or when the watcher reports the turn finished (now re-asserted every 5s rather than 30s).
- Fixed a freshly started full-screen TUI session (OpenCode) sitting on RUNNING forever: the idle-timeout check keyed off the last completed output line, which a TUI that repaints via cursor positioning never produces.
Expand Down
93 changes: 93 additions & 0 deletions docs/panes-and-navigation.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ and shows real token counts while skipping meaningless cost.
| `alt-o` | Focus next pane |
| `alt-b` | Toggle broadcast input to all sessions |
| `alt-g` | Dock the chat pane |
| `alt-p` | Toggle the planning pane |
| `alt-shift-p` | Planning pane over the whole output region (docks it first if closed) |
| `ctrl-space` | Toggle the menu bar |
| `alt-1` … `alt-8` | Switch to session by number |
| `alt-←` / `alt-→` | Cycle sessions |
| `ctrl-q` | Quit (shuts down the server and all sessions; use `alt-d` to leave them running) |
Expand All @@ -71,6 +74,96 @@ and shows real token counts while skipping meaningless cost.
All other input is passed through to the active session's PTY. Keybindings are
configurable — see the [configuration reference](config-reference.md).

### Menu bar keys

`ctrl-space` opens a menu bar across the top (Sessions, View, Pipes,
Orchestrator, Agenda, Help). It is the runtime settings surface: the
Orchestrator section cycles persona, provider, model, context budget, approval
mode and the wake-on-event toggles, and the Agenda section opens the planning
backend/model picker, all without editing config.toml.

| Key | Action |
|-----|--------|
| `←` / `→` | Move between sections |
| `↓` | Drop into the section, then move down its items |
| `↑` | Move up; from the first item, back out to the section row |
| `Enter` | Activate the item. Value rows (model, context, toggles) cycle in place and leave the menu open |
| a letter | Jump to the section whose title starts with it |
| `ctrl-space` / `esc` | Close |

Rows whose action is unavailable are greyed out and do nothing rather than
closing the menu — the orchestrator lifecycle rows are greyed while it is not
running, and "Planning Model" is greyed when there is no endpoint to pick from.

The planning backend list does not have to be written out twice: every
`[agents.NAME]` endpoint, and an API-class `[orchestrator]`, are offered as
planning backends automatically under their own names. A
`[planning.backends.NAME]` entry with the same name shadows the derived one,
so declaring a backend explicitly is how you give it a different model or
context budget than the agent it came from. Derived backends are never written
back into config.toml.

### Planning pane keys

`alt-p` docks the planning pane into a split leaf; `alt-shift-p` gives it the
whole output region, docking it first if it was closed. A plan is a document,
and a third of a split is not enough room to hold one in your head. The session
bar and status panel stay visible, so the sessions you are planning against are
still on screen with their state and token counts updating — only their output
is covered, and their PTYs keep the size they last laid out at. `esc` steps
back to the split, and a second `esc` moves focus on.

While the pane is focused these keys apply instead of being passed to a PTY:

| Key | Action |
|-----|--------|
| `Enter` | Send the input as a planning turn |
| `alt-Enter` | Insert a newline (a planning message is usually a paragraph) |
| `Tab` | Move focus between the thread list and the transcript |
| `ctrl-b` | Collapse/expand the thread list |
| `alt-m` | Open the backend/model picker |
| `ctrl-k` | Commit the thread to a plan revision |
| `alt-i` | Hand the committed plan to a session as work |
| `↑` / `↓` | Thread list: change selection. Transcript: scroll |
| `PageUp` / `PageDown` | Scroll the transcript |
| `Enter` (list focus) | Open the selected thread |
| `n` (list focus) | New thread — browse for its scope root |
| `d` (list focus) | Delete the selected thread (confirms first) |
| `esc` | Leave fullscreen, else return focus to the other pane |

The backend picker is `alt-m` rather than `ctrl-m` because terminals encode
`ctrl-m` as carriage return, making it indistinguishable from `Enter`.

It has two levels. The first lists backends — endpoints. On opening, every
self-hosted endpoint is asked what it is currently serving (`GET /v1/models`,
2s timeout, cached), and `Enter` or `→` on one descends into that answer to
pick a model; `←` backs out, `r` re-probes. Hosted catalogues
(`api.openai.com`, Anthropic) are not asked — they list hundreds of models and
are not what changes under you — so `Enter` there selects the endpoint on its
configured model, as does an endpoint that did not respond. A model picked
this way applies to the next turn and is recorded on each message, so a thread
shows where it switched models.

`alt-i` hands the thread's latest committed plan to a session you pick. What
is sent is a path plus a staleness warning, not the conversation: an
implementation session may run sandboxed, and a read-only bind mount of one
file is simpler to arrange than replaying a thread. Staleness is recomputed at
handoff, not reused from commit time, so a plan grounded in files that have
since moved on says so. The brief queues if the target session is mid-turn.

The status row's context meter shows two numbers: `~868/131k (peak 5.4k)`. The
first is the thread transcript plus your draft — what the *next* turn starts
from. The peak is the largest request the last turn actually built, including
the file contents it read. They diverge sharply, because tool results are
consumed within a turn and never stored in the thread, so reading a codebase
moves the peak and barely touches the transcript. The peak is not restored when
a thread is reopened; it describes a turn, not a thread.

When a thread exceeds the selected backend's context budget the pane shows a
prompt offering `[c]` compact, `[b]` switch backend, or `[Esc]` dismiss. This
is deliberately not automatic: compacting silently would drop the early turns,
which in a planning thread are usually the premises everything else rests on.

## Command bar

Press `alt-c` to open. Available commands:
Expand Down
Loading
Loading