---
title: "Sessions and groups"
description: "Every agent is a real tmux session: its model, effort and profile, killing, reviving on the same conversation, the group tree, archiving, and self-naming."
canonical: "https://agent-manager.dev/docs/sessions/"
last-updated: "2026-10-23"
section: "Documentation"
source: "agent-manager"
---

# Sessions and groups

Every agent is a real tmux session with its own directory, command and environment. Quit the manager and they keep working; close the laptop lid and they are still there.

## Model, effort and profile

The `n` form has `model` and `effort` rows under `tool`, and a `profile` row above them for a tool that has profiles. Quick prompt mode has the same choices on `ctrl+l`, `ctrl+x` and `ctrl+y`, and `agent-manager spawn` takes `--model`, `--effort` and `--profile`. Every value comes from the CLI itself, asked through the interface it offers programs, so a model that ships next week shows up in the list without an Agent Manager release.

Left alone, each row keeps the CLI's own default, whatever you set up in that CLI. Each CLI remembers the model, effort and profile you last picked for it, and the next session on that CLI starts there. The model list filters as you type, your last picks come first, and only a listed model can be picked. The effort row offers the levels the chosen model takes. The choice is saved on the session, so restart (`R`), revive (`v`) and fork (`f`) all run on it.

| CLI | Asked through | Model | Effort | Profile |
| --- | --- | --- | --- | --- |
| Claude Code | the Agent SDK `initialize` request | yes | per model | · |
| Codex | `codex app-server` | yes | per model, with its default | not supported |
| Grok Build | ACP over `grok agent stdio` | yes | per model | · |
| Pi | `pi --mode rpc` | yes | per model | · |
| Oh My Pi | `omp --mode rpc` | yes | per model | · |
| Muse Code | `muse serve` | yes | per model | · |
| OpenCode | `opencode models` | yes | not supported | · |
| Gemini CLI | ACP over `gemini --acp` | yes | not supported | · |
| Hermes Agent | `hermes serve` | per profile, with the provider | typed | yes |
| Antigravity CLI | not asked yet | not supported | not supported | · |
| Command Code | nothing a program can ask | not supported | not supported | · |
| Cursor CLI | ACP over `cursor-agent acp` | yes | carried in the model id | · |

A row the CLI cannot fill reads "not supported". OpenCode and Gemini CLI take no effort when they launch. Cursor CLI carries effort inside the model id, so the form offers no separate effort control. Hermes says which models reason but not their levels, so its effort is typed, and only a model that reasons shows the row. Pi's list needs Pi 0.84.3 or later, and an older Pi opens the update dialog instead. When a CLI fails to answer for any other reason, the model row says why, the session launches on the CLI's defaults, and the next form asks again once half a minute has passed. Answers are kept for six hours under `catalogs` in the config directory, and an upgraded CLI is asked again at once.

## Own git worktree per session

Parallel agents on the same repo can each get a private checkout. The `n` form has a `worktree` field between directory and prompt, and quick prompt mode's `ctrl+t` does the same when you spawn into a group. Both start on the tool and the worktree choice you picked last time, for as long as the manager runs, and Settings sets what they start from before that.

The worktree lives at `<repo>-worktrees/<name>` next to the repo, on a new branch `am/<name>`. It starts from the branch the repo's work merges into: the default branch of an `upstream` remote when the repo has one, then origin's default branch, then a local `main` or `master`, and finally `HEAD`. A group can name the starting point itself with the `base` row of the `g` form or its edit card, and a group left on `auto` takes its parent's. While the form or quick prompt mode is set to spawn a worktree, the manager fetches that one branch from its remote, at most once a minute for the same directory, and the `fetch on spawn` row in Settings turns that off. A worktree that fails to create blocks the spawn with an error. A path that is not a git repo cannot hold one, so the field reads unavailable and the session launches there as a plain session.

A branch that starts from `upstream` keeps tracking that remote, so `git pull` brings in the parent's changes and `git push` sends the branch to `origin`, your fork. A `remote.pushDefault` you already set is what `git push` uses.

