---
title: Stop, restart, pause, archive, message, or refill many sessions at once
---

# Stop, restart, pause, archive, message, or refill many sessions at once

## What it is

A **Manage sessions** picker for when you have lots of sessions and want to act on a hand-picked set — not all-or-nothing. One menu item opens a modal with **seven** views you toggle between:

- **Stop view** lists only the sessions that are **actively running** (`running` / `starting`) — the busy ones with a live process. It deliberately does **not** list sessions waiting on you (Needs You), paused, errored, stalled, or parked on a usage limit: those aren't "running", and a conversation you're mid-way through should never sit in a "stop these" list. Tick the ones you want and Apply ends just those. Each is left as **ended**, so it stays in your list — you can bring it back later from the **Restart view** here (or by sending it a message). Stop does not archive or delete anything, and there's **no extra confirm** — opening the modal, ticking sessions, and pressing a button that names the count ("Stop 12") is the deliberate step. The Stop list is a plain, flat list (there's only one kind of session in it — running ones). **A Stop sticks:** nothing automatic brings a stopped session back — not the auto-restarter ([Session Refill Governor](session-refill-governor.md)), another agent, or the branch lander — until you restart or message it. A big batch answers at once and shuts down in the background.
- **Restart view** lists every **broken or stuck** session — anything in **error** (the red dot), **stalled** (the orange "no output for a while" state), that **gave up recovering** (the ember "recovery-failed" dot), or that was **interrupted when the app last closed** — **plus every grey "ended" session you stopped (or that finished on its own) that still has a conversation to pick up** — **plus every session you paused**. Because that list now holds two different kinds of session, a small **Interrupted / Paused / Both** switch sits under the filter box: **Interrupted** is what it opens on (exactly the list you had before paused sessions were included), **Paused** shows just the parked ones, and **Both** shows them together. Whichever you pick scopes everything else on the screen — **Select all**, the "picked of available" count, the first/last quick-picker and the **Restart N** button only ever touch the rows you can see. If the option you picked happens to be empty, the box says which one ("No paused sessions to restart") instead of blaming your search. Tick the ones you want and Apply relaunches just those — **top to bottom, in the order they're listed** — each in place with `--resume` so it picks up exactly where it left off. (The list isn't hand-reorderable: it's a picker, and every row is kept to a checkbox, a status dot, and the session's **name**, so the name is actually readable on a phone.) When you restart **several at once**, Omniscio **paces the relaunches** (by default _at most_ roughly 1.5 seconds apart — adjustable in **Settings → Performance → "Spacing between sessions when many restart at once"**, or set to 0 to turn the gap off) so a big batch doesn't boot every agent in the same instant and bog the machine down — they trickle back rather than storming it all at once. That setting is a **ceiling, not a fixed wait**: the pace **starts at the full gap while the app is still opening, then accelerates on its own** — down to a quarter of it — once startup work is finished and your computer measures calm, so most of the batch comes back sooner than the flat spacing would allow. If the machine gets busy again the gap widens straight back out. **That short gap is the only thing that holds a batch you asked for** — it is deliberately never made to wait for your computer to "calm down" or for a capacity limit first, so restarting twenty sessions is twenty starts a moment apart, not a long sit. The gap is counted **only against your own batch** — it never puts your batch in line behind restarts Omniscio or your agents are doing in the background. And if a session in your batch comes back on its own before its turn (another restart got to it first), the batch **leaves it running** instead of restarting it a second time, and counts it as restarted. (Omniscio's own automatic recoveries still ease off while the machine is under load, because nobody is sitting there waiting on those.) A **single** restart still fires immediately.
- **Pause view** lists every session that is **alive and steady** — running, starting, ready, waiting on a usage limit, stalled, _and_ the ones waiting on you in Needs You. Pausing parks a session where it is instead of ending it, and you can unpause straight back into the same conversation — so unlike Stop it is safe to offer on a conversation you're mid-way through. Tick the ones you want, press Apply, and they all park. **Ctrl+Z undoes it** (or click **Undo** on the toast), which unpauses exactly the ones you just paused.
- **Archive view** lists **every session that isn't already archived** — deliberately the widest list of the four, matching what right-click → Archive already offers on any selection. Archiving is the everyday "file it away" gesture, not a process action: it takes the session out of your sidebar, and if the session was still running Omniscio stops it first. **Ctrl+Z undoes it** (or click **Undo** on the toast) and each session comes back to the exact state it was in — a session that was waiting on you returns to Needs You, not as a plain idle row. You can also restore anything later from the Archived view.

