---
title: Compaction Summary (expandable inline)
---

# Compaction Summary (expandable inline)

## What it is

When a Claude session fills its context window it **auto-compacts**: Claude writes a structured 9-section summary of the whole conversation so far (primary intent, key technical concepts, files touched, errors and fixes, pending tasks, etc.) and the old turns are swapped out for that summary. You can also trigger the same thing manually by typing `/compact` in the prompt input. Either way, the session feed shows a subtle "Conversation compacted" divider where the swap happened.

Previously that divider was just a label — now, next to the timestamp, there is a small **"Show summary ▾"** link. Click it and the full compaction summary expands inline underneath the divider, rendered as markdown. There is a **Copy** link in the header of the expanded panel that puts the raw text on your clipboard. Click "Hide summary" to collapse it back down. The summary is streamed from disk on first expansion and cached locally for the rest of the session, so reopening is instant.

## Where to find it

There is nothing to open and nothing to switch on — the whole feature lives inside a session's
message feed, on the thin divider line that marks where a compaction happened. Scroll to that line
and the link you need is sitting next to its timestamp.

## How it behaves

### How to use it

1. **Find a compacted session.** Look in the session feed for a thin divider line that says "Conversation compacted" followed by "(auto, 156k tokens)" or similar. If a session has been running for a while and hit the context limit, you will have one of these. If you want to create one on purpose: type `/compact` in the input of any active session with a decent amount of conversation in it.
2. **Click "Show summary ▾".** The link sits next to the timestamp on the divider, in the accent color.
3. **Read the summary.** A card expands directly below the divider. At the top it shows a "SUMMARY" label and a "Copy" link. Below that, the full markdown summary renders with its normal code blocks, lists, and headings.
4. **Copy if useful.** Click "Copy" to put the raw markdown on your clipboard — good for pasting into a new session's prompt, an issue tracker, or a note. The clipboard contains the source markdown, not stripped plain text, so heading and bullet markers are preserved.
5. **Collapse.** Click the link (now labeled "Hide summary") to close the panel. The fetched content stays cached; reopening does not re-hit disk.

### When it is not available

The button only shows when all three of these are true:

- The divider was emitted by **this version of Omniscio** (or a later one). Old compactions from before the feature shipped do not have the required metadata on the divider, so there is no button — just the old label. You'll only see it on newly-compacted sessions going forward.
- The session was run by the **local Claude CLI binary** — which includes the engines that _are_ that binary pointed at another vendor's endpoint: DeepSeek, Kimi, GLM, MiniMax, Meta, Qwen, OpenRouter, GPT, xAI and the custom Anthropic/OpenAI providers. They all write the same Claude-shaped transcript, so they all get the button. (Until 2026-09-13 every one of them was wrongly treated as having no transcript, and their compactions answered "this session type has no transcript" over a summary that was sitting on disk. Fixed — and the fix is retroactive, because the historical dividers already carry the metadata the button needs.) Sessions running through the OpenClaw provider (the virtual `__openclaw__` project) have no local JSONL transcript, and SSH-remote sessions keep their transcript on the remote machine. In both cases the button is suppressed.
- The Claude CLI **transcript file is still on disk** at `~/.claude/projects/<encoded-workdir>/<cliSessionId>.jsonl`. If the CLI was uninstalled, the transcript folder was deleted, or the file was corrupted, clicking the button shows an inline error instead of the summary.

**Other engines compact too, and show the divider — just without the button.** **Codex** (2026-09-10) and **OpenCode** (2026-09-11) both rewrite their history the same way when the context window fills, and both now show a “Conversation compacted” divider where it happened — before that, neither left any trace in the transcript at all. Codex also shows the pulsing “Compacting conversation…” pill while the rewrite runs; OpenCode does not, because it announces the rewrite only once it is finished. Neither keeps a Claude-shaped on-disk transcript, so both dividers are written with the summary toggle switched off rather than offering a button that could only ever answer “unavailable”.

**On those engines the compaction row cannot be opened, and it says why (2026-09-14).** Hovering it shows: _“No summary to show — this engine compacts by dropping older messages rather than writing a summary of them, so there is nothing to read back. The conversation above the line is still in your history.”_ That is the literal truth for Codex, verified against its own on-disk session files: its compaction record carries an **empty** message field and its `context_compacted` event carries nothing but its own name, because Codex **truncates** history rather than summarizing it. There is no summary anywhere to find — not on the wire, not on disk. Before this, the two compaction surfaces disagreed about the same state: the `Compaction N` divider in the turn list offered a toggle that could only answer “this session type has no transcript”, while the message-bubble row hid its toggle and explained nothing. Both now refuse to open and give the same reason. See [codex-provider.md](codex-provider.md) and [opencode-provider.md](opencode-provider.md).

