# Agent Sessions, Service, and Control Center

> A `remits-cli` skill reference. **Load this when** you are registering this session as an agent, diagnosing why a routed ticket never started, or a question involves the background service, the control center, or multiple authenticated sessions.
>
> The table of contents below carries **real line numbers** (`- L84  Some Heading`), resolved when
> this file is installed, so they are never stale. Read the head, pick your sections, and offset-read
> only those. The entry text is the heading verbatim, so it also greps.

## Table of Contents

- [Registering This Session As An Agent](#registering-this-session-as-an-agent)
  - [What registration actually does](#what-registration-actually-does)
  - [What `serve` adds](#what-serve-adds)
  - [Resolving which agent a command means](#resolving-which-agent-a-command-means)
  - [Which agent gets a ticket](#which-agent-gets-a-ticket)
  - [Why a routed ticket might not have started](#why-a-routed-ticket-might-not-have-started)
- [Background Service and Control Center](#background-service-and-control-center)
  - [Control Center](#control-center)
  - [Configuring the Preferred Agent](#configuring-the-preferred-agent)
- [Multi-Session Support](#multi-session-support)

## Registering This Session As An Agent

`remits-cli agent` is how a terminal session makes itself available to work support tickets. It is
independent of everything else — no background service is required, and every tab is its own agent.

```bash
remits-cli agent serve               # routable AND autonomously working tickets — the usual choice
remits-cli agent workers             # what this session is running right now
remits-cli agent list                # who else is available across the accounts you cover
remits-cli agent release             # stop serving and stop receiving tickets right now

remits-cli agent register            # presence ONLY — nothing starts on its own
remits-cli agent work --wait 300     # ask for tickets routed here (manual loop)
remits-cli agent status --state working --ticket 22454 --activity "reproducing"
```

`serve` is `register` plus a supervisor. Everything below about registration applies to both.

**Defaults exist so the user does not have to type them.** With no flags, `register` targets the
production platform (`REMITS_BASE_URL` when set) and the **prod** lane, and claims every account repo
indexed on this machine. That is a deliberate exception to the CLI's test-first default: registering
presence mutates no business data, and a test-lane agent is silently useless for support — it appears
online and can never be routed a production ticket. Every command after `register` reuses the
platform, lane and identity it registered with.

### What registration actually does

- Mints an `agentId` for this session and tells the platform which account repos this machine has
  checked out — read from the machine-wide index (`~/.remits-cli/account-repos.json`), which is why
  the working directory does not matter. The platform **verifies** those claims against what your user
  can access and returns the ones it refused, so "I registered but never get tickets for account 52"
  is answerable from the registration output alone.
- Starts a small detached heartbeat process **anchored to the agent process that owns this terminal**
  (it walks up the process tree to find `claude`/`codex`/`gemini`, then an interactive shell). When
  that process ends, the heartbeat ends and the session stops being routable within a couple of
  minutes. Closing the tab is a valid way to go offline; `agent release` just makes it immediate.
- Registers in the session's **data lane**. A `test`-lane run only ever reaches a `test`-lane agent,
  which is what keeps fixture traffic away from a production terminal.
- Declares **capacity** — how many tickets this session can genuinely run at once (`--max-concurrent`,
  default 1). The router will not exceed it.

### What `serve` adds

The same detached process that maintains presence also polls for work. On each poll it publishes the
tickets it currently has running, renews their claims, and takes at most enough new ones to fill its
capacity. For each new ticket it launches a headless worker:

| | headless invocation | edit mode | investigate mode |
|---|---|---|---|
| `codex` | `codex exec --cd <repo>` | `--sandbox workspace-write` | `workspace-write with investigation-only brief and no edit lease` |
| `claude` | `claude -p --add-dir <repo>` | `--permission-mode acceptEdits` | `--permission-mode plan` |
| `gemini` | `gemini -p` in `<repo>` | `--approval-mode auto_edit` | `--approval-mode plan` |

**Which of the three it launches:** `--worker-agent` if you pass it, otherwise **whatever agent this
session is** (detected from the process the agent anchored to — which finds nothing in a plain tab),
otherwise `~/.remits-cli/config.json`'s `agent` (set it with `remits-cli config set --agent NAME`),
otherwise `claude`. **It prints which it chose and why on startup**, because in the intended
plain-tab setup the choice comes from a config file you may have set months ago. So running `agent serve` inside a Codex tab gives you Codex workers
without a flag, and the label the operator sees (`codex@repo`) is resolved the same way — the label
and the worker are one answer, not two.

The brief always arrives on **stdin**, never in argv — it is a page of prose and argv has a hard
length limit, so an argv brief would fail on exactly the detailed tickets that most need the detail.
The brief itself is generated by the platform, so all three worker types are told the same thing.
In `--mode investigate`, the supervisor still claims tickets and may update them with findings, but it
does **not** acquire or renew the repository edit lease. The brief says the run was launched for
investigation only and forbids file edits, component staging, commits, and manual lease escalation.

**Following a run.** `remits-cli start`'s control center lists every worker; **Follow live** streams
that run's transcript into the page, rendering commands with their exit codes, file edits, and the
agent's own messages as distinct things. It keeps working after the worker exits — a finished run is
usually the one worth reading. For a terminal instead, each worker prints a `tail -f` for its log in
`~/.remits-cli/workers/`.

**There is no terminal to attach to, by design.** A worker is spawned with pipes and runs
non-interactively (`codex exec`, `claude -p`), so there is no tty and no prompt to type at — the
whole point is that it needs no supervision. Following the transcript is the way to watch, and it is
strictly better than a terminal would be: the output is structured, so it can be read as events
rather than scraped back out of ANSI text.

**No worker ever commits.** The brief forbids `git commit`/`git push` and nothing in the supervisor
runs git — a worker leaves its changes in the working tree and says so on the ticket, for a human to
review.

### Resolving which agent a command means

Order: `--agent-id`, then `REMITS_AGENT_ID`, then the single agent registered for this working
directory. With several registered and no way to tell them apart, the command **fails and lists the
candidates** rather than guessing — routing work to the wrong tab is silent, and a message is not.

### Which agent gets a ticket

The platform walks a ladder — the ticket's implementation account first (a defect seen on a client is
usually fixed in the platform repo above it), then the ticket's own account, then its
`PLATFORM`/`PRODUCT` ancestors — and takes the first account with an available agent. Among those it
prefers an **idle** agent, then the one that has waited longest, so work spreads across your tabs
instead of piling onto whichever one most recently ran a command. A `paused` agent stays visible and
is never routed to, and so is one **at capacity** — those are different facts and stay separate:
`paused` means a human stopped this session, at-capacity means its workers are all busy.

### Why a routed ticket might not have started

In order of likelihood:

1. **The session registered but never served.** `agent register` starts nothing. `agent workers`
   showing none while a ticket is routed here is this.
2. **At capacity.** Check `agent workers`; the ticket starts when one finishes.
3. **Another session is running it.** A claim held by a different agent blocks it until that claim is
   dropped or expires.
4. **No local checkout.** The worker still starts, and its brief tells it to locate the repo rather
   than guess — but if the account genuinely is not on this machine it will say so and stop.
5. **It failed twice already** and was released back to the queue. The transcripts in
   `~/.remits-cli/workers/` say why.

## Background Service and Control Center

`remits-cli start` runs an optional background service for the **human** watching:

- Maintains persistent WebSocket connections to the Remits platform
- Hosts a localhost browser **control center** (typically `http://127.0.0.1:8787/`)

```bash
remits-cli start
remits-cli start --foreground true
remits-cli status
remits-cli whoami
remits-cli stop
```

Compatibility aliases still exist:

```bash
remits-cli listen
remits-cli listen status
remits-cli listen stop
```

**The service is not part of ticket delivery.** Agents register and collect work on their own, so the
dashboard being down never stops a ticket reaching an agent. What the service adds is visibility: it
scans for `account-info.json` files to rebuild the repo index, keeps one WebSocket per platform URL,
and holds a PID lock (`~/.remits-cli/listener.pid`) so only one runs per machine.

### Control Center

The control center shows, in one browser view:
- which agent sessions are registered, what each is doing right now, and its recent activity
- live and recent worker runs, with historical transcript follow/stop controls keyed to the exact run
- the open support-ticket queue, and which agent each ticket is routed to
- a control to route a ticket at a specific available agent
- websocket connection and topic health
- the discovered account repo index and the important global/per-repo files

**What it shows is what the agents see.** The ticket queue comes from the platform's own generic
endpoint, not from any product's tool, so the control center reads the same on every Remits platform
and never implies a workflow that a particular product does not have. Product-specific views belong
in that product's Embeddables.

When a user asks a broad or vague question about remits-cli behavior, prefer reasoning from the
control center state before spelunking individual files.

### Configuring the Preferred Agent

```bash
remits-cli config                    # Show current config
remits-cli config set --agent claude # Use Claude Code (default)
remits-cli config set --agent codex  # Use OpenAI Codex CLI
remits-cli config set --agent gemini # Use Gemini CLI
```

The agent preference is stored in `~/.remits-cli/config.json` and applies globally.

## Multi-Session Support

The CLI supports multiple authenticated sessions simultaneously. Sessions are stored in `~/.remits-cli/sessions.json` as a map keyed by `accountId + dataMode + baseUrl`, so the same account can stay authenticated against both localhost and production without one session overwriting the other. When you run any command from an account repo, the CLI automatically resolves the best matching session based on the `account-info.json` in that directory and any `--base-url` or `--data-mode` flags provided. If the same account is authenticated against multiple hosts and you omit `--base-url`, the CLI may legitimately choose either localhost or a deployed host depending on the best session match, so agents should treat `--base-url` as mandatory whenever host matters.

```bash
# Authenticate for an account (run from its repo, or pass --account-id)
remits-cli auth
remits-cli auth --account-id 42
remits-cli auth --account-id 42 --base-url http://localhost:8080

# List all active sessions
remits-cli sessions list

# Remove a session (removes all sessions for the account, or narrow with --base-url / --data-mode)
remits-cli sessions remove --account-id 42
remits-cli sessions remove --account-id 42 --base-url http://localhost:8080
```

You can work in multiple account repos simultaneously across different terminal windows — each uses its own session. You can also be authenticated against different base URLs (e.g., localhost for development and production) for the same account at the same time.

Use `remits-cli whoami` when you need a compact proof of the active target before a sensitive operation.
It prints the resolved Account ID, User ID, current git branch, data mode, and base URL. `remits-cli status`
prints the same session tuple after the service/dashboard status. Pass `--base-url`, `--account-id`, and
`--data-mode` when host or lane matters; do not infer those values from the repo directory or account name.

**The reported data mode describes the next `tool` / `tools` / `token` call, not `test run`.** It falls back
to the stored session lane, whereas `remits-cli test run` deliberately ignores that and defaults to `test`
unless you pass `--data-mode prod` explicitly. So `whoami` can read `prod` while a test run goes to the test
lane — which is the safe direction, but not the one you would predict from the output alone. `whoami` says
so in its own output; when a test run genuinely needs prod data, pass the flag.