- **Message view** is the broadcast: **one message, many agents**. Type it once in the box at the top, tick who should get it, and Apply reads **Send to 12**. Each recipient gets it as a real turn in its own conversation and carries on from there. Because the toolbar opens the modal across **every** project and a project's ⋮ menu opens it scoped to **that repo**, "tell every agent in this repo X" and "tell every agent everywhere X" are the same feature opened from two places. This view lists a much **wider** set than the others on purpose — running, starting, waiting-on-you, stalled, paused, stopped, and errored sessions are all reachable, because the whole point is to reach an agent wherever it's parked, and Omniscio already knows how to wake each of those. Three kinds are deliberately left out: a session **parked on a usage limit** (it's quietly waiting its turn to resume, and poking it fights that), a **brand-new blank session** that isn't doing anything yet, and one that's **already shutting down**. Background "silent" automation lanes are never included either — **not even running ones** — because a broadcast landing mid-run would derail a script. And a session you explicitly **closed** is never reachable, same as everywhere else in the app. A recipient that is **mid-turn is interrupted** so your message goes in now — the same thing the Send button in a session does — and the modal says so in a line under the list. The one exception is a session on an **outside engine** (Codex, Gemini, Cursor, Grok and friends): it can't be interrupted that way, so it waits for its turn to finish, and the same line names that too.
  - **Archived sessions are opt-in.** Tick **Include archived sessions** (off every time you open the modal) and your archived sessions join the list, each marked "Archived", for you to pick individually. Messaging one **reopens it and starts its agent back up** — that costs tokens — so it's deliberately something you switch on and choose, never something **Select all** can sweep up by accident. Once you've ticked some, a line under the list tells you how many will be reopened this way.
  - **Narrow the list by status.** A dropdown under the title filter — the same one the **Archive** view has, in the same place — shows one status at a time: Running, Needs You, Error, Stalled?, Starting, Interrupted or Paused, plus **Archived** while **Include archived sessions** is ticked. It starts on **All statuses**, so nothing changes until you pick. Once you pick, **Select all**, the count, the first / last picker and **Send** act only on the sessions it is showing — so "message every session waiting on me" is: pick **Needs You**, **Select all**, type, send. It goes back to **All statuses** when you switch to another view, and if you untick **Include archived sessions** while **Archived** is picked. When a status has nobody in it, the list says "No sessions with this status to message".

- **Refill view** is the **auto-restarter's queue**: the interrupted sessions Omniscio would bring back on its own to reach your target, in the order it would pick them, with each row saying **why** it qualifies in plain English — "Process stopped mid-work", "Automatic recovery gave up", "Interrupted when the app closed". A strip along the top shows how many sessions are running against your target, how many automatic restarts are left today, whether it is limited to chosen projects, and one line saying what it will actually do next. **A long list with nothing happening is normal and meaningful** — when you already have more sessions running than your target, it holds back, and the strip says so rather than hiding the list. When the list _is_ empty it tells you which kind of empty: nothing broken, versus plenty broken but nothing eligible yet (usually cooldowns, or sessions that have had their one restart today). Tick any of them and **Restart now N** brings them back immediately, through the same paced path the Restart tab uses. This works even with the auto-restarter switched off — then it is simply a list of what it _would_ restart. Not undoable, like every restart. See [session-refill-governor.md](session-refill-governor.md).
- **Queued view** is **what is restarting right now** — the sessions actually waiting in line to come back, in the order they will, so a restart wave is finally something you can look at instead of just wait out. It merges the two queues that exist: the ones Omniscio is bringing back **after you restarted the app** (each row says _"Was running when the app closed"_), and the ones **you queued yourself** when you ticked a batch in the Restart view (_"You queued this restart"_). The list **shrinks live** as each session comes back, so it always shows what is genuinely still waiting, and a strip at the top says how many that is and reminds you they come back one at a time. Tick any of them and **Cancel restart N** takes just those out of the line — **the rest keep coming back**, which is the whole difference from the **Stop** button on the "Resuming N sessions" toast, which halts the entire wave and then vanishes. A cancelled session is simply left **stopped**: nothing is lost, nothing nags you about it, and you can restart it any time from the Restart view. Cancelling is not undoable (bringing one back is a paid restart), and a session that already started up in the moment between opening the list and pressing the button is reported as _"already restarting, so there was nothing to cancel"_ rather than silently counted as cancelled. Below the restarts, under **Waiting to start**, it also lists the **new sessions agents and automations have asked for** that are still waiting their turn to launch — exactly the requests the _"Session-spawn queue is backing up"_ inbox alert counts, so the two always agree. Each row shows the requested name, the project, how long it has waited and which session asked for it (_"Waiting 31m · asked for by …"_), or _"Starting now"_ once it is launching; one started by a scheduled job shows only how long it has waited. These rows are **read-only** — no tick box, and Cancel restart never touches them — because cancelling one would leave the session that asked for it waiting on a session that never comes. The strip at the top counts them on their own line (_"27 new sessions are waiting to start, one at a time."_), and when you opened the modal from one project it also says how many more are waiting in other projects. The whole view **refreshes itself every 10 seconds** while the modal is open, so a request drops off as it launches and a new one appears. Empty means one of three different things and the view says which: nothing is waiting to start or restart at all, the last few are already starting up, or nothing is queued _in this project_ while N are queued elsewhere (restarts and new sessions both count). See [crash-recovery.md](crash-recovery.md).

You pick with a checkbox on each row plus a **Select all** at the top — its label shows how many you've ticked out of how many you _can_ pick in this view (e.g. **Select all (2/5)**), so the count of available sessions is visible at a glance — and the modal **opens with nothing selected** (so it starts at **(0/5)**), always a clean slate. Next to "Select all" is a **Select first/last** quick-picker — a small **first / last** dropdown next to a number box: type a number and it ticks that many sessions from the **top** (first) or the **bottom** (last) of the list in one go — the quick way to grab, say, the first 20 or the last 20 when you've got dozens running, without clicking each one. Flip the dropdown with a number already typed and it re-picks from the other end. It just drives the same checkboxes, so you still see exactly what's ticked and can fine-tune before pressing Apply, and **nothing happens until you do**. Typing more than there are selects them all; **0** clears the selection; an **empty box leaves your current picks untouched** (so clearing it to retype never wipes what you had); and switching between the Stop and Restart tabs resets both the box and the dropdown back to "first". "First" and "last" always mean the top and bottom of the list as shown. A single **Apply** button names exactly what it will do — **Stop 3**, **Restart 5**, **Pause 8**, **Archive 12** — and is greyed out until you've picked something. **No view shows a second confirm** — opening the modal, picking your sessions, and pressing a button that names the count is the deliberate step (and Stop only ever ends a running session, never a conversation you're mid-way through). Two views add one quiet line under the list where the count alone doesn't tell the whole story: **Stop** warns it can't be undone, and **Archive** notes it stops a running session first and that archived sessions can be restored. (Stop and Restart aren't undoable with Ctrl+Z — the only way to reverse them is to spawn the agent again, which costs tokens — so the modal itself is the guard. **Pause and Archive are** undoable, because putting them back is free.)

**Each tab remembers its own ticks.** Picking sessions on Stop and then flipping to Archive gives you a clean, empty list — your Stop picks aren't dragged along, and they're still there when you flip back. Apply only ever acts on the tab you're looking at.

**Can't find a session in a long list? Filter by title.** At the top of the list is a **search box** — type any part of a session's title and the list narrows to just the matching sessions as you type (it ignores case and accents, so "pr" finds "PR #1498"). The filter **scopes everything else to what's showing**: **Select all**, the count, the **first / last** picker, and the **Apply** button all act on the matching rows only — so the button's count stays honest and you never stop or restart a session you can't see (a session you'd already ticked before filtering is remembered, just not acted on while it's hidden). Clear the box (or its ✕) to bring the whole list back, and switching between the Stop and Restart tabs clears it too. If nothing matches, you get a short **"No sessions match your filter"** line. Typing here never runs Apply — plain **Enter** in the filter box just leaves the filter as-is (the list already updated live), while **Ctrl+Enter** still applies.

The key safety rule for the two _ending_ views is the same: neither ever acts on a _live_ "Needs You" session — one waiting on _your_ input (a question, an approval, a transient API-error or rate-limit pause) — so a conversation you're mid-way through is never _stopped_ or _relaunched_ out from under you. **Stop** lists only running/starting sessions, full stop. **Restart**'s two "Needs You" exceptions are sessions that **gave up recovering** or were **interrupted on close** — broken/parked, exactly what Restart is for — plus grey **"ended"** sessions with a conversation to resume (blank never-used sessions and already-closing ones are left alone). Both the modal's Stop and the quick **right-click Stop** are narrow — running/starting only — so neither can ever end a conversation you're answering. **Pause and Archive are deliberately wider**, because neither destroys the conversation: pause parks it and unpause resumes it, and archive is reversible from the Archived view — the same reason right-click → Pause / Archive already offer them on a Needs You row.

## Where to find it

The modal opens from two places, and they differ only in scope. A Manage sessions item lives in
the toolbar's More menu, acting on every project; right-click it to pin it to the toolbar. The
same item also sits at the bottom of a project's own three-dot menu, where it acts on that project
alone. For a handful of sessions you need not open the modal at all — select rows in the sidebar
and right-click to Stop or Restart them.

## How it behaves

### How to use it

There are two places to open the modal, and they differ only in **scope**:

1. **The toolbar item acts on ALL projects.** A **Manage sessions** button lives in the toolbar's overflow "More" (⋯) menu by default; right-click → _Pin to toolbar_ to keep it on the bar. Clicking it opens the modal listing qualifying sessions from **every** project.
2. **The project's three-dot (⋮) menu acts on the project you're viewing.** Open the ⋮ menu next to the project's name at the top of its session list — a **Manage sessions** item **always** sits at the **bottom** of the menu, below a divider that sets it apart from the everyday **Search conversations** / **Hide sessions sidebar** items. When that project has nothing to act on at all the item is **greyed-out** (hover it and a tooltip explains "No sessions to manage"); the moment it has any session that isn't already archived the item lights up and opens the same modal, scoped to just that project, so you can clear out one project's runaway or stuck sessions without touching anything elsewhere.
   2b. **Sending a broadcast.** Open the modal either way, switch to **Message**, type your message in the box at the top, tick the recipients, and press **Send to N**. Inside that box plain **Enter** starts a new line — so a multi-line message works normally — and **Ctrl+Enter** sends. That's the one place in this modal where Enter doesn't apply, because sending on every newline would fire a paid message per recipient by accident. Send stays greyed out until you've both written something and picked someone. Messages go out **one recipient at a time** so waking a batch never storms your machine, and one failure never stops the rest — the toast reports honestly, e.g. "Sent to 11 of 12 sessions; 1 couldn't be reached." Each agent sees the message clearly labelled as coming from **you**, with a short note explaining it's a broadcast — so an agent sitting on a question doesn't mistake your message for the answer to it. There's no undo: once a message is delivered the agent is already working on it.
3. **Inside the modal.** Toggle between **Stop**, **Restart**, **Pause**, **Archive**, **Message**, **Refill**, and **Queued** at the top. (**Refill** drives the in-development Session Refill Governor — the tab is always there, and the governor runs only once you switch it on there or in **Settings → Lab**; see [Session Refill Governor](session-refill-governor.md). A **Pull requests** tab also appears, but only for developers.) **On a phone it is two steps instead.** You get the list of actions first — each with a line saying what it does — you tap one, and it opens on its own screen with a back chevron in the header to return to the list. Your ticks are kept per action, so stepping out and back in never clears them. This exists because the tab strip plus the filter and selection controls had grown to about **71% of a phone screen**, leaving room for two or three sessions out of dozens; splitting it gives most of the screen back to the list. On a phone the filter box and the selection controls also share **one** row: a **Select** button (showing e.g. `3/74`) opens a small menu holding the same **Select all** checkbox and **first / last** + number picker that sit inline on desktop — nothing was removed, it just moved. Desktop is unchanged. Tick the sessions you want — one at a time, **Select all**, or use the **first / last** dropdown + number box to grab the first N from the top or the last N from the bottom. Press **Apply** (it names the count) — or just press **Enter** and it runs the same Apply from wherever you are in the modal (the number box, a ticked checkbox, a row); Enter does nothing when nothing's picked, Enter inside the **first / last** dropdown just picks an option, and **Ctrl+Enter** works too. If a view has nothing to act on, it shows a short message naming that view's own bucket — "No running sessions to stop", "No stopped or stuck sessions to restart", "No running sessions to pause", "No sessions to archive" — and Apply stays disabled. The **Restart** view has three such messages, because its list is also narrowed by the Interrupted / Paused / Both switch: whichever half you picked is the half it names ("No paused sessions to restart"), so an empty option never looks like a failed search. The footer also has a **"Resume sessions when Omniscio restarts"** switch — the persistent off-switch for auto-resume-on-restart, put here (next to where you stop sessions) so you don't have to hunt through Settings; flip it off and Omniscio stops bringing sessions back on every restart until you turn it back on. (Same setting as Settings → Workflow → "Auto-Resume Sessions on Restart". For halting a resume that's happening _right now_, use the **Stop** button on the "Resuming N sessions" toast instead — see [crash recovery](crash-recovery.md#stopping-the-resume-the-stop-button-and-the-auto-resume-switch).)
4. **What you'll see after.** The moment you press Apply, **the modal closes and the work happens in the background** — you can keep working while a big batch is still relaunching. Stopped sessions turn grey ("ended") and stay in the list — send any message to start one again. Restarted sessions flip to "Starting" and resume on their own, top-to-bottom in the order they were listed. A **Restart** batch also confirms the moment you press Apply — a quick "Restarting 12 sessions…" — so you can see straight away that it took, and the rows go grey in the live list at that same instant rather than only once the work is done. When it finishes, a brief toast tells you the outcome — and if some couldn't be acted on (e.g. one was briefly busy, or finished while the modal was open), it says so honestly: "Stopped 3 of 4 sessions; 1 couldn't be stopped." A Restart that half-failed adds **why** — the reason the backend gave for those rows, e.g. "This session runs in the cloud and its cloud machine is gone — send it a message to start a new one." — shown once even when every refused row shares it. **A cloud session whose machine is gone is brought back in the cloud**, as sending it a message does — never on your own computer. For **Pause** and **Archive** that toast carries an **Undo** button (and Ctrl+Z does the same).

**A quicker path for just a few: the right-click menu.** You don't have to open the modal at all. Select one or more sessions in the sidebar (Ctrl/Shift+click for several) and **right-click → Stop** or **Restart** — they act immediately on the eligible sessions in your selection. Both the right-click **Stop** and the **modal's** Stop are **narrow** — running/starting only, so neither can end a conversation you're answering — and both Restarts cover the same broken, stuck and stopped-with-history sessions, never a live "Needs You". There is one deliberate difference: the **modal's** Restart list also includes the sessions you **paused**, because that screen is the one place whose whole point is putting a session back to work. The **right-click Restart leaves paused sessions alone** — restarting a session you parked on purpose is a surprise from a context menu, not a convenience. Reach for the modal instead when you want to **pick from a list**, **filter by title**, or act across **all** projects at once. See [bulk-select-sidebar.md](bulk-select-sidebar.md) for the multi-select keys.

**Restarting the one session you're looking at moves you to the next one.** When you restart a _single_ session that's the one currently in front of you — from this right-click menu, the session's own **Restart** button, or a recovering session's **"Restart now"** line — Omniscio carries you to the **next** session to deal with, exactly like replying, archiving, or snoozing does (2026-06-30). A _deliberate_ multi-session restart (ticking several in the modal, or right-clicking a multi-selection) is treated as a batch operation, not one-at-a-time triage, so it leaves your place put. If a single restart fails, you snap back to the session you were on. See [inbox-navigation-contract.md](../../.claude/memory/contracts/inbox-navigation-contract.md) `active-session-auto-eject`.

## For agents

### How it works

Restart, Pause and Archive loop their **existing** single-session calls (Restart is `SESSION_RESTART` → `relaunchSessionInPlace`, which resumes with `--resume`), so [session-bulk-actions-slice.ts](../../src/renderer/src/stores/slices/session-bulk-actions-slice.ts)'s `bulkRestartSessions` / `bulkPauseSessions` / `bulkArchiveSessions` inherit the per-session busy-guard and the routing for non-Claude engines (Codex/Gemini/etc.) for free. **Stop is the one exception:** `bulkTerminateSessions` sends **one** `session:terminate-many` request. Main refuses ids that are missing, someone else's, or mid-restart, writes the "stopped by you" hold on the rest in one write, answers at once, then stops them in the background at the app's own quit-time pace; a repeat Stop joins the one in progress instead of failing. A refused row reverts to its real status (never a fake "ended"); a Restart flips each row to "Starting" **before** it makes the relaunch call — not after — so the backend's own "running" signal always lands afterward and the dot settles green (flipping it _after_ the call used to let a session that was already running get repainted a stuck grey "Starting" that never cleared). A row whose relaunch fails is reverted to its prior status (never a stranded fake "Starting"), and the relaunches **fire in the list's display order**, top to bottom. For a **multi-session** restart, each relaunch is tagged with the `user-cohort` lane of the app's spawn gate ([spawn-concurrency-gate.ts](../../src/main/process/spawn-concurrency-gate.ts)). WHICH lane is decided by **who asked**, never by the batch's size: `relaunchSessionInPlace` reads the `trigger` it already requires, so a batch a PERSON pressed is `user-cohort` while the unattended ops sweeper that passes the same `paced` flag stays on `recovery` (fail-closed — only a call that explicitly says a human asked gets the user lane). `user-cohort` takes the same user-configurable **`Spacing between sessions when many restart at once`** gap (`sessionRestartSpacingSeconds`, default ~1.5s, Settings → Performance; 0 disables the gap) and the commit-cliff memory guard, and takes **neither** busy-based hold: not the box-load hold (`isBoxUnderHeavyLoad`) and not the capacity hold. That is `the-user-is-never-paced-by-capacity` (capacity contract AX4), and it is what stops a watched batch paying 30 s + 30 s per session — measured 2026-09-21, 40-70 s per session serially, because on a permanently-busy box both holds burned their full ceiling on every spawn and let it through anyway. That setting is the gap's **ceiling**: `restartRampFactor()` scales it down to a floor of ¼ once BOTH app uptime has cleared the boot window AND the box measures calm (CPU + commit headroom), because the startup work the full gap was protecting is by then already done — and it re-widens to the full value the moment load returns. **The gap runs on the lane's OWN start line** — `paceRecoveryStart` keeps one cursor per paced lane, so a `user-cohort` batch is spaced only against its own members and never queues behind the `recovery` starts already reserved (Sentry 7760505906, 2026-09-28: one shared cursor put a person's batch ~12 minutes behind background restarts, and the restart IPC outlived its 10-minute abandon ceiling). **Each batch row is re-checked when its turn comes:** a row that was a restart target (`isRestartableTarget`) when picked carries `onlyIfRestartable`, and `relaunchSessionInPlace` leaves it alone — nothing killed, marked or written, counted as done — if it no longer is one (it came back on its own) or another restart already holds it; a came-back row also gets its real status re-pushed, because the batch painted it "Starting" and no launch will correct that. A single restart a person asks for is marked the immediate "interactive" lane outright rather than left unset, so a leftover lane mark (a replay that bailed without spawning) can never slow it. Locked by contract [I10](../../.claude/memory/contracts/bulk-session-actions-contract.md) and [I28](../../.claude/memory/contracts/bulk-session-actions-invariants-postmortem-ledger-contract.md).

Adding Pause and Archive added **no** store or IPC surface at all — the modal simply calls the same two bulk actions the sidebar right-click menu has always used, so both paths share one busy-guard, one optimistic update, and one per-row failure reconcile. Their **Undo** goes through `registerSessionActionUndo`, the single chokepoint that arms the Ctrl+Z entry and shows the Undo toast together (so the two can never drift apart): Pause's inverse is `bulkUnpauseSessions`, and Archive's captures each Needs-You row's status _before_ archiving so undo restores it exactly rather than dropping it back as a plain idle row. Every archive from this modal is stamped with its own `user-manage-sessions` attribution, so the Archived view can tell you it was you, from here.

Which sessions each view shows is decided by pure selectors in [session-bulk-actions.ts](../../src/renderer/src/lib/session-bulk-actions.ts): both the modal's **Stop** and the quick right-click Stop use the narrow `selectStoppableSessions` = running + starting; `selectPausableSessions` reuses the app-wide `PAUSE_ELIGIBLE_STATUSES` set that the right-click Pause, the mobile selection bar and the inbox-row menu already gate on (imported, never re-derived, so they cannot disagree) — everything except `archived` / `paused` / `error` / `terminating`, which is what the pause service itself accepts, and which notably INCLUDES a settled `ended` session so the sidebar's Interrupted pile can be parked; `selectArchivableSessions` is simply everything not already archived; `selectRestartableSessions` = error + stalled, plus the `needs_you` give-up / interrupted-on-close carve-outs, plus stopped `ended` sessions that have content to resume — a completed turn (`numTurns > 0`) OR a message you sent (`lastUserMessageAt`), which keeps a session interrupted mid-first-turn while genuinely blank never-used sessions and every _other_ `needs_you` sub-state stay excluded), both of which also drop hidden background "silent recipe" sessions so the list always matches what you can see. The **modal's Restart view** adds one selector on top of that — `selectModalRestartSessions`, which is `selectRestartableSessions` PLUS every parked `paused` session, with **no** conversation gate (the sidebar's Paused list shows them all, so hiding a blank one here would read as a bug). That is the ONLY selector the Restart view uses — for the list it shows **and** for the re-check at Apply time — which is exactly why a paused pick survives to Apply instead of being filtered away as unknown at the last moment. The **right-click** Restart keeps `isRestartableTarget` untouched. The modal itself ([ManageSessionsModal.tsx](../../src/renderer/src/features/dashboard/ManageSessionsModal.tsx)) is almost entirely presentational — it just owns the current view, your selection, and the filter (there is **no** list order to own: the list is a picker, and both views render the same one-control row so the session **name** gets the width), and reuses the app's shared modal, button, and plain-Enter-submit building blocks — the last via [useEnterSubmit.ts](../../src/renderer/src/hooks/useEnterSubmit.ts) — and every per-view thing (the list, the selector, the icon, the Apply verb, the empty state, the selection Set) is keyed by action kind, so adding another view is a compile error until it names all of them (and because typecheck is currently switched off on this repo, a test also walks the registry at runtime and opens every view, so a missing entry cannot ship as a blank label); so plain **Enter** anywhere in the body runs Apply while `useCtrlEnterSubmit` keeps Ctrl+Enter; the hook deliberately yields Enter to the "first/last" dropdown because a native descendant listener beats React's delegated `onKeyDown`, so it bails on any Enter-owning control instead of trusting `preventDefault` (see [plain-enter-submit-postmortem.md](../../.claude/memory/postmortems/plain-enter-submit-postmortem.md)). The Stop list renders as a plain flat list (running-only is a single kind of session, so there are no group headers). The **title filter** sits purely on top in the renderer: it derives the visible (matching) rows and points Select-all, the count, the first/last picker, and the applied ids at them — so an empty filter is byte-identical to before and the filter never touches the status buckets, selectors, store, or IPC. The **Restart** view carries a second filter on that same mechanism — the **Interrupted / Paused / Both** switch — narrowing the same visible set by each row's own status (a row is Paused iff its status is `paused`; `Both` is a filter value, never a bucket a row can be in, and the bucket is read from the row's own status rather than the sidebar's display partition, which would file a reconnecting error row under `live` and put it in neither option). It is Restart-only, because no other view's rows carry a restart bucket, and it resets to Interrupted on every view switch — so the tab always opens showing exactly the list it showed before paused sessions were included. Because that Enter hook would otherwise fire the **paid** Stop/Restart on plain Enter from the filter box too (a `type="search"` input is not an Enter-owning control), the filter box carries its own small **native keydown guard** that stops plain Enter locally before it reaches the body listener (Ctrl+Enter still passes through and applies). Locked by contract [I11](../../.claude/memory/contracts/bulk-session-actions-contract.md). Its one live touch: each row's status dot renders through a small store-connected `LiveStatusDot` so the dots honor the same recovery/overlay overrides as everywhere else in the app — a session mid-restart shows the green "running" dot here too, not a stale grey "Starting" that reads as suspended (see [optimistic-running-dot-contract.md](../../.claude/memory/contracts/optimistic-running-dot-contract.md)). One app-level owner, [useBulkSessionActions.ts](../../src/renderer/src/hooks/useBulkSessionActions.ts), listens for a single open event from both the toolbar and the project-header ⋮-menu item ([project-bulk-action-menu-items.ts](../../src/renderer/src/features/dashboard/project-bulk-action-menu-items.ts)), feeds the modal the live lists, and — at the moment you press Apply — re-checks your picks against what's still actionable so a session that changed while the modal was open is never acted on by mistake. The full rule set (the status buckets, the per-id failure handling, the order-preserving restart, the narrow-Stop + no-confirm decision, and which views are undoable) is locked in [bulk-session-actions-contract.md](../../.claude/memory/contracts/bulk-session-actions-contract.md).

