Reference

Where AI coding tools store your chat history.

Six tools, six formats, none of them documented and none of them promising to stay put. Every path below was read off a working macOS machine by code that parses these files daily — not from a changelog, because none of these tools publish one for this.

The short answer
ToolLocation on macOSFormat
Cursor~/Library/Application Support/Cursor/User/globalStorage/state.vscdbSQLite (VS Code key/value store)
Claude Code~/.claude/projects/<url-encoded-cwd>/<session-uuid>.jsonlJSONL, one file per session
Codex~/.codex/sessions/YYYY/MM/DD/rollout-<iso>-<uuid>.jsonlJSONL, date-partitioned
Zed~/Library/Application Support/Zed/threads/threads.dbSQLite
Kiro~/Library/Application Support/Kiro/User/globalStorage/kiro.kiroagent/workspace-sessions/Ordinary JSON files, plus a sessions.json index
Antigravity~/.gemini/antigravity/Three stores — one encrypted, two not

macOS paths. ~ is your home directory. These are user-level stores; none of them are synced anywhere by default.

Cursor
~/Library/Application Support/Cursor/User/globalStorage/state.vscdb

Worth knowing: The widely repeated answer points at workspaceStorage, which holds per-project metadata. The conversation bodies live in globalStorage.

Cursor is a VS Code fork, so it inherits VS Code's storage layout: one large SQLite database under globalStorage, plus a workspaceStorage/<hash>/ directory per project folder. The per-workspace databases carry a workspace.json naming the folder and a composer.composerData key listing that workspace's conversations. The conversation content itself is in the global database.

Almost all of it sits in one table, cursorDiskKV, as JSON blobs keyed by id. The database is large and grows: on the machine this was measured, roughly 2.5 GB with a further ~1.5 GB write-ahead log alongside it. Anything that copies the file before reading copies all of that, every time.

There is a second, tempting file: conversation-search.db. It is smaller, tidier, and already full-text indexed. It is also a derived cache — on the same machine it held 330 conversations against 403 in the real store, and each one is flattened into a single blob with the speaker labels discarded, so you cannot tell what you said from what the model said. Read the real store.

One SQLite detail costs an afternoon: cursorDiskKV.value is declared BLOB, but Cursor writes JSON into it as TEXT. SQLite applies no type affinity to a BLOB column, so a reader that asks for bytes gets a type error on every single row. Combined with an iterator that silently drops errors, that produces an empty result set and no failure — COUNT(*) reports thousands of rows while SELECT value appears to return nothing.

Claude Code
~/.claude/projects/<url-encoded-cwd>/<session-uuid>.jsonl

Worth knowing: The directory name is the working directory with its slashes rewritten, so it is not obvious which folder a directory belongs to at a glance.

The most readable of the six. Each session is a JSON Lines file — one JSON object per line, appended as the conversation happens — inside a directory named after the project it ran in. A path like /Users/you/code/api becomes the directory -Users-you-code-api.

Because it is append-only plain text, it is the one store you can usefully grep directly, and the one that survives being copied while the tool is running.

It is also the only one of the six that deletes itself. Claude Code removes session files older than cleanupPeriodDays — 30 by default — at startup, with no prompt.

How the 30-day cleanup works, and how to change it

Codex
~/.codex/sessions/YYYY/MM/DD/rollout-<iso>-<uuid>.jsonl

Worth knowing: session_id is not unique across files — a child thread inherits its parent's. Key on id, or two files will fight over one record forever.

Sessions are JSONL like Claude Code's, but partitioned into year/month/day directories, with a parallel ~/.codex/archived_sessions tree for older ones. Both need reading if you want everything.

The trap is identity. A resumed run or an approval prompt spawns a child thread that carries the same session_id as its parent while living in its own file with its own id. Anything that treats session_id as the primary key will map two files onto one record, and each pass will overwrite the other — an indexer doing this reindexes the same pair on every run, forever, with no error to notice.

Zed
~/Library/Application Support/Zed/threads/threads.db

Worth knowing: A single database for all threads, not one file per conversation — so there is no per-project directory to browse.

