Cursor IDE chat history.
- Source:
src/providers/cursor.ts - Loading: lazy (
src/providers/index.ts:44-57). Thenode:sqliteimport is the heavy dependency that justifies lazy loading. - Test:
tests/providers/cursor.test.ts(77 lines),tests/providers/cursor-bubble-dedup.test.ts(176 lines)
A single SQLite database per platform:
| Platform | Path |
|---|---|
| macOS | ~/Library/Application Support/Cursor/User/globalStorage/state.vscdb |
| Windows | %APPDATA%/Cursor/User/globalStorage/state.vscdb |
| Linux | ~/.config/Cursor/User/globalStorage/state.vscdb |
SQLite. Two parallel sources within the same db:
- Bubbles (
cursor.ts:201-331): per-message rows. The richer source. - agentKv (
cursor.ts:350-460): per-conversation key-value blobs. The fallback for older sessions.
The parser tries both and dedupes via seenKeys.
src/cursor-cache.ts writes ~/.cache/codeburn/cursor-results.v<n>.json (override with $CODEBURN_CACHE_DIR). The unsuffixed cursor-results.json is left for older binaries; a matching-version copy is adopted once and never overwritten. The fingerprint is dbMtimeMs + dbSizeBytes of state.vscdb. Atomic write via temp + rename.
- Bubbles: per
bubbleId(cursor.ts:282). - agentKv: per
requestId(cursor.ts:429).
- 180-day lookback. The bubbles query bounds itself to the trailing 180 days (
cursor.ts:205). Older history is ignored. If a user reports "Cursor data missing", confirm the date range first. - 250 000 bubble cap. Power users with massive history are capped to prevent unbounded memory. If you need to raise this, also raise the cache size budget.
- Per-conversation user-message queue. The parser caches the user-message stream per conversation to avoid an O(n) shift on every turn (
cursor.ts:171-191). - agentKv has no per-message timestamp. The DB file's mtime is used as the timestamp for every agentKv-derived call (
cursor.ts:358-363). This is wrong but consistent. - Cursor v3 reports zero token counts. The parser falls back to char-counting (
CHARS_PER_TOKEN = 4) for those rows (cursor.ts:265-272).
The local database carries no per-turn token counts and none of the cache reads Cursor re-sends on every request, so local figures are estimates and run far below Cursor's dashboard. Cursor's dashboard exports every usage event with the token split it billed:
codeburn import cursor ~/Downloads/usage-events-2026-09-25.csv --from 2026-08-27 --to 2026-09-25
codeburn import cursor --remove
- Where it lives. Events are stored in
~/.cache/codeburn/imports/cursor-usage.v1.json(or$CODEBURN_CACHE_DIR), beside the daily cache, and never swept. Re-importing merges: each row is keyed by a hash of all its fields, so an overlapping export adds only rows not seen before. Nothing is uploaded. - Coverage. The export has no range of its own. Pass the range you exported with
--from/--to(a date, an ISO time, or the epoch milliseconds in the export URL'sstartDate/endDate); a bare--todate runs to the end of that UTC day. Without them the import covers the UTC days of its first and last event. Either way coverage ends no later than the CSV file's modification time, so usage after the export keeps its local estimate. Events outside--from/--toare refused. Coverage from several imports is merged. - Replacement. Inside coverage, local
cursor(IDE) andcursor-agent(CLI) calls are dropped at serve time and the imported events stand for them; outside it the local estimates stay. The export is the account's usage, which includes the Agent CLI. When the export holds Grok Bot events (grok-bot-*), the localgrokbotmirror estimates are replaced the same way: Grok Bot bills the same account. The cached local calls are never touched, so--removerestores them. - Rows. IDE events show under provider Cursor, project
Cursor (imported);grok-bot-*events under provider Grok Bot, projectGrok Bot (imported). Tokens:Input (w/o Cache Write)is input,Input (w/ Cache Write)cache write,Cache Readcache read,Output Tokensoutput. Modelautoiscursor-auto(Cursor (auto), priced as Sonnet 4.5 like the local parser),cursor-grok-*drops its prefix, andgrok-bot-*prices at the grok-4.6 rate like the local Grok Bot provider. - Cost. A dollar amount in
Costis kept as billed (costFromBilling, billingmetered).IncludedandFreerows are priced from their tokens at API rates, the same API-equivalent value every local Cursor call gets, and carry billingsubscription. - Daily cache. An import or removal drops the Cursor, Cursor Agent and Grok Bot slices of the local days it covers and pulls the watermark back, so the next run re-derives those days.
- Always reproduce against a fixture, not a real db. SQLite over the live db is racy; the user might be using Cursor while you read.
- If the bug is "tokens are zero", check whether the row is a v3 zero-token bubble, in which case the char-fallback should kick in.
- If the bug is "duplicate counts", check both
bubbleIddedup and the cross-providerseenKeysdedup. - Cache poisoning is the most common failure mode after a Cursor schema change. Bump
CURSOR_CACHE_VERSIONinsrc/cursor-cache.tsso old caches are invalidated.