Renaming a session renames its branch to `am/<new name>` and keeps the worktree directory at the path it was made at, so the running agent's working path stays valid. Deleting a session removes a clean worktree and its branch. Clean means no uncommitted changes and no commits that exist only in that worktree. A branch already in the base counts as merged, including after a squash, on git 2.38 or later. Commits that are already pushed leave the worktree free to go, and the local branch stays so those commits remain reachable. A dirty worktree stays on disk and the status line shows its path. Clean follows `git status`, so a gitignored file such as a `.env` is removed with the directory. Killing, archiving and reviving never touch the worktree.

## Terminal tabs

`T` opens a shell tab: a session like any other, with your shell in the pane instead of an agent. On an agent, the new shell nests under that session, in that agent's group and directory. On a group, it lands in the group as an un-nested sibling. A nested shell is named after the session it hangs under, `terminal-review-done` rather than a generated id. Shells live in the tree with the agents they belong to, marked with `❯` where an agent carries its status dot. `m` on a terminal moves it onto an agent or onto a group.

The keys that write into a pane refuse a shell. `space` and the review screen's `C` both paste their text and press Enter, so on a shell a sentence meant for an agent would run as a command. Enter the session to type there.

## Killing without losing the row

`x` ends a session that is holding RAM you want back, and on a group row it ends every live session under it. `X` ends every live session in view. Each asks to confirm first, and what it ends is the tmux session, not the record: the row stays in the tree, marked `dead`, with its name, group and conversation id intact.

A group of agents is done for the night. `x` on the group frees the RAM and leaves the rows. `v` on the group in the morning brings those same conversations back.

## Reviving the same conversation

`v` relaunches a dead session under its old id, keeping its name, group and history. When the manager holds that session's own conversation id, revive resumes **that exact conversation** through the tool's `resume_by_id_command`. The row follows a conversation the agent switches to with `/clear`, `/new`, `/resume` or `/fork`, so `v` reopens that one rather than the conversation the session started on. Only the agent the launch started moves the row. A CLI it runs as a tool, or one typed into the pane by hand, leaves the row where it is. Cursor CLI stays on the chat captured at launch. The commands are:

`resume_by_id_command`

```
claude --resume {id}
codex resume {id}
opencode --session {id}
grok --resume {id}
gemini --resume {id}
pi --session {id}
cmd --session {id}
hermes --cli --resume {id}
muse resume {id}
agy --conversation {id}
omp --resume {id}
cursor-agent --resume {id}
```

The id arrives one of two ways:

| Field | How the id is obtained |
| --- | --- |
| session_id_flag | The tool launches under an id the manager mints. |
| session_store | The tool mints its own, and a capturer reads it back (`codex`, `opencode`, `gemini`, `hermes`, `command-code`, `muse`, `antigravity`, `omp`, `cursor`). |

> **Without an id** Revive falls back to `revive_command` (`claude --continue`), which resumes the directory's most recent conversation, and the manager says so in the status line, since sessions sharing a directory would otherwise land on the wrong one.

On a group row `v` revives everything dead under it, and `V` revives everything dead in view. Each asks to confirm first when that is more than one session. Both revive what they can, carry on past a failure, and name the first one.

## Forking a conversation

The session under the cursor tried one approach. `f` names a second session and continues from the same history, in the same group and directory, so the other approach runs beside it. It runs the tool's `fork_command`, which references the source through `{id}` or `{session_file}`, so a fork needs both that command and a captured conversation id. A session without one, or a shell, says so instead of forking. Claude Code, OpenCode, Codex, Grok Build, Gemini CLI, Pi, Command Code and Muse Code ship fork commands. Hermes Agent, Antigravity CLI, Oh My Pi and Cursor CLI have no fork. Muse forks only from inside the running source, so `f` types `/fork` into that session and opens the fork in a new pane. The source has to be at rest with nothing typed at its prompt.

## Restarting on an empty context

`R` keeps the row and drops the context: same name, group, tool and directory, launched on a conversation the agent has never seen. It is what a session that has piled up context you are done with wants, where reviving would spend the budget re-reading history. It asks to confirm first and works on a live session too, ending the running agent before the fresh one starts. The old conversation is retired rather than resumed, left on disk while the row stops pointing at it, and the row only changes hands once the new agent is up, so a launch that cannot start leaves the session on the conversation it had.

## Full screen