### Inline error messages

If you click the button and something goes wrong, the panel expands with one of these messages instead of the summary:

- **"Summary not available for this session."** — no non-sidechain `isCompactSummary` record sits at-or-before the divider's `boundaryTimestamp` in the session's current transcript **or** in any sibling transcript in the same project folder (within ~60s before the boundary). The reader scans both because the Claude CLI **rotates its session id** after an auto-compaction: the next `--resume` hands back a fresh `session_id` and writes a new `.jsonl`, so the live file the database points at holds only post-boundary rows and the summary is orphaned in the pre-compact file that came before it (see "Under the hood" below). With the folder-scan fallback in place this message should be genuinely rare — it means the pre-compact transcript was deleted/pruned, was truncated mid-write before the summary landed, or the gap between the record and the boundary exceeded the fallback window.
- **"Transcript is being written — retry in a moment."** — Windows file-lock conflict while the CLI is mid-write. The reader retries 3 times with 500ms between attempts; this message only shows if all three attempts hit the lock. Close the panel and reopen after a second or two.
- **"Unable to read the summary file."** — I/O error. Usually a permissions issue or a truncated file.
- **"Summary unavailable — this session type has no transcript."** — an engine that keeps no Claude-shaped transcript: Codex, Gemini, OpenCode, Cursor and the other external engines, plus SSH and OpenClaw sessions. A vendor running the Claude binary (DeepSeek, Kimi, GLM, …) must **never** produce this message — if one does, the capability flag has drifted from `usesClaudeBinary` again; see the guard in `provider-registry.test.ts`.
- **"Summary unavailable — no CLI session ID for this message."** — session row missing its CLI session identifier (rare, only on half-initialized rows).
- **"Summary unavailable — session metadata missing."** — session row not found in the database.

### How history loads after `/compact`

When you switch into a session that has a `/compact` divider in its history, Omniscio does not re-load the entire transcript. Cold-mount history is **clamped** to keep the renderer responsive on multi-thousand-message sessions:

- **First user message (pinned).** The session's earliest operator turn is always included so you can still see what you originally asked for at the top of the thread.
- **Everything since the most recent compact.** All rows after the latest persisted `/compact` divider load immediately — so the post-compaction conversation is fully visible.
- **Pre-compact rows are hidden by default.** Older turns from before the most recent `/compact` are not in the cold-mount fetch. (Earlier builds rendered a **"Load older messages"** pill at the top of the panel that revealed them; as of 2026-05-21 that pill is disabled by default for every user via the `LOAD_OLDER_MESSAGES_PILL_ENABLED` code-level flag in `SessionPanel.tsx` — see [load-older-pill-disabled postmortem](../../.claude/memory/postmortems/load-older-pill-disabled-postmortem.md). Search, export, share-publish and audit-export still read the full transcript.)
- **No clamp during in-flight compaction.** Omniscio waits until at least one agent turn lands after the `/compact` divider before the clamp activates. If you switch into a session during compaction (the divider has been written but the post-compact summary turn has not yet streamed), the full history still loads so you don't land at a blank panel.

The same clamp applies across every cold-mount cache-warmer (panel mount, Dashboard prefetch, sidebar-row hover, archive-row hover, mobile prefetch) so the cache is always consistent — an unclamped warmer cannot satisfy a clamped cold-mount fetch and silently include pre-compact rows.

On mobile the cold-mount limit is much smaller (2 main-thread rows + the pinned first user message) since web clients are weaker; otherwise the contract is identical.

This is a behavior change from earlier builds (pre-2026-05-20) where `/compact` only affected what Claude saw, not what the renderer showed. After the change, `/compact` is also a UI checkpoint — pre-compact context still exists in the database (search and export reach it) but is no longer surfaced in the chat panel by default.

## For agents

### Under the hood

**The summary is not stored in Omniscio's database.** It stays on disk in the Claude CLI's transcript file and is streamed in line-by-line on the first click. Three consequences:

1. You always see the current disk state. No stale cache. If the CLI rewrites the transcript, the next click reflects that.
2. Global Search (Ctrl+K) is not polluted with summary text — only the actual conversation turns are indexed.
3. No database migration was required to ship this feature. We only added a `metadata TEXT` column to `conversation_messages` (migration v82, already in place), and the divider message now carries `{ kind: 'compact-divider', boundaryTimestamp, trigger, preTokens }` in that column.

**The IPC path on click:**