**The broadcast reuses the app's existing agent-to-agent delivery**, rather than inventing a second way to put a message into a session. A single new channel, `SESSION_DELIVER_MESSAGE`, wraps `deliverPeerMessage` ([peer-message-service.ts](../../src/main/services/peer-message-service.ts)) — the same code path one agent uses to message another — so every safety rule it already enforces applies unchanged: a session you closed is refused, a session that has asked not to be disturbed is still held, and the arrival renders as the same labelled bubble. The one rule the broadcast deliberately does **not** inherit is the busy-target hold: an automated sender's message to a mid-turn agent is parked in that agent's queue until the turn ends, and a broadcast from **you** used to be parked there too — while the app still reported it sent. The broadcast now grants the same interrupt your own Send button has, so a mid-turn recipient takes the message immediately. An outside engine still queues instead (it has no interrupt path), and the modal's consequence line says so rather than promising an interrupt the code cannot perform. A store action, `bulkBroadcastMessage`, loops that single-session channel **sequentially**, one recipient at a time, because a delivery can respawn an agent and firing N at once would be a spawn storm. The recipient bucket lives beside the other four as `MESSAGEABLE_STATUSES` / `selectMessageableSessions`, with one deliberate difference from its siblings: it drops **every** silent automation lane, not just the hidden ones. The app's "is this hidden?" rule answers _no_ for a silent lane that's actively running — correct for the views that act on what you can see, but wrong for a broadcast, which would land mid-script. The archived rail's rows are appended to the same list the other views use, so the filter, Select-all and first/last picker govern them with no special cases; the wake flag is decided **per recipient**, so a session that archived itself while the modal was open is left alone, and one that un-archived is delivered to once, not twice. The same action is available from a terminal as `POST /session/:id/deliver-message`, deliberately restricted to you as the operator: an agent-scoped token is refused, because otherwise any running agent could wake archives and reach across repos — two things the agent-to-agent route withholds on purpose. Locked by contract [I17–I22](../../.claude/memory/contracts/bulk-session-actions-invariants-postmortem-ledger-contract.md).

