---
title: Resources Diagnostic Panel (in-app process monitor)
---

# Resources Diagnostic Panel (in-app process monitor)

## What it is

A read-only Task-Manager-style view inside Omniscio at **Settings → Tools & Maintenance → Diagnostics → Resources tab** that shows live CPU and memory for the Omniscio main process, the Omniscio renderer, every active Claude CLI session, every running MCP server, every aside agent, and any other Omniscio-spawned child process. Each row carries an inline 30-sample CPU sparkline and (for the killable types) a Kill button that opens a confirm dialog before sending SIGKILL.

**Why this exists.** When Omniscio starts feeling sluggish or its RSS climbs into the gigabytes, the user needs a way to see _which_ child process is responsible without opening Task Manager and squinting at PID columns trying to figure out which `node.exe` is which CLI session. The Resources panel groups every Omniscio-spawned process by type, labels each one with its session/account/project, and lets the user kill a runaway without taking down the whole app. Pairs with the heap-snapshot diagnostic for the renderer-only side of the picture.

## Where to find it

### How to open it

Two entry points:

- **Toolbar overflow menu** (fast path) — click the **`…`** (More) icon at the right of the Omniscio toolbar and pick **Resources** (activity icon). This jumps directly to the Resources tab under Settings → Tools & Maintenance → Diagnostics, expanding the Tools & Maintenance sidebar group on the way. Buried in overflow by default; right-click the menu item and pin it to surface it on the toolbar bar itself.
- **Settings sidebar** — click the gear icon (or open the **Settings** virtual project from the sidebar), expand the **Tools & Maintenance** group, open **Diagnostics**, and select the **Resources** tab.

The panel fills the right pane. There is no global hotkey.

## How it behaves

### Desktop-only (not available over the mobile/web bridge)