1. Renderer fires `compaction-summary:read` with `{ sessionId, boundaryTimestamp }` (the timestamp on the divider metadata).
2. Main process resolves the session's current CLI identifier (the `cli_session_id` column) and locates `<claudeHome>/projects/<encoded>/<cliSessionId>.jsonl` by scanning the project folders (`CLAUDE_CONFIG_DIR` overrides `~/.claude`).
3. The reader opens that file with a streaming `readline` interface (so memory stays bounded even for 100MB+ transcripts) and walks top-down looking for `isCompactSummary === true && isSidechain === false`. It tracks the **latest matching record whose timestamp is at-or-before `boundaryTimestamp`**, then returns that record's `message.content`. As soon as it sees a record with `timestamp > boundaryTimestamp` it stops scanning (JSONL is append-only and chronological, so nothing after that line can match).
4. **Backward direction + the rotation fallback.** The "at-or-before" direction matters because the CLI writes the `isCompactSummary` record FIRST and _then_ emits the `compact_boundary` event over stdout. Omniscio stamps `boundaryTimestamp = Date.now()` on receipt, so the record's timestamp is always slightly BEFORE the boundary — a few ms when Omniscio reads the same live stream, but observed as much as **~2 seconds** when the boundary is processed during a busy reconnect/replay. A forward-looking `timestamp >= boundaryTimestamp` filter (the original v1) skipped the matching record entirely. **There is a second failure mode the backward scan alone does not solve:** after an auto-compaction the CLI **rotates its `session_id`**, so the file the database now points at is the POST-compact transcript — every record in it is after the boundary and the scan finds nothing. When the primary file yields a clean miss, the reader **falls back to scanning every `.jsonl` in the same project folder** (same cwd hash) and returns the non-sidechain summary whose timestamp is the latest at-or-before the boundary, provided it falls within ~60s of it. That recovers the orphaned pre-compact transcript with no database migration. (`locked`/`io-error` on the primary file are NOT treated as a miss — they mean "retry", so the fallback is skipped rather than surfacing a stale neighbour.)
5. The `isSidechain` filter is important — subagent compactions in the same file would otherwise match and return the wrong summary.
6. On Windows file-lock errors (`EBUSY`/`EACCES`/`EPERM`), the reader retries up to 3 times with 500ms delays before returning `{ ok: false, reason: 'locked' }`.

**Key files:**

- [src/main/services/compaction-summary-reader.ts](/src/main/services/compaction-summary-reader.ts) — the streaming reader with retry logic and the same-folder fallback scan that recovers the orphaned pre-compact transcript after a CLI session-id rotation
- [src/main/services/cli/cli-jsonl-resolver.ts](/src/main/services/cli/cli-jsonl-resolver.ts) — resolves a `cliSessionId` to its JSONL path and enumerates the project folder(s) the fallback scans
- [src/main/ipc/compaction-summary-handlers.ts](/src/main/ipc/compaction-summary-handlers.ts) — the IPC handler that resolves the CLI session ID and calls the reader
- [src/main/process/process-manager.ts](/src/main/process/process-manager.ts) — the `emitSystemMessageWithMetadata` helper that writes the divider with its metadata when compaction happens
- [src/renderer/src/components/ui/MessageBubble/MessageBubble.tsx](/src/renderer/src/components/ui/MessageBubble/MessageBubble.tsx) — the button, the expand handler, the per-reason error messages, and the Copy wiring

## Related

Compaction is one answer to a context window filling up, and the pages worth reading alongside it
are the [lazy content loading](lazy-content-load.md) rules that decide how much history comes back
afterwards, the [stuck session](session-stuck-in-needs-you.md) failure mode where a turn simply
stops because it ran out of room, and the deliberate alternative of starting a fresh linked session
instead, described on the [session handoff](session-handoff.md) page. Where compaction summaries
are — and deliberately are not — searchable is covered on the [global search](global-search.md)
page, and the project in which long-running chats most often hit a compaction is described on the
[default Claude project](default-claude-project.md) page.

- [lazy-content-load.md](lazy-content-load.md) — the `/compact`-clamp described above sits underneath the lite cold-mount path; a clamped lite cold-mount on a settled-compact session returns the since-compact rows in lite shape (function-pair `getSessionHistoryLite`, not the full `getSessionHistory`)
- [session-stuck-in-needs-you.md](session-stuck-in-needs-you.md) — compaction is the CLI's way of avoiding context-overflow errors; this page explains the adjacent failure modes
- [default-claude-project.md](default-claude-project.md) — long-running chats in the auto-created `~/Claude` project are the most common place you'll encounter compactions
- [global-search.md](global-search.md) — Ctrl+K does _not_ index compaction summaries, by design; use the inline panel to read them
- [session-handoff.md](session-handoff.md) — the deliberate alternative to letting a session compact. Compaction keeps the SAME session and carries everything forward in condensed form, dead ends included; a handoff starts a NEW linked session from a curated summary that explicitly lists what not to retry. Handoff does not replace compaction — a session you never hand off still compacts exactly as described above