Zed keeps agent threads in one SQLite database under its application support directory. Unlike the VS Code forks there is no workspace-keyed split; the database is the whole store.

Zed's Windows support is recent and its store location there was never confirmed on a real machine, so anything reading this cross-platform should probe both the roaming and local app-data directories rather than assume one.

Kiro
~/Library/Application Support/Kiro/User/globalStorage/kiro.kiroagent/workspace-sessions/

Worth knowing: Kiro is a VS Code fork but does not put transcripts in state.vscdb — they are plain files in their own directory.

Kiro inherits the VS Code layout, which makes it easy to assume its conversations are inside state.vscdb with everything else. They are not. The agent writes ordinary files under globalStorage/kiro.kiroagent/workspace-sessions/, with a sessions.json acting as the index.

That makes it the most straightforward of the VS Code forks to read: no SQLite, no blob columns, no cache to mistake for the real thing.

Antigravity
~/.gemini/antigravity/

Worth knowing: The obvious file is encrypted and the readable transcript lives somewhere else entirely, in an operational log.

Antigravity splits one conversation across three files, and the one that looks canonical is the one you cannot read. conversations/<uuid>.pb is encrypted: measured at 7.9997 bits per byte of entropy, which is indistinguishable from random data, with no recoverable strings.

The same conversation is also written, unencrypted, to an operational log at brain/<uuid>/.system_generated/logs/overview.txt — JSONL, one step per line, with user messages, model replies and timestamps. Titles, workspace folders and branches live in a third file, agyhub_summaries_proto.pb, a binary protobuf with no published schema.

Two practical notes. Not every conversation has a log — on the machine this was measured, 11 of 13 did, and the two without were the oldest. And Antigravity truncates its own tool arguments at 256 bytes, appending a note after a raw newline; since those arguments are double-encoded JSON strings, the result is a string that is both unterminated and carrying a control character illegal inside JSON. A parser that does not repair that before parsing fills up with stray escape sequences and looks like it worked.

What none of these tools promise

Every path on this page is a private implementation detail of somebody else's application. None of these tools document their storage layout, none of them version it, and none of them owe anyone notice before changing it. A format change is a matter of when, not if — and when it happens, anything reading these files gets an empty result rather than an error, because a parser that no longer recognises a file's shape and a file that genuinely holds nothing look identical from the outside.

That ambiguity is worth stating plainly, because it is the thing that makes reading these stores harder than finding them. Anything built on top of these paths should treat "I read the file and found nothing" as a fault to be investigated, not as a fact about the file.

Common questions

Where does Cursor store chat history on macOS?

In a SQLite database at ~/Library/Application Support/Cursor/User/globalStorage/state.vscdb, mostly in the cursorDiskKV table. The workspaceStorage directory that is often cited instead holds per-project metadata, not the conversation bodies. There is also a conversation-search.db, but it is a derived cache that discards speaker labels.

Where does Claude Code store conversation history?

As JSONL files at ~/.claude/projects/<url-encoded-working-directory>/<session-uuid>.jsonl — one file per session, one JSON object per line. Old transcripts are deleted on a schedule set by cleanupPeriodDays in ~/.claude/settings.json.

Can I read these files directly, or grep them?

The JSONL stores (Claude Code, Codex, and Antigravity's operational log) are plain text and can be grepped directly. Cursor, Zed and Kiro are SQLite or structured files and need a query rather than a text search. Antigravity's conversations/*.pb files are encrypted and cannot be read at all.

Are these locations documented by the tools themselves?

No. None of these tools document their storage layout, and none promise it will stay the same. Every path here was found by reading the files on a real machine, and any of them can change in a release without notice.

Is it safe to copy or back up these files?

The plain-text JSONL stores copy safely. The SQLite stores are being written to while the tool runs, so a copy taken mid-write can be inconsistent — Cursor's in particular is large enough (roughly 2.5 GB plus a ~1.5 GB write-ahead log on the machine measured) that copying it routinely is expensive as well as risky.

This page exists because Inventory reads all six of these stores to build one searchable index of every AI conversation on your Mac. The research was done to make the product work; writing it down cost nothing extra. If a path here is wrong on your machine, or a tool has moved, tell us and this page gets corrected.