The Resources panel is a **desktop-only** surface. Its live sampler feed (`RESOURCES_TICK`) is never forwarded to a paired phone, so a mobile/web client could not render live data anyway — and all five of its IPC channels (`RESOURCES_SUBSCRIBE` / `RESOURCES_UNSUBSCRIBE` / `RESOURCES_HEARTBEAT` / `RESOURCES_GET_INITIAL` / `RESOURCES_KILL_PROCESS`) are refused at the WS bridge (`BLOCKED_CHANNELS`), so a phone only ever gets _"This action is only available in the desktop app."_ The renderer hook is fully inert off the desktop — it fires no invokes and reports the panel unavailable. Killing a host process from a phone is deliberately not reachable. (Before this was enforced, three of the handlers threw `Cannot read properties of null (reading 'sender')` over the bridge — the panel keyed its subscription off the Electron event's `sender.id`, which is null on the eventless WS transport. See the Resources Diagnostic Panel postmortem — `.claude/memory/postmortems/resources-diagnostic-panel-postmortem.md`, the 2026-08-01 entry.)

### Three view states

The panel reflects whichever of three states the resources sampler is in:

1. **Loading** — first-mount, before the initial subscribe round-trip has resolved. Shows a centered spinner with the label "Connecting…". This is the `tick === null && available === null` state.
2. **Unavailable** — the resources sampler IPC handler is missing or returned a failure envelope. Shows a red message: _"Resources sampler unavailable. Restart Omniscio to retry, or check the main-process log for sampler errors."_ This is the **partial-deploy gate** — if a future build of Omniscio ships the renderer panel without the matching main-process service (or the service crashed during boot), the panel fails closed instead of hanging on a perpetual spinner. Triggered by either a thrown rejection or a `{ success: false, error: ... }` envelope from `RESOURCES_SUBSCRIBE`.
3. **Ready** — the first sampler tick has arrived. Renders the process tree and the Flatten / Tree toggle. Subsequent ticks update in place every ~2 seconds via push.

### What you see in the Ready state

### Top-right toggle

A single button — **Flatten** when in tree view, **Tree** when in flat view — switches between two layouts. The toggle only appears once a tick is loaded; it disappears in Loading and Unavailable states.

### Tree layout (default)

Processes are grouped by Omniscio role:

- **Omniscio main** — the Electron main process itself
- **Omniscio renderer** — the renderer (UI) process
- **CLI sessions** — every active `claude` child process, one row per session
- **MCP servers** — every running MCP server (e.g. MemPalace, Playwright, Context7)
- **Asides** — every running `--fork-session` aside agent
- **Other** — any other Omniscio-spawned child process the sampler picked up

Each group is a labeled section with its rows beneath. The tree layout is what the user wants 90% of the time — it answers "which kind of process is bloated" first, then drills to the specific row.

### Flat layout

A single sorted list of every process with no grouping, sorted descending by CPU. Useful when you want to see the absolute top consumers regardless of type. The list is capped at 50 rows to avoid long scrollbacks on heavily-multiplexed installs.

### Per-row columns

Every row carries:

- **Type icon** (small lucide icon — server, monitor, terminal, wrench, split, circle)
- **Label** — for CLI sessions, the session label/title; for MCP servers, the server name; for asides, the aside id; for Omniscio main/renderer, a fixed name
- **PID**
- **CPU%** — current sample, formatted to one decimal (e.g. `12.3%`). Tooltip clarifies "% of one logical core" so 100% is one core, not the whole machine
- **Memory** — RSS in MB, formatted to whole numbers (e.g. `567 MB`)
- **Uptime** — `42s` / `15m` / `2h 30m` / `1d 4h` rollup
- **Account • Project** — for sessions, the Claude account label and the project name joined by `•`; empty for Omniscio main / renderer / orphan rows
- **Status** — for sessions, the live `sessionStatus` (`running`, `paused`, `error`, etc.); for non-session rows, the type label
- **Sparkline** — 30-sample CPU history rendered as a 60×16 px inline SVG, so trends are visible without clicking
- **Kill button** — only on killable types (see below)

### The Kill flow

Clicking a row's **Kill** button opens a confirm dialog with type-specific consequence text so the user knows what they're about to break:

| Process type   | Consequence text                                                                                  |
| -------------- | ------------------------------------------------------------------------------------------------- |
| `cli-session`  | "This terminates the Claude CLI for this session."                                                |
| `mcp-server`   | "Killing this MCP server may cause the parent CLI session to lose tool access until it restarts." |
| `aside`        | "This terminates the running aside agent. Any in-flight work will be lost."                       |
| `other`        | "This sends SIGKILL to the process. Use only if you know what this process does."                 |
| `amc-main`     | (no kill button — see below)                                                                      |
| `amc-renderer` | (no kill button — see below)                                                                      |

The dialog title is "Kill process?" and the body always opens with the row's label and PID — `Hello world session (PID 4242). This terminates the Claude CLI for this session.` Pressing **Confirm** fires `RESOURCES_KILL_PROCESS` with `{ pid, expectedSpawnTime }`; pressing **Cancel** does nothing.

The `expectedSpawnTime` field is critical. The sampler captures each process's spawn timestamp on first sight, the renderer sends it back on kill, and the main-process handler verifies it matches the live PID's actual spawn time before killing it. This prevents a recycled-PID race: between the user clicking Kill and the IPC arriving in main, the original process could have already exited and the OS could have reassigned its PID to a new, unrelated process. The spawn-time guard refuses to kill in that case.

Once the guard passes, a **`cli-session`** row routes through the normal session-terminate path (which reaps the whole session tree). A **non-session** row (`mcp-server`, `aside`, `other`) gets a **verified tree-kill**: it terminates the target _and its entire child subtree_ (`taskkill /F /T` on Windows), then re-checks that the PID is actually gone before it removes the row. This matters for a wedged leaf like a stuck `grep` under `rtk` — the tree-kill takes the whole `rtk → grep` chain, and the confirmation means a process that _survives_ the kill (protected/elevated, or stuck in uninterruptible I/O) surfaces as an honest failure and keeps its row, instead of being optimistically removed and then silently reappearing on the next 2-second tick.

### Why Omniscio main and Omniscio renderer have no Kill button

Killing the Omniscio main process takes the whole app down, including this panel. Killing the Omniscio renderer takes the UI down (and forces a reload). Both are "killing the doctor" cases — there's no realistic user benefit to surfacing the button, so it's hidden entirely on those two row types. The dialog and consequence text don't apply.

### Cost and limits

- **CPU**: the sampler costs ~1–3% of one core during each ~2-second tick on a typical install (10–20 child processes). The cost scales linearly with process count; an extreme install with 50+ children would cost more.
- **Memory**: the sampler keeps a 30-sample sparkline ring buffer per process — small (~120 bytes per process), bounded.
- **No persistence**: the tick history is in-memory only. Closing and reopening the panel starts fresh; closing Omniscio drops everything.
- **Push-only**: the panel subscribes via `RESOURCES_SUBSCRIBE` on mount and unsubscribes on unmount. There is no polling — the renderer doesn't ask, the main process pushes. If the user leaves the panel open in a background tab the subscription stays live; close the panel to stop the push stream.
- **Flat layout cap**: 50 rows. The tree layout has no cap (groups are short).
- **Kill is a forceful, verified tree-kill**: `taskkill /F /T` for non-session rows (no graceful-shutdown variant) — it hard-stops the target and every child under it, then confirms the PID is gone before clearing the row (a survivor stays visible as an honest failure, never optimistically removed). The button is a fast-stop for runaways; it does not give the process a chance to flush state. Use the in-app pause / archive flows for normal lifecycle.

### When to use it

- "Omniscio is using 4 GB of RAM, where's it going?" Open Resources, sort the flat view, look for the row with the highest memory; check whether it's a single CLI session, a stuck MCP server, or the renderer itself.
- "One of my Claude sessions seems frozen — its CPU has been pinned at 100% for 30 seconds." Find the row in the tree under **CLI sessions**, watch the sparkline confirm the pin, kill it.
- "I'm not sure if my MemPalace MCP server is actually running." Check **MCP servers** in the tree; presence of a row means it's alive.
- "Aside spawned, completed, but the process didn't exit?" Look under **Asides** — you'd see a long-uptime row that should have ended seconds ago, kill it.

### When NOT to use it

- Don't use it as a session lifecycle tool. The right way to end a session is the in-app **pause** (P key), **archive** (Ctrl+W), or **End** keyboard flows — those clean up state, write last messages, update the sidebar. Kill is for "this child process is unresponsive and won't die."
- Don't use it for the renderer's own memory — that's what the [heap snapshot diagnostic](heap-snapshot-diagnostics.md) is for. The Resources panel shows _RSS_ for the renderer; the heap diagnostic shows _what's pinned_ inside it.
- Don't kill the `amc-main` or `amc-renderer` processes externally either — there's no kill button for a reason. To close Omniscio, use the tray quit menu.

## For agents

### Related code

- `src/main/services/resource-monitor.ts` — the sampler service that ticks every ~2 seconds, walks the Omniscio process tree, captures CPU/RSS/spawn-time, and emits `RESOURCES_TICK` push events. Owns the kill-by-PID-and-spawn-time guard.
- `src/renderer/src/features/settings/sections/diagnostics/DiagnosticsSettings.tsx` — the Settings sub-page; renders the heading + flatten toggle and routes between Loading / Unavailable / Ready states.
- `src/renderer/src/features/settings/sections/diagnostics/diagnostics-resources/useResourcesSubscription.ts` — the React hook that owns the subscribe / unsubscribe round-trip and surfaces `{ tick, available }`. Returns `available: false` when `RESOURCES_SUBSCRIBE` rejects or returns a failure envelope (the partial-deploy gate), and is fully inert (no invokes, `available: false`) on a non-Electron client since the panel is desktop-only.
- `src/renderer/src/features/settings/sections/diagnostics/diagnostics-resources/ProcessTree.tsx` — the tree-vs-flat container. Groups by type for tree layout; sorts by CPU and caps at 50 rows for flat.
- `src/renderer/src/features/settings/sections/diagnostics/diagnostics-resources/ProcessRow.tsx` — a single row. Owns the per-row layout, the kill button, the confirm dialog, and the type-specific consequence text map.
- `src/renderer/src/features/settings/sections/diagnostics/diagnostics-resources/Sparkline.tsx` — the inline 60×16 SVG sparkline (no external charting dep).
- `src/shared/ipc-channels/` — `RESOURCES_SUBSCRIBE`, `RESOURCES_GET_INITIAL`, `RESOURCES_TICK`, `RESOURCES_KILL_PROCESS` channel names.
- `src/shared/resources-types.ts` — `ResourcesProcess`, `ResourcesProcessType`, `ResourcesTick` shared types.
- `tests/unit/features/settings/DiagnosticsSettings-resources-tab.test.tsx` — shell + state coverage (loading, unavailable, ready, tree wiring).
- `tests/unit/features/settings/diagnostics-resources/{ProcessRow,ProcessTree,Sparkline,useResourcesSubscription}.test.tsx` — per-component coverage including the kill-confirm flow and consequence text per type.

## Related

If the problem is the renderer's own memory rather than a child process, [heap-snapshot-diagnostics.md](heap-snapshot-diagnostics.md) shows what is pinned inside it — the companion to this panel's RSS figures and the reason the two sit side by side in Diagnostics. [INDEX.md](INDEX.md) is the library index if you want to browse the rest of the diagnostics and performance surfaces.
