Cursor IDE¶
The Cursor desktop application, modelled as its own backend
(cursor-ide) separate from the terminal CLI. It
stores chat history in VS Code-style state.vscdb SQLite databases
under the platform user-data directory.
Cursor IDE has no dedicated fast prompt-history backend. Pass --exhaustive
to project prompt records from these databases. Targeted effort cannot route
this backend.
Stores¶
- Role:
primary_chat- Format:
sqlite- Coverage:
default_search- Adapter:
cursor_ide.state_vscdb_modern.v1,cursor_ide.state_vscdb_legacy.v1
- Role:
primary_chat- Format:
sqlite- Coverage:
default_search- Adapter:
cursor_ide.state_vscdb_modern.v1
- Role:
app_state- Format:
sqlite- Coverage:
catalog_only- Adapter:
-
- Role:
cache- Format:
sqlite- Coverage:
catalog_only- Adapter:
-
- Role:
app_state- Format:
text- Coverage:
catalog_only- Adapter:
-
- Role:
app_state- Format:
text- Coverage:
private- Adapter:
-
cursor-ide.state_vscdb¶
Cursor IDE chat storage; known prompt/chat keys in ItemTable/cursorDiskKV hold conversation JSON. agentgrep does not scan arbitrary state values. Cursor does not publish a formal schema — agentgrep’s parser is the reference implementation. On WSL the store is discovered under the Windows-host mount too (see ADR 0009).
- Agent:
Cursor IDE
- Role:
primary_chat- Format:
sqlite- Coverage:
default_search- Path:
${HOME}/.config/Cursor/User/globalStorage/state.vscdb- Adapter:
cursor_ide.state_vscdb_modern.v1, cursor_ide.state_vscdb_legacy.v1- Data version:
-- Version strategies:
-- Observed:
Cursor IDE 3.26.13 (2026-10-25)
- Default search:
yes
SQLite tables observed (3.26.13, 2026-10-25)
- ItemTable columns:
key,value- composerHeaders columns:
composerId,workspaceId,createdAt,lastUpdatedAt,isArchived,isSubagent,recency,checkpointAt,value,subagentTypeName- cursorDiskKV columns:
key,value
Cursor IDE store, parsed by the current cursor_ide.state_vscdb_modern.v1 adapter. Not the same as the Cursor CLI agent transcripts.
cursor-ide.workspace_state¶
Per-workspace state.vscdb, one per opened project under workspaceStorage/<hash>/. Same ItemTable shape as the global store; the aiService.prompts key holds that workspace’s prompt history. The directory hash contributes origin.cwd_hash; sibling workspace.json can contribute origin.cwd. Reuses the cursor_ide.state_vscdb_modern.v1 adapter.
- Agent:
Cursor IDE
- Role:
primary_chat- Format:
sqlite- Coverage:
default_search- Path:
${HOME}/.config/Cursor/User/workspaceStorage/<hash>/state.vscdb- Adapter:
cursor_ide.state_vscdb_modern.v1- Data version:
-- Version strategies:
-- Observed:
Cursor IDE 3.26.13 (2026-10-25)
- Default search:
yes
SQLite tables observed (3.26.13, 2026-10-25)
- ItemTable columns:
key,value- composerHeaders columns:
composerId,workspaceId,createdAt,lastUpdatedAt,isArchived,isSubagent,recency,checkpointAt,value,subagentTypeName- cursorDiskKV columns:
key,value
Per-workspace IDE history, complementing the global cursor-ide.state_vscdb. Parsed by the shared cursor_ide.state_vscdb_modern.v1 adapter.
cursor-ide.composer_headers¶
A third table in the same state.vscdb as ItemTable and cursorDiskKV, holding one row per session: composerHeaders(composerId, workspaceId, createdAt, lastUpdatedAt, isArchived, isSubagent, recency, checkpointAt, value, subagentTypeName). The value JSON carries session identity and origin — name, isWorktree, trackedGitRepos, agentLocation, workspaceIdentifier, referencedPlans. Notably it lists sessions that have neither a composerData: nor a bubbleId: row, which no other store can see at all. agentgrep reads it as a lookup: workspaceIdentifier.uri gives a composer’s working directory, workspaceId its cwd_hash, and name backs up the composerData title. Sessions only this table knows have no turns, so they yield no records.
- Agent:
Cursor IDE
- Role:
app_state- Format:
sqlite- Coverage:
catalog_only- Path:
${HOME}/.config/Cursor/User/globalStorage/state.vscdb- Adapter:
-
- Data version:
-- Version strategies:
shape_inference- Observed:
Cursor IDE 3.26.13 (2026-10-25)
- Default search:
no
cursor-ide.conversation_search¶
Cursor’s own FTS5 index over conversation bodies, beside state.vscdb. conversations(fts_rowid, source, scope, id, title, updated_at, is_archived, root_fingerprint, cache_fingerprint) with source constrained to local or cloud-cache, plus a virtual conversation_fts(title, body) using unicode61. Derived data — the bodies also live in cursorDiskKV — so it is catalogued as a cache rather than a transcript, and searching it would duplicate hits.
- Agent:
Cursor IDE
- Role:
cache- Format:
sqlite- Coverage:
catalog_only- Path:
${HOME}/.config/Cursor/User/globalStorage/conversation-search.db- Adapter:
-
- Data version:
-- Version strategies:
shape_inference- Observed:
Cursor IDE 3.26.13 (2026-10-25)
- Default search:
no
cursor-ide.server_log¶
The remote server’s startup log for one build, named by the build’s commit: the address it bound and the extension host’s port. Runtime state, not conversation content.
- Agent:
Cursor IDE
- Role:
app_state- Format:
text- Coverage:
catalog_only- Path:
${HOME}/.cursor-server/.<commit>.log- Adapter:
-
- Data version:
-- Version strategies:
-- Observed:
Cursor IDE 3.26.13 (2026-10-25)
- Default search:
no
cursor-ide.server_token¶
The connection token for one remote-server build, named by its commit. A credential, so it is documented and never enumerated.
- Agent:
Cursor IDE
- Role:
app_state- Format:
text- Coverage:
private- Path:
${HOME}/.cursor-server/.<commit>.token- Adapter:
-
- Data version:
-- Version strategies:
-- Observed:
Cursor IDE 3.26.13 (2026-10-25)
- Default search:
no
Record schemas¶
Global state database¶
cursor-ide.state_vscdb is the platform-specific global SQLite
database (state.vscdb). agentgrep reads known prompt and chat keys in
ItemTable/cursorDiskKV; it does not scan arbitrary state values.
Global records may not carry project origin because the database is
shared across workspaces.
Platform |
Path |
|---|---|
Linux |
|
macOS |
|
Windows |
|
Workspace state databases¶
cursor-ide.workspace_state covers one state.vscdb per opened
project under workspaceStorage/<hash>/. These share the global store’s
ItemTable shape; the aiService.prompts key holds that workspace’s typed
prompt history. agentgrep enumerates them through the platform
workspaceStorage directory and parses them with the same adapter as the
global store.
Project context¶
Store |
|
|
|
|---|---|---|---|
|
|
|
|
same composer keys |
sibling |
|
Cursor keeps the interesting metadata in cursorDiskKV, not in the
prompt keys: composerData:<uuid> is the session document, carrying the
model it ran under and a gitWorktree block with the worktree path and
branch name, and bubbleId:<uuid>:<uuid> is one turn of that session,
naming its own model. Both are read, and both are
lossless — Cursor writes the path, so --cwd,
cwd:, and branch: answer with the real values. A turn that ran in its
own worktree keeps its own origin rather than inheriting the session’s.
Per-workspace stores also contribute origin.cwd_hash from the
workspaceStorage/<hash> directory, and agentgrep resolves the sibling
workspace.json folder URI to origin.cwd. That pair lets --cwd,
cwd:, MCP cwd, and cwd_hash: target one Cursor workspace without
opening unrelated workspace databases. The workspace cwd is a fact
about the database, not a promise about every record in it, so a
composer bubble that names a different worktree still wins for its own
record.
Where a composer has no gitWorktree block, its row in the
cursor-ide.composer_headers table fills in: the
workspaceIdentifier.uri folder becomes origin.cwd (a
vscode-remote://wsl+<distro> URI maps to its Linux path) and workspaceId
becomes origin.cwd_hash, the same digest the per-workspace databases are
filed under. Every turn of a composer also takes the session’s name as its
title, from composerData, else from composerHeaders. A title is searchable
text, so a term in it matches every turn of that composer.
Global state.vscdb records stay conservative when no composer origin is
known. They remain searchable by text, agent, scope, and other non-origin
fields, but they do not satisfy a hard current-project filter unless the
stored record itself carries project metadata.
Note
Cursor publishes no schema for the composerData and bubbleId keys.
Every read is guarded, so a schema change degrades to an absent field
rather than a wrong one.
Cross-host discovery on WSL¶
Cursor is a VS Code fork, so when its UI runs on a Windows host and edits
a project inside WSL, the IDE chat is written client-side on Windows under
/mnt/c/Users/<user>/AppData/Roaming/Cursor/User, not inside the distro.
On WSL, agentgrep detects this and also probes the Windows users mount so
both the global and per-workspace state.vscdb databases are searchable
from Linux. AGENTGREP_WSL_USERS_ROOT overrides the mount root (default
/mnt/c/Users) for non-default drive letters. See
ADR 0009: Cross-host discovery and remote-workspace path mapping for the design.
Changes by version¶
Each entry brackets a change between the observation that first saw it and the last one that did not; see Observed record shapes.
Observed 2026-10-25¶
The observer now reads a version for the WSL remote-server build, 3.26.13
(3.17.8 on 2026-09-11), so this entry is bracketed by date: seen 2026-10-25,
absent 2026-09-11. The Windows install directory still has no product.json,
so the desktop app’s own version is unread.
No store changed shape: the global and workspace
state.vscdbtables and columns match the last observation.The remote server keeps a
.<commit>.logand a.<commit>.tokenfile per build besidebin/. They are catalogued since ascursor-ide.server_logandcursor-ide.server_token; the token is private.
Observed 2026-09-11¶
The observer reads no version for the desktop app, so this entry is bracketed
by date: seen 2026-09-11, absent 2026-08-08. The WSL remote-server build
present at the later date reports Cursor 3.17.8. The Windows install
directory has no product.json, so the desktop app’s own version could not
be read.
composerHeaders, in both the global and per-workspacestate.vscdb, adds asubagentTypeNamecolumn. agentgrep reads the table for workspace origin and titles, not for that column.