@juspay/neurolink
Version:
The pipe layer of an AI nervous system: one interface connecting provider neurons to your application, across three inference types — generate, stream and decide. `decide` returns typed, calibrated judgements from a non-generative model (~400ms, ~$0.00002
51 lines (50 loc) • 3.03 kB
TypeScript
/**
* Reads token usage out of Cursor CLI's local chat store.
*
* Store: `~/.cursor/chats/<workspaceHash>/<agentId>/store.db`, one SQLite file
* per agent session, with two tables:
*
* - `meta` — a single row whose `value` is **hex-encoded** JSON (not JSON,
* and not a blob: a hex *string*), carrying `latestRootBlobId`, `name`,
* `createdAt` and `lastUsedModel`.
* - `blobs` — a content-addressed store, `id` = SHA-256 of `data`. Some
* blobs are UTF-8 JSON messages; the root blob is **protobuf**.
*
* **What this reader reports is NOT cumulative spend, and that difference is
* the whole reason to read this file before trusting the number.** Every other
* reader here sums per-turn usage across a transcript. Cursor persists no
* per-turn usage at all: the only counts in the store are a snapshot of the
* *current context composition* on the newest root blob — how many tokens the
* system prompt, tools, rules, skills, subagents and conversation each occupy
* right now. A session with 200 turns and a session with 1 turn produce one
* snapshot each. Summing across sessions therefore answers "how much context
* was loaded, last time each session was touched", never "how much was billed".
* It lands in `inputTokens` because context is what gets sent as input, and it
* is the honest home for it — but a dashboard adding this to a metered CLI's
* input tokens is adding two different quantities. The same hazard OpenCode's
* reader documents for double-counted proxy traffic, one step further.
*
* **Why the parse validates itself.** Protobuf field numbers are an internal
* detail of a closed-source binary that updates itself, so hard-coding "the
* total is field 5.1" is a claim about a format nobody published. Instead the
* parser finds the breakdown by *shape* — submessages of exactly
* `{1: string, 2: string, 3?: varint, 4?: varint}` — sums their token fields,
* and then requires that sum to appear verbatim as a varint elsewhere in the
* same message. On the reference store that is
* 480 + 11592 + 9257 + 6621 + 391 + 143 = 28484, matching the stored 28484
* exactly, against a 200000 window. If a future Cursor renumbers its fields the
* shape match still works; if it changes the *structure*, the sum stops
* matching and the session is reported as an error rather than counted wrong.
* A reader that cannot tell "I parsed nothing" from "this session used nothing"
* is the failure this check exists to prevent.
*
* Cost is `unavailable`, not zero-with-confidence: Cursor is a subscription and
* the store records no model-priced turns to derive a figure from.
*/
import type { LocalUsageReader } from "../types/index.js";
/**
* @returns the session's context-token total, or `null` when the blob holds no
* breakdown whose entries sum to a total stated alongside them.
*/
export declare function extractCursorContextTokens(rootBlob: Uint8Array): number | null;
export declare function createCursorReader(): Promise<LocalUsageReader>;