JsonlLocalAgentStore checkpoints.get rescans whole file, making every send quadratic

Where does the bug appear (feature/product)?

Cursor SDK

Describe the Bug

Every send() restores conversation state by fetching each of the agent’s checkpoint blobs individually. JsonlLocalAgentStore.checkpoints.get reads and parses the whole checkpoints.ndjson on every call, so one point lookup costs O(file size). Restoring N blobs costs O(N x file size), and both grow each turn, so turn latency grows quadratically with conversation length. In my app a conversation reached 492 blobs / 9.3MB: send() took 11.65s to return a run, 8.44s of it inside 492 checkpoints.get calls. Another reached 3030 blobs / 34MB, projecting ~200s per message. checkpoints.create is affected too, roughly 64ms per append on a 1.4MB file.

Steps to Reproduce

No network or API key needed. For n in [50, 200, 800]: create a fresh JsonlLocalAgentStore in a temp dir, write n checkpoint blobs with an identical 20KB payload via checkpoints.create({agentId, blobId, data}), then time 20 checkpoints.get calls on distinct blobIds.

Measured per-get:

  • 50 blobs, 1.3MB file: 2.53ms
  • 200 blobs, 5.3MB file: 7.60ms
  • 800 blobs, 21.4MB file: 37.16ms

Per-get cost tracks file size linearly while blob size stays constant, which confirms a full-file scan per lookup. Implied full-restore cost: 0.1s, 1.5s, 29.7s. An n=3200 case never finished building, because create rescans too. I see the same curve instrumenting real stores in my app.

Expected Behavior

checkpoints.get should be a point lookup: index the file on open, or cache parsed contents keyed by mtime, so restoring an agent stays linear in state size instead of quadratic. checkpoints.create should append without rescanning.

Version Information

@cursor/sdk 1.0.24, Node v20.11.0, Ubuntu 22.04.5 (Linux 6.12.67-linuxkit x86_64). Models composer-2.5 and claude-opus-5. Store wired as local: { cwd, settingSources: [‘user’,‘project’,‘plugins’], store: new JsonlLocalAgentStore(dir) }.

Hey, thanks for the detailed report. Having benchmarks and offline repro steps really helps.

We reproduced it: per-get latency grows linearly with the size of checkpoints.ndjson, which confirms a full file scan on every lookup, and create also rescans the file. So yeah, send() ends up being quadratic in conversation length, just like you described. I’ve passed this to the team.

On the workaround: a JSONL store is append-only by design and has no index, so for long conversations it will hit a full scan. The default SQLite-backed store does indexed lookups and shouldn’t show this quadratic growth. Built-in node:sqlite requires Node >= 22.13, and you’re currently on Node v20.11.0.

Quick question: did you configure store: new JsonlLocalAgentStore(dir) explicitly for a specific reason (environment limits, Node version, file format requirement)? If there’s no hard reason, try upgrading to Node 22.13+ and remove the explicit store so the SDK uses the default SQLite store. That should get rid of the quadratic slowdown.

Let me know what led you to the JSONL store and whether switching to SQLite helps. I’ll reply here once there’s an update on the JSONL store itself.