Skip to content

feat(cursor-agent): discover and reconstruct sessions from store.db (fixes #986) - #1233

Open
maheshsingh20 wants to merge 1 commit into
getagentseal:mainfrom
maheshsingh20:feat/cursor-agent-store-db-986
Open

feat(cursor-agent): discover and reconstruct sessions from store.db (fixes #986)#1233
maheshsingh20 wants to merge 1 commit into
getagentseal:mainfrom
maheshsingh20:feat/cursor-agent-store-db-986

Conversation

@maheshsingh20

@maheshsingh20 maheshsingh20 commented Sep 2, 2026

Copy link
Copy Markdown

Summary

Fixes #986.

The cursor-agent provider previously missed CLI sessions stored in ~/.cursor/chats/**/store.db. This PR adds full discovery and reconstruction for those sessions, with proper source precedence, deduplication, WAL-aware caching, and blobEncryptionKey redaction.


Changes

src/providers/cursor-agent.ts

Discovery

  • discoverSessions() now scans ~/.cursor/chats/<hash>/<uuid>/store.db before the existing transcript walk, without following symlinks beyond two directory levels.
  • probeRoots() gains a chats entry so codeburn doctor reports it.

Schema validation

  • Checks for required meta + blobs tables before any data is read. A missing or incompatible schema emits a warning and falls through to the transcript fallback.

Metadata decoding

  • meta['0'] is hex-decoded from UTF-8 JSON to extract agentId, latestRootBlobId, name, createdAt, and lastUsedModel.
  • blobEncryptionKey is explicitly excluded from the decoded object and never appears in logs, caches, exports, snapshots, emitted calls, or stderr output.

Blob-graph reconstruction

  • BFS walk from latestRootBlobId, following nextBlobId chains and childBlobIds arrays.
  • Classifies blobs by role (user / assistant / model / request).
  • Handles both plain text strings and content-block arrays ([{type:'text', text:'...'}]).
  • Handles both toolCalls: [{name}] and content-block {type:'tool_use', name} formats.
  • Unknown fields are silently ignored for forward compatibility.

Token provenance

  • Explicit per-request inputTokens / outputTokens used when present and plausible (<=2,000,000). Values above that threshold are treated as context-window gauges and replaced by char-based estimates.
  • cacheCreationInputTokens and cacheReadInputTokens recorded when present.
  • costIsEstimated: true set on any call where at least one count was estimated.

Timestamps

  • Internal blob/request timestamps preferred; session createdAt used as session-level fallback. File mtime is never reported as an exact request timestamp.
  • Both epoch-ms (> 1e12) and epoch-s (< 1e12) values are normalised correctly.

WAL-aware in-memory cache

  • Per-session cache keyed on a fingerprint combining store.db mtime and the -wal sidecar size/mtime. Active sessions always trigger a re-parse when the WAL changes.

Source precedence and deduplication

  • store.db sources are emitted first in discoverSessions().
  • A successfully decoded store records a cursor-agent-store-decoded:<uuid> sentinel in seenKeys; transcript parsers for the same session UUID check it and skip themselves.
  • A store that fails schema/metadata validation does not set the sentinel, so the JSONL/TXT transcript fallback runs normally without double-counting.

tests/providers/cursor-agent-store.test.ts (new, 44 tests)

Category Tests
Discovery Empty/missing chats dir, valid store found, multiple sessions, non-UUID dirs skipped, no store.db skipped, probeRoots
Valid minimal store Single-turn parse, model resolution, costUSD > 0
Multi-turn blob graph 3-turn nextBlobId chain, childBlobIds branching, stable dedup keys, seenKeys respected
Malformed metadata Bad hex, invalid JSON, missing agentId, short blobId, agentId mismatch, missing meta table, missing blobs table
Missing/invalid blobs Absent root blob, broken nextBlobId (partial graph still yields), corrupt blob JSON
Unknown fields Ignored without crash, content-block array format, toolCalls array format
Timestamp normalization Epoch-ms, epoch-s promoted to ms, session-level fallback
Token provenance Exact both, input estimated, output estimated, gauge rejected, cache tokens, all absent
Store + transcript dedup One set of calls per session UUID, transcript fallback on corrupt store, store-only session appears
WAL cache Re-parse on file change, cache hit on unchanged fingerprint
blobEncryptionKey redaction Absent from emitted calls, absent from stderr, absent from source paths

Test results

Tests  44 passed  <- new (cursor-agent-store.test.ts)
Tests  17 passed  <- existing cursor-agent.test.ts, no regressions

…chats/**/store.db

Fixes getagentseal#986.

## What changed

- **Discovery**: discoverSessions() now scans ~/.cursor/chats/<hash>/<uuid>/store.db
  in addition to the existing ~/.cursor/projects/**/agent-transcripts/ transcript
  walk. probeRoots() gains a chats entry so codeburn doctor can report it.

- **Schema validation**: each store.db is checked for the required meta and �lobs
  tables before any reads are attempted. A missing or incompatible schema emits a
  warning and falls through to the transcript fallback.

- **Metadata decoding**: meta['0'] is hex-decoded from UTF-8 JSON to extract
  �gentId, latestRootBlobId,
ame, createdAt, and lastUsedModel.
  �lobEncryptionKey is explicitly excluded from the decoded object and must
  never appear in logs, caches, exports, snapshots, or committed fixtures.

- **Blob-graph reconstruction**: starting at latestRootBlobId, the parser
  walks
extBlobId chains and childBlobIds arrays in BFS order, classifying
  blobs by 
ole (user / assistant / model / request). Unknown fields are
  silently ignored for forward compatibility.

- **Token provenance**: explicit per-request inputTokens/outputTokens fields
  are used when present and plausible (≤ 2 000 000 — values above that are
  treated as context-window gauges and discarded). Missing components are
  estimated from character counts. costIsEstimated is set on calls where any
  count was estimated.

- **Timestamps**: internal request/blob timestamps take precedence; the session
  createdAt is used as a session-level fallback. File mtime is never reported
  as an exact request timestamp.

- **WAL-aware cache**: the in-memory per-session cache is keyed on a fingerprint
  combining the store.db mtime and the -wal sidecar size/mtime, so active
  sessions (written via WAL) always trigger a re-parse.

- **Source precedence + dedup**: store.db sources are emitted first. A
  successfully decoded store records a sentinel in seenKeys; transcript parsers
  for the same session UUID check the sentinel and skip themselves. A store that
  fails schema/metadata validation does not set the sentinel, so the transcript
  fallback runs normally.

## Tests (44 new, 0 regressions)

Covers all items from the issue test matrix: empty chats dir, valid minimal
store, multi-turn blob graph, malformed hex metadata, missing/invalid root blob,
unknown fields, seconds vs milliseconds timestamps, exact/partial/gauge/absent
token data, store+transcript dedup, transcript fallback on corrupt store,
WAL-aware cache invalidation, blobEncryptionKey redaction.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Cursor Agent provider misses CLI sessions stored in ~/.cursor/chats/**/store.db

1 participant