---
title: Copy / export a session as Markdown
---

# Copy / export a session as Markdown

## What it is

Every session can be exported as a Markdown transcript. Right-click a session in
the sidebar (or click the `⋯` overflow menu in the session header) and open the
**Exports** submenu. It's grouped into sections by destination — copy-to-clipboard,
save-to-file, send-elsewhere — separated by thin dividers, with a small
size line at the foot:

- **Copy as Markdown** — copies the visible conversation to your clipboard,
  ready to paste into another Claude session, ChatGPT, a doc, or anywhere else.
- **Copy since last compaction** — copies **only** the part of the conversation
  from the most recent compaction onward (explained below).
- **Export to Markdown** — saves the visible conversation to a `.md` file. On the
  desktop a native Save dialog opens with a pre-filled filename
  (`<session-name>-<date>.md`); on a phone it downloads/shares directly (see
  [On the mobile web client](#on-the-mobile-web-client)). (This was previously
  labeled **Download**.)
- **Export to Word** — saves the same conversation as a `.docx`. **Off by
  default** — turn it on in Settings → Sessions → "Export to Word" (it then also
  appears in the Export… menu on docs, scratchpads, and messages).
- **Export to PDF** — saves the same conversation as a `.pdf`. Always available.
- **Export raw thread** — a troubleshooting file rather than a transcript: every message the
  session stored, word for word, each labelled with whether you saw it, could have expanded it, or
  never could. It is unredacted, so treat it as sensitive. See
  [raw-thread-export.md](raw-thread-export.md).
- **Export to Google Docs** — turns the conversation into a new Google Doc.
  **Off by default** — turn it on in Settings → Google Workspace → Google Docs
  Export (uses your existing Google sign-in). See
  [google-docs-export.md](google-docs-export.md).
- **Share…** — publishes the session as a read-only web link (a separate
  feature; see [artifact-sharing.md](artifact-sharing.md)).

Word and Google Docs only show up once you've turned each feature on, so the
menu stays short until you opt in.

The exported Markdown starts with a small header (session name, project, start
time, status, and — when available — cost / turn count / token usage), then a
`## Conversation` section with each kept message under a `### You` or
`### Claude` heading, and a footer line `*Exported from Omniscio*`.

## Where to find it

There is no separate export screen. Everything on this page starts from the session itself:
right-click a session in the sidebar, or open the session and use the **⋯** overflow menu in its
header, and the actions are collected under the **Exports** submenu — grouped by destination, with
a size line at the foot of the list. The same actions are available from the mobile web client.

## How it behaves

### What's in the export — "visible-only" (2026-05-25)

The one exception is **Export raw thread**, which deliberately copies everything for
troubleshooting (see [raw-thread-export.md](raw-thread-export.md)); the rest of this page is about
the ordinary exports.

Every export and copy action exports **only what you actually saw on screen** —
your messages and Claude's prose, **including intermediate narration** ("Let me
check this file…", "Now I'll grep for X…"). The mechanical tool-call (`▸`) and
tool-result (`←`) marker lines that show up next to each agent turn are
**stripped** out, because they're the chatter you collapsed under "tool
activity" while reading, not the conversation itself.

A few specifics worth knowing:

- **System rows are dropped.** Status noise, the `Conversation compacted…`
  divider rows, and notable-system rows like "Process paused" or "CLI error"
  are all removed from the export. The export is a clean read of the actual
  conversation, not a process log.
- **A Claude turn that was only tool activity is skipped entirely.** If Claude's
  turn was nothing but `▸ Read: foo.ts` / `← 50 lines received` (no prose at
  all), it collapses to nothing after stripping, so Omniscio does not emit a bare
  `### Claude` header with no body under it.
- **`▸`/`←` markers inside fenced code blocks are preserved.** If Claude's
  message contains a code block that illustrates these markers (for example,
  docs about Omniscio itself), the export's stripper notices the surrounding triple
  backticks and leaves the fenced content untouched. So a code block that
  literally contains `▸ Read: foo` survives intact in the export.
- **Operator (your) messages are verbatim.** Visible-only stripping never
  touches your messages — they go in exactly as you typed them.

### The size line — words, characters, tokens

At the foot of the Exports submenu, below the last divider, is a single muted
line showing the size of the conversation you'd export:

```
1,234 words · 32k chars · ~7.1k tokens
```

So before you pay for the export — opening a save dialog, hijacking your
clipboard, creating a Doc — you know how big it is. The first time you open the
submenu on a session it shows `…` briefly (one small read-only fetch from the
database) and then resolves to the numbers. There's just **one** size line for
the whole conversation now, instead of the old repeated number next to every
item.

**"Copy since last compaction" is the one exception.** After a compaction it
copies a _smaller_ slice than the rest, so that item carries its own token count
in parentheses — e.g. `Copy since last compaction (~850)` — and hovering it
shows the word and character counts for that slice. Before any compaction (when
it would copy the whole thread anyway) it shows no separate number, so the same
count is never repeated.

The same size also appears in the **success toast** after you click (e.g.
`Copied as Markdown · ~7.1k tokens`), computed from the exact same assembler so
the menu and the toast always agree.

About the numbers:

- The token count is a fast estimate (characters ÷ 4), the same heuristic Omniscio
  uses elsewhere for context-size labels. It's within a few percent for prose
  and can under-count code-heavy transcripts by up to ~20%, which is why it's
  always shown with a leading **`~`** to mean "approximately." Words and
  characters are exact counts (no `~`).
- They format compactly: `~7.1k` tokens, `32k` chars, `1,234` words.
- They live **only** in the menu and the toast. They are **never** written into
  the Markdown file or the copied text — your transcript stays clean.

### "Copy since last compaction"

When a long session runs low on context, Claude **compacts** it — it summarizes
the conversation so far into a compact form and continues from there. Omniscio marks
that moment in the transcript with a `Conversation compacted…` divider.

**Copy since last compaction** copies the most recent compaction divider plus
everything that happened after it — the summary and all the work since. This is
the useful slice when you want to hand off "where things stand now" without
dragging along the entire pre-compaction history.

Two behaviors worth knowing:

- **Never compacted yet?** The action falls back to copying the **entire
  visible thread**, and the toast tells you so: `No compaction yet — copied
entire thread · ~N tokens`. You never get an empty copy. In this case the
  Copy-since item shows **no** separate size in the menu — it'd just be the
  whole-thread number already on the footer line.
- **A compaction that just happened doesn't count until Claude has replied
  after it.** A compaction divider only becomes a real "boundary" once a
  normal (main-thread) agent turn exists after it. If a compaction just
  landed and Claude hasn't produced its post-compaction reply yet, the action
  treats the session as "not compacted yet" and copies the whole thread
  instead — so you never copy a lone divider with nothing under it. (This
  matches how the chat view itself decides where the post-compaction
  conversation begins.)

### Asides are excluded

Every export and copy action exports the **main conversation only** (Export raw thread keeps
asides, labelled as such). If you've
used **Asides** (Ctrl+B side questions — see [asides.md](asides.md)), those
side branches are filtered out of the export. Asides are throwaway side
questions, not part of the transcript you're trying to capture.

### File export vs. Copy: which scopes support "since last compaction"?

Only **Copy** supports the since-last-compaction scope. The file exports
(**Export to Markdown / Word / PDF**) and **Export to Google Docs** always
use the whole thread. (If you need a since-compaction slice as a file, Copy it,
then paste into a new file.)

### On the mobile web client

Copy and export both work from your **phone** (the mobile web client), not just the
desktop:

- **Copy as Markdown / Copy since last compaction** put the transcript on the
  **phone's** clipboard. Even when you open Omniscio over a plain `http://` LAN
  address — an "insecure" browser context where the normal clipboard API is
  unavailable — Omniscio falls back to a copy method that targets the phone (on
  iPhone the paste may briefly flash a text selection). It never writes to your
  desktop's clipboard by mistake.
- **Export to Markdown / Word / PDF** download or **share** the file right in the
  phone browser — a phone has no native Save dialog, so Omniscio builds the file and
  hands it to your phone's download/share sheet. A conversation too large to
  transfer degrades to a friendly "open it on the desktop app" message rather than
  failing silently.

Both mobile paths reuse the exact same transcript builder as the desktop, so the
copied text / exported file is identical. (Under the hood: copy uses
`SESSION_COPY_MARKDOWN`; mobile file export uses a bytes-returning
`SESSION_EXPORT_BUFFER`; the desktop save-dialog channel stays desktop-only.)
Behavior contract:
`.claude/memory/contracts/mobile-conversation-export-contract.md`.

## For agents

### Where it lives in code

- **Markdown assembly + all three IPC handlers**: `buildSessionMarkdown` (now
  visible-only), `SESSION_EXPORT_MARKDOWN` (Export to Markdown / Word / PDF),
  `SESSION_COPY_MARKDOWN` (Copy / Copy-since), and `SESSION_EXPORT_TOKEN_PREVIEW`
  (read-only menu preview) in
  [src/main/ipc/session/export.ts](../../src/main/ipc/session/export.ts).
  Both action handlers fetch the whole thread via
  `getSessionHistory(id, 99_999)`; the copy handler branches to the scoped
  query when asked. The preview handler runs the same pipeline for both the
  whole-thread and since-compaction slices and returns words + characters +
  tokens for each, so the footer line always matches the toast counts. The save handler (and the
  phone's `SESSION_EXPORT_BUFFER`) also serves the troubleshooting raw thread export when the
  request carries `variant: 'raw'`; that path is described on
  [raw-thread-export.md](raw-thread-export.md).
- **Fence-aware tool-line strip**: `stripToolLinesFenceAware` in
  [src/shared/agent-content-markers.ts](../../src/shared/agent-content-markers.ts) —
  removes `▸`/`←` lines outside fenced code blocks and collapses the blank
  runs they leave behind.
- **Since-compaction scope query**: `getMessagesSinceLastCompaction` in
  [src/main/db/queries-messages/index.ts](../../src/main/db/queries-messages/index.ts) —
  returns the divider-forward rows and a `hadCompaction` flag (the
  "has-Claude-replied-yet" settle-gate).
- **Count estimate + display format**: `estimateTokens` / `formatApproxTokens`
  (tokens), plus `countWords` / `formatCount` (the footer's word + char counts)
  in [src/shared/token-estimate.ts](../../src/shared/token-estimate.ts).
- **Menu + toasts**: the **Exports** submenu — one shared `exportsBody` rendered
  by both the desktop flyout and the narrow-screen version, the footer size line,
  and the Word / Google Docs visibility gates (`wordExportEnabled` /
  `googleDocsExportEnabled`) — lives in
  [SessionOverflowMenu.tsx](../../src/renderer/src/features/sessions/SessionOverflowMenu.tsx);
  the handlers that build each toast (and `handlePublishToGoogleDocs`) live in
  [SessionPanel.tsx](../../src/renderer/src/features/sessions/SessionPanel.tsx).

The full set of test-locked invariants (token-format boundaries, visible-only
stripping rules, fence-aware strip, aside filtering, the since-compaction
settle-gate, menu-count === toast-count parity, audit-privacy rules) is
documented for agents in the feature contract at
`.claude/memory/contracts/session-markdown-export-contract.md`.

## Related

The **Share…** action that sits in the same submenu — a read-only public web link instead of a
local copy — is on the [artifact sharing](artifact-sharing.md) page. The side-question branches
exports deliberately leave out are on the [asides](asides.md) page, and the compaction divider the
since-last-compaction scope is built on is explained on the
[real conversation layout](real-conversation-layout.md) page.

- [artifact-sharing.md](artifact-sharing.md) — the **Share…** action in the
  same Exports submenu: a read-only public web link instead of a local copy.
- [raw-thread-export.md](raw-thread-export.md) — the troubleshooting export in the
  same submenu: every stored message, not just what was on screen, each labelled with
  whether it was shown, folded or hidden.
- [asides.md](asides.md) — the side-question branches that exports
  deliberately exclude.
- [real-conversation-layout.md](real-conversation-layout.md) — explains
  compaction dividers and how the chat view scopes the post-compaction
  conversation, the same boundary "Copy since last compaction" uses. Also
  explains why the **visible** transcript is what the export captures (tool
  chatter folds into the per-turn header on the screen too).
- [context-details.md](context-details.md) — the other place Omniscio shows an
  approximate token breakdown (what's in the agent's context window right
  now).
