Web MD Open in Web MD

Confirmed — you're looking at a real path, but it's the wrong (and mostly empty) source. Here's the actual layout on your machine.

Why agent-transcripts/**/*.jsonl is empty

That directory exists but is essentially unpopulated — across all your projects there's exactly one 12-line file, and it only has {role, message} (no token data). Cursor writes JSONL transcripts only for a narrow slice (background/cloud agents), not normal Composer/chat sessions. It is not the system of record. Don't build on it.

Where Cursor actually keeps everything (SQLite, not JSONL)

The real store is a LevelDB-in-SQLite blob DB:

~/Library/Application Support/Cursor/User/globalStorage/state.vscdb → table cursorDiskKV

Key namespaces I see in yours:

Key prefix Count What it is
bubbleId:<composer>:<bubble> 1029 individual messages — this is your gold
composerData:<uuid> 57 conversation-level metadata
messageRequestContext:* 124 per-request attached context
checkpointId:*, codeBlockDiff:* edit/checkpoint state

And the per-message token data is populated — each bubbleId blob has:

tokenCount: { inputTokens, outputTokens }
usageUuid        // links to server-side billing record
type             // 1 = user, 2 = assistant
timingInfo
text, toolResults, codeBlocks, ...

Sampled assistant bubbles: in=5713 out=753, in=15677 out=324, in=24881, in=34634, in=49568… — note the input side climbs monotonically within a conversation, i.e. it's recording the running context window, so you can reconstruct context growth per turn.

composerData:* carries createdAt, unifiedMode, and a usageData field (conversation rollup). Per-workspace copies also live at .../workspaceStorage/<hash>/state.vscdb.

Separately, ~/.cursor/ai-tracking/ai-code-tracking.db is a different concern — it tracks AI-authored code % (lines added by tab/composer/human, scored_commits, conversation_summaries with model/mode). Useful for "how much code is AI-written" but not for token cost.

The two caveats that matter for an org tool

  1. Cache is not broken out locally. You get input/output token counts and a usageUuid, but no cacheReadTokens/cacheWriteTokens split. So local scraping gives you context-size and I/O volume, not true cache-hit economics. That's the part Cursor keeps server-side (the usageUuid is the join key to it).
  2. Model name isn't cleanly on the bubble (_v is a schema version int, not the model). Pull model from composerData / conversation_summaries.model.

For org-level cost, don't scrape at all

Per-machine SQLite scraping only ever sees one dev's laptop and misses cache economics. For the org rollup you actually want the Cursor Admin API (Teams/Business plan): POST /teams/daily-usage-data and /teams/spend return per-user, per-model token counts and spend server-side — which is exactly the cross-org, cache-aware view your plugin infra needs. Use local state.vscdb scraping only as the supplement for fine-grained per-turn context-window analysis that the API doesn't expose.

Want me to sketch the schema-extraction queries (a cursorDiskKV → normalized per-turn table) or map out the Admin API side? If so, tell me whether you'd like it as a Markdown design doc or an HTML page.