Compaction Summary (expandable inline)
What you see when a session's context window fills and Claude writes a condensed summary of the conversation so far — the "Conversation compacted" divider, and the Show summary link that expands the full text inline. Covers how to open and copy it, when the link is not offered, and how history loads after a compaction.
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
- 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
/compactin the input of any active session with a decent amount of conversation in it. - Click "Show summary ▾". The link sits next to the timestamp on the divider, in the accent color.
- 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.
- 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.
- 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 and 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
isCompactSummaryrecord sits at-or-before the divider'sboundaryTimestampin 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--resumehands back a freshsession_idand 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
usesClaudeBinaryagain; see the guard inprovider-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
/compactdivider load immediately — so the post-compaction conversation is fully visible. - Pre-compact rows are hidden by default. Older turns from before the most recent
/compactare 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 theLOAD_OLDER_MESSAGES_PILL_ENABLEDcode-level flag inSessionPanel.tsx— see load-older-pill-disabled postmortem. 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
/compactdivider 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:
- You always see the current disk state. No stale cache. If the CLI rewrites the transcript, the next click reflects that.
- Global Search (Ctrl+K) is not polluted with summary text — only the actual conversation turns are indexed.
- No database migration was required to ship this feature. We only added a
metadata TEXTcolumn toconversation_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:
- Renderer fires
compaction-summary:readwith{ sessionId, boundaryTimestamp }(the timestamp on the divider metadata). - Main process resolves the session's current CLI identifier (the
cli_session_idcolumn) and locates<claudeHome>/projects/<encoded>/<cliSessionId>.jsonlby scanning the project folders (CLAUDE_CONFIG_DIRoverrides~/.claude). - The reader opens that file with a streaming
readlineinterface (so memory stays bounded even for 100MB+ transcripts) and walks top-down looking forisCompactSummary === true && isSidechain === false. It tracks the latest matching record whose timestamp is at-or-beforeboundaryTimestamp, then returns that record'smessage.content. As soon as it sees a record withtimestamp > boundaryTimestampit stops scanning (JSONL is append-only and chronological, so nothing after that line can match). - Backward direction + the rotation fallback. The "at-or-before" direction matters because the CLI writes the
isCompactSummaryrecord FIRST and then emits thecompact_boundaryevent over stdout. Omniscio stampsboundaryTimestamp = 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-lookingtimestamp >= boundaryTimestampfilter (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 itssession_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.jsonlin 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-erroron the primary file are NOT treated as a miss — they mean "retry", so the fallback is skipped rather than surfacing a stale neighbour.) - The
isSidechainfilter is important — subagent compactions in the same file would otherwise match and return the wrong summary. - 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 — 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 — resolves a
cliSessionIdto its JSONL path and enumerates the project folder(s) the fallback scans - 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 — the
emitSystemMessageWithMetadatahelper that writes the divider with its metadata when compaction happens - 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 rules that decide how much history comes back afterwards, the stuck session 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 page. Where compaction summaries are — and deliberately are not — searchable is covered on the global search page, and the project in which long-running chats most often hit a compaction is described on the default Claude project page.
- 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-pairgetSessionHistoryLite, not the fullgetSessionHistory) - 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 — long-running chats in the auto-created
~/Claudeproject are the most common place you'll encounter compactions - global-search.md — Ctrl+K does not index compaction summaries, by design; use the inline panel to read them
- 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
Last verified 2026-10-05