The **sessions layout** row in Settings (`s`) hands the session list the whole terminal: `←` or `→` swaps it to full screen, the preview column steps aside, and every row takes the full width. The machine meters condense to one line above the key legend, and `→` on a row focuses that session across the whole body. While a message is unread, the View footer paints `M unread` as a filled chip, and a click on it opens them. The same keys return the layout to split, and the choice persists like the split ratio.

![Animated demo: Settings swaps the sessions layout to full screen, the list takes the whole terminal with the computer stats on its foot line, and a session is focused full screen then left.](/assets/demo-fullscreen.gif)

_Fig. 1sessions layout · full screen_

The **list density** row picks the rows' rhythm in either layout. Compact keeps a session to one line with the agent's last message riding beside its name. Comfortable unfolds each session to three lines: the name, your last message behind a `❯`, and the agent's last message under it.

![Animated demo: the same full-screen list at the comfortable density, every session on three lines with its task and the agent's last message under the name.](/assets/demo-fullscreen-comfortable.gif)

_Fig. 2full screen · comfortable density_

## Sessions on another machine

`C` adds an SSH connection: a name, a destination (`user@host` or an alias from `~/.ssh/config`), and optionally where agent-manager lives there. The other machine runs Linux or macOS with agent-manager on its login shell's `PATH`, and this machine's `ssh` needs a key it can use without a prompt. The manager never asks for a password and never touches your SSH config. Connect once by hand first (`ssh user@host`), so the host key is already known. A host it has never seen shows as `unreachable`.

The connection sits in the list after your own groups, in its own colour with an `ssh` label, and the other machine's groups, agents and terminals nest under it. It folds like a group. Each poll refreshes it in the background. A host that fails keeps its last rows with a note saying why (`unreachable`, `agent-manager missing`, `agent-manager too old`, `agent-manager newer` or `error`) and is tried again after a pause that grows to half a minute. The first refresh starts a headless manager there, `agent-manager serve --background`, so messages to its agents get delivered.

| Key | On a connection's rows |
| --- | --- |
| enter / A | Attach over SSH in this terminal. Detaching returns to the manager. A remote pane is never embedded. |
| space | Type into a remote agent's pane as you, or start a new agent there on the CLI, model, effort and profile the bar shows. |
| n | Start an agent there. The directory and worktree default are the other machine's unless you set them. |
| T / g | Open a terminal there, or create a group there. |
| x / v / a / u | Kill, revive, archive and restore. |
| r / d | On the connection row: edit it, or remove it. Removing asks first, and its sessions keep running on that host. |

Rename, move, delete, restart, fork, review, the editor, copying a reply, reordering, marking idle, cancelling an after-turn end, and kill all or revive all are refused on a remote row with a message naming the gap. Nothing from a connection is written to this machine's store. Agents reach the connection too, through [MCP](/docs/mcp/#ssh), so add one only for a host you would let all of them use.

## Groups are a tree

Groups are paths like `backend/api/auth`, forming a tree of unlimited depth, and sessions live at any node including the root.

| Key | Action |
| --- | --- |
| g | Create a subgroup inline. |
| enter | Fold a subtree. `F` folds or unfolds everything. |
| e | Hide or restore empty groups. |
| r | Edit a group's name, default path and worktree base. |
| K / J | Reorder sessions among their siblings, or whole groups among theirs. The order persists. |
| m | Move a session to a group, a terminal into a session, or a group under another group. |
| a / u | Archive / restore a session or group. Archive kills the process and keeps the last preview; restore resumes it. Restoring un-archives whichever ancestors it needs. |
| t | Toggle the archived view. |

## Sessions name themselves

A session spawned without a name gets a placeholder like `claude-a1b2`, and its first prompt opens by asking the agent to run `agent-manager rename` once, with a short name for the broad feature it is working on rather than a single subtask. The directive also tells the agent not to rename again unless you ask.

When the first prompt cannot carry the directive, because it is a `/slash` command or there is no prompt at all, the manager sends it as its own message once the tool's input box appears. The subcommand drops the name into a per-session file, and the manager picks it up on the next poll.

> **Names you set are kept** A session you named yourself is never renamed by its agent. The first prompt only notes that `agent-manager rename` is available later if you ask for it.