**The Queued view is computed in Main and refreshed while open.** `session:restart-queue-preview` runs `buildRestartQueueSnapshot` ([restart-queue-preview.ts](../../src/main/services/recovery/restart-queue-preview.ts)), which reads the recovery drip-feed's ordered queue and the durable command-line spawn queue together, then resolves every session name in one batched read and every project name in another. The spawn half comes from `listDrivableSpawnSummaries()` ([queries-pending-cli-spawns.ts](../../src/main/db/queries-pending-cli-spawns.ts)), which shares one FROM/JOIN/WHERE/ORDER clause with `listDrivableSpawns()` — the read the backed-up alert counts — so the view and the alert can never list different rows, and it skips the prompt column (a prompt can run to ~100 KB). `startQueue` is scoped to the opener's project like the restart `queue`, while both totals stay global so the view can say what waits elsewhere. The owner hook re-reads the snapshot on open and every 10 s while the modal is open, through one reader whose sequence number drops a slow answer that lands after a newer one; a failed re-read never replaces a list on screen with the error state. The start rows never join `sessionsByView`, so Select-all, the count and Apply cannot reach them. Locked by contract [I25](../../.claude/memory/contracts/bulk-session-actions-invariants-postmortem-ledger-contract.md).

## Related

The single-session controls these bulk actions loop over — Pause, Esc and End, and how sending a
message auto-resumes a stopped session — are on the
[pause or stop a session](pause-or-stop-a-session.md) page. Putting one session away at a time is
on [archive a session](archive-a-session.md), and what happens to your sessions when the app
restarts is on [crash recovery](crash-recovery.md).

- [pause-or-stop-a-session.md](pause-or-stop-a-session.md) — the single-session Pause / Esc / End controls, and how Send auto-resumes a stopped session
- [archive-a-session.md](archive-a-session.md) — archiving one session at a time, and restoring from the Archived view
- [distribute-sessions-across-accounts.md](distribute-sessions-across-accounts.md) — how a batch of restarts spreads across your Claude accounts
- [crash-recovery.md](crash-recovery.md) — what happens to your sessions when Omniscio restarts, and the two other ways to stop a resume wave
- [session-refill-governor.md](session-refill-governor.md) — the auto-restarter behind the Refill view: what counts as "interrupted", and every limit on how much it